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

github.com/ambrop72/badvpn.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAmbroz Bizjak <ambrop7@gmail.com>2017-09-26 00:16:58 +0300
committerAmbroz Bizjak <ambrop7@gmail.com>2017-09-26 00:17:33 +0300
commitdce31a8e6a68d518a0fbed6bc644d14f2d548785 (patch)
treeaacf1b0911155e8126f6ea3d8ed8dd10c799ec34
parent9e3d26a81f7b705eb0241b488f708e6f2bde608a (diff)
tun2socks: Update lwIP to 931b5e643c25820a99bb8df94ab37db6b58c446b
Based partially on the work by Syrone Wong with additional fixes.
-rw-r--r--lwip/CHANGELOG1293
-rw-r--r--lwip/CMakeLists.txt4
-rw-r--r--lwip/FILES1
-rw-r--r--lwip/README65
-rw-r--r--lwip/UPGRADING133
-rw-r--r--lwip/astylerc22
-rw-r--r--lwip/custom/arch/cc.h21
-rw-r--r--lwip/custom/arch/perf.h36
-rw-r--r--lwip/custom/lwipopts.h14
-rw-r--r--lwip/custom/sys.c4
-rw-r--r--lwip/doc/FILES5
-rw-r--r--lwip/doc/NO_SYS_SampleCode.c122
-rw-r--r--lwip/doc/ZeroCopyRx.c39
-rw-r--r--lwip/doc/contrib.txt17
-rw-r--r--lwip/doc/doxygen/generate.bat1
-rwxr-xr-xlwip/doc/doxygen/generate.sh3
-rw-r--r--lwip/doc/doxygen/lwip.Doxyfile2510
-rw-r--r--lwip/doc/doxygen/main_page.h138
-rw-r--r--lwip/doc/doxygen/output/index.html10
-rw-r--r--lwip/doc/mdns.txt113
-rw-r--r--lwip/doc/mqtt_client.txt162
-rw-r--r--lwip/doc/ppp.txt529
-rw-r--r--lwip/doc/rawapi.txt171
-rw-r--r--lwip/doc/savannah.txt131
-rw-r--r--lwip/doc/snmp_agent.txt181
-rw-r--r--lwip/doc/sys_arch.txt122
-rw-r--r--lwip/lwip-base-version2
-rw-r--r--lwip/src/FILES6
-rw-r--r--lwip/src/Filelists.mk198
-rw-r--r--lwip/src/api/api_lib.c967
-rw-r--r--lwip/src/api/api_msg.c1541
-rw-r--r--lwip/src/api/err.c102
-rw-r--r--lwip/src/api/if_api.c102
-rw-r--r--lwip/src/api/netbuf.c83
-rw-r--r--lwip/src/api/netdb.c181
-rw-r--r--lwip/src/api/netifapi.c294
-rw-r--r--lwip/src/api/sockets.c4204
-rw-r--r--lwip/src/api/tcpip.c439
-rw-r--r--lwip/src/apps/altcp_tls/altcp_tls_mbedtls.c1008
-rw-r--r--lwip/src/apps/altcp_tls/altcp_tls_mbedtls_mem.c212
-rw-r--r--lwip/src/apps/altcp_tls/altcp_tls_mbedtls_mem.h72
-rw-r--r--lwip/src/apps/altcp_tls/altcp_tls_mbedtls_structs.h85
-rw-r--r--lwip/src/apps/httpd/fs.c174
-rw-r--r--lwip/src/apps/httpd/fs/404.html21
-rw-r--r--lwip/src/apps/httpd/fs/img/sics.gifbin0 -> 724 bytes
-rw-r--r--lwip/src/apps/httpd/fs/index.html47
-rw-r--r--lwip/src/apps/httpd/fsdata.c337
-rw-r--r--lwip/src/apps/httpd/fsdata.h41
-rw-r--r--lwip/src/apps/httpd/httpd.c2672
-rw-r--r--lwip/src/apps/httpd/httpd_structs.h123
-rw-r--r--lwip/src/apps/httpd/makefsdata/makefsdata97
-rw-r--r--lwip/src/apps/httpd/makefsdata/makefsdata.c1047
-rw-r--r--lwip/src/apps/httpd/makefsdata/readme.txt13
-rw-r--r--lwip/src/apps/httpd/makefsdata/tinydir.h808
-rw-r--r--lwip/src/apps/lwiperf/lwiperf.c661
-rw-r--r--lwip/src/apps/mdns/mdns.c2114
-rw-r--r--lwip/src/apps/mqtt/mqtt.c1419
-rw-r--r--lwip/src/apps/netbiosns/netbiosns.c368
-rw-r--r--lwip/src/apps/smtp/smtp.c1540
-rw-r--r--lwip/src/apps/snmp/snmp_asn1.c704
-rw-r--r--lwip/src/apps/snmp/snmp_asn1.h113
-rw-r--r--lwip/src/apps/snmp/snmp_core.c1350
-rw-r--r--lwip/src/apps/snmp/snmp_core_priv.h83
-rw-r--r--lwip/src/apps/snmp/snmp_mib2.c116
-rw-r--r--lwip/src/apps/snmp/snmp_mib2_icmp.c182
-rw-r--r--lwip/src/apps/snmp/snmp_mib2_interfaces.c368
-rw-r--r--lwip/src/apps/snmp/snmp_mib2_ip.c731
-rw-r--r--lwip/src/apps/snmp/snmp_mib2_snmp.c227
-rw-r--r--lwip/src/apps/snmp/snmp_mib2_system.c376
-rw-r--r--lwip/src/apps/snmp/snmp_mib2_tcp.c607
-rw-r--r--lwip/src/apps/snmp/snmp_mib2_udp.c372
-rw-r--r--lwip/src/apps/snmp/snmp_msg.c1952
-rw-r--r--lwip/src/apps/snmp/snmp_msg.h185
-rw-r--r--lwip/src/apps/snmp/snmp_netconn.c122
-rw-r--r--lwip/src/apps/snmp/snmp_pbuf_stream.c156
-rw-r--r--lwip/src/apps/snmp/snmp_pbuf_stream.h72
-rw-r--r--lwip/src/apps/snmp/snmp_raw.c101
-rw-r--r--lwip/src/apps/snmp/snmp_scalar.c220
-rw-r--r--lwip/src/apps/snmp/snmp_snmpv2_framework.c90
-rw-r--r--lwip/src/apps/snmp/snmp_snmpv2_usm.c410
-rw-r--r--lwip/src/apps/snmp/snmp_table.c342
-rw-r--r--lwip/src/apps/snmp/snmp_threadsync.c219
-rw-r--r--lwip/src/apps/snmp/snmp_traps.c454
-rw-r--r--lwip/src/apps/snmp/snmpv3.c136
-rw-r--r--lwip/src/apps/snmp/snmpv3_mbedtls.c342
-rw-r--r--lwip/src/apps/snmp/snmpv3_priv.h69
-rw-r--r--lwip/src/apps/sntp/sntp.c819
-rw-r--r--lwip/src/apps/tftp/tftp_server.c414
-rw-r--r--lwip/src/core/altcp.c547
-rw-r--r--lwip/src/core/altcp_tcp.c501
-rw-r--r--lwip/src/core/def.c216
-rw-r--r--lwip/src/core/dhcp.c1771
-rw-r--r--lwip/src/core/dns.c1373
-rw-r--r--lwip/src/core/inet_chksum.c239
-rw-r--r--lwip/src/core/init.c298
-rw-r--r--lwip/src/core/ip.c167
-rw-r--r--lwip/src/core/ipv4/autoip.c440
-rw-r--r--lwip/src/core/ipv4/dhcp.c1971
-rw-r--r--lwip/src/core/ipv4/etharp.c (renamed from lwip/src/netif/etharp.c)897
-rw-r--r--lwip/src/core/ipv4/icmp.c350
-rw-r--r--lwip/src/core/ipv4/igmp.c756
-rw-r--r--lwip/src/core/ipv4/ip4.c782
-rw-r--r--lwip/src/core/ipv4/ip4_addr.c179
-rw-r--r--lwip/src/core/ipv4/ip4_frag.c (renamed from lwip/src/core/ipv4/ip_frag.c)473
-rw-r--r--lwip/src/core/ipv6/README1
-rw-r--r--lwip/src/core/ipv6/dhcp6.c257
-rw-r--r--lwip/src/core/ipv6/ethip6.c128
-rw-r--r--lwip/src/core/ipv6/icmp6.c206
-rw-r--r--lwip/src/core/ipv6/inet6.c36
-rw-r--r--lwip/src/core/ipv6/ip6.c768
-rw-r--r--lwip/src/core/ipv6/ip6_addr.c179
-rw-r--r--lwip/src/core/ipv6/ip6_frag.c349
-rw-r--r--lwip/src/core/ipv6/mld6.c423
-rw-r--r--lwip/src/core/ipv6/nd6.c1370
-rw-r--r--lwip/src/core/mem.c285
-rw-r--r--lwip/src/core/memp.c635
-rw-r--r--lwip/src/core/netif.c1328
-rw-r--r--lwip/src/core/pbuf.c1065
-rw-r--r--lwip/src/core/raw.c460
-rw-r--r--lwip/src/core/snmp/asn1_dec.c657
-rw-r--r--lwip/src/core/snmp/asn1_enc.c611
-rw-r--r--lwip/src/core/snmp/mib2.c4146
-rw-r--r--lwip/src/core/snmp/mib_structs.c1174
-rw-r--r--lwip/src/core/snmp/msg_in.c1453
-rw-r--r--lwip/src/core/snmp/msg_out.c678
-rw-r--r--lwip/src/core/stats.c130
-rw-r--r--lwip/src/core/sys.c38
-rw-r--r--lwip/src/core/tcp.c1457
-rw-r--r--lwip/src/core/tcp_in.c1550
-rw-r--r--lwip/src/core/tcp_out.c1195
-rw-r--r--lwip/src/core/timeouts.c (renamed from lwip/src/core/timers.c)406
-rw-r--r--lwip/src/core/udp.c972
-rw-r--r--lwip/src/include/compat/posix/arpa/inet.h33
-rw-r--r--lwip/src/include/compat/posix/net/if.h36
-rw-r--r--lwip/src/include/compat/posix/netdb.h (renamed from lwip/src/include/posix/netdb.h)22
-rw-r--r--lwip/src/include/compat/posix/sys/socket.h (renamed from lwip/src/include/posix/sys/socket.h)22
-rw-r--r--lwip/src/include/compat/stdc/errno.h33
-rw-r--r--lwip/src/include/ipv4/lwip/autoip.h118
-rw-r--r--lwip/src/include/ipv4/lwip/inet.h107
-rw-r--r--lwip/src/include/ipv6/lwip/icmp6.h152
-rw-r--r--lwip/src/include/ipv6/lwip/inet6.h92
-rw-r--r--lwip/src/include/ipv6/lwip/ip6_addr.h286
-rw-r--r--lwip/src/include/lwip/altcp.h187
-rw-r--r--lwip/src/include/lwip/altcp_tcp.h70
-rw-r--r--lwip/src/include/lwip/altcp_tls.h97
-rw-r--r--lwip/src/include/lwip/api.h273
-rw-r--r--lwip/src/include/lwip/apps/FILES2
-rw-r--r--lwip/src/include/lwip/apps/altcp_tls_mbedtls_opts.h67
-rw-r--r--lwip/src/include/lwip/apps/fs.h124
-rw-r--r--lwip/src/include/lwip/apps/httpd.h240
-rw-r--r--lwip/src/include/lwip/apps/httpd_opts.h339
-rw-r--r--lwip/src/include/lwip/apps/lwiperf.h84
-rw-r--r--lwip/src/include/lwip/apps/mdns.h73
-rw-r--r--lwip/src/include/lwip/apps/mdns_opts.h74
-rw-r--r--lwip/src/include/lwip/apps/mdns_priv.h66
-rw-r--r--lwip/src/include/lwip/apps/mqtt.h212
-rw-r--r--lwip/src/include/lwip/apps/mqtt_opts.h103
-rw-r--r--lwip/src/include/lwip/apps/mqtt_priv.h104
-rw-r--r--lwip/src/include/lwip/apps/netbiosns.h43
-rw-r--r--lwip/src/include/lwip/apps/netbiosns_opts.h59
-rw-r--r--lwip/src/include/lwip/apps/smtp.h120
-rw-r--r--lwip/src/include/lwip/apps/smtp_opts.h81
-rw-r--r--lwip/src/include/lwip/apps/snmp.h135
-rw-r--r--lwip/src/include/lwip/apps/snmp_core.h377
-rw-r--r--lwip/src/include/lwip/apps/snmp_mib2.h78
-rw-r--r--lwip/src/include/lwip/apps/snmp_opts.h297
-rw-r--r--lwip/src/include/lwip/apps/snmp_scalar.h113
-rw-r--r--lwip/src/include/lwip/apps/snmp_snmpv2_framework.h32
-rw-r--r--lwip/src/include/lwip/apps/snmp_snmpv2_usm.h24
-rw-r--r--lwip/src/include/lwip/apps/snmp_table.h134
-rw-r--r--lwip/src/include/lwip/apps/snmp_threadsync.h114
-rw-r--r--lwip/src/include/lwip/apps/snmpv3.h106
-rw-r--r--lwip/src/include/lwip/apps/sntp.h76
-rw-r--r--lwip/src/include/lwip/apps/sntp_opts.h202
-rw-r--r--lwip/src/include/lwip/apps/tftp_opts.h106
-rw-r--r--lwip/src/include/lwip/apps/tftp_server.h94
-rw-r--r--lwip/src/include/lwip/arch.h465
-rw-r--r--lwip/src/include/lwip/autoip.h99
-rw-r--r--lwip/src/include/lwip/debug.h132
-rw-r--r--lwip/src/include/lwip/def.h148
-rw-r--r--lwip/src/include/lwip/dhcp.h257
-rw-r--r--lwip/src/include/lwip/dhcp6.h (renamed from lwip/src/include/ipv6/lwip/dhcp6.h)8
-rw-r--r--lwip/src/include/lwip/dns.h96
-rw-r--r--lwip/src/include/lwip/err.h110
-rw-r--r--lwip/src/include/lwip/errno.h198
-rw-r--r--lwip/src/include/lwip/etharp.h106
-rw-r--r--lwip/src/include/lwip/ethip6.h (renamed from lwip/src/include/ipv6/lwip/ethip6.h)8
-rw-r--r--lwip/src/include/lwip/icmp.h110
-rw-r--r--lwip/src/include/lwip/icmp6.h (renamed from lwip/src/include/ipv6/lwip/ip6_frag.h)60
-rw-r--r--lwip/src/include/lwip/if_api.h68
-rw-r--r--lwip/src/include/lwip/igmp.h (renamed from lwip/src/include/ipv4/lwip/igmp.h)91
-rw-r--r--lwip/src/include/lwip/inet.h169
-rw-r--r--lwip/src/include/lwip/inet_chksum.h103
-rw-r--r--lwip/src/include/lwip/init.h82
-rw-r--r--lwip/src/include/lwip/ip.h334
-rw-r--r--lwip/src/include/lwip/ip4.h111
-rw-r--r--lwip/src/include/lwip/ip4_addr.h (renamed from lwip/src/include/ipv4/lwip/ip4_addr.h)166
-rw-r--r--lwip/src/include/lwip/ip4_frag.h (renamed from lwip/src/include/ipv4/lwip/ip_frag.h)59
-rw-r--r--lwip/src/include/lwip/ip6.h93
-rw-r--r--lwip/src/include/lwip/ip6_addr.h352
-rw-r--r--lwip/src/include/lwip/ip6_frag.h144
-rw-r--r--lwip/src/include/lwip/ip6_zone.h296
-rw-r--r--lwip/src/include/lwip/ip_addr.h463
-rw-r--r--lwip/src/include/lwip/mem.h101
-rw-r--r--lwip/src/include/lwip/memp.h155
-rw-r--r--lwip/src/include/lwip/mld6.h (renamed from lwip/src/include/ipv6/lwip/mld6.h)53
-rw-r--r--lwip/src/include/lwip/nd6.h84
-rw-r--r--lwip/src/include/lwip/netbuf.h78
-rw-r--r--lwip/src/include/lwip/netdb.h68
-rw-r--r--lwip/src/include/lwip/netif.h526
-rw-r--r--lwip/src/include/lwip/netifapi.h169
-rw-r--r--lwip/src/include/lwip/opt.h2168
-rw-r--r--lwip/src/include/lwip/pbuf.h227
-rw-r--r--lwip/src/include/lwip/priv/altcp_priv.h146
-rw-r--r--lwip/src/include/lwip/priv/api_msg.h (renamed from lwip/src/include/lwip/api_msg.h)198
-rw-r--r--lwip/src/include/lwip/priv/memp_priv.h183
-rw-r--r--lwip/src/include/lwip/priv/memp_std.h (renamed from lwip/src/include/lwip/memp_std.h)62
-rw-r--r--lwip/src/include/lwip/priv/nd6_priv.h142
-rw-r--r--lwip/src/include/lwip/priv/sockets_priv.h164
-rw-r--r--lwip/src/include/lwip/priv/tcp_priv.h (renamed from lwip/src/include/lwip/tcp_impl.h)252
-rw-r--r--lwip/src/include/lwip/priv/tcpip_priv.h162
-rw-r--r--lwip/src/include/lwip/prot/autoip.h78
-rw-r--r--lwip/src/include/lwip/prot/dhcp.h176
-rw-r--r--lwip/src/include/lwip/prot/dhcp6.h136
-rw-r--r--lwip/src/include/lwip/prot/dns.h140
-rw-r--r--lwip/src/include/lwip/prot/etharp.h114
-rw-r--r--lwip/src/include/lwip/prot/ethernet.h125
-rw-r--r--lwip/src/include/lwip/prot/iana.h89
-rw-r--r--lwip/src/include/lwip/prot/icmp.h (renamed from lwip/src/include/ipv4/lwip/icmp.h)92
-rw-r--r--lwip/src/include/lwip/prot/icmp6.h170
-rw-r--r--lwip/src/include/lwip/prot/ieee.h83
-rw-r--r--lwip/src/include/lwip/prot/igmp.h90
-rw-r--r--lwip/src/include/lwip/prot/ip.h51
-rw-r--r--lwip/src/include/lwip/prot/ip4.h (renamed from lwip/src/include/ipv4/lwip/ip4.h)125
-rw-r--r--lwip/src/include/lwip/prot/ip6.h (renamed from lwip/src/include/ipv6/lwip/ip6.h)146
-rw-r--r--lwip/src/include/lwip/prot/mld6.h70
-rw-r--r--lwip/src/include/lwip/prot/nd6.h (renamed from lwip/src/include/ipv6/lwip/nd6.h)227
-rw-r--r--lwip/src/include/lwip/prot/tcp.h98
-rw-r--r--lwip/src/include/lwip/prot/udp.h68
-rw-r--r--lwip/src/include/lwip/raw.h98
-rw-r--r--lwip/src/include/lwip/sio.h55
-rw-r--r--lwip/src/include/lwip/snmp.h402
-rw-r--r--lwip/src/include/lwip/snmp_asn1.h101
-rw-r--r--lwip/src/include/lwip/snmp_msg.h315
-rw-r--r--lwip/src/include/lwip/snmp_structs.h268
-rw-r--r--lwip/src/include/lwip/sockets.h451
-rw-r--r--lwip/src/include/lwip/stats.h218
-rw-r--r--lwip/src/include/lwip/sys.h265
-rw-r--r--lwip/src/include/lwip/tcp.h298
-rw-r--r--lwip/src/include/lwip/tcpbase.h86
-rw-r--r--lwip/src/include/lwip/tcpip.h167
-rw-r--r--lwip/src/include/lwip/timeouts.h (renamed from lwip/src/include/lwip/timers.h)73
-rw-r--r--lwip/src/include/lwip/udp.h170
-rw-r--r--lwip/src/include/netif/bridgeif.h95
-rw-r--r--lwip/src/include/netif/bridgeif_opts.h98
-rw-r--r--lwip/src/include/netif/etharp.h226
-rw-r--r--lwip/src/include/netif/ethernet.h77
-rw-r--r--lwip/src/include/netif/lowpan6.h86
-rw-r--r--lwip/src/include/netif/lowpan6_opts.h70
-rw-r--r--lwip/src/include/netif/ppp/ccp.h156
-rw-r--r--lwip/src/include/netif/ppp/chap-md5.h36
-rw-r--r--lwip/src/include/netif/ppp/chap-new.h192
-rw-r--r--lwip/src/include/netif/ppp/chap_ms.h44
-rw-r--r--lwip/src/include/netif/ppp/eap.h169
-rw-r--r--lwip/src/include/netif/ppp/ecp.h50
-rw-r--r--lwip/src/include/netif/ppp/eui64.h94
-rw-r--r--lwip/src/include/netif/ppp/fsm.h175
-rw-r--r--lwip/src/include/netif/ppp/ipcp.h126
-rw-r--r--lwip/src/include/netif/ppp/ipv6cp.h183
-rw-r--r--lwip/src/include/netif/ppp/lcp.h171
-rw-r--r--lwip/src/include/netif/ppp/magic.h122
-rw-r--r--lwip/src/include/netif/ppp/mppe.h173
-rw-r--r--lwip/src/include/netif/ppp/polarssl/arc4.h81
-rw-r--r--lwip/src/include/netif/ppp/polarssl/des.h92
-rw-r--r--lwip/src/include/netif/ppp/polarssl/md4.h97
-rw-r--r--lwip/src/include/netif/ppp/polarssl/md5.h96
-rw-r--r--lwip/src/include/netif/ppp/polarssl/sha1.h96
-rw-r--r--lwip/src/include/netif/ppp/ppp.h690
-rw-r--r--lwip/src/include/netif/ppp/ppp_impl.h626
-rw-r--r--lwip/src/include/netif/ppp/ppp_opts.h604
-rw-r--r--lwip/src/include/netif/ppp/pppapi.h137
-rw-r--r--lwip/src/include/netif/ppp/pppcrypt.h136
-rw-r--r--lwip/src/include/netif/ppp/pppdebug.h (renamed from lwip/src/netif/ppp/pppdebug.h)37
-rw-r--r--lwip/src/include/netif/ppp/pppoe.h (renamed from lwip/src/include/netif/ppp_oe.h)63
-rw-r--r--lwip/src/include/netif/ppp/pppol2tp.h201
-rw-r--r--lwip/src/include/netif/ppp/pppos.h118
-rw-r--r--lwip/src/include/netif/ppp/upap.h123
-rw-r--r--lwip/src/include/netif/ppp/vj.h (renamed from lwip/src/netif/ppp/vj.h)43
-rw-r--r--lwip/src/include/netif/slipif.h64
-rw-r--r--lwip/src/netif/FILES21
-rw-r--r--lwip/src/netif/bridgeif.c743
-rw-r--r--lwip/src/netif/ethernet.c318
-rw-r--r--lwip/src/netif/ethernetif.c139
-rw-r--r--lwip/src/netif/lowpan6.c1208
-rw-r--r--lwip/src/netif/ppp/PPPD_FOLLOWUP473
-rw-r--r--lwip/src/netif/ppp/auth.c2976
-rw-r--r--lwip/src/netif/ppp/auth.h111
-rw-r--r--lwip/src/netif/ppp/ccp.c1740
-rw-r--r--lwip/src/netif/ppp/chap-md5.c126
-rw-r--r--lwip/src/netif/ppp/chap-new.c677
-rw-r--r--lwip/src/netif/ppp/chap.c908
-rw-r--r--lwip/src/netif/ppp/chap.h150
-rw-r--r--lwip/src/netif/ppp/chap_ms.c962
-rw-r--r--lwip/src/netif/ppp/chpms.c396
-rw-r--r--lwip/src/netif/ppp/chpms.h64
-rw-r--r--lwip/src/netif/ppp/demand.c465
-rw-r--r--lwip/src/netif/ppp/eap.c2423
-rw-r--r--lwip/src/netif/ppp/ecp.c191
-rw-r--r--lwip/src/netif/ppp/eui64.c56
-rw-r--r--lwip/src/netif/ppp/fsm.c1373
-rw-r--r--lwip/src/netif/ppp/fsm.h157
-rw-r--r--lwip/src/netif/ppp/ipcp.c3365
-rw-r--r--lwip/src/netif/ppp/ipcp.h106
-rw-r--r--lwip/src/netif/ppp/ipv6cp.c1533
-rw-r--r--lwip/src/netif/ppp/lcp.c4196
-rw-r--r--lwip/src/netif/ppp/lcp.h151
-rw-r--r--lwip/src/netif/ppp/magic.c284
-rw-r--r--lwip/src/netif/ppp/magic.h63
-rw-r--r--lwip/src/netif/ppp/md5.c320
-rw-r--r--lwip/src/netif/ppp/md5.h55
-rw-r--r--lwip/src/netif/ppp/mppe.c412
-rw-r--r--lwip/src/netif/ppp/multilink.c609
-rw-r--r--lwip/src/netif/ppp/pap.c628
-rw-r--r--lwip/src/netif/ppp/pap.h118
-rw-r--r--lwip/src/netif/ppp/polarssl/README22
-rw-r--r--lwip/src/netif/ppp/polarssl/arc4.c101
-rw-r--r--lwip/src/netif/ppp/polarssl/des.c422
-rw-r--r--lwip/src/netif/ppp/polarssl/md4.c281
-rw-r--r--lwip/src/netif/ppp/polarssl/md5.c300
-rw-r--r--lwip/src/netif/ppp/polarssl/sha1.c335
-rw-r--r--lwip/src/netif/ppp/ppp.c2905
-rw-r--r--lwip/src/netif/ppp/ppp.h201
-rw-r--r--lwip/src/netif/ppp/ppp_impl.h363
-rw-r--r--lwip/src/netif/ppp/pppapi.c427
-rw-r--r--lwip/src/netif/ppp/pppcrypt.c66
-rw-r--r--lwip/src/netif/ppp/pppoe.c (renamed from lwip/src/netif/ppp/ppp_oe.c)619
-rw-r--r--lwip/src/netif/ppp/pppol2tp.c1138
-rw-r--r--lwip/src/netif/ppp/pppos.c881
-rw-r--r--lwip/src/netif/ppp/randm.c249
-rw-r--r--lwip/src/netif/ppp/randm.h81
-rw-r--r--lwip/src/netif/ppp/readme.txt21
-rw-r--r--lwip/src/netif/ppp/upap.c677
-rw-r--r--lwip/src/netif/ppp/utils.c959
-rw-r--r--lwip/src/netif/ppp/vj.c351
-rw-r--r--lwip/src/netif/slipif.c128
-rw-r--r--lwip/test/fuzz/Makefile53
-rw-r--r--lwip/test/fuzz/README34
-rw-r--r--lwip/test/fuzz/config.h0
-rw-r--r--lwip/test/fuzz/fuzz.c136
-rw-r--r--lwip/test/fuzz/inputs/arp/arp_req.binbin0 -> 42 bytes
-rw-r--r--lwip/test/fuzz/inputs/icmp/icmp_ping.binbin0 -> 98 bytes
-rw-r--r--lwip/test/fuzz/inputs/ipv6/neighbor_solicitation.binbin0 -> 86 bytes
-rw-r--r--lwip/test/fuzz/inputs/ipv6/router_adv.binbin0 -> 118 bytes
-rw-r--r--lwip/test/fuzz/inputs/tcp/tcp_syn.binbin0 -> 74 bytes
-rw-r--r--lwip/test/fuzz/inputs/udp/udp_port_5000.binbin0 -> 50 bytes
-rw-r--r--lwip/test/fuzz/lwipopts.h68
-rw-r--r--lwip/test/fuzz/output_to_pcap.sh31
-rw-r--r--lwip/test/unit/Filelists.mk47
-rw-r--r--lwip/test/unit/api/test_sockets.c809
-rw-r--r--lwip/test/unit/api/test_sockets.h8
-rw-r--r--lwip/test/unit/arch/sys_arch.c341
-rw-r--r--lwip/test/unit/arch/sys_arch.h69
-rw-r--r--lwip/test/unit/core/test_mem.c64
-rw-r--r--lwip/test/unit/core/test_mem.h4
-rw-r--r--lwip/test/unit/core/test_pbuf.c211
-rw-r--r--lwip/test/unit/core/test_pbuf.h4
-rw-r--r--lwip/test/unit/dhcp/test_dhcp.c278
-rw-r--r--lwip/test/unit/dhcp/test_dhcp.h4
-rw-r--r--lwip/test/unit/etharp/test_etharp.c50
-rw-r--r--lwip/test/unit/etharp/test_etharp.h4
-rw-r--r--lwip/test/unit/ip4/test_ip4.c160
-rw-r--r--lwip/test/unit/ip4/test_ip4.h8
-rw-r--r--lwip/test/unit/lwip_check.h45
-rw-r--r--lwip/test/unit/lwip_unittests.c53
-rw-r--r--lwip/test/unit/lwipopts.h34
-rw-r--r--lwip/test/unit/mdns/test_mdns.c915
-rw-r--r--lwip/test/unit/mdns/test_mdns.h8
-rw-r--r--lwip/test/unit/mqtt/test_mqtt.c113
-rw-r--r--lwip/test/unit/mqtt/test_mqtt.h8
-rw-r--r--lwip/test/unit/tcp/tcp_helper.c80
-rw-r--r--lwip/test/unit/tcp/tcp_helper.h16
-rw-r--r--lwip/test/unit/tcp/test_tcp.c869
-rw-r--r--lwip/test/unit/tcp/test_tcp.h4
-rw-r--r--lwip/test/unit/tcp/test_tcp_oos.c254
-rw-r--r--lwip/test/unit/tcp/test_tcp_oos.h4
-rw-r--r--lwip/test/unit/udp/test_udp.c16
-rw-r--r--lwip/test/unit/udp/test_udp.h4
-rw-r--r--tun2socks/tun2socks.c204
388 files changed, 101383 insertions, 37910 deletions
diff --git a/lwip/CHANGELOG b/lwip/CHANGELOG
index 685b568..8f52e01 100644
--- a/lwip/CHANGELOG
+++ b/lwip/CHANGELOG
@@ -1,10 +1,1000 @@
HISTORY
-(CVS HEAD)
+(git master)
* [Enter new changes just after this line - do not remove this line]
- ++ New features:
+ ++ New features:
+
+ 2017-09-10: Joel Cunningham
+ * sockets: add readv() implementation (task #14610)
+
+ 2017-08-04: Simon Goldschmidt
+ * Clean up DHCP a bit: no need keep msg_out and msg_in as members in struct
+ dhcp - they are used in a call stack only (p_out and options_out_len as well)
+
+ 2017-08-04: Simon Goldschmidt
+ * pbuf: split pbuf_header(s16_t) into pbuf_add_header(size_t) and
+ pbuf_remove_header(size_t)
+
+ 2017-07-20: Douglas
+ * sys: deprecate sys_arch_sem_wait and sys_arch_mbox_fetch returning the
+ time waited rather they are now defined to return != SYS_ARCH_TIMEOUT
+ on success.
+
+ 2017-07-03: Jakub Schmidtke
+ * tcp: added support for sending TCP SACKs
+
+ 2017-06-20: Joel Cunningham
+ * netconn/netdb: added core locking support to netconn_gethostbyname (task #14523)
+
+ 2017-04-25: Simon Goldschmidt
+ * dhcp: added two hooks for adding and parsing user defined DHCP options
+
+ 2017-04-25: Joel Cunningham
+ * sockets: added CMSG and IP_PKTINFO for use with recvmsg (task #14247)
+
+ 2017-04-20: Joel Cunningham
+ * tcp: added Appropriate Byte Counting support (task #14128)
+
+ 2017-04-11: Simon Goldschmidt
+ * netconn/sockets: remove fatal error handling, fix asynchronous error handling,
+ ensure data before RST can be received
+
+ 2017-03-02: Joel Cunningham
+ * netconn/sockets: vectorize netconn_write for TCP, treating a vectored I/O write
+ atomically in regards to TCP segmentation (patch #8882)
+
+ 2017-03-02: Simon Goldschmidt
+ * netconn: added nonblocking accept/recv to netconn API (task #14396)
+
+ 2017-02-28: Simon Goldschmidt
+ * Added LWIP_SINGLE_NETIF for small targets with only one netif
+
+ 2017-02-17: Simon Goldschmidt
+ * Improved DNS_LOCAL_HOSTLIST interface
+
+ 2017-02-10: David van Moolenbroek
+ * Implement UDP and RAW multicast support for IPv6 (core API, not netconn/sockets)
+
+ 2017-02-10: Simon Goldschmidt
+ * tcp_close does not fail on memory error (instead, FIN is sent from tcp_tmr)
+
+ 2017-02-04: David van Moolenbroek
+ - IPv6 scopes support
+
+ 2017-01-20: Joel Cunningham
+ * sockets: add interface name/index APIs (task #14314)
+
+ 2017-01-08: David van Moolenbroek
+ * Extensions to RAW API (patch #9208)
+ - Connected RAW PCBs
+ - Add raw_sendto_if_src()
+ - Support IP_HDRINCL socket option
+
+ ++ Bugfixes:
+
+ 2017-09-12: David Lockyer
+ * select: allocate select_cb from memp for LWIP_MPU_COMPATIBLE = 1 (bug #51990)
+
+ 2017-09-11: Simon Goldschmidt
+ * tcp_in.c: fix bug #51937 (leaking tcp_pcbs on passive close with unacked data)
+
+ 2017-08-11: Joel Cunningham
+ * lwip_itoa: fix converting the number 0 (previously converted to '\0') (bug #51729)
+
+ 2017-08-08: Dirk Ziegelmeier
+ * ip4_route_src: parameter order is reversed: ip4_route_src(dest, src) -> ip4_route_src(src, dest)
+ to make parameter order consistent with other ip*_route*() functions
+ Same also applies to LWIP_HOOK_IP4_ROUTE_SRC() parameter order.
+
+ 2017-08-04: Joel Cunningham
+ * tcp: re-work persist timer to fully close window (details in bug #50837)
+
+ 2017-07-26: Simon Goldschmidt
+ * snmp_msg.c: fix bug #51578 (SNMP failed to decode some values on non 32bit platforms)
+
+ 2017-07-20: Simon Goldschmidt
+ * compatibility headers: moved from 'src/include/posix' to 'src/include/compat/posix',
+ 'src/include/compat/stdc' etc.
+
+ 2017-05-09: Joel Cunningham
+ * tcp: add zero-window probe timeout (bug #50837)
+
+ 2017-04-11: Simon Goldschmidt
+ * sockets.c: task #14420 (Remove sys_sem_signal from inside SYS_ARCH_PROTECT
+ crit section) done for LWIP_TCPIP_CORE_LOCKING==1
+
+ 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-24: Simon Goldschmidt
+ * sockets.c: fixed close race conditions in lwip_select (for LWIP_NETCONN_FULLDUPLEX)
+
+ 2017-02-24: Simon Goldschmidt
+ * sockets.c: fixed that select ignored invalid/not open sockets in the fd_sets (bug #50392)
+
+ 2017-02-16: Simon Goldschmidt
+ * LWIP_NETCONN_FULLDUPLEX: fixed shutdown during write (bug #50274)
+
+ 2017-01-18: Dirk Ziegelmeier
+ * Fix zero-copy RX, see bug bug #50064. PBUF_REFs were not supported as ARP requests.
+
+ 2017-01-11: David van Moolenbroek
+ * Lots of IPv6 related fixes and improvements
+
+(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
@@ -55,42 +1045,16 @@ HISTORY
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-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-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-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 :-)
-
- 2011-05-14: Simon Goldschmidt (patch by Stéphane Lesage)
+ 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:
-
- 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
+ ++ Bugfixes:
2012-09-26: Simon Goldschmidt
* api_msg.c: fixed bug #37405 'err_tcp()' uses already freed 'netconn' object
@@ -104,33 +1068,10 @@ HISTORY
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-08: Simon Goldschmidt
- * tcp_out.c: fixed bug #36380: unsent_oversize mismatch in 1.4.1RC1 (this was
+ * tcp_out.c: fixed bug: #36380 unsent_oversize mismatch in 1.4.1RC1 (this was
a debug-check issue only)
- 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-27: Simon Goldschmidt
* vj.c: fixed bug #35756 header length calculation problem in ppp/vj.c
@@ -138,14 +1079,6 @@ HISTORY
* tcp_out.c: fixed bug #35945: SYN packet should provide the recv MSS not the
send MSS
- 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
-
2012-03-22: Simon Goldschmidt
* ip4.c: fixed bug #35927: missing refragmentaion in ip_forward
@@ -168,7 +1101,7 @@ HISTORY
* 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)
+ 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
@@ -305,9 +1238,6 @@ HISTORY
* tcp_in.c: fixed bug #34111 RST for ACK to listening pcb has wrong seqno
2011-08-24: Simon Goldschmidt
- * inet6.h: fixed bug #34124 struct in6_addr does not conform to the standard
-
- 2011-08-24: Simon Goldschmidt
* api_msg.c, sockets.c: fixed bug #33956 Wrong error returned when calling
accept() on UDP connections
@@ -356,6 +1286,9 @@ HISTORY
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)
@@ -893,11 +1826,11 @@ HISTORY
* 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)
+ 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)
+ 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.
@@ -1134,7 +2067,7 @@ HISTORY
LWIP_AUTOIP_CREATE_SEED_ADDR() returned address in host byte order instead
of network byte order
- 2009-10-11 Simon Goldschmidt (Jörg Kesten)
+ 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
@@ -1312,7 +2245,7 @@ HISTORY
2009-05-04 Simon Goldschmidt
* init.c: snmp was not initialized in lwip_init()
- 2009-05-04 Frédéric Bernon
+ 2009-05-04 Frédéric Bernon
* dhcp.c, netbios.c: Changes if IP_SOF_BROADCAST is enabled.
2009-05-03 Simon Goldschmidt
@@ -1332,7 +2265,7 @@ HISTORY
2009-05-01 Simon Goldschmidt
* ppp.c: bug #24228: Memory corruption with PPP and DHCP
- 2009-04-29 Frédéric Bernon
+ 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
@@ -1354,7 +2287,7 @@ HISTORY
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
+ 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)
@@ -1464,7 +2397,7 @@ HISTORY
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
+ 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.
@@ -1498,7 +2431,7 @@ HISTORY
* 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
+ 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).
@@ -1546,39 +2479,39 @@ HISTORY
* 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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
@@ -1591,7 +2524,7 @@ HISTORY
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
+ 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
@@ -1600,31 +2533,31 @@ HISTORY
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
+ 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
+ 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
+ 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
+ 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
+ 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
+ 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)"
@@ -1656,7 +2589,7 @@ HISTORY
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
+ 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
@@ -1673,7 +2606,7 @@ HISTORY
dns resolver function for netconn api (netconn_gethostbyname) and socket api
(gethostbyname/gethostbyname_r).
- 2007-11-15 Jim Pettinato, Frédéric Bernon
+ 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
@@ -1692,7 +2625,7 @@ HISTORY
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
+ 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
@@ -1700,14 +2633,14 @@ HISTORY
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
+ 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
+ 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
@@ -1727,23 +2660,23 @@ HISTORY
* 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
+ 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
+ 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
+ 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
+ 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
@@ -1755,7 +2688,7 @@ HISTORY
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
+ 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().
@@ -1763,20 +2696,20 @@ HISTORY
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
+ 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
+ 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
+ 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
@@ -1785,11 +2718,11 @@ HISTORY
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
+ 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
+ 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
@@ -1797,7 +2730,7 @@ HISTORY
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
+ 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)
@@ -1805,22 +2738,22 @@ HISTORY
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
+ 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
+ 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
+ 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
+ 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".
@@ -1835,15 +2768,15 @@ HISTORY
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)
+ 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
+ 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
+ 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).
@@ -1895,13 +2828,13 @@ HISTORY
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)
+ 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
+ 2007-06-30 Frédéric Bernon
* sockets.h, sockets.c: Implement MSG_PEEK flag for recv/recvfrom functions.
2007-06-21 Simon Goldschmidt
@@ -1938,7 +2871,7 @@ HISTORY
* 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
+ 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.
@@ -1951,7 +2884,7 @@ HISTORY
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)
+ 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
@@ -1972,11 +2905,11 @@ HISTORY
(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
+ 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
+ 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...
@@ -1993,7 +2926,7 @@ HISTORY
PBUF_POOL pbufs instead of the old pool implementation in pbuf.c to reduce
code size.
- 2007-05-11 Frédéric Bernon
+ 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
@@ -2021,7 +2954,7 @@ HISTORY
* 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
+ 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
@@ -2035,12 +2968,12 @@ HISTORY
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
+ 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
+ 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
@@ -2048,26 +2981,26 @@ HISTORY
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
+ 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
+ 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
+ 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
+ 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
@@ -2078,24 +3011,24 @@ HISTORY
* 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
+ 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
+ 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
+ 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
+ 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.
@@ -2103,7 +3036,7 @@ HISTORY
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
+ 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.
@@ -2123,19 +3056,19 @@ HISTORY
++ Bug fixes:
- 2008-03-17 Frédéric Bernon, Ed Kerekes
+ 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
+ 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
+ 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
@@ -2150,7 +3083,7 @@ HISTORY
* 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)
+ 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
@@ -2224,7 +3157,7 @@ HISTORY
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
+ 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.
@@ -2256,7 +3189,7 @@ HISTORY
2007-10-08 Simon Goldschmidt
* mem.c: lfree was not updated in mem_realloc!
- 2007-10-07 Frédéric Bernon
+ 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
@@ -2276,23 +3209,23 @@ HISTORY
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
+ 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
+ 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
+ 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
+ 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).
@@ -2301,19 +3234,19 @@ HISTORY
* 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
+ 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
+ 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
+ 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")
+ 2007-07-26 Frédéric Bernon (and "thedoctor")
* igmp.c: Fix bug #20595 to accept IGMPv3 "Query" messages.
2007-07-25 Simon Goldschmidt
@@ -2337,11 +3270,11 @@ HISTORY
* 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
+ 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
+ 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.
@@ -2378,13 +3311,13 @@ HISTORY
* 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
+ 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
+ 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...)
@@ -2409,11 +3342,11 @@ HISTORY
-> netconn_new_..() does not allocate a new connection for unsupported
protocols.
- 2007-06-13 Frédéric Bernon, Simon Goldschmidt
+ 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
+ 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.
@@ -2442,13 +3375,13 @@ HISTORY
2007-06-01 Simon Goldschmidt
* sockets.c: Checked in patch #5914: Moved sockopt processing into tcpip_thread.
- 2007-05-23 Frédéric Bernon
+ 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
+ 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).
@@ -2457,13 +3390,13 @@ HISTORY
* 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
+ 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
+ 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).
@@ -2487,7 +3420,7 @@ HISTORY
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
+ 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
@@ -2551,24 +3484,24 @@ HISTORY
"Constant is long" warnings with 16bit compilers. Contributed by
avatar@mmlab.cse.yzu.edu.tw
- 2007-04-05 Frédéric Bernon, Jonathan Larmour
+ 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
+ 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
+ 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
+ 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
+ 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.
@@ -2582,19 +3515,19 @@ HISTORY
* 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)
+ 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
+ 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
+ 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
+ 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).
@@ -2603,17 +3536,17 @@ HISTORY
* sockets.c: fixed bug #5067 (essentialy a signed/unsigned warning fixed
by casting to unsigned).
- 2007-03-21 Frédéric Bernon
+ 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
+ 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
+ 2007-03-21 Frédéric Bernon
* api_msg.c, igmp.c, igmp.h: Fix C++ style comments
2007-03-21 Kieran Mansley
@@ -2630,11 +3563,11 @@ HISTORY
* 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
+ 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
+ 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.
@@ -2643,11 +3576,11 @@ HISTORY
* 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
+ 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
+ 2007-03-19 Frédéric Bernon
* Remove unimplemented "memp_realloc" function from memp.h.
2007-03-11 Simon Goldschmidt
@@ -2659,7 +3592,7 @@ HISTORY
(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)
+ 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
@@ -2673,7 +3606,7 @@ HISTORY
* 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
+ 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().
@@ -2681,7 +3614,7 @@ HISTORY
* 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
+ 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
@@ -2693,15 +3626,15 @@ HISTORY
* err.h, err.c: fixed compiler warning "initialization dircards qualifiers
from pointer target type"
- 2007-03-05 Frédéric Bernon
+ 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
+ 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
+ 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).
@@ -2715,7 +3648,7 @@ HISTORY
* 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
+ 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().
@@ -2724,7 +3657,7 @@ HISTORY
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
+ 2007-03-02 Frédéric Bernon
* rawapi.txt: Fix documentation mismatch with etharp.h about etharp_tmr's call
interval.
diff --git a/lwip/CMakeLists.txt b/lwip/CMakeLists.txt
index 121892d..5bca601 100644
--- a/lwip/CMakeLists.txt
+++ b/lwip/CMakeLists.txt
@@ -1,5 +1,4 @@
set(LWIP_SOURCES
- src/core/timers.c
src/core/udp.c
src/core/memp.c
src/core/init.c
@@ -12,11 +11,12 @@ set(LWIP_SOURCES
src/core/mem.c
src/core/tcp_in.c
src/core/stats.c
+ src/core/ip.c
src/core/inet_chksum.c
src/core/ipv4/icmp.c
src/core/ipv4/ip4.c
src/core/ipv4/ip4_addr.c
- src/core/ipv4/ip_frag.c
+ src/core/ipv4/ip4_frag.c
src/core/ipv6/ip6.c
src/core/ipv6/nd6.c
src/core/ipv6/icmp6.c
diff --git a/lwip/FILES b/lwip/FILES
index 6625319..e6e0998 100644
--- a/lwip/FILES
+++ b/lwip/FILES
@@ -1,4 +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/lwip/README b/lwip/README
index a62cc4f..0884d27 100644
--- a/lwip/README
+++ b/lwip/README
@@ -10,28 +10,41 @@ while still having a full scale TCP. This making lwIP suitable for use
in embedded systems with tens of kilobytes of free RAM and room for
around 40 kilobytes of code ROM.
+
FEATURES
- * IP (Internet Protocol) including packet forwarding over multiple network
- interfaces
+ * 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
- * Specialized raw/native API for enhanced performance
+ * raw/native API for enhanced performance
* Optional Berkeley-like socket API
* DNS (Domain names resolver)
- * SNMP (Simple Network Management Protocol)
- * DHCP (Dynamic Host Configuration Protocol)
- * AUTOIP (for IPv4, conform with RFC 3927)
- * PPP (Point-to-Point Protocol)
- * ARP (Address Resolution Protocol) for Ethernet
+
+
+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,
@@ -40,38 +53,34 @@ and additions to the stack to further increase its usefulness.
Development of lwIP is hosted on Savannah, a central point for
software development, maintenance and distribution. Everyone can
-help improve lwIP by use of Savannah's interface, CVS and the
+help improve lwIP by use of Savannah's interface, Git and the
mailing list. A core team of developers will commit changes to the
-CVS source tree.
+Git source tree.
-The lwIP TCP/IP stack is maintained in the 'lwip' CVS module and
-contributions (such as platform ports) are in the 'contrib' module.
+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 CVS server access for users and
+See doc/savannah.txt for details on Git server access for users and
developers.
-Last night's CVS tar ball can be downloaded from:
- http://savannah.gnu.org/cvs.backups/lwip.tar.gz [CHANGED - NEEDS FIXING]
-
-The current CVS trees are web-browsable:
- http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/lwip/
- http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/contrib/
+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
-The original out-dated homepage of lwIP and Adam Dunkels' papers on
-lwIP are at the official lwIP home page:
- http://www.sics.se/~adam/lwip/
+DOCUMENTATION
-Self documentation of the source code is regularly extracted from the
-current CVS sources and is available from this web page:
+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 growin wiki about lwIP at
+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
@@ -80,10 +89,12 @@ 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/lwip/UPGRADING b/lwip/UPGRADING
index 6501107..85be6c9 100644
--- a/lwip/UPGRADING
+++ b/lwip/UPGRADING
@@ -4,12 +4,143 @@ application written for an older version of lwIP to correctly work
with newer versions.
-(CVS HEAD)
+(git master)
* [Enter new changes just after this line - do not remove this line]
++ Application changes:
+ * ip4_route_src: parameter order is reversed: ip4_route_src(dest, src) -> ip4_route_src(src, dest)
+ to make parameter order consistent with other ip*_route*() functions.
+ Same also applies to LWIP_HOOK_IP4_ROUTE_SRC() parameter order.
+
+ * pbuf API: pbuf->type (an u8_t holding the enum 'pbuf_type') has changed to only hold a
+ description of the pbuf (e.g. data following pbuf struct, data volatile, allocation
+ source heap/pool/etc.). As a consequence, applications can't test pbuf->type any more.
+ Use pbuf_match_type(pbuf, type) instead.
+
+ * socket API: according to the standard, SO_ERROR now only returns asynchronous errors.
+ All other/normal/synchronous errors are (and always were) available via 'errno'.
+ LWIP_SOCKET_SET_ERRNO has been removed - 'errno' is always set - and required!
+
+ * compatibility headers: moved from 'src/include/posix' to 'src/include/compat/posix',
+ 'src/include/compat/stdc' etc.
+
+ * The IPv6 implementation now supports address scopes. All addresses that have a scope according
+ to the default policy (link-local unicast addresses, interface-local and link-local multicast
+ addresses) should now have a zone set on them before being passed to the core API, although
+ lwIP will currently attempt to select a zone on the caller's behalf when necessary.
+ Applications that directly assign IPv6 addresses to interfaces (which is NOT recommended)
+ must now ensure that link-local addresses carry the netif's zone. See the new ip6_zone.h header
+ file for more information and relevant macros. For now it is still possible to turn off scopes
+ support through the new LWIP_IPV6_SCOPES option. When upgrading an implementation that uses the
+ core API directly, it is highly recommended to enable LWIP_IPV6_SCOPES_DEBUG at least for
+ a while, to ensure e.g. proper address initialization.
+
+ * LWIP_HOOK_DHCP_APPEND_OPTIONS() has changed, see description in opt.h (options_out_len is not
+ available in struct dhcp any more)
+
+(2.0.2)
+
+ ++ Application changes:
+
+ * slipif: The way to pass serial port number has changed. netif->num is not
+ supported any more, netif->state is interpreted as an u8_t port number now
+ (it's not a POINTER to an u8_t any more!)
+
+(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).
diff --git a/lwip/astylerc b/lwip/astylerc
new file mode 100644
index 0000000..3f2209a
--- /dev/null
+++ b/lwip/astylerc
@@ -0,0 +1,22 @@
+# lwIP astyle formatting options
+
+# NOT FINISHED - DON'T USE
+
+# braces and indent
+style=otbs
+indent=spaces=2
+attach-extern-c
+#attach-closing-while
+# indentation
+indent-switches
+#max-continuation-indent=40
+# padding
+pad-oper
+pad-comma
+pad-header
+align-pointer=name
+# formatting
+break-one-line-headers
+keep-one-line-blocks
+# don't use "other options" (e.g. formatted) in this file
+# send them as additional command line options
diff --git a/lwip/custom/arch/cc.h b/lwip/custom/arch/cc.h
index 653a2e2..4af94aa 100644
--- a/lwip/custom/arch/cc.h
+++ b/lwip/custom/arch/cc.h
@@ -42,14 +42,6 @@
#include <misc/byteorder.h>
#include <base/BLog.h>
-#define u8_t uint8_t
-#define s8_t int8_t
-#define u16_t uint16_t
-#define s16_t int16_t
-#define u32_t uint32_t
-#define s32_t int32_t
-#define mem_ptr_t uintptr_t
-
#define PACK_STRUCT_BEGIN B_START_PACKED
#define PACK_STRUCT_END B_END_PACKED
#define PACK_STRUCT_STRUCT B_PACKED
@@ -57,17 +49,8 @@
#define LWIP_PLATFORM_DIAG(x) { if (BLog_WouldLog(BLOG_CHANNEL_lwip, BLOG_INFO)) { BLog_Begin(); BLog_Append x; BLog_Finish(BLOG_CHANNEL_lwip, BLOG_INFO); } }
#define LWIP_PLATFORM_ASSERT(x) { fprintf(stderr, "%s: lwip assertion failure: %s\n", __FUNCTION__, (x)); abort(); }
-#define U16_F PRIu16
-#define S16_F PRId16
-#define X16_F PRIx16
-#define U32_F PRIu32
-#define S32_F PRId32
-#define X32_F PRIx32
-#define SZT_F "zu"
-
-#define LWIP_PLATFORM_BYTESWAP 1
-#define LWIP_PLATFORM_HTONS(x) hton16(x)
-#define LWIP_PLATFORM_HTONL(x) hton32(x)
+#define lwip_htons(x) hton16(x)
+#define lwip_htonl(x) hton32(x)
#define LWIP_RAND() ( \
(((uint32_t)(rand() & 0xFF)) << 24) | \
diff --git a/lwip/custom/arch/perf.h b/lwip/custom/arch/perf.h
deleted file mode 100644
index 09c9d47..0000000
--- a/lwip/custom/arch/perf.h
+++ /dev/null
@@ -1,36 +0,0 @@
-/**
- * @file perf.h
- * @author Ambroz Bizjak <ambrop7@gmail.com>
- *
- * @section LICENSE
- *
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright
- * notice, this list of conditions and the following disclaimer in the
- * documentation and/or other materials provided with the distribution.
- * 3. Neither the name of the author 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 AUTHOR BE LIABLE FOR ANY
- * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
- * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
- * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
- * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
- * (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_CUSTOM_PERF_H
-#define LWIP_CUSTOM_PERF_H
-
-#define PERF_START
-#define PERF_STOP(x)
-
-#endif
diff --git a/lwip/custom/lwipopts.h b/lwip/custom/lwipopts.h
index 64e03ec..4bf0913 100644
--- a/lwip/custom/lwipopts.h
+++ b/lwip/custom/lwipopts.h
@@ -31,6 +31,7 @@
#define LWIP_CUSTOM_LWIPOPTS_H
#define NO_SYS 1
+#define LWIP_TIMERS 0
#define MEM_ALIGNMENT 4
#define LWIP_ARP 0
@@ -67,4 +68,17 @@
#define MEM_LIBC_MALLOC 1
#define MEMP_MEM_MALLOC 1
+#define LWIP_PERF 0
+#define SYS_LIGHTWEIGHT_PROT 0
+#define LWIP_DONT_PROVIDE_BYTEORDER_FUNCTIONS
+
+/*
+#define LWIP_DEBUG 1
+#define IP_DEBUG LWIP_DBG_ON
+#define NETIF_DEBUG LWIP_DBG_ON
+#define TCP_DEBUG LWIP_DBG_ON
+#define TCP_INPUT_DEBUG LWIP_DBG_ON
+#define TCP_OUTPUT_DEBUG LWIP_DBG_ON
+*/
+
#endif
diff --git a/lwip/custom/sys.c b/lwip/custom/sys.c
index efd1455..dbc1f20 100644
--- a/lwip/custom/sys.c
+++ b/lwip/custom/sys.c
@@ -35,3 +35,7 @@ u32_t sys_now (void)
{
return btime_gettime();
}
+
+void tcp_timer_needed(void)
+{
+}
diff --git a/lwip/doc/FILES b/lwip/doc/FILES
index 05d356f..e588575 100644
--- a/lwip/doc/FILES
+++ b/lwip/doc/FILES
@@ -1,6 +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.
-snmp_agent.txt - The documentation for the lwIP SNMP agent.
sys_arch.txt - The documentation for a system abstraction layer of lwIP.
+ppp.txt - Documentation of the PPP interface for lwIP.
diff --git a/lwip/doc/NO_SYS_SampleCode.c b/lwip/doc/NO_SYS_SampleCode.c
new file mode 100644
index 0000000..f0af660
--- /dev/null
+++ b/lwip/doc/NO_SYS_SampleCode.c
@@ -0,0 +1,122 @@
+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_start(&netif );
+ 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/lwip/doc/ZeroCopyRx.c b/lwip/doc/ZeroCopyRx.c
new file mode 100644
index 0000000..525b28f
--- /dev/null
+++ b/lwip/doc/ZeroCopyRx.c
@@ -0,0 +1,39 @@
+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);
+ }
+}
diff --git a/lwip/doc/contrib.txt b/lwip/doc/contrib.txt
index 39596fc..6f0d7bc 100644
--- a/lwip/doc/contrib.txt
+++ b/lwip/doc/contrib.txt
@@ -34,30 +34,25 @@ features of Savannah help us not lose users' input.
2.3 Bug reports and patches:
1. Make sure you are reporting bugs or send patches against the latest
- sources. (From the latest release and/or the current CVS sources.)
+ 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 prefered way
+ 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. Trivial patches (compiler warning, indentation and spelling fixes or anything obvious which takes a line or two)
- can go to the lwip-users list. This is still the fastest way of interaction and the list is not so crowded
- as to allow for loss of fixes. Putting bugs on Savannah and subsequently closing them is too much an overhead
- for reporting a compiler warning fix.
-6. Patches should be specific to a single change or to related changes.Do not mix bugfixes with spelling and other
- trivial fixes unless the bugfix is trivial too.Do not reorganize code and rename identifiers in the same patch you
- change behaviour if not necessary.A patch is easier to read and understand if it's to the point and short than
+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 CVS access to submit and maintain your port in the contrib CVS module.
- \ No newline at end of file
+ can also ask for Git access to submit and maintain your port in the contrib Git module.
diff --git a/lwip/doc/doxygen/generate.bat b/lwip/doc/doxygen/generate.bat
new file mode 100644
index 0000000..99afb12
--- /dev/null
+++ b/lwip/doc/doxygen/generate.bat
@@ -0,0 +1 @@
+doxygen lwip.Doxyfile
diff --git a/lwip/doc/doxygen/generate.sh b/lwip/doc/doxygen/generate.sh
new file mode 100755
index 0000000..89344b0
--- /dev/null
+++ b/lwip/doc/doxygen/generate.sh
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+doxygen lwip.Doxyfile
diff --git a/lwip/doc/doxygen/lwip.Doxyfile b/lwip/doc/doxygen/lwip.Doxyfile
new file mode 100644
index 0000000..c8aaaab
--- /dev/null
+++ b/lwip/doc/doxygen/lwip.Doxyfile
@@ -0,0 +1,2510 @@
+# 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.3"
+
+# 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 \
+ LWIP_ALTCP=1 \
+ LWIP_ALTCP_TLS=1 \
+ LWIP_IPV6_SCOPES=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_NETIF_EXT_STATUS_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 \
+ HTTPD_ENABLE_HTTPS=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/lwip/doc/doxygen/main_page.h b/lwip/doc/doxygen/main_page.h
new file mode 100644
index 0000000..efe11a6
--- /dev/null
+++ b/lwip/doc/doxygen/main_page.h
@@ -0,0 +1,138 @@
+/**
+ * @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 netifs NETIFs
+ *
+ * @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
+ */
+
+/**
+ * @page zerocopyrx Zero-copy RX
+ * The following code is an example for zero-copy RX ethernet driver:
+ * @include ZeroCopyRx.c
+ */
+
+/**
+ * @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/lwip/doc/doxygen/output/index.html b/lwip/doc/doxygen/output/index.html
new file mode 100644
index 0000000..a52e09f
--- /dev/null
+++ b/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/lwip/doc/mdns.txt b/lwip/doc/mdns.txt
new file mode 100644
index 0000000..c5929b7
--- /dev/null
+++ b/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 (like other apps) needs a strncasecmp() implementation. If you have one, define
+'lwip_strnicmp' 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/lwip/doc/mqtt_client.txt b/lwip/doc/mqtt_client.txt
new file mode 100644
index 0000000..7fd93a8
--- /dev/null
+++ b/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/lwip/doc/ppp.txt b/lwip/doc/ppp.txt
new file mode 100644
index 0000000..8b88b3a
--- /dev/null
+++ b/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/lwip/doc/rawapi.txt b/lwip/doc/rawapi.txt
index 8c19030..813ddad 100644
--- a/lwip/doc/rawapi.txt
+++ b/lwip/doc/rawapi.txt
@@ -8,6 +8,12 @@ to use for communication with the TCP/IP code:
* 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
@@ -22,14 +28,17 @@ on other platforms (e.g. unix / windows etc.). However, due to limitations
in the specification of this API, there might be incompatibilities
that require small modifications of existing programs.
-** Threading
+** 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"). The raw API may only be used from
-this thread! Application threads using the sequential- or socket API
-communicate with this main thread through message passing.
+(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
@@ -38,6 +47,7 @@ communicate with this main thread through message passing.
- netbuf.h
- netdb.h
- netifapi.h
+ - pppapi.h
- sockets.h
- sys.h
@@ -46,13 +56,18 @@ communicate with this main thread through message passing.
since they are protected by SYS_LIGHTWEIGHT_PROT and/or
semaphores.
- Only since 1.3.0, if SYS_LIGHTWEIGHT_PROT is set to 1
- and LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT is set to 1,
+ 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. **
@@ -71,13 +86,28 @@ the raw TCP/IP interface are more difficult to understand. Still, this
is the preferred way of writing applications that should be small in
code size and memory usage.
-Both APIs can be used simultaneously by different application
+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. Each callback is an ordinary
+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,
@@ -141,15 +171,6 @@ incoming connections or be explicitly connected to another host.
in the listen queue to the value specified by the backlog argument.
To use it, your need to set TCP_LISTEN_BACKLOG=1 in your lwipopts.h.
-- void tcp_accepted(struct tcp_pcb *pcb)
-
- Inform lwIP that an incoming connection has been accepted. This would
- usually be called from the accept callback. This allows lwIP to perform
- housekeeping tasks, such as allowing further incoming connections to be
- queued in the listen backlog.
- ATTENTION: the PCB passed in must be the listening pcb, not the pcb passed
- into the accept callback!
-
- void tcp_accept(struct tcp_pcb *pcb,
err_t (* accept)(void *arg, struct tcp_pcb *newpcb,
err_t err))
@@ -196,7 +217,7 @@ callback function.
should be allocated and the data should only be referenced by pointer. This
also means that the memory behind dataptr must not change until the data is
ACKed by the remote host
- - TCP_WRITE_FLAG_MORE: indicates that more data follows. If this is given,
+ - 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.
@@ -302,17 +323,6 @@ function to be called is set using the tcp_err() function.
parameter since the pcb may already have been deallocated.
---- Lower layer TCP interface
-
-TCP provides a simple interface to the lower layers of the
-system. During system initialization, the function tcp_init() has
-to be called before any other TCP function is called. When the system
-is running, the two timer functions tcp_fasttmr() and tcp_slowtmr()
-must be called with regular intervals. The tcp_fasttmr() should be
-called every TCP_FAST_INTERVAL milliseconds (defined in tcp.h) and
-tcp_slowtmr() should be called every TCP_SLOW_INTERVAL milliseconds.
-
-
--- UDP interface
The UDP interface is similar to that of TCP, but due to the lower
@@ -363,9 +373,9 @@ level of complexity of UDP, the interface is significantly simpler.
--- System initalization
-A truly complete and generic sequence for initializing the lwip stack
-cannot be given because it depends on the build configuration (lwipopts.h)
-and additional initializations for your runtime environment (e.g. timers).
+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
@@ -373,51 +383,13 @@ UDP and TCP transport layers, IPv4 and the DHCP client.
Call these functions in the order of appearance:
-- stats_init()
-
- Clears the structure where runtime statistics are gathered.
-
-- sys_init()
-
- Not of much use since we set the NO_SYS 1 option in lwipopts.h,
- to be called for easy configuration changes.
-
-- mem_init()
-
- Initializes the dynamic memory heap defined by MEM_SIZE.
+- lwip_init()
-- memp_init()
+ Initialize the lwIP stack and all of its subsystems.
- Initializes the memory pools defined by MEMP_NUM_x.
-
-- pbuf_init()
-
- Initializes the pbuf memory pool defined by PBUF_POOL_SIZE.
-
-- etharp_init()
-
- Initializes the ARP table and queue.
- Note: you must call etharp_tmr at a ARP_TMR_INTERVAL (5 seconds) regular interval
- after this initialization.
-
-- ip_init()
-
- Doesn't do much, it should be called to handle future changes.
-
-- udp_init()
-
- Clears the UDP PCB list.
-
-- tcp_init()
-
- Clears the TCP PCB list and clears some internal TCP timers.
- Note: you must call tcp_fasttmr() and tcp_slowtmr() at the
- predefined regular intervals after this initialization.
-
-- netif_add(struct netif *netif, ip_addr_t *ipaddr,
- ip_addr_t *netmask, ip_addr_t *gw,
- void *state, err_t (* init)(struct netif *netif),
- err_t (* input)(struct pbuf *p, struct netif *netif))
+- 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.
@@ -425,18 +397,20 @@ Call these functions in the order of appearance:
or fill them with sane numbers otherwise. The state pointer may be NULL.
The init function pointer must point to a initialization function for
- your ethernet netif interface. The following code illustrates it's use.
+ 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];
+ 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
+ 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".
@@ -444,20 +418,32 @@ Call these functions in the order of appearance:
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)
- When the netif is fully configured this function must be called.
+ 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.
- Note: you must call dhcp_fine_tmr() and dhcp_coarse_tmr() at
- the predefined regular intervals after starting the client.
You can peek in the netif->dhcp struct for the actual DHCP status.
+- 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
+--- Optimization hints
The first thing you want to optimize is the lwip_standard_checksum()
routine from src/core/inet.c. You can override this standard
@@ -470,9 +456,11 @@ introduction to this subject.
Other significant improvements can be made by supplying
assembly or inline replacements for htons() and htonl()
if you're using a little-endian architecture.
-#define LWIP_PLATFORM_BYTESWAP 1
-#define LWIP_PLATFORM_HTONS(x) <your_htons>
-#define LWIP_PLATFORM_HTONL(x) <your_htonl>
+#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
@@ -498,14 +486,13 @@ 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.
+This implies that *ALL* pbufs passed to send functions must *not* be reused by
+the application unless the send function returns an error indicating the pbuf
+is not sent/queued for sending.
-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!
+Also, data passed to tcp_write without the copy-flag must not be changed until
+sent and ACKed (check the amount of bytes marked as 'sent')!
Therefore, be careful which type of PBUF you use and if you copy TCP data
or not!
diff --git a/lwip/doc/savannah.txt b/lwip/doc/savannah.txt
index 409905b..d7d19eb 100644
--- a/lwip/doc/savannah.txt
+++ b/lwip/doc/savannah.txt
@@ -2,37 +2,34 @@ Daily Use Guide for using Savannah for lwIP
Table of Contents:
-1 - Obtaining lwIP from the CVS repository
-2 - Committers/developers CVS access using SSH (to be written)
-3 - Merging from DEVEL branch to main trunk (stable branch)
+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 CVS repository
+1 Obtaining lwIP from the Git repository
----------------------------------------
-To perform an anonymous CVS checkout of the main trunk (this is where
+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
-cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout lwip
-
Or, obtain a stable branch (updated with bug fixes only) as follows:
-cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \
- -r STABLE-0_7 -d lwip-0.7 lwip
+ git clone --branch DEVEL-1_4_1 git://git.savannah.nongnu.org/lwip.git
Or, obtain a specific (fixed) release as follows:
-cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \
- -r STABLE-0_7_0 -d lwip-0.7.0 lwip
+ git clone --branch STABLE-1_4_1 git://git.savannah.nongnu.org/lwip.git
-3 Committers/developers CVS access using SSH
+
+2 Committers/developers Git access using SSH
--------------------------------------------
The Savannah server uses SSH (Secure Shell) protocol 2 authentication and encryption.
-As such, CVS commits to the server occur through a SSH tunnel for project members.
+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
+ 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.
@@ -41,95 +38,83 @@ Now paste the id_dsa.pub contents into your Savannah account public key list. Wa
a while so that Savannah can update its configuration (This can take minutes).
Try to login using SSH:
-
-ssh -v your_login@cvs.sv.gnu.org
+ 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
-Authenticating with public key "your_key_name"...
-Server refused to allocate pty
+ 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 CVS only. Now, you should be able to do this:
+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
-export CVS_RSH=ssh
-cvs -z3 -d:ext:your_login@cvs.sv.gnu.org:/sources/lwip co lwip
-
-after which you can edit your local files with bug fixes or new features and
-commit them. Make sure you know what you are doing when using CVS to make
+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 http://savannah.nongnu.org/cvs/?group=lwip)
-
+fingerprint against https://savannah.nongnu.org/git/?group=lwip
-3 Merging from DEVEL branch to main trunk (stable)
---------------------------------------------------
-Merging is a delicate process in CVS and requires the
-following disciplined steps in order to prevent conflicts
-in the future. Conflicts can be hard to solve!
+3 - Merging a development branch to master branch
+-------------------------------------------------
-Merging from branch A to branch B requires that the A branch
-has a tag indicating the previous merger. This tag is called
-'merged_from_A_to_B'. After merging, the tag is moved in the
-A branch to remember this merger for future merge actions.
+Merging is a straightforward process in Git. How to merge all changes in a
+development branch since our last merge from main:
-IMPORTANT: AFTER COMMITTING A SUCCESFUL MERGE IN THE
-REPOSITORY, THE TAG MUST BE SET ON THE SOURCE BRANCH OF THE
-MERGE ACTION (REPLACING EXISTING TAGS WITH THE SAME NAME).
+Checkout the master branch:
+ git checkout master
-Merge all changes in DEVEL since our last merge to main:
+Merge the development branch to master:
+ git merge your-development-branch
-In the working copy of the main trunk:
-cvs update -P -jmerged_from_DEVEL_to_main -jDEVEL
+Resolve any conflict.
-(This will apply the changes between 'merged_from_DEVEL_to_main'
-and 'DEVEL' to your work set of files)
+Commit the merge result.
+ git commit -a
-We can now commit the merge result.
-cvs commit -R -m "Merged from DEVEL to main."
+Push your commits:
+ git push
-If this worked out OK, we now move the tag in the DEVEL branch
-to this merge point, so we can use this point for future merges:
-
-cvs rtag -F -r DEVEL merged_from_DEVEL_to_main lwip
4 How to release lwIP
---------------------
-First, checkout a clean copy of the branch to be released. Tag this set with
-tag name "STABLE-0_6_3". (I use release number 0.6.3 throughout this example).
-
-Login CVS using pserver authentication, then export a clean copy of the
-tagged tree. Export is similar to a checkout, except that the CVS metadata
-is not created locally.
+First, tag the release using Git: (I use release number 1.4.1 throughout
+this example).
+ git tag -a STABLE-1_4_1
-export CVS_RSH=ssh
-cvs -z3 -d:pserver:anonymous@cvs.sv.gnu.org:/sources/lwip checkout \
- -r STABLE-0_6_3 -d lwip-0.6.3 lwip
+Share the tag reference by pushing it to remote:
+ git push origin STABLE-1_4_1
-Archive this directory using tar, gzip'd, bzip2'd and zip'd.
+Prepare the release:
+ cp -r lwip lwip-1.4.1
+ rm -rf lwip-1.4.1/.git lwip-1.4.1/.gitattributes
-tar czvf lwip-0.6.3.tar.gz lwip-0.6.3
-tar cjvf lwip-0.6.3.tar.bz2 lwip-0.6.3
-zip -r lwip-0.6.3.zip lwip-0.6.3
+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-0.6.3.tar.gz
-gpg -b lwip-0.6.3.tar.bz2
-gpg -b lwip-0.6.3.zip
+ 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 *0.6.3.*
+ 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 0.6.3 release is now available here:
-http://savannah.nongnu.org/files/?group=lwip&highlight=0.6.3
+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. \ No newline at end of file
+this via the Administrator News interface.
diff --git a/lwip/doc/snmp_agent.txt b/lwip/doc/snmp_agent.txt
deleted file mode 100644
index 2653230..0000000
--- a/lwip/doc/snmp_agent.txt
+++ /dev/null
@@ -1,181 +0,0 @@
-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/lwip/doc/sys_arch.txt b/lwip/doc/sys_arch.txt
index 847cd77..bb9da35 100644
--- a/lwip/doc/sys_arch.txt
+++ b/lwip/doc/sys_arch.txt
@@ -1,6 +1,7 @@
-sys_arch interface for lwIP 0.6++
+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
@@ -9,12 +10,11 @@ small changes to a few header files and a new sys_arch
implementation. It is also possible to do a sys_arch implementation
that does not rely on any underlying operating system.
-The sys_arch provides semaphores and mailboxes to lwIP. For the full
+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. Previous versions of lwIP required the sys_arch to
-implement timer scheduling as well but as of lwIP 0.5 this is
-implemented in a higher layer.
+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
@@ -22,19 +22,18 @@ macros used throughout lwip. The files required and the macros they
must define are listed below the sys_arch description.
Semaphores can be either counting or binary - lwIP works with both
-kinds. Mailboxes are used for message passing and can be implemented
-either as a queue which allows multiple messages to be posted to a
-mailbox, or as a rendez-vous point where only one message can be
-posted at a time. lwIP works with both kinds, but the former type will
-be more efficient. A message in a mailbox is just a pointer, nothing
-more.
+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". lwIP does not place any restrictions on how
-sys_sem_t or sys_mbox_t are represented internally.
+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 and mailbox functions are prototyped in a way that
+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).
@@ -65,17 +64,14 @@ The following functions must be implemented by the sys_arch:
- 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.
+ 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.
+ The return value is SYS_ARCH_TIMEOUT if the semaphore wasn't signaled within
+ the specified time or any other value if it was signaled (with or without
+ waiting).
Notice that lwIP implements a function with a similar name,
sys_sem_wait(), that uses the sys_arch_sem_wait() function.
@@ -94,6 +90,40 @@ The following functions must be implemented by the sys_arch:
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
@@ -131,8 +161,8 @@ The following functions must be implemented by the sys_arch:
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.
+ SYS_ARCH_TIMEOUT if there was a timeout, any other value if a messages
+ is received.
Note that a function with a similar name, sys_mbox_fetch(), is
implemented by lwIP.
@@ -176,26 +206,35 @@ to be implemented as well:
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.
+ This optional function does a "fast" critical region protection. 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.
+
+ The return value is opaque to lwip and passed to the sys_arch_unprotect() call
+ matching the sys_arch_protect() call at the same nesting level. This value
+ might be used to restore the status. However implementations may depend on
+ every call to sys_arch_protect() having a matching call to sys_arch_unprotect()
+ and thus can use a nesting count or a recursive mutex.
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.
+ This optional function does a "fast" exit of critical region protection
+ nesting level. The value passed in pval is the opaque value returned the
+ respective call to sys_arch_protect(). 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:
@@ -209,7 +248,7 @@ For some configurations, you also need:
Note:
-Be carefull with using mem_malloc() in sys_arch. When malloc() refers to
+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.
@@ -246,8 +285,9 @@ cc.h - Architecture environment, some compiler specific, some
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.
+ the standard *nix error codes (or define LWIP_ERRNO_INCLUDE to that file name),
+ 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.
diff --git a/lwip/lwip-base-version b/lwip/lwip-base-version
index b48d5b6..ce5f390 100644
--- a/lwip/lwip-base-version
+++ b/lwip/lwip-base-version
@@ -1 +1 @@
-666e84eef281d0059377d0f5029c1c550488f829
+931b5e643c25820a99bb8df94ab37db6b58c446b
diff --git a/lwip/src/FILES b/lwip/src/FILES
index 952aeab..0be0741 100644
--- a/lwip/src/FILES
+++ b/lwip/src/FILES
@@ -1,13 +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,
- as well as the ARP module.
+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/lwip/src/Filelists.mk b/lwip/src/Filelists.mk
new file mode 100644
index 0000000..b6601b7
--- /dev/null
+++ b/lwip/src/Filelists.mk
@@ -0,0 +1,198 @@
+#
+# 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/altcp.c \
+ $(LWIPDIR)/core/altcp_tcp.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/if_api.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/bridgeif.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_snmpv2_framework.c \
+ $(LWIPDIR)/apps/snmp/snmp_snmpv2_usm.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
+
+# HTTPDFILES: HTTP server
+HTTPDFILES=$(LWIPDIR)/apps/httpd/fs.c \
+ $(LWIPDIR)/apps/httpd/httpd.c
+
+# MAKEFSDATA: MAKEFSDATA HTTP server host utility
+MAKEFSDATAFILES=$(LWIPDIR)/apps/httpd/makefsdata/makefsdata.c
+
+# LWIPERFFILES: IPERF server
+LWIPERFFILES=$(LWIPDIR)/apps/lwiperf/lwiperf.c
+
+# SMTPFILES: SMTP client
+SMTPFILES=$(LWIPDIR)/apps/smtp/smtp.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
+
+# MBEDTLS_FILES: MBEDTLS related files of lwIP rep
+MBEDTLS_FILES=$(LWIPDIR)/apps/altcp_tls/altcp_tls_mbedtls.c \
+ $(LWIPDIR)/apps/altcp_tls/altcp_tls_mbedtls_mem.c \
+ $(LWIPDIR)/apps/snmp/snmpv3_mbedtls.c
+
+# LWIPAPPFILES: All LWIP APPs
+LWIPAPPFILES=$(SNMPFILES) \
+ $(HTTPDFILES) \
+ $(LWIPERFFILES) \
+ $(SMTPFILES) \
+ $(SNTPFILES) \
+ $(MDNSFILES) \
+ $(NETBIOSNSFILES) \
+ $(TFTPFILES) \
+ $(MQTTFILES) \
+ $(MBEDTLS_FILES)
diff --git a/lwip/src/api/api_lib.c b/lwip/src/api/api_lib.c
index adaaad4..b92cf3b 100644
--- a/lwip/src/api/api_lib.c
+++ b/lwip/src/api/api_lib.c
@@ -2,13 +2,30 @@
* @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -17,23 +34,22 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
*
+ * Author: Adam Dunkels <adam@sics.se>
*/
/* This is the part of the API that is linked with
@@ -44,16 +60,55 @@
#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
#include "lwip/api.h"
-#include "lwip/tcpip.h"
#include "lwip/memp.h"
#include "lwip/ip.h"
#include "lwip/raw.h"
#include "lwip/udp.h"
-#include "lwip/tcp.h"
+#include "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.
@@ -64,35 +119,42 @@
* @return a newly allocated struct netconn or
* NULL on memory error
*/
-struct netconn*
+struct netconn *
netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)
{
struct netconn *conn;
- struct api_msg msg;
+ API_MSG_VAR_DECLARE(msg);
+ API_MSG_VAR_ALLOC_RETURN_NULL(msg);
conn = netconn_alloc(t, callback);
if (conn != NULL) {
err_t err;
- msg.msg.msg.n.proto = proto;
- msg.msg.conn = conn;
- TCPIP_APIMSG((&msg), lwip_netconn_do_newconn, err);
+
+ 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 op_completed", sys_sem_valid(&conn->op_completed));
LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox));
#if LWIP_TCP
LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox));
#endif /* LWIP_TCP */
+#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.
@@ -103,21 +165,35 @@ netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_cal
err_t
netconn_delete(struct netconn *conn)
{
- struct api_msg msg;
+ 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;
}
- msg.function = lwip_netconn_do_delconn;
- msg.msg.conn = conn;
- tcpip_apimsg(&msg);
+ 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);
- /* don't care for return value of lwip_netconn_do_delconn since it only calls void functions */
-
return ERR_OK;
}
@@ -135,51 +211,104 @@ netconn_delete(struct netconn *conn)
err_t
netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
{
- struct api_msg msg;
+ 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;);
- msg.msg.conn = conn;
- msg.msg.msg.ad.ipaddr = ip_2_ipX(addr);
- msg.msg.msg.ad.port = port;
- msg.msg.msg.ad.local = local;
- TCPIP_APIMSG(&msg, lwip_netconn_do_getaddr, err);
+ 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);
- NETCONN_SET_SAFE_ERR(conn, err);
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 IP_ADDR_ANY
- * to bind to all addresses)
+ * @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, ip_addr_t *addr, u16_t port)
+netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port)
{
- struct api_msg msg;
+ API_MSG_VAR_DECLARE(msg);
err_t err;
LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
- msg.msg.conn = conn;
- msg.msg.msg.bc.ipaddr = addr;
- msg.msg.msg.bc.port = port;
- TCPIP_APIMSG(&msg, lwip_netconn_do_bind, err);
+#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);
- NETCONN_SET_SAFE_ERR(conn, err);
return err;
}
/**
+ * @ingroup netconn_common
+ * Bind a netconn to a specific interface and port.
+ * Binding one netconn twice might not always be checked correctly!
+ *
+ * @param conn the netconn to bind
+ * @param if_idx the local interface index to bind the netconn to
+ * @return ERR_OK if bound, any other err_t on failure
+ */
+err_t
+netconn_bind_if(struct netconn *conn, u8_t if_idx)
+{
+ API_MSG_VAR_DECLARE(msg);
+ err_t err;
+
+ LWIP_ERROR("netconn_bind_if: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+ API_MSG_VAR_REF(msg).msg.bc.if_idx = if_idx;
+ err = netconn_apimsg(lwip_netconn_do_bind_if, &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
@@ -188,63 +317,55 @@ netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port)
* @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise
*/
err_t
-netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port)
+netconn_connect(struct netconn *conn, const ip_addr_t *addr, u16_t port)
{
- struct api_msg msg;
+ API_MSG_VAR_DECLARE(msg);
err_t err;
LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
- msg.msg.conn = conn;
- msg.msg.msg.bc.ipaddr = addr;
- msg.msg.msg.bc.port = port;
-#if LWIP_TCP
-#if (LWIP_UDP || LWIP_RAW)
- if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
-#endif /* (LWIP_UDP || LWIP_RAW) */
- {
- /* The TCP version waits for the connect to succeed,
- so always needs to use message passing. */
- msg.function = lwip_netconn_do_connect;
- err = tcpip_apimsg(&msg);
+#if LWIP_IPV4
+ /* Don't propagate NULL pointer (IP_ADDR_ANY alias) to subsequent functions */
+ if (addr == NULL) {
+ addr = IP4_ADDR_ANY;
}
-#endif /* LWIP_TCP */
-#if (LWIP_UDP || LWIP_RAW) && LWIP_TCP
- else
-#endif /* (LWIP_UDP || LWIP_RAW) && LWIP_TCP */
-#if (LWIP_UDP || LWIP_RAW)
- {
- /* UDP and RAW only set flags, so we can use core-locking. */
- TCPIP_APIMSG(&msg, lwip_netconn_do_connect, err);
- }
-#endif /* (LWIP_UDP || LWIP_RAW) */
+#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);
- NETCONN_SET_SAFE_ERR(conn, err);
return err;
}
/**
+ * @ingroup netconn_udp
* Disconnect a netconn from its current peer (only valid for UDP netconns).
*
* @param conn the netconn to disconnect
- * @return TODO: return value is not set here...
+ * @return See @ref err_t
*/
err_t
netconn_disconnect(struct netconn *conn)
{
- struct api_msg msg;
+ API_MSG_VAR_DECLARE(msg);
err_t err;
LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;);
- msg.msg.conn = conn;
- TCPIP_APIMSG(&msg, lwip_netconn_do_disconnect, err);
+ 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);
- NETCONN_SET_SAFE_ERR(conn, err);
return err;
}
/**
+ * @ingroup netconn_tcp
* Set a TCP netconn into listen mode
*
* @param conn the tcp netconn to set to listen mode
@@ -256,7 +377,7 @@ err_t
netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
{
#if LWIP_TCP
- struct api_msg msg;
+ API_MSG_VAR_DECLARE(msg);
err_t err;
/* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */
@@ -264,13 +385,14 @@ netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;);
- msg.msg.conn = conn;
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
#if TCP_LISTEN_BACKLOG
- msg.msg.msg.lb.backlog = backlog;
+ API_MSG_VAR_REF(msg).msg.lb.backlog = backlog;
#endif /* TCP_LISTEN_BACKLOG */
- TCPIP_APIMSG(&msg, lwip_netconn_do_listen, err);
+ err = netconn_apimsg(lwip_netconn_do_listen, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
- NETCONN_SET_SAFE_ERR(conn, err);
return err;
#else /* LWIP_TCP */
LWIP_UNUSED_ARG(conn);
@@ -280,6 +402,7 @@ netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
}
/**
+ * @ingroup netconn_tcp
* Accept a new connection on a TCP listening netconn.
*
* @param conn the TCP listen netconn
@@ -291,45 +414,83 @@ err_t
netconn_accept(struct netconn *conn, struct netconn **new_conn)
{
#if LWIP_TCP
- struct netconn *newconn;
err_t err;
+ void *accept_ptr;
+ struct netconn *newconn;
#if TCP_LISTEN_BACKLOG
- struct api_msg msg;
+ 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;);
- LWIP_ERROR("netconn_accept: invalid acceptmbox", sys_mbox_valid(&conn->acceptmbox), return ERR_ARG;);
- err = conn->last_err;
- if (ERR_IS_FATAL(err)) {
- /* don't recv on fatal errors: this might block the application task
- waiting on acceptmbox forever! */
+ /* NOTE: Although the opengroup spec says a pending error shall be returned to
+ send/recv/getsockopt(SO_ERROR) only, we return it for listening
+ connections also, to handle embedded-system errors */
+ err = netconn_err(conn);
+ if (err != ERR_OK) {
+ /* return pending error */
return err;
}
+ if (conn->flags & NETCONN_FLAG_MBOXCLOSED) {
+ /* don't accept if closed: this might block the application task
+ waiting on acceptmbox forever! */
+ return ERR_CLSD;
+ }
+ if (!sys_mbox_valid(&conn->acceptmbox)) {
+ return ERR_CLSD;
+ }
+
+#if TCP_LISTEN_BACKLOG
+ /* need to allocate API message here so empty message pool does not result in event loss
+ * see bug #47512: MPU_COMPATIBLE may fail on empty pool */
+ API_MSG_VAR_ALLOC(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+ if (netconn_is_nonblocking(conn)) {
+ if (sys_arch_mbox_tryfetch(&conn->acceptmbox, &accept_ptr) == SYS_ARCH_TIMEOUT) {
+#if TCP_LISTEN_BACKLOG
+ API_MSG_VAR_FREE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+ return ERR_WOULDBLOCK;
+ }
+ } else {
#if LWIP_SO_RCVTIMEO
- if (sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
- NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
- return ERR_TIMEOUT;
- }
+ 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, (void **)&newconn, 0);
+ sys_arch_mbox_fetch(&conn->acceptmbox, &accept_ptr, 0);
#endif /* LWIP_SO_RCVTIMEO*/
+ }
/* Register event with callback */
API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
- if (newconn == NULL) {
+ if (lwip_netconn_is_err_msg(accept_ptr, &err)) {
+ /* a connection has been aborted: e.g. out of pcbs or out of netconns during accept */
+#if TCP_LISTEN_BACKLOG
+ API_MSG_VAR_FREE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+ return err;
+ }
+ if (accept_ptr == NULL) {
/* connection has been aborted */
- NETCONN_SET_SAFE_ERR(conn, ERR_ABRT);
- return ERR_ABRT;
+#if TCP_LISTEN_BACKLOG
+ API_MSG_VAR_FREE(msg);
+#endif /* TCP_LISTEN_BACKLOG */
+ return ERR_CLSD;
}
+ newconn = (struct netconn *)accept_ptr;
#if TCP_LISTEN_BACKLOG
/* Let the stack know that we have accepted the connection. */
- msg.msg.conn = conn;
+ API_MSG_VAR_REF(msg).conn = newconn;
/* don't care for the return value of lwip_netconn_do_recv */
- TCPIP_APIMSG_NOERR(&msg, 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;
@@ -343,72 +504,77 @@ netconn_accept(struct netconn *conn, struct netconn **new_conn)
}
/**
+ * @ingroup netconn_common
* Receive data: actual implementation that doesn't care whether pbuf or netbuf
- * is received
+ * is received (this is internal, it's just here for describing common errors)
*
* @param conn the netconn from which to receive data
* @param new_buf pointer where a new pbuf/netbuf is stored when received data
+ * @param apiflags flags that control function behaviour. For now only:
+ * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
* @return ERR_OK if data has been received, an error code otherwise (timeout,
* memory error or another error)
+ * ERR_CONN if not connected
+ * ERR_CLSD if TCP connection has been closed
+ * ERR_WOULDBLOCK if the netconn is nonblocking but would block to wait for data
+ * ERR_TIMEOUT if the netconn has a receive timeout and no data was received
*/
static err_t
-netconn_recv_data(struct netconn *conn, void **new_buf)
+netconn_recv_data(struct netconn *conn, void **new_buf, u8_t apiflags)
{
void *buf = NULL;
u16_t len;
- err_t err;
-#if LWIP_TCP
- struct api_msg msg;
-#endif /* LWIP_TCP */
LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
*new_buf = NULL;
LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
- LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
-
- err = conn->last_err;
- if (ERR_IS_FATAL(err)) {
- /* don't recv on fatal errors: this might block the application task
- waiting on recvmbox forever! */
- /* @todo: this does not allow us to fetch data that has been put into recvmbox
- before the fatal error occurred - is that a problem? */
- return err;
+
+ if (!sys_mbox_valid(&conn->recvmbox)) {
+ err_t err = netconn_err(conn);
+ if (err != ERR_OK) {
+ /* return pending error */
+ return err;
+ }
+ return ERR_CONN;
}
+ if (netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK) ||
+ (conn->flags & NETCONN_FLAG_MBOXCLOSED) || (conn->pending_err != ERR_OK)) {
+ if (sys_arch_mbox_tryfetch(&conn->recvmbox, &buf) == SYS_ARCH_TIMEOUT) {
+ err_t err = netconn_err(conn);
+ if (err != ERR_OK) {
+ /* return pending error */
+ return err;
+ }
+ if (conn->flags & NETCONN_FLAG_MBOXCLOSED) {
+ return ERR_CONN;
+ }
+ return ERR_WOULDBLOCK;
+ }
+ } else {
#if LWIP_SO_RCVTIMEO
- if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
- NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
- return ERR_TIMEOUT;
- }
+ if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+ return ERR_TIMEOUT;
+ }
#else
- sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
+ sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
#endif /* LWIP_SO_RCVTIMEO*/
+ }
#if LWIP_TCP
#if (LWIP_UDP || LWIP_RAW)
if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
#endif /* (LWIP_UDP || LWIP_RAW) */
{
- if (!netconn_get_noautorecved(conn) || (buf == NULL)) {
- /* Let the stack know that we have taken the data. */
- /* TODO: Speedup: Don't block and wait for the answer here
- (to prevent multiple thread-switches). */
- msg.msg.conn = conn;
- if (buf != NULL) {
- msg.msg.msg.r.len = ((struct pbuf *)buf)->tot_len;
- } else {
- msg.msg.msg.r.len = 1;
+ err_t err;
+ /* Check if this is an error message or a pbuf */
+ if (lwip_netconn_is_err_msg(buf, &err)) {
+ /* new_buf has been zeroed above already */
+ if (err == ERR_CLSD) {
+ /* connection closed translates to ERR_OK with *new_buf == NULL */
+ return ERR_OK;
}
- /* don't care for the return value of lwip_netconn_do_recv */
- TCPIP_APIMSG_NOERR(&msg, lwip_netconn_do_recv);
- }
-
- /* If we are closed, we indicate that we no longer wish to use the socket */
- if (buf == NULL) {
- API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
- /* Avoid to lose any previous error code */
- NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
- return ERR_CLSD;
+ return err;
}
len = ((struct pbuf *)buf)->tot_len;
}
@@ -436,25 +602,173 @@ netconn_recv_data(struct netconn *conn, void **new_buf)
return ERR_OK;
}
+#if LWIP_TCP
+static err_t
+netconn_tcp_recvd_msg(struct netconn *conn, size_t len, struct api_msg *msg)
+{
+ LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
+
+ msg->conn = conn;
+ msg->msg.r.len = len;
+
+ return netconn_apimsg(lwip_netconn_do_recv, msg);
+}
+
+err_t
+netconn_tcp_recvd(struct netconn *conn, size_t len)
+{
+ err_t err;
+ API_MSG_VAR_DECLARE(msg);
+ LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
+
+ API_MSG_VAR_ALLOC(msg);
+ err = netconn_tcp_recvd_msg(conn, len, &API_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
+ return err;
+}
+
+static err_t
+netconn_recv_data_tcp(struct netconn *conn, struct pbuf **new_buf, u8_t apiflags)
+{
+ err_t err;
+ struct pbuf *buf;
+#if LWIP_TCP
+ API_MSG_VAR_DECLARE(msg);
+#if LWIP_MPU_COMPATIBLE
+ msg = NULL;
+#endif
+#endif /* LWIP_TCP */
+
+ 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;
+ }
+
+ if (!(apiflags & NETCONN_NOAUTORCVD)) {
+ /* need to allocate API message here so empty message pool does not result in event loss
+ * see bug #47512: MPU_COMPATIBLE may fail on empty pool */
+ API_MSG_VAR_ALLOC(msg);
+ }
+
+ err = netconn_recv_data(conn, (void **)new_buf, apiflags);
+ if (err != ERR_OK) {
+ if (!(apiflags & NETCONN_NOAUTORCVD)) {
+ API_MSG_VAR_FREE(msg);
+ }
+ return err;
+ }
+ buf = *new_buf;
+ if (!(apiflags & NETCONN_NOAUTORCVD)) {
+ /* Let the stack know that we have taken the data. */
+ u16_t len = buf ? buf->tot_len : 1;
+ /* don't care for the return value of lwip_netconn_do_recv */
+ /* @todo: this should really be fixed, e.g. by retrying in poll on error */
+ netconn_tcp_recvd_msg(conn, len, &API_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 */
+ err = netconn_err(conn);
+ if (err != ERR_OK) {
+ return err;
+ }
+ return ERR_RST;
+ }
+ /* 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;
+ }
+ return err;
+}
+
/**
+ * @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)
+ * memory error or another error, @see netconn_recv_data)
* 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) &&
+ LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
- return netconn_recv_data(conn, (void **)new_buf);
+ return netconn_recv_data_tcp(conn, new_buf, 0);
}
/**
+ * @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
+ * @param apiflags flags that control function behaviour. For now only:
+ * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error, @see netconn_recv_data)
+ * ERR_ARG if conn is not a TCP netconn
+ */
+err_t
+netconn_recv_tcp_pbuf_flags(struct netconn *conn, struct pbuf **new_buf, u8_t apiflags)
+{
+ LWIP_ERROR("netconn_recv_tcp_pbuf: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
+
+ return netconn_recv_data_tcp(conn, new_buf, apiflags);
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Receive data (in form of a netbuf) from a UDP or RAW 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_ARG if conn is not a UDP/RAW netconn
+ */
+err_t
+netconn_recv_udp_raw_netbuf(struct netconn *conn, struct netbuf **new_buf)
+{
+ LWIP_ERROR("netconn_recv_udp_raw_netbuf: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) != NETCONN_TCP, return ERR_ARG;);
+
+ return netconn_recv_data(conn, (void **)new_buf, 0);
+}
+
+/**
+ * Receive data (in form of a netbuf) from a UDP or RAW netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new netbuf is stored when received data
+ * @param apiflags flags that control function behaviour. For now only:
+ * - NETCONN_DONTBLOCK: only read data that is available now, don't wait for more 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 UDP/RAW netconn
+ */
+err_t
+netconn_recv_udp_raw_netbuf_flags(struct netconn *conn, struct netbuf **new_buf, u8_t apiflags)
+{
+ LWIP_ERROR("netconn_recv_udp_raw_netbuf: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) != NETCONN_TCP, return ERR_ARG;);
+
+ return netconn_recv_data(conn, (void **)new_buf, apiflags);
+}
+
+/**
+ * @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
@@ -473,7 +787,6 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
*new_buf = NULL;
LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
- LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
#if LWIP_TCP
#if (LWIP_UDP || LWIP_RAW)
@@ -485,11 +798,10 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
if (buf == NULL) {
- NETCONN_SET_SAFE_ERR(conn, ERR_MEM);
return ERR_MEM;
}
- err = netconn_recv_data(conn, (void **)&p);
+ err = netconn_recv_data_tcp(conn, &p, 0);
if (err != ERR_OK) {
memp_free(MEMP_NETBUF, buf);
return err;
@@ -499,7 +811,7 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
buf->p = p;
buf->ptr = p;
buf->port = 0;
- ipX_addr_set_any(LWIP_IPV6, &buf->addr);
+ ip_addr_set_zero(&buf->addr);
*new_buf = buf;
/* don't set conn->last_err: it's only ERR_OK, anyway */
return ERR_OK;
@@ -510,43 +822,13 @@ netconn_recv(struct netconn *conn, struct netbuf **new_buf)
#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
{
#if (LWIP_UDP || LWIP_RAW)
- return netconn_recv_data(conn, (void **)new_buf);
+ return netconn_recv_data(conn, (void **)new_buf, 0);
#endif /* (LWIP_UDP || LWIP_RAW) */
}
}
/**
- * TCP: update the receive window: by calling this, the application
- * tells the stack that it has processed data and is able to accept
- * new data.
- * ATTENTION: use with care, this is mainly used for sockets!
- * Can only be used when calling netconn_set_noautorecved(conn, 1) before.
- *
- * @param conn the netconn for which to update the receive window
- * @param length amount of data processed (ATTENTION: this must be accurate!)
- */
-void
-netconn_recved(struct netconn *conn, u32_t length)
-{
-#if LWIP_TCP
- if ((conn != NULL) && (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) &&
- (netconn_get_noautorecved(conn))) {
- struct api_msg msg;
- /* Let the stack know that we have taken the data. */
- /* TODO: Speedup: Don't block and wait for the answer here
- (to prevent multiple thread-switches). */
- msg.msg.conn = conn;
- msg.msg.msg.r.len = length;
- /* don't care for the return value of lwip_netconn_do_recv */
- TCPIP_APIMSG_NOERR(&msg, lwip_netconn_do_recv);
- }
-#else /* LWIP_TCP */
- LWIP_UNUSED_ARG(conn);
- LWIP_UNUSED_ARG(length);
-#endif /* LWIP_TCP */
-}
-
-/**
+ * @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).
*
@@ -557,10 +839,10 @@ netconn_recved(struct netconn *conn, u32_t length)
* @return ERR_OK if data was sent, any other err_t on error
*/
err_t
-netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t port)
+netconn_sendto(struct netconn *conn, struct netbuf *buf, const ip_addr_t *addr, u16_t port)
{
if (buf != NULL) {
- ipX_addr_set_ipaddr(PCB_ISIPV6(conn->pcb.ip), &buf->addr, addr);
+ ip_addr_set(&buf->addr, addr);
buf->port = port;
return netconn_send(conn, buf);
}
@@ -568,6 +850,7 @@ netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t
}
/**
+ * @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
@@ -577,21 +860,24 @@ netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t
err_t
netconn_send(struct netconn *conn, struct netbuf *buf)
{
- struct api_msg msg;
+ 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));
- msg.msg.conn = conn;
- msg.msg.msg.b = buf;
- TCPIP_APIMSG(&msg, lwip_netconn_do_send, err);
- NETCONN_SET_SAFE_ERR(conn, err);
+ 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
@@ -600,7 +886,7 @@ netconn_send(struct netconn *conn, struct netbuf *buf)
* @param apiflags combination of following flags :
* - NETCONN_COPY: data will be copied into memory belonging to the stack
* - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
- * - NETCONN_DONTBLOCK: only write the data if all dat can be written at once
+ * - 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
*/
@@ -608,61 +894,112 @@ err_t
netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
u8_t apiflags, size_t *bytes_written)
{
- struct api_msg msg;
+ struct netvector vector;
+ vector.ptr = dataptr;
+ vector.len = size;
+ return netconn_write_vectors_partly(conn, &vector, 1, apiflags, bytes_written);
+}
+
+/**
+ * Send vectorized data atomically over a TCP netconn.
+ *
+ * @param conn the TCP netconn over which to send data
+ * @param vectors array of vectors containing data to send
+ * @param vectorcnt number of vectors in the array
+ * @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_vectors_partly(struct netconn *conn, struct netvector *vectors, u16_t vectorcnt,
+ u8_t apiflags, size_t *bytes_written)
+{
+ API_MSG_VAR_DECLARE(msg);
err_t err;
u8_t dontblock;
+ size_t size;
+ int i;
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;
- }
+ LWIP_ERROR("netconn_write: invalid conn->type", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP), return ERR_VAL;);
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;
}
+ /* sum up the total size */
+ size = 0;
+ for (i = 0; i < vectorcnt; i++) {
+ size += vectors[i].len;
+ if (size < vectors[i].len) {
+ /* overflow */
+ return ERR_VAL;
+ }
+ }
+ if (size == 0) {
+ return ERR_OK;
+ } else if (size > SSIZE_MAX) {
+ ssize_t limited;
+ /* this is required by the socket layer (cannot send full size_t range) */
+ if (!bytes_written) {
+ return ERR_VAL;
+ }
+ /* limit the amount of data to send */
+ limited = SSIZE_MAX;
+ size = (size_t)limited;
+ }
+
+ API_MSG_VAR_ALLOC(msg);
/* non-blocking write sends as much */
- msg.msg.conn = conn;
- msg.msg.msg.w.dataptr = dataptr;
- msg.msg.msg.w.apiflags = apiflags;
- msg.msg.msg.w.len = size;
+ API_MSG_VAR_REF(msg).conn = conn;
+ API_MSG_VAR_REF(msg).msg.w.vector = vectors;
+ API_MSG_VAR_REF(msg).msg.w.vector_cnt = vectorcnt;
+ API_MSG_VAR_REF(msg).msg.w.vector_off = 0;
+ API_MSG_VAR_REF(msg).msg.w.apiflags = apiflags;
+ API_MSG_VAR_REF(msg).msg.w.len = size;
+ API_MSG_VAR_REF(msg).msg.w.offset = 0;
#if LWIP_SO_SNDTIMEO
if (conn->send_timeout != 0) {
/* get the time we started, which is later compared to
sys_now() + conn->send_timeout */
- msg.msg.msg.w.time_started = sys_now();
+ API_MSG_VAR_REF(msg).msg.w.time_started = sys_now();
} else {
- msg.msg.msg.w.time_started = 0;
+ 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. */
- TCPIP_APIMSG(&msg, lwip_netconn_do_write, err);
- if ((err == ERR_OK) && (bytes_written != NULL)) {
- if (dontblock
-#if LWIP_SO_SNDTIMEO
- || (conn->send_timeout != 0)
-#endif /* LWIP_SO_SNDTIMEO */
- ) {
- /* nonblocking write: maybe the data has been sent partly */
- *bytes_written = msg.msg.msg.w.len;
- } else {
- /* blocking call succeeded: all data has been sent if it */
- *bytes_written = size;
+ err = netconn_apimsg(lwip_netconn_do_write, &API_MSG_VAR_REF(msg));
+ if (err == ERR_OK) {
+ if (bytes_written != NULL) {
+ *bytes_written = API_MSG_VAR_REF(msg).msg.w.offset;
+ }
+ /* for blocking, check all requested bytes were written, NOTE: send_timeout is
+ treated as dontblock (see dontblock assignment above) */
+ if (!dontblock) {
+ LWIP_ASSERT("do_write failed to write all bytes", API_MSG_VAR_REF(msg).msg.w.offset == size);
}
}
+ API_MSG_VAR_FREE(msg);
- NETCONN_SET_SAFE_ERR(conn, err);
return err;
}
/**
- * Close ot shutdown a TCP netconn (doesn't delete it).
+ * @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?
@@ -671,24 +1008,34 @@ netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
static err_t
netconn_close_shutdown(struct netconn *conn, u8_t how)
{
- struct api_msg msg;
+ API_MSG_VAR_DECLARE(msg);
err_t err;
+ LWIP_UNUSED_ARG(how);
LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;);
- msg.function = lwip_netconn_do_close;
- msg.msg.conn = conn;
+ API_MSG_VAR_ALLOC(msg);
+ API_MSG_VAR_REF(msg).conn = conn;
+#if LWIP_TCP
/* shutting down both ends is the same as closing */
- msg.msg.msg.sd.shut = how;
- /* because of the LWIP_TCPIP_CORE_LOCKING implementation of lwip_netconn_do_close,
- don't use TCPIP_APIMSG here */
- err = tcpip_apimsg(&msg);
+ 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);
- NETCONN_SET_SAFE_ERR(conn, err);
return err;
}
/**
+ * @ingroup netconn_tcp
* Close a TCP netconn (doesn't delete it).
*
* @param conn the TCP netconn to close
@@ -702,19 +1049,45 @@ netconn_close(struct netconn *conn)
}
/**
+ * @ingroup netconn_common
+ * Get and reset pending error on a netconn
+ *
+ * @param conn the netconn to get the error from
+ * @return and pending error or ERR_OK if no error was pending
+ */
+err_t
+netconn_err(struct netconn *conn)
+{
+ err_t err;
+ SYS_ARCH_DECL_PROTECT(lev);
+ if (conn == NULL) {
+ return ERR_OK;
+ }
+ SYS_ARCH_PROTECT(lev);
+ err = conn->pending_err;
+ conn->pending_err = ERR_OK;
+ SYS_ARCH_UNPROTECT(lev);
+ return err;
+}
+
+/**
+ * @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));
+ return netconn_close_shutdown(conn, (u8_t)((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
@@ -726,63 +1099,179 @@ netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
*/
err_t
netconn_join_leave_group(struct netconn *conn,
- ip_addr_t *multiaddr,
- ip_addr_t *netif_addr,
+ const ip_addr_t *multiaddr,
+ const ip_addr_t *netif_addr,
enum netconn_igmp join_or_leave)
{
- struct api_msg msg;
+ API_MSG_VAR_DECLARE(msg);
err_t err;
LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;);
- msg.msg.conn = conn;
- msg.msg.msg.jl.multiaddr = ip_2_ipX(multiaddr);
- msg.msg.msg.jl.netif_addr = ip_2_ipX(netif_addr);
- msg.msg.msg.jl.join_or_leave = join_or_leave;
- TCPIP_APIMSG(&msg, lwip_netconn_do_join_leave_group, err);
+ 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;
+}
+/**
+ * @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 if_idx the index of the netif
+ * @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_netif(struct netconn *conn,
+ const ip_addr_t *multiaddr,
+ u8_t if_idx,
+ 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 (if_idx == NETIF_NO_INDEX) {
+ return ERR_IF;
+ }
+#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.if_idx = if_idx;
+ API_MSG_VAR_REF(msg).msg.jl.join_or_leave = join_or_leave;
+ err = netconn_apimsg(lwip_netconn_do_join_leave_group_netif, &API_MSG_VAR_REF(msg));
+ API_MSG_VAR_FREE(msg);
- NETCONN_SET_SAFE_ERR(conn, err);
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
{
- struct dns_api_msg msg;
- err_t err;
+ 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;);
-
- err = sys_sem_new(&sem, 0);
- if (err != ERR_OK) {
- return err;
+#if LWIP_MPU_COMPATIBLE
+ if (strlen(name) >= DNS_MAX_NAME_LENGTH) {
+ return ERR_ARG;
}
+#endif
- msg.name = name;
- msg.addr = addr;
+ 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_send_msg_wait_sem(lwip_netconn_do_gethostbyname, &API_VAR_REF(msg), API_EXPR_REF(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 (cberr != ERR_OK) {
+ API_VAR_FREE(MEMP_DNS_API_MSG, msg);
+ return cberr;
+ }
- tcpip_callback(lwip_netconn_do_gethostbyname, &msg);
- sys_sem_wait(&sem);
- sys_sem_free(&sem);
+#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/lwip/src/api/api_msg.c b/lwip/src/api/api_msg.c
index b1a9b77..1cc2cc9 100644
--- a/lwip/src/api/api_msg.c
+++ b/lwip/src/api/api_msg.c
@@ -6,9 +6,9 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -17,21 +17,21 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
@@ -40,44 +40,103 @@
#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
-#include "lwip/api_msg.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/tcpip.h"
#include "lwip/igmp.h"
#include "lwip/dns.h"
#include "lwip/mld6.h"
+#include "lwip/priv/tcpip_priv.h"
#include <string.h>
-#define SET_NONBLOCKING_CONNECT(conn, val) do { if(val) { \
- (conn)->flags |= NETCONN_FLAG_IN_NONBLOCKING_CONNECT; \
+/* 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) { \
+ netconn_set_flags(conn, 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)
+ netconn_clear_flags(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT); }} while(0)
+#define IN_NONBLOCKING_CONNECT(conn) netconn_is_flag_set(conn, NETCONN_FLAG_IN_NONBLOCKING_CONNECT)
/* forward declarations */
#if LWIP_TCP
-static err_t lwip_netconn_do_writemore(struct netconn *conn);
-static void lwip_netconn_do_close_internal(struct netconn *conn);
+#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)
+#else /* LWIP_TCPIP_CORE_LOCKING */
+#define TCPIP_APIMSG_ACK(m) do { sys_sem_signal(LWIP_API_MSG_SEM(m)); } while(0)
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+#if LWIP_TCP
+const u8_t netconn_aborted = 0;
+const u8_t netconn_reset = 0;
+const u8_t netconn_closed = 0;
+
+/** Translate an error to a unique void* passed via an mbox */
+static void *
+lwip_netconn_err_to_msg(err_t err)
+{
+ switch (err) {
+ case ERR_ABRT:
+ return LWIP_CONST_CAST(void *, &netconn_aborted);
+ case ERR_RST:
+ return LWIP_CONST_CAST(void *, &netconn_reset);
+ case ERR_CLSD:
+ return LWIP_CONST_CAST(void *, &netconn_closed);
+ default:
+ LWIP_ASSERT("unhandled error", err == ERR_OK);
+ return NULL;
+ }
+}
+
+int
+lwip_netconn_is_err_msg(void *msg, err_t *err)
+{
+ LWIP_ASSERT("err != NULL", err != NULL);
+
+ if (msg == &netconn_aborted) {
+ *err = ERR_ABRT;
+ return 1;
+ } else if (msg == &netconn_reset) {
+ *err = ERR_RST;
+ return 1;
+ } else if (msg == &netconn_closed) {
+ *err = ERR_CLSD;
+ return 1;
+ }
+ return 0;
+}
+#endif /* LWIP_TCP */
+
+
#if LWIP_RAW
/**
* Receive callback function for RAW netconns.
- * Doesn't 'eat' the packet, only references it and sends it to
+ * 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,
- ip_addr_t *addr)
+ const ip_addr_t *addr)
{
struct pbuf *q;
struct netbuf *buf;
@@ -96,7 +155,7 @@ recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
#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 (q != NULL) {
if (pbuf_copy(q, p) != ERR_OK) {
pbuf_free(q);
q = NULL;
@@ -113,7 +172,7 @@ recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
buf->p = q;
buf->ptr = q;
- ipX_addr_copy(PCB_ISIPV6(pcb), buf->addr, *ipX_current_src_addr());
+ ip_addr_copy(buf->addr, *ip_current_src_addr());
buf->port = pcb->protocol;
len = q->tot_len;
@@ -143,7 +202,7 @@ recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
*/
static void
recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
- ip_addr_t *addr, u16_t port)
+ const ip_addr_t *addr, u16_t port)
{
struct netbuf *buf;
struct netconn *conn;
@@ -156,14 +215,20 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
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;
+
+ if (conn == NULL) {
+ pbuf_free(p);
+ return;
+ }
+
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) ||
+ if (!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)) {
+ if (!sys_mbox_valid(&conn->recvmbox)) {
#endif /* LWIP_SO_RCVBUF */
pbuf_free(p);
return;
@@ -176,16 +241,14 @@ recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
} else {
buf->p = p;
buf->ptr = p;
- ipX_addr_set_ipaddr(ip_current_is_v6(), &buf->addr, addr);
+ ip_addr_set(&buf->addr, addr);
buf->port = port;
#if LWIP_NETBUF_RECVINFO
- {
+ if (conn->flags & NETCONN_FLAG_PKTINFO) {
/* get the UDP header - always in the first pbuf, ensured by udp_input */
- const struct udp_hdr* udphdr = ipX_next_header_ptr();
-#if LWIP_CHECKSUM_ON_COPY
+ const struct udp_hdr *udphdr = (const struct udp_hdr *)ip_next_header_ptr();
buf->flags = NETBUF_FLAG_DESTADDR;
-#endif /* LWIP_CHECKSUM_ON_COPY */
- ipX_addr_set(ip_current_is_v6(), &buf->toaddr, ipX_current_dest_addr());
+ ip_addr_set(&buf->toaddr, ip_current_dest_addr());
buf->toport_chksum = udphdr->dest;
}
#endif /* LWIP_NETBUF_RECVINFO */
@@ -217,16 +280,19 @@ recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
{
struct netconn *conn;
u16_t len;
+ void *msg;
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);
+ LWIP_ASSERT("err != ERR_OK unhandled", err == ERR_OK);
conn = (struct netconn *)arg;
- LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb);
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) {
@@ -239,16 +305,15 @@ recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
using recv_avail since that could break the connection
(data is already ACKed) */
- /* don't overwrite fatal errors! */
- NETCONN_SET_SAFE_ERR(conn, err);
-
if (p != NULL) {
+ msg = p;
len = p->tot_len;
} else {
+ msg = LWIP_CONST_CAST(void *, &netconn_closed);
len = 0;
}
- if (sys_mbox_trypost(&conn->recvmbox, p) != ERR_OK) {
+ if (sys_mbox_trypost(&conn->recvmbox, msg) != ERR_OK) {
/* don't deallocate p: it is presented to us later again from tcp_fasttmr! */
return ERR_MEM;
} else {
@@ -282,9 +347,14 @@ poll_tcp(void *arg, struct tcp_pcb *pcb)
LWIP_ASSERT("conn != NULL", (conn != NULL));
if (conn->state == NETCONN_WRITE) {
- lwip_netconn_do_writemore(conn);
+ lwip_netconn_do_writemore(conn WRITE_DELAYED);
} else if (conn->state == NETCONN_CLOSE) {
- lwip_netconn_do_close_internal(conn);
+#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? */
@@ -293,8 +363,8 @@ poll_tcp(void *arg, struct tcp_pcb *pcb)
/* 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;
+ (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+ netconn_clear_flags(conn, NETCONN_FLAG_CHECK_WRITESPACE);
API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
}
}
@@ -317,22 +387,22 @@ sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
LWIP_UNUSED_ARG(pcb);
LWIP_ASSERT("conn != NULL", (conn != NULL));
- if (conn->state == NETCONN_WRITE) {
- lwip_netconn_do_writemore(conn);
- } else if (conn->state == NETCONN_CLOSE) {
- lwip_netconn_do_close_internal(conn);
- }
-
if (conn) {
+ if (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;
+ (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+ netconn_clear_flags(conn, NETCONN_FLAG_CHECK_WRITESPACE);
API_EVENT(conn, NETCONN_EVT_SENDPLUS, len);
}
}
-
+
return ERR_OK;
}
@@ -348,39 +418,44 @@ err_tcp(void *arg, err_t err)
{
struct netconn *conn;
enum netconn_state old_state;
+ void *mbox_msg;
SYS_ARCH_DECL_PROTECT(lev);
conn = (struct netconn *)arg;
LWIP_ASSERT("conn != NULL", (conn != NULL));
- conn->pcb.tcp = NULL;
-
- /* no check since this is always fatal! */
SYS_ARCH_PROTECT(lev);
- conn->last_err = err;
- SYS_ARCH_UNPROTECT(lev);
+
+ /* when err is called, the pcb is deallocated, so delete the reference */
+ conn->pcb.tcp = NULL;
+ /* store pending error */
+ conn->pending_err = err;
+ /* prevent application threads from blocking on 'recvmbox'/'acceptmbox' */
+ conn->flags |= NETCONN_FLAG_MBOXCLOSED;
/* reset conn->state now before waking up other threads */
old_state = conn->state;
conn->state = NETCONN_NONE;
- /* Notify the user layer about a connection error. Used to signal
- select. */
+ SYS_ARCH_UNPROTECT(lev);
+
+ /* 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 */
+ mbox_msg = lwip_netconn_err_to_msg(err);
+ /* pass error 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);
+ sys_mbox_trypost(&conn->recvmbox, mbox_msg);
}
- /* pass NULL-message to acceptmbox to wake up pending accept */
+ /* pass error 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);
+ sys_mbox_trypost(&conn->acceptmbox, mbox_msg);
}
if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) ||
@@ -391,12 +466,23 @@ err_tcp(void *arg, err_t err)
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;
+ if (old_state == NETCONN_CLOSE) {
+ /* let close succeed: the connection is closed after all... */
+ conn->current_msg->err = ERR_OK;
+ } else {
+ /* Write and connect fail */
+ 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 */
- sys_sem_signal(&conn->op_completed);
+ sys_sem_signal(op_completed_sem);
+ } else {
+ /* @todo: test what happens for error on nonblocking connect */
}
} else {
LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL);
@@ -418,7 +504,7 @@ setup_tcp(struct netconn *conn)
tcp_arg(pcb, conn);
tcp_recv(pcb, recv_tcp);
tcp_sent(pcb, sent_tcp);
- tcp_poll(pcb, poll_tcp, 4);
+ tcp_poll(pcb, poll_tcp, NETCONN_TCP_POLL_INTERVAL);
tcp_err(pcb, err_tcp);
}
@@ -434,34 +520,53 @@ 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, lwip_netconn_err_to_msg(ERR_ABRT)) == ERR_OK) {
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ }
+ return ERR_VAL;
+ }
+ LWIP_ASSERT("expect newpcb == NULL or err == ERR_OK", err == ERR_OK);
+ LWIP_UNUSED_ARG(err); /* for LWIP_NOASSERT */
+
+ LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->state: %s\n", tcp_debug_state_str(newpcb->state)));
+
/* We have to set the callback here even though
- * the new socket is unknown. conn->socket is marked as -1. */
+ * 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, lwip_netconn_err_to_msg(ERR_ABRT)) == 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;
+ struct tcp_pcb *pcb = newconn->pcb.tcp;
tcp_arg(pcb, NULL);
tcp_recv(pcb, NULL);
tcp_sent(pcb, NULL);
- tcp_poll(pcb, NULL, 4);
+ tcp_poll(pcb, NULL, 0);
tcp_err(pcb, NULL);
/* remove reference from to the pcb from this netconn */
newconn->pcb.tcp = NULL;
@@ -483,76 +588,86 @@ accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
* Create a new pcb of a specific type.
* Called from lwip_netconn_do_newconn().
*
- * @param msg the api_msg_msg describing the connection type
- * @return msg->conn->err, but the return value is currently ignored
+ * @param msg the api_msg describing the connection type
*/
static void
-pcb_new(struct api_msg_msg *msg)
+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)) {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
#if LWIP_RAW
- case NETCONN_RAW:
- msg->conn->pcb.raw = raw_new(msg->msg.n.proto);
- if(msg->conn->pcb.raw != NULL) {
- raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
- }
- break;
+ 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();
- if(msg->conn->pcb.udp != NULL) {
+ 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);
- }
+ 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);
+ 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);
}
- udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
- }
- break;
+ break;
#endif /* LWIP_UDP */
#if LWIP_TCP
- case NETCONN_TCP:
- msg->conn->pcb.tcp = tcp_new();
- if(msg->conn->pcb.tcp != NULL) {
- setup_tcp(msg->conn);
- }
- break;
+ 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;
+ default:
+ /* Unsupported netconn type, e.g. protocol disabled */
+ msg->err = ERR_VAL;
+ return;
}
if (msg->conn->pcb.ip == NULL) {
msg->err = ERR_MEM;
}
-#if LWIP_IPV6
- else {
- if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
- ip_set_v6(msg->conn->pcb.ip, 1);
- }
- }
-#endif /* LWIP_IPV6 */
}
/**
* Create a new pcb of a specific type inside a netconn.
* Called from netconn_new_with_proto_and_callback.
*
- * @param msg the api_msg_msg describing the connection type
+ * @param m the api_msg describing the connection type
*/
void
-lwip_netconn_do_newconn(struct api_msg_msg *msg)
+lwip_netconn_do_newconn(void *m)
{
+ struct api_msg *msg = (struct api_msg *)m;
+
msg->err = ERR_OK;
- if(msg->conn->pcb.tcp == NULL) {
+ if (msg->conn->pcb.tcp == NULL) {
pcb_new(msg);
}
/* Else? This "new" connection already has a PCB allocated. */
@@ -567,55 +682,60 @@ lwip_netconn_do_newconn(struct api_msg_msg *msg)
* The corresponding pcb is NOT created!
*
* @param t the type of 'connection' to create (@see enum netconn_type)
- * @param proto the IP protocol for RAW IP pcbs
* @param callback a function to call on status changes (RX available, TX'ed)
* @return a newly allocated struct netconn or
* NULL on memory error
*/
-struct netconn*
+struct netconn *
netconn_alloc(enum netconn_type t, netconn_callback callback)
{
struct netconn *conn;
int size;
+ u8_t init_flags = 0;
conn = (struct netconn *)memp_malloc(MEMP_NETCONN);
if (conn == NULL) {
return NULL;
}
- conn->last_err = ERR_OK;
+ conn->pending_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 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;
+ case NETCONN_RAW:
+ size = DEFAULT_RAW_RECVMBOX_SIZE;
+ break;
#endif /* LWIP_RAW */
#if LWIP_UDP
- case NETCONN_UDP:
- size = DEFAULT_UDP_RECVMBOX_SIZE;
- break;
+ case NETCONN_UDP:
+ size = DEFAULT_UDP_RECVMBOX_SIZE;
+#if LWIP_NETBUF_RECVINFO
+ init_flags |= NETCONN_FLAG_PKTINFO;
+#endif /* LWIP_NETBUF_RECVINFO */
+ break;
#endif /* LWIP_UDP */
#if LWIP_TCP
- case NETCONN_TCP:
- size = DEFAULT_TCP_RECVMBOX_SIZE;
- break;
+ 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;
+ default:
+ LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
+ goto free_and_return;
}
- if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) {
+ if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) {
goto free_and_return;
}
- if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) {
- sys_sem_free(&conn->op_completed);
+#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);
@@ -628,7 +748,6 @@ netconn_alloc(enum netconn_type t, netconn_callback callback)
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;
@@ -640,7 +759,10 @@ netconn_alloc(enum netconn_type t, netconn_callback callback)
conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
conn->recv_avail = 0;
#endif /* LWIP_SO_RCVBUF */
- conn->flags = 0;
+#if LWIP_SO_LINGER
+ conn->linger = -1;
+#endif /* LWIP_SO_LINGER */
+ conn->flags = init_flags;
return conn;
free_and_return:
memp_free(MEMP_NETCONN, conn);
@@ -658,14 +780,16 @@ 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));
+ !sys_mbox_valid(&conn->recvmbox));
#if LWIP_TCP
LWIP_ASSERT("acceptmbox must be deallocated before calling this function",
- !sys_mbox_valid(&conn->acceptmbox));
+ !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);
}
@@ -693,8 +817,9 @@ netconn_drain(struct netconn *conn)
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;
+ err_t err;
+ if (!lwip_netconn_is_err_msg(mem, &err)) {
+ 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);
@@ -715,19 +840,19 @@ netconn_drain(struct netconn *conn)
#if LWIP_TCP
if (sys_mbox_valid(&conn->acceptmbox)) {
while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
- struct netconn *newconn = (struct netconn *)mem;
- /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */
- /* pcb might be set to NULL already by err_tcp() */
- if (conn->pcb.tcp != NULL) {
- tcp_accepted(conn->pcb.tcp);
- }
- /* drain recvmbox */
- netconn_drain(newconn);
- if (newconn->pcb.tcp != NULL) {
- tcp_abort(newconn->pcb.tcp);
- newconn->pcb.tcp = NULL;
+ err_t err;
+ if (!lwip_netconn_is_err_msg(mem, &err)) {
+ 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);
}
- netconn_free(newconn);
}
sys_mbox_free(&conn->acceptmbox);
sys_mbox_set_invalid(&conn->acceptmbox);
@@ -743,11 +868,16 @@ netconn_drain(struct netconn *conn)
*
* @param conn the TCP netconn to close
*/
-static void
-lwip_netconn_do_close_internal(struct netconn *conn)
+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 shut, shut_rx, shut_tx, shut_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));
@@ -755,70 +885,173 @@ lwip_netconn_do_close_internal(struct netconn *conn)
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 */
- close = shut == NETCONN_SHUT_RDWR;
+ /* 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) {
+ shut_close = 1;
+ } else if (shut_rx &&
+ ((tpcb->state == FIN_WAIT_1) ||
+ (tpcb->state == FIN_WAIT_2) ||
+ (tpcb->state == CLOSING))) {
+ shut_close = 1;
+ } else if (shut_tx && ((tpcb->flags & TF_RXCLOSED) != 0)) {
+ shut_close = 1;
+ } else {
+ shut_close = 0;
+ }
/* Set back some callback pointers */
- if (close) {
- tcp_arg(conn->pcb.tcp, NULL);
+ if (shut_close) {
+ tcp_arg(tpcb, NULL);
}
- if (conn->pcb.tcp->state == LISTEN) {
- tcp_accept(conn->pcb.tcp, 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(conn->pcb.tcp, NULL);
- tcp_accept(conn->pcb.tcp, NULL);
+ tcp_recv(tpcb, NULL);
+ tcp_accept(tpcb, NULL);
}
if (shut_tx) {
- tcp_sent(conn->pcb.tcp, NULL);
+ tcp_sent(tpcb, NULL);
}
- if (close) {
- tcp_poll(conn->pcb.tcp, NULL, 4);
- tcp_err(conn->pcb.tcp, NULL);
+ if (shut_close) {
+ tcp_poll(tpcb, NULL, 0);
+ tcp_err(tpcb, NULL);
}
}
/* Try to close the connection */
- if (close) {
- err = tcp_close(conn->pcb.tcp);
+ if (shut_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(conn->pcb.tcp, shut_rx, shut_tx);
+ err = tcp_shutdown(tpcb, shut_rx, shut_tx);
}
if (err == ERR_OK) {
- /* Closing succeeded */
- conn->current_msg->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 (shut_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 (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 (err == ERR_OK) {
+ if (shut_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);
+ }
}
- if (shut_rx) {
- API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+#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) {
- API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+ tcp_sent(tpcb, sent_tcp);
}
- /* wake up the application task */
- sys_sem_signal(&conn->op_completed);
- } else {
- /* Closing failed, restore some of the callbacks */
- /* Closing of listen pcb will never fail! */
- LWIP_ASSERT("Closing a listen pcb may not fail!", (conn->pcb.tcp->state != LISTEN));
- tcp_sent(conn->pcb.tcp, sent_tcp);
- tcp_poll(conn->pcb.tcp, poll_tcp, 4);
- tcp_err(conn->pcb.tcp, err_tcp);
- tcp_arg(conn->pcb.tcp, conn);
+ /* 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 */
@@ -826,22 +1059,45 @@ lwip_netconn_do_close_internal(struct netconn *conn)
* Delete the pcb inside a netconn.
* Called from netconn_delete.
*
- * @param msg the api_msg_msg pointing to the connection
+ * @param m the api_msg pointing to the connection
*/
void
-lwip_netconn_do_delconn(struct api_msg_msg *msg)
+lwip_netconn_do_delconn(void *m)
{
- /* @todo TCP: abort running write/connect? */
- if ((msg->conn->state != NETCONN_NONE) &&
- (msg->conn->state != NETCONN_LISTEN) &&
- (msg->conn->state != NETCONN_CONNECT)) {
- /* this only happens for TCP netconns */
- LWIP_ASSERT("NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP",
- NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP);
+ 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->state = NETCONN_NONE;
+ 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 {
+ } else
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+ {
LWIP_ASSERT("blocking connect in progress",
- (msg->conn->state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn));
+ (state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn));
+ msg->err = ERR_OK;
/* Drain and delete mboxes */
netconn_drain(msg->conn);
@@ -849,30 +1105,39 @@ lwip_netconn_do_delconn(struct api_msg_msg *msg)
switch (NETCONNTYPE_GROUP(msg->conn->type)) {
#if LWIP_RAW
- case NETCONN_RAW:
- raw_remove(msg->conn->pcb.raw);
- break;
+ 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;
+ case NETCONN_UDP:
+ msg->conn->pcb.udp->recv_arg = NULL;
+ udp_remove(msg->conn->pcb.udp);
+ break;
#endif /* LWIP_UDP */
#if LWIP_TCP
- case NETCONN_TCP:
- LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
- msg->conn->write_offset == 0);
- msg->conn->state = NETCONN_CLOSE;
- msg->msg.sd.shut = NETCONN_SHUT_RDWR;
- msg->conn->current_msg = msg;
- lwip_netconn_do_close_internal(msg->conn);
- /* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing
- the application thread, so we can return at this point! */
- return;
+ case NETCONN_TCP:
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
+ 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;
+ default:
+ break;
}
msg->conn->pcb.tcp = NULL;
}
@@ -883,8 +1148,8 @@ lwip_netconn_do_delconn(struct api_msg_msg *msg)
API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0);
API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0);
}
- if (sys_sem_valid(&msg->conn->op_completed)) {
- sys_sem_signal(&msg->conn->op_completed);
+ if (sys_sem_valid(LWIP_API_MSG_SEM(msg))) {
+ TCPIP_APIMSG_ACK(msg);
}
}
@@ -892,38 +1157,84 @@ lwip_netconn_do_delconn(struct api_msg_msg *msg)
* Bind a pcb contained in a netconn
* Called from netconn_bind.
*
- * @param msg the api_msg_msg pointing to the connection and containing
- * the IP address and port to bind to
+ * @param m the api_msg pointing to the connection and containing
+ * the IP address and port to bind to
*/
void
-lwip_netconn_do_bind(struct api_msg_msg *msg)
+lwip_netconn_do_bind(void *m)
{
- if (ERR_IS_FATAL(msg->conn->last_err)) {
- msg->err = msg->conn->last_err;
+ struct api_msg *msg = (struct api_msg *)m;
+ err_t err;
+
+ if (msg->conn->pcb.tcp != NULL) {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ err = raw_bind(msg->conn->pcb.raw, API_EXPR_REF(msg->msg.bc.ipaddr));
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ 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:
+ err = tcp_bind(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr), msg->msg.bc.port);
+ break;
+#endif /* LWIP_TCP */
+ default:
+ err = ERR_VAL;
+ break;
+ }
} else {
- msg->err = ERR_VAL;
- if (msg->conn->pcb.tcp != NULL) {
- switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+ err = ERR_VAL;
+ }
+ msg->err = err;
+ TCPIP_APIMSG_ACK(msg);
+}
+/**
+ * Bind a pcb contained in a netconn to an interface
+ * Called from netconn_bind_if.
+ *
+ * @param m the api_msg pointing to the connection and containing
+ * the IP address and port to bind to
+ */
+void
+lwip_netconn_do_bind_if(void *m)
+{
+ struct netif *netif;
+ struct api_msg *msg = (struct api_msg *)m;
+ err_t err;
+
+ netif = netif_get_by_index(msg->msg.bc.if_idx);
+
+ if ((netif != NULL) && (msg->conn->pcb.tcp != NULL)) {
+ err = ERR_OK;
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
#if LWIP_RAW
case NETCONN_RAW:
- msg->err = raw_bind(msg->conn->pcb.raw, msg->msg.bc.ipaddr);
+ raw_bind_netif(msg->conn->pcb.raw, netif);
break;
#endif /* LWIP_RAW */
#if LWIP_UDP
case NETCONN_UDP:
- msg->err = udp_bind(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port);
+ udp_bind_netif(msg->conn->pcb.udp, netif);
break;
#endif /* LWIP_UDP */
#if LWIP_TCP
case NETCONN_TCP:
- msg->err = tcp_bind(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port);
+ tcp_bind_netif(msg->conn->pcb.tcp, netif);
break;
#endif /* LWIP_TCP */
default:
+ err = ERR_VAL;
break;
- }
}
+ } else {
+ err = ERR_VAL;
}
+ msg->err = err;
TCPIP_APIMSG_ACK(msg);
}
@@ -939,6 +1250,7 @@ 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);
@@ -950,25 +1262,26 @@ lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
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));
+ (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;
- if (!was_blocking) {
- NETCONN_SET_SAFE_ERR(conn, ERR_OK);
- }
API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
if (was_blocking) {
- sys_sem_signal(&conn->op_completed);
+ sys_sem_signal(op_completed_sem);
}
return ERR_OK;
}
@@ -978,79 +1291,89 @@ lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
* Connect a pcb contained inside a netconn
* Called from netconn_connect.
*
- * @param msg the api_msg_msg pointing to the connection and containing
- * the IP address and port to connect to
+ * @param m the api_msg pointing to the connection and containing
+ * the IP address and port to connect to
*/
void
-lwip_netconn_do_connect(struct api_msg_msg *msg)
+lwip_netconn_do_connect(void *m)
{
+ struct api_msg *msg = (struct api_msg *)m;
+ err_t err;
+
if (msg->conn->pcb.tcp == NULL) {
/* This may happen when calling netconn_connect() a second time */
- msg->err = ERR_CLSD;
- if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
- /* For TCP, netconn_connect() calls tcpip_apimsg(), so signal op_completed here. */
- sys_sem_signal(&msg->conn->op_completed);
- return;
- }
+ err = ERR_CLSD;
} else {
switch (NETCONNTYPE_GROUP(msg->conn->type)) {
#if LWIP_RAW
- case NETCONN_RAW:
- msg->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr);
- break;
+ case NETCONN_RAW:
+ 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, msg->msg.bc.ipaddr, msg->msg.bc.port);
- break;
+ case NETCONN_UDP:
+ 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_NONE) {
- msg->err = ERR_ISCONN;
- } else {
- setup_tcp(msg->conn);
- msg->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr,
- msg->msg.bc.port, lwip_netconn_do_connected);
- if (msg->err == ERR_OK) {
- u8_t non_blocking = netconn_is_nonblocking(msg->conn);
- msg->conn->state = NETCONN_CONNECT;
- SET_NONBLOCKING_CONNECT(msg->conn, non_blocking);
- if (non_blocking) {
- msg->err = ERR_INPROGRESS;
- } else {
- msg->conn->current_msg = msg;
- /* sys_sem_signal() is called from lwip_netconn_do_connected (or err_tcp()),
- * when the connection is established! */
- return;
+ case NETCONN_TCP:
+ /* Prevent connect while doing any other action. */
+ if (msg->conn->state == NETCONN_CONNECT) {
+ err = ERR_ALREADY;
+ } else if (msg->conn->state != NETCONN_NONE) {
+ err = ERR_ISCONN;
+ } else {
+ setup_tcp(msg->conn);
+ err = tcp_connect(msg->conn->pcb.tcp, API_EXPR_REF(msg->msg.bc.ipaddr),
+ msg->msg.bc.port, lwip_netconn_do_connected);
+ if (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) {
+ 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;
+ }
}
}
- }
- /* For TCP, netconn_connect() calls tcpip_apimsg(), so signal op_completed here. */
- sys_sem_signal(&msg->conn->op_completed);
- return;
+ break;
#endif /* LWIP_TCP */
- default:
- LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0));
- break;
+ default:
+ LWIP_ERROR("Invalid netconn type", 0, do {
+ err = ERR_VAL;
+ } while (0));
+ break;
}
}
+ msg->err = err;
/* For all other protocols, netconn_connect() calls TCPIP_APIMSG(),
so use TCPIP_APIMSG_ACK() here. */
TCPIP_APIMSG_ACK(msg);
}
/**
- * Connect a pcb contained inside a netconn
+ * Disconnect a pcb contained inside a netconn
* Only used for UDP netconns.
* Called from netconn_disconnect.
*
- * @param msg the api_msg_msg pointing to the connection to disconnect
+ * @param m the api_msg pointing to the connection to disconnect
*/
void
-lwip_netconn_do_disconnect(struct api_msg_msg *msg)
+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);
@@ -1068,38 +1391,44 @@ lwip_netconn_do_disconnect(struct api_msg_msg *msg)
* Set a TCP pcb contained in a netconn into listen mode
* Called from netconn_listen.
*
- * @param msg the api_msg_msg pointing to the connection
+ * @param m the api_msg pointing to the connection
*/
void
-lwip_netconn_do_listen(struct api_msg_msg *msg)
+lwip_netconn_do_listen(void *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 LWIP_IPV6
- if ((msg->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) == 0) {
-#if TCP_LISTEN_BACKLOG
- lpcb = tcp_listen_dual_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog);
-#else /* TCP_LISTEN_BACKLOG */
- lpcb = tcp_listen_dual(msg->conn->pcb.tcp);
-#endif /* TCP_LISTEN_BACKLOG */
- } else
-#endif /* LWIP_IPV6 */
- {
+ struct api_msg *msg = (struct api_msg *)m;
+ err_t err;
+
+ 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 */
+ err = ERR_VAL;
+ } else {
+ u8_t backlog;
#if TCP_LISTEN_BACKLOG
- lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog);
+ backlog = msg->msg.lb.backlog;
#else /* TCP_LISTEN_BACKLOG */
- lpcb = tcp_listen(msg->conn->pcb.tcp);
+ 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_MEM;
} else {
/* delete the recvmbox and allocate the acceptmbox */
if (sys_mbox_valid(&msg->conn->recvmbox)) {
@@ -1107,11 +1436,11 @@ lwip_netconn_do_listen(struct api_msg_msg *msg)
sys_mbox_free(&msg->conn->recvmbox);
sys_mbox_set_invalid(&msg->conn->recvmbox);
}
- msg->err = ERR_OK;
+ err = ERR_OK;
if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
- msg->err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
+ err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
}
- if (msg->err == ERR_OK) {
+ if (err == ERR_OK) {
msg->conn->state = NETCONN_LISTEN;
msg->conn->pcb.tcp = lpcb;
tcp_arg(msg->conn->pcb.tcp, msg->conn);
@@ -1123,11 +1452,20 @@ lwip_netconn_do_listen(struct api_msg_msg *msg)
}
}
}
+ } else if (msg->conn->state == NETCONN_LISTEN) {
+ /* already listening, allow updating of the backlog */
+ err = ERR_OK;
+ tcp_backlog_set(msg->conn->pcb.tcp, msg->msg.lb.backlog);
} else {
- msg->err = ERR_ARG;
+ err = ERR_CONN;
}
+ } else {
+ err = ERR_ARG;
}
+ } else {
+ err = ERR_CONN;
}
+ msg->err = err;
TCPIP_APIMSG_ACK(msg);
}
#endif /* LWIP_TCP */
@@ -1136,51 +1474,55 @@ lwip_netconn_do_listen(struct api_msg_msg *msg)
* Send some data on a RAW or UDP pcb contained in a netconn
* Called from netconn_send
*
- * @param msg the api_msg_msg pointing to the connection
+ * @param m the api_msg pointing to the connection
*/
void
-lwip_netconn_do_send(struct api_msg_msg *msg)
+lwip_netconn_do_send(void *m)
{
- if (ERR_IS_FATAL(msg->conn->last_err)) {
- msg->err = msg->conn->last_err;
- } else {
- msg->err = ERR_CONN;
+ struct api_msg *msg = (struct api_msg *)m;
+
+ err_t err = netconn_err(msg->conn);
+ if (err == ERR_OK) {
if (msg->conn->pcb.tcp != NULL) {
switch (NETCONNTYPE_GROUP(msg->conn->type)) {
#if LWIP_RAW
- case NETCONN_RAW:
- if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &msg->msg.b->addr)) {
- msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
- } else {
- msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, ipX_2_ip(&msg->msg.b->addr));
- }
- break;
+ case NETCONN_RAW:
+ if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+ err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
+ } else {
+ err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, &msg->msg.b->addr);
+ }
+ break;
#endif
#if LWIP_UDP
- case NETCONN_UDP:
+ case NETCONN_UDP:
#if LWIP_CHECKSUM_ON_COPY
- if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &msg->msg.b->addr)) {
- msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
- msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
- } else {
- msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
- ipX_2_ip(&msg->msg.b->addr), msg->msg.b->port,
- msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
- }
+ if (ip_addr_isany(&msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+ 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 {
+ 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 (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &msg->msg.b->addr)) {
- msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
- } else {
- msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, ipX_2_ip(&msg->msg.b->addr), msg->msg.b->port);
- }
+ if (ip_addr_isany_val(msg->msg.b->addr) || IP_IS_ANY_TYPE_VAL(msg->msg.b->addr)) {
+ err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
+ } else {
+ 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;
+ break;
#endif /* LWIP_UDP */
- default:
- break;
+ default:
+ err = ERR_CONN;
+ break;
}
+ } else {
+ err = ERR_CONN;
}
}
+ msg->err = err;
TCPIP_APIMSG_ACK(msg);
}
@@ -1189,31 +1531,47 @@ lwip_netconn_do_send(struct api_msg_msg *msg)
* Indicate data has been received from a TCP pcb contained in a netconn
* Called from netconn_recv
*
- * @param msg the api_msg_msg pointing to the connection
+ * @param m the api_msg pointing to the connection
*/
void
-lwip_netconn_do_recv(struct api_msg_msg *msg)
+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) {
+ size_t remaining = msg->msg.r.len;
+ do {
+ u16_t recved = (u16_t)((remaining > 0xffff) ? 0xffff : remaining);
+ tcp_recved(msg->conn->pcb.tcp, recved);
+ remaining -= recved;
+ } while (remaining != 0);
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
#if TCP_LISTEN_BACKLOG
- if (msg->conn->pcb.tcp->state == LISTEN) {
- tcp_accepted(msg->conn->pcb.tcp);
- } else
-#endif /* TCP_LISTEN_BACKLOG */
- {
- u32_t remaining = msg->msg.r.len;
- do {
- u16_t recved = (remaining > 0xffff) ? 0xffff : (u16_t)remaining;
- tcp_recved(msg->conn->pcb.tcp, recved);
- remaining -= recved;
- }while(remaining != 0);
- }
+/** Indicate that a TCP pcb has been accepted
+ * Called from netconn_accept
+ *
+ * @param m the api_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.
@@ -1227,129 +1585,167 @@ lwip_netconn_do_recv(struct api_msg_msg *msg)
* ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
*/
static err_t
-lwip_netconn_do_writemore(struct netconn *conn)
+lwip_netconn_do_writemore(struct netconn *conn WRITE_DELAYED_PARAM)
{
err_t err;
- void *dataptr;
+ const void *dataptr;
u16_t len, available;
u8_t write_finished = 0;
size_t diff;
- u8_t dontblock = netconn_is_nonblocking(conn) ||
- (conn->current_msg->msg.w.apiflags & NETCONN_DONTBLOCK);
- u8_t apiflags = conn->current_msg->msg.w.apiflags;
+ u8_t dontblock;
+ u8_t apiflags;
+ u8_t write_more;
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);
+ LWIP_ASSERT("conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len",
+ conn->current_msg->msg.w.offset < conn->current_msg->msg.w.len);
+ LWIP_ASSERT("conn->current_msg->msg.w.vector_cnt > 0", conn->current_msg->msg.w.vector_cnt > 0);
+
+ 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) {
+ if (conn->current_msg->msg.w.offset == 0) {
/* nothing has been written */
err = ERR_WOULDBLOCK;
- conn->current_msg->msg.w.len = 0;
} else {
/* partial write */
err = ERR_OK;
- conn->current_msg->msg.w.len = conn->write_offset;
}
} else
#endif /* LWIP_SO_SNDTIMEO */
{
- dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset;
- diff = conn->current_msg->msg.w.len - conn->write_offset;
- if (diff > 0xffffUL) { /* max_u16_t */
- len = 0xffff;
-#if LWIP_TCPIP_CORE_LOCKING
- conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
-#endif
- apiflags |= TCP_WRITE_FLAG_MORE;
- } else {
- len = (u16_t)diff;
- }
- available = tcp_sndbuf(conn->pcb.tcp);
- if (available < len) {
- /* don't try to write more than sendbuf */
- len = available;
- if (dontblock){
- if (!len) {
- err = ERR_WOULDBLOCK;
- goto err_mem;
- }
+ do {
+ dataptr = (const u8_t *)conn->current_msg->msg.w.vector->ptr + conn->current_msg->msg.w.vector_off;
+ diff = conn->current_msg->msg.w.vector->len - conn->current_msg->msg.w.vector_off;
+ if (diff > 0xffffUL) { /* max_u16_t */
+ len = 0xffff;
+ apiflags |= TCP_WRITE_FLAG_MORE;
} else {
-#if LWIP_TCPIP_CORE_LOCKING
- conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
-#endif
+ 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) {
+ /* set error according to partial write or not */
+ err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
+ goto err_mem;
+ }
+ } else {
+ apiflags |= TCP_WRITE_FLAG_MORE;
+ }
+ }
+ LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!",
+ ((conn->current_msg->msg.w.vector_off + len) <= conn->current_msg->msg.w.vector->len));
+ /* we should loop around for more sending in the following cases:
+ 1) We couldn't finish the current vector because of 16-bit size limitations.
+ tcp_write() and tcp_sndbuf() both are limited to 16-bit sizes
+ 2) We are sending the remainder of the current vector and have more */
+ if ((len == 0xffff && diff > 0xffffUL) ||
+ (len == (u16_t)diff && conn->current_msg->msg.w.vector_cnt > 1)) {
+ write_more = 1;
apiflags |= TCP_WRITE_FLAG_MORE;
+ } else {
+ write_more = 0;
}
- }
- 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);
+ err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
+ if (err == ERR_OK) {
+ conn->current_msg->msg.w.offset += len;
+ conn->current_msg->msg.w.vector_off += len;
+ /* check if current vector is finished */
+ if (conn->current_msg->msg.w.vector_off == conn->current_msg->msg.w.vector->len) {
+ conn->current_msg->msg.w.vector_cnt--;
+ /* if we have additional vectors, move on to them */
+ if (conn->current_msg->msg.w.vector_cnt > 0) {
+ conn->current_msg->msg.w.vector++;
+ conn->current_msg->msg.w.vector_off = 0;
+ }
+ }
+ }
+ } while (write_more && err == ERR_OK);
/* 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)) {
+ if (dontblock && (conn->current_msg->msg.w.offset < 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);
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
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);
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, 0);
}
}
if (err == ERR_OK) {
- conn->write_offset += len;
- if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) {
- /* return sent length */
- conn->current_msg->msg.w.len = conn->write_offset;
- /* everything was written */
+ err_t out_err;
+ if ((conn->current_msg->msg.w.offset == conn->current_msg->msg.w.len) || dontblock) {
+ /* return sent length (caller reads length from msg.w.offset) */
write_finished = 1;
- conn->write_offset = 0;
}
- tcp_output(conn->pcb.tcp);
- } else if ((err == ERR_MEM) && !dontblock) {
- /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called
- we do NOT return to the application thread, since ERR_MEM is
- only a temporary error! */
+ out_err = tcp_output(conn->pcb.tcp);
+ if (out_err == ERR_RTE) {
+ /* If tcp_output fails because no route is found,
+ don't try writing any more but return the error
+ to the application thread. */
+ err = out_err;
+ write_finished = 1;
+ }
+ } 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 */
- tcp_output(conn->pcb.tcp);
-
-#if LWIP_TCPIP_CORE_LOCKING
- conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
-#endif
+ err_t out_err = tcp_output(conn->pcb.tcp);
+ if (out_err == ERR_RTE) {
+ /* If tcp_output fails because no route is found,
+ don't try writing any more but return the error
+ to the application thread. */
+ err = out_err;
+ write_finished = 1;
+ } else if (dontblock) {
+ /* non-blocking write is done on ERR_MEM, set error according
+ to partial write or not */
+ err = (conn->current_msg->msg.w.offset == 0) ? ERR_WOULDBLOCK : ERR_OK;
+ write_finished = 1;
+ }
} 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->state = NETCONN_NONE;
#if LWIP_TCPIP_CORE_LOCKING
- if ((conn->flags & NETCONN_FLAG_WRITE_DELAYED) != 0)
+ if (delayed)
#endif
{
- sys_sem_signal(&conn->op_completed);
+ sys_sem_signal(op_completed_sem);
}
}
#if LWIP_TCPIP_CORE_LOCKING
- else
+ else {
return ERR_MEM;
+ }
#endif
return ERR_OK;
}
@@ -1359,35 +1755,33 @@ err_mem:
* Send some data on a TCP pcb contained in a netconn
* Called from netconn_write
*
- * @param msg the api_msg_msg pointing to the connection
+ * @param m the api_msg pointing to the connection
*/
void
-lwip_netconn_do_write(struct api_msg_msg *msg)
+lwip_netconn_do_write(void *m)
{
- if (ERR_IS_FATAL(msg->conn->last_err)) {
- msg->err = msg->conn->last_err;
- } else {
+ struct api_msg *msg = (struct api_msg *)m;
+
+ err_t err = netconn_err(msg->conn);
+ if (err == ERR_OK) {
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;
+ 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("already writing or closing", msg->conn->current_msg == NULL);
LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
msg->conn->current_msg = msg;
- msg->conn->write_offset = 0;
#if LWIP_TCPIP_CORE_LOCKING
- msg->conn->flags &= ~NETCONN_FLAG_WRITE_DELAYED;
- if (lwip_netconn_do_writemore(msg->conn) != ERR_OK) {
+ 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(&msg->conn->op_completed, 0);
+ sys_arch_sem_wait(LWIP_API_MSG_SEM(msg), 0);
LOCK_TCPIP_CORE();
- LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
+ LWIP_ASSERT("state!", msg->conn->state != NETCONN_WRITE);
}
#else /* LWIP_TCPIP_CORE_LOCKING */
lwip_netconn_do_writemore(msg->conn);
@@ -1396,17 +1790,18 @@ lwip_netconn_do_write(struct api_msg_msg *msg)
since lwip_netconn_do_writemore ACKs it! */
return;
} else {
- msg->err = ERR_CONN;
+ err = ERR_CONN;
}
#else /* LWIP_TCP */
- msg->err = ERR_VAL;
+ err = ERR_VAL;
#endif /* LWIP_TCP */
#if (LWIP_UDP || LWIP_RAW)
} else {
- msg->err = ERR_VAL;
+ err = ERR_VAL;
#endif /* (LWIP_UDP || LWIP_RAW) */
}
}
+ msg->err = err;
TCPIP_APIMSG_ACK(msg);
}
@@ -1414,52 +1809,61 @@ lwip_netconn_do_write(struct api_msg_msg *msg)
* Return a connection's local or remote address
* Called from netconn_getaddr
*
- * @param msg the api_msg_msg pointing to the connection
+ * @param m the api_msg pointing to the connection
*/
void
-lwip_netconn_do_getaddr(struct api_msg_msg *msg)
+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) {
- ipX_addr_copy(PCB_ISIPV6(msg->conn->pcb.ip), *(msg->msg.ad.ipaddr),
- msg->conn->pcb.ip->local_ip);
+ ip_addr_copy(API_EXPR_DEREF(msg->msg.ad.ipaddr),
+ msg->conn->pcb.ip->local_ip);
} else {
- ipX_addr_copy(PCB_ISIPV6(msg->conn->pcb.ip), *(msg->msg.ad.ipaddr),
- msg->conn->pcb.ip->remote_ip);
+ 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) {
- *(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;
+ 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) {
- *(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;
+ case NETCONN_UDP:
+ if (msg->msg.ad.local) {
+ API_EXPR_DEREF(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
} else {
- *(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
+ 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;
+ break;
#endif /* LWIP_UDP */
#if LWIP_TCP
- case NETCONN_TCP:
- *(msg->msg.ad.port) = (msg->msg.ad.local?msg->conn->pcb.tcp->local_port:msg->conn->pcb.tcp->remote_port);
- break;
+ 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;
+ default:
+ LWIP_ASSERT("invalid netconn_type", 0);
+ break;
}
} else {
msg->err = ERR_CONN;
@@ -1468,44 +1872,79 @@ lwip_netconn_do_getaddr(struct api_msg_msg *msg)
}
/**
- * Close a TCP pcb contained in a netconn
+ * 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 msg the api_msg_msg pointing to the connection
+ * @param m the api_msg pointing to the connection
*/
void
-lwip_netconn_do_close(struct api_msg_msg *msg)
+lwip_netconn_do_close(void *m)
{
+ struct api_msg *msg = (struct api_msg *)m;
+
#if LWIP_TCP
- /* @todo: abort running write/connect? */
- if ((msg->conn->state != NETCONN_NONE) && (msg->conn->state != NETCONN_LISTEN)) {
- /* this only happens for TCP netconns */
- LWIP_ASSERT("NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP",
- NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP);
- msg->err = ERR_INPROGRESS;
- } else if ((msg->conn->pcb.tcp != NULL) && (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP)) {
- if ((msg->msg.sd.shut != NETCONN_SHUT_RDWR) && (msg->conn->state == NETCONN_LISTEN)) {
- /* LISTEN doesn't support half shutdown */
+ 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->state = NETCONN_NONE;
+ state = NETCONN_NONE;
+ 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);
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL);
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_VAL;
+ msg->err = ERR_CONN;
}
- sys_sem_signal(&msg->conn->op_completed);
+ TCPIP_APIMSG_ACK(msg);
}
#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
@@ -1513,50 +1952,101 @@ lwip_netconn_do_close(struct api_msg_msg *msg)
* Join multicast groups for UDP netconns.
* Called from netconn_join_leave_group
*
- * @param msg the api_msg_msg pointing to the connection
+ * @param m the api_msg pointing to the connection
*/
void
-lwip_netconn_do_join_leave_group(struct api_msg_msg *msg)
-{
- if (ERR_IS_FATAL(msg->conn->last_err)) {
- msg->err = msg->conn->last_err;
- } else {
- if (msg->conn->pcb.tcp != NULL) {
- if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+lwip_netconn_do_join_leave_group(void *m)
+{
+ struct api_msg *msg = (struct api_msg *)m;
+
+ msg->err = ERR_CONN;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
#if LWIP_UDP
#if LWIP_IPV6 && LWIP_IPV6_MLD
- if (PCB_ISIPV6(msg->conn->pcb.udp)) {
- if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
- msg->err = mld6_joingroup(ipX_2_ip6(msg->msg.jl.netif_addr),
- ipX_2_ip6(msg->msg.jl.multiaddr));
- } else {
- msg->err = mld6_leavegroup(ipX_2_ip6(msg->msg.jl.netif_addr),
- ipX_2_ip6(msg->msg.jl.multiaddr));
- }
+ 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
+ } else
#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
- {
+ {
#if LWIP_IGMP
- if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
- msg->err = igmp_joingroup(ipX_2_ip(msg->msg.jl.netif_addr),
- ipX_2_ip(msg->msg.jl.multiaddr));
- } else {
- msg->err = igmp_leavegroup(ipX_2_ip(msg->msg.jl.netif_addr),
- ipX_2_ip(msg->msg.jl.multiaddr));
- }
-#endif /* LWIP_IGMP */
+ 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;
+ } else {
+ msg->err = ERR_VAL;
#endif /* (LWIP_TCP || LWIP_RAW) */
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+/**
+ * Join multicast groups for UDP netconns.
+ * Called from netconn_join_leave_group_netif
+ *
+ * @param m the api_msg pointing to the connection
+ */
+void
+lwip_netconn_do_join_leave_group_netif(void *m)
+{
+ struct api_msg *msg = (struct api_msg *)m;
+ struct netif *netif;
+
+ netif = netif_get_by_index(msg->msg.jl.if_idx);
+ if (netif == NULL) {
+ msg->err = ERR_IF;
+ goto done;
+ }
+
+ msg->err = ERR_CONN;
+ 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_netif(netif,
+ ip_2_ip6(API_EXPR_REF(msg->msg.jl.multiaddr)));
+ } else {
+ msg->err = mld6_leavegroup_netif(netif,
+ 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_netif(netif,
+ ip_2_ip4(API_EXPR_REF(msg->msg.jl.multiaddr)));
+ } else {
+ msg->err = igmp_leavegroup_netif(netif,
+ 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_CONN;
+ msg->err = ERR_VAL;
+#endif /* (LWIP_TCP || LWIP_RAW) */
}
}
+
+done:
TCPIP_APIMSG_ACK(msg);
}
#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
@@ -1568,23 +2058,23 @@ lwip_netconn_do_join_leave_group(struct api_msg_msg *msg)
* signaling the semaphore.
*/
static void
-lwip_netconn_do_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
+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;
+ struct dns_api_msg *msg = (struct dns_api_msg *)arg;
- LWIP_ASSERT("DNS response for wrong host name", strcmp(msg->name, name) == 0);
+ /* we trust the internal implementation to be correct :-) */
LWIP_UNUSED_ARG(name);
if (ipaddr == NULL) {
/* timeout or memory error */
- *msg->err = ERR_VAL;
+ API_EXPR_DEREF(msg->err) = ERR_VAL;
} else {
/* address was resolved */
- *msg->err = ERR_OK;
- *msg->addr = *ipaddr;
+ 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(msg->sem);
+ sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
}
/**
@@ -1596,14 +2086,31 @@ lwip_netconn_do_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
void
lwip_netconn_do_gethostbyname(void *arg)
{
- struct dns_api_msg *msg = (struct dns_api_msg*)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
- *msg->err = dns_gethostbyname(msg->name, msg->addr, lwip_netconn_do_dns_found, msg);
- if (*msg->err != ERR_INPROGRESS) {
+ API_EXPR_DEREF(msg->err) = dns_gethostbyname_addrtype(msg->name,
+ API_EXPR_REF(msg->addr), lwip_netconn_do_dns_found, msg, addrtype);
+#if LWIP_TCPIP_CORE_LOCKING
+ /* For core locking, only block if we need to wait for answer/timeout */
+ if (API_EXPR_DEREF(msg->err) == ERR_INPROGRESS) {
+ UNLOCK_TCPIP_CORE();
+ sys_sem_wait(API_EXPR_REF_SEM(msg->sem));
+ LOCK_TCPIP_CORE();
+ LWIP_ASSERT("do_gethostbyname still in progress!!", API_EXPR_DEREF(msg->err) != ERR_INPROGRESS);
+ }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ 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(msg->sem);
+ sys_sem_signal(API_EXPR_REF_SEM(msg->sem));
}
+#endif /* LWIP_TCPIP_CORE_LOCKING */
}
#endif /* LWIP_DNS */
diff --git a/lwip/src/api/err.c b/lwip/src/api/err.c
index 92fa8b7..dd2b62d 100644
--- a/lwip/src/api/err.c
+++ b/lwip/src/api/err.c
@@ -6,9 +6,9 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -17,46 +17,84 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
#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 connected.", /* ERR_ISCONN -9 */
- "Connection aborted.", /* ERR_ABRT -10 */
- "Connection reset.", /* ERR_RST -11 */
- "Connection closed.", /* ERR_CLSD -12 */
- "Not connected.", /* ERR_CONN -13 */
- "Illegal argument.", /* ERR_ARG -14 */
- "Low-level netif error.", /* ERR_IF -15 */
+ "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 */
};
/**
@@ -68,8 +106,10 @@ static const char *err_strerr[] = {
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/lwip/src/api/if_api.c b/lwip/src/api/if_api.c
new file mode 100644
index 0000000..8e094d0
--- /dev/null
+++ b/lwip/src/api/if_api.c
@@ -0,0 +1,102 @@
+/**
+ * @file
+ * Interface Identification APIs from:
+ * RFC 3493: Basic Socket Interface Extensions for IPv6
+ * Section 4: Interface Identification
+ *
+ * @defgroup if_api Interface Identification API
+ * @ingroup socket
+ */
+
+/*
+ * Copyright (c) 2017 Joel Cunningham, Garmin International, Inc. <joel.cunningham@garmin.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: Joel Cunningham <joel.cunningham@me.com>
+ *
+ */
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET
+
+#include "lwip/errno.h"
+#include "lwip/if_api.h"
+#include "lwip/netifapi.h"
+#include "lwip/priv/sockets_priv.h"
+
+/**
+ * @ingroup if_api
+ * Maps an interface index to its corresponding name.
+ * @param ifindex interface index
+ * @param ifname shall point to a buffer of at least {IF_NAMESIZE} bytes
+ * @return If ifindex is an interface index, then the function shall return the
+ * value supplied in ifname, which points to a buffer now containing the interface name.
+ * Otherwise, the function shall return a NULL pointer.
+ */
+char *
+lwip_if_indextoname(unsigned int ifindex, char *ifname)
+{
+#if LWIP_NETIF_API
+ if (ifindex <= 0xff) {
+ err_t err = netifapi_netif_index_to_name((u8_t)ifindex, ifname);
+ if (!err && ifname[0] != '\0') {
+ return ifname;
+ }
+ }
+#else /* LWIP_NETIF_API */
+ LWIP_UNUSED_ARG(ifindex);
+ LWIP_UNUSED_ARG(ifname);
+#endif /* LWIP_NETIF_API */
+ set_errno(ENXIO);
+ return NULL;
+}
+
+/**
+ * @ingroup if_api
+ * Returs the interface index corresponding to name ifname.
+ * @param ifname Interface name
+ * @return The corresponding index if ifname is the name of an interface;
+ * otherwise, zero.
+ */
+unsigned int
+lwip_if_nametoindex(const char *ifname)
+{
+#if LWIP_NETIF_API
+ err_t err;
+ u8_t idx;
+
+ err = netifapi_netif_name_to_index(ifname, &idx);
+ if (!err) {
+ return idx;
+ }
+#else /* LWIP_NETIF_API */
+ LWIP_UNUSED_ARG(ifname);
+#endif /* LWIP_NETIF_API */
+ return 0; /* invalid index */
+}
+
+#endif /* LWIP_SOCKET */
diff --git a/lwip/src/api/netbuf.c b/lwip/src/api/netbuf.c
index 0ccd2bc..3b910de 100644
--- a/lwip/src/api/netbuf.c
+++ b/lwip/src/api/netbuf.c
@@ -2,13 +2,19 @@
* @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -17,21 +23,21 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
@@ -46,6 +52,7 @@
#include <string.h>
/**
+ * @ingroup netbuf
* Create (allocate) and initialize a new netbuf.
* The netbuf doesn't yet contain a packet buffer!
*
@@ -59,26 +66,13 @@ netbuf *netbuf_new(void)
buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
if (buf != NULL) {
- buf->p = NULL;
- buf->ptr = NULL;
- ipX_addr_set_any(LWIP_IPV6, &buf->addr);
- buf->port = 0;
-#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY
-#if LWIP_CHECKSUM_ON_COPY
- buf->flags = 0;
-#endif /* LWIP_CHECKSUM_ON_COPY */
- buf->toport_chksum = 0;
-#if LWIP_NETBUF_RECVINFO
- ipX_addr_set_any(LWIP_IPV6, &buf->toaddr);
-#endif /* LWIP_NETBUF_RECVINFO */
-#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */
- return buf;
- } else {
- return NULL;
+ 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()
@@ -96,6 +90,7 @@ netbuf_delete(struct 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
@@ -114,15 +109,16 @@ netbuf_alloc(struct netbuf *buf, u16_t size)
}
buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
if (buf->p == NULL) {
- return NULL;
+ return NULL;
}
LWIP_ASSERT("check that first pbuf can hold size",
- (buf->p->len >= 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
@@ -135,9 +131,14 @@ netbuf_free(struct netbuf *buf)
pbuf_free(buf->p);
}
buf->p = buf->ptr = NULL;
+#if LWIP_CHECKSUM_ON_COPY
+ buf->flags = 0;
+ buf->toport_chksum = 0;
+#endif /* LWIP_CHECKSUM_ON_COPY */
}
/**
+ * @ingroup netbuf
* Let a netbuf reference existing (non-volatile) data.
*
* @param buf netbuf which should reference the data
@@ -158,13 +159,14 @@ netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
buf->ptr = NULL;
return ERR_MEM;
}
- buf->p->payload = (void*)dataptr;
+ ((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
@@ -173,7 +175,7 @@ netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
void
netbuf_chain(struct netbuf *head, struct netbuf *tail)
{
- LWIP_ERROR("netbuf_ref: invalid head", (head != NULL), return;);
+ 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;
@@ -181,12 +183,13 @@ netbuf_chain(struct netbuf *head, struct 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 retreived,
+ * @return ERR_OK if the information was retrieved,
* ERR_BUF on error.
*/
err_t
@@ -205,6 +208,7 @@ netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
}
/**
+ * @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.
@@ -217,7 +221,7 @@ netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
s8_t
netbuf_next(struct netbuf *buf)
{
- LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return -1;);
+ LWIP_ERROR("netbuf_next: invalid buf", (buf != NULL), return -1;);
if (buf->ptr->next == NULL) {
return -1;
}
@@ -229,6 +233,7 @@ netbuf_next(struct netbuf *buf)
}
/**
+ * @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.
@@ -238,7 +243,7 @@ netbuf_next(struct netbuf *buf)
void
netbuf_first(struct netbuf *buf)
{
- LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
+ LWIP_ERROR("netbuf_first: invalid buf", (buf != NULL), return;);
buf->ptr = buf->p;
}
diff --git a/lwip/src/api/netdb.c b/lwip/src/api/netdb.c
index 6a4bac5..8771425 100644
--- a/lwip/src/api/netdb.c
+++ b/lwip/src/api/netdb.c
@@ -2,10 +2,12 @@
* @file
* API functions for name resolving
*
+ * @defgroup netdbapi NETDB API
+ * @ingroup socket
*/
/*
- * Redistribution and use in source and binary forms, with or without modification,
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -14,21 +16,21 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Simon Goldschmidt
*
*/
@@ -44,8 +46,8 @@
#include "lwip/api.h"
#include "lwip/dns.h"
-#include <string.h>
-#include <stdlib.h>
+#include <string.h> /* memset */
+#include <stdlib.h> /* atoi */
/** helper struct for gethostbyname_r to access the char* buffer */
struct gethostbyname_r_helper {
@@ -81,7 +83,7 @@ int h_errno;
* @return an entry containing addresses of address family AF_INET
* for the host with name name
*/
-struct hostent*
+struct hostent *
lwip_gethostbyname(const char *name)
{
err_t err;
@@ -92,6 +94,7 @@ lwip_gethostbyname(const char *name)
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);
@@ -105,31 +108,28 @@ lwip_gethostbyname(const char *name)
s_hostent_addr = addr;
s_phostent_addr[0] = &s_hostent_addr;
s_phostent_addr[1] = NULL;
- s_hostent.h_name = (char*)name;
+ 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;
+ s_hostent.h_addr_list = (char **)&s_phostent_addr;
#if DNS_DEBUG
/* dump hostent */
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name == %s\n", s_hostent.h_name));
- LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases == %p\n", s_hostent.h_aliases));
- if (s_hostent.h_aliases != NULL) {
- u8_t idx;
- for ( idx=0; s_hostent.h_aliases[idx]; idx++) {
- LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %p\n", idx, s_hostent.h_aliases[idx]));
- LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %s\n", idx, s_hostent.h_aliases[idx]));
- }
- }
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_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", s_hostent.h_addr_list));
+ 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++) {
+ for (idx = 0; s_hostent.h_addr_list[idx]; idx++) {
LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i] == %p\n", idx, s_hostent.h_addr_list[idx]));
- LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ip_ntoa((ip_addr_t*)s_hostent.h_addr_list[idx])));
+ 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 */
@@ -160,7 +160,7 @@ 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)
+ size_t buflen, struct hostent **result, int *h_errnop)
{
err_t err;
struct gethostbyname_r_helper *h;
@@ -187,14 +187,14 @@ lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
}
namelen = strlen(name);
- if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) {
+ if (buflen < (sizeof(struct gethostbyname_r_helper) + LWIP_MEM_ALIGN_BUFFER(namelen + 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);
+ 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);
@@ -216,7 +216,7 @@ lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
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;
+ ret->h_addr_list = (char **)&h->addr_list;
/* set result != NULL */
*result = ret;
@@ -258,22 +258,25 @@ lwip_freeaddrinfo(struct addrinfo *ai)
*
* @param nodename descriptive name or address string of the host
* (may be NULL -> local address)
- * @param servname port number as string of NULL
+ * @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)
+ const struct addrinfo *hints, struct addrinfo **res)
{
err_t err;
ip_addr_t addr;
struct addrinfo *ai;
- struct sockaddr_in *sa = NULL;
+ 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;
@@ -283,9 +286,25 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
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! */
+ * @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;
@@ -294,38 +313,85 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
if (nodename != NULL) {
/* service location specified, try to resolve */
- err = netconn_gethostbyname(nodename, &addr);
- if (err != ERR_OK) {
- return EAI_FAIL;
+ 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 */
- ip_addr_set_loopback(&addr);
+ if ((hints != NULL) && (hints->ai_flags & AI_PASSIVE)) {
+ ip_addr_set_any_val(ai_family == AF_INET6, addr);
+ } else {
+ ip_addr_set_loopback_val(ai_family == AF_INET6, addr);
+ }
}
- total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_in);
+ total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_storage);
if (nodename != NULL) {
namelen = strlen(nodename);
- LWIP_ASSERT("namelen is too long", (namelen + 1) <= (mem_size_t)-1);
+ 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);
+ total_size <= NETDB_ELEM_SIZE);
ai = (struct addrinfo *)memp_malloc(MEMP_NETDB);
if (ai == NULL) {
- goto memerr;
+ return EAI_MEMORY;
}
memset(ai, 0, total_size);
- sa = (struct sockaddr_in*)((u8_t*)ai + sizeof(struct addrinfo));
- /* set up sockaddr */
- inet_addr_from_ipaddr(&sa->sin_addr, &addr);
- sa->sin_family = AF_INET;
- sa->sin_len = sizeof(struct sockaddr_in);
- sa->sin_port = htons((u16_t)port_nr);
+ /* 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);
+ sa6->sin6_scope_id = ip6_addr_zone(ip_2_ip6(&addr));
+ 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 */
- ai->ai_family = AF_INET;
if (hints != NULL) {
/* copy socktype & protocol from hints if specified */
ai->ai_socktype = hints->ai_socktype;
@@ -333,21 +399,16 @@ lwip_getaddrinfo(const char *nodename, const char *servname,
}
if (nodename != NULL) {
/* copy nodename to canonname if specified */
- ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
+ 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_in);
- ai->ai_addr = (struct sockaddr*)sa;
+ ai->ai_addrlen = sizeof(struct sockaddr_storage);
+ ai->ai_addr = (struct sockaddr *)sa;
*res = ai;
return 0;
-memerr:
- if (ai != NULL) {
- memp_free(MEMP_NETDB, ai);
- }
- return EAI_MEMORY;
}
#endif /* LWIP_DNS && LWIP_SOCKET */
diff --git a/lwip/src/api/netifapi.c b/lwip/src/api/netifapi.c
index 81403f8..415574e 100644
--- a/lwip/src/api/netifapi.c
+++ b/lwip/src/api/netifapi.c
@@ -2,10 +2,17 @@
* @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,
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -14,21 +21,21 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
*/
#include "lwip/opt.h"
@@ -36,59 +43,112 @@
#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
#include "lwip/netifapi.h"
-#include "lwip/tcpip.h"
+#include "lwip/memp.h"
+#include "lwip/priv/tcpip_priv.h"
+
+#include <string.h> /* strncpy */
+
+#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 void
-netifapi_do_netif_add(struct netifapi_msg_msg *msg)
+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,
- msg->msg.add.ipaddr,
- msg->msg.add.netmask,
- msg->msg.add.gw,
+#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)) {
- msg->err = ERR_IF;
+ return ERR_IF;
} else {
- msg->err = ERR_OK;
+ return ERR_OK;
}
- TCPIP_NETIFAPI_ACK(msg);
}
+#if LWIP_IPV4
/**
* Call netif_set_addr() inside the tcpip_thread context.
*/
-static void
-netifapi_do_netif_set_addr(struct netifapi_msg_msg *msg)
+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,
- msg->msg.add.ipaddr,
- msg->msg.add.netmask,
- msg->msg.add.gw);
- msg->err = ERR_OK;
- TCPIP_NETIFAPI_ACK(msg);
+ 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 netif_name_to_index() inside the tcpip_thread context.
+*/
+static err_t
+netifapi_do_name_to_index(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;
+
+ msg->msg.ifs.index = netif_name_to_index(msg->msg.ifs.name);
+ return ERR_OK;
+}
+
+/**
+* Call netif_index_to_name() inside the tcpip_thread context.
+*/
+static err_t
+netifapi_do_index_to_name(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_index_to_name(msg->msg.ifs.index, msg->msg.ifs.name)) {
+ /* return failure via empty name */
+ msg->msg.ifs.name[0] = '\0';
+ }
+ return ERR_OK;
}
/**
* Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the
* tcpip_thread context.
*/
-static void
-netifapi_do_netif_common(struct netifapi_msg_msg *msg)
+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) {
- msg->err = msg->msg.common.errtfunc(msg->netif);
+ return msg->msg.common.errtfunc(msg->netif);
} else {
- msg->err = ERR_OK;
msg->msg.common.voidfunc(msg->netif);
+ return ERR_OK;
}
- TCPIP_NETIFAPI_ACK(msg);
}
/**
+ * @ingroup netifapi_netif
* Call netif_add() in a thread-safe way by running that function inside the
* tcpip_thread context.
*
@@ -96,27 +156,44 @@ netifapi_do_netif_common(struct netifapi_msg_msg *msg)
*/
err_t
netifapi_netif_add(struct netif *netif,
- ip_addr_t *ipaddr,
- ip_addr_t *netmask,
- ip_addr_t *gw,
- void *state,
- netif_init_fn init,
- netif_input_fn input)
+#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)
{
- struct netifapi_msg msg;
- msg.function = netifapi_do_netif_add;
- msg.msg.netif = netif;
- msg.msg.msg.add.ipaddr = ipaddr;
- msg.msg.msg.add.netmask = netmask;
- msg.msg.msg.add.gw = gw;
- msg.msg.msg.add.state = state;
- msg.msg.msg.add.init = init;
- msg.msg.msg.add.input = input;
- TCPIP_NETIFAPI(&msg);
- return msg.msg.err;
+ 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.
*
@@ -124,19 +201,33 @@ netifapi_netif_add(struct netif *netif,
*/
err_t
netifapi_netif_set_addr(struct netif *netif,
- ip_addr_t *ipaddr,
- ip_addr_t *netmask,
- ip_addr_t *gw)
+ const ip4_addr_t *ipaddr,
+ const ip4_addr_t *netmask,
+ const ip4_addr_t *gw)
{
- struct netifapi_msg msg;
- msg.function = netifapi_do_netif_set_addr;
- msg.msg.netif = netif;
- msg.msg.msg.add.ipaddr = ipaddr;
- msg.msg.msg.add.netmask = netmask;
- msg.msg.msg.add.gw = gw;
- TCPIP_NETIFAPI(&msg);
- return msg.msg.err;
+ 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
@@ -146,15 +237,80 @@ netifapi_netif_set_addr(struct netif *netif,
*/
err_t
netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
- netifapi_errt_fn errtfunc)
+ 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;
+}
+
+/**
+* @ingroup netifapi_netif
+* Call netif_name_to_index() in a thread-safe way by running that function inside the
+* tcpip_thread context.
+*
+* @param name the interface name of the netif
+* @param idx output index of the found netif
+*/
+err_t
+netifapi_netif_name_to_index(const char *name, u8_t *idx)
{
- struct netifapi_msg msg;
- msg.function = netifapi_do_netif_common;
- msg.msg.netif = netif;
- msg.msg.msg.common.voidfunc = voidfunc;
- msg.msg.msg.common.errtfunc = errtfunc;
- TCPIP_NETIFAPI(&msg);
- return msg.msg.err;
+ err_t err;
+ NETIFAPI_VAR_DECLARE(msg);
+ NETIFAPI_VAR_ALLOC(msg);
+
+ *idx = 0;
+
+#if LWIP_MPU_COMPATIBLE
+ strncpy(NETIFAPI_VAR_REF(msg).msg.ifs.name, name, NETIF_NAMESIZE - 1);
+ NETIFAPI_VAR_REF(msg).msg.ifs.name[NETIF_NAMESIZE - 1] = '\0';
+#else
+ NETIFAPI_VAR_REF(msg).msg.ifs.name = LWIP_CONST_CAST(char *, name);
+#endif /* LWIP_MPU_COMPATIBLE */
+ err = tcpip_api_call(netifapi_do_name_to_index, &API_VAR_REF(msg).call);
+ if (!err) {
+ *idx = NETIFAPI_VAR_REF(msg).msg.ifs.index;
+ }
+ NETIFAPI_VAR_FREE(msg);
+ return err;
+}
+
+/**
+* @ingroup netifapi_netif
+* Call netif_index_to_name() in a thread-safe way by running that function inside the
+* tcpip_thread context.
+*
+* @param idx the interface index of the netif
+* @param name output name of the found netif, empty '\0' string if netif not found.
+* name should be of at least NETIF_NAMESIZE bytes
+*/
+err_t
+netifapi_netif_index_to_name(u8_t idx, char *name)
+{
+ err_t err;
+ NETIFAPI_VAR_DECLARE(msg);
+ NETIFAPI_VAR_ALLOC(msg);
+
+ NETIFAPI_VAR_REF(msg).msg.ifs.index = idx;
+#if !LWIP_MPU_COMPATIBLE
+ NETIFAPI_VAR_REF(msg).msg.ifs.name = name;
+#endif /* LWIP_MPU_COMPATIBLE */
+ err = tcpip_api_call(netifapi_do_index_to_name, &API_VAR_REF(msg).call);
+#if LWIP_MPU_COMPATIBLE
+ if (!err) {
+ strncpy(name, NETIFAPI_VAR_REF(msg).msg.ifs.name, NETIF_NAMESIZE - 1);
+ name[NETIF_NAMESIZE - 1] = '\0';
+ }
+#endif /* LWIP_MPU_COMPATIBLE */
+ NETIFAPI_VAR_FREE(msg);
+ return err;
}
#endif /* LWIP_NETIF_API */
diff --git a/lwip/src/api/sockets.c b/lwip/src/api/sockets.c
index 6603671..8cb096d 100644
--- a/lwip/src/api/sockets.c
+++ b/lwip/src/api/sockets.c
@@ -2,6 +2,12 @@
* @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
*/
/*
@@ -43,32 +49,76 @@
#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
#include "lwip/sockets.h"
+#include "lwip/priv/sockets_priv.h"
#include "lwip/api.h"
-#include "lwip/sys.h"
#include "lwip/igmp.h"
#include "lwip/inet.h"
#include "lwip/tcp.h"
#include "lwip/raw.h"
#include "lwip/udp.h"
-#include "lwip/tcpip.h"
+#include "lwip/memp.h"
#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/priv/tcpip_priv.h"
+#include "lwip/mld6.h"
#if LWIP_CHECKSUM_ON_COPY
#include "lwip/inet_chksum.h"
#endif
+#if LWIP_COMPAT_SOCKETS == 2 && LWIP_POSIX_SOCKETS_IO_NAMES
+#include <stdarg.h>
+#endif
+
#include <string.h>
-#define IP4ADDR_PORT_TO_SOCKADDR(sin, ipXaddr, port) do { \
+/* 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
+
+#define API_SELECT_CB_VAR_REF(name) API_VAR_REF(name)
+#define API_SELECT_CB_VAR_DECLARE(name) API_VAR_DECLARE(struct lwip_select_cb, name)
+#define API_SELECT_CB_VAR_ALLOC(name, retblock) API_VAR_ALLOC_EXT(struct lwip_select_cb, MEMP_SELECT_CB, name, retblock)
+#define API_SELECT_CB_VAR_FREE(name) API_VAR_FREE(MEMP_SELECT_CB, name)
+
+#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 = htons((port)); \
- inet_addr_from_ipaddr(&(sin)->sin_addr, ipX_2_ip(ipXaddr)); \
+ (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, ipXaddr, port) do { \
- inet_addr_to_ipaddr(ipX_2_ip(ipXaddr), &((sin)->sin_addr)); \
- (port) = ntohs((sin)->sin_port); }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 = ip6_addr_zone(ipaddr); }while(0)
+#define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipaddr, port) do { \
+ inet6_addr_to_ip6addr(ip_2_ip6(ipaddr), &((sin6)->sin6_addr)); \
+ if (ip6_addr_has_scope(ip_2_ip6(ipaddr), IP6_UNKNOWN)) { \
+ ip6_addr_set_zone(ip_2_ip6(ipaddr), (u8_t)((sin6)->sin6_scope_id)); \
+ } \
+ (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) || \
@@ -76,37 +126,32 @@
#define SOCK_ADDR_TYPE_MATCH(name, sock) \
((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \
(((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type))))
-#define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipXaddr, port) do { \
- (sin6)->sin6_len = sizeof(struct sockaddr_in6); \
- (sin6)->sin6_family = AF_INET6; \
- (sin6)->sin6_port = htons((port)); \
- (sin6)->sin6_flowinfo = 0; \
- inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipX_2_ip6(ipXaddr)); }while(0)
-#define IPXADDR_PORT_TO_SOCKADDR(isipv6, sockaddr, ipXaddr, port) do { \
- if (isipv6) { \
- IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ipXaddr, port); \
+#define IPADDR_PORT_TO_SOCKADDR(sockaddr, ipaddr, port) do { \
+ if (IP_IS_ANY_TYPE_VAL(*ipaddr) || IP_IS_V6_VAL(*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), ipXaddr, port); \
- } } while(0)
-#define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipXaddr, port) do { \
- inet6_addr_to_ip6addr(ipX_2_ip6(ipXaddr), &((sin6)->sin6_addr)); \
- (port) = ntohs((sin6)->sin6_port); }while(0)
-#define SOCKADDR_TO_IPXADDR_PORT(isipv6, sockaddr, ipXaddr, port) do { \
- if (isipv6) { \
- SOCKADDR6_TO_IP6ADDR_PORT((struct sockaddr_in6*)(void*)(sockaddr), ipXaddr, port); \
- } else { \
- SOCKADDR4_TO_IP4ADDR_PORT((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port); \
+ 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))
-#else /* LWIP_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 IPXADDR_PORT_TO_SOCKADDR(isipv6, sockaddr, ipXaddr, port) \
- IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port)
-#define SOCKADDR_TO_IPXADDR_PORT(isipv6, sockaddr, ipXaddr, port) \
- SOCKADDR4_TO_IP4ADDR_PORT((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port)
+#define 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 */
@@ -117,190 +162,283 @@
#define IS_SOCK_ADDR_ALIGNED(name) ((((mem_ptr_t)(name)) % 4) == 0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype) do { if ((optlen) < sizeof(opttype)) { done_socket(sock); return EINVAL; }}while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, opttype) do { \
+ LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype); \
+ if ((sock)->conn == NULL) { done_socket(sock); return EINVAL; } }while(0)
+#define LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, opttype) do { \
+ LWIP_SOCKOPT_CHECK_OPTLEN(sock, optlen, opttype); \
+ if (((sock)->conn == NULL) || ((sock)->conn->pcb.tcp == NULL)) { done_socket(sock); 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) { done_socket(sock); 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); \
+ done_socket(sock); \
+ 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) ((long)*(const int*)(optval))
+#else
+#define LWIP_SO_SNDRCVTIMEO_OPTTYPE struct timeval
+#define LWIP_SO_SNDRCVTIMEO_SET(optval, val) do { \
+ u32_t loc = (val); \
+ ((struct timeval *)(optval))->tv_sec = (long)((loc) / 1000U); \
+ ((struct timeval *)(optval))->tv_usec = (long)(((loc) % 1000U) * 1000U); }while(0)
+#define LWIP_SO_SNDRCVTIMEO_GET_MS(optval) ((((const struct timeval *)(optval))->tv_sec * 1000) + (((const struct timeval *)(optval))->tv_usec / 1000))
+#endif
-#define NUM_SOCKETS MEMP_NUM_NETCONN
-
-/** Contains all internal pointers and states used for a socket */
-struct lwip_sock {
- /** sockets currently are built on netconns, each socket has one netconn */
- struct netconn *conn;
- /** data that was left from the previous read */
- void *lastdata;
- /** offset in the data that was left from the previous read */
- u16_t lastoffset;
- /** number of times data was received, set by event_callback(),
- tested by the receive and select functions */
- s16_t rcvevent;
- /** number of times data was ACKed (free send buffer), set by event_callback(),
- tested by select */
- u16_t sendevent;
- /** error happened for this socket, set by event_callback(), tested by select */
- u16_t errevent;
- /** last error that occurred on this socket */
- int err;
- /** counter of how many threads are waiting for this socket using select */
- int select_waiting;
-};
-
-/** Description for a task waiting in select */
-struct lwip_select_cb {
- /** Pointer to the next waiting task */
- struct lwip_select_cb *next;
- /** Pointer to the previous waiting task */
- struct lwip_select_cb *prev;
- /** readset passed to select */
- fd_set *readset;
- /** writeset passed to select */
- fd_set *writeset;
- /** unimplemented: exceptset passed to select */
- fd_set *exceptset;
- /** don't signal the same semaphore twice: set to 1 when signalled */
- int sem_signalled;
- /** semaphore to wake up a task waiting for select */
- sys_sem_t sem;
-};
-
-/** This struct is used to pass data to the set/getsockopt_internal
- * functions running in tcpip_thread context (only a void* is allowed) */
-struct lwip_setgetsockopt_data {
- /** socket struct for which to change options */
- struct lwip_sock *sock;
-#ifdef LWIP_DEBUG
- /** socket index for which to change options */
- int s;
-#endif /* LWIP_DEBUG */
- /** level of the option to process */
- int level;
- /** name of the option to process */
- int optname;
- /** set: value to set the option to
- * get: value of the option is stored here */
- void *optval;
- /** size of *optval */
- socklen_t *optlen;
- /** if an error occures, it is temporarily stored here */
- err_t err;
-};
/** A struct sockaddr replacement that has the same alignment as sockaddr_in/
* sockaddr_in6 if instantiated.
*/
union sockaddr_aligned {
- struct sockaddr sa;
+ struct sockaddr sa;
#if LWIP_IPV6
- struct sockaddr_in6 sin6;
+ struct sockaddr_in6 sin6;
#endif /* LWIP_IPV6 */
- struct sockaddr_in sin;
+#if LWIP_IPV4
+ struct sockaddr_in sin;
+#endif /* LWIP_IPV4 */
};
+/* 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
-/** The global array of available sockets */
-static struct lwip_sock sockets[NUM_SOCKETS];
-/** The global list of tasks waiting for select */
-static struct lwip_select_cb *select_cb_list;
-/** This counter is increased from lwip_select when the list is chagned
- and checked in event_callback to see if it has changed. */
-static volatile int select_cb_ctr;
+#if LWIP_IGMP
+/* 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 */
-/** Table to quickly map an lwIP error (err_t) to a socket error
- * by using -err as an index */
-static const int err_to_errno_table[] = {
- 0, /* ERR_OK 0 No error, everything OK. */
- ENOMEM, /* ERR_MEM -1 Out of memory error. */
- ENOBUFS, /* ERR_BUF -2 Buffer error. */
- EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */
- EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */
- EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */
- EINVAL, /* ERR_VAL -6 Illegal value. */
- EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */
- EADDRINUSE, /* ERR_USE -8 Address in use. */
- EALREADY, /* ERR_ISCONN -9 Already connected. */
- ECONNABORTED, /* ERR_ABRT -10 Connection aborted. */
- ECONNRESET, /* ERR_RST -11 Connection reset. */
- ENOTCONN, /* ERR_CLSD -12 Connection closed. */
- ENOTCONN, /* ERR_CONN -13 Not connected. */
- EIO, /* ERR_ARG -14 Illegal argument. */
- -1, /* ERR_IF -15 Low-level netif error */
+#if LWIP_IPV6_MLD
+/* This is to keep track of IP_JOIN_GROUP calls to drop the membership when
+ a socket is closed */
+struct lwip_socket_multicast_mld6_pair {
+ /** the socket */
+ struct lwip_sock *sock;
+ /** the interface index */
+ u8_t if_idx;
+ /** the group address */
+ ip6_addr_t multi_addr;
};
-#define ERR_TO_ERRNO_TABLE_SIZE \
- (sizeof(err_to_errno_table)/sizeof(err_to_errno_table[0]))
+struct lwip_socket_multicast_mld6_pair socket_ipv6_multicast_memberships[LWIP_SOCKET_MAX_MEMBERSHIPS];
-#define err_to_errno(err) \
- ((unsigned)(-(err)) < ERR_TO_ERRNO_TABLE_SIZE ? \
- err_to_errno_table[-(err)] : EIO)
+static int lwip_socket_register_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr);
+static void lwip_socket_unregister_mld6_membership(int s, unsigned int if_idx, const ip6_addr_t *multi_addr);
+static void lwip_socket_drop_registered_mld6_memberships(int s);
+#endif /* LWIP_IGMP */
-#ifdef ERRNO
-#ifndef set_errno
-#define set_errno(err) errno = (err)
-#endif
-#else /* ERRNO */
-#define set_errno(err)
-#endif /* ERRNO */
+/** The global array of available sockets */
+static struct lwip_sock sockets[NUM_SOCKETS];
+
+#if LWIP_SOCKET_SELECT
+#if LWIP_TCPIP_CORE_LOCKING
+/* protect the select_cb_list using core lock */
+#define LWIP_SOCKET_SELECT_DECL_PROTECT(lev)
+#define LWIP_SOCKET_SELECT_PROTECT(lev) LOCK_TCPIP_CORE()
+#define LWIP_SOCKET_SELECT_UNPROTECT(lev) UNLOCK_TCPIP_CORE()
+#else /* LWIP_TCPIP_CORE_LOCKING */
+/* protect the select_cb_list using SYS_LIGHTWEIGHT_PROT */
+#define LWIP_SOCKET_SELECT_DECL_PROTECT(lev) SYS_ARCH_DECL_PROTECT(lev)
+#define LWIP_SOCKET_SELECT_PROTECT(lev) SYS_ARCH_PROTECT(lev)
+#define LWIP_SOCKET_SELECT_UNPROTECT(lev) SYS_ARCH_UNPROTECT(lev)
+/** This counter is increased from lwip_select when the list is changed
+ and checked in select_check_waiters to see if it has changed. */
+static volatile int select_cb_ctr;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+/** The global list of tasks waiting for select */
+static struct lwip_select_cb *select_cb_list;
+#endif /* LWIP_SOCKET_SELECT */
#define sock_set_errno(sk, e) do { \
- sk->err = (e); \
- set_errno(sk->err); \
+ const int sockerr = (e); \
+ set_errno(sockerr); \
} while (0)
-/* Forward delcaration of some functions */
+/* Forward declaration of some functions */
+#if LWIP_SOCKET_SELECT
static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
-static void lwip_getsockopt_internal(void *arg);
-static void lwip_setsockopt_internal(void *arg);
+#define DEFAULT_SOCKET_EVENTCB event_callback
+static void select_check_waiters(int s, int has_recvevent, int has_sendevent, int has_errevent);
+#else
+#define DEFAULT_SOCKET_EVENTCB NULL
+#endif
+#if !LWIP_TCPIP_CORE_LOCKING
+static void lwip_getsockopt_callback(void *arg);
+static void lwip_setsockopt_callback(void *arg);
+#endif
+static int lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen);
+static int lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen);
+static void free_socket(struct lwip_sock *sock, int is_tcp);
-/**
- * Initialize this module. This function has to be called before any other
- * functions in this module!
- */
+#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_init(void)
+lwip_socket_thread_init(void)
{
+ netconn_thread_init();
}
-/**
- * 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
+/** LWIP_NETCONN_SEM_PER_THREAD==1: destroy thread-local semaphore */
+void
+lwip_socket_thread_cleanup(void)
+{
+ netconn_thread_cleanup();
+}
+
+#if LWIP_NETCONN_FULLDUPLEX
+/* Thread-safe increment of sock->fd_used, with overflow check */
+static void
+sock_inc_used(struct lwip_sock *sock)
+{
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_ASSERT("sock != NULL", sock != NULL);
+
+ SYS_ARCH_PROTECT(lev);
+ ++sock->fd_used;
+ LWIP_ASSERT("sock->fd_used != 0", sock->fd_used != 0);
+ SYS_ARCH_UNPROTECT(lev);
+}
+
+/* In full-duplex mode,sock->fd_used != 0 prevents a socket descriptor from being
+ * released (and possibly reused) when used from more than one thread
+ * (e.g. read-while-write or close-while-write, etc)
+ * This function is called at the end of functions using (try)get_socket*().
*/
-static struct lwip_sock *
-get_socket(int s)
+static void
+done_socket(struct lwip_sock *sock)
{
- struct lwip_sock *sock;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_ASSERT("sock != NULL", sock != NULL);
+
+ SYS_ARCH_PROTECT(lev);
+ LWIP_ASSERT("sock->fd_used > 0", sock->fd_used > 0);
+ if (--sock->fd_used == 0) {
+ if (sock->fd_free_pending) {
+ /* free the socket */
+ sock->fd_used = 1;
+ free_socket(sock, sock->fd_free_pending & LWIP_SOCK_FD_FREE_TCP);
+ }
+ }
+ SYS_ARCH_UNPROTECT(lev);
+}
+#else /* LWIP_NETCONN_FULLDUPLEX */
+#define sock_inc_used(sock)
+#define done_socket(sock)
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+/* Translate a socket 'int' into a pointer (only fails if the index is invalid) */
+static struct lwip_sock *
+tryget_socket_unconn_nouse(int fd)
+{
+ int s = fd - LWIP_SOCKET_OFFSET;
if ((s < 0) || (s >= NUM_SOCKETS)) {
- LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s));
- set_errno(EBADF);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("tryget_socket_unconn(%d): invalid\n", fd));
return NULL;
}
+ return &sockets[s];
+}
- sock = &sockets[s];
+struct lwip_sock *
+lwip_socket_dbg_get_socket(int fd)
+{
+ return tryget_socket_unconn_nouse(fd);
+}
- if (!sock->conn) {
- LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s));
- set_errno(EBADF);
- return NULL;
+/* Translate a socket 'int' into a pointer (only fails if the index is invalid) */
+static struct lwip_sock *
+tryget_socket_unconn(int fd)
+{
+ struct lwip_sock *ret = tryget_socket_unconn_nouse(fd);
+ if (ret != NULL) {
+ sock_inc_used(ret);
}
-
- return sock;
+ return ret;
}
/**
* Same as get_socket but doesn't set errno
*
- * @param s externally used socket index
+ * @param fd externally used socket index
* @return struct lwip_sock for the socket or NULL if not found
*/
static struct lwip_sock *
-tryget_socket(int s)
+tryget_socket(int fd)
{
- if ((s < 0) || (s >= NUM_SOCKETS)) {
- return NULL;
+ struct lwip_sock *sock = tryget_socket_unconn(fd);
+ if (sock != NULL) {
+ if (sock->conn) {
+ return sock;
+ }
+ done_socket(sock);
}
- if (!sockets[s].conn) {
+ return NULL;
+}
+
+/**
+ * Map a externally used socket index to the internal socket representation.
+ *
+ * @param fd externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+get_socket(int fd)
+{
+ struct lwip_sock *sock = tryget_socket(fd);
+ if (!sock) {
+ if ((fd < LWIP_SOCKET_OFFSET) || (fd >= (LWIP_SOCKET_OFFSET + NUM_SOCKETS))) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", fd));
+ }
+ set_errno(EBADF);
return NULL;
}
- return &sockets[s];
+ return sock;
}
/**
@@ -316,26 +454,35 @@ alloc_socket(struct netconn *newconn, int accepted)
{
int i;
SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_UNUSED_ARG(accepted);
/* allocate a new socket identifier */
for (i = 0; i < NUM_SOCKETS; ++i) {
/* Protect socket array */
SYS_ARCH_PROTECT(lev);
if (!sockets[i].conn) {
+#if LWIP_NETCONN_FULLDUPLEX
+ if (sockets[i].fd_used) {
+ SYS_ARCH_UNPROTECT(lev);
+ continue;
+ }
+ sockets[i].fd_used = 1;
+ sockets[i].fd_free_pending = 0;
+#endif
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].lastdata.pbuf = NULL;
+#if LWIP_SOCKET_SELECT
+ LWIP_ASSERT("sockets[i].select_waiting == 0", sockets[i].select_waiting == 0);
sockets[i].rcvevent = 0;
/* TCP sendbuf is empty, but the socket is not yet writable until connected
* (unless it has been created by accept()). */
sockets[i].sendevent = (NETCONNTYPE_GROUP(newconn->type) == NETCONN_TCP ? (accepted != 0) : 1);
sockets[i].errevent = 0;
- sockets[i].err = 0;
- sockets[i].select_waiting = 0;
- return i;
+#endif /* LWIP_SOCKET_SELECT */
+ return i + LWIP_SOCKET_OFFSET;
}
SYS_ARCH_UNPROTECT(lev);
}
@@ -351,25 +498,32 @@ alloc_socket(struct netconn *newconn, int accepted)
static void
free_socket(struct lwip_sock *sock, int is_tcp)
{
- void *lastdata;
+ union lwip_sock_lastdata lastdata;
SYS_ARCH_DECL_PROTECT(lev);
- lastdata = sock->lastdata;
- sock->lastdata = NULL;
- sock->lastoffset = 0;
- sock->err = 0;
-
/* Protect socket array */
SYS_ARCH_PROTECT(lev);
- sock->conn = NULL;
+
+#if LWIP_NETCONN_FULLDUPLEX
+ LWIP_ASSERT("sock->fd_used > 0", sock->fd_used > 0);
+ if (--sock->fd_used > 0) {
+ sock->fd_free_pending = LWIP_SOCK_FD_FREE_FREE | is_tcp ? LWIP_SOCK_FD_FREE_TCP : 0;
+ SYS_ARCH_UNPROTECT(lev);
+ return;
+ }
+#endif
+
+ lastdata = sock->lastdata;
+ sock->lastdata.pbuf = NULL;
+ sock->conn = NULL;
SYS_ARCH_UNPROTECT(lev);
/* don't use 'sock' after this line, as another task might have allocated it */
- if (lastdata != NULL) {
+ if (lastdata.pbuf != NULL) {
if (is_tcp) {
- pbuf_free((struct pbuf *)lastdata);
+ pbuf_free(lastdata.pbuf);
} else {
- netbuf_delete((struct netbuf *)lastdata);
+ netbuf_delete(lastdata.netbuf);
}
}
}
@@ -385,10 +539,11 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
{
struct lwip_sock *sock, *nsock;
struct netconn *newconn;
- ipX_addr_t naddr;
+ ip_addr_t naddr;
u16_t port = 0;
int newsock;
err_t err;
+ int recvevent;
SYS_ARCH_DECL_PROTECT(lev);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s));
@@ -397,77 +552,83 @@ lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
return -1;
}
- if (netconn_is_nonblocking(sock->conn) && (sock->rcvevent <= 0)) {
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s));
- sock_set_errno(sock, EWOULDBLOCK);
- return -1;
- }
-
/* wait for a new connection */
err = netconn_accept(sock->conn, &newconn);
if (err != ERR_OK) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err));
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
sock_set_errno(sock, EOPNOTSUPP);
- return EOPNOTSUPP;
+ } else if (err == ERR_CLSD) {
+ sock_set_errno(sock, EINVAL);
+ } else {
+ sock_set_errno(sock, err_to_errno(err));
}
- sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
return -1;
}
LWIP_ASSERT("newconn != NULL", newconn != NULL);
- /* Prevent automatic window updates, we do this on our own! */
- netconn_set_noautorecved(newconn, 1);
+
+ newsock = alloc_socket(newconn, 1);
+ if (newsock == -1) {
+ netconn_delete(newconn);
+ sock_set_errno(sock, ENFILE);
+ done_socket(sock);
+ return -1;
+ }
+ LWIP_ASSERT("invalid socket index", (newsock >= LWIP_SOCKET_OFFSET) && (newsock < NUM_SOCKETS + LWIP_SOCKET_OFFSET));
+ 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);
+ recvevent = (s16_t)(-1 - newconn->socket);
+ newconn->socket = newsock;
+ SYS_ARCH_UNPROTECT(lev);
+
+ if (newconn->callback) {
+ LOCK_TCPIP_CORE();
+ while (recvevent > 0) {
+ recvevent--;
+ newconn->callback(newconn, NETCONN_EVT_RCVPLUS, 0);
+ }
+ UNLOCK_TCPIP_CORE();
+ }
/* Note that POSIX only requires us to check addr is non-NULL. addrlen must
* not be NULL if addr is valid.
*/
- if (addr != NULL) {
+ if ((addr != NULL) && (addrlen != NULL)) {
union sockaddr_aligned tempaddr;
/* get the IP address and port of the remote host */
- err = netconn_peer(newconn, ipX_2_ip(&naddr), &port);
+ 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));
+ done_socket(sock);
return -1;
}
- LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL);
- IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(newconn->type), &tempaddr, &naddr, port);
+ IPADDR_PORT_TO_SOCKADDR(&tempaddr, &naddr, port);
if (*addrlen > tempaddr.sa.sa_len) {
*addrlen = tempaddr.sa.sa_len;
}
MEMCPY(addr, &tempaddr, *addrlen);
- }
-
- newsock = alloc_socket(newconn, 1);
- if (newsock == -1) {
- netconn_delete(newconn);
- sock_set_errno(sock, ENFILE);
- return -1;
- }
- LWIP_ASSERT("invalid socket index", (newsock >= 0) && (newsock < NUM_SOCKETS));
- LWIP_ASSERT("newconn->callback == event_callback", newconn->callback == event_callback);
- nsock = &sockets[newsock];
-
- /* See event_callback: If data comes in right away after an accept, even
- * though the server task might not have created a new socket yet.
- * In that case, newconn->socket is counted down (newconn->socket--),
- * so nsock->rcvevent is >= 1 here!
- */
- SYS_ARCH_PROTECT(lev);
- nsock->rcvevent += (s16_t)(-1 - newconn->socket);
- newconn->socket = newsock;
- SYS_ARCH_UNPROTECT(lev);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d", s, newsock));
- if (addr != NULL) {
- LWIP_DEBUGF(SOCKETS_DEBUG, (" addr="));
- ipX_addr_debug_print(NETCONNTYPE_ISIPV6(newconn->type), SOCKETS_DEBUG, &naddr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("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);
+ done_socket(sock);
+ done_socket(nsock);
return newsock;
}
@@ -475,7 +636,7 @@ int
lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
{
struct lwip_sock *sock;
- ipX_addr_t local_addr;
+ ip_addr_t local_addr;
u16_t local_port;
err_t err;
@@ -487,30 +648,41 @@ lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
if (!SOCK_ADDR_TYPE_MATCH(name, sock)) {
/* sockaddr does not match socket type (IPv4/IPv6) */
sock_set_errno(sock, err_to_errno(ERR_VAL));
+ done_socket(sock);
return -1;
}
- /* check size, familiy and alignment of 'name' */
+ /* 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;);
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
LWIP_UNUSED_ARG(namelen);
- SOCKADDR_TO_IPXADDR_PORT((name->sa_family == AF_INET6), name, &local_addr, local_port);
+ SOCKADDR_TO_IPADDR_PORT(name, &local_addr, local_port);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s));
- ipX_addr_debug_print(name->sa_family == AF_INET6, SOCKETS_DEBUG, &local_addr);
+ ip_addr_debug_print_val(SOCKETS_DEBUG, local_addr);
LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port));
- err = netconn_bind(sock->conn, ipX_2_ip(&local_addr), local_port);
+#if 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));
+ done_socket(sock);
return -1;
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s));
sock_set_errno(sock, 0);
+ done_socket(sock);
return 0;
}
@@ -519,6 +691,7 @@ lwip_close(int s)
{
struct lwip_sock *sock;
int is_tcp = 0;
+ err_t err;
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
@@ -527,13 +700,27 @@ lwip_close(int s)
return -1;
}
- if(sock->conn != NULL) {
+ if (sock->conn != NULL) {
is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP;
} else {
- LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL);
+ LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata.pbuf == NULL);
}
- netconn_delete(sock->conn);
+#if LWIP_IGMP
+ /* drop all possibly joined IGMP memberships */
+ lwip_socket_drop_registered_memberships(s);
+#endif /* LWIP_IGMP */
+#if LWIP_IPV6_MLD
+ /* drop all possibly joined MLD6 memberships */
+ lwip_socket_drop_registered_mld6_memberships(s);
+#endif /* LWIP_IPV6_MLD */
+
+ err = netconn_delete(sock->conn);
+ if (err != ERR_OK) {
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ return -1;
+ }
free_socket(sock, is_tcp);
set_errno(0);
@@ -553,37 +740,50 @@ lwip_connect(int s, const struct sockaddr *name, socklen_t namelen)
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;
+ sock_set_errno(sock, err_to_errno(ERR_VAL));
+ done_socket(sock);
+ return -1;
}
- /* check size, familiy and alignment of 'name' */
- LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) &&
- IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name),
- sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
LWIP_UNUSED_ARG(namelen);
if (name->sa_family == AF_UNSPEC) {
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s));
err = netconn_disconnect(sock->conn);
} else {
- ipX_addr_t remote_addr;
+ ip_addr_t remote_addr;
u16_t remote_port;
- SOCKADDR_TO_IPXADDR_PORT((name->sa_family == AF_INET6), name, &remote_addr, 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)); done_socket(sock); return -1;);
+
+ SOCKADDR_TO_IPADDR_PORT(name, &remote_addr, remote_port);
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s));
- ipX_addr_debug_print(name->sa_family == AF_INET6, SOCKETS_DEBUG, &remote_addr);
+ ip_addr_debug_print_val(SOCKETS_DEBUG, remote_addr);
LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port));
- err = netconn_connect(sock->conn, ipX_2_ip(&remote_addr), remote_port);
+#if 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));
+ done_socket(sock);
return -1;
}
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s));
sock_set_errno(sock, 0);
+ done_socket(sock);
return 0;
}
@@ -617,76 +817,70 @@ lwip_listen(int s, int backlog)
LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err));
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
sock_set_errno(sock, EOPNOTSUPP);
- return EOPNOTSUPP;
+ } else {
+ sock_set_errno(sock, err_to_errno(err));
}
- sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
return -1;
}
sock_set_errno(sock, 0);
+ done_socket(sock);
return 0;
}
-int
-lwip_recvfrom(int s, void *mem, size_t len, int flags,
- struct sockaddr *from, socklen_t *fromlen)
+#if LWIP_TCP
+/* Helper function to loop over receiving pbufs from netconn
+ * until "len" bytes are received or we're otherwise done.
+ * Keeps sock->lastdata for peeking or partly copying.
+ */
+static ssize_t
+lwip_recv_tcp(struct lwip_sock *sock, void *mem, size_t len, int flags)
{
- struct lwip_sock *sock;
- void *buf = NULL;
- struct pbuf *p;
- u16_t buflen, copylen;
- int off = 0;
- u8_t done = 0;
- err_t err;
+ u8_t apiflags = NETCONN_NOAUTORCVD;
+ ssize_t recvd = 0;
+ ssize_t recv_left = (len <= SSIZE_MAX) ? (ssize_t)len : SSIZE_MAX;
- 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;
+ LWIP_ASSERT("no socket given", sock != NULL);
+ LWIP_ASSERT("this should be checked internally", NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP);
+
+ if (flags & MSG_DONTWAIT) {
+ apiflags |= NETCONN_DONTBLOCK;
}
do {
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", sock->lastdata));
+ struct pbuf *p;
+ err_t err;
+ u16_t copylen;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: top while sock->lastdata=%p\n", (void *)sock->lastdata.pbuf));
/* Check if there is data left from the last recv operation. */
- if (sock->lastdata) {
- buf = sock->lastdata;
+ if (sock->lastdata.pbuf) {
+ p = sock->lastdata.pbuf;
} else {
- /* If this is non-blocking call, then check first */
- if (((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) &&
- (sock->rcvevent <= 0)) {
- if (off > 0) {
- /* update receive window */
- netconn_recved(sock->conn, (u32_t)off);
- /* already received data, return that */
- sock_set_errno(sock, 0);
- return off;
- }
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s));
- sock_set_errno(sock, EWOULDBLOCK);
- return -1;
- }
-
/* No data was left from the previous operation, so we try to get
some from the network. */
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
- err = netconn_recv_tcp_pbuf(sock->conn, (struct pbuf **)&buf);
- } else {
- err = netconn_recv(sock->conn, (struct netbuf **)&buf);
- }
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv err=%d, netbuf=%p\n",
- err, buf));
+ err = netconn_recv_tcp_pbuf_flags(sock->conn, &p, apiflags);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: netconn_recv err=%d, pbuf=%p\n",
+ err, (void *)p));
if (err != ERR_OK) {
- if (off > 0) {
- /* update receive window */
- netconn_recved(sock->conn, (u32_t)off);
- /* already received data, return that */
- sock_set_errno(sock, 0);
- return off;
+ if (recvd > 0) {
+ /* already received data, return that (this trusts in getting the same error from
+ netconn layer again next time netconn_recv is called) */
+ if (err == ERR_CLSD) {
+ /* closed but already received data, ensure select gets the FIN, too */
+ if (sock->conn->callback != NULL) {
+ LOCK_TCPIP_CORE();
+ sock->conn->callback(sock->conn, NETCONN_EVT_RCVPLUS, 0);
+ UNLOCK_TCPIP_CORE();
+ }
+ }
+ goto lwip_recv_tcp_done;
}
/* 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)));
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: p == NULL, error is \"%s\"!\n",
+ lwip_strerr(err)));
sock_set_errno(sock, err_to_errno(err));
if (err == ERR_CLSD) {
return 0;
@@ -694,125 +888,424 @@ lwip_recvfrom(int s, void *mem, size_t len, int flags,
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;
+ LWIP_ASSERT("p != NULL", p != NULL);
+ sock->lastdata.pbuf = 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;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: buflen=%"U16_F" recv_left=%d off=%d\n",
+ p->tot_len, (int)recv_left, (int)recvd));
- if (len > buflen) {
- copylen = buflen;
+ if (recv_left > p->tot_len) {
+ copylen = p->tot_len;
} else {
- copylen = (u16_t)len;
+ copylen = (u16_t)recv_left;
+ }
+ if (recvd + copylen < recvd) {
+ /* overflow */
+ copylen = (u16_t)(SSIZE_MAX - recvd);
}
/* 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;
+ pbuf_copy_partial(p, (u8_t *)mem + recvd, copylen, 0);
+
+ recvd += copylen;
+
+ /* TCP combines multiple pbufs for one recv */
+ LWIP_ASSERT("invalid copylen, len would underflow", recv_left >= copylen);
+ recv_left -= copylen;
+
+ /* Unless we peek the incoming message... */
+ if ((flags & MSG_PEEK) == 0) {
+ /* ... check if there is data left in the pbuf */
+ LWIP_ASSERT("invalid copylen", p->tot_len >= copylen);
+ if (p->tot_len - copylen > 0) {
+ /* If so, it should be saved in the sock structure for the next recv call.
+ We store the pbuf but hide/free the consumed data: */
+ sock->lastdata.pbuf = pbuf_free_header(p, copylen);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: lastdata now pbuf=%p\n", (void *)sock->lastdata.pbuf));
+ } else {
+ sock->lastdata.pbuf = NULL;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recv_tcp: deleting pbuf=%p\n", (void *)p));
+ pbuf_free(p);
}
+ }
+ /* once we have some data to return, only add more if we don't need to wait */
+ apiflags |= NETCONN_DONTBLOCK;
+ /* @todo: do we need to support peeking more than one pbuf? */
+ } while ((recv_left > 0) || (flags & MSG_PEEK));
+lwip_recv_tcp_done:
+ if (recvd > 0) {
+ /* ensure window update after copying all data */
+ netconn_tcp_recvd(sock->conn, (size_t)recvd);
+ }
+ sock_set_errno(sock, 0);
+ return recvd;
+}
+#endif
+
+/* Convert a netbuf's address data to struct sockaddr */
+static int
+lwip_sock_make_addr(struct netconn *conn, ip_addr_t *fromaddr, u16_t port,
+ struct sockaddr *from, socklen_t *fromlen)
+{
+ int truncated = 0;
+ union sockaddr_aligned saddr;
+
+ LWIP_UNUSED_ARG(conn);
+
+ LWIP_ASSERT("fromaddr != NULL", fromaddr != NULL);
+ LWIP_ASSERT("from != NULL", from != NULL);
+ LWIP_ASSERT("fromlen != NULL", fromlen != NULL);
+
+#if LWIP_IPV4 && LWIP_IPV6
+ /* Dual-stack: Map IPv4 addresses to IPv4 mapped IPv6 */
+ if (NETCONNTYPE_ISIPV6(netconn_type(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);
+ if (*fromlen < saddr.sa.sa_len) {
+ truncated = 1;
+ } else if (*fromlen > saddr.sa.sa_len) {
+ *fromlen = saddr.sa.sa_len;
+ }
+ MEMCPY(from, &saddr, *fromlen);
+ return truncated;
+}
+
+#if LWIP_TCP
+/* Helper function to get a tcp socket's remote address info */
+static int
+lwip_recv_tcp_from(struct lwip_sock *sock, struct sockaddr *from, socklen_t *fromlen, const char *dbg_fn, int dbg_s, ssize_t dbg_ret)
+{
+ if (sock == NULL) {
+ return 0;
+ }
+ LWIP_UNUSED_ARG(dbg_fn);
+ LWIP_UNUSED_ARG(dbg_s);
+ LWIP_UNUSED_ARG(dbg_ret);
+
+#if !SOCKETS_DEBUG
+ if (from && fromlen)
+#endif /* !SOCKETS_DEBUG */
+ {
+ /* get remote addr/port from tcp_pcb */
+ u16_t port;
+ ip_addr_t tmpaddr;
+ netconn_getaddr(sock->conn, &tmpaddr, &port, 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("%s(%d): addr=", dbg_fn, dbg_s));
+ ip_addr_debug_print_val(SOCKETS_DEBUG, tmpaddr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, (int)dbg_ret));
+ if (from && fromlen) {
+ return lwip_sock_make_addr(sock->conn, &tmpaddr, port, from, fromlen);
+ }
+ }
+ return 0;
+}
+#endif
+
+/* Helper function to receive a netbuf from a udp or raw netconn.
+ * Keeps sock->lastdata for peeking.
+ */
+static err_t
+lwip_recvfrom_udp_raw(struct lwip_sock *sock, int flags, struct msghdr *msg, u16_t *datagram_len, int dbg_s)
+{
+ struct netbuf *buf;
+ u8_t apiflags;
+ err_t err;
+ u16_t buflen, copylen, copied;
+ int i;
+
+ LWIP_UNUSED_ARG(dbg_s);
+ LWIP_ERROR("lwip_recvfrom_udp_raw: invalid arguments", (msg->msg_iov != NULL) || (msg->msg_iovlen <= 0), return ERR_ARG;);
+
+ if (flags & MSG_DONTWAIT) {
+ apiflags = NETCONN_DONTBLOCK;
+ } else {
+ apiflags = 0;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw[UDP/RAW]: top sock->lastdata=%p\n", (void *)sock->lastdata.netbuf));
+ /* Check if there is data left from the last recv operation. */
+ buf = sock->lastdata.netbuf;
+ if (buf == NULL) {
+ /* No data was left from the previous operation, so we try to get
+ some from the network. */
+ err = netconn_recv_udp_raw_netbuf_flags(sock->conn, &buf, apiflags);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw[UDP/RAW]: netconn_recv err=%d, netbuf=%p\n",
+ err, (void *)buf));
+
+ if (err != ERR_OK) {
+ return err;
+ }
+ LWIP_ASSERT("buf != NULL", buf != NULL);
+ sock->lastdata.netbuf = buf;
+ }
+ buflen = buf->p->tot_len;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw: buflen=%"U16_F"\n", buflen));
+
+ copied = 0;
+ /* copy the pbuf payload into the iovs */
+ for (i = 0; (i < msg->msg_iovlen) && (copied < buflen); i++) {
+ u16_t len_left = (u16_t)(buflen - copied);
+ if (msg->msg_iov[i].iov_len > len_left) {
+ copylen = len_left;
} else {
- done = 1;
+ copylen = (u16_t)msg->msg_iov[i].iov_len;
}
- /* Check to see from where the data was.*/
- if (done) {
+ /* copy the contents of the received buffer into
+ the supplied memory buffer */
+ pbuf_copy_partial(buf->p, (u8_t *)msg->msg_iov[i].iov_base, copylen, copied);
+ copied = (u16_t)(copied + copylen);
+ }
+
+ /* Check to see from where the data was.*/
#if !SOCKETS_DEBUG
- if (from && fromlen)
+ if (msg->msg_name && msg->msg_namelen)
#endif /* !SOCKETS_DEBUG */
- {
- u16_t port;
- ipX_addr_t tmpaddr;
- ipX_addr_t *fromaddr;
- union sockaddr_aligned saddr;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s));
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
- fromaddr = &tmpaddr;
- /* @todo: this does not work for IPv6, yet */
- netconn_getaddr(sock->conn, ipX_2_ip(fromaddr), &port, 0);
- } else {
- port = netbuf_fromport((struct netbuf *)buf);
- fromaddr = netbuf_fromaddr_ipX((struct netbuf *)buf);
- }
- IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)),
- &saddr, fromaddr, port);
- ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)),
- SOCKETS_DEBUG, fromaddr);
- LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off));
-#if SOCKETS_DEBUG
- if (from && fromlen)
-#endif /* SOCKETS_DEBUG */
- {
- if (*fromlen > saddr.sa.sa_len) {
- *fromlen = saddr.sa.sa_len;
- }
- MEMCPY(from, &saddr, *fromlen);
- }
- }
+ {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom_udp_raw(%d): addr=", dbg_s));
+ ip_addr_debug_print_val(SOCKETS_DEBUG, *netbuf_fromaddr(buf));
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", netbuf_fromport(buf), copied));
+ if (msg->msg_name && msg->msg_namelen) {
+ lwip_sock_make_addr(sock->conn, netbuf_fromaddr(buf), netbuf_fromport(buf),
+ (struct sockaddr *)msg->msg_name, &msg->msg_namelen);
}
+ }
- /* 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);
+ /* Initialize flag output */
+ msg->msg_flags = 0;
+
+ if (msg->msg_control) {
+ u8_t wrote_msg = 0;
+#if LWIP_NETBUF_RECVINFO
+ /* Check if packet info was recorded */
+ if (buf->flags & NETBUF_FLAG_DESTADDR) {
+ if (IP_IS_V4(&buf->toaddr)) {
+#if LWIP_IPV4
+ if (msg->msg_controllen >= CMSG_SPACE(sizeof(struct in_pktinfo))) {
+ struct cmsghdr *chdr = CMSG_FIRSTHDR(msg); /* This will always return a header!! */
+ struct in_pktinfo *pkti = (struct in_pktinfo *)CMSG_DATA(chdr);
+ chdr->cmsg_level = IPPROTO_IP;
+ chdr->cmsg_type = IP_PKTINFO;
+ chdr->cmsg_len = CMSG_LEN(sizeof(struct in_pktinfo));
+ pkti->ipi_ifindex = buf->p->if_idx;
+ inet_addr_from_ip4addr(&pkti->ipi_addr, ip_2_ip4(netbuf_destaddr(buf)));
+ msg->msg_controllen = CMSG_SPACE(sizeof(struct in_pktinfo));
+ wrote_msg = 1;
} else {
- netbuf_delete((struct netbuf *)buf);
+ msg->msg_flags |= MSG_CTRUNC;
}
+#endif /* LWIP_IPV4 */
}
}
- } while (!done);
+#endif /* LWIP_NETBUF_RECVINFO */
+
+ if (!wrote_msg) {
+ msg->msg_controllen = 0;
+ }
+ }
+
+ /* If we don't peek the incoming message: zero lastdata pointer and free the netbuf */
+ if ((flags & MSG_PEEK) == 0) {
+ sock->lastdata.netbuf = NULL;
+ netbuf_delete(buf);
+ }
+ if (datagram_len) {
+ *datagram_len = buflen;
+ }
+ return ERR_OK;
+}
+
+ssize_t
+lwip_recvfrom(int s, void *mem, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromlen)
+{
+ struct lwip_sock *sock;
+ ssize_t ret;
- if ((off > 0) && (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP)) {
- /* update receive window */
- netconn_recved(sock->conn, (u32_t)off);
+ 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;
+ }
+#if LWIP_TCP
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+ ret = lwip_recv_tcp(sock, mem, len, flags);
+ lwip_recv_tcp_from(sock, from, fromlen, "lwip_recvfrom", s, ret);
+ done_socket(sock);
+ return ret;
+ } else
+#endif
+ {
+ u16_t datagram_len = 0;
+ struct iovec vec;
+ struct msghdr msg;
+ err_t err;
+ vec.iov_base = mem;
+ vec.iov_len = len;
+ msg.msg_control = NULL;
+ msg.msg_controllen = 0;
+ msg.msg_flags = 0;
+ msg.msg_iov = &vec;
+ msg.msg_iovlen = 1;
+ msg.msg_name = from;
+ msg.msg_namelen = (fromlen ? *fromlen : 0);
+ err = lwip_recvfrom_udp_raw(sock, flags, &msg, &datagram_len, s);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n",
+ s, lwip_strerr(err)));
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ return -1;
+ }
+ ret = (ssize_t)LWIP_MIN(LWIP_MIN(len, datagram_len), SSIZE_MAX);
+ if (fromlen) {
+ *fromlen = msg.msg_namelen;
+ }
}
+
sock_set_errno(sock, 0);
- return off;
+ done_socket(sock);
+ return ret;
}
-int
+ssize_t
lwip_read(int s, void *mem, size_t len)
{
return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
}
-int
+ssize_t
+lwip_readv(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_recvmsg(s, &msg, 0);
+}
+
+ssize_t
lwip_recv(int s, void *mem, size_t len, int flags)
{
return lwip_recvfrom(s, mem, len, flags, NULL, NULL);
}
-int
+ssize_t
+lwip_recvmsg(int s, struct msghdr *message, int flags)
+{
+ struct lwip_sock *sock;
+ int i;
+ ssize_t buflen;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvmsg(%d, message=%p, flags=0x%x)\n", s, (void *)message, flags));
+ LWIP_ERROR("lwip_recvmsg: invalid message pointer", message != NULL, return ERR_ARG;);
+ LWIP_ERROR("lwip_recvmsg: unsupported flags", ((flags == 0) || (flags == MSG_PEEK)),
+ set_errno(EOPNOTSUPP); return -1;);
+
+ if ((message->msg_iovlen <= 0) || (message->msg_iovlen > IOV_MAX)) {
+ set_errno(EMSGSIZE);
+ return -1;
+ }
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ /* check for valid vectors */
+ buflen = 0;
+ for (i = 0; i < message->msg_iovlen; i++) {
+ if ((message->msg_iov[i].iov_base == NULL) || ((ssize_t)message->msg_iov[i].iov_len <= 0) ||
+ ((size_t)(ssize_t)message->msg_iov[i].iov_len != message->msg_iov[i].iov_len) ||
+ ((ssize_t)(buflen + (ssize_t)message->msg_iov[i].iov_len) <= 0)) {
+ sock_set_errno(sock, ERR_VAL);
+ done_socket(sock);
+ return -1;
+ }
+ buflen = (ssize_t)(buflen + (ssize_t)message->msg_iov[i].iov_len);
+ }
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+ int recv_flags = flags;
+ message->msg_flags = 0;
+ /* recv the data */
+ buflen = 0;
+ for (i = 0; i < message->msg_iovlen; i++) {
+ /* try to receive into this vector's buffer */
+ ssize_t recvd_local = lwip_recv_tcp(sock, message->msg_iov[i].iov_base, message->msg_iov[i].iov_len, recv_flags);
+ if (recvd_local > 0) {
+ /* sum up received bytes */
+ buflen += recvd_local;
+ }
+ if ((recvd_local < 0) || (recvd_local < (int)message->msg_iov[i].iov_len) ||
+ (flags & MSG_PEEK)) {
+ /* returned prematurely (or peeking, which might actually be limitated to the first iov) */
+ if (buflen <= 0) {
+ /* nothing received at all, propagate the error */
+ buflen = recvd_local;
+ }
+ break;
+ }
+ /* while MSG_DONTWAIT is not supported for this function, we pass it to
+ lwip_recv_tcp() to prevent waiting for more data */
+ recv_flags |= MSG_DONTWAIT;
+ }
+ if (buflen > 0) {
+ /* reset socket error since we have received something */
+ sock_set_errno(sock, 0);
+ }
+ /* " If the socket is connected, the msg_name and msg_namelen members shall be ignored." */
+ done_socket(sock);
+ return buflen;
+#else /* LWIP_TCP */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
+ return -1;
+#endif /* LWIP_TCP */
+ }
+ /* else, UDP and RAW NETCONNs */
+#if LWIP_UDP || LWIP_RAW
+ {
+ u16_t datagram_len = 0;
+ err_t err;
+ err = lwip_recvfrom_udp_raw(sock, flags, message, &datagram_len, s);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvmsg[UDP/RAW](%d): buf == NULL, error is \"%s\"!\n",
+ s, lwip_strerr(err)));
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ return -1;
+ }
+ if (datagram_len > buflen) {
+ message->msg_flags |= MSG_TRUNC;
+ }
+
+ sock_set_errno(sock, 0);
+ done_socket(sock);
+ return (int)datagram_len;
+ }
+#else /* LWIP_UDP || LWIP_RAW */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
+ return -1;
+#endif /* LWIP_UDP || LWIP_RAW */
+}
+
+ssize_t
lwip_send(int s, const void *data, size_t size, int flags)
{
struct lwip_sock *sock;
@@ -830,35 +1323,200 @@ lwip_send(int s, const void *data, size_t size, int flags)
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
#if (LWIP_UDP || LWIP_RAW)
+ done_socket(sock);
return lwip_sendto(s, data, size, flags, NULL, 0);
#else /* (LWIP_UDP || LWIP_RAW) */
sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
return -1;
#endif /* (LWIP_UDP || LWIP_RAW) */
}
- write_flags = NETCONN_COPY |
- ((flags & MSG_MORE) ? NETCONN_MORE : 0) |
- ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
+ write_flags = (u8_t)(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);
+ done_socket(sock);
+ /* casting 'written' to ssize_t is OK here since the netconn API limits it to SSIZE_MAX */
+ return (err == ERR_OK ? (ssize_t)written : -1);
}
-int
+ssize_t
+lwip_sendmsg(int s, const struct msghdr *msg, int flags)
+{
+ struct lwip_sock *sock;
+#if LWIP_TCP
+ u8_t write_flags;
+ size_t written;
+#endif
+ 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)); done_socket(sock); return -1;);
+ LWIP_ERROR("lwip_sendmsg: invalid msghdr iov", msg->msg_iov != NULL,
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
+ LWIP_ERROR("lwip_sendmsg: maximum iovs exceeded", (msg->msg_iovlen > 0) && (msg->msg_iovlen <= IOV_MAX),
+ sock_set_errno(sock, EMSGSIZE); done_socket(sock); return -1;);
+ LWIP_ERROR("lwip_sendmsg: unsupported flags", (flags & ~(MSG_DONTWAIT | MSG_MORE)) == 0,
+ sock_set_errno(sock, EOPNOTSUPP); done_socket(sock); return -1;);
+
+ LWIP_UNUSED_ARG(msg->msg_control);
+ LWIP_UNUSED_ARG(msg->msg_controllen);
+ LWIP_UNUSED_ARG(msg->msg_flags);
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+ write_flags = (u8_t)(NETCONN_COPY |
+ ((flags & MSG_MORE) ? NETCONN_MORE : 0) |
+ ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0));
+
+ written = 0;
+ err = netconn_write_vectors_partly(sock->conn, (struct netvector *)msg->msg_iov, (u16_t)msg->msg_iovlen, write_flags, &written);
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ /* casting 'written' to ssize_t is OK here since the netconn API limits it to SSIZE_MAX */
+ return (err == ERR_OK ? (ssize_t)written : -1);
+#else /* LWIP_TCP */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
+ return -1;
+#endif /* LWIP_TCP */
+ }
+ /* else, UDP and RAW NETCONNs */
+#if LWIP_UDP || LWIP_RAW
+ {
+ struct netbuf chain_buf;
+ int i;
+ ssize_t size = 0;
+
+ 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)); done_socket(sock); return -1;);
+
+ /* initialize chain buffer with destination */
+ memset(&chain_buf, 0, sizeof(struct netbuf));
+ 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;
+ if ((msg->msg_iov[i].iov_len > INT_MAX) || (size < (int)msg->msg_iov[i].iov_len)) {
+ /* overflow */
+ goto sendmsg_emsgsize;
+ }
+ }
+ if (size > 0xFFFF) {
+ /* overflow */
+ goto sendmsg_emsgsize;
+ }
+ /* 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;
+ if (msg->msg_iov[i].iov_len > 0xFFFF) {
+ /* overflow */
+ goto sendmsg_emsgsize;
+ }
+ 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;
+ 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 {
+ if (chain_buf.p->tot_len + p->len > 0xffff) {
+ /* overflow */
+ pbuf_free(p);
+ goto sendmsg_emsgsize;
+ }
+ 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_free(&chain_buf);
+
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ return (err == ERR_OK ? size : -1);
+sendmsg_emsgsize:
+ sock_set_errno(sock, EMSGSIZE);
+ netbuf_free(&chain_buf);
+ done_socket(sock);
+ return -1;
+ }
+#else /* LWIP_UDP || LWIP_RAW */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
+ return -1;
+#endif /* LWIP_UDP || LWIP_RAW */
+}
+
+ssize_t
lwip_sendto(int s, const void *data, size_t size, int flags,
- const struct sockaddr *to, socklen_t tolen)
+ const struct sockaddr *to, socklen_t tolen)
{
struct lwip_sock *sock;
err_t err;
u16_t short_size;
u16_t remote_port;
-#if !LWIP_TCPIP_CORE_LOCKING
struct netbuf buf;
-#endif
sock = get_socket(s);
if (!sock) {
@@ -867,119 +1525,46 @@ lwip_sendto(int s, const void *data, size_t size, int flags,
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
#if LWIP_TCP
+ done_socket(sock);
return lwip_send(s, data, size, flags);
#else /* LWIP_TCP */
LWIP_UNUSED_ARG(flags);
sock_set_errno(sock, err_to_errno(ERR_ARG));
+ done_socket(sock);
return -1;
#endif /* LWIP_TCP */
}
- if ((to != NULL) && !SOCK_ADDR_TYPE_MATCH(to, sock)) {
- /* sockaddr does not match socket type (IPv4/IPv6) */
- sock_set_errno(sock, err_to_errno(ERR_VAL));
+ if (size > LWIP_MIN(0xFFFF, SSIZE_MAX)) {
+ /* cannot fit into one datagram (at least for us) */
+ sock_set_errno(sock, EMSGSIZE);
+ done_socket(sock);
return -1;
}
-
- /* @todo: split into multiple sendto's? */
- LWIP_ASSERT("lwip_sendto: size must fit in u16_t", size <= 0xffff);
short_size = (u16_t)size;
LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) ||
(IS_SOCK_ADDR_LEN_VALID(tolen) &&
- IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))),
- sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+ IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); done_socket(sock); return -1;);
LWIP_UNUSED_ARG(tolen);
-#if LWIP_TCPIP_CORE_LOCKING
- /* Special speedup for fast UDP/RAW sending: call the raw API directly
- instead of using the netconn functions. */
- {
- struct pbuf* p;
- ipX_addr_t *remote_addr;
- ipX_addr_t remote_addr_tmp;
-
-#if LWIP_NETIF_TX_SINGLE_PBUF
- p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_RAM);
- if (p != NULL) {
-#if LWIP_CHECKSUM_ON_COPY
- u16_t chksum = 0;
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) {
- chksum = LWIP_CHKSUM_COPY(p->payload, data, short_size);
- } else
-#endif /* LWIP_CHECKSUM_ON_COPY */
- MEMCPY(p->payload, data, size);
-#else /* LWIP_NETIF_TX_SINGLE_PBUF */
- p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_REF);
- if (p != NULL) {
- p->payload = (void*)data;
-#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
-
- if (to != NULL) {
- SOCKADDR_TO_IPXADDR_PORT(to->sa_family == AF_INET6,
- to, &remote_addr_tmp, remote_port);
- remote_addr = &remote_addr_tmp;
- } else {
- remote_addr = &sock->conn->pcb.ip->remote_ip;
-#if LWIP_UDP
- if (NETCONNTYPE_GROUP(sock->conn->type) == NETCONN_UDP) {
- remote_port = sock->conn->pcb.udp->remote_port;
- } else
-#endif /* LWIP_UDP */
- {
- remote_port = 0;
- }
- }
-
- LOCK_TCPIP_CORE();
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW) {
-#if LWIP_RAW
- err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, ipX_2_ip(remote_addr));
-#else /* LWIP_RAW */
- err = ERR_ARG;
-#endif /* LWIP_RAW */
- }
-#if LWIP_UDP && LWIP_RAW
- else
-#endif /* LWIP_UDP && LWIP_RAW */
- {
-#if LWIP_UDP
-#if LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF
- err = sock->conn->last_err = udp_sendto_chksum(sock->conn->pcb.udp, p,
- ipX_2_ip(remote_addr), remote_port, 1, chksum);
-#else /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */
- err = sock->conn->last_err = udp_sendto(sock->conn->pcb.udp, p,
- ipX_2_ip(remote_addr), remote_port);
-#endif /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */
-#else /* LWIP_UDP */
- err = ERR_ARG;
-#endif /* LWIP_UDP */
- }
- UNLOCK_TCPIP_CORE();
-
- pbuf_free(p);
- } else {
- err = ERR_MEM;
- }
- }
-#else /* LWIP_TCPIP_CORE_LOCKING */
/* initialize a buffer */
buf.p = buf.ptr = NULL;
#if LWIP_CHECKSUM_ON_COPY
buf.flags = 0;
#endif /* LWIP_CHECKSUM_ON_COPY */
if (to) {
- SOCKADDR_TO_IPXADDR_PORT((to->sa_family) == AF_INET6, to, &buf.addr, remote_port);
+ SOCKADDR_TO_IPADDR_PORT(to, &buf.addr, remote_port);
} else {
remote_port = 0;
- ipX_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr);
+ 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));
- ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)),
- SOCKETS_DEBUG, &buf.addr);
+ s, data, short_size, flags));
+ ip_addr_debug_print_val(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 */
@@ -992,25 +1577,34 @@ lwip_sendto(int s, const void *data, size_t size, int flags,
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) {
u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size);
netbuf_set_chksum(&buf, chksum);
- err = ERR_OK;
} else
#endif /* LWIP_CHECKSUM_ON_COPY */
{
- err = netbuf_take(&buf, data, short_size);
+ 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);
-#endif /* LWIP_TCPIP_CORE_LOCKING */
+
sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
return (err == ERR_OK ? short_size : -1);
}
@@ -1020,39 +1614,39 @@ lwip_socket(int domain, int type, int protocol)
struct netconn *conn;
int i;
-#if !LWIP_IPV6
LWIP_UNUSED_ARG(domain); /* @todo: check this */
-#endif /* LWIP_IPV6 */
/* create a netconn */
switch (type) {
- case SOCK_RAW:
- conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_RAW),
- (u8_t)protocol, event_callback);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ",
- domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
- break;
- case SOCK_DGRAM:
- conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain,
- ((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP)) ,
- event_callback);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ",
- domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
- break;
- case SOCK_STREAM:
- conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), event_callback);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ",
- domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
- if (conn != NULL) {
- /* Prevent automatic window updates, we do this on our own! */
- netconn_set_noautorecved(conn, 1);
- }
- break;
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n",
- domain, type, protocol));
- set_errno(EINVAL);
- return -1;
+ case SOCK_RAW:
+ conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_RAW),
+ (u8_t)protocol, DEFAULT_SOCKET_EVENTCB);
+ 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)),
+ DEFAULT_SOCKET_EVENTCB);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ",
+ domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+#if LWIP_NETBUF_RECVINFO
+ if (conn) {
+ /* netconn layer enables pktinfo by default, sockets default to off */
+ conn->flags &= ~NETCONN_FLAG_PKTINFO;
+ }
+#endif /* LWIP_NETBUF_RECVINFO */
+ break;
+ case SOCK_STREAM:
+ conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), DEFAULT_SOCKET_EVENTCB);
+ 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) {
@@ -1069,31 +1663,48 @@ lwip_socket(int domain, int type, int protocol)
return -1;
}
conn->socket = i;
+ done_socket(&sockets[i - LWIP_SOCKET_OFFSET]);
LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i));
set_errno(0);
return i;
}
-int
+ssize_t
lwip_write(int s, const void *data, size_t size)
{
return lwip_send(s, data, size, 0);
}
+ssize_t
+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);
+}
+
+#if LWIP_SOCKET_SELECT
/**
* Go through the readset and writeset lists and see which socket of the sockets
* set in the sets has events. On return, readset, writeset and exceptset have
* the sockets enabled that had events.
*
- * exceptset is not used for now!!!
- *
* @param maxfdp1 the highest socket index in the sets
- * @param readset_in: set of sockets to check for read events
- * @param writeset_in: set of sockets to check for write events
- * @param exceptset_in: set of sockets to check for error events
- * @param readset_out: set of sockets that had read events
- * @param writeset_out: set of sockets that had write events
- * @param exceptset_out: set os sockets that had error events
+ * @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
@@ -1111,39 +1722,47 @@ lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *excep
/* Go through each socket in each list to count number of sockets which
currently match */
- for(i = 0; i < maxfdp1; i++) {
- void* lastdata = NULL;
- s16_t rcvevent = 0;
- u16_t sendevent = 0;
- u16_t errevent = 0;
+ 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);
+ sock = tryget_socket_unconn(i);
if (sock != NULL) {
- lastdata = sock->lastdata;
- rcvevent = sock->rcvevent;
- sendevent = sock->sendevent;
- errevent = sock->errevent;
- }
- SYS_ARCH_UNPROTECT(lev);
- /* ... then examine it: */
- /* See if netconn of this socket is ready for read */
- if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) {
- FD_SET(i, &lreadset);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i));
- nready++;
- }
- /* See if netconn of this socket is ready for write */
- if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) {
- FD_SET(i, &lwriteset);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i));
- nready++;
- }
- /* See if netconn of this socket had an error */
- if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) {
- FD_SET(i, &lexceptset);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i));
- nready++;
+ void *lastdata = sock->lastdata.pbuf;
+ 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++;
+ }
+ done_socket(sock);
+ } else {
+ SYS_ARCH_UNPROTECT(lev);
+ /* no a valid open socket */
+ return -1;
}
}
/* copy local sets to the ones provided as arguments */
@@ -1155,9 +1774,68 @@ lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *excep
return nready;
}
-/**
- * Processing exceptset is not yet implemented.
+#if LWIP_NETCONN_FULLDUPLEX
+/* Mark all of the set sockets in one of the three fdsets passed to select as used.
+ * All sockets are marked (and later unmarked), whether they are open or not.
+ * This is OK as lwip_selscan aborts select when non-open sockets are found.
+ */
+static void
+lwip_select_inc_sockets_used_set(int maxfdp, fd_set *fdset, fd_set *used_sockets)
+{
+ SYS_ARCH_DECL_PROTECT(lev);
+ if (fdset) {
+ int i;
+ for (i = LWIP_SOCKET_OFFSET; i < maxfdp; i++) {
+ /* if this FD is in the set, lock it (unless already done) */
+ if (FD_ISSET(i, fdset) && !FD_ISSET(i, used_sockets)) {
+ struct lwip_sock *sock;
+ SYS_ARCH_PROTECT(lev);
+ sock = tryget_socket_unconn(i);
+ if (sock != NULL) {
+ /* leave the socket used until released by lwip_select_dec_sockets_used */
+ FD_SET(i, used_sockets);
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ }
+ }
+}
+
+/* Mark all sockets passed to select as used to prevent them from being freed
+ * from other threads while select is running.
+ * Marked sockets are added to 'used_sockets' to mark them only once an be able
+ * to unmark them correctly.
*/
+static void
+lwip_select_inc_sockets_used(int maxfdp, fd_set *fdset1, fd_set *fdset2, fd_set *fdset3, fd_set *used_sockets)
+{
+ FD_ZERO(used_sockets);
+ lwip_select_inc_sockets_used_set(maxfdp, fdset1, used_sockets);
+ lwip_select_inc_sockets_used_set(maxfdp, fdset2, used_sockets);
+ lwip_select_inc_sockets_used_set(maxfdp, fdset3, used_sockets);
+}
+
+/* Let go all sockets that were marked as used when starting select */
+static void
+lwip_select_dec_sockets_used(int maxfdp, fd_set *used_sockets)
+{
+ int i;
+ for (i = LWIP_SOCKET_OFFSET; i < maxfdp; i++) {
+ /* if this FD is not in the set, continue */
+ if (FD_ISSET(i, used_sockets)) {
+ struct lwip_sock *sock = tryget_socket_unconn_nouse(i);
+ LWIP_ASSERT("socket gone at the end of select", sock != NULL);
+ if (sock != NULL) {
+ done_socket(sock);
+ }
+ }
+ }
+}
+#else /* LWIP_NETCONN_FULLDUPLEX */
+#define lwip_select_inc_sockets_used(maxfdp1, readset, writeset, exceptset, used_sockets)
+#define lwip_select_dec_sockets_used(maxfdp1, used_sockets)
+#endif /* LWIP_NETCONN_FULLDUPLEX */
+
int
lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
struct timeval *timeout)
@@ -1166,138 +1844,222 @@ lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
int nready;
fd_set lreadset, lwriteset, lexceptset;
u32_t msectimeout;
- struct lwip_select_cb select_cb;
- err_t err;
int i;
+ int maxfdp2;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ int waited = 0;
+#endif
+#if LWIP_NETCONN_FULLDUPLEX
+ fd_set used_sockets;
+#endif
SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_SOCKET_SELECT_DECL_PROTECT(lev2);
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));
+ 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));
+
+ if ((maxfdp1 < 0) || (maxfdp1 > (FD_SETSIZE + LWIP_SOCKET_OFFSET))) {
+ set_errno(EINVAL);
+ return -1;
+ }
+
+ lwip_select_inc_sockets_used(maxfdp1, readset, writeset, exceptset, &used_sockets);
/* 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 (nready < 0) {
+ /* one of the sockets in one of the fd_sets was invalid */
+ set_errno(EBADF);
+ lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
+ return -1;
+ } else if (nready > 0) {
+ /* one or more sockets are set, no need to wait */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
+ } else {
+ /* If we don't have any current events, then suspend if we are supposed to */
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;
- }
+ } else {
+ /* 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 (unless we're running in MPU compatible
+ mode). */
+ API_SELECT_CB_VAR_DECLARE(select_cb);
+ API_SELECT_CB_VAR_ALLOC(select_cb, set_errno(ENOMEM); return -1);
+
+ API_SELECT_CB_VAR_REF(select_cb).next = NULL;
+ API_SELECT_CB_VAR_REF(select_cb).prev = NULL;
+ API_SELECT_CB_VAR_REF(select_cb).readset = readset;
+ API_SELECT_CB_VAR_REF(select_cb).writeset = writeset;
+ API_SELECT_CB_VAR_REF(select_cb).exceptset = exceptset;
+ API_SELECT_CB_VAR_REF(select_cb).sem_signalled = 0;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ API_SELECT_CB_VAR_REF(select_cb).sem = LWIP_NETCONN_THREAD_SEM_GET();
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+ if (sys_sem_new(&API_SELECT_CB_VAR_REF(select_cb).sem, 0) != ERR_OK) {
+ /* failed to create semaphore */
+ set_errno(ENOMEM);
+ lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
+ API_SELECT_CB_VAR_FREE(select_cb);
+ return -1;
+ }
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
- /* None ready: add our semaphore to list:
- We don't actually need any dynamic memory. Our entry on the
- list is only valid while we are in this function, so it's ok
- to use local variables. */
-
- select_cb.next = NULL;
- select_cb.prev = NULL;
- select_cb.readset = readset;
- select_cb.writeset = writeset;
- select_cb.exceptset = exceptset;
- select_cb.sem_signalled = 0;
- err = sys_sem_new(&select_cb.sem, 0);
- if (err != ERR_OK) {
- /* failed to create semaphore */
- set_errno(ENOMEM);
- return -1;
- }
+ /* Protect the select_cb_list */
+ LWIP_SOCKET_SELECT_PROTECT(lev2);
- /* Protect the select_cb_list */
- SYS_ARCH_PROTECT(lev);
+ /* Put this select_cb on top of list */
+ API_SELECT_CB_VAR_REF(select_cb).next = select_cb_list;
+ if (select_cb_list != NULL) {
+ select_cb_list->prev = &API_SELECT_CB_VAR_REF(select_cb);
+ }
+ select_cb_list = &API_SELECT_CB_VAR_REF(select_cb);
+#if !LWIP_TCPIP_CORE_LOCKING
+ /* Increasing this counter tells select_check_waiters that the list has changed. */
+ select_cb_ctr++;
+#endif
- /* Put this select_cb on top of list */
- select_cb.next = select_cb_list;
- if (select_cb_list != NULL) {
- select_cb_list->prev = &select_cb;
- }
- select_cb_list = &select_cb;
- /* Increasing this counter tells even_callback that the list has changed. */
- select_cb_ctr++;
+ /* Now we can safely unprotect */
+ LWIP_SOCKET_SELECT_UNPROTECT(lev2);
+
+ /* 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_unconn(i);
+ if (sock != NULL) {
+ sock->select_waiting++;
+ if (sock->select_waiting == 0) {
+ /* overflow - too many threads waiting */
+ sock->select_waiting--;
+ done_socket(sock);
+ nready = -1;
+ maxfdp2 = i;
+ SYS_ARCH_UNPROTECT(lev);
+ set_errno(EBUSY);
+ break;
+ }
+ done_socket(sock);
+ } else {
+ /* Not a valid socket */
+ nready = -1;
+ maxfdp2 = i;
+ SYS_ARCH_UNPROTECT(lev);
+ set_errno(EBADF);
+ break;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ }
- /* Now we can safely unprotect */
- 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 {
+ long msecs_long = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500) / 1000));
+ if (msecs_long <= 0) {
+ /* Wait 1ms at least (0 means wait forever) */
+ msectimeout = 1;
+ } else {
+ msectimeout = (u32_t)msecs_long;
+ }
+ }
- /* Increase select_waiting for each socket we are interested in */
- for(i = 0; i < maxfdp1; i++) {
- if ((readset && FD_ISSET(i, readset)) ||
- (writeset && FD_ISSET(i, writeset)) ||
- (exceptset && FD_ISSET(i, exceptset))) {
- struct lwip_sock *sock = tryget_socket(i);
- LWIP_ASSERT("sock != NULL", sock != NULL);
- SYS_ARCH_PROTECT(lev);
- sock->select_waiting++;
- LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
- SYS_ARCH_UNPROTECT(lev);
+ waitres = sys_arch_sem_wait(SELECT_SEM_PTR(API_SELECT_CB_VAR_REF(select_cb).sem), msectimeout);
+#if LWIP_NETCONN_SEM_PER_THREAD
+ waited = 1;
+#endif
+ }
}
- }
- /* Call lwip_selscan again: there could have been events between
- the last scan (whithout us on the list) and putting us on the list! */
- nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
- if (!nready) {
- /* Still none ready, just wait to be woken */
- if (timeout == 0) {
- /* Wait forever */
- msectimeout = 0;
- } else {
- msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000));
- if (msectimeout == 0) {
- /* Wait 1ms at least (0 means wait forever) */
- msectimeout = 1;
+ /* 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_unconn(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--;
+ }
+ done_socket(sock);
+ } else {
+ /* Not a valid socket */
+ nready = -1;
+ set_errno(EBADF);
+ }
+ SYS_ARCH_UNPROTECT(lev);
}
}
+ /* Take us off the list */
+ LWIP_SOCKET_SELECT_PROTECT(lev2);
+ if (API_SELECT_CB_VAR_REF(select_cb).next != NULL) {
+ API_SELECT_CB_VAR_REF(select_cb).next->prev = API_SELECT_CB_VAR_REF(select_cb).prev;
+ }
+ if (select_cb_list == &API_SELECT_CB_VAR_REF(select_cb)) {
+ LWIP_ASSERT("select_cb.prev == NULL", API_SELECT_CB_VAR_REF(select_cb).prev == NULL);
+ select_cb_list = API_SELECT_CB_VAR_REF(select_cb).next;
+ } else {
+ LWIP_ASSERT("select_cb.prev != NULL", API_SELECT_CB_VAR_REF(select_cb).prev != NULL);
+ API_SELECT_CB_VAR_REF(select_cb).prev->next = API_SELECT_CB_VAR_REF(select_cb).next;
+ }
+#if !LWIP_TCPIP_CORE_LOCKING
+ /* Increasing this counter tells select_check_waiters that the list has changed. */
+ select_cb_ctr++;
+#endif
+ LWIP_SOCKET_SELECT_UNPROTECT(lev2);
- waitres = sys_arch_sem_wait(&select_cb.sem, msectimeout);
- }
- /* Increase select_waiting for each socket we are interested in */
- for(i = 0; i < maxfdp1; i++) {
- if ((readset && FD_ISSET(i, readset)) ||
- (writeset && FD_ISSET(i, writeset)) ||
- (exceptset && FD_ISSET(i, exceptset))) {
- struct lwip_sock *sock = tryget_socket(i);
- LWIP_ASSERT("sock != NULL", sock != NULL);
- SYS_ARCH_PROTECT(lev);
- sock->select_waiting--;
- LWIP_ASSERT("sock->select_waiting >= 0", sock->select_waiting >= 0);
- SYS_ARCH_UNPROTECT(lev);
+#if LWIP_NETCONN_SEM_PER_THREAD
+ if (API_SELECT_CB_VAR_REF(select_cb).sem_signalled && (!waited || (waitres == SYS_ARCH_TIMEOUT))) {
+ /* don't leave the thread-local semaphore signalled */
+ sys_arch_sem_wait(API_SELECT_CB_VAR_REF(select_cb).sem, 1);
+ }
+#else /* LWIP_NETCONN_SEM_PER_THREAD */
+ sys_sem_free(&API_SELECT_CB_VAR_REF(select_cb).sem);
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+ API_SELECT_CB_VAR_FREE(select_cb);
+
+ if (nready < 0) {
+ /* This happens when a socket got closed while waiting */
+ lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
+ return -1;
}
- }
- /* Take us off the list */
- SYS_ARCH_PROTECT(lev);
- if (select_cb.next != NULL) {
- select_cb.next->prev = select_cb.prev;
- }
- if (select_cb_list == &select_cb) {
- LWIP_ASSERT("select_cb.prev == NULL", select_cb.prev == NULL);
- select_cb_list = select_cb.next;
- } else {
- LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL);
- select_cb.prev->next = select_cb.next;
- }
- /* Increasing this counter tells even_callback that the list has changed. */
- select_cb_ctr++;
- SYS_ARCH_UNPROTECT(lev);
- sys_sem_free(&select_cb.sem);
- if (waitres == SYS_ARCH_TIMEOUT) {
- /* Timeout */
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
- /* This is OK as the local fdsets are empty and nready is zero,
- or we would have returned earlier. */
- goto return_copy_fdsets;
+ 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. */
+ } else {
+ /* See what's set now after waiting */
+ nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
+ }
}
-
- /* 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:
+ lwip_select_dec_sockets_used(maxfdp1, &used_sockets);
set_errno(0);
if (readset) {
*readset = lreadset;
@@ -1314,14 +2076,19 @@ return_copy_fdsets:
/**
* Callback registered in the netconn layer for each socket-netconn.
* Processes recvevent (data available) and wakes up tasks waiting for select.
+ *
+ * @note for LWIP_TCPIP_CORE_LOCKING any caller of this function
+ * must have the core lock held when signaling the following events
+ * as they might cause select_list_cb to be checked:
+ * NETCONN_EVT_RCVPLUS
+ * NETCONN_EVT_SENDPLUS
+ * NETCONN_EVT_ERROR
*/
static void
event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
{
- int s;
+ int s, check_waiters;
struct lwip_sock *sock;
- struct lwip_select_cb *scb;
- int last_select_cb_ctr;
SYS_ARCH_DECL_PROTECT(lev);
LWIP_UNUSED_ARG(len);
@@ -1338,6 +2105,8 @@ event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
SYS_ARCH_PROTECT(lev);
if (conn->socket < 0) {
if (evt == NETCONN_EVT_RCVPLUS) {
+ /* conn->socket is -1 on initialization
+ lwip_accept adjusts sock->recvevent if conn->socket < -1 */
conn->socket--;
}
SYS_ARCH_UNPROTECT(lev);
@@ -1355,21 +2124,28 @@ event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
return;
}
+ check_waiters = 1;
SYS_ARCH_PROTECT(lev);
/* Set event as required */
switch (evt) {
case NETCONN_EVT_RCVPLUS:
sock->rcvevent++;
+ if (sock->rcvevent > 1) {
+ check_waiters = 0;
+ }
break;
case NETCONN_EVT_RCVMINUS:
sock->rcvevent--;
- break;
+ check_waiters = 0;
case NETCONN_EVT_SENDPLUS:
+ if (sock->sendevent) {
+ check_waiters = 0;
+ }
sock->sendevent = 1;
break;
case NETCONN_EVT_SENDMINUS:
sock->sendevent = 0;
- break;
+ check_waiters = 0;
case NETCONN_EVT_ERROR:
sock->errevent = 1;
break;
@@ -1378,48 +2154,80 @@ event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
break;
}
- if (sock->select_waiting == 0) {
- /* noone is waiting for this socket, no need to check select_cb_list */
+ if (sock->select_waiting && check_waiters) {
+ /* Save which events are active */
+ int has_recvevent, has_sendevent, has_errevent;
+ has_recvevent = sock->rcvevent > 0;
+ has_sendevent = sock->sendevent != 0;
+ has_errevent = sock->errevent != 0;
+ SYS_ARCH_UNPROTECT(lev);
+ /* Check any select calls waiting on this socket */
+ select_check_waiters(s, has_recvevent, has_sendevent, has_errevent);
+ } else {
SYS_ARCH_UNPROTECT(lev);
- return;
}
+ done_socket(sock);
+}
- /* 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. */
+/**
+ * Check if any select waiters are waiting on this socket and its events
+ *
+ * @note on synchronization of select_cb_list:
+ * LWIP_TCPIP_CORE_LOCKING: the select_cb_list must only be accessed while holding
+ * the core lock. We do a single pass through the list and signal any waiters.
+ * Core lock should already be held when calling here!!!!
+
+ * !LWIP_TCPIP_CORE_LOCKING: we use SYS_ARCH_PROTECT but unlock on each iteration
+ * of the loop, thus creating a possibility where a thread could modify the
+ * select_cb_list during our UNPROTECT/PROTECT. We use a generational counter to
+ * detect this change and restart the list walk. The list is expected to be small
+ */
+static void select_check_waiters(int s, int has_recvevent, int has_sendevent, int has_errevent)
+{
+ struct lwip_select_cb *scb;
+#if !LWIP_TCPIP_CORE_LOCKING
+ int last_select_cb_ctr;
+ SYS_ARCH_DECL_PROTECT(lev);
+#endif
- /* At this point, SYS_ARCH is still protected! */
+#if !LWIP_TCPIP_CORE_LOCKING
+ SYS_ARCH_PROTECT(lev);
again:
+ /* remember the state of select_cb_list to detect changes */
+ last_select_cb_ctr = select_cb_ctr;
+#endif
for (scb = select_cb_list; scb != NULL; scb = scb->next) {
if (scb->sem_signalled == 0) {
/* semaphore not signalled yet */
int do_signal = 0;
/* Test this select call for our socket */
- if (sock->rcvevent > 0) {
+ if (has_recvevent) {
if (scb->readset && FD_ISSET(s, scb->readset)) {
do_signal = 1;
}
}
- if (sock->sendevent != 0) {
+ if (has_sendevent) {
if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
do_signal = 1;
}
}
- if (sock->errevent != 0) {
+ if (has_errevent) {
if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
do_signal = 1;
}
}
if (do_signal) {
scb->sem_signalled = 1;
- /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might
- lead to the select thread taking itself off the list, invalidagin the semaphore. */
- sys_sem_signal(&scb->sem);
+ /* For !LWIP_TCPIP_CORE_LOCKING, we 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));
}
}
+#if LWIP_TCPIP_CORE_LOCKING
+ }
+#else
/* unlock interrupts with each step */
- last_select_cb_ctr = select_cb_ctr;
SYS_ARCH_UNPROTECT(lev);
/* this makes sure interrupt protection time is short */
SYS_ARCH_PROTECT(lev);
@@ -1427,13 +2235,16 @@ again:
/* someone has changed select_cb_list, restart at the beginning */
goto again;
}
+ /* remember the state of select_cb_list to detect changes */
+ last_select_cb_ctr = select_cb_ctr;
}
SYS_ARCH_UNPROTECT(lev);
+#endif
}
+#endif /* LWIP_SOCKET_SELECT */
/**
- * Unimplemented: Close one end of a full-duplex connection.
- * Currently, the full connection is closed.
+ * Close one end of a full-duplex connection.
*/
int
lwip_shutdown(int s, int how)
@@ -1452,27 +2263,31 @@ lwip_shutdown(int s, int how)
if (sock->conn != NULL) {
if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
sock_set_errno(sock, EOPNOTSUPP);
- return EOPNOTSUPP;
+ done_socket(sock);
+ return -1;
}
} else {
sock_set_errno(sock, ENOTCONN);
- return ENOTCONN;
+ done_socket(sock);
+ return -1;
}
if (how == SHUT_RD) {
shut_rx = 1;
} else if (how == SHUT_WR) {
shut_tx = 1;
- } else if(how == SHUT_RDWR) {
+ } else if (how == SHUT_RDWR) {
shut_rx = 1;
shut_tx = 1;
} else {
sock_set_errno(sock, EINVAL);
- return EINVAL;
+ done_socket(sock);
+ return -1;
}
err = netconn_shutdown(sock->conn, shut_rx, shut_tx);
sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
return (err == ERR_OK ? 0 : -1);
}
@@ -1481,8 +2296,9 @@ lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
{
struct lwip_sock *sock;
union sockaddr_aligned saddr;
- ipX_addr_t naddr;
+ ip_addr_t naddr;
u16_t port;
+ err_t err;
sock = get_socket(s);
if (!sock) {
@@ -1490,14 +2306,26 @@ lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
}
/* get the IP address and port */
- /* @todo: this does not work for IPv6, yet */
- netconn_getaddr(sock->conn, ipX_2_ip(&naddr), &port, local);
- IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)),
- &saddr, &naddr, port);
+ err = netconn_getaddr(sock->conn, &naddr, &port, local);
+ if (err != ERR_OK) {
+ sock_set_errno(sock, err_to_errno(err));
+ done_socket(sock);
+ 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));
- ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)),
- SOCKETS_DEBUG, &naddr);
+ ip_addr_debug_print_val(SOCKETS_DEBUG, naddr);
LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", port));
if (*namelen > saddr.sa.sa_len) {
@@ -1506,6 +2334,7 @@ lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
MEMCPY(name, &saddr, *namelen);
sock_set_errno(sock, 0);
+ done_socket(sock);
return 0;
}
@@ -1524,9 +2353,12 @@ lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
int
lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
{
- err_t err = ERR_OK;
+ int err;
struct lwip_sock *sock = get_socket(s);
- struct lwip_setgetsockopt_data data;
+#if !LWIP_TCPIP_CORE_LOCKING
+ err_t cberr;
+ LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
if (!sock) {
return -1;
@@ -1534,445 +2366,416 @@ lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
if ((NULL == optval) || (NULL == optlen)) {
sock_set_errno(sock, EFAULT);
+ done_socket(sock);
return -1;
}
- /* Do length and type checks for the various options first, to keep it readable. */
- switch (level) {
-
-/* Level: SOL_SOCKET */
- case SOL_SOCKET:
- switch (optname) {
-
- case SO_ACCEPTCONN:
- case SO_BROADCAST:
- /* UNIMPL case SO_DEBUG: */
- /* UNIMPL case SO_DONTROUTE: */
- case SO_ERROR:
- case SO_KEEPALIVE:
- /* UNIMPL case SO_CONTIMEO: */
-#if LWIP_SO_SNDTIMEO
- case SO_SNDTIMEO:
-#endif /* LWIP_SO_SNDTIMEO */
-#if LWIP_SO_RCVTIMEO
- case SO_RCVTIMEO:
-#endif /* LWIP_SO_RCVTIMEO */
-#if LWIP_SO_RCVBUF
- case SO_RCVBUF:
-#endif /* LWIP_SO_RCVBUF */
- /* UNIMPL case SO_OOBINLINE: */
- /* UNIMPL case SO_SNDBUF: */
- /* UNIMPL case SO_RCVLOWAT: */
- /* UNIMPL case SO_SNDLOWAT: */
-#if SO_REUSE
- case SO_REUSEADDR:
- case SO_REUSEPORT:
-#endif /* SO_REUSE */
- case SO_TYPE:
- /* UNIMPL case SO_USELOOPBACK: */
- if (*optlen < sizeof(int)) {
- err = EINVAL;
- }
- break;
-
- case SO_NO_CHECK:
- if (*optlen < sizeof(int)) {
- err = EINVAL;
- }
-#if LWIP_UDP
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP ||
- ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) {
- /* this flag is only available for UDP, not for UDP lite */
- err = EAFNOSUPPORT;
- }
-#endif /* LWIP_UDP */
- break;
-
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-
-/* Level: IPPROTO_IP */
- case IPPROTO_IP:
- switch (optname) {
- /* UNIMPL case IP_HDRINCL: */
- /* UNIMPL case IP_RCVDSTADDR: */
- /* UNIMPL case IP_RCVIF: */
- case IP_TTL:
- case IP_TOS:
- if (*optlen < sizeof(int)) {
- err = EINVAL;
- }
- break;
-#if LWIP_IGMP
- case IP_MULTICAST_TTL:
- if (*optlen < sizeof(u8_t)) {
- err = EINVAL;
- }
- break;
- case IP_MULTICAST_IF:
- if (*optlen < sizeof(struct in_addr)) {
- err = EINVAL;
- }
- break;
- case IP_MULTICAST_LOOP:
- if (*optlen < sizeof(u8_t)) {
- err = EINVAL;
- }
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
- err = EAFNOSUPPORT;
- }
- break;
-#endif /* LWIP_IGMP */
-
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-
-#if LWIP_TCP
-/* Level: IPPROTO_TCP */
- case IPPROTO_TCP:
- if (*optlen < sizeof(int)) {
- err = EINVAL;
- break;
- }
-
- /* If this is no TCP socket, ignore any options. */
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP)
- return 0;
-
- switch (optname) {
- case TCP_NODELAY:
- case TCP_KEEPALIVE:
-#if LWIP_TCP_KEEPALIVE
- case TCP_KEEPIDLE:
- case TCP_KEEPINTVL:
- case TCP_KEEPCNT:
-#endif /* LWIP_TCP_KEEPALIVE */
- break;
-
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-#endif /* LWIP_TCP */
-
-#if LWIP_IPV6
-/* Level: IPPROTO_IPV6 */
- case IPPROTO_IPV6:
- switch (optname) {
- case IPV6_V6ONLY:
- if (*optlen < sizeof(int)) {
- err = EINVAL;
- }
- /* @todo: this does not work for datagram sockets, yet */
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP)
- return 0;
- break;
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-#endif /* LWIP_IPV6 */
-
-#if LWIP_UDP && LWIP_UDPLITE
-/* Level: IPPROTO_UDPLITE */
- case IPPROTO_UDPLITE:
- if (*optlen < sizeof(int)) {
- err = EINVAL;
- break;
- }
-
- /* If this is no UDP lite socket, ignore any options. */
- if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
- return 0;
- }
+#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();
- switch (optname) {
- case UDPLITE_SEND_CSCOV:
- case UDPLITE_RECV_CSCOV:
- break;
-
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-#endif /* LWIP_UDP && LWIP_UDPLITE*/
-/* UNDEFINED LEVEL */
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
- s, level, optname));
- err = ENOPROTOOPT;
- } /* switch */
+#else /* LWIP_TCPIP_CORE_LOCKING */
-
- if (err != ERR_OK) {
- sock_set_errno(sock, err);
+#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);
+ done_socket(sock);
+ 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
+ cberr = tcpip_callback(lwip_getsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
+ if (cberr != ERR_OK) {
+ LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+ sock_set_errno(sock, err_to_errno(cberr));
+ done_socket(sock);
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 */
- /* Now do the actual option processing */
- data.sock = sock;
-#ifdef LWIP_DEBUG
- data.s = s;
-#endif /* LWIP_DEBUG */
- data.level = level;
- data.optname = optname;
- data.optval = optval;
- data.optlen = optlen;
- data.err = err;
- tcpip_callback(lwip_getsockopt_internal, &data);
- sys_arch_sem_wait(&sock->conn->op_completed, 0);
/* maybe lwip_getsockopt_internal has changed err */
- err = data.err;
+ err = LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).err;
+ LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
sock_set_errno(sock, err);
+ done_socket(sock);
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_internal(void *arg)
+lwip_getsockopt_callback(void *arg)
{
- struct lwip_sock *sock;
-#ifdef LWIP_DEBUG
- int s;
-#endif /* LWIP_DEBUG */
- int level, optname;
- void *optval;
struct lwip_setgetsockopt_data *data;
-
LWIP_ASSERT("arg != NULL", arg != NULL);
+ data = (struct lwip_setgetsockopt_data *)arg;
- data = (struct lwip_setgetsockopt_data*)arg;
- sock = data->sock;
-#ifdef LWIP_DEBUG
- s = data->s;
-#endif /* LWIP_DEBUG */
- level = data->level;
- optname = data->optname;
- optval = data->optval;
+ 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 int
+lwip_getsockopt_impl(int s, int level, int optname, void *optval, socklen_t *optlen)
+{
+ int err = 0;
+ struct lwip_sock *sock = tryget_socket(s);
+ if (!sock) {
+ return EBADF;
+ }
switch (level) {
-/* Level: SOL_SOCKET */
- case SOL_SOCKET:
- switch (optname) {
-
- /* The option flags */
- case SO_ACCEPTCONN:
- case SO_BROADCAST:
- /* UNIMPL case SO_DEBUG: */
- /* UNIMPL case SO_DONTROUTE: */
- case SO_KEEPALIVE:
- /* UNIMPL case SO_OOBINCLUDE: */
-#if SO_REUSE
- case SO_REUSEADDR:
- case SO_REUSEPORT:
-#endif /* SO_REUSE */
- /*case SO_USELOOPBACK: UNIMPL */
- *(int*)optval = ip_get_option(sock->conn->pcb.ip, optname);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n",
- s, optname, (*(int*)optval?"on":"off")));
- break;
+ /* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
- case SO_TYPE:
- switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) {
- case NETCONN_RAW:
- *(int*)optval = SOCK_RAW;
- break;
- case NETCONN_TCP:
- *(int*)optval = SOCK_STREAM;
- break;
- case NETCONN_UDP:
- *(int*)optval = SOCK_DGRAM;
- break;
- default: /* unrecognized socket type */
- *(int*)optval = netconn_type(sock->conn);
- LWIP_DEBUGF(SOCKETS_DEBUG,
- ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n",
- s, *(int *)optval));
- } /* switch (netconn_type(sock->conn)) */
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n",
- s, *(int *)optval));
- break;
+#if LWIP_TCP
+ case SO_ACCEPTCONN:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, int);
+ if (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_TCP) {
+ done_socket(sock);
+ 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 */
- case SO_ERROR:
- /* only overwrite ERR_OK or tempoary errors */
- if ((sock->err == 0) || (sock->err == EINPROGRESS)) {
- sock_set_errno(sock, err_to_errno(sock->conn->last_err));
- }
- *(int *)optval = sock->err;
- sock->err = 0;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n",
- s, *(int *)optval));
- break;
+ /* The option flags */
+ case SO_BROADCAST:
+ case SO_KEEPALIVE:
+#if SO_REUSE
+ case SO_REUSEADDR:
+#endif /* SO_REUSE */
+ if ((optname == SO_BROADCAST) &&
+ (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP)) {
+ done_socket(sock);
+ return ENOPROTOOPT;
+ }
+ 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(sock, *optlen, int);
+ *(int *)optval = err_to_errno(netconn_err(sock->conn));
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n",
+ s, *(int *)optval));
+ break;
#if LWIP_SO_SNDTIMEO
- case SO_SNDTIMEO:
- *(int *)optval = netconn_get_sendtimeout(sock->conn);
- break;
+ 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:
- *(int *)optval = netconn_get_recvtimeout(sock->conn);
- break;
+ 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:
- *(int *)optval = netconn_get_recvbufsize(sock->conn);
- break;
+ 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:
- *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0;
- break;
+ 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 */
+ done_socket(sock);
+ return EAFNOSUPPORT;
+ }
+#endif /* LWIP_UDPLITE */
+ *(int *)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0;
+ break;
#endif /* LWIP_UDP*/
- default:
- LWIP_ASSERT("unhandled optname", 0);
- break;
- } /* switch (optname) */
- break;
-
-/* Level: IPPROTO_IP */
- case IPPROTO_IP:
- switch (optname) {
- case IP_TTL:
- *(int*)optval = sock->conn->pcb.ip->ttl;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n",
- s, *(int *)optval));
- break;
- case IP_TOS:
- *(int*)optval = sock->conn->pcb.ip->tos;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n",
- s, *(int *)optval));
- break;
-#if LWIP_IGMP
- case IP_MULTICAST_TTL:
- *(u8_t*)optval = sock->conn->pcb.ip->ttl;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
- s, *(int *)optval));
- break;
- case IP_MULTICAST_IF:
- inet_addr_from_ipaddr((struct in_addr*)optval, &sock->conn->pcb.udp->multicast_ip);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
- s, *(u32_t *)optval));
- break;
- case IP_MULTICAST_LOOP:
- if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
- *(u8_t*)optval = 1;
- } else {
- *(u8_t*)optval = 0;
- }
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
- s, *(int *)optval));
- break;
-#endif /* LWIP_IGMP */
- default:
- LWIP_ASSERT("unhandled optname", 0);
+ 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_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
+ case IP_MULTICAST_TTL:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, *optlen, u8_t);
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+ done_socket(sock);
+ 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) {
+ done_socket(sock);
+ 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_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
break;
- } /* switch (optname) */
- break;
#if LWIP_TCP
-/* Level: IPPROTO_TCP */
- case IPPROTO_TCP:
- switch (optname) {
- case TCP_NODELAY:
- *(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n",
- s, (*(int*)optval)?"on":"off") );
- break;
- case TCP_KEEPALIVE:
- *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPALIVE) = %d\n",
- s, *(int *)optval));
- break;
+ /* 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) {
+ done_socket(sock);
+ 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_IP, TCP_KEEPIDLE) = %d\n",
- s, *(int *)optval));
- break;
- case TCP_KEEPINTVL:
- *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPINTVL) = %d\n",
- s, *(int *)optval));
- break;
- case TCP_KEEPCNT:
- *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPCNT) = %d\n",
- s, *(int *)optval));
- break;
+ 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_ASSERT("unhandled optname", 0);
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
break;
- } /* switch (optname) */
- break;
#endif /* LWIP_TCP */
#if LWIP_IPV6
-/* Level: IPPROTO_IPV6 */
- case IPPROTO_IPV6:
- switch (optname) {
- case IPV6_V6ONLY:
- *(int*)optval = ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
- s, *(int *)optval));
+ /* 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;
- default:
- LWIP_ASSERT("unhandled optname", 0);
- break;
- } /* switch (optname) */
- break;
#endif /* LWIP_IPV6 */
#if LWIP_UDP && LWIP_UDPLITE
- /* Level: IPPROTO_UDPLITE */
- case IPPROTO_UDPLITE:
- switch (optname) {
- case UDPLITE_SEND_CSCOV:
- *(int*)optval = sock->conn->pcb.udp->chksum_len_tx;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n",
- s, (*(int*)optval)) );
+ /* 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))) {
+ done_socket(sock);
+ 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;
- 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)) );
+#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_ASSERT("unhandled optname", 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+ s, level, optname));
+ err = ENOPROTOOPT;
break;
- } /* switch (optname) */
- break;
-#endif /* LWIP_UDP */
- default:
- LWIP_ASSERT("unhandled level", 0);
- break;
} /* switch (level) */
- sys_sem_signal(&sock->conn->op_completed);
+
+ done_socket(sock);
+ return err;
}
int
lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
{
+ int err = 0;
struct lwip_sock *sock = get_socket(s);
- err_t err = ERR_OK;
- struct lwip_setgetsockopt_data data;
+#if !LWIP_TCPIP_CORE_LOCKING
+ err_t cberr;
+ LWIP_SETGETSOCKOPT_DATA_VAR_DECLARE(data);
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
if (!sock) {
return -1;
@@ -1980,453 +2783,517 @@ lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t opt
if (NULL == optval) {
sock_set_errno(sock, EFAULT);
+ done_socket(sock);
return -1;
}
- /* Do length and type checks for the various options first, to keep it readable. */
- switch (level) {
-
-/* Level: SOL_SOCKET */
- case SOL_SOCKET:
- switch (optname) {
-
- case SO_BROADCAST:
- /* UNIMPL case SO_DEBUG: */
- /* UNIMPL case SO_DONTROUTE: */
- case SO_KEEPALIVE:
- /* UNIMPL case case SO_CONTIMEO: */
-#if LWIP_SO_SNDTIMEO
- case SO_SNDTIMEO:
-#endif /* LWIP_SO_SNDTIMEO */
-#if LWIP_SO_RCVTIMEO
- case SO_RCVTIMEO:
-#endif /* LWIP_SO_RCVTIMEO */
-#if LWIP_SO_RCVBUF
- case SO_RCVBUF:
-#endif /* LWIP_SO_RCVBUF */
- /* UNIMPL case SO_OOBINLINE: */
- /* UNIMPL case SO_SNDBUF: */
- /* UNIMPL case SO_RCVLOWAT: */
- /* UNIMPL case SO_SNDLOWAT: */
-#if SO_REUSE
- case SO_REUSEADDR:
- case SO_REUSEPORT:
-#endif /* SO_REUSE */
- /* UNIMPL case SO_USELOOPBACK: */
- if (optlen < sizeof(int)) {
- err = EINVAL;
- }
- break;
- case SO_NO_CHECK:
- if (optlen < sizeof(int)) {
- err = EINVAL;
- }
-#if LWIP_UDP
- if ((NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) ||
- ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) {
- /* this flag is only available for UDP, not for UDP lite */
- err = EAFNOSUPPORT;
- }
-#endif /* LWIP_UDP */
- break;
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-
-/* Level: IPPROTO_IP */
- case IPPROTO_IP:
- switch (optname) {
- /* UNIMPL case IP_HDRINCL: */
- /* UNIMPL case IP_RCVDSTADDR: */
- /* UNIMPL case IP_RCVIF: */
- case IP_TTL:
- case IP_TOS:
- if (optlen < sizeof(int)) {
- err = EINVAL;
- }
- break;
-#if LWIP_IGMP
- case IP_MULTICAST_TTL:
- if (optlen < sizeof(u8_t)) {
- err = EINVAL;
- }
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
- err = EAFNOSUPPORT;
- }
- break;
- case IP_MULTICAST_IF:
- if (optlen < sizeof(struct in_addr)) {
- err = EINVAL;
- }
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
- err = EAFNOSUPPORT;
- }
- break;
- case IP_MULTICAST_LOOP:
- if (optlen < sizeof(u8_t)) {
- err = EINVAL;
- }
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
- err = EAFNOSUPPORT;
- }
- break;
- case IP_ADD_MEMBERSHIP:
- case IP_DROP_MEMBERSHIP:
- if (optlen < sizeof(struct ip_mreq)) {
- err = EINVAL;
- }
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
- err = EAFNOSUPPORT;
- }
- break;
-#endif /* LWIP_IGMP */
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-
-#if LWIP_TCP
-/* Level: IPPROTO_TCP */
- case IPPROTO_TCP:
- if (optlen < sizeof(int)) {
- err = EINVAL;
- break;
- }
-
- /* If this is no TCP socket, ignore any options. */
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP)
- return 0;
-
- switch (optname) {
- case TCP_NODELAY:
- case TCP_KEEPALIVE:
-#if LWIP_TCP_KEEPALIVE
- case TCP_KEEPIDLE:
- case TCP_KEEPINTVL:
- case TCP_KEEPCNT:
-#endif /* LWIP_TCP_KEEPALIVE */
- break;
-
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-#endif /* LWIP_TCP */
-
-#if LWIP_IPV6
-/* Level: IPPROTO_IPV6 */
- case IPPROTO_IPV6:
- switch (optname) {
- case IPV6_V6ONLY:
- if (optlen < sizeof(int)) {
- err = EINVAL;
- }
-
- /* @todo: this does not work for datagram sockets, yet */
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP)
- return 0;
-
- break;
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-#endif /* LWIP_IPV6 */
-
-#if LWIP_UDP && LWIP_UDPLITE
-/* Level: IPPROTO_UDPLITE */
- case IPPROTO_UDPLITE:
- if (optlen < sizeof(int)) {
- err = EINVAL;
- break;
- }
-
- /* If this is no UDP lite socket, ignore any options. */
- if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn)))
- return 0;
-
- switch (optname) {
- case UDPLITE_SEND_CSCOV:
- case UDPLITE_RECV_CSCOV:
- break;
-
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
- s, optname));
- err = ENOPROTOOPT;
- } /* switch (optname) */
- break;
-#endif /* LWIP_UDP && LWIP_UDPLITE */
-/* UNDEFINED LEVEL */
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
- s, level, optname));
- err = ENOPROTOOPT;
- } /* switch (level) */
+#if 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 (err != ERR_OK) {
- sock_set_errno(sock, err);
+#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);
+ done_socket(sock);
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
+ cberr = tcpip_callback(lwip_setsockopt_callback, &LWIP_SETGETSOCKOPT_DATA_VAR_REF(data));
+ if (cberr != ERR_OK) {
+ LWIP_SETGETSOCKOPT_DATA_VAR_FREE(data);
+ sock_set_errno(sock, err_to_errno(cberr));
+ done_socket(sock);
+ return -1;
+ }
+ sys_arch_sem_wait((sys_sem_t *)(LWIP_SETGETSOCKOPT_DATA_VAR_REF(data).completed_sem), 0);
-
- /* Now do the actual option processing */
- data.sock = sock;
-#ifdef LWIP_DEBUG
- data.s = s;
-#endif /* LWIP_DEBUG */
- data.level = level;
- data.optname = optname;
- data.optval = (void*)optval;
- data.optlen = &optlen;
- data.err = err;
- tcpip_callback(lwip_setsockopt_internal, &data);
- sys_arch_sem_wait(&sock->conn->op_completed, 0);
- /* maybe lwip_setsockopt_internal has changed err */
- err = data.err;
+ /* 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);
+ done_socket(sock);
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_internal(void *arg)
+lwip_setsockopt_callback(void *arg)
{
- struct lwip_sock *sock;
-#ifdef LWIP_DEBUG
- int s;
-#endif /* LWIP_DEBUG */
- int level, optname;
- const void *optval;
struct lwip_setgetsockopt_data *data;
-
LWIP_ASSERT("arg != NULL", arg != NULL);
+ data = (struct lwip_setgetsockopt_data *)arg;
+
+ 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);
- data = (struct lwip_setgetsockopt_data*)arg;
- sock = data->sock;
-#ifdef LWIP_DEBUG
- s = data->s;
-#endif /* LWIP_DEBUG */
- level = data->level;
- optname = data->optname;
- optval = data->optval;
+ 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 int
+lwip_setsockopt_impl(int s, int level, int optname, const void *optval, socklen_t optlen)
+{
+ int err = 0;
+ struct lwip_sock *sock = tryget_socket(s);
+ if (!sock) {
+ return EBADF;
+ }
switch (level) {
-/* Level: SOL_SOCKET */
- case SOL_SOCKET:
- switch (optname) {
+ /* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
- /* The option flags */
- case SO_BROADCAST:
- /* UNIMPL case SO_DEBUG: */
- /* UNIMPL case SO_DONTROUTE: */
- case SO_KEEPALIVE:
- /* UNIMPL case SO_OOBINCLUDE: */
+ /* SO_ACCEPTCONN is get-only */
+
+ /* The option flags */
+ case SO_BROADCAST:
+ case SO_KEEPALIVE:
#if SO_REUSE
- case SO_REUSEADDR:
- case SO_REUSEPORT:
+ case SO_REUSEADDR:
#endif /* SO_REUSE */
- /* UNIMPL case SO_USELOOPBACK: */
- if (*(int*)optval) {
- ip_set_option(sock->conn->pcb.ip, optname);
- } else {
- ip_reset_option(sock->conn->pcb.ip, optname);
- }
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n",
- s, optname, (*(int*)optval?"on":"off")));
- break;
+ if ((optname == SO_BROADCAST) &&
+ (NETCONNTYPE_GROUP(sock->conn->type) != NETCONN_UDP)) {
+ done_socket(sock);
+ return ENOPROTOOPT;
+ }
+ 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:
- netconn_set_sendtimeout(sock->conn, (s32_t)*(int*)optval);
- break;
+ case SO_SNDTIMEO: {
+ long ms_long;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+ ms_long = LWIP_SO_SNDRCVTIMEO_GET_MS(optval);
+ if (ms_long < 0) {
+ done_socket(sock);
+ return EINVAL;
+ }
+ netconn_set_sendtimeout(sock->conn, ms_long);
+ break;
+ }
#endif /* LWIP_SO_SNDTIMEO */
#if LWIP_SO_RCVTIMEO
- case SO_RCVTIMEO:
- netconn_set_recvtimeout(sock->conn, *(int*)optval);
- break;
+ case SO_RCVTIMEO: {
+ long ms_long;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, LWIP_SO_SNDRCVTIMEO_OPTTYPE);
+ ms_long = LWIP_SO_SNDRCVTIMEO_GET_MS(optval);
+ if (ms_long < 0) {
+ done_socket(sock);
+ return EINVAL;
+ }
+ netconn_set_recvtimeout(sock->conn, (u32_t)ms_long);
+ break;
+ }
#endif /* LWIP_SO_RCVTIMEO */
#if LWIP_SO_RCVBUF
- case SO_RCVBUF:
- netconn_set_recvbufsize(sock->conn, *(int*)optval);
- break;
+ 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) {
+ done_socket(sock);
+ 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:
- if (*(int*)optval) {
- udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM);
- } else {
- udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM);
- }
- break;
+ 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 */
+ done_socket(sock);
+ return EAFNOSUPPORT;
+ }
+#endif /* LWIP_UDPLITE */
+ if (*(const int *)optval) {
+ udp_set_flags(sock->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+ } else {
+ udp_clear_flags(sock->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+ }
+ break;
#endif /* LWIP_UDP */
- default:
- LWIP_ASSERT("unhandled optname", 0);
- break;
- } /* switch (optname) */
- break;
-
-/* Level: IPPROTO_IP */
- case IPPROTO_IP:
- switch (optname) {
- case IP_TTL:
- sock->conn->pcb.ip->ttl = (u8_t)(*(int*)optval);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n",
- s, sock->conn->pcb.ip->ttl));
- break;
- case IP_TOS:
- sock->conn->pcb.ip->tos = (u8_t)(*(int*)optval);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n",
- s, sock->conn->pcb.ip->tos));
- break;
-#if LWIP_IGMP
- case IP_MULTICAST_TTL:
- sock->conn->pcb.udp->ttl = (u8_t)(*(u8_t*)optval);
- break;
- case IP_MULTICAST_IF:
- inet_addr_to_ipaddr(&sock->conn->pcb.udp->multicast_ip, (struct in_addr*)optval);
- break;
- case IP_MULTICAST_LOOP:
- if (*(u8_t*)optval) {
- udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP);
- } else {
- udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP);
- }
- break;
- case IP_ADD_MEMBERSHIP:
- case IP_DROP_MEMBERSHIP:
- {
- /* If this is a TCP or a RAW socket, ignore these options. */
- struct ip_mreq *imr = (struct ip_mreq *)optval;
- ip_addr_t if_addr;
- ip_addr_t multi_addr;
- inet_addr_to_ipaddr(&if_addr, &imr->imr_interface);
- inet_addr_to_ipaddr(&multi_addr, &imr->imr_multiaddr);
- if(optname == IP_ADD_MEMBERSHIP){
- data->err = igmp_joingroup(&if_addr, &multi_addr);
- } else {
- data->err = igmp_leavegroup(&if_addr, &multi_addr);
+ case SO_BINDTODEVICE: {
+ const struct ifreq *iface;
+ struct netif *n = NULL;
+
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN(sock, optlen, struct ifreq);
+
+ iface = (const struct ifreq *)optval;
+ if (iface->ifr_name[0] != 0) {
+ n = netif_find(iface->ifr_name);
+ if (n == NULL) {
+ done_socket(sock);
+ return ENODEV;
+ }
+ }
+
+ switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) {
+#if LWIP_TCP
+ case NETCONN_TCP:
+ tcp_bind_netif(sock->conn->pcb.tcp, n);
+ break;
+#endif
+#if LWIP_UDP
+ case NETCONN_UDP:
+ udp_bind_netif(sock->conn->pcb.udp, n);
+ break;
+#endif
+#if LWIP_RAW
+ case NETCONN_RAW:
+ raw_bind_netif(sock->conn->pcb.raw, n);
+ break;
+#endif
+ default:
+ LWIP_ASSERT("Unhandled netconn type in SO_BINDTODEVICE", 0);
+ break;
+ }
}
- if(data->err != ERR_OK) {
- data->err = EADDRNOTAVAIL;
+ break;
+ 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_NETBUF_RECVINFO
+ case IP_PKTINFO:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, int, NETCONN_UDP);
+ if (*(const int *)optval) {
+ sock->conn->flags |= NETCONN_FLAG_PKTINFO;
+ } else {
+ sock->conn->flags &= ~NETCONN_FLAG_PKTINFO;
+ }
+ break;
+#endif /* LWIP_NETBUF_RECVINFO */
+#if LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP
+ 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;
+ break;
+ case IP_MULTICAST_LOOP:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, u8_t, NETCONN_UDP);
+ if (*(const u8_t *)optval) {
+ udp_set_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
+ } else {
+ udp_clear_flags(sock->conn->pcb.udp, UDP_FLAGS_MULTICAST_LOOP);
+ }
+ break;
+#endif /* LWIP_IPV4 && LWIP_MULTICAST_TX_OPTIONS && LWIP_UDP */
+#if LWIP_IGMP
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP: {
+ /* If this is a TCP or a RAW socket, ignore these options. */
+ 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_ASSERT("unhandled optname", 0);
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
break;
- } /* switch (optname) */
- break;
#if LWIP_TCP
-/* Level: IPPROTO_TCP */
- case IPPROTO_TCP:
- switch (optname) {
- case TCP_NODELAY:
- if (*(int*)optval) {
- tcp_nagle_disable(sock->conn->pcb.tcp);
- } else {
- tcp_nagle_enable(sock->conn->pcb.tcp);
+ /* 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) {
+ done_socket(sock);
+ return EINVAL;
}
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n",
- s, (*(int *)optval)?"on":"off") );
- break;
- case TCP_KEEPALIVE:
- sock->conn->pcb.tcp->keep_idle = (u32_t)(*(int*)optval);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n",
- s, sock->conn->pcb.tcp->keep_idle));
- break;
+ 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)(*(int*)optval);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n",
- s, sock->conn->pcb.tcp->keep_idle));
- break;
- case TCP_KEEPINTVL:
- sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(int*)optval);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n",
- s, sock->conn->pcb.tcp->keep_intvl));
- break;
- case TCP_KEEPCNT:
- sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(int*)optval);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n",
- s, sock->conn->pcb.tcp->keep_cnt));
- break;
+ 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_ASSERT("unhandled optname", 0);
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
break;
- } /* switch (optname) */
- break;
#endif /* LWIP_TCP*/
#if LWIP_IPV6
-/* Level: IPPROTO_IPV6 */
- case IPPROTO_IPV6:
- switch (optname) {
- case IPV6_V6ONLY:
- if (*(int*)optval) {
- sock->conn->flags |= NETCONN_FLAG_IPV6_V6ONLY;
- } else {
- sock->conn->flags &= ~NETCONN_FLAG_IPV6_V6ONLY;
- }
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n",
- s, ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0)));
- break;
- default:
- LWIP_ASSERT("unhandled optname", 0);
+ /* Level: IPPROTO_IPV6 */
+ case IPPROTO_IPV6:
+ switch (optname) {
+ case IPV6_V6ONLY:
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB(sock, optlen, int);
+ 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;
+#if LWIP_IPV6_MLD
+ case IPV6_JOIN_GROUP:
+ case IPV6_LEAVE_GROUP: {
+ /* If this is a TCP or a RAW socket, ignore these options. */
+ err_t mld6_err;
+ struct netif *netif;
+ ip6_addr_t multi_addr;
+ const struct ipv6_mreq *imr = (const struct ipv6_mreq *)optval;
+ LWIP_SOCKOPT_CHECK_OPTLEN_CONN_PCB_TYPE(sock, optlen, struct ipv6_mreq, NETCONN_UDP);
+ inet6_addr_to_ip6addr(&multi_addr, &imr->ipv6mr_multiaddr);
+ LWIP_ASSERT("Invalid netif index", imr->ipv6mr_interface <= 0xFFu);
+ netif = netif_get_by_index((u8_t)imr->ipv6mr_interface);
+ if (netif == NULL) {
+ err = EADDRNOTAVAIL;
+ break;
+ }
+
+ if (optname == IPV6_JOIN_GROUP) {
+ if (!lwip_socket_register_mld6_membership(s, imr->ipv6mr_interface, &multi_addr)) {
+ /* cannot track membership (out of memory) */
+ err = ENOMEM;
+ mld6_err = ERR_OK;
+ } else {
+ mld6_err = mld6_joingroup_netif(netif, &multi_addr);
+ }
+ } else {
+ mld6_err = mld6_leavegroup_netif(netif, &multi_addr);
+ lwip_socket_unregister_mld6_membership(s, imr->ipv6mr_interface, &multi_addr);
+ }
+ if (mld6_err != ERR_OK) {
+ err = EADDRNOTAVAIL;
+ }
+ }
+ break;
+#endif /* LWIP_IPV6_MLD */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ break;
+ } /* switch (optname) */
break;
- } /* switch (optname) */
- break;
#endif /* LWIP_IPV6 */
#if LWIP_UDP && LWIP_UDPLITE
- /* Level: IPPROTO_UDPLITE */
- case IPPROTO_UDPLITE:
- switch (optname) {
- case UDPLITE_SEND_CSCOV:
- if ((*(int*)optval != 0) && ((*(int*)optval < 8) || (*(int*)optval > 0xffff))) {
- /* don't allow illegal values! */
- sock->conn->pcb.udp->chksum_len_tx = 8;
- } else {
- sock->conn->pcb.udp->chksum_len_tx = (u16_t)*(int*)optval;
+ /* 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))) {
+ done_socket(sock);
+ return ENOPROTOOPT;
}
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n",
- s, (*(int*)optval)) );
+ 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;
- case UDPLITE_RECV_CSCOV:
- if ((*(int*)optval != 0) && ((*(int*)optval < 8) || (*(int*)optval > 0xffff))) {
- /* don't allow illegal values! */
- sock->conn->pcb.udp->chksum_len_rx = 8;
- } else {
- sock->conn->pcb.udp->chksum_len_rx = (u16_t)*(int*)optval;
- }
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n",
- s, (*(int*)optval)) );
+#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) {
+ done_socket(sock);
+ 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 */
+ done_socket(sock);
+ 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_ASSERT("unhandled optname", 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+ s, level, optname));
+ err = ENOPROTOOPT;
break;
- } /* switch (optname) */
- break;
-#endif /* LWIP_UDP */
- default:
- LWIP_ASSERT("unhandled level", 0);
- break;
} /* switch (level) */
- sys_sem_signal(&sock->conn->op_completed);
+
+ done_socket(sock);
+ return err;
}
int
@@ -2435,8 +3302,7 @@ lwip_ioctl(int s, long cmd, void *argp)
struct lwip_sock *sock = get_socket(s);
u8_t val;
#if LWIP_SO_RCVBUF
- u16_t buflen = 0;
- s16_t recv_avail;
+ int recv_avail;
#endif /* LWIP_SO_RCVBUF */
if (!sock) {
@@ -2445,111 +3311,407 @@ lwip_ioctl(int s, long cmd, void *argp)
switch (cmd) {
#if LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE
- case FIONREAD:
- if (!argp) {
- sock_set_errno(sock, EINVAL);
- return -1;
- }
+ case FIONREAD:
+ if (!argp) {
+ sock_set_errno(sock, EINVAL);
+ done_socket(sock);
+ return -1;
+ }
#if LWIP_FIONREAD_LINUXMODE
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
- struct pbuf *p;
- if (sock->lastdata) {
- p = ((struct netbuf *)sock->lastdata)->p;
- } else {
- struct netbuf *rxbuf;
- err_t err;
- if (sock->rcvevent <= 0) {
- *((u16_t*)argp) = 0;
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+ struct netbuf *nb;
+ if (sock->lastdata.netbuf) {
+ nb = sock->lastdata.netbuf;
+ *((int *)argp) = nb->p->tot_len;
} else {
- err = netconn_recv(sock->conn, &rxbuf);
+ struct netbuf *rxbuf;
+ err_t err = netconn_recv_udp_raw_netbuf_flags(sock->conn, &rxbuf, NETCONN_DONTBLOCK);
if (err != ERR_OK) {
- *((u16_t*)argp) = 0;
+ *((int *)argp) = 0;
} else {
- sock->lastdata = rxbuf;
- *((u16_t*)argp) = rxbuf->p->tot_len;
+ sock->lastdata.netbuf = rxbuf;
+ *((int *)argp) = rxbuf->p->tot_len;
}
}
+ done_socket(sock);
+ return 0;
}
- return 0;
- }
#endif /* LWIP_FIONREAD_LINUXMODE */
#if LWIP_SO_RCVBUF
- /* we come here if either LWIP_FIONREAD_LINUXMODE==0 or this is a TCP socket */
- SYS_ARCH_GET(sock->conn->recv_avail, recv_avail);
- if (recv_avail < 0) {
- recv_avail = 0;
- }
- *((u16_t*)argp) = (u16_t)recv_avail;
-
- /* Check if there is data left from the last recv operation. /maq 041215 */
- if (sock->lastdata) {
- struct pbuf *p = (struct pbuf *)sock->lastdata;
- if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
- p = ((struct netbuf *)p)->p;
+ /* 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;
}
- buflen = p->tot_len;
- buflen -= sock->lastoffset;
- *((u16_t*)argp) += buflen;
- }
+ /* Check if there is data left from the last recv operation. /maq 041215 */
+ if (sock->lastdata.netbuf) {
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+ recv_avail += sock->lastdata.pbuf->tot_len;
+ } else {
+ recv_avail += sock->lastdata.netbuf->p->tot_len;
+ }
+ }
+ *((int *)argp) = recv_avail;
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp)));
- sock_set_errno(sock, 0);
- return 0;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t *)argp)));
+ sock_set_errno(sock, 0);
+ done_socket(sock);
+ return 0;
#else /* LWIP_SO_RCVBUF */
- break;
+ break;
#endif /* LWIP_SO_RCVBUF */
#endif /* LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE */
- case FIONBIO:
- val = 0;
- if (argp && *(u32_t*)argp) {
- val = 1;
- }
- netconn_set_nonblocking(sock->conn, val);
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val));
- sock_set_errno(sock, 0);
- return 0;
+ case (long)FIONBIO:
+ val = 0;
+ if (argp && *(int *)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);
+ done_socket(sock);
+ return 0;
- default:
- break;
+ 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 */
+ done_socket(sock);
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.
+ * The flag O_NONBLOCK and access modes are supported for F_GETFL, only
+ * the flag O_NONBLOCK is implemented for F_SETFL.
*/
int
lwip_fcntl(int s, int cmd, int val)
{
struct lwip_sock *sock = get_socket(s);
int ret = -1;
+ int op_mode = 0;
- if (!sock || !sock->conn) {
+ if (!sock) {
return -1;
}
switch (cmd) {
- case F_GETFL:
- ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0;
- break;
- case F_SETFL:
- if ((val & ~O_NONBLOCK) == 0) {
- /* only O_NONBLOCK, all other bits are zero */
- netconn_set_nonblocking(sock->conn, val & O_NONBLOCK);
- ret = 0;
- }
- break;
- default:
- LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val));
- break;
+ case F_GETFL:
+ ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0;
+ sock_set_errno(sock, 0);
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCPIP_CORE_LOCKING
+ LOCK_TCPIP_CORE();
+#else
+ SYS_ARCH_DECL_PROTECT(lev);
+ /* the proper thing to do here would be to get into the tcpip_thread,
+ but locking should be OK as well since we only *read* some flags */
+ SYS_ARCH_PROTECT(lev);
+#endif
+#if LWIP_TCP
+ if (sock->conn->pcb.tcp) {
+ if (!(sock->conn->pcb.tcp->flags & TF_RXCLOSED)) {
+ op_mode |= O_RDONLY;
+ }
+ if (!(sock->conn->pcb.tcp->flags & TF_FIN)) {
+ op_mode |= O_WRONLY;
+ }
+ }
+#endif
+#if LWIP_TCPIP_CORE_LOCKING
+ UNLOCK_TCPIP_CORE();
+#else
+ SYS_ARCH_UNPROTECT(lev);
+#endif
+ } else {
+ op_mode |= O_RDWR;
+ }
+
+ /* ensure O_RDWR for (O_RDONLY|O_WRONLY) != O_RDWR cases */
+ ret |= (op_mode == (O_RDONLY | O_WRONLY)) ? O_RDWR : op_mode;
+
+ break;
+ case F_SETFL:
+ /* Bits corresponding to the file access mode and the file creation flags [..] that are set in arg shall be ignored */
+ val &= ~(O_RDONLY | O_WRONLY | O_RDWR);
+ 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;
+ }
+ done_socket(sock);
+ return ret;
+}
+
+#if LWIP_COMPAT_SOCKETS == 2 && LWIP_POSIX_SOCKETS_IO_NAMES
+int
+fcntl(int s, int cmd, ...)
+{
+ va_list ap;
+ int val;
+
+ va_start(ap, cmd);
+ val = va_arg(ap, int);
+ va_end(ap);
+ return lwip_fcntl(s, cmd, val);
+}
+#endif
+
+const char *
+lwip_inet_ntop(int af, const void *src, char *dst, socklen_t size)
+{
+ const char *ret = NULL;
+ int size_int = (int)size;
+ if (size_int < 0) {
+ set_errno(ENOSPC);
+ return NULL;
+ }
+ switch (af) {
+#if LWIP_IPV4
+ case AF_INET:
+ ret = ip4addr_ntoa_r((const ip4_addr_t *)src, dst, size_int);
+ if (ret == NULL) {
+ set_errno(ENOSPC);
+ }
+ break;
+#endif
+#if LWIP_IPV6
+ case AF_INET6:
+ ret = ip6addr_ntoa_r((const ip6_addr_t *)src, dst, size_int);
+ if (ret == NULL) {
+ set_errno(ENOSPC);
+ }
+ break;
+#endif
+ default:
+ set_errno(EAFNOSUPPORT);
+ break;
}
return ret;
}
+int
+lwip_inet_pton(int af, const char *src, void *dst)
+{
+ int err;
+ switch (af) {
+#if LWIP_IPV4
+ case AF_INET:
+ err = ip4addr_aton(src, (ip4_addr_t *)dst);
+ break;
+#endif
+#if LWIP_IPV6
+ case AF_INET6: {
+ /* convert into temporary variable since ip6_addr_t might be larger
+ than in6_addr when scopes are enabled */
+ ip6_addr_t addr;
+ err = ip6addr_aton(src, &addr);
+ if (err) {
+ memcpy(dst, &addr.addr, sizeof(addr.addr));
+ }
+ break;
+ }
+#endif
+ default:
+ err = -1;
+ set_errno(EAFNOSUPPORT);
+ break;
+ }
+ return err;
+}
+
+#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);
+ done_socket(sock);
+ return 1;
+ }
+ }
+ done_socket(sock);
+ 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);
+ break;
+ }
+ }
+ done_socket(sock);
+}
+
+/** 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);
+ }
+ }
+ done_socket(sock);
+}
+#endif /* LWIP_IGMP */
+
+#if LWIP_IPV6_MLD
+/** Register a new MLD6 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_mld6_membership(int s, unsigned int if_idx, const ip6_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_ipv6_multicast_memberships[i].sock == NULL) {
+ socket_ipv6_multicast_memberships[i].sock = sock;
+ socket_ipv6_multicast_memberships[i].if_idx = (u8_t)if_idx;
+ ip6_addr_copy(socket_ipv6_multicast_memberships[i].multi_addr, *multi_addr);
+ done_socket(sock);
+ return 1;
+ }
+ }
+ done_socket(sock);
+ return 0;
+}
+
+/** Unregister a previously registered MLD6 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_mld6_membership(int s, unsigned int if_idx, const ip6_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_ipv6_multicast_memberships[i].sock == sock) &&
+ (socket_ipv6_multicast_memberships[i].if_idx == if_idx) &&
+ ip6_addr_cmp(&socket_ipv6_multicast_memberships[i].multi_addr, multi_addr)) {
+ socket_ipv6_multicast_memberships[i].sock = NULL;
+ socket_ipv6_multicast_memberships[i].if_idx = NETIF_NO_INDEX;
+ ip6_addr_set_zero(&socket_ipv6_multicast_memberships[i].multi_addr);
+ break;
+ }
+ }
+ done_socket(sock);
+}
+
+/** Drop all MLD6 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_mld6_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_ipv6_multicast_memberships[i].sock == sock) {
+ ip_addr_t multi_addr;
+ u8_t if_idx;
+
+ ip_addr_copy_from_ip6(multi_addr, socket_ipv6_multicast_memberships[i].multi_addr);
+ if_idx = socket_ipv6_multicast_memberships[i].if_idx;
+
+ socket_ipv6_multicast_memberships[i].sock = NULL;
+ socket_ipv6_multicast_memberships[i].if_idx = NETIF_NO_INDEX;
+ ip6_addr_set_zero(&socket_ipv6_multicast_memberships[i].multi_addr);
+
+ netconn_join_leave_group_netif(sock->conn, &multi_addr, if_idx, NETCONN_LEAVE);
+ }
+ }
+ done_socket(sock);
+}
+#endif /* LWIP_IPV6_MLD */
+
#endif /* LWIP_SOCKET */
diff --git a/lwip/src/api/tcpip.c b/lwip/src/api/tcpip.c
index 7c1c9ca..f2c6534 100644
--- a/lwip/src/api/tcpip.c
+++ b/lwip/src/api/tcpip.c
@@ -40,14 +40,20 @@
#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/pbuf.h"
-#include "lwip/tcpip.h"
#include "lwip/init.h"
-#include "netif/etharp.h"
-#include "netif/ppp_oe.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;
@@ -59,6 +65,15 @@ static sys_mbox_t mbox;
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 */
+
+static void tcpip_thread_handle_msg(struct tcpip_msg *msg);
/**
* The main lwIP thread. This thread has exclusive access to lwIP core functions
@@ -85,44 +100,45 @@ tcpip_thread(void *arg)
UNLOCK_TCPIP_CORE();
LWIP_TCPIP_THREAD_ALIVE();
/* wait for a message, timeouts are processed while waiting */
- sys_timeouts_mbox_fetch(&mbox, (void **)&msg);
+ TCPIP_MBOX_FETCH(&mbox, (void **)&msg);
LOCK_TCPIP_CORE();
- switch (msg->type) {
-#if LWIP_NETCONN
+ if (msg == NULL) {
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: NULL\n"));
+ LWIP_ASSERT("tcpip_thread: invalid message", 0);
+ continue;
+ }
+ tcpip_thread_handle_msg(msg);
+ }
+}
+
+/* Handle a single tcpip_msg
+ * This is in its own function for access by tests only.
+ */
+static void
+tcpip_thread_handle_msg(struct tcpip_msg *msg)
+{
+ 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.apimsg->function(&(msg->msg.apimsg->msg));
+ msg->msg.api_msg.function(msg->msg.api_msg.msg);
break;
-#endif /* LWIP_NETCONN */
+ 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));
-#if LWIP_ETHERNET
- if (msg->msg.inp.netif->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
- ethernet_input(msg->msg.inp.p, msg->msg.inp.netif);
- } else
-#endif /* LWIP_ETHERNET */
-#if LWIP_IPV6
- if ((*((unsigned char *)(msg->msg.inp.p->payload)) & 0xf0) == 0x60) {
- ip6_input(msg->msg.inp.p, msg->msg.inp.netif);
- } else
-#endif /* LWIP_IPV6 */
- {
- ip_input(msg->msg.inp.p, msg->msg.inp.netif);
- }
+ 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_NETIF_API
- case TCPIP_MSG_NETIFAPI:
- LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: Netif API message %p\n", (void *)msg));
- msg->msg.netifapimsg->function(&(msg->msg.netifapimsg->msg));
- break;
-#endif /* LWIP_NETIF_API */
+#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
-#if LWIP_TCPIP_TIMEOUT
+#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);
@@ -133,7 +149,7 @@ tcpip_thread(void *arg)
sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
memp_free(MEMP_TCPIP_MSG_API, msg);
break;
-#endif /* LWIP_TCPIP_TIMEOUT */
+#endif /* LWIP_TCPIP_TIMEOUT && LWIP_TIMERS */
case TCPIP_MSG_CALLBACK:
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
@@ -150,41 +166,52 @@ tcpip_thread(void *arg)
LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
LWIP_ASSERT("tcpip_thread: invalid message", 0);
break;
+ }
+}
+
+#ifdef TCPIP_THREAD_TEST
+/** Work on queued items in single-threaded test mode */
+int
+tcpip_thread_poll_one(void)
+{
+ int ret = 0;
+ struct tcpip_msg *msg;
+
+ /* wait for a message, timeouts are processed while waiting */
+ if (sys_arch_mbox_tryfetch(&mbox, (void **)&msg) != SYS_ARCH_TIMEOUT) {
+ LOCK_TCPIP_CORE();
+ if (msg != NULL) {
+ tcpip_thread_handle_msg(msg);
+ ret = 1;
}
+ UNLOCK_TCPIP_CORE();
}
+ return ret;
}
+#endif
/**
* Pass a received packet to tcpip_thread for input processing
*
- * @param p the received packet, p->payload pointing to the Ethernet header or
- * to an IP header (if inp doesn't have NETIF_FLAG_ETHARP or
- * NETIF_FLAG_ETHERNET flags)
+ * @param 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_input(struct pbuf *p, struct netif *inp)
+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_input: PACKET %p/%p\n", (void *)p, (void *)inp));
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_inpkt: PACKET %p/%p\n", (void *)p, (void *)inp));
LOCK_TCPIP_CORE();
-#if LWIP_ETHERNET
- if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
- ret = ethernet_input(p, inp);
- } else
-#endif /* LWIP_ETHERNET */
- {
- ret = ip_input(p, inp);
- }
+ ret = input_fn(p, inp);
UNLOCK_TCPIP_CORE();
return ret;
#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
struct tcpip_msg *msg;
- if (!sys_mbox_valid(&mbox)) {
- return ERR_VAL;
- }
+ 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;
@@ -193,6 +220,7 @@ tcpip_input(struct pbuf *p, struct netif *inp)
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;
@@ -202,48 +230,106 @@ tcpip_input(struct pbuf *p, struct netif *inp)
}
/**
+ * @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);
+}
+
+/**
+ * @ingroup lwip_os
* 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.
+ * Blocks until the request is posted.
+ * Must not be called from interrupt context!
*
- * @param f the function to call
+ * @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
+ *
+ * @see tcpip_try_callback
*/
err_t
-tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block)
+tcpip_callback(tcpip_callback_fn function, void *ctx)
{
struct tcpip_msg *msg;
- if (sys_mbox_valid(&mbox)) {
- msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
- if (msg == NULL) {
- return ERR_MEM;
- }
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
- 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;
+ 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;
+
+ sys_mbox_post(&mbox, msg);
+ return ERR_OK;
+}
+
+/**
+ * @ingroup lwip_os
+ * 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.
+ * Does NOT block when the request cannot be posted because the
+ * mbox is full, but returns ERR_MEM instead.
+ * Can be called from interrupt context.
+ *
+ * @param function the function to call
+ * @param ctx parameter passed to f
+ * @return ERR_OK if the function was called, another err_t if not
+ *
+ * @see tcpip_callback
+ */
+err_t
+tcpip_try_callback(tcpip_callback_fn function, void *ctx)
+{
+ 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;
}
- return ERR_VAL;
+
+ msg->type = TCPIP_MSG_CALLBACK;
+ msg->msg.cb.function = function;
+ msg->msg.cb.ctx = ctx;
+
+ 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
+#if LWIP_TCPIP_TIMEOUT && LWIP_TIMERS
/**
* call sys_timeout in tcpip_thread
*
- * @param msec time in milliseconds for timeout
+ * @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
@@ -253,26 +339,24 @@ tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
{
struct tcpip_msg *msg;
- if (sys_mbox_valid(&mbox)) {
- msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
- if (msg == NULL) {
- return ERR_MEM;
- }
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
- 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;
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
}
- return ERR_VAL;
+
+ 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 msec time in milliseconds for timeout
* @param h function to be called on timeout
* @param arg argument to pass to timeout function h
* @return ERR_MEM on memory error, ERR_OK otherwise
@@ -282,101 +366,111 @@ tcpip_untimeout(sys_timeout_handler h, void *arg)
{
struct tcpip_msg *msg;
- if (sys_mbox_valid(&mbox)) {
- msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
- if (msg == NULL) {
- return ERR_MEM;
- }
+ LWIP_ASSERT("Invalid mbox", sys_mbox_valid_val(mbox));
- msg->type = TCPIP_MSG_UNTIMEOUT;
- msg->msg.tmo.h = h;
- msg->msg.tmo.arg = arg;
- sys_mbox_post(&mbox, msg);
- return ERR_OK;
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
}
- return ERR_VAL;
-}
-#endif /* LWIP_TCPIP_TIMEOUT */
-#if LWIP_NETCONN
-/**
- * Call the lower part of a netconn_* function
- * This function is then running in the thread context
- * of tcpip_thread and has exclusive access to lwIP core code.
- *
- * @param apimsg a struct containing the function to call and its parameters
- * @return ERR_OK if the function was called, another err_t if not
- */
-err_t
-tcpip_apimsg(struct api_msg *apimsg)
-{
- struct tcpip_msg msg;
-#ifdef LWIP_DEBUG
- /* catch functions that don't set err */
- apimsg->msg.err = ERR_VAL;
-#endif
-
- if (sys_mbox_valid(&mbox)) {
- msg.type = TCPIP_MSG_API;
- msg.msg.apimsg = apimsg;
- sys_mbox_post(&mbox, &msg);
- sys_arch_sem_wait(&apimsg->msg.conn->op_completed, 0);
- return apimsg->msg.err;
- }
- return ERR_VAL;
+ 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 */
-#endif /* LWIP_NETCONN */
-#if LWIP_NETIF_API
-#if !LWIP_TCPIP_CORE_LOCKING
/**
- * Much like tcpip_apimsg, but calls the lower part of a netifapi_*
- * function.
+ * 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 netifapimsg a struct containing the function to call and its parameters
- * @return error code given back by the function that was called
+ * @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_netifapi(struct netifapi_msg* netifapimsg)
+tcpip_send_msg_wait_sem(tcpip_callback_fn fn, void *apimsg, sys_sem_t *sem)
{
- struct tcpip_msg msg;
-
- if (sys_mbox_valid(&mbox)) {
- err_t err = sys_sem_new(&netifapimsg->msg.sem, 0);
- if (err != ERR_OK) {
- netifapimsg->msg.err = err;
- return err;
- }
-
- msg.type = TCPIP_MSG_NETIFAPI;
- msg.msg.netifapimsg = netifapimsg;
- sys_mbox_post(&mbox, &msg);
- sys_sem_wait(&netifapimsg->msg.sem);
- sys_sem_free(&netifapimsg->msg.sem);
- return netifapimsg->msg.err;
- }
- return ERR_VAL;
+#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 */
}
-#else /* !LWIP_TCPIP_CORE_LOCKING */
+
/**
- * Call the lower part of a netifapi_* function
- * This function has exclusive access to lwIP core code by locking it
- * before the function is called.
- *
- * @param netifapimsg a struct containing the function to call and its parameters
- * @return ERR_OK (only for compatibility fo tcpip_netifapi())
+ * 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_netifapi_lock(struct netifapi_msg* netifapimsg)
+tcpip_api_call(tcpip_api_call_fn fn, struct tcpip_api_call_data *call)
{
- LOCK_TCPIP_CORE();
- netifapimsg->function(&(netifapimsg->msg));
+#if LWIP_TCPIP_CORE_LOCKING
+ err_t err;
+ LOCK_TCPIP_CORE();
+ err = fn(call);
UNLOCK_TCPIP_CORE();
- return netifapimsg->msg.err;
+ 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 */
}
-#endif /* !LWIP_TCPIP_CORE_LOCKING */
-#endif /* LWIP_NETIF_API */
/**
* Allocate a structure for a static callback message and initialize it.
@@ -386,7 +480,8 @@ tcpip_netifapi_lock(struct netifapi_msg* netifapimsg)
* @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_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) {
@@ -395,7 +490,7 @@ struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, voi
msg->type = TCPIP_MSG_CALLBACK_STATIC;
msg->msg.cb.function = function;
msg->msg.cb.ctx = ctx;
- return (struct tcpip_callback_msg*)msg;
+ return (struct tcpip_callback_msg *)msg;
}
/**
@@ -403,7 +498,8 @@ struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, voi
*
* @param msg the message to free
*/
-void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg)
+void
+tcpip_callbackmsg_delete(struct tcpip_callback_msg *msg)
{
memp_free(MEMP_TCPIP_MSG_API, msg);
}
@@ -416,15 +512,14 @@ void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg)
* @return sys_mbox_trypost() return code
*/
err_t
-tcpip_trycallback(struct tcpip_callback_msg* msg)
+tcpip_trycallback(struct tcpip_callback_msg *msg)
{
- if (!sys_mbox_valid(&mbox)) {
- return ERR_VAL;
- }
+ 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
@@ -439,11 +534,11 @@ tcpip_init(tcpip_init_done_fn initfunc, void *arg)
tcpip_init_done = initfunc;
tcpip_init_done_arg = arg;
- if(sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
+ 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) {
+ if (sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
LWIP_ASSERT("failed to create lock_tcpip_core", 0);
}
#endif /* LWIP_TCPIP_CORE_LOCKING */
@@ -473,7 +568,7 @@ pbuf_free_int(void *p)
err_t
pbuf_free_callback(struct pbuf *p)
{
- return tcpip_callback_with_block(pbuf_free_int, p, 0);
+ return tcpip_try_callback(pbuf_free_int, p);
}
/**
@@ -486,7 +581,7 @@ pbuf_free_callback(struct pbuf *p)
err_t
mem_free_callback(void *m)
{
- return tcpip_callback_with_block(mem_free, m, 0);
+ return tcpip_try_callback(mem_free, m);
}
#endif /* !NO_SYS */
diff --git a/lwip/src/apps/altcp_tls/altcp_tls_mbedtls.c b/lwip/src/apps/altcp_tls/altcp_tls_mbedtls.c
new file mode 100644
index 0000000..de60801
--- /dev/null
+++ b/lwip/src/apps/altcp_tls/altcp_tls_mbedtls.c
@@ -0,0 +1,1008 @@
+/**
+ * @file
+ * Application layered TCP/TLS connection API (to be used from TCPIP thread)
+ *
+ * This file provides a TLS layer using mbedTLS
+ */
+
+/*
+ * Copyright (c) 2017 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 <goldsimon@gmx.de>
+ *
+ * Watch out:
+ * - 'sent' is always called with len==0 to the upper layer. This is because keeping
+ * track of the ratio of application data and TLS overhead would be too much.
+ *
+ * Mandatory security-related configuration:
+ * - define ALTCP_MBEDTLS_RNG_FN to a custom GOOD rng function returning 0 on success:
+ * int my_rng_fn(void *ctx, unsigned char *buffer , size_t len)
+ * - define ALTCP_MBEDTLS_ENTROPY_PTR and ALTCP_MBEDTLS_ENTROPY_LEN to something providing
+ * GOOD custom entropy
+ *
+ * Missing things / @todo:
+ * - RX data is acknowledged after receiving (tcp_recved is called when enqueueing
+ * the pbuf for mbedTLS receive, not when processed by mbedTLS or the inner
+ * connection; altcp_recved() from inner connection does nothing)
+ * - Client connections starting with 'connect()' are not handled yet...
+ * - some unhandled things are caught by LWIP_ASSERTs...
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/altcp_tls_mbedtls_opts.h"
+
+#if LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS
+
+#include "lwip/altcp.h"
+#include "lwip/altcp_tls.h"
+#include "lwip/priv/altcp_priv.h"
+
+#include "altcp_tls_mbedtls_structs.h"
+#include "altcp_tls_mbedtls_mem.h"
+
+/* @todo: which includes are really needed? */
+#include "mbedtls/entropy.h"
+#include "mbedtls/ctr_drbg.h"
+#include "mbedtls/certs.h"
+#include "mbedtls/x509.h"
+#include "mbedtls/ssl.h"
+#include "mbedtls/net.h"
+#include "mbedtls/error.h"
+#include "mbedtls/debug.h"
+#include "mbedtls/platform.h"
+#include "mbedtls/memory_buffer_alloc.h"
+#include "mbedtls/ssl_cache.h"
+
+#include "mbedtls/ssl_internal.h" /* to call mbedtls_flush_output after ERR_MEM */
+
+#include <string.h>
+
+#ifndef ALTCP_MBEDTLS_ENTROPY_PTR
+#define ALTCP_MBEDTLS_ENTROPY_PTR NULL
+#endif
+#ifndef ALTCP_MBEDTLS_ENTROPY_LEN
+#define ALTCP_MBEDTLS_ENTROPY_LEN 0
+#endif
+
+/* Variable prototype, the actual declaration is at the end of this file
+ since it contains pointers to static functions declared here */
+extern const struct altcp_functions altcp_mbedtls_functions;
+
+/** Our global mbedTLS configuration (server-specific, not connection-specific) */
+struct altcp_tls_config {
+ mbedtls_ssl_config conf;
+ mbedtls_entropy_context entropy;
+ mbedtls_ctr_drbg_context ctr_drbg;
+#if defined(MBEDTLS_SSL_CACHE_C) && ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS
+ /** Inter-connection cache for fast connection startup */
+ struct mbedtls_ssl_cache_context cache;
+#endif
+};
+
+static err_t altcp_mbedtls_lower_recv(void *arg, struct altcp_pcb *inner_conn, struct pbuf *p, err_t err);
+static err_t altcp_mbedtls_setup(void *conf, struct altcp_pcb *conn, struct altcp_pcb *inner_conn);
+static void altcp_mbedtls_dealloc(struct altcp_pcb *conn);
+static err_t altcp_mbedtls_lower_recv_process(struct altcp_pcb *conn, altcp_mbedtls_state_t *state);
+static err_t altcp_mbedtls_handle_rx_appldata(struct altcp_pcb *conn, altcp_mbedtls_state_t *state);
+static int altcp_mbedtls_bio_send(void *ctx, const unsigned char *dataptr, size_t size);
+
+
+/* callback functions from inner/lower connection: */
+
+/** Accept callback from lower connection (i.e. TCP)
+ * Allocate one of our structures, assign it to the new connection's 'state' and
+ * call the new connection's 'accepted' callback. If that succeeds, we wait
+ * to receive connection setup handshake bytes from the client.
+ */
+static err_t
+altcp_mbedtls_lower_accept(void *arg, struct altcp_pcb *accepted_conn, err_t err)
+{
+ struct altcp_pcb *listen_conn = (struct altcp_pcb *)arg;
+ if (listen_conn && listen_conn->state && listen_conn->accept) {
+ err_t setup_err;
+ altcp_mbedtls_state_t *listen_state = (altcp_mbedtls_state_t *)listen_conn->state;
+ /* create a new altcp_conn to pass to the next 'accept' callback */
+ struct altcp_pcb *new_conn = altcp_alloc();
+ if (new_conn == NULL) {
+ return ERR_MEM;
+ }
+ setup_err = altcp_mbedtls_setup(listen_state->conf, new_conn, accepted_conn);
+ if (setup_err != ERR_OK) {
+ altcp_free(new_conn);
+ return setup_err;
+ }
+ return listen_conn->accept(listen_conn->arg, new_conn, err);
+ }
+ return ERR_ARG;
+}
+
+/** Connected callback from lower connection (i.e. TCP).
+ * Not really implemented/tested yet...
+ */
+static err_t
+altcp_mbedtls_lower_connected(void *arg, struct altcp_pcb *inner_conn, err_t err)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn && conn->state) {
+ LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+ /* upper connected is called when handshake is done */
+ if (err != ERR_OK) {
+ if (conn->connected) {
+ if (conn->connected(conn->arg, conn, err) == ERR_ABRT) {
+ return ERR_ABRT;
+ }
+ return ERR_OK;
+ }
+ }
+ return altcp_mbedtls_lower_recv_process(conn, (altcp_mbedtls_state_t *)conn->state);
+ }
+ return ERR_VAL;
+}
+
+/* Call recved for possibly more than an u16_t */
+static void
+altcp_mbedtls_lower_recved(struct altcp_pcb *inner_conn, int recvd_cnt)
+{
+ while (recvd_cnt > 0) {
+ u16_t recvd_part = (u16_t)LWIP_MIN(recvd_cnt, 0xFFFF);
+ altcp_recved(inner_conn, recvd_part);
+ recvd_cnt -= recvd_part;
+ }
+}
+
+/** Recv callback from lower connection (i.e. TCP)
+ * This one mainly differs between connection setup/handshake (data is fed into mbedTLS only)
+ * and application phase (data is decoded by mbedTLS and passed on to the application).
+ */
+static err_t
+altcp_mbedtls_lower_recv(void *arg, struct altcp_pcb *inner_conn, struct pbuf *p, err_t err)
+{
+ altcp_mbedtls_state_t *state;
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+
+ LWIP_ASSERT("no err expected", err == ERR_OK);
+ LWIP_UNUSED_ARG(err);
+
+ if (!conn) {
+ /* no connection given as arg? should not happen, but prevent pbuf/conn leaks */
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+ altcp_close(inner_conn);
+ return ERR_CLSD;
+ }
+ state = (altcp_mbedtls_state_t *)conn->state;
+ LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+ if (!state) {
+ /* already closed */
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+ altcp_close(inner_conn);
+ return ERR_CLSD;
+ }
+
+ /* handle NULL pbuf (connection closed) */
+ if (p == NULL) {
+ /* remote host sent FIN, remember this (SSL state is destroyed
+ when both sides are closed only!) */
+ state->flags |= ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED;
+ if ((state->flags & (ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE | ALTCP_MBEDTLS_FLAGS_UPPER_CALLED)) ==
+ (ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE | ALTCP_MBEDTLS_FLAGS_UPPER_CALLED)) {
+ /* need to notify upper layer (e.g. 'accept' called or 'connect' succeeded) */
+ if ((state->rx != NULL) || (state->rx_app != NULL)) {
+ /* this is a normal close (FIN) but we have unprocessed data, so delay the FIN */
+ altcp_mbedtls_handle_rx_appldata(conn, state);
+ return ERR_OK;
+ }
+ if (conn->recv) {
+ err_t local_err = conn->recv(conn->arg, conn, NULL, ERR_OK);
+ if (local_err == ERR_ABRT) {
+ return ERR_ABRT;
+ }
+ }
+ } else {
+ /* before connection setup is done: call 'err' */
+ if (conn->err) {
+ conn->err(conn->arg, ERR_CLSD);
+ }
+ }
+ altcp_close(conn);
+ state->flags |= ALTCP_MBEDTLS_FLAGS_RX_CLOSED;
+ if (conn->state && ((state->flags & ALTCP_MBEDTLS_FLAGS_CLOSED) == ALTCP_MBEDTLS_FLAGS_CLOSED)) {
+ altcp_mbedtls_dealloc(conn);
+ }
+ return ERR_OK;
+ }
+
+ /* If we come here, the connection is in good state (handshake phase or application data phase).
+ Queue up the pbuf for processing as handshake data or application data. */
+ if (state->rx == NULL) {
+ state->rx = p;
+ } else {
+ LWIP_ASSERT("rx pbuf overflow", (int)p->tot_len + (int)p->len <= 0xFFFF);
+ pbuf_cat(state->rx, p);
+ }
+ return altcp_mbedtls_lower_recv_process(conn, state);
+}
+
+static err_t
+altcp_mbedtls_lower_recv_process(struct altcp_pcb *conn, altcp_mbedtls_state_t *state)
+{
+ if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+ /* handle connection setup (handshake not done) */
+ int ret = mbedtls_ssl_handshake(&state->ssl_context);
+ /* try to send data... */
+ altcp_output(conn->inner_conn);
+ if (state->bio_bytes_read) {
+ /* acknowledge all bytes read */
+ altcp_mbedtls_lower_recved(conn->inner_conn, state->bio_bytes_read);
+ state->bio_bytes_read = 0;
+ }
+
+ if (ret == MBEDTLS_ERR_SSL_WANT_READ || ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
+ /* handshake not done, wait for more recv calls */
+ LWIP_ASSERT("in this state, the rx chain should be empty", state->rx == NULL);
+ return ERR_OK;
+ }
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_handshake failed: %d\n", ret));
+ /* handshake failed, connection has to be closed */
+ conn->recv(conn->arg, conn, NULL, ERR_OK);
+ if (altcp_close(conn->inner_conn) != ERR_OK) {
+ altcp_abort(conn->inner_conn);
+ }
+ return ERR_OK;
+ }
+ /* If we come here, handshake succeeded. */
+ LWIP_ASSERT("state", state->bio_bytes_read == 0);
+ LWIP_ASSERT("state", state->bio_bytes_appl == 0);
+ state->flags |= ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE;
+ /* issue "connect" callback" to upper connection (this can only happen for active open) */
+ if (conn->connected) {
+ conn->connected(conn->arg, conn, ERR_OK);
+ }
+ if (state->rx == NULL) {
+ return ERR_OK;
+ }
+ }
+ /* handle application data */
+ return altcp_mbedtls_handle_rx_appldata(conn, state);
+}
+
+/* Pass queued decoded rx data to application */
+static err_t
+altcp_mbedtls_pass_rx_data(struct altcp_pcb *conn, altcp_mbedtls_state_t *state)
+{
+ err_t err;
+ struct pbuf *buf;
+ LWIP_ASSERT("conn != NULL", conn != NULL);
+ LWIP_ASSERT("state != NULL", state != NULL);
+ buf = state->rx_app;
+ if (buf) {
+ if (conn->recv) {
+ u16_t tot_len = state->rx_app->tot_len;
+ /* this needs to be increased first because the 'recved' call may come nested */
+ state->rx_passed_unrecved += tot_len;
+ state->flags |= ALTCP_MBEDTLS_FLAGS_UPPER_CALLED;
+ err = conn->recv(conn->arg, conn, state->rx_app, ERR_OK);
+ if (err != ERR_OK) {
+ /* not received, leave the pbuf(s) queued (and decrease 'unrecved' again) */
+ state->rx_passed_unrecved -= tot_len;
+ LWIP_ASSERT("state->rx_passed_unrecved >= 0", state->rx_passed_unrecved >= 0);
+ if (state->rx_passed_unrecved < 0) {
+ state->rx_passed_unrecved = 0;
+ }
+ return err;
+ }
+ } else {
+ pbuf_free(buf);
+ }
+ state->rx_app = NULL;
+ } else if ((state->flags & (ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED | ALTCP_MBEDTLS_FLAGS_RX_CLOSED)) ==
+ ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED) {
+ state->flags |= ALTCP_MBEDTLS_FLAGS_RX_CLOSED;
+ if (conn->recv) {
+ err = conn->recv(conn->arg, conn, NULL, ERR_OK);
+ if (err == ERR_ABRT) {
+ return ERR_ABRT;
+ }
+ }
+ }
+
+ return ERR_OK;
+}
+
+/* Helper function that processes rx application data stored in rx pbuf chain */
+static err_t
+altcp_mbedtls_handle_rx_appldata(struct altcp_pcb *conn, altcp_mbedtls_state_t *state)
+{
+ int ret;
+ LWIP_ASSERT("state != NULL", state != NULL);
+ if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+ /* handshake not done yet */
+ return ERR_VAL;
+ }
+ do {
+ /* allocate a full-sized unchained PBUF_POOL: this is for RX! */
+ struct pbuf *buf = pbuf_alloc(PBUF_RAW, PBUF_POOL_BUFSIZE, PBUF_POOL);
+ if (buf == NULL) {
+ /* We're short on pbufs, try again later from 'poll' or 'recv' callbacks.
+ @todo: close on excessive allocation failures or leave this up to upper conn? */
+ return ERR_OK;
+ }
+
+ /* decrypt application data, this pulls encrypted RX data off state->rx pbuf chain */
+ ret = mbedtls_ssl_read(&state->ssl_context, (unsigned char *)buf->payload, PBUF_POOL_BUFSIZE);
+ if (ret < 0) {
+ if (ret == MBEDTLS_ERR_SSL_CLIENT_RECONNECT) {
+ /* client is initiating a new connection using the same source port -> close connection or make handshake */
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("new connection on same source port\n"));
+ LWIP_ASSERT("TODO: new connection on same source port, close this connection", 0);
+ } else if ((ret != MBEDTLS_ERR_SSL_WANT_READ) && (ret != MBEDTLS_ERR_SSL_WANT_WRITE)) {
+ if (ret == MBEDTLS_ERR_SSL_PEER_CLOSE_NOTIFY) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("connection was closed gracefully\n"));
+ } else if (ret == MBEDTLS_ERR_NET_CONN_RESET) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("connection was reset by peer\n"));
+ }
+ pbuf_free(buf);
+ return ERR_OK;
+ } else {
+ pbuf_free(buf);
+ return ERR_OK;
+ }
+ pbuf_free(buf);
+ altcp_abort(conn);
+ return ERR_ABRT;
+ } else {
+ err_t err;
+ if (ret) {
+ LWIP_ASSERT("bogus receive length", ret <= PBUF_POOL_BUFSIZE);
+ /* trim pool pbuf to actually decoded length */
+ pbuf_realloc(buf, (u16_t)ret);
+
+ state->bio_bytes_appl += ret;
+ if (mbedtls_ssl_get_bytes_avail(&state->ssl_context) == 0) {
+ /* Record is done, now we know the share between application and protocol bytes
+ and can adjust the RX window by the protocol bytes.
+ The rest is 'recved' by the application calling our 'recved' fn. */
+ int overhead_bytes;
+ LWIP_ASSERT("bogus byte counts", state->bio_bytes_read > state->bio_bytes_appl);
+ overhead_bytes = state->bio_bytes_read - state->bio_bytes_appl;
+ altcp_mbedtls_lower_recved(conn->inner_conn, overhead_bytes);
+ state->bio_bytes_read = 0;
+ state->bio_bytes_appl = 0;
+ }
+
+ if (state->rx_app == NULL) {
+ state->rx_app = buf;
+ } else {
+ pbuf_cat(state->rx_app, buf);
+ }
+ } else {
+ pbuf_free(buf);
+ buf = NULL;
+ }
+ err = altcp_mbedtls_pass_rx_data(conn, state);
+ if (err != ERR_OK) {
+ if (err == ERR_ABRT) {
+ /* recv callback needs to return this as the pcb is deallocated */
+ return ERR_ABRT;
+ }
+ /* we hide all other errors as we retry feeding the pbuf to the app later */
+ return ERR_OK;
+ }
+ }
+ } while (ret > 0);
+ return ERR_OK;
+}
+
+/** Receive callback function called from mbedtls (set via mbedtls_ssl_set_bio)
+ * This function mainly copies data from pbufs and frees the pbufs after copying.
+ */
+static int
+altcp_mbedtls_bio_recv(void *ctx, unsigned char *buf, size_t len)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)ctx;
+ altcp_mbedtls_state_t *state;
+ struct pbuf *p;
+ u16_t ret;
+ u16_t copy_len;
+ err_t err;
+
+ if ((conn == NULL) || (conn->state == NULL)) {
+ return MBEDTLS_ERR_NET_INVALID_CONTEXT;
+ }
+ state = (altcp_mbedtls_state_t *)conn->state;
+ p = state->rx;
+
+ /* @todo: return MBEDTLS_ERR_NET_CONN_RESET/MBEDTLS_ERR_NET_RECV_FAILED? */
+
+ if ((p == NULL) || ((p->len == 0) && (p->next == NULL))) {
+ if (p) {
+ pbuf_free(p);
+ }
+ state->rx = NULL;
+ if ((state->flags & (ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED | ALTCP_MBEDTLS_FLAGS_RX_CLOSED)) ==
+ ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED) {
+ /* close queued but not passed up yet */
+ return 0;
+ }
+ return MBEDTLS_ERR_SSL_WANT_READ;
+ }
+ /* limit number of bytes again to copy from first pbuf in a chain only */
+ copy_len = (u16_t)LWIP_MIN(len, p->len);
+ /* copy the data */
+ ret = pbuf_copy_partial(p, buf, copy_len, 0);
+ LWIP_ASSERT("ret == copy_len", ret == copy_len);
+ /* hide the copied bytes from the pbuf */
+ err = pbuf_remove_header(p, ret);
+ LWIP_ASSERT("error", err == ERR_OK);
+ if (p->len == 0) {
+ /* the first pbuf has been fully read, free it */
+ state->rx = p->next;
+ p->next = NULL;
+ pbuf_free(p);
+ }
+
+ state->bio_bytes_read += (int)ret;
+ return ret;
+}
+
+/** Sent callback from lower connection (i.e. TCP)
+ * This only informs the upper layer to try to send more, not about
+ * the number of ACKed bytes.
+ */
+static err_t
+altcp_mbedtls_lower_sent(void *arg, struct altcp_pcb *inner_conn, u16_t len)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ LWIP_UNUSED_ARG(len);
+ if (conn) {
+ altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
+ LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+ if (!state || !(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+ /* @todo: do something here? */
+ return ERR_OK;
+ }
+ /* try to send more if we failed before */
+ mbedtls_ssl_flush_output(&state->ssl_context);
+ /* call upper sent with len==0 if the application already sent data */
+ if ((state->flags & ALTCP_MBEDTLS_FLAGS_APPLDATA_SENT) && conn->sent) {
+ return conn->sent(conn->arg, conn, 0);
+ }
+ }
+ return ERR_OK;
+}
+
+/** Poll callback from lower connection (i.e. TCP)
+ * Just pass this on to the application.
+ * @todo: retry sending?
+ */
+static err_t
+altcp_mbedtls_lower_poll(void *arg, struct altcp_pcb *inner_conn)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ LWIP_ASSERT("pcb mismatch", conn->inner_conn == inner_conn);
+ /* check if there's unreceived rx data */
+ if (conn->state) {
+ altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
+ /* try to send more if we failed before */
+ mbedtls_ssl_flush_output(&state->ssl_context);
+ if (altcp_mbedtls_handle_rx_appldata(conn, state) == ERR_ABRT) {
+ return ERR_ABRT;
+ }
+ }
+ if (conn->poll) {
+ return conn->poll(conn->arg, conn);
+ }
+ }
+ return ERR_OK;
+}
+
+static void
+altcp_mbedtls_lower_err(void *arg, err_t err)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ conn->inner_conn = NULL; /* already freed */
+ if (conn->err) {
+ conn->err(conn->arg, err);
+ }
+ altcp_free(conn);
+ }
+}
+
+/* setup functions */
+static void
+altcp_mbedtls_setup_callbacks(struct altcp_pcb *conn, struct altcp_pcb *inner_conn)
+{
+ altcp_arg(inner_conn, conn);
+ altcp_recv(inner_conn, altcp_mbedtls_lower_recv);
+ altcp_sent(inner_conn, altcp_mbedtls_lower_sent);
+ altcp_err(inner_conn, altcp_mbedtls_lower_err);
+ /* tcp_poll is set when interval is set by application */
+ /* listen is set totally different :-) */
+}
+
+static err_t
+altcp_mbedtls_setup(void *conf, struct altcp_pcb *conn, struct altcp_pcb *inner_conn)
+{
+ int ret;
+ struct altcp_tls_config *config = (struct altcp_tls_config *)conf;
+ altcp_mbedtls_state_t *state;
+ if (!conf) {
+ return ERR_ARG;
+ }
+ /* allocate mbedtls context */
+ state = altcp_mbedtls_alloc(conf);
+ if (state == NULL) {
+ return ERR_MEM;
+ }
+ /* initialize mbedtls context: */
+ mbedtls_ssl_init(&state->ssl_context);
+ ret = mbedtls_ssl_setup(&state->ssl_context, &config->conf);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_setup failed\n"));
+ /* @todo: convert 'ret' to err_t */
+ altcp_mbedtls_free(conf, state);
+ return ERR_MEM;
+ }
+ /* tell mbedtls about our I/O functions */
+ mbedtls_ssl_set_bio(&state->ssl_context, conn, altcp_mbedtls_bio_send, altcp_mbedtls_bio_recv, NULL);
+
+ altcp_mbedtls_setup_callbacks(conn, inner_conn);
+ conn->inner_conn = inner_conn;
+ conn->fns = &altcp_mbedtls_functions;
+ conn->state = state;
+ return ERR_OK;
+}
+
+struct altcp_pcb *
+altcp_tls_new(struct altcp_tls_config *config, struct altcp_pcb *inner_pcb)
+{
+ struct altcp_pcb *ret;
+ if (inner_pcb == NULL) {
+ return NULL;
+ }
+ ret = altcp_alloc();
+ if (ret != NULL) {
+ if (altcp_mbedtls_setup(config, ret, inner_pcb) != ERR_OK) {
+ altcp_free(ret);
+ return NULL;
+ }
+ }
+ return ret;
+}
+
+void *
+altcp_tls_context(struct altcp_pcb *conn)
+{
+ if (conn && conn->state) {
+ altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
+ return &state->ssl_context;
+ }
+ return NULL;
+}
+
+#if ALTCP_MBEDTLS_DEBUG != LWIP_DBG_OFF
+static void
+altcp_mbedtls_debug(void *ctx, int level, const char *file, int line, const char *str)
+{
+ LWIP_UNUSED_ARG(str);
+ LWIP_UNUSED_ARG(level);
+ LWIP_UNUSED_ARG(file);
+ LWIP_UNUSED_ARG(line);
+ LWIP_UNUSED_ARG(ctx);
+ /* @todo: output debug string :-) */
+}
+#endif
+
+#ifndef ALTCP_MBEDTLS_RNG_FN
+/** ATTENTION: It is *really* important to *NOT* use this dummy RNG in production code!!!! */
+static int
+dummy_rng(void *ctx, unsigned char *buffer, size_t len)
+{
+ static size_t ctr;
+ size_t i;
+ LWIP_UNUSED_ARG(ctx);
+ for (i = 0; i < len; i++) {
+ buffer[i] = (unsigned char)++ctr;
+ }
+ return 0;
+}
+#define ALTCP_MBEDTLS_RNG_FN dummy_rng
+#endif /* ALTCP_MBEDTLS_RNG_FN */
+
+/** Create new TLS configuration
+ * ATTENTION: Server certificate and private key have to be added outside this function!
+ */
+static struct altcp_tls_config *
+altcp_tls_create_config(int is_server)
+{
+ int ret;
+ struct altcp_tls_config *conf;
+
+ altcp_mbedtls_mem_init();
+
+ conf = (struct altcp_tls_config *)altcp_mbedtls_alloc_config(sizeof(struct altcp_tls_config));
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ mbedtls_ssl_config_init(&conf->conf);
+ mbedtls_entropy_init(&conf->entropy);
+ mbedtls_ctr_drbg_init(&conf->ctr_drbg);
+
+ /* Seed the RNG */
+ ret = mbedtls_ctr_drbg_seed(&conf->ctr_drbg, ALTCP_MBEDTLS_RNG_FN, &conf->entropy, ALTCP_MBEDTLS_ENTROPY_PTR, ALTCP_MBEDTLS_ENTROPY_LEN);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ctr_drbg_seed failed: %d\n", ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+
+ /* Setup ssl context (@todo: what's different for a client here? -> might better be done on listen/connect) */
+ ret = mbedtls_ssl_config_defaults(&conf->conf, is_server ? MBEDTLS_SSL_IS_SERVER : MBEDTLS_SSL_IS_CLIENT,
+ MBEDTLS_SSL_TRANSPORT_STREAM, MBEDTLS_SSL_PRESET_DEFAULT);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_config_defaults failed: %d\n", ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+ mbedtls_ssl_conf_authmode(&conf->conf, MBEDTLS_SSL_VERIFY_OPTIONAL);
+
+ mbedtls_ssl_conf_rng(&conf->conf, mbedtls_ctr_drbg_random, &conf->ctr_drbg);
+#if ALTCP_MBEDTLS_DEBUG != LWIP_DBG_OFF
+ mbedtls_ssl_conf_dbg(&conf->conf, altcp_mbedtls_debug, stdout);
+#endif
+#if defined(MBEDTLS_SSL_CACHE_C) && ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS
+ mbedtls_ssl_conf_session_cache(&conf->conf, &conf->cache, mbedtls_ssl_cache_get, mbedtls_ssl_cache_set);
+ mbedtls_ssl_cache_set_timeout(&conf->cache, 30);
+ mbedtls_ssl_cache_set_max_entries(&conf->cache, 30);
+#endif
+
+ return conf;
+}
+
+/** Create new TLS configuration
+ * This is a suboptimal version that gets the encrypted private key and its password,
+ * as well as the server certificate.
+ */
+struct altcp_tls_config *
+altcp_tls_create_config_server_privkey_cert(const u8_t *privkey, size_t privkey_len,
+ const u8_t *privkey_pass, size_t privkey_pass_len,
+ const u8_t *cert, size_t cert_len)
+{
+ int ret;
+ static mbedtls_x509_crt srvcert;
+ static mbedtls_pk_context pkey;
+ struct altcp_tls_config *conf = altcp_tls_create_config(1);
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ mbedtls_x509_crt_init(&srvcert);
+ mbedtls_pk_init(&pkey);
+
+ /* Load the certificates and private key */
+ ret = mbedtls_x509_crt_parse(&srvcert, cert, cert_len);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_x509_crt_parse failed: %d\n", ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+
+ ret = mbedtls_pk_parse_key(&pkey, (const unsigned char *) privkey, privkey_len, privkey_pass, privkey_pass_len);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_pk_parse_public_key failed: %d\n", ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+
+ mbedtls_ssl_conf_ca_chain(&conf->conf, srvcert.next, NULL);
+ ret = mbedtls_ssl_conf_own_cert(&conf->conf, &srvcert, &pkey);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_ssl_conf_own_cert failed: %d\n", ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+ return conf;
+}
+
+struct altcp_tls_config *
+altcp_tls_create_config_client(const u8_t *cert, size_t cert_len)
+{
+ int ret;
+ static mbedtls_x509_crt acc_cert;
+ struct altcp_tls_config *conf = altcp_tls_create_config(0);
+ if (conf == NULL) {
+ return NULL;
+ }
+
+ mbedtls_x509_crt_init(&acc_cert);
+
+ /* Load the certificates */
+ ret = mbedtls_x509_crt_parse(&acc_cert, cert, cert_len);
+ if (ret != 0) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("mbedtls_x509_crt_parse failed: %d", ret));
+ altcp_mbedtls_free_config(conf);
+ return NULL;
+ }
+
+ mbedtls_ssl_conf_ca_chain(&conf->conf, &acc_cert, NULL);
+ return conf;
+}
+
+void
+altcp_tls_free_config(struct altcp_tls_config *conf)
+{
+ altcp_mbedtls_free_config(conf);
+}
+
+/* "virtual" functions */
+static void
+altcp_mbedtls_set_poll(struct altcp_pcb *conn, u8_t interval)
+{
+ if (conn != NULL) {
+ altcp_poll(conn->inner_conn, altcp_mbedtls_lower_poll, interval);
+ }
+}
+
+static void
+altcp_mbedtls_recved(struct altcp_pcb *conn, u16_t len)
+{
+ u16_t lower_recved;
+ altcp_mbedtls_state_t *state;
+ if (conn == NULL) {
+ return;
+ }
+ state = (altcp_mbedtls_state_t *)conn->state;
+ if (state == NULL) {
+ return;
+ }
+ if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+ return;
+ }
+ lower_recved = len;
+ if (lower_recved > state->rx_passed_unrecved) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_DEBUG, ("bogus recved count (len > state->rx_passed_unrecved / %d / %d)",
+ len, state->rx_passed_unrecved));
+ lower_recved = (u16_t)state->rx_passed_unrecved;
+ }
+ state->rx_passed_unrecved -= lower_recved;
+
+ altcp_recved(conn->inner_conn, lower_recved);
+}
+
+static err_t
+altcp_mbedtls_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
+{
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ conn->connected = connected;
+ return altcp_connect(conn->inner_conn, ipaddr, port, altcp_mbedtls_lower_connected);
+}
+
+static struct altcp_pcb *
+altcp_mbedtls_listen(struct altcp_pcb *conn, u8_t backlog, err_t *err)
+{
+ struct altcp_pcb *lpcb;
+ if (conn == NULL) {
+ return NULL;
+ }
+ lpcb = altcp_listen_with_backlog_and_err(conn->inner_conn, backlog, err);
+ if (lpcb != NULL) {
+ conn->inner_conn = lpcb;
+ altcp_accept(lpcb, altcp_mbedtls_lower_accept);
+ return conn;
+ }
+ return NULL;
+}
+
+static void
+altcp_mbedtls_abort(struct altcp_pcb *conn)
+{
+ if (conn != NULL) {
+ altcp_abort(conn->inner_conn);
+ }
+}
+
+static err_t
+altcp_mbedtls_close(struct altcp_pcb *conn)
+{
+ altcp_mbedtls_state_t *state;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ state = (altcp_mbedtls_state_t *)conn->state;
+ if (state != NULL) {
+ state->flags |= ALTCP_MBEDTLS_FLAGS_TX_CLOSED;
+ if (state->flags & ALTCP_MBEDTLS_FLAGS_RX_CLOSED) {
+ altcp_mbedtls_dealloc(conn);
+ }
+ }
+ return altcp_close(conn->inner_conn);
+}
+
+/** Write data to a TLS connection. Calls into mbedTLS, which in turn calls into
+ * @ref altcp_mbedtls_bio_send() to send the encrypted data
+ */
+static err_t
+altcp_mbedtls_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+ int ret;
+ altcp_mbedtls_state_t *state;
+
+ LWIP_UNUSED_ARG(apiflags);
+
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+
+ state = (altcp_mbedtls_state_t *)conn->state;
+ if (state == NULL) {
+ /* @todo: which error? */
+ return ERR_CLSD;
+ }
+ if (!(state->flags & ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE)) {
+ /* @todo: which error? */
+ return ERR_VAL;
+ }
+
+ /* HACK: if thre is something left to send, try to flush it and only
+ allow sending more if this succeeded (this is a hack because neither
+ returning 0 nor MBEDTLS_ERR_SSL_WANT_WRITE worked for me) */
+ if (state->ssl_context.out_left) {
+ mbedtls_ssl_flush_output(&state->ssl_context);
+ if (state->ssl_context.out_left) {
+ return ERR_MEM;
+ }
+ }
+ ret = mbedtls_ssl_write(&state->ssl_context, (const unsigned char *)dataptr, len);
+ /* try to send data... */
+ altcp_output(conn->inner_conn);
+ if (ret >= 0) {
+ if (ret == len) {
+ state->flags |= ALTCP_MBEDTLS_FLAGS_APPLDATA_SENT;
+ return ERR_OK;
+ } else {
+ /* @todo/@fixme: assumption: either everything sent or error */
+ LWIP_ASSERT("ret <= 0", 0);
+ return ERR_MEM;
+ }
+ } else {
+ if (ret == MBEDTLS_ERR_SSL_WANT_WRITE) {
+ /* @todo: convert error to err_t */
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("unhandled error", 0);
+ return ERR_VAL;
+ }
+}
+
+/** Send callback function called from mbedtls (set via mbedtls_ssl_set_bio)
+ * This function is either called during handshake or when sending application
+ * data via @ref altcp_mbedtls_write (or altcp_write)
+ */
+static int
+altcp_mbedtls_bio_send(void *ctx, const unsigned char *dataptr, size_t size)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *) ctx;
+ int written = 0;
+ size_t size_left = size;
+ u8_t apiflags = TCP_WRITE_FLAG_COPY;
+
+ LWIP_ASSERT("conn != NULL", conn != NULL);
+ if ((conn == NULL) || (conn->inner_conn == NULL)) {
+ return MBEDTLS_ERR_NET_INVALID_CONTEXT;
+ }
+
+ while (size_left) {
+ u16_t write_len = (u16_t)LWIP_MIN(size_left, 0xFFFF);
+ err_t err = altcp_write(conn->inner_conn, (const void *)dataptr, write_len, apiflags);
+ if (err == ERR_OK) {
+ written += write_len;
+ size_left -= write_len;
+ } else if (err == ERR_MEM) {
+ if (written) {
+ return written;
+ }
+ return 0; /* MBEDTLS_ERR_SSL_WANT_WRITE; */
+ } else {
+ LWIP_ASSERT("tls_write, tcp_write: err != ERR MEM", 0);
+ /* @todo: return MBEDTLS_ERR_NET_CONN_RESET or MBEDTLS_ERR_NET_SEND_FAILED */
+ return MBEDTLS_ERR_NET_SEND_FAILED;
+ }
+ }
+ return written;
+}
+
+static u16_t
+altcp_mbedtls_mss(struct altcp_pcb *conn)
+{
+ if (conn == NULL) {
+ return 0;
+ }
+ /* @todo: LWIP_MIN(mss, mbedtls_ssl_get_max_frag_len()) ? */
+ return altcp_mss(conn->inner_conn);
+}
+
+static void
+altcp_mbedtls_dealloc(struct altcp_pcb *conn)
+{
+ /* clean up and free tls state */
+ if (conn) {
+ altcp_mbedtls_state_t *state = (altcp_mbedtls_state_t *)conn->state;
+ if (state) {
+ mbedtls_ssl_free(&state->ssl_context);
+ state->flags = 0;
+ altcp_mbedtls_free(state->conf, state);
+ conn->state = NULL;
+ }
+ if (conn->inner_conn) {
+ altcp_free(conn->inner_conn);
+ conn->inner_conn = NULL;
+ }
+ }
+}
+
+const struct altcp_functions altcp_mbedtls_functions = {
+ altcp_mbedtls_set_poll,
+ altcp_mbedtls_recved,
+ altcp_default_bind,
+ altcp_mbedtls_connect,
+ altcp_mbedtls_listen,
+ altcp_mbedtls_abort,
+ altcp_mbedtls_close,
+ altcp_default_shutdown,
+ altcp_mbedtls_write,
+ altcp_default_output,
+ altcp_mbedtls_mss,
+ altcp_default_sndbuf,
+ altcp_default_sndqueuelen,
+ altcp_default_nagle_disable,
+ altcp_default_nagle_enable,
+ altcp_default_nagle_disabled,
+ altcp_default_setprio,
+ altcp_mbedtls_dealloc,
+ altcp_default_get_tcp_addrinfo,
+ altcp_default_get_ip,
+ altcp_default_get_port
+#ifdef LWIP_DEBUG
+ , altcp_default_dbg_get_tcp_state
+#endif
+};
+
+#endif /* LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS */
+#endif /* LWIP_ALTCP */
diff --git a/lwip/src/apps/altcp_tls/altcp_tls_mbedtls_mem.c b/lwip/src/apps/altcp_tls/altcp_tls_mbedtls_mem.c
new file mode 100644
index 0000000..691232c
--- /dev/null
+++ b/lwip/src/apps/altcp_tls/altcp_tls_mbedtls_mem.c
@@ -0,0 +1,212 @@
+/**
+ * @file
+ * Application layered TCP connection API (to be used from TCPIP thread)
+ *
+ * This file contains memory management functions for a TLS layer using mbedTLS.
+ *
+ * ATTENTION: For production usage, you might want to override this file with
+ * your own implementation since this implementation simply uses the
+ * lwIP heap without caring for fragmentation or leaving heap for
+ * other parts of lwIP!
+ */
+
+/*
+ * Copyright (c) 2017 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 <goldsimon@gmx.de>
+ *
+ * Missing things / @todo:
+ * - RX data is acknowledged after receiving (tcp_recved is called when enqueueing
+ * the pbuf for mbedTLS receive, not when processed by mbedTLS or the inner
+ * connection; altcp_recved() from inner connection does nothing)
+ * - TX data is marked as 'sent' (i.e. acknowledged; sent callback is called) right
+ * after enqueueing for transmission, not when actually ACKed be the remote host.
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/altcp_tls_mbedtls_opts.h"
+
+#if LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS
+
+#include "altcp_tls_mbedtls_mem.h"
+#include "altcp_tls_mbedtls_structs.h"
+#include "lwip/mem.h"
+
+#include "mbedtls/platform.h"
+
+#include <string.h>
+
+#ifndef ALTCP_MBEDTLS_MEM_DEBUG
+#define ALTCP_MBEDTLS_MEM_DEBUG LWIP_DBG_OFF
+#endif
+
+#if defined(MBEDTLS_PLATFORM_MEMORY) && \
+ (!defined(MBEDTLS_PLATFORM_FREE_MACRO) || \
+ defined(MBEDTLS_PLATFORM_CALLOC_MACRO))
+#define ALTCP_MBEDTLS_PLATFORM_ALLOC 1
+#else
+#define ALTCP_MBEDTLS_PLATFORM_ALLOC 0
+#endif
+
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC
+
+#ifndef ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+#define ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS 0
+#endif
+
+/* This is an example/debug implementation of alloc/free functions only */
+typedef struct altcp_mbedtls_malloc_helper_s {
+ size_t c;
+ size_t len;
+} altcp_mbedtls_malloc_helper_t;
+
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+typedef struct altcp_mbedtls_malloc_stats_s {
+ size_t allocedBytes;
+ size_t allocCnt;
+ size_t maxBytes;
+ size_t totalBytes;
+} altcp_mbedtls_malloc_stats_t;
+altcp_mbedtls_malloc_stats_t altcp_mbedtls_malloc_stats;
+volatile int altcp_mbedtls_malloc_clear_stats;
+#endif
+
+static void *
+tls_malloc(size_t c, size_t len)
+{
+ altcp_mbedtls_malloc_helper_t *hlpr;
+ void *ret;
+ size_t alloc_size;
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+ if (altcp_mbedtls_malloc_clear_stats) {
+ if (altcp_mbedtls_malloc_clear_stats) {
+ altcp_mbedtls_malloc_clear_stats = 0;
+ memset(&altcp_mbedtls_malloc_stats, 0, sizeof(altcp_mbedtls_malloc_stats));
+ }
+ }
+#endif
+ alloc_size = sizeof(altcp_mbedtls_malloc_helper_t) + (c * len);
+ /* check for maximum allocation size, mainly to prevent mem_size_t overflow */
+ if (alloc_size > MEM_SIZE) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_MEM_DEBUG, ("mbedtls allocation too big: %c * %d bytes vs MEM_SIZE=%d",
+ (int)c, (int)len, (int)MEM_SIZE));
+ return NULL;
+ }
+ hlpr = (altcp_mbedtls_malloc_helper_t *)mem_malloc((mem_size_t)alloc_size);
+ if (hlpr == NULL) {
+ LWIP_DEBUGF(ALTCP_MBEDTLS_MEM_DEBUG, ("mbedtls alloc callback failed for %c * %d bytes", (int)c, (int)len));
+ return NULL;
+ }
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+ altcp_mbedtls_malloc_stats.allocCnt++;
+ altcp_mbedtls_malloc_stats.allocedBytes += c * len;
+ if (altcp_mbedtls_malloc_stats.allocedBytes > altcp_mbedtls_malloc_stats.maxBytes) {
+ altcp_mbedtls_malloc_stats.maxBytes = altcp_mbedtls_malloc_stats.allocedBytes;
+ }
+ altcp_mbedtls_malloc_stats.totalBytes += c * len;
+#endif
+ hlpr->c = c;
+ hlpr->len = len;
+ ret = hlpr + 1;
+ /* zeroing the allocated chunk is required by mbedTLS! */
+ memset(ret, 0, c * len);
+ return ret;
+}
+
+static void
+tls_free(void *ptr)
+{
+ altcp_mbedtls_malloc_helper_t *hlpr;
+ if (ptr == NULL) {
+ /* this obviously happened in mbedtls... */
+ return;
+ }
+ hlpr = ((altcp_mbedtls_malloc_helper_t *)ptr) - 1;
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC_STATS
+ if (!altcp_mbedtls_malloc_clear_stats) {
+ altcp_mbedtls_malloc_stats.allocedBytes -= hlpr->c * hlpr->len;
+ }
+#endif
+ mem_free(hlpr);
+}
+#endif /* ALTCP_MBEDTLS_PLATFORM_ALLOC*/
+
+void
+altcp_mbedtls_mem_init(void)
+{
+ /* not much to do here when using the heap */
+
+#if ALTCP_MBEDTLS_PLATFORM_ALLOC
+ /* set mbedtls allocation methods */
+ mbedtls_platform_set_calloc_free(&tls_malloc, &tls_free);
+#endif
+}
+
+altcp_mbedtls_state_t *
+altcp_mbedtls_alloc(void *conf)
+{
+ altcp_mbedtls_state_t *ret = (altcp_mbedtls_state_t *)mem_calloc(1, sizeof(altcp_mbedtls_state_t));
+ if (ret != NULL) {
+ ret->conf = conf;
+ }
+ return ret;
+}
+
+void
+altcp_mbedtls_free(void *conf, altcp_mbedtls_state_t *state)
+{
+ LWIP_UNUSED_ARG(conf);
+ LWIP_ASSERT("state != NULL", state != NULL);
+ mem_free(state);
+}
+
+void *
+altcp_mbedtls_alloc_config(size_t size)
+{
+ void *ret;
+ size_t checked_size = (mem_size_t)size;
+ if (size != checked_size) {
+ /* allocation too big (mem_size_t overflow) */
+ return NULL;
+ }
+ ret = (altcp_mbedtls_state_t *)mem_calloc(1, (mem_size_t)size);
+ return ret;
+}
+
+void
+altcp_mbedtls_free_config(void *item)
+{
+ LWIP_ASSERT("item != NULL", item != NULL);
+ mem_free(item);
+}
+
+#endif /* LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS */
+#endif /* LWIP_ALTCP */
diff --git a/lwip/src/apps/altcp_tls/altcp_tls_mbedtls_mem.h b/lwip/src/apps/altcp_tls/altcp_tls_mbedtls_mem.h
new file mode 100644
index 0000000..9ccde64
--- /dev/null
+++ b/lwip/src/apps/altcp_tls/altcp_tls_mbedtls_mem.h
@@ -0,0 +1,72 @@
+/**
+ * @file
+ * Application layered TCP/TLS connection API (to be used from TCPIP thread)
+ *
+ * This file contains memory management function prototypes for a TLS layer using mbedTLS.
+ *
+ * Memory management contains:
+ * - allocating/freeing altcp_mbedtls_state_t
+ * - allocating/freeing memory used in the mbedTLS library
+ */
+
+/*
+ * Copyright (c) 2017 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 <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_MBEDTLS_MEM_H
+#define LWIP_HDR_ALTCP_MBEDTLS_MEM_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/altcp_tls_mbedtls_opts.h"
+
+#if LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS
+
+#include "altcp_tls_mbedtls_structs.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void altcp_mbedtls_mem_init(void);
+altcp_mbedtls_state_t *altcp_mbedtls_alloc(void *conf);
+void altcp_mbedtls_free(void *conf, altcp_mbedtls_state_t *state);
+void *altcp_mbedtls_alloc_config(size_t size);
+void altcp_mbedtls_free_config(void *item);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS */
+#endif /* LWIP_ALTCP */
+#endif /* LWIP_HDR_ALTCP_TLS_H */
diff --git a/lwip/src/apps/altcp_tls/altcp_tls_mbedtls_structs.h b/lwip/src/apps/altcp_tls/altcp_tls_mbedtls_structs.h
new file mode 100644
index 0000000..5b401c3
--- /dev/null
+++ b/lwip/src/apps/altcp_tls/altcp_tls_mbedtls_structs.h
@@ -0,0 +1,85 @@
+/**
+ * @file
+ * Application layered TCP/TLS connection API (to be used from TCPIP thread)
+ *
+ * This file contains structure definitions for a TLS layer using mbedTLS.
+ */
+
+/*
+ * Copyright (c) 2017 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 <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_MBEDTLS_STRUCTS_H
+#define LWIP_HDR_ALTCP_MBEDTLS_STRUCTS_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/altcp_tls_mbedtls_opts.h"
+
+#if LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS
+
+#include "lwip/altcp.h"
+#include "lwip/pbuf.h"
+
+#include "mbedtls/ssl.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ALTCP_MBEDTLS_FLAGS_HANDSHAKE_DONE 0x01
+#define ALTCP_MBEDTLS_FLAGS_APPLDATA_SENT 0x02
+#define ALTCP_MBEDTLS_FLAGS_RX_CLOSE_QUEUED 0x04
+#define ALTCP_MBEDTLS_FLAGS_RX_CLOSED 0x08
+#define ALTCP_MBEDTLS_FLAGS_TX_CLOSED 0x10
+#define ALTCP_MBEDTLS_FLAGS_CLOSED (ALTCP_MBEDTLS_FLAGS_RX_CLOSED|ALTCP_MBEDTLS_FLAGS_TX_CLOSED)
+#define ALTCP_MBEDTLS_FLAGS_UPPER_CALLED 0x20
+
+typedef struct altcp_mbedtls_state_s {
+ void *conf;
+ mbedtls_ssl_context ssl_context;
+ /* chain of rx pbufs (before decryption) */
+ struct pbuf *rx;
+ struct pbuf *rx_app;
+ u8_t flags;
+ int rx_passed_unrecved;
+ int bio_bytes_read;
+ int bio_bytes_appl;
+} altcp_mbedtls_state_t;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_ALTCP_TLS && LWIP_ALTCP_TLS_MBEDTLS */
+#endif /* LWIP_ALTCP */
+#endif /* LWIP_HDR_ALTCP_MBEDTLS_STRUCTS_H */
diff --git a/lwip/src/apps/httpd/fs.c b/lwip/src/apps/httpd/fs.c
new file mode 100644
index 0000000..f15a480
--- /dev/null
+++ b/lwip/src/apps/httpd/fs.c
@@ -0,0 +1,174 @@
+/*
+ * 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 <string.h>
+
+
+#include HTTPD_FSDATA_FILE
+
+/*-----------------------------------------------------------------------------------*/
+
+#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/lwip/src/apps/httpd/fs/404.html b/lwip/src/apps/httpd/fs/404.html
new file mode 100644
index 0000000..40b343a
--- /dev/null
+++ b/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/lwip/src/apps/httpd/fs/img/sics.gif b/lwip/src/apps/httpd/fs/img/sics.gif
new file mode 100644
index 0000000..0a4fc7b
--- /dev/null
+++ b/lwip/src/apps/httpd/fs/img/sics.gif
Binary files differ
diff --git a/lwip/src/apps/httpd/fs/index.html b/lwip/src/apps/httpd/fs/index.html
new file mode 100644
index 0000000..ab575ef
--- /dev/null
+++ b/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/lwip/src/apps/httpd/fsdata.c b/lwip/src/apps/httpd/fsdata.c
new file mode 100644
index 0000000..ab9da7e
--- /dev/null
+++ b/lwip/src/apps/httpd/fsdata.c
@@ -0,0 +1,337 @@
+#include "lwip/apps/fs.h"
+#include "lwip/def.h"
+
+
+#define file_NULL (struct fsdata_file *) NULL
+
+
+#ifndef FS_FILE_FLAGS_HEADER_INCLUDED
+#define FS_FILE_FLAGS_HEADER_INCLUDED 1
+#endif
+#ifndef FS_FILE_FLAGS_HEADER_PERSISTENT
+#define FS_FILE_FLAGS_HEADER_PERSISTENT 0
+#endif
+/* FSDATA_FILE_ALIGNMENT: 0=off, 1=by variable, 2=by include */
+#ifndef FSDATA_FILE_ALIGNMENT
+#define FSDATA_FILE_ALIGNMENT 0
+#endif
+#ifndef FSDATA_ALIGN_PRE
+#define FSDATA_ALIGN_PRE
+#endif
+#ifndef FSDATA_ALIGN_POST
+#define FSDATA_ALIGN_POST
+#endif
+#if FSDATA_FILE_ALIGNMENT==2
+#include "fsdata_alignment.h"
+#endif
+#if FSDATA_FILE_ALIGNMENT==1
+static const unsigned int dummy_align__img_sics_gif = 0;
+#endif
+static const unsigned char FSDATA_ALIGN_PRE data__img_sics_gif[] FSDATA_ALIGN_POST = {
+/* /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/2.0.3d (http://savannah.nongnu.org/projects/lwip)
+" (64 bytes) */
+0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x32,0x2e,0x30,
+0x2e,0x33,0x64,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-Length: 724
+" (18+ bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20,
+0x37,0x32,0x34,0x0d,0x0a,
+/* "Content-Type: image/gif
+
+" (27 bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,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,};
+
+#if FSDATA_FILE_ALIGNMENT==1
+static const unsigned int dummy_align__404_html = 1;
+#endif
+static const unsigned char FSDATA_ALIGN_PRE data__404_html[] FSDATA_ALIGN_POST = {
+/* /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/2.0.3d (http://savannah.nongnu.org/projects/lwip)
+" (64 bytes) */
+0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x32,0x2e,0x30,
+0x2e,0x33,0x64,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-Length: 565
+" (18+ bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20,
+0x35,0x36,0x35,0x0d,0x0a,
+/* "Content-Type: text/html
+
+" (27 bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,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,};
+
+#if FSDATA_FILE_ALIGNMENT==1
+static const unsigned int dummy_align__index_html = 2;
+#endif
+static const unsigned char FSDATA_ALIGN_PRE data__index_html[] FSDATA_ALIGN_POST = {
+/* /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/2.0.3d (http://savannah.nongnu.org/projects/lwip)
+" (64 bytes) */
+0x53,0x65,0x72,0x76,0x65,0x72,0x3a,0x20,0x6c,0x77,0x49,0x50,0x2f,0x32,0x2e,0x30,
+0x2e,0x33,0x64,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-Length: 1751
+" (18+ bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x4c,0x65,0x6e,0x67,0x74,0x68,0x3a,0x20,
+0x31,0x37,0x35,0x31,0x0d,0x0a,
+/* "Content-Type: text/html
+
+" (27 bytes) */
+0x43,0x6f,0x6e,0x74,0x65,0x6e,0x74,0x2d,0x54,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,
+FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT,
+}};
+
+const struct fsdata_file file__404_html[] = { {
+file__img_sics_gif,
+data__404_html,
+data__404_html + 12,
+sizeof(data__404_html) - 12,
+FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT,
+}};
+
+const struct fsdata_file file__index_html[] = { {
+file__404_html,
+data__index_html,
+data__index_html + 12,
+sizeof(data__index_html) - 12,
+FS_FILE_FLAGS_HEADER_INCLUDED | FS_FILE_FLAGS_HEADER_PERSISTENT,
+}};
+
+#define FS_ROOT file__index_html
+#define FS_NUMFILES 3
+
diff --git a/lwip/src/apps/httpd/fsdata.h b/lwip/src/apps/httpd/fsdata.h
new file mode 100644
index 0000000..d31550d
--- /dev/null
+++ b/lwip/src/apps/httpd/fsdata.h
@@ -0,0 +1,41 @@
+/*
+ * 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"
+
+/* THIS FILE IS DEPRECATED AND WILL BE REMOVED IN THE FUTURE */
+/* content was moved to fs.h to simplify #include structure */
+
+#endif /* LWIP_FSDATA_H */
diff --git a/lwip/src/apps/httpd/httpd.c b/lwip/src/apps/httpd/httpd.c
new file mode 100644
index 0000000..b3fd569
--- /dev/null
+++ b/lwip/src/apps/httpd/httpd.c
@@ -0,0 +1,2672 @@
+/**
+ * @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/altcp.h"
+#include "lwip/altcp_tcp.h"
+#if HTTPD_ENABLE_HTTPS
+#include "lwip/altcp_tls.h"
+#endif
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+
+#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
+
+#if LWIP_HTTPD_DYNAMIC_FILE_READ
+#define HTTP_IS_DYNAMIC_FILE(hs) ((hs)->buf != NULL)
+#else
+#define HTTP_IS_DYNAMIC_FILE(hs) 0
+#endif
+
+/* This defines checks whether tcp_write has to copy data or not */
+
+#ifndef HTTP_IS_DATA_VOLATILE
+/** tcp_write does not have to copy data when sent from rom-file-system directly */
+#define HTTP_IS_DATA_VOLATILE(hs) (HTTP_IS_DYNAMIC_FILE(hs) ? TCP_WRITE_FLAG_COPY : 0)
+#endif
+/** Default: dynamic headers are sent from ROM (non-dynamic headers are handled like file data) */
+#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 altcp_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 altcp_pcb *pcb, struct http_state *hs);
+static err_t http_close_or_abort_conn(struct altcp_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 altcp_pcb *pcb);
+static u8_t http_check_eof(struct altcp_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 altcp_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 altcp_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 = altcp_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 to send %d bytes\n", len));
+ err = altcp_write(pcb, ptr, len, apiflags);
+ if (err == ERR_MEM) {
+ if ((altcp_sndbuf(pcb) == 0) ||
+ (altcp_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 */
+ altcp_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 altcp_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*/
+
+
+ altcp_arg(pcb, NULL);
+ altcp_recv(pcb, NULL);
+ altcp_err(pcb, NULL);
+ altcp_poll(pcb, NULL, 0);
+ altcp_sent(pcb, NULL);
+ if (hs != NULL) {
+ http_state_free(hs);
+ }
+
+ if (abort_conn) {
+ altcp_abort(pcb);
+ return ERR_OK;
+ }
+ err = altcp_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 */
+ altcp_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 altcp_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 altcp_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: */
+ altcp_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, 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 altcp_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 = altcp_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 altcp_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 = altcp_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 altcp_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 altcp_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 = altcp_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 (altcp_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->tag_state == TAG_SENDING) || 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 += (u16_t)(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 += (u16_t)(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)) {
+#if LWIP_HTTPD_DYNAMIC_FILE_READ && !LWIP_HTTPD_SSI_INCLUDE_TAG
+ if ((ssi->tag_state != TAG_NONE) && (ssi->tag_started > ssi->tag_end)) {
+ /* If we found tag on the edge of the read buffer: just throw away the first part
+ (we have copied/saved everything required for parsing on later). */
+ len = (u16_t)(ssi->tag_started - hs->file);
+ hs->left -= (ssi->parsed - ssi->tag_started);
+ ssi->parsed = ssi->tag_started;
+ ssi->tag_started = hs->buf;
+ } else
+#endif /* LWIP_HTTPD_DYNAMIC_FILE_READ && !LWIP_HTTPD_SSI_INCLUDE_TAG */
+ {
+ 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 altcp_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_remove_header(q, 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) {
+ altcp_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"));
+ altcp_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 altcp_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 altcp_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 = (u32_t)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) {
+ int 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 altcp_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 altcp_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(altcp_dbg_get_tcp_state(pcb))));
+
+ 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) {
+ altcp_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"));
+ altcp_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 altcp_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. */
+ altcp_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. */
+ altcp_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 altcp_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 */
+ altcp_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. */
+ altcp_arg(pcb, hs);
+
+ /* Set up the various callback functions */
+ altcp_recv(pcb, http_recv);
+ altcp_err(pcb, http_err);
+ altcp_poll(pcb, http_poll, HTTPD_POLL_INTERVAL);
+ altcp_sent(pcb, http_sent);
+
+ return ERR_OK;
+}
+
+static void
+httpd_init_pcb(struct altcp_pcb *pcb, u16_t port)
+{
+ err_t err;
+
+ if (pcb) {
+ altcp_setprio(pcb, HTTPD_TCP_PRIO);
+ /* set SOF_REUSEADDR here to explicitly bind httpd to multiple interfaces */
+ err = altcp_bind(pcb, IP_ANY_TYPE, port);
+ LWIP_UNUSED_ARG(err); /* in case of LWIP_NOASSERT */
+ LWIP_ASSERT("httpd_init: tcp_bind failed", err == ERR_OK);
+ pcb = altcp_listen(pcb);
+ LWIP_ASSERT("httpd_init: tcp_listen failed", pcb != NULL);
+ altcp_accept(pcb, http_accept);
+ }
+}
+
+/**
+ * @ingroup httpd
+ * Initialize the httpd: set up a listening PCB and bind it to the defined port
+ */
+void
+httpd_init(void)
+{
+ struct altcp_pcb *pcb;
+
+#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 = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY);
+ LWIP_ASSERT("httpd_init: tcp_new failed", pcb != NULL);
+ httpd_init_pcb(pcb, HTTPD_SERVER_PORT);
+}
+
+#if HTTPD_ENABLE_HTTPS
+/**
+ * @ingroup httpd
+ * Initialize the httpd: set up a listening PCB and bind it to the defined port.
+ * Also set up TLS connection handling (HTTPS).
+ */
+void
+httpd_inits(struct altcp_tls_config *conf)
+{
+#if LWIP_ALTCP_TLS
+ struct altcp_pcb *pcb_tls;
+ struct altcp_pcb *pcb_tcp = altcp_tcp_new_ip_type(IPADDR_TYPE_ANY);
+ LWIP_ASSERT("httpd_init: tcp_new failed", pcb_tcp != NULL);
+ pcb_tls = altcp_tls_new(conf, pcb_tcp);
+ LWIP_ASSERT("httpd_init: altcp_tls_new failed", pcb_tls != NULL);
+ httpd_init_pcb(pcb_tls, HTTPD_SERVER_PORT_HTTPS);
+#else /* LWIP_ALTCP_TLS */
+ LWIP_UNUSED_ARG(conf);
+#endif /* LWIP_ALTCP_TLS */
+}
+#endif /* HTTPD_ENABLE_HTTPS */
+
+#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/lwip/src/apps/httpd/httpd_structs.h b/lwip/src/apps/httpd/httpd_structs.h
new file mode 100644
index 0000000..0dfee77
--- /dev/null
+++ b/lwip/src/apps/httpd/httpd_structs.h
@@ -0,0 +1,123 @@
+#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_CONTENT_TYPE(contenttype) "Content-Type: "contenttype"\r\n\r\n"
+#define HTTP_CONTENT_TYPE_ENCODING(contenttype, encoding) "Content-Type: "contenttype"\r\nContent-Encoding: "encoding"\r\n\r\n"
+
+#define HTTP_HDR_HTML HTTP_CONTENT_TYPE("text/html")
+#define HTTP_HDR_SSI HTTP_CONTENT_TYPE("text/html\r\nExpires: Fri, 10 Apr 2008 14:00:00 GMT\r\nPragma: no-cache")
+#define HTTP_HDR_GIF HTTP_CONTENT_TYPE("image/gif")
+#define HTTP_HDR_PNG HTTP_CONTENT_TYPE("image/png")
+#define HTTP_HDR_JPG HTTP_CONTENT_TYPE("image/jpeg")
+#define HTTP_HDR_BMP HTTP_CONTENT_TYPE("image/bmp")
+#define HTTP_HDR_ICO HTTP_CONTENT_TYPE("image/x-icon")
+#define HTTP_HDR_APP HTTP_CONTENT_TYPE("application/octet-stream")
+#define HTTP_HDR_JS HTTP_CONTENT_TYPE("application/javascript")
+#define HTTP_HDR_RA HTTP_CONTENT_TYPE("application/javascript")
+#define HTTP_HDR_CSS HTTP_CONTENT_TYPE("text/css")
+#define HTTP_HDR_SWF HTTP_CONTENT_TYPE("application/x-shockwave-flash")
+#define HTTP_HDR_XML HTTP_CONTENT_TYPE("text/xml")
+#define HTTP_HDR_PDF HTTP_CONTENT_TYPE("application/pdf")
+#define HTTP_HDR_JSON HTTP_CONTENT_TYPE("application/json")
+#define HTTP_HDR_CSV HTTP_CONTENT_TYPE("text/csv")
+#define HTTP_HDR_TSV HTTP_CONTENT_TYPE("text/tsv")
+#define HTTP_HDR_SVG HTTP_CONTENT_TYPE("image/svg+xml")
+#define HTTP_HDR_SVGZ HTTP_CONTENT_TYPE_ENCODING("image/svg+xml", "gzip")
+
+#define HTTP_HDR_DEFAULT_TYPE HTTP_CONTENT_TYPE("text/plain")
+
+/** 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},
+#ifdef HTTPD_ADDITIONAL_CONTENT_TYPES
+ /* If you need to add content types not listed here:
+ * #define HTTPD_ADDITIONAL_CONTENT_TYPES {"ct1", HTTP_CONTENT_TYPE("text/ct1")}, {"exe", HTTP_CONTENT_TYPE("application/exe")}
+ */
+ , HTTPD_ADDITIONAL_CONTENT_TYPES
+#endif
+};
+
+#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/lwip/src/apps/httpd/makefsdata/makefsdata b/lwip/src/apps/httpd/makefsdata/makefsdata
new file mode 100644
index 0000000..37b4203
--- /dev/null
+++ b/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/lwip/src/apps/httpd/makefsdata/makefsdata.c b/lwip/src/apps/httpd/makefsdata/makefsdata.c
new file mode 100644
index 0000000..1efe89d
--- /dev/null
+++ b/lwip/src/apps/httpd/makefsdata/makefsdata.c
@@ -0,0 +1,1047 @@
+/**
+ * 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>
+#include <string.h>
+#include <time.h>
+#include <sys/stat.h>
+
+#include "tinydir.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 */
+
+#ifdef WIN32
+
+#define GETCWD(path, len) GetCurrentDirectoryA(len, path)
+#define CHDIR(path) SetCurrentDirectoryA(path)
+#define CHDIR_SUCCEEDED(ret) (ret == TRUE)
+
+#elif __linux__
+
+#define GETCWD(path, len) getcwd(path, len)
+#define CHDIR(path) chdir(path)
+#define CHDIR_SUCCEEDED(ret) (ret == 0)
+
+#else
+
+#error makefsdata not supported on this platform
+
+#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);
+
+ LWIP_ASSERT("sizeof(hdr_buf) must fit into an u16_t", sizeof(hdr_buf) <= 0xffff);
+
+ 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"));
+
+ curSubdir[0] = '\0'; /* 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 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)
+{
+ tinydir_dir dir;
+ int filesProcessed = 0;
+
+ if (processSubs) {
+ /* process subs recursively */
+ size_t sublen = strlen(curSubdir);
+ size_t freelen = sizeof(curSubdir) - sublen - 1;
+ int ret;
+ LWIP_ASSERT("sublen < sizeof(curSubdir)", sublen < sizeof(curSubdir));
+
+ ret = tinydir_open_sorted(&dir, TINYDIR_STRING("."));
+
+ if (ret == 0) {
+ unsigned int i;
+ for (i = 0; i < dir.n_files; i++) {
+ tinydir_file file;
+
+ ret = tinydir_readfile_n(&dir, &file, i);
+
+ if (ret == 0) {
+#if (defined _MSC_VER || defined __MINGW32__)
+ size_t i;
+ char currName[256];
+ wcstombs_s(&i, currName, sizeof(currName), file.name, sizeof(currName));
+#else
+ const char *currName = file.name;
+#endif
+
+ if (currName[0] == '.') {
+ continue;
+ }
+ if (!file.is_dir) {
+ continue;
+ }
+ if (freelen > 0) {
+ CHDIR(currName);
+ strncat(curSubdir, "/", freelen);
+ strncat(curSubdir, currName, 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, currName);
+ }
+ }
+ }
+ }
+
+ ret = tinydir_open_sorted(&dir, TINYDIR_STRING("."));
+ if (ret == 0) {
+ unsigned int i;
+ for (i = 0; i < dir.n_files; i++) {
+ tinydir_file file;
+
+ ret = tinydir_readfile_n(&dir, &file, i);
+
+ if (ret == 0) {
+ if (!file.is_dir) {
+#if (defined _MSC_VER || defined __MINGW32__)
+ size_t i;
+ char curName[256];
+ wcstombs_s(&i, curName, sizeof(curName), file.name, sizeof(curName));
+#else
+ const char *currName = file.name;
+#endif
+
+ if (strcmp(curName, "fsdata.tmp") == 0) {
+ continue;
+ }
+ if (strcmp(curName, "fshdr.tmp") == 0) {
+ continue;
+ }
+
+ printf("processing %s/%s..." NEWLINE, curSubdir, curName);
+
+ if (process_file(data_file, struct_file, curName) < 0) {
+ printf(NEWLINE "Error... aborting" NEWLINE);
+ return -1;
+ }
+ filesProcessed++;
+ }
+ }
+ }
+ }
+ }
+
+ return filesProcessed;
+}
+
+static 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);
+ LWIP_ASSERT("r == fsize", r == fsize);
+ *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;
+}
+
+static 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%02x,", 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);
+}
+
+static 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;
+ const void *data = (const 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, %"SZT_F"}," 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;
+ }
+}
+
+static 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 (%"SZT_F" 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%02x,", 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;
+ size_t 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\" (%"SZT_F" 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\" (%"SZT_F" 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\" (%"SZT_F"+ 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;
+ }
+
+ lwip_itoa(intbuf, sizeof(intbuf), content_len);
+ 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\" (%"SZT_F"+ 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\" (%"SZT_F" 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\" (%"SZT_F" 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) {
+ LWIP_ASSERT("hdr_len + cur_len <= sizeof(hdr_buf)", hdr_len + cur_len <= sizeof(hdr_buf));
+ memcpy(&hdr_buf[hdr_len], cur_string, cur_len);
+ hdr_len += cur_len;
+
+ 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%02x,", 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%02x,", cur);
+ idx += 5;
+ if ((++(*i) % HEX_BYTES_PER_LINE) == 0) {
+ sprintf(&buf[idx], NEWLINE);
+ idx += NEWLINE_LEN;
+ }
+ }
+ return len;
+}
diff --git a/lwip/src/apps/httpd/makefsdata/readme.txt b/lwip/src/apps/httpd/makefsdata/readme.txt
new file mode 100644
index 0000000..3768585
--- /dev/null
+++ b/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/lwip/src/apps/httpd/makefsdata/tinydir.h b/lwip/src/apps/httpd/makefsdata/tinydir.h
new file mode 100644
index 0000000..32ae5e8
--- /dev/null
+++ b/lwip/src/apps/httpd/makefsdata/tinydir.h
@@ -0,0 +1,808 @@
+/*
+Copyright (c) 2013-2017, tinydir authors:
+- Cong Xu
+- Lautis Sun
+- Baudouin Feildel
+- Andargor <andargor@yahoo.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.
+
+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.
+*/
+#ifndef TINYDIR_H
+#define TINYDIR_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if ((defined _UNICODE) && !(defined UNICODE))
+#define UNICODE
+#endif
+
+#if ((defined UNICODE) && !(defined _UNICODE))
+#define _UNICODE
+#endif
+
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _MSC_VER
+# define WIN32_LEAN_AND_MEAN
+# include <windows.h>
+# include <tchar.h>
+# pragma warning(push)
+# pragma warning (disable : 4996)
+#else
+# include <dirent.h>
+# include <libgen.h>
+# include <sys/stat.h>
+# include <stddef.h>
+#endif
+#ifdef __MINGW32__
+# include <tchar.h>
+#endif
+
+
+/* types */
+
+/* Windows UNICODE wide character support */
+#if defined _MSC_VER || defined __MINGW32__
+#define _tinydir_char_t TCHAR
+#define TINYDIR_STRING(s) _TEXT(s)
+#define _tinydir_strlen _tcslen
+#define _tinydir_strcpy _tcscpy
+#define _tinydir_strcat _tcscat
+#define _tinydir_strcmp _tcscmp
+#define _tinydir_strrchr _tcsrchr
+#define _tinydir_strncmp _tcsncmp
+#else
+#define _tinydir_char_t char
+#define TINYDIR_STRING(s) s
+#define _tinydir_strlen strlen
+#define _tinydir_strcpy strcpy
+#define _tinydir_strcat strcat
+#define _tinydir_strcmp strcmp
+#define _tinydir_strrchr strrchr
+#define _tinydir_strncmp strncmp
+#endif
+
+#if (defined _MSC_VER || defined __MINGW32__)
+#include <windows.h>
+#define _TINYDIR_PATH_MAX MAX_PATH
+#elif defined __linux__
+#include <linux/limits.h>
+#define _TINYDIR_PATH_MAX PATH_MAX
+#else
+#define _TINYDIR_PATH_MAX 4096
+#endif
+
+#ifdef _MSC_VER
+/* extra chars for the "\\*" mask */
+# define _TINYDIR_PATH_EXTRA 2
+#else
+# define _TINYDIR_PATH_EXTRA 0
+#endif
+
+#define _TINYDIR_FILENAME_MAX 256
+
+#if (defined _MSC_VER || defined __MINGW32__)
+#define _TINYDIR_DRIVE_MAX 3
+#endif
+
+#ifdef _MSC_VER
+# define _TINYDIR_FUNC static __inline
+#elif !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L
+# define _TINYDIR_FUNC static __inline__
+#else
+# define _TINYDIR_FUNC static inline
+#endif
+
+/* readdir_r usage; define TINYDIR_USE_READDIR_R to use it (if supported) */
+#ifdef TINYDIR_USE_READDIR_R
+
+/* readdir_r is a POSIX-only function, and may not be available under various
+ * environments/settings, e.g. MinGW. Use readdir fallback */
+#if _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _BSD_SOURCE || _SVID_SOURCE ||\
+ _POSIX_SOURCE
+# define _TINYDIR_HAS_READDIR_R
+#endif
+#if _POSIX_C_SOURCE >= 200112L
+# define _TINYDIR_HAS_FPATHCONF
+# include <unistd.h>
+#endif
+#if _BSD_SOURCE || _SVID_SOURCE || \
+ (_POSIX_C_SOURCE >= 200809L || _XOPEN_SOURCE >= 700)
+# define _TINYDIR_HAS_DIRFD
+# include <sys/types.h>
+#endif
+#if defined _TINYDIR_HAS_FPATHCONF && defined _TINYDIR_HAS_DIRFD &&\
+ defined _PC_NAME_MAX
+# define _TINYDIR_USE_FPATHCONF
+#endif
+#if defined __MINGW32__ || !defined _TINYDIR_HAS_READDIR_R ||\
+ !(defined _TINYDIR_USE_FPATHCONF || defined NAME_MAX)
+# define _TINYDIR_USE_READDIR
+#endif
+
+/* Use readdir by default */
+#else
+# define _TINYDIR_USE_READDIR
+#endif
+
+/* MINGW32 has two versions of dirent, ASCII and UNICODE*/
+#ifndef _MSC_VER
+#if (defined __MINGW32__) && (defined _UNICODE)
+#define _TINYDIR_DIR _WDIR
+#define _tinydir_dirent _wdirent
+#define _tinydir_opendir _wopendir
+#define _tinydir_readdir _wreaddir
+#define _tinydir_closedir _wclosedir
+#else
+#define _TINYDIR_DIR DIR
+#define _tinydir_dirent dirent
+#define _tinydir_opendir opendir
+#define _tinydir_readdir readdir
+#define _tinydir_closedir closedir
+#endif
+#endif
+
+/* Allow user to use a custom allocator by defining _TINYDIR_MALLOC and _TINYDIR_FREE. */
+#if defined(_TINYDIR_MALLOC) && defined(_TINYDIR_FREE)
+#elif !defined(_TINYDIR_MALLOC) && !defined(_TINYDIR_FREE)
+#else
+#error "Either define both alloc and free or none of them!"
+#endif
+
+#if !defined(_TINYDIR_MALLOC)
+ #define _TINYDIR_MALLOC(_size) malloc(_size)
+ #define _TINYDIR_FREE(_ptr) free(_ptr)
+#endif /* !defined(_TINYDIR_MALLOC) */
+
+typedef struct tinydir_file
+{
+ _tinydir_char_t path[_TINYDIR_PATH_MAX];
+ _tinydir_char_t name[_TINYDIR_FILENAME_MAX];
+ _tinydir_char_t *extension;
+ int is_dir;
+ int is_reg;
+
+#ifndef _MSC_VER
+#ifdef __MINGW32__
+ struct _stat _s;
+#else
+ struct stat _s;
+#endif
+#endif
+} tinydir_file;
+
+typedef struct tinydir_dir
+{
+ _tinydir_char_t path[_TINYDIR_PATH_MAX];
+ int has_next;
+ size_t n_files;
+
+ tinydir_file *_files;
+#ifdef _MSC_VER
+ HANDLE _h;
+ WIN32_FIND_DATA _f;
+#else
+ _TINYDIR_DIR *_d;
+ struct _tinydir_dirent *_e;
+#ifndef _TINYDIR_USE_READDIR
+ struct _tinydir_dirent *_ep;
+#endif
+#endif
+} tinydir_dir;
+
+
+/* declarations */
+
+_TINYDIR_FUNC
+int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path);
+_TINYDIR_FUNC
+int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path);
+_TINYDIR_FUNC
+void tinydir_close(tinydir_dir *dir);
+
+_TINYDIR_FUNC
+int tinydir_next(tinydir_dir *dir);
+_TINYDIR_FUNC
+int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file);
+_TINYDIR_FUNC
+int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i);
+_TINYDIR_FUNC
+int tinydir_open_subdir_n(tinydir_dir *dir, size_t i);
+
+_TINYDIR_FUNC
+int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path);
+_TINYDIR_FUNC
+void _tinydir_get_ext(tinydir_file *file);
+_TINYDIR_FUNC
+int _tinydir_file_cmp(const void *a, const void *b);
+#ifndef _MSC_VER
+#ifndef _TINYDIR_USE_READDIR
+_TINYDIR_FUNC
+size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp);
+#endif
+#endif
+
+
+/* definitions*/
+
+_TINYDIR_FUNC
+int tinydir_open(tinydir_dir *dir, const _tinydir_char_t *path)
+{
+#ifndef _MSC_VER
+#ifndef _TINYDIR_USE_READDIR
+ int error;
+ int size; /* using int size */
+#endif
+#else
+ _tinydir_char_t path_buf[_TINYDIR_PATH_MAX];
+#endif
+ _tinydir_char_t *pathp;
+
+ if (dir == NULL || path == NULL || _tinydir_strlen(path) == 0)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
+ {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ /* initialise dir */
+ dir->_files = NULL;
+#ifdef _MSC_VER
+ dir->_h = INVALID_HANDLE_VALUE;
+#else
+ dir->_d = NULL;
+#ifndef _TINYDIR_USE_READDIR
+ dir->_ep = NULL;
+#endif
+#endif
+ tinydir_close(dir);
+
+ _tinydir_strcpy(dir->path, path);
+ /* Remove trailing slashes */
+ pathp = &dir->path[_tinydir_strlen(dir->path) - 1];
+ while (pathp != dir->path && (*pathp == TINYDIR_STRING('\\') || *pathp == TINYDIR_STRING('/')))
+ {
+ *pathp = TINYDIR_STRING('\0');
+ pathp++;
+ }
+#ifdef _MSC_VER
+ _tinydir_strcpy(path_buf, dir->path);
+ _tinydir_strcat(path_buf, TINYDIR_STRING("\\*"));
+#if (defined WINAPI_FAMILY) && (WINAPI_FAMILY != WINAPI_FAMILY_DESKTOP_APP)
+ dir->_h = FindFirstFileEx(path_buf, FindExInfoStandard, &dir->_f, FindExSearchNameMatch, NULL, 0);
+#else
+ dir->_h = FindFirstFile(path_buf, &dir->_f);
+#endif
+ if (dir->_h == INVALID_HANDLE_VALUE)
+ {
+ errno = ENOENT;
+#else
+ dir->_d = _tinydir_opendir(path);
+ if (dir->_d == NULL)
+ {
+#endif
+ goto bail;
+ }
+
+ /* read first file */
+ dir->has_next = 1;
+#ifndef _MSC_VER
+#ifdef _TINYDIR_USE_READDIR
+ dir->_e = _tinydir_readdir(dir->_d);
+#else
+ /* allocate dirent buffer for readdir_r */
+ size = _tinydir_dirent_buf_size(dir->_d); /* conversion to int */
+ if (size == -1) return -1;
+ dir->_ep = (struct _tinydir_dirent*)_TINYDIR_MALLOC(size);
+ if (dir->_ep == NULL) return -1;
+
+ error = readdir_r(dir->_d, dir->_ep, &dir->_e);
+ if (error != 0) return -1;
+#endif
+ if (dir->_e == NULL)
+ {
+ dir->has_next = 0;
+ }
+#endif
+
+ return 0;
+
+bail:
+ tinydir_close(dir);
+ return -1;
+}
+
+_TINYDIR_FUNC
+int tinydir_open_sorted(tinydir_dir *dir, const _tinydir_char_t *path)
+{
+ /* Count the number of files first, to pre-allocate the files array */
+ size_t n_files = 0;
+ if (tinydir_open(dir, path) == -1)
+ {
+ return -1;
+ }
+ while (dir->has_next)
+ {
+ n_files++;
+ if (tinydir_next(dir) == -1)
+ {
+ goto bail;
+ }
+ }
+ tinydir_close(dir);
+
+ if (tinydir_open(dir, path) == -1)
+ {
+ return -1;
+ }
+
+ dir->n_files = 0;
+ dir->_files = (tinydir_file *)_TINYDIR_MALLOC(sizeof *dir->_files * n_files);
+ if (dir->_files == NULL)
+ {
+ goto bail;
+ }
+ while (dir->has_next)
+ {
+ tinydir_file *p_file;
+ dir->n_files++;
+
+ p_file = &dir->_files[dir->n_files - 1];
+ if (tinydir_readfile(dir, p_file) == -1)
+ {
+ goto bail;
+ }
+
+ if (tinydir_next(dir) == -1)
+ {
+ goto bail;
+ }
+
+ /* Just in case the number of files has changed between the first and
+ second reads, terminate without writing into unallocated memory */
+ if (dir->n_files == n_files)
+ {
+ break;
+ }
+ }
+
+ qsort(dir->_files, dir->n_files, sizeof(tinydir_file), _tinydir_file_cmp);
+
+ return 0;
+
+bail:
+ tinydir_close(dir);
+ return -1;
+}
+
+_TINYDIR_FUNC
+void tinydir_close(tinydir_dir *dir)
+{
+ if (dir == NULL)
+ {
+ return;
+ }
+
+ memset(dir->path, 0, sizeof(dir->path));
+ dir->has_next = 0;
+ dir->n_files = 0;
+ _TINYDIR_FREE(dir->_files);
+ dir->_files = NULL;
+#ifdef _MSC_VER
+ if (dir->_h != INVALID_HANDLE_VALUE)
+ {
+ FindClose(dir->_h);
+ }
+ dir->_h = INVALID_HANDLE_VALUE;
+#else
+ if (dir->_d)
+ {
+ _tinydir_closedir(dir->_d);
+ }
+ dir->_d = NULL;
+ dir->_e = NULL;
+#ifndef _TINYDIR_USE_READDIR
+ _TINYDIR_FREE(dir->_ep);
+ dir->_ep = NULL;
+#endif
+#endif
+}
+
+_TINYDIR_FUNC
+int tinydir_next(tinydir_dir *dir)
+{
+ if (dir == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (!dir->has_next)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+#ifdef _MSC_VER
+ if (FindNextFile(dir->_h, &dir->_f) == 0)
+#else
+#ifdef _TINYDIR_USE_READDIR
+ dir->_e = _tinydir_readdir(dir->_d);
+#else
+ if (dir->_ep == NULL)
+ {
+ return -1;
+ }
+ if (readdir_r(dir->_d, dir->_ep, &dir->_e) != 0)
+ {
+ return -1;
+ }
+#endif
+ if (dir->_e == NULL)
+#endif
+ {
+ dir->has_next = 0;
+#ifdef _MSC_VER
+ if (GetLastError() != ERROR_SUCCESS &&
+ GetLastError() != ERROR_NO_MORE_FILES)
+ {
+ tinydir_close(dir);
+ errno = EIO;
+ return -1;
+ }
+#endif
+ }
+
+ return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_readfile(const tinydir_dir *dir, tinydir_file *file)
+{
+ if (dir == NULL || file == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+#ifdef _MSC_VER
+ if (dir->_h == INVALID_HANDLE_VALUE)
+#else
+ if (dir->_e == NULL)
+#endif
+ {
+ errno = ENOENT;
+ return -1;
+ }
+ if (_tinydir_strlen(dir->path) +
+ _tinydir_strlen(
+#ifdef _MSC_VER
+ dir->_f.cFileName
+#else
+ dir->_e->d_name
+#endif
+ ) + 1 + _TINYDIR_PATH_EXTRA >=
+ _TINYDIR_PATH_MAX)
+ {
+ /* the path for the file will be too long */
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+ if (_tinydir_strlen(
+#ifdef _MSC_VER
+ dir->_f.cFileName
+#else
+ dir->_e->d_name
+#endif
+ ) >= _TINYDIR_FILENAME_MAX)
+ {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ _tinydir_strcpy(file->path, dir->path);
+ _tinydir_strcat(file->path, TINYDIR_STRING("/"));
+ _tinydir_strcpy(file->name,
+#ifdef _MSC_VER
+ dir->_f.cFileName
+#else
+ dir->_e->d_name
+#endif
+ );
+ _tinydir_strcat(file->path, file->name);
+#ifndef _MSC_VER
+#ifdef __MINGW32__
+ if (_tstat(
+#else
+ if (stat(
+#endif
+ file->path, &file->_s) == -1)
+ {
+ return -1;
+ }
+#endif
+ _tinydir_get_ext(file);
+
+ file->is_dir =
+#ifdef _MSC_VER
+ !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
+#else
+ S_ISDIR(file->_s.st_mode);
+#endif
+ file->is_reg =
+#ifdef _MSC_VER
+ !!(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NORMAL) ||
+ (
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DEVICE) &&
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
+#ifdef FILE_ATTRIBUTE_INTEGRITY_STREAM
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_INTEGRITY_STREAM) &&
+#endif
+#ifdef FILE_ATTRIBUTE_NO_SCRUB_DATA
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_NO_SCRUB_DATA) &&
+#endif
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) &&
+ !(dir->_f.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY));
+#else
+ S_ISREG(file->_s.st_mode);
+#endif
+
+ return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_readfile_n(const tinydir_dir *dir, tinydir_file *file, size_t i)
+{
+ if (dir == NULL || file == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (i >= dir->n_files)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ memcpy(file, &dir->_files[i], sizeof(tinydir_file));
+ _tinydir_get_ext(file);
+
+ return 0;
+}
+
+_TINYDIR_FUNC
+int tinydir_open_subdir_n(tinydir_dir *dir, size_t i)
+{
+ _tinydir_char_t path[_TINYDIR_PATH_MAX];
+ if (dir == NULL)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (i >= dir->n_files || !dir->_files[i].is_dir)
+ {
+ errno = ENOENT;
+ return -1;
+ }
+
+ _tinydir_strcpy(path, dir->_files[i].path);
+ tinydir_close(dir);
+ if (tinydir_open_sorted(dir, path) == -1)
+ {
+ return -1;
+ }
+
+ return 0;
+}
+
+/* Open a single file given its path */
+_TINYDIR_FUNC
+int tinydir_file_open(tinydir_file *file, const _tinydir_char_t *path)
+{
+ tinydir_dir dir;
+ int result = 0;
+ int found = 0;
+ _tinydir_char_t dir_name_buf[_TINYDIR_PATH_MAX];
+ _tinydir_char_t file_name_buf[_TINYDIR_FILENAME_MAX];
+ _tinydir_char_t *dir_name;
+ _tinydir_char_t *base_name;
+#if (defined _MSC_VER || defined __MINGW32__)
+ _tinydir_char_t drive_buf[_TINYDIR_PATH_MAX];
+ _tinydir_char_t ext_buf[_TINYDIR_FILENAME_MAX];
+#endif
+
+ if (file == NULL || path == NULL || _tinydir_strlen(path) == 0)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ if (_tinydir_strlen(path) + _TINYDIR_PATH_EXTRA >= _TINYDIR_PATH_MAX)
+ {
+ errno = ENAMETOOLONG;
+ return -1;
+ }
+
+ /* Get the parent path */
+#if (defined _MSC_VER || defined __MINGW32__)
+#if ((defined _MSC_VER) && (_MSC_VER >= 1400))
+ _tsplitpath_s(
+ path,
+ drive_buf, _TINYDIR_DRIVE_MAX,
+ dir_name_buf, _TINYDIR_FILENAME_MAX,
+ file_name_buf, _TINYDIR_FILENAME_MAX,
+ ext_buf, _TINYDIR_FILENAME_MAX);
+#else
+ _tsplitpath(
+ path,
+ drive_buf,
+ dir_name_buf,
+ file_name_buf,
+ ext_buf);
+#endif
+
+/* _splitpath_s not work fine with only filename and widechar support */
+#ifdef _UNICODE
+ if (drive_buf[0] == L'\xFEFE')
+ drive_buf[0] = '\0';
+ if (dir_name_buf[0] == L'\xFEFE')
+ dir_name_buf[0] = '\0';
+#endif
+
+ if (errno)
+ {
+ errno = EINVAL;
+ return -1;
+ }
+ /* Emulate the behavior of dirname by returning "." for dir name if it's
+ empty */
+ if (drive_buf[0] == '\0' && dir_name_buf[0] == '\0')
+ {
+ _tinydir_strcpy(dir_name_buf, TINYDIR_STRING("."));
+ }
+ /* Concatenate the drive letter and dir name to form full dir name */
+ _tinydir_strcat(drive_buf, dir_name_buf);
+ dir_name = drive_buf;
+ /* Concatenate the file name and extension to form base name */
+ _tinydir_strcat(file_name_buf, ext_buf);
+ base_name = file_name_buf;
+#else
+ _tinydir_strcpy(dir_name_buf, path);
+ dir_name = dirname(dir_name_buf);
+ _tinydir_strcpy(file_name_buf, path);
+ base_name =basename(file_name_buf);
+#endif
+
+ /* Open the parent directory */
+ if (tinydir_open(&dir, dir_name) == -1)
+ {
+ return -1;
+ }
+
+ /* Read through the parent directory and look for the file */
+ while (dir.has_next)
+ {
+ if (tinydir_readfile(&dir, file) == -1)
+ {
+ result = -1;
+ goto bail;
+ }
+ if (_tinydir_strcmp(file->name, base_name) == 0)
+ {
+ /* File found */
+ found = 1;
+ break;
+ }
+ tinydir_next(&dir);
+ }
+ if (!found)
+ {
+ result = -1;
+ errno = ENOENT;
+ }
+
+bail:
+ tinydir_close(&dir);
+ return result;
+}
+
+_TINYDIR_FUNC
+void _tinydir_get_ext(tinydir_file *file)
+{
+ _tinydir_char_t *period = _tinydir_strrchr(file->name, TINYDIR_STRING('.'));
+ if (period == NULL)
+ {
+ file->extension = &(file->name[_tinydir_strlen(file->name)]);
+ }
+ else
+ {
+ file->extension = period + 1;
+ }
+}
+
+_TINYDIR_FUNC
+int _tinydir_file_cmp(const void *a, const void *b)
+{
+ const tinydir_file *fa = (const tinydir_file *)a;
+ const tinydir_file *fb = (const tinydir_file *)b;
+ if (fa->is_dir != fb->is_dir)
+ {
+ return -(fa->is_dir - fb->is_dir);
+ }
+ return _tinydir_strncmp(fa->name, fb->name, _TINYDIR_FILENAME_MAX);
+}
+
+#ifndef _MSC_VER
+#ifndef _TINYDIR_USE_READDIR
+/*
+The following authored by Ben Hutchings <ben@decadent.org.uk>
+from https://womble.decadent.org.uk/readdir_r-advisory.html
+*/
+/* Calculate the required buffer size (in bytes) for directory *
+* entries read from the given directory handle. Return -1 if this *
+* this cannot be done. *
+* *
+* This code does not trust values of NAME_MAX that are less than *
+* 255, since some systems (including at least HP-UX) incorrectly *
+* define it to be a smaller value. */
+_TINYDIR_FUNC
+size_t _tinydir_dirent_buf_size(_TINYDIR_DIR *dirp)
+{
+ long name_max;
+ size_t name_end;
+ /* parameter may be unused */
+ (void)dirp;
+
+#if defined _TINYDIR_USE_FPATHCONF
+ name_max = fpathconf(dirfd(dirp), _PC_NAME_MAX);
+ if (name_max == -1)
+#if defined(NAME_MAX)
+ name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
+#else
+ return (size_t)(-1);
+#endif
+#elif defined(NAME_MAX)
+ name_max = (NAME_MAX > 255) ? NAME_MAX : 255;
+#else
+#error "buffer size for readdir_r cannot be determined"
+#endif
+ name_end = (size_t)offsetof(struct _tinydir_dirent, d_name) + name_max + 1;
+ return (name_end > sizeof(struct _tinydir_dirent) ?
+ name_end : sizeof(struct _tinydir_dirent));
+}
+#endif
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+# if defined (_MSC_VER)
+# pragma warning(pop)
+# endif
+
+#endif
diff --git a/lwip/src/apps/lwiperf/lwiperf.c b/lwip/src/apps/lwiperf/lwiperf.c
new file mode 100644
index 0000000..2a5e350
--- /dev/null
+++ b/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_remove_header(p, 24);
+ LWIP_ASSERT("pbuf_remove_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/lwip/src/apps/mdns/mdns.c b/lwip/src/apps/mdns/mdns.c
new file mode 100644
index 0000000..2b049d7
--- /dev/null
+++ b/lwip/src/apps/mdns/mdns.c
@@ -0,0 +1,2114 @@
+/**
+ * @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
+ * - 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 "lwip/prot/iana.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_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;
+NETIF_DECLARE_EXT_CALLBACK(netif_callback)
+
+#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;
+};
+
+static err_t
+mdns_domain_add_label_base(struct mdns_domain *domain, 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++;
+ return ERR_OK;
+}
+
+/**
+ * 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)
+{
+ err_t err = mdns_domain_add_label_base(domain, len);
+ if (err != ERR_OK) {
+ return err;
+ }
+ if (len) {
+ MEMCPY(&domain->name[domain->length], label, len);
+ domain->length += len;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Add a label part to a domain (@see mdns_domain_add_label but copy directly from pbuf)
+ */
+static err_t
+mdns_domain_add_label_pbuf(struct mdns_domain *domain, const struct pbuf *p, u16_t offset, u8_t len)
+{
+ err_t err = mdns_domain_add_label_base(domain, len);
+ if (err != ERR_OK) {
+ return err;
+ }
+ if (len) {
+ if (pbuf_copy_partial(p, &domain->name[domain->length], len, offset) != len) {
+ /* take back the ++ done before */
+ domain->length--;
+ return ERR_ARG;
+ }
+ 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) {
+ err_t res;
+
+ if (c + domain->length >= MDNS_DOMAIN_MAXLEN) {
+ return MDNS_READNAME_ERROR;
+ }
+ res = mdns_domain_add_label_pbuf(domain, p, offset, c);
+ if (res != ERR_OK) {
+ return MDNS_READNAME_ERROR;
+ }
+ offset += c;
+ } 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_p_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_p_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 != LWIP_IANA_PORT_MDNS) {
+ 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, LWIP_IANA_PORT_MDNS, 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 = LWIP_IANA_PORT_MDNS;
+ 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_p_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
+ * Announce IP settings have changed on netif.
+ * Call this in your callback registered by netif_set_status_callback().
+ * No need to call this function when LWIP_NETIF_EXT_STATUS_CALLBACK==1,
+ * this handled automatically for you.
+ * @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
+}
+
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+static void
+mdns_netif_ext_status_callback(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args)
+{
+ LWIP_UNUSED_ARG(args);
+
+ /* MDNS enabled on netif? */
+ if (NETIF_TO_HOST(netif) == NULL) {
+ return;
+ }
+
+ switch (reason) {
+ case LWIP_NSC_STATUS_CHANGED:
+ if (args->status_changed.state != 0) {
+ mdns_resp_netif_settings_changed(netif);
+ }
+ /* TODO: send goodbye message */
+ break;
+ case LWIP_NSC_LINK_CHANGED:
+ if (args->link_changed.state != 0) {
+ mdns_resp_netif_settings_changed(netif);
+ }
+ break;
+ case LWIP_NSC_IPV4_ADDRESS_CHANGED: /* fall through */
+ case LWIP_NSC_IPV4_GATEWAY_CHANGED: /* fall through */
+ case LWIP_NSC_IPV4_NETMASK_CHANGED: /* fall through */
+ case LWIP_NSC_IPV4_SETTINGS_CHANGED: /* fall through */
+ case LWIP_NSC_IPV6_SET: /* fall through */
+ case LWIP_NSC_IPV6_ADDR_STATE_CHANGED:
+ mdns_resp_netif_settings_changed(netif);
+ break;
+ default:
+ break;
+ }
+}
+#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_calloc(1, 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);
+
+ 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 service_id if the service was added to the netif, an err_t otherwise
+ */
+s8_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)
+{
+ s8_t i;
+ s8_t 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_calloc(1, sizeof(struct mdns_service));
+ LWIP_ERROR("mdns_resp_add_service: Alloc failed", (srv != NULL), return ERR_MEM);
+
+ 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 slot;
+}
+
+/**
+ * @ingroup mdns
+ * Delete a service on the selected network interface.
+ * @param netif The network interface on which service should be removed
+ * @param slot The service slot number returned by mdns_resp_add_service
+ * @return ERR_OK if the service was removed from the netif, an err_t otherwise
+ */
+err_t
+mdns_resp_del_service(struct netif *netif, s8_t slot)
+{
+ struct mdns_host *mdns;
+ struct mdns_service *srv;
+ LWIP_ASSERT("mdns_resp_del_service: netif != NULL", netif);
+ mdns = NETIF_TO_HOST(netif);
+ LWIP_ERROR("mdns_resp_del_service: Not an mdns netif", (mdns != NULL), return ERR_VAL);
+ LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (slot >= 0) && (slot < MDNS_MAX_SERVICES), return ERR_VAL);
+ LWIP_ERROR("mdns_resp_del_service: Invalid Service ID", (mdns->services[slot] != NULL), return ERR_VAL);
+
+ srv = mdns->services[slot];
+ mdns->services[slot] = NULL;
+ mem_free(srv);
+ 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);
+}
+
+/**
+ * @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, LWIP_IANA_PORT_MDNS);
+ 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();
+
+ /* register for netif events when started on first netif */
+ netif_add_ext_callback(&netif_callback, mdns_netif_ext_status_callback);
+}
+
+#endif /* LWIP_MDNS_RESPONDER */
diff --git a/lwip/src/apps/mqtt/mqtt.c b/lwip/src/apps/mqtt/mqtt.c
new file mode 100644
index 0000000..878b1d2
--- /dev/null
+++ b/lwip/src/apps/mqtt/mqtt.c
@@ -0,0 +1,1419 @@
+/**
+ * @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/apps/mqtt_priv.h"
+#include "lwip/timeouts.h"
+#include "lwip/ip_addr.h"
+#include "lwip/mem.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/altcp.h"
+#include "lwip/altcp_tcp.h"
+#include "lwip/altcp_tls.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)
+
+
+
+/**
+ * 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
+};
+
+
+static void mqtt_cyclic_timer(void *arg);
+
+#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 */
+
+/** Add single item to ring buffer */
+static void
+mqtt_ringbuf_put(struct mqtt_ringbuf_t *rb, u8_t item)
+{
+ rb->buf[rb->put] = item;
+ rb->put++;
+ if (rb->put >= MQTT_OUTPUT_RINGBUF_SIZE) {
+ rb->put = 0;
+ }
+}
+
+/** Return pointer to ring buffer get position */
+static u8_t *
+mqtt_ringbuf_get_ptr(struct mqtt_ringbuf_t *rb)
+{
+ return &rb->buf[rb->get];
+}
+
+static void
+mqtt_ringbuf_advance_get_idx(struct mqtt_ringbuf_t *rb, u16_t len)
+{
+ LWIP_ASSERT("mqtt_ringbuf_advance_get_idx: len < MQTT_OUTPUT_RINGBUF_SIZE", len < MQTT_OUTPUT_RINGBUF_SIZE);
+
+ rb->get += len;
+ if (rb->get >= MQTT_OUTPUT_RINGBUF_SIZE) {
+ rb->get = rb->get - MQTT_OUTPUT_RINGBUF_SIZE;
+ }
+}
+
+/** Return number of bytes in ring buffer */
+static u16_t
+mqtt_ringbuf_len(struct mqtt_ringbuf_t *rb)
+{
+ u32_t len = rb->put - rb->get;
+ if (len > 0xFFFF) {
+ len += MQTT_OUTPUT_RINGBUF_SIZE;
+ }
+ return (u16_t)len;
+}
+
+/** 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))
+
+/**
+ * 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 altcp_pcb *tpcb)
+{
+ err_t err;
+ u8_t wrap = 0;
+ u16_t ringbuf_lin_len = mqtt_ringbuf_linear_read_length(rb);
+ u16_t send_len = altcp_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, rb->put));
+
+ 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 = altcp_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(altcp_sndbuf(tpcb), mqtt_ringbuf_linear_read_length(rb));
+ err = altcp_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 */
+ altcp_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;
+ LWIP_ASSERT("mqtt_request_time_elapsed: tail != NULL", tail != NULL);
+ r = *tail;
+ 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 fdup MQTT DUP flag
+ * @param fqos MQTT QoS field
+ * @param fretain 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 fdup,
+ u8_t fqos, u8_t fretain, u16_t r_length)
+{
+ /* Start with control byte */
+ mqtt_output_append_u8(rb, (((msg_type & 0x0f) << 4) | ((fdup & 1) << 3) | ((fqos & 3) << 1) | (fretain & 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;
+ altcp_recv(client->conn, NULL);
+ altcp_err(client->conn, NULL);
+ altcp_sent(client->conn, NULL);
+ res = altcp_close(client->conn);
+ if (res != ERR_OK) {
+ altcp_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;
+
+ LWIP_ERROR("buffer length mismatch", fixed_hdr_idx + length <= MQTT_VAR_HEADER_BUFFER_LEN,
+ return MQTT_CONNECT_DISCONNECTED);
+
+ 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*/
+ u8_t *topic;
+ u16_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 altcp_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 */
+ altcp_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 altcp_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 altcp_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 altcp_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 */
+ altcp_recv(tpcb, mqtt_tcp_recv_cb);
+ altcp_sent(tpcb, mqtt_tcp_sent_cb);
+ altcp_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)
+{
+ return (mqtt_client_t *)mem_calloc(1, sizeof(mqtt_client_t));
+}
+
+/**
+ * @ingroup mqtt
+ * Free MQTT client instance
+ * @param client Pointer to instance to be freed
+ */
+void
+mqtt_client_free(mqtt_client_t *client)
+{
+ mem_free(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;
+ u8_t client_user_len = 0, client_pass_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;
+ }
+ if (client_info->client_user != NULL) {
+ flags |= MQTT_CONNECT_FLAG_USERNAME;
+ len = strlen(client_info->client_user);
+ LWIP_ERROR("mqtt_client_connect: client_info->client_user length overflow", len <= 0xFF, return ERR_VAL);
+ LWIP_ERROR("mqtt_client_connect: client_info->client_user length must be > 0", len > 0, return ERR_VAL);
+ client_user_len = (u8_t)len;
+ len = remaining_length + 2 + client_user_len;
+ LWIP_ERROR("mqtt_client_connect: remaining_length overflow", len <= 0xFFFF, return ERR_VAL);
+ remaining_length = (u16_t)len;
+ }
+ if (client_info->client_pass != NULL) {
+ flags |= MQTT_CONNECT_FLAG_PASSWORD;
+ len = strlen(client_info->client_pass);
+ LWIP_ERROR("mqtt_client_connect: client_info->client_pass length overflow", len <= 0xFF, return ERR_VAL);
+ LWIP_ERROR("mqtt_client_connect: client_info->client_pass length must be > 0", len > 0, return ERR_VAL);
+ client_pass_len = (u8_t)len;
+ len = remaining_length + 2 + client_pass_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 = altcp_tcp_new();
+ if (client->conn == NULL) {
+ return ERR_MEM;
+ }
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+ if (client_info->tls_config) {
+ struct altcp_pcb *pcb_tls = altcp_tls_new(client_info->tls_config, client->conn);
+ if (pcb_tls == NULL) {
+ altcp_close(client->conn);
+ return ERR_MEM;
+ }
+ client->conn = pcb_tls;
+ }
+#endif
+
+ /* Set arg pointer for callbacks */
+ altcp_arg(client->conn, client);
+ /* Any local address, pick random local port number */
+ err = altcp_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 = altcp_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 */
+ altcp_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);
+ }
+ /* Append user name if given */
+ if ((flags & MQTT_CONNECT_FLAG_USERNAME) != 0) {
+ mqtt_output_append_string(&client->output, client_info->client_user, client_user_len);
+ }
+ /* Append password if given */
+ if ((flags & MQTT_CONNECT_FLAG_PASSWORD) != 0) {
+ mqtt_output_append_string(&client->output, client_info->client_pass, client_pass_len);
+ }
+ return ERR_OK;
+
+tcp_fail:
+ altcp_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/lwip/src/apps/netbiosns/netbiosns.c b/lwip/src/apps/netbiosns/netbiosns.c
new file mode 100644
index 0000000..a04db7b
--- /dev/null
+++ b/lwip/src/apps/netbiosns/netbiosns.c
@@ -0,0 +1,368 @@
+/**
+ * @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 "lwip/prot/iana.h"
+
+#include <string.h>
+
+/** 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, LWIP_IANA_PORT_NETBIOS);
+ 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/lwip/src/apps/smtp/smtp.c b/lwip/src/apps/smtp/smtp.c
new file mode 100644
index 0000000..199b07a
--- /dev/null
+++ b/lwip/src/apps/smtp/smtp.c
@@ -0,0 +1,1540 @@
+/**
+ * @file
+ * SMTP client module
+ *
+ * Author: Simon Goldschmidt
+ *
+ * @defgroup smtp SMTP client
+ * @ingroup apps
+ *
+ * This is simple SMTP client for raw API.
+ * It is a minimal implementation of SMTP as specified in RFC 5321.
+ *
+ * Example usage:
+@code{.c}
+ void my_smtp_result_fn(void *arg, u8_t smtp_result, u16_t srv_err, err_t err)
+ {
+ printf("mail (%p) sent with results: 0x%02x, 0x%04x, 0x%08x\n", arg,
+ smtp_result, srv_err, err);
+ }
+ static void my_smtp_test(void)
+ {
+ smtp_set_server_addr("mymailserver.org");
+ -> set both username and password as NULL if no auth needed
+ smtp_set_auth("username", "password");
+ smtp_send_mail("sender", "recipient", "subject", "body", my_smtp_result_fn,
+ some_argument);
+ }
+@endcode
+
+ * When using from any other thread than the tcpip_thread (for NO_SYS==0), use
+ * smtp_send_mail_int()!
+ *
+ * SMTP_BODYDH usage:
+@code{.c}
+ int my_smtp_bodydh_fn(void *arg, struct smtp_bodydh *bdh)
+ {
+ if(bdh->state >= 10) {
+ return BDH_DONE;
+ }
+ sprintf(bdh->buffer,"Line #%2d\r\n",bdh->state);
+ bdh->length = strlen(bdh->buffer);
+ ++bdh->state;
+ return BDH_WORKING;
+ }
+
+ smtp_send_mail_bodycback("sender", "recipient", "subject",
+ my_smtp_bodydh_fn, my_smtp_result_fn, some_argument);
+@endcode
+ *
+ * @todo:
+ * - attachments (the main difficulty here is streaming base64-encoding to
+ * prevent having to allocate a buffer for the whole encoded file at once)
+ * - test with more mail servers...
+ *
+ */
+
+#include "lwip/apps/smtp.h"
+
+#if LWIP_TCP && LWIP_CALLBACK_API
+#include "lwip/sys.h"
+#include "lwip/sockets.h"
+#include "lwip/altcp.h"
+#include "lwip/dns.h"
+#include "lwip/mem.h"
+#include "lwip/altcp_tcp.h"
+#include "lwip/altcp_tls.h"
+
+#include <string.h> /* strnlen, memcpy */
+#include <stdlib.h>
+
+/** TCP poll interval. Unit is 0.5 sec. */
+#define SMTP_POLL_INTERVAL 4
+/** TCP poll timeout while sending message body, reset after every
+ * successful write. 3 minutes */
+#define SMTP_TIMEOUT_DATABLOCK ( 3 * 60 * SMTP_POLL_INTERVAL / 2)
+/** TCP poll timeout while waiting for confirmation after sending the body.
+ * 10 minutes */
+#define SMTP_TIMEOUT_DATATERM (10 * 60 * SMTP_POLL_INTERVAL / 2)
+/** TCP poll timeout while not sending the body.
+ * This is somewhat lower than the RFC states (5 minutes for initial, MAIL
+ * and RCPT) but still OK for us here.
+ * 2 minutes */
+#define SMTP_TIMEOUT ( 2 * 60 * SMTP_POLL_INTERVAL / 2)
+
+/* the various debug levels for this file */
+#define SMTP_DEBUG_TRACE (SMTP_DEBUG | LWIP_DBG_TRACE)
+#define SMTP_DEBUG_STATE (SMTP_DEBUG | LWIP_DBG_STATE)
+#define SMTP_DEBUG_WARN (SMTP_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define SMTP_DEBUG_WARN_STATE (SMTP_DEBUG | LWIP_DBG_LEVEL_WARNING | LWIP_DBG_STATE)
+#define SMTP_DEBUG_SERIOUS (SMTP_DEBUG | LWIP_DBG_LEVEL_SERIOUS)
+
+
+#define SMTP_RX_BUF_LEN 255
+#define SMTP_TX_BUF_LEN 255
+#define SMTP_CRLF "\r\n"
+#define SMTP_CRLF_LEN 2
+
+#define SMTP_RESP_220 "220"
+#define SMTP_RESP_235 "235"
+#define SMTP_RESP_250 "250"
+#define SMTP_RESP_334 "334"
+#define SMTP_RESP_354 "354"
+#define SMTP_RESP_LOGIN_UNAME "VXNlcm5hbWU6"
+#define SMTP_RESP_LOGIN_PASS "UGFzc3dvcmQ6"
+
+#define SMTP_KEYWORD_AUTH_SP "AUTH "
+#define SMTP_KEYWORD_AUTH_EQ "AUTH="
+#define SMTP_KEYWORD_AUTH_LEN 5
+#define SMTP_AUTH_PARAM_PLAIN "PLAIN"
+#define SMTP_AUTH_PARAM_LOGIN "LOGIN"
+
+#define SMTP_CMD_EHLO_1 "EHLO ["
+#define SMTP_CMD_EHLO_1_LEN 6
+#define SMTP_CMD_EHLO_2 "]\r\n"
+#define SMTP_CMD_EHLO_2_LEN 3
+#define SMTP_CMD_AUTHPLAIN_1 "AUTH PLAIN "
+#define SMTP_CMD_AUTHPLAIN_1_LEN 11
+#define SMTP_CMD_AUTHPLAIN_2 "\r\n"
+#define SMTP_CMD_AUTHPLAIN_2_LEN 2
+#define SMTP_CMD_AUTHLOGIN "AUTH LOGIN\r\n"
+#define SMTP_CMD_AUTHLOGIN_LEN 12
+#define SMTP_CMD_MAIL_1 "MAIL FROM: <"
+#define SMTP_CMD_MAIL_1_LEN 12
+#define SMTP_CMD_MAIL_2 ">\r\n"
+#define SMTP_CMD_MAIL_2_LEN 3
+#define SMTP_CMD_RCPT_1 "RCPT TO: <"
+#define SMTP_CMD_RCPT_1_LEN 10
+#define SMTP_CMD_RCPT_2 ">\r\n"
+#define SMTP_CMD_RCPT_2_LEN 3
+#define SMTP_CMD_DATA "DATA\r\n"
+#define SMTP_CMD_DATA_LEN 6
+#define SMTP_CMD_HEADER_1 "From: <"
+#define SMTP_CMD_HEADER_1_LEN 7
+#define SMTP_CMD_HEADER_2 ">\r\nTo: <"
+#define SMTP_CMD_HEADER_2_LEN 8
+#define SMTP_CMD_HEADER_3 ">\r\nSubject: "
+#define SMTP_CMD_HEADER_3_LEN 12
+#define SMTP_CMD_HEADER_4 "\r\n\r\n"
+#define SMTP_CMD_HEADER_4_LEN 4
+#define SMTP_CMD_BODY_FINISHED "\r\n.\r\n"
+#define SMTP_CMD_BODY_FINISHED_LEN 5
+#define SMTP_CMD_QUIT "QUIT\r\n"
+#define SMTP_CMD_QUIT_LEN 6
+
+#if defined(SMTP_STAT_TX_BUF_MAX) && SMTP_STAT_TX_BUF_MAX
+#define SMTP_TX_BUF_MAX(len) LWIP_MACRO(if((len) > smtp_tx_buf_len_max) smtp_tx_buf_len_max = (len);)
+#else /* SMTP_STAT_TX_BUF_MAX */
+#define SMTP_TX_BUF_MAX(len)
+#endif /* SMTP_STAT_TX_BUF_MAX */
+
+#if SMTP_COPY_AUTHDATA
+#define SMTP_USERNAME(session) (session)->username
+#define SMTP_PASS(session) (session)->pass
+#define SMTP_AUTH_PLAIN_DATA(session) (session)->auth_plain
+#define SMTP_AUTH_PLAIN_LEN(session) (session)->auth_plain_len
+#else /* SMTP_COPY_AUTHDATA */
+#define SMTP_USERNAME(session) smtp_username
+#define SMTP_PASS(session) smtp_pass
+#define SMTP_AUTH_PLAIN_DATA(session) smtp_auth_plain
+#define SMTP_AUTH_PLAIN_LEN(session) smtp_auth_plain_len
+#endif /* SMTP_COPY_AUTHDATA */
+
+#if SMTP_BODYDH
+#ifndef SMTP_BODYDH_MALLOC
+#define SMTP_BODYDH_MALLOC(size) mem_malloc(size)
+#define SMTP_BODYDH_FREE(ptr) mem_free(ptr)
+#endif
+
+/* Some internal state return values */
+#define BDHALLDATASENT 2
+#define BDHSOMEDATASENT 1
+
+enum bdh_handler_state {
+ BDH_SENDING, /* Serving the user function generating body content */
+ BDH_STOP /* User function stopped, closing */
+};
+#endif
+
+/** State for SMTP client state machine */
+enum smtp_session_state {
+ SMTP_NULL,
+ SMTP_HELO,
+ SMTP_AUTH_PLAIN,
+ SMTP_AUTH_LOGIN_UNAME,
+ SMTP_AUTH_LOGIN_PASS,
+ SMTP_AUTH_LOGIN,
+ SMTP_MAIL,
+ SMTP_RCPT,
+ SMTP_DATA,
+ SMTP_BODY,
+ SMTP_QUIT,
+ SMTP_CLOSED
+};
+
+#ifdef LWIP_DEBUG
+/** State-to-string table for debugging */
+static const char *smtp_state_str[] = {
+ "SMTP_NULL",
+ "SMTP_HELO",
+ "SMTP_AUTH_PLAIN",
+ "SMTP_AUTH_LOGIN_UNAME",
+ "SMTP_AUTH_LOGIN_PASS",
+ "SMTP_AUTH_LOGIN",
+ "SMTP_MAIL",
+ "SMTP_RCPT",
+ "SMTP_DATA",
+ "SMTP_BODY",
+ "SMTP_QUIT",
+ "SMTP_CLOSED",
+};
+
+static const char *smtp_result_strs[] = {
+ "SMTP_RESULT_OK",
+ "SMTP_RESULT_ERR_UNKNOWN",
+ "SMTP_RESULT_ERR_CONNECT",
+ "SMTP_RESULT_ERR_HOSTNAME",
+ "SMTP_RESULT_ERR_CLOSED",
+ "SMTP_RESULT_ERR_TIMEOUT",
+ "SMTP_RESULT_ERR_SVR_RESP",
+ "SMTP_RESULT_ERR_MEM"
+};
+#endif /* LWIP_DEBUG */
+
+#if SMTP_BODYDH
+struct smtp_bodydh_state {
+ smtp_bodycback_fn callback_fn; /* The function to call (again) */
+ u16_t state;
+ struct smtp_bodydh exposed; /* the user function structure */
+};
+#endif /* SMTP_BODYDH */
+
+/** struct keeping the body and state of an smtp session */
+struct smtp_session {
+ /** keeping the state of the smtp session */
+ enum smtp_session_state state;
+ /** timeout handling, if this reaches 0, the connection is closed */
+ u16_t timer;
+ /** helper buffer for transmit, not used for sending body */
+ char tx_buf[SMTP_TX_BUF_LEN + 1];
+ struct pbuf* p;
+ /** source email address */
+ const char* from;
+ /** size of the sourceemail address */
+ u16_t from_len;
+ /** target email address */
+ const char* to;
+ /** size of the target email address */
+ u16_t to_len;
+ /** subject of the email */
+ const char *subject;
+ /** length of the subject string */
+ u16_t subject_len;
+ /** this is the body of the mail to be sent */
+ const char* body;
+ /** this is the length of the body to be sent */
+ u16_t body_len;
+ /** amount of data from body already sent */
+ u16_t body_sent;
+ /** callback function to call when closed */
+ smtp_result_fn callback_fn;
+ /** argument for callback function */
+ void *callback_arg;
+#if SMTP_COPY_AUTHDATA
+ /** Username to use for this request */
+ char *username;
+ /** Password to use for this request */
+ char *pass;
+ /** Username and password combined as necessary for PLAIN authentication */
+ char auth_plain[SMTP_MAX_USERNAME_LEN + SMTP_MAX_PASS_LEN + 3];
+ /** Length of smtp_auth_plain string (cannot use strlen since it includes \0) */
+ size_t auth_plain_len;
+#endif /* SMTP_COPY_AUTHDATA */
+#if SMTP_BODYDH
+ struct smtp_bodydh_state *bodydh;
+#endif /* SMTP_BODYDH */
+};
+
+/** IP address or DNS name of the server to use for next SMTP request */
+static char smtp_server[SMTP_MAX_SERVERNAME_LEN + 1];
+/** TCP port of the server to use for next SMTP request */
+static u16_t smtp_server_port = SMTP_DEFAULT_PORT;
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+/** If this is set, mail is sent using SMTPS */
+static struct altcp_tls_config *smtp_server_tls_config;
+#endif
+/** Username to use for the next SMTP request */
+static char *smtp_username;
+/** Password to use for the next SMTP request */
+static char *smtp_pass;
+/** Username and password combined as necessary for PLAIN authentication */
+static char smtp_auth_plain[SMTP_MAX_USERNAME_LEN + SMTP_MAX_PASS_LEN + 3];
+/** Length of smtp_auth_plain string (cannot use strlen since it includes \0) */
+static size_t smtp_auth_plain_len;
+
+static err_t smtp_verify(const char *data, size_t data_len, u8_t linebreaks_allowed);
+static err_t smtp_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err);
+static void smtp_tcp_err(void *arg, err_t err);
+static err_t smtp_tcp_poll(void *arg, struct altcp_pcb *pcb);
+static err_t smtp_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len);
+static err_t smtp_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err);
+#if LWIP_DNS
+static void smtp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg);
+#endif /* LWIP_DNS */
+static size_t smtp_base64_encode(char* target, size_t target_len, const char* source, size_t source_len);
+static enum smtp_session_state smtp_prepare_mail(struct smtp_session *s, u16_t *tx_buf_len);
+static void smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb);
+static void smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p);
+#if SMTP_BODYDH
+static void smtp_send_body_data_handler(struct smtp_session *s, struct altcp_pcb *pcb);
+#endif /* SMTP_BODYDH */
+
+
+#ifdef LWIP_DEBUG
+/** Convert an smtp result to a string */
+const char*
+smtp_result_str(u8_t smtp_result)
+{
+ if (smtp_result >= LWIP_ARRAYSIZE(smtp_result_strs)) {
+ return "UNKNOWN";
+ }
+ return smtp_result_strs[smtp_result];
+}
+
+/** Null-terminates the payload of p for printing out messages.
+ * WARNING: use this only if p is not needed any more as the last byte of
+ * payload is deleted!
+ */
+static const char*
+smtp_pbuf_str(struct pbuf* p)
+{
+ if ((p == NULL) || (p->len == 0)) {
+ return "";
+ }
+ ((char*)p->payload)[p->len] = 0;
+ return (const char*)p->payload;
+}
+#endif /* LWIP_DEBUG */
+
+/** @ingroup smtp
+ * Set IP address or DNS name for next SMTP connection
+ *
+ * @param server IP address (in ASCII representation) or DNS name of the server
+ */
+err_t
+smtp_set_server_addr(const char* server)
+{
+ size_t len = 0;
+ if (server != NULL) {
+ /* strnlen: returns length WITHOUT terminating 0 byte OR
+ * SMTP_MAX_SERVERNAME_LEN+1 when string is too long */
+ len = strnlen(server, SMTP_MAX_SERVERNAME_LEN+1);
+ }
+ if (len > SMTP_MAX_SERVERNAME_LEN) {
+ return ERR_MEM;
+ }
+ if (len != 0) {
+ MEMCPY(smtp_server, server, len);
+ }
+ smtp_server[len] = 0; /* always OK because of smtp_server[SMTP_MAX_SERVERNAME_LEN + 1] */
+ return ERR_OK;
+}
+
+/** @ingroup smtp
+ * Set TCP port for next SMTP connection
+ *
+ * @param port TCP port
+ */
+void
+smtp_set_server_port(u16_t port)
+{
+ smtp_server_port = port;
+}
+
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+/** @ingroup smtp
+ * Set TLS configuration for next SMTP connection
+ *
+ * @param tls_config TLS configuration
+ */
+void
+smtp_set_tls_config(struct altcp_tls_config *tls_config)
+{
+ smtp_server_tls_config = tls_config;
+}
+#endif
+
+/** @ingroup smtp
+ * Set authentication parameters for next SMTP connection
+ *
+ * @param username login name as passed to the server
+ * @param pass password passed to the server together with username
+ */
+err_t
+smtp_set_auth(const char* username, const char* pass)
+{
+ size_t uname_len = 0;
+ size_t pass_len = 0;
+
+ memset(smtp_auth_plain, 0xfa, 64);
+ if (username != NULL) {
+ uname_len = strlen(username);
+ if (uname_len > SMTP_MAX_USERNAME_LEN) {
+ LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Username is too long, %d instead of %d\n",
+ (int)uname_len, SMTP_MAX_USERNAME_LEN));
+ return ERR_ARG;
+ }
+ }
+ if (pass != NULL) {
+#if SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN
+ pass_len = strlen(pass);
+ if (pass_len > SMTP_MAX_PASS_LEN) {
+ LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Password is too long, %d instead of %d\n",
+ (int)uname_len, SMTP_MAX_USERNAME_LEN));
+ return ERR_ARG;
+ }
+#else /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("Password not supported as no authentication methods are activated\n"));
+#endif /* SMTP_SUPPORT_AUTH_LOGIN || SMTP_SUPPORT_AUTH_PLAIN */
+ }
+ *smtp_auth_plain = 0;
+ if (username != NULL) {
+ smtp_username = smtp_auth_plain + 1;
+ strcpy(smtp_username, username);
+ }
+ if (pass != NULL) {
+ smtp_pass = smtp_auth_plain + uname_len + 2;
+ strcpy(smtp_pass, pass);
+ }
+ smtp_auth_plain_len = uname_len + pass_len + 2;
+
+ return ERR_OK;
+}
+
+#if SMTP_BODYDH
+static void smtp_free_struct(struct smtp_session *s)
+{
+ if (s->bodydh != NULL) {
+ SMTP_BODYDH_FREE(s->bodydh);
+ }
+ SMTP_STATE_FREE(s);
+}
+#else /* SMTP_BODYDH */
+#define smtp_free_struct(x) SMTP_STATE_FREE(x)
+#endif /* SMTP_BODYDH */
+
+static struct altcp_pcb*
+smtp_setup_pcb(struct smtp_session *s, const ip_addr_t* remote_ip)
+{
+ struct altcp_pcb* pcb;
+ LWIP_UNUSED_ARG(remote_ip);
+
+ pcb = altcp_tcp_new_ip_type(IP_GET_TYPE(remote_ip));
+ if (pcb != NULL) {
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+ if (smtp_server_tls_config) {
+ struct altcp_pcb *pcb_tls = altcp_tls_new(smtp_server_tls_config, pcb);
+ if (pcb_tls == NULL) {
+ altcp_close(pcb);
+ return NULL;
+ }
+ pcb = pcb_tls;
+ }
+#endif
+ altcp_arg(pcb, s);
+ altcp_recv(pcb, smtp_tcp_recv);
+ altcp_err(pcb, smtp_tcp_err);
+ altcp_poll(pcb, smtp_tcp_poll, SMTP_POLL_INTERVAL);
+ altcp_sent(pcb, smtp_tcp_sent);
+ }
+ return pcb;
+}
+
+/** The actual mail-sending function, called by smtp_send_mail and
+ * smtp_send_mail_static after setting up the struct smtp_session.
+ */
+static err_t
+smtp_send_mail_alloced(struct smtp_session *s)
+{
+ err_t err;
+ struct altcp_pcb* pcb = NULL;
+ ip_addr_t addr;
+
+ LWIP_ASSERT("no smtp_session supplied", s != NULL);
+
+#if SMTP_CHECK_DATA
+ /* check that body conforms to RFC:
+ * - convert all single-CR or -LF in body to CRLF
+ * - only 7-bit ASCII is allowed
+ */
+ if (smtp_verify(s->to, s->to_len, 0) != ERR_OK) {
+ err = ERR_ARG;
+ goto leave;
+ }
+ if (smtp_verify(s->from, s->from_len, 0) != ERR_OK) {
+ err = ERR_ARG;
+ goto leave;
+ }
+ if (smtp_verify(s->subject, s->subject_len, 0) != ERR_OK) {
+ err = ERR_ARG;
+ goto leave;
+ }
+#if SMTP_BODYDH
+ if (s->bodydh == NULL)
+#endif /* SMTP_BODYDH */
+ {
+ if (smtp_verify(s->body, s->body_len, 0) != ERR_OK) {
+ err = ERR_ARG;
+ goto leave;
+ }
+ }
+#endif /* SMTP_CHECK_DATA */
+
+#if SMTP_COPY_AUTHDATA
+ /* copy auth data, ensuring the first byte is always zero */
+ MEMCPY(s->auth_plain + 1, smtp_auth_plain + 1, smtp_auth_plain_len - 1);
+ s->auth_plain_len = smtp_auth_plain_len;
+ /* default username and pass is empty string */
+ s->username = s->auth_plain;
+ s->pass = s->auth_plain;
+ if (smtp_username != NULL) {
+ s->username += smtp_username - smtp_auth_plain;
+ }
+ if (smtp_pass != NULL) {
+ s->pass += smtp_pass - smtp_auth_plain;
+ }
+#endif /* SMTP_COPY_AUTHDATA */
+
+ s->state = SMTP_NULL;
+ s->timer = SMTP_TIMEOUT;
+
+#if LWIP_DNS
+ err = dns_gethostbyname(smtp_server, &addr, smtp_dns_found, s);
+#else /* LWIP_DNS */
+ err = ipaddr_aton(smtp_server, &addr) ? ERR_OK : ERR_ARG;
+#endif /* LWIP_DNS */
+ if (err == ERR_OK) {
+ pcb = smtp_setup_pcb(s, &addr);
+ if (pcb == NULL) {
+ err = ERR_MEM;
+ goto leave;
+ }
+ err = altcp_connect(pcb, &addr, smtp_server_port, smtp_tcp_connected);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
+ goto deallocate_and_leave;
+ }
+ } else if (err != ERR_INPROGRESS) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("dns_gethostbyname failed: %d\n", (int)err));
+ goto deallocate_and_leave;
+ }
+ return ERR_OK;
+
+deallocate_and_leave:
+ if (pcb != NULL) {
+ altcp_arg(pcb, NULL);
+ altcp_close(pcb);
+ }
+leave:
+ smtp_free_struct(s);
+ /* no need to call the callback here since we return != ERR_OK */
+ return err;
+}
+
+/** @ingroup smtp
+ * Send an email via the currently selected server, username and password.
+ *
+ * @param from source email address (must be NULL-terminated)
+ * @param to target email address (must be NULL-terminated)
+ * @param subject email subject (must be NULL-terminated)
+ * @param body email body (must be NULL-terminated)
+ * @param callback_fn callback function
+ * @param callback_arg user argument to callback_fn
+ * @returns - ERR_OK if structures were allocated and no error occured starting the connection
+ * (this does not mean the email has been successfully sent!)
+ * - another err_t on error.
+ */
+err_t
+smtp_send_mail(const char* from, const char* to, const char* subject, const char* body,
+ smtp_result_fn callback_fn, void* callback_arg)
+{
+ struct smtp_session* s;
+ size_t from_len = strlen(from);
+ size_t to_len = strlen(to);
+ size_t subject_len = strlen(subject);
+ size_t body_len = strlen(body);
+ size_t mem_len = sizeof(struct smtp_session);
+ char *sfrom, *sto, *ssubject, *sbody;
+
+ mem_len += from_len + to_len + subject_len + body_len + 4;
+ if (mem_len > 0xffff) {
+ /* too long! */
+ return ERR_MEM;
+ }
+
+ /* Allocate memory to keep this email's session state */
+ s = (struct smtp_session *)SMTP_STATE_MALLOC((mem_size_t)mem_len);
+ if (s == NULL) {
+ return ERR_MEM;
+ }
+ /* initialize the structure */
+ memset(s, 0, mem_len);
+ s->from = sfrom = (char*)s + sizeof(struct smtp_session);
+ s->from_len = (u16_t)from_len;
+ s->to = sto = sfrom + from_len + 1;
+ s->to_len = (u16_t)to_len;
+ s->subject = ssubject = sto + to_len + 1;
+ s->subject_len = (u16_t)subject_len;
+ s->body = sbody = ssubject + subject_len + 1;
+ s->body_len = (u16_t)body_len;
+ /* copy source and target email address */
+ /* cast to size_t is a hack to cast away constness */
+ MEMCPY(sfrom, from, from_len + 1);
+ MEMCPY(sto, to, to_len + 1);
+ MEMCPY(ssubject, subject, subject_len + 1);
+ MEMCPY(sbody, body, body_len + 1);
+
+ s->callback_fn = callback_fn;
+ s->callback_arg = callback_arg;
+
+ /* call the actual implementation of this function */
+ return smtp_send_mail_alloced(s);
+}
+
+/** @ingroup smtp
+ * Same as smtp_send_mail, but doesn't copy from, to, subject and body into
+ * an internal buffer to save memory.
+ * WARNING: the above data must stay untouched until the callback function is
+ * called (unless the function returns != ERR_OK)
+ */
+err_t
+smtp_send_mail_static(const char *from, const char* to, const char* subject,
+ const char* body, smtp_result_fn callback_fn, void* callback_arg)
+{
+ struct smtp_session* s;
+ size_t len;
+
+ s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session));
+ if (s == NULL) {
+ return ERR_MEM;
+ }
+ memset(s, 0, sizeof(struct smtp_session));
+ /* initialize the structure */
+ s->from = from;
+ len = strlen(from);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->from_len = (u16_t)len;
+ s->to = to;
+ len = strlen(to);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->to_len = (u16_t)len;
+ s->subject = subject;
+ len = strlen(subject);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->subject_len = (u16_t)len;
+ s->body = body;
+ len = strlen(body);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->body_len = (u16_t)len;
+ s->callback_fn = callback_fn;
+ s->callback_arg = callback_arg;
+ /* call the actual implementation of this function */
+ return smtp_send_mail_alloced(s);
+}
+
+
+/** @ingroup smtp
+ * Same as smtp_send_mail but takes a struct smtp_send_request as single
+ * parameter which contains all the other parameters.
+ * To be used with tcpip_callback to send mail from interrupt context or from
+ * another thread.
+ *
+ * WARNING: server and authentication must stay untouched until this function has run!
+ *
+ * Usage example:
+ * - allocate a struct smtp_send_request (in a way that is allowed in interrupt context)
+ * - fill the members of the struct as if calling smtp_send_mail
+ * - specify a callback_function
+ * - set callback_arg to the structure itself
+ * - call this function
+ * - wait for the callback function to be called
+ * - in the callback function, deallocate the structure (passed as arg)
+ */
+void
+smtp_send_mail_int(void *arg)
+{
+ struct smtp_send_request *req = (struct smtp_send_request*)arg;
+ err_t err;
+
+ LWIP_ASSERT("smtp_send_mail_int: no argument given", arg != NULL);
+
+ if (req->static_data) {
+ err = smtp_send_mail_static(req->from, req->to, req->subject, req->body,
+ req->callback_fn, req->callback_arg);
+ } else {
+ err = smtp_send_mail(req->from, req->to, req->subject, req->body,
+ req->callback_fn, req->callback_arg);
+ }
+ if ((err != ERR_OK) && (req->callback_fn != NULL)) {
+ req->callback_fn(req->callback_arg, SMTP_RESULT_ERR_UNKNOWN, 0, err);
+ }
+}
+
+#if SMTP_CHECK_DATA
+/** Verify that a given string conforms to the SMTP rules
+ * (7-bit only, no single CR or LF,
+ * @todo: no line consisting of a single dot only)
+ */
+static err_t
+smtp_verify(const char *data, size_t data_len, u8_t linebreaks_allowed)
+{
+ size_t i;
+ u8_t last_was_cr = 0;
+ for (i = 0; i < data_len; i++) {
+ char current = data[i];
+ if ((current & 0x80) != 0) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: no 8-bit data supported: %s\n", data));
+ return ERR_ARG;
+ }
+ if (current == '\r') {
+ if (!linebreaks_allowed) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found CR where no linebreaks allowed: %s\n", data));
+ return ERR_ARG;
+ }
+ if (last_was_cr) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found double CR: %s\n", data));
+ return ERR_ARG;
+ }
+ last_was_cr = 1;
+ } else {
+ if (current == '\n') {
+ if (!last_was_cr) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_verify: found LF without CR before: %s\n", data));
+ return ERR_ARG;
+ }
+ }
+ last_was_cr = 0;
+ }
+ }
+ return ERR_OK;
+}
+#endif /* SMTP_CHECK_DATA */
+
+/** Frees the smtp_session and calls the callback function */
+static void
+smtp_free(struct smtp_session *s, u8_t result, u16_t srv_err, err_t err)
+{
+ smtp_result_fn fn = s->callback_fn;
+ void *arg = s->callback_arg;
+ if (s->p != NULL) {
+ pbuf_free(s->p);
+ }
+ smtp_free_struct(s);
+ if (fn != NULL) {
+ fn(arg, result, srv_err, err);
+ }
+}
+
+/** Try to close a pcb and free the arg if successful */
+static void
+smtp_close(struct smtp_session *s, struct altcp_pcb *pcb, u8_t result,
+ u16_t srv_err, err_t err)
+{
+ if (pcb != NULL) {
+ altcp_arg(pcb, NULL);
+ if (altcp_close(pcb) == ERR_OK) {
+ if (s != NULL) {
+ smtp_free(s, result, srv_err, err);
+ }
+ } else {
+ /* close failed, set back arg */
+ altcp_arg(pcb, s);
+ }
+ } else {
+ if (s != NULL) {
+ smtp_free(s, result, srv_err, err);
+ }
+ }
+}
+
+/** Raw API TCP err callback: pcb is already deallocated */
+static void
+smtp_tcp_err(void *arg, err_t err)
+{
+ LWIP_UNUSED_ARG(err);
+ if (arg != NULL) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_err: connection reset by remote host\n"));
+ smtp_free((struct smtp_session*)arg, SMTP_RESULT_ERR_CLOSED, 0, err);
+ }
+}
+
+/** Raw API TCP poll callback */
+static err_t
+smtp_tcp_poll(void *arg, struct altcp_pcb *pcb)
+{
+ if (arg != NULL) {
+ struct smtp_session *s = (struct smtp_session*)arg;
+ if (s->timer != 0) {
+ s->timer--;
+ }
+ }
+ smtp_process(arg, pcb, NULL);
+ return ERR_OK;
+}
+
+/** Raw API TCP sent callback */
+static err_t
+smtp_tcp_sent(void *arg, struct altcp_pcb *pcb, u16_t len)
+{
+ LWIP_UNUSED_ARG(len);
+
+ smtp_process(arg, pcb, NULL);
+
+ return ERR_OK;
+}
+
+/** Raw API TCP recv callback */
+static err_t
+smtp_tcp_recv(void *arg, struct altcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ LWIP_UNUSED_ARG(err);
+ if (p != NULL) {
+ altcp_recved(pcb, p->tot_len);
+ smtp_process(arg, pcb, p);
+ } else {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_tcp_recv: connection closed by remote host\n"));
+ smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CLOSED, 0, err);
+ }
+ return ERR_OK;
+}
+
+static err_t
+smtp_tcp_connected(void *arg, struct altcp_pcb *pcb, err_t err)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ if (err == ERR_OK) {
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_connected: Waiting for 220\n"));
+ } else {
+ /* shouldn't happen, but we still check 'err', only to be sure */
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_connected: %d\n", (int)err));
+ smtp_close((struct smtp_session*)arg, pcb, SMTP_RESULT_ERR_CONNECT, 0, err);
+ }
+ return ERR_OK;
+}
+
+#if LWIP_DNS
+/** DNS callback
+ * If ipaddr is non-NULL, resolving succeeded, otherwise it failed.
+ */
+static void
+smtp_dns_found(const char* hostname, const ip_addr_t *ipaddr, void *arg)
+{
+ struct smtp_session *s = (struct smtp_session*)arg;
+ struct altcp_pcb *pcb;
+ err_t err;
+ u8_t result;
+
+ LWIP_UNUSED_ARG(hostname);
+
+ if (ipaddr != NULL) {
+ pcb = smtp_setup_pcb(s, ipaddr);
+ if (pcb != NULL) {
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: hostname resolved, connecting\n"));
+ err = altcp_connect(pcb, ipaddr, smtp_server_port, smtp_tcp_connected);
+ if (err == ERR_OK) {
+ return;
+ }
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("tcp_connect failed: %d\n", (int)err));
+ result = SMTP_RESULT_ERR_CONNECT;
+ } else {
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_dns_found: failed to allocate tcp pcb\n"));
+ result = SMTP_RESULT_ERR_MEM;
+ err = ERR_MEM;
+ }
+ } else {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_dns_found: failed to resolve hostname: %s\n",
+ hostname));
+ pcb = NULL;
+ result = SMTP_RESULT_ERR_HOSTNAME;
+ err = ERR_ARG;
+ }
+ smtp_close(s, pcb, result, 0, err);
+}
+#endif /* LWIP_DNS */
+
+#if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
+
+/** Table 6-bit-index-to-ASCII used for base64-encoding */
+static const char base64_table[] = {
+ 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
+ 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
+ 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
+ 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h',
+ 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p',
+ 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
+ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ '+', '/'
+};
+
+/** Base64 encoding */
+static size_t
+smtp_base64_encode(char* target, size_t target_len, const char* source, size_t source_len)
+{
+ size_t i;
+ s8_t j;
+ size_t target_idx = 0;
+ size_t longer = (source_len % 3) ? (3 - (source_len % 3)) : 0;
+ size_t source_len_b64 = source_len + longer;
+ size_t len = (((source_len_b64) * 4) / 3);
+ u8_t x = 5;
+ u8_t current = 0;
+ LWIP_UNUSED_ARG(target_len);
+
+ LWIP_ASSERT("target_len is too short", target_len >= len);
+
+ for (i = 0; i < source_len_b64; i++) {
+ u8_t b = (i < source_len ? (u8_t)source[i] : 0);
+ for (j = 7; j >= 0; j--, x--) {
+ if ((b & (1 << j)) != 0) {
+ current = (u8_t)(current | (1U << x));
+ }
+ if (x == 0) {
+ target[target_idx++] = base64_table[current];
+ x = 6;
+ current = 0;
+ }
+ }
+ }
+ for (i = len - longer; i < len; i++) {
+ target[i] = '=';
+ }
+ return len;
+}
+#endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
+
+/** Parse pbuf to see if it contains the beginning of an answer.
+ * If so, it returns the contained response code as number between 1 and 999.
+ * If not, zero is returned.
+ *
+ * @param s smtp session struct
+ */
+static u16_t
+smtp_is_response(struct smtp_session *s)
+{
+ char digits[4];
+ long num;
+
+ if (s->p == NULL) {
+ return 0;
+ }
+ /* copy three digits and convert them to int */
+ if (pbuf_copy_partial(s->p, digits, 3, 0) != 3) {
+ /* pbuf was too short */
+ return 0;
+ }
+ digits[3] = 0;
+ num = strtol(digits, NULL, 10);
+ if ((num <= 0) || (num >= 1000)) {
+ /* failed to find response code at start of line */
+ return 0;
+ }
+ return (u16_t)num;
+}
+
+/** Parse pbuf to see if it contains a fully received answer.
+ * If one is found, ERR_OK is returned.
+ * If none is found, ERR_VAL is returned.
+ *
+ * A fully received answer is a 3-digit number followed by a space,
+ * some string and a CRLF as line ending.
+ *
+ * @param s smtp session struct
+ */
+static err_t
+smtp_is_response_finished(struct smtp_session *s)
+{
+ u8_t sp;
+ u16_t crlf;
+ u16_t offset;
+
+ if (s->p == NULL) {
+ return ERR_VAL;
+ }
+ offset = 0;
+again:
+ /* We could check the response number here, but we trust the
+ * protocol definition which says the client can rely on it being
+ * the same on every line. */
+
+ /* find CRLF */
+ if (offset > 0xFFFF - 4) {
+ /* would overflow */
+ return ERR_VAL;
+ }
+ crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, (u16_t)(offset + 4));
+ if (crlf == 0xFFFF) {
+ /* no CRLF found */
+ return ERR_VAL;
+ }
+ sp = pbuf_get_at(s->p, (u16_t)(offset + 3));
+ if (sp == '-') {
+ /* no space after response code -> try next line */
+ offset = (u16_t)(crlf + 2);
+ if (offset < crlf) {
+ /* overflow */
+ return ERR_VAL;
+ }
+ goto again;
+ } else if (sp == ' ') {
+ /* CRLF found after response code + space -> valid response */
+ return ERR_OK;
+ }
+ /* sp contains invalid character */
+ return ERR_VAL;
+}
+
+/** Prepare HELO/EHLO message */
+static enum smtp_session_state
+smtp_prepare_helo(struct smtp_session *s, u16_t *tx_buf_len, struct altcp_pcb *pcb)
+{
+ size_t ipa_len;
+ const char *ipa = ipaddr_ntoa(altcp_get_ip(pcb, 1));
+ LWIP_ASSERT("ipaddr_ntoa returned NULL", ipa != NULL);
+ ipa_len = strlen(ipa);
+ LWIP_ASSERT("string too long", ipa_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_EHLO_1_LEN-SMTP_CMD_EHLO_2_LEN));
+
+ *tx_buf_len = (u16_t)(SMTP_CMD_EHLO_1_LEN + (u16_t)ipa_len + SMTP_CMD_EHLO_2_LEN);
+ LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN);
+
+ SMEMCPY(s->tx_buf, SMTP_CMD_EHLO_1, SMTP_CMD_EHLO_1_LEN);
+ MEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN], ipa, ipa_len);
+ SMEMCPY(&s->tx_buf[SMTP_CMD_EHLO_1_LEN + ipa_len], SMTP_CMD_EHLO_2, SMTP_CMD_EHLO_2_LEN);
+ return SMTP_HELO;
+}
+
+#if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
+/** Parse last server response (in rx_buf) for supported authentication method,
+ * create data to send out (to tx_buf), set tx_data_len correctly
+ * and return the next state.
+ */
+static enum smtp_session_state
+smtp_prepare_auth_or_mail(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ /* check response for supported authentication method */
+ u16_t auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_SP);
+ if (auth == 0xFFFF) {
+ auth = pbuf_strstr(s->p, SMTP_KEYWORD_AUTH_EQ);
+ }
+ if (auth != 0xFFFF) {
+ u16_t crlf = pbuf_memfind(s->p, SMTP_CRLF, SMTP_CRLF_LEN, auth);
+ if ((crlf != 0xFFFF) && (crlf > auth)) {
+ /* use tx_buf temporarily */
+ u16_t copied = pbuf_copy_partial(s->p, s->tx_buf, (u16_t)(crlf - auth), auth);
+ if (copied != 0) {
+ char *sep = s->tx_buf + SMTP_KEYWORD_AUTH_LEN;
+ s->tx_buf[copied] = 0;
+#if SMTP_SUPPORT_AUTH_PLAIN
+ /* favour PLAIN over LOGIN since it involves less requests */
+ if (strstr(sep, SMTP_AUTH_PARAM_PLAIN) != NULL) {
+ size_t auth_len;
+ /* server supports AUTH PLAIN */
+ SMEMCPY(s->tx_buf, SMTP_CMD_AUTHPLAIN_1, SMTP_CMD_AUTHPLAIN_1_LEN);
+
+ /* add base64-encoded string "\0username\0password" */
+ auth_len = smtp_base64_encode(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN],
+ SMTP_TX_BUF_LEN - SMTP_CMD_AUTHPLAIN_1_LEN, SMTP_AUTH_PLAIN_DATA(s),
+ SMTP_AUTH_PLAIN_LEN(s));
+ LWIP_ASSERT("string too long", auth_len <= (SMTP_TX_BUF_LEN-SMTP_CMD_AUTHPLAIN_1_LEN-SMTP_CMD_AUTHPLAIN_2_LEN));
+ *tx_buf_len = (u16_t)(SMTP_CMD_AUTHPLAIN_1_LEN + SMTP_CMD_AUTHPLAIN_2_LEN + (u16_t)auth_len);
+ SMEMCPY(&s->tx_buf[SMTP_CMD_AUTHPLAIN_1_LEN + auth_len], SMTP_CMD_AUTHPLAIN_2,
+ SMTP_CMD_AUTHPLAIN_2_LEN);
+ return SMTP_AUTH_PLAIN;
+ } else
+#endif /* SMTP_SUPPORT_AUTH_PLAIN */
+ {
+#if SMTP_SUPPORT_AUTH_LOGIN
+ if (strstr(sep, SMTP_AUTH_PARAM_LOGIN) != NULL) {
+ /* server supports AUTH LOGIN */
+ *tx_buf_len = SMTP_CMD_AUTHLOGIN_LEN;
+ SMEMCPY(s->tx_buf, SMTP_CMD_AUTHLOGIN, SMTP_CMD_AUTHLOGIN_LEN);
+ return SMTP_AUTH_LOGIN_UNAME;
+ }
+#endif /* SMTP_SUPPORT_AUTH_LOGIN */
+ }
+ }
+ }
+ }
+ /* server didnt's send correct keywords for AUTH, try sending directly */
+ return smtp_prepare_mail(s, tx_buf_len);
+}
+#endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
+
+#if SMTP_SUPPORT_AUTH_LOGIN
+/** Send base64-encoded username */
+static enum smtp_session_state
+smtp_prepare_auth_login_uname(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN,
+ SMTP_USERNAME(s), strlen(SMTP_USERNAME(s)));
+ /* @todo: support base64-encoded longer than 64k */
+ LWIP_ASSERT("string too long", base64_len <= 0xffff);
+ LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN);
+ *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN);
+
+ SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN);
+ s->tx_buf[*tx_buf_len] = 0;
+ return SMTP_AUTH_LOGIN_PASS;
+}
+
+/** Send base64-encoded password */
+static enum smtp_session_state
+smtp_prepare_auth_login_pass(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ size_t base64_len = smtp_base64_encode(s->tx_buf, SMTP_TX_BUF_LEN,
+ SMTP_PASS(s), strlen(SMTP_PASS(s)));
+ /* @todo: support base64-encoded longer than 64k */
+ LWIP_ASSERT("string too long", base64_len <= 0xffff);
+ LWIP_ASSERT("tx_buf overflow detected", base64_len <= SMTP_TX_BUF_LEN - SMTP_CRLF_LEN);
+ *tx_buf_len = (u16_t)(base64_len + SMTP_CRLF_LEN);
+
+ SMEMCPY(&s->tx_buf[base64_len], SMTP_CRLF, SMTP_CRLF_LEN);
+ s->tx_buf[*tx_buf_len] = 0;
+ return SMTP_AUTH_LOGIN;
+}
+#endif /* SMTP_SUPPORT_AUTH_LOGIN */
+
+/** Prepare MAIL message */
+static enum smtp_session_state
+smtp_prepare_mail(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ char *target = s->tx_buf;
+ LWIP_ASSERT("tx_buf overflow detected", s->from_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_MAIL_1_LEN - SMTP_CMD_MAIL_2_LEN));
+ *tx_buf_len = (u16_t)(SMTP_CMD_MAIL_1_LEN + SMTP_CMD_MAIL_2_LEN + s->from_len);
+ target[*tx_buf_len] = 0;
+
+ SMEMCPY(target, SMTP_CMD_MAIL_1, SMTP_CMD_MAIL_1_LEN);
+ target += SMTP_CMD_MAIL_1_LEN;
+ MEMCPY(target, s->from, s->from_len);
+ target += s->from_len;
+ SMEMCPY(target, SMTP_CMD_MAIL_2, SMTP_CMD_MAIL_2_LEN);
+ return SMTP_MAIL;
+}
+
+/** Prepare RCPT message */
+static enum smtp_session_state
+smtp_prepare_rcpt(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ char *target = s->tx_buf;
+ LWIP_ASSERT("tx_buf overflow detected", s->to_len <= (SMTP_TX_BUF_LEN - SMTP_CMD_RCPT_1_LEN - SMTP_CMD_RCPT_2_LEN));
+ *tx_buf_len = (u16_t)(SMTP_CMD_RCPT_1_LEN + SMTP_CMD_RCPT_2_LEN + s->to_len);
+ target[*tx_buf_len] = 0;
+
+ SMEMCPY(target, SMTP_CMD_RCPT_1, SMTP_CMD_RCPT_1_LEN);
+ target += SMTP_CMD_RCPT_1_LEN;
+ MEMCPY(target, s->to, s->to_len);
+ target += s->to_len;
+ SMEMCPY(target, SMTP_CMD_RCPT_2, SMTP_CMD_RCPT_2_LEN);
+ return SMTP_RCPT;
+}
+
+/** Prepare header of body */
+static enum smtp_session_state
+smtp_prepare_header(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ char *target = s->tx_buf;
+ int len = SMTP_CMD_HEADER_1_LEN + SMTP_CMD_HEADER_2_LEN +
+ SMTP_CMD_HEADER_3_LEN + SMTP_CMD_HEADER_4_LEN + s->from_len + s->to_len +
+ s->subject_len;
+ LWIP_ASSERT("tx_buf overflow detected", len > 0 && len <= SMTP_TX_BUF_LEN);
+ *tx_buf_len = (u16_t)len;
+ target[*tx_buf_len] = 0;
+
+ SMEMCPY(target, SMTP_CMD_HEADER_1, SMTP_CMD_HEADER_1_LEN);
+ target += SMTP_CMD_HEADER_1_LEN;
+ MEMCPY(target, s->from, s->from_len);
+ target += s->from_len;
+ SMEMCPY(target, SMTP_CMD_HEADER_2, SMTP_CMD_HEADER_2_LEN);
+ target += SMTP_CMD_HEADER_2_LEN;
+ MEMCPY(target, s->to, s->to_len);
+ target += s->to_len;
+ SMEMCPY(target, SMTP_CMD_HEADER_3, SMTP_CMD_HEADER_3_LEN);
+ target += SMTP_CMD_HEADER_3_LEN;
+ MEMCPY(target, s->subject, s->subject_len);
+ target += s->subject_len;
+ SMEMCPY(target, SMTP_CMD_HEADER_4, SMTP_CMD_HEADER_4_LEN);
+
+ return SMTP_BODY;
+}
+
+/** Prepare QUIT message */
+static enum smtp_session_state
+smtp_prepare_quit(struct smtp_session *s, u16_t *tx_buf_len)
+{
+ *tx_buf_len = SMTP_CMD_QUIT_LEN;
+ s->tx_buf[*tx_buf_len] = 0;
+ SMEMCPY(s->tx_buf, SMTP_CMD_QUIT, SMTP_CMD_QUIT_LEN);
+ LWIP_ASSERT("tx_buf overflow detected", *tx_buf_len <= SMTP_TX_BUF_LEN);
+ return SMTP_CLOSED;
+}
+
+/** If in state SMTP_BODY, try to send more body data */
+static void
+smtp_send_body(struct smtp_session *s, struct altcp_pcb *pcb)
+{
+ err_t err;
+
+ if (s->state == SMTP_BODY) {
+#if SMTP_BODYDH
+ if (s->bodydh) {
+ smtp_send_body_data_handler(s, pcb);
+ } else
+#endif /* SMTP_BODYDH */
+ {
+ u16_t send_len = (u16_t)(s->body_len - s->body_sent);
+ if (send_len > 0) {
+ u16_t snd_buf = altcp_sndbuf(pcb);
+ if (send_len > snd_buf) {
+ send_len = snd_buf;
+ }
+ if (send_len > 0) {
+ /* try to send something out */
+ err = altcp_write(pcb, &s->body[s->body_sent], (u16_t)send_len, TCP_WRITE_FLAG_COPY);
+ if (err == ERR_OK) {
+ s->timer = SMTP_TIMEOUT_DATABLOCK;
+ s->body_sent = (u16_t)(s->body_sent + send_len);
+ if (s->body_sent < s->body_len) {
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: %d of %d bytes written\n",
+ s->body_sent, s->body_len));
+ }
+ }
+ }
+ }
+ }
+ if (s->body_sent == s->body_len) {
+ /* the whole body has been written, write last line */
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: body completely written (%d bytes), appending end-of-body\n",
+ s->body_len));
+ err = altcp_write(pcb, SMTP_CMD_BODY_FINISHED, SMTP_CMD_BODY_FINISHED_LEN, 0);
+ if (err == ERR_OK) {
+ s->timer = SMTP_TIMEOUT_DATATERM;
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_send_body: end-of-body written, changing state to %s\n",
+ smtp_state_str[SMTP_QUIT]));
+ /* last line written, change state, wait for confirmation */
+ s->state = SMTP_QUIT;
+ }
+ }
+ }
+}
+
+/** State machine-like implementation of an SMTP client.
+ */
+static void
+smtp_process(void *arg, struct altcp_pcb *pcb, struct pbuf *p)
+{
+ struct smtp_session* s = (struct smtp_session*)arg;
+ u16_t response_code = 0;
+ u16_t tx_buf_len = 0;
+ enum smtp_session_state next_state;
+
+ if (arg == NULL) {
+ /* already closed SMTP connection */
+ if (p != NULL) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("Received %d bytes after closing: %s\n",
+ p->tot_len, smtp_pbuf_str(p)));
+ pbuf_free(p);
+ }
+ return;
+ }
+
+ next_state = s->state;
+
+ if (p != NULL) {
+ /* received data */
+ if (s->p == NULL) {
+ s->p = p;
+ } else {
+ pbuf_cat(s->p, p);
+ }
+ } else {
+ /* idle timer, close connection if timed out */
+ if (s->timer == 0) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process: connection timed out, closing\n"));
+ smtp_close(s, pcb, SMTP_RESULT_ERR_TIMEOUT, 0, ERR_TIMEOUT);
+ return;
+ }
+ if (s->state == SMTP_BODY) {
+ smtp_send_body(s, pcb);
+ return;
+ }
+ }
+ response_code = smtp_is_response(s);
+ if (response_code) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: received response code: %d\n", response_code));
+ if (smtp_is_response_finished(s) != ERR_OK) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process: partly received response code: %d\n", response_code));
+ /* wait for next packet to complete the respone */
+ return;
+ }
+ } else {
+ if (s->p != NULL) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN, ("smtp_process: unknown data received (%s)\n",
+ smtp_pbuf_str(s->p)));
+ pbuf_free(s->p);
+ s->p = NULL;
+ }
+ return;
+ }
+
+ switch(s->state)
+ {
+ case(SMTP_NULL):
+ /* wait for 220 */
+ if (response_code == 220) {
+ /* then send EHLO */
+ next_state = smtp_prepare_helo(s, &tx_buf_len, pcb);
+ }
+ break;
+ case(SMTP_HELO):
+ /* wait for 250 */
+ if (response_code == 250) {
+#if SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN
+ /* then send AUTH or MAIL */
+ next_state = smtp_prepare_auth_or_mail(s, &tx_buf_len);
+ }
+ break;
+ case(SMTP_AUTH_LOGIN):
+ case(SMTP_AUTH_PLAIN):
+ /* wait for 235 */
+ if (response_code == 235) {
+#endif /* SMTP_SUPPORT_AUTH_PLAIN || SMTP_SUPPORT_AUTH_LOGIN */
+ /* send MAIL */
+ next_state = smtp_prepare_mail(s, &tx_buf_len);
+ }
+ break;
+#if SMTP_SUPPORT_AUTH_LOGIN
+ case(SMTP_AUTH_LOGIN_UNAME):
+ /* wait for 334 Username */
+ if (response_code == 334) {
+ if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_UNAME) != 0xFFFF) {
+ /* send username */
+ next_state = smtp_prepare_auth_login_uname(s, &tx_buf_len);
+ }
+ }
+ break;
+ case(SMTP_AUTH_LOGIN_PASS):
+ /* wait for 334 Password */
+ if (response_code == 334) {
+ if (pbuf_strstr(s->p, SMTP_RESP_LOGIN_PASS) != 0xFFFF) {
+ /* send username */
+ next_state = smtp_prepare_auth_login_pass(s, &tx_buf_len);
+ }
+ }
+ break;
+#endif /* SMTP_SUPPORT_AUTH_LOGIN */
+ case(SMTP_MAIL):
+ /* wait for 250 */
+ if (response_code == 250) {
+ /* send RCPT */
+ next_state = smtp_prepare_rcpt(s, &tx_buf_len);
+ }
+ break;
+ case(SMTP_RCPT):
+ /* wait for 250 */
+ if (response_code == 250) {
+ /* send DATA */
+ SMEMCPY(s->tx_buf, SMTP_CMD_DATA, SMTP_CMD_DATA_LEN);
+ tx_buf_len = SMTP_CMD_DATA_LEN;
+ next_state = SMTP_DATA;
+ }
+ break;
+ case(SMTP_DATA):
+ /* wait for 354 */
+ if (response_code == 354) {
+ /* send email header */
+ next_state = smtp_prepare_header(s, &tx_buf_len);
+ }
+ break;
+ case(SMTP_BODY):
+ /* nothing to be done here, handled somewhere else */
+ break;
+ case(SMTP_QUIT):
+ /* wait for 250 */
+ if (response_code == 250) {
+ /* send QUIT */
+ next_state = smtp_prepare_quit(s, &tx_buf_len);
+ }
+ break;
+ case(SMTP_CLOSED):
+ /* nothing to do, wait for connection closed from server */
+ return;
+ default:
+ LWIP_DEBUGF(SMTP_DEBUG_SERIOUS, ("Invalid state: %d/%s\n", (int)s->state,
+ smtp_state_str[s->state]));
+ break;
+ }
+ if (s->state == next_state) {
+ LWIP_DEBUGF(SMTP_DEBUG_WARN_STATE, ("smtp_process[%s]: unexpected response_code, closing: %d (%s)\n",
+ smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p)));
+ /* close connection */
+ smtp_close(s, pcb, SMTP_RESULT_ERR_SVR_RESP, response_code, ERR_OK);
+ return;
+ }
+ if (tx_buf_len > 0) {
+ SMTP_TX_BUF_MAX(tx_buf_len);
+ if (altcp_write(pcb, s->tx_buf, tx_buf_len, TCP_WRITE_FLAG_COPY) == ERR_OK) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: received command %d (%s)\n",
+ smtp_state_str[s->state], response_code, smtp_pbuf_str(s->p)));
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_process[%s]: sent %"U16_F" bytes: \"%s\"\n",
+ smtp_state_str[s->state], tx_buf_len, s->tx_buf));
+ s->timer = SMTP_TIMEOUT;
+ pbuf_free(s->p);
+ s->p = NULL;
+ LWIP_DEBUGF(SMTP_DEBUG_STATE, ("smtp_process: changing state from %s to %s\n",
+ smtp_state_str[s->state], smtp_state_str[next_state]));
+ s->state = next_state;
+ if (next_state == SMTP_BODY) {
+ /* try to stream-send body data right now */
+ smtp_send_body(s, pcb);
+ } else if (next_state == SMTP_CLOSED) {
+ /* sent out all data, delete structure */
+ altcp_arg(pcb, NULL);
+ smtp_free(s, SMTP_RESULT_OK, 0, ERR_OK);
+ }
+ }
+ }
+}
+
+#if SMTP_BODYDH
+/** Elementary sub-function to send data
+ *
+ * @returns: BDHALLDATASENT all data has been written
+ * BDHSOMEDATASENT some data has been written
+ * 0 no data has been written
+ */
+static int
+smtp_send_bodyh_data(struct altcp_pcb *pcb, const char **from, u16_t *howmany)
+{
+ err_t err;
+ u16_t len = *howmany;
+
+ len = (u16_t)LWIP_MIN(len, altcp_sndbuf(pcb));
+ err = altcp_write(pcb, *from, len, TCP_WRITE_FLAG_COPY);
+ if (err == ERR_OK) {
+ *from += len;
+ if ((*howmany -= len) > 0) {
+ return BDHSOMEDATASENT;
+ }
+ return BDHALLDATASENT;
+ }
+ return 0;
+}
+
+/** Same as smtp_send_mail_static, but uses a callback function to send body data
+ */
+err_t
+smtp_send_mail_bodycback(const char *from, const char* to, const char* subject,
+ smtp_bodycback_fn bodycback_fn, smtp_result_fn callback_fn, void* callback_arg)
+{
+ struct smtp_session* s;
+ size_t len;
+
+ s = (struct smtp_session*)SMTP_STATE_MALLOC(sizeof(struct smtp_session));
+ if (s == NULL) {
+ return ERR_MEM;
+ }
+ memset(s, 0, sizeof(struct smtp_session));
+ s->bodydh = (struct smtp_bodydh_state*)SMTP_BODYDH_MALLOC(sizeof(struct smtp_bodydh_state));
+ if (s->bodydh == NULL) {
+ SMTP_STATE_FREE(s);
+ return ERR_MEM;
+ }
+ memset(s->bodydh, 0, sizeof(struct smtp_bodydh));
+ /* initialize the structure */
+ s->from = from;
+ len = strlen(from);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->from_len = (u16_t)len;
+ s->to = to;
+ len = strlen(to);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->to_len = (u16_t)len;
+ s->subject = subject;
+ len = strlen(subject);
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->subject_len = (u16_t)len;
+ s->body = NULL;
+ LWIP_ASSERT("string is too long", len <= 0xffff);
+ s->callback_fn = callback_fn;
+ s->callback_arg = callback_arg;
+ s->bodydh->callback_fn = bodycback_fn;
+ s->bodydh->state = BDH_SENDING;
+ /* call the actual implementation of this function */
+ return smtp_send_mail_alloced(s);
+}
+
+static void
+smtp_send_body_data_handler(struct smtp_session *s, struct altcp_pcb *pcb)
+{
+ struct smtp_bodydh_state *bdh = s->bodydh;
+ int res = 0, ret;
+ LWIP_ASSERT("s != NULL", s != NULL);
+ LWIP_ASSERT("bodydh != NULL", bdh != NULL);
+
+ /* resume any leftovers from prior memory constraints */
+ if (s->body_len) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: resume\n"));
+ if((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len))
+ != BDHALLDATASENT) {
+ s->body_sent = s->body_len - 1;
+ return;
+ }
+ }
+ ret = res;
+ /* all data on buffer has been queued, resume execution */
+ if (bdh->state == BDH_SENDING) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: run\n"));
+ do {
+ ret |= res; /* remember if we once queued something to send */
+ bdh->exposed.length = 0;
+ if (bdh->callback_fn(s->callback_arg, &bdh->exposed) == BDH_DONE) {
+ bdh->state = BDH_STOP;
+ }
+ s->body = bdh->exposed.buffer;
+ s->body_len = bdh->exposed.length;
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: trying to send %u bytes\n", (unsigned int)s->body_len));
+ } while (s->body_len &&
+ ((res = smtp_send_bodyh_data(pcb, (const char **)&s->body, &s->body_len)) == BDHALLDATASENT)
+ && (bdh->state != BDH_STOP));
+ }
+ if ((bdh->state != BDH_SENDING) && (ret != BDHSOMEDATASENT)) {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: stop\n"));
+ s->body_sent = s->body_len;
+ } else {
+ LWIP_DEBUGF(SMTP_DEBUG_TRACE, ("smtp_send_body_data_handler: pause\n"));
+ s->body_sent = s->body_len - 1;
+ }
+}
+#endif /* SMTP_BODYDH */
+
+#endif /* LWIP_TCP && LWIP_CALLBACK_API */
diff --git a/lwip/src/apps/snmp/snmp_asn1.c b/lwip/src/apps/snmp/snmp_asn1.c
new file mode 100644
index 0000000..6df34db
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_asn1.c
@@ -0,0 +1,704 @@
+/**
+ * @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 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 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;
+ if (length_bytes > pbuf_stream->length) {
+ return ERR_VAL;
+ }
+ 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 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)
+{
+ u8_t data;
+
+ if ((len > 0) && (len < 5)) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+
+ if (data & 0x80) {
+ /* negative, start from -1 */
+ *value = -1;
+ *value = (*value << 8) | data;
+ } else {
+ /* positive, start from 0 */
+ *value = data;
+ }
+ len--;
+ /* shift in the remaining value */
+ while (len > 0) {
+ PBUF_OP_EXEC(snmp_pbuf_stream_read(pbuf_stream, &data));
+ *value = (*value << 8) | data;
+ len--;
+ }
+ 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;
+}
+
+#if LWIP_HAVE_INT64
+/**
+ * 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 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!!
+ */
+void
+snmp_asn1_enc_u64t_cnt(u64_t value, u16_t *octets_needed)
+{
+ /* check if high u32 is 0 */
+ if ((value >> 32) == 0) {
+ /* only low u32 is important */
+ snmp_asn1_enc_u32t_cnt((u32_t)value, octets_needed);
+ } else {
+ /* low u32 does not matter for length determination */
+ snmp_asn1_enc_u32t_cnt((u32_t)(value >> 32), octets_needed);
+ *octets_needed = *octets_needed + 4; /* add the 4 bytes of low u32 */
+ }
+}
+
+/**
+ * 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 64 bit 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 0xFFFFFFFFFFFFFFFF is preceded with 0x00 and the length is 9 octets!!
+ */
+err_t
+snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u64_t *value)
+{
+ u8_t data;
+
+ 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));
+ *value <<= 8;
+ *value |= data;
+ len--;
+ }
+
+ return ERR_OK;
+ }
+ }
+
+ return ERR_VAL;
+}
+
+/**
+ * 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 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, u64_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 > 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;
+}
+#endif
+
+#endif /* LWIP_SNMP */
diff --git a/lwip/src/apps/snmp/snmp_asn1.h b/lwip/src/apps/snmp/snmp_asn1.h
new file mode 100644
index 0000000..87c0973
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_asn1.h
@@ -0,0 +1,113 @@
+/**
+ * @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 and RFC1905) */
+#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_PDU_INFORM_REQ 6
+#define SNMP_ASN1_CONTEXT_PDU_V2_TRAP 7
+#define SNMP_ASN1_CONTEXT_PDU_REPORT 8
+
+#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_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_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_raw(struct snmp_pbuf_stream *pbuf_stream, const u8_t *raw, u16_t raw_len);
+
+#if LWIP_HAVE_INT64
+err_t snmp_asn1_dec_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t len, u64_t *value);
+void snmp_asn1_enc_u64t_cnt(u64_t value, u16_t *octets_needed);
+err_t snmp_asn1_enc_u64t(struct snmp_pbuf_stream *pbuf_stream, u16_t octets_needed, u64_t value);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_ASN1_H */
diff --git a/lwip/src/apps/snmp/snmp_core.c b/lwip/src/apps/snmp/snmp_core.c
new file mode 100644
index 0000000..15936b4
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_core.c
@@ -0,0 +1,1350 @@
+/**
+ * @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/v3 agent
+ * @ingroup apps
+ * SNMPv2c and SNMPv3 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
+ * without IPv6 statistics (TODO).\n
+ * Rewritten by Martin Hentschel <info@cl-soft.de> and
+ * Dirk Ziegelmeier <dziegel@gmx.de>\n
+ *
+ * 0 Agent Capabilities
+ * ====================
+ *
+ * Features:
+ * ---------
+ * - SNMPv2c support.
+ * - SNMPv3 support (a port to ARM mbedtls is provided, LWIP_SNMP_V3_MBEDTLS option).
+ * - 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).
+ *
+ * SNMPv3
+ * ------
+ * When SNMPv3 is used, several functions from snmpv3.h must be implemented
+ * by the user. This is mainly user management and persistence handling.
+ * The sample provided in lwip-contrib is insecure, don't use it in production
+ * systems, especially the missing persistence for engine boots variable
+ * simplifies replay attacks.
+ *
+ * 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
+#if SNMP_MAX_OBJ_ID_LEN > 255
+#error "SNMP_MAX_OBJ_ID_LEN must fit into an u8_t"
+#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 && LWIP_SNMP_V3
+#include "lwip/apps/snmp_mib2.h"
+#include "lwip/apps/snmp_snmpv2_framework.h"
+#include "lwip/apps/snmp_snmpv2_usm.h"
+static const struct snmp_mib *const default_mibs[] = { &mib2, &snmpframeworkmib, &snmpusmmib };
+static u8_t snmp_num_mibs = LWIP_ARRAYSIZE(default_mibs);
+#elif SNMP_LWIP_MIB2
+#include "lwip/apps/snmp_mib2.h"
+static const struct snmp_mib *const default_mibs[] = { &mib2 };
+static u8_t snmp_num_mibs = LWIP_ARRAYSIZE(default_mibs);
+#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;
+
+ /* InetAddressType + InetAddress */
+ idx = snmp_oid_to_ip(&oid[0], oid_len, 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 = (u8_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)
+{
+ return netif_get_index(netif);
+}
+
+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, 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, 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/lwip/src/apps/snmp/snmp_core_priv.h b/lwip/src/apps/snmp/snmp_core_priv.h
new file mode 100644
index 0000000..9532c05
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_core_priv.h
@@ -0,0 +1,83 @@
+/*
+ * 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_UNKNOWN_ENGINEID 30
+#define SNMP_ERR_UNKNOWN_SECURITYNAME 31
+#define SNMP_ERR_UNSUPPORTED_SECLEVEL 32
+#define SNMP_ERR_NOTINTIMEWINDOW 33
+#define SNMP_ERR_DECRYIPTION_ERROR 34
+
+#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/lwip/src/apps/snmp/snmp_mib2.c b/lwip/src/apps/snmp/snmp_mib2.c
new file mode 100644
index 0000000..3383e44
--- /dev/null
+++ b/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/lwip/src/apps/snmp/snmp_mib2_icmp.c b/lwip/src/apps/snmp/snmp_mib2_icmp.c
new file mode 100644
index 0000000..033d229
--- /dev/null
+++ b/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/lwip/src/apps/snmp/snmp_mib2_interfaces.c b/lwip/src/apps/snmp/snmp_mib2_interfaces.c
new file mode 100644
index 0000000..5f12dd5
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_mib2_interfaces.c
@@ -0,0 +1,368 @@
+/**
+ * @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_FOREACH(netif) {
+ num_netifs++;
+ }
+
+ *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_FOREACH(netif) {
+ if (netif_to_num(netif) == ifIndex) {
+ /* store netif pointer for subsequent operations (get/test/set) */
+ cell_instance->reference.ptr = netif;
+ return SNMP_ERR_NOERROR;
+ }
+ }
+
+ /* 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_FOREACH(netif) {
+ 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);
+ }
+
+ /* 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/lwip/src/apps/snmp/snmp_mib2_ip.c b/lwip/src/apps/snmp/snmp_mib2_ip.c
new file mode 100644
index 0000000..64c2d8e
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_mib2_ip.c
@@ -0,0 +1,731 @@
+/**
+ * @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_FOREACH(netif) {
+ 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);
+ }
+ }
+
+ /* 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_FOREACH(netif) {
+ 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);
+ }
+
+ /* 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_FOREACH(netif) {
+ 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);
+ }
+ }
+
+ /* 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_FOREACH(netif) {
+ 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);
+ }
+ }
+
+ /* 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/lwip/src/apps/snmp/snmp_mib2_snmp.c b/lwip/src/apps/snmp/snmp_mib2_snmp.c
new file mode 100644
index 0000000..ca22e41
--- /dev/null
+++ b/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/lwip/src/apps/snmp/snmp_mib2_system.c b/lwip/src/apps/snmp/snmp_mib2_system.c
new file mode 100644
index 0000000..71c1c29
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_mib2_system.c
@@ -0,0 +1,376 @@
+/**
+ * @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/lwip/src/apps/snmp/snmp_mib2_tcp.c b/lwip/src/apps/snmp/snmp_mib2_tcp.c
new file mode 100644
index 0000000..8a321f3
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_mib2_tcp.c
@@ -0,0 +1,607 @@
+/**
+ * @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);
+#if LWIP_HAVE_INT64
+ case 17: { /* tcpHCInSegs */
+ /* use the 32 bit counter for now... */
+ u64_t val64 = STATS_GET(mib2.tcpinsegs);
+ *((u64_t *)value) = val64;
+ }
+ return sizeof(u64_t);
+ case 18: { /* tcpHCOutSegs */
+ /* use the 32 bit counter for now... */
+ u64_t val64 = STATS_GET(mib2.tcpoutsegs);
+ *((u64_t *)value) = val64;
+ }
+ return sizeof(u64_t);
+#endif
+ 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);
+#if LWIP_HAVE_INT64
+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);
+#endif
+
+#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)
+#if LWIP_HAVE_INT64
+CREATE_LWIP_SYNC_NODE(17, tcp_HCInSegs)
+CREATE_LWIP_SYNC_NODE(18, tcp_HCOutSegs)
+#endif
+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,
+#if LWIP_HAVE_INT64
+ &SYNC_NODE_NAME(tcp_HCOutSegs).node.node,
+ &SYNC_NODE_NAME(tcp_ConnectionTable).node.node,
+#endif
+ &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/lwip/src/apps/snmp/snmp_mib2_udp.c b/lwip/src/apps/snmp/snmp_mib2_udp.c
new file mode 100644
index 0000000..d0bd5de
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_mib2_udp.c
@@ -0,0 +1,372 @@
+/**
+ * @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);
+#if LWIP_HAVE_INT64
+ case 8: { /* udpHCInDatagrams */
+ /* use the 32 bit counter for now... */
+ u64_t val64 = STATS_GET(mib2.udpindatagrams);
+ *((u64_t *)value) = val64;
+ }
+ return sizeof(u64_t);
+ case 9: { /* udpHCOutDatagrams */
+ /* use the 32 bit counter for now... */
+ u64_t val64 = STATS_GET(mib2.udpoutdatagrams);
+ *((u64_t *)value) = val64;
+ }
+ return sizeof(u64_t);
+#endif
+ 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);
+#if LWIP_HAVE_INT64
+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);
+#endif
+
+#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)
+#if LWIP_HAVE_INT64
+CREATE_LWIP_SYNC_NODE(8, udp_HCInDatagrams)
+CREATE_LWIP_SYNC_NODE(9, udp_HCOutDatagrams)
+#endif
+
+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
+#if LWIP_HAVE_INT64
+ ,
+ &SYNC_NODE_NAME(udp_HCInDatagrams).node.node,
+ &SYNC_NODE_NAME(udp_HCOutDatagrams).node.node
+#endif
+};
+
+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/lwip/src/apps/snmp/snmp_msg.c b/lwip/src/apps/snmp/snmp_msg.c
new file mode 100644
index 0000000..6a3fbf1
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_msg.c
@@ -0,0 +1,1952 @@
+/**
+ * @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_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+#endif
+
+#include <string.h>
+
+#define SNMP_V3_AUTH_FLAG 0x01
+#define SNMP_V3_PRIV_FLAG 0x02
+
+/* Security levels */
+#define SNMP_V3_NOAUTHNOPRIV 0x00
+#define SNMP_V3_AUTHNOPRIV SNMP_V3_AUTH_FLAG
+#define SNMP_V3_AUTHPRIV (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)
+
+/* 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;
+
+#if LWIP_SNMP_CONFIGURE_VERSIONS
+
+static u8_t v1_enabled = 1;
+static u8_t v2c_enabled = 1;
+static u8_t v3_enabled = 1;
+
+static u8_t
+snmp_version_enabled(u8_t version)
+{
+ LWIP_ASSERT("Invalid SNMP version", (version == SNMP_VERSION_1) || (version == SNMP_VERSION_2c)
+#if LWIP_SNMP_V3
+ || (version == SNMP_VERSION_3)
+#endif
+ );
+
+ if (version == SNMP_VERSION_1) {
+ return v1_enabled;
+ } else if (version == SNMP_VERSION_2c) {
+ return v2c_enabled;
+ }
+#if LWIP_SNMP_V3
+ else { /* version == SNMP_VERSION_3 */
+ return v3_enabled;
+ }
+#endif
+}
+
+u8_t
+snmp_v1_enabled(void)
+{
+ return snmp_version_enabled(SNMP_VERSION_1);
+}
+
+u8_t
+snmp_v2c_enabled(void)
+{
+ return snmp_version_enabled(SNMP_VERSION_2c);
+}
+
+u8_t
+snmp_v3_enabled(void)
+{
+ return snmp_version_enabled(SNMP_VERSION_3);
+}
+
+static void
+snmp_version_enable(u8_t version, u8_t enable)
+{
+ LWIP_ASSERT("Invalid SNMP version", (version == SNMP_VERSION_1) || (version == SNMP_VERSION_2c)
+#if LWIP_SNMP_V3
+ || (version == SNMP_VERSION_3)
+#endif
+ );
+
+ if (version == SNMP_VERSION_1) {
+ v1_enabled = enable;
+ } else if (version == SNMP_VERSION_2c) {
+ v2c_enabled = enable;
+ }
+#if LWIP_SNMP_V3
+ else { /* version == SNMP_VERSION_3 */
+ v3_enabled = enable;
+ }
+#endif
+}
+
+void
+snmp_v1_enable(u8_t enable)
+{
+ snmp_version_enable(SNMP_VERSION_1, enable);
+}
+
+void
+snmp_v2c_enable(u8_t enable)
+{
+ snmp_version_enable(SNMP_VERSION_2c, enable);
+}
+
+void
+snmp_v3_enable(u8_t enable)
+{
+ snmp_version_enable(SNMP_VERSION_3, enable);
+}
+
+#endif
+
+/**
+ * @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 LWIP_SNMP_V3
+ else {
+ struct snmp_varbind vb;
+
+ vb.next = NULL;
+ vb.prev = NULL;
+ vb.type = SNMP_ASN1_TYPE_COUNTER32;
+ vb.value_len = sizeof(u32_t);
+
+ switch (request.error_status) {
+ case SNMP_ERR_AUTHORIZATIONERROR: {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 5, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.wrongdigests;
+ }
+ break;
+ case SNMP_ERR_UNKNOWN_ENGINEID: {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 4, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.unknownengineids;
+ }
+ break;
+ case SNMP_ERR_UNKNOWN_SECURITYNAME: {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 3, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.unknownusernames;
+ }
+ break;
+ case SNMP_ERR_UNSUPPORTED_SECLEVEL: {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 1, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.unsupportedseclevels;
+ }
+ break;
+ case SNMP_ERR_NOTINTIMEWINDOW: {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 2, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.notintimewindows;
+ }
+ break;
+ case SNMP_ERR_DECRYIPTION_ERROR: {
+ static const u32_t oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 1, 6, 0 };
+ snmp_oid_assign(&vb.oid, oid, LWIP_ARRAYSIZE(oid));
+ vb.value = &snmp_stats.decryptionerrors;
+ }
+ break;
+ default:
+ /* Unknown or unhandled error_status */
+ err = ERR_ARG;
+ }
+
+ if (err == ERR_OK) {
+ snmp_append_outbound_varbind(&(request.outbound_pbuf_stream), &vb);
+ request.error_status = SNMP_ERR_NOERROR;
+ }
+
+ request.request_out_type = (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_REPORT);
+ request.request_id = request.msg_id;
+ }
+#endif
+
+ 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 LWIP_HAVE_INT64
+ 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;
+ }
+#endif
+
+ 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 LWIP_SNMP_V3
+ snmpv3_auth_algo_t auth;
+ snmpv3_priv_algo_t priv;
+#endif
+
+ 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
+ )
+#if LWIP_SNMP_CONFIGURE_VERSIONS
+ || (!snmp_version_enabled(s32_value))
+#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 */
+ strncpy((char *)request->community, snmp_community, SNMP_MAX_COMMUNITY_STR_LEN);
+ request->community[SNMP_MAX_COMMUNITY_STR_LEN] = 0; /* ensure NULL termination (strncpy does NOT guarantee it!) */
+ request->community_strlen = (u16_t)strnlen((char *)request->community, SNMP_MAX_COMMUNITY_STR_LEN);
+
+ /* 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));
+
+ /* 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;
+
+ /* 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));
+ request->msg_authentication_parameters_len = (u8_t)u16_value;
+
+ /* 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));
+ request->msg_privacy_parameters_len = (u8_t)u16_value;
+
+ /* validate securityParameters here (do this after decoding because we don't want to increase other counters for wrong frames)
+ * 1) securityParameters was correctly serialized if we reach here.
+ * 2) securityParameters are already cached.
+ * 3) if msgAuthoritativeEngineID is unknown, zero-length or too long:
+ b) https://tools.ietf.org/html/rfc3414#section-7
+ */
+ {
+ const char *eid;
+ u8_t eid_len;
+
+ snmpv3_get_engine_id(&eid, &eid_len);
+
+ if ((request->msg_authoritative_engine_id_len == 0) ||
+ (request->msg_authoritative_engine_id_len != eid_len) ||
+ (memcmp(eid, request->msg_authoritative_engine_id, eid_len) != 0)) {
+ snmp_stats.unknownengineids++;
+ request->msg_flags = 0; /* noauthnopriv */
+ request->error_status = SNMP_ERR_UNKNOWN_ENGINEID;
+ return ERR_OK;
+ }
+ }
+
+ /* 4) verify username */
+ if (snmpv3_get_user((char *)request->msg_user_name, &auth, NULL, &priv, NULL)) {
+ snmp_stats.unknownusernames++;
+ request->msg_flags = 0; /* noauthnopriv */
+ request->error_status = SNMP_ERR_UNKNOWN_SECURITYNAME;
+ return ERR_OK;
+ }
+
+ /* 5) verify security level */
+ switch (request->msg_flags & (SNMP_V3_AUTH_FLAG | SNMP_V3_PRIV_FLAG)) {
+ case SNMP_V3_NOAUTHNOPRIV:
+ if ((auth != SNMP_V3_AUTH_ALGO_INVAL) || (priv != SNMP_V3_PRIV_ALGO_INVAL)) {
+ /* Invalid security level for user */
+ snmp_stats.unsupportedseclevels++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+ return ERR_OK;
+ }
+ break;
+#if LWIP_SNMP_V3_CRYPTO
+ case SNMP_V3_AUTHNOPRIV:
+ if ((auth == SNMP_V3_AUTH_ALGO_INVAL) || (priv != SNMP_V3_PRIV_ALGO_INVAL)) {
+ /* Invalid security level for user */
+ snmp_stats.unsupportedseclevels++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+ return ERR_OK;
+ }
+ break;
+ case SNMP_V3_AUTHPRIV:
+ if ((auth == SNMP_V3_AUTH_ALGO_INVAL) || (priv == SNMP_V3_PRIV_ALGO_INVAL)) {
+ /* Invalid security level for user */
+ snmp_stats.unsupportedseclevels++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+ return ERR_OK;
+ }
+ break;
+#endif
+ default:
+ snmp_stats.unsupportedseclevels++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_UNSUPPORTED_SECLEVEL;
+ return ERR_OK;
+ }
+
+ /* 6) if securitylevel specifies authentication, authenticate message. */
+#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 hmac[LWIP_MAX(SNMP_V3_SHA_LEN, SNMP_V3_MD5_LEN)];
+ struct snmp_pbuf_stream auth_stream;
+
+ if (request->msg_authentication_parameters_len > SNMP_V3_MAX_AUTH_PARAM_LENGTH) {
+ snmp_stats.wrongdigests++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_AUTHORIZATIONERROR;
+ return ERR_OK;
+ }
+
+ /* Rewind stream */
+ IF_PARSE_EXEC(snmp_pbuf_stream_init(&auth_stream, request->inbound_pbuf, 0, request->inbound_pbuf->tot_len));
+ IF_PARSE_EXEC(snmp_pbuf_stream_seek_abs(&auth_stream, inbound_msgAuthenticationParameters_offset));
+ /* Set auth parameters to zero for verification */
+ IF_PARSE_EXEC(snmp_asn1_enc_raw(&auth_stream, zero_arr, request->msg_authentication_parameters_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, &auth, key, NULL, NULL));
+ IF_PARSE_EXEC(snmpv3_auth(&auth_stream, request->inbound_pbuf->tot_len, key, auth, hmac));
+
+ if (memcmp(request->msg_authentication_parameters, hmac, SNMP_V3_MAX_AUTH_PARAM_LENGTH)) {
+ snmp_stats.wrongdigests++;
+ request->msg_flags = SNMP_V3_NOAUTHNOPRIV;
+ request->error_status = SNMP_ERR_AUTHORIZATIONERROR;
+ return ERR_OK;
+ }
+
+ /* 7) if securitylevel specifies authentication, verify engineboots, enginetime and lastenginetime */
+ {
+ s32_t boots = snmpv3_get_engine_boots_internal();
+ if ((request->msg_authoritative_engine_boots != boots) || (boots == 2147483647UL)) {
+ snmp_stats.notintimewindows++;
+ request->msg_flags = SNMP_V3_AUTHNOPRIV;
+ request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
+ return ERR_OK;
+ }
+ }
+ {
+ s32_t time = snmpv3_get_engine_time_internal();
+ if (request->msg_authoritative_engine_time > time) {
+ snmp_stats.notintimewindows++;
+ request->msg_flags = SNMP_V3_AUTHNOPRIV;
+ request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
+ return ERR_OK;
+ } else if (time > 150) {
+ if (request->msg_authoritative_engine_time < time - 150) {
+ snmp_stats.notintimewindows++;
+ request->msg_flags = SNMP_V3_AUTHNOPRIV;
+ request->error_status = SNMP_ERR_NOTINTIMEWINDOW;
+ return ERR_OK;
+ }
+ }
+ }
+ }
+#endif
+
+ /* 8) if securitylevel specifies privacy, decrypt message. */
+#if LWIP_SNMP_V3_CRYPTO
+ if (request->msg_flags & SNMP_V3_PRIV_FLAG) {
+ /* Decrypt message */
+
+ u8_t key[20];
+
+ 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, &priv, key));
+ if (snmpv3_crypt(&pbuf_stream, tlv.value_len, key,
+ request->msg_privacy_parameters, request->msg_authoritative_engine_boots,
+ request->msg_authoritative_engine_time, priv, SNMP_V3_PRIV_MODE_DECRYPT) != ERR_OK) {
+ snmp_stats.decryptionerrors++;
+ request->msg_flags = SNMP_V3_AUTHNOPRIV;
+ request->error_status = SNMP_ERR_DECRYIPTION_ERROR;
+ return ERR_OK;
+ }
+ }
+#endif
+ /* 9) calculate max size of scoped pdu?
+ * 10) securityname for user is retrieved from usertable?
+ * 11) security data is cached?
+ * 12)
+ */
+
+ /* 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;
+ /* TODO: do we need to verify this contextengineid too? */
+
+ /* 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;
+ /* TODO: do we need to verify this contextname too? */
+ } 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;
+ }
+ request->request_type = tlv.type & SNMP_ASN1_DATATYPE_MASK;
+ request->request_out_type = (SNMP_ASN1_CLASS_CONTEXT | SNMP_ASN1_CONTENTTYPE_CONSTRUCTED | SNMP_ASN1_CONTEXT_PDU_GET_RESP);
+
+ /* 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, request->request_out_type, 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;
+#if LWIP_HAVE_INT64
+ case SNMP_ASN1_TYPE_COUNTER64:
+ if (varbind->value_len != sizeof(u64_t)) {
+ return ERR_VAL;
+ }
+ snmp_asn1_enc_u64t_cnt(*(u64_t *)varbind->value, &len->value_value_len);
+ break;
+#endif
+ 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;
+#if LWIP_HAVE_INT64
+ case SNMP_ASN1_TYPE_COUNTER64:
+ OVB_BUILD_EXEC(snmp_asn1_enc_u64t(pbuf_stream, len.value_value_len, *(u64_t *) varbind->value));
+ break;
+#endif
+ 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) );
+ OF_BUILD_EXEC( 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++) {
+ OF_BUILD_EXEC( 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, request->request_out_type, 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];
+ snmpv3_priv_algo_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];
+ snmpv3_auth_algo_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;
+#if LWIP_HAVE_INT64
+ case SNMP_ASN1_TYPE_COUNTER64:
+ VB_PARSE_EXEC(snmp_asn1_dec_u64t(&(enumerator->pbuf_stream), tlv.value_len, (u64_t *)varbind->value));
+ varbind->value_len = sizeof(u64_t);
+ break;
+#endif
+ 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/lwip/src/apps/snmp/snmp_msg.h b/lwip/src/apps/snmp/snmp_msg.h
new file mode 100644
index 0000000..903f08a
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_msg.h
@@ -0,0 +1,185 @@
+/**
+ * @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
+
+/* 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;
+
+ /* Usually response-pdu (2). When snmpv3 errors are detected report-pdu(8) */
+ u8_t request_out_type;
+
+#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_authentication_parameters_len;
+ u8_t msg_privacy_parameters[SNMP_V3_MAX_PRIV_PARAM_LENGTH];
+ u8_t msg_privacy_parameters_len;
+ 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/lwip/src/apps/snmp/snmp_netconn.c b/lwip/src/apps/snmp/snmp_netconn.c
new file mode 100644
index 0000000..70f8bfc
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_netconn.c
@@ -0,0 +1,122 @@
+/**
+ * @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"
+#include "lwip/prot/iana.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, LWIP_IANA_PORT_SNMP);
+#else /* LWIP_IPV6 */
+ conn = netconn_new(NETCONN_UDP);
+ netconn_bind(conn, IP4_ADDR_ANY, LWIP_IANA_PORT_SNMP);
+#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/lwip/src/apps/snmp/snmp_pbuf_stream.c b/lwip/src/apps/snmp/snmp_pbuf_stream.c
new file mode 100644
index 0000000..42867fb
--- /dev/null
+++ b/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/lwip/src/apps/snmp/snmp_pbuf_stream.h b/lwip/src/apps/snmp/snmp_pbuf_stream.h
new file mode 100644
index 0000000..682713f
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_pbuf_stream.h
@@ -0,0 +1,72 @@
+/**
+ * @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/lwip/src/apps/snmp/snmp_raw.c b/lwip/src/apps/snmp/snmp_raw.c
new file mode 100644
index 0000000..4135474
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_raw.c
@@ -0,0 +1,101 @@
+/**
+ * @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 "lwip/prot/iana.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, NULL);
+ err = udp_bind(snmp_pcb, IP_ANY_TYPE, LWIP_IANA_PORT_SNMP);
+ LWIP_ERROR("snmp_raw: Unable to bind PCB", (err == ERR_OK), return;);
+}
+
+#endif /* LWIP_SNMP && SNMP_USE_RAW */
diff --git a/lwip/src/apps/snmp/snmp_scalar.c b/lwip/src/apps/snmp/snmp_scalar.c
new file mode 100644
index 0000000..4c5b940
--- /dev/null
+++ b/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/lwip/src/apps/snmp/snmp_snmpv2_framework.c b/lwip/src/apps/snmp/snmp_snmpv2_framework.c
new file mode 100644
index 0000000..a5611db
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_snmpv2_framework.c
@@ -0,0 +1,90 @@
+/*
+Generated by LwipMibCompiler
+*/
+
+#include "lwip/apps/snmp_opts.h"
+
+#if LWIP_SNMP && LWIP_SNMP_V3 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/apps/snmp_snmpv2_framework.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+
+#include "lwip/sys.h"
+
+#include <string.h>
+
+const struct snmp_obj_id usmNoAuthProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 1, 1 } };
+const struct snmp_obj_id usmHMACMD5AuthProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 1, 2 } };
+const struct snmp_obj_id usmHMACSHAAuthProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 1, 3 } };
+/* .4 sha-224
+ * .5 sha-256
+ * .6 sha-384
+ * .7 sha-512
+ */
+
+const struct snmp_obj_id usmNoPrivProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 2, 1 } };
+const struct snmp_obj_id usmDESPrivProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 2, 2 } };
+/* .3 3des-ede */
+const struct snmp_obj_id usmAESPrivProtocol = { 10, { 1, 3, 6, 1, 6, 3, 10, 1, 2, 4 } };
+/* .5 unknown
+ * .6 unknown
+ * .7 unknown
+ */
+
+/* TODO: where should this value come from? */
+#define SNMP_FRAMEWORKMIB_SNMPENGINEMAXMESSAGESIZE 1500
+
+/* --- snmpFrameworkMIBObjects 1.3.6.1.6.3.10.2 ----------------------------------------------------- */
+static s16_t snmpengine_scalars_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+ const char *engineid;
+ u8_t engineid_len;
+
+ switch (node->oid) {
+ case 1: /* snmpEngineID */
+ snmpv3_get_engine_id(&engineid, &engineid_len);
+ MEMCPY(value, engineid, engineid_len);
+ return engineid_len;
+ case 2: /* snmpEngineBoots */
+ *(s32_t *)value = snmpv3_get_engine_boots_internal();
+ return sizeof(s32_t);
+ case 3: /* snmpEngineTime */
+ *(s32_t *)value = snmpv3_get_engine_time_internal();
+ return sizeof(s32_t);
+ case 4: /* snmpEngineMaxMessageSize */
+ *(s32_t *)value = SNMP_FRAMEWORKMIB_SNMPENGINEMAXMESSAGESIZE;
+ return sizeof(s32_t);
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG, ("snmpengine_scalars_get_value(): unknown id: %"S32_F"\n", node->oid));
+ return 0;
+ }
+}
+
+static const struct snmp_scalar_array_node_def snmpengine_scalars_nodes[] = {
+ {1, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpEngineID */
+ {2, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpEngineBoots */
+ {3, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpEngineTime */
+ {4, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY}, /* snmpEngineMaxMessageSize */
+};
+static const struct snmp_scalar_array_node snmpengine_scalars = SNMP_SCALAR_CREATE_ARRAY_NODE(1, snmpengine_scalars_nodes, snmpengine_scalars_get_value, NULL, NULL);
+
+static const struct snmp_node *const snmpframeworkmibobjects_subnodes[] = {
+ &snmpengine_scalars.node.node
+};
+static const struct snmp_tree_node snmpframeworkmibobjects_treenode = SNMP_CREATE_TREE_NODE(2, snmpframeworkmibobjects_subnodes);
+
+/* --- snmpFrameworkMIB ----------------------------------------------------- */
+static const struct snmp_node *const snmpframeworkmib_subnodes[] = {
+ &snmpframeworkmibobjects_treenode.node
+};
+static const struct snmp_tree_node snmpframeworkmib_root = SNMP_CREATE_TREE_NODE(10, snmpframeworkmib_subnodes);
+static const u32_t snmpframeworkmib_base_oid[] = {1, 3, 6, 1, 6, 3, 10};
+const struct snmp_mib snmpframeworkmib = {snmpframeworkmib_base_oid, LWIP_ARRAYSIZE(snmpframeworkmib_base_oid), &snmpframeworkmib_root.node};
+
+/* --- snmpFrameworkMIB ----------------------------------------------------- */
+#endif /* LWIP_SNMP */
diff --git a/lwip/src/apps/snmp/snmp_snmpv2_usm.c b/lwip/src/apps/snmp/snmp_snmpv2_usm.c
new file mode 100644
index 0000000..7490c9c
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_snmpv2_usm.c
@@ -0,0 +1,410 @@
+/*
+Generated by LwipMibCompiler
+*/
+
+#include "lwip/apps/snmp_opts.h"
+#if LWIP_SNMP && LWIP_SNMP_V3
+
+#include "lwip/apps/snmp_snmpv2_usm.h"
+#include "lwip/apps/snmp.h"
+#include "lwip/apps/snmp_core.h"
+#include "lwip/apps/snmp_scalar.h"
+#include "lwip/apps/snmp_table.h"
+#include "lwip/apps/snmpv3.h"
+#include "snmpv3_priv.h"
+
+#include "lwip/apps/snmp_snmpv2_framework.h"
+
+#include <string.h>
+
+/* --- usmUser 1.3.6.1.6.3.15.1.2 ----------------------------------------------------- */
+
+static const struct snmp_oid_range usmUserTable_oid_ranges[] = {
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff },
+ { 0, 0xff }, { 0, 0xff }, { 0, 0xff }, { 0, 0xff }
+};
+
+static void snmp_engineid_to_oid(const char *engineid, u32_t *oid, u32_t len)
+{
+ u8_t i;
+
+ for (i = 0; i < len; i++) {
+ oid[i] = engineid[i];
+ }
+}
+
+static void snmp_oid_to_name(char *name, const u32_t *oid, size_t len)
+{
+ u8_t i;
+
+ for (i = 0; i < len; i++) {
+ name[i] = (char)oid[i];
+ }
+}
+
+static void snmp_name_to_oid(const char *name, u32_t *oid, size_t len)
+{
+ u8_t i;
+
+ for (i = 0; i < len; i++) {
+ oid[i] = name[i];
+ }
+}
+
+static const struct snmp_obj_id *snmp_auth_algo_to_oid(snmpv3_auth_algo_t algo)
+{
+ if (algo == SNMP_V3_AUTH_ALGO_MD5) {
+ return &usmHMACMD5AuthProtocol;
+ } else if (algo == SNMP_V3_AUTH_ALGO_SHA) {
+ return &usmHMACMD5AuthProtocol;
+ }
+
+ return &usmNoAuthProtocol;
+}
+
+static const struct snmp_obj_id *snmp_priv_algo_to_oid(snmpv3_priv_algo_t algo)
+{
+ if (algo == SNMP_V3_PRIV_ALGO_DES) {
+ return &usmDESPrivProtocol;
+ } else if (algo == SNMP_V3_PRIV_ALGO_AES) {
+ return &usmAESPrivProtocol;
+ }
+
+ return &usmNoPrivProtocol;
+}
+
+char username[32];
+
+static snmp_err_t usmusertable_get_instance(const u32_t *column, const u32_t *row_oid, u8_t row_oid_len, struct snmp_node_instance *cell_instance)
+{
+ const char *engineid;
+ u8_t eid_len;
+
+ u32_t engineid_oid[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+
+ u8_t name_len;
+ u8_t engineid_len;
+
+ u8_t name_start;
+ u8_t engineid_start;
+
+ LWIP_UNUSED_ARG(column);
+
+ snmpv3_get_engine_id(&engineid, &eid_len);
+
+ engineid_len = (u8_t)row_oid[0];
+ engineid_start = 1;
+
+ if (engineid_len != eid_len) {
+ /* EngineID length does not match! */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ if (engineid_len > row_oid_len) {
+ /* row OID doesn't contain enough data according to engineid_len.*/
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(&row_oid[engineid_start], engineid_len, usmUserTable_oid_ranges, engineid_len)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ snmp_engineid_to_oid(engineid, engineid_oid, engineid_len);
+
+ /* Verify EngineID */
+ if (snmp_oid_equal(&row_oid[engineid_start], engineid_len, engineid_oid, engineid_len)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ name_len = (u8_t)row_oid[engineid_start + engineid_len];
+ name_start = engineid_start + engineid_len + 1;
+
+ if (name_len > SNMP_V3_MAX_USER_LENGTH) {
+ /* specified name is too long */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ if (1 + engineid_len + 1 + name_len != row_oid_len) {
+ /* Length of EngineID and name does not match row oid length. (+2 for length fields)*/
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* check if incoming OID length and if values are in plausible range */
+ if (!snmp_oid_in_range(&row_oid[name_start], name_len, usmUserTable_oid_ranges, name_len)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* Verify if user exists */
+ memset(username, 0, sizeof(username));
+ snmp_oid_to_name(username, &row_oid[name_start], name_len);
+ if (snmpv3_get_user(username, NULL, NULL, NULL, NULL) != ERR_OK) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ /* Save name in reference pointer to make it easier to handle later on */
+ cell_instance->reference.ptr = username;
+ cell_instance->reference_len = name_len;
+
+ /* user was found */
+ return SNMP_ERR_NOERROR;
+}
+
+/*
+ * valid oid options
+ * <oid>
+ * <oid>.<EngineID length>
+ * <oid>.<EngineID length>.<partial EngineID>
+ * <oid>.<EngineID length>.<EngineID>
+ * <oid>.<EngineID length>.<EngineID>.<UserName length>
+ * <oid>.<EngineID length>.<EngineID>.<UserName length>.<partial UserName>
+ * <oid>.<EngineID length>.<EngineID>.<UserName length>.<UserName>
+ *
+ */
+static snmp_err_t usmusertable_get_next_instance(const u32_t *column, struct snmp_obj_id *row_oid, struct snmp_node_instance *cell_instance)
+{
+ const char *engineid;
+ u8_t eid_len;
+
+ u32_t engineid_oid[SNMP_V3_MAX_ENGINE_ID_LENGTH];
+
+ u8_t name_len;
+ u8_t engineid_len;
+
+ u8_t name_start;
+ u8_t engineid_start = 1;
+ u8_t i;
+
+ struct snmp_next_oid_state state;
+
+ u32_t result_temp[LWIP_ARRAYSIZE(usmUserTable_oid_ranges)];
+
+ LWIP_UNUSED_ARG(column);
+
+ snmpv3_get_engine_id(&engineid, &eid_len);
+
+ /* If EngineID might be given */
+ if (row_oid->len > 0) {
+ engineid_len = (u8_t)row_oid->id[0];
+ engineid_start = 1;
+
+ if (engineid_len != eid_len) {
+ /* EngineID length does not match! */
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ if (engineid_len > row_oid->len) {
+ /* Verify partial EngineID */
+ snmp_engineid_to_oid(engineid, engineid_oid, row_oid->len - 1);
+ if (!snmp_oid_equal(&row_oid->id[engineid_start], row_oid->len - 1, engineid_oid, row_oid->len - 1)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ } else {
+ /* Verify complete EngineID */
+ snmp_engineid_to_oid(engineid, engineid_oid, engineid_len);
+ if (!snmp_oid_equal(&row_oid->id[engineid_start], engineid_len, engineid_oid, engineid_len)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ }
+
+ /* At this point, the given EngineID (partially) matches the local EngineID.*/
+
+ /* If name might also be given */
+ if (row_oid->len > engineid_start + engineid_len) {
+ name_len = (u8_t)row_oid->id[engineid_start + engineid_len];
+ name_start = engineid_start + engineid_len + 1;
+
+ if (name_len > SNMP_V3_MAX_USER_LENGTH) {
+ /* specified name is too long, max length is 32 according to mib file.*/
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+
+ if (row_oid->len < engineid_len + name_len + 2) {
+ /* Partial name given according to oid.*/
+ u8_t tmplen = row_oid->len - engineid_len - 2;
+ if (!snmp_oid_in_range(&row_oid->id[name_start], tmplen, usmUserTable_oid_ranges, tmplen)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ } else {
+ /* Full name given according to oid. Also test for too much data.*/
+ u8_t tmplen = row_oid->len - engineid_len - 2;
+ if (!snmp_oid_in_range(&row_oid->id[name_start], name_len, usmUserTable_oid_ranges, tmplen)) {
+ return SNMP_ERR_NOSUCHINSTANCE;
+ }
+ }
+
+ /* At this point the EngineID and (partial) UserName match the local EngineID and UserName.*/
+ }
+ }
+
+ /* init struct to search next oid */
+ snmp_next_oid_init(&state, row_oid->id, row_oid->len, result_temp, LWIP_ARRAYSIZE(usmUserTable_oid_ranges));
+
+ for (i = 0; i < snmpv3_get_amount_of_users(); i++) {
+ u32_t test_oid[LWIP_ARRAYSIZE(usmUserTable_oid_ranges)];
+
+ test_oid[0] = eid_len;
+ snmp_engineid_to_oid(engineid, &test_oid[1], eid_len);
+
+ snmpv3_get_username(username, i);
+
+ test_oid[1 + eid_len] = strlen(username);
+ snmp_name_to_oid(username, &test_oid[2 + eid_len], strlen(username));
+
+ /* check generated OID: is it a candidate for the next one? */
+ snmp_next_oid_check(&state, test_oid, (u8_t)(1 + eid_len + 1 + strlen(username)), 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);
+ /* store username for subsequent operations (get/test/set) */
+ memset(username, 0, sizeof(username));
+ snmpv3_get_username(username, LWIP_PTR_NUMERIC_CAST(u8_t, state.reference));
+ cell_instance->reference.ptr = username;
+ cell_instance->reference_len = strlen(username);
+ return SNMP_ERR_NOERROR;
+ }
+
+ /* not found */
+ return SNMP_ERR_NOSUCHINSTANCE;
+}
+
+static s16_t usmusertable_get_value(struct snmp_node_instance *cell_instance, void *value)
+{
+ snmpv3_user_storagetype_t storage_type;
+
+ switch (SNMP_TABLE_GET_COLUMN_FROM_OID(cell_instance->instance_oid.id)) {
+ case 3: /* usmUserSecurityName */
+ MEMCPY(value, cell_instance->reference.ptr, cell_instance->reference_len);
+ return (s16_t)cell_instance->reference_len;
+ case 4: /* usmUserCloneFrom */
+ MEMCPY(value, snmp_zero_dot_zero.id, snmp_zero_dot_zero.len * sizeof(u32_t));
+ return snmp_zero_dot_zero.len * sizeof(u32_t);
+ case 5: { /* usmUserAuthProtocol */
+ const struct snmp_obj_id *auth_algo;
+ snmpv3_auth_algo_t auth_algo_val;
+ snmpv3_get_user((const char *)cell_instance->reference.ptr, &auth_algo_val, NULL, NULL, NULL);
+ auth_algo = snmp_auth_algo_to_oid(auth_algo_val);
+ MEMCPY(value, auth_algo->id, auth_algo->len * sizeof(u32_t));
+ return auth_algo->len * sizeof(u32_t);
+ }
+ case 6: /* usmUserAuthKeyChange */
+ return 0;
+ case 7: /* usmUserOwnAuthKeyChange */
+ return 0;
+ case 8: { /* usmUserPrivProtocol */
+ const struct snmp_obj_id *priv_algo;
+ snmpv3_priv_algo_t priv_algo_val;
+ snmpv3_get_user((const char *)cell_instance->reference.ptr, NULL, NULL, &priv_algo_val, NULL);
+ priv_algo = snmp_priv_algo_to_oid(priv_algo_val);
+ MEMCPY(value, priv_algo->id, priv_algo->len * sizeof(u32_t));
+ return priv_algo->len * sizeof(u32_t);
+ }
+ case 9: /* usmUserPrivKeyChange */
+ return 0;
+ case 10: /* usmUserOwnPrivKeyChange */
+ return 0;
+ case 11: /* usmUserPublic */
+ /* TODO: Implement usmUserPublic */
+ return 0;
+ case 12: /* usmUserStorageType */
+ snmpv3_get_user_storagetype((const char *)cell_instance->reference.ptr, &storage_type);
+ *(s32_t *)value = storage_type;
+ return sizeof(s32_t);
+ case 13: /* usmUserStatus */
+ *(s32_t *)value = 1; /* active */
+ return sizeof(s32_t);
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG, ("usmusertable_get_value(): unknown id: %"S32_F"\n", SNMP_TABLE_GET_COLUMN_FROM_OID(cell_instance->instance_oid.id)));
+ return 0;
+ }
+}
+
+/* --- usmMIBObjects 1.3.6.1.6.3.15.1 ----------------------------------------------------- */
+static s16_t usmstats_scalars_get_value(const struct snmp_scalar_array_node_def *node, void *value)
+{
+ u32_t *uint_ptr = (u32_t *)value;
+ switch (node->oid) {
+ case 1: /* usmStatsUnsupportedSecLevels */
+ *uint_ptr = snmp_stats.unsupportedseclevels;
+ break;
+ case 2: /* usmStatsNotInTimeWindows */
+ *uint_ptr = snmp_stats.notintimewindows;
+ break;
+ case 3: /* usmStatsUnknownUserNames */
+ *uint_ptr = snmp_stats.unknownusernames;
+ break;
+ case 4: /* usmStatsUnknownEngineIDs */
+ *uint_ptr = snmp_stats.unknownengineids;
+ break;
+ case 5: /* usmStatsWrongDigests */
+ *uint_ptr = snmp_stats.wrongdigests;
+ break;
+ case 6: /* usmStatsDecryptionErrors */
+ *uint_ptr = snmp_stats.decryptionerrors;
+ break;
+ default:
+ LWIP_DEBUGF(SNMP_MIB_DEBUG, ("usmstats_scalars_get_value(): unknown id: %"S32_F"\n", node->oid));
+ return 0;
+ }
+
+ return sizeof(*uint_ptr);
+}
+
+/* --- snmpUsmMIB ----------------------------------------------------- */
+
+/* --- usmUser 1.3.6.1.6.3.15.1.2 ----------------------------------------------------- */
+
+static const struct snmp_table_col_def usmusertable_columns[] = {
+ {3, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserSecurityName */
+ {4, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserCloneFrom */
+ {5, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserAuthProtocol */
+ {6, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserAuthKeyChange */
+ {7, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserOwnAuthKeyChange */
+ {8, SNMP_ASN1_TYPE_OBJECT_ID, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserPrivProtocol */
+ {9, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserPrivKeyChange */
+ {10, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserOwnPrivKeyChange */
+ {11, SNMP_ASN1_TYPE_OCTET_STRING, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserPublic */
+ {12, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserStorageType */
+ {13, SNMP_ASN1_TYPE_INTEGER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmUserStatus */
+};
+static const struct snmp_table_node usmusertable = SNMP_TABLE_CREATE(2, usmusertable_columns, usmusertable_get_instance, usmusertable_get_next_instance, usmusertable_get_value, NULL, NULL);
+
+static const struct snmp_node *const usmuser_subnodes[] = {
+ &usmusertable.node.node
+};
+static const struct snmp_tree_node usmuser_treenode = SNMP_CREATE_TREE_NODE(2, usmuser_subnodes);
+
+/* --- usmMIBObjects 1.3.6.1.6.3.15.1 ----------------------------------------------------- */
+static const struct snmp_scalar_array_node_def usmstats_scalars_nodes[] = {
+ {1, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsUnsupportedSecLevels */
+ {2, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsNotInTimeWindows */
+ {3, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsUnknownUserNames */
+ {4, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsUnknownEngineIDs */
+ {5, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsWrongDigests */
+ {6, SNMP_ASN1_TYPE_COUNTER, SNMP_NODE_INSTANCE_READ_ONLY}, /* usmStatsDecryptionErrors */
+};
+static const struct snmp_scalar_array_node usmstats_scalars = SNMP_SCALAR_CREATE_ARRAY_NODE(1, usmstats_scalars_nodes, usmstats_scalars_get_value, NULL, NULL);
+
+static const struct snmp_node *const usmmibobjects_subnodes[] = {
+ &usmstats_scalars.node.node,
+ &usmuser_treenode.node
+};
+static const struct snmp_tree_node usmmibobjects_treenode = SNMP_CREATE_TREE_NODE(1, usmmibobjects_subnodes);
+
+/* --- snmpUsmMIB ----------------------------------------------------- */
+static const struct snmp_node *const snmpusmmib_subnodes[] = {
+ &usmmibobjects_treenode.node
+};
+static const struct snmp_tree_node snmpusmmib_root = SNMP_CREATE_TREE_NODE(15, snmpusmmib_subnodes);
+static const u32_t snmpusmmib_base_oid[] = {1, 3, 6, 1, 6, 3, 15};
+const struct snmp_mib snmpusmmib = {snmpusmmib_base_oid, LWIP_ARRAYSIZE(snmpusmmib_base_oid), &snmpusmmib_root.node};
+
+#endif /* LWIP_SNMP */
diff --git a/lwip/src/apps/snmp/snmp_table.c b/lwip/src/apps/snmp/snmp_table.c
new file mode 100644
index 0000000..4b77a47
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_table.c
@@ -0,0 +1,342 @@
+/**
+ * @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/lwip/src/apps/snmp/snmp_threadsync.c b/lwip/src/apps/snmp/snmp_threadsync.c
new file mode 100644
index 0000000..cfcfb27
--- /dev/null
+++ b/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/lwip/src/apps/snmp/snmp_traps.c b/lwip/src/apps/snmp/snmp_traps.c
new file mode 100644
index 0000000..5af55ce
--- /dev/null
+++ b/lwip/src/apps/snmp/snmp_traps.c
@@ -0,0 +1,454 @@
+/**
+ * @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 "lwip/prot/iana.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 err_t snmp_trap_header_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream);
+static err_t snmp_trap_varbind_enc(struct snmp_msg_trap *trap, struct snmp_pbuf_stream *pbuf_stream, struct snmp_varbind *varbinds);
+
+#define BUILD_EXEC(code) \
+ if ((code) != ERR_OK) { \
+ LWIP_DEBUGF(SNMP_DEBUG, ("SNMP error during creation of outbound trap frame!")); \
+ return ERR_ARG; \
+ }
+
+/** 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 into 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, LWIP_IANA_PORT_SNMP_TRAP);
+ 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 err_t
+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);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+
+ while (varbind != NULL) {
+ BUILD_EXEC( snmp_append_outbound_varbind(pbuf_stream, varbind) );
+
+ varbind = varbind->next;
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Encodes trap header from head to tail.
+ */
+static err_t
+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);
+ 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(trap->snmp_version, &tlv.value_len);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( 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);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( 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);
+ BUILD_EXEC( 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);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( 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));
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( 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));
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( 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);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( 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);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( 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);
+ BUILD_EXEC( snmp_ans1_enc_tlv(pbuf_stream, &tlv) );
+ BUILD_EXEC( snmp_asn1_enc_s32t(pbuf_stream, tlv.value_len, trap->ts) );
+
+ return ERR_OK;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/lwip/src/apps/snmp/snmpv3.c b/lwip/src/apps/snmp/snmpv3.c
new file mode 100644
index 0000000..ed5a076
--- /dev/null
+++ b/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.
+ */
+s32_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.
+ */
+s32_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 = snmpv3_get_engine_boots_internal();
+ 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/lwip/src/apps/snmp/snmpv3_mbedtls.c b/lwip/src/apps/snmp/snmpv3_mbedtls.c
new file mode 100644
index 0000000..48c1a81
--- /dev/null
+++ b/lwip/src/apps/snmp/snmpv3_mbedtls.c
@@ -0,0 +1,342 @@
+/**
+ * @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, snmpv3_auth_algo_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, snmpv3_priv_algo_t algo, snmpv3_priv_mode_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++) {
+ if (snmp_pbuf_stream_read(&read_stream, &in_bytes[j]) != ERR_OK) {
+ goto error;
+ }
+ }
+
+ if (mbedtls_cipher_update(&ctx, in_bytes, LWIP_ARRAYSIZE(in_bytes), out_bytes, &out_len) != 0) {
+ goto error;
+ }
+
+ if (snmp_pbuf_stream_writebuf(&write_stream, out_bytes, (u16_t)out_len) != ERR_OK) {
+ goto error;
+ }
+ }
+
+ out_len = LWIP_ARRAYSIZE(out_bytes);
+ if (mbedtls_cipher_finish(&ctx, out_bytes, &out_len) != 0) {
+ goto error;
+ }
+
+ if (snmp_pbuf_stream_writebuf(&write_stream, out_bytes, (u16_t)out_len) != ERR_OK) {
+ goto error;
+ }
+ } 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);
+
+ if (snmp_pbuf_stream_read(&read_stream, &in_byte) != ERR_OK) {
+ goto error;
+ }
+ if (mbedtls_cipher_update(&ctx, &in_byte, sizeof(in_byte), &out_byte, &out_len) != 0) {
+ goto error;
+ }
+ if (snmp_pbuf_stream_write(&write_stream, out_byte) != ERR_OK) {
+ goto error;
+ }
+ }
+ } 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 */
+ size_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 */
+ size_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/lwip/src/apps/snmp/snmpv3_priv.h b/lwip/src/apps/snmp/snmpv3_priv.h
new file mode 100644
index 0000000..323364c
--- /dev/null
+++ b/lwip/src/apps/snmp/snmpv3_priv.h
@@ -0,0 +1,69 @@
+/**
+ * @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 "lwip/apps/snmpv3.h"
+#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_MD5_LEN 16
+#define SNMP_V3_SHA_LEN 20
+
+typedef enum {
+ SNMP_V3_PRIV_MODE_DECRYPT = 0,
+ SNMP_V3_PRIV_MODE_ENCRYPT = 1
+} snmpv3_priv_mode_t;
+
+s32_t snmpv3_get_engine_boots_internal(void);
+err_t snmpv3_auth(struct snmp_pbuf_stream *stream, u16_t length, const u8_t *key, snmpv3_auth_algo_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, snmpv3_priv_algo_t algo, snmpv3_priv_mode_t mode);
+err_t snmpv3_build_priv_param(u8_t *priv_param);
+void snmpv3_enginetime_timer(void *arg);
+
+#endif
+
+#endif /* LWIP_HDR_APPS_SNMP_V3_PRIV_H */
diff --git a/lwip/src/apps/sntp/sntp.c b/lwip/src/apps/sntp/sntp.c
new file mode 100644
index 0000000..480af60
--- /dev/null
+++ b/lwip/src/apps/sntp/sntp.c
@@ -0,0 +1,819 @@
+/**
+ * @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:
+ * - 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
+
+/* 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 << 6)
+#define SNTP_LI_LAST_MINUTE_61_SEC (0x01 << 6)
+#define SNTP_LI_LAST_MINUTE_59_SEC (0x02 << 6)
+#define SNTP_LI_ALARM_CONDITION (0x03 << 6) /* (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 1970 and Feb 7, 2036 06:28:16 UTC (epoch 1) */
+#define DIFF_SEC_1970_2036 ((s32_t)2085978496L)
+
+/** Convert NTP timestamp fraction to microseconds.
+ */
+#ifndef SNTP_FRAC_TO_US
+# if LWIP_HAVE_INT64
+# define SNTP_FRAC_TO_US(f) ((u32_t)(((u64_t)(f) * 1000000UL) >> 32))
+# else
+# define SNTP_FRAC_TO_US(f) ((u32_t)(f) / 4295)
+# endif
+#endif /* !SNTP_FRAC_TO_US */
+
+/* Configure behaviour depending on native, microsecond or second precision.
+ * Treat NTP timestamps as signed two's-complement integers. This way,
+ * timestamps that have the MSB set simply become negative offsets from
+ * the epoch (Feb 7, 2036 06:28:16 UTC). Representable dates range from
+ * 1968 to 2104.
+ */
+#ifndef SNTP_SET_SYSTEM_TIME_NTP
+# ifdef SNTP_SET_SYSTEM_TIME_US
+# define SNTP_SET_SYSTEM_TIME_NTP(s, f) \
+ SNTP_SET_SYSTEM_TIME_US((u32_t)((s) + DIFF_SEC_1970_2036), SNTP_FRAC_TO_US(f))
+# else
+# define SNTP_SET_SYSTEM_TIME_NTP(s, f) \
+ SNTP_SET_SYSTEM_TIME((u32_t)((s) + DIFF_SEC_1970_2036))
+# endif
+#endif /* !SNTP_SET_SYSTEM_TIME_NTP */
+
+/* Get the system time either natively as NTP timestamp or convert from
+ * Unix time in seconds and microseconds. Take care to avoid overflow if the
+ * microsecond value is at the maximum of 999999. Also add 0.5 us fudge to
+ * avoid special values like 0, and to mask round-off errors that would
+ * otherwise break round-trip conversion identity.
+ */
+#ifndef SNTP_GET_SYSTEM_TIME_NTP
+# define SNTP_GET_SYSTEM_TIME_NTP(s, f) do { \
+ u32_t sec_, usec_; \
+ SNTP_GET_SYSTEM_TIME(sec_, usec_); \
+ (s) = (s32_t)(sec_ - DIFF_SEC_1970_2036); \
+ (f) = usec_ * 4295 - ((usec_ * 2143) >> 16) + 2147; \
+ } while (0)
+#endif /* !SNTP_GET_SYSTEM_TIME_NTP */
+
+/* Start offset of the timestamps to extract from the SNTP packet */
+#define SNTP_OFFSET_TIMESTAMPS \
+ (SNTP_OFFSET_TRANSMIT_TIME + 8 - sizeof(struct sntp_timestamps))
+
+/* Round-trip delay arithmetic helpers */
+#if SNTP_COMP_ROUNDTRIP
+# if !LWIP_HAVE_INT64
+# error "SNTP round-trip delay compensation requires 64-bit arithmetic"
+# endif
+# define SNTP_SEC_FRAC_TO_S64(s, f) \
+ ((s64_t)(((u64_t)(s) << 32) | (u32_t)(f)))
+# define SNTP_TIMESTAMP_TO_S64(t) \
+ SNTP_SEC_FRAC_TO_S64(lwip_ntohl((t).sec), lwip_ntohl((t).frac))
+#endif /* SNTP_COMP_ROUNDTRIP */
+
+/**
+ * 64-bit NTP timestamp, in network byte order.
+ */
+struct sntp_time {
+ u32_t sec;
+ u32_t frac;
+};
+
+/**
+ * Timestamps to be extracted from the NTP header.
+ */
+struct sntp_timestamps {
+#if SNTP_COMP_ROUNDTRIP || SNTP_CHECK_RESPONSE >= 2
+ struct sntp_time orig;
+ struct sntp_time recv;
+#endif
+ struct sntp_time xmit;
+};
+
+/**
+ * SNTP packet format (without optional fields)
+ * Timestamps are coded as 64 bits:
+ * - signed 32 bits seconds since Feb 07, 2036, 06:28:16 UTC (epoch 1)
+ * - unsigned 32 bits seconds fraction (2^32 = 1 second)
+ */
+#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. Stored in network byte order. */
+static struct sntp_time sntp_last_timestamp_sent;
+#endif /* SNTP_CHECK_RESPONSE >= 2 */
+
+#if defined(LWIP_DEBUG) && !defined(sntp_format_time)
+/* Debug print helper. */
+static const char *
+sntp_format_time(s32_t sec)
+{
+ time_t ut;
+ ut = (time_t)((time_t)sec + (time_t)DIFF_SEC_1970_2036);
+ return ctime(&ut);
+}
+#endif /* LWIP_DEBUG && !sntp_format_time */
+
+/**
+ * SNTP processing of received timestamp
+ */
+static void
+sntp_process(const struct sntp_timestamps *timestamps)
+{
+ s32_t sec;
+ u32_t frac;
+
+ sec = (s32_t)lwip_ntohl(timestamps->xmit.sec);
+ frac = lwip_ntohl(timestamps->xmit.frac);
+
+#if SNTP_COMP_ROUNDTRIP
+# if SNTP_CHECK_RESPONSE >= 2
+ if (timestamps->recv.sec != 0 || timestamps->recv.frac != 0)
+# endif
+ {
+ s32_t dest_sec;
+ u32_t dest_frac;
+ u32_t step_sec;
+
+ /* Get the destination time stamp, i.e. the current system time */
+ SNTP_GET_SYSTEM_TIME_NTP(dest_sec, dest_frac);
+
+ step_sec = (dest_sec < sec) ? ((u32_t)sec - (u32_t)dest_sec)
+ : ((u32_t)dest_sec - (u32_t)sec);
+ /* In order to avoid overflows, skip the compensation if the clock step
+ * is larger than about 34 years. */
+ if ((step_sec >> 30) == 0) {
+ s64_t t1, t2, t3, t4;
+
+ t4 = SNTP_SEC_FRAC_TO_S64(dest_sec, dest_frac);
+ t3 = SNTP_SEC_FRAC_TO_S64(sec, frac);
+ t1 = SNTP_TIMESTAMP_TO_S64(timestamps->orig);
+ t2 = SNTP_TIMESTAMP_TO_S64(timestamps->recv);
+ /* Clock offset calculation according to RFC 4330 */
+ t4 += ((t2 - t1) + (t3 - t4)) / 2;
+
+ sec = (s32_t)((u64_t)t4 >> 32);
+ frac = (u32_t)((u64_t)t4);
+ }
+ }
+#endif /* SNTP_COMP_ROUNDTRIP */
+
+ SNTP_SET_SYSTEM_TIME_NTP(sec, frac);
+ LWIP_UNUSED_ARG(frac); /* might be unused if only seconds are set */
+ LWIP_DEBUGF(SNTP_DEBUG_TRACE, ("sntp_process: %s, %" U32_F " us\n",
+ sntp_format_time(sec), SNTP_FRAC_TO_US(frac)));
+}
+
+/**
+ * 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 || SNTP_COMP_ROUNDTRIP
+ {
+ s32_t secs;
+ u32_t sec, frac;
+ /* Get the transmit timestamp */
+ SNTP_GET_SYSTEM_TIME_NTP(secs, frac);
+ sec = lwip_htonl((u32_t)secs);
+ frac = lwip_htonl(frac);
+
+# if SNTP_CHECK_RESPONSE >= 2
+ sntp_last_timestamp_sent.sec = sec;
+ sntp_last_timestamp_sent.frac = frac;
+# endif
+ req->transmit_timestamp[0] = sec;
+ req->transmit_timestamp[1] = frac;
+ }
+#endif /* SNTP_CHECK_RESPONSE >= 2 || SNTP_COMP_ROUNDTRIP */
+}
+
+/**
+ * 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)
+{
+ struct sntp_timestamps timestamps;
+ u8_t mode;
+ u8_t stratum;
+ err_t err;
+
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(pcb);
+
+ 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) {
+ mode = pbuf_get_at(p, SNTP_OFFSET_LI_VN_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))) {
+ stratum = pbuf_get_at(p, 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 {
+ pbuf_copy_partial(p, &timestamps, sizeof(timestamps), SNTP_OFFSET_TIMESTAMPS);
+#if SNTP_CHECK_RESPONSE >= 2
+ /* check originate_timetamp against sntp_last_timestamp_sent */
+ if (timestamps.orig.sec != sntp_last_timestamp_sent.sec ||
+ timestamps.orig.frac != sntp_last_timestamp_sent.frac) {
+ 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;
+ }
+ }
+ } 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) {
+ /* correct packet received: process it it */
+ sntp_process(&timestamps);
+
+ /* Set up timeout for next request (only if poll response was received)*/
+ if (sntp_opmode == SNTP_OPMODE_POLL) {
+ sys_untimeout(sntp_try_next_server, NULL);
+ sys_untimeout(sntp_request, NULL);
+
+ /* 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 == SNTP_ERR_KOD) {
+ /* KOD errors are only processed in case of an explicit poll response */
+ if (sntp_opmode == SNTP_OPMODE_POLL) {
+ /* Kiss-of-death packet. Use another server or increase UPDATE_DELAY. */
+ sntp_try_next_server(NULL);
+ }
+ } else {
+ /* ignore any broken packet, poll mode: retry after timeout to avoid flooding */
+ }
+}
+
+/** 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/lwip/src/apps/tftp/tftp_server.c b/lwip/src/apps/tftp/tftp_server.c
new file mode 100644
index 0000000..e6bb287
--- /dev/null
+++ b/lwip/src/apps/tftp/tftp_server.c
@@ -0,0 +1,414 @@
+/**
+ *
+ * @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 + 1];
+ char mode[TFTP_MAX_MODE_LEN + 1];
+ 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 - 1) > 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 - 1, 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_remove_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/lwip/src/core/altcp.c b/lwip/src/core/altcp.c
new file mode 100644
index 0000000..2941a6c
--- /dev/null
+++ b/lwip/src/core/altcp.c
@@ -0,0 +1,547 @@
+/**
+ * @file
+ * @defgroup altcp Application layered TCP
+ * @ingroup callbackstyle_api
+ * Application layered TCP connection API (to be used from TCPIP thread)\n
+ * This interface mimics the tcp callback API to the application while preventing
+ * direct linking (much like virtual functions).
+ * This way, an application can make use of other application layer protocols
+ * on top of TCP without knowing the details (e.g. TLS, proxy connection).
+ *
+ * This file contains the common functions for altcp to work.
+ */
+
+/*
+ * Copyright (c) 2017 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 <goldsimon@gmx.de>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/altcp.h"
+#include "lwip/priv/altcp_priv.h"
+#include "lwip/tcp.h"
+#include "lwip/mem.h"
+
+#include <string.h>
+
+extern const struct altcp_functions altcp_tcp_functions;
+
+struct altcp_pcb *
+altcp_alloc(void)
+{
+ struct altcp_pcb *ret = (struct altcp_pcb *)memp_malloc(MEMP_ALTCP_PCB);
+ if (ret != NULL) {
+ memset(ret, 0, sizeof(struct altcp_pcb));
+ }
+ return ret;
+}
+
+void
+altcp_free(struct altcp_pcb *conn)
+{
+ if (conn) {
+ if (conn->fns && conn->fns->dealloc) {
+ conn->fns->dealloc(conn);
+ }
+ memp_free(MEMP_ALTCP_PCB, conn);
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_arg()
+ */
+void
+altcp_arg(struct altcp_pcb *conn, void *arg)
+{
+ if (conn) {
+ conn->arg = arg;
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_accept()
+ */
+void
+altcp_accept(struct altcp_pcb *conn, altcp_accept_fn accept)
+{
+ if (conn != NULL) {
+ conn->accept = accept;
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_recv()
+ */
+void
+altcp_recv(struct altcp_pcb *conn, altcp_recv_fn recv)
+{
+ if (conn) {
+ conn->recv = recv;
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_sent()
+ */
+void
+altcp_sent(struct altcp_pcb *conn, altcp_sent_fn sent)
+{
+ if (conn) {
+ conn->sent = sent;
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_poll()
+ */
+void
+altcp_poll(struct altcp_pcb *conn, altcp_poll_fn poll, u8_t interval)
+{
+ if (conn) {
+ conn->poll = poll;
+ conn->pollinterval = interval;
+ if (conn->fns && conn->fns->set_poll) {
+ conn->fns->set_poll(conn, interval);
+ }
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_err()
+ */
+void
+altcp_err(struct altcp_pcb *conn, altcp_err_fn err)
+{
+ if (conn) {
+ conn->err = err;
+ }
+}
+
+/* Generic functions calling the "virtual" ones */
+
+/**
+ * @ingroup altcp
+ * @see tcp_recved()
+ */
+void
+altcp_recved(struct altcp_pcb *conn, u16_t len)
+{
+ if (conn && conn->fns && conn->fns->recved) {
+ conn->fns->recved(conn, len);
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_bind()
+ */
+err_t
+altcp_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port)
+{
+ if (conn && conn->fns && conn->fns->bind) {
+ return conn->fns->bind(conn, ipaddr, port);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_connect()
+ */
+err_t
+altcp_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
+{
+ if (conn && conn->fns && conn->fns->connect) {
+ return conn->fns->connect(conn, ipaddr, port, connected);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_listen_with_backlog_and_err()
+ */
+struct altcp_pcb *
+altcp_listen_with_backlog_and_err(struct altcp_pcb *conn, u8_t backlog, err_t *err)
+{
+ if (conn && conn->fns && conn->fns->listen) {
+ return conn->fns->listen(conn, backlog, err);
+ }
+ return NULL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_abort()
+ */
+void
+altcp_abort(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->abort) {
+ conn->fns->abort(conn);
+ }
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_close()
+ */
+err_t
+altcp_close(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->close) {
+ return conn->fns->close(conn);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_shutdown()
+ */
+err_t
+altcp_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx)
+{
+ if (conn && conn->fns && conn->fns->shutdown) {
+ return conn->fns->shutdown(conn, shut_rx, shut_tx);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_write()
+ */
+err_t
+altcp_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+ if (conn && conn->fns && conn->fns->write) {
+ return conn->fns->write(conn, dataptr, len, apiflags);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_output()
+ */
+err_t
+altcp_output(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->output) {
+ return conn->fns->output(conn);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_mss()
+ */
+u16_t
+altcp_mss(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->mss) {
+ return conn->fns->mss(conn);
+ }
+ return 0;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_sndbuf()
+ */
+u16_t
+altcp_sndbuf(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->sndbuf) {
+ return conn->fns->sndbuf(conn);
+ }
+ return 0;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_sndqueuelen()
+ */
+u16_t
+altcp_sndqueuelen(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->sndqueuelen) {
+ return conn->fns->sndqueuelen(conn);
+ }
+ return 0;
+}
+
+void
+altcp_nagle_disable(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->nagle_disable) {
+ conn->fns->nagle_disable(conn);
+ }
+}
+
+void
+altcp_nagle_enable(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->nagle_enable) {
+ conn->fns->nagle_enable(conn);
+ }
+}
+
+int
+altcp_nagle_disabled(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->nagle_disabled) {
+ return conn->fns->nagle_disabled(conn);
+ }
+ return 0;
+}
+
+/**
+ * @ingroup altcp
+ * @see tcp_setprio()
+ */
+void
+altcp_setprio(struct altcp_pcb *conn, u8_t prio)
+{
+ if (conn && conn->fns && conn->fns->setprio) {
+ conn->fns->setprio(conn, prio);
+ }
+}
+
+err_t
+altcp_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port)
+{
+ if (conn && conn->fns && conn->fns->addrinfo) {
+ return conn->fns->addrinfo(conn, local, addr, port);
+ }
+ return ERR_VAL;
+}
+
+ip_addr_t *
+altcp_get_ip(struct altcp_pcb *conn, int local)
+{
+ if (conn && conn->fns && conn->fns->getip) {
+ return conn->fns->getip(conn, local);
+ }
+ return NULL;
+}
+
+u16_t
+altcp_get_port(struct altcp_pcb *conn, int local)
+{
+ if (conn && conn->fns && conn->fns->getport) {
+ return conn->fns->getport(conn, local);
+ }
+ return 0;
+}
+
+#ifdef LWIP_DEBUG
+enum tcp_state
+altcp_dbg_get_tcp_state(struct altcp_pcb *conn)
+{
+ if (conn && conn->fns && conn->fns->dbg_get_tcp_state) {
+ return conn->fns->dbg_get_tcp_state(conn);
+ }
+ return CLOSED;
+}
+#endif
+
+/* Default implementations for the "virtual" functions */
+
+void
+altcp_default_set_poll(struct altcp_pcb *conn, u8_t interval)
+{
+ if (conn && conn->inner_conn) {
+ altcp_poll(conn->inner_conn, conn->poll, interval);
+ }
+}
+
+void
+altcp_default_recved(struct altcp_pcb *conn, u16_t len)
+{
+ if (conn && conn->inner_conn) {
+ altcp_recved(conn->inner_conn, len);
+ }
+}
+
+err_t
+altcp_default_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_bind(conn->inner_conn, ipaddr, port);
+ }
+ return ERR_VAL;
+}
+
+err_t
+altcp_default_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_shutdown(conn->inner_conn, shut_rx, shut_tx);
+ }
+ return ERR_VAL;
+}
+
+err_t
+altcp_default_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_write(conn->inner_conn, dataptr, len, apiflags);
+ }
+ return ERR_VAL;
+}
+
+err_t
+altcp_default_output(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_output(conn->inner_conn);
+ }
+ return ERR_VAL;
+}
+
+u16_t
+altcp_default_mss(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_mss(conn->inner_conn);
+ }
+ return 0;
+}
+
+u16_t
+altcp_default_sndbuf(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_sndbuf(conn->inner_conn);
+ }
+ return 0;
+}
+
+u16_t
+altcp_default_sndqueuelen(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_sndqueuelen(conn->inner_conn);
+ }
+ return 0;
+}
+
+void
+altcp_default_nagle_disable(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ altcp_nagle_disable(conn->inner_conn);
+ }
+}
+
+void
+altcp_default_nagle_enable(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ altcp_nagle_enable(conn->inner_conn);
+ }
+}
+
+int
+altcp_default_nagle_disabled(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_nagle_disabled(conn->inner_conn);
+ }
+ return 0;
+}
+
+void
+altcp_default_setprio(struct altcp_pcb *conn, u8_t prio)
+{
+ if (conn && conn->inner_conn) {
+ altcp_setprio(conn->inner_conn, prio);
+ }
+}
+
+void
+altcp_default_dealloc(struct altcp_pcb *conn)
+{
+ LWIP_UNUSED_ARG(conn);
+ /* nothing to do */
+}
+
+err_t
+altcp_default_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_get_tcp_addrinfo(conn->inner_conn, local, addr, port);
+ }
+ return ERR_VAL;
+}
+
+ip_addr_t *
+altcp_default_get_ip(struct altcp_pcb *conn, int local)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_get_ip(conn->inner_conn, local);
+ }
+ return NULL;
+}
+
+u16_t
+altcp_default_get_port(struct altcp_pcb *conn, int local)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_get_port(conn->inner_conn, local);
+ }
+ return 0;
+}
+
+#ifdef LWIP_DEBUG
+enum tcp_state
+altcp_default_dbg_get_tcp_state(struct altcp_pcb *conn)
+{
+ if (conn && conn->inner_conn) {
+ return altcp_dbg_get_tcp_state(conn->inner_conn);
+ }
+ return CLOSED;
+}
+#endif
+
+
+#endif /* LWIP_ALTCP */
diff --git a/lwip/src/core/altcp_tcp.c b/lwip/src/core/altcp_tcp.c
new file mode 100644
index 0000000..cbbc422
--- /dev/null
+++ b/lwip/src/core/altcp_tcp.c
@@ -0,0 +1,501 @@
+/**
+ * @file
+ * Application layered TCP connection API (to be used from TCPIP thread)\n
+ * This interface mimics the tcp callback API to the application while preventing
+ * direct linking (much like virtual functions).
+ * This way, an application can make use of other application layer protocols
+ * on top of TCP without knowing the details (e.g. TLS, proxy connection).
+ *
+ * This file contains the base implementation calling into tcp.
+ */
+
+/*
+ * Copyright (c) 2017 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 <goldsimon@gmx.de>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/altcp.h"
+#include "lwip/altcp_tcp.h"
+#include "lwip/priv/altcp_priv.h"
+#include "lwip/tcp.h"
+#include "lwip/mem.h"
+
+#include <string.h>
+
+#define ALTCP_TCP_ASSERT_CONN(conn) LWIP_ASSERT("conn->inner_conn == NULL", (conn)->inner_conn == NULL)
+#define ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb) do { \
+ LWIP_ASSERT("pcb mismatch", (conn)->state == tpcb); \
+ ALTCP_TCP_ASSERT_CONN(conn); } while(0)
+
+
+/* Variable prototype, the actual declaration is at the end of this file
+ since it contains pointers to static functions declared here */
+extern const struct altcp_functions altcp_tcp_functions;
+
+static void altcp_tcp_setup(struct altcp_pcb *conn, struct tcp_pcb *tpcb);
+
+/* callback functions for TCP */
+static err_t
+altcp_tcp_accept(void *arg, struct tcp_pcb *new_tpcb, err_t err)
+{
+ struct altcp_pcb *listen_conn = (struct altcp_pcb *)arg;
+ if (listen_conn && listen_conn->accept) {
+ /* create a new altcp_conn to pass to the next 'accept' callback */
+ struct altcp_pcb *new_conn = altcp_alloc();
+ if (new_conn == NULL) {
+ return ERR_MEM;
+ }
+ altcp_tcp_setup(new_conn, new_tpcb);
+ return listen_conn->accept(listen_conn->arg, new_conn, err);
+ }
+ return ERR_ARG;
+}
+
+static err_t
+altcp_tcp_connected(void *arg, struct tcp_pcb *tpcb, err_t err)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
+ if (conn->connected) {
+ return conn->connected(conn->arg, conn, err);
+ }
+ }
+ return ERR_OK;
+}
+
+static err_t
+altcp_tcp_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
+ if (conn->recv) {
+ return conn->recv(conn->arg, conn, p, err);
+ }
+ }
+ if (p != NULL) {
+ /* prevent memory leaks */
+ pbuf_free(p);
+ }
+ return ERR_OK;
+}
+
+static err_t
+altcp_tcp_sent(void *arg, struct tcp_pcb *tpcb, u16_t len)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
+ if (conn->sent) {
+ return conn->sent(conn->arg, conn, len);
+ }
+ }
+ return ERR_OK;
+}
+
+static err_t
+altcp_tcp_poll(void *arg, struct tcp_pcb *tpcb)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ ALTCP_TCP_ASSERT_CONN_PCB(conn, tpcb);
+ if (conn->poll) {
+ return conn->poll(conn->arg, conn);
+ }
+ }
+ return ERR_OK;
+}
+
+static void
+altcp_tcp_err(void *arg, err_t err)
+{
+ struct altcp_pcb *conn = (struct altcp_pcb *)arg;
+ if (conn) {
+ conn->state = NULL; /* already freed */
+ if (conn->err) {
+ conn->err(conn->arg, err);
+ }
+ altcp_free(conn);
+ }
+}
+
+/* setup functions */
+static void
+altcp_tcp_setup_callbacks(struct altcp_pcb *conn, struct tcp_pcb *tpcb)
+{
+ tcp_arg(tpcb, conn);
+ tcp_recv(tpcb, altcp_tcp_recv);
+ tcp_sent(tpcb, altcp_tcp_sent);
+ tcp_err(tpcb, altcp_tcp_err);
+ /* tcp_poll is set when interval is set by application */
+ /* listen is set totally different :-) */
+}
+
+static void
+altcp_tcp_setup(struct altcp_pcb *conn, struct tcp_pcb *tpcb)
+{
+ altcp_tcp_setup_callbacks(conn, tpcb);
+ conn->state = tpcb;
+ conn->fns = &altcp_tcp_functions;
+}
+
+struct altcp_pcb *
+altcp_tcp_new_ip_type(u8_t ip_type)
+{
+ struct altcp_pcb *ret = altcp_alloc();
+ if (ret != NULL) {
+ struct tcp_pcb *tpcb = tcp_new_ip_type(ip_type);
+ if (tpcb != NULL) {
+ altcp_tcp_setup(ret, tpcb);
+ } else {
+ /* tcp_pcb allocation failed -> free the altcp_pcb too */
+ altcp_free(ret);
+ ret = NULL;
+ }
+ }
+ return ret;
+}
+
+struct altcp_pcb *
+altcp_tcp_wrap(struct tcp_pcb *tpcb)
+{
+ if (tpcb != NULL) {
+ struct altcp_pcb *ret = altcp_alloc();
+ if (ret != NULL) {
+ altcp_tcp_setup(ret, tpcb);
+ return ret;
+ }
+ }
+ return NULL;
+}
+
+
+/* "virtual" functions calling into tcp */
+static void
+altcp_tcp_set_poll(struct altcp_pcb *conn, u8_t interval)
+{
+ if (conn != NULL) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ tcp_poll(pcb, altcp_tcp_poll, interval);
+ }
+}
+
+static void
+altcp_tcp_recved(struct altcp_pcb *conn, u16_t len)
+{
+ if (conn != NULL) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ tcp_recved(pcb, len);
+ }
+}
+
+static err_t
+altcp_tcp_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_bind(pcb, ipaddr, port);
+}
+
+static err_t
+altcp_tcp_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ conn->connected = connected;
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_connect(pcb, ipaddr, port, altcp_tcp_connected);
+}
+
+static struct altcp_pcb *
+altcp_tcp_listen(struct altcp_pcb *conn, u8_t backlog, err_t *err)
+{
+ struct tcp_pcb *pcb;
+ struct tcp_pcb *lpcb;
+ if (conn == NULL) {
+ return NULL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ lpcb = tcp_listen_with_backlog_and_err(pcb, backlog, err);
+ if (lpcb != NULL) {
+ conn->state = lpcb;
+ tcp_accept(lpcb, altcp_tcp_accept);
+ return conn;
+ }
+ return NULL;
+}
+
+static void
+altcp_tcp_abort(struct altcp_pcb *conn)
+{
+ if (conn != NULL) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ if (pcb) {
+ tcp_abort(pcb);
+ }
+ }
+}
+
+static err_t
+altcp_tcp_close(struct altcp_pcb *conn)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_close(pcb);
+}
+
+static err_t
+altcp_tcp_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_shutdown(pcb, shut_rx, shut_tx);
+}
+
+static err_t
+altcp_tcp_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_write(pcb, dataptr, len, apiflags);
+}
+
+static err_t
+altcp_tcp_output(struct altcp_pcb *conn)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_output(pcb);
+}
+
+static u16_t
+altcp_tcp_mss(struct altcp_pcb *conn)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return 0;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_mss(pcb);
+}
+
+static u16_t
+altcp_tcp_sndbuf(struct altcp_pcb *conn)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return 0;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_sndbuf(pcb);
+}
+
+static u16_t
+altcp_tcp_sndqueuelen(struct altcp_pcb *conn)
+{
+ struct tcp_pcb *pcb;
+ if (conn == NULL) {
+ return 0;
+ }
+ ALTCP_TCP_ASSERT_CONN(conn);
+ pcb = (struct tcp_pcb *)conn->state;
+ return tcp_sndqueuelen(pcb);
+}
+
+static void
+altcp_tcp_nagle_disable(struct altcp_pcb *conn)
+{
+ if (conn && conn->state) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ tcp_nagle_disable(pcb);
+ }
+}
+
+static void
+altcp_tcp_nagle_enable(struct altcp_pcb *conn)
+{
+ if (conn && conn->state) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ tcp_nagle_enable(pcb);
+ }
+}
+
+static int
+altcp_tcp_nagle_disabled(struct altcp_pcb *conn)
+{
+ if (conn && conn->state) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ return tcp_nagle_disabled(pcb);
+ }
+ return 0;
+}
+
+static void
+altcp_tcp_setprio(struct altcp_pcb *conn, u8_t prio)
+{
+ if (conn != NULL) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ tcp_setprio(pcb, prio);
+ }
+}
+
+static void
+altcp_tcp_dealloc(struct altcp_pcb *conn)
+{
+ LWIP_UNUSED_ARG(conn);
+ ALTCP_TCP_ASSERT_CONN(conn);
+ /* no private state to clean up */
+}
+
+static err_t
+altcp_tcp_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port)
+{
+ if (conn) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ return tcp_tcp_get_tcp_addrinfo(pcb, local, addr, port);
+ }
+ return ERR_VAL;
+}
+
+static ip_addr_t *
+altcp_tcp_get_ip(struct altcp_pcb *conn, int local)
+{
+ if (conn) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ if (pcb) {
+ if (local) {
+ return &pcb->local_ip;
+ } else {
+ return &pcb->remote_ip;
+ }
+ }
+ }
+ return NULL;
+}
+
+static u16_t
+altcp_tcp_get_port(struct altcp_pcb *conn, int local)
+{
+ if (conn) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ if (pcb) {
+ if (local) {
+ return pcb->local_port;
+ } else {
+ return pcb->remote_port;
+ }
+ }
+ }
+ return 0;
+}
+
+#ifdef LWIP_DEBUG
+static enum tcp_state
+altcp_tcp_dbg_get_tcp_state(struct altcp_pcb *conn)
+{
+ if (conn) {
+ struct tcp_pcb *pcb = (struct tcp_pcb *)conn->state;
+ ALTCP_TCP_ASSERT_CONN(conn);
+ if (pcb) {
+ return pcb->state;
+ }
+ }
+ return CLOSED;
+}
+#endif
+const struct altcp_functions altcp_tcp_functions = {
+ altcp_tcp_set_poll,
+ altcp_tcp_recved,
+ altcp_tcp_bind,
+ altcp_tcp_connect,
+ altcp_tcp_listen,
+ altcp_tcp_abort,
+ altcp_tcp_close,
+ altcp_tcp_shutdown,
+ altcp_tcp_write,
+ altcp_tcp_output,
+ altcp_tcp_mss,
+ altcp_tcp_sndbuf,
+ altcp_tcp_sndqueuelen,
+ altcp_tcp_nagle_disable,
+ altcp_tcp_nagle_enable,
+ altcp_tcp_nagle_disabled,
+ altcp_tcp_setprio,
+ altcp_tcp_dealloc,
+ altcp_tcp_get_tcp_addrinfo,
+ altcp_tcp_get_ip,
+ altcp_tcp_get_port
+#ifdef LWIP_DEBUG
+ , altcp_tcp_dbg_get_tcp_state
+#endif
+};
+
+#endif /* LWIP_ALTCP */
diff --git a/lwip/src/core/def.c b/lwip/src/core/def.c
index 352b552..c483aa6 100644
--- a/lwip/src/core/def.c
+++ b/lwip/src/core/def.c
@@ -2,6 +2,28 @@
* @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.
*/
/*
@@ -39,21 +61,11 @@
#include "lwip/opt.h"
#include "lwip/def.h"
-/**
- * These are reference implementations of the byte swapping functions.
- * Again with the aim of being simple, correct and fully portable.
- * Byte swapping is the second thing you would want to optimize. You will
- * need to port it to your architecture and in your cc.h:
- *
- * #define LWIP_PLATFORM_BYTESWAP 1
- * #define LWIP_PLATFORM_HTONS(x) <your_htons>
- * #define LWIP_PLATFORM_HTONL(x) <your_htonl>
- *
- * Note ntohs() and ntohl() are merely references to the htonx counterparts.
- */
+#include <string.h>
-#if (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN)
+#if BYTE_ORDER == LITTLE_ENDIAN
+#if !defined(lwip_htons)
/**
* Convert an u16_t from host- to network byte order.
*
@@ -63,21 +75,11 @@
u16_t
lwip_htons(u16_t n)
{
- return ((n & 0xff) << 8) | ((n & 0xff00) >> 8);
-}
-
-/**
- * Convert an u16_t from network- to host byte order.
- *
- * @param n u16_t in network byte order
- * @return n in host byte order
- */
-u16_t
-lwip_ntohs(u16_t n)
-{
- return lwip_htons(n);
+ return PP_HTONS(n);
}
+#endif /* lwip_htons */
+#if !defined(lwip_htonl)
/**
* Convert an u32_t from host- to network byte order.
*
@@ -87,22 +89,160 @@ lwip_ntohs(u16_t n)
u32_t
lwip_htonl(u32_t n)
{
- return ((n & 0xff) << 24) |
- ((n & 0xff00) << 8) |
- ((n & 0xff0000UL) >> 8) |
- ((n & 0xff000000UL) >> 24);
+ return PP_HTONL(n);
}
+#endif /* lwip_htonl */
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+
+#ifndef lwip_strnstr
/**
- * Convert an u32_t from network- to host byte order.
- *
- * @param n u32_t in network byte order
- * @return n in host byte order
+ * @ingroup sys_nonstandard
+ * lwIP default implementation for strnstr() non-standard function.
+ * This can be \#defined to strnstr() depending on your platform port.
*/
-u32_t
-lwip_ntohl(u32_t n)
+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)
{
- return lwip_htonl(n);
+ 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
-#endif /* (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) */
+#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)
+{
+ char *res = result;
+ char *tmp = result;
+ size_t res_left = bufsize;
+ size_t result_len;
+ int n = (number > 0) ? number : -number;
+
+ /* handle invalid bufsize */
+ if (bufsize < 2) {
+ if (bufsize == 1) {
+ *result = 0;
+ }
+ return;
+ }
+
+ /* ensure output string is zero terminated */
+ result[bufsize - 1] = 0;
+ result_len = 1;
+ /* create the string in a temporary buffer since we don't know how long
+ it will get */
+ tmp = &result[bufsize - 2];
+ if (n == 0) {
+ *tmp = '0';
+ tmp--;
+ result_len++;
+ }
+ while ((n != 0) && (result_len < (bufsize - 1))) {
+ char val = (char)('0' + (n % 10));
+ *tmp = val;
+ tmp--;
+ n = n / 10;
+ result_len++;
+ }
+
+ /* output sign first */
+ if (number < 0) {
+ *res = '-';
+ res++;
+ res_left--;
+ }
+ if (result_len > res_left) {
+ /* buffer is too small */
+ result[0] = '.';
+ result[1] = 0;
+ return;
+ }
+ /* copy from temporary buffer to output buffer */
+ memmove(res, tmp + 1, result_len);
+}
+#endif
diff --git a/lwip/src/core/dhcp.c b/lwip/src/core/dhcp.c
deleted file mode 100644
index 86b9cfe..0000000
--- a/lwip/src/core/dhcp.c
+++ /dev/null
@@ -1,1771 +0,0 @@
-/**
- * @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
-
-/** Option handling: options are parsed in dhcp_parse_reply
- * and saved in an array where other functions can load them from.
- * This might be moved into the struct dhcp (not necessarily since
- * lwIP is single-threaded and the array is only used while in recv
- * callback). */
-#define DHCP_OPTION_IDX_OVERLOAD 0
-#define DHCP_OPTION_IDX_MSG_TYPE 1
-#define DHCP_OPTION_IDX_SERVER_ID 2
-#define DHCP_OPTION_IDX_LEASE_TIME 3
-#define DHCP_OPTION_IDX_T1 4
-#define DHCP_OPTION_IDX_T2 5
-#define DHCP_OPTION_IDX_SUBNET_MASK 6
-#define DHCP_OPTION_IDX_ROUTER 7
-#define DHCP_OPTION_IDX_DNS_SERVER 8
-#define DHCP_OPTION_IDX_MAX (DHCP_OPTION_IDX_DNS_SERVER + DNS_MAX_SERVERS)
-
-/** Holds the decoded option values, only valid while in dhcp_recv.
- @todo: move this into struct dhcp? */
-u32_t dhcp_rx_options_val[DHCP_OPTION_IDX_MAX];
-/** Holds a flag which option was received and is contained in dhcp_rx_options_val,
- only valid while in dhcp_recv.
- @todo: move this into struct dhcp? */
-u8_t dhcp_rx_options_given[DHCP_OPTION_IDX_MAX];
-
-#ifdef DHCP_GLOBAL_XID
-static u32_t xid;
-static u8_t xid_initialised;
-#endif /* DHCP_GLOBAL_XID */
-
-#define dhcp_option_given(dhcp, idx) (dhcp_rx_options_given[idx] != 0)
-#define dhcp_got_option(dhcp, idx) (dhcp_rx_options_given[idx] = 1)
-#define dhcp_clear_option(dhcp, idx) (dhcp_rx_options_given[idx] = 0)
-#define dhcp_clear_all_options(dhcp) (memset(dhcp_rx_options_given, 0, sizeof(dhcp_rx_options_given)))
-#define dhcp_get_option_value(dhcp, idx) (dhcp_rx_options_val[idx])
-#define dhcp_set_option_value(dhcp, idx, val) (dhcp_rx_options_val[idx] = (val))
-
-
-/* DHCP client state machine functions */
-static err_t dhcp_discover(struct netif *netif);
-static err_t dhcp_select(struct netif *netif);
-static void dhcp_bind(struct netif *netif);
-#if DHCP_DOES_ARP_CHECK
-static err_t dhcp_decline(struct netif *netif);
-#endif /* DHCP_DOES_ARP_CHECK */
-static err_t dhcp_rebind(struct netif *netif);
-static err_t dhcp_reboot(struct netif *netif);
-static void dhcp_set_state(struct dhcp *dhcp, u8_t new_state);
-
-/* receive, unfold, parse and free incoming messages */
-static void dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
-
-/* set the DHCP timers */
-static void dhcp_timeout(struct netif *netif);
-static void dhcp_t1_timeout(struct netif *netif);
-static void dhcp_t2_timeout(struct netif *netif);
-
-/* build outgoing messages */
-/* create a DHCP message, fill in common headers */
-static err_t dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type);
-/* free a DHCP request */
-static void dhcp_delete_msg(struct dhcp *dhcp);
-/* add a DHCP option (type, then length in bytes) */
-static void dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len);
-/* add option values */
-static void dhcp_option_byte(struct dhcp *dhcp, u8_t value);
-static void dhcp_option_short(struct dhcp *dhcp, u16_t value);
-static void dhcp_option_long(struct dhcp *dhcp, u32_t value);
-#if LWIP_NETIF_HOSTNAME
-static void dhcp_option_hostname(struct dhcp *dhcp, struct netif *netif);
-#endif /* LWIP_NETIF_HOSTNAME */
-/* always add the DHCP options trailer to end and pad */
-static void dhcp_option_trailer(struct dhcp *dhcp);
-
-/**
- * Back-off the DHCP client (because of a received NAK response).
- *
- * Back-off the DHCP client because of a received NAK. Receiving a
- * NAK means the client asked for something non-sensible, for
- * example when it tries to renew a lease obtained on another network.
- *
- * We clear any existing set IP address and restart DHCP negotiation
- * afresh (as per RFC2131 3.2.3).
- *
- * @param netif the netif under DHCP control
- */
-static void
-dhcp_handle_nak(struct netif *netif)
-{
- struct dhcp *dhcp = netif->dhcp;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_nak(netif=%p) %c%c%"U16_F"\n",
- (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
- /* Set the interface down since the address must no longer be used, as per RFC2131 */
- netif_set_down(netif);
- /* remove IP address from interface */
- netif_set_ipaddr(netif, IP_ADDR_ANY);
- netif_set_gw(netif, IP_ADDR_ANY);
- netif_set_netmask(netif, IP_ADDR_ANY);
- /* Change to a defined state */
- dhcp_set_state(dhcp, DHCP_BACKING_OFF);
- /* We can immediately restart discovery */
- dhcp_discover(netif);
-}
-
-#if DHCP_DOES_ARP_CHECK
-/**
- * Checks if the offered IP address is already in use.
- *
- * It does so by sending an ARP request for the offered address and
- * entering CHECKING state. If no ARP reply is received within a small
- * interval, the address is assumed to be free for use by us.
- *
- * @param netif the netif under DHCP control
- */
-static void
-dhcp_check(struct netif *netif)
-{
- struct dhcp *dhcp = netif->dhcp;
- err_t result;
- u16_t msecs;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_check(netif=%p) %c%c\n", (void *)netif, (s16_t)netif->name[0],
- (s16_t)netif->name[1]));
- dhcp_set_state(dhcp, DHCP_CHECKING);
- /* create an ARP query for the offered IP address, expecting that no host
- responds, as the IP address should not be in use. */
- result = etharp_query(netif, &dhcp->offered_ip_addr, NULL);
- if (result != ERR_OK) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_check: could not perform ARP query\n"));
- }
- dhcp->tries++;
- msecs = 500;
- dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_check(): set request timeout %"U16_F" msecs\n", msecs));
-}
-#endif /* DHCP_DOES_ARP_CHECK */
-
-/**
- * Remember the configuration offered by a DHCP server.
- *
- * @param netif the netif under DHCP control
- */
-static void
-dhcp_handle_offer(struct netif *netif)
-{
- struct dhcp *dhcp = netif->dhcp;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_handle_offer(netif=%p) %c%c%"U16_F"\n",
- (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
- /* obtain the server address */
- if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SERVER_ID)) {
- ip4_addr_set_u32(&dhcp->server_ip_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SERVER_ID)));
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): server 0x%08"X32_F"\n",
- ip4_addr_get_u32(&dhcp->server_ip_addr)));
- /* remember offered address */
- ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_handle_offer(): offer for 0x%08"X32_F"\n",
- ip4_addr_get_u32(&dhcp->offered_ip_addr)));
-
- dhcp_select(netif);
- } else {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
- ("dhcp_handle_offer(netif=%p) did not get server ID!\n", (void*)netif));
- }
-}
-
-/**
- * Select a DHCP server offer out of all offers.
- *
- * Simply select the first offer received.
- *
- * @param netif the netif under DHCP control
- * @return lwIP specific error (see error.h)
- */
-static err_t
-dhcp_select(struct netif *netif)
-{
- struct dhcp *dhcp = netif->dhcp;
- err_t result;
- u16_t msecs;
-
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_select(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
- dhcp_set_state(dhcp, DHCP_REQUESTING);
-
- /* create and initialize the DHCP message header */
- result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
- if (result == ERR_OK) {
- dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
- dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
-
- /* MUST request the offered IP address */
- dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
- dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
-
- dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
- dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->server_ip_addr)));
-
- dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/);
- dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK);
- dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER);
- dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST);
- dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER);
-
-#if LWIP_NETIF_HOSTNAME
- dhcp_option_hostname(dhcp, netif);
-#endif /* LWIP_NETIF_HOSTNAME */
-
- dhcp_option_trailer(dhcp);
- /* shrink the pbuf to the actual content length */
- pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
-
- /* send broadcast to any DHCP server */
- udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
- dhcp_delete_msg(dhcp);
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_select: REQUESTING\n"));
- } else {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("dhcp_select: could not allocate DHCP request\n"));
- }
- dhcp->tries++;
- msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
- dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_select(): set request timeout %"U16_F" msecs\n", msecs));
- return result;
-}
-
-/**
- * The DHCP timer that checks for lease renewal/rebind timeouts.
- */
-void
-dhcp_coarse_tmr()
-{
- struct netif *netif = netif_list;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n"));
- /* iterate through all network interfaces */
- while (netif != NULL) {
- /* only act on DHCP configured interfaces */
- if (netif->dhcp != NULL) {
- /* timer is active (non zero), and triggers (zeroes) now? */
- if (netif->dhcp->t2_timeout-- == 1) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t2 timeout\n"));
- /* this clients' rebind timeout triggered */
- dhcp_t2_timeout(netif);
- /* timer is active (non zero), and triggers (zeroes) now */
- } else if (netif->dhcp->t1_timeout-- == 1) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_coarse_tmr(): t1 timeout\n"));
- /* this clients' renewal timeout triggered */
- dhcp_t1_timeout(netif);
- }
- }
- /* proceed to next netif */
- netif = netif->next;
- }
-}
-
-/**
- * DHCP transaction timeout handling
- *
- * A DHCP server is expected to respond within a short period of time.
- * This timer checks whether an outstanding DHCP request is timed out.
- */
-void
-dhcp_fine_tmr()
-{
- struct netif *netif = netif_list;
- /* loop through netif's */
- while (netif != NULL) {
- /* only act on DHCP configured interfaces */
- if (netif->dhcp != NULL) {
- /* timer is active (non zero), and is about to trigger now */
- if (netif->dhcp->request_timeout > 1) {
- netif->dhcp->request_timeout--;
- }
- else if (netif->dhcp->request_timeout == 1) {
- netif->dhcp->request_timeout--;
- /* { netif->dhcp->request_timeout == 0 } */
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_fine_tmr(): request timeout\n"));
- /* this client's request timeout triggered */
- dhcp_timeout(netif);
- }
- }
- /* proceed to next network interface */
- netif = netif->next;
- }
-}
-
-/**
- * A DHCP negotiation transaction, or ARP request, has timed out.
- *
- * The timer that was started with the DHCP or ARP request has
- * timed out, indicating no response was received in time.
- *
- * @param netif the netif under DHCP control
- */
-static void
-dhcp_timeout(struct netif *netif)
-{
- struct dhcp *dhcp = netif->dhcp;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout()\n"));
- /* back-off period has passed, or server selection timed out */
- if ((dhcp->state == DHCP_BACKING_OFF) || (dhcp->state == DHCP_SELECTING)) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_timeout(): restarting discovery\n"));
- dhcp_discover(netif);
- /* receiving the requested lease timed out */
- } else if (dhcp->state == DHCP_REQUESTING) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, DHCP request timed out\n"));
- if (dhcp->tries <= 5) {
- dhcp_select(netif);
- } else {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REQUESTING, releasing, restarting\n"));
- dhcp_release(netif);
- dhcp_discover(netif);
- }
-#if DHCP_DOES_ARP_CHECK
- /* received no ARP reply for the offered address (which is good) */
- } else if (dhcp->state == DHCP_CHECKING) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): CHECKING, ARP request timed out\n"));
- if (dhcp->tries <= 1) {
- dhcp_check(netif);
- /* no ARP replies on the offered address,
- looks like the IP address is indeed free */
- } else {
- /* bind the interface to the offered address */
- dhcp_bind(netif);
- }
-#endif /* DHCP_DOES_ARP_CHECK */
- }
- /* did not get response to renew request? */
- else if (dhcp->state == DHCP_RENEWING) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RENEWING, DHCP request timed out\n"));
- /* just retry renewal */
- /* note that the rebind timer will eventually time-out if renew does not work */
- dhcp_renew(netif);
- /* did not get response to rebind request? */
- } else if (dhcp->state == DHCP_REBINDING) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): REBINDING, DHCP request timed out\n"));
- if (dhcp->tries <= 8) {
- dhcp_rebind(netif);
- } else {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_timeout(): RELEASING, DISCOVERING\n"));
- dhcp_release(netif);
- dhcp_discover(netif);
- }
- } else if (dhcp->state == DHCP_REBOOTING) {
- if (dhcp->tries < REBOOT_TRIES) {
- dhcp_reboot(netif);
- } else {
- dhcp_discover(netif);
- }
- }
-}
-
-/**
- * The renewal period has timed out.
- *
- * @param netif the netif under DHCP control
- */
-static void
-dhcp_t1_timeout(struct netif *netif)
-{
- struct dhcp *dhcp = netif->dhcp;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_t1_timeout()\n"));
- if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) ||
- (dhcp->state == DHCP_RENEWING)) {
- /* just retry to renew - note that the rebind timer (t2) will
- * eventually time-out if renew tries fail. */
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
- ("dhcp_t1_timeout(): must renew\n"));
- /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
- DHCP_RENEWING, not DHCP_BOUND */
- dhcp_renew(netif);
- }
-}
-
-/**
- * The rebind period has timed out.
- *
- * @param netif the netif under DHCP control
- */
-static void
-dhcp_t2_timeout(struct netif *netif)
-{
- struct dhcp *dhcp = netif->dhcp;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_t2_timeout()\n"));
- if ((dhcp->state == DHCP_REQUESTING) || (dhcp->state == DHCP_BOUND) ||
- (dhcp->state == DHCP_RENEWING)) {
- /* just retry to rebind */
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
- ("dhcp_t2_timeout(): must rebind\n"));
- /* This slightly different to RFC2131: DHCPREQUEST will be sent from state
- DHCP_REBINDING, not DHCP_BOUND */
- dhcp_rebind(netif);
- }
-}
-
-/**
- * Handle a DHCP ACK packet
- *
- * @param netif the netif under DHCP control
- */
-static void
-dhcp_handle_ack(struct netif *netif)
-{
- struct dhcp *dhcp = netif->dhcp;
-#if LWIP_DNS
- u8_t n;
-#endif /* LWIP_DNS */
-
- /* clear options we might not get from the ACK */
- ip_addr_set_zero(&dhcp->offered_sn_mask);
- ip_addr_set_zero(&dhcp->offered_gw_addr);
-#if LWIP_DHCP_BOOTP_FILE
- ip_addr_set_zero(&dhcp->offered_si_addr);
-#endif /* LWIP_DHCP_BOOTP_FILE */
-
- /* lease time given? */
- if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_LEASE_TIME)) {
- /* remember offered lease time */
- dhcp->offered_t0_lease = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_LEASE_TIME);
- }
- /* renewal period given? */
- if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T1)) {
- /* remember given renewal period */
- dhcp->offered_t1_renew = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T1);
- } else {
- /* calculate safe periods for renewal */
- dhcp->offered_t1_renew = dhcp->offered_t0_lease / 2;
- }
-
- /* renewal period given? */
- if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_T2)) {
- /* remember given rebind period */
- dhcp->offered_t2_rebind = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_T2);
- } else {
- /* calculate safe periods for rebinding */
- dhcp->offered_t2_rebind = dhcp->offered_t0_lease;
- }
-
- /* (y)our internet address */
- ip_addr_copy(dhcp->offered_ip_addr, dhcp->msg_in->yiaddr);
-
-#if LWIP_DHCP_BOOTP_FILE
- /* copy boot server address,
- boot file name copied in dhcp_parse_reply if not overloaded */
- ip_addr_copy(dhcp->offered_si_addr, dhcp->msg_in->siaddr);
-#endif /* LWIP_DHCP_BOOTP_FILE */
-
- /* subnet mask given? */
- if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)) {
- /* remember given subnet mask */
- ip4_addr_set_u32(&dhcp->offered_sn_mask, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_SUBNET_MASK)));
- dhcp->subnet_mask_given = 1;
- } else {
- dhcp->subnet_mask_given = 0;
- }
-
- /* gateway router */
- if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_ROUTER)) {
- ip4_addr_set_u32(&dhcp->offered_gw_addr, htonl(dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_ROUTER)));
- }
-
-#if LWIP_DNS
- /* DNS servers */
- 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 = ERR_OK;
-
- LWIP_ERROR("netif != NULL", (netif != NULL), return ERR_ARG;);
- dhcp = netif->dhcp;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
- /* Remove the flag that says this netif is handled by DHCP,
- it is set when we succeeded starting. */
- netif->flags &= ~NETIF_FLAG_DHCP;
-
- /* check hwtype of the netif */
- if ((netif->flags & NETIF_FLAG_ETHARP) == 0) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): No ETHARP netif\n"));
- return ERR_ARG;
- }
-
- /* check MTU of the netif */
- if (netif->mtu < DHCP_MAX_MSG_LEN_MIN_REQUIRED) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): Cannot use this netif with DHCP: MTU is too small\n"));
- return ERR_MEM;
- }
-
- /* no DHCP client attached yet? */
- if (dhcp == NULL) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting new DHCP client\n"));
- dhcp = (struct dhcp *)mem_malloc(sizeof(struct dhcp));
- if (dhcp == NULL) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not allocate dhcp\n"));
- return ERR_MEM;
- }
- /* store this dhcp client in the netif */
- netif->dhcp = dhcp;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): allocated dhcp"));
- /* already has DHCP client attached */
- } else {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_start(): restarting DHCP configuration\n"));
- if (dhcp->pcb != NULL) {
- udp_remove(dhcp->pcb);
- }
- LWIP_ASSERT("pbuf p_out wasn't freed", dhcp->p_out == NULL);
- LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL );
- }
-
- /* clear data structure */
- memset(dhcp, 0, sizeof(struct dhcp));
- /* dhcp_set_state(&dhcp, DHCP_OFF); */
- /* allocate UDP PCB */
- dhcp->pcb = udp_new();
- if (dhcp->pcb == NULL) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): could not obtain pcb\n"));
- return ERR_MEM;
- }
- ip_set_option(dhcp->pcb, SOF_BROADCAST);
- /* set up local and remote port for the pcb */
- udp_bind(dhcp->pcb, IP_ADDR_ANY, DHCP_CLIENT_PORT);
- udp_connect(dhcp->pcb, IP_ADDR_ANY, DHCP_SERVER_PORT);
- /* set up the recv callback and argument */
- udp_recv(dhcp->pcb, dhcp_recv, netif);
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_start(): starting DHCP configuration\n"));
- /* (re)start the DHCP negotiation */
- result = dhcp_discover(netif);
- if (result != ERR_OK) {
- /* free resources allocated above */
- dhcp_stop(netif);
- return ERR_MEM;
- }
- /* Set the flag that says this netif is handled by DHCP. */
- netif->flags |= NETIF_FLAG_DHCP;
- return result;
-}
-
-/**
- * Inform a DHCP server of our manual configuration.
- *
- * This informs DHCP servers of our fixed IP address configuration
- * by sending an INFORM message. It does not involve DHCP address
- * configuration, it is just here to be nice to the network.
- *
- * @param netif The lwIP network interface
- */
-void
-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:
- netif_set_down(netif);
- dhcp->tries = 0;
- dhcp_reboot(netif);
- break;
- case DHCP_OFF:
- /* stay off */
- break;
- default:
- dhcp->tries = 0;
-#if LWIP_DHCP_AUTOIP_COOP
- if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
- autoip_stop(netif);
- dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
- }
-#endif /* LWIP_DHCP_AUTOIP_COOP */
- dhcp_discover(netif);
- break;
- }
-}
-
-#if DHCP_DOES_ARP_CHECK
-/**
- * Match an ARP reply with the offered IP address.
- *
- * @param netif the network interface on which the reply was received
- * @param addr The IP address we received a reply from
- */
-void dhcp_arp_reply(struct netif *netif, ip_addr_t *addr)
-{
- LWIP_ERROR("netif != NULL", (netif != NULL), return;);
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_arp_reply()\n"));
- /* is a DHCP client doing an ARP check? */
- if ((netif->dhcp != NULL) && (netif->dhcp->state == DHCP_CHECKING)) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_arp_reply(): CHECKING, arp reply for 0x%08"X32_F"\n",
- ip4_addr_get_u32(addr)));
- /* did a host respond with the address we
- were offered by the DHCP server? */
- if (ip_addr_cmp(addr, &netif->dhcp->offered_ip_addr)) {
- /* we will not accept the offered address */
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
- ("dhcp_arp_reply(): arp reply matched with offered address, declining\n"));
- dhcp_decline(netif);
- }
- }
-}
-
-/**
- * Decline an offered lease.
- *
- * Tell the DHCP server we do not accept the offered address.
- * One reason to decline the lease is when we find out the address
- * is already in use by another host (through ARP).
- *
- * @param netif the netif under DHCP control
- */
-static err_t
-dhcp_decline(struct netif *netif)
-{
- struct dhcp *dhcp = netif->dhcp;
- err_t result = ERR_OK;
- u16_t msecs;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline()\n"));
- dhcp_set_state(dhcp, DHCP_BACKING_OFF);
- /* create and initialize the DHCP message header */
- result = dhcp_create_msg(netif, dhcp, DHCP_DECLINE);
- if (result == ERR_OK) {
- dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
- dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
-
- dhcp_option_trailer(dhcp);
- /* resize pbuf to reflect true size of options */
- pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
-
- /* per section 4.4.4, broadcast DECLINE messages */
- udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
- dhcp_delete_msg(dhcp);
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_decline: BACKING OFF\n"));
- } else {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
- ("dhcp_decline: could not allocate DHCP request\n"));
- }
- dhcp->tries++;
- msecs = 10*1000;
- dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_decline(): set request timeout %"U16_F" msecs\n", msecs));
- return result;
-}
-#endif /* DHCP_DOES_ARP_CHECK */
-
-
-/**
- * Start the DHCP process, discover a DHCP server.
- *
- * @param netif the netif under DHCP control
- */
-static err_t
-dhcp_discover(struct netif *netif)
-{
- struct dhcp *dhcp = netif->dhcp;
- err_t result = ERR_OK;
- u16_t msecs;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover()\n"));
- ip_addr_set_any(&dhcp->offered_ip_addr);
- dhcp_set_state(dhcp, DHCP_SELECTING);
- /* create and initialize the DHCP message header */
- result = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER);
- if (result == ERR_OK) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n"));
-
- dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
- dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
-
- dhcp_option(dhcp, DHCP_OPTION_PARAMETER_REQUEST_LIST, 4/*num options*/);
- dhcp_option_byte(dhcp, DHCP_OPTION_SUBNET_MASK);
- dhcp_option_byte(dhcp, DHCP_OPTION_ROUTER);
- dhcp_option_byte(dhcp, DHCP_OPTION_BROADCAST);
- dhcp_option_byte(dhcp, DHCP_OPTION_DNS_SERVER);
-
- dhcp_option_trailer(dhcp);
-
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: realloc()ing\n"));
- pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
-
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, DHCP_SERVER_PORT)\n"));
- udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n"));
- dhcp_delete_msg(dhcp);
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover: SELECTING\n"));
- } else {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_discover: could not allocate DHCP request\n"));
- }
- dhcp->tries++;
-#if LWIP_DHCP_AUTOIP_COOP
- if(dhcp->tries >= LWIP_DHCP_AUTOIP_COOP_TRIES && dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_OFF) {
- dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_ON;
- autoip_start(netif);
- }
-#endif /* LWIP_DHCP_AUTOIP_COOP */
- msecs = (dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000;
- dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_discover(): set request timeout %"U16_F" msecs\n", msecs));
- return result;
-}
-
-
-/**
- * Bind the interface to the offered IP address.
- *
- * @param netif network interface to bind to the offered address
- */
-static void
-dhcp_bind(struct netif *netif)
-{
- u32_t timeout;
- struct dhcp *dhcp;
- ip_addr_t sn_mask, gw_addr;
- LWIP_ERROR("dhcp_bind: netif != NULL", (netif != NULL), return;);
- dhcp = netif->dhcp;
- LWIP_ERROR("dhcp_bind: dhcp != NULL", (dhcp != NULL), return;);
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num));
-
- /* temporary DHCP lease? */
- if (dhcp->offered_t1_renew != 0xffffffffUL) {
- /* set renewal period timer */
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t1 renewal timer %"U32_F" secs\n", dhcp->offered_t1_renew));
- timeout = (dhcp->offered_t1_renew + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
- if(timeout > 0xffff) {
- timeout = 0xffff;
- }
- dhcp->t1_timeout = (u16_t)timeout;
- if (dhcp->t1_timeout == 0) {
- dhcp->t1_timeout = 1;
- }
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t1_renew*1000));
- }
- /* set renewal period timer */
- if (dhcp->offered_t2_rebind != 0xffffffffUL) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_bind(): t2 rebind timer %"U32_F" secs\n", dhcp->offered_t2_rebind));
- timeout = (dhcp->offered_t2_rebind + DHCP_COARSE_TIMER_SECS / 2) / DHCP_COARSE_TIMER_SECS;
- if(timeout > 0xffff) {
- timeout = 0xffff;
- }
- dhcp->t2_timeout = (u16_t)timeout;
- if (dhcp->t2_timeout == 0) {
- dhcp->t2_timeout = 1;
- }
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_bind(): set request timeout %"U32_F" msecs\n", dhcp->offered_t2_rebind*1000));
- }
-
- /* If we have sub 1 minute lease, t2 and t1 will kick in at the same time. */
- if ((dhcp->t1_timeout >= dhcp->t2_timeout) && (dhcp->t2_timeout > 0)) {
- dhcp->t1_timeout = 0;
- }
-
- if (dhcp->subnet_mask_given) {
- /* copy offered network mask */
- ip_addr_copy(sn_mask, dhcp->offered_sn_mask);
- } else {
- /* subnet mask not given, choose a safe subnet mask given the network class */
- u8_t first_octet = ip4_addr1(&dhcp->offered_ip_addr);
- if (first_octet <= 127) {
- ip4_addr_set_u32(&sn_mask, PP_HTONL(0xff000000UL));
- } else if (first_octet >= 192) {
- ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffffff00UL));
- } else {
- ip4_addr_set_u32(&sn_mask, PP_HTONL(0xffff0000UL));
- }
- }
-
- ip_addr_copy(gw_addr, dhcp->offered_gw_addr);
- /* gateway address not given? */
- if (ip_addr_isany(&gw_addr)) {
- /* copy network address */
- ip_addr_get_network(&gw_addr, &dhcp->offered_ip_addr, &sn_mask);
- /* use first host address on network as gateway */
- ip4_addr_set_u32(&gw_addr, ip4_addr_get_u32(&gw_addr) | PP_HTONL(0x00000001UL));
- }
-
-#if LWIP_DHCP_AUTOIP_COOP
- if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
- autoip_stop(netif);
- dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
- }
-#endif /* LWIP_DHCP_AUTOIP_COOP */
-
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): IP: 0x%08"X32_F"\n",
- ip4_addr_get_u32(&dhcp->offered_ip_addr)));
- netif_set_ipaddr(netif, &dhcp->offered_ip_addr);
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): SN: 0x%08"X32_F"\n",
- ip4_addr_get_u32(&sn_mask)));
- netif_set_netmask(netif, &sn_mask);
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_STATE, ("dhcp_bind(): GW: 0x%08"X32_F"\n",
- ip4_addr_get_u32(&gw_addr)));
- netif_set_gw(netif, &gw_addr);
- /* bring the interface up */
- netif_set_up(netif);
- /* netif is now bound to DHCP leased address */
- dhcp_set_state(dhcp, DHCP_BOUND);
-}
-
-/**
- * Renew an existing DHCP lease at the involved DHCP server.
- *
- * @param netif network interface which must renew its lease
- */
-err_t
-dhcp_renew(struct netif *netif)
-{
- struct dhcp *dhcp = netif->dhcp;
- err_t result;
- u16_t msecs;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_renew()\n"));
- dhcp_set_state(dhcp, DHCP_RENEWING);
-
- /* create and initialize the DHCP message header */
- result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
- if (result == ERR_OK) {
- dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
- dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
-
-#if 0
- dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
- dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr));
-#endif
-
-#if 0
- dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
- dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr));
-#endif
-
-#if LWIP_NETIF_HOSTNAME
- dhcp_option_hostname(dhcp, netif);
-#endif /* LWIP_NETIF_HOSTNAME */
-
- /* append DHCP message trailer */
- dhcp_option_trailer(dhcp);
-
- pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
-
- udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif);
- dhcp_delete_msg(dhcp);
-
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew: RENEWING\n"));
- } else {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_renew: could not allocate DHCP request\n"));
- }
- dhcp->tries++;
- /* back-off on retries, but to a maximum of 20 seconds */
- msecs = dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000;
- dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_renew(): set request timeout %"U16_F" msecs\n", msecs));
- return result;
-}
-
-/**
- * Rebind with a DHCP server for an existing DHCP lease.
- *
- * @param netif network interface which must rebind with a DHCP server
- */
-static err_t
-dhcp_rebind(struct netif *netif)
-{
- struct dhcp *dhcp = netif->dhcp;
- err_t result;
- u16_t msecs;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind()\n"));
- dhcp_set_state(dhcp, DHCP_REBINDING);
-
- /* create and initialize the DHCP message header */
- result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
- if (result == ERR_OK) {
- dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
- dhcp_option_short(dhcp, DHCP_MAX_MSG_LEN(netif));
-
-#if LWIP_NETIF_HOSTNAME
- dhcp_option_hostname(dhcp, netif);
-#endif /* LWIP_NETIF_HOSTNAME */
-
-#if 0
- dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
- dhcp_option_long(dhcp, ntohl(dhcp->offered_ip_addr.addr));
-
- dhcp_option(dhcp, DHCP_OPTION_SERVER_ID, 4);
- dhcp_option_long(dhcp, ntohl(dhcp->server_ip_addr.addr));
-#endif
-
- dhcp_option_trailer(dhcp);
-
- pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
-
- /* broadcast to server */
- udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
- dhcp_delete_msg(dhcp);
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind: REBINDING\n"));
- } else {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_rebind: could not allocate DHCP request\n"));
- }
- dhcp->tries++;
- msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
- dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_rebind(): set request timeout %"U16_F" msecs\n", msecs));
- return result;
-}
-
-/**
- * Enter REBOOTING state to verify an existing lease
- *
- * @param netif network interface which must reboot
- */
-static err_t
-dhcp_reboot(struct netif *netif)
-{
- struct dhcp *dhcp = netif->dhcp;
- err_t result;
- u16_t msecs;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot()\n"));
- dhcp_set_state(dhcp, DHCP_REBOOTING);
-
- /* create and initialize the DHCP message header */
- result = dhcp_create_msg(netif, dhcp, DHCP_REQUEST);
- if (result == ERR_OK) {
- dhcp_option(dhcp, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
- dhcp_option_short(dhcp, 576);
-
- dhcp_option(dhcp, DHCP_OPTION_REQUESTED_IP, 4);
- dhcp_option_long(dhcp, ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
-
- dhcp_option_trailer(dhcp);
-
- pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
-
- /* broadcast to server */
- udp_sendto_if(dhcp->pcb, dhcp->p_out, IP_ADDR_BROADCAST, DHCP_SERVER_PORT, netif);
- dhcp_delete_msg(dhcp);
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot: REBOOTING\n"));
- } else {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_reboot: could not allocate DHCP request\n"));
- }
- dhcp->tries++;
- msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
- dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_reboot(): set request timeout %"U16_F" msecs\n", msecs));
- return result;
-}
-
-
-/**
- * Release a DHCP lease.
- *
- * @param netif network interface which must release its lease
- */
-err_t
-dhcp_release(struct netif *netif)
-{
- struct dhcp *dhcp = netif->dhcp;
- err_t result;
- u16_t msecs;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release()\n"));
- if (dhcp == NULL) {
- return ERR_ARG;
- }
-
- /* idle DHCP client */
- dhcp_set_state(dhcp, DHCP_OFF);
- /* clean old DHCP offer */
- ip_addr_set_zero(&dhcp->server_ip_addr);
- ip_addr_set_zero(&dhcp->offered_ip_addr);
- ip_addr_set_zero(&dhcp->offered_sn_mask);
- ip_addr_set_zero(&dhcp->offered_gw_addr);
-#if LWIP_DHCP_BOOTP_FILE
- ip_addr_set_zero(&dhcp->offered_si_addr);
-#endif /* LWIP_DHCP_BOOTP_FILE */
- dhcp->offered_t0_lease = dhcp->offered_t1_renew = dhcp->offered_t2_rebind = 0;
-
- /* create and initialize the DHCP message header */
- result = dhcp_create_msg(netif, dhcp, DHCP_RELEASE);
- if (result == ERR_OK) {
- dhcp_option_trailer(dhcp);
-
- pbuf_realloc(dhcp->p_out, sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + dhcp->options_out_len);
-
- udp_sendto_if(dhcp->pcb, dhcp->p_out, &dhcp->server_ip_addr, DHCP_SERVER_PORT, netif);
- dhcp_delete_msg(dhcp);
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release: RELEASED, DHCP_OFF\n"));
- } else {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("dhcp_release: could not allocate DHCP request\n"));
- }
- dhcp->tries++;
- msecs = dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000;
- dhcp->request_timeout = (msecs + DHCP_FINE_TIMER_MSECS - 1) / DHCP_FINE_TIMER_MSECS;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_release(): set request timeout %"U16_F" msecs\n", msecs));
- /* bring the interface down */
- netif_set_down(netif);
- /* remove IP address from interface */
- netif_set_ipaddr(netif, IP_ADDR_ANY);
- netif_set_gw(netif, IP_ADDR_ANY);
- netif_set_netmask(netif, IP_ADDR_ANY);
-
- return result;
-}
-
-/**
- * Remove the DHCP client from the interface.
- *
- * @param netif The network interface to stop DHCP on
- */
-void
-dhcp_stop(struct netif *netif)
-{
- struct dhcp *dhcp;
- LWIP_ERROR("dhcp_stop: netif != NULL", (netif != NULL), return;);
- dhcp = netif->dhcp;
- /* Remove the flag that says this netif is handled by DHCP. */
- netif->flags &= ~NETIF_FLAG_DHCP;
-
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_stop()\n"));
- /* netif is DHCP configured? */
- if (dhcp != NULL) {
-#if LWIP_DHCP_AUTOIP_COOP
- if(dhcp->autoip_coop_state == DHCP_AUTOIP_COOP_STATE_ON) {
- autoip_stop(netif);
- dhcp->autoip_coop_state = DHCP_AUTOIP_COOP_STATE_OFF;
- }
-#endif /* LWIP_DHCP_AUTOIP_COOP */
-
- if (dhcp->pcb != NULL) {
- udp_remove(dhcp->pcb);
- dhcp->pcb = NULL;
- }
- LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
- dhcp_set_state(dhcp, DHCP_OFF);
- }
-}
-
-/*
- * Set the DHCP state of a DHCP client.
- *
- * If the state changed, reset the number of tries.
- */
-static void
-dhcp_set_state(struct dhcp *dhcp, u8_t new_state)
-{
- if (new_state != dhcp->state) {
- dhcp->state = new_state;
- dhcp->tries = 0;
- dhcp->request_timeout = 0;
- }
-}
-
-/*
- * Concatenate an option type and length field to the outgoing
- * DHCP message.
- *
- */
-static void
-dhcp_option(struct dhcp *dhcp, u8_t option_type, u8_t option_len)
-{
- LWIP_ASSERT("dhcp_option: dhcp->options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U + option_len <= DHCP_OPTIONS_LEN);
- dhcp->msg_out->options[dhcp->options_out_len++] = option_type;
- dhcp->msg_out->options[dhcp->options_out_len++] = option_len;
-}
-/*
- * Concatenate a single byte to the outgoing DHCP message.
- *
- */
-static void
-dhcp_option_byte(struct dhcp *dhcp, u8_t value)
-{
- LWIP_ASSERT("dhcp_option_byte: dhcp->options_out_len < DHCP_OPTIONS_LEN", dhcp->options_out_len < DHCP_OPTIONS_LEN);
- dhcp->msg_out->options[dhcp->options_out_len++] = value;
-}
-
-static void
-dhcp_option_short(struct dhcp *dhcp, u16_t value)
-{
- LWIP_ASSERT("dhcp_option_short: dhcp->options_out_len + 2 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 2U <= DHCP_OPTIONS_LEN);
- dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff00U) >> 8);
- dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t) (value & 0x00ffU);
-}
-
-static void
-dhcp_option_long(struct dhcp *dhcp, u32_t value)
-{
- LWIP_ASSERT("dhcp_option_long: dhcp->options_out_len + 4 <= DHCP_OPTIONS_LEN", dhcp->options_out_len + 4U <= DHCP_OPTIONS_LEN);
- dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24);
- dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16);
- dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8);
- dhcp->msg_out->options[dhcp->options_out_len++] = (u8_t)((value & 0x000000ffUL));
-}
-
-#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 conitguous piece of memory, and
- * use that further on.
- *
- */
-static err_t
-dhcp_parse_reply(struct dhcp *dhcp, struct pbuf *p)
-{
- u8_t *options;
- u16_t offset;
- u16_t offset_max;
- u16_t options_idx;
- u16_t options_idx_max;
- struct pbuf *q;
- int parse_file_as_options = 0;
- int parse_sname_as_options = 0;
-
- /* clear received options */
- dhcp_clear_all_options(dhcp);
- /* check that beginning of dhcp_msg (up to and including chaddr) is in first pbuf */
- if (p->len < DHCP_SNAME_OFS) {
- return ERR_BUF;
- }
- dhcp->msg_in = (struct dhcp_msg *)p->payload;
-#if LWIP_DHCP_BOOTP_FILE
- /* clear boot file name */
- dhcp->boot_file_name[0] = 0;
-#endif /* LWIP_DHCP_BOOTP_FILE */
-
- /* parse options */
-
- /* start with options field */
- options_idx = DHCP_OPTIONS_OFS;
- /* parse options to the end of the received packet */
- options_idx_max = p->tot_len;
-again:
- q = p;
- while((q != NULL) && (options_idx >= q->len)) {
- options_idx -= q->len;
- options_idx_max -= q->len;
- q = q->next;
- }
- if (q == NULL) {
- return ERR_BUF;
- }
- offset = options_idx;
- offset_max = options_idx_max;
- options = (u8_t*)q->payload;
- /* at least 1 byte to read and no end marker, then at least 3 bytes to read? */
- while((q != NULL) && (options[offset] != DHCP_OPTION_END) && (offset < offset_max)) {
- u8_t op = options[offset];
- u8_t len;
- u8_t decode_len = 0;
- int decode_idx = -1;
- u16_t val_offset = offset + 2;
- /* len byte might be in the next pbuf */
- if (offset + 1 < q->len) {
- len = options[offset + 1];
- } else {
- len = (q->next != NULL ? ((u8_t*)q->next->payload)[0] : 0);
- }
- /* LWIP_DEBUGF(DHCP_DEBUG, ("msg_offset=%"U16_F", q->len=%"U16_F, msg_offset, q->len)); */
- decode_len = len;
- switch(op) {
- /* case(DHCP_OPTION_END): handled above */
- case(DHCP_OPTION_PAD):
- /* special option: no len encoded */
- decode_len = len = 0;
- /* will be increased below */
- offset--;
- break;
- case(DHCP_OPTION_SUBNET_MASK):
- LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
- decode_idx = DHCP_OPTION_IDX_SUBNET_MASK;
- break;
- case(DHCP_OPTION_ROUTER):
- decode_len = 4; /* only copy the first given router */
- LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
- decode_idx = DHCP_OPTION_IDX_ROUTER;
- break;
- case(DHCP_OPTION_DNS_SERVER):
- /* special case: there might be more than one server */
- LWIP_ERROR("len % 4 == 0", len % 4 == 0, return ERR_VAL;);
- /* limit number of DNS servers */
- decode_len = LWIP_MIN(len, 4 * DNS_MAX_SERVERS);
- LWIP_ERROR("len >= decode_len", len >= decode_len, return ERR_VAL;);
- decode_idx = DHCP_OPTION_IDX_DNS_SERVER;
- break;
- case(DHCP_OPTION_LEASE_TIME):
- LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
- decode_idx = DHCP_OPTION_IDX_LEASE_TIME;
- break;
- case(DHCP_OPTION_OVERLOAD):
- LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
- decode_idx = DHCP_OPTION_IDX_OVERLOAD;
- break;
- case(DHCP_OPTION_MESSAGE_TYPE):
- LWIP_ERROR("len == 1", len == 1, return ERR_VAL;);
- decode_idx = DHCP_OPTION_IDX_MSG_TYPE;
- break;
- case(DHCP_OPTION_SERVER_ID):
- LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
- decode_idx = DHCP_OPTION_IDX_SERVER_ID;
- break;
- case(DHCP_OPTION_T1):
- LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
- decode_idx = DHCP_OPTION_IDX_T1;
- break;
- case(DHCP_OPTION_T2):
- LWIP_ERROR("len == 4", len == 4, return ERR_VAL;);
- decode_idx = DHCP_OPTION_IDX_T2;
- break;
- default:
- decode_len = 0;
- LWIP_DEBUGF(DHCP_DEBUG, ("skipping option %"U16_F" in options\n", op));
- break;
- }
- offset += len + 2;
- if (decode_len > 0) {
- u32_t value = 0;
- u16_t copy_len;
-decode_next:
- LWIP_ASSERT("check decode_idx", decode_idx >= 0 && decode_idx < DHCP_OPTION_IDX_MAX);
- if (!dhcp_option_given(dhcp, decode_idx)) {
- copy_len = LWIP_MIN(decode_len, 4);
- pbuf_copy_partial(q, &value, copy_len, val_offset);
- if (decode_len > 4) {
- /* decode more than one u32_t */
- LWIP_ERROR("decode_len % 4 == 0", decode_len % 4 == 0, return ERR_VAL;);
- dhcp_got_option(dhcp, decode_idx);
- dhcp_set_option_value(dhcp, decode_idx, htonl(value));
- decode_len -= 4;
- val_offset += 4;
- decode_idx++;
- goto decode_next;
- } else if (decode_len == 4) {
- value = ntohl(value);
- } else {
- LWIP_ERROR("invalid decode_len", decode_len == 1, return ERR_VAL;);
- value = ((u8_t*)&value)[0];
- }
- dhcp_got_option(dhcp, decode_idx);
- dhcp_set_option_value(dhcp, decode_idx, value);
- }
- }
- if (offset >= q->len) {
- offset -= q->len;
- offset_max -= q->len;
- if ((offset < offset_max) && offset_max) {
- q = q->next;
- LWIP_ASSERT("next pbuf was null", q);
- options = (u8_t*)q->payload;
- } else {
- /* We've run out of bytes, probably no end marker. Don't proceed. */
- break;
- }
- }
- }
- /* is this an overloaded message? */
- if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_OVERLOAD)) {
- u32_t overload = dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_OVERLOAD);
- dhcp_clear_option(dhcp, DHCP_OPTION_IDX_OVERLOAD);
- if (overload == DHCP_OVERLOAD_FILE) {
- parse_file_as_options = 1;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded file field\n"));
- } else if (overload == DHCP_OVERLOAD_SNAME) {
- parse_sname_as_options = 1;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname field\n"));
- } else if (overload == DHCP_OVERLOAD_SNAME_FILE) {
- parse_sname_as_options = 1;
- parse_file_as_options = 1;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("overloaded sname and file field\n"));
- } else {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("invalid overload option: %d\n", (int)overload));
- }
-#if LWIP_DHCP_BOOTP_FILE
- if (!parse_file_as_options) {
- /* only do this for ACK messages */
- if (dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) &&
- (dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) == DHCP_ACK))
- /* copy bootp file name, don't care for sname (server hostname) */
- pbuf_copy_partial(p, dhcp->boot_file_name, DHCP_FILE_LEN-1, DHCP_FILE_OFS);
- /* make sure the string is really NULL-terminated */
- dhcp->boot_file_name[DHCP_FILE_LEN-1] = 0;
- }
-#endif /* LWIP_DHCP_BOOTP_FILE */
- }
- if (parse_file_as_options) {
- /* if both are overloaded, parse file first and then sname (RFC 2131 ch. 4.1) */
- parse_file_as_options = 0;
- options_idx = DHCP_FILE_OFS;
- options_idx_max = DHCP_FILE_OFS + DHCP_FILE_LEN;
- goto again;
- } else if (parse_sname_as_options) {
- parse_sname_as_options = 0;
- options_idx = DHCP_SNAME_OFS;
- options_idx_max = DHCP_SNAME_OFS + DHCP_SNAME_LEN;
- goto again;
- }
- return ERR_OK;
-}
-
-/**
- * If an incoming DHCP message is in response to us, then trigger the state machine
- */
-static void
-dhcp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
-{
- struct netif *netif = (struct netif *)arg;
- struct dhcp *dhcp = netif->dhcp;
- struct dhcp_msg *reply_msg = (struct dhcp_msg *)p->payload;
- u8_t msg_type;
- u8_t i;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_recv(pbuf = %p) from DHCP server %"U16_F".%"U16_F".%"U16_F".%"U16_F" port %"U16_F"\n", (void*)p,
- ip4_addr1_16(addr), ip4_addr2_16(addr), ip4_addr3_16(addr), ip4_addr4_16(addr), port));
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->len = %"U16_F"\n", p->len));
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("pbuf->tot_len = %"U16_F"\n", p->tot_len));
- /* prevent warnings about unused arguments */
- LWIP_UNUSED_ARG(pcb);
- LWIP_UNUSED_ARG(addr);
- LWIP_UNUSED_ARG(port);
-
- LWIP_ASSERT("reply wasn't freed", dhcp->msg_in == NULL);
-
- if (p->len < DHCP_MIN_REPLY_LEN) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP reply message or pbuf too short\n"));
- goto free_pbuf_and_return;
- }
-
- if (reply_msg->op != DHCP_BOOTREPLY) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("not a DHCP reply message, but type %"U16_F"\n", (u16_t)reply_msg->op));
- goto free_pbuf_and_return;
- }
- /* iterate through hardware address and match against DHCP message */
- for (i = 0; i < netif->hwaddr_len; i++) {
- if (netif->hwaddr[i] != reply_msg->chaddr[i]) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
- ("netif->hwaddr[%"U16_F"]==%02"X16_F" != reply_msg->chaddr[%"U16_F"]==%02"X16_F"\n",
- (u16_t)i, (u16_t)netif->hwaddr[i], (u16_t)i, (u16_t)reply_msg->chaddr[i]));
- goto free_pbuf_and_return;
- }
- }
- /* match transaction ID against what we expected */
- if (ntohl(reply_msg->xid) != dhcp->xid) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
- ("transaction id mismatch reply_msg->xid(%"X32_F")!=dhcp->xid(%"X32_F")\n",ntohl(reply_msg->xid),dhcp->xid));
- goto free_pbuf_and_return;
- }
- /* option fields could be unfold? */
- if (dhcp_parse_reply(dhcp, p) != ERR_OK) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
- ("problem unfolding DHCP message - too short on memory?\n"));
- goto free_pbuf_and_return;
- }
-
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("searching DHCP_OPTION_MESSAGE_TYPE\n"));
- /* obtain pointer to DHCP message type */
- if (!dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE)) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("DHCP_OPTION_MESSAGE_TYPE option not found\n"));
- goto free_pbuf_and_return;
- }
-
- /* read DHCP message type */
- msg_type = (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE);
- /* message type is DHCP ACK? */
- if (msg_type == DHCP_ACK) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_ACK received\n"));
- /* in requesting state? */
- if (dhcp->state == DHCP_REQUESTING) {
- dhcp_handle_ack(netif);
-#if DHCP_DOES_ARP_CHECK
- /* check if the acknowledged lease address is already in use */
- dhcp_check(netif);
-#else
- /* bind interface to the acknowledged lease address */
- dhcp_bind(netif);
-#endif
- }
- /* already bound to the given lease address? */
- else if ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING)) {
- dhcp_bind(netif);
- }
- }
- /* received a DHCP_NAK in appropriate state? */
- else if ((msg_type == DHCP_NAK) &&
- ((dhcp->state == DHCP_REBOOTING) || (dhcp->state == DHCP_REQUESTING) ||
- (dhcp->state == DHCP_REBINDING) || (dhcp->state == DHCP_RENEWING ))) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_NAK received\n"));
- dhcp_handle_nak(netif);
- }
- /* received a DHCP_OFFER in DHCP_SELECTING state? */
- else if ((msg_type == DHCP_OFFER) && (dhcp->state == DHCP_SELECTING)) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("DHCP_OFFER received in DHCP_SELECTING state\n"));
- dhcp->request_timeout = 0;
- /* remember offered lease */
- dhcp_handle_offer(netif);
- }
-free_pbuf_and_return:
- dhcp->msg_in = NULL;
- pbuf_free(p);
-}
-
-/**
- * Create a DHCP request, fill in common headers
- *
- * @param netif the netif under DHCP control
- * @param dhcp dhcp control struct
- * @param message_type message type of the request
- */
-static err_t
-dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type)
-{
- u16_t i;
-#ifndef DHCP_GLOBAL_XID
- /** default global transaction identifier starting value (easy to match
- * with a packet analyser). We simply increment for each new request.
- * Predefine DHCP_GLOBAL_XID to a better value or a function call to generate one
- * at runtime, any supporting function prototypes can be defined in DHCP_GLOBAL_XID_HEADER */
-#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND)
- static u32_t xid;
-#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
- static u32_t xid = 0xABCD0000;
-#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
-#else
- if (!xid_initialised) {
- xid = DHCP_GLOBAL_XID;
- xid_initialised = !xid_initialised;
- }
-#endif
- LWIP_ERROR("dhcp_create_msg: netif != NULL", (netif != NULL), return ERR_ARG;);
- LWIP_ERROR("dhcp_create_msg: dhcp != NULL", (dhcp != NULL), return ERR_VAL;);
- LWIP_ASSERT("dhcp_create_msg: dhcp->p_out == NULL", dhcp->p_out == NULL);
- LWIP_ASSERT("dhcp_create_msg: dhcp->msg_out == NULL", dhcp->msg_out == NULL);
- dhcp->p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM);
- if (dhcp->p_out == NULL) {
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
- ("dhcp_create_msg(): could not allocate pbuf\n"));
- return ERR_MEM;
- }
- LWIP_ASSERT("dhcp_create_msg: check that first pbuf can hold struct dhcp_msg",
- (dhcp->p_out->len >= sizeof(struct dhcp_msg)));
-
- /* reuse transaction identifier in retransmissions */
- if (dhcp->tries == 0) {
-#if DHCP_CREATE_RAND_XID && defined(LWIP_RAND)
- xid = LWIP_RAND();
-#else /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
- xid++;
-#endif /* DHCP_CREATE_RAND_XID && defined(LWIP_RAND) */
- }
- dhcp->xid = xid;
- LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE,
- ("transaction id xid(%"X32_F")\n", xid));
-
- dhcp->msg_out = (struct dhcp_msg *)dhcp->p_out->payload;
-
- dhcp->msg_out->op = DHCP_BOOTREQUEST;
- /* TODO: make link layer independent */
- dhcp->msg_out->htype = DHCP_HTYPE_ETH;
- dhcp->msg_out->hlen = netif->hwaddr_len;
- dhcp->msg_out->hops = 0;
- dhcp->msg_out->xid = htonl(dhcp->xid);
- dhcp->msg_out->secs = 0;
- /* we don't need the broadcast flag since we can receive unicast traffic
- before being fully configured! */
- dhcp->msg_out->flags = 0;
- ip_addr_set_zero(&dhcp->msg_out->ciaddr);
- /* set ciaddr to netif->ip_addr based on message_type and state */
- if ((message_type == DHCP_INFORM) || (message_type == DHCP_DECLINE) ||
- ((message_type == DHCP_REQUEST) && /* DHCP_BOUND not used for sending! */
- ((dhcp->state==DHCP_RENEWING) || dhcp->state==DHCP_REBINDING))) {
- ip_addr_copy(dhcp->msg_out->ciaddr, netif->ip_addr);
- }
- ip_addr_set_zero(&dhcp->msg_out->yiaddr);
- ip_addr_set_zero(&dhcp->msg_out->siaddr);
- ip_addr_set_zero(&dhcp->msg_out->giaddr);
- for (i = 0; i < DHCP_CHADDR_LEN; i++) {
- /* copy netif hardware address, pad with zeroes */
- dhcp->msg_out->chaddr[i] = (i < netif->hwaddr_len && i < NETIF_MAX_HWADDR_LEN) ? netif->hwaddr[i] : 0/* pad byte*/;
- }
- for (i = 0; i < DHCP_SNAME_LEN; i++) {
- dhcp->msg_out->sname[i] = 0;
- }
- for (i = 0; i < DHCP_FILE_LEN; i++) {
- dhcp->msg_out->file[i] = 0;
- }
- dhcp->msg_out->cookie = PP_HTONL(DHCP_MAGIC_COOKIE);
- dhcp->options_out_len = 0;
- /* fill options field with an incrementing array (for debugging purposes) */
- for (i = 0; i < DHCP_OPTIONS_LEN; i++) {
- dhcp->msg_out->options[i] = (u8_t)i; /* for debugging only, no matter if truncated */
- }
- /* Add option MESSAGE_TYPE */
- dhcp_option(dhcp, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
- dhcp_option_byte(dhcp, message_type);
- return ERR_OK;
-}
-
-/**
- * Free previously allocated memory used to send a DHCP request.
- *
- * @param dhcp the dhcp struct to free the request from
- */
-static void
-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/lwip/src/core/dns.c b/lwip/src/core/dns.c
index 90821a6..4a64866 100644
--- a/lwip/src/core/dns.c
+++ b/lwip/src/core/dns.c
@@ -2,15 +2,44 @@
* @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.
*/
-/**
-
- * This file implements a 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.
*
@@ -37,26 +66,6 @@
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
* NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *
- * DNS.C
- *
- * The lwIP DNS resolver functions are used to lookup a host name and
- * map it to a numerical IP address. It maintains a list of resolved
- * hostnames that can be queried with the dns_lookup() function.
- * New hostnames can be resolved using the dns_query() function.
- *
- * The lwIP version of the resolver also adds a non-blocking version of
- * gethostbyname() that will work with a raw API application. This function
- * checks for an IP address string first and converts it if it is valid.
- * gethostbyname() then does a dns_lookup() to see if the name is
- * already in the table. If so, the IP is returned. If not, a query is
- * issued and the function returns with a ERR_INPROGRESS status. The app
- * using the dns client must then go into a waiting state.
- *
- * Once a hostname has been resolved (or found to be non-existent),
- * the resolver code calls a specified callback function (which
- * must be implemented by the module that uses the resolver).
*/
/*-----------------------------------------------------------------------------
@@ -67,6 +76,7 @@
/** @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
@@ -76,21 +86,28 @@
#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>
-/** DNS server IP address */
-#ifndef DNS_SERVER_ADDRESS
-#define DNS_SERVER_ADDRESS(ipaddr) (ip4_addr_set_u32(ipaddr, ipaddr_addr("208.67.222.222"))) /* resolver1.opendns.com */
+/** 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
-/** DNS server port address */
-#ifndef DNS_SERVER_PORT
-#define DNS_SERVER_PORT 53
+/** 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". */
@@ -101,46 +118,74 @@
/** 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
-/* DNS protocol flags */
-#define DNS_FLAG1_RESPONSE 0x80
-#define DNS_FLAG1_OPCODE_STATUS 0x10
-#define DNS_FLAG1_OPCODE_INVERSE 0x08
-#define DNS_FLAG1_OPCODE_STANDARD 0x00
-#define DNS_FLAG1_AUTHORATIVE 0x04
-#define DNS_FLAG1_TRUNC 0x02
-#define DNS_FLAG1_RD 0x01
-#define DNS_FLAG2_RA 0x80
-#define DNS_FLAG2_ERR_MASK 0x0f
-#define DNS_FLAG2_ERR_NONE 0x00
-#define DNS_FLAG2_ERR_NAME 0x03
-
-/* DNS protocol states */
-#define DNS_STATE_UNUSED 0
-#define DNS_STATE_NEW 1
-#define DNS_STATE_ASKING 2
-#define DNS_STATE_DONE 3
-
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-/** DNS message header */
-struct dns_hdr {
- PACK_STRUCT_FIELD(u16_t id);
- PACK_STRUCT_FIELD(u8_t flags1);
- PACK_STRUCT_FIELD(u8_t flags2);
- PACK_STRUCT_FIELD(u16_t numquestions);
- PACK_STRUCT_FIELD(u16_t numanswers);
- PACK_STRUCT_FIELD(u16_t numauthrr);
- PACK_STRUCT_FIELD(u16_t numextrarr);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
-#define SIZEOF_DNS_HDR 12
+/* 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. */
@@ -163,21 +208,52 @@ struct dns_answer {
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 numdns;
+ u8_t server_idx;
u8_t tmr;
u8_t retries;
u8_t seqno;
- u8_t err;
- u32_t ttl;
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+ u8_t pcb_idx;
+#endif
char name[DNS_MAX_NAME_LENGTH];
- ip_addr_t ipaddr;
+#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
@@ -203,95 +279,114 @@ DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static
#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
-static void dns_init_local();
+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, ip_addr_t *addr, u16_t port);
+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);
/*-----------------------------------------------------------------------------
- * Globales
+ * Globals
*----------------------------------------------------------------------------*/
/* DNS variables */
-static struct udp_pcb *dns_pcb;
+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];
-/** Contiguous buffer for processing responses */
-static u8_t dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)];
-static u8_t* dns_payload;
+
+#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
- * (DNS_SERVER_ADDRESS).
+ * (if DNS_SERVER_ADDRESS is set).
*/
void
-dns_init()
+dns_init(void)
{
- ip_addr_t dnsserver;
-
- dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer);
-
+#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 (dns_pcb == NULL) {
- dns_pcb = udp_new();
-
- if (dns_pcb != NULL) {
- /* initialize DNS table not needed (initialized to zero since it is a
- * global variable) */
- LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
- DNS_STATE_UNUSED == 0);
-
- /* initialize DNS client */
- udp_bind(dns_pcb, IP_ADDR_ANY, 0);
- udp_recv(dns_pcb, dns_recv, NULL);
-
- /* initialize default DNS primary server */
- dns_setserver(0, &dnsserver);
- }
+#if ((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, ip_addr_t *dnsserver)
+dns_setserver(u8_t numdns, const ip_addr_t *dnsserver)
{
- if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) &&
- (dnsserver != NULL) && !ip_addr_isany(dnsserver)) {
- dns_servers[numdns] = (*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.
*/
-ip_addr_t
+const ip_addr_t *
dns_getserver(u8_t numdns)
{
if (numdns < DNS_MAX_SERVERS) {
- return dns_servers[numdns];
+ return &dns_servers[numdns];
} else {
- return *IP_ADDR_ANY;
+ return IP_ADDR_ANY;
}
}
@@ -302,23 +397,21 @@ dns_getserver(u8_t numdns)
void
dns_tmr(void)
{
- if (dns_pcb != NULL) {
- LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
- dns_check_entries();
- }
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
+ dns_check_entries();
}
#if DNS_LOCAL_HOSTLIST
static void
-dns_init_local()
+dns_init_local(void)
{
#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT)
- int i;
+ 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 < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) {
+ 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);
@@ -326,9 +419,10 @@ dns_init_local()
entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
LWIP_ASSERT("mem-error in dns_init_local", entry != NULL);
if (entry != NULL) {
- entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
- MEMCPY((char*)entry->name, init_entry->name, namelen);
- ((char*)entry->name)[namelen] = 0;
+ 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;
@@ -338,37 +432,93 @@ dns_init_local()
}
/**
+ * @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
- * @return The first IP address for the hostname in the local host-list or
+ * @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
*/
-static u32_t
-dns_lookup_local(const char *hostname)
+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(strcmp(entry->name, hostname) == 0) {
- return ip4_addr_get_u32(&entry->addr);
+ 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 */
- int i;
- for (i = 0; i < sizeof(local_hostlist_static) / sizeof(struct local_hostlist_entry); i++) {
- if(strcmp(local_hostlist_static[i].name, hostname) == 0) {
- return ip4_addr_get_u32(&local_hostlist_static[i].addr);
+ 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 IPADDR_NONE;
+ return ERR_ARG;
}
#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
-/** Remove all entries from the local host-list for a specific hostname
- * and/or IP addess
+/**
+ * @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
@@ -382,7 +532,7 @@ dns_local_removehost(const char *hostname, const ip_addr_t *addr)
struct local_hostlist_entry *entry = local_hostlist_dynamic;
struct local_hostlist_entry *last_entry = NULL;
while (entry != NULL) {
- if (((hostname == NULL) || !strcmp(entry->name, hostname)) &&
+ 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) {
@@ -403,6 +553,7 @@ dns_local_removehost(const char *hostname, const ip_addr_t *addr)
}
/**
+ * @ingroup dns
* Add a hostname/IP address pair to the local host-list.
* Duplicates are not checked.
*
@@ -415,6 +566,7 @@ 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);
@@ -422,9 +574,10 @@ dns_local_addhost(const char *hostname, const ip_addr_t *addr)
if (entry == NULL) {
return ERR_MEM;
}
- entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
- MEMCPY((char*)entry->name, hostname, namelen);
- ((char*)entry->name)[namelen] = 0;
+ 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;
@@ -434,6 +587,7 @@ dns_local_addhost(const char *hostname, const ip_addr_t *addr)
#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
@@ -442,43 +596,44 @@ dns_local_addhost(const char *hostname, const ip_addr_t *addr)
* for a hostname.
*
* @param name the hostname to look up
- * @return the hostname's IP address, as u32_t (instead of ip_addr_t to
+ * @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 u32_t
-dns_lookup(const char *name)
+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)
- u32_t addr;
-#endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */
#if DNS_LOCAL_HOSTLIST
- if ((addr = dns_lookup_local(name)) != IPADDR_NONE) {
- return addr;
+ 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((addr = DNS_LOOKUP_LOCAL_EXTERN(name)) != IPADDR_NONE) {
- return addr;
+ 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) &&
- (strcmp(name, dns_table[i].name) == 0)) {
+ (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));
+ ip_addr_debug_print_val(DNS_DEBUG, dns_table[i].ipaddr);
LWIP_DEBUGF(DNS_DEBUG, ("\n"));
- return ip4_addr_get_u32(&dns_table[i].ipaddr);
+ if (addr) {
+ ip_addr_copy(*addr, dns_table[i].ipaddr);
+ }
+ return ERR_OK;
}
}
- return IPADDR_NONE;
+ return ERR_ARG;
}
-#if DNS_DOES_NAME_CHECK
/**
* Compare the "dotted" name "query" with the encoded name "response"
* to make sure an answer from the DNS server matches the current dns_table
@@ -486,136 +641,207 @@ dns_lookup(const char *name)
* any more).
*
* @param query hostname (not encoded) from the dns_table
- * @param response encoded hostname in the DNS response
- * @return 0: names equal; 1: names differ
+ * @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 u8_t
-dns_compare_name(unsigned char *query, unsigned char *response)
+static u16_t
+dns_compare_name(const char *query, struct pbuf *p, u16_t start_offset)
{
- unsigned char n;
+ int n;
+ u16_t response_offset = start_offset;
do {
- n = *response++;
+ n = pbuf_try_get_at(p, response_offset++);
+ if ((n < 0) || (response_offset == 0)) {
+ return 0xFFFF;
+ }
/** @see RFC 1035 - 4.1.4. Message compression */
if ((n & 0xc0) == 0xc0) {
- /* Compressed name */
- break;
+ /* Compressed name: cannot be equal since we don't send them */
+ return 0xFFFF;
} else {
/* Not compressed name */
while (n > 0) {
- if ((*query) != (*response)) {
- return 1;
+ int c = pbuf_try_get_at(p, response_offset);
+ if (c < 0) {
+ return 0xFFFF;
+ }
+ if ((*query) != (u8_t)c) {
+ return 0xFFFF;
+ }
+ ++response_offset;
+ if (response_offset == 0) {
+ return 0xFFFF;
}
- ++response;
++query;
--n;
- };
+ }
++query;
}
- } while (*response != 0);
+ n = pbuf_try_get_at(p, response_offset);
+ if (n < 0) {
+ return 0xFFFF;
+ }
+ } while (n != 0);
- return 0;
+ if (response_offset == 0xFFFF) {
+ return 0xFFFF;
+ }
+ return (u16_t)(response_offset + 1);
}
-#endif /* DNS_DOES_NAME_CHECK */
/**
* Walk through a compact encoded DNS name and return the end of the name.
*
- * @param query encoded DNS name in the DNS server response
- * @return end of the name
+ * @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 unsigned char *
-dns_parse_name(unsigned char *query)
+static u16_t
+dns_skip_name(struct pbuf *p, u16_t query_idx)
{
- unsigned char n;
+ int n;
+ u16_t offset = query_idx;
do {
- n = *query++;
+ n = pbuf_try_get_at(p, offset++);
+ if ((n < 0) || (offset == 0)) {
+ return 0xFFFF;
+ }
/** @see RFC 1035 - 4.1.4. Message compression */
if ((n & 0xc0) == 0xc0) {
- /* Compressed name */
+ /* Compressed name: since we only want to skip it (not check it), stop here */
break;
} else {
/* Not compressed name */
- while (n > 0) {
- ++query;
- --n;
- };
+ if (offset + n >= p->tot_len) {
+ return 0xFFFF;
+ }
+ offset = (u16_t)(offset + n);
}
- } while (*query != 0);
+ n = pbuf_try_get_at(p, offset);
+ if (n < 0) {
+ return 0xFFFF;
+ }
+ } while (n != 0);
- return query + 1;
+ if (offset == 0xFFFF) {
+ return 0xFFFF;
+ }
+ return (u16_t)(offset + 1);
}
/**
* Send a DNS query packet.
*
- * @param numdns index of the DNS server in the dns_servers table
- * @param name hostname to query
- * @param id index of the hostname in dns_table, used as transaction ID in the
- * DNS query packet
+ * @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 numdns, const char* name, u8_t id)
+dns_send(u8_t idx)
{
err_t err;
- struct dns_hdr *hdr;
+ struct dns_hdr hdr;
struct dns_query qry;
struct pbuf *p;
- char *query, *nptr;
- const char *pHostname;
+ 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)(numdns), name));
- LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS);
- LWIP_ASSERT("dns server has no IP address set", !ip_addr_isany(&dns_servers[numdns]));
+ (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, SIZEOF_DNS_HDR + DNS_MAX_NAME_LENGTH + 1 +
- SIZEOF_DNS_QUERY, PBUF_RAM);
+ p = pbuf_alloc(PBUF_TRANSPORT, (u16_t)(SIZEOF_DNS_HDR + strlen(entry->name) + 2 +
+ SIZEOF_DNS_QUERY), PBUF_RAM);
if (p != NULL) {
- u16_t realloc_size;
- LWIP_ASSERT("pbuf must be in one piece", p->next == NULL);
+ const ip_addr_t *dst;
+ u16_t dst_port;
/* fill dns header */
- hdr = (struct dns_hdr*)p->payload;
- memset(hdr, 0, SIZEOF_DNS_HDR);
- hdr->id = htons(id);
- hdr->flags1 = DNS_FLAG1_RD;
- hdr->numquestions = PP_HTONS(1);
- query = (char*)hdr + SIZEOF_DNS_HDR;
- pHostname = name;
- --pHostname;
+ 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 {
- ++pHostname;
- nptr = query;
- ++query;
- for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
- *query = *pHostname;
- ++query;
+ ++hostname;
+ hostname_part = hostname;
+ for (n = 0; *hostname != '.' && *hostname != 0; ++hostname) {
++n;
}
- *nptr = n;
- } while(*pHostname != 0);
- *query++='\0';
+ copy_len = (u16_t)(hostname - hostname_part);
+ if (query_idx + n + 1 > 0xFFFF) {
+ /* u16_t overflow */
+ goto overflow_return;
+ }
+ pbuf_put_at(p, query_idx, n);
+ pbuf_take_at(p, hostname_part, copy_len, (u16_t)(query_idx + 1));
+ query_idx = (u16_t)(query_idx + n + 1);
+ } while (*hostname != 0);
+ pbuf_put_at(p, query_idx, 0);
+ query_idx++;
/* fill dns query */
- qry.type = PP_HTONS(DNS_RRTYPE_A);
+ 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);
- SMEMCPY(query, &qry, SIZEOF_DNS_QUERY);
-
- /* resize pbuf to the exact dns query */
- realloc_size = (u16_t)((query + SIZEOF_DNS_QUERY) - ((char*)(p->payload)));
- LWIP_ASSERT("p->tot_len >= realloc_size", p->tot_len >= realloc_size);
- pbuf_realloc(p, realloc_size);
+ pbuf_take_at(p, &qry, SIZEOF_DNS_QUERY, query_idx);
- /* connect to the server for faster receiving */
- udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT);
+#if ((LWIP_DNS_SECURE & LWIP_DNS_SECURE_RAND_SRC_PORT) != 0)
+ pcb_idx = entry->pcb_idx;
+#else
+ pcb_idx = 0;
+#endif
/* send dns packet */
- err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT);
+ 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);
@@ -624,10 +850,169 @@ dns_send(u8_t numdns, const char* name, u8_t id)
}
return err;
+overflow_return:
+ pbuf_free(p);
+ return ERR_VAL;
}
+#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 *pcb;
+
+ pcb = udp_new_ip_type(IPADDR_TYPE_ANY);
+ if (pcb == 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)) {
+ err = udp_bind(pcb, IP_ANY_TYPE, port);
+ } else {
+ /* this port is not allowed, try again */
+ err = ERR_USE;
+ }
+ } while (err == ERR_USE);
+ if (err != ERR_OK) {
+ udp_remove(pcb);
+ return NULL;
+ }
+ udp_recv(pcb, dns_recv, NULL);
+ return pcb;
+}
+
+/**
+ * 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 (so overflow is no issue) */
+ for (i = 0, idx = (u8_t)(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_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query.
+ * 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)
@@ -639,72 +1024,67 @@ static void
dns_check_entry(u8_t i)
{
err_t err;
- struct dns_table_entry *pEntry = &dns_table[i];
+ struct dns_table_entry *entry = &dns_table[i];
LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
- switch(pEntry->state) {
-
- case DNS_STATE_NEW: {
+ switch (entry->state) {
+ case DNS_STATE_NEW:
/* initialize new entry */
- pEntry->state = DNS_STATE_ASKING;
- pEntry->numdns = 0;
- pEntry->tmr = 1;
- pEntry->retries = 0;
-
+ 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(pEntry->numdns, pEntry->name, i);
+ 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 (--pEntry->tmr == 0) {
- if (++pEntry->retries == DNS_MAX_RETRIES) {
- if ((pEntry->numdns+1<DNS_MAX_SERVERS) && !ip_addr_isany(&dns_servers[pEntry->numdns+1])) {
+ 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 */
- pEntry->numdns++;
- pEntry->tmr = 1;
- pEntry->retries = 0;
- break;
+ entry->server_idx++;
+ entry->tmr = 1;
+ entry->retries = 0;
} else {
- LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name));
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", entry->name));
/* call specified callback function if provided */
- if (pEntry->found)
- (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
+ dns_call_found(i, NULL);
/* flush this entry */
- pEntry->state = DNS_STATE_UNUSED;
- pEntry->found = NULL;
+ entry->state = DNS_STATE_UNUSED;
break;
}
+ } else {
+ /* wait longer for the next retry */
+ entry->tmr = entry->retries;
}
- /* wait longer for the next retry */
- pEntry->tmr = pEntry->retries;
-
/* send DNS packet for this entry */
- err = dns_send(pEntry->numdns, pEntry->name, i);
+ 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: {
+ case DNS_STATE_DONE:
/* if the time to live is nul */
- if ((pEntry->ttl == 0) || (--pEntry->ttl == 0)) {
- LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name));
- /* flush this entry */
- pEntry->state = DNS_STATE_UNUSED;
- pEntry->found = NULL;
+ 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;
@@ -728,133 +1108,218 @@ dns_check_entries(void)
}
/**
+ * 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_val(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.
- *
- * @params see udp.h
*/
static void
-dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
+dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
{
- u16_t i;
- char *pHostname;
- struct dns_hdr *hdr;
+ u8_t i;
+ u16_t txid;
+ u16_t res_idx;
+ struct dns_hdr hdr;
struct dns_answer ans;
- struct dns_table_entry *pEntry;
+ struct dns_query qry;
u16_t nquestions, nanswers;
LWIP_UNUSED_ARG(arg);
LWIP_UNUSED_ARG(pcb);
- LWIP_UNUSED_ARG(addr);
LWIP_UNUSED_ARG(port);
- /* is the dns message too big ? */
- if (p->tot_len > DNS_MSG_SIZE) {
- LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n"));
- /* free pbuf and return */
- goto memerr;
- }
-
/* is the dns message big enough ? */
- if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) {
+ 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, dns_payload, p->tot_len, 0) == p->tot_len) {
- /* The ID in the DNS header should be our entry into the name table. */
- hdr = (struct dns_hdr*)dns_payload;
- i = htons(hdr->id);
- if (i < DNS_TABLE_SIZE) {
- pEntry = &dns_table[i];
- if(pEntry->state == DNS_STATE_ASKING) {
- /* This entry is now completed. */
- pEntry->state = DNS_STATE_DONE;
- pEntry->err = hdr->flags2 & DNS_FLAG2_ERR_MASK;
+ /* 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 = htons(hdr->numquestions);
- nanswers = htons(hdr->numanswers);
+ nquestions = lwip_htons(hdr.numquestions);
+ nanswers = lwip_htons(hdr.numanswers);
- /* Check for error. If so, call callback to inform. */
- if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) {
- LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name));
- /* call callback to indicate error, clean up memory and return */
- goto responseerr;
+ /* 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 */
}
-#if DNS_DOES_NAME_CHECK
- /* Check if the name in the "question" part match with the name in the entry. */
- if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + SIZEOF_DNS_HDR) != 0) {
- LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name));
- /* call callback to indicate error, clean up memory and return */
- goto responseerr;
+ /* 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 */
}
-#endif /* DNS_DOES_NAME_CHECK */
-
- /* Skip the name in the "question" part */
- pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + SIZEOF_DNS_HDR) + SIZEOF_DNS_QUERY;
-
- while (nanswers > 0) {
- /* skip answer resource record's host name */
- pHostname = (char *) dns_parse_name((unsigned char *)pHostname);
-
- /* Check for IP address type and Internet class. Others are discarded. */
- SMEMCPY(&ans, pHostname, SIZEOF_DNS_ANSWER);
- if((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) &&
- (ans.len == PP_HTONS(sizeof(ip_addr_t))) ) {
- /* read the answer resource record's TTL, and maximize it if needed */
- pEntry->ttl = ntohl(ans.ttl);
- if (pEntry->ttl > DNS_MAX_TTL) {
- pEntry->ttl = DNS_MAX_TTL;
+ 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 */
+ if (res_idx + SIZEOF_DNS_QUERY > 0xFFFF) {
+ goto memerr;
+ }
+ res_idx = (u16_t)(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 */
}
- /* read the IP address after answer resource record's header */
- SMEMCPY(&(pEntry->ipaddr), (pHostname+SIZEOF_DNS_ANSWER), sizeof(ip_addr_t));
- LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name));
- ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr)));
- LWIP_DEBUGF(DNS_DEBUG, ("\n"));
- /* call specified callback function if provided */
- if (pEntry->found) {
- (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg);
+
+ /* 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 */
}
- if (pEntry->ttl == 0) {
- /* RFC 883, page 29: "Zero values are
- interpreted to mean that the RR can only be used for the
- transaction in progress, and should not be cached."
- -> flush this entry now */
- goto flushentry;
+ if (res_idx + SIZEOF_DNS_ANSWER > 0xFFFF) {
+ goto memerr;
}
- /* deallocate memory and return */
- goto memerr;
- } else {
- pHostname = pHostname + SIZEOF_DNS_ANSWER + htons(ans.len);
+ res_idx = (u16_t)(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_p_t)))) {
+#if LWIP_IPV4 && LWIP_IPV6
+ if (LWIP_DNS_ADDRTYPE_IS_IPV6(entry->reqaddrtype))
+#endif /* LWIP_IPV4 && LWIP_IPV6 */
+ {
+ ip6_addr_p_t ip6addr;
+ /* read the IP address after answer resource record's header */
+ if (pbuf_copy_partial(p, &ip6addr, sizeof(ip6_addr_p_t), res_idx) != sizeof(ip6_addr_p_t)) {
+ goto memerr; /* ignore this packet */
+ }
+ /* @todo: scope ip6addr? Might be required for link-local addresses at least? */
+ ip_addr_copy_from_ip6_packed(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 = (u16_t)(res_idx + lwip_htons(ans.len));
+ --nanswers;
}
- --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));
}
- LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name));
/* call callback to indicate error, clean up memory and return */
- goto responseerr;
+ pbuf_free(p);
+ dns_call_found(i, NULL);
+ dns_table[i].state = DNS_STATE_UNUSED;
+ return;
}
}
}
- /* deallocate memory and return */
- goto memerr;
-
-responseerr:
- /* ERROR: call specified callback function with NULL as name to indicate an error */
- if (pEntry->found) {
- (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
- }
-flushentry:
- /* flush this entry */
- pEntry->state = DNS_STATE_UNUSED;
- pEntry->found = NULL;
-
memerr:
- /* free pbuf */
+ /* deallocate memory and return */
pbuf_free(p);
return;
}
@@ -864,31 +1329,64 @@ memerr:
*
* @param name the hostname that is to be queried
* @param hostnamelen length of the hostname
- * @param found a callback founction to be called on success, failure or timeout
+ * @param found a callback function to be called on success, failure or timeout
* @param callback_arg argument to pass to the callback function
- * @return @return a err_t return code.
+ * @return err_t return code.
*/
static err_t
dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
- void *callback_arg)
+ 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 *pEntry = NULL;
+ 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 = lseqi = 0;
+ lseq = 0;
+ lseqi = DNS_TABLE_SIZE;
for (i = 0; i < DNS_TABLE_SIZE; ++i) {
- pEntry = &dns_table[i];
+ entry = &dns_table[i];
/* is it an unused entry ? */
- if (pEntry->state == DNS_STATE_UNUSED)
+ if (entry->state == DNS_STATE_UNUSED) {
break;
-
+ }
/* check if this is the oldest completed entry */
- if (pEntry->state == DNS_STATE_DONE) {
- if ((dns_seqno - pEntry->seqno) > lseq) {
- lseq = dns_seqno - pEntry->seqno;
+ if (entry->state == DNS_STATE_DONE) {
+ u8_t age = (u8_t)(dns_seqno - entry->seqno);
+ if (age > lseq) {
+ lseq = age;
lseqi = i;
}
}
@@ -897,27 +1395,67 @@ dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
/* if we don't have found an unused entry, use the oldest completed one */
if (i == DNS_TABLE_SIZE) {
if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
- /* no entry can't be used now, table is full */
+ /* 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;
- pEntry = &dns_table[i];
+ 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 */
- pEntry->state = DNS_STATE_NEW;
- pEntry->seqno = dns_seqno++;
- pEntry->found = found;
- pEntry->arg = callback_arg;
- namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH-1);
- MEMCPY(pEntry->name, name, namelen);
- pEntry->name[namelen] = 0;
+ 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);
@@ -927,6 +1465,7 @@ dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
}
/**
+ * @ingroup dns
* Resolve a hostname (string) into an IP address.
* NON-BLOCKING callback version for use with raw API!!!
*
@@ -949,40 +1488,106 @@ err_t
dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
void *callback_arg)
{
- u32_t ipaddr;
+ 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 ((dns_pcb == NULL) || (addr == NULL) ||
+ 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(addr);
+ 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 */
- ipaddr = ipaddr_addr(hostname);
- if (ipaddr == IPADDR_NONE) {
- /* already have this address cached? */
- ipaddr = dns_lookup(hostname);
+ 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;
+ }
}
- if (ipaddr != IPADDR_NONE) {
- ip4_addr_set_u32(addr, ipaddr);
+ /* 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);
+ 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/lwip/src/core/inet_chksum.c b/lwip/src/core/inet_chksum.c
index 8bc42c1..79fbd8d 100644
--- a/lwip/src/core/inet_chksum.c
+++ b/lwip/src/core/inet_chksum.c
@@ -1,7 +1,16 @@
/**
* @file
- * Incluse internet checksum functions.
+ * 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.
*/
/*
@@ -40,27 +49,16 @@
#include "lwip/inet_chksum.h"
#include "lwip/def.h"
+#include "lwip/ip_addr.h"
-#include <stddef.h>
#include <string.h>
-/* These are some reference implementations of the checksum algorithm, with the
- * aim of being simple, correct and fully portable. Checksumming is the
- * first thing you would want to optimize for your platform. If you create
- * your own version, link it in and in your cc.h put:
- *
- * #define LWIP_CHKSUM <your_checksum_routine>
- *
- * Or you can select from the implementations below by defining
- * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3.
- */
-
#ifndef LWIP_CHKSUM
# define LWIP_CHKSUM lwip_standard_chksum
# ifndef LWIP_CHKSUM_ALGORITHM
# define LWIP_CHKSUM_ALGORITHM 2
# endif
-u16_t lwip_standard_chksum(void *dataptr, int len);
+u16_t lwip_standard_chksum(const void *dataptr, int len);
#endif
/* If none set: */
#ifndef LWIP_CHKSUM_ALGORITHM
@@ -73,21 +71,21 @@ u16_t lwip_standard_chksum(void *dataptr, int len);
*
* @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)
+ * @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(void *dataptr, u16_t len)
+lwip_standard_chksum(const void *dataptr, int len)
{
u32_t acc;
u16_t src;
- u8_t *octetptr;
+ const u8_t *octetptr;
acc = 0;
/* dataptr may be at odd or even addresses */
- octetptr = (u8_t*)dataptr;
+ octetptr = (const u8_t *)dataptr;
while (len > 1) {
/* declare first octet as most significant
thus assume network order, ignoring host order */
@@ -109,10 +107,10 @@ lwip_standard_chksum(void *dataptr, u16_t len)
if ((acc & 0xffff0000UL) != 0) {
acc = (acc >> 16) + (acc & 0x0000ffffUL);
}
- /* This maybe a little confusing: reorder sum using htons()
- instead of ntohs() since it has a little less call overhead.
+ /* 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 htons((u16_t)acc);
+ return lwip_htons((u16_t)acc);
}
#endif
@@ -129,14 +127,14 @@ lwip_standard_chksum(void *dataptr, u16_t len)
*
* @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)
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
*/
-
u16_t
-lwip_standard_chksum(void *dataptr, int len)
+lwip_standard_chksum(const void *dataptr, int len)
{
- u8_t *pb = (u8_t *)dataptr;
- u16_t *ps, t = 0;
+ 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);
@@ -147,7 +145,7 @@ lwip_standard_chksum(void *dataptr, int len)
}
/* Add the bulk of the data */
- ps = (u16_t *)(void *)pb;
+ ps = (const u16_t *)(const void *)pb;
while (len > 1) {
sum += *ps++;
len -= 2;
@@ -155,14 +153,14 @@ lwip_standard_chksum(void *dataptr, int len)
/* Consume left-over byte, if any */
if (len > 0) {
- ((u8_t *)&t)[0] = *(u8_t *)ps;
+ ((u8_t *)&t)[0] = *(const u8_t *)ps;
}
/* Add end bytes */
sum += t;
/* Fold 32-bit sum to 16 bits
- calling this twice is propably faster than if statements... */
+ calling this twice is probably faster than if statements... */
sum = FOLD_U32T(sum);
sum = FOLD_U32T(sum);
@@ -179,21 +177,21 @@ lwip_standard_chksum(void *dataptr, int len)
/**
* 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.
+ * 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)
- *
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ *
* by Curt McDowell, Broadcom Corp. December 8th, 2005
*/
-
u16_t
-lwip_standard_chksum(void *dataptr, int len)
+lwip_standard_chksum(const void *dataptr, int len)
{
- u8_t *pb = (u8_t *)dataptr;
- u16_t *ps, t = 0;
- u32_t *pl;
+ 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);
@@ -203,14 +201,14 @@ lwip_standard_chksum(void *dataptr, int len)
len--;
}
- ps = (u16_t *)pb;
+ ps = (const u16_t *)(const void *)pb;
if (((mem_ptr_t)ps & 3) && len > 1) {
sum += *ps++;
len -= 2;
}
- pl = (u32_t *)ps;
+ pl = (const u32_t *)(const void *)ps;
while (len > 7) {
tmp = sum + *pl++; /* ping */
@@ -229,7 +227,7 @@ lwip_standard_chksum(void *dataptr, int len)
/* make room in upper bits */
sum = FOLD_U32T(sum);
- ps = (u16_t *)pl;
+ ps = (const u16_t *)pl;
/* 16-bit aligned word remaining? */
while (len > 1) {
@@ -239,13 +237,13 @@ lwip_standard_chksum(void *dataptr, int len)
/* dangling tail byte remaining? */
if (len > 0) { /* include odd byte */
- ((u8_t *)&t)[0] = *(u8_t *)ps;
+ ((u8_t *)&t)[0] = *(const u8_t *)ps;
}
sum += t; /* add end bytes */
/* Fold 32-bit sum to 16 bits
- calling this twice is propably faster than if statements... */
+ calling this twice is probably faster than if statements... */
sum = FOLD_U32T(sum);
sum = FOLD_U32T(sum);
@@ -262,19 +260,19 @@ 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;
+ int swapped = 0;
/* iterate through all pbuf in chain */
- for(q = p; q != NULL; q = q->next) {
+ 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));
+ (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;
+ swapped = !swapped;
acc = SWAP_BYTES_IN_WORD(acc);
}
/*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
@@ -284,20 +282,21 @@ inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc)
acc = SWAP_BYTES_IN_WORD(acc);
}
- acc += (u32_t)htons((u16_t)proto);
- acc += (u32_t)htons(proto_len);
+ 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 propably faster than if statements... */
+ 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 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * 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)
@@ -309,38 +308,40 @@ inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc)
*/
u16_t
inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
- ip_addr_t *src, ip_addr_t *dest)
+ 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);
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
addr = ip4_addr_get_u32(dest);
- acc += (addr & 0xffffUL);
- acc += ((addr >> 16) & 0xffffUL);
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(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 src source ipv6 address (used for checksum of pseudo header)
- * @param dst destination ipv6 address (used for checksum of pseudo header)
* @param proto ipv6 protocol/next header (used for checksum of pseudo header)
* @param proto_len length of the ipv6 payload (used for checksum of pseudo header)
+ * @param 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,
- ip6_addr_t *src, ip6_addr_t *dest)
+ const ip6_addr_t *src, const ip6_addr_t *dest)
{
u32_t acc = 0;
u32_t addr;
@@ -348,11 +349,11 @@ ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
for (addr_part = 0; addr_part < 4; addr_part++) {
addr = src->addr[addr_part];
- acc += (addr & 0xffffUL);
- acc += ((addr >> 16) & 0xffffUL);
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
addr = dest->addr[addr_part];
- acc += (addr & 0xffffUL);
- acc += ((addr >> 16) & 0xffffUL);
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
}
/* fold down to 16 bits */
acc = FOLD_U32T(acc);
@@ -362,31 +363,62 @@ ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
}
#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)
+ u16_t chksum_len, u32_t acc)
{
struct pbuf *q;
- u8_t swapped = 0;
+ int swapped = 0;
u16_t chklen;
/* iterate through all pbuf in chain */
- for(q = p; (q != NULL) && (chksum_len > 0); q = q->next) {
+ 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));
+ (void *)q, (void *)q->next));
chklen = q->len;
if (chklen > chksum_len) {
chklen = chksum_len;
}
acc += LWIP_CHKSUM(q->payload, chklen);
- chksum_len -= chklen;
+ chksum_len = (u16_t)(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;
+ swapped = !swapped;
acc = SWAP_BYTES_IN_WORD(acc);
}
/*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
@@ -396,20 +428,21 @@ inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len,
acc = SWAP_BYTES_IN_WORD(acc);
}
- acc += (u32_t)htons((u16_t)proto);
- acc += (u32_t)htons(proto_len);
+ 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 propably faster than if statements... */
+ 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 pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * 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)
@@ -421,23 +454,24 @@ inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len,
*/
u16_t
inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
- u16_t chksum_len, ip_addr_t *src, ip_addr_t *dest)
+ 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);
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
addr = ip4_addr_get_u32(dest);
- acc += (addr & 0xffffUL);
- acc += ((addr >> 16) & 0xffffUL);
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(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
/**
@@ -446,16 +480,16 @@ inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
* portion of the payload.
*
* @param p chain of pbufs over that a checksum should be calculated (ip data part)
- * @param src source ipv6 address (used for checksum of pseudo header)
- * @param dst destination ipv6 address (used for checksum of pseudo header)
* @param proto ipv6 protocol/next header (used for checksum of pseudo header)
* @param proto_len length of the ipv6 payload (used for checksum of pseudo header)
* @param chksum_len number of payload bytes used to compute chksum
+ * @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, ip6_addr_t *src, ip6_addr_t *dest)
+ u16_t chksum_len, const ip6_addr_t *src, const ip6_addr_t *dest)
{
u32_t acc = 0;
u32_t addr;
@@ -463,11 +497,11 @@ ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
for (addr_part = 0; addr_part < 4; addr_part++) {
addr = src->addr[addr_part];
- acc += (addr & 0xffffUL);
- acc += ((addr >> 16) & 0xffffUL);
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
addr = dest->addr[addr_part];
- acc += (addr & 0xffffUL);
- acc += ((addr >> 16) & 0xffffUL);
+ acc = (u32_t)(acc + (addr & 0xffffUL));
+ acc = (u32_t)(acc + ((addr >> 16) & 0xffffUL));
}
/* fold down to 16 bits */
acc = FOLD_U32T(acc);
@@ -477,6 +511,36 @@ ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
}
#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
@@ -488,9 +552,9 @@ ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
*/
u16_t
-inet_chksum(void *dataptr, u16_t len)
+inet_chksum(const void *dataptr, u16_t len)
{
- return ~LWIP_CHKSUM(dataptr, len);
+ return (u16_t)~(unsigned int)LWIP_CHKSUM(dataptr, len);
}
/**
@@ -505,15 +569,14 @@ inet_chksum_pbuf(struct pbuf *p)
{
u32_t acc;
struct pbuf *q;
- u8_t swapped;
+ int swapped = 0;
acc = 0;
- swapped = 0;
- for(q = p; q != NULL; q = q->next) {
+ 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;
+ swapped = !swapped;
acc = SWAP_BYTES_IN_WORD(acc);
}
}
diff --git a/lwip/src/core/init.c b/lwip/src/core/init.c
index c24c027..8841fa0 100644
--- a/lwip/src/core/init.c
+++ b/lwip/src/core/init.c
@@ -6,9 +6,9 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -17,23 +17,22 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
*
+ * Author: Adam Dunkels <adam@sics.se>
*/
#include "lwip/opt.h"
@@ -49,168 +48,202 @@
#include "lwip/ip.h"
#include "lwip/raw.h"
#include "lwip/udp.h"
-#include "lwip/tcp_impl.h"
-#include "lwip/snmp_msg.h"
-#include "lwip/autoip.h"
+#include "lwip/priv/tcp_priv.h"
#include "lwip/igmp.h"
#include "lwip/dns.h"
-#include "lwip/timers.h"
-#include "netif/etharp.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"
+#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"
+#error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h"
#endif
#if (!LWIP_UDP && LWIP_UDPLITE)
- #error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h"
-#endif
-#if (!LWIP_UDP && LWIP_SNMP)
- #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#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"
+#error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h"
#endif
-#if (!LWIP_UDP && LWIP_IGMP)
- #error "If you want to use IGMP, you have to define LWIP_UDP=1 in your lwipopts.h"
-#endif
-#if (!LWIP_UDP && LWIP_SNMP)
- #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#if (!LWIP_UDP && !LWIP_RAW && LWIP_MULTICAST_TX_OPTIONS)
+#error "If you want to use LWIP_MULTICAST_TX_OPTIONS, you have to define LWIP_UDP=1 and/or LWIP_RAW=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"
+#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"
+#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"
+#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"
+#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"
+#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"
+#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_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"
+#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"
+#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < LWIP_NUM_SYS_TIMEOUT_INTERNAL)
+#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!"
+#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"
+#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"
+#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"
+#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"
+#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_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"
+#if (LWIP_TCP && LWIP_TCP_SACK_OUT && !TCP_QUEUE_OOSEQ)
+#error "To use LWIP_TCP_SACK_OUT, TCP_QUEUE_OOSEQ needs to be enabled"
+#endif
+#if (LWIP_TCP && LWIP_TCP_SACK_OUT && (LWIP_TCP_MAX_SACK_NUM < 1))
+#error "LWIP_TCP_MAX_SACK_NUM must be greater than 0"
#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"
+#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"
+#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_NETCONN && LWIP_SOCKET)
- #error "If you want to use Socket API, you have to define LWIP_NETCONN=1 in your lwipopts.h"
+#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"
+#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"
+#error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h"
#endif
#if (!LWIP_ARP && LWIP_AUTOIP)
- #error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h"
-#endif
-#if (LWIP_SNMP && (SNMP_CONCURRENT_REQUESTS<=0))
- #error "If you want to use SNMP, you have to define SNMP_CONCURRENT_REQUESTS>=1 in your lwipopts.h"
-#endif
-#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0))
- #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h"
+#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"
+#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"
+#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"
+#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"
+#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"
+#error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST"
#endif
-#if PPP_SUPPORT && !PPPOS_SUPPORT & !PPPOE_SUPPORT
- #error "PPP_SUPPORT needs either PPPOS_SUPPORT or PPPOE_SUPPORT turned on"
+#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 !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT)
- #error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT"
+#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 (LWIP_IGMP || LWIP_IPV6) && !defined(LWIP_RAND)
- #error "When using IGMP or IPv6, LWIP_RAND() needs to be defined to a random-function returning an u32_t random value"
+#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"
+#error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too"
#endif
#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE
- #error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets"
-#endif
-#if IP_FRAG && IP_FRAG_USES_STATIC_BUF && LWIP_NETIF_TX_SINGLE_PBUF
- #error "LWIP_NETIF_TX_SINGLE_PBUF does not work with IP_FRAG_USES_STATIC_BUF==1 as that creates pbuf queues"
+#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"
+#error "NETCONN_COPY != TCP_WRITE_FLAG_COPY"
#endif
#if NETCONN_MORE != TCP_WRITE_FLAG_MORE
- #error "NETCONN_MORE != TCP_WRITE_FLAG_MORE"
+#error "NETCONN_MORE != TCP_WRITE_FLAG_MORE"
#endif
-#endif /* LWIP_NETCONN && LWIP_TCP */
+#endif /* LWIP_NETCONN && LWIP_TCP */
#if LWIP_SOCKET
/* Check that the SO_* socket options and SOF_* lwIP-internal flags match */
-#if SO_ACCEPTCONN != SOF_ACCEPTCONN
- #error "SO_ACCEPTCONN != SOF_ACCEPTCONN"
-#endif
#if SO_REUSEADDR != SOF_REUSEADDR
- #error "WARNING: SO_REUSEADDR != SOF_REUSEADDR"
+#error "WARNING: SO_REUSEADDR != SOF_REUSEADDR"
#endif
#if SO_KEEPALIVE != SOF_KEEPALIVE
- #error "WARNING: SO_KEEPALIVE != SOF_KEEPALIVE"
+#error "WARNING: SO_KEEPALIVE != SOF_KEEPALIVE"
#endif
#if SO_BROADCAST != SOF_BROADCAST
- #error "WARNING: SO_BROADCAST != SOF_BROADCAST"
-#endif
-#if SO_LINGER != SOF_LINGER
- #error "WARNING: SO_LINGER != SOF_LINGER"
+#error "WARNING: SO_BROADCAST != SOF_BROADCAST"
#endif
#endif /* LWIP_SOCKET */
@@ -218,22 +251,22 @@
/* Compile-time checks for deprecated options.
*/
#ifdef MEMP_NUM_TCPIP_MSG
- #error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h."
-#endif
-#ifdef MEMP_NUM_API_MSG
- #error "MEMP_NUM_API_MSG option is deprecated. Remove it from your lwipopts.h."
+#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."
+#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."
+#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."
+#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."
+#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
@@ -244,9 +277,9 @@
#endif
/* MEMP sanity checks */
-#if !LWIP_DISABLE_MEMP_SANITY_CHECKS
-#if LWIP_NETCONN
#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
@@ -254,46 +287,66 @@
#if MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB)
#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN. If you know what you are doing, define LWIP_DISABLE_MEMP_SANITY_CHECKS to 1 to disable this error."
#endif
-#endif /* MEMP_MEM_MALLOC */
-#endif /* LWIP_NETCONN */
+#endif /* LWIP_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."
+#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."
+#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."
+#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."
+#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."
+#error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
#endif
-#if !MEMP_MEM_MALLOC && (PBUF_POOL_BUFSIZE <= (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))
- #error "lwip_sanity_check: WARNING: PBUF_POOL_BUFSIZE does not provide enough space for protocol headers. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#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 && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))))
- #error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#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."
+#error "lwip_sanity_check: WARNING: TCP_WND is smaller than MSS. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
#endif
#endif /* LWIP_TCP */
#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */
/**
- * Perform Sanity check of user-configurable values, and initialize all modules.
+ * @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 = 0;
+ 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
@@ -303,13 +356,12 @@ lwip_init(void)
memp_init();
pbuf_init();
netif_init();
-#if LWIP_SOCKET
- lwip_socket_init();
-#endif /* LWIP_SOCKET */
+#if LWIP_IPV4
ip_init();
#if LWIP_ARP
etharp_init();
#endif /* LWIP_ARP */
+#endif /* LWIP_IPV4 */
#if LWIP_RAW
raw_init();
#endif /* LWIP_RAW */
@@ -319,25 +371,15 @@ lwip_init(void)
#if LWIP_TCP
tcp_init();
#endif /* LWIP_TCP */
-#if LWIP_SNMP
- snmp_init();
-#endif /* LWIP_SNMP */
-#if LWIP_AUTOIP
- autoip_init();
-#endif /* LWIP_AUTOIP */
#if LWIP_IGMP
igmp_init();
#endif /* LWIP_IGMP */
#if LWIP_DNS
dns_init();
#endif /* LWIP_DNS */
-#if LWIP_IPV6
- ip6_init();
- nd6_init();
-#if LWIP_IPV6_MLD
- mld6_init();
-#endif /* LWIP_IPV6_MLD */
-#endif /* LWIP_IPV6 */
+#if PPP_SUPPORT
+ ppp_init();
+#endif
#if LWIP_TIMERS
sys_timeouts_init();
diff --git a/lwip/src/core/ip.c b/lwip/src/core/ip.c
new file mode 100644
index 0000000..18514cf
--- /dev/null
+++ b/lwip/src/core/ip.c
@@ -0,0 +1,167 @@
+/**
+ * @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 numeric IP address (both versions) into 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 *ipaddr_ntoa(const ip_addr_t *addr)
+{
+ if (addr == NULL) {
+ return NULL;
+ }
+ if (IP_IS_V6(addr)) {
+ return ip6addr_ntoa(ip_2_ip6(addr));
+ } else {
+ return ip4addr_ntoa(ip_2_ip4(addr));
+ }
+}
+
+/**
+ * @ingroup ipaddr
+ * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
+ *
+ * @param addr ip address in network order to convert
+ * @param buf target buffer where the string is stored
+ * @param buflen length of buf
+ * @return either pointer to buf which now holds the ASCII
+ * representation of addr or NULL if buf was too small
+ */
+char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen)
+{
+ if (addr == NULL) {
+ return NULL;
+ }
+ if (IP_IS_V6(addr)) {
+ return ip6addr_ntoa_r(ip_2_ip6(addr), buf, buflen);
+ } else {
+ return ip4addr_ntoa_r(ip_2_ip4(addr), buf, buflen);
+ }
+}
+
+/**
+ * @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/lwip/src/core/ipv4/autoip.c b/lwip/src/core/ipv4/autoip.c
index b122da2..a3cf9fe 100644
--- a/lwip/src/core/ipv4/autoip.c
+++ b/lwip/src/core/ipv4/autoip.c
@@ -2,6 +2,28 @@
* @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
*/
/*
@@ -32,58 +54,22 @@
* OF SUCH DAMAGE.
*
* Author: Dominik Spies <kontakt@dspies.de>
- *
- * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
- * with RFC 3927.
- *
- *
- * Please coordinate changes and requests with Dominik Spies
- * <kontakt@dspies.de>
- */
-
-/*******************************************************************************
- * USAGE:
- *
- * define LWIP_AUTOIP 1 in your lwipopts.h
- *
- * If you don't use tcpip.c (so, don't call, you don't call tcpip_init):
- * - First, call autoip_init().
- * - call autoip_tmr() all AUTOIP_TMR_INTERVAL msces,
- * that should be defined in autoip.h.
- * I recommend a value of 100. The value must divide 1000 with a remainder almost 0.
- * Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 ....
- *
- * Without DHCP:
- * - Call autoip_start() after netif_add().
- *
- * With DHCP:
- * - define LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h.
- * - Configure your DHCP Client.
- *
*/
#include "lwip/opt.h"
-#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
+#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/udp.h" */
#include "lwip/ip_addr.h"
#include "lwip/netif.h"
#include "lwip/autoip.h"
-#include "netif/etharp.h"
+#include "lwip/etharp.h"
+#include "lwip/prot/autoip.h"
-#include <stdlib.h>
#include <string.h>
-/* 169.254.0.0 */
-#define AUTOIP_NET 0xA9FE0000
-/* 169.254.1.0 */
-#define AUTOIP_RANGE_START (AUTOIP_NET | 0x0100)
-/* 169.254.254.255 */
-#define AUTOIP_RANGE_END (AUTOIP_NET | 0xFEFF)
-
-
/** Pseudo random macro based on netif informations.
* You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */
#ifndef LWIP_AUTOIP_RAND
@@ -91,7 +77,7 @@
((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \
((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \
((u32_t)((netif->hwaddr[4]) & 0xff))) + \
- (netif->autoip?netif->autoip->tried_llipaddr:0))
+ (netif_autoip_data(netif)? netif_autoip_data(netif)->tried_llipaddr : 0))
#endif /* LWIP_AUTOIP_RAND */
/**
@@ -100,46 +86,34 @@
*/
#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR
#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \
- htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \
+ 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 void autoip_handle_arp_conflict(struct netif *netif);
-
-/* creates a pseudo random LL IP-Address for a network interface */
-static void autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr);
-
-/* sends an ARP probe */
-static err_t autoip_arp_probe(struct netif *netif);
-
-/* sends an ARP announce */
static err_t autoip_arp_announce(struct netif *netif);
-
-/* configure interface for use with current LL IP-Address */
-static err_t autoip_bind(struct netif *netif);
-
-/* start sending probes for llipaddr */
static void autoip_start_probing(struct netif *netif);
-
-/** Set a statically allocated struct autoip to work with.
+/**
+ * @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 dhcp (uninitialised) dhcp struct allocated by the application
+ * @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 == 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->autoip = autoip;
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_AUTOIP, autoip);
}
/** Restart AutoIP client and check the next address (conflict detected)
@@ -149,7 +123,8 @@ autoip_set_struct(struct netif *netif, struct autoip *autoip)
static void
autoip_restart(struct netif *netif)
{
- netif->autoip->tried_llipaddr++;
+ struct autoip *autoip = netif_autoip_data(netif);
+ autoip->tried_llipaddr++;
autoip_start(netif);
}
@@ -159,30 +134,27 @@ autoip_restart(struct netif *netif)
static void
autoip_handle_arp_conflict(struct netif *netif)
{
- /* Somehow detect if we are defending or retreating */
- unsigned char defend = 1; /* tbd */
+ struct autoip *autoip = netif_autoip_data(netif);
- if (defend) {
- if (netif->autoip->lastconflict > 0) {
- /* retreat, there was a conflicting ARP in the last
- * DEFEND_INTERVAL seconds
- */
- LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
- ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n"));
+ /* 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. */
- /* TODO: close all TCP sessions */
- autoip_restart(netif);
- } else {
- LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
- ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n"));
- autoip_arp_announce(netif);
- netif->autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND;
- }
- } else {
+ 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 do not defend, retreating\n"));
- /* TODO: close all TCP sessions */
+ ("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;
}
}
@@ -193,16 +165,18 @@ autoip_handle_arp_conflict(struct netif *netif)
* @param ipaddr ip address to initialize
*/
static void
-autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr)
+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 = ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif));
- addr += netif->autoip->tried_llipaddr;
+ 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 */
+ /* Now, 169.254.0.0 <= addr <= 169.254.255.255 */
if (addr < AUTOIP_RANGE_START) {
addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
@@ -211,13 +185,13 @@ autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr)
addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
}
LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) &&
- (addr <= AUTOIP_RANGE_END));
- ip4_addr_set_u32(ipaddr, htonl(addr));
-
+ (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)(netif->autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr),
- ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+ ("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)));
}
/**
@@ -228,9 +202,9 @@ autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr)
static err_t
autoip_arp_probe(struct netif *netif)
{
- return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,
- (struct eth_addr *)netif->hwaddr, IP_ADDR_ANY, &ethzero,
- &netif->autoip->llipaddr, ARP_REQUEST);
+ struct autoip *autoip = netif_autoip_data(netif);
+ /* this works because netif->ip_addr is ANY */
+ return etharp_request(netif, &autoip->llipaddr);
}
/**
@@ -241,9 +215,7 @@ autoip_arp_probe(struct netif *netif)
static err_t
autoip_arp_announce(struct netif *netif)
{
- return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,
- (struct eth_addr *)netif->hwaddr, &netif->autoip->llipaddr, &ethzero,
- &netif->autoip->llipaddr, ARP_REQUEST);
+ return etharp_gratuitous(netif);
}
/**
@@ -254,29 +226,26 @@ autoip_arp_announce(struct netif *netif)
static err_t
autoip_bind(struct netif *netif)
{
- struct autoip *autoip = netif->autoip;
- ip_addr_t sn_mask, gw_addr;
+ 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)));
+ ("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ (void *)netif, netif->name[0], netif->name[1], (u16_t)netif->num,
+ ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
+ ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
IP4_ADDR(&sn_mask, 255, 255, 0, 0);
IP4_ADDR(&gw_addr, 0, 0, 0, 0);
- netif_set_ipaddr(netif, &autoip->llipaddr);
- netif_set_netmask(netif, &sn_mask);
- netif_set_gw(netif, &gw_addr);
-
- /* bring the interface up */
- netif_set_up(netif);
+ 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
@@ -284,42 +253,37 @@ autoip_bind(struct netif *netif)
err_t
autoip_start(struct netif *netif)
{
- struct autoip *autoip = netif->autoip;
+ struct autoip *autoip = netif_autoip_data(netif);
err_t result = ERR_OK;
- if (netif_is_up(netif)) {
- netif_set_down(netif);
- }
+ 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
*/
- ip_addr_set_zero(&netif->ip_addr);
- ip_addr_set_zero(&netif->netmask);
- ip_addr_set_zero(&netif->gw);
+ 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));
+ ("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));
+ ("autoip_start(): starting new AUTOIP client\n"));
+ autoip = (struct autoip *)mem_calloc(1, sizeof(struct autoip));
if (autoip == NULL) {
LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
- ("autoip_start(): could not allocate autoip\n"));
+ ("autoip_start(): could not allocate autoip\n"));
return ERR_MEM;
}
- memset(autoip, 0, sizeof(struct autoip));
/* store this AutoIP client in the netif */
- netif->autoip = autoip;
+ 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;
- ip_addr_set_zero(&autoip->llipaddr);
+ ip4_addr_set_zero(&autoip->llipaddr);
autoip->lastconflict = 0;
}
@@ -332,24 +296,24 @@ autoip_start(struct netif *netif)
static void
autoip_start_probing(struct netif *netif)
{
- struct autoip *autoip = netif->autoip;
+ 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(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
- ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
+ ("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
- * choosen out of 0 to PROBE_WAIT seconds.
+ * 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
- * accquiring and probing address
+ * acquiring and probing address
* compliant to RFC 3927 Section 2.2.1
*/
if (autoip->tried_llipaddr > MAX_CONFLICTS) {
@@ -366,13 +330,15 @@ autoip_start_probing(struct netif *netif)
void
autoip_network_changed(struct netif *netif)
{
- if (netif->autoip && netif->autoip->state != AUTOIP_STATE_OFF) {
- netif_set_down(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
@@ -380,8 +346,14 @@ autoip_network_changed(struct netif *netif)
err_t
autoip_stop(struct netif *netif)
{
- netif->autoip->state = AUTOIP_STATE_OFF;
- netif_set_down(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;
}
@@ -389,87 +361,87 @@ autoip_stop(struct netif *netif)
* Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds
*/
void
-autoip_tmr()
+autoip_tmr(void)
{
- struct netif *netif = netif_list;
+ struct netif *netif;
/* loop through netif's */
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
+ struct autoip *autoip = netif_autoip_data(netif);
/* only act on AutoIP configured interfaces */
- if (netif->autoip != NULL) {
- if (netif->autoip->lastconflict > 0) {
- netif->autoip->lastconflict--;
+ 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)(netif->autoip->state), netif->autoip->ttw));
+ ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n",
+ (u16_t)(autoip->state), autoip->ttw));
+
+ if (autoip->ttw > 0) {
+ autoip->ttw--;
+ }
- switch(netif->autoip->state) {
+ switch (autoip->state) {
case AUTOIP_STATE_PROBING:
- if (netif->autoip->ttw > 0) {
- netif->autoip->ttw--;
- } else {
- if (netif->autoip->sent_num >= PROBE_NUM) {
- netif->autoip->state = AUTOIP_STATE_ANNOUNCING;
- netif->autoip->sent_num = 0;
- netif->autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
+ 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(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
- ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
+ ("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"));
- netif->autoip->sent_num++;
- /* calculate time to wait to next probe */
- netif->autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) %
- ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) +
- PROBE_MIN * AUTOIP_TICKS_PER_SECOND);
+ 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 (netif->autoip->ttw > 0) {
- netif->autoip->ttw--;
- } else {
- if (netif->autoip->sent_num == 0) {
- /* We are here the first time, so we waited ANNOUNCE_WAIT seconds
- * Now we can bind to an IP address and use it.
- *
- * autoip_bind calls netif_set_up. This triggers a gratuitous ARP
- * which counts as an announcement.
- */
- autoip_bind(netif);
- } else {
- autoip_arp_announce(netif);
- LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
- ("autoip_tmr() ANNOUNCING Sent Announce\n"));
- }
- netif->autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND;
- netif->autoip->sent_num++;
-
- if (netif->autoip->sent_num >= ANNOUNCE_NUM) {
- netif->autoip->state = AUTOIP_STATE_BOUND;
- netif->autoip->sent_num = 0;
- netif->autoip->ttw = 0;
- LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
- ("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
- ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
- ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
+ 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_arp_input.
+ * Handles every incoming ARP Packet, called by etharp_input().
*
* @param netif network interface to use for autoip processing
* @param hdr Incoming ARP packet
@@ -477,52 +449,76 @@ autoip_tmr()
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 ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) {
- /* when ip.src == llipaddr && hw.src != netif->hwaddr
- *
- * when probing ip.dst == llipaddr && hw.src != netif->hwaddr
- * we have a conflict and must solve it
- */
- ip_addr_t sipaddr, dipaddr;
+ 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);
+ SMEMCPY(netifaddr.addr, netif->hwaddr, ETH_HWADDR_LEN);
- /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
+ /* Copy struct ip4_addr_wordaligned 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 ((netif->autoip->state == AUTOIP_STATE_PROBING) ||
- ((netif->autoip->state == AUTOIP_STATE_ANNOUNCING) &&
- (netif->autoip->sent_num == 0))) {
- /* RFC 3927 Section 2.2.1:
- * from beginning to after ANNOUNCE_WAIT
- * seconds we have a conflict if
- * ip.src == llipaddr OR
- * ip.dst == llipaddr && hw.src != own hwaddr
- */
- if ((ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr)) ||
- (ip_addr_cmp(&dipaddr, &netif->autoip->llipaddr) &&
+ IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr);
+ IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&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_arp_reply(): Probe Conflict detected\n"));
autoip_restart(netif);
}
} else {
- /* RFC 3927 Section 2.5:
- * in any state we have a conflict if
- * ip.src == llipaddr && hw.src != own hwaddr
- */
- if (ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr) &&
+ /* 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_arp_reply(): Conflicting ARP-Packet detected\n"));
autoip_handle_arp_conflict(netif);
}
}
}
}
-#endif /* LWIP_AUTOIP */
+/** 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/lwip/src/core/ipv4/dhcp.c b/lwip/src/core/ipv4/dhcp.c
new file mode 100644
index 0000000..821aa65
--- /dev/null
+++ b/lwip/src/core/ipv4/dhcp.c
@@ -0,0 +1,1971 @@
+/**
+ * @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 LWIP_HOOK_DHCP_APPEND_OPTIONS
+ * @see LWIP_HOOK_DHCP_PARSE_OPTION
+ *
+ * @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 "lwip/prot/iana.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+#ifndef LWIP_HOOK_DHCP_APPEND_OPTIONS
+#define LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, state, msg, msg_type, options_len_ptr)
+#endif
+#ifndef LWIP_HOOK_DHCP_PARSE_OPTION
+#define LWIP_HOOK_DHCP_PARSE_OPTION(netif, dhcp, state, msg, msg_type, option, len, pbuf, offset) do { LWIP_UNUSED_ARG(msg); } while(0)
+#endif
+
+/** 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 struct pbuf *dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type, u16_t *options_out_len);
+/* add a DHCP option (type, then length in bytes) */
+static u16_t dhcp_option(u16_t options_out_len, u8_t *options, u8_t option_type, u8_t option_len);
+/* add option values */
+static u16_t dhcp_option_byte(u16_t options_out_len, u8_t *options, u8_t value);
+static u16_t dhcp_option_short(u16_t options_out_len, u8_t *options, u16_t value);
+static u16_t dhcp_option_long(u16_t options_out_len, u8_t *options, u32_t value);
+#if LWIP_NETIF_HOSTNAME
+static u16_t dhcp_option_hostname(u16_t options_out_len, u8_t *options, struct netif *netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+/* always add the DHCP options trailer to end and pad */
+static void dhcp_option_trailer(u16_t options_out_len, u8_t *options, struct pbuf *p_out);
+
+/** 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, LWIP_IANA_PORT_DHCP_CLIENT);
+ udp_connect(dhcp_pcb, IP4_ADDR_ANY, LWIP_IANA_PORT_DHCP_SERVER);
+ 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 = (u16_t)((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_msg *msg_in)
+{
+ 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, 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;
+ struct pbuf *p_out;
+ u16_t options_out_len;
+
+ LWIP_ERROR("dhcp_select: netif != NULL", (netif != NULL), return ERR_ARG;);
+ LWIP_ERROR("dhcp_select: dhcp != NULL", (dhcp != NULL), return ERR_VAL;);
+
+ 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 */
+ p_out = dhcp_create_msg(netif, dhcp, DHCP_REQUEST, &options_out_len);
+ if (p_out != NULL) {
+ struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN(netif));
+
+ /* MUST request the offered IP address */
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_REQUESTED_IP, 4);
+ options_out_len = dhcp_option_long(options_out_len, msg_out->options, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_SERVER_ID, 4);
+ options_out_len = dhcp_option_long(options_out_len, msg_out->options, lwip_ntohl(ip4_addr_get_u32(ip_2_ip4(&dhcp->server_ip_addr))));
+
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+ for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+ options_out_len = dhcp_option_byte(options_out_len, msg_out->options, dhcp_discover_request_options[i]);
+ }
+
+#if LWIP_NETIF_HOSTNAME
+ options_out_len = dhcp_option_hostname(options_out_len, msg_out->options, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_REQUESTING, msg_out, DHCP_REQUEST, &options_out_len);
+ dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+ /* send broadcast to any DHCP server */
+ result = udp_sendto_if_src(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif, IP4_ADDR_ANY);
+ pbuf_free(p_out);
+ 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"));
+ result = ERR_MEM;
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+ msecs = (u16_t)((dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000);
+ dhcp->request_timeout = (u16_t)((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;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_coarse_tmr()\n"));
+ /* iterate through all network interfaces */
+ NETIF_FOREACH(netif) {
+ /* 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_and_stop(netif);
+ dhcp_start(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);
+ }
+ }
+ }
+}
+
+/**
+ * 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;
+ /* loop through netif's */
+ NETIF_FOREACH(netif) {
+ 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);
+ }
+ }
+ }
+}
+
+/**
+ * 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_and_stop(netif);
+ dhcp_start(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 = (u16_t)((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 = (u16_t)((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_msg *msg_in)
+{
+ 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, 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, 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_val(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(): mallocing 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"));
+
+ 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_release_and_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;
+ struct pbuf *p_out;
+ u16_t options_out_len;
+
+ 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 */
+ p_out = dhcp_create_msg(netif, &dhcp, DHCP_INFORM, &options_out_len);
+ if (p_out != NULL) {
+ struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN(netif));
+
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, &dhcp, DHCP_STATE_INFORMING, msg_out, DHCP_INFORM, &options_out_len);
+ dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("dhcp_inform: INFORMING\n"));
+
+ udp_sendto_if(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif);
+
+ pbuf_free(p_out);
+ } 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:
+ LWIP_ASSERT("invalid dhcp->state", dhcp->state <= DHCP_STATE_BACKING_OFF);
+ /* 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;
+ u16_t msecs;
+ struct pbuf *p_out;
+ u16_t options_out_len;
+
+ 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 */
+ p_out = dhcp_create_msg(netif, dhcp, DHCP_DECLINE, &options_out_len);
+ if (p_out != NULL) {
+ struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_REQUESTED_IP, 4);
+ options_out_len = dhcp_option_long(options_out_len, msg_out->options, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_BACKING_OFF, msg_out, DHCP_DECLINE, &options_out_len);
+ dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+ /* per section 4.4.4, broadcast DECLINE messages */
+ result = udp_sendto_if_src(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif, IP4_ADDR_ANY);
+ pbuf_free(p_out);
+ 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"));
+ result = ERR_MEM;
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+ msecs = 10 * 1000;
+ dhcp->request_timeout = (u16_t)((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;
+ struct pbuf *p_out;
+ u16_t options_out_len;
+
+ 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 */
+ p_out = dhcp_create_msg(netif, dhcp, DHCP_DISCOVER, &options_out_len);
+ if (p_out != NULL) {
+ struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: making request\n"));
+
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN(netif));
+
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+ for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+ options_out_len = dhcp_option_byte(options_out_len, msg_out->options, dhcp_discover_request_options[i]);
+ }
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_SELECTING, msg_out, DHCP_DISCOVER, &options_out_len);
+ dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: sendto(DISCOVER, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER)\n"));
+ udp_sendto_if_src(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif, IP4_ADDR_ANY);
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_discover: deleting()ing\n"));
+ pbuf_free(p_out);
+ 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 = (u16_t)((dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000);
+ dhcp->request_timeout = (u16_t)((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;
+ struct pbuf *p_out;
+ u16_t options_out_len;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_renew()\n"));
+ dhcp_set_state(dhcp, DHCP_STATE_RENEWING);
+
+ /* create and initialize the DHCP message header */
+ p_out = dhcp_create_msg(netif, dhcp, DHCP_REQUEST, &options_out_len);
+ if (p_out != NULL) {
+ struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN(netif));
+
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+ for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+ options_out_len = dhcp_option_byte(options_out_len, msg_out->options, dhcp_discover_request_options[i]);
+ }
+
+#if LWIP_NETIF_HOSTNAME
+ options_out_len = dhcp_option_hostname(options_out_len, msg_out->options, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_RENEWING, msg_out, DHCP_REQUEST, &options_out_len);
+ dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+ result = udp_sendto_if(dhcp_pcb, p_out, &dhcp->server_ip_addr, LWIP_IANA_PORT_DHCP_SERVER, netif);
+ pbuf_free(p_out);
+
+ 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"));
+ result = ERR_MEM;
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+ /* back-off on retries, but to a maximum of 20 seconds */
+ msecs = (u16_t)(dhcp->tries < 10 ? dhcp->tries * 2000 : 20 * 1000);
+ dhcp->request_timeout = (u16_t)((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;
+ struct pbuf *p_out;
+ u16_t options_out_len;
+
+ 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 */
+ p_out = dhcp_create_msg(netif, dhcp, DHCP_REQUEST, &options_out_len);
+ if (p_out != NULL) {
+ struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN(netif));
+
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+ for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+ options_out_len = dhcp_option_byte(options_out_len, msg_out->options, dhcp_discover_request_options[i]);
+ }
+
+#if LWIP_NETIF_HOSTNAME
+ options_out_len = dhcp_option_hostname(options_out_len, msg_out->options, netif);
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_REBINDING, msg_out, DHCP_DISCOVER, &options_out_len);
+ dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+ /* broadcast to server */
+ result = udp_sendto_if(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif);
+ pbuf_free(p_out);
+ 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"));
+ result = ERR_MEM;
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+ msecs = (u16_t)(dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000);
+ dhcp->request_timeout = (u16_t)((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;
+ struct pbuf *p_out;
+ u16_t options_out_len;
+
+ 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 */
+ p_out = dhcp_create_msg(netif, dhcp, DHCP_REQUEST, &options_out_len);
+ if (p_out != NULL) {
+ struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_MAX_MSG_SIZE, DHCP_OPTION_MAX_MSG_SIZE_LEN);
+ options_out_len = dhcp_option_short(options_out_len, msg_out->options, DHCP_MAX_MSG_LEN_MIN_REQUIRED);
+
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_REQUESTED_IP, 4);
+ options_out_len = dhcp_option_long(options_out_len, msg_out->options, lwip_ntohl(ip4_addr_get_u32(&dhcp->offered_ip_addr)));
+
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_PARAMETER_REQUEST_LIST, LWIP_ARRAYSIZE(dhcp_discover_request_options));
+ for (i = 0; i < LWIP_ARRAYSIZE(dhcp_discover_request_options); i++) {
+ options_out_len = dhcp_option_byte(options_out_len, msg_out->options, dhcp_discover_request_options[i]);
+ }
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_REBOOTING, msg_out, DHCP_REQUEST, &options_out_len);
+ dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+ /* broadcast to server */
+ result = udp_sendto_if(dhcp_pcb, p_out, IP_ADDR_BROADCAST, LWIP_IANA_PORT_DHCP_SERVER, netif);
+ pbuf_free(p_out);
+ 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"));
+ result = ERR_MEM;
+ }
+ if (dhcp->tries < 255) {
+ dhcp->tries++;
+ }
+ msecs = (u16_t)(dhcp->tries < 10 ? dhcp->tries * 1000 : 10 * 1000);
+ dhcp->request_timeout = (u16_t)((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 and stop DHCP statemachine (and AUTOIP if LWIP_DHCP_AUTOIP_COOP).
+ *
+ * @param netif network interface
+ */
+void
+dhcp_release_and_stop(struct netif *netif)
+{
+ struct dhcp *dhcp = netif_dhcp_data(netif);
+ ip_addr_t server_ip_addr;
+
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE, ("dhcp_release_and_stop()\n"));
+ if (dhcp == NULL) {
+ return;
+ }
+
+ /* already off? -> nothing to do */
+ if (dhcp->state == DHCP_STATE_OFF) {
+ return;
+ }
+
+ ip_addr_copy(server_ip_addr, dhcp->server_ip_addr);
+
+ /* 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;
+
+ /* send release message when current IP was assigned via DHCP */
+ if (dhcp_supplied_address(netif)) {
+ /* create and initialize the DHCP message header */
+ struct pbuf *p_out;
+ u16_t options_out_len;
+ p_out = dhcp_create_msg(netif, dhcp, DHCP_RELEASE, &options_out_len);
+ if (p_out != NULL) {
+ struct dhcp_msg *msg_out = (struct dhcp_msg *)p_out->payload;
+ options_out_len = dhcp_option(options_out_len, msg_out->options, DHCP_OPTION_SERVER_ID, 4);
+ options_out_len = dhcp_option_long(options_out_len, msg_out->options, lwip_ntohl(ip4_addr_get_u32(ip_2_ip4(&server_ip_addr))));
+
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, dhcp->state, msg_out, DHCP_RELEASE, &options_out_len);
+ dhcp_option_trailer(options_out_len, msg_out->options, p_out);
+
+ udp_sendto_if(dhcp_pcb, p_out, &server_ip_addr, LWIP_IANA_PORT_DHCP_SERVER, netif);
+ pbuf_free(p_out);
+ 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);
+
+#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_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;
+ }
+}
+
+/**
+ * @ingroup dhcp4
+ * This function calls dhcp_release_and_stop() internally.
+ * @deprecated Use dhcp_release_and_stop() instead.
+ */
+err_t
+dhcp_release(struct netif *netif)
+{
+ dhcp_release_and_stop(netif);
+ return ERR_OK;
+}
+
+/**
+ * @ingroup dhcp4
+ * This function calls dhcp_release_and_stop() internally.
+ * @deprecated Use dhcp_release_and_stop() instead.
+ */
+void
+dhcp_stop(struct netif *netif)
+{
+ dhcp_release_and_stop(netif);
+}
+
+/*
+ * 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 u16_t
+dhcp_option(u16_t options_out_len, u8_t *options, u8_t option_type, u8_t option_len)
+{
+ LWIP_ASSERT("dhcp_option: options_out_len + 2 + option_len <= DHCP_OPTIONS_LEN", options_out_len + 2U + option_len <= DHCP_OPTIONS_LEN);
+ options[options_out_len++] = option_type;
+ options[options_out_len++] = option_len;
+ return options_out_len;
+}
+/*
+ * Concatenate a single byte to the outgoing DHCP message.
+ *
+ */
+static u16_t
+dhcp_option_byte(u16_t options_out_len, u8_t *options, u8_t value)
+{
+ LWIP_ASSERT("dhcp_option_byte: options_out_len < DHCP_OPTIONS_LEN", options_out_len < DHCP_OPTIONS_LEN);
+ options[options_out_len++] = value;
+ return options_out_len;
+}
+
+static u16_t
+dhcp_option_short(u16_t options_out_len, u8_t *options, u16_t value)
+{
+ LWIP_ASSERT("dhcp_option_short: options_out_len + 2 <= DHCP_OPTIONS_LEN", options_out_len + 2U <= DHCP_OPTIONS_LEN);
+ options[options_out_len++] = (u8_t)((value & 0xff00U) >> 8);
+ options[options_out_len++] = (u8_t) (value & 0x00ffU);
+ return options_out_len;
+}
+
+static u16_t
+dhcp_option_long(u16_t options_out_len, u8_t *options, u32_t value)
+{
+ LWIP_ASSERT("dhcp_option_long: options_out_len + 4 <= DHCP_OPTIONS_LEN", options_out_len + 4U <= DHCP_OPTIONS_LEN);
+ options[options_out_len++] = (u8_t)((value & 0xff000000UL) >> 24);
+ options[options_out_len++] = (u8_t)((value & 0x00ff0000UL) >> 16);
+ options[options_out_len++] = (u8_t)((value & 0x0000ff00UL) >> 8);
+ options[options_out_len++] = (u8_t)((value & 0x000000ffUL));
+ return options_out_len;
+}
+
+#if LWIP_NETIF_HOSTNAME
+static u16_t
+dhcp_option_hostname(u16_t options_out_len, u8_t *options, 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 - 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);
+ options_out_len = dhcp_option(options_out_len, options, DHCP_OPTION_HOSTNAME, (u8_t)len);
+ while (len--) {
+ options_out_len = dhcp_option_byte(options_out_len, options, *p++);
+ }
+ }
+ }
+ return options_out_len;
+}
+#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 pbuf *p, struct dhcp *dhcp)
+{
+ 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;
+ struct dhcp_msg *msg_in;
+
+ LWIP_UNUSED_ARG(dhcp);
+
+ /* 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;
+ }
+ 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 = (u16_t)(options_idx - q->len);
+ options_idx_max = (u16_t)(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) && (offset < offset_max) && (options[offset] != DHCP_OPTION_END)) {
+ u8_t op = options[offset];
+ u8_t len;
+ u8_t decode_len = 0;
+ int decode_idx = -1;
+ u16_t val_offset = (u16_t)(offset + 2);
+ if (val_offset < offset) {
+ /* overflow */
+ return ERR_BUF;
+ }
+ /* 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));
+ LWIP_HOOK_DHCP_PARSE_OPTION(ip_current_netif(), dhcp, dhcp->state, msg_in,
+ dhcp_option_given(dhcp, DHCP_OPTION_IDX_MSG_TYPE) ? (u8_t)dhcp_get_option_value(dhcp, DHCP_OPTION_IDX_MSG_TYPE) : 0,
+ op, len, q, val_offset);
+ break;
+ }
+ if (offset + len + 2 > 0xFFFF) {
+ /* overflow */
+ return ERR_BUF;
+ }
+ offset = (u16_t)(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 */
+ u16_t next_val_offset;
+ 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 = (u8_t)(decode_len - 4);
+ next_val_offset = (u16_t)(val_offset + 4);
+ if (next_val_offset < val_offset) {
+ /* overflow */
+ return ERR_BUF;
+ }
+ val_offset = next_val_offset;
+ 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 = (u16_t)(offset - q->len);
+ offset_max = (u16_t)(offset_max - q->len);
+ if ((offset < offset_max) && offset_max) {
+ q = q->next;
+ LWIP_ERROR("next pbuf was null", q != NULL, return ERR_VAL;);
+ 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;
+ struct dhcp_msg *msg_in;
+
+ 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);
+
+ 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(p, dhcp) != 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;
+ }
+
+ msg_in = (struct dhcp_msg *)p->payload;
+ /* 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, msg_in);
+#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, msg_in);
+ 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, msg_in);
+ }
+
+free_pbuf_and_return:
+ 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 struct pbuf *
+dhcp_create_msg(struct netif *netif, struct dhcp *dhcp, u8_t message_type, u16_t *options_out_len)
+{
+ u16_t i;
+ struct pbuf *p_out;
+ struct dhcp_msg *msg_out;
+ u16_t options_out_len_loc;
+
+#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 NULL;);
+ LWIP_ERROR("dhcp_create_msg: dhcp != NULL", (dhcp != NULL), return NULL;);
+ p_out = pbuf_alloc(PBUF_TRANSPORT, sizeof(struct dhcp_msg), PBUF_RAM);
+ if (p_out == NULL) {
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("dhcp_create_msg(): could not allocate pbuf\n"));
+ return NULL;
+ }
+ LWIP_ASSERT("dhcp_create_msg: check that first pbuf can hold struct dhcp_msg",
+ (p_out->len >= sizeof(struct dhcp_msg)));
+
+ /* DHCP_REQUEST should reuse 'xid' from DHCPOFFER */
+ if ((message_type != DHCP_REQUEST) || (dhcp->state == DHCP_STATE_REBOOTING)) {
+ /* 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));
+
+ msg_out = (struct dhcp_msg *)p_out->payload;
+ memset(msg_out, 0, sizeof(struct dhcp_msg));
+
+ msg_out->op = DHCP_BOOTREQUEST;
+ /* @todo: make link layer independent */
+ msg_out->htype = LWIP_IANA_HWTYPE_ETHERNET;
+ msg_out->hlen = netif->hwaddr_len;
+ msg_out->xid = lwip_htonl(dhcp->xid);
+ /* we don't need the broadcast flag since we can receive unicast traffic
+ before being fully configured! */
+ /* 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(msg_out->ciaddr, *netif_ip4_addr(netif));
+ }
+ for (i = 0; i < LWIP_MIN(DHCP_CHADDR_LEN, NETIF_MAX_HWADDR_LEN); i++) {
+ /* copy netif hardware address (padded with zeroes through memset already) */
+ msg_out->chaddr[i] = netif->hwaddr[i];
+ }
+ msg_out->cookie = PP_HTONL(DHCP_MAGIC_COOKIE);
+ /* Add option MESSAGE_TYPE */
+ options_out_len_loc = dhcp_option(0, msg_out->options, DHCP_OPTION_MESSAGE_TYPE, DHCP_OPTION_MESSAGE_TYPE_LEN);
+ options_out_len_loc = dhcp_option_byte(options_out_len_loc, msg_out->options, message_type);
+ if (options_out_len) {
+ *options_out_len = options_out_len_loc;
+ }
+ return p_out;
+}
+
+/**
+ * Add a DHCP message trailer
+ *
+ * Adds the END option to the DHCP message, and if
+ * necessary, up to three padding bytes.
+ */
+static void
+dhcp_option_trailer(u16_t options_out_len, u8_t *options, struct pbuf *p_out)
+{
+ options[options_out_len++] = DHCP_OPTION_END;
+ /* packet is too small, or not 4 byte aligned? */
+ while (((options_out_len < DHCP_MIN_OPTIONS_LEN) || (options_out_len & 3)) &&
+ (options_out_len < DHCP_OPTIONS_LEN)) {
+ /* add a fill/padding byte */
+ options[options_out_len++] = 0;
+ }
+ /* shrink the pbuf to the actual content length */
+ pbuf_realloc(p_out, (u16_t)(sizeof(struct dhcp_msg) - DHCP_OPTIONS_LEN + options_out_len));
+}
+
+/** 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) ||
+ (dhcp->state == DHCP_STATE_REBINDING);
+ }
+ return 0;
+}
+
+#endif /* LWIP_IPV4 && LWIP_DHCP */
diff --git a/lwip/src/netif/etharp.c b/lwip/src/core/ipv4/etharp.c
index 1b7eb98..9bb1df5 100644
--- a/lwip/src/netif/etharp.c
+++ b/lwip/src/core/ipv4/etharp.c
@@ -42,64 +42,50 @@
* 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/etharp.h"
#include "lwip/stats.h"
#include "lwip/snmp.h"
#include "lwip/dhcp.h"
#include "lwip/autoip.h"
-#include "netif/etharp.h"
-#include "lwip/ip6.h"
-
-#if PPPOE_SUPPORT
-#include "netif/ppp_oe.h"
-#endif /* PPPOE_SUPPORT */
+#include "lwip/prot/iana.h"
+#include "netif/ethernet.h"
#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
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
-#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */
+#if LWIP_IPV4 && LWIP_ARP /* don't build if not configured for use in lwipopts.h */
-/** the time an ARP entry stays valid after its last update,
- * for ARP_TMR_INTERVAL = 5000, this is
- * (240 * 5) seconds = 20 minutes.
- */
-#define ARP_MAXAGE 240
/** Re-request a used ARP entry 1 minute before it would expire to prevent
* breaking a steadily used connection because the ARP entry timed out. */
-#define ARP_AGE_REREQUEST_USED (ARP_MAXAGE - 12)
+#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 = 5000, this is
- * (2 * 5) seconds = 10 seconds.
- *
+ * 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 2
-
-#define HWTYPE_ETHERNET 1
+#define ARP_MAXPENDING 5
+/** ARP states */
enum etharp_state {
ETHARP_STATE_EMPTY = 0,
ETHARP_STATE_PENDING,
ETHARP_STATE_STABLE,
- ETHARP_STATE_STABLE_REREQUESTING
+ ETHARP_STATE_STABLE_REREQUESTING_1,
+ ETHARP_STATE_STABLE_REREQUESTING_2
#if ETHARP_SUPPORT_STATIC_ENTRIES
- ,ETHARP_STATE_STATIC
+ , ETHARP_STATE_STATIC
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
};
@@ -111,11 +97,11 @@ struct etharp_entry {
/** Pointer to a single pending outgoing packet on this ARP entry. */
struct pbuf *q;
#endif /* ARP_QUEUEING */
- ip_addr_t ipaddr;
+ ip4_addr_t ipaddr;
struct netif *netif;
struct eth_addr ethaddr;
+ u16_t ctime;
u8_t state;
- u8_t ctime;
};
static struct etharp_entry arp_table[ARP_TABLE_SIZE];
@@ -133,19 +119,26 @@ static u8_t etharp_cached_entry;
#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);
+#define ETHARP_SET_ADDRHINT(netif, addrhint) do { if (((netif) != NULL) && ((netif)->hints != NULL)) { \
+ (netif)->hints->addr_hint = (addrhint); }} while(0)
#else /* LWIP_NETIF_HWADDRHINT */
-#define ETHARP_SET_HINT(netif, hint) (etharp_cached_entry = (hint))
+#define ETHARP_SET_ADDRHINT(netif, addrhint) (etharp_cached_entry = (addrhint))
#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"
+#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
@@ -178,7 +171,7 @@ 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);
+ 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 */
@@ -192,7 +185,7 @@ etharp_free_entry(int i)
/* 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);
+ ip4_addr_set_zero(&arp_table[i].ipaddr);
arp_table[i].ethaddr = ethzero;
#endif /* LWIP_DEBUG */
}
@@ -200,7 +193,7 @@ etharp_free_entry(int i)
/**
* Clears expired entries in the ARP table.
*
- * This function should be called every ETHARP_TMR_INTERVAL milliseconds (5 seconds),
+ * This function should be called every ARP_TMR_INTERVAL milliseconds (1 second),
* in order to expire entries in the ARP table.
*/
void
@@ -214,65 +207,66 @@ etharp_tmr(void)
u8_t state = arp_table[i].state;
if (state != ETHARP_STATE_EMPTY
#if ETHARP_SUPPORT_STATIC_ENTRIES
- && (state != ETHARP_STATE_STATIC)
+ && (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));
+ 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) {
+ } 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);
}
-#if ARP_QUEUEING
- /* still pending entry? (not expired) */
- if (arp_table[i].state == ETHARP_STATE_PENDING) {
- /* resend an ARP query here? */
- }
-#endif /* ARP_QUEUEING */
}
}
}
/**
* Search the ARP table for a matching or new entry.
- *
+ *
* If an IP address is given, return a pending or stable ARP entry that matches
* the address. If no match is found, create a new entry with this address set,
* but in state ETHARP_EMPTY. The caller must check and possibly change the
* state of the returned entry.
- *
+ *
* If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY.
- *
+ *
* In all cases, attempt to create new entries from an empty entry. If no
* empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle
* old entries. Heuristic choose the least important entry for recycling.
*
* @param ipaddr IP address to find in ARP cache, or to add if not found.
- * @param flags @see definition of ETHARP_FLAG_*
+ * @param 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(ip_addr_t *ipaddr, u8_t flags)
+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, age_pending = 0, age_stable = 0;
+ u8_t i = 0;
/* oldest entry with packets on queue */
s8_t old_queue = ARP_TABLE_SIZE;
/* its age */
- u8_t age_queue = 0;
+ u16_t age_queue = 0, age_pending = 0, age_stable = 0;
+
+ LWIP_UNUSED_ARG(netif);
/**
* a) do a search through the cache, remember candidates
@@ -295,33 +289,37 @@ etharp_find_entry(ip_addr_t *ipaddr, u8_t flags)
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;
+ empty = (s8_t)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);
+ 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 (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;
+ return (s8_t)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;
+ old_queue = (s8_t)i;
age_queue = arp_table[i].ctime;
}
} else
- /* pending without queued packets? */
+ /* pending without queued packets? */
{
if (arp_table[i].ctime >= age_pending) {
- old_pending = i;
+ old_pending = (s8_t)i;
age_pending = arp_table[i].ctime;
}
}
- /* stable entry? */
+ /* 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 */
@@ -330,7 +328,7 @@ etharp_find_entry(ip_addr_t *ipaddr, u8_t flags)
{
/* remember entry with oldest stable entry in oldest, its age in maxtime */
if (arp_table[i].ctime >= age_stable) {
- old_stable = i;
+ old_stable = (s8_t)i;
age_stable = arp_table[i].ctime;
}
}
@@ -338,7 +336,7 @@ etharp_find_entry(ip_addr_t *ipaddr, u8_t flags)
}
}
/* { 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? */
@@ -346,37 +344,37 @@ etharp_find_entry(ip_addr_t *ipaddr, u8_t flags)
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;
+ i = (u8_t)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;
+ i = (u8_t)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? */
+ /* 3) found recyclable pending entry without queued packets? */
} else if (old_pending < ARP_TABLE_SIZE) {
/* recycle oldest pending */
- i = old_pending;
+ i = (u8_t)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? */
+ /* 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;
+ i = (u8_t)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 {
@@ -391,78 +389,56 @@ etharp_find_entry(ip_addr_t *ipaddr, u8_t flags)
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);
+ arp_table[i].state == ETHARP_STATE_EMPTY);
/* IP address given? */
if (ipaddr != NULL) {
/* set IP address */
- ip_addr_copy(arp_table[i].ipaddr, *ipaddr);
+ 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;
}
/**
- * Send an IP packet on the network using netif->linkoutput
- * The ethernet header is filled in before sending.
- *
- * @params netif the lwIP network interface on which to send the packet
- * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header
- * @params src the source MAC address to be copied into the ethernet header
- * @params dst the destination MAC address to be copied into the ethernet header
- * @return ERR_OK if the packet was sent, any other err_t on failure
- */
-static err_t
-etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst)
-{
- struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload;
-
- LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
- (netif->hwaddr_len == ETHARP_HWADDR_LEN));
- ETHADDR32_COPY(&ethhdr->dest, dst);
- ETHADDR16_COPY(&ethhdr->src, src);
- ethhdr->type = PP_HTONS(ETHTYPE_IP);
- LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_send_ip: sending packet %p\n", (void *)p));
- /* send the packet */
- return netif->linkoutput(netif, p);
-}
-
-/**
* Update (or insert) a IP/MAC address pair in the ARP cache.
*
* If a pending entry is resolved, any queued packets will be sent
* at this point.
- *
+ *
* @param netif netif related to this entry (used for NETIF_ADDRHINT)
* @param ipaddr IP address of the inserted ARP entry.
* @param ethaddr Ethernet address of the inserted ARP entry.
- * @param flags @see definition of ETHARP_FLAG_*
+ * @param flags See @ref etharp_state
*
* @return
- * - ERR_OK Succesfully updated ARP cache.
+ * - 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, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags)
+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 == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN);
+ 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),
- ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
- ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
+ 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 (ip_addr_isany(ipaddr) ||
- ip_addr_isbroadcast(ipaddr, netif) ||
- ip_addr_ismulticast(ipaddr)) {
+ 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);
+ i = etharp_find_entry(ipaddr, flags, netif);
/* bail out if no entry could be found */
if (i < 0) {
return (err_t)i;
@@ -472,6 +448,9 @@ etharp_update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr
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 */
{
@@ -482,11 +461,11 @@ etharp_update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr
/* record network interface */
arp_table[i].netif = netif;
/* insert in SNMP ARP index tree */
- snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr);
+ 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);
+ SMEMCPY(&arp_table[i].ethaddr, ethaddr, ETH_HWADDR_LEN);
/* reset time stamp */
arp_table[i].ctime = 0;
/* this is where we will send out queued packets! */
@@ -507,7 +486,7 @@ etharp_update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr
arp_table[i].q = NULL;
#endif /* ARP_QUEUEING */
/* send the queued IP packet */
- etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr);
+ ethernet_output(netif, p, (struct eth_addr *)(netif->hwaddr), ethaddr, ETHTYPE_IP);
/* free the queued IP packet */
pbuf_free(p);
}
@@ -521,18 +500,18 @@ etharp_update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr
*
* @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
+ * @return See return values of etharp_add_static_entry
*/
err_t
-etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr)
+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),
- ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
- ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
+ 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 = ip_route(ipaddr);
+ netif = ip4_route(ipaddr);
if (netif == NULL) {
return ERR_RTE;
}
@@ -549,14 +528,14 @@ etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr)
* ERR_ARG: entry wasn't a static entry but a dynamic one
*/
err_t
-etharp_remove_static_entry(ip_addr_t *ipaddr)
+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)));
+ 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);
+ 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;
@@ -577,7 +556,8 @@ etharp_remove_static_entry(ip_addr_t *ipaddr)
*
* @param netif points to a network interface
*/
-void etharp_cleanup_netif(struct netif *netif)
+void
+etharp_cleanup_netif(struct netif *netif)
{
u8_t i;
@@ -601,132 +581,83 @@ void etharp_cleanup_netif(struct netif *netif)
* @return table index if found, -1 otherwise
*/
s8_t
-etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr,
- struct eth_addr **eth_ret, ip_addr_t **ip_ret)
+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);
+ eth_ret != NULL && ip_ret != NULL);
LWIP_UNUSED_ARG(netif);
- i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY);
- if((i >= 0) && (arp_table[i].state >= ETHARP_STATE_STABLE)) {
- *eth_ret = &arp_table[i].ethaddr;
- *ip_ret = &arp_table[i].ipaddr;
- return i;
+ 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.
+ * Possibility to iterate over stable ARP table entries
*
- * @return NULL
- *
- * @see pbuf_free()
+ * @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
*/
-static void
-etharp_ip_input(struct netif *netif, struct pbuf *p)
+u8_t
+etharp_get_entry(u8_t i, ip4_addr_t **ipaddr, struct netif **netif, struct eth_addr **eth_ret)
{
- 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);
+ LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL);
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("eth_ret != NULL", eth_ret != NULL);
- /* source is not on the local network? */
- if (!ip_addr_netcmp(&iphdr_src, &(netif->ip_addr), &(netif->netmask))) {
- /* do nothing */
- return;
+ 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;
}
-
- 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
+ * 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
+ * @param netif The lwIP network interface on which the ARP packet pbuf arrived.
*
* @see pbuf_free()
*/
-static void
-etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
+void
+etharp_input(struct pbuf *p, struct netif *netif)
{
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;
+ ip4_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 */
+ hdr = (struct etharp_hdr *)p->payload;
/* RFC 826 "Packet Reception": */
- if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) ||
- (hdr->hwlen != ETHARP_HWADDR_LEN) ||
- (hdr->protolen != sizeof(ip_addr_t)) ||
+ if ((hdr->hwtype != PP_HTONS(LWIP_IANA_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_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_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);
@@ -736,22 +667,22 @@ etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
#if LWIP_AUTOIP
/* We have to check if a host already has configured our random
- * created link local address and continously check if there is
+ * 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
+ /* Copy struct ip4_addr_wordaligned 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);
+ IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&sipaddr, &hdr->sipaddr);
+ IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(&dipaddr, &hdr->dipaddr);
/* this interface is not configured? */
- if (ip_addr_isany(&netif->ip_addr)) {
+ if (ip4_addr_isany_val(*netif_ip4_addr(netif))) {
for_us = 0;
} else {
/* ARP packet directed to us? */
- for_us = (u8_t)ip_addr_cmp(&dipaddr, &(netif->ip_addr));
+ for_us = (u8_t)ip4_addr_cmp(&dipaddr, netif_ip4_addr(netif));
}
/* ARP message directed to us?
@@ -760,77 +691,50 @@ etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
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);
+ for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY);
/* now act on the message itself */
switch (hdr->opcode) {
- /* ARP request? */
- case PP_HTONS(ARP_REQUEST):
- /* ARP request. If it asked for our address, we send out a
- * reply. In any case, we time-stamp any existing ARP entry,
- * and possiby send out an IP packet that was queued on it. */
-
- LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP request\n"));
- /* ARP request for our address? */
- if (for_us) {
-
- LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n"));
- /* Re-use pbuf to send ARP reply.
- Since we are re-using an existing pbuf, we can't call etharp_raw since
- that would allocate a new pbuf. */
- hdr->opcode = htons(ARP_REPLY);
-
- IPADDR2_COPY(&hdr->dipaddr, &hdr->sipaddr);
- IPADDR2_COPY(&hdr->sipaddr, &netif->ip_addr);
-
- LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
- (netif->hwaddr_len == ETHARP_HWADDR_LEN));
-#if LWIP_AUTOIP
- /* If we are using Link-Local, all ARP packets that contain a Link-Local
- * 'sender IP address' MUST be sent using link-layer broadcast instead of
- * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
- ethdst_hwaddr = ip_addr_islinklocal(&netif->ip_addr) ? (u8_t*)(ethbroadcast.addr) : hdr->shwaddr.addr;
-#endif /* LWIP_AUTOIP */
-
- ETHADDR16_COPY(&hdr->dhwaddr, &hdr->shwaddr);
-#if LWIP_AUTOIP
- ETHADDR16_COPY(&ethhdr->dest, ethdst_hwaddr);
-#else /* LWIP_AUTOIP */
- ETHADDR16_COPY(&ethhdr->dest, &hdr->shwaddr);
-#endif /* LWIP_AUTOIP */
- ETHADDR16_COPY(&hdr->shwaddr, ethaddr);
- ETHADDR16_COPY(&ethhdr->src, ethaddr);
-
- /* hwtype, hwaddr_len, proto, protolen and the type in the ethernet header
- are already correct, we tested that before */
-
- /* return ARP reply */
- netif->linkoutput(netif, p);
- /* we are not configured? */
- } else if (ip_addr_isany(&netif->ip_addr)) {
- /* { for_us == 0 and netif->ip_addr.addr == 0 } */
- LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n"));
- /* request was not directed to us */
- } else {
- /* { for_us == 0 and netif->ip_addr.addr != 0 } */
- LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n"));
- }
- break;
- case PP_HTONS(ARP_REPLY):
- /* ARP reply. We already updated the ARP cache earlier. */
- LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n"));
+ /* 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);
+ /* 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;
+ 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);
@@ -847,15 +751,21 @@ etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx)
/* if arp table entry is about to expire: re-request it,
but only if its state is ETHARP_STATE_STABLE to prevent flooding the
network with ARP requests if this address is used frequently. */
- if ((arp_table[arp_idx].state == ETHARP_STATE_STABLE) &&
- (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED)) {
- if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) {
- arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING;
+ 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);
+
+ return ethernet_output(netif, q, (struct eth_addr *)(netif->hwaddr), &arp_table[arp_idx].ethaddr, ETHTYPE_IP);
}
/**
@@ -874,82 +784,83 @@ etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx)
*
* @return
* - ERR_RTE No route to destination (no gateway to external networks),
- * or the return type of either etharp_query() or etharp_send_ip().
+ * or the return type of either etharp_query() or ethernet_output().
*/
err_t
-etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
+etharp_output(struct netif *netif, struct pbuf *q, const ip4_addr_t *ipaddr)
{
- struct eth_addr *dest;
+ const struct eth_addr *dest;
struct eth_addr mcastaddr;
- ip_addr_t *dst_addr = ipaddr;
+ 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);
- /* make room for Ethernet header - should not fail */
- if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
- /* bail out */
- LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
- ("etharp_output: could not allocate room for header.\n"));
- LINK_STATS_INC(link.lenerr);
- return ERR_BUF;
- }
-
/* Determine on destination hardware address. Broadcasts and multicasts
* are special, other IP addresses are looked up in the ARP table. */
/* broadcast destination IP address? */
- if (ip_addr_isbroadcast(ipaddr, netif)) {
+ if (ip4_addr_isbroadcast(ipaddr, netif)) {
/* broadcast on Ethernet also */
- dest = (struct eth_addr *)&ethbroadcast;
- /* multicast destination IP address? */
- } else if (ip_addr_ismulticast(ipaddr)) {
+ 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_MULTICAST_ADDR_0;
- mcastaddr.addr[1] = LL_MULTICAST_ADDR_1;
- mcastaddr.addr[2] = LL_MULTICAST_ADDR_2;
+ 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? */
+ /* unicast destination IP address? */
} else {
- s8_t i;
+ u8_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 (!ip4_addr_netcmp(ipaddr, netif_ip4_addr(netif), netif_ip4_netmask(netif)) &&
+ !ip4_addr_islinklocal(ipaddr)) {
#if LWIP_AUTOIP
- struct ip_hdr *iphdr = (struct ip_hdr*)((u8_t*)q->payload +
- sizeof(struct eth_hdr));
+ 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 (!ip_addr_islinklocal(&iphdr->src))
+ if (!ip4_addr_islinklocal(&iphdr->src))
#endif /* LWIP_AUTOIP */
{
- /* interface has default gateway? */
- if (!ip_addr_isany(&netif->gw)) {
- /* send to hardware address of default gateway IP address */
- dst_addr = &(netif->gw);
- /* no default gateway available */
- } else {
- /* no route to destination error (default gateway missing) */
- return ERR_RTE;
+#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) {
+ if (netif->hints != NULL) {
/* per-pcb cached entry was given */
- u8_t etharp_cached_entry = *(netif->addr_hint);
+ u8_t etharp_cached_entry = netif->hints->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))) {
+#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);
@@ -963,9 +874,12 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
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))) {
+#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);
+ ETHARP_SET_ADDRHINT(netif, i);
return etharp_output_to_arp_index(netif, q, i);
}
}
@@ -977,7 +891,7 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
/* 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);
+ return ethernet_output(netif, q, (struct eth_addr *)(netif->hwaddr), dest, ETHTYPE_IP);
}
/**
@@ -991,11 +905,11 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
* 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.
- *
+ * 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.
@@ -1014,45 +928,51 @@ etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
*
*/
err_t
-etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
+etharp_query(struct netif *netif, const ip4_addr_t *ipaddr, struct pbuf *q)
{
- struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr;
+ struct eth_addr *srcaddr = (struct eth_addr *)netif->hwaddr;
err_t result = ERR_MEM;
- s8_t i; /* ARP entry index */
+ int is_new_entry = 0;
+ s8_t i_err;
+ u8_t i;
/* non-unicast address? */
- if (ip_addr_isbroadcast(ipaddr, netif) ||
- ip_addr_ismulticast(ipaddr) ||
- ip_addr_isany(ipaddr)) {
+ 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);
+ i_err = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD, netif);
/* could not find or create entry? */
- if (i < 0) {
+ if (i_err < 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;
+ return (err_t)i_err;
}
+ i = (u8_t)i_err;
/* 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)));
+ ((arp_table[i].state == ETHARP_STATE_PENDING) ||
+ (arp_table[i].state >= ETHARP_STATE_STABLE)));
- /* do we have a pending entry? or an implicit query request? */
- if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) {
+ /* 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) {
@@ -1071,30 +991,29 @@ etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
/* stable entry? */
if (arp_table[i].state >= ETHARP_STATE_STABLE) {
/* we have a valid IP->Ethernet address mapping */
- ETHARP_SET_HINT(netif, i);
+ ETHARP_SET_ADDRHINT(netif, i);
/* send the packet */
- result = etharp_send_ip(netif, q, srcaddr, &(arp_table[i].ethaddr));
- /* pending entry? (either just created or already pending */
+ 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. */
+ /* IF q includes a pbuf that must be copied, copy the whole chain into a
+ * new PBUF_RAM. See the definition of PBUF_NEEDS_COPY for details. */
p = q;
while (p) {
LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0));
- if(p->type != PBUF_ROM) {
+ if (PBUF_NEEDS_COPY(p)) {
copy_needed = 1;
break;
}
p = p->next;
}
- if(copy_needed) {
+ if (copy_needed) {
/* copy the whole packet into new pbufs */
- p = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
- if(p != NULL) {
+ 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;
@@ -1113,20 +1032,32 @@ etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
/* 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) {
+ 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 {
@@ -1169,75 +1100,65 @@ etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
* 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
+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 ip_addr_t *ipsrc_addr,
- const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_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 eth_hdr *ethhdr;
struct etharp_hdr *hdr;
-#if LWIP_AUTOIP
- const u8_t * ethdst_hwaddr;
-#endif /* LWIP_AUTOIP */
LWIP_ASSERT("netif != NULL", netif != NULL);
/* allocate a pbuf for the outgoing ARP request packet */
- p = pbuf_alloc(PBUF_RAW, SIZEOF_ETHARP_PACKET, PBUF_RAM);
+ 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_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));
+ (p->len >= SIZEOF_ETHARP_HDR));
- ethhdr = (struct eth_hdr *)p->payload;
- hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
+ hdr = (struct etharp_hdr *)p->payload;
LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n"));
- hdr->opcode = htons(opcode);
+ hdr->opcode = lwip_htons(opcode);
- LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
- (netif->hwaddr_len == ETHARP_HWADDR_LEN));
+ 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 */
+ SMEMCPY(&hdr->shwaddr, hwsrc_addr, ETH_HWADDR_LEN);
+ SMEMCPY(&hdr->dhwaddr, hwdst_addr, ETH_HWADDR_LEN);
+ /* Copy struct ip4_addr_wordaligned to aligned ip4_addr, to support compilers without
+ * structure packing. */
+ IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T(&hdr->sipaddr, ipsrc_addr);
+ IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T(&hdr->dipaddr, ipdst_addr);
+
+ hdr->hwtype = PP_HTONS(LWIP_IANA_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) */
- ethdst_hwaddr = ip_addr_islinklocal(ipsrc_addr) ? (u8_t*)(ethbroadcast.addr) : ethdst_addr->addr;
-#endif /* LWIP_AUTOIP */
- /* Write the ARP MAC-Addresses */
- ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr);
- ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr);
- /* Write the Ethernet MAC-Addresses */
-#if LWIP_AUTOIP
- ETHADDR16_COPY(&ethhdr->dest, ethdst_hwaddr);
-#else /* LWIP_AUTOIP */
- ETHADDR16_COPY(&ethhdr->dest, ethdst_addr);
+ if (ip4_addr_islinklocal(ipsrc_addr)) {
+ ethernet_output(netif, p, ethsrc_addr, &ethbroadcast, ETHTYPE_ARP);
+ } else
#endif /* LWIP_AUTOIP */
- ETHADDR16_COPY(&ethhdr->src, ethsrc_addr);
- /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
- * structure packing. */
- IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr);
- IPADDR2_COPY(&hdr->dipaddr, ipdst_addr);
-
- hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET);
- hdr->proto = PP_HTONS(ETHTYPE_IP);
- /* set hwlen and protolen */
- hdr->hwlen = ETHARP_HWADDR_LEN;
- hdr->protolen = sizeof(ip_addr_t);
+ {
+ ethernet_output(netif, p, ethsrc_addr, ethdst_addr, ETHTYPE_ARP);
+ }
- ethhdr->type = PP_HTONS(ETHTYPE_ARP);
- /* send ARP query */
- result = netif->linkoutput(netif, p);
ETHARP_STATS_INC(etharp.xmit);
/* free ARP query packet */
pbuf_free(p);
@@ -1248,166 +1169,40 @@ etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
}
/**
- * Send an ARP request packet asking for ipaddr.
+ * 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
*/
-err_t
-etharp_request(struct netif *netif, ip_addr_t *ipaddr)
+static err_t
+etharp_request_dst(struct netif *netif, const ip4_addr_t *ipaddr, const struct eth_addr *hw_dst_addr)
{
- LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n"));
- return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,
- (struct eth_addr *)netif->hwaddr, &netif->ip_addr, &ethzero,
+ 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);
}
-#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.
+ * Send an ARP request packet asking for ipaddr.
*
- * @param p the recevied packet, p->payload pointing to the ethernet header
- * @param netif the network interface on which the packet was received
+ * @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
-ethernet_input(struct pbuf *p, struct netif *netif)
+etharp_request(struct netif *netif, const ip4_addr_t *ipaddr)
{
- struct eth_hdr* ethhdr;
- u16_t type;
-#if LWIP_ARP || ETHARP_SUPPORT_VLAN
- s16_t ip_hdr_offset = SIZEOF_ETH_HDR;
-#endif /* LWIP_ARP || ETHARP_SUPPORT_VLAN */
-
- if (p->len <= SIZEOF_ETH_HDR) {
- /* a packet with only an ethernet header (or less) is not valid for us */
- ETHARP_STATS_INC(etharp.proterr);
- ETHARP_STATS_INC(etharp.drop);
- goto free_and_return;
- }
-
- /* points to packet payload, which starts with an Ethernet header */
- ethhdr = (struct eth_hdr *)p->payload;
- LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
- ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n",
- (unsigned)ethhdr->dest.addr[0], (unsigned)ethhdr->dest.addr[1], (unsigned)ethhdr->dest.addr[2],
- (unsigned)ethhdr->dest.addr[3], (unsigned)ethhdr->dest.addr[4], (unsigned)ethhdr->dest.addr[5],
- (unsigned)ethhdr->src.addr[0], (unsigned)ethhdr->src.addr[1], (unsigned)ethhdr->src.addr[2],
- (unsigned)ethhdr->src.addr[3], (unsigned)ethhdr->src.addr[4], (unsigned)ethhdr->src.addr[5],
- (unsigned)htons(ethhdr->type)));
-
- type = ethhdr->type;
-#if ETHARP_SUPPORT_VLAN
- if (type == PP_HTONS(ETHTYPE_VLAN)) {
- struct eth_vlan_hdr *vlan = (struct eth_vlan_hdr*)(((char*)ethhdr) + SIZEOF_ETH_HDR);
- if (p->len <= SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR) {
- /* a packet with only an ethernet/vlan header (or less) is not valid for us */
- ETHARP_STATS_INC(etharp.proterr);
- ETHARP_STATS_INC(etharp.drop);
- goto free_and_return;
- }
-#if defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) /* if not, allow all VLANs */
-#ifdef ETHARP_VLAN_CHECK_FN
- if (!ETHARP_VLAN_CHECK_FN(ethhdr, vlan)) {
-#elif defined(ETHARP_VLAN_CHECK)
- if (VLAN_ID(vlan) != ETHARP_VLAN_CHECK) {
-#endif
- /* silently ignore this packet: not for our VLAN */
- pbuf_free(p);
- return ERR_OK;
- }
-#endif /* defined(ETHARP_VLAN_CHECK) || defined(ETHARP_VLAN_CHECK_FN) */
- type = vlan->tpid;
- ip_hdr_offset = SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR;
- }
-#endif /* ETHARP_SUPPORT_VLAN */
-
-#if LWIP_ARP_FILTER_NETIF
- netif = LWIP_ARP_FILTER_NETIF_FN(p, netif, htons(type));
-#endif /* LWIP_ARP_FILTER_NETIF*/
-
- if (ethhdr->dest.addr[0] & 1) {
- /* this might be a multicast or broadcast packet */
- if (ethhdr->dest.addr[0] == LL_MULTICAST_ADDR_0) {
- if ((ethhdr->dest.addr[1] == LL_MULTICAST_ADDR_1) &&
- (ethhdr->dest.addr[2] == LL_MULTICAST_ADDR_2)) {
- /* mark the pbuf as link-layer multicast */
- p->flags |= PBUF_FLAG_LLMCAST;
- }
- } else if (eth_addr_cmp(&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(pbuf_header(p, -ip_hdr_offset)) {
- LWIP_ASSERT("Can't move over header in packet", 0);
- goto free_and_return;
- } else {
- /* pass to IP layer */
- ip_input(p, netif);
- }
- break;
-
- case PP_HTONS(ETHTYPE_ARP):
- if (!(netif->flags & NETIF_FLAG_ETHARP)) {
- goto free_and_return;
- }
- /* pass p to ARP module */
- etharp_arp_input(netif, (struct eth_addr*)(netif->hwaddr), p);
- break;
-#endif /* LWIP_ARP */
-#if PPPOE_SUPPORT
- case PP_HTONS(ETHTYPE_PPPOEDISC): /* PPP Over Ethernet Discovery Stage */
- pppoe_disc_input(netif, p);
- break;
-
- case PP_HTONS(ETHTYPE_PPPOE): /* PPP Over Ethernet Session Stage */
- pppoe_data_input(netif, p);
- break;
-#endif /* PPPOE_SUPPORT */
-
-#if LWIP_IPV6
- case PP_HTONS(ETHTYPE_IPV6): /* IPv6 */
- /* skip Ethernet header */
- if(pbuf_header(p, -(s16_t)SIZEOF_ETH_HDR)) {
- LWIP_ASSERT("Can't move over header in packet", 0);
- goto free_and_return;
- } else {
- /* pass to IPv6 layer */
- ip6_input(p, netif);
- }
- break;
-#endif /* LWIP_IPV6 */
-
- default:
- ETHARP_STATS_INC(etharp.proterr);
- ETHARP_STATS_INC(etharp.drop);
- goto free_and_return;
- }
-
- /* This means the pbuf is freed or consumed,
- so the caller doesn't have to free it again */
- return ERR_OK;
-
-free_and_return:
- pbuf_free(p);
- return ERR_OK;
+ 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/lwip/src/core/ipv4/icmp.c b/lwip/src/core/ipv4/icmp.c
index af47153..3c9445d 100644
--- a/lwip/src/core/ipv4/icmp.c
+++ b/lwip/src/core/ipv4/icmp.c
@@ -41,17 +41,20 @@
#include "lwip/opt.h"
-#if LWIP_ICMP /* don't build if not configured for use in lwipopts.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 "lwip/snmp.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) */
@@ -81,170 +84,215 @@ icmp_input(struct pbuf *p, struct netif *inp)
u8_t code;
#endif /* LWIP_DEBUG */
struct icmp_echo_hdr *iecho;
- struct ip_hdr *iphdr;
- s16_t hlen;
+ const struct ip_hdr *iphdr_in;
+ u16_t hlen;
+ const ip4_addr_t *src;
ICMP_STATS_INC(icmp.recv);
- snmp_inc_icmpinmsgs();
+ MIB2_STATS_INC(mib2.icmpinmsgs);
- iphdr = (struct ip_hdr *)ip_current_header();
- hlen = IPH_HL(iphdr) * 4;
- if (p->len < sizeof(u16_t)*2) {
+ iphdr_in = ip4_current_header();
+ hlen = IPH_HL_BYTES(iphdr_in);
+ 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);
+ code = *(((u8_t *)p->payload) + 1);
+ /* if debug is enabled but debug statement below is somehow disabled: */
+ LWIP_UNUSED_ARG(code);
#endif /* LWIP_DEBUG */
switch (type) {
- case ICMP_ER:
- /* This is OK, echo reply might have been parsed by a raw PCB
- (as obviously, an echo request has been sent, too). */
- break;
- case ICMP_ECHO:
-#if !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING
- {
- int accepted = 1;
-#if !LWIP_MULTICAST_PING
+ 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 (ip_addr_ismulticast(ip_current_dest_addr())) {
- accepted = 0;
- }
+ 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 */
-#if !LWIP_BROADCAST_PING
- /* broadcast destination address? */
- if (ip_addr_isbroadcast(ip_current_dest_addr(), inp)) {
- accepted = 0;
}
+ /* 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 */
- /* broadcast or multicast destination address not acceptd? */
- if (!accepted) {
- LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n"));
- ICMP_STATS_INC(icmp.err);
- pbuf_free(p);
- return;
}
- }
-#endif /* !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */
- LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));
- if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
- LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));
- goto lenerr;
- }
- if (inet_chksum_pbuf(p) != 0) {
- LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: checksum failed for received ICMP echo\n"));
- pbuf_free(p);
- ICMP_STATS_INC(icmp.chkerr);
- snmp_inc_icmpinerrors();
- return;
- }
-#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
- if (pbuf_header(p, (PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
- /* p is not big enough to contain link headers
- * allocate a new one and copy p into it
- */
- struct pbuf *r;
- /* switch p->payload to ip header */
- if (pbuf_header(p, hlen)) {
- LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0);
- goto memerr;
+ 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;
}
- /* allocate new packet buffer with space for link headers */
- r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
- if (r == NULL) {
- LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n"));
- goto memerr;
+#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;
+ }
}
- LWIP_ASSERT("check that first pbuf can hold struct the ICMP header",
- (r->len >= hlen + sizeof(struct icmp_echo_hdr)));
- /* copy the whole packet including ip header */
- if (pbuf_copy(r, p) != ERR_OK) {
- LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0);
- goto memerr;
- }
- iphdr = (struct ip_hdr *)r->payload;
- /* switch r->payload back to icmp header */
- if (pbuf_header(r, -hlen)) {
- LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
- goto memerr;
- }
- /* free the original p */
- pbuf_free(p);
- /* we now have an identical copy of p that has room for link headers */
- p = r;
- } else {
- /* restore p->payload to point to icmp header */
- if (pbuf_header(p, -(s16_t)(PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
- LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
- goto memerr;
+#endif
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+ if (pbuf_add_header(p, 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;
+ u16_t alloc_len = (u16_t)(p->tot_len + hlen);
+ if (alloc_len < p->tot_len) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed (tot_len overflow)\n"));
+ goto icmperr;
+ }
+ /* allocate new packet buffer with space for link headers */
+ r = pbuf_alloc(PBUF_LINK, alloc_len, 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_remove_header(r, 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_remove_header(p, 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;
- ip_addr_copy(iphdr->src, *ip_current_dest_addr());
- ip_addr_copy(iphdr->dest, *ip_current_src_addr());
- ICMPH_TYPE_SET(iecho, ICMP_ER);
+ /* 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_add_header(p, 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
- /* 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__NETIF_CHECKSUM_ENABLED(inp, NETIF_CHECKSUM_GEN_ICMP) {
+ /* adjust the checksum */
+ if (iecho->chksum > PP_HTONS(0xffffU - (ICMP_ECHO << 8))) {
+ iecho->chksum = (u16_t)(iecho->chksum + PP_HTONS((u16_t)(ICMP_ECHO << 8)) + 1);
+ } else {
+ iecho->chksum = (u16_t)(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;
+ 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);
+ /* Set the correct TTL and recalculate the header checksum. */
+ IPH_TTL_SET(iphdr, ICMP_TTL);
+ IPH_CHKSUM_SET(iphdr, 0);
#if CHECKSUM_GEN_IP
- IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+ 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 */
- snmp_inc_icmpoutmsgs();
- /* increase number of echo replies attempted to send */
- snmp_inc_icmpoutechoreps();
+ 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);
- if(pbuf_header(p, hlen)) {
- LWIP_ASSERT("Can't move over header in packet", 0);
- } else {
- err_t ret;
- /* send an ICMP packet, src addr is the dest addr of the curren packet */
- ret = ip_output_if(p, ip_current_dest_addr(), IP_HDRINCL,
- ICMP_TTL, 0, IP_PROTO_ICMP, inp);
- if (ret != ERR_OK) {
- LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %c.\n", ret));
+ /* 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:
- 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);
+ 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);
- snmp_inc_icmpinerrors();
+ MIB2_STATS_INC(mib2.icmpinerrors);
return;
-#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
-memerr:
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING
+icmperr:
pbuf_free(p);
ICMP_STATS_INC(icmp.err);
- snmp_inc_icmpinerrors();
+ MIB2_STATS_INC(mib2.icmpinerrors);
return;
-#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN || !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */
}
/**
@@ -259,6 +307,7 @@ memerr:
void
icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
{
+ MIB2_STATS_INC(mib2.icmpoutdestunreachs);
icmp_send_response(p, ICMP_DUR, t);
}
@@ -273,6 +322,7 @@ icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
void
icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
{
+ MIB2_STATS_INC(mib2.icmpouttimeexcds);
icmp_send_response(p, ICMP_TE, t);
}
@@ -293,23 +343,28 @@ icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
struct ip_hdr *iphdr;
/* we can use the echo header here */
struct icmp_echo_hdr *icmphdr;
- ip_addr_t iphdr_src;
+ 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)));
+ (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));
iphdr = (struct ip_hdr *)p->payload;
LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));
- ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src));
+ ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->src);
LWIP_DEBUGF(ICMP_DEBUG, (" to "));
- ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest));
+ ip4_addr_debug_print_val(ICMP_DEBUG, iphdr->dest);
LWIP_DEBUGF(ICMP_DEBUG, ("\n"));
icmphdr = (struct icmp_echo_hdr *)q->payload;
@@ -322,17 +377,28 @@ icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload,
IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
- /* calculate checksum */
- icmphdr->chksum = 0;
- icmphdr->chksum = inet_chksum(icmphdr, q->len);
- ICMP_STATS_INC(icmp.xmit);
- /* increase number of messages attempted to send */
- snmp_inc_icmpoutmsgs();
- /* increase number of destination unreachable messages attempted to send */
- snmp_inc_icmpouttimeexcds();
- ip_addr_copy(iphdr_src, iphdr->src);
- ip_output(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP);
+ 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_ICMP */
+#endif /* LWIP_IPV4 && LWIP_ICMP */
diff --git a/lwip/src/core/ipv4/igmp.c b/lwip/src/core/ipv4/igmp.c
index bd52744..c3b79a7 100644
--- a/lwip/src/core/ipv4/igmp.c
+++ b/lwip/src/core/ipv4/igmp.c
@@ -2,35 +2,38 @@
* @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.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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.
+ * AND ANY EXPRESS OR 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
@@ -70,7 +73,7 @@ Steve Reynolds
* 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 -
+ * RFC 2113 - IP Router Alert Option -
*----------------------------------------------------------------------------*/
/*-----------------------------------------------------------------------------
@@ -79,7 +82,7 @@ Steve Reynolds
#include "lwip/opt.h"
-#if LWIP_IGMP /* don't build if not configured for use in lwipopts.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"
@@ -88,66 +91,21 @@ Steve Reynolds
#include "lwip/ip.h"
#include "lwip/inet_chksum.h"
#include "lwip/netif.h"
-#include "lwip/icmp.h"
-#include "lwip/udp.h"
-#include "lwip/tcp.h"
#include "lwip/stats.h"
+#include "lwip/prot/igmp.h"
#include "string.h"
-/*
- * IGMP constants
- */
-#define IGMP_TTL 1
-#define IGMP_MINLEN 8
-#define ROUTER_ALERT 0x9404U
-#define ROUTER_ALERTLEN 4
-
-/*
- * IGMP message types, including version number.
- */
-#define IGMP_MEMB_QUERY 0x11 /* Membership query */
-#define IGMP_V1_MEMB_REPORT 0x12 /* Ver. 1 membership report */
-#define IGMP_V2_MEMB_REPORT 0x16 /* Ver. 2 membership report */
-#define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */
-
-/* Group membership states */
-#define IGMP_GROUP_NON_MEMBER 0
-#define IGMP_GROUP_DELAYING_MEMBER 1
-#define IGMP_GROUP_IDLE_MEMBER 2
-
-/**
- * IGMP packet format.
- */
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct igmp_msg {
- PACK_STRUCT_FIELD(u8_t igmp_msgtype);
- PACK_STRUCT_FIELD(u8_t igmp_maxresp);
- PACK_STRUCT_FIELD(u16_t igmp_checksum);
- PACK_STRUCT_FIELD(ip_addr_p_t igmp_group_address);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
-
-
-static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr);
-static err_t igmp_remove_group(struct igmp_group *group);
-static void igmp_timeout( struct igmp_group *group);
+static 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, ip_addr_t *src, ip_addr_t *dest, struct netif *netif);
-static void igmp_send(struct igmp_group *group, u8_t type);
-
-
-static struct igmp_group* igmp_group_list;
-static ip_addr_t allsystems;
-static ip_addr_t allrouters;
+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
@@ -161,27 +119,6 @@ igmp_init(void)
IP4_ADDR(&allrouters, 224, 0, 0, 2);
}
-#ifdef LWIP_DEBUG
-/**
- * Dump global IGMP groups list
- */
-void
-igmp_dump_group_list()
-{
- struct igmp_group *group = igmp_group_list;
-
- while (group != NULL) {
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state)));
- ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
- LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
- group = group->next;
- }
- LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
-}
-#else
-#define igmp_dump_group_list()
-#endif /* LWIP_DEBUG */
-
/**
* Start IGMP processing on interface
*
@@ -190,9 +127,9 @@ igmp_dump_group_list()
err_t
igmp_start(struct netif *netif)
{
- struct igmp_group* group;
+ struct igmp_group *group;
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif));
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", (void *)netif));
group = igmp_lookup_group(netif, &allsystems);
@@ -203,9 +140,9 @@ igmp_start(struct netif *netif)
/* Allow the igmp messages at the MAC level */
if (netif->igmp_mac_filter != NULL) {
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
- ip_addr_debug_print(IGMP_DEBUG, &allsystems);
- LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
- netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER);
+ 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;
@@ -222,36 +159,24 @@ igmp_start(struct netif *netif)
err_t
igmp_stop(struct netif *netif)
{
- struct igmp_group *group = igmp_group_list;
- struct igmp_group *prev = NULL;
- struct igmp_group *next;
+ struct igmp_group *group = netif_igmp_data(netif);
+
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_IGMP, NULL);
- /* look for groups joined on this interface further down the list */
while (group != NULL) {
- next = group->next;
- /* is it a group joined on this interface? */
- if (group->netif == netif) {
- /* is it the first group of the list? */
- if (group == igmp_group_list) {
- igmp_group_list = next;
- }
- /* is there a "previous" group defined? */
- if (prev != NULL) {
- prev->next = next;
- }
- /* disable the group at the MAC level */
- if (netif->igmp_mac_filter != NULL) {
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
- ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
- LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
- netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER);
- }
- /* free group */
- memp_free(MEMP_IGMP_GROUP, group);
- } else {
- /* change the "previous" */
- prev = group;
+ 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;
}
@@ -266,20 +191,23 @@ igmp_stop(struct netif *netif)
void
igmp_report_groups(struct netif *netif)
{
- struct igmp_group *group = igmp_group_list;
+ struct igmp_group *group = netif_igmp_data(netif);
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", 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) {
- if (group->netif == netif) {
- igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
- }
+ igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
group = group->next;
}
}
/**
- * Search for a group in the global igmp_group_list
+ * Search for a group in the netif's igmp group list
*
* @param ifp the network interface for which to look
* @param addr the group ip address to search for
@@ -287,12 +215,12 @@ igmp_report_groups(struct netif *netif)
* NULL if the group wasn't found.
*/
struct igmp_group *
-igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr)
+igmp_lookfor_group(struct netif *ifp, const ip4_addr_t *addr)
{
- struct igmp_group *group = igmp_group_list;
+ struct igmp_group *group = netif_igmp_data(ifp);
while (group != NULL) {
- if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) {
+ if (ip4_addr_cmp(&(group->group_address), addr)) {
return group;
}
group = group->next;
@@ -312,11 +240,12 @@ igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr)
* @return a struct igmp_group*,
* NULL on memory error.
*/
-struct igmp_group *
-igmp_lookup_group(struct netif *ifp, ip_addr_t *addr)
+static struct igmp_group *
+igmp_lookup_group(struct netif *ifp, const ip4_addr_t *addr)
{
- struct igmp_group *group = igmp_group_list;
-
+ 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) {
@@ -327,53 +256,58 @@ igmp_lookup_group(struct netif *ifp, ip_addr_t *addr)
/* Group doesn't exist yet, create a new one */
group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
if (group != NULL) {
- group->netif = ifp;
- ip_addr_set(&(group->group_address), addr);
+ 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;
- group->next = igmp_group_list;
-
- igmp_group_list = group;
+
+ /* 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 ")));
- ip_addr_debug_print(IGMP_DEBUG, addr);
- LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp));
+ 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
+ * Remove a group from netif's igmp group list, but don't free it yet
*
- * @param group the group to remove from the global igmp_group_list
+ * @param group the group to remove from the netif's igmp group list
* @return ERR_OK if group was removed from the list, an err_t otherwise
*/
static err_t
-igmp_remove_group(struct igmp_group *group)
+igmp_remove_group(struct netif *netif, struct igmp_group *group)
{
err_t err = ERR_OK;
+ struct igmp_group *tmp_group;
- /* Is it the first group? */
- if (igmp_group_list == group) {
- igmp_group_list = group->next;
- } else {
- /* look for group further down the list */
- struct igmp_group *tmpGroup;
- for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
- if (tmpGroup->next == group) {
- tmpGroup->next = group->next;
- break;
- }
+ /* 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 (tmpGroup == NULL)
- err = ERR_ARG;
}
- /* free group */
- memp_free(MEMP_IGMP_GROUP, group);
+ /* Group not found in netif's igmp group list */
+ if (tmp_group == NULL) {
+ err = ERR_ARG;
+ }
return err;
}
@@ -386,15 +320,15 @@ igmp_remove_group(struct igmp_group *group)
* @param dest destination ip address of the igmp packet
*/
void
-igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
+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;
+ 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 */
+ /* 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);
@@ -403,10 +337,10 @@ igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
}
LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
- ip_addr_debug_print(IGMP_DEBUG, &(ip_current_header()->src));
+ ip4_addr_debug_print(IGMP_DEBUG, &(ip4_current_header()->src));
LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
- ip_addr_debug_print(IGMP_DEBUG, &(ip_current_header()->dest));
- LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp));
+ 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;
@@ -419,7 +353,7 @@ igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
/* 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);
@@ -430,72 +364,73 @@ igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
/* NOW ACT ON THE INCOMING MESSAGE TYPE... */
switch (igmp->igmp_msgtype) {
- case IGMP_MEMB_QUERY: {
- /* IGMP_MEMB_QUERY to the "all systems" address ? */
- if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) {
- /* THIS IS THE GENERAL QUERY */
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
-
- if (igmp->igmp_maxresp == 0) {
- IGMP_STATS_INC(igmp.rx_v1);
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
- igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
- } else {
- IGMP_STATS_INC(igmp.rx_general);
- }
-
- groupref = igmp_group_list;
- while (groupref) {
- /* Do not send messages on the all systems group address! */
- if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) {
- igmp_delaying_member(groupref, igmp->igmp_maxresp);
- }
- groupref = groupref->next;
- }
- } else {
- /* IGMP_MEMB_QUERY to a specific group ? */
- if (!ip_addr_isany(&igmp->igmp_group_address)) {
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
- ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
- if (ip_addr_cmp(dest, &allsystems)) {
- ip_addr_t groupaddr;
- LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
- /* we first need to re-look for the group since we used dest last time */
- ip_addr_copy(groupaddr, igmp->igmp_group_address);
- group = igmp_lookfor_group(inp, &groupaddr);
- } else {
- LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
- }
-
- if (group != NULL) {
- IGMP_STATS_INC(igmp.rx_group);
- igmp_delaying_member(group, igmp->igmp_maxresp);
- } else {
- IGMP_STATS_INC(igmp.drop);
- }
- } else {
- IGMP_STATS_INC(igmp.proterr);
- }
- }
- break;
- }
- case IGMP_V2_MEMB_REPORT: {
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
- IGMP_STATS_INC(igmp.rx_report);
- if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
- /* This is on a specific group we have already looked up */
- group->timer = 0; /* stopped */
- group->group_state = IGMP_GROUP_IDLE_MEMBER;
- group->last_reporter_flag = 0;
- }
- break;
- }
- default: {
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
- igmp->igmp_msgtype, group->group_state, &group, group->netif));
- IGMP_STATS_INC(igmp.proterr);
- break;
- }
+ 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);
@@ -503,6 +438,7 @@ igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
}
/**
+ * @ingroup igmp
* Join a group on one network interface.
*
* @param ifaddr ip address of the network interface which should join a new group
@@ -510,69 +446,92 @@ igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
* @return ERR_OK if group was joined on the netif(s), an err_t otherwise
*/
err_t
-igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
+igmp_joingroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
{
- err_t err = ERR_VAL; /* no matching interface */
- struct igmp_group *group;
- struct netif *netif;
+ 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", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
- LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+ 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) {
+ NETIF_FOREACH(netif) {
/* Should we join this interface ? */
- if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
- /* find group or create a new one if not found */
- group = igmp_lookup_group(netif, groupaddr);
-
- if (group != NULL) {
- /* This should create a new group, check the state to make sure */
- if (group->group_state != IGMP_GROUP_NON_MEMBER) {
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
- } else {
- /* OK - it was new group */
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: "));
- ip_addr_debug_print(IGMP_DEBUG, groupaddr);
- LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
-
- /* If first use of the group, allow the group at the MAC level */
- if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD "));
- ip_addr_debug_print(IGMP_DEBUG, groupaddr);
- LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
- netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER);
- }
-
- IGMP_STATS_INC(igmp.tx_join);
- igmp_send(group, IGMP_V2_MEMB_REPORT);
-
- igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
-
- /* Need to work out where this timer comes from */
- group->group_state = IGMP_GROUP_DELAYING_MEMBER;
- }
- /* Increment group use */
- group->use++;
- /* Join on this interface */
- err = ERR_OK;
- } else {
+ 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 */
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n"));
- return ERR_MEM;
+ 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
@@ -580,88 +539,113 @@ igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
* @return ERR_OK if group was left on the netif(s), an err_t otherwise
*/
err_t
-igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
+igmp_leavegroup(const ip4_addr_t *ifaddr, const ip4_addr_t *groupaddr)
{
- err_t err = ERR_VAL; /* no matching interface */
- struct igmp_group *group;
- struct netif *netif;
+ 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", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
- LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+ 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) {
+ NETIF_FOREACH(netif) {
/* Should we leave this interface ? */
- if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
- /* find group */
- group = igmp_lookfor_group(netif, groupaddr);
-
- if (group != NULL) {
- /* Only send a leave if the flag is set according to the state diagram */
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: "));
- ip_addr_debug_print(IGMP_DEBUG, groupaddr);
- LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
-
- /* If there is no other use of the group */
- if (group->use <= 1) {
- /* If we are the last reporter for this group */
- if (group->last_reporter_flag) {
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n"));
- IGMP_STATS_INC(igmp.tx_leave);
- igmp_send(group, IGMP_LEAVE_GROUP);
- }
-
- /* Disable the group at the MAC level */
- if (netif->igmp_mac_filter != NULL) {
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL "));
- ip_addr_debug_print(IGMP_DEBUG, groupaddr);
- LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
- netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER);
- }
-
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: "));
- ip_addr_debug_print(IGMP_DEBUG, groupaddr);
- LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
-
- /* Free the group */
- igmp_remove_group(group);
- } else {
- /* Decrement group use */
- group->use--;
- }
- /* Leave on this interface */
- err = ERR_OK;
- } else {
- /* It's not a fatal error on "leavegroup" */
- LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n"));
+ 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 igmp_group *group = igmp_group_list;
+ struct netif *netif;
- while (group != NULL) {
- if (group->timer > 0) {
- group->timer--;
- if (group->timer == 0) {
- igmp_timeout(group);
+ NETIF_FOREACH(netif) {
+ 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;
}
- group = group->next;
}
}
@@ -672,16 +656,20 @@ igmp_tmr(void)
* @param group an igmp_group for which a timeout is reached
*/
static void
-igmp_timeout(struct igmp_group *group)
+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 */
- if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
+ /* 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 "));
- ip_addr_debug_print(IGMP_DEBUG, &(group->group_address));
- LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
+ 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(group, IGMP_V2_MEMB_REPORT);
+ igmp_send(netif, group, IGMP_V2_MEMB_REPORT);
}
}
@@ -695,14 +683,16 @@ igmp_timeout(struct igmp_group *group)
static void
igmp_start_timer(struct igmp_group *group, u8_t max_time)
{
- /* ensure the input value is > 0 */
- if (max_time == 0) {
- max_time = 1;
- }
#ifdef LWIP_RAND
- /* ensure the random value is > 0 */
- group->timer = (LWIP_RAND() % (max_time - 1)) + 1;
+ group->timer = (u16_t)(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;
+ }
}
/**
@@ -715,8 +705,8 @@ 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)))) {
+ ((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;
}
@@ -729,27 +719,25 @@ igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
* the IP address of the outgoing network interface is filled in as source address.
*
* @param p the packet to send (p->payload points to the data, e.g. next
- protocol header; if dest == IP_HDRINCL, p already includes an IP
- header and p->payload points to that IP header)
- * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ 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 proto the PROTOCOL to be set in the IP header
* @param netif the netif on which to send this packet
* @return ERR_OK if the packet was sent OK
* ERR_BUF if p doesn't have enough space for IP/LINK headers
* returns errors returned by netif->output
*/
static err_t
-igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif)
+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 ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
+ return ip4_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
}
/**
@@ -759,30 +747,30 @@ igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif
* @param type the type of igmp packet to send
*/
static void
-igmp_send(struct igmp_group *group, u8_t type)
+igmp_send(struct netif *netif, struct igmp_group *group, u8_t type)
{
- struct pbuf* p = NULL;
- struct igmp_msg* igmp = NULL;
- ip_addr_t src = *IP_ADDR_ANY;
- ip_addr_t* dest = NULL;
+ 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)));
- ip_addr_copy(src, group->netif->ip_addr);
-
+ (p->len >= sizeof(struct igmp_msg)));
+ ip4_addr_copy(src, *netif_ip4_addr(netif));
+
if (type == IGMP_V2_MEMB_REPORT) {
dest = &(group->group_address);
- ip_addr_copy(igmp->igmp_group_address, 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;
- ip_addr_copy(igmp->igmp_group_address, group->group_address);
+ ip4_addr_copy(igmp->igmp_group_address, group->group_address);
}
}
@@ -792,7 +780,7 @@ igmp_send(struct igmp_group *group, u8_t type)
igmp->igmp_checksum = 0;
igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
- igmp_ip_output_if(p, &src, dest, group->netif);
+ igmp_ip_output_if(p, &src, dest, netif);
}
pbuf_free(p);
@@ -802,4 +790,4 @@ igmp_send(struct igmp_group *group, u8_t type)
}
}
-#endif /* LWIP_IGMP */
+#endif /* LWIP_IPV4 && LWIP_IGMP */
diff --git a/lwip/src/core/ipv4/ip4.c b/lwip/src/core/ipv4/ip4.c
index 1acc255..2835fae 100644
--- a/lwip/src/core/ipv4/ip4.c
+++ b/lwip/src/core/ipv4/ip4.c
@@ -1,7 +1,7 @@
/**
* @file
* This is the IPv4 layer implementation for incoming and outgoing IP traffic.
- *
+ *
* @see ip_frag.c
*
*/
@@ -39,30 +39,40 @@
*/
#include "lwip/opt.h"
+
+#if LWIP_IPV4
+
#include "lwip/ip.h"
#include "lwip/def.h"
#include "lwip/mem.h"
-#include "lwip/ip_frag.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/tcp_impl.h"
-#include "lwip/snmp.h"
-#include "lwip/dhcp.h"
+#include "lwip/priv/tcp_priv.h"
#include "lwip/autoip.h"
#include "lwip/stats.h"
-#include "arch/perf.h"
+#include "lwip/prot/iana.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
@@ -74,31 +84,61 @@
/** Some defines for DHCP to let link-layer-addressed packets through while the
* netif is down.
- * To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT
+ * To 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)) \
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (((port) == PP_NTOHS(LWIP_IANA_PORT_DHCP_CLIENT)) \
|| (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))
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) ((port) == PP_NTOHS(LWIP_IANA_PORT_DHCP_CLIENT))
#endif /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
#else /* LWIP_DHCP */
#define IP_ACCEPT_LINK_LAYER_ADDRESSING 0
#endif /* LWIP_DHCP */
-/** Global data for both IPv4 and IPv6 */
-struct ip_globals ip_data;
-
/** The IP header ID of the next outgoing IP packet */
static u16_t ip_id;
+#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 the parameters.
+ */
+struct netif *
+ip4_route_src(const ip4_addr_t *src, const ip4_addr_t *dest)
+{
+ if (src != NULL) {
+ /* when src==NULL, the hook is called from ip4_route(dest) */
+ struct netif *netif = LWIP_HOOK_IP4_ROUTE_SRC(src, dest);
+ 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
@@ -109,35 +149,78 @@ static u16_t ip_id;
* @return the netif on which to send to reach dest
*/
struct netif *
-ip_route(ip_addr_t *dest)
+ip4_route(const ip4_addr_t *dest)
{
+#if !LWIP_SINGLE_NETIF
struct netif *netif;
-#ifdef LWIP_HOOK_IP4_ROUTE
- netif = LWIP_HOOK_IP4_ROUTE(dest);
- if (netif != NULL) {
- return 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
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
/* iterate through netifs */
for (netif = netif_list; netif != NULL; netif = netif->next) {
- /* network mask matches? */
- if (netif_is_up(netif)) {
- if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) {
+ /* 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 ((netif_default == NULL) || (!netif_is_up(netif_default))) {
- LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
- ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+
+#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(NULL, dest);
+ if (netif != NULL) {
+ return netif;
+ }
+#elif defined(LWIP_HOOK_IP4_ROUTE)
+ netif = LWIP_HOOK_IP4_ROUTE(dest);
+ if (netif != NULL) {
+ return netif;
+ }
+#endif
+#endif /* !LWIP_SINGLE_NETIF */
+
+ if ((netif_default == NULL) || !netif_is_up(netif_default) || !netif_is_link_up(netif_default)
+ /* Yes we are binding to any addr */
+ // || 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);
- snmp_inc_ipoutnoroutes();
+ MIB2_STATS_INC(mib2.ipoutnoroutes);
return NULL;
}
- /* no matching netif found, use default netif */
+
return netif_default;
}
@@ -147,13 +230,12 @@ ip_route(ip_addr_t *dest)
* that may not be forwarded, or whether datagrams to that destination
* may be forwarded.
* @param p the packet to forward
- * @param dest the destination IP address
* @return 1: can forward 0: discard
*/
static int
-ip_canforward(struct pbuf *p)
+ip4_canforward(struct pbuf *p)
{
- u32_t addr = htonl(ip4_addr_get_u32(ip_current_dest_addr()));
+ 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 */
@@ -187,30 +269,31 @@ ip_canforward(struct pbuf *p)
* @param inp the netif on which this packet was received
*/
static void
-ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
+ip4_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
{
struct netif *netif;
PERF_START;
+ LWIP_UNUSED_ARG(inp);
- if (!ip_canforward(p)) {
+ if (!ip4_canforward(p)) {
goto return_noroute;
}
/* RFC3927 2.7: do not forward link-local addresses */
- if (ip_addr_islinklocal(ip_current_dest_addr())) {
- LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
- ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()),
- ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr())));
+ 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 = ip_route(ip_current_dest_addr());
+ netif = ip4_route_src(ip4_current_src_addr(), ip4_current_dest_addr());
if (netif == NULL) {
- LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n",
- ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()),
- ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr())));
+ 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;
}
@@ -218,7 +301,7 @@ ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
/* Do not forward packets onto the same network interface on which
* they arrived. */
if (netif == inp) {
- LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n"));
+ 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 */
@@ -227,7 +310,7 @@ ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1);
/* send ICMP if TTL == 0 */
if (IPH_TTL(iphdr) == 0) {
- snmp_inc_ipinhdrerrors();
+ 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) {
@@ -239,42 +322,83 @@ ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
/* Incrementally update the IP checksum. */
if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffffU - 0x100)) {
- IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1);
+ IPH_CHKSUM_SET(iphdr, (u16_t)(IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1));
} else {
- IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100));
+ IPH_CHKSUM_SET(iphdr, (u16_t)(IPH_CHKSUM(iphdr) + PP_HTONS(0x100)));
}
- LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
- ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()),
- ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr())));
+ 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);
- snmp_inc_ipforwdatagrams();
- PERF_STOP("ip_forward");
+ 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
- ip_frag(p, netif, ip_current_dest_addr());
+ ip4_frag(p, netif, ip4_current_dest_addr());
#else /* IP_FRAG */
- /* @todo: send ICMP Destination Unreacheable code 13 "Communication administratively prohibited"? */
+ /* @todo: send ICMP Destination Unreachable code 13 "Communication administratively prohibited"? */
#endif /* IP_FRAG */
} else {
- /* send ICMP Destination Unreacheable code 4: "Fragmentation Needed and DF Set" */
+#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, ip_current_dest_addr());
+ netif->output(netif, p, ip4_current_dest_addr());
return;
return_noroute:
- snmp_inc_ipoutnoroutes();
+ MIB2_STATS_INC(mib2.ipoutnoroutes);
}
#endif /* IP_FORWARD */
+/** Return true if the current input packet should be accepted on this netif */
+static int
+ip4_input_accept(struct netif *netif)
+{
+ 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(ip4_current_dest_addr()), ip4_addr_get_u32(netif_ip4_addr(netif)),
+ ip4_addr_get_u32(ip4_current_dest_addr()) & 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(ip4_current_dest_addr()) & ~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]));
+ /* accept on this netif */
+ return 1;
+ }
+#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]));
+ /* accept on this netif */
+ return 1;
+ }
+#endif /* LWIP_AUTOIP */
+ }
+ return 0;
+}
+
/**
* This function is called by the network interface device driver when
* an IP packet is received. The function does the basic checks of the
@@ -283,35 +407,35 @@ return_noroute:
* forwarded (using ip_forward). The IP checksum is always checked.
*
* Finally, the packet is sent to the upper layer protocol input function.
- *
+ *
* @param p the received IP packet (p->payload points to IP header)
* @param inp the netif on which this packet was received
* @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
* processed, but currently always returns ERR_OK)
*/
err_t
-ip_input(struct pbuf *p, struct netif *inp)
+ip4_input(struct pbuf *p, struct netif *inp)
{
- struct ip_hdr *iphdr;
+ const struct ip_hdr *iphdr;
struct netif *netif;
u16_t iphdr_hlen;
u16_t iphdr_len;
-#if IP_ACCEPT_LINK_LAYER_ADDRESSING
- int check_ip_src=1;
-#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+#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);
- snmp_inc_ipinreceives();
+ 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", IPH_V(iphdr)));
- ip_debug_print(p);
+ 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);
- snmp_inc_ipinhdrerrors();
+ MIB2_STATS_INC(mib2.ipinhdrerrors);
return ERR_OK;
}
@@ -322,112 +446,110 @@ ip_input(struct pbuf *p, struct netif *inp)
}
#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 header length in bytes */
+ iphdr_hlen = IPH_HL_BYTES(iphdr);
/* obtain ip length in bytes */
- iphdr_len = ntohs(IPH_LEN(iphdr));
+ 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)) {
+ 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));
+ ("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));
+ ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n",
+ iphdr_len, p->tot_len));
}
/* free (drop) packet pbufs */
pbuf_free(p);
IP_STATS_INC(ip.lenerr);
IP_STATS_INC(ip.drop);
- snmp_inc_ipindiscards();
+ MIB2_STATS_INC(mib2.ipindiscards);
return ERR_OK;
}
/* verify checksum */
#if CHECKSUM_CHECK_IP
- if (inet_chksum(iphdr, iphdr_hlen) != 0) {
+ 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)));
- ip_debug_print(p);
- pbuf_free(p);
- IP_STATS_INC(ip.chkerr);
- IP_STATS_INC(ip.drop);
- snmp_inc_ipinhdrerrors();
- return ERR_OK;
+ 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
- /* Trim pbuf. This should have been done at the netif layer,
- * but we'll do it anyway just to be sure that its done. */
- pbuf_realloc(p, iphdr_len);
-
/* copy IP addresses to aligned ip_addr_t */
- ip_addr_copy(*ipX_2_ip(&ip_data.current_iphdr_dest), iphdr->dest);
- ip_addr_copy(*ipX_2_ip(&ip_data.current_iphdr_src), iphdr->src);
+ 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 (ip_addr_ismulticast(ip_current_dest_addr())) {
- if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ip_current_dest_addr()))) {
+ 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;
}
- } else
#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->ip_addr),
- ip4_addr_get_u32(&iphdr->dest) & ip4_addr_get_u32(&netif->netmask),
- ip4_addr_get_u32(&netif->ip_addr) & ip4_addr_get_u32(&netif->netmask),
- ip4_addr_get_u32(&iphdr->dest) & ~ip4_addr_get_u32(&netif->netmask)));
-
- /* interface is up and configured? */
- if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) {
- /* unicast to this interface address? */
- if (ip_addr_cmp(ip_current_dest_addr(), &(netif->ip_addr)) ||
- /* or broadcast on this interface network address? */
- ip_addr_isbroadcast(ip_current_dest_addr(), netif)) {
- LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n",
- netif->name[0], netif->name[1]));
- /* break out of for loop */
- break;
- }
-#if LWIP_AUTOIP
- /* connections to link-local addresses must persist after changing
- the netif's address (RFC3927 ch. 1.9) */
- if ((netif->autoip != NULL) &&
- ip_addr_cmp(ip_current_dest_addr(), &(netif->autoip->llipaddr))) {
- LWIP_DEBUGF(IP_DEBUG, ("ip_input: LLA packet accepted on interface %c%c\n",
- netif->name[0], netif->name[1]));
- /* break out of for loop */
- break;
+ list of configured netifs. */
+ if (ip4_input_accept(inp)) {
+ netif = inp;
+ } else {
+ netif = NULL;
+#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()))
+#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */
+ {
+#if !LWIP_SINGLE_NETIF
+ NETIF_FOREACH(netif) {
+ if (netif == inp) {
+ /* we checked that before already */
+ continue;
+ }
+ if (ip4_input_accept(netif)) {
+ break;
+ }
}
-#endif /* LWIP_AUTOIP */
- }
- if (first) {
- first = 0;
- netif = netif_list;
- } else {
- netif = netif->next;
- }
- if (netif == inp) {
- netif = netif->next;
+#endif /* !LWIP_SINGLE_NETIF */
}
- } while(netif != NULL);
+ }
}
#if IP_ACCEPT_LINK_LAYER_ADDRESSING
@@ -443,11 +565,11 @@ ip_input(struct pbuf *p, struct netif *inp)
if (netif == NULL) {
/* remote port is DHCP server? */
if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
- struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen);
- LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: UDP packet to DHCP client port %"U16_F"\n",
- ntohs(udphdr->dest)));
+ const struct udp_hdr *udphdr = (const struct udp_hdr *)((const 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, ("ip_input: DHCP packet accepted.\n"));
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: DHCP packet accepted.\n"));
netif = inp;
check_ip_src = 0;
}
@@ -456,19 +578,24 @@ ip_input(struct pbuf *p, struct netif *inp)
#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) */
- if (check_ip_src && !ip_addr_isany(ip_current_src_addr()))
+ /* 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 */
- { if ((ip_addr_isbroadcast(ip_current_src_addr(), inp)) ||
- (ip_addr_ismulticast(ip_current_src_addr()))) {
+ )
+#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, ("ip_input: packet source is not valid.\n"));
+ 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);
- snmp_inc_ipinaddrerrors();
- snmp_inc_ipindiscards();
+ MIB2_STATS_INC(mib2.ipinaddrerrors);
+ MIB2_STATS_INC(mib2.ipindiscards);
return ERR_OK;
}
}
@@ -482,17 +609,18 @@ ip_input(struct pbuf *p, struct netif *inp)
/* packet not for us? */
if (netif == NULL) {
/* packet not for us, route or discard */
- LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: packet not for us.\n"));
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip4_input: packet not for us.\n"));
#if IP_FORWARD
/* non-broadcast packet? */
- if (!ip_addr_isbroadcast(ip_current_dest_addr(), inp)) {
+ if (!ip4_addr_isbroadcast(ip4_current_dest_addr(), inp)) {
/* try to forward IP packet on (other) interfaces */
- ip_forward(p, iphdr, inp);
+ ip4_forward(p, (struct ip_hdr *)p->payload, inp);
} else
#endif /* IP_FORWARD */
{
- snmp_inc_ipinaddrerrors();
- snmp_inc_ipindiscards();
+ IP_STATS_INC(ip.drop);
+ MIB2_STATS_INC(mib2.ipinaddrerrors);
+ MIB2_STATS_INC(mib2.ipindiscards);
}
pbuf_free(p);
return ERR_OK;
@@ -500,23 +628,23 @@ ip_input(struct pbuf *p, struct netif *inp)
/* packet consists of multiple fragments? */
if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) {
#if IP_REASSEMBLY /* packet fragment reassembly code present? */
- LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n",
- ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & PP_HTONS(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8));
+ 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 = ip_reass(p);
+ p = ip4_reass(p);
/* packet not fully reassembled yet? */
if (p == NULL) {
return ERR_OK;
}
- iphdr = (struct ip_hdr *)p->payload;
+ iphdr = (const struct ip_hdr *)p->payload;
#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */
pbuf_free(p);
LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n",
- ntohs(IPH_OFFSET(iphdr))));
+ lwip_ntohs(IPH_OFFSET(iphdr))));
IP_STATS_INC(ip.opterr);
IP_STATS_INC(ip.drop);
/* unsupported protocol feature */
- snmp_inc_ipinunknownprotos();
+ MIB2_STATS_INC(mib2.ipinunknownprotos);
return ERR_OK;
#endif /* IP_REASSEMBLY */
}
@@ -525,7 +653,7 @@ ip_input(struct pbuf *p, struct netif *inp)
#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)) {
+ if ((iphdr_hlen > IP_HLEN) && (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) {
#else
if (iphdr_hlen > IP_HLEN) {
#endif /* LWIP_IGMP */
@@ -534,80 +662,81 @@ ip_input(struct pbuf *p, struct netif *inp)
IP_STATS_INC(ip.opterr);
IP_STATS_INC(ip.drop);
/* unsupported protocol feature */
- snmp_inc_ipinunknownprotos();
+ MIB2_STATS_INC(mib2.ipinunknownprotos);
return ERR_OK;
}
#endif /* IP_OPTIONS_ALLOWED == 0 */
/* send to upper layers */
- LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n"));
- ip_debug_print(p);
- LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
+ 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 = inp;
+ 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;
+ ip_data.current_ip_header_tot_len = IPH_HL_BYTES(iphdr);
#if LWIP_RAW
/* raw input did not eat the packet? */
if (raw_input(p, inp) == 0)
#endif /* LWIP_RAW */
{
- pbuf_header(p, -iphdr_hlen); /* Move to payload, no check necessary. */
+ pbuf_remove_header(p, iphdr_hlen); /* Move to payload, no check necessary. */
switch (IPH_PROTO(iphdr)) {
#if LWIP_UDP
- case IP_PROTO_UDP:
+ case IP_PROTO_UDP:
#if LWIP_UDPLITE
- case IP_PROTO_UDPLITE:
+ case IP_PROTO_UDPLITE:
#endif /* LWIP_UDPLITE */
- snmp_inc_ipindelivers();
- udp_input(p, inp);
- break;
+ MIB2_STATS_INC(mib2.ipindelivers);
+ udp_input(p, inp);
+ break;
#endif /* LWIP_UDP */
#if LWIP_TCP
- case IP_PROTO_TCP:
- snmp_inc_ipindelivers();
- tcp_input(p, inp);
- break;
+ case IP_PROTO_TCP:
+ MIB2_STATS_INC(mib2.ipindelivers);
+ tcp_input(p, inp);
+ break;
#endif /* LWIP_TCP */
#if LWIP_ICMP
- case IP_PROTO_ICMP:
- snmp_inc_ipindelivers();
- icmp_input(p, inp);
- break;
+ 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, ip_current_dest_addr());
- break;
+ case IP_PROTO_IGMP:
+ igmp_input(p, inp, ip4_current_dest_addr());
+ break;
#endif /* LWIP_IGMP */
- default:
+ default:
#if LWIP_ICMP
- /* send ICMP destination protocol unreachable unless is was a broadcast */
- if (!ip_addr_isbroadcast(ip_current_dest_addr(), inp) &&
- !ip_addr_ismulticast(ip_current_dest_addr())) {
- pbuf_header(p, iphdr_hlen); /* Move to ip header, no check necessary. */
- p->payload = iphdr;
- icmp_dest_unreach(p, ICMP_DUR_PROTO);
- }
+ /* 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, (s16_t)iphdr_hlen); /* Move to ip header, no check necessary. */
+ icmp_dest_unreach(p, ICMP_DUR_PROTO);
+ }
#endif /* LWIP_ICMP */
- pbuf_free(p);
+ pbuf_free(p);
- LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr)));
+ 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);
- snmp_inc_ipinunknownprotos();
+ 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;
- ip_addr_set_any(ip_current_src_addr());
- ip_addr_set_any(ip_current_dest_addr());
+ ip4_addr_set_any(ip4_current_src_addr());
+ ip4_addr_set_any(ip4_current_dest_addr());
return ERR_OK;
}
@@ -617,13 +746,13 @@ ip_input(struct pbuf *p, struct netif *inp)
* the IP header and calculates the IP header checksum. If the source
* IP address is NULL, the IP address of the outgoing network
* interface is filled in as source address.
- * If the destination IP address is IP_HDRINCL, p is assumed to already
+ * 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 == IP_HDRINCL, p already includes an IP
- header and p->payload points to that IP header)
- * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ 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
@@ -638,12 +767,12 @@ ip_input(struct pbuf *p, struct netif *inp)
* unique identifiers independent of destination"
*/
err_t
-ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
- u8_t ttl, u8_t tos,
- u8_t proto, struct netif *netif)
+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 ip_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0);
+ return ip4_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0);
}
/**
@@ -652,25 +781,62 @@ ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
* @ param ip_options pointer to the IP options, copied into the IP header
* @ param optlen length of ip_options
*/
-err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
- u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
- u16_t optlen)
+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;
- ip_addr_t dest_addr;
+ ip4_addr_t dest_addr;
#if CHECKSUM_GEN_IP_INLINE
u32_t chk_sum = 0;
#endif /* CHECKSUM_GEN_IP_INLINE */
- /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
- gets altered as the packet is passed down the stack */
- LWIP_ASSERT("p->ref == 1", p->ref == 1);
+ LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
- snmp_inc_ipoutrequests();
+ MIB2_STATS_INC(mib2.ipoutrequests);
/* Should the IP header be generated or is it already included in p? */
- if (dest != IP_HDRINCL) {
+ if (dest != LWIP_IP_HDRINCL) {
u16_t ip_hlen = IP_HLEN;
#if IP_OPTIONS_SEND
u16_t optlen_aligned = 0;
@@ -678,49 +844,56 @@ err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
#if CHECKSUM_GEN_IP_INLINE
int i;
#endif /* CHECKSUM_GEN_IP_INLINE */
+ if (optlen > (IP_HLEN_MAX - IP_HLEN)) {
+ /* optlen too long */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output_if_opt: optlen too long\n"));
+ IP_STATS_INC(ip.err);
+ MIB2_STATS_INC(mib2.ipoutdiscards);
+ return ERR_VAL;
+ }
/* round up to a multiple of 4 */
- optlen_aligned = ((optlen + 3) & ~3);
- ip_hlen += optlen_aligned;
+ optlen_aligned = (u16_t)((optlen + 3) & ~3);
+ ip_hlen = (u16_t)(ip_hlen + optlen_aligned);
/* First write in the IP options */
- if (pbuf_header(p, optlen_aligned)) {
- LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output_if_opt: not enough room for IP options in pbuf\n"));
+ if (pbuf_add_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);
- snmp_inc_ipoutdiscards();
+ 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);
+ memset(((char *)p->payload) + optlen, 0, (size_t)(optlen_aligned - optlen));
}
#if CHECKSUM_GEN_IP_INLINE
- for (i = 0; i < optlen_aligned/2; i++) {
- chk_sum += ((u16_t*)p->payload)[i];
+ for (i = 0; i < optlen_aligned / 2; i++) {
+ chk_sum += ((u16_t *)p->payload)[i];
}
#endif /* CHECKSUM_GEN_IP_INLINE */
}
#endif /* IP_OPTIONS_SEND */
/* generate IP header */
- if (pbuf_header(p, IP_HLEN)) {
- LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output: not enough room for IP header in pbuf\n"));
+ if (pbuf_add_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);
- snmp_inc_ipoutdiscards();
+ 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)));
+ (p->len >= sizeof(struct ip_hdr)));
IPH_TTL_SET(iphdr, ttl);
IPH_PROTO_SET(iphdr, proto);
#if CHECKSUM_GEN_IP_INLINE
- chk_sum += LWIP_MAKE_U16(proto, ttl);
+ chk_sum += PP_NTOHS(proto | (ttl << 8));
#endif /* CHECKSUM_GEN_IP_INLINE */
/* dest cannot be NULL here */
- ip_addr_copy(iphdr->dest, *dest);
+ 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;
@@ -729,24 +902,24 @@ err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
IPH_VHL_SET(iphdr, 4, ip_hlen / 4);
IPH_TOS_SET(iphdr, tos);
#if CHECKSUM_GEN_IP_INLINE
- chk_sum += LWIP_MAKE_U16(tos, iphdr->_v_hl);
+ chk_sum += PP_NTOHS(tos | (iphdr->_v_hl << 8));
#endif /* CHECKSUM_GEN_IP_INLINE */
- IPH_LEN_SET(iphdr, htons(p->tot_len));
+ 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, htons(ip_id));
+ 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 (ip_addr_isany(src)) {
- ip_addr_copy(iphdr->src, netif->ip_addr);
+ if (src == NULL) {
+ ip4_addr_copy(iphdr->src, *IP4_ADDR_ANY4);
} else {
/* src cannot be NULL here */
- ip_addr_copy(iphdr->src, *src);
+ ip4_addr_copy(iphdr->src, *src);
}
#if CHECKSUM_GEN_IP_INLINE
@@ -755,45 +928,64 @@ err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
chk_sum = (chk_sum >> 16) + chk_sum;
chk_sum = ~chk_sum;
- iphdr->_chksum = chk_sum; /* network order */
+ 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
- IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));
-#endif
+ 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 */
+ if (p->len < IP_HLEN) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip4_output: LWIP_IP_HDRINCL but pbuf is too short\n"));
+ IP_STATS_INC(ip.err);
+ MIB2_STATS_INC(mib2.ipoutdiscards);
+ return ERR_BUF;
+ }
iphdr = (struct ip_hdr *)p->payload;
- ip_addr_copy(dest_addr, iphdr->dest);
+ ip4_addr_copy(dest_addr, iphdr->dest);
dest = &dest_addr;
}
IP_STATS_INC(ip.xmit);
- LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num));
- ip_debug_print(p);
+ 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 (ip_addr_cmp(dest, &netif->ip_addr)) {
+ 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, dest);
+ return netif_loop_output(netif, p);
}
-#if LWIP_IGMP
+#if LWIP_MULTICAST_TX_OPTIONS
if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) {
- netif_loop_output(netif, p, dest);
+ netif_loop_output(netif, p);
}
-#endif /* LWIP_IGMP */
+#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 ip_frag(p, netif, dest);
+ return ip4_frag(p, netif, dest);
}
#endif /* IP_FRAG */
- LWIP_DEBUGF(IP_DEBUG, ("netif->output()"));
+ LWIP_DEBUGF(IP_DEBUG, ("ip4_output_if: call netif->output()\n"));
return netif->output(netif, p, dest);
}
@@ -802,9 +994,9 @@ err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
* interface and calls upon ip_output_if to do the actual work.
*
* @param p the packet to send (p->payload points to the data, e.g. next
- protocol header; if dest == IP_HDRINCL, p already includes an IP
- header and p->payload points to that IP header)
- * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ 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
@@ -815,110 +1007,108 @@ err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
* see ip_output_if() for more return values
*/
err_t
-ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
- u8_t ttl, u8_t tos, u8_t proto)
+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;
- /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
- gets altered as the packet is passed down the stack */
- LWIP_ASSERT("p->ref == 1", p->ref == 1);
+ LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
- if ((netif = ip_route(dest)) == NULL) {
- LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
- ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ if ((netif = ip4_route_src(src, dest)) == 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 ip_output_if(p, src, dest, ttl, tos, proto, netif);
+ return ip4_output_if(p, src, dest, ttl, tos, proto, netif);
}
-#if LWIP_NETIF_HWADDRHINT
+#if LWIP_NETIF_USE_HINTS
/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint
* before calling ip_output_if.
*
* @param p the packet to send (p->payload points to the data, e.g. next
- protocol header; if dest == IP_HDRINCL, p already includes an IP
- header and p->payload points to that IP header)
- * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ 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
+ * @param netif_hint netif output hint pointer set to netif->hint before
* calling ip_output_if()
*
* @return ERR_RTE if no route is found
* see ip_output_if() for more return values
*/
err_t
-ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
- u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint)
+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, struct netif_hint *netif_hint)
{
struct netif *netif;
err_t err;
- /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
- gets altered as the packet is passed down the stack */
- LWIP_ASSERT("p->ref == 1", p->ref == 1);
+ LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
- if ((netif = ip_route(dest)) == NULL) {
- LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
- ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ if ((netif = ip4_route_src(src, dest)) == 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 = ip_output_if(p, src, dest, ttl, tos, proto, netif);
- NETIF_SET_HWADDRHINT(netif, NULL);
+ NETIF_SET_HINTS(netif, netif_hint);
+ err = ip4_output_if(p, src, dest, ttl, tos, proto, netif);
+ NETIF_RESET_HINTS(netif);
return err;
}
-#endif /* LWIP_NETIF_HWADDRHINT*/
+#endif /* LWIP_NETIF_USE_HINTS*/
#if IP_DEBUG
/* Print an IP header by using LWIP_DEBUGF
* @param p an IP packet, p->payload pointing to the IP header
*/
void
-ip_debug_print(struct pbuf *p)
+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",
- IPH_V(iphdr),
- IPH_HL(iphdr),
- IPH_TOS(iphdr),
- ntohs(IPH_LEN(iphdr))));
+ (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",
- ntohs(IPH_ID(iphdr)),
- ntohs(IPH_OFFSET(iphdr)) >> 15 & 1,
- ntohs(IPH_OFFSET(iphdr)) >> 14 & 1,
- ntohs(IPH_OFFSET(iphdr)) >> 13 & 1,
- ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK));
+ lwip_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",
- IPH_TTL(iphdr),
- IPH_PROTO(iphdr),
- ntohs(IPH_CHKSUM(iphdr))));
+ (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)));
+ 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)));
+ 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/lwip/src/core/ipv4/ip4_addr.c b/lwip/src/core/ipv4/ip4_addr.c
index 8f633ff..e60e2cf 100644
--- a/lwip/src/core/ipv4/ip4_addr.c
+++ b/lwip/src/core/ipv4/ip4_addr.c
@@ -6,9 +6,9 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -17,63 +17,66 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
#include "lwip/opt.h"
+
+#if LWIP_IPV4
+
#include "lwip/ip_addr.h"
#include "lwip/netif.h"
-/* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */
-const ip_addr_t ip_addr_any = { IPADDR_ANY };
-const ip_addr_t ip_addr_broadcast = { IPADDR_BROADCAST };
+/* 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
- *
+ * Determine if an address is a broadcast address on a network interface
+ *
* @param addr address to be checked
* @param netif the network interface against which the address is checked
* @return returns non-zero if the address is a broadcast address
*/
u8_t
-ip4_addr_isbroadcast(u32_t addr, const struct netif *netif)
+ip4_addr_isbroadcast_u32(u32_t addr, const struct netif *netif)
{
- ip_addr_t ipaddr;
+ 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? */
+ /* no broadcast support on this network interface? */
} else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) {
/* the given address cannot be a broadcast address
* nor can we check against any broadcast addresses */
return 0;
- /* address matches network interface address exactly? => no broadcast */
- } else if (addr == ip4_addr_get_u32(&netif->ip_addr)) {
+ /* 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 (ip_addr_netcmp(&ipaddr, &(netif->ip_addr), &(netif->netmask))
- /* ...and host identifier bits are all ones? =>... */
- && ((addr & ~ip4_addr_get_u32(&netif->netmask)) ==
- (IPADDR_BROADCAST & ~ip4_addr_get_u32(&netif->netmask)))) {
+ /* 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 {
@@ -123,15 +126,15 @@ ip4_addr_netmask_valid(u32_t netmask)
* Ascii internet address interpretation routine.
* The value returned is in network order.
*
- * @param cp IP address in ascii represenation (e.g. "127.0.0.1")
+ * @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)
{
- ip_addr_t val;
+ ip4_addr_t val;
- if (ipaddr_aton(cp, &val)) {
+ if (ip4addr_aton(cp, &val)) {
return ip4_addr_get_u32(&val);
}
return (IPADDR_NONE);
@@ -144,12 +147,12 @@ ipaddr_addr(const char *cp)
* This replaces inet_addr, the return value from which
* cannot distinguish between failure and a local broadcast address.
*
- * @param cp IP address in ascii represenation (e.g. "127.0.0.1")
+ * @param 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
-ipaddr_aton(const char *cp, ip_addr_t *addr)
+ip4addr_aton(const char *cp, ip4_addr_t *addr)
{
u32_t val;
u8_t base;
@@ -164,8 +167,9 @@ ipaddr_aton(const char *cp, ip_addr_t *addr)
* Values are specified as for C:
* 0x=hex, 0=octal, 1-9=decimal.
*/
- if (!isdigit(c))
- return (0);
+ if (!isdigit(c)) {
+ return 0;
+ }
val = 0;
base = 10;
if (c == '0') {
@@ -173,18 +177,20 @@ ipaddr_aton(const char *cp, ip_addr_t *addr)
if (c == 'x' || c == 'X') {
base = 16;
c = *++cp;
- } else
+ } else {
base = 8;
+ }
}
for (;;) {
if (isdigit(c)) {
- val = (val * base) + (int)(c - '0');
+ val = (val * base) + (u32_t)(c - '0');
c = *++cp;
} else if (base == 16 && isxdigit(c)) {
- val = (val << 4) | (int)(c + 10 - (islower(c) ? 'a' : 'A'));
+ val = (val << 4) | (u32_t)(c + 10 - (islower(c) ? 'a' : 'A'));
c = *++cp;
- } else
+ } else {
break;
+ }
}
if (c == '.') {
/*
@@ -194,18 +200,19 @@ ipaddr_aton(const char *cp, ip_addr_t *addr)
* a.b (with b treated as 24 bits)
*/
if (pp >= parts + 3) {
- return (0);
+ return 0;
}
*pp++ = val;
c = *++cp;
- } else
+ } else {
break;
+ }
}
/*
* Check for trailing characters.
*/
if (c != '\0' && !isspace(c)) {
- return (0);
+ return 0;
}
/*
* Concoct the address according to
@@ -213,40 +220,49 @@ ipaddr_aton(const char *cp, ip_addr_t *addr)
*/
switch (pp - parts + 1) {
- case 0:
- return (0); /* initial nondigit */
+ case 0:
+ return 0; /* initial nondigit */
- case 1: /* a -- 32 bits */
- break;
+ case 1: /* a -- 32 bits */
+ break;
- case 2: /* a.b -- 8.24 bits */
- if (val > 0xffffffUL) {
- return (0);
- }
- val |= parts[0] << 24;
- 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);
- }
- val |= (parts[0] << 24) | (parts[1] << 16);
- 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);
- }
- val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
- break;
- default:
- LWIP_ASSERT("unhandled", 0);
- 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, htonl(val));
+ ip4_addr_set_u32(addr, lwip_htonl(val));
}
- return (1);
+ return 1;
}
/**
@@ -255,17 +271,17 @@ ipaddr_aton(const char *cp, ip_addr_t *addr)
*
* @param addr ip address in network order to convert
* @return pointer to a global static (!) buffer that holds the ASCII
- * represenation of addr
+ * representation of addr
*/
char *
-ipaddr_ntoa(const ip_addr_t *addr)
+ip4addr_ntoa(const ip4_addr_t *addr)
{
- static char str[16];
- return ipaddr_ntoa_r(addr, str, 16);
+ 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.
+ * Same as ip4addr_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
@@ -273,7 +289,8 @@ ipaddr_ntoa(const ip_addr_t *addr)
* @return either pointer to buf which now holds the ASCII
* representation of addr or NULL if buf was too small
*/
-char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen)
+char *
+ip4addr_ntoa_r(const ip4_addr_t *addr, char *buf, int buflen)
{
u32_t s_addr;
char inv[3];
@@ -288,14 +305,14 @@ char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen)
rp = buf;
ap = (u8_t *)&s_addr;
- for(n = 0; n < 4; n++) {
+ for (n = 0; n < 4; n++) {
i = 0;
do {
rem = *ap % (u8_t)10;
*ap /= (u8_t)10;
- inv[i++] = '0' + rem;
- } while(*ap);
- while(i--) {
+ inv[i++] = (char)('0' + rem);
+ } while (*ap);
+ while (i--) {
if (len++ >= buflen) {
return NULL;
}
@@ -310,3 +327,5 @@ char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen)
*--rp = 0;
return buf;
}
+
+#endif /* LWIP_IPV4 */
diff --git a/lwip/src/core/ipv4/ip_frag.c b/lwip/src/core/ipv4/ip4_frag.c
index 8d18434..cf681a7 100644
--- a/lwip/src/core/ipv4/ip_frag.c
+++ b/lwip/src/core/ipv4/ip4_frag.c
@@ -6,9 +6,9 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -17,33 +17,35 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
- * Author: Jani Monoses <jani@iv.ro>
+ *
+ * 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"
+
+#if LWIP_IPV4
+
+#include "lwip/ip4_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"
@@ -77,6 +79,10 @@
#define IP_REASS_FLAG_LASTFRAG 0x01
+#define IP_REASS_VALIDATE_TELEGRAM_FINISHED 1
+#define IP_REASS_VALIDATE_PBUF_QUEUED 0
+#define IP_REASS_VALIDATE_PBUF_DROPPED -1
+
/** This is a helper struct which holds the starting
* offset and the ending offset of this fragment to
* easily chain the fragments.
@@ -100,8 +106,8 @@ PACK_STRUCT_END
#endif
#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \
- (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
- ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
+ (ip4_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
+ ip4_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
/* global variables */
@@ -129,7 +135,7 @@ ip_reass_tmr(void)
* 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));
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n", (u16_t)r->timer));
prev = r;
r = r->next;
} else {
@@ -141,8 +147,8 @@ ip_reass_tmr(void)
r = r->next;
/* free the helper struct and all enqueued pbufs */
ip_reass_free_complete_datagram(tmp, prev);
- }
- }
+ }
+ }
}
/**
@@ -158,7 +164,7 @@ static int
ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
{
u16_t pbufs_freed = 0;
- u8_t clen;
+ u16_t clen;
struct pbuf *p;
struct ip_reass_helper *iprh;
@@ -167,7 +173,7 @@ ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *p
LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
}
- snmp_inc_ipreasmfails();
+ MIB2_STATS_INC(mib2.ipreasmfails);
#if LWIP_ICMP
iprh = (struct ip_reass_helper *)ipr->p->payload;
if (iprh->start == 0) {
@@ -180,12 +186,12 @@ ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *p
icmp_time_exceeded(p, ICMP_TE_FRAG);
clen = pbuf_clen(p);
LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
- pbufs_freed += clen;
+ pbufs_freed = (u16_t)(pbufs_freed + clen);
pbuf_free(p);
}
#endif /* LWIP_ICMP */
- /* First, free all received pbufs. The individual pbufs need to be released
+ /* 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) {
@@ -196,13 +202,13 @@ ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *p
p = iprh->next_pbuf;
clen = pbuf_clen(pcur);
LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
- pbufs_freed += clen;
+ pbufs_freed = (u16_t)(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;
+ LWIP_ASSERT("ip_reass_pbufcount >= pbufs_freed", ip_reass_pbufcount >= pbufs_freed);
+ ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount - pbufs_freed);
return pbufs_freed;
}
@@ -223,7 +229,7 @@ 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;
+ struct ip_reassdata *r, *oldest, *prev, *oldest_prev;
int pbufs_freed = 0, pbufs_freed_current;
int other_datagrams;
@@ -232,6 +238,7 @@ ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
do {
oldest = NULL;
prev = NULL;
+ oldest_prev = NULL;
other_datagrams = 0;
r = reassdatagrams;
while (r != NULL) {
@@ -240,9 +247,11 @@ ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
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) {
@@ -251,7 +260,7 @@ ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
r = r->next;
}
if (oldest != NULL) {
- pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev);
+ pbufs_freed_current = ip_reass_free_complete_datagram(oldest, oldest_prev);
pbufs_freed += pbufs_freed_current;
}
} while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
@@ -265,10 +274,14 @@ ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
* @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*
+static struct ip_reassdata *
ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
{
- struct ip_reassdata* ipr;
+ 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) {
@@ -280,7 +293,7 @@ ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
#endif /* IP_REASS_FREE_OLDEST */
{
IPFRAG_STATS_INC(ip_frag.memerr);
- LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n"));
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("Failed to alloc reassdata struct\n"));
return NULL;
}
}
@@ -303,7 +316,6 @@ ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
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 */
@@ -314,7 +326,7 @@ ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
prev->next = ipr->next;
}
- /* now we can free the ip_reass struct */
+ /* now we can free the ip_reassdata struct */
memp_free(MEMP_REASSDATA, ipr);
}
@@ -323,38 +335,50 @@ ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
* 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 ipr points to the reassembly state
* @param new_p points to the pbuf for the current fragment
- * @return 0 if invalid, >0 otherwise
+ * @param is_last is 1 if this pbuf has MF==0 (ipr->flags not updated yet)
+ * @return see IP_REASS_VALIDATE_* defines
*/
static int
-ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
+ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p, int is_last)
{
- struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
+ struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev = NULL;
struct pbuf *q;
- u16_t offset,len;
+ u16_t offset, len, clen;
+ u8_t hlen;
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;
+ fraghdr = (struct ip_hdr *)new_p->payload;
+ len = lwip_ntohs(IPH_LEN(fraghdr));
+ hlen = IPH_HL_BYTES(fraghdr);
+ if (hlen > len) {
+ /* invalid datagram */
+ goto freepbuf;
+ }
+ len = (u16_t)(len - hlen);
+ offset = IPH_OFFSET_BYTES(fraghdr);
/* 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 = (struct ip_reass_helper *)new_p->payload;
iprh->next_pbuf = NULL;
iprh->start = offset;
- iprh->end = offset + len;
+ iprh->end = (u16_t)(offset + len);
+ if (iprh->end < offset) {
+ /* u16_t overflow, cannot handle this */
+ goto freepbuf;
+ }
/* Iterate through until we either get to the end of the list (append),
- * or we find on with a larger offset (insert). */
+ * or we find one with a larger offset (insert). */
for (q = ipr->p; q != NULL;) {
- iprh_tmp = (struct ip_reass_helper*)q->payload;
+ 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;
@@ -367,21 +391,32 @@ ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct
}
#endif /* IP_REASS_CHECK_OVERLAP */
iprh_prev->next_pbuf = new_p;
+ if (iprh_prev->end != iprh->start) {
+ /* There is a fragment missing between the current
+ * and the previous fragment */
+ valid = 0;
+ }
} else {
+#if IP_REASS_CHECK_OVERLAP
+ if (iprh->end > iprh_tmp->start) {
+ /* fragment overlaps with following, throw away */
+ goto freepbuf;
+ }
+#endif /* IP_REASS_CHECK_OVERLAP */
/* fragment with the lowest offset */
ipr->p = new_p;
}
break;
- } else if(iprh->start == iprh_tmp->start) {
+ } 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) {
+ } 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. */
+ /* 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
@@ -409,7 +444,7 @@ ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct
} else {
#if IP_REASS_CHECK_OVERLAP
LWIP_ASSERT("no previous fragment, this must be the first fragment!",
- ipr->p == NULL);
+ ipr->p == NULL);
#endif /* IP_REASS_CHECK_OVERLAP */
/* this is the first fragment we ever received for this ip datagram */
ipr->p = new_p;
@@ -418,19 +453,19 @@ ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct
/* 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 (is_last || ((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 (((struct ip_reass_helper*)ipr->p->payload)->start != 0) {
+ 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 */
+ /* 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;
+ iprh = (struct ip_reass_helper *)q->payload;
if (iprh_prev->end != iprh->start) {
valid = 0;
break;
@@ -443,26 +478,26 @@ ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct
if (valid) {
LWIP_ASSERT("sanity check", ipr->p != NULL);
LWIP_ASSERT("sanity check",
- ((struct ip_reass_helper*)ipr->p->payload) != iprh);
+ ((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);
+ iprh->next_pbuf == NULL);
}
}
}
/* 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;
+ return valid ? IP_REASS_VALIDATE_TELEGRAM_FINISHED : IP_REASS_VALIDATE_PBUF_QUEUED;
}
/* If we come here, not all fragments were received, yet! */
- return 0; /* not yet valid! */
+ return IP_REASS_VALIDATE_PBUF_QUEUED; /* not yet valid! */
#if IP_REASS_CHECK_OVERLAP
freepbuf:
- ip_reass_pbufcount -= pbuf_clen(new_p);
+ clen = pbuf_clen(new_p);
+ LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= clen);
+ ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount - clen);
pbuf_free(new_p);
- return 0;
+ return IP_REASS_VALIDATE_PBUF_DROPPED;
#endif /* IP_REASS_CHECK_OVERLAP */
}
@@ -473,29 +508,36 @@ freepbuf:
* @return NULL if reassembly is incomplete, ? otherwise
*/
struct pbuf *
-ip_reass(struct pbuf *p)
+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;
- u8_t clen;
- struct ip_reassdata *ipr_prev = NULL;
+ u16_t offset, len, clen;
+ u8_t hlen;
+ int valid;
+ int is_last;
IPFRAG_STATS_INC(ip_frag.recv);
- snmp_inc_ipreasmreqds();
+ MIB2_STATS_INC(mib2.ipreasmreqds);
- fraghdr = (struct ip_hdr*)p->payload;
+ 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"));
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_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;
+ offset = IPH_OFFSET_BYTES(fraghdr);
+ len = lwip_ntohs(IPH_LEN(fraghdr));
+ hlen = IPH_HL_BYTES(fraghdr);
+ if (hlen > len) {
+ /* invalid datagram */
+ goto nullreturn;
+ }
+ len = (u16_t)(len - hlen);
/* Check if we are allowed to enqueue more datagrams. */
clen = pbuf_clen(p);
@@ -506,8 +548,8 @@ ip_reass(struct pbuf *p)
#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));
+ 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 */
@@ -522,24 +564,23 @@ ip_reass(struct pbuf *p)
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))));
+ 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;
}
- ipr_prev = ipr;
}
if (ipr == NULL) {
- /* Enqueue a new datagram into the datagram queue */
+ /* Enqueue a new datagram into the datagram queue */
ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
/* Bail if unable to enqueue */
- if(ipr == NULL) {
+ if (ipr == NULL) {
goto nullreturn;
}
} else {
- if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) &&
- ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
+ 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
@@ -547,66 +588,104 @@ ip_reass(struct pbuf *p)
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
+ /* 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));
+ is_last = (IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0;
+ if (is_last) {
+ u16_t datagram_len = (u16_t)(offset + len);
+ if ((datagram_len < offset) || (datagram_len > (0xFFFF - IP_HLEN))) {
+ /* u16_t overflow, cannot handle this */
+ goto nullreturn;
+ }
}
/* 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)) {
+ valid = ip_reass_chain_frag_into_datagram_and_validate(ipr, p, is_last);
+ if (valid == IP_REASS_VALIDATE_PBUF_DROPPED) {
+ goto nullreturn;
+ }
+ /* if we come here, the pbuf has been enqueued */
+
+ /* 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
+ (overflow checked by testing against IP_REASS_MAX_PBUFS) */
+ ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount + clen);
+ if (is_last) {
+ u16_t datagram_len = (u16_t)(offset + len);
+ ipr->datagram_len = datagram_len;
+ ipr->flags |= IP_REASS_FLAG_LASTFRAG;
+ LWIP_DEBUGF(IP_REASS_DEBUG,
+ ("ip4_reass: last fragment seen, total len %"S16_F"\n",
+ ipr->datagram_len));
+ }
+
+ if (valid == IP_REASS_VALIDATE_TELEGRAM_FINISHED) {
+ 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;
+ u16_t datagram_len = (u16_t)(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;
+ 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);
+ fraghdr = (struct ip_hdr *)(ipr->p->payload);
SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
- IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
+ IPH_LEN_SET(fraghdr, lwip_htons(datagram_len));
IPH_OFFSET_SET(fraghdr, 0);
IPH_CHKSUM_SET(fraghdr, 0);
- /* @todo: do we need to set calculate the correct checksum? */
- IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
+ /* @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;
+ while (r != NULL) {
+ iprh = (struct ip_reass_helper *)r->payload;
- /* hide the ip header for every succeding fragment */
- pbuf_header(r, -IP_HLEN);
+ /* hide the ip header for every succeeding fragment */
+ pbuf_remove_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);
+ clen = pbuf_clen(p);
+ LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= clen);
+ ip_reass_pbufcount = (u16_t)(ip_reass_pbufcount - clen);
+
+ 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));
+ 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"));
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip4_reass: nullreturn\n"));
IPFRAG_STATS_INC(ip_frag.drop);
pbuf_free(p);
return NULL;
@@ -614,21 +693,17 @@ nullreturn:
#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*
+static struct pbuf_custom_ref *
ip_frag_alloc_pbuf_custom_ref(void)
{
- return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
+ 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)
+ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref *p)
{
LWIP_ASSERT("p != NULL", p != NULL);
memp_free(MEMP_FRAG_PBUF, p);
@@ -639,23 +714,21 @@ ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
static void
ipfrag_free_pbuf_custom(struct pbuf *p)
{
- struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)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);
+ 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).
+ * by pointing PBUF_REFs into p.
*
* @param p ip packet to send
* @param netif the netif on which to send
@@ -663,93 +736,56 @@ ipfrag_free_pbuf_custom(struct pbuf *p)
*
* @return ERR_OK if sent successfully, err_t otherwise
*/
-err_t
-ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
+err_t
+ip4_frag(struct pbuf *p, struct netif *netif, const ip4_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;
+ u16_t newpbuflen = 0;
+ u16_t left_to_copy;
#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;
+ const u16_t nfb = (u16_t)((netif->mtu - IP_HLEN) / 8);
+ u16_t left, fragsize;
+ u16_t ofo;
+ int 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 */
+ LWIP_ERROR("ip4_frag() does not support IP options", IPH_HL_BYTES(iphdr) == IP_HLEN, return ERR_VAL);
+ LWIP_ERROR("ip4_frag(): pbuf too short", p->len >= IP_HLEN, return ERR_VAL);
/* Save original offset */
- tmp = ntohs(IPH_OFFSET(iphdr));
+ tmp = lwip_ntohs(IPH_OFFSET(iphdr));
ofo = tmp & IP_OFFMASK;
- omf = tmp & IP_MF;
-
- left = p->tot_len - IP_HLEN;
+ LWIP_ERROR("ip_frag(): MF already set", (tmp & IP_MF) == 0, return ERR_VAL);
- nfb = (mtu - IP_HLEN) / 8;
+ left = (u16_t)(p->tot_len - IP_HLEN);
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;
+ fragsize = LWIP_MIN(left, (u16_t)(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);
+ rambuf = pbuf_alloc(PBUF_IP, fragsize, PBUF_RAM);
if (rambuf == NULL) {
- return ERR_MEM;
+ 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, cop, poff);
+ (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)) {
+ if (pbuf_add_header(rambuf, IP_HLEN)) {
pbuf_free(rambuf);
- return ERR_MEM;
+ goto memerr;
}
/* fill in the IP header */
SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
- iphdr = rambuf->payload;
+ 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.
@@ -758,37 +794,37 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
*/
rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
if (rambuf == NULL) {
- return ERR_MEM;
+ 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;
- /* Can just adjust p directly for needed offset. */
- p->payload = (u8_t *)p->payload + poff;
- p->len -= poff;
-
- left_to_copy = cop;
+ left_to_copy = fragsize;
while (left_to_copy) {
struct pbuf_custom_ref *pcr;
- newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
+ u16_t plen = (u16_t)(p->len - poff);
+ LWIP_ASSERT("p->len >= poff", 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);
- return ERR_MEM;
+ goto memerr;
}
/* 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);
+ 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);
- return ERR_MEM;
+ goto memerr;
}
pbuf_ref(p);
pcr->original = p;
@@ -798,44 +834,32 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
* so that it is removed when pbuf_dechain is later called on rambuf.
*/
pbuf_cat(rambuf, newpbuf);
- left_to_copy -= newpbuflen;
+ left_to_copy = (u16_t)(left_to_copy - newpbuflen);
if (left_to_copy) {
+ poff = 0;
p = p->next;
}
}
- poff = newpbuflen;
+ poff = (u16_t)(poff + newpbuflen);
#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
-#endif /* IP_FRAG_USES_STATIC_BUF */
/* Correct header */
- IPH_OFFSET_SET(iphdr, htons(tmp));
- IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
- IPH_CHKSUM_SET(iphdr, 0);
- IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+ last = (left <= netif->mtu - IP_HLEN);
-#if IP_FRAG_USES_STATIC_BUF
- if (last) {
- pbuf_realloc(rambuf, left + IP_HLEN);
+ /* Set new offset and MF flag */
+ tmp = (IP_OFFMASK & (ofo));
+ if (!last) {
+ tmp = tmp | IP_MF;
}
-
- /* 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;
+ IPH_OFFSET_SET(iphdr, lwip_htons(tmp));
+ IPH_LEN_SET(iphdr, lwip_htons((u16_t)(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));
}
-#else /* IP_FRAG_USES_STATIC_BUF */
+#endif /* CHECKSUM_GEN_IP */
+
/* No need for separate header pbuf - we allowed room for it in rambuf
* when allocated.
*/
@@ -848,16 +872,17 @@ ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
* 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;
+ left = (u16_t)(left - fragsize);
+ ofo = (u16_t)(ofo + nfb);
}
-#if IP_FRAG_USES_STATIC_BUF
- pbuf_free(rambuf);
-#endif /* IP_FRAG_USES_STATIC_BUF */
- snmp_inc_ipfragoks();
+ 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/lwip/src/core/ipv6/README b/lwip/src/core/ipv6/README
deleted file mode 100644
index 3620004..0000000
--- a/lwip/src/core/ipv6/README
+++ /dev/null
@@ -1 +0,0 @@
-IPv6 support in lwIP is very experimental.
diff --git a/lwip/src/core/ipv6/dhcp6.c b/lwip/src/core/ipv6/dhcp6.c
index 9656c3b..ecad68b 100644
--- a/lwip/src/core/ipv6/dhcp6.c
+++ b/lwip/src/core/ipv6/dhcp6.c
@@ -1,11 +1,10 @@
/**
* @file
- *
- * DHCPv6.
+ * DHCPv6 - RFC 3315
*/
/*
- * Copyright (c) 2010 Inico Technologies Ltd.
+ * Copyright (c) 2017 Simon Goldschmidt
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -32,19 +31,255 @@
*
* 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>
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
*/
#include "lwip/opt.h"
-#if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.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/dhcp6.h"
+#include "lwip/prot/dhcp6.h"
#include "lwip/def.h"
+#include "lwip/udp.h"
+
+#include <string.h>
+
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
+#endif
+#ifndef LWIP_HOOK_DHCP6_APPEND_OPTIONS
+#define LWIP_HOOK_DHCP6_APPEND_OPTIONS(netif, dhcp6, state, msg, msg_type)
+#endif
+#ifndef LWIP_HOOK_DHCP6_PARSE_OPTION
+#define LWIP_HOOK_DHCP6_PARSE_OPTION(netif, dhcp6, state, msg, msg_type, option, len, pbuf, offset)
+#endif
+
+const ip_addr_t dhcp6_All_DHCP_Relay_Agents_and_Servers = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x00010002);
+const ip_addr_t dhcp6_All_DHCP_Servers = IPADDR6_INIT_HOST(0xFF020000, 0, 0, 0x00010003);
+
+static struct udp_pcb *dhcp6_pcb;
+static u8_t dhcp6_pcb_refcount;
+
+
+/* receive, unfold, parse and free incoming messages */
+static void dhcp6_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port);
+
+/** Ensure DHCP PCB is allocated and bound */
+static err_t
+dhcp6_inc_pcb_refcount(void)
+{
+ if (dhcp6_pcb_refcount == 0) {
+ LWIP_ASSERT("dhcp6_inc_pcb_refcount(): memory leak", dhcp6_pcb == NULL);
+
+ /* allocate UDP PCB */
+ dhcp6_pcb = udp_new();
+
+ if (dhcp6_pcb == NULL) {
+ return ERR_MEM;
+ }
+
+ ip_set_option(dhcp6_pcb, SOF_BROADCAST);
+
+ /* set up local and remote port for the pcb -> listen on all interfaces on all src/dest IPs */
+ udp_bind(dhcp6_pcb, IP6_ADDR_ANY, DHCP6_CLIENT_PORT);
+ udp_recv(dhcp6_pcb, dhcp6_recv, NULL);
+ }
+
+ dhcp6_pcb_refcount++;
+
+ return ERR_OK;
+}
+
+/** Free DHCP PCB if the last netif stops using it */
+static void
+dhcp6_dec_pcb_refcount(void)
+{
+ LWIP_ASSERT("dhcp6_pcb_refcount(): refcount error", (dhcp6_pcb_refcount > 0));
+ dhcp6_pcb_refcount--;
+
+ if (dhcp6_pcb_refcount == 0) {
+ udp_remove(dhcp6_pcb);
+ dhcp6_pcb = NULL;
+ }
+}
+
+/**
+ * @ingroup dhcp6
+ * Set a statically allocated struct dhcp6 to work with.
+ * Using this prevents dhcp6_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct dhcp
+ * @param dhcp6 (uninitialised) dhcp6 struct allocated by the application
+ */
+void
+dhcp6_set_struct(struct netif *netif, struct dhcp6 *dhcp6)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("dhcp6 != NULL", dhcp6 != NULL);
+ LWIP_ASSERT("netif already has a struct dhcp6 set", netif_dhcp6_data(netif) == NULL);
+
+ /* clear data structure */
+ memset(dhcp6, 0, sizeof(struct dhcp6));
+ /* dhcp_set_state(&dhcp, DHCP_STATE_OFF); */
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, dhcp6);
+}
+
+/**
+ * @ingroup dhcp6
+ * Removes a struct dhcp6 from a netif.
+ *
+ * ATTENTION: Only use this when not using dhcp6_set_struct() to allocate the
+ * struct dhcp6 since the memory is passed back to the heap.
+ *
+ * @param netif the netif from which to remove the struct dhcp
+ */
+void dhcp6_cleanup(struct netif *netif)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+
+ if (netif_dhcp6_data(netif) != NULL) {
+ mem_free(netif_dhcp6_data(netif));
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL);
+ }
+}
+
+static struct dhcp6*
+dhcp6_get_struct(struct netif *netif, const char *dbg_requester)
+{
+ struct dhcp6 *dhcp6 = netif_dhcp6_data(netif);
+ if (dhcp6 == NULL) {
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: mallocing new DHCPv6 client\n", dbg_requester));
+ dhcp6 = (struct dhcp6 *)mem_malloc(sizeof(struct dhcp6));
+ if (dhcp6 == NULL) {
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: could not allocate dhcp6\n", dbg_requester));
+ return NULL;
+ }
+
+ /* clear data structure, this implies DHCP6_STATE_OFF */
+ memset(dhcp6, 0, sizeof(struct dhcp6));
+ /* store this dhcp6 client in the netif */
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, dhcp6);
+ } else {
+ /* already has DHCP6 client attached */
+ LWIP_DEBUGF(DHCP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("%s: using existing DHCPv6 client\n", dbg_requester));
+ }
+
+ if (!dhcp6->pcb_allocated) {
+ if (dhcp6_inc_pcb_refcount() != ERR_OK) { /* ensure DHCP6 PCB is allocated */
+ mem_free(dhcp6);
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP6, NULL);
+ return NULL;
+ }
+ LWIP_DEBUGF(DHCP6_DEBUG | LWIP_DBG_TRACE, ("%s: allocated dhcp6", dbg_requester));
+ dhcp6->pcb_allocated = 1;
+ }
+ return dhcp6;
+}
+
+static u32_t
+dhcp6_create_next_xid(struct netif *netif, struct dhcp6 dhcp6)
+{
+ LWIP_UNUSED_ARG(netif);
+ LWIP_UNUSED_ARG(dhcp6);
+}
+
+#if LWIP_IPV6_DHCP6_STATELESS
+static err_t
+dhcp_information_request(struct netif *netif, struct dhcp6 *dhcp6)
+{
+ 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]);
+ }
+ LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, DHCP_STATE_SELECTING, dhcp->msg_out, DHCP_DISCOVER);
+ dhcp_option_trailer(dhcp);
+
+ 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 = (u16_t)((dhcp->tries < 6 ? 1 << dhcp->tries : 60) * 1000);
+ dhcp->request_timeout = (u16_t)((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;
+}
+
+static err_t
+dhcp6_request_config(struct netif *netif)
+{
+ struct dhcp6 *dhcp6;
+
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+
+ dhcp6 = dhcp6_get_struct(netif, "dhcp6_request_config()");
+ if (dhcp6 == NULL) {
+ return ERR_MEM;
+ }
+
+ /* if state is not idle, set a flag to send an information-request later? */
+ if (dhcp6->state != DHCP6_STATE_OFF) {
+ dhcp6->request_config_pending = 1;
+ return ERR_OK;
+ }
+
+ /* send Information-request and wait for answer; setup receive timeout */
+ dhcp_information_request(netif, dhcp6);
+
+ return ERR_OK;
+}
+#endif /* LWIP_IPV6_DHCP6_STATELESS */
+
+void
+dhcp6_nd6_ra_trigger(struct netif *netif, u8_t managed_addr_config, u8_t other_config)
+{
+ LWIP_UNUSED_ARG(managed_addr_config);
+ LWIP_UNUSED_ARG(other_config);
+
+#if LWIP_IPV6_DHCP6_STATELESS
+ if (other_config) {
+ dhcp6_request_config(netif);
+ }
+#endif /* LWIP_IPV6_DHCP6_STATELESS */
+}
+
+static void
+dhcp6_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, const ip_addr_t *addr, u16_t port)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(p);
+ LWIP_UNUSED_ARG(addr);
+ LWIP_UNUSED_ARG(port);
+}
-#endif /* LWIP_IPV6_DHCP6 */
+#endif /* LWIP_IPV6 && LWIP_IPV6_DHCP6 */
diff --git a/lwip/src/core/ipv6/ethip6.c b/lwip/src/core/ipv6/ethip6.c
index ab9783a..904005c 100644
--- a/lwip/src/core/ipv6/ethip6.c
+++ b/lwip/src/core/ipv6/ethip6.c
@@ -51,143 +51,71 @@
#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>
-#define ETHTYPE_IPV6 0x86DD
-
-/** The ethernet address */
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct eth_addr {
- PACK_STRUCT_FIELD(u8_t addr[6]);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
-
-/** Ethernet header */
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct eth_hdr {
-#if ETH_PAD_SIZE
- PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]);
-#endif
- PACK_STRUCT_FIELD(struct eth_addr dest);
- PACK_STRUCT_FIELD(struct eth_addr src);
- PACK_STRUCT_FIELD(u16_t type);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
-
-#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE)
-
-/**
- * Send an IPv6 packet on the network using netif->linkoutput
- * The ethernet header is filled in before sending.
- *
- * @params netif the lwIP network interface on which to send the packet
- * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header
- * @params src the source MAC address to be copied into the ethernet header
- * @params dst the destination MAC address to be copied into the ethernet header
- * @return ERR_OK if the packet was sent, any other err_t on failure
- */
-static err_t
-ethip6_send(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst)
-{
- struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload;
-
- LWIP_ASSERT("netif->hwaddr_len must be 6 for ethip6!",
- (netif->hwaddr_len == 6));
- SMEMCPY(&ethhdr->dest, dst, 6);
- SMEMCPY(&ethhdr->src, src, 6);
- ethhdr->type = PP_HTONS(ETHTYPE_IPV6);
- LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("ethip6_send: sending packet %p\n", (void *)p));
- /* send the packet */
- return netif->linkoutput(netif, p);
-}
-
/**
* Resolve and fill-in Ethernet address header for outgoing IPv6 packet.
*
* For IPv6 multicast, corresponding Ethernet addresses
* are selected and the packet is transmitted on the link.
*
- * For unicast addresses, ...
+ * 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
+ * @todo anycast addresses
*
* @param netif The lwIP network interface which the IP packet will be sent on.
* @param q The pbuf(s) containing the IP packet to be sent.
* @param ip6addr The IP address of the packet destination.
*
* @return
- * - ERR_RTE No route to destination (no gateway to external networks),
- * or the return type of either etharp_query() or etharp_send_ip().
+ * - ERR_OK or the return value of @ref nd6_get_next_hop_addr_or_queue.
*/
err_t
-ethip6_output(struct netif *netif, struct pbuf *q, ip6_addr_t *ip6addr)
+ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr)
{
struct eth_addr dest;
- s8_t i;
+ const u8_t *hwaddr;
+ err_t result;
- /* make room for Ethernet header - should not fail */
- if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
- /* bail out */
- LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
- ("etharp_output: could not allocate room for header.\n"));
- return ERR_BUF;
- }
+ /* The destination IP address must be properly zoned from here on down. */
+ IP6_ADDR_ZONECHECK_NETIF(ip6addr, netif);
/* multicast destination IP address? */
if (ip6_addr_ismulticast(ip6addr)) {
/* Hash IP multicast address to MAC address.*/
dest.addr[0] = 0x33;
dest.addr[1] = 0x33;
- dest.addr[2] = ((u8_t *)(&(ip6addr->addr[3])))[0];
- dest.addr[3] = ((u8_t *)(&(ip6addr->addr[3])))[1];
- dest.addr[4] = ((u8_t *)(&(ip6addr->addr[3])))[2];
- dest.addr[5] = ((u8_t *)(&(ip6addr->addr[3])))[3];
+ 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 ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest);
+ return ethernet_output(netif, q, (const struct eth_addr*)(netif->hwaddr), &dest, ETHTYPE_IPV6);
}
/* We have a unicast destination IP address */
- /* TODO anycast? */
- /* Get next hop record. */
- i = nd6_get_next_hop_entry(ip6addr, netif);
- if (i < 0) {
- /* failed to get a next hop neighbor record. */
- return ERR_MEM;
- }
+ /* @todo anycast? */
- /* 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;
+ /* 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;
}
- /* TODO should we send or queue if PROBE? send for now, to let unicast NS pass. */
- if ((neighbor_cache[i].state == ND6_REACHABLE) ||
- (neighbor_cache[i].state == ND6_DELAY) ||
- (neighbor_cache[i].state == ND6_PROBE)) {
- /* Send out. */
- SMEMCPY(dest.addr, neighbor_cache[i].lladdr, 6);
- return ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest);
+ /* If no hardware address is returned, nd6 has queued the packet for later. */
+ if (hwaddr == NULL) {
+ return ERR_OK;
}
- /* We should queue packet on this interface. */
- pbuf_header(q, -(s16_t)SIZEOF_ETH_HDR);
- return nd6_queue_packet(i, q);
+ /* 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/lwip/src/core/ipv6/icmp6.c b/lwip/src/core/ipv6/icmp6.c
index ea82682..08db381 100644
--- a/lwip/src/core/ipv6/icmp6.c
+++ b/lwip/src/core/ipv6/icmp6.c
@@ -44,6 +44,7 @@
#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"
@@ -51,6 +52,7 @@
#include "lwip/netif.h"
#include "lwip/nd6.h"
#include "lwip/mld6.h"
+#include "lwip/ip.h"
#include "lwip/stats.h"
#include <string.h>
@@ -64,6 +66,10 @@
/* Forward declarations */
static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type);
+static void icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data,
+ u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr);
+static void icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data,
+ u8_t type, const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr, struct netif *netif);
/**
@@ -79,8 +85,8 @@ void
icmp6_input(struct pbuf *p, struct netif *inp)
{
struct icmp6_hdr *icmp6hdr;
- struct pbuf * r;
- ip6_addr_t * reply_src;
+ struct pbuf *r;
+ const ip6_addr_t *reply_src;
ICMP6_STATS_INC(icmp6.recv);
@@ -95,16 +101,18 @@ icmp6_input(struct pbuf *p, struct netif *inp)
icmp6hdr = (struct icmp6_hdr *)p->payload;
-#if LWIP_ICMP6_CHECKSUM_CHECK
- if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(),
- ip6_current_dest_addr()) != 0) {
- /* Checksum failed */
- pbuf_free(p);
- ICMP6_STATS_INC(icmp6.chkerr);
- ICMP6_STATS_INC(icmp6.drop);
- return;
+#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 /* LWIP_ICMP6_CHECKSUM_CHECK */
+#endif /* CHECKSUM_CHECK_ICMP6 */
switch (icmp6hdr->type) {
case ICMP6_TYPE_NA: /* Neighbor advertisement */
@@ -114,10 +122,9 @@ icmp6_input(struct pbuf *p, struct netif *inp)
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 */
+ /* @todo implement router functionality */
#endif
break;
#if LWIP_IPV6_MLD
@@ -126,7 +133,6 @@ icmp6_input(struct pbuf *p, struct netif *inp)
case ICMP6_TYPE_MLD:
mld6_input(p, inp);
return;
- break;
#endif
case ICMP6_TYPE_EREQ:
#if !LWIP_MULTICAST_PING
@@ -160,7 +166,7 @@ icmp6_input(struct pbuf *p, struct netif *inp)
/* Determine reply source IPv6 address. */
#if LWIP_MULTICAST_PING
if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
- reply_src = ip6_select_source_address(inp, ip6_current_src_addr());
+ reply_src = ip_2_ip6(ip6_select_source_address(inp, ip6_current_src_addr()));
if (reply_src == NULL) {
/* drop */
pbuf_free(p);
@@ -178,8 +184,12 @@ icmp6_input(struct pbuf *p, struct netif *inp)
/* Set fields in reply. */
((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP;
((struct icmp6_echo_hdr *)(r->payload))->chksum = 0;
- ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r,
- IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr());
+#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);
@@ -201,6 +211,9 @@ icmp6_input(struct pbuf *p, struct netif *inp)
/**
* Send an icmpv6 'destination unreachable' packet.
*
+ * This function must be used only in direct response to a packet that is being
+ * received right now. Otherwise, address zones would be lost.
+ *
* @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
@@ -214,6 +227,9 @@ icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c)
/**
* Send an icmpv6 'packet too big' packet.
*
+ * This function must be used only in direct response to a packet that is being
+ * received right now. Otherwise, address zones would be lost.
+ *
* @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
@@ -227,7 +243,10 @@ icmp6_packet_too_big(struct pbuf *p, u32_t mtu)
/**
* Send an icmpv6 'time exceeded' packet.
*
- * @param p the input packet for which the 'unreachable' should be sent,
+ * This function must be used only in direct response to a packet that is being
+ * received right now. Otherwise, address zones would be lost.
+ *
+ * @param p the input packet for which the 'time exceeded' should be sent,
* p->payload pointing to the IPv6 header
* @param c ICMPv6 code for the time exceeded type
*/
@@ -238,8 +257,33 @@ icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c)
}
/**
+ * Send an icmpv6 'time exceeded' packet, with explicit source and destination
+ * addresses.
+ *
+ * This function may be used to send a response sometime after receiving the
+ * packet for which this response is meant. The provided source and destination
+ * addresses are used primarily to retain their zone information.
+ *
+ * @param p the input packet for which the 'time exceeded' should be sent,
+ * p->payload pointing to the IPv6 header
+ * @param c ICMPv6 code for the time exceeded type
+ * @param src_addr source address of the original packet, with zone information
+ * @param dest_addr destination address of the original packet, with zone
+ * information
+ */
+void
+icmp6_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c,
+ const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr)
+{
+ icmp6_send_response_with_addrs(p, c, 0, ICMP6_TYPE_TE, src_addr, dest_addr);
+}
+
+/**
* Send an icmpv6 'parameter problem' packet.
*
+ * This function must be used only in direct response to a packet that is being
+ * received right now. Otherwise, address zones would be lost.
+ *
* @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
@@ -253,6 +297,7 @@ icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer)
/**
* Send an ICMPv6 packet in response to an incoming packet.
+ * The packet is sent *to* ip_current_src_addr() on ip_current_netif().
*
* @param p the input packet for which the response should be sent,
* p->payload pointing to the IPv6 header
@@ -263,12 +308,85 @@ icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer)
static void
icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
{
+ const struct ip6_addr *reply_src, *reply_dest;
+ struct netif *netif = ip_current_netif();
+
+ LWIP_ASSERT("icmpv6 packet not a direct response", netif != NULL);
+ 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) {
+ ICMP6_STATS_INC(icmp6.rterr);
+ return;
+ }
+ icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src, reply_dest, netif);
+}
+
+/**
+ * Send an ICMPv6 packet in response to an incoming packet.
+ *
+ * Call this function if the packet is NOT sent as a direct response to an
+ * incoming packet, but rather sometime later (e.g. for a fragment reassembly
+ * timeout). The caller must provide the zoned source and destination addresses
+ * from the original packet with the src_addr and dest_addr parameters. The
+ * reason for this approach is that while the addresses themselves are part of
+ * the original packet, their zone information is not, thus possibly resulting
+ * in a link-local response being sent over the wrong link.
+ *
+ * @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
+ * @param src_addr original source address
+ * @param dest_addr original destination address
+ */
+static void
+icmp6_send_response_with_addrs(struct pbuf *p, u8_t code, u32_t data, u8_t type,
+ const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr)
+{
+ const struct ip6_addr *reply_src, *reply_dest;
+ struct netif *netif;
+
+ /* Get the destination address and netif for this ICMP message. */
+ LWIP_ASSERT("must provide both source and destination", src_addr != NULL);
+ LWIP_ASSERT("must provide both source and destination", dest_addr != NULL);
+
+ /* Special case, as ip6_current_xxx is either NULL, or points
+ to a different packet than the one that expired. */
+ IP6_ADDR_ZONECHECK(src_addr);
+ IP6_ADDR_ZONECHECK(dest_addr);
+ /* Swap source and destination for the reply. */
+ reply_dest = src_addr;
+ reply_src = dest_addr;
+ netif = ip6_route(reply_src, reply_dest);
+ if (netif == NULL) {
+ ICMP6_STATS_INC(icmp6.rterr);
+ return;
+ }
+ icmp6_send_response_with_addrs_and_netif(p, code, data, type, reply_src,
+ reply_dest, netif);
+}
+
+/**
+ * Send an ICMPv6 packet (with srd/dst address and netif given).
+ *
+ * @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
+ * @param reply_src source address of the packet to send
+ * @param reply_dest destination address of the packet to send
+ * @param netif netif to send the packet
+ */
+static void
+icmp6_send_response_with_addrs_and_netif(struct pbuf *p, u8_t code, u32_t data, u8_t type,
+ const ip6_addr_t *reply_src, const ip6_addr_t *reply_dest, struct netif *netif)
+{
struct pbuf *q;
struct icmp6_hdr *icmp6hdr;
- ip6_addr_t *reply_src, *reply_dest;
- ip6_addr_t reply_src_local, reply_dest_local;
- struct ip6_hdr *ip6hdr;
- struct netif *netif;
/* ICMPv6 header + IPv6 header + data */
q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE,
@@ -284,50 +402,20 @@ icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
icmp6hdr = (struct icmp6_hdr *)q->payload;
icmp6hdr->type = type;
icmp6hdr->code = code;
- icmp6hdr->data = data;
+ icmp6hdr->data = lwip_htonl(data);
/* copy fields from original packet */
SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload,
IP6_HLEN + LWIP_ICMP6_DATASIZE);
- /* Get the destination address and netif for this ICMP message. */
- if ((ip_current_netif() == NULL) ||
- ((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) {
- /* Special case, as ip6_current_xxx is either NULL, or points
- * to a different packet than the one that expired.
- * We must use the addresses that are stored in the expired packet. */
- ip6hdr = (struct ip6_hdr *)p->payload;
- /* copy from packed address to aligned address */
- ip6_addr_copy(reply_dest_local, ip6hdr->src);
- ip6_addr_copy(reply_src_local, ip6hdr->dest);
- reply_dest = &reply_dest_local;
- reply_src = &reply_src_local;
- netif = ip6_route(reply_src, reply_dest);
- if (netif == NULL) {
- /* drop */
- pbuf_free(q);
- ICMP6_STATS_INC(icmp6.rterr);
- return;
- }
- }
- else {
- netif = ip_current_netif();
- reply_dest = ip6_current_src_addr();
-
- /* Select an address to use as source. */
- reply_src = ip6_select_source_address(netif, reply_dest);
- if (reply_src == NULL) {
- /* drop */
- pbuf_free(q);
- ICMP6_STATS_INC(icmp6.rterr);
- return;
- }
- }
-
/* calculate checksum */
icmp6hdr->chksum = 0;
- icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len,
- reply_src, reply_dest);
+#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);
diff --git a/lwip/src/core/ipv6/inet6.c b/lwip/src/core/ipv6/inet6.c
index bdf4ff4..d9a992c 100644
--- a/lwip/src/core/ipv6/inet6.c
+++ b/lwip/src/core/ipv6/inet6.c
@@ -6,9 +6,9 @@
/*
* Copyright (c) 2010 Inico Technologies Ltd.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -17,21 +17,21 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF 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>
*
*
@@ -44,8 +44,10 @@
#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
#include "lwip/def.h"
-#include "lwip/inet6.h"
+#include "lwip/inet.h"
-/** @see ip6_addr.c for implementation of functions. */
+/** 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/lwip/src/core/ipv6/ip6.c b/lwip/src/core/ipv6/ip6.c
index 1eb91f9..907f0aa 100644
--- a/lwip/src/core/ipv6/ip6.c
+++ b/lwip/src/core/ipv6/ip6.c
@@ -39,7 +39,6 @@
* <delamer@inicotech.com>
*/
-
#include "lwip/opt.h"
#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
@@ -47,91 +46,178 @@
#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/tcp_impl.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
+ * 2) if the destination is a zoned address, match its zone to a netif
+ * 3) if the either the source or destination address is a scoped address,
+ * match the source address's zone (if set) or address (if not) to a netif
+ * 4) tries to match the destination subnet to a configured address
+ * 5) tries to find a router-announced route
+ * 6) tries to match the (unscoped) source address to the netif
+ * 7) returns the default netif, if configured
+ *
+ * Note that each of the two given addresses may or may not be properly zoned.
*
* @param src the source IPv6 address, if known
* @param dest the destination IPv6 address for which to find the route
* @return the netif on which to send to reach dest
*/
struct netif *
-ip6_route(struct ip6_addr *src, struct ip6_addr *dest)
+ip6_route(const ip6_addr_t *src, const ip6_addr_t *dest)
{
+#if LWIP_SINGLE_NETIF
+ LWIP_UNUSED_ARG(src);
+ LWIP_UNUSED_ARG(dest);
+#else /* LWIP_SINGLE_NETIF */
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) ||
+ (ip6_addr_has_zone(dest) && !ip6_addr_test_zone(dest, 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. */
- return netif_default;
+#if LWIP_IPV6_SCOPES
+ /* Special processing for zoned destination addresses. This includes link-
+ * local unicast addresses and interface/link-local multicast addresses. Use
+ * the zone to find a matching netif. If the address is not zoned, then there
+ * is technically no "wrong" netif to choose, and we leave routing to other
+ * rules; in most cases this should be the scoped-source rule below. */
+ if (ip6_addr_has_zone(dest)) {
+ IP6_ADDR_ZONECHECK(dest);
+ /* Find a netif based on the zone. For custom mappings, one zone may map
+ * to multiple netifs, so find one that can actually send a packet. */
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ if (ip6_addr_test_zone(dest, netif) &&
+ netif_is_up(netif) && netif_is_link_up(netif)) {
+ return netif;
+ }
}
-
- /* Try to find the netif for the source address. */
- for(netif = netif_list; netif != NULL; netif = netif->next) {
- for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
- ip6_addr_cmp(src, netif_ip6_addr(netif, i))) {
+ /* No matching netif found. Do no try to route to a different netif,
+ * as that would be a zone violation, resulting in any packets sent to
+ * that netif being dropped on output. */
+ return NULL;
+ }
+#endif /* LWIP_IPV6_SCOPES */
+
+ /* Special processing for scoped source and destination addresses. If we get
+ * here, the destination address does not have a zone, so either way we need
+ * to look at the source address, which may or may not have a zone. If it
+ * does, the zone is restrictive: there is (typically) only one matching
+ * netif for it, and we should avoid routing to any other netif as that would
+ * result in guaranteed zone violations. For scoped source addresses that do
+ * not have a zone, use (only) a netif that has that source address locally
+ * assigned. This case also applies to the loopback source address, which has
+ * an implied link-local scope. If only the destination address is scoped
+ * (but, again, not zoned), we still want to use only the source address to
+ * determine its zone because that's most likely what the user/application
+ * wants, regardless of whether the source address is scoped. Finally, some
+ * of this story also applies if scoping is disabled altogether. */
+#if LWIP_IPV6_SCOPES
+ if (ip6_addr_has_scope(dest, IP6_UNKNOWN) ||
+ ip6_addr_has_scope(src, IP6_UNICAST) ||
+#else /* LWIP_IPV6_SCOPES */
+ if (ip6_addr_islinklocal(dest) || ip6_addr_ismulticast_iflocal(dest) ||
+ ip6_addr_ismulticast_linklocal(dest) || ip6_addr_islinklocal(src) ||
+#endif /* LWIP_IPV6_SCOPES */
+ ip6_addr_isloopback(src)) {
+#if LWIP_IPV6_SCOPES
+ if (ip6_addr_has_zone(src)) {
+ /* Find a netif matching the source zone (relatively cheap). */
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ if (netif_is_up(netif) && netif_is_link_up(netif) &&
+ ip6_addr_test_zone(src, netif)) {
return netif;
}
}
+ } else
+#endif /* LWIP_IPV6_SCOPES */
+ {
+ /* Find a netif matching the source address (relatively expensive). */
+ 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_zoneless(src, netif_ip6_addr(netif, i))) {
+ return netif;
+ }
+ }
+ }
}
-
- /* netif not found, use default netif */
- return netif_default;
+ /* Again, do not use any other netif in this case, as that could result in
+ * zone boundary violations. */
+ return NULL;
}
- /* See if the destination subnet matches a configured address. */
- for(netif = netif_list; netif != NULL; netif = netif->next) {
+ /* We come here only if neither source nor destination is scoped. */
+ IP6_ADDR_ZONECHECK(src);
+
+#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. In accordance
+ * with RFC 5942, dynamically configured addresses do not have an implied
+ * local subnet, and thus should be considered /128 assignments. However, as
+ * such, the destination address may still match a local address, and so we
+ * still need to check for exact matches here. By (lwIP) policy, statically
+ * configured addresses do always have an implied local /64 subnet. */
+ 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))) {
+ ip6_addr_netcmp(dest, netif_ip6_addr(netif, i)) &&
+ (netif_ip6_addr_isstatic(netif, i) ||
+ ip6_addr_nethostcmp(dest, netif_ip6_addr(netif, i)))) {
return netif;
}
}
}
- /* Get the netif for a suitable router. */
- i = nd6_select_router(dest, NULL);
- if (i >= 0) {
- if (default_router_list[i].neighbor_entry != NULL) {
- if (default_router_list[i].neighbor_entry->netif != NULL) {
- return default_router_list[i].neighbor_entry->netif;
- }
- }
+ /* Get the netif for a suitable router-announced route. */
+ netif = nd6_find_route(dest);
+ if (netif != NULL) {
+ return netif;
}
- /* try with the netif that matches the source address. */
+ /* Try with the netif that matches the source address. Given the earlier rule
+ * for scoped source addresses, this applies to unscoped addresses only. */
if (!ip6_addr_isany(src)) {
- for(netif = netif_list; netif != NULL; netif = netif->next) {
+ 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))) {
@@ -141,90 +227,127 @@ ip6_route(struct ip6_addr *src, struct ip6_addr *dest)
}
}
- /* no matching netif found, use default 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 */
+#endif /* !LWIP_SINGLE_NETIF */
+
+ /* 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;
}
/**
- * Select the best IPv6 source address for a given destination
- * IPv6 address. Loosely follows RFC 3484. "Strong host" behavior
- * is assumed.
+ * @ingroup ip6
+ * Select the best IPv6 source address for a given destination IPv6 address.
+ *
+ * This implementation follows RFC 6724 Sec. 5 to the following extent:
+ * - Rules 1, 2, 3: fully implemented
+ * - Rules 4, 5, 5.5: not applicable
+ * - Rule 6: not implemented
+ * - Rule 7: not applicable
+ * - Rule 8: limited to "prefer /64 subnet match over non-match"
+ *
+ * For Rule 2, we deliberately deviate from RFC 6724 Sec. 3.1 by considering
+ * ULAs to be of smaller scope than global addresses, to avoid that a preferred
+ * ULA is picked over a deprecated global address when given a global address
+ * as destination, as that would likely result in broken two-way communication.
+ *
+ * As long as temporary addresses are not supported (as used in Rule 7), a
+ * proper implementation of Rule 8 would obviate the need to implement Rule 6.
*
* @param netif the netif on which to send a packet
- * @param dest the destination we are trying to reach
+ * @param dest the destination we are trying to reach (possibly not properly
+ * zoned)
* @return the most suitable source address to use, or NULL if no suitable
* source address is found
*/
-ip6_addr_t *
-ip6_select_source_address(struct netif *netif, ip6_addr_t * dest)
+const ip_addr_t *
+ip6_select_source_address(struct netif *netif, const ip6_addr_t *dest)
{
- ip6_addr_t * src = NULL;
- u8_t i;
-
- /* If dest is link-local, choose a link-local source. */
- if (ip6_addr_islinklocal(dest) || ip6_addr_ismulticast_linklocal(dest) || ip6_addr_ismulticast_iflocal(dest)) {
- for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
- ip6_addr_islinklocal(netif_ip6_addr(netif, i))) {
- return netif_ip6_addr(netif, i);
- }
- }
+ const ip_addr_t *best_addr;
+ const ip6_addr_t *cand_addr;
+ s8_t dest_scope, cand_scope;
+ s8_t best_scope = IP6_MULTICAST_SCOPE_RESERVED;
+ u8_t i, cand_pref, cand_bits;
+ u8_t best_pref = 0;
+ u8_t best_bits = 0;
+
+ /* Start by determining the scope of the given destination address. These
+ * tests are hopefully (roughly) in order of likeliness to match. */
+ if (ip6_addr_isglobal(dest)) {
+ dest_scope = IP6_MULTICAST_SCOPE_GLOBAL;
+ } else if (ip6_addr_islinklocal(dest) || ip6_addr_isloopback(dest)) {
+ dest_scope = IP6_MULTICAST_SCOPE_LINK_LOCAL;
+ } else if (ip6_addr_isuniquelocal(dest)) {
+ dest_scope = IP6_MULTICAST_SCOPE_ORGANIZATION_LOCAL;
+ } else if (ip6_addr_ismulticast(dest)) {
+ dest_scope = ip6_addr_multicast_scope(dest);
+ } else if (ip6_addr_issitelocal(dest)) {
+ dest_scope = IP6_MULTICAST_SCOPE_SITE_LOCAL;
+ } else {
+ /* no match, consider scope global */
+ dest_scope = IP6_MULTICAST_SCOPE_GLOBAL;
}
- /* Choose a site-local with matching prefix. */
- if (ip6_addr_issitelocal(dest) || ip6_addr_ismulticast_sitelocal(dest)) {
- for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
- ip6_addr_issitelocal(netif_ip6_addr(netif, i)) &&
- ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
- return netif_ip6_addr(netif, i);
- }
- }
- }
+ best_addr = NULL;
- /* Choose a unique-local with matching prefix. */
- if (ip6_addr_isuniquelocal(dest) || ip6_addr_ismulticast_orglocal(dest)) {
- for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
- ip6_addr_isuniquelocal(netif_ip6_addr(netif, i)) &&
- ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
- return netif_ip6_addr(netif, i);
- }
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ /* Consider only valid (= preferred and deprecated) addresses. */
+ if (!ip6_addr_isvalid(netif_ip6_addr_state(netif, i))) {
+ continue;
}
- }
-
- /* Choose a global with best matching prefix. */
- if (ip6_addr_isglobal(dest) || ip6_addr_ismulticast_global(dest)) {
- for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
- ip6_addr_isglobal(netif_ip6_addr(netif, i))) {
- if (src == NULL) {
- src = netif_ip6_addr(netif, i);
- }
- else {
- /* Replace src only if we find a prefix match. */
- /* TODO find longest matching prefix. */
- if ((!(ip6_addr_netcmp(src, dest))) &&
- ip6_addr_netcmp(netif_ip6_addr(netif, i), dest)) {
- src = netif_ip6_addr(netif, i);
- }
- }
- }
+ /* Determine the scope of this candidate address. Same ordering idea. */
+ cand_addr = netif_ip6_addr(netif, i);
+ if (ip6_addr_isglobal(cand_addr)) {
+ cand_scope = IP6_MULTICAST_SCOPE_GLOBAL;
+ } else if (ip6_addr_islinklocal(cand_addr)) {
+ cand_scope = IP6_MULTICAST_SCOPE_LINK_LOCAL;
+ } else if (ip6_addr_isuniquelocal(cand_addr)) {
+ cand_scope = IP6_MULTICAST_SCOPE_ORGANIZATION_LOCAL;
+ } else if (ip6_addr_issitelocal(cand_addr)) {
+ cand_scope = IP6_MULTICAST_SCOPE_SITE_LOCAL;
+ } else {
+ /* no match, treat as low-priority global scope */
+ cand_scope = IP6_MULTICAST_SCOPE_RESERVEDF;
}
- if (src != NULL) {
- return src;
+ cand_pref = ip6_addr_ispreferred(netif_ip6_addr_state(netif, i));
+ /* @todo compute the actual common bits, for longest matching prefix. */
+ /* We cannot count on the destination address having a proper zone
+ * assignment, so do not compare zones in this case. */
+ cand_bits = ip6_addr_netcmp_zoneless(cand_addr, dest); /* just 1 or 0 for now */
+ if (cand_bits && ip6_addr_nethostcmp(cand_addr, dest)) {
+ return netif_ip_addr6(netif, i); /* Rule 1 */
}
- }
-
- /* Last resort: see if arbitrary prefix matches. */
- for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
- ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
- return netif_ip6_addr(netif, i);
+ if ((best_addr == NULL) || /* no alternative yet */
+ ((cand_scope < best_scope) && (cand_scope >= dest_scope)) ||
+ ((cand_scope > best_scope) && (best_scope < dest_scope)) || /* Rule 2 */
+ ((cand_scope == best_scope) && ((cand_pref > best_pref) || /* Rule 3 */
+ ((cand_pref == best_pref) && (cand_bits > best_bits))))) { /* Rule 8 */
+ /* We found a new "winning" candidate. */
+ best_addr = netif_ip_addr6(netif, i);
+ best_scope = cand_scope;
+ best_pref = cand_pref;
+ best_bits = cand_bits;
}
}
- return NULL;
+ return best_addr; /* may be NULL */
}
#if LWIP_IPV6_FORWARD
@@ -242,8 +365,9 @@ ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp)
{
struct netif *netif;
- /* do not forward link-local addresses */
- if (ip6_addr_islinklocal(ip6_current_dest_addr())) {
+ /* 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);
@@ -251,7 +375,7 @@ ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp)
}
/* Find network interface where to forward this IP packet to. */
- netif = ip6_route(IP6_ADDR_ANY, ip6_current_dest_addr());
+ 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()),
@@ -272,6 +396,20 @@ ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp)
IP6_STATS_INC(ip6.drop);
return;
}
+#if LWIP_IPV6_SCOPES
+ /* Do not forward packets with a zoned (e.g., link-local) source address
+ * outside of their zone. We determined the zone a bit earlier, so we know
+ * that the address is properly zoned here, so we can safely use has_zone.
+ * Also skip packets with a loopback source address (link-local implied). */
+ if ((ip6_addr_has_zone(ip6_current_src_addr()) &&
+ !ip6_addr_test_zone(ip6_current_src_addr(), netif)) ||
+ ip6_addr_isloopback(ip6_current_src_addr())) {
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding packet beyond its source address zone.\n"));
+ IP6_STATS_INC(ip6.rterr);
+ IP6_STATS_INC(ip6.drop);
+ return;
+ }
+#endif /* LWIP_IPV6_SCOPES */
/* Do not forward packets onto the same network interface on which
* they arrived. */
if (netif == inp) {
@@ -324,6 +462,32 @@ ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp)
}
#endif /* LWIP_IPV6_FORWARD */
+/** Return true if the current input packet should be accepted on this netif */
+static int
+ip6_input_accept(struct netif *netif)
+{
+ /* interface is up? */
+ if (netif_is_up(netif)) {
+ u8_t i;
+ /* unicast to this interface address? address configured? */
+ /* If custom scopes are used, the destination zone will be tested as
+ * part of the local-address comparison, but we need to test the source
+ * scope as well (e.g., is this interface on the same link?). */
+ 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))
+#if IPV6_CUSTOM_SCOPES
+ && (!ip6_addr_has_zone(ip6_current_src_addr()) ||
+ ip6_addr_test_zone(ip6_current_src_addr(), netif))
+#endif /* IPV6_CUSTOM_SCOPES */
+ ) {
+ /* accept on this netif */
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
/**
* This function is called by the network interface device driver when
@@ -345,8 +509,7 @@ 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;
+ u16_t hlen, hlen_tot; /* the current header length */
#if 0 /*IP_ACCEPT_LINK_LAYER_ADDRESSING*/
@todo
int check_ip_src=1;
@@ -365,17 +528,24 @@ ip6_input(struct pbuf *p, struct netif *inp)
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) || (IP6H_PLEN(ip6hdr) > (p->tot_len - IP6_HLEN))) {
if (IP6_HLEN > p->len) {
LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
("IPv6 header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n",
- IP6_HLEN, p->len));
+ (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",
- IP6H_PLEN(ip6hdr) + IP6_HLEN, p->tot_len));
+ (u16_t)(IP6H_PLEN(ip6hdr) + IP6_HLEN), p->tot_len));
}
/* free (drop) packet pbufs */
pbuf_free(p);
@@ -386,17 +556,32 @@ ip6_input(struct pbuf *p, struct netif *inp)
/* 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));
+ pbuf_realloc(p, (u16_t)(IP6_HLEN + IP6H_PLEN(ip6hdr)));
/* copy IP addresses to aligned ip6_addr_t */
- ip6_addr_copy(ip_data.current_iphdr_dest.ip6, ip6hdr->dest);
- ip6_addr_copy(ip_data.current_iphdr_src.ip6, ip6hdr->src);
+ ip_addr_copy_from_ip6_packed(ip_data.current_iphdr_dest, ip6hdr->dest);
+ ip_addr_copy_from_ip6_packed(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;
+ }
+
+ /* Set the appropriate zone identifier on the addresses. */
+ ip6_addr_assign_zone(ip_2_ip6(&ip_data.current_iphdr_dest), IP6_UNKNOWN, inp);
+ ip6_addr_assign_zone(ip_2_ip6(&ip_data.current_iphdr_src), IP6_UNICAST, inp);
/* 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())) {
@@ -411,6 +596,7 @@ ip6_input(struct pbuf *p, struct netif *inp)
}
#else /* LWIP_IPV6_MLD */
else if (ip6_addr_issolicitednode(ip6_current_dest_addr())) {
+ u8_t i;
/* Filter solicited node packets when MLD is not enabled
* (for Neighbor discovery). */
netif = NULL;
@@ -428,40 +614,46 @@ ip6_input(struct pbuf *p, struct netif *inp)
else {
netif = NULL;
}
- }
- else {
+ } else {
/* start trying with inp. if that's not acceptable, start walking the
- list of configured netifs.
- 'first' is used as a boolean to mark whether we started walking the list */
- int first = 1;
- netif = inp;
- do {
- /* interface is up? */
- if (netif_is_up(netif)) {
- /* unicast to this interface address? address configured? */
- for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
- ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(netif, i))) {
- /* exit outer loop */
- goto netif_found;
- }
- }
- }
- if (ip6_addr_islinklocal(ip6_current_dest_addr())) {
- /* Do not match link-local addresses to other netifs. */
- netif = NULL;
- break;
+ list of configured netifs. */
+ if (ip6_input_accept(inp)) {
+ netif = inp;
+ } else {
+ netif = NULL;
+#if !IPV6_CUSTOM_SCOPES
+ /* Shortcut: stop looking for other interfaces if either the source or
+ * the destination has a scope constrained to this interface. Custom
+ * scopes may break the 1:1 link/interface mapping, however. */
+ if (ip6_addr_islinklocal(ip6_current_dest_addr()) ||
+ ip6_addr_islinklocal(ip6_current_src_addr())) {
+ goto netif_found;
}
- if (first) {
- first = 0;
- netif = netif_list;
- } else {
- netif = netif->next;
+#endif /* !IPV6_CUSTOM_SCOPES */
+#if !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF
+ /* The loopback address is to be considered link-local. Packets to it
+ * should be dropped on other interfaces, as per RFC 4291 Sec. 2.5.3.
+ * Its implied scope means packets *from* the loopback address should
+ * not be accepted on other interfaces, either. These requirements
+ * cannot be implemented in the case that loopback traffic is sent
+ * across a non-loopback interface, however. */
+ if (ip6_addr_isloopback(ip6_current_dest_addr()) ||
+ ip6_addr_isloopback(ip6_current_src_addr())) {
+ goto netif_found;
}
- if (netif == inp) {
- netif = netif->next;
+#endif /* !LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF */
+#if !LWIP_SINGLE_NETIF
+ NETIF_FOREACH(netif) {
+ if (netif == inp) {
+ /* we checked that before already */
+ continue;
+ }
+ if (ip6_input_accept(netif)) {
+ break;
+ }
}
- } while(netif != NULL);
+#endif /* !LWIP_SINGLE_NETIF */
+ }
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'));
@@ -477,7 +669,7 @@ netif_found:
IP6_STATS_INC(ip6.drop);
goto ip6_input_cleanup;
}
-
+
/* if we're pretending we are everyone for TCP, assume the packet is for source interface if it
isn't for a local address */
if (netif == NULL && (inp->flags & NETIF_FLAG_PRETEND_TCP) && IP6H_NEXTH(ip6hdr) == IP6_NEXTH_TCP) {
@@ -506,10 +698,10 @@ netif_found:
nexth = IP6H_NEXTH(ip6hdr);
/* Init header length. */
- hlen = ip_data.current_ip_header_tot_len = IP6_HLEN;
+ hlen = hlen_tot = IP6_HLEN;
/* Move to payload. */
- pbuf_header(p, -IP6_HLEN);
+ pbuf_remove_header(p, IP6_HLEN);
/* Process known option extension headers, if present. */
while (nexth != IP6_NEXTH_NONE)
@@ -517,15 +709,9 @@ netif_found:
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) {
+ /* Get and check the header length, while staying in packet bounds. */
+ hlen = (u16_t)(8 * (1 + *((u8_t *)p->payload + 1)));
+ if ((p->len < 8) || (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));
@@ -536,19 +722,20 @@ netif_found:
goto ip6_input_cleanup;
}
- pbuf_header(p, -hlen);
- break;
- case IP6_NEXTH_DESTOPTS:
- LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n"));
+ hlen_tot = (u16_t)(hlen_tot + hlen);
+
/* 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) {
+ pbuf_remove_header(p, hlen);
+ break;
+ case IP6_NEXTH_DESTOPTS:
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n"));
+
+ /* Get and check the header length, while staying in packet bounds. */
+ hlen = (u16_t)(8 * (1 + *((u8_t *)p->payload + 1)));
+ if ((p->len < 8) || (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));
@@ -559,19 +746,20 @@ netif_found:
goto ip6_input_cleanup;
}
- pbuf_header(p, -hlen);
- break;
- case IP6_NEXTH_ROUTING:
- LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n"));
+ hlen_tot = (u16_t)(hlen_tot + hlen);
+
/* 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) {
+ pbuf_remove_header(p, hlen);
+ break;
+ case IP6_NEXTH_ROUTING:
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n"));
+
+ /* Get and check the header length, while staying in packet bounds. */
+ hlen = (u16_t)(8 * (1 + *((u8_t *)p->payload + 1)));
+ if ((p->len < 8) || (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));
@@ -582,22 +770,22 @@ netif_found:
goto ip6_input_cleanup;
}
- pbuf_header(p, -hlen);
+ /* Get next header type. */
+ nexth = *((u8_t *)p->payload);
+
+ /* Skip over this header. */
+ hlen_tot = (u16_t)(hlen_tot + hlen);
+
+ pbuf_remove_header(p, hlen);
break;
case IP6_NEXTH_FRAGMENT:
{
- struct ip6_frag_hdr * frag_hdr;
+ 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) {
@@ -611,18 +799,23 @@ netif_found:
goto ip6_input_cleanup;
}
- /* Offset == 0 and more_fragments == 0? */
- if (((frag_hdr->_fragment_offset & IP6_FRAG_OFFSET_MASK) == 0) &&
- ((frag_hdr->_fragment_offset & IP6_FRAG_MORE_FLAG) == 0)) {
+ hlen_tot = (u16_t)(hlen_tot + hlen);
- /* This is a 1-fragment packet, usually a packet that we have
- * already reassembled. Skip this header anc continue. */
- pbuf_header(p, -hlen);
- }
- else {
+ frag_hdr = (struct ip6_frag_hdr *)p->payload;
+
+ /* Get next header type. */
+ nexth = frag_hdr->_nexth;
+
+ /* 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. Skip this header and continue. */
+ pbuf_remove_header(p, hlen);
+ } else {
#if LWIP_IPV6_REASS
/* reassemble the packet */
+ ip_data.current_ip_header_tot_len = hlen_tot;
p = ip6_reass(p);
/* packet not fully reassembled yet? */
if (p == NULL) {
@@ -633,8 +826,8 @@ netif_found:
* 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);
+ hlen = hlen_tot = IP6_HLEN;
+ pbuf_remove_header(p, IP6_HLEN);
#else /* LWIP_IPV6_REASS */
/* free (drop) packet pbufs */
@@ -649,24 +842,37 @@ netif_found:
}
default:
goto options_done;
- break;
}
}
options_done:
- /* p points to IPv6 header again. */
- pbuf_header(p, ip_data.current_ip_header_tot_len);
+ if (hlen_tot >= 0x8000) {
+ /* s16_t overflow */
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: header length overflow: %"U16_F"\n", hlen_tot));
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.proterr);
+ IP6_STATS_INC(ip6.drop);
+ goto options_done;
+ }
/* 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));
+ ip_data.current_ip_header_tot_len = hlen_tot;
+
#if LWIP_RAW
+ /* p points to IPv6 header again for raw_input. */
+ pbuf_header_force(p, (s16_t)hlen_tot);
/* raw input did not eat the packet? */
if (raw_input(p, inp) == 0)
-#endif /* LWIP_RAW */
{
+ /* Point to payload. */
+ pbuf_remove_header(p, hlen_tot);
+#else /* LWIP_RAW */
+ {
+#endif /* LWIP_RAW */
switch (nexth) {
case IP6_NEXTH_NONE:
pbuf_free(p);
@@ -676,34 +882,30 @@ options_done:
#if LWIP_UDPLITE
case IP6_NEXTH_UDPLITE:
#endif /* LWIP_UDPLITE */
- /* Point to payload. */
- pbuf_header(p, -ip_data.current_ip_header_tot_len);
udp_input(p, inp);
break;
#endif /* LWIP_UDP */
#if LWIP_TCP
case IP6_NEXTH_TCP:
- /* Point to payload. */
- pbuf_header(p, -ip_data.current_ip_header_tot_len);
tcp_input(p, inp);
break;
#endif /* LWIP_TCP */
#if LWIP_ICMP6
case IP6_NEXTH_ICMP6:
- /* Point to payload. */
- pbuf_header(p, -ip_data.current_ip_header_tot_len);
icmp6_input(p, inp);
break;
#endif /* LWIP_ICMP */
default:
#if LWIP_ICMP6
+ /* p points to IPv6 header again for raw_input. */
+ pbuf_header_force(p, (s16_t)hlen_tot);
/* 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);
+ icmp6_param_problem(p, ICMP6_PP_HEADER, (u32_t)(hlen_tot - hlen));
}
#endif /* LWIP_ICMP */
- LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", IP6H_NEXTH(ip6hdr)));
+ 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);
@@ -713,10 +915,11 @@ options_done:
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_any(&ip_data.current_iphdr_src.ip6);
- ip6_addr_set_any(&ip_data.current_iphdr_dest.ip6);
+ ip6_addr_set_zero(ip6_current_src_addr());
+ ip6_addr_set_zero(ip6_current_dest_addr());
return ERR_OK;
}
@@ -728,16 +931,18 @@ ip6_input_cleanup:
* used as source (usually during network startup). If the source IPv6 address it
* IP6_ADDR_ANY, the most appropriate IPv6 address of the outgoing network
* interface is filled in as source address. If the destination IPv6 address is
- * IP_HDRINCL, p is assumed to already include an IPv6 header and p->payload points
- * to it instead of the data.
+ * 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 == IP_HDRINCL, p already includes an
+ 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
+ * if src == NULL, IP6_ADDR_ANY is used as source) (src is possibly not
+ * properly zoned)
+ * @param dest the destination IPv6 address to send the packet to (possibly not
+ * properly zoned)
* @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
@@ -747,21 +952,57 @@ ip6_input_cleanup:
* returns errors returned by netif->output
*/
err_t
-ip6_output_if(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest,
+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;
- /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
- gets altered as the packet is passed down the stack */
- LWIP_ASSERT("p->ref == 1", p->ref == 1);
+ LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
/* Should the IPv6 header be generated or is it already included in p? */
- if (dest != IP_HDRINCL) {
+ if (dest != LWIP_IP_HDRINCL) {
+#if LWIP_IPV6_SCOPES
+ /* If the destination address is scoped but lacks a zone, add a zone now,
+ * based on the outgoing interface. The lower layers (e.g., nd6) absolutely
+ * require addresses to be properly zoned for correctness. In some cases,
+ * earlier attempts will have been made to add a zone to the destination,
+ * but this function is the only one that is called in all (other) cases,
+ * so we must do this here. */
+ if (ip6_addr_lacks_zone(dest, IP6_UNKNOWN)) {
+ ip6_addr_copy(dest_addr, *dest);
+ ip6_addr_assign_zone(&dest_addr, IP6_UNKNOWN, netif);
+ dest = &dest_addr;
+ }
+#endif /* LWIP_IPV6_SCOPES */
+
/* generate IPv6 header */
- if (pbuf_header(p, IP6_HLEN)) {
+ if (pbuf_add_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;
@@ -775,43 +1016,52 @@ ip6_output_if(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest,
IP6H_NEXTH_SET(ip6hdr, nexth);
/* dest cannot be NULL here */
- ip6_addr_copy(ip6hdr->dest, *dest);
+ ip6_addr_copy_to_packed(ip6hdr->dest, *dest);
IP6H_VTCFL_SET(ip6hdr, 6, tc, 0);
- IP6H_PLEN_SET(ip6hdr, p->tot_len - IP6_HLEN);
+ IP6H_PLEN_SET(ip6hdr, (u16_t)(p->tot_len - IP6_HLEN));
if (src == NULL) {
- src = IP6_ADDR_ANY;
- }
- else if (ip6_addr_isany(src)) {
- src = ip6_select_source_address(netif, dest);
- if ((src == NULL) || ip6_addr_isany(src)) {
- /* No appropriate source address was found for this packet. */
- LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: No suitable source address for packet.\n"));
- IP6_STATS_INC(ip6.rterr);
- return ERR_RTE;
- }
+ src = IP6_ADDR_ANY6;
}
/* src cannot be NULL here */
- ip6_addr_copy(ip6hdr->src, *src);
+ ip6_addr_copy_to_packed(ip6hdr->src, *src);
} else {
/* IP header already included in p */
ip6hdr = (struct ip6_hdr *)p->payload;
- ip6_addr_copy(dest_addr, ip6hdr->dest);
+ ip6_addr_copy_from_packed(dest_addr, ip6hdr->dest);
+ ip6_addr_assign_zone(&dest_addr, IP6_UNKNOWN, netif);
dest = &dest_addr;
}
IP6_STATS_INC(ip6.xmit);
- LWIP_DEBUGF(IP6_DEBUG, ("ip6_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num));
+ 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
- /* TODO implement loopback for v6
- if (ip6_addr_cmp(dest, netif_ip6_addr(0))) {
- return netif_loop_output(netif, p, dest);
- }*/
+ {
+ 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);
+ }
+ }
+ }
+#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 LWIP_IPV6_FRAG
/* don't fragment if interface has mtu set to 0 [loopif] */
@@ -820,7 +1070,7 @@ ip6_output_if(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest,
}
#endif /* LWIP_IPV6_FRAG */
- LWIP_DEBUGF(IP6_DEBUG, ("netif->output_ip6()"));
+ LWIP_DEBUGF(IP6_DEBUG, ("netif->output_ip6()\n"));
return netif->output_ip6(netif, p, dest);
}
@@ -829,7 +1079,7 @@ ip6_output_if(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest,
* interface and calls upon ip6_output_if to do the actual work.
*
* @param p the packet to send (p->payload points to the data, e.g. next
- protocol header; if dest == IP_HDRINCL, p already includes an
+ 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.
@@ -843,24 +1093,22 @@ ip6_output_if(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest,
* see ip_output_if() for more return values
*/
err_t
-ip6_output(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest,
+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;
- /* pbufs passed to IPv6 must have a ref-count of 1 as their payload pointer
- gets altered as the packet is passed down the stack */
- LWIP_ASSERT("p->ref == 1", p->ref == 1);
+ LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
- if (dest != IP_HDRINCL) {
+ 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);
+ ip6_addr_copy_from_packed(src_addr, ip6hdr->src);
+ ip6_addr_copy_from_packed(dest_addr, ip6hdr->dest);
netif = ip6_route(&src_addr, &dest_addr);
}
@@ -882,12 +1130,12 @@ ip6_output(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest,
}
-#if LWIP_NETIF_HWADDRHINT
+#if LWIP_NETIF_USE_HINTS
/** Like ip6_output, but takes and addr_hint pointer that is passed on to netif->addr_hint
* before calling ip6_output_if.
*
* @param p the packet to send (p->payload points to the data, e.g. next
- protocol header; if dest == IP_HDRINCL, p already includes an
+ 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.
@@ -896,32 +1144,30 @@ ip6_output(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest,
* @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
+ * @param netif_hint netif output hint pointer set to netif->hint before
* calling ip_output_if()
*
* @return ERR_RTE if no route is found
* see ip_output_if() for more return values
*/
err_t
-ip6_output_hinted(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest,
- u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint)
+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, struct netif_hint *netif_hint)
{
struct netif *netif;
struct ip6_hdr *ip6hdr;
ip6_addr_t src_addr, dest_addr;
err_t err;
- /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
- gets altered as the packet is passed down the stack */
- LWIP_ASSERT("p->ref == 1", p->ref == 1);
+ LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p);
- if (dest != IP_HDRINCL) {
+ 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);
+ ip6_addr_copy_from_packed(src_addr, ip6hdr->src);
+ ip6_addr_copy_from_packed(dest_addr, ip6hdr->dest);
netif = ip6_route(&src_addr, &dest_addr);
}
@@ -939,13 +1185,13 @@ ip6_output_hinted(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest,
return ERR_RTE;
}
- NETIF_SET_HWADDRHINT(netif, addr_hint);
+ NETIF_SET_HINTS(netif, netif_hint);
err = ip6_output_if(p, src, dest, hl, tc, nexth, netif);
- NETIF_SET_HWADDRHINT(netif, NULL);
+ NETIF_RESET_HINTS(netif);
return err;
}
-#endif /* LWIP_NETIF_HWADDRHINT*/
+#endif /* LWIP_NETIF_USE_HINTS*/
#if LWIP_IPV6_MLD
/**
@@ -959,12 +1205,12 @@ ip6_output_hinted(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest,
* @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)
+ip6_options_add_hbh_ra(struct pbuf *p, u8_t nexth, u8_t value)
{
- struct ip6_hbh_hdr * hbh_hdr;
+ 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))) {
+ if (pbuf_add_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;
diff --git a/lwip/src/core/ipv6/ip6_addr.c b/lwip/src/core/ipv6/ip6_addr.c
index 65d2798..56a21ce 100644
--- a/lwip/src/core/ipv6/ip6_addr.c
+++ b/lwip/src/core/ipv6/ip6_addr.c
@@ -6,9 +6,9 @@
/*
* Copyright (c) 2010 Inico Technologies Ltd.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -17,21 +17,21 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Ivan Delamer <delamer@inicotech.com>
*
* Functions for handling IPv6 addresses.
@@ -44,11 +44,11 @@
#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
-#include "lwip/ip6_addr.h"
+#include "lwip/ip_addr.h"
#include "lwip/def.h"
-/* used by IP6_ADDR_ANY in ip6_addr.h */
-const ip6_addr_t ip6_addr_any = { { 0ul, 0ul, 0ul, 0ul } };
+/* 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)
@@ -57,7 +57,7 @@ const ip6_addr_t ip6_addr_any = { { 0ul, 0ul, 0ul, 0ul } };
#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)
+#define xchar(i) ((char)((i) < 10 ? '0' + (i) : 'A' + (i) - 10))
#endif
/**
@@ -65,7 +65,7 @@ const ip6_addr_t ip6_addr_any = { { 0ul, 0ul, 0ul, 0ul } };
* of an IPv6 address and convert to a binary address.
* Returns 1 if the address is valid, 0 if not.
*
- * @param cp IPv6 address in ascii represenation (e.g. "FF01::1")
+ * @param 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
*/
@@ -73,16 +73,17 @@ 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;
+ 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 == ':')
+ if (*s == ':') {
zero_blocks--;
- else if (!isxdigit(*s))
+ } else if (!isxdigit(*s)) {
break;
+ }
}
/* parse each block */
@@ -104,26 +105,35 @@ ip6addr_aton(const char *cp, ip6_addr_t *addr)
if (current_block_index > 7) {
/* address too long! */
return 0;
- } if (s[1] == ':') {
+ }
+ if (s[1] == ':') {
+ if (s[2] == ':') {
+ /* invalid format: three successive colons */
+ return 0;
+ }
s++;
/* "::" found, set zeros */
- while (zero_blocks-- > 0) {
+ while (zero_blocks > 0) {
+ zero_blocks--;
if (current_block_index & 0x1) {
addr_index++;
- }
- else {
+ } 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) ? *s - '0' :
- 10 + (islower(*s) ? *s - 'a' : *s - 'A'));
+ (isdigit(*s) ? (u32_t)(*s - '0') :
+ (u32_t)(10 + (islower(*s) ? *s - 'a' : *s - 'A')));
} else {
/* unexpected digit, space? CRLF? */
break;
@@ -142,7 +152,7 @@ ip6addr_aton(const char *cp, ip6_addr_t *addr)
/* convert to network byte order. */
if (addr) {
for (addr_index = 0; addr_index < 4; addr_index++) {
- addr->addr[addr_index] = htonl(addr->addr[addr_index]);
+ addr->addr[addr_index] = lwip_htonl(addr->addr[addr_index]);
}
}
@@ -150,6 +160,8 @@ ip6addr_aton(const char *cp, ip6_addr_t *addr)
return 0;
}
+ ip6_addr_clear_zone(addr);
+
return 1;
}
@@ -159,7 +171,7 @@ ip6addr_aton(const char *cp, ip6_addr_t *addr)
*
* @param addr ip6 address in network order to convert
* @return pointer to a global static (!) buffer that holds the ASCII
- * represenation of addr
+ * representation of addr
*/
char *
ip6addr_ntoa(const ip6_addr_t *addr)
@@ -180,67 +192,97 @@ ip6addr_ntoa(const ip6_addr_t *addr)
char *
ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen)
{
- u32_t current_block_index, current_block_value;
- s32_t zero_flag, i;
+ u32_t current_block_index, current_block_value, next_block_value;
+ s32_t i;
+ u8_t zero_flag, empty_block_flag;
i = 0;
- zero_flag = 0; /* used to indicate a zero chain for "::' */
+ 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 = htonl(addr->addr[current_block_index >> 1]);
+ 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) {
- /* generate empty block "::" */
- if (!zero_flag) {
- if (current_block_index > 0) {
- zero_flag = 1;
+ 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;
+ 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;
}
- else {
- if (current_block_index > 0) {
- buf[i++] = ':';
- if (i >= buflen) return NULL;
- }
- if ((current_block_value & 0xf000) == 0) {
- zero_flag = 1;
- }
- else {
- buf[i++] = xchar(((current_block_value & 0xf000) >> 12));
- zero_flag = 0;
- if (i >= buflen) return NULL;
+ if (current_block_index > 0) {
+ buf[i++] = ':';
+ 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 & 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 & 0xf0) == 0) && (zero_flag)) {
- /* do nothing */
- }
- else {
- buf[i++] = xchar(((current_block_value & 0xf0) >> 4));
- 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;
}
+ }
- buf[i++] = xchar((current_block_value & 0xf));
- 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;
}
}
@@ -248,4 +290,5 @@ ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen)
return buf;
}
+
#endif /* LWIP_IPV6 */
diff --git a/lwip/src/core/ipv6/ip6_frag.c b/lwip/src/core/ipv6/ip6_frag.c
index a43fd61..d6c5d22 100644
--- a/lwip/src/core/ipv6/ip6_frag.c
+++ b/lwip/src/core/ipv6/ip6_frag.c
@@ -44,6 +44,7 @@
#include "lwip/ip6.h"
#include "lwip/icmp6.h"
#include "lwip/nd6.h"
+#include "lwip/ip.h"
#include "lwip/pbuf.h"
#include "lwip/memp.h"
@@ -69,6 +70,12 @@
#define IP_REASS_FREE_OLDEST 1
#endif /* IP_REASS_FREE_OLDEST */
+#if IPV6_FRAG_COPYHEADER
+/* The number of bytes we need to "borrow" from (i.e., overwrite in) the header
+ * that precedes the fragment header for reassembly pruposes. */
+#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
@@ -107,6 +114,11 @@ 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,
@@ -137,7 +149,7 @@ ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr)
{
struct ip6_reassdata *prev;
u16_t pbufs_freed = 0;
- u8_t clen;
+ u16_t clen;
struct pbuf *p;
struct ip6_reass_helper *iprh;
@@ -148,16 +160,28 @@ ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr)
/* First, de-queue the first pbuf from r->p. */
p = ipr->p;
ipr->p = iprh->next_pbuf;
- /* Then, move back to the original header (we are now pointing to Fragment header). */
- if (pbuf_header(p, (u8_t*)p->payload - (u8_t*)ipr->iphdr)) {
+ /* Restore the part that we've overwritten with our helper structure, or we
+ * might send garbage (and disclose a pointer) in the ICMPv6 reply. */
+ MEMCPY(p->payload, ipr->orig_hdr, sizeof(iprh));
+ /* 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*)ipr->iphdr))) {
LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0);
}
else {
- icmp6_time_exceeded(p, ICMP6_TE_FRAG);
+ /* Reconstruct the zoned source and destination addresses, so that we do
+ * not end up sending the ICMP response over the wrong link. */
+ ip6_addr_t src_addr, dest_addr;
+ ip6_addr_copy_from_packed(src_addr, IPV6_FRAG_SRC(ipr));
+ ip6_addr_set_zone(&src_addr, ipr->src_zone);
+ ip6_addr_copy_from_packed(dest_addr, IPV6_FRAG_DEST(ipr));
+ ip6_addr_set_zone(&dest_addr, ipr->dest_zone);
+ /* Send the actual ICMP response. */
+ icmp6_time_exceeded_with_addrs(p, ICMP6_TE_FRAG, &src_addr, &dest_addr);
}
clen = pbuf_clen(p);
LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
- pbufs_freed += clen;
+ pbufs_freed = (u16_t)(pbufs_freed + clen);
pbuf_free(p);
}
#endif /* LWIP_ICMP6 */
@@ -173,7 +197,7 @@ ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr)
p = iprh->next_pbuf;
clen = pbuf_clen(pcur);
LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
- pbufs_freed += clen;
+ pbufs_freed = (u16_t)(pbufs_freed + clen);
pbuf_free(pcur);
}
@@ -196,7 +220,7 @@ ip6_reass_free_complete_datagram(struct 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;
+ ip6_reass_pbufcount = (u16_t)(ip6_reass_pbufcount - pbufs_freed);
}
#if IP_REASS_FREE_OLDEST
@@ -226,6 +250,10 @@ ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed)
}
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);
}
@@ -237,7 +265,6 @@ ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed)
* Reassembles incoming IPv6 fragments into an IPv6 datagram.
*
* @param p points to the IPv6 Fragment Header
- * @param len the length of the payload (after Fragment Header)
* @return NULL if reassembly is incomplete, pbuf pointing to
* IPv6 Header if reassembly is complete
*/
@@ -246,25 +273,45 @@ ip6_reass(struct pbuf *p)
{
struct ip6_reassdata *ipr, *ipr_prev;
struct ip6_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
- struct ip6_frag_hdr * frag_hdr;
- u16_t offset, len;
- u8_t clen, valid = 1;
- struct pbuf *q;
+ struct ip6_frag_hdr *frag_hdr;
+ u16_t offset, len, start, end;
+ ptrdiff_t hdrdiff;
+ u16_t clen;
+ u8_t valid = 1;
+ struct pbuf *q, *next_pbuf;
IP6_FRAG_STATS_INC(ip6_frag.recv);
+ /* ip6_frag_hdr must be in the first pbuf, not chained. Checked by caller. */
+ LWIP_ASSERT("IPv6 fragment header does not fit in first pbuf",
+ p->len >= sizeof(struct ip6_frag_hdr));
+
frag_hdr = (struct ip6_frag_hdr *) p->payload;
clen = pbuf_clen(p);
- offset = ntohs(frag_hdr->_fragment_offset);
+ 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 = ntohs(ip6_current_header()->_plen);
- len -= ((u8_t*)p->payload - (u8_t*)ip6_current_header()) - IP6_HLEN;
- len -= IP6_FRAG_HLEN;
+ len = lwip_ntohs(ip6_current_header()->_plen);
+ hdrdiff = (u8_t*)p->payload - (const u8_t*)ip6_current_header();
+ LWIP_ASSERT("not a valid pbuf (ip6_input check missing?)", hdrdiff <= 0xFFFF);
+ LWIP_ASSERT("not a valid pbuf (ip6_input check missing?)", hdrdiff >= IP6_HLEN);
+ hdrdiff -= IP6_HLEN;
+ hdrdiff += IP6_FRAG_HLEN;
+ if (hdrdiff > len) {
+ IP6_FRAG_STATS_INC(ip6_frag.proterr);
+ goto nullreturn;
+ }
+ len = (u16_t)(len - hdrdiff);
+ start = (offset & IP6_FRAG_OFFSET_MASK);
+ if (start > (0xFFFF - len)) {
+ /* u16_t overflow, cannot handle this */
+ IP6_FRAG_STATS_INC(ip6_frag.proterr);
+ goto nullreturn;
+ }
/* Look for the datagram the fragment belongs to in the current datagram queue,
* remembering the previous in the queue for later dequeueing. */
@@ -273,8 +320,8 @@ ip6_reass(struct pbuf *p)
in the reassembly buffer. If so, we proceed with copying the
fragment into the buffer. */
if ((frag_hdr->_identification == ipr->identification) &&
- ip6_addr_cmp(ip6_current_src_addr(), &(ipr->iphdr->src)) &&
- ip6_addr_cmp(ip6_current_dest_addr(), &(ipr->iphdr->dest))) {
+ ip6_addr_cmp_packed(ip6_current_src_addr(), &(IPV6_FRAG_SRC(ipr)), ipr->src_zone) &&
+ ip6_addr_cmp_packed(ip6_current_dest_addr(), &(IPV6_FRAG_DEST(ipr)), ipr->dest_zone)) {
IP6_FRAG_STATS_INC(ip6_frag.cachehit);
break;
}
@@ -289,17 +336,23 @@ ip6_reass(struct pbuf *p)
/* Make room and try again. */
ip6_reass_remove_oldest_datagram(ipr, clen);
ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
- if (ipr == NULL)
+ 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;
+ ipr->timer = IPV6_REASS_MAXAGE;
/* enqueue the new structure to the front of the list */
ipr->next = reassdatagrams;
@@ -308,8 +361,22 @@ ip6_reass(struct pbuf *p)
/* Use the current IPv6 header for src/dest address reference.
* Eventually, we will replace it when we get the first fragment
* (it might be this one, in any case, it is done later). */
- ipr->iphdr = (struct ip6_hdr *)ip6_current_header();
-
+ /* need to use the none-const pointer here: */
+ ipr->iphdr = ip_data.current_ip6_header;
+#if IPV6_FRAG_COPYHEADER
+ MEMCPY(&ipr->src, &ip6_current_header()->src, sizeof(ipr->src));
+ MEMCPY(&ipr->dest, &ip6_current_header()->dest, sizeof(ipr->dest));
+#endif /* IPV6_FRAG_COPYHEADER */
+#if LWIP_IPV6_SCOPES
+ /* Also store the address zone information.
+ * @todo It is possible that due to netif destruction and recreation, the
+ * stored zones end up resolving to a different interface. In that case, we
+ * risk sending a "time exceeded" ICMP response over the wrong link.
+ * Ideally, netif destruction would clean up matching pending reassembly
+ * structures, but custom zone mappings would make that non-trivial. */
+ ipr->src_zone = ip6_addr_zone(ip6_current_src_addr());
+ ipr->dest_zone = ip6_addr_zone(ip6_current_dest_addr());
+#endif /* LWIP_IPV6_SCOPES */
/* copy the fragmented packet id. */
ipr->identification = frag_hdr->_identification;
@@ -321,47 +388,67 @@ ip6_reass(struct pbuf *p)
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)
+ 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 */
+
+ /* Prepare the pointer to the helper structure, and its initial values.
+ * Do not yet write to the structure itself, as we still have to make a
+ * backup of the original data, and we should not do that until we know for
+ * sure that we are going to add this packet to the list. */
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;
+ next_pbuf = NULL;
+ end = (u16_t)(start + 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 (start < iprh_tmp->start) {
#if IP_REASS_CHECK_OVERLAP
- if (iprh->end > iprh_tmp->start) {
+ if (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) {
+ if (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;
+ next_pbuf = q;
if (iprh_prev != NULL) {
/* not the fragment with the lowest offset */
iprh_prev->next_pbuf = p;
@@ -370,15 +457,13 @@ ip6_reass(struct pbuf *p)
ipr->p = p;
}
break;
- } else if(iprh->start == iprh_tmp->start) {
+ } else if (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) {
+ } else if (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 {
@@ -401,10 +486,10 @@ ip6_reass(struct pbuf *p)
/* 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);
+ LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= start);
#endif /* IP_REASS_CHECK_OVERLAP */
iprh_prev->next_pbuf = p;
- if (iprh_prev->end != iprh->start) {
+ if (iprh_prev->end != start) {
valid = 0;
}
} else {
@@ -419,12 +504,23 @@ ip6_reass(struct pbuf *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;
+ ip6_reass_pbufcount = (u16_t)(ip6_reass_pbufcount + clen);
/* Remember IPv6 header if this is the first fragment. */
- if (iprh->start == 0) {
- ipr->iphdr = (struct ip6_hdr *)ip6_current_header();
+ if (start == 0) {
+ /* need to use the none-const pointer here: */
+ ipr->iphdr = ip_data.current_ip6_header;
+ /* Make a backup of the part of the packet data that we are about to
+ * overwrite, so that we can restore the original later. */
+ MEMCPY(ipr->orig_hdr, p->payload, sizeof(*iprh));
+ /* For IPV6_FRAG_COPYHEADER there is no need to copy src/dst again, as they
+ * will be the same as they were. With LWIP_IPV6_SCOPES, the same applies
+ * to the source/destination zones. */
}
+ /* Only after the backup do we get to fill in the actual helper structure. */
+ iprh->next_pbuf = next_pbuf;
+ iprh->start = start;
+ iprh->end = end;
/* If this is the last fragment, calculate total packet length. */
if ((offset & IP6_FRAG_MORE_FLAG) == 0) {
@@ -455,18 +551,27 @@ ip6_reass(struct pbuf *p)
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) {
-
- if (iprh->next_pbuf != NULL) {
+ while (iprh != NULL) {
+ 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*) iprh->next_pbuf->payload;
-
- /* hide the fragment header for every succeding fragment */
- pbuf_header(iprh->next_pbuf, -IP6_FRAG_HLEN);
- pbuf_cat(ipr->p, iprh->next_pbuf);
+ iprh_tmp = (struct ip6_reass_helper*)next_pbuf->payload;
+
+ /* hide the fragment header for every succeeding fragment */
+ pbuf_remove_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_remove_header(next_pbuf, 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;
@@ -475,26 +580,61 @@ ip6_reass(struct pbuf *p)
iprh = iprh_tmp;
}
- /* Adjust datagram length by adding header lengths. */
- ipr->datagram_len += ((u8_t*)ipr->p->payload - (u8_t*)ipr->iphdr)
- + IP6_FRAG_HLEN
- - IP6_HLEN ;
+ /* Get the first pbuf. */
+ p = ipr->p;
- /* Set payload length in ip header. */
- ipr->iphdr->_plen = htons(ipr->datagram_len);
+#if IPV6_FRAG_COPYHEADER
+ if (IPV6_FRAG_REQROOM > 0) {
+ u8_t hdrerr;
+ /* Restore (only) the bytes that we overwrote beyond the fragment header.
+ * Those bytes may belong to either the IPv6 header or an extension
+ * header placed before the fragment header. */
+ MEMCPY(p->payload, ipr->orig_hdr, IPV6_FRAG_REQROOM);
+ /* get back room for struct ip6_reass_helper (only required if sizeof(void*) > 4) */
+ hdrerr = pbuf_remove_header(p, IPV6_FRAG_REQROOM);
+ LWIP_UNUSED_ARG(hdrerr); /* in case of LWIP_NOASSERT */
+ LWIP_ASSERT("no room for struct ip6_reass_helper", hdrerr == 0);
+ }
+#endif
- /* Get the furst pbuf. */
- p = ipr->p;
+ /* We need to get rid of the fragment header itself, which is somewhere in
+ * the middle of the packet (but still in the first pbuf of the chain).
+ * Getting rid of the header is required by RFC 2460 Sec. 4.5 and necessary
+ * in order to be able to reassemble packets that are close to full size
+ * (i.e., around 65535 bytes). We simply move up all the headers before the
+ * fragment header, including the IPv6 header, and adjust the payload start
+ * accordingly. This works because all these headers are in the first pbuf
+ * of the chain, and because the caller adjusts all its pointers on
+ * successful reassembly. */
+ MEMMOVE((u8_t*)ipr->iphdr + sizeof(struct ip6_frag_hdr), ipr->iphdr,
+ (size_t)((u8_t*)p->payload - (u8_t*)ipr->iphdr));
+
+ /* This is where the IPv6 header is now. */
+ iphdr_ptr = (struct ip6_hdr*)((u8_t*)ipr->iphdr +
+ sizeof(struct ip6_frag_hdr));
- /* 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;
+ /* Adjust datagram length by adding header lengths. */
+ ipr->datagram_len = (u16_t)(ipr->datagram_len + ((u8_t*)p->payload - (u8_t*)iphdr_ptr)
+ - IP6_HLEN);
- /* release the sources allocate for the fragment queue entry */
+ /* Set payload length in ip header. */
+ iphdr_ptr->_plen = lwip_htons(ipr->datagram_len);
+
+ /* With the fragment header gone, we now need to adjust the next-header
+ * field of whatever header was originally before it. Since the packet made
+ * it through the original header processing routines at least up to the
+ * fragment header, we do not need any further sanity checks here. */
+ if (IP6H_NEXTH(iphdr_ptr) == IP6_NEXTH_FRAGMENT) {
+ iphdr_ptr->_nexth = ipr->nexth;
+ } else {
+ u8_t *ptr = (u8_t *)iphdr_ptr + IP6_HLEN;
+ while (*ptr != IP6_NEXTH_FRAGMENT) {
+ ptr += 8 * (1 + ptr[1]);
+ }
+ *ptr = ipr->nexth;
+ }
+
+ /* release the resources allocated for the fragment queue entry */
if (reassdatagrams == ipr) {
/* it was the first in the list */
reassdatagrams = ipr->next;
@@ -506,10 +646,12 @@ ip6_reass(struct pbuf *p)
memp_free(MEMP_IP6_REASSDATA, ipr);
/* adjust the number of pbufs currently queued for reassembly. */
- ip6_reass_pbufcount -= pbuf_clen(p);
+ clen = pbuf_clen(p);
+ LWIP_ASSERT("ip6_reass_pbufcount >= clen", ip6_reass_pbufcount >= clen);
+ ip6_reass_pbufcount = (u16_t)(ip6_reass_pbufcount - clen);
- /* Move pbuf back to IPv6 header. */
- if (pbuf_header(p, (u8_t*)p->payload - (u8_t*)ipr->iphdr)) {
+ /* Move pbuf back to IPv6 header. This should never fail. */
+ 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;
@@ -522,14 +664,16 @@ ip6_reass(struct pbuf *p)
return NULL;
nullreturn:
+ IP6_FRAG_STATS_INC(ip6_frag.drop);
pbuf_free(p);
return NULL;
}
-#endif /* LWIP_IPV6 ^^ LWIP_IPV6_REASS */
+#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)
@@ -558,6 +702,7 @@ ip6_frag_free_pbuf_custom(struct pbuf *p)
}
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.
@@ -572,33 +717,32 @@ ip6_frag_free_pbuf_custom(struct pbuf *p)
* @return ERR_OK if sent successfully, err_t otherwise
*/
err_t
-ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest)
+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 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;
+ const u16_t mtu = nd6_get_destination_mtu(dest, netif);
+ const u16_t nfb = (u16_t)((mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK);
u16_t fragment_offset = 0;
u16_t last;
u16_t poff = IP6_HLEN;
- u16_t newpbuflen = 0;
- u16_t left_to_copy;
identification++;
original_ip6hdr = (struct ip6_hdr *)p->payload;
- mtu = nd6_get_destination_mtu(dest, netif);
-
- /* TODO we assume there are no options in the unfragmentable part (IPv6 header). */
- left = p->tot_len - IP6_HLEN;
-
- nfb = (mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK;
+ /* @todo we assume there are no options in the unfragmentable part (IPv6 header). */
+ LWIP_ASSERT("p->tot_len >= IP6_HLEN", p->tot_len >= IP6_HLEN);
+ left = (u16_t)(p->tot_len - IP6_HLEN);
while (left) {
last = (left <= nfb);
@@ -606,6 +750,26 @@ ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest)
/* 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_add_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,
@@ -617,15 +781,15 @@ ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest)
return ERR_MEM;
}
LWIP_ASSERT("this needs a pbuf in one piece!",
- (p->len >= (IP6_HLEN + IP6_FRAG_HLEN)));
+ (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;
+ p->len = (u16_t)(p->len - poff);
+ p->tot_len = (u16_t)(p->tot_len - poff);
left_to_copy = cop;
while (left_to_copy) {
@@ -658,21 +822,22 @@ ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest)
* so that it is removed when pbuf_dechain is later called on rambuf.
*/
pbuf_cat(rambuf, newpbuf);
- left_to_copy -= newpbuflen;
+ left_to_copy = (u16_t)(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 = htons((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG));
- frag_hdr->_identification = htonl(identification);
+ frag_hdr->_fragment_offset = lwip_htons((u16_t)((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);
+ IP6H_PLEN_SET(ip6hdr, (u16_t)(cop + IP6_FRAG_HLEN));
/* No need for separate header pbuf - we allowed room for it in rambuf
* when allocated.
@@ -688,8 +853,8 @@ ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest)
*/
pbuf_free(rambuf);
- left -= cop;
- fragment_offset += cop;
+ left = (u16_t)(left - cop);
+ fragment_offset = (u16_t)(fragment_offset + cop);
}
return ERR_OK;
}
diff --git a/lwip/src/core/ipv6/mld6.c b/lwip/src/core/ipv6/mld6.c
index 1cb2dd9..b5a159b 100644
--- a/lwip/src/core/ipv6/mld6.c
+++ b/lwip/src/core/ipv6/mld6.c
@@ -1,8 +1,12 @@
/**
* @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.
+ * No support for MLDv2.\n
+ * To be called from TCPIP thread
*/
/*
@@ -47,9 +51,11 @@
#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"
@@ -69,16 +75,11 @@
#define MLD6_GROUP_DELAYING_MEMBER 1
#define MLD6_GROUP_IDLE_MEMBER 2
-
-/* The list of joined groups. */
-static struct mld_group* mld_group_list;
-
-
/* Forward declarations. */
-static struct mld_group * mld6_new_group(struct netif *ifp, ip6_addr_t *addr);
-static err_t mld6_free_group(struct mld_group *group);
+static 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 mld_group *group, u8_t type);
+static void mld6_send(struct netif *netif, struct mld_group *group, u8_t type);
/**
@@ -89,33 +90,21 @@ static void mld6_send(struct mld_group *group, u8_t type);
err_t
mld6_stop(struct netif *netif)
{
- struct mld_group *group = mld_group_list;
- struct mld_group *prev = NULL;
- struct mld_group *next;
+ struct mld_group *group = netif_mld6_data(netif);
+
+ netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, NULL);
- /* look for groups joined on this interface further down the list */
while (group != NULL) {
- next = group->next;
- /* is it a group joined on this interface? */
- if (group->netif == netif) {
- /* is it the first group of the list? */
- if (group == mld_group_list) {
- mld_group_list = next;
- }
- /* is there a "previous" group defined? */
- if (prev != NULL) {
- prev->next = next;
- }
- /* disable the group at the MAC level */
- if (netif->mld_mac_filter != NULL) {
- netif->mld_mac_filter(netif, &(group->group_address), MLD6_DEL_MAC_FILTER);
- }
- /* free group */
- memp_free(MEMP_MLD6_GROUP, group);
- } else {
- /* change the "previous" */
- prev = group;
+ 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;
}
@@ -130,12 +119,10 @@ mld6_stop(struct netif *netif)
void
mld6_report_groups(struct netif *netif)
{
- struct mld_group *group = mld_group_list;
+ struct mld_group *group = netif_mld6_data(netif);
while (group != NULL) {
- if (group->netif == netif) {
- mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
- }
+ mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
group = group->next;
}
}
@@ -149,12 +136,12 @@ mld6_report_groups(struct netif *netif)
* NULL if the group wasn't found.
*/
struct mld_group *
-mld6_lookfor_group(struct netif *ifp, ip6_addr_t *addr)
+mld6_lookfor_group(struct netif *ifp, const ip6_addr_t *addr)
{
- struct mld_group *group = mld_group_list;
+ struct mld_group *group = netif_mld6_data(ifp);
while (group != NULL) {
- if ((group->netif == ifp) && (ip6_addr_cmp(&(group->group_address), addr))) {
+ if (ip6_addr_cmp(&(group->group_address), addr)) {
return group;
}
group = group->next;
@@ -173,55 +160,53 @@ mld6_lookfor_group(struct netif *ifp, ip6_addr_t *addr)
* NULL on memory error.
*/
static struct mld_group *
-mld6_new_group(struct netif *ifp, ip6_addr_t *addr)
+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) {
- group->netif = ifp;
ip6_addr_set(&(group->group_address), addr);
group->timer = 0; /* Not running */
group->group_state = MLD6_GROUP_IDLE_MEMBER;
group->last_reporter_flag = 0;
group->use = 0;
- group->next = mld_group_list;
+ group->next = netif_mld6_data(ifp);
- mld_group_list = group;
+ netif_set_client_data(ifp, LWIP_NETIF_CLIENT_DATA_INDEX_MLD6, group);
}
return group;
}
/**
- * Remove a group in the mld_group_list and free
+ * 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_free_group(struct mld_group *group)
+mld6_remove_group(struct netif *netif, struct mld_group *group)
{
err_t err = ERR_OK;
/* Is it the first group? */
- if (mld_group_list == group) {
- mld_group_list = group->next;
+ 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 = mld_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
+ 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)
+ if (tmpGroup == NULL) {
err = ERR_ARG;
+ }
}
- /* free group */
- memp_free(MEMP_MLD6_GROUP, group);
return err;
}
@@ -236,14 +221,14 @@ mld6_free_group(struct mld_group *group)
void
mld6_input(struct pbuf *p, struct netif *inp)
{
- struct mld_header * mld_hdr;
- struct mld_group* group;
+ 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 */
+ /* @todo debug message */
pbuf_free(p);
MLD6_STATS_INC(mld6.lenerr);
MLD6_STATS_INC(mld6.drop);
@@ -254,23 +239,20 @@ mld6_input(struct pbuf *p, struct netif *inp)
switch (mld_hdr->type) {
case ICMP6_TYPE_MLQ: /* Multicast listener query. */
- {
/* Is it a general query? */
if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) &&
ip6_addr_isany(&(mld_hdr->multicast_address))) {
MLD6_STATS_INC(mld6.rx_general);
/* Report all groups, except all nodes group, and if-local groups. */
- group = mld_group_list;
+ group = netif_mld6_data(inp);
while (group != NULL) {
- if ((group->netif == inp) &&
- (!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) &&
+ 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 {
+ } 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. */
@@ -282,9 +264,7 @@ mld6_input(struct pbuf *p, struct netif *inp)
}
}
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. */
@@ -299,12 +279,9 @@ mld6_input(struct pbuf *p, struct netif *inp)
}
}
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);
@@ -315,138 +292,184 @@ mld6_input(struct pbuf *p, struct netif *inp)
}
/**
- * Join a group on a network interface.
+ * @ingroup mld6
+ * Join a group on one or all network interfaces.
*
- * @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
+ * If the group is to be joined on all interfaces, the given group address must
+ * not have a zone set (i.e., it must have its zone index set to IP6_NO_ZONE).
+ * If the group is to be joined on one particular interface, the given group
+ * address may or may not have a zone set.
+ *
+ * @param srcaddr ipv6 address (zoned) of the network interface which should
+ * join a new group. If IP6_ADDR_ANY6, join on all netifs
+ * @param groupaddr the ipv6 address of the group to join (possibly but not
+ * necessarily zoned)
* @return ERR_OK if group was joined on the netif(s), an err_t otherwise
*/
err_t
-mld6_joingroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr)
+mld6_joingroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
{
- err_t err = ERR_VAL; /* no matching interface */
- struct mld_group *group;
- struct netif *netif;
- u8_t match;
- u8_t i;
+ err_t err = ERR_VAL; /* no matching interface */
+ struct netif *netif;
/* loop through netif's */
- netif = netif_list;
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
/* Should we join this interface ? */
- match = 0;
- if (ip6_addr_isany(srcaddr)) {
- match = 1;
- }
- else {
- for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_cmp(srcaddr, netif_ip6_addr(netif, i))) {
- match = 1;
- break;
- }
+ if (ip6_addr_isany(srcaddr) ||
+ netif_get_ip6_addr_match(netif, srcaddr) >= 0) {
+ err = mld6_joingroup_netif(netif, groupaddr);
+ if (err != ERR_OK) {
+ return err;
}
}
- if (match) {
- /* find group or create a new one if not found */
- group = mld6_lookfor_group(netif, groupaddr);
-
- if (group == NULL) {
- /* Joining a new group. Create a new group entry. */
- group = mld6_new_group(netif, groupaddr);
- if (group == NULL) {
- return ERR_MEM;
- }
+ }
- /* Activate this address on the MAC layer. */
- if (netif->mld_mac_filter != NULL) {
- netif->mld_mac_filter(netif, groupaddr, MLD6_ADD_MAC_FILTER);
- }
+ return err;
+}
- /* Report our membership. */
- MLD6_STATS_INC(mld6.tx_report);
- mld6_send(group, ICMP6_TYPE_MLR);
- mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
- }
+/**
+ * @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 (possibly but not
+ * necessarily zoned)
+ * @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;
+#if LWIP_IPV6_SCOPES
+ ip6_addr_t ip6addr;
+
+ /* If the address has a particular scope but no zone set, use the netif to
+ * set one now. Within the mld6 module, all addresses are properly zoned. */
+ if (ip6_addr_lacks_zone(groupaddr, IP6_MULTICAST)) {
+ ip6_addr_set(&ip6addr, groupaddr);
+ ip6_addr_assign_zone(&ip6addr, IP6_MULTICAST, netif);
+ groupaddr = &ip6addr;
+ }
+ IP6_ADDR_ZONECHECK_NETIF(groupaddr, netif);
+#endif /* LWIP_IPV6_SCOPES */
+
+ /* find group or create a new one if not found */
+ group = mld6_lookfor_group(netif, groupaddr);
- /* Increment group use */
- group->use++;
- err = ERR_OK;
+ if (group == NULL) {
+ /* Joining a new group. Create a new group entry. */
+ group = mld6_new_group(netif, groupaddr);
+ if (group == NULL) {
+ return ERR_MEM;
}
- /* proceed to next network interface */
- netif = netif->next;
+ /* 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);
}
- return err;
+ /* 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
+ * Zoning of address follows the same rules as @ref mld6_joingroup.
+ *
+ * @param srcaddr ipv6 address (zoned) of the network interface which should
+ * leave the group. If IP6_ADDR_ANY6, leave on all netifs
+ * @param groupaddr the ipv6 address of the group to leave (possibly, but not
+ * necessarily zoned)
* @return ERR_OK if group was left on the netif(s), an err_t otherwise
*/
err_t
-mld6_leavegroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr)
+mld6_leavegroup(const ip6_addr_t *srcaddr, const ip6_addr_t *groupaddr)
{
- err_t err = ERR_VAL; /* no matching interface */
- struct mld_group *group;
- struct netif *netif;
- u8_t match;
- u8_t i;
+ err_t err = ERR_VAL; /* no matching interface */
+ struct netif *netif;
/* loop through netif's */
- netif = netif_list;
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
/* Should we leave this interface ? */
- match = 0;
- if (ip6_addr_isany(srcaddr)) {
- match = 1;
- }
- else {
- for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_cmp(srcaddr, netif_ip6_addr(netif, i))) {
- match = 1;
- break;
- }
+ if (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;
}
}
- if (match) {
- /* find group */
- group = mld6_lookfor_group(netif, groupaddr);
+ }
- if (group != NULL) {
- /* Leave if there is no other use of the group */
- if (group->use <= 1) {
- /* If we are the last reporter for this group */
- if (group->last_reporter_flag) {
- MLD6_STATS_INC(mld6.tx_leave);
- mld6_send(group, ICMP6_TYPE_MLD);
- }
+ return err;
+}
- /* Disable the group at the MAC level */
- if (netif->mld_mac_filter != NULL) {
- netif->mld_mac_filter(netif, groupaddr, MLD6_DEL_MAC_FILTER);
- }
+/**
+ * @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 (possibly, but not
+ * necessarily zoned)
+ * @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;
+#if LWIP_IPV6_SCOPES
+ ip6_addr_t ip6addr;
- /* Free the group */
- mld6_free_group(group);
- } else {
- /* Decrement group use */
- group->use--;
- }
- /* Leave on this interface */
- err = ERR_OK;
+ if (ip6_addr_lacks_zone(groupaddr, IP6_MULTICAST)) {
+ ip6_addr_set(&ip6addr, groupaddr);
+ ip6_addr_assign_zone(&ip6addr, IP6_MULTICAST, netif);
+ groupaddr = &ip6addr;
+ }
+ IP6_ADDR_ZONECHECK_NETIF(groupaddr, netif);
+#endif /* LWIP_IPV6_SCOPES */
+
+ /* 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--;
}
- /* proceed to next network interface */
- netif = netif->next;
+
+ /* Left group */
+ return ERR_OK;
}
- return err;
+ /* Group not found */
+ return ERR_VAL;
}
@@ -459,21 +482,25 @@ mld6_leavegroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr)
void
mld6_tmr(void)
{
- struct mld_group *group = mld_group_list;
-
- while (group != NULL) {
- if (group->timer > 0) {
- group->timer--;
- if (group->timer == 0) {
- /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */
- if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
- MLD6_STATS_INC(mld6.tx_report);
- mld6_send(group, ICMP6_TYPE_MLR);
- group->group_state = MLD6_GROUP_IDLE_MEMBER;
+ struct netif *netif;
+
+ NETIF_FOREACH(netif) {
+ 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;
}
- group = group->next;
}
}
@@ -482,20 +509,23 @@ mld6_tmr(void)
*
* @param group the mld_group for which "delaying" membership report
* should be sent
- * @param maxresp the max resp delay provided in the query
+ * @param maxresp_in the max resp delay provided in the query
*/
static void
-mld6_delayed_report(struct mld_group *group, u16_t maxresp)
+mld6_delayed_report(struct mld_group *group, u16_t maxresp_in)
{
/* Convert maxresp from milliseconds to tmr ticks */
- maxresp = maxresp / MLD6_TMR_INTERVAL;
+ u16_t maxresp = maxresp_in / MLD6_TMR_INTERVAL;
if (maxresp == 0) {
maxresp = 1;
}
#ifdef LWIP_RAND
/* Randomize maxresp. (if LWIP_RAND is supported) */
- maxresp = (LWIP_RAND() % (maxresp - 1)) + 1;
+ maxresp = (u16_t)(LWIP_RAND() % maxresp);
+ if (maxresp == 0) {
+ maxresp = 1;
+ }
#endif /* LWIP_RAND */
/* Apply timer value if no report has been scheduled already. */
@@ -517,38 +547,34 @@ mld6_delayed_report(struct mld_group *group, u16_t maxresp)
* @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done)
*/
static void
-mld6_send(struct mld_group *group, u8_t type)
+mld6_send(struct netif *netif, struct mld_group *group, u8_t type)
{
- struct mld_header * mld_hdr;
- struct pbuf * p;
- ip6_addr_t * src_addr;
+ 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) || (p->len < (sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr)))) {
- /* We couldn't allocate a suitable pbuf. drop it. */
- if (p != NULL) {
- pbuf_free(p);
- }
+ 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)) {
+ if (pbuf_remove_header(p, IP6_HBH_HLEN)) {
pbuf_free(p);
MLD6_STATS_INC(mld6.lenerr);
return;
}
/* Select our source address. */
- if (!ip6_addr_isvalid(netif_ip6_addr_state(group->netif, 0))) {
+ 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_ANY;
+ src_addr = IP6_ADDR_ANY6;
} else {
/* Use link-local address as source address. */
- src_addr = netif_ip6_addr(group->netif, 0);
+ src_addr = netif_ip6_addr(netif, 0);
}
/* MLD message header pointer. */
@@ -560,21 +586,28 @@ mld6_send(struct mld_group *group, u8_t type)
mld_hdr->chksum = 0;
mld_hdr->max_resp_delay = 0;
mld_hdr->reserved = 0;
- ip6_addr_set(&(mld_hdr->multicast_address), &(group->group_address));
+ ip6_addr_copy_to_packed(mld_hdr->multicast_address, group->group_address);
- mld_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len,
- src_addr, &(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, group->netif);
+ MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, netif);
pbuf_free(p);
}
-
-
#endif /* LWIP_IPV6 */
diff --git a/lwip/src/core/ipv6/nd6.c b/lwip/src/core/ipv6/nd6.c
index 513db98..31a1f69 100644
--- a/lwip/src/core/ipv6/nd6.c
+++ b/lwip/src/core/ipv6/nd6.c
@@ -46,6 +46,9 @@
#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"
@@ -55,10 +58,19 @@
#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];
@@ -68,7 +80,7 @@ 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 */
+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;
@@ -77,27 +89,40 @@ 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)];
+/* Static buffer to parse RA packet options */
+union ra_options {
+ struct lladdr_option lladdr;
+ struct mtu_option mtu;
+ struct prefix_option prefix;
+#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+ struct rdnss_option rdnss;
+#endif
+};
+static union ra_options nd6_ra_buffer;
/* Forward declarations. */
-static s8_t nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr);
+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(ip6_addr_t * ip6addr);
+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(ip6_addr_t * ip6addr, struct netif * netif);
-static s8_t nd6_get_router(ip6_addr_t * router_addr, struct netif * netif);
-static s8_t nd6_new_router(ip6_addr_t * router_addr, struct netif * netif);
-static s8_t nd6_get_onlink_prefix(ip6_addr_t * prefix, struct netif * netif);
-static s8_t nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif);
+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(const ip6_addr_t *prefix, struct netif *netif);
+static s8_t nd6_new_onlink_prefix(const 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, ip6_addr_t * target_addr, u8_t flags);
-static void nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags);
+#define ND6_SEND_FLAG_ANY_SRC 0x04
+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 void nd6_send_rs(struct netif * netif);
+static err_t nd6_send_rs(struct netif *netif);
#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
#if LWIP_ND6_QUEUEING
@@ -109,6 +134,147 @@ static void nd6_send_q(s8_t i);
/**
+ * A local address has been determined to be a duplicate. Take the appropriate
+ * action(s) on the address and the interface as a whole.
+ *
+ * @param netif the netif that owns the address
+ * @param addr_idx the index of the address detected to be a duplicate
+ */
+static void
+nd6_duplicate_addr_detected(struct netif *netif, s8_t addr_idx)
+{
+
+ /* Mark the address as duplicate, but leave its lifetimes alone. If this was
+ * a manually assigned address, it will remain in existence as duplicate, and
+ * as such be unusable for any practical purposes until manual intervention.
+ * If this was an autogenerated address, the address will follow normal
+ * expiration rules, and thus disappear once its valid lifetime expires. */
+ netif_ip6_addr_set_state(netif, addr_idx, IP6_ADDR_DUPLICATED);
+
+#if LWIP_IPV6_AUTOCONFIG
+ /* If the affected address was the link-local address that we use to generate
+ * all other addresses, then we should not continue to use those derived
+ * addresses either, so mark them as duplicate as well. For autoconfig-only
+ * setups, this will make the interface effectively unusable, approaching the
+ * intention of RFC 4862 Sec. 5.4.5. @todo implement the full requirements */
+ if (addr_idx == 0) {
+ s8_t i;
+ for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i)) &&
+ !netif_ip6_addr_isstatic(netif, i)) {
+ netif_ip6_addr_set_state(netif, i, IP6_ADDR_DUPLICATED);
+ }
+ }
+ }
+#endif /* LWIP_IPV6_AUTOCONFIG */
+}
+
+#if LWIP_IPV6_AUTOCONFIG
+/**
+ * We received a router advertisement that contains a prefix with the
+ * autoconfiguration flag set. Add or update an associated autogenerated
+ * address.
+ *
+ * @param netif the netif on which the router advertisement arrived
+ * @param prefix_opt a pointer to the prefix option data
+ * @param prefix_addr an aligned copy of the prefix address
+ */
+static void
+nd6_process_autoconfig_prefix(struct netif *netif,
+ struct prefix_option *prefix_opt, const ip6_addr_t *prefix_addr)
+{
+ ip6_addr_t ip6addr;
+ u32_t valid_life, pref_life;
+ u8_t addr_state;
+ s8_t i, free_idx;
+
+ /* The caller already checks RFC 4862 Sec. 5.5.3 points (a) and (b). We do
+ * the rest, starting with checks for (c) and (d) here. */
+ valid_life = lwip_htonl(prefix_opt->valid_lifetime);
+ pref_life = lwip_htonl(prefix_opt->preferred_lifetime);
+ if (pref_life > valid_life || prefix_opt->prefix_length != 64) {
+ return; /* silently ignore this prefix for autoconfiguration purposes */
+ }
+
+ /* If an autogenerated address already exists for this prefix, update its
+ * lifetimes. An address is considered autogenerated if 1) it is not static
+ * (i.e., manually assigned), and 2) there is an advertised autoconfiguration
+ * prefix for it (the one we are processing here). This does not necessarily
+ * exclude the possibility that the address was actually assigned by, say,
+ * DHCPv6. If that distinction becomes important in the future, more state
+ * must be kept. As explained elsewhere we also update lifetimes of tentative
+ * and duplicate addresses. Skip address slot 0 (the link-local address). */
+ for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ addr_state = netif_ip6_addr_state(netif, i);
+ if (!ip6_addr_isinvalid(addr_state) && !netif_ip6_addr_isstatic(netif, i) &&
+ ip6_addr_netcmp(prefix_addr, netif_ip6_addr(netif, i))) {
+ /* Update the valid lifetime, as per RFC 4862 Sec. 5.5.3 point (e).
+ * The valid lifetime will never drop to zero as a result of this. */
+ u32_t remaining_life = netif_ip6_addr_valid_life(netif, i);
+ if (valid_life > ND6_2HRS || valid_life > remaining_life) {
+ netif_ip6_addr_set_valid_life(netif, i, valid_life);
+ } else if (remaining_life > ND6_2HRS) {
+ netif_ip6_addr_set_valid_life(netif, i, ND6_2HRS);
+ }
+ LWIP_ASSERT("bad valid lifetime", !netif_ip6_addr_isstatic(netif, i));
+ /* Update the preferred lifetime. No bounds checks are needed here. In
+ * rare cases the advertisement may un-deprecate the address, though.
+ * Deprecation is left to the timer code where it is handled anyway. */
+ if (pref_life > 0 && addr_state == IP6_ADDR_DEPRECATED) {
+ netif_ip6_addr_set_state(netif, i, IP6_ADDR_PREFERRED);
+ }
+ netif_ip6_addr_set_pref_life(netif, i, pref_life);
+ return; /* there should be at most one matching address */
+ }
+ }
+
+ /* No autogenerated address exists for this prefix yet. See if we can add a
+ * new one. However, if IPv6 autoconfiguration is administratively disabled,
+ * do not generate new addresses, but do keep updating lifetimes for existing
+ * addresses. Also, when adding new addresses, we must protect explicitly
+ * against a valid lifetime of zero, because again, we use that as a special
+ * value. The generated address would otherwise expire immediately anyway.
+ * Finally, the original link-local address must be usable at all. We start
+ * creating addresses even if the link-local address is still in tentative
+ * state though, and deal with the fallout of that upon DAD collision. */
+ addr_state = netif_ip6_addr_state(netif, 0);
+ if (!netif->ip6_autoconfig_enabled || valid_life == IP6_ADDR_LIFE_STATIC ||
+ ip6_addr_isinvalid(addr_state) || ip6_addr_isduplicated(addr_state)) {
+ return;
+ }
+
+ /* Construct the new address that we intend to use, and then see if that
+ * address really does not exist. It might have been added manually, after
+ * all. As a side effect, find a free slot. Note that we cannot use
+ * netif_add_ip6_address() here, as it would return ERR_OK if the address
+ * already did exist, resulting in that address being given lifetimes. */
+ IP6_ADDR(&ip6addr, prefix_addr->addr[0], prefix_addr->addr[1],
+ netif_ip6_addr(netif, 0)->addr[2], netif_ip6_addr(netif, 0)->addr[3]);
+ ip6_addr_assign_zone(&ip6addr, IP6_UNICAST, netif);
+
+ free_idx = 0;
+ for (i = 1; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i))) {
+ if (ip6_addr_cmp(&ip6addr, netif_ip6_addr(netif, i))) {
+ return; /* formed address already exists */
+ }
+ } else if (free_idx == 0) {
+ free_idx = i;
+ }
+ }
+ if (free_idx == 0) {
+ return; /* no address slots available, try again on next advertisement */
+ }
+
+ /* Assign the new address to the interface. */
+ ip_addr_copy_from_ip6(netif->ip6_addr[free_idx], ip6addr);
+ netif_ip6_addr_set_valid_life(netif, free_idx, valid_life);
+ netif_ip6_addr_set_pref_life(netif, free_idx, pref_life);
+ netif_ip6_addr_set_state(netif, free_idx, IP6_ADDR_TENTATIVE);
+}
+#endif /* LWIP_IPV6_AUTOCONFIG */
+
+/**
* Process an incoming neighbor discovery message
*
* @param p the nd packet, p->payload pointing to the icmpv6 header
@@ -126,12 +292,13 @@ nd6_input(struct pbuf *p, struct netif *inp)
switch (msg_type) {
case ICMP6_TYPE_NA: /* Neighbor Advertisement. */
{
- struct na_header * na_hdr;
- struct lladdr_option * lladdr_opt;
+ struct na_header *na_hdr;
+ struct lladdr_option *lladdr_opt;
+ ip6_addr_t target_address;
/* Check that na header fits in packet. */
if (p->len < (sizeof(struct na_header))) {
- /* TODO debug message */
+ /* @todo debug message */
pbuf_free(p);
ND6_STATS_INC(nd6.lenerr);
ND6_STATS_INC(nd6.drop);
@@ -140,53 +307,36 @@ nd6_input(struct pbuf *p, struct netif *inp)
na_hdr = (struct na_header *)p->payload;
+ /* Create an aligned, zoned copy of the target address. */
+ ip6_addr_copy_from_packed(target_address, na_hdr->target_address);
+ ip6_addr_assign_zone(&target_address, IP6_UNICAST, inp);
+
+ /* Check a subset of the other RFC 4861 Sec. 7.1.2 requirements. */
+ if (IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM || na_hdr->code != 0 ||
+ ip6_addr_ismulticast(&target_address)) {
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.proterr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ /* @todo RFC MUST: if IP destination is multicast, Solicited flag is zero */
+ /* @todo RFC MUST: all included options have a length greater than zero */
+
/* Unsolicited NA?*/
if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
/* This is an unsolicited NA.
* link-layer changed?
* part of DAD mechanism? */
- /* Check that link-layer address option also fits in packet. */
- if (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option))) {
- /* TODO debug message */
- pbuf_free(p);
- ND6_STATS_INC(nd6.lenerr);
- ND6_STATS_INC(nd6.drop);
- return;
- }
-
- lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
-
- /* Override ip6_current_dest_addr() so that we have an aligned copy. */
- ip6_addr_set(ip6_current_dest_addr(), &(na_hdr->target_address));
-
#if LWIP_IPV6_DUP_DETECT_ATTEMPTS
/* If the target address matches this netif, it is a DAD response. */
for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) {
+ if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) &&
+ !ip6_addr_isduplicated(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_MLD
- /* Leave solicited node multicast group. */
- ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(inp, i)->addr[3]);
- mld6_leavegroup(netif_ip6_addr(inp, i), &multicast_address);
-#endif /* LWIP_IPV6_MLD */
-
-
-
-
-#if LWIP_IPV6_AUTOCONFIG
- /* Check to see if this address was autoconfigured. */
- if (!ip6_addr_islinklocal(ip6_current_dest_addr())) {
- i = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp);
- if (i >= 0) {
- /* Mark this prefix as duplicate, so that we don't use it
- * to generate this address again. */
- prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE;
- }
- }
-#endif /* LWIP_IPV6_AUTOCONFIG */
+ nd6_duplicate_addr_detected(inp, i);
pbuf_free(p);
return;
@@ -194,24 +344,39 @@ nd6_input(struct pbuf *p, struct netif *inp)
}
#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(ip6_current_dest_addr());
+ 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 {
+ } else {
/* This is a solicited NA.
* neighbor address resolution response?
* neighbor unreachability detection response? */
- /* Override ip6_current_dest_addr() so that we have an aligned copy. */
- ip6_addr_set(ip6_current_dest_addr(), &(na_hdr->target_address));
-
/* Find the cache entry corresponding to this na. */
- i = nd6_find_neighbor_cache_entry(ip6_current_dest_addr());
+ i = nd6_find_neighbor_cache_entry(&target_address);
if (i < 0) {
/* We no longer care about this target address. drop it. */
pbuf_free(p);
@@ -219,13 +384,11 @@ nd6_input(struct pbuf *p, struct netif *inp)
}
/* Update cache entry. */
- neighbor_cache[i].netif = inp;
- neighbor_cache[i].counter.reachable_time = reachable_time;
if ((na_hdr->flags & ND6_FLAG_OVERRIDE) ||
(neighbor_cache[i].state == ND6_INCOMPLETE)) {
/* Check that link-layer address option also fits in packet. */
- if (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option))) {
- /* TODO debug message */
+ if (p->len < (sizeof(struct na_header) + 2)) {
+ /* @todo debug message */
pbuf_free(p);
ND6_STATS_INC(nd6.lenerr);
ND6_STATS_INC(nd6.drop);
@@ -234,9 +397,20 @@ nd6_input(struct pbuf *p, struct netif *inp)
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) {
@@ -248,13 +422,14 @@ nd6_input(struct pbuf *p, struct netif *inp)
}
case ICMP6_TYPE_NS: /* Neighbor solicitation. */
{
- struct ns_header * ns_hdr;
- struct lladdr_option * lladdr_opt;
+ struct ns_header *ns_hdr;
+ struct lladdr_option *lladdr_opt;
+ ip6_addr_t target_address;
u8_t accepted;
/* Check that ns header fits in packet. */
if (p->len < sizeof(struct ns_header)) {
- /* TODO debug message */
+ /* @todo debug message */
pbuf_free(p);
ND6_STATS_INC(nd6.lenerr);
ND6_STATS_INC(nd6.drop);
@@ -263,10 +438,31 @@ nd6_input(struct pbuf *p, struct netif *inp)
ns_hdr = (struct ns_header *)p->payload;
+ /* Create an aligned, zoned copy of the target address. */
+ ip6_addr_copy_from_packed(target_address, ns_hdr->target_address);
+ ip6_addr_assign_zone(&target_address, IP6_UNICAST, inp);
+
+ /* Check a subset of the other RFC 4861 Sec. 7.1.1 requirements. */
+ if (IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM || ns_hdr->code != 0 ||
+ ip6_addr_ismulticast(&target_address)) {
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.proterr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ /* @todo RFC MUST: all included options have a length greater than zero */
+ /* @todo RFC MUST: if IP source is 'any', destination is solicited-node multicast address */
+ /* @todo RFC MUST: if IP source is 'any', there is no source LL address option */
+
/* Check if there is a link-layer address provided. Only point to it if in this buffer. */
- lladdr_opt = NULL;
- if (p->len >= (sizeof(struct ns_header) + sizeof(struct lladdr_option))) {
+ 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. */
@@ -275,7 +471,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
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))) {
+ ip6_addr_cmp(&target_address, netif_ip6_addr(inp, i))) {
accepted = 1;
break;
}
@@ -291,17 +487,17 @@ nd6_input(struct pbuf *p, struct netif *inp)
if (ip6_addr_isany(ip6_current_src_addr())) {
/* Sender is validating this address. */
for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
- if (ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) {
+ if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) &&
+ ip6_addr_cmp(&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);
+ nd6_duplicate_addr_detected(inp, i);
}
}
}
- }
- else {
+ } else {
/* Sender is trying to resolve our address. */
/* Verify that they included their own link-layer address. */
if (lladdr_opt == NULL) {
@@ -313,7 +509,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
}
i = nd6_find_neighbor_cache_entry(ip6_current_src_addr());
- if ( i>= 0) {
+ if (i>= 0) {
/* We already have a record for the solicitor. */
if (neighbor_cache[i].state == ND6_INCOMPLETE) {
neighbor_cache[i].netif = inp;
@@ -321,11 +517,9 @@ nd6_input(struct pbuf *p, struct netif *inp)
/* 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;
+ neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
}
- }
- else
- {
+ } 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. */
@@ -344,27 +538,28 @@ nd6_input(struct pbuf *p, struct netif *inp)
/* 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;
+ neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
}
- /* Override ip6_current_dest_addr() so that we have an aligned copy. */
- ip6_addr_set(ip6_current_dest_addr(), &(ns_hdr->target_address));
-
/* Send back a NA for us. Allocate the reply pbuf. */
- nd6_send_na(inp, ip6_current_dest_addr(), ND6_FLAG_SOLICITED | ND6_FLAG_OVERRIDE);
+ 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. */
+ struct ra_header *ra_hdr;
+ u8_t *buffer; /* Used to copy options. */
u16_t offset;
+#if LWIP_ND6_RDNSS_MAX_DNS_SERVERS
+ /* There can be 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 */
+ /* @todo debug message */
pbuf_free(p);
ND6_STATS_INC(nd6.lenerr);
ND6_STATS_INC(nd6.drop);
@@ -373,9 +568,24 @@ nd6_input(struct pbuf *p, struct netif *inp)
ra_hdr = (struct ra_header *)p->payload;
+ /* Check a subset of the other RFC 4861 Sec. 6.1.2 requirements. */
+ if (!ip6_addr_islinklocal(ip6_current_src_addr()) ||
+ IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM || ra_hdr->code != 0) {
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.proterr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ /* @todo RFC MUST: all included options have a length greater than zero */
+
/* If we are sending RS messages, stop. */
#if LWIP_IPV6_SEND_ROUTER_SOLICIT
- inp->rs_count = 0;
+ /* 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. */
@@ -393,19 +603,19 @@ nd6_input(struct pbuf *p, struct netif *inp)
}
/* Re-set invalidation timer. */
- default_router_list[i].invalidation_timer = ra_hdr->router_lifetime;
+ 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 = ra_hdr->retrans_timer;
+ retrans_timer = lwip_htonl(ra_hdr->retrans_timer);
}
if (ra_hdr->reachable_time > 0) {
- reachable_time = ra_hdr->reachable_time;
+ reachable_time = lwip_htonl(ra_hdr->reachable_time);
}
#endif /* LWIP_ND6_ALLOW_RA_UPDATES */
- /* TODO set default hop limit... */
+ /* @todo set default hop limit... */
/* ra_hdr->current_hop_limit;*/
/* Update flags in local entry (incl. preference). */
@@ -415,18 +625,44 @@ nd6_input(struct pbuf *p, struct netif *inp)
offset = sizeof(struct ra_header);
/* Process each option. */
- while ((p->tot_len - offset) > 0) {
+ while ((p->tot_len - offset) >= 2) {
+ u8_t option_type;
+ u16_t option_len;
+ int option_len8 = pbuf_try_get_at(p, offset + 1);
+ if (option_len8 <= 0) {
+ /* read beyond end or zero length */
+ goto lenerr_drop_free_return;
+ }
+ option_len = ((u8_t)option_len8) << 3;
+ if (option_len > p->tot_len - offset) {
+ /* short packet (option does not fit in) */
+ goto lenerr_drop_free_return;
+ }
if (p->len == p->tot_len) {
/* no need to copy from contiguous pbuf */
buffer = &((u8_t*)p->payload)[offset];
} else {
- buffer = nd6_ra_buffer;
- pbuf_copy_partial(p, buffer, sizeof(struct prefix_option), offset);
+ /* check if this option fits into our buffer */
+ if (option_len > sizeof(nd6_ra_buffer)) {
+ option_type = pbuf_get_at(p, offset);
+ /* invalid option length */
+ if (option_type != ND6_OPTION_TYPE_RDNSS) {
+ goto lenerr_drop_free_return;
+ }
+ /* we allow RDNSS option to be longer - we'll just drop some servers */
+ option_len = sizeof(nd6_ra_buffer);
+ }
+ buffer = (u8_t*)&nd6_ra_buffer;
+ option_len = pbuf_copy_partial(p, &nd6_ra_buffer, option_len, offset);
}
- switch (buffer[0]) {
+ option_type = buffer[0];
+ switch (option_type) {
case ND6_OPTION_TYPE_SOURCE_LLADDR:
{
- struct lladdr_option * lladdr_opt;
+ struct lladdr_option *lladdr_opt;
+ if (option_len < sizeof(struct lladdr_option)) {
+ goto lenerr_drop_free_return;
+ }
lladdr_opt = (struct lladdr_option *)buffer;
if ((default_router_list[i].neighbor_entry != NULL) &&
(default_router_list[i].neighbor_entry->state == ND6_INCOMPLETE)) {
@@ -438,73 +674,125 @@ nd6_input(struct pbuf *p, struct netif *inp)
}
case ND6_OPTION_TYPE_MTU:
{
- struct mtu_option * mtu_opt;
+ struct mtu_option *mtu_opt;
+ if (option_len < sizeof(struct mtu_option)) {
+ goto lenerr_drop_free_return;
+ }
mtu_opt = (struct mtu_option *)buffer;
- if (mtu_opt->mtu >= 1280) {
+ if (lwip_htonl(mtu_opt->mtu) >= 1280) {
#if LWIP_ND6_ALLOW_RA_UPDATES
- inp->mtu = mtu_opt->mtu;
+ 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;
+ struct prefix_option *prefix_opt;
+ ip6_addr_t prefix_addr;
+ if (option_len < sizeof(struct prefix_option)) {
+ goto lenerr_drop_free_return;
+ }
+
prefix_opt = (struct prefix_option *)buffer;
- if (prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) {
- /* Add to on-link prefix list. */
+ /* Get a memory-aligned copy of the prefix. */
+ ip6_addr_copy_from_packed(prefix_addr, prefix_opt->prefix);
+ ip6_addr_assign_zone(&prefix_addr, IP6_UNICAST, inp);
- /* Get a memory-aligned copy of the prefix. */
- ip6_addr_set(ip6_current_dest_addr(), &(prefix_opt->prefix));
+ if (!ip6_addr_islinklocal(&prefix_addr)) {
+ if ((prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) &&
+ (prefix_opt->prefix_length == 64)) {
+ /* Add to on-link prefix list. */
+ u32_t valid_life;
+ s8_t prefix;
- /* find cache entry for this prefix. */
- i = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp);
- if (i < 0) {
- /* Create a new cache entry. */
- i = nd6_new_onlink_prefix(ip6_current_dest_addr(), inp);
- }
- if (i >= 0) {
- prefix_list[i].invalidation_timer = prefix_opt->valid_lifetime;
+ valid_life = 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[i].flags |= ND6_PREFIX_AUTOCONFIG_AUTONOMOUS;
+ /* find cache entry for this prefix. */
+ prefix = nd6_get_onlink_prefix(&prefix_addr, inp);
+ if (prefix < 0 && valid_life > 0) {
+ /* Create a new cache entry. */
+ prefix = nd6_new_onlink_prefix(&prefix_addr, inp);
+ }
+ if (prefix >= 0) {
+ prefix_list[prefix].invalidation_timer = valid_life;
}
-#endif /* LWIP_IPV6_AUTOCONFIG */
}
+#if LWIP_IPV6_AUTOCONFIG
+ if (prefix_opt->flags & ND6_PREFIX_FLAG_AUTONOMOUS) {
+ /* Perform processing for autoconfiguration. */
+ nd6_process_autoconfig_prefix(inp, prefix_opt, &prefix_addr);
+ }
+#endif /* LWIP_IPV6_AUTOCONFIG */
}
break;
}
case ND6_OPTION_TYPE_ROUTE_INFO:
- {
- /* TODO implement preferred routes.
+ /* @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;
+ u16_t copy_offset = offset + SIZEOF_RDNSS_OPTION_BASE;
+ struct rdnss_option * rdnss_opt;
+ if (option_len < SIZEOF_RDNSS_OPTION_BASE) {
+ goto lenerr_drop_free_return;
+ }
+
+ 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;
+
+ /* Copy directly from pbuf to get an aligned, zoned copy of the prefix. */
+ if (pbuf_copy_partial(p, &rdnss_address, sizeof(ip6_addr_p_t), copy_offset) == sizeof(ip6_addr_p_t)) {
+ IP_SET_TYPE_VAL(rdnss_address, IPADDR_TYPE_V6);
+ ip6_addr_assign_zone(ip_2_ip6(&rdnss_address), IP6_UNKNOWN, inp);
+
+ 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;
}
- offset += 8 * ((u16_t)buffer[1]);
+ /* option length is checked earlier to be non-zero to make sure loop ends */
+ offset += 8 * (u8_t)option_len8;
}
break; /* ICMP6_TYPE_RA */
}
case ICMP6_TYPE_RD: /* Redirect */
{
- struct redirect_header * redir_hdr;
- struct lladdr_option * lladdr_opt;
+ struct redirect_header *redir_hdr;
+ struct lladdr_option *lladdr_opt;
+ ip6_addr_t destination_address, target_address;
/* Check that Redir header fits in packet. */
if (p->len < sizeof(struct redirect_header)) {
- /* TODO debug message */
+ /* @todo debug message */
pbuf_free(p);
ND6_STATS_INC(nd6.lenerr);
ND6_STATS_INC(nd6.drop);
@@ -513,43 +801,63 @@ nd6_input(struct pbuf *p, struct netif *inp)
redir_hdr = (struct redirect_header *)p->payload;
- lladdr_opt = NULL;
- if (p->len >= (sizeof(struct redirect_header) + sizeof(struct lladdr_option))) {
- lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct redirect_header));
+ /* Create an aligned, zoned copy of the destination address. */
+ ip6_addr_copy_from_packed(destination_address, redir_hdr->destination_address);
+ ip6_addr_assign_zone(&destination_address, IP6_UNICAST, inp);
+
+ /* Check a subset of the other RFC 4861 Sec. 8.1 requirements. */
+ if (!ip6_addr_islinklocal(ip6_current_src_addr()) ||
+ IP6H_HOPLIM(ip6_current_header()) != ND6_HOPLIM ||
+ redir_hdr->code != 0 || ip6_addr_ismulticast(&destination_address)) {
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.proterr);
+ ND6_STATS_INC(nd6.drop);
+ return;
}
- /* Copy original destination address to current source address, to have an aligned copy. */
- ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->destination_address));
+ /* @todo RFC MUST: IP source address equals first-hop router for destination_address */
+ /* @todo RFC MUST: ICMP target address is either link-local address or same as destination_address */
+ /* @todo RFC MUST: all included options have a length greater than zero */
+
+ 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;
+ }
/* Find dest address in cache */
- i = nd6_find_destination_cache_entry(ip6_current_src_addr());
+ i = nd6_find_destination_cache_entry(&destination_address);
if (i < 0) {
/* Destination not in cache, drop packet. */
pbuf_free(p);
return;
}
+ /* Create an aligned, zoned copy of the target address. */
+ ip6_addr_copy_from_packed(target_address, redir_hdr->target_address);
+ ip6_addr_assign_zone(&target_address, IP6_UNICAST, inp);
+
/* Set the new target address. */
- ip6_addr_set(&(destination_cache[i].next_hop_addr), &(redir_hdr->target_address));
+ ip6_addr_copy(destination_cache[i].next_hop_addr, target_address);
/* If Link-layer address of other router is given, try to add to neighbor cache. */
if (lladdr_opt != NULL) {
if (lladdr_opt->type == ND6_OPTION_TYPE_TARGET_LLADDR) {
- /* Copy target address to current source address, to have an aligned copy. */
- ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->target_address));
-
- i = nd6_find_neighbor_cache_entry(ip6_current_src_addr());
+ i = nd6_find_neighbor_cache_entry(&target_address);
if (i < 0) {
i = nd6_new_neighbor_cache_entry();
if (i >= 0) {
neighbor_cache[i].netif = inp;
MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
- ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr());
+ ip6_addr_copy(neighbor_cache[i].next_hop_address, target_address);
/* 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;
+ neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
}
}
if (i >= 0) {
@@ -558,7 +866,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
/* 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;
+ neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME / ND6_TMR_INTERVAL;
}
}
}
@@ -568,7 +876,9 @@ nd6_input(struct pbuf *p, struct netif *inp)
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 */
+ struct ip6_hdr *ip6hdr; /* IPv6 header of the packet which caused the error */
+ u32_t pmtu;
+ ip6_addr_t destination_address;
/* Check that ICMPv6 header + IPv6 header fit in payload */
if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) {
@@ -582,11 +892,12 @@ nd6_input(struct pbuf *p, struct netif *inp)
icmp6hdr = (struct icmp6_hdr *)p->payload;
ip6hdr = (struct ip6_hdr *)((u8_t*)p->payload + sizeof(struct icmp6_hdr));
- /* Copy original destination address to current source address, to have an aligned copy. */
- ip6_addr_set(ip6_current_src_addr(), &(ip6hdr->dest));
+ /* Create an aligned, zoned copy of the destination address. */
+ ip6_addr_copy_from_packed(destination_address, ip6hdr->dest);
+ ip6_addr_assign_zone(&destination_address, IP6_UNKNOWN, inp);
/* Look for entry in destination cache. */
- i = nd6_find_destination_cache_entry(ip6_current_src_addr());
+ i = nd6_find_destination_cache_entry(&destination_address);
if (i < 0) {
/* Destination not in cache, drop packet. */
pbuf_free(p);
@@ -594,7 +905,8 @@ nd6_input(struct pbuf *p, struct netif *inp)
}
/* Change the Path MTU. */
- destination_cache[i].pmtu = icmp6hdr->data;
+ pmtu = lwip_htonl(icmp6hdr->data);
+ destination_cache[i].pmtu = (u16_t)LWIP_MIN(pmtu, 0xFFFF);
break; /* ICMP6_TYPE_PTB */
}
@@ -606,6 +918,11 @@ nd6_input(struct pbuf *p, struct netif *inp)
}
pbuf_free(p);
+ return;
+lenerr_drop_free_return:
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ pbuf_free(p);
}
@@ -615,6 +932,7 @@ nd6_input(struct pbuf *p, struct netif *inp)
* - Update neighbor reachability states
* - Update destination cache entries age
* - Update invalidation timers of default routers and on-link prefixes
+ * - Update lifetimes of our addresses
* - Perform duplicate address detection (DAD) for our addresses
* - Send router solicitations
*/
@@ -622,23 +940,20 @@ void
nd6_tmr(void)
{
s8_t i;
-#if LWIP_IPV6_AUTOCONFIG
- s8_t j;
-#endif
- struct netif * netif;
+ 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) {
+ 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 {
+ } else {
/* Send a NS for this entry. */
neighbor_cache[i].counter.probes_sent++;
- nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), ND6_SEND_FLAG_MULTICAST_DEST);
+ nd6_send_neighbor_cache_probe(&neighbor_cache[i], ND6_SEND_FLAG_MULTICAST_DEST);
}
break;
case ND6_REACHABLE:
@@ -650,33 +965,31 @@ nd6_tmr(void)
/* Change to stale state. */
neighbor_cache[i].state = ND6_STALE;
neighbor_cache[i].counter.stale_time = 0;
- }
- else {
+ } else {
neighbor_cache[i].counter.reachable_time -= ND6_TMR_INTERVAL;
}
break;
case ND6_STALE:
- neighbor_cache[i].counter.stale_time += ND6_TMR_INTERVAL;
+ neighbor_cache[i].counter.stale_time++;
break;
case ND6_DELAY:
- if (neighbor_cache[i].counter.delay_time <= ND6_TMR_INTERVAL) {
+ 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 -= ND6_TMR_INTERVAL;
+ } else {
+ neighbor_cache[i].counter.delay_time--;
}
break;
case ND6_PROBE:
- if (neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) {
+ 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 {
+ } else {
/* Send a NS for this entry. */
neighbor_cache[i].counter.probes_sent++;
- nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), 0);
+ nd6_send_neighbor_cache_probe(&neighbor_cache[i], 0);
}
break;
case ND6_NO_ENTRY:
@@ -695,81 +1008,112 @@ nd6_tmr(void)
for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
if (default_router_list[i].neighbor_entry != NULL) {
/* Active entry. */
- if (default_router_list[i].invalidation_timer > 0) {
- default_router_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
- }
- if (default_router_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) {
- /* Less than 1 second remainig. Clear this entry. */
+ if (default_router_list[i].invalidation_timer <= ND6_TMR_INTERVAL / 1000) {
+ /* No more than 1 second remaining. Clear this entry. Also clear any of
+ * its destination cache entries, as per RFC 4861 Sec. 5.3 and 6.3.5. */
+ s8_t j;
+ for (j = 0; j < LWIP_ND6_NUM_DESTINATIONS; j++) {
+ if (ip6_addr_cmp(&destination_cache[j].next_hop_addr,
+ &default_router_list[i].neighbor_entry->next_hop_address)) {
+ ip6_addr_set_any(&destination_cache[j].destination_addr);
+ }
+ }
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;
+ } else {
+ default_router_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
}
}
}
/* Process prefix entries. */
for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
- if (prefix_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) {
- prefix_list[i].invalidation_timer = 0;
- }
- if ((prefix_list[i].invalidation_timer > 0) &&
- (prefix_list[i].netif != NULL)) {
- prefix_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
-
-#if LWIP_IPV6_AUTOCONFIG
- /* Initiate address autoconfiguration for this prefix, if conditions are met. */
- if (prefix_list[i].netif->ip6_autoconfig_enabled &&
- (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_AUTONOMOUS) &&
- !(prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED)) {
- /* Try to get an address on this netif that is invalid.
- * Skip 0 index (link-local address) */
- for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) {
- if (netif_ip6_addr_state(prefix_list[i].netif, j) == IP6_ADDRESS_STATE_INVALID) {
- /* Generate an address using this prefix and interface ID from link-local address. */
- prefix_list[i].netif->ip6_addr[j].addr[0] = prefix_list[i].prefix.addr[0];
- prefix_list[i].netif->ip6_addr[j].addr[1] = prefix_list[i].prefix.addr[1];
- prefix_list[i].netif->ip6_addr[j].addr[2] = prefix_list[i].netif->ip6_addr[0].addr[2];
- prefix_list[i].netif->ip6_addr[j].addr[3] = prefix_list[i].netif->ip6_addr[0].addr[3];
-
- /* Mark it as tentative (DAD will be performed if configured). */
- netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_TENTATIVE);
-
- /* Mark this prefix with ADDRESS_GENERATED, so that we don't try again. */
- prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED;
-
- /* Exit loop. */
- break;
- }
- }
+ 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;
+ prefix_list[i].netif = NULL;
+ } else {
+ prefix_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
}
-#endif /* LWIP_IPV6_AUTOCONFIG */
}
}
-
- /* Process our own addresses, if DAD configured. */
- for (netif = netif_list; netif != NULL; netif = netif->next) {
+ /* Process our own addresses, updating address lifetimes and/or DAD state. */
+ NETIF_FOREACH(netif) {
for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
- if (ip6_addr_istentative(netif->ip6_addr_state[i])) {
- if ((netif->ip6_addr_state[i] & 0x07) >= LWIP_IPV6_DUP_DETECT_ATTEMPTS) {
- /* No NA received in response. Mark address as valid. */
- netif->ip6_addr_state[i] = IP6_ADDR_PREFERRED;
- /* TODO implement preferred and valid lifetimes. */
+ u8_t addr_state;
+#if LWIP_IPV6_ADDRESS_LIFETIMES
+ /* Step 1: update address lifetimes (valid and preferred). */
+ addr_state = netif_ip6_addr_state(netif, i);
+ /* RFC 4862 is not entirely clear as to whether address lifetimes affect
+ * tentative addresses, and is even less clear as to what should happen
+ * with duplicate addresses. We choose to track and update lifetimes for
+ * both those types, although for different reasons:
+ * - for tentative addresses, the line of thought of Sec. 5.7 combined
+ * with the potentially long period that an address may be in tentative
+ * state (due to the interface being down) suggests that lifetimes
+ * should be independent of external factors which would include DAD;
+ * - for duplicate addresses, retiring them early could result in a new
+ * but unwanted attempt at marking them as valid, while retiring them
+ * late/never could clog up address slots on the netif.
+ * As a result, we may end up expiring addresses of either type here.
+ */
+ if (!ip6_addr_isinvalid(addr_state) &&
+ !netif_ip6_addr_isstatic(netif, i)) {
+ u32_t life = netif_ip6_addr_valid_life(netif, i);
+ if (life <= ND6_TMR_INTERVAL / 1000) {
+ /* The address has expired. */
+ netif_ip6_addr_set_valid_life(netif, i, 0);
+ netif_ip6_addr_set_pref_life(netif, i, 0);
+ netif_ip6_addr_set_state(netif, i, IP6_ADDR_INVALID);
+ } else {
+ if (!ip6_addr_life_isinfinite(life)) {
+ life -= ND6_TMR_INTERVAL / 1000;
+ LWIP_ASSERT("bad valid lifetime", life != IP6_ADDR_LIFE_STATIC);
+ netif_ip6_addr_set_valid_life(netif, i, life);
+ }
+ /* The address is still here. Update the preferred lifetime too. */
+ life = netif_ip6_addr_pref_life(netif, i);
+ if (life <= ND6_TMR_INTERVAL / 1000) {
+ /* This case must also trigger if 'life' was already zero, so as to
+ * deal correctly with advertised preferred-lifetime reductions. */
+ netif_ip6_addr_set_pref_life(netif, i, 0);
+ if (addr_state == IP6_ADDR_PREFERRED)
+ netif_ip6_addr_set_state(netif, i, IP6_ADDR_DEPRECATED);
+ } else if (!ip6_addr_life_isinfinite(life)) {
+ life -= ND6_TMR_INTERVAL / 1000;
+ netif_ip6_addr_set_pref_life(netif, i, life);
+ }
}
- else if (netif->flags & NETIF_FLAG_UP) {
-#if LWIP_IPV6_MLD
- if ((netif->ip6_addr_state[i] & 0x07) == 0) {
- /* Join solicited node multicast group. */
- ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, i)->addr[3]);
- mld6_joingroup(netif_ip6_addr(netif, i), &multicast_address);
+ }
+ /* The address state may now have changed, so reobtain it next. */
+#endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
+ /* Step 2: update DAD state. */
+ 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. For dynamic
+ * addresses with an expired preferred lifetime, the state is set to
+ * deprecated right away. That should almost never happen, though. */
+ addr_state = IP6_ADDR_PREFERRED;
+#if LWIP_IPV6_ADDRESS_LIFETIMES
+ if (!netif_ip6_addr_isstatic(netif, i) &&
+ netif_ip6_addr_pref_life(netif, i) == 0) {
+ addr_state = IP6_ADDR_DEPRECATED;
}
-#endif /* LWIP_IPV6_MLD */
- /* Send a NS for this address. */
- nd6_send_ns(netif, netif_ip6_addr(netif, i), ND6_SEND_FLAG_MULTICAST_DEST);
- (netif->ip6_addr_state[i])++;
- /* TODO send max 1 NS per tmr call? enable return*/
- /*return;*/
+#endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
+ netif_ip6_addr_set_state(netif, i, addr_state);
+ } else if (netif_is_up(netif) && netif_is_link_up(netif)) {
+ /* tentative: set next state by increasing by one */
+ netif_ip6_addr_set_state(netif, i, addr_state + 1);
+ /* Send a NS for this address. Use the unspecified address as source
+ * address in all cases (RFC 4862 Sec. 5.4.2), not in the least
+ * because as it is, we only consider multicast replies for DAD. */
+ nd6_send_ns(netif, netif_ip6_addr(netif, i),
+ ND6_SEND_FLAG_MULTICAST_DEST | ND6_SEND_FLAG_ANY_SRC);
}
}
}
@@ -777,16 +1121,31 @@ nd6_tmr(void)
#if LWIP_IPV6_SEND_ROUTER_SOLICIT
/* Send router solicitation messages, if necessary. */
- for (netif = netif_list; netif != NULL; netif = netif->next) {
- if ((netif->rs_count > 0) && (netif->flags & NETIF_FLAG_UP)) {
- nd6_send_rs(netif);
- netif->rs_count--;
+ NETIF_FOREACH(netif) {
+ if ((netif->rs_count > 0) && netif_is_up(netif) &&
+ netif_is_link_up(netif) &&
+ !ip6_addr_isinvalid(netif_ip6_addr_state(netif, 0)) &&
+ !ip6_addr_isduplicated(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
*
@@ -795,58 +1154,68 @@ nd6_tmr(void)
* @param flags one of ND6_SEND_FLAG_*
*/
static void
-nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags)
+nd6_send_ns(struct netif *netif, const ip6_addr_t *target_addr, u8_t flags)
{
- struct ns_header * ns_hdr;
- struct lladdr_option * lladdr_opt;
- struct pbuf * p;
- ip6_addr_t * src_addr;
+ 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))) {
+ LWIP_ASSERT("target address is required", target_addr != NULL);
+
+ if (!(flags & ND6_SEND_FLAG_ANY_SRC) &&
+ 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_ANY;
+ 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) + sizeof(struct lladdr_option), PBUF_RAM);
- if ((p == NULL) || (p->len < (sizeof(struct ns_header) + sizeof(struct lladdr_option)))) {
- /* We couldn't allocate a suitable pbuf for the ns. drop it. */
- if (p != NULL) {
- pbuf_free(p);
- }
+ 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;
- lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header));
ns_hdr->type = ICMP6_TYPE_NS;
ns_hdr->code = 0;
ns_hdr->chksum = 0;
ns_hdr->reserved = 0;
- ip6_addr_set(&(ns_hdr->target_address), target_addr);
+ ip6_addr_copy_to_packed(ns_hdr->target_address, *target_addr);
- lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR;
- lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
- SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len);
+ 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]);
+ ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif);
target_addr = &multicast_address;
}
- ns_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
- target_addr);
+#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_ANY) ? NULL : src_addr, target_addr,
- LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
+ ip6_output_if(p, (src_addr == IP6_ADDR_ANY6) ? NULL : src_addr, target_addr,
+ ND6_HOPLIM, 0, IP6_NEXTH_ICMP6, netif);
pbuf_free(p);
}
@@ -858,26 +1227,26 @@ nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags)
* @param flags one of ND6_SEND_FLAG_*
*/
static void
-nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags)
+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;
- ip6_addr_t * src_addr;
- ip6_addr_t * dest_addr;
+ 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;
+
+ LWIP_ASSERT("target address is required", target_addr != NULL);
/* Use link-local address as source address. */
- /* src_addr = &(netif->ip6_addr[0]); */
+ /* src_addr = netif_ip6_addr(netif, 0); */
/* Use target address as source address. */
src_addr = target_addr;
/* Allocate a packet. */
- p = pbuf_alloc(PBUF_IP, sizeof(struct na_header) + sizeof(struct lladdr_option), PBUF_RAM);
- if ((p == NULL) || (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option)))) {
- /* We couldn't allocate a suitable pbuf for the ns. drop it. */
- if (p != NULL) {
- pbuf_free(p);
- }
+ 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;
}
@@ -893,32 +1262,36 @@ nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags)
na_hdr->reserved[0] = 0;
na_hdr->reserved[1] = 0;
na_hdr->reserved[2] = 0;
- ip6_addr_set(&(na_hdr->target_address), target_addr);
+ ip6_addr_copy_to_packed(na_hdr->target_address, *target_addr);
lladdr_opt->type = ND6_OPTION_TYPE_TARGET_LLADDR;
- lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
+ 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]);
+ ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif);
dest_addr = &multicast_address;
- }
- else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) {
+ } else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) {
ip6_addr_set_allnodes_linklocal(&multicast_address);
+ ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif);
dest_addr = &multicast_address;
- }
- else {
+ } else {
dest_addr = ip6_current_src_addr();
}
- na_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
- dest_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);
+ ND6_HOPLIM, 0, IP6_NEXTH_ICMP6, netif);
pbuf_free(p);
}
@@ -928,39 +1301,35 @@ nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags)
*
* @param netif the netif on which to send the message
*/
-static void
-nd6_send_rs(struct netif * netif)
+static err_t
+nd6_send_rs(struct netif *netif)
{
- struct rs_header * rs_hdr;
- struct lladdr_option * lladdr_opt;
- struct pbuf * p;
- ip6_addr_t * src_addr;
- u16_t packet_len;
+ 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_ANY;
+ } else {
+ src_addr = IP6_ADDR_ANY6;
}
/* Generate the all routers target address. */
ip6_addr_set_allrouters_linklocal(&multicast_address);
+ ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif);
/* Allocate a packet. */
- packet_len = sizeof(struct rs_header);
- if (src_addr != IP6_ADDR_ANY) {
- packet_len += sizeof(struct lladdr_option);
+ 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, packet_len, PBUF_RAM);
- if ((p == NULL) || (p->len < packet_len)) {
- /* We couldn't allocate a suitable pbuf for the ns. drop it. */
- if (p != NULL) {
- pbuf_free(p);
- }
+ p = pbuf_alloc(PBUF_IP, sizeof(struct rs_header) + (lladdr_opt_len << 3), PBUF_RAM);
+ if (p == NULL) {
ND6_STATS_INC(nd6.memerr);
- return;
+ return ERR_BUF;
}
/* Set fields. */
@@ -971,22 +1340,29 @@ nd6_send_rs(struct netif * netif)
rs_hdr->chksum = 0;
rs_hdr->reserved = 0;
- if (src_addr != IP6_ADDR_ANY) {
+ 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 = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
+ lladdr_opt->length = (u8_t)lladdr_opt_len;
SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len);
}
- rs_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
- &multicast_address);
+#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);
- ip6_output_if(p, src_addr, &multicast_address,
- LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
+
+ err = ip6_output_if(p, (src_addr == IP6_ADDR_ANY6) ? NULL : src_addr, &multicast_address,
+ ND6_HOPLIM, 0, IP6_NEXTH_ICMP6, netif);
pbuf_free(p);
+
+ return err;
}
#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
@@ -998,7 +1374,7 @@ nd6_send_rs(struct netif * netif)
* entry is found
*/
static s8_t
-nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr)
+nd6_find_neighbor_cache_entry(const ip6_addr_t *ip6addr)
{
s8_t i;
for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
@@ -1131,6 +1507,10 @@ 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) {
@@ -1153,9 +1533,12 @@ nd6_free_neighbor_cache_entry(s8_t i)
* entry is found
*/
static s8_t
-nd6_find_destination_cache_entry(ip6_addr_t * ip6addr)
+nd6_find_destination_cache_entry(const ip6_addr_t *ip6addr)
{
s8_t i;
+
+ IP6_ADDR_ZONECHECK(ip6addr);
+
for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
if (ip6_addr_cmp(ip6addr, &(destination_cache[i].destination_addr))) {
return i;
@@ -1197,15 +1580,34 @@ nd6_new_destination_cache_entry(void)
}
/**
- * Determine whether an address matches an on-link prefix.
+ * 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 or the subnet of a
+ * statically assigned address.
*
* @param ip6addr the IPv6 address to match
* @return 1 if the address is on-link, 0 otherwise
*/
static s8_t
-nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif)
+nd6_is_prefix_in_netif(const ip6_addr_t *ip6addr, struct netif *netif)
{
s8_t i;
+
+ /* Check to see if the address matches an on-link prefix. */
for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
if ((prefix_list[i].netif == netif) &&
(prefix_list[i].invalidation_timer > 0) &&
@@ -1213,9 +1615,13 @@ nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif)
return 1;
}
}
- /* Check to see if address prefix matches a (manually?) configured address. */
+ /* Check to see if address prefix matches a manually configured (= static)
+ * address. Static addresses have an implied /64 subnet assignment. Dynamic
+ * addresses (from autoconfiguration) have no implied subnet assignment, and
+ * are thus effectively /128 assignments. See RFC 5942 for more on this. */
for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ netif_ip6_addr_isstatic(netif, i) &&
ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i))) {
return 1;
}
@@ -1226,56 +1632,71 @@ nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif)
/**
* Select a default router for a destination.
*
+ * This function is used both for routing and for finding a next-hop target for
+ * a packet. In the former case, the given netif is NULL, and the returned
+ * router entry must be for a netif suitable for sending packets (up, link up).
+ * In the latter case, the given netif is not NULL and restricts router choice.
+ *
* @param ip6addr the destination address
* @param netif the netif for the outgoing packet, if known
* @return the default router entry index, or -1 if no suitable
* router is found
*/
-s8_t
-nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif)
+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. */
+ struct netif *router_netif;
+ s8_t i, j, valid_router;
static s8_t last_router;
- (void)ip6addr; /* TODO match preferred routes!! (must implement ND6_OPTION_TYPE_ROUTE_INFO) */
- /* TODO: implement default router preference */
+ LWIP_UNUSED_ARG(ip6addr); /* @todo match preferred routes!! (must implement ND6_OPTION_TYPE_ROUTE_INFO) */
- /* 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;
- }
- }
+ /* @todo: implement default router preference */
- /* Look for router in other reachability states, but still valid according to timer. */
+ /* Look for valid routers. A reachable router is preferred. */
+ valid_router = -1;
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;
+ /* Is the router netif both set and apppropriate? */
+ if (default_router_list[i].neighbor_entry != NULL) {
+ router_netif = default_router_list[i].neighbor_entry->netif;
+ if ((router_netif != NULL) && (netif != NULL ? netif == router_netif :
+ (netif_is_up(router_netif) && netif_is_link_up(router_netif)))) {
+ /* Is the router valid, i.e., reachable or probably reachable as per
+ * RFC 4861 Sec. 6.3.6? Note that we will never return a router that
+ * has no neighbor cache entry, due to the netif association tests. */
+ if (default_router_list[i].neighbor_entry->state != ND6_INCOMPLETE) {
+ /* Is the router known to be reachable? */
+ if (default_router_list[i].neighbor_entry->state == ND6_REACHABLE) {
+ return i; /* valid and reachable - done! */
+ } else if (valid_router < 0) {
+ valid_router = i; /* valid but not known to be reachable */
+ }
+ }
+ }
}
}
+ if (valid_router >= 0) {
+ return valid_router;
+ }
/* 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;
+ /* last_router is used for round-robin selection of incomplete routers, as
+ * recommended in RFC 4861 Sec. 6.3.6 point (2). Advance only when picking a
+ * route, to select the same router as next-hop target in the common case. */
+ if ((netif == NULL) && (++last_router >= LWIP_ND6_NUM_ROUTERS)) {
+ last_router = 0;
+ }
+ i = last_router;
+ for (j = 0; j < LWIP_ND6_NUM_ROUTERS; j++) {
+ if (default_router_list[i].neighbor_entry != NULL) {
+ router_netif = default_router_list[i].neighbor_entry->netif;
+ if ((router_netif != NULL) && (netif != NULL ? netif == router_netif :
+ (netif_is_up(router_netif) && netif_is_link_up(router_netif)))) {
+ return i;
+ }
}
- if (default_router_list[i].neighbor_entry != NULL &&
- (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1)) {
- return i;
+ if (++i >= LWIP_ND6_NUM_ROUTERS) {
+ i = 0;
}
}
@@ -1284,6 +1705,45 @@ nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif)
}
/**
+ * Find a router-announced route to the given destination. This route may be
+ * based on an on-link prefix or a default router.
+ *
+ * If a suitable route is found, the returned netif is guaranteed to be 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)
+{
+ struct netif *netif;
+ s8_t i;
+
+ /* @todo decide if it makes sense to check the destination cache first */
+
+ /* Check if there is a matching on-link prefix. There may be multiple
+ * matches. Pick the first one that is associated with a suitable netif. */
+ for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) {
+ netif = prefix_list[i].netif;
+ if ((netif != NULL) && ip6_addr_netcmp(&prefix_list[i].prefix, ip6addr) &&
+ netif_is_up(netif) && netif_is_link_up(netif)) {
+ return netif;
+ }
+ }
+
+ /* No on-link prefix match. Find a router that can forward the packet. */
+ i = nd6_select_router(ip6addr, NULL);
+ if (i >= 0) {
+ LWIP_ASSERT("selected router must have a neighbor entry",
+ default_router_list[i].neighbor_entry != NULL);
+ return default_router_list[i].neighbor_entry->netif;
+ }
+
+ return NULL;
+}
+
+/**
* Find an entry for a default router.
*
* @param router_addr the IPv6 address of the router
@@ -1291,10 +1751,12 @@ nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif)
* @return the index of the router entry, or -1 if not found
*/
static s8_t
-nd6_get_router(ip6_addr_t * router_addr, struct netif * netif)
+nd6_get_router(const ip6_addr_t *router_addr, struct netif *netif)
{
s8_t i;
+ IP6_ADDR_ZONECHECK_NETIF(router_addr, netif);
+
/* Look for router. */
for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
if ((default_router_list[i].neighbor_entry != NULL) &&
@@ -1316,11 +1778,14 @@ nd6_get_router(ip6_addr_t * router_addr, struct netif * netif)
* @return the index on the router table, or -1 if could not be created
*/
static s8_t
-nd6_new_router(ip6_addr_t * router_addr, struct netif * netif)
+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;
+ IP6_ADDR_ZONECHECK_NETIF(router_addr, netif);
+
/* Do we have a neighbor entry for this router? */
neighbor_index = nd6_find_neighbor_cache_entry(router_addr);
if (neighbor_index < 0) {
@@ -1334,19 +1799,30 @@ nd6_new_router(ip6_addr_t * router_addr, struct netif * netif)
neighbor_cache[neighbor_index].netif = netif;
neighbor_cache[neighbor_index].q = NULL;
neighbor_cache[neighbor_index].state = ND6_INCOMPLETE;
- neighbor_cache[neighbor_index].counter.probes_sent = 0;
+ 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. */
- for (router_index = 0; router_index < LWIP_ND6_NUM_ROUTERS; router_index++) {
+ 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) {
- default_router_list[router_index].neighbor_entry = &(neighbor_cache[neighbor_index]);
- return router_index;
+ /* 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. */
@@ -1365,7 +1841,7 @@ nd6_new_router(ip6_addr_t * router_addr, struct netif * netif)
* @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)
+nd6_get_onlink_prefix(const ip6_addr_t *prefix, struct netif *netif)
{
s8_t i;
@@ -1389,7 +1865,7 @@ nd6_get_onlink_prefix(ip6_addr_t * prefix, struct netif * netif)
* @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)
+nd6_new_onlink_prefix(const ip6_addr_t *prefix, struct netif *netif)
{
s8_t i;
@@ -1400,9 +1876,6 @@ nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif)
/* 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
return i;
}
}
@@ -1423,15 +1896,20 @@ nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif)
* suitable next hop was found, ERR_MEM if no cache entry
* could be created
*/
-s8_t
-nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif)
+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;
+ IP6_ADDR_ZONECHECK_NETIF(ip6addr, netif);
+
#if LWIP_NETIF_HWADDRHINT
- if (netif->addr_hint != NULL) {
+ if (netif->hints != NULL) {
/* per-pcb cached entry was given */
- u8_t addr_hint = *(netif->addr_hint);
+ u8_t addr_hint = netif->hints->addr_hint;
if (addr_hint < LWIP_ND6_NUM_DESTINATIONS) {
nd6_cached_destination_index = addr_hint;
}
@@ -1449,8 +1927,7 @@ nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif)
if (i >= 0) {
/* found destination entry. make it our new cached index. */
nd6_cached_destination_index = i;
- }
- else {
+ } else {
/* Not found. Create a new destination entry. */
i = nd6_new_destination_cache_entry();
if (i >= 0) {
@@ -1470,8 +1947,13 @@ nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif)
/* Destination in local link. */
destination_cache[nd6_cached_destination_index].pmtu = netif->mtu;
ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, destination_cache[nd6_cached_destination_index].destination_addr);
- }
- else {
+#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) {
@@ -1486,9 +1968,9 @@ nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif)
}
#if LWIP_NETIF_HWADDRHINT
- if (netif->addr_hint != NULL) {
+ if (netif->hints != NULL) {
/* per-pcb cached entry was given */
- *(netif->addr_hint) = nd6_cached_destination_index;
+ netif->hints->addr_hint = nd6_cached_destination_index;
}
#endif /* LWIP_NETIF_HWADDRHINT */
@@ -1503,8 +1985,7 @@ nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif)
if (i >= 0) {
/* Found a matching record, make it new cached entry. */
nd6_cached_neighbor_index = i;
- }
- else {
+ } else {
/* Neighbor not in cache. Make a new entry. */
i = nd6_new_neighbor_cache_entry();
if (i >= 0) {
@@ -1521,7 +2002,8 @@ nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif)
neighbor_cache[i].isrouter = 0;
neighbor_cache[i].netif = netif;
neighbor_cache[i].state = ND6_INCOMPLETE;
- neighbor_cache[i].counter.probes_sent = 0;
+ neighbor_cache[i].counter.probes_sent = 1;
+ nd6_send_neighbor_cache_probe(&neighbor_cache[i], ND6_SEND_FLAG_MULTICAST_DEST);
}
}
@@ -1538,8 +2020,8 @@ nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif)
* @param q packet to be queued
* @return ERR_OK if succeeded, ERR_MEM if out of memory
*/
-err_t
-nd6_queue_packet(s8_t neighbor_index, struct pbuf * q)
+static err_t
+nd6_queue_packet(s8_t neighbor_index, struct pbuf *q)
{
err_t result = ERR_MEM;
struct pbuf *p;
@@ -1552,18 +2034,17 @@ nd6_queue_packet(s8_t neighbor_index, struct pbuf * q)
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. */
+ /* IF q includes a pbuf that must be copied, we have to copy the whole chain
+ * into a new PBUF_RAM. See the definition of PBUF_NEEDS_COPY for details. */
p = q;
while (p) {
- if(p->type != PBUF_ROM) {
+ if (PBUF_NEEDS_COPY(p)) {
copy_needed = 1;
break;
}
p = p->next;
}
- if(copy_needed) {
+ 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)) {
@@ -1579,7 +2060,7 @@ nd6_queue_packet(s8_t neighbor_index, struct pbuf * q)
#endif /* LWIP_ND6_QUEUEING */
p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM);
}
- if(p != NULL) {
+ if (p != NULL) {
if (pbuf_copy(p, q) != ERR_OK) {
pbuf_free(p);
p = NULL;
@@ -1607,7 +2088,7 @@ nd6_queue_packet(s8_t neighbor_index, struct pbuf * q)
if (new_entry != NULL) {
new_entry->next = NULL;
new_entry->p = p;
- if(neighbor_cache[neighbor_index].q != NULL) {
+ 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) {
@@ -1674,6 +2155,7 @@ 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 */
@@ -1690,10 +2172,12 @@ nd6_send_q(s8_t i)
neighbor_cache[i].q = q->next;
/* Get ipv6 header. */
ip6hdr = (struct ip6_hdr *)(q->p->payload);
- /* Override ip6_current_dest_addr() so that we have an aligned copy. */
- ip6_addr_set(ip6_current_dest_addr(), &(ip6hdr->dest));
+ /* Create an aligned copy. */
+ ip6_addr_copy_from_packed(dest, ip6hdr->dest);
+ /* Restore the zone, if applicable. */
+ ip6_addr_assign_zone(&dest, IP6_UNKNOWN, neighbor_cache[i].netif);
/* send the queued IPv6 packet */
- (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, q->p, ip6_current_dest_addr());
+ (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 */
@@ -1703,10 +2187,12 @@ nd6_send_q(s8_t i)
if (neighbor_cache[i].q != NULL) {
/* Get ipv6 header. */
ip6hdr = (struct ip6_hdr *)(neighbor_cache[i].q->payload);
- /* Override ip6_current_dest_addr() so that we have an aligned copy. */
- ip6_addr_set(ip6_current_dest_addr(), &(ip6hdr->dest));
+ /* Create an aligned copy. */
+ ip6_addr_copy_from_packed(dest, ip6hdr->dest);
+ /* Restore the zone, if applicable. */
+ ip6_addr_assign_zone(&dest, IP6_UNKNOWN, neighbor_cache[i].netif);
/* send the queued IPv6 packet */
- (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, neighbor_cache[i].q, ip6_current_dest_addr());
+ (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;
@@ -1714,6 +2200,61 @@ nd6_send_q(s8_t i)
#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.
@@ -1723,7 +2264,7 @@ nd6_send_q(s8_t i)
* @return the Path MTU, if known, or the netif default MTU
*/
u16_t
-nd6_get_destination_mtu(ip6_addr_t * ip6addr, struct netif * netif)
+nd6_get_destination_mtu(const ip6_addr_t *ip6addr, struct netif *netif)
{
s8_t i;
@@ -1753,7 +2294,7 @@ nd6_get_destination_mtu(ip6_addr_t * ip6addr, struct netif * netif)
* by an upper layer protocol (TCP)
*/
void
-nd6_reachability_hint(ip6_addr_t * ip6addr)
+nd6_reachability_hint(const ip6_addr_t *ip6addr)
{
s8_t i;
@@ -1761,8 +2302,7 @@ nd6_reachability_hint(ip6_addr_t * ip6addr)
if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) {
i = nd6_cached_destination_index;
ND6_STATS_INC(nd6.cachehit);
- }
- else {
+ } else {
i = nd6_find_destination_cache_entry(ip6addr);
}
if (i < 0) {
@@ -1773,18 +2313,90 @@ nd6_reachability_hint(ip6_addr_t * ip6addr)
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 {
+ } 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;
+ }
+ }
+ 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);
+ }
+ }
+ /* Clear the destination cache, since many entries may now have become
+ * invalid for one of several reasons. As destination cache entries have no
+ * netif association, use a sledgehammer approach (this can be improved). */
+ nd6_clear_destination_cache();
+}
+
+#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_DUPLICATED && old_state != IP6_ADDR_TENTATIVE);
+ new_member = (new_state != IP6_ADDR_INVALID && new_state != IP6_ADDR_DUPLICATED && new_state != IP6_ADDR_TENTATIVE);
+
+ if (old_member != new_member) {
+ ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, addr_idx)->addr[3]);
+ ip6_addr_assign_zone(&multicast_address, IP6_MULTICAST, netif);
+
+ 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/lwip/src/core/mem.c b/lwip/src/core/mem.c
index 1659a2c..8a77a67 100644
--- a/lwip/src/core/mem.c
+++ b/lwip/src/core/mem.c
@@ -9,7 +9,7 @@
*
* To let mem_malloc() use pools (prevents fragmentation and is much faster than
* a heap but might waste some memory), define MEM_USE_POOLS to 1, define
- * MEM_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list
+ * 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
@@ -54,19 +54,110 @@
*/
#include "lwip/opt.h"
-
-#if !MEM_LIBC_MALLOC /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/def.h"
#include "lwip/mem.h"
+#include "lwip/def.h"
#include "lwip/sys.h"
#include "lwip/stats.h"
#include "lwip/err.h"
#include <string.h>
-#if MEM_USE_POOLS
-/* lwIP head implemented with different sized pools */
+#if MEM_LIBC_MALLOC
+#include <stdlib.h> /* for malloc()/free() */
+#endif
+
+#define MEM_STATS_INC_LOCKED(x) SYS_ARCH_LOCKED(MEM_STATS_INC(x))
+#define MEM_STATS_INC_USED_LOCKED(x, y) SYS_ARCH_LOCKED(MEM_STATS_INC_USED(x, y))
+#define MEM_STATS_DEC_USED_LOCKED(x, y) SYS_ARCH_LOCKED(MEM_STATS_DEC_USED(x, y))
+
+#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_LOCKED(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_LOCKED(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_LOCKED(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
@@ -79,43 +170,49 @@ void *
mem_malloc(mem_size_t size)
{
void *ret;
- struct memp_malloc_helper *element;
+ 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)) {
-#if MEM_USE_POOLS_TRY_BIGGER_POOL
-again:
-#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
/* is this pool big enough to hold an element of the required size
plus a struct memp_malloc_helper that saves the pool this element came from? */
- if (required_size <= memp_sizes[poolnr]) {
+ 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_LOCKED(err);
+ return NULL;
+ }
break;
}
}
if (poolnr > MEMP_POOL_LAST) {
LWIP_ASSERT("mem_malloc(): no pool is that big!", 0);
- return NULL;
- }
- element = (struct memp_malloc_helper*)memp_malloc(poolnr);
- if (element == NULL) {
- /* No need to DEBUGF or ASSERT: This error is already
- taken care of in memp.c */
-#if MEM_USE_POOLS_TRY_BIGGER_POOL
- /** Try a bigger pool if this one is empty! */
- if (poolnr < MEMP_POOL_LAST) {
- poolnr++;
- goto again;
- }
-#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
+ MEM_STATS_INC_LOCKED(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));
-
+ 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_LOCKED(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;
}
@@ -135,12 +232,27 @@ mem_free(void *rmem)
LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem)));
/* get the original struct memp_malloc_helper */
- hmem = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)));
+ /* 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_LOCKED(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);
}
@@ -151,7 +263,7 @@ mem_free(void *rmem)
/**
* The heap is made up as a list of structs of this type.
* This does not have to be aligned since for getting its size,
- * we only use the macro SIZEOF_STRUCT_MEM, which automatically alignes.
+ * we only use the macro SIZEOF_STRUCT_MEM, which automatically aligns.
*/
struct mem {
/** index (-> ram[next]) of the next struct */
@@ -179,7 +291,7 @@ struct mem {
* how that space is calculated). */
#ifndef LWIP_RAM_HEAP_POINTER
/** the heap. we need one struct mem at the end and some room for alignment */
-u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT];
+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 */
@@ -209,11 +321,11 @@ static volatile u8_t mem_free_count;
#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
-/* Protect the heap only by using a semaphore */
+/* Protect the heap only by using a mutex */
#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 */
+/* mem_malloc is protected using mutex AND LWIP_MEM_ALLOC_PROTECT */
#define LWIP_MEM_ALLOC_DECL_PROTECT()
#define LWIP_MEM_ALLOC_PROTECT()
#define LWIP_MEM_ALLOC_UNPROTECT()
@@ -276,7 +388,7 @@ mem_init(void)
struct mem *mem;
LWIP_ASSERT("Sanity check alignment",
- (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0);
+ (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT - 1)) == 0);
/* align the heap */
ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER);
@@ -296,7 +408,7 @@ mem_init(void)
MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED);
- if(sys_mutex_new(&mem_mutex) != ERR_OK) {
+ if (sys_mutex_new(&mem_mutex) != ERR_OK) {
LWIP_ASSERT("failed to create mem_mutex", 0);
}
}
@@ -317,23 +429,21 @@ mem_free(void *rmem)
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: 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);
+ (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);
+ MEM_STATS_INC_LOCKED(illegal);
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);
@@ -359,16 +469,16 @@ mem_free(void *rmem)
* 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
+ * @param new_size 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_trim(void *rmem, mem_size_t new_size)
{
- mem_size_t size;
+ mem_size_t size, newsize;
mem_size_t ptr, ptr2;
struct mem *mem, *mem2;
/* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */
@@ -376,35 +486,32 @@ mem_trim(void *rmem, mem_size_t newsize)
/* Expand the size of the allocated memory region so that we can
adjust for alignment. */
- newsize = LWIP_MEM_ALIGN_SIZE(newsize);
+ newsize = (mem_size_t)LWIP_MEM_ALIGN_SIZE(new_size);
+ if ((newsize > MEM_SIZE_ALIGNED) || (newsize < new_size)) {
+ return NULL;
+ }
- if(newsize < MIN_SIZE_ALIGNED) {
+ 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);
+ (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);
+ MEM_STATS_INC_LOCKED(illegal);
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;
+ size = (mem_size_t)((mem_size_t)(mem->next - ptr) - SIZEOF_STRUCT_MEM);
LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size);
if (newsize > size) {
/* not supported */
@@ -419,13 +526,13 @@ mem_trim(void *rmem, mem_size_t newsize)
LWIP_MEM_FREE_PROTECT();
mem2 = (struct mem *)(void *)&ram[mem->next];
- if(mem2->used == 0) {
+ 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;
+ ptr2 = (mem_size_t)(ptr + SIZEOF_STRUCT_MEM + newsize);
if (lfree == mem2) {
lfree = (struct mem *)(void *)&ram[ptr2];
}
@@ -453,7 +560,7 @@ mem_trim(void *rmem, mem_size_t newsize)
* @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;
+ ptr2 = (mem_size_t)(ptr + SIZEOF_STRUCT_MEM + newsize);
mem2 = (struct mem *)(void *)&ram[ptr2];
if (mem2 < lfree) {
lfree = mem2;
@@ -471,7 +578,7 @@ mem_trim(void *rmem, mem_size_t newsize)
/* 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.
+ -> don't do anyhting.
-> the remaining space stays unused since it is too small
} */
#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
@@ -482,41 +589,40 @@ mem_trim(void *rmem, mem_size_t newsize)
}
/**
- * Adam's mem_malloc() plus solution for bug #17922
* Allocate a block of memory with a minimum of 'size' bytes.
*
- * @param size is the minimum size of the requested block in bytes.
+ * @param size_in 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_malloc(mem_size_t size_in)
{
- mem_size_t ptr, ptr2;
+ mem_size_t ptr, ptr2, size;
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) {
+ if (size_in == 0) {
return NULL;
}
/* Expand the size of the allocated memory region so that we can
adjust for alignment. */
- size = LWIP_MEM_ALIGN_SIZE(size);
+ size = (mem_size_t)LWIP_MEM_ALIGN_SIZE(size_in);
+ if ((size > MEM_SIZE_ALIGNED) ||
+ (size < size_in)) {
+ return NULL;
+ }
- if(size < MIN_SIZE_ALIGNED) {
+ 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();
@@ -561,7 +667,7 @@ mem_malloc(mem_size_t size)
* 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;
+ ptr2 = (mem_size_t)(ptr + SIZEOF_STRUCT_MEM + size);
/* create mem2 struct */
mem2 = (struct mem *)(void *)&ram[ptr2];
mem2->used = 0;
@@ -579,7 +685,7 @@ mem_malloc(mem_size_t size)
/* (a mem2 struct does no fit into the user data space of mem and mem->next will always
* be used at this point: if not we have 2 unused structs in a row, plug_holes should have
* take care of this).
- * -> near fit or excact fit: do not split, no mem2 creation
+ * -> 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!
*/
@@ -612,27 +718,36 @@ mem_malloc_adjust_lfree:
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);
+ (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);
+ ((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);
+ (((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);
+ } 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);
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size));
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.
@@ -643,17 +758,23 @@ mem_malloc_adjust_lfree:
* @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 *
+mem_calloc(mem_size_t count, mem_size_t size)
{
void *p;
+ size_t alloc_size = (size_t)count * (size_t)size;
+
+ if ((size_t)(mem_size_t)alloc_size != alloc_size) {
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_calloc: could not allocate %"SZT_F" bytes\n", alloc_size));
+ return NULL;
+ }
/* allocate 'count' objects of size 'size' */
- p = mem_malloc(count * size);
+ p = mem_malloc((mem_size_t)alloc_size);
if (p) {
/* zero the memory */
- memset(p, 0, count * size);
+ memset(p, 0, alloc_size);
}
return p;
}
-
-#endif /* !MEM_LIBC_MALLOC */
+#endif /* MEM_LIBC_MALLOC && (!LWIP_STATS || !MEM_STATS) */
diff --git a/lwip/src/core/memp.c b/lwip/src/core/memp.c
index 1323463..1093210 100644
--- a/lwip/src/core/memp.c
+++ b/lwip/src/core/memp.c
@@ -4,13 +4,18 @@
*
* 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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -19,21 +24,21 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
@@ -41,205 +46,104 @@
#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/udp.h"
#include "lwip/raw.h"
-#include "lwip/tcp_impl.h"
-#include "lwip/igmp.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/altcp.h"
+#include "lwip/ip4_frag.h"
+#include "lwip/netbuf.h"
#include "lwip/api.h"
-#include "lwip/api_msg.h"
-#include "lwip/tcpip.h"
-#include "lwip/sys.h"
-#include "lwip/timers.h"
-#include "lwip/stats.h"
-#include "netif/etharp.h"
-#include "lwip/ip_frag.h"
-#include "lwip/snmp_structs.h"
-#include "lwip/snmp_msg.h"
+#include "lwip/priv/tcpip_priv.h"
+#include "lwip/priv/api_msg.h"
+#include "lwip/sockets.h"
+#include "lwip/priv/sockets_priv.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 "netif/ppp_oe.h"
-#include "lwip/nd6.h"
+#include "lwip/priv/nd6_priv.h"
#include "lwip/ip6_frag.h"
#include "lwip/mld6.h"
-#include <string.h>
-
-#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */
+#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEMPOOL_DECLARE(name,num,size,desc)
+#include "lwip/priv/memp_std.h"
-struct memp {
- struct memp *next;
-#if MEMP_OVERFLOW_CHECK
- const char *file;
- int line;
-#endif /* MEMP_OVERFLOW_CHECK */
+const struct memp_desc *const memp_pools[MEMP_MAX] = {
+#define LWIP_MEMPOOL(name,num,size,desc) &memp_ ## name,
+#include "lwip/priv/memp_std.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 */
-
-/** This array holds the first free element of each pool.
- * Elements form a linked list. */
-static struct memp *memp_tab[MEMP_MAX];
-
-#else /* MEMP_MEM_MALLOC */
-
-#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x))
-
-#endif /* MEMP_MEM_MALLOC */
-
-/** This array holds the element sizes of each pool. */
-#if !MEM_USE_POOLS && !MEMP_MEM_MALLOC
-static
+#ifdef LWIP_HOOK_FILENAME
+#include LWIP_HOOK_FILENAME
#endif
-const u16_t memp_sizes[MEMP_MAX] = {
-#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_SIZE(size),
-#include "lwip/memp_std.h"
-};
-
-#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */
-/** This array holds the number of elements in each pool. */
-static const u16_t memp_num[MEMP_MAX] = {
-#define LWIP_MEMPOOL(name,num,size,desc) (num),
-#include "lwip/memp_std.h"
-};
-
-/** This array holds a textual description of each pool. */
-#ifdef LWIP_DEBUG
-static const char *memp_desc[MEMP_MAX] = {
-#define LWIP_MEMPOOL(name,num,size,desc) (desc),
-#include "lwip/memp_std.h"
-};
-#endif /* LWIP_DEBUG */
-
-#if MEMP_SEPARATE_POOLS
-
-/** This creates each memory pool. These are named memp_memory_XXX_base (where
- * XXX is the name of the pool defined in memp_std.h).
- * To relocate a pool, declare it as extern in cc.h. Example for GCC:
- * extern u8_t __attribute__((section(".onchip_mem"))) memp_memory_UDP_PCB_base[];
- */
-#define LWIP_MEMPOOL(name,num,size,desc) u8_t memp_memory_ ## name ## _base \
- [((num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size)))];
-#include "lwip/memp_std.h"
-
-/** This array holds the base of each memory pool. */
-static u8_t *const memp_bases[] = {
-#define LWIP_MEMPOOL(name,num,size,desc) memp_memory_ ## name ## _base,
-#include "lwip/memp_std.h"
-};
-
-#else /* MEMP_SEPARATE_POOLS */
-
-/** This is the actual memory used by the pools (all pools in one big block). */
-static u8_t memp_memory[MEM_ALIGNMENT - 1
-#define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) )
-#include "lwip/memp_std.h"
-];
-
-#endif /* MEMP_SEPARATE_POOLS */
+#if MEMP_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
+#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(void)
+memp_sanity(const struct memp_desc *desc)
{
- s16_t i;
struct memp *t, *h;
- for (i = 0; i < MEMP_MAX; i++) {
- t = memp_tab[i];
- if(t != NULL) {
- for (h = t->next; (t != NULL) && (h != NULL); t = t->next,
- h = (((h->next != NULL) && (h->next->next != NULL)) ? h->next->next : NULL)) {
- if (t == h) {
- return 0;
- }
+ 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*/
-#if MEMP_OVERFLOW_CHECK
-#if defined(LWIP_DEBUG) && MEMP_STATS
-static const char * memp_overflow_names[] = {
-#define LWIP_MEMPOOL(name,num,size,desc) "/"desc,
-#include "lwip/memp_std.h"
- };
-#endif
+#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 memp_type the pool p comes from
+ * @param desc the pool p comes from
*/
static void
-memp_overflow_check_element_overflow(struct memp *p, u16_t memp_type)
+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;
-#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
- m = (u8_t*)p + MEMP_SIZE + memp_sizes[memp_type];
+ 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 ";
- char digit[] = "0";
- if(memp_type >= 10) {
- digit[0] = '0' + (memp_type/10);
- strcat(errstr, digit);
- }
- digit[0] = '0' + (memp_type%10);
- strcat(errstr, digit);
-#if defined(LWIP_DEBUG) && MEMP_STATS
- strcat(errstr, memp_overflow_names[memp_type]);
-#endif
+ strcat(errstr, desc->desc);
LWIP_ASSERT(errstr, 0);
}
}
-#endif
+#else /* MEMP_SANITY_REGION_AFTER_ALIGNED > 0 */
+ LWIP_UNUSED_ARG(p);
+ LWIP_UNUSED_ARG(desc);
+#endif /* MEMP_SANITY_REGION_AFTER_ALIGNED > 0 */
}
/**
@@ -247,34 +151,51 @@ memp_overflow_check_element_overflow(struct memp *p, u16_t memp_type)
* (e.g. the restricted area before it has been altered)
*
* @param p the memp element to check
- * @param memp_type the pool p comes from
+ * @param desc the pool p comes from
*/
static void
-memp_overflow_check_element_underflow(struct memp *p, u16_t memp_type)
+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;
-#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
- m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
+ m = (u8_t *)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) {
if (m[k] != 0xcd) {
char errstr[128] = "detected memp underflow in pool ";
- char digit[] = "0";
- if(memp_type >= 10) {
- digit[0] = '0' + (memp_type/10);
- strcat(errstr, digit);
- }
- digit[0] = '0' + (memp_type%10);
- strcat(errstr, digit);
-#if defined(LWIP_DEBUG) && MEMP_STATS
- strcat(errstr, memp_overflow_names[memp_type]);
-#endif
+ 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.
*
@@ -285,108 +206,181 @@ memp_overflow_check_all(void)
{
u16_t i, j;
struct memp *p;
+ SYS_ARCH_DECL_PROTECT(old_level);
+ SYS_ARCH_PROTECT(old_level);
-#if !MEMP_SEPARATE_POOLS
- p = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
-#endif /* !MEMP_SEPARATE_POOLS */
for (i = 0; i < MEMP_MAX; ++i) {
-#if MEMP_SEPARATE_POOLS
- p = (struct memp *)(memp_bases[i]);
-#endif /* MEMP_SEPARATE_POOLS */
- for (j = 0; j < memp_num[i]; ++j) {
- memp_overflow_check_element_overflow(p, i);
- p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED);
- }
- }
-#if !MEMP_SEPARATE_POOLS
- p = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
-#endif /* !MEMP_SEPARATE_POOLS */
- for (i = 0; i < MEMP_MAX; ++i) {
-#if MEMP_SEPARATE_POOLS
- p = (struct memp *)(memp_bases[i]);
-#endif /* MEMP_SEPARATE_POOLS */
- for (j = 0; j < memp_num[i]; ++j) {
- memp_overflow_check_element_underflow(p, i);
- p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED);
+ 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 the restricted areas of all memp elements in every pool.
+ * Initialize custom memory pool.
+ * Related functions: memp_malloc_pool, memp_free_pool
+ *
+ * @param desc pool to initialize
*/
-static void
-memp_overflow_init(void)
+void
+memp_init_pool(const struct memp_desc *desc)
{
- u16_t i, j;
- struct memp *p;
- u8_t *m;
+#if MEMP_MEM_MALLOC
+ LWIP_UNUSED_ARG(desc);
+#else
+ int i;
+ struct memp *memp;
-#if !MEMP_SEPARATE_POOLS
- p = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
-#endif /* !MEMP_SEPARATE_POOLS */
- for (i = 0; i < MEMP_MAX; ++i) {
-#if MEMP_SEPARATE_POOLS
- p = (struct memp *)(memp_bases[i]);
-#endif /* MEMP_SEPARATE_POOLS */
- for (j = 0; j < memp_num[i]; ++j) {
-#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
- m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
- memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED);
+ *desc->tab = NULL;
+ memp = (struct memp *)LWIP_MEM_ALIGN(desc->base);
+#if MEMP_MEM_INIT
+ /* force memset on pool memory */
+ memset(memp, 0, (size_t)desc->num * (MEMP_SIZE + desc->size
+#if MEMP_OVERFLOW_CHECK
+ + MEMP_SANITY_REGION_AFTER_ALIGNED
#endif
-#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
- m = (u8_t*)p + MEMP_SIZE + memp_sizes[i];
- memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED);
+ ));
#endif
- p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED);
- }
+ /* 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) */
}
-#endif /* MEMP_OVERFLOW_CHECK */
/**
- * Initialize this module.
- *
+ * 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)
{
- struct memp *memp;
- u16_t i, j;
-
- for (i = 0; i < MEMP_MAX; ++i) {
- MEMP_STATS_AVAIL(used, i, 0);
- MEMP_STATS_AVAIL(max, i, 0);
- MEMP_STATS_AVAIL(err, i, 0);
- MEMP_STATS_AVAIL(avail, i, memp_num[i]);
- }
+ u16_t i;
-#if !MEMP_SEPARATE_POOLS
- memp = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
-#endif /* !MEMP_SEPARATE_POOLS */
/* for every pool: */
- for (i = 0; i < MEMP_MAX; ++i) {
- memp_tab[i] = NULL;
-#if MEMP_SEPARATE_POOLS
- memp = (struct memp*)memp_bases[i];
-#endif /* MEMP_SEPARATE_POOLS */
- /* create a linked list of memp elements */
- for (j = 0; j < memp_num[i]; ++j) {
- memp->next = memp_tab[i];
- memp_tab[i] = memp;
- memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i]
-#if MEMP_OVERFLOW_CHECK
- + MEMP_SANITY_REGION_AFTER_ALIGNED
+ 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
- memp_overflow_init();
+
+#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 {
+#if MEMP_STATS
+ desc->stats->err++;
+#endif
+ SYS_ARCH_UNPROTECT(old_level);
+ LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", desc->desc));
+ }
+
+ 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
}
/**
@@ -394,50 +388,85 @@ memp_init(void)
*
* @param type the pool to get an element from
*
- * the debug version has two more parameters:
- * @param file file name calling this function
- * @param line number of line where this function is called
- *
* @return a pointer to the allocated memory or a NULL pointer on error
*/
void *
#if !MEMP_OVERFLOW_CHECK
memp_malloc(memp_t type)
#else
-memp_malloc_fn(memp_t type, const char* file, const int line)
+memp_malloc_fn(memp_t type, const char *file, const int line)
#endif
{
- struct memp *memp;
- SYS_ARCH_DECL_PROTECT(old_level);
-
+ void *memp;
LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;);
- SYS_ARCH_PROTECT(old_level);
#if MEMP_OVERFLOW_CHECK >= 2
memp_overflow_check_all();
#endif /* MEMP_OVERFLOW_CHECK >= 2 */
- memp = memp_tab[type];
-
- if (memp != NULL) {
- memp_tab[type] = memp->next;
-#if MEMP_OVERFLOW_CHECK
- memp->next = NULL;
- memp->file = file;
- memp->line = line;
+#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 */
- MEMP_STATS_INC_USED(used, type);
- LWIP_ASSERT("memp_malloc: memp properly aligned",
- ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0);
- memp = (struct memp*)(void *)((u8_t*)memp + MEMP_SIZE);
- } else {
- LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", memp_desc[type]));
- MEMP_STATS_INC(err, type);
- }
+#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;
- return 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);
}
/**
@@ -449,37 +478,29 @@ memp_malloc_fn(memp_t type, const char* file, const int line)
void
memp_free(memp_t type, void *mem)
{
- struct memp *memp;
- SYS_ARCH_DECL_PROTECT(old_level);
+#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;
}
- LWIP_ASSERT("memp_free: mem properly aligned",
- ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0);
-
- memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE);
- SYS_ARCH_PROTECT(old_level);
-#if MEMP_OVERFLOW_CHECK
#if MEMP_OVERFLOW_CHECK >= 2
memp_overflow_check_all();
-#else
- memp_overflow_check_element_overflow(memp, type);
- memp_overflow_check_element_underflow(memp, type);
#endif /* MEMP_OVERFLOW_CHECK >= 2 */
-#endif /* MEMP_OVERFLOW_CHECK */
- MEMP_STATS_DEC(used, type);
-
- memp->next = memp_tab[type];
- memp_tab[type] = memp;
+#ifdef LWIP_HOOK_MEMP_AVAILABLE
+ old_first = *memp_pools[type]->tab;
+#endif
-#if MEMP_SANITY_CHECK
- LWIP_ASSERT("memp sanity", memp_sanity());
-#endif /* MEMP_SANITY_CHECK */
+ do_memp_free_pool(memp_pools[type], mem);
- SYS_ARCH_UNPROTECT(old_level);
+#ifdef LWIP_HOOK_MEMP_AVAILABLE
+ if (old_first == NULL) {
+ LWIP_HOOK_MEMP_AVAILABLE(type);
+ }
+#endif
}
-
-#endif /* MEMP_MEM_MALLOC */
diff --git a/lwip/src/core/netif.c b/lwip/src/core/netif.c
index dcc8758..2100bf9 100644
--- a/lwip/src/core/netif.c
+++ b/lwip/src/core/netif.c
@@ -2,6 +2,19 @@
* @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
*/
/*
@@ -33,27 +46,34 @@
* This file is part of the lwIP TCP/IP stack.
*
* Author: Adam Dunkels <adam@sics.se>
- *
*/
#include "lwip/opt.h"
+#include <string.h> /* memset */
+#include <stdlib.h> /* atoi */
+
#include "lwip/def.h"
#include "lwip/ip_addr.h"
#include "lwip/ip6_addr.h"
#include "lwip/netif.h"
-#include "lwip/tcp_impl.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/udp.h"
+#include "lwip/raw.h"
#include "lwip/snmp.h"
#include "lwip/igmp.h"
-#include "netif/etharp.h"
+#include "lwip/etharp.h"
#include "lwip/stats.h"
-#if ENABLE_LOOPBACK
#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 */
@@ -66,29 +86,55 @@
#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 */
+#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 */
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+static netif_ext_callback_t *ext_callback;
+#endif
+#if !LWIP_SINGLE_NETIF
struct netif *netif_list;
+#endif /* !LWIP_SINGLE_NETIF */
struct netif *netif_default;
+#define netif_index_to_num(index) ((index) - 1)
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, ip6_addr_t *ipaddr);
+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;
/**
@@ -104,11 +150,19 @@ netif_loopif_init(struct netif *netif)
/* initialize the snmp variables and counters inside the struct netif
* ifSpeed: no assumption can be made!
*/
- NETIF_INIT_SNMP(netif, snmp_ifType_softwareLoopback, 0);
+ MIB2_INIT_NETIF(netif, snmp_ifType_softwareLoopback, 0);
netif->name[0] = 'l';
netif->name[1] = 'o';
- netif->output = netif_loop_output;
+#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_set_flags(netif, NETIF_FLAG_IGMP);
+#endif
return ERR_OK;
}
#endif /* LWIP_HAVE_LOOPIF */
@@ -117,22 +171,71 @@ void
netif_init(void)
{
#if LWIP_HAVE_LOOPIF
- ip_addr_t loop_ipaddr, loop_netmask, loop_gw;
- IP4_ADDR(&loop_gw, 127,0,0,1);
- IP4_ADDR(&loop_ipaddr, 127,0,0,1);
- IP4_ADDR(&loop_netmask, 255,0,0,0);
+#if 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, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, ip_input);
+ netif_add(&loop_netif, LOOPIF_ADDRINIT NULL, netif_loopif_init, ip_input);
#else /* NO_SYS */
- netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, tcpip_input);
+ 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.
+ *
+ * Same as @ref netif_add but without IPv4 addresses
+ */
+struct netif *
+netif_add_noaddr(struct netif *netif, void *state, netif_init_fn init, netif_input_fn input)
+{
+ return netif_add(netif,
+#if LWIP_IPV4
+ NULL, NULL, NULL,
+#endif /* LWIP_IPV4*/
+ state, init, input);
+}
+
+/**
+ * @ingroup netif
* Add a network interface to the list of lwIP netifs.
*
* @param netif a pre-allocated netif structure
@@ -142,40 +245,74 @@ netif_init(void)
* @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.
+ * 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, ip_addr_t *ipaddr, ip_addr_t *netmask,
- ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input)
+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
- u32_t i;
+ s8_t i;
+#endif
+
+#if LWIP_SINGLE_NETIF
+ if (netif_default != NULL) {
+ LWIP_ASSERT("single netif already set", 0);
+ return NULL;
+ }
#endif
LWIP_ASSERT("No init function given", init != NULL);
+#if LWIP_IPV4
+ if (ipaddr == NULL) {
+ ipaddr = ip_2_ip4(IP4_ADDR_ANY);
+ }
+ if (netmask == NULL) {
+ netmask = ip_2_ip4(IP4_ADDR_ANY);
+ }
+ if (gw == NULL) {
+ gw = ip_2_ip4(IP4_ADDR_ANY);
+ }
+
/* reset new interface configuration state */
- ip_addr_set_zero(&netif->ip_addr);
- ip_addr_set_zero(&netif->netmask);
- ip_addr_set_zero(&netif->gw);
+ 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++) {
- ip6_addr_set_zero(&netif->ip6_addr[i]);
- netif_ip6_addr_set_state(netif, i, IP6_ADDR_INVALID);
+ ip_addr_set_zero_ip6(&netif->ip6_addr[i]);
+ netif->ip6_addr_state[i] = IP6_ADDR_INVALID;
+#if LWIP_IPV6_ADDRESS_LIFETIMES
+ netif->ip6_addr_valid_life[i] = IP6_ADDR_LIFE_STATIC;
+ netif->ip6_addr_pref_life[i] = IP6_ADDR_LIFE_STATIC;
+#endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
}
netif->output_ip6 = netif_null_output_ip6;
#endif /* LWIP_IPV6 */
+ NETIF_SET_CHECKSUM_CTRL(netif, NETIF_CHECKSUM_ENABLE_ALL);
netif->flags = 0;
-#if LWIP_DHCP
- /* netif not under DHCP control by default */
- netif->dhcp = NULL;
-#endif /* LWIP_DHCP */
-#if LWIP_AUTOIP
- /* netif not under AutoIP control by default */
- netif->autoip = NULL;
-#endif /* LWIP_AUTOIP */
+#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;
@@ -183,10 +320,6 @@ netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
#if LWIP_IPV6_SEND_ROUTER_SOLICIT
netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT;
#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
-#if LWIP_IPV6_DHCP6
- /* netif not under DHCPv6 control by default */
- netif->dhcp6 = NULL;
-#endif /* LWIP_IPV6_DHCP6 */
#if LWIP_NETIF_STATUS_CALLBACK
netif->status_callback = NULL;
#endif /* LWIP_NETIF_STATUS_CALLBACK */
@@ -206,24 +339,58 @@ netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
/* remember netif specific state information data */
netif->state = state;
- netif->num = netif_num++;
+ netif->num = netif_num;
netif->input = input;
- NETIF_SET_HWADDRHINT(netif, NULL);
+
+ NETIF_RESET_HINTS(netif);
#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;
}
+#if !LWIP_SINGLE_NETIF
+ /* Assign a unique netif number in the range [0..254], so that (num+1) can
+ serve as an interface index that fits in a u8_t.
+ We assume that the new netif has not yet been added to the list here.
+ This algorithm is O(n^2), but that should be OK for lwIP.
+ */
+ {
+ struct netif *netif2;
+ int num_netifs;
+ do {
+ if (netif->num == 255) {
+ netif->num = 0;
+ }
+ num_netifs = 0;
+ for (netif2 = netif_list; netif2 != NULL; netif2 = netif2->next) {
+ num_netifs++;
+ LWIP_ASSERT("too many netifs, max. supported number is 255", num_netifs <= 255);
+ if (netif2->num == netif->num) {
+ netif->num++;
+ break;
+ }
+ }
+ } while (netif2 != NULL);
+ }
+ if (netif->num == 254) {
+ netif_num = 0;
+ } else {
+ netif_num = (u8_t)(netif->num + 1);
+ }
+
/* add this netif to the list */
netif->next = netif_list;
netif_list = netif;
- snmp_inc_iflist();
+#endif /* "LWIP_SINGLE_NETIF */
+ mib2_netif_added(netif);
#if LWIP_IGMP
/* start IGMP processing */
@@ -232,18 +399,26 @@ netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
}
#endif /* LWIP_IGMP */
- LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP addr ",
- netif->name[0], netif->name[1]));
- ip_addr_debug_print(NETIF_DEBUG, ipaddr);
+ LWIP_DEBUGF(NETIF_DEBUG, ("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 "));
- ip_addr_debug_print(NETIF_DEBUG, netmask);
+ ip4_addr_debug_print(NETIF_DEBUG, netmask);
LWIP_DEBUGF(NETIF_DEBUG, (" gw "));
- ip_addr_debug_print(NETIF_DEBUG, gw);
+ ip4_addr_debug_print(NETIF_DEBUG, gw);
+#endif /* LWIP_IPV4 */
LWIP_DEBUGF(NETIF_DEBUG, ("\n"));
+
+ netif_invoke_ext_callback(netif, LWIP_NSC_NETIF_ADDED, NULL);
+
return netif;
}
+#if LWIP_IPV4
/**
+ * @ingroup netif_ip4
* Change IP address configuration for a network interface (including netmask
* and default gateway).
*
@@ -253,15 +428,42 @@ netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
* @param gw the new default gateway
*/
void
-netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
- ip_addr_t *gw)
+netif_set_addr(struct netif *netif, const ip4_addr_t *ipaddr, const ip4_addr_t *netmask,
+ const ip4_addr_t *gw)
{
- netif_set_ipaddr(netif, ipaddr);
- netif_set_netmask(netif, netmask);
- netif_set_gw(netif, gw);
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ u8_t something_changed = 0;
+
+ if ((ip4_addr_cmp(ipaddr, netif_ip4_addr(netif)) == 0) ||
+ (ip4_addr_cmp(gw, netif_ip4_gw(netif)) == 0) ||
+ (ip4_addr_cmp(netmask, netif_ip4_netmask(netif)) == 0)) {
+ something_changed = 1;
+ }
+#endif
+
+ 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);
+ }
+
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ if (something_changed != 0) {
+ netif_invoke_ext_callback(netif, LWIP_NSC_IPV4_SETTINGS_CHANGED, NULL);
+ }
+#endif
}
+#endif /* LWIP_IPV4*/
/**
+ * @ingroup netif
* Remove a network interface from the list of lwIP netifs.
*
* @param netif the network interface to remove
@@ -269,48 +471,87 @@ netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
void
netif_remove(struct netif *netif)
{
+#if LWIP_IPV6
+ int i;
+#endif
+
if (netif == NULL) {
return;
}
+ netif_invoke_ext_callback(netif, LWIP_NSC_NETIF_REMOVED, NULL);
+
+#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 */
-#if LWIP_IPV6 && LWIP_IPV6_MLD
+#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 && LWIP_IPV6_MLD */
+#endif /* LWIP_IPV6_MLD */
+#endif /* LWIP_IPV6 */
if (netif_is_up(netif)) {
/* set netif down before removing (call callback function) */
netif_set_down(netif);
}
- snmp_delete_ipaddridx_tree(netif);
+ mib2_remove_ip4(netif);
+ /* this netif is default? */
+ if (netif_default == netif) {
+ /* reset default netif */
+ netif_set_default(NULL);
+ }
+#if !LWIP_SINGLE_NETIF
/* is it the first netif? */
if (netif_list == netif) {
netif_list = netif->next;
} else {
/* look for netif further down the list */
- struct netif * tmpNetif;
- for (tmpNetif = netif_list; tmpNetif != NULL; tmpNetif = tmpNetif->next) {
- if (tmpNetif->next == netif) {
- tmpNetif->next = netif->next;
+ 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 (tmpNetif == NULL)
- return; /* we didn't find any netif today */
- }
- snmp_dec_iflist();
- /* this netif is default? */
- if (netif_default == netif) {
- /* reset default netif */
- netif_set_default(NULL);
+ if (tmp_netif == NULL) {
+ return; /* netif is not on the list */
+ }
}
+#endif /* !LWIP_SINGLE_NETIF */
+ mib2_netif_removed(netif);
#if LWIP_NETIF_REMOVE_CALLBACK
if (netif->remove_callback) {
netif->remove_callback(netif);
@@ -319,44 +560,9 @@ netif_remove(struct netif *netif)
LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") );
}
+#if LWIP_IPV4
/**
- * Find a network interface by searching for its name
- *
- * @param name the name of the netif (like netif->name) plus concatenated number
- * in ascii representation (e.g. 'en0')
- */
-struct netif *
-netif_find(char *name)
-{
- struct netif *netif;
- u8_t num;
-
- if (name == NULL) {
- return NULL;
- }
-
- num = name[2] - '0';
-
- for(netif = netif_list; netif != NULL; netif = netif->next) {
- if (num == netif->num &&
- name[0] == netif->name[0] &&
- name[1] == netif->name[1]) {
- LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: found %c%c\n", name[0], name[1]));
- return netif;
- }
- }
- LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: didn't find %c%c\n", name[0], name[1]));
- return NULL;
-}
-
-int netif_is_named (struct netif *netif, const char name[3])
-{
- u8_t num = name[2] - '0';
-
- return (!memcmp(netif->name, name, 2) && netif->num == num);
-}
-
-/**
+ * @ingroup netif_ip4
* Change the IP address of a network interface
*
* @param netif the network interface to change
@@ -366,63 +572,58 @@ int netif_is_named (struct netif *netif, const char name[3])
* default gateway
*/
void
-netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr)
+netif_set_ipaddr(struct netif *netif, const ip4_addr_t *ipaddr)
{
- /* TODO: Handling of obsolete pcbs */
- /* See: http://mail.gnu.org/archive/html/lwip-users/2003-03/msg00118.html */
-#if LWIP_TCP
- struct tcp_pcb *pcb;
- struct tcp_pcb_listen *lpcb;
+ 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 (ipaddr && (ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) {
- /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */
+ if (ip4_addr_cmp(ip_2_ip4(&new_addr), netif_ip4_addr(netif)) == 0) {
+ ip_addr_t old_addr;
+ ip_addr_copy(old_addr, *netif_ip_addr4(netif));
+
LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n"));
- pcb = tcp_active_pcbs;
- while (pcb != NULL) {
- /* PCB bound to current local interface address? */
- if (ip_addr_cmp(ipX_2_ip(&pcb->local_ip), &(netif->ip_addr))
-#if LWIP_AUTOIP
- /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */
- && !ip_addr_islinklocal(ipX_2_ip(&pcb->local_ip))
-#endif /* LWIP_AUTOIP */
- ) {
- /* this connection must be aborted */
- struct tcp_pcb *next = pcb->next;
- LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb));
- tcp_abort(pcb);
- pcb = next;
- } else {
- pcb = pcb->next;
- }
- }
- for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
- /* PCB bound to current local interface address? */
- if ((!(ip_addr_isany(ipX_2_ip(&lpcb->local_ip)))) &&
- (ip_addr_cmp(ipX_2_ip(&lpcb->local_ip), &(netif->ip_addr)))) {
- /* The PCB is listening to the old ipaddr and
- * is set to listen to the new one instead */
- ip_addr_set(ipX_2_ip(&lpcb->local_ip), ipaddr);
- }
+#if LWIP_TCP
+ tcp_netif_ip_addr_changed(&old_addr, &new_addr);
+#endif /* LWIP_TCP */
+#if LWIP_UDP
+ udp_netif_ip_addr_changed(&old_addr, &new_addr);
+#endif /* LWIP_UDP */
+#if LWIP_RAW
+ raw_netif_ip_addr_changed(&old_addr, &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);
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ {
+ netif_ext_callback_args_t args;
+ args.ipv4_changed.old_address = &old_addr;
+ netif_invoke_ext_callback(netif, LWIP_NSC_IPV4_ADDRESS_CHANGED, &args);
}
- }
#endif
- snmp_delete_ipaddridx_tree(netif);
- snmp_delete_iprteidx_tree(0,netif);
- /* set new IP address to netif */
- ip_addr_set(&(netif->ip_addr), ipaddr);
- snmp_insert_ipaddridx_tree(netif);
- snmp_insert_iprteidx_tree(0,netif);
+ }
LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
- netif->name[0], netif->name[1],
- ip4_addr1_16(&netif->ip_addr),
- ip4_addr2_16(&netif->ip_addr),
- ip4_addr3_16(&netif->ip_addr),
- ip4_addr4_16(&netif->ip_addr)));
+ 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
@@ -431,15 +632,29 @@ netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr)
* @note call netif_set_addr() if you also want to change ip address and netmask
*/
void
-netif_set_gw(struct netif *netif, ip_addr_t *gw)
+netif_set_gw(struct netif *netif, const ip4_addr_t *gw)
{
- ip_addr_set(&(netif->gw), gw);
- LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
- netif->name[0], netif->name[1],
- ip4_addr1_16(&netif->gw),
- ip4_addr2_16(&netif->gw),
- ip4_addr3_16(&netif->gw),
- ip4_addr4_16(&netif->gw)));
+ const ip4_addr_t *safe_gw = gw ? gw : IP4_ADDR_ANY4;
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ netif_ext_callback_args_t args;
+ ip_addr_t old_addr;
+ ip_addr_copy(old_addr, *netif_ip_gw4(netif));
+ args.ipv4_gw_changed.old_address = &old_addr;
+#endif
+
+ /* address is actually being changed? */
+ if (ip4_addr_cmp(safe_gw, netif_ip4_gw(netif)) == 0) {
+ 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))));
+
+ netif_invoke_ext_callback(netif, LWIP_NSC_IPV4_GATEWAY_CHANGED, &args);
+ }
}
void netif_set_pretend_tcp (struct netif *netif, u8_t pretend)
@@ -452,6 +667,7 @@ void netif_set_pretend_tcp (struct netif *netif, u8_t pretend)
}
/**
+ * @ingroup netif_ip4
* Change the netmask of a network interface
*
* @param netif the network interface to change
@@ -461,21 +677,44 @@ void netif_set_pretend_tcp (struct netif *netif, u8_t pretend)
* default gateway
*/
void
-netif_set_netmask(struct netif *netif, ip_addr_t *netmask)
+netif_set_netmask(struct netif *netif, const ip4_addr_t *netmask)
+{
+ const ip4_addr_t *safe_netmask = netmask ? netmask : IP4_ADDR_ANY4;
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ netif_ext_callback_args_t args;
+ ip_addr_t old_addr;
+ ip_addr_copy(old_addr, *netif_ip_netmask4(netif));
+ args.ipv4_nm_changed.old_address = &old_addr;
+#endif
+
+ /* address is actually being changed? */
+ if (ip4_addr_cmp(safe_netmask, netif_ip4_netmask(netif)) == 0) {
+ 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))));
+
+ netif_invoke_ext_callback(netif, LWIP_NSC_IPV4_NETMASK_CHANGED, &args);
+ }
+}
+#endif /* LWIP_IPV4 */
+
+int netif_is_named (struct netif *netif, const char name[3])
{
- snmp_delete_iprteidx_tree(0, netif);
- /* set new netmask to netif */
- ip_addr_set(&(netif->netmask), netmask);
- snmp_insert_iprteidx_tree(0, netif);
- LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
- netif->name[0], netif->name[1],
- ip4_addr1_16(&netif->netmask),
- ip4_addr2_16(&netif->netmask),
- ip4_addr3_16(&netif->netmask),
- ip4_addr4_16(&netif->netmask)));
+ u8_t num = name[2] - '0';
+
+ return (!memcmp(netif->name, name, 2) && netif->num == num);
}
/**
+ * @ingroup netif
* Set a network interface as the default network interface
* (used to output all packets for which no specific route is found)
*
@@ -486,94 +725,123 @@ netif_set_default(struct netif *netif)
{
if (netif == NULL) {
/* remove default route */
- snmp_delete_iprteidx_tree(1, netif);
+ mib2_remove_route_ip4(1, netif);
} else {
/* install default route */
- snmp_insert_iprteidx_tree(1, netif);
+ 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] : '\''));
+ netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\''));
}
/**
+ * @ingroup netif
* Bring an interface up, available for processing
* traffic.
- *
- * @note: Enabling DHCP on a down interface will make it come
- * up once configured.
- *
- * @see dhcp_start()
- */
-void netif_set_up(struct netif *netif)
+ */
+void
+netif_set_up(struct netif *netif)
{
if (!(netif->flags & NETIF_FLAG_UP)) {
- netif->flags |= NETIF_FLAG_UP;
-
-#if LWIP_SNMP
- snmp_get_sysuptime(&netif->ts);
-#endif /* LWIP_SNMP */
+ netif_set_flags(netif, NETIF_FLAG_UP);
+
+ MIB2_COPY_SYSUPTIME_TO(&netif->ts);
NETIF_STATUS_CALLBACK(netif);
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ {
+ netif_ext_callback_args_t args;
+ args.status_changed.state = 1;
+ netif_invoke_ext_callback(netif, LWIP_NSC_STATUS_CHANGED, &args);
+ }
+#endif
+
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);
- }
+ /* 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);
- }
+ /* resend IGMP memberships */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_report_groups(netif);
+ }
#endif /* LWIP_IGMP */
-#if LWIP_IPV6 && LWIP_IPV6_MLD
- /* send mld memberships */
- mld6_report_groups( netif);
-#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+ }
+#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;
+ /* 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.
- *
- * @note: Enabling DHCP on a down interface will make it come
- * up once configured.
- *
- * @see dhcp_start()
- */
-void netif_set_down(struct netif *netif)
+ */
+void
+netif_set_down(struct netif *netif)
{
if (netif->flags & NETIF_FLAG_UP) {
- netif->flags &= ~NETIF_FLAG_UP;
-#if LWIP_SNMP
- snmp_get_sysuptime(&netif->ts);
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ {
+ netif_ext_callback_args_t args;
+ args.status_changed.state = 0;
+ netif_invoke_ext_callback(netif, LWIP_NSC_STATUS_CHANGED, &args);
+ }
#endif
-#if LWIP_ARP
+ netif_clear_flags(netif, 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_ARP */
+#endif /* LWIP_IPV4 && LWIP_ARP */
+
+#if LWIP_IPV6
+ nd6_cleanup_netif(netif);
+#endif /* LWIP_IPV6 */
+
NETIF_STATUS_CALLBACK(netif);
}
}
#if LWIP_NETIF_STATUS_CALLBACK
/**
- * Set callback to be called when interface is brought up/down
+ * @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)
+void
+netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback)
{
if (netif) {
netif->status_callback = status_callback;
@@ -583,6 +851,7 @@ void netif_set_status_callback(struct netif *netif, netif_status_callback_fn sta
#if LWIP_NETIF_REMOVE_CALLBACK
/**
+ * @ingroup netif
* Set callback to be called when the interface has been removed
*/
void
@@ -595,64 +864,64 @@ netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_c
#endif /* LWIP_NETIF_REMOVE_CALLBACK */
/**
+ * @ingroup netif
* Called by a driver when its link goes up
*/
-void netif_set_link_up(struct netif *netif )
+void
+netif_set_link_up(struct netif *netif)
{
if (!(netif->flags & NETIF_FLAG_LINK_UP)) {
- netif->flags |= NETIF_FLAG_LINK_UP;
+ netif_set_flags(netif, NETIF_FLAG_LINK_UP);
#if LWIP_DHCP
- if (netif->dhcp) {
- dhcp_network_changed(netif);
- }
+ dhcp_network_changed(netif);
#endif /* LWIP_DHCP */
#if LWIP_AUTOIP
- if (netif->autoip) {
- autoip_network_changed(netif);
- }
+ autoip_network_changed(netif);
#endif /* LWIP_AUTOIP */
if (netif->flags & NETIF_FLAG_UP) {
-#if LWIP_ARP
- /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */
- if (netif->flags & NETIF_FLAG_ETHARP) {
- etharp_gratuitous(netif);
- }
-#endif /* LWIP_ARP */
-
-#if LWIP_IGMP
- /* resend IGMP memberships */
- if (netif->flags & NETIF_FLAG_IGMP) {
- igmp_report_groups( netif);
- }
-#endif /* LWIP_IGMP */
-#if LWIP_IPV6 && LWIP_IPV6_MLD
- /* send mld memberships */
- mld6_report_groups( netif);
-#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+ netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV4 | NETIF_REPORT_TYPE_IPV6);
}
NETIF_LINK_CALLBACK(netif);
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ {
+ netif_ext_callback_args_t args;
+ args.link_changed.state = 1;
+ netif_invoke_ext_callback(netif, LWIP_NSC_LINK_CHANGED, &args);
+ }
+#endif
}
}
/**
+ * @ingroup netif
* Called by a driver when its link goes down
*/
-void netif_set_link_down(struct netif *netif )
+void
+netif_set_link_down(struct netif *netif )
{
if (netif->flags & NETIF_FLAG_LINK_UP) {
- netif->flags &= ~NETIF_FLAG_LINK_UP;
+ netif_clear_flags(netif, NETIF_FLAG_LINK_UP);
NETIF_LINK_CALLBACK(netif);
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ {
+ netif_ext_callback_args_t args;
+ args.link_changed.state = 0;
+ netif_invoke_ext_callback(netif, LWIP_NSC_LINK_CHANGED, &args);
+ }
+#endif
}
}
#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)
+void
+netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback)
{
if (netif) {
netif->link_callback = link_callback;
@@ -662,6 +931,7 @@ void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_
#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
@@ -671,52 +941,49 @@ void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_
*
* @param netif the lwip network interface structure
* @param p the (IP) packet to 'send'
- * @param ipaddr the ip address to send the packet to (not used)
* @return ERR_OK if the packet has been sent
* ERR_MEM if the pbuf used to copy the packet couldn't be allocated
*/
err_t
-netif_loop_output(struct netif *netif, struct pbuf *p,
- ip_addr_t *ipaddr)
+netif_loop_output(struct netif *netif, struct pbuf *p)
{
struct pbuf *r;
err_t err;
struct pbuf *last;
#if LWIP_LOOPBACK_MAX_PBUFS
- u8_t clen = 0;
+ 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 LWIP_SNMP
+#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 /* LWIP_SNMP */
+#endif /* MIB2_STATS */
SYS_ARCH_DECL_PROTECT(lev);
- LWIP_UNUSED_ARG(ipaddr);
/* Allocate a new pbuf */
r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
if (r == NULL) {
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
- snmp_inc_ifoutdiscards(stats_if);
+ 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)) {
+ if (((netif->loop_cnt_current + clen) < netif->loop_cnt_current) ||
+ ((netif->loop_cnt_current + clen) > LWIP_MIN(LWIP_LOOPBACK_MAX_PBUFS, 0xFFFF))) {
pbuf_free(r);
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
- snmp_inc_ifoutdiscards(stats_if);
+ MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards);
return ERR_MEM;
}
- netif->loop_cnt_current += clen;
+ netif->loop_cnt_current = (u16_t)(netif->loop_cnt_current + clen);
#endif /* LWIP_LOOPBACK_MAX_PBUFS */
/* Copy the whole pbuf queue p into the single pbuf r */
@@ -724,7 +991,7 @@ netif_loop_output(struct netif *netif, struct pbuf *p,
pbuf_free(r);
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
- snmp_inc_ifoutdiscards(stats_if);
+ MIB2_STATS_NETIF_INC(stats_if, ifoutdiscards);
return err;
}
@@ -732,10 +999,12 @@ netif_loop_output(struct netif *netif, struct pbuf *p,
netif_poll(). */
/* let last point to the last pbuf in chain r */
- for (last = r; last->next != NULL; last = last->next);
+ for (last = r; last->next != NULL; last = last->next) {
+ /* nothing to do here, just get to the last pbuf */
+ }
SYS_ARCH_PROTECT(lev);
- if(netif->loop_first != NULL) {
+ 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;
@@ -746,17 +1015,38 @@ netif_loop_output(struct netif *netif, struct pbuf *p,
SYS_ARCH_UNPROTECT(lev);
LINK_STATS_INC(link.xmit);
- snmp_add_ifoutoctets(stats_if, p->tot_len);
- snmp_inc_ifoutucastpkts(stats_if);
+ 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((tcpip_callback_fn)netif_poll, netif);
+ tcpip_try_callback((tcpip_callback_fn)netif_poll, netif);
#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
@@ -766,62 +1056,65 @@ netif_loop_output(struct netif *netif, struct pbuf *p,
void
netif_poll(struct netif *netif)
{
- struct pbuf *in;
/* If we have a loopif, SNMP counters are adjusted for it,
* if not they are adjusted for 'netif'. */
-#if LWIP_SNMP
+#if 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 /* LWIP_SNMP */
+#endif /* MIB2_STATS */
SYS_ARCH_DECL_PROTECT(lev);
- do {
- /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */
- SYS_ARCH_PROTECT(lev);
- in = netif->loop_first;
- if (in != NULL) {
- struct pbuf *in_end = in;
+ /* 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 = pbuf_clen(in);
- /* adjust the number of pbufs on queue */
- LWIP_ASSERT("netif->loop_cnt_current underflow",
- ((netif->loop_cnt_current - clen) < netif->loop_cnt_current));
- netif->loop_cnt_current -= clen;
+ u8_t clen = 1;
#endif /* LWIP_LOOPBACK_MAX_PBUFS */
- while (in_end->len != in_end->tot_len) {
- LWIP_ASSERT("bogus pbuf: len != tot_len but next == NULL!", in_end->next != NULL);
- in_end = in_end->next;
- }
- /* 'in_end' now points to the last pbuf from 'in' */
- if (in_end == netif->loop_last) {
- /* this was the last pbuf in the list */
- netif->loop_first = netif->loop_last = NULL;
- } else {
- /* pop the pbuf off the list */
- netif->loop_first = in_end->next;
- LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL);
- }
- /* De-queue the pbuf from its successors on the 'loop_' list. */
- in_end->next = NULL;
+
+ 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 = (u16_t)(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);
- if (in != NULL) {
- LINK_STATS_INC(link.recv);
- snmp_add_ifinoctets(stats_if, in->tot_len);
- snmp_inc_ifinucastpkts(stats_if);
- /* loopback packets are always IP packets! */
- if (ip_input(in, netif) != ERR_OK) {
- pbuf_free(in);
- }
- /* Don't reference the packet any more! */
- in = NULL;
+ in->if_idx = netif_get_index(netif);
+
+ 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);
}
- /* go on while there is a packet on the list */
- } while (netif->loop_first != NULL);
+ SYS_ARCH_PROTECT(lev);
+ }
+ SYS_ARCH_UNPROTECT(lev);
}
#if !LWIP_NETIF_LOOPBACK_MULTITHREADING
@@ -831,83 +1124,470 @@ netif_poll(struct netif *netif)
void
netif_poll_all(void)
{
- struct netif *netif = netif_list;
+ struct netif *netif;
/* loop through netifs */
- while (netif != NULL) {
+ NETIF_FOREACH(netif) {
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++;
+
+#if LWIP_NUM_NETIF_CLIENT_DATA > 256
+#error LWIP_NUM_NETIF_CLIENT_DATA must be <= 256
+#endif
+ LWIP_ASSERT("Increase LWIP_NUM_NETIF_CLIENT_DATA in lwipopts.h", result < LWIP_NUM_NETIF_CLIENT_DATA);
+ return (u8_t)(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)
+{
+ ip_addr_t old_addr;
+ ip_addr_t new_ipaddr;
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("invalid index", addr_idx < LWIP_IPV6_NUM_ADDRESSES);
+
+ ip6_addr_copy(*ip_2_ip6(&old_addr), *netif_ip6_addr(netif, addr_idx));
+ IP_SET_TYPE_VAL(old_addr, IPADDR_TYPE_V6);
+
+ /* address is actually being changed? */
+ if ((ip_2_ip6(&old_addr)->addr[0] != i0) || (ip_2_ip6(&old_addr)->addr[1] != i1) ||
+ (ip_2_ip6(&old_addr)->addr[2] != i2) || (ip_2_ip6(&old_addr)->addr[3] != i3)) {
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_ip6_addr_set: netif address being changed\n"));
+
+ IP_ADDR6(&new_ipaddr, i0, i1, i2, i3);
+ ip6_addr_assign_zone(ip_2_ip6(&new_ipaddr), IP6_UNICAST, netif);
+
+ if (netif_ip6_addr_state(netif, addr_idx) & IP6_ADDR_VALID) {
+#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? */
+
+ ip_addr_copy(netif->ip6_addr[addr_idx], new_ipaddr);
+
+ if (netif_ip6_addr_state(netif, addr_idx) & IP6_ADDR_VALID) {
+ netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV6);
+ NETIF_STATUS_CALLBACK(netif);
+ }
+
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ {
+ netif_ext_callback_args_t args;
+ args.ipv6_set.addr_index = addr_idx;
+ args.ipv6_set.old_address = &old_addr;
+ netif_invoke_ext_callback(netif, LWIP_NSC_IPV6_SET, &args);
+ }
+#endif
+ }
+
+ 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 */
+ /* This is a good moment to check that the address is properly zoned. */
+ IP6_ADDR_ZONECHECK_NETIF(netif_ip6_addr(netif, addr_idx), netif);
+ /* @todo: add mib2 ip6 entries? */
+ netif_issue_reports(netif, NETIF_REPORT_TYPE_IPV6);
+ }
+ if ((old_state & ~IP6_ADDR_TENTATIVE_COUNT_MASK) !=
+ (state & ~IP6_ADDR_TENTATIVE_COUNT_MASK)) {
+ /* address state has changed -> call the callback function */
+ NETIF_STATUS_CALLBACK(netif);
+ }
+ }
+
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+ {
+ netif_ext_callback_args_t args;
+ args.ipv6_addr_state_changed.addr_index = addr_idx;
+ args.ipv6_addr_state_changed.address = netif_ip_addr6(netif, addr_idx);
+ netif_invoke_ext_callback(netif, LWIP_NSC_IPV6_ADDR_STATE_CHANGED, &args);
+ }
+#endif
+
+ 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 local address is present on the netif and returns its
+ * index. Depending on its state, it may or may not be assigned to the
+ * interface (as per RFC terminology).
+ *
+ * The given address may or may not be zoned (i.e., have a zone index other
+ * than IP6_NO_ZONE). If the address is zoned, it must have the correct zone
+ * for the given netif, or no match will be found.
+ *
+ * @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_matches_ip6_addr(struct netif * netif, ip6_addr_t * ip6addr)
+netif_get_ip6_addr_match(struct netif *netif, const ip6_addr_t *ip6addr)
{
s8_t i;
+
+#if LWIP_IPV6_SCOPES
+ if (ip6_addr_has_zone(ip6addr) && !ip6_addr_test_zone(ip6addr, netif)) {
+ return -1; /* wrong zone, no match */
+ }
+#endif /* LWIP_IPV6_SCOPES */
+
for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
- if (ip6_addr_cmp(netif_ip6_addr(netif, i), ip6addr)) {
+ if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_cmp_zoneless(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)
+netif_create_ip6_linklocal_address(struct netif *netif, u8_t from_mac_48bit)
{
- u8_t i, addr_index, min_len;
+ u8_t i, addr_index;
/* Link-local prefix. */
- netif->ip6_addr[0].addr[0] = PP_HTONL(0xfe800000ul);
- netif->ip6_addr[0].addr[1] = 0;
+ 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. */
- netif->ip6_addr[0].addr[2] = htonl((((u32_t)(netif->hwaddr[0] ^ 0x02)) << 24) |
+ 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));
- netif->ip6_addr[0].addr[3] = htonl((0xfeul << 24) |
+ ip_2_ip6(&netif->ip6_addr[0])->addr[3] = lwip_htonl((u32_t)(0xfeul << 24) |
((u32_t)(netif->hwaddr[3]) << 16) |
((u32_t)(netif->hwaddr[4]) << 8) |
(netif->hwaddr[5]));
- }
- else {
+ } else {
/* Use hwaddr directly as interface ID. */
- netif->ip6_addr[0].addr[2] = 0;
- netif->ip6_addr[0].addr[3] = 0;
+ ip_2_ip6(&netif->ip6_addr[0])->addr[2] = 0;
+ ip_2_ip6(&netif->ip6_addr[0])->addr[3] = 0;
- min_len = netif->hwaddr_len < 8 ? netif->hwaddr_len : 8;
addr_index = 3;
- for (i = 0; i < min_len; i++) {
+ for (i = 0; (i < 8) && (i < netif->hwaddr_len); i++) {
if (i == 4) {
addr_index--;
}
- netif->ip6_addr[0].addr[addr_index] |= ((u32_t)(netif->hwaddr[netif->hwaddr_len - i - 1])) << (8 * (i & 0x03));
+ ip_2_ip6(&netif->ip6_addr[0])->addr[addr_index] |= ((u32_t)(netif->hwaddr[netif->hwaddr_len - i - 1])) << (8 * (i & 0x03));
}
}
+ /* Set a link-local zone. Even though the zone is implied by the owning
+ * netif, setting the zone anyway has two important conceptual advantages:
+ * 1) it avoids the need for a ton of exceptions in internal code, allowing
+ * e.g. ip6_addr_cmp() to be used on local addresses;
+ * 2) the properly zoned address is visible externally, e.g. when any outside
+ * code enumerates available addresses or uses one to bind a socket.
+ * Any external code unaware of address scoping is likely to just ignore the
+ * zone field, so this should not create any compatibility problems. */
+ ip6_addr_assign_zone(ip_2_ip6(&netif->ip6_addr[0]), IP6_UNICAST, netif);
+
/* Set address state. */
#if LWIP_IPV6_DUP_DETECT_ATTEMPTS
/* Will perform duplicate address detection (DAD). */
- netif->ip6_addr_state[0] = IP6_ADDR_TENTATIVE;
+ netif_ip6_addr_set_state(netif, 0, IP6_ADDR_TENTATIVE);
#else
/* Consider address valid. */
- netif->ip6_addr_state[0] = IP6_ADDR_PREFERRED;
+ 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. The first one is reserved for link-local addresses. */
+ for (i = ip6_addr_islinklocal(ip6addr) ? 0 : 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);
+ ip6_addr_assign_zone(ip_2_ip6(&netif->ip6_addr[i]), IP6_UNICAST, netif);
+ 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, ip6_addr_t *ipaddr)
+netif_null_output_ip6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr)
{
- (void)netif;
- (void)p;
- (void)ipaddr;
+ LWIP_UNUSED_ARG(netif);
+ LWIP_UNUSED_ARG(p);
+ LWIP_UNUSED_ARG(ipaddr);
- return ERR_IF;
+ return ERR_IF;
}
#endif /* LWIP_IPV6 */
+
+/**
+* @ingroup netif
+* Return the interface index for the netif with name
+* or NETIF_NO_INDEX if not found/on error
+*
+* @param name the name of the netif
+*/
+u8_t
+netif_name_to_index(const char *name)
+{
+ struct netif *netif = netif_find(name);
+ if (netif != NULL) {
+ return netif_get_index(netif);
+ }
+ /* No name found, return invalid index */
+ return NETIF_NO_INDEX;
+}
+
+/**
+* @ingroup netif
+* Return the interface name for the netif matching index
+* or NULL if not found/on error
+*
+* @param idx the interface index of the netif
+* @param name char buffer of at least NETIF_NAMESIZE bytes
+*/
+char *
+netif_index_to_name(u8_t idx, char *name)
+{
+ struct netif *netif = netif_get_by_index(idx);
+
+ if (netif != NULL) {
+ name[0] = netif->name[0];
+ name[1] = netif->name[1];
+ lwip_itoa(&name[2], NETIF_NAMESIZE - 2, netif_index_to_num(idx));
+ return name;
+ }
+ return NULL;
+}
+
+/**
+* @ingroup netif
+* Return the interface for the netif index
+*
+* @param idx index of netif to find
+*/
+struct netif *
+netif_get_by_index(u8_t idx)
+{
+ struct netif *netif;
+
+ if (idx != NETIF_NO_INDEX) {
+ NETIF_FOREACH(netif) {
+ if (idx == netif_get_index(netif)) {
+ return netif; /* found! */
+ }
+ }
+ }
+
+ return NULL;
+}
+
+/**
+ * @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)atoi(&name[2]);
+
+ NETIF_FOREACH(netif) {
+ 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_NETIF_EXT_STATUS_CALLBACK
+/**
+ * @ingroup netif
+ * Add extended netif events listener
+ * @param callback pointer to listener structure
+ * @param fn callback function
+ */
+void netif_add_ext_callback(netif_ext_callback_t *callback, netif_ext_callback_fn fn)
+{
+ LWIP_ASSERT("callback must be != NULL", callback != NULL);
+ LWIP_ASSERT("fn must be != NULL", fn != NULL);
+
+ callback->callback_fn = fn;
+ callback->next = ext_callback;
+ ext_callback = callback;
+}
+
+/**
+ * Invoke extended netif status event
+ * @param netif netif that is affected by change
+ * @param reason change reason
+ * @param args depends on reason, see reason description
+ */
+void netif_invoke_ext_callback(struct netif *netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t *args)
+{
+ netif_ext_callback_t *callback = ext_callback;
+
+ LWIP_ASSERT("netif must be != NULL", netif != NULL);
+
+ while (callback != NULL) {
+ callback->callback_fn(netif, reason, args);
+ callback = callback->next;
+ }
+}
+#endif /* LWIP_NETIF_EXT_STATUS_CALLBACK */
diff --git a/lwip/src/core/pbuf.c b/lwip/src/core/pbuf.c
index 1e5e53b..0ecffd2 100644
--- a/lwip/src/core/pbuf.c
+++ b/lwip/src/core/pbuf.c
@@ -1,6 +1,11 @@
/**
* @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
@@ -12,13 +17,13 @@
*
* 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.
+ * 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
@@ -27,6 +32,8 @@
*
* 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: @ref zerocopyrx
*/
/*
@@ -63,15 +70,15 @@
#include "lwip/opt.h"
+#include "lwip/pbuf.h"
#include "lwip/stats.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
-#include "lwip/pbuf.h"
#include "lwip/sys.h"
-#include "arch/perf.h"
+#include "lwip/netif.h"
#if LWIP_TCP && TCP_QUEUE_OOSEQ
-#include "lwip/tcp_impl.h"
+#include "lwip/priv/tcp_priv.h"
#endif
#if LWIP_CHECKSUM_ON_COPY
#include "lwip/inet_chksum.h"
@@ -92,7 +99,7 @@
#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) { \
+ if (tcpip_try_callback(pbuf_free_ooseq_callback, NULL) != ERR_OK) { \
SYS_ARCH_PROTECT(old_level); \
pbuf_free_ooseq_pending = 0; \
SYS_ARCH_UNPROTECT(old_level); \
@@ -117,19 +124,14 @@ static
void
pbuf_free_ooseq(void)
{
- struct tcp_pcb* pcb;
- SYS_ARCH_DECL_PROTECT(old_level);
-
- SYS_ARCH_PROTECT(old_level);
- pbuf_free_ooseq_pending = 0;
- SYS_ARCH_UNPROTECT(old_level);
+ 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) {
+ if (pcb->ooseq != NULL) {
/** 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;
+ tcp_free_ooseq(pcb);
return;
}
}
@@ -137,7 +139,7 @@ pbuf_free_ooseq(void)
#if !NO_SYS
/**
- * Just a callback function for tcpip_timeout() that calls pbuf_free_ooseq().
+ * Just a callback function for tcpip_callback() that calls pbuf_free_ooseq().
*/
static void
pbuf_free_ooseq_callback(void *arg)
@@ -152,10 +154,7 @@ static void
pbuf_pool_is_empty(void)
{
#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL
- SYS_ARCH_DECL_PROTECT(old_level);
- SYS_ARCH_PROTECT(old_level);
- pbuf_free_ooseq_pending = 1;
- SYS_ARCH_UNPROTECT(old_level);
+ SYS_ARCH_SET(pbuf_free_ooseq_pending, 1);
#else /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
u8_t queued;
SYS_ARCH_DECL_PROTECT(old_level);
@@ -164,7 +163,7 @@ pbuf_pool_is_empty(void)
pbuf_free_ooseq_pending = 1;
SYS_ARCH_UNPROTECT(old_level);
- if(!queued) {
+ if (!queued) {
/* queue a call to pbuf_free_ooseq if not already queued */
PBUF_POOL_FREE_OOSEQ_QUEUE_CALL();
}
@@ -172,14 +171,29 @@ pbuf_pool_is_empty(void)
}
#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */
+/* Initialize members of struct pbuf after allocation */
+static void
+pbuf_init_alloced_pbuf(struct pbuf *p, void *payload, u16_t tot_len, u16_t len, pbuf_type type, u8_t flags)
+{
+ p->next = NULL;
+ p->payload = payload;
+ p->tot_len = tot_len;
+ p->len = len;
+ p->type_internal = (u8_t)type;
+ p->flags = flags;
+ p->ref = 1;
+ p->if_idx = NETIF_NO_INDEX;
+}
+
/**
+ * @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 layer 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:
@@ -206,152 +220,131 @@ pbuf_pool_is_empty(void)
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 */
+ struct pbuf *p;
+ u16_t offset = (u16_t)layer;
LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length));
- /* determine header offset */
- switch (layer) {
- case PBUF_TRANSPORT:
- /* add room for transport (often TCP) layer header */
- offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
- break;
- case PBUF_IP:
- /* add room for IP layer header */
- offset = PBUF_LINK_HLEN + PBUF_IP_HLEN;
- break;
- case PBUF_LINK:
- /* add room for link layer header */
- offset = PBUF_LINK_HLEN;
- break;
- case PBUF_RAW:
- offset = 0;
- break;
- default:
- LWIP_ASSERT("pbuf_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;
+ case PBUF_REF: /* fall through */
+ case PBUF_ROM:
+ p = pbuf_alloc_reference(NULL, length, type);
+ break;
+ case PBUF_POOL: {
+ struct pbuf *q, *last;
+ u16_t rem_len; /* remaining length */
+ p = NULL;
+ last = NULL;
+ rem_len = length;
+ do {
+ u16_t qlen;
+ q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
+ if (q == NULL) {
+ PBUF_POOL_IS_EMPTY();
+ /* free chain so far allocated */
+ if (p) {
+ pbuf_free(p);
+ }
+ /* bail out unsuccessfully */
+ return NULL;
+ }
+ qlen = LWIP_MIN(rem_len, (u16_t)(PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)));
+ pbuf_init_alloced_pbuf(q, LWIP_MEM_ALIGN((void *)((u8_t *)q + SIZEOF_STRUCT_PBUF + offset)),
+ rem_len, qlen, type, 0);
+ LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned",
+ ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0);
+ LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT",
+ (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 );
+ if (p == NULL) {
+ /* allocated head of pbuf chain (into p) */
+ p = q;
+ } else {
+ /* make previous pbuf point to this pbuf */
+ last->next = q;
+ }
+ last = q;
+ rem_len = (u16_t)(rem_len - qlen);
+ offset = 0;
+ } while (rem_len > 0);
+ break;
}
- p->type = type;
- p->next = NULL;
+ case PBUF_RAM: {
+ u16_t payload_len = (u16_t)(LWIP_MEM_ALIGN_SIZE(offset) + LWIP_MEM_ALIGN_SIZE(length));
+ mem_size_t alloc_len = (mem_size_t)(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF) + payload_len);
- /* make the payload pointer point 'offset' bytes into pbuf data memory */
- p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset)));
- LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned",
- ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
- /* the total length of the pbuf chain is the requested size */
- p->tot_len = length;
- /* set the length of the first pbuf in the chain */
- p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset));
- LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
- ((u8_t*)p->payload + p->len <=
- (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
- LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT",
- (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 );
- /* set reference count (needed here in case we fail) */
- p->ref = 1;
-
- /* now allocate the tail of the pbuf chain */
-
- /* remember first pbuf for linkage in next iteration */
- r = p;
- /* remaining length to be allocated */
- rem_len = length - p->len;
- /* any remaining pbufs to be allocated? */
- while (rem_len > 0) {
- q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
- if (q == NULL) {
- PBUF_POOL_IS_EMPTY();
- /* free chain so far allocated */
- pbuf_free(p);
- /* bail out unsuccesfully */
+ /* bug #50040: Check for integer overflow when calculating alloc_len */
+ if ((payload_len < LWIP_MEM_ALIGN_SIZE(length)) ||
+ (alloc_len < LWIP_MEM_ALIGN_SIZE(length))) {
return NULL;
}
- q->type = type;
- q->flags = 0;
- q->next = NULL;
- /* make previous pbuf point to this pbuf */
- r->next = q;
- /* set total length of this pbuf and next in chain */
- LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff);
- q->tot_len = (u16_t)rem_len;
- /* this pbuf length is pool size, unless smaller sized tail */
- q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED);
- q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF);
- LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned",
- ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0);
- LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
- ((u8_t*)p->payload + p->len <=
- (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
- q->ref = 1;
- /* calculate remaining length to be allocated */
- rem_len -= q->len;
- /* remember this pbuf for linkage in next iteration */
- r = q;
- }
- /* end of chain */
- /*r->next = NULL;*/
-
- break;
- case PBUF_RAM:
- /* If pbuf is to be allocated in RAM, allocate memory for it. */
- p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length));
- if (p == NULL) {
- return NULL;
+
+ /* If pbuf is to be allocated in RAM, allocate memory for it. */
+ p = (struct pbuf *)mem_malloc(alloc_len);
+ if (p == NULL) {
+ return NULL;
+ }
+ pbuf_init_alloced_pbuf(p, LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset)),
+ length, length, type, 0);
+ LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned",
+ ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
+ break;
}
- /* 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"));
+ default:
+ LWIP_ASSERT("pbuf_alloc: erroneous type", 0);
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;
}
+/**
+ * @ingroup pbuf
+ * Allocates a pbuf for referenced data.
+ * Referenced data can be volatile (PBUF_REF) or long-lived (PBUF_ROM).
+ *
+ * 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 payload referenced payload
+ * @param length size of the pbuf's payload
+ * @param type this parameter decides how and where the pbuf
+ * should be allocated as follows:
+ *
+ * - PBUF_ROM: 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: 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.
+ *
+ * @return the allocated pbuf.
+ */
+struct pbuf *
+pbuf_alloc_reference(void *payload, u16_t length, pbuf_type type)
+{
+ struct pbuf *p;
+ LWIP_ASSERT("invalid pbuf_type", (type == PBUF_REF) || (type == PBUF_ROM));
+ /* 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_reference: Could not allocate MEMP_PBUF for PBUF_%s.\n",
+ (type == PBUF_ROM) ? "ROM" : "REF"));
+ return NULL;
+ }
+ pbuf_init_alloced_pbuf(p, payload, length, length, type, 0);
+ return p;
+}
+
+
#if LWIP_SUPPORT_CUSTOM_PBUF
-/** Initialize a custom pbuf (already allocated).
+/**
+ * @ingroup pbuf
+ * Initialize a custom pbuf (already allocated).
+ * Example of custom pbuf usage: @ref zerocopyrx
*
- * @param layer flag to define header size
+ * @param l 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)
@@ -363,55 +356,31 @@ pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
* @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*
+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;
+ u16_t offset = (u16_t)l;
+ void *payload;
LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length));
- /* determine header offset */
- switch (l) {
- case PBUF_TRANSPORT:
- /* add room for transport (often TCP) layer header */
- offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
- break;
- case PBUF_IP:
- /* add room for IP layer header */
- offset = PBUF_LINK_HLEN + PBUF_IP_HLEN;
- break;
- case PBUF_LINK:
- /* add room for link layer header */
- offset = PBUF_LINK_HLEN;
- break;
- case PBUF_RAW:
- offset = 0;
- break;
- default:
- LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0);
- return NULL;
- }
-
if (LWIP_MEM_ALIGN_SIZE(offset) + length > payload_mem_len) {
LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length));
return NULL;
}
- p->pbuf.next = NULL;
if (payload_mem != NULL) {
- p->pbuf.payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset);
+ payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset);
} else {
- p->pbuf.payload = NULL;
+ 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;
+ pbuf_init_alloced_pbuf(&p->pbuf, payload, length, length, type, PBUF_FLAG_IS_CUSTOM);
return &p->pbuf;
}
#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
/**
+ * @ingroup pbuf
* Shrink a pbuf chain to a desired length.
*
* @param p pbuf to shrink.
@@ -431,13 +400,9 @@ pbuf_realloc(struct pbuf *p, u16_t new_len)
{
struct pbuf *q;
u16_t rem_len; /* remaining length */
- s32_t grow;
+ u16_t shrink;
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) {
@@ -447,7 +412,7 @@ pbuf_realloc(struct pbuf *p, u16_t new_len)
/* 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;
+ shrink = (u16_t)(p->tot_len - new_len);
/* first, step over any pbufs that should remain in the chain */
rem_len = new_len;
@@ -455,10 +420,9 @@ pbuf_realloc(struct pbuf *p, u16_t new_len)
/* should this pbuf be kept? */
while (rem_len > q->len) {
/* decrease remaining length by pbuf length */
- rem_len -= q->len;
+ rem_len = (u16_t)(rem_len - q->len);
/* decrease total length indicator */
- LWIP_ASSERT("grow < max_u16_t", grow < 0xffff);
- q->tot_len += (u16_t)grow;
+ q->tot_len = (u16_t)(q->tot_len - shrink);
/* proceed to next pbuf in chain */
q = q->next;
LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL);
@@ -468,9 +432,13 @@ pbuf_realloc(struct pbuf *p, u16_t new_len)
/* shrink allocated memory for PBUF_RAM */
/* (other types merely adjust their length fields */
- if ((q->type == PBUF_RAM) && (rem_len != q->len)) {
+ if (pbuf_match_allocsrc(q, PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP) && (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);
+ q = (struct pbuf *)mem_trim(q, (mem_size_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 */
@@ -488,100 +456,230 @@ pbuf_realloc(struct pbuf *p, u16_t new_len)
}
/**
- * 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.
+ * Adjusts the payload pointer to reveal headers in the payload.
+ * @see pbuf_add_header.
*
* @param p pbuf to change the header size.
- * @param header_size_increment Number of bytes to increment header size which
- * increases the size of the pbuf. New space is on the front.
- * (Using a negative value decreases the header size.)
- * If hdr_size_inc is 0, this function does nothing and returns succesful.
+ * @param header_size_increment Number of bytes to increment header size.
+ * @param force Allow 'header_size_increment > 0' for PBUF_REF/PBUF_ROM types
*
- * 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)
+static u8_t
+pbuf_add_header_impl(struct pbuf *p, size_t header_size_increment, u8_t force)
{
- u16_t type;
+ u16_t type_internal;
void *payload;
u16_t increment_magnitude;
LWIP_ASSERT("p != NULL", p != NULL);
- if ((header_size_increment == 0) || (p == NULL)) {
+ if ((header_size_increment == 0) || (p == NULL) || (header_size_increment > 0xFFFF)) {
return 0;
}
-
- if (header_size_increment < 0){
- increment_magnitude = -header_size_increment;
- /* Check that we aren't going to move off the end of the pbuf */
- LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;);
- } else {
- increment_magnitude = header_size_increment;
-#if 0
- /* Can't assert these as some callers speculatively call
- pbuf_header() to see if it's OK. Will return 1 below instead. */
- /* Check that we've got the correct type of pbuf to work with */
- LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL",
- p->type == PBUF_RAM || p->type == PBUF_POOL);
- /* Check that we aren't going to move off the beginning of the pbuf */
- LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF",
- (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF);
-#endif
+
+ increment_magnitude = (u16_t)header_size_increment;
+ /* Do not allow tot_len to wrap as a result. */
+ if ((u16_t)(increment_magnitude + p->tot_len) < increment_magnitude) {
+ return 1;
}
- type = p->type;
- /* remember current payload pointer */
- payload = p->payload;
+ type_internal = p->type_internal;
/* pbuf types containing payloads? */
- if (type == PBUF_RAM || type == PBUF_POOL) {
+ if (type_internal & PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS) {
/* set new payload pointer */
- p->payload = (u8_t *)p->payload - header_size_increment;
+ payload = (u8_t *)p->payload - header_size_increment;
/* boundary check fails? */
- if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) {
- LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
- ("pbuf_header: failed as %p < %p (not enough space for new header size)\n",
- (void *)p->payload, (void *)(p + 1)));
- /* restore old payload pointer */
- p->payload = payload;
- /* bail out unsuccesfully */
+ if ((u8_t *)payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) {
+ LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE,
+ ("pbuf_add_header: failed as %p < %p (not enough space for new header size)\n",
+ (void *)payload, (void *)((u8_t *)p + SIZEOF_STRUCT_PBUF)));
+ /* bail out unsuccessfully */
return 1;
}
- /* pbuf types refering to external payloads? */
- } else if (type == PBUF_REF || type == PBUF_ROM) {
+ /* pbuf types referring to external payloads? */
+ } else {
/* 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;
+ if (force) {
+ payload = (u8_t *)p->payload - header_size_increment;
} else {
/* cannot expand payload to front (yet!)
- * bail out unsuccesfully */
+ * bail out unsuccessfully */
return 1;
}
- } else {
- /* Unknown type */
- LWIP_ASSERT("bad pbuf type", 0);
- return 1;
}
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_add_header: old %p new %p (%"U16_F")\n",
+ (void *)p->payload, (void *)payload, increment_magnitude));
+
+ /* modify pbuf fields */
+ p->payload = payload;
+ p->len = (u16_t)(p->len + increment_magnitude);
+ p->tot_len = (u16_t)(p->tot_len + increment_magnitude);
+
+
+ return 0;
+}
+
+/**
+ * Adjusts the payload pointer to reveal headers in the payload.
+ *
+ * Adjusts the ->payload pointer so that space for a header
+ * 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.
+ * 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_add_header(struct pbuf *p, size_t header_size_increment)
+{
+ return pbuf_add_header_impl(p, header_size_increment, 0);
+}
+
+/**
+ * Same as @ref pbuf_add_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_add_header_force(struct pbuf *p, size_t header_size_increment)
+{
+ return pbuf_add_header_impl(p, header_size_increment, 1);
+}
+
+/**
+ * Adjusts the payload pointer to hide headers in the payload.
+ *
+ * Adjusts the ->payload pointer so that space for a header
+ * disappears in the pbuf payload.
+ *
+ * The ->payload, ->tot_len and ->len fields are adjusted.
+ *
+ * @param p pbuf to change the header size.
+ * @param header_size_decrement Number of bytes to decrement header size which
+ * decreases the size of the pbuf.
+ * If hdr_size_inc is 0, this function does nothing and returns successful.
+ * @return non-zero on failure, zero on success.
+ *
+ */
+u8_t
+pbuf_remove_header(struct pbuf *p, size_t header_size_decrement)
+{
+ void *payload;
+ u16_t increment_magnitude;
+
+ LWIP_ASSERT("p != NULL", p != NULL);
+ if ((header_size_decrement == 0) || (p == NULL) || (header_size_decrement > 0xFFFF)) {
+ return 0;
+ }
+
+ increment_magnitude = (u16_t)header_size_decrement;
+ /* 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;);
+
+ /* remember current payload pointer */
+ payload = p->payload;
+ LWIP_UNUSED_ARG(payload); /* only used in LWIP_DEBUGF below */
+
+ /* increase payload pointer (guarded by length check above) */
+ p->payload = (u8_t *)p->payload + header_size_decrement;
/* modify pbuf length fields */
- p->len += header_size_increment;
- p->tot_len += header_size_increment;
+ p->len = (u16_t)(p->len - increment_magnitude);
+ p->tot_len = (u16_t)(p->tot_len - increment_magnitude);
- LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"S16_F")\n",
- (void *)payload, (void *)p->payload, header_size_increment));
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_remove_header: old %p new %p (%"U16_F")\n",
+ (void *)payload, (void *)p->payload, increment_magnitude));
return 0;
}
+static u8_t
+pbuf_header_impl(struct pbuf *p, s16_t header_size_increment, u8_t force)
+{
+ if (header_size_increment < 0) {
+ return pbuf_remove_header(p, (size_t) - header_size_increment);
+ } else {
+ return pbuf_add_header_impl(p, (size_t)header_size_increment, force);
+ }
+}
+
+/**
+ * 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);
+}
+
+/** Similar to pbuf_header(-size) but de-refs header pbufs for (size >= p->len)
+ *
+ * @param q pbufs to operate on
+ * @param size The number of bytes to remove from the beginning of the pbuf list.
+ * While size >= p->len, pbufs are freed.
+ * ATTENTION: this is the opposite direction as @ref pbuf_header, but
+ * takes an u16_t not s16_t!
+ * @return the new head pbuf
+ */
+struct pbuf *
+pbuf_free_header(struct pbuf *q, u16_t size)
+{
+ struct pbuf *p = q;
+ u16_t free_left = size;
+ while (free_left && p) {
+ if (free_left >= p->len) {
+ struct pbuf *f = p;
+ free_left = (u16_t)(free_left - p->len);
+ p = p->next;
+ f->next = 0;
+ pbuf_free(f);
+ } else {
+ pbuf_remove_header(p, free_left);
+ free_left = 0;
+ }
+ }
+ return p;
+}
+
+/**
+ * @ingroup pbuf
* Dereference a pbuf chain or queue and deallocate any no-longer-used
* pbufs at the head of this chain or queue.
*
@@ -606,7 +704,7 @@ pbuf_header(struct pbuf *p, s16_t header_size_increment)
*
* 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
@@ -617,7 +715,7 @@ pbuf_header(struct pbuf *p, s16_t header_size_increment)
u8_t
pbuf_free(struct pbuf *p)
{
- u16_t type;
+ u8_t alloc_src;
struct pbuf *q;
u8_t count;
@@ -625,22 +723,18 @@ pbuf_free(struct pbuf *p)
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"));
+ ("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;
+ LWIP_PBUF_REF_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
@@ -656,32 +750,35 @@ pbuf_free(struct pbuf *p)
/* 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;
+ alloc_src = pbuf_get_allocsrc(p);
#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;
+ 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) {
+ if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF_POOL) {
memp_free(MEMP_PBUF_POOL, p);
- /* is this a ROM or RAM referencing pbuf? */
- } else if (type == PBUF_ROM || type == PBUF_REF) {
+ /* is this a ROM or RAM referencing pbuf? */
+ } else if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF) {
memp_free(MEMP_PBUF, p);
- /* type == PBUF_RAM */
- } else {
+ /* type == PBUF_RAM */
+ } else if (alloc_src == PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP) {
mem_free(p);
+ } else {
+ /* @todo: support freeing other types */
+ LWIP_ASSERT("invalid pbuf type", 0);
}
}
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) */
+ /* 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 */
@@ -699,11 +796,10 @@ pbuf_free(struct pbuf *p)
* @param p first pbuf of chain
* @return the number of pbufs in a chain
*/
-
-u8_t
-pbuf_clen(struct pbuf *p)
+u16_t
+pbuf_clen(const struct pbuf *p)
{
- u8_t len;
+ u16_t len;
len = 0;
while (p != NULL) {
@@ -714,6 +810,7 @@ pbuf_clen(struct pbuf *p)
}
/**
+ * @ingroup pbuf
* Increment the reference count of the pbuf.
*
* @param p pbuf to increase reference counter of
@@ -722,25 +819,27 @@ pbuf_clen(struct pbuf *p)
void
pbuf_ref(struct pbuf *p)
{
- SYS_ARCH_DECL_PROTECT(old_level);
/* pbuf given? */
if (p != NULL) {
- SYS_ARCH_PROTECT(old_level);
- ++(p->ref);
- SYS_ARCH_UNPROTECT(old_level);
+ SYS_ARCH_SET(p->ref, (u8_t)(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.
- *
+ *
+ * This function explicitly does not check for tot_len overflow to prevent
+ * failing to queue too long pbufs. This can produce invalid pbufs, so
+ * handle with care!
+ *
* @see pbuf_chain()
*/
-
void
pbuf_cat(struct pbuf *h, struct pbuf *t)
{
@@ -752,13 +851,13 @@ pbuf_cat(struct pbuf *h, struct pbuf *t)
/* 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->tot_len = (u16_t)(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;
+ p->tot_len = (u16_t)(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,
@@ -767,11 +866,12 @@ pbuf_cat(struct pbuf *h, struct pbuf *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.
@@ -811,7 +911,7 @@ pbuf_dechain(struct pbuf *p)
/* 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;
+ q->tot_len = (u16_t)(p->tot_len - p->len);
/* decouple pbuf from remainder */
p->next = NULL;
/* total length of pbuf p is its own length only */
@@ -831,7 +931,7 @@ pbuf_dechain(struct pbuf *p)
}
/**
- *
+ * @ingroup pbuf
* Create PBUF_RAM copies of pbufs.
*
* Used to queue packets on behalf of the lwIP stack, such as
@@ -849,20 +949,19 @@ pbuf_dechain(struct pbuf *p)
* enough to hold p_from
*/
err_t
-pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
+pbuf_copy(struct pbuf *p_to, const struct pbuf *p_from)
{
- u16_t offset_to=0, offset_from=0, len;
+ size_t offset_to = 0, offset_from = 0, len;
LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n",
- (void*)p_to, (void*)p_from));
+ (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
- {
+ 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 */
@@ -871,7 +970,7 @@ pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
/* 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);
+ 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);
@@ -885,18 +984,18 @@ pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
/* 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;);
+ 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)) {
+ if ((p_from != NULL) && (p_from->len == p_from->tot_len)) {
/* don't copy more than one packet! */
- LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
+ 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)) {
+ if ((p_to != NULL) && (p_to->len == p_to->tot_len)) {
/* don't copy more than one packet! */
- LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
- (p_to->next == NULL), return ERR_VAL;);
+ 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"));
@@ -904,48 +1003,44 @@ pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
}
/**
+ * @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
+ * @param len length of data to copy (dataptr must be big enough). No more
* than buf->tot_len will be copied, irrespective of len
* @param offset offset into the packet buffer from where to begin copying len bytes
* @return the number of bytes copied, or 0 on failure
*/
u16_t
-pbuf_copy_partial(struct pbuf *buf, void *dataptr, u16_t len, u16_t offset)
+pbuf_copy_partial(const struct pbuf *buf, void *dataptr, u16_t len, u16_t offset)
{
- struct pbuf *p;
- u16_t left;
+ const struct pbuf *p;
+ u16_t left = 0;
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) {
+ 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;
+ offset = (u16_t)(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;
+ buf_copy_len = (u16_t)(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;
+ MEMCPY(&((char *)dataptr)[left], &((char *)p->payload)[offset], buf_copy_len);
+ copied_total = (u16_t)(copied_total + buf_copy_len);
+ left = (u16_t)(left + buf_copy_len);
+ len = (u16_t)(len - buf_copy_len);
offset = 0;
}
}
@@ -953,6 +1048,135 @@ pbuf_copy_partial(struct pbuf *buf, void *dataptr, u16_t len, u16_t offset)
}
/**
+ * @ingroup pbuf
+ * Get part of a pbuf's payload as contiguous memory. The returned memory is
+ * either a pointer into the pbuf's payload or, if split over multiple pbufs,
+ * a copy into the user-supplied buffer.
+ *
+ * @param p the pbuf from which to copy data
+ * @param buffer the application supplied buffer
+ * @param bufsize size of 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
+ */
+void *
+pbuf_get_contiguous(const struct pbuf *p, void *buffer, size_t bufsize, u16_t len, u16_t offset)
+{
+ const struct pbuf *q;
+
+ LWIP_ERROR("pbuf_get_contiguous: invalid buf", (p != NULL), return NULL;);
+ LWIP_ERROR("pbuf_get_contiguous: invalid dataptr", (buffer != NULL), return NULL;);
+ LWIP_ERROR("pbuf_get_contiguous: invalid dataptr", (bufsize >= len), return NULL;);
+
+ for (q = p; q != NULL; q = q->next) {
+ if ((offset != 0) && (offset >= q->len)) {
+ /* don't copy from this buffer -> on to the next */
+ offset = (u16_t)(offset - q->len);
+ } else {
+ if (q->len >= (offset + len)) {
+ /* all data in this pbuf, return zero-copy */
+ return (u8_t *)q->payload + offset;
+ }
+ /* need to copy */
+ if (pbuf_copy_partial(q, buffer, len, offset) != len) {
+ /* copying failed: pbuf is too short */
+ return NULL;
+ }
+ return buffer;
+ }
+ }
+ /* pbuf is too short (offset does not fit in) */
+ return NULL;
+}
+
+#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 = (u16_t)(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 = (u16_t)(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 = (u16_t)(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.
*
@@ -966,19 +1190,20 @@ 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;
+ size_t buf_copy_len;
+ size_t total_copy_len = len;
+ size_t copied_total = 0;
- LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return 0;);
- LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return 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) {
+ 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) {
@@ -986,7 +1211,7 @@ pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
buf_copy_len = p->len;
}
/* copy the necessary parts of the buffer */
- MEMCPY(p->payload, &((char*)dataptr)[copied_total], buf_copy_len);
+ MEMCPY(p->payload, &((const char *)dataptr)[copied_total], buf_copy_len);
total_copy_len -= buf_copy_len;
copied_total += buf_copy_len;
}
@@ -995,6 +1220,43 @@ pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
}
/**
+ * @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_ASSERT("chekc pbuf_skip result", target_offset < q->len);
+ first_copy_len = (u16_t)LWIP_MIN(q->len - target_offset, len);
+ MEMCPY(((u8_t *)q->payload) + target_offset, dataptr, first_copy_len);
+ remaining_len = (u16_t)(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
@@ -1006,22 +1268,46 @@ pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
* @return a new, single pbuf (p->next is NULL)
* or the old pbuf if allocation fails
*/
-struct pbuf*
+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);
+ q = pbuf_clone(layer, PBUF_RAM, p);
if (q == NULL) {
/* @todo: what do we do now? */
return p;
}
+ pbuf_free(p);
+ return q;
+}
+
+/**
+ * @ingroup pbuf
+ * Allocates a new pbuf of same length (via pbuf_alloc()) and copies the source
+ * pbuf into this new pbuf (using pbuf_copy()).
+ *
+ * @param layer pbuf_layer of the new pbuf
+ * @param type this parameter decides how and where the pbuf should be allocated
+ * (@see pbuf_alloc())
+ * @param p the source pbuf
+ *
+ * @return a new pbuf or NULL if allocation fails
+ */
+struct pbuf *
+pbuf_clone(pbuf_layer layer, pbuf_type type, struct pbuf *p)
+{
+ struct pbuf *q;
+ err_t err;
+ q = pbuf_alloc(layer, p->tot_len, type);
+ if (q == NULL) {
+ return NULL;
+ }
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;
}
@@ -1054,7 +1340,7 @@ pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
return ERR_ARG;
}
- dst_ptr = ((char*)p->payload) + start_offset;
+ 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);
@@ -1066,7 +1352,9 @@ pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
}
#endif /* LWIP_CHECKSUM_ON_COPY */
- /** Get one byte from the specified position in a pbuf
+/**
+ * @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
@@ -1074,59 +1362,101 @@ pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
* @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len
*/
u8_t
-pbuf_get_at(struct pbuf* p, u16_t offset)
+pbuf_get_at(const struct pbuf *p, u16_t offset)
{
- u16_t copy_from = offset;
- struct pbuf* q = p;
-
- /* get the correct pbuf */
- while ((q != NULL) && (q->len <= copy_from)) {
- copy_from -= q->len;
- q = q->next;
+ 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 > copy_from)) {
- return ((u8_t*)q->payload)[copy_from];
+ if ((q != NULL) && (q->len > q_idx)) {
+ return ((u8_t *)q->payload)[q_idx];
}
- return 0;
+ return -1;
}
-/** Compare pbuf contents at specified offset with memory s2, both of length n
+/**
+ * @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 wich to start comparing
+ * @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(struct pbuf* p, u16_t offset, const void* s2, u16_t n)
+pbuf_memcmp(const struct pbuf *p, u16_t offset, const void *s2, u16_t n)
{
u16_t start = offset;
- struct pbuf* q = p;
+ const struct pbuf *q = p;
+ u16_t i;
- /* get the correct pbuf */
+ /* 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;
+ start = (u16_t)(start - q->len);
q = q->next;
}
+
/* return requested data if pbuf is OK */
- if ((q != NULL) && (q->len > start)) {
- u16_t i;
- for(i = 0; i < n; i++) {
- u8_t a = pbuf_get_at(q, start + i);
- u8_t b = ((u8_t*)s2)[i];
- if (a != b) {
- return i+1;
- }
+ 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, (u16_t)(start + i));
+ u8_t b = ((const u8_t *)s2)[i];
+ if (a != b) {
+ return (u16_t)LWIP_MIN(i + 1, 0xFFFF);
}
- return 0;
}
- return 0xffff;
+ return 0;
}
-/** Find occurrence of mem (with length mem_len) in pbuf p, starting at offset
+/**
+ * @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
@@ -1137,24 +1467,23 @@ pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n)
* @return 0xFFFF if substr was not found in p or the index where it was found
*/
u16_t
-pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)
+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;
+ u16_t max_cmp_start = (u16_t)(p->tot_len - mem_len);
if (p->tot_len >= mem_len + start_offset) {
- for(i = start_offset; i <= max; ) {
+ for (i = start_offset; i <= max_cmp_start; i++) {
u16_t plus = pbuf_memcmp(p, i, mem, mem_len);
if (plus == 0) {
return i;
- } else {
- i += plus;
}
}
}
return 0xFFFF;
}
-/** Find occurrence of substr with length substr_len in pbuf p, start at offset
+/**
+ * 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!
@@ -1165,7 +1494,7 @@ pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)
* @return 0xFFFF if substr was not found in p or the index where it was found
*/
u16_t
-pbuf_strstr(struct pbuf* p, const char* substr)
+pbuf_strstr(const struct pbuf *p, const char *substr)
{
size_t substr_len;
if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) {
diff --git a/lwip/src/core/raw.c b/lwip/src/core/raw.c
index 68b23c6..b3a0ba2 100644
--- a/lwip/src/core/raw.c
+++ b/lwip/src/core/raw.c
@@ -2,8 +2,15 @@
* @file
* Implementation of raw protocol PCBs for low-level handling of
* different types of protocols besides (or overriding) those
- * already available in lwIP.
+ * 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
*/
/*
@@ -48,15 +55,64 @@
#include "lwip/netif.h"
#include "lwip/raw.h"
#include "lwip/stats.h"
-#include "arch/perf.h"
#include "lwip/ip6.h"
#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
#include <string.h>
/** The list of RAW PCBs */
static struct raw_pcb *raw_pcbs;
+static u8_t
+raw_input_local_match(struct raw_pcb *pcb, u8_t broadcast)
+{
+ LWIP_UNUSED_ARG(broadcast); /* in IPv6 only case */
+
+ /* check if PCB is bound to specific netif */
+ if ((pcb->netif_idx != NETIF_NO_INDEX) &&
+ (pcb->netif_idx != netif_get_index(ip_data.current_input_netif))) {
+ return 0;
+ }
+
+#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.
@@ -78,73 +134,65 @@ u8_t
raw_input(struct pbuf *p, struct netif *inp)
{
struct raw_pcb *pcb, *prev;
- struct ip_hdr *iphdr;
s16_t proto;
u8_t eaten = 0;
-#if LWIP_IPV6
- struct ip6_hdr *ip6hdr;
-#endif /* LWIP_IPV6 */
-
+ u8_t broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), ip_current_netif());
LWIP_UNUSED_ARG(inp);
- iphdr = (struct ip_hdr *)p->payload;
#if LWIP_IPV6
- if (IPH_V(iphdr) == 6) {
- ip6hdr = (struct ip6_hdr *)p->payload;
+#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(iphdr);
+ 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) && IP_PCB_IPVER_INPUT_MATCH(pcb) &&
- (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip) ||
- ipX_addr_cmp(PCB_ISIPV6(pcb), &(pcb->local_ip), ipX_current_dest_addr()))) {
-#if IP_SOF_BROADCAST_RECV
- /* broadcast filter? */
- if ((ip_get_option(pcb, SOF_BROADCAST) || !ip_addr_isbroadcast(ip_current_dest_addr(), inp))
-#if LWIP_IPV6
- && !PCB_ISIPV6(pcb)
-#endif /* LWIP_IPV6 */
- )
-#endif /* IP_SOF_BROADCAST_RECV */
- {
- /* receive callback function available? */
- if (pcb->recv.ip4 != NULL) {
+ if ((pcb->protocol == proto) && raw_input_local_match(pcb, broadcast) &&
+ (((pcb->flags & RAW_FLAGS_CONNECTED) == 0) ||
+ ip_addr_cmp(&pcb->remote_ip, ip_current_src_addr()))) {
+ /* receive callback function available? */
+ if (pcb->recv != NULL) {
#ifndef LWIP_NOASSERT
- void* old_payload = p->payload;
+ void *old_payload = p->payload;
#endif
- /* the receive callback function did not eat the packet? */
- eaten = pcb->recv.ip4(pcb->recv_arg, pcb, p, ip_current_src_addr());
- if (eaten != 0) {
- /* receive function ate the packet */
- p = NULL;
- eaten = 1;
- if (prev != NULL) {
+ /* 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);
+ 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 */
+ /* no receive callback function was set for this raw PCB */
}
+ /* drop the packet */
prev = pcb;
pcb = pcb->next;
}
@@ -152,27 +200,63 @@ raw_input(struct pbuf *p, struct netif *inp)
}
/**
+ * @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 IP_ADDR_ANY to
+ * @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 occured.
+ * - 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, ip_addr_t *ipaddr)
+raw_bind(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
{
- ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->local_ip, ipaddr);
+ if ((pcb == NULL) || (ipaddr == NULL)) {
+ return ERR_VAL;
+ }
+ ip_addr_set_ipaddr(&pcb->local_ip, ipaddr);
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+ /* If the given IP address should have a zone but doesn't, assign one now.
+ * This is legacy support: scope-aware callers should always provide properly
+ * zoned source addresses. */
+ if (IP_IS_V6(&pcb->local_ip) &&
+ ip6_addr_lacks_zone(ip_2_ip6(&pcb->local_ip), IP6_UNKNOWN)) {
+ ip6_addr_select_zone(ip_2_ip6(&pcb->local_ip), ip_2_ip6(&pcb->local_ip));
+ }
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
return ERR_OK;
}
/**
+ * @ingroup raw_raw
+ * Bind an RAW PCB to a specific netif.
+ * After calling this function, all packets received via this PCB
+ * are guaranteed to have come in via the specified netif, and all
+ * outgoing packets will go out via the specified netif.
+ *
+ * @param pcb RAW PCB to be bound with netif.
+ * @param netif netif to bind to. Can be NULL.
+ *
+ * @see raw_disconnect()
+ */
+void
+raw_bind_netif(struct raw_pcb *pcb, const struct netif *netif)
+{
+ if (netif != NULL) {
+ pcb->netif_idx = netif_get_index(netif);
+ } else {
+ pcb->netif_idx = NETIF_NO_INDEX;
+ }
+}
+
+/**
+ * @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
*
@@ -186,40 +270,72 @@ raw_bind(struct raw_pcb *pcb, ip_addr_t *ipaddr)
* @see raw_disconnect() and raw_sendto()
*/
err_t
-raw_connect(struct raw_pcb *pcb, ip_addr_t *ipaddr)
+raw_connect(struct raw_pcb *pcb, const ip_addr_t *ipaddr)
{
- ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->remote_ip, ipaddr);
+ if ((pcb == NULL) || (ipaddr == NULL)) {
+ return ERR_VAL;
+ }
+ ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+ /* If the given IP address should have a zone but doesn't, assign one now,
+ * using the bound address to make a more informed decision when possible. */
+ if (IP_IS_V6(&pcb->remote_ip) &&
+ ip6_addr_lacks_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNKNOWN)) {
+ ip6_addr_select_zone(ip_2_ip6(&pcb->remote_ip), ip_2_ip6(&pcb->local_ip));
+ }
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
+ raw_set_flags(pcb, RAW_FLAGS_CONNECTED);
return ERR_OK;
}
+/**
+ * @ingroup raw_raw
+ * Disconnect a RAW PCB.
+ *
+ * @param pcb the raw pcb to disconnect.
+ */
+void
+raw_disconnect(struct raw_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->netif_idx = NETIF_NO_INDEX;
+ /* mark PCB as unconnected */
+ raw_clear_flags(pcb, RAW_FLAGS_CONNECTED);
+}
/**
+ * @ingroup raw_raw
* Set the callback function for received packets that match the
- * raw PCB's protocol and binding.
- *
+ * raw PCB's protocol and binding.
+ *
* The callback function MUST either
* - eat the packet by calling pbuf_free() and returning non-zero. The
* packet will not be passed to other raw PCBs or other protocol layers.
* - not free the packet, and return zero. The packet will be matched
* against further PCBs and/or forwarded to another protocol layers.
- *
- * @return non-zero if the packet was free()d, zero if the packet remains
- * available for others.
*/
void
raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg)
{
/* remember recv() callback and user data */
- pcb->recv.ip4 = recv;
+ pcb->recv = recv;
pcb->recv_arg = recv_arg;
}
/**
- * Send the raw IP packet to the given address. Note that actually you cannot
- * modify the IP headers (this is inconsistent with the receive callback where
- * you actually get the IP headers), you can only specify the IP payload here.
- * It requires some more changes in lwIP. (there will be a raw_send() function
- * then.)
+ * @ingroup raw_raw
+ * Send the raw IP packet to the given address. An IP header will be prepended
+ * to the packet, unless the RAW_FLAGS_HDRINCL flag is set on the PCB. In that
+ * case, the packet must include an IP header, which will then be sent as is.
*
* @param pcb the raw pcb which to send
* @param p the IP payload to send
@@ -227,25 +343,115 @@ raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg)
*
*/
err_t
-raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr)
+raw_sendto(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr)
{
- err_t err;
struct netif *netif;
- ipX_addr_t *src_ip;
- struct pbuf *q; /* q will be sent down the stack */
- s16_t header_size;
- ipX_addr_t *dst_ip = ip_2_ipX(ipaddr);
+ const ip_addr_t *src_ip;
+
+ 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 (pcb->netif_idx != NETIF_NO_INDEX) {
+ netif = netif_get_by_index(pcb->netif_idx);
+ } else {
+#if LWIP_MULTICAST_TX_OPTIONS
+ netif = NULL;
+ if (ip_addr_ismulticast(ipaddr)) {
+ /* For multicast-destined packets, use the user-provided interface index to
+ * determine the outgoing interface, if an interface index is set and a
+ * matching netif can be found. Otherwise, fall back to regular routing. */
+ netif = netif_get_by_index(pcb->mcast_ifindex);
+ }
+
+ if (netif == NULL)
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+ {
+ 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);
+ return ERR_RTE;
+ }
+
+ if (ip_addr_isany(&pcb->local_ip) || ip_addr_ismulticast(&pcb->local_ip)) {
+ /* use outgoing network interface IP address as source address */
+ src_ip = ip_netif_get_local_ip(netif, ipaddr);
#if LWIP_IPV6
- PCB_ISIPV6(pcb) ? IP6_HLEN :
+ if (src_ip == NULL) {
+ return ERR_RTE;
+ }
#endif /* LWIP_IPV6 */
- IP_HLEN);
+ } else {
+ /* use RAW PCB local IP address as source address */
+ src_ip = &pcb->local_ip;
+ }
+ return raw_sendto_if_src(pcb, p, ipaddr, netif, src_ip);
+}
+
+/**
+ * @ingroup raw_raw
+ * Send the raw IP packet to the given address, using a particular outgoing
+ * netif and source IP address. An IP header will be prepended to the packet,
+ * unless the RAW_FLAGS_HDRINCL flag is set on the PCB. In that case, the
+ * packet must include an IP header, which will then be sent as is.
+ *
+ * @param pcb RAW PCB used to send the data
+ * @param p chain of pbufs to be sent
+ * @param dst_ip destination IP address
+ * @param netif the netif used for sending
+ * @param src_ip source IP address
+ */
+err_t
+raw_sendto_if_src(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip,
+ struct netif *netif, const ip_addr_t *src_ip)
+{
+ err_t err;
+ struct pbuf *q; /* q will be sent down the stack */
+ u16_t header_size;
+ u8_t ttl;
+
+ if ((pcb == NULL) || (dst_ip == NULL) || (netif == NULL) || (src_ip == NULL) ||
+ !IP_ADDR_PCB_VERSION_MATCH(pcb, src_ip) || !IP_ADDR_PCB_VERSION_MATCH(pcb, dst_ip)) {
+ return ERR_VAL;
+ }
+
+ header_size = (
+#if LWIP_IPV4 && LWIP_IPV6
+ IP_IS_V6(dst_ip) ? IP6_HLEN : IP_HLEN);
+#elif LWIP_IPV4
+ IP_HLEN);
+#else
+ IP6_HLEN);
+#endif
+
+ /* Handle the HDRINCL option as an exception: none of the code below applies
+ * to this case, and sending the packet needs to be done differently too. */
+ if (pcb->flags & RAW_FLAGS_HDRINCL) {
+ /* A full header *must* be present in the first pbuf of the chain, as the
+ * output routines may access its fields directly. */
+ if (p->len < header_size) {
+ return ERR_VAL;
+ }
+ /* @todo multicast loop support, if at all desired for this scenario.. */
+ NETIF_SET_HINTS(netif, &pcb->netif_hints);
+ err = ip_output_if_hdrincl(p, src_ip, dst_ip, netif);
+ NETIF_RESET_HINTS(netif);
+ return err;
+ }
+
+ /* packet too large to add an IP header without causing an overflow? */
+ if ((u16_t)(p->tot_len + header_size) < p->tot_len) {
+ return ERR_MEM;
+ }
/* not enough space to add an IP header to first pbuf in given p chain? */
- if (pbuf_header(p, header_size)) {
+ if (pbuf_add_header(p, header_size)) {
/* allocate header in new pbuf */
q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM);
/* new header pbuf could not be allocated? */
@@ -259,34 +465,19 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr)
}
/* { 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 {
+ } else {
/* first pbuf q equals given pbuf */
q = p;
- if(pbuf_header(q, -header_size)) {
+ if (pbuf_remove_header(q, header_size)) {
LWIP_ASSERT("Can't restore header we just removed!", 0);
return ERR_MEM;
}
}
- netif = ipX_route(PCB_ISIPV6(pcb), &pcb->local_ip, dst_ip);
- if (netif == NULL) {
- LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to "));
- ipX_addr_debug_print(PCB_ISIPV6(pcb), RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, dst_ip);
- /* free any temporary header pbuf allocated by pbuf_header() */
- if (q != p) {
- pbuf_free(q);
- }
- return ERR_RTE;
- }
-
#if IP_SOF_BROADCAST
-#if LWIP_IPV6
- /* @todo: why does IPv6 not filter broadcast with SOF_BROADCAST enabled? */
- if (!PCB_ISIPV6(pcb))
-#endif /* LWIP_IPV6 */
- {
+ if (IP_IS_V4(dst_ip)) {
/* broadcast filter? */
- if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(ipaddr, netif)) {
+ if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(dst_ip, 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) {
@@ -297,25 +488,33 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr)
}
#endif /* IP_SOF_BROADCAST */
- if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) {
- /* use outgoing network interface IP address as source address */
- src_ip = ipX_netif_get_local_ipX(PCB_ISIPV6(pcb), netif, dst_ip);
+ /* Multicast Loop? */
+#if LWIP_MULTICAST_TX_OPTIONS
+ if (((pcb->flags & RAW_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip)) {
+ q->flags |= PBUF_FLAG_MCASTLOOP;
+ }
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
#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 requested, based on the IPV6_CHECKSUM socket option per RFC3542,
+ compute the checksum and update the checksum in the payload. */
+ if (IP_IS_V6(dst_ip) && pcb->chksum_reqd) {
+ u16_t chksum = ip6_chksum_pseudo(p, pcb->protocol, p->tot_len, ip_2_ip6(src_ip), ip_2_ip6(dst_ip));
+ 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 = ipX_output_if(PCB_ISIPV6(pcb), q, ipX_2_ip(src_ip), ipX_2_ip(dst_ip), pcb->ttl, pcb->tos, pcb->protocol, netif);
- NETIF_SET_HWADDRHINT(netif, NULL);
+ /* Determine TTL to use */
+#if LWIP_MULTICAST_TX_OPTIONS
+ ttl = (ip_addr_ismulticast(dst_ip) ? raw_get_multicast_ttl(pcb) : pcb->ttl);
+#else /* LWIP_MULTICAST_TX_OPTIONS */
+ ttl = pcb->ttl;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
+
+ NETIF_SET_HINTS(netif, &pcb->netif_hints);
+ err = ip_output_if(q, src_ip, dst_ip, ttl, pcb->tos, pcb->protocol, netif);
+ NETIF_RESET_HINTS(netif);
/* did we chain a header earlier? */
if (q != p) {
@@ -326,6 +525,7 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr)
}
/**
+ * @ingroup raw_raw
* Send the raw IP packet to the address given by raw_connect()
*
* @param pcb the raw pcb which to send
@@ -335,10 +535,11 @@ raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr)
err_t
raw_send(struct raw_pcb *pcb, struct pbuf *p)
{
- return raw_sendto(pcb, p, ipX_2_ip(&pcb->remote_ip));
+ 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
@@ -356,11 +557,12 @@ raw_remove(struct raw_pcb *pcb)
raw_pcbs = raw_pcbs->next;
/* pcb not 1st in list */
} else {
- for(pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) {
+ 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;
}
}
}
@@ -368,6 +570,7 @@ raw_remove(struct raw_pcb *pcb)
}
/**
+ * @ingroup raw_raw
* Create a RAW PCB.
*
* @return The RAW PCB which was created. NULL if the PCB data structure
@@ -391,32 +594,65 @@ raw_new(u8_t proto)
memset(pcb, 0, sizeof(struct raw_pcb));
pcb->protocol = proto;
pcb->ttl = RAW_TTL;
+#if LWIP_MULTICAST_TX_OPTIONS
+ raw_set_multicast_ttl(pcb, RAW_TTL);
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
pcb->next = raw_pcbs;
raw_pcbs = pcb;
}
return pcb;
}
-#if LWIP_IPV6
/**
- * Create a RAW PCB for IPv6.
+ * @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_ip6(u8_t proto)
+raw_new_ip_type(u8_t type, u8_t proto)
{
struct raw_pcb *pcb;
pcb = raw_new(proto);
- ip_set_v6(pcb, 1);
+#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;
}
-#endif /* LWIP_IPV6 */
+
+/** 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/lwip/src/core/snmp/asn1_dec.c b/lwip/src/core/snmp/asn1_dec.c
deleted file mode 100644
index 1d56582..0000000
--- a/lwip/src/core/snmp/asn1_dec.c
+++ /dev/null
@@ -1,657 +0,0 @@
-/**
- * @file
- * Abstract Syntax Notation One (ISO 8824, 8825) decoding
- *
- * @todo not optimised (yet), favor correctness over speed, favor speed over size
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/snmp_asn1.h"
-
-/**
- * Retrieves type field from incoming pbuf chain.
- *
- * @param p points to a pbuf holding an ASN1 coded type field
- * @param ofs points to the offset within the pbuf chain of the ASN1 coded type field
- * @param type return ASN1 type
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
- */
-err_t
-snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type)
-{
- u16_t plen, base;
- u8_t *msg_ptr;
-
- plen = 0;
- while (p != NULL)
- {
- base = plen;
- plen += p->len;
- if (ofs < plen)
- {
- msg_ptr = (u8_t*)p->payload;
- msg_ptr += ofs - base;
- *type = *msg_ptr;
- return ERR_OK;
- }
- p = p->next;
- }
- /* p == NULL, ofs >= plen */
- return ERR_ARG;
-}
-
-/**
- * Decodes length field from incoming pbuf chain into host length.
- *
- * @param p points to a pbuf holding an ASN1 coded length
- * @param ofs points to the offset within the pbuf chain of the ASN1 coded length
- * @param octets_used returns number of octets used by the length code
- * @param length return host order length, upto 64k
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
- */
-err_t
-snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length)
-{
- u16_t plen, base;
- u8_t *msg_ptr;
-
- plen = 0;
- while (p != NULL)
- {
- base = plen;
- plen += p->len;
- if (ofs < plen)
- {
- msg_ptr = (u8_t*)p->payload;
- msg_ptr += ofs - base;
-
- if (*msg_ptr < 0x80)
- {
- /* primitive definite length format */
- *octets_used = 1;
- *length = *msg_ptr;
- return ERR_OK;
- }
- else if (*msg_ptr == 0x80)
- {
- /* constructed indefinite length format, termination with two zero octets */
- u8_t zeros;
- u8_t i;
-
- *length = 0;
- zeros = 0;
- while (zeros != 2)
- {
- i = 2;
- while (i > 0)
- {
- i--;
- (*length) += 1;
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- plen += p->len;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- if (*msg_ptr == 0)
- {
- zeros++;
- if (zeros == 2)
- {
- /* stop while (i > 0) */
- i = 0;
- }
- }
- else
- {
- zeros = 0;
- }
- }
- }
- *octets_used = 1;
- return ERR_OK;
- }
- else if (*msg_ptr == 0x81)
- {
- /* constructed definite length format, one octet */
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- *length = *msg_ptr;
- *octets_used = 2;
- return ERR_OK;
- }
- else if (*msg_ptr == 0x82)
- {
- u8_t i;
-
- /* constructed definite length format, two octets */
- i = 2;
- while (i > 0)
- {
- i--;
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- plen += p->len;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- if (i == 0)
- {
- /* least significant length octet */
- *length |= *msg_ptr;
- }
- else
- {
- /* most significant length octet */
- *length = (*msg_ptr) << 8;
- }
- }
- *octets_used = 3;
- return ERR_OK;
- }
- else
- {
- /* constructed definite length format 3..127 octets, this is too big (>64k) */
- /** @todo: do we need to accept inefficient codings with many leading zero's? */
- *octets_used = 1 + ((*msg_ptr) & 0x7f);
- return ERR_ARG;
- }
- }
- p = p->next;
- }
-
- /* p == NULL, ofs >= plen */
- return ERR_ARG;
-}
-
-/**
- * Decodes positive integer (counter, gauge, timeticks) into u32_t.
- *
- * @param p points to a pbuf holding an ASN1 coded integer
- * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
- * @param len length of the coded integer field
- * @param value return host order integer
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
- *
- * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
- * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
- * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
- */
-err_t
-snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value)
-{
- u16_t plen, base;
- u8_t *msg_ptr;
-
- plen = 0;
- while (p != NULL)
- {
- base = plen;
- plen += p->len;
- if (ofs < plen)
- {
- msg_ptr = (u8_t*)p->payload;
- msg_ptr += ofs - base;
- if ((len > 0) && (len < 6))
- {
- /* start from zero */
- *value = 0;
- if (*msg_ptr & 0x80)
- {
- /* negative, expecting zero sign bit! */
- return ERR_ARG;
- }
- else
- {
- /* positive */
- if ((len > 1) && (*msg_ptr == 0))
- {
- /* skip leading "sign byte" octet 0x00 */
- len--;
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- plen += p->len;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- }
- }
- /* OR octets with value */
- while (len > 1)
- {
- len--;
- *value |= *msg_ptr;
- *value <<= 8;
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- plen += p->len;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- }
- *value |= *msg_ptr;
- return ERR_OK;
- }
- else
- {
- return ERR_ARG;
- }
- }
- p = p->next;
- }
- /* p == NULL, ofs >= plen */
- return ERR_ARG;
-}
-
-/**
- * Decodes integer into s32_t.
- *
- * @param p points to a pbuf holding an ASN1 coded integer
- * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
- * @param len length of the coded integer field
- * @param value return host order integer
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
- *
- * @note ASN coded integers are _always_ signed!
- */
-err_t
-snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value)
-{
- u16_t plen, base;
- u8_t *msg_ptr;
-#if BYTE_ORDER == LITTLE_ENDIAN
- u8_t *lsb_ptr = (u8_t*)value;
-#endif
-#if BYTE_ORDER == BIG_ENDIAN
- u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
-#endif
- u8_t sign;
-
- plen = 0;
- while (p != NULL)
- {
- base = plen;
- plen += p->len;
- if (ofs < plen)
- {
- msg_ptr = (u8_t*)p->payload;
- msg_ptr += ofs - base;
- if ((len > 0) && (len < 5))
- {
- if (*msg_ptr & 0x80)
- {
- /* negative, start from -1 */
- *value = -1;
- sign = 1;
- }
- else
- {
- /* positive, start from 0 */
- *value = 0;
- sign = 0;
- }
- /* OR/AND octets with value */
- while (len > 1)
- {
- len--;
- if (sign)
- {
- *lsb_ptr &= *msg_ptr;
- *value <<= 8;
- *lsb_ptr |= 255;
- }
- else
- {
- *lsb_ptr |= *msg_ptr;
- *value <<= 8;
- }
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- plen += p->len;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- }
- if (sign)
- {
- *lsb_ptr &= *msg_ptr;
- }
- else
- {
- *lsb_ptr |= *msg_ptr;
- }
- return ERR_OK;
- }
- else
- {
- return ERR_ARG;
- }
- }
- p = p->next;
- }
- /* p == NULL, ofs >= plen */
- return ERR_ARG;
-}
-
-/**
- * Decodes object identifier from incoming message into array of s32_t.
- *
- * @param p points to a pbuf holding an ASN1 coded object identifier
- * @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier
- * @param len length of the coded object identifier
- * @param oid return object identifier struct
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
- */
-err_t
-snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid)
-{
- u16_t plen, base;
- u8_t *msg_ptr;
- s32_t *oid_ptr;
-
- plen = 0;
- while (p != NULL)
- {
- base = plen;
- plen += p->len;
- if (ofs < plen)
- {
- msg_ptr = (u8_t*)p->payload;
- msg_ptr += ofs - base;
-
- oid->len = 0;
- oid_ptr = &oid->id[0];
- if (len > 0)
- {
- /* first compressed octet */
- if (*msg_ptr == 0x2B)
- {
- /* (most) common case 1.3 (iso.org) */
- *oid_ptr = 1;
- oid_ptr++;
- *oid_ptr = 3;
- oid_ptr++;
- }
- else if (*msg_ptr < 40)
- {
- *oid_ptr = 0;
- oid_ptr++;
- *oid_ptr = *msg_ptr;
- oid_ptr++;
- }
- else if (*msg_ptr < 80)
- {
- *oid_ptr = 1;
- oid_ptr++;
- *oid_ptr = (*msg_ptr) - 40;
- oid_ptr++;
- }
- else
- {
- *oid_ptr = 2;
- oid_ptr++;
- *oid_ptr = (*msg_ptr) - 80;
- oid_ptr++;
- }
- oid->len = 2;
- }
- else
- {
- /* accepting zero length identifiers e.g. for
- getnext operation. uncommon but valid */
- return ERR_OK;
- }
- len--;
- if (len > 0)
- {
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- plen += p->len;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- }
- while ((len > 0) && (oid->len < LWIP_SNMP_OBJ_ID_LEN))
- {
- /* sub-identifier uses multiple octets */
- if (*msg_ptr & 0x80)
- {
- s32_t sub_id = 0;
-
- while ((*msg_ptr & 0x80) && (len > 1))
- {
- len--;
- sub_id = (sub_id << 7) + (*msg_ptr & ~0x80);
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- plen += p->len;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- }
- if (!(*msg_ptr & 0x80) && (len > 0))
- {
- /* last octet sub-identifier */
- len--;
- sub_id = (sub_id << 7) + *msg_ptr;
- *oid_ptr = sub_id;
- }
- }
- else
- {
- /* !(*msg_ptr & 0x80) sub-identifier uses single octet */
- len--;
- *oid_ptr = *msg_ptr;
- }
- if (len > 0)
- {
- /* remaining oid bytes available ... */
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- plen += p->len;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- }
- oid_ptr++;
- oid->len++;
- }
- if (len == 0)
- {
- /* len == 0, end of oid */
- return ERR_OK;
- }
- else
- {
- /* len > 0, oid->len == LWIP_SNMP_OBJ_ID_LEN or malformed encoding */
- return ERR_ARG;
- }
-
- }
- p = p->next;
- }
- /* p == NULL, ofs >= plen */
- return ERR_ARG;
-}
-
-/**
- * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
- * from incoming message into array.
- *
- * @param p points to a pbuf holding an ASN1 coded raw data
- * @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data
- * @param len length of the coded raw data (zero is valid, e.g. empty string!)
- * @param raw_len length of the raw return value
- * @param raw return raw bytes
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
- */
-err_t
-snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw)
-{
- u16_t plen, base;
- u8_t *msg_ptr;
-
- if (len > 0)
- {
- plen = 0;
- while (p != NULL)
- {
- base = plen;
- plen += p->len;
- if (ofs < plen)
- {
- msg_ptr = (u8_t*)p->payload;
- msg_ptr += ofs - base;
- if (raw_len >= len)
- {
- while (len > 1)
- {
- /* copy len - 1 octets */
- len--;
- *raw = *msg_ptr;
- raw++;
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- plen += p->len;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- }
- /* copy last octet */
- *raw = *msg_ptr;
- return ERR_OK;
- }
- else
- {
- /* raw_len < len, not enough dst space */
- return ERR_ARG;
- }
- }
- p = p->next;
- }
- /* p == NULL, ofs >= plen */
- return ERR_ARG;
- }
- else
- {
- /* len == 0, empty string */
- return ERR_OK;
- }
-}
-
-#endif /* LWIP_SNMP */
diff --git a/lwip/src/core/snmp/asn1_enc.c b/lwip/src/core/snmp/asn1_enc.c
deleted file mode 100644
index 64dfc5f..0000000
--- a/lwip/src/core/snmp/asn1_enc.c
+++ /dev/null
@@ -1,611 +0,0 @@
-/**
- * @file
- * Abstract Syntax Notation One (ISO 8824, 8825) encoding
- *
- * @todo not optimised (yet), favor correctness over speed, favor speed over size
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/snmp_asn1.h"
-
-/**
- * Returns octet count for length.
- *
- * @param length
- * @param octets_needed points to the return value
- */
-void
-snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
-{
- if (length < 0x80U)
- {
- *octets_needed = 1;
- }
- else if (length < 0x100U)
- {
- *octets_needed = 2;
- }
- else
- {
- *octets_needed = 3;
- }
-}
-
-/**
- * Returns octet count for an u32_t.
- *
- * @param value
- * @param octets_needed points to the return value
- *
- * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
- * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
- * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
- */
-void
-snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
-{
- if (value < 0x80UL)
- {
- *octets_needed = 1;
- }
- else if (value < 0x8000UL)
- {
- *octets_needed = 2;
- }
- else if (value < 0x800000UL)
- {
- *octets_needed = 3;
- }
- else if (value < 0x80000000UL)
- {
- *octets_needed = 4;
- }
- else
- {
- *octets_needed = 5;
- }
-}
-
-/**
- * Returns octet count for an s32_t.
- *
- * @param value
- * @param octets_needed points to the return value
- *
- * @note ASN coded integers are _always_ signed.
- */
-void
-snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
-{
- if (value < 0)
- {
- value = ~value;
- }
- if (value < 0x80L)
- {
- *octets_needed = 1;
- }
- else if (value < 0x8000L)
- {
- *octets_needed = 2;
- }
- else if (value < 0x800000L)
- {
- *octets_needed = 3;
- }
- else
- {
- *octets_needed = 4;
- }
-}
-
-/**
- * Returns octet count for an object identifier.
- *
- * @param ident_len object identifier array length
- * @param ident points to object identifier array
- * @param octets_needed points to the return value
- */
-void
-snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed)
-{
- s32_t sub_id;
- u8_t cnt;
-
- cnt = 0;
- if (ident_len > 1)
- {
- /* compressed prefix in one octet */
- cnt++;
- ident_len -= 2;
- ident += 2;
- }
- while(ident_len > 0)
- {
- ident_len--;
- sub_id = *ident;
-
- sub_id >>= 7;
- cnt++;
- while(sub_id > 0)
- {
- sub_id >>= 7;
- cnt++;
- }
- ident++;
- }
- *octets_needed = cnt;
-}
-
-/**
- * Encodes ASN type field into a pbuf chained ASN1 msg.
- *
- * @param p points to output pbuf to encode value into
- * @param ofs points to the offset within the pbuf chain
- * @param type input ASN1 type
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
- */
-err_t
-snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type)
-{
- u16_t plen, base;
- u8_t *msg_ptr;
-
- plen = 0;
- while (p != NULL)
- {
- base = plen;
- plen += p->len;
- if (ofs < plen)
- {
- msg_ptr = (u8_t*)p->payload;
- msg_ptr += ofs - base;
- *msg_ptr = type;
- return ERR_OK;
- }
- p = p->next;
- }
- /* p == NULL, ofs >= plen */
- return ERR_ARG;
-}
-
-/**
- * Encodes host order length field into a pbuf chained ASN1 msg.
- *
- * @param p points to output pbuf to encode length into
- * @param ofs points to the offset within the pbuf chain
- * @param length is the host order length to be encoded
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
- */
-err_t
-snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length)
-{
- u16_t plen, base;
- u8_t *msg_ptr;
-
- plen = 0;
- while (p != NULL)
- {
- base = plen;
- plen += p->len;
- if (ofs < plen)
- {
- msg_ptr = (u8_t*)p->payload;
- msg_ptr += ofs - base;
-
- if (length < 0x80)
- {
- *msg_ptr = (u8_t)length;
- return ERR_OK;
- }
- else if (length < 0x100)
- {
- *msg_ptr = 0x81;
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- *msg_ptr = (u8_t)length;
- return ERR_OK;
- }
- else
- {
- u8_t i;
-
- /* length >= 0x100 && length <= 0xFFFF */
- *msg_ptr = 0x82;
- i = 2;
- while (i > 0)
- {
- i--;
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- plen += p->len;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- if (i == 0)
- {
- /* least significant length octet */
- *msg_ptr = (u8_t)length;
- }
- else
- {
- /* most significant length octet */
- *msg_ptr = (u8_t)(length >> 8);
- }
- }
- return ERR_OK;
- }
- }
- p = p->next;
- }
- /* p == NULL, ofs >= plen */
- return ERR_ARG;
-}
-
-/**
- * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
- *
- * @param p points to output pbuf to encode value into
- * @param ofs points to the offset within the pbuf chain
- * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
- * @param value is the host order u32_t value to be encoded
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
- *
- * @see snmp_asn1_enc_u32t_cnt()
- */
-err_t
-snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value)
-{
- u16_t plen, base;
- u8_t *msg_ptr;
-
- plen = 0;
- while (p != NULL)
- {
- base = plen;
- plen += p->len;
- if (ofs < plen)
- {
- msg_ptr = (u8_t*)p->payload;
- msg_ptr += ofs - base;
-
- if (octets_needed == 5)
- {
- /* not enough bits in 'value' add leading 0x00 */
- octets_needed--;
- *msg_ptr = 0x00;
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- plen += p->len;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- }
- while (octets_needed > 1)
- {
- octets_needed--;
- *msg_ptr = (u8_t)(value >> (octets_needed << 3));
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- plen += p->len;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- }
- /* (only) one least significant octet */
- *msg_ptr = (u8_t)value;
- return ERR_OK;
- }
- p = p->next;
- }
- /* p == NULL, ofs >= plen */
- return ERR_ARG;
-}
-
-/**
- * Encodes s32_t integer into a pbuf chained ASN1 msg.
- *
- * @param p points to output pbuf to encode value into
- * @param ofs points to the offset within the pbuf chain
- * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
- * @param value is the host order s32_t value to be encoded
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
- *
- * @see snmp_asn1_enc_s32t_cnt()
- */
-err_t
-snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value)
-{
- u16_t plen, base;
- u8_t *msg_ptr;
-
- plen = 0;
- while (p != NULL)
- {
- base = plen;
- plen += p->len;
- if (ofs < plen)
- {
- msg_ptr = (u8_t*)p->payload;
- msg_ptr += ofs - base;
-
- while (octets_needed > 1)
- {
- octets_needed--;
- *msg_ptr = (u8_t)(value >> (octets_needed << 3));
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- plen += p->len;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- }
- /* (only) one least significant octet */
- *msg_ptr = (u8_t)value;
- return ERR_OK;
- }
- p = p->next;
- }
- /* p == NULL, ofs >= plen */
- return ERR_ARG;
-}
-
-/**
- * Encodes object identifier into a pbuf chained ASN1 msg.
- *
- * @param p points to output pbuf to encode oid into
- * @param ofs points to the offset within the pbuf chain
- * @param ident_len object identifier array length
- * @param ident points to object identifier array
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
- */
-err_t
-snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident)
-{
- u16_t plen, base;
- u8_t *msg_ptr;
-
- plen = 0;
- while (p != NULL)
- {
- base = plen;
- plen += p->len;
- if (ofs < plen)
- {
- msg_ptr = (u8_t*)p->payload;
- msg_ptr += ofs - base;
-
- if (ident_len > 1)
- {
- if ((ident[0] == 1) && (ident[1] == 3))
- {
- /* compressed (most common) prefix .iso.org */
- *msg_ptr = 0x2b;
- }
- else
- {
- /* calculate prefix */
- *msg_ptr = (u8_t)((ident[0] * 40) + ident[1]);
- }
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- plen += p->len;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- ident_len -= 2;
- ident += 2;
- }
- else
- {
-/* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
- /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
- return ERR_ARG;
- }
- while (ident_len > 0)
- {
- s32_t sub_id;
- u8_t shift, tail;
-
- ident_len--;
- sub_id = *ident;
- tail = 0;
- shift = 28;
- while(shift > 0)
- {
- u8_t code;
-
- code = (u8_t)(sub_id >> shift);
- if ((code != 0) || (tail != 0))
- {
- tail = 1;
- *msg_ptr = code | 0x80;
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- plen += p->len;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- }
- shift -= 7;
- }
- *msg_ptr = (u8_t)sub_id & 0x7F;
- if (ident_len > 0)
- {
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- plen += p->len;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- }
- /* proceed to next sub-identifier */
- ident++;
- }
- return ERR_OK;
- }
- p = p->next;
- }
- /* p == NULL, ofs >= plen */
- return ERR_ARG;
-}
-
-/**
- * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
- *
- * @param p points to output pbuf to encode raw data into
- * @param ofs points to the offset within the pbuf chain
- * @param raw_len raw data length
- * @param raw points raw data
- * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
- */
-err_t
-snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw)
-{
- u16_t plen, base;
- u8_t *msg_ptr;
-
- plen = 0;
- while (p != NULL)
- {
- base = plen;
- plen += p->len;
- if (ofs < plen)
- {
- msg_ptr = (u8_t*)p->payload;
- msg_ptr += ofs - base;
-
- while (raw_len > 1)
- {
- /* copy raw_len - 1 octets */
- raw_len--;
- *msg_ptr = *raw;
- raw++;
- ofs += 1;
- if (ofs >= plen)
- {
- /* next octet in next pbuf */
- p = p->next;
- if (p == NULL) { return ERR_ARG; }
- msg_ptr = (u8_t*)p->payload;
- plen += p->len;
- }
- else
- {
- /* next octet in same pbuf */
- msg_ptr++;
- }
- }
- if (raw_len > 0)
- {
- /* copy last or single octet */
- *msg_ptr = *raw;
- }
- return ERR_OK;
- }
- p = p->next;
- }
- /* p == NULL, ofs >= plen */
- return ERR_ARG;
-}
-
-#endif /* LWIP_SNMP */
diff --git a/lwip/src/core/snmp/mib2.c b/lwip/src/core/snmp/mib2.c
deleted file mode 100644
index dcd3b62..0000000
--- a/lwip/src/core/snmp/mib2.c
+++ /dev/null
@@ -1,4146 +0,0 @@
-/**
- * @file
- * Management Information Base II (RFC1213) objects and functions.
- *
- * @note the object identifiers for this MIB-2 and private MIB tree
- * must be kept in sorted ascending order. This to ensure correct getnext operation.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/snmp.h"
-#include "lwip/netif.h"
-#include "lwip/ip.h"
-#include "lwip/ip_frag.h"
-#include "lwip/mem.h"
-#include "lwip/tcp_impl.h"
-#include "lwip/udp.h"
-#include "lwip/snmp_asn1.h"
-#include "lwip/snmp_structs.h"
-#include "lwip/sys.h"
-#include "netif/etharp.h"
-
-/**
- * IANA assigned enterprise ID for lwIP is 26381
- * @see http://www.iana.org/assignments/enterprise-numbers
- *
- * @note this enterprise ID is assigned to the lwIP project,
- * all object identifiers living under this ID are assigned
- * by the lwIP maintainers (contact Christiaan Simons)!
- * @note don't change this define, use snmp_set_sysobjid()
- *
- * If you need to create your own private MIB you'll need
- * to apply for your own enterprise ID with IANA:
- * http://www.iana.org/numbers.html
- */
-#define SNMP_ENTERPRISE_ID 26381
-#define SNMP_SYSOBJID_LEN 7
-#define SNMP_SYSOBJID {1, 3, 6, 1, 4, 1, SNMP_ENTERPRISE_ID}
-
-#ifndef SNMP_SYSSERVICES
-#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2))
-#endif
-
-#ifndef SNMP_GET_SYSUPTIME
-#define SNMP_GET_SYSUPTIME(sysuptime) (sysuptime = (sys_now() / 10))
-#endif
-
-static void system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void system_get_value(struct obj_def *od, u16_t len, void *value);
-static u8_t system_set_test(struct obj_def *od, u16_t len, void *value);
-static void system_set_value(struct obj_def *od, u16_t len, void *value);
-static void interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void interfaces_get_value(struct obj_def *od, u16_t len, void *value);
-static void ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void ifentry_get_value(struct obj_def *od, u16_t len, void *value);
-#if !SNMP_SAFE_REQUESTS
-static u8_t ifentry_set_test (struct obj_def *od, u16_t len, void *value);
-static void ifentry_set_value (struct obj_def *od, u16_t len, void *value);
-#endif /* SNMP_SAFE_REQUESTS */
-static void atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void atentry_get_value(struct obj_def *od, u16_t len, void *value);
-static void ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void ip_get_value(struct obj_def *od, u16_t len, void *value);
-static u8_t ip_set_test(struct obj_def *od, u16_t len, void *value);
-static void ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value);
-static void ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value);
-static void ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value);
-static void icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void icmp_get_value(struct obj_def *od, u16_t len, void *value);
-#if LWIP_TCP
-static void tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void tcp_get_value(struct obj_def *od, u16_t len, void *value);
-#ifdef THIS_SEEMS_UNUSED
-static void tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value);
-#endif
-#endif
-static void udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void udp_get_value(struct obj_def *od, u16_t len, void *value);
-static void udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void udpentry_get_value(struct obj_def *od, u16_t len, void *value);
-static void snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-static void snmp_get_value(struct obj_def *od, u16_t len, void *value);
-static u8_t snmp_set_test(struct obj_def *od, u16_t len, void *value);
-static void snmp_set_value(struct obj_def *od, u16_t len, void *value);
-
-
-/* snmp .1.3.6.1.2.1.11 */
-const mib_scalar_node snmp_scalar = {
- &snmp_get_object_def,
- &snmp_get_value,
- &snmp_set_test,
- &snmp_set_value,
- MIB_NODE_SC,
- 0
-};
-const s32_t snmp_ids[28] = {
- 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16,
- 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30
-};
-struct mib_node* const snmp_nodes[28] = {
- (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
- (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
- (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
- (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
- (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
- (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
- (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
- (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
- (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
- (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
- (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
- (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
- (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
- (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar
-};
-const struct mib_array_node snmp = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 28,
- snmp_ids,
- snmp_nodes
-};
-
-/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */
-/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */
-/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */
-
-/* udp .1.3.6.1.2.1.7 */
-/** index root node for udpTable */
-struct mib_list_rootnode udp_root = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_LR,
- 0,
- NULL,
- NULL,
- 0
-};
-const s32_t udpentry_ids[2] = { 1, 2 };
-struct mib_node* const udpentry_nodes[2] = {
- (struct mib_node*)&udp_root, (struct mib_node*)&udp_root,
-};
-const struct mib_array_node udpentry = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 2,
- udpentry_ids,
- udpentry_nodes
-};
-
-s32_t udptable_id = 1;
-struct mib_node* udptable_node = (struct mib_node*)&udpentry;
-struct mib_ram_array_node udptable = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_RA,
- 0,
- &udptable_id,
- &udptable_node
-};
-
-const mib_scalar_node udp_scalar = {
- &udp_get_object_def,
- &udp_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_SC,
- 0
-};
-const s32_t udp_ids[5] = { 1, 2, 3, 4, 5 };
-struct mib_node* const udp_nodes[5] = {
- (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar,
- (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar,
- (struct mib_node*)&udptable
-};
-const struct mib_array_node udp = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 5,
- udp_ids,
- udp_nodes
-};
-
-/* tcp .1.3.6.1.2.1.6 */
-#if LWIP_TCP
-/* only if the TCP protocol is available may implement this group */
-/** index root node for tcpConnTable */
-struct mib_list_rootnode tcpconntree_root = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_LR,
- 0,
- NULL,
- NULL,
- 0
-};
-const s32_t tcpconnentry_ids[5] = { 1, 2, 3, 4, 5 };
-struct mib_node* const tcpconnentry_nodes[5] = {
- (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root,
- (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root,
- (struct mib_node*)&tcpconntree_root
-};
-const struct mib_array_node tcpconnentry = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 5,
- tcpconnentry_ids,
- tcpconnentry_nodes
-};
-
-s32_t tcpconntable_id = 1;
-struct mib_node* tcpconntable_node = (struct mib_node*)&tcpconnentry;
-struct mib_ram_array_node tcpconntable = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_RA,
-/** @todo update maxlength when inserting / deleting from table
- 0 when table is empty, 1 when more than one entry */
- 0,
- &tcpconntable_id,
- &tcpconntable_node
-};
-
-const mib_scalar_node tcp_scalar = {
- &tcp_get_object_def,
- &tcp_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_SC,
- 0
-};
-const s32_t tcp_ids[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
-struct mib_node* const tcp_nodes[15] = {
- (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
- (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
- (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
- (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
- (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
- (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
- (struct mib_node*)&tcpconntable, (struct mib_node*)&tcp_scalar,
- (struct mib_node*)&tcp_scalar
-};
-const struct mib_array_node tcp = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 15,
- tcp_ids,
- tcp_nodes
-};
-#endif
-
-/* icmp .1.3.6.1.2.1.5 */
-const mib_scalar_node icmp_scalar = {
- &icmp_get_object_def,
- &icmp_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_SC,
- 0
-};
-const s32_t icmp_ids[26] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 };
-struct mib_node* const icmp_nodes[26] = {
- (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
- (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
- (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
- (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
- (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
- (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
- (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
- (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
- (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
- (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
- (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
- (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
- (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar
-};
-const struct mib_array_node icmp = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 26,
- icmp_ids,
- icmp_nodes
-};
-
-/** index root node for ipNetToMediaTable */
-struct mib_list_rootnode ipntomtree_root = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_LR,
- 0,
- NULL,
- NULL,
- 0
-};
-const s32_t ipntomentry_ids[4] = { 1, 2, 3, 4 };
-struct mib_node* const ipntomentry_nodes[4] = {
- (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root,
- (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root
-};
-const struct mib_array_node ipntomentry = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 4,
- ipntomentry_ids,
- ipntomentry_nodes
-};
-
-s32_t ipntomtable_id = 1;
-struct mib_node* ipntomtable_node = (struct mib_node*)&ipntomentry;
-struct mib_ram_array_node ipntomtable = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_RA,
- 0,
- &ipntomtable_id,
- &ipntomtable_node
-};
-
-/** index root node for ipRouteTable */
-struct mib_list_rootnode iprtetree_root = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_LR,
- 0,
- NULL,
- NULL,
- 0
-};
-const s32_t iprteentry_ids[13] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
-struct mib_node* const iprteentry_nodes[13] = {
- (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
- (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
- (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
- (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
- (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
- (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
- (struct mib_node*)&iprtetree_root
-};
-const struct mib_array_node iprteentry = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 13,
- iprteentry_ids,
- iprteentry_nodes
-};
-
-s32_t iprtetable_id = 1;
-struct mib_node* iprtetable_node = (struct mib_node*)&iprteentry;
-struct mib_ram_array_node iprtetable = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_RA,
- 0,
- &iprtetable_id,
- &iprtetable_node
-};
-
-/** index root node for ipAddrTable */
-struct mib_list_rootnode ipaddrtree_root = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_LR,
- 0,
- NULL,
- NULL,
- 0
-};
-const s32_t ipaddrentry_ids[5] = { 1, 2, 3, 4, 5 };
-struct mib_node* const ipaddrentry_nodes[5] = {
- (struct mib_node*)&ipaddrtree_root,
- (struct mib_node*)&ipaddrtree_root,
- (struct mib_node*)&ipaddrtree_root,
- (struct mib_node*)&ipaddrtree_root,
- (struct mib_node*)&ipaddrtree_root
-};
-const struct mib_array_node ipaddrentry = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 5,
- ipaddrentry_ids,
- ipaddrentry_nodes
-};
-
-s32_t ipaddrtable_id = 1;
-struct mib_node* ipaddrtable_node = (struct mib_node*)&ipaddrentry;
-struct mib_ram_array_node ipaddrtable = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_RA,
- 0,
- &ipaddrtable_id,
- &ipaddrtable_node
-};
-
-/* ip .1.3.6.1.2.1.4 */
-const mib_scalar_node ip_scalar = {
- &ip_get_object_def,
- &ip_get_value,
- &ip_set_test,
- &noleafs_set_value,
- MIB_NODE_SC,
- 0
-};
-const s32_t ip_ids[23] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 };
-struct mib_node* const ip_nodes[23] = {
- (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
- (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
- (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
- (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
- (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
- (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
- (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
- (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
- (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
- (struct mib_node*)&ip_scalar, (struct mib_node*)&ipaddrtable,
- (struct mib_node*)&iprtetable, (struct mib_node*)&ipntomtable,
- (struct mib_node*)&ip_scalar
-};
-const struct mib_array_node mib2_ip = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 23,
- ip_ids,
- ip_nodes
-};
-
-/** index root node for atTable */
-struct mib_list_rootnode arptree_root = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_LR,
- 0,
- NULL,
- NULL,
- 0
-};
-const s32_t atentry_ids[3] = { 1, 2, 3 };
-struct mib_node* const atentry_nodes[3] = {
- (struct mib_node*)&arptree_root,
- (struct mib_node*)&arptree_root,
- (struct mib_node*)&arptree_root
-};
-const struct mib_array_node atentry = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 3,
- atentry_ids,
- atentry_nodes
-};
-
-const s32_t attable_id = 1;
-struct mib_node* const attable_node = (struct mib_node*)&atentry;
-const struct mib_array_node attable = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 1,
- &attable_id,
- &attable_node
-};
-
-/* at .1.3.6.1.2.1.3 */
-s32_t at_id = 1;
-struct mib_node* mib2_at_node = (struct mib_node*)&attable;
-struct mib_ram_array_node at = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_RA,
- 0,
- &at_id,
- &mib2_at_node
-};
-
-/** index root node for ifTable */
-struct mib_list_rootnode iflist_root = {
- &ifentry_get_object_def,
- &ifentry_get_value,
-#if SNMP_SAFE_REQUESTS
- &noleafs_set_test,
- &noleafs_set_value,
-#else /* SNMP_SAFE_REQUESTS */
- &ifentry_set_test,
- &ifentry_set_value,
-#endif /* SNMP_SAFE_REQUESTS */
- MIB_NODE_LR,
- 0,
- NULL,
- NULL,
- 0
-};
-const s32_t ifentry_ids[22] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 };
-struct mib_node* const ifentry_nodes[22] = {
- (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
- (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
- (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
- (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
- (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
- (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
- (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
- (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
- (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
- (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
- (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root
-};
-const struct mib_array_node ifentry = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 22,
- ifentry_ids,
- ifentry_nodes
-};
-
-s32_t iftable_id = 1;
-struct mib_node* iftable_node = (struct mib_node*)&ifentry;
-struct mib_ram_array_node iftable = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_RA,
- 0,
- &iftable_id,
- &iftable_node
-};
-
-/* interfaces .1.3.6.1.2.1.2 */
-const mib_scalar_node interfaces_scalar = {
- &interfaces_get_object_def,
- &interfaces_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_SC,
- 0
-};
-const s32_t interfaces_ids[2] = { 1, 2 };
-struct mib_node* const interfaces_nodes[2] = {
- (struct mib_node*)&interfaces_scalar, (struct mib_node*)&iftable
-};
-const struct mib_array_node interfaces = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 2,
- interfaces_ids,
- interfaces_nodes
-};
-
-
-/* 0 1 2 3 4 5 6 */
-/* system .1.3.6.1.2.1.1 */
-const mib_scalar_node sys_tem_scalar = {
- &system_get_object_def,
- &system_get_value,
- &system_set_test,
- &system_set_value,
- MIB_NODE_SC,
- 0
-};
-const s32_t sys_tem_ids[7] = { 1, 2, 3, 4, 5, 6, 7 };
-struct mib_node* const sys_tem_nodes[7] = {
- (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar,
- (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar,
- (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar,
- (struct mib_node*)&sys_tem_scalar
-};
-/* work around name issue with 'sys_tem', some compiler(s?) seem to reserve 'system' */
-const struct mib_array_node sys_tem = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 7,
- sys_tem_ids,
- sys_tem_nodes
-};
-
-/* mib-2 .1.3.6.1.2.1 */
-#if LWIP_TCP
-#define MIB2_GROUPS 8
-#else
-#define MIB2_GROUPS 7
-#endif
-const s32_t mib2_ids[MIB2_GROUPS] =
-{
- 1,
- 2,
- 3,
- 4,
- 5,
-#if LWIP_TCP
- 6,
-#endif
- 7,
- 11
-};
-struct mib_node* const mib2_nodes[MIB2_GROUPS] = {
- (struct mib_node*)&sys_tem,
- (struct mib_node*)&interfaces,
- (struct mib_node*)&at,
- (struct mib_node*)&mib2_ip,
- (struct mib_node*)&icmp,
-#if LWIP_TCP
- (struct mib_node*)&tcp,
-#endif
- (struct mib_node*)&udp,
- (struct mib_node*)&snmp
-};
-
-const struct mib_array_node mib2 = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- MIB2_GROUPS,
- mib2_ids,
- mib2_nodes
-};
-
-/* mgmt .1.3.6.1.2 */
-const s32_t mgmt_ids[1] = { 1 };
-struct mib_node* const mgmt_nodes[1] = { (struct mib_node*)&mib2 };
-const struct mib_array_node mgmt = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 1,
- mgmt_ids,
- mgmt_nodes
-};
-
-/* internet .1.3.6.1 */
-#if SNMP_PRIVATE_MIB
-/* When using a private MIB, you have to create a file 'private_mib.h' that contains
- * a 'struct mib_array_node mib_private' which contains your MIB. */
-s32_t internet_ids[2] = { 2, 4 };
-struct mib_node* const internet_nodes[2] = { (struct mib_node*)&mgmt, (struct mib_node*)&mib_private };
-const struct mib_array_node internet = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 2,
- internet_ids,
- internet_nodes
-};
-#else
-const s32_t internet_ids[1] = { 2 };
-struct mib_node* const internet_nodes[1] = { (struct mib_node*)&mgmt };
-const struct mib_array_node internet = {
- &noleafs_get_object_def,
- &noleafs_get_value,
- &noleafs_set_test,
- &noleafs_set_value,
- MIB_NODE_AR,
- 1,
- internet_ids,
- internet_nodes
-};
-#endif
-
-/** mib-2.system.sysObjectID */
-static struct snmp_obj_id sysobjid = {SNMP_SYSOBJID_LEN, SNMP_SYSOBJID};
-/** enterprise ID for generic TRAPs, .iso.org.dod.internet.mgmt.mib-2.snmp */
-static struct snmp_obj_id snmpgrp_id = {7,{1,3,6,1,2,1,11}};
-/** mib-2.system.sysServices */
-static const s32_t sysservices = SNMP_SYSSERVICES;
-
-/** mib-2.system.sysDescr */
-static const u8_t sysdescr_len_default = 4;
-static const u8_t sysdescr_default[] = "lwIP";
-static u8_t* sysdescr_len_ptr = (u8_t*)&sysdescr_len_default;
-static u8_t* sysdescr_ptr = (u8_t*)&sysdescr_default[0];
-/** mib-2.system.sysContact */
-static const u8_t syscontact_len_default = 0;
-static const u8_t syscontact_default[] = "";
-static u8_t* syscontact_len_ptr = (u8_t*)&syscontact_len_default;
-static u8_t* syscontact_ptr = (u8_t*)&syscontact_default[0];
-/** mib-2.system.sysName */
-static const u8_t sysname_len_default = 8;
-static const u8_t sysname_default[] = "FQDN-unk";
-static u8_t* sysname_len_ptr = (u8_t*)&sysname_len_default;
-static u8_t* sysname_ptr = (u8_t*)&sysname_default[0];
-/** mib-2.system.sysLocation */
-static const u8_t syslocation_len_default = 0;
-static const u8_t syslocation_default[] = "";
-static u8_t* syslocation_len_ptr = (u8_t*)&syslocation_len_default;
-static u8_t* syslocation_ptr = (u8_t*)&syslocation_default[0];
-/** mib-2.snmp.snmpEnableAuthenTraps */
-static const u8_t snmpenableauthentraps_default = 2; /* disabled */
-static u8_t* snmpenableauthentraps_ptr = (u8_t*)&snmpenableauthentraps_default;
-
-/** mib-2.interfaces.ifTable.ifEntry.ifSpecific (zeroDotZero) */
-static const struct snmp_obj_id ifspecific = {2, {0, 0}};
-/** mib-2.ip.ipRouteTable.ipRouteEntry.ipRouteInfo (zeroDotZero) */
-static const struct snmp_obj_id iprouteinfo = {2, {0, 0}};
-
-
-
-/* mib-2.system counter(s) */
-static u32_t sysuptime = 0;
-
-/* mib-2.ip counter(s) */
-static u32_t ipinreceives = 0,
- ipinhdrerrors = 0,
- ipinaddrerrors = 0,
- ipforwdatagrams = 0,
- ipinunknownprotos = 0,
- ipindiscards = 0,
- ipindelivers = 0,
- ipoutrequests = 0,
- ipoutdiscards = 0,
- ipoutnoroutes = 0,
- ipreasmreqds = 0,
- ipreasmoks = 0,
- ipreasmfails = 0,
- ipfragoks = 0,
- ipfragfails = 0,
- ipfragcreates = 0,
- iproutingdiscards = 0;
-/* mib-2.icmp counter(s) */
-static u32_t icmpinmsgs = 0,
- icmpinerrors = 0,
- icmpindestunreachs = 0,
- icmpintimeexcds = 0,
- icmpinparmprobs = 0,
- icmpinsrcquenchs = 0,
- icmpinredirects = 0,
- icmpinechos = 0,
- icmpinechoreps = 0,
- icmpintimestamps = 0,
- icmpintimestampreps = 0,
- icmpinaddrmasks = 0,
- icmpinaddrmaskreps = 0,
- icmpoutmsgs = 0,
- icmpouterrors = 0,
- icmpoutdestunreachs = 0,
- icmpouttimeexcds = 0,
- icmpoutparmprobs = 0,
- icmpoutsrcquenchs = 0,
- icmpoutredirects = 0,
- icmpoutechos = 0,
- icmpoutechoreps = 0,
- icmpouttimestamps = 0,
- icmpouttimestampreps = 0,
- icmpoutaddrmasks = 0,
- icmpoutaddrmaskreps = 0;
-/* mib-2.tcp counter(s) */
-static u32_t tcpactiveopens = 0,
- tcppassiveopens = 0,
- tcpattemptfails = 0,
- tcpestabresets = 0,
- tcpinsegs = 0,
- tcpoutsegs = 0,
- tcpretranssegs = 0,
- tcpinerrs = 0,
- tcpoutrsts = 0;
-/* mib-2.udp counter(s) */
-static u32_t udpindatagrams = 0,
- udpnoports = 0,
- udpinerrors = 0,
- udpoutdatagrams = 0;
-/* mib-2.snmp counter(s) */
-static u32_t snmpinpkts = 0,
- snmpoutpkts = 0,
- snmpinbadversions = 0,
- snmpinbadcommunitynames = 0,
- snmpinbadcommunityuses = 0,
- snmpinasnparseerrs = 0,
- snmpintoobigs = 0,
- snmpinnosuchnames = 0,
- snmpinbadvalues = 0,
- snmpinreadonlys = 0,
- snmpingenerrs = 0,
- snmpintotalreqvars = 0,
- snmpintotalsetvars = 0,
- snmpingetrequests = 0,
- snmpingetnexts = 0,
- snmpinsetrequests = 0,
- snmpingetresponses = 0,
- snmpintraps = 0,
- snmpouttoobigs = 0,
- snmpoutnosuchnames = 0,
- snmpoutbadvalues = 0,
- snmpoutgenerrs = 0,
- snmpoutgetrequests = 0,
- snmpoutgetnexts = 0,
- snmpoutsetrequests = 0,
- snmpoutgetresponses = 0,
- snmpouttraps = 0;
-
-
-
-/* prototypes of the following functions are in lwip/src/include/lwip/snmp.h */
-/**
- * Copy octet string.
- *
- * @param dst points to destination
- * @param src points to source
- * @param n number of octets to copy.
- */
-static void ocstrncpy(u8_t *dst, u8_t *src, u16_t n)
-{
- u16_t i = n;
- while (i > 0) {
- i--;
- *dst++ = *src++;
- }
-}
-
-/**
- * Copy object identifier (s32_t) array.
- *
- * @param dst points to destination
- * @param src points to source
- * @param n number of sub identifiers to copy.
- */
-void objectidncpy(s32_t *dst, s32_t *src, u8_t n)
-{
- u8_t i = n;
- while(i > 0) {
- i--;
- *dst++ = *src++;
- }
-}
-
-/**
- * Initializes sysDescr pointers.
- *
- * @param str if non-NULL then copy str pointer
- * @param len points to string length, excluding zero terminator
- */
-void snmp_set_sysdesr(u8_t *str, u8_t *len)
-{
- if (str != NULL)
- {
- sysdescr_ptr = str;
- sysdescr_len_ptr = len;
- }
-}
-
-void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid)
-{
- *oid = &sysobjid;
-}
-
-/**
- * Initializes sysObjectID value.
- *
- * @param oid points to stuct snmp_obj_id to copy
- */
-void snmp_set_sysobjid(struct snmp_obj_id *oid)
-{
- sysobjid = *oid;
-}
-
-/**
- * Must be called at regular 10 msec interval from a timer interrupt
- * or signal handler depending on your runtime environment.
- */
-void snmp_inc_sysuptime(void)
-{
- sysuptime++;
-}
-
-void snmp_add_sysuptime(u32_t value)
-{
- sysuptime+=value;
-}
-
-void snmp_get_sysuptime(u32_t *value)
-{
- SNMP_GET_SYSUPTIME(sysuptime);
- *value = sysuptime;
-}
-
-/**
- * Initializes sysContact pointers,
- * e.g. ptrs to non-volatile memory external to lwIP.
- *
- * @param ocstr if non-NULL then copy str pointer
- * @param ocstrlen points to string length, excluding zero terminator
- */
-void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen)
-{
- if (ocstr != NULL)
- {
- syscontact_ptr = ocstr;
- syscontact_len_ptr = ocstrlen;
- }
-}
-
-/**
- * Initializes sysName pointers,
- * e.g. ptrs to non-volatile memory external to lwIP.
- *
- * @param ocstr if non-NULL then copy str pointer
- * @param ocstrlen points to string length, excluding zero terminator
- */
-void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen)
-{
- if (ocstr != NULL)
- {
- sysname_ptr = ocstr;
- sysname_len_ptr = ocstrlen;
- }
-}
-
-/**
- * Initializes sysLocation pointers,
- * e.g. ptrs to non-volatile memory external to lwIP.
- *
- * @param ocstr if non-NULL then copy str pointer
- * @param ocstrlen points to string length, excluding zero terminator
- */
-void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen)
-{
- if (ocstr != NULL)
- {
- syslocation_ptr = ocstr;
- syslocation_len_ptr = ocstrlen;
- }
-}
-
-
-void snmp_add_ifinoctets(struct netif *ni, u32_t value)
-{
- ni->ifinoctets += value;
-}
-
-void snmp_inc_ifinucastpkts(struct netif *ni)
-{
- (ni->ifinucastpkts)++;
-}
-
-void snmp_inc_ifinnucastpkts(struct netif *ni)
-{
- (ni->ifinnucastpkts)++;
-}
-
-void snmp_inc_ifindiscards(struct netif *ni)
-{
- (ni->ifindiscards)++;
-}
-
-void snmp_add_ifoutoctets(struct netif *ni, u32_t value)
-{
- ni->ifoutoctets += value;
-}
-
-void snmp_inc_ifoutucastpkts(struct netif *ni)
-{
- (ni->ifoutucastpkts)++;
-}
-
-void snmp_inc_ifoutnucastpkts(struct netif *ni)
-{
- (ni->ifoutnucastpkts)++;
-}
-
-void snmp_inc_ifoutdiscards(struct netif *ni)
-{
- (ni->ifoutdiscards)++;
-}
-
-void snmp_inc_iflist(void)
-{
- struct mib_list_node *if_node = NULL;
-
- snmp_mib_node_insert(&iflist_root, iflist_root.count + 1, &if_node);
- /* enable getnext traversal on filled table */
- iftable.maxlength = 1;
-}
-
-void snmp_dec_iflist(void)
-{
- snmp_mib_node_delete(&iflist_root, iflist_root.tail);
- /* disable getnext traversal on empty table */
- if(iflist_root.count == 0) iftable.maxlength = 0;
-}
-
-/**
- * Inserts ARP table indexes (.xIfIndex.xNetAddress)
- * into arp table index trees (both atTable and ipNetToMediaTable).
- */
-void snmp_insert_arpidx_tree(struct netif *ni, ip_addr_t *ip)
-{
- struct mib_list_rootnode *at_rn;
- struct mib_list_node *at_node;
- s32_t arpidx[5];
- u8_t level, tree;
-
- LWIP_ASSERT("ni != NULL", ni != NULL);
- snmp_netiftoifindex(ni, &arpidx[0]);
- snmp_iptooid(ip, &arpidx[1]);
-
- for (tree = 0; tree < 2; tree++)
- {
- if (tree == 0)
- {
- at_rn = &arptree_root;
- }
- else
- {
- at_rn = &ipntomtree_root;
- }
- for (level = 0; level < 5; level++)
- {
- at_node = NULL;
- snmp_mib_node_insert(at_rn, arpidx[level], &at_node);
- if ((level != 4) && (at_node != NULL))
- {
- if (at_node->nptr == NULL)
- {
- at_rn = snmp_mib_lrn_alloc();
- at_node->nptr = (struct mib_node*)at_rn;
- if (at_rn != NULL)
- {
- if (level == 3)
- {
- if (tree == 0)
- {
- at_rn->get_object_def = atentry_get_object_def;
- at_rn->get_value = atentry_get_value;
- }
- else
- {
- at_rn->get_object_def = ip_ntomentry_get_object_def;
- at_rn->get_value = ip_ntomentry_get_value;
- }
- at_rn->set_test = noleafs_set_test;
- at_rn->set_value = noleafs_set_value;
- }
- }
- else
- {
- /* at_rn == NULL, malloc failure */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_arpidx_tree() insert failed, mem full"));
- break;
- }
- }
- else
- {
- at_rn = (struct mib_list_rootnode*)at_node->nptr;
- }
- }
- }
- }
- /* enable getnext traversal on filled tables */
- at.maxlength = 1;
- ipntomtable.maxlength = 1;
-}
-
-/**
- * Removes ARP table indexes (.xIfIndex.xNetAddress)
- * from arp table index trees.
- */
-void snmp_delete_arpidx_tree(struct netif *ni, ip_addr_t *ip)
-{
- struct mib_list_rootnode *at_rn, *next, *del_rn[5];
- struct mib_list_node *at_n, *del_n[5];
- s32_t arpidx[5];
- u8_t fc, tree, level, del_cnt;
-
- snmp_netiftoifindex(ni, &arpidx[0]);
- snmp_iptooid(ip, &arpidx[1]);
-
- for (tree = 0; tree < 2; tree++)
- {
- /* mark nodes for deletion */
- if (tree == 0)
- {
- at_rn = &arptree_root;
- }
- else
- {
- at_rn = &ipntomtree_root;
- }
- level = 0;
- del_cnt = 0;
- while ((level < 5) && (at_rn != NULL))
- {
- fc = snmp_mib_node_find(at_rn, arpidx[level], &at_n);
- if (fc == 0)
- {
- /* arpidx[level] does not exist */
- del_cnt = 0;
- at_rn = NULL;
- }
- else if (fc == 1)
- {
- del_rn[del_cnt] = at_rn;
- del_n[del_cnt] = at_n;
- del_cnt++;
- at_rn = (struct mib_list_rootnode*)(at_n->nptr);
- }
- else if (fc == 2)
- {
- /* reset delete (2 or more childs) */
- del_cnt = 0;
- at_rn = (struct mib_list_rootnode*)(at_n->nptr);
- }
- level++;
- }
- /* delete marked index nodes */
- while (del_cnt > 0)
- {
- del_cnt--;
-
- at_rn = del_rn[del_cnt];
- at_n = del_n[del_cnt];
-
- next = snmp_mib_node_delete(at_rn, at_n);
- if (next != NULL)
- {
- LWIP_ASSERT("next_count == 0",next->count == 0);
- snmp_mib_lrn_free(next);
- }
- }
- }
- /* disable getnext traversal on empty tables */
- if(arptree_root.count == 0) at.maxlength = 0;
- if(ipntomtree_root.count == 0) ipntomtable.maxlength = 0;
-}
-
-void snmp_inc_ipinreceives(void)
-{
- ipinreceives++;
-}
-
-void snmp_inc_ipinhdrerrors(void)
-{
- ipinhdrerrors++;
-}
-
-void snmp_inc_ipinaddrerrors(void)
-{
- ipinaddrerrors++;
-}
-
-void snmp_inc_ipforwdatagrams(void)
-{
- ipforwdatagrams++;
-}
-
-void snmp_inc_ipinunknownprotos(void)
-{
- ipinunknownprotos++;
-}
-
-void snmp_inc_ipindiscards(void)
-{
- ipindiscards++;
-}
-
-void snmp_inc_ipindelivers(void)
-{
- ipindelivers++;
-}
-
-void snmp_inc_ipoutrequests(void)
-{
- ipoutrequests++;
-}
-
-void snmp_inc_ipoutdiscards(void)
-{
- ipoutdiscards++;
-}
-
-void snmp_inc_ipoutnoroutes(void)
-{
- ipoutnoroutes++;
-}
-
-void snmp_inc_ipreasmreqds(void)
-{
- ipreasmreqds++;
-}
-
-void snmp_inc_ipreasmoks(void)
-{
- ipreasmoks++;
-}
-
-void snmp_inc_ipreasmfails(void)
-{
- ipreasmfails++;
-}
-
-void snmp_inc_ipfragoks(void)
-{
- ipfragoks++;
-}
-
-void snmp_inc_ipfragfails(void)
-{
- ipfragfails++;
-}
-
-void snmp_inc_ipfragcreates(void)
-{
- ipfragcreates++;
-}
-
-void snmp_inc_iproutingdiscards(void)
-{
- iproutingdiscards++;
-}
-
-/**
- * Inserts ipAddrTable indexes (.ipAdEntAddr)
- * into index tree.
- */
-void snmp_insert_ipaddridx_tree(struct netif *ni)
-{
- struct mib_list_rootnode *ipa_rn;
- struct mib_list_node *ipa_node;
- s32_t ipaddridx[4];
- u8_t level;
-
- LWIP_ASSERT("ni != NULL", ni != NULL);
- snmp_iptooid(&ni->ip_addr, &ipaddridx[0]);
-
- level = 0;
- ipa_rn = &ipaddrtree_root;
- while (level < 4)
- {
- ipa_node = NULL;
- snmp_mib_node_insert(ipa_rn, ipaddridx[level], &ipa_node);
- if ((level != 3) && (ipa_node != NULL))
- {
- if (ipa_node->nptr == NULL)
- {
- ipa_rn = snmp_mib_lrn_alloc();
- ipa_node->nptr = (struct mib_node*)ipa_rn;
- if (ipa_rn != NULL)
- {
- if (level == 2)
- {
- ipa_rn->get_object_def = ip_addrentry_get_object_def;
- ipa_rn->get_value = ip_addrentry_get_value;
- ipa_rn->set_test = noleafs_set_test;
- ipa_rn->set_value = noleafs_set_value;
- }
- }
- else
- {
- /* ipa_rn == NULL, malloc failure */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_ipaddridx_tree() insert failed, mem full"));
- break;
- }
- }
- else
- {
- ipa_rn = (struct mib_list_rootnode*)ipa_node->nptr;
- }
- }
- level++;
- }
- /* enable getnext traversal on filled table */
- ipaddrtable.maxlength = 1;
-}
-
-/**
- * Removes ipAddrTable indexes (.ipAdEntAddr)
- * from index tree.
- */
-void snmp_delete_ipaddridx_tree(struct netif *ni)
-{
- struct mib_list_rootnode *ipa_rn, *next, *del_rn[4];
- struct mib_list_node *ipa_n, *del_n[4];
- s32_t ipaddridx[4];
- u8_t fc, level, del_cnt;
-
- LWIP_ASSERT("ni != NULL", ni != NULL);
- snmp_iptooid(&ni->ip_addr, &ipaddridx[0]);
-
- /* mark nodes for deletion */
- level = 0;
- del_cnt = 0;
- ipa_rn = &ipaddrtree_root;
- while ((level < 4) && (ipa_rn != NULL))
- {
- fc = snmp_mib_node_find(ipa_rn, ipaddridx[level], &ipa_n);
- if (fc == 0)
- {
- /* ipaddridx[level] does not exist */
- del_cnt = 0;
- ipa_rn = NULL;
- }
- else if (fc == 1)
- {
- del_rn[del_cnt] = ipa_rn;
- del_n[del_cnt] = ipa_n;
- del_cnt++;
- ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr);
- }
- else if (fc == 2)
- {
- /* reset delete (2 or more childs) */
- del_cnt = 0;
- ipa_rn = (struct mib_list_rootnode*)(ipa_n->nptr);
- }
- level++;
- }
- /* delete marked index nodes */
- while (del_cnt > 0)
- {
- del_cnt--;
-
- ipa_rn = del_rn[del_cnt];
- ipa_n = del_n[del_cnt];
-
- next = snmp_mib_node_delete(ipa_rn, ipa_n);
- if (next != NULL)
- {
- LWIP_ASSERT("next_count == 0",next->count == 0);
- snmp_mib_lrn_free(next);
- }
- }
- /* disable getnext traversal on empty table */
- if (ipaddrtree_root.count == 0) ipaddrtable.maxlength = 0;
-}
-
-/**
- * Inserts ipRouteTable indexes (.ipRouteDest)
- * into index tree.
- *
- * @param dflt non-zero for the default rte, zero for network rte
- * @param ni points to network interface for this rte
- *
- * @todo record sysuptime for _this_ route when it is installed
- * (needed for ipRouteAge) in the netif.
- */
-void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni)
-{
- u8_t insert = 0;
- ip_addr_t dst;
-
- if (dflt != 0)
- {
- /* the default route 0.0.0.0 */
- ip_addr_set_any(&dst);
- insert = 1;
- }
- else
- {
- /* route to the network address */
- ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask);
- /* exclude 0.0.0.0 network (reserved for default rte) */
- if (!ip_addr_isany(&dst)) {
- insert = 1;
- }
- }
- if (insert)
- {
- struct mib_list_rootnode *iprte_rn;
- struct mib_list_node *iprte_node;
- s32_t iprteidx[4];
- u8_t level;
-
- snmp_iptooid(&dst, &iprteidx[0]);
- level = 0;
- iprte_rn = &iprtetree_root;
- while (level < 4)
- {
- iprte_node = NULL;
- snmp_mib_node_insert(iprte_rn, iprteidx[level], &iprte_node);
- if ((level != 3) && (iprte_node != NULL))
- {
- if (iprte_node->nptr == NULL)
- {
- iprte_rn = snmp_mib_lrn_alloc();
- iprte_node->nptr = (struct mib_node*)iprte_rn;
- if (iprte_rn != NULL)
- {
- if (level == 2)
- {
- iprte_rn->get_object_def = ip_rteentry_get_object_def;
- iprte_rn->get_value = ip_rteentry_get_value;
- iprte_rn->set_test = noleafs_set_test;
- iprte_rn->set_value = noleafs_set_value;
- }
- }
- else
- {
- /* iprte_rn == NULL, malloc failure */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_iprteidx_tree() insert failed, mem full"));
- break;
- }
- }
- else
- {
- iprte_rn = (struct mib_list_rootnode*)iprte_node->nptr;
- }
- }
- level++;
- }
- }
- /* enable getnext traversal on filled table */
- iprtetable.maxlength = 1;
-}
-
-/**
- * Removes ipRouteTable indexes (.ipRouteDest)
- * from index tree.
- *
- * @param dflt non-zero for the default rte, zero for network rte
- * @param ni points to network interface for this rte or NULL
- * for default route to be removed.
- */
-void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni)
-{
- u8_t del = 0;
- ip_addr_t dst;
-
- if (dflt != 0)
- {
- /* the default route 0.0.0.0 */
- ip_addr_set_any(&dst);
- del = 1;
- }
- else
- {
- /* route to the network address */
- ip_addr_get_network(&dst, &ni->ip_addr, &ni->netmask);
- /* exclude 0.0.0.0 network (reserved for default rte) */
- if (!ip_addr_isany(&dst)) {
- del = 1;
- }
- }
- if (del)
- {
- struct mib_list_rootnode *iprte_rn, *next, *del_rn[4];
- struct mib_list_node *iprte_n, *del_n[4];
- s32_t iprteidx[4];
- u8_t fc, level, del_cnt;
-
- snmp_iptooid(&dst, &iprteidx[0]);
- /* mark nodes for deletion */
- level = 0;
- del_cnt = 0;
- iprte_rn = &iprtetree_root;
- while ((level < 4) && (iprte_rn != NULL))
- {
- fc = snmp_mib_node_find(iprte_rn, iprteidx[level], &iprte_n);
- if (fc == 0)
- {
- /* iprteidx[level] does not exist */
- del_cnt = 0;
- iprte_rn = NULL;
- }
- else if (fc == 1)
- {
- del_rn[del_cnt] = iprte_rn;
- del_n[del_cnt] = iprte_n;
- del_cnt++;
- iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr);
- }
- else if (fc == 2)
- {
- /* reset delete (2 or more childs) */
- del_cnt = 0;
- iprte_rn = (struct mib_list_rootnode*)(iprte_n->nptr);
- }
- level++;
- }
- /* delete marked index nodes */
- while (del_cnt > 0)
- {
- del_cnt--;
-
- iprte_rn = del_rn[del_cnt];
- iprte_n = del_n[del_cnt];
-
- next = snmp_mib_node_delete(iprte_rn, iprte_n);
- if (next != NULL)
- {
- LWIP_ASSERT("next_count == 0",next->count == 0);
- snmp_mib_lrn_free(next);
- }
- }
- }
- /* disable getnext traversal on empty table */
- if (iprtetree_root.count == 0) iprtetable.maxlength = 0;
-}
-
-
-void snmp_inc_icmpinmsgs(void)
-{
- icmpinmsgs++;
-}
-
-void snmp_inc_icmpinerrors(void)
-{
- icmpinerrors++;
-}
-
-void snmp_inc_icmpindestunreachs(void)
-{
- icmpindestunreachs++;
-}
-
-void snmp_inc_icmpintimeexcds(void)
-{
- icmpintimeexcds++;
-}
-
-void snmp_inc_icmpinparmprobs(void)
-{
- icmpinparmprobs++;
-}
-
-void snmp_inc_icmpinsrcquenchs(void)
-{
- icmpinsrcquenchs++;
-}
-
-void snmp_inc_icmpinredirects(void)
-{
- icmpinredirects++;
-}
-
-void snmp_inc_icmpinechos(void)
-{
- icmpinechos++;
-}
-
-void snmp_inc_icmpinechoreps(void)
-{
- icmpinechoreps++;
-}
-
-void snmp_inc_icmpintimestamps(void)
-{
- icmpintimestamps++;
-}
-
-void snmp_inc_icmpintimestampreps(void)
-{
- icmpintimestampreps++;
-}
-
-void snmp_inc_icmpinaddrmasks(void)
-{
- icmpinaddrmasks++;
-}
-
-void snmp_inc_icmpinaddrmaskreps(void)
-{
- icmpinaddrmaskreps++;
-}
-
-void snmp_inc_icmpoutmsgs(void)
-{
- icmpoutmsgs++;
-}
-
-void snmp_inc_icmpouterrors(void)
-{
- icmpouterrors++;
-}
-
-void snmp_inc_icmpoutdestunreachs(void)
-{
- icmpoutdestunreachs++;
-}
-
-void snmp_inc_icmpouttimeexcds(void)
-{
- icmpouttimeexcds++;
-}
-
-void snmp_inc_icmpoutparmprobs(void)
-{
- icmpoutparmprobs++;
-}
-
-void snmp_inc_icmpoutsrcquenchs(void)
-{
- icmpoutsrcquenchs++;
-}
-
-void snmp_inc_icmpoutredirects(void)
-{
- icmpoutredirects++;
-}
-
-void snmp_inc_icmpoutechos(void)
-{
- icmpoutechos++;
-}
-
-void snmp_inc_icmpoutechoreps(void)
-{
- icmpoutechoreps++;
-}
-
-void snmp_inc_icmpouttimestamps(void)
-{
- icmpouttimestamps++;
-}
-
-void snmp_inc_icmpouttimestampreps(void)
-{
- icmpouttimestampreps++;
-}
-
-void snmp_inc_icmpoutaddrmasks(void)
-{
- icmpoutaddrmasks++;
-}
-
-void snmp_inc_icmpoutaddrmaskreps(void)
-{
- icmpoutaddrmaskreps++;
-}
-
-void snmp_inc_tcpactiveopens(void)
-{
- tcpactiveopens++;
-}
-
-void snmp_inc_tcppassiveopens(void)
-{
- tcppassiveopens++;
-}
-
-void snmp_inc_tcpattemptfails(void)
-{
- tcpattemptfails++;
-}
-
-void snmp_inc_tcpestabresets(void)
-{
- tcpestabresets++;
-}
-
-void snmp_inc_tcpinsegs(void)
-{
- tcpinsegs++;
-}
-
-void snmp_inc_tcpoutsegs(void)
-{
- tcpoutsegs++;
-}
-
-void snmp_inc_tcpretranssegs(void)
-{
- tcpretranssegs++;
-}
-
-void snmp_inc_tcpinerrs(void)
-{
- tcpinerrs++;
-}
-
-void snmp_inc_tcpoutrsts(void)
-{
- tcpoutrsts++;
-}
-
-void snmp_inc_udpindatagrams(void)
-{
- udpindatagrams++;
-}
-
-void snmp_inc_udpnoports(void)
-{
- udpnoports++;
-}
-
-void snmp_inc_udpinerrors(void)
-{
- udpinerrors++;
-}
-
-void snmp_inc_udpoutdatagrams(void)
-{
- udpoutdatagrams++;
-}
-
-/**
- * Inserts udpTable indexes (.udpLocalAddress.udpLocalPort)
- * into index tree.
- */
-void snmp_insert_udpidx_tree(struct udp_pcb *pcb)
-{
- struct mib_list_rootnode *udp_rn;
- struct mib_list_node *udp_node;
- s32_t udpidx[5];
- u8_t level;
-
- LWIP_ASSERT("pcb != NULL", pcb != NULL);
- snmp_iptooid(ipX_2_ip(&pcb->local_ip), &udpidx[0]);
- udpidx[4] = pcb->local_port;
-
- udp_rn = &udp_root;
- for (level = 0; level < 5; level++)
- {
- udp_node = NULL;
- snmp_mib_node_insert(udp_rn, udpidx[level], &udp_node);
- if ((level != 4) && (udp_node != NULL))
- {
- if (udp_node->nptr == NULL)
- {
- udp_rn = snmp_mib_lrn_alloc();
- udp_node->nptr = (struct mib_node*)udp_rn;
- if (udp_rn != NULL)
- {
- if (level == 3)
- {
- udp_rn->get_object_def = udpentry_get_object_def;
- udp_rn->get_value = udpentry_get_value;
- udp_rn->set_test = noleafs_set_test;
- udp_rn->set_value = noleafs_set_value;
- }
- }
- else
- {
- /* udp_rn == NULL, malloc failure */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_insert_udpidx_tree() insert failed, mem full"));
- break;
- }
- }
- else
- {
- udp_rn = (struct mib_list_rootnode*)udp_node->nptr;
- }
- }
- }
- udptable.maxlength = 1;
-}
-
-/**
- * Removes udpTable indexes (.udpLocalAddress.udpLocalPort)
- * from index tree.
- */
-void snmp_delete_udpidx_tree(struct udp_pcb *pcb)
-{
- struct udp_pcb *npcb;
- struct mib_list_rootnode *udp_rn, *next, *del_rn[5];
- struct mib_list_node *udp_n, *del_n[5];
- s32_t udpidx[5];
- u8_t bindings, fc, level, del_cnt;
-
- LWIP_ASSERT("pcb != NULL", pcb != NULL);
- snmp_iptooid(ipX_2_ip(&pcb->local_ip), &udpidx[0]);
- udpidx[4] = pcb->local_port;
-
- /* count PCBs for a given binding
- (e.g. when reusing ports or for temp output PCBs) */
- bindings = 0;
- npcb = udp_pcbs;
- while ((npcb != NULL))
- {
- if (ipX_addr_cmp(0, &npcb->local_ip, &pcb->local_ip) &&
- (npcb->local_port == udpidx[4]))
- {
- bindings++;
- }
- npcb = npcb->next;
- }
- if (bindings == 1)
- {
- /* selectively remove */
- /* mark nodes for deletion */
- level = 0;
- del_cnt = 0;
- udp_rn = &udp_root;
- while ((level < 5) && (udp_rn != NULL))
- {
- fc = snmp_mib_node_find(udp_rn, udpidx[level], &udp_n);
- if (fc == 0)
- {
- /* udpidx[level] does not exist */
- del_cnt = 0;
- udp_rn = NULL;
- }
- else if (fc == 1)
- {
- del_rn[del_cnt] = udp_rn;
- del_n[del_cnt] = udp_n;
- del_cnt++;
- udp_rn = (struct mib_list_rootnode*)(udp_n->nptr);
- }
- else if (fc == 2)
- {
- /* reset delete (2 or more childs) */
- del_cnt = 0;
- udp_rn = (struct mib_list_rootnode*)(udp_n->nptr);
- }
- level++;
- }
- /* delete marked index nodes */
- while (del_cnt > 0)
- {
- del_cnt--;
-
- udp_rn = del_rn[del_cnt];
- udp_n = del_n[del_cnt];
-
- next = snmp_mib_node_delete(udp_rn, udp_n);
- if (next != NULL)
- {
- LWIP_ASSERT("next_count == 0",next->count == 0);
- snmp_mib_lrn_free(next);
- }
- }
- }
- /* disable getnext traversal on empty table */
- if (udp_root.count == 0) udptable.maxlength = 0;
-}
-
-
-void snmp_inc_snmpinpkts(void)
-{
- snmpinpkts++;
-}
-
-void snmp_inc_snmpoutpkts(void)
-{
- snmpoutpkts++;
-}
-
-void snmp_inc_snmpinbadversions(void)
-{
- snmpinbadversions++;
-}
-
-void snmp_inc_snmpinbadcommunitynames(void)
-{
- snmpinbadcommunitynames++;
-}
-
-void snmp_inc_snmpinbadcommunityuses(void)
-{
- snmpinbadcommunityuses++;
-}
-
-void snmp_inc_snmpinasnparseerrs(void)
-{
- snmpinasnparseerrs++;
-}
-
-void snmp_inc_snmpintoobigs(void)
-{
- snmpintoobigs++;
-}
-
-void snmp_inc_snmpinnosuchnames(void)
-{
- snmpinnosuchnames++;
-}
-
-void snmp_inc_snmpinbadvalues(void)
-{
- snmpinbadvalues++;
-}
-
-void snmp_inc_snmpinreadonlys(void)
-{
- snmpinreadonlys++;
-}
-
-void snmp_inc_snmpingenerrs(void)
-{
- snmpingenerrs++;
-}
-
-void snmp_add_snmpintotalreqvars(u8_t value)
-{
- snmpintotalreqvars += value;
-}
-
-void snmp_add_snmpintotalsetvars(u8_t value)
-{
- snmpintotalsetvars += value;
-}
-
-void snmp_inc_snmpingetrequests(void)
-{
- snmpingetrequests++;
-}
-
-void snmp_inc_snmpingetnexts(void)
-{
- snmpingetnexts++;
-}
-
-void snmp_inc_snmpinsetrequests(void)
-{
- snmpinsetrequests++;
-}
-
-void snmp_inc_snmpingetresponses(void)
-{
- snmpingetresponses++;
-}
-
-void snmp_inc_snmpintraps(void)
-{
- snmpintraps++;
-}
-
-void snmp_inc_snmpouttoobigs(void)
-{
- snmpouttoobigs++;
-}
-
-void snmp_inc_snmpoutnosuchnames(void)
-{
- snmpoutnosuchnames++;
-}
-
-void snmp_inc_snmpoutbadvalues(void)
-{
- snmpoutbadvalues++;
-}
-
-void snmp_inc_snmpoutgenerrs(void)
-{
- snmpoutgenerrs++;
-}
-
-void snmp_inc_snmpoutgetrequests(void)
-{
- snmpoutgetrequests++;
-}
-
-void snmp_inc_snmpoutgetnexts(void)
-{
- snmpoutgetnexts++;
-}
-
-void snmp_inc_snmpoutsetrequests(void)
-{
- snmpoutsetrequests++;
-}
-
-void snmp_inc_snmpoutgetresponses(void)
-{
- snmpoutgetresponses++;
-}
-
-void snmp_inc_snmpouttraps(void)
-{
- snmpouttraps++;
-}
-
-void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid)
-{
- *oid = &snmpgrp_id;
-}
-
-void snmp_set_snmpenableauthentraps(u8_t *value)
-{
- if (value != NULL)
- {
- snmpenableauthentraps_ptr = value;
- }
-}
-
-void snmp_get_snmpenableauthentraps(u8_t *value)
-{
- *value = *snmpenableauthentraps_ptr;
-}
-
-void
-noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
- LWIP_UNUSED_ARG(ident_len);
- LWIP_UNUSED_ARG(ident);
- od->instance = MIB_OBJECT_NONE;
-}
-
-void
-noleafs_get_value(struct obj_def *od, u16_t len, void *value)
-{
- LWIP_UNUSED_ARG(od);
- LWIP_UNUSED_ARG(len);
- LWIP_UNUSED_ARG(value);
-}
-
-u8_t
-noleafs_set_test(struct obj_def *od, u16_t len, void *value)
-{
- LWIP_UNUSED_ARG(od);
- LWIP_UNUSED_ARG(len);
- LWIP_UNUSED_ARG(value);
- /* can't set */
- return 0;
-}
-
-void
-noleafs_set_value(struct obj_def *od, u16_t len, void *value)
-{
- LWIP_UNUSED_ARG(od);
- LWIP_UNUSED_ARG(len);
- LWIP_UNUSED_ARG(value);
-}
-
-
-/**
- * Returns systems object definitions.
- *
- * @param ident_len the address length (2)
- * @param ident points to objectname.0 (object id trailer)
- * @param od points to object definition.
- */
-static void
-system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
- u8_t id;
-
- /* return to object name, adding index depth (1) */
- ident_len += 1;
- ident -= 1;
- if (ident_len == 2)
- {
- od->id_inst_len = ident_len;
- od->id_inst_ptr = ident;
-
- LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
- id = (u8_t)ident[0];
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def system.%"U16_F".0\n",(u16_t)id));
- switch (id)
- {
- case 1: /* sysDescr */
- od->instance = MIB_OBJECT_SCALAR;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
- od->v_len = *sysdescr_len_ptr;
- break;
- case 2: /* sysObjectID */
- od->instance = MIB_OBJECT_SCALAR;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID);
- od->v_len = sysobjid.len * sizeof(s32_t);
- break;
- case 3: /* sysUpTime */
- od->instance = MIB_OBJECT_SCALAR;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS);
- od->v_len = sizeof(u32_t);
- break;
- case 4: /* sysContact */
- od->instance = MIB_OBJECT_SCALAR;
- od->access = MIB_OBJECT_READ_WRITE;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
- od->v_len = *syscontact_len_ptr;
- break;
- case 5: /* sysName */
- od->instance = MIB_OBJECT_SCALAR;
- od->access = MIB_OBJECT_READ_WRITE;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
- od->v_len = *sysname_len_ptr;
- break;
- case 6: /* sysLocation */
- od->instance = MIB_OBJECT_SCALAR;
- od->access = MIB_OBJECT_READ_WRITE;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
- od->v_len = *syslocation_len_ptr;
- break;
- case 7: /* sysServices */
- od->instance = MIB_OBJECT_SCALAR;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
- od->v_len = sizeof(s32_t);
- break;
- default:
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no such object\n"));
- od->instance = MIB_OBJECT_NONE;
- break;
- };
- }
- else
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("system_get_object_def: no scalar\n"));
- od->instance = MIB_OBJECT_NONE;
- }
-}
-
-/**
- * Returns system object value.
- *
- * @param ident_len the address length (2)
- * @param ident points to objectname.0 (object id trailer)
- * @param len return value space (in bytes)
- * @param value points to (varbind) space to copy value into.
- */
-static void
-system_get_value(struct obj_def *od, u16_t len, void *value)
-{
- u8_t id;
-
- LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
- id = (u8_t)od->id_inst_ptr[0];
- switch (id)
- {
- case 1: /* sysDescr */
- ocstrncpy((u8_t*)value, sysdescr_ptr, len);
- break;
- case 2: /* sysObjectID */
- objectidncpy((s32_t*)value, (s32_t*)sysobjid.id, (u8_t)(len / sizeof(s32_t)));
- break;
- case 3: /* sysUpTime */
- {
- snmp_get_sysuptime((u32_t*)value);
- }
- break;
- case 4: /* sysContact */
- ocstrncpy((u8_t*)value, syscontact_ptr, len);
- break;
- case 5: /* sysName */
- ocstrncpy((u8_t*)value, sysname_ptr, len);
- break;
- case 6: /* sysLocation */
- ocstrncpy((u8_t*)value, syslocation_ptr, len);
- break;
- case 7: /* sysServices */
- {
- s32_t *sint_ptr = (s32_t*)value;
- *sint_ptr = sysservices;
- }
- break;
- };
-}
-
-static u8_t
-system_set_test(struct obj_def *od, u16_t len, void *value)
-{
- u8_t id, set_ok;
-
- LWIP_UNUSED_ARG(value);
- set_ok = 0;
- LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
- id = (u8_t)od->id_inst_ptr[0];
- switch (id)
- {
- case 4: /* sysContact */
- if ((syscontact_ptr != syscontact_default) &&
- (len <= 255))
- {
- set_ok = 1;
- }
- break;
- case 5: /* sysName */
- if ((sysname_ptr != sysname_default) &&
- (len <= 255))
- {
- set_ok = 1;
- }
- break;
- case 6: /* sysLocation */
- if ((syslocation_ptr != syslocation_default) &&
- (len <= 255))
- {
- set_ok = 1;
- }
- break;
- };
- return set_ok;
-}
-
-static void
-system_set_value(struct obj_def *od, u16_t len, void *value)
-{
- u8_t id;
-
- LWIP_ASSERT("invalid len", len <= 0xff);
- LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
- id = (u8_t)od->id_inst_ptr[0];
- switch (id)
- {
- case 4: /* sysContact */
- ocstrncpy(syscontact_ptr, (u8_t*)value, len);
- *syscontact_len_ptr = (u8_t)len;
- break;
- case 5: /* sysName */
- ocstrncpy(sysname_ptr, (u8_t*)value, len);
- *sysname_len_ptr = (u8_t)len;
- break;
- case 6: /* sysLocation */
- ocstrncpy(syslocation_ptr, (u8_t*)value, len);
- *syslocation_len_ptr = (u8_t)len;
- break;
- };
-}
-
-/**
- * Returns interfaces.ifnumber object definition.
- *
- * @param ident_len the address length (2)
- * @param ident points to objectname.index
- * @param od points to object definition.
- */
-static void
-interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
- /* return to object name, adding index depth (1) */
- ident_len += 1;
- ident -= 1;
- if (ident_len == 2)
- {
- od->id_inst_len = ident_len;
- od->id_inst_ptr = ident;
-
- od->instance = MIB_OBJECT_SCALAR;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
- od->v_len = sizeof(s32_t);
- }
- else
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("interfaces_get_object_def: no scalar\n"));
- od->instance = MIB_OBJECT_NONE;
- }
-}
-
-/**
- * Returns interfaces.ifnumber object value.
- *
- * @param ident_len the address length (2)
- * @param ident points to objectname.0 (object id trailer)
- * @param len return value space (in bytes)
- * @param value points to (varbind) space to copy value into.
- */
-static void
-interfaces_get_value(struct obj_def *od, u16_t len, void *value)
-{
- LWIP_UNUSED_ARG(len);
- if (od->id_inst_ptr[0] == 1)
- {
- s32_t *sint_ptr = (s32_t*)value;
- *sint_ptr = iflist_root.count;
- }
-}
-
-/**
- * Returns ifentry object definitions.
- *
- * @param ident_len the address length (2)
- * @param ident points to objectname.index
- * @param od points to object definition.
- */
-static void
-ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
- u8_t id;
-
- /* return to object name, adding index depth (1) */
- ident_len += 1;
- ident -= 1;
- if (ident_len == 2)
- {
- od->id_inst_len = ident_len;
- od->id_inst_ptr = ident;
-
- LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
- id = (u8_t)ident[0];
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ifentry.%"U16_F"\n",(u16_t)id));
- switch (id)
- {
- case 1: /* ifIndex */
- case 3: /* ifType */
- case 4: /* ifMtu */
- case 8: /* ifOperStatus */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
- od->v_len = sizeof(s32_t);
- break;
- case 2: /* ifDescr */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
- /** @todo this should be some sort of sizeof(struct netif.name) */
- od->v_len = 2;
- break;
- case 5: /* ifSpeed */
- case 21: /* ifOutQLen */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE);
- od->v_len = sizeof(u32_t);
- break;
- case 6: /* ifPhysAddress */
- {
- struct netif *netif;
-
- snmp_ifindextonetif(ident[1], &netif);
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
- od->v_len = netif->hwaddr_len;
- }
- break;
- case 7: /* ifAdminStatus */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_WRITE;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
- od->v_len = sizeof(s32_t);
- break;
- case 9: /* ifLastChange */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS);
- od->v_len = sizeof(u32_t);
- break;
- case 10: /* ifInOctets */
- case 11: /* ifInUcastPkts */
- case 12: /* ifInNUcastPkts */
- case 13: /* ifInDiscarts */
- case 14: /* ifInErrors */
- case 15: /* ifInUnkownProtos */
- case 16: /* ifOutOctets */
- case 17: /* ifOutUcastPkts */
- case 18: /* ifOutNUcastPkts */
- case 19: /* ifOutDiscarts */
- case 20: /* ifOutErrors */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
- od->v_len = sizeof(u32_t);
- break;
- case 22: /* ifSpecific */
- /** @note returning zeroDotZero (0.0) no media specific MIB support */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID);
- od->v_len = ifspecific.len * sizeof(s32_t);
- break;
- default:
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no such object\n"));
- od->instance = MIB_OBJECT_NONE;
- break;
- };
- }
- else
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("ifentry_get_object_def: no scalar\n"));
- od->instance = MIB_OBJECT_NONE;
- }
-}
-
-/**
- * Returns ifentry object value.
- *
- * @param ident_len the address length (2)
- * @param ident points to objectname.0 (object id trailer)
- * @param len return value space (in bytes)
- * @param value points to (varbind) space to copy value into.
- */
-static void
-ifentry_get_value(struct obj_def *od, u16_t len, void *value)
-{
- struct netif *netif;
- u8_t id;
-
- snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
- LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
- id = (u8_t)od->id_inst_ptr[0];
- switch (id)
- {
- case 1: /* ifIndex */
- {
- s32_t *sint_ptr = (s32_t*)value;
- *sint_ptr = od->id_inst_ptr[1];
- }
- break;
- case 2: /* ifDescr */
- ocstrncpy((u8_t*)value, (u8_t*)netif->name, len);
- break;
- case 3: /* ifType */
- {
- s32_t *sint_ptr = (s32_t*)value;
- *sint_ptr = netif->link_type;
- }
- break;
- case 4: /* ifMtu */
- {
- s32_t *sint_ptr = (s32_t*)value;
- *sint_ptr = netif->mtu;
- }
- break;
- case 5: /* ifSpeed */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = netif->link_speed;
- }
- break;
- case 6: /* ifPhysAddress */
- ocstrncpy((u8_t*)value, netif->hwaddr, len);
- break;
- case 7: /* ifAdminStatus */
- {
- s32_t *sint_ptr = (s32_t*)value;
- if (netif_is_up(netif))
- {
- if (netif_is_link_up(netif))
- {
- *sint_ptr = 1; /* up */
- }
- else
- {
- *sint_ptr = 7; /* lowerLayerDown */
- }
- }
- else
- {
- *sint_ptr = 2; /* down */
- }
- }
- break;
- case 8: /* ifOperStatus */
- {
- s32_t *sint_ptr = (s32_t*)value;
- if (netif_is_up(netif))
- {
- *sint_ptr = 1;
- }
- else
- {
- *sint_ptr = 2;
- }
- }
- break;
- case 9: /* ifLastChange */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = netif->ts;
- }
- break;
- case 10: /* ifInOctets */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = netif->ifinoctets;
- }
- break;
- case 11: /* ifInUcastPkts */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = netif->ifinucastpkts;
- }
- break;
- case 12: /* ifInNUcastPkts */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = netif->ifinnucastpkts;
- }
- break;
- case 13: /* ifInDiscarts */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = netif->ifindiscards;
- }
- break;
- case 14: /* ifInErrors */
- case 15: /* ifInUnkownProtos */
- /** @todo add these counters! */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = 0;
- }
- break;
- case 16: /* ifOutOctets */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = netif->ifoutoctets;
- }
- break;
- case 17: /* ifOutUcastPkts */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = netif->ifoutucastpkts;
- }
- break;
- case 18: /* ifOutNUcastPkts */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = netif->ifoutnucastpkts;
- }
- break;
- case 19: /* ifOutDiscarts */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = netif->ifoutdiscards;
- }
- break;
- case 20: /* ifOutErrors */
- /** @todo add this counter! */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = 0;
- }
- break;
- case 21: /* ifOutQLen */
- /** @todo figure out if this must be 0 (no queue) or 1? */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = 0;
- }
- break;
- case 22: /* ifSpecific */
- objectidncpy((s32_t*)value, (s32_t*)ifspecific.id, (u8_t)(len / sizeof(s32_t)));
- break;
- };
-}
-
-#if !SNMP_SAFE_REQUESTS
-static u8_t
-ifentry_set_test(struct obj_def *od, u16_t len, void *value)
-{
- struct netif *netif;
- u8_t id, set_ok;
- LWIP_UNUSED_ARG(len);
-
- set_ok = 0;
- snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
- id = (u8_t)od->id_inst_ptr[0];
- switch (id)
- {
- case 7: /* ifAdminStatus */
- {
- s32_t *sint_ptr = (s32_t*)value;
- if (*sint_ptr == 1 || *sint_ptr == 2)
- set_ok = 1;
- }
- break;
- }
- return set_ok;
-}
-
-static void
-ifentry_set_value(struct obj_def *od, u16_t len, void *value)
-{
- struct netif *netif;
- u8_t id;
- LWIP_UNUSED_ARG(len);
-
- snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
- id = (u8_t)od->id_inst_ptr[0];
- switch (id)
- {
- case 7: /* ifAdminStatus */
- {
- s32_t *sint_ptr = (s32_t*)value;
- if (*sint_ptr == 1)
- {
- netif_set_up(netif);
- }
- else if (*sint_ptr == 2)
- {
- netif_set_down(netif);
- }
- }
- break;
- }
-}
-#endif /* SNMP_SAFE_REQUESTS */
-
-/**
- * Returns atentry object definitions.
- *
- * @param ident_len the address length (6)
- * @param ident points to objectname.atifindex.atnetaddress
- * @param od points to object definition.
- */
-static void
-atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
- /* return to object name, adding index depth (5) */
- ident_len += 5;
- ident -= 5;
-
- if (ident_len == 6)
- {
- od->id_inst_len = ident_len;
- od->id_inst_ptr = ident;
-
- switch (ident[0])
- {
- case 1: /* atIfIndex */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_WRITE;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
- od->v_len = sizeof(s32_t);
- break;
- case 2: /* atPhysAddress */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_WRITE;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
- od->v_len = 6; /** @todo try to use netif::hwaddr_len */
- break;
- case 3: /* atNetAddress */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_WRITE;
- od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
- od->v_len = 4;
- break;
- default:
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no such object\n"));
- od->instance = MIB_OBJECT_NONE;
- break;
- }
- }
- else
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("atentry_get_object_def: no scalar\n"));
- od->instance = MIB_OBJECT_NONE;
- }
-}
-
-static void
-atentry_get_value(struct obj_def *od, u16_t len, void *value)
-{
-#if LWIP_ARP
- u8_t id;
- struct eth_addr* ethaddr_ret;
- ip_addr_t* ipaddr_ret;
-#endif /* LWIP_ARP */
- ip_addr_t ip;
- struct netif *netif;
-
- LWIP_UNUSED_ARG(len);
- LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */
-
- snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
- snmp_oidtoip(&od->id_inst_ptr[2], &ip);
-
-#if LWIP_ARP /** @todo implement a netif_find_addr */
- if (etharp_find_addr(netif, &ip, &ethaddr_ret, &ipaddr_ret) > -1)
- {
- LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
- id = (u8_t)od->id_inst_ptr[0];
- switch (id)
- {
- case 1: /* atIfIndex */
- {
- s32_t *sint_ptr = (s32_t*)value;
- *sint_ptr = od->id_inst_ptr[1];
- }
- break;
- case 2: /* atPhysAddress */
- {
- struct eth_addr *dst = (struct eth_addr*)value;
-
- *dst = *ethaddr_ret;
- }
- break;
- case 3: /* atNetAddress */
- {
- ip_addr_t *dst = (ip_addr_t*)value;
-
- *dst = *ipaddr_ret;
- }
- break;
- }
- }
-#endif /* LWIP_ARP */
-}
-
-static void
-ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
- u8_t id;
-
- /* return to object name, adding index depth (1) */
- ident_len += 1;
- ident -= 1;
- if (ident_len == 2)
- {
- od->id_inst_len = ident_len;
- od->id_inst_ptr = ident;
-
- LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
- id = (u8_t)ident[0];
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def ip.%"U16_F".0\n",(u16_t)id));
- switch (id)
- {
- case 1: /* ipForwarding */
- case 2: /* ipDefaultTTL */
- od->instance = MIB_OBJECT_SCALAR;
- od->access = MIB_OBJECT_READ_WRITE;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
- od->v_len = sizeof(s32_t);
- break;
- case 3: /* ipInReceives */
- case 4: /* ipInHdrErrors */
- case 5: /* ipInAddrErrors */
- case 6: /* ipForwDatagrams */
- case 7: /* ipInUnknownProtos */
- case 8: /* ipInDiscards */
- case 9: /* ipInDelivers */
- case 10: /* ipOutRequests */
- case 11: /* ipOutDiscards */
- case 12: /* ipOutNoRoutes */
- case 14: /* ipReasmReqds */
- case 15: /* ipReasmOKs */
- case 16: /* ipReasmFails */
- case 17: /* ipFragOKs */
- case 18: /* ipFragFails */
- case 19: /* ipFragCreates */
- case 23: /* ipRoutingDiscards */
- od->instance = MIB_OBJECT_SCALAR;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
- od->v_len = sizeof(u32_t);
- break;
- case 13: /* ipReasmTimeout */
- od->instance = MIB_OBJECT_SCALAR;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
- od->v_len = sizeof(s32_t);
- break;
- default:
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no such object\n"));
- od->instance = MIB_OBJECT_NONE;
- break;
- };
- }
- else
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_get_object_def: no scalar\n"));
- od->instance = MIB_OBJECT_NONE;
- }
-}
-
-static void
-ip_get_value(struct obj_def *od, u16_t len, void *value)
-{
- u8_t id;
-
- LWIP_UNUSED_ARG(len);
- LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
- id = (u8_t)od->id_inst_ptr[0];
- switch (id)
- {
- case 1: /* ipForwarding */
- {
- s32_t *sint_ptr = (s32_t*)value;
-#if IP_FORWARD
- /* forwarding */
- *sint_ptr = 1;
-#else
- /* not-forwarding */
- *sint_ptr = 2;
-#endif
- }
- break;
- case 2: /* ipDefaultTTL */
- {
- s32_t *sint_ptr = (s32_t*)value;
- *sint_ptr = IP_DEFAULT_TTL;
- }
- break;
- case 3: /* ipInReceives */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = ipinreceives;
- }
- break;
- case 4: /* ipInHdrErrors */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = ipinhdrerrors;
- }
- break;
- case 5: /* ipInAddrErrors */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = ipinaddrerrors;
- }
- break;
- case 6: /* ipForwDatagrams */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = ipforwdatagrams;
- }
- break;
- case 7: /* ipInUnknownProtos */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = ipinunknownprotos;
- }
- break;
- case 8: /* ipInDiscards */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = ipindiscards;
- }
- break;
- case 9: /* ipInDelivers */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = ipindelivers;
- }
- break;
- case 10: /* ipOutRequests */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = ipoutrequests;
- }
- break;
- case 11: /* ipOutDiscards */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = ipoutdiscards;
- }
- break;
- case 12: /* ipOutNoRoutes */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = ipoutnoroutes;
- }
- break;
- case 13: /* ipReasmTimeout */
- {
- s32_t *sint_ptr = (s32_t*)value;
-#if IP_REASSEMBLY
- *sint_ptr = IP_REASS_MAXAGE;
-#else
- *sint_ptr = 0;
-#endif
- }
- break;
- case 14: /* ipReasmReqds */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = ipreasmreqds;
- }
- break;
- case 15: /* ipReasmOKs */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = ipreasmoks;
- }
- break;
- case 16: /* ipReasmFails */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = ipreasmfails;
- }
- break;
- case 17: /* ipFragOKs */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = ipfragoks;
- }
- break;
- case 18: /* ipFragFails */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = ipfragfails;
- }
- break;
- case 19: /* ipFragCreates */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = ipfragcreates;
- }
- break;
- case 23: /* ipRoutingDiscards */
- /** @todo can lwIP discard routes at all?? hardwire this to 0?? */
- {
- u32_t *uint_ptr = (u32_t*)value;
- *uint_ptr = iproutingdiscards;
- }
- break;
- };
-}
-
-/**
- * Test ip object value before setting.
- *
- * @param od is the object definition
- * @param len return value space (in bytes)
- * @param value points to (varbind) space to copy value from.
- *
- * @note we allow set if the value matches the hardwired value,
- * otherwise return badvalue.
- */
-static u8_t
-ip_set_test(struct obj_def *od, u16_t len, void *value)
-{
- u8_t id, set_ok;
- s32_t *sint_ptr = (s32_t*)value;
-
- LWIP_UNUSED_ARG(len);
- set_ok = 0;
- LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
- id = (u8_t)od->id_inst_ptr[0];
- switch (id)
- {
- case 1: /* ipForwarding */
-#if IP_FORWARD
- /* forwarding */
- if (*sint_ptr == 1)
-#else
- /* not-forwarding */
- if (*sint_ptr == 2)
-#endif
- {
- set_ok = 1;
- }
- break;
- case 2: /* ipDefaultTTL */
- if (*sint_ptr == IP_DEFAULT_TTL)
- {
- set_ok = 1;
- }
- break;
- };
- return set_ok;
-}
-
-static void
-ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
- /* return to object name, adding index depth (4) */
- ident_len += 4;
- ident -= 4;
-
- if (ident_len == 5)
- {
- u8_t id;
-
- od->id_inst_len = ident_len;
- od->id_inst_ptr = ident;
-
- LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
- id = (u8_t)ident[0];
- switch (id)
- {
- case 1: /* ipAdEntAddr */
- case 3: /* ipAdEntNetMask */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
- od->v_len = 4;
- break;
- case 2: /* ipAdEntIfIndex */
- case 4: /* ipAdEntBcastAddr */
- case 5: /* ipAdEntReasmMaxSize */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
- od->v_len = sizeof(s32_t);
- break;
- default:
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no such object\n"));
- od->instance = MIB_OBJECT_NONE;
- break;
- }
- }
- else
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_addrentry_get_object_def: no scalar\n"));
- od->instance = MIB_OBJECT_NONE;
- }
-}
-
-static void
-ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value)
-{
- u8_t id;
- u16_t ifidx;
- ip_addr_t ip;
- struct netif *netif = netif_list;
-
- LWIP_UNUSED_ARG(len);
- snmp_oidtoip(&od->id_inst_ptr[1], &ip);
- ifidx = 0;
- while ((netif != NULL) && !ip_addr_cmp(&ip, &netif->ip_addr))
- {
- netif = netif->next;
- ifidx++;
- }
-
- if (netif != NULL)
- {
- LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
- id = (u8_t)od->id_inst_ptr[0];
- switch (id)
- {
- case 1: /* ipAdEntAddr */
- {
- ip_addr_t *dst = (ip_addr_t*)value;
- *dst = netif->ip_addr;
- }
- break;
- case 2: /* ipAdEntIfIndex */
- {
- s32_t *sint_ptr = (s32_t*)value;
- *sint_ptr = ifidx + 1;
- }
- break;
- case 3: /* ipAdEntNetMask */
- {
- ip_addr_t *dst = (ip_addr_t*)value;
- *dst = netif->netmask;
- }
- break;
- case 4: /* ipAdEntBcastAddr */
- {
- s32_t *sint_ptr = (s32_t*)value;
-
- /* lwIP oddity, there's no broadcast
- address in the netif we can rely on */
- *sint_ptr = IPADDR_BROADCAST & 1;
- }
- break;
- case 5: /* ipAdEntReasmMaxSize */
- {
- s32_t *sint_ptr = (s32_t*)value;
-#if IP_REASSEMBLY
- /* @todo The theoretical maximum is IP_REASS_MAX_PBUFS * size of the pbufs,
- * but only if receiving one fragmented packet at a time.
- * The current solution is to calculate for 2 simultaneous packets...
- */
- *sint_ptr = (IP_HLEN + ((IP_REASS_MAX_PBUFS/2) *
- (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN - IP_HLEN)));
-#else
- /** @todo returning MTU would be a bad thing and
- returning a wild guess like '576' isn't good either */
- *sint_ptr = 0;
-#endif
- }
- break;
- }
- }
-}
-
-/**
- * @note
- * lwIP IP routing is currently using the network addresses in netif_list.
- * if no suitable network IP is found in netif_list, the default_netif is used.
- */
-static void
-ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
- u8_t id;
-
- /* return to object name, adding index depth (4) */
- ident_len += 4;
- ident -= 4;
-
- if (ident_len == 5)
- {
- od->id_inst_len = ident_len;
- od->id_inst_ptr = ident;
-
- LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
- id = (u8_t)ident[0];
- switch (id)
- {
- case 1: /* ipRouteDest */
- case 7: /* ipRouteNextHop */
- case 11: /* ipRouteMask */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_WRITE;
- od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
- od->v_len = 4;
- break;
- case 2: /* ipRouteIfIndex */
- case 3: /* ipRouteMetric1 */
- case 4: /* ipRouteMetric2 */
- case 5: /* ipRouteMetric3 */
- case 6: /* ipRouteMetric4 */
- case 8: /* ipRouteType */
- case 10: /* ipRouteAge */
- case 12: /* ipRouteMetric5 */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_WRITE;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
- od->v_len = sizeof(s32_t);
- break;
- case 9: /* ipRouteProto */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
- od->v_len = sizeof(s32_t);
- break;
- case 13: /* ipRouteInfo */
- /** @note returning zeroDotZero (0.0) no routing protocol specific MIB */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID);
- od->v_len = iprouteinfo.len * sizeof(s32_t);
- break;
- default:
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no such object\n"));
- od->instance = MIB_OBJECT_NONE;
- break;
- }
- }
- else
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_rteentry_get_object_def: no scalar\n"));
- od->instance = MIB_OBJECT_NONE;
- }
-}
-
-static void
-ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value)
-{
- struct netif *netif;
- ip_addr_t dest;
- s32_t *ident;
- u8_t id;
-
- ident = od->id_inst_ptr;
- snmp_oidtoip(&ident[1], &dest);
-
- if (ip_addr_isany(&dest))
- {
- /* ip_route() uses default netif for default route */
- netif = netif_default;
- }
- else
- {
- /* not using ip_route(), need exact match! */
- netif = netif_list;
- while ((netif != NULL) &&
- !ip_addr_netcmp(&dest, &(netif->ip_addr), &(netif->netmask)) )
- {
- netif = netif->next;
- }
- }
- if (netif != NULL)
- {
- LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
- id = (u8_t)ident[0];
- switch (id)
- {
- case 1: /* ipRouteDest */
- {
- ip_addr_t *dst = (ip_addr_t*)value;
-
- if (ip_addr_isany(&dest))
- {
- /* default rte has 0.0.0.0 dest */
- ip_addr_set_zero(dst);
- }
- else
- {
- /* netifs have netaddress dest */
- ip_addr_get_network(dst, &netif->ip_addr, &netif->netmask);
- }
- }
- break;
- case 2: /* ipRouteIfIndex */
- {
- s32_t *sint_ptr = (s32_t*)value;
-
- snmp_netiftoifindex(netif, sint_ptr);
- }
- break;
- case 3: /* ipRouteMetric1 */
- {
- s32_t *sint_ptr = (s32_t*)value;
-
- if (ip_addr_isany(&dest))
- {
- /* default rte has metric 1 */
- *sint_ptr = 1;
- }
- else
- {
- /* other rtes have metric 0 */
- *sint_ptr = 0;
- }
- }
- break;
- case 4: /* ipRouteMetric2 */
- case 5: /* ipRouteMetric3 */
- case 6: /* ipRouteMetric4 */
- case 12: /* ipRouteMetric5 */
- {
- s32_t *sint_ptr = (s32_t*)value;
- /* not used */
- *sint_ptr = -1;
- }
- break;
- case 7: /* ipRouteNextHop */
- {
- ip_addr_t *dst = (ip_addr_t*)value;
-
- if (ip_addr_isany(&dest))
- {
- /* default rte: gateway */
- *dst = netif->gw;
- }
- else
- {
- /* other rtes: netif ip_addr */
- *dst = netif->ip_addr;
- }
- }
- break;
- case 8: /* ipRouteType */
- {
- s32_t *sint_ptr = (s32_t*)value;
-
- if (ip_addr_isany(&dest))
- {
- /* default rte is indirect */
- *sint_ptr = 4;
- }
- else
- {
- /* other rtes are direct */
- *sint_ptr = 3;
- }
- }
- break;
- case 9: /* ipRouteProto */
- {
- s32_t *sint_ptr = (s32_t*)value;
- /* locally defined routes */
- *sint_ptr = 2;
- }
- break;
- case 10: /* ipRouteAge */
- {
- s32_t *sint_ptr = (s32_t*)value;
- /** @todo (sysuptime - timestamp last change) / 100
- @see snmp_insert_iprteidx_tree() */
- *sint_ptr = 0;
- }
- break;
- case 11: /* ipRouteMask */
- {
- ip_addr_t *dst = (ip_addr_t*)value;
-
- if (ip_addr_isany(&dest))
- {
- /* default rte use 0.0.0.0 mask */
- ip_addr_set_zero(dst);
- }
- else
- {
- /* other rtes use netmask */
- *dst = netif->netmask;
- }
- }
- break;
- case 13: /* ipRouteInfo */
- objectidncpy((s32_t*)value, (s32_t*)iprouteinfo.id, (u8_t)(len / sizeof(s32_t)));
- break;
- }
- }
-}
-
-static void
-ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
- /* return to object name, adding index depth (5) */
- ident_len += 5;
- ident -= 5;
-
- if (ident_len == 6)
- {
- u8_t id;
-
- od->id_inst_len = ident_len;
- od->id_inst_ptr = ident;
-
- LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
- id = (u8_t)ident[0];
- switch (id)
- {
- case 1: /* ipNetToMediaIfIndex */
- case 4: /* ipNetToMediaType */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_WRITE;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
- od->v_len = sizeof(s32_t);
- break;
- case 2: /* ipNetToMediaPhysAddress */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_WRITE;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR);
- od->v_len = 6; /** @todo try to use netif::hwaddr_len */
- break;
- case 3: /* ipNetToMediaNetAddress */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_WRITE;
- od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
- od->v_len = 4;
- break;
- default:
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no such object\n"));
- od->instance = MIB_OBJECT_NONE;
- break;
- }
- }
- else
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("ip_ntomentry_get_object_def: no scalar\n"));
- od->instance = MIB_OBJECT_NONE;
- }
-}
-
-static void
-ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value)
-{
-#if LWIP_ARP
- u8_t id;
- struct eth_addr* ethaddr_ret;
- ip_addr_t* ipaddr_ret;
-#endif /* LWIP_ARP */
- ip_addr_t ip;
- struct netif *netif;
-
- LWIP_UNUSED_ARG(len);
- LWIP_UNUSED_ARG(value);/* if !LWIP_ARP */
-
- snmp_ifindextonetif(od->id_inst_ptr[1], &netif);
- snmp_oidtoip(&od->id_inst_ptr[2], &ip);
-
-#if LWIP_ARP /** @todo implement a netif_find_addr */
- if (etharp_find_addr(netif, &ip, &ethaddr_ret, &ipaddr_ret) > -1)
- {
- LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
- id = (u8_t)od->id_inst_ptr[0];
- switch (id)
- {
- case 1: /* ipNetToMediaIfIndex */
- {
- s32_t *sint_ptr = (s32_t*)value;
- *sint_ptr = od->id_inst_ptr[1];
- }
- break;
- case 2: /* ipNetToMediaPhysAddress */
- {
- struct eth_addr *dst = (struct eth_addr*)value;
-
- *dst = *ethaddr_ret;
- }
- break;
- case 3: /* ipNetToMediaNetAddress */
- {
- ip_addr_t *dst = (ip_addr_t*)value;
-
- *dst = *ipaddr_ret;
- }
- break;
- case 4: /* ipNetToMediaType */
- {
- s32_t *sint_ptr = (s32_t*)value;
- /* dynamic (?) */
- *sint_ptr = 3;
- }
- break;
- }
- }
-#endif /* LWIP_ARP */
-}
-
-static void
-icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
- /* return to object name, adding index depth (1) */
- ident_len += 1;
- ident -= 1;
- if ((ident_len == 2) &&
- (ident[0] > 0) && (ident[0] < 27))
- {
- od->id_inst_len = ident_len;
- od->id_inst_ptr = ident;
-
- od->instance = MIB_OBJECT_SCALAR;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
- od->v_len = sizeof(u32_t);
- }
- else
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("icmp_get_object_def: no scalar\n"));
- od->instance = MIB_OBJECT_NONE;
- }
-}
-
-static void
-icmp_get_value(struct obj_def *od, u16_t len, void *value)
-{
- u32_t *uint_ptr = (u32_t*)value;
- u8_t id;
-
- LWIP_UNUSED_ARG(len);
- LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
- id = (u8_t)od->id_inst_ptr[0];
- switch (id)
- {
- case 1: /* icmpInMsgs */
- *uint_ptr = icmpinmsgs;
- break;
- case 2: /* icmpInErrors */
- *uint_ptr = icmpinerrors;
- break;
- case 3: /* icmpInDestUnreachs */
- *uint_ptr = icmpindestunreachs;
- break;
- case 4: /* icmpInTimeExcds */
- *uint_ptr = icmpintimeexcds;
- break;
- case 5: /* icmpInParmProbs */
- *uint_ptr = icmpinparmprobs;
- break;
- case 6: /* icmpInSrcQuenchs */
- *uint_ptr = icmpinsrcquenchs;
- break;
- case 7: /* icmpInRedirects */
- *uint_ptr = icmpinredirects;
- break;
- case 8: /* icmpInEchos */
- *uint_ptr = icmpinechos;
- break;
- case 9: /* icmpInEchoReps */
- *uint_ptr = icmpinechoreps;
- break;
- case 10: /* icmpInTimestamps */
- *uint_ptr = icmpintimestamps;
- break;
- case 11: /* icmpInTimestampReps */
- *uint_ptr = icmpintimestampreps;
- break;
- case 12: /* icmpInAddrMasks */
- *uint_ptr = icmpinaddrmasks;
- break;
- case 13: /* icmpInAddrMaskReps */
- *uint_ptr = icmpinaddrmaskreps;
- break;
- case 14: /* icmpOutMsgs */
- *uint_ptr = icmpoutmsgs;
- break;
- case 15: /* icmpOutErrors */
- *uint_ptr = icmpouterrors;
- break;
- case 16: /* icmpOutDestUnreachs */
- *uint_ptr = icmpoutdestunreachs;
- break;
- case 17: /* icmpOutTimeExcds */
- *uint_ptr = icmpouttimeexcds;
- break;
- case 18: /* icmpOutParmProbs */
- *uint_ptr = icmpoutparmprobs;
- break;
- case 19: /* icmpOutSrcQuenchs */
- *uint_ptr = icmpoutsrcquenchs;
- break;
- case 20: /* icmpOutRedirects */
- *uint_ptr = icmpoutredirects;
- break;
- case 21: /* icmpOutEchos */
- *uint_ptr = icmpoutechos;
- break;
- case 22: /* icmpOutEchoReps */
- *uint_ptr = icmpoutechoreps;
- break;
- case 23: /* icmpOutTimestamps */
- *uint_ptr = icmpouttimestamps;
- break;
- case 24: /* icmpOutTimestampReps */
- *uint_ptr = icmpouttimestampreps;
- break;
- case 25: /* icmpOutAddrMasks */
- *uint_ptr = icmpoutaddrmasks;
- break;
- case 26: /* icmpOutAddrMaskReps */
- *uint_ptr = icmpoutaddrmaskreps;
- break;
- }
-}
-
-#if LWIP_TCP
-/** @todo tcp grp */
-static void
-tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
- u8_t id;
-
- /* return to object name, adding index depth (1) */
- ident_len += 1;
- ident -= 1;
- if (ident_len == 2)
- {
- od->id_inst_len = ident_len;
- od->id_inst_ptr = ident;
-
- LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
- id = (u8_t)ident[0];
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id));
-
- switch (id)
- {
- case 1: /* tcpRtoAlgorithm */
- case 2: /* tcpRtoMin */
- case 3: /* tcpRtoMax */
- case 4: /* tcpMaxConn */
- od->instance = MIB_OBJECT_SCALAR;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
- od->v_len = sizeof(s32_t);
- break;
- case 5: /* tcpActiveOpens */
- case 6: /* tcpPassiveOpens */
- case 7: /* tcpAttemptFails */
- case 8: /* tcpEstabResets */
- case 10: /* tcpInSegs */
- case 11: /* tcpOutSegs */
- case 12: /* tcpRetransSegs */
- case 14: /* tcpInErrs */
- case 15: /* tcpOutRsts */
- od->instance = MIB_OBJECT_SCALAR;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
- od->v_len = sizeof(u32_t);
- break;
- case 9: /* tcpCurrEstab */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE);
- od->v_len = sizeof(u32_t);
- break;
- default:
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no such object\n"));
- od->instance = MIB_OBJECT_NONE;
- break;
- };
- }
- else
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcp_get_object_def: no scalar\n"));
- od->instance = MIB_OBJECT_NONE;
- }
-}
-
-static void
-tcp_get_value(struct obj_def *od, u16_t len, void *value)
-{
- u32_t *uint_ptr = (u32_t*)value;
- s32_t *sint_ptr = (s32_t*)value;
- u8_t id;
-
- LWIP_UNUSED_ARG(len);
- LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
- id = (u8_t)od->id_inst_ptr[0];
- switch (id)
- {
- case 1: /* tcpRtoAlgorithm, vanj(4) */
- *sint_ptr = 4;
- break;
- case 2: /* tcpRtoMin */
- /* @todo not the actual value, a guess,
- needs to be calculated */
- *sint_ptr = 1000;
- break;
- case 3: /* tcpRtoMax */
- /* @todo not the actual value, a guess,
- needs to be calculated */
- *sint_ptr = 60000;
- break;
- case 4: /* tcpMaxConn */
- *sint_ptr = MEMP_NUM_TCP_PCB;
- break;
- case 5: /* tcpActiveOpens */
- *uint_ptr = tcpactiveopens;
- break;
- case 6: /* tcpPassiveOpens */
- *uint_ptr = tcppassiveopens;
- break;
- case 7: /* tcpAttemptFails */
- *uint_ptr = tcpattemptfails;
- break;
- case 8: /* tcpEstabResets */
- *uint_ptr = tcpestabresets;
- break;
- case 9: /* tcpCurrEstab */
- {
- u16_t tcpcurrestab = 0;
- struct tcp_pcb *pcb = tcp_active_pcbs;
- while (pcb != NULL)
- {
- if ((pcb->state == ESTABLISHED) ||
- (pcb->state == CLOSE_WAIT))
- {
- tcpcurrestab++;
- }
- pcb = pcb->next;
- }
- *uint_ptr = tcpcurrestab;
- }
- break;
- case 10: /* tcpInSegs */
- *uint_ptr = tcpinsegs;
- break;
- case 11: /* tcpOutSegs */
- *uint_ptr = tcpoutsegs;
- break;
- case 12: /* tcpRetransSegs */
- *uint_ptr = tcpretranssegs;
- break;
- case 14: /* tcpInErrs */
- *uint_ptr = tcpinerrs;
- break;
- case 15: /* tcpOutRsts */
- *uint_ptr = tcpoutrsts;
- break;
- }
-}
-#ifdef THIS_SEEMS_UNUSED
-static void
-tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
- /* return to object name, adding index depth (10) */
- ident_len += 10;
- ident -= 10;
-
- if (ident_len == 11)
- {
- u8_t id;
-
- od->id_inst_len = ident_len;
- od->id_inst_ptr = ident;
-
- id = ident[0];
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("get_object_def tcp.%"U16_F".0\n",(u16_t)id));
-
- switch (id)
- {
- case 1: /* tcpConnState */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_WRITE;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
- od->v_len = sizeof(s32_t);
- break;
- case 2: /* tcpConnLocalAddress */
- case 4: /* tcpConnRemAddress */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
- od->v_len = 4;
- break;
- case 3: /* tcpConnLocalPort */
- case 5: /* tcpConnRemPort */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
- od->v_len = sizeof(s32_t);
- break;
- default:
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n"));
- od->instance = MIB_OBJECT_NONE;
- break;
- };
- }
- else
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("tcpconnentry_get_object_def: no such object\n"));
- od->instance = MIB_OBJECT_NONE;
- }
-}
-
-static void
-tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value)
-{
- ip_addr_t lip, rip;
- u16_t lport, rport;
- s32_t *ident;
-
- ident = od->id_inst_ptr;
- snmp_oidtoip(&ident[1], &lip);
- lport = ident[5];
- snmp_oidtoip(&ident[6], &rip);
- rport = ident[10];
-
- /** @todo find matching PCB */
-}
-#endif /* if 0 */
-#endif
-
-static void
-udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
- /* return to object name, adding index depth (1) */
- ident_len += 1;
- ident -= 1;
- if ((ident_len == 2) &&
- (ident[0] > 0) && (ident[0] < 6))
- {
- od->id_inst_len = ident_len;
- od->id_inst_ptr = ident;
-
- od->instance = MIB_OBJECT_SCALAR;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
- od->v_len = sizeof(u32_t);
- }
- else
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("udp_get_object_def: no scalar\n"));
- od->instance = MIB_OBJECT_NONE;
- }
-}
-
-static void
-udp_get_value(struct obj_def *od, u16_t len, void *value)
-{
- u32_t *uint_ptr = (u32_t*)value;
- u8_t id;
-
- LWIP_UNUSED_ARG(len);
- LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
- id = (u8_t)od->id_inst_ptr[0];
- switch (id)
- {
- case 1: /* udpInDatagrams */
- *uint_ptr = udpindatagrams;
- break;
- case 2: /* udpNoPorts */
- *uint_ptr = udpnoports;
- break;
- case 3: /* udpInErrors */
- *uint_ptr = udpinerrors;
- break;
- case 4: /* udpOutDatagrams */
- *uint_ptr = udpoutdatagrams;
- break;
- }
-}
-
-static void
-udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
- /* return to object name, adding index depth (5) */
- ident_len += 5;
- ident -= 5;
-
- if (ident_len == 6)
- {
- od->id_inst_len = ident_len;
- od->id_inst_ptr = ident;
-
- switch (ident[0])
- {
- case 1: /* udpLocalAddress */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR);
- od->v_len = 4;
- break;
- case 2: /* udpLocalPort */
- od->instance = MIB_OBJECT_TAB;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
- od->v_len = sizeof(s32_t);
- break;
- default:
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no such object\n"));
- od->instance = MIB_OBJECT_NONE;
- break;
- }
- }
- else
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("udpentry_get_object_def: no scalar\n"));
- od->instance = MIB_OBJECT_NONE;
- }
-}
-
-static void
-udpentry_get_value(struct obj_def *od, u16_t len, void *value)
-{
- u8_t id;
- struct udp_pcb *pcb;
- ipX_addr_t ip;
- u16_t port;
-
- LWIP_UNUSED_ARG(len);
- snmp_oidtoip(&od->id_inst_ptr[1], (ip_addr_t*)&ip);
- LWIP_ASSERT("invalid port", (od->id_inst_ptr[5] >= 0) && (od->id_inst_ptr[5] <= 0xffff));
- port = (u16_t)od->id_inst_ptr[5];
-
- pcb = udp_pcbs;
- while ((pcb != NULL) &&
- !(ipX_addr_cmp(0, &pcb->local_ip, &ip) &&
- (pcb->local_port == port)))
- {
- pcb = pcb->next;
- }
-
- if (pcb != NULL)
- {
- LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
- id = (u8_t)od->id_inst_ptr[0];
- switch (id)
- {
- case 1: /* udpLocalAddress */
- {
- ipX_addr_t *dst = (ipX_addr_t*)value;
- ipX_addr_copy(0, *dst, pcb->local_ip);
- }
- break;
- case 2: /* udpLocalPort */
- {
- s32_t *sint_ptr = (s32_t*)value;
- *sint_ptr = pcb->local_port;
- }
- break;
- }
- }
-}
-
-static void
-snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od)
-{
- /* return to object name, adding index depth (1) */
- ident_len += 1;
- ident -= 1;
- if (ident_len == 2)
- {
- u8_t id;
-
- od->id_inst_len = ident_len;
- od->id_inst_ptr = ident;
-
- LWIP_ASSERT("invalid id", (ident[0] >= 0) && (ident[0] <= 0xff));
- id = (u8_t)ident[0];
- switch (id)
- {
- case 1: /* snmpInPkts */
- case 2: /* snmpOutPkts */
- case 3: /* snmpInBadVersions */
- case 4: /* snmpInBadCommunityNames */
- case 5: /* snmpInBadCommunityUses */
- case 6: /* snmpInASNParseErrs */
- case 8: /* snmpInTooBigs */
- case 9: /* snmpInNoSuchNames */
- case 10: /* snmpInBadValues */
- case 11: /* snmpInReadOnlys */
- case 12: /* snmpInGenErrs */
- case 13: /* snmpInTotalReqVars */
- case 14: /* snmpInTotalSetVars */
- case 15: /* snmpInGetRequests */
- case 16: /* snmpInGetNexts */
- case 17: /* snmpInSetRequests */
- case 18: /* snmpInGetResponses */
- case 19: /* snmpInTraps */
- case 20: /* snmpOutTooBigs */
- case 21: /* snmpOutNoSuchNames */
- case 22: /* snmpOutBadValues */
- case 24: /* snmpOutGenErrs */
- case 25: /* snmpOutGetRequests */
- case 26: /* snmpOutGetNexts */
- case 27: /* snmpOutSetRequests */
- case 28: /* snmpOutGetResponses */
- case 29: /* snmpOutTraps */
- od->instance = MIB_OBJECT_SCALAR;
- od->access = MIB_OBJECT_READ_ONLY;
- od->asn_type = (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER);
- od->v_len = sizeof(u32_t);
- break;
- case 30: /* snmpEnableAuthenTraps */
- od->instance = MIB_OBJECT_SCALAR;
- od->access = MIB_OBJECT_READ_WRITE;
- od->asn_type = (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG);
- od->v_len = sizeof(s32_t);
- break;
- default:
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no such object\n"));
- od->instance = MIB_OBJECT_NONE;
- break;
- };
- }
- else
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("snmp_get_object_def: no scalar\n"));
- od->instance = MIB_OBJECT_NONE;
- }
-}
-
-static void
-snmp_get_value(struct obj_def *od, u16_t len, void *value)
-{
- u32_t *uint_ptr = (u32_t*)value;
- u8_t id;
-
- LWIP_UNUSED_ARG(len);
- LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
- id = (u8_t)od->id_inst_ptr[0];
- switch (id)
- {
- case 1: /* snmpInPkts */
- *uint_ptr = snmpinpkts;
- break;
- case 2: /* snmpOutPkts */
- *uint_ptr = snmpoutpkts;
- break;
- case 3: /* snmpInBadVersions */
- *uint_ptr = snmpinbadversions;
- break;
- case 4: /* snmpInBadCommunityNames */
- *uint_ptr = snmpinbadcommunitynames;
- break;
- case 5: /* snmpInBadCommunityUses */
- *uint_ptr = snmpinbadcommunityuses;
- break;
- case 6: /* snmpInASNParseErrs */
- *uint_ptr = snmpinasnparseerrs;
- break;
- case 8: /* snmpInTooBigs */
- *uint_ptr = snmpintoobigs;
- break;
- case 9: /* snmpInNoSuchNames */
- *uint_ptr = snmpinnosuchnames;
- break;
- case 10: /* snmpInBadValues */
- *uint_ptr = snmpinbadvalues;
- break;
- case 11: /* snmpInReadOnlys */
- *uint_ptr = snmpinreadonlys;
- break;
- case 12: /* snmpInGenErrs */
- *uint_ptr = snmpingenerrs;
- break;
- case 13: /* snmpInTotalReqVars */
- *uint_ptr = snmpintotalreqvars;
- break;
- case 14: /* snmpInTotalSetVars */
- *uint_ptr = snmpintotalsetvars;
- break;
- case 15: /* snmpInGetRequests */
- *uint_ptr = snmpingetrequests;
- break;
- case 16: /* snmpInGetNexts */
- *uint_ptr = snmpingetnexts;
- break;
- case 17: /* snmpInSetRequests */
- *uint_ptr = snmpinsetrequests;
- break;
- case 18: /* snmpInGetResponses */
- *uint_ptr = snmpingetresponses;
- break;
- case 19: /* snmpInTraps */
- *uint_ptr = snmpintraps;
- break;
- case 20: /* snmpOutTooBigs */
- *uint_ptr = snmpouttoobigs;
- break;
- case 21: /* snmpOutNoSuchNames */
- *uint_ptr = snmpoutnosuchnames;
- break;
- case 22: /* snmpOutBadValues */
- *uint_ptr = snmpoutbadvalues;
- break;
- case 24: /* snmpOutGenErrs */
- *uint_ptr = snmpoutgenerrs;
- break;
- case 25: /* snmpOutGetRequests */
- *uint_ptr = snmpoutgetrequests;
- break;
- case 26: /* snmpOutGetNexts */
- *uint_ptr = snmpoutgetnexts;
- break;
- case 27: /* snmpOutSetRequests */
- *uint_ptr = snmpoutsetrequests;
- break;
- case 28: /* snmpOutGetResponses */
- *uint_ptr = snmpoutgetresponses;
- break;
- case 29: /* snmpOutTraps */
- *uint_ptr = snmpouttraps;
- break;
- case 30: /* snmpEnableAuthenTraps */
- *uint_ptr = *snmpenableauthentraps_ptr;
- break;
- };
-}
-
-/**
- * Test snmp object value before setting.
- *
- * @param od is the object definition
- * @param len return value space (in bytes)
- * @param value points to (varbind) space to copy value from.
- */
-static u8_t
-snmp_set_test(struct obj_def *od, u16_t len, void *value)
-{
- u8_t id, set_ok;
-
- LWIP_UNUSED_ARG(len);
- set_ok = 0;
- LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
- id = (u8_t)od->id_inst_ptr[0];
- if (id == 30)
- {
- /* snmpEnableAuthenTraps */
- s32_t *sint_ptr = (s32_t*)value;
-
- if (snmpenableauthentraps_ptr != &snmpenableauthentraps_default)
- {
- /* we should have writable non-volatile mem here */
- if ((*sint_ptr == 1) || (*sint_ptr == 2))
- {
- set_ok = 1;
- }
- }
- else
- {
- /* const or hardwired value */
- if (*sint_ptr == snmpenableauthentraps_default)
- {
- set_ok = 1;
- }
- }
- }
- return set_ok;
-}
-
-static void
-snmp_set_value(struct obj_def *od, u16_t len, void *value)
-{
- u8_t id;
-
- LWIP_UNUSED_ARG(len);
- LWIP_ASSERT("invalid id", (od->id_inst_ptr[0] >= 0) && (od->id_inst_ptr[0] <= 0xff));
- id = (u8_t)od->id_inst_ptr[0];
- if (id == 30)
- {
- /* snmpEnableAuthenTraps */
- /* @todo @fixme: which kind of pointer is 'value'? s32_t or u8_t??? */
- u8_t *ptr = (u8_t*)value;
- *snmpenableauthentraps_ptr = *ptr;
- }
-}
-
-#endif /* LWIP_SNMP */
diff --git a/lwip/src/core/snmp/mib_structs.c b/lwip/src/core/snmp/mib_structs.c
deleted file mode 100644
index 2f185cb..0000000
--- a/lwip/src/core/snmp/mib_structs.c
+++ /dev/null
@@ -1,1174 +0,0 @@
-/**
- * @file
- * MIB tree access/construction functions.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/snmp_structs.h"
-#include "lwip/memp.h"
-#include "lwip/netif.h"
-
-/** .iso.org.dod.internet address prefix, @see snmp_iso_*() */
-const s32_t prefix[4] = {1, 3, 6, 1};
-
-#define NODE_STACK_SIZE (LWIP_SNMP_OBJ_ID_LEN)
-/** node stack entry (old news?) */
-struct nse
-{
- /** right child */
- struct mib_node* r_ptr;
- /** right child identifier */
- s32_t r_id;
- /** right child next level */
- u8_t r_nl;
-};
-static u8_t node_stack_cnt;
-static struct nse node_stack[NODE_STACK_SIZE];
-
-/**
- * Pushes nse struct onto stack.
- */
-static void
-push_node(struct nse* node)
-{
- LWIP_ASSERT("node_stack_cnt < NODE_STACK_SIZE",node_stack_cnt < NODE_STACK_SIZE);
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("push_node() node=%p id=%"S32_F"\n",(void*)(node->r_ptr),node->r_id));
- if (node_stack_cnt < NODE_STACK_SIZE)
- {
- node_stack[node_stack_cnt] = *node;
- node_stack_cnt++;
- }
-}
-
-/**
- * Pops nse struct from stack.
- */
-static void
-pop_node(struct nse* node)
-{
- if (node_stack_cnt > 0)
- {
- node_stack_cnt--;
- *node = node_stack[node_stack_cnt];
- }
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("pop_node() node=%p id=%"S32_F"\n",(void *)(node->r_ptr),node->r_id));
-}
-
-/**
- * Conversion from ifIndex to lwIP netif
- * @param ifindex is a s32_t object sub-identifier
- * @param netif points to returned netif struct pointer
- */
-void
-snmp_ifindextonetif(s32_t ifindex, struct netif **netif)
-{
- struct netif *nif = netif_list;
- s32_t i, ifidx;
-
- ifidx = ifindex - 1;
- i = 0;
- while ((nif != NULL) && (i < ifidx))
- {
- nif = nif->next;
- i++;
- }
- *netif = nif;
-}
-
-/**
- * Conversion from lwIP netif to ifIndex
- * @param netif points to a netif struct
- * @param ifidx points to s32_t object sub-identifier
- */
-void
-snmp_netiftoifindex(struct netif *netif, s32_t *ifidx)
-{
- struct netif *nif = netif_list;
- u16_t i;
-
- i = 0;
- while ((nif != NULL) && (nif != netif))
- {
- nif = nif->next;
- i++;
- }
- *ifidx = i+1;
-}
-
-/**
- * Conversion from oid to lwIP ip_addr
- * @param ident points to s32_t ident[4] input
- * @param ip points to output struct
- */
-void
-snmp_oidtoip(s32_t *ident, ip_addr_t *ip)
-{
- IP4_ADDR(ip, ident[0], ident[1], ident[2], ident[3]);
-}
-
-/**
- * Conversion from lwIP ip_addr to oid
- * @param ip points to input struct
- * @param ident points to s32_t ident[4] output
- */
-void
-snmp_iptooid(ip_addr_t *ip, s32_t *ident)
-{
- ident[0] = ip4_addr1(ip);
- ident[1] = ip4_addr2(ip);
- ident[2] = ip4_addr3(ip);
- ident[3] = ip4_addr4(ip);
-}
-
-struct mib_list_node *
-snmp_mib_ln_alloc(s32_t id)
-{
- struct mib_list_node *ln;
-
- ln = (struct mib_list_node *)memp_malloc(MEMP_SNMP_NODE);
- if (ln != NULL)
- {
- ln->prev = NULL;
- ln->next = NULL;
- ln->objid = id;
- ln->nptr = NULL;
- }
- return ln;
-}
-
-void
-snmp_mib_ln_free(struct mib_list_node *ln)
-{
- memp_free(MEMP_SNMP_NODE, ln);
-}
-
-struct mib_list_rootnode *
-snmp_mib_lrn_alloc(void)
-{
- struct mib_list_rootnode *lrn;
-
- lrn = (struct mib_list_rootnode*)memp_malloc(MEMP_SNMP_ROOTNODE);
- if (lrn != NULL)
- {
- lrn->get_object_def = noleafs_get_object_def;
- lrn->get_value = noleafs_get_value;
- lrn->set_test = noleafs_set_test;
- lrn->set_value = noleafs_set_value;
- lrn->node_type = MIB_NODE_LR;
- lrn->maxlength = 0;
- lrn->head = NULL;
- lrn->tail = NULL;
- lrn->count = 0;
- }
- return lrn;
-}
-
-void
-snmp_mib_lrn_free(struct mib_list_rootnode *lrn)
-{
- memp_free(MEMP_SNMP_ROOTNODE, lrn);
-}
-
-/**
- * Inserts node in idx list in a sorted
- * (ascending order) fashion and
- * allocates the node if needed.
- *
- * @param rn points to the root node
- * @param objid is the object sub identifier
- * @param insn points to a pointer to the inserted node
- * used for constructing the tree.
- * @return -1 if failed, 1 if inserted, 2 if present.
- */
-s8_t
-snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn)
-{
- struct mib_list_node *nn;
- s8_t insert;
-
- LWIP_ASSERT("rn != NULL",rn != NULL);
-
- /* -1 = malloc failure, 0 = not inserted, 1 = inserted, 2 = was present */
- insert = 0;
- if (rn->head == NULL)
- {
- /* empty list, add first node */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc empty list objid==%"S32_F"\n",objid));
- nn = snmp_mib_ln_alloc(objid);
- if (nn != NULL)
- {
- rn->head = nn;
- rn->tail = nn;
- *insn = nn;
- insert = 1;
- }
- else
- {
- insert = -1;
- }
- }
- else
- {
- struct mib_list_node *n;
- /* at least one node is present */
- n = rn->head;
- while ((n != NULL) && (insert == 0))
- {
- if (n->objid == objid)
- {
- /* node is already there */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("node already there objid==%"S32_F"\n",objid));
- *insn = n;
- insert = 2;
- }
- else if (n->objid < objid)
- {
- if (n->next == NULL)
- {
- /* alloc and insert at the tail */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins tail objid==%"S32_F"\n",objid));
- nn = snmp_mib_ln_alloc(objid);
- if (nn != NULL)
- {
- nn->next = NULL;
- nn->prev = n;
- n->next = nn;
- rn->tail = nn;
- *insn = nn;
- insert = 1;
- }
- else
- {
- /* insertion failure */
- insert = -1;
- }
- }
- else
- {
- /* there's more to explore: traverse list */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("traverse list\n"));
- n = n->next;
- }
- }
- else
- {
- /* n->objid > objid */
- /* alloc and insert between n->prev and n */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins n->prev, objid==%"S32_F", n\n",objid));
- nn = snmp_mib_ln_alloc(objid);
- if (nn != NULL)
- {
- if (n->prev == NULL)
- {
- /* insert at the head */
- nn->next = n;
- nn->prev = NULL;
- rn->head = nn;
- n->prev = nn;
- }
- else
- {
- /* insert in the middle */
- nn->next = n;
- nn->prev = n->prev;
- n->prev->next = nn;
- n->prev = nn;
- }
- *insn = nn;
- insert = 1;
- }
- else
- {
- /* insertion failure */
- insert = -1;
- }
- }
- }
- }
- if (insert == 1)
- {
- rn->count += 1;
- }
- LWIP_ASSERT("insert != 0",insert != 0);
- return insert;
-}
-
-/**
- * Finds node in idx list and returns deletion mark.
- *
- * @param rn points to the root node
- * @param objid is the object sub identifier
- * @param fn returns pointer to found node
- * @return 0 if not found, 1 if deletable,
- * 2 can't delete (2 or more children), 3 not a list_node
- */
-s8_t
-snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn)
-{
- s8_t fc;
- struct mib_list_node *n;
-
- LWIP_ASSERT("rn != NULL",rn != NULL);
- n = rn->head;
- while ((n != NULL) && (n->objid != objid))
- {
- n = n->next;
- }
- if (n == NULL)
- {
- fc = 0;
- }
- else if (n->nptr == NULL)
- {
- /* leaf, can delete node */
- fc = 1;
- }
- else
- {
- struct mib_list_rootnode *r;
-
- if (n->nptr->node_type == MIB_NODE_LR)
- {
- r = (struct mib_list_rootnode *)n->nptr;
- if (r->count > 1)
- {
- /* can't delete node */
- fc = 2;
- }
- else
- {
- /* count <= 1, can delete node */
- fc = 1;
- }
- }
- else
- {
- /* other node type */
- fc = 3;
- }
- }
- *fn = n;
- return fc;
-}
-
-/**
- * Removes node from idx list
- * if it has a single child left.
- *
- * @param rn points to the root node
- * @param n points to the node to delete
- * @return the nptr to be freed by caller
- */
-struct mib_list_rootnode *
-snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n)
-{
- struct mib_list_rootnode *next;
-
- LWIP_ASSERT("rn != NULL",rn != NULL);
- LWIP_ASSERT("n != NULL",n != NULL);
-
- /* caller must remove this sub-tree */
- next = (struct mib_list_rootnode*)(n->nptr);
- rn->count -= 1;
-
- if (n == rn->head)
- {
- rn->head = n->next;
- if (n->next != NULL)
- {
- /* not last node, new list begin */
- n->next->prev = NULL;
- }
- }
- else if (n == rn->tail)
- {
- rn->tail = n->prev;
- if (n->prev != NULL)
- {
- /* not last node, new list end */
- n->prev->next = NULL;
- }
- }
- else
- {
- /* node must be in the middle */
- n->prev->next = n->next;
- n->next->prev = n->prev;
- }
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("free list objid==%"S32_F"\n",n->objid));
- snmp_mib_ln_free(n);
- if (rn->count == 0)
- {
- rn->head = NULL;
- rn->tail = NULL;
- }
- return next;
-}
-
-
-
-/**
- * Searches tree for the supplied (scalar?) object identifier.
- *
- * @param node points to the root of the tree ('.internet')
- * @param ident_len the length of the supplied object identifier
- * @param ident points to the array of sub identifiers
- * @param np points to the found object instance (return)
- * @return pointer to the requested parent (!) node if success, NULL otherwise
- */
-struct mib_node *
-snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np)
-{
- u8_t node_type, ext_level;
-
- ext_level = 0;
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("node==%p *ident==%"S32_F"\n",(void*)node,*ident));
- while (node != NULL)
- {
- node_type = node->node_type;
- if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA))
- {
- struct mib_array_node *an;
- u16_t i;
-
- if (ident_len > 0)
- {
- /* array node (internal ROM or RAM, fixed length) */
- an = (struct mib_array_node *)node;
- i = 0;
- while ((i < an->maxlength) && (an->objid[i] != *ident))
- {
- i++;
- }
- if (i < an->maxlength)
- {
- /* found it, if available proceed to child, otherwise inspect leaf */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident));
- if (an->nptr[i] == NULL)
- {
- /* a scalar leaf OR table,
- inspect remaining instance number / table index */
- np->ident_len = ident_len;
- np->ident = ident;
- return (struct mib_node*)an;
- }
- else
- {
- /* follow next child pointer */
- ident++;
- ident_len--;
- node = an->nptr[i];
- }
- }
- else
- {
- /* search failed, identifier mismatch (nosuchname) */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed *ident==%"S32_F"\n",*ident));
- return NULL;
- }
- }
- else
- {
- /* search failed, short object identifier (nosuchname) */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed, short object identifier\n"));
- return NULL;
- }
- }
- else if(node_type == MIB_NODE_LR)
- {
- struct mib_list_rootnode *lrn;
- struct mib_list_node *ln;
-
- if (ident_len > 0)
- {
- /* list root node (internal 'RAM', variable length) */
- lrn = (struct mib_list_rootnode *)node;
- ln = lrn->head;
- /* iterate over list, head to tail */
- while ((ln != NULL) && (ln->objid != *ident))
- {
- ln = ln->next;
- }
- if (ln != NULL)
- {
- /* found it, proceed to child */;
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident));
- if (ln->nptr == NULL)
- {
- np->ident_len = ident_len;
- np->ident = ident;
- return (struct mib_node*)lrn;
- }
- else
- {
- /* follow next child pointer */
- ident_len--;
- ident++;
- node = ln->nptr;
- }
- }
- else
- {
- /* search failed */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed *ident==%"S32_F"\n",*ident));
- return NULL;
- }
- }
- else
- {
- /* search failed, short object identifier (nosuchname) */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed, short object identifier\n"));
- return NULL;
- }
- }
- else if(node_type == MIB_NODE_EX)
- {
- struct mib_external_node *en;
- u16_t i, len;
-
- if (ident_len > 0)
- {
- /* external node (addressing and access via functions) */
- en = (struct mib_external_node *)node;
-
- i = 0;
- len = en->level_length(en->addr_inf,ext_level);
- while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) != 0))
- {
- i++;
- }
- if (i < len)
- {
- s32_t debug_id;
-
- en->get_objid(en->addr_inf,ext_level,i,&debug_id);
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid==%"S32_F" *ident==%"S32_F"\n",debug_id,*ident));
- if ((ext_level + 1) == en->tree_levels)
- {
- np->ident_len = ident_len;
- np->ident = ident;
- return (struct mib_node*)en;
- }
- else
- {
- /* found it, proceed to child */
- ident_len--;
- ident++;
- ext_level++;
- }
- }
- else
- {
- /* search failed */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed *ident==%"S32_F"\n",*ident));
- return NULL;
- }
- }
- else
- {
- /* search failed, short object identifier (nosuchname) */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed, short object identifier\n"));
- return NULL;
- }
- }
- else if (node_type == MIB_NODE_SC)
- {
- mib_scalar_node *sn;
-
- sn = (mib_scalar_node *)node;
- if ((ident_len == 1) && (*ident == 0))
- {
- np->ident_len = ident_len;
- np->ident = ident;
- return (struct mib_node*)sn;
- }
- else
- {
- /* search failed, short object identifier (nosuchname) */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, invalid object identifier length\n"));
- return NULL;
- }
- }
- else
- {
- /* unknown node_type */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node_type %"U16_F" unkown\n",(u16_t)node_type));
- return NULL;
- }
- }
- /* done, found nothing */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node==%p\n",(void*)node));
- return NULL;
-}
-
-/**
- * Test table for presence of at least one table entry.
- */
-static u8_t
-empty_table(struct mib_node *node)
-{
- u8_t node_type;
- u8_t empty = 0;
-
- if (node != NULL)
- {
- node_type = node->node_type;
- if (node_type == MIB_NODE_LR)
- {
- struct mib_list_rootnode *lrn;
- lrn = (struct mib_list_rootnode *)node;
- if ((lrn->count == 0) || (lrn->head == NULL))
- {
- empty = 1;
- }
- }
- else if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA))
- {
- struct mib_array_node *an;
- an = (struct mib_array_node *)node;
- if ((an->maxlength == 0) || (an->nptr == NULL))
- {
- empty = 1;
- }
- }
- else if (node_type == MIB_NODE_EX)
- {
- struct mib_external_node *en;
- en = (struct mib_external_node *)node;
- if (en->tree_levels == 0)
- {
- empty = 1;
- }
- }
- }
- return empty;
-}
-
-/**
- * Tree expansion.
- */
-struct mib_node *
-snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret)
-{
- u8_t node_type, ext_level, climb_tree;
-
- ext_level = 0;
- /* reset node stack */
- node_stack_cnt = 0;
- while (node != NULL)
- {
- climb_tree = 0;
- node_type = node->node_type;
- if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA))
- {
- struct mib_array_node *an;
- u16_t i;
-
- /* array node (internal ROM or RAM, fixed length) */
- an = (struct mib_array_node *)node;
- if (ident_len > 0)
- {
- i = 0;
- while ((i < an->maxlength) && (an->objid[i] < *ident))
- {
- i++;
- }
- if (i < an->maxlength)
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident));
- /* add identifier to oidret */
- oidret->id[oidret->len] = an->objid[i];
- (oidret->len)++;
-
- if (an->nptr[i] == NULL)
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n"));
- /* leaf node (e.g. in a fixed size table) */
- if (an->objid[i] > *ident)
- {
- return (struct mib_node*)an;
- }
- else if ((i + 1) < an->maxlength)
- {
- /* an->objid[i] == *ident */
- (oidret->len)--;
- oidret->id[oidret->len] = an->objid[i + 1];
- (oidret->len)++;
- return (struct mib_node*)an;
- }
- else
- {
- /* (i + 1) == an->maxlength */
- (oidret->len)--;
- climb_tree = 1;
- }
- }
- else
- {
- u8_t j;
- struct nse cur_node;
-
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n"));
- /* non-leaf, store right child ptr and id */
- LWIP_ASSERT("i < 0xff", i < 0xff);
- j = (u8_t)i + 1;
- while ((j < an->maxlength) && (empty_table(an->nptr[j])))
- {
- j++;
- }
- if (j < an->maxlength)
- {
- cur_node.r_ptr = an->nptr[j];
- cur_node.r_id = an->objid[j];
- cur_node.r_nl = 0;
- }
- else
- {
- cur_node.r_ptr = NULL;
- }
- push_node(&cur_node);
- if (an->objid[i] == *ident)
- {
- ident_len--;
- ident++;
- }
- else
- {
- /* an->objid[i] < *ident */
- ident_len = 0;
- }
- /* follow next child pointer */
- node = an->nptr[i];
- }
- }
- else
- {
- /* i == an->maxlength */
- climb_tree = 1;
- }
- }
- else
- {
- u8_t j;
- /* ident_len == 0, complete with leftmost '.thing' */
- j = 0;
- while ((j < an->maxlength) && empty_table(an->nptr[j]))
- {
- j++;
- }
- if (j < an->maxlength)
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("left an->objid[j]==%"S32_F"\n",an->objid[j]));
- oidret->id[oidret->len] = an->objid[j];
- (oidret->len)++;
- if (an->nptr[j] == NULL)
- {
- /* leaf node */
- return (struct mib_node*)an;
- }
- else
- {
- /* no leaf, continue */
- node = an->nptr[j];
- }
- }
- else
- {
- /* j == an->maxlength */
- climb_tree = 1;
- }
- }
- }
- else if(node_type == MIB_NODE_LR)
- {
- struct mib_list_rootnode *lrn;
- struct mib_list_node *ln;
-
- /* list root node (internal 'RAM', variable length) */
- lrn = (struct mib_list_rootnode *)node;
- if (ident_len > 0)
- {
- ln = lrn->head;
- /* iterate over list, head to tail */
- while ((ln != NULL) && (ln->objid < *ident))
- {
- ln = ln->next;
- }
- if (ln != NULL)
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident));
- oidret->id[oidret->len] = ln->objid;
- (oidret->len)++;
- if (ln->nptr == NULL)
- {
- /* leaf node */
- if (ln->objid > *ident)
- {
- return (struct mib_node*)lrn;
- }
- else if (ln->next != NULL)
- {
- /* ln->objid == *ident */
- (oidret->len)--;
- oidret->id[oidret->len] = ln->next->objid;
- (oidret->len)++;
- return (struct mib_node*)lrn;
- }
- else
- {
- /* ln->next == NULL */
- (oidret->len)--;
- climb_tree = 1;
- }
- }
- else
- {
- struct mib_list_node *jn;
- struct nse cur_node;
-
- /* non-leaf, store right child ptr and id */
- jn = ln->next;
- while ((jn != NULL) && empty_table(jn->nptr))
- {
- jn = jn->next;
- }
- if (jn != NULL)
- {
- cur_node.r_ptr = jn->nptr;
- cur_node.r_id = jn->objid;
- cur_node.r_nl = 0;
- }
- else
- {
- cur_node.r_ptr = NULL;
- }
- push_node(&cur_node);
- if (ln->objid == *ident)
- {
- ident_len--;
- ident++;
- }
- else
- {
- /* ln->objid < *ident */
- ident_len = 0;
- }
- /* follow next child pointer */
- node = ln->nptr;
- }
-
- }
- else
- {
- /* ln == NULL */
- climb_tree = 1;
- }
- }
- else
- {
- struct mib_list_node *jn;
- /* ident_len == 0, complete with leftmost '.thing' */
- jn = lrn->head;
- while ((jn != NULL) && empty_table(jn->nptr))
- {
- jn = jn->next;
- }
- if (jn != NULL)
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("left jn->objid==%"S32_F"\n",jn->objid));
- oidret->id[oidret->len] = jn->objid;
- (oidret->len)++;
- if (jn->nptr == NULL)
- {
- /* leaf node */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("jn->nptr == NULL\n"));
- return (struct mib_node*)lrn;
- }
- else
- {
- /* no leaf, continue */
- node = jn->nptr;
- }
- }
- else
- {
- /* jn == NULL */
- climb_tree = 1;
- }
- }
- }
- else if(node_type == MIB_NODE_EX)
- {
- struct mib_external_node *en;
- s32_t ex_id;
-
- /* external node (addressing and access via functions) */
- en = (struct mib_external_node *)node;
- if (ident_len > 0)
- {
- u16_t i, len;
-
- i = 0;
- len = en->level_length(en->addr_inf,ext_level);
- while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) < 0))
- {
- i++;
- }
- if (i < len)
- {
- /* add identifier to oidret */
- en->get_objid(en->addr_inf,ext_level,i,&ex_id);
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,ex_id,*ident));
- oidret->id[oidret->len] = ex_id;
- (oidret->len)++;
-
- if ((ext_level + 1) == en->tree_levels)
- {
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n"));
- /* leaf node */
- if (ex_id > *ident)
- {
- return (struct mib_node*)en;
- }
- else if ((i + 1) < len)
- {
- /* ex_id == *ident */
- en->get_objid(en->addr_inf,ext_level,i + 1,&ex_id);
- (oidret->len)--;
- oidret->id[oidret->len] = ex_id;
- (oidret->len)++;
- return (struct mib_node*)en;
- }
- else
- {
- /* (i + 1) == len */
- (oidret->len)--;
- climb_tree = 1;
- }
- }
- else
- {
- u8_t j;
- struct nse cur_node;
-
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n"));
- /* non-leaf, store right child ptr and id */
- LWIP_ASSERT("i < 0xff", i < 0xff);
- j = (u8_t)i + 1;
- if (j < len)
- {
- /* right node is the current external node */
- cur_node.r_ptr = node;
- en->get_objid(en->addr_inf,ext_level,j,&cur_node.r_id);
- cur_node.r_nl = ext_level + 1;
- }
- else
- {
- cur_node.r_ptr = NULL;
- }
- push_node(&cur_node);
- if (en->ident_cmp(en->addr_inf,ext_level,i,*ident) == 0)
- {
- ident_len--;
- ident++;
- }
- else
- {
- /* external id < *ident */
- ident_len = 0;
- }
- /* proceed to child */
- ext_level++;
- }
- }
- else
- {
- /* i == len (en->level_len()) */
- climb_tree = 1;
- }
- }
- else
- {
- /* ident_len == 0, complete with leftmost '.thing' */
- en->get_objid(en->addr_inf,ext_level,0,&ex_id);
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("left en->objid==%"S32_F"\n",ex_id));
- oidret->id[oidret->len] = ex_id;
- (oidret->len)++;
- if ((ext_level + 1) == en->tree_levels)
- {
- /* leaf node */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("(ext_level + 1) == en->tree_levels\n"));
- return (struct mib_node*)en;
- }
- else
- {
- /* no leaf, proceed to child */
- ext_level++;
- }
- }
- }
- else if(node_type == MIB_NODE_SC)
- {
- mib_scalar_node *sn;
-
- /* scalar node */
- sn = (mib_scalar_node *)node;
- if (ident_len > 0)
- {
- /* at .0 */
- climb_tree = 1;
- }
- else
- {
- /* ident_len == 0, complete object identifier */
- oidret->id[oidret->len] = 0;
- (oidret->len)++;
- /* leaf node */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("completed scalar leaf\n"));
- return (struct mib_node*)sn;
- }
- }
- else
- {
- /* unknown/unhandled node_type */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node_type %"U16_F" unkown\n",(u16_t)node_type));
- return NULL;
- }
-
- if (climb_tree)
- {
- struct nse child;
-
- /* find right child ptr */
- child.r_ptr = NULL;
- child.r_id = 0;
- child.r_nl = 0;
- while ((node_stack_cnt > 0) && (child.r_ptr == NULL))
- {
- pop_node(&child);
- /* trim returned oid */
- (oidret->len)--;
- }
- if (child.r_ptr != NULL)
- {
- /* incoming ident is useless beyond this point */
- ident_len = 0;
- oidret->id[oidret->len] = child.r_id;
- oidret->len++;
- node = child.r_ptr;
- ext_level = child.r_nl;
- }
- else
- {
- /* tree ends here ... */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed, tree ends here\n"));
- return NULL;
- }
- }
- }
- /* done, found nothing */
- LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node==%p\n",(void*)node));
- return NULL;
-}
-
-/**
- * Test object identifier for the iso.org.dod.internet prefix.
- *
- * @param ident_len the length of the supplied object identifier
- * @param ident points to the array of sub identifiers
- * @return 1 if it matches, 0 otherwise
- */
-u8_t
-snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident)
-{
- if ((ident_len > 3) &&
- (ident[0] == 1) && (ident[1] == 3) &&
- (ident[2] == 6) && (ident[3] == 1))
- {
- return 1;
- }
- else
- {
- return 0;
- }
-}
-
-/**
- * Expands object identifier to the iso.org.dod.internet
- * prefix for use in getnext operation.
- *
- * @param ident_len the length of the supplied object identifier
- * @param ident points to the array of sub identifiers
- * @param oidret points to returned expanded object identifier
- * @return 1 if it matches, 0 otherwise
- *
- * @note ident_len 0 is allowed, expanding to the first known object id!!
- */
-u8_t
-snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret)
-{
- const s32_t *prefix_ptr;
- s32_t *ret_ptr;
- u8_t i;
-
- i = 0;
- prefix_ptr = &prefix[0];
- ret_ptr = &oidret->id[0];
- ident_len = ((ident_len < 4)?ident_len:4);
- while ((i < ident_len) && ((*ident) <= (*prefix_ptr)))
- {
- *ret_ptr++ = *prefix_ptr++;
- ident++;
- i++;
- }
- if (i == ident_len)
- {
- /* match, complete missing bits */
- while (i < 4)
- {
- *ret_ptr++ = *prefix_ptr++;
- i++;
- }
- oidret->len = i;
- return 1;
- }
- else
- {
- /* i != ident_len */
- return 0;
- }
-}
-
-#endif /* LWIP_SNMP */
diff --git a/lwip/src/core/snmp/msg_in.c b/lwip/src/core/snmp/msg_in.c
deleted file mode 100644
index be940c6..0000000
--- a/lwip/src/core/snmp/msg_in.c
+++ /dev/null
@@ -1,1453 +0,0 @@
-/**
- * @file
- * SNMP input message processing (RFC1157).
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/snmp.h"
-#include "lwip/snmp_asn1.h"
-#include "lwip/snmp_msg.h"
-#include "lwip/snmp_structs.h"
-#include "lwip/ip_addr.h"
-#include "lwip/memp.h"
-#include "lwip/udp.h"
-#include "lwip/stats.h"
-
-#include <string.h>
-
-/* public (non-static) constants */
-/** SNMP v1 == 0 */
-const s32_t snmp_version = 0;
-/** default SNMP community string */
-const char snmp_publiccommunity[7] = "public";
-
-/* statically allocated buffers for SNMP_CONCURRENT_REQUESTS */
-struct snmp_msg_pstat msg_input_list[SNMP_CONCURRENT_REQUESTS];
-/* UDP Protocol Control Block */
-struct udp_pcb *snmp1_pcb;
-
-static void snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
-static err_t snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
-static err_t snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
-
-
-/**
- * Starts SNMP Agent.
- * Allocates UDP pcb and binds it to IP_ADDR_ANY port 161.
- */
-void
-snmp_init(void)
-{
- struct snmp_msg_pstat *msg_ps;
- u8_t i;
-
- snmp1_pcb = udp_new();
- if (snmp1_pcb != NULL)
- {
- udp_recv(snmp1_pcb, snmp_recv, (void *)SNMP_IN_PORT);
- udp_bind(snmp1_pcb, IP_ADDR_ANY, SNMP_IN_PORT);
- }
- msg_ps = &msg_input_list[0];
- for (i=0; i<SNMP_CONCURRENT_REQUESTS; i++)
- {
- msg_ps->state = SNMP_MSG_EMPTY;
- msg_ps->error_index = 0;
- msg_ps->error_status = SNMP_ES_NOERROR;
- msg_ps++;
- }
- trap_msg.pcb = snmp1_pcb;
-
-#ifdef SNMP_PRIVATE_MIB_INIT
- /* If defined, this must be a function-like define to initialize the
- * private MIB after the stack has been initialized.
- * The private MIB can also be initialized in tcpip_callback (or after
- * the stack is initialized), this define is only for convenience. */
- SNMP_PRIVATE_MIB_INIT();
-#endif /* SNMP_PRIVATE_MIB_INIT */
-
- /* The coldstart trap will only be output
- if our outgoing interface is up & configured */
- snmp_coldstart_trap();
-}
-
-static void
-snmp_error_response(struct snmp_msg_pstat *msg_ps, u8_t error)
-{
- /* move names back from outvb to invb */
- int v;
- struct snmp_varbind *vbi = msg_ps->invb.head;
- struct snmp_varbind *vbo = msg_ps->outvb.head;
- for (v=0; v<msg_ps->vb_idx; v++) {
- vbi->ident_len = vbo->ident_len;
- vbo->ident_len = 0;
- vbi->ident = vbo->ident;
- vbo->ident = NULL;
- vbi = vbi->next;
- vbo = vbo->next;
- }
- /* free outvb */
- snmp_varbind_list_free(&msg_ps->outvb);
- /* we send invb back */
- msg_ps->outvb = msg_ps->invb;
- msg_ps->invb.head = NULL;
- msg_ps->invb.tail = NULL;
- msg_ps->invb.count = 0;
- msg_ps->error_status = error;
- /* error index must be 0 for error too big */
- msg_ps->error_index = (error != SNMP_ES_TOOBIG) ? (1 + msg_ps->vb_idx) : 0;
- snmp_send_response(msg_ps);
- snmp_varbind_list_free(&msg_ps->outvb);
- msg_ps->state = SNMP_MSG_EMPTY;
-}
-
-static void
-snmp_ok_response(struct snmp_msg_pstat *msg_ps)
-{
- err_t err_ret;
-
- err_ret = snmp_send_response(msg_ps);
- if (err_ret == ERR_MEM)
- {
- /* serious memory problem, can't return tooBig */
- }
- else
- {
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event = %"S32_F"\n",msg_ps->error_status));
- }
- /* free varbinds (if available) */
- snmp_varbind_list_free(&msg_ps->invb);
- snmp_varbind_list_free(&msg_ps->outvb);
- msg_ps->state = SNMP_MSG_EMPTY;
-}
-
-/**
- * Service an internal or external event for SNMP GET.
- *
- * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
- * @param msg_ps points to the assosicated message process state
- */
-static void
-snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
-{
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
-
- if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
- {
- struct mib_external_node *en;
- struct snmp_name_ptr np;
-
- /* get_object_def() answer*/
- en = msg_ps->ext_mib_node;
- np = msg_ps->ext_name_ptr;
-
- /* translate answer into a known lifeform */
- en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
- if ((msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) &&
- (msg_ps->ext_object_def.access & MIB_ACCESS_READ))
- {
- msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
- en->get_value_q(request_id, &msg_ps->ext_object_def);
- }
- else
- {
- en->get_object_def_pc(request_id, np.ident_len, np.ident);
- /* search failed, object id points to unknown object (nosuchname) */
- snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
- }
- }
- else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
- {
- struct mib_external_node *en;
- struct snmp_varbind *vb;
-
- /* get_value() answer */
- en = msg_ps->ext_mib_node;
-
- /* allocate output varbind */
- vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
- if (vb != NULL)
- {
- vb->next = NULL;
- vb->prev = NULL;
-
- /* move name from invb to outvb */
- vb->ident = msg_ps->vb_ptr->ident;
- vb->ident_len = msg_ps->vb_ptr->ident_len;
- /* ensure this memory is refereced once only */
- msg_ps->vb_ptr->ident = NULL;
- msg_ps->vb_ptr->ident_len = 0;
-
- vb->value_type = msg_ps->ext_object_def.asn_type;
- LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff);
- vb->value_len = (u8_t)msg_ps->ext_object_def.v_len;
- if (vb->value_len > 0)
- {
- LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE);
- vb->value = memp_malloc(MEMP_SNMP_VALUE);
- if (vb->value != NULL)
- {
- en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
- snmp_varbind_tail_add(&msg_ps->outvb, vb);
- /* search again (if vb_idx < msg_ps->invb.count) */
- msg_ps->state = SNMP_MSG_SEARCH_OBJ;
- msg_ps->vb_idx += 1;
- }
- else
- {
- en->get_value_pc(request_id, &msg_ps->ext_object_def);
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no variable space\n"));
- msg_ps->vb_ptr->ident = vb->ident;
- msg_ps->vb_ptr->ident_len = vb->ident_len;
- memp_free(MEMP_SNMP_VARBIND, vb);
- snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
- }
- }
- else
- {
- /* vb->value_len == 0, empty value (e.g. empty string) */
- en->get_value_a(request_id, &msg_ps->ext_object_def, 0, NULL);
- vb->value = NULL;
- snmp_varbind_tail_add(&msg_ps->outvb, vb);
- /* search again (if vb_idx < msg_ps->invb.count) */
- msg_ps->state = SNMP_MSG_SEARCH_OBJ;
- msg_ps->vb_idx += 1;
- }
- }
- else
- {
- en->get_value_pc(request_id, &msg_ps->ext_object_def);
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no outvb space\n"));
- snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
- }
- }
-
- while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
- (msg_ps->vb_idx < msg_ps->invb.count))
- {
- struct mib_node *mn;
- struct snmp_name_ptr np;
-
- if (msg_ps->vb_idx == 0)
- {
- msg_ps->vb_ptr = msg_ps->invb.head;
- }
- else
- {
- msg_ps->vb_ptr = msg_ps->vb_ptr->next;
- }
- /** test object identifier for .iso.org.dod.internet prefix */
- if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident))
- {
- mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
- msg_ps->vb_ptr->ident + 4, &np);
- if (mn != NULL)
- {
- if (mn->node_type == MIB_NODE_EX)
- {
- /* external object */
- struct mib_external_node *en = (struct mib_external_node*)mn;
-
- msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
- /* save en && args in msg_ps!! */
- msg_ps->ext_mib_node = en;
- msg_ps->ext_name_ptr = np;
-
- en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
- }
- else
- {
- /* internal object */
- struct obj_def object_def;
-
- msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
- mn->get_object_def(np.ident_len, np.ident, &object_def);
- if ((object_def.instance != MIB_OBJECT_NONE) &&
- (object_def.access & MIB_ACCESS_READ))
- {
- mn = mn;
- }
- else
- {
- /* search failed, object id points to unknown object (nosuchname) */
- mn = NULL;
- }
- if (mn != NULL)
- {
- struct snmp_varbind *vb;
-
- msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
- /* allocate output varbind */
- vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
- if (vb != NULL)
- {
- vb->next = NULL;
- vb->prev = NULL;
-
- /* move name from invb to outvb */
- vb->ident = msg_ps->vb_ptr->ident;
- vb->ident_len = msg_ps->vb_ptr->ident_len;
- /* ensure this memory is refereced once only */
- msg_ps->vb_ptr->ident = NULL;
- msg_ps->vb_ptr->ident_len = 0;
-
- vb->value_type = object_def.asn_type;
- LWIP_ASSERT("invalid length", object_def.v_len <= 0xff);
- vb->value_len = (u8_t)object_def.v_len;
- if (vb->value_len > 0)
- {
- LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low",
- vb->value_len <= SNMP_MAX_VALUE_SIZE);
- vb->value = memp_malloc(MEMP_SNMP_VALUE);
- if (vb->value != NULL)
- {
- mn->get_value(&object_def, vb->value_len, vb->value);
- snmp_varbind_tail_add(&msg_ps->outvb, vb);
- msg_ps->state = SNMP_MSG_SEARCH_OBJ;
- msg_ps->vb_idx += 1;
- }
- else
- {
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n"));
- msg_ps->vb_ptr->ident = vb->ident;
- msg_ps->vb_ptr->ident_len = vb->ident_len;
- vb->ident = NULL;
- vb->ident_len = 0;
- memp_free(MEMP_SNMP_VARBIND, vb);
- snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
- }
- }
- else
- {
- /* vb->value_len == 0, empty value (e.g. empty string) */
- vb->value = NULL;
- snmp_varbind_tail_add(&msg_ps->outvb, vb);
- msg_ps->state = SNMP_MSG_SEARCH_OBJ;
- msg_ps->vb_idx += 1;
- }
- }
- else
- {
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate outvb space\n"));
- snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
- }
- }
- }
- }
- }
- else
- {
- mn = NULL;
- }
- if (mn == NULL)
- {
- /* mn == NULL, noSuchName */
- snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
- }
- }
- if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
- (msg_ps->vb_idx == msg_ps->invb.count))
- {
- snmp_ok_response(msg_ps);
- }
-}
-
-/**
- * Service an internal or external event for SNMP GETNEXT.
- *
- * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
- * @param msg_ps points to the assosicated message process state
- */
-static void
-snmp_msg_getnext_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
-{
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
-
- if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
- {
- struct mib_external_node *en;
-
- /* get_object_def() answer*/
- en = msg_ps->ext_mib_node;
-
- /* translate answer into a known lifeform */
- en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def);
- if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
- {
- msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
- en->get_value_q(request_id, &msg_ps->ext_object_def);
- }
- else
- {
- en->get_object_def_pc(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1]);
- /* search failed, object id points to unknown object (nosuchname) */
- snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
- }
- }
- else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
- {
- struct mib_external_node *en;
- struct snmp_varbind *vb;
-
- /* get_value() answer */
- en = msg_ps->ext_mib_node;
-
- LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff);
- vb = snmp_varbind_alloc(&msg_ps->ext_oid,
- msg_ps->ext_object_def.asn_type,
- (u8_t)msg_ps->ext_object_def.v_len);
- if (vb != NULL)
- {
- en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
- snmp_varbind_tail_add(&msg_ps->outvb, vb);
- msg_ps->state = SNMP_MSG_SEARCH_OBJ;
- msg_ps->vb_idx += 1;
- }
- else
- {
- en->get_value_pc(request_id, &msg_ps->ext_object_def);
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: couldn't allocate outvb space\n"));
- snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
- }
- }
-
- while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
- (msg_ps->vb_idx < msg_ps->invb.count))
- {
- struct mib_node *mn;
- struct snmp_obj_id oid;
-
- if (msg_ps->vb_idx == 0)
- {
- msg_ps->vb_ptr = msg_ps->invb.head;
- }
- else
- {
- msg_ps->vb_ptr = msg_ps->vb_ptr->next;
- }
- if (snmp_iso_prefix_expand(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident, &oid))
- {
- if (msg_ps->vb_ptr->ident_len > 3)
- {
- /* can offset ident_len and ident */
- mn = snmp_expand_tree((struct mib_node*)&internet,
- msg_ps->vb_ptr->ident_len - 4,
- msg_ps->vb_ptr->ident + 4, &oid);
- }
- else
- {
- /* can't offset ident_len -4, ident + 4 */
- mn = snmp_expand_tree((struct mib_node*)&internet, 0, NULL, &oid);
- }
- }
- else
- {
- mn = NULL;
- }
- if (mn != NULL)
- {
- if (mn->node_type == MIB_NODE_EX)
- {
- /* external object */
- struct mib_external_node *en = (struct mib_external_node*)mn;
-
- msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
- /* save en && args in msg_ps!! */
- msg_ps->ext_mib_node = en;
- msg_ps->ext_oid = oid;
-
- en->get_object_def_q(en->addr_inf, request_id, 1, &oid.id[oid.len - 1]);
- }
- else
- {
- /* internal object */
- struct obj_def object_def;
- struct snmp_varbind *vb;
-
- msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
- mn->get_object_def(1, &oid.id[oid.len - 1], &object_def);
-
- LWIP_ASSERT("invalid length", object_def.v_len <= 0xff);
- vb = snmp_varbind_alloc(&oid, object_def.asn_type, (u8_t)object_def.v_len);
- if (vb != NULL)
- {
- msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
- mn->get_value(&object_def, object_def.v_len, vb->value);
- snmp_varbind_tail_add(&msg_ps->outvb, vb);
- msg_ps->state = SNMP_MSG_SEARCH_OBJ;
- msg_ps->vb_idx += 1;
- }
- else
- {
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space\n"));
- snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
- }
- }
- }
- if (mn == NULL)
- {
- /* mn == NULL, noSuchName */
- snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
- }
- }
- if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
- (msg_ps->vb_idx == msg_ps->invb.count))
- {
- snmp_ok_response(msg_ps);
- }
-}
-
-/**
- * Service an internal or external event for SNMP SET.
- *
- * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
- * @param msg_ps points to the assosicated message process state
- */
-static void
-snmp_msg_set_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
-{
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_set_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
-
- if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
- {
- struct mib_external_node *en;
- struct snmp_name_ptr np;
-
- /* get_object_def() answer*/
- en = msg_ps->ext_mib_node;
- np = msg_ps->ext_name_ptr;
-
- /* translate answer into a known lifeform */
- en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
- if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
- {
- msg_ps->state = SNMP_MSG_EXTERNAL_SET_TEST;
- en->set_test_q(request_id, &msg_ps->ext_object_def);
- }
- else
- {
- en->get_object_def_pc(request_id, np.ident_len, np.ident);
- /* search failed, object id points to unknown object (nosuchname) */
- snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
- }
- }
- else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_TEST)
- {
- struct mib_external_node *en;
-
- /* set_test() answer*/
- en = msg_ps->ext_mib_node;
-
- if (msg_ps->ext_object_def.access & MIB_ACCESS_WRITE)
- {
- if ((msg_ps->ext_object_def.asn_type == msg_ps->vb_ptr->value_type) &&
- (en->set_test_a(request_id,&msg_ps->ext_object_def,
- msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
- {
- msg_ps->state = SNMP_MSG_SEARCH_OBJ;
- msg_ps->vb_idx += 1;
- }
- else
- {
- en->set_test_pc(request_id,&msg_ps->ext_object_def);
- /* bad value */
- snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
- }
- }
- else
- {
- en->set_test_pc(request_id,&msg_ps->ext_object_def);
- /* object not available for set */
- snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
- }
- }
- else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF_S)
- {
- struct mib_external_node *en;
- struct snmp_name_ptr np;
-
- /* get_object_def() answer*/
- en = msg_ps->ext_mib_node;
- np = msg_ps->ext_name_ptr;
-
- /* translate answer into a known lifeform */
- en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
- if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
- {
- msg_ps->state = SNMP_MSG_EXTERNAL_SET_VALUE;
- en->set_value_q(request_id, &msg_ps->ext_object_def,
- msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
- }
- else
- {
- en->get_object_def_pc(request_id, np.ident_len, np.ident);
- /* set_value failed, object has disappeared for some odd reason?? */
- snmp_error_response(msg_ps,SNMP_ES_GENERROR);
- }
- }
- else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_VALUE)
- {
- struct mib_external_node *en;
-
- /** set_value_a() */
- en = msg_ps->ext_mib_node;
- en->set_value_a(request_id, &msg_ps->ext_object_def,
- msg_ps->vb_ptr->value_len, msg_ps->vb_ptr->value);
-
- /** @todo use set_value_pc() if toobig */
- msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
- msg_ps->vb_idx += 1;
- }
-
- /* test all values before setting */
- while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
- (msg_ps->vb_idx < msg_ps->invb.count))
- {
- struct mib_node *mn;
- struct snmp_name_ptr np;
-
- if (msg_ps->vb_idx == 0)
- {
- msg_ps->vb_ptr = msg_ps->invb.head;
- }
- else
- {
- msg_ps->vb_ptr = msg_ps->vb_ptr->next;
- }
- /** test object identifier for .iso.org.dod.internet prefix */
- if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident))
- {
- mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
- msg_ps->vb_ptr->ident + 4, &np);
- if (mn != NULL)
- {
- if (mn->node_type == MIB_NODE_EX)
- {
- /* external object */
- struct mib_external_node *en = (struct mib_external_node*)mn;
-
- msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
- /* save en && args in msg_ps!! */
- msg_ps->ext_mib_node = en;
- msg_ps->ext_name_ptr = np;
-
- en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
- }
- else
- {
- /* internal object */
- struct obj_def object_def;
-
- msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
- mn->get_object_def(np.ident_len, np.ident, &object_def);
- if (object_def.instance != MIB_OBJECT_NONE)
- {
- mn = mn;
- }
- else
- {
- /* search failed, object id points to unknown object (nosuchname) */
- mn = NULL;
- }
- if (mn != NULL)
- {
- msg_ps->state = SNMP_MSG_INTERNAL_SET_TEST;
-
- if (object_def.access & MIB_ACCESS_WRITE)
- {
- if ((object_def.asn_type == msg_ps->vb_ptr->value_type) &&
- (mn->set_test(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
- {
- msg_ps->state = SNMP_MSG_SEARCH_OBJ;
- msg_ps->vb_idx += 1;
- }
- else
- {
- /* bad value */
- snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
- }
- }
- else
- {
- /* object not available for set */
- snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
- }
- }
- }
- }
- }
- else
- {
- mn = NULL;
- }
- if (mn == NULL)
- {
- /* mn == NULL, noSuchName */
- snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
- }
- }
-
- if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
- (msg_ps->vb_idx == msg_ps->invb.count))
- {
- msg_ps->vb_idx = 0;
- msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
- }
-
- /* set all values "atomically" (be as "atomic" as possible) */
- while ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
- (msg_ps->vb_idx < msg_ps->invb.count))
- {
- struct mib_node *mn;
- struct snmp_name_ptr np;
-
- if (msg_ps->vb_idx == 0)
- {
- msg_ps->vb_ptr = msg_ps->invb.head;
- }
- else
- {
- msg_ps->vb_ptr = msg_ps->vb_ptr->next;
- }
- /* skip iso prefix test, was done previously while settesting() */
- mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
- msg_ps->vb_ptr->ident + 4, &np);
- /* check if object is still available
- (e.g. external hot-plug thingy present?) */
- if (mn != NULL)
- {
- if (mn->node_type == MIB_NODE_EX)
- {
- /* external object */
- struct mib_external_node *en = (struct mib_external_node*)mn;
-
- msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF_S;
- /* save en && args in msg_ps!! */
- msg_ps->ext_mib_node = en;
- msg_ps->ext_name_ptr = np;
-
- en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
- }
- else
- {
- /* internal object */
- struct obj_def object_def;
-
- msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF_S;
- mn->get_object_def(np.ident_len, np.ident, &object_def);
- msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
- mn->set_value(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
- msg_ps->vb_idx += 1;
- }
- }
- }
- if ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
- (msg_ps->vb_idx == msg_ps->invb.count))
- {
- /* simply echo the input if we can set it
- @todo do we need to return the actual value?
- e.g. if value is silently modified or behaves sticky? */
- msg_ps->outvb = msg_ps->invb;
- msg_ps->invb.head = NULL;
- msg_ps->invb.tail = NULL;
- msg_ps->invb.count = 0;
- snmp_ok_response(msg_ps);
- }
-}
-
-
-/**
- * Handle one internal or external event.
- * Called for one async event. (recv external/private answer)
- *
- * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
- */
-void
-snmp_msg_event(u8_t request_id)
-{
- struct snmp_msg_pstat *msg_ps;
-
- if (request_id < SNMP_CONCURRENT_REQUESTS)
- {
- msg_ps = &msg_input_list[request_id];
- if (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ)
- {
- snmp_msg_getnext_event(request_id, msg_ps);
- }
- else if (msg_ps->rt == SNMP_ASN1_PDU_GET_REQ)
- {
- snmp_msg_get_event(request_id, msg_ps);
- }
- else if(msg_ps->rt == SNMP_ASN1_PDU_SET_REQ)
- {
- snmp_msg_set_event(request_id, msg_ps);
- }
- }
-}
-
-
-/* lwIP UDP receive callback function */
-static void
-snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
-{
- struct snmp_msg_pstat *msg_ps;
- u8_t req_idx;
- err_t err_ret;
- u16_t payload_len = p->tot_len;
- u16_t payload_ofs = 0;
- u16_t varbind_ofs = 0;
-
- /* suppress unused argument warning */
- LWIP_UNUSED_ARG(arg);
-
- /* traverse input message process list, look for SNMP_MSG_EMPTY */
- msg_ps = &msg_input_list[0];
- req_idx = 0;
- while ((req_idx < SNMP_CONCURRENT_REQUESTS) && (msg_ps->state != SNMP_MSG_EMPTY))
- {
- req_idx++;
- msg_ps++;
- }
- if (req_idx == SNMP_CONCURRENT_REQUESTS)
- {
- /* exceeding number of concurrent requests */
- pbuf_free(p);
- return;
- }
-
- /* accepting request */
- snmp_inc_snmpinpkts();
- /* record used 'protocol control block' */
- msg_ps->pcb = pcb;
- /* source address (network order) */
- msg_ps->sip = *addr;
- /* source port (host order (lwIP oddity)) */
- msg_ps->sp = port;
-
- /* check total length, version, community, pdu type */
- err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps);
- /* Only accept requests and requests without error (be robust) */
- /* Reject response and trap headers or error requests as input! */
- if ((err_ret != ERR_OK) ||
- ((msg_ps->rt != SNMP_ASN1_PDU_GET_REQ) &&
- (msg_ps->rt != SNMP_ASN1_PDU_GET_NEXT_REQ) &&
- (msg_ps->rt != SNMP_ASN1_PDU_SET_REQ)) ||
- ((msg_ps->error_status != SNMP_ES_NOERROR) ||
- (msg_ps->error_index != 0)) )
- {
- /* header check failed drop request silently, do not return error! */
- pbuf_free(p);
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n"));
- return;
- }
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community));
-
- /* Builds a list of variable bindings. Copy the varbinds from the pbuf
- chain to glue them when these are divided over two or more pbuf's. */
- err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps);
- /* we've decoded the incoming message, release input msg now */
- pbuf_free(p);
- if ((err_ret != ERR_OK) || (msg_ps->invb.count == 0))
- {
- /* varbind-list decode failed, or varbind list empty.
- drop request silently, do not return error!
- (errors are only returned for a specific varbind failure) */
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n"));
- return;
- }
-
- msg_ps->error_status = SNMP_ES_NOERROR;
- msg_ps->error_index = 0;
- /* find object for each variable binding */
- msg_ps->state = SNMP_MSG_SEARCH_OBJ;
- /* first variable binding from list to inspect */
- msg_ps->vb_idx = 0;
-
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count));
-
- /* handle input event and as much objects as possible in one go */
- snmp_msg_event(req_idx);
-}
-
-/**
- * Checks and decodes incoming SNMP message header, logs header errors.
- *
- * @param p points to pbuf chain of SNMP message (UDP payload)
- * @param ofs points to first octet of SNMP message
- * @param pdu_len the length of the UDP payload
- * @param ofs_ret returns the ofset of the variable bindings
- * @param m_stat points to the current message request state return
- * @return
- * - ERR_OK SNMP header is sane and accepted
- * - ERR_ARG SNMP header is either malformed or rejected
- */
-static err_t
-snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
-{
- err_t derr;
- u16_t len, ofs_base;
- u8_t len_octets;
- u8_t type;
- s32_t version;
-
- ofs_base = ofs;
- snmp_asn1_dec_type(p, ofs, &type);
- derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
- if ((derr != ERR_OK) ||
- (pdu_len != (1 + len_octets + len)) ||
- (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
- {
- snmp_inc_snmpinasnparseerrs();
- return ERR_ARG;
- }
- ofs += (1 + len_octets);
- snmp_asn1_dec_type(p, ofs, &type);
- derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
- if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
- {
- /* can't decode or no integer (version) */
- snmp_inc_snmpinasnparseerrs();
- return ERR_ARG;
- }
- derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version);
- if (derr != ERR_OK)
- {
- /* can't decode */
- snmp_inc_snmpinasnparseerrs();
- return ERR_ARG;
- }
- if (version != 0)
- {
- /* not version 1 */
- snmp_inc_snmpinbadversions();
- return ERR_ARG;
- }
- ofs += (1 + len_octets + len);
- snmp_asn1_dec_type(p, ofs, &type);
- derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
- if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)))
- {
- /* can't decode or no octet string (community) */
- snmp_inc_snmpinasnparseerrs();
- return ERR_ARG;
- }
- derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community);
- if (derr != ERR_OK)
- {
- snmp_inc_snmpinasnparseerrs();
- return ERR_ARG;
- }
- /* add zero terminator */
- len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN));
- m_stat->community[len] = 0;
- m_stat->com_strlen = (u8_t)len;
- if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0)
- {
- /** @todo: move this if we need to check more names */
- snmp_inc_snmpinbadcommunitynames();
- snmp_authfail_trap();
- return ERR_ARG;
- }
- ofs += (1 + len_octets + len);
- snmp_asn1_dec_type(p, ofs, &type);
- derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
- if (derr != ERR_OK)
- {
- snmp_inc_snmpinasnparseerrs();
- return ERR_ARG;
- }
- switch(type)
- {
- case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ):
- /* GetRequest PDU */
- snmp_inc_snmpingetrequests();
- derr = ERR_OK;
- break;
- case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ):
- /* GetNextRequest PDU */
- snmp_inc_snmpingetnexts();
- derr = ERR_OK;
- break;
- case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP):
- /* GetResponse PDU */
- snmp_inc_snmpingetresponses();
- derr = ERR_ARG;
- break;
- case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ):
- /* SetRequest PDU */
- snmp_inc_snmpinsetrequests();
- derr = ERR_OK;
- break;
- case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP):
- /* Trap PDU */
- snmp_inc_snmpintraps();
- derr = ERR_ARG;
- break;
- default:
- snmp_inc_snmpinasnparseerrs();
- derr = ERR_ARG;
- break;
- }
- if (derr != ERR_OK)
- {
- /* unsupported input PDU for this agent (no parse error) */
- return ERR_ARG;
- }
- m_stat->rt = type & 0x1F;
- ofs += (1 + len_octets);
- if (len != (pdu_len - (ofs - ofs_base)))
- {
- /* decoded PDU length does not equal actual payload length */
- snmp_inc_snmpinasnparseerrs();
- return ERR_ARG;
- }
- snmp_asn1_dec_type(p, ofs, &type);
- derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
- if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
- {
- /* can't decode or no integer (request ID) */
- snmp_inc_snmpinasnparseerrs();
- return ERR_ARG;
- }
- derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid);
- if (derr != ERR_OK)
- {
- /* can't decode */
- snmp_inc_snmpinasnparseerrs();
- return ERR_ARG;
- }
- ofs += (1 + len_octets + len);
- snmp_asn1_dec_type(p, ofs, &type);
- derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
- if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
- {
- /* can't decode or no integer (error-status) */
- snmp_inc_snmpinasnparseerrs();
- return ERR_ARG;
- }
- /* must be noError (0) for incoming requests.
- log errors for mib-2 completeness and for debug purposes */
- derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status);
- if (derr != ERR_OK)
- {
- /* can't decode */
- snmp_inc_snmpinasnparseerrs();
- return ERR_ARG;
- }
- switch (m_stat->error_status)
- {
- case SNMP_ES_TOOBIG:
- snmp_inc_snmpintoobigs();
- break;
- case SNMP_ES_NOSUCHNAME:
- snmp_inc_snmpinnosuchnames();
- break;
- case SNMP_ES_BADVALUE:
- snmp_inc_snmpinbadvalues();
- break;
- case SNMP_ES_READONLY:
- snmp_inc_snmpinreadonlys();
- break;
- case SNMP_ES_GENERROR:
- snmp_inc_snmpingenerrs();
- break;
- }
- ofs += (1 + len_octets + len);
- snmp_asn1_dec_type(p, ofs, &type);
- derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
- if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
- {
- /* can't decode or no integer (error-index) */
- snmp_inc_snmpinasnparseerrs();
- return ERR_ARG;
- }
- /* must be 0 for incoming requests.
- decode anyway to catch bad integers (and dirty tricks) */
- derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index);
- if (derr != ERR_OK)
- {
- /* can't decode */
- snmp_inc_snmpinasnparseerrs();
- return ERR_ARG;
- }
- ofs += (1 + len_octets + len);
- *ofs_ret = ofs;
- return ERR_OK;
-}
-
-static err_t
-snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
-{
- err_t derr;
- u16_t len, vb_len;
- u8_t len_octets;
- u8_t type;
-
- /* variable binding list */
- snmp_asn1_dec_type(p, ofs, &type);
- derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len);
- if ((derr != ERR_OK) ||
- (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
- {
- snmp_inc_snmpinasnparseerrs();
- return ERR_ARG;
- }
- ofs += (1 + len_octets);
-
- /* start with empty list */
- m_stat->invb.count = 0;
- m_stat->invb.head = NULL;
- m_stat->invb.tail = NULL;
-
- while (vb_len > 0)
- {
- struct snmp_obj_id oid, oid_value;
- struct snmp_varbind *vb;
-
- snmp_asn1_dec_type(p, ofs, &type);
- derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
- if ((derr != ERR_OK) ||
- (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) ||
- (len == 0) || (len > vb_len))
- {
- snmp_inc_snmpinasnparseerrs();
- /* free varbinds (if available) */
- snmp_varbind_list_free(&m_stat->invb);
- return ERR_ARG;
- }
- ofs += (1 + len_octets);
- vb_len -= (1 + len_octets);
-
- snmp_asn1_dec_type(p, ofs, &type);
- derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
- if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)))
- {
- /* can't decode object name length */
- snmp_inc_snmpinasnparseerrs();
- /* free varbinds (if available) */
- snmp_varbind_list_free(&m_stat->invb);
- return ERR_ARG;
- }
- derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid);
- if (derr != ERR_OK)
- {
- /* can't decode object name */
- snmp_inc_snmpinasnparseerrs();
- /* free varbinds (if available) */
- snmp_varbind_list_free(&m_stat->invb);
- return ERR_ARG;
- }
- ofs += (1 + len_octets + len);
- vb_len -= (1 + len_octets + len);
-
- snmp_asn1_dec_type(p, ofs, &type);
- derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
- if (derr != ERR_OK)
- {
- /* can't decode object value length */
- snmp_inc_snmpinasnparseerrs();
- /* free varbinds (if available) */
- snmp_varbind_list_free(&m_stat->invb);
- return ERR_ARG;
- }
-
- switch (type)
- {
- case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
- vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t));
- if (vb != NULL)
- {
- s32_t *vptr = (s32_t*)vb->value;
-
- derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr);
- snmp_varbind_tail_add(&m_stat->invb, vb);
- }
- else
- {
- derr = ERR_ARG;
- }
- break;
- case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
- case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
- case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
- vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t));
- if (vb != NULL)
- {
- u32_t *vptr = (u32_t*)vb->value;
-
- derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr);
- snmp_varbind_tail_add(&m_stat->invb, vb);
- }
- else
- {
- derr = ERR_ARG;
- }
- break;
- case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
- case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
- LWIP_ASSERT("invalid length", len <= 0xff);
- vb = snmp_varbind_alloc(&oid, type, (u8_t)len);
- if (vb != NULL)
- {
- derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
- snmp_varbind_tail_add(&m_stat->invb, vb);
- }
- else
- {
- derr = ERR_ARG;
- }
- break;
- case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
- vb = snmp_varbind_alloc(&oid, type, 0);
- if (vb != NULL)
- {
- snmp_varbind_tail_add(&m_stat->invb, vb);
- derr = ERR_OK;
- }
- else
- {
- derr = ERR_ARG;
- }
- break;
- case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
- derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value);
- if (derr == ERR_OK)
- {
- vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t));
- if (vb != NULL)
- {
- u8_t i = oid_value.len;
- s32_t *vptr = (s32_t*)vb->value;
-
- while(i > 0)
- {
- i--;
- vptr[i] = oid_value.id[i];
- }
- snmp_varbind_tail_add(&m_stat->invb, vb);
- derr = ERR_OK;
- }
- else
- {
- derr = ERR_ARG;
- }
- }
- break;
- case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
- if (len == 4)
- {
- /* must be exactly 4 octets! */
- vb = snmp_varbind_alloc(&oid, type, 4);
- if (vb != NULL)
- {
- derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
- snmp_varbind_tail_add(&m_stat->invb, vb);
- }
- else
- {
- derr = ERR_ARG;
- }
- }
- else
- {
- derr = ERR_ARG;
- }
- break;
- default:
- derr = ERR_ARG;
- break;
- }
- if (derr != ERR_OK)
- {
- snmp_inc_snmpinasnparseerrs();
- /* free varbinds (if available) */
- snmp_varbind_list_free(&m_stat->invb);
- return ERR_ARG;
- }
- ofs += (1 + len_octets + len);
- vb_len -= (1 + len_octets + len);
- }
-
- if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ)
- {
- snmp_add_snmpintotalsetvars(m_stat->invb.count);
- }
- else
- {
- snmp_add_snmpintotalreqvars(m_stat->invb.count);
- }
-
- *ofs_ret = ofs;
- return ERR_OK;
-}
-
-struct snmp_varbind*
-snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len)
-{
- struct snmp_varbind *vb;
-
- vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
- if (vb != NULL)
- {
- u8_t i;
-
- vb->next = NULL;
- vb->prev = NULL;
- i = oid->len;
- vb->ident_len = i;
- if (i > 0)
- {
- LWIP_ASSERT("SNMP_MAX_TREE_DEPTH is configured too low", i <= SNMP_MAX_TREE_DEPTH);
- /* allocate array of s32_t for our object identifier */
- vb->ident = (s32_t*)memp_malloc(MEMP_SNMP_VALUE);
- if (vb->ident == NULL)
- {
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate ident value space\n"));
- memp_free(MEMP_SNMP_VARBIND, vb);
- return NULL;
- }
- while(i > 0)
- {
- i--;
- vb->ident[i] = oid->id[i];
- }
- }
- else
- {
- /* i == 0, pass zero length object identifier */
- vb->ident = NULL;
- }
- vb->value_type = type;
- vb->value_len = len;
- if (len > 0)
- {
- LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE);
- /* allocate raw bytes for our object value */
- vb->value = memp_malloc(MEMP_SNMP_VALUE);
- if (vb->value == NULL)
- {
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate value space\n"));
- if (vb->ident != NULL)
- {
- memp_free(MEMP_SNMP_VALUE, vb->ident);
- }
- memp_free(MEMP_SNMP_VARBIND, vb);
- return NULL;
- }
- }
- else
- {
- /* ASN1_NUL type, or zero length ASN1_OC_STR */
- vb->value = NULL;
- }
- }
- else
- {
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate varbind space\n"));
- }
- return vb;
-}
-
-void
-snmp_varbind_free(struct snmp_varbind *vb)
-{
- if (vb->value != NULL )
- {
- memp_free(MEMP_SNMP_VALUE, vb->value);
- }
- if (vb->ident != NULL )
- {
- memp_free(MEMP_SNMP_VALUE, vb->ident);
- }
- memp_free(MEMP_SNMP_VARBIND, vb);
-}
-
-void
-snmp_varbind_list_free(struct snmp_varbind_root *root)
-{
- struct snmp_varbind *vb, *prev;
-
- vb = root->tail;
- while ( vb != NULL )
- {
- prev = vb->prev;
- snmp_varbind_free(vb);
- vb = prev;
- }
- root->count = 0;
- root->head = NULL;
- root->tail = NULL;
-}
-
-void
-snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb)
-{
- if (root->count == 0)
- {
- /* add first varbind to list */
- root->head = vb;
- root->tail = vb;
- }
- else
- {
- /* add nth varbind to list tail */
- root->tail->next = vb;
- vb->prev = root->tail;
- root->tail = vb;
- }
- root->count += 1;
-}
-
-struct snmp_varbind*
-snmp_varbind_tail_remove(struct snmp_varbind_root *root)
-{
- struct snmp_varbind* vb;
-
- if (root->count > 0)
- {
- /* remove tail varbind */
- vb = root->tail;
- root->tail = vb->prev;
- vb->prev->next = NULL;
- root->count -= 1;
- }
- else
- {
- /* nothing to remove */
- vb = NULL;
- }
- return vb;
-}
-
-#endif /* LWIP_SNMP */
diff --git a/lwip/src/core/snmp/msg_out.c b/lwip/src/core/snmp/msg_out.c
deleted file mode 100644
index fc0807c..0000000
--- a/lwip/src/core/snmp/msg_out.c
+++ /dev/null
@@ -1,678 +0,0 @@
-/**
- * @file
- * SNMP output message processing (RFC1157).
- *
- * Output responses and traps are build in two passes:
- *
- * Pass 0: iterate over the output message backwards to determine encoding lengths
- * Pass 1: the actual forward encoding of internal form into ASN1
- *
- * The single-pass encoding method described by Comer & Stevens
- * requires extra buffer space and copying for reversal of the packet.
- * The buffer requirement can be prohibitively large for big payloads
- * (>= 484) therefore we use the two encoding passes.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#include "lwip/opt.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/udp.h"
-#include "lwip/netif.h"
-#include "lwip/snmp.h"
-#include "lwip/snmp_asn1.h"
-#include "lwip/snmp_msg.h"
-
-struct snmp_trap_dst
-{
- /* destination IP address in network order */
- ip_addr_t dip;
- /* set to 0 when disabled, >0 when enabled */
- u8_t enable;
-};
-struct snmp_trap_dst trap_dst[SNMP_TRAP_DESTINATIONS];
-
-/** TRAP message structure */
-struct snmp_msg_trap trap_msg;
-
-static u16_t snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len);
-static u16_t snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len);
-static u16_t snmp_varbind_list_sum(struct snmp_varbind_root *root);
-
-static u16_t snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p);
-static u16_t snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p);
-static u16_t snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs);
-
-/**
- * Sets enable switch for this trap destination.
- * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
- * @param enable switch if 0 destination is disabled >0 enabled.
- */
-void
-snmp_trap_dst_enable(u8_t dst_idx, u8_t enable)
-{
- if (dst_idx < SNMP_TRAP_DESTINATIONS)
- {
- trap_dst[dst_idx].enable = enable;
- }
-}
-
-/**
- * Sets IPv4 address for this trap destination.
- * @param dst_idx index in 0 .. SNMP_TRAP_DESTINATIONS-1
- * @param dst IPv4 address in host order.
- */
-void
-snmp_trap_dst_ip_set(u8_t dst_idx, ip_addr_t *dst)
-{
- if (dst_idx < SNMP_TRAP_DESTINATIONS)
- {
- ip_addr_set(&trap_dst[dst_idx].dip, dst);
- }
-}
-
-/**
- * Sends a 'getresponse' message to the request originator.
- *
- * @param m_stat points to the current message request state source
- * @return ERR_OK when success, ERR_MEM if we're out of memory
- *
- * @note the caller is responsible for filling in outvb in the m_stat
- * and provide error-status and index (except for tooBig errors) ...
- */
-err_t
-snmp_send_response(struct snmp_msg_pstat *m_stat)
-{
- struct snmp_varbind_root emptyvb = {NULL, NULL, 0, 0, 0};
- struct pbuf *p;
- u16_t tot_len;
- err_t err;
-
- /* pass 0, calculate length fields */
- tot_len = snmp_varbind_list_sum(&m_stat->outvb);
- tot_len = snmp_resp_header_sum(m_stat, tot_len);
-
- /* try allocating pbuf(s) for complete response */
- p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL);
- if (p == NULL)
- {
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() tooBig\n"));
-
- /* can't construct reply, return error-status tooBig */
- m_stat->error_status = SNMP_ES_TOOBIG;
- m_stat->error_index = 0;
- /* pass 0, recalculate lengths, for empty varbind-list */
- tot_len = snmp_varbind_list_sum(&emptyvb);
- tot_len = snmp_resp_header_sum(m_stat, tot_len);
- /* retry allocation once for header and empty varbind-list */
- p = pbuf_alloc(PBUF_TRANSPORT, tot_len, PBUF_POOL);
- }
- if (p != NULL)
- {
- /* first pbuf alloc try or retry alloc success */
- u16_t ofs;
-
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() p != NULL\n"));
-
- /* pass 1, size error, encode packet ino the pbuf(s) */
- ofs = snmp_resp_header_enc(m_stat, p);
- snmp_varbind_list_enc(&m_stat->outvb, p, ofs);
-
- switch (m_stat->error_status)
- {
- case SNMP_ES_TOOBIG:
- snmp_inc_snmpouttoobigs();
- break;
- case SNMP_ES_NOSUCHNAME:
- snmp_inc_snmpoutnosuchnames();
- break;
- case SNMP_ES_BADVALUE:
- snmp_inc_snmpoutbadvalues();
- break;
- case SNMP_ES_GENERROR:
- snmp_inc_snmpoutgenerrs();
- break;
- }
- snmp_inc_snmpoutgetresponses();
- snmp_inc_snmpoutpkts();
-
- /** @todo do we need separate rx and tx pcbs for threaded case? */
- /** connect to the originating source */
- udp_connect(m_stat->pcb, &m_stat->sip, m_stat->sp);
- err = udp_send(m_stat->pcb, p);
- if (err == ERR_MEM)
- {
- /** @todo release some memory, retry and return tooBig? tooMuchHassle? */
- err = ERR_MEM;
- }
- else
- {
- err = ERR_OK;
- }
- /** disassociate remote address and port with this pcb */
- udp_disconnect(m_stat->pcb);
-
- pbuf_free(p);
- LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_snd_response() done\n"));
- return err;
- }
- else
- {
- /* first pbuf alloc try or retry alloc failed
- very low on memory, couldn't return tooBig */
- return ERR_MEM;
- }
-}
-
-
-/**
- * Sends an generic or enterprise specific trap message.
- *
- * @param generic_trap is the trap code
- * @param eoid points to enterprise object identifier
- * @param specific_trap used for enterprise traps when generic_trap == 6
- * @return ERR_OK when success, ERR_MEM if we're out of memory
- *
- * @note the caller is responsible for filling in outvb in the trap_msg
- * @note the use of the enterpise identifier field
- * is per RFC1215.
- * Use .iso.org.dod.internet.mgmt.mib-2.snmp for generic traps
- * and .iso.org.dod.internet.private.enterprises.yourenterprise
- * (sysObjectID) for specific traps.
- */
-err_t
-snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap)
-{
- struct snmp_trap_dst *td;
- struct netif *dst_if;
- ip_addr_t dst_ip;
- struct pbuf *p;
- u16_t i,tot_len;
- 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 lenght for encoding the response header
- */
-static u16_t
-snmp_resp_header_sum(struct snmp_msg_pstat *m_stat, u16_t vb_len)
-{
- u16_t tot_len;
- struct snmp_resp_header_lengths *rhl;
-
- rhl = &m_stat->rhl;
- tot_len = vb_len;
- snmp_asn1_enc_s32t_cnt(m_stat->error_index, &rhl->erridxlen);
- snmp_asn1_enc_length_cnt(rhl->erridxlen, &rhl->erridxlenlen);
- tot_len += 1 + rhl->erridxlenlen + rhl->erridxlen;
-
- snmp_asn1_enc_s32t_cnt(m_stat->error_status, &rhl->errstatlen);
- snmp_asn1_enc_length_cnt(rhl->errstatlen, &rhl->errstatlenlen);
- tot_len += 1 + rhl->errstatlenlen + rhl->errstatlen;
-
- snmp_asn1_enc_s32t_cnt(m_stat->rid, &rhl->ridlen);
- snmp_asn1_enc_length_cnt(rhl->ridlen, &rhl->ridlenlen);
- tot_len += 1 + rhl->ridlenlen + rhl->ridlen;
-
- rhl->pdulen = tot_len;
- snmp_asn1_enc_length_cnt(rhl->pdulen, &rhl->pdulenlen);
- tot_len += 1 + rhl->pdulenlen;
-
- rhl->comlen = m_stat->com_strlen;
- snmp_asn1_enc_length_cnt(rhl->comlen, &rhl->comlenlen);
- tot_len += 1 + rhl->comlenlen + rhl->comlen;
-
- snmp_asn1_enc_s32t_cnt(snmp_version, &rhl->verlen);
- snmp_asn1_enc_length_cnt(rhl->verlen, &rhl->verlenlen);
- tot_len += 1 + rhl->verlen + rhl->verlenlen;
-
- rhl->seqlen = tot_len;
- snmp_asn1_enc_length_cnt(rhl->seqlen, &rhl->seqlenlen);
- tot_len += 1 + rhl->seqlenlen;
-
- return tot_len;
-}
-
-/**
- * Sums trap header field lengths from tail to head and
- * returns trap_header_lengths for second encoding pass.
- *
- * @param vb_len varbind-list length
- * @param thl points to returned header lengths
- * @return the required lenght for encoding the trap header
- */
-static u16_t
-snmp_trap_header_sum(struct snmp_msg_trap *m_trap, u16_t vb_len)
-{
- u16_t tot_len;
- struct snmp_trap_header_lengths *thl;
-
- thl = &m_trap->thl;
- tot_len = vb_len;
-
- snmp_asn1_enc_u32t_cnt(m_trap->ts, &thl->tslen);
- snmp_asn1_enc_length_cnt(thl->tslen, &thl->tslenlen);
- tot_len += 1 + thl->tslen + thl->tslenlen;
-
- snmp_asn1_enc_s32t_cnt(m_trap->spc_trap, &thl->strplen);
- snmp_asn1_enc_length_cnt(thl->strplen, &thl->strplenlen);
- tot_len += 1 + thl->strplen + thl->strplenlen;
-
- snmp_asn1_enc_s32t_cnt(m_trap->gen_trap, &thl->gtrplen);
- snmp_asn1_enc_length_cnt(thl->gtrplen, &thl->gtrplenlen);
- tot_len += 1 + thl->gtrplen + thl->gtrplenlen;
-
- thl->aaddrlen = 4;
- snmp_asn1_enc_length_cnt(thl->aaddrlen, &thl->aaddrlenlen);
- tot_len += 1 + thl->aaddrlen + thl->aaddrlenlen;
-
- snmp_asn1_enc_oid_cnt(m_trap->enterprise->len, &m_trap->enterprise->id[0], &thl->eidlen);
- snmp_asn1_enc_length_cnt(thl->eidlen, &thl->eidlenlen);
- tot_len += 1 + thl->eidlen + thl->eidlenlen;
-
- thl->pdulen = tot_len;
- snmp_asn1_enc_length_cnt(thl->pdulen, &thl->pdulenlen);
- tot_len += 1 + thl->pdulenlen;
-
- thl->comlen = sizeof(snmp_publiccommunity) - 1;
- snmp_asn1_enc_length_cnt(thl->comlen, &thl->comlenlen);
- tot_len += 1 + thl->comlenlen + thl->comlen;
-
- snmp_asn1_enc_s32t_cnt(snmp_version, &thl->verlen);
- snmp_asn1_enc_length_cnt(thl->verlen, &thl->verlenlen);
- tot_len += 1 + thl->verlen + thl->verlenlen;
-
- thl->seqlen = tot_len;
- snmp_asn1_enc_length_cnt(thl->seqlen, &thl->seqlenlen);
- tot_len += 1 + thl->seqlenlen;
-
- return tot_len;
-}
-
-/**
- * Sums varbind lengths from tail to head and
- * annotates lengths in varbind for second encoding pass.
- *
- * @param root points to the root of the variable binding list
- * @return the required lenght for encoding the variable bindings
- */
-static u16_t
-snmp_varbind_list_sum(struct snmp_varbind_root *root)
-{
- struct snmp_varbind *vb;
- u32_t *uint_ptr;
- s32_t *sint_ptr;
- u16_t tot_len;
-
- tot_len = 0;
- vb = root->tail;
- while ( vb != NULL )
- {
- /* encoded value lenght depends on type */
- switch (vb->value_type)
- {
- case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
- sint_ptr = (s32_t*)vb->value;
- snmp_asn1_enc_s32t_cnt(*sint_ptr, &vb->vlen);
- break;
- case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
- case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
- case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
- uint_ptr = (u32_t*)vb->value;
- snmp_asn1_enc_u32t_cnt(*uint_ptr, &vb->vlen);
- break;
- case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
- case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
- case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
- case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
- vb->vlen = vb->value_len;
- break;
- case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
- sint_ptr = (s32_t*)vb->value;
- snmp_asn1_enc_oid_cnt(vb->value_len / sizeof(s32_t), sint_ptr, &vb->vlen);
- break;
- default:
- /* unsupported type */
- vb->vlen = 0;
- break;
- };
- /* encoding length of value length field */
- snmp_asn1_enc_length_cnt(vb->vlen, &vb->vlenlen);
- snmp_asn1_enc_oid_cnt(vb->ident_len, vb->ident, &vb->olen);
- snmp_asn1_enc_length_cnt(vb->olen, &vb->olenlen);
-
- vb->seqlen = 1 + vb->vlenlen + vb->vlen;
- vb->seqlen += 1 + vb->olenlen + vb->olen;
- snmp_asn1_enc_length_cnt(vb->seqlen, &vb->seqlenlen);
-
- /* varbind seq */
- tot_len += 1 + vb->seqlenlen + vb->seqlen;
-
- vb = vb->prev;
- }
-
- /* varbind-list seq */
- root->seqlen = tot_len;
- snmp_asn1_enc_length_cnt(root->seqlen, &root->seqlenlen);
- tot_len += 1 + root->seqlenlen;
-
- return tot_len;
-}
-
-/**
- * Encodes response header from head to tail.
- */
-static u16_t
-snmp_resp_header_enc(struct snmp_msg_pstat *m_stat, struct pbuf *p)
-{
- u16_t ofs;
-
- ofs = 0;
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, m_stat->rhl.seqlen);
- ofs += m_stat->rhl.seqlenlen;
-
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, m_stat->rhl.verlen);
- ofs += m_stat->rhl.verlenlen;
- snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.verlen, snmp_version);
- ofs += m_stat->rhl.verlen;
-
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, m_stat->rhl.comlen);
- ofs += m_stat->rhl.comlenlen;
- snmp_asn1_enc_raw(p, ofs, m_stat->rhl.comlen, m_stat->community);
- ofs += m_stat->rhl.comlen;
-
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, m_stat->rhl.pdulen);
- ofs += m_stat->rhl.pdulenlen;
-
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, m_stat->rhl.ridlen);
- ofs += m_stat->rhl.ridlenlen;
- snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.ridlen, m_stat->rid);
- ofs += m_stat->rhl.ridlen;
-
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, m_stat->rhl.errstatlen);
- ofs += m_stat->rhl.errstatlenlen;
- snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.errstatlen, m_stat->error_status);
- ofs += m_stat->rhl.errstatlen;
-
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, m_stat->rhl.erridxlen);
- ofs += m_stat->rhl.erridxlenlen;
- snmp_asn1_enc_s32t(p, ofs, m_stat->rhl.erridxlen, m_stat->error_index);
- ofs += m_stat->rhl.erridxlen;
-
- return ofs;
-}
-
-/**
- * Encodes trap header from head to tail.
- */
-static u16_t
-snmp_trap_header_enc(struct snmp_msg_trap *m_trap, struct pbuf *p)
-{
- u16_t ofs;
-
- ofs = 0;
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, m_trap->thl.seqlen);
- ofs += m_trap->thl.seqlenlen;
-
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, m_trap->thl.verlen);
- ofs += m_trap->thl.verlenlen;
- snmp_asn1_enc_s32t(p, ofs, m_trap->thl.verlen, snmp_version);
- ofs += m_trap->thl.verlen;
-
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, m_trap->thl.comlen);
- ofs += m_trap->thl.comlenlen;
- snmp_asn1_enc_raw(p, ofs, m_trap->thl.comlen, (u8_t *)&snmp_publiccommunity[0]);
- ofs += m_trap->thl.comlen;
-
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, m_trap->thl.pdulen);
- ofs += m_trap->thl.pdulenlen;
-
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, m_trap->thl.eidlen);
- ofs += m_trap->thl.eidlenlen;
- snmp_asn1_enc_oid(p, ofs, m_trap->enterprise->len, &m_trap->enterprise->id[0]);
- ofs += m_trap->thl.eidlen;
-
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, m_trap->thl.aaddrlen);
- ofs += m_trap->thl.aaddrlenlen;
- snmp_asn1_enc_raw(p, ofs, m_trap->thl.aaddrlen, &m_trap->sip_raw[0]);
- ofs += m_trap->thl.aaddrlen;
-
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, m_trap->thl.gtrplen);
- ofs += m_trap->thl.gtrplenlen;
- snmp_asn1_enc_u32t(p, ofs, m_trap->thl.gtrplen, m_trap->gen_trap);
- ofs += m_trap->thl.gtrplen;
-
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, m_trap->thl.strplen);
- ofs += m_trap->thl.strplenlen;
- snmp_asn1_enc_u32t(p, ofs, m_trap->thl.strplen, m_trap->spc_trap);
- ofs += m_trap->thl.strplen;
-
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, m_trap->thl.tslen);
- ofs += m_trap->thl.tslenlen;
- snmp_asn1_enc_u32t(p, ofs, m_trap->thl.tslen, m_trap->ts);
- ofs += m_trap->thl.tslen;
-
- return ofs;
-}
-
-/**
- * Encodes varbind list from head to tail.
- */
-static u16_t
-snmp_varbind_list_enc(struct snmp_varbind_root *root, struct pbuf *p, u16_t ofs)
-{
- struct snmp_varbind *vb;
- s32_t *sint_ptr;
- u32_t *uint_ptr;
- u8_t *raw_ptr;
-
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, root->seqlen);
- ofs += root->seqlenlen;
-
- vb = root->head;
- while ( vb != NULL )
- {
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, vb->seqlen);
- ofs += vb->seqlenlen;
-
- snmp_asn1_enc_type(p, ofs, (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID));
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, vb->olen);
- ofs += vb->olenlen;
- snmp_asn1_enc_oid(p, ofs, vb->ident_len, &vb->ident[0]);
- ofs += vb->olen;
-
- snmp_asn1_enc_type(p, ofs, vb->value_type);
- ofs += 1;
- snmp_asn1_enc_length(p, ofs, vb->vlen);
- ofs += vb->vlenlen;
-
- switch (vb->value_type)
- {
- case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
- sint_ptr = (s32_t*)vb->value;
- snmp_asn1_enc_s32t(p, ofs, vb->vlen, *sint_ptr);
- break;
- case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
- case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
- case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
- uint_ptr = (u32_t*)vb->value;
- snmp_asn1_enc_u32t(p, ofs, vb->vlen, *uint_ptr);
- break;
- case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
- case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
- case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
- raw_ptr = (u8_t*)vb->value;
- snmp_asn1_enc_raw(p, ofs, vb->vlen, raw_ptr);
- break;
- case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
- break;
- case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
- sint_ptr = (s32_t*)vb->value;
- snmp_asn1_enc_oid(p, ofs, vb->value_len / sizeof(s32_t), sint_ptr);
- break;
- default:
- /* unsupported type */
- break;
- };
- ofs += vb->vlen;
- vb = vb->next;
- }
- return ofs;
-}
-
-#endif /* LWIP_SNMP */
diff --git a/lwip/src/core/stats.c b/lwip/src/core/stats.c
index 06fbe0f..34e9b27 100644
--- a/lwip/src/core/stats.c
+++ b/lwip/src/core/stats.c
@@ -6,9 +6,9 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -17,21 +17,21 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
@@ -43,24 +43,16 @@
#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)
+void
+stats_init(void)
{
#ifdef LWIP_DEBUG
-#if MEMP_STATS
- const char * memp_names[] = {
-#define LWIP_MEMPOOL(name,num,size,desc) desc,
-#include "lwip/memp_std.h"
- };
- int i;
- for (i = 0; i < MEMP_MAX; i++) {
- lwip_stats.memp[i].name = memp_names[i];
- }
-#endif /* MEMP_STATS */
#if MEM_STATS
lwip_stats.mem.name = "MEM";
#endif /* MEM_STATS */
@@ -72,63 +64,59 @@ 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));
+ 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
+#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(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit));
+ LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv));
+ LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop));
+ LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr));
+ LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr));
+ LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr));
+ LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr));
+ LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1));
LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n\t", igmp->rx_group));
LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n\t", igmp->rx_general));
- LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report));
- LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join));
- LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave));
- LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n\t", igmp->tx_report));
+ 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 */
+#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));
+ LWIP_PLATFORM_DIAG(("avail: %"MEM_SIZE_F"\n\t", mem->avail));
+ LWIP_PLATFORM_DIAG(("used: %"MEM_SIZE_F"\n\t", mem->used));
+ LWIP_PLATFORM_DIAG(("max: %"MEM_SIZE_F"\n\t", mem->max));
+ LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n", mem->err));
}
#if MEMP_STATS
void
-stats_display_memp(struct stats_mem *mem, int index)
+stats_display_memp(struct stats_mem *mem, int idx)
{
- char * memp_names[] = {
-#define LWIP_MEMPOOL(name,num,size,desc) desc,
-#include "lwip/memp_std.h"
- };
- if(index < MEMP_MAX) {
- stats_display_mem(mem, memp_names[index]);
+ if (idx < MEMP_MAX) {
+ stats_display_mem(mem, mem->name);
}
}
#endif /* MEMP_STATS */
@@ -139,15 +127,15 @@ void
stats_display_sys(struct stats_sys *sys)
{
LWIP_PLATFORM_DIAG(("\nSYS\n\t"));
- LWIP_PLATFORM_DIAG(("sem.used: %"U32_F"\n\t", (u32_t)sys->sem.used));
- LWIP_PLATFORM_DIAG(("sem.max: %"U32_F"\n\t", (u32_t)sys->sem.max));
- LWIP_PLATFORM_DIAG(("sem.err: %"U32_F"\n\t", (u32_t)sys->sem.err));
- LWIP_PLATFORM_DIAG(("mutex.used: %"U32_F"\n\t", (u32_t)sys->mutex.used));
- LWIP_PLATFORM_DIAG(("mutex.max: %"U32_F"\n\t", (u32_t)sys->mutex.max));
- LWIP_PLATFORM_DIAG(("mutex.err: %"U32_F"\n\t", (u32_t)sys->mutex.err));
- LWIP_PLATFORM_DIAG(("mbox.used: %"U32_F"\n\t", (u32_t)sys->mbox.used));
- LWIP_PLATFORM_DIAG(("mbox.max: %"U32_F"\n\t", (u32_t)sys->mbox.max));
- LWIP_PLATFORM_DIAG(("mbox.err: %"U32_F"\n\t", (u32_t)sys->mbox.err));
+ LWIP_PLATFORM_DIAG(("sem.used: %"STAT_COUNTER_F"\n\t", sys->sem.used));
+ LWIP_PLATFORM_DIAG(("sem.max: %"STAT_COUNTER_F"\n\t", sys->sem.max));
+ LWIP_PLATFORM_DIAG(("sem.err: %"STAT_COUNTER_F"\n\t", sys->sem.err));
+ LWIP_PLATFORM_DIAG(("mutex.used: %"STAT_COUNTER_F"\n\t", sys->mutex.used));
+ LWIP_PLATFORM_DIAG(("mutex.max: %"STAT_COUNTER_F"\n\t", sys->mutex.max));
+ LWIP_PLATFORM_DIAG(("mutex.err: %"STAT_COUNTER_F"\n\t", sys->mutex.err));
+ LWIP_PLATFORM_DIAG(("mbox.used: %"STAT_COUNTER_F"\n\t", sys->mbox.used));
+ LWIP_PLATFORM_DIAG(("mbox.max: %"STAT_COUNTER_F"\n\t", sys->mbox.max));
+ LWIP_PLATFORM_DIAG(("mbox.err: %"STAT_COUNTER_F"\n", sys->mbox.err));
}
#endif /* SYS_STATS */
diff --git a/lwip/src/core/sys.c b/lwip/src/core/sys.c
index f177737..7059b4d 100644
--- a/lwip/src/core/sys.c
+++ b/lwip/src/core/sys.c
@@ -36,6 +36,44 @@
*
*/
+/**
+ * @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"
diff --git a/lwip/src/core/tcp.c b/lwip/src/core/tcp.c
index 55496d0..610ff66 100644
--- a/lwip/src/core/tcp.c
+++ b/lwip/src/core/tcp.c
@@ -1,18 +1,23 @@
/**
* @file
* Transmission Control Protocol for IP
+ * See also @ref tcp_raw
*
- * This file contains common functions for the TCP implementation, such as functinos
- * for manipulating the data structures and the TCP timer functions. TCP functions
- * related to input and output is found in tcp_in.c and tcp_out.c respectively.
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -21,21 +26,21 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
@@ -47,9 +52,8 @@
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
-#include "lwip/snmp.h"
#include "lwip/tcp.h"
-#include "lwip/tcp_impl.h"
+#include "lwip/priv/tcp_priv.h"
#include "lwip/debug.h"
#include "lwip/stats.h"
#include "lwip/ip6.h"
@@ -58,12 +62,16 @@
#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) (((port) & ~TCP_LOCAL_PORT_RANGE_START) + TCP_LOCAL_PORT_RANGE_START)
+#define TCP_ENSURE_LOCAL_PORT_RANGE(port) ((u16_t)(((port) & (u16_t)~TCP_LOCAL_PORT_RANGE_START) + TCP_LOCAL_PORT_RANGE_START))
#endif
#if LWIP_TCP_KEEPALIVE
@@ -74,18 +82,25 @@
#define TCP_KEEP_INTVL(pcb) TCP_KEEPINTVL_DEFAULT
#endif /* LWIP_TCP_KEEPALIVE */
-const char * const tcp_state_str[] = {
- "CLOSED",
- "LISTEN",
- "SYN_SENT",
- "SYN_RCVD",
- "ESTABLISHED",
- "FIN_WAIT_1",
- "FIN_WAIT_2",
- "CLOSE_WAIT",
- "CLOSING",
- "LAST_ACK",
- "TIME_WAIT"
+/* 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 */
@@ -93,10 +108,10 @@ static u16_t tcp_port = TCP_LOCAL_PORT_RANGE_START;
/* Incremented every coarse grained timer shot (typically every 500 ms). */
u32_t tcp_ticks;
-const u8_t tcp_backoff[13] =
- { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7};
- /* Times per slowtmr hits */
-const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 };
+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. */
@@ -110,31 +125,29 @@ struct tcp_pcb *tcp_active_pcbs;
/** List of all TCP PCBs in TIME-WAIT state */
struct tcp_pcb *tcp_tw_pcbs;
-#define NUM_TCP_PCB_LISTS 4
-#define NUM_TCP_PCB_LISTS_NO_TIME_WAIT 3
/** An array with all (non-temporary) PCB lists, mainly used for smaller code size */
-struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs,
- &tcp_active_pcbs, &tcp_tw_pcbs};
-
-/** Only used for temporary storage. */
-struct tcp_pcb *tcp_tmp_pcb;
+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() */
+/** 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)
+#ifdef LWIP_RAND
tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
-#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */
+#endif /* LWIP_RAND */
}
/**
@@ -147,12 +160,92 @@ tcp_tmr(void)
tcp_fasttmr();
if (++tcp_timer & 1) {
- /* Call tcp_tmr() every 500 ms, i.e., every other timer
+ /* 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);
+ tcp_set_flags(pcb, 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--;
+ tcp_clear_flags(pcb, 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
@@ -172,18 +265,16 @@ tcp_tmr(void)
static err_t
tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
{
- err_t err;
-
if (rst_on_unacked_data && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) {
- if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) {
+ 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, PCB_ISIPV6(pcb));
+ tcp_rst(pcb, 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);
@@ -193,84 +284,105 @@ tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
TCP_REG(&tcp_tw_pcbs, pcb);
} else {
/* CLOSE_WAIT: deallocate the pcb since we already sent a RST for it */
- memp_free(MEMP_TCP_PCB, pcb);
+ 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. */
- err = ERR_OK;
- if (pcb->local_port != 0 || pcb->bound_to_netif) {
- TCP_RMV(&tcp_bound_pcbs, pcb);
- }
- memp_free(MEMP_TCP_PCB, pcb);
- pcb = NULL;
- break;
- case LISTEN:
- err = ERR_OK;
- tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb);
- memp_free(MEMP_TCP_PCB_LISTEN, pcb);
- pcb = NULL;
- break;
- case SYN_SENT:
- err = ERR_OK;
- TCP_PCB_REMOVE_ACTIVE(pcb);
- memp_free(MEMP_TCP_PCB, pcb);
- pcb = NULL;
- snmp_inc_tcpattemptfails();
- break;
- case SYN_RCVD:
- err = tcp_send_fin(pcb);
- if (err == ERR_OK) {
- snmp_inc_tcpattemptfails();
- pcb->state = FIN_WAIT_1;
- }
- break;
- case ESTABLISHED:
- err = tcp_send_fin(pcb);
- if (err == ERR_OK) {
- snmp_inc_tcpestabresets();
- pcb->state = FIN_WAIT_1;
- }
- break;
- case CLOSE_WAIT:
- err = tcp_send_fin(pcb);
- if (err == ERR_OK) {
- snmp_inc_tcpestabresets();
- pcb->state = LAST_ACK;
- }
- break;
- default:
- /* Has already been closed, do nothing. */
- err = ERR_OK;
- pcb = NULL;
- break;
+ 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 || pcb->bound_to_netif) {
+ 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;
}
- if (pcb != NULL && err == ERR_OK) {
+ 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. */
- /* @todo: When implementing SO_LINGER, this must be changed somehow:
- If SOF_LINGER is set, the data should be sent and acked before close returns.
- This can only be valid for sequential APIs, not for the raw API. */
tcp_output(pcb);
+ } else if (err == ERR_MEM) {
+ /* Mark this pcb for closing. Closing is retried from tcp_tmr. */
+ tcp_set_flags(pcb, TF_CLOSEPEND);
+ /* We have to return ERR_OK from here to indicate to the callers that this
+ pcb should not be used any more as it will be freed soon via tcp_tmr.
+ This is OK here since sending FIN does not guarantee a time frime for
+ actually freeing the pcb, either (it is left in closure states for
+ remote ACK or timeout) */
+ return ERR_OK;
}
return err;
}
/**
+ * @ingroup tcp_raw
* Closes the connection held by the PCB.
*
* Listening pcbs are freed and may not be referenced any more.
@@ -287,24 +399,23 @@ tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
err_t
tcp_close(struct tcp_pcb *pcb)
{
-#if TCP_DEBUG
LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in "));
tcp_debug_print_state(pcb->state);
-#endif /* TCP_DEBUG */
if (pcb->state != LISTEN) {
/* Set a flag not to receive any more data... */
- pcb->flags |= TF_RXCLOSED;
+ tcp_set_flags(pcb, 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.
+ * Shutting down both sides is the same as calling tcp_close, so if it succeds
+ * (i.e. returns ER_OK), the PCB must not be referenced any more!
*
* @param pcb PCB to shutdown
* @param shut_rx shut down receive side if this is != 0
@@ -320,7 +431,7 @@ tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx)
}
if (shut_rx) {
/* shut down the receive side: set a flag not to receive any more data... */
- pcb->flags |= TF_RXCLOSED;
+ tcp_set_flags(pcb, 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);
@@ -335,14 +446,14 @@ tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx)
/* This can't happen twice since if it succeeds, the pcb's state is changed.
Only close in these states as the others directly deallocate the PCB */
switch (pcb->state) {
- case SYN_RCVD:
- case ESTABLISHED:
- case CLOSE_WAIT:
- return tcp_close_shutdown(pcb, shut_rx);
- default:
- /* Not (yet?) connected, cannot shutdown the TX side as that would bring us
- into CLOSED state, where the PCB is deallocated. */
- return ERR_CONN;
+ 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;
@@ -360,14 +471,14 @@ void
tcp_abandon(struct tcp_pcb *pcb, int reset)
{
u32_t seqno, ackno;
-#if LWIP_CALLBACK_API
+#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);
+ 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. */
@@ -375,35 +486,49 @@ tcp_abandon(struct tcp_pcb *pcb, int reset)
tcp_pcb_remove(&tcp_tw_pcbs, pcb);
memp_free(MEMP_TCP_PCB, pcb);
} else {
- int send_rst = reset && (pcb->state != CLOSED);
+ 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;
- TCP_PCB_REMOVE_ACTIVE(pcb);
+ 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 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, pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb));
+ tcp_rst(pcb, 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(errf, errf_arg, ERR_ABRT);
+ 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.
*
@@ -420,13 +545,14 @@ tcp_abort(struct tcp_pcb *pcb)
}
/**
- * Binds the connection to a local portnumber and IP address. If the
+ * @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 IP_ADDR_ANY to bind
+ * @param ipaddr the local ip address to bind to (use IPx_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
@@ -434,11 +560,26 @@ tcp_abort(struct tcp_pcb *pcb)
* ERR_OK if bound
*/
err_t
-tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
+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_IPV6 && LWIP_IPV6_SCOPES
+ ip_addr_t zoned_ipaddr;
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
+
+#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);
@@ -453,31 +594,43 @@ tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
}
#endif /* SO_REUSE */
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+ /* If the given IP address should have a zone but doesn't, assign one now.
+ * This is legacy support: scope-aware callers should always provide properly
+ * zoned source addresses. Do the zone selection before the address-in-use
+ * check below; as such we have to make a temporary copy of the address. */
+ if (IP_IS_V6(ipaddr) && ip6_addr_lacks_zone(ip_2_ip6(ipaddr), IP6_UNICAST)) {
+ ip_addr_copy(zoned_ipaddr, *ipaddr);
+ ip6_addr_select_zone(ip_2_ip6(&zoned_ipaddr), ip_2_ip6(&zoned_ipaddr));
+ ipaddr = &zoned_ipaddr;
+ }
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
+
if (port == 0) {
port = tcp_new_port();
if (port == 0) {
return ERR_BUF;
}
- }
-
- /* Check if the address already is in use (on all lists) */
- for (i = 0; i < max_pcb_list; i++) {
- for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
- if (cpcb->local_port == port) {
+ } 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))
+ /* Omit checking for the same port if both pcbs have REUSEADDR set.
+ For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in
+ tcp_connect. */
+ if (!ip_get_option(pcb, SOF_REUSEADDR) ||
+ !ip_get_option(cpcb, SOF_REUSEADDR))
#endif /* SO_REUSE */
- {
- /* @todo: check accept_any_ip_version */
- if (IP_PCB_IPVER_EQ(pcb, cpcb) &&
- (ipX_addr_isany(PCB_ISIPV6(pcb), &cpcb->local_ip) ||
- ipX_addr_isany(PCB_ISIPV6(pcb), ip_2_ipX(ipaddr)) ||
- ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->local_ip, ip_2_ipX(ipaddr)))) {
- return ERR_USE;
+ {
+ /* @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;
+ }
}
}
}
@@ -485,8 +638,8 @@ tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
}
pcb->bound_to_netif = 0;
- if (!ipX_addr_isany(PCB_ISIPV6(pcb), ip_2_ipX(ipaddr))) {
- ipX_addr_set(PCB_ISIPV6(pcb), &pcb->local_ip, ip_2_ipX(ipaddr));
+ if (!ip_addr_isany(ipaddr)) {
+ ip_addr_set(&pcb->local_ip, ipaddr);
}
pcb->local_port = port;
TCP_REG(&tcp_bound_pcbs, pcb);
@@ -497,28 +650,55 @@ tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
err_t
tcp_bind_to_netif(struct tcp_pcb *pcb, const char ifname[3])
{
- LWIP_ERROR("tcp_bind_if: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
+ int i;
+ struct tcp_pcb *cpcb;
- /* Check if the interface is already in use */
- for (int i = 0; i < NUM_TCP_PCB_LISTS; i++) {
- for(struct tcp_pcb *cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
- if (IP_PCB_IPVER_EQ(pcb, cpcb) &&
- cpcb->bound_to_netif &&
- !memcmp(cpcb->local_netif, ifname, sizeof(cpcb->local_netif))) {
+ LWIP_ERROR("tcp_bind_to_netif: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_VAL);
+
+ /* Check if the interface is already in use (in tcp_listen_pcbs and tcp_bound_pcbs) */
+ for (i = 0; i < 2; i++) {
+ for (cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
+ if (cpcb->bound_to_netif &&
+ !memcmp(cpcb->local_netif, ifname, sizeof(cpcb->local_netif)) && (
+ IP_IS_ANY_TYPE_VAL(pcb->local_ip) || IP_IS_ANY_TYPE_VAL(cpcb->local_ip) ||
+ IP_GET_TYPE(&pcb->local_ip) == IP_GET_TYPE(&cpcb->local_ip))) {
return ERR_USE;
}
}
}
pcb->bound_to_netif = 1;
- ipX_addr_set_any(PCB_ISIPV6(pcb), &pcb->local_ip);
+ if (!IP_IS_ANY_TYPE_VAL(pcb->local_ip)) {
+ ip_addr_set_any(IP_IS_V6(&pcb->local_ip), &pcb->local_ip);
+ }
pcb->local_port = 0;
memcpy(pcb->local_netif, ifname, sizeof(pcb->local_netif));
TCP_REG(&tcp_bound_pcbs, pcb);
LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind_to_netif: bind to interface %c%c%c\n", ifname[0], ifname[1], ifname[2]));
+ // should call tcp_bind_netif() to set pcb->netif_idx
return ERR_OK;
}
+/**
+ * @ingroup tcp_raw
+ * Binds the connection to a netif and IP address.
+ * After calling this function, all packets received via this PCB
+ * are guaranteed to have come in via the specified netif, and all
+ * outgoing packets will go out via the specified netif.
+ *
+ * @param pcb the tcp_pcb to bind.
+ * @param netif the netif to bind to. Can be NULL.
+ */
+void
+tcp_bind_netif(struct tcp_pcb *pcb, const struct netif *netif)
+{
+ if (netif != NULL) {
+ pcb->netif_idx = netif_get_index(netif);
+ } else {
+ pcb->netif_idx = NETIF_NO_INDEX;
+ }
+}
+
#if LWIP_CALLBACK_API
/**
* Default accept callback if no accept callback is specified by the user.
@@ -527,14 +707,16 @@ static err_t
tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err)
{
LWIP_UNUSED_ARG(arg);
- LWIP_UNUSED_ARG(pcb);
LWIP_UNUSED_ARG(err);
+ 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
@@ -546,39 +728,65 @@ tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err)
*
* @note The original tcp_pcb is freed. This function therefore has to be
* called like this:
- * tpcb = tcp_listen(tpcb);
+ * tpcb = tcp_listen_with_backlog(tpcb, backlog);
*/
struct tcp_pcb *
tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
{
- struct tcp_pcb_listen *lpcb;
+ 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, return NULL);
+ LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, res = ERR_CLSD; goto done);
/* already listening? */
if (pcb->state == LISTEN) {
- return pcb;
+ lpcb = (struct tcp_pcb_listen *)pcb;
+ res = ERR_ALREADY;
+ goto done;
}
#if SO_REUSE
if (ip_get_option(pcb, SOF_REUSEADDR) && !pcb->have_local_netif) {
/* 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) {
+ for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
if ((lpcb->local_port == pcb->local_port) &&
- IP_PCB_IPVER_EQ(pcb, lpcb)) {
- if (ipX_addr_cmp(PCB_ISIPV6(pcb), &lpcb->local_ip, &pcb->local_ip)) {
- /* this address/port is already used */
- return NULL;
- }
+ 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) {
- return NULL;
+ res = ERR_MEM;
+ goto done;
}
lpcb->callback_arg = pcb->callback_arg;
lpcb->bound_to_netif = pcb->bound_to_netif;
@@ -587,14 +795,13 @@ tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
lpcb->state = LISTEN;
lpcb->prio = pcb->prio;
lpcb->so_options = pcb->so_options;
- ip_set_option(lpcb, SOF_ACCEPTCONN);
+ lpcb->netif_idx = NETIF_NO_INDEX;
lpcb->ttl = pcb->ttl;
lpcb->tos = pcb->tos;
-#if LWIP_IPV6
- PCB_ISIPV6(lpcb) = PCB_ISIPV6(pcb);
- lpcb->accept_any_ip_version = 0;
-#endif /* LWIP_IPV6 */
- ipX_addr_copy(PCB_ISIPV6(pcb), lpcb->local_ip, pcb->local_ip);
+#if 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 || pcb->bound_to_netif) {
TCP_RMV(&tcp_bound_pcbs, pcb);
}
@@ -604,33 +811,16 @@ tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
#endif /* LWIP_CALLBACK_API */
#if TCP_LISTEN_BACKLOG
lpcb->accepts_pending = 0;
- lpcb->backlog = (backlog ? backlog : 1);
+ tcp_backlog_set(lpcb, backlog);
#endif /* TCP_LISTEN_BACKLOG */
TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);
- return (struct tcp_pcb *)lpcb;
-}
-
-#if LWIP_IPV6
-/**
- * Same as tcp_listen_with_backlog, but allows to accept IPv4 and IPv6
- * connections, if the pcb's local address is set to ANY.
- */
-struct tcp_pcb *
-tcp_listen_dual_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
-{
- struct tcp_pcb *lpcb;
-
- lpcb = tcp_listen_with_backlog(pcb, backlog);
- if ((lpcb != NULL) &&
- ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) {
- /* The default behavior is to accept connections on either
- * IPv4 or IPv6, if not bound. */
- /* @see NETCONN_FLAG_IPV6_V6ONLY for changing this behavior */
- ((struct tcp_pcb_listen*)lpcb)->accept_any_ip_version = 1;
+ res = ERR_OK;
+done:
+ if (err != NULL) {
+ *err = res;
}
- return lpcb;
+ return (struct tcp_pcb *)lpcb;
}
-#endif /* LWIP_IPV6 */
/**
* Update the state that tracks the available window space to advertise.
@@ -638,7 +828,8 @@ tcp_listen_dual_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
* 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
+tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
{
u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd;
@@ -654,14 +845,17 @@ u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
} 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);
- pcb->rcv_ann_wnd = (u16_t)new_rcv_ann_wnd;
+#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.
@@ -672,17 +866,25 @@ u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
void
tcp_recved(struct tcp_pcb *pcb, u16_t len)
{
- int wnd_inflation;
+ u32_t wnd_inflation;
/* pcb->state LISTEN not allowed here */
LWIP_ASSERT("don't call tcp_recved for listen-pcbs",
- pcb->state != LISTEN);
- LWIP_ASSERT("tcp_recved: len would wrap rcv_wnd\n",
- len <= 0xffff - pcb->rcv_wnd );
-
- pcb->rcv_wnd += len;
- if (pcb->rcv_wnd > TCP_WND) {
- pcb->rcv_wnd = TCP_WND;
+ pcb->state != LISTEN);
+
+ pcb->rcv_wnd = (tcpwnd_size_t)(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);
@@ -696,8 +898,8 @@ tcp_recved(struct tcp_pcb *pcb, u16_t len)
tcp_output(pcb);
}
- LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: recveived %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n",
- len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd));
+ 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)));
}
/**
@@ -711,16 +913,18 @@ 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++;
+ 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) {
+ 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)) {
+ n++;
+ if (n > (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START)) {
return 0;
}
goto again;
@@ -731,50 +935,67 @@ again:
}
/**
+ * @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 (or on error)
+ * @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, ip_addr_t *ipaddr, u16_t port,
- tcp_connected_fn connected)
+tcp_connect(struct tcp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port,
+ tcp_connected_fn connected)
{
+ struct netif *netif = NULL;
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_ERROR("tcp_connect: cannot connect pcb bound to netif", !pcb->bound_to_netif, return ERR_VAL);
LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port));
- if (ipaddr != NULL) {
- ipX_addr_set(PCB_ISIPV6(pcb), &pcb->remote_ip, ip_2_ipX(ipaddr));
+ ip_addr_set(&pcb->remote_ip, ipaddr);
+ pcb->remote_port = port;
+
+ if (pcb->netif_idx != NETIF_NO_INDEX) {
+ netif = netif_get_by_index(pcb->netif_idx);
} else {
- return ERR_VAL;
+ /* check if we have a route to the remote host */
+ netif = ip_route(&pcb->local_ip, &pcb->remote_ip);
+ }
+ if (netif == NULL) {
+ /* Don't even try to send a SYN packet if we have no route since that will fail. */
+ return ERR_RTE;
}
- pcb->remote_port = port;
- /* check if we have a route to the remote host */
- if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) {
- /* no local IP address set, yet. */
- struct netif *netif;
- ipX_addr_t *local_ip;
- ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip);
- if ((netif == NULL) || (local_ip == NULL)) {
- /* Don't even try to send a SYN packet if we have no route
- since that will fail. */
+ /* check if local IP has been assigned to pcb, if not, get one */
+ if (ip_addr_isany(&pcb->local_ip)) {
+ const ip_addr_t *local_ip = ip_netif_get_local_ip(netif, ipaddr);
+ if (local_ip == NULL) {
return ERR_RTE;
}
- /* Use the address as local address of the pcb. */
- ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip);
+ ip_addr_copy(pcb->local_ip, *local_ip);
+ }
+
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+ /* If the given IP address should have a zone but doesn't, assign one now.
+ * Given that we already have the target netif, this is easy and cheap. */
+ if (IP_IS_V6(&pcb->remote_ip) &&
+ ip6_addr_lacks_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNICAST)) {
+ ip6_addr_assign_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNICAST, netif);
}
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
old_local_port = pcb->local_port;
if (pcb->local_port == 0) {
@@ -782,45 +1003,47 @@ tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t 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_PCB_IPVER_EQ(cpcb, pcb) &&
- ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->local_ip, &pcb->local_ip) &&
- ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->remote_ip, ip_2_ipX(ipaddr))) {
- /* linux returns EISCONN here, but ERR_USE should be OK for us */
- return ERR_USE;
+ 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();
+ }
+
+ 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;
- pcb->rcv_wnd = TCP_WND;
- pcb->rcv_ann_wnd = TCP_WND;
+ /* 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 = (TCP_MSS > 536) ? 536 : TCP_MSS;
+ pcb->mss = INITIAL_MSS;
#if TCP_CALCULATE_EFF_SEND_MSS
- pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip, PCB_ISIPV6(pcb));
+ pcb->mss = tcp_eff_send_mss_netif(pcb->mss, netif, &pcb->remote_ip);
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
pcb->cwnd = 1;
- pcb->ssthresh = pcb->mss * 10;
#if LWIP_CALLBACK_API
pcb->connected = connected;
#else /* LWIP_CALLBACK_API */
@@ -836,7 +1059,7 @@ tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port,
TCP_RMV(&tcp_bound_pcbs, pcb);
}
TCP_REG_ACTIVE(pcb);
- snmp_inc_tcpactiveopens();
+ MIB2_STATS_INC(mib2.tcpactiveopens);
tcp_output(pcb);
}
@@ -854,7 +1077,7 @@ void
tcp_slowtmr(void)
{
struct tcp_pcb *pcb, *prev;
- u16_t eff_wnd;
+ 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;
@@ -886,28 +1109,50 @@ tcp_slowtmr_start:
pcb_remove = 0;
pcb_reset = 0;
- if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) {
+ 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) {
+ } else if (pcb->nrtx >= TCP_MAXRTX) {
++pcb_remove;
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n"));
} else {
if (pcb->persist_backoff > 0) {
- /* If snd_wnd is zero, use persist timer to send 1 byte probes
- * instead of using the standard retransmission mechanism. */
- pcb->persist_cnt++;
- if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) {
- pcb->persist_cnt = 0;
- if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
- pcb->persist_backoff++;
+ LWIP_ASSERT("tcp_slowtimr: persist ticking with in-flight data", pcb->unacked == NULL);
+ LWIP_ASSERT("tcp_slowtimr: persist ticking with empty send buffer", pcb->unsent != NULL);
+ if (pcb->persist_probe >= TCP_MAXRTX) {
+ ++pcb_remove; /* max probes reached */
+ } else {
+ 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) {
+ int next_slot = 1; /* increment timer to next slot */
+ /* If snd_wnd is zero, send 1 byte probes */
+ if (pcb->snd_wnd == 0) {
+ if (tcp_zero_window_probe(pcb) != ERR_OK) {
+ next_slot = 0; /* try probe again with current slot */
+ }
+ /* snd_wnd not fully closed, split unsent head and fill window */
+ } else {
+ if (tcp_split_unsent_seg(pcb, (u16_t)pcb->snd_wnd) == ERR_OK) {
+ if (tcp_output(pcb) == ERR_OK) {
+ /* sending will cancel persist timer, else retry with current slot */
+ next_slot = 0;
+ }
+ }
+ }
+ if (next_slot) {
+ pcb->persist_cnt = 0;
+ if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
+ pcb->persist_backoff++;
+ }
+ }
}
- tcp_zero_window_probe(pcb);
}
} else {
/* Increase the retransmission timer if it is running */
- if(pcb->rtime >= 0) {
+ if (pcb->rtime >= 0) {
++pcb->rtime;
}
@@ -916,30 +1161,34 @@ tcp_slowtmr_start:
LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F
" pcb->rto %"S16_F"\n",
pcb->rtime, pcb->rto));
-
- /* Double retransmission time-out unless we are trying to
- * connect to somebody (i.e., we are in SYN_SENT). */
- if (pcb->state != SYN_SENT) {
- pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx];
+ if (tcp_rexmit_rto_prepare(pcb) == ERR_OK) {
+ /* 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);
+ int calc_rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[backoff_idx];
+ pcb->rto = (s16_t)LWIP_MIN(calc_rto, 0x7FFF);
+ }
+
+ /* 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 = (tcpwnd_size_t)(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));
+ pcb->bytes_acked = 0;
+
+ /* The following needs to be called AFTER cwnd is set to one
+ mss - STJ */
+ tcp_rexmit_rto_commit(pcb);
}
-
- /* Reset the retransmission timer. */
- pcb->rtime = 0;
-
- /* Reduce congestion window and ssthresh. */
- eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd);
- pcb->ssthresh = eff_wnd >> 1;
- if (pcb->ssthresh < (pcb->mss << 1)) {
- pcb->ssthresh = (pcb->mss << 1);
- }
- pcb->cwnd = pcb->mss;
- LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F
- " ssthresh %"U16_F"\n",
- pcb->cwnd, pcb->ssthresh));
-
- /* The following needs to be called AFTER cwnd is set to one
- mss - STJ */
- tcp_rexmit_rto(pcb);
}
}
}
@@ -958,25 +1207,24 @@ tcp_slowtmr_start:
}
/* 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)
- {
+ if (ip_get_option(pcb, SOF_KEEPALIVE) &&
+ ((pcb->state == ESTABLISHED) ||
+ (pcb->state == CLOSE_WAIT))) {
+ if ((u32_t)(tcp_ticks - pcb->tmr) >
+ (pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to "));
- ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip);
+ ip_addr_debug_print_val(TCP_DEBUG, pcb->remote_ip);
LWIP_DEBUGF(TCP_DEBUG, ("\n"));
-
+
++pcb_remove;
++pcb_reset;
- }
- else if((u32_t)(tcp_ticks - pcb->tmr) >
- (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb))
- / TCP_SLOW_INTERVAL)
- {
- tcp_keepalive(pcb);
- pcb->keep_cnt_sent++;
+ } 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++;
+ }
}
}
@@ -985,10 +1233,9 @@ tcp_slowtmr_start:
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;
+ (tcp_ticks - pcb->tmr >= (u32_t)pcb->rto * TCP_OOSEQ_TIMEOUT)) {
LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n"));
+ tcp_free_ooseq(pcb);
}
#endif /* TCP_QUEUE_OOSEQ */
@@ -1012,8 +1259,11 @@ tcp_slowtmr_start:
/* If the PCB should be removed, do it. */
if (pcb_remove) {
struct tcp_pcb *pcb2;
- tcp_err_fn err_fn;
+#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) {
@@ -1026,18 +1276,18 @@ tcp_slowtmr_start:
}
if (pcb_reset) {
- tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
- pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb));
+ tcp_rst(pcb, pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+ pcb->local_port, pcb->remote_port);
}
- err_fn = pcb->errf;
err_arg = pcb->callback_arg;
+ last_state = pcb->state;
pcb2 = pcb;
pcb = pcb->next;
memp_free(MEMP_TCP_PCB, pcb2);
tcp_active_pcbs_changed = 0;
- TCP_EVENT_ERR(err_fn, err_arg, ERR_ABRT);
+ TCP_EVENT_ERR(last_state, err_fn, err_arg, ERR_ABRT);
if (tcp_active_pcbs_changed) {
goto tcp_slowtmr_start;
}
@@ -1064,7 +1314,7 @@ tcp_slowtmr_start:
}
}
-
+
/* Steps through all of the TIME-WAIT PCBs. */
prev = NULL;
pcb = tcp_tw_pcbs;
@@ -1076,8 +1326,6 @@ tcp_slowtmr_start:
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) {
@@ -1104,7 +1352,7 @@ tcp_slowtmr_start:
/**
* Is called every TCP_FAST_INTERVAL (250 ms) and process data previously
- * "refused" by upper layer (application) and sends delayed ACKs.
+ * "refused" by upper layer (application) and sends delayed ACKs or pending FINs.
*
* Automatically called from tcp_tmr().
*/
@@ -1118,7 +1366,7 @@ tcp_fasttmr(void)
tcp_fasttmr_start:
pcb = tcp_active_pcbs;
- while(pcb != NULL) {
+ while (pcb != NULL) {
if (pcb->last_timer != tcp_timer_ctr) {
struct tcp_pcb *next;
pcb->last_timer = tcp_timer_ctr;
@@ -1127,7 +1375,13 @@ tcp_fasttmr_start:
LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n"));
tcp_ack_now(pcb);
tcp_output(pcb);
- pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+ tcp_clear_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
+ }
+ /* send pending FIN */
+ if (pcb->flags & TF_CLOSEPEND) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: pending FIN\n"));
+ tcp_clear_flags(pcb, TF_CLOSEPEND);
+ tcp_close_shutdown_fin(pcb);
}
next = pcb->next;
@@ -1142,6 +1396,21 @@ 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);
}
}
}
@@ -1150,37 +1419,58 @@ tcp_fasttmr_start:
err_t
tcp_process_refused_data(struct tcp_pcb *pcb)
{
- err_t err;
- u8_t refused_flags = pcb->refused_data->flags;
- /* set pcb->refused_data to NULL in case the callback frees it and then
- closes the pcb */
- struct pbuf *refused_data = pcb->refused_data;
- pcb->refused_data = NULL;
- /* Notify again application with data previously received. */
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n"));
- TCP_EVENT_RECV(pcb, refused_data, ERR_OK, err);
- if (err == ERR_OK) {
- /* did refused_data include a FIN? */
- if (refused_flags & PBUF_FLAG_TCP_FIN) {
- /* correct rcv_wnd as the application won't call tcp_recved()
- for the FIN's seqno */
- if (pcb->rcv_wnd != TCP_WND) {
- pcb->rcv_wnd++;
+#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;
+ }
}
- 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;
}
- } else if (err == ERR_ABRT) {
- /* if err == ERR_ABRT, 'pcb' is already deallocated */
- /* Drop incoming packets because pcb is "full" (only if the incoming
- segment contains data). */
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n"));
- return ERR_ABRT;
- } else {
- /* data is still refused, pbuf is still valid (go on for ACK-only packets) */
- pcb->refused_data = refused_data;
}
return ERR_OK;
}
@@ -1220,6 +1510,7 @@ tcp_seg_free(struct tcp_seg *seg)
}
/**
+ * @ingroup tcp
* Sets the priority of a connection.
*
* @param pcb the tcp_pcb to manipulate
@@ -1238,7 +1529,7 @@ tcp_setprio(struct tcp_pcb *pcb, u8_t prio)
*
* @param seg the old tcp_seg
* @return a copy of seg
- */
+ */
struct tcp_seg *
tcp_seg_copy(struct tcp_seg *seg)
{
@@ -1248,7 +1539,7 @@ tcp_seg_copy(struct tcp_seg *seg)
if (cseg == NULL) {
return NULL;
}
- SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg));
+ SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg));
pbuf_ref(cseg->p);
return cseg;
}
@@ -1286,16 +1577,14 @@ tcp_kill_prio(u8_t prio)
u32_t inactivity;
u8_t mprio;
+ mprio = LWIP_MIN(TCP_PRIO_MAX, prio);
- mprio = TCP_PRIO_MAX;
-
/* We kill the oldest active connection that has lower priority than prio. */
inactivity = 0;
inactive = NULL;
- for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
- if (pcb->prio <= prio &&
- pcb->prio <= mprio &&
- (u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
+ 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;
@@ -1303,12 +1592,44 @@ tcp_kill_prio(u8_t prio)
}
if (inactive != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n",
- (void *)inactive, inactivity));
+ (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.
*/
@@ -1321,7 +1642,7 @@ tcp_kill_timewait(void)
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) {
+ 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;
@@ -1329,7 +1650,7 @@ tcp_kill_timewait(void)
}
if (inactive != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n",
- (void *)inactive, inactivity));
+ (void *)inactive, inactivity));
tcp_abort(inactive);
}
}
@@ -1344,8 +1665,7 @@ struct tcp_pcb *
tcp_alloc(u8_t prio)
{
struct tcp_pcb *pcb;
- u32_t iss;
-
+
pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
if (pcb == NULL) {
/* Try killing oldest connection in TIME-WAIT. */
@@ -1354,66 +1674,87 @@ tcp_alloc(u8_t prio)
/* 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 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 twice before */
+ /* adjust err stats: memp_malloc failed multiple times before */
MEMP_STATS_DEC(err, MEMP_TCP_PCB);
}
}
if (pcb != NULL) {
- /* adjust err stats: timewait PCB was freed above */
+ /* 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;
- pcb->snd_queuelen = 0;
- pcb->rcv_wnd = TCP_WND;
- pcb->rcv_ann_wnd = TCP_WND;
- pcb->tos = 0;
+ /* 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 = (TCP_MSS > 536) ? 536 : TCP_MSS;
+ pcb->mss = INITIAL_MSS;
pcb->rto = 3000 / TCP_SLOW_INTERVAL;
- pcb->sa = 0;
pcb->sv = 3000 / TCP_SLOW_INTERVAL;
pcb->rtime = -1;
pcb->cwnd = 1;
- iss = tcp_next_iss();
- pcb->snd_wl2 = iss;
- pcb->snd_nxt = iss;
- pcb->lastack = iss;
- pcb->snd_lbb = iss;
pcb->tmr = tcp_ticks;
pcb->last_timer = tcp_timer_ctr;
- pcb->polltmr = 0;
+ /* 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 */
-
+#endif /* LWIP_CALLBACK_API */
+
/* Init KEEPALIVE timer */
pcb->keep_idle = TCP_KEEPIDLE_DEFAULT;
-
+
#if LWIP_TCP_KEEPALIVE
pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT;
pcb->keep_cnt = TCP_KEEPCNT_DEFAULT;
#endif /* LWIP_TCP_KEEPALIVE */
-
- pcb->keep_cnt_sent = 0;
}
return pcb;
}
/**
+ * @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().
@@ -1431,116 +1772,142 @@ tcp_new(void)
return tcp_alloc(TCP_PRIO_NORMAL);
}
-#if LWIP_IPV6
/**
- * Creates a new TCP-over-IPv6 protocol control block but doesn't
+ * @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_ip6(void)
+tcp_new_ip_type(u8_t type)
{
- struct tcp_pcb * pcb;
+ struct tcp_pcb *pcb;
pcb = tcp_alloc(TCP_PRIO_NORMAL);
- ip_set_v6(pcb, 1);
+#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;
}
-#endif /* LWIP_IPV6 */
/**
+ * @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. */
- pcb->callback_arg = arg;
+ 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)
{
- LWIP_ASSERT("invalid socket state for recv callback", pcb->state != LISTEN);
- pcb->recv = 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)
{
- LWIP_ASSERT("invalid socket state for sent callback", pcb->state != LISTEN);
- pcb->sent = 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 occured on the connection.
+ * 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 occured on the connection
- */
+ * has occurred on the connection
+ */
void
tcp_err(struct tcp_pcb *pcb, tcp_err_fn err)
{
- LWIP_ASSERT("invalid socket state for err callback", pcb->state != LISTEN);
- pcb->errf = err;
+ 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)
{
- /* This function is allowed to be called for both listen pcbs and
- connection pcbs. */
- pcb->accept = 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 */
+#else /* LWIP_CALLBACK_API */
LWIP_UNUSED_ARG(poll);
-#endif /* LWIP_CALLBACK_API */
+#endif /* LWIP_CALLBACK_API */
pcb->pollinterval = interval;
}
@@ -1554,35 +1921,12 @@ void
tcp_pcb_purge(struct tcp_pcb *pcb)
{
if (pcb->state != CLOSED &&
- pcb->state != TIME_WAIT &&
- pcb->state != LISTEN) {
+ pcb->state != TIME_WAIT &&
+ pcb->state != LISTEN) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n"));
-#if TCP_LISTEN_BACKLOG
- if (pcb->state == SYN_RCVD) {
- /* Need to find the corresponding listen_pcb and decrease its accepts_pending */
- struct tcp_pcb_listen *lpcb;
- LWIP_ASSERT("tcp_pcb_purge: pcb->state == SYN_RCVD but tcp_listen_pcbs is NULL",
- tcp_listen_pcbs.listen_pcbs != NULL);
- for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
- if ((!lpcb->bound_to_netif && !pcb->bound_to_netif &&
- (lpcb->local_port == pcb->local_port) &&
- IP_PCB_IPVER_EQ(pcb, lpcb) &&
- (ipX_addr_isany(PCB_ISIPV6(lpcb), &lpcb->local_ip) ||
- ipX_addr_cmp(PCB_ISIPV6(lpcb), &pcb->local_ip, &lpcb->local_ip))) ||
- (lpcb->bound_to_netif && pcb->bound_to_netif &&
- !memcmp(lpcb->local_netif, pcb->local_netif, sizeof(pcb->local_netif)))) {
- /* port and address of the listen pcb match the timed-out pcb */
- LWIP_ASSERT("tcp_pcb_purge: listen pcb does not have accepts pending",
- lpcb->accepts_pending > 0);
- lpcb->accepts_pending--;
- break;
- }
- }
- }
-#endif /* TCP_LISTEN_BACKLOG */
-
+ tcp_backlog_accepted(pcb);
if (pcb->refused_data != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n"));
@@ -1598,9 +1942,8 @@ tcp_pcb_purge(struct tcp_pcb *pcb)
#if TCP_QUEUE_OOSEQ
if (pcb->ooseq != NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n"));
+ tcp_free_ooseq(pcb);
}
- tcp_segs_free(pcb->ooseq);
- pcb->ooseq = NULL;
#endif /* TCP_QUEUE_OOSEQ */
/* Stop the retransmission timer as it will expect data on unacked
@@ -1628,12 +1971,12 @@ 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;
+ if ((pcb->state != TIME_WAIT) &&
+ (pcb->state != LISTEN) &&
+ (pcb->flags & TF_ACK_DELAY)) {
+ tcp_ack_now(pcb);
tcp_output(pcb);
}
@@ -1646,6 +1989,8 @@ tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb)
}
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());
}
@@ -1656,51 +2001,74 @@ tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb)
* @return u32_t pseudo random sequence number
*/
u32_t
-tcp_next_iss(void)
+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
/**
- * Calcluates the effective send mss that can be used for a specific IP address
- * by using ip_route to determin the netif used to send to the address and
- * calculating the minimum of TCP_MSS and that netif's mtu (if set).
+ * Calculates the effective send mss that can be used for a specific IP address
+ * by calculating the minimum of TCP_MSS and the mtu (if set) of the target
+ * netif (if not NULL).
*/
u16_t
-tcp_eff_send_mss_impl(u16_t sendmss, ipX_addr_t *dest
-#if LWIP_IPV6
- , ipX_addr_t *src, u8_t isipv6
-#endif /* LWIP_IPV6 */
- )
+tcp_eff_send_mss_netif(u16_t sendmss, struct netif *outif, const ip_addr_t *dest)
{
u16_t mss_s;
- struct netif *outif;
- s16_t mtu;
+ u16_t mtu;
+
+ LWIP_UNUSED_ARG(dest); /* in case IPv6 is disabled */
- outif = ipX_route(isipv6, src, dest);
#if LWIP_IPV6
- if (isipv6) {
+#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(ipX_2_ip6(dest), outif);
- } else
+ 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) {
- mss_s = mtu - IP_HLEN - TCP_HLEN;
+ u16_t offset;
#if LWIP_IPV6
- /* for IPv6, substract the difference in header size */
- mss_s -= (IP6_HLEN - IP_HLEN);
+#if LWIP_IPV4
+ if (IP_IS_V6(dest))
+#endif /* LWIP_IPV4 */
+ {
+ offset = IP6_HLEN + TCP_HLEN;
+ }
+#if LWIP_IPV4
+ else
+#endif /* LWIP_IPV4 */
#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+ {
+ offset = IP_HLEN + TCP_HLEN;
+ }
+#endif /* LWIP_IPV4 */
+ mss_s = (mtu > offset) ? (u16_t)(mtu - offset) : 0;
/* 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.
@@ -1711,12 +2079,105 @@ tcp_eff_send_mss_impl(u16_t sendmss, ipX_addr_t *dest
}
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
-const char*
+/** 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];
}
+err_t
+tcp_tcp_get_tcp_addrinfo(struct tcp_pcb *pcb, int local, ip_addr_t *addr, u16_t *port)
+{
+ if (pcb) {
+ if (local) {
+ if (addr) {
+ *addr = pcb->local_ip;
+ }
+ if (port) {
+ *port = pcb->local_port;
+ }
+ } else {
+ if (addr) {
+ *addr = pcb->remote_ip;
+ }
+ if (port) {
+ *port = pcb->remote_port;
+ }
+ }
+ return ERR_OK;
+ }
+ return ERR_VAL;
+}
+
+#if TCP_QUEUE_OOSEQ
+/* Free all ooseq pbufs (and possibly reset SACK state) */
+void
+tcp_free_ooseq(struct tcp_pcb *pcb)
+{
+ if (pcb->ooseq) {
+ tcp_segs_free(pcb->ooseq);
+ pcb->ooseq = NULL;
+#if LWIP_TCP_SACK_OUT
+ memset(pcb->rcv_sacks, 0, sizeof(pcb->rcv_sacks));
+#endif /* LWIP_TCP_SACK_OUT */
+ }
+}
+#endif /* TCP_QUEUE_OOSEQ */
+
#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
/**
* Print a tcp header for debugging purposes.
@@ -1729,28 +2190,28 @@ tcp_debug_print(struct tcp_hdr *tcphdr)
LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n"));
LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
LWIP_DEBUGF(TCP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n",
- ntohs(tcphdr->src), ntohs(tcphdr->dest)));
+ lwip_ntohs(tcphdr->src), lwip_ntohs(tcphdr->dest)));
LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (seq no)\n",
- ntohl(tcphdr->seqno)));
+ lwip_ntohl(tcphdr->seqno)));
LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (ack no)\n",
- ntohl(tcphdr->ackno)));
+ 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),
- TCPH_FLAGS(tcphdr) >> 5 & 1,
- TCPH_FLAGS(tcphdr) >> 4 & 1,
- TCPH_FLAGS(tcphdr) >> 3 & 1,
- TCPH_FLAGS(tcphdr) >> 2 & 1,
- TCPH_FLAGS(tcphdr) >> 1 & 1,
- TCPH_FLAGS(tcphdr) & 1,
- ntohs(tcphdr->wnd)));
+ 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",
- ntohs(tcphdr->chksum), ntohs(tcphdr->urgp)));
+ lwip_ntohs(tcphdr->chksum), lwip_ntohs(tcphdr->urgp)));
LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
}
@@ -1807,27 +2268,29 @@ 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) {
+ 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));
+ pcb->local_port, pcb->remote_port,
+ pcb->snd_nxt, pcb->rcv_nxt));
tcp_debug_print_state(pcb->state);
- }
+ }
+
LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n"));
- for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) {
- LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
- pcb->local_port, pcb->remote_port,
- pcb->snd_nxt, pcb->rcv_nxt));
- tcp_debug_print_state(pcb->state);
- }
+ 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) {
+ 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));
+ pcb->local_port, pcb->remote_port,
+ pcb->snd_nxt, pcb->rcv_nxt));
tcp_debug_print_state(pcb->state);
- }
+ }
}
/**
@@ -1837,12 +2300,12 @@ s16_t
tcp_pcbs_sane(void)
{
struct tcp_pcb *pcb;
- for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ 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) {
+ 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;
diff --git a/lwip/src/core/tcp_in.c b/lwip/src/core/tcp_in.c
index 6c382d0..5f502a1 100644
--- a/lwip/src/core/tcp_in.c
+++ b/lwip/src/core/tcp_in.c
@@ -6,7 +6,7 @@
*
* These functions are generally called in the order (ip_input() ->)
* tcp_input() -> * tcp_process() -> tcp_receive() (-> application).
- *
+ *
*/
/*
@@ -45,7 +45,7 @@
#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
-#include "lwip/tcp_impl.h"
+#include "lwip/priv/tcp_priv.h"
#include "lwip/def.h"
#include "lwip/ip_addr.h"
#include "lwip/netif.h"
@@ -53,23 +53,30 @@
#include "lwip/memp.h"
#include "lwip/inet_chksum.h"
#include "lwip/stats.h"
-#include "lwip/snmp.h"
-#include "arch/perf.h"
#include "lwip/ip6.h"
#include "lwip/ip6_addr.h"
-#include "lwip/inet_chksum.h"
#if LWIP_ND6_TCP_REACHABILITY_HINTS
#include "lwip/nd6.h"
#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */
+#include <string.h>
+
+/** Initial CWND calculation as defined RFC 2581 */
+#define LWIP_TCP_CALC_INITIAL_CWND(mss) ((tcpwnd_size_t)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 u8_t flags;
+static tcpwnd_size_t recv_acked;
static u16_t tcplen;
+static u8_t flags;
static u8_t recv_flags;
static struct pbuf *recv_data;
@@ -81,8 +88,18 @@ static err_t tcp_process(struct tcp_pcb *pcb);
static void tcp_receive(struct tcp_pcb *pcb);
static void tcp_parseopt(struct tcp_pcb *pcb);
-static err_t tcp_listen_input(struct tcp_pcb_listen *pcb);
-static err_t tcp_timewait_input(struct tcp_pcb *pcb);
+static void tcp_listen_input(struct tcp_pcb_listen *pcb);
+static void tcp_timewait_input(struct tcp_pcb *pcb);
+
+static int tcp_input_delayed_close(struct tcp_pcb *pcb);
+
+#if LWIP_TCP_SACK_OUT
+static void tcp_add_sack(struct tcp_pcb *pcb, u32_t left, u32_t right);
+static void tcp_remove_sacks_lt(struct tcp_pcb *pcb, u32_t seq);
+#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
+static void tcp_remove_sacks_gt(struct tcp_pcb *pcb, u32_t seq);
+#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */
+#endif /* LWIP_TCP_SACK_OUT */
/**
* The initial input processing of TCP. It verifies the TCP header, demultiplexes
@@ -102,16 +119,15 @@ tcp_input(struct pbuf *p, struct netif *inp)
struct tcp_pcb *lpcb_prev = NULL;
struct tcp_pcb_listen *lpcb_any = NULL;
#endif /* SO_REUSE */
- u8_t hdrlen;
+ u8_t hdrlen_bytes;
err_t err;
-#if CHECKSUM_CHECK_TCP
- u16_t chksum;
-#endif /* CHECKSUM_CHECK_TCP */
+
+ LWIP_UNUSED_ARG(inp);
PERF_START;
TCP_STATS_INC(tcp.recv);
- snmp_inc_tcpinsegs();
+ MIB2_STATS_INC(mib2.tcpinsegs);
tcphdr = (struct tcp_hdr *)p->payload;
@@ -120,7 +136,7 @@ tcp_input(struct pbuf *p, struct netif *inp)
#endif
/* Check that TCP header fits in payload */
- if (p->len < sizeof(struct tcp_hdr)) {
+ 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);
@@ -128,59 +144,119 @@ tcp_input(struct pbuf *p, struct netif *inp)
}
/* Don't even process incoming broadcasts/multicasts. */
- if ((!ip_current_is_v6() && ip_addr_isbroadcast(ip_current_dest_addr(), inp)) ||
- ipX_addr_ismulticast(ip_current_is_v6(), ipX_current_dest_addr())) {
+ 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
- /* Verify TCP checksum. */
- chksum = ipX_chksum_pseudo(ip_current_is_v6(), p, IP_PROTO_TCP, p->tot_len,
- ipX_current_src_addr(), ipX_current_dest_addr());
- if (chksum != 0) {
+ 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;
+ chksum));
+ tcp_debug_print(tcphdr);
+ TCP_STATS_INC(tcp.chkerr);
+ goto dropped;
+ }
}
#endif /* CHECKSUM_CHECK_TCP */
- /* Move the payload pointer in the pbuf so that it points to the
- TCP data instead of the TCP header. */
- hdrlen = TCPH_HDRLEN(tcphdr);
- if(pbuf_header(p, -(hdrlen * 4))){
- /* drop short packets */
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n"));
+ /* sanity-check header length */
+ hdrlen_bytes = TCPH_HDRLEN_BYTES(tcphdr);
+ 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 = (u16_t)(hdrlen_bytes - TCP_HLEN);
+ tcphdr_opt2 = NULL;
+ if (p->len >= hdrlen_bytes) {
+ /* all options are in the first pbuf */
+ tcphdr_opt1len = tcphdr_optlen;
+ pbuf_remove_header(p, 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_remove_header(p, TCP_HLEN);
+
+ /* determine how long the first and second parts of the options are */
+ tcphdr_opt1len = p->len;
+ opt2len = (u16_t)(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_remove_header(p, 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_remove_header(p->next, opt2len);
+ p->tot_len = (u16_t)(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 = ntohs(tcphdr->src);
- tcphdr->dest = ntohs(tcphdr->dest);
- seqno = tcphdr->seqno = ntohl(tcphdr->seqno);
- ackno = tcphdr->ackno = ntohl(tcphdr->ackno);
- tcphdr->wnd = ntohs(tcphdr->wnd);
+ 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);
+ tcplen = p->tot_len;
+ if (flags & (TCP_FIN | TCP_SYN)) {
+ tcplen++;
+ if (tcplen < p->tot_len) {
+ /* u16_t overflow, cannot handle this */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: length u16_t overflow, cannot handle this\n"));
+ TCP_STATS_INC(tcp.lenerr);
+ goto dropped;
+ }
+ }
/* 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) {
+ 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);
+
+ /* check if PCB is bound to specific netif */
+ if ((pcb->netif_idx != NETIF_NO_INDEX) &&
+ (pcb->netif_idx != netif_get_index(ip_data.current_input_netif))) {
+ prev = pcb;
+ continue;
+ }
+
if (pcb->remote_port == tcphdr->src &&
pcb->local_port == tcphdr->dest &&
- IP_PCB_IPVER_INPUT_MATCH(pcb) &&
- ipX_addr_cmp(ip_current_is_v6(), &pcb->remote_ip, ipX_current_src_addr()) &&
- ipX_addr_cmp(ip_current_is_v6(),&pcb->local_ip, ipX_current_dest_addr())) {
+ 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). */
@@ -189,6 +265,8 @@ tcp_input(struct pbuf *p, struct netif *inp)
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;
@@ -199,13 +277,19 @@ tcp_input(struct pbuf *p, struct netif *inp)
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) {
+ for (pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+
+ /* check if PCB is bound to specific netif */
+ if ((pcb->netif_idx != NETIF_NO_INDEX) &&
+ (pcb->netif_idx != netif_get_index(ip_data.current_input_netif))) {
+ continue;
+ }
+
if (pcb->remote_port == tcphdr->src &&
pcb->local_port == tcphdr->dest &&
- IP_PCB_IPVER_INPUT_MATCH(pcb) &&
- ipX_addr_cmp(ip_current_is_v6(), &pcb->remote_ip, ipX_current_src_addr()) &&
- ipX_addr_cmp(ip_current_is_v6(),&pcb->local_ip, ipX_current_dest_addr())) {
+ 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. */
@@ -221,37 +305,43 @@ tcp_input(struct pbuf *p, struct netif *inp)
prev = NULL;
struct tcp_pcb_listen *netif_pcb = NULL;
struct tcp_pcb *netif_pcb_prev = NULL;
- for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+ for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+ /* check if PCB is bound to specific netif */
+ if ((lpcb->netif_idx != NETIF_NO_INDEX) &&
+ (lpcb->netif_idx != netif_get_index(ip_data.current_input_netif))) {
+ prev = (struct tcp_pcb *)lpcb;
+ continue;
+ }
+
if (lpcb->bound_to_netif) {
- if (IP_PCB_IPVER_INPUT_MATCH(lpcb) && netif_is_named(inp, lpcb->local_netif)) {
+ if (IP_ADDR_PCB_VERSION_MATCH(lpcb, ip_current_dest_addr()) &&
+ netif_is_named(inp, lpcb->local_netif)) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_input: found lpcb->bound_to_netif, set netif_pcb(_prev)\n"));
netif_pcb = lpcb;
netif_pcb_prev = prev;
}
}
else if (lpcb->local_port == tcphdr->dest) {
-#if LWIP_IPV6
- if (lpcb->accept_any_ip_version) {
- /* found an ANY-match */
+ 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
-#endif /* LWIP_IPV6 */
- if (IP_PCB_IPVER_INPUT_MATCH(lpcb)) {
- if (ipX_addr_cmp(ip_current_is_v6(), &lpcb->local_ip, ipX_current_dest_addr())) {
+ } 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 (ipX_addr_isany(ip_current_is_v6(), &lpcb->local_ip)) {
+ } 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 */
+#endif /* SO_REUSE */
}
}
}
@@ -275,12 +365,14 @@ tcp_input(struct pbuf *p, struct netif *inp)
arrivals). */
if (prev != NULL) {
((struct tcp_pcb_listen *)prev)->next = lpcb->next;
- /* our successor is the remainder of the listening list */
+ /* 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 */
+ /* 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);
@@ -298,9 +390,7 @@ tcp_input(struct pbuf *p, struct netif *inp)
if (pcb != NULL) {
/* The incoming segment belongs to a connection. */
#if TCP_INPUT_DEBUG
-#if TCP_DEBUG
tcp_debug_print_state(pcb->state);
-#endif /* TCP_DEBUG */
#endif /* TCP_INPUT_DEBUG */
/* Set up a tcp_seg structure. */
@@ -311,6 +401,7 @@ tcp_input(struct pbuf *p, struct netif *inp)
recv_data = NULL;
recv_flags = 0;
+ recv_acked = 0;
if (flags & TCP_PSH) {
p->flags |= PBUF_FLAG_PUSH;
@@ -319,11 +410,16 @@ tcp_input(struct pbuf *p, struct netif *inp)
/* 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->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);
- snmp_inc_tcpinerrs();
+ MIB2_STATS_INC(mib2.tcpinerrs);
goto aborted;
}
}
@@ -337,18 +433,7 @@ tcp_input(struct pbuf *p, struct netif *inp)
end. We then call the error callback to inform the
application that the connection is dead before we
deallocate the PCB. */
- TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST);
- tcp_pcb_remove(&tcp_active_pcbs, pcb);
- memp_free(MEMP_TCP_PCB, pcb);
- } else if (recv_flags & TF_CLOSED) {
- /* The connection has been closed and we will deallocate the
- PCB. */
- if (!(pcb->flags & TF_RXCLOSED)) {
- /* Connection closed although the application has only shut down the
- tx side: call the PCB's err callback and indicate the closure to
- ensure the application doesn't continue using the PCB. */
- TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_CLSD);
- }
+ TCP_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 {
@@ -356,19 +441,47 @@ tcp_input(struct pbuf *p, struct netif *inp)
/* If the application has registered a "sent" function to be
called when new send buffer space is available, we call it
now. */
- if (pcb->acked > 0) {
- TCP_EVENT_SENT(pcb, pcb->acked, err);
- if (err == ERR_ABRT) {
- goto aborted;
+ if (recv_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 (tcp_input_delayed_close(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;
}
@@ -376,13 +489,29 @@ tcp_input(struct pbuf *p, struct netif *inp)
/* 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 */
}
}
@@ -395,7 +524,7 @@ tcp_input(struct pbuf *p, struct netif *inp)
} else {
/* correct rcv_wnd as the application won't call tcp_recved()
for the FIN's seqno */
- if (pcb->rcv_wnd != TCP_WND) {
+ if (pcb->rcv_wnd != TCP_WND_MAX(pcb)) {
pcb->rcv_wnd++;
}
TCP_EVENT_CLOSED(pcb, err);
@@ -406,6 +535,9 @@ tcp_input(struct pbuf *p, struct netif *inp)
}
tcp_input_pcb = NULL;
+ if (tcp_input_delayed_close(pcb)) {
+ goto aborted;
+ }
/* Try to send something out. */
tcp_output(pcb);
#if TCP_INPUT_DEBUG
@@ -422,21 +554,19 @@ aborted:
recv_data = NULL;
/* give up our reference to inseg.p */
- if (inseg.p != NULL)
- {
+ if (inseg.p != NULL) {
pbuf_free(inseg.p);
inseg.p = NULL;
}
} else {
-
/* If no matching PCB was found, send a TCP RST (reset) to the
sender. */
LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n"));
if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) {
TCP_STATS_INC(tcp.proterr);
TCP_STATS_INC(tcp.drop);
- tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(),
- ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6());
+ tcp_rst(NULL, ackno, seqno + tcplen, ip_current_dest_addr(),
+ ip_current_src_addr(), tcphdr->dest, tcphdr->src);
}
pbuf_free(p);
}
@@ -446,31 +576,53 @@ aborted:
return;
dropped:
TCP_STATS_INC(tcp.drop);
- snmp_inc_tcpinerrs();
+ MIB2_STATS_INC(mib2.tcpinerrs);
pbuf_free(p);
}
+/** Called from tcp_input to check for TF_CLOSED flag. This results in closing
+ * and deallocating a pcb at the correct place to ensure noone references it
+ * any more.
+ * @returns 1 if the pcb has been closed and deallocated, 0 otherwise
+ */
+static int
+tcp_input_delayed_close(struct tcp_pcb *pcb)
+{
+ 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);
+ return 1;
+ }
+ return 0;
+}
+
/**
* Called by tcp_input() when a segment arrives for a listening
* connection (from tcp_input()).
*
* @param pcb the tcp_pcb_listen for which a segment arrived
- * @return ERR_OK if the segment was processed
- * another err_t on error
*
- * @note the return value is not (yet?) used in tcp_input()
* @note the segment which arrived is saved in global variables, therefore only the pcb
* involved is passed as a parameter to this function
*/
-static err_t
+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 ERR_OK;
+ return;
}
/* In the LISTEN state, we check for incoming SYN segments,
@@ -479,14 +631,14 @@ tcp_listen_input(struct tcp_pcb_listen *pcb)
/* For incoming segments with the ACK flag set, respond with a
RST. */
LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n"));
- tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(),
- ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6());
+ tcp_rst((const struct tcp_pcb *)pcb, 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 ERR_ABRT;
+ return;
}
#endif /* TCP_LISTEN_BACKLOG */
npcb = tcp_alloc(pcb->prio);
@@ -494,58 +646,63 @@ tcp_listen_input(struct tcp_pcb_listen *pcb)
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);
- return ERR_MEM;
+ 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++;
+ tcp_set_flags(npcb, TF_BACKLOGPEND);
#endif /* TCP_LISTEN_BACKLOG */
/* Set up the new PCB. */
-#if LWIP_IPV6
- PCB_ISIPV6(npcb) = ip_current_is_v6();
-#endif /* LWIP_IPV6 */
- ipX_addr_copy(ip_current_is_v6(), npcb->local_ip, *ipX_current_dest_addr());
- ipX_addr_copy(ip_current_is_v6(), npcb->remote_ip, *ipX_current_src_addr());
- npcb->bound_to_netif = pcb->bound_to_netif;
+ ip_addr_copy(npcb->local_ip, *ip_current_dest_addr());
+ ip_addr_copy(npcb->remote_ip, *ip_current_src_addr());
+ /* npcb->bound_to_netif is 0 regardless of lpcb */
npcb->local_port = tcphdr->dest;
- memcpy(npcb->local_netif, pcb->local_netif, sizeof(pcb->local_netif));
npcb->remote_port = tcphdr->src;
npcb->state = SYN_RCVD;
npcb->rcv_nxt = seqno + 1;
npcb->rcv_ann_right_edge = npcb->rcv_nxt;
- npcb->snd_wnd = tcphdr->wnd;
- npcb->snd_wnd_max = tcphdr->wnd;
- npcb->ssthresh = npcb->snd_wnd;
+ 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
- npcb->accept = pcb->accept;
-#endif /* LWIP_CALLBACK_API */
+#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;
+ npcb->netif_idx = pcb->netif_idx;
/* 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, PCB_ISIPV6(npcb));
+ npcb->mss = tcp_eff_send_mss(npcb->mss, &npcb->local_ip, &npcb->remote_ip);
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
- snmp_inc_tcppassiveopens();
+ 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 rc;
+ return;
}
- return tcp_output(npcb);
+ tcp_output(npcb);
}
- return ERR_OK;
+ return;
}
/**
@@ -557,7 +714,7 @@ tcp_listen_input(struct tcp_pcb_listen *pcb)
* @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
+static void
tcp_timewait_input(struct tcp_pcb *pcb)
{
/* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */
@@ -565,18 +722,18 @@ tcp_timewait_input(struct tcp_pcb *pcb)
* - first check sequence number - we skip that one in TIME_WAIT (always
* acceptable since we only send ACKs)
* - second check the RST bit (... return) */
- if (flags & TCP_RST) {
- return ERR_OK;
+ 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 (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd)) {
/* If the SYN is in the window it is an error, send a reset */
- tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(),
- ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6());
- return ERR_OK;
+ tcp_rst(pcb, 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.
@@ -584,12 +741,12 @@ tcp_timewait_input(struct tcp_pcb *pcb)
pcb->tmr = tcp_ticks;
}
- if ((tcplen > 0)) {
+ if ((tcplen > 0)) {
/* Acknowledge data, FIN or out-of-window SYN */
- pcb->flags |= TF_ACK_NOW;
- return tcp_output(pcb);
+ tcp_ack_now(pcb);
+ tcp_output(pcb);
}
- return ERR_OK;
+ return;
}
/**
@@ -616,13 +773,23 @@ tcp_process(struct tcp_pcb *pcb)
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 {
- if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
- pcb->rcv_nxt+pcb->rcv_wnd)) {
+ /* "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 send a challenge ACK
+ and wait for a re-send with matching sequence number.
+ This follows RFC 5961 section 3.2 and addresses CVE-2004-0230
+ (RST spoofing attack), which is present in RFC 793 RST handling. */
+ tcp_ack_now(pcb);
}
}
@@ -630,195 +797,220 @@ tcp_process(struct tcp_pcb *pcb)
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;
+ tcp_clear_flags(pcb, 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));
+ seqno, pcb->rcv_nxt));
LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
- seqno, pcb->rcv_nxt));
+ seqno, pcb->rcv_nxt));
return ERR_OK;
}
}
- if ((flags & TCP_SYN) && (pcb->state != SYN_SENT && pcb->state != SYN_RCVD)) {
+ 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;
+ pcb->persist_probe = 0;
tcp_parseopt(pcb);
/* Do different things depending on the TCP state. */
switch (pcb->state) {
- case SYN_SENT:
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
- pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno)));
- /* received SYN ACK with expected sequence number? */
- if ((flags & TCP_ACK) && (flags & TCP_SYN)
- && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
- pcb->snd_buf++;
- pcb->rcv_nxt = seqno + 1;
- pcb->rcv_ann_right_edge = pcb->rcv_nxt;
- pcb->lastack = ackno;
- pcb->snd_wnd = tcphdr->wnd;
- pcb->snd_wnd_max = tcphdr->wnd;
- pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
- pcb->state = ESTABLISHED;
+ 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,
- PCB_ISIPV6(pcb));
+ pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip);
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
- /* Set ssthresh again after changing pcb->mss (already set in tcp_connect
- * but for the default value of pcb->mss) */
- pcb->ssthresh = pcb->mss * 10;
+ pcb->cwnd = 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);
- pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
- LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
- --pcb->snd_queuelen;
- LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen));
- rseg = pcb->unacked;
- pcb->unacked = rseg->next;
- tcp_seg_free(rseg);
+ /* If there's nothing left to acknowledge, stop the retransmit
+ timer, otherwise reset it to start again */
+ if (pcb->unacked == NULL) {
+ pcb->rtime = -1;
+ } else {
+ pcb->rtime = 0;
+ pcb->nrtx = 0;
+ }
- /* 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);
}
-
- /* Call the user specified function to call when sucessfully
- * connected. */
- TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
- if (err == ERR_ABRT) {
- return ERR_ABRT;
+ /* 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(pcb, 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);
+ }
}
- tcp_ack_now(pcb);
- }
- /* received ACK? possibly a half-open connection */
- else if (flags & TCP_ACK) {
- /* send a RST to bring the other side in a non-synchronized state. */
- tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(),
- ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6());
- }
- break;
- case SYN_RCVD:
- if (flags & TCP_ACK) {
- /* expected ACK number? */
- if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
- u16_t old_cwnd;
- pcb->state = ESTABLISHED;
- LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ 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 (pcb->listener == NULL) {
+ /* listen pcb might be closed by now */
+ err = ERR_VAL;
+ } else
+#endif /* LWIP_CALLBACK_API || TCP_LISTEN_BACKLOG */
+ {
#if LWIP_CALLBACK_API
- LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL);
+ LWIP_ASSERT("pcb->listener->accept != NULL", pcb->listener->accept != NULL);
#endif
- /* Call the accept function. */
- TCP_EVENT_ACCEPT(pcb, ERR_OK, err);
- if (err != ERR_OK) {
- /* If the accept function returns with an error, we abort
- * the connection. */
- /* Already aborted? */
- if (err != ERR_ABRT) {
- tcp_abort(pcb);
+ tcp_backlog_accepted(pcb);
+ /* Call the accept function. */
+ TCP_EVENT_ACCEPT(pcb->listener, pcb, pcb->callback_arg, ERR_OK, err);
}
- return ERR_ABRT;
- }
- old_cwnd = pcb->cwnd;
- /* If there was any data contained within this ACK,
- * we'd better pass it on to the application as well. */
- tcp_receive(pcb);
-
- /* Prevent ACK for SYN to generate a sent event */
- if (pcb->acked != 0) {
- pcb->acked--;
- }
+ 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);
- pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
+ /* Prevent ACK for SYN to generate a sent event */
+ if (recv_acked != 0) {
+ recv_acked--;
+ }
- if (recv_flags & TF_GOT_FIN) {
- tcp_ack_now(pcb);
- pcb->state = CLOSE_WAIT;
+ 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(pcb, ackno, seqno + tcplen, ip_current_dest_addr(),
+ ip_current_src_addr(), tcphdr->dest, tcphdr->src);
}
- } else {
- /* incorrect ACK number, send RST */
- tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(),
- ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6());
+ } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) {
+ /* Looks like another copy of the SYN - retransmit our SYN-ACK */
+ tcp_rexmit(pcb);
}
- } 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:
+ break;
+ case CLOSE_WAIT:
/* FALLTHROUGH */
- case ESTABLISHED:
- tcp_receive(pcb);
- if (recv_flags & TF_GOT_FIN) { /* passive close */
- tcp_ack_now(pcb);
- pcb->state = CLOSE_WAIT;
- }
- break;
- case FIN_WAIT_1:
- tcp_receive(pcb);
- if (recv_flags & TF_GOT_FIN) {
- if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
- LWIP_DEBUGF(TCP_DEBUG,
- ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ 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);
- } else {
- tcp_ack_now(pcb);
- pcb->state = CLOSING;
}
- } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt)) {
- pcb->state = FIN_WAIT_2;
- }
- break;
- case FIN_WAIT_2:
- tcp_receive(pcb);
- if (recv_flags & TF_GOT_FIN) {
- LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
- tcp_ack_now(pcb);
- tcp_pcb_purge(pcb);
- TCP_RMV_ACTIVE(pcb);
- pcb->state = TIME_WAIT;
- TCP_REG(&tcp_tw_pcbs, pcb);
- }
- break;
- case CLOSING:
- tcp_receive(pcb);
- if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
- LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
- tcp_pcb_purge(pcb);
- TCP_RMV_ACTIVE(pcb);
- pcb->state = TIME_WAIT;
- TCP_REG(&tcp_tw_pcbs, pcb);
- }
- break;
- case LAST_ACK:
- tcp_receive(pcb);
- if (flags & TCP_ACK && ackno == pcb->snd_nxt) {
- LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
- /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */
- recv_flags |= TF_CLOSED;
- }
- break;
- default:
- break;
+ 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;
}
@@ -838,13 +1030,12 @@ tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
/* received segment overlaps all following segments */
tcp_segs_free(next);
next = NULL;
- }
- else {
+ } 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))) {
+ (next->tcphdr->seqno + next->len))) {
/* cseg with FIN already processed */
if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN);
@@ -864,9 +1055,51 @@ tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
}
#endif /* TCP_QUEUE_OOSEQ */
+/** Remove segments from a list if the incoming ACK acknowledges them */
+static struct tcp_seg *
+tcp_free_acked_segments(struct tcp_pcb *pcb, struct tcp_seg *seg_list, const char *dbg_list_name,
+ struct tcp_seg *dbg_other_seg_list)
+{
+ struct tcp_seg *next;
+ u16_t clen;
+
+ LWIP_UNUSED_ARG(dbg_list_name);
+ LWIP_UNUSED_ARG(dbg_other_seg_list);
+
+ while (seg_list != NULL &&
+ TCP_SEQ_LEQ(lwip_ntohl(seg_list->tcphdr->seqno) +
+ TCP_TCPLEN(seg_list), ackno)) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->%s\n",
+ lwip_ntohl(seg_list->tcphdr->seqno),
+ lwip_ntohl(seg_list->tcphdr->seqno) + TCP_TCPLEN(seg_list),
+ dbg_list_name));
+
+ next = seg_list;
+ seg_list = seg_list->next;
+
+ clen = pbuf_clen(next->p);
+ 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 >= clen));
+
+ pcb->snd_queuelen = (u16_t)(pcb->snd_queuelen - clen);
+ recv_acked = (tcpwnd_size_t)(recv_acked + next->len);
+ tcp_seg_free(next);
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"TCPWNDSIZE_F" (after freeing %s)\n",
+ (tcpwnd_size_t)pcb->snd_queuelen,
+ dbg_list_name));
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_receive: valid queue length",
+ seg_list != NULL || dbg_other_seg_list != NULL);
+ }
+ }
+ return seg_list;
+}
+
/**
* Called by tcp_process. Checks if the given segment is an ACK for outstanding
- * data, and if so frees the memory of the buffered data. Next, is places the
+ * 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.
@@ -879,15 +1112,14 @@ tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
static void
tcp_receive(struct tcp_pcb *pcb)
{
+#if TCP_QUEUE_OOSEQ || TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
struct tcp_seg *next;
+#endif
#if TCP_QUEUE_OOSEQ
struct tcp_seg *prev, *cseg;
#endif /* TCP_QUEUE_OOSEQ */
- struct pbuf *p;
- s32_t off;
s16_t m;
u32_t right_wnd_edge;
- u16_t new_tot_len;
int found_dupack = 0;
#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
u32_t ooseq_blen;
@@ -901,31 +1133,21 @@ tcp_receive(struct tcp_pcb *pcb)
/* Update window. */
if (TCP_SEQ_LT(pcb->snd_wl1, seqno) ||
- (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) ||
- (pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) {
- pcb->snd_wnd = tcphdr->wnd;
+ (pcb->snd_wl1 == seqno && 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 < tcphdr->wnd) {
- pcb->snd_wnd_max = tcphdr->wnd;
+ 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 %"U16_F"\n", pcb->snd_wnd));
+ LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"TCPWNDSIZE_F"\n", pcb->snd_wnd));
#if TCP_WND_DEBUG
} else {
- if (pcb->snd_wnd != tcphdr->wnd) {
- LWIP_DEBUGF(TCP_WND_DEBUG,
+ 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));
@@ -935,17 +1157,17 @@ tcp_receive(struct tcp_pcb *pcb)
/* (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
+ * 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 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)
*
@@ -955,11 +1177,10 @@ tcp_receive(struct tcp_pcb *pcb)
/* Clause 1 */
if (TCP_SEQ_LEQ(ackno, pcb->lastack)) {
- pcb->acked = 0;
/* Clause 2 */
if (tcplen == 0) {
/* Clause 3 */
- if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){
+ if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge) {
/* Clause 4 */
if (pcb->rtime >= 0) {
/* Clause 5 */
@@ -969,13 +1190,11 @@ tcp_receive(struct tcp_pcb *pcb)
++pcb->dupacks;
}
if (pcb->dupacks > 3) {
- /* Inflate the congestion window, but not if it means that
- the value overflows. */
- if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
- pcb->cwnd += pcb->mss;
- }
- } else if (pcb->dupacks == 3) {
- /* Do fast retransmit */
+ /* Inflate the congestion window */
+ TCP_WND_INC(pcb->cwnd, pcb->mss);
+ }
+ if (pcb->dupacks >= 3) {
+ /* Do fast retransmit (checked via TF_INFR, not via dupacks count) */
tcp_rexmit_fast(pcb);
}
}
@@ -987,27 +1206,27 @@ tcp_receive(struct tcp_pcb *pcb)
if (!found_dupack) {
pcb->dupacks = 0;
}
- } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)){
+ } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack + 1, pcb->snd_nxt)) {
/* We come here when the ACK acknowledges new data. */
+ tcpwnd_size_t acked;
/* 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;
+ tcp_clear_flags(pcb, TF_INFR);
pcb->cwnd = pcb->ssthresh;
+ pcb->bytes_acked = 0;
}
/* Reset the number of retransmissions. */
pcb->nrtx = 0;
/* Reset the retransmission time-out. */
- pcb->rto = (pcb->sa >> 3) + pcb->sv;
+ pcb->rto = (s16_t)((pcb->sa >> 3) + pcb->sv);
- /* Update the send buffer space. Diff between the two can never exceed 64K? */
- pcb->acked = (u16_t)(ackno - pcb->lastack);
-
- pcb->snd_buf += pcb->acked;
+ /* Record how much data this ACK acks */
+ acked = (tcpwnd_size_t)(ackno - pcb->lastack);
/* Reset the fast retransmit variables. */
pcb->dupacks = 0;
@@ -1017,110 +1236,85 @@ tcp_receive(struct tcp_pcb *pcb)
ssthresh). */
if (pcb->state >= ESTABLISHED) {
if (pcb->cwnd < pcb->ssthresh) {
- if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
- pcb->cwnd += pcb->mss;
- }
- LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd));
+ tcpwnd_size_t increase;
+ /* limit to 1 SMSS segment during period following RTO */
+ u8_t num_seg = (pcb->flags & TF_RTO) ? 1 : 2;
+ /* RFC 3465, section 2.2 Slow Start */
+ increase = LWIP_MIN(acked, (tcpwnd_size_t)(num_seg * pcb->mss));
+ TCP_WND_INC(pcb->cwnd, increase);
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"TCPWNDSIZE_F"\n", pcb->cwnd));
} else {
- u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd);
- if (new_cwnd > pcb->cwnd) {
- pcb->cwnd = new_cwnd;
+ /* RFC 3465, section 2.1 Congestion Avoidance */
+ TCP_WND_INC(pcb->bytes_acked, acked);
+ if (pcb->bytes_acked >= pcb->cwnd) {
+ pcb->bytes_acked = (tcpwnd_size_t)(pcb->bytes_acked - pcb->cwnd);
+ TCP_WND_INC(pcb->cwnd, pcb->mss);
}
- LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_F"\n", pcb->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?
- ntohl(pcb->unacked->tcphdr->seqno): 0,
- pcb->unacked != NULL?
- ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0));
+ 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 acknowlegdes them. */
- while (pcb->unacked != NULL &&
- TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) +
- TCP_TCPLEN(pcb->unacked), ackno)) {
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n",
- ntohl(pcb->unacked->tcphdr->seqno),
- ntohl(pcb->unacked->tcphdr->seqno) +
- TCP_TCPLEN(pcb->unacked)));
-
- next = pcb->unacked;
- pcb->unacked = pcb->unacked->next;
-
- LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen));
- LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
- /* Prevent ACK for FIN to generate a sent event */
- if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
- pcb->acked--;
- }
-
- pcb->snd_queuelen -= pbuf_clen(next->p);
- tcp_seg_free(next);
-
- LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unacked)\n", (u16_t)pcb->snd_queuelen));
- if (pcb->snd_queuelen != 0) {
- LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL ||
- pcb->unsent != NULL);
- }
- }
+ ACK acknowledges them. */
+ pcb->unacked = tcp_free_acked_segments(pcb, pcb->unacked, "unacked", pcb->unsent);
+ /* 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. */
+ pcb->unsent = tcp_free_acked_segments(pcb, pcb->unsent, "unsent", pcb->unacked);
/* If there's nothing left to acknowledge, stop the retransmit
timer, otherwise reset it to start again */
- if(pcb->unacked == NULL)
+ if (pcb->unacked == NULL) {
pcb->rtime = -1;
- else
+ } else {
pcb->rtime = 0;
+ }
pcb->polltmr = 0;
+#if TCP_OVERSIZE
+ if (pcb->unsent == NULL) {
+ pcb->unsent_oversize = 0;
+ }
+#endif /* TCP_OVERSIZE */
+
#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS
- if (PCB_ISIPV6(pcb)) {
+ 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 {
- /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */
- pcb->acked = 0;
- }
- /* We go through the ->unsent list to see if any of the segments
- on the list are acknowledged by the ACK. This may seem
- strange since an "unsent" segment shouldn't be acked. The
- rationale is that lwIP puts all outstanding segments on the
- ->unsent list after a retransmission, so these segments may
- in fact have been sent once. */
- while (pcb->unsent != NULL &&
- TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) +
- TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) {
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n",
- ntohl(pcb->unsent->tcphdr->seqno), ntohl(pcb->unsent->tcphdr->seqno) +
- TCP_TCPLEN(pcb->unsent)));
-
- next = pcb->unsent;
- pcb->unsent = pcb->unsent->next;
-#if TCP_OVERSIZE
- if (pcb->unsent == NULL) {
- pcb->unsent_oversize = 0;
- }
-#endif /* TCP_OVERSIZE */
- LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen));
- LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
- /* Prevent ACK for FIN to generate a sent event */
- if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
- pcb->acked--;
- }
- pcb->snd_queuelen -= pbuf_clen(next->p);
- tcp_seg_free(next);
- LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unsent)\n", (u16_t)pcb->snd_queuelen));
- if (pcb->snd_queuelen != 0) {
- LWIP_ASSERT("tcp_receive: valid queue length",
- pcb->unacked != NULL || pcb->unsent != NULL);
+ pcb->snd_buf = (tcpwnd_size_t)(pcb->snd_buf + recv_acked);
+ /* check if this ACK ends our retransmission of in-flight data */
+ if (pcb->flags & TF_RTO) {
+ /* RTO is done if
+ 1) both queues are empty or
+ 2) unacked is empty and unsent head contains data not part of RTO or
+ 3) unacked head contains data not part of RTO */
+ if (pcb->unacked == NULL) {
+ if ((pcb->unsent == NULL) ||
+ (TCP_SEQ_LEQ(pcb->rto_end, lwip_ntohl(pcb->unsent->tcphdr->seqno)))) {
+ tcp_clear_flags(pcb, TF_RTO);
+ }
+ } else if (TCP_SEQ_LEQ(pcb->rto_end, lwip_ntohl(pcb->unacked->tcphdr->seqno))) {
+ tcp_clear_flags(pcb, TF_RTO);
+ }
}
+ /* End of ACK for new data processing. */
+ } else {
+ /* Out of sequence ACK, didn't really ack anything */
+ tcp_send_empty_ack(pcb);
}
- /* 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));
@@ -1134,20 +1328,20 @@ tcp_receive(struct tcp_pcb *pcb)
m = (s16_t)(tcp_ticks - pcb->rttest);
LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n",
- m, m * TCP_SLOW_INTERVAL));
+ 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;
+ m = (s16_t)(m - (pcb->sa >> 3));
+ pcb->sa = (s16_t)(pcb->sa + m);
if (m < 0) {
- m = -m;
+ m = (s16_t) - m;
}
- m = m - (pcb->sv >> 2);
- pcb->sv += m;
- pcb->rto = (pcb->sa >> 3) + pcb->sv;
+ m = (s16_t)(m - (pcb->sv >> 2));
+ pcb->sv = (s16_t)(pcb->sv + m);
+ pcb->rto = (s16_t)((pcb->sa >> 3) + pcb->sv);
LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n",
- pcb->rto, pcb->rto * TCP_SLOW_INTERVAL));
+ pcb->rto, (u16_t)(pcb->rto * TCP_SLOW_INTERVAL)));
pcb->rttest = 0;
}
@@ -1155,7 +1349,7 @@ tcp_receive(struct tcp_pcb *pcb)
/* If the incoming segment contains data, we must process it
further unless the pcb already received a FIN.
- (RFC 793, chapeter 3.9, "SEGMENT ARRIVES" in states CLOSE-WAIT, CLOSING,
+ (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:
@@ -1186,9 +1380,9 @@ tcp_receive(struct tcp_pcb *pcb)
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(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)){
+ 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
@@ -1209,37 +1403,27 @@ tcp_receive(struct tcp_pcb *pcb)
adjust the ->data pointer in the seg and the segment
length.*/
- off = pcb->rcv_nxt - seqno;
- p = inseg.p;
+ struct pbuf *p = inseg.p;
+ u32_t off32 = pcb->rcv_nxt - seqno;
+ u16_t new_tot_len, off;
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);
- }
+ LWIP_ASSERT("insane offset!", (off32 < 0xffff));
+ off = (u16_t)off32;
+ LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off));
+ inseg.len -= off;
+ new_tot_len = (u16_t)(inseg.p->tot_len - off);
+ while (p->len < off) {
+ off -= p->len;
+ /* all pbufs up to and including this one have len==0, so tot_len is equal */
+ p->tot_len = new_tot_len;
+ p->len = 0;
+ p = p->next;
}
- inseg.len -= (u16_t)(pcb->rcv_nxt - seqno);
+ /* cannot fail... */
+ pbuf_remove_header(p, off);
inseg.tcphdr->seqno = seqno = pcb->rcv_nxt;
- }
- else {
- if (TCP_SEQ_LT(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 */
@@ -1251,8 +1435,8 @@ tcp_receive(struct tcp_pcb *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 (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
@@ -1260,17 +1444,18 @@ tcp_receive(struct tcp_pcb *pcb)
tcplen = TCP_TCPLEN(&inseg);
if (tcplen > pcb->rcv_wnd) {
- LWIP_DEBUGF(TCP_INPUT_DEBUG,
+ 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
+ /* Must remove the FIN from the header as we're trimming
* that byte of sequence-space from the packet */
- TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) &~ TCP_FIN);
+ TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) & ~(unsigned int)TCP_FIN);
}
/* Adjust length of segment to fit in the window. */
- inseg.len = pcb->rcv_wnd;
+ TCPWND_CHECK16(pcb->rcv_wnd);
+ inseg.len = (u16_t)pcb->rcv_wnd;
if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
inseg.len -= 1;
}
@@ -1285,7 +1470,7 @@ tcp_receive(struct tcp_pcb *pcb)
- inseq overlaps with ooseq */
if (pcb->ooseq != NULL) {
if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
- LWIP_DEBUGF(TCP_INPUT_DEBUG,
+ 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
@@ -1303,7 +1488,7 @@ tcp_receive(struct tcp_pcb *pcb)
TCP_SEQ_GEQ(seqno + tcplen,
next->tcphdr->seqno + next->len)) {
/* inseg cannot have FIN here (already processed above) */
- if (TCPH_FLAGS(next->tcphdr) & TCP_FIN &&
+ if ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0 &&
(TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) {
TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN);
tcplen = TCP_TCPLEN(&inseg);
@@ -1380,6 +1565,9 @@ tcp_receive(struct tcp_pcb *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 {
@@ -1392,20 +1580,42 @@ tcp_receive(struct tcp_pcb *pcb)
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);
}
+#if LWIP_TCP_SACK_OUT
+ if (pcb->flags & TF_SACK) {
+ if (pcb->ooseq != NULL) {
+ /* Some segments may have been removed from ooseq, let's remove all SACKs that
+ describe anything before the new beginning of that list. */
+ tcp_remove_sacks_lt(pcb, pcb->ooseq->tcphdr->seqno);
+ } else if (LWIP_TCP_SACK_VALID(pcb, 0)) {
+ /* ooseq has been cleared. Nothing to SACK */
+ memset(pcb->rcv_sacks, 0, sizeof(pcb->rcv_sacks));
+ }
+ }
+#endif /* LWIP_TCP_SACK_OUT */
#endif /* TCP_QUEUE_OOSEQ */
/* Acknowledge the segment(s). */
tcp_ack(pcb);
+#if LWIP_TCP_SACK_OUT
+ if (LWIP_TCP_SACK_VALID(pcb, 0)) {
+ /* Normally the ACK for the data received could be piggy-backed on a data packet,
+ but lwIP currently does not support including SACKs in data packets. So we force
+ it to respond with an empty ACK packet (only if there is at least one SACK to be sent).
+ NOTE: tcp_send_empty_ack() on success clears the ACK flags (set by tcp_ack()) */
+ tcp_send_empty_ack(pcb);
+ }
+#endif /* LWIP_TCP_SACK_OUT */
+
#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS
- if (PCB_ISIPV6(pcb)) {
+ if (ip_current_is_v6()) {
/* Inform neighbor reachability of forward progress. */
nd6_reachability_hint(ip6_current_src_addr());
}
@@ -1413,11 +1623,18 @@ tcp_receive(struct tcp_pcb *pcb)
} else {
/* We get here if the incoming segment is out-of-sequence. */
- tcp_send_empty_ack(pcb);
+
#if TCP_QUEUE_OOSEQ
/* We queue the segment on the ->ooseq queue. */
if (pcb->ooseq == NULL) {
pcb->ooseq = tcp_seg_copy(&inseg);
+#if LWIP_TCP_SACK_OUT
+ if (pcb->flags & TF_SACK) {
+ /* All the SACKs should be invalid, so we can simply store the most recent one: */
+ pcb->rcv_sacks[0].left = seqno;
+ pcb->rcv_sacks[0].right = seqno + inseg.len;
+ }
+#endif /* LWIP_TCP_SACK_OUT */
} else {
/* If the queue is not empty, we walk through the queue and
try to find a place where the sequence number of the
@@ -1431,8 +1648,13 @@ tcp_receive(struct tcp_pcb *pcb)
segment on the ->ooseq queue, we discard the segment that
contains less data. */
+#if LWIP_TCP_SACK_OUT
+ /* This is the left edge of the lowest possible SACK range.
+ It may start before the newly received segment (possibly adjusted below). */
+ u32_t sackbeg = TCP_SEQ_LT(seqno, pcb->ooseq->tcphdr->seqno) ? seqno : pcb->ooseq->tcphdr->seqno;
+#endif /* LWIP_TCP_SACK_OUT */
prev = NULL;
- for(next = pcb->ooseq; next != NULL; next = next->next) {
+ 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
@@ -1453,7 +1675,7 @@ tcp_receive(struct tcp_pcb *pcb)
}
break;
} else {
- /* Either the lenghts are the same or the incoming
+ /* 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;
@@ -1475,7 +1697,7 @@ tcp_receive(struct tcp_pcb *pcb)
} 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)) {
+ 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
@@ -1494,6 +1716,20 @@ tcp_receive(struct tcp_pcb *pcb)
break;
}
}
+
+#if LWIP_TCP_SACK_OUT
+ /* The new segment goes after the 'next' one. If there is a "hole" in sequence numbers
+ between 'prev' and the beginning of 'next', we want to move sackbeg. */
+ if (prev != NULL && prev->tcphdr->seqno + prev->len != next->tcphdr->seqno) {
+ sackbeg = next->tcphdr->seqno;
+ }
+#endif /* LWIP_TCP_SACK_OUT */
+
+ /* We don't use 'prev' below, so let's set it to current 'next'.
+ This way even if we break the loop below, 'prev' will be pointing
+ at the segment right in front of the newly added one. */
+ prev = next;
+
/* If the "next" segment is the last segment on the
ooseq queue, we add the incoming segment to the end
of the list. */
@@ -1511,18 +1747,18 @@ tcp_receive(struct tcp_pcb *pcb)
pbuf_realloc(next->p, next->len);
}
/* check if the remote side overruns our receive window */
- if ((u32_t)tcplen + seqno > pcb->rcv_nxt + (u32_t)pcb->rcv_wnd) {
- LWIP_DEBUGF(TCP_INPUT_DEBUG,
+ 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
+ /* 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);
+ TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) & ~TCP_FIN);
}
/* Adjust length of segment to fit in the window. */
- next->next->len = pcb->rcv_nxt + pcb->rcv_wnd - seqno;
+ 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",
@@ -1532,8 +1768,33 @@ tcp_receive(struct tcp_pcb *pcb)
break;
}
}
- prev = next;
}
+
+#if LWIP_TCP_SACK_OUT
+ if (pcb->flags & TF_SACK) {
+ if (prev == NULL) {
+ /* The new segment is at the beginning. sackbeg should already be set properly.
+ We need to find the right edge. */
+ next = pcb->ooseq;
+ } else if (prev->next != NULL) {
+ /* The new segment was added after 'prev'. If there is a "hole" between 'prev' and 'prev->next',
+ we need to move sackbeg. After that we should find the right edge. */
+ next = prev->next;
+ if (prev->tcphdr->seqno + prev->len != next->tcphdr->seqno) {
+ sackbeg = next->tcphdr->seqno;
+ }
+ } else {
+ next = NULL;
+ }
+ if (next != NULL) {
+ u32_t sackend = next->tcphdr->seqno;
+ for ( ; (next != NULL) && (sackend == next->tcphdr->seqno); next = next->next) {
+ sackend += next->len;
+ }
+ tcp_add_sack(pcb, sackbeg, sackend);
+ }
+ }
+#endif /* LWIP_TCP_SACK_OUT */
}
#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
/* Check that the data on ooseq doesn't exceed one of the limits
@@ -1541,44 +1802,65 @@ tcp_receive(struct tcp_pcb *pcb)
ooseq_blen = 0;
ooseq_qlen = 0;
prev = NULL;
- for(next = pcb->ooseq; next != NULL; prev = next, next = next->next) {
+ 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;
+#if LWIP_TCP_SACK_OUT
+ if (pcb->flags & TF_SACK) {
+ /* Let's remove all SACKs from next's seqno up. */
+ tcp_remove_sacks_gt(pcb, next->tcphdr->seqno);
+ }
+#endif /* LWIP_TCP_SACK_OUT */
+ /* 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 */
+
+ /* We send the ACK packet after we've (potentially) dealt with SACKs,
+ so they can be included in the acknowledgment. */
+ tcp_send_empty_ack(pcb);
}
} else {
- /* The incoming segment is not withing the window. */
+ /* 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_GT(pcb->rcv_nxt, seqno) ||
- TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/
- if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){
+ if (!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd - 1)) {
tcp_ack_now(pcb);
}
}
}
+static u8_t
+tcp_get_next_optbyte(void)
+{
+ u16_t optidx = tcp_optidx++;
+ if ((tcphdr_opt2 == NULL) || (optidx < tcphdr_opt1len)) {
+ u8_t *opts = (u8_t *)tcphdr + TCP_HLEN;
+ return opts[optidx];
+ } else {
+ u8_t idx = (u8_t)(optidx - tcphdr_opt1len);
+ return tcphdr_opt2[idx];
+ }
+}
+
/**
- * Parses the options contained in the incoming segment.
+ * Parses the options contained in the incoming segment.
*
* Called from tcp_listen_input() and tcp_process().
* Currently, only the MSS option is supported!
@@ -1588,79 +1870,269 @@ tcp_receive(struct tcp_pcb *pcb)
static void
tcp_parseopt(struct tcp_pcb *pcb)
{
- u16_t c, max_c;
+ u8_t data;
u16_t mss;
- u8_t *opts, opt;
#if LWIP_TCP_TIMESTAMPS
u32_t tsval;
#endif
- opts = (u8_t *)tcphdr + TCP_HLEN;
-
/* Parse the TCP MSS option, if present. */
- if(TCPH_HDRLEN(tcphdr) > 0x5) {
- max_c = (TCPH_HDRLEN(tcphdr) - 5) << 2;
- for (c = 0; c < max_c; ) {
- opt = opts[c];
+ if (tcphdr_optlen != 0) {
+ for (tcp_optidx = 0; tcp_optidx < tcphdr_optlen; ) {
+ u8_t opt = tcp_get_next_optbyte();
switch (opt) {
- case 0x00:
- /* End of options. */
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n"));
- return;
- case 0x01:
- /* NOP option. */
- ++c;
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n"));
- break;
- case 0x02:
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n"));
- if (opts[c + 1] != 0x04 || c + 0x04 > max_c) {
- /* Bad length */
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+ case LWIP_TCP_OPT_EOL:
+ /* End of options. */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n"));
return;
- }
- /* An MSS option with the right option length. */
- mss = (opts[c + 2] << 8) | opts[c + 3];
- /* Limit the mss to the configured TCP_MSS and prevent division by zero */
- pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss;
- /* Advance to next option */
- c += 0x04;
- break;
+ 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_get_next_optbyte() != 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 = (u16_t)(tcp_get_next_optbyte() << 8);
+ mss |= tcp_get_next_optbyte();
+ /* 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_get_next_optbyte() != 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;
+ }
+ /* An WND_SCALE option with the right option length. */
+ data = tcp_get_next_optbyte();
+ /* 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)) {
+ pcb->snd_scale = data;
+ if (pcb->snd_scale > 14U) {
+ pcb->snd_scale = 14U;
+ }
+ pcb->rcv_scale = TCP_RCV_SCALE;
+ tcp_set_flags(pcb, 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 /* LWIP_WND_SCALE */
#if LWIP_TCP_TIMESTAMPS
- case 0x08:
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n"));
- if (opts[c + 1] != 0x0A || c + 0x0A > max_c) {
- /* Bad length */
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
- return;
- }
- /* TCP timestamp option with valid length */
- tsval = (opts[c+2]) | (opts[c+3] << 8) |
- (opts[c+4] << 16) | (opts[c+5] << 24);
- if (flags & TCP_SYN) {
- pcb->ts_recent = ntohl(tsval);
- pcb->flags |= TF_TIMESTAMP;
- } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) {
- pcb->ts_recent = ntohl(tsval);
- }
- /* Advance to next option */
- c += 0x0A;
- break;
-#endif
- default:
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n"));
- if (opts[c + 1] == 0) {
- LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
- /* If the length field is zero, the options are malformed
- and we don't process them further. */
- return;
- }
- /* All other options have a length field, so that we easily
- can skip past them. */
- c += opts[c + 1];
+ case LWIP_TCP_OPT_TS:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n"));
+ if (tcp_get_next_optbyte() != 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_get_next_optbyte();
+ tsval |= (tcp_get_next_optbyte() << 8);
+ tsval |= (tcp_get_next_optbyte() << 16);
+ tsval |= (tcp_get_next_optbyte() << 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. */
+ tcp_set_flags(pcb, 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 /* LWIP_TCP_TIMESTAMPS */
+#if LWIP_TCP_SACK_OUT
+ case LWIP_TCP_OPT_SACK_PERM:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: SACK_PERM\n"));
+ if (tcp_get_next_optbyte() != LWIP_TCP_OPT_LEN_SACK_PERM || (tcp_optidx - 2 + LWIP_TCP_OPT_LEN_SACK_PERM) > tcphdr_optlen) {
+ /* Bad length */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+ return;
+ }
+ /* TCP SACK_PERM option with valid length */
+ if (flags & TCP_SYN) {
+ /* We only set it if we receive it in a SYN (or SYN+ACK) packet */
+ tcp_set_flags(pcb, TF_SACK);
+ }
+ break;
+#endif /* LWIP_TCP_SACK_OUT */
+ default:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n"));
+ data = tcp_get_next_optbyte();
+ 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;
+}
+
+#if LWIP_TCP_SACK_OUT
+/**
+ * Called by tcp_receive() to add new SACK entry.
+ *
+ * The new SACK entry will be placed at the beginning of rcv_sacks[], as the newest one.
+ * Existing SACK entries will be "pushed back", to preserve their order.
+ * This is the behavior described in RFC 2018, section 4.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ * @param left the left side of the SACK (the first sequence number)
+ * @param right the right side of the SACK (the first sequence number past this SACK)
+ */
+static void
+tcp_add_sack(struct tcp_pcb *pcb, u32_t left, u32_t right)
+{
+ u8_t i;
+ u8_t unused_idx;
+
+ if ((pcb->flags & TF_SACK) == 0 || !TCP_SEQ_LT(left, right)) {
+ return;
+ }
+
+ /* First, let's remove all SACKs that are no longer needed (because they overlap with the newest one),
+ while moving all other SACKs forward.
+ We run this loop for all entries, until we find the first invalid one.
+ There is no point checking after that. */
+ for (i = unused_idx = 0; (i < LWIP_TCP_MAX_SACK_NUM) && LWIP_TCP_SACK_VALID(pcb, i); ++i) {
+ /* We only want to use SACK at [i] if it doesn't overlap with left:right range.
+ It does not overlap if its right side is before the newly added SACK,
+ or if its left side is after the newly added SACK.
+ NOTE: The equality should not really happen, but it doesn't hurt. */
+ if (TCP_SEQ_LEQ(pcb->rcv_sacks[i].right, left) || TCP_SEQ_LEQ(right, pcb->rcv_sacks[i].left)) {
+ if (unused_idx != i) {
+ /* We don't need to copy if it's already in the right spot */
+ pcb->rcv_sacks[unused_idx] = pcb->rcv_sacks[i];
+ }
+ ++unused_idx;
+ }
+ }
+
+ /* Now 'unused_idx' is the index of the first invalid SACK entry,
+ anywhere between 0 (no valid entries) and LWIP_TCP_MAX_SACK_NUM (all entries are valid).
+ We want to clear this and all following SACKs.
+ However, we will be adding another one in the front (and shifting everything else back).
+ So let's just iterate from the back, and set each entry to the one to the left if it's valid,
+ or to 0 if it is not. */
+ for (i = LWIP_TCP_MAX_SACK_NUM - 1; i > 0; --i) {
+ /* [i] is the index we are setting, and the value should be at index [i-1],
+ or 0 if that index is unused (>= unused_idx). */
+ if (i - 1 >= unused_idx) {
+ /* [i-1] is unused. Let's clear [i]. */
+ pcb->rcv_sacks[i].left = pcb->rcv_sacks[i].right = 0;
+ } else {
+ pcb->rcv_sacks[i] = pcb->rcv_sacks[i - 1];
+ }
+ }
+
+ /* And now we can store the newest SACK */
+ pcb->rcv_sacks[0].left = left;
+ pcb->rcv_sacks[0].right = right;
+}
+
+/**
+ * Called to remove a range of SACKs.
+ *
+ * SACK entries will be removed or adjusted to not acknowledge any sequence
+ * numbers that are less than 'seq' passed. It not only invalidates entries,
+ * but also moves all entries that are still valid to the beginning.
+ *
+ * @param pcb the tcp_pcb to modify
+ * @param seq the lowest sequence number to keep in SACK entries
+ */
+static void
+tcp_remove_sacks_lt(struct tcp_pcb *pcb, u32_t seq)
+{
+ u8_t i;
+ u8_t unused_idx;
+
+ /* We run this loop for all entries, until we find the first invalid one.
+ There is no point checking after that. */
+ for (i = unused_idx = 0; (i < LWIP_TCP_MAX_SACK_NUM) && LWIP_TCP_SACK_VALID(pcb, i); ++i) {
+ /* We only want to use SACK at index [i] if its right side is > 'seq'. */
+ if (TCP_SEQ_GT(pcb->rcv_sacks[i].right, seq)) {
+ if (unused_idx != i) {
+ /* We only copy it if it's not in the right spot already. */
+ pcb->rcv_sacks[unused_idx] = pcb->rcv_sacks[i];
+ }
+ /* NOTE: It is possible that its left side is < 'seq', in which case we should adjust it. */
+ if (TCP_SEQ_LT(pcb->rcv_sacks[unused_idx].left, seq)) {
+ pcb->rcv_sacks[unused_idx].left = seq;
+ }
+ ++unused_idx;
+ }
+ }
+
+ /* We also need to invalidate everything from 'unused_idx' till the end */
+ for (i = unused_idx; i < LWIP_TCP_MAX_SACK_NUM; ++i) {
+ pcb->rcv_sacks[i].left = pcb->rcv_sacks[i].right = 0;
+ }
+}
+
+#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
+/**
+ * Called to remove a range of SACKs.
+ *
+ * SACK entries will be removed or adjusted to not acknowledge any sequence
+ * numbers that are greater than (or equal to) 'seq' passed. It not only invalidates entries,
+ * but also moves all entries that are still valid to the beginning.
+ *
+ * @param pcb the tcp_pcb to modify
+ * @param seq the highest sequence number to keep in SACK entries
+ */
+static void
+tcp_remove_sacks_gt(struct tcp_pcb *pcb, u32_t seq)
+{
+ u8_t i;
+ u8_t unused_idx;
+
+ /* We run this loop for all entries, until we find the first invalid one.
+ There is no point checking after that. */
+ for (i = unused_idx = 0; (i < LWIP_TCP_MAX_SACK_NUM) && LWIP_TCP_SACK_VALID(pcb, i); ++i) {
+ /* We only want to use SACK at index [i] if its left side is < 'seq'. */
+ if (TCP_SEQ_LT(pcb->rcv_sacks[i].left, seq)) {
+ if (unused_idx != i) {
+ /* We only copy it if it's not in the right spot already. */
+ pcb->rcv_sacks[unused_idx] = pcb->rcv_sacks[i];
+ }
+ /* NOTE: It is possible that its right side is > 'seq', in which case we should adjust it. */
+ if (TCP_SEQ_GT(pcb->rcv_sacks[unused_idx].right, seq)) {
+ pcb->rcv_sacks[unused_idx].right = seq;
+ }
+ ++unused_idx;
+ }
+ }
+
+ /* We also need to invalidate everything from 'unused_idx' till the end */
+ for (i = unused_idx; i < LWIP_TCP_MAX_SACK_NUM; ++i) {
+ pcb->rcv_sacks[i].left = pcb->rcv_sacks[i].right = 0;
+ }
+}
+#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */
+
+#endif /* LWIP_TCP_SACK_OUT */
+
#endif /* LWIP_TCP */
diff --git a/lwip/src/core/tcp_out.c b/lwip/src/core/tcp_out.c
index d662217..f72c03a 100644
--- a/lwip/src/core/tcp_out.c
+++ b/lwip/src/core/tcp_out.c
@@ -42,7 +42,7 @@
#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
-#include "lwip/tcp_impl.h"
+#include "lwip/priv/tcp_priv.h"
#include "lwip/def.h"
#include "lwip/mem.h"
#include "lwip/memp.h"
@@ -50,10 +50,8 @@
#include "lwip/netif.h"
#include "lwip/inet_chksum.h"
#include "lwip/stats.h"
-#include "lwip/snmp.h"
#include "lwip/ip6.h"
#include "lwip/ip6_addr.h"
-#include "lwip/inet_chksum.h"
#if LWIP_TCP_TIMESTAMPS
#include "lwip/sys.h"
#endif
@@ -79,9 +77,35 @@
#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 void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
+static err_t tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb, struct netif *netif);
+
+/* tcp_route: common code that returns a fixed bound netif or calls ip_route */
+static struct netif *
+tcp_route(const struct tcp_pcb *pcb, const ip_addr_t *src, const ip_addr_t *dst)
+{
+ LWIP_UNUSED_ARG(src); /* in case IPv4-only and source-based routing is disabled */
+
+ if ((pcb != NULL) && (pcb->netif_idx != NETIF_NO_INDEX)) {
+ return netif_get_by_index(pcb->netif_idx);
+ } else {
+ return ip_route(src, dst);
+ }
+}
/** Allocate a pbuf and create a tcphdr at p->payload, used for output
* functions other than the default tcp_output -> tcp_output_segment
@@ -95,20 +119,20 @@ static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
*/
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 */)
+ 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));
+ (p->len >= TCP_HLEN + optlen));
tcphdr = (struct tcp_hdr *)p->payload;
- tcphdr->src = htons(pcb->local_port);
- tcphdr->dest = htons(pcb->remote_port);
+ tcphdr->src = lwip_htons(pcb->local_port);
+ tcphdr->dest = lwip_htons(pcb->remote_port);
tcphdr->seqno = seqno_be;
- tcphdr->ackno = htonl(pcb->rcv_nxt);
+ tcphdr->ackno = lwip_htonl(pcb->rcv_nxt);
TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK);
- tcphdr->wnd = htons(pcb->rcv_ann_wnd);
+ tcphdr->wnd = lwip_htons(TCPWND_MIN16(RCV_WND_SCALE(pcb, pcb->rcv_ann_wnd)));
tcphdr->chksum = 0;
tcphdr->urgp = 0;
@@ -136,7 +160,7 @@ tcp_send_fin(struct tcp_pcb *pcb)
if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) {
/* no SYN/FIN/RST flag in the header, we can add the FIN flag */
TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN);
- pcb->flags |= TF_FIN;
+ tcp_set_flags(pcb, TF_FIN);
return ERR_OK;
}
}
@@ -165,13 +189,14 @@ tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno,
u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags);
if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) {
- LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n"));
+ 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;
@@ -185,22 +210,22 @@ tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno,
#endif /* TCP_CHECKSUM_ON_COPY */
/* build TCP header */
- if (pbuf_header(p, TCP_HLEN)) {
- LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no room for TCP header in pbuf.\n"));
+ if (pbuf_add_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 = htons(pcb->local_port);
- seg->tcphdr->dest = htons(pcb->remote_port);
- seg->tcphdr->seqno = htonl(seqno);
+ 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.
@@ -212,10 +237,9 @@ tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno,
* @param length size of the pbuf's payload.
* @param max_length maximum usable size of payload+oversize.
* @param oversize pointer to a u16_t that will receive the number of usable tail bytes.
- * @param pcb The TCP connection that willo enqueue the pbuf.
+ * @param 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.
- * @param
*/
#if TCP_OVERSIZE
static struct pbuf *
@@ -231,7 +255,6 @@ tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length,
LWIP_UNUSED_ARG(pcb);
LWIP_UNUSED_ARG(apiflags);
LWIP_UNUSED_ARG(first_seg);
- /* always create MSS-sized pbufs */
alloc = max_length;
#else /* LWIP_NETIF_TX_SINGLE_PBUF */
if (length < max_length) {
@@ -251,7 +274,7 @@ tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length,
(!first_seg ||
pcb->unsent != NULL ||
pcb->unacked != NULL))) {
- alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(length + TCP_OVERSIZE));
+ alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(TCP_OVERSIZE_CALC_LENGTH(length)));
}
}
#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
@@ -309,35 +332,36 @@ tcp_write_checks(struct tcp_pcb *pcb, u16_t len)
/* fail on too much data */
if (len > pcb->snd_buf) {
- LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n",
- len, pcb->snd_buf));
- pcb->flags |= TF_NAGLEMEMERR;
+ 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));
+ tcp_set_flags(pcb, TF_NAGLEMEMERR);
return ERR_MEM;
}
- LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));
+ 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 | 3, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n",
- pcb->snd_queuelen, TCP_SND_QUEUELEN));
+ 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;
+ tcp_set_flags(pcb, 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);
+ pcb->unacked != NULL || pcb->unsent != NULL);
} else {
LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty",
- pcb->unacked == NULL && pcb->unsent == NULL);
+ 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
@@ -350,7 +374,7 @@ tcp_write_checks(struct tcp_pcb *pcb, u16_t len)
* @param len Data length in bytes
* @param apiflags combination of following flags :
* - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
- * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent,
+ * - 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
@@ -365,7 +389,11 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
#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;
@@ -373,7 +401,8 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
#endif /* TCP_CHECKSUM_ON_COPY */
err_t err;
/* don't allocate segments bigger than half the maximum window we ever received */
- u16_t mss_local = LWIP_MIN(pcb->mss, pcb->snd_wnd_max/2);
+ 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 */
@@ -381,8 +410,8 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
#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)",
+ (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);
@@ -393,8 +422,12 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
#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 */
@@ -432,6 +465,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
/* Usable space at the end of the last unsent segment */
unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags);
+ LWIP_ASSERT("mss_local is too small", mss_local >= last_unsent->len + unsent_optlen);
space = mss_local - (last_unsent->len + unsent_optlen);
/*
@@ -443,32 +477,41 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
*/
#if TCP_OVERSIZE
#if TCP_OVERSIZE_DBGCHECK
- /* check that pcb->unsent_oversize matches last_unsent->unsent_oversize */
+ /* 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_used <= space);
+ LWIP_ASSERT("inconsistent oversize vs. space", oversize <= space);
seg = last_unsent;
- oversize_used = oversize < len ? oversize : len;
+ 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("inconsistend oversize vs. len", (oversize == 0) || (pos == len));
+ LWIP_ASSERT("inconsistent oversize vs. len", (oversize == 0) || (pos == len));
#endif /* TCP_OVERSIZE */
+#if !LWIP_NETIF_TX_SINGLE_PBUF
/*
* 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.
+ *
+ * This phase is skipped for LWIP_NETIF_TX_SINGLE_PBUF as we could only execute
+ * it after rexmit puts a segment from unacked to unsent and at this point,
+ * oversize info is lost.
*/
if ((pos < len) && (space > 0) && (last_unsent->len > 0)) {
- u16_t seglen = space < len - pos ? space : len - pos;
+ u16_t seglen = LWIP_MIN(space, len - pos);
seg = last_unsent;
/* Create a pbuf with a copy or reference to seglen bytes. We
@@ -477,38 +520,49 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
if (apiflags & TCP_WRITE_FLAG_COPY) {
/* Data is copied */
if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) {
- LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
+ 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
- last_unsent->oversize_left += oversize;
+ oversize_add = oversize;
#endif /* TCP_OVERSIZE_DBGCHECK */
- TCP_DATA_COPY2(concat_p->payload, (u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped);
+ 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 ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
- LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
- ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
- goto memerr;
+ /* If 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_internal & (PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS | PBUF_TYPE_FLAG_DATA_VOLATILE)) == 0) &&
+ (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((u8_t*)arg + pos, seglen), seglen,
- &concat_chksum, &concat_chksum_swapped);
+ 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 */
- /* reference the non-volatile payload data */
- concat_p->payload = (u8_t*)arg + pos;
}
pos += seglen;
- queuelen += pbuf_clen(concat_p);
}
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
} else {
#if TCP_OVERSIZE
LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)",
@@ -526,7 +580,7 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
struct pbuf *p;
u16_t left = len - pos;
u16_t max_len = mss_local - optlen;
- u16_t seglen = left > max_len ? max_len : left;
+ u16_t seglen = LWIP_MIN(left, max_len);
#if TCP_CHECKSUM_ON_COPY
u16_t chksum = 0;
u8_t chksum_swapped = 0;
@@ -536,12 +590,12 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
/* If copy is set, memory should be allocated and data copied
* into pbuf */
if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, mss_local, &oversize, pcb, apiflags, queue == NULL)) == NULL) {
- LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
+ 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, (u8_t*)arg + pos, seglen, &chksum, &chksum_swapped);
+ 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
@@ -553,22 +607,26 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
LWIP_ASSERT("oversize == 0", oversize == 0);
#endif /* TCP_OVERSIZE */
if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
- LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+ 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((u8_t*)arg + pos, seglen);
+ 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 */
- p2->payload = (u8_t*)arg + pos;
+ ((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 | 2, ("tcp_write: could not allocate memory for header pbuf\n"));
+ 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. */
@@ -581,7 +639,8 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
* length of the queue exceeds the configured maximum or
* overflows. */
if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
- LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN));
+ 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;
}
@@ -610,8 +669,8 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
prev_seg = seg;
LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n",
- ntohl(seg->tcphdr->seqno),
- ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg)));
+ lwip_ntohl(seg->tcphdr->seqno),
+ lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg)));
pos += seglen;
}
@@ -620,6 +679,11 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
* 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
@@ -647,21 +711,39 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
#endif /* TCP_OVERSIZE */
/*
- * Phase 2: concat_p can be concatenated onto last_unsent->p
+ * 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));
+ (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) {
- tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,
- &last_unsent->chksum_swapped);
- last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;
+ 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);
}
-#endif /* TCP_CHECKSUM_ON_COPY */
+ 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
@@ -681,20 +763,20 @@ tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
pcb->snd_queuelen = queuelen;
LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n",
- pcb->snd_queuelen));
+ 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)) {
+ 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_set_flags(pcb, TF_NAGLEMEMERR);
TCP_STATS_INC(tcp.memerr);
if (concat_p != NULL) {
@@ -705,7 +787,7 @@ memerr:
}
if (pcb->snd_queuelen != 0) {
LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL ||
- pcb->unsent != 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;
@@ -718,8 +800,6 @@ memerr:
*
* @param pcb Protocol control block for the TCP connection.
* @param flags TCP header flags to set in the outgoing segment.
- * @param optdata pointer to TCP options, or NULL.
- * @param optlen length of TCP options in bytes.
*/
err_t
tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
@@ -734,38 +814,45 @@ tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)",
(flags & (TCP_SYN | TCP_FIN)) != 0);
- /* check for configured max queuelen and possible overflow */
- if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
- LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n",
- pcb->snd_queuelen, TCP_SND_QUEUELEN));
+ /* 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;
+ tcp_set_flags(pcb, 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_SACK_OUT
+ if ((pcb->state != SYN_RCVD) || (pcb->flags & TF_SACK)) {
+ /* In a <SYN,ACK> (sent in state SYN_RCVD), the SACK_PERM option may only
+ be sent if we received a SACK_PERM option from the remote host. */
+ optflags |= TF_SEG_OPTS_SACK_PERM;
+ }
+#endif /* LWIP_TCP_SACK_OUT */
}
#if LWIP_TCP_TIMESTAMPS
- if ((pcb->flags & TF_TIMESTAMP)) {
+ if ((pcb->flags & TF_TIMESTAMP) || ((flags & TCP_SYN) && (pcb->state != SYN_RCVD))) {
+ /* Make sure the timestamp option is only included in data segments if we
+ agreed about it with the remote host (and in active open SYN segments). */
optflags |= TF_SEG_OPTS_TS;
}
#endif /* LWIP_TCP_TIMESTAMPS */
optlen = LWIP_TCP_OPT_LENGTH(optflags);
- /* tcp_enqueue_flags is always called with either SYN or FIN in flags.
- * We need one available snd_buf byte to do that.
- * This means we can't send FIN while snd_buf==0. A better fix would be to
- * not include SYN and FIN sequence numbers in the snd_buf count. */
- if (pcb->snd_buf == 0) {
- LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: no send buffer available\n"));
- TCP_STATS_INC(tcp.memerr);
- return ERR_MEM;
- }
-
/* Allocate pbuf with room for TCP header + options */
if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
- pcb->flags |= TF_NAGLEMEMERR;
+ tcp_set_flags(pcb, TF_NAGLEMEMERR);
TCP_STATS_INC(tcp.memerr);
return ERR_MEM;
}
@@ -774,17 +861,17 @@ tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
/* 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_set_flags(pcb, TF_NAGLEMEMERR);
TCP_STATS_INC(tcp.memerr);
return ERR_MEM;
}
- LWIP_ASSERT("seg->tcphdr not aligned", ((mem_ptr_t)seg->tcphdr % MEM_ALIGNMENT) == 0);
+ LWIP_ASSERT("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",
- ntohl(seg->tcphdr->seqno),
- ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
+ lwip_ntohl(seg->tcphdr->seqno),
+ lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg),
(u16_t)flags));
/* Now append seg to pcb->unsent queue */
@@ -804,10 +891,9 @@ tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
pcb->snd_lbb++;
/* optlen does not influence snd_buf */
- pcb->snd_buf--;
}
if (flags & TCP_FIN) {
- pcb->flags |= TF_FIN;
+ tcp_set_flags(pcb, TF_FIN);
}
/* update number of segments on the queues */
@@ -815,7 +901,7 @@ tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
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);
+ pcb->unacked != NULL || pcb->unsent != NULL);
}
return ERR_OK;
@@ -832,23 +918,102 @@ tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts)
{
/* Pad with two NOP options to make everything nicely aligned */
opts[0] = PP_HTONL(0x0101080A);
- opts[1] = htonl(sys_now());
- opts[2] = htonl(pcb->ts_recent);
+ opts[1] = lwip_htonl(sys_now());
+ opts[2] = lwip_htonl(pcb->ts_recent);
+}
+#endif
+
+#if LWIP_TCP_SACK_OUT
+/**
+ * Calculates the number of SACK entries that should be generated.
+ * It takes into account whether TF_SACK flag is set,
+ * the number of SACK entries in tcp_pcb that are valid,
+ * as well as the available options size.
+ *
+ * @param pcb tcp_pcb
+ * @param optlen the length of other TCP options (in bytes)
+ * @return the number of SACK ranges that can be used
+ */
+static u8_t
+tcp_get_num_sacks(struct tcp_pcb *pcb, u8_t optlen)
+{
+ u8_t num_sacks = 0;
+
+ if (pcb->flags & TF_SACK) {
+ u8_t i;
+
+ /* The first SACK takes up 12 bytes (it includes SACK header and two NOP options),
+ each additional one - 8 bytes. */
+ optlen += 12;
+
+ /* Max options size = 40, number of SACK array entries = LWIP_TCP_MAX_SACK_NUM */
+ for (i = 0; (i < LWIP_TCP_MAX_SACK_NUM) && (optlen <= 40) && LWIP_TCP_SACK_VALID(pcb, i); ++i) {
+ ++num_sacks;
+ optlen += 8;
+ }
+ }
+
+ return num_sacks;
+}
+
+/** Build a SACK option (12 or more bytes long) at the specified options pointer)
+ *
+ * @param pcb tcp_pcb
+ * @param opts option pointer where to store the SACK option
+ * @param num_sacks the number of SACKs to store
+ */
+static void
+tcp_build_sack_option(struct tcp_pcb *pcb, u32_t *opts, u8_t num_sacks)
+{
+ u8_t i;
+
+ /* Pad with two NOP options to make everything nicely aligned.
+ We add the length (of just the SACK option, not the NOPs in front of it),
+ which is 2B of header, plus 8B for each SACK. */
+ *(opts++) = PP_HTONL(0x01010500 + 2 + num_sacks * 8);
+
+ for (i = 0; i < num_sacks; ++i) {
+ *(opts++) = lwip_htonl(pcb->rcv_sacks[i].left);
+ *(opts++) = lwip_htonl(pcb->rcv_sacks[i].right);
+ }
+}
+
+#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.
+/**
+ * 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;
-#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP
+ struct netif *netif;
+#if CHECKSUM_GEN_TCP || LWIP_TCP_TIMESTAMPS || LWIP_TCP_SACK_OUT
struct tcp_hdr *tcphdr;
-#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */
+#if LWIP_TCP_TIMESTAMPS || LWIP_TCP_SACK_OUT
+ u32_t *opts;
+#if LWIP_TCP_SACK_OUT
+ u8_t num_sacks;
+#endif /* LWIP_TCP_SACK_OUT */
+#endif /* LWIP_TCP_TIMESTAMPS || LWIP_TCP_SACK_OUT */
+#endif /* CHECKSUM_GEN_TCP || LWIP_TCP_TIMESTAMPS || LWIP_TCP_SACK_OUT */
#if LWIP_TCP_TIMESTAMPS
if (pcb->flags & TF_TIMESTAMP) {
@@ -856,45 +1021,79 @@ tcp_send_empty_ack(struct tcp_pcb *pcb)
}
#endif
- p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt));
+#if LWIP_TCP_SACK_OUT
+ if ((num_sacks = tcp_get_num_sacks(pcb, optlen)) > 0) {
+ optlen += 4 + num_sacks * 8; /* 4 bytes for header (including 2*NOP), plus 8B for each SACK */
+ }
+#endif
+
+ p = tcp_output_alloc_header(pcb, optlen, 0, lwip_htonl(pcb->snd_nxt));
if (p == NULL) {
+ /* let tcp_fasttmr retry sending this ACK */
+ tcp_set_flags(pcb, 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
+
+#if CHECKSUM_GEN_TCP || LWIP_TCP_TIMESTAMPS || LWIP_TCP_SACK_OUT
tcphdr = (struct tcp_hdr *)p->payload;
-#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */
- LWIP_DEBUGF(TCP_OUTPUT_DEBUG,
+#if LWIP_TCP_TIMESTAMPS || LWIP_TCP_SACK_OUT
+ /* cast through void* to get rid of alignment warnings */
+ opts = (u32_t *)(void *)(tcphdr + 1);
+#endif /* LWIP_TCP_TIMESTAMPS || LWIP_TCP_SACK_OUT */
+#endif /* CHECKSUM_GEN_TCP || LWIP_TCP_TIMESTAMPS || LWIP_TCP_SACK_OUT */
+
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG,
("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt));
- /* remove ACK flags from the PCB, as we send an empty ACK now */
- pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
- /* NB. MSS option is only sent on SYNs, so ignore it here */
+ /* 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));
+ tcp_build_timestamp_option(pcb, opts);
+ opts += 3;
}
-#endif
+#endif
+#if LWIP_TCP_SACK_OUT
+ if (num_sacks > 0) {
+ tcp_build_sack_option(pcb, opts, num_sacks);
+ /* 1 word for SACKs header (including 2xNOP), and 2 words for each SACK */
+ opts += 1 + num_sacks * 2;
+ }
+#endif
+
+ netif = tcp_route(pcb, &pcb->local_ip, &pcb->remote_ip);
+ if (netif == NULL) {
+ err = ERR_RTE;
+ } else {
#if CHECKSUM_GEN_TCP
- tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len,
- &pcb->local_ip, &pcb->remote_ip);
+ 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
-#if LWIP_NETIF_HWADDRHINT
- ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos,
- IP_PROTO_TCP, &pcb->addr_hint);
-#else /* LWIP_NETIF_HWADDRHINT*/
- ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos,
- IP_PROTO_TCP);
-#endif /* LWIP_NETIF_HWADDRHINT*/
+ NETIF_SET_HINTS(netif, &(pcb->netif_hints));
+ err = ip_output_if(p, &pcb->local_ip, &pcb->remote_ip,
+ pcb->ttl, pcb->tos, IP_PROTO_TCP, netif);
+ NETIF_RESET_HINTS(netif);
+ }
pbuf_free(p);
- return ERR_OK;
+ if (err != ERR_OK) {
+ /* let tcp_fasttmr retry sending this ACK */
+ tcp_set_flags(pcb, TF_ACK_DELAY | TF_ACK_NOW);
+ } else {
+ /* remove ACK flags from the PCB, as we sent an empty ACK now */
+ tcp_clear_flags(pcb, 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
@@ -906,13 +1105,15 @@ 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);
+ 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
@@ -926,49 +1127,75 @@ tcp_output(struct tcp_pcb *pcb)
seg = pcb->unsent;
- /* If the TF_ACK_NOW flag is set and no data will be sent (either
- * because the ->unsent queue is empty or because the window does
- * not allow it), construct an empty ACK segment and send it.
- *
- * If data is to be sent, we will just piggyback the ACK (see below).
- */
- if (pcb->flags & TF_ACK_NOW &&
- (seg == NULL ||
- ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
- return tcp_send_empty_ack(pcb);
- }
-
- /* useg should point to last segment on unacked queue */
- useg = pcb->unacked;
- if (useg != NULL) {
- for (; useg->next != NULL; useg = useg->next);
- }
-
-#if TCP_OUTPUT_DEBUG
if (seg == NULL) {
LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n",
- (void*)pcb->unsent));
- }
-#endif /* TCP_OUTPUT_DEBUG */
-#if TCP_CWND_DEBUG
- if (seg == NULL) {
- LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F
- ", cwnd %"U16_F", wnd %"U32_F
+ (void *)pcb->unsent));
+ 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));
+
+ /* If the TF_ACK_NOW flag is set and the ->unsent queue is empty, construct
+ * an empty ACK segment and send it. */
+ if (pcb->flags & TF_ACK_NOW) {
+ return tcp_send_empty_ack(pcb);
+ }
+ /* nothing to send: shortcut out of here */
+ goto output_done;
} else {
- LWIP_DEBUGF(TCP_CWND_DEBUG,
- ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F
+ 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,
- ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
- ntohl(seg->tcphdr->seqno), pcb->lastack));
+ lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
+ lwip_ntohl(seg->tcphdr->seqno), pcb->lastack));
+ }
+
+ netif = tcp_route(pcb, &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);
+ }
+
+ /* Handle the current segment not fitting within the window */
+ if (lwip_ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd) {
+ /* We need to start the persistent timer when the next unsent segment does not fit
+ * within the remaining (could be 0) send window and RTO timer is not running (we
+ * have no in-flight data). If window is still too small after persist timer fires,
+ * then we split the segment. We don't consider the congestion window since a cwnd
+ * smaller than 1 SMSS implies in-flight data
+ */
+ if (wnd == pcb->snd_wnd && pcb->unacked == NULL && pcb->persist_backoff == 0) {
+ pcb->persist_cnt = 0;
+ pcb->persist_backoff = 1;
+ pcb->persist_probe = 0;
+ }
+ /* We need an ACK, but can't send data now, so send an empty ACK */
+ if (pcb->flags & TF_ACK_NOW) {
+ return tcp_send_empty_ack(pcb);
+ }
+ goto output_done;
+ }
+ /* Stop persist timer, above conditions are not active */
+ pcb->persist_backoff = 0;
+
+ /* useg should point to last segment on unacked queue */
+ useg = pcb->unacked;
+ if (useg != NULL) {
+ for (; useg->next != NULL; useg = useg->next);
}
-#endif /* TCP_CWND_DEBUG */
/* data available and window allows it to be sent? */
while (seg != NULL &&
- ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
- LWIP_ASSERT("RST not expected here!",
+ 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:
@@ -977,31 +1204,37 @@ tcp_output(struct tcp_pcb *pcb)
* 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)){
+ if ((tcp_do_output_nagle(pcb) == 0) &&
+ ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)) {
break;
}
#if TCP_CWND_DEBUG
- LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n",
- pcb->snd_wnd, pcb->cwnd, wnd,
- ntohl(seg->tcphdr->seqno) + seg->len -
- pcb->lastack,
- ntohl(seg->tcphdr->seqno), pcb->lastack, i));
+ 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 */
- pcb->unsent = seg->next;
-
if (pcb->state != SYN_SENT) {
TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
- pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
}
#if TCP_OVERSIZE_DBGCHECK
seg->oversize_left = 0;
#endif /* TCP_OVERSIZE_DBGCHECK */
- tcp_output_segment(seg, pcb);
- snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
+ err = tcp_output_segment(seg, pcb, netif);
+ if (err != ERR_OK) {
+ /* segment could not be sent, for whatever reason */
+ tcp_set_flags(pcb, TF_NAGLEMEMERR);
+ return err;
+ }
+ pcb->unsent = seg->next;
+ if (pcb->state != SYN_SENT) {
+ tcp_clear_flags(pcb, 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;
}
@@ -1012,17 +1245,17 @@ tcp_output(struct tcp_pcb *pcb)
if (pcb->unacked == NULL) {
pcb->unacked = seg;
useg = seg;
- /* unacked list is not empty? */
+ /* unacked list is not empty? */
} else {
/* In the case of fast retransmit, the packet should not go to the tail
* of the unacked queue, but rather somewhere before it. We need to check for
* this case. -STJ Jul 27, 2004 */
- if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))) {
+ 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(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) {
- cur_seg = &((*cur_seg)->next );
+ 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;
@@ -1032,7 +1265,7 @@ tcp_output(struct tcp_pcb *pcb)
useg = useg->next;
}
}
- /* do not queue empty segments on the unacked list */
+ /* do not queue empty segments on the unacked list */
} else {
tcp_seg_free(seg);
}
@@ -1045,41 +1278,81 @@ tcp_output(struct tcp_pcb *pcb)
}
#endif /* TCP_OVERSIZE */
- pcb->flags &= ~TF_NAGLEMEMERR;
+output_done:
+ tcp_clear_flags(pcb, TF_NAGLEMEMERR);
return ERR_OK;
}
+/** Check if a segment's pbufs are used by someone else than TCP.
+ * This can happen on retransmission if the pbuf of this segment is still
+ * referenced by the netif driver due to deferred transmission.
+ * This is the case (only!) if someone down the TX call path called
+ * pbuf_ref() on one of the pbufs!
+ *
+ * @arg seg the tcp segment to check
+ * @return 1 if ref != 1, 0 if ref == 1
+ */
+static int
+tcp_output_segment_busy(struct tcp_seg *seg)
+{
+ /* We only need to check the first pbuf here:
+ If a pbuf is queued for transmission, a driver calls pbuf_ref(),
+ which only changes the ref count of the first pbuf */
+ if (seg->p->ref != 1) {
+ /* other reference found */
+ return 1;
+ }
+ /* no other references found */
+ return 0;
+}
+
/**
* 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 void
-tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
+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;
- /** @bug Exclude retransmitted segments from this count. */
- snmp_inc_tcpoutsegs();
+ if (tcp_output_segment_busy(seg)) {
+ /* This should not happen: rexmit functions should have checked this.
+ However, since this function modifies p->len, we must not continue in this case. */
+ LWIP_DEBUGF(TCP_RTO_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("tcp_output_segment: segment busy\n"));
+ return ERR_OK;
+ }
/* The TCP header has already been constructed, but the ackno and
wnd fields remain. */
- seg->tcphdr->ackno = htonl(pcb->rcv_nxt);
+ seg->tcphdr->ackno = lwip_htonl(pcb->rcv_nxt);
/* advertise our receive window size in this TCP segment */
- seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd);
+#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, PCB_ISIPV6(pcb));
+ mss = tcp_eff_send_mss_netif(TCP_MSS, netif, &pcb->remote_ip);
#else /* TCP_CALCULATE_EFF_SEND_MSS */
mss = TCP_MSS;
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
@@ -1094,36 +1367,44 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
opts += 3;
}
#endif
+#if LWIP_WND_SCALE
+ if (seg->flags & TF_SEG_OPTS_WND_SCALE) {
+ tcp_build_wnd_scale_option(opts);
+ opts += 1;
+ }
+#endif
+#if LWIP_TCP_SACK_OUT
+ if (seg->flags & TF_SEG_OPTS_SACK_PERM) {
+ /* Pad with two NOP options to make everything nicely aligned
+ * NOTE: When we send both timestamp and SACK_PERM options,
+ * we could use the first two NOPs before the timestamp to store SACK_PERM option,
+ * but that would complicate the code.
+ */
+ *(opts++) = PP_HTONL(0x01010402);
+ }
+#endif
- /* Set retransmission timer running if it is not currently enabled
+ /* Set retransmission timer running if it is not currently enabled
This must be set before checking the route. */
- if (pcb->rtime == -1) {
+ if (pcb->rtime < 0) {
pcb->rtime = 0;
}
- /* If we don't have a local IP address, we get one by
- calling ip_route(). */
- if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) {
- struct netif *netif;
- ipX_addr_t *local_ip;
- ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip);
- if ((netif == NULL) || (local_ip == NULL)) {
- return;
- }
- ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip);
- }
-
if (pcb->rttest == 0) {
pcb->rttest = tcp_ticks;
- pcb->rtseq = ntohl(seg->tcphdr->seqno);
+ 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",
- htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) +
- seg->len));
+ 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;
@@ -1131,21 +1412,22 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
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 = ipX_chksum_pseudo(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP,
- seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip);
+ 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));
+ seg->p->tot_len == TCPH_HDRLEN_BYTES(seg->tcphdr));
}
/* rebuild TCP header checksum (TCP header changes for retransmissions!) */
- acc = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP,
- seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4, &pcb->local_ip, &pcb->remote_ip);
+ acc = ip_chksum_pseudo_partial(seg->p, IP_PROTO_TCP,
+ seg->p->tot_len, TCPH_HDRLEN_BYTES(seg->tcphdr), &pcb->local_ip, &pcb->remote_ip);
/* add payload checksum */
if (seg->chksum_swapped) {
seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum);
@@ -1155,28 +1437,25 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
seg->tcphdr->chksum = FOLD_U32T(acc);
#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
if (chksum_slow != seg->tcphdr->chksum) {
- LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING,
- ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n",
- seg->tcphdr->chksum, chksum_slow));
+ 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 */
-#if CHECKSUM_GEN_TCP
- seg->tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP,
- seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip);
-#endif /* CHECKSUM_GEN_TCP */
+ 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);
-#if LWIP_NETIF_HWADDRHINT
- ipX_output_hinted(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip,
- pcb->ttl, pcb->tos, IP_PROTO_TCP, &pcb->addr_hint);
-#else /* LWIP_NETIF_HWADDRHINT*/
- ipX_output(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
- pcb->tos, IP_PROTO_TCP);
-#endif /* LWIP_NETIF_HWADDRHINT*/
+ NETIF_SET_HINTS(netif, &(pcb->netif_hints));
+ err = ip_output_if(seg->p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
+ pcb->tos, IP_PROTO_TCP, netif);
+ NETIF_RESET_HINTS(netif);
+ return err;
}
/**
@@ -1192,6 +1471,7 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
* tcp_rst() has a number of arguments that are taken from a tcp_pcb for
* most other segment output functions.
*
+ * @param pcb TCP pcb
* @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
@@ -1200,43 +1480,49 @@ tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
* @param remote_port the remote TCP port to send the segment to
*/
void
-tcp_rst_impl(u32_t seqno, u32_t ackno,
- ipX_addr_t *local_ip, ipX_addr_t *remote_ip,
- u16_t local_port, u16_t remote_port
-#if LWIP_IPV6
- , u8_t isipv6
-#endif /* LWIP_IPV6 */
- )
+tcp_rst(const struct tcp_pcb *pcb, 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_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n"));
+ return;
}
LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
(p->len >= sizeof(struct tcp_hdr)));
tcphdr = (struct tcp_hdr *)p->payload;
- tcphdr->src = htons(local_port);
- tcphdr->dest = htons(remote_port);
- tcphdr->seqno = htonl(seqno);
- tcphdr->ackno = htonl(ackno);
- TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK);
+ tcphdr->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);
- snmp_inc_tcpoutrsts();
+ MIB2_STATS_INC(mib2.tcpoutrsts);
+ netif = tcp_route(pcb, local_ip, remote_ip);
+ if (netif != NULL) {
#if CHECKSUM_GEN_TCP
- tcphdr->chksum = ipX_chksum_pseudo(isipv6, p, IP_PROTO_TCP, p->tot_len,
- local_ip, remote_ip);
+ 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 */
- ipX_output(isipv6, p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP);
+ /* 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));
}
@@ -1248,61 +1534,119 @@ tcp_rst_impl(u32_t seqno, u32_t ackno,
*
* @param pcb the tcp_pcb for which to re-enqueue all unacked segments
*/
-void
-tcp_rexmit_rto(struct tcp_pcb *pcb)
+err_t
+tcp_rexmit_rto_prepare(struct tcp_pcb *pcb)
{
struct tcp_seg *seg;
if (pcb->unacked == NULL) {
- return;
+ return ERR_VAL;
}
- /* Move all unacked segments to the head of the unsent queue */
- for (seg = pcb->unacked; seg->next != NULL; seg = seg->next);
+ /* Move all unacked segments to the head of the unsent queue.
+ However, give up if any of the unsent pbufs are still referenced by the
+ netif driver due to deferred transmission. No point loading the link further
+ if it is struggling to flush its buffered writes. */
+ for (seg = pcb->unacked; seg->next != NULL; seg = seg->next) {
+ if (tcp_output_segment_busy(seg)) {
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_rexmit_rto: segment busy\n"));
+ return ERR_VAL;
+ }
+ }
+ if (tcp_output_segment_busy(seg)) {
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_rexmit_rto: segment busy\n"));
+ return ERR_VAL;
+ }
/* 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;
- /* last unsent hasn't changed, no need to reset unsent_oversize */
-
- /* increment number of retransmissions */
- ++pcb->nrtx;
+ /* Mark RTO in-progress */
+ tcp_set_flags(pcb, TF_RTO);
+ /* Record the next byte following retransmit */
+ pcb->rto_end = lwip_ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
/* Don't take any RTT measurements after retransmitting. */
pcb->rttest = 0;
+ return ERR_OK;
+}
+
+/**
+ * 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_commit(struct tcp_pcb *pcb)
+{
+ /* increment number of retransmissions */
+ if (pcb->nrtx < 0xFF) {
+ ++pcb->nrtx;
+ }
/* Do the actual retransmission */
tcp_output(pcb);
}
/**
+ * 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)
+{
+ if (tcp_rexmit_rto_prepare(pcb) == ERR_OK) {
+ tcp_rexmit_rto_commit(pcb);
+ }
+}
+
+/**
* Requeue the first unacked segment for retransmission
*
- * Called by tcp_receive() for fast retramsmit.
+ * Called by tcp_receive() for fast retransmit.
*
* @param pcb the tcp_pcb for which to retransmit the first unacked segment
*/
-void
+err_t
tcp_rexmit(struct tcp_pcb *pcb)
{
struct tcp_seg *seg;
struct tcp_seg **cur_seg;
if (pcb->unacked == NULL) {
- return;
+ return ERR_VAL;
+ }
+
+ seg = pcb->unacked;
+
+ /* Give up if the segment is still referenced by the netif driver
+ due to deferred transmission. */
+ if (tcp_output_segment_busy(seg)) {
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_rexmit busy\n"));
+ return ERR_VAL;
}
/* Move the first unacked segment to the unsent queue */
/* Keep the unsent queue sorted. */
- seg = pcb->unacked;
pcb->unacked = seg->next;
cur_seg = &(pcb->unsent);
while (*cur_seg &&
- TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) {
- cur_seg = &((*cur_seg)->next );
+ 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;
@@ -1313,15 +1657,18 @@ tcp_rexmit(struct tcp_pcb *pcb)
}
#endif /* TCP_OVERSIZE */
- ++pcb->nrtx;
+ if (pcb->nrtx < 0xFF) {
+ ++pcb->nrtx;
+ }
/* Don't take any rtt measurements after retransmitting. */
pcb->rttest = 0;
/* Do the actual retransmission. */
- snmp_inc_tcpretranssegs();
+ MIB2_STATS_INC(mib2.tcpretranssegs);
/* No need to call tcp_output: we are always called from tcp_input()
and thus tcp_output directly returns. */
+ return ERR_OK;
}
@@ -1330,38 +1677,37 @@ tcp_rexmit(struct tcp_pcb *pcb)
*
* @param pcb the tcp_pcb for which to retransmit the first unacked segment
*/
-void
+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,
+ LWIP_DEBUGF(TCP_FR_DEBUG,
("tcp_receive: dupacks %"U16_F" (%"U32_F
"), fast retransmit %"U32_F"\n",
(u16_t)pcb->dupacks, pcb->lastack,
- ntohl(pcb->unacked->tcphdr->seqno)));
- tcp_rexmit(pcb);
+ lwip_ntohl(pcb->unacked->tcphdr->seqno)));
+ if (tcp_rexmit(pcb) == ERR_OK) {
+ /* 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;
+ }
- /* Set ssthresh to half of the minimum of the current
- * cwnd and the advertised window */
- if (pcb->cwnd > pcb->snd_wnd) {
- pcb->ssthresh = pcb->snd_wnd / 2;
- } else {
- pcb->ssthresh = pcb->cwnd / 2;
- }
-
- /* The minimum value for ssthresh should be 2 MSS */
- if (pcb->ssthresh < 2*pcb->mss) {
- LWIP_DEBUGF(TCP_FR_DEBUG,
- ("tcp_receive: The minimum value for ssthresh %"U16_F
- " should be min 2 mss %"U16_F"...\n",
- pcb->ssthresh, 2*pcb->mss));
- pcb->ssthresh = 2*pcb->mss;
+ pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
+ tcp_set_flags(pcb, TF_INFR);
+
+ /* Reset the retransmission timer to prevent immediate rto retransmissions */
+ pcb->rtime = 0;
}
-
- pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
- pcb->flags |= TF_INFR;
- }
+ }
}
@@ -1373,50 +1719,210 @@ tcp_rexmit_fast(struct tcp_pcb *pcb)
*
* @param pcb the tcp_pcb for which to send a keepalive packet
*/
-void
+err_t
tcp_keepalive(struct tcp_pcb *pcb)
{
+ err_t err;
struct pbuf *p;
-#if CHECKSUM_GEN_TCP
- struct tcp_hdr *tcphdr;
-#endif /* CHECKSUM_GEN_TCP */
+ struct netif *netif;
LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to "));
- ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip);
+ ip_addr_debug_print_val(TCP_DEBUG, pcb->remote_ip);
LWIP_DEBUGF(TCP_DEBUG, ("\n"));
- LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
- tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
-
- p = tcp_output_alloc_header(pcb, 0, 0, htonl(pcb->snd_nxt - 1));
- if(p == NULL) {
- LWIP_DEBUGF(TCP_DEBUG,
+ 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;
+ return ERR_MEM;
}
-#if CHECKSUM_GEN_TCP
- tcphdr = (struct tcp_hdr *)p->payload;
-
- tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len,
- &pcb->local_ip, &pcb->remote_ip);
-#endif /* CHECKSUM_GEN_TCP */
- TCP_STATS_INC(tcp.xmit);
-
- /* Send output to IP */
-#if LWIP_NETIF_HWADDRHINT
- ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip,
- pcb->ttl, 0, IP_PROTO_TCP, &pcb->addr_hint);
-#else /* LWIP_NETIF_HWADDRHINT*/
- ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
- 0, IP_PROTO_TCP);
-#endif /* LWIP_NETIF_HWADDRHINT*/
+ netif = tcp_route(pcb, &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_HINTS(netif, &(pcb->netif_hints));
+ err = ip_output_if(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP, netif);
+ NETIF_RESET_HINTS(netif);
+ }
pbuf_free(p);
- LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n",
- pcb->snd_nxt - 1, pcb->rcv_nxt));
+ 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;
}
+/**
+ * Split segment on the head of the unsent queue. If return is not
+ * ERR_OK, existing head remains intact
+ *
+ * The split is accomplished by creating a new TCP segment and pbuf
+ * which holds the remainder payload after the split. The original
+ * pbuf is trimmed to new length. This allows splitting of read-only
+ * pbufs
+ *
+ * @param pcb the tcp_pcb for which to split the unsent head
+ * @param split the amount of payload to remain in the head
+ */
+err_t
+tcp_split_unsent_seg(struct tcp_pcb *pcb, u16_t split)
+{
+ struct tcp_seg *seg = NULL, *useg = NULL;
+ struct pbuf *p = NULL;
+ u8_t optlen;
+ u8_t optflags;
+ u8_t split_flags;
+ u8_t remainder_flags;
+ u16_t remainder;
+ u16_t offset;
+#if TCP_CHECKSUM_ON_COPY
+ u16_t chksum = 0;
+ u8_t chksum_swapped = 0;
+ struct pbuf *q;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+ useg = pcb->unsent;
+ if (useg == NULL) {
+ return ERR_MEM;
+ }
+
+ if (split == 0) {
+ LWIP_ASSERT("Can't split segment into length 0", 0);
+ return ERR_VAL;
+ }
+
+ if (useg->len <= split) {
+ return ERR_OK;
+ }
+
+ LWIP_ASSERT("split <= mss", split <= pcb->mss);
+ LWIP_ASSERT("useg->len > 0", useg->len > 0);
+
+ /* We should check that we don't exceed TCP_SND_QUEUELEN but we need
+ * to split this packet so we may actually exceed the max value by
+ * one!
+ */
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue: split_unsent_seg: %u\n", (unsigned int)pcb->snd_queuelen));
+
+ optflags = useg->flags;
+#if TCP_CHECKSUM_ON_COPY
+ /* Remove since checksum is not stored until after tcp_create_segment() */
+ optflags &= ~TF_SEG_DATA_CHECKSUMMED;
+#endif /* TCP_CHECKSUM_ON_COPY */
+ optlen = LWIP_TCP_OPT_LENGTH(optflags);
+ remainder = useg->len - split;
+
+ /* Create new pbuf for the remainder of the split */
+ p = pbuf_alloc(PBUF_TRANSPORT, remainder + optlen, PBUF_RAM);
+ if (p == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("tcp_split_unsent_seg: could not allocate memory for pbuf remainder %u\n", remainder));
+ goto memerr;
+ }
+
+ /* Offset into the original pbuf is past TCP/IP headers, options, and split amount */
+ offset = useg->p->tot_len - useg->len + split;
+ /* Copy remainder into new pbuf, headers and options will not be filled out */
+ if (pbuf_copy_partial(useg->p, (u8_t *)p->payload + optlen, remainder, offset ) != remainder) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("tcp_split_unsent_seg: could not copy pbuf remainder %u\n", remainder));
+ goto memerr;
+ }
+#if TCP_CHECKSUM_ON_COPY
+ /* calculate the checksum on remainder data */
+ tcp_seg_add_chksum(~inet_chksum((const u8_t *)p->payload + optlen, remainder), remainder,
+ &chksum, &chksum_swapped);
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+ /* Options are created when calling tcp_output() */
+
+ /* Migrate flags from original segment */
+ split_flags = TCPH_FLAGS(useg->tcphdr);
+ remainder_flags = 0; /* ACK added in tcp_output() */
+
+ if (split_flags & TCP_PSH) {
+ split_flags &= ~TCP_PSH;
+ remainder_flags |= TCP_PSH;
+ }
+ if (split_flags & TCP_FIN) {
+ split_flags &= ~TCP_FIN;
+ remainder |= TCP_FIN;
+ }
+ /* SYN should be left on split, RST should not be present with data */
+
+ seg = tcp_create_segment(pcb, p, remainder_flags, lwip_ntohl(useg->tcphdr->seqno) + split, optflags);
+ if (seg == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("tcp_split_unsent_seg: could not create new TCP segment\n"));
+ goto memerr;
+ }
+
+#if TCP_CHECKSUM_ON_COPY
+ seg->chksum = chksum;
+ seg->chksum_swapped = chksum_swapped;
+ seg->flags |= TF_SEG_DATA_CHECKSUMMED;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+ /* Trim the original pbuf into our split size. At this point our remainder segment must be setup
+ successfully because we are modifying the original segment */
+ pbuf_realloc(useg->p, useg->p->tot_len - remainder);
+ useg->len -= remainder;
+ TCPH_SET_FLAG(useg->tcphdr, split_flags);
+
+#if TCP_CHECKSUM_ON_COPY
+ /* The checksum on the split segment is now incorrect. We need to re-run it over the split */
+ useg->chksum = 0;
+ useg->chksum_swapped = 0;
+ q = useg->p;
+ offset = q->tot_len - useg->len; /* Offset due to exposed headers */
+
+ /* Advance to the pbuf where the offset ends */
+ while (q != NULL && offset > q->len) {
+ offset -= q->len;
+ q = q->next;
+ }
+ LWIP_ASSERT("Found start of payload pbuf", q != NULL);
+ /* Checksum the first payload pbuf accounting for offset, then other pbufs are all payload */
+ for (; q != NULL; offset = 0, q = q->next) {
+ tcp_seg_add_chksum(~inet_chksum((const u8_t *)q->payload + offset, q->len - offset), q->len - offset,
+ &useg->chksum, &useg->chksum_swapped);
+ }
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+ /* Update number of segments on the queues. Note that length now may
+ * exceed TCP_SND_QUEUELEN! We don't have to touch pcb->snd_buf
+ * because the total amount of data is constant when packet is split */
+ pcb->snd_queuelen++;
+
+ /* Finally insert remainder into queue after split (which stays head) */
+ seg->next = useg->next;
+ useg->next = seg;
+
+ return ERR_OK;
+memerr:
+ TCP_STATS_INC(tcp.memerr);
+
+ if (seg != NULL) {
+ tcp_segs_free(seg);
+ }
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+
+ return ERR_MEM;
+}
/**
* Send persist timer zero-window probes to keep a connection active
@@ -1426,31 +1932,40 @@ tcp_keepalive(struct tcp_pcb *pcb)
*
* @param pcb the tcp_pcb for which to send a zero-window probe packet
*/
-void
+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 "));
- ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip);
+ ip_addr_debug_print_val(TCP_DEBUG, pcb->remote_ip);
LWIP_DEBUGF(TCP_DEBUG, ("\n"));
- LWIP_DEBUGF(TCP_DEBUG,
+ LWIP_DEBUGF(TCP_DEBUG,
("tcp_zero_window_probe: tcp_ticks %"U32_F
- " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
- tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
-
- seg = pcb->unacked;
+ " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
+ tcp_ticks, pcb->tmr, (u16_t)pcb->keep_cnt_sent));
- if(seg == NULL) {
- seg = pcb->unsent;
+ /* Only consider unsent, persist timer should be off when there data is in-flight */
+ seg = pcb->unsent;
+ if (seg == NULL) {
+ /* Not expected, persist timer should be off when the send buffer is empty */
+ return ERR_OK;
}
- if(seg == NULL) {
- return;
+
+ /* increment probe count. NOTE: we record probe even if it fails
+ to actually transmit due to an error. This ensures memory exhaustion/
+ routing problem doesn't leave a zero-window pcb as an indefinite zombie.
+ RTO mechanism has similar behavior, see pcb->nrtx */
+ if (pcb->persist_probe < 0xFF) {
+ ++pcb->persist_probe;
}
is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0);
@@ -1458,9 +1973,9 @@ tcp_zero_window_probe(struct tcp_pcb *pcb)
len = is_fin ? 0 : 1;
p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno);
- if(p == NULL) {
+ if (p == NULL) {
LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n"));
- return;
+ return ERR_MEM;
}
tcphdr = (struct tcp_hdr *)p->payload;
@@ -1476,24 +1991,36 @@ tcp_zero_window_probe(struct tcp_pcb *pcb)
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 = tcp_route(pcb, &pcb->local_ip, &pcb->remote_ip);
+ if (netif == NULL) {
+ err = ERR_RTE;
+ } else {
#if CHECKSUM_GEN_TCP
- tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len,
- &pcb->local_ip, &pcb->remote_ip);
+ 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);
+ TCP_STATS_INC(tcp.xmit);
- /* Send output to IP */
-#if LWIP_NETIF_HWADDRHINT
- ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
- 0, IP_PROTO_TCP, &pcb->addr_hint);
-#else /* LWIP_NETIF_HWADDRHINT*/
- ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
-#endif /* LWIP_NETIF_HWADDRHINT*/
+ /* Send output to IP */
+ NETIF_SET_HINTS(netif, &(pcb->netif_hints));
+ err = ip_output_if(p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
+ 0, IP_PROTO_TCP, netif);
+ NETIF_RESET_HINTS(netif);
+ }
pbuf_free(p);
LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F
- " ackno %"U32_F".\n",
- pcb->snd_nxt - 1, pcb->rcv_nxt));
+ " ackno %"U32_F" err %d.\n",
+ pcb->snd_nxt - 1, pcb->rcv_nxt, (int)err));
+ return err;
}
#endif /* LWIP_TCP */
diff --git a/lwip/src/core/timers.c b/lwip/src/core/timeouts.c
index c8ead4e..1290661 100644
--- a/lwip/src/core/timers.c
+++ b/lwip/src/core/timeouts.c
@@ -41,17 +41,15 @@
#include "lwip/opt.h"
-#include "lwip/timers.h"
-#include "lwip/tcp_impl.h"
-
-#if LWIP_TIMERS
+#include "lwip/timeouts.h"
+#include "lwip/priv/tcp_priv.h"
#include "lwip/def.h"
#include "lwip/memp.h"
-#include "lwip/tcpip.h"
+#include "lwip/priv/tcpip_priv.h"
-#include "lwip/ip_frag.h"
-#include "netif/etharp.h"
+#include "lwip/ip4_frag.h"
+#include "lwip/etharp.h"
#include "lwip/dhcp.h"
#include "lwip/autoip.h"
#include "lwip/igmp.h"
@@ -62,11 +60,58 @@
#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 */
+};
+const int lwip_num_cyclic_timers = LWIP_ARRAYSIZE(lwip_cyclic_timers);
+
+#if LWIP_TIMERS && !LWIP_TIMERS_CUSTOM
+
/** The one and only timeout list */
static struct sys_timeo *next_timeout;
-#if NO_SYS
static u32_t timeouts_last_time;
-#endif /* NO_SYS */
#if LWIP_TCP
/** global variable that shows if the tcp timer is currently scheduled or not */
@@ -111,200 +156,35 @@ tcp_timer_needed(void)
}
#endif /* LWIP_TCP */
-#if IP_REASSEMBLY
-/**
- * Timer callback function that calls ip_reass_tmr() and reschedules itself.
- *
- * @param arg unused argument
- */
-static void
-ip_reass_timer(void *arg)
-{
- LWIP_UNUSED_ARG(arg);
- LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip_reass_tmr()\n"));
- ip_reass_tmr();
- sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL);
-}
-#endif /* IP_REASSEMBLY */
-
-#if LWIP_ARP
-/**
- * Timer callback function that calls etharp_tmr() and reschedules itself.
- *
- * @param arg unused argument
- */
-static void
-arp_timer(void *arg)
-{
- LWIP_UNUSED_ARG(arg);
- LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: etharp_tmr()\n"));
- etharp_tmr();
- sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
-}
-#endif /* LWIP_ARP */
-
-#if LWIP_DHCP
/**
- * Timer callback function that calls dhcp_coarse_tmr() and reschedules itself.
+ * Timer callback function that calls cyclic->handler() and reschedules itself.
*
* @param arg unused argument
*/
static void
-dhcp_timer_coarse(void *arg)
+cyclic_timer(void *arg)
{
- LWIP_UNUSED_ARG(arg);
- LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n"));
- dhcp_coarse_tmr();
- sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL);
-}
-
-/**
- * Timer callback function that calls dhcp_fine_tmr() and reschedules itself.
- *
- * @param arg unused argument
- */
-static void
-dhcp_timer_fine(void *arg)
-{
- LWIP_UNUSED_ARG(arg);
- LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_fine_tmr()\n"));
- dhcp_fine_tmr();
- sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL);
-}
-#endif /* LWIP_DHCP */
-
-#if LWIP_AUTOIP
-/**
- * Timer callback function that calls autoip_tmr() and reschedules itself.
- *
- * @param arg unused argument
- */
-static void
-autoip_timer(void *arg)
-{
- LWIP_UNUSED_ARG(arg);
- LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: autoip_tmr()\n"));
- autoip_tmr();
- sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL);
-}
-#endif /* LWIP_AUTOIP */
-
-#if LWIP_IGMP
-/**
- * Timer callback function that calls igmp_tmr() and reschedules itself.
- *
- * @param arg unused argument
- */
-static void
-igmp_timer(void *arg)
-{
- LWIP_UNUSED_ARG(arg);
- LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: igmp_tmr()\n"));
- igmp_tmr();
- sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL);
-}
-#endif /* LWIP_IGMP */
-
-#if LWIP_DNS
-/**
- * Timer callback function that calls dns_tmr() and reschedules itself.
- *
- * @param arg unused argument
- */
-static void
-dns_timer(void *arg)
-{
- LWIP_UNUSED_ARG(arg);
- LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dns_tmr()\n"));
- dns_tmr();
- sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL);
-}
-#endif /* LWIP_DNS */
-
-#if LWIP_IPV6
-/**
- * Timer callback function that calls nd6_tmr() and reschedules itself.
- *
- * @param arg unused argument
- */
-static void
-nd6_timer(void *arg)
-{
- LWIP_UNUSED_ARG(arg);
- LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: nd6_tmr()\n"));
- nd6_tmr();
- sys_timeout(ND6_TMR_INTERVAL, nd6_timer, NULL);
-}
-
-#if LWIP_IPV6_REASS
-/**
- * Timer callback function that calls ip6_reass_tmr() and reschedules itself.
- *
- * @param arg unused argument
- */
-static void
-ip6_reass_timer(void *arg)
-{
- LWIP_UNUSED_ARG(arg);
- LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: ip6_reass_tmr()\n"));
- ip6_reass_tmr();
- sys_timeout(IP6_REASS_TMR_INTERVAL, ip6_reass_timer, NULL);
-}
-#endif /* LWIP_IPV6_REASS */
-
-#if LWIP_IPV6_MLD
-/**
- * Timer callback function that calls mld6_tmr() and reschedules itself.
- *
- * @param arg unused argument
- */
-static void
-mld6_timer(void *arg)
-{
- LWIP_UNUSED_ARG(arg);
- LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: mld6_tmr()\n"));
- mld6_tmr();
- sys_timeout(MLD6_TMR_INTERVAL, mld6_timer, NULL);
+ 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);
}
-#endif /* LWIP_IPV6_MLD */
-#endif /* LWIP_IPV6 */
/** Initialize this module */
void sys_timeouts_init(void)
{
-#if IP_REASSEMBLY
- sys_timeout(IP_TMR_INTERVAL, ip_reass_timer, NULL);
-#endif /* IP_REASSEMBLY */
-#if LWIP_ARP
- sys_timeout(ARP_TMR_INTERVAL, arp_timer, NULL);
-#endif /* LWIP_ARP */
-#if LWIP_DHCP
- sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL);
- sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL);
-#endif /* LWIP_DHCP */
-#if LWIP_AUTOIP
- sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL);
-#endif /* LWIP_AUTOIP */
-#if LWIP_IGMP
- sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL);
-#endif /* LWIP_IGMP */
-#if LWIP_DNS
- sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL);
-#endif /* LWIP_DNS */
-#if LWIP_IPV6
- sys_timeout(ND6_TMR_INTERVAL, nd6_timer, NULL);
-#if LWIP_IPV6_REASS
- sys_timeout(IP6_REASS_TMR_INTERVAL, ip6_reass_timer, NULL);
-#endif /* LWIP_IPV6_REASS */
-#if LWIP_IPV6_MLD
- sys_timeout(MLD6_TMR_INTERVAL, mld6_timer, NULL);
-#endif /* LWIP_IPV6_MLD */
-#endif /* LWIP_IPV6 */
+ size_t i;
+ /* tcp_tmr() at index 0 is started on demand */
+ for (i = (LWIP_TCP ? 1 : 0); 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]));
+ }
-#if NO_SYS
/* Initialise timestamp for sys_check_timeouts */
timeouts_last_time = sys_now();
-#endif
}
/**
@@ -319,27 +199,37 @@ 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)
+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;
+ 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));
+ (void *)timeout, msecs, handler_name, (void *)arg));
#endif /* LWIP_DEBUG_TIMERNAMES */
if (next_timeout == NULL) {
@@ -352,11 +242,17 @@ sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg)
timeout->next = next_timeout;
next_timeout = timeout;
} else {
- for(t = next_timeout; t != NULL; t = t->next) {
+ 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;
@@ -368,10 +264,8 @@ sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg)
/**
* Go through timeout list (for this task only) and remove the first matching
- * entry, even though the timeout has not triggered yet.
- *
- * @note This function only works as expected if there is only one timeout
- * calling 'handler' in the list of timeouts.
+ * 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
@@ -405,14 +299,17 @@ sys_untimeout(sys_timeout_handler handler, void *arg)
return;
}
-#if NO_SYS
-
-/** Handle timeouts for NO_SYS==1 (i.e. without using
+/**
+ * @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)
{
@@ -427,17 +324,14 @@ sys_check_timeouts(void)
now = sys_now();
/* this cares for wraparounds */
diff = now - timeouts_last_time;
- do
- {
-#if PBUF_POOL_FREE_OOSEQ
+ do {
PBUF_CHECK_FREE_OOSEQ();
-#endif /* PBUF_POOL_FREE_OOSEQ */
had_one = 0;
tmptimeout = next_timeout;
if (tmptimeout && (tmptimeout->time <= diff)) {
/* timeout has expired */
had_one = 1;
- timeouts_last_time = now;
+ timeouts_last_time += tmptimeout->time;
diff -= tmptimeout->time;
next_timeout = tmptimeout->next;
handler = tmptimeout->h;
@@ -445,16 +339,25 @@ sys_check_timeouts(void)
#if LWIP_DEBUG_TIMERNAMES
if (handler != NULL) {
LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s arg=%p\n",
- tmptimeout->handler_name, arg));
+ 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);
+ /* repeat until all expired timers have been called */
+ } while (had_one);
}
}
@@ -469,7 +372,28 @@ sys_restart_timeouts(void)
timeouts_last_time = sys_now();
}
-#else /* NO_SYS */
+/** 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.
@@ -481,66 +405,30 @@ sys_restart_timeouts(void)
void
sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
{
- u32_t time_needed;
- struct sys_timeo *tmptimeout;
- sys_timeout_handler handler;
- void *arg;
+ u32_t sleeptime;
- again:
+again:
if (!next_timeout) {
- time_needed = sys_arch_mbox_fetch(mbox, msg, 0);
- } else {
- if (next_timeout->time > 0) {
- time_needed = sys_arch_mbox_fetch(mbox, msg, next_timeout->time);
- } else {
- time_needed = SYS_ARCH_TIMEOUT;
- }
+ sys_arch_mbox_fetch(mbox, msg, 0);
+ return;
+ }
- if (time_needed == SYS_ARCH_TIMEOUT) {
- /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message
- could be fetched. We should now call the timeout handler and
- deallocate the memory allocated for the timeout. */
- tmptimeout = next_timeout;
- next_timeout = tmptimeout->next;
- handler = tmptimeout->h;
- arg = tmptimeout->arg;
-#if LWIP_DEBUG_TIMERNAMES
- if (handler != NULL) {
- LWIP_DEBUGF(TIMERS_DEBUG, ("stmf calling h=%s arg=%p\n",
- tmptimeout->handler_name, arg));
- }
-#endif /* LWIP_DEBUG_TIMERNAMES */
- memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
- if (handler != NULL) {
- /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the
- timeout handler function. */
- LOCK_TCPIP_CORE();
- handler(arg);
- UNLOCK_TCPIP_CORE();
- }
- LWIP_TCPIP_THREAD_ALIVE();
-
- /* We try again to fetch a message from the mbox. */
- goto again;
- } else {
- /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout
- occured. The time variable is set to the number of
- milliseconds we waited for the message. */
- if (time_needed < next_timeout->time) {
- next_timeout->time -= time_needed;
- } else {
- next_timeout->time = 0;
- }
- }
+ 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 */
+#else /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */
/* Satisfy the TCP code which calls this function */
void
tcp_timer_needed(void)
{
}
-#endif /* LWIP_TIMERS */
+#endif /* LWIP_TIMERS && !LWIP_TIMERS_CUSTOM */
diff --git a/lwip/src/core/udp.c b/lwip/src/core/udp.c
index ac0e56d..e255735 100644
--- a/lwip/src/core/udp.c
+++ b/lwip/src/core/udp.c
@@ -1,7 +1,13 @@
/**
* @file
- * User Datagram Protocol module
+ * 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
*/
/*
@@ -36,13 +42,6 @@
*
*/
-
-/* udp.c
- *
- * The code for the User Datagram Protocol UDP & UDPLite (RFC 3828).
- *
- */
-
/* @todo Check the use of '(struct udp_pcb).chksum_len_rx'!
*/
@@ -57,13 +56,11 @@
#include "lwip/ip_addr.h"
#include "lwip/ip6.h"
#include "lwip/ip6_addr.h"
-#include "lwip/inet_chksum.h"
#include "lwip/netif.h"
#include "lwip/icmp.h"
#include "lwip/icmp6.h"
#include "lwip/stats.h"
#include "lwip/snmp.h"
-#include "arch/perf.h"
#include "lwip/dhcp.h"
#include <string.h>
@@ -73,7 +70,7 @@
"The Dynamic and/or Private Ports are those from 49152 through 65535" */
#define UDP_LOCAL_PORT_RANGE_START 0xc000
#define UDP_LOCAL_PORT_RANGE_END 0xffff
-#define UDP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~UDP_LOCAL_PORT_RANGE_START) + UDP_LOCAL_PORT_RANGE_START)
+#define UDP_ENSURE_LOCAL_PORT_RANGE(port) ((u16_t)(((port) & (u16_t)~UDP_LOCAL_PORT_RANGE_START) + UDP_LOCAL_PORT_RANGE_START))
#endif
/* last local UDP port */
@@ -89,9 +86,9 @@ struct udp_pcb *udp_pcbs;
void
udp_init(void)
{
-#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND)
+#ifdef LWIP_RAND
udp_port = UDP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
-#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */
+#endif /* LWIP_RAND */
}
/**
@@ -104,13 +101,13 @@ 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) {
+ 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;
@@ -119,24 +116,63 @@ again:
}
}
return udp_port;
-#if 0
- struct udp_pcb *ipcb = udp_pcbs;
- while ((ipcb != NULL) && (udp_port != UDP_LOCAL_PORT_RANGE_END)) {
- if (ipcb->local_port == udp_port) {
- /* port is already used by another udp_pcb */
- udp_port++;
- /* restart scanning all udp pcbs */
- ipcb = udp_pcbs;
- } else {
- /* go on with next udp pcb */
- ipcb = ipcb->next;
+}
+
+/** 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 */
+
+ /* check if PCB is bound to specific netif */
+ if ((pcb->netif_idx != NETIF_NO_INDEX) &&
+ (pcb->netif_idx != netif_get_index(ip_data.current_input_netif))) {
+ return 0;
+ }
+
+ /* 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;
}
- if (ipcb != NULL) {
- return 0;
+
+ /* 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 udp_port;
-#endif
+
+ return 0;
}
/**
@@ -158,9 +194,10 @@ udp_input(struct pbuf *p, struct netif *inp)
struct udp_pcb *pcb, *prev;
struct udp_pcb *uncon_pcb;
u16_t src, dest;
- u8_t local_match;
u8_t broadcast;
- u8_t for_us;
+ u8_t for_us = 0;
+
+ LWIP_UNUSED_ARG(inp);
PERF_START;
@@ -173,7 +210,7 @@ udp_input(struct pbuf *p, struct netif *inp)
("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len));
UDP_STATS_INC(udp.lenerr);
UDP_STATS_INC(udp.drop);
- snmp_inc_udpinerrors();
+ MIB2_STATS_INC(mib2.udpinerrors);
pbuf_free(p);
goto end;
}
@@ -181,106 +218,56 @@ udp_input(struct pbuf *p, struct netif *inp)
udphdr = (struct udp_hdr *)p->payload;
/* is broadcast packet ? */
-#if LWIP_IPV6
- broadcast = !ip_current_is_v6() && ip_addr_isbroadcast(ip_current_dest_addr(), inp);
-#else /* LWIP_IPV6 */
- broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), inp);
-#endif /* LWIP_IPV6 */
+ 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 = ntohs(udphdr->src);
- dest = ntohs(udphdr->dest);
+ 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 ("));
- ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_dest_addr());
- LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", ntohs(udphdr->dest)));
- ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_src_addr());
- LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", ntohs(udphdr->src)));
+ ip_addr_debug_print_val(UDP_DEBUG, *ip_current_dest_addr());
+ LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", lwip_ntohs(udphdr->dest)));
+ ip_addr_debug_print_val(UDP_DEBUG, *ip_current_src_addr());
+ LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", lwip_ntohs(udphdr->src)));
-#if LWIP_DHCP
pcb = NULL;
- /* when LWIP_DHCP is active, packets to DHCP_CLIENT_PORT may only be processed by
- the dhcp module, no other UDP pcb may use the local UDP port DHCP_CLIENT_PORT */
- if (dest == DHCP_CLIENT_PORT) {
- /* all packets for DHCP_CLIENT_PORT not coming from DHCP_SERVER_PORT are dropped! */
- if (src == DHCP_SERVER_PORT) {
- if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) {
- /* accept the packe if
- (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY!
- - inp->dhcp->pcb->remote == ANY or iphdr->src
- (no need to check for IPv6 since the dhcp struct always uses IPv4) */
- if (ipX_addr_isany(0, &inp->dhcp->pcb->remote_ip) ||
- ip_addr_cmp(ipX_2_ip(&(inp->dhcp->pcb->remote_ip)), ip_current_src_addr())) {
- pcb = inp->dhcp->pcb;
- }
- }
- }
- } else
-#endif /* LWIP_DHCP */
- {
- prev = NULL;
- local_match = 0;
- uncon_pcb = NULL;
- /* Iterate through the UDP pcb list for a matching pcb.
- * 'Perfect match' pcbs (connected to the remote port & ip address) are
- * preferred. If no perfect match is found, the first unconnected pcb that
- * matches the local port and ip address gets the datagram. */
- for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
- local_match = 0;
- /* print the PCB local and remote address */
- LWIP_DEBUGF(UDP_DEBUG, ("pcb ("));
- ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG, &pcb->local_ip);
- LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port));
- ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG, &pcb->remote_ip);
- LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port));
-
- /* compare PCB local addr+port to UDP destination addr+port */
- if (pcb->local_port == dest) {
- if (
-#if LWIP_IPV6
- ((PCB_ISIPV6(pcb) && (ip_current_is_v6()) &&
- (ip6_addr_isany(ipX_2_ip6(&pcb->local_ip)) ||
-#if LWIP_IPV6_MLD
- ip6_addr_ismulticast(ip6_current_dest_addr()) ||
-#endif /* LWIP_IPV6_MLD */
- ip6_addr_cmp(ipX_2_ip6(&pcb->local_ip), ip6_current_dest_addr()))) ||
- (!PCB_ISIPV6(pcb) &&
- (ip_current_header() != NULL) &&
-#else /* LWIP_IPV6 */
- ((
-#endif /* LWIP_IPV6 */
- ((!broadcast && ipX_addr_isany(0, &pcb->local_ip)) ||
- ip_addr_cmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr()) ||
-#if LWIP_IGMP
- ip_addr_ismulticast(ip_current_dest_addr()) ||
-#endif /* LWIP_IGMP */
-#if IP_SOF_BROADCAST_RECV
- (broadcast && ip_get_option(pcb, SOF_BROADCAST) &&
- (ipX_addr_isany(0, &pcb->local_ip) ||
- ip_addr_netcmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr(), &inp->netmask))))))) {
-#else /* IP_SOF_BROADCAST_RECV */
- (broadcast &&
- (ipX_addr_isany(0, &pcb->local_ip) ||
- ip_addr_netcmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr(), &inp->netmask))))))) {
-#endif /* IP_SOF_BROADCAST_RECV */
- local_match = 1;
- if ((uncon_pcb == NULL) &&
- ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) {
- /* the first unconnected matching PCB */
- uncon_pcb = pcb;
- }
- }
+ 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_val(UDP_DEBUG, pcb->local_ip);
+ LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port));
+ ip_addr_debug_print_val(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 ((local_match != 0) &&
- (pcb->remote_port == src) && IP_PCB_IPVER_INPUT_MATCH(pcb) &&
- (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->remote_ip) ||
- ipX_addr_cmp(PCB_ISIPV6(pcb), &pcb->remote_ip, ipX_current_src_addr()))) {
+ 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
@@ -293,12 +280,13 @@ udp_input(struct pbuf *p, struct netif *inp)
}
break;
}
- prev = pcb;
- }
- /* no fully matching pcb found? then look for an unconnected pcb */
- if (pcb == NULL) {
- pcb = uncon_pcb;
}
+
+ 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. */
@@ -307,139 +295,92 @@ udp_input(struct pbuf *p, struct netif *inp)
} else {
#if LWIP_IPV6
if (ip_current_is_v6()) {
- for_us = netif_matches_ip6_addr(inp, ip6_current_dest_addr());
- } else
+ for_us = netif_get_ip6_addr_match(inp, ip6_current_dest_addr()) >= 0;
+ }
#endif /* LWIP_IPV6 */
- {
- for_us = ip_addr_cmp(&inp->ip_addr, ip_current_dest_addr());
+#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 = 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) */
+ 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;
}
- }
- if (ipX_chksum_pseudo_partial(ip_current_is_v6(), p, IP_PROTO_UDPLITE,
- p->tot_len, chklen,
- ipX_current_src_addr(), ipX_current_dest_addr()) != 0) {
- goto chkerr;
- }
- } else
+ } else
#endif /* LWIP_UDPLITE */
- {
- if (udphdr->chksum != 0) {
- if (ipX_chksum_pseudo(ip_current_is_v6(), p, IP_PROTO_UDP, p->tot_len,
- ipX_current_src_addr(),
- ipX_current_dest_addr()) != 0) {
- goto chkerr;
+ {
+ 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)) {
+ if (pbuf_remove_header(p, UDP_HLEN)) {
/* Can we cope with this failing? Just assert for now */
- LWIP_ASSERT("pbuf_header failed\n", 0);
+ LWIP_ASSERT("pbuf_remove_header failed\n", 0);
UDP_STATS_INC(udp.drop);
- snmp_inc_udpinerrors();
+ MIB2_STATS_INC(mib2.udpinerrors);
pbuf_free(p);
goto end;
}
+
if (pcb != NULL) {
- snmp_inc_udpindatagrams();
+ MIB2_STATS_INC(mib2.udpindatagrams);
#if SO_REUSE && SO_REUSE_RXTOALL
- if ((broadcast ||
-#if LWIP_IPV6
- ip6_addr_ismulticast(ip6_current_dest_addr()) ||
-#endif /* LWIP_IPV6 */
- ip_addr_ismulticast(ip_current_dest_addr())) &&
- ip_get_option(pcb, SOF_REUSEADDR)) {
+ 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) &&
-#if LWIP_IPV6
- ((PCB_ISIPV6(mpcb) &&
- (ip6_addr_ismulticast(ip6_current_dest_addr()) ||
- ip6_addr_cmp(ipX_2_ip6(&mpcb->local_ip), ip6_current_dest_addr()))) ||
- (!PCB_ISIPV6(mpcb) &&
-#else /* LWIP_IPV6 */
- ((
-#endif /* LWIP_IPV6 */
- ((!broadcast && ipX_addr_isany(0, &mpcb->local_ip)) ||
- ip_addr_cmp(ipX_2_ip(&mpcb->local_ip), ip_current_dest_addr()) ||
-#if LWIP_IGMP
- ip_addr_ismulticast(ip_current_dest_addr()) ||
-#endif /* LWIP_IGMP */
-#if IP_SOF_BROADCAST_RECV
- (broadcast && ip_get_option(mpcb, SOF_BROADCAST)))))) {
-#else /* IP_SOF_BROADCAST_RECV */
- (broadcast))))) {
-#endif /* IP_SOF_BROADCAST_RECV */
+ (udp_input_local_match(mpcb, inp, broadcast) != 0)) {
/* pass a copy of the packet to all local matches */
- if (mpcb->recv.ip4 != NULL) {
+ if (mpcb->recv != NULL) {
struct pbuf *q;
- /* for that, move payload to IP header again */
- if (p_header_changed == 0) {
- pbuf_header(p, hdrs_len);
- p_header_changed = 1;
- }
- q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ q = pbuf_clone(PBUF_RAW, PBUF_POOL, p);
if (q != NULL) {
- err_t err = pbuf_copy(q, p);
- if (err == ERR_OK) {
- /* move payload to UDP data */
- pbuf_header(q, -hdrs_len);
-#if LWIP_IPV6
- if (PCB_ISIPV6(mpcb)) {
- mpcb->recv.ip6(mpcb->recv_arg, mpcb, q, ip6_current_src_addr(), src);
- }
- else
-#endif /* LWIP_IPV6 */
- {
- mpcb->recv.ip4(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src);
- }
- }
+ 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.ip4 != NULL) {
+ if (pcb->recv != NULL) {
/* now the recv function is responsible for freeing p */
-#if LWIP_IPV6
- if (PCB_ISIPV6(pcb)) {
- pcb->recv.ip6(pcb->recv_arg, pcb, p, ip6_current_src_addr(), src);
- }
- else
-#endif /* LWIP_IPV6 */
- {
- pcb->recv.ip4(pcb->recv_arg, pcb, p, ip_current_src_addr(), src);
- }
+ 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);
@@ -451,19 +392,15 @@ udp_input(struct pbuf *p, struct netif *inp)
#if LWIP_ICMP || LWIP_ICMP6
/* No match was found, send ICMP destination port unreachable unless
destination address was broadcast/multicast. */
- if (!broadcast &&
-#if LWIP_IPV6
- !ip6_addr_ismulticast(ip6_current_dest_addr()) &&
-#endif /* LWIP_IPV6 */
- !ip_addr_ismulticast(ip_current_dest_addr())) {
+ if (!broadcast && !ip_addr_ismulticast(ip_current_dest_addr())) {
/* move payload pointer back to ip header */
- pbuf_header(p, ip_current_header_tot_len() + UDP_HLEN);
+ 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);
- snmp_inc_udpnoports();
+ MIB2_STATS_INC(mib2.udpnoports);
pbuf_free(p);
}
} else {
@@ -478,13 +415,14 @@ chkerr:
("udp_input: UDP (or UDP Lite) datagram discarded due to failing checksum\n"));
UDP_STATS_INC(udp.chkerr);
UDP_STATS_INC(udp.drop);
- snmp_inc_udpinerrors();
+ 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.
@@ -495,9 +433,10 @@ chkerr:
* automatically be bound to a random port.
*
* @return lwIP error code.
- * - ERR_OK. Successful. No error occured.
+ * - 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()
@@ -505,24 +444,34 @@ chkerr:
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, ipX_2_ip(&pcb->remote_ip), pcb->remote_port);
+ return udp_sendto(pcb, p, &pcb->remote_ip, pcb->remote_port);
}
#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
-/** Same as udp_send() but with checksum
+/** @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, ipX_2_ip(&pcb->remote_ip), pcb->remote_port,
- have_chksum, chksum);
+ 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.
@@ -534,53 +483,80 @@ udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p,
*
* If the PCB already has a remote address association, it will
* be restored after the data is sent.
- *
+ *
* @return lwIP error code (@see udp_send for possible error codes)
*
* @see udp_disconnect() udp_send()
*/
err_t
udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
- ip_addr_t *dst_ip, u16_t dst_port)
+ 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);
}
-/** Same as udp_sendto(), but with checksum */
+/** @ingroup udp_raw
+ * Same as udp_sendto(), but with checksum */
err_t
-udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
+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;
- ipX_addr_t *dst_ip_route = ip_2_ipX(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_IGMP
- if (ipX_addr_ismulticast(PCB_ISIPV6(pcb), dst_ip_route)) {
- /* For multicast, find a netif based on source address. */
+ if (pcb->netif_idx != NETIF_NO_INDEX) {
+ netif = netif_get_by_index(pcb->netif_idx);
+ } else {
+#if LWIP_MULTICAST_TX_OPTIONS
+ netif = NULL;
+ if (ip_addr_ismulticast(dst_ip)) {
+ /* For IPv6, the interface to use for packets with a multicast destination
+ * is specified using an interface index. The same approach may be used for
+ * IPv4 as well, in which case it overrides the IPv4 multicast override
+ * address below. Here we have to look up the netif by going through the
+ * list, but by doing so we skip a route lookup. If the interface index has
+ * gone stale, we fall through and do the regular route lookup after all. */
+ if (pcb->mcast_ifindex != NETIF_NO_INDEX) {
+ netif = netif_get_by_index(pcb->mcast_ifindex);
+ }
+#if LWIP_IPV4
+ else
#if LWIP_IPV6
- if (PCB_ISIPV6(pcb)) {
- dst_ip_route = &pcb->local_ip;
- } else
+ if (IP_IS_V4(dst_ip))
#endif /* LWIP_IPV6 */
+ {
+ /* 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->mcast_ip4 that is used for routing. If this routing lookup
+ fails, we try regular routing as though no override was set. */
+ if (!ip4_addr_isany_val(pcb->mcast_ip4) &&
+ !ip4_addr_cmp(&pcb->mcast_ip4, IP4_ADDR_BROADCAST)) {
+ netif = ip4_route_src(ip_2_ip4(&pcb->local_ip), &pcb->mcast_ip4);
+ }
+ }
+#endif /* LWIP_IPV4 */
+ }
+
+ if (netif == NULL)
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
{
-#if LWIP_IGMP
- dst_ip_route = ip_2_ipX(&pcb->multicast_ip);
-#endif /* LWIP_IGMP */
+ /* find the outgoing network interface for this packet */
+ netif = ip_route(&pcb->local_ip, dst_ip);
}
}
-#endif /* LWIP_IPV6 || LWIP_IGMP */
-
- /* find the outgoing network interface for this packet */
- netif = ipX_route(PCB_ISIPV6(pcb), &pcb->local_ip, dst_ip_route);
/* no outgoing network interface could be found? */
if (netif == NULL) {
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to "));
- ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ip_2_ipX(dst_ip));
+ 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;
@@ -593,6 +569,7 @@ udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
}
/**
+ * @ingroup udp_raw
* Send data to a specified address using UDP.
* The netif used for sending can be specified.
*
@@ -613,7 +590,7 @@ udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
*/
err_t
udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p,
- ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif)
+ 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);
@@ -621,42 +598,121 @@ udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p,
/** Same as udp_sendto_if(), but with checksum */
err_t
-udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
+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 or multicast? */
+#if LWIP_IPV6
+ if (IP_IS_V6(dst_ip)) {
+ if (ip6_addr_isany(ip_2_ip6(&pcb->local_ip)) ||
+ ip6_addr_ismulticast(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;
- ip_addr_t *src_ip;
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 IP_SOF_BROADCAST
+#if LWIP_IPV4 && IP_SOF_BROADCAST
/* broadcast filter? */
if (!ip_get_option(pcb, SOF_BROADCAST) &&
#if LWIP_IPV6
- !PCB_ISIPV6(pcb) &&
+ IP_IS_V4(dst_ip) &&
#endif /* LWIP_IPV6 */
- ip_addr_isbroadcast(dst_ip, netif) ) {
+ 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));
+ ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
return ERR_VAL;
}
-#endif /* IP_SOF_BROADCAST */
+#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, ipX_2_ip(&pcb->local_ip), pcb->local_port);
+ 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;
}
}
+ /* packet too large to add a UDP header without causing an overflow? */
+ if ((u16_t)(p->tot_len + UDP_HLEN) < p->tot_len) {
+ return ERR_MEM;
+ }
/* not enough space to add an UDP header to first pbuf in given p chain? */
- if (pbuf_header(p, UDP_HLEN)) {
+ if (pbuf_add_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? */
@@ -681,79 +737,17 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
(q->len >= sizeof(struct udp_hdr)));
/* q now represents the packet to be sent */
udphdr = (struct udp_hdr *)q->payload;
- udphdr->src = htons(pcb->local_port);
- udphdr->dest = htons(dst_port);
+ udphdr->src = lwip_htons(pcb->local_port);
+ udphdr->dest = lwip_htons(dst_port);
/* in UDP, 0 checksum means 'no checksum' */
- udphdr->chksum = 0x0000;
+ udphdr->chksum = 0x0000;
/* Multicast Loop? */
-#if LWIP_IGMP
- if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) &&
-#if LWIP_IPV6
- (
-#if LWIP_IPV6_MLD
- (PCB_ISIPV6(pcb) &&
- ip6_addr_ismulticast(ip_2_ip6(dst_ip))) ||
-#endif /* LWIP_IPV6_MLD */
- (!PCB_ISIPV6(pcb) &&
-#else /* LWIP_IPV6 */
- ((
-#endif /* LWIP_IPV6 */
- ip_addr_ismulticast(dst_ip)))) {
+#if LWIP_MULTICAST_TX_OPTIONS
+ if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) && ip_addr_ismulticast(dst_ip)) {
q->flags |= PBUF_FLAG_MCASTLOOP;
}
-#endif /* LWIP_IGMP */
-
-
- /* PCB local address is IP_ANY_ADDR? */
-#if LWIP_IPV6
- if (PCB_ISIPV6(pcb)) {
- if (ip6_addr_isany(ipX_2_ip6(&pcb->local_ip))) {
- src_ip = ip6_2_ip(ip6_select_source_address(netif, ip_2_ip6(dst_ip)));
- if (src_ip == NULL) {
- /* No suitable source address was found. */
- if (q != p) {
- /* free the header pbuf */
- pbuf_free(q);
- /* p is still referenced by the caller, and will live on */
- }
- return ERR_RTE;
- }
- } else {
- /* use UDP PCB local IPv6 address as source address, if still valid. */
- if (netif_matches_ip6_addr(netif, ipX_2_ip6(&pcb->local_ip)) < 0) {
- /* Address isn't valid anymore. */
- if (q != p) {
- /* free the header pbuf */
- pbuf_free(q);
- /* p is still referenced by the caller, and will live on */
- }
- return ERR_RTE;
- }
- src_ip = ipX_2_ip(&pcb->local_ip);
- }
- }
- else
-#endif /* LWIP_IPV6 */
- if (ip_addr_isany(ipX_2_ip(&pcb->local_ip))) {
- /* use outgoing network interface IP address as source address */
- src_ip = &(netif->ip_addr);
- } else {
- /* check if UDP PCB local IP address is correct
- * this could be an old address if netif->ip_addr has changed */
- if (!ip_addr_cmp(ipX_2_ip(&(pcb->local_ip)), &(netif->ip_addr))) {
- /* local_ip doesn't match, drop the packet */
- if (q != p) {
- /* free the header pbuf */
- pbuf_free(q);
- q = NULL;
- /* p is still referenced by the caller, and will live on */
- }
- return ERR_VAL;
- }
- /* use UDP PCB local IP address as source address */
- src_ip = ipX_2_ip(&(pcb->local_ip));
- }
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len));
@@ -777,27 +771,29 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
chklen_hdr = 0;
chklen = q->tot_len;
}
- udphdr->len = htons(chklen_hdr);
+ 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;
- }
+ if (have_chksum) {
+ chklen = UDP_HLEN;
+ }
#endif /* LWIP_CHECKSUM_ON_COPY */
- udphdr->chksum = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), q, IP_PROTO_UDPLITE,
- q->tot_len, chklen, ip_2_ipX(src_ip), ip_2_ipX(dst_ip));
+ 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);
- }
+ 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;
+ /* chksum zero must become 0xffff, as zero means 'no checksum' */
+ if (udphdr->chksum == 0x0000) {
+ udphdr->chksum = 0xffff;
+ }
}
#endif /* CHECKSUM_GEN_UDP */
@@ -806,45 +802,54 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
#endif /* LWIP_UDPLITE */
{ /* UDP */
LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len));
- udphdr->len = htons(q->tot_len);
+ udphdr->len = lwip_htons(q->tot_len);
/* calculate checksum */
#if CHECKSUM_GEN_UDP
- /* Checksum is mandatory over IPv6. */
- if (PCB_ISIPV6(pcb) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) {
- u16_t udpchksum;
+ IF__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 = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), q, IP_PROTO_UDP,
- q->tot_len, UDP_HLEN, ip_2_ipX(src_ip), ip_2_ipX(dst_ip));
- acc = udpchksum + (u16_t)~(chksum);
- udpchksum = FOLD_U32T(acc);
- } else
+ 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 = ipX_chksum_pseudo(PCB_ISIPV6(pcb), q, IP_PROTO_UDP, q->tot_len,
- ip_2_ipX(src_ip), ip_2_ipX(dst_ip));
- }
+ {
+ 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;
+ /* chksum zero must become 0xffff, as zero means 'no checksum' */
+ if (udpchksum == 0x0000) {
+ udpchksum = 0xffff;
+ }
+ udphdr->chksum = udpchksum;
}
- 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 = ipX_output_if(PCB_ISIPV6(pcb), q, src_ip, dst_ip, pcb->ttl, pcb->tos, ip_proto, netif);
- NETIF_SET_HWADDRHINT(netif, NULL);
+ NETIF_SET_HINTS(netif, &(pcb->netif_hints));
+ err = ip_output_if_src(q, src_ip, dst_ip, ttl, pcb->tos, ip_proto, netif);
+ NETIF_RESET_HINTS(netif);
- /* TODO: must this be increased even if error occured? */
- snmp_inc_udpoutdatagrams();
+ /* @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) {
@@ -859,10 +864,11 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
}
/**
+ * @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 IP_ADDR_ANY to
+ * @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
@@ -871,20 +877,35 @@ udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
* ipaddr & port are expected to be in the same byte order as in the pcb.
*
* @return lwIP error code.
- * - ERR_OK. Successful. No error occured.
+ * - ERR_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, ip_addr_t *ipaddr, u16_t port)
+udp_bind(struct udp_pcb *pcb, const ip_addr_t *ipaddr, u16_t port)
{
struct udp_pcb *ipcb;
u8_t rebind;
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+ ip_addr_t zoned_ipaddr;
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
+
+#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 = "));
- ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE, ip_2_ipX(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;
@@ -892,36 +913,22 @@ udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
/* is this UDP PCB already on active list? */
if (pcb == ipcb) {
- /* pcb may occur at most once in active list */
- LWIP_ASSERT("rebind == 0", rebind == 0);
- /* pcb already in list, just rebind */
rebind = 1;
- }
-
- /* By default, we don't allow to bind to a port that any other udp
- PCB is alread bound to, unless *all* PCBs with that port have tha
- REUSEADDR flag set. */
-#if SO_REUSE
- else if (!ip_get_option(pcb, SOF_REUSEADDR) &&
- !ip_get_option(ipcb, SOF_REUSEADDR)) {
-#else /* SO_REUSE */
- /* port matches that of PCB in list and REUSEADDR not set -> reject */
- else {
-#endif /* SO_REUSE */
- if ((ipcb->local_port == port) && IP_PCB_IPVER_EQ(pcb, ipcb) &&
- /* IP address matches, or one is IP_ADDR_ANY? */
- (ipX_addr_isany(PCB_ISIPV6(ipcb), &(ipcb->local_ip)) ||
- ipX_addr_isany(PCB_ISIPV6(ipcb), ip_2_ipX(ipaddr)) ||
- ipX_addr_cmp(PCB_ISIPV6(ipcb), &(ipcb->local_ip), ip_2_ipX(ipaddr)))) {
- /* other PCB already binds to this local IP and port */
- LWIP_DEBUGF(UDP_DEBUG,
- ("udp_bind: local port %"U16_F" already bound by another pcb\n", port));
- return ERR_USE;
- }
+ break;
}
}
- ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->local_ip, ipaddr);
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+ /* If the given IP address should have a zone but doesn't, assign one now.
+ * This is legacy support: scope-aware callers should always provide properly
+ * zoned source addresses. Do the zone selection before the address-in-use
+ * check below; as such we have to make a temporary copy of the address. */
+ if (IP_IS_V6(ipaddr) && ip6_addr_lacks_zone(ip_2_ip6(ipaddr), IP6_UNKNOWN)) {
+ ip_addr_copy(zoned_ipaddr, *ipaddr);
+ ip6_addr_select_zone(ip_2_ip6(&zoned_ipaddr), ip_2_ip6(&zoned_ipaddr));
+ ipaddr = &zoned_ipaddr;
+ }
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
/* no port specified? */
if (port == 0) {
@@ -931,9 +938,35 @@ udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
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;
- snmp_insert_udpidx_tree(pcb);
+ mib2_udp_bind(pcb);
/* pcb not active yet? */
if (rebind == 0) {
/* place the PCB on the active list if not already there */
@@ -941,12 +974,35 @@ udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
udp_pcbs = pcb;
}
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_bind: bound to "));
- ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, &pcb->local_ip);
+ ip_addr_debug_print_val(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
+ * Bind an UDP PCB to a specific netif.
+ * After calling this function, all packets received via this PCB
+ * are guaranteed to have come in via the specified netif, and all
+ * outgoing packets will go out via the specified netif.
+ *
+ * @param pcb UDP PCB to be bound.
+ * @param netif netif to bind udp pcb to. Can be NULL.
+ *
+ * @see udp_disconnect()
+ */
+void
+udp_bind_netif(struct udp_pcb *pcb, const struct netif *netif)
+{
+ if (netif != NULL) {
+ pcb->netif_idx = netif_get_index(netif);
+ } else {
+ pcb->netif_idx = NETIF_NO_INDEX;
+ }
+}
+
+/**
+ * @ingroup udp_raw
* Connect an UDP PCB.
*
* This will associate the UDP PCB with the remote address.
@@ -964,48 +1020,37 @@ udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
* @see udp_disconnect()
*/
err_t
-udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
+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, ipX_2_ip(&pcb->local_ip), pcb->local_port);
+ err_t err = udp_bind(pcb, &pcb->local_ip, pcb->local_port);
if (err != ERR_OK) {
return err;
}
}
- ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->remote_ip, ipaddr);
+ ip_addr_set_ipaddr(&pcb->remote_ip, ipaddr);
+#if LWIP_IPV6 && LWIP_IPV6_SCOPES
+ /* If the given IP address should have a zone but doesn't, assign one now,
+ * using the bound address to make a more informed decision when possible. */
+ if (IP_IS_V6(&pcb->remote_ip) &&
+ ip6_addr_lacks_zone(ip_2_ip6(&pcb->remote_ip), IP6_UNKNOWN)) {
+ ip6_addr_select_zone(ip_2_ip6(&pcb->remote_ip), ip_2_ip6(&pcb->local_ip));
+ }
+#endif /* LWIP_IPV6 && LWIP_IPV6_SCOPES */
+
pcb->remote_port = port;
pcb->flags |= UDP_FLAGS_CONNECTED;
-/** TODO: this functionality belongs in upper layers */
-#ifdef LWIP_UDP_TODO
-#if LWIP_IPV6
- if (!PCB_ISIPV6(pcb))
-#endif /* LWIP_IPV6 */
- {
- /* Nail down local IP for netconn_addr()/getsockname() */
- if (ip_addr_isany(ipX_2_ip(&pcb->local_ip)) && !ip_addr_isany(ipX_2_ip(&pcb->remote_ip))) {
- struct netif *netif;
-
- if ((netif = ip_route(ipX_2_ip(&pcb->remote_ip))) == NULL) {
- LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n",
- ip4_addr_get_u32(ipX_2_ip(&pcb->remote_ip))));
- UDP_STATS_INC(udp.rterr);
- return ERR_RTE;
- }
- /** TODO: this will bind the udp pcb locally, to the interface which
- is used to route output packets to the remote address. However, we
- might want to accept incoming packets on any interface! */
- ipX_addr_copy(0, pcb->local_ip, netif->ip_addr);
- } else if (ip_addr_isany(ipX_2_ip(&pcb->remote_ip))) {
- ipX_addr_set_any(0, &pcb->local_ip);
- }
- }
-#endif
+
LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_connect: connected to "));
- ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
- &pcb->remote_ip);
+ ip_addr_debug_print_val(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. */
@@ -1022,6 +1067,7 @@ udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
}
/**
+ * @ingroup udp_raw
* Disconnect a UDP PCB
*
* @param pcb the udp pcb to disconnect.
@@ -1030,18 +1076,28 @@ void
udp_disconnect(struct udp_pcb *pcb)
{
/* reset remote address association */
- ipX_addr_set_any(PCB_ISIPV6(pcb), &pcb->remote_ip);
+#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;
+ pcb->netif_idx = NETIF_NO_INDEX;
/* mark PCB as unconnected */
- pcb->flags &= ~UDP_FLAGS_CONNECTED;
+ udp_clear_flags(pcb, 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 wich to set the recv callback
+ * @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
*/
@@ -1049,11 +1105,12 @@ void
udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg)
{
/* remember recv() callback and user data */
- pcb->recv.ip4 = recv;
+ pcb->recv = 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
@@ -1066,7 +1123,7 @@ udp_remove(struct udp_pcb *pcb)
{
struct udp_pcb *pcb2;
- snmp_delete_udpidx_tree(pcb);
+ mib2_udp_unbind(pcb);
/* pcb to be removed is first in list? */
if (udp_pcbs == pcb) {
/* make list start at 2nd pcb */
@@ -1078,6 +1135,7 @@ udp_remove(struct udp_pcb *pcb)
if (pcb2->next != NULL && pcb2->next == pcb) {
/* remove pcb from list */
pcb2->next = pcb->next;
+ break;
}
}
}
@@ -1085,6 +1143,7 @@ udp_remove(struct udp_pcb *pcb)
}
/**
+ * @ingroup udp_raw
* Create a UDP PCB.
*
* @return The UDP PCB which was created. NULL if the PCB data structure
@@ -1105,28 +1164,61 @@ udp_new(void)
/* 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;
}
-#if LWIP_IPV6
/**
- * Create a UDP PCB for IPv6.
+ * @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_ip6(void)
+udp_new_ip_type(u8_t type)
{
struct udp_pcb *pcb;
pcb = udp_new();
- ip_set_v6(pcb, 1);
+#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;
}
-#endif /* LWIP_IPV6 */
+
+/** 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
/**
@@ -1140,10 +1232,10 @@ udp_debug_print(struct udp_hdr *udphdr)
LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n"));
LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n",
- ntohs(udphdr->src), ntohs(udphdr->dest)));
+ lwip_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",
- ntohs(udphdr->len), ntohs(udphdr->chksum)));
+ lwip_ntohs(udphdr->len), lwip_ntohs(udphdr->chksum)));
LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
}
#endif /* UDP_DEBUG */
diff --git a/lwip/src/include/compat/posix/arpa/inet.h b/lwip/src/include/compat/posix/arpa/inet.h
new file mode 100644
index 0000000..0ed9baf
--- /dev/null
+++ b/lwip/src/include/compat/posix/arpa/inet.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/lwip/src/include/compat/posix/net/if.h b/lwip/src/include/compat/posix/net/if.h
new file mode 100644
index 0000000..6b8e63a
--- /dev/null
+++ b/lwip/src/include/compat/posix/net/if.h
@@ -0,0 +1,36 @@
+/**
+ * @file
+ * This file is a posix wrapper for lwip/if_api.h.
+ */
+
+/*
+ * Copyright (c) 2017 Joel Cunningham, Garmin International, Inc. <joel.cunningham@garmin.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.
+ *
+ */
+
+#include "lwip/if_api.h"
diff --git a/lwip/src/include/posix/netdb.h b/lwip/src/include/compat/posix/netdb.h
index 7134032..12d4c7f 100644
--- a/lwip/src/include/posix/netdb.h
+++ b/lwip/src/include/compat/posix/netdb.h
@@ -4,7 +4,7 @@
*/
/*
- * Redistribution and use in source and binary forms, with or without modification,
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -13,17 +13,17 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
diff --git a/lwip/src/include/posix/sys/socket.h b/lwip/src/include/compat/posix/sys/socket.h
index f7c7066..0ed9baf 100644
--- a/lwip/src/include/posix/sys/socket.h
+++ b/lwip/src/include/compat/posix/sys/socket.h
@@ -4,7 +4,7 @@
*/
/*
- * Redistribution and use in source and binary forms, with or without modification,
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -13,17 +13,17 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
diff --git a/lwip/src/include/compat/stdc/errno.h b/lwip/src/include/compat/stdc/errno.h
new file mode 100644
index 0000000..98a9aec
--- /dev/null
+++ b/lwip/src/include/compat/stdc/errno.h
@@ -0,0 +1,33 @@
+/**
+ * @file
+ * This file is a posix/stdc 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/lwip/src/include/ipv4/lwip/autoip.h b/lwip/src/include/ipv4/lwip/autoip.h
deleted file mode 100644
index e62b72e..0000000
--- a/lwip/src/include/ipv4/lwip/autoip.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/**
- * @file
- *
- * AutoIP Automatic LinkLocal IP Configuration
- */
-
-/*
- *
- * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Dominik Spies <kontakt@dspies.de>
- *
- * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
- * with RFC 3927.
- *
- *
- * Please coordinate changes and requests with Dominik Spies
- * <kontakt@dspies.de>
- */
-
-#ifndef __LWIP_AUTOIP_H__
-#define __LWIP_AUTOIP_H__
-
-#include "lwip/opt.h"
-
-#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/netif.h"
-#include "lwip/udp.h"
-#include "netif/etharp.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* AutoIP Timing */
-#define AUTOIP_TMR_INTERVAL 100
-#define AUTOIP_TICKS_PER_SECOND (1000 / AUTOIP_TMR_INTERVAL)
-
-/* RFC 3927 Constants */
-#define PROBE_WAIT 1 /* second (initial random delay) */
-#define PROBE_MIN 1 /* second (minimum delay till repeated probe) */
-#define PROBE_MAX 2 /* seconds (maximum delay till repeated probe) */
-#define PROBE_NUM 3 /* (number of probe packets) */
-#define ANNOUNCE_NUM 2 /* (number of announcement packets) */
-#define ANNOUNCE_INTERVAL 2 /* seconds (time between announcement packets) */
-#define ANNOUNCE_WAIT 2 /* seconds (delay before announcing) */
-#define MAX_CONFLICTS 10 /* (max conflicts before rate limiting) */
-#define RATE_LIMIT_INTERVAL 60 /* seconds (delay between successive attempts) */
-#define DEFEND_INTERVAL 10 /* seconds (min. wait between defensive ARPs) */
-
-/* AutoIP client states */
-#define AUTOIP_STATE_OFF 0
-#define AUTOIP_STATE_PROBING 1
-#define AUTOIP_STATE_ANNOUNCING 2
-#define AUTOIP_STATE_BOUND 3
-
-struct autoip
-{
- ip_addr_t llipaddr; /* the currently selected, probed, announced or used LL IP-Address */
- u8_t state; /* current AutoIP state machine state */
- u8_t sent_num; /* sent number of probes or announces, dependent on state */
- u16_t ttw; /* ticks to wait, tick is AUTOIP_TMR_INTERVAL long */
- u8_t lastconflict; /* ticks until a conflict can be solved by defending */
- u8_t tried_llipaddr; /* total number of probed/used Link Local IP-Addresses */
-};
-
-
-#define autoip_init() /* Compatibility define, no init needed. */
-
-/** Set a struct autoip allocated by the application to work with */
-void autoip_set_struct(struct netif *netif, struct autoip *autoip);
-
-/** Start AutoIP client */
-err_t autoip_start(struct netif *netif);
-
-/** Stop AutoIP client */
-err_t autoip_stop(struct netif *netif);
-
-/** Handles every incoming ARP Packet, called by etharp_arp_input */
-void autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr);
-
-/** Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds */
-void autoip_tmr(void);
-
-/** Handle a possible change in the network configuration */
-void autoip_network_changed(struct netif *netif);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LWIP_AUTOIP */
-
-#endif /* __LWIP_AUTOIP_H__ */
diff --git a/lwip/src/include/ipv4/lwip/inet.h b/lwip/src/include/ipv4/lwip/inet.h
deleted file mode 100644
index 7bff49b..0000000
--- a/lwip/src/include/ipv4/lwip/inet.h
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-#ifndef __LWIP_INET_H__
-#define __LWIP_INET_H__
-
-#include "lwip/opt.h"
-#include "lwip/def.h"
-#include "lwip/ip_addr.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/** For compatibility with BSD code */
-struct in_addr {
- u32_t s_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
-
-/* Definitions of the bits in an Internet address integer.
-
- On subnets, host and network parts are found according to
- the subnet mask, not these masks. */
-#define IN_CLASSA(a) IP_CLASSA(a)
-#define IN_CLASSA_NET IP_CLASSA_NET
-#define IN_CLASSA_NSHIFT IP_CLASSA_NSHIFT
-#define IN_CLASSA_HOST IP_CLASSA_HOST
-#define IN_CLASSA_MAX IP_CLASSA_MAX
-
-#define IN_CLASSB(b) IP_CLASSB(b)
-#define IN_CLASSB_NET IP_CLASSB_NET
-#define IN_CLASSB_NSHIFT IP_CLASSB_NSHIFT
-#define IN_CLASSB_HOST IP_CLASSB_HOST
-#define IN_CLASSB_MAX IP_CLASSB_MAX
-
-#define IN_CLASSC(c) IP_CLASSC(c)
-#define IN_CLASSC_NET IP_CLASSC_NET
-#define IN_CLASSC_NSHIFT IP_CLASSC_NSHIFT
-#define IN_CLASSC_HOST IP_CLASSC_HOST
-#define IN_CLASSC_MAX IP_CLASSC_MAX
-
-#define IN_CLASSD(d) IP_CLASSD(d)
-#define IN_CLASSD_NET IP_CLASSD_NET /* These ones aren't really */
-#define IN_CLASSD_NSHIFT IP_CLASSD_NSHIFT /* net and host fields, but */
-#define IN_CLASSD_HOST IP_CLASSD_HOST /* routing needn't know. */
-#define IN_CLASSD_MAX IP_CLASSD_MAX
-
-#define IN_MULTICAST(a) IP_MULTICAST(a)
-
-#define IN_EXPERIMENTAL(a) IP_EXPERIMENTAL(a)
-#define IN_BADCLASS(a) IP_BADCLASS(a)
-
-#define IN_LOOPBACKNET IP_LOOPBACKNET
-
-#define inet_addr_from_ipaddr(target_inaddr, source_ipaddr) ((target_inaddr)->s_addr = ip4_addr_get_u32(source_ipaddr))
-#define inet_addr_to_ipaddr(target_ipaddr, source_inaddr) (ip4_addr_set_u32(target_ipaddr, (source_inaddr)->s_addr))
-/* ATTENTION: the next define only works because both s_addr and ip_addr_t are an u32_t effectively! */
-#define inet_addr_to_ipaddr_p(target_ipaddr_p, source_inaddr) ((target_ipaddr_p) = (ip_addr_t*)&((source_inaddr)->s_addr))
-
-/* directly map this to the lwip internal functions */
-#define inet_addr(cp) ipaddr_addr(cp)
-#define inet_aton(cp, addr) ipaddr_aton(cp, (ip_addr_t*)addr)
-#define inet_ntoa(addr) ipaddr_ntoa((ip_addr_t*)&(addr))
-#define inet_ntoa_r(addr, buf, buflen) ipaddr_ntoa_r((ip_addr_t*)&(addr), buf, buflen)
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* __LWIP_INET_H__ */
diff --git a/lwip/src/include/ipv6/lwip/icmp6.h b/lwip/src/include/ipv6/lwip/icmp6.h
deleted file mode 100644
index 74bfdbe..0000000
--- a/lwip/src/include/ipv6/lwip/icmp6.h
+++ /dev/null
@@ -1,152 +0,0 @@
-/**
- * @file
- *
- * IPv6 version of ICMP, as per RFC 4443.
- */
-
-/*
- * Copyright (c) 2010 Inico Technologies Ltd.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Ivan Delamer <delamer@inicotech.com>
- *
- *
- * Please coordinate changes and requests with Ivan Delamer
- * <delamer@inicotech.com>
- */
-#ifndef __LWIP_ICMP6_H__
-#define __LWIP_ICMP6_H__
-
-#include "lwip/opt.h"
-#include "lwip/pbuf.h"
-#include "lwip/ip6_addr.h"
-#include "lwip/netif.h"
-
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-enum icmp6_type {
- ICMP6_TYPE_DUR = 1, /* Destination unreachable */
- ICMP6_TYPE_PTB = 2, /* Packet too big */
- ICMP6_TYPE_TE = 3, /* Time exceeded */
- ICMP6_TYPE_PP = 4, /* Parameter problem */
- ICMP6_TYPE_PE1 = 100, /* Private experimentation */
- ICMP6_TYPE_PE2 = 101, /* Private experimentation */
- ICMP6_TYPE_RSV_ERR = 127, /* Reserved for expansion of error messages */
-
- ICMP6_TYPE_EREQ = 128, /* Echo request */
- ICMP6_TYPE_EREP = 129, /* Echo reply */
- ICMP6_TYPE_MLQ = 130, /* Multicast listener query */
- ICMP6_TYPE_MLR = 131, /* Multicast listener report */
- ICMP6_TYPE_MLD = 132, /* Multicast listener done */
- ICMP6_TYPE_RS = 133, /* Router solicitation */
- ICMP6_TYPE_RA = 134, /* Router advertisement */
- ICMP6_TYPE_NS = 135, /* Neighbor solicitation */
- ICMP6_TYPE_NA = 136, /* Neighbor advertisement */
- ICMP6_TYPE_RD = 137, /* Redirect */
- ICMP6_TYPE_MRA = 151, /* Multicast router advertisement */
- ICMP6_TYPE_MRS = 152, /* Multicast router solicitation */
- ICMP6_TYPE_MRT = 153, /* Multicast router termination */
- ICMP6_TYPE_PE3 = 200, /* Private experimentation */
- ICMP6_TYPE_PE4 = 201, /* Private experimentation */
- ICMP6_TYPE_RSV_INF = 255 /* Reserved for expansion of informational messages */
-};
-
-enum icmp6_dur_code {
- ICMP6_DUR_NO_ROUTE = 0, /* No route to destination */
- ICMP6_DUR_PROHIBITED = 1, /* Communication with destination administratively prohibited */
- ICMP6_DUR_SCOPE = 2, /* Beyond scope of source address */
- ICMP6_DUR_ADDRESS = 3, /* Address unreachable */
- ICMP6_DUR_PORT = 4, /* Port unreachable */
- ICMP6_DUR_POLICY = 5, /* Source address failed ingress/egress policy */
- ICMP6_DUR_REJECT_ROUTE = 6 /* Reject route to destination */
-};
-
-enum icmp6_te_code {
- ICMP6_TE_HL = 0, /* Hop limit exceeded in transit */
- ICMP6_TE_FRAG = 1 /* Fragment reassembly time exceeded */
-};
-
-enum icmp6_pp_code {
- ICMP6_PP_FIELD = 0, /* Erroneous header field encountered */
- ICMP6_PP_HEADER = 1, /* Unrecognized next header type encountered */
- ICMP6_PP_OPTION = 2 /* Unrecognized IPv6 option encountered */
-};
-
-/** This is the standard ICMP6 header. */
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct icmp6_hdr {
- PACK_STRUCT_FIELD(u8_t type);
- PACK_STRUCT_FIELD(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_FIELD(u8_t type);
- PACK_STRUCT_FIELD(u8_t code);
- PACK_STRUCT_FIELD(u16_t chksum);
- PACK_STRUCT_FIELD(u16_t id);
- PACK_STRUCT_FIELD(u16_t seqno);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
-
-
-#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_ICMP6_H__ */
diff --git a/lwip/src/include/ipv6/lwip/inet6.h b/lwip/src/include/ipv6/lwip/inet6.h
deleted file mode 100644
index dbf98df..0000000
--- a/lwip/src/include/ipv6/lwip/inet6.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/**
- * @file
- *
- * INET v6 addresses.
- */
-
-/*
- * Copyright (c) 2010 Inico Technologies Ltd.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Ivan Delamer <delamer@inicotech.com>
- *
- *
- * Please coordinate changes and requests with Ivan Delamer
- * <delamer@inicotech.com>
- */
-#ifndef __LWIP_INET6_H__
-#define __LWIP_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.u32_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_INET6_H__ */
-
diff --git a/lwip/src/include/ipv6/lwip/ip6_addr.h b/lwip/src/include/ipv6/lwip/ip6_addr.h
deleted file mode 100644
index 89b5b81..0000000
--- a/lwip/src/include/ipv6/lwip/ip6_addr.h
+++ /dev/null
@@ -1,286 +0,0 @@
-/**
- * @file
- *
- * IPv6 addresses.
- */
-
-/*
- * Copyright (c) 2010 Inico Technologies Ltd.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Ivan Delamer <delamer@inicotech.com>
- *
- * Structs and macros for handling IPv6 addresses.
- *
- * Please coordinate changes and requests with Ivan Delamer
- * <delamer@inicotech.com>
- */
-#ifndef __LWIP_IP6_ADDR_H__
-#define __LWIP_IP6_ADDR_H__
-
-#include "lwip/opt.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];
-};
-
-/* 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
-
-/** ip6_addr_t uses a struct for convenience only, so that the same defines can
- * operate both on ip6_addr_t as well as on ip6_addr_p_t. */
-typedef struct ip6_addr ip6_addr_t;
-typedef struct ip6_addr_packed ip6_addr_p_t;
-
-
-/** IP6_ADDR_ANY can be used as a fixed IPv6 address
- * for the wildcard
- */
-extern const ip6_addr_t ip6_addr_any;
-#define IP6_ADDR_ANY ((ip6_addr_t *)&ip6_addr_any)
-
-
-
-
-#if BYTE_ORDER == BIG_ENDIAN
-/** Set an IPv6 partial address given by byte-parts. */
-#define IP6_ADDR(ip6addr, index, a,b,c,d) \
- (ip6addr)->addr[index] = ((u32_t)((a) & 0xff) << 24) | \
- ((u32_t)((b) & 0xff) << 16) | \
- ((u32_t)((c) & 0xff) << 8) | \
- (u32_t)((d) & 0xff)
-#else
-/** Set an IPv6 partial address given by byte-parts.
-Little-endian version, stored in network order (no htonl). */
-#define IP6_ADDR(ip6addr, index, a,b,c,d) \
- (ip6addr)->addr[index] = ((u32_t)((d) & 0xff) << 24) | \
- ((u32_t)((c) & 0xff) << 16) | \
- ((u32_t)((b) & 0xff) << 8) | \
- (u32_t)((a) & 0xff)
-#endif
-
-/** Access address in 16-bit block */
-#define IP6_ADDR_BLOCK1(ip6addr) ((u16_t)(htonl((ip6addr)->addr[0]) >> 16) & 0xffff)
-#define IP6_ADDR_BLOCK2(ip6addr) ((u16_t)(htonl((ip6addr)->addr[0])) & 0xffff)
-#define IP6_ADDR_BLOCK3(ip6addr) ((u16_t)(htonl((ip6addr)->addr[1]) >> 16) & 0xffff)
-#define IP6_ADDR_BLOCK4(ip6addr) ((u16_t)(htonl((ip6addr)->addr[1])) & 0xffff)
-#define IP6_ADDR_BLOCK5(ip6addr) ((u16_t)(htonl((ip6addr)->addr[2]) >> 16) & 0xffff)
-#define IP6_ADDR_BLOCK6(ip6addr) ((u16_t)(htonl((ip6addr)->addr[2])) & 0xffff)
-#define IP6_ADDR_BLOCK7(ip6addr) ((u16_t)(htonl((ip6addr)->addr[3]) >> 16) & 0xffff)
-#define IP6_ADDR_BLOCK8(ip6addr) ((u16_t)(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 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 : htonl((src)->addr[0]); \
- (dest)->addr[1] = (src) == NULL ? 0 : htonl((src)->addr[1]); \
- (dest)->addr[2] = (src) == NULL ? 0 : htonl((src)->addr[2]); \
- (dest)->addr[3] = (src) == NULL ? 0 : 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) (htonl((ip6addr)->addr[2]) & 0x0000ffffUL)
-
-#define ip6_addr_isany(ip6addr) (((ip6addr) == NULL) || \
- (((ip6addr)->addr[0] == 0) && \
- ((ip6addr)->addr[1] == 0) && \
- ((ip6addr)->addr[2] == 0) && \
- ((ip6addr)->addr[3] == 0)))
-
-
-#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_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) ((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(0xffff0000UL)) == PP_HTONL(0xff010000UL))
-#define ip6_addr_ismulticast_linklocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff020000UL))
-#define ip6_addr_ismulticast_adminlocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff040000UL))
-#define ip6_addr_ismulticast_sitelocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff050000UL))
-#define ip6_addr_ismulticast_orglocal(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xff080000UL))
-#define ip6_addr_ismulticast_global(ip6addr) (((ip6addr)->addr[0] & PP_HTONL(0xffff0000UL)) == 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] = htonl(0xff000000UL | (htonl(if_id) & 0x00ffffffUL));}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] == htonl(0xff000000UL | (htonl((sn_addr)->addr[3]) & 0x00ffffffUL))))
-
-/* 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
-#define IP6_ADDR_PREFERRED 0x30
-#define IP6_ADDR_DEPRECATED 0x50
-
-#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(debug, ipaddr) \
- LWIP_DEBUGF(debug, ("%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F, \
- ipaddr != NULL ? IP6_ADDR_BLOCK1(ipaddr) : 0, \
- ipaddr != NULL ? IP6_ADDR_BLOCK2(ipaddr) : 0, \
- ipaddr != NULL ? IP6_ADDR_BLOCK3(ipaddr) : 0, \
- ipaddr != NULL ? IP6_ADDR_BLOCK4(ipaddr) : 0, \
- ipaddr != NULL ? IP6_ADDR_BLOCK5(ipaddr) : 0, \
- ipaddr != NULL ? IP6_ADDR_BLOCK6(ipaddr) : 0, \
- ipaddr != NULL ? IP6_ADDR_BLOCK7(ipaddr) : 0, \
- ipaddr != NULL ? IP6_ADDR_BLOCK8(ipaddr) : 0))
-
-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_IP6_ADDR_H__ */
diff --git a/lwip/src/include/lwip/altcp.h b/lwip/src/include/lwip/altcp.h
new file mode 100644
index 0000000..37c9c5d
--- /dev/null
+++ b/lwip/src/include/lwip/altcp.h
@@ -0,0 +1,187 @@
+/**
+ * @file
+ * Application layered TCP connection API (to be used from TCPIP thread)\n
+ * This interface mimics the tcp callback API to the application while preventing
+ * direct linking (much like virtual functions).
+ * This way, an application can make use of other application layer protocols
+ * on top of TCP without knowing the details (e.g. TLS, proxy connection).
+ *
+ * This file contains the generic API.
+ */
+
+/*
+ * Copyright (c) 2017 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 <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_H
+#define LWIP_HDR_ALTCP_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/tcpbase.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct altcp_pcb;
+struct altcp_functions;
+
+typedef err_t (*altcp_accept_fn)(void *arg, struct altcp_pcb *new_conn, err_t err);
+typedef err_t (*altcp_connected_fn)(void *arg, struct altcp_pcb *conn, err_t err);
+typedef err_t (*altcp_recv_fn)(void *arg, struct altcp_pcb *conn, struct pbuf *p, err_t err);
+typedef err_t (*altcp_sent_fn)(void *arg, struct altcp_pcb *conn, u16_t len);
+typedef err_t (*altcp_poll_fn)(void *arg, struct altcp_pcb *conn);
+typedef void (*altcp_err_fn)(void *arg, err_t err);
+
+
+struct altcp_pcb {
+ const struct altcp_functions *fns;
+ struct altcp_pcb *inner_conn;
+ void *arg;
+ void *state;
+ /* application callbacks */
+ altcp_accept_fn accept;
+ altcp_connected_fn connected;
+ altcp_recv_fn recv;
+ altcp_sent_fn sent;
+ altcp_poll_fn poll;
+ altcp_err_fn err;
+ u8_t pollinterval;
+};
+
+void altcp_arg(struct altcp_pcb *conn, void *arg);
+void altcp_accept(struct altcp_pcb *conn, altcp_accept_fn accept);
+void altcp_recv(struct altcp_pcb *conn, altcp_recv_fn recv);
+void altcp_sent(struct altcp_pcb *conn, altcp_sent_fn sent);
+void altcp_poll(struct altcp_pcb *conn, altcp_poll_fn poll, u8_t interval);
+void altcp_err(struct altcp_pcb *conn, altcp_err_fn err);
+
+void altcp_recved(struct altcp_pcb *conn, u16_t len);
+err_t altcp_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port);
+err_t altcp_connect(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected);
+
+/* return conn for source code compatibility to tcp callback API only */
+struct altcp_pcb *altcp_listen_with_backlog_and_err(struct altcp_pcb *conn, u8_t backlog, err_t *err);
+#define altcp_listen_with_backlog(conn, backlog) altcp_listen_with_backlog_and_err(conn, backlog, NULL)
+/** @ingroup altcp */
+#define altcp_listen(conn) altcp_listen_with_backlog_and_err(conn, TCP_DEFAULT_LISTEN_BACKLOG, NULL)
+
+void altcp_abort(struct altcp_pcb *conn);
+err_t altcp_close(struct altcp_pcb *conn);
+err_t altcp_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx);
+
+err_t altcp_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags);
+err_t altcp_output(struct altcp_pcb *conn);
+
+u16_t altcp_mss(struct altcp_pcb *conn);
+u16_t altcp_sndbuf(struct altcp_pcb *conn);
+u16_t altcp_sndqueuelen(struct altcp_pcb *conn);
+void altcp_nagle_disable(struct altcp_pcb *conn);
+void altcp_nagle_enable(struct altcp_pcb *conn);
+int altcp_nagle_disabled(struct altcp_pcb *conn);
+
+void altcp_setprio(struct altcp_pcb *conn, u8_t prio);
+
+err_t altcp_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port);
+ip_addr_t *altcp_get_ip(struct altcp_pcb *conn, int local);
+u16_t altcp_get_port(struct altcp_pcb *conn, int local);
+
+#ifdef LWIP_DEBUG
+enum tcp_state altcp_dbg_get_tcp_state(struct altcp_pcb *conn);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#else /* LWIP_ALTCP */
+
+/* ALTCP disabled, define everything to link against tcp callback API (e.g. to get a small non-ssl httpd) */
+
+#include "lwip/tcp.h"
+
+#define altcp_accept_fn tcp_accept_fn
+#define altcp_connected_fn tcp_connected_fn
+#define altcp_recv_fn tcp_recv_fn
+#define altcp_sent_fn tcp_sent_fn
+#define altcp_poll_fn tcp_poll_fn
+#define altcp_err_fn tcp_err_fn
+
+#define altcp_pcb tcp_pcb
+#define altcp_tcp_new_ip_type tcp_new_ip_type
+#define altcp_tcp_new tcp_new
+#define altcp_tcp_new_ip6 tcp_new_ip6
+
+#define altcp_arg tcp_arg
+#define altcp_accept tcp_accept
+#define altcp_recv tcp_recv
+#define altcp_sent tcp_sent
+#define altcp_poll tcp_poll
+#define altcp_err tcp_err
+
+#define altcp_recved tcp_recved
+#define altcp_bind tcp_bind
+#define altcp_connect tcp_connect
+
+#define altcp_listen_with_backlog_and_err tcp_listen_with_backlog_and_err
+#define altcp_listen_with_backlog tcp_listen_with_backlog
+#define altcp_listen tcp_listen
+
+#define altcp_abort tcp_abort
+#define altcp_close tcp_close
+#define altcp_shutdown tcp_shutdown
+
+#define altcp_write tcp_write
+#define altcp_output tcp_output
+
+#define altcp_mss tcp_mss
+#define altcp_sndbuf tcp_sndbuf
+#define altcp_sndqueuelen tcp_sndqueuelen
+#define altcp_nagle_disable tcp_nagle_disable
+#define altcp_nagle_enable tcp_nagle_enable
+#define altcp_nagle_disabled tcp_nagle_disabled
+#define altcp_setprio tcp_setprio
+
+#define altcp_get_tcp_addrinfo tcp_get_tcp_addrinfo
+#define altcp_get_ip(pcb, local) ((local) ? (&(pcb)->local_ip) : (&(pcb)->remote_ip))
+
+#ifdef LWIP_DEBUG
+#define altcp_dbg_get_tcp_state tcp_dbg_get_tcp_state
+#endif
+
+#endif /* LWIP_ALTCP */
+
+#endif /* LWIP_HDR_ALTCP_H */
diff --git a/lwip/src/include/lwip/altcp_tcp.h b/lwip/src/include/lwip/altcp_tcp.h
new file mode 100644
index 0000000..3bccd63
--- /dev/null
+++ b/lwip/src/include/lwip/altcp_tcp.h
@@ -0,0 +1,70 @@
+/**
+ * @file
+ * Application layered TCP connection API (to be used from TCPIP thread)\n
+ * This interface mimics the tcp callback API to the application while preventing
+ * direct linking (much like virtual functions).
+ * This way, an application can make use of other application layer protocols
+ * on top of TCP without knowing the details (e.g. TLS, proxy connection).
+ *
+ * This file contains the base implementation calling into tcp.
+ */
+
+/*
+ * Copyright (c) 2017 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 <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_TCP_H
+#define LWIP_HDR_ALTCP_TCP_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/altcp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct altcp_pcb *altcp_tcp_new_ip_type(u8_t ip_type);
+
+#define altcp_tcp_new() altcp_tcp_new_ip_type(IPADDR_TYPE_V4)
+#define altcp_tcp_new_ip6() altcp_tcp_new_ip_type(IPADDR_TYPE_V6)
+
+struct tcp_pcb;
+struct altcp_pcb *altcp_tcp_wrap(struct tcp_pcb *tpcb);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_ALTCP */
+
+#endif /* LWIP_HDR_ALTCP_TCP_H */
diff --git a/lwip/src/include/lwip/altcp_tls.h b/lwip/src/include/lwip/altcp_tls.h
new file mode 100644
index 0000000..b9fb31d
--- /dev/null
+++ b/lwip/src/include/lwip/altcp_tls.h
@@ -0,0 +1,97 @@
+/**
+ * @file
+ * Application layered TCP/TLS connection API (to be used from TCPIP thread)
+ *
+ * @defgroup altcp_tls TLS layer
+ * @ingroup altcp
+ * This file contains function prototypes for a TLS layer.
+ * A port to ARM mbedtls is provided in the apps/ tree
+ * (LWIP_ALTCP_TLS_MBEDTLS option).
+ */
+
+/*
+ * Copyright (c) 2017 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 <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_TLS_H
+#define LWIP_HDR_ALTCP_TLS_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#if LWIP_ALTCP_TLS
+
+#include "lwip/altcp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** @ingroup altcp_tls
+ * ALTCP_TLS configuration handle, content depends on port (e.g. mbedtls)
+ */
+struct altcp_tls_config;
+
+/** @ingroup altcp_tls
+ * Create an ALTCP_TLS server configuration handle
+ */
+struct altcp_tls_config *altcp_tls_create_config_server_privkey_cert(const u8_t *privkey, size_t privkey_len,
+ const u8_t *privkey_pass, size_t privkey_pass_len,
+ const u8_t *cert, size_t cert_len);
+
+/** @ingroup altcp_tls
+ * Create an ALTCP_TLS client configuration handle
+ */
+struct altcp_tls_config *altcp_tls_create_config_client(const u8_t *cert, size_t cert_len);
+
+/** @ingroup altcp_tls
+ * Free an ALTCP_TLS configuration handle
+ */
+void altcp_tls_free_config(struct altcp_tls_config *conf);
+
+/** @ingroup altcp_tls
+ * Create new ALTCP_TLS layer
+ */
+struct altcp_pcb *altcp_tls_new(struct altcp_tls_config* config, struct altcp_pcb *inner_pcb);
+
+/** @ingroup altcp_tls
+ * Return pointer to internal TLS context so application can tweak it.
+ * Real type depends on port (e.g. mbedtls)
+ */
+void *altcp_tls_context (struct altcp_pcb *conn);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_ALTCP_TLS */
+#endif /* LWIP_ALTCP */
+#endif /* LWIP_HDR_ALTCP_TLS_H */
diff --git a/lwip/src/include/lwip/api.h b/lwip/src/include/lwip/api.h
index ac58eeb..f6175ff 100644
--- a/lwip/src/include/lwip/api.h
+++ b/lwip/src/include/lwip/api.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,33 +16,34 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_API_H__
-#define __LWIP_API_H__
+#ifndef LWIP_HDR_API_H
+#define LWIP_HDR_API_H
#include "lwip/opt.h"
-#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
-
-#include <stddef.h> /* for size_t */
+#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"
@@ -52,24 +58,20 @@ extern "C" {
*/
/* 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
+#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
+#define NETCONN_NOAUTORCVD 0x08 /* prevent netconn_recv_data_tcp() from updating the tcp window - must be done manually via netconn_tcp_recvd() */
/* Flags for struct netconn.flags (u8_t) */
-/** TCP: when data passed to netconn_write doesn't fit into the send buffer,
- this temporarily stores whether to wake up the original application task
- if data couldn't be sent in the first try. */
-#define NETCONN_FLAG_WRITE_DELAYED 0x01
+/** This netconn had an error, don't block on recvmbox/acceptmbox any more */
+#define NETCONN_FLAG_MBOXCLOSED 0x01
/** Should this netconn avoid blocking? */
#define NETCONN_FLAG_NON_BLOCKING 0x02
/** Was the last connect action a non-blocking one? */
#define NETCONN_FLAG_IN_NONBLOCKING_CONNECT 0x04
-/** If this is set, a TCP netconn must call netconn_recved() to update
- the TCP receive window (done automatically if not set). */
-#define NETCONN_FLAG_NO_AUTO_RECVED 0x08
/** If a nonblocking write has been rejected before, poll_tcp needs to
check if the netconn is writable again */
#define NETCONN_FLAG_CHECK_WRITESPACE 0x10
@@ -79,6 +81,10 @@ extern "C" {
dual-stack usage by default. */
#define NETCONN_FLAG_IPV6_V6ONLY 0x20
#endif /* LWIP_IPV6 */
+#if LWIP_NETBUF_RECVINFO
+/** Received packet info will be recorded for this netconn */
+#define NETCONN_FLAG_PKTINFO 0x40
+#endif /* LWIP_NETBUF_RECVINFO */
/* Helpers to process several netconn_types by the same code */
@@ -86,36 +92,47 @@ extern "C" {
#define NETCONNTYPE_DATAGRAM(t) ((t)&0xE0)
#if LWIP_IPV6
#define NETCONN_TYPE_IPV6 0x08
-#define NETCONNTYPE_ISIPV6(t) ((t)&0x08)
-#define NETCONNTYPE_ISUDPLITE(t) (((t)&0xF7) == NETCONN_UDPLITE)
-#define NETCONNTYPE_ISUDPNOCHKSUM(t) (((t)&0xF7) == NETCONN_UDPNOCHKSUM)
+#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 */
-/** Protocol family and type of the netconn */
+/** @ingroup netconn_common
+ * Protocol family and type of the netconn
+ */
enum netconn_type {
NETCONN_INVALID = 0,
- /* NETCONN_TCP Group */
+ /** TCP IPv4 */
NETCONN_TCP = 0x10,
#if LWIP_IPV6
+ /** TCP IPv6 */
NETCONN_TCP_IPV6 = NETCONN_TCP | NETCONN_TYPE_IPV6 /* 0x18 */,
#endif /* LWIP_IPV6 */
- /* NETCONN_UDP Group */
+ /** 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 */
- /* NETCONN_RAW Group */
+
+ /** Raw connection IPv4 */
NETCONN_RAW = 0x40
#if LWIP_IPV6
- ,
- NETCONN_RAW_IPV6 = NETCONN_RAW | NETCONN_TYPE_IPV6 /* 0x48 */
+ /** 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 */
};
@@ -129,7 +146,32 @@ enum netconn_state {
NETCONN_CLOSE
};
-/** Use to inform the callback function about changes */
+/** 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,
@@ -146,13 +188,22 @@ enum netconn_igmp {
};
#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_msg;
+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);
@@ -170,10 +221,12 @@ struct netconn {
struct udp_pcb *udp;
struct raw_pcb *raw;
} pcb;
- /** the last error this netconn had */
- err_t last_err;
- /** sem that is used to synchroneously execute functions in the core context */
+ /** the last asynchronous unreported error this netconn had */
+ err_t pending_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;
@@ -188,13 +241,13 @@ struct netconn {
#endif /* LWIP_SOCKET */
#if LWIP_SO_SNDTIMEO
/** timeout to wait for sending data (which means enqueueing data for sending
- in internal buffers) */
+ in internal buffers) in milliseconds */
s32_t send_timeout;
#endif /* LWIP_SO_RCVTIMEO */
#if LWIP_SO_RCVTIMEO
- /** timeout to wait for new data to be received
+ /** timeout in milliseconds to wait for new data to be received
(or connections to arrive for listening netconns) */
- int recv_timeout;
+ u32_t recv_timeout;
#endif /* LWIP_SO_RCVTIMEO */
#if LWIP_SO_RCVBUF
/** maximum amount of bytes queued in recvmbox
@@ -203,43 +256,49 @@ struct netconn {
/** number of bytes currently in recvmbox to be received,
tested against recv_bufsize to limit bytes on recvmbox
for UDP and RAW, used for FIONREAD */
- s16_t recv_avail;
+ 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_msg *current_msg;
+ struct api_msg *current_msg;
#endif /* LWIP_TCP */
/** A callback function that is informed about events for this netconn */
netconn_callback callback;
};
+/** This vector type is passed to @ref netconn_write_vectors_partly to send
+ * multiple buffers at once.
+ * ATTENTION: This type has to directly map struct iovec since one is casted
+ * into the other!
+ */
+struct netvector {
+ /** pointer to the application buffer that contains the data to send */
+ const void *ptr;
+ /** size of the application data to send */
+ size_t len;
+};
+
/** Register an Network connection event */
#define API_EVENT(c,e,l) if (c->callback) { \
(*c->callback)(c, e, l); \
}
-/** Set conn->last_err to err but don't overwrite fatal errors */
-#define NETCONN_SET_SAFE_ERR(conn, err) do { \
- SYS_ARCH_DECL_PROTECT(lev); \
- SYS_ARCH_PROTECT(lev); \
- if (!ERR_IS_FATAL((conn)->last_err)) { \
- (conn)->last_err = err; \
- } \
- SYS_ARCH_UNPROTECT(lev); \
-} while(0);
-
/* Network connection functions: */
+
+/** @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,
+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). */
@@ -247,68 +306,82 @@ err_t netconn_delete(struct netconn *conn);
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, ip_addr_t *addr, u16_t port);
-err_t netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port);
+err_t netconn_bind(struct netconn *conn, const ip_addr_t *addr, u16_t port);
+err_t netconn_bind_if(struct netconn *conn, u8_t if_idx);
+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_udp_raw_netbuf(struct netconn *conn, struct netbuf **new_buf);
+err_t netconn_recv_udp_raw_netbuf_flags(struct netconn *conn, struct netbuf **new_buf, u8_t apiflags);
err_t netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf);
-void netconn_recved(struct netconn *conn, u32_t length);
+err_t netconn_recv_tcp_pbuf_flags(struct netconn *conn, struct pbuf **new_buf, u8_t apiflags);
+err_t netconn_tcp_recvd(struct netconn *conn, size_t len);
err_t netconn_sendto(struct netconn *conn, struct netbuf *buf,
- ip_addr_t *addr, u16_t port);
+ 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);
+err_t netconn_write_vectors_partly(struct netconn *conn, struct netvector *vectors, u16_t vectorcnt,
+ 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, ip_addr_t *multiaddr,
- ip_addr_t *netif_addr, enum netconn_igmp join_or_leave);
+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);
+err_t netconn_join_leave_group_netif(struct netconn *conn, const ip_addr_t *multiaddr,
+ u8_t if_idx, 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 */
-#if LWIP_IPV6
-
-#define netconn_bind_ip6(conn, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \
- netconn_bind(conn, ip6_2_ip(ip6addr), port) : ERR_VAL)
-#define netconn_connect_ip6(conn, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \
- netconn_connect(conn, ip6_2_ip(ip6addr), port) : ERR_VAL)
-#define netconn_sendto_ip6(conn, buf, ip6addr, port) (NETCONNTYPE_ISIPV6((conn)->type) ? \
- netconn_sendto(conn, buf, ip6_2_ip(ip6addr), port) : ERR_VAL)
-#if LWIP_IPV6_MLD
-#define netconn_join_leave_group_ip6(conn, multiaddr, srcaddr, join_or_leave) (NETCONNTYPE_ISIPV6((conn)->type) ? \
- netconn_join_leave_group(conn, ip6_2_ip(multiaddr), ip6_2_ip(srcaddr), join_or_leave) :\
- ERR_VAL)
-#endif /* LWIP_IPV6_MLD*/
-#endif /* LWIP_IPV6 */
-#define netconn_err(conn) ((conn)->last_err)
+err_t netconn_err(struct netconn *conn);
#define netconn_recv_bufsize(conn) ((conn)->recv_bufsize)
+#define netconn_set_flags(conn, set_flags) do { (conn)->flags = (u8_t)((conn)->flags | (set_flags)); } while(0)
+#define netconn_clear_flags(conn, clr_flags) do { (conn)->flags = (u8_t)((conn)->flags & ~(clr_flags)); } while(0)
+#define netconn_is_flag_set(conn, flag) (((conn)->flags & (flag)) != 0)
+
/** 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; \
+ netconn_set_flags(conn, NETCONN_FLAG_NON_BLOCKING); \
} else { \
- (conn)->flags &= ~ NETCONN_FLAG_NON_BLOCKING; }} while(0)
+ netconn_clear_flags(conn, NETCONN_FLAG_NON_BLOCKING); }} while(0)
/** Get the blocking status of netconn calls (@todo: write/send is missing) */
#define netconn_is_nonblocking(conn) (((conn)->flags & NETCONN_FLAG_NON_BLOCKING) != 0)
-/** TCP: Set the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */
-#define netconn_set_noautorecved(conn, val) do { if(val) { \
- (conn)->flags |= NETCONN_FLAG_NO_AUTO_RECVED; \
+#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) { \
+ netconn_set_flags(conn, NETCONN_FLAG_IPV6_V6ONLY); \
} else { \
- (conn)->flags &= ~ NETCONN_FLAG_NO_AUTO_RECVED; }} while(0)
-/** TCP: Get the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */
-#define netconn_get_noautorecved(conn) (((conn)->flags & NETCONN_FLAG_NO_AUTO_RECVED) != 0)
+ netconn_clear_flags(conn, 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 */
@@ -329,10 +402,18 @@ err_t netconn_gethostbyname(const char *name, ip_addr_t *addr);
#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 */
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
-#endif /* __LWIP_API_H__ */
+#endif /* LWIP_HDR_API_H */
diff --git a/lwip/src/include/lwip/apps/FILES b/lwip/src/include/lwip/apps/FILES
new file mode 100644
index 0000000..adfc0f3
--- /dev/null
+++ b/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/lwip/src/include/lwip/apps/altcp_tls_mbedtls_opts.h b/lwip/src/include/lwip/apps/altcp_tls_mbedtls_opts.h
new file mode 100644
index 0000000..36cddd9
--- /dev/null
+++ b/lwip/src/include/lwip/apps/altcp_tls_mbedtls_opts.h
@@ -0,0 +1,67 @@
+/**
+ * @file
+ * Application layered TCP/TLS connection API (to be used from TCPIP thread)
+ *
+ * This file contains options for an mbedtls port of the TLS layer.
+ */
+
+/*
+ * Copyright (c) 2017 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 <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_TLS_OPTS_H
+#define LWIP_HDR_ALTCP_TLS_OPTS_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+/** LWIP_ALTCP_TLS_MBEDTLS==1: use mbedTLS for TLS support for altcp API
+ * mbedtls include directory must be reachable via include search path
+ */
+#ifndef LWIP_ALTCP_TLS_MBEDTLS
+#define LWIP_ALTCP_TLS_MBEDTLS 0
+#endif
+
+/** Configure debug level of this file */
+#ifndef ALTCP_MBEDTLS_DEBUG
+#define ALTCP_MBEDTLS_DEBUG LWIP_DBG_OFF
+#endif
+
+/** Set a session timeout in seconds for the basic session cache
+ * ATTENTION: Using a session cache can lower security by reusing keys!
+ */
+#ifndef ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS
+#define ALTCP_MBEDTLS_SESSION_CACHE_TIMEOUT_SECONDS 0
+#endif
+
+#endif /* LWIP_ALTCP */
+
+#endif /* LWIP_HDR_ALTCP_TLS_OPTS_H */
diff --git a/lwip/src/include/lwip/apps/fs.h b/lwip/src/include/lwip/apps/fs.h
new file mode 100644
index 0000000..0a2d13a
--- /dev/null
+++ b/lwip/src/include/lwip/apps/fs.h
@@ -0,0 +1,124 @@
+/*
+ * 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
+
+/** Define FS_FILE_EXTENSION_T_DEFINED if you have typedef'ed to your private
+ * pointer type (defaults to 'void' so the default usage is 'void*')
+ */
+#ifndef FS_FILE_EXTENSION_T_DEFINED
+typedef void fs_file_extension;
+#endif
+
+struct fs_file {
+ const char *data;
+ int len;
+ int index;
+ /* pextension is free for implementations to hold private (extensional)
+ arbitrary data, e.g. holding some file state or file system handle */
+ fs_file_extension *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 */
+
+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 */
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_FS_H */
diff --git a/lwip/src/include/lwip/apps/httpd.h b/lwip/src/include/lwip/apps/httpd.h
new file mode 100644
index 0000000..efa2086
--- /dev/null
+++ b/lwip/src/include/lwip/apps/httpd.h
@@ -0,0 +1,240 @@
+/**
+ * @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);
+
+#if HTTPD_ENABLE_HTTPS
+struct altcp_tls_config;
+void httpd_inits(struct altcp_tls_config *conf);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HTTPD_H */
diff --git a/lwip/src/include/lwip/apps/httpd_opts.h b/lwip/src/include/lwip/apps/httpd_opts.h
new file mode 100644
index 0000000..8802b50
--- /dev/null
+++ b/lwip/src/include/lwip/apps/httpd_opts.h
@@ -0,0 +1,339 @@
+/**
+ * @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"
+#include "lwip/prot/iana.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 LWIP_IANA_PORT_HTTP
+#endif
+
+/** The https server port for HTTPD to use */
+#if !defined HTTPD_SERVER_PORT_HTTPS || defined __DOXYGEN__
+#define HTTPD_SERVER_PORT_HTTPS LWIP_IANA_PORT_HTTPS
+#endif
+
+/** Enable https support? */
+#if !defined HTTPD_ENABLE_HTTPS || defined __DOXYGEN__
+#define HTTPD_ENABLE_HTTPS 0
+#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 altcp_pcb* pcb);
+ The best place to define this is the hooks file (@see LWIP_HOOK_FILENAME) */
+#if !defined HTTPD_MAX_WRITE_LEN || defined __DOXYGEN__
+#if HTTPD_LIMIT_SENDING_TO_2MSS
+#define HTTPD_MAX_WRITE_LEN(pcb) ((u16_t)(2 * altcp_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
+
+/** Filename (including path) to use as FS data file */
+#if !defined HTTPD_FSDATA_FILE || defined __DOXYGEN__
+/* HTTPD_USE_CUSTOM_FSDATA: Compatibility with deprecated lwIP option */
+#if defined(HTTPD_USE_CUSTOM_FSDATA) && (HTTPD_USE_CUSTOM_FSDATA != 0)
+#define HTTPD_FSDATA_FILE "fsdata_custom.c"
+#else
+#define HTTPD_FSDATA_FILE "fsdata.c"
+#endif
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_APPS_HTTPD_OPTS_H */
diff --git a/lwip/src/include/lwip/apps/lwiperf.h b/lwip/src/include/lwip/apps/lwiperf.h
new file mode 100644
index 0000000..7dbebb0
--- /dev/null
+++ b/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/lwip/src/include/lwip/apps/mdns.h b/lwip/src/include/lwip/apps/mdns.h
new file mode 100644
index 0000000..f5c6ed8
--- /dev/null
+++ b/lwip/src/include/lwip/apps/mdns.h
@@ -0,0 +1,73 @@
+/**
+ * @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_H
+#define LWIP_HDR_APPS_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);
+
+s8_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_del_service(struct netif *netif, s8_t slot);
+
+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_APPS_MDNS_H */
diff --git a/lwip/src/include/lwip/apps/mdns_opts.h b/lwip/src/include/lwip/apps/mdns_opts.h
new file mode 100644
index 0000000..bf186bc
--- /dev/null
+++ b/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/lwip/src/include/lwip/apps/mdns_priv.h b/lwip/src/include/lwip/apps/mdns_priv.h
new file mode 100644
index 0000000..8ee6db8
--- /dev/null
+++ b/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/lwip/src/include/lwip/apps/mqtt.h b/lwip/src/include/lwip/apps/mqtt.h
new file mode 100644
index 0000000..f5e0055
--- /dev/null
+++ b/lwip/src/include/lwip/apps/mqtt.h
@@ -0,0 +1,212 @@
+/**
+ * @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"
+#include "lwip/prot/iana.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef struct mqtt_client_s mqtt_client_t;
+
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+struct altcp_tls_config;
+#endif
+
+/** @ingroup mqtt
+ * Default MQTT port (non-TLS) */
+#define MQTT_PORT LWIP_IANA_PORT_MQTT
+/** @ingroup mqtt
+ * Default MQTT TLS port */
+#define MQTT_TLS_PORT LWIP_IANA_PORT_SEQURE_MQTT
+
+/*---------------------------------------------------------------------------------------------- */
+/* 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, set to NULL if not used */
+ const char* client_user;
+ /** Password, set to NULL if not used */
+ 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;
+ /** will_msg, see will_topic */
+ const char* will_msg;
+ /** will_qos, see will_topic */
+ u8_t will_qos;
+ /** will_retain, see will_topic */
+ u8_t will_retain;
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+ /** TLS configuration for secure connections */
+ struct altcp_tls_config *tls_config;
+#endif
+};
+
+/**
+ * @ingroup mqtt
+ * Connection status codes */
+typedef enum
+{
+ /** Accepted */
+ MQTT_CONNECT_ACCEPTED = 0,
+ /** Refused protocol version */
+ MQTT_CONNECT_REFUSED_PROTOCOL_VERSION = 1,
+ /** Refused identifier */
+ MQTT_CONNECT_REFUSED_IDENTIFIER = 2,
+ /** Refused server */
+ MQTT_CONNECT_REFUSED_SERVER = 3,
+ /** Refused user credentials */
+ MQTT_CONNECT_REFUSED_USERNAME_PASS = 4,
+ /** Refused not authorized */
+ MQTT_CONNECT_REFUSED_NOT_AUTHORIZED_ = 5,
+ /** Disconnected */
+ MQTT_CONNECT_DISCONNECTED = 256,
+ /** Timeout */
+ 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);
+
+
+/** 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);
+
+mqtt_client_t *mqtt_client_new(void);
+void mqtt_client_free(mqtt_client_t* client);
+
+/** 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/lwip/src/include/lwip/apps/mqtt_opts.h b/lwip/src/include/lwip/apps/mqtt_opts.h
new file mode 100644
index 0000000..ffefacd
--- /dev/null
+++ b/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/lwip/src/include/lwip/apps/mqtt_priv.h b/lwip/src/include/lwip/apps/mqtt_priv.h
new file mode 100644
index 0000000..b775913
--- /dev/null
+++ b/lwip/src/include/lwip/apps/mqtt_priv.h
@@ -0,0 +1,104 @@
+/**
+ * @file
+ * MQTT client (private interface)
+ */
+
+/*
+ * 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_PRIV_H
+#define LWIP_HDR_APPS_MQTT_PRIV_H
+
+#include "lwip/apps/mqtt.h"
+#include "lwip/altcp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** 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_s
+{
+ /** 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 altcp_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;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_APPS_MQTT_PRIV_H */
diff --git a/lwip/src/include/lwip/apps/netbiosns.h b/lwip/src/include/lwip/apps/netbiosns.h
new file mode 100644
index 0000000..c9f68d8
--- /dev/null
+++ b/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/lwip/src/include/lwip/apps/netbiosns_opts.h b/lwip/src/include/lwip/apps/netbiosns_opts.h
new file mode 100644
index 0000000..0909ef7
--- /dev/null
+++ b/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/lwip/src/include/lwip/apps/smtp.h b/lwip/src/include/lwip/apps/smtp.h
new file mode 100644
index 0000000..456b226
--- /dev/null
+++ b/lwip/src/include/lwip/apps/smtp.h
@@ -0,0 +1,120 @@
+#ifndef LWIP_HDR_APPS_SMTP_H
+#define LWIP_HDR_APPS_SMTP_H
+
+#include "lwip/apps/smtp_opts.h"
+#include "lwip/err.h"
+#include "lwip/prot/iana.h"
+
+/** The default TCP port used for SMTP */
+#define SMTP_DEFAULT_PORT LWIP_IANA_PORT_SMTP
+/** The default TCP port used for SMTPS */
+#define SMTPS_DEFAULT_PORT LWIP_IANA_PORT_SMTPS
+
+/** Email successfully sent */
+#define SMTP_RESULT_OK 0
+/** Unknown error */
+#define SMTP_RESULT_ERR_UNKNOWN 1
+/** Connection to server failed */
+#define SMTP_RESULT_ERR_CONNECT 2
+/** Failed to resolve server hostname */
+#define SMTP_RESULT_ERR_HOSTNAME 3
+/** Connection unexpectedly closed by remote server */
+#define SMTP_RESULT_ERR_CLOSED 4
+/** Connection timed out (server didn't respond in time) */
+#define SMTP_RESULT_ERR_TIMEOUT 5
+/** Server responded with an unknown response code */
+#define SMTP_RESULT_ERR_SVR_RESP 6
+/** Out of resources locally */
+#define SMTP_RESULT_ERR_MEM 7
+
+/** Prototype of an smtp callback function
+ *
+ * @param arg argument specified when initiating the email
+ * @param smtp_result result of the mail transfer (see defines SMTP_RESULT_*)
+ * @param srv_err if aborted by the server, this contains the error code received
+ * @param err an error returned by internal lwip functions, can help to specify
+ * the source of the error but must not necessarily be != ERR_OK
+ */
+typedef void (*smtp_result_fn)(void *arg, u8_t smtp_result, u16_t srv_err, err_t err);
+
+/** This structure is used as argument for smtp_send_mail_int(),
+ * which in turn can be used with tcpip_callback() to send mail
+ * from interrupt context, e.g. like this:
+ * struct smtp_send_request *req; (to be filled)
+ * tcpip_try_callback(smtp_send_mail_int, (void*)req);
+ *
+ * For member description, see parameter description of smtp_send_mail().
+ * When using with tcpip_callback, this structure has to stay allocated
+ * (e.g. using mem_malloc/mem_free) until its 'callback_fn' is called.
+ */
+struct smtp_send_request {
+ const char *from;
+ const char* to;
+ const char* subject;
+ const char* body;
+ smtp_result_fn callback_fn;
+ void* callback_arg;
+ /** If this is != 0, data is *not* copied into an extra buffer
+ * but used from the pointers supplied in this struct.
+ * This means less memory usage, but data must stay untouched until
+ * the callback function is called. */
+ u8_t static_data;
+};
+
+
+#if SMTP_BODYDH
+
+#ifndef SMTP_BODYDH_BUFFER_SIZE
+#define SMTP_BODYDH_BUFFER_SIZE 256
+#endif /* SMTP_BODYDH_BUFFER_SIZE */
+
+struct smtp_bodydh {
+ u16_t state;
+ u16_t length; /* Length of content in buffer */
+ char buffer[SMTP_BODYDH_BUFFER_SIZE]; /* buffer for generated content */
+#ifdef SMTP_BODYDH_USER_SIZE
+ u8_t user[SMTP_BODYDH_USER_SIZE];
+#endif /* SMTP_BODYDH_USER_SIZE */
+};
+
+enum bdh_retvals_e {
+ BDH_DONE = 0,
+ BDH_WORKING
+};
+
+/** Prototype of an smtp body callback function
+ * It receives a struct smtp_bodydh, and a buffer to write data,
+ * must return BDH_WORKING to be called again and BDH_DONE when
+ * it has finished processing. This one tries to fill one TCP buffer with
+ * data, your function will be repeatedly called until that happens; so if you
+ * know you'll be taking too long to serve your request, pause once in a while
+ * by writing length=0 to avoid hogging system resources
+ *
+ * @param arg argument specified when initiating the email
+ * @param smtp_bodydh state handling + buffer structure
+ */
+typedef int (*smtp_bodycback_fn)(void *arg, struct smtp_bodydh *bodydh);
+
+err_t smtp_send_mail_bodycback(const char *from, const char* to, const char* subject,
+ smtp_bodycback_fn bodycback_fn, smtp_result_fn callback_fn, void* callback_arg);
+
+#endif /* SMTP_BODYDH */
+
+
+err_t smtp_set_server_addr(const char* server);
+void smtp_set_server_port(u16_t port);
+#if LWIP_ALTCP && LWIP_ALTCP_TLS
+struct altcp_tls_config;
+void smtp_set_tls_config(struct altcp_tls_config *tls_config);
+#endif
+err_t smtp_set_auth(const char* username, const char* pass);
+err_t smtp_send_mail(const char *from, const char* to, const char* subject, const char* body,
+ smtp_result_fn callback_fn, void* callback_arg);
+err_t smtp_send_mail_static(const char *from, const char* to, const char* subject, const char* body,
+ smtp_result_fn callback_fn, void* callback_arg);
+void smtp_send_mail_int(void *arg);
+#ifdef LWIP_DEBUG
+const char* smtp_result_str(u8_t smtp_result);
+#endif
+
+#endif /* LWIP_HDR_APPS_SMTP_H */
diff --git a/lwip/src/include/lwip/apps/smtp_opts.h b/lwip/src/include/lwip/apps/smtp_opts.h
new file mode 100644
index 0000000..0c56a9f
--- /dev/null
+++ b/lwip/src/include/lwip/apps/smtp_opts.h
@@ -0,0 +1,81 @@
+#ifndef LWIP_HDR_APPS_SMTP_OPTS_H
+#define LWIP_HDR_APPS_SMTP_OPTS_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/**
+ * @defgroup smtp_opts Options
+ * @ingroup smtp
+ *
+ * @{
+ */
+
+/** Set this to 1 to enable data handler callback on BODY */
+#ifndef SMTP_BODYDH
+#define SMTP_BODYDH 0
+#endif
+
+/** SMTP_DEBUG: Enable debugging for SNTP. */
+#ifndef SMTP_DEBUG
+#define SMTP_DEBUG LWIP_DBG_OFF
+#endif
+
+/** Maximum length reserved for server name including terminating 0 byte */
+#ifndef SMTP_MAX_SERVERNAME_LEN
+#define SMTP_MAX_SERVERNAME_LEN 256
+#endif
+
+/** Maximum length reserved for username */
+#ifndef SMTP_MAX_USERNAME_LEN
+#define SMTP_MAX_USERNAME_LEN 32
+#endif
+
+/** Maximum length reserved for password */
+#ifndef SMTP_MAX_PASS_LEN
+#define SMTP_MAX_PASS_LEN 32
+#endif
+
+/** Set this to 0 if you know the authentication data will not change
+ * during the smtp session, which saves some heap space. */
+#ifndef SMTP_COPY_AUTHDATA
+#define SMTP_COPY_AUTHDATA 1
+#endif
+
+/** Set this to 0 to save some code space if you know for sure that all data
+ * passed to this module conforms to the requirements in the SMTP RFC.
+ * WARNING: use this with care!
+ */
+#ifndef SMTP_CHECK_DATA
+#define SMTP_CHECK_DATA 1
+#endif
+
+/** Set this to 1 to enable AUTH PLAIN support */
+#ifndef SMTP_SUPPORT_AUTH_PLAIN
+#define SMTP_SUPPORT_AUTH_PLAIN 1
+#endif
+
+/** Set this to 1 to enable AUTH LOGIN support */
+#ifndef SMTP_SUPPORT_AUTH_LOGIN
+#define SMTP_SUPPORT_AUTH_LOGIN 1
+#endif
+
+/* Memory allocation/deallocation can be overridden... */
+#ifndef SMTP_STATE_MALLOC
+#define SMTP_STATE_MALLOC(size) mem_malloc(size)
+#define SMTP_STATE_FREE(ptr) mem_free(ptr)
+#endif
+
+/**
+ * @}
+ */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* SMTP_OPTS_H */
+
diff --git a/lwip/src/include/lwip/apps/snmp.h b/lwip/src/include/lwip/apps/snmp.h
new file mode 100644
index 0000000..a3f8eb1
--- /dev/null
+++ b/lwip/src/include/lwip/apps/snmp.h
@@ -0,0 +1,135 @@
+/**
+ * @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);
+
+u8_t snmp_v1_enabled(void);
+u8_t snmp_v2c_enabled(void);
+u8_t snmp_v3_enabled(void);
+void snmp_v1_enable(u8_t enable);
+void snmp_v2c_enable(u8_t enable);
+void snmp_v3_enable(u8_t enable);
+
+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/lwip/src/include/lwip/apps/snmp_core.h b/lwip/src/include/lwip/apps/snmp_core.h
new file mode 100644
index 0000000..9e7a518
--- /dev/null
+++ b/lwip/src/include/lwip/apps/snmp_core.h
@@ -0,0 +1,377 @@
+/**
+ * @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)
+#if LWIP_HAVE_INT64
+#define SNMP_ASN1_TYPE_COUNTER64 (SNMP_ASN1_CLASS_APPLICATION | SNMP_ASN1_CONTENTTYPE_PRIMITIVE | SNMP_ASN1_APPLICATION_COUNTER64)
+#endif
+
+#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;
+#if LWIP_HAVE_INT64
+ u64_t u64;
+#endif
+};
+
+
+/**
+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, u8_t oid_len);
+u8_t snmp_next_oid_check(struct snmp_next_oid_state *state, const u32_t *oid, 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;
+#if LWIP_SNMP_V3
+ u32_t unsupportedseclevels;
+ u32_t notintimewindows;
+ u32_t unknownusernames;
+ u32_t unknownengineids;
+ u32_t wrongdigests;
+ u32_t decryptionerrors;
+#endif
+};
+
+extern struct snmp_statistics snmp_stats;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* LWIP_HDR_APPS_SNMP_CORE_H */
diff --git a/lwip/src/include/lwip/apps/snmp_mib2.h b/lwip/src/include/lwip/apps/snmp_mib2.h
new file mode 100644
index 0000000..2f4a689
--- /dev/null
+++ b/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/lwip/src/include/lwip/apps/snmp_opts.h b/lwip/src/include/lwip/apps/snmp_opts.h
new file mode 100644
index 0000000..67d2cdd
--- /dev/null
+++ b/lwip/src/include/lwip/apps/snmp_opts.h
@@ -0,0 +1,297 @@
+/**
+ * @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_MBEDTLS
+#define LWIP_SNMP_V3_MBEDTLS LWIP_SNMP_V3
+#endif
+
+#ifndef LWIP_SNMP_V3_CRYPTO
+#define LWIP_SNMP_V3_CRYPTO LWIP_SNMP_V3_MBEDTLS
+#endif
+
+#ifndef LWIP_SNMP_CONFIGURE_VERSIONS
+#define LWIP_SNMP_CONFIGURE_VERSIONS 0
+#endif
+
+#endif /* LWIP_HDR_SNMP_OPTS_H */
diff --git a/lwip/src/include/lwip/apps/snmp_scalar.h b/lwip/src/include/lwip/apps/snmp_scalar.h
new file mode 100644
index 0000000..40a060c
--- /dev/null
+++ b/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/lwip/src/include/lwip/apps/snmp_snmpv2_framework.h b/lwip/src/include/lwip/apps/snmp_snmpv2_framework.h
new file mode 100644
index 0000000..47409cc
--- /dev/null
+++ b/lwip/src/include/lwip/apps/snmp_snmpv2_framework.h
@@ -0,0 +1,32 @@
+/*
+Generated by LwipMibCompiler
+*/
+
+#ifndef LWIP_HDR_APPS_SNMP_FRAMEWORK_MIB_H
+#define LWIP_HDR_APPS_SNMP_FRAMEWORK_MIB_H
+
+#include "lwip/apps/snmp_opts.h"
+#if LWIP_SNMP
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "lwip/apps/snmp_core.h"
+
+extern const struct snmp_obj_id usmNoAuthProtocol;
+extern const struct snmp_obj_id usmHMACMD5AuthProtocol;
+extern const struct snmp_obj_id usmHMACSHAAuthProtocol;
+
+extern const struct snmp_obj_id usmNoPrivProtocol;
+extern const struct snmp_obj_id usmDESPrivProtocol;
+extern const struct snmp_obj_id usmAESPrivProtocol;
+
+extern const struct snmp_mib snmpframeworkmib;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LWIP_SNMP */
+#endif /* LWIP_HDR_APPS_SNMP_FRAMEWORK_MIB_H */
diff --git a/lwip/src/include/lwip/apps/snmp_snmpv2_usm.h b/lwip/src/include/lwip/apps/snmp_snmpv2_usm.h
new file mode 100644
index 0000000..88cfcd8
--- /dev/null
+++ b/lwip/src/include/lwip/apps/snmp_snmpv2_usm.h
@@ -0,0 +1,24 @@
+/*
+Generated by LwipMibCompiler
+*/
+
+#ifndef LWIP_HDR_APPS_SNMP_USER_BASED_SM_MIB_H
+#define LWIP_HDR_APPS_SNMP_USER_BASED_SM_MIB_H
+
+#include "lwip/apps/snmp_opts.h"
+#if LWIP_SNMP
+
+#ifdef __cplusplus
+extern "C" {
+#endif /* __cplusplus */
+
+#include "lwip/apps/snmp_core.h"
+
+extern const struct snmp_mib snmpusmmib;
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
+#endif /* LWIP_SNMP */
+#endif /* LWIP_HDR_APPS_SNMP_USER_BASED_SM_MIB_H */
diff --git a/lwip/src/include/lwip/apps/snmp_table.h b/lwip/src/include/lwip/apps/snmp_table.h
new file mode 100644
index 0000000..4988b51
--- /dev/null
+++ b/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/lwip/src/include/lwip/apps/snmp_threadsync.h b/lwip/src/include/lwip/apps/snmp_threadsync.h
new file mode 100644
index 0000000..a25dbf2
--- /dev/null
+++ b/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/lwip/src/include/lwip/apps/snmpv3.h b/lwip/src/include/lwip/apps/snmpv3.h
new file mode 100644
index 0000000..ef92724
--- /dev/null
+++ b/lwip/src/include/lwip/apps/snmpv3.h
@@ -0,0 +1,106 @@
+/**
+ * @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
+
+typedef enum
+{
+ SNMP_V3_AUTH_ALGO_INVAL = 0,
+ SNMP_V3_AUTH_ALGO_MD5 = 1,
+ SNMP_V3_AUTH_ALGO_SHA = 2
+} snmpv3_auth_algo_t;
+
+typedef enum
+{
+ SNMP_V3_PRIV_ALGO_INVAL = 0,
+ SNMP_V3_PRIV_ALGO_DES = 1,
+ SNMP_V3_PRIV_ALGO_AES = 2
+} snmpv3_priv_algo_t;
+
+typedef enum
+{
+ SNMP_V3_USER_STORAGETYPE_OTHER = 1,
+ SNMP_V3_USER_STORAGETYPE_VOLATILE = 2,
+ SNMP_V3_USER_STORAGETYPE_NONVOLATILE = 3,
+ SNMP_V3_USER_STORAGETYPE_PERMANENT = 4,
+ SNMP_V3_USER_STORAGETYPE_READONLY = 5
+} snmpv3_user_storagetype_t;
+
+/*
+ * 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, snmpv3_auth_algo_t *auth_algo, u8_t *auth_key, snmpv3_priv_algo_t *priv_algo, u8_t *priv_key);
+u8_t snmpv3_get_amount_of_users(void);
+err_t snmpv3_get_user_storagetype(const char *username, snmpv3_user_storagetype_t *storagetype);
+err_t snmpv3_get_username(char *username, u8_t index);
+
+/* The following functions are provided by the SNMPv3 agent */
+
+void snmpv3_engine_id_changed(void);
+s32_t snmpv3_get_engine_time_internal(void);
+
+void snmpv3_password_to_key_md5(
+ const u8_t *password, /* IN */
+ size_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 */
+ size_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/lwip/src/include/lwip/apps/sntp.h b/lwip/src/include/lwip/apps/sntp.h
new file mode 100644
index 0000000..40df9cc
--- /dev/null
+++ b/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/lwip/src/include/lwip/apps/sntp_opts.h b/lwip/src/include/lwip/apps/sntp_opts.h
new file mode 100644
index 0000000..db3101c
--- /dev/null
+++ b/lwip/src/include/lwip/apps/sntp_opts.h
@@ -0,0 +1,202 @@
+/**
+ * @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"
+#include "lwip/prot/iana.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. Alternatively,
+ * define SNTP_SET_SYSTEM_TIME_NTP(sec, frac) in order to work with native
+ * NTP timestamps instead.
+ */
+#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 LWIP_IANA_PORT_SNTP
+#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 VN, 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
+
+/** Enable round-trip delay compensation.
+ * Compensate for the round-trip delay by calculating the clock offset from
+ * the originate, receive, transmit and destination timestamps, as per RFC.
+ *
+ * The calculation requires compiler support for 64-bit integers. Also, either
+ * SNTP_SET_SYSTEM_TIME_US or SNTP_SET_SYSTEM_TIME_NTP has to be implemented
+ * for setting the system clock with sub-second precision. Likewise, either
+ * SNTP_GET_SYSTEM_TIME or SNTP_GET_SYSTEM_TIME_NTP needs to be implemented
+ * with sub-second precision.
+ *
+ * Although not strictly required, it makes sense to combine this option with
+ * SNTP_CHECK_RESPONSE >= 2 for sanity-checking of the received timestamps.
+ * Also, in order for the round-trip calculation to work, the difference
+ * between the local clock and the NTP server clock must not be larger than
+ * about 34 years. If that limit is exceeded, the implementation will fall back
+ * to setting the clock without compensation. In order to ensure that the local
+ * clock is always within the permitted range for compensation, even at first
+ * try, it may be necessary to store at least the current year in non-volatile
+ * memory.
+ */
+#if !defined SNTP_COMP_ROUNDTRIP || defined __DOXYGEN__
+#define SNTP_COMP_ROUNDTRIP 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__
+#ifdef LWIP_RAND
+#define SNTP_STARTUP_DELAY 1
+#else
+#define SNTP_STARTUP_DELAY 0
+#endif
+#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 (LWIP_RAND() % 5000)
+#endif
+
+/** SNTP receive timeout - in milliseconds
+ * Also used as retry timeout - this shouldn't be too low.
+ * Default is 15 seconds. Must not be beolw 15 seconds by specification (i.e. 15000)
+ */
+#if !defined SNTP_RECV_TIMEOUT || defined __DOXYGEN__
+#define SNTP_RECV_TIMEOUT 15000
+#endif
+
+/** SNTP update delay - in milliseconds
+ * Default is 1 hour. Must not be beolw 60 seconds by specification (i.e. 60000)
+ */
+#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. Also used for round-trip
+ * delay compensation if SNTP_COMP_ROUNDTRIP != 0.
+ * Alternatively, define SNTP_GET_SYSTEM_TIME_NTP(sec, frac) in order to
+ * work with native NTP timestamps instead.
+ */
+#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/lwip/src/include/lwip/apps/tftp_opts.h b/lwip/src/include/lwip/apps/tftp_opts.h
new file mode 100644
index 0000000..f3f4d17
--- /dev/null
+++ b/lwip/src/include/lwip/apps/tftp_opts.h
@@ -0,0 +1,106 @@
+/**
+ *
+ * @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"
+#include "lwip/prot/iana.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 LWIP_IANA_PORT_TFTP
+#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/lwip/src/include/lwip/apps/tftp_server.h b/lwip/src/include/lwip/apps/tftp_server.h
new file mode 100644
index 0000000..a27ed96
--- /dev/null
+++ b/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/lwip/src/include/lwip/arch.h b/lwip/src/include/lwip/arch.h
index 4d6df77..56f5425 100644
--- a/lwip/src/include/lwip/arch.h
+++ b/lwip/src/include/lwip/arch.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,26 +16,28 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_ARCH_H__
-#define __LWIP_ARCH_H__
+#ifndef LWIP_HDR_ARCH_H
+#define LWIP_HDR_ARCH_H
+
+#include "arch/cc.h"
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN 1234
@@ -40,178 +47,308 @@
#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.
+ * @{
+ */
-/** Temporary: define format string for size_t if not defined in cc.h */
-#ifndef SZT_F
-#define SZT_F U32_F
-#endif /* SZT_F */
-/** Temporary upgrade helper: define format string for u8_t as hex if not
- defined in cc.h */
+/** 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>
+/* stdint.h is C99 which should also provide support for 64-bit integers */
+#if !defined(LWIP_HAVE_INT64) && defined(UINT64_MAX)
+#define LWIP_HAVE_INT64 1
+#endif
+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;
+#if LWIP_HAVE_INT64
+typedef uint64_t u64_t;
+typedef int64_t s64_t;
+#endif
+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 "02x"
-#endif /* 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, SSIZE_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
+
+/* Do we need to define ssize_t? This is a compatibility hack:
+ * Unfortunately, this type seems to be unavailable on some systems (even if
+ * sys/types or unistd.h are available).
+ * Being like that, we define it to 'int' if SSIZE_MAX is not defined.
+ */
+#ifdef SSIZE_MAX
+/* If SSIZE_MAX is defined, unistd.h should provide the type as well */
+#ifndef LWIP_NO_UNISTD_H
+#define LWIP_NO_UNISTD_H 0
+#endif
+#if !LWIP_NO_UNISTD_H
+#include <unistd.h>
+#endif
+#else /* SSIZE_MAX */
+typedef int ssize_t;
+#define SSIZE_MAX INT_MAX
+#endif /* SSIZE_MAX */
+
+/** 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 */
+
+/** PACK_STRUCT_USE_INCLUDES==1: 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 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
-
-#endif /* LWIP_PROVIDE_ERRNO */
+#endif /* LWIP_UNUSED_ARG */
+
+/** LWIP_PROVIDE_ERRNO==1: Let lwIP provide ERRNO values and the 'errno' variable.
+ * If this is disabled, cc.h must either define 'errno', include <errno.h>,
+ * define LWIP_ERRNO_STDINCLUDE to get <errno.h> included or
+ * define LWIP_ERRNO_INCLUDE to <errno.h> or equivalent.
+ */
+#if defined __DOXYGEN__
+#define LWIP_PROVIDE_ERRNO
+#endif
+
+/**
+ * @}
+ */
#ifdef __cplusplus
}
#endif
-#endif /* __LWIP_ARCH_H__ */
+#endif /* LWIP_HDR_ARCH_H */
diff --git a/lwip/src/include/lwip/autoip.h b/lwip/src/include/lwip/autoip.h
new file mode 100644
index 0000000..1d85bcc
--- /dev/null
+++ b/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/lwip/src/include/lwip/debug.h b/lwip/src/include/lwip/debug.h
index 0fe0413..baa6a40 100644
--- a/lwip/src/include/lwip/debug.h
+++ b/lwip/src/include/lwip/debug.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,48 +16,69 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_DEBUG_H__
-#define __LWIP_DEBUG_H__
+#ifndef LWIP_HDR_DEBUG_H
+#define LWIP_HDR_DEBUG_H
#include "lwip/arch.h"
#include "lwip/opt.h"
-/** lower two bits indicate debug level
- * - 0 all
- * - 1 warning
- * - 2 serious
- * - 3 severe
+/**
+ * @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
-#define LWIP_DBG_LEVEL_OFF LWIP_DBG_LEVEL_ALL /* compatibility define only */
-#define LWIP_DBG_LEVEL_WARNING 0x01 /* bad checksums, dropped packets, ... */
-#define LWIP_DBG_LEVEL_SERIOUS 0x02 /* memory allocation failures, ... */
+/** 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) */
@@ -61,24 +87,61 @@
#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)
+#define LWIP_ASSERT(message, assertion) do { if (!(assertion)) { \
+ LWIP_PLATFORM_ASSERT(message); }} while(0)
#else /* LWIP_NOASSERT */
-#define LWIP_ASSERT(message, assertion)
+#define LWIP_ASSERT(message, assertion)
#endif /* LWIP_NOASSERT */
-/** if "expression" isn't true, then print "message" and execute "handler" expression */
#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_ASSERT(message); handler;}} while(0)
+ LWIP_PLATFORM_ERROR(message); handler;}} while(0)
#endif /* LWIP_ERROR */
-#ifdef LWIP_DEBUG
-/** print debug message only if debug message type is enabled...
- * AND is of correct type AND is at least LWIP_DBG_LEVEL
+/** 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
#define LWIP_DEBUGF(debug, message) do { \
if ( \
((debug) & LWIP_DBG_ON) && \
@@ -92,8 +155,7 @@
} while(0)
#else /* LWIP_DEBUG */
-#define LWIP_DEBUGF(debug, message)
+#define LWIP_DEBUGF(debug, message)
#endif /* LWIP_DEBUG */
-#endif /* __LWIP_DEBUG_H__ */
-
+#endif /* LWIP_HDR_DEBUG_H */
diff --git a/lwip/src/include/lwip/def.h b/lwip/src/include/lwip/def.h
index 73a1b56..f836ba9 100644
--- a/lwip/src/include/lwip/def.h
+++ b/lwip/src/include/lwip/def.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,30 +16,36 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_DEF_H__
-#define __LWIP_DEF_H__
+#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" {
@@ -43,43 +54,23 @@ extern "C" {
#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
-
-/* Endianess-optimized shifting of two u8_t to create one u16_t */
-#if BYTE_ORDER == LITTLE_ENDIAN
-#define LWIP_MAKE_U16(a, b) ((a << 8) | b)
-#else
-#define LWIP_MAKE_U16(a, b) ((b << 8) | a)
-#endif
-
-#ifndef LWIP_PLATFORM_BYTESWAP
-#define LWIP_PLATFORM_BYTESWAP 0
#endif
-#ifndef LWIP_PREFIX_BYTEORDER_FUNCS
-/* workaround for naming collisions on some platforms */
-
-#ifdef htons
-#undef htons
-#endif /* htons */
-#ifdef htonl
-#undef htonl
-#endif /* htonl */
-#ifdef ntohs
-#undef ntohs
-#endif /* ntohs */
-#ifdef ntohl
-#undef ntohl
-#endif /* ntohl */
-
-#define htons(x) lwip_htons(x)
-#define ntohs(x) lwip_ntohs(x)
-#define htonl(x) lwip_htonl(x)
-#define ntohl(x) lwip_ntohl(x)
-#endif /* LWIP_PREFIX_BYTEORDER_FUNCS */
-
#if BYTE_ORDER == BIG_ENDIAN
#define lwip_htons(x) (x)
#define lwip_ntohs(x) (x)
@@ -90,34 +81,61 @@ extern "C" {
#define PP_HTONL(x) (x)
#define PP_NTOHL(x) (x)
#else /* BYTE_ORDER != BIG_ENDIAN */
-#if LWIP_PLATFORM_BYTESWAP
-#define lwip_htons(x) LWIP_PLATFORM_HTONS(x)
-#define lwip_ntohs(x) LWIP_PLATFORM_HTONS(x)
-#define lwip_htonl(x) LWIP_PLATFORM_HTONL(x)
-#define lwip_ntohl(x) LWIP_PLATFORM_HTONL(x)
-#else /* LWIP_PLATFORM_BYTESWAP */
+#ifndef lwip_htons
u16_t lwip_htons(u16_t x);
-u16_t lwip_ntohs(u16_t x);
+#endif
+#define lwip_ntohs(x) lwip_htons(x)
+
+#ifndef lwip_htonl
u32_t lwip_htonl(u32_t x);
-u32_t lwip_ntohl(u32_t x);
-#endif /* LWIP_PLATFORM_BYTESWAP */
+#endif
+#define lwip_ntohl(x) lwip_htonl(x)
/* These macros should be calculated by the preprocessor and are used
with compile-time constants only (so that there is no little-endian
overhead at runtime). */
-#define PP_HTONS(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8))
+#define PP_HTONS(x) ((u16_t)((((x) & (u16_t)0x00ffU) << 8) | (((x) & (u16_t)0xff00U) >> 8)))
#define PP_NTOHS(x) PP_HTONS(x)
-#define PP_HTONL(x) ((((x) & 0xff) << 24) | \
- (((x) & 0xff00) << 8) | \
- (((x) & 0xff0000UL) >> 8) | \
- (((x) & 0xff000000UL) >> 24))
+#define PP_HTONL(x) ((((x) & (u32_t)0x000000ffUL) << 24) | \
+ (((x) & (u32_t)0x0000ff00UL) << 8) | \
+ (((x) & (u32_t)0x00ff0000UL) >> 8) | \
+ (((x) & (u32_t)0xff000000UL) >> 24))
#define PP_NTOHL(x) PP_HTONL(x)
-
#endif /* BYTE_ORDER == BIG_ENDIAN */
+/* 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
+
+/* 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_DEF_H__ */
-
+#endif /* LWIP_HDR_DEF_H */
diff --git a/lwip/src/include/lwip/dhcp.h b/lwip/src/include/lwip/dhcp.h
index 32d9338..c78aa0b 100644
--- a/lwip/src/include/lwip/dhcp.h
+++ b/lwip/src/include/lwip/dhcp.h
@@ -1,8 +1,42 @@
-/** @file
+/**
+ * @file
+ * DHCP client API
*/
-#ifndef __LWIP_DHCP_H__
-#define __LWIP_DHCP_H__
+/*
+ * 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"
@@ -16,24 +50,26 @@ extern "C" {
#endif
/** period (in seconds) of the application calling dhcp_coarse_tmr() */
-#define DHCP_COARSE_TIMER_SECS 60
+#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_FINE_TIMER_MSECS 500
+
+#define DHCP_BOOT_FILE_LEN 128U
-#define DHCP_CHADDR_LEN 16U
-#define DHCP_SNAME_LEN 64U
-#define DHCP_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 */
+ /** transaction identifier of last sent request */
u32_t xid;
- /** our connection to the DHCP server */
- struct udp_pcb *pcb;
- /** incoming msg */
- struct dhcp_msg *msg_in;
+ /** track PCB allocation state */
+ u8_t pcb_allocated;
/** current DHCP state machine state */
u8_t state;
/** retries of current request */
@@ -43,195 +79,56 @@ struct dhcp
#endif
u8_t subnet_mask_given;
- struct pbuf *p_out; /* pbuf of outcoming msg */
- struct dhcp_msg *msg_out; /* outgoing msg */
- u16_t options_out_len; /* outgoing msg options length */
u16_t request_timeout; /* #ticks with period DHCP_FINE_TIMER_SECS for request timeout */
u16_t t1_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for renewal time */
u16_t t2_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for rebind time */
- ip_addr_t server_ip_addr; /* dhcp server address that offered this lease */
- ip_addr_t offered_ip_addr;
- ip_addr_t offered_sn_mask;
- ip_addr_t offered_gw_addr;
-
+ 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 66% of lease period) */
- /* @todo: LWIP_DHCP_BOOTP_FILE configuration option?
- integrate with possible TFTP-client for booting? */
+ u32_t offered_t2_rebind; /* recommended rebind time (usually 87.5 of lease period) */
#if LWIP_DHCP_BOOTP_FILE
- ip_addr_t offered_si_addr;
- char boot_file_name[DHCP_FILE_LEN];
+ ip4_addr_t offered_si_addr;
+ char boot_file_name[DHCP_BOOT_FILE_LEN];
#endif /* LWIP_DHCP_BOOTPFILE */
};
-/* MUST be compiled with "pack structs" or equivalent! */
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-/** minimum set of fields of any DHCP message */
-struct dhcp_msg
-{
- PACK_STRUCT_FIELD(u8_t op);
- PACK_STRUCT_FIELD(u8_t htype);
- PACK_STRUCT_FIELD(u8_t hlen);
- PACK_STRUCT_FIELD(u8_t hops);
- PACK_STRUCT_FIELD(u32_t xid);
- PACK_STRUCT_FIELD(u16_t secs);
- PACK_STRUCT_FIELD(u16_t flags);
- PACK_STRUCT_FIELD(ip_addr_p_t ciaddr);
- PACK_STRUCT_FIELD(ip_addr_p_t yiaddr);
- PACK_STRUCT_FIELD(ip_addr_p_t siaddr);
- PACK_STRUCT_FIELD(ip_addr_p_t giaddr);
- PACK_STRUCT_FIELD(u8_t chaddr[DHCP_CHADDR_LEN]);
- PACK_STRUCT_FIELD(u8_t sname[DHCP_SNAME_LEN]);
- PACK_STRUCT_FIELD(u8_t file[DHCP_FILE_LEN]);
- PACK_STRUCT_FIELD(u32_t cookie);
-#define DHCP_MIN_OPTIONS_LEN 68U
-/** make sure user does not configure this too small */
-#if ((defined(DHCP_OPTIONS_LEN)) && (DHCP_OPTIONS_LEN < DHCP_MIN_OPTIONS_LEN))
-# undef DHCP_OPTIONS_LEN
-#endif
-/** allow this to be configured in lwipopts.h, but not too small */
-#if (!defined(DHCP_OPTIONS_LEN))
-/** set this to be sufficient for your options in outgoing DHCP msgs */
-# define DHCP_OPTIONS_LEN DHCP_MIN_OPTIONS_LEN
-#endif
- PACK_STRUCT_FIELD(u8_t options[DHCP_OPTIONS_LEN]);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
void dhcp_set_struct(struct netif *netif, struct dhcp *dhcp);
/** Remove a struct dhcp previously set to the netif using dhcp_set_struct() */
-#define dhcp_remove_struct(netif) do { (netif)->dhcp = NULL; } while(0)
+#define dhcp_remove_struct(netif) netif_set_client_data(netif, LWIP_NETIF_CLIENT_DATA_INDEX_DHCP, NULL)
void dhcp_cleanup(struct netif *netif);
-/** start DHCP configuration */
err_t dhcp_start(struct netif *netif);
-/** enforce early lease renewal (not needed normally)*/
err_t dhcp_renew(struct netif *netif);
-/** release the DHCP lease, usually called before dhcp_stop()*/
err_t dhcp_release(struct netif *netif);
-/** stop DHCP configuration */
void dhcp_stop(struct netif *netif);
-/** inform server of our manual IP address */
+void dhcp_release_and_stop(struct netif *netif);
void dhcp_inform(struct netif *netif);
-/** Handle a possible change in the network configuration */
void dhcp_network_changed(struct netif *netif);
-
-/** if enabled, check whether the offered IP address is not in use, using ARP */
#if DHCP_DOES_ARP_CHECK
-void dhcp_arp_reply(struct netif *netif, ip_addr_t *addr);
+void dhcp_arp_reply(struct netif *netif, const ip4_addr_t *addr);
#endif
-
-/** to be called every minute */
+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 */
+/* to be called every half second */
void dhcp_fine_tmr(void);
-
-/** DHCP message item offsets and length */
-#define DHCP_OP_OFS 0
-#define DHCP_HTYPE_OFS 1
-#define DHCP_HLEN_OFS 2
-#define DHCP_HOPS_OFS 3
-#define DHCP_XID_OFS 4
-#define DHCP_SECS_OFS 8
-#define DHCP_FLAGS_OFS 10
-#define DHCP_CIADDR_OFS 12
-#define DHCP_YIADDR_OFS 16
-#define DHCP_SIADDR_OFS 20
-#define DHCP_GIADDR_OFS 24
-#define DHCP_CHADDR_OFS 28
-#define DHCP_SNAME_OFS 44
-#define DHCP_FILE_OFS 108
-#define DHCP_MSG_LEN 236
-
-#define DHCP_COOKIE_OFS DHCP_MSG_LEN
-#define DHCP_OPTIONS_OFS (DHCP_MSG_LEN + 4)
-
-#define DHCP_CLIENT_PORT 68
-#define DHCP_SERVER_PORT 67
-
-/** DHCP client states */
-#define DHCP_OFF 0
-#define DHCP_REQUESTING 1
-#define DHCP_INIT 2
-#define DHCP_REBOOTING 3
-#define DHCP_REBINDING 4
-#define DHCP_RENEWING 5
-#define DHCP_SELECTING 6
-#define DHCP_INFORMING 7
-#define DHCP_CHECKING 8
-#define DHCP_PERMANENT 9
-#define DHCP_BOUND 10
-/** not yet implemented #define DHCP_RELEASING 11 */
-#define DHCP_BACKING_OFF 12
-
-/** AUTOIP cooperatation flags */
-#define DHCP_AUTOIP_COOP_STATE_OFF 0
-#define DHCP_AUTOIP_COOP_STATE_ON 1
-
-#define DHCP_BOOTREQUEST 1
-#define DHCP_BOOTREPLY 2
-
-/** DHCP message types */
-#define DHCP_DISCOVER 1
-#define DHCP_OFFER 2
-#define DHCP_REQUEST 3
-#define DHCP_DECLINE 4
-#define DHCP_ACK 5
-#define DHCP_NAK 6
-#define DHCP_RELEASE 7
-#define DHCP_INFORM 8
-
-/** DHCP hardware type, currently only ethernet is supported */
-#define DHCP_HTYPE_ETH 1
-
-#define DHCP_MAGIC_COOKIE 0x63825363UL
-
-/* This is a list of options for BOOTP and DHCP, see RFC 2132 for descriptions */
-
-/** BootP options */
-#define DHCP_OPTION_PAD 0
-#define DHCP_OPTION_SUBNET_MASK 1 /* RFC 2132 3.3 */
-#define DHCP_OPTION_ROUTER 3
-#define DHCP_OPTION_DNS_SERVER 6
-#define DHCP_OPTION_HOSTNAME 12
-#define DHCP_OPTION_IP_TTL 23
-#define DHCP_OPTION_MTU 26
-#define DHCP_OPTION_BROADCAST 28
-#define DHCP_OPTION_TCP_TTL 37
-#define DHCP_OPTION_END 255
-
-/** DHCP options */
-#define DHCP_OPTION_REQUESTED_IP 50 /* RFC 2132 9.1, requested IP address */
-#define DHCP_OPTION_LEASE_TIME 51 /* RFC 2132 9.2, time in seconds, in 4 bytes */
-#define DHCP_OPTION_OVERLOAD 52 /* RFC2132 9.3, use file and/or sname field for options */
-
-#define DHCP_OPTION_MESSAGE_TYPE 53 /* RFC 2132 9.6, important for DHCP */
-#define DHCP_OPTION_MESSAGE_TYPE_LEN 1
-
-#define DHCP_OPTION_SERVER_ID 54 /* RFC 2132 9.7, server IP address */
-#define DHCP_OPTION_PARAMETER_REQUEST_LIST 55 /* RFC 2132 9.8, requested option types */
-
-#define DHCP_OPTION_MAX_MSG_SIZE 57 /* RFC 2132 9.10, message size accepted >= 576 */
-#define DHCP_OPTION_MAX_MSG_SIZE_LEN 2
-
-#define DHCP_OPTION_T1 58 /* T1 renewal time */
-#define DHCP_OPTION_T2 59 /* T2 rebinding time */
-#define DHCP_OPTION_US 60
-#define DHCP_OPTION_CLIENT_ID 61
-#define DHCP_OPTION_TFTP_SERVERNAME 66
-#define DHCP_OPTION_BOOTFILE 67
-
-/** possible combinations of overloading the file and sname fields with options */
-#define DHCP_OVERLOAD_NONE 0
-#define DHCP_OVERLOAD_FILE 1
-#define DHCP_OVERLOAD_SNAME 2
-#define DHCP_OVERLOAD_SNAME_FILE 3
+
+#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
}
@@ -239,4 +136,4 @@ void dhcp_fine_tmr(void);
#endif /* LWIP_DHCP */
-#endif /*__LWIP_DHCP_H__*/
+#endif /*LWIP_HDR_DHCP_H*/
diff --git a/lwip/src/include/ipv6/lwip/dhcp6.h b/lwip/src/include/lwip/dhcp6.h
index 4b905c5..455336d 100644
--- a/lwip/src/include/ipv6/lwip/dhcp6.h
+++ b/lwip/src/include/lwip/dhcp6.h
@@ -40,8 +40,8 @@
* <delamer@inicotech.com>
*/
-#ifndef __LWIP_IP6_DHCP6_H__
-#define __LWIP_IP6_DHCP6_H__
+#ifndef LWIP_HDR_IP6_DHCP6_H
+#define LWIP_HDR_IP6_DHCP6_H
#include "lwip/opt.h"
@@ -50,9 +50,9 @@
struct dhcp6
{
- /*TODO: implement DHCP6*/
+ /*@todo: implement DHCP6*/
};
#endif /* LWIP_IPV6_DHCP6 */
-#endif /* __LWIP_IP6_DHCP6_H__ */
+#endif /* LWIP_HDR_IP6_DHCP6_H */
diff --git a/lwip/src/include/lwip/dns.h b/lwip/src/include/lwip/dns.h
index 6c7d9b0..1453d72 100644
--- a/lwip/src/include/lwip/dns.h
+++ b/lwip/src/include/lwip/dns.h
@@ -1,7 +1,12 @@
/**
+ * @file
+ * DNS API
+ */
+
+/**
* lwip DNS resolver header file.
- * Author: Jim Pettinato
+ * Author: Jim Pettinato
* April 2007
* ported from uIP resolv.c Copyright (c) 2002-2003, Adam Dunkels.
@@ -31,12 +36,14 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
-#ifndef __LWIP_DNS_H__
-#define __LWIP_DNS_H__
+#ifndef LWIP_HDR_DNS_H
+#define LWIP_HDR_DNS_H
#include "lwip/opt.h"
-#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
+#if LWIP_DNS
+
+#include "lwip/ip_addr.h"
#ifdef __cplusplus
extern "C" {
@@ -45,37 +52,20 @@ extern "C" {
/** DNS timer period */
#define DNS_TMR_INTERVAL 1000
-/** DNS field TYPE used for "Resource Records" */
-#define DNS_RRTYPE_A 1 /* a host address */
-#define DNS_RRTYPE_NS 2 /* an authoritative name server */
-#define DNS_RRTYPE_MD 3 /* a mail destination (Obsolete - use MX) */
-#define DNS_RRTYPE_MF 4 /* a mail forwarder (Obsolete - use MX) */
-#define DNS_RRTYPE_CNAME 5 /* the canonical name for an alias */
-#define DNS_RRTYPE_SOA 6 /* marks the start of a zone of authority */
-#define DNS_RRTYPE_MB 7 /* a mailbox domain name (EXPERIMENTAL) */
-#define DNS_RRTYPE_MG 8 /* a mail group member (EXPERIMENTAL) */
-#define DNS_RRTYPE_MR 9 /* a mail rename domain name (EXPERIMENTAL) */
-#define DNS_RRTYPE_NULL 10 /* a null RR (EXPERIMENTAL) */
-#define DNS_RRTYPE_WKS 11 /* a well known service description */
-#define DNS_RRTYPE_PTR 12 /* a domain name pointer */
-#define DNS_RRTYPE_HINFO 13 /* host information */
-#define DNS_RRTYPE_MINFO 14 /* mailbox or mail list information */
-#define DNS_RRTYPE_MX 15 /* mail exchange */
-#define DNS_RRTYPE_TXT 16 /* text strings */
-
-/** DNS field CLASS used for "Resource Records" */
-#define DNS_RRCLASS_IN 1 /* the Internet */
-#define DNS_RRCLASS_CS 2 /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */
-#define DNS_RRCLASS_CH 3 /* the CHAOS class */
-#define DNS_RRCLASS_HS 4 /* Hesiod [Dyer 87] */
-#define DNS_RRCLASS_FLUSH 0x800 /* Flush bit */
-
-/* The size used for the next line is rather a hack, but it prevents including socket.h in all files
- that include memp.h, and that would possibly break portability (since socket.h defines some types
- and constants possibly already define by the OS).
- Calculation rule:
- sizeof(struct addrinfo) + sizeof(struct sockaddr_in) + DNS_MAX_NAME_LENGTH + 1 byte zero-termination */
-#define NETDB_ELEM_SIZE (32 + 16 + DNS_MAX_NAME_LENGTH + 1)
+/* 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 */
@@ -86,6 +76,7 @@ struct local_hostlist_entry {
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
@@ -94,6 +85,13 @@ struct local_hostlist_entry {
#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.
@@ -101,19 +99,27 @@ struct local_hostlist_entry {
* or NULL if the name could not be found (or on any other error).
* @param callback_arg a user-specified callback argument passed to dns_gethostbyname
*/
-typedef void (*dns_found_callback)(const char *name, ip_addr_t *ipaddr, void *callback_arg);
+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);
-void dns_init(void);
-void dns_tmr(void);
-void dns_setserver(u8_t numdns, ip_addr_t *dnsserver);
-ip_addr_t dns_getserver(u8_t numdns);
-err_t dns_gethostbyname(const char *hostname, ip_addr_t *addr,
- dns_found_callback found, void *callback_arg);
-#if DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+#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 && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+#endif /* DNS_LOCAL_HOSTLIST */
#ifdef __cplusplus
}
@@ -121,4 +127,4 @@ err_t dns_local_addhost(const char *hostname, const ip_addr_t *addr);
#endif /* LWIP_DNS */
-#endif /* __LWIP_DNS_H__ */
+#endif /* LWIP_HDR_DNS_H */
diff --git a/lwip/src/include/lwip/err.h b/lwip/src/include/lwip/err.h
index ac90772..7773098 100644
--- a/lwip/src/include/lwip/err.h
+++ b/lwip/src/include/lwip/err.h
@@ -1,8 +1,12 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,26 +15,26 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_ERR_H__
-#define __LWIP_ERR_H__
+#ifndef LWIP_HDR_ERR_H
+#define LWIP_HDR_ERR_H
#include "lwip/opt.h"
#include "lwip/arch.h"
@@ -39,6 +43,12 @@
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
@@ -47,30 +57,48 @@ typedef LWIP_ERR_T err_t;
typedef s8_t err_t;
#endif /* LWIP_ERR_T*/
-/* Definitions for error constants. */
-
-#define ERR_OK 0 /* No error, everything OK. */
-#define ERR_MEM -1 /* Out of memory error. */
-#define ERR_BUF -2 /* Buffer error. */
-#define ERR_TIMEOUT -3 /* Timeout. */
-#define ERR_RTE -4 /* Routing problem. */
-#define ERR_INPROGRESS -5 /* Operation in progress */
-#define ERR_VAL -6 /* Illegal value. */
-#define ERR_WOULDBLOCK -7 /* Operation would block. */
-#define ERR_USE -8 /* Address in use. */
-#define ERR_ISCONN -9 /* Already connected. */
+/** 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,
-#define ERR_IS_FATAL(e) ((e) < ERR_ISCONN)
-
-#define ERR_ABRT -10 /* Connection aborted. */
-#define ERR_RST -11 /* Connection reset. */
-#define ERR_CLSD -12 /* Connection closed. */
-#define ERR_CONN -13 /* Not connected. */
-
-#define ERR_ARG -14 /* Illegal argument. */
-
-#define ERR_IF -15 /* Low-level netif error */
+/** Connection aborted. */
+ ERR_ABRT = -13,
+/** Connection reset. */
+ ERR_RST = -14,
+/** Connection closed. */
+ ERR_CLSD = -15,
+/** Illegal argument. */
+ ERR_ARG = -16
+} err_enum_t;
+/**
+ * @}
+ */
#ifdef LWIP_DEBUG
extern const char *lwip_strerr(err_t err);
@@ -78,8 +106,12 @@ extern const char *lwip_strerr(err_t err);
#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_ERR_H__ */
+#endif /* LWIP_HDR_ERR_H */
diff --git a/lwip/src/include/lwip/errno.h b/lwip/src/include/lwip/errno.h
new file mode 100644
index 0000000..48d6b53
--- /dev/null
+++ b/lwip/src/include/lwip/errno.h
@@ -0,0 +1,198 @@
+/**
+ * @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_STDINCLUDE if you want to include <errno.h> here */
+#ifdef LWIP_ERRNO_STDINCLUDE
+#include <errno.h>
+#else /* LWIP_ERRNO_STDINCLUDE */
+/* Define LWIP_ERRNO_INCLUDE to an equivalent of <errno.h> to include the error defines here */
+#ifdef LWIP_ERRNO_INCLUDE
+#include LWIP_ERRNO_INCLUDE
+#endif /* LWIP_ERRNO_INCLUDE */
+#endif /* LWIP_ERRNO_STDINCLUDE */
+
+#endif /* LWIP_PROVIDE_ERRNO */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_HDR_ERRNO_H */
diff --git a/lwip/src/include/lwip/etharp.h b/lwip/src/include/lwip/etharp.h
new file mode 100644
index 0000000..7080a19
--- /dev/null
+++ b/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/lwip/src/include/ipv6/lwip/ethip6.h b/lwip/src/include/lwip/ethip6.h
index e7f412b..5e88dff 100644
--- a/lwip/src/include/ipv6/lwip/ethip6.h
+++ b/lwip/src/include/lwip/ethip6.h
@@ -39,8 +39,8 @@
* <delamer@inicotech.com>
*/
-#ifndef __LWIP_ETHIP6_H__
-#define __LWIP_ETHIP6_H__
+#ifndef LWIP_HDR_ETHIP6_H
+#define LWIP_HDR_ETHIP6_H
#include "lwip/opt.h"
@@ -57,7 +57,7 @@ extern "C" {
#endif
-err_t ethip6_output(struct netif *netif, struct pbuf *q, ip6_addr_t *ip6addr);
+err_t ethip6_output(struct netif *netif, struct pbuf *q, const ip6_addr_t *ip6addr);
#ifdef __cplusplus
}
@@ -65,4 +65,4 @@ err_t ethip6_output(struct netif *netif, struct pbuf *q, ip6_addr_t *ip6addr);
#endif /* LWIP_IPV6 && LWIP_ETHERNET */
-#endif /* __LWIP_ETHIP6_H__ */
+#endif /* LWIP_HDR_ETHIP6_H */
diff --git a/lwip/src/include/lwip/icmp.h b/lwip/src/include/lwip/icmp.h
new file mode 100644
index 0000000..f5a31fd
--- /dev/null
+++ b/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/lwip/src/include/ipv6/lwip/ip6_frag.h b/lwip/src/include/lwip/icmp6.h
index 75898b8..374ff44 100644
--- a/lwip/src/include/ipv6/lwip/ip6_frag.h
+++ b/lwip/src/include/lwip/icmp6.h
@@ -1,7 +1,7 @@
/**
* @file
*
- * IPv6 fragmentation and reassembly.
+ * IPv6 version of ICMP, as per RFC 4443.
*/
/*
@@ -38,65 +38,35 @@
* Please coordinate changes and requests with Ivan Delamer
* <delamer@inicotech.com>
*/
-#ifndef __LWIP_IP6_FRAG_H__
-#define __LWIP_IP6_FRAG_H__
+#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 */
-#if LWIP_IPV6 && LWIP_IPV6_REASS /* 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_time_exceeded_with_addrs(struct pbuf *p, enum icmp6_te_code c,
+ const ip6_addr_t *src_addr, const ip6_addr_t *dest_addr);
+void icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer);
-/* The IPv6 reassembly timer interval in milliseconds. */
-#define IP6_REASS_TMR_INTERVAL 1000
-
-/* 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 * 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 */
-
-/** 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__
-#define __LWIP_PBUF_CUSTOM_REF__
-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__ */
-
-err_t ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest);
-
-#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */
+#endif /* LWIP_ICMP6 && LWIP_IPV6 */
#ifdef __cplusplus
}
#endif
-#endif /* __LWIP_IP6_FRAG_H__ */
+
+#endif /* LWIP_HDR_ICMP6_H */
diff --git a/lwip/src/include/lwip/if_api.h b/lwip/src/include/lwip/if_api.h
new file mode 100644
index 0000000..39017ab
--- /dev/null
+++ b/lwip/src/include/lwip/if_api.h
@@ -0,0 +1,68 @@
+/**
+ * @file
+ * Interface Identification APIs from:
+ * RFC 3493: Basic Socket Interface Extensions for IPv6
+ * Section 4: Interface Identification
+ */
+
+/*
+ * Copyright (c) 2017 Joel Cunningham, Garmin International, Inc. <joel.cunningham@garmin.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: Joel Cunningham <joel.cunningham@me.com>
+ *
+ */
+#ifndef LWIP_HDR_IF_H
+#define LWIP_HDR_IF_H
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define IF_NAMESIZE NETIF_NAMESIZE
+
+char * lwip_if_indextoname(unsigned int ifindex, char *ifname);
+unsigned int lwip_if_nametoindex(const char *ifname);
+
+#if LWIP_COMPAT_SOCKETS
+#define if_indextoname(ifindex, ifname) lwip_if_indextoname(ifindex,ifname)
+#define if_nametoindex(ifname) lwip_if_nametoindex(ifname)
+#endif /* LWIP_COMPAT_SOCKETS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SOCKET */
+
+#endif /* LWIP_HDR_IF_H */
diff --git a/lwip/src/include/ipv4/lwip/igmp.h b/lwip/src/include/lwip/igmp.h
index 8aabac2..ffd80e6 100644
--- a/lwip/src/include/ipv4/lwip/igmp.h
+++ b/lwip/src/include/lwip/igmp.h
@@ -1,30 +1,35 @@
+/**
+ * @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.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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.
+ * AND ANY EXPRESS OR 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
@@ -32,31 +37,28 @@
* source code.
*/
-#ifndef __LWIP_IGMP_H__
-#define __LWIP_IGMP_H__
+#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_IGMP /* don't build if not configured for use in lwipopts.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)
-/* MAC Filter Actions, these are passed to a netif's
- * igmp_mac_filter callback function. */
-#define IGMP_DEL_MAC_FILTER 0
-#define IGMP_ADD_MAC_FILTER 1
-
+/* 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
@@ -64,18 +66,16 @@ extern "C" {
* 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
+ *
+ * There will be a group for the all systems group address but this
* will not run the state machine as it is used to kick off reports
* from all the other groups
*/
struct igmp_group {
/** next link */
struct igmp_group *next;
- /** interface on which the group is active */
- struct netif *netif;
/** multicast address */
- ip_addr_t group_address;
+ ip4_addr_t group_address;
/** signifies we were the last person to report */
u8_t last_reporter_flag;
/** current state of the group */
@@ -91,16 +91,25 @@ void igmp_init(void);
err_t igmp_start(struct netif *netif);
err_t igmp_stop(struct netif *netif);
void igmp_report_groups(struct netif *netif);
-struct igmp_group *igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr);
-void igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest);
-err_t igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr);
-err_t igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr);
+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_IGMP */
+#endif /* LWIP_IPV4 && LWIP_IGMP */
-#endif /* __LWIP_IGMP_H__ */
+#endif /* LWIP_HDR_IGMP_H */
diff --git a/lwip/src/include/lwip/inet.h b/lwip/src/include/lwip/inet.h
new file mode 100644
index 0000000..2982a0f
--- /dev/null
+++ b/lwip/src/include/lwip/inet.h
@@ -0,0 +1,169 @@
+/**
+ * @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))
+
+/* 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]; \
+ ip6_addr_clear_zone(target_ip6addr);}
+
+/* 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/lwip/src/include/lwip/inet_chksum.h b/lwip/src/include/lwip/inet_chksum.h
index e168788..76893ef 100644
--- a/lwip/src/include/lwip/inet_chksum.h
+++ b/lwip/src/include/lwip/inet_chksum.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,102 +16,90 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_INET_CHKSUM_H__
-#define __LWIP_INET_CHKSUM_H__
+#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 htons() for little-endian */
+/** Swap the bytes in an u16_t: much like lwip_htons() for little-endian */
#ifndef SWAP_BYTES_IN_WORD
-#if LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN)
-/* little endian and PLATFORM_BYTESWAP defined */
-#define SWAP_BYTES_IN_WORD(w) LWIP_PLATFORM_HTONS(w)
-#else /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN) */
-/* can't use htons on big endian (or PLATFORM_BYTESWAP not defined)... */
#define SWAP_BYTES_IN_WORD(w) (((w) & 0xff) << 8) | (((w) & 0xff00) >> 8)
-#endif /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN)*/
#endif /* SWAP_BYTES_IN_WORD */
/** Split an u32_t in two u16_ts and add them up */
#ifndef FOLD_U32T
-#define FOLD_U32T(u) (((u) >> 16) + ((u) & 0x0000ffffUL))
+#define FOLD_U32T(u) ((u32_t)(((u) >> 16) + ((u) & 0x0000ffffUL)))
#endif
#if LWIP_CHECKSUM_ON_COPY
/** Function-like macro: same as MEMCPY but returns the checksum of copied data
as u16_t */
-#ifndef LWIP_CHKSUM_COPY
-#define LWIP_CHKSUM_COPY(dst, src, len) lwip_chksum_copy(dst, src, len)
-#ifndef LWIP_CHKSUM_COPY_ALGORITHM
-#define LWIP_CHKSUM_COPY_ALGORITHM 1
-#endif /* LWIP_CHKSUM_COPY_ALGORITHM */
-#endif /* LWIP_CHKSUM_COPY */
+# 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
+# define LWIP_CHKSUM_COPY_ALGORITHM 0
#endif /* LWIP_CHECKSUM_ON_COPY */
#ifdef __cplusplus
extern "C" {
#endif
-u16_t inet_chksum(void *dataptr, u16_t len);
+u16_t inet_chksum(const void *dataptr, u16_t len);
u16_t inet_chksum_pbuf(struct pbuf *p);
-u16_t inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
- ip_addr_t *src, ip_addr_t *dest);
-u16_t inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto,
- u16_t proto_len, u16_t chksum_len, ip_addr_t *src, ip_addr_t *dest);
#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,
- ip6_addr_t *src, ip6_addr_t *dest);
+ 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, ip6_addr_t *src, ip6_addr_t *dest);
-
-#define ipX_chksum_pseudo(isipv6, p, proto, proto_len, src, dest) \
- ((isipv6) ? \
- ip6_chksum_pseudo(p, proto, proto_len, ipX_2_ip6(src), ipX_2_ip6(dest)) :\
- inet_chksum_pseudo(p, proto, proto_len, ipX_2_ip(src), ipX_2_ip(dest)))
-#define ipX_chksum_pseudo_partial(isipv6, p, proto, proto_len, chksum_len, src, dest) \
- ((isipv6) ? \
- ip6_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ipX_2_ip6(src), ipX_2_ip6(dest)) :\
- inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, ipX_2_ip(src), ipX_2_ip(dest)))
-
-#else /* LWIP_IPV6 */
+ u16_t chksum_len, const ip6_addr_t *src, const ip6_addr_t *dest);
+#endif /* LWIP_IPV6 */
-#define ipX_chksum_pseudo(isipv6, p, proto, proto_len, src, dest) \
- inet_chksum_pseudo(p, proto, proto_len, src, dest)
-#define ipX_chksum_pseudo_partial(isipv6, p, proto, proto_len, chksum_len, src, dest) \
- inet_chksum_pseudo_partial(p, proto, proto_len, chksum_len, src, 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_INET_H__ */
+#endif /* LWIP_HDR_INET_H */
diff --git a/lwip/src/include/lwip/init.h b/lwip/src/include/lwip/init.h
index 4e2e285..91f99f7 100644
--- a/lwip/src/include/lwip/init.h
+++ b/lwip/src/include/lwip/init.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,26 +16,26 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_INIT_H__
-#define __LWIP_INIT_H__
+#ifndef LWIP_HDR_INIT_H
+#define LWIP_HDR_INIT_H
#include "lwip/opt.h"
@@ -38,29 +43,52 @@
extern "C" {
#endif
+/**
+ * @defgroup lwip_version Version
+ * @ingroup lwip
+ * @{
+ */
+
/** X.x.x: Major version of the stack */
-#define LWIP_VERSION_MAJOR 1U
+#define LWIP_VERSION_MAJOR 2
/** x.X.x: Minor version of the stack */
-#define LWIP_VERSION_MINOR 4U
+#define LWIP_VERSION_MINOR 0
/** x.x.X: Revision of the stack */
-#define LWIP_VERSION_REVISION 1U
+#define LWIP_VERSION_REVISION 3
/** For release candidates, this is set to 1..254
* For official releases, this is set to 255 (LWIP_RC_RELEASE)
- * For development versions (CVS), this is set to 0 (LWIP_RC_DEVELOPMENT) */
-#define LWIP_VERSION_RC 0U
+ * For development versions (Git), this is set to 0 (LWIP_RC_DEVELOPMENT) */
+#define LWIP_VERSION_RC LWIP_RC_DEVELOPMENT
/** LWIP_VERSION_RC is set to LWIP_RC_RELEASE for official releases */
-#define LWIP_RC_RELEASE 255U
-/** LWIP_VERSION_RC is set to LWIP_RC_DEVELOPMENT for CVS versions */
-#define LWIP_RC_DEVELOPMENT 0U
+#define LWIP_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 (LWIP_VERSION_MAJOR << 24 | LWIP_VERSION_MINOR << 16 | \
- LWIP_VERSION_REVISION << 8 | LWIP_VERSION_RC)
+#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);
@@ -69,4 +97,4 @@ void lwip_init(void);
}
#endif
-#endif /* __LWIP_INIT_H__ */
+#endif /* LWIP_HDR_INIT_H */
diff --git a/lwip/src/include/lwip/ip.h b/lwip/src/include/lwip/ip.h
index a0cd1d4..653c3b2 100644
--- a/lwip/src/include/lwip/ip.h
+++ b/lwip/src/include/lwip/ip.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,26 +16,26 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_IP_H__
-#define __LWIP_IP_H__
+#ifndef LWIP_HDR_IP_H
+#define LWIP_HDR_IP_H
#include "lwip/opt.h"
@@ -41,6 +46,7 @@
#include "lwip/netif.h"
#include "lwip/ip4.h"
#include "lwip/ip6.h"
+#include "lwip/prot/ip.h"
#ifdef __cplusplus
extern "C" {
@@ -49,206 +55,276 @@ extern "C" {
/* This is passed as the destination address to ip_output_if (not
to ip_output), meaning that an IP header already is constructed
in the pbuf. This is used when TCP retransmits. */
-#ifdef IP_HDRINCL
-#undef IP_HDRINCL
-#endif /* IP_HDRINCL */
-#define IP_HDRINCL NULL
+#define LWIP_IP_HDRINCL NULL
-#if LWIP_NETIF_HWADDRHINT
-#define IP_PCB_ADDRHINT ;u8_t addr_hint
-#else
-#define IP_PCB_ADDRHINT
-#endif /* LWIP_NETIF_HWADDRHINT */
+/** 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
+#define LWIP_IP_CHECK_PBUF_REF_COUNT_FOR_TX(p) LWIP_ASSERT("p->ref == 1", (p)->ref == 1)
+#endif
-#if LWIP_IPV6
-#define IP_PCB_ISIPV6_MEMBER u8_t isipv6;
-#define IP_PCB_IPVER_EQ(pcb1, pcb2) ((pcb1)->isipv6 == (pcb2)->isipv6)
-#define IP_PCB_IPVER_INPUT_MATCH(pcb) (ip_current_is_v6() ? \
- ((pcb)->isipv6 != 0) : \
- ((pcb)->isipv6 == 0))
-#define PCB_ISIPV6(pcb) ((pcb)->isipv6)
-#else
-#define IP_PCB_ISIPV6_MEMBER
-#define IP_PCB_IPVER_EQ(pcb1, pcb2) 1
-#define IP_PCB_IPVER_INPUT_MATCH(pcb) 1
-#define PCB_ISIPV6(pcb) 0
-#endif /* LWIP_IPV6 */
+#if LWIP_NETIF_USE_HINTS
+#define IP_PCB_NETIFHINT ;struct netif_hint netif_hints
+#else /* LWIP_NETIF_USE_HINTS */
+#define IP_PCB_NETIFHINT
+#endif /* LWIP_NETIF_USE_HINTS */
-/* This is the common part of all PCB types. It needs to be at the
+/** 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_PCB_ISIPV6_MEMBER \
+#define IP_PCB \
/* ip addresses in network byte order */ \
- ipX_addr_t local_ip; \
- ipX_addr_t remote_ip; \
- /* Socket options */ \
- u8_t so_options; \
- /* Type Of Service */ \
- u8_t tos; \
- /* Time To Live */ \
- u8_t ttl \
+ ip_addr_t local_ip; \
+ ip_addr_t remote_ip; \
+ /* Bound netif index */ \
+ u8_t netif_idx; \
+ /* 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
+ IP_PCB_NETIFHINT
struct ip_pcb {
-/* Common members of all PCB types */
+ /* Common members of all PCB types */
IP_PCB;
};
/*
- * Option flags per-socket. These are the same like SO_XXX.
+ * Option flags per-socket. These are the same like SO_XXX in sockets.h
*/
-/*#define SOF_DEBUG 0x01U Unimplemented: turn on debugging info recording */
-#define SOF_ACCEPTCONN 0x02U /* socket has had listen() */
#define SOF_REUSEADDR 0x04U /* allow local address reuse */
#define SOF_KEEPALIVE 0x08U /* keep connections alive */
-/*#define SOF_DONTROUTE 0x10U Unimplemented: just use interface addresses */
#define SOF_BROADCAST 0x20U /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */
-/*#define SOF_USELOOPBACK 0x40U Unimplemented: bypass hardware when possible */
-#define SOF_LINGER 0x80U /* linger on close if data present */
-/*#define SOF_OOBINLINE 0x0100U Unimplemented: leave received OOB data in line */
-/*#define SOF_REUSEPORT 0x0200U Unimplemented: allow local address & port reuse */
/* These flags are inherited (e.g. from a listen-pcb to a connection-pcb): */
-#define SOF_INHERITED (SOF_REUSEADDR|SOF_KEEPALIVE|SOF_LINGER/*|SOF_DEBUG|SOF_DONTROUTE|SOF_OOBINLINE*/)
+#define SOF_INHERITED (SOF_REUSEADDR|SOF_KEEPALIVE)
-/* Global variables of this module, kept in a struct for efficient access using base+index. */
+/** Global variables of this module, kept in a struct for efficient access using base+index. */
struct ip_globals
{
- /** The interface that provided the packet for the current callback invocation. */
+ /** 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. */
const struct ip_hdr *current_ip4_header;
+#endif /* LWIP_IPV4 */
#if LWIP_IPV6
/** Header of the input IPv6 packet currently being processed. */
- const struct ip6_hdr *current_ip6_header;
+ 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 */
- ipX_addr_t current_iphdr_src;
+ ip_addr_t current_iphdr_src;
/** Destination IP address of current_header */
- ipX_addr_t current_iphdr_dest;
+ ip_addr_t current_iphdr_dest;
};
extern struct ip_globals ip_data;
-/** Get the interface that received the current packet.
+/** 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 IP header of the current packet.
+/** 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_header() (ip_data.current_ip4_header)
+#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 ipX_current_src_addr() (&ip_data.current_iphdr_src)
+#define ip_current_src_addr() (&ip_data.current_iphdr_src)
/** Destination IP address of current_header */
-#define ipX_current_dest_addr() (&ip_data.current_iphdr_dest)
+#define ip_current_dest_addr() (&ip_data.current_iphdr_dest)
-#if LWIP_IPV6
+#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() 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() (ip_data.current_ip6_header)
+#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() (ipX_2_ip6(&ip_data.current_iphdr_src))
+#define ip6_current_src_addr() (ip_2_ip6(&ip_data.current_iphdr_src))
/** Destination IPv6 address of current_header */
-#define ip6_current_dest_addr() (ipX_2_ip6(&ip_data.current_iphdr_dest))
+#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(ip_current_header()))
+ IPH_PROTO(ip4_current_header()))
/** Get the transport layer header */
-#define ipX_next_header_ptr() ((void*)((ip_current_is_v6() ? \
- (u8_t*)ip6_current_header() : (u8_t*)ip_current_header()) + ip_current_header_tot_len()))
-
-/** Set an IP_PCB to IPv6 (IPv4 is the default) */
-#define ip_set_v6(pcb, val) do{if(pcb != NULL) { pcb->isipv6 = val; }}while(0)
+#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 ip_current_src_addr() (ipX_2_ip(&ip_data.current_iphdr_src))
+#define ip4_current_src_addr() (ip_2_ip4(&ip_data.current_iphdr_src))
/** Destination IP4 address of current_header */
-#define ip_current_dest_addr() (ipX_2_ip(&ip_data.current_iphdr_dest))
+#define ip4_current_dest_addr() (ip_2_ip4(&ip_data.current_iphdr_dest))
-#else /* LWIP_IPV6 */
+#elif LWIP_IPV4 /* LWIP_IPV4 && LWIP_IPV6 */
-/** Always returns FALSE when only supporting IPv4 */
+/** 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() 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(ip_current_header())
+#define ip_current_header_proto() IPH_PROTO(ip4_current_header())
/** Get the transport layer header */
-#define ipX_next_header_ptr() ((void*)((u8_t*)ip_current_header() + ip_current_header_tot_len()))
+#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 ip_current_src_addr() (&ip_data.current_iphdr_src)
+#define ip4_current_src_addr() (&ip_data.current_iphdr_src)
/** Destination IP4 address of current_header */
-#define ip_current_dest_addr() (&ip_data.current_iphdr_dest)
+#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()) + ip_current_header_tot_len()))
+/** 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 ipX_current_src_addr() (&ip_data.current_iphdr_src)
+#define ip_current_src_addr() (&ip_data.current_iphdr_src)
/** Union destination address of current_header */
-#define ipX_current_dest_addr() (&ip_data.current_iphdr_dest)
+#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))
+#define ip_set_option(pcb, opt) ((pcb)->so_options = (u8_t)((pcb)->so_options | (opt)))
/** Resets an IP pcb option (SOF_* flags) */
-#define ip_reset_option(pcb, opt) ((pcb)->so_options &= ~(opt))
+#define ip_reset_option(pcb, opt) ((pcb)->so_options = (u8_t)((pcb)->so_options & ~(opt)))
-#if LWIP_IPV6
-#define ipX_output(isipv6, p, src, dest, ttl, tos, proto) \
- ((isipv6) ? \
- ip6_output(p, ipX_2_ip6(src), ipX_2_ip6(dest), ttl, tos, proto) : \
- ip_output(p, ipX_2_ip(src), ipX_2_ip(dest), ttl, tos, proto))
-#define ipX_output_if(isipv6, p, src, dest, ttl, tos, proto, netif) \
- ((isipv6) ? \
+#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) : \
- ip_output_if(p, (src), (dest), ttl, tos, proto, netif))
-#define ipX_output_hinted(isipv6, p, src, dest, ttl, tos, proto, addr_hint) \
- ((isipv6) ? \
- ip6_output_hinted(p, ipX_2_ip6(src), ipX_2_ip6(dest), ttl, tos, proto, addr_hint) : \
- ip_output_hinted(p, ipX_2_ip(src), ipX_2_ip(dest), ttl, tos, proto, addr_hint))
-#define ipX_route(isipv6, src, dest) \
- ((isipv6) ? \
- ip6_route(ipX_2_ip6(src), ipX_2_ip6(dest)) : \
- ip_route(ipX_2_ip(dest)))
-#define ipX_netif_get_local_ipX(isipv6, netif, dest) \
- ((isipv6) ? \
- ip6_netif_get_local_ipX(netif, ipX_2_ip6(dest)) : \
- ip_netif_get_local_ipX(netif))
-#define ipX_debug_print(is_ipv6, p) ((is_ipv6) ? ip6_debug_print(p) : ip_debug_print(p))
-#else /* LWIP_IPV6 */
-#define ipX_output(isipv6, p, src, dest, ttl, tos, proto) \
- ip_output(p, src, dest, ttl, tos, proto)
-#define ipX_output_if(isipv6, p, src, dest, ttl, tos, proto, netif) \
- ip_output_if(p, src, dest, ttl, tos, proto, netif)
-#define ipX_output_hinted(isipv6, p, src, dest, ttl, tos, proto, addr_hint) \
- ip_output_hinted(p, src, dest, ttl, tos, proto, addr_hint)
-#define ipX_route(isipv6, src, dest) \
- ip_route(ipX_2_ip(dest))
-#define ipX_netif_get_local_ipX(isipv6, netif, dest) \
- ip_netif_get_local_ipX(netif)
-#define ipX_debug_print(is_ipv6, p) ip_debug_print(p)
+ 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 that already includes an IP header. */
+#define ip_output_if_hdrincl(p, src, dest, netif) \
+ (IP_IS_V6(dest) ? \
+ ip6_output_if(p, ip_2_ip6(src), LWIP_IP_HDRINCL, 0, 0, 0, netif) : \
+ ip4_output_if(p, ip_2_ip4(src), LWIP_IP_HDRINCL, 0, 0, 0, netif))
+/** Output IP packet with netif_hint */
+#define ip_output_hinted(p, src, dest, ttl, tos, proto, netif_hint) \
+ (IP_IS_V6(dest) ? \
+ ip6_output_hinted(p, ip_2_ip6(src), ip_2_ip6(dest), ttl, tos, proto, netif_hint) : \
+ ip4_output_hinted(p, ip_2_ip4(src), ip_2_ip4(dest), ttl, tos, proto, netif_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(src), ip_2_ip4(dest)))
+/**
+ * @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, netif_hint) \
+ ip4_output_hinted(p, src, dest, ttl, tos, proto, netif_hint)
+#define ip_output_if_hdrincl(p, src, dest, netif) \
+ ip4_output_if(p, src, LWIP_IP_HDRINCL, 0, 0, 0, netif)
+#define ip_route(src, dest) \
+ ip4_route_src(src, dest)
+#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, netif_hint) \
+ ip6_output_hinted(p, src, dest, ttl, tos, proto, netif_hint)
+#define ip_output_if_hdrincl(p, src, dest, netif) \
+ ip6_output_if(p, src, LWIP_IP_HDRINCL, 0, 0, 0, netif)
+#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 ipX_route_get_local_ipX(isipv6, src, dest, netif, ipXaddr) do { \
- (netif) = ipX_route(isipv6, src, dest); \
- (ipXaddr) = ipX_netif_get_local_ipX(isipv6, netif, dest); \
+#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_IP_H__ */
+#endif /* LWIP_HDR_IP_H */
diff --git a/lwip/src/include/lwip/ip4.h b/lwip/src/include/lwip/ip4.h
new file mode 100644
index 0000000..fd35a33
--- /dev/null
+++ b/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 *src, const ip4_addr_t *dest);
+#else /* LWIP_IPV4_SRC_ROUTING */
+#define ip4_route_src(src, dest) 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_USE_HINTS
+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, struct netif_hint *netif_hint);
+#endif /* LWIP_NETIF_USE_HINTS */
+#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/lwip/src/include/ipv4/lwip/ip4_addr.h b/lwip/src/include/lwip/ip4_addr.h
index b05ae53..e0c32c7 100644
--- a/lwip/src/include/ipv4/lwip/ip4_addr.h
+++ b/lwip/src/include/lwip/ip4_addr.h
@@ -1,3 +1,8 @@
+/**
+ * @file
+ * IPv4 address API
+ */
+
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* All rights reserved.
@@ -29,69 +34,31 @@
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_IP4_ADDR_H__
-#define __LWIP_IP4_ADDR_H__
+#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 ip_addr_t,
+/** This is the aligned version of ip4_addr_t,
used as local variable, on the stack, etc. */
-struct ip_addr {
+struct ip4_addr {
u32_t addr;
};
-/* This is the packed version of ip_addr_t,
- used in network headers that are itself packed */
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct ip_addr_packed {
- PACK_STRUCT_FIELD(u32_t addr);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
-
-/** ip_addr_t uses a struct for convenience only, so that the same defines can
- * operate both on ip_addr_t as well as on ip_addr_p_t. */
-typedef struct ip_addr ip_addr_t;
-typedef struct ip_addr_packed ip_addr_p_t;
-
-/*
- * struct ipaddr2 is used in the definition of the ARP packet format in
- * order to support compilers that don't have structure packing.
- */
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct ip_addr2 {
- PACK_STRUCT_FIELD(u16_t addrw[2]);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
+/** 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;
/* Forward declaration to not include netif.h */
struct netif;
-extern const ip_addr_t ip_addr_any;
-extern const ip_addr_t ip_addr_broadcast;
-
-/** IP_ADDR_ can be used as a fixed IP address
- * for the wildcard and the broadcast address
- */
-#define IP_ADDR_ANY ((ip_addr_t *)&ip_addr_any)
-#define IP_ADDR_BROADCAST ((ip_addr_t *)&ip_addr_broadcast)
-
/** 255.255.255.255 */
#define IPADDR_NONE ((u32_t)0xffffffffUL)
/** 127.0.0.1 */
@@ -133,55 +100,35 @@ extern const ip_addr_t ip_addr_broadcast;
#define IP_LOOPBACKNET 127 /* official! */
-
-#if BYTE_ORDER == BIG_ENDIAN
/** Set an IP address given by the four byte-parts */
-#define IP4_ADDR(ipaddr, a,b,c,d) \
- (ipaddr)->addr = ((u32_t)((a) & 0xff) << 24) | \
- ((u32_t)((b) & 0xff) << 16) | \
- ((u32_t)((c) & 0xff) << 8) | \
- (u32_t)((d) & 0xff)
-#else
-/** Set an IP address given by the four byte-parts.
- Little-endian version that prevents the use of htonl. */
-#define IP4_ADDR(ipaddr, a,b,c,d) \
- (ipaddr)->addr = ((u32_t)((d) & 0xff) << 24) | \
- ((u32_t)((c) & 0xff) << 16) | \
- ((u32_t)((b) & 0xff) << 8) | \
- (u32_t)((a) & 0xff)
-#endif
+#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(ip_addr_t))
-#endif
-
-/** Copy IP address - faster than ip_addr_set: no NULL check */
-#define ip_addr_copy(dest, src) ((dest).addr = (src).addr)
+/** 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 ip_addr_set(dest, src) ((dest)->addr = \
+#define ip4_addr_set(dest, src) ((dest)->addr = \
((src) == NULL ? 0 : \
(src)->addr))
/** Set complete address to zero */
-#define ip_addr_set_zero(ipaddr) ((ipaddr)->addr = 0)
-/** Set address to IPADDR_ANY (no need for htonl()) */
-#define ip_addr_set_any(ipaddr) ((ipaddr)->addr = IPADDR_ANY)
+#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 ip_addr_set_loopback(ipaddr) ((ipaddr)->addr = PP_HTONL(IPADDR_LOOPBACK))
+#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 ip_addr_set_hton(dest, src) ((dest)->addr = \
+#define ip4_addr_set_hton(dest, src) ((dest)->addr = \
((src) == NULL ? 0:\
- htonl((src)->addr)))
+ 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 ip_addr_get_network(target, host, netmask) ((target)->addr = ((host)->addr) & ((netmask)->addr))
+#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.
@@ -191,36 +138,45 @@ extern const ip_addr_t ip_addr_broadcast;
* @arg mask network identifier mask
* @return !0 if the network identifiers of both address match
*/
-#define ip_addr_netcmp(addr1, addr2, mask) (((addr1)->addr & \
+#define ip4_addr_netcmp(addr1, addr2, mask) (((addr1)->addr & \
(mask)->addr) == \
((addr2)->addr & \
(mask)->addr))
-#define ip_addr_cmp(addr1, addr2) ((addr1)->addr == (addr2)->addr)
+#define ip4_addr_cmp(addr1, addr2) ((addr1)->addr == (addr2)->addr)
-#define ip_addr_isany(addr1) ((addr1) == NULL || (addr1)->addr == IPADDR_ANY)
+#define ip4_addr_isany_val(addr1) ((addr1).addr == IPADDR_ANY)
+#define ip4_addr_isany(addr1) ((addr1) == NULL || ip4_addr_isany_val(*(addr1)))
-#define ip_addr_isbroadcast(ipaddr, netif) ip4_addr_isbroadcast((ipaddr)->addr, (netif))
-u8_t ip4_addr_isbroadcast(u32_t addr, const struct netif *netif);
+#define 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 ip_addr_ismulticast(addr1) (((addr1)->addr & PP_HTONL(0xf0000000UL)) == PP_HTONL(0xe0000000UL))
-
-#define ip_addr_islinklocal(addr1) (((addr1)->addr & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xa9fe0000UL))
-
-#define ip_addr_debug_print(debug, ipaddr) \
- LWIP_DEBUGF(debug, ("%"U16_F".%"U16_F".%"U16_F".%"U16_F, \
- ipaddr != NULL ? ip4_addr1_16(ipaddr) : 0, \
- ipaddr != NULL ? ip4_addr2_16(ipaddr) : 0, \
- ipaddr != NULL ? ip4_addr3_16(ipaddr) : 0, \
- ipaddr != NULL ? ip4_addr4_16(ipaddr) : 0))
+#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) (((u8_t*)(ipaddr))[0])
-#define ip4_addr2(ipaddr) (((u8_t*)(ipaddr))[1])
-#define ip4_addr3(ipaddr) (((u8_t*)(ipaddr))[2])
-#define ip4_addr4(ipaddr) (((u8_t*)(ipaddr))[3])
+#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))
@@ -228,17 +184,21 @@ u8_t ip4_addr_netmask_valid(u32_t netmask);
#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 ipaddr_aton(const char *cp, ip_addr_t *addr);
+int ip4addr_aton(const char *cp, ip4_addr_t *addr);
/** returns ptr to static buffer; not reentrant! */
-char *ipaddr_ntoa(const ip_addr_t *addr);
-char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen);
+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_IP_ADDR_H__ */
+#endif /* LWIP_IPV4 */
+
+#endif /* LWIP_HDR_IP_ADDR_H */
diff --git a/lwip/src/include/ipv4/lwip/ip_frag.h b/lwip/src/include/lwip/ip4_frag.h
index 47eca9f..ed5bf14 100644
--- a/lwip/src/include/ipv4/lwip/ip_frag.h
+++ b/lwip/src/include/lwip/ip4_frag.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,27 +16,27 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Jani Monoses <jani@iv.ro>
*
*/
-#ifndef __LWIP_IP_FRAG_H__
-#define __LWIP_IP_FRAG_H__
+#ifndef LWIP_HDR_IP4_FRAG_H
+#define LWIP_HDR_IP4_FRAG_H
#include "lwip/opt.h"
#include "lwip/err.h"
@@ -40,6 +45,8 @@
#include "lwip/ip_addr.h"
#include "lwip/ip.h"
+#if LWIP_IPV4
+
#ifdef __cplusplus
extern "C" {
#endif
@@ -48,7 +55,7 @@ extern "C" {
/* The IP reassembly timer interval in milliseconds. */
#define IP_TMR_INTERVAL 1000
-/* IP reassembly helper struct.
+/** IP reassembly helper struct.
* This is exported because memp needs to know the size.
*/
struct ip_reassdata {
@@ -62,30 +69,32 @@ struct ip_reassdata {
void ip_reass_init(void);
void ip_reass_tmr(void);
-struct pbuf * ip_reass(struct pbuf *p);
+struct pbuf * ip4_reass(struct pbuf *p);
#endif /* IP_REASSEMBLY */
#if IP_FRAG
-#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF
+#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. */
-#ifndef __LWIP_PBUF_CUSTOM_REF__
-#define __LWIP_PBUF_CUSTOM_REF__
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__ */
-#endif /* !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */
+#endif /* LWIP_PBUF_CUSTOM_REF_DEFINED */
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
-err_t ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest);
+err_t ip4_frag(struct pbuf *p, struct netif *netif, const ip4_addr_t *dest);
#endif /* IP_FRAG */
#ifdef __cplusplus
}
#endif
-#endif /* __LWIP_IP_FRAG_H__ */
+#endif /* LWIP_IPV4 */
+
+#endif /* LWIP_HDR_IP4_FRAG_H */
diff --git a/lwip/src/include/lwip/ip6.h b/lwip/src/include/lwip/ip6.h
new file mode 100644
index 0000000..f894e06
--- /dev/null
+++ b/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_USE_HINTS
+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, struct netif_hint *netif_hint);
+#endif /* LWIP_NETIF_USE_HINTS */
+#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/lwip/src/include/lwip/ip6_addr.h b/lwip/src/include/lwip/ip6_addr.h
new file mode 100644
index 0000000..29c2a34
--- /dev/null
+++ b/lwip/src/include/lwip/ip6_addr.h
@@ -0,0 +1,352 @@
+/**
+ * @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 */
+
+#include "lwip/ip6_zone.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];
+#if LWIP_IPV6_SCOPES
+ u8_t zone;
+#endif /* LWIP_IPV6_SCOPES */
+};
+
+/** 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; \
+ ip6_addr_clear_zone(ip6addr); } 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]; \
+ ip6_addr_copy_zone((dest), (src)); }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]; \
+ ip6_addr_set_zone((dest), (src) == NULL ? IP6_NO_ZONE : ip6_addr_zone(src)); }while(0)
+
+/** Copy packed IPv6 address to unpacked IPv6 address; zone is not set */
+#define ip6_addr_copy_from_packed(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]; \
+ ip6_addr_clear_zone(&dest); }while(0)
+
+/** Copy unpacked IPv6 address to packed IPv6 address; zone is lost */
+#define ip6_addr_copy_to_packed(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)
+
+/** 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; \
+ ip6_addr_clear_zone(ip6addr);}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); \
+ ip6_addr_clear_zone(ip6addr);}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]); \
+ ip6_addr_set_zone((dest), (src) == NULL ? IP6_NO_ZONE : ip6_addr_zone(src));}while(0)
+
+
+/** Compare IPv6 networks, ignoring zone information. To be used sparingly! */
+#define ip6_addr_netcmp_zoneless(addr1, addr2) (((addr1)->addr[0] == (addr2)->addr[0]) && \
+ ((addr1)->addr[1] == (addr2)->addr[1]))
+
+/**
+ * Determine if two IPv6 address are on the same network.
+ *
+ * @param addr1 IPv6 address 1
+ * @param addr2 IPv6 address 2
+ * @return 1 if the network identifiers of both address match, 0 if not
+ */
+#define ip6_addr_netcmp(addr1, addr2) (ip6_addr_netcmp_zoneless((addr1), (addr2)) && \
+ ip6_addr_cmp_zone((addr1), (addr2)))
+
+/* Exact-host comparison *after* ip6_addr_netcmp() succeeded, for efficiency. */
+#define ip6_addr_nethostcmp(addr1, addr2) (((addr1)->addr[2] == (addr2)->addr[2]) && \
+ ((addr1)->addr[3] == (addr2)->addr[3]))
+
+/** Compare IPv6 addresses, ignoring zone information. To be used sparingly! */
+#define ip6_addr_cmp_zoneless(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]))
+/**
+ * Determine if two IPv6 addresses are the same. In particular, the address
+ * part of both must be the same, and the zone must be compatible.
+ *
+ * @param addr1 IPv6 address 1
+ * @param addr2 IPv6 address 2
+ * @return 1 if the addresses are considered equal, 0 if not
+ */
+#define ip6_addr_cmp(addr1, addr2) (ip6_addr_cmp_zoneless((addr1), (addr2)) && \
+ ip6_addr_cmp_zone((addr1), (addr2)))
+
+/** Compare IPv6 address to packed address and zone */
+#define ip6_addr_cmp_packed(ip6addr, paddr, zone_idx) (((ip6addr)->addr[0] == (paddr)->addr[0]) && \
+ ((ip6addr)->addr[1] == (paddr)->addr[1]) && \
+ ((ip6addr)->addr[2] == (paddr)->addr[2]) && \
+ ((ip6addr)->addr[3] == (paddr)->addr[3]) && \
+ ip6_addr_equals_zone((ip6addr), (zone_idx)))
+
+#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))
+
+/* Scoping note: while interface-local and link-local multicast addresses do
+ * have a scope (i.e., they are meaningful only in the context of a particular
+ * interface), the following functions are not assigning or comparing zone
+ * indices. The reason for this is backward compatibility. Any call site that
+ * produces a non-global multicast address must assign a multicast address as
+ * appropriate itself. */
+
+#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); \
+ ip6_addr_clear_zone(ip6addr); }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); \
+ ip6_addr_clear_zone(ip6addr); }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)); \
+ ip6_addr_clear_zone(ip6addr); }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_DUPLICATED 0x40 /* Failed DAD test, not valid */
+
+#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_isduplicated(addr_state) (addr_state == IP6_ADDR_DUPLICATED)
+
+#if LWIP_IPV6_ADDRESS_LIFETIMES
+#define IP6_ADDR_LIFE_STATIC (0)
+#define IP6_ADDR_LIFE_INFINITE (0xffffffffUL)
+#define ip6_addr_life_isstatic(addr_life) ((addr_life) == IP6_ADDR_LIFE_STATIC)
+#define ip6_addr_life_isinfinite(addr_life) ((addr_life) == IP6_ADDR_LIFE_INFINITE)
+#endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
+
+#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/lwip/src/include/lwip/ip6_frag.h b/lwip/src/include/lwip/ip6_frag.h
new file mode 100644
index 0000000..87e0e86
--- /dev/null
+++ b/lwip/src/include/lwip/ip6_frag.h
@@ -0,0 +1,144 @@
+/**
+ * @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 */
+
+/** The IPv6 reassembly timer interval in milliseconds. */
+#define IP6_REASS_TMR_INTERVAL 1000
+
+/** IP6_FRAG_COPYHEADER==1: for platforms where sizeof(void*) > 4, "struct
+ * ip6_reass_helper" is too large to be stored in the IPv6 fragment header, and
+ * will bleed into the header before it, which may be the IPv6 header or an
+ * extension header. This means that for each first fragment packet, we need to
+ * 1) make a copy of some IPv6 header fields (src+dest) that we need later on,
+ * just in case we do overwrite part of the IPv6 header, and 2) make a copy of
+ * the header data that we overwrote, so that we can restore it before either
+ * completing reassembly or sending an ICMPv6 reply. The last part is true even
+ * if this setting is disabled, but if it is enabled, we need to save a bit
+ * more data (up to the size of a pointer) because we overwrite more. */
+#ifndef IPV6_FRAG_COPYHEADER
+#define IPV6_FRAG_COPYHEADER 0
+#endif
+
+/* With IPV6_FRAG_COPYHEADER==1, a helper structure may (or, depending on the
+ * presence of extensions, may not) overwrite part of the IP header. Therefore,
+ * we copy the fields that we need from the IP header for as long as the helper
+ * structure may still be in place. This is easier than temporarily restoring
+ * those fields in the IP header each time we need to perform checks on them. */
+#if IPV6_FRAG_COPYHEADER
+#define IPV6_FRAG_SRC(ipr) ((ipr)->src)
+#define IPV6_FRAG_DEST(ipr) ((ipr)->dest)
+#else /* IPV6_FRAG_COPYHEADER */
+#define IPV6_FRAG_SRC(ipr) ((ipr)->iphdr->src)
+#define IPV6_FRAG_DEST(ipr) ((ipr)->iphdr->dest)
+#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 *iphdr; /* pointer to the first (original) IPv6 header */
+#if IPV6_FRAG_COPYHEADER
+ ip6_addr_p_t src; /* copy of the source address in the IP header */
+ ip6_addr_p_t dest; /* copy of the destination address in the IP header */
+ /* This buffer (for the part of the original header that we overwrite) will
+ * be slightly oversized, but we cannot compute the exact size from here. */
+ u8_t orig_hdr[sizeof(struct ip6_frag_hdr) + sizeof(void*)];
+#else /* IPV6_FRAG_COPYHEADER */
+ /* In this case we still need the buffer, for sending ICMPv6 replies. */
+ u8_t orig_hdr[sizeof(struct ip6_frag_hdr)];
+#endif /* IPV6_FRAG_COPYHEADER */
+ u32_t identification;
+ u16_t datagram_len;
+ u8_t nexth;
+ u8_t timer;
+#if LWIP_IPV6_SCOPES
+ u8_t src_zone; /* zone of original packet's source address */
+ u8_t dest_zone; /* zone of original packet's destination address */
+#endif /* LWIP_IPV6_SCOPES */
+};
+
+#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/lwip/src/include/lwip/ip6_zone.h b/lwip/src/include/lwip/ip6_zone.h
new file mode 100644
index 0000000..a757575
--- /dev/null
+++ b/lwip/src/include/lwip/ip6_zone.h
@@ -0,0 +1,296 @@
+/**
+ * @file
+ *
+ * IPv6 address scopes, zones, and scoping policy.
+ *
+ * This header provides the means to implement support for IPv6 address scopes,
+ * as per RFC 4007. An address scope can be either global or more constrained.
+ * In lwIP, we say that an address "has a scope" or "is scoped" when its scope
+ * is constrained, in which case the address is meaningful only in a specific
+ * "zone." For unicast addresses, only link-local addresses have a scope; in
+ * that case, the scope is the link. For multicast addresses, there are various
+ * scopes defined by RFC 4007 and others. For any constrained scope, a system
+ * must establish a (potentially one-to-many) mapping between zones and local
+ * interfaces. For example, a link-local address is valid on only one link (its
+ * zone). That link may be attached to one or more local interfaces. The
+ * decisions on which scopes are constrained and the mapping between zones and
+ * interfaces is together what we refer to as the "scoping policy" - more on
+ * this in a bit.
+ *
+ * In lwIP, each IPv6 address has an associated zone index. This zone index may
+ * be set to "no zone" (IP6_NO_ZONE, 0) or an actual zone. We say that an
+ * address "has a zone" or "is zoned" when its zone index is *not* set to "no
+ * zone." In lwIP, in principle, each address should be "properly zoned," which
+ * means that if the address has a zone if and only if has a scope. As such, it
+ * is a rule that an unscoped (e.g., global) address must never have a zone.
+ * Even though one could argue that there is always one zone even for global
+ * scopes, this rule exists for implementation simplicity. Violation of the
+ * rule will trigger assertions or otherwise result in undesired behavior.
+ *
+ * Backward compatibility prevents us from requiring that applications always
+ * provide properly zoned addresses. We do enforce the rule that the in the
+ * lwIP link layer (everything below netif->output_ip6() and in particular ND6)
+ * *all* addresses are properly zoned. Thus, on the output paths down the
+ * stack, various places deal with the case of addresses that lack a zone.
+ * Some of them are best-effort for efficiency (e.g. the PCB bind and connect
+ * API calls' attempts to add missing zones); ultimately the IPv6 output
+ * handler (@ref ip6_output_if_src) will set a zone if necessary.
+ *
+ * Aside from dealing with scoped addresses lacking a zone, a proper IPv6
+ * implementation must also ensure that a packet with a scoped source and/or
+ * destination address does not leave its zone. This is currently implemented
+ * in the input and forward functions. However, for output, these checks are
+ * deliberately omitted in order to keep the implementation lightweight. The
+ * routing algorithm in @ref ip6_route will take decisions such that it will
+ * not cause zone violations unless the application sets bad addresses, though.
+ *
+ * In terms of scoping policy, lwIP implements the default policy from RFC 4007
+ * using macros in this file. This policy considers link-local unicast
+ * addresses and (only) interface-local and link-local multicast addresses as
+ * having a scope. For all these addresses, the zone is equal to the interface.
+ * As shown below in this file, it is possible to implement a custom policy.
+ */
+
+/*
+ * Copyright (c) 2017 The MINIX 3 Project.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF 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: David van Moolenbroek <david@minix3.org>
+ *
+ */
+#ifndef LWIP_HDR_IP6_ZONE_H
+#define LWIP_HDR_IP6_ZONE_H
+
+/**
+ * @defgroup ip6_zones IPv6 Zones
+ * @ingroup ip6
+ * @{
+ */
+
+#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+/** Identifier for "no zone". */
+#define IP6_NO_ZONE 0
+
+#if LWIP_IPV6_SCOPES
+
+/** Zone initializer for static IPv6 address initialization, including comma. */
+#define IPADDR6_ZONE_INIT , IP6_NO_ZONE
+
+/** Return the zone index of the given IPv6 address; possibly "no zone". */
+#define ip6_addr_zone(ip6addr) ((ip6addr)->zone)
+
+/** Does the given IPv6 address have a zone set? (0/1) */
+#define ip6_addr_has_zone(ip6addr) (ip6_addr_zone(ip6addr) != IP6_NO_ZONE)
+
+/** Set the zone field of an IPv6 address to a particular value. */
+#define ip6_addr_set_zone(ip6addr, zone_idx) ((ip6addr)->zone = (zone_idx))
+
+/** Clear the zone field of an IPv6 address, setting it to "no zone". */
+#define ip6_addr_clear_zone(ip6addr) ((ip6addr)->zone = IP6_NO_ZONE)
+
+/** Copy the zone field from the second IPv6 address to the first one. */
+#define ip6_addr_copy_zone(ip6addr1, ip6addr2) ((ip6addr1).zone = (ip6addr2).zone)
+
+/** Is the zone field of the given IPv6 address equal to the given zone index? (0/1) */
+#define ip6_addr_equals_zone(ip6addr, zone_idx) ((ip6addr)->zone == (zone_idx))
+
+/** Are the zone fields of the given IPv6 addresses equal? (0/1)
+ * This macro must only be used on IPv6 addresses of the same scope. */
+#define ip6_addr_cmp_zone(ip6addr1, ip6addr2) ((ip6addr1)->zone == (ip6addr2)->zone)
+
+/** Symbolic constants for the 'type' parameters in some of the macros.
+ * These exist for efficiency only, allowing the macros to avoid certain tests
+ * when the address is known not to be of a certain type. Dead code elimination
+ * will do the rest. IP6_MULTICAST is supported but currently not optimized.
+ * @see ip6_addr_has_scope, ip6_addr_assign_zone, ip6_addr_lacks_zone.
+ */
+enum lwip_ipv6_scope_type
+{
+ /** Unknown */
+ IP6_UNKNOWN = 0,
+ /** Unicast */
+ IP6_UNICAST = 1,
+ /** Multicast */
+ IP6_MULTICAST = 2
+};
+
+/** IPV6_CUSTOM_SCOPES: together, the following three macro definitions,
+ * @ref ip6_addr_has_scope, @ref ip6_addr_assign_zone, and
+ * @ref ip6_addr_test_zone, completely define the lwIP scoping policy.
+ * The definitions below implement the default policy from RFC 4007 Sec. 6.
+ * Should an implementation desire to implement a different policy, it can
+ * define IPV6_CUSTOM_SCOPES to 1 and supply its own definitions for the three
+ * macros instead.
+ */
+#ifndef IPV6_CUSTOM_SCOPES
+#define IPV6_CUSTOM_SCOPES 0
+#endif /* !IPV6_CUSTOM_SCOPES */
+
+#if !IPV6_CUSTOM_SCOPES
+
+/**
+ * Determine whether an IPv6 address has a constrained scope, and as such is
+ * meaningful only if accompanied by a zone index to identify the scope's zone.
+ * The given address type may be used to eliminate at compile time certain
+ * checks that will evaluate to false at run time anyway.
+ *
+ * This default implementation follows the default model of RFC 4007, where
+ * only interface-local and link-local scopes are defined.
+ *
+ * Even though the unicast loopback address does have an implied link-local
+ * scope, in this implementation it does not have an explicitly assigned zone
+ * index. As such it should not be tested for in this macro.
+ *
+ * @param ip6addr the IPv6 address (const); only its address part is examined.
+ * @param type address type; see @ref lwip_ipv6_scope_type.
+ * @return 1 if the address has a constrained scope, 0 if it does not.
+ */
+#define ip6_addr_has_scope(ip6addr, type) \
+ (ip6_addr_islinklocal(ip6addr) || (((type) != IP6_UNICAST) && \
+ (ip6_addr_ismulticast_iflocal(ip6addr) || \
+ ip6_addr_ismulticast_linklocal(ip6addr))))
+
+/**
+ * Assign a zone index to an IPv6 address, based on a network interface. If the
+ * given address has a scope, the assigned zone index is that scope's zone of
+ * the given netif; otherwise, the assigned zone index is "no zone".
+ *
+ * This default implementation follows the default model of RFC 4007, where
+ * only interface-local and link-local scopes are defined, and the zone index
+ * of both of those scopes always equals the index of the network interface.
+ * As such, this default implementation need not distinguish between different
+ * constrained scopes when assigning the zone.
+ *
+ * @param ip6addr the IPv6 address; its address part is examined, and its zone
+ * index is assigned.
+ * @param type address type; see @ref lwip_ipv6_scope_type.
+ * @param netif the network interface (const).
+ */
+#define ip6_addr_assign_zone(ip6addr, type, netif) \
+ (ip6_addr_set_zone((ip6addr), \
+ ip6_addr_has_scope((ip6addr), (type)) ? netif_get_index(netif) : 0))
+
+/**
+ * Test whether an IPv6 address is "zone-compatible" with a network interface.
+ * That is, test whether the network interface is part of the zone associated
+ * with the address. For efficiency, this macro is only ever called if the
+ * given address is either scoped or zoned, and thus, it need not test this.
+ * If an address is scoped but not zoned, or zoned and not scoped, it is
+ * considered not zone-compatible with any netif.
+ *
+ * This default implementation follows the default model of RFC 4007, where
+ * only interface-local and link-local scopes are defined, and the zone index
+ * of both of those scopes always equals the index of the network interface.
+ * As such, there is always only one matching netif for a specific zone index,
+ * but all call sites of this macro currently support multiple matching netifs
+ * as well (at no additional expense in the common case).
+ *
+ * @param ip6addr the IPv6 address (const).
+ * @param netif the network interface (const).
+ * @return 1 if the address is scope-compatible with the netif, 0 if not.
+ */
+#define ip6_addr_test_zone(ip6addr, netif) \
+ (ip6_addr_equals_zone((ip6addr), netif_get_index(netif)))
+
+#endif /* !IPV6_CUSTOM_SCOPES */
+
+/** Does the given IPv6 address have a scope, and as such should also have a
+ * zone to be meaningful, but does not actually have a zone? (0/1) */
+#define ip6_addr_lacks_zone(ip6addr, type) \
+ (!ip6_addr_has_zone(ip6addr) && ip6_addr_has_scope((ip6addr), (type)))
+
+/**
+ * Try to select a zone for a scoped address that does not yet have a zone.
+ * Called from PCB bind and connect routines, for two reasons: 1) to save on
+ * this (relatively expensive) selection for every individual packet route
+ * operation and 2) to allow the application to obtain the selected zone from
+ * the PCB as is customary for e.g. getsockname/getpeername BSD socket calls.
+ *
+ * Ideally, callers would always supply a properly zoned address, in which case
+ * this function would not be needed. It exists both for compatibility with the
+ * BSD socket API (which accepts zoneless destination addresses) and for
+ * backward compatibility with pre-scoping lwIP code.
+ *
+ * It may be impossible to select a zone, e.g. if there are no netifs. In that
+ * case, the address's zone field will be left as is.
+ *
+ * @param dest the IPv6 address for which to select and set a zone.
+ * @param src source IPv6 address (const); may be equal to dest.
+ */
+#define ip6_addr_select_zone(dest, src) do { struct netif *selected_netif; \
+ selected_netif = ip6_route((src), (dest)); \
+ if (selected_netif != NULL) { \
+ ip6_addr_assign_zone((dest), IP6_UNKNOWN, selected_netif); \
+ } } while (0)
+
+/**
+ * @}
+ */
+
+#else /* LWIP_IPV6_SCOPES */
+
+#define IPADDR6_ZONE_INIT
+#define ip6_addr_zone(ip6addr) (IP6_NO_ZONE)
+#define ip6_addr_has_zone(ip6addr) (0)
+#define ip6_addr_set_zone(ip6addr, zone_idx)
+#define ip6_addr_clear_zone(ip6addr)
+#define ip6_addr_copy_zone(ip6addr1, ip6addr2)
+#define ip6_addr_equals_zone(ip6addr, zone_idx) (1)
+#define ip6_addr_cmp_zone(ip6addr1, ip6addr2) (1)
+#define IPV6_CUSTOM_SCOPES 0
+#define ip6_addr_has_scope(ip6addr, type) (0)
+#define ip6_addr_assign_zone(ip6addr, type, netif)
+#define ip6_addr_test_zone(ip6addr, netif) (1)
+#define ip6_addr_lacks_zone(ip6addr, type) (0)
+#define ip6_addr_select_zone(ip6addr, src)
+
+#endif /* LWIP_IPV6_SCOPES */
+
+#if LWIP_IPV6_SCOPES && LWIP_IPV6_SCOPES_DEBUG
+
+/** Verify that the given IPv6 address is properly zoned. */
+#define IP6_ADDR_ZONECHECK(ip6addr) LWIP_ASSERT("IPv6 zone check failed", \
+ ip6_addr_has_scope(ip6addr, IP6_UNKNOWN) == ip6_addr_has_zone(ip6addr))
+
+/** Verify that the given IPv6 address is properly zoned for the given netif. */
+#define IP6_ADDR_ZONECHECK_NETIF(ip6addr, netif) LWIP_ASSERT("IPv6 netif zone check failed", \
+ ip6_addr_has_scope(ip6addr, IP6_UNKNOWN) ? \
+ (ip6_addr_has_zone(ip6addr) && \
+ (((netif) == NULL) || ip6_addr_test_zone((ip6addr), (netif)))) : \
+ !ip6_addr_has_zone(ip6addr))
+
+#else /* LWIP_IPV6_SCOPES && LWIP_IPV6_SCOPES_DEBUG */
+
+#define IP6_ADDR_ZONECHECK(ip6addr)
+#define IP6_ADDR_ZONECHECK_NETIF(ip6addr, netif)
+
+#endif /* LWIP_IPV6_SCOPES && LWIP_IPV6_SCOPES_DEBUG */
+
+#endif /* LWIP_IPV6 */
+
+#endif /* LWIP_HDR_IP6_ADDR_H */
diff --git a/lwip/src/include/lwip/ip_addr.h b/lwip/src/include/lwip/ip_addr.h
index 7bd03cb..8b0056b 100644
--- a/lwip/src/include/lwip/ip_addr.h
+++ b/lwip/src/include/lwip/ip_addr.h
@@ -1,3 +1,8 @@
+/**
+ * @file
+ * IP address API (common IPv4 and IPv6)
+ */
+
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* All rights reserved.
@@ -29,8 +34,8 @@
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_IP_ADDR_H__
-#define __LWIP_IP_ADDR_H__
+#ifndef LWIP_HDR_IP_ADDR_H
+#define LWIP_HDR_IP_ADDR_H
#include "lwip/opt.h"
#include "lwip/def.h"
@@ -42,89 +47,383 @@
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 } IPADDR6_ZONE_INIT } }, 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 } IPADDR6_ZONE_INIT } }, 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) } IPADDR6_ZONE_INIT } }, 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 } IPADDR6_ZONE_INIT } }, 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))
+
+#define ip_clear_no4(ipaddr) do { ip_2_ip6(ipaddr)->addr[1] = \
+ ip_2_ip6(ipaddr)->addr[2] = \
+ ip_2_ip6(ipaddr)->addr[3] = 0; \
+ ip6_addr_clear_zone(ip_2_ip6(ipaddr)); }while(0)
+
+/** @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))); ip_clear_no4(&dest); }}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 ip6addr */
+#define ip_addr_copy_from_ip6_packed(dest, src) do{ \
+ ip6_addr_copy_from_packed(*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); ip_clear_no4(&dest); }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); ip_clear_no4(ipaddr); }}while(0)
+/** @ingroup ip4addr */
+#define ip_addr_set_ip4_u32_val(ipaddr, val) do{ ip4_addr_set_u32(ip_2_ip4(&(ipaddr)), val); \
+ IP_SET_TYPE_VAL(ipaddr, IPADDR_TYPE_V4); ip_clear_no4(&ipaddr); }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)); ip_clear_no4(dest); }}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); ip_clear_no4(ipaddr); }}while(0)
+/** @ingroup ipaddr */
+#define ip_addr_set_any_val(is_ipv6, ipaddr) do{if(is_ipv6){ \
+ ip6_addr_set_any(ip_2_ip6(&(ipaddr))); IP_SET_TYPE_VAL(ipaddr, IPADDR_TYPE_V6); }else{ \
+ ip4_addr_set_any(ip_2_ip4(&(ipaddr))); IP_SET_TYPE_VAL(ipaddr, IPADDR_TYPE_V4); ip_clear_no4(&ipaddr); }}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); ip_clear_no4(ipaddr); }}while(0)
+/** @ingroup ipaddr */
+#define ip_addr_set_loopback_val(is_ipv6, ipaddr) do{if(is_ipv6){ \
+ ip6_addr_set_loopback(ip_2_ip6(&(ipaddr))); IP_SET_TYPE_VAL(ipaddr, IPADDR_TYPE_V6); }else{ \
+ ip4_addr_set_loopback(ip_2_ip4(&(ipaddr))); IP_SET_TYPE_VAL(ipaddr, IPADDR_TYPE_V4); ip_clear_no4(&ipaddr); }}while(0)
+/** @ingroup ipaddr */
+#define ip_addr_set_hton(dest, src) do{if(IP_IS_V6(src)){ \
+ ip6_addr_set_hton(ip_2_ip6(dest), ip_2_ip6(src)); IP_SET_TYPE(dest, IPADDR_TYPE_V6); }else{ \
+ ip4_addr_set_hton(ip_2_ip4(dest), ip_2_ip4(src)); IP_SET_TYPE(dest, IPADDR_TYPE_V4); ip_clear_no4(ipaddr); }}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) (((ipaddr) == NULL) ? 1 : ((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)
+char *ipaddr_ntoa(const ip_addr_t *addr);
+char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int 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; \
+ ip6_addr_clear_zone(ip6addr); } 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
+
+#define ip_addr_set_any_val(is_ipv6, ipaddr) ip_addr_set_any(is_ipv6, &(ipaddr))
+#define ip_addr_set_loopback_val(is_ipv6, ipaddr) ip_addr_set_loopback(is_ipv6, &(ipaddr))
+
+#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_set_ip4_u32_val(ipaddr, val) ip_addr_set_ip4_u32(&(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 } IPADDR6_ZONE_INIT }
+#define IPADDR6_INIT_HOST(a, b, c, d) { { PP_HTONL(a), PP_HTONL(b), PP_HTONL(c), PP_HTONL(d) } IPADDR6_ZONE_INIT }
+#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_copy_from_ip6_packed(dest, src) ip6_addr_copy_from_packed(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
-/* A union struct for both IP version's addresses. */
-typedef union {
- ip_addr_t ip4;
- ip6_addr_t ip6;
-} ipX_addr_t;
-
-/** These functions only exist for type-safe conversion from ip_addr_t to
- ip6_addr_t and back */
-#ifdef LWIP_ALLOW_STATIC_FN_IN_HEADER
-static ip6_addr_t* ip_2_ip6(ip_addr_t *ipaddr)
-{ return (ip6_addr_t*)ipaddr;}
-static ip_addr_t* ip6_2_ip(ip6_addr_t *ip6addr)
-{ return (ip_addr_t*)ip6addr; }
-static ipX_addr_t* ip_2_ipX(ip_addr_t *ipaddr)
-{ return (ipX_addr_t*)ipaddr; }
-static ipX_addr_t* ip6_2_ipX(ip6_addr_t *ip6addr)
-{ return (ipX_addr_t*)ip6addr; }
-#else /* LWIP_ALLOW_STATIC_FN_IN_HEADER */
-#define ip_2_ip6(ipaddr) ((ip6_addr_t*)(ipaddr))
-#define ip6_2_ip(ip6addr) ((ip_addr_t*)(ip6addr))
-#define ip_2_ipX(ipaddr) ((ipX_addr_t*)ipaddr)
-#define ip6_2_ipX(ip6addr) ((ipX_addr_t*)ip6addr)
-#endif /* LWIP_ALLOW_STATIC_FN_IN_HEADER*/
-#define ipX_2_ip6(ip6addr) (&((ip6addr)->ip6))
-#define ipX_2_ip(ipaddr) (&((ipaddr)->ip4))
-
-#define ipX_addr_copy(is_ipv6, dest, src) do{if(is_ipv6){ \
- ip6_addr_copy((dest).ip6, (src).ip6); }else{ \
- ip_addr_copy((dest).ip4, (src).ip4); }}while(0)
-#define ipX_addr_set(is_ipv6, dest, src) do{if(is_ipv6){ \
- ip6_addr_set(ipX_2_ip6(dest), ipX_2_ip6(src)); }else{ \
- ip_addr_set(ipX_2_ip(dest), ipX_2_ip(src)); }}while(0)
-#define ipX_addr_set_ipaddr(is_ipv6, dest, src) do{if(is_ipv6){ \
- ip6_addr_set(ipX_2_ip6(dest), ip_2_ip6(src)); }else{ \
- ip_addr_set(ipX_2_ip(dest), src); }}while(0)
-#define ipX_addr_set_zero(is_ipv6, ipaddr) do{if(is_ipv6){ \
- ip6_addr_set_zero(ipX_2_ip6(ipaddr)); }else{ \
- ip_addr_set_zero(ipX_2_ip(ipaddr)); }}while(0)
-#define ipX_addr_set_any(is_ipv6, ipaddr) do{if(is_ipv6){ \
- ip6_addr_set_any(ipX_2_ip6(ipaddr)); }else{ \
- ip_addr_set_any(ipX_2_ip(ipaddr)); }}while(0)
-#define ipX_addr_set_loopback(is_ipv6, ipaddr) do{if(is_ipv6){ \
- ip6_addr_set_loopback(ipX_2_ip6(ipaddr)); }else{ \
- ip_addr_set_loopback(ipX_2_ip(ipaddr)); }}while(0)
-#define ipX_addr_set_hton(is_ipv6, dest, src) do{if(is_ipv6){ \
- ip6_addr_set_hton(ipX_2_ip6(ipaddr), (src)) ;}else{ \
- ip_addr_set_hton(ipX_2_ip(ipaddr), (src));}}while(0)
-#define ipX_addr_cmp(is_ipv6, addr1, addr2) ((is_ipv6) ? \
- ip6_addr_cmp(ipX_2_ip6(addr1), ipX_2_ip6(addr2)) : \
- ip_addr_cmp(ipX_2_ip(addr1), ipX_2_ip(addr2)))
-#define ipX_addr_isany(is_ipv6, ipaddr) ((is_ipv6) ? \
- ip6_addr_isany(ipX_2_ip6(ipaddr)) : \
- ip_addr_isany(ipX_2_ip(ipaddr)))
-#define ipX_addr_ismulticast(is_ipv6, ipaddr) ((is_ipv6) ? \
- ip6_addr_ismulticast(ipX_2_ip6(ipaddr)) : \
- ip_addr_ismulticast(ipX_2_ip(ipaddr)))
-#define ipX_addr_debug_print(is_ipv6, debug, ipaddr) do { if(is_ipv6) { \
- ip6_addr_debug_print(debug, ipX_2_ip6(ipaddr)); } else { \
- ip_addr_debug_print(debug, ipX_2_ip(ipaddr)); }}while(0)
-
-#else /* LWIP_IPV6 */
-
-typedef ip_addr_t ipX_addr_t;
-#define ipX_2_ip(ipaddr) (ipaddr)
-#define ip_2_ipX(ipaddr) (ipaddr)
-
-#define ipX_addr_copy(is_ipv6, dest, src) ip_addr_copy(dest, src)
-#define ipX_addr_set(is_ipv6, dest, src) ip_addr_set(dest, src)
-#define ipX_addr_set_ipaddr(is_ipv6, dest, src) ip_addr_set(dest, src)
-#define ipX_addr_set_zero(is_ipv6, ipaddr) ip_addr_set_zero(ipaddr)
-#define ipX_addr_set_any(is_ipv6, ipaddr) ip_addr_set_any(ipaddr)
-#define ipX_addr_set_loopback(is_ipv6, ipaddr) ip_addr_set_loopback(ipaddr)
-#define ipX_addr_set_hton(is_ipv6, dest, src) ip_addr_set_hton(dest, src)
-#define ipX_addr_cmp(is_ipv6, addr1, addr2) ip_addr_cmp(addr1, addr2)
-#define ipX_addr_isany(is_ipv6, ipaddr) ip_addr_isany(ipaddr)
-#define ipX_addr_ismulticast(is_ipv6, ipaddr) ip_addr_ismulticast(ipaddr)
-#define ipX_addr_debug_print(is_ipv6, debug, ipaddr) ip_addr_debug_print(debug, ipaddr)
-
-#endif /* 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_IP_ADDR_H__ */
+#endif /* LWIP_HDR_IP_ADDR_H */
diff --git a/lwip/src/include/lwip/mem.h b/lwip/src/include/lwip/mem.h
index 5bb906b..ff208d2 100644
--- a/lwip/src/include/lwip/mem.h
+++ b/lwip/src/include/lwip/mem.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,26 +16,26 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_MEM_H__
-#define __LWIP_MEM_H__
+#ifndef LWIP_HDR_MEM_H
+#define LWIP_HDR_MEM_H
#include "lwip/opt.h"
@@ -40,31 +45,17 @@ extern "C" {
#if MEM_LIBC_MALLOC
-#include <stddef.h> /* for size_t */
+#include "lwip/arch.h"
typedef size_t mem_size_t;
#define MEM_SIZE_F SZT_F
-/* aliases for C library malloc() */
-#define mem_init()
-/* in case C library malloc() needs extra protection,
- * allow these defines to be overridden.
- */
-#ifndef mem_free
-#define mem_free free
-#endif
-#ifndef mem_malloc
-#define mem_malloc malloc
-#endif
-#ifndef mem_calloc
-#define mem_calloc calloc
-#endif
-/* Since there is no C library allocation function to shrink memory without
- moving it, define this to nothing. */
-#ifndef mem_trim
-#define mem_trim(mem, size) (mem)
-#endif
-#else /* MEM_LIBC_MALLOC */
+#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...
@@ -76,48 +67,16 @@ typedef u32_t mem_size_t;
typedef u16_t mem_size_t;
#define MEM_SIZE_F U16_F
#endif /* MEM_SIZE > 64000 */
+#endif
-#if MEM_USE_POOLS
-/** mem_init is not used when using pools instead of a heap */
-#define mem_init()
-/** mem_trim is not used when using pools instead of a heap:
- we can't free part of a pool element and don't want to copy the rest */
-#define mem_trim(mem, size) (mem)
-#else /* MEM_USE_POOLS */
-/* lwIP alternative malloc */
void mem_init(void);
void *mem_trim(void *mem, mem_size_t size);
-#endif /* MEM_USE_POOLS */
void *mem_malloc(mem_size_t size);
void *mem_calloc(mem_size_t count, mem_size_t size);
void mem_free(void *mem);
-#endif /* MEM_LIBC_MALLOC */
-
-/** Calculate memory size for an aligned buffer - returns the next highest
- * multiple of MEM_ALIGNMENT (e.g. LWIP_MEM_ALIGN_SIZE(3) and
- * LWIP_MEM_ALIGN_SIZE(4) will both yield 4 for MEM_ALIGNMENT == 4).
- */
-#ifndef LWIP_MEM_ALIGN_SIZE
-#define LWIP_MEM_ALIGN_SIZE(size) (((size) + MEM_ALIGNMENT - 1) & ~(MEM_ALIGNMENT-1))
-#endif
-
-/** Calculate safe memory size for an aligned buffer when using an unaligned
- * type as storage. This includes a safety-margin on (MEM_ALIGNMENT - 1) at the
- * start (e.g. if buffer is u8_t[] and actual data will be u32_t*)
- */
-#ifndef LWIP_MEM_ALIGN_BUFFER
-#define LWIP_MEM_ALIGN_BUFFER(size) (((size) + MEM_ALIGNMENT - 1))
-#endif
-
-/** Align a memory pointer to the alignment defined by MEM_ALIGNMENT
- * so that ADDR % MEM_ALIGNMENT == 0
- */
-#ifndef LWIP_MEM_ALIGN
-#define LWIP_MEM_ALIGN(addr) ((void *)(((mem_ptr_t)(addr) + MEM_ALIGNMENT - 1) & ~(mem_ptr_t)(MEM_ALIGNMENT-1)))
-#endif
#ifdef __cplusplus
}
#endif
-#endif /* __LWIP_MEM_H__ */
+#endif /* LWIP_HDR_MEM_H */
diff --git a/lwip/src/include/lwip/memp.h b/lwip/src/include/lwip/memp.h
index f0d0739..562cd05 100644
--- a/lwip/src/include/lwip/memp.h
+++ b/lwip/src/include/lwip/memp.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,27 +16,27 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_MEMP_H__
-#define __LWIP_MEMP_H__
+#ifndef LWIP_HDR_MEMP_H
+#define LWIP_HDR_MEMP_H
#include "lwip/opt.h"
@@ -39,61 +44,97 @@
extern "C" {
#endif
-/* Create the list of all memory pools managed by memp. MEMP_MAX represents a NULL pool at the end */
+/* 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/memp_std.h"
+#include "lwip/priv/memp_std.h"
MEMP_MAX
} memp_t;
-#if MEM_USE_POOLS
-/* Use a helper type to get the start and end of the user "memory pools" for mem_malloc */
-typedef enum {
- /* Get the first (via:
- MEMP_POOL_HELPER_START = ((u8_t) 1*MEMP_POOL_A + 0*MEMP_POOL_B + 0*MEMP_POOL_C + 0)*/
- MEMP_POOL_HELPER_FIRST = ((u8_t)
-#define LWIP_MEMPOOL(name,num,size,desc)
-#define LWIP_MALLOC_MEMPOOL_START 1
-#define LWIP_MALLOC_MEMPOOL(num, size) * MEMP_POOL_##size + 0
-#define LWIP_MALLOC_MEMPOOL_END
-#include "lwip/memp_std.h"
- ) ,
- /* Get the last (via:
- MEMP_POOL_HELPER_END = ((u8_t) 0 + MEMP_POOL_A*0 + MEMP_POOL_B*0 + MEMP_POOL_C*1) */
- MEMP_POOL_HELPER_LAST = ((u8_t)
-#define LWIP_MEMPOOL(name,num,size,desc)
-#define LWIP_MALLOC_MEMPOOL_START
-#define LWIP_MALLOC_MEMPOOL(num, size) 0 + MEMP_POOL_##size *
-#define LWIP_MALLOC_MEMPOOL_END 1
-#include "lwip/memp_std.h"
- )
-} memp_pool_helper_t;
-
-/* The actual start and stop values are here (cast them over)
- We use this helper type and these defines so we can avoid using const memp_t values */
-#define MEMP_POOL_FIRST ((memp_t) MEMP_POOL_HELPER_FIRST)
-#define MEMP_POOL_LAST ((memp_t) MEMP_POOL_HELPER_LAST)
-#endif /* MEM_USE_POOLS */
+#include "lwip/priv/memp_priv.h"
+#include "lwip/stats.h"
-#if MEMP_MEM_MALLOC || MEM_USE_POOLS
-extern const u16_t memp_sizes[MEMP_MAX];
-#endif /* MEMP_MEM_MALLOC || MEM_USE_POOLS */
+extern const struct memp_desc* const memp_pools[MEMP_MAX];
-#if MEMP_MEM_MALLOC
+/**
+ * @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
-#include "mem.h"
+#if MEMP_MEM_MALLOC
-#define memp_init()
-#define memp_malloc(type) mem_malloc(memp_sizes[type])
-#define memp_free(type, mem) mem_free(mem)
+#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 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 */
@@ -107,10 +148,8 @@ void *memp_malloc(memp_t type);
#endif
void memp_free(memp_t type, void *mem);
-#endif /* MEMP_MEM_MALLOC */
-
#ifdef __cplusplus
}
#endif
-#endif /* __LWIP_MEMP_H__ */
+#endif /* LWIP_HDR_MEMP_H */
diff --git a/lwip/src/include/ipv6/lwip/mld6.h b/lwip/src/include/lwip/mld6.h
index abd86e5..7fa0797 100644
--- a/lwip/src/include/ipv6/lwip/mld6.h
+++ b/lwip/src/include/lwip/mld6.h
@@ -40,8 +40,8 @@
* <delamer@inicotech.com>
*/
-#ifndef __LWIP_MLD6_H__
-#define __LWIP_MLD6_H__
+#ifndef LWIP_HDR_MLD6_H
+#define LWIP_HDR_MLD6_H
#include "lwip/opt.h"
@@ -50,16 +50,14 @@
#include "lwip/pbuf.h"
#include "lwip/netif.h"
-
#ifdef __cplusplus
extern "C" {
#endif
+/** MLD group */
struct mld_group {
/** next link */
struct mld_group *next;
- /** interface on which the group is active */
- struct netif *netif;
/** multicast address */
ip6_addr_t group_address;
/** signifies we were the last person to report */
@@ -72,42 +70,25 @@ struct mld_group {
u8_t use;
};
-/** 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_FIELD(u8_t type);
- PACK_STRUCT_FIELD(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_FIELD(ip6_addr_p_t multicast_address);
- /* Options follow. */
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
-
#define MLD6_TMR_INTERVAL 100 /* Milliseconds */
-/* MAC Filter Actions, these are passed to a netif's
- * mld_mac_filter callback function. */
-#define MLD6_DEL_MAC_FILTER 0
-#define MLD6_ADD_MAC_FILTER 1
-
-
-#define mld6_init() /* TODO should we init tables? */
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, ip6_addr_t *addr);
+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(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr);
-err_t mld6_leavegroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr);
-
+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
}
@@ -115,4 +96,4 @@ err_t mld6_leavegroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr);
#endif /* LWIP_IPV6_MLD && LWIP_IPV6 */
-#endif /* __LWIP_MLD6_H__ */
+#endif /* LWIP_HDR_MLD6_H */
diff --git a/lwip/src/include/lwip/nd6.h b/lwip/src/include/lwip/nd6.h
new file mode 100644
index 0000000..8204fa4
--- /dev/null
+++ b/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/lwip/src/include/lwip/netbuf.h b/lwip/src/include/lwip/netbuf.h
index d12fe27..42a911b 100644
--- a/lwip/src/include/lwip/netbuf.h
+++ b/lwip/src/include/lwip/netbuf.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,28 +16,33 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_NETBUF_H__
-#define __LWIP_NETBUF_H__
+#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"
@@ -46,17 +56,16 @@ extern "C" {
/** This netbuf includes a checksum */
#define NETBUF_FLAG_CHKSUM 0x02
+/** "Network buffer" - contains data and addressing info */
struct netbuf {
struct pbuf *p, *ptr;
- ipX_addr_t addr;
+ 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
- ipX_addr_t toaddr;
+ ip_addr_t toaddr;
#endif /* LWIP_NETBUF_RECVINFO */
#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */
};
@@ -68,8 +77,7 @@ 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);
+void netbuf_chain (struct netbuf *head, struct netbuf *tail);
err_t netbuf_data (struct netbuf *buf,
void **dataptr, u16_t *len);
@@ -82,31 +90,27 @@ void netbuf_first (struct netbuf *buf);
#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) (ipX_2_ip(&((buf)->addr)))
-#define netbuf_set_fromaddr(buf, fromaddr) ip_addr_set(ipX_2_ip(&((buf)->addr)), fromaddr)
+#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) (ipX_2_ip(&((buf)->toaddr)))
-#define netbuf_set_destaddr(buf, destaddr) ip_addr_set(ipX_2_ip(&((buf)->toaddr)), destaddr)
+#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 */
-#if LWIP_IPV6
-#define netbuf_fromaddr_ip6(buf) (ipX_2_ip6(&((buf)->addr)))
-#define netbuf_set_fromaddr_ip6(buf, fromaddr) ip6_addr_set(ipX_2_ip6(&((buf)->addr)), fromaddr)
-#define netbuf_destaddr_ip6(buf) (ipX_2_ip6(&((buf)->toaddr)))
-#define netbuf_set_destaddr_ip6(buf, destaddr) ip6_addr_set(ipX_2_ip6(&((buf)->toaddr)), destaddr)
-#endif /* LWIP_IPV6 */
-
-#define netbuf_fromaddr_ipX(buf) (&((buf)->addr))
-#define netbuf_destaddr_ipX(buf) (&((buf)->toaddr))
-
#ifdef __cplusplus
}
#endif
-#endif /* __LWIP_NETBUF_H__ */
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
+
+#endif /* LWIP_HDR_NETBUF_H */
diff --git a/lwip/src/include/lwip/netdb.h b/lwip/src/include/lwip/netdb.h
index 7587e2f..d3d15df 100644
--- a/lwip/src/include/lwip/netdb.h
+++ b/lwip/src/include/lwip/netdb.h
@@ -1,5 +1,10 @@
+/**
+ * @file
+ * NETDB API (sockets)
+ */
+
/*
- * Redistribution and use in source and binary forms, with or without modification,
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -8,33 +13,32 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Simon Goldschmidt
*
*/
-#ifndef __LWIP_NETDB_H__
-#define __LWIP_NETDB_H__
+#ifndef LWIP_HDR_NETDB_H
+#define LWIP_HDR_NETDB_H
#include "lwip/opt.h"
#if LWIP_DNS && LWIP_SOCKET
-#include <stddef.h> /* for size_t */
-
+#include "lwip/arch.h"
#include "lwip/inet.h"
#include "lwip/sockets.h"
@@ -44,15 +48,19 @@ extern "C" {
/* some rarely used options */
#ifndef LWIP_DNS_API_DECLARE_H_ERRNO
-#define LWIP_DNS_API_DECLARE_H_ERRNO 1
+#define LWIP_DNS_API_DECLARE_H_ERRNO 1
#endif
#ifndef LWIP_DNS_API_DEFINE_ERRORS
-#define LWIP_DNS_API_DEFINE_ERRORS 1
+#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
+#define LWIP_DNS_API_DECLARE_STRUCTS 1
#endif
#if LWIP_DNS_API_DEFINE_ERRORS
@@ -61,6 +69,7 @@ extern "C" {
#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
@@ -68,6 +77,17 @@ extern "C" {
#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. */
@@ -92,8 +112,10 @@ struct addrinfo {
};
#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 accessable error code set by the DNS API functions */
+/* application accessible error code set by the DNS API functions */
extern int h_errno;
#endif /* LWIP_DNS_API_DECLARE_H_ERRNO*/
@@ -107,10 +129,14 @@ int lwip_getaddrinfo(const char *nodename,
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 */
@@ -121,4 +147,4 @@ int lwip_getaddrinfo(const char *nodename,
#endif /* LWIP_DNS && LWIP_SOCKET */
-#endif /* __LWIP_NETDB_H__ */
+#endif /* LWIP_HDR_NETDB_H */
diff --git a/lwip/src/include/lwip/netif.h b/lwip/src/include/lwip/netif.h
index f977032..f552708 100644
--- a/lwip/src/include/lwip/netif.h
+++ b/lwip/src/include/lwip/netif.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,26 +16,26 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_NETIF_H__
-#define __LWIP_NETIF_H__
+#ifndef LWIP_HDR_NETIF_H
+#define LWIP_HDR_NETIF_H
#include "lwip/opt.h"
@@ -39,19 +44,10 @@
#include "lwip/err.h"
#include "lwip/ip_addr.h"
-#include "lwip/ip6_addr.h"
#include "lwip/def.h"
#include "lwip/pbuf.h"
-#if LWIP_DHCP
-struct dhcp;
-#endif
-#if LWIP_AUTOIP
-struct autoip;
-#endif
-#if LWIP_IPV6_DHCP6
-#include "lwip/dhcp6.h"
-#endif /* LWIP_IPV6_DHCP6 */
+#include "lwip/stats.h"
#ifdef __cplusplus
extern "C" {
@@ -60,47 +56,106 @@ extern "C" {
/* 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 */
+/** 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
+
+/** The size of a fully constructed netif name which the
+ * netif can be identified by in APIs. Composed of
+ * 2 chars, 3 (max) digits, and 1 \0
+ */
+#define NETIF_NAMESIZE 6
+
+/**
+ * @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 is set by the startup code (for static IP configuration) or
- * by dhcp/autoip when an address has been assigned.
+ * 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 netif is one end of a point-to-point connection.
- * Set by the netif driver in its init function. */
-#define NETIF_FLAG_POINTTOPOINT 0x04U
-/** If set, the interface is configured using DHCP.
- * Set by the DHCP code when starting or stopping DHCP. */
-#define NETIF_FLAG_DHCP 0x08U
/** If set, the interface has an active link
* (set by the network interface driver).
* Either set by the netif driver in its init function (if the link
* is up at that time) or at a later point once the link comes up
* (if link detection is supported by the hardware). */
-#define NETIF_FLAG_LINK_UP 0x10U
+#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 0x20U
+#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 0x40U
+#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 0x80U
+#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
/** Whether to pretend that we are every host for TCP packets.
* Set by netif_set_pretend_tcp. */
#define NETIF_FLAG_PRETEND_TCP 0x100U
+/**
+ * @}
+ */
+
+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.
*
@@ -114,6 +169,8 @@ typedef err_t (*netif_init_fn)(struct netif *netif);
* @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'.
@@ -123,10 +180,12 @@ typedef err_t (*netif_input_fn)(struct pbuf *p, struct netif *inp);
* @param ipaddr The IP address to which the packet shall be sent
*/
typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p,
- ip_addr_t *ipaddr);
+ 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 'nd_output' and set
+ * shall be sent. For ethernet netif, set this to 'ethip6_output' and set
* 'linkoutput'.
*
* @param netif The netif which shall send a packet
@@ -134,8 +193,9 @@ typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p,
* @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,
- ip6_addr_t *ipaddr);
+ 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.
*
@@ -145,49 +205,86 @@ typedef err_t (*netif_output_ip6_fn)(struct netif *netif, struct pbuf *p,
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,
- ip_addr_t *group, u8_t action);
+ 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,
- ip6_addr_t *group, u8_t action);
+ 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
+
+#if LWIP_NETIF_HWADDRHINT
+#define LWIP_NETIF_USE_HINTS 1
+struct netif_hint {
+ u8_t addr_hint;
+};
+#else /* LWIP_NETIF_HWADDRHINT */
+#define LWIP_NETIF_USE_HINTS 0
+#endif /* LWIP_NETIF_HWADDRHINT */
+
/** 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 {
+#if !LWIP_SINGLE_NETIF
/** pointer to next in linked list */
struct netif *next;
+#endif
+#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. */
- ip6_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES];
+ 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];
+#if LWIP_IPV6_ADDRESS_LIFETIMES
+ /** Remaining valid and preferred lifetime of each IPv6 address, in seconds.
+ * For valid lifetimes, the special value of IP6_ADDR_LIFE_STATIC (0)
+ * indicates the address is static and has no lifetimes. */
+ u32_t ip6_addr_valid_life[LWIP_IPV6_NUM_ADDRESSES];
+ u32_t ip6_addr_pref_life[LWIP_IPV6_NUM_ADDRESSES];
+#endif /* LWIP_IPV6_ADDRESS_LIFETIMES */
#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. */
+ * first resolves the hardware address, then sends the packet.
+ * For ethernet physical layer, this is usually etharp_output() */
netif_output_fn output;
- /** This function is called by the ARP module when it wants
+#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. */
+ * 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
@@ -207,72 +304,60 @@ struct netif {
/** This field can be set by the device driver and could point
* to state information for the device. */
void *state;
-#if LWIP_DHCP
- /** the DHCP client state information for this netif */
- struct dhcp *dhcp;
-#endif /* LWIP_DHCP */
-#if LWIP_AUTOIP
- /** the AutoIP client state information for this netif */
- struct autoip *autoip;
+#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_IPV6_DHCP6
- /** the DHCPv6 client state information for this netif */
- struct dhcp6 *dhcp6;
-#endif /* LWIP_IPV6_DHCP6 */
#if LWIP_NETIF_HOSTNAME
/* the hostname for this netif, NULL is a valid value */
- char* hostname;
+ 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 NETIF_FLAG_ above) */
+ /** number of bytes used in hwaddr */
+ u8_t hwaddr_len;
+ /** flags (@see @ref netif_flags) */
u16_t flags;
/** descriptive abbreviation */
char name[2];
- /** number of this interface */
+ /** number of this interface. Used for @ref if_api and @ref netifapi_netif,
+ * as well as for IPv6 zones */
u8_t num;
-#if LWIP_SNMP
- /** link type (from "snmp_ifType" enum from snmp.h) */
+#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 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 */
- u32_t ifinoctets;
- u32_t ifinucastpkts;
- u32_t ifinnucastpkts;
- u32_t ifindiscards;
- u32_t ifoutoctets;
- u32_t ifoutucastpkts;
- u32_t ifoutnucastpkts;
- u32_t ifoutdiscards;
-#endif /* LWIP_SNMP */
-#if LWIP_IGMP
+ 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_IGMP */
+#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 LWIP_NETIF_USE_HINTS
+ struct netif_hint *hints;
+#endif /* LWIP_NETIF_USE_HINTS */
#if ENABLE_LOOPBACK
/* List of packets to be queued for ourselves. */
struct pbuf *loop_first;
@@ -283,59 +368,78 @@ struct netif {
#endif /* ENABLE_LOOPBACK */
};
-#if LWIP_SNMP
-#define NETIF_INIT_SNMP(netif, type, speed) \
- /* use "snmp_ifType" enum from snmp.h for "type", snmp_ifType_ethernet_csmacd by example */ \
- (netif)->link_type = (type); \
- /* your link speed here (units: bits per second) */ \
- (netif)->link_speed = (speed); \
- (netif)->ts = 0; \
- (netif)->ifinoctets = 0; \
- (netif)->ifinucastpkts = 0; \
- (netif)->ifinnucastpkts = 0; \
- (netif)->ifindiscards = 0; \
- (netif)->ifoutoctets = 0; \
- (netif)->ifoutucastpkts = 0; \
- (netif)->ifoutnucastpkts = 0; \
- (netif)->ifoutdiscards = 0
-#else /* LWIP_SNMP */
-#define NETIF_INIT_SNMP(netif, type, speed)
-#endif /* LWIP_SNMP */
-
-
+#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 */
+
+#if LWIP_SINGLE_NETIF
+#define NETIF_FOREACH(netif) if (((netif) = netif_default) != NULL)
+#else /* LWIP_SINGLE_NETIF */
/** The list of network interfaces. */
extern struct netif *netif_list;
+#define NETIF_FOREACH(netif) for (netif = netif_list; netif != NULL; netif = netif->next)
+#endif /* LWIP_SINGLE_NETIF */
/** The default network interface. */
extern struct netif *netif_default;
void netif_init(void);
-struct netif *netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
- ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input);
-
-void
-netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
- ip_addr_t *gw);
+struct netif *netif_add_noaddr(struct netif *netif, void *state, netif_init_fn init, netif_input_fn input);
+
+#if LWIP_IPV4
+struct netif *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);
+void netif_set_addr(struct netif *netif, const ip4_addr_t *ipaddr, const ip4_addr_t *netmask,
+ const ip4_addr_t *gw);
+#else /* LWIP_IPV4 */
+struct netif *netif_add(struct netif *netif, void *state, netif_init_fn init, netif_input_fn input);
+#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(char *name);
+struct netif *netif_find(const char *name);
int netif_is_named (struct netif *netif, const char name[3]);
void netif_set_default(struct netif *netif);
-void netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr);
-void netif_set_netmask(struct netif *netif, ip_addr_t *netmask);
-void netif_set_gw(struct netif *netif, ip_addr_t *gw);
+#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);
void netif_set_pretend_tcp(struct netif *netif, u8_t pretend);
+/** @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 */
+
+#define netif_set_flags(netif, set_flags) do { (netif)->flags = (u8_t)((netif)->flags | (set_flags)); } while(0)
+#define netif_clear_flags(netif, clr_flags) do { (netif)->flags = (u8_t)((netif)->flags & ~(clr_flags)); } while(0)
+#define netif_is_flag_set(nefif, flag) (((netif)->flags & (flag)) != 0)
void netif_set_up(struct netif *netif);
void netif_set_down(struct netif *netif);
-/** Ask if an interface is up */
+/** @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
@@ -347,7 +451,7 @@ void netif_set_remove_callback(struct netif *netif, netif_status_callback_fn rem
void netif_set_link_up(struct netif *netif);
void netif_set_link_down(struct netif *netif);
-/** Ask if a link is up */
+/** 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
@@ -355,39 +459,193 @@ void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_
#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, ip_addr_t *dest_ip);
+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
-#define netif_ip6_addr(netif, i) (&((netif)->ip6_addr[(i)]))
-#define netif_ip6_addr_state(netif, i) ((netif)->ip6_addr_state[(i)])
-#define netif_ip6_addr_set_state(netif, i, state) ((netif)->ip6_addr_state[(i)] = (state))
-s8_t netif_matches_ip6_addr(struct netif * netif, ip6_addr_t * ip6addr);
-void netif_create_ip6_linklocal_address(struct netif * netif, u8_t from_mac_48bit);
+/** @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)
+#if LWIP_IPV6_ADDRESS_LIFETIMES
+#define netif_ip6_addr_valid_life(netif, i) \
+ (((netif) != NULL) ? ((netif)->ip6_addr_valid_life[i]) : IP6_ADDR_LIFE_STATIC)
+#define netif_ip6_addr_set_valid_life(netif, i, secs) \
+ do { if (netif != NULL) { (netif)->ip6_addr_valid_life[i] = (secs); }} while (0)
+#define netif_ip6_addr_pref_life(netif, i) \
+ (((netif) != NULL) ? ((netif)->ip6_addr_pref_life[i]) : IP6_ADDR_LIFE_STATIC)
+#define netif_ip6_addr_set_pref_life(netif, i, secs) \
+ do { if (netif != NULL) { (netif)->ip6_addr_pref_life[i] = (secs); }} while (0)
+#define netif_ip6_addr_isstatic(netif, i) \
+ (netif_ip6_addr_valid_life((netif), (i)) == IP6_ADDR_LIFE_STATIC)
+#else /* !LWIP_IPV6_ADDRESS_LIFETIMES */
+#define netif_ip6_addr_isstatic(netif, i) (1) /* all addresses are static */
+#endif /* !LWIP_IPV6_ADDRESS_LIFETIMES */
#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 */
+#if LWIP_NETIF_USE_HINTS
+#define NETIF_SET_HINTS(netif, netifhint) (netif)->hints = (netifhint)
+#define NETIF_RESET_HINTS(netif) (netif)->hints = NULL
+#else /* LWIP_NETIF_USE_HINTS */
+#define NETIF_SET_HINTS(netif, netifhint)
+#define NETIF_RESET_HINTS(netif)
+#endif /* LWIP_NETIF_USE_HINTS */
+
+u8_t netif_name_to_index(const char *name);
+char * netif_index_to_name(u8_t idx, char *name);
+struct netif* netif_get_by_index(u8_t idx);
+
+/* Interface indexes always start at 1 per RFC 3493, section 4, num starts at 0 (internal index is 0..254)*/
+#define netif_get_index(netif) ((u8_t)((netif)->num + 1))
+#define NETIF_NO_INDEX (0)
+
+/**
+ * @ingroup netif
+ * Extended netif status callback (NSC) reasons enumeration.
+ * May be extended in the future!
+ */
+typedef enum
+{
+ /** netif was added. arg: NULL. Called AFTER netif was added. */
+ LWIP_NSC_NETIF_ADDED,
+ /** netif was removed. arg: NULL. Called BEFORE netif is removed. */
+ LWIP_NSC_NETIF_REMOVED,
+ /** link changed */
+ LWIP_NSC_LINK_CHANGED,
+ /** netif administrative status changed.\n
+ * up is called AFTER netif is set up.\n
+ * down is called BEFORE the netif is actually set down. */
+ LWIP_NSC_STATUS_CHANGED,
+ /** IPv4 address has changed */
+ LWIP_NSC_IPV4_ADDRESS_CHANGED,
+ /** IPv4 gateway has changed */
+ LWIP_NSC_IPV4_GATEWAY_CHANGED,
+ /** IPv4 netmask has changed */
+ LWIP_NSC_IPV4_NETMASK_CHANGED,
+ /** called AFTER IPv4 address/gateway/netmask changes have been applied. arg: NULL */
+ LWIP_NSC_IPV4_SETTINGS_CHANGED,
+ /** IPv6 address was added */
+ LWIP_NSC_IPV6_SET,
+ /** IPv6 address state has changed */
+ LWIP_NSC_IPV6_ADDR_STATE_CHANGED
+} netif_nsc_reason_t;
+
+/** @ingroup netif
+ * Argument supplied to netif_ext_callback_fn.
+ */
+typedef union
+{
+ /** Args to LWIP_NSC_LINK_CHANGED callback */
+ struct link_changed_s
+ {
+ /** 1: up; 0: down */
+ u8_t state;
+ } link_changed;
+ /** Args to LWIP_NSC_STATUS_CHANGED callback */
+ struct status_changed_s
+ {
+ /** 1: up; 0: down */
+ u8_t state;
+ } status_changed;
+ /** Args to LWIP_NSC_IPV4_ADDRESS_CHANGED callback */
+ struct ipv4_changed_s
+ {
+ /** Old IPv4 address */
+ const ip_addr_t* old_address;
+ } ipv4_changed;
+ /** Args to LWIP_NSC_IPV4_GATEWAY_CHANGED callback */
+ struct ipv4_gw_changed_s
+ {
+ /** Old IPv4 gateway */
+ const ip_addr_t* old_address;
+ } ipv4_gw_changed;
+ /** Args to LWIP_NSC_IPV4_NETMASK_CHANGED callback */
+ struct ipv4_nm_changed_s
+ {
+ /** Old IPv4 netmask */
+ const ip_addr_t* old_address;
+ } ipv4_nm_changed;
+ /** Args to LWIP_NSC_IPV6_SET callback */
+ struct ipv6_set_s
+ {
+ /** Index of changed IPv6 address */
+ s8_t addr_index;
+ /** Old IPv6 address */
+ const ip_addr_t* old_address;
+ } ipv6_set;
+ /** Args to LWIP_NSC_IPV6_ADDR_STATE_CHANGED callback */
+ struct ipv6_addr_state_changed_s
+ {
+ /** Index of affected IPv6 address */
+ s8_t addr_index;
+ /** Affected IPv6 address */
+ const ip_addr_t* address;
+ } ipv6_addr_state_changed;
+} netif_ext_callback_args_t;
+
+/**
+ * @ingroup netif
+ * Function used for extended netif status callbacks
+ * Note: When parsing reason argument, keep in mind that more reasons may be added in the future!
+ * @param netif netif that is affected by change
+ * @param reason change reason
+ * @param args depends on reason, see reason description
+ */
+typedef void (*netif_ext_callback_fn)(struct netif* netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t* args);
+
+#if LWIP_NETIF_EXT_STATUS_CALLBACK
+struct netif_ext_callback;
+typedef struct netif_ext_callback
+{
+ netif_ext_callback_fn callback_fn;
+ struct netif_ext_callback* next;
+} netif_ext_callback_t;
+
+#define NETIF_DECLARE_EXT_CALLBACK(name) static netif_ext_callback_t name;
+void netif_add_ext_callback(netif_ext_callback_t* callback, netif_ext_callback_fn fn);
+void netif_invoke_ext_callback(struct netif* netif, netif_nsc_reason_t reason, const netif_ext_callback_args_t* args);
+#else
+#define NETIF_DECLARE_EXT_CALLBACK(name)
+#define netif_add_ext_callback(callback, fn)
+#define netif_invoke_ext_callback(netif, reason, args)
+#endif
#ifdef __cplusplus
}
#endif
-#endif /* __LWIP_NETIF_H__ */
+#endif /* LWIP_HDR_NETIF_H */
diff --git a/lwip/src/include/lwip/netifapi.h b/lwip/src/include/lwip/netifapi.h
index 33318ef..546f03f 100644
--- a/lwip/src/include/lwip/netifapi.h
+++ b/lwip/src/include/lwip/netifapi.h
@@ -1,5 +1,10 @@
+/**
+ * @file
+ * netif API (to be used from non-TCPIP threads)
+ */
+
/*
- * Redistribution and use in source and binary forms, with or without modification,
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -8,25 +13,24 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
*/
-
-#ifndef __LWIP_NETIFAPI_H__
-#define __LWIP_NETIFAPI_H__
+#ifndef LWIP_HDR_NETIFAPI_H
+#define LWIP_HDR_NETIFAPI_H
#include "lwip/opt.h"
@@ -36,67 +40,102 @@
#include "lwip/netif.h"
#include "lwip/dhcp.h"
#include "lwip/autoip.h"
+#include "lwip/priv/tcpip_priv.h"
+#include "lwip/priv/api_msg.h"
#ifdef __cplusplus
extern "C" {
#endif
-typedef void (*netifapi_void_fn)(struct netif *netif);
-typedef err_t (*netifapi_errt_fn)(struct netif *netif);
-
-struct netifapi_msg_msg {
-#if !LWIP_TCPIP_CORE_LOCKING
- sys_sem_t sem;
-#endif /* !LWIP_TCPIP_CORE_LOCKING */
- err_t err;
- struct netif *netif;
- union {
- struct {
- ip_addr_t *ipaddr;
- ip_addr_t *netmask;
- ip_addr_t *gw;
- void *state;
- netif_init_fn init;
- netif_input_fn input;
- } add;
- struct {
- netifapi_void_fn voidfunc;
- netifapi_errt_fn errtfunc;
- } common;
- } msg;
-};
+/* 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);
-struct netifapi_msg {
- void (* function)(struct netifapi_msg_msg *msg);
- struct netifapi_msg_msg msg;
-};
+#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);
-/* API for application */
-err_t netifapi_netif_add ( struct netif *netif,
- ip_addr_t *ipaddr,
- ip_addr_t *netmask,
- ip_addr_t *gw,
- void *state,
- netif_init_fn init,
- netif_input_fn input);
+/** @ingroup netifapi_netif */
+err_t netifapi_netif_name_to_index(const char *name, u8_t *index);
+/** @ingroup netifapi_netif */
+err_t netifapi_netif_index_to_name(u8_t index, char *name);
-err_t netifapi_netif_set_addr ( struct netif *netif,
- ip_addr_t *ipaddr,
- ip_addr_t *netmask,
- ip_addr_t *gw );
+/** @ingroup netifapi_netif
+ * @see netif_remove()
+ */
+#define netifapi_netif_remove(n) netifapi_netif_common(n, netif_remove, NULL)
+/** @ingroup netifapi_netif
+ * @see netif_set_up()
+ */
+#define netifapi_netif_set_up(n) netifapi_netif_common(n, netif_set_up, NULL)
+/** @ingroup netifapi_netif
+ * @see netif_set_down()
+ */
+#define netifapi_netif_set_down(n) netifapi_netif_common(n, netif_set_down, NULL)
+/** @ingroup netifapi_netif
+ * @see netif_set_default()
+ */
+#define netifapi_netif_set_default(n) netifapi_netif_common(n, netif_set_default, NULL)
+/** @ingroup netifapi_netif
+ * @see netif_set_link_up()
+ */
+#define netifapi_netif_set_link_up(n) netifapi_netif_common(n, netif_set_link_up, NULL)
+/** @ingroup netifapi_netif
+ * @see netif_set_link_down()
+ */
+#define netifapi_netif_set_link_down(n) netifapi_netif_common(n, netif_set_link_down, NULL)
-err_t netifapi_netif_common ( struct netif *netif,
- netifapi_void_fn voidfunc,
- netifapi_errt_fn errtfunc);
+/**
+ * @defgroup netifapi_dhcp4 DHCPv4
+ * @ingroup netifapi
+ * To be called from non-TCPIP threads
+ */
+/** @ingroup netifapi_dhcp4
+ * @see dhcp_start()
+ */
+#define netifapi_dhcp_start(n) netifapi_netif_common(n, NULL, dhcp_start)
+/**
+ * @ingroup netifapi_dhcp4
+ * @deprecated Use netifapi_dhcp_release_and_stop() instead.
+ */
+#define netifapi_dhcp_stop(n) netifapi_netif_common(n, dhcp_stop, NULL)
+/** @ingroup netifapi_dhcp4
+ * @see dhcp_inform()
+ */
+#define netifapi_dhcp_inform(n) netifapi_netif_common(n, dhcp_inform, NULL)
+/** @ingroup netifapi_dhcp4
+ * @see dhcp_renew()
+ */
+#define netifapi_dhcp_renew(n) netifapi_netif_common(n, NULL, dhcp_renew)
+/**
+ * @ingroup netifapi_dhcp4
+ * @deprecated Use netifapi_dhcp_release_and_stop() instead.
+ */
+#define netifapi_dhcp_release(n) netifapi_netif_common(n, NULL, dhcp_release)
+/** @ingroup netifapi_dhcp4
+ * @see dhcp_release_and_stop()
+ */
+#define netifapi_dhcp_release_and_stop(n) netifapi_netif_common(n, dhcp_release_and_stop, NULL)
-#define netifapi_netif_remove(n) netifapi_netif_common(n, netif_remove, NULL)
-#define netifapi_netif_set_up(n) netifapi_netif_common(n, netif_set_up, NULL)
-#define netifapi_netif_set_down(n) netifapi_netif_common(n, netif_set_down, NULL)
-#define netifapi_netif_set_default(n) netifapi_netif_common(n, netif_set_default, NULL)
-#define netifapi_dhcp_start(n) netifapi_netif_common(n, NULL, dhcp_start)
-#define netifapi_dhcp_stop(n) netifapi_netif_common(n, dhcp_stop, NULL)
+/**
+ * @defgroup netifapi_autoip AUTOIP
+ * @ingroup netifapi
+ * To be called from non-TCPIP threads
+ */
+/** @ingroup netifapi_autoip
+ * @see autoip_start()
+ */
#define netifapi_autoip_start(n) netifapi_netif_common(n, NULL, autoip_start)
+/** @ingroup netifapi_autoip
+ * @see autoip_stop()
+ */
#define netifapi_autoip_stop(n) netifapi_netif_common(n, NULL, autoip_stop)
#ifdef __cplusplus
@@ -105,4 +144,4 @@ err_t netifapi_netif_common ( struct netif *netif,
#endif /* LWIP_NETIF_API */
-#endif /* __LWIP_NETIFAPI_H__ */
+#endif /* LWIP_HDR_NETIFAPI_H */
diff --git a/lwip/src/include/lwip/opt.h b/lwip/src/include/lwip/opt.h
index e51f8e5..465a9c7 100644
--- a/lwip/src/include/lwip/opt.h
+++ b/lwip/src/include/lwip/opt.h
@@ -6,9 +6,9 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -17,70 +17,123 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_OPT_H__
-#define __LWIP_OPT_H__
+
+/*
+ * 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 dont like!
+ * will be set to standard values. Override anything you don't like!
*/
#include "lwipopts.h"
#include "lwip/debug.h"
-/*
- -----------------------------------------------
- ---------- Platform specific locking ----------
- -----------------------------------------------
-*/
+/**
+ * @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
+ * @{
+ */
/**
- * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain
- * critical regions during buffer allocation, deallocation and memory
- * allocation and deallocation.
+ * 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!)
*/
-#ifndef SYS_LIGHTWEIGHT_PROT
-#define SYS_LIGHTWEIGHT_PROT 0
+#if !defined NO_SYS || defined __DOXYGEN__
+#define NO_SYS 0
#endif
+/**
+ * @}
+ */
-/**
- * NO_SYS==1: Provides VERY minimal functionality. Otherwise,
- * use lwIP facilities.
+/**
+ * @defgroup lwip_opts_timers Timers
+ * @ingroup lwip_opts_infrastructure
+ * @{
*/
-#ifndef NO_SYS
-#define NO_SYS 0
+/**
+ * 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
/**
- * NO_SYS_NO_TIMERS==1: Drop support for sys_timeout when NO_SYS==1
- * Mainly for compatibility to old versions.
+ * 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()
*/
-#ifndef NO_SYS_NO_TIMERS
-#define NO_SYS_NO_TIMERS 0
+#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
*/
-#ifndef MEMCPY
+#if !defined MEMCPY || defined __DOXYGEN__
#define MEMCPY(dst,src,len) memcpy(dst,src,len)
#endif
@@ -88,39 +141,128 @@
* SMEMCPY: override this with care! Some compilers (e.g. gcc) can inline a
* call to memcpy() if the length is known at compile time and is small.
*/
-#ifndef SMEMCPY
+#if !defined SMEMCPY || defined __DOXYGEN__
#define SMEMCPY(dst,src,len) memcpy(dst,src,len)
#endif
+/**
+ * MEMMOVE: override this if you have a faster implementation at hand than the
+ * one included in your C library. lwIP currently uses MEMMOVE only when IPv6
+ * fragmentation support is enabled.
+ */
+#if !defined MEMMOVE || defined __DOXYGEN__
+#define MEMMOVE(dst,src,len) memmove(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.
*/
-#ifndef MEM_LIBC_MALLOC
+#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 and usage from interrupts!
-*/
-#ifndef MEMP_MEM_MALLOC
+ * 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
/**
+ * MEMP_MEM_INIT==1: Force use of memset to initialize pool memory.
+ * Useful if pool are moved in uninitialized section of memory. This will ensure
+ * default values in pcbs struct are well initialized in all conditions.
+ */
+#if !defined MEMP_MEM_INIT || defined __DOXYGEN__
+#define MEMP_MEM_INIT 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
+ * 4 byte alignment -> \#define MEM_ALIGNMENT 4
+ * 2 byte alignment -> \#define MEM_ALIGNMENT 2
*/
-#ifndef MEM_ALIGNMENT
+#if !defined MEM_ALIGNMENT || defined __DOXYGEN__
#define MEM_ALIGNMENT 1
#endif
@@ -128,20 +270,11 @@
* MEM_SIZE: the size of the heap memory. If the application will send
* a lot of data that needs to be copied, this should be set high.
*/
-#ifndef MEM_SIZE
+#if !defined MEM_SIZE || defined __DOXYGEN__
#define MEM_SIZE 1600
#endif
/**
- * MEMP_SEPARATE_POOLS: if defined to 1, each pool is placed in its own array.
- * This can be used to individually change the location of each pool.
- * Default is one big array for all pools
- */
-#ifndef MEMP_SEPARATE_POOLS
-#define MEMP_SEPARATE_POOLS 0
-#endif
-
-/**
* MEMP_OVERFLOW_CHECK: memp overflow protection reserves a configurable
* amount of bytes before and after each memp element in every pool and fills
* it with a prominent default value.
@@ -150,7 +283,7 @@
* MEMP_OVERFLOW_CHECK >= 2 checks each element in every pool every time
* memp_malloc() or memp_free() is called (useful but slow!)
*/
-#ifndef MEMP_OVERFLOW_CHECK
+#if !defined MEMP_OVERFLOW_CHECK || defined __DOXYGEN__
#define MEMP_OVERFLOW_CHECK 0
#endif
@@ -158,7 +291,7 @@
* MEMP_SANITY_CHECK==1: run a sanity check after each memp_free() to make
* sure that there are no cycles in the linked lists.
*/
-#ifndef MEMP_SANITY_CHECK
+#if !defined MEMP_SANITY_CHECK || defined __DOXYGEN__
#define MEMP_SANITY_CHECK 0
#endif
@@ -168,7 +301,7 @@
* the smallest pool that can provide the length needed is returned.
* To use this, MEMP_USE_CUSTOM_POOLS also has to be enabled.
*/
-#ifndef MEM_USE_POOLS
+#if !defined MEM_USE_POOLS || defined __DOXYGEN__
#define MEM_USE_POOLS 0
#endif
@@ -176,17 +309,17 @@
* MEM_USE_POOLS_TRY_BIGGER_POOL==1: if one malloc-pool is empty, try the next
* bigger pool - WARNING: THIS MIGHT WASTE MEMORY but it can make a system more
* reliable. */
-#ifndef MEM_USE_POOLS_TRY_BIGGER_POOL
+#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
- * inlude path somewhere.
+ * by lwIP. If you set this to 1, you must have lwippools.h in your
+ * include path somewhere.
*/
-#ifndef MEMP_USE_CUSTOM_POOLS
+#if !defined MEMP_USE_CUSTOM_POOLS || defined __DOXYGEN__
#define MEMP_USE_CUSTOM_POOLS 0
#endif
@@ -208,9 +341,12 @@
* - pbuf_free_callback(p);
* - mem_free_callback(m);
*/
-#ifndef LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+#if !defined LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT || defined __DOXYGEN__
#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 0
#endif
+/**
+ * @}
+ */
/*
------------------------------------------------
@@ -218,11 +354,16 @@
------------------------------------------------
*/
/**
+ * @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.
*/
-#ifndef MEMP_NUM_PBUF
+#if !defined MEMP_NUM_PBUF || defined __DOXYGEN__
#define MEMP_NUM_PBUF 16
#endif
@@ -230,7 +371,7 @@
* MEMP_NUM_RAW_PCB: Number of raw connection PCBs
* (requires the LWIP_RAW option)
*/
-#ifndef MEMP_NUM_RAW_PCB
+#if !defined MEMP_NUM_RAW_PCB || defined __DOXYGEN__
#define MEMP_NUM_RAW_PCB 4
#endif
@@ -239,15 +380,15 @@
* per active UDP "connection".
* (requires the LWIP_UDP option)
*/
-#ifndef MEMP_NUM_UDP_PCB
+#if !defined MEMP_NUM_UDP_PCB || defined __DOXYGEN__
#define MEMP_NUM_UDP_PCB 4
#endif
/**
- * MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections.
+ * MEMP_NUM_TCP_PCB: the number of simultaneously active TCP connections.
* (requires the LWIP_TCP option)
*/
-#ifndef MEMP_NUM_TCP_PCB
+#if !defined MEMP_NUM_TCP_PCB || defined __DOXYGEN__
#define MEMP_NUM_TCP_PCB 5
#endif
@@ -255,7 +396,7 @@
* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections.
* (requires the LWIP_TCP option)
*/
-#ifndef MEMP_NUM_TCP_PCB_LISTEN
+#if !defined MEMP_NUM_TCP_PCB_LISTEN || defined __DOXYGEN__
#define MEMP_NUM_TCP_PCB_LISTEN 8
#endif
@@ -263,64 +404,79 @@
* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments.
* (requires the LWIP_TCP option)
*/
-#ifndef MEMP_NUM_TCP_SEG
+#if !defined MEMP_NUM_TCP_SEG || defined __DOXYGEN__
#define MEMP_NUM_TCP_SEG 16
#endif
/**
+ * MEMP_NUM_ALTCP_PCB: the number of simultaneously active altcp layer pcbs.
+ * (requires the LWIP_ALTCP option)
+ * Connections with multiple layers require more than one altcp_pcb (e.g. TLS
+ * over TCP requires 2 altcp_pcbs, one for TLS and one for TCP).
+ */
+#if !defined MEMP_NUM_ALTCP_PCB || defined __DOXYGEN__
+#define MEMP_NUM_ALTCP_PCB MEMP_NUM_TCP_PCB
+#endif
+
+/**
* MEMP_NUM_REASSDATA: the number of IP packets simultaneously queued for
* reassembly (whole packets, not fragments!)
*/
-#ifndef MEMP_NUM_REASSDATA
+#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 IP_FRAG_USES_STATIC_BUF==0 and
- * LWIP_NETIF_TX_SINGLE_PBUF==0 and only has to be > 1 with DMA-enabled MACs
- * where the packet is not yet sent when netif->output returns.
+ * 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.
*/
-#ifndef MEMP_NUM_FRAG_PBUF
+#if !defined MEMP_NUM_FRAG_PBUF || defined __DOXYGEN__
#define MEMP_NUM_FRAG_PBUF 15
#endif
/**
- * MEMP_NUM_ARP_QUEUE: the number of simulateously queued outgoing
+ * 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)
*/
-#ifndef MEMP_NUM_ARP_QUEUE
+#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 et the same time (one per netif - allsystems group -, plus one
+ * can be members at the same time (one per netif - allsystems group -, plus one
* per netif membership).
* (requires the LWIP_IGMP option)
*/
-#ifndef MEMP_NUM_IGMP_GROUP
+#if !defined MEMP_NUM_IGMP_GROUP || defined __DOXYGEN__
#define MEMP_NUM_IGMP_GROUP 8
#endif
/**
- * MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts.
- * (requires NO_SYS==0)
+ * The number of sys timeouts used by the core stack (not apps)
+ * The default number of timeouts is calculated here for all enabled modules.
+ */
+#define LWIP_NUM_SYS_TIMEOUT_INTERNAL (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_NUM_TIMEOUTS + (LWIP_IPV6 * (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD)))
+
+/**
+ * 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'.
*/
-#ifndef MEMP_NUM_SYS_TIMEOUT
-#define 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))
+#if !defined MEMP_NUM_SYS_TIMEOUT || defined __DOXYGEN__
+#define MEMP_NUM_SYS_TIMEOUT LWIP_NUM_SYS_TIMEOUT_INTERNAL
#endif
/**
* MEMP_NUM_NETBUF: the number of struct netbufs.
* (only needed if you use the sequential API, like api_lib.c)
*/
-#ifndef MEMP_NUM_NETBUF
+#if !defined MEMP_NUM_NETBUF || defined __DOXYGEN__
#define MEMP_NUM_NETBUF 2
#endif
@@ -328,91 +484,89 @@
* MEMP_NUM_NETCONN: the number of struct netconns.
* (only needed if you use the sequential API, like api_lib.c)
*/
-#ifndef MEMP_NUM_NETCONN
+#if !defined MEMP_NUM_NETCONN || defined __DOXYGEN__
#define MEMP_NUM_NETCONN 4
#endif
/**
+ * MEMP_NUM_SELECT_CB: the number of struct lwip_select_cb.
+ * (Only needed if you have LWIP_MPU_COMPATIBLE==1 and use the socket API.
+ * In that case, you need one per thread calling lwip_select.)
+ */
+#if !defined MEMP_NUM_SELECT_CB || defined __DOXYGEN__
+#define MEMP_NUM_SELECT_CB 4
+#endif
+
+/**
* MEMP_NUM_TCPIP_MSG_API: the number of struct tcpip_msg, which are used
- * for callback/timeout API communication.
+ * for callback/timeout API communication.
* (only needed if you use tcpip.c)
*/
-#ifndef MEMP_NUM_TCPIP_MSG_API
+#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.
+ * for incoming packets.
* (only needed if you use tcpip.c)
*/
-#ifndef MEMP_NUM_TCPIP_MSG_INPKT
+#if !defined MEMP_NUM_TCPIP_MSG_INPKT || defined __DOXYGEN__
#define MEMP_NUM_TCPIP_MSG_INPKT 8
#endif
/**
- * MEMP_NUM_SNMP_NODE: the number of leafs in the SNMP tree.
+ * MEMP_NUM_NETDB: the number of concurrently running lwip_addrinfo() calls
+ * (before freeing the corresponding memory using lwip_freeaddrinfo()).
*/
-#ifndef MEMP_NUM_SNMP_NODE
-#define MEMP_NUM_SNMP_NODE 50
+#if !defined MEMP_NUM_NETDB || defined __DOXYGEN__
+#define MEMP_NUM_NETDB 1
#endif
/**
- * MEMP_NUM_SNMP_ROOTNODE: the number of branches in the SNMP tree.
- * Every branch has one leaf (MEMP_NUM_SNMP_NODE) at least!
+ * MEMP_NUM_LOCALHOSTLIST: the number of host entries in the local host list
+ * if DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1.
*/
-#ifndef MEMP_NUM_SNMP_ROOTNODE
-#define MEMP_NUM_SNMP_ROOTNODE 30
+#if !defined MEMP_NUM_LOCALHOSTLIST || defined __DOXYGEN__
+#define MEMP_NUM_LOCALHOSTLIST 1
#endif
/**
- * MEMP_NUM_SNMP_VARBIND: the number of concurrent requests (does not have to
- * be changed normally) - 2 of these are used per request (1 for input,
- * 1 for output)
+ * PBUF_POOL_SIZE: the number of buffers in the pbuf pool.
*/
-#ifndef MEMP_NUM_SNMP_VARBIND
-#define MEMP_NUM_SNMP_VARBIND 2
+#if !defined PBUF_POOL_SIZE || defined __DOXYGEN__
+#define PBUF_POOL_SIZE 16
#endif
-/**
- * MEMP_NUM_SNMP_VALUE: the number of OID or values concurrently used
- * (does not have to be changed normally) - 3 of these are used per request
- * (1 for the value read and 2 for OIDs - input and output)
+/** MEMP_NUM_API_MSG: the number of concurrently active calls to various
+ * socket, netconn, and tcpip functions
*/
-#ifndef MEMP_NUM_SNMP_VALUE
-#define MEMP_NUM_SNMP_VALUE 3
+#if !defined MEMP_NUM_API_MSG || defined __DOXYGEN__
+#define MEMP_NUM_API_MSG MEMP_NUM_TCPIP_MSG_API
#endif
-/**
- * MEMP_NUM_NETDB: the number of concurrently running lwip_addrinfo() calls
- * (before freeing the corresponding memory using lwip_freeaddrinfo()).
+/** MEMP_NUM_DNS_API_MSG: the number of concurrently active calls to netconn_gethostbyname
*/
-#ifndef MEMP_NUM_NETDB
-#define MEMP_NUM_NETDB 1
+#if !defined MEMP_NUM_DNS_API_MSG || defined __DOXYGEN__
+#define MEMP_NUM_DNS_API_MSG MEMP_NUM_TCPIP_MSG_API
#endif
-/**
- * MEMP_NUM_LOCALHOSTLIST: the number of host entries in the local host list
- * if DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1.
+/** MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA: the number of concurrently active calls
+ * to getsockopt/setsockopt
*/
-#ifndef MEMP_NUM_LOCALHOSTLIST
-#define MEMP_NUM_LOCALHOSTLIST 1
+#if !defined MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA || defined __DOXYGEN__
+#define MEMP_NUM_SOCKET_SETGETSOCKOPT_DATA MEMP_NUM_TCPIP_MSG_API
#endif
-/**
- * MEMP_NUM_PPPOE_INTERFACES: the number of concurrently active PPPoE
- * interfaces (only used with PPPOE_SUPPORT==1)
+/** MEMP_NUM_NETIFAPI_MSG: the number of concurrently active calls to the
+ * netifapi functions
*/
-#ifndef MEMP_NUM_PPPOE_INTERFACES
-#define MEMP_NUM_PPPOE_INTERFACES 1
+#if !defined MEMP_NUM_NETIFAPI_MSG || defined __DOXYGEN__
+#define MEMP_NUM_NETIFAPI_MSG MEMP_NUM_TCPIP_MSG_API
#endif
-
/**
- * PBUF_POOL_SIZE: the number of buffers in the pbuf pool.
+ * @}
*/
-#ifndef PBUF_POOL_SIZE
-#define PBUF_POOL_SIZE 16
-#endif
/*
---------------------------------
@@ -420,19 +574,32 @@
---------------------------------
*/
/**
+ * @defgroup lwip_opts_arp ARP
+ * @ingroup lwip_opts_ipv4
+ * @{
+ */
+/**
* LWIP_ARP==1: Enable ARP functionality.
*/
-#ifndef LWIP_ARP
+#if !defined LWIP_ARP || defined __DOXYGEN__
#define LWIP_ARP 1
#endif
/**
* ARP_TABLE_SIZE: Number of active MAC-IP address pairs cached.
*/
-#ifndef ARP_TABLE_SIZE
+#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.
@@ -440,41 +607,36 @@
* startup time. Set this to 1 if you know your application sends more than one
* packet in a row to an IP address that is not in the ARP cache.
*/
-#ifndef ARP_QUEUEING
+#if !defined ARP_QUEUEING || defined __DOXYGEN__
#define ARP_QUEUEING 0
#endif
-/**
- * ETHARP_TRUST_IP_MAC==1: Incoming IP packets cause the ARP table to be
- * updated with the source MAC and IP addresses supplied in the packet.
- * You may want to disable this if you do not trust LAN peers to have the
- * correct addresses, or as a limited approach to attempt to handle
- * spoofing. If disabled, lwIP will need to make a new ARP request if
- * the peer is not already in the ARP table, adding a little latency.
- * The peer *is* in the ARP table if it requested our address before.
- * Also notice that this slows down input processing of every IP packet!
+/** 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.
*/
-#ifndef ETHARP_TRUST_IP_MAC
-#define ETHARP_TRUST_IP_MAC 0
+#if !defined ARP_QUEUE_LEN || defined __DOXYGEN__
+#define ARP_QUEUE_LEN 3
#endif
/**
- * ETHARP_SUPPORT_VLAN==1: support receiving ethernet packets with VLAN header.
+ * 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.
*/
-#ifndef ETHARP_SUPPORT_VLAN
+#if !defined ETHARP_SUPPORT_VLAN || defined __DOXYGEN__
#define ETHARP_SUPPORT_VLAN 0
#endif
-/** LWIP_ETHERNET==1: enable ethernet support for PPPoE even though ARP
- * might be disabled
+/** LWIP_ETHERNET==1: enable ethernet support even though ARP might be disabled
*/
-#ifndef LWIP_ETHERNET
-#define LWIP_ETHERNET (LWIP_ARP || PPPOE_SUPPORT)
+#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
@@ -482,17 +644,27 @@
* without this padding e.g. addresses in the IP header will not be aligned
* on a 32-bit boundary, so setting this to 2 can speed up 32-bit-platforms.
*/
-#ifndef ETH_PAD_SIZE
+#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).
*/
-#ifndef ETHARP_SUPPORT_STATIC_ENTRIES
+#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 !LWIP_SINGLE_NETIF
+#endif
+/**
+ * @}
+ */
/*
--------------------------------
@@ -500,21 +672,24 @@
--------------------------------
*/
/**
- * 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.
+ * @defgroup lwip_opts_ipv4 IPv4
+ * @ingroup lwip_opts
+ * @{
*/
-#ifndef IP_FORWARD
-#define IP_FORWARD 0
+/**
+ * LWIP_IPV4==1: Enable IPv4
+ */
+#if !defined LWIP_IPV4 || defined __DOXYGEN__
+#define LWIP_IPV4 1
#endif
/**
- * IP_OPTIONS_ALLOWED: Defines the behavior for IP options.
- * IP_OPTIONS_ALLOWED==0: All packets with IP options are dropped.
- * IP_OPTIONS_ALLOWED==1: IP options are allowed (but not parsed).
+ * IP_FORWARD==1: Enables the ability to forward IP packets across network
+ * interfaces. If you are going to run lwIP on a device with only one network
+ * interface, define this to 0.
*/
-#ifndef IP_OPTIONS_ALLOWED
-#define IP_OPTIONS_ALLOWED 1
+#if !defined IP_FORWARD || defined __DOXYGEN__
+#define IP_FORWARD 0
#endif
/**
@@ -522,7 +697,7 @@
* this option does not affect outgoing packet sizes, which can be controlled
* via IP_FRAG.
*/
-#ifndef IP_REASSEMBLY
+#if !defined IP_REASSEMBLY || defined __DOXYGEN__
#define IP_REASSEMBLY 1
#endif
@@ -531,17 +706,36 @@
* that this option does not affect incoming packet sizes, which can be
* controlled via IP_REASSEMBLY.
*/
-#ifndef IP_FRAG
+#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.
*/
-#ifndef IP_REASS_MAXAGE
-#define IP_REASS_MAXAGE 3
+#if !defined IP_REASS_MAXAGE || defined __DOXYGEN__
+#define IP_REASS_MAXAGE 15
#endif
/**
@@ -550,33 +744,14 @@
* PBUF_POOL_SIZE > IP_REASS_MAX_PBUFS so that the stack is still able to receive
* packets even if the maximum amount of fragments is enqueued for reassembly!
*/
-#ifndef IP_REASS_MAX_PBUFS
+#if !defined IP_REASS_MAX_PBUFS || defined __DOXYGEN__
#define IP_REASS_MAX_PBUFS 10
#endif
/**
- * IP_FRAG_USES_STATIC_BUF==1: Use a static MTU-sized buffer for IP
- * fragmentation. Otherwise pbufs are allocated and reference the original
- * packet data to be fragmented (or with LWIP_NETIF_TX_SINGLE_PBUF==1,
- * new PBUF_RAM pbufs are used for fragments).
- * ATTENTION: IP_FRAG_USES_STATIC_BUF==1 may not be used for DMA-enabled MACs!
- */
-#ifndef IP_FRAG_USES_STATIC_BUF
-#define IP_FRAG_USES_STATIC_BUF 0
-#endif
-
-/**
- * IP_FRAG_MAX_MTU: Assumed max MTU on any interface for IP frag buffer
- * (requires IP_FRAG_USES_STATIC_BUF==1)
- */
-#if IP_FRAG_USES_STATIC_BUF && !defined(IP_FRAG_MAX_MTU)
-#define IP_FRAG_MAX_MTU 1500
-#endif
-
-/**
* IP_DEFAULT_TTL: Default value for Time-To-Live used by transport layers.
*/
-#ifndef IP_DEFAULT_TTL
+#if !defined IP_DEFAULT_TTL || defined __DOXYGEN__
#define IP_DEFAULT_TTL 255
#endif
@@ -585,7 +760,7 @@
* filter per pcb on udp and raw send operations. To enable broadcast filter
* on recv operations, you also have to set IP_SOF_BROADCAST_RECV=1.
*/
-#ifndef IP_SOF_BROADCAST
+#if !defined IP_SOF_BROADCAST || defined __DOXYGEN__
#define IP_SOF_BROADCAST 0
#endif
@@ -593,7 +768,7 @@
* IP_SOF_BROADCAST_RECV (requires IP_SOF_BROADCAST=1) enable the broadcast
* filter on recv operations.
*/
-#ifndef IP_SOF_BROADCAST_RECV
+#if !defined IP_SOF_BROADCAST_RECV || defined __DOXYGEN__
#define IP_SOF_BROADCAST_RECV 0
#endif
@@ -604,18 +779,12 @@
* ATTENTION: When this is 1, make sure your netif driver correctly marks incoming
* link-layer-broadcast/multicast packets as such using the corresponding pbuf flags!
*/
-#ifndef IP_FORWARD_ALLOW_TX_ON_RX_NETIF
+#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.
+ * @}
*/
-#ifndef LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS
-#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 0
-#endif
/*
----------------------------------
@@ -623,33 +792,41 @@
----------------------------------
*/
/**
+ * @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
*/
-#ifndef LWIP_ICMP
+#if !defined LWIP_ICMP || defined __DOXYGEN__
#define LWIP_ICMP 1
#endif
/**
* ICMP_TTL: Default value for Time-To-Live used by ICMP packets.
*/
-#ifndef ICMP_TTL
+#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)
*/
-#ifndef LWIP_BROADCAST_PING
+#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)
*/
-#ifndef LWIP_MULTICAST_PING
+#if !defined LWIP_MULTICAST_PING || defined __DOXYGEN__
#define LWIP_MULTICAST_PING 0
#endif
+/**
+ * @}
+ */
/*
---------------------------------
@@ -657,18 +834,26 @@
---------------------------------
*/
/**
+ * @defgroup lwip_opts_raw RAW
+ * @ingroup lwip_opts_callback
+ * @{
+ */
+/**
* LWIP_RAW==1: Enable application layer to hook into the IP layer itself.
*/
-#ifndef LWIP_RAW
-#define LWIP_RAW 1
+#if !defined LWIP_RAW || defined __DOXYGEN__
+#define LWIP_RAW 0
#endif
/**
* LWIP_RAW==1: Enable application layer to hook into the IP layer itself.
*/
-#ifndef RAW_TTL
+#if !defined RAW_TTL || defined __DOXYGEN__
#define RAW_TTL (IP_DEFAULT_TTL)
#endif
+/**
+ * @}
+ */
/*
----------------------------------
@@ -676,122 +861,161 @@
----------------------------------
*/
/**
+ * @defgroup lwip_opts_dhcp DHCP
+ * @ingroup lwip_opts_ipv4
+ * @{
+ */
+/**
* LWIP_DHCP==1: Enable DHCP module.
*/
-#ifndef LWIP_DHCP
+#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.
*/
-#ifndef DHCP_DOES_ARP_CHECK
+#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.
*/
-#ifndef LWIP_AUTOIP
+#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.
*/
-#ifndef LWIP_DHCP_AUTOIP_COOP
+#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. 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.
+ * 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.
*/
-#ifndef LWIP_DHCP_AUTOIP_COOP_TRIES
+#if !defined LWIP_DHCP_AUTOIP_COOP_TRIES || defined __DOXYGEN__
#define LWIP_DHCP_AUTOIP_COOP_TRIES 9
#endif
+/**
+ * @}
+ */
/*
----------------------------------
- ---------- SNMP options ----------
+ ----- SNMP MIB2 support -----
----------------------------------
*/
/**
- * LWIP_SNMP==1: Turn on SNMP module. UDP must be available for SNMP
- * transport.
- */
-#ifndef LWIP_SNMP
-#define LWIP_SNMP 0
-#endif
-
-/**
- * SNMP_CONCURRENT_REQUESTS: Number of concurrent requests the module will
- * allow. At least one request buffer is required.
- * Does not have to be changed unless external MIBs answer request asynchronously
- */
-#ifndef SNMP_CONCURRENT_REQUESTS
-#define SNMP_CONCURRENT_REQUESTS 1
-#endif
-
-/**
- * SNMP_TRAP_DESTINATIONS: Number of trap destinations. At least one trap
- * destination is required
+ * @defgroup lwip_opts_mib2 SNMP MIB2 callbacks
+ * @ingroup lwip_opts_infrastructure
+ * @{
*/
-#ifndef SNMP_TRAP_DESTINATIONS
-#define SNMP_TRAP_DESTINATIONS 1
-#endif
-
/**
- * SNMP_PRIVATE_MIB:
- * When using a private MIB, you have to create a file 'private_mib.h' that contains
- * a 'struct mib_array_node mib_private' which contains your MIB.
+ * 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.
*/
-#ifndef SNMP_PRIVATE_MIB
-#define SNMP_PRIVATE_MIB 0
+#if !defined LWIP_MIB2_CALLBACKS || defined __DOXYGEN__
+#define LWIP_MIB2_CALLBACKS 0
#endif
-
/**
- * Only allow SNMP write actions that are 'safe' (e.g. disabeling netifs is not
- * a safe action and disabled when SNMP_SAFE_REQUESTS = 1).
- * Unsafe requests are disabled by default!
+ * @}
*/
-#ifndef SNMP_SAFE_REQUESTS
-#define SNMP_SAFE_REQUESTS 1
-#endif
+/*
+ ----------------------------------
+ -------- Multicast options -------
+ ----------------------------------
+*/
/**
- * The maximum length of strings used. This affects the size of
- * MEMP_SNMP_VALUE elements.
+ * @defgroup lwip_opts_multicast Multicast
+ * @ingroup lwip_opts_infrastructure
+ * @{
*/
-#ifndef SNMP_MAX_OCTET_STRING_LEN
-#define SNMP_MAX_OCTET_STRING_LEN 127
-#endif
-
/**
- * The maximum depth of the SNMP tree.
- * With private MIBs enabled, this depends on your MIB!
- * This affects the size of MEMP_SNMP_VALUE elements.
+ * LWIP_MULTICAST_TX_OPTIONS==1: Enable multicast TX support like the socket options
+ * IP_MULTICAST_TTL/IP_MULTICAST_IF/IP_MULTICAST_LOOP, as well as (currently only)
+ * core support for the corresponding IPv6 options.
*/
-#ifndef SNMP_MAX_TREE_DEPTH
-#define SNMP_MAX_TREE_DEPTH 15
+#if !defined LWIP_MULTICAST_TX_OPTIONS || defined __DOXYGEN__
+#define LWIP_MULTICAST_TX_OPTIONS ((LWIP_IGMP || LWIP_IPV6_MLD) && (LWIP_UDP || LWIP_RAW))
#endif
-
/**
- * The size of the MEMP_SNMP_VALUE elements, normally calculated from
- * SNMP_MAX_OCTET_STRING_LEN and SNMP_MAX_TREE_DEPTH.
+ * @}
*/
-#ifndef SNMP_MAX_VALUE_SIZE
-#define SNMP_MAX_VALUE_SIZE LWIP_MAX((SNMP_MAX_OCTET_STRING_LEN)+1, sizeof(s32_t)*(SNMP_MAX_TREE_DEPTH))
-#endif
/*
----------------------------------
@@ -799,11 +1023,23 @@
----------------------------------
*/
/**
- * LWIP_IGMP==1: Turn on IGMP module.
+ * @defgroup lwip_opts_igmp IGMP
+ * @ingroup lwip_opts_ipv4
+ * @{
*/
-#ifndef LWIP_IGMP
+/**
+ * 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
+/**
+ * @}
+ */
/*
----------------------------------
@@ -811,90 +1047,121 @@
----------------------------------
*/
/**
+ * @defgroup lwip_opts_dns DNS
+ * @ingroup lwip_opts_callback
+ * @{
+ */
+/**
* LWIP_DNS==1: Turn on DNS module. UDP must be available for DNS
* transport.
*/
-#ifndef LWIP_DNS
+#if !defined LWIP_DNS || defined __DOXYGEN__
#define LWIP_DNS 0
#endif
/** DNS maximum number of entries to maintain locally. */
-#ifndef DNS_TABLE_SIZE
+#if !defined DNS_TABLE_SIZE || defined __DOXYGEN__
#define DNS_TABLE_SIZE 4
#endif
/** DNS maximum host name length supported in the name table. */
-#ifndef DNS_MAX_NAME_LENGTH
+#if !defined DNS_MAX_NAME_LENGTH || defined __DOXYGEN__
#define DNS_MAX_NAME_LENGTH 256
#endif
-/** The maximum of DNS servers */
-#ifndef DNS_MAX_SERVERS
+/** 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. */
-#ifndef DNS_DOES_NAME_CHECK
+#if !defined DNS_DOES_NAME_CHECK || defined __DOXYGEN__
#define DNS_DOES_NAME_CHECK 1
#endif
-/** DNS message max. size. Default value is RFC compliant. */
-#ifndef DNS_MSG_SIZE
-#define DNS_MSG_SIZE 512
+/** 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
-/** DNS_LOCAL_HOSTLIST: Implements a local host-to-address list. If enabled,
- * you have to define
- * #define DNS_LOCAL_HOSTLIST_INIT {{"host1", 0x123}, {"host2", 0x234}}
- * (an array of structs name/address, where address is an u32_t in network
- * byte order).
+/* 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 u32_t my_lookup_function(const char *name)
- * that returns the IP address or INADDR_NONE if not found.
+ * \#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).
*/
-#ifndef DNS_LOCAL_HOSTLIST
+#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. */
-#ifndef DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+#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.
*/
-#ifndef LWIP_UDP
+#if !defined LWIP_UDP || defined __DOXYGEN__
#define LWIP_UDP 1
#endif
/**
* LWIP_UDPLITE==1: Turn on UDP-Lite. (Requires LWIP_UDP)
*/
-#ifndef LWIP_UDPLITE
+#if !defined LWIP_UDPLITE || defined __DOXYGEN__
#define LWIP_UDPLITE 0
#endif
/**
* UDP_TTL: Default Time-To-Live value.
*/
-#ifndef UDP_TTL
+#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.
*/
-#ifndef LWIP_NETBUF_RECVINFO
+#if !defined LWIP_NETBUF_RECVINFO || defined __DOXYGEN__
#define LWIP_NETBUF_RECVINFO 0
#endif
+/**
+ * @}
+ */
/*
---------------------------------
@@ -902,38 +1169,46 @@
---------------------------------
*/
/**
+ * @defgroup lwip_opts_tcp TCP
+ * @ingroup lwip_opts_callback
+ * @{
+ */
+/**
* LWIP_TCP==1: Turn on TCP.
*/
-#ifndef LWIP_TCP
+#if !defined LWIP_TCP || defined __DOXYGEN__
#define LWIP_TCP 1
#endif
/**
* TCP_TTL: Default Time-To-Live value.
*/
-#ifndef TCP_TTL
+#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
+ * 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
*/
-#ifndef TCP_WND
+#if !defined TCP_WND || defined __DOXYGEN__
#define TCP_WND (4 * TCP_MSS)
-#endif
+#endif
/**
* TCP_MAXRTX: Maximum number of retransmissions of data segments.
*/
-#ifndef TCP_MAXRTX
+#if !defined TCP_MAXRTX || defined __DOXYGEN__
#define TCP_MAXRTX 12
#endif
/**
* TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments.
*/
-#ifndef TCP_SYNMAXRTX
+#if !defined TCP_SYNMAXRTX || defined __DOXYGEN__
#define TCP_SYNMAXRTX 6
#endif
@@ -941,18 +1216,39 @@
* TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order.
* Define to 0 if your device is low on memory.
*/
-#ifndef TCP_QUEUE_OOSEQ
+#if !defined TCP_QUEUE_OOSEQ || defined __DOXYGEN__
#define TCP_QUEUE_OOSEQ (LWIP_TCP)
#endif
/**
+ * LWIP_TCP_SACK_OUT==1: TCP will support sending selective acknowledgements (SACKs).
+ */
+#if !defined LWIP_TCP_SACK_OUT || defined __DOXYGEN__
+#define LWIP_TCP_SACK_OUT 0
+#endif
+
+/**
+ * LWIP_TCP_MAX_SACK_NUM: The maximum number of SACK values to include in TCP segments.
+ * Must be at least 1, but is only used if LWIP_TCP_SACK_OUT is enabled.
+ * NOTE: Even though we never send more than 3 or 4 SACK ranges in a single segment
+ * (depending on other options), setting this option to values greater than 4 is not pointless.
+ * This is basically the max number of SACK ranges we want to keep track of.
+ * As new data is delivered, some of the SACK ranges may be removed or merged.
+ * In that case some of those older SACK ranges may be used again.
+ * The amount of memory used to store SACK ranges is LWIP_TCP_MAX_SACK_NUM * 8 bytes for each TCP PCB.
+ */
+#if !defined LWIP_TCP_MAX_SACK_NUM || defined __DOXYGEN__
+#define LWIP_TCP_MAX_SACK_NUM 4
+#endif
+
+/**
* TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default,
* you might want to increase this.)
* For the receive side, this MSS is advertised to the remote side
* when opening a connection. For the transmit size, this MSS sets
* an upper limit on the MSS advertised by the remote host.
*/
-#ifndef TCP_MSS
+#if !defined TCP_MSS || defined __DOXYGEN__
#define TCP_MSS 536
#endif
@@ -964,7 +1260,7 @@
* Setting this to 1 enables code that checks TCP_MSS against the MTU of the
* netif used for a connection and limits the MSS if it would be too big otherwise.
*/
-#ifndef TCP_CALCULATE_EFF_SEND_MSS
+#if !defined TCP_CALCULATE_EFF_SEND_MSS || defined __DOXYGEN__
#define TCP_CALCULATE_EFF_SEND_MSS 1
#endif
@@ -973,7 +1269,7 @@
* TCP_SND_BUF: TCP sender buffer space (bytes).
* To achieve good performance, this should be at least 2 * TCP_MSS.
*/
-#ifndef TCP_SND_BUF
+#if !defined TCP_SND_BUF || defined __DOXYGEN__
#define TCP_SND_BUF (2 * TCP_MSS)
#endif
@@ -981,7 +1277,7 @@
* TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least
* as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work.
*/
-#ifndef TCP_SND_QUEUELEN
+#if !defined TCP_SND_QUEUELEN || defined __DOXYGEN__
#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS))
#endif
@@ -990,7 +1286,7 @@
* TCP_SND_BUF. It is the amount of space which must be available in the
* TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT).
*/
-#ifndef TCP_SNDLOWAT
+#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
@@ -999,30 +1295,30 @@
* than TCP_SND_QUEUELEN. If the number of pbufs queued on a pcb drops below
* this number, select returns writable (combined with TCP_SNDLOWAT).
*/
-#ifndef TCP_SNDQUEUELOWAT
+#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==0.
+ * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==1.
*/
-#ifndef TCP_OOSEQ_MAX_BYTES
+#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==0.
+ * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==1.
*/
-#ifndef TCP_OOSEQ_MAX_PBUFS
+#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.
*/
-#ifndef TCP_LISTEN_BACKLOG
+#if !defined TCP_LISTEN_BACKLOG || defined __DOXYGEN__
#define TCP_LISTEN_BACKLOG 0
#endif
@@ -1031,7 +1327,7 @@
* This backlog is used unless another is explicitly specified.
* 0xff is the maximum (u8_t).
*/
-#ifndef TCP_DEFAULT_LISTEN_BACKLOG
+#if !defined TCP_DEFAULT_LISTEN_BACKLOG || defined __DOXYGEN__
#define TCP_DEFAULT_LISTEN_BACKLOG 0xff
#endif
@@ -1049,14 +1345,17 @@
* TCP_MSS: Try to create unfragmented TCP packets.
* TCP_MSS/4: Try to create 4 fragments or less per TCP packet.
*/
-#ifndef TCP_OVERSIZE
+#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.
*/
-#ifndef LWIP_TCP_TIMESTAMPS
+#if !defined LWIP_TCP_TIMESTAMPS || defined __DOXYGEN__
#define LWIP_TCP_TIMESTAMPS 0
#endif
@@ -1064,8 +1363,8 @@
* TCP_WND_UPDATE_THRESHOLD: difference in window to trigger an
* explicit window update
*/
-#ifndef TCP_WND_UPDATE_THRESHOLD
-#define TCP_WND_UPDATE_THRESHOLD (TCP_WND / 4)
+#if !defined TCP_WND_UPDATE_THRESHOLD || defined __DOXYGEN__
+#define TCP_WND_UPDATE_THRESHOLD LWIP_MIN((TCP_WND / 4), (TCP_MSS * 4))
#endif
/**
@@ -1075,11 +1374,55 @@
* 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)
+#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
+
+/** LWIP_ALTCP==1: enable the altcp API
+ * altcp is an abstraction layer that prevents applications linking against the
+ * tcp.h functions but provides the same functionality. It is used to e.g. add
+ * SSL/TLS or proxy-connect support to an application written for the tcp callback
+ * API without that application knowing the protocol details.
+ * Applications written against the altcp API are directly linked against the
+ * tcp callback API for LWIP_ALTCP==0, but then cannot use layered protocols.
+ */
+#ifndef LWIP_ALTCP
+#define LWIP_ALTCP 0
+#endif
+
+/** LWIP_ALTCP_TLS==1: enable TLS support for altcp API.
+ * This needs a port of the functions in altcp_tls.h to a TLS library.
+ * A port to ARM mbedtls is provided with lwIP, see apps/altcp_tls/ directory
+ * and LWIP_ALTCP_TLS_MBEDTLS option.
+ */
+#ifndef LWIP_ALTCP_TLS
+#define LWIP_ALTCP_TLS 0
#endif
+/**
+ * @}
+ */
/*
----------------------------------
@@ -1087,22 +1430,50 @@
----------------------------------
*/
/**
+ * @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.
*/
-#ifndef PBUF_LINK_HLEN
+#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 0
#endif
/**
* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. The default is
- * designed to accomodate single full size TCP frame in one pbuf, including
+ * designed to accommodate single full size TCP frame in one pbuf, including
* TCP_MSS, IP header, and link header.
*/
-#ifndef PBUF_POOL_BUFSIZE
-#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_HLEN)
+#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
+
+/**
+ * LWIP_PBUF_REF_T: Refcount type in pbuf.
+ * Default width of u8_t can be increased if 255 refs are not enough for you.
+ */
+#ifndef LWIP_PBUF_REF_T
+#define LWIP_PBUF_REF_T u8_t
#endif
+/**
+ * @}
+ */
/*
------------------------------------------------
@@ -1110,33 +1481,55 @@
------------------------------------------------
*/
/**
+ * @defgroup lwip_opts_netif NETIF
+ * @ingroup lwip_opts
+ * @{
+ */
+/**
+ * LWIP_SINGLE_NETIF==1: use a single netif only. This is the common case for
+ * small real-life targets. Some code like routing etc. can be left out.
+ */
+#if !defined LWIP_SINGLE_NETIF || defined __DOXYGEN__
+#define LWIP_SINGLE_NETIF 0
+#endif
+
+/**
* LWIP_NETIF_HOSTNAME==1: use DHCP_OPTION_HOSTNAME with netif's hostname
* field.
*/
-#ifndef LWIP_NETIF_HOSTNAME
+#if !defined LWIP_NETIF_HOSTNAME || defined __DOXYGEN__
#define LWIP_NETIF_HOSTNAME 0
#endif
/**
* LWIP_NETIF_API==1: Support netif api (in netifapi.c)
*/
-#ifndef LWIP_NETIF_API
+#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 acquistion)
+ * changes its up/down status (i.e., due to DHCP IP acquisition)
*/
-#ifndef LWIP_NETIF_STATUS_CALLBACK
+#if !defined LWIP_NETIF_STATUS_CALLBACK || defined __DOXYGEN__
#define LWIP_NETIF_STATUS_CALLBACK 0
#endif
/**
+ * LWIP_NETIF_EXT_STATUS_CALLBACK==1: Support an extended callback function
+ * for several netif related event that supports multiple subscribers.
+ * @see netif_ext_status_callback
+ */
+#if !defined LWIP_NETIF_EXT_STATUS_CALLBACK || defined __DOXYGEN__
+#define LWIP_NETIF_EXT_STATUS_CALLBACK 0
+#endif
+
+/**
* LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface
* whenever the link changes (i.e., link down)
*/
-#ifndef LWIP_NETIF_LINK_CALLBACK
+#if !defined LWIP_NETIF_LINK_CALLBACK || defined __DOXYGEN__
#define LWIP_NETIF_LINK_CALLBACK 0
#endif
@@ -1144,7 +1537,7 @@
* LWIP_NETIF_REMOVE_CALLBACK==1: Support a callback function that is called
* when a netif has been removed
*/
-#ifndef LWIP_NETIF_REMOVE_CALLBACK
+#if !defined LWIP_NETIF_REMOVE_CALLBACK || defined __DOXYGEN__
#define LWIP_NETIF_REMOVE_CALLBACK 0
#endif
@@ -1155,15 +1548,74 @@
* ARP tables or many concurrent connections, it might be counterproductive
* if you have a tiny ARP table or if there never are concurrent connections.
*/
-#ifndef LWIP_NETIF_HWADDRHINT
+#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!
+ *
+ * ATTENTION: a driver should *NOT* rely on getting single pbufs but check TX
+ * pbufs for being in one piece. If not, @ref pbuf_clone can be used to get
+ * a single pbuf:
+ * if (p->next != NULL) {
+ * struct pbuf *q = pbuf_clone(PBUF_RAW, PBUF_RAM, p);
+ * if (q == NULL) {
+ * return ERR_MEM;
+ * }
+ * p = q; ATTENTION: do NOT free the old 'p' as the ref belongs to the caller!
+ * }
+ */
+#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 (max. 256).
+ */
+#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 && !LWIP_SINGLE_NETIF)
+#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.
*/
-#ifndef LWIP_NETIF_LOOPBACK
+#if !defined LWIP_NETIF_LOOPBACK || defined __DOXYGEN__
#define LWIP_NETIF_LOOPBACK 0
#endif
@@ -1171,7 +1623,7 @@
* LWIP_LOOPBACK_MAX_PBUFS: Maximum number of pbufs on queue for loopback
* sending for each netif (0 = disabled)
*/
-#ifndef LWIP_LOOPBACK_MAX_PBUFS
+#if !defined LWIP_LOOPBACK_MAX_PBUFS || defined __DOXYGEN__
#define LWIP_LOOPBACK_MAX_PBUFS 0
#endif
@@ -1188,56 +1640,27 @@
* The packets are put on a list and netif_poll() must be called in
* the main application loop.
*/
-#ifndef LWIP_NETIF_LOOPBACK_MULTITHREADING
+#if !defined LWIP_NETIF_LOOPBACK_MULTITHREADING || defined __DOXYGEN__
#define LWIP_NETIF_LOOPBACK_MULTITHREADING (!NO_SYS)
#endif
-
/**
- * LWIP_NETIF_TX_SINGLE_PBUF: if this is set to 1, lwIP tries to put all data
- * to be sent into one single pbuf. This is for compatibility with DMA-enabled
- * MACs that do not support scatter-gather.
- * Beware that this might involve CPU-memcpy before transmitting that would not
- * be needed without this flag! Use this only if you need to!
- *
- * @todo: TCP and IP-frag do not work with this, yet:
+ * @}
*/
-#ifndef LWIP_NETIF_TX_SINGLE_PBUF
-#define LWIP_NETIF_TX_SINGLE_PBUF 0
-#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
-
-/*
- ------------------------------------
- ---------- LOOPIF options ----------
- ------------------------------------
-*/
-/**
- * LWIP_HAVE_LOOPIF==1: Support loop interface (127.0.0.1) and loopif.c
- */
-#ifndef LWIP_HAVE_LOOPIF
-#define LWIP_HAVE_LOOPIF 0
-#endif
/*
------------------------------------
- ---------- SLIPIF options ----------
+ ---------- Thread options ----------
------------------------------------
*/
/**
- * LWIP_HAVE_SLIPIF==1: Support slip interface and slipif.c
+ * @defgroup lwip_opts_thread Threading
+ * @ingroup lwip_opts_infrastructure
+ * @{
*/
-#ifndef LWIP_HAVE_SLIPIF
-#define LWIP_HAVE_SLIPIF 0
-#endif
-
-/*
- ------------------------------------
- ---------- Thread options ----------
- ------------------------------------
-*/
/**
* TCPIP_THREAD_NAME: The name assigned to the main tcpip thread.
*/
-#ifndef TCPIP_THREAD_NAME
+#if !defined TCPIP_THREAD_NAME || defined __DOXYGEN__
#define TCPIP_THREAD_NAME "tcpip_thread"
#endif
@@ -1246,7 +1669,7 @@
* The stack size value itself is platform-dependent, but is passed to
* sys_thread_new() when the thread is created.
*/
-#ifndef TCPIP_THREAD_STACKSIZE
+#if !defined TCPIP_THREAD_STACKSIZE || defined __DOXYGEN__
#define TCPIP_THREAD_STACKSIZE 0
#endif
@@ -1255,7 +1678,7 @@
* The priority value itself is platform-dependent, but is passed to
* sys_thread_new() when the thread is created.
*/
-#ifndef TCPIP_THREAD_PRIO
+#if !defined TCPIP_THREAD_PRIO || defined __DOXYGEN__
#define TCPIP_THREAD_PRIO 1
#endif
@@ -1264,14 +1687,22 @@
* The queue size value itself is platform-dependent, but is passed to
* sys_mbox_new() when tcpip_init is called.
*/
-#ifndef TCPIP_MBOX_SIZE
+#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.
*/
-#ifndef SLIPIF_THREAD_NAME
+#if !defined SLIPIF_THREAD_NAME || defined __DOXYGEN__
#define SLIPIF_THREAD_NAME "slipif_loop"
#endif
@@ -1280,7 +1711,7 @@
* The stack size value itself is platform-dependent, but is passed to
* sys_thread_new() when the thread is created.
*/
-#ifndef SLIPIF_THREAD_STACKSIZE
+#if !defined SLIPIF_THREAD_STACKSIZE || defined __DOXYGEN__
#define SLIPIF_THREAD_STACKSIZE 0
#endif
@@ -1289,39 +1720,14 @@
* The priority value itself is platform-dependent, but is passed to
* sys_thread_new() when the thread is created.
*/
-#ifndef SLIPIF_THREAD_PRIO
+#if !defined SLIPIF_THREAD_PRIO || defined __DOXYGEN__
#define SLIPIF_THREAD_PRIO 1
#endif
/**
- * PPP_THREAD_NAME: The name assigned to the pppInputThread.
- */
-#ifndef PPP_THREAD_NAME
-#define PPP_THREAD_NAME "pppInputThread"
-#endif
-
-/**
- * PPP_THREAD_STACKSIZE: The stack size used by the pppInputThread.
- * The stack size value itself is platform-dependent, but is passed to
- * sys_thread_new() when the thread is created.
- */
-#ifndef PPP_THREAD_STACKSIZE
-#define PPP_THREAD_STACKSIZE 0
-#endif
-
-/**
- * PPP_THREAD_PRIO: The priority assigned to the pppInputThread.
- * The priority value itself is platform-dependent, but is passed to
- * sys_thread_new() when the thread is created.
- */
-#ifndef PPP_THREAD_PRIO
-#define PPP_THREAD_PRIO 1
-#endif
-
-/**
* DEFAULT_THREAD_NAME: The name assigned to any other lwIP thread.
*/
-#ifndef DEFAULT_THREAD_NAME
+#if !defined DEFAULT_THREAD_NAME || defined __DOXYGEN__
#define DEFAULT_THREAD_NAME "lwIP"
#endif
@@ -1330,7 +1736,7 @@
* The stack size value itself is platform-dependent, but is passed to
* sys_thread_new() when the thread is created.
*/
-#ifndef DEFAULT_THREAD_STACKSIZE
+#if !defined DEFAULT_THREAD_STACKSIZE || defined __DOXYGEN__
#define DEFAULT_THREAD_STACKSIZE 0
#endif
@@ -1339,7 +1745,7 @@
* The priority value itself is platform-dependent, but is passed to
* sys_thread_new() when the thread is created.
*/
-#ifndef DEFAULT_THREAD_PRIO
+#if !defined DEFAULT_THREAD_PRIO || defined __DOXYGEN__
#define DEFAULT_THREAD_PRIO 1
#endif
@@ -1348,7 +1754,7 @@
* NETCONN_RAW. The queue size value itself is platform-dependent, but is passed
* to sys_mbox_new() when the recvmbox is created.
*/
-#ifndef DEFAULT_RAW_RECVMBOX_SIZE
+#if !defined DEFAULT_RAW_RECVMBOX_SIZE || defined __DOXYGEN__
#define DEFAULT_RAW_RECVMBOX_SIZE 0
#endif
@@ -1357,7 +1763,7 @@
* NETCONN_UDP. The queue size value itself is platform-dependent, but is passed
* to sys_mbox_new() when the recvmbox is created.
*/
-#ifndef DEFAULT_UDP_RECVMBOX_SIZE
+#if !defined DEFAULT_UDP_RECVMBOX_SIZE || defined __DOXYGEN__
#define DEFAULT_UDP_RECVMBOX_SIZE 0
#endif
@@ -1366,7 +1772,7 @@
* NETCONN_TCP. The queue size value itself is platform-dependent, but is passed
* to sys_mbox_new() when the recvmbox is created.
*/
-#ifndef DEFAULT_TCP_RECVMBOX_SIZE
+#if !defined DEFAULT_TCP_RECVMBOX_SIZE || defined __DOXYGEN__
#define DEFAULT_TCP_RECVMBOX_SIZE 0
#endif
@@ -1375,9 +1781,12 @@
* The queue size value itself is platform-dependent, but is passed to
* sys_mbox_new() when the acceptmbox is created.
*/
-#ifndef DEFAULT_ACCEPTMBOX_SIZE
+#if !defined DEFAULT_ACCEPTMBOX_SIZE || defined __DOXYGEN__
#define DEFAULT_ACCEPTMBOX_SIZE 0
#endif
+/**
+ * @}
+ */
/*
----------------------------------------------
@@ -1385,34 +1794,52 @@
----------------------------------------------
*/
/**
- * LWIP_TCPIP_CORE_LOCKING: (EXPERIMENTAL!)
- * Don't use it if you're not an active lwIP project member
+ * @defgroup lwip_opts_netconn Netconn
+ * @ingroup lwip_opts_threadsafe_apis
+ * @{
*/
-#ifndef LWIP_TCPIP_CORE_LOCKING
-#define LWIP_TCPIP_CORE_LOCKING 0
+/**
+ * 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_CORE_LOCKING_INPUT: (EXPERIMENTAL!)
- * Don't use it if you're not an active lwIP project member
+/** LWIP_TCPIP_TIMEOUT==1: Enable tcpip_timeout/tcpip_untimeout to create
+ * timers running in tcpip_thread from another thread.
*/
-#ifndef LWIP_TCPIP_CORE_LOCKING_INPUT
-#define LWIP_TCPIP_CORE_LOCKING_INPUT 0
+#if !defined LWIP_TCPIP_TIMEOUT || defined __DOXYGEN__
+#define LWIP_TCPIP_TIMEOUT 0
#endif
-/**
- * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)
+/** 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().
*/
-#ifndef LWIP_NETCONN
-#define LWIP_NETCONN 1
+#if !defined LWIP_NETCONN_SEM_PER_THREAD || defined __DOXYGEN__
+#define LWIP_NETCONN_SEM_PER_THREAD 0
#endif
-/** LWIP_TCPIP_TIMEOUT==1: Enable tcpip_timeout/tcpip_untimeout tod create
- * timers running in tcpip_thread from another thread.
+/** 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
*/
-#ifndef LWIP_TCPIP_TIMEOUT
-#define LWIP_TCPIP_TIMEOUT 1
+#if !defined LWIP_NETCONN_FULLDUPLEX || defined __DOXYGEN__
+#define LWIP_NETCONN_FULLDUPLEX 0
#endif
+/**
+ * @}
+ */
/*
------------------------------------
@@ -1420,17 +1847,24 @@
------------------------------------
*/
/**
+ * @defgroup lwip_opts_socket Sockets
+ * @ingroup lwip_opts_threadsafe_apis
+ * @{
+ */
+/**
* LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
*/
-#ifndef LWIP_SOCKET
+#if !defined LWIP_SOCKET || defined __DOXYGEN__
#define LWIP_SOCKET 1
#endif
/**
- * LWIP_COMPAT_SOCKETS==1: Enable BSD-style sockets functions names.
+ * 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)
*/
-#ifndef LWIP_COMPAT_SOCKETS
+#if !defined LWIP_COMPAT_SOCKETS || defined __DOXYGEN__
#define LWIP_COMPAT_SOCKETS 1
#endif
@@ -1439,16 +1873,27 @@
* Disable this option if you use a POSIX operating system that uses the same
* names (read, write & close). (only used if you use sockets.c)
*/
-#ifndef LWIP_POSIX_SOCKETS_IO_NAMES
+#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)
*/
-#ifndef LWIP_TCP_KEEPALIVE
+#if !defined LWIP_TCP_KEEPALIVE || defined __DOXYGEN__
#define LWIP_TCP_KEEPALIVE 0
#endif
@@ -1456,7 +1901,7 @@
* LWIP_SO_SNDTIMEO==1: Enable send timeout for sockets/netconns and
* SO_SNDTIMEO processing.
*/
-#ifndef LWIP_SO_SNDTIMEO
+#if !defined LWIP_SO_SNDTIMEO || defined __DOXYGEN__
#define LWIP_SO_SNDTIMEO 0
#endif
@@ -1464,28 +1909,50 @@
* LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and
* SO_RCVTIMEO processing.
*/
-#ifndef LWIP_SO_RCVTIMEO
+#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.
*/
-#ifndef LWIP_SO_RCVBUF
+#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.
*/
-#ifndef RECV_BUFSIZE_DEFAULT
+#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.
*/
-#ifndef SO_REUSE
+#if !defined SO_REUSE || defined __DOXYGEN__
#define SO_REUSE 0
#endif
@@ -1494,7 +1961,7 @@
* to all local matches if SO_REUSEADDR is turned on.
* WARNING: Adds a memcpy for every packet if passing to more than one pcb!
*/
-#ifndef SO_REUSE_RXTOALL
+#if !defined SO_REUSE_RXTOALL || defined __DOXYGEN__
#define SO_REUSE_RXTOALL 0
#endif
@@ -1506,19 +1973,37 @@
* pending datagram in bytes. This is the way linux does it. This code is only
* here for compatibility.
*/
-#ifndef LWIP_FIONREAD_LINUXMODE
+#if !defined LWIP_FIONREAD_LINUXMODE || defined __DOXYGEN__
#define LWIP_FIONREAD_LINUXMODE 0
#endif
+/**
+ * LWIP_SOCKET_SELECT==1 (default): enable select() for sockets (uses a netconn
+ * callback to keep track of events).
+ * This saves RAM (counters per socket) and code (netconn event callback), which
+ * should improve performance a bit).
+ */
+#if !defined LWIP_SOCKET_SELECT || defined __DOXYGEN__
+#define LWIP_SOCKET_SELECT 1
+#endif
+/**
+ * @}
+ */
+
/*
----------------------------------------
---------- Statistics options ----------
----------------------------------------
*/
/**
+ * @defgroup lwip_opts_stats Statistics
+ * @ingroup lwip_opts_debug
+ * @{
+ */
+/**
* LWIP_STATS==1: Enable statistics collection in lwip_stats.
*/
-#ifndef LWIP_STATS
+#if !defined LWIP_STATS || defined __DOXYGEN__
#define LWIP_STATS 1
#endif
@@ -1527,28 +2012,28 @@
/**
* LWIP_STATS_DISPLAY==1: Compile in the statistics output functions.
*/
-#ifndef LWIP_STATS_DISPLAY
+#if !defined LWIP_STATS_DISPLAY || defined __DOXYGEN__
#define LWIP_STATS_DISPLAY 0
#endif
/**
* LINK_STATS==1: Enable link stats.
*/
-#ifndef LINK_STATS
+#if !defined LINK_STATS || defined __DOXYGEN__
#define LINK_STATS 1
#endif
/**
* ETHARP_STATS==1: Enable etharp stats.
*/
-#ifndef ETHARP_STATS
+#if !defined ETHARP_STATS || defined __DOXYGEN__
#define ETHARP_STATS (LWIP_ARP)
#endif
/**
* IP_STATS==1: Enable IP stats.
*/
-#ifndef IP_STATS
+#if !defined IP_STATS || defined __DOXYGEN__
#define IP_STATS 1
#endif
@@ -1556,21 +2041,21 @@
* IPFRAG_STATS==1: Enable IP fragmentation stats. Default is
* on if using either frag or reass.
*/
-#ifndef IPFRAG_STATS
+#if !defined IPFRAG_STATS || defined __DOXYGEN__
#define IPFRAG_STATS (IP_REASSEMBLY || IP_FRAG)
#endif
/**
* ICMP_STATS==1: Enable ICMP stats.
*/
-#ifndef ICMP_STATS
+#if !defined ICMP_STATS || defined __DOXYGEN__
#define ICMP_STATS 1
#endif
/**
* IGMP_STATS==1: Enable IGMP stats.
*/
-#ifndef IGMP_STATS
+#if !defined IGMP_STATS || defined __DOXYGEN__
#define IGMP_STATS (LWIP_IGMP)
#endif
@@ -1578,7 +2063,7 @@
* UDP_STATS==1: Enable UDP stats. Default is on if
* UDP enabled, otherwise off.
*/
-#ifndef UDP_STATS
+#if !defined UDP_STATS || defined __DOXYGEN__
#define UDP_STATS (LWIP_UDP)
#endif
@@ -1586,69 +2071,77 @@
* TCP_STATS==1: Enable TCP stats. Default is on if TCP
* enabled, otherwise off.
*/
-#ifndef TCP_STATS
+#if !defined TCP_STATS || defined __DOXYGEN__
#define TCP_STATS (LWIP_TCP)
#endif
/**
* MEM_STATS==1: Enable mem.c stats.
*/
-#ifndef MEM_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.
*/
-#ifndef MEMP_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).
*/
-#ifndef SYS_STATS
+#if !defined SYS_STATS || defined __DOXYGEN__
#define SYS_STATS (NO_SYS == 0)
#endif
/**
* IP6_STATS==1: Enable IPv6 stats.
*/
-#ifndef IP6_STATS
+#if !defined IP6_STATS || defined __DOXYGEN__
#define IP6_STATS (LWIP_IPV6)
#endif
/**
* ICMP6_STATS==1: Enable ICMP for IPv6 stats.
*/
-#ifndef ICMP6_STATS
+#if !defined ICMP6_STATS || defined __DOXYGEN__
#define ICMP6_STATS (LWIP_IPV6 && LWIP_ICMP6)
#endif
/**
* IP6_FRAG_STATS==1: Enable IPv6 fragmentation stats.
*/
-#ifndef IP6_FRAG_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.
*/
-#ifndef MLD6_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.
*/
-#ifndef ND6_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
@@ -1664,265 +2157,231 @@
#define IP6_FRAG_STATS 0
#define MLD6_STATS 0
#define ND6_STATS 0
+#define MIB2_STATS 0
#endif /* LWIP_STATS */
+/**
+ * @}
+ */
/*
- ---------------------------------
- ---------- PPP options ----------
- ---------------------------------
+ --------------------------------------
+ ---------- Checksum options ----------
+ --------------------------------------
*/
/**
- * PPP_SUPPORT==1: Enable PPP.
+ * @defgroup lwip_opts_checksum Checksum
+ * @ingroup lwip_opts_infrastructure
+ * @{
*/
-#ifndef PPP_SUPPORT
-#define PPP_SUPPORT 0
-#endif
-
/**
- * PPPOE_SUPPORT==1: Enable PPP Over Ethernet
+ * 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!
*/
-#ifndef PPPOE_SUPPORT
-#define PPPOE_SUPPORT 0
+#if !defined LWIP_CHECKSUM_CTRL_PER_NETIF || defined __DOXYGEN__
+#define LWIP_CHECKSUM_CTRL_PER_NETIF 0
#endif
/**
- * PPPOS_SUPPORT==1: Enable PPP Over Serial
+ * CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.
*/
-#ifndef PPPOS_SUPPORT
-#define PPPOS_SUPPORT PPP_SUPPORT
+#if !defined CHECKSUM_GEN_IP || defined __DOXYGEN__
+#define CHECKSUM_GEN_IP 1
#endif
-#if PPP_SUPPORT
-
/**
- * NUM_PPP: Max PPP sessions.
+ * CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.
*/
-#ifndef NUM_PPP
-#define NUM_PPP 1
+#if !defined CHECKSUM_GEN_UDP || defined __DOXYGEN__
+#define CHECKSUM_GEN_UDP 1
#endif
/**
- * PAP_SUPPORT==1: Support PAP.
+ * CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.
*/
-#ifndef PAP_SUPPORT
-#define PAP_SUPPORT 0
+#if !defined CHECKSUM_GEN_TCP || defined __DOXYGEN__
+#define CHECKSUM_GEN_TCP 1
#endif
/**
- * CHAP_SUPPORT==1: Support CHAP.
+ * CHECKSUM_GEN_ICMP==1: Generate checksums in software for outgoing ICMP packets.
*/
-#ifndef CHAP_SUPPORT
-#define CHAP_SUPPORT 0
+#if !defined CHECKSUM_GEN_ICMP || defined __DOXYGEN__
+#define CHECKSUM_GEN_ICMP 1
#endif
/**
- * MSCHAP_SUPPORT==1: Support MSCHAP. CURRENTLY NOT SUPPORTED! DO NOT SET!
+ * CHECKSUM_GEN_ICMP6==1: Generate checksums in software for outgoing ICMP6 packets.
*/
-#ifndef MSCHAP_SUPPORT
-#define MSCHAP_SUPPORT 0
+#if !defined CHECKSUM_GEN_ICMP6 || defined __DOXYGEN__
+#define CHECKSUM_GEN_ICMP6 1
#endif
/**
- * CBCP_SUPPORT==1: Support CBCP. CURRENTLY NOT SUPPORTED! DO NOT SET!
+ * CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.
*/
-#ifndef CBCP_SUPPORT
-#define CBCP_SUPPORT 0
+#if !defined CHECKSUM_CHECK_IP || defined __DOXYGEN__
+#define CHECKSUM_CHECK_IP 1
#endif
/**
- * CCP_SUPPORT==1: Support CCP. CURRENTLY NOT SUPPORTED! DO NOT SET!
+ * CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.
*/
-#ifndef CCP_SUPPORT
-#define CCP_SUPPORT 0
+#if !defined CHECKSUM_CHECK_UDP || defined __DOXYGEN__
+#define CHECKSUM_CHECK_UDP 1
#endif
/**
- * VJ_SUPPORT==1: Support VJ header compression.
+ * CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.
*/
-#ifndef VJ_SUPPORT
-#define VJ_SUPPORT 0
+#if !defined CHECKSUM_CHECK_TCP || defined __DOXYGEN__
+#define CHECKSUM_CHECK_TCP 1
#endif
/**
- * MD5_SUPPORT==1: Support MD5 (see also CHAP).
+ * CHECKSUM_CHECK_ICMP==1: Check checksums in software for incoming ICMP packets.
*/
-#ifndef MD5_SUPPORT
-#define MD5_SUPPORT 0
+#if !defined CHECKSUM_CHECK_ICMP || defined __DOXYGEN__
+#define CHECKSUM_CHECK_ICMP 1
#endif
-/*
- * Timeouts
+/**
+ * CHECKSUM_CHECK_ICMP6==1: Check checksums in software for incoming ICMPv6 packets
*/
-#ifndef FSM_DEFTIMEOUT
-#define FSM_DEFTIMEOUT 6 /* Timeout time in seconds */
-#endif
-
-#ifndef FSM_DEFMAXTERMREQS
-#define FSM_DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */
-#endif
-
-#ifndef FSM_DEFMAXCONFREQS
-#define FSM_DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */
-#endif
-
-#ifndef FSM_DEFMAXNAKLOOPS
-#define FSM_DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */
-#endif
-
-#ifndef UPAP_DEFTIMEOUT
-#define UPAP_DEFTIMEOUT 6 /* Timeout (seconds) for retransmitting req */
-#endif
-
-#ifndef UPAP_DEFREQTIME
-#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */
+#if !defined CHECKSUM_CHECK_ICMP6 || defined __DOXYGEN__
+#define CHECKSUM_CHECK_ICMP6 1
#endif
-#ifndef CHAP_DEFTIMEOUT
-#define CHAP_DEFTIMEOUT 6 /* Timeout time in seconds */
-#endif
-
-#ifndef CHAP_DEFTRANSMITS
-#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */
-#endif
-
-/* Interval in seconds between keepalive echo requests, 0 to disable. */
-#ifndef LCP_ECHOINTERVAL
-#define LCP_ECHOINTERVAL 0
-#endif
-
-/* Number of unanswered echo requests before failure. */
-#ifndef LCP_MAXECHOFAILS
-#define LCP_MAXECHOFAILS 3
-#endif
-
-/* Max Xmit idle time (in jiffies) before resend flag char. */
-#ifndef PPP_MAXIDLEFLAG
-#define PPP_MAXIDLEFLAG 100
+/**
+ * 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
+/**
+ * @}
+ */
/*
- * Packet sizes
- *
- * Note - lcp shouldn't be allowed to negotiate stuff outside these
- * limits. See lcp.h in the pppd directory.
- * (XXX - these constants should simply be shared by lcp.c instead
- * of living in lcp.h)
+ ---------------------------------------
+ ---------- IPv6 options ---------------
+ ---------------------------------------
+*/
+/**
+ * @defgroup lwip_opts_ipv6 IPv6
+ * @ingroup lwip_opts
+ * @{
*/
-#define PPP_MTU 1500 /* Default MTU (size of Info field) */
-#ifndef PPP_MAXMTU
-/* #define PPP_MAXMTU 65535 - (PPP_HDRLEN + PPP_FCSLEN) */
-#define PPP_MAXMTU 1500 /* Largest MTU we allow */
-#endif
-#define PPP_MINMTU 64
-#define PPP_MRU 1500 /* default MRU = max length of info field */
-#define PPP_MAXMRU 1500 /* Largest MRU we allow */
-#ifndef PPP_DEFMRU
-#define PPP_DEFMRU 296 /* Try for this */
-#endif
-#define PPP_MINMRU 128 /* No MRUs below this */
-
-#ifndef MAXNAMELEN
-#define MAXNAMELEN 256 /* max length of hostname or name for auth */
-#endif
-#ifndef MAXSECRETLEN
-#define MAXSECRETLEN 256 /* max length of password or secret */
+/**
+ * LWIP_IPV6==1: Enable IPv6
+ */
+#if !defined LWIP_IPV6 || defined __DOXYGEN__
+#define LWIP_IPV6 0
#endif
-#endif /* PPP_SUPPORT */
-
-/*
- --------------------------------------
- ---------- Checksum options ----------
- --------------------------------------
-*/
/**
- * CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.
+ * IPV6_REASS_MAXAGE: Maximum time (in multiples of IP6_REASS_TMR_INTERVAL - so seconds, normally)
+ * a fragmented IP packet waits for all fragments to arrive. If not all fragments arrived
+ * in this time, the whole packet is discarded.
*/
-#ifndef CHECKSUM_GEN_IP
-#define CHECKSUM_GEN_IP 1
+#if !defined IPV6_REASS_MAXAGE || defined __DOXYGEN__
+#define IPV6_REASS_MAXAGE 60
#endif
-
+
/**
- * CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.
+ * LWIP_IPV6_SCOPES==1: Enable support for IPv6 address scopes, ensuring that
+ * e.g. link-local addresses are really treated as link-local. Disable this
+ * setting only for single-interface configurations.
*/
-#ifndef CHECKSUM_GEN_UDP
-#define CHECKSUM_GEN_UDP 1
+#if !defined LWIP_IPV6_SCOPES || defined __DOXYGEN__
+#define LWIP_IPV6_SCOPES (LWIP_IPV6 && !LWIP_SINGLE_NETIF)
#endif
-
+
/**
- * CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.
+ * LWIP_IPV6_SCOPES_DEBUG==1: Perform run-time checks to verify that addresses
+ * are properly zoned (see ip6_zone.h on what that means) where it matters.
+ * Enabling this setting is highly recommended when upgrading from an existing
+ * installation that is not yet scope-aware; otherwise it may be too expensive.
*/
-#ifndef CHECKSUM_GEN_TCP
-#define CHECKSUM_GEN_TCP 1
+#if !defined LWIP_IPV6_SCOPES_DEBUG || defined __DOXYGEN__
+#define LWIP_IPV6_SCOPES_DEBUG 0
#endif
/**
- * CHECKSUM_GEN_ICMP==1: Generate checksums in software for outgoing ICMP packets.
+ * LWIP_IPV6_NUM_ADDRESSES: Number of IPv6 addresses per netif.
*/
-#ifndef CHECKSUM_GEN_ICMP
-#define CHECKSUM_GEN_ICMP 1
+#if !defined LWIP_IPV6_NUM_ADDRESSES || defined __DOXYGEN__
+#define LWIP_IPV6_NUM_ADDRESSES 3
#endif
-
+
/**
- * CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.
+ * LWIP_IPV6_FORWARD==1: Forward IPv6 packets across netifs
*/
-#ifndef CHECKSUM_CHECK_IP
-#define CHECKSUM_CHECK_IP 1
+#if !defined LWIP_IPV6_FORWARD || defined __DOXYGEN__
+#define LWIP_IPV6_FORWARD 0
#endif
-
+
/**
- * CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.
+ * LWIP_IPV6_FRAG==1: Fragment outgoing IPv6 packets that are too big.
*/
-#ifndef CHECKSUM_CHECK_UDP
-#define CHECKSUM_CHECK_UDP 1
+#if !defined LWIP_IPV6_FRAG || defined __DOXYGEN__
+#define LWIP_IPV6_FRAG 0
#endif
/**
- * CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.
+ * LWIP_IPV6_REASS==1: reassemble incoming IPv6 packets that fragmented
*/
-#ifndef CHECKSUM_CHECK_TCP
-#define CHECKSUM_CHECK_TCP 1
+#if !defined LWIP_IPV6_REASS || defined __DOXYGEN__
+#define LWIP_IPV6_REASS (LWIP_IPV6)
#endif
/**
- * LWIP_CHECKSUM_ON_COPY==1: Calculate checksum when copying data from
- * application buffers to pbufs.
+ * LWIP_IPV6_SEND_ROUTER_SOLICIT==1: Send router solicitation messages during
+ * network startup.
*/
-#ifndef LWIP_CHECKSUM_ON_COPY
-#define LWIP_CHECKSUM_ON_COPY 0
+#if !defined LWIP_IPV6_SEND_ROUTER_SOLICIT || defined __DOXYGEN__
+#define LWIP_IPV6_SEND_ROUTER_SOLICIT 1
#endif
-/*
- ---------------------------------------
- ---------- IPv6 options ---------------
- ---------------------------------------
-*/
/**
- * LWIP_IPV6==1: Enable IPv6
+ * LWIP_IPV6_AUTOCONFIG==1: Enable stateless address autoconfiguration as per RFC 4862.
*/
-#ifndef LWIP_IPV6
-#define LWIP_IPV6 0
+#if !defined LWIP_IPV6_AUTOCONFIG || defined __DOXYGEN__
+#define LWIP_IPV6_AUTOCONFIG (LWIP_IPV6)
#endif
/**
- * LWIP_IPV6_NUM_ADDRESSES: Number of IPv6 addresses per netif.
+ * LWIP_IPV6_ADDRESS_LIFETIMES==1: Keep valid and preferred lifetimes for each
+ * IPv6 address. Required for LWIP_IPV6_AUTOCONFIG. May still be enabled
+ * otherwise, in which case the application may assign address lifetimes with
+ * the appropriate macros. Addresses with no lifetime are assumed to be static.
+ * If this option is disabled, all addresses are assumed to be static.
*/
-#ifndef LWIP_IPV6_NUM_ADDRESSES
-#define LWIP_IPV6_NUM_ADDRESSES 3
+#if !defined LWIP_IPV6_ADDRESS_LIFETIMES || defined __DOXYGEN__
+#define LWIP_IPV6_ADDRESS_LIFETIMES (LWIP_IPV6_AUTOCONFIG)
#endif
/**
- * LWIP_IPV6_FORWARD==1: Forward IPv6 packets across netifs
+ * LWIP_IPV6_DUP_DETECT_ATTEMPTS=[0..7]: Number of duplicate address detection attempts.
*/
-#ifndef LWIP_IPV6_FORWARD
-#define LWIP_IPV6_FORWARD 0
+#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)
*/
-#ifndef LWIP_ICMP6
+#if !defined LWIP_ICMP6 || defined __DOXYGEN__
#define LWIP_ICMP6 (LWIP_IPV6)
#endif
@@ -1930,92 +2389,92 @@
* LWIP_ICMP6_DATASIZE: bytes from original packet to send back in
* ICMPv6 error messages.
*/
-#ifndef LWIP_ICMP6_DATASIZE
+#if !defined LWIP_ICMP6_DATASIZE || defined __DOXYGEN__
#define LWIP_ICMP6_DATASIZE 8
#endif
/**
* LWIP_ICMP6_HL: default hop limit for ICMPv6 messages
*/
-#ifndef LWIP_ICMP6_HL
+#if !defined LWIP_ICMP6_HL || defined __DOXYGEN__
#define LWIP_ICMP6_HL 255
#endif
-
/**
- * LWIP_ICMP6_CHECKSUM_CHECK==1: verify checksum on ICMPv6 packets
+ * @}
*/
-#ifndef LWIP_ICMP6_CHECKSUM_CHECK
-#define LWIP_ICMP6_CHECKSUM_CHECK 1
-#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.
*/
-#ifndef LWIP_IPV6_MLD
+#if !defined LWIP_IPV6_MLD || defined __DOXYGEN__
#define LWIP_IPV6_MLD (LWIP_IPV6)
#endif
/**
- * MEMP_NUM_MLD6_GROUP: Max number of IPv6 multicast that can be joined.
+ * 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.
*/
-#ifndef MEMP_NUM_MLD6_GROUP
+#if !defined MEMP_NUM_MLD6_GROUP || defined __DOXYGEN__
#define MEMP_NUM_MLD6_GROUP 4
#endif
-
/**
- * LWIP_IPV6_FRAG==1: Fragment outgoing IPv6 packets that are too big.
+ * @}
*/
-#ifndef LWIP_IPV6_FRAG
-#define LWIP_IPV6_FRAG 0
-#endif
/**
- * LWIP_IPV6_REASS==1: reassemble incoming IPv6 packets that fragmented
+ * @defgroup lwip_opts_nd6 Neighbor discovery
+ * @ingroup lwip_opts_ipv6
+ * @{
*/
-#ifndef LWIP_IPV6_REASS
-#define LWIP_IPV6_REASS (LWIP_IPV6)
-#endif
-
/**
* LWIP_ND6_QUEUEING==1: queue outgoing IPv6 packets while MAC address
* is being resolved.
*/
-#ifndef LWIP_ND6_QUEUEING
+#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.
*/
-#ifndef MEMP_NUM_ND6_QUEUE
+#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
*/
-#ifndef LWIP_ND6_NUM_NEIGHBORS
+#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
*/
-#ifndef LWIP_ND6_NUM_DESTINATIONS
+#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
*/
-#ifndef LWIP_ND6_NUM_PREFIXES
+#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
*/
-#ifndef LWIP_ND6_NUM_ROUTERS
+#if !defined LWIP_ND6_NUM_ROUTERS || defined __DOXYGEN__
#define LWIP_ND6_NUM_ROUTERS 3
#endif
@@ -2023,7 +2482,7 @@
* LWIP_ND6_MAX_MULTICAST_SOLICIT: max number of multicast solicit messages to send
* (neighbor solicit and router solicit)
*/
-#ifndef LWIP_ND6_MAX_MULTICAST_SOLICIT
+#if !defined LWIP_ND6_MAX_MULTICAST_SOLICIT || defined __DOXYGEN__
#define LWIP_ND6_MAX_MULTICAST_SOLICIT 3
#endif
@@ -2031,21 +2490,21 @@
* LWIP_ND6_MAX_UNICAST_SOLICIT: max number of unicast neighbor solicitation messages
* to send during neighbor reachability detection.
*/
-#ifndef LWIP_ND6_MAX_UNICAST_SOLICIT
+#if !defined LWIP_ND6_MAX_UNICAST_SOLICIT || defined __DOXYGEN__
#define LWIP_ND6_MAX_UNICAST_SOLICIT 3
#endif
/**
* Unused: See ND RFC (time in milliseconds).
*/
-#ifndef LWIP_ND6_MAX_ANYCAST_DELAY_TIME
+#if !defined LWIP_ND6_MAX_ANYCAST_DELAY_TIME || defined __DOXYGEN__
#define LWIP_ND6_MAX_ANYCAST_DELAY_TIME 1000
#endif
/**
* Unused: See ND RFC
*/
-#ifndef LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT
+#if !defined LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT || defined __DOXYGEN__
#define LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT 3
#endif
@@ -2053,14 +2512,14 @@
* LWIP_ND6_REACHABLE_TIME: default neighbor reachable time (in milliseconds).
* May be updated by router advertisement messages.
*/
-#ifndef LWIP_ND6_REACHABLE_TIME
+#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
*/
-#ifndef LWIP_ND6_RETRANS_TIMER
+#if !defined LWIP_ND6_RETRANS_TIMER || defined __DOXYGEN__
#define LWIP_ND6_RETRANS_TIMER 1000
#endif
@@ -2068,7 +2527,7 @@
* LWIP_ND6_DELAY_FIRST_PROBE_TIME: Delay before first unicast neighbor solicitation
* message is sent, during neighbor reachability detection.
*/
-#ifndef LWIP_ND6_DELAY_FIRST_PROBE_TIME
+#if !defined LWIP_ND6_DELAY_FIRST_PROBE_TIME || defined __DOXYGEN__
#define LWIP_ND6_DELAY_FIRST_PROBE_TIME 5000
#endif
@@ -2076,45 +2535,35 @@
* LWIP_ND6_ALLOW_RA_UPDATES==1: Allow Router Advertisement messages to update
* Reachable time and retransmission timers, and netif MTU.
*/
-#ifndef LWIP_ND6_ALLOW_RA_UPDATES
+#if !defined LWIP_ND6_ALLOW_RA_UPDATES || defined __DOXYGEN__
#define LWIP_ND6_ALLOW_RA_UPDATES 1
#endif
/**
- * LWIP_IPV6_SEND_ROUTER_SOLICIT==1: Send router solicitation messages during
- * network startup.
- */
-#ifndef LWIP_IPV6_SEND_ROUTER_SOLICIT
-#define LWIP_IPV6_SEND_ROUTER_SOLICIT 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.
*/
-#ifndef LWIP_ND6_TCP_REACHABILITY_HINTS
+#if !defined LWIP_ND6_TCP_REACHABILITY_HINTS || defined __DOXYGEN__
#define LWIP_ND6_TCP_REACHABILITY_HINTS 1
#endif
/**
- * LWIP_IPV6_AUTOCONFIG==1: Enable stateless address autoconfiguration as per RFC 4862.
+ * 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.
*/
-#ifndef LWIP_IPV6_AUTOCONFIG
-#define LWIP_IPV6_AUTOCONFIG (LWIP_IPV6)
+#if !defined LWIP_ND6_RDNSS_MAX_DNS_SERVERS || defined __DOXYGEN__
+#define LWIP_ND6_RDNSS_MAX_DNS_SERVERS 0
#endif
-
/**
- * LWIP_IPV6_DUP_DETECT_ATTEMPTS: Number of duplicate address detection attempts.
+ * @}
*/
-#ifndef LWIP_IPV6_DUP_DETECT_ATTEMPTS
-#define LWIP_IPV6_DUP_DETECT_ATTEMPTS 1
-#endif
/**
* LWIP_IPV6_DHCP6==1: enable DHCPv6 stateful address autoconfiguration.
*/
-#ifndef LWIP_IPV6_DHCP6
+#if !defined LWIP_IPV6_DHCP6 || defined __DOXYGEN__
#define LWIP_IPV6_DHCP6 0
#endif
@@ -2124,11 +2573,52 @@
---------------------------------------
*/
-/* Hooks are undefined by default, define them to a function if you need them. */
+/**
+ * @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)
+ * Called from ip_input() (IPv4)
+ * Signature:
+ * int my_hook(struct pbuf *pbuf, struct netif *input_netif);
+ * Arguments:
* - pbuf: received struct pbuf passed to ip_input()
* - input_netif: struct netif on which the packet has been received
* Return values:
@@ -2137,13 +2627,241 @@
* 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)
+ * Called from ip_route() (IPv4)
+ * Signature:
+ * struct netif *my_hook(const ip4_addr_t *dest);
+ * Arguments:
+ * - dest: destination IPv4 address
+ * Returns values:
+ * - the destination netif
+ * - 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(src, dest):
+ * Source-based routing for IPv4 - called from ip_route() (IPv4)
+ * Signature:
+ * struct netif *my_hook(const ip4_addr_t *src, const ip4_addr_t *dest);
+ * Arguments:
+ * - src: local/source IPv4 address
* - dest: destination IPv4 address
- * Returns the destination netif or NULL if no destination netif is found. In
- * that case, ip_route() continues as normal.
+ * Returns values:
+ * - the destination netif
+ * - NULL if no destination netif is found. In that case, ip_route() continues as normal.
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_IP4_ROUTE_SRC(src, dest)
+#endif
+
+/**
+ * LWIP_HOOK_ETHARP_GET_GW(netif, dest):
+ * Called from etharp_output() (IPv4)
+ * Signature:
+ * const ip4_addr_t *my_hook(struct netif *netif, const ip4_addr_t *dest);
+ * Arguments:
+ * - netif: the netif used for sending
+ * - dest: the destination IPv4 address
+ * Return values:
+ * - the IPv4 address of the gateway to handle the specified destination IPv4 address
+ * - NULL, in which case 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)
+ * Signature:
+ * int my_hook(struct pbuf *pbuf, struct netif *input_netif);
+ * Arguments:
+ * - 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 ip_route() (IPv6)
+ * Signature:
+ * struct netif *my_hook(const ip6_addr_t *dest, const ip6_addr_t *src);
+ * Arguments:
+ * - src: source IPv6 address
+ * - dest: destination IPv6 address
+ * Return values:
+ * - the destination netif
+ * - 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)
+ * Signature:
+ * const ip6_addr_t *my_hook(struct netif *netif, const ip6_addr_t *dest);
+ * Arguments:
+ * - netif: the netif used for sending
+ * - dest: the destination IPv6 address
+ * Return values:
+ * - the IPv6 address of the next hop to handle the specified destination IPv6 address
+ * - NULL, in which case 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
+ * Signature:
+ * int my_hook(struct netif *netif, struct eth_hdr *eth_hdr, struct eth_vlan_hdr *vlan_hdr);
+ * Arguments:
+ * - 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
+ * Signature:
+ * void my_hook(memp_t type);
+ */
+#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.
+ * Signature:
+ * err_t my_hook(struct pbuf* pbuf, struct netif* netif);
+ * Arguments:
+ * - p: rx packet with unknown eth type
+ * - netif: netif on which the packet has been received
+ * Return values:
+ * - ERR_OK if packet is accepted (hook function now owns the pbuf)
+ * - any error code otherwise (pbuf is freed)
+ *
+ * Payload points to ethernet header!
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_UNKNOWN_ETH_PROTOCOL(pbuf, netif)
+#endif
+
+/**
+ * LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, state, msg, msg_type, options_len_ptr):
+ * Called from various dhcp functions when sending a DHCP message.
+ * This hook is called just before the DHCP message trailer is added, so the
+ * options are at the end of a DHCP message.
+ * Signature:
+ * void my_hook(struct netif *netif, struct dhcp *dhcp, u8_t state, struct dhcp_msg *msg,
+ * u8_t msg_type, u16_t *options_len_ptr);
+ * Arguments:
+ * - netif: struct netif that the packet will be sent through
+ * - dhcp: struct dhcp on that netif
+ * - state: current dhcp state (dhcp_state_enum_t as an u8_t)
+ * - msg: struct dhcp_msg that will be sent
+ * - msg_type: dhcp message type to be sent (u8_t)
+ * - options_len_ptr: pointer to the current length of options in the dhcp_msg "msg"
+ * (must be increased when options are added!)
+ *
+ * Options need to appended like this:
+ * LWIP_ASSERT("dhcp option overflow", *options_len_ptr + option_len + 2 <= DHCP_OPTIONS_LEN);
+ * msg->options[(*options_len_ptr)++] = &lt;option_number&gt;;
+ * msg->options[(*options_len_ptr)++] = &lt;option_len&gt;;
+ * msg->options[(*options_len_ptr)++] = &lt;option_bytes&gt;;
+ * [...]
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_DHCP_APPEND_OPTIONS(netif, dhcp, state, msg, msg_type, options_len_ptr)
+#endif
+
+/**
+ * LWIP_HOOK_DHCP_PARSE_OPTION(netif, dhcp, state, msg, msg_type, option, len, pbuf, option_value_offset):
+ * Called from dhcp_parse_reply when receiving a DHCP message.
+ * This hook is called for every option in the received message that is not handled internally.
+ * Signature:
+ * void my_hook(struct netif *netif, struct dhcp *dhcp, u8_t state, struct dhcp_msg *msg,
+ * u8_t msg_type, u8_t option, u8_t option_len, struct pbuf *pbuf, u16_t option_value_offset);
+ * Arguments:
+ * - netif: struct netif that the packet will be sent through
+ * - dhcp: struct dhcp on that netif
+ * - state: current dhcp state (dhcp_state_enum_t as an u8_t)
+ * - msg: struct dhcp_msg that was received
+ * - msg_type: dhcp message type received (u8_t, ATTENTION: only valid after
+ * the message type option has been parsed!)
+ * - option: option value (u8_t)
+ * - len: option data length (u8_t)
+ * - pbuf: pbuf where option data is contained
+ * - option_value_offset: offset in pbuf where option *data* begins
+ *
+ * A nice way to get the option contents is pbuf_get_contiguous():
+ * u8_t buf[32];
+ * u8_t *ptr = (u8_t*)pbuf_get_contiguous(p, buf, sizeof(buf), LWIP_MIN(option_len, sizeof(buf)), offset);
+ */
+#ifdef __DOXYGEN__
+#define LWIP_HOOK_DHCP_PARSE_OPTION(netif, dhcp, state, msg, msg_type, option, len, pbuf, offset)
+#endif
+/**
+ * @}
*/
/*
@@ -2152,152 +2870,159 @@
---------------------------------------
*/
/**
+ * @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
*/
-#ifndef LWIP_DBG_MIN_LEVEL
+#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
*/
-#ifndef LWIP_DBG_TYPES_ON
+#if !defined LWIP_DBG_TYPES_ON || defined __DOXYGEN__
#define LWIP_DBG_TYPES_ON LWIP_DBG_ON
#endif
/**
* ETHARP_DEBUG: Enable debugging in etharp.c.
*/
-#ifndef ETHARP_DEBUG
+#if !defined ETHARP_DEBUG || defined __DOXYGEN__
#define ETHARP_DEBUG LWIP_DBG_OFF
#endif
/**
* NETIF_DEBUG: Enable debugging in netif.c.
*/
-#ifndef NETIF_DEBUG
+#if !defined NETIF_DEBUG || defined __DOXYGEN__
#define NETIF_DEBUG LWIP_DBG_OFF
#endif
/**
* PBUF_DEBUG: Enable debugging in pbuf.c.
*/
-#ifndef PBUF_DEBUG
+#if !defined PBUF_DEBUG || defined __DOXYGEN__
#define PBUF_DEBUG LWIP_DBG_OFF
#endif
/**
* API_LIB_DEBUG: Enable debugging in api_lib.c.
*/
-#ifndef API_LIB_DEBUG
+#if !defined API_LIB_DEBUG || defined __DOXYGEN__
#define API_LIB_DEBUG LWIP_DBG_OFF
#endif
/**
* API_MSG_DEBUG: Enable debugging in api_msg.c.
*/
-#ifndef API_MSG_DEBUG
+#if !defined API_MSG_DEBUG || defined __DOXYGEN__
#define API_MSG_DEBUG LWIP_DBG_OFF
#endif
/**
* SOCKETS_DEBUG: Enable debugging in sockets.c.
*/
-#ifndef SOCKETS_DEBUG
+#if !defined SOCKETS_DEBUG || defined __DOXYGEN__
#define SOCKETS_DEBUG LWIP_DBG_OFF
#endif
/**
* ICMP_DEBUG: Enable debugging in icmp.c.
*/
-#ifndef ICMP_DEBUG
+#if !defined ICMP_DEBUG || defined __DOXYGEN__
#define ICMP_DEBUG LWIP_DBG_OFF
#endif
/**
* IGMP_DEBUG: Enable debugging in igmp.c.
*/
-#ifndef IGMP_DEBUG
+#if !defined IGMP_DEBUG || defined __DOXYGEN__
#define IGMP_DEBUG LWIP_DBG_OFF
#endif
/**
* INET_DEBUG: Enable debugging in inet.c.
*/
-#ifndef INET_DEBUG
+#if !defined INET_DEBUG || defined __DOXYGEN__
#define INET_DEBUG LWIP_DBG_OFF
#endif
/**
* IP_DEBUG: Enable debugging for IP.
*/
-#ifndef IP_DEBUG
+#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.
*/
-#ifndef IP_REASS_DEBUG
+#if !defined IP_REASS_DEBUG || defined __DOXYGEN__
#define IP_REASS_DEBUG LWIP_DBG_OFF
#endif
/**
* RAW_DEBUG: Enable debugging in raw.c.
*/
-#ifndef RAW_DEBUG
+#if !defined RAW_DEBUG || defined __DOXYGEN__
#define RAW_DEBUG LWIP_DBG_OFF
#endif
/**
* MEM_DEBUG: Enable debugging in mem.c.
*/
-#ifndef MEM_DEBUG
+#if !defined MEM_DEBUG || defined __DOXYGEN__
#define MEM_DEBUG LWIP_DBG_OFF
#endif
/**
* MEMP_DEBUG: Enable debugging in memp.c.
*/
-#ifndef MEMP_DEBUG
+#if !defined MEMP_DEBUG || defined __DOXYGEN__
#define MEMP_DEBUG LWIP_DBG_OFF
#endif
/**
* SYS_DEBUG: Enable debugging in sys.c.
*/
-#ifndef SYS_DEBUG
+#if !defined SYS_DEBUG || defined __DOXYGEN__
#define SYS_DEBUG LWIP_DBG_OFF
#endif
/**
* TIMERS_DEBUG: Enable debugging in timers.c.
*/
-#ifndef TIMERS_DEBUG
+#if !defined TIMERS_DEBUG || defined __DOXYGEN__
#define TIMERS_DEBUG LWIP_DBG_OFF
#endif
/**
* TCP_DEBUG: Enable debugging for TCP.
*/
-#ifndef TCP_DEBUG
+#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.
*/
-#ifndef TCP_INPUT_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.
*/
-#ifndef TCP_FR_DEBUG
+#if !defined TCP_FR_DEBUG || defined __DOXYGEN__
#define TCP_FR_DEBUG LWIP_DBG_OFF
#endif
@@ -2305,113 +3030,116 @@
* TCP_RTO_DEBUG: Enable debugging in TCP for retransmit
* timeout.
*/
-#ifndef TCP_RTO_DEBUG
+#if !defined TCP_RTO_DEBUG || defined __DOXYGEN__
#define TCP_RTO_DEBUG LWIP_DBG_OFF
#endif
/**
* TCP_CWND_DEBUG: Enable debugging for TCP congestion window.
*/
-#ifndef TCP_CWND_DEBUG
+#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.
*/
-#ifndef TCP_WND_DEBUG
+#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.
*/
-#ifndef TCP_OUTPUT_DEBUG
+#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.
*/
-#ifndef TCP_RST_DEBUG
+#if !defined TCP_RST_DEBUG || defined __DOXYGEN__
#define TCP_RST_DEBUG LWIP_DBG_OFF
#endif
/**
* TCP_QLEN_DEBUG: Enable debugging for TCP queue lengths.
*/
-#ifndef TCP_QLEN_DEBUG
+#if !defined TCP_QLEN_DEBUG || defined __DOXYGEN__
#define TCP_QLEN_DEBUG LWIP_DBG_OFF
#endif
/**
* UDP_DEBUG: Enable debugging in UDP.
*/
-#ifndef UDP_DEBUG
+#if !defined UDP_DEBUG || defined __DOXYGEN__
#define UDP_DEBUG LWIP_DBG_OFF
#endif
/**
* TCPIP_DEBUG: Enable debugging in tcpip.c.
*/
-#ifndef TCPIP_DEBUG
+#if !defined TCPIP_DEBUG || defined __DOXYGEN__
#define TCPIP_DEBUG LWIP_DBG_OFF
#endif
/**
- * PPP_DEBUG: Enable debugging for PPP.
- */
-#ifndef PPP_DEBUG
-#define PPP_DEBUG LWIP_DBG_OFF
-#endif
-
-/**
* SLIP_DEBUG: Enable debugging in slipif.c.
*/
-#ifndef SLIP_DEBUG
+#if !defined SLIP_DEBUG || defined __DOXYGEN__
#define SLIP_DEBUG LWIP_DBG_OFF
#endif
/**
* DHCP_DEBUG: Enable debugging in dhcp.c.
*/
-#ifndef DHCP_DEBUG
+#if !defined DHCP_DEBUG || defined __DOXYGEN__
#define DHCP_DEBUG LWIP_DBG_OFF
#endif
/**
* AUTOIP_DEBUG: Enable debugging in autoip.c.
*/
-#ifndef AUTOIP_DEBUG
+#if !defined AUTOIP_DEBUG || defined __DOXYGEN__
#define AUTOIP_DEBUG LWIP_DBG_OFF
#endif
/**
- * SNMP_MSG_DEBUG: Enable debugging for SNMP messages.
+ * DNS_DEBUG: Enable debugging for DNS.
*/
-#ifndef SNMP_MSG_DEBUG
-#define SNMP_MSG_DEBUG LWIP_DBG_OFF
+#if !defined DNS_DEBUG || defined __DOXYGEN__
+#define DNS_DEBUG LWIP_DBG_OFF
#endif
/**
- * SNMP_MIB_DEBUG: Enable debugging for SNMP MIBs.
+ * IP6_DEBUG: Enable debugging for IPv6.
*/
-#ifndef SNMP_MIB_DEBUG
-#define SNMP_MIB_DEBUG LWIP_DBG_OFF
+#if !defined IP6_DEBUG || defined __DOXYGEN__
+#define IP6_DEBUG LWIP_DBG_OFF
#endif
-
/**
- * DNS_DEBUG: Enable debugging for DNS.
+ * @}
*/
-#ifndef DNS_DEBUG
-#define DNS_DEBUG LWIP_DBG_OFF
-#endif
+/*
+ --------------------------------------------------
+ ---------- Performance tracking options ----------
+ --------------------------------------------------
+*/
/**
- * IP6_DEBUG: Enable debugging for IPv6.
+ * @defgroup lwip_opts_perf Performance
+ * @ingroup lwip_opts_debug
+ * @{
*/
-#ifndef IP6_DEBUG
-#define IP6_DEBUG LWIP_DBG_OFF
+/**
+ * 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_OPT_H__ */
+#endif /* LWIP_HDR_OPT_H */
diff --git a/lwip/src/include/lwip/pbuf.h b/lwip/src/include/lwip/pbuf.h
index 4f8dca8..82902a4 100644
--- a/lwip/src/include/lwip/pbuf.h
+++ b/lwip/src/include/lwip/pbuf.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,27 +16,27 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_PBUF_H__
-#define __LWIP_PBUF_H__
+#ifndef LWIP_HDR_PBUF_H
+#define LWIP_HDR_PBUF_H
#include "lwip/opt.h"
#include "lwip/err.h"
@@ -40,9 +45,32 @@
extern "C" {
#endif
-/** Currently, the pbuf_custom code is only needed for one specific configuration
- * of IP_FRAG */
-#define LWIP_SUPPORT_CUSTOM_PBUF (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF)
+/** 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
+
+/** @ingroup pbuf
+ * PBUF_NEEDS_COPY(p): return a boolean value indicating whether the given
+ * pbuf needs to be copied in order to be kept around beyond the current call
+ * stack without risking being corrupted. The default setting provides safety:
+ * it will make a copy iof any pbuf chain that does not consist entirely of
+ * PBUF_ROM type pbufs. For setups with zero-copy support, it may be redefined
+ * to evaluate to true in all cases, for example. However, doing so also has an
+ * effect on the application side: any buffers that are *not* copied must also
+ * *not* be reused by the application after passing them to lwIP. For example,
+ * when setting PBUF_NEEDS_COPY to (0), after using udp_send() with a PBUF_RAM
+ * pbuf, the application must free the pbuf immediately, rather than reusing it
+ * for other purposes. For more background information on this, see tasks #6735
+ * and #7896, and bugs #11400 and #49914. */
+#ifndef PBUF_NEEDS_COPY
+#define PBUF_NEEDS_COPY(p) ((p)->type_internal & PBUF_TYPE_FLAG_DATA_VOLATILE)
+#endif /* PBUF_NEEDS_COPY */
/* @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) */
@@ -54,25 +82,96 @@ extern "C" {
#define PBUF_IP_HLEN 20
#endif
+/**
+ * @ingroup pbuf
+ * Enumeration of pbuf layers
+ */
typedef enum {
- PBUF_TRANSPORT,
- PBUF_IP,
- PBUF_LINK,
- PBUF_RAW
+ /** 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 = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN,
+ /** Includes spare room for IP header.
+ * Use this if you intend to pass the pbuf to functions like raw_send().
+ */
+ PBUF_IP = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN,
+ /** 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 = PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN,
+ /** 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 = PBUF_LINK_ENCAPSULATION_HLEN,
+ /** Use this for input packets in a netif driver when calling netif->input()
+ * in the most common case - ethernet-layer netif driver. */
+ PBUF_RAW = 0
} pbuf_layer;
+
+/* Base flags for pbuf_type definitions: */
+
+/** Indicates that the payload directly follows the struct pbuf.
+ * This makes @ref pbuf_header work in both directions. */
+#define PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS 0x80
+/** Indicates the data stored in this pbuf can change. If this pbuf needs
+ * to be queued, it must be copied/duplicated. */
+#define PBUF_TYPE_FLAG_DATA_VOLATILE 0x40
+/** 4 bits are reserved for 16 allocation sources (e.g. heap, pool1, pool2, etc)
+ * Internally, we use: 0=heap, 1=MEMP_PBUF, 2=MEMP_PBUF_POOL -> 13 types free*/
+#define PBUF_TYPE_ALLOC_SRC_MASK 0x0F
+/** Indicates this pbuf is used for RX (if not set, indicates use for TX).
+ * This information can be used to keep some spare RX buffers e.g. for
+ * receiving TCP ACKs to unblock a connection) */
+#define PBUF_ALLOC_FLAG_RX 0x0100
+/** Indicates the application needs the pbuf payload to be in one piece */
+#define PBUF_ALLOC_FLAG_DATA_CONTIGUOUS 0x0200
+
+#define PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP 0x00
+#define PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF 0x01
+#define PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF_POOL 0x02
+/** First pbuf allocation type for applications */
+#define PBUF_TYPE_ALLOC_SRC_MASK_APP_MIN 0x03
+/** Last pbuf allocation type for applications */
+#define PBUF_TYPE_ALLOC_SRC_MASK_APP_MAX PBUF_TYPE_ALLOC_SRC_MASK
+
+/**
+ * @ingroup pbuf
+ * Enumeration of pbuf types
+ */
typedef enum {
- PBUF_RAM, /* pbuf data is stored in RAM */
- PBUF_ROM, /* pbuf data is stored in ROM */
- PBUF_REF, /* pbuf comes from the pbuf pool */
- PBUF_POOL /* pbuf payload refers to RAM */
+ /** pbuf 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_ALLOC_FLAG_DATA_CONTIGUOUS | PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS | PBUF_TYPE_ALLOC_SRC_MASK_STD_HEAP),
+ /** 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_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF,
+ /** 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_TYPE_FLAG_DATA_VOLATILE | PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_PBUF),
+ /** 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_ALLOC_FLAG_RX | PBUF_TYPE_FLAG_STRUCT_DATA_CONTIGUOUS | PBUF_TYPE_ALLOC_SRC_MASK_STD_MEMP_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 and pbuf_header handle such a
- a pbuf differently */
+/** 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
@@ -83,6 +182,7 @@ typedef enum {
/** 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;
@@ -102,8 +202,10 @@ struct pbuf {
/** length of this buffer */
u16_t len;
- /** pbuf_type as u8_t instead of enum to save space */
- u8_t /*pbuf_type*/ type;
+ /** a bit field indicating pbuf type and allocation sources
+ (see PBUF_TYPE_FLAG_*, PBUF_ALLOC_FLAG_* and PBUF_TYPE_ALLOC_SRC_MASK)
+ */
+ u8_t type_internal;
/** misc flags */
u8_t flags;
@@ -113,7 +215,23 @@ struct pbuf {
* 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;
+ LWIP_PBUF_REF_T ref;
+
+ /** For incoming packets, this contains the input netif's index */
+ u8_t if_idx;
+};
+
+
+/** 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
@@ -129,12 +247,11 @@ struct pbuf_custom {
};
#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
-#if LWIP_TCP && TCP_QUEUE_OOSEQ
/** Define this to 0 to prevent freeing ooseq pbufs when the PBUF_POOL is empty */
#ifndef PBUF_POOL_FREE_OOSEQ
#define PBUF_POOL_FREE_OOSEQ 1
#endif /* PBUF_POOL_FREE_OOSEQ */
-#if NO_SYS && PBUF_POOL_FREE_OOSEQ
+#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()
@@ -144,42 +261,62 @@ void pbuf_free_ooseq(void);
/* pbuf_alloc() reported PBUF_POOL to be empty -> try to free some \
ooseq queued pbufs now */ \
pbuf_free_ooseq(); }}while(0)
-#endif /* NO_SYS && PBUF_POOL_FREE_OOSEQ*/
-#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ */
+#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);
+struct pbuf *pbuf_alloc_reference(void *payload, 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);
+void pbuf_realloc(struct pbuf *p, u16_t size);
+#define pbuf_get_allocsrc(p) ((p)->type_internal & PBUF_TYPE_ALLOC_SRC_MASK)
+#define pbuf_match_allocsrc(p, type) (pbuf_get_allocsrc(p) == ((type) & PBUF_TYPE_ALLOC_SRC_MASK))
+#define pbuf_match_type(p, type) pbuf_match_allocsrc(p, type)
u8_t pbuf_header(struct pbuf *p, s16_t header_size);
+u8_t pbuf_header_force(struct pbuf *p, s16_t header_size);
+u8_t pbuf_add_header(struct pbuf *p, size_t header_size_increment);
+u8_t pbuf_add_header_force(struct pbuf *p, size_t header_size_increment);
+u8_t pbuf_remove_header(struct pbuf *p, size_t header_size);
+struct pbuf *pbuf_free_header(struct pbuf *q, u16_t size);
void pbuf_ref(struct pbuf *p);
u8_t pbuf_free(struct pbuf *p);
-u8_t pbuf_clen(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, struct pbuf *p_from);
-u16_t pbuf_copy_partial(struct pbuf *p, void *dataptr, u16_t len, u16_t offset);
+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);
+void *pbuf_get_contiguous(const struct pbuf *p, void *buffer, size_t bufsize, 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);
+struct pbuf *pbuf_clone(pbuf_layer l, pbuf_type type, struct pbuf *p);
#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(struct pbuf* p, u16_t offset);
-u16_t pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n);
-u16_t pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset);
-u16_t pbuf_strstr(struct pbuf* p, const char* substr);
+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_PBUF_H__ */
+#endif /* LWIP_HDR_PBUF_H */
diff --git a/lwip/src/include/lwip/priv/altcp_priv.h b/lwip/src/include/lwip/priv/altcp_priv.h
new file mode 100644
index 0000000..2d3b2fd
--- /dev/null
+++ b/lwip/src/include/lwip/priv/altcp_priv.h
@@ -0,0 +1,146 @@
+/**
+ * @file
+ * Application layered TCP connection API (to be used from TCPIP thread)\n
+ * This interface mimics the tcp callback API to the application while preventing
+ * direct linking (much like virtual functions).
+ * This way, an application can make use of other application layer protocols
+ * on top of TCP without knowing the details (e.g. TLS, proxy connection).
+ */
+
+/*
+ * Copyright (c) 2017 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 <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_ALTCP_PRIV_H
+#define LWIP_HDR_ALTCP_PRIV_H
+
+#include "lwip/opt.h"
+
+#if LWIP_ALTCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/altcp.h"
+#include "lwip/ip_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct altcp_pcb *altcp_alloc(void);
+void altcp_free(struct altcp_pcb *conn);
+
+/* Function prototypes for application layers */
+typedef void (*altcp_set_poll_fn)(struct altcp_pcb *conn, u8_t interval);
+typedef void (*altcp_recved_fn)(struct altcp_pcb *conn, u16_t len);
+typedef err_t (*altcp_bind_fn)(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port);
+typedef err_t (*altcp_connect_fn)(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port, altcp_connected_fn connected);
+
+typedef struct altcp_pcb *(*altcp_listen_fn)(struct altcp_pcb *conn, u8_t backlog, err_t *err);
+
+typedef void (*altcp_abort_fn)(struct altcp_pcb *conn);
+typedef err_t (*altcp_close_fn)(struct altcp_pcb *conn);
+typedef err_t (*altcp_shutdown_fn)(struct altcp_pcb *conn, int shut_rx, int shut_tx);
+
+typedef err_t (*altcp_write_fn)(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags);
+typedef err_t (*altcp_output_fn)(struct altcp_pcb *conn);
+
+typedef u16_t (*altcp_mss_fn)(struct altcp_pcb *conn);
+typedef u16_t (*altcp_sndbuf_fn)(struct altcp_pcb *conn);
+typedef u16_t (*altcp_sndqueuelen_fn)(struct altcp_pcb *conn);
+typedef void (*altcp_nagle_disable_fn)(struct altcp_pcb *conn);
+typedef void (*altcp_nagle_enable_fn)(struct altcp_pcb *conn);
+typedef int (*altcp_nagle_disabled_fn)(struct altcp_pcb *conn);
+
+typedef void (*altcp_setprio_fn)(struct altcp_pcb *conn, u8_t prio);
+
+typedef void (*altcp_dealloc_fn)(struct altcp_pcb *conn);
+
+typedef err_t (*altcp_get_tcp_addrinfo_fn)(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port);
+typedef ip_addr_t *(*altcp_get_ip_fn)(struct altcp_pcb *conn, int local);
+typedef u16_t (*altcp_get_port_fn)(struct altcp_pcb *conn, int local);
+
+#ifdef LWIP_DEBUG
+typedef enum tcp_state (*altcp_dbg_get_tcp_state_fn)(struct altcp_pcb *conn);
+#endif
+
+struct altcp_functions {
+ altcp_set_poll_fn set_poll;
+ altcp_recved_fn recved;
+ altcp_bind_fn bind;
+ altcp_connect_fn connect;
+ altcp_listen_fn listen;
+ altcp_abort_fn abort;
+ altcp_close_fn close;
+ altcp_shutdown_fn shutdown;
+ altcp_write_fn write;
+ altcp_output_fn output;
+ altcp_mss_fn mss;
+ altcp_sndbuf_fn sndbuf;
+ altcp_sndqueuelen_fn sndqueuelen;
+ altcp_nagle_disable_fn nagle_disable;
+ altcp_nagle_enable_fn nagle_enable;
+ altcp_nagle_disabled_fn nagle_disabled;
+ altcp_setprio_fn setprio;
+ altcp_dealloc_fn dealloc;
+ altcp_get_tcp_addrinfo_fn addrinfo;
+ altcp_get_ip_fn getip;
+ altcp_get_port_fn getport;
+#ifdef LWIP_DEBUG
+ altcp_dbg_get_tcp_state_fn dbg_get_tcp_state;
+#endif
+};
+
+void altcp_default_set_poll(struct altcp_pcb *conn, u8_t interval);
+void altcp_default_recved(struct altcp_pcb *conn, u16_t len);
+err_t altcp_default_bind(struct altcp_pcb *conn, const ip_addr_t *ipaddr, u16_t port);
+err_t altcp_default_shutdown(struct altcp_pcb *conn, int shut_rx, int shut_tx);
+err_t altcp_default_write(struct altcp_pcb *conn, const void *dataptr, u16_t len, u8_t apiflags);
+err_t altcp_default_output(struct altcp_pcb *conn);
+u16_t altcp_default_mss(struct altcp_pcb *conn);
+u16_t altcp_default_sndbuf(struct altcp_pcb *conn);
+u16_t altcp_default_sndqueuelen(struct altcp_pcb *conn);
+void altcp_default_nagle_disable(struct altcp_pcb *conn);
+void altcp_default_nagle_enable(struct altcp_pcb *conn);
+int altcp_default_nagle_disabled(struct altcp_pcb *conn);
+void altcp_default_setprio(struct altcp_pcb *conn, u8_t prio);
+void altcp_default_dealloc(struct altcp_pcb *conn);
+err_t altcp_default_get_tcp_addrinfo(struct altcp_pcb *conn, int local, ip_addr_t *addr, u16_t *port);
+ip_addr_t *altcp_default_get_ip(struct altcp_pcb *conn, int local);
+u16_t altcp_default_get_port(struct altcp_pcb *conn, int local);
+#ifdef LWIP_DEBUG
+enum tcp_state altcp_default_dbg_get_tcp_state(struct altcp_pcb *conn);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_ALTCP */
+
+#endif /* LWIP_HDR_ALTCP_PRIV_H */
diff --git a/lwip/src/include/lwip/api_msg.h b/lwip/src/include/lwip/priv/api_msg.h
index 8268036..a64e4c3 100644
--- a/lwip/src/include/lwip/api_msg.h
+++ b/lwip/src/include/lwip/priv/api_msg.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,43 +16,55 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_API_MSG_H__
-#define __LWIP_API_MSG_H__
+#ifndef LWIP_HDR_API_MSG_H
+#define LWIP_HDR_API_MSG_H
#include "lwip/opt.h"
-#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
-
-#include <stddef.h> /* for size_t */
+#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
@@ -59,7 +76,7 @@ extern "C" {
/** 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 {
+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;
@@ -75,19 +92,28 @@ struct api_msg_msg {
} n;
/** used for lwip_netconn_do_bind and lwip_netconn_do_connect */
struct {
- ip_addr_t *ipaddr;
+ API_MSG_M_DEF_C(ip_addr_t, ipaddr);
u16_t port;
+ u8_t if_idx;
} bc;
/** used for lwip_netconn_do_getaddr */
struct {
- ipX_addr_t *ipaddr;
- u16_t *port;
+ 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;
+ /** current vector to write */
+ const struct netvector *vector;
+ /** number of unwritten vectors */
+ u16_t vector_cnt;
+ /** offset into current vector */
+ size_t vector_off;
+ /** total length across vectors */
size_t len;
+ /** offset into total length/output of bytes written when err == ERR_OK */
+ size_t offset;
u8_t apiflags;
#if LWIP_SO_SNDTIMEO
u32_t time_started;
@@ -95,17 +121,25 @@ struct api_msg_msg {
} w;
/** used for lwip_netconn_do_recv */
struct {
- u32_t len;
+ size_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 {
- ipX_addr_t *multiaddr;
- ipX_addr_t *netif_addr;
+ API_MSG_M_DEF_C(ip_addr_t, multiaddr);
+ API_MSG_M_DEF_C(ip_addr_t, netif_addr);
+ u8_t if_idx;
enum netconn_igmp join_or_leave;
} jl;
#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
@@ -115,17 +149,17 @@ struct api_msg_msg {
} lb;
#endif /* TCP_LISTEN_BACKLOG */
} msg;
+#if LWIP_NETCONN_SEM_PER_THREAD
+ sys_sem_t* op_completed_sem;
+#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_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,
@@ -134,31 +168,45 @@ struct api_msg {
(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;
- /** Rhe resolved address is stored here */
- ip_addr_t *addr;
+#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 *sem;
+ sys_sem_t API_MSG_M_DEF_SEM(sem);
/** Errors are given back here */
- err_t *err;
+ err_t API_MSG_M_DEF(err);
};
#endif /* LWIP_DNS */
-void lwip_netconn_do_newconn ( struct api_msg_msg *msg);
-void lwip_netconn_do_delconn ( struct api_msg_msg *msg);
-void lwip_netconn_do_bind ( struct api_msg_msg *msg);
-void lwip_netconn_do_connect ( struct api_msg_msg *msg);
-void lwip_netconn_do_disconnect ( struct api_msg_msg *msg);
-void lwip_netconn_do_listen ( struct api_msg_msg *msg);
-void lwip_netconn_do_send ( struct api_msg_msg *msg);
-void lwip_netconn_do_recv ( struct api_msg_msg *msg);
-void lwip_netconn_do_write ( struct api_msg_msg *msg);
-void lwip_netconn_do_getaddr ( struct api_msg_msg *msg);
-void lwip_netconn_do_close ( struct api_msg_msg *msg);
-void lwip_netconn_do_shutdown ( struct api_msg_msg *msg);
+int lwip_netconn_is_err_msg(void *msg, err_t *err);
+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_bind_if (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( struct api_msg_msg *msg);
+void lwip_netconn_do_join_leave_group(void *m);
+void lwip_netconn_do_join_leave_group_netif(void *m);
#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
#if LWIP_DNS
@@ -168,10 +216,50 @@ void lwip_netconn_do_gethostbyname(void *arg);
struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback);
void netconn_free(struct netconn *conn);
+/* netifapi related lwIP internal definitions */
+
+#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;
+ struct {
+#if LWIP_MPU_COMPATIBLE
+ char name[NETIF_NAMESIZE];
+#else /* LWIP_MPU_COMPATIBLE */
+ char *name;
+#endif /* LWIP_MPU_COMPATIBLE */
+ u8_t index;
+ } ifs;
+ } msg;
+};
+
#ifdef __cplusplus
}
#endif
-#endif /* LWIP_NETCONN */
+#endif /* LWIP_NETCONN || LWIP_SOCKET */
-#endif /* __LWIP_API_MSG_H__ */
+#endif /* LWIP_HDR_API_MSG_H */
diff --git a/lwip/src/include/lwip/priv/memp_priv.h b/lwip/src/include/lwip/priv/memp_priv.h
new file mode 100644
index 0000000..f246061
--- /dev/null
+++ b/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/lwip/src/include/lwip/memp_std.h b/lwip/src/include/lwip/priv/memp_std.h
index 592a282..4ad0b6d 100644
--- a/lwip/src/include/lwip/memp_std.h
+++ b/lwip/src/include/lwip/priv/memp_std.h
@@ -1,3 +1,11 @@
+/**
+ * @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.
*
@@ -12,10 +20,10 @@
#ifndef LWIP_MALLOC_MEMPOOL
/* This treats "malloc pools" just like any other pool.
The pools are a little bigger to provide 'size' as the amount of user data. */
-#define LWIP_MALLOC_MEMPOOL(num, size) LWIP_MEMPOOL(POOL_##size, num, (size + sizeof(struct memp_malloc_helper)), "MALLOC_"#size)
+#define LWIP_MALLOC_MEMPOOL(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 */
+#endif /* LWIP_MALLOC_MEMPOOL */
#ifndef LWIP_PBUF_MEMPOOL
/* This treats "pbuf pools" just like any other pool.
@@ -44,52 +52,62 @@ LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, sizeof(struct tcp_pcb_lis
LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG")
#endif /* LWIP_TCP */
-#if IP_REASSEMBLY
+#if LWIP_ALTCP && LWIP_TCP
+LWIP_MEMPOOL(ALTCP_PCB, MEMP_NUM_ALTCP_PCB, sizeof(struct altcp_pcb), "ALTCP_PCB")
+#endif /* LWIP_ALTCP && LWIP_TCP */
+
+#if LWIP_IPV4 && 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_FRAG
+#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 && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */
+#endif /* IP_FRAG && !LWIP_NETIF_TX_SINGLE_PBUF || (LWIP_IPV6 && LWIP_IPV6_FRAG) */
-#if LWIP_NETCONN
+#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 */
+#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_SOCKET && LWIP_SOCKET_SELECT
+LWIP_MEMPOOL(SELECT_CB, MEMP_NUM_SELECT_CB, sizeof(struct lwip_select_cb), "SELECT_CB")
+#endif /* LWIP_SOCKET && LWIP_SOCKET_SELECT */
+#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
+#if LWIP_IPV4 && LWIP_ARP && ARP_QUEUEING
LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE")
-#endif /* LWIP_ARP && ARP_QUEUEING */
+#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 (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS)) /* LWIP_TIMERS */
+#if LWIP_TIMERS && !LWIP_TIMERS_CUSTOM
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 */
+#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 PPP_SUPPORT && PPPOE_SUPPORT
-LWIP_MEMPOOL(PPPOE_IF, MEMP_NUM_PPPOE_INTERFACES, sizeof(struct pppoe_softc), "PPPOE_IF")
-#endif /* PPP_SUPPORT && PPPOE_SUPPORT */
#if LWIP_IPV6 && LWIP_ND6_QUEUEING
LWIP_MEMPOOL(ND6_QUEUE, MEMP_NUM_ND6_QUEUE, sizeof(struct nd6_q_entry), "ND6_QUEUE")
diff --git a/lwip/src/include/lwip/priv/nd6_priv.h b/lwip/src/include/lwip/priv/nd6_priv.h
new file mode 100644
index 0000000..cc3007d
--- /dev/null
+++ b/lwip/src/include/lwip/priv/nd6_priv.h
@@ -0,0 +1,142 @@
+/**
+ * @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 seconds */
+ 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 seconds */
+};
+
+struct nd6_router_list_entry {
+ struct nd6_neighbor_cache_entry *neighbor_entry;
+ u32_t invalidation_timer; /* in seconds */
+ u8_t flags;
+};
+
+enum nd6_neighbor_cache_entry_state {
+ ND6_NO_ENTRY = 0,
+ ND6_INCOMPLETE,
+ ND6_REACHABLE,
+ ND6_STALE,
+ ND6_DELAY,
+ ND6_PROBE
+};
+
+#define ND6_HOPLIM 255 /* maximum hop limit, required in all ND packets */
+
+#define ND6_2HRS 7200 /* two hours, expressed in number of seconds */
+
+/* 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/lwip/src/include/lwip/priv/sockets_priv.h b/lwip/src/include/lwip/priv/sockets_priv.h
new file mode 100644
index 0000000..9918aa2
--- /dev/null
+++ b/lwip/src/include/lwip/priv/sockets_priv.h
@@ -0,0 +1,164 @@
+/**
+ * @file
+ * Sockets API internal implementations (do not use in application code)
+ */
+
+/*
+ * Copyright (c) 2017 Joel Cunningham, Garmin International, Inc. <joel.cunningham@garmin.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: Joel Cunningham <joel.cunningham@me.com>
+ *
+ */
+#ifndef LWIP_HDR_SOCKETS_PRIV_H
+#define LWIP_HDR_SOCKETS_PRIV_H
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/err.h"
+#include "lwip/sockets.h"
+#include "lwip/sys.h"
+
+#ifdef __cplusplus
+extern "C" {
+#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
+
+union lwip_sock_lastdata {
+ struct netbuf *netbuf;
+ struct pbuf *pbuf;
+};
+
+/** 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 */
+ union lwip_sock_lastdata lastdata;
+#if LWIP_SOCKET_SELECT
+ /** 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;
+ /** counter of how many threads are waiting for this socket using select */
+ SELWAIT_T select_waiting;
+#endif /* LWIP_SOCKET_SELECT */
+#if LWIP_NETCONN_FULLDUPLEX
+ /* counter of how many threads are using a struct lwip_sock (not the 'int') */
+ u8_t fd_used;
+ /* status of pending close/delete actions */
+ u8_t fd_free_pending;
+#define LWIP_SOCK_FD_FREE_TCP 1
+#define LWIP_SOCK_FD_FREE_FREE 2
+#endif
+};
+
+#ifndef set_errno
+#define set_errno(err) do { if (err) { errno = (err); } } while(0)
+#endif
+
+#if !LWIP_TCPIP_CORE_LOCKING
+/** Maximum optlen used by setsockopt/getsockopt */
+#define LWIP_SETGETSOCKOPT_MAXOPTLEN LWIP_MAX(16, sizeof(struct ifreq))
+
+/** 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 */
+ int err;
+ /** semaphore to wake up the calling task */
+ void* completed_sem;
+};
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+
+#ifdef __cplusplus
+}
+#endif
+
+struct lwip_sock* lwip_socket_dbg_get_socket(int fd);
+
+#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;
+};
+
+#endif /* LWIP_SOCKET */
+
+#endif /* LWIP_HDR_SOCKETS_PRIV_H */
diff --git a/lwip/src/include/lwip/tcp_impl.h b/lwip/src/include/lwip/priv/tcp_priv.h
index 2afc20d..9987657 100644
--- a/lwip/src/include/lwip/tcp_impl.h
+++ b/lwip/src/include/lwip/priv/tcp_priv.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,26 +16,26 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_TCP_IMPL_H__
-#define __LWIP_TCP_IMPL_H__
+#ifndef LWIP_HDR_TCP_PRIV_H
+#define LWIP_HDR_TCP_PRIV_H
#include "lwip/opt.h"
@@ -44,6 +49,7 @@
#include "lwip/err.h"
#include "lwip/ip6.h"
#include "lwip/ip6_addr.h"
+#include "lwip/prot/tcp.h"
#ifdef __cplusplus
extern "C" {
@@ -61,6 +67,11 @@ void tcp_tmr (void); /* Must be called every
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);
@@ -68,7 +79,9 @@ void tcp_input (struct pbuf *p, struct netif *inp);
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);
+err_t tcp_rexmit (struct tcp_pcb *pcb);
+err_t tcp_rexmit_rto_prepare(struct tcp_pcb *pcb);
+void tcp_rexmit_rto_commit(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);
@@ -101,19 +114,6 @@ err_t tcp_process_refused_data(struct tcp_pcb *pcb);
#define TCP_SEQ_BETWEEN(a,b,c) ((c)-(b) >= (a)-(b))
#endif
#define TCP_SEQ_BETWEEN(a,b,c) (TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c))
-#define TCP_FIN 0x01U
-#define TCP_SYN 0x02U
-#define TCP_RST 0x04U
-#define TCP_PSH 0x08U
-#define TCP_ACK 0x10U
-#define TCP_URG 0x20U
-#define TCP_ECE 0x40U
-#define TCP_CWR 0x80U
-
-#define TCP_FLAGS 0x3fU
-
-/* Length of the TCP header, excluding options. */
-#define TCP_HLEN 20
#ifndef TCP_TMR_INTERVAL
#define TCP_TMR_INTERVAL 250 /* The TCP timer interval in milliseconds. */
@@ -151,50 +151,18 @@ err_t tcp_process_refused_data(struct tcp_pcb *pcb);
#define TCP_MAXIDLE TCP_KEEPCNT_DEFAULT * TCP_KEEPINTVL_DEFAULT /* Maximum KEEPALIVE probe time */
-/* Fields are (of course) in network byte order.
- * Some fields are converted to host byte order in tcp_input().
- */
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct tcp_hdr {
- PACK_STRUCT_FIELD(u16_t src);
- PACK_STRUCT_FIELD(u16_t dest);
- PACK_STRUCT_FIELD(u32_t seqno);
- PACK_STRUCT_FIELD(u32_t ackno);
- PACK_STRUCT_FIELD(u16_t _hdrlen_rsvd_flags);
- PACK_STRUCT_FIELD(u16_t wnd);
- PACK_STRUCT_FIELD(u16_t chksum);
- PACK_STRUCT_FIELD(u16_t urgp);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
-
-#define TCPH_HDRLEN(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) >> 12)
-#define TCPH_FLAGS(phdr) (ntohs((phdr)->_hdrlen_rsvd_flags) & TCP_FLAGS)
-
-#define TCPH_HDRLEN_SET(phdr, len) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | TCPH_FLAGS(phdr))
-#define TCPH_FLAGS_SET(phdr, flags) (phdr)->_hdrlen_rsvd_flags = (((phdr)->_hdrlen_rsvd_flags & PP_HTONS((u16_t)(~(u16_t)(TCP_FLAGS)))) | htons(flags))
-#define TCPH_HDRLEN_FLAGS_SET(phdr, len, flags) (phdr)->_hdrlen_rsvd_flags = htons(((len) << 12) | (flags))
-
-#define TCPH_SET_FLAG(phdr, flags ) (phdr)->_hdrlen_rsvd_flags = ((phdr)->_hdrlen_rsvd_flags | htons(flags))
-#define TCPH_UNSET_FLAG(phdr, flags) (phdr)->_hdrlen_rsvd_flags = htons(ntohs((phdr)->_hdrlen_rsvd_flags) | (TCPH_FLAGS(phdr) & ~(flags)) )
-
-#define TCP_TCPLEN(seg) ((seg)->len + ((TCPH_FLAGS((seg)->tcphdr) & (TCP_FIN | TCP_SYN)) != 0))
+#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 sucessfully closed. */
+#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(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+#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)
@@ -204,17 +172,19 @@ PACK_STRUCT_END
LWIP_EVENT_RECV, NULL, 0, ERR_OK)
#define TCP_EVENT_CONNECTED(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
LWIP_EVENT_CONNECTED, NULL, 0, (err))
-#define TCP_EVENT_POLL(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
- LWIP_EVENT_POLL, NULL, 0, ERR_OK)
-#define TCP_EVENT_ERR(errf,arg,err) lwip_tcp_event((arg), NULL, \
- LWIP_EVENT_ERR, NULL, 0, (err))
+#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(pcb,err,ret) \
+#define TCP_EVENT_ACCEPT(lpcb,pcb,arg,err,ret) \
do { \
- if((pcb)->accept != NULL) \
- (ret) = (pcb)->accept((pcb)->callback_arg,(pcb),(err)); \
+ if((lpcb)->accept != NULL) \
+ (ret) = (lpcb)->accept((arg),(pcb),(err)); \
else (ret) = ERR_ARG; \
} while (0)
@@ -257,8 +227,9 @@ PACK_STRUCT_END
else (ret) = ERR_OK; \
} while (0)
-#define TCP_EVENT_ERR(errf,arg,err) \
+#define TCP_EVENT_ERR(last_state,errf,arg,err) \
do { \
+ LWIP_UNUSED_ARG(last_state); \
if((errf) != NULL) \
(errf)((arg),(err)); \
} while (0)
@@ -277,14 +248,14 @@ PACK_STRUCT_END
/* This structure represents a TCP segment on the unsent, unacked and ooseq queues */
struct tcp_seg {
- struct tcp_seg *next; /* used when putting segements on a queue */
+ struct 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_oversized only) */
-#endif /* TCP_OVERSIZE_DBGCHECK */
+ tcp_pcb.unsent_oversize only) */
+#endif /* TCP_OVERSIZE_DBGCHECK */
#if TCP_CHECKSUM_ON_COPY
u16_t chksum;
u8_t chksum_swapped;
@@ -294,15 +265,59 @@ struct tcp_seg {
#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 */
+#define TF_SEG_OPTS_SACK_PERM (u8_t)0x10U /* Include SACK Permitted option */
struct tcp_hdr *tcphdr; /* the TCP header */
};
-#define LWIP_TCP_OPT_LENGTH(flags) \
- (flags & TF_SEG_OPTS_MSS ? 4 : 0) + \
- (flags & TF_SEG_OPTS_TS ? 12 : 0)
+#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_SACK_PERM 4
+#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
+
+#if LWIP_TCP_SACK_OUT
+#define LWIP_TCP_OPT_LEN_SACK_PERM 2
+#define LWIP_TCP_OPT_LEN_SACK_PERM_OUT 4 /* aligned for output (includes NOP padding) */
+#else
+#define LWIP_TCP_OPT_LEN_SACK_PERM_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) + \
+ (flags & TF_SEG_OPTS_SACK_PERM ? LWIP_TCP_OPT_LEN_SACK_PERM_OUT : 0)
/** This returns a TCP header option for MSS in an u32_t */
-#define TCP_BUILD_MSS_OPTION(mss) htonl(0x02040000 | ((mss) & 0xFFFF))
+#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;
@@ -311,7 +326,7 @@ 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_listen *listen_pcbs;
struct tcp_pcb *pcbs;
};
extern struct tcp_pcb *tcp_bound_pcbs;
@@ -321,9 +336,11 @@ extern struct tcp_pcb *tcp_active_pcbs; /* List of all TCP PCBs that are in a
data. */
extern struct tcp_pcb *tcp_tw_pcbs; /* List of all TCP PCBs in TIME-WAIT. */
-extern struct tcp_pcb *tcp_tmp_pcb; /* Only used for temporary storage. */
+#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:
+/* 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.
@@ -336,8 +353,9 @@ extern struct tcp_pcb *tcp_tmp_pcb; /* Only used for temporary storage. */
#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); \
+ 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)); \
@@ -350,11 +368,12 @@ extern struct tcp_pcb *tcp_tmp_pcb; /* Only used for temporary storage. */
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) { \
+ } 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; \
@@ -380,7 +399,8 @@ extern struct tcp_pcb *tcp_tmp_pcb; /* Only used for temporary storage. */
(*(pcbs)) = (*pcbs)->next; \
} \
else { \
- for(tcp_tmp_pcb = *pcbs; \
+ 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)) { \
@@ -425,55 +445,38 @@ 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; \
+ tcp_clear_flags(pcb, TF_ACK_DELAY); \
+ tcp_ack_now(pcb); \
} \
else { \
- (pcb)->flags |= TF_ACK_DELAY; \
+ tcp_set_flags(pcb, TF_ACK_DELAY); \
} \
} while (0)
#define tcp_ack_now(pcb) \
- do { \
- (pcb)->flags |= TF_ACK_NOW; \
- } while (0)
+ tcp_set_flags(pcb, TF_ACK_NOW)
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_impl(u32_t seqno, u32_t ackno,
- ipX_addr_t *local_ip, ipX_addr_t *remote_ip,
- u16_t local_port, u16_t remote_port
-#if LWIP_IPV6
- , u8_t isipv6
-#endif /* LWIP_IPV6 */
- );
-#if LWIP_IPV6
-#define tcp_rst(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6) \
- tcp_rst_impl(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6)
-#else /* LWIP_IPV6 */
-#define tcp_rst(seqno, ackno, local_ip, remote_ip, local_port, remote_port, isipv6) \
- tcp_rst_impl(seqno, ackno, local_ip, remote_ip, local_port, remote_port)
-#endif /* LWIP_IPV6 */
-
-u32_t tcp_next_iss(void);
-
-void tcp_keepalive(struct tcp_pcb *pcb);
-void tcp_zero_window_probe(struct tcp_pcb *pcb);
+void tcp_rst(const struct tcp_pcb* pcb, 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_split_unsent_seg(struct tcp_pcb *pcb, u16_t split);
+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, ipX_addr_t *dest
-#if LWIP_IPV6
- , ipX_addr_t *src, u8_t isipv6
-#endif /* LWIP_IPV6 */
- );
-#if LWIP_IPV6
-#define tcp_eff_send_mss(sendmss, src, dest, isipv6) tcp_eff_send_mss_impl(sendmss, dest, src, isipv6)
-#else /* LWIP_IPV6 */
-#define tcp_eff_send_mss(sendmss, src, dest, isipv6) tcp_eff_send_mss_impl(sendmss, dest)
-#endif /* LWIP_IPV6 */
+u16_t tcp_eff_send_mss_netif(u16_t sendmss, struct netif *outif,
+ const ip_addr_t *dest);
+#define tcp_eff_send_mss(sendmss, src, dest) \
+ tcp_eff_send_mss_netif(sendmss, ip_route(src, dest), dest)
#endif /* TCP_CALCULATE_EFF_SEND_MSS */
#if LWIP_CALLBACK_API
@@ -498,6 +501,11 @@ s16_t tcp_pcbs_sane(void);
* 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);
+
+#if TCP_QUEUE_OOSEQ
+void tcp_free_ooseq(struct tcp_pcb *pcb);
+#endif
#ifdef __cplusplus
}
@@ -505,4 +513,4 @@ void tcp_timer_needed(void);
#endif /* LWIP_TCP */
-#endif /* __LWIP_TCP_H__ */
+#endif /* LWIP_HDR_TCP_PRIV_H */
diff --git a/lwip/src/include/lwip/priv/tcpip_priv.h b/lwip/src/include/lwip/priv/tcpip_priv.h
new file mode 100644
index 0000000..2164368
--- /dev/null
+++ b/lwip/src/include/lwip/priv/tcpip_priv.h
@@ -0,0 +1,162 @@
+/**
+ * @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_EXT(type, pool, name, errorblock) do { \
+ name = (type *)memp_malloc(pool); \
+ if (name == NULL) { \
+ errorblock; \
+ } \
+ } while(0)
+#define API_VAR_ALLOC(type, pool, name, errorval) API_VAR_ALLOC_EXT(type, pool, name, return errorval)
+#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_EXT(type, pool, name, errorblock)
+#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/lwip/src/include/lwip/prot/autoip.h b/lwip/src/include/lwip/prot/autoip.h
new file mode 100644
index 0000000..fd3af8a
--- /dev/null
+++ b/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/lwip/src/include/lwip/prot/dhcp.h b/lwip/src/include/lwip/prot/dhcp.h
new file mode 100644
index 0000000..c88a283
--- /dev/null
+++ b/lwip/src/include/lwip/prot/dhcp.h
@@ -0,0 +1,176 @@
+/**
+ * @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
+
+ /* 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
+
+#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/lwip/src/include/lwip/prot/dhcp6.h b/lwip/src/include/lwip/prot/dhcp6.h
new file mode 100644
index 0000000..a53601c
--- /dev/null
+++ b/lwip/src/include/lwip/prot/dhcp6.h
@@ -0,0 +1,136 @@
+/**
+ * @file
+ * DHCPv6 protocol definitions
+ */
+
+/*
+ * Copyright (c) 2017 Simon Goldschmidt <goldsimon@gmx.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.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_PROT_DHCP6_H
+#define LWIP_HDR_PROT_DHCP6_H
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define DHCP6_CLIENT_PORT 546
+#define DHCP6_SERVER_PORT 547
+
+
+ /* DHCPv6 message item offsets and length */
+#define DHCP6_TRANSACTION_ID_LEN 3
+#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 DHCPv6 message */
+struct dhcp6_msg
+{
+ PACK_STRUCT_FLD_8(u8_t msgtype);
+ PACK_STRUCT_FLD_8(u8_t transaction_id[DHCP6_TRANSACTION_ID_LEN]);
+ /* options follow */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+
+/* DHCP6 client states */
+typedef enum {
+ DHCP6_STATE_OFF = 0,
+ DHCP6_STATE_REQUESTING_CONFIG = 1
+} dhcp_state_enum_t;
+
+/* DHCPv6 message types */
+#define DHCP6_SOLICIT 1
+#define DHCP6_ADVERTISE 2
+#define DHCP6_REQUEST 3
+#define DHCP6_CONFIRM 4
+#define DHCP6_RENEW 5
+#define DHCP6_REBIND 6
+#define DHCP6_REPLY 7
+#define DHCP6_RELEASE 8
+#define DHCP6_DECLINE 9
+#define DHCP6_RECONFIGURE 10
+#define DHCP6_INFOREQUEST 11
+#define DHCP6_RELAYFORW 12
+#define DHCP6_RELAYREPL 13
+
+/** DHCPv6 status codes */
+#define DHCP6_STATUS_SUCCESS 0 /* Success. */
+#define DHCP6_STATUS_UNSPECFAIL 1 /* Failure, reason unspecified; this status code is sent by either a client or a server to indicate a failure not explicitly specified in this document. */
+#define DHCP6_STATUS_NOADDRSAVAIL 2 /* Server has no addresses available to assign to the IA(s). */
+#define DHCP6_STATUS_NOBINDING 3 /* Client record (binding) unavailable. */
+#define DHCP6_STATUS_NOTONLINK 4 /* The prefix for the address is not appropriate for the link to which the client is attached. */
+#define DHCP6_STATUS_USEMULTICAST 5 /* Sent by a server to a client to force the client to send messages to the server using the All_DHCP_Relay_Agents_and_Servers address. */
+
+/** DHCPv6 DUID types */
+#define DHCP6_DUID_LLT 1 /* LLT: Link-layer Address Plus Time */
+#define DHCP6_DUID_EN 2 /* EN: Enterprise number */
+#define DHCP6_DUID_LL 3 /* LL: Link-layer Address */
+
+/* DHCPv6 options */
+#define DHCP6_OPTION_CLIENTID 1
+#define DHCP6_OPTION_SERVERID 2
+#define DHCP6_OPTION_IA_NA 3
+#define DHCP6_OPTION_IA_TA 4
+#define DHCP6_OPTION_IAADDR 5
+#define DHCP6_OPTION_ORO 6
+#define DHCP6_OPTION_PREFERENCE 7
+#define DHCP6_OPTION_ELAPSED_TIME 8
+#define DHCP6_OPTION_RELAY_MSG 9
+#define DHCP6_OPTION_AUTH 11
+#define DHCP6_OPTION_UNICAST 12
+#define DHCP6_OPTION_STATUS_CODE 13
+#define DHCP6_OPTION_RAPID_COMMIT 14
+#define DHCP6_OPTION_USER_CLASS 15
+#define DHCP6_OPTION_VENDOR_CLASS 16
+#define DHCP6_OPTION_VENDOR_OPTS 17
+#define DHCP6_OPTION_INTERFACE_ID 18
+#define DHCP6_OPTION_RECONF_MSG 19
+#define DHCP6_OPTION_ACCEPT 20
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /*LWIP_HDR_PROT_DHCP_H*/
diff --git a/lwip/src/include/lwip/prot/dns.h b/lwip/src/include/lwip/prot/dns.h
new file mode 100644
index 0000000..94782d6
--- /dev/null
+++ b/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/lwip/src/include/lwip/prot/etharp.h b/lwip/src/include/lwip/prot/etharp.h
new file mode 100644
index 0000000..811c228
--- /dev/null
+++ b/lwip/src/include/lwip/prot/etharp.h
@@ -0,0 +1,114 @@
+/**
+ * @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"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef ETHARP_HWADDR_LEN
+#define ETHARP_HWADDR_LEN ETH_HWADDR_LEN
+#endif
+
+/**
+ * struct ip4_addr_wordaligned 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_addr_wordaligned {
+ PACK_STRUCT_FIELD(u16_t addrw[2]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** MEMCPY-like copying of IP addresses where addresses are known to be
+ * 16-bit-aligned if the port is correctly configured (so a port could define
+ * this to copying 2 u16_t's) - no NULL-pointer-checking needed. */
+#ifndef IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T
+#define IPADDR_WORDALIGNED_COPY_TO_IP4_ADDR_T(dest, src) SMEMCPY(dest, src, sizeof(ip4_addr_t))
+#endif
+
+ /** MEMCPY-like copying of IP addresses where addresses are known to be
+ * 16-bit-aligned if the port is correctly configured (so a port could define
+ * this to copying 2 u16_t's) - no NULL-pointer-checking needed. */
+#ifndef IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T
+#define IPADDR_WORDALIGNED_COPY_FROM_IP4_ADDR_T(dest, src) SMEMCPY(dest, src, sizeof(ip4_addr_t))
+#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_addr_wordaligned sipaddr);
+ PACK_STRUCT_FLD_S(struct eth_addr dhwaddr);
+ PACK_STRUCT_FLD_S(struct ip4_addr_wordaligned dipaddr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define SIZEOF_ETHARP_HDR 28
+
+/* 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/lwip/src/include/lwip/prot/ethernet.h b/lwip/src/include/lwip/prot/ethernet.h
new file mode 100644
index 0000000..309e574
--- /dev/null
+++ b/lwip/src/include/lwip/prot/ethernet.h
@@ -0,0 +1,125 @@
+/**
+ * @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"
+#include "lwip/prot/ieee.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
+/** An Ethernet MAC address */
+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
+
+/** Initialize a struct eth_addr with its 6 bytes (takes care of correct braces) */
+#define ETH_ADDR(b0, b1, b2, b3, b4, b5) {{b0, b1, b2, b3, b4, b5}}
+
+#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)
+
+/** 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
+
+#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/lwip/src/include/lwip/prot/iana.h b/lwip/src/include/lwip/prot/iana.h
new file mode 100644
index 0000000..1a27b07
--- /dev/null
+++ b/lwip/src/include/lwip/prot/iana.h
@@ -0,0 +1,89 @@
+/**
+ * @file
+ * IANA assigned numbers (RFC 1700 and successors)
+ *
+ * @defgroup iana IANA assigned numbers
+ * @ingroup infrastructure
+ */
+
+/*
+ * Copyright (c) 2017 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 <dziegel@gmx.de>
+ *
+ */
+
+#ifndef LWIP_HDR_PROT_IANA_H
+#define LWIP_HDR_PROT_IANA_H
+
+/**
+ * @ingroup iana
+ * Hardware types
+ */
+enum lwip_iana_hwtype {
+ /** Ethernet */
+ LWIP_IANA_HWTYPE_ETHERNET = 1
+};
+
+/**
+ * @ingroup iana
+ * Port numbers
+ * https://www.iana.org/assignments/service-names-port-numbers/service-names-port-numbers.txt
+ */
+enum lwip_iana_port_number {
+ /** SMTP */
+ LWIP_IANA_PORT_SMTP = 25,
+ /** DHCP server */
+ LWIP_IANA_PORT_DHCP_SERVER = 67,
+ /** DHCP client */
+ LWIP_IANA_PORT_DHCP_CLIENT = 68,
+ /** TFTP */
+ LWIP_IANA_PORT_TFTP = 69,
+ /** HTTP */
+ LWIP_IANA_PORT_HTTP = 80,
+ /** SNTP */
+ LWIP_IANA_PORT_SNTP = 123,
+ /** NETBIOS */
+ LWIP_IANA_PORT_NETBIOS = 137,
+ /** SNMP */
+ LWIP_IANA_PORT_SNMP = 161,
+ /** SNMP traps */
+ LWIP_IANA_PORT_SNMP_TRAP = 162,
+ /** HTTPS */
+ LWIP_IANA_PORT_HTTPS = 443,
+ /** SMTPS */
+ LWIP_IANA_PORT_SMTPS = 465,
+ /** MQTT */
+ LWIP_IANA_PORT_MQTT = 1883,
+ /** MDNS */
+ LWIP_IANA_PORT_MDNS = 5353,
+ /** Secure MQTT */
+ LWIP_IANA_PORT_SEQURE_MQTT = 8883
+};
+
+#endif /* LWIP_HDR_PROT_IANA_H */
diff --git a/lwip/src/include/ipv4/lwip/icmp.h b/lwip/src/include/lwip/prot/icmp.h
index fa893b6..7d19385 100644
--- a/lwip/src/include/ipv4/lwip/icmp.h
+++ b/lwip/src/include/lwip/prot/icmp.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,35 +16,28 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_ICMP_H__
-#define __LWIP_ICMP_H__
+#ifndef LWIP_HDR_PROT_ICMP_H
+#define LWIP_HDR_PROT_ICMP_H
-#include "lwip/opt.h"
-#include "lwip/pbuf.h"
-#include "lwip/ip_addr.h"
-#include "lwip/netif.h"
-
-#if LWIP_IPV6 && LWIP_ICMP6
-#include "lwip/icmp6.h"
-#endif
+#include "lwip/arch.h"
#ifdef __cplusplus
extern "C" {
@@ -56,33 +54,21 @@ extern "C" {
#define ICMP_TSR 14 /* timestamp reply */
#define ICMP_IRQ 15 /* information request */
#define ICMP_IR 16 /* information reply */
-
-enum icmp_dur_type {
- ICMP_DUR_NET = 0, /* net unreachable */
- ICMP_DUR_HOST = 1, /* host unreachable */
- ICMP_DUR_PROTO = 2, /* protocol unreachable */
- ICMP_DUR_PORT = 3, /* port unreachable */
- ICMP_DUR_FRAG = 4, /* fragmentation needed and DF set */
- ICMP_DUR_SR = 5 /* source route failed */
-};
-
-enum icmp_te_type {
- ICMP_TE_TTL = 0, /* time to live exceeded in transit */
- ICMP_TE_FRAG = 1 /* fragment reassembly time exceeded */
-};
+#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 splitted to two u16_t like ICMP echo needs it.
+ * 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_FIELD(u8_t type);
- PACK_STRUCT_FIELD(u8_t code);
+ 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);
@@ -92,34 +78,14 @@ PACK_STRUCT_END
# 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)
-
-/** Combines type and code to an u16_t */
#define ICMPH_TYPE_SET(hdr, t) ((hdr)->type = (t))
#define ICMPH_CODE_SET(hdr, c) ((hdr)->code = (c))
-
-#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
-
-void icmp_input(struct pbuf *p, struct netif *inp);
-void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t);
-void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t);
-
-#endif /* LWIP_ICMP */
-
-#if (LWIP_IPV6 && 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) icmp_dest_unreach(pbuf, ICMP_DUR_PORT)
-#else /* (LWIP_IPV6 && LWIP_ICMP6) || LWIP_ICMP*/
-#define icmp_port_unreach(isipv6, pbuf)
-#endif /* (LWIP_IPV6 && LWIP_ICMP6) || LWIP_ICMP*/
-
#ifdef __cplusplus
}
#endif
-#endif /* __LWIP_ICMP_H__ */
+#endif /* LWIP_HDR_PROT_ICMP_H */
diff --git a/lwip/src/include/lwip/prot/icmp6.h b/lwip/src/include/lwip/prot/icmp6.h
new file mode 100644
index 0000000..3461120
--- /dev/null
+++ b/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/lwip/src/include/lwip/prot/ieee.h b/lwip/src/include/lwip/prot/ieee.h
new file mode 100644
index 0000000..9ccbc9d
--- /dev/null
+++ b/lwip/src/include/lwip/prot/ieee.h
@@ -0,0 +1,83 @@
+/**
+ * @file
+ * IEEE assigned numbers
+ *
+ * @defgroup ieee IEEE assigned numbers
+ * @ingroup infrastructure
+ */
+
+/*
+ * Copyright (c) 2017 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 <dziegel@gmx.de>
+ *
+ */
+
+#ifndef LWIP_HDR_PROT_IEEE_H
+#define LWIP_HDR_PROT_IEEE_H
+
+/**
+ * @ingroup ieee
+ * A list of often ethtypes (although lwIP does not use all of them).
+ */
+enum lwip_ieee_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
+};
+
+#endif /* LWIP_HDR_PROT_IEEE_H */
diff --git a/lwip/src/include/lwip/prot/igmp.h b/lwip/src/include/lwip/prot/igmp.h
new file mode 100644
index 0000000..d60cb31
--- /dev/null
+++ b/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/lwip/src/include/lwip/prot/ip.h b/lwip/src/include/lwip/prot/ip.h
new file mode 100644
index 0000000..bbfae36
--- /dev/null
+++ b/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/lwip/src/include/ipv4/lwip/ip4.h b/lwip/src/include/lwip/prot/ip4.h
index 04b5b8a..f80a9b0 100644
--- a/lwip/src/include/ipv4/lwip/ip4.h
+++ b/lwip/src/include/lwip/prot/ip4.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,61 +16,65 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_IP4_H__
-#define __LWIP_IP4_H__
-
-#include "lwip/opt.h"
+#ifndef LWIP_HDR_PROT_IP4_H
+#define LWIP_HDR_PROT_IP4_H
-#include "lwip/def.h"
-#include "lwip/pbuf.h"
-#include "lwip/ip_addr.h"
-#include "lwip/ip6_addr.h"
-#include "lwip/err.h"
-#include "lwip/netif.h"
+#include "lwip/arch.h"
+#include "lwip/ip4_addr.h"
#ifdef __cplusplus
extern "C" {
#endif
-/** Currently, the function ip_output_if_opt() is only used with IGMP */
-#define IP_OPTIONS_SEND LWIP_IGMP
-
-#define IP_HLEN 20
+/** 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
-#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
+typedef struct ip4_addr_packed ip4_addr_p_t;
+/* Size of the IPv4 header. Same as 'sizeof(struct ip_hdr)'. */
+#define IP_HLEN 20
+/* Maximum size of the IPv4 header with options. */
+#define IP_HLEN_MAX 60
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h"
#endif
PACK_STRUCT_BEGIN
+/* The IPv4 header */
struct ip_hdr {
/* version / header length */
- PACK_STRUCT_FIELD(u8_t _v_hl);
+ PACK_STRUCT_FLD_8(u8_t _v_hl);
/* type of service */
- PACK_STRUCT_FIELD(u8_t _tos);
+ PACK_STRUCT_FLD_8(u8_t _tos);
/* total length */
PACK_STRUCT_FIELD(u16_t _len);
/* identification */
@@ -73,35 +82,39 @@ struct ip_hdr {
/* fragment offset field */
PACK_STRUCT_FIELD(u16_t _offset);
#define IP_RF 0x8000U /* reserved fragment flag */
-#define IP_DF 0x4000U /* dont fragment flag */
+#define IP_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_FIELD(u8_t _ttl);
+ PACK_STRUCT_FLD_8(u8_t _ttl);
/* protocol*/
- PACK_STRUCT_FIELD(u8_t _proto);
+ PACK_STRUCT_FLD_8(u8_t _proto);
/* checksum */
PACK_STRUCT_FIELD(u16_t _chksum);
/* source and destination IP addresses */
- PACK_STRUCT_FIELD(ip_addr_p_t src);
- PACK_STRUCT_FIELD(ip_addr_p_t dest);
+ PACK_STRUCT_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_HL_BYTES(hdr) ((u8_t)(IPH_HL(hdr) * 4))
#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_OFFSET_BYTES(hdr) ((u16_t)((lwip_ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8U))
#define IPH_TTL(hdr) ((hdr)->_ttl)
#define IPH_PROTO(hdr) ((hdr)->_proto)
#define IPH_CHKSUM(hdr) ((hdr)->_chksum)
-#define IPH_VHL_SET(hdr, v, hl) (hdr)->_v_hl = (((v) << 4) | (hl))
+/* 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)
@@ -111,36 +124,8 @@ PACK_STRUCT_END
#define IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum)
-#define ip_init() /* Compatibility define, no init needed. */
-struct netif *ip_route(ip_addr_t *dest);
-err_t ip_input(struct pbuf *p, struct netif *inp);
-err_t ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
- u8_t ttl, u8_t tos, u8_t proto);
-err_t ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
- u8_t ttl, u8_t tos, u8_t proto,
- struct netif *netif);
-#if LWIP_NETIF_HWADDRHINT
-err_t ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
- u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint);
-#endif /* LWIP_NETIF_HWADDRHINT */
-#if IP_OPTIONS_SEND
-err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
- u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
- u16_t optlen);
-#endif /* IP_OPTIONS_SEND */
-
-#define ip_netif_get_local_ipX(netif) (((netif) != NULL) ? ip_2_ipX(&((netif)->ip_addr)) : NULL)
-
-#if IP_DEBUG
-void ip_debug_print(struct pbuf *p);
-#else
-#define ip_debug_print(p)
-#endif /* IP_DEBUG */
-
#ifdef __cplusplus
}
#endif
-#endif /* __LWIP_IP_H__ */
-
-
+#endif /* LWIP_HDR_PROT_IP4_H */
diff --git a/lwip/src/include/ipv6/lwip/ip6.h b/lwip/src/include/lwip/prot/ip6.h
index b199c95..6e1e263 100644
--- a/lwip/src/include/ipv6/lwip/ip6.h
+++ b/lwip/src/include/lwip/prot/ip6.h
@@ -1,14 +1,13 @@
/**
* @file
- *
- * IPv6 layer.
+ * IPv6 protocol definitions
*/
/*
- * Copyright (c) 2010 Inico Technologies Ltd.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
+ * 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,
@@ -17,45 +16,48 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
- * Author: Ivan Delamer <delamer@inicotech.com>
*
+ * Author: Adam Dunkels <adam@sics.se>
*
- * Please coordinate changes and requests with Ivan Delamer
- * <delamer@inicotech.com>
*/
-#ifndef __LWIP_IP6_H__
-#define __LWIP_IP6_H__
-
-#include "lwip/opt.h"
+#ifndef LWIP_HDR_PROT_IP6_H
+#define LWIP_HDR_PROT_IP6_H
-#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/ip.h"
+#include "lwip/arch.h"
#include "lwip/ip6_addr.h"
-#include "lwip/def.h"
-#include "lwip/pbuf.h"
-#include "lwip/netif.h"
-
-#include "lwip/err.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
@@ -70,24 +72,23 @@ extern "C" {
#define IP6_NEXTH_DESTOPTS 60
#define IP6_NEXTH_UDPLITE 136
-
-/* The IPv6 header. */
+/** The IPv6 header. */
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h"
#endif
PACK_STRUCT_BEGIN
struct ip6_hdr {
- /* version / traffic class / flow label */
+ /** version / traffic class / flow label */
PACK_STRUCT_FIELD(u32_t _v_tc_fl);
- /* payload length */
+ /** payload length */
PACK_STRUCT_FIELD(u16_t _plen);
- /* next header */
- PACK_STRUCT_FIELD(u8_t _nexth);
- /* hop limit */
- PACK_STRUCT_FIELD(u8_t _hoplim);
- /* source and destination IP addresses */
- PACK_STRUCT_FIELD(ip6_addr_p_t src);
- PACK_STRUCT_FIELD(ip6_addr_p_t dest);
+ /** 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
@@ -106,19 +107,19 @@ PACK_STRUCT_END
PACK_STRUCT_BEGIN
struct ip6_hbh_hdr {
/* next header */
- PACK_STRUCT_FIELD(u8_t _nexth);
+ PACK_STRUCT_FLD_8(u8_t _nexth);
/* header length */
- PACK_STRUCT_FIELD(u8_t _hlen);
+ PACK_STRUCT_FLD_8(u8_t _hlen);
/* router alert option type */
- PACK_STRUCT_FIELD(u8_t _ra_opt_type);
+ PACK_STRUCT_FLD_8(u8_t _ra_opt_type);
/* router alert option data len */
- PACK_STRUCT_FIELD(u8_t _ra_opt_dlen);
+ 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_FIELD(u8_t _padn_opt_type);
+ PACK_STRUCT_FLD_8(u8_t _padn_opt_type);
/* PadN option data len */
- PACK_STRUCT_FIELD(u8_t _padn_opt_dlen);
+ PACK_STRUCT_FLD_8(u8_t _padn_opt_dlen);
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
#ifdef PACK_STRUCT_USE_INCLUDES
@@ -135,9 +136,9 @@ PACK_STRUCT_END
PACK_STRUCT_BEGIN
struct ip6_frag_hdr {
/* next header */
- PACK_STRUCT_FIELD(u8_t _nexth);
+ PACK_STRUCT_FLD_8(u8_t _nexth);
/* reserved */
- PACK_STRUCT_FIELD(u8_t reserved);
+ PACK_STRUCT_FLD_8(u8_t reserved);
/* fragment offset */
PACK_STRUCT_FIELD(u16_t _fragment_offset);
/* fragmented packet identification */
@@ -148,50 +149,21 @@ PACK_STRUCT_END
# include "arch/epstruct.h"
#endif
-#define IP6H_V(hdr) ((ntohl((hdr)->_v_tc_fl) >> 28) & 0x0f)
-#define IP6H_TC(hdr) ((ntohl((hdr)->_v_tc_fl) >> 20) & 0xff)
-#define IP6H_FL(hdr) (ntohl((hdr)->_v_tc_fl) & 0x000fffff)
-#define IP6H_PLEN(hdr) (ntohs((hdr)->_plen))
+#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 = (htonl(((v) << 28) | ((tc) << 20) | (fl)))
-#define IP6H_PLEN_SET(hdr, plen) (hdr)->_plen = htons(plen)
+#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)
-
-#define ip6_init() /* TODO should we init current addresses and header pointer? */
-struct netif *ip6_route(struct ip6_addr *src, struct ip6_addr *dest);
-ip6_addr_t *ip6_select_source_address(struct netif *netif, ip6_addr_t * dest);
-err_t ip6_input(struct pbuf *p, struct netif *inp);
-err_t ip6_output(struct pbuf *p, struct ip6_addr *src, struct ip6_addr *dest,
- u8_t hl, u8_t tc, u8_t nexth);
-err_t ip6_output_if(struct pbuf *p, struct ip6_addr *src, struct ip6_addr *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, ip6_addr_t *src, 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_ipX(netif, dest) (((netif) != NULL) ? \
- ip6_2_ipX(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_IP6_H__ */
+#endif /* LWIP_HDR_PROT_IP6_H */
diff --git a/lwip/src/include/lwip/prot/mld6.h b/lwip/src/include/lwip/prot/mld6.h
new file mode 100644
index 0000000..be3a006
--- /dev/null
+++ b/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/lwip/src/include/ipv6/lwip/nd6.h b/lwip/src/include/lwip/prot/nd6.h
index 28636e8..c270d07 100644
--- a/lwip/src/include/ipv6/lwip/nd6.h
+++ b/lwip/src/include/lwip/prot/nd6.h
@@ -1,13 +1,10 @@
/**
* @file
- *
- * Neighbor discovery and stateless address autoconfiguration for IPv6.
- * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862
- * (Address autoconfiguration).
+ * ND6 protocol definitions
*/
/*
- * Copyright (c) 2010 Inico Technologies Ltd.
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without modification,
@@ -34,109 +31,31 @@
*
* This file is part of the lwIP TCP/IP stack.
*
- * Author: Ivan Delamer <delamer@inicotech.com>
- *
+ * Author: Adam Dunkels <adam@sics.se>
*
- * Please coordinate changes and requests with Ivan Delamer
- * <delamer@inicotech.com>
*/
+#ifndef LWIP_HDR_PROT_ND6_H
+#define LWIP_HDR_PROT_ND6_H
-#ifndef __LWIP_ND6_H__
-#define __LWIP_ND6_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.h"
+#include "lwip/arch.h"
#include "lwip/ip6_addr.h"
-#include "lwip/netif.h"
-
+#include "lwip/prot/ip6.h"
#ifdef __cplusplus
extern "C" {
#endif
-/* 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;
- u32_t delay_time;
- u32_t probes_sent;
- u32_t stale_time;
- } counter;
-};
-
-struct nd6_destination_cache_entry {
- ip6_addr_t destination_addr;
- ip6_addr_t next_hop_addr;
- u32_t pmtu;
- u32_t age;
-};
-
-struct nd6_prefix_list_entry {
- ip6_addr_t prefix;
- struct netif * netif;
- u32_t invalidation_timer;
-#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;
- u8_t flags;
-};
-
-
-enum nd6_neighbor_cache_entry_state {
- ND6_NO_ENTRY = 0,
- ND6_INCOMPLETE,
- ND6_REACHABLE,
- ND6_STALE,
- ND6_DELAY,
- ND6_PROBE
-};
-
-#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 */
-
/** Neighbor solicitation message header. */
#ifdef PACK_STRUCT_USE_INCLUDES
# include "arch/bpstruct.h"
#endif
PACK_STRUCT_BEGIN
struct ns_header {
- PACK_STRUCT_FIELD(u8_t type);
- PACK_STRUCT_FIELD(u8_t code);
+ 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_FIELD(ip6_addr_p_t target_address);
+ PACK_STRUCT_FLD_S(ip6_addr_p_t target_address);
/* Options follow. */
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
@@ -150,12 +69,12 @@ PACK_STRUCT_END
#endif
PACK_STRUCT_BEGIN
struct na_header {
- PACK_STRUCT_FIELD(u8_t type);
- PACK_STRUCT_FIELD(u8_t code);
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t code);
PACK_STRUCT_FIELD(u16_t chksum);
- PACK_STRUCT_FIELD(u8_t flags);
- PACK_STRUCT_FIELD(u8_t reserved[3]);
- PACK_STRUCT_FIELD(ip6_addr_p_t target_address);
+ 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
@@ -172,8 +91,8 @@ PACK_STRUCT_END
#endif
PACK_STRUCT_BEGIN
struct rs_header {
- PACK_STRUCT_FIELD(u8_t type);
- PACK_STRUCT_FIELD(u8_t code);
+ 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. */
@@ -185,7 +104,7 @@ PACK_STRUCT_END
/** Router advertisement message header. */
#define ND6_RA_FLAG_MANAGED_ADDR_CONFIG (0x80)
-#define ND6_RA_FLAG_OTHER_STATEFUL_CONFIG (0x40)
+#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)
@@ -197,11 +116,11 @@ PACK_STRUCT_END
#endif
PACK_STRUCT_BEGIN
struct ra_header {
- PACK_STRUCT_FIELD(u8_t type);
- PACK_STRUCT_FIELD(u8_t code);
+ PACK_STRUCT_FLD_8(u8_t type);
+ PACK_STRUCT_FLD_8(u8_t code);
PACK_STRUCT_FIELD(u16_t chksum);
- PACK_STRUCT_FIELD(u8_t current_hop_limit);
- PACK_STRUCT_FIELD(u8_t flags);
+ 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);
@@ -218,12 +137,12 @@ PACK_STRUCT_END
#endif
PACK_STRUCT_BEGIN
struct redirect_header {
- PACK_STRUCT_FIELD(u8_t type);
- PACK_STRUCT_FIELD(u8_t code);
+ 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_FIELD(ip6_addr_p_t target_address);
- PACK_STRUCT_FIELD(ip6_addr_p_t destination_address);
+ 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
@@ -239,9 +158,9 @@ PACK_STRUCT_END
#endif
PACK_STRUCT_BEGIN
struct lladdr_option {
- PACK_STRUCT_FIELD(u8_t type);
- PACK_STRUCT_FIELD(u8_t length);
- PACK_STRUCT_FIELD(u8_t addr[NETIF_MAX_HWADDR_LEN]);
+ 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
@@ -259,15 +178,15 @@ PACK_STRUCT_END
#endif
PACK_STRUCT_BEGIN
struct prefix_option {
- PACK_STRUCT_FIELD(u8_t type);
- PACK_STRUCT_FIELD(u8_t length);
- PACK_STRUCT_FIELD(u8_t prefix_length);
- PACK_STRUCT_FIELD(u8_t flags);
+ 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_FIELD(u8_t reserved2[3]);
- PACK_STRUCT_FIELD(u8_t site_prefix_length);
- PACK_STRUCT_FIELD(ip6_addr_p_t prefix);
+ 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
@@ -281,11 +200,11 @@ PACK_STRUCT_END
#endif
PACK_STRUCT_BEGIN
struct redirected_header_option {
- PACK_STRUCT_FIELD(u8_t type);
- PACK_STRUCT_FIELD(u8_t length);
- PACK_STRUCT_FIELD(u8_t reserved[6]);
+ 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_FIELD(u8_t redirected[8]); */
+ /* PACK_STRUCT_FLD_8(u8_t redirected[8]); */
} PACK_STRUCT_STRUCT;
PACK_STRUCT_END
#ifdef PACK_STRUCT_USE_INCLUDES
@@ -299,8 +218,8 @@ PACK_STRUCT_END
#endif
PACK_STRUCT_BEGIN
struct mtu_option {
- PACK_STRUCT_FIELD(u8_t type);
- PACK_STRUCT_FIELD(u8_t length);
+ 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;
@@ -316,54 +235,40 @@ PACK_STRUCT_END
#endif
PACK_STRUCT_BEGIN
struct route_option {
- PACK_STRUCT_FIELD(u8_t type);
- PACK_STRUCT_FIELD(u8_t length);
- PACK_STRUCT_FIELD(u8_t prefix_length);
- PACK_STRUCT_FIELD(u8_t preference);
+ 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_FIELD(ip6_addr_p_t prefix);
+ 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
-/* the possible states of an IP address */
-#define IP6_ADDRESS_STATE_INVALID (0)
-#define IP6_ADDRESS_STATE_VALID (0x4)
-#define IP6_ADDRESS_STATE_PREFERRED (0x5) /* includes valid */
-#define IP6_ADDRESS_STATE_DEPRECATED (0x6) /* includes valid */
-#define IP6_ADDRESS_STATE_TENTATIV (0x8)
-
-/** 1 second period */
-#define ND6_TMR_INTERVAL 1000
-
-/* 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;
+/** Recursive DNS Server Option. */
+#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[1]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
-#define nd6_init() /* TODO should we init tables? */
-void nd6_tmr(void);
-void nd6_input(struct pbuf *p, struct netif *inp);
-s8_t nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif);
-s8_t nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif);
-u16_t nd6_get_destination_mtu(ip6_addr_t * ip6addr, struct netif * netif);
-err_t nd6_queue_packet(s8_t neighbor_index, struct pbuf * p);
-#if LWIP_ND6_TCP_REACHABILITY_HINTS
-void nd6_reachability_hint(ip6_addr_t * ip6addr);
-#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */
+#define SIZEOF_RDNSS_OPTION_BASE 8 /* size without addresses */
#ifdef __cplusplus
}
#endif
-#endif /* LWIP_IPV6 */
-
-#endif /* __LWIP_ND6_H__ */
+#endif /* LWIP_HDR_PROT_ND6_H */
diff --git a/lwip/src/include/lwip/prot/tcp.h b/lwip/src/include/lwip/prot/tcp.h
new file mode 100644
index 0000000..6e70838
--- /dev/null
+++ b/lwip/src/include/lwip/prot/tcp.h
@@ -0,0 +1,98 @@
+/**
+ * @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_HDRLEN_BYTES(phdr) ((u8_t)(TCPH_HDRLEN(phdr) << 2))
+#define TCPH_FLAGS(phdr) ((u8_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/lwip/src/include/lwip/prot/udp.h b/lwip/src/include/lwip/prot/udp.h
new file mode 100644
index 0000000..664f19a
--- /dev/null
+++ b/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/lwip/src/include/lwip/raw.h b/lwip/src/include/lwip/raw.h
index f0c8ed4..8a872a5 100644
--- a/lwip/src/include/lwip/raw.h
+++ b/lwip/src/include/lwip/raw.h
@@ -1,3 +1,9 @@
+/**
+ * @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.
@@ -29,8 +35,8 @@
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_RAW_H__
-#define __LWIP_RAW_H__
+#ifndef LWIP_HDR_RAW_H
+#define LWIP_HDR_RAW_H
#include "lwip/opt.h"
@@ -46,6 +52,10 @@
extern "C" {
#endif
+#define RAW_FLAGS_CONNECTED 0x01U
+#define RAW_FLAGS_HDRINCL 0x02U
+#define RAW_FLAGS_MULTICAST_LOOP 0x04U
+
struct raw_pcb;
/** Function prototype for raw pcb receive callback functions.
@@ -59,29 +69,9 @@ struct raw_pcb;
* if it's not used any more.
*/
typedef u8_t (*raw_recv_fn)(void *arg, struct raw_pcb *pcb, struct pbuf *p,
- ip_addr_t *addr);
-
-#if LWIP_IPV6
-/** Function prototype for raw pcb IPv6 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 IPv6 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_ip6_fn)(void *arg, struct raw_pcb *pcb, struct pbuf *p,
- ip6_addr_t *addr);
-#endif /* LWIP_IPV6 */
-
-#if LWIP_IPV6
-#define RAW_PCB_RECV_IP6 raw_recv_ip6_fn ip6;
-#else
-#define RAW_PCB_RECV_IP6
-#endif /* LWIP_IPV6 */
+ const ip_addr_t *addr);
+/** the RAW protocol control block */
struct raw_pcb {
/* Common members of all PCB types */
IP_PCB;
@@ -89,38 +79,64 @@ struct raw_pcb {
struct raw_pcb *next;
u8_t protocol;
+ u8_t flags;
+
+#if LWIP_MULTICAST_TX_OPTIONS
+ /** outgoing network interface for multicast packets, by interface index (if nonzero) */
+ u8_t mcast_ifindex;
+ /** TTL for outgoing multicast packets */
+ u8_t mcast_ttl;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
/** receive callback function */
- union {
- raw_recv_fn ip4;
- RAW_PCB_RECV_IP6
- } recv;
+ 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, ip_addr_t *ipaddr);
-err_t raw_connect (struct raw_pcb *pcb, ip_addr_t *ipaddr);
+err_t raw_bind (struct raw_pcb *pcb, const ip_addr_t *ipaddr);
+void raw_bind_netif (struct raw_pcb *pcb, const struct netif *netif);
+err_t raw_connect (struct raw_pcb *pcb, const ip_addr_t *ipaddr);
+void raw_disconnect (struct raw_pcb *pcb);
-void raw_recv (struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg);
-err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr);
+err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *ipaddr);
+err_t raw_sendto_if_src(struct raw_pcb *pcb, struct pbuf *p, const ip_addr_t *dst_ip, struct netif *netif, const ip_addr_t *src_ip);
err_t raw_send (struct raw_pcb *pcb, struct pbuf *p);
-#if LWIP_IPV6
-struct raw_pcb * raw_new_ip6 (u8_t proto);
-#define raw_bind_ip6(pcb, ip6addr) raw_bind(pcb, ip6_2_ip(ip6addr))
-#define raw_connect_ip6(pcb, ip6addr) raw_connect(pcb, ip6_2_ip(ip6addr))
-#define raw_recv_ip6(pcb, recv_ip6_fn, recv_arg) raw_recv(pcb, (raw_recv_fn)recv_ip6_fn, recv_arg)
-#define raw_sendto_ip6(pcb, pbuf, ip6addr) raw_sendto(pcb, pbuf, ip6_2_ip(ip6addr))
-#endif /* LWIP_IPV6 */
+void raw_recv (struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg);
+
+#define raw_flags(pcb) ((pcb)->flags)
+#define raw_setflags(pcb,f) ((pcb)->flags = (f))
+
+#define raw_set_flags(pcb, set_flags) do { (pcb)->flags = (u8_t)((pcb)->flags | (set_flags)); } while(0)
+#define raw_clear_flags(pcb, clr_flags) do { (pcb)->flags = (u8_t)((pcb)->flags & ~(clr_flags)); } while(0)
+#define raw_is_flag_set(pcb, flag) (((pcb)->flags & (flag)) != 0)
/* The following functions are the lower layer interface to RAW. */
u8_t raw_input (struct pbuf *p, struct netif *inp);
-#define raw_init() /* Compatibility define, not init needed. */
+#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)
+
+#if LWIP_MULTICAST_TX_OPTIONS
+#define raw_set_multicast_netif_index(pcb, idx) ((pcb)->mcast_ifindex = (idx))
+#define raw_get_multicast_netif_index(pcb) ((pcb)->mcast_ifindex)
+#define raw_set_multicast_ttl(pcb, value) ((pcb)->mcast_ttl = (value))
+#define raw_get_multicast_ttl(pcb) ((pcb)->mcast_ttl)
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
#ifdef __cplusplus
}
@@ -128,4 +144,4 @@ u8_t raw_input (struct pbuf *p, struct netif *inp);
#endif /* LWIP_RAW */
-#endif /* __LWIP_RAW_H__ */
+#endif /* LWIP_HDR_RAW_H */
diff --git a/lwip/src/include/lwip/sio.h b/lwip/src/include/lwip/sio.h
index 28ae2f2..7643e19 100644
--- a/lwip/src/include/lwip/sio.h
+++ b/lwip/src/include/lwip/sio.h
@@ -1,8 +1,8 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,17 +11,17 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
@@ -32,10 +32,11 @@
* It needs to be implemented by those platforms which need SLIP or PPP
*/
-#ifndef __SIO_H__
-#define __SIO_H__
+#ifndef SIO_H
+#define SIO_H
#include "lwip/arch.h"
+#include "lwip/opt.h"
#ifdef __cplusplus
extern "C" {
@@ -53,7 +54,7 @@ typedef void * sio_fd_t;
#ifndef sio_open
/**
* Opens a serial device for communication.
- *
+ *
* @param devnum device number
* @return handle to serial device if successful, NULL otherwise
*/
@@ -63,10 +64,10 @@ sio_fd_t sio_open(u8_t devnum);
#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);
@@ -75,9 +76,9 @@ void sio_send(u8_t c, sio_fd_t fd);
#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);
@@ -86,12 +87,12 @@ u8_t sio_recv(sio_fd_t fd);
#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().
*/
@@ -102,7 +103,7 @@ u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len);
/**
* 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
@@ -114,12 +115,12 @@ u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len);
#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);
@@ -128,7 +129,7 @@ u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len);
#ifndef sio_read_abort
/**
* Aborts a blocking sio_read() call.
- *
+ *
* @param fd serial device handle
*/
void sio_read_abort(sio_fd_t fd);
@@ -138,4 +139,4 @@ void sio_read_abort(sio_fd_t fd);
}
#endif
-#endif /* __SIO_H__ */
+#endif /* SIO_H */
diff --git a/lwip/src/include/lwip/snmp.h b/lwip/src/include/lwip/snmp.h
index 2ed043d..8704d0b 100644
--- a/lwip/src/include/lwip/snmp.h
+++ b/lwip/src/include/lwip/snmp.h
@@ -1,9 +1,13 @@
+/**
+ * @file
+ * SNMP support API for implementing netifs and statitics for MIB2
+ */
+
/*
- * Copyright (c) 2001, 2002 Leon Woestenberg <leon.woestenberg@axon.tv>
- * Copyright (c) 2001, 2002 Axon Digital Design B.V., The Netherlands.
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
* All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -12,39 +16,46 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
- * Author: Leon Woestenberg <leon.woestenberg@axon.tv>
+ *
+ * Author: Dirk Ziegelmeier <dziegel@gmx.de>
*
*/
-#ifndef __LWIP_SNMP_H__
-#define __LWIP_SNMP_H__
+#ifndef LWIP_HDR_SNMP_H
+#define LWIP_HDR_SNMP_H
#include "lwip/opt.h"
+#include "lwip/ip_addr.h"
#ifdef __cplusplus
extern "C" {
#endif
-#include "lwip/ip_addr.h"
-
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 {
@@ -82,286 +93,121 @@ enum snmp_ifType {
snmp_ifType_frame_relay
};
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-/** SNMP "sysuptime" Interval */
-#define SNMP_SYSUPTIME_INTERVAL 10
-
-/** fixed maximum length for object identifier type */
-#define LWIP_SNMP_OBJ_ID_LEN 32
+/** 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
-/** internal object identifier representation */
-struct snmp_obj_id
-{
- u8_t len;
- s32_t id[LWIP_SNMP_OBJ_ID_LEN];
-};
+/**
+ * @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)
-/* system */
-void snmp_set_sysdesr(u8_t* str, u8_t* len);
-void snmp_set_sysobjid(struct snmp_obj_id *oid);
-void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid);
-void snmp_inc_sysuptime(void);
-void snmp_add_sysuptime(u32_t value);
-void snmp_get_sysuptime(u32_t *value);
-void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen);
-void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen);
-void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen);
+/**
+ * @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 snmp_add_ifinoctets(struct netif *ni, u32_t value);
-void snmp_inc_ifinucastpkts(struct netif *ni);
-void snmp_inc_ifinnucastpkts(struct netif *ni);
-void snmp_inc_ifindiscards(struct netif *ni);
-void snmp_add_ifoutoctets(struct netif *ni, u32_t value);
-void snmp_inc_ifoutucastpkts(struct netif *ni);
-void snmp_inc_ifoutnucastpkts(struct netif *ni);
-void snmp_inc_ifoutdiscards(struct netif *ni);
-void snmp_inc_iflist(void);
-void snmp_dec_iflist(void);
+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 snmp_insert_arpidx_tree(struct netif *ni, ip_addr_t *ip);
-void snmp_delete_arpidx_tree(struct netif *ni, ip_addr_t *ip);
+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 */
-void snmp_inc_ipinreceives(void);
-void snmp_inc_ipinhdrerrors(void);
-void snmp_inc_ipinaddrerrors(void);
-void snmp_inc_ipforwdatagrams(void);
-void snmp_inc_ipinunknownprotos(void);
-void snmp_inc_ipindiscards(void);
-void snmp_inc_ipindelivers(void);
-void snmp_inc_ipoutrequests(void);
-void snmp_inc_ipoutdiscards(void);
-void snmp_inc_ipoutnoroutes(void);
-void snmp_inc_ipreasmreqds(void);
-void snmp_inc_ipreasmoks(void);
-void snmp_inc_ipreasmfails(void);
-void snmp_inc_ipfragoks(void);
-void snmp_inc_ipfragfails(void);
-void snmp_inc_ipfragcreates(void);
-void snmp_inc_iproutingdiscards(void);
-void snmp_insert_ipaddridx_tree(struct netif *ni);
-void snmp_delete_ipaddridx_tree(struct netif *ni);
-void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni);
-void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni);
-
-/* ICMP */
-void snmp_inc_icmpinmsgs(void);
-void snmp_inc_icmpinerrors(void);
-void snmp_inc_icmpindestunreachs(void);
-void snmp_inc_icmpintimeexcds(void);
-void snmp_inc_icmpinparmprobs(void);
-void snmp_inc_icmpinsrcquenchs(void);
-void snmp_inc_icmpinredirects(void);
-void snmp_inc_icmpinechos(void);
-void snmp_inc_icmpinechoreps(void);
-void snmp_inc_icmpintimestamps(void);
-void snmp_inc_icmpintimestampreps(void);
-void snmp_inc_icmpinaddrmasks(void);
-void snmp_inc_icmpinaddrmaskreps(void);
-void snmp_inc_icmpoutmsgs(void);
-void snmp_inc_icmpouterrors(void);
-void snmp_inc_icmpoutdestunreachs(void);
-void snmp_inc_icmpouttimeexcds(void);
-void snmp_inc_icmpoutparmprobs(void);
-void snmp_inc_icmpoutsrcquenchs(void);
-void snmp_inc_icmpoutredirects(void);
-void snmp_inc_icmpoutechos(void);
-void snmp_inc_icmpoutechoreps(void);
-void snmp_inc_icmpouttimestamps(void);
-void snmp_inc_icmpouttimestampreps(void);
-void snmp_inc_icmpoutaddrmasks(void);
-void snmp_inc_icmpoutaddrmaskreps(void);
-
-/* TCP */
-void snmp_inc_tcpactiveopens(void);
-void snmp_inc_tcppassiveopens(void);
-void snmp_inc_tcpattemptfails(void);
-void snmp_inc_tcpestabresets(void);
-void snmp_inc_tcpinsegs(void);
-void snmp_inc_tcpoutsegs(void);
-void snmp_inc_tcpretranssegs(void);
-void snmp_inc_tcpinerrs(void);
-void snmp_inc_tcpoutrsts(void);
+#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 */
-void snmp_inc_udpindatagrams(void);
-void snmp_inc_udpnoports(void);
-void snmp_inc_udpinerrors(void);
-void snmp_inc_udpoutdatagrams(void);
-void snmp_insert_udpidx_tree(struct udp_pcb *pcb);
-void snmp_delete_udpidx_tree(struct udp_pcb *pcb);
+#if LWIP_UDP
+void mib2_udp_bind(struct udp_pcb *pcb);
+void mib2_udp_unbind(struct udp_pcb *pcb);
+#endif /* LWIP_UDP */
-/* SNMP */
-void snmp_inc_snmpinpkts(void);
-void snmp_inc_snmpoutpkts(void);
-void snmp_inc_snmpinbadversions(void);
-void snmp_inc_snmpinbadcommunitynames(void);
-void snmp_inc_snmpinbadcommunityuses(void);
-void snmp_inc_snmpinasnparseerrs(void);
-void snmp_inc_snmpintoobigs(void);
-void snmp_inc_snmpinnosuchnames(void);
-void snmp_inc_snmpinbadvalues(void);
-void snmp_inc_snmpinreadonlys(void);
-void snmp_inc_snmpingenerrs(void);
-void snmp_add_snmpintotalreqvars(u8_t value);
-void snmp_add_snmpintotalsetvars(u8_t value);
-void snmp_inc_snmpingetrequests(void);
-void snmp_inc_snmpingetnexts(void);
-void snmp_inc_snmpinsetrequests(void);
-void snmp_inc_snmpingetresponses(void);
-void snmp_inc_snmpintraps(void);
-void snmp_inc_snmpouttoobigs(void);
-void snmp_inc_snmpoutnosuchnames(void);
-void snmp_inc_snmpoutbadvalues(void);
-void snmp_inc_snmpoutgenerrs(void);
-void snmp_inc_snmpoutgetrequests(void);
-void snmp_inc_snmpoutgetnexts(void);
-void snmp_inc_snmpoutsetrequests(void);
-void snmp_inc_snmpoutgetresponses(void);
-void snmp_inc_snmpouttraps(void);
-void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid);
-void snmp_set_snmpenableauthentraps(u8_t *value);
-void snmp_get_snmpenableauthentraps(u8_t *value);
-
-/* LWIP_SNMP support not available */
+#else /* LWIP_MIB2_CALLBACKS */
+/* LWIP_MIB2_CALLBACKS support not available */
/* define everything to be empty */
-#else
-
-/* system */
-#define snmp_set_sysdesr(str, len)
-#define snmp_set_sysobjid(oid);
-#define snmp_get_sysobjid_ptr(oid)
-#define snmp_inc_sysuptime()
-#define snmp_add_sysuptime(value)
-#define snmp_get_sysuptime(value)
-#define snmp_set_syscontact(ocstr, ocstrlen);
-#define snmp_set_sysname(ocstr, ocstrlen);
-#define snmp_set_syslocation(ocstr, ocstrlen);
/* network interface */
-#define snmp_add_ifinoctets(ni,value)
-#define snmp_inc_ifinucastpkts(ni)
-#define snmp_inc_ifinnucastpkts(ni)
-#define snmp_inc_ifindiscards(ni)
-#define snmp_add_ifoutoctets(ni,value)
-#define snmp_inc_ifoutucastpkts(ni)
-#define snmp_inc_ifoutnucastpkts(ni)
-#define snmp_inc_ifoutdiscards(ni)
-#define snmp_inc_iflist()
-#define snmp_dec_iflist()
+#define mib2_netif_added(ni)
+#define mib2_netif_removed(ni)
/* ARP */
-#define snmp_insert_arpidx_tree(ni,ip)
-#define snmp_delete_arpidx_tree(ni,ip)
+#define mib2_add_arp_entry(ni,ip)
+#define mib2_remove_arp_entry(ni,ip)
/* IP */
-#define snmp_inc_ipinreceives()
-#define snmp_inc_ipinhdrerrors()
-#define snmp_inc_ipinaddrerrors()
-#define snmp_inc_ipforwdatagrams()
-#define snmp_inc_ipinunknownprotos()
-#define snmp_inc_ipindiscards()
-#define snmp_inc_ipindelivers()
-#define snmp_inc_ipoutrequests()
-#define snmp_inc_ipoutdiscards()
-#define snmp_inc_ipoutnoroutes()
-#define snmp_inc_ipreasmreqds()
-#define snmp_inc_ipreasmoks()
-#define snmp_inc_ipreasmfails()
-#define snmp_inc_ipfragoks()
-#define snmp_inc_ipfragfails()
-#define snmp_inc_ipfragcreates()
-#define snmp_inc_iproutingdiscards()
-#define snmp_insert_ipaddridx_tree(ni)
-#define snmp_delete_ipaddridx_tree(ni)
-#define snmp_insert_iprteidx_tree(dflt, ni)
-#define snmp_delete_iprteidx_tree(dflt, ni)
-
-/* ICMP */
-#define snmp_inc_icmpinmsgs()
-#define snmp_inc_icmpinerrors()
-#define snmp_inc_icmpindestunreachs()
-#define snmp_inc_icmpintimeexcds()
-#define snmp_inc_icmpinparmprobs()
-#define snmp_inc_icmpinsrcquenchs()
-#define snmp_inc_icmpinredirects()
-#define snmp_inc_icmpinechos()
-#define snmp_inc_icmpinechoreps()
-#define snmp_inc_icmpintimestamps()
-#define snmp_inc_icmpintimestampreps()
-#define snmp_inc_icmpinaddrmasks()
-#define snmp_inc_icmpinaddrmaskreps()
-#define snmp_inc_icmpoutmsgs()
-#define snmp_inc_icmpouterrors()
-#define snmp_inc_icmpoutdestunreachs()
-#define snmp_inc_icmpouttimeexcds()
-#define snmp_inc_icmpoutparmprobs()
-#define snmp_inc_icmpoutsrcquenchs()
-#define snmp_inc_icmpoutredirects()
-#define snmp_inc_icmpoutechos()
-#define snmp_inc_icmpoutechoreps()
-#define snmp_inc_icmpouttimestamps()
-#define snmp_inc_icmpouttimestampreps()
-#define snmp_inc_icmpoutaddrmasks()
-#define snmp_inc_icmpoutaddrmaskreps()
-/* TCP */
-#define snmp_inc_tcpactiveopens()
-#define snmp_inc_tcppassiveopens()
-#define snmp_inc_tcpattemptfails()
-#define snmp_inc_tcpestabresets()
-#define snmp_inc_tcpinsegs()
-#define snmp_inc_tcpoutsegs()
-#define snmp_inc_tcpretranssegs()
-#define snmp_inc_tcpinerrs()
-#define snmp_inc_tcpoutrsts()
+#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 snmp_inc_udpindatagrams()
-#define snmp_inc_udpnoports()
-#define snmp_inc_udpinerrors()
-#define snmp_inc_udpoutdatagrams()
-#define snmp_insert_udpidx_tree(pcb)
-#define snmp_delete_udpidx_tree(pcb)
-
-/* SNMP */
-#define snmp_inc_snmpinpkts()
-#define snmp_inc_snmpoutpkts()
-#define snmp_inc_snmpinbadversions()
-#define snmp_inc_snmpinbadcommunitynames()
-#define snmp_inc_snmpinbadcommunityuses()
-#define snmp_inc_snmpinasnparseerrs()
-#define snmp_inc_snmpintoobigs()
-#define snmp_inc_snmpinnosuchnames()
-#define snmp_inc_snmpinbadvalues()
-#define snmp_inc_snmpinreadonlys()
-#define snmp_inc_snmpingenerrs()
-#define snmp_add_snmpintotalreqvars(value)
-#define snmp_add_snmpintotalsetvars(value)
-#define snmp_inc_snmpingetrequests()
-#define snmp_inc_snmpingetnexts()
-#define snmp_inc_snmpinsetrequests()
-#define snmp_inc_snmpingetresponses()
-#define snmp_inc_snmpintraps()
-#define snmp_inc_snmpouttoobigs()
-#define snmp_inc_snmpoutnosuchnames()
-#define snmp_inc_snmpoutbadvalues()
-#define snmp_inc_snmpoutgenerrs()
-#define snmp_inc_snmpoutgetrequests()
-#define snmp_inc_snmpoutgetnexts()
-#define snmp_inc_snmpoutsetrequests()
-#define snmp_inc_snmpoutgetresponses()
-#define snmp_inc_snmpouttraps()
-#define snmp_get_snmpgrpid_ptr(oid)
-#define snmp_set_snmpenableauthentraps(value)
-#define snmp_get_snmpenableauthentraps(value)
-
-#endif /* LWIP_SNMP */
+#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_SNMP_H__ */
+#endif /* LWIP_HDR_SNMP_H */
diff --git a/lwip/src/include/lwip/snmp_asn1.h b/lwip/src/include/lwip/snmp_asn1.h
deleted file mode 100644
index 605fa3f..0000000
--- a/lwip/src/include/lwip/snmp_asn1.h
+++ /dev/null
@@ -1,101 +0,0 @@
-/**
- * @file
- * Abstract Syntax Notation One (ISO 8824, 8825) codec.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#ifndef __LWIP_SNMP_ASN1_H__
-#define __LWIP_SNMP_ASN1_H__
-
-#include "lwip/opt.h"
-#include "lwip/err.h"
-#include "lwip/pbuf.h"
-#include "lwip/snmp.h"
-
-#if LWIP_SNMP
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define SNMP_ASN1_UNIV (0) /* (!0x80 | !0x40) */
-#define SNMP_ASN1_APPLIC (0x40) /* (!0x80 | 0x40) */
-#define SNMP_ASN1_CONTXT (0x80) /* ( 0x80 | !0x40) */
-
-#define SNMP_ASN1_CONSTR (0x20) /* ( 0x20) */
-#define SNMP_ASN1_PRIMIT (0) /* (!0x20) */
-
-/* universal tags */
-#define SNMP_ASN1_INTEG 2
-#define SNMP_ASN1_OC_STR 4
-#define SNMP_ASN1_NUL 5
-#define SNMP_ASN1_OBJ_ID 6
-#define SNMP_ASN1_SEQ 16
-
-/* application specific (SNMP) tags */
-#define SNMP_ASN1_IPADDR 0 /* octet string size(4) */
-#define SNMP_ASN1_COUNTER 1 /* u32_t */
-#define SNMP_ASN1_GAUGE 2 /* u32_t */
-#define SNMP_ASN1_TIMETICKS 3 /* u32_t */
-#define SNMP_ASN1_OPAQUE 4 /* octet string */
-
-/* context specific (SNMP) tags */
-#define SNMP_ASN1_PDU_GET_REQ 0
-#define SNMP_ASN1_PDU_GET_NEXT_REQ 1
-#define SNMP_ASN1_PDU_GET_RESP 2
-#define SNMP_ASN1_PDU_SET_REQ 3
-#define SNMP_ASN1_PDU_TRAP 4
-
-err_t snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type);
-err_t snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length);
-err_t snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value);
-err_t snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value);
-err_t snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid);
-err_t snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw);
-
-void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed);
-void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed);
-void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed);
-void snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed);
-err_t snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type);
-err_t snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length);
-err_t snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value);
-err_t snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value);
-err_t snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident);
-err_t snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LWIP_SNMP */
-
-#endif /* __LWIP_SNMP_ASN1_H__ */
diff --git a/lwip/src/include/lwip/snmp_msg.h b/lwip/src/include/lwip/snmp_msg.h
deleted file mode 100644
index 1183e3a..0000000
--- a/lwip/src/include/lwip/snmp_msg.h
+++ /dev/null
@@ -1,315 +0,0 @@
-/**
- * @file
- * SNMP Agent message handling structures.
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#ifndef __LWIP_SNMP_MSG_H__
-#define __LWIP_SNMP_MSG_H__
-
-#include "lwip/opt.h"
-#include "lwip/snmp.h"
-#include "lwip/snmp_structs.h"
-#include "lwip/ip_addr.h"
-#include "lwip/err.h"
-
-#if LWIP_SNMP
-
-#if SNMP_PRIVATE_MIB
-/* When using a private MIB, you have to create a file 'private_mib.h' that contains
- * a 'struct mib_array_node mib_private' which contains your MIB. */
-#include "private_mib.h"
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* The listen port of the SNMP agent. Clients have to make their requests to
- this port. Most standard clients won't work if you change this! */
-#ifndef SNMP_IN_PORT
-#define SNMP_IN_PORT 161
-#endif
-/* The remote port the SNMP agent sends traps to. Most standard trap sinks won't
- work if you change this! */
-#ifndef SNMP_TRAP_PORT
-#define SNMP_TRAP_PORT 162
-#endif
-
-#define SNMP_ES_NOERROR 0
-#define SNMP_ES_TOOBIG 1
-#define SNMP_ES_NOSUCHNAME 2
-#define SNMP_ES_BADVALUE 3
-#define SNMP_ES_READONLY 4
-#define SNMP_ES_GENERROR 5
-
-#define SNMP_GENTRAP_COLDSTART 0
-#define SNMP_GENTRAP_WARMSTART 1
-#define SNMP_GENTRAP_AUTHFAIL 4
-#define SNMP_GENTRAP_ENTERPRISESPC 6
-
-struct snmp_varbind
-{
- /* next pointer, NULL for last in list */
- struct snmp_varbind *next;
- /* previous pointer, NULL for first in list */
- struct snmp_varbind *prev;
-
- /* object identifier length (in s32_t) */
- u8_t ident_len;
- /* object identifier array */
- s32_t *ident;
-
- /* object value ASN1 type */
- u8_t value_type;
- /* object value length (in u8_t) */
- u8_t value_len;
- /* object value */
- void *value;
-
- /* encoding varbind seq length length */
- u8_t seqlenlen;
- /* encoding object identifier length length */
- u8_t olenlen;
- /* encoding object value length length */
- u8_t vlenlen;
- /* encoding varbind seq length */
- u16_t seqlen;
- /* encoding object identifier length */
- u16_t olen;
- /* encoding object value length */
- u16_t vlen;
-};
-
-struct snmp_varbind_root
-{
- struct snmp_varbind *head;
- struct snmp_varbind *tail;
- /* number of variable bindings in list */
- u8_t count;
- /* encoding varbind-list seq length length */
- u8_t seqlenlen;
- /* encoding varbind-list seq length */
- u16_t seqlen;
-};
-
-/** output response message header length fields */
-struct snmp_resp_header_lengths
-{
- /* encoding error-index length length */
- u8_t erridxlenlen;
- /* encoding error-status length length */
- u8_t errstatlenlen;
- /* encoding request id length length */
- u8_t ridlenlen;
- /* encoding pdu length length */
- u8_t pdulenlen;
- /* encoding community length length */
- u8_t comlenlen;
- /* encoding version length length */
- u8_t verlenlen;
- /* encoding sequence length length */
- u8_t seqlenlen;
-
- /* encoding error-index length */
- u16_t erridxlen;
- /* encoding error-status length */
- u16_t errstatlen;
- /* encoding request id length */
- u16_t ridlen;
- /* encoding pdu length */
- u16_t pdulen;
- /* encoding community length */
- u16_t comlen;
- /* encoding version length */
- u16_t verlen;
- /* encoding sequence length */
- u16_t seqlen;
-};
-
-/** output response message header length fields */
-struct snmp_trap_header_lengths
-{
- /* encoding timestamp length length */
- u8_t tslenlen;
- /* encoding specific-trap length length */
- u8_t strplenlen;
- /* encoding generic-trap length length */
- u8_t gtrplenlen;
- /* encoding agent-addr length length */
- u8_t aaddrlenlen;
- /* encoding enterprise-id length length */
- u8_t eidlenlen;
- /* encoding pdu length length */
- u8_t pdulenlen;
- /* encoding community length length */
- u8_t comlenlen;
- /* encoding version length length */
- u8_t verlenlen;
- /* encoding sequence length length */
- u8_t seqlenlen;
-
- /* encoding timestamp length */
- u16_t tslen;
- /* encoding specific-trap length */
- u16_t strplen;
- /* encoding generic-trap length */
- u16_t gtrplen;
- /* encoding agent-addr length */
- u16_t aaddrlen;
- /* encoding enterprise-id length */
- u16_t eidlen;
- /* encoding pdu length */
- u16_t pdulen;
- /* encoding community length */
- u16_t comlen;
- /* encoding version length */
- u16_t verlen;
- /* encoding sequence length */
- u16_t seqlen;
-};
-
-/* Accepting new SNMP messages. */
-#define SNMP_MSG_EMPTY 0
-/* Search for matching object for variable binding. */
-#define SNMP_MSG_SEARCH_OBJ 1
-/* Perform SNMP operation on in-memory object.
- Pass-through states, for symmetry only. */
-#define SNMP_MSG_INTERNAL_GET_OBJDEF 2
-#define SNMP_MSG_INTERNAL_GET_VALUE 3
-#define SNMP_MSG_INTERNAL_SET_TEST 4
-#define SNMP_MSG_INTERNAL_GET_OBJDEF_S 5
-#define SNMP_MSG_INTERNAL_SET_VALUE 6
-/* Perform SNMP operation on object located externally.
- In theory this could be used for building a proxy agent.
- Practical use is for an enterprise spc. app. gateway. */
-#define SNMP_MSG_EXTERNAL_GET_OBJDEF 7
-#define SNMP_MSG_EXTERNAL_GET_VALUE 8
-#define SNMP_MSG_EXTERNAL_SET_TEST 9
-#define SNMP_MSG_EXTERNAL_GET_OBJDEF_S 10
-#define SNMP_MSG_EXTERNAL_SET_VALUE 11
-
-#define SNMP_COMMUNITY_STR_LEN 64
-struct snmp_msg_pstat
-{
- /* lwIP local port (161) binding */
- struct udp_pcb *pcb;
- /* source IP address */
- ip_addr_t sip;
- /* source UDP port */
- u16_t sp;
- /* request type */
- u8_t rt;
- /* request ID */
- s32_t rid;
- /* error status */
- s32_t error_status;
- /* error index */
- s32_t error_index;
- /* community name (zero terminated) */
- u8_t community[SNMP_COMMUNITY_STR_LEN + 1];
- /* community string length (exclusive zero term) */
- u8_t com_strlen;
- /* one out of MSG_EMPTY, MSG_DEMUX, MSG_INTERNAL, MSG_EXTERNAL_x */
- u8_t state;
- /* saved arguments for MSG_EXTERNAL_x */
- struct mib_external_node *ext_mib_node;
- struct snmp_name_ptr ext_name_ptr;
- struct obj_def ext_object_def;
- struct snmp_obj_id ext_oid;
- /* index into input variable binding list */
- u8_t vb_idx;
- /* ptr into input variable binding list */
- struct snmp_varbind *vb_ptr;
- /* list of variable bindings from input */
- struct snmp_varbind_root invb;
- /* list of variable bindings to output */
- struct snmp_varbind_root outvb;
- /* output response lengths used in ASN encoding */
- struct snmp_resp_header_lengths rhl;
-};
-
-struct snmp_msg_trap
-{
- /* lwIP local port (161) binding */
- struct udp_pcb *pcb;
- /* destination IP address in network order */
- ip_addr_t dip;
-
- /* source enterprise ID (sysObjectID) */
- struct snmp_obj_id *enterprise;
- /* source IP address, raw network order format */
- u8_t sip_raw[4];
- /* generic trap code */
- u32_t gen_trap;
- /* specific trap code */
- u32_t spc_trap;
- /* timestamp */
- u32_t ts;
- /* list of variable bindings to output */
- struct snmp_varbind_root outvb;
- /* output trap lengths used in ASN encoding */
- struct snmp_trap_header_lengths thl;
-};
-
-/** Agent Version constant, 0 = v1 oddity */
-extern const s32_t snmp_version;
-/** Agent default "public" community string */
-extern const char snmp_publiccommunity[7];
-
-extern struct snmp_msg_trap trap_msg;
-
-/** Agent setup, start listening to port 161. */
-void snmp_init(void);
-void snmp_trap_dst_enable(u8_t dst_idx, u8_t enable);
-void snmp_trap_dst_ip_set(u8_t dst_idx, ip_addr_t *dst);
-
-/** Varbind-list functions. */
-struct snmp_varbind* snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len);
-void snmp_varbind_free(struct snmp_varbind *vb);
-void snmp_varbind_list_free(struct snmp_varbind_root *root);
-void snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb);
-struct snmp_varbind* snmp_varbind_tail_remove(struct snmp_varbind_root *root);
-
-/** Handle an internal (recv) or external (private response) event. */
-void snmp_msg_event(u8_t request_id);
-err_t snmp_send_response(struct snmp_msg_pstat *m_stat);
-err_t snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap);
-void snmp_coldstart_trap(void);
-void snmp_authfail_trap(void);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LWIP_SNMP */
-
-#endif /* __LWIP_SNMP_MSG_H__ */
diff --git a/lwip/src/include/lwip/snmp_structs.h b/lwip/src/include/lwip/snmp_structs.h
deleted file mode 100644
index 0d3b46a..0000000
--- a/lwip/src/include/lwip/snmp_structs.h
+++ /dev/null
@@ -1,268 +0,0 @@
-/**
- * @file
- * Generic MIB tree structures.
- *
- * @todo namespace prefixes
- */
-
-/*
- * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * Author: Christiaan Simons <christiaan.simons@axon.tv>
- */
-
-#ifndef __LWIP_SNMP_STRUCTS_H__
-#define __LWIP_SNMP_STRUCTS_H__
-
-#include "lwip/opt.h"
-
-#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/snmp.h"
-
-#if SNMP_PRIVATE_MIB
-/* When using a private MIB, you have to create a file 'private_mib.h' that contains
- * a 'struct mib_array_node mib_private' which contains your MIB. */
-#include "private_mib.h"
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-/* MIB object instance */
-#define MIB_OBJECT_NONE 0
-#define MIB_OBJECT_SCALAR 1
-#define MIB_OBJECT_TAB 2
-
-/* MIB access types */
-#define MIB_ACCESS_READ 1
-#define MIB_ACCESS_WRITE 2
-
-/* MIB object access */
-#define MIB_OBJECT_READ_ONLY MIB_ACCESS_READ
-#define MIB_OBJECT_READ_WRITE (MIB_ACCESS_READ | MIB_ACCESS_WRITE)
-#define MIB_OBJECT_WRITE_ONLY MIB_ACCESS_WRITE
-#define MIB_OBJECT_NOT_ACCESSIBLE 0
-
-/** object definition returned by (get_object_def)() */
-struct obj_def
-{
- /* MIB_OBJECT_NONE (0), MIB_OBJECT_SCALAR (1), MIB_OBJECT_TAB (2) */
- u8_t instance;
- /* 0 read-only, 1 read-write, 2 write-only, 3 not-accessible */
- u8_t access;
- /* ASN type for this object */
- u8_t asn_type;
- /* value length (host length) */
- u16_t v_len;
- /* length of instance part of supplied object identifier */
- u8_t id_inst_len;
- /* instance part of supplied object identifier */
- s32_t *id_inst_ptr;
-};
-
-struct snmp_name_ptr
-{
- u8_t ident_len;
- s32_t *ident;
-};
-
-/** MIB const scalar (.0) node */
-#define MIB_NODE_SC 0x01
-/** MIB const array node */
-#define MIB_NODE_AR 0x02
-/** MIB array node (mem_malloced from RAM) */
-#define MIB_NODE_RA 0x03
-/** MIB list root node (mem_malloced from RAM) */
-#define MIB_NODE_LR 0x04
-/** MIB node for external objects */
-#define MIB_NODE_EX 0x05
-
-/** node "base class" layout, the mandatory fields for a node */
-struct mib_node
-{
- /** returns struct obj_def for the given object identifier */
- void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
- /** returns object value for the given object identifier,
- @note the caller must allocate at least len bytes for the value */
- void (*get_value)(struct obj_def *od, u16_t len, void *value);
- /** tests length and/or range BEFORE setting */
- u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
- /** sets object value, only to be called when set_test() */
- void (*set_value)(struct obj_def *od, u16_t len, void *value);
- /** One out of MIB_NODE_AR, MIB_NODE_LR or MIB_NODE_EX */
- u8_t node_type;
- /* array or max list length */
- u16_t maxlength;
-};
-
-/** derived node for scalars .0 index */
-typedef struct mib_node mib_scalar_node;
-
-/** derived node, points to a fixed size const array
- of sub-identifiers plus a 'child' pointer */
-struct mib_array_node
-{
- /* inherited "base class" members */
- void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
- void (*get_value)(struct obj_def *od, u16_t len, void *value);
- u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
- void (*set_value)(struct obj_def *od, u16_t len, void *value);
-
- u8_t node_type;
- u16_t maxlength;
-
- /* additional struct members */
- const s32_t *objid;
- struct mib_node* const *nptr;
-};
-
-/** derived node, points to a fixed size mem_malloced array
- of sub-identifiers plus a 'child' pointer */
-struct mib_ram_array_node
-{
- /* inherited "base class" members */
- void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
- void (*get_value)(struct obj_def *od, u16_t len, void *value);
- u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
- void (*set_value)(struct obj_def *od, u16_t len, void *value);
-
- u8_t node_type;
- u16_t maxlength;
-
- /* aditional struct members */
- s32_t *objid;
- struct mib_node **nptr;
-};
-
-struct mib_list_node
-{
- struct mib_list_node *prev;
- struct mib_list_node *next;
- s32_t objid;
- struct mib_node *nptr;
-};
-
-/** derived node, points to a doubly linked list
- of sub-identifiers plus a 'child' pointer */
-struct mib_list_rootnode
-{
- /* inherited "base class" members */
- void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
- void (*get_value)(struct obj_def *od, u16_t len, void *value);
- u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
- void (*set_value)(struct obj_def *od, u16_t len, void *value);
-
- u8_t node_type;
- u16_t maxlength;
-
- /* additional struct members */
- struct mib_list_node *head;
- struct mib_list_node *tail;
- /* counts list nodes in list */
- u16_t count;
-};
-
-/** derived node, has access functions for mib object in external memory or device
- using 'tree_level' and 'idx', with a range 0 .. (level_length() - 1) */
-struct mib_external_node
-{
- /* inherited "base class" members */
- void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
- void (*get_value)(struct obj_def *od, u16_t len, void *value);
- u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
- void (*set_value)(struct obj_def *od, u16_t len, void *value);
-
- u8_t node_type;
- u16_t maxlength;
-
- /* additional struct members */
- /** points to an external (in memory) record of some sort of addressing
- information, passed to and interpreted by the funtions below */
- void* addr_inf;
- /** tree levels under this node */
- u8_t tree_levels;
- /** number of objects at this level */
- u16_t (*level_length)(void* addr_inf, u8_t level);
- /** compares object sub identifier with external id
- return zero when equal, nonzero when unequal */
- s32_t (*ident_cmp)(void* addr_inf, u8_t level, u16_t idx, s32_t sub_id);
- void (*get_objid)(void* addr_inf, u8_t level, u16_t idx, s32_t *sub_id);
-
- /** async Questions */
- void (*get_object_def_q)(void* addr_inf, u8_t rid, u8_t ident_len, s32_t *ident);
- void (*get_value_q)(u8_t rid, struct obj_def *od);
- void (*set_test_q)(u8_t rid, struct obj_def *od);
- void (*set_value_q)(u8_t rid, struct obj_def *od, u16_t len, void *value);
- /** async Answers */
- void (*get_object_def_a)(u8_t rid, u8_t ident_len, s32_t *ident, struct obj_def *od);
- void (*get_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value);
- u8_t (*set_test_a)(u8_t rid, struct obj_def *od, u16_t len, void *value);
- void (*set_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value);
- /** async Panic Close (agent returns error reply,
- e.g. used for external transaction cleanup) */
- void (*get_object_def_pc)(u8_t rid, u8_t ident_len, s32_t *ident);
- void (*get_value_pc)(u8_t rid, struct obj_def *od);
- void (*set_test_pc)(u8_t rid, struct obj_def *od);
- void (*set_value_pc)(u8_t rid, struct obj_def *od);
-};
-
-/** export MIB tree from mib2.c */
-extern const struct mib_array_node internet;
-
-/** dummy function pointers for non-leaf MIB nodes from mib2.c */
-void noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
-void noleafs_get_value(struct obj_def *od, u16_t len, void *value);
-u8_t noleafs_set_test(struct obj_def *od, u16_t len, void *value);
-void noleafs_set_value(struct obj_def *od, u16_t len, void *value);
-
-void snmp_oidtoip(s32_t *ident, ip_addr_t *ip);
-void snmp_iptooid(ip_addr_t *ip, s32_t *ident);
-void snmp_ifindextonetif(s32_t ifindex, struct netif **netif);
-void snmp_netiftoifindex(struct netif *netif, s32_t *ifidx);
-
-struct mib_list_node* snmp_mib_ln_alloc(s32_t id);
-void snmp_mib_ln_free(struct mib_list_node *ln);
-struct mib_list_rootnode* snmp_mib_lrn_alloc(void);
-void snmp_mib_lrn_free(struct mib_list_rootnode *lrn);
-
-s8_t snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn);
-s8_t snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn);
-struct mib_list_rootnode *snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n);
-
-struct mib_node* snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np);
-struct mib_node* snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret);
-u8_t snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident);
-u8_t snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret);
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LWIP_SNMP */
-
-#endif /* __LWIP_SNMP_STRUCTS_H__ */
diff --git a/lwip/src/include/lwip/sockets.h b/lwip/src/include/lwip/sockets.h
index 7346137..d51cfb6 100644
--- a/lwip/src/include/lwip/sockets.h
+++ b/lwip/src/include/lwip/sockets.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,70 +16,90 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_SOCKETS_H__
-#define __LWIP_SOCKETS_H__
+#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 <stddef.h> /* for size_t */
-
#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/err.h"
#include "lwip/inet.h"
-#include "lwip/inet6.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;
- u8_t sin_family;
- u16_t sin_port;
- struct in_addr sin_addr;
+ 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];
+ char sin_zero[SIN_ZERO_LEN];
};
+#endif /* LWIP_IPV4 */
#if LWIP_IPV6
struct sockaddr_in6 {
- u8_t sin6_len; /* length of this structure */
- u8_t sin6_family; /* AF_INET6 */
- u16_t sin6_port; /* Transport layer port # */
- u32_t sin6_flowinfo; /* IPv6 flow information */
- struct in6_addr sin6_addr; /* IPv6 address */
+ 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;
- u8_t sa_family;
+ 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
- u8_t sa_data[22];
-#else /* LWIP_IPV6 */
- u8_t sa_data[14];
+ u32_t s2_data3[3];
#endif /* LWIP_IPV6 */
};
@@ -84,6 +109,79 @@ struct sockaddr {
typedef u32_t socklen_t;
#endif
+#if !defined IOV_MAX
+#define IOV_MAX 0xFFFF
+#elif IOV_MAX > 0xFFFF
+#error "IOV_MAX larger than supported by LwIP"
+#endif /* IOV_MAX */
+
+#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;
+};
+
+/* struct msghdr->msg_flags bit field values */
+#define MSG_TRUNC 0x04
+#define MSG_CTRUNC 0x08
+
+/* RFC 3542, Section 20: Ancillary Data */
+struct cmsghdr {
+ socklen_t cmsg_len; /* number of bytes, including header */
+ int cmsg_level; /* originating protocol */
+ int cmsg_type; /* protocol-specific type */
+};
+/* Data section follows header and possible padding, typically referred to as
+ unsigned char cmsg_data[]; */
+
+/* cmsg header/data alignment. NOTE: we align to native word size (double word
+size on 16-bit arch) so structures are not placed at an unaligned address.
+16-bit arch needs double word to ensure 32-bit alignment because socklen_t
+could be 32 bits. If we ever have cmsg data with a 64-bit variable, alignment
+will need to increase long long */
+#define ALIGN_H(size) (((size) + sizeof(long) - 1U) & ~(sizeof(long)-1U))
+#define ALIGN_D(size) ALIGN_H(size)
+
+#define CMSG_FIRSTHDR(mhdr) \
+ ((mhdr)->msg_controllen >= sizeof(struct cmsghdr) ? \
+ (struct cmsghdr *)(mhdr)->msg_control : \
+ (struct cmsghdr *)NULL)
+
+#define CMSG_NXTHDR(mhdr, cmsg) \
+ (((cmsg) == NULL) ? CMSG_FIRSTHDR(mhdr) : \
+ (((u8_t *)(cmsg) + ALIGN_H((cmsg)->cmsg_len) \
+ + ALIGN_D(sizeof(struct cmsghdr)) > \
+ (u8_t *)((mhdr)->msg_control) + (mhdr)->msg_controllen) ? \
+ (struct cmsghdr *)NULL : \
+ (struct cmsghdr *)((void*)((u8_t *)(cmsg) + \
+ ALIGN_H((cmsg)->cmsg_len)))))
+
+#define CMSG_DATA(cmsg) ((void*)((u8_t *)(cmsg) + \
+ ALIGN_D(sizeof(struct cmsghdr))))
+
+#define CMSG_SPACE(length) (ALIGN_D(sizeof(struct cmsghdr)) + \
+ ALIGN_H(length))
+
+#define CMSG_LEN(length) (ALIGN_D(sizeof(struct cmsghdr)) + \
+ length)
+
+/* Set socket options argument */
+#define IFNAMSIZ NETIF_NAMESIZE
+struct ifreq {
+ char ifr_name[IFNAMSIZ]; /* Interface name */
+};
+
/* Socket protocol types (TCP/UDP/RAW) */
#define SOCK_STREAM 1
#define SOCK_DGRAM 2
@@ -92,40 +190,40 @@ typedef u32_t socklen_t;
/*
* Option flags per-socket. These must match the SOF_ flags in ip.h (checked in init.c)
*/
-#define SO_DEBUG 0x0001 /* Unimplemented: turn on debugging info recording */
-#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */
-#define SO_REUSEADDR 0x0004 /* Allow local address reuse */
-#define SO_KEEPALIVE 0x0008 /* keep connections alive */
-#define SO_DONTROUTE 0x0010 /* Unimplemented: just use interface addresses */
-#define SO_BROADCAST 0x0020 /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */
-#define SO_USELOOPBACK 0x0040 /* Unimplemented: bypass hardware when possible */
-#define SO_LINGER 0x0080 /* linger on close if data present */
-#define SO_OOBINLINE 0x0100 /* Unimplemented: leave received OOB data in line */
-#define SO_REUSEPORT 0x0200 /* Unimplemented: allow local address & port reuse */
+#define SO_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) */
-#define SO_DONTLINGER ((int)(~SO_LINGER))
/*
* Additional options, not kept in so_options.
*/
-#define SO_SNDBUF 0x1001 /* Unimplemented: send buffer size */
-#define SO_RCVBUF 0x1002 /* receive buffer size */
-#define SO_SNDLOWAT 0x1003 /* Unimplemented: send low-water mark */
-#define SO_RCVLOWAT 0x1004 /* Unimplemented: receive low-water mark */
-#define SO_SNDTIMEO 0x1005 /* Unimplemented: send timeout */
-#define SO_RCVTIMEO 0x1006 /* receive timeout */
-#define SO_ERROR 0x1007 /* get error status and clear */
-#define SO_TYPE 0x1008 /* get socket type */
-#define SO_CONTIMEO 0x1009 /* Unimplemented: connect timeout */
-#define SO_NO_CHECK 0x100a /* don't create UDP checksum */
-
+#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 */
+#define SO_BINDTODEVICE 0x100b /* bind to device */
/*
* Structure used for manipulating linger option.
*/
struct linger {
- int l_onoff; /* option on/off */
- int l_linger; /* linger time */
+ int l_onoff; /* option on/off */
+ int l_linger; /* linger time in seconds */
};
/*
@@ -146,12 +244,15 @@ struct linger {
#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 */
@@ -159,6 +260,7 @@ struct linger {
#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 */
+#define MSG_NOSIGNAL 0x20 /* Uninmplemented: Requests not to send the SIGPIPE signal if an attempt to send is made on a stream-oriented socket that is no longer connected. */
/*
@@ -166,6 +268,7 @@ struct linger {
*/
#define IP_TOS 1
#define IP_TTL 2
+#define IP_PKTINFO 8
#if LWIP_TCP
/*
@@ -182,7 +285,8 @@ struct linger {
/*
* Options for level IPPROTO_IPV6
*/
-#define IPV6_V6ONLY 27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */
+#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
@@ -194,15 +298,21 @@ struct linger {
#endif /* LWIP_UDP && LWIP_UDPLITE*/
-#if LWIP_IGMP
+#if LWIP_MULTICAST_TX_OPTIONS
/*
* Options and types for UDP multicast traffic handling
*/
-#define IP_ADD_MEMBERSHIP 3
-#define IP_DROP_MEMBERSHIP 4
#define IP_MULTICAST_TTL 5
#define IP_MULTICAST_IF 6
#define IP_MULTICAST_LOOP 7
+#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 */
@@ -210,6 +320,28 @@ typedef struct ip_mreq {
} ip_mreq;
#endif /* LWIP_IGMP */
+#if LWIP_IPV4
+struct in_pktinfo {
+ unsigned int ipi_ifindex; /* Interface index */
+ struct in_addr ipi_addr; /* Destination (from header) address */
+};
+#endif /* LWIP_IPV4 */
+
+#if LWIP_IPV6_MLD
+/*
+ * Options and types related to IPv6 multicast membership
+ */
+#define IPV6_JOIN_GROUP 12
+#define IPV6_ADD_MEMBERSHIP IPV6_JOIN_GROUP
+#define IPV6_LEAVE_GROUP 13
+#define IPV6_DROP_MEMBERSHIP IPV6_LEAVE_GROUP
+
+typedef struct ipv6_mreq {
+ struct in6_addr ipv6mr_multiaddr; /* IPv6 multicast addr */
+ unsigned int ipv6mr_interface; /* interface index, or 0 */
+} ipv6_mreq;
+#endif /* LWIP_IPV6_MLD */
+
/*
* The Type of Service provides an indication of the abstract
* parameters of the quality of service desired. These parameters are
@@ -273,11 +405,11 @@ typedef struct ip_mreq {
#define IOC_INOUT (IOC_IN|IOC_OUT)
/* 0x20000000 distinguishes new &
old ioctl's */
-#define _IO(x,y) (IOC_VOID|((x)<<8)|(y))
+#define _IO(x,y) ((long)(IOC_VOID|((x)<<8)|(y)))
-#define _IOR(x,y,t) (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y))
+#define _IOR(x,y,t) ((long)(IOC_OUT|((sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)))
-#define _IOW(x,y,t) (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y))
+#define _IOW(x,y,t) ((long)(IOC_IN|((sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y)))
#endif /* !defined(FIONREAD) || !defined(FIONBIO) */
#ifndef FIONREAD
@@ -312,6 +444,15 @@ typedef struct ip_mreq {
#ifndef O_NDELAY
#define O_NDELAY 1 /* same as O_NONBLOCK, for compatibility */
#endif
+#ifndef O_RDONLY
+#define O_RDONLY 2
+#endif
+#ifndef O_WRONLY
+#define O_WRONLY 4
+#endif
+#ifndef O_RDWR
+#define O_RDWR (O_RDONLY|O_WRONLY)
+#endif
#ifndef SHUT_RD
#define SHUT_RD 0
@@ -321,22 +462,32 @@ typedef struct ip_mreq {
/* FD_SET used for lwip_select */
#ifndef FD_SET
- #undef FD_SETSIZE
- /* Make FD_SETSIZE match NUM_SOCKETS in socket.c */
- #define FD_SETSIZE MEMP_NUM_NETCONN
- #define FD_SET(n, p) ((p)->fd_bits[(n)/8] |= (1 << ((n) & 7)))
- #define FD_CLR(n, p) ((p)->fd_bits[(n)/8] &= ~(1 << ((n) & 7)))
- #define FD_ISSET(n,p) ((p)->fd_bits[(n)/8] & (1 << ((n) & 7)))
- #define FD_ZERO(p) memset((void*)(p),0,sizeof(*(p)))
-
- typedef struct fd_set {
- unsigned char fd_bits [(FD_SETSIZE+7)/8];
- } fd_set;
-
+#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] = (u8_t)((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] = (u8_t)((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 */
+ * 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
@@ -348,7 +499,46 @@ struct timeval {
};
#endif /* LWIP_TIMEVAL_PRIVATE */
-void lwip_socket_init(void);
+#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_recvmsg recvmsg
+#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
+#define lwip_inet_ntop inet_ntop
+#define lwip_inet_pton inet_pton
+
+#if LWIP_POSIX_SOCKETS_IO_NAMES
+#define lwip_read read
+#define lwip_readv readv
+#define lwip_write write
+#define lwip_writev writev
+#undef lwip_close
+#define lwip_close close
+#define closesocket(s) close(s)
+int fcntl(int s, int cmd, ...);
+#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);
@@ -357,48 +547,93 @@ 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_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,
+ssize_t lwip_recv(int s, void *mem, size_t len, int flags);
+ssize_t lwip_read(int s, void *mem, size_t len);
+ssize_t lwip_readv(int s, const struct iovec *iov, int iovcnt);
+ssize_t lwip_recvfrom(int s, void *mem, size_t len, int flags,
struct sockaddr *from, socklen_t *fromlen);
-int lwip_send(int s, const void *dataptr, size_t size, int flags);
-int lwip_sendto(int s, const void *dataptr, size_t size, int flags,
+ssize_t lwip_recvmsg(int s, struct msghdr *message, int flags);
+ssize_t lwip_send(int s, const void *dataptr, size_t size, int flags);
+ssize_t lwip_sendmsg(int s, const struct msghdr *message, int flags);
+ssize_t 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);
+ssize_t lwip_write(int s, const void *dataptr, size_t size);
+ssize_t lwip_writev(int s, const struct iovec *iov, int iovcnt);
+#if LWIP_SOCKET_SELECT
int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
struct timeval *timeout);
+#endif
int lwip_ioctl(int s, long cmd, void *argp);
int lwip_fcntl(int s, int cmd, int val);
+const char *lwip_inet_ntop(int af, const void *src, char *dst, socklen_t size);
+int lwip_inet_pton(int af, const char *src, void *dst);
#if LWIP_COMPAT_SOCKETS
-#define accept(a,b,c) lwip_accept(a,b,c)
-#define bind(a,b,c) lwip_bind(a,b,c)
-#define shutdown(a,b) lwip_shutdown(a,b)
-#define closesocket(s) lwip_close(s)
-#define connect(a,b,c) lwip_connect(a,b,c)
-#define getsockname(a,b,c) lwip_getsockname(a,b,c)
-#define getpeername(a,b,c) lwip_getpeername(a,b,c)
-#define setsockopt(a,b,c,d,e) lwip_setsockopt(a,b,c,d,e)
-#define getsockopt(a,b,c,d,e) lwip_getsockopt(a,b,c,d,e)
-#define listen(a,b) lwip_listen(a,b)
-#define recv(a,b,c,d) lwip_recv(a,b,c,d)
-#define recvfrom(a,b,c,d,e,f) lwip_recvfrom(a,b,c,d,e,f)
-#define send(a,b,c,d) lwip_send(a,b,c,d)
-#define sendto(a,b,c,d,e,f) lwip_sendto(a,b,c,d,e,f)
-#define socket(a,b,c) lwip_socket(a,b,c)
-#define select(a,b,c,d,e) lwip_select(a,b,c,d,e)
-#define ioctlsocket(a,b,c) lwip_ioctl(a,b,c)
+#if LWIP_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 recvmsg(s,message,flags) lwip_recvmsg(s,message,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)
+/** @ingroup socket */
+#define inet_ntop(af,src,dst,size) lwip_inet_ntop(af,src,dst,size)
+/** @ingroup socket */
+#define inet_pton(af,src,dst) lwip_inet_pton(af,src,dst)
#if LWIP_POSIX_SOCKETS_IO_NAMES
-#define read(a,b,c) lwip_read(a,b,c)
-#define write(a,b,c) lwip_write(a,b,c)
-#define close(s) lwip_close(s)
-#define fcntl(a,b,c) lwip_fcntl(a,b,c)
+/** @ingroup socket */
+#define read(s,mem,len) lwip_read(s,mem,len)
+/** @ingroup socket */
+#define readv(s,iov,iovcnt) lwip_readv(s,iov,iovcnt)
+/** @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 */
#endif /* LWIP_COMPAT_SOCKETS */
@@ -408,4 +643,4 @@ int lwip_fcntl(int s, int cmd, int val);
#endif /* LWIP_SOCKET */
-#endif /* __LWIP_SOCKETS_H__ */
+#endif /* LWIP_HDR_SOCKETS_H */
diff --git a/lwip/src/include/lwip/stats.h b/lwip/src/include/lwip/stats.h
index d911216..b570dba 100644
--- a/lwip/src/include/lwip/stats.h
+++ b/lwip/src/include/lwip/stats.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,26 +16,26 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_STATS_H__
-#define __LWIP_STATS_H__
+#ifndef LWIP_HDR_STATS_H
+#define LWIP_HDR_STATS_H
#include "lwip/opt.h"
@@ -53,8 +58,9 @@ extern "C" {
#else
#define STAT_COUNTER u16_t
#define STAT_COUNTER_F U16_F
-#endif
+#endif
+/** Protocol related stats */
struct stats_proto {
STAT_COUNTER xmit; /* Transmitted packets. */
STAT_COUNTER recv; /* Received packets. */
@@ -70,6 +76,7 @@ struct stats_proto {
STAT_COUNTER cachehit;
};
+/** IGMP stats */
struct stats_igmp {
STAT_COUNTER xmit; /* Transmitted packets. */
STAT_COUNTER recv; /* Received packets. */
@@ -87,96 +94,231 @@ struct stats_igmp {
STAT_COUNTER tx_report; /* Sent reports. */
};
+/** Memory stats */
struct stats_mem {
-#ifdef LWIP_DEBUG
+#if defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY
const char *name;
-#endif /* LWIP_DEBUG */
+#endif /* defined(LWIP_DEBUG) || LWIP_STATS_DISPLAY */
+ STAT_COUNTER err;
mem_size_t avail;
mem_size_t used;
mem_size_t max;
- STAT_COUNTER err;
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
- struct stats_mem memp[MEMP_MAX];
+ /** 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; \
+#define STATS_INC_USED(x, y, type) do { lwip_stats.x.used = (type)(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)
+#define STATS_INC_USED(x, y, type)
#endif /* LWIP_STATS */
#if TCP_STATS
@@ -246,8 +388,8 @@ void stats_init(void);
#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_INC_USED(x, y) STATS_INC_USED(mem, y, mem_size_t)
+#define MEM_STATS_DEC_USED(x, y) lwip_stats.mem.x = (mem_size_t)((lwip_stats.mem.x) - (y))
#define MEM_STATS_DISPLAY() stats_display_mem(&lwip_stats.mem, "HEAP")
#else
#define MEM_STATS_AVAIL(x, y)
@@ -257,24 +399,20 @@ void stats_init(void);
#define MEM_STATS_DISPLAY()
#endif
-#if MEMP_STATS
-#define MEMP_STATS_AVAIL(x, i, y) lwip_stats.memp[i].x = y
-#define MEMP_STATS_INC(x, i) STATS_INC(memp[i].x)
-#define MEMP_STATS_DEC(x, i) STATS_DEC(memp[i].x)
-#define MEMP_STATS_INC_USED(x, i) STATS_INC_USED(memp[i], 1)
-#define MEMP_STATS_DISPLAY(i) stats_display_memp(&lwip_stats.memp[i], i)
-#else
-#define MEMP_STATS_AVAIL(x, i, y)
-#define MEMP_STATS_INC(x, i)
+ #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_INC_USED(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_INC_USED(x) STATS_INC_USED(sys.x, 1, STAT_COUNTER)
#define SYS_STATS_DISPLAY() stats_display_sys(&lwip_stats.sys)
#else
#define SYS_STATS_INC(x)
@@ -323,6 +461,12 @@ void stats_init(void);
#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);
@@ -344,4 +488,4 @@ void stats_display_sys(struct stats_sys *sys);
}
#endif
-#endif /* __LWIP_STATS_H__ */
+#endif /* LWIP_HDR_STATS_H */
diff --git a/lwip/src/include/lwip/sys.h b/lwip/src/include/lwip/sys.h
index fd45ee8..4e2386c 100644
--- a/lwip/src/include/lwip/sys.h
+++ b/lwip/src/include/lwip/sys.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,26 +16,26 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
*
+ * Author: Adam Dunkels <adam@sics.se>
*/
-#ifndef __LWIP_SYS_H__
-#define __LWIP_SYS_H__
+
+#ifndef LWIP_HDR_SYS_H
+#define LWIP_HDR_SYS_H
#include "lwip/opt.h"
@@ -52,7 +57,9 @@ typedef u8_t sys_mbox_t;
#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)
@@ -66,7 +73,9 @@ typedef u8_t sys_mbox_t;
#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)
@@ -80,7 +89,7 @@ typedef u8_t sys_mbox_t;
/** sys_mbox_tryfetch() returns SYS_MBOX_EMPTY if appropriate.
* For now we use the same magic value, but we allow this to change in future.
*/
-#define SYS_MBOX_EMPTY SYS_ARCH_TIMEOUT
+#define SYS_MBOX_EMPTY SYS_ARCH_TIMEOUT
#include "lwip/err.h"
#include "arch/sys_arch.h"
@@ -95,6 +104,10 @@ typedef void (*lwip_thread_fn)(void *arg);
/** 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
@@ -107,114 +120,206 @@ typedef void (*lwip_thread_fn)(void *arg);
#else /* LWIP_COMPAT_MUTEX */
-/** Create a new 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 a new mutex */
+ * @return ERR_OK if successful, another err_t otherwise
+ */
err_t sys_mutex_new(sys_mutex_t *mutex);
-/** Lock a mutex
- * @param mutex the mutex to lock */
+/**
+ * @ingroup sys_mutex
+ * Lock a mutex
+ * @param mutex the mutex to lock
+ */
void sys_mutex_lock(sys_mutex_t *mutex);
-/** Unlock a mutex
- * @param mutex the mutex to unlock */
+/**
+ * @ingroup sys_mutex
+ * Unlock a mutex
+ * @param mutex the mutex to unlock
+ */
void sys_mutex_unlock(sys_mutex_t *mutex);
-/** Delete a semaphore
- * @param mutex the mutex to delete */
-void sys_mutex_free(sys_mutex_t *mutex);
+/**
+ * @ingroup sys_mutex
+ * Delete a semaphore
+ * @param mutex the mutex to delete
+ */
+void sys_mutex_free(sys_mutex_t *mutex);
#ifndef sys_mutex_valid
-/** Check if a mutex is valid/allocated: return 1 for valid, 0 for invalid */
+/**
+ * @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
-/** Set a mutex invalid so that sys_mutex_valid returns 0 */
+/**
+ * @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: */
-/** Create a new semaphore
+/**
+ * @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 */
+ * @return ERR_OK if successful, another err_t otherwise
+ */
err_t sys_sem_new(sys_sem_t *sem, u8_t count);
-/** Signals a semaphore
- * @param sem the semaphore to signal */
+/**
+ * @ingroup sys_sem
+ * Signals a semaphore
+ * @param sem the semaphore to signal
+ */
void sys_sem_signal(sys_sem_t *sem);
-/** Wait for a semaphore for the specified timeout
+/**
+ * @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 */
+ * @return SYS_ARCH_TIMEOUT on timeout, any other value on success
+ */
u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout);
-/** Delete a semaphore
- * @param sem semaphore to delete */
+/**
+ * @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
-/** Check if a sempahore is valid/allocated: return 1 for valid, 0 for invalid */
+/**
+ * @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
-/** Set a semaphore invalid so that sys_sem_valid returns 0 */
+/**
+ * @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
-/* Time functions. */
#ifndef sys_msleep
-void sys_msleep(u32_t ms); /* only has a (close to) 1 jiffy resolution. */
+/**
+ * @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. */
-/** Create a new mbox of specified size
+/**
+ * @ingroup sys_mbox
+ * Create a new mbox of specified size
* @param mbox pointer to the mbox to create
- * @param size (miminum) number of messages in this mbox
- * @return ERR_OK if successful, another err_t otherwise */
+ * @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);
-/** Post a message to an mbox - may not fail
+/**
+ * @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) */
+ * @param msg message to post (ATTENTION: can be NULL)
+ */
void sys_mbox_post(sys_mbox_t *mbox, void *msg);
-/** Try to post a message to an mbox - may fail if full or ISR
+/**
+ * @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) */
+ * @param msg message to post (ATTENTION: can be NULL)
+ */
err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg);
-/** Wait for a new message to arrive in the mbox
+/**
+ * @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! */
+ * @return SYS_ARCH_TIMEOUT on timeout, any other value if a message has been received
+ */
u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout);
-/* Allow port to override with a macro, e.g. special timout for sys_arch_mbox_fetch() */
+/* Allow port to override with a macro, e.g. special timeout for sys_arch_mbox_fetch() */
#ifndef sys_arch_mbox_tryfetch
-/** Wait for a new message to arrive in the mbox
+/**
+ * @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 */
+ * 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. */
+/**
+ * For now, we map straight to sys_arch implementation.
+ */
#define sys_mbox_tryfetch(mbox, msg) sys_arch_mbox_tryfetch(mbox, msg)
-/** Delete an mbox
- * @param mbox mbox to delete */
+/**
+ * @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
-/** Check if an mbox is valid/allocated: return 1 for valid, 0 for invalid */
+/**
+ * @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
-/** Set an mbox invalid so that sys_mbox_valid returns 0 */
+/**
+ * @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
-/** The only thread function:
+
+/**
+ * @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'
@@ -224,16 +329,21 @@ sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg,
#endif /* NO_SYS */
-/* sys_init() must be called before anthing else. */
+/* sys_init() must be called before anything else. */
void sys_init(void);
#ifndef sys_jiffies
-/** Ticks/jiffies since power up. */
+/**
+ * Ticks/jiffies since power up.
+ */
u32_t sys_jiffies(void);
#endif
-/** Returns the current time in milliseconds,
- * may be the same as sys_jiffies or at least based on it. */
+/**
+ * @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 */
@@ -249,13 +359,17 @@ u32_t sys_now(void);
*/
#if SYS_LIGHTWEIGHT_PROT
-/** SYS_ARCH_DECL_PROTECT
+/**
+ * @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
-/** SYS_ARCH_PROTECT
+/**
+ * @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
@@ -265,7 +379,9 @@ u32_t sys_now(void);
* different implementation, then this macro may be defined in sys_arch.h
*/
#define SYS_ARCH_PROTECT(lev) lev = sys_arch_protect()
-/** SYS_ARCH_UNPROTECT
+/**
+ * @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
@@ -328,9 +444,18 @@ void sys_arch_unprotect(sys_prot_t pval);
} while(0)
#endif /* SYS_ARCH_SET */
+#ifndef SYS_ARCH_LOCKED
+#define SYS_ARCH_LOCKED(code) do { \
+ SYS_ARCH_DECL_PROTECT(old_level); \
+ SYS_ARCH_PROTECT(old_level); \
+ code; \
+ SYS_ARCH_UNPROTECT(old_level); \
+ } while(0)
+#endif /* SYS_ARCH_LOCKED */
+
#ifdef __cplusplus
}
#endif
-#endif /* __LWIP_SYS_H__ */
+#endif /* LWIP_HDR_SYS_H */
diff --git a/lwip/src/include/lwip/tcp.h b/lwip/src/include/lwip/tcp.h
index 535b6a3..9a7fd32 100644
--- a/lwip/src/include/lwip/tcp.h
+++ b/lwip/src/include/lwip/tcp.h
@@ -1,8 +1,14 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,31 +17,32 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_TCP_H__
-#define __LWIP_TCP_H__
+#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/tcpbase.h"
#include "lwip/mem.h"
#include "lwip/pbuf.h"
#include "lwip/ip.h"
@@ -117,7 +124,7 @@ typedef void (*tcp_err_fn)(void *arg, err_t err);
*
* @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!
+ * @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!
*
@@ -125,32 +132,38 @@ typedef void (*tcp_err_fn)(void *arg, err_t err);
*/
typedef err_t (*tcp_connected_fn)(void *arg, struct tcp_pcb *tpcb, err_t err);
-enum tcp_state {
- CLOSED = 0,
- LISTEN = 1,
- SYN_SENT = 2,
- SYN_RCVD = 3,
- ESTABLISHED = 4,
- FIN_WAIT_1 = 5,
- FIN_WAIT_2 = 6,
- CLOSE_WAIT = 7,
- CLOSING = 8,
- LAST_ACK = 9,
- TIME_WAIT = 10
+#if LWIP_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)))
+#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
+#endif
+/* Increments a tcpwnd_size_t and holds at max value rather than rollover */
+#define TCP_WND_INC(wnd, inc) do { \
+ if ((tcpwnd_size_t)(wnd + inc) >= wnd) { \
+ wnd = (tcpwnd_size_t)(wnd + inc); \
+ } else { \
+ wnd = (tcpwnd_size_t)-1; \
+ } \
+ } while(0)
+
+#if LWIP_TCP_SACK_OUT
+/** SACK ranges to include in ACK packets.
+ * SACK entry is invalid if left==right. */
+struct tcp_sack_range {
+ /** Left edge of the SACK: the first acknowledged sequence number. */
+ u32_t left;
+ /** Right edge of the SACK: the last acknowledged sequence number +1 (so first NOT acknowledged). */
+ u32_t right;
};
+#endif /* LWIP_TCP_SACK_OUT */
-#if LWIP_CALLBACK_API
- /* Function to call when a listener has been connected.
- * @param arg user-supplied argument (tcp_pcb.callback_arg)
- * @param pcb a new tcp_pcb that now is connected
- * @param err an error argument (TODO: that is current always ERR_OK?)
- * @return ERR_OK: accept the new connection,
- * any other err_t abortsthe new connection
- */
-#define DEF_ACCEPT_CALLBACK tcp_accept_fn accept;
-#else /* LWIP_CALLBACK_API */
-#define DEF_ACCEPT_CALLBACK
-#endif /* LWIP_CALLBACK_API */
+typedef u16_t tcpflags_t;
/**
* members common to struct tcp_pcb and struct tcp_listen_pcb
@@ -158,17 +171,34 @@ enum tcp_state {
#define TCP_PCB_COMMON(type) \
type *next; /* for the linked list */ \
void *callback_arg; \
- /* the accept callback for listen- and normal pcbs, if LWIP_CALLBACK_API */ \
- DEF_ACCEPT_CALLBACK \
enum tcp_state state; /* TCP state */ \
u8_t prio; \
/* ports are in host byte order */ \
- int bound_to_netif; \
- u16_t local_port; \
- char local_netif[3]
+ u8_t bound_to_netif; \
+ char local_netif[3]; \
+ u16_t local_port
-/* the TCP protocol control block */
+/** 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;
@@ -177,16 +207,29 @@ struct tcp_pcb {
/* ports are in host byte order */
u16_t remote_port;
-
- u8_t flags;
-#define TF_ACK_DELAY ((u8_t)0x01U) /* Delayed ACK. */
-#define TF_ACK_NOW ((u8_t)0x02U) /* Immediate ACK. */
-#define TF_INFR ((u8_t)0x04U) /* In fast recovery. */
-#define TF_TIMESTAMP ((u8_t)0x08U) /* Timestamp option enabled */
-#define TF_RXCLOSED ((u8_t)0x10U) /* rx closed by tcp_shutdown */
-#define TF_FIN ((u8_t)0x20U) /* Connection was closed locally (FIN segment enqueued). */
-#define TF_NODELAY ((u8_t)0x40U) /* Disable Nagle algorithm */
-#define TF_NAGLEMEMERR ((u8_t)0x80U) /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */
+
+ 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
+#define TF_RTO 0x0800U /* RTO timer has fired, in-flight data moved to unsent and being retransmitted */
+#if LWIP_TCP_SACK_OUT
+#define TF_SACK 0x1000U /* Selective ACKs enabled */
+#endif
/* the rest of the fields are in host byte order
as we have to do some math with them */
@@ -198,10 +241,16 @@ struct tcp_pcb {
/* receiver variables */
u32_t rcv_nxt; /* next seqno expected */
- u16_t rcv_wnd; /* receiver window available */
- u16_t rcv_ann_wnd; /* receiver window to announce */
+ 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 */
+#if LWIP_TCP_SACK_OUT
+ /* SACK ranges to include in ACK packets (entry is invalid if left==right) */
+ struct tcp_sack_range rcv_sacks[LWIP_TCP_MAX_SACK_NUM];
+#define LWIP_TCP_SACK_VALID(pcb, idx) ((pcb)->rcv_sacks[idx].left != (pcb)->rcv_sacks[idx].right)
+#endif /* LWIP_TCP_SACK_OUT */
+
/* Retransmission timer. */
s16_t rtime;
@@ -210,9 +259,9 @@ struct tcp_pcb {
/* 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 sa, sv; /* @see "Congestion Avoidance and Control" by Van Jacobson and Karels */
- s16_t rto; /* retransmission time-out */
+ s16_t rto; /* retransmission time-out (in ticks of TCP_SLOW_INTERVAL) */
u8_t nrtx; /* number of retransmissions */
/* fast retransmit/recovery */
@@ -220,37 +269,44 @@ struct tcp_pcb {
u32_t lastack; /* Highest acknowledged seqno. */
/* congestion avoidance/control variables */
- u16_t cwnd;
- u16_t ssthresh;
+ tcpwnd_size_t cwnd;
+ tcpwnd_size_t ssthresh;
+
+ /* first byte following last rto byte */
+ u32_t rto_end;
/* 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. */
- u16_t snd_wnd; /* sender window */
- u16_t snd_wnd_max; /* the maximum sender window announced by the remote host */
+ tcpwnd_size_t snd_wnd; /* sender window */
+ tcpwnd_size_t snd_wnd_max; /* the maximum sender window announced by the remote host */
- u16_t acked;
-
- u16_t snd_buf; /* Available buffer space for sending (in bytes). */
+ tcpwnd_size_t snd_buf; /* Available buffer space for sending (in bytes). */
#define TCP_SNDQUEUELEN_OVERFLOW (0xffffU-3)
- u16_t snd_queuelen; /* Available buffer space for sending (in tcp_segs). */
+ 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 */
+#endif /* TCP_OVERSIZE */
+
+ tcpwnd_size_t bytes_acked;
/* 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
+#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;
@@ -275,29 +331,21 @@ struct tcp_pcb {
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;
+ /* Number of persist probes */
+ u8_t persist_probe;
/* KEEPALIVE counter */
u8_t keep_cnt_sent;
-};
-struct tcp_pcb_listen {
-/* Common members of all PCB types */
- IP_PCB;
-/* Protocol specific PCB members */
- TCP_PCB_COMMON(struct tcp_pcb_listen);
-
-#if TCP_LISTEN_BACKLOG
- u8_t backlog;
- u8_t accepts_pending;
-#endif /* TCP_LISTEN_BACKLOG */
-#if LWIP_IPV6
- u8_t accept_any_ip_version;
-#endif /* LWIP_IPV6 */
+#if LWIP_WND_SCALE
+ u8_t snd_scale;
+ u8_t rcv_scale;
+#endif
};
#if LWIP_EVENT_API
@@ -321,75 +369,81 @@ err_t lwip_tcp_event(void *arg, struct tcp_pcb *pcb,
/* 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);
-void tcp_accept (struct tcp_pcb *pcb, tcp_accept_fn accept);
+#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_poll (struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval);
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);
+
+#define tcp_set_flags(pcb, set_flags) do { (pcb)->flags = (tcpflags_t)((pcb)->flags | (set_flags)); } while(0)
+#define tcp_clear_flags(pcb, clr_flags) do { (pcb)->flags = (tcpflags_t)((pcb)->flags & ~(clr_flags)); } while(0)
+#define tcp_is_flag_set(pcb, flag) (((pcb)->flags & (flag)) != 0)
+#if LWIP_TCP_TIMESTAMPS
#define tcp_mss(pcb) (((pcb)->flags & TF_TIMESTAMP) ? ((pcb)->mss - 12) : (pcb)->mss)
-#define tcp_sndbuf(pcb) ((pcb)->snd_buf)
+#else /* LWIP_TCP_TIMESTAMPS */
+/** @ingroup tcp_raw */
+#define tcp_mss(pcb) ((pcb)->mss)
+#endif /* LWIP_TCP_TIMESTAMPS */
+/** @ingroup tcp_raw */
+#define tcp_sndbuf(pcb) (TCPWND16((pcb)->snd_buf))
+/** @ingroup tcp_raw */
#define tcp_sndqueuelen(pcb) ((pcb)->snd_queuelen)
-#define tcp_nagle_disable(pcb) ((pcb)->flags |= TF_NODELAY)
-#define tcp_nagle_enable(pcb) ((pcb)->flags &= ~TF_NODELAY)
-#define tcp_nagle_disabled(pcb) (((pcb)->flags & TF_NODELAY) != 0)
+/** @ingroup tcp_raw */
+#define tcp_nagle_disable(pcb) tcp_set_flags(pcb, TF_NODELAY)
+/** @ingroup tcp_raw */
+#define tcp_nagle_enable(pcb) tcp_clear_flags(pcb, TF_NODELAY)
+/** @ingroup tcp_raw */
+#define tcp_nagle_disabled(pcb) tcp_is_flag_set(pcb, TF_NODELAY)
#if TCP_LISTEN_BACKLOG
-#define tcp_accepted(pcb) do { \
- LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", pcb->state == LISTEN); \
- (((struct tcp_pcb_listen *)(pcb))->accepts_pending--); } while(0)
+#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)
+void tcp_backlog_delayed(struct tcp_pcb* pcb);
+void tcp_backlog_accepted(struct tcp_pcb* pcb);
#else /* TCP_LISTEN_BACKLOG */
-#define tcp_accepted(pcb) LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", \
- (pcb)->state == LISTEN)
+#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) do { LWIP_UNUSED_ARG(pcb); } while(0) /* compatibility define, not needed any more */
void tcp_recved (struct tcp_pcb *pcb, u16_t len);
-err_t tcp_bind (struct tcp_pcb *pcb, ip_addr_t *ipaddr,
+err_t tcp_bind (struct tcp_pcb *pcb, const ip_addr_t *ipaddr,
u16_t port);
+void tcp_bind_netif(struct tcp_pcb *pcb, const struct netif *netif);
err_t tcp_bind_to_netif (struct tcp_pcb *pcb, const char ifname[3]);
-err_t tcp_connect (struct tcp_pcb *pcb, ip_addr_t *ipaddr,
+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);
+err_t tcp_tcp_get_tcp_addrinfo(struct tcp_pcb *pcb, int local, ip_addr_t *addr, u16_t *port);
-const char* tcp_debug_state_str(enum tcp_state s);
-
-#if LWIP_IPV6
-struct tcp_pcb * tcp_new_ip6 (void);
-#define tcp_bind_ip6(pcb, ip6addr, port) \
- tcp_bind(pcb, ip6_2_ip(ip6addr), port)
-#define tcp_connect_ip6(pcb, ip6addr, port, connected) \
- tcp_connect(pcb, ip6_2_ip(ip6addr), port, connected)
-struct tcp_pcb * tcp_listen_dual_with_backlog(struct tcp_pcb *pcb, u8_t backlog);
-#define tcp_listen_dual(pcb) tcp_listen_dual_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG)
-#else /* LWIP_IPV6 */
-#define tcp_listen_dual_with_backlog(pcb, backlog) tcp_listen_with_backlog(pcb, backlog)
-#define tcp_listen_dual(pcb) tcp_listen(pcb)
-#endif /* LWIP_IPV6 */
+#define tcp_dbg_get_tcp_state(pcb) ((pcb)->state)
+/* for compatibility with older implementation */
+#define tcp_new_ip6() tcp_new_ip_type(IPADDR_TYPE_V6)
#ifdef __cplusplus
}
@@ -397,4 +451,4 @@ struct tcp_pcb * tcp_listen_dual_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
#endif /* LWIP_TCP */
-#endif /* __LWIP_TCP_H__ */
+#endif /* LWIP_HDR_TCP_H */
diff --git a/lwip/src/include/lwip/tcpbase.h b/lwip/src/include/lwip/tcpbase.h
new file mode 100644
index 0000000..50c2853
--- /dev/null
+++ b/lwip/src/include/lwip/tcpbase.h
@@ -0,0 +1,86 @@
+/**
+ * @file
+ * Base TCP API definitions shared by TCP and ALTCP\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_TCPBASE_H
+#define LWIP_HDR_TCPBASE_H
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+#if LWIP_WND_SCALE
+typedef u32_t tcpwnd_size_t;
+#else
+typedef u16_t tcpwnd_size_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
+};
+
+/* Flags for "apiflags" parameter in tcp_write */
+#define TCP_WRITE_FLAG_COPY 0x01
+#define TCP_WRITE_FLAG_MORE 0x02
+
+#define TCP_PRIO_MIN 1
+#define TCP_PRIO_NORMAL 64
+#define TCP_PRIO_MAX 127
+
+const char* tcp_debug_state_str(enum tcp_state s);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_TCP */
+
+#endif /* LWIP_HDR_TCPBASE_H */
diff --git a/lwip/src/include/lwip/tcpip.h b/lwip/src/include/lwip/tcpip.h
index 04567f2..7ce8106 100644
--- a/lwip/src/include/lwip/tcpip.h
+++ b/lwip/src/include/lwip/tcpip.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,82 +16,54 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_TCPIP_H__
-#define __LWIP_TCPIP_H__
+#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/api_msg.h"
-#include "lwip/netifapi.h"
-#include "lwip/pbuf.h"
-#include "lwip/api.h"
-#include "lwip/sys.h"
-#include "lwip/timers.h"
+#include "lwip/err.h"
+#include "lwip/timeouts.h"
#include "lwip/netif.h"
#ifdef __cplusplus
extern "C" {
#endif
-/** Define this to something that triggers a watchdog. This is called from
- * tcpip_thread after processing a message. */
-#ifndef LWIP_TCPIP_THREAD_ALIVE
-#define LWIP_TCPIP_THREAD_ALIVE()
-#endif
-
#if LWIP_TCPIP_CORE_LOCKING
/** The global semaphore to lock the stack. */
extern sys_mutex_t lock_tcpip_core;
+/** 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)
-#ifdef LWIP_DEBUG
-#define TCIP_APIMSG_SET_ERR(m, e) (m)->msg.err = e /* catch functions that don't set err */
-#else
-#define TCIP_APIMSG_SET_ERR(m, e)
-#endif
-#define TCPIP_APIMSG_NOERR(m,f) do { \
- TCIP_APIMSG_SET_ERR(m, ERR_VAL); \
- LOCK_TCPIP_CORE(); \
- f(&((m)->msg)); \
- UNLOCK_TCPIP_CORE(); \
-} while(0)
-#define TCPIP_APIMSG(m,f,e) do { \
- TCPIP_APIMSG_NOERR(m,f); \
- (e) = (m)->msg.err; \
-} while(0)
-#define TCPIP_APIMSG_ACK(m)
-#define TCPIP_NETIFAPI(m) tcpip_netifapi_lock(m)
-#define TCPIP_NETIFAPI_ACK(m)
#else /* LWIP_TCPIP_CORE_LOCKING */
#define LOCK_TCPIP_CORE()
#define UNLOCK_TCPIP_CORE()
-#define TCPIP_APIMSG_NOERR(m,f) do { (m)->function = f; tcpip_apimsg(m); } while(0)
-#define TCPIP_APIMSG(m,f,e) do { (m)->function = f; (e) = tcpip_apimsg(m); } while(0)
-#define TCPIP_APIMSG_ACK(m) sys_sem_signal(&m->conn->op_completed)
-#define TCPIP_NETIFAPI(m) tcpip_netifapi(m)
-#define TCPIP_NETIFAPI_ACK(m) sys_sem_signal(&m->sem)
#endif /* LWIP_TCPIP_CORE_LOCKING */
+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() */
@@ -95,80 +72,34 @@ 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);
+void tcpip_init(tcpip_init_done_fn tcpip_init_done, void *arg);
-#if LWIP_NETCONN
-err_t tcpip_apimsg(struct api_msg *apimsg);
-#endif /* LWIP_NETCONN */
+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_input(struct pbuf *p, struct netif *inp);
-
-#if LWIP_NETIF_API
-err_t tcpip_netifapi(struct netifapi_msg *netifapimsg);
-#if LWIP_TCPIP_CORE_LOCKING
-err_t tcpip_netifapi_lock(struct netifapi_msg *netifapimsg);
-#endif /* LWIP_TCPIP_CORE_LOCKING */
-#endif /* LWIP_NETIF_API */
-
-err_t tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block);
-#define tcpip_callback(f, ctx) tcpip_callback_with_block(f, ctx, 1)
+err_t tcpip_try_callback(tcpip_callback_fn function, void *ctx);
+err_t tcpip_callback(tcpip_callback_fn function, void *ctx);
+/** @ingroup lwip_os
+ * @deprecated use tcpip_try_callback() or tcpip_callback() instead
+ */
+#define tcpip_callback_with_block(function, ctx, block) ((block != 0)? tcpip_callback(function, ctx) : tcpip_try_callback(function, ctx))
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
-err_t tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg);
-err_t tcpip_untimeout(sys_timeout_handler h, void *arg);
-#endif /* LWIP_TCPIP_TIMEOUT */
-
-enum tcpip_msg_type {
-#if LWIP_NETCONN
- TCPIP_MSG_API,
-#endif /* LWIP_NETCONN */
- TCPIP_MSG_INPKT,
-#if LWIP_NETIF_API
- TCPIP_MSG_NETIFAPI,
-#endif /* LWIP_NETIF_API */
-#if LWIP_TCPIP_TIMEOUT
- TCPIP_MSG_TIMEOUT,
- TCPIP_MSG_UNTIMEOUT,
-#endif /* LWIP_TCPIP_TIMEOUT */
- TCPIP_MSG_CALLBACK,
- TCPIP_MSG_CALLBACK_STATIC
-};
-
-struct tcpip_msg {
- enum tcpip_msg_type type;
- sys_sem_t *sem;
- union {
-#if LWIP_NETCONN
- struct api_msg *apimsg;
-#endif /* LWIP_NETCONN */
-#if LWIP_NETIF_API
- struct netifapi_msg *netifapimsg;
-#endif /* LWIP_NETIF_API */
- struct {
- struct pbuf *p;
- struct netif *netif;
- } inp;
- struct {
- tcpip_callback_fn function;
- void *ctx;
- } cb;
-#if LWIP_TCPIP_TIMEOUT
- struct {
- u32_t msecs;
- sys_timeout_handler h;
- void *arg;
- } tmo;
-#endif /* LWIP_TCPIP_TIMEOUT */
- } msg;
-};
+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 TCPIP_THREAD_TEST
+int tcpip_thread_poll_one(void);
+#endif
#ifdef __cplusplus
}
@@ -176,4 +107,4 @@ struct tcpip_msg {
#endif /* !NO_SYS */
-#endif /* __LWIP_TCPIP_H__ */
+#endif /* LWIP_HDR_TCPIP_H */
diff --git a/lwip/src/include/lwip/timers.h b/lwip/src/include/lwip/timeouts.h
index 04e78e0..002fcd4 100644
--- a/lwip/src/include/lwip/timers.h
+++ b/lwip/src/include/lwip/timeouts.h
@@ -1,8 +1,13 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,35 +16,29 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
* Simon Goldschmidt
*
*/
-#ifndef __LWIP_TIMERS_H__
-#define __LWIP_TIMERS_H__
+#ifndef LWIP_HDR_TIMEOUTS_H
+#define LWIP_HDR_TIMEOUTS_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"
@@ -57,6 +56,28 @@ extern "C" {
#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_num_cyclic_timers */
+extern const struct lwip_cyclic_timer lwip_cyclic_timers[];
+/** Array size of lwip_cyclic_timers[] */
+extern const int lwip_num_cyclic_timers;
+
+#if LWIP_TIMERS
+
/** Function prototype for a timeout callback function. Register such a function
* using sys_timeout().
*
@@ -84,17 +105,19 @@ 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);
-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 */
+#endif /* LWIP_TIMERS */
+
#ifdef __cplusplus
}
#endif
-#endif /* LWIP_TIMERS */
-#endif /* __LWIP_TIMERS_H__ */
+#endif /* LWIP_HDR_TIMEOUTS_H */
diff --git a/lwip/src/include/lwip/udp.h b/lwip/src/include/lwip/udp.h
index 14d5c0a..4f4a36d 100644
--- a/lwip/src/include/lwip/udp.h
+++ b/lwip/src/include/lwip/udp.h
@@ -1,8 +1,14 @@
+/**
+ * @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,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -11,26 +17,26 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __LWIP_UDP_H__
-#define __LWIP_UDP_H__
+#ifndef LWIP_HDR_UDP_H
+#define LWIP_HDR_UDP_H
#include "lwip/opt.h"
@@ -41,29 +47,12 @@
#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_HLEN 8
-
-/* Fields are (of course) in network byte order. */
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct udp_hdr {
- PACK_STRUCT_FIELD(u16_t src);
- PACK_STRUCT_FIELD(u16_t dest); /* src/dest UDP ports */
- PACK_STRUCT_FIELD(u16_t len);
- PACK_STRUCT_FIELD(u16_t chksum);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
-
#define UDP_FLAGS_NOCHKSUM 0x01U
#define UDP_FLAGS_UDPLITE 0x02U
#define UDP_FLAGS_CONNECTED 0x04U
@@ -76,8 +65,8 @@ struct udp_pcb;
* The callback is responsible for freeing the pbuf
* if it's not used any more.
*
- * ATTENTION: Be aware that 'addr' points into the pbuf 'p' so freeing this pbuf
- * makes 'addr' invalid, too.
+ * 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
@@ -86,31 +75,11 @@ struct udp_pcb;
* @param port the remote port from which the packet was received
*/
typedef void (*udp_recv_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p,
- ip_addr_t *addr, u16_t port);
-
-#if LWIP_IPV6
-/** Function prototype for udp pcb IPv6 receive callback functions
- * The callback is responsible for freeing the pbuf
- * if it's not used any more.
- *
- * @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 IPv6 address from which the packet was received
- * @param port the remote port from which the packet was received
- */
-typedef void (*udp_recv_ip6_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p,
- ip6_addr_t *addr, u16_t port);
-#endif /* LWIP_IPV6 */
-
-#if LWIP_IPV6
-#define UDP_PCB_RECV_IP6 udp_recv_ip6_fn ip6;
-#else
-#define UDP_PCB_RECV_IP6
-#endif /* LWIP_IPV6 */
+ const ip_addr_t *addr, u16_t port);
+/** the UDP protocol control block */
struct udp_pcb {
-/* Common members of all PCB types */
+/** Common members of all PCB types */
IP_PCB;
/* Protocol specific PCB members */
@@ -121,10 +90,16 @@ struct udp_pcb {
/** ports are in host byte order */
u16_t local_port, remote_port;
-#if LWIP_IGMP
- /** outgoing network interface for multicast packets */
- ip_addr_t multicast_ip;
-#endif /* LWIP_IGMP */
+#if LWIP_MULTICAST_TX_OPTIONS
+#if LWIP_IPV4
+ /** outgoing network interface for multicast packets, by IPv4 address (if not 'any') */
+ ip4_addr_t mcast_ip4;
+#endif /* LWIP_IPV4 */
+ /** outgoing network interface for multicast packets, by interface index (if nonzero) */
+ u8_t mcast_ifindex;
+ /** TTL for outgoing multicast packets */
+ u8_t mcast_ttl;
+#endif /* LWIP_MULTICAST_TX_OPTIONS */
#if LWIP_UDPLITE
/** used for UDP_LITE only */
@@ -132,73 +107,76 @@ struct udp_pcb {
#endif /* LWIP_UDPLITE */
/** receive callback function */
- union {
- udp_recv_fn ip4;
- UDP_PCB_RECV_IP6
- }recv;
+ udp_recv_fn recv;
/** user-supplied argument for the recv callback */
- void *recv_arg;
+ void *recv_arg;
};
-/* udp_pcbs export for exernal reference (e.g. SNMP agent) */
+/* 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, ip_addr_t *ipaddr,
+err_t udp_bind (struct udp_pcb *pcb, const ip_addr_t *ipaddr,
u16_t port);
-err_t udp_connect (struct udp_pcb *pcb, ip_addr_t *ipaddr,
+void udp_bind_netif (struct udp_pcb *pcb, const struct netif* netif);
+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,
- ip_addr_t *dst_ip, u16_t dst_port,
+ 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,
- ip_addr_t *dst_ip, u16_t dst_port);
+ 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,
- ip_addr_t *dst_ip, u16_t dst_port,
+ 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,
- ip_addr_t *dst_ip, u16_t dst_port,
+ 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))
+#define udp_set_flags(pcb, set_flags) do { (pcb)->flags = (u8_t)((pcb)->flags | (set_flags)); } while(0)
+#define udp_clear_flags(pcb, clr_flags) do { (pcb)->flags = (u8_t)((pcb)->flags & ~(clr_flags)); } while(0)
+#define udp_is_flag_set(pcb, flag) (((pcb)->flags & (flag)) != 0)
+
/* The following functions are the lower layer interface to UDP. */
void udp_input (struct pbuf *p, struct netif *inp);
void udp_init (void);
-#if LWIP_IPV6
-struct udp_pcb * udp_new_ip6(void);
-#define udp_bind_ip6(pcb, ip6addr, port) \
- udp_bind(pcb, ip6_2_ip(ip6addr), port)
-#define udp_connect_ip6(pcb, ip6addr, port) \
- udp_connect(pcb, ip6_2_ip(ip6addr), port)
-#define udp_recv_ip6(pcb, recv_ip6_fn, recv_arg) \
- udp_recv(pcb, (udp_recv_fn)recv_ip6_fn, recv_arg)
-#define udp_sendto_ip6(pcb, pbuf, ip6addr, port) \
- udp_sendto(pcb, pbuf, ip6_2_ip(ip6addr), port)
-#define udp_sendto_if_ip6(pcb, pbuf, ip6addr, port, netif) \
- udp_sendto_if(pcb, pbuf, ip6_2_ip(ip6addr), port, netif)
-#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
-#define udp_sendto_chksum_ip6(pcb, pbuf, ip6addr, port, have_chk, chksum) \
- udp_sendto_chksum(pcb, pbuf, ip6_2_ip(ip6addr), port, have_chk, chksum)
-#define udp_sendto_if_chksum_ip6(pcb, pbuf, ip6addr, port, netif, have_chk, chksum) \
- udp_sendto_if_chksum(pcb, pbuf, ip6_2_ip(ip6addr), port, netif, have_chk, chksum)
-#endif /*LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
-#endif /* LWIP_IPV6 */
+/* for compatibility with older implementation */
+#define udp_new_ip6() udp_new_ip_type(IPADDR_TYPE_V6)
+
+#if LWIP_MULTICAST_TX_OPTIONS
+#if LWIP_IPV4
+#define udp_set_multicast_netif_addr(pcb, ip4addr) ip4_addr_copy((pcb)->mcast_ip4, *(ip4addr))
+#define udp_get_multicast_netif_addr(pcb) (&(pcb)->mcast_ip4)
+#endif /* LWIP_IPV4 */
+#define udp_set_multicast_netif_index(pcb, idx) ((pcb)->mcast_ifindex = (idx))
+#define udp_get_multicast_netif_index(pcb) ((pcb)->mcast_ifindex)
+#define udp_set_multicast_ttl(pcb, value) ((pcb)->mcast_ttl = (value))
+#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);
@@ -206,10 +184,12 @@ void udp_debug_print(struct udp_hdr *udphdr);
#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_UDP_H__ */
+#endif /* LWIP_HDR_UDP_H */
diff --git a/lwip/src/include/netif/bridgeif.h b/lwip/src/include/netif/bridgeif.h
new file mode 100644
index 0000000..01448e4
--- /dev/null
+++ b/lwip/src/include/netif/bridgeif.h
@@ -0,0 +1,95 @@
+/**
+ * @file
+ * lwIP netif implementing an IEEE 802.1D MAC Bridge
+ */
+
+/*
+ * Copyright (c) 2017 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 <goldsimon@gmx.de>
+ *
+ */
+#ifndef LWIP_HDR_NETIF_BRIDGEIF_H
+#define LWIP_HDR_NETIF_BRIDGEIF_H
+
+#include "netif/bridgeif_opts.h"
+
+#include "lwip/err.h"
+#include "lwip/prot/ethernet.h"
+
+struct netif;
+
+#if (BRIDGEIF_MAX_PORTS < 0) || (BRIDGEIF_MAX_PORTS >= 64)
+#error BRIDGEIF_MAX_PORTS must be [1..63]
+#elif BRIDGEIF_MAX_PORTS < 8
+typedef u8_t bridgeif_portmask_t;
+#elif BRIDGEIF_MAX_PORTS < 16
+typedef u16_t bridgeif_portmask_t;
+#elif BRIDGEIF_MAX_PORTS < 32
+typedef u32_t bridgeif_portmask_t;
+#elif BRIDGEIF_MAX_PORTS < 64
+typedef u64_t bridgeif_portmask_t;
+#endif
+
+#define BR_FLOOD ((bridgeif_portmask_t)-1)
+
+/** @ingroup bridgeif
+ * Initialisation data for @ref bridgeif_init.
+ * An instance of this type must be passed as parameter 'state' to @ref netif_add
+ * when the bridge is added.
+ */
+typedef struct bridgeif_initdata_s {
+ /** MAC address of the bridge (cannot use the netif's addresses) */
+ struct eth_addr ethaddr;
+ /** Maximum number of ports in the bridge (ports are stored in an array, this
+ influences memory allocated for netif->state of the bridge netif). */
+ u8_t max_ports;
+ /** Maximum number of dynamic/learning entries in the bridge's forwarding database.
+ In the default implementation, this controls memory consumption only. */
+ u16_t max_fdb_dynamic_entries;
+ /** Maximum number of static forwarding entries. Influences memory consumption! */
+ u16_t max_fdb_static_entries;
+} bridgeif_initdata_t;
+
+/** @ingroup bridgeif
+ * Use this for constant initialization of a bridgeif_initdat_t
+ * (ethaddr must be passed as ETH_ADDR())
+ */
+#define BRIDGEIF_INITDATA1(max_ports, max_fdb_dynamic_entries, max_fdb_static_entries, ethaddr) {ethaddr, max_ports, max_fdb_dynamic_entries, max_fdb_static_entries}
+/** @ingroup bridgeif
+ * Use this for constant initialization of a bridgeif_initdat_t
+ * (each byte of ethaddr must be passed)
+ */
+#define BRIDGEIF_INITDATA2(max_ports, max_fdb_dynamic_entries, max_fdb_static_entries, e0, e1, e2, e3, e4, e5) {{e0, e1, e2, e3, e4, e5}, max_ports, max_fdb_dynamic_entries, max_fdb_static_entries}
+
+err_t bridgeif_init(struct netif *netif);
+err_t bridgeif_add_port(struct netif *bridgeif, struct netif *portif);
+err_t bridgeif_fdb_add(struct netif *bridgeif, const struct eth_addr *addr, bridgeif_portmask_t ports);
+err_t bridgeif_fdb_remove(struct netif *bridgeif, const struct eth_addr *addr);
+
+#endif /* LWIP_HDR_NETIF_BRIDGEIF_H */
diff --git a/lwip/src/include/netif/bridgeif_opts.h b/lwip/src/include/netif/bridgeif_opts.h
new file mode 100644
index 0000000..4ca0847
--- /dev/null
+++ b/lwip/src/include/netif/bridgeif_opts.h
@@ -0,0 +1,98 @@
+/**
+ * @file
+ * lwIP netif implementing an IEEE 802.1D MAC Bridge
+ */
+
+/*
+ * Copyright (c) 2017 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 <goldsimon@gmx.de>
+ *
+ */
+
+#ifndef LWIP_HDR_NETIF_BRIDGEIF_OPTS_H
+#define LWIP_HDR_NETIF_BRIDGEIF_OPTS_H
+
+#include "lwip/opt.h"
+
+/**
+ * @defgroup bridgeif_opts Options
+ * @ingroup bridgeif
+ * @{
+ */
+
+/** BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT==1: set port netif's 'input' function
+ * to call directly into bridgeif code and on top of that, directly call into
+ * the selected forwarding port's 'linkoutput' function.
+ * This means that the bridgeif input/output path is protected from concurrent access
+ * but as well, *all* bridge port netif's drivers must correctly handle concurrent access!
+ * == 0: get into tcpip_thread for every input packet (no multithreading)
+ * ATTENTION: as ==0 relies on tcpip.h, the default depends on NO_SYS setting
+ */
+#ifndef BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT
+#define BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT NO_SYS
+#endif
+
+/** BRIDGEIF_EXTERNAL_FDB==1: use an external implementation for the forwarding
+ * database which is possibly faster than our example implementation.
+ * (Watch out for concurrent access!)
+ */
+#ifndef BRIDGEIF_EXTERNAL_FDB
+#define BRIDGEIF_EXTERNAL_FDB 0
+#endif
+
+/** BRIDGEIF_MAX_PORTS: this is used to create a typedef used for forwarding
+ * bit-fields: the number of bits required is this + 1 (for the internal/cpu port)
+ * (63 is the maximum, resulting in an u64_t for the bit mask)
+ * ATTENTION: this controls the maximum number of the implementation only!
+ * The max. number of ports per bridge must still be passed via netif_add parameter!
+ */
+#ifndef BRIDGEIF_MAX_PORTS
+#define BRIDGEIF_MAX_PORTS 7
+#endif
+
+/** BRIDGEIF_DEBUG: Enable generic debugging in bridgeif.c. */
+#ifndef BRIDGEIF_DEBUG
+#define BRIDGEIF_DEBUG LWIP_DBG_OFF
+#endif
+
+/** BRIDGEIF_DEBUG: Enable FDB debugging in bridgeif.c. */
+#ifndef BRIDGEIF_FDB_DEBUG
+#define BRIDGEIF_FDB_DEBUG LWIP_DBG_OFF
+#endif
+
+/** BRIDGEIF_DEBUG: Enable forwarding debugging in bridgeif.c. */
+#ifndef BRIDGEIF_FW_DEBUG
+#define BRIDGEIF_FW_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * @}
+ */
+
+#endif /* LWIP_HDR_NETIF_BRIDGEIF_OPTS_H */
diff --git a/lwip/src/include/netif/etharp.h b/lwip/src/include/netif/etharp.h
index 8275a28..b536fd2 100644
--- a/lwip/src/include/netif/etharp.h
+++ b/lwip/src/include/netif/etharp.h
@@ -1,223 +1,3 @@
-/*
- * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
- * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
- * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
- * are permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice,
- * this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce the above copyright notice,
- * this list of conditions and the following disclaimer in the documentation
- * and/or other materials provided with the distribution.
- * 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * This file is part of the lwIP TCP/IP stack.
- *
- * Author: Adam Dunkels <adam@sics.se>
- *
- */
-
-#ifndef __NETIF_ETHARP_H__
-#define __NETIF_ETHARP_H__
-
-#include "lwip/opt.h"
-
-#if LWIP_ARP || LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/pbuf.h"
-#include "lwip/ip_addr.h"
-#include "lwip/netif.h"
-#include "lwip/ip.h"
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#ifndef ETHARP_HWADDR_LEN
-#define ETHARP_HWADDR_LEN 6
-#endif
-
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct eth_addr {
- PACK_STRUCT_FIELD(u8_t addr[ETHARP_HWADDR_LEN]);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
-
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-/** Ethernet header */
-struct eth_hdr {
-#if ETH_PAD_SIZE
- PACK_STRUCT_FIELD(u8_t padding[ETH_PAD_SIZE]);
-#endif
- PACK_STRUCT_FIELD(struct eth_addr dest);
- PACK_STRUCT_FIELD(struct eth_addr src);
- PACK_STRUCT_FIELD(u16_t type);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
-
-#define SIZEOF_ETH_HDR (14 + ETH_PAD_SIZE)
-
-#if ETHARP_SUPPORT_VLAN
-
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-/** VLAN header inserted between ethernet header and payload
- * if 'type' in ethernet header is ETHTYPE_VLAN.
- * See IEEE802.Q */
-struct eth_vlan_hdr {
- PACK_STRUCT_FIELD(u16_t prio_vid);
- PACK_STRUCT_FIELD(u16_t tpid);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
-
-#define SIZEOF_VLAN_HDR 4
-#define VLAN_ID(vlan_hdr) (htons((vlan_hdr)->prio_vid) & 0xFFF)
-
-#endif /* ETHARP_SUPPORT_VLAN */
-
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-/** the ARP message, see RFC 826 ("Packet format") */
-struct etharp_hdr {
- PACK_STRUCT_FIELD(u16_t hwtype);
- PACK_STRUCT_FIELD(u16_t proto);
- PACK_STRUCT_FIELD(u8_t hwlen);
- PACK_STRUCT_FIELD(u8_t protolen);
- PACK_STRUCT_FIELD(u16_t opcode);
- PACK_STRUCT_FIELD(struct eth_addr shwaddr);
- PACK_STRUCT_FIELD(struct ip_addr2 sipaddr);
- PACK_STRUCT_FIELD(struct eth_addr dhwaddr);
- PACK_STRUCT_FIELD(struct ip_addr2 dipaddr);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
-
-#define SIZEOF_ETHARP_HDR 28
-#define SIZEOF_ETHARP_PACKET (SIZEOF_ETH_HDR + SIZEOF_ETHARP_HDR)
-
-/** 5 seconds period */
-#define ARP_TMR_INTERVAL 5000
-
-#define ETHTYPE_ARP 0x0806U
-#define ETHTYPE_IP 0x0800U
-#define ETHTYPE_VLAN 0x8100U
-#define ETHTYPE_IPV6 0x86DDU
-#define ETHTYPE_PPPOEDISC 0x8863U /* PPP Over Ethernet Discovery Stage */
-#define ETHTYPE_PPPOE 0x8864U /* PPP Over Ethernet Session Stage */
-
-/** MEMCPY-like macro to copy to/from struct eth_addr's that are local variables
- * or known to be 32-bit aligned within the protocol header. */
-#ifndef ETHADDR32_COPY
-#define ETHADDR32_COPY(src, dst) SMEMCPY(src, dst, ETHARP_HWADDR_LEN)
-#endif
-
-/** MEMCPY-like macro to copy to/from struct eth_addr's that are no local
- * variables and known to be 16-bit aligned within the protocol header. */
-#ifndef ETHADDR16_COPY
-#define ETHADDR16_COPY(src, dst) SMEMCPY(src, dst, ETHARP_HWADDR_LEN)
-#endif
-
-#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */
-
-/** ARP message types (opcodes) */
-#define ARP_REQUEST 1
-#define ARP_REPLY 2
-
-/** Define this to 1 and define LWIP_ARP_FILTER_NETIF_FN(pbuf, netif, type)
- * to a filter function that returns the correct netif when using multiple
- * netifs on one hardware interface where the netif's low-level receive
- * routine cannot decide for the correct netif (e.g. when mapping multiple
- * IP addresses to one hardware interface).
- */
-#ifndef LWIP_ARP_FILTER_NETIF
-#define LWIP_ARP_FILTER_NETIF 0
-#endif
-
-#if ARP_QUEUEING
-/** struct for queueing outgoing packets for unknown address
- * defined here to be accessed by memp.h
- */
-struct etharp_q_entry {
- struct etharp_q_entry *next;
- struct pbuf *p;
-};
-#endif /* ARP_QUEUEING */
-
-#define etharp_init() /* Compatibility define, not init needed. */
-void etharp_tmr(void);
-s8_t etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr,
- struct eth_addr **eth_ret, ip_addr_t **ip_ret);
-err_t etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr);
-err_t etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q);
-err_t etharp_request(struct netif *netif, ip_addr_t *ipaddr);
-/** For Ethernet network interfaces, we might want to send "gratuitous ARP";
- * this is an ARP packet sent by a node in order to spontaneously cause other
- * nodes to update an entry in their ARP cache.
- * From RFC 3220 "IP Mobility Support for IPv4" section 4.6. */
-#define etharp_gratuitous(netif) etharp_request((netif), &(netif)->ip_addr)
-void etharp_cleanup_netif(struct netif *netif);
-
-#if ETHARP_SUPPORT_STATIC_ENTRIES
-err_t etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr);
-err_t etharp_remove_static_entry(ip_addr_t *ipaddr);
-#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
-
-#if LWIP_AUTOIP
-err_t etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
- const struct eth_addr *ethdst_addr,
- const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr,
- const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr,
- const u16_t opcode);
-#endif /* LWIP_AUTOIP */
-
-#endif /* LWIP_ARP */
-
-err_t ethernet_input(struct pbuf *p, struct netif *netif);
-
-#define eth_addr_cmp(addr1, addr2) (memcmp((addr1)->addr, (addr2)->addr, ETHARP_HWADDR_LEN) == 0)
-
-extern const struct eth_addr ethbroadcast, ethzero;
-
-#ifdef __cplusplus
-}
-#endif
-
-#endif /* LWIP_ARP || LWIP_ETHERNET */
-
-#endif /* __NETIF_ARP_H__ */
+/* ARP has been moved to core/ipv4, provide this #include for compatibility only */
+#include "lwip/etharp.h"
+#include "netif/ethernet.h"
diff --git a/lwip/src/include/netif/ethernet.h b/lwip/src/include/netif/ethernet.h
new file mode 100644
index 0000000..49649cb
--- /dev/null
+++ b/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/lwip/src/include/netif/lowpan6.h b/lwip/src/include/netif/lowpan6.h
new file mode 100644
index 0000000..7020e0d
--- /dev/null
+++ b/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 idx, 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/lwip/src/include/netif/lowpan6_opts.h b/lwip/src/include/netif/lowpan6_opts.h
new file mode 100644
index 0000000..fb93ea0
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/ccp.h b/lwip/src/include/netif/ppp/ccp.h
new file mode 100644
index 0000000..14dd659
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/chap-md5.h b/lwip/src/include/netif/ppp/chap-md5.h
new file mode 100644
index 0000000..eb0269f
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/chap-new.h b/lwip/src/include/netif/ppp/chap-new.h
new file mode 100644
index 0000000..64eae32
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/chap_ms.h b/lwip/src/include/netif/ppp/chap_ms.h
new file mode 100644
index 0000000..0795291
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/eap.h b/lwip/src/include/netif/ppp/eap.h
new file mode 100644
index 0000000..3ee9aaf
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/ecp.h b/lwip/src/include/netif/ppp/ecp.h
new file mode 100644
index 0000000..5cdce29
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/eui64.h b/lwip/src/include/netif/ppp/eui64.h
new file mode 100644
index 0000000..20ac22e
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/fsm.h b/lwip/src/include/netif/ppp/fsm.h
new file mode 100644
index 0000000..b6915d3
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/ipcp.h b/lwip/src/include/netif/ppp/ipcp.h
new file mode 100644
index 0000000..45f46b3
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/ipv6cp.h b/lwip/src/include/netif/ppp/ipv6cp.h
new file mode 100644
index 0000000..07d1ae3
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/lcp.h b/lwip/src/include/netif/ppp/lcp.h
new file mode 100644
index 0000000..12e2a05
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/magic.h b/lwip/src/include/netif/ppp/magic.h
new file mode 100644
index 0000000..a2a9b53
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/mppe.h b/lwip/src/include/netif/ppp/mppe.h
new file mode 100644
index 0000000..1ae8a5d
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/polarssl/arc4.h b/lwip/src/include/netif/ppp/polarssl/arc4.h
new file mode 100644
index 0000000..4af724c
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/polarssl/des.h b/lwip/src/include/netif/ppp/polarssl/des.h
new file mode 100644
index 0000000..e893890
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/polarssl/md4.h b/lwip/src/include/netif/ppp/polarssl/md4.h
new file mode 100644
index 0000000..5704456
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/polarssl/md5.h b/lwip/src/include/netif/ppp/polarssl/md5.h
new file mode 100644
index 0000000..1244011
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/polarssl/sha1.h b/lwip/src/include/netif/ppp/polarssl/sha1.h
new file mode 100644
index 0000000..a4c53e0
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/ppp.h b/lwip/src/include/netif/ppp/ppp.h
new file mode 100644
index 0000000..d9ea097
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/ppp_impl.h b/lwip/src/include/netif/ppp/ppp_impl.h
new file mode 100644
index 0000000..d63db0b
--- /dev/null
+++ b/lwip/src/include/netif/ppp/ppp_impl.h
@@ -0,0 +1,626 @@
+/*****************************************************************************
+* 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);
+
+
+/*
+ * 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/lwip/src/include/netif/ppp/ppp_opts.h b/lwip/src/include/netif/ppp/ppp_opts.h
new file mode 100644
index 0000000..c987c11
--- /dev/null
+++ b/lwip/src/include/netif/ppp/ppp_opts.h
@@ -0,0 +1,604 @@
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF 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
+
+/**
+ * PPP_NUM_TIMEOUTS_PER_PCB: the number of sys_timeouts running in parallel per
+ * ppp_pcb. This is a conservative default which needs to be checked...
+ */
+#ifndef PPP_NUM_TIMEOUTS_PER_PCB
+#define PPP_NUM_TIMEOUTS_PER_PCB 6
+#endif
+
+/* The number of sys_timeouts required for the PPP module */
+#define PPP_NUM_TIMEOUTS (PPP_SUPPORT * PPP_NUM_TIMEOUTS_PER_PCB * MEMP_NUM_PPP_PCB)
+
+#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/lwip/src/include/netif/ppp/pppapi.h b/lwip/src/include/netif/ppp/pppapi.h
new file mode 100644
index 0000000..913d93f
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/pppcrypt.h b/lwip/src/include/netif/ppp/pppcrypt.h
new file mode 100644
index 0000000..a7b2099
--- /dev/null
+++ b/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/lwip/src/netif/ppp/pppdebug.h b/lwip/src/include/netif/ppp/pppdebug.h
index 8134997..7ead045 100644
--- a/lwip/src/netif/ppp/pppdebug.h
+++ b/lwip/src/include/netif/ppp/pppdebug.h
@@ -33,6 +33,10 @@
*
*****************************************************************************
*/
+
+#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
@@ -45,29 +49,32 @@
#define LOG_DETAIL (PPP_DEBUG)
#define LOG_DEBUG (PPP_DEBUG)
-
-#define TRACELCP PPP_DEBUG
-
#if PPP_DEBUG
-#define AUTHDEBUG(a, b) LWIP_DEBUGF(a, b)
-#define IPCPDEBUG(a, b) LWIP_DEBUGF(a, b)
-#define UPAPDEBUG(a, b) LWIP_DEBUGF(a, b)
-#define LCPDEBUG(a, b) LWIP_DEBUGF(a, b)
-#define FSMDEBUG(a, b) LWIP_DEBUGF(a, b)
-#define CHAPDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define 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 AUTHDEBUG(a, b)
-#define IPCPDEBUG(a, b)
-#define UPAPDEBUG(a, b)
-#define LCPDEBUG(a, b)
-#define FSMDEBUG(a, b)
-#define CHAPDEBUG(a, b)
+#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/lwip/src/include/netif/ppp_oe.h b/lwip/src/include/netif/ppp/pppoe.h
index e1cdfa5..9f8f289 100644
--- a/lwip/src/include/netif/ppp_oe.h
+++ b/lwip/src/include/netif/ppp/pppoe.h
@@ -1,5 +1,5 @@
/*****************************************************************************
-* ppp_oe.h - PPP Over Ethernet implementation for lwIP.
+* pppoe.h - PPP Over Ethernet implementation for lwIP.
*
* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc.
*
@@ -67,22 +67,22 @@
* 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 "lwip/opt.h"
-
-#if PPPOE_SUPPORT > 0
-
-#include "netif/etharp.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_FIELD(u8_t vertype);
- PACK_STRUCT_FIELD(u8_t code);
+ 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;
@@ -109,7 +109,6 @@ PACK_STRUCT_END
#define PPPOE_STATE_PADI_SENT 1
#define PPPOE_STATE_PADR_SENT 2
#define PPPOE_STATE_SESSION 3
-#define PPPOE_STATE_CLOSING 4
/* passive */
#define PPPOE_STATE_PADO_SENT 1
@@ -133,13 +132,6 @@ PACK_STRUCT_END
#define PPPOE_CODE_PADS 0x65 /* Active Discovery Session confirmation */
#define PPPOE_CODE_PADT 0xA7 /* Active Discovery Terminate */
-#ifndef ETHERMTU
-#define ETHERMTU 1500
-#endif
-
-/* two byte PPP protocol discriminator, then IP data */
-#define PPPOE_MAXMTU (ETHERMTU-PPPOE_HEADERLEN-2)
-
#ifndef PPPOE_MAX_AC_COOKIE_LEN
#define PPPOE_MAX_AC_COOKIE_LEN 64
#endif
@@ -147,44 +139,41 @@ PACK_STRUCT_END
struct pppoe_softc {
struct pppoe_softc *next;
struct netif *sc_ethif; /* ethernet interface we are using */
- int sc_pd; /* ppp unit number */
- void (*sc_linkStatusCB)(int pd, int up);
+ ppp_pcb *pcb; /* PPP PCB */
- int sc_state; /* discovery phase or session connected */
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
- char *sc_service_name; /* if != NULL: requested name of service */
- char *sc_concentrator_name; /* if != NULL: requested concentrator id */
+ 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 */
- size_t sc_ac_cookie_len; /* length of cookie data */
+ 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 */
- size_t sc_hunique_len; /* length of host unique */
+ u8_t sc_hunique_len; /* length of host unique */
#endif
- int sc_padi_retried; /* number of PADI retries already done */
- int sc_padr_retried; /* number of PADR retries already done */
+ 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 */
-err_t pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr);
-err_t pppoe_destroy(struct netif *ifp);
-
-int pppoe_connect(struct pppoe_softc *sc);
-void pppoe_disconnect(struct pppoe_softc *sc);
+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);
-err_t pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb);
-
-/** used in ppp.c */
-#define PPPOE_HDRLEN (sizeof(struct eth_hdr) + PPPOE_HEADERLEN)
-
-#endif /* PPPOE_SUPPORT */
-
#endif /* PPP_OE_H */
+
+#endif /* PPP_SUPPORT && PPPOE_SUPPORT */
diff --git a/lwip/src/include/netif/ppp/pppol2tp.h b/lwip/src/include/netif/ppp/pppol2tp.h
new file mode 100644
index 0000000..f03950e
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/pppos.h b/lwip/src/include/netif/ppp/pppos.h
new file mode 100644
index 0000000..d924a9f
--- /dev/null
+++ b/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/lwip/src/include/netif/ppp/upap.h b/lwip/src/include/netif/ppp/upap.h
new file mode 100644
index 0000000..7da792e
--- /dev/null
+++ b/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/lwip/src/netif/ppp/vj.h b/lwip/src/include/netif/ppp/vj.h
index fad1213..7f389c8 100644
--- a/lwip/src/netif/ppp/vj.h
+++ b/lwip/src/include/netif/ppp/vj.h
@@ -22,11 +22,14 @@
* - 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/tcp_impl.h"
+#include "lwip/priv/tcp_priv.h"
#define MAX_SLOTS 16 /* must be > 2 and < 256 */
#define MAX_HDR 128
@@ -43,7 +46,7 @@
* 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
@@ -104,9 +107,9 @@
*/
struct cstate {
struct cstate *cs_next; /* next most recently used state (xmit only) */
- u_short cs_hlen; /* size of hdr (receive only) */
- u_char cs_id; /* connection # associated with this state */
- u_char cs_filler;
+ 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 */
@@ -117,14 +120,14 @@ struct cstate {
struct vjstat {
- unsigned long vjs_packets; /* outbound packets */
- unsigned long vjs_compressed; /* outbound compressed packets */
- unsigned long vjs_searches; /* searches for connection state */
- unsigned long vjs_misses; /* times couldn't find conn. state */
- unsigned long vjs_uncompressedin; /* inbound uncompressed packets */
- unsigned long vjs_compressedin; /* inbound compressed packets */
- unsigned long vjs_errorin; /* inbound unknown type packets */
- unsigned long vjs_tossed; /* inbound packets tossed because of error */
+ 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 */
};
/*
@@ -132,11 +135,11 @@ struct vjstat {
*/
struct vjcompress {
struct cstate *last_cs; /* most recently used tstate */
- u_char last_recv; /* last rcvd conn. id */
- u_char last_xmit; /* last sent conn. id */
- u_short flags;
- u_char maxSlotIndex;
- u_char compressSlot; /* Flag indicating OK to compress slot ID. */
+ 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
@@ -148,9 +151,11 @@ struct vjcompress {
#define VJF_TOSS 1U /* tossing rcvd frames because of input err */
extern void vj_compress_init (struct vjcompress *comp);
-extern u_int vj_compress_tcp (struct vjcompress *comp, struct pbuf *pb);
+extern 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/lwip/src/include/netif/slipif.h b/lwip/src/include/netif/slipif.h
index 7b6ce5e..65ba31f 100644
--- a/lwip/src/include/netif/slipif.h
+++ b/lwip/src/include/netif/slipif.h
@@ -1,38 +1,44 @@
+/**
+ * @file
+ *
+ * SLIP netif API
+ */
+
/*
* Copyright (c) 2001, Swedish Institute of Computer Science.
- * All rights reserved.
+ * All rights reserved.
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce 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.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
-#ifndef __NETIF_SLIPIF_H__
-#define __NETIF_SLIPIF_H__
+#ifndef LWIP_HDR_NETIF_SLIPIF_H
+#define LWIP_HDR_NETIF_SLIPIF_H
#include "lwip/opt.h"
#include "lwip/netif.h"
@@ -47,7 +53,7 @@
/** Set this to 1 to enable functions to pass in RX bytes from ISR context.
* If enabled, slipif_received_byte[s]() process incoming bytes and put assembled
* packets on a queue, which is fed into lwIP from slipif_poll().
- * If disabled, slipif_poll() polls the serila line (using sio_tryread()).
+ * If disabled, slipif_poll() polls the serial line (using sio_tryread()).
*/
#ifndef SLIP_RX_FROM_ISR
#define SLIP_RX_FROM_ISR 0
@@ -76,6 +82,6 @@ void slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len);
#ifdef __cplusplus
}
#endif
-
-#endif
+
+#endif /* LWIP_HDR_NETIF_SLIPIF_H */
diff --git a/lwip/src/netif/FILES b/lwip/src/netif/FILES
index 099dbf3..a3ff431 100644
--- a/lwip/src/netif/FILES
+++ b/lwip/src/netif/FILES
@@ -2,28 +2,23 @@ This directory contains generic network interface device drivers that
do not contain any hardware or architecture specific code. The files
are:
-etharp.c
- Implements the ARP (Address Resolution Protocol) over
- Ethernet. The code in this file should be used together with
- Ethernet device drivers. Note that this module has been
- largely made Ethernet independent so you should be able to
- adapt this for other link layers (such as Firewire).
+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.
-loopif.c
- A "loopback" network interface driver. It requires configuration
- through the define LWIP_LOOPIF_MULTITHREADING (see opt.h).
+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 PPP stack has been ported from ucip (http://ucip.sourceforge.net).
- It matches quite well to pppd 2.3.1 (http://ppp.samba.org), although
- compared to that, it has some modifications for embedded systems and
- the source code has been reordered a bit. \ No newline at end of file
+ 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/lwip/src/netif/bridgeif.c b/lwip/src/netif/bridgeif.c
new file mode 100644
index 0000000..3bab10e
--- /dev/null
+++ b/lwip/src/netif/bridgeif.c
@@ -0,0 +1,743 @@
+/**
+ * @file
+ * lwIP netif implementing an IEEE 802.1D MAC Bridge
+ */
+
+/*
+ * Copyright (c) 2017 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 <goldsimon@gmx.de>
+ *
+ */
+
+/**
+ * @defgroup bridgeif IEEE 802.1D bridge
+ * @ingroup netifs
+ * This file implements an IEEE 802.1D bridge by using a multilayer netif approach
+ * (one hardware-independent netif for the bridge that uses hardware netifs for its ports).
+ * On transmit, the bridge selects the outgoing port(s).
+ * On receive, the port netif calls into the bridge (via its netif->input function) and
+ * the bridge selects the port(s) (and/or its netif->input function) to pass the received pbuf to.
+ *
+ * Usage:
+ * - add the port netifs just like you would when using them as dedicated netif without a bridge
+ * - only NETIF_FLAG_ETHARP/NETIF_FLAG_ETHERNET netifs are supported as bridge ports
+ * - add the bridge port netifs without IPv4 addresses (i.e. pass 'NULL, NULL, NULL')
+ * - don't add IPv6 addresses to the port netifs!
+ * - set up the bridge configuration in a global variable of type 'bridgeif_initdata_t' that contains
+ * - the MAC address of the bridge
+ * - some configuration options controlling the memory consumption (maximum number of ports
+ * and FDB entries)
+ * - e.g. for a bridge MAC address 00-01-02-03-04-05, 2 bridge ports, 1024 FDB entries + 16 static MAC entries:
+ * bridgeif_initdata_t mybridge_initdata = BRIDGEIF_INITDATA1(2, 1024, 16, ETH_ADDR(0, 1, 2, 3, 4, 5));
+ * - add the bridge netif (with IPv4 config):
+ * struct netif bridge_netif;
+ * netif_add(&bridge_netif, &my_ip, &my_netmask, &my_gw, &mybridge_initdata, bridgeif_init, tcpip_input);
+ * NOTE: the passed 'input' function depends on BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT setting,
+ * which controls where the forwarding is done (netif low level input context vs. tcpip_thread)
+ * - set up all ports netifs and the bridge netif
+ *
+ * - When adding a port netif, NETIF_FLAG_ETHARP flag will be removed from a port
+ * to prevent ETHARP working on that port netif (we only want one IP per bridge not per port).
+ * - When adding a port netif, its input function is changed to call into the bridge.
+ *
+ *
+ * @todo:
+ * - compact static FDB entries (instead of walking the whole array)
+ * - add FDB query/read access
+ * - add FDB change callback (when learning or dropping auto-learned entries)
+ * - prefill FDB with MAC classes that should never be forwarded
+ * - multicast snooping? (and only forward group addresses to interested ports)
+ * - support removing ports
+ * - check SNMP integration
+ * - VLAN handling / trunk ports
+ * - priority handling? (although that largely depends on TX queue limitations and lwIP doesn't provide tx-done handling)
+ */
+
+#include "netif/bridgeif.h"
+#include "lwip/netif.h"
+#include "lwip/sys.h"
+#include "lwip/etharp.h"
+#include "lwip/ethip6.h"
+#include "lwip/snmp.h"
+#include "lwip/timeouts.h"
+#include <string.h>
+
+#if LWIP_NUM_NETIF_CLIENT_DATA
+
+#define BRIDGEIF_AGE_TIMER_MS 1000
+
+#if BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT
+#ifndef BRIDGEIF_DECL_PROTECT
+/* define bridgeif protection to sys_arch_protect... */
+#include "lwip/sys.h"
+#define BRIDGEIF_DECL_PROTECT(lev) SYS_ARCH_DECL_PROTECT(lev)
+#define BRIDGEIF_READ_PROTECT(lev) SYS_ARCH_PROTECT(lev)
+#define BRIDGEIF_READ_UNPROTECT(lev) SYS_ARCH_UNPROTECT(lev)
+#define BRIDGEIF_WRITE_PROTECT(lev)
+#define BRIDGEIF_WRITE_UNPROTECT(lev)
+#endif
+#else /* BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT */
+#include "lwip/tcpip.h"
+#define BRIDGEIF_DECL_PROTECT(lev)
+#define BRIDGEIF_READ_PROTECT(lev)
+#define BRIDGEIF_READ_UNPROTECT(lev)
+#define BRIDGEIF_WRITE_PROTECT(lev)
+#define BRIDGEIF_WRITE_UNPROTECT(lev)
+#endif /* BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT */
+
+/* Define those to better describe your network interface. */
+#define IFNAME0 'b'
+#define IFNAME1 'r'
+
+#define BR_FDB_TIMEOUT_SEC (60*5) /* 5 minutes FDB timeout */
+
+#if !BRIDGEIF_EXTERNAL_FDB
+typedef struct bridgeif_dfdb_entry_s {
+ u8_t used;
+ u8_t port;
+ u32_t ts;
+ struct eth_addr addr;
+} bridgeif_dfdb_entry_t;
+
+typedef struct bridgeif_dfdb_s {
+ u16_t max_fdb_entries;
+ bridgeif_dfdb_entry_t *fdb;
+} bridgeif_dfdb_t;
+#endif /* BRIDGEIF_EXTERNAL_FDB */
+
+struct bridgeif_private_s;
+typedef struct bridgeif_port_private_s {
+ struct bridgeif_private_s *bridge;
+ struct netif *port_netif;
+ u8_t port_num;
+} bridgeif_port_t;
+
+typedef struct bridgeif_fdb_static_entry_s {
+ u8_t used;
+ bridgeif_portmask_t dst_ports;
+ struct eth_addr addr;
+} bridgeif_fdb_static_entry_t;
+
+typedef struct bridgeif_private_s {
+ struct netif *netif;
+ struct eth_addr ethaddr;
+ u8_t max_ports;
+ u8_t num_ports;
+ bridgeif_port_t *ports;
+ u16_t max_fdbs_entries;
+ bridgeif_fdb_static_entry_t *fdbs;
+ u16_t max_fdbd_entries;
+ void *fdbd;
+} bridgeif_private_t;
+
+/* netif data index to get the bridge on input */
+u8_t bridgeif_netif_client_id = 0xff;
+
+#if !BRIDGEIF_EXTERNAL_FDB
+/** A real simple and slow implementation of an auto-learning forwarding database that
+ * remembers known src mac addresses to know which port to send frames destined for that
+ * mac address.
+ *
+ * ATTENTION: This is meant as an example only, in real-world use, you should override this
+ * by setting BRIDGEIF_EXTERNAL_FDB==1 and providing a better implementation :-)
+ */
+static void
+bridgeif_fdb_update_src(void *fdb_ptr, struct eth_addr *src_addr, u8_t port_idx)
+{
+ int i;
+ bridgeif_dfdb_t *fdb = (bridgeif_dfdb_t*)fdb_ptr;
+ BRIDGEIF_DECL_PROTECT(lev);
+ BRIDGEIF_READ_PROTECT(lev);
+ for (i = 0; i < fdb->max_fdb_entries; i++) {
+ bridgeif_dfdb_entry_t *e = &fdb->fdb[i];
+ if (e->used && e->ts) {
+ if (!memcmp(&e->addr, src_addr, sizeof(struct eth_addr))) {
+ LWIP_DEBUGF(BRIDGEIF_FDB_DEBUG, ("br: update src %02x:%02x:%02x:%02x:%02x:%02x (from %d) @ idx %d\n",
+ src_addr->addr[0], src_addr->addr[1], src_addr->addr[2], src_addr->addr[3], src_addr->addr[4], src_addr->addr[5],
+ port_idx, i));
+ BRIDGEIF_WRITE_PROTECT(lev);
+ e->ts = BR_FDB_TIMEOUT_SEC;
+ e->port = port_idx;
+ BRIDGEIF_WRITE_UNPROTECT(lev);
+ BRIDGEIF_READ_UNPROTECT(lev);
+ return;
+ }
+ }
+ }
+ /* not found, allocate new entry from free */
+ for (i = 0; i < fdb->max_fdb_entries; i++) {
+ bridgeif_dfdb_entry_t *e = &fdb->fdb[i];
+ if (!e->used || !e->ts) {
+ BRIDGEIF_WRITE_PROTECT(lev);
+ /* check again when protected */
+ if (!e->used || !e->ts) {
+ LWIP_DEBUGF(BRIDGEIF_FDB_DEBUG, ("br: create src %02x:%02x:%02x:%02x:%02x:%02x (from %d) @ idx %d\n",
+ src_addr->addr[0], src_addr->addr[1], src_addr->addr[2], src_addr->addr[3], src_addr->addr[4], src_addr->addr[5],
+ port_idx, i));
+ memcpy(&e->addr, src_addr, sizeof(struct eth_addr));
+ e->ts = BR_FDB_TIMEOUT_SEC;
+ e->port = port_idx;
+ e->used = 1;
+ BRIDGEIF_WRITE_UNPROTECT(lev);
+ BRIDGEIF_READ_UNPROTECT(lev);
+ return;
+ }
+ BRIDGEIF_WRITE_UNPROTECT(lev);
+ }
+ }
+ BRIDGEIF_READ_UNPROTECT(lev);
+ /* not found, no free entry -> flood */
+}
+
+/** Walk our list of auto-learnt fdb entries and return a port to forward or BR_FLOOD if unknown */
+static bridgeif_portmask_t
+bridgeif_fdb_get_dst_ports(void *fdb_ptr, struct eth_addr *dst_addr)
+{
+ int i;
+ bridgeif_dfdb_t *fdb = (bridgeif_dfdb_t*)fdb_ptr;
+ BRIDGEIF_DECL_PROTECT(lev);
+ BRIDGEIF_READ_PROTECT(lev);
+ for (i = 0; i < fdb->max_fdb_entries; i++) {
+ bridgeif_dfdb_entry_t *e = &fdb->fdb[i];
+ if (e->used && e->ts) {
+ if (!memcmp(&e->addr, dst_addr, sizeof(struct eth_addr))) {
+ bridgeif_portmask_t ret = (bridgeif_portmask_t)(1 << e->port);
+ BRIDGEIF_READ_UNPROTECT(lev);
+ return ret;
+ }
+ }
+ }
+ BRIDGEIF_READ_UNPROTECT(lev);
+ return BR_FLOOD;
+}
+
+/** Init our simple fdb list */
+static void*
+bridgeif_fdb_init(u16_t max_fdb_entries)
+{
+ bridgeif_dfdb_t *fdb;
+ size_t alloc_len_sizet = sizeof(bridgeif_dfdb_t) + (max_fdb_entries*sizeof(bridgeif_dfdb_entry_t));
+ mem_size_t alloc_len = (mem_size_t)alloc_len_sizet;
+ LWIP_ASSERT("alloc_len == alloc_len_sizet", alloc_len == alloc_len_sizet);
+ LWIP_DEBUGF(BRIDGEIF_DEBUG, ("bridgeif_fdb_init: allocating %d bytes for private FDB data\n", (int)alloc_len));
+ fdb = (bridgeif_dfdb_t*)mem_calloc(1, alloc_len);
+ if (fdb == NULL) {
+ return NULL;
+ }
+ fdb->max_fdb_entries = max_fdb_entries;
+ fdb->fdb = (bridgeif_dfdb_entry_t *)(fdb + 1);
+ return fdb;
+}
+
+/** Aging implementation of our simple fdb */
+static void
+bridgeif_fdb_age_one_second(void *fdb_ptr)
+{
+ int i;
+ bridgeif_dfdb_t *fdb;
+ BRIDGEIF_DECL_PROTECT(lev);
+
+ fdb = (bridgeif_dfdb_t *)fdb_ptr;
+ BRIDGEIF_READ_PROTECT(lev);
+
+ for (i = 0; i < fdb->max_fdb_entries; i++) {
+ bridgeif_dfdb_entry_t *e = &fdb->fdb[i];
+ if (e->used && e->ts) {
+ BRIDGEIF_WRITE_PROTECT(lev);
+ /* check again when protected */
+ if (e->used && e->ts) {
+ if (--e->ts == 0) {
+ e->used = 0;
+ }
+ }
+ BRIDGEIF_WRITE_UNPROTECT(lev);
+ }
+ }
+ BRIDGEIF_READ_UNPROTECT(lev);
+}
+
+#endif /* !BRIDGEIF_EXTERNAL_FDB */
+
+/** Timer callback for fdb aging, called once per second */
+static void
+bridgeif_age_tmr(void *arg)
+{
+ struct netif *bridgeif;
+ bridgeif_private_t *br;
+ LWIP_ASSERT("invalid arg", arg != NULL);
+ bridgeif = (struct netif *)arg;
+ LWIP_ASSERT("invalid netif state", bridgeif->state != NULL);
+
+ br = (bridgeif_private_t *)bridgeif->state;
+ bridgeif_fdb_age_one_second(br->fdbd);
+ sys_timeout(BRIDGEIF_AGE_TIMER_MS, bridgeif_age_tmr, arg);
+}
+
+/**
+ * @ingroup bridgeif
+ * Add a static entry to the forwarding database.
+ * A static entry marks where frames to a specific eth address (unicast or group address) are
+ * forwarded.
+ * bits [0..(BRIDGEIF_MAX_PORTS-1)]: hw ports
+ * bit [BRIDGEIF_MAX_PORTS]: cpu port
+ * 0: drop
+ */
+err_t
+bridgeif_fdb_add(struct netif *bridgeif, const struct eth_addr *addr, bridgeif_portmask_t ports)
+{
+ int i;
+ bridgeif_private_t *br;
+ BRIDGEIF_DECL_PROTECT(lev);
+ LWIP_ASSERT("invalid netif", bridgeif != NULL);
+ br = (bridgeif_private_t*)bridgeif->state;
+ LWIP_ASSERT("invalid state", br != NULL);
+
+ BRIDGEIF_READ_PROTECT(lev);
+ for (i = 0; i < br->max_fdbs_entries; i++) {
+ if (!br->fdbs[i].used) {
+ BRIDGEIF_WRITE_PROTECT(lev);
+ if (!br->fdbs[i].used) {
+ br->fdbs[i].used = 1;
+ br->fdbs[i].dst_ports = ports;
+ memcpy(&br->fdbs[i].addr, addr, sizeof(struct eth_addr));
+ BRIDGEIF_WRITE_UNPROTECT(lev);
+ BRIDGEIF_READ_UNPROTECT(lev);
+ return ERR_OK;
+ }
+ BRIDGEIF_WRITE_UNPROTECT(lev);
+ }
+ }
+ BRIDGEIF_READ_UNPROTECT(lev);
+ return ERR_MEM;
+}
+
+/**
+ * @ingroup bridgeif
+ * Remove a static entry from the forwarding database
+ */
+err_t
+bridgeif_fdb_remove(struct netif *bridgeif, const struct eth_addr *addr)
+{
+ int i;
+ bridgeif_private_t *br;
+ BRIDGEIF_DECL_PROTECT(lev);
+ LWIP_ASSERT("invalid netif", bridgeif != NULL);
+ br = (bridgeif_private_t*)bridgeif->state;
+ LWIP_ASSERT("invalid state", br != NULL);
+
+ BRIDGEIF_READ_PROTECT(lev);
+ for (i = 0; i < br->max_fdbs_entries; i++) {
+ if (br->fdbs[i].used && !memcmp(&br->fdbs[i].addr, addr, sizeof(struct eth_addr))) {
+ BRIDGEIF_WRITE_PROTECT(lev);
+ if (br->fdbs[i].used && !memcmp(&br->fdbs[i].addr, addr, sizeof(struct eth_addr))) {
+ memset(&br->fdbs[i], 0, sizeof(bridgeif_fdb_static_entry_t));
+ BRIDGEIF_WRITE_UNPROTECT(lev);
+ BRIDGEIF_READ_UNPROTECT(lev);
+ return ERR_OK;
+ }
+ BRIDGEIF_WRITE_UNPROTECT(lev);
+ }
+ }
+ BRIDGEIF_READ_UNPROTECT(lev);
+ return ERR_VAL;
+}
+
+/** Get the forwarding port(s) (as bit mask) for the specified destination mac address */
+static bridgeif_portmask_t
+bridgeif_find_dst_ports(bridgeif_private_t *br, struct eth_addr *dst_addr)
+{
+ int i;
+ BRIDGEIF_DECL_PROTECT(lev);
+ BRIDGEIF_READ_PROTECT(lev);
+ /* first check for static entries */
+ for (i = 0; i < br->max_fdbs_entries; i++) {
+ if (br->fdbs[i].used) {
+ if (!memcmp(&br->fdbs[i].addr, dst_addr, sizeof(struct eth_addr))) {
+ bridgeif_portmask_t ret = br->fdbs[i].dst_ports;
+ BRIDGEIF_READ_UNPROTECT(lev);
+ return ret;
+ }
+ }
+ }
+ if (dst_addr->addr[0] & 1) {
+ /* no match found: flood remaining group address */
+ BRIDGEIF_READ_UNPROTECT(lev);
+ return BR_FLOOD;
+ }
+ BRIDGEIF_READ_UNPROTECT(lev);
+ /* no match found: check dynamic fdb for port or fall back to flooding */
+ return bridgeif_fdb_get_dst_ports(br->fdbd, dst_addr);
+}
+
+/** Helper function to see if a destination mac belongs to the bridge
+ * (bridge netif or one of the port netifs), in which case the frame
+ * is sent to the cpu only.
+ */
+static int
+bridgeif_is_local_mac(bridgeif_private_t *br, struct eth_addr *addr)
+{
+ int i;
+ BRIDGEIF_DECL_PROTECT(lev);
+ if (!memcmp(br->netif->hwaddr, addr, sizeof(struct eth_addr))) {
+ return 1;
+ }
+ BRIDGEIF_READ_PROTECT(lev);
+ for (i = 0; i < br->num_ports; i++) {
+ struct netif *portif = br->ports[i].port_netif;
+ if (portif != NULL) {
+ if (!memcmp(portif->hwaddr, addr, sizeof(struct eth_addr))) {
+ BRIDGEIF_READ_UNPROTECT(lev);
+ return 1;
+ }
+ }
+ }
+ BRIDGEIF_READ_UNPROTECT(lev);
+ return 0;
+}
+
+/* Output helper function */
+static err_t
+bridgeif_send_to_port(bridgeif_private_t *br, struct pbuf *p, u8_t dstport_idx)
+{
+ if (dstport_idx < BRIDGEIF_MAX_PORTS) {
+ /* possibly an external port */
+ if (dstport_idx < br->max_ports) {
+ struct netif *portif = br->ports[dstport_idx].port_netif;
+ if (br->ports[dstport_idx].port_netif != NULL) {
+ if ((portif != NULL) && (portif->linkoutput != NULL)) {
+ /* prevent sending out to rx port */
+ if (netif_get_index(portif) != p->if_idx) {
+ if (netif_is_link_up(portif)) {
+ LWIP_DEBUGF(BRIDGEIF_FW_DEBUG, ("br -> flood(%p:%d) -> %d\n", (void*)p, p->if_idx, netif_get_index(portif)));
+ return portif->linkoutput(portif, p);
+ }
+ }
+ }
+ }
+ }
+ } else {
+ LWIP_ASSERT("invalid port index", dstport_idx == BRIDGEIF_MAX_PORTS);
+ }
+ return ERR_OK;
+}
+
+/** Helper function to pass a pbuf to all ports marked in 'dstports'
+ */
+static err_t
+bridgeif_send_to_ports(bridgeif_private_t *br, struct pbuf *p, bridgeif_portmask_t dstports)
+{
+ err_t err, ret_err = ERR_OK;
+ u8_t i;
+ bridgeif_portmask_t mask = 1;
+ BRIDGEIF_DECL_PROTECT(lev);
+ BRIDGEIF_READ_PROTECT(lev);
+ for (i = 0; i < BRIDGEIF_MAX_PORTS; i++, mask = (bridgeif_portmask_t)(mask << 1)) {
+ if (dstports & mask) {
+ err = bridgeif_send_to_port(br, p, i);
+ if (err != ERR_OK) {
+ ret_err = err;
+ }
+ }
+ }
+ BRIDGEIF_READ_UNPROTECT(lev);
+ return ret_err;
+}
+
+/** Output function of the application port of the bridge (the one with an ip address).
+ * The forwarding port(s) where this pbuf is sent on is/are automatically selected
+ * from the FDB.
+ */
+static err_t
+bridgeif_output(struct netif *netif, struct pbuf *p)
+{
+ err_t err;
+ bridgeif_private_t *br = (bridgeif_private_t*)netif->state;
+ struct eth_addr *dst = (struct eth_addr *)(p->payload);
+
+ bridgeif_portmask_t dstports = bridgeif_find_dst_ports(br, dst);
+ err = bridgeif_send_to_ports(br, p, dstports);
+
+ 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 */
+
+ LINK_STATS_INC(link.xmit);
+
+ return err;
+}
+
+/** The actual bridge input function. Port netif's input is changed to call
+ * here. This function decides where the frame is forwarded.
+ */
+static err_t
+bridgeif_input(struct pbuf *p, struct netif *netif)
+{
+ u8_t rx_idx;
+ bridgeif_portmask_t dstports;
+ struct eth_addr *src, *dst;
+ bridgeif_private_t *br;
+ bridgeif_port_t *port;
+ if (p == NULL || netif == NULL) {
+ return ERR_VAL;
+ }
+ port = (bridgeif_port_t *)netif_get_client_data(netif, bridgeif_netif_client_id);
+ LWIP_ASSERT("port data not set", port != NULL);
+ if (port == NULL || port->bridge == NULL) {
+ return ERR_VAL;
+ }
+ br = (bridgeif_private_t *)port->bridge;
+ rx_idx = netif_get_index(netif);
+ /* store receive index in pbuf */
+ p->if_idx = rx_idx;
+
+ dst = (struct eth_addr*)p->payload;
+ src = (struct eth_addr*)(((u8_t*)p->payload) + sizeof(struct eth_addr));
+
+ if ((src->addr[0] & 1) == 0) {
+ /* update src for all non-group addresses */
+ bridgeif_fdb_update_src(br->fdbd, src, port->port_num);
+ }
+
+ if (dst->addr[0] & 1) {
+ /* group address -> flood + cpu? */
+ dstports = bridgeif_find_dst_ports(br, dst);
+ bridgeif_send_to_ports(br, p, dstports);
+ if (dstports & (1 << BRIDGEIF_MAX_PORTS)) {
+ /* we pass the reference to ->input or have to free it */
+ LWIP_DEBUGF(BRIDGEIF_FW_DEBUG, ("br -> input(%p)\n", (void*)p));
+ if (br->netif->input(p, br->netif) != ERR_OK) {
+ pbuf_free(p);
+ }
+ } else {
+ /* all references done */
+ pbuf_free(p);
+ }
+ /* always return ERR_OK here to prevent the caller freeing the pbuf */
+ return ERR_OK;
+ } else {
+ /* is this for one of the local ports? */
+ if (bridgeif_is_local_mac(br, dst)) {
+ /* yes, send to cpu port only */
+ LWIP_DEBUGF(BRIDGEIF_FW_DEBUG, ("br -> input(%p)\n", (void*)p));
+ return br->netif->input(p, br->netif);
+ }
+
+ /* get dst port */
+ dstports = bridgeif_find_dst_ports(br, dst);
+ bridgeif_send_to_ports(br, p, dstports);
+ /* no need to send to cpu, flooding is for external ports only */
+ /* by this, we consumed the pbuf */
+ pbuf_free(p);
+ /* always return ERR_OK here to prevent the caller freeing the pbuf */
+ return ERR_OK;
+ }
+}
+
+#if !BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT
+/** Input function for port netifs used to synchronize into tcpip_thread.
+ */
+static err_t
+bridgeif_tcpip_input(struct pbuf *p, struct netif *netif)
+{
+ return tcpip_inpkt(p, netif, bridgeif_input);
+}
+#endif /* BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT */
+
+/**
+ * @ingroup bridgeif
+ * Initialization function passed to netif_add().
+ *
+ * ATTENTION: A pointer to a @ref bridgeif_initdata_t must be passed as 'state'
+ * to @ref netif_add when adding the bridge. I supplies MAC address
+ * and controls memory allocation (number of ports, FDB size).
+ *
+ * @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
+bridgeif_init(struct netif *netif)
+{
+ bridgeif_initdata_t *init_data;
+ bridgeif_private_t *br;
+ size_t alloc_len_sizet;
+ mem_size_t alloc_len;
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+#if !BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT
+ if (netif->input == tcpip_input) {
+ LWIP_DEBUGF(BRIDGEIF_DEBUG|LWIP_DBG_ON, ("bridgeif does not need tcpip_input, use netif_input/ethernet_input instead"));
+ }
+#endif
+
+ if (bridgeif_netif_client_id == 0xFF) {
+ bridgeif_netif_client_id = netif_alloc_client_data_id();
+ }
+
+ init_data = (bridgeif_initdata_t *)netif->state;
+ LWIP_ASSERT("init_data != NULL", (init_data != NULL));
+ LWIP_ASSERT("init_data->max_ports <= BRIDGEIF_MAX_PORTS",
+ init_data->max_ports <= BRIDGEIF_MAX_PORTS);
+
+ alloc_len_sizet = sizeof(bridgeif_private_t) + (init_data->max_ports*sizeof(bridgeif_port_t) + (init_data->max_fdb_static_entries*sizeof(bridgeif_fdb_static_entry_t)));
+ alloc_len = (mem_size_t)alloc_len_sizet;
+ LWIP_ASSERT("alloc_len == alloc_len_sizet", alloc_len == alloc_len_sizet);
+ LWIP_DEBUGF(BRIDGEIF_DEBUG, ("bridgeif_init: allocating %d bytes for private data\n", (int)alloc_len));
+ br = (bridgeif_private_t*)mem_calloc(1, alloc_len);
+ if (br == NULL) {
+ LWIP_DEBUGF(NETIF_DEBUG, ("bridgeif_init: out of memory\n"));
+ return ERR_MEM;
+ }
+ memcpy(&br->ethaddr, &init_data->ethaddr, sizeof(br->ethaddr));
+ br->netif = netif;
+
+ br->max_ports = init_data->max_ports;
+ br->ports = (bridgeif_port_t*)(br + 1);
+
+ br->max_fdbs_entries = init_data->max_fdb_static_entries;
+ br->fdbs = (bridgeif_fdb_static_entry_t*)(((u8_t*)(br + 1)) + (init_data->max_ports*sizeof(bridgeif_port_t)));
+
+ br->max_fdbd_entries = init_data->max_fdb_dynamic_entries;
+ br->fdbd = bridgeif_fdb_init(init_data->max_fdb_dynamic_entries);
+ if (br->fdbd == NULL) {
+ LWIP_DEBUGF(NETIF_DEBUG, ("bridgeif_init: out of memory in fdb_init\n"));
+ mem_free(br);
+ 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, 0);
+
+ netif->state = br;
+ 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...) */
+#if LWIP_IPV4
+ netif->output = etharp_output;
+#endif /* LWIP_IPV4 */
+#if LWIP_IPV6
+ netif->output_ip6 = ethip6_output;
+#endif /* LWIP_IPV6 */
+ netif->linkoutput = bridgeif_output;
+
+ /* set MAC hardware address length */
+ netif->hwaddr_len = ETH_HWADDR_LEN;
+
+ /* set MAC hardware address */
+ memcpy(netif->hwaddr, &br->ethaddr, ETH_HWADDR_LEN);
+
+ /* 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_ETHERNET | NETIF_FLAG_IGMP | NETIF_FLAG_MLD6 |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 */
+
+ sys_timeout(BRIDGEIF_AGE_TIMER_MS, bridgeif_age_tmr, netif);
+
+ return ERR_OK;
+}
+
+/**
+ * @ingroup bridgeif
+ * Add a port to the bridge
+ */
+err_t
+bridgeif_add_port(struct netif *bridgeif, struct netif *portif)
+{
+ bridgeif_private_t *br;
+ bridgeif_port_t *port;
+
+ LWIP_ASSERT("bridgeif != NULL", bridgeif != NULL);
+ LWIP_ASSERT("bridgeif->state != NULL", bridgeif->state != NULL);
+ LWIP_ASSERT("portif != NULL", portif != NULL);
+
+ if (!(portif->flags & NETIF_FLAG_ETHARP) || !(portif->flags & NETIF_FLAG_ETHERNET)) {
+ /* can only add ETHERNET/ETHARP interfaces */
+ return ERR_VAL;
+ }
+
+ br = (bridgeif_private_t *)bridgeif->state;
+
+ if (br->num_ports >= br->max_ports) {
+ return ERR_VAL;
+ }
+ port = &br->ports[br->num_ports];
+ port->port_netif = portif;
+ port->port_num = br->num_ports;
+ port->bridge = br;
+ br->num_ports++;
+
+ /* let the port call us on input */
+#if BRIDGEIF_PORT_NETIFS_OUTPUT_DIRECT
+ portif->input = bridgeif_input;
+#else
+ portif->input = bridgeif_tcpip_input;
+#endif
+ /* store pointer to bridge in netif */
+ netif_set_client_data(portif, bridgeif_netif_client_id, port);
+ /* remove ETHARP flag to prevent sending report events on netif-up */
+ netif_clear_flags(portif, NETIF_FLAG_ETHARP);
+
+ return ERR_OK;
+}
+
+#endif /* LWIP_NUM_NETIF_CLIENT_DATA */
diff --git a/lwip/src/netif/ethernet.c b/lwip/src/netif/ethernet.c
new file mode 100644
index 0000000..65bcdc9
--- /dev/null
+++ b/lwip/src/netif/ethernet.c
@@ -0,0 +1,318 @@
+/**
+ * @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
+ u16_t next_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;
+ }
+
+ if (p->if_idx == NETIF_NO_INDEX) {
+ p->if_idx = netif_get_index(netif);
+ }
+
+ /* points to packet payload, which starts with an Ethernet header */
+ ethhdr = (struct eth_hdr *)p->payload;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
+ ("ethernet_input: dest:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", src:%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F":%"X8_F", type:%"X16_F"\n",
+ (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;
+ next_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 < next_hdr_offset) || pbuf_remove_header(p, next_hdr_offset)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("ethernet_input: IPv4 packet dropped, too short (%"U16_F"/%"U16_F")\n",
+ p->tot_len, next_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 < next_hdr_offset) || pbuf_remove_header(p, next_hdr_offset)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("ethernet_input: ARP response packet dropped, too short (%"U16_F"/%"U16_F")\n",
+ p->tot_len, next_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 < next_hdr_offset) || pbuf_remove_header(p, next_hdr_offset)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("ethernet_input: IPv6 packet dropped, too short (%"U16_F"/%"U16_F")\n",
+ p->tot_len, next_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_add_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_add_header(p, SIZEOF_ETH_HDR) != 0) {
+ goto pbuf_header_failed;
+ }
+ }
+
+ ethhdr = (struct eth_hdr*)p->payload;
+ ethhdr->type = eth_type_be;
+ SMEMCPY(&ethhdr->dest, dst, ETH_HWADDR_LEN);
+ SMEMCPY(&ethhdr->src, src, ETH_HWADDR_LEN);
+
+ 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/lwip/src/netif/ethernetif.c b/lwip/src/netif/ethernetif.c
index 46900bd..e2bf982 100644
--- a/lwip/src/netif/ethernetif.c
+++ b/lwip/src/netif/ethernetif.c
@@ -6,9 +6,9 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification,
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
* are permitted provided that the following conditions are met:
*
* 1. Redistributions of source code must retain the above copyright notice,
@@ -17,21 +17,21 @@
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote products
- * derived from this software without specific prior written permission.
+ * derived from this software without specific prior written permission.
*
- * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
- * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
- * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
- * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
- * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
* OF SUCH DAMAGE.
*
* This file is part of the lwIP TCP/IP stack.
- *
+ *
* Author: Adam Dunkels <adam@sics.se>
*
*/
@@ -53,8 +53,8 @@
#include "lwip/stats.h"
#include "lwip/snmp.h"
#include "lwip/ethip6.h"
-#include "netif/etharp.h"
-#include "netif/ppp_oe.h"
+#include "lwip/etharp.h"
+#include "netif/ppp/pppoe.h"
/* Define those to better describe your network interface. */
#define IFNAME0 'e'
@@ -85,7 +85,7 @@ static void
low_level_init(struct netif *netif)
{
struct ethernetif *ethernetif = netif->state;
-
+
/* set MAC hardware address length */
netif->hwaddr_len = ETHARP_HWADDR_LEN;
@@ -96,12 +96,25 @@ low_level_init(struct netif *netif)
/* maximum transfer unit */
netif->mtu = 1500;
-
+
/* device capabilities */
/* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
-
- /* Do whatever else is needed to initialize interface. */
+
+#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. */
}
/**
@@ -116,7 +129,7 @@ low_level_init(struct netif *netif)
*
* @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
* strange results. You might consider waiting for space in the DMA queue
- * to become availale since the stack doesn't retry to send a packet
+ * to become available since the stack doesn't retry to send a packet
* dropped because of memory failure (except for the TCP timers).
*/
@@ -127,12 +140,12 @@ low_level_output(struct netif *netif, struct pbuf *p)
struct pbuf *q;
initiate transfer();
-
+
#if ETH_PAD_SIZE
- pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
+ pbuf_remove_header(p, ETH_PAD_SIZE); /* drop the padding word */
#endif
- for(q = p; q != NULL; q = q->next) {
+ 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. */
@@ -141,10 +154,20 @@ low_level_output(struct netif *netif, struct pbuf *p)
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 */
+ pbuf_add_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif
-
+
LINK_STATS_INC(link.xmit);
return ERR_OK;
@@ -175,16 +198,16 @@ low_level_input(struct netif *netif)
/* 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 */
+ pbuf_remove_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) {
+ 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.
@@ -197,8 +220,16 @@ low_level_input(struct netif *netif)
}
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 */
+ pbuf_add_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
#endif
LINK_STATS_INC(link.recv);
@@ -206,9 +237,10 @@ low_level_input(struct netif *netif)
drop packet();
LINK_STATS_INC(link.memerr);
LINK_STATS_INC(link.drop);
+ MIB2_STATS_NETIF_INC(netif, ifindiscards);
}
- return p;
+ return p;
}
/**
@@ -231,33 +263,14 @@ ethernetif_input(struct netif *netif)
/* move received packet into a new pbuf */
p = low_level_input(netif);
- /* no packet could be read, silently ignore this */
- if (p == NULL) return;
- /* points to packet payload, which starts with an Ethernet header */
- ethhdr = p->payload;
-
- switch (htons(ethhdr->type)) {
- /* IP or ARP packet? */
- case ETHTYPE_IP:
- case ETHTYPE_IPV6:
- case ETHTYPE_ARP:
-#if PPPOE_SUPPORT
- /* PPPoE packet? */
- case ETHTYPE_PPPOEDISC:
- case ETHTYPE_PPPOE:
-#endif /* PPPOE_SUPPORT */
- /* full packet send to tcpip_thread to process */
- if (netif->input(p, netif)!=ERR_OK)
- { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
- pbuf_free(p);
- p = NULL;
- }
- break;
-
- default:
- pbuf_free(p);
- p = NULL;
- break;
+ /* 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;
+ }
}
}
@@ -279,7 +292,7 @@ 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"));
@@ -296,7 +309,7 @@ ethernetif_init(struct netif *netif)
* The last argument should be replaced with your link speed, in units
* of bits per second.
*/
- NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
+ MIB2_INIT_NETIF(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
netif->state = ethernetif;
netif->name[0] = IFNAME0;
@@ -305,14 +318,16 @@ ethernetif_init(struct netif *netif)
* 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...) */
+#if LWIP_IPV4
netif->output = etharp_output;
+#endif /* LWIP_IPV4 */
#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);
diff --git a/lwip/src/netif/lowpan6.c b/lwip/src/netif/lowpan6.c
new file mode 100644
index 0000000..603cde0
--- /dev/null
+++ b/lwip/src/netif/lowpan6.c
@@ -0,0 +1,1208 @@
+/**
+ * @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
+ * @ingroup netifs
+ * 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_packed(ip_data.current_iphdr_dest, ip6hdr->dest);
+ ip6_addr_assign_zone(ip_2_ip6(&ip_data.current_iphdr_dest), IP6_UNKNOWN, netif);
+ ip_addr_copy_from_ip6_packed(ip_data.current_iphdr_src, ip6hdr->src);
+ ip6_addr_assign_zone(ip_2_ip6(&ip_data.current_iphdr_src), IP6_UNKNOWN, netif);
+
+ /* 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_remove_header(p, IP6_HLEN);
+
+#if LWIP_UDP
+ /* 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_remove_header(p, UDP_HLEN);
+ }
+#endif /* LWIP_UDP */
+ }
+
+#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_ZONECHECK(context);
+
+ 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_copy_from_packed(ip6_src, ip6_hdr->src);
+ ip6_addr_assign_zone(&ip6_src, IP6_UNICAST, netif);
+ 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_zoneless(&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;
+ u16_t lowpan6_offset;
+ struct ip6_hdr *ip6hdr;
+ s8_t i;
+ s8_t ip6_offset = IP6_HLEN;
+
+#if LWIP_UDP
+#define UDP_HLEN_ALLOC UDP_HLEN
+#else
+#define UDP_HLEN_ALLOC 0
+#endif
+
+ q = pbuf_alloc(PBUF_IP, p->len + IP6_HLEN + UDP_HLEN_ALLOC, 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++] << 16));
+ 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 LWIP_UDP
+ 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
+#endif /* LWIP_UDP */
+ {
+ /* @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_remove_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_remove_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_remove_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_remove_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_remove_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/lwip/src/netif/ppp/PPPD_FOLLOWUP b/lwip/src/netif/ppp/PPPD_FOLLOWUP
new file mode 100644
index 0000000..c231982
--- /dev/null
+++ b/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/lwip/src/netif/ppp/auth.c b/lwip/src/netif/ppp/auth.c
index 0fd87a3..c8673ad 100644
--- a/lwip/src/netif/ppp/auth.c
+++ b/lwip/src/netif/ppp/auth.c
@@ -1,1198 +1,2228 @@
-/*****************************************************************************
-* auth.c - Network Authentication and Phase Control program file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 97-12-08 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
-* Ported from public pppd code.
-*****************************************************************************/
/*
* auth.c - PPP authentication and phase control.
*
- * Copyright (c) 1993 The Australian National University.
- * All rights reserved.
+ * Copyright (c) 1993-2002 Paul Mackerras. All rights reserved.
*
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the Australian National University. The name of the University
- * may not be used to endorse or promote products derived from this
- * software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
*
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
*
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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 "lwip/opt.h"
-
+#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
-#include "ppp_impl.h"
-#include "pppdebug.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 "fsm.h"
-#include "lcp.h"
-#include "pap.h"
-#include "chap.h"
-#include "auth.h"
-#include "ipcp.h"
+#include <netdb.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
-#if CBCP_SUPPORT
-#include "cbcp.h"
-#endif /* CBCP_SUPPORT */
+#ifdef HAS_SHADOW
+#include <shadow.h>
+#ifndef PW_PPP
+#define PW_PPP PW_LOGIN
+#endif
+#endif
-#include "lwip/inet.h"
+#include <time.h>
+#endif /* UNUSED */
-#include <string.h>
+#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 */
-/* Bits in scan_authfile return value */
-#define NONWILD_SERVER 1
-#define NONWILD_CLIENT 2
-
-#define ISWILD(word) (word[0] == '*' && word[1] == 0)
+#include "session.h"
#endif /* UNUSED */
-#if PAP_SUPPORT || CHAP_SUPPORT
-/* The name by which the peer authenticated itself to us. */
-static char peer_authname[MAXNAMELEN];
-#endif /* PAP_SUPPORT || CHAP_SUPPORT */
-
-/* Records which authentication operations haven't completed yet. */
-static int auth_pending[NUM_PPP];
-
-/* Set if we have successfully called plogin() */
-static int logged_in;
+#if 0 /* UNUSED */
+/* Bits in scan_authfile return value */
+#define NONWILD_SERVER 1
+#define NONWILD_CLIENT 2
-/* Set if we have run the /etc/ppp/auth-up script. */
-static int did_authup; /* @todo, we don't need this in lwip*/
+#define ISWILD(word) (word[0] == '*' && word[1] == 0)
+#endif /* UNUSED */
+#if 0 /* UNUSED */
/* List of addresses which the peer may use. */
-static struct wordlist *addresses[NUM_PPP];
+static struct permitted_ip *addresses[NUM_PPP];
-#if 0 /* UNUSED */
/* 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 */
-/* Number of network protocols which we have opened. */
-static int num_np_open;
-
-/* Number of network protocols which have come up. */
-static int num_np_up;
-
-#if PAP_SUPPORT || CHAP_SUPPORT
-/* Set if we got the contents of passwd[] from the pap-secrets file. */
-static int passwd_from_file;
-#endif /* PAP_SUPPORT || CHAP_SUPPORT */
-
#if 0 /* UNUSED */
/* Set if we require authentication only because we have a default route. */
static bool default_auth;
/* Hook to enable a plugin to control the idle time limit */
-int (*idle_time_hook) __P((struct ppp_idle *)) = NULL;
+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) __P((void)) = NULL;
+int (*pap_check_hook) (void) = NULL;
/* Hook for a plugin to check the PAP user and password */
-int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp,
- struct wordlist **paddrs,
- struct wordlist **popts)) = NULL;
+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) __P((void)) = NULL;
+void (*pap_logout_hook) (void) = NULL;
/* Hook for a plugin to get the PAP password for authenticating us */
-int (*pap_passwd_hook) __P((char *user, char *passwd)) = NULL;
+int (*pap_passwd_hook) (char *user, char *passwd) = NULL;
-/*
- * This is used to ensure that we don't start an auth-up/down
- * script while one is already running.
- */
-enum script_state {
- s_down,
- s_up
-};
+/* Hook for a plugin to say if we can possibly authenticate a peer using CHAP */
+int (*chap_check_hook) (void) = NULL;
-static enum script_state auth_state = s_down;
-static enum script_state auth_script_state = s_down;
-static pid_t auth_script_pid = 0;
+/* Hook for a plugin to get the CHAP password for authenticating us */
+int (*chap_passwd_hook) (char *user, char *passwd) = NULL;
-/*
- * Option variables.
- * lwip: some of these are present in the ppp_settings structure
- */
-bool uselogin = 0; /* Use /etc/passwd for checking PAP */
-bool cryptpap = 0; /* Passwords in pap-secrets are encrypted */
-bool refuse_pap = 0; /* Don't wanna auth. ourselves with PAP */
-bool refuse_chap = 0; /* Don't wanna auth. ourselves with CHAP */
-bool usehostname = 0; /* Use hostname for our_name */
-bool auth_required = 0; /* Always require authentication from peer */
-bool allow_any_ip = 0; /* Allow peer to use any IP address */
-bool explicit_remote = 0; /* User specified explicit remote name */
-char remote_name[MAXNAMELEN]; /* Peer's name for authentication */
+/* 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 */
-/* Bits in auth_pending[] */
-#define PAP_WITHPEER 1
-#define PAP_PEER 2
-#define CHAP_WITHPEER 4
-#define CHAP_PEER 8
-
-/* @todo, move this somewhere */
-/* Used for storing a sequence of words. Usually malloced. */
-struct wordlist {
- struct wordlist *next;
- char word[1];
-};
+#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;
-extern char *crypt (const char *, const char *);
+/* 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 (int);
-static void check_idle (void *);
-static void connect_time_expired (void *);
-#if 0
-static int plogin (char *, char *, char **, int *);
-#endif
-static void plogout (void);
+static 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 (int, char *, char *);
-static int have_pap_secret (void);
-static int have_chap_secret (char *, char *, u32_t);
-static int ip_addr_check (u32_t, struct wordlist *);
-
-#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */
+/* static int 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 *);
+ struct wordlist **, struct wordlist **,
+ char *, int);
static void free_wordlist (struct wordlist *);
-static void auth_script (char *);
-static void auth_script_done (void *);
-static void set_allowed_addrs (int unit, struct wordlist *addrs);
+static 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 *);
-#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */
+static int wordlist_count (struct wordlist *);
+#endif /* UNUSED */
-#if 0 /* 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", 1, &auth_required },
+ "Require PAP authentication from peer",
+ OPT_PRIOSUB | 1, &auth_required },
{ "+pap", o_bool, &lcp_wantoptions[0].neg_upap,
- "Require PAP authentication from peer", 1, &auth_required },
+ "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", 1 },
- { "require-chap", o_bool, &lcp_wantoptions[0].neg_chap,
- "Require CHAP authentication from peer", 1, &auth_required },
- { "+chap", o_bool, &lcp_wantoptions[0].neg_chap,
- "Require CHAP authentication from peer", 1, &auth_required },
+ "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", 1 },
+ "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", 1 },
+ "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_PRIV|OPT_STATIC, NULL, MAXNAMELEN },
+ 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_STATIC, NULL, MAXNAMELEN },
+ "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_STATIC,
+ "Set remote name for authentication", OPT_PRIO | OPT_STATIC,
&explicit_remote, MAXNAMELEN },
- { "auth", o_bool, &auth_required,
- "Require authentication from peer", 1 },
- { "noauth", o_bool, &auth_required,
- "Don't require peer to authenticate", OPT_PRIV, &allow_any_ip },
- { "login", o_bool, &uselogin,
- "Use system password database for PAP", 1 },
+
+ { "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 },
- { "+ua", o_special, (void *)setupapfile,
- "Get PAP user and password from file" },
- { "password", o_string, passwd,
- "Password for authenticating us to the peer", OPT_STATIC,
- NULL, MAXSECRETLEN },
+
{ "privgroup", o_special, (void *)privgroup,
- "Allow group members to use privileged options", OPT_PRIV },
+ "Allow 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_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 /* UNUSED */
+#endif /* PPP_OPTIONS */
+
#if 0 /* UNUSED */
/*
* setupapfile - specifies UPAP info for authenticating with peer.
*/
static int
-setupapfile(char **argv)
+setupapfile(argv)
+ char **argv;
{
- FILE * ufile;
+ FILE *ufile;
int l;
+ uid_t euid;
+ char u[MAXNAMELEN], p[MAXSECRETLEN];
+ char *fname;
lcp_allowoptions[0].neg_upap = 1;
/* open user info file */
- seteuid(getuid());
- ufile = fopen(*argv, "r");
- seteuid(0);
+ 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", *argv);
- return 0;
+ option_error("unable to open user login data file %s", fname);
+ return 0;
}
- check_access(ufile, *argv);
+ check_access(ufile, fname);
+ uafname = fname;
/* get username */
- if (fgets(user, MAXNAMELEN - 1, ufile) == NULL
- || fgets(passwd, MAXSECRETLEN - 1, ufile) == NULL){
- option_error("unable to read user login data file %s", *argv);
- return 0;
+ 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(user);
- if (l > 0 && user[l-1] == '\n')
- user[l-1] = 0;
- l = strlen(passwd);
- if (l > 0 && passwd[l-1] == '\n')
- passwd[l-1] = 0;
+ 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);
}
-#endif /* UNUSED */
-#if 0 /* UNUSED */
/*
* privgroup - allow members of the group to have privileged access.
*/
static int
-privgroup(char **argv)
+privgroup(argv)
+ char **argv;
{
struct group *g;
int i;
g = getgrnam(*argv);
if (g == 0) {
- option_error("group %s is unknown", *argv);
- return 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;
- }
+ if (groups[i] == g->gr_gid) {
+ privileged = 1;
+ break;
+ }
}
return 1;
}
-#endif
-#if 0 /* UNUSED */
+
/*
* set_noauth_addr - set address(es) that can be used without authentication.
* Equivalent to specifying an entry like `"" * "" addr' in pap-secrets.
*/
static int
-set_noauth_addr(char **argv)
+set_noauth_addr(argv)
+ char **argv;
{
char *addr = *argv;
- int l = strlen(addr);
+ int l = strlen(addr) + 1;
struct wordlist *wp;
- wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l + 1);
+ wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l);
if (wp == NULL)
- novm("allow-ip argument");
+ novm("allow-ip argument");
wp->word = (char *) (wp + 1);
wp->next = noauth_addrs;
- BCOPY(addr, wp->word, l);
+ MEMCPY(wp->word, addr, l);
noauth_addrs = wp;
return 1;
}
-#endif /* UNUSED */
+
/*
- * An Open on LCP has requested a change from Dead to Establish phase.
- * Do what's necessary to bring the physical layer up.
+ * set_permitted_number - set remote telephone number(s) that may connect.
*/
-void
-link_required(int unit)
+static int
+set_permitted_number(argv)
+ char **argv;
{
- LWIP_UNUSED_ARG(unit);
+ char *number = *argv;
+ int l = strlen(number) + 1;
+ struct wordlist *wp;
- AUTHDEBUG(LOG_INFO, ("link_required: %d\n", unit));
+ 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
/*
- * LCP has terminated the link; go to the Dead phase and take the
- * physical layer down.
+ * An Open on LCP has requested a change from Dead to Establish phase.
*/
-void
-link_terminated(int unit)
+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;
{
- AUTHDEBUG(LOG_INFO, ("link_terminated: %d\n", unit));
- if (lcp_phase[unit] == PHASE_DEAD) {
+ 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;
- }
- if (logged_in) {
- plogout();
- }
- lcp_phase[unit] = PHASE_DEAD;
- AUTHDEBUG(LOG_NOTICE, ("Connection terminated.\n"));
- pppLinkTerminated(unit);
+
+ 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 gone down; it will either die or try to re-establish.
+ * LCP has terminated the link; go to the Dead phase and take the
+ * physical layer down.
*/
-void
-link_down(int unit)
-{
- int i;
- struct protent *protp;
+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);
- AUTHDEBUG(LOG_INFO, ("link_down: %d\n", unit));
+#if 0 /* UNUSED */
+ if (pap_logout_hook) {
+ pap_logout_hook();
+ }
+ session_end(devnam);
+#endif /* UNUSED */
- if (did_authup) {
- /* XXX Do link down processing. */
- did_authup = 0;
- }
- for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
- if (!protp->enabled_flag) {
- continue;
+ if (!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 (protp->protocol != PPP_LCP && protp->lowerdown != NULL) {
- (*protp->lowerdown)(unit);
+ 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 (protp->protocol < 0xC000 && protp->close != NULL) {
- (*protp->close)(unit, "LCP down");
+ 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);
}
- }
- num_np_open = 0; /* number of network protocols we have opened */
- num_np_up = 0; /* Number of network protocols which have come up */
+ /* XXX if doing_multilink, should do something to stop
+ network-layer traffic on the link */
+}
- if (lcp_phase[unit] != PHASE_DEAD) {
- lcp_phase[unit] = PHASE_TERMINATE;
- }
- pppLinkDown(unit);
+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(int unit)
-{
- int auth;
- int i;
- struct protent *protp;
- lcp_options *wo = &lcp_wantoptions[unit];
- lcp_options *go = &lcp_gotoptions[unit];
-#if PAP_SUPPORT || CHAP_SUPPORT
- lcp_options *ho = &lcp_hisoptions[unit];
-#endif /* PAP_SUPPORT || CHAP_SUPPORT */
-
- AUTHDEBUG(LOG_INFO, ("link_established: unit %d; Lowering up all protocols...\n", unit));
- /*
- * Tell higher-level protocols that LCP is up.
- */
- for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
- if (protp->protocol != PPP_LCP && protp->enabled_flag && protp->lowerup != NULL) {
- (*protp->lowerup)(unit);
- }
- }
- if (ppp_settings.auth_required && !(go->neg_chap || go->neg_upap)) {
+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;
+
/*
- * We wanted the peer to authenticate itself, and it refused:
- * treat it as though it authenticated with PAP using a username
- * of "" and a password of "". If that's not OK, boot it out.
+ * Tell higher-level protocols that LCP is up.
*/
- if (!wo->neg_upap || !null_login(unit)) {
- AUTHDEBUG(LOG_WARNING, ("peer refused to authenticate\n"));
- lcp_close(unit, "peer refused to authenticate");
- return;
+ if (!doing_multilink) {
+ for (i = 0; (protp = protocols[i]) != NULL; ++i)
+ if (protp->protocol != PPP_LCP
+ && protp->lowerup != NULL)
+ (*protp->lowerup)(pcb);
}
- }
- lcp_phase[unit] = PHASE_AUTHENTICATE;
- auth = 0;
+#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
- if (go->neg_chap) {
- ChapAuthPeer(unit, ppp_settings.our_name, go->chap_mdtype);
- auth |= CHAP_PEER;
- }
+ || go->neg_chap
#endif /* CHAP_SUPPORT */
-#if PAP_SUPPORT && CHAP_SUPPORT
- else
-#endif /* PAP_SUPPORT && 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
- if (go->neg_upap) {
- upap_authpeer(unit);
- auth |= PAP_PEER;
- }
+ || !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) {
- ChapAuthWithPeer(unit, ppp_settings.user, ho->chap_mdtype);
- auth |= CHAP_WITHPEER;
- }
+ 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 && CHAP_SUPPORT
- else
-#endif /* PAP_SUPPORT && CHAP_SUPPORT */
#if PAP_SUPPORT
- if (ho->neg_upap) {
- if (ppp_settings.passwd[0] == 0) {
- passwd_from_file = 1;
- if (!get_pap_passwd(unit, ppp_settings.user, ppp_settings.passwd)) {
- AUTHDEBUG(LOG_ERR, ("No secret found for PAP login\n"));
- }
- }
- upap_authwithpeer(unit, ppp_settings.user, ppp_settings.passwd);
- auth |= PAP_WITHPEER;
- }
+ if (ho->neg_upap) {
+ upap_authwithpeer(pcb, pcb->settings.user, pcb->settings.passwd);
+ auth |= PAP_WITHPEER;
+ } else
#endif /* PAP_SUPPORT */
- auth_pending[unit] = auth;
+ {}
- if (!auth) {
- network_phase(unit);
- }
+ 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(int unit)
-{
- int i;
- struct protent *protp;
- lcp_options *go = &lcp_gotoptions[unit];
-
- /*
- * If the peer had to authenticate, run the auth-up script now.
- */
- if ((go->neg_chap || go->neg_upap) && !did_authup) {
- /* XXX Do setup for peer authentication. */
- did_authup = 1;
- }
+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) {
- lcp_phase[unit] = PHASE_CALLBACK;
- (*cbcp_protent.open)(unit);
- return;
- }
-#endif /* CBCP_SUPPORT */
-
- lcp_phase[unit] = PHASE_NETWORK;
- for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
- if (protp->protocol < 0xC000 && protp->enabled_flag && protp->open != NULL) {
- (*protp->open)(unit);
- if (protp->protocol != PPP_CCP) {
- ++num_np_open;
- }
+ /*
+ * If we negotiated callback, do it now.
+ */
+ if (go->neg_cbcp) {
+ new_phase(pcb, PPP_PHASE_CALLBACK);
+ (*cbcp_protent.open)(pcb);
+ return;
}
- }
+#endif
- if (num_np_open == 0) {
- /* nothing to do */
- lcp_close(0, "No network protocols running");
- }
+#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);
}
-/* @todo: add void start_networks(void) here (pppd 2.3.11) */
+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
/*
- * The peer has failed to authenticate himself using `protocol'.
+ * 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.
*/
-void
-auth_peer_fail(int unit, u16_t protocol)
-{
- LWIP_UNUSED_ARG(protocol);
+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;
+ }
+ }
- AUTHDEBUG(LOG_INFO, ("auth_peer_fail: %d proto=%X\n", unit, protocol));
- /*
- * Authentication failure: take the link down
- */
- lcp_close(unit, "Authentication failed");
+ *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");
+}
-#if PAP_SUPPORT || CHAP_SUPPORT
/*
* The peer has been successfully authenticated using `protocol'.
*/
-void
-auth_peer_success(int unit, u16_t protocol, char *name, int namelen)
-{
- int pbit;
-
- AUTHDEBUG(LOG_INFO, ("auth_peer_success: %d proto=%X\n", unit, protocol));
- switch (protocol) {
+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:
- pbit = CHAP_PEER;
- break;
+ 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:
- pbit = PAP_PEER;
- break;
+ bit = PAP_PEER;
+ break;
+#endif /* PAP_SUPPORT */
+#if EAP_SUPPORT
+ case PPP_EAP:
+ bit = EAP_PEER;
+ break;
+#endif /* EAP_SUPPORT */
default:
- AUTHDEBUG(LOG_WARNING, ("auth_peer_success: unknown protocol %x\n", protocol));
- return;
- }
+ ppp_warn("auth_peer_success: unknown protocol %x", protocol);
+ return;
+ }
- /*
- * Save the authenticated name of the peer for later.
- */
- if (namelen > (int)sizeof(peer_authname) - 1) {
- namelen = sizeof(peer_authname) - 1;
- }
- BCOPY(name, peer_authname, namelen);
- peer_authname[namelen] = 0;
-
- /*
- * If there is no more authentication still to be done,
- * proceed to the network (or callback) phase.
- */
- if ((auth_pending[unit] &= ~pbit) == 0) {
- network_phase(unit);
- }
+#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(int unit, u16_t protocol)
-{
- int errCode = PPPERR_AUTHFAIL;
-
- LWIP_UNUSED_ARG(protocol);
-
- AUTHDEBUG(LOG_INFO, ("auth_withpeer_fail: %d proto=%X\n", unit, protocol));
- if (passwd_from_file) {
- BZERO(ppp_settings.passwd, MAXSECRETLEN);
- }
-
- /*
- * We've failed to authenticate ourselves to our peer.
- * He'll probably take the link down, and there's not much
- * we can do except wait for that.
- */
- pppIOCtl(unit, PPPCTLS_ERRCODE, &errCode);
- lcp_close(unit, "Failed to authenticate ourselves to peer");
+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(int unit, u16_t protocol)
-{
- int pbit;
+void auth_withpeer_success(ppp_pcb *pcb, int protocol, int prot_flavor) {
+ int bit;
+ const char *prot = "";
- AUTHDEBUG(LOG_INFO, ("auth_withpeer_success: %d proto=%X\n", unit, protocol));
- switch (protocol) {
+ switch (protocol) {
+#if CHAP_SUPPORT
case PPP_CHAP:
- pbit = CHAP_WITHPEER;
- break;
+ 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:
- if (passwd_from_file) {
- BZERO(ppp_settings.passwd, MAXSECRETLEN);
- }
- pbit = PAP_WITHPEER;
- break;
+ 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:
- AUTHDEBUG(LOG_WARNING, ("auth_peer_success: unknown protocol %x\n", protocol));
- pbit = 0;
- }
+ ppp_warn("auth_withpeer_success: unknown protocol %x", protocol);
+ bit = 0;
+ /* no break */
+ }
- /*
- * If there is no more authentication still being done,
- * proceed to the network (or callback) phase.
- */
- if ((auth_pending[unit] &= ~pbit) == 0) {
- network_phase(unit);
- }
+ 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 /* PAP_SUPPORT || CHAP_SUPPORT */
+#endif /* PPP_AUTH_SUPPORT */
/*
* np_up - a network protocol has come up.
*/
-void
-np_up(int unit, u16_t proto)
-{
- LWIP_UNUSED_ARG(unit);
- LWIP_UNUSED_ARG(proto);
-
- AUTHDEBUG(LOG_INFO, ("np_up: %d proto=%X\n", unit, proto));
- if (num_np_up == 0) {
- AUTHDEBUG(LOG_INFO, ("np_up: maxconnect=%d idle_time_limit=%d\n",ppp_settings.maxconnect,ppp_settings.idle_time_limit));
- /*
- * At this point we consider that the link has come up successfully.
- */
- if (ppp_settings.idle_time_limit > 0) {
- TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit);
- }
+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
- /*
- * Set a timeout to close the connection once the maximum
- * connect time has expired.
- */
- if (ppp_settings.maxconnect > 0) {
- TIMEOUT(connect_time_expired, 0, ppp_settings.maxconnect);
+#if 0 /* Unused */
+ /*
+ * Detach now, if the updetach option was given.
+ */
+ if (updetach && !nodetach)
+ detach();
+#endif /* Unused */
}
- }
- ++num_np_up;
+ ++pcb->num_np_up;
}
/*
* np_down - a network protocol has gone down.
*/
-void
-np_down(int unit, u16_t proto)
-{
- LWIP_UNUSED_ARG(unit);
- LWIP_UNUSED_ARG(proto);
-
- AUTHDEBUG(LOG_INFO, ("np_down: %d proto=%X\n", unit, proto));
- if (--num_np_up == 0 && ppp_settings.idle_time_limit > 0) {
- UNTIMEOUT(check_idle, NULL);
- }
+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(int unit, u16_t proto)
-{
- LWIP_UNUSED_ARG(unit);
- LWIP_UNUSED_ARG(proto);
+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");
+ }
+}
- AUTHDEBUG(LOG_INFO, ("np_finished: %d proto=%X\n", unit, proto));
- if (--num_np_open <= 0) {
- /* no further use for the link: shut up shop. */
- lcp_close(0, "No network protocols running");
- }
+#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)
-{
- struct ppp_idle idle;
- u_short itime;
-
- LWIP_UNUSED_ARG(arg);
- if (!get_idle_time(0, &idle)) {
- return;
- }
- itime = LWIP_MIN(idle.xmit_idle, idle.recv_idle);
- if (itime >= ppp_settings.idle_time_limit) {
- /* link is idle: shut it down. */
- AUTHDEBUG(LOG_INFO, ("Terminating connection due to lack of activity.\n"));
- lcp_close(0, "Link inactive");
- } else {
- TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit - itime);
- }
+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)
-{
- LWIP_UNUSED_ARG(arg);
-
- AUTHDEBUG(LOG_INFO, ("Connect time expired\n"));
- lcp_close(0, "Connect time expired"); /* Close 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 0 /* UNUSED */
+#if PPP_OPTIONS
/*
* auth_check_options - called to check authentication options.
*/
void
-auth_check_options(void)
+auth_check_options()
{
- lcp_options *wo = &lcp_wantoptions[0];
- int can_auth;
- ipcp_options *ipwo = &ipcp_wantoptions[0];
- u32_t remote;
-
- /* Default our_name to hostname, and user to our_name */
- if (ppp_settings.our_name[0] == 0 || ppp_settings.usehostname) {
- strcpy(ppp_settings.our_name, ppp_settings.hostname);
- }
+ 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 (ppp_settings.user[0] == 0) {
- strcpy(ppp_settings.user, ppp_settings.our_name);
- }
+ /*
+ * 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 authentication is required, ask peer for CHAP or PAP. */
- if (ppp_settings.auth_required && !wo->neg_chap && !wo->neg_upap) {
- wo->neg_chap = 1;
- wo->neg_upap = 1;
- }
-
- /*
- * Check whether we have appropriate secrets to use
- * to authenticate the peer.
- */
- can_auth = wo->neg_upap && have_pap_secret();
- if (!can_auth && wo->neg_chap) {
- remote = ipwo->accept_remote? 0: ipwo->hisaddr;
- can_auth = have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote);
- }
+#if 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 (ppp_settings.auth_required && !can_auth) {
- ppp_panic("No auth secret");
- }
+ /* 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 /* UNUSED */
+#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(int unit)
+auth_reset(unit)
+ int unit;
{
- lcp_options *go = &lcp_gotoptions[unit];
- lcp_options *ao = &lcp_allowoptions[0];
- ipcp_options *ipwo = &ipcp_wantoptions[0];
- u32_t remote;
-
- AUTHDEBUG(LOG_INFO, ("auth_reset: %d\n", unit));
- ao->neg_upap = !ppp_settings.refuse_pap && (ppp_settings.passwd[0] != 0 || get_pap_passwd(unit, NULL, NULL));
- ao->neg_chap = !ppp_settings.refuse_chap && ppp_settings.passwd[0] != 0 /*have_chap_secret(ppp_settings.user, ppp_settings.remote_name, (u32_t)0)*/;
-
- if (go->neg_upap && !have_pap_secret()) {
- go->neg_upap = 0;
- }
- if (go->neg_chap) {
- remote = ipwo->accept_remote? 0: ipwo->hisaddr;
- if (!have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote)) {
- go->neg_chap = 0;
+ 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;
}
-#if PAP_SUPPORT
/*
* check_passwd - Check the user name and passwd against the PAP secrets
* file. If requested, also check against the system password database,
* and login the user if OK.
*
* returns:
- * UPAP_AUTHNAK: Authentication failed.
- * UPAP_AUTHACK: Authentication succeeded.
+ * UPAP_AUTHNAK: Authentication failed.
+ * UPAP_AUTHACK: Authentication succeeded.
* In either case, msg points to an appropriate message.
*/
-u_char
-check_passwd( int unit, char *auser, int userlen, char *apasswd, int passwdlen, char **msg, int *msglen)
+int
+check_passwd(unit, auser, userlen, apasswd, passwdlen, msg)
+ int unit;
+ char *auser;
+ int userlen;
+ char *apasswd;
+ int passwdlen;
+ char **msg;
{
-#if 1 /* XXX Assume all entries OK. */
- LWIP_UNUSED_ARG(unit);
- LWIP_UNUSED_ARG(auser);
- LWIP_UNUSED_ARG(userlen);
- LWIP_UNUSED_ARG(apasswd);
- LWIP_UNUSED_ARG(passwdlen);
- LWIP_UNUSED_ARG(msglen);
- *msg = (char *) 0;
- return UPAP_AUTHACK; /* XXX Assume all entries OK. */
-#else
- u_char ret = 0;
- struct wordlist *addrs = NULL;
- char passwd[256], user[256];
- char secret[MAXWORDLEN];
- static u_short attempts = 0;
-
- /*
- * Make copies of apasswd and auser, then null-terminate them.
- */
- BCOPY(apasswd, passwd, passwdlen);
- passwd[passwdlen] = '\0';
- BCOPY(auser, user, userlen);
- user[userlen] = '\0';
- *msg = (char *) 0;
-
- /* XXX Validate user name and password. */
- ret = UPAP_AUTHACK; /* XXX Assume all entries OK. */
-
- if (ret == UPAP_AUTHNAK) {
- if (*msg == (char *) 0) {
- *msg = "Login incorrect";
- }
- *msglen = strlen(*msg);
+ 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;
+
/*
- * Frustrate passwd stealer programs.
- * Allow 10 tries, but start backing off after 3 (stolen from login).
- * On 10'th, drop the connection.
+ * Make copies of apasswd and auser, then null-terminate them.
+ * If there are unprintable characters in the password, make
+ * them visible.
*/
- if (attempts++ >= 10) {
- AUTHDEBUG(LOG_WARNING, ("%d LOGIN FAILURES BY %s\n", attempts, user));
- /*ppp_panic("Excess Bad Logins");*/
- }
- if (attempts > 3) {
- /* @todo: this was sleep(), i.e. seconds, not milliseconds
- * I don't think we really need this in lwIP - we would block tcpip_thread!
- */
- /*sys_msleep((attempts - 3) * 5);*/
+ 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;
+ }
}
- if (addrs != NULL) {
- free_wordlist(addrs);
+
+ /*
+ * 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);
}
- } else {
- attempts = 0; /* Reset count */
- if (*msg == (char *) 0) {
- *msg = "Login ok";
+
+ 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);
}
- *msglen = strlen(*msg);
- set_allowed_addrs(unit, addrs);
- }
- BZERO(passwd, sizeof(passwd));
- BZERO(secret, sizeof(secret));
+ if (addrs != NULL)
+ free_wordlist(addrs);
+ BZERO(ppp_settings.passwd, sizeof(ppp_settings.passwd));
+ BZERO(secret, sizeof(secret));
- return ret;
-#endif
+ return ret;
}
-#endif /* PAP_SUPPORT */
-#if 0 /* UNUSED */
/*
- * This function is needed for PAM.
- */
-
-#ifdef USE_PAM
-
-/* lwip does not support PAM*/
-
-#endif /* USE_PAM */
-
-#endif /* UNUSED */
-
-
-#if 0 /* UNUSED */
-/*
- * plogin - Check the user name and password against the system
- * password database, and login the user if OK.
- *
- * returns:
- * UPAP_AUTHNAK: Login failed.
- * UPAP_AUTHACK: Login succeeded.
- * In either case, msg points to an appropriate message.
+ * 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
-plogin(char *user, char *passwd, char **msg, int *msglen)
+null_login(unit)
+ int unit;
{
+ char *filename;
+ FILE *f;
+ int i, ret;
+ struct wordlist *addrs, *opts;
+ char secret[MAXWORDLEN];
- LWIP_UNUSED_ARG(user);
- LWIP_UNUSED_ARG(passwd);
- LWIP_UNUSED_ARG(msg);
- LWIP_UNUSED_ARG(msglen);
-
-
- /* The new lines are here align the file when
- * compared against the pppd 2.3.11 code */
-
-
-
-
-
-
-
-
-
-
-
-
-
+ /*
+ * 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);
- /* XXX Fail until we decide that we want to support logins. */
- return (UPAP_AUTHNAK);
+ return ret;
}
-#endif
-
-
/*
- * plogout - Logout the user.
+ * 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 void
-plogout(void)
+static int
+get_pap_passwd(passwd)
+ char *passwd;
{
- logged_in = 0;
+ 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;
}
/*
- * 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.
+ * have_pap_secret - check whether we have a PAP file with any
+ * secrets that we could possibly use for authenticating the peer.
*/
static int
-null_login(int unit)
+have_pap_secret(lacks_ipp)
+ int *lacks_ipp;
{
- LWIP_UNUSED_ARG(unit);
- /* XXX Fail until we decide that we want to support logins. */
- return 0;
-}
+ 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);
-/*
- * get_pap_passwd - get a password for authenticating ourselves with
- * our peer using PAP. Returns 1 on success, 0 if no suitable password
- * could be found.
- */
-static int
-get_pap_passwd(int unit, char *user, char *passwd)
-{
- LWIP_UNUSED_ARG(unit);
-/* normally we would reject PAP if no password is provided,
- but this causes problems with some providers (like CHT in Taiwan)
- who incorrectly request PAP and expect a bogus/empty password, so
- always provide a default user/passwd of "none"/"none"
-
- @todo: This should be configured by the user, instead of being hardcoded here!
-*/
- if(user) {
- strcpy(user, "none");
- }
- if(passwd) {
- strcpy(passwd, "none");
- }
- return 1;
+ return ret >= 0;
}
/*
- * have_pap_secret - check whether we have a PAP file with any
- * secrets that we could possibly use for authenticating the peer.
+ * 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_pap_secret(void)
+have_chap_secret(client, server, need_ip, lacks_ipp)
+ char *client;
+ char *server;
+ int need_ip;
+ int *lacks_ipp;
{
- /* XXX Fail until we set up our passwords. */
- return 0;
+ 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_chap_secret - check whether we have a CHAP file with a
+ * 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_chap_secret(char *client, char *server, u32_t remote)
+have_srp_secret(client, server, need_ip, lacks_ipp)
+ char *client;
+ char *server;
+ int need_ip;
+ int *lacks_ipp;
{
- LWIP_UNUSED_ARG(client);
- LWIP_UNUSED_ARG(server);
- LWIP_UNUSED_ARG(remote);
+ 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);
- /* XXX Fail until we set up our passwords. */
- return 0;
+ return ret >= 0;
}
-#if CHAP_SUPPORT
+#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(int unit, char *client, char *server, char *secret, int *secret_len, int save_addrs)
-{
-#if 1
+int get_secret(ppp_pcb *pcb, const char *client, const char *server, char *secret, int *secret_len, int am_server) {
int len;
- struct wordlist *addrs;
-
- LWIP_UNUSED_ARG(unit);
LWIP_UNUSED_ARG(server);
- LWIP_UNUSED_ARG(save_addrs);
-
- addrs = NULL;
+ LWIP_UNUSED_ARG(am_server);
- if(!client || !client[0] || strcmp(client, ppp_settings.user)) {
+ if (!client || !client[0] || !pcb->settings.user || !pcb->settings.passwd || strcmp(client, pcb->settings.user)) {
return 0;
}
- len = (int)strlen(ppp_settings.passwd);
+ len = (int)strlen(pcb->settings.passwd);
if (len > MAXSECRETLEN) {
- AUTHDEBUG(LOG_ERR, ("Secret for %s on %s is too long\n", client, server));
+ ppp_error("Secret for %s on %s is too long", client, server);
len = MAXSECRETLEN;
}
- BCOPY(ppp_settings.passwd, secret, len);
+ MEMCPY(secret, pcb->settings.passwd, len);
*secret_len = len;
-
return 1;
-#else
- int ret = 0, len;
- struct wordlist *addrs;
- char secbuf[MAXWORDLEN];
-
- addrs = NULL;
- secbuf[0] = 0;
-
- /* XXX Find secret. */
- if (ret < 0) {
- return 0;
- }
- if (save_addrs) {
- set_allowed_addrs(unit, addrs);
- }
-
- len = strlen(secbuf);
- if (len > MAXSECRETLEN) {
- AUTHDEBUG(LOG_ERR, ("Secret for %s on %s is too long\n", client, server));
- len = MAXSECRETLEN;
- }
+#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);
+ }
- BCOPY(secbuf, secret, len);
- BZERO(secbuf, sizeof(secbuf));
- *secret_len = len;
+ 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
+ return 1;
+#endif /* UNUSED */
}
-#endif /* CHAP_SUPPORT */
+#endif /* PPP_AUTH_SUPPORT */
-#if 0 /* PAP_SUPPORT || CHAP_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(int unit, struct wordlist *addrs)
+set_allowed_addrs(unit, addrs, opts)
+ int unit;
+ struct wordlist *addrs;
+ struct wordlist *opts;
{
- if (addresses[unit] != NULL) {
- free_wordlist(addresses[unit]);
- }
- addresses[unit] = addrs;
-
-#if 0
- /*
- * If there's only one authorized address we might as well
- * ask our peer for that one right away
- */
- if (addrs != NULL && addrs->next == NULL) {
- char *p = addrs->word;
- struct ipcp_options *wo = &ipcp_wantoptions[unit];
- u32_t a;
+ int n;
+ struct wordlist *ap, **plink;
+ struct permitted_ip *ip;
+ char *ptr_word, *ptr_mask;
struct hostent *hp;
-
- if (wo->hisaddr == 0 && *p != '!' && *p != '-' && strchr(p, '/') == NULL) {
- hp = gethostbyname(p);
- if (hp != NULL && hp->h_addrtype == AF_INET) {
- a = *(u32_t *)hp->h_addr;
- } else {
- a = inet_addr(p);
- }
- if (a != (u32_t) -1) {
- wo->hisaddr = a;
- }
+ 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;
}
- }
-#endif
}
-#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */
/*
* auth_ip_addr - check whether the peer is authorized to use
* a given IP address. Returns 1 if authorized, 0 otherwise.
*/
int
-auth_ip_addr(int unit, u32_t addr)
+auth_ip_addr(unit, addr)
+ int unit;
+ u32_t addr;
{
- return ip_addr_check(addr, addresses[unit]);
-}
+ int ok;
-static int /* @todo: integrate this funtion into auth_ip_addr()*/
-ip_addr_check(u32_t addr, struct wordlist *addrs)
-{
- /* don't allow loopback or multicast address */
- if (bad_ip_adrs(addr)) {
- return 0;
- }
+ /* don't allow loopback or multicast address */
+ if (bad_ip_adrs(addr))
+ return 0;
- if (addrs == NULL) {
- return !ppp_settings.auth_required; /* no addresses authorized */
- }
+ if (allowed_address_hook) {
+ ok = allowed_address_hook(addr);
+ if (ok >= 0) return ok;
+ }
- /* XXX All other addresses allowed. */
- return 1;
+ 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;
}
/*
@@ -1201,27 +2231,56 @@ ip_addr_check(u32_t addr, struct wordlist *addrs)
* addr is in network byte order.
*/
int
-bad_ip_adrs(u32_t addr)
+bad_ip_adrs(addr)
+ u32_t addr;
{
- addr = ntohl(addr);
- return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET
- || IN_MULTICAST(addr) || IN_BADCLASS(addr);
+ addr = lwip_ntohl(addr);
+ return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET
+ || IN_MULTICAST(addr) || IN_BADCLASS(addr);
}
-#if 0 /* UNUSED */ /* PAP_SUPPORT || CHAP_SUPPORT */
/*
* some_ip_ok - check a wordlist to see if it authorizes any
* IP address(es).
*/
static int
-some_ip_ok(struct wordlist *addrs)
+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 */
+ 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;
}
@@ -1229,19 +2288,20 @@ some_ip_ok(struct wordlist *addrs)
* check_access - complain if a secret file has too-liberal permissions.
*/
static void
-check_access(FILE *f, char *filename)
+check_access(f, filename)
+ FILE *f;
+ char *filename;
{
struct stat sbuf;
if (fstat(fileno(f), &sbuf) < 0) {
- warn("cannot stat secret file %s: %m", filename);
+ ppp_warn("cannot stat secret file %s: %m", filename);
} else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
- warn("Warning - secret file %s has world and/or group access",
- filename);
+ 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
@@ -1253,82 +2313,198 @@ check_access(FILE *f, char *filename)
* 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(FILE *f, char *client, char *server, char *secret, struct wordlist **addrs, struct wordlist **opts, char *filename)
-{
- /* We do not (currently) need this in lwip */
- return 0; /* dummy */
-}
-/*
- * free_wordlist - release memory allocated for a wordlist.
- */
-static void
-free_wordlist(struct wordlist *wp)
+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;
{
- struct wordlist *next;
+ 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;
+ }
- while (wp != NULL) {
- next = wp->next;
- free(wp);
- wp = next;
- }
+ /* 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;
}
/*
- * auth_script_done - called when the auth-up or auth-down script
- * has finished.
+ * wordlist_count - return the number of items in a wordlist
*/
-static void
-auth_script_done(void *arg)
+static int
+wordlist_count(wp)
+ struct wordlist *wp;
{
- auth_script_pid = 0;
- switch (auth_script_state) {
- case s_up:
- if (auth_state == s_down) {
- auth_script_state = s_down;
- auth_script(_PATH_AUTHDOWN);
- }
- break;
- case s_down:
- if (auth_state == s_up) {
- auth_script_state = s_up;
- auth_script(_PATH_AUTHUP);
- }
- break;
- }
+ int n;
+
+ for (n = 0; wp != NULL; wp = wp->next)
+ ++n;
+ return n;
}
/*
- * auth_script - execute a script with arguments
- * interface-name peer-name real-user tty speed
+ * free_wordlist - release memory allocated for a wordlist.
*/
static void
-auth_script(char *script)
+free_wordlist(wp)
+ struct wordlist *wp;
{
- char strspeed[32];
- struct passwd *pw;
- char struid[32];
- char *user_name;
- char *argv[8];
-
- if ((pw = getpwuid(getuid())) != NULL && pw->pw_name != NULL)
- user_name = pw->pw_name;
- else {
- slprintf(struid, sizeof(struid), "%d", getuid());
- user_name = struid;
- }
- slprintf(strspeed, sizeof(strspeed), "%d", baud_rate);
+ struct wordlist *next;
- argv[0] = script;
- argv[1] = ifname;
- argv[2] = peer_authname;
- argv[3] = user_name;
- argv[4] = devnam;
- argv[5] = strspeed;
- argv[6] = NULL;
-
- auth_script_pid = run_program(script, argv, 0, auth_script_done, NULL);
+ while (wp != NULL) {
+ next = wp->next;
+ free(wp);
+ wp = next;
+ }
}
-#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */
+#endif /* UNUSED */
+
#endif /* PPP_SUPPORT */
diff --git a/lwip/src/netif/ppp/auth.h b/lwip/src/netif/ppp/auth.h
deleted file mode 100644
index a8069ec..0000000
--- a/lwip/src/netif/ppp/auth.h
+++ /dev/null
@@ -1,111 +0,0 @@
-/*****************************************************************************
-* auth.h - PPP Authentication and phase control header file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* portions Copyright (c) 1998 Global Election Systems Inc.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 97-12-04 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
-* Original derived from BSD pppd.h.
-*****************************************************************************/
-/*
- * pppd.h - PPP daemon global declarations.
- *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- */
-
-#ifndef AUTH_H
-#define AUTH_H
-
-/***********************
-*** PUBLIC FUNCTIONS ***
-***********************/
-
-/* we are starting to use the link */
-void link_required (int);
-
-/* we are finished with the link */
-void link_terminated (int);
-
-/* the LCP layer has left the Opened state */
-void link_down (int);
-
-/* the link is up; authenticate now */
-void link_established (int);
-
-/* a network protocol has come up */
-void np_up (int, u16_t);
-
-/* a network protocol has gone down */
-void np_down (int, u16_t);
-
-/* a network protocol no longer needs link */
-void np_finished (int, u16_t);
-
-/* peer failed to authenticate itself */
-void auth_peer_fail (int, u16_t);
-
-/* peer successfully authenticated itself */
-void auth_peer_success (int, u16_t, char *, int);
-
-/* we failed to authenticate ourselves */
-void auth_withpeer_fail (int, u16_t);
-
-/* we successfully authenticated ourselves */
-void auth_withpeer_success (int, u16_t);
-
-/* check authentication options supplied */
-void auth_check_options (void);
-
-/* check what secrets we have */
-void auth_reset (int);
-
-/* Check peer-supplied username/password */
-u_char check_passwd (int, char *, int, char *, int, char **, int *);
-
-/* get "secret" for chap */
-int get_secret (int, char *, char *, char *, int *, int);
-
-/* check if IP address is authorized */
-int auth_ip_addr (int, u32_t);
-
-/* check if IP address is unreasonable */
-int bad_ip_adrs (u32_t);
-
-#endif /* AUTH_H */
diff --git a/lwip/src/netif/ppp/ccp.c b/lwip/src/netif/ppp/ccp.c
new file mode 100644
index 0000000..f8519eb
--- /dev/null
+++ b/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/lwip/src/netif/ppp/chap-md5.c b/lwip/src/netif/ppp/chap-md5.c
new file mode 100644
index 0000000..88f069f
--- /dev/null
+++ b/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/lwip/src/netif/ppp/chap-new.c b/lwip/src/netif/ppp/chap-new.c
new file mode 100644
index 0000000..485122d
--- /dev/null
+++ b/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/lwip/src/netif/ppp/chap.c b/lwip/src/netif/ppp/chap.c
deleted file mode 100644
index f10e27d..0000000
--- a/lwip/src/netif/ppp/chap.c
+++ /dev/null
@@ -1,908 +0,0 @@
-/*** WARNING - THIS HAS NEVER BEEN FINISHED ***/
-/*****************************************************************************
-* chap.c - Network Challenge Handshake Authentication Protocol program file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* portions Copyright (c) 1997 by Global Election Systems Inc.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 97-12-04 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
-* Original based on BSD chap.c.
-*****************************************************************************/
-/*
- * chap.c - Challenge Handshake Authentication Protocol.
- *
- * Copyright (c) 1993 The Australian National University.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the Australian National University. The name of the University
- * may not be used to endorse or promote products derived from this
- * software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * Copyright (c) 1991 Gregory M. Christy.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Gregory M. Christy. The name of the author may not be used to
- * endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
-
-#include "lwip/opt.h"
-
-#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
-
-#if CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
-
-#include "ppp_impl.h"
-#include "pppdebug.h"
-
-#include "magic.h"
-#include "randm.h"
-#include "auth.h"
-#include "md5.h"
-#include "chap.h"
-#include "chpms.h"
-
-#include <string.h>
-
-#if 0 /* UNUSED */
-/*
- * Command-line options.
- */
-static option_t chap_option_list[] = {
- { "chap-restart", o_int, &chap[0].timeouttime,
- "Set timeout for CHAP" },
- { "chap-max-challenge", o_int, &chap[0].max_transmits,
- "Set max #xmits for challenge" },
- { "chap-interval", o_int, &chap[0].chal_interval,
- "Set interval for rechallenge" },
-#ifdef MSLANMAN
- { "ms-lanman", o_bool, &ms_lanman,
- "Use LanMan passwd when using MS-CHAP", 1 },
-#endif
- { NULL }
-};
-#endif /* UNUSED */
-
-/*
- * Protocol entry points.
- */
-static void ChapInit (int);
-static void ChapLowerUp (int);
-static void ChapLowerDown (int);
-static void ChapInput (int, u_char *, int);
-static void ChapProtocolReject (int);
-#if PPP_ADDITIONAL_CALLBACKS
-static int ChapPrintPkt (u_char *, int, void (*) (void *, char *, ...), void *);
-#endif
-
-struct protent chap_protent = {
- PPP_CHAP,
- ChapInit,
- ChapInput,
- ChapProtocolReject,
- ChapLowerUp,
- ChapLowerDown,
- NULL,
- NULL,
-#if PPP_ADDITIONAL_CALLBACKS
- ChapPrintPkt,
- NULL,
-#endif /* PPP_ADDITIONAL_CALLBACKS */
- 1,
- "CHAP",
-#if PPP_ADDITIONAL_CALLBACKS
- NULL,
- NULL,
- NULL
-#endif /* PPP_ADDITIONAL_CALLBACKS */
-};
-
-chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */
-
-static void ChapChallengeTimeout (void *);
-static void ChapResponseTimeout (void *);
-static void ChapReceiveChallenge (chap_state *, u_char *, u_char, int);
-static void ChapRechallenge (void *);
-static void ChapReceiveResponse (chap_state *, u_char *, int, int);
-static void ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len);
-static void ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len);
-static void ChapSendStatus (chap_state *, int);
-static void ChapSendChallenge (chap_state *);
-static void ChapSendResponse (chap_state *);
-static void ChapGenChallenge (chap_state *);
-
-/*
- * ChapInit - Initialize a CHAP unit.
- */
-static void
-ChapInit(int unit)
-{
- chap_state *cstate = &chap[unit];
-
- BZERO(cstate, sizeof(*cstate));
- cstate->unit = unit;
- cstate->clientstate = CHAPCS_INITIAL;
- cstate->serverstate = CHAPSS_INITIAL;
- cstate->timeouttime = CHAP_DEFTIMEOUT;
- cstate->max_transmits = CHAP_DEFTRANSMITS;
- /* random number generator is initialized in magic_init */
-}
-
-
-/*
- * ChapAuthWithPeer - Authenticate us with our peer (start client).
- *
- */
-void
-ChapAuthWithPeer(int unit, char *our_name, u_char digest)
-{
- chap_state *cstate = &chap[unit];
-
- cstate->resp_name = our_name;
- cstate->resp_type = digest;
-
- if (cstate->clientstate == CHAPCS_INITIAL ||
- cstate->clientstate == CHAPCS_PENDING) {
- /* lower layer isn't up - wait until later */
- cstate->clientstate = CHAPCS_PENDING;
- return;
- }
-
- /*
- * We get here as a result of LCP coming up.
- * So even if CHAP was open before, we will
- * have to re-authenticate ourselves.
- */
- cstate->clientstate = CHAPCS_LISTEN;
-}
-
-
-/*
- * ChapAuthPeer - Authenticate our peer (start server).
- */
-void
-ChapAuthPeer(int unit, char *our_name, u_char digest)
-{
- chap_state *cstate = &chap[unit];
-
- cstate->chal_name = our_name;
- cstate->chal_type = digest;
-
- if (cstate->serverstate == CHAPSS_INITIAL ||
- cstate->serverstate == CHAPSS_PENDING) {
- /* lower layer isn't up - wait until later */
- cstate->serverstate = CHAPSS_PENDING;
- return;
- }
-
- ChapGenChallenge(cstate);
- ChapSendChallenge(cstate); /* crank it up dude! */
- cstate->serverstate = CHAPSS_INITIAL_CHAL;
-}
-
-
-/*
- * ChapChallengeTimeout - Timeout expired on sending challenge.
- */
-static void
-ChapChallengeTimeout(void *arg)
-{
- chap_state *cstate = (chap_state *) arg;
-
- /* if we aren't sending challenges, don't worry. then again we */
- /* probably shouldn't be here either */
- if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
- cstate->serverstate != CHAPSS_RECHALLENGE) {
- return;
- }
-
- if (cstate->chal_transmits >= cstate->max_transmits) {
- /* give up on peer */
- CHAPDEBUG(LOG_ERR, ("Peer failed to respond to CHAP challenge\n"));
- cstate->serverstate = CHAPSS_BADAUTH;
- auth_peer_fail(cstate->unit, PPP_CHAP);
- return;
- }
-
- ChapSendChallenge(cstate); /* Re-send challenge */
-}
-
-
-/*
- * ChapResponseTimeout - Timeout expired on sending response.
- */
-static void
-ChapResponseTimeout(void *arg)
-{
- chap_state *cstate = (chap_state *) arg;
-
- /* if we aren't sending a response, don't worry. */
- if (cstate->clientstate != CHAPCS_RESPONSE) {
- return;
- }
-
- ChapSendResponse(cstate); /* re-send response */
-}
-
-
-/*
- * ChapRechallenge - Time to challenge the peer again.
- */
-static void
-ChapRechallenge(void *arg)
-{
- chap_state *cstate = (chap_state *) arg;
-
- /* if we aren't sending a response, don't worry. */
- if (cstate->serverstate != CHAPSS_OPEN) {
- return;
- }
-
- ChapGenChallenge(cstate);
- ChapSendChallenge(cstate);
- cstate->serverstate = CHAPSS_RECHALLENGE;
-}
-
-
-/*
- * ChapLowerUp - The lower layer is up.
- *
- * Start up if we have pending requests.
- */
-static void
-ChapLowerUp(int unit)
-{
- chap_state *cstate = &chap[unit];
-
- if (cstate->clientstate == CHAPCS_INITIAL) {
- cstate->clientstate = CHAPCS_CLOSED;
- } else if (cstate->clientstate == CHAPCS_PENDING) {
- cstate->clientstate = CHAPCS_LISTEN;
- }
-
- if (cstate->serverstate == CHAPSS_INITIAL) {
- cstate->serverstate = CHAPSS_CLOSED;
- } else if (cstate->serverstate == CHAPSS_PENDING) {
- ChapGenChallenge(cstate);
- ChapSendChallenge(cstate);
- cstate->serverstate = CHAPSS_INITIAL_CHAL;
- }
-}
-
-
-/*
- * ChapLowerDown - The lower layer is down.
- *
- * Cancel all timeouts.
- */
-static void
-ChapLowerDown(int unit)
-{
- chap_state *cstate = &chap[unit];
-
- /* Timeout(s) pending? Cancel if so. */
- if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
- cstate->serverstate == CHAPSS_RECHALLENGE) {
- UNTIMEOUT(ChapChallengeTimeout, cstate);
- } else if (cstate->serverstate == CHAPSS_OPEN
- && cstate->chal_interval != 0) {
- UNTIMEOUT(ChapRechallenge, cstate);
- }
- if (cstate->clientstate == CHAPCS_RESPONSE) {
- UNTIMEOUT(ChapResponseTimeout, cstate);
- }
- cstate->clientstate = CHAPCS_INITIAL;
- cstate->serverstate = CHAPSS_INITIAL;
-}
-
-
-/*
- * ChapProtocolReject - Peer doesn't grok CHAP.
- */
-static void
-ChapProtocolReject(int unit)
-{
- chap_state *cstate = &chap[unit];
-
- if (cstate->serverstate != CHAPSS_INITIAL &&
- cstate->serverstate != CHAPSS_CLOSED) {
- auth_peer_fail(unit, PPP_CHAP);
- }
- if (cstate->clientstate != CHAPCS_INITIAL &&
- cstate->clientstate != CHAPCS_CLOSED) {
- auth_withpeer_fail(unit, PPP_CHAP); /* lwip: just sets the PPP error code on this unit to PPPERR_AUTHFAIL */
- }
- ChapLowerDown(unit); /* shutdown chap */
-}
-
-
-/*
- * ChapInput - Input CHAP packet.
- */
-static void
-ChapInput(int unit, u_char *inpacket, int packet_len)
-{
- chap_state *cstate = &chap[unit];
- u_char *inp;
- u_char code, id;
- int len;
-
- /*
- * Parse header (code, id and length).
- * If packet too short, drop it.
- */
- inp = inpacket;
- if (packet_len < CHAP_HEADERLEN) {
- CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd short header.\n"));
- return;
- }
- GETCHAR(code, inp);
- GETCHAR(id, inp);
- GETSHORT(len, inp);
- if (len < CHAP_HEADERLEN) {
- CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd illegal length.\n"));
- return;
- }
- if (len > packet_len) {
- CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd short packet.\n"));
- return;
- }
- len -= CHAP_HEADERLEN;
-
- /*
- * Action depends on code (as in fact it usually does :-).
- */
- switch (code) {
- case CHAP_CHALLENGE:
- ChapReceiveChallenge(cstate, inp, id, len);
- break;
-
- case CHAP_RESPONSE:
- ChapReceiveResponse(cstate, inp, id, len);
- break;
-
- case CHAP_FAILURE:
- ChapReceiveFailure(cstate, inp, id, len);
- break;
-
- case CHAP_SUCCESS:
- ChapReceiveSuccess(cstate, inp, id, len);
- break;
-
- default: /* Need code reject? */
- CHAPDEBUG(LOG_WARNING, ("Unknown CHAP code (%d) received.\n", code));
- break;
- }
-}
-
-
-/*
- * ChapReceiveChallenge - Receive Challenge and send Response.
- */
-static void
-ChapReceiveChallenge(chap_state *cstate, u_char *inp, u_char id, int len)
-{
- int rchallenge_len;
- u_char *rchallenge;
- int secret_len;
- char secret[MAXSECRETLEN];
- char rhostname[256];
- MD5_CTX mdContext;
- u_char hash[MD5_SIGNATURE_SIZE];
-
- CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: Rcvd id %d.\n", id));
- if (cstate->clientstate == CHAPCS_CLOSED ||
- cstate->clientstate == CHAPCS_PENDING) {
- CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: in state %d\n",
- cstate->clientstate));
- return;
- }
-
- if (len < 2) {
- CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: rcvd short packet.\n"));
- return;
- }
-
- GETCHAR(rchallenge_len, inp);
- len -= sizeof (u_char) + rchallenge_len; /* now name field length */
- if (len < 0) {
- CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: rcvd short packet.\n"));
- return;
- }
- rchallenge = inp;
- INCPTR(rchallenge_len, inp);
-
- if (len >= (int)sizeof(rhostname)) {
- len = sizeof(rhostname) - 1;
- }
- BCOPY(inp, rhostname, len);
- rhostname[len] = '\000';
-
- CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: received name field '%s'\n",
- rhostname));
-
- /* Microsoft doesn't send their name back in the PPP packet */
- if (ppp_settings.remote_name[0] != 0 && (ppp_settings.explicit_remote || rhostname[0] == 0)) {
- strncpy(rhostname, ppp_settings.remote_name, sizeof(rhostname));
- rhostname[sizeof(rhostname) - 1] = 0;
- CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: using '%s' as remote name\n",
- rhostname));
- }
-
- /* get secret for authenticating ourselves with the specified host */
- if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
- secret, &secret_len, 0)) {
- secret_len = 0; /* assume null secret if can't find one */
- CHAPDEBUG(LOG_WARNING, ("No CHAP secret found for authenticating us to %s\n",
- rhostname));
- }
-
- /* cancel response send timeout if necessary */
- if (cstate->clientstate == CHAPCS_RESPONSE) {
- UNTIMEOUT(ChapResponseTimeout, cstate);
- }
-
- cstate->resp_id = id;
- cstate->resp_transmits = 0;
-
- /* generate MD based on negotiated type */
- switch (cstate->resp_type) {
-
- case CHAP_DIGEST_MD5:
- MD5Init(&mdContext);
- MD5Update(&mdContext, &cstate->resp_id, 1);
- MD5Update(&mdContext, (u_char*)secret, secret_len);
- MD5Update(&mdContext, rchallenge, rchallenge_len);
- MD5Final(hash, &mdContext);
- BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
- cstate->resp_length = MD5_SIGNATURE_SIZE;
- break;
-
-#if MSCHAP_SUPPORT
- case CHAP_MICROSOFT:
- ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len);
- break;
-#endif
-
- default:
- CHAPDEBUG(LOG_INFO, ("unknown digest type %d\n", cstate->resp_type));
- return;
- }
-
- BZERO(secret, sizeof(secret));
- ChapSendResponse(cstate);
-}
-
-
-/*
- * ChapReceiveResponse - Receive and process response.
- */
-static void
-ChapReceiveResponse(chap_state *cstate, u_char *inp, int id, int len)
-{
- u_char *remmd, remmd_len;
- int secret_len, old_state;
- int code;
- char rhostname[256];
- MD5_CTX mdContext;
- char secret[MAXSECRETLEN];
- u_char hash[MD5_SIGNATURE_SIZE];
-
- CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: Rcvd id %d.\n", id));
-
- if (cstate->serverstate == CHAPSS_CLOSED ||
- cstate->serverstate == CHAPSS_PENDING) {
- CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: in state %d\n",
- cstate->serverstate));
- return;
- }
-
- if (id != cstate->chal_id) {
- return; /* doesn't match ID of last challenge */
- }
-
- /*
- * If we have received a duplicate or bogus Response,
- * we have to send the same answer (Success/Failure)
- * as we did for the first Response we saw.
- */
- if (cstate->serverstate == CHAPSS_OPEN) {
- ChapSendStatus(cstate, CHAP_SUCCESS);
- return;
- }
- if (cstate->serverstate == CHAPSS_BADAUTH) {
- ChapSendStatus(cstate, CHAP_FAILURE);
- return;
- }
-
- if (len < 2) {
- CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: rcvd short packet.\n"));
- return;
- }
- GETCHAR(remmd_len, inp); /* get length of MD */
- remmd = inp; /* get pointer to MD */
- INCPTR(remmd_len, inp);
-
- len -= sizeof (u_char) + remmd_len;
- if (len < 0) {
- CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: rcvd short packet.\n"));
- return;
- }
-
- UNTIMEOUT(ChapChallengeTimeout, cstate);
-
- if (len >= (int)sizeof(rhostname)) {
- len = sizeof(rhostname) - 1;
- }
- BCOPY(inp, rhostname, len);
- rhostname[len] = '\000';
-
- CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: received name field: %s\n",
- rhostname));
-
- /*
- * Get secret for authenticating them with us,
- * do the hash ourselves, and compare the result.
- */
- code = CHAP_FAILURE;
- if (!get_secret(cstate->unit, rhostname, cstate->chal_name,
- secret, &secret_len, 1)) {
- CHAPDEBUG(LOG_WARNING, ("No CHAP secret found for authenticating %s\n",
- rhostname));
- } else {
- /* generate MD based on negotiated type */
- switch (cstate->chal_type) {
-
- case CHAP_DIGEST_MD5: /* only MD5 is defined for now */
- if (remmd_len != MD5_SIGNATURE_SIZE) {
- break; /* it's not even the right length */
- }
- MD5Init(&mdContext);
- MD5Update(&mdContext, &cstate->chal_id, 1);
- MD5Update(&mdContext, (u_char*)secret, secret_len);
- MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
- MD5Final(hash, &mdContext);
-
- /* compare local and remote MDs and send the appropriate status */
- if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0) {
- code = CHAP_SUCCESS; /* they are the same! */
- }
- break;
-
- default:
- CHAPDEBUG(LOG_INFO, ("unknown digest type %d\n", cstate->chal_type));
- }
- }
-
- BZERO(secret, sizeof(secret));
- ChapSendStatus(cstate, code);
-
- if (code == CHAP_SUCCESS) {
- old_state = cstate->serverstate;
- cstate->serverstate = CHAPSS_OPEN;
- if (old_state == CHAPSS_INITIAL_CHAL) {
- auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
- }
- if (cstate->chal_interval != 0) {
- TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
- }
- } else {
- CHAPDEBUG(LOG_ERR, ("CHAP peer authentication failed\n"));
- cstate->serverstate = CHAPSS_BADAUTH;
- auth_peer_fail(cstate->unit, PPP_CHAP);
- }
-}
-
-/*
- * ChapReceiveSuccess - Receive Success
- */
-static void
-ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len)
-{
- LWIP_UNUSED_ARG(id);
- LWIP_UNUSED_ARG(inp);
-
- CHAPDEBUG(LOG_INFO, ("ChapReceiveSuccess: Rcvd id %d.\n", id));
-
- if (cstate->clientstate == CHAPCS_OPEN) {
- /* presumably an answer to a duplicate response */
- return;
- }
-
- if (cstate->clientstate != CHAPCS_RESPONSE) {
- /* don't know what this is */
- CHAPDEBUG(LOG_INFO, ("ChapReceiveSuccess: in state %d\n",
- cstate->clientstate));
- return;
- }
-
- UNTIMEOUT(ChapResponseTimeout, cstate);
-
- /*
- * Print message.
- */
- if (len > 0) {
- PRINTMSG(inp, len);
- }
-
- cstate->clientstate = CHAPCS_OPEN;
-
- auth_withpeer_success(cstate->unit, PPP_CHAP);
-}
-
-
-/*
- * ChapReceiveFailure - Receive failure.
- */
-static void
-ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len)
-{
- LWIP_UNUSED_ARG(id);
- LWIP_UNUSED_ARG(inp);
-
- CHAPDEBUG(LOG_INFO, ("ChapReceiveFailure: Rcvd id %d.\n", id));
-
- if (cstate->clientstate != CHAPCS_RESPONSE) {
- /* don't know what this is */
- CHAPDEBUG(LOG_INFO, ("ChapReceiveFailure: in state %d\n",
- cstate->clientstate));
- return;
- }
-
- UNTIMEOUT(ChapResponseTimeout, cstate);
-
- /*
- * Print message.
- */
- if (len > 0) {
- PRINTMSG(inp, len);
- }
-
- CHAPDEBUG(LOG_ERR, ("CHAP authentication failed\n"));
- auth_withpeer_fail(cstate->unit, PPP_CHAP); /* lwip: just sets the PPP error code on this unit to PPPERR_AUTHFAIL */
-}
-
-
-/*
- * ChapSendChallenge - Send an Authenticate challenge.
- */
-static void
-ChapSendChallenge(chap_state *cstate)
-{
- u_char *outp;
- int chal_len, name_len;
- int outlen;
-
- chal_len = cstate->chal_len;
- name_len = (int)strlen(cstate->chal_name);
- outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
- outp = outpacket_buf[cstate->unit];
-
- MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */
-
- PUTCHAR(CHAP_CHALLENGE, outp);
- PUTCHAR(cstate->chal_id, outp);
- PUTSHORT(outlen, outp);
-
- PUTCHAR(chal_len, outp); /* put length of challenge */
- BCOPY(cstate->challenge, outp, chal_len);
- INCPTR(chal_len, outp);
-
- BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
-
- pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN);
-
- CHAPDEBUG(LOG_INFO, ("ChapSendChallenge: Sent id %d.\n", cstate->chal_id));
-
- TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
- ++cstate->chal_transmits;
-}
-
-
-/*
- * ChapSendStatus - Send a status response (ack or nak).
- */
-static void
-ChapSendStatus(chap_state *cstate, int code)
-{
- u_char *outp;
- int outlen, msglen;
- char msg[256]; /* @todo: this can be a char*, no strcpy needed */
-
- if (code == CHAP_SUCCESS) {
- strcpy(msg, "Welcome!");
- } else {
- strcpy(msg, "I don't like you. Go 'way.");
- }
- msglen = (int)strlen(msg);
-
- outlen = CHAP_HEADERLEN + msglen;
- outp = outpacket_buf[cstate->unit];
-
- MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
-
- PUTCHAR(code, outp);
- PUTCHAR(cstate->chal_id, outp);
- PUTSHORT(outlen, outp);
- BCOPY(msg, outp, msglen);
- pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN);
-
- CHAPDEBUG(LOG_INFO, ("ChapSendStatus: Sent code %d, id %d.\n", code,
- cstate->chal_id));
-}
-
-/*
- * ChapGenChallenge is used to generate a pseudo-random challenge string of
- * a pseudo-random length between min_len and max_len. The challenge
- * string and its length are stored in *cstate, and various other fields of
- * *cstate are initialized.
- */
-
-static void
-ChapGenChallenge(chap_state *cstate)
-{
- int chal_len;
- u_char *ptr = cstate->challenge;
- int i;
-
- /* pick a random challenge length between MIN_CHALLENGE_LENGTH and
- MAX_CHALLENGE_LENGTH */
- chal_len = (unsigned)
- ((((magic() >> 16) *
- (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) >> 16)
- + MIN_CHALLENGE_LENGTH);
- LWIP_ASSERT("chal_len <= 0xff", chal_len <= 0xffff);
- cstate->chal_len = (u_char)chal_len;
- cstate->chal_id = ++cstate->id;
- cstate->chal_transmits = 0;
-
- /* generate a random string */
- for (i = 0; i < chal_len; i++ ) {
- *ptr++ = (char) (magic() & 0xff);
- }
-}
-
-/*
- * ChapSendResponse - send a response packet with values as specified
- * in *cstate.
- */
-/* ARGSUSED */
-static void
-ChapSendResponse(chap_state *cstate)
-{
- u_char *outp;
- int outlen, md_len, name_len;
-
- md_len = cstate->resp_length;
- name_len = (int)strlen(cstate->resp_name);
- outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
- outp = outpacket_buf[cstate->unit];
-
- MAKEHEADER(outp, PPP_CHAP);
-
- PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
- PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */
- PUTSHORT(outlen, outp); /* packet length */
-
- PUTCHAR(md_len, outp); /* length of MD */
- BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */
- INCPTR(md_len, outp);
-
- BCOPY(cstate->resp_name, outp, name_len); /* append our name */
-
- /* send the packet */
- pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN);
-
- cstate->clientstate = CHAPCS_RESPONSE;
- TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
- ++cstate->resp_transmits;
-}
-
-#if PPP_ADDITIONAL_CALLBACKS
-static char *ChapCodenames[] = {
- "Challenge", "Response", "Success", "Failure"
-};
-/*
- * ChapPrintPkt - print the contents of a CHAP packet.
- */
-static int
-ChapPrintPkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg)
-{
- int code, id, len;
- int clen, nlen;
- u_char x;
-
- if (plen < CHAP_HEADERLEN) {
- return 0;
- }
- GETCHAR(code, p);
- GETCHAR(id, p);
- GETSHORT(len, p);
- if (len < CHAP_HEADERLEN || len > plen) {
- return 0;
- }
-
- if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *)) {
- printer(arg, " %s", ChapCodenames[code-1]);
- } else {
- printer(arg, " code=0x%x", code);
- }
- printer(arg, " id=0x%x", id);
- len -= CHAP_HEADERLEN;
- switch (code) {
- case CHAP_CHALLENGE:
- case CHAP_RESPONSE:
- if (len < 1) {
- break;
- }
- clen = p[0];
- if (len < clen + 1) {
- break;
- }
- ++p;
- nlen = len - clen - 1;
- printer(arg, " <");
- for (; clen > 0; --clen) {
- GETCHAR(x, p);
- printer(arg, "%.2x", x);
- }
- printer(arg, ">, name = %.*Z", nlen, p);
- break;
- case CHAP_FAILURE:
- case CHAP_SUCCESS:
- printer(arg, " %.*Z", len, p);
- break;
- default:
- for (clen = len; clen > 0; --clen) {
- GETCHAR(x, p);
- printer(arg, " %.2x", x);
- }
- }
-
- return len + CHAP_HEADERLEN;
-}
-#endif /* PPP_ADDITIONAL_CALLBACKS */
-
-#endif /* CHAP_SUPPORT */
-
-#endif /* PPP_SUPPORT */
diff --git a/lwip/src/netif/ppp/chap.h b/lwip/src/netif/ppp/chap.h
deleted file mode 100644
index fedcab8..0000000
--- a/lwip/src/netif/ppp/chap.h
+++ /dev/null
@@ -1,150 +0,0 @@
-/*****************************************************************************
-* chap.h - Network Challenge Handshake Authentication Protocol header file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* portions Copyright (c) 1998 Global Election Systems Inc.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 97-12-03 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
-* Original built from BSD network code.
-******************************************************************************/
-/*
- * chap.h - Challenge Handshake Authentication Protocol definitions.
- *
- * Copyright (c) 1993 The Australian National University.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the Australian National University. The name of the University
- * may not be used to endorse or promote products derived from this
- * software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * Copyright (c) 1991 Gregory M. Christy
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by the author.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * $Id: chap.h,v 1.6 2010/01/24 13:19:34 goldsimon Exp $
- */
-
-#ifndef CHAP_H
-#define CHAP_H
-
-/* Code + ID + length */
-#define CHAP_HEADERLEN 4
-
-/*
- * CHAP codes.
- */
-
-#define CHAP_DIGEST_MD5 5 /* use MD5 algorithm */
-#define MD5_SIGNATURE_SIZE 16 /* 16 bytes in a MD5 message digest */
-#define CHAP_MICROSOFT 0x80 /* use Microsoft-compatible alg. */
-#define MS_CHAP_RESPONSE_LEN 49 /* Response length for MS-CHAP */
-
-#define CHAP_CHALLENGE 1
-#define CHAP_RESPONSE 2
-#define CHAP_SUCCESS 3
-#define CHAP_FAILURE 4
-
-/*
- * Challenge lengths (for challenges we send) and other limits.
- */
-#define MIN_CHALLENGE_LENGTH 32
-#define MAX_CHALLENGE_LENGTH 64
-#define MAX_RESPONSE_LENGTH 64 /* sufficient for MD5 or MS-CHAP */
-
-/*
- * Each interface is described by a chap structure.
- */
-
-typedef struct chap_state {
- int unit; /* Interface unit number */
- int clientstate; /* Client state */
- int serverstate; /* Server state */
- u_char challenge[MAX_CHALLENGE_LENGTH]; /* last challenge string sent */
- u_char chal_len; /* challenge length */
- u_char chal_id; /* ID of last challenge */
- u_char chal_type; /* hash algorithm for challenges */
- u_char id; /* Current id */
- char *chal_name; /* Our name to use with challenge */
- int chal_interval; /* Time until we challenge peer again */
- int timeouttime; /* Timeout time in seconds */
- int max_transmits; /* Maximum # of challenge transmissions */
- int chal_transmits; /* Number of transmissions of challenge */
- int resp_transmits; /* Number of transmissions of response */
- u_char response[MAX_RESPONSE_LENGTH]; /* Response to send */
- u_char resp_length; /* length of response */
- u_char resp_id; /* ID for response messages */
- u_char resp_type; /* hash algorithm for responses */
- char *resp_name; /* Our name to send with response */
-} chap_state;
-
-
-/*
- * Client (peer) states.
- */
-#define CHAPCS_INITIAL 0 /* Lower layer down, not opened */
-#define CHAPCS_CLOSED 1 /* Lower layer up, not opened */
-#define CHAPCS_PENDING 2 /* Auth us to peer when lower up */
-#define CHAPCS_LISTEN 3 /* Listening for a challenge */
-#define CHAPCS_RESPONSE 4 /* Sent response, waiting for status */
-#define CHAPCS_OPEN 5 /* We've received Success */
-
-/*
- * Server (authenticator) states.
- */
-#define CHAPSS_INITIAL 0 /* Lower layer down, not opened */
-#define CHAPSS_CLOSED 1 /* Lower layer up, not opened */
-#define CHAPSS_PENDING 2 /* Auth peer when lower up */
-#define CHAPSS_INITIAL_CHAL 3 /* We've sent the first challenge */
-#define CHAPSS_OPEN 4 /* We've sent a Success msg */
-#define CHAPSS_RECHALLENGE 5 /* We've sent another challenge */
-#define CHAPSS_BADAUTH 6 /* We've sent a Failure msg */
-
-extern chap_state chap[];
-
-void ChapAuthWithPeer (int, char *, u_char);
-void ChapAuthPeer (int, char *, u_char);
-
-extern struct protent chap_protent;
-
-#endif /* CHAP_H */
diff --git a/lwip/src/netif/ppp/chap_ms.c b/lwip/src/netif/ppp/chap_ms.c
new file mode 100644
index 0000000..5a989c9
--- /dev/null
+++ b/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/lwip/src/netif/ppp/chpms.c b/lwip/src/netif/ppp/chpms.c
deleted file mode 100644
index 81a887b..0000000
--- a/lwip/src/netif/ppp/chpms.c
+++ /dev/null
@@ -1,396 +0,0 @@
-/*** WARNING - THIS CODE HAS NOT BEEN FINISHED! ***/
-/*** The original PPPD code is written in a way to require either the UNIX DES
- encryption functions encrypt(3) and setkey(3) or the DES library libdes.
- Since both is not included in lwIP, MSCHAP currently does not work! */
-/*****************************************************************************
-* chpms.c - Network MicroSoft Challenge Handshake Authentication Protocol program file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 97-12-08 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
-* Original based on BSD chap_ms.c.
-*****************************************************************************/
-/*
- * chap_ms.c - Microsoft MS-CHAP compatible implementation.
- *
- * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
- * http://www.strataware.com/
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Eric Rosenquist. The name of the author may not be used to
- * endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
-
-/*
- * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
- *
- * Implemented LANManager type password response to MS-CHAP challenges.
- * Now pppd provides both NT style and LANMan style blocks, and the
- * prefered is set by option "ms-lanman". Default is to use NT.
- * The hash text (StdText) was taken from Win95 RASAPI32.DLL.
- *
- * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
- */
-
-#define USE_CRYPT
-
-#include "lwip/opt.h"
-
-#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
-
-#if MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
-
-#include "ppp_impl.h"
-#include "pppdebug.h"
-
-#include "md4.h"
-#ifndef USE_CRYPT
-#include "des.h"
-#endif
-#include "chap.h"
-#include "chpms.h"
-
-#include <string.h>
-
-
-/*************************/
-/*** LOCAL DEFINITIONS ***/
-/*************************/
-
-
-/************************/
-/*** LOCAL DATA TYPES ***/
-/************************/
-typedef struct {
- u_char LANManResp[24];
- u_char NTResp[24];
- u_char UseNT; /* If 1, ignore the LANMan response field */
-} MS_ChapResponse;
-/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
- in case this struct gets padded. */
-
-
-
-/***********************************/
-/*** LOCAL FUNCTION DECLARATIONS ***/
-/***********************************/
-
-/* XXX Don't know what to do with these. */
-extern void setkey(const char *);
-extern void encrypt(char *, int);
-
-static void DesEncrypt (u_char *, u_char *, u_char *);
-static void MakeKey (u_char *, u_char *);
-
-#ifdef USE_CRYPT
-static void Expand (u_char *, u_char *);
-static void Collapse (u_char *, u_char *);
-#endif
-
-static void ChallengeResponse(
- u_char *challenge, /* IN 8 octets */
- u_char *pwHash, /* IN 16 octets */
- u_char *response /* OUT 24 octets */
-);
-static void ChapMS_NT(
- char *rchallenge,
- int rchallenge_len,
- char *secret,
- int secret_len,
- MS_ChapResponse *response
-);
-static u_char Get7Bits(
- u_char *input,
- int startBit
-);
-
-static void
-ChallengeResponse( u_char *challenge, /* IN 8 octets */
- u_char *pwHash, /* IN 16 octets */
- u_char *response /* OUT 24 octets */)
-{
- u_char ZPasswordHash[21];
-
- BZERO(ZPasswordHash, sizeof(ZPasswordHash));
- BCOPY(pwHash, ZPasswordHash, 16);
-
-#if 0
- log_packet(ZPasswordHash, sizeof(ZPasswordHash), "ChallengeResponse - ZPasswordHash", LOG_DEBUG);
-#endif
-
- DesEncrypt(challenge, ZPasswordHash + 0, response + 0);
- DesEncrypt(challenge, ZPasswordHash + 7, response + 8);
- DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
-
-#if 0
- log_packet(response, 24, "ChallengeResponse - response", LOG_DEBUG);
-#endif
-}
-
-
-#ifdef USE_CRYPT
-static void
-DesEncrypt( u_char *clear, /* IN 8 octets */
- u_char *key, /* IN 7 octets */
- u_char *cipher /* OUT 8 octets */)
-{
- u_char des_key[8];
- u_char crypt_key[66];
- u_char des_input[66];
-
- MakeKey(key, des_key);
-
- Expand(des_key, crypt_key);
- setkey((char*)crypt_key);
-
-#if 0
- CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n",
- clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7]));
-#endif
-
- Expand(clear, des_input);
- encrypt((char*)des_input, 0);
- Collapse(des_input, cipher);
-
-#if 0
- CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n",
- cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7]));
-#endif
-}
-
-#else /* USE_CRYPT */
-
-static void
-DesEncrypt( u_char *clear, /* IN 8 octets */
- u_char *key, /* IN 7 octets */
- u_char *cipher /* OUT 8 octets */)
-{
- des_cblock des_key;
- des_key_schedule key_schedule;
-
- MakeKey(key, des_key);
-
- des_set_key(&des_key, key_schedule);
-
-#if 0
- CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n",
- clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7]));
-#endif
-
- des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
-
-#if 0
- CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n",
- cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7]));
-#endif
-}
-
-#endif /* USE_CRYPT */
-
-
-static u_char
-Get7Bits( u_char *input, int startBit)
-{
- register unsigned int word;
-
- word = (unsigned)input[startBit / 8] << 8;
- word |= (unsigned)input[startBit / 8 + 1];
-
- word >>= 15 - (startBit % 8 + 7);
-
- return word & 0xFE;
-}
-
-#ifdef USE_CRYPT
-
-/* in == 8-byte string (expanded version of the 56-bit key)
- * out == 64-byte string where each byte is either 1 or 0
- * Note that the low-order "bit" is always ignored by by setkey()
- */
-static void
-Expand(u_char *in, u_char *out)
-{
- int j, c;
- int i;
-
- for(i = 0; i < 64; in++){
- c = *in;
- for(j = 7; j >= 0; j--) {
- *out++ = (c >> j) & 01;
- }
- i += 8;
- }
-}
-
-/* The inverse of Expand
- */
-static void
-Collapse(u_char *in, u_char *out)
-{
- int j;
- int i;
- unsigned int c;
-
- for (i = 0; i < 64; i += 8, out++) {
- c = 0;
- for (j = 7; j >= 0; j--, in++) {
- c |= *in << j;
- }
- *out = c & 0xff;
- }
-}
-#endif
-
-static void
-MakeKey( u_char *key, /* IN 56 bit DES key missing parity bits */
- u_char *des_key /* OUT 64 bit DES key with parity bits added */)
-{
- des_key[0] = Get7Bits(key, 0);
- des_key[1] = Get7Bits(key, 7);
- des_key[2] = Get7Bits(key, 14);
- des_key[3] = Get7Bits(key, 21);
- des_key[4] = Get7Bits(key, 28);
- des_key[5] = Get7Bits(key, 35);
- des_key[6] = Get7Bits(key, 42);
- des_key[7] = Get7Bits(key, 49);
-
-#ifndef USE_CRYPT
- des_set_odd_parity((des_cblock *)des_key);
-#endif
-
-#if 0
- CHAPDEBUG(LOG_INFO, ("MakeKey: 56-bit input : %02X%02X%02X%02X%02X%02X%02X\n",
- key[0], key[1], key[2], key[3], key[4], key[5], key[6]));
- CHAPDEBUG(LOG_INFO, ("MakeKey: 64-bit output: %02X%02X%02X%02X%02X%02X%02X%02X\n",
- des_key[0], des_key[1], des_key[2], des_key[3], des_key[4], des_key[5], des_key[6], des_key[7]));
-#endif
-}
-
-static void
-ChapMS_NT( char *rchallenge,
- int rchallenge_len,
- char *secret,
- int secret_len,
- MS_ChapResponse *response)
-{
- int i;
- MDstruct md4Context;
- u_char unicodePassword[MAX_NT_PASSWORD * 2];
- static int low_byte_first = -1;
-
- LWIP_UNUSED_ARG(rchallenge_len);
-
- /* Initialize the Unicode version of the secret (== password). */
- /* This implicitly supports 8-bit ISO8859/1 characters. */
- BZERO(unicodePassword, sizeof(unicodePassword));
- for (i = 0; i < secret_len; i++) {
- unicodePassword[i * 2] = (u_char)secret[i];
- }
- MDbegin(&md4Context);
- MDupdate(&md4Context, unicodePassword, secret_len * 2 * 8); /* Unicode is 2 bytes/char, *8 for bit count */
-
- if (low_byte_first == -1) {
- low_byte_first = (PP_HTONS((unsigned short int)1) != 1);
- }
- if (low_byte_first == 0) {
- /* @todo: arg type - u_long* or u_int* ? */
- MDreverse((unsigned int*)&md4Context); /* sfb 961105 */
- }
-
- MDupdate(&md4Context, NULL, 0); /* Tell MD4 we're done */
-
- ChallengeResponse((u_char*)rchallenge, (u_char*)md4Context.buffer, response->NTResp);
-}
-
-#ifdef MSLANMAN
-static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
-
-static void
-ChapMS_LANMan( char *rchallenge,
- int rchallenge_len,
- char *secret,
- int secret_len,
- MS_ChapResponse *response)
-{
- int i;
- u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
- u_char PasswordHash[16];
-
- /* LANMan password is case insensitive */
- BZERO(UcasePassword, sizeof(UcasePassword));
- for (i = 0; i < secret_len; i++) {
- UcasePassword[i] = (u_char)toupper(secret[i]);
- }
- DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 );
- DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 );
- ChallengeResponse(rchallenge, PasswordHash, response->LANManResp);
-}
-#endif
-
-void
-ChapMS( chap_state *cstate, char *rchallenge, int rchallenge_len, char *secret, int secret_len)
-{
- MS_ChapResponse response;
-#ifdef MSLANMAN
- extern int ms_lanman;
-#endif
-
-#if 0
- CHAPDEBUG(LOG_INFO, ("ChapMS: secret is '%.*s'\n", secret_len, secret));
-#endif
- BZERO(&response, sizeof(response));
-
- /* Calculate both always */
- ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response);
-
-#ifdef MSLANMAN
- ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response);
-
- /* prefered method is set by option */
- response.UseNT = !ms_lanman;
-#else
- response.UseNT = 1;
-#endif
-
- BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN);
- cstate->resp_length = MS_CHAP_RESPONSE_LEN;
-}
-
-#endif /* MSCHAP_SUPPORT */
-
-#endif /* PPP_SUPPORT */
diff --git a/lwip/src/netif/ppp/chpms.h b/lwip/src/netif/ppp/chpms.h
deleted file mode 100644
index df070fb..0000000
--- a/lwip/src/netif/ppp/chpms.h
+++ /dev/null
@@ -1,64 +0,0 @@
-/*****************************************************************************
-* chpms.h - Network Microsoft Challenge Handshake Protocol header file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* portions Copyright (c) 1998 Global Election Systems Inc.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 98-01-30 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
-* Original built from BSD network code.
-******************************************************************************/
-/*
- * chap.h - Challenge Handshake Authentication Protocol definitions.
- *
- * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
- * http://www.strataware.com/
- *
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Eric Rosenquist. The name of the author may not be used to
- * endorse or promote products derived from this software without
- * specific prior written permission.
- *
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * $Id: chpms.h,v 1.5 2007/12/19 20:47:23 fbernon Exp $
- */
-
-#ifndef CHPMS_H
-#define CHPMS_H
-
-#define MAX_NT_PASSWORD 256 /* Maximum number of (Unicode) chars in an NT password */
-
-void ChapMS (chap_state *, char *, int, char *, int);
-
-#endif /* CHPMS_H */
diff --git a/lwip/src/netif/ppp/demand.c b/lwip/src/netif/ppp/demand.c
new file mode 100644
index 0000000..26c6c30
--- /dev/null
+++ b/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/lwip/src/netif/ppp/eap.c b/lwip/src/netif/ppp/eap.c
new file mode 100644
index 0000000..8fb5636
--- /dev/null
+++ b/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/lwip/src/netif/ppp/ecp.c b/lwip/src/netif/ppp/ecp.c
new file mode 100644
index 0000000..4d84f60
--- /dev/null
+++ b/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/lwip/src/netif/ppp/eui64.c b/lwip/src/netif/ppp/eui64.c
new file mode 100644
index 0000000..01493bc
--- /dev/null
+++ b/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/lwip/src/netif/ppp/fsm.c b/lwip/src/netif/ppp/fsm.c
index e8a254e..81eba11 100644
--- a/lwip/src/netif/ppp/fsm.c
+++ b/lwip/src/netif/ppp/fsm.c
@@ -1,151 +1,113 @@
-/*****************************************************************************
-* fsm.c - Network Control Protocol Finite State Machine program file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* portions Copyright (c) 1997 by Global Election Systems Inc.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 97-12-01 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
-* Original based on BSD fsm.c.
-*****************************************************************************/
/*
* fsm.c - {Link, IP} Control Protocol Finite State Machine.
*
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
*
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above 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:
+ * @todo:
* Randomize fsm id on link/init.
* Deal with variable outgoing MTU.
*/
-#include "lwip/opt.h"
-
-#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
-
-#include "ppp_impl.h"
-#include "pppdebug.h"
-
-#include "fsm.h"
-
+#if 0 /* UNUSED */
+#include <stdio.h>
#include <string.h>
+#include <sys/types.h>
+#endif /* UNUSED */
-#if PPP_DEBUG
-static const char *ppperr_strerr[] = {
- "LS_INITIAL", /* LS_INITIAL 0 */
- "LS_STARTING", /* LS_STARTING 1 */
- "LS_CLOSED", /* LS_CLOSED 2 */
- "LS_STOPPED", /* LS_STOPPED 3 */
- "LS_CLOSING", /* LS_CLOSING 4 */
- "LS_STOPPING", /* LS_STOPPING 5 */
- "LS_REQSENT", /* LS_REQSENT 6 */
- "LS_ACKRCVD", /* LS_ACKRCVD 7 */
- "LS_ACKSENT", /* LS_ACKSENT 8 */
- "LS_OPENED" /* LS_OPENED 9 */
-};
-#endif /* PPP_DEBUG */
+#include "netif/ppp/ppp_impl.h"
-static void fsm_timeout (void *);
-static void fsm_rconfreq (fsm *, u_char, u_char *, int);
-static void fsm_rconfack (fsm *, int, u_char *, int);
-static void fsm_rconfnakrej (fsm *, int, int, u_char *, int);
-static void fsm_rtermreq (fsm *, int, u_char *, int);
-static void fsm_rtermack (fsm *);
-static void fsm_rcoderej (fsm *, u_char *, int);
-static void fsm_sconfreq (fsm *, int);
+#include "netif/ppp/fsm.h"
-#define PROTO_NAME(f) ((f)->callbacks->proto_name)
-
-int peer_mru[NUM_PPP];
+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)
-{
- f->state = LS_INITIAL;
- f->flags = 0;
- f->id = 0; /* XXX Start with random id? */
- f->timeouttime = FSM_DEFTIMEOUT;
- f->maxconfreqtransmits = FSM_DEFMAXCONFREQS;
- f->maxtermtransmits = FSM_DEFMAXTERMREQS;
- f->maxnakloops = FSM_DEFMAXNAKLOOPS;
- f->term_reason_len = 0;
+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)
-{
- int oldState = f->state;
-
- LWIP_UNUSED_ARG(oldState);
-
- switch( f->state ) {
- case LS_INITIAL:
- f->state = LS_CLOSED;
- break;
-
- case LS_STARTING:
- if( f->flags & OPT_SILENT ) {
- f->state = LS_STOPPED;
- } else {
- /* Send an initial configure-request */
- fsm_sconfreq(f, 0);
- f->state = LS_REQSENT;
- }
- break;
+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(LOG_INFO, ("%s: Up event in state %d (%s)!\n",
- PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
- }
-
- FSMDEBUG(LOG_INFO, ("%s: lowerup state %d (%s) -> %d (%s)\n",
- PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
+ FSMDEBUG(("%s: Up event in state %d!", PROTO_NAME(f), f->state));
+ /* no break */
+ }
}
@@ -154,601 +116,545 @@ fsm_lowerup(fsm *f)
*
* Cancel all timeouts and inform upper layers.
*/
-void
-fsm_lowerdown(fsm *f)
-{
- int oldState = f->state;
-
- LWIP_UNUSED_ARG(oldState);
-
- switch( f->state ) {
- case LS_CLOSED:
- f->state = LS_INITIAL;
- break;
-
- case LS_STOPPED:
- f->state = LS_STARTING;
- if( f->callbacks->starting ) {
- (*f->callbacks->starting)(f);
- }
- break;
-
- case LS_CLOSING:
- f->state = LS_INITIAL;
- UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
- break;
-
- case LS_STOPPING:
- case LS_REQSENT:
- case LS_ACKRCVD:
- case LS_ACKSENT:
- f->state = LS_STARTING;
- UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
- break;
-
- case LS_OPENED:
- if( f->callbacks->down ) {
- (*f->callbacks->down)(f);
- }
- f->state = LS_STARTING;
- break;
+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(LOG_INFO, ("%s: Down event in state %d (%s)!\n",
- PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
- }
-
- FSMDEBUG(LOG_INFO, ("%s: lowerdown state %d (%s) -> %d (%s)\n",
- PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
+ 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)
-{
- int oldState = f->state;
-
- LWIP_UNUSED_ARG(oldState);
-
- switch( f->state ) {
- case LS_INITIAL:
- f->state = LS_STARTING;
- if( f->callbacks->starting ) {
- (*f->callbacks->starting)(f);
- }
- break;
-
- case LS_CLOSED:
- if( f->flags & OPT_SILENT ) {
- f->state = LS_STOPPED;
- } else {
- /* Send an initial configure-request */
- fsm_sconfreq(f, 0);
- f->state = LS_REQSENT;
- }
- break;
-
- case LS_CLOSING:
- f->state = LS_STOPPING;
- /* fall through */
- case LS_STOPPED:
- case LS_OPENED:
- if( f->flags & OPT_RESTART ) {
- fsm_lowerdown(f);
- fsm_lowerup(f);
- }
- break;
- }
-
- FSMDEBUG(LOG_INFO, ("%s: open state %d (%s) -> %d (%s)\n",
- PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
+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;
+ }
}
-#if 0 /* backport pppd 2.4.4b1; */
/*
* terminate_layer - Start process of shutting down the FSM
*
* Cancel any timeout running, notify upper layers we're done, and
* send a terminate-request message as configured.
*/
-static void
-terminate_layer(fsm *f, int nextstate)
-{
- /* @todo */
+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;
}
-#endif
/*
* fsm_close - Start closing connection.
*
* Cancel timeouts and either initiate close or possibly go directly to
- * the LS_CLOSED state.
+ * the PPP_FSM_CLOSED state.
*/
-void
-fsm_close(fsm *f, char *reason)
-{
- int oldState = f->state;
-
- LWIP_UNUSED_ARG(oldState);
-
- f->term_reason = reason;
- f->term_reason_len = (reason == NULL ? 0 : (int)strlen(reason));
- switch( f->state ) {
- case LS_STARTING:
- f->state = LS_INITIAL;
- break;
- case LS_STOPPED:
- f->state = LS_CLOSED;
- break;
- case LS_STOPPING:
- f->state = LS_CLOSING;
- break;
-
- case LS_REQSENT:
- case LS_ACKRCVD:
- case LS_ACKSENT:
- case LS_OPENED:
- if( f->state != LS_OPENED ) {
- UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
- } else if( f->callbacks->down ) {
- (*f->callbacks->down)(f); /* Inform upper layers we're down */
- }
- /* Init restart counter, send Terminate-Request */
- f->retransmits = f->maxtermtransmits;
- fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
- (u_char *) f->term_reason, f->term_reason_len);
- TIMEOUT(fsm_timeout, f, f->timeouttime);
- --f->retransmits;
-
- f->state = LS_CLOSING;
- break;
- }
-
- FSMDEBUG(LOG_INFO, ("%s: close reason=%s state %d (%s) -> %d (%s)\n",
- PROTO_NAME(f), reason, oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
+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;
-
- switch (f->state) {
- case LS_CLOSING:
- case LS_STOPPING:
- if( f->retransmits <= 0 ) {
- FSMDEBUG(LOG_WARNING, ("%s: timeout sending Terminate-Request state=%d (%s)\n",
- PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
- /*
- * We've waited for an ack long enough. Peer probably heard us.
- */
- f->state = (f->state == LS_CLOSING)? LS_CLOSED: LS_STOPPED;
- if( f->callbacks->finished ) {
- (*f->callbacks->finished)(f);
- }
- } else {
- FSMDEBUG(LOG_WARNING, ("%s: timeout resending Terminate-Requests state=%d (%s)\n",
- PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
- /* Send Terminate-Request */
- fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
- (u_char *) f->term_reason, f->term_reason_len);
- TIMEOUT(fsm_timeout, f, f->timeouttime);
- --f->retransmits;
- }
- break;
-
- case LS_REQSENT:
- case LS_ACKRCVD:
- case LS_ACKSENT:
- if (f->retransmits <= 0) {
- FSMDEBUG(LOG_WARNING, ("%s: timeout sending Config-Requests state=%d (%s)\n",
- PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
- f->state = LS_STOPPED;
- if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) {
- (*f->callbacks->finished)(f);
- }
- } else {
- FSMDEBUG(LOG_WARNING, ("%s: timeout resending Config-Request state=%d (%s)\n",
- PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
- /* Retransmit the configure-request */
- if (f->callbacks->retransmit) {
- (*f->callbacks->retransmit)(f);
- }
- fsm_sconfreq(f, 1); /* Re-send Configure-Request */
- if( f->state == LS_ACKRCVD ) {
- f->state = LS_REQSENT;
- }
- }
- break;
+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(LOG_INFO, ("%s: UNHANDLED timeout event in state %d (%s)!\n",
- PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
- }
+ 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 = inpacket;
- u_char code, id;
- int len;
-
- /*
- * Parse header (code, id and length).
- * If packet too short, drop it.
- */
- if (l < HEADERLEN) {
- FSMDEBUG(LOG_WARNING, ("fsm_input(%x): Rcvd short header.\n",
- f->protocol));
- return;
- }
- GETCHAR(code, inp);
- GETCHAR(id, inp);
- GETSHORT(len, inp);
- if (len < HEADERLEN) {
- FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd illegal length.\n",
- f->protocol));
- return;
- }
- if (len > l) {
- FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd short packet.\n",
- f->protocol));
- return;
- }
- len -= HEADERLEN; /* subtract header length */
-
- if( f->state == LS_INITIAL || f->state == LS_STARTING ) {
- FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd packet in state %d (%s).\n",
- f->protocol, f->state, ppperr_strerr[f->state]));
- return;
- }
- FSMDEBUG(LOG_INFO, ("fsm_input(%s):%d,%d,%d\n", PROTO_NAME(f), code, id, l));
- /*
- * Action depends on code.
- */
- switch (code) {
+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;
+ fsm_rconfreq(f, id, inp, len);
+ break;
case CONFACK:
- fsm_rconfack(f, id, inp, len);
- break;
+ fsm_rconfack(f, id, inp, len);
+ break;
case CONFNAK:
case CONFREJ:
- fsm_rconfnakrej(f, code, id, inp, len);
- break;
+ fsm_rconfnakrej(f, code, id, inp, len);
+ break;
case TERMREQ:
- fsm_rtermreq(f, id, inp, len);
- break;
+ fsm_rtermreq(f, id, inp, len);
+ break;
case TERMACK:
- fsm_rtermack(f);
- break;
+ fsm_rtermack(f);
+ break;
case CODEREJ:
- fsm_rcoderej(f, inp, len);
- break;
+ fsm_rcoderej(f, inp, len);
+ break;
default:
- FSMDEBUG(LOG_INFO, ("fsm_input(%s): default: \n", PROTO_NAME(f)));
- if( !f->callbacks->extcode ||
- !(*f->callbacks->extcode)(f, code, id, inp, len) ) {
- fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
- }
- break;
- }
+ if( !f->callbacks->extcode
+ || !(*f->callbacks->extcode)(f, code, id, inp, len) )
+ fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
+ break;
+ }
}
/*
* fsm_rconfreq - Receive Configure-Request.
*/
-static void
-fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len)
-{
- int code, reject_if_disagree;
-
- FSMDEBUG(LOG_INFO, ("fsm_rconfreq(%s): Rcvd id %d state=%d (%s)\n",
- PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
- switch( f->state ) {
- case LS_CLOSED:
- /* Go away, we're closed */
- fsm_sdata(f, TERMACK, id, NULL, 0);
- return;
- case LS_CLOSING:
- case LS_STOPPING:
- return;
-
- case LS_OPENED:
- /* Go down and restart negotiation */
- if( f->callbacks->down ) {
- (*f->callbacks->down)(f); /* Inform upper layers */
- }
- fsm_sconfreq(f, 0); /* Send initial Configure-Request */
- break;
-
- case LS_STOPPED:
- /* Negotiation started by our peer */
- fsm_sconfreq(f, 0); /* Send initial Configure-Request */
- f->state = LS_REQSENT;
- break;
- }
-
- /*
- * Pass the requested configuration options
- * to protocol-specific code for checking.
- */
- if (f->callbacks->reqci) { /* Check CI */
- reject_if_disagree = (f->nakloops >= f->maxnakloops);
- code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
- } else if (len) {
- code = CONFREJ; /* Reject all CI */
- } else {
- code = CONFACK;
- }
-
- /* send the Ack, Nak or Rej to the peer */
- fsm_sdata(f, (u_char)code, id, inp, len);
-
- if (code == CONFACK) {
- if (f->state == LS_ACKRCVD) {
- UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
- f->state = LS_OPENED;
- if (f->callbacks->up) {
- (*f->callbacks->up)(f); /* Inform upper layers */
- }
- } else {
- f->state = LS_ACKSENT;
- }
- f->nakloops = 0;
- } else {
- /* we sent CONFACK or CONFREJ */
- if (f->state != LS_ACKRCVD) {
- f->state = LS_REQSENT;
+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;
}
- if( code == CONFNAK ) {
- ++f->nakloops;
+
+ /*
+ * 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)
-{
- FSMDEBUG(LOG_INFO, ("fsm_rconfack(%s): Rcvd id %d state=%d (%s)\n",
- PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
-
- if (id != f->reqid || f->seen_ack) { /* Expected id? */
- return; /* Nope, toss... */
- }
- if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ) {
- /* Ack is bad - ignore it */
- FSMDEBUG(LOG_INFO, ("%s: received bad Ack (length %d)\n",
- PROTO_NAME(f), len));
- return;
- }
- f->seen_ack = 1;
-
- switch (f->state) {
- case LS_CLOSED:
- case LS_STOPPED:
- fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
- break;
-
- case LS_REQSENT:
- f->state = LS_ACKRCVD;
- f->retransmits = f->maxconfreqtransmits;
- break;
-
- case LS_ACKRCVD:
- /* Huh? an extra valid Ack? oh well... */
- UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
- fsm_sconfreq(f, 0);
- f->state = LS_REQSENT;
- break;
-
- case LS_ACKSENT:
- UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
- f->state = LS_OPENED;
- f->retransmits = f->maxconfreqtransmits;
- if (f->callbacks->up) {
- (*f->callbacks->up)(f); /* Inform upper layers */
- }
- break;
-
- case LS_OPENED:
- /* Go down and restart negotiation */
- if (f->callbacks->down) {
- (*f->callbacks->down)(f); /* Inform upper layers */
- }
- fsm_sconfreq(f, 0); /* Send initial Configure-Request */
- f->state = LS_REQSENT;
- break;
- }
+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 (*proc) (fsm *, u_char *, int);
- int ret;
-
- FSMDEBUG(LOG_INFO, ("fsm_rconfnakrej(%s): Rcvd id %d state=%d (%s)\n",
- PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
-
- if (id != f->reqid || f->seen_ack) { /* Expected id? */
- return; /* Nope, toss... */
- }
- proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
- if (!proc || !((ret = proc(f, inp, len)))) {
- /* Nak/reject is bad - ignore it */
- FSMDEBUG(LOG_INFO, ("%s: received bad %s (length %d)\n",
- PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len));
- return;
- }
- f->seen_ack = 1;
-
- switch (f->state) {
- case LS_CLOSED:
- case LS_STOPPED:
- fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
- break;
-
- case LS_REQSENT:
- case LS_ACKSENT:
- /* They didn't agree to what we wanted - try another request */
- UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
- if (ret < 0) {
- f->state = LS_STOPPED; /* kludge for stopping CCP */
- } else {
- fsm_sconfreq(f, 0); /* Send Configure-Request */
- }
- break;
-
- case LS_ACKRCVD:
- /* Got a Nak/reject when we had already had an Ack?? oh well... */
- UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
- fsm_sconfreq(f, 0);
- f->state = LS_REQSENT;
- break;
-
- case LS_OPENED:
- /* Go down and restart negotiation */
- if (f->callbacks->down) {
- (*f->callbacks->down)(f); /* Inform upper layers */
- }
- fsm_sconfreq(f, 0); /* Send initial Configure-Request */
- f->state = LS_REQSENT;
- break;
- }
+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)
-{
- LWIP_UNUSED_ARG(p);
-
- FSMDEBUG(LOG_INFO, ("fsm_rtermreq(%s): Rcvd id %d state=%d (%s)\n",
- PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
-
- switch (f->state) {
- case LS_ACKRCVD:
- case LS_ACKSENT:
- f->state = LS_REQSENT; /* Start over but keep trying */
- break;
-
- case LS_OPENED:
- if (len > 0) {
- FSMDEBUG(LOG_INFO, ("%s terminated by peer (%p)\n", PROTO_NAME(f), p));
- } else {
- FSMDEBUG(LOG_INFO, ("%s terminated by peer\n", PROTO_NAME(f)));
- }
- if (f->callbacks->down) {
- (*f->callbacks->down)(f); /* Inform upper layers */
- }
- f->retransmits = 0;
- f->state = LS_STOPPING;
- TIMEOUT(fsm_timeout, f, f->timeouttime);
- break;
- }
-
- fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
+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)
-{
- FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): state=%d (%s)\n",
- PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
-
- switch (f->state) {
- case LS_CLOSING:
- UNTIMEOUT(fsm_timeout, f);
- f->state = LS_CLOSED;
- if( f->callbacks->finished ) {
- (*f->callbacks->finished)(f);
- }
- break;
-
- case LS_STOPPING:
- UNTIMEOUT(fsm_timeout, f);
- f->state = LS_STOPPED;
- if( f->callbacks->finished ) {
- (*f->callbacks->finished)(f);
- }
- break;
-
- case LS_ACKRCVD:
- f->state = LS_REQSENT;
- break;
-
- case LS_OPENED:
- if (f->callbacks->down) {
- (*f->callbacks->down)(f); /* Inform upper layers */
- }
- fsm_sconfreq(f, 0);
- break;
+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:
- FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): UNHANDLED state=%d (%s)!!!\n",
- PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
- }
+ break;
+ }
}
/*
* fsm_rcoderej - Receive an Code-Reject.
*/
-static void
-fsm_rcoderej(fsm *f, u_char *inp, int len)
-{
- u_char code, id;
-
- FSMDEBUG(LOG_INFO, ("fsm_rcoderej(%s): state=%d (%s)\n",
- PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
-
- if (len < HEADERLEN) {
- FSMDEBUG(LOG_INFO, ("fsm_rcoderej: Rcvd short Code-Reject packet!\n"));
- return;
- }
- GETCHAR(code, inp);
- GETCHAR(id, inp);
- FSMDEBUG(LOG_WARNING, ("%s: Rcvd Code-Reject for code %d, id %d\n",
- PROTO_NAME(f), code, id));
-
- if( f->state == LS_ACKRCVD ) {
- f->state = LS_REQSENT;
- }
+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;
}
@@ -757,50 +663,39 @@ fsm_rcoderej(fsm *f, u_char *inp, int len)
*
* Treat this as a catastrophic error (RXJ-).
*/
-void
-fsm_protreject(fsm *f)
-{
- switch( f->state ) {
- case LS_CLOSING:
- UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
- /* fall through */
- case LS_CLOSED:
- f->state = LS_CLOSED;
- if( f->callbacks->finished ) {
- (*f->callbacks->finished)(f);
- }
- break;
-
- case LS_STOPPING:
- case LS_REQSENT:
- case LS_ACKRCVD:
- case LS_ACKSENT:
- UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
- /* fall through */
- case LS_STOPPED:
- f->state = LS_STOPPED;
- if( f->callbacks->finished ) {
- (*f->callbacks->finished)(f);
- }
- break;
-
- case LS_OPENED:
- if( f->callbacks->down ) {
- (*f->callbacks->down)(f);
- }
- /* Init restart counter, send Terminate-Request */
- f->retransmits = f->maxtermtransmits;
- fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
- (u_char *) f->term_reason, f->term_reason_len);
- TIMEOUT(fsm_timeout, f, f->timeouttime);
- --f->retransmits;
-
- f->state = LS_STOPPING;
- break;
-
+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(LOG_INFO, ("%s: Protocol-reject event in state %d (%s)!\n",
- PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ FSMDEBUG(("%s: Protocol-reject event in state %d!",
+ PROTO_NAME(f), f->state));
+ /* no break */
}
}
@@ -808,53 +703,62 @@ fsm_protreject(fsm *f)
/*
* fsm_sconfreq - Send a Configure-Request.
*/
-static void
-fsm_sconfreq(fsm *f, int retransmit)
-{
- u_char *outp;
- int cilen;
-
- if( f->state != LS_REQSENT && f->state != LS_ACKRCVD && f->state != LS_ACKSENT ) {
- /* Not currently negotiating - reset options */
- if( f->callbacks->resetci ) {
- (*f->callbacks->resetci)(f);
+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;
}
- f->nakloops = 0;
- }
-
- if( !retransmit ) {
- /* New request - reset retransmission counter, use new ID */
- f->retransmits = f->maxconfreqtransmits;
- f->reqid = ++f->id;
- }
-
- f->seen_ack = 0;
-
- /*
- * Make up the request packet
- */
- outp = outpacket_buf[f->unit] + PPP_HDRLEN + HEADERLEN;
- if( f->callbacks->cilen && f->callbacks->addci ) {
- cilen = (*f->callbacks->cilen)(f);
- if( cilen > peer_mru[f->unit] - (int)HEADERLEN ) {
- cilen = peer_mru[f->unit] - HEADERLEN;
+
+ if( !retransmit ){
+ /* New request - reset retransmission counter, use new ID */
+ f->retransmits = pcb->settings.fsm_max_conf_req_transmits;
+ f->reqid = ++f->id;
}
- if (f->callbacks->addci) {
- (*f->callbacks->addci)(f, outp, &cilen);
+
+ 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);
}
- } else {
- cilen = 0;
- }
-
- /* send the request to our peer */
- fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
-
- /* start the retransmit timer */
- --f->retransmits;
- TIMEOUT(fsm_timeout, f, f->timeouttime);
-
- FSMDEBUG(LOG_INFO, ("%s: sending Configure-Request, id %d\n",
- PROTO_NAME(f), f->reqid));
+
+ ppp_write(pcb, p);
+
+ /* start the retransmit timer */
+ --f->retransmits;
+ TIMEOUT(fsm_timeout, f, pcb->settings.fsm_timeout_time);
}
@@ -863,28 +767,33 @@ fsm_sconfreq(fsm *f, int retransmit)
*
* Used for all packets sent to our peer by this module.
*/
-void
-fsm_sdata( fsm *f, u_char code, u_char id, u_char *data, int datalen)
-{
- u_char *outp;
- int outlen;
-
- /* Adjust length to be smaller than MTU */
- outp = outpacket_buf[f->unit];
- if (datalen > peer_mru[f->unit] - (int)HEADERLEN) {
- datalen = peer_mru[f->unit] - HEADERLEN;
- }
- if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) {
- BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
- }
- outlen = datalen + HEADERLEN;
- MAKEHEADER(outp, f->protocol);
- PUTCHAR(code, outp);
- PUTCHAR(id, outp);
- PUTSHORT(outlen, outp);
- pppWrite(f->unit, outpacket_buf[f->unit], outlen + PPP_HDRLEN);
- FSMDEBUG(LOG_INFO, ("fsm_sdata(%s): Sent code %d,%d,%d.\n",
- PROTO_NAME(f), code, id, outlen));
+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/lwip/src/netif/ppp/fsm.h b/lwip/src/netif/ppp/fsm.h
deleted file mode 100644
index 8d41b5f..0000000
--- a/lwip/src/netif/ppp/fsm.h
+++ /dev/null
@@ -1,157 +0,0 @@
-/*****************************************************************************
-* fsm.h - Network Control Protocol Finite State Machine header file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* Copyright (c) 1997 Global Election Systems Inc.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 97-11-05 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
-* Original based on BSD code.
-*****************************************************************************/
-/*
- * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions.
- *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * $Id: fsm.h,v 1.5 2009/12/31 17:08:08 goldsimon Exp $
- */
-
-#ifndef FSM_H
-#define FSM_H
-
-/*
- * LCP Packet header = Code, id, length.
- */
-#define HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
-
-
-/*
- * CP (LCP, IPCP, etc.) codes.
- */
-#define CONFREQ 1 /* Configuration Request */
-#define CONFACK 2 /* Configuration Ack */
-#define CONFNAK 3 /* Configuration Nak */
-#define CONFREJ 4 /* Configuration Reject */
-#define TERMREQ 5 /* Termination Request */
-#define TERMACK 6 /* Termination Ack */
-#define CODEREJ 7 /* Code Reject */
-
-
-/*
- * Each FSM is described by an fsm structure and fsm callbacks.
- */
-typedef struct fsm {
- int unit; /* Interface unit number */
- u_short protocol; /* Data Link Layer Protocol field value */
- int state; /* State */
- int flags; /* Contains option bits */
- u_char id; /* Current id */
- u_char reqid; /* Current request id */
- u_char seen_ack; /* Have received valid Ack/Nak/Rej to Req */
- int timeouttime; /* Timeout time in milliseconds */
- int maxconfreqtransmits; /* Maximum Configure-Request transmissions */
- int retransmits; /* Number of retransmissions left */
- int maxtermtransmits; /* Maximum Terminate-Request transmissions */
- int nakloops; /* Number of nak loops since last ack */
- int maxnakloops; /* Maximum number of nak loops tolerated */
- struct fsm_callbacks* callbacks; /* Callback routines */
- char* term_reason; /* Reason for closing protocol */
- int term_reason_len; /* Length of term_reason */
-} fsm;
-
-
-typedef struct fsm_callbacks {
- void (*resetci)(fsm*); /* Reset our Configuration Information */
- int (*cilen)(fsm*); /* Length of our Configuration Information */
- void (*addci)(fsm*, u_char*, int*); /* Add our Configuration Information */
- int (*ackci)(fsm*, u_char*, int); /* ACK our Configuration Information */
- int (*nakci)(fsm*, u_char*, int); /* NAK our Configuration Information */
- int (*rejci)(fsm*, u_char*, int); /* Reject our Configuration Information */
- int (*reqci)(fsm*, u_char*, int*, int); /* Request peer's Configuration Information */
- void (*up)(fsm*); /* Called when fsm reaches LS_OPENED state */
- void (*down)(fsm*); /* Called when fsm leaves LS_OPENED state */
- void (*starting)(fsm*); /* Called when we want the lower layer */
- void (*finished)(fsm*); /* Called when we don't want the lower layer */
- void (*protreject)(int); /* Called when Protocol-Reject received */
- void (*retransmit)(fsm*); /* Retransmission is necessary */
- int (*extcode)(fsm*, int, u_char, u_char*, int); /* Called when unknown code received */
- char *proto_name; /* String name for protocol (for messages) */
-} fsm_callbacks;
-
-
-/*
- * Link states.
- */
-#define LS_INITIAL 0 /* Down, hasn't been opened */
-#define LS_STARTING 1 /* Down, been opened */
-#define LS_CLOSED 2 /* Up, hasn't been opened */
-#define LS_STOPPED 3 /* Open, waiting for down event */
-#define LS_CLOSING 4 /* Terminating the connection, not open */
-#define LS_STOPPING 5 /* Terminating, but open */
-#define LS_REQSENT 6 /* We've sent a Config Request */
-#define LS_ACKRCVD 7 /* We've received a Config Ack */
-#define LS_ACKSENT 8 /* We've sent a Config Ack */
-#define LS_OPENED 9 /* Connection available */
-
-/*
- * Flags - indicate options controlling FSM operation
- */
-#define OPT_PASSIVE 1 /* Don't die if we don't get a response */
-#define OPT_RESTART 2 /* Treat 2nd OPEN as DOWN, UP */
-#define OPT_SILENT 4 /* Wait for peer to speak first */
-
-
-/*
- * Prototypes
- */
-void fsm_init (fsm*);
-void fsm_lowerup (fsm*);
-void fsm_lowerdown (fsm*);
-void fsm_open (fsm*);
-void fsm_close (fsm*, char*);
-void fsm_input (fsm*, u_char*, int);
-void fsm_protreject (fsm*);
-void fsm_sdata (fsm*, u_char, u_char, u_char*, int);
-
-
-/*
- * Variables
- */
-extern int peer_mru[]; /* currently negotiated peer MRU (per unit) */
-
-#endif /* FSM_H */
diff --git a/lwip/src/netif/ppp/ipcp.c b/lwip/src/netif/ppp/ipcp.c
index f0ab2e0..b7c766e 100644
--- a/lwip/src/netif/ppp/ipcp.c
+++ b/lwip/src/netif/ppp/ipcp.c
@@ -1,263 +1,696 @@
-/** In contrast to pppd 2.3.1, DNS support has been added, proxy-ARP and
- dial-on-demand has been stripped. */
-/*****************************************************************************
-* ipcp.c - Network PPP IP Control Protocol program file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* portions Copyright (c) 1997 by Global Election Systems Inc.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 97-12-08 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
-* Original.
-*****************************************************************************/
/*
* ipcp.c - PPP IP Control Protocol.
*
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Copyright (c) 1984-2000 Carnegie Mellon University. All rights reserved.
*
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * 2. Redistributions in binary form must reproduce the above 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 "lwip/opt.h"
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPP_IPV4_SUPPORT /* don't build if not configured for use in lwipopts.h */
-#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+/*
+ * @todo:
+ */
-#include "ppp_impl.h"
-#include "pppdebug.h"
+#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 "auth.h"
-#include "fsm.h"
-#include "vj.h"
-#include "ipcp.h"
+#include "netif/ppp/ppp_impl.h"
-#include "lwip/inet.h"
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/ipcp.h"
-#include <string.h>
+#if 0 /* UNUSED */
+/* global vars */
+u32_t netmask = 0; /* IP netmask to set on interface */
+#endif /* UNUSED */
-/* #define OLD_CI_ADDRS 1 */ /* Support deprecated address negotiation. */
+#if 0 /* UNUSED */
+bool disable_defaultip = 0; /* Don't use hostname for default IP adrs */
+#endif /* UNUSED */
-/* global vars */
-ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */
-ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
-ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
-ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+#if 0 /* moved to ppp_settings */
+bool noremoteip = 0; /* Let him have no IP address */
+#endif /* moved to ppp_setting */
-/* local vars */
-static int default_route_set[NUM_PPP]; /* Have set up a default route */
-static int cis_received[NUM_PPP]; /* # Conf-Reqs received */
+#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 *); /* Reset our CI */
-static int ipcp_cilen (fsm *); /* Return length of our CI */
-static void ipcp_addci (fsm *, u_char *, int *); /* Add our CI */
-static int ipcp_ackci (fsm *, u_char *, int); /* Peer ack'd our CI */
-static int ipcp_nakci (fsm *, u_char *, int); /* Peer nak'd our CI */
-static int ipcp_rejci (fsm *, u_char *, int); /* Peer rej'd our CI */
-static int ipcp_reqci (fsm *, u_char *, int *, int); /* Rcv CI */
-static void ipcp_up (fsm *); /* We're UP */
-static void ipcp_down (fsm *); /* We're DOWN */
-#if PPP_ADDITIONAL_CALLBACKS
-static void ipcp_script (fsm *, char *); /* Run an up/down script */
-#endif
-static void ipcp_finished (fsm *); /* Don't need lower layer */
-
-
-fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */
-
-
-static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */
- ipcp_resetci, /* Reset our Configuration Information */
- ipcp_cilen, /* Length of our Configuration Information */
- ipcp_addci, /* Add our Configuration Information */
- ipcp_ackci, /* ACK our Configuration Information */
- ipcp_nakci, /* NAK our Configuration Information */
- ipcp_rejci, /* Reject our Configuration Information */
- ipcp_reqci, /* Request peer's Configuration Information */
- ipcp_up, /* Called when fsm reaches LS_OPENED state */
- ipcp_down, /* Called when fsm leaves LS_OPENED state */
- NULL, /* Called when we want the lower layer up */
- ipcp_finished, /* Called when we want the lower layer down */
- NULL, /* Called when Protocol-Reject received */
- NULL, /* Retransmission is necessary */
- NULL, /* Called to handle protocol-specific codes */
- "IPCP" /* String name of protocol */
+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 (int);
-static void ipcp_open (int);
-static void ipcp_close (int, char *);
-static void ipcp_lowerup (int);
-static void ipcp_lowerdown (int);
-static void ipcp_input (int, u_char *, int);
-static void ipcp_protrej (int);
-
-
-struct protent ipcp_protent = {
- PPP_IPCP,
- ipcp_init,
- ipcp_input,
- ipcp_protrej,
- ipcp_lowerup,
- ipcp_lowerdown,
- ipcp_open,
- ipcp_close,
-#if PPP_ADDITIONAL_CALLBACKS
- ipcp_printpkt,
- NULL,
-#endif /* PPP_ADDITIONAL_CALLBACKS */
- 1,
- "IPCP",
-#if PPP_ADDITIONAL_CALLBACKS
- ip_check_options,
- NULL,
- ip_active_pkt
-#endif /* PPP_ADDITIONAL_CALLBACKS */
+static void ipcp_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 (int);
+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 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")
+#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 */
/*
- * ipcp_init - Initialize IPCP.
+ * 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
-ipcp_init(int unit)
+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;
{
- fsm *f = &ipcp_fsm[unit];
- ipcp_options *wo = &ipcp_wantoptions[unit];
- ipcp_options *ao = &ipcp_allowoptions[unit];
+ 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;
+ }
- f->unit = unit;
- f->protocol = PPP_IPCP;
- f->callbacks = &ipcp_callbacks;
- fsm_init(&ipcp_fsm[unit]);
+ netmask = mask;
+ slprintf(netmask_str, sizeof(netmask_str), "%I", mask);
+
+ return (1);
+}
- memset(wo, 0, sizeof(*wo));
- memset(ao, 0, sizeof(*ao));
+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 */
- wo->neg_addr = 1;
- wo->ouraddr = 0;
+/*
+ * 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;
-#else /* VJ_SUPPORT */
- wo->neg_vj = 0;
+ wo->neg_vj = 1;
+ wo->vj_protocol = IPCP_VJ_COMP;
+ wo->maxslotindex = MAX_STATES - 1; /* really max index */
+ wo->cflag = 1;
#endif /* VJ_SUPPORT */
- wo->vj_protocol = IPCP_VJ_COMP;
- wo->maxslotindex = MAX_SLOTS - 1;
- wo->cflag = 0;
- wo->default_route = 1;
- ao->neg_addr = 1;
+#if 0 /* UNUSED */
+ /* wanting default route by default */
+ wo->default_route = 1;
+#endif /* UNUSED */
+
+ ao->neg_addr = ao->old_addrs = 1;
#if VJ_SUPPORT
- ao->neg_vj = 1;
-#else /* VJ_SUPPORT */
- ao->neg_vj = 0;
-#endif /* VJ_SUPPORT */
- ao->maxslotindex = MAX_SLOTS - 1;
- ao->cflag = 1;
- ao->default_route = 1;
+ /* 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(int unit)
-{
- fsm_open(&ipcp_fsm[unit]);
+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(int unit, char *reason)
-{
- fsm_close(&ipcp_fsm[unit], reason);
+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(int unit)
-{
- fsm_lowerup(&ipcp_fsm[unit]);
+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(int unit)
-{
- fsm_lowerdown(&ipcp_fsm[unit]);
+static void ipcp_lowerdown(ppp_pcb *pcb) {
+ fsm *f = &pcb->ipcp_fsm;
+ fsm_lowerdown(f);
}
/*
* ipcp_input - Input IPCP packet.
*/
-static void
-ipcp_input(int unit, u_char *p, int len)
-{
- fsm_input(&ipcp_fsm[unit], p, len);
+static void ipcp_input(ppp_pcb *pcb, u_char *p, int len) {
+ fsm *f = &pcb->ipcp_fsm;
+ fsm_input(f, p, len);
}
@@ -266,1037 +699,1413 @@ ipcp_input(int unit, u_char *p, int len)
*
* Pretend the lower layer went down, so we shut up.
*/
-static void
-ipcp_protrej(int unit)
-{
- fsm_lowerdown(&ipcp_fsm[unit]);
+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)
-{
- ipcp_options *wo = &ipcp_wantoptions[f->unit];
-
- wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr;
- if (wo->ouraddr == 0) {
- wo->accept_local = 1;
- }
- if (wo->hisaddr == 0) {
- wo->accept_remote = 1;
- }
- /* Request DNS addresses from the peer */
- wo->req_dns1 = ppp_settings.usepeerdns;
- wo->req_dns2 = ppp_settings.usepeerdns;
- ipcp_gotoptions[f->unit] = *wo;
- cis_received[f->unit] = 0;
+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)
-{
- ipcp_options *go = &ipcp_gotoptions[f->unit];
- ipcp_options *wo = &ipcp_wantoptions[f->unit];
- ipcp_options *ho = &ipcp_hisoptions[f->unit];
-
-#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0)
-#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0)
-#define LENCIDNS(neg) (neg ? (CILEN_ADDR) : 0)
-
- /*
- * First see if we want to change our options to the old
- * forms because we have received old forms from the peer.
- */
- if (wo->neg_addr && !go->neg_addr && !go->old_addrs) {
- /* use the old style of address negotiation */
- go->neg_addr = 1;
- go->old_addrs = 1;
- }
- if (wo->neg_vj && !go->neg_vj && !go->old_vj) {
- /* try an older style of VJ negotiation */
- if (cis_received[f->unit] == 0) {
- /* keep trying the new style until we see some CI from the peer */
- go->neg_vj = 1;
- } else {
- /* use the old style only if the peer did */
- if (ho->neg_vj && ho->old_vj) {
- go->neg_vj = 1;
- go->old_vj = 1;
- go->vj_protocol = ho->vj_protocol;
- }
+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 (LENCIADDR(go->neg_addr, go->old_addrs) +
- LENCIVJ(go->neg_vj, go->old_vj) +
- LENCIDNS(go->req_dns1) +
- LENCIDNS(go->req_dns2));
+ 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)
-{
- ipcp_options *go = &ipcp_gotoptions[f->unit];
- int len = *lenp;
+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; \
- } \
- }
-
-#define ADDCIADDR(opt, neg, old, val1, val2) \
- if (neg) { \
- int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
- if (len >= addrlen) { \
- u32_t l; \
- PUTCHAR(opt, ucp); \
- PUTCHAR(addrlen, ucp); \
- l = ntohl(val1); \
- PUTLONG(l, ucp); \
- if (old) { \
- l = ntohl(val2); \
- PUTLONG(l, ucp); \
- } \
- len -= addrlen; \
- } else { \
- neg = 0; \
- } \
- }
+ 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 = ntohl(addr); \
- PUTLONG(l, ucp); \
- len -= CILEN_ADDR; \
- } else { \
- neg = 0; \
- } \
- }
-
- ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
- go->old_addrs, go->ouraddr, go->hisaddr);
-
- ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
- go->maxslotindex, go->cflag);
-
- ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
-
- ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
-
- *lenp -= len;
+ 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.
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
*/
-static int
-ipcp_ackci(fsm *f, u_char *p, int len)
-{
- ipcp_options *go = &ipcp_gotoptions[f->unit];
- u_short cilen, citype, cishort;
- u32_t cilong;
- u_char cimaxslotindex, cicflag;
+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.
- */
+ /*
+ * 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; \
- } \
- } \
- }
-
-#define ACKCIADDR(opt, neg, old, val1, val2) \
- if (neg) { \
- int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
- u32_t l; \
- if ((len -= addrlen) < 0) { \
- goto bad; \
- } \
- GETCHAR(citype, p); \
- GETCHAR(cilen, p); \
- if (cilen != addrlen || \
- citype != opt) { \
- goto bad; \
- } \
- GETLONG(l, p); \
- cilong = htonl(l); \
- if (val1 != cilong) { \
- goto bad; \
- } \
- if (old) { \
- GETLONG(l, p); \
- cilong = htonl(l); \
- if (val2 != cilong) { \
- goto bad; \
- } \
- } \
- }
+ 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 = htonl(l); \
- if (addr != cilong) { \
- goto bad; \
- } \
- }
-
- ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
- go->old_addrs, go->ouraddr, go->hisaddr);
-
- ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
- go->maxslotindex, go->cflag);
-
- ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
-
- ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
-
- /*
- * If there are any remaining CIs, then this packet is bad.
- */
- if (len != 0) {
- goto bad;
- }
- return (1);
+ 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(LOG_INFO, ("ipcp_ackci: received bad Ack!\n"));
- return (0);
+ 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 LS_OPENED state.
+ * 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.
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
*/
-static int
-ipcp_nakci(fsm *f, u_char *p, int len)
-{
- ipcp_options *go = &ipcp_gotoptions[f->unit];
- u_char cimaxslotindex, cicflag;
- u_char citype, cilen, *next;
- u_short cishort;
- u32_t ciaddr1, ciaddr2, l, cidnsaddr;
- ipcp_options no; /* options we've seen Naks for */
- ipcp_options try; /* options to request next time */
-
- BZERO(&no, sizeof(no));
- try = *go;
-
- /*
- * Any Nak'd CIs must be in exactly the same order that we sent.
- * Check packet length and CI length at each step.
- * If we find any deviations, then this packet is bad.
- */
-#define NAKCIADDR(opt, neg, old, code) \
- if (go->neg && \
- len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \
- p[1] == cilen && \
- p[0] == opt) { \
- len -= cilen; \
- INCPTR(2, p); \
- GETLONG(l, p); \
- ciaddr1 = htonl(l); \
- if (old) { \
- GETLONG(l, p); \
- ciaddr2 = htonl(l); \
- no.old_addrs = 1; \
- } else { \
- ciaddr2 = 0; \
- } \
- no.neg = 1; \
- code \
- }
+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 \
- }
-
-#define NAKCIDNS(opt, neg, code) \
- if (go->neg && \
- ((cilen = p[1]) == CILEN_ADDR) && \
- len >= cilen && \
- p[0] == opt) { \
- len -= cilen; \
- INCPTR(2, p); \
- GETLONG(l, p); \
- cidnsaddr = htonl(l); \
- no.neg = 1; \
- code \
- }
-
- /*
- * Accept the peer's idea of {our,his} address, if different
- * from our idea, only if the accept_{local,remote} flag is set.
- */
- NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs,
- if (go->accept_local && ciaddr1) { /* Do we know our address? */
- try.ouraddr = ciaddr1;
- IPCPDEBUG(LOG_INFO, ("local IP address %s\n",
- inet_ntoa(ciaddr1)));
+ if (go->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 \
}
- if (go->accept_remote && ciaddr2) { /* Does he know his? */
- try.hisaddr = ciaddr2;
- IPCPDEBUG(LOG_INFO, ("remote IP address %s\n",
- inet_ntoa(ciaddr2)));
- }
- );
-
- /*
- * Accept the peer's value of maxslotindex provided that it
- * is less than what we asked for. Turn off slot-ID compression
- * if the peer wants. Send old-style compress-type option if
- * the peer wants.
- */
- NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
- if (cilen == CILEN_VJ) {
- GETCHAR(cimaxslotindex, p);
- GETCHAR(cicflag, p);
- if (cishort == IPCP_VJ_COMP) {
- try.old_vj = 0;
- if (cimaxslotindex < go->maxslotindex) {
- try.maxslotindex = cimaxslotindex;
- }
- if (!cicflag) {
- try.cflag = 0;
- }
- } else {
- try.neg_vj = 0;
- }
- } else {
- if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) {
- try.old_vj = 1;
- try.vj_protocol = cishort;
- } else {
- try.neg_vj = 0;
- }
- }
- );
-
- NAKCIDNS(CI_MS_DNS1, req_dns1,
- try.dnsaddr[0] = cidnsaddr;
- IPCPDEBUG(LOG_INFO, ("primary DNS address %s\n", inet_ntoa(cidnsaddr)));
- );
-
- NAKCIDNS(CI_MS_DNS2, req_dns2,
- try.dnsaddr[1] = cidnsaddr;
- IPCPDEBUG(LOG_INFO, ("secondary DNS address %s\n", inet_ntoa(cidnsaddr)));
- );
-
- /*
- * There may be remaining CIs, if the peer is requesting negotiation
- * on an option that we didn't include in our request packet.
- * If they want to negotiate about IP addresses, we comply.
- * If they want us to ask for compression, we refuse.
- */
- while (len > CILEN_VOID) {
- GETCHAR(citype, p);
- GETCHAR(cilen, p);
- if( (len -= cilen) < 0 ) {
- goto bad;
+#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 \
}
- next = p + cilen - 2;
-
- switch (citype) {
- case CI_COMPRESSTYPE:
- if (go->neg_vj || no.neg_vj ||
- (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) {
- goto bad;
- }
- no.neg_vj = 1;
- break;
- case CI_ADDRS:
- if ((go->neg_addr && go->old_addrs) || no.old_addrs
- || cilen != CILEN_ADDRS) {
- goto bad;
- }
- try.neg_addr = 1;
- try.old_addrs = 1;
- GETLONG(l, p);
- ciaddr1 = htonl(l);
- if (ciaddr1 && go->accept_local) {
- try.ouraddr = ciaddr1;
- }
- GETLONG(l, p);
- ciaddr2 = htonl(l);
- if (ciaddr2 && go->accept_remote) {
- try.hisaddr = ciaddr2;
- }
- no.old_addrs = 1;
- break;
- case CI_ADDR:
- if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) {
- goto bad;
- }
- try.old_addrs = 0;
- GETLONG(l, p);
- ciaddr1 = htonl(l);
- if (ciaddr1 && go->accept_local) {
- try.ouraddr = ciaddr1;
- }
- if (try.ouraddr != 0) {
- try.neg_addr = 1;
- }
- no.neg_addr = 1;
- break;
+
+#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 \
}
- p = next;
- }
+#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 */
- /* If there is still anything left, this packet is bad. */
- if (len != 0) {
- goto bad;
- }
+ 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 (f->state != LS_OPENED) {
- *go = try;
- }
+ /*
+ * 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;
+ return 1;
bad:
- IPCPDEBUG(LOG_INFO, ("ipcp_nakci: received bad Nak!\n"));
- return 0;
+ 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)
-{
- ipcp_options *go = &ipcp_gotoptions[f->unit];
- u_char cimaxslotindex, ciflag, cilen;
- u_short cishort;
- u32_t cilong;
- ipcp_options try; /* options to request next time */
-
- try = *go;
- /*
- * Any Rejected CIs must be in exactly the same order that we sent.
- * Check packet length and CI length at each step.
- * If we find any deviations, then this packet is bad.
- */
-#define REJCIADDR(opt, neg, old, val1, val2) \
- if (go->neg && \
- len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \
- p[1] == cilen && \
- p[0] == opt) { \
- u32_t l; \
- len -= cilen; \
- INCPTR(2, p); \
- GETLONG(l, p); \
- cilong = htonl(l); \
- /* Check rejected value. */ \
- if (cilong != val1) { \
- goto bad; \
- } \
- if (old) { \
- GETLONG(l, p); \
- cilong = htonl(l); \
- /* Check rejected value. */ \
- if (cilong != val2) { \
- goto bad; \
- } \
- } \
- try.neg = 0; \
- }
+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; \
- }
+ 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 = htonl(l); \
- /* Check rejected value. */ \
- if (cilong != dnsaddr) { \
- goto bad; \
- } \
- try.neg = 0; \
- }
-
- REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr,
- go->old_addrs, go->ouraddr, go->hisaddr);
-
- REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj,
- go->maxslotindex, go->cflag);
-
- REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]);
-
- REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]);
-
- /*
- * If there are any remaining CIs, then this packet is bad.
- */
- if (len != 0) {
- goto bad;
- }
- /*
- * Now we can update state.
- */
- if (f->state != LS_OPENED) {
- *go = try;
- }
- return 1;
+ 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(LOG_INFO, ("ipcp_rejci: received bad Reject!\n"));
- return 0;
+ 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/* Requested CIs */,int *len/* Length of requested CIs */,int reject_if_disagree)
-{
- ipcp_options *wo = &ipcp_wantoptions[f->unit];
- ipcp_options *ho = &ipcp_hisoptions[f->unit];
- ipcp_options *ao = &ipcp_allowoptions[f->unit];
-#ifdef OLD_CI_ADDRS
- ipcp_options *go = &ipcp_gotoptions[f->unit];
-#endif
- u_char *cip, *next; /* Pointer to current and next CIs */
- u_short cilen, citype; /* Parsed len, type */
- u_short cishort; /* Parsed short value */
- u32_t tl, ciaddr1; /* Parsed address values */
-#ifdef OLD_CI_ADDRS
- u32_t ciaddr2; /* Parsed address values */
-#endif
- int rc = CONFACK; /* Final packet return code */
- int orc; /* Individual option return code */
- u_char *p; /* Pointer to next char to parse */
- u_char *ucp = inp; /* Pointer to current output char */
- int l = *len; /* Length left */
- u_char maxslotindex, cflag;
- int d;
-
- cis_received[f->unit] = 1;
-
- /*
- * Reset all his options.
- */
- BZERO(ho, sizeof(*ho));
-
- /*
- * Process all his options.
- */
- next = inp;
- while (l) {
- orc = CONFACK; /* Assume success */
- cip = p = next; /* Remember begining of CI */
- if (l < 2 || /* Not enough data for CI header or */
- p[1] < 2 || /* CI length too small or */
- p[1] > l) { /* CI length too big? */
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: bad CI length!\n"));
- orc = CONFREJ; /* Reject bad CI */
- cilen = (u_short)l;/* Reject till end of packet */
- l = 0; /* Don't loop again */
- goto endswitch;
- }
- GETCHAR(citype, p); /* Parse CI type */
- GETCHAR(cilen, p); /* Parse CI length */
- l -= cilen; /* Adjust remaining length */
- next += cilen; /* Step to next CI */
-
- switch (citype) { /* Check CI type */
-#ifdef OLD_CI_ADDRS /* Need to save space... */
- case CI_ADDRS:
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received ADDRS\n"));
- if (!ao->neg_addr ||
- cilen != CILEN_ADDRS) { /* Check CI length */
- orc = CONFREJ; /* Reject CI */
- break;
- }
-
- /*
- * If he has no address, or if we both have his address but
- * disagree about it, then NAK it with our idea.
- * In particular, if we don't know his address, but he does,
- * then accept it.
- */
- GETLONG(tl, p); /* Parse source address (his) */
- ciaddr1 = htonl(tl);
- IPCPDEBUG(LOG_INFO, ("his addr %s\n", inet_ntoa(ciaddr1)));
- if (ciaddr1 != wo->hisaddr
- && (ciaddr1 == 0 || !wo->accept_remote)) {
- orc = CONFNAK;
- if (!reject_if_disagree) {
- DECPTR(sizeof(u32_t), p);
- tl = ntohl(wo->hisaddr);
- PUTLONG(tl, p);
- }
- } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
- /*
- * If neither we nor he knows his address, reject the option.
- */
- orc = CONFREJ;
- wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
- break;
- }
-
- /*
- * If he doesn't know our address, or if we both have our address
- * but disagree about it, then NAK it with our idea.
- */
- GETLONG(tl, p); /* Parse desination address (ours) */
- ciaddr2 = htonl(tl);
- IPCPDEBUG(LOG_INFO, ("our addr %s\n", inet_ntoa(ciaddr2)));
- if (ciaddr2 != wo->ouraddr) {
- if (ciaddr2 == 0 || !wo->accept_local) {
- orc = CONFNAK;
- if (!reject_if_disagree) {
- DECPTR(sizeof(u32_t), p);
- tl = ntohl(wo->ouraddr);
- PUTLONG(tl, p);
- }
- } else {
- go->ouraddr = ciaddr2; /* accept peer's idea */
- }
- }
-
- ho->neg_addr = 1;
- ho->old_addrs = 1;
- ho->hisaddr = ciaddr1;
- ho->ouraddr = ciaddr2;
- break;
-#endif
-
- case CI_ADDR:
- if (!ao->neg_addr) {
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR not allowed\n"));
- orc = CONFREJ; /* Reject CI */
- break;
- } else if (cilen != CILEN_ADDR) { /* Check CI length */
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR bad len\n"));
- orc = CONFREJ; /* Reject CI */
- break;
- }
-
- /*
- * If he has no address, or if we both have his address but
- * disagree about it, then NAK it with our idea.
- * In particular, if we don't know his address, but he does,
- * then accept it.
- */
- GETLONG(tl, p); /* Parse source address (his) */
- ciaddr1 = htonl(tl);
- if (ciaddr1 != wo->hisaddr
- && (ciaddr1 == 0 || !wo->accept_remote)) {
- orc = CONFNAK;
- if (!reject_if_disagree) {
- DECPTR(sizeof(u32_t), p);
- tl = ntohl(wo->hisaddr);
- PUTLONG(tl, p);
- }
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Nak ADDR %s\n", inet_ntoa(ciaddr1)));
- } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
- /*
- * Don't ACK an address of 0.0.0.0 - reject it instead.
- */
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR %s\n", inet_ntoa(ciaddr1)));
- orc = CONFREJ;
- wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
- break;
- }
-
- ho->neg_addr = 1;
- ho->hisaddr = ciaddr1;
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: ADDR %s\n", inet_ntoa(ciaddr1)));
- break;
-
- case CI_MS_DNS1:
- case CI_MS_DNS2:
- /* Microsoft primary or secondary DNS request */
- d = citype == CI_MS_DNS2;
-
- /* If we do not have a DNS address then we cannot send it */
- if (ao->dnsaddr[d] == 0 ||
- cilen != CILEN_ADDR) { /* Check CI length */
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting DNS%d Request\n", d+1));
- orc = CONFREJ; /* Reject CI */
- break;
- }
- GETLONG(tl, p);
- if (htonl(tl) != ao->dnsaddr[d]) {
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking DNS%d Request %s\n",
- d+1, inet_ntoa(tl)));
- DECPTR(sizeof(u32_t), p);
- tl = ntohl(ao->dnsaddr[d]);
- PUTLONG(tl, p);
- orc = CONFNAK;
- }
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received DNS%d Request\n", d+1));
- break;
-
- case CI_MS_WINS1:
- case CI_MS_WINS2:
- /* Microsoft primary or secondary WINS request */
- d = citype == CI_MS_WINS2;
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received WINS%d Request\n", d+1));
-
- /* If we do not have a DNS address then we cannot send it */
- if (ao->winsaddr[d] == 0 ||
- cilen != CILEN_ADDR) { /* Check CI length */
- orc = CONFREJ; /* Reject CI */
- break;
- }
- GETLONG(tl, p);
- if (htonl(tl) != ao->winsaddr[d]) {
- DECPTR(sizeof(u32_t), p);
- tl = ntohl(ao->winsaddr[d]);
- PUTLONG(tl, p);
- orc = CONFNAK;
- }
- break;
-
- case CI_COMPRESSTYPE:
- if (!ao->neg_vj) {
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE not allowed\n"));
- orc = CONFREJ;
- break;
- } else if (cilen != CILEN_VJ && cilen != CILEN_COMPRESS) {
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE len=%d\n", cilen));
- orc = CONFREJ;
- break;
- }
- GETSHORT(cishort, p);
-
- if (!(cishort == IPCP_VJ_COMP ||
- (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) {
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE %d\n", cishort));
- orc = CONFREJ;
- break;
- }
-
- ho->neg_vj = 1;
- ho->vj_protocol = cishort;
- if (cilen == CILEN_VJ) {
- GETCHAR(maxslotindex, p);
- if (maxslotindex > ao->maxslotindex) {
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking VJ max slot %d\n", maxslotindex));
- orc = CONFNAK;
- if (!reject_if_disagree) {
- DECPTR(1, p);
- PUTCHAR(ao->maxslotindex, p);
+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;
}
- }
- GETCHAR(cflag, p);
- if (cflag && !ao->cflag) {
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking VJ cflag %d\n", cflag));
- orc = CONFNAK;
- if (!reject_if_disagree) {
- DECPTR(1, p);
- PUTCHAR(wo->cflag, p);
+ 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;
}
- }
- ho->maxslotindex = maxslotindex;
- ho->cflag = cflag;
- } else {
- ho->old_vj = 1;
- ho->maxslotindex = MAX_SLOTS - 1;
- ho->cflag = 1;
- }
- IPCPDEBUG(LOG_INFO, (
- "ipcp_reqci: received COMPRESSTYPE p=%d old=%d maxslot=%d cflag=%d\n",
- ho->vj_protocol, ho->old_vj, ho->maxslotindex, ho->cflag));
- break;
-
- default:
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting unknown CI type %d\n", citype));
- orc = CONFREJ;
- break;
- }
+ break;
+#endif /* UNUSED - WINS */
-endswitch:
- if (orc == CONFACK && /* Good CI */
- rc != CONFACK) { /* but prior CI wasnt? */
- continue; /* Don't send this one */
- }
+#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 */
- if (orc == CONFNAK) { /* Nak this CI? */
- if (reject_if_disagree) { /* Getting fed up with sending NAKs? */
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting too many naks\n"));
- orc = CONFREJ; /* Get tough if so */
- } else {
- if (rc == CONFREJ) { /* Rejecting prior CI? */
- continue; /* Don't send this one */
- }
- if (rc == CONFACK) { /* Ack'd all prior CIs? */
- rc = CONFNAK; /* Not anymore... */
- ucp = inp; /* Backup */
- }
- }
+ 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 (orc == CONFREJ && /* Reject this CI */
- rc != CONFREJ) { /* but no prior ones? */
- rc = CONFREJ;
- ucp = inp; /* Backup */
- }
-
- /* Need to move CI? */
- if (ucp != cip) {
- BCOPY(cip, ucp, cilen); /* Move it */
+ /*
+ * 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);
}
- /* Update output pointer */
- INCPTR(cilen, ucp);
- }
-
- /*
- * If we aren't rejecting this packet, and we want to negotiate
- * their address, and they didn't send their address, then we
- * send a NAK with a CI_ADDR option appended. We assume the
- * input buffer is long enough that we can append the extra
- * option safely.
- */
- if (rc != CONFREJ && !ho->neg_addr &&
- wo->req_addr && !reject_if_disagree) {
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Requesting peer address\n"));
- if (rc == CONFACK) {
- rc = CONFNAK;
- ucp = inp; /* reset pointer */
- wo->req_addr = 0; /* don't ask again */
- }
- PUTCHAR(CI_ADDR, ucp);
- PUTCHAR(CILEN_ADDR, ucp);
- tl = ntohl(wo->hisaddr);
- PUTLONG(tl, ucp);
- }
-
- *len = (int)(ucp - inp); /* Compute output length */
- IPCPDEBUG(LOG_INFO, ("ipcp_reqci: returning Configure-%s\n", CODENAME(rc)));
- return (rc); /* Return final code */
+ *len = ucp - inp; /* Compute output length */
+ IPCPDEBUG(("ipcp: returning Configure-%s", CODENAME(rc)));
+ return (rc); /* Return final code */
}
-#if 0
+#if 0 /* UNUSED */
/*
* ip_check_options - check that any IP-related options are OK,
* and assign appropriate defaults.
*/
static void
-ip_check_options(u_long localAddr)
+ip_check_options()
{
- ipcp_options *wo = &ipcp_wantoptions[0];
-
- /*
- * Load our default IP address but allow the remote host to give us
- * a new address.
- */
- if (wo->ouraddr == 0 && !ppp_settings.disable_defaultip) {
- wo->accept_local = 1; /* don't insist on this default value */
- wo->ouraddr = htonl(localAddr);
- }
+ 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
+#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)
-{
- u32_t mask;
- ipcp_options *ho = &ipcp_hisoptions[f->unit];
- ipcp_options *go = &ipcp_gotoptions[f->unit];
- ipcp_options *wo = &ipcp_wantoptions[f->unit];
-
- np_up(f->unit, PPP_IP);
- IPCPDEBUG(LOG_INFO, ("ipcp: up\n"));
-
- /*
- * We must have a non-zero IP address for both ends of the link.
- */
- if (!ho->neg_addr) {
- ho->hisaddr = wo->hisaddr;
- }
-
- if (ho->hisaddr == 0) {
- IPCPDEBUG(LOG_ERR, ("Could not determine remote IP address\n"));
- ipcp_close(f->unit, "Could not determine remote IP address");
- return;
- }
- if (go->ouraddr == 0) {
- IPCPDEBUG(LOG_ERR, ("Could not determine local IP address\n"));
- ipcp_close(f->unit, "Could not determine local IP address");
- return;
- }
-
- if (ppp_settings.usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) {
- /*pppGotDNSAddrs(go->dnsaddr[0], go->dnsaddr[1]);*/
- }
-
- /*
- * Check that the peer is allowed to use the IP address it wants.
- */
- if (!auth_ip_addr(f->unit, ho->hisaddr)) {
- IPCPDEBUG(LOG_ERR, ("Peer is not authorized to use remote address %s\n",
- inet_ntoa(ho->hisaddr)));
- ipcp_close(f->unit, "Unauthorized remote IP address");
- return;
- }
-
- /* set tcp compression */
- sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex);
-
- /*
- * Set IP addresses and (if specified) netmask.
- */
- mask = GetMask(go->ouraddr);
-
- if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask, go->dnsaddr[0], go->dnsaddr[1])) {
- IPCPDEBUG(LOG_WARNING, ("sifaddr failed\n"));
- ipcp_close(f->unit, "Interface configuration failed");
- return;
- }
-
- /* bring the interface up for IP */
- if (!sifup(f->unit)) {
- IPCPDEBUG(LOG_WARNING, ("sifup failed\n"));
- ipcp_close(f->unit, "Interface configuration failed");
- return;
- }
-
- sifnpmode(f->unit, PPP_IP, NPMODE_PASS);
-
- /* assign a default route through the interface if required */
- if (ipcp_wantoptions[f->unit].default_route) {
- if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) {
- default_route_set[f->unit] = 1;
+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 */
}
- }
-
- IPCPDEBUG(LOG_NOTICE, ("local IP address %s\n", inet_ntoa(go->ouraddr)));
- IPCPDEBUG(LOG_NOTICE, ("remote IP address %s\n", inet_ntoa(ho->hisaddr)));
- if (go->dnsaddr[0]) {
- IPCPDEBUG(LOG_NOTICE, ("primary DNS address %s\n", inet_ntoa(go->dnsaddr[0])));
- }
- if (go->dnsaddr[1]) {
- IPCPDEBUG(LOG_NOTICE, ("secondary DNS address %s\n", inet_ntoa(go->dnsaddr[1])));
- }
+
+#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 */
}
@@ -1306,106 +2115,304 @@ ipcp_up(fsm *f)
* Take the IP network interface down, clear its addresses
* and delete routes through it.
*/
-static void
-ipcp_down(fsm *f)
-{
- IPCPDEBUG(LOG_INFO, ("ipcp: down\n"));
- np_down(f->unit, PPP_IP);
- sifvjcomp(f->unit, 0, 0, 0);
+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 */
- sifdown(f->unit);
- ipcp_clear_addrs(f->unit);
+#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, etc.
+ * ipcp_clear_addrs() - clear the interface addresses, routes,
+ * proxy arp entries, etc.
*/
-static void
-ipcp_clear_addrs(int unit)
-{
- u32_t ouraddr, hisaddr;
-
- ouraddr = ipcp_gotoptions[unit].ouraddr;
- hisaddr = ipcp_hisoptions[unit].hisaddr;
- if (default_route_set[unit]) {
- cifdefaultroute(unit, ouraddr, hisaddr);
- default_route_set[unit] = 0;
- }
- cifaddr(unit, ouraddr, hisaddr);
+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
-ipcp_finished(fsm *f)
+create_resolv(peerdns1, peerdns2)
+ u32_t peerdns1, peerdns2;
{
- np_finished(f->unit, PPP_IP);
+
}
+#endif /* UNUSED */
-#if PPP_ADDITIONAL_CALLBACKS
-static int
-ipcp_printpkt(u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg)
-{
- LWIP_UNUSED_ARG(p);
- LWIP_UNUSED_ARG(plen);
- LWIP_UNUSED_ARG(printer);
- LWIP_UNUSED_ARG(arg);
- return 0;
+#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
-#define IPPROTO_TCP 6
-#define TCP_HDRLEN 20
-#define TH_FIN 0x01
+#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])
+#define net_short(x) (((x)[0] << 8) + (x)[1])
+#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF)
+#define get_ipoff(x) net_short((unsigned char *)(x) + 6)
+#define get_ipproto(x) (((unsigned char *)(x))[9])
+#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4)
+#define get_tcpflags(x) (((unsigned char *)(x))[13])
static int
-ip_active_pkt(u_char *pkt, int len)
+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) {
+ 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;
- }
- hlen = get_iphl(pkt) * 4;
- if (len < hlen + TCP_HDRLEN) {
- return 0;
- }
- tcp = pkt + hlen;
- if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4) {
- return 0;
- }
- return 1;
}
-#endif /* PPP_ADDITIONAL_CALLBACKS */
+#endif /* DEMAND_SUPPORT */
-#endif /* PPP_SUPPORT */
+#endif /* PPP_SUPPORT && PPP_IPV4_SUPPORT */
diff --git a/lwip/src/netif/ppp/ipcp.h b/lwip/src/netif/ppp/ipcp.h
deleted file mode 100644
index de03f46..0000000
--- a/lwip/src/netif/ppp/ipcp.h
+++ /dev/null
@@ -1,106 +0,0 @@
-/*****************************************************************************
-* ipcp.h - PPP IP NCP: Internet Protocol Network Control Protocol header file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* portions Copyright (c) 1997 Global Election Systems Inc.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 97-12-04 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
-* Original derived from BSD codes.
-*****************************************************************************/
-/*
- * ipcp.h - IP Control Protocol definitions.
- *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * $Id: ipcp.h,v 1.4 2010/01/18 20:49:43 goldsimon Exp $
- */
-
-#ifndef IPCP_H
-#define IPCP_H
-
-/*
- * Options.
- */
-#define CI_ADDRS 1 /* IP Addresses */
-#define CI_COMPRESSTYPE 2 /* Compression Type */
-#define CI_ADDR 3
-
-#define CI_MS_DNS1 129 /* Primary DNS value */
-#define CI_MS_WINS1 128 /* Primary WINS value */
-#define CI_MS_DNS2 131 /* Secondary DNS value */
-#define CI_MS_WINS2 130 /* Secondary WINS value */
-
-#define IPCP_VJMODE_OLD 1 /* "old" mode (option # = 0x0037) */
-#define IPCP_VJMODE_RFC1172 2 /* "old-rfc"mode (option # = 0x002d) */
-#define IPCP_VJMODE_RFC1332 3 /* "new-rfc"mode (option # = 0x002d, */
- /* maxslot and slot number compression) */
-
-#define IPCP_VJ_COMP 0x002d /* current value for VJ compression option */
-#define IPCP_VJ_COMP_OLD 0x0037 /* "old" (i.e, broken) value for VJ */
- /* compression option */
-
-typedef struct ipcp_options {
- u_int neg_addr : 1; /* Negotiate IP Address? */
- u_int old_addrs : 1; /* Use old (IP-Addresses) option? */
- u_int req_addr : 1; /* Ask peer to send IP address? */
- u_int default_route : 1; /* Assign default route through interface? */
- u_int proxy_arp : 1; /* Make proxy ARP entry for peer? */
- u_int neg_vj : 1; /* Van Jacobson Compression? */
- u_int old_vj : 1; /* use old (short) form of VJ option? */
- u_int accept_local : 1; /* accept peer's value for ouraddr */
- u_int accept_remote : 1; /* accept peer's value for hisaddr */
- u_int req_dns1 : 1; /* Ask peer to send primary DNS address? */
- u_int req_dns2 : 1; /* Ask peer to send secondary DNS address? */
- u_short vj_protocol; /* protocol value to use in VJ option */
- u_char maxslotindex; /* VJ slots - 1. */
- u_char cflag; /* VJ slot compression flag. */
- u32_t ouraddr, hisaddr; /* Addresses in NETWORK BYTE ORDER */
- u32_t dnsaddr[2]; /* Primary and secondary MS DNS entries */
- u32_t winsaddr[2]; /* Primary and secondary MS WINS entries */
-} ipcp_options;
-
-extern fsm ipcp_fsm[];
-extern ipcp_options ipcp_wantoptions[];
-extern ipcp_options ipcp_gotoptions[];
-extern ipcp_options ipcp_allowoptions[];
-extern ipcp_options ipcp_hisoptions[];
-
-extern struct protent ipcp_protent;
-
-#endif /* IPCP_H */
diff --git a/lwip/src/netif/ppp/ipv6cp.c b/lwip/src/netif/ppp/ipv6cp.c
new file mode 100644
index 0000000..11c18df
--- /dev/null
+++ b/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/lwip/src/netif/ppp/lcp.c b/lwip/src/netif/ppp/lcp.c
index 54f758a..90ed183 100644
--- a/lwip/src/netif/ppp/lcp.c
+++ b/lwip/src/netif/ppp/lcp.c
@@ -1,159 +1,262 @@
-/*****************************************************************************
-* lcp.c - Network Link Control Protocol program file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* portions Copyright (c) 1997 by Global Election Systems Inc.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 97-12-01 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
-* Original.
-*****************************************************************************/
-
/*
* lcp.c - PPP Link Control Protocol.
*
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * 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.
*
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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 "lwip/opt.h"
+#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
-#include "ppp_impl.h"
-#include "pppdebug.h"
-
-#include "fsm.h"
-#include "chap.h"
-#include "magic.h"
-#include "auth.h"
-#include "lcp.h"
+/*
+ * @todo:
+ */
+#if 0 /* UNUSED */
+#include <stdio.h>
#include <string.h>
+#include <stdlib.h>
+#endif /* UNUSED */
-#if PPPOE_SUPPORT
-#include "netif/ppp_oe.h"
-#else
-#define PPPOE_MAXMTU PPP_MAXMRU
-#endif
+#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"
-#if 0 /* UNUSED */
/*
- * LCP-related command-line options.
+ * 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.
*/
-int lcp_echo_interval = 0; /* Interval between LCP echo-requests */
-int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */
-bool lax_recv = 0; /* accept control chars in asyncmap */
+/* steal a bit in fsm flags word */
+#define DELAYED_UP 0x80
-static int setescape (char **);
+static void lcp_delayed_up(void *arg);
-static option_t lcp_option_list[] = {
- /* LCP options */
- /* list stripped for simplicity */
- {NULL}
-};
+/*
+ * 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 */
-LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */
static u_int lcp_echo_interval = LCP_ECHOINTERVAL; /* Interval between LCP echo-requests */
static u_int lcp_echo_fails = LCP_MAXECHOFAILS; /* Tolerance to unanswered echo-requests */
+#endif /* UNUSED */
-/* global vars */
-static fsm lcp_fsm[NUM_PPP]; /* LCP fsm structure (global)*/
-lcp_options lcp_wantoptions[NUM_PPP]; /* Options that we want to request */
-lcp_options lcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
-lcp_options lcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
-lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
-ext_accm xmit_accm[NUM_PPP]; /* extended transmit ACCM */
+#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 */
-static u32_t lcp_echos_pending = 0; /* Number of outstanding echo msgs */
-static u32_t lcp_echo_number = 0; /* ID number of next echo frame */
-static u32_t lcp_echo_timer_running = 0; /* TRUE if a timer is running */
+#if PPP_OPTIONS
+static int noopt (char **);
+#endif /* PPP_OPTIONS */
-/* @todo: do we really need such a large buffer? The typical 1500 bytes seem too much. */
-static u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */
+#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*); /* Reset our CI */
-static int lcp_cilen (fsm*); /* Return length of our CI */
-static void lcp_addci (fsm*, u_char*, int*); /* Add our CI to pkt */
-static int lcp_ackci (fsm*, u_char*, int); /* Peer ack'd our CI */
-static int lcp_nakci (fsm*, u_char*, int); /* Peer nak'd our CI */
-static int lcp_rejci (fsm*, u_char*, int); /* Peer rej'd our CI */
-static int lcp_reqci (fsm*, u_char*, int*, int); /* Rcv peer CI */
-static void lcp_up (fsm*); /* We're UP */
-static void lcp_down (fsm*); /* We're DOWN */
-static void lcp_starting (fsm*); /* We need lower layer up */
-static void lcp_finished (fsm*); /* We need lower layer down */
-static int lcp_extcode (fsm*, int, u_char, u_char*, int);
-static void lcp_rprotrej (fsm*, u_char*, int);
+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 (int);
-static void lcp_echo_lowerdown (int);
-static void LcpEchoTimeout (void*);
-static void lcp_received_echo_reply (fsm*, int, u_char*, int);
-static void LcpSendEchoRequest (fsm*);
-static void LcpLinkFailure (fsm*);
-static void LcpEchoCheck (fsm*);
-
-static fsm_callbacks lcp_callbacks = { /* LCP callback routines */
- lcp_resetci, /* Reset our Configuration Information */
- lcp_cilen, /* Length of our Configuration Information */
- lcp_addci, /* Add our Configuration Information */
- lcp_ackci, /* ACK our Configuration Information */
- lcp_nakci, /* NAK our Configuration Information */
- lcp_rejci, /* Reject our Configuration Information */
- lcp_reqci, /* Request peer's Configuration Information */
- lcp_up, /* Called when fsm reaches LS_OPENED state */
- lcp_down, /* Called when fsm leaves LS_OPENED state */
- lcp_starting, /* Called when we want the lower layer up */
- lcp_finished, /* Called when we want the lower layer down */
- NULL, /* Called when Protocol-Reject received */
- NULL, /* Retransmission is necessary */
- lcp_extcode, /* Called to handle LCP-specific codes */
- "LCP" /* String name of protocol */
+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 */
};
/*
@@ -161,10 +264,15 @@ static fsm_callbacks lcp_callbacks = { /* LCP callback routines */
* Some of these are called directly.
*/
-static void lcp_input (int, u_char *, int);
-static void lcp_protrej (int);
+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 */
-struct protent lcp_protent = {
+const struct protent lcp_protent = {
PPP_LCP,
lcp_init,
lcp_input,
@@ -173,455 +281,658 @@ struct protent lcp_protent = {
lcp_lowerdown,
lcp_open,
lcp_close,
-#if PPP_ADDITIONAL_CALLBACKS
+#if PRINTPKT_SUPPORT
lcp_printpkt,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_DATAINPUT
NULL,
-#endif /* PPP_ADDITIONAL_CALLBACKS */
- 1,
+#endif /* PPP_DATAINPUT */
+#if PRINTPKT_SUPPORT
"LCP",
-#if PPP_ADDITIONAL_CALLBACKS
NULL,
+#endif /* PRINTPKT_SUPPORT */
+#if PPP_OPTIONS
+ lcp_option_list,
+ NULL,
+#endif /* PPP_OPTIONS */
+#if DEMAND_SUPPORT
NULL,
NULL
-#endif /* PPP_ADDITIONAL_CALLBACKS */
+#endif /* DEMAND_SUPPORT */
};
-int lcp_loopbackfail = DEFLOOPBACKFAIL;
-
/*
* Length of each type of configuration option (in octets)
*/
-#define CILEN_VOID 2
-#define CILEN_CHAR 3
-#define CILEN_SHORT 4 /* CILEN_VOID + sizeof(short) */
-#define CILEN_CHAP 5 /* CILEN_VOID + sizeof(short) + 1 */
-#define CILEN_LONG 6 /* CILEN_VOID + sizeof(long) */
-#define CILEN_LQR 8 /* CILEN_VOID + sizeof(short) + sizeof(long) */
-#define CILEN_CBCP 3
-
-#define CODENAME(x) ((x) == CONFACK ? "ACK" : (x) == CONFNAK ? "NAK" : "REJ")
-
-#if 0 /* UNUSED */
+#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
/*
- * setescape - add chars to the set we escape on transmission.
+ * noopt - Disable all options (why?).
*/
static int
-setescape(argv)
+noopt(argv)
char **argv;
{
- int n, ret;
- char *p, *endp;
-
- p = *argv;
- ret = 1;
- while (*p) {
- n = strtol(p, &endp, 16);
- if (p == endp) {
- option_error("escape parameter contains invalid hex number '%s'", p);
- return 0;
- }
- p = endp;
- if (n < 0 || n == 0x5E || n > 0xFF) {
- option_error("can't escape character 0x%x", n);
- ret = 0;
- } else
- xmit_accm[0][n >> 5] |= 1 << (n & 0x1F);
- while (*p == ',' || *p == ' ')
- ++p;
+ 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;
}
- return ret;
+ option_error("Can't parse '%s' as an endpoint discriminator", *argv);
+ return 0;
}
-#endif /* UNUSED */
+
+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.
*/
-void
-lcp_init(int unit)
-{
- fsm *f = &lcp_fsm[unit];
- lcp_options *wo = &lcp_wantoptions[unit];
- lcp_options *ao = &lcp_allowoptions[unit];
-
- f->unit = unit;
- f->protocol = PPP_LCP;
- f->callbacks = &lcp_callbacks;
-
- fsm_init(f);
-
- wo->passive = 0;
- wo->silent = 0;
- wo->restart = 0; /* Set to 1 in kernels or multi-line implementations */
- wo->neg_mru = 1;
- wo->mru = PPP_DEFMRU;
- wo->neg_asyncmap = 1;
- wo->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */
- wo->neg_chap = 0; /* Set to 1 on server */
- wo->neg_upap = 0; /* Set to 1 on server */
- wo->chap_mdtype = CHAP_DIGEST_MD5;
- wo->neg_magicnumber = 1;
- wo->neg_pcompression = 1;
- wo->neg_accompression = 1;
- wo->neg_lqr = 0; /* no LQR implementation yet */
- wo->neg_cbcp = 0;
-
- ao->neg_mru = 1;
- ao->mru = PPP_MAXMRU;
- ao->neg_asyncmap = 1;
- ao->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */
- ao->neg_chap = (CHAP_SUPPORT != 0);
- ao->chap_mdtype = CHAP_DIGEST_MD5;
- ao->neg_upap = (PAP_SUPPORT != 0);
- ao->neg_magicnumber = 1;
- ao->neg_pcompression = 1;
- ao->neg_accompression = 1;
- ao->neg_lqr = 0; /* no LQR implementation yet */
- ao->neg_cbcp = (CBCP_SUPPORT != 0);
-
- /*
- * Set transmit escape for the flag and escape characters plus anything
- * set for the allowable options.
- */
- memset(xmit_accm[unit], 0, sizeof(xmit_accm[0]));
- xmit_accm[unit][15] = 0x60;
- xmit_accm[unit][0] = (u_char)((ao->asyncmap & 0xFF));
- xmit_accm[unit][1] = (u_char)((ao->asyncmap >> 8) & 0xFF);
- xmit_accm[unit][2] = (u_char)((ao->asyncmap >> 16) & 0xFF);
- xmit_accm[unit][3] = (u_char)((ao->asyncmap >> 24) & 0xFF);
- LCPDEBUG(LOG_INFO, ("lcp_init: xmit_accm=%X %X %X %X\n",
- xmit_accm[unit][0],
- xmit_accm[unit][1],
- xmit_accm[unit][2],
- xmit_accm[unit][3]));
-
- lcp_phase[unit] = PHASE_INITIALIZE;
+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(int unit)
-{
- fsm *f = &lcp_fsm[unit];
- lcp_options *wo = &lcp_wantoptions[unit];
-
- f->flags = 0;
- if (wo->passive) {
- f->flags |= OPT_PASSIVE;
- }
- if (wo->silent) {
- f->flags |= OPT_SILENT;
- }
- fsm_open(f);
-
- lcp_phase[unit] = PHASE_ESTABLISH;
+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(int unit, char *reason)
-{
- fsm *f = &lcp_fsm[unit];
+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;
- if (lcp_phase[unit] != PHASE_DEAD) {
- lcp_phase[unit] = PHASE_TERMINATE;
- }
- if (f->state == LS_STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) {
- /*
- * This action is not strictly according to the FSM in RFC1548,
- * but it does mean that the program terminates if you do an
- * lcp_close() in passive/silent mode when a connection hasn't
- * been established.
- */
- f->state = LS_CLOSED;
- lcp_finished(f);
- } else {
fsm_close(f, reason);
- }
+ 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(int unit)
-{
- lcp_options *wo = &lcp_wantoptions[unit];
-
- /*
- * Don't use A/C or protocol compression on transmission,
- * but accept A/C and protocol compressed packets
- * if we are going to ask for A/C and protocol compression.
- */
- ppp_set_xaccm(unit, &xmit_accm[unit]);
- ppp_send_config(unit, PPP_MRU, 0xffffffffl, 0, 0);
- ppp_recv_config(unit, PPP_MRU, 0x00000000l,
- wo->neg_pcompression, wo->neg_accompression);
- peer_mru[unit] = PPP_MRU;
- lcp_allowoptions[unit].asyncmap = (u_long)xmit_accm[unit][0]
- | ((u_long)xmit_accm[unit][1] << 8)
- | ((u_long)xmit_accm[unit][2] << 16)
- | ((u_long)xmit_accm[unit][3] << 24);
- LCPDEBUG(LOG_INFO, ("lcp_lowerup: asyncmap=%X %X %X %X\n",
- xmit_accm[unit][3],
- xmit_accm[unit][2],
- xmit_accm[unit][1],
- xmit_accm[unit][0]));
-
- fsm_lowerup(&lcp_fsm[unit]);
+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(int unit)
-{
- fsm_lowerdown(&lcp_fsm[unit]);
+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_input - Input LCP packet.
+ * lcp_delayed_up - Bring the lower layer up now.
*/
-static void
-lcp_input(int unit, u_char *p, int len)
-{
- fsm *f = &lcp_fsm[unit];
+static void lcp_delayed_up(void *arg) {
+ fsm *f = (fsm*)arg;
- fsm_input(f, p, len);
+ 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, u_char id, u_char *inp, int len)
-{
- u_char *magp;
+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 ){
+ switch( code ){
case PROTREJ:
- lcp_rprotrej(f, inp, len);
- break;
-
+ lcp_rprotrej(f, inp, len);
+ break;
+
case ECHOREQ:
- if (f->state != LS_OPENED) {
- break;
- }
- LCPDEBUG(LOG_INFO, ("lcp: Echo-Request, Rcvd id %d\n", id));
- magp = inp;
- PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp);
- fsm_sdata(f, ECHOREP, id, inp, len);
- break;
-
+ 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;
+ lcp_received_echo_reply(f, id, inp, len);
+ break;
case DISCREQ:
- break;
+ case IDENTIF:
+ case TIMEREM:
+ break;
default:
- return 0;
- }
- return 1;
+ return 0;
+ }
+ return 1;
}
-
+
/*
* lcp_rprotrej - Receive an Protocol-Reject.
*
* Figure out which protocol is rejected and inform it.
*/
-static void
-lcp_rprotrej(fsm *f, u_char *inp, int len)
-{
- int i;
- struct protent *protp;
- u_short prot;
-
- if (len < (int)sizeof (u_short)) {
- LCPDEBUG(LOG_INFO, ("lcp_rprotrej: Rcvd short Protocol-Reject packet!\n"));
- return;
- }
-
- GETSHORT(prot, inp);
-
- LCPDEBUG(LOG_INFO, ("lcp_rprotrej: Rcvd Protocol-Reject packet for %x!\n", prot));
-
- /*
- * Protocol-Reject packets received in any state other than the LCP
- * LS_OPENED state SHOULD be silently discarded.
- */
- if( f->state != LS_OPENED ) {
- LCPDEBUG(LOG_INFO, ("Protocol-Reject discarded: LCP in state %d\n", f->state));
- return;
- }
-
- /*
- * Upcall the proper Protocol-Reject routine.
- */
- for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
- if (protp->protocol == prot && protp->enabled_flag) {
- (*protp->protrej)(f->unit);
- return;
+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;
}
- }
- LCPDEBUG(LOG_WARNING, ("Protocol-Reject for unsupported protocol 0x%x\n", prot));
+#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.
*/
-static void
-lcp_protrej(int unit)
-{
- LWIP_UNUSED_ARG(unit);
- /*
- * Can't reject LCP!
- */
- LCPDEBUG(LOG_WARNING, ("lcp_protrej: Received Protocol-Reject for LCP!\n"));
- fsm_protreject(&lcp_fsm[unit]);
+/*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(int unit, u_char *p, int len)
-{
- /*
- * Send back the protocol and the information field of the
- * rejected packet. We only get here if LCP is in the LS_OPENED state.
- */
+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(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id, p, len);
+ fsm_sdata(f, PROTREJ, ++f->id,
+ p, len);
}
/*
* lcp_resetci - Reset our CI.
*/
-static void
-lcp_resetci(fsm *f)
-{
- lcp_wantoptions[f->unit].magicnumber = magic();
- lcp_wantoptions[f->unit].numloops = 0;
- lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit];
- peer_mru[f->unit] = PPP_MRU;
- auth_reset(f->unit);
+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)
-{
- lcp_options *go = &lcp_gotoptions[f->unit];
-
-#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0)
-#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0)
-#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0)
-#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0)
-#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0)
-#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0)
- /*
- * NB: we only ask for one of CHAP and UPAP, even if we will
- * accept either.
- */
- return (LENCISHORT(go->neg_mru && go->mru != PPP_DEFMRU) +
- LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) +
- LENCICHAP(go->neg_chap) +
- LENCISHORT(!go->neg_chap && go->neg_upap) +
- LENCILQR(go->neg_lqr) +
- LENCICBCP(go->neg_cbcp) +
- LENCILONG(go->neg_magicnumber) +
- LENCIVOID(go->neg_pcompression) +
- LENCIVOID(go->neg_accompression));
+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)
-{
- lcp_options *go = &lcp_gotoptions[f->unit];
- u_char *start_ucp = ucp;
+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) { \
- LCPDEBUG(LOG_INFO, ("lcp_addci: opt=%d\n", opt)); \
- PUTCHAR(opt, ucp); \
- PUTCHAR(CILEN_VOID, ucp); \
- }
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_VOID, ucp); \
+ }
#define ADDCISHORT(opt, neg, val) \
- if (neg) { \
- LCPDEBUG(LOG_INFO, ("lcp_addci: INT opt=%d %X\n", opt, val)); \
- PUTCHAR(opt, ucp); \
- PUTCHAR(CILEN_SHORT, ucp); \
- PUTSHORT(val, ucp); \
- }
-#define ADDCICHAP(opt, neg, val, digest) \
- if (neg) { \
- LCPDEBUG(LOG_INFO, ("lcp_addci: CHAP opt=%d %X\n", opt, val)); \
- PUTCHAR(opt, ucp); \
- PUTCHAR(CILEN_CHAP, ucp); \
- PUTSHORT(val, ucp); \
- PUTCHAR(digest, ucp); \
- }
+ 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) { \
- LCPDEBUG(LOG_INFO, ("lcp_addci: L opt=%d %lX\n", opt, val)); \
- PUTCHAR(opt, ucp); \
- PUTCHAR(CILEN_LONG, ucp); \
- PUTLONG(val, ucp); \
- }
+ if (neg) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_LONG, ucp); \
+ PUTLONG(val, ucp); \
+ }
+#if LQR_SUPPORT
#define ADDCILQR(opt, neg, val) \
- if (neg) { \
- LCPDEBUG(LOG_INFO, ("lcp_addci: LQR opt=%d %lX\n", opt, val)); \
- PUTCHAR(opt, ucp); \
- PUTCHAR(CILEN_LQR, ucp); \
- PUTSHORT(PPP_LQR, ucp); \
- PUTLONG(val, ucp); \
- }
+ 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) { \
- LCPDEBUG(LOG_INFO, ("lcp_addci: CHAR opt=%d %X '%z'\n", opt, val, val)); \
- PUTCHAR(opt, ucp); \
- PUTCHAR(CILEN_CHAR, ucp); \
- PUTCHAR(val, ucp); \
- }
-
- ADDCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru);
- ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap);
- ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype);
- ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
- ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
- ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
- ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
- ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
- ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
-
- if (ucp - start_ucp != *lenp) {
- /* this should never happen, because peer_mtu should be 1500 */
- LCPDEBUG(LOG_ERR, ("Bug in lcp_addci: wrong length\n"));
- }
+ 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");
+ }
}
@@ -630,585 +941,869 @@ lcp_addci(fsm *f, u_char *ucp, int *lenp)
* This should not modify any state if the Ack is bad.
*
* Returns:
- * 0 - Ack was bad.
- * 1 - Ack was good.
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
*/
-static int
-lcp_ackci(fsm *f, u_char *p, int len)
-{
- lcp_options *go = &lcp_gotoptions[f->unit];
- u_char cilen, citype, cichar;
- u_short cishort;
- u32_t cilong;
-
- /*
- * CIs must be in exactly the same order that we sent.
- * Check packet length and CI length at each step.
- * If we find any deviations, then this packet is bad.
- */
+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; \
- }
+ 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; \
- }
+ if (neg) { \
+ if ((len -= CILEN_SHORT) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_SHORT || \
+ citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ }
#define ACKCICHAR(opt, neg, val) \
- if (neg) { \
- if ((len -= CILEN_CHAR) < 0) \
- goto bad; \
- GETCHAR(citype, p); \
- GETCHAR(cilen, p); \
- if (cilen != CILEN_CHAR || citype != opt) \
- goto bad; \
- GETCHAR(cichar, p); \
- if (cichar != val) \
- goto bad; \
- }
-#define ACKCICHAP(opt, neg, val, digest) \
- if (neg) { \
- if ((len -= CILEN_CHAP) < 0) \
- goto bad; \
- GETCHAR(citype, p); \
- GETCHAR(cilen, p); \
- if (cilen != CILEN_CHAP || citype != opt) \
- goto bad; \
- GETSHORT(cishort, p); \
- if (cishort != val) \
- goto bad; \
- GETCHAR(cichar, p); \
- if (cichar != digest) \
- goto bad; \
- }
+ 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 (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; \
- }
-
- ACKCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru);
- ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap);
- ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype);
- ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
- ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
- ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
- ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
- ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
- ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
-
- /*
- * If there are any remaining CIs, then this packet is bad.
- */
- if (len != 0) {
- goto bad;
- }
- LCPDEBUG(LOG_INFO, ("lcp_acki: Ack\n"));
- return (1);
+ 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(LOG_WARNING, ("lcp_acki: received bad Ack!\n"));
- return (0);
+ 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 LS_OPENED state.
+ * or if LCP is in the OPENED state.
*
* Returns:
- * 0 - Nak was bad.
- * 1 - Nak was good.
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
*/
-static int
-lcp_nakci(fsm *f, u_char *p, int len)
-{
- lcp_options *go = &lcp_gotoptions[f->unit];
- lcp_options *wo = &lcp_wantoptions[f->unit];
- u_char citype, cichar, *next;
- u_short cishort;
- u32_t cilong;
- lcp_options no; /* options we've seen Naks for */
- lcp_options try; /* options to request next time */
- int looped_back = 0;
- int cilen;
-
- BZERO(&no, sizeof(no));
- try = *go;
-
- /*
- * Any Nak'd CIs must be in exactly the same order that we sent.
- * Check packet length and CI length at each step.
- * If we find any deviations, then this packet is bad.
- */
-#define NAKCIVOID(opt, neg, code) \
- if (go->neg && \
- len >= CILEN_VOID && \
- p[1] == CILEN_VOID && \
- p[0] == opt) { \
- len -= CILEN_VOID; \
- INCPTR(CILEN_VOID, p); \
- no.neg = 1; \
- code \
- }
+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 \
- }
+ 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 \
- }
+ 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 \
- }
+ 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 (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 \
- }
-
- /*
- * We don't care if they want to send us smaller packets than
- * we want. Therefore, accept any MRU less than what we asked for,
- * but then ignore the new value when setting the MRU in the kernel.
- * If they send us a bigger MRU than what we asked, accept it, up to
- * the limit of the default MRU we'd get if we didn't negotiate.
- */
- if (go->neg_mru && go->mru != PPP_DEFMRU) {
- NAKCISHORT(CI_MRU, neg_mru,
- if (cishort <= wo->mru || cishort < PPP_DEFMRU) {
- try.mru = cishort;
- }
- );
- }
-
- /*
- * Add any characters they want to our (receive-side) asyncmap.
- */
- if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) {
- NAKCILONG(CI_ASYNCMAP, neg_asyncmap,
- try.asyncmap = go->asyncmap | cilong;
- );
- }
-
- /*
- * If they've nak'd our authentication-protocol, check whether
- * they are proposing a different protocol, or a different
- * hash algorithm for CHAP.
- */
- if ((go->neg_chap || go->neg_upap)
- && len >= CILEN_SHORT
- && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) {
- cilen = p[1];
- len -= cilen;
- no.neg_chap = go->neg_chap;
- no.neg_upap = go->neg_upap;
- INCPTR(2, p);
- GETSHORT(cishort, p);
- if (cishort == PPP_PAP && cilen == CILEN_SHORT) {
- /*
- * If we were asking for CHAP, they obviously don't want to do it.
- * If we weren't asking for CHAP, then we were asking for PAP,
- * in which case this Nak is bad.
- */
- if (!go->neg_chap) {
- goto bad;
- }
- try.neg_chap = 0;
-
- } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) {
- GETCHAR(cichar, p);
- if (go->neg_chap) {
- /*
- * We were asking for CHAP/MD5; they must want a different
- * algorithm. If they can't do MD5, we'll have to stop
- * asking for CHAP.
- */
- if (cichar != go->chap_mdtype) {
- try.neg_chap = 0;
- }
- } else {
- /*
- * Stop asking for PAP if we were asking for it.
- */
- try.neg_upap = 0;
- }
-
- } else {
- /*
- * We don't recognize what they're suggesting.
- * Stop asking for what we were asking for.
- */
- if (go->neg_chap) {
- try.neg_chap = 0;
- } else {
- try.neg_upap = 0;
- }
- p += cilen - CILEN_SHORT;
+ if (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 \
}
- }
-
- /*
- * 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 */
+#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; \
}
- );
-
- /*
- * Only implementing CBCP...not the rest of the callback options
- */
- NAKCICHAR(CI_CALLBACK, neg_cbcp,
- try.neg_cbcp = 0;
- );
-
- /*
- * Check for a looped-back line.
- */
- NAKCILONG(CI_MAGICNUMBER, neg_magicnumber,
- try.magicnumber = magic();
- looped_back = 1;
- );
-
- /*
- * Peer shouldn't send Nak for protocol compression or
- * address/control compression requests; they should send
- * a Reject instead. If they send a Nak, treat it as a Reject.
- */
- NAKCIVOID(CI_PCOMPRESSION, neg_pcompression,
- try.neg_pcompression = 0;
- );
- NAKCIVOID(CI_ACCOMPRESSION, neg_accompression,
- try.neg_accompression = 0;
- );
-
- /*
- * There may be remaining CIs, if the peer is requesting negotiation
- * on an option that we didn't include in our request packet.
- * If we see an option that we requested, or one we've already seen
- * in this packet, then this packet is bad.
- * If we wanted to respond by starting to negotiate on the requested
- * option(s), we could, but we don't, because except for the
- * authentication type and quality protocol, if we are not negotiating
- * an option, it is because we were told not to.
- * For the authentication type, the Nak from the peer means
- * `let me authenticate myself with you' which is a bit pointless.
- * For the quality protocol, the Nak means `ask me to send you quality
- * reports', but if we didn't ask for them, we don't want them.
- * An option we don't recognize represents the peer asking to
- * negotiate some option we don't support, so ignore it.
- */
- while (len > CILEN_VOID) {
- GETCHAR(citype, p);
- GETCHAR(cilen, p);
- if (cilen < CILEN_VOID || (len -= cilen) < 0) {
- goto bad;
+
+ /*
+ * 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;
+ );
}
- next = p + cilen - 2;
- switch (citype) {
- case CI_MRU:
- if ((go->neg_mru && go->mru != PPP_DEFMRU)
- || no.neg_mru || cilen != CILEN_SHORT) {
- goto bad;
- }
- GETSHORT(cishort, p);
- if (cishort < PPP_DEFMRU) {
- try.mru = cishort;
- }
- break;
- case CI_ASYNCMAP:
- if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl)
- || no.neg_asyncmap || cilen != CILEN_LONG) {
- goto bad;
- }
- break;
- case CI_AUTHTYPE:
- if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap) {
- goto bad;
- }
- break;
- case CI_MAGICNUMBER:
- if (go->neg_magicnumber || no.neg_magicnumber ||
- cilen != CILEN_LONG) {
- goto bad;
- }
- break;
- case CI_PCOMPRESSION:
- if (go->neg_pcompression || no.neg_pcompression
- || cilen != CILEN_VOID) {
- goto bad;
- }
- break;
- case CI_ACCOMPRESSION:
- if (go->neg_accompression || no.neg_accompression
- || cilen != CILEN_VOID) {
- goto bad;
- }
- break;
- case CI_QUALITY:
- if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR) {
- goto bad;
- }
- break;
+ /*
+ * 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;
+ );
}
- p = next;
- }
-
- /* If there is still anything left, this packet is bad. */
- if (len != 0) {
- goto bad;
- }
-
- /*
- * OK, the Nak is good. Now we can update state.
- */
- if (f->state != LS_OPENED) {
- if (looped_back) {
- if (++try.numloops >= lcp_loopbackfail) {
- LCPDEBUG(LOG_NOTICE, ("Serial line is looped back.\n"));
- lcp_close(f->unit, "Loopback detected");
- }
- } else {
- try.numloops = 0;
+
+ /*
+ * 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;
+ }
}
- *go = try;
- }
- return 1;
+#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(LOG_WARNING, ("lcp_nakci: received bad Nak!\n"));
- return 0;
+ 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 LS_OPENED state.
+ * or if LCP is in the OPENED state.
*
* Returns:
- * 0 - Reject was bad.
- * 1 - Reject was good.
+ * 0 - Reject was bad.
+ * 1 - Reject was good.
*/
-static int
-lcp_rejci(fsm *f, u_char *p, int len)
-{
- lcp_options *go = &lcp_gotoptions[f->unit];
- u_char cichar;
- u_short cishort;
- u32_t cilong;
- lcp_options try; /* options to request next time */
-
- try = *go;
-
- /*
- * Any Rejected CIs must be in exactly the same order that we sent.
- * Check packet length and CI length at each step.
- * If we find any deviations, then this packet is bad.
- */
+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; \
- LCPDEBUG(LOG_INFO, ("lcp_rejci: void opt %d rejected\n", opt)); \
- }
+ 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; \
- LCPDEBUG(LOG_INFO, ("lcp_rejci: short opt %d rejected\n", opt)); \
- }
-#define REJCICHAP(opt, neg, val, digest) \
- if (go->neg && \
- len >= CILEN_CHAP && \
- p[1] == CILEN_CHAP && \
- p[0] == opt) { \
- len -= CILEN_CHAP; \
- INCPTR(2, p); \
- GETSHORT(cishort, p); \
- GETCHAR(cichar, p); \
- /* Check rejected value. */ \
- if (cishort != val || cichar != digest) { \
- goto bad; \
- } \
- try.neg = 0; \
- try.neg_upap = 0; \
- LCPDEBUG(LOG_INFO, ("lcp_rejci: chap opt %d rejected\n", opt)); \
- }
+ 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; \
- LCPDEBUG(LOG_INFO, ("lcp_rejci: long opt %d rejected\n", opt)); \
- }
+ 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; \
- LCPDEBUG(LOG_INFO, ("lcp_rejci: LQR opt %d rejected\n", opt)); \
- }
+ 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; \
- LCPDEBUG(LOG_INFO, ("lcp_rejci: Callback opt %d rejected\n", opt)); \
- }
-
- REJCISHORT(CI_MRU, neg_mru, go->mru);
- REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap);
- REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, go->chap_mdtype);
- if (!go->neg_chap) {
- REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP);
- }
- REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period);
- REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT);
- REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber);
- REJCIVOID(CI_PCOMPRESSION, neg_pcompression);
- REJCIVOID(CI_ACCOMPRESSION, neg_accompression);
-
- /*
- * If there are any remaining CIs, then this packet is bad.
- */
- if (len != 0) {
- goto bad;
- }
- /*
- * Now we can update state.
- */
- if (f->state != LS_OPENED) {
- *go = try;
- }
- return 1;
-
+ 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(LOG_WARNING, ("lcp_rejci: received bad Reject!\n"));
- return 0;
+ LCPDEBUG(("lcp_rejci: received bad Reject!"));
+ return 0;
}
@@ -1218,464 +1813,521 @@ bad:
* 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, /* Requested CIs */
- int *lenp, /* Length of requested CIs */
- int reject_if_disagree)
-{
- lcp_options *go = &lcp_gotoptions[f->unit];
- lcp_options *ho = &lcp_hisoptions[f->unit];
- lcp_options *ao = &lcp_allowoptions[f->unit];
- u_char *cip, *next; /* Pointer to current and next CIs */
- int cilen, citype; /* Parsed len, type */
- u_char cichar; /* Parsed char value */
- u_short cishort; /* Parsed short value */
- u32_t cilong; /* Parse long value */
- int rc = CONFACK; /* Final packet return code */
- int orc; /* Individual option return code */
- u_char *p; /* Pointer to next char to parse */
- u_char *rejp; /* Pointer to next char in reject frame */
- u_char *nakp; /* Pointer to next char in Nak frame */
- int l = *lenp; /* Length left */
-#if TRACELCP > 0
- char traceBuf[80];
- size_t traceNdx = 0;
-#endif
-
- /*
- * Reset all his options.
- */
- BZERO(ho, sizeof(*ho));
-
- /*
- * Process all his options.
- */
- next = inp;
- nakp = nak_buffer;
- rejp = inp;
- while (l) {
- orc = CONFACK; /* Assume success */
- cip = p = next; /* Remember begining of CI */
- if (l < 2 || /* Not enough data for CI header or */
- p[1] < 2 || /* CI length too small or */
- p[1] > l) { /* CI length too big? */
- LCPDEBUG(LOG_WARNING, ("lcp_reqci: bad CI length!\n"));
- orc = CONFREJ; /* Reject bad CI */
- cilen = l; /* Reject till end of packet */
- l = 0; /* Don't loop again */
- citype = 0;
- goto endswitch;
- }
- GETCHAR(citype, p); /* Parse CI type */
- GETCHAR(cilen, p); /* Parse CI length */
- l -= cilen; /* Adjust remaining length */
- next += cilen; /* Step to next CI */
-
- switch (citype) { /* Check CI type */
- case CI_MRU:
- if (!ao->neg_mru) { /* Allow option? */
- LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject MRU - not allowed\n"));
- orc = CONFREJ; /* Reject CI */
- break;
- } else if (cilen != CILEN_SHORT) { /* Check CI length */
- LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject MRU - bad length\n"));
- orc = CONFREJ; /* Reject CI */
- break;
- }
- GETSHORT(cishort, p); /* Parse MRU */
-
- /*
- * He must be able to receive at least our minimum.
- * No need to check a maximum. If he sends a large number,
- * we'll just ignore it.
- */
- if (cishort < PPP_MINMRU) {
- LCPDEBUG(LOG_INFO, ("lcp_reqci: Nak - MRU too small\n"));
- orc = CONFNAK; /* Nak CI */
- PUTCHAR(CI_MRU, nakp);
- PUTCHAR(CILEN_SHORT, nakp);
- PUTSHORT(PPP_MINMRU, nakp); /* Give him a hint */
- break;
- }
- ho->neg_mru = 1; /* Remember he sent MRU */
- ho->mru = cishort; /* And remember value */
-#if TRACELCP > 0
- snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MRU %d", cishort);
- traceNdx = strlen(traceBuf);
-#endif
- break;
-
- case CI_ASYNCMAP:
- if (!ao->neg_asyncmap) {
- LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject ASYNCMAP not allowed\n"));
- orc = CONFREJ;
- break;
- } else if (cilen != CILEN_LONG) {
- LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject ASYNCMAP bad length\n"));
- orc = CONFREJ;
- break;
- }
- GETLONG(cilong, p);
-
- /*
- * Asyncmap must have set at least the bits
- * which are set in lcp_allowoptions[unit].asyncmap.
- */
- if ((ao->asyncmap & ~cilong) != 0) {
- LCPDEBUG(LOG_INFO, ("lcp_reqci: Nak ASYNCMAP %lX missing %lX\n",
- cilong, ao->asyncmap));
- orc = CONFNAK;
- PUTCHAR(CI_ASYNCMAP, nakp);
- PUTCHAR(CILEN_LONG, nakp);
- PUTLONG(ao->asyncmap | cilong, nakp);
- break;
- }
- ho->neg_asyncmap = 1;
- ho->asyncmap = cilong;
-#if TRACELCP > 0
- snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ASYNCMAP=%lX", cilong);
- traceNdx = strlen(traceBuf);
-#endif
- break;
-
- case CI_AUTHTYPE:
- if (cilen < CILEN_SHORT) {
- LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject AUTHTYPE missing arg\n"));
- orc = CONFREJ;
- break;
- } else if (!(ao->neg_upap || ao->neg_chap)) {
- /*
- * Reject the option if we're not willing to authenticate.
- */
- LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject AUTHTYPE not allowed\n"));
- orc = CONFREJ;
- break;
- }
- GETSHORT(cishort, p);
-
- /*
- * Authtype must be UPAP or CHAP.
- *
- * Note: if both ao->neg_upap and ao->neg_chap are set,
- * and the peer sends a Configure-Request with two
- * authenticate-protocol requests, one for CHAP and one
- * for UPAP, then we will reject the second request.
- * Whether we end up doing CHAP or UPAP depends then on
- * the ordering of the CIs in the peer's Configure-Request.
- */
-
- if (cishort == PPP_PAP) {
- if (ho->neg_chap) { /* we've already accepted CHAP */
- LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE PAP already accepted\n"));
- orc = CONFREJ;
- break;
- } else if (cilen != CILEN_SHORT) {
- LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE PAP bad len\n"));
- orc = CONFREJ;
- break;
- }
- if (!ao->neg_upap) { /* we don't want to do PAP */
- LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE PAP not allowed\n"));
- orc = CONFNAK; /* NAK it and suggest CHAP */
- PUTCHAR(CI_AUTHTYPE, nakp);
- PUTCHAR(CILEN_CHAP, nakp);
- PUTSHORT(PPP_CHAP, nakp);
- PUTCHAR(ao->chap_mdtype, nakp);
- break;
- }
- ho->neg_upap = 1;
-#if TRACELCP > 0
- snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PAP (%X)", cishort);
- traceNdx = strlen(traceBuf);
-#endif
- break;
- }
- if (cishort == PPP_CHAP) {
- if (ho->neg_upap) { /* we've already accepted PAP */
- LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE CHAP accepted PAP\n"));
- orc = CONFREJ;
- break;
- } else if (cilen != CILEN_CHAP) {
- LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE CHAP bad len\n"));
- orc = CONFREJ;
- break;
- }
- if (!ao->neg_chap) { /* we don't want to do CHAP */
- LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE CHAP not allowed\n"));
- orc = CONFNAK; /* NAK it and suggest PAP */
- PUTCHAR(CI_AUTHTYPE, nakp);
- PUTCHAR(CILEN_SHORT, nakp);
- PUTSHORT(PPP_PAP, nakp);
- break;
- }
- GETCHAR(cichar, p); /* get digest type*/
- if (cichar != CHAP_DIGEST_MD5
-#if MSCHAP_SUPPORT
- && cichar != CHAP_MICROSOFT
-#endif
- ) {
- LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE CHAP digest=%d\n", (int)cichar));
- orc = CONFNAK;
- PUTCHAR(CI_AUTHTYPE, nakp);
- PUTCHAR(CILEN_CHAP, nakp);
- PUTSHORT(PPP_CHAP, nakp);
- PUTCHAR(ao->chap_mdtype, nakp);
- break;
- }
-#if TRACELCP > 0
- snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CHAP %X,%d", cishort, (int)cichar);
- traceNdx = strlen(traceBuf);
-#endif
- ho->chap_mdtype = cichar; /* save md type */
- ho->neg_chap = 1;
- break;
- }
-
- /*
- * We don't recognize the protocol they're asking for.
- * Nak it with something we're willing to do.
- * (At this point we know ao->neg_upap || ao->neg_chap.)
- */
- orc = CONFNAK;
- PUTCHAR(CI_AUTHTYPE, nakp);
- if (ao->neg_chap) {
- LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE %d req CHAP\n", cishort));
- PUTCHAR(CILEN_CHAP, nakp);
- PUTSHORT(PPP_CHAP, nakp);
- PUTCHAR(ao->chap_mdtype, nakp);
- } else {
- LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE %d req PAP\n", cishort));
- PUTCHAR(CILEN_SHORT, nakp);
- PUTSHORT(PPP_PAP, nakp);
- }
- break;
-
- case CI_QUALITY:
- GETSHORT(cishort, p);
- GETLONG(cilong, p);
-#if TRACELCP > 0
- snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " QUALITY (%x %x)", cishort, (unsigned int) cilong);
- traceNdx = strlen(traceBuf);
-#endif
+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 */
- if (!ao->neg_lqr ||
- cilen != CILEN_LQR) {
- orc = CONFREJ;
- break;
- }
-
- /*
- * Check the protocol and the reporting period.
- * XXX When should we Nak this, and what with?
- */
- if (cishort != PPP_LQR) {
- orc = CONFNAK;
- PUTCHAR(CI_QUALITY, nakp);
- PUTCHAR(CILEN_LQR, nakp);
- PUTSHORT(PPP_LQR, nakp);
- PUTLONG(ao->lqr_period, nakp);
- break;
- }
- break;
-
- case CI_MAGICNUMBER:
- if (!(ao->neg_magicnumber || go->neg_magicnumber) ||
- cilen != CILEN_LONG) {
- orc = CONFREJ;
- break;
- }
- GETLONG(cilong, p);
-#if TRACELCP > 0
- snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MAGICNUMBER (%lX)", cilong);
- traceNdx = strlen(traceBuf);
-#endif
-
- /*
- * He must have a different magic number.
- */
- if (go->neg_magicnumber &&
- cilong == go->magicnumber) {
- cilong = magic(); /* Don't put magic() inside macro! */
- orc = CONFNAK;
- PUTCHAR(CI_MAGICNUMBER, nakp);
- PUTCHAR(CILEN_LONG, nakp);
- PUTLONG(cilong, nakp);
- break;
- }
- ho->neg_magicnumber = 1;
- ho->magicnumber = cilong;
- break;
-
-
- case CI_PCOMPRESSION:
-#if TRACELCP > 0
- snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PCOMPRESSION");
- traceNdx = strlen(traceBuf);
-#endif
- if (!ao->neg_pcompression ||
- cilen != CILEN_VOID) {
- orc = CONFREJ;
- break;
- }
- ho->neg_pcompression = 1;
- break;
-
- case CI_ACCOMPRESSION:
-#if TRACELCP > 0
- snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ACCOMPRESSION");
- traceNdx = strlen(traceBuf);
-#endif
- if (!ao->neg_accompression ||
- cilen != CILEN_VOID) {
- orc = CONFREJ;
- break;
- }
- ho->neg_accompression = 1;
- break;
-
- case CI_MRRU:
-#if TRACELCP > 0
- snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_MRRU");
- traceNdx = strlen(traceBuf);
-#endif
- orc = CONFREJ;
- break;
-
- case CI_SSNHF:
-#if TRACELCP > 0
- snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_SSNHF");
- traceNdx = strlen(traceBuf);
-#endif
- orc = CONFREJ;
- break;
-
- case CI_EPDISC:
-#if TRACELCP > 0
- snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_EPDISC");
- traceNdx = strlen(traceBuf);
-#endif
- orc = CONFREJ;
- break;
-
- default:
-#if TRACELCP
- snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " unknown %d", citype);
- traceNdx = strlen(traceBuf);
-#endif
- orc = CONFREJ;
- break;
- }
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
- endswitch:
-#if TRACELCP
- if (traceNdx >= 80 - 32) {
- LCPDEBUG(LOG_INFO, ("lcp_reqci: rcvd%s\n", traceBuf));
- traceNdx = 0;
- }
-#endif
- if (orc == CONFACK && /* Good CI */
- rc != CONFACK) { /* but prior CI wasnt? */
- continue; /* Don't send this one */
+ /*
+ * 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;
}
- if (orc == CONFNAK) { /* Nak this CI? */
- if (reject_if_disagree /* Getting fed up with sending NAKs? */
- && citype != CI_MAGICNUMBER) {
- orc = CONFREJ; /* Get tough if so */
- } else {
- if (rc == CONFREJ) { /* Rejecting prior CI? */
- continue; /* Don't send this one */
- }
- rc = CONFNAK;
- }
- }
- if (orc == CONFREJ) { /* Reject this CI */
- rc = CONFREJ;
- if (cip != rejp) { /* Need to move rejected CI? */
- BCOPY(cip, rejp, cilen); /* Move it */
- }
- INCPTR(cilen, rejp); /* Update output pointer */
+ 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 *nakp.
- * At present there are no cases where we want to ask the
- * peer to negotiate an option.
- */
+ /*
+ * 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) {
+ switch (rc) {
case CONFACK:
- *lenp = (int)(next - inp);
- break;
+ *lenp = next - inp;
+ break;
case CONFNAK:
- /*
- * Copy the Nak'd options from the nak_buffer to the caller's buffer.
- */
- *lenp = (int)(nakp - nak_buffer);
- BCOPY(nak_buffer, inp, *lenp);
- break;
+ /*
+ * 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 = (int)(rejp - inp);
- break;
- }
-
-#if TRACELCP > 0
- if (traceNdx > 0) {
- LCPDEBUG(LOG_INFO, ("lcp_reqci: %s\n", traceBuf));
- }
-#endif
- LCPDEBUG(LOG_INFO, ("lcp_reqci: returning CONF%s.\n", CODENAME(rc)));
- return (rc); /* Return final code */
+ *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)
-{
- lcp_options *wo = &lcp_wantoptions[f->unit];
- lcp_options *ho = &lcp_hisoptions[f->unit];
- lcp_options *go = &lcp_gotoptions[f->unit];
- lcp_options *ao = &lcp_allowoptions[f->unit];
-
- if (!go->neg_magicnumber) {
- go->magicnumber = 0;
- }
- if (!ho->neg_magicnumber) {
- ho->magicnumber = 0;
- }
-
- /*
- * Set our MTU to the smaller of the MTU we wanted and
- * the MRU our peer wanted. If we negotiated an MRU,
- * set our MRU to the larger of value we wanted and
- * the value we got in the negotiation.
- */
- ppp_send_config(f->unit, LWIP_MIN(ao->mru, (ho->neg_mru? ho->mru: PPP_MRU)),
- (ho->neg_asyncmap? ho->asyncmap: 0xffffffffl),
- ho->neg_pcompression, ho->neg_accompression);
- /*
- * If the asyncmap hasn't been negotiated, we really should
- * set the receive asyncmap to ffffffff, but we set it to 0
- * for backwards contemptibility.
- */
- ppp_recv_config(f->unit, (go->neg_mru? LWIP_MAX(wo->mru, go->mru): PPP_MRU),
- (go->neg_asyncmap? go->asyncmap: 0x00000000),
- go->neg_pcompression, go->neg_accompression);
-
- if (ho->neg_mru) {
- peer_mru[f->unit] = ho->mru;
- }
-
- lcp_echo_lowerup(f->unit); /* Enable echo messages */
-
- link_established(f->unit); /* The link is up; authenticate now */
+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);
}
@@ -1684,383 +2336,455 @@ lcp_up(fsm *f)
*
* Alert other protocols.
*/
-static void
-lcp_down(fsm *f)
-{
- lcp_options *go = &lcp_gotoptions[f->unit];
+static void lcp_down(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+ lcp_options *go = &pcb->lcp_gotoptions;
- lcp_echo_lowerdown(f->unit);
+ lcp_echo_lowerdown(f->pcb);
- link_down(f->unit);
+ link_down(pcb);
- ppp_send_config(f->unit, PPP_MRU, 0xffffffffl, 0, 0);
- ppp_recv_config(f->unit, PPP_MRU,
- (go->neg_asyncmap? go->asyncmap: 0x00000000),
- go->neg_pcompression, go->neg_accompression);
- peer_mru[f->unit] = PPP_MRU;
+ 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)
-{
- link_required(f->unit); /* lwip: currently does nothing */
+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)
-{
- link_terminated(f->unit); /* we are finished with the link */
-}
-
-
-#if PPP_ADDITIONAL_CALLBACKS
-/*
- * print_string - print a readable representation of a string using
- * printer.
- */
-static void
-print_string( char *p, int len, void (*printer) (void *, char *, ...), void *arg)
-{
- int c;
-
- printer(arg, "\"");
- for (; len > 0; --len) {
- c = *p++;
- if (' ' <= c && c <= '~') {
- if (c == '\\' || c == '"') {
- printer(arg, "\\");
- }
- printer(arg, "%c", c);
- } else {
- switch (c) {
- case '\n':
- printer(arg, "\\n");
- break;
- case '\r':
- printer(arg, "\\r");
- break;
- case '\t':
- printer(arg, "\\t");
- break;
- default:
- printer(arg, "\\%.3o", c);
- }
- }
- }
- printer(arg, "\"");
+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 char *lcp_codenames[] = {
- "ConfReq", "ConfAck", "ConfNak", "ConfRej",
- "TermReq", "TermAck", "CodeRej", "ProtRej",
- "EchoReq", "EchoRep", "DiscReq"
+static const char* const lcp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej", "ProtRej",
+ "EchoReq", "EchoRep", "DiscReq", "Ident",
+ "TimeRem"
};
-static int
-lcp_printpkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg)
-{
- int code, id, len, olen;
- u_char *pstart, *optend;
- u_short cishort;
- u32_t cilong;
+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 <= sizeof(lcp_codenames) / sizeof(char *)) {
- printer(arg, " %s", lcp_codenames[code-1]);
- } else {
- printer(arg, " code=0x%x", code);
- }
- printer(arg, " id=0x%x", id);
- len -= HEADERLEN;
- switch (code) {
+ 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%lx", cilong);
- }
- break;
- case CI_AUTHTYPE:
- if (olen >= CILEN_SHORT) {
- p += 2;
- printer(arg, "auth ");
- GETSHORT(cishort, p);
- switch (cishort) {
- case PPP_PAP:
- printer(arg, "pap");
- break;
- case PPP_CHAP:
- printer(arg, "chap");
- break;
- default:
- printer(arg, "0x%x", cishort);
- }
- }
- break;
- case CI_QUALITY:
- if (olen >= CILEN_SHORT) {
- p += 2;
- printer(arg, "quality ");
- GETSHORT(cishort, p);
- switch (cishort) {
- case PPP_LQR:
- printer(arg, "lqr");
- break;
- default:
- printer(arg, "0x%x", cishort);
- }
- }
- break;
- case CI_CALLBACK:
- if (olen >= CILEN_CHAR) {
- p += 2;
- printer(arg, "callback ");
- GETSHORT(cishort, p);
- switch (cishort) {
- case CBCP_OPT:
- printer(arg, "CBCP");
- break;
- default:
- printer(arg, "0x%x", cishort);
- }
- }
- break;
- case CI_MAGICNUMBER:
- if (olen == CILEN_LONG) {
- p += 2;
- GETLONG(cilong, p);
- printer(arg, "magic 0x%x", cilong);
- }
- break;
- case CI_PCOMPRESSION:
- if (olen == CILEN_VOID) {
- p += 2;
- printer(arg, "pcomp");
- }
- break;
- case CI_ACCOMPRESSION:
- if (olen == CILEN_VOID) {
- p += 2;
- printer(arg, "accomp");
- }
- break;
- }
- while (p < optend) {
- GETCHAR(code, p);
- printer(arg, " %.2x", code);
- }
- printer(arg, ">");
- }
- break;
-
+ /* 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, " ");
- print_string((char*)p, len, printer, arg);
- p += len;
- len = 0;
- }
- break;
-
+ 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);
- p += 4;
- len -= 4;
- }
- break;
- }
+ 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 (; len > 0; --len) {
- GETCHAR(code, p);
- printer(arg, " %.2x", code);
- }
+ /* 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 (int)(p - pstart);
+ return p - pstart;
}
-#endif /* PPP_ADDITIONAL_CALLBACKS */
+#endif /* PRINTPKT_SUPPORT */
/*
* Time to shut down the link because there is nothing out there.
*/
-static void
-LcpLinkFailure (fsm *f)
-{
- if (f->state == LS_OPENED) {
- LCPDEBUG(LOG_INFO, ("No response to %d echo-requests\n", lcp_echos_pending));
- LCPDEBUG(LOG_NOTICE, ("Serial link appears to be disconnected.\n"));
- lcp_close(f->unit, "Peer not responding");
- }
+
+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)
-{
- LcpSendEchoRequest (f);
- /*
- * Start the timer for the next interval.
- */
- LWIP_ASSERT("lcp_echo_timer_running == 0", lcp_echo_timer_running == 0);
+static void LcpEchoCheck(fsm *f) {
+ ppp_pcb *pcb = f->pcb;
+
+ LcpSendEchoRequest (f);
+ if (f->state != PPP_FSM_OPENED)
+ return;
- TIMEOUT (LcpEchoTimeout, f, lcp_echo_interval);
- lcp_echo_timer_running = 1;
+ /*
+ * 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)
-{
- if (lcp_echo_timer_running != 0) {
- lcp_echo_timer_running = 0;
- LcpEchoCheck ((fsm *) arg);
- }
+
+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)
-{
- u32_t magic;
-
- LWIP_UNUSED_ARG(id);
-
- /* Check the magic number - don't count replies from ourselves. */
- if (len < 4) {
- LCPDEBUG(LOG_WARNING, ("lcp: received short Echo-Reply, length %d\n", len));
- return;
- }
- GETLONG(magic, inp);
- if (lcp_gotoptions[f->unit].neg_magicnumber && magic == lcp_gotoptions[f->unit].magicnumber) {
- LCPDEBUG(LOG_WARNING, ("appear to have received our own echo-reply!\n"));
- return;
- }
-
- /* Reset the number of outstanding echo frames */
- lcp_echos_pending = 0;
+
+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)
-{
- u32_t lcp_magic;
- u_char pkt[4], *pktp;
-
- /*
- * Detect the failure of the peer at this point.
- */
- if (lcp_echo_fails != 0) {
- if (lcp_echos_pending >= lcp_echo_fails) {
- LcpLinkFailure(f);
- lcp_echos_pending = 0;
+
+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;
}
- }
-
- /*
- * Make and send the echo request frame.
- */
- if (f->state == LS_OPENED) {
- lcp_magic = lcp_gotoptions[f->unit].magicnumber;
- pktp = pkt;
- PUTLONG(lcp_magic, pktp);
- fsm_sdata(f, ECHOREQ, (u_char)(lcp_echo_number++ & 0xFF), pkt, (int)(pktp - pkt));
- ++lcp_echos_pending;
- }
}
/*
* lcp_echo_lowerup - Start the timer for the LCP frame
*/
-static void
-lcp_echo_lowerup (int unit)
-{
- fsm *f = &lcp_fsm[unit];
-
- /* Clear the parameters for generating echo frames */
- lcp_echos_pending = 0;
- lcp_echo_number = 0;
- lcp_echo_timer_running = 0;
+static void lcp_echo_lowerup(ppp_pcb *pcb) {
+ fsm *f = &pcb->lcp_fsm;
- /* If a timeout interval is specified then start the timer */
- if (lcp_echo_interval != 0) {
- LcpEchoCheck (f);
- }
+ /* 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 (int unit)
-{
- fsm *f = &lcp_fsm[unit];
+static void lcp_echo_lowerdown(ppp_pcb *pcb) {
+ fsm *f = &pcb->lcp_fsm;
- if (lcp_echo_timer_running != 0) {
- UNTIMEOUT (LcpEchoTimeout, f);
- lcp_echo_timer_running = 0;
- }
+ if (pcb->lcp_echo_timer_running != 0) {
+ UNTIMEOUT (LcpEchoTimeout, f);
+ pcb->lcp_echo_timer_running = 0;
+ }
}
#endif /* PPP_SUPPORT */
diff --git a/lwip/src/netif/ppp/lcp.h b/lwip/src/netif/ppp/lcp.h
deleted file mode 100644
index b9201ee..0000000
--- a/lwip/src/netif/ppp/lcp.h
+++ /dev/null
@@ -1,151 +0,0 @@
-/*****************************************************************************
-* lcp.h - Network Link Control Protocol header file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* portions Copyright (c) 1997 Global Election Systems Inc.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 97-11-05 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
-* Original derived from BSD codes.
-*****************************************************************************/
-/*
- * lcp.h - Link Control Protocol definitions.
- *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * $Id: lcp.h,v 1.4 2010/01/18 20:49:43 goldsimon Exp $
- */
-
-#ifndef LCP_H
-#define LCP_H
-/*
- * Options.
- */
-#define CI_MRU 1 /* Maximum Receive Unit */
-#define CI_ASYNCMAP 2 /* Async Control Character Map */
-#define CI_AUTHTYPE 3 /* Authentication Type */
-#define CI_QUALITY 4 /* Quality Protocol */
-#define CI_MAGICNUMBER 5 /* Magic Number */
-#define CI_PCOMPRESSION 7 /* Protocol Field Compression */
-#define CI_ACCOMPRESSION 8 /* Address/Control Field Compression */
-#define CI_CALLBACK 13 /* callback */
-#define CI_MRRU 17 /* max reconstructed receive unit; multilink */
-#define CI_SSNHF 18 /* short sequence numbers for multilink */
-#define CI_EPDISC 19 /* endpoint discriminator */
-
-/*
- * LCP-specific packet types (code numbers).
- */
-#define PROTREJ 8 /* Protocol Reject */
-#define ECHOREQ 9 /* Echo Request */
-#define ECHOREP 10 /* Echo Reply */
-#define DISCREQ 11 /* Discard Request */
-#define CBCP_OPT 6 /* Use callback control protocol */
-
-/*
- * The state of options is described by an lcp_options structure.
- */
-typedef struct lcp_options {
- u_int passive : 1; /* Don't die if we don't get a response */
- u_int silent : 1; /* Wait for the other end to start first */
- u_int restart : 1; /* Restart vs. exit after close */
- u_int neg_mru : 1; /* Negotiate the MRU? */
- u_int neg_asyncmap : 1; /* Negotiate the async map? */
- u_int neg_upap : 1; /* Ask for UPAP authentication? */
- u_int neg_chap : 1; /* Ask for CHAP authentication? */
- u_int neg_magicnumber : 1; /* Ask for magic number? */
- u_int neg_pcompression : 1; /* HDLC Protocol Field Compression? */
- u_int neg_accompression : 1; /* HDLC Address/Control Field Compression? */
- u_int neg_lqr : 1; /* Negotiate use of Link Quality Reports */
- u_int neg_cbcp : 1; /* Negotiate use of CBCP */
-#ifdef PPP_MULTILINK
- u_int neg_mrru : 1; /* Negotiate multilink MRRU */
- u_int neg_ssnhf : 1; /* Negotiate short sequence numbers */
- u_int neg_endpoint : 1; /* Negotiate endpoint discriminator */
-#endif
- u_short mru; /* Value of MRU */
-#ifdef PPP_MULTILINK
- u_short mrru; /* Value of MRRU, and multilink enable */
-#endif
- u_char chap_mdtype; /* which MD type (hashing algorithm) */
- u32_t asyncmap; /* Value of async map */
- u32_t magicnumber;
- int numloops; /* Number of loops during magic number neg. */
- u32_t lqr_period; /* Reporting period for LQR 1/100ths second */
-#ifdef PPP_MULTILINK
- struct epdisc endpoint; /* endpoint discriminator */
-#endif
-} lcp_options;
-
-/*
- * Values for phase from BSD pppd.h based on RFC 1661.
- */
-typedef enum {
- PHASE_DEAD = 0,
- PHASE_INITIALIZE,
- PHASE_ESTABLISH,
- PHASE_AUTHENTICATE,
- PHASE_CALLBACK,
- PHASE_NETWORK,
- PHASE_TERMINATE
-} LinkPhase;
-
-
-
-extern LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */
-extern lcp_options lcp_wantoptions[];
-extern lcp_options lcp_gotoptions[];
-extern lcp_options lcp_allowoptions[];
-extern lcp_options lcp_hisoptions[];
-extern ext_accm xmit_accm[];
-
-
-void lcp_init (int);
-void lcp_open (int);
-void lcp_close (int, char *);
-void lcp_lowerup (int);
-void lcp_lowerdown(int);
-void lcp_sprotrej (int, u_char *, int); /* send protocol reject */
-
-extern struct protent lcp_protent;
-
-/* Default number of times we receive our magic number from the peer
- before deciding the link is looped-back. */
-#define DEFLOOPBACKFAIL 10
-
-#endif /* LCP_H */
diff --git a/lwip/src/netif/ppp/magic.c b/lwip/src/netif/ppp/magic.c
index 3732a42..d0d87c5 100644
--- a/lwip/src/netif/ppp/magic.c
+++ b/lwip/src/netif/ppp/magic.c
@@ -1,19 +1,60 @@
+/*
+ * 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.
+ */
/*****************************************************************************
-* magic.c - Network Random Number Generator program file.
+* randm.c - Random number generator program file.
*
* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* portions Copyright (c) 1997 by Global Election Systems 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
+* 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.
+* 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,
@@ -27,54 +68,227 @@
*
* 03-01-01 Marc Boucher <marc@mbsi.ca>
* Ported to lwIP.
-* 97-12-04 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
-* Original based on BSD magic.c.
+* 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. */
+
/*
- * magic.c - PPP Magic Number routines.
+ * 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.
*
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
+ * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427
*
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ * 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. */
-#include "lwip/opt.h"
-#if PPP_SUPPORT
+/***********************************/
+/*** PUBLIC FUNCTION DEFINITIONS ***/
+/***********************************/
-#include "ppp_impl.h"
-#include "randm.h"
-#include "magic.h"
+/*
+ * 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 */
+}
/*
- * magicInit - Initialize the magic number generator.
+ * Return a new random number.
*
- * Since we use another random number generator that has its own
- * initialization, we do nothing here.
+ * 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.
*/
-void magicInit()
-{
- return;
+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 */
/*
- * magic - Returns the next magic number.
+ * Return a new random number between 0 and (2^pow)-1 included.
*/
-u32_t magic()
-{
- return avRandom();
+u32_t magic_pow(u8_t pow) {
+ return magic() & ~(~0UL<<pow);
}
#endif /* PPP_SUPPORT */
diff --git a/lwip/src/netif/ppp/magic.h b/lwip/src/netif/ppp/magic.h
deleted file mode 100644
index eba70d2..0000000
--- a/lwip/src/netif/ppp/magic.h
+++ /dev/null
@@ -1,63 +0,0 @@
-/*****************************************************************************
-* magic.h - Network Random Number Generator header file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* portions Copyright (c) 1997 Global Election Systems Inc.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 97-12-04 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
-* Original derived from BSD codes.
-*****************************************************************************/
-/*
- * magic.h - PPP Magic Number definitions.
- *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- * $Id: magic.h,v 1.3 2010/01/18 20:49:43 goldsimon Exp $
- */
-
-#ifndef MAGIC_H
-#define MAGIC_H
-
-/* Initialize the magic number generator */
-void magicInit(void);
-
-/* Returns the next magic number */
-u32_t magic(void);
-
-#endif /* MAGIC_H */
diff --git a/lwip/src/netif/ppp/md5.c b/lwip/src/netif/ppp/md5.c
deleted file mode 100644
index dc3cc75..0000000
--- a/lwip/src/netif/ppp/md5.c
+++ /dev/null
@@ -1,320 +0,0 @@
-/*
- ***********************************************************************
- ** md5.c -- the source code for MD5 routines **
- ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
- ** Created: 2/17/90 RLR **
- ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. **
- ***********************************************************************
- */
-
-/*
- ***********************************************************************
- ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
- ** **
- ** License to copy and use this software is granted provided that **
- ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
- ** Digest Algorithm" in all material mentioning or referencing this **
- ** software or this function. **
- ** **
- ** License is also granted to make and use derivative works **
- ** provided that such works are identified as "derived from the RSA **
- ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
- ** material mentioning or referencing the derived work. **
- ** **
- ** RSA Data Security, Inc. makes no representations concerning **
- ** either the merchantability of this software or the suitability **
- ** of this software for any particular purpose. It is provided "as **
- ** is" without express or implied warranty of any kind. **
- ** **
- ** These notices must be retained in any copies of any part of this **
- ** documentation and/or software. **
- ***********************************************************************
- */
-
-#include "lwip/opt.h"
-
-#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
-
-#if CHAP_SUPPORT || MD5_SUPPORT
-
-#include "ppp_impl.h"
-#include "pppdebug.h"
-
-#include "md5.h"
-
-#include <string.h>
-
-/*
- ***********************************************************************
- ** Message-digest routines: **
- ** To form the message digest for a message M **
- ** (1) Initialize a context buffer mdContext using MD5Init **
- ** (2) Call MD5Update on mdContext and M **
- ** (3) Call MD5Final on mdContext **
- ** The message digest is now in mdContext->digest[0...15] **
- ***********************************************************************
- */
-
-/* forward declaration */
-static void Transform (u32_t *buf, u32_t *in);
-
-static unsigned char PADDING[64] = {
- 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
-};
-
-/* F, G, H and I are basic MD5 functions */
-#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
-#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
-#define H(x, y, z) ((x) ^ (y) ^ (z))
-#define I(x, y, z) ((y) ^ ((x) | (~z)))
-
-/* ROTATE_LEFT rotates x left n bits */
-#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
-
-/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
-/* Rotation is separate from addition to prevent recomputation */
-#define FF(a, b, c, d, x, s, ac) \
- {(a) += F ((b), (c), (d)) + (x) + (u32_t)(ac); \
- (a) = ROTATE_LEFT ((a), (s)); \
- (a) += (b); \
- }
-#define GG(a, b, c, d, x, s, ac) \
- {(a) += G ((b), (c), (d)) + (x) + (u32_t)(ac); \
- (a) = ROTATE_LEFT ((a), (s)); \
- (a) += (b); \
- }
-#define HH(a, b, c, d, x, s, ac) \
- {(a) += H ((b), (c), (d)) + (x) + (u32_t)(ac); \
- (a) = ROTATE_LEFT ((a), (s)); \
- (a) += (b); \
- }
-#define II(a, b, c, d, x, s, ac) \
- {(a) += I ((b), (c), (d)) + (x) + (u32_t)(ac); \
- (a) = ROTATE_LEFT ((a), (s)); \
- (a) += (b); \
- }
-
-#ifdef __STDC__
-#define UL(x) x##UL
-#else
-#ifdef WIN32
-#define UL(x) x##UL
-#else
-#define UL(x) x
-#endif
-#endif
-
-/* The routine MD5Init initializes the message-digest context
- mdContext. All fields are set to zero.
- */
-void
-MD5Init (MD5_CTX *mdContext)
-{
- mdContext->i[0] = mdContext->i[1] = (u32_t)0;
-
- /* Load magic initialization constants. */
- mdContext->buf[0] = (u32_t)0x67452301UL;
- mdContext->buf[1] = (u32_t)0xefcdab89UL;
- mdContext->buf[2] = (u32_t)0x98badcfeUL;
- mdContext->buf[3] = (u32_t)0x10325476UL;
-}
-
-/* The routine MD5Update updates the message-digest context to
- account for the presence of each of the characters inBuf[0..inLen-1]
- in the message whose digest is being computed.
- */
-void
-MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen)
-{
- u32_t in[16];
- int mdi;
- unsigned int i, ii;
-
-#if 0
- PPPDEBUG(LOG_INFO, ("MD5Update: %u:%.*H\n", inLen, LWIP_MIN(inLen, 20) * 2, inBuf));
- PPPDEBUG(LOG_INFO, ("MD5Update: %u:%s\n", inLen, inBuf));
-#endif
-
- /* compute number of bytes mod 64 */
- mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
-
- /* update number of bits */
- if ((mdContext->i[0] + ((u32_t)inLen << 3)) < mdContext->i[0]) {
- mdContext->i[1]++;
- }
- mdContext->i[0] += ((u32_t)inLen << 3);
- mdContext->i[1] += ((u32_t)inLen >> 29);
-
- while (inLen--) {
- /* add new character to buffer, increment mdi */
- mdContext->in[mdi++] = *inBuf++;
-
- /* transform if necessary */
- if (mdi == 0x40) {
- for (i = 0, ii = 0; i < 16; i++, ii += 4) {
- in[i] = (((u32_t)mdContext->in[ii+3]) << 24) |
- (((u32_t)mdContext->in[ii+2]) << 16) |
- (((u32_t)mdContext->in[ii+1]) << 8) |
- ((u32_t)mdContext->in[ii]);
- }
- Transform (mdContext->buf, in);
- mdi = 0;
- }
- }
-}
-
-/* The routine MD5Final terminates the message-digest computation and
- ends with the desired message digest in mdContext->digest[0...15].
- */
-void
-MD5Final (unsigned char hash[], MD5_CTX *mdContext)
-{
- u32_t in[16];
- int mdi;
- unsigned int i, ii;
- unsigned int padLen;
-
- /* save number of bits */
- in[14] = mdContext->i[0];
- in[15] = mdContext->i[1];
-
- /* compute number of bytes mod 64 */
- mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
-
- /* pad out to 56 mod 64 */
- padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
- MD5Update (mdContext, PADDING, padLen);
-
- /* append length in bits and transform */
- for (i = 0, ii = 0; i < 14; i++, ii += 4) {
- in[i] = (((u32_t)mdContext->in[ii+3]) << 24) |
- (((u32_t)mdContext->in[ii+2]) << 16) |
- (((u32_t)mdContext->in[ii+1]) << 8) |
- ((u32_t)mdContext->in[ii]);
- }
- Transform (mdContext->buf, in);
-
- /* store buffer in digest */
- for (i = 0, ii = 0; i < 4; i++, ii += 4) {
- mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);
- mdContext->digest[ii+1] =
- (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
- mdContext->digest[ii+2] =
- (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
- mdContext->digest[ii+3] =
- (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
- }
- SMEMCPY(hash, mdContext->digest, 16);
-}
-
-/* Basic MD5 step. Transforms buf based on in.
- */
-static void
-Transform (u32_t *buf, u32_t *in)
-{
- u32_t a = buf[0], b = buf[1], c = buf[2], d = buf[3];
-
- /* Round 1 */
-#define S11 7
-#define S12 12
-#define S13 17
-#define S14 22
- FF ( a, b, c, d, in[ 0], S11, UL(3614090360)); /* 1 */
- FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */
- FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */
- FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */
- FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */
- FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */
- FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */
- FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */
- FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */
- FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */
- FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */
- FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */
- FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */
- FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */
- FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */
- FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */
-
- /* Round 2 */
-#define S21 5
-#define S22 9
-#define S23 14
-#define S24 20
- GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */
- GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */
- GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */
- GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */
- GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */
- GG ( d, a, b, c, in[10], S22, UL( 38016083)); /* 22 */
- GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */
- GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */
- GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */
- GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */
- GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */
- GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */
- GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */
- GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */
- GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */
- GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */
-
- /* Round 3 */
-#define S31 4
-#define S32 11
-#define S33 16
-#define S34 23
- HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */
- HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */
- HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */
- HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */
- HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */
- HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */
- HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */
- HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */
- HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */
- HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */
- HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */
- HH ( b, c, d, a, in[ 6], S34, UL( 76029189)); /* 44 */
- HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */
- HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */
- HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */
- HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */
-
- /* Round 4 */
-#define S41 6
-#define S42 10
-#define S43 15
-#define S44 21
- II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */
- II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */
- II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */
- II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */
- II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */
- II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */
- II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */
- II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */
- II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */
- II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */
- II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */
- II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */
- II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */
- II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */
- II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */
- II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */
-
- buf[0] += a;
- buf[1] += b;
- buf[2] += c;
- buf[3] += d;
-}
-
-#endif /* CHAP_SUPPORT || MD5_SUPPORT */
-
-#endif /* PPP_SUPPORT */
diff --git a/lwip/src/netif/ppp/md5.h b/lwip/src/netif/ppp/md5.h
deleted file mode 100644
index e129533..0000000
--- a/lwip/src/netif/ppp/md5.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- ***********************************************************************
- ** md5.h -- header file for implementation of MD5 **
- ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
- ** Created: 2/17/90 RLR **
- ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version **
- ** Revised (for MD5): RLR 4/27/91 **
- ** -- G modified to have y&~z instead of y&z **
- ** -- FF, GG, HH modified to add in last register done **
- ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 **
- ** -- distinct additive constant for each step **
- ** -- round 4 added, working mod 7 **
- ***********************************************************************
- */
-
-/*
- ***********************************************************************
- ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
- ** **
- ** License to copy and use this software is granted provided that **
- ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
- ** Digest Algorithm" in all material mentioning or referencing this **
- ** software or this function. **
- ** **
- ** License is also granted to make and use derivative works **
- ** provided that such works are identified as "derived from the RSA **
- ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
- ** material mentioning or referencing the derived work. **
- ** **
- ** RSA Data Security, Inc. makes no representations concerning **
- ** either the merchantability of this software or the suitability **
- ** of this software for any particular purpose. It is provided "as **
- ** is" without express or implied warranty of any kind. **
- ** **
- ** These notices must be retained in any copies of any part of this **
- ** documentation and/or software. **
- ***********************************************************************
- */
-
-#ifndef MD5_H
-#define MD5_H
-
-/* Data structure for MD5 (Message-Digest) computation */
-typedef struct {
- u32_t i[2]; /* number of _bits_ handled mod 2^64 */
- u32_t buf[4]; /* scratch buffer */
- unsigned char in[64]; /* input buffer */
- unsigned char digest[16]; /* actual digest after MD5Final call */
-} MD5_CTX;
-
-void MD5Init ( MD5_CTX *mdContext);
-void MD5Update( MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen);
-void MD5Final ( unsigned char hash[], MD5_CTX *mdContext);
-
-#endif /* MD5_H */
diff --git a/lwip/src/netif/ppp/mppe.c b/lwip/src/netif/ppp/mppe.c
new file mode 100644
index 0000000..a619796
--- /dev/null
+++ b/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_remove_header(np, MPPE_OVHD + sizeof(protocol));
+
+ if ((err = pbuf_copy(np, *pb)) != ERR_OK) {
+ pbuf_free(np);
+ return err;
+ }
+
+ /* Reveal MPPE header + protocol */
+ pbuf_add_header(np, 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_remove_header(np, 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_add_header(np, 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_remove_header(n0, 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/lwip/src/netif/ppp/multilink.c b/lwip/src/netif/ppp/multilink.c
new file mode 100644
index 0000000..62014e8
--- /dev/null
+++ b/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/lwip/src/netif/ppp/pap.c b/lwip/src/netif/ppp/pap.c
deleted file mode 100644
index 5fb9f88..0000000
--- a/lwip/src/netif/ppp/pap.c
+++ /dev/null
@@ -1,628 +0,0 @@
-/*****************************************************************************
-* pap.c - Network Password Authentication Protocol program file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* portions Copyright (c) 1997 by Global Election Systems Inc.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 97-12-12 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
-* Original.
-*****************************************************************************/
-/*
- * upap.c - User/Password Authentication Protocol.
- *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
-
-#include "lwip/opt.h"
-
-#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
-
-#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
-
-#include "ppp_impl.h"
-#include "pppdebug.h"
-
-#include "auth.h"
-#include "pap.h"
-
-#include <string.h>
-
-#if 0 /* UNUSED */
-static bool hide_password = 1;
-
-/*
- * Command-line options.
- */
-static option_t pap_option_list[] = {
- { "hide-password", o_bool, &hide_password,
- "Don't output passwords to log", 1 },
- { "show-password", o_bool, &hide_password,
- "Show password string in debug log messages", 0 },
- { "pap-restart", o_int, &upap[0].us_timeouttime,
- "Set retransmit timeout for PAP" },
- { "pap-max-authreq", o_int, &upap[0].us_maxtransmits,
- "Set max number of transmissions for auth-reqs" },
- { "pap-timeout", o_int, &upap[0].us_reqtimeout,
- "Set time limit for peer PAP authentication" },
- { NULL }
-};
-#endif
-
-/*
- * Protocol entry points.
- */
-static void upap_init (int);
-static void upap_lowerup (int);
-static void upap_lowerdown (int);
-static void upap_input (int, u_char *, int);
-static void upap_protrej (int);
-#if PPP_ADDITIONAL_CALLBACKS
-static int upap_printpkt (u_char *, int, void (*)(void *, char *, ...), void *);
-#endif /* PPP_ADDITIONAL_CALLBACKS */
-
-struct protent pap_protent = {
- PPP_PAP,
- upap_init,
- upap_input,
- upap_protrej,
- upap_lowerup,
- upap_lowerdown,
- NULL,
- NULL,
-#if PPP_ADDITIONAL_CALLBACKS
- upap_printpkt,
- NULL,
-#endif /* PPP_ADDITIONAL_CALLBACKS */
- 1,
- "PAP",
-#if PPP_ADDITIONAL_CALLBACKS
- NULL,
- NULL,
- NULL
-#endif /* PPP_ADDITIONAL_CALLBACKS */
-};
-
-upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */
-
-static void upap_timeout (void *);
-static void upap_reqtimeout(void *);
-static void upap_rauthreq (upap_state *, u_char *, u_char, int);
-static void upap_rauthack (upap_state *, u_char *, int, int);
-static void upap_rauthnak (upap_state *, u_char *, int, int);
-static void upap_sauthreq (upap_state *);
-static void upap_sresp (upap_state *, u_char, u_char, char *, int);
-
-
-/*
- * upap_init - Initialize a UPAP unit.
- */
-static void
-upap_init(int unit)
-{
- upap_state *u = &upap[unit];
-
- UPAPDEBUG(LOG_INFO, ("upap_init: %d\n", unit));
- u->us_unit = unit;
- u->us_user = NULL;
- u->us_userlen = 0;
- u->us_passwd = NULL;
- u->us_passwdlen = 0;
- u->us_clientstate = UPAPCS_INITIAL;
- u->us_serverstate = UPAPSS_INITIAL;
- u->us_id = 0;
- u->us_timeouttime = UPAP_DEFTIMEOUT;
- u->us_maxtransmits = 10;
- u->us_reqtimeout = UPAP_DEFREQTIME;
-}
-
-/*
- * upap_authwithpeer - Authenticate us with our peer (start client).
- *
- * Set new state and send authenticate's.
- */
-void
-upap_authwithpeer(int unit, char *user, char *password)
-{
- upap_state *u = &upap[unit];
-
- UPAPDEBUG(LOG_INFO, ("upap_authwithpeer: %d user=%s password=%s s=%d\n",
- unit, user, password, u->us_clientstate));
-
- /* Save the username and password we're given */
- u->us_user = user;
- u->us_userlen = (int)strlen(user);
- u->us_passwd = password;
- u->us_passwdlen = (int)strlen(password);
-
- u->us_transmits = 0;
-
- /* Lower layer up yet? */
- if (u->us_clientstate == UPAPCS_INITIAL ||
- u->us_clientstate == UPAPCS_PENDING) {
- u->us_clientstate = UPAPCS_PENDING;
- return;
- }
-
- upap_sauthreq(u); /* Start protocol */
-}
-
-
-/*
- * upap_authpeer - Authenticate our peer (start server).
- *
- * Set new state.
- */
-void
-upap_authpeer(int unit)
-{
- upap_state *u = &upap[unit];
-
- /* Lower layer up yet? */
- if (u->us_serverstate == UPAPSS_INITIAL ||
- u->us_serverstate == UPAPSS_PENDING) {
- u->us_serverstate = UPAPSS_PENDING;
- return;
- }
-
- u->us_serverstate = UPAPSS_LISTEN;
- if (u->us_reqtimeout > 0) {
- TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
- }
-}
-
-/*
- * upap_timeout - Retransmission timer for sending auth-reqs expired.
- */
-static void
-upap_timeout(void *arg)
-{
- upap_state *u = (upap_state *) arg;
-
- UPAPDEBUG(LOG_INFO, ("upap_timeout: %d timeout %d expired s=%d\n",
- u->us_unit, u->us_timeouttime, u->us_clientstate));
-
- if (u->us_clientstate != UPAPCS_AUTHREQ) {
- UPAPDEBUG(LOG_INFO, ("upap_timeout: not in AUTHREQ state!\n"));
- return;
- }
-
- if (u->us_transmits >= u->us_maxtransmits) {
- /* give up in disgust */
- UPAPDEBUG(LOG_ERR, ("No response to PAP authenticate-requests\n"));
- u->us_clientstate = UPAPCS_BADAUTH;
- auth_withpeer_fail(u->us_unit, PPP_PAP);
- return;
- }
-
- upap_sauthreq(u); /* Send Authenticate-Request and set upap timeout*/
-}
-
-
-/*
- * upap_reqtimeout - Give up waiting for the peer to send an auth-req.
- */
-static void
-upap_reqtimeout(void *arg)
-{
- upap_state *u = (upap_state *) arg;
-
- if (u->us_serverstate != UPAPSS_LISTEN) {
- return; /* huh?? */
- }
-
- auth_peer_fail(u->us_unit, PPP_PAP);
- u->us_serverstate = UPAPSS_BADAUTH;
-}
-
-
-/*
- * upap_lowerup - The lower layer is up.
- *
- * Start authenticating if pending.
- */
-static void
-upap_lowerup(int unit)
-{
- upap_state *u = &upap[unit];
-
- UPAPDEBUG(LOG_INFO, ("upap_lowerup: init %d clientstate s=%d\n", unit, u->us_clientstate));
-
- if (u->us_clientstate == UPAPCS_INITIAL) {
- u->us_clientstate = UPAPCS_CLOSED;
- } else if (u->us_clientstate == UPAPCS_PENDING) {
- upap_sauthreq(u); /* send an auth-request */
- /* now client state is UPAPCS__AUTHREQ */
- }
-
- if (u->us_serverstate == UPAPSS_INITIAL) {
- u->us_serverstate = UPAPSS_CLOSED;
- } else if (u->us_serverstate == UPAPSS_PENDING) {
- u->us_serverstate = UPAPSS_LISTEN;
- if (u->us_reqtimeout > 0) {
- TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
- }
- }
-}
-
-
-/*
- * upap_lowerdown - The lower layer is down.
- *
- * Cancel all timeouts.
- */
-static void
-upap_lowerdown(int unit)
-{
- upap_state *u = &upap[unit];
-
- UPAPDEBUG(LOG_INFO, ("upap_lowerdown: %d s=%d\n", unit, u->us_clientstate));
-
- if (u->us_clientstate == UPAPCS_AUTHREQ) { /* Timeout pending? */
- UNTIMEOUT(upap_timeout, u); /* Cancel timeout */
- }
- if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0) {
- UNTIMEOUT(upap_reqtimeout, u);
- }
-
- u->us_clientstate = UPAPCS_INITIAL;
- u->us_serverstate = UPAPSS_INITIAL;
-}
-
-
-/*
- * upap_protrej - Peer doesn't speak this protocol.
- *
- * This shouldn't happen. In any case, pretend lower layer went down.
- */
-static void
-upap_protrej(int unit)
-{
- upap_state *u = &upap[unit];
-
- if (u->us_clientstate == UPAPCS_AUTHREQ) {
- UPAPDEBUG(LOG_ERR, ("PAP authentication failed due to protocol-reject\n"));
- auth_withpeer_fail(unit, PPP_PAP);
- }
- if (u->us_serverstate == UPAPSS_LISTEN) {
- UPAPDEBUG(LOG_ERR, ("PAP authentication of peer failed (protocol-reject)\n"));
- auth_peer_fail(unit, PPP_PAP);
- }
- upap_lowerdown(unit);
-}
-
-
-/*
- * upap_input - Input UPAP packet.
- */
-static void
-upap_input(int unit, u_char *inpacket, int l)
-{
- upap_state *u = &upap[unit];
- u_char *inp;
- u_char code, id;
- int len;
-
- /*
- * Parse header (code, id and length).
- * If packet too short, drop it.
- */
- inp = inpacket;
- if (l < (int)UPAP_HEADERLEN) {
- UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short header.\n"));
- return;
- }
- GETCHAR(code, inp);
- GETCHAR(id, inp);
- GETSHORT(len, inp);
- if (len < (int)UPAP_HEADERLEN) {
- UPAPDEBUG(LOG_INFO, ("pap_input: rcvd illegal length.\n"));
- return;
- }
- if (len > l) {
- UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short packet.\n"));
- return;
- }
- len -= UPAP_HEADERLEN;
-
- /*
- * Action depends on code.
- */
- switch (code) {
- case UPAP_AUTHREQ:
- upap_rauthreq(u, inp, id, len);
- break;
-
- case UPAP_AUTHACK:
- upap_rauthack(u, inp, id, len);
- break;
-
- case UPAP_AUTHNAK:
- upap_rauthnak(u, inp, id, len);
- break;
-
- default: /* XXX Need code reject */
- UPAPDEBUG(LOG_INFO, ("pap_input: UNHANDLED default: code: %d, id: %d, len: %d.\n", code, id, len));
- break;
- }
-}
-
-
-/*
- * upap_rauth - Receive Authenticate.
- */
-static void
-upap_rauthreq(upap_state *u, u_char *inp, u_char id, int len)
-{
- u_char ruserlen, rpasswdlen;
- char *ruser, *rpasswd;
- u_char retcode;
- char *msg;
- int msglen;
-
- UPAPDEBUG(LOG_INFO, ("pap_rauth: Rcvd id %d.\n", id));
-
- if (u->us_serverstate < UPAPSS_LISTEN) {
- return;
- }
-
- /*
- * If we receive a duplicate authenticate-request, we are
- * supposed to return the same status as for the first request.
- */
- if (u->us_serverstate == UPAPSS_OPEN) {
- upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */
- return;
- }
- if (u->us_serverstate == UPAPSS_BADAUTH) {
- upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */
- return;
- }
-
- /*
- * Parse user/passwd.
- */
- if (len < (int)sizeof (u_char)) {
- UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n"));
- return;
- }
- GETCHAR(ruserlen, inp);
- len -= sizeof (u_char) + ruserlen + sizeof (u_char);
- if (len < 0) {
- UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n"));
- return;
- }
- ruser = (char *) inp;
- INCPTR(ruserlen, inp);
- GETCHAR(rpasswdlen, inp);
- if (len < rpasswdlen) {
- UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n"));
- return;
- }
- rpasswd = (char *) inp;
-
- /*
- * Check the username and password given.
- */
- retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen);
- /* lwip: currently retcode is always UPAP_AUTHACK */
- BZERO(rpasswd, rpasswdlen);
-
- upap_sresp(u, retcode, id, msg, msglen);
-
- if (retcode == UPAP_AUTHACK) {
- u->us_serverstate = UPAPSS_OPEN;
- auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen);
- } else {
- u->us_serverstate = UPAPSS_BADAUTH;
- auth_peer_fail(u->us_unit, PPP_PAP);
- }
-
- if (u->us_reqtimeout > 0) {
- UNTIMEOUT(upap_reqtimeout, u);
- }
-}
-
-
-/*
- * upap_rauthack - Receive Authenticate-Ack.
- */
-static void
-upap_rauthack(upap_state *u, u_char *inp, int id, int len)
-{
- u_char msglen;
- char *msg;
-
- LWIP_UNUSED_ARG(id);
-
- UPAPDEBUG(LOG_INFO, ("pap_rauthack: Rcvd id %d s=%d\n", id, u->us_clientstate));
-
- if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */
- UPAPDEBUG(LOG_INFO, ("pap_rauthack: us_clientstate != UPAPCS_AUTHREQ\n"));
- return;
- }
-
- /*
- * Parse message.
- */
- if (len < (int)sizeof (u_char)) {
- UPAPDEBUG(LOG_INFO, ("pap_rauthack: ignoring missing msg-length.\n"));
- } else {
- GETCHAR(msglen, inp);
- if (msglen > 0) {
- len -= sizeof (u_char);
- if (len < msglen) {
- UPAPDEBUG(LOG_INFO, ("pap_rauthack: rcvd short packet.\n"));
- return;
- }
- msg = (char *) inp;
- PRINTMSG(msg, msglen);
- }
- }
- UNTIMEOUT(upap_timeout, u); /* Cancel timeout */
- u->us_clientstate = UPAPCS_OPEN;
-
- auth_withpeer_success(u->us_unit, PPP_PAP);
-}
-
-
-/*
- * upap_rauthnak - Receive Authenticate-Nak.
- */
-static void
-upap_rauthnak(upap_state *u, u_char *inp, int id, int len)
-{
- u_char msglen;
- char *msg;
-
- LWIP_UNUSED_ARG(id);
-
- UPAPDEBUG(LOG_INFO, ("pap_rauthnak: Rcvd id %d s=%d\n", id, u->us_clientstate));
-
- if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */
- return;
- }
-
- /*
- * Parse message.
- */
- if (len < sizeof (u_char)) {
- UPAPDEBUG(LOG_INFO, ("pap_rauthnak: ignoring missing msg-length.\n"));
- } else {
- GETCHAR(msglen, inp);
- if(msglen > 0) {
- len -= sizeof (u_char);
- if (len < msglen) {
- UPAPDEBUG(LOG_INFO, ("pap_rauthnak: rcvd short packet.\n"));
- return;
- }
- msg = (char *) inp;
- PRINTMSG(msg, msglen);
- }
- }
-
- u->us_clientstate = UPAPCS_BADAUTH;
-
- UPAPDEBUG(LOG_ERR, ("PAP authentication failed\n"));
- auth_withpeer_fail(u->us_unit, PPP_PAP);
-}
-
-
-/*
- * upap_sauthreq - Send an Authenticate-Request.
- */
-static void
-upap_sauthreq(upap_state *u)
-{
- u_char *outp;
- int outlen;
-
- outlen = UPAP_HEADERLEN + 2 * sizeof (u_char)
- + u->us_userlen + u->us_passwdlen;
- outp = outpacket_buf[u->us_unit];
-
- MAKEHEADER(outp, PPP_PAP);
-
- PUTCHAR(UPAP_AUTHREQ, outp);
- PUTCHAR(++u->us_id, outp);
- PUTSHORT(outlen, outp);
- PUTCHAR(u->us_userlen, outp);
- BCOPY(u->us_user, outp, u->us_userlen);
- INCPTR(u->us_userlen, outp);
- PUTCHAR(u->us_passwdlen, outp);
- BCOPY(u->us_passwd, outp, u->us_passwdlen);
-
- pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN);
-
- UPAPDEBUG(LOG_INFO, ("pap_sauth: Sent id %d\n", u->us_id));
-
- TIMEOUT(upap_timeout, u, u->us_timeouttime);
- ++u->us_transmits;
- u->us_clientstate = UPAPCS_AUTHREQ;
-}
-
-
-/*
- * upap_sresp - Send a response (ack or nak).
- */
-static void
-upap_sresp(upap_state *u, u_char code, u_char id, char *msg, int msglen)
-{
- u_char *outp;
- int outlen;
-
- outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen;
- outp = outpacket_buf[u->us_unit];
- MAKEHEADER(outp, PPP_PAP);
-
- PUTCHAR(code, outp);
- PUTCHAR(id, outp);
- PUTSHORT(outlen, outp);
- PUTCHAR(msglen, outp);
- BCOPY(msg, outp, msglen);
- pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN);
-
- UPAPDEBUG(LOG_INFO, ("pap_sresp: Sent code %d, id %d s=%d\n", code, id, u->us_clientstate));
-}
-
-#if PPP_ADDITIONAL_CALLBACKS
-static char *upap_codenames[] = {
- "AuthReq", "AuthAck", "AuthNak"
-};
-
-/*
- * upap_printpkt - print the contents of a PAP packet.
- */
-static int upap_printpkt(
- u_char *p,
- int plen,
- void (*printer) (void *, char *, ...),
- void *arg
-)
-{
- LWIP_UNUSED_ARG(p);
- LWIP_UNUSED_ARG(plen);
- LWIP_UNUSED_ARG(printer);
- LWIP_UNUSED_ARG(arg);
- return 0;
-}
-#endif /* PPP_ADDITIONAL_CALLBACKS */
-
-#endif /* PAP_SUPPORT */
-
-#endif /* PPP_SUPPORT */
diff --git a/lwip/src/netif/ppp/pap.h b/lwip/src/netif/ppp/pap.h
deleted file mode 100644
index c99a204..0000000
--- a/lwip/src/netif/ppp/pap.h
+++ /dev/null
@@ -1,118 +0,0 @@
-/*****************************************************************************
-* pap.h - PPP Password Authentication Protocol header file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* portions Copyright (c) 1997 Global Election Systems Inc.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 97-12-04 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
-* Original derived from BSD codes.
-*****************************************************************************/
-/*
- * upap.h - User/Password Authentication Protocol definitions.
- *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- */
-
-#ifndef PAP_H
-#define PAP_H
-
-#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
-
-/*
- * Packet header = Code, id, length.
- */
-#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
-
-
-/*
- * UPAP codes.
- */
-#define UPAP_AUTHREQ 1 /* Authenticate-Request */
-#define UPAP_AUTHACK 2 /* Authenticate-Ack */
-#define UPAP_AUTHNAK 3 /* Authenticate-Nak */
-
-/*
- * Each interface is described by upap structure.
- */
-typedef struct upap_state {
- int us_unit; /* Interface unit number */
- const char *us_user; /* User */
- int us_userlen; /* User length */
- const char *us_passwd; /* Password */
- int us_passwdlen; /* Password length */
- int us_clientstate; /* Client state */
- int us_serverstate; /* Server state */
- u_char us_id; /* Current id */
- int us_timeouttime; /* Timeout (seconds) for auth-req retrans. */
- int us_transmits; /* Number of auth-reqs sent */
- int us_maxtransmits; /* Maximum number of auth-reqs to send */
- int us_reqtimeout; /* Time to wait for auth-req from peer */
-} upap_state;
-
-/*
- * Client states.
- */
-#define UPAPCS_INITIAL 0 /* Connection down */
-#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */
-#define UPAPCS_PENDING 2 /* Connection down, have requested auth */
-#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */
-#define UPAPCS_OPEN 4 /* We've received an Ack */
-#define UPAPCS_BADAUTH 5 /* We've received a Nak */
-
-/*
- * Server states.
- */
-#define UPAPSS_INITIAL 0 /* Connection down */
-#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */
-#define UPAPSS_PENDING 2 /* Connection down, have requested auth */
-#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */
-#define UPAPSS_OPEN 4 /* We've sent an Ack */
-#define UPAPSS_BADAUTH 5 /* We've sent a Nak */
-
-
-extern upap_state upap[];
-
-void upap_authwithpeer (int, char *, char *);
-void upap_authpeer (int);
-
-extern struct protent pap_protent;
-
-#endif /* PAP_SUPPORT */
-
-#endif /* PAP_H */
diff --git a/lwip/src/netif/ppp/polarssl/README b/lwip/src/netif/ppp/polarssl/README
new file mode 100644
index 0000000..3fdf159
--- /dev/null
+++ b/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/lwip/src/netif/ppp/polarssl/arc4.c b/lwip/src/netif/ppp/polarssl/arc4.c
new file mode 100644
index 0000000..6e17ec4
--- /dev/null
+++ b/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/lwip/src/netif/ppp/polarssl/des.c b/lwip/src/netif/ppp/polarssl/des.c
new file mode 100644
index 0000000..9a89d00
--- /dev/null
+++ b/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/lwip/src/netif/ppp/polarssl/md4.c b/lwip/src/netif/ppp/polarssl/md4.c
new file mode 100644
index 0000000..b1701a0
--- /dev/null
+++ b/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/lwip/src/netif/ppp/polarssl/md5.c b/lwip/src/netif/ppp/polarssl/md5.c
new file mode 100644
index 0000000..1ec4d81
--- /dev/null
+++ b/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/lwip/src/netif/ppp/polarssl/sha1.c b/lwip/src/netif/ppp/polarssl/sha1.c
new file mode 100644
index 0000000..c2192ea
--- /dev/null
+++ b/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/lwip/src/netif/ppp/ppp.c b/lwip/src/netif/ppp/ppp.c
index 2a34657..66470bc 100644
--- a/lwip/src/netif/ppp/ppp.c
+++ b/lwip/src/netif/ppp/ppp.c
@@ -7,13 +7,13 @@
* 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
+* 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.
+* 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,
@@ -79,1183 +79,1110 @@
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
*/
-#include "lwip/opt.h"
+/**
+ * @defgroup ppp PPP
+ * @ingroup netifs
+ * @verbinclude "ppp.txt"
+ */
+#include "netif/ppp/ppp_opts.h"
#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
-#include "ppp_impl.h"
-#include "lwip/ip.h" /* for ip_input() */
+#include "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 "pppdebug.h"
+#include "netif/ppp/fsm.h"
+#include "netif/ppp/lcp.h"
+#include "netif/ppp/magic.h"
-#include "randm.h"
-#include "fsm.h"
#if PAP_SUPPORT
-#include "pap.h"
+#include "netif/ppp/upap.h"
#endif /* PAP_SUPPORT */
#if CHAP_SUPPORT
-#include "chap.h"
+#include "netif/ppp/chap-new.h"
#endif /* CHAP_SUPPORT */
-#include "ipcp.h"
-#include "lcp.h"
-#include "magic.h"
-#include "auth.h"
+#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 "vj.h"
+#include "netif/ppp/vj.h"
#endif /* VJ_SUPPORT */
-#if PPPOE_SUPPORT
-#include "netif/ppp_oe.h"
-#endif /* PPPOE_SUPPORT */
-
-#include "lwip/tcpip.h"
-#include "lwip/api.h"
-#include "lwip/snmp.h"
-
-#include <string.h>
+#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 ***/
/*************************/
-/** PPP_INPROC_MULTITHREADED==1 call pppInput using tcpip_callback().
- * Set this to 0 if pppInProc is called inside tcpip_thread or with NO_SYS==1.
- * Default is 1 for NO_SYS==0 (multithreaded) and 0 for NO_SYS==1 (single-threaded).
- */
-#ifndef PPP_INPROC_MULTITHREADED
-#define PPP_INPROC_MULTITHREADED (NO_SYS==0)
+/* Memory pools */
+#if PPPOS_SUPPORT
+LWIP_MEMPOOL_PROTOTYPE(PPPOS_PCB);
#endif
-
-/** PPP_INPROC_OWNTHREAD==1: start a dedicated RX thread per PPP session.
- * Default is 0: call pppos_input() for received raw characters, charcater
- * reception is up to the port */
-#ifndef PPP_INPROC_OWNTHREAD
-#define PPP_INPROC_OWNTHREAD PPP_INPROC_MULTITHREADED
+#if PPPOE_SUPPORT
+LWIP_MEMPOOL_PROTOTYPE(PPPOE_IF);
#endif
-
-#if PPP_INPROC_OWNTHREAD && !PPP_INPROC_MULTITHREADED
- #error "PPP_INPROC_OWNTHREAD needs PPP_INPROC_MULTITHREADED==1"
+#if PPPOL2TP_SUPPORT
+LWIP_MEMPOOL_PROTOTYPE(PPPOL2TP_PCB);
#endif
-
-/*
- * The basic PPP frame.
- */
-#define PPP_ADDRESS(p) (((u_char *)(p))[0])
-#define PPP_CONTROL(p) (((u_char *)(p))[1])
-#define PPP_PROTOCOL(p) ((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3])
-
-/* PPP packet parser states. Current state indicates operation yet to be
- * completed. */
-typedef enum {
- PDIDLE = 0, /* Idle state - waiting. */
- PDSTART, /* Process start flag. */
- PDADDRESS, /* Process address field. */
- PDCONTROL, /* Process control field. */
- PDPROTOCOL1, /* Process protocol field 1. */
- PDPROTOCOL2, /* Process protocol field 2. */
- PDDATA /* Process data byte. */
-} PPPDevStates;
-
-#define ESCAPE_P(accm, c) ((accm)[(c) >> 3] & pppACCMMask[c & 0x07])
-
-/************************/
-/*** LOCAL DATA TYPES ***/
-/************************/
-
-/** RX buffer size: this may be configured smaller! */
-#ifndef PPPOS_RX_BUFSIZE
-#define PPPOS_RX_BUFSIZE (PPP_MRU + PPP_HDRLEN)
+#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")
-typedef struct PPPControlRx_s {
- /** unit number / ppp descriptor */
- int pd;
- /** the rx file descriptor */
- sio_fd_t fd;
- /** receive buffer - encoded data is stored here */
-#if PPP_INPROC_OWNTHREAD
- u_char rxbuf[PPPOS_RX_BUFSIZE];
-#endif /* PPP_INPROC_OWNTHREAD */
-
- /* The input packet. */
- struct pbuf *inHead, *inTail;
-
-#if PPPOS_SUPPORT
- u16_t inProtocol; /* The input protocol code. */
- u16_t inFCS; /* Input Frame Check Sequence value. */
-#endif /* PPPOS_SUPPORT */
- PPPDevStates inState; /* The input process state. */
- char inEscaped; /* Escape next character. */
- ext_accm inACCM; /* Async-Ctl-Char-Map for input. */
-} PPPControlRx;
-
-/*
- * PPP interface control block.
- */
-typedef struct PPPControl_s {
- PPPControlRx rx;
- char openFlag; /* True when in use. */
-#if PPPOE_SUPPORT
- struct netif *ethif;
- struct pppoe_softc *pppoe_sc;
-#endif /* PPPOE_SUPPORT */
- int if_up; /* True when the interface is up. */
- int errCode; /* Code indicating why interface is down. */
-#if PPPOS_SUPPORT
- sio_fd_t fd; /* File device ID of port. */
-#endif /* PPPOS_SUPPORT */
- u16_t mtu; /* Peer's mru */
- int pcomp; /* Does peer accept protocol compression? */
- int accomp; /* Does peer accept addr/ctl compression? */
- u_long lastXMit; /* Time of last transmission. */
- ext_accm outACCM; /* Async-Ctl-Char-Map for output. */
-#if PPPOS_SUPPORT && VJ_SUPPORT
- int vjEnabled; /* Flag indicating VJ compression enabled. */
- struct vjcompress vjComp; /* Van Jacobson compression header. */
-#endif /* PPPOS_SUPPORT && VJ_SUPPORT */
-
- struct netif netif;
-
- struct ppp_addrs addrs;
-
- void (*linkStatusCB)(void *ctx, int errCode, void *arg);
- void *linkStatusCtx;
-
-} PPPControl;
-
-
-/*
- * Ioctl definitions.
- */
-
-struct npioctl {
- int protocol; /* PPP procotol, e.g. PPP_IP */
- enum NPmode mode;
-};
-
-
-
-/***********************************/
-/*** LOCAL FUNCTION DECLARATIONS ***/
-/***********************************/
-#if PPPOS_SUPPORT
-#if PPP_INPROC_OWNTHREAD
-static void pppInputThread(void *arg);
-#endif /* PPP_INPROC_OWNTHREAD */
-static void pppDrop(PPPControlRx *pcrx);
-static void pppInProc(PPPControlRx *pcrx, u_char *s, int l);
-static void pppFreeCurrentInputPacket(PPPControlRx *pcrx);
-#endif /* PPPOS_SUPPORT */
-
-
-/******************************/
-/*** PUBLIC DATA STRUCTURES ***/
-/******************************/
-static PPPControl pppControl[NUM_PPP]; /* The PPP interface control blocks. */
+/* 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.
*/
-struct protent *ppp_protocols[] = {
- &lcp_protent,
+const struct protent* const protocols[] = {
+ &lcp_protent,
#if PAP_SUPPORT
- &pap_protent,
+ &pap_protent,
#endif /* PAP_SUPPORT */
#if CHAP_SUPPORT
- &chap_protent,
+ &chap_protent,
#endif /* CHAP_SUPPORT */
#if CBCP_SUPPORT
- &cbcp_protent,
+ &cbcp_protent,
#endif /* CBCP_SUPPORT */
- &ipcp_protent,
+#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,
+ &ccp_protent,
#endif /* CCP_SUPPORT */
- NULL
+#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);
-/*
- * Buffers for outgoing packets. This must be accessed only from the appropriate
- * PPP task so that it doesn't need to be protected to avoid collisions.
- */
-u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN];
-
-
-/*****************************/
-/*** LOCAL DATA STRUCTURES ***/
-/*****************************/
-
-#if PPPOS_SUPPORT
-/*
- * FCS lookup table as calculated by genfcstab.
- * @todo: smaller, slower implementation for lower memory footprint?
- */
-static const u_short fcstab[256] = {
- 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
- 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
- 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
- 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
- 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
- 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
- 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
- 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
- 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
- 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
- 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
- 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
- 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
- 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
- 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
- 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
- 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
- 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
- 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
- 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
- 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
- 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
- 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
- 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
- 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
- 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
- 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
- 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
- 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
- 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
- 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
- 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
-};
-
-/* PPP's Asynchronous-Control-Character-Map. The mask array is used
- * to select the specific bit for a character. */
-static u_char pppACCMMask[] = {
- 0x01,
- 0x02,
- 0x04,
- 0x08,
- 0x10,
- 0x20,
- 0x40,
- 0x80
-};
-
-#if PPP_INPROC_OWNTHREAD
-/** Wake up the task blocked in reading from serial line (if any) */
-static void
-pppRecvWakeup(int pd)
-{
- PPPDEBUG(LOG_DEBUG, ("pppRecvWakeup: unit %d\n", pd));
- if (pppControl[pd].openFlag != 0) {
- sio_read_abort(pppControl[pd].fd);
- }
-}
-#endif /* PPP_INPROC_OWNTHREAD */
-#endif /* PPPOS_SUPPORT */
-
-void
-pppLinkTerminated(int pd)
-{
- PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: unit %d\n", pd));
-
-#if PPPOE_SUPPORT
- if (pppControl[pd].ethif) {
- pppoe_disconnect(pppControl[pd].pppoe_sc);
- } else
-#endif /* PPPOE_SUPPORT */
- {
-#if PPPOS_SUPPORT
- PPPControl* pc;
-#if PPP_INPROC_OWNTHREAD
- pppRecvWakeup(pd);
-#endif /* PPP_INPROC_OWNTHREAD */
- pc = &pppControl[pd];
-
- PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode));
- if (pc->linkStatusCB) {
- pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL);
- }
-
- pc->openFlag = 0;/**/
-#endif /* PPPOS_SUPPORT */
- }
- PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: finished.\n"));
+/***********************************/
+/*** 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;
}
-
-void
-pppLinkDown(int pd)
-{
- PPPDEBUG(LOG_DEBUG, ("pppLinkDown: unit %d\n", pd));
-
-#if PPPOE_SUPPORT
- if (pppControl[pd].ethif) {
- pppoe_disconnect(pppControl[pd].pppoe_sc);
- } else
-#endif /* PPPOE_SUPPORT */
- {
-#if PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD
- pppRecvWakeup(pd);
-#endif /* PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD*/
+#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;
}
-}
-/** Initiate LCP open request */
-static void
-pppStart(int pd)
-{
- PPPDEBUG(LOG_DEBUG, ("pppStart: unit %d\n", pd));
- lcp_lowerup(pd);
- lcp_open(pd); /* Start protocol */
- PPPDEBUG(LOG_DEBUG, ("pppStart: finished\n"));
+ 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 */
-/** LCP close request */
-static void
-pppStop(int pd)
-{
- PPPDEBUG(LOG_DEBUG, ("pppStop: unit %d\n", pd));
- lcp_close(pd, "User request");
+#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 */
-/** Called when carrier/link is lost */
-static void
-pppHup(int pd)
-{
- PPPDEBUG(LOG_DEBUG, ("pppHupCB: unit %d\n", pd));
- lcp_lowerdown(pd);
- link_terminated(pd);
-}
-
-/***********************************/
-/*** PUBLIC FUNCTION DEFINITIONS ***/
-/***********************************/
-/* Initialize the PPP subsystem. */
-
-struct ppp_settings ppp_settings;
-
-void
-pppInit(void)
-{
- struct protent *protp;
- int i, j;
-
- memset(&ppp_settings, 0, sizeof(ppp_settings));
- ppp_settings.usepeerdns = 1;
- pppSetAuth(PPPAUTHTYPE_NONE, NULL, NULL);
-
- magicInit();
-
- for (i = 0; i < NUM_PPP; i++) {
- /* Initialize each protocol to the standard option set. */
- for (j = 0; (protp = ppp_protocols[j]) != NULL; ++j) {
- (*protp->init)(i);
- }
+/*
+ * 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;
}
-}
-
-void
-pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd)
-{
- switch(authType) {
- case PPPAUTHTYPE_NONE:
- default:
-#ifdef LWIP_PPP_STRICT_PAP_REJECT
- ppp_settings.refuse_pap = 1;
-#else /* LWIP_PPP_STRICT_PAP_REJECT */
- /* some providers request pap and accept an empty login/pw */
- ppp_settings.refuse_pap = 0;
-#endif /* LWIP_PPP_STRICT_PAP_REJECT */
- ppp_settings.refuse_chap = 1;
- break;
- case PPPAUTHTYPE_ANY:
- /* Warning: Using PPPAUTHTYPE_ANY might have security consequences.
- * RFC 1994 says:
- *
- * In practice, within or associated with each PPP server, there is a
- * database which associates "user" names with authentication
- * information ("secrets"). It is not anticipated that a particular
- * named user would be authenticated by multiple methods. This would
- * make the user vulnerable to attacks which negotiate the least secure
- * method from among a set (such as PAP rather than CHAP). If the same
- * secret was used, PAP would reveal the secret to be used later with
- * CHAP.
- *
- * Instead, for each user name there should be an indication of exactly
- * one method used to authenticate that user name. If a user needs to
- * make use of different authentication methods under different
- * circumstances, then distinct user names SHOULD be employed, each of
- * which identifies exactly one authentication method.
- *
- */
- ppp_settings.refuse_pap = 0;
- ppp_settings.refuse_chap = 0;
- break;
-
- case PPPAUTHTYPE_PAP:
- ppp_settings.refuse_pap = 0;
- ppp_settings.refuse_chap = 1;
- break;
-
- case PPPAUTHTYPE_CHAP:
- ppp_settings.refuse_pap = 1;
- ppp_settings.refuse_chap = 0;
- break;
- }
+ PPPDEBUG(LOG_DEBUG, ("ppp_connect[%d]: holdoff=%d\n", pcb->netif->num, holdoff));
- if(user) {
- strncpy(ppp_settings.user, user, sizeof(ppp_settings.user)-1);
- ppp_settings.user[sizeof(ppp_settings.user)-1] = '\0';
- } else {
- ppp_settings.user[0] = '\0';
+ if (holdoff == 0) {
+ ppp_do_connect(pcb);
+ return ERR_OK;
}
- if(passwd) {
- strncpy(ppp_settings.passwd, passwd, sizeof(ppp_settings.passwd)-1);
- ppp_settings.passwd[sizeof(ppp_settings.passwd)-1] = '\0';
- } else {
- ppp_settings.passwd[0] = '\0';
- }
+ new_phase(pcb, PPP_PHASE_HOLDOFF);
+ sys_timeout((u32_t)(holdoff*1000), ppp_do_connect, pcb);
+ return ERR_OK;
}
-#if PPPOS_SUPPORT
-/** Open a new PPP connection using the given I/O device.
- * This initializes the PPP control block but does not
- * attempt to negotiate the LCP session. If this port
- * connects to a modem, the modem connection must be
- * established before calling this.
- * Return a new PPP connection descriptor on success or
- * an error code (negative) on failure.
+#if PPP_SERVER
+/*
+ * Listen for an incoming PPP connection.
*
- * pppOpen() is directly defined to this function.
+ * 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.
*/
-int
-pppOverSerialOpen(sio_fd_t fd, pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx)
-{
- PPPControl *pc;
- int pd;
-
- if (linkStatusCB == NULL) {
- /* PPP is single-threaded: without a callback,
- * there is no way to know when the link is up. */
- return PPPERR_PARAM;
+err_t ppp_listen(ppp_pcb *pcb) {
+ if (pcb->phase != PPP_PHASE_DEAD) {
+ return ERR_ALREADY;
}
- /* Find a free PPP session descriptor. */
- for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++);
-
- if (pd >= NUM_PPP) {
- pd = PPPERR_OPEN;
- } else {
- pc = &pppControl[pd];
- /* input pbuf left over from last session? */
- pppFreeCurrentInputPacket(&pc->rx);
- /* @todo: is this correct or do I overwrite something? */
- memset(pc, 0, sizeof(PPPControl));
- pc->rx.pd = pd;
- pc->rx.fd = fd;
-
- pc->openFlag = 1;
- pc->fd = fd;
+ PPPDEBUG(LOG_DEBUG, ("ppp_listen[%d]\n", pcb->netif->num));
-#if VJ_SUPPORT
- vj_compress_init(&pc->vjComp);
-#endif /* VJ_SUPPORT */
-
- /*
- * Default the in and out accm so that escape and flag characters
- * are always escaped.
- */
- pc->rx.inACCM[15] = 0x60; /* no need to protect since RX is not running */
- pc->outACCM[15] = 0x60;
-
- pc->linkStatusCB = linkStatusCB;
- pc->linkStatusCtx = linkStatusCtx;
-
- /*
- * Start the connection and handle incoming events (packet or timeout).
- */
- PPPDEBUG(LOG_INFO, ("pppOverSerialOpen: unit %d: Connecting\n", pd));
- pppStart(pd);
-#if PPP_INPROC_OWNTHREAD
- sys_thread_new(PPP_THREAD_NAME, pppInputThread, (void*)&pc->rx, PPP_THREAD_STACKSIZE, PPP_THREAD_PRIO);
-#endif /* PPP_INPROC_OWNTHREAD */
+ if (pcb->link_cb->listen) {
+ new_phase(pcb, PPP_PHASE_INITIALIZE);
+ pcb->link_cb->listen(pcb, pcb->link_ctx_cb);
+ return ERR_OK;
}
-
- return pd;
+ return ERR_IF;
}
-#endif /* PPPOS_SUPPORT */
-
-#if PPPOE_SUPPORT
-static void pppOverEthernetLinkStatusCB(int pd, int up);
+#endif /* PPP_SERVER */
-void
-pppOverEthernetClose(int pd)
+/*
+ * 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)
{
- PPPControl* pc = &pppControl[pd];
+ pcb->err_code = PPPERR_USER;
- /* *TJL* There's no lcp_deinit */
- lcp_close(pd, NULL);
-
- pppoe_destroy(&pc->netif);
-}
-
-int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name,
- pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx)
-{
- PPPControl *pc;
- int pd;
+ /* holdoff phase, cancel the reconnection */
+ if (pcb->phase == PPP_PHASE_HOLDOFF) {
+ sys_untimeout(ppp_do_connect, pcb);
+ new_phase(pcb, PPP_PHASE_DEAD);
+ }
- LWIP_UNUSED_ARG(service_name);
- LWIP_UNUSED_ARG(concentrator_name);
+ /* 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;
+ }
- if (linkStatusCB == NULL) {
- /* PPP is single-threaded: without a callback,
- * there is no way to know when the link is up. */
- return PPPERR_PARAM;
+ /* Already terminating, nothing to do */
+ if (pcb->phase >= PPP_PHASE_TERMINATE) {
+ return ERR_INPROGRESS;
}
- /* Find a free PPP session descriptor. Critical region? */
- for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++);
- if (pd >= NUM_PPP) {
- pd = PPPERR_OPEN;
- } else {
- pc = &pppControl[pd];
- memset(pc, 0, sizeof(PPPControl));
- pc->openFlag = 1;
- pc->ethif = ethif;
-
- pc->linkStatusCB = linkStatusCB;
- pc->linkStatusCtx = linkStatusCtx;
-
- lcp_wantoptions[pd].mru = PPPOE_MAXMTU;
- lcp_wantoptions[pd].neg_asyncmap = 0;
- lcp_wantoptions[pd].neg_pcompression = 0;
- lcp_wantoptions[pd].neg_accompression = 0;
-
- lcp_allowoptions[pd].mru = PPPOE_MAXMTU;
- lcp_allowoptions[pd].neg_asyncmap = 0;
- lcp_allowoptions[pd].neg_pcompression = 0;
- lcp_allowoptions[pd].neg_accompression = 0;
-
- if(pppoe_create(ethif, pd, pppOverEthernetLinkStatusCB, &pc->pppoe_sc) != ERR_OK) {
- pc->openFlag = 0;
- return PPPERR_OPEN;
- }
+ /* 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;
+ }
- pppoe_connect(pc->pppoe_sc);
+ /*
+ * 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;
}
- return pd;
+ /* 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;
}
-#endif /* PPPOE_SUPPORT */
-
-/* Close a PPP connection and release the descriptor.
- * Any outstanding packets in the queues are dropped.
- * Return 0 on success, an error code on failure. */
-int
-pppClose(int pd)
-{
- PPPControl *pc = &pppControl[pd];
- int st = 0;
+/*
+ * 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, ("pppClose() called\n"));
+ PPPDEBUG(LOG_DEBUG, ("ppp_free[%d]\n", pcb->netif->num));
- /* Disconnect */
-#if PPPOE_SUPPORT
- if(pc->ethif) {
- PPPDEBUG(LOG_DEBUG, ("pppClose: unit %d kill_link -> pppStop\n", pd));
- pc->errCode = PPPERR_USER;
- /* This will leave us at PHASE_DEAD. */
- pppStop(pd);
- } else
-#endif /* PPPOE_SUPPORT */
- {
-#if PPPOS_SUPPORT
- PPPDEBUG(LOG_DEBUG, ("pppClose: unit %d kill_link -> pppStop\n", pd));
- pc->errCode = PPPERR_USER;
- /* This will leave us at PHASE_DEAD. */
- pppStop(pd);
-#if PPP_INPROC_OWNTHREAD
- pppRecvWakeup(pd);
-#endif /* PPP_INPROC_OWNTHREAD */
-#endif /* PPPOS_SUPPORT */
- }
+ netif_remove(pcb->netif);
- return st;
-}
+ err = pcb->link_cb->free(pcb, pcb->link_ctx_cb);
-/* This function is called when carrier is lost on the PPP channel. */
-void
-pppSigHUP(int pd)
-{
- PPPDEBUG(LOG_DEBUG, ("pppSigHUP: unit %d sig_hup -> pppHupCB\n", pd));
- pppHup(pd);
+ LWIP_MEMPOOL_FREE(PPP_PCB, pcb);
+ return err;
}
-#if PPPOS_SUPPORT
-static void
-nPut(PPPControl *pc, struct pbuf *nb)
+/* 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)
{
- struct pbuf *b;
- int c;
-
- for(b = nb; b != NULL; b = b->next) {
- if((c = sio_write(pc->fd, b->payload, b->len)) != b->len) {
- PPPDEBUG(LOG_WARNING,
- ("PPP nPut: incomplete sio_write(fd:%"SZT_F", len:%d, c: 0x%"X8_F") c = %d\n", (size_t)pc->fd, b->len, c, c));
- LINK_STATS_INC(link.err);
- pc->lastXMit = 0; /* prepend PPP_FLAG to next packet */
- snmp_inc_ifoutdiscards(&pc->netif);
- pbuf_free(nb);
- return;
- }
+ if (pcb == NULL) {
+ return ERR_VAL;
}
- snmp_add_ifoutoctets(&pc->netif, nb->tot_len);
- snmp_inc_ifoutucastpkts(&pc->netif);
- pbuf_free(nb);
- LINK_STATS_INC(link.xmit);
-}
+ 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;
-/*
- * pppAppend - append given character to end of given pbuf. If outACCM
- * is not NULL and the character needs to be escaped, do so.
- * If pbuf is full, append another.
- * Return the current pbuf.
- */
-static struct pbuf *
-pppAppend(u_char c, struct pbuf *nb, ext_accm *outACCM)
-{
- struct pbuf *tb = nb;
-
- /* Make sure there is room for the character and an escape code.
- * Sure we don't quite fill the buffer if the character doesn't
- * get escaped but is one character worth complicating this? */
- /* Note: We assume no packet header. */
- if (nb && (PBUF_POOL_BUFSIZE - nb->len) < 2) {
- tb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
- if (tb) {
- nb->next = tb;
- } else {
- LINK_STATS_INC(link.memerr);
- }
- nb = tb;
- }
+ case PPPCTLG_ERRCODE: /* Get the PPP error code. */
+ if (!arg) {
+ goto fail;
+ }
+ *(int *)arg = (int)(pcb->err_code);
+ return ERR_OK;
- if (nb) {
- if (outACCM && ESCAPE_P(*outACCM, c)) {
- *((u_char*)nb->payload + nb->len++) = PPP_ESCAPE;
- *((u_char*)nb->payload + nb->len++) = c ^ PPP_TRANS;
- } else {
- *((u_char*)nb->payload + nb->len++) = c;
- }
+ default:
+ goto fail;
}
- return tb;
+fail:
+ return ERR_VAL;
}
-#endif /* PPPOS_SUPPORT */
-#if PPPOE_SUPPORT
-static err_t
-pppifOutputOverEthernet(int pd, struct pbuf *p)
-{
- PPPControl *pc = &pppControl[pd];
- struct pbuf *pb;
- u_short protocol = PPP_IP;
- int i=0;
- u16_t tot_len;
-
- /* @todo: try to use pbuf_header() here! */
- pb = pbuf_alloc(PBUF_LINK, PPPOE_HDRLEN + sizeof(protocol), PBUF_RAM);
- if(!pb) {
- LINK_STATS_INC(link.memerr);
- LINK_STATS_INC(link.proterr);
- snmp_inc_ifoutdiscards(&pc->netif);
- return ERR_MEM;
- }
-
- pbuf_header(pb, -(s16_t)PPPOE_HDRLEN);
- pc->lastXMit = sys_jiffies();
+/**********************************/
+/*** LOCAL FUNCTION DEFINITIONS ***/
+/**********************************/
- if (!pc->pcomp || protocol > 0xFF) {
- *((u_char*)pb->payload + i++) = (protocol >> 8) & 0xFF;
- }
- *((u_char*)pb->payload + i) = protocol & 0xFF;
+static void ppp_do_connect(void *arg) {
+ ppp_pcb *pcb = (ppp_pcb*)arg;
- pbuf_chain(pb, p);
- tot_len = pb->tot_len;
+ LWIP_ASSERT("pcb->phase == PPP_PHASE_DEAD || pcb->phase == PPP_PHASE_HOLDOFF", pcb->phase == PPP_PHASE_DEAD || pcb->phase == PPP_PHASE_HOLDOFF);
- if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) {
- LINK_STATS_INC(link.err);
- snmp_inc_ifoutdiscards(&pc->netif);
- return PPPERR_DEVICE;
- }
+ new_phase(pcb, PPP_PHASE_INITIALIZE);
+ pcb->link_cb->connect(pcb, pcb->link_ctx_cb);
+}
- snmp_add_ifoutoctets(&pc->netif, tot_len);
- snmp_inc_ifoutucastpkts(&pc->netif);
- LINK_STATS_INC(link.xmit);
+/*
+ * 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;
}
-#endif /* PPPOE_SUPPORT */
-/* Send a packet on the given connection. */
-static err_t
-pppifOutput(struct netif *netif, struct pbuf *pb, ip_addr_t *ipaddr)
-{
- int pd = (int)(size_t)netif->state;
- PPPControl *pc = &pppControl[pd];
-#if PPPOS_SUPPORT
- u_short protocol = PPP_IP;
- u_int fcsOut = PPP_INITFCS;
- struct pbuf *headMB = NULL, *tailMB = NULL, *p;
- u_char c;
-#endif /* PPPOS_SUPPORT */
+#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 */
- /* Validate parameters. */
- /* We let any protocol value go through - it can't hurt us
- * and the peer will just drop it if it's not accepting it. */
- if (pd < 0 || pd >= NUM_PPP || !pc->openFlag || !pb) {
- PPPDEBUG(LOG_WARNING, ("pppifOutput[%d]: bad parms prot=%d pb=%p\n",
- pd, PPP_IP, pb));
- LINK_STATS_INC(link.opterr);
- LINK_STATS_INC(link.drop);
- snmp_inc_ifoutdiscards(netif);
- return ERR_ARG;
- }
+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 (lcp_phase[pd] == PHASE_DEAD) {
- PPPDEBUG(LOG_ERR, ("pppifOutput[%d]: link not up\n", pd));
- LINK_STATS_INC(link.rterr);
- LINK_STATS_INC(link.drop);
- snmp_inc_ifoutdiscards(netif);
- return ERR_RTE;
+ if (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 PPPOE_SUPPORT
- if(pc->ethif) {
- return pppifOutputOverEthernet(pd, pb);
- }
-#endif /* PPPOE_SUPPORT */
-
-#if PPPOS_SUPPORT
- /* Grab an output buffer. */
- headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
- if (headMB == NULL) {
- PPPDEBUG(LOG_WARNING, ("pppifOutput[%d]: first alloc fail\n", pd));
- LINK_STATS_INC(link.memerr);
- LINK_STATS_INC(link.drop);
- snmp_inc_ifoutdiscards(netif);
- return ERR_MEM;
+#if 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.
+ * this is an IP packet.
*/
- if (protocol == PPP_IP && pc->vjEnabled) {
- switch (vj_compress_tcp(&pc->vjComp, pb)) {
+ if (protocol == PPP_IP && pcb->vj_enabled) {
+ switch (vj_compress_tcp(&pcb->vj_comp, &pb)) {
case TYPE_IP:
/* No change...
- protocol = PPP_IP_PROTOCOL; */
+ 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, ("pppifOutput[%d]: bad IP packet\n", pd));
+ PPPDEBUG(LOG_WARNING, ("ppp_netif_output[%d]: bad IP packet\n", pcb->netif->num));
LINK_STATS_INC(link.proterr);
LINK_STATS_INC(link.drop);
- snmp_inc_ifoutdiscards(netif);
- pbuf_free(headMB);
+ MIB2_STATS_NETIF_INC(pcb->netif, ifoutdiscards);
return ERR_VAL;
}
}
#endif /* VJ_SUPPORT */
- tailMB = headMB;
-
- /* Build the PPP header. */
- if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) {
- tailMB = pppAppend(PPP_FLAG, tailMB, NULL);
- }
-
- pc->lastXMit = sys_jiffies();
- if (!pc->accomp) {
- fcsOut = PPP_FCS(fcsOut, PPP_ALLSTATIONS);
- tailMB = pppAppend(PPP_ALLSTATIONS, tailMB, &pc->outACCM);
- fcsOut = PPP_FCS(fcsOut, PPP_UI);
- tailMB = pppAppend(PPP_UI, tailMB, &pc->outACCM);
- }
- if (!pc->pcomp || protocol > 0xFF) {
- c = (protocol >> 8) & 0xFF;
- fcsOut = PPP_FCS(fcsOut, c);
- tailMB = pppAppend(c, tailMB, &pc->outACCM);
- }
- c = protocol & 0xFF;
- fcsOut = PPP_FCS(fcsOut, c);
- tailMB = pppAppend(c, tailMB, &pc->outACCM);
-
- /* Load packet. */
- for(p = pb; p; p = p->next) {
- int n;
- u_char *sPtr;
-
- sPtr = (u_char*)p->payload;
- n = p->len;
- while (n-- > 0) {
- c = *sPtr++;
-
- /* Update FCS before checking for special characters. */
- fcsOut = PPP_FCS(fcsOut, c);
-
- /* Copy to output buffer escaping special characters. */
- tailMB = pppAppend(c, tailMB, &pc->outACCM);
+#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;
- /* Add FCS and trailing flag. */
- c = ~fcsOut & 0xFF;
- tailMB = pppAppend(c, tailMB, &pc->outACCM);
- c = (~fcsOut >> 8) & 0xFF;
- tailMB = pppAppend(c, tailMB, &pc->outACCM);
- tailMB = pppAppend(PPP_FLAG, tailMB, NULL);
-
- /* If we failed to complete the packet, throw it away. */
- if (!tailMB) {
- PPPDEBUG(LOG_WARNING,
- ("pppifOutput[%d]: Alloc err - dropping proto=%d\n",
- pd, protocol));
- pbuf_free(headMB);
- LINK_STATS_INC(link.memerr);
- LINK_STATS_INC(link.drop);
- snmp_inc_ifoutdiscards(netif);
- return ERR_MEM;
+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;
+}
- /* Send it. */
- PPPDEBUG(LOG_INFO, ("pppifOutput[%d]: proto=0x%"X16_F"\n", pd, protocol));
+/************************************/
+/*** PRIVATE FUNCTION DEFINITIONS ***/
+/************************************/
- nPut(pc, headMB);
-#endif /* PPPOS_SUPPORT */
+/* 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
- return ERR_OK;
+ 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;
-/* Get and set parameters for the given connection.
- * Return 0 on success, an error code on failure. */
-int
-pppIOCtl(int pd, int cmd, void *arg)
-{
- PPPControl *pc = &pppControl[pd];
- int st = 0;
+ /* 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;
+ }
- if (pd < 0 || pd >= NUM_PPP) {
- st = PPPERR_PARAM;
- } else {
- switch(cmd) {
- case PPPCTLG_UPSTATUS: /* Get the PPP up status. */
- if (arg) {
- *(int *)arg = (int)(pc->if_up);
- } else {
- st = PPPERR_PARAM;
- }
- break;
- case PPPCTLS_ERRCODE: /* Set the PPP error code. */
- if (arg) {
- pc->errCode = *(int *)arg;
- } else {
- st = PPPERR_PARAM;
- }
- break;
- case PPPCTLG_ERRCODE: /* Get the PPP error code. */
- if (arg) {
- *(int *)arg = (int)(pc->errCode);
- } else {
- st = PPPERR_PARAM;
- }
- break;
-#if PPPOS_SUPPORT
- case PPPCTLG_FD: /* Get the fd associated with the ppp */
- if (arg) {
- *(sio_fd_t *)arg = pc->fd;
- } else {
- st = PPPERR_PARAM;
- }
- break;
-#endif /* PPPOS_SUPPORT */
- default:
- st = PPPERR_PARAM;
- break;
- }
+ pcb = (ppp_pcb*)LWIP_MEMPOOL_ALLOC(PPP_PCB);
+ if (pcb == NULL) {
+ return NULL;
}
- return st;
-}
+ memset(pcb, 0, sizeof(ppp_pcb));
-/*
- * Return the Maximum Transmission Unit for the given PPP connection.
- */
-u_short
-pppMTU(int pd)
-{
- PPPControl *pc = &pppControl[pd];
- u_short st;
+ /* 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 */
- /* Validate parameters. */
- if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
- st = 0;
- } else {
- st = pc->mtu;
+#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;
}
- return st;
-}
+ pcb->link_cb = callbacks;
+ pcb->link_ctx_cb = link_ctx_cb;
+ pcb->link_status_cb = link_status_cb;
+ pcb->ctx_cb = ctx_cb;
-#if PPPOE_SUPPORT
-int
-pppWriteOverEthernet(int pd, const u_char *s, int n)
-{
- PPPControl *pc = &pppControl[pd];
- struct pbuf *pb;
-
- /* skip address & flags */
- s += 2;
- n -= 2;
-
- LWIP_ASSERT("PPPOE_HDRLEN + n <= 0xffff", PPPOE_HDRLEN + n <= 0xffff);
- pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HDRLEN + n), PBUF_RAM);
- if(!pb) {
- LINK_STATS_INC(link.memerr);
- LINK_STATS_INC(link.proterr);
- snmp_inc_ifoutdiscards(&pc->netif);
- return PPPERR_ALLOC;
+ /*
+ * Initialize each protocol.
+ */
+ for (i = 0; (protp = protocols[i]) != NULL; ++i) {
+ (*protp->init)(pcb);
}
- pbuf_header(pb, -(s16_t)PPPOE_HDRLEN);
+ 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 */
- pc->lastXMit = sys_jiffies();
+ /* 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));
+}
- MEMCPY(pb->payload, s, n);
+/** 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);
+}
- if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) {
- LINK_STATS_INC(link.err);
- snmp_inc_ifoutdiscards(&pc->netif);
- return PPPERR_DEVICE;
+/** 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;
}
-
- snmp_add_ifoutoctets(&pc->netif, (u16_t)n);
- snmp_inc_ifoutucastpkts(&pc->netif);
- LINK_STATS_INC(link.xmit);
- return PPPERR_NONE;
+ pcb->link_status_cb(pcb, pcb->err_code, pcb->ctx_cb);
}
-#endif /* PPPOE_SUPPORT */
/*
- * Write n characters to a ppp link.
- * RETURN: >= 0 Number of characters written
- * -1 Failed to write to device
+ * Pass the processed input packet to the appropriate handler.
+ * This function and all handlers run in the context of the tcpip_thread
*/
-int
-pppWrite(int pd, const u_char *s, int n)
-{
- PPPControl *pc = &pppControl[pd];
-#if PPPOS_SUPPORT
- u_char c;
- u_int fcsOut;
- struct pbuf *headMB, *tailMB;
-#endif /* PPPOS_SUPPORT */
+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 */
-#if PPPOE_SUPPORT
- if(pc->ethif) {
- return pppWriteOverEthernet(pd, s, n);
+ magic_randomize();
+
+ if (pb->len < 2) {
+ PPPDEBUG(LOG_ERR, ("ppp_input[%d]: packet too short\n", pcb->netif->num));
+ goto drop;
}
-#endif /* PPPOE_SUPPORT */
+ protocol = (((u8_t *)pb->payload)[0] << 8) | ((u8_t*)pb->payload)[1];
-#if PPPOS_SUPPORT
- headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
- if (headMB == NULL) {
- LINK_STATS_INC(link.memerr);
- LINK_STATS_INC(link.proterr);
- snmp_inc_ifoutdiscards(&pc->netif);
- return PPPERR_ALLOC;
+#if PRINTPKT_SUPPORT
+ ppp_dump_packet(pcb, "rcvd", (unsigned char *)pb->payload, pb->len);
+#endif /* PRINTPKT_SUPPORT */
+
+ pbuf_remove_header(pb, 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;
}
- tailMB = headMB;
+ /*
+ * 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 the link has been idle, we'll send a fresh flag character to
- * flush any noise. */
- if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) {
- tailMB = pppAppend(PPP_FLAG, tailMB, NULL);
+#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;
}
- pc->lastXMit = sys_jiffies();
+#endif /* MPPE_SUPPORT */
- fcsOut = PPP_INITFCS;
- /* Load output buffer. */
- while (n-- > 0) {
- c = *s++;
+ if (protocol == PPP_COMP) {
+ u8_t *pl;
- /* Update FCS before checking for special characters. */
- fcsOut = PPP_FCS(fcsOut, c);
+ 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;
+ }
- /* Copy to output buffer escaping special characters. */
- tailMB = pppAppend(c, tailMB, &pc->outACCM);
+ /* Extract and hide protocol (do PFC decompression if necessary) */
+ pl = (u8_t*)pb->payload;
+ if (pl[0] & 0x01) {
+ protocol = pl[0];
+ pbuf_remove_header(pb, 1);
+ } else {
+ protocol = (pl[0] << 8) | pl[1];
+ pbuf_remove_header(pb, 2);
+ }
}
-
- /* Add FCS and trailing flag. */
- c = ~fcsOut & 0xFF;
- tailMB = pppAppend(c, tailMB, &pc->outACCM);
- c = (~fcsOut >> 8) & 0xFF;
- tailMB = pppAppend(c, tailMB, &pc->outACCM);
- tailMB = pppAppend(PPP_FLAG, tailMB, NULL);
-
- /* If we failed to complete the packet, throw it away.
- * Otherwise send it. */
- if (!tailMB) {
- PPPDEBUG(LOG_WARNING,
- ("pppWrite[%d]: Alloc err - dropping pbuf len=%d\n", pd, headMB->len));
- /*"pppWrite[%d]: Alloc err - dropping %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */
- pbuf_free(headMB);
- LINK_STATS_INC(link.memerr);
- LINK_STATS_INC(link.proterr);
- snmp_inc_ifoutdiscards(&pc->netif);
- return PPPERR_ALLOC;
+#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 = pbuf_coalesce(pb, PBUF_RAW);
+ (*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_add_header(pb, sizeof(protocol));
+ lcp_sprotrej(pcb, (u8_t*)pb->payload, pb->len);
+ }
+ break;
}
- PPPDEBUG(LOG_INFO, ("pppWrite[%d]: len=%d\n", pd, headMB->len));
- /* "pppWrite[%d]: %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */
- nPut(pc, headMB);
-#endif /* PPPOS_SUPPORT */
+drop:
+ LINK_STATS_INC(link.drop);
+ MIB2_STATS_NETIF_INC(pcb->netif, ifindiscards);
- return PPPERR_NONE;
+out:
+ pbuf_free(pb);
}
/*
- * ppp_send_config - configure the transmit characteristics of
- * the ppp interface.
+ * 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).
*/
-void
-ppp_send_config( int unit, u16_t mtu, u32_t asyncmap, int pcomp, int accomp)
-{
- PPPControl *pc = &pppControl[unit];
- int i;
-
- pc->mtu = mtu;
- pc->pcomp = pcomp;
- pc->accomp = accomp;
-
- /* Load the ACCM bits for the 32 control codes. */
- for (i = 0; i < 32/8; i++) {
- pc->outACCM[i] = (u_char)((asyncmap >> (8 * i)) & 0xFF);
- }
- PPPDEBUG(LOG_INFO, ("ppp_send_config[%d]: outACCM=%X %X %X %X\n",
- unit,
- pc->outACCM[0], pc->outACCM[1], pc->outACCM[2], pc->outACCM[3]));
+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.
+ */
/*
- * ppp_set_xaccm - set the extended transmit ACCM for the interface.
+ * new_phase - signal the start of a new phase of pppd's operation.
*/
-void
-ppp_set_xaccm(int unit, ext_accm *accm)
-{
- SMEMCPY(pppControl[unit].outACCM, accm, sizeof(ext_accm));
- PPPDEBUG(LOG_INFO, ("ppp_set_xaccm[%d]: outACCM=%X %X %X %X\n",
- unit,
- pppControl[unit].outACCM[0],
- pppControl[unit].outACCM[1],
- pppControl[unit].outACCM[2],
- pppControl[unit].outACCM[3]));
+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.
*/
-void
-ppp_recv_config( int unit, int mru, u32_t asyncmap, int pcomp, int accomp)
-{
- PPPControl *pc = &pppControl[unit];
- int i;
- SYS_ARCH_DECL_PROTECT(lev);
-
- LWIP_UNUSED_ARG(accomp);
- LWIP_UNUSED_ARG(pcomp);
+int ppp_recv_config(ppp_pcb *pcb, int mru, u32_t accm, int pcomp, int accomp) {
LWIP_UNUSED_ARG(mru);
- /* Load the ACCM bits for the 32 control codes. */
- SYS_ARCH_PROTECT(lev);
- for (i = 0; i < 32 / 8; i++) {
- /* @todo: does this work? ext_accm has been modified from pppd! */
- pc->rx.inACCM[i] = (u_char)(asyncmap >> (i * 8));
+ if (pcb->link_cb->recv_config) {
+ pcb->link_cb->recv_config(pcb, pcb->link_ctx_cb, accm, pcomp, accomp);
}
- SYS_ARCH_UNPROTECT(lev);
- PPPDEBUG(LOG_INFO, ("ppp_recv_config[%d]: inACCM=%X %X %X %X\n",
- unit,
- pc->rx.inACCM[0], pc->rx.inACCM[1], pc->rx.inACCM[2], pc->rx.inACCM[3]));
+
+ PPPDEBUG(LOG_INFO, ("ppp_recv_config[%d]\n", pcb->netif->num));
+ return 0;
}
-#if 0
+#if PPP_IPV4_SUPPORT
/*
- * ccp_test - ask kernel whether a given compression method
- * is acceptable for use. Returns 1 if the method and parameters
- * are OK, 0 if the method is known but the parameters are not OK
- * (e.g. code size should be reduced), or -1 if the method is unknown.
+ * sifaddr - Config the interface IP addresses and netmask.
*/
-int
-ccp_test( int unit, int opt_len, int for_transmit, u_char *opt_ptr)
-{
- return 0; /* XXX Currently no compression. */
+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;
}
-/*
- * ccp_flags_set - inform kernel about the current state of CCP.
+/********************************************************************
+ *
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
*/
-void
-ccp_flags_set(int unit, int isopen, int isup)
-{
- /* XXX */
+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;
}
-/*
- * 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.
+#if 0 /* UNUSED - PROXY ARP */
+/********************************************************************
+ *
+ * sifproxyarp - Make a proxy ARP entry for the peer.
*/
-int
-ccp_fatal_error(int unit)
-{
- /* XXX */
+
+int sifproxyarp(ppp_pcb *pcb, u32_t his_adr) {
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(his_adr);
return 0;
}
-#endif
-/*
- * get_idle_time - return how long the link has been idle.
+/********************************************************************
+ *
+ * cifproxyarp - Delete the proxy ARP entry for the peer.
*/
-int
-get_idle_time(int u, struct ppp_idle *ip)
-{
- /* XXX */
- LWIP_UNUSED_ARG(u);
- LWIP_UNUSED_ARG(ip);
+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_val(ns, ns1);
+ dns_setserver(0, &ns);
+ ip_addr_set_ip4_u32_val(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_val(nsb, ns1);
+ if (ip_addr_cmp(nsa, &nsb)) {
+ dns_setserver(0, IP_ADDR_ANY);
+ }
+ nsa = dns_getserver(1);
+ ip_addr_set_ip4_u32_val(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
@@ -1263,790 +1190,432 @@ get_idle_time(int u, struct ppp_idle *ip)
* network as `addr'. If we find any, we OR in their netmask to the
* user-specified netmask.
*/
-u32_t
-GetMask(u32_t addr)
-{
+u32_t get_mask(u32_t addr) {
+#if 0
u32_t mask, nmask;
- addr = htonl(addr);
+ 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 {
+ } else {
nmask = IP_CLASSC_NET;
}
/* class D nets are disallowed by bad_ip_adrs */
- mask = PP_HTONL(0xffffff00UL) | htonl(nmask);
-
+ 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 */
-/*
- * sifvjcomp - config tcp header compression
+#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
-sifvjcomp(int pd, int vjcomp, u8_t cidcomp, u8_t maxcid)
-{
-#if PPPOS_SUPPORT && VJ_SUPPORT
- PPPControl *pc = &pppControl[pd];
-
- pc->vjEnabled = vjcomp;
- pc->vjComp.compressSlot = cidcomp;
- pc->vjComp.maxSlotIndex = maxcid;
- PPPDEBUG(LOG_INFO, ("sifvjcomp: VJ compress enable=%d slot=%d max slot=%d\n",
- vjcomp, cidcomp, maxcid));
-#else /* PPPOS_SUPPORT && VJ_SUPPORT */
- LWIP_UNUSED_ARG(pd);
- LWIP_UNUSED_ARG(vjcomp);
- LWIP_UNUSED_ARG(cidcomp);
- LWIP_UNUSED_ARG(maxcid);
-#endif /* PPPOS_SUPPORT && VJ_SUPPORT */
+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;
+}
- return 0;
+/********************************************************************
+ *
+ * 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_state(pcb->netif, 0, IP6_ADDR_INVALID);
+ netif_ip6_addr_set(pcb->netif, 0, IP6_ADDR_ANY6);
+ return 1;
}
/*
- * pppifNetifInit - netif init callback
+ * sif6up - Config the interface up and enable IPv6 packets to pass.
*/
-static err_t
-pppifNetifInit(struct netif *netif)
-{
- netif->name[0] = 'p';
- netif->name[1] = 'p';
- netif->output = pppifOutput;
- netif->mtu = pppMTU((int)(size_t)netif->state);
- netif->flags = NETIF_FLAG_POINTTOPOINT | NETIF_FLAG_LINK_UP;
-#if LWIP_NETIF_HOSTNAME
- /* @todo: Initialize interface hostname */
- /* netif_set_hostname(netif, "lwip"); */
-#endif /* LWIP_NETIF_HOSTNAME */
- return ERR_OK;
-}
+int sif6up(ppp_pcb *pcb) {
+ pcb->if6_up = 1;
+ pcb->err_code = PPPERR_NONE;
+ netif_set_link_up(pcb->netif);
-/*
- * sifup - Config the interface up and enable IP packets to pass.
+ 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
-sifup(int pd)
-{
- PPPControl *pc = &pppControl[pd];
- int st = 1;
-
- if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
- st = 0;
- PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
- } else {
- netif_remove(&pc->netif);
- if (netif_add(&pc->netif, &pc->addrs.our_ipaddr, &pc->addrs.netmask,
- &pc->addrs.his_ipaddr, (void *)(size_t)pd, pppifNetifInit, ip_input)) {
- netif_set_up(&pc->netif);
- pc->if_up = 1;
- pc->errCode = PPPERR_NONE;
-
- PPPDEBUG(LOG_DEBUG, ("sifup: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode));
- if (pc->linkStatusCB) {
- pc->linkStatusCB(pc->linkStatusCtx, pc->errCode, &pc->addrs);
- }
- } else {
- st = 0;
- PPPDEBUG(LOG_ERR, ("sifup[%d]: netif_add failed\n", pd));
- }
+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);
}
-
- return st;
+ 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(int u, int proto, enum NPmode mode)
-{
- LWIP_UNUSED_ARG(u);
+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 */
/*
- * sifdown - Config the interface down and disable IP.
+ * netif_set_mtu - set the MTU on the PPP network interface.
*/
-int
-sifdown(int pd)
-{
- PPPControl *pc = &pppControl[pd];
- int st = 1;
-
- if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
- st = 0;
- PPPDEBUG(LOG_WARNING, ("sifdown[%d]: bad parms\n", pd));
- } else {
- pc->if_up = 0;
- /* make sure the netif status callback is called */
- netif_set_down(&pc->netif);
- netif_remove(&pc->netif);
- PPPDEBUG(LOG_DEBUG, ("sifdown: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode));
- if (pc->linkStatusCB) {
- pc->linkStatusCB(pc->linkStatusCtx, PPPERR_CONNECT, NULL);
- }
- }
- return st;
-}
+void netif_set_mtu(ppp_pcb *pcb, int mtu) {
-/**
- * sifaddr - Config the interface IP addresses and netmask.
- * @param pd Interface unit ???
- * @param o Our IP address ???
- * @param h His IP address ???
- * @param m IP subnet mask ???
- * @param ns1 Primary DNS
- * @param ns2 Secondary DNS
- */
-int
-sifaddr( int pd, u32_t o, u32_t h, u32_t m, u32_t ns1, u32_t ns2)
-{
- PPPControl *pc = &pppControl[pd];
- int st = 1;
-
- if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
- st = 0;
- PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
- } else {
- SMEMCPY(&pc->addrs.our_ipaddr, &o, sizeof(o));
- SMEMCPY(&pc->addrs.his_ipaddr, &h, sizeof(h));
- SMEMCPY(&pc->addrs.netmask, &m, sizeof(m));
- SMEMCPY(&pc->addrs.dns1, &ns1, sizeof(ns1));
- SMEMCPY(&pc->addrs.dns2, &ns2, sizeof(ns2));
- }
- return st;
+ pcb->netif->mtu = mtu;
+ PPPDEBUG(LOG_INFO, ("netif_set_mtu[%d]: mtu=%d\n", pcb->netif->num, mtu));
}
-/**
- * cifaddr - Clear the interface IP addresses, and delete routes
- * through the interface if possible.
- * @param pd Interface unit ???
- * @param o Our IP address ???
- * @param h IP broadcast address ???
+/*
+ * netif_get_mtu - get PPP interface MTU
*/
-int
-cifaddr( int pd, u32_t o, u32_t h)
-{
- PPPControl *pc = &pppControl[pd];
- int st = 1;
-
- LWIP_UNUSED_ARG(o);
- LWIP_UNUSED_ARG(h);
- if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
- st = 0;
- PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
- } else {
- IP4_ADDR(&pc->addrs.our_ipaddr, 0,0,0,0);
- IP4_ADDR(&pc->addrs.his_ipaddr, 0,0,0,0);
- IP4_ADDR(&pc->addrs.netmask, 255,255,255,0);
- IP4_ADDR(&pc->addrs.dns1, 0,0,0,0);
- IP4_ADDR(&pc->addrs.dns2, 0,0,0,0);
- }
- return st;
+int netif_get_mtu(ppp_pcb *pcb) {
+
+ return pcb->netif->mtu;
}
+#if CCP_SUPPORT
+#if 0 /* unused */
/*
- * sifdefaultroute - assign a default route through the address given.
+ * ccp_test - whether a given compression method is acceptable for use.
*/
int
-sifdefaultroute(int pd, u32_t l, u32_t g)
+ccp_test(ppp_pcb *pcb, u_char *opt_ptr, int opt_len, int for_transmit)
{
- PPPControl *pc = &pppControl[pd];
- int st = 1;
-
- LWIP_UNUSED_ARG(l);
- LWIP_UNUSED_ARG(g);
-
- if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
- st = 0;
- PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
- } else {
- netif_set_default(&pc->netif);
- }
-
- /* TODO: check how PPP handled the netMask, previously not set by ipSetDefault */
-
- return st;
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(opt_ptr);
+ LWIP_UNUSED_ARG(opt_len);
+ LWIP_UNUSED_ARG(for_transmit);
+ return -1;
}
+#endif /* unused */
/*
- * cifdefaultroute - delete a default route through the address given.
+ * ccp_set - inform about the current state of CCP.
*/
-int
-cifdefaultroute(int pd, u32_t l, u32_t g)
+void
+ccp_set(ppp_pcb *pcb, u8_t isopen, u8_t isup, u8_t receive_method, u8_t transmit_method)
{
- PPPControl *pc = &pppControl[pd];
- int st = 1;
-
- LWIP_UNUSED_ARG(l);
- LWIP_UNUSED_ARG(g);
-
- if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
- st = 0;
- PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
- } else {
- netif_set_default(NULL);
- }
-
- return st;
+ 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));
}
-/**********************************/
-/*** LOCAL FUNCTION DEFINITIONS ***/
-/**********************************/
-
-#if PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD
-/* The main PPP process function. This implements the state machine according
- * to section 4 of RFC 1661: The Point-To-Point Protocol. */
-static void
-pppInputThread(void *arg)
+void
+ccp_reset_comp(ppp_pcb *pcb)
{
- int count;
- PPPControlRx *pcrx = arg;
-
- while (lcp_phase[pcrx->pd] != PHASE_DEAD) {
- count = sio_read(pcrx->fd, pcrx->rxbuf, PPPOS_RX_BUFSIZE);
- if(count > 0) {
- pppInProc(pcrx, pcrx->rxbuf, count);
- } else {
- /* nothing received, give other tasks a chance to run */
- sys_msleep(1);
- }
+ switch (pcb->ccp_transmit_method) {
+#if MPPE_SUPPORT
+ case CI_MPPE:
+ mppe_comp_reset(pcb, &pcb->mppe_comp);
+ break;
+#endif /* MPPE_SUPPORT */
+ default:
+ break;
}
}
-#endif /* PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD */
-
-#if PPPOE_SUPPORT
void
-pppOverEthernetInitFailed(int pd)
+ccp_reset_decomp(ppp_pcb *pcb)
{
- PPPControl* pc;
-
- pppHup(pd);
- pppStop(pd);
-
- pc = &pppControl[pd];
- pppoe_destroy(&pc->netif);
- pc->openFlag = 0;
-
- if(pc->linkStatusCB) {
- pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL);
+ switch (pcb->ccp_receive_method) {
+#if MPPE_SUPPORT
+ case CI_MPPE:
+ mppe_decomp_reset(pcb, &pcb->mppe_decomp);
+ break;
+#endif /* MPPE_SUPPORT */
+ default:
+ break;
}
}
-static void
-pppOverEthernetLinkStatusCB(int pd, int up)
+#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)
{
- if(up) {
- PPPDEBUG(LOG_INFO, ("pppOverEthernetLinkStatusCB: unit %d: Connecting\n", pd));
- pppStart(pd);
- } else {
- pppOverEthernetInitFailed(pd);
- }
+ LWIP_UNUSED_ARG(pcb);
+ return 1;
}
-#endif /* PPPOE_SUPPORT */
-
-struct pbuf *
-pppSingleBuf(struct pbuf *p)
-{
- struct pbuf *q, *b;
- u_char *pl;
-
- if(p->tot_len == p->len) {
- return p;
- }
-
- q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
- if(!q) {
- PPPDEBUG(LOG_ERR,
- ("pppSingleBuf: unable to alloc new buf (%d)\n", p->tot_len));
- return p; /* live dangerously */
- }
-
- for(b = p, pl = q->payload; b != NULL; b = b->next) {
- MEMCPY(pl, b->payload, b->len);
- pl += b->len;
- }
-
- pbuf_free(p);
+#endif /* unused */
+#endif /* CCP_SUPPORT */
- return q;
+#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 */
-/** Input helper struct, must be packed since it is stored to pbuf->payload,
- * which might be unaligned.
+#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.
*/
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/bpstruct.h"
-#endif
-PACK_STRUCT_BEGIN
-struct pppInputHeader {
- PACK_STRUCT_FIELD(int unit);
- PACK_STRUCT_FIELD(u16_t proto);
-} PACK_STRUCT_STRUCT;
-PACK_STRUCT_END
-#ifdef PACK_STRUCT_USE_INCLUDES
-# include "arch/epstruct.h"
-#endif
+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 },
+};
/*
- * Pass the processed input packet to the appropriate handler.
- * This function and all handlers run in the context of the tcpip_thread
+ * protocol_name - find a name for a PPP protocol.
*/
-static void
-pppInput(void *arg)
-{
- struct pbuf *nb = (struct pbuf *)arg;
- u16_t protocol;
- int pd;
-
- pd = ((struct pppInputHeader *)nb->payload)->unit;
- protocol = ((struct pppInputHeader *)nb->payload)->proto;
-
- if(pbuf_header(nb, -(int)sizeof(struct pppInputHeader))) {
- LWIP_ASSERT("pbuf_header failed\n", 0);
- goto drop;
- }
-
- LINK_STATS_INC(link.recv);
- snmp_inc_ifinucastpkts(&pppControl[pd].netif);
- snmp_add_ifinoctets(&pppControl[pd].netif, nb->tot_len);
-
- /*
- * Toss all non-LCP packets unless LCP is OPEN.
- * Until we get past the authentication phase, toss all packets
- * except LCP, LQR and authentication packets.
- */
- if((lcp_phase[pd] <= PHASE_AUTHENTICATE) && (protocol != PPP_LCP)) {
- if(!((protocol == PPP_LQR) || (protocol == PPP_PAP) || (protocol == PPP_CHAP)) ||
- (lcp_phase[pd] != PHASE_AUTHENTICATE)) {
- PPPDEBUG(LOG_INFO, ("pppInput: discarding proto 0x%"X16_F" in phase %d\n", protocol, lcp_phase[pd]));
- goto drop;
- }
- }
-
- switch(protocol) {
- case PPP_VJC_COMP: /* VJ compressed TCP */
-#if PPPOS_SUPPORT && VJ_SUPPORT
- PPPDEBUG(LOG_INFO, ("pppInput[%d]: vj_comp in pbuf len=%d\n", pd, nb->len));
- /*
- * Clip off the VJ header and prepend the rebuilt TCP/IP header and
- * pass the result to IP.
- */
- if ((vj_uncompress_tcp(&nb, &pppControl[pd].vjComp) >= 0) && (pppControl[pd].netif.input)) {
- pppControl[pd].netif.input(nb, &pppControl[pd].netif);
- return;
- }
- /* Something's wrong so drop it. */
- PPPDEBUG(LOG_WARNING, ("pppInput[%d]: Dropping VJ compressed\n", pd));
-#else /* PPPOS_SUPPORT && VJ_SUPPORT */
- /* No handler for this protocol so drop the packet. */
- PPPDEBUG(LOG_INFO, ("pppInput[%d]: drop VJ Comp in %d:%s\n", pd, nb->len, nb->payload));
-#endif /* PPPOS_SUPPORT && VJ_SUPPORT */
- break;
-
- case PPP_VJC_UNCOMP: /* VJ uncompressed TCP */
-#if PPPOS_SUPPORT && VJ_SUPPORT
- PPPDEBUG(LOG_INFO, ("pppInput[%d]: vj_un in pbuf len=%d\n", pd, nb->len));
- /*
- * Process the TCP/IP header for VJ header compression and then pass
- * the packet to IP.
- */
- if ((vj_uncompress_uncomp(nb, &pppControl[pd].vjComp) >= 0) && pppControl[pd].netif.input) {
- pppControl[pd].netif.input(nb, &pppControl[pd].netif);
- return;
- }
- /* Something's wrong so drop it. */
- PPPDEBUG(LOG_WARNING, ("pppInput[%d]: Dropping VJ uncompressed\n", pd));
-#else /* PPPOS_SUPPORT && VJ_SUPPORT */
- /* No handler for this protocol so drop the packet. */
- PPPDEBUG(LOG_INFO,
- ("pppInput[%d]: drop VJ UnComp in %d:.*H\n",
- pd, nb->len, LWIP_MIN(nb->len * 2, 40), nb->payload));
-#endif /* PPPOS_SUPPORT && VJ_SUPPORT */
- break;
+const char * protocol_name(int proto) {
+ const struct protocol_list *lp;
- case PPP_IP: /* Internet Protocol */
- PPPDEBUG(LOG_INFO, ("pppInput[%d]: ip in pbuf len=%d\n", pd, nb->len));
- if (pppControl[pd].netif.input) {
- pppControl[pd].netif.input(nb, &pppControl[pd].netif);
- return;
- }
- break;
-
- default: {
- struct protent *protp;
- int i;
-
- /*
- * Upcall the proper protocol input routine.
- */
- for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
- if (protp->protocol == protocol && protp->enabled_flag) {
- PPPDEBUG(LOG_INFO, ("pppInput[%d]: %s len=%d\n", pd, protp->name, nb->len));
- nb = pppSingleBuf(nb);
- (*protp->input)(pd, nb->payload, nb->len);
- PPPDEBUG(LOG_DETAIL, ("pppInput[%d]: packet processed\n", pd));
- goto out;
- }
- }
-
- /* No handler for this protocol so reject the packet. */
- PPPDEBUG(LOG_INFO, ("pppInput[%d]: rejecting unsupported proto 0x%"X16_F" len=%d\n", pd, protocol, nb->len));
- if (pbuf_header(nb, sizeof(protocol))) {
- LWIP_ASSERT("pbuf_header failed\n", 0);
- goto drop;
- }
-#if BYTE_ORDER == LITTLE_ENDIAN
- protocol = htons(protocol);
-#endif /* BYTE_ORDER == LITTLE_ENDIAN */
- SMEMCPY(nb->payload, &protocol, sizeof(protocol));
- lcp_sprotrej(pd, nb->payload, nb->len);
+ for (lp = protocol_list; lp->proto != 0; ++lp) {
+ if (proto == lp->proto) {
+ return lp->name;
}
- break;
}
-
-drop:
- LINK_STATS_INC(link.drop);
- snmp_inc_ifindiscards(&pppControl[pd].netif);
-
-out:
- pbuf_free(nb);
- return;
+ return NULL;
}
+#endif /* PPP_PROTOCOLNAME */
-#if PPPOS_SUPPORT
-/*
- * Drop the input packet.
+#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.
*/
-static void
-pppFreeCurrentInputPacket(PPPControlRx *pcrx)
-{
- if (pcrx->inHead != NULL) {
- if (pcrx->inTail && (pcrx->inTail != pcrx->inHead)) {
- pbuf_free(pcrx->inTail);
- }
- pbuf_free(pcrx->inHead);
- pcrx->inHead = NULL;
- }
- pcrx->inTail = NULL;
-}
/*
- * Drop the input packet and increase error counters.
+ * reset_link_stats - "reset" stats when link goes up.
*/
-static void
-pppDrop(PPPControlRx *pcrx)
-{
- if (pcrx->inHead != NULL) {
-#if 0
- PPPDEBUG(LOG_INFO, ("pppDrop: %d:%.*H\n", pcrx->inHead->len, min(60, pcrx->inHead->len * 2), pcrx->inHead->payload));
-#endif
- PPPDEBUG(LOG_INFO, ("pppDrop: pbuf len=%d, addr %p\n", pcrx->inHead->len, (void*)pcrx->inHead));
+void reset_link_stats(int u) {
+ if (!get_ppp_stats(u, &old_link_stats)) {
+ return;
}
- pppFreeCurrentInputPacket(pcrx);
-#if VJ_SUPPORT
- vj_uncompress_err(&pppControl[pcrx->pd].vjComp);
-#endif /* VJ_SUPPORT */
-
- LINK_STATS_INC(link.drop);
- snmp_inc_ifindiscards(&pppControl[pcrx->pd].netif);
+ gettimeofday(&start_time, NULL);
}
-#if !PPP_INPROC_OWNTHREAD
-/** Pass received raw characters to PPPoS to be decoded. This function is
- * thread-safe and can be called from a dedicated RX-thread or from a main-loop.
- *
- * @param pd PPP descriptor index, returned by pppOpen()
- * @param data received data
- * @param len length of received data
- */
-void
-pppos_input(int pd, u_char* data, int len)
-{
- pppInProc(&pppControl[pd].rx, data, len);
-}
-#endif
-
-/**
- * Process a received octet string.
+/*
+ * update_link_stats - get stats at link termination.
*/
-static void
-pppInProc(PPPControlRx *pcrx, u_char *s, int l)
-{
- struct pbuf *nextNBuf;
- u_char curChar;
- u_char escaped;
- SYS_ARCH_DECL_PROTECT(lev);
-
- PPPDEBUG(LOG_DEBUG, ("pppInProc[%d]: got %d bytes\n", pcrx->pd, l));
- while (l-- > 0) {
- curChar = *s++;
-
- SYS_ARCH_PROTECT(lev);
- escaped = ESCAPE_P(pcrx->inACCM, curChar);
- SYS_ARCH_UNPROTECT(lev);
- /* Handle special characters. */
- if (escaped) {
- /* Check for escape sequences. */
- /* XXX Note that this does not handle an escaped 0x5d character which
- * would appear as an escape character. Since this is an ASCII ']'
- * and there is no reason that I know of to escape it, I won't complicate
- * the code to handle this case. GLL */
- if (curChar == PPP_ESCAPE) {
- pcrx->inEscaped = 1;
- /* Check for the flag character. */
- } else if (curChar == PPP_FLAG) {
- /* If this is just an extra flag character, ignore it. */
- if (pcrx->inState <= PDADDRESS) {
- /* ignore it */;
- /* If we haven't received the packet header, drop what has come in. */
- } else if (pcrx->inState < PDDATA) {
- PPPDEBUG(LOG_WARNING,
- ("pppInProc[%d]: Dropping incomplete packet %d\n",
- pcrx->pd, pcrx->inState));
- LINK_STATS_INC(link.lenerr);
- pppDrop(pcrx);
- /* If the fcs is invalid, drop the packet. */
- } else if (pcrx->inFCS != PPP_GOODFCS) {
- PPPDEBUG(LOG_INFO,
- ("pppInProc[%d]: Dropping bad fcs 0x%"X16_F" proto=0x%"X16_F"\n",
- pcrx->pd, pcrx->inFCS, pcrx->inProtocol));
- /* Note: If you get lots of these, check for UART frame errors or try different baud rate */
- LINK_STATS_INC(link.chkerr);
- pppDrop(pcrx);
- /* Otherwise it's a good packet so pass it on. */
- } else {
- struct pbuf *inp;
- /* Trim off the checksum. */
- if(pcrx->inTail->len > 2) {
- pcrx->inTail->len -= 2;
-
- pcrx->inTail->tot_len = pcrx->inTail->len;
- if (pcrx->inTail != pcrx->inHead) {
- pbuf_cat(pcrx->inHead, pcrx->inTail);
- }
- } else {
- pcrx->inTail->tot_len = pcrx->inTail->len;
- if (pcrx->inTail != pcrx->inHead) {
- pbuf_cat(pcrx->inHead, pcrx->inTail);
- }
-
- pbuf_realloc(pcrx->inHead, pcrx->inHead->tot_len - 2);
- }
-
- /* Dispatch the packet thereby consuming it. */
- inp = pcrx->inHead;
- /* Packet consumed, release our references. */
- pcrx->inHead = NULL;
- pcrx->inTail = NULL;
-#if PPP_INPROC_MULTITHREADED
- if(tcpip_callback_with_block(pppInput, inp, 0) != ERR_OK) {
- PPPDEBUG(LOG_ERR, ("pppInProc[%d]: tcpip_callback() failed, dropping packet\n", pcrx->pd));
- pbuf_free(inp);
- LINK_STATS_INC(link.drop);
- snmp_inc_ifindiscards(&pppControl[pcrx->pd].netif);
- }
-#else /* PPP_INPROC_MULTITHREADED */
- pppInput(inp);
-#endif /* PPP_INPROC_MULTITHREADED */
- }
-
- /* Prepare for a new packet. */
- pcrx->inFCS = PPP_INITFCS;
- pcrx->inState = PDADDRESS;
- pcrx->inEscaped = 0;
- /* Other characters are usually control characters that may have
- * been inserted by the physical layer so here we just drop them. */
- } else {
- PPPDEBUG(LOG_WARNING,
- ("pppInProc[%d]: Dropping ACCM char <%d>\n", pcrx->pd, curChar));
- }
- /* Process other characters. */
- } else {
- /* Unencode escaped characters. */
- if (pcrx->inEscaped) {
- pcrx->inEscaped = 0;
- curChar ^= PPP_TRANS;
- }
-
- /* Process character relative to current state. */
- switch(pcrx->inState) {
- case PDIDLE: /* Idle state - waiting. */
- /* Drop the character if it's not 0xff
- * we would have processed a flag character above. */
- if (curChar != PPP_ALLSTATIONS) {
- break;
- }
-
- /* Fall through */
- case PDSTART: /* Process start flag. */
- /* Prepare for a new packet. */
- pcrx->inFCS = PPP_INITFCS;
-
- /* Fall through */
- case PDADDRESS: /* Process address field. */
- if (curChar == PPP_ALLSTATIONS) {
- pcrx->inState = PDCONTROL;
- break;
- }
- /* Else assume compressed address and control fields so
- * fall through to get the protocol... */
- case PDCONTROL: /* Process control field. */
- /* If we don't get a valid control code, restart. */
- if (curChar == PPP_UI) {
- pcrx->inState = PDPROTOCOL1;
- break;
- }
-#if 0
- else {
- PPPDEBUG(LOG_WARNING,
- ("pppInProc[%d]: Invalid control <%d>\n", pcrx->pd, curChar));
- pcrx->inState = PDSTART;
- }
-#endif
- case PDPROTOCOL1: /* Process protocol field 1. */
- /* If the lower bit is set, this is the end of the protocol
- * field. */
- if (curChar & 1) {
- pcrx->inProtocol = curChar;
- pcrx->inState = PDDATA;
- } else {
- pcrx->inProtocol = (u_int)curChar << 8;
- pcrx->inState = PDPROTOCOL2;
- }
- break;
- case PDPROTOCOL2: /* Process protocol field 2. */
- pcrx->inProtocol |= curChar;
- pcrx->inState = PDDATA;
- break;
- case PDDATA: /* Process data byte. */
- /* Make space to receive processed data. */
- if (pcrx->inTail == NULL || pcrx->inTail->len == PBUF_POOL_BUFSIZE) {
- if (pcrx->inTail != NULL) {
- pcrx->inTail->tot_len = pcrx->inTail->len;
- if (pcrx->inTail != pcrx->inHead) {
- pbuf_cat(pcrx->inHead, pcrx->inTail);
- /* give up the inTail reference now */
- pcrx->inTail = NULL;
- }
- }
- /* If we haven't started a packet, we need a packet header. */
- nextNBuf = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
- if (nextNBuf == NULL) {
- /* No free buffers. Drop the input packet and let the
- * higher layers deal with it. Continue processing
- * the received pbuf chain in case a new packet starts. */
- PPPDEBUG(LOG_ERR, ("pppInProc[%d]: NO FREE MBUFS!\n", pcrx->pd));
- LINK_STATS_INC(link.memerr);
- pppDrop(pcrx);
- pcrx->inState = PDSTART; /* Wait for flag sequence. */
- break;
- }
- if (pcrx->inHead == NULL) {
- struct pppInputHeader *pih = nextNBuf->payload;
-
- pih->unit = pcrx->pd;
- pih->proto = pcrx->inProtocol;
-
- nextNBuf->len += sizeof(*pih);
-
- pcrx->inHead = nextNBuf;
- }
- pcrx->inTail = nextNBuf;
- }
- /* Load character into buffer. */
- ((u_char*)pcrx->inTail->payload)[pcrx->inTail->len++] = curChar;
- break;
- }
+void update_link_stats(int u) {
+ struct timeval now;
+ char numbuf[32];
- /* update the frame check sequence number. */
- pcrx->inFCS = PPP_FCS(pcrx->inFCS, curChar);
- }
- } /* while (l-- > 0), all bytes processed */
-
- avRandomize();
-}
-#endif /* PPPOS_SUPPORT */
-
-#if PPPOE_SUPPORT
-void
-pppInProcOverEthernet(int pd, struct pbuf *pb)
-{
- struct pppInputHeader *pih;
- u16_t inProtocol;
-
- if(pb->len < sizeof(inProtocol)) {
- PPPDEBUG(LOG_ERR, ("pppInProcOverEthernet: too small for protocol field\n"));
- goto drop;
+ 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;
- inProtocol = (((u8_t *)pb->payload)[0] << 8) | ((u8_t*)pb->payload)[1];
-
- /* make room for pppInputHeader - should not fail */
- if (pbuf_header(pb, sizeof(*pih) - sizeof(inProtocol)) != 0) {
- PPPDEBUG(LOG_ERR, ("pppInProcOverEthernet: could not allocate room for header\n"));
- goto drop;
- }
-
- pih = pb->payload;
-
- pih->unit = pd;
- pih->proto = inProtocol;
-
- /* Dispatch the packet thereby consuming it. */
- pppInput(pb);
- return;
-
-drop:
- LINK_STATS_INC(link.drop);
- snmp_inc_ifindiscards(&pppControl[pd].netif);
- pbuf_free(pb);
- return;
-}
-#endif /* PPPOE_SUPPORT */
-
-#if LWIP_NETIF_STATUS_CALLBACK
-/** Set the status callback of a PPP's netif
- *
- * @param pd The PPP descriptor returned by pppOpen()
- * @param status_callback pointer to the status callback function
- *
- * @see netif_set_status_callback
- */
-void
-ppp_set_netif_statuscallback(int pd, netif_status_callback_fn status_callback)
-{
- netif_set_status_callback(&pppControl[pd].netif, status_callback);
+ 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;
}
-#endif /* LWIP_NETIF_STATUS_CALLBACK */
-#if LWIP_NETIF_LINK_CALLBACK
-/** Set the link callback of a PPP's netif
- *
- * @param pd The PPP descriptor returned by pppOpen()
- * @param link_callback pointer to the link callback function
- *
- * @see netif_set_link_callback
- */
-void
-ppp_set_netif_linkcallback(int pd, netif_status_callback_fn link_callback)
-{
- netif_set_link_callback(&pppControl[pd].netif, link_callback);
+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 /* LWIP_NETIF_LINK_CALLBACK */
+#endif /* PPP_STATS_SUPPORT */
#endif /* PPP_SUPPORT */
diff --git a/lwip/src/netif/ppp/ppp.h b/lwip/src/netif/ppp/ppp.h
deleted file mode 100644
index 08d6e62..0000000
--- a/lwip/src/netif/ppp/ppp.h
+++ /dev/null
@@ -1,201 +0,0 @@
-/*****************************************************************************
-* ppp.h - Network Point to Point Protocol header file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* portions Copyright (c) 1997 Global Election Systems Inc.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 97-11-05 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
-* Original derived from BSD codes.
-*****************************************************************************/
-
-#ifndef PPP_H
-#define PPP_H
-
-#include "lwip/opt.h"
-
-#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
-
-#include "lwip/def.h"
-#include "lwip/sio.h"
-#include "lwip/stats.h"
-#include "lwip/mem.h"
-#include "lwip/netif.h"
-#include "lwip/sys.h"
-#include "lwip/timers.h"
-
-
-#ifndef __u_char_defined
-
-/* Type definitions for BSD code. */
-typedef unsigned long u_long;
-typedef unsigned int u_int;
-typedef unsigned short u_short;
-typedef unsigned char u_char;
-
-#endif
-
-
-/*************************
-*** PUBLIC DEFINITIONS ***
-*************************/
-
-/* Error codes. */
-#define PPPERR_NONE 0 /* No error. */
-#define PPPERR_PARAM -1 /* Invalid parameter. */
-#define PPPERR_OPEN -2 /* Unable to open PPP session. */
-#define PPPERR_DEVICE -3 /* Invalid I/O device for PPP. */
-#define PPPERR_ALLOC -4 /* Unable to allocate resources. */
-#define PPPERR_USER -5 /* User interrupt. */
-#define PPPERR_CONNECT -6 /* Connection lost. */
-#define PPPERR_AUTHFAIL -7 /* Failed authentication challenge. */
-#define PPPERR_PROTOCOL -8 /* Failed to meet protocol. */
-
-/*
- * PPP IOCTL commands.
- */
-/*
- * Get the up status - 0 for down, non-zero for up. The argument must
- * point to an int.
- */
-#define PPPCTLG_UPSTATUS 100 /* Get the up status - 0 down else up */
-#define PPPCTLS_ERRCODE 101 /* Set the error code */
-#define PPPCTLG_ERRCODE 102 /* Get the error code */
-#define PPPCTLG_FD 103 /* Get the fd associated with the ppp */
-
-/************************
-*** PUBLIC DATA TYPES ***
-************************/
-
-struct ppp_addrs {
- ip_addr_t our_ipaddr, his_ipaddr, netmask, dns1, dns2;
-};
-
-
-/***********************
-*** PUBLIC FUNCTIONS ***
-***********************/
-
-/* Initialize the PPP subsystem. */
-void pppInit(void);
-
-/* Warning: Using PPPAUTHTYPE_ANY might have security consequences.
- * RFC 1994 says:
- *
- * In practice, within or associated with each PPP server, there is a
- * database which associates "user" names with authentication
- * information ("secrets"). It is not anticipated that a particular
- * named user would be authenticated by multiple methods. This would
- * make the user vulnerable to attacks which negotiate the least secure
- * method from among a set (such as PAP rather than CHAP). If the same
- * secret was used, PAP would reveal the secret to be used later with
- * CHAP.
- *
- * Instead, for each user name there should be an indication of exactly
- * one method used to authenticate that user name. If a user needs to
- * make use of different authentication methods under different
- * circumstances, then distinct user names SHOULD be employed, each of
- * which identifies exactly one authentication method.
- *
- */
-enum pppAuthType {
- PPPAUTHTYPE_NONE,
- PPPAUTHTYPE_ANY,
- PPPAUTHTYPE_PAP,
- PPPAUTHTYPE_CHAP
-};
-
-void pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd);
-
-/* Link status callback function prototype */
-typedef void (*pppLinkStatusCB_fn)(void *ctx, int errCode, void *arg);
-
-#if PPPOS_SUPPORT
-/*
- * Open a new PPP connection using the given serial I/O device.
- * This initializes the PPP control block but does not
- * attempt to negotiate the LCP session.
- * Return a new PPP connection descriptor on success or
- * an error code (negative) on failure.
- */
-int pppOverSerialOpen(sio_fd_t fd, pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx);
-#endif /* PPPOS_SUPPORT */
-
-#if PPPOE_SUPPORT
-/*
- * Open a new PPP Over Ethernet (PPPOE) connection.
- */
-int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name,
- pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx);
-#endif /* PPPOE_SUPPORT */
-
-/* for source code compatibility */
-#define pppOpen(fd,cb,ls) pppOverSerialOpen(fd,cb,ls)
-
-/*
- * Close a PPP connection and release the descriptor.
- * Any outstanding packets in the queues are dropped.
- * Return 0 on success, an error code on failure.
- */
-int pppClose(int pd);
-
-/*
- * Indicate to the PPP process that the line has disconnected.
- */
-void pppSigHUP(int pd);
-
-/*
- * Get and set parameters for the given connection.
- * Return 0 on success, an error code on failure.
- */
-int pppIOCtl(int pd, int cmd, void *arg);
-
-/*
- * Return the Maximum Transmission Unit for the given PPP connection.
- */
-u_short pppMTU(int pd);
-
-#if PPPOS_SUPPORT && !PPP_INPROC_OWNTHREAD
-/*
- * PPP over Serial: this is the input function to be called for received data.
- * If PPP_INPROC_OWNTHREAD==1, a seperate input thread using the blocking
- * sio_read() is used, so this is deactivated.
- */
-void pppos_input(int pd, u_char* data, int len);
-#endif /* PPPOS_SUPPORT && !PPP_INPROC_OWNTHREAD */
-
-
-#if LWIP_NETIF_STATUS_CALLBACK
-/* Set an lwIP-style status-callback for the selected PPP device */
-void ppp_set_netif_statuscallback(int pd, netif_status_callback_fn status_callback);
-#endif /* LWIP_NETIF_STATUS_CALLBACK */
-#if LWIP_NETIF_LINK_CALLBACK
-/* Set an lwIP-style link-callback for the selected PPP device */
-void ppp_set_netif_linkcallback(int pd, netif_status_callback_fn link_callback);
-#endif /* LWIP_NETIF_LINK_CALLBACK */
-
-#endif /* PPP_SUPPORT */
-
-#endif /* PPP_H */
diff --git a/lwip/src/netif/ppp/ppp_impl.h b/lwip/src/netif/ppp/ppp_impl.h
deleted file mode 100644
index 89aea60..0000000
--- a/lwip/src/netif/ppp/ppp_impl.h
+++ /dev/null
@@ -1,363 +0,0 @@
-/*****************************************************************************
-* ppp.h - Network Point to Point Protocol header file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* portions Copyright (c) 1997 Global Election Systems Inc.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 97-11-05 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
-* Original derived from BSD codes.
-*****************************************************************************/
-
-#ifndef PPP_IMPL_H
-#define PPP_IMPL_H
-
-#include "lwip/opt.h"
-
-#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
-
-#include "ppp.h"
-#include "lwip/def.h"
-#include "lwip/sio.h"
-#include "lwip/stats.h"
-#include "lwip/mem.h"
-#include "lwip/netif.h"
-#include "lwip/sys.h"
-#include "lwip/timers.h"
-
-/** Some defines for code we skip compared to the original pppd.
- * These are just here to minimise the use of the ugly "#if 0". */
-#define PPP_ADDITIONAL_CALLBACKS 0
-
-/** Some error checks to test for unsupported code */
-#if CBCP_SUPPORT
-#error "CBCP is not supported in lwIP PPP"
-#endif
-#if CCP_SUPPORT
-#error "CCP is not supported in lwIP PPP"
-#endif
-
-/*
- * pppd.h - PPP daemon global declarations.
- *
- * Copyright (c) 1989 Carnegie Mellon University.
- * All rights reserved.
- *
- * Redistribution and use in source and binary forms are permitted
- * provided that the above copyright notice and this paragraph are
- * duplicated in all such forms and that any documentation,
- * advertising materials, and other materials related to such
- * distribution and use acknowledge that the software was developed
- * by Carnegie Mellon University. The name of the
- * University may not be used to endorse or promote products derived
- * from this software without specific prior written permission.
- * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
- * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
- * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
- *
- */
-/*
- * ppp_defs.h - PPP definitions.
- *
- * Copyright (c) 1994 The Australian National University.
- * All rights reserved.
- *
- * Permission to use, copy, modify, and distribute this software and its
- * documentation is hereby granted, provided that the above copyright
- * notice appears in all copies. This software is provided without any
- * warranty, express or implied. The Australian National University
- * makes no representations about the suitability of this software for
- * any purpose.
- *
- * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
- * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
- * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
- * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
- * OF SUCH DAMAGE.
- *
- * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
- * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
- * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
- * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
- * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
- * OR MODIFICATIONS.
- */
-
-#define TIMEOUT(f, a, t) do { sys_untimeout((f), (a)); sys_timeout((t)*1000, (f), (a)); } while(0)
-#define UNTIMEOUT(f, a) sys_untimeout((f), (a))
-
-
-/*
- * Constants and structures defined by the internet system,
- * Per RFC 790, September 1981, and numerous additions.
- */
-
-/*
- * The basic PPP frame.
- */
-#define PPP_HDRLEN 4 /* octets for standard ppp header */
-#define PPP_FCSLEN 2 /* octets for FCS */
-
-
-/*
- * Significant octet values.
- */
-#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */
-#define PPP_UI 0x03 /* Unnumbered Information */
-#define PPP_FLAG 0x7e /* Flag Sequence */
-#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */
-#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */
-
-/*
- * Protocol field values.
- */
-#define PPP_IP 0x21 /* Internet Protocol */
-#define PPP_AT 0x29 /* AppleTalk Protocol */
-#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */
-#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */
-#define PPP_COMP 0xfd /* compressed packet */
-#define PPP_IPCP 0x8021 /* IP Control Protocol */
-#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */
-#define PPP_CCP 0x80fd /* Compression Control Protocol */
-#define PPP_LCP 0xc021 /* Link Control Protocol */
-#define PPP_PAP 0xc023 /* Password Authentication Protocol */
-#define PPP_LQR 0xc025 /* Link Quality Report protocol */
-#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */
-#define PPP_CBCP 0xc029 /* Callback Control Protocol */
-
-/*
- * Values for FCS calculations.
- */
-#define PPP_INITFCS 0xffff /* Initial FCS value */
-#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */
-#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
-
-/*
- * Extended asyncmap - allows any character to be escaped.
- */
-typedef u_char ext_accm[32];
-
-/*
- * What to do with network protocol (NP) packets.
- */
-enum NPmode {
- NPMODE_PASS, /* pass the packet through */
- NPMODE_DROP, /* silently drop the packet */
- NPMODE_ERROR, /* return an error */
- NPMODE_QUEUE /* save it up for later. */
-};
-
-/*
- * Inline versions of get/put char/short/long.
- * Pointer is advanced; we assume that both arguments
- * are lvalues and will already be in registers.
- * cp MUST be u_char *.
- */
-#define GETCHAR(c, cp) { \
- (c) = *(cp)++; \
-}
-#define PUTCHAR(c, cp) { \
- *(cp)++ = (u_char) (c); \
-}
-
-
-#define GETSHORT(s, cp) { \
- (s) = *(cp); (cp)++; (s) <<= 8; \
- (s) |= *(cp); (cp)++; \
-}
-#define PUTSHORT(s, cp) { \
- *(cp)++ = (u_char) ((s) >> 8); \
- *(cp)++ = (u_char) (s & 0xff); \
-}
-
-#define GETLONG(l, cp) { \
- (l) = *(cp); (cp)++; (l) <<= 8; \
- (l) |= *(cp); (cp)++; (l) <<= 8; \
- (l) |= *(cp); (cp)++; (l) <<= 8; \
- (l) |= *(cp); (cp)++; \
-}
-#define PUTLONG(l, cp) { \
- *(cp)++ = (u_char) ((l) >> 24); \
- *(cp)++ = (u_char) ((l) >> 16); \
- *(cp)++ = (u_char) ((l) >> 8); \
- *(cp)++ = (u_char) (l); \
-}
-
-
-#define INCPTR(n, cp) ((cp) += (n))
-#define DECPTR(n, cp) ((cp) -= (n))
-
-#define BCMP(s0, s1, l) memcmp((u_char *)(s0), (u_char *)(s1), (l))
-#define BCOPY(s, d, l) MEMCPY((d), (s), (l))
-#define BZERO(s, n) memset(s, 0, n)
-
-#if PPP_DEBUG
-#define PRINTMSG(m, l) { m[l] = '\0'; LWIP_DEBUGF(LOG_INFO, ("Remote message: %s\n", m)); }
-#else /* PPP_DEBUG */
-#define PRINTMSG(m, l)
-#endif /* PPP_DEBUG */
-
-/*
- * MAKEHEADER - Add PPP Header fields to a packet.
- */
-#define MAKEHEADER(p, t) { \
- PUTCHAR(PPP_ALLSTATIONS, p); \
- PUTCHAR(PPP_UI, p); \
- PUTSHORT(t, p); }
-
-/************************
-*** PUBLIC DATA TYPES ***
-************************/
-
-/*
- * The following struct gives the addresses of procedures to call
- * for a particular protocol.
- */
-struct protent {
- u_short protocol; /* PPP protocol number */
- /* Initialization procedure */
- void (*init) (int unit);
- /* Process a received packet */
- void (*input) (int unit, u_char *pkt, int len);
- /* Process a received protocol-reject */
- void (*protrej) (int unit);
- /* Lower layer has come up */
- void (*lowerup) (int unit);
- /* Lower layer has gone down */
- void (*lowerdown) (int unit);
- /* Open the protocol */
- void (*open) (int unit);
- /* Close the protocol */
- void (*close) (int unit, char *reason);
-#if PPP_ADDITIONAL_CALLBACKS
- /* Print a packet in readable form */
- int (*printpkt) (u_char *pkt, int len,
- void (*printer) (void *, char *, ...),
- void *arg);
- /* Process a received data packet */
- void (*datainput) (int unit, u_char *pkt, int len);
-#endif /* PPP_ADDITIONAL_CALLBACKS */
- int enabled_flag; /* 0 if protocol is disabled */
- char *name; /* Text name of protocol */
-#if PPP_ADDITIONAL_CALLBACKS
- /* Check requested options, assign defaults */
- void (*check_options) (u_long);
- /* Configure interface for demand-dial */
- int (*demand_conf) (int unit);
- /* Say whether to bring up link for this pkt */
- int (*active_pkt) (u_char *pkt, int len);
-#endif /* PPP_ADDITIONAL_CALLBACKS */
-};
-
-/*
- * The following structure records the time in seconds since
- * the last NP packet was sent or received.
- */
-struct ppp_idle {
- u_short xmit_idle; /* seconds since last NP packet sent */
- u_short recv_idle; /* seconds since last NP packet received */
-};
-
-struct ppp_settings {
-
- u_int disable_defaultip : 1; /* Don't use hostname for default IP addrs */
- u_int auth_required : 1; /* Peer is required to authenticate */
- u_int explicit_remote : 1; /* remote_name specified with remotename opt */
- u_int refuse_pap : 1; /* Don't wanna auth. ourselves with PAP */
- u_int refuse_chap : 1; /* Don't wanna auth. ourselves with CHAP */
- u_int usehostname : 1; /* Use hostname for our_name */
- u_int usepeerdns : 1; /* Ask peer for DNS adds */
-
- u_short idle_time_limit; /* Shut down link if idle for this long */
- int maxconnect; /* Maximum connect time (seconds) */
-
- char user [MAXNAMELEN + 1]; /* Username for PAP */
- char passwd [MAXSECRETLEN + 1]; /* Password for PAP, secret for CHAP */
- char our_name [MAXNAMELEN + 1]; /* Our name for authentication purposes */
- char remote_name[MAXNAMELEN + 1]; /* Peer's name for authentication */
-};
-
-/*****************************
-*** PUBLIC DATA STRUCTURES ***
-*****************************/
-
-/* Buffers for outgoing packets. */
-extern u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN];
-
-extern struct ppp_settings ppp_settings;
-
-extern struct protent *ppp_protocols[]; /* Table of pointers to supported protocols */
-
-
-/***********************
-*** PUBLIC FUNCTIONS ***
-***********************/
-
-/*
- * Write n characters to a ppp link.
- * RETURN: >= 0 Number of characters written, -1 Failed to write to device.
- */
-int pppWrite(int pd, const u_char *s, int n);
-
-void pppInProcOverEthernet(int pd, struct pbuf *pb);
-
-struct pbuf *pppSingleBuf(struct pbuf *p);
-
-void pppLinkTerminated(int pd);
-
-void pppLinkDown(int pd);
-
-/* Configure i/f transmit parameters */
-void ppp_send_config (int, u16_t, u32_t, int, int);
-/* Set extended transmit ACCM */
-void ppp_set_xaccm (int, ext_accm *);
-/* Configure i/f receive parameters */
-void ppp_recv_config (int, int, u32_t, int, int);
-/* Find out how long link has been idle */
-int get_idle_time (int, struct ppp_idle *);
-
-/* Configure VJ TCP header compression */
-int sifvjcomp (int, int, u8_t, u8_t);
-/* Configure i/f down (for IP) */
-int sifup (int);
-/* Set mode for handling packets for proto */
-int sifnpmode (int u, int proto, enum NPmode mode);
-/* Configure i/f down (for IP) */
-int sifdown (int);
-/* Configure IP addresses for i/f */
-int sifaddr (int, u32_t, u32_t, u32_t, u32_t, u32_t);
-/* Reset i/f IP addresses */
-int cifaddr (int, u32_t, u32_t);
-/* Create default route through i/f */
-int sifdefaultroute (int, u32_t, u32_t);
-/* Delete default route through i/f */
-int cifdefaultroute (int, u32_t, u32_t);
-
-/* Get appropriate netmask for address */
-u32_t GetMask (u32_t);
-
-#endif /* PPP_SUPPORT */
-
-#endif /* PPP_IMPL_H */
diff --git a/lwip/src/netif/ppp/pppapi.c b/lwip/src/netif/ppp/pppapi.c
new file mode 100644
index 0000000..947f7ba
--- /dev/null
+++ b/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/lwip/src/netif/ppp/pppcrypt.c b/lwip/src/netif/ppp/pppcrypt.c
new file mode 100644
index 0000000..82d78c1
--- /dev/null
+++ b/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/lwip/src/netif/ppp/ppp_oe.c b/lwip/src/netif/ppp/pppoe.c
index fdf52ae..05d4b35 100644
--- a/lwip/src/netif/ppp/ppp_oe.c
+++ b/lwip/src/netif/ppp/pppoe.c
@@ -1,5 +1,5 @@
/*****************************************************************************
-* ppp_oe.c - PPP Over Ethernet implementation for lwIP.
+* pppoe.c - PPP Over Ethernet implementation for lwIP.
*
* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc.
*
@@ -68,21 +68,27 @@
* POSSIBILITY OF SUCH DAMAGE.
*/
-#include "lwip/opt.h"
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */
-#if PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */
-
-#include "netif/ppp_oe.h"
-
-#include "ppp_impl.h"
-#include "pppdebug.h"
+#if 0 /* UNUSED */
+#include <string.h>
+#include <stdio.h>
+#endif /* UNUSED */
-#include "lwip/timers.h"
+#include "lwip/timeouts.h"
#include "lwip/memp.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
-#include <string.h>
-#include <stdio.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) \
@@ -107,19 +113,21 @@
#define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */
#endif
-#ifndef PPPOE_ERRORSTRING_LEN
#define PPPOE_ERRORSTRING_LEN 64
-#endif
-static char pppoe_error_tmp[PPPOE_ERRORSTRING_LEN];
-/* input routines */
-static void pppoe_dispatch_disc_pkt(struct netif *, struct pbuf *);
+/* 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 int pppoe_do_disconnect(struct pppoe_softc *);
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 *);
@@ -134,62 +142,162 @@ static err_t pppoe_send_pads(struct pppoe_softc *);
static err_t pppoe_send_padt(struct netif *, u_int, const u8_t *);
/* internal helper functions */
-static struct pppoe_softc * pppoe_find_softc_by_session(u_int, struct netif *);
-static struct pppoe_softc * pppoe_find_softc_by_hunique(u8_t *, size_t, struct netif *);
+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;
-err_t
-pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr)
+/* 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 *)memp_malloc(MEMP_PPPOE_IF);
+ sc = (struct pppoe_softc *)LWIP_MEMPOOL_ALLOC(PPPOE_IF);
if (sc == NULL) {
- *scptr = NULL;
- return ERR_MEM;
+ return NULL;
}
- memset(sc, 0, sizeof(struct pppoe_softc));
- /* changed to real address later */
- MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+ ppp = ppp_new(pppif, &pppoe_callbacks, sc, link_status_cb, ctx_cb);
+ if (ppp == NULL) {
+ LWIP_MEMPOOL_FREE(PPPOE_IF, sc);
+ return NULL;
+ }
- sc->sc_pd = pd;
- sc->sc_linkStatusCB = linkStatusCB;
+ 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_remove_header(p, 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;
+ }
- *scptr = sc;
+ pbuf_remove_header(ph, 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;
}
-err_t
-pppoe_destroy(struct netif *ifp)
-{
- struct pppoe_softc *sc, *prev = NULL;
-
- for (sc = pppoe_softc_list; sc != NULL; prev = sc, sc = sc->next) {
- if (sc->sc_ethif == ifp) {
- break;
- }
+/* 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;
}
- if(!(sc && (sc->sc_ethif == ifp))) {
- return ERR_IF;
+ pbuf_remove_header(pb, 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);
- if (prev == NULL) {
- /* remove sc from the head of the list */
- pppoe_softc_list = sc->next;
- } else {
- /* remove sc from the list */
- prev->next = sc->next;
+
+ /* remove interface from list */
+ for (copp = &pppoe_softc_list; (freep = *copp); copp = &freep->next) {
+ if (freep == sc) {
+ *copp = freep->next;
+ break;
+ }
}
#ifdef PPPOE_TODO
@@ -200,7 +308,7 @@ pppoe_destroy(struct netif *ifp)
mem_free(sc->sc_service_name);
}
#endif /* PPPOE_TODO */
- memp_free(MEMP_PPPOE_IF, sc);
+ LWIP_MEMPOOL_FREE(PPPOE_IF, sc);
return ERR_OK;
}
@@ -211,39 +319,24 @@ pppoe_destroy(struct netif *ifp)
* 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)
-{
+static struct pppoe_softc* pppoe_find_softc_by_session(u_int session, struct netif *rcvif) {
struct pppoe_softc *sc;
- if (session == 0) {
- return NULL;
- }
-
for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) {
if (sc->sc_state == PPPOE_STATE_SESSION
- && sc->sc_session == session) {
- if (sc->sc_ethif == rcvif) {
- return sc;
- } else {
- return NULL;
+ && 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)
-{
+static struct pppoe_softc* pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif) {
struct pppoe_softc *sc, *t;
- if (pppoe_softc_list == NULL) {
- return NULL;
- }
-
if (len != sizeof sc) {
return NULL;
}
@@ -262,33 +355,28 @@ pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif)
/* should be safe to access *sc now */
if (sc->sc_state < PPPOE_STATE_PADI_SENT || sc->sc_state >= PPPOE_STATE_SESSION) {
- printf("%c%c%"U16_F": host unique tag found, but it belongs to a connection in state %d\n",
- sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_state);
+ 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) {
- printf("%c%c%"U16_F": wrong interface, not accepting host unique\n",
- sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ 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;
}
-static void
-pppoe_linkstatus_up(struct pppoe_softc *sc)
-{
- sc->sc_linkStatusCB(sc->sc_pd, 1);
-}
-
/* analyze and handle a single received packet while not in session state */
-static void
-pppoe_dispatch_disc_pkt(struct netif *netif, struct pbuf *pb)
+void
+pppoe_disc_input(struct netif *netif, struct pbuf *pb)
{
u16_t tag, len;
u16_t session, plen;
struct pppoe_softc *sc;
- const char *err_msg;
- char devname[6];
+#if PPP_DEBUG
+ const char *err_msg = NULL;
+#endif /* PPP_DEBUG */
u8_t *ac_cookie;
u16_t ac_cookie_len;
#ifdef PPPOE_SERVER
@@ -297,14 +385,17 @@ pppoe_dispatch_disc_pkt(struct netif *netif, struct pbuf *pb)
#endif
struct pppoehdr *ph;
struct pppoetag pt;
- int off, err, errortag;
+ int off, err;
struct eth_hdr *ethhdr;
- pb = pppSingleBuf(pb);
+ /* don't do anything if there is not a single PPPoE instance */
+ if (pppoe_softc_list == NULL) {
+ pbuf_free(pb);
+ return;
+ }
+
+ pb = pbuf_coalesce(pb, PBUF_RAW);
- strcpy(devname, "pppoe"); /* as long as we don't know which instance */
- err_msg = NULL;
- errortag = 0;
if (pb->len < sizeof(*ethhdr)) {
goto done;
}
@@ -318,23 +409,23 @@ pppoe_dispatch_disc_pkt(struct netif *netif, struct pbuf *pb)
hunique_len = 0;
#endif
session = 0;
- if (pb->len - off < PPPOE_HEADERLEN) {
- printf("pppoe: packet too short: %d\n", pb->len);
+ 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) {
- printf("pppoe: unknown version/type packet: 0x%x\n", ph->vertype);
+ PPPDEBUG(LOG_DEBUG, ("pppoe: unknown version/type packet: 0x%x\n", ph->vertype));
goto done;
}
- session = ntohs(ph->session);
- plen = ntohs(ph->plen);
+ session = lwip_ntohs(ph->session);
+ plen = lwip_ntohs(ph->plen);
off += sizeof(*ph);
if (plen + off > pb->len) {
- printf("pppoe: packet content does not fit: data available = %d, packet size = %u\n",
- pb->len - off, plen);
+ 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) {
@@ -345,10 +436,10 @@ pppoe_dispatch_disc_pkt(struct netif *netif, struct pbuf *pb)
sc = NULL;
while (off + sizeof(pt) <= pb->len) {
MEMCPY(&pt, (u8_t*)pb->payload + off, sizeof(pt));
- tag = ntohs(pt.tag);
- len = ntohs(pt.len);
+ tag = lwip_ntohs(pt.tag);
+ len = lwip_ntohs(pt.len);
if (off + sizeof(pt) + len > pb->len) {
- printf("pppoe: tag 0x%x len 0x%x is too long\n", tag, len);
+ PPPDEBUG(LOG_DEBUG, ("pppoe: tag 0x%x len 0x%x is too long\n", tag, len));
goto done;
}
switch (tag) {
@@ -367,42 +458,44 @@ pppoe_dispatch_disc_pkt(struct netif *netif, struct pbuf *pb)
hunique_len = len;
#endif
sc = pppoe_find_softc_by_hunique((u8_t*)pb->payload + off + sizeof(pt), len, netif);
- if (sc != NULL) {
- snprintf(devname, sizeof(devname), "%c%c%"U16_F, sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
- }
break;
case PPPOE_TAG_ACCOOKIE:
if (ac_cookie == NULL) {
+ 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";
- errortag = 1;
break;
case PPPOE_TAG_ACSYS_ERR:
err_msg = "AC SYSTEM ERROR";
- errortag = 1;
break;
case PPPOE_TAG_GENERIC_ERR:
err_msg = "GENERIC ERROR";
- errortag = 1;
+ break;
+#endif /* PPP_DEBUG */
+ default:
break;
}
- if (err_msg) {
- if (errortag && len) {
- u16_t error_len = LWIP_MIN(len, sizeof(pppoe_error_tmp)-1);
- strncpy(pppoe_error_tmp, (char*)pb->payload + off + sizeof(pt), error_len);
- pppoe_error_tmp[error_len-1] = '\0';
- printf("%s: %s: %s\n", devname, err_msg, pppoe_error_tmp);
+#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 {
- printf("%s: %s\n", devname, err_msg);
- }
- if (errortag) {
- goto done;
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %s: %s\n", err_msg, error_tmp));
}
}
+#endif /* PPP_DEBUG */
off += sizeof(pt) + len;
}
@@ -429,7 +522,7 @@ breakbreak:;
}
}
if (sc == NULL) {
- /* printf("pppoe: free passive interface is not found\n"); */
+ /* PPPDEBUG(LOG_DEBUG, ("pppoe: free passive interface is not found\n")); */
goto done;
}
if (hunique) {
@@ -455,19 +548,19 @@ breakbreak:;
*/
if (ac_cookie == NULL) {
/* be quiet if there is not a single pppoe instance */
- printf("pppoe: received PADR but not includes ac_cookie\n");
+ 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)) {
- printf("pppoe: received PADR but could not find request for it\n");
+ PPPDEBUG(LOG_DEBUG, ("pppoe: received PADR but could not find request for it\n"));
}
goto done;
}
if (sc->sc_state != PPPOE_STATE_PADO_SENT) {
- printf("%c%c%"U16_F": received unexpected PADR\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ 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) {
@@ -483,7 +576,7 @@ breakbreak:;
}
pppoe_send_pads(sc);
sc->sc_state = PPPOE_STATE_SESSION;
- pppoe_linkstatus_up(sc); /* notify upper layers */
+ ppp_start(sc->pcb); /* notify upper layers */
break;
#else
/* ignore, we are no access concentrator */
@@ -493,12 +586,12 @@ breakbreak:;
if (sc == NULL) {
/* be quiet if there is not a single pppoe instance */
if (pppoe_softc_list != NULL) {
- printf("pppoe: received PADO but could not find request for it\n");
+ PPPDEBUG(LOG_DEBUG, ("pppoe: received PADO but could not find request for it\n"));
}
goto done;
}
if (sc->sc_state != PPPOE_STATE_PADI_SENT) {
- printf("%c%c%"U16_F": received unexpected PADO\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ 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) {
@@ -511,6 +604,7 @@ breakbreak:;
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));
+ LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
}
sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc);
break;
@@ -522,21 +616,28 @@ breakbreak:;
sys_untimeout(pppoe_timeout, sc);
PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x connected\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, session));
sc->sc_state = PPPOE_STATE_SESSION;
- pppoe_linkstatus_up(sc); /* notify upper layers */
+ ppp_start(sc->pcb); /* notify upper layers */
break;
case PPPOE_CODE_PADT:
- if (sc == NULL) {
+ /* 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) {
- printf("%c%c%"U16_F": unknown code (0x%"X16_F") session = 0x%"X16_F"\n",
+ 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);
+ (u16_t)ph->code, session));
} else {
- printf("pppoe: unknown code (0x%"X16_F") session = 0x%"X16_F"\n", (u16_t)ph->code, session);
+ PPPDEBUG(LOG_DEBUG, ("pppoe: unknown code (0x%"X16_F") session = 0x%"X16_F"\n", (u16_t)ph->code, session));
}
break;
}
@@ -547,17 +648,6 @@ done:
}
void
-pppoe_disc_input(struct netif *netif, struct pbuf *p)
-{
- /* avoid error messages if there is not a single pppoe instance */
- if (pppoe_softc_list != NULL) {
- pppoe_dispatch_disc_pkt(netif, p);
- } else {
- pbuf_free(p);
- }
-}
-
-void
pppoe_data_input(struct netif *netif, struct pbuf *pb)
{
u16_t session, plen;
@@ -570,49 +660,42 @@ pppoe_data_input(struct netif *netif, struct pbuf *pb)
#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
MEMCPY(shost, ((struct eth_hdr *)pb->payload)->src.addr, sizeof(shost));
#endif
- if (pbuf_header(pb, -(int)sizeof(struct eth_hdr)) != 0) {
+ if (pbuf_remove_header(pb, sizeof(struct eth_hdr)) != 0) {
/* bail out */
- PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header failed\n"));
+ PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_remove_header failed\n"));
LINK_STATS_INC(link.lenerr);
goto drop;
}
- pb = pppSingleBuf (pb);
-
- if (pb->len <= PPPOE_HEADERLEN) {
- printf("pppoe (data): dropping too short packet: %d bytes\n", pb->len);
- goto drop;
- }
-
if (pb->len < sizeof(*ph)) {
- printf("pppoe_data_input: could not get PPPoE header\n");
+ PPPDEBUG(LOG_DEBUG, ("pppoe_data_input: could not get PPPoE header\n"));
goto drop;
}
ph = (struct pppoehdr *)pb->payload;
if (ph->vertype != PPPOE_VERTYPE) {
- printf("pppoe (data): unknown version/type packet: 0x%x\n", ph->vertype);
+ PPPDEBUG(LOG_DEBUG, ("pppoe (data): unknown version/type packet: 0x%x\n", ph->vertype));
goto drop;
}
if (ph->code != 0) {
goto drop;
}
- session = ntohs(ph->session);
+ session = lwip_ntohs(ph->session);
sc = pppoe_find_softc_by_session(session, netif);
if (sc == NULL) {
#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
- printf("pppoe: input for unknown session 0x%x, sending PADT\n", session);
+ PPPDEBUG(LOG_DEBUG, ("pppoe: input for unknown session 0x%x, sending PADT\n", session));
pppoe_send_padt(netif, session, shost);
#endif
goto drop;
}
- plen = ntohs(ph->plen);
+ plen = lwip_ntohs(ph->plen);
- if (pbuf_header(pb, -(int)(PPPOE_HEADERLEN)) != 0) {
+ if (pbuf_remove_header(pb, PPPOE_HEADERLEN) != 0) {
/* bail out */
- PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header PPPOE_HEADERLEN failed\n"));
+ PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_remove_header PPPOE_HEADERLEN failed\n"));
LINK_STATS_INC(link.lenerr);
goto drop;
}
@@ -621,12 +704,12 @@ pppoe_data_input(struct netif *netif, struct pbuf *pb)
sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num,
pb->len, plen));
- if (pb->len < plen) {
+ if (pb->tot_len < plen) {
goto drop;
}
- pppInProcOverEthernet(sc->sc_pd, pb);
-
+ /* Dispatch the packet thereby consuming it. */
+ ppp_input(sc->pcb, pb);
return;
drop:
@@ -640,16 +723,19 @@ pppoe_output(struct pppoe_softc *sc, struct pbuf *pb)
u16_t etype;
err_t res;
- if (!sc->sc_ethif) {
+ /* make room for Ethernet header - should not fail */
+ if (pbuf_add_header(pb, 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_IF;
+ return ERR_BUF;
}
-
ethhdr = (struct eth_hdr *)pb->payload;
etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHTYPE_PPPOE : ETHTYPE_PPPOEDISC;
- ethhdr->type = htons(etype);
- MEMCPY(ethhdr->dest.addr, sc->sc_dest.addr, sizeof(ethhdr->dest.addr));
- MEMCPY(ethhdr->src.addr, ((struct eth_addr *)sc->sc_ethif->hwaddr)->addr, sizeof(ethhdr->src.addr));
+ 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,
@@ -674,10 +760,6 @@ pppoe_send_padi(struct pppoe_softc *sc)
int l1 = 0, l2 = 0; /* XXX: gcc */
#endif /* PPPOE_TODO */
- if (sc->sc_state >PPPOE_STATE_PADI_SENT) {
- PPPDEBUG(LOG_ERR, ("ERROR: pppoe_send_padi in state %d", sc->sc_state));
- }
-
/* calculate length of frame (excluding ethernet header + pppoe header) */
len = 2 + 2 + 2 + 2 + sizeof sc; /* service name tag is required, host unique is send too */
#ifdef PPPOE_TODO
@@ -694,13 +776,13 @@ pppoe_send_padi(struct pppoe_softc *sc)
sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff);
/* allocate a buffer */
- pb = pbuf_alloc(PBUF_LINK, (u16_t)(sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len), PBUF_RAM);
+ 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 + sizeof (struct eth_hdr);
+ 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);
@@ -733,7 +815,8 @@ pppoe_send_padi(struct pppoe_softc *sc)
static void
pppoe_timeout(void *arg)
{
- int retry_wait, err;
+ 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));
@@ -749,12 +832,10 @@ pppoe_timeout(void *arg)
* We only enter slow retry mode if IFF_LINK1 (aka autodial)
* is not set.
*/
-
- /* initialize for quick retry mode */
- retry_wait = PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried);
-
- sc->sc_padi_retried++;
- if (sc->sc_padi_retried >= PPPOE_DISC_MAXPADI) {
+ if (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 */
@@ -766,9 +847,12 @@ pppoe_timeout(void *arg)
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));
+ LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
}
sys_timeout(retry_wait, pppoe_timeout, sc);
break;
@@ -781,6 +865,7 @@ pppoe_timeout(void *arg)
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));
+ LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
}
sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried), pppoe_timeout, sc);
return;
@@ -788,100 +873,105 @@ pppoe_timeout(void *arg)
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));
+ LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
}
sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc);
break;
- case PPPOE_STATE_CLOSING:
- pppoe_do_disconnect(sc);
- break;
default:
return; /* all done, work in peace */
}
}
/* Start a connection (i.e. initiate discovery phase) */
-int
-pppoe_connect(struct pppoe_softc *sc)
+static void
+pppoe_connect(ppp_pcb *ppp, void *ctx)
{
- int err;
-
- if (sc->sc_state != PPPOE_STATE_INITIAL) {
- return EBUSY;
- }
+ 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;
- sc->sc_padr_retried = 0;
- err = pppoe_send_padi(sc);
- PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+ 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);
- return err;
}
/* disconnect */
-void
-pppoe_disconnect(struct pppoe_softc *sc)
-{
- if (sc->sc_state < PPPOE_STATE_SESSION) {
- return;
- }
- /*
- * Do not call pppoe_disconnect here, the upper layer state
- * machine gets confused by this. We must return from this
- * function and defer disconnecting to the timeout handler.
- */
- sc->sc_state = PPPOE_STATE_CLOSING;
- sys_timeout(20, pppoe_timeout, sc);
-}
-
-static int
-pppoe_do_disconnect(struct pppoe_softc *sc)
+static void
+pppoe_disconnect(ppp_pcb *ppp, void *ctx)
{
- int err;
+ struct pppoe_softc *sc = (struct pppoe_softc *)ctx;
- if (sc->sc_state < PPPOE_STATE_SESSION) {
- err = EBUSY;
- } else {
- PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": disconnecting\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
- err = pppoe_send_padt(sc->sc_ethif, sc->sc_session, (const u8_t *)&sc->sc_dest);
+ 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);
}
- /* cleanup softc */
+ /* stop any timer, disconnect can be called while initiating is in progress */
+ sys_untimeout(pppoe_timeout, sc);
sc->sc_state = PPPOE_STATE_INITIAL;
- MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
- sc->sc_ac_cookie_len = 0;
#ifdef PPPOE_SERVER
if (sc->sc_hunique) {
mem_free(sc->sc_hunique);
- sc->sc_hunique = NULL;
+ sc->sc_hunique = NULL; /* probably not necessary, if state is initial we shouldn't have to access hunique anyway */
}
- sc->sc_hunique_len = 0;
+ sc->sc_hunique_len = 0; /* probably not necessary, if state is initial we shouldn't have to access hunique anyway */
#endif
- sc->sc_session = 0;
-
- sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */
-
- return err;
+ ppp_link_end(ppp); /* notify upper layers */
+ return;
}
/* Connection attempt aborted */
static void
pppoe_abort_connect(struct pppoe_softc *sc)
{
- printf("%c%c%"U16_F": could not establish connection\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
- sc->sc_state = PPPOE_STATE_CLOSING;
-
- sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */
-
- /* clear connection state */
- MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+ 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 */
@@ -895,10 +985,6 @@ pppoe_send_padr(struct pppoe_softc *sc)
size_t l1 = 0; /* XXX: gcc */
#endif /* PPPOE_TODO */
- if (sc->sc_state != PPPOE_STATE_PADR_SENT) {
- return ERR_CONN;
- }
-
len = 2 + 2 + 2 + 2 + sizeof(sc); /* service name, host unique */
#ifdef PPPOE_TODO
if (sc->sc_service_name != NULL) { /* service name tag maybe empty */
@@ -911,12 +997,12 @@ pppoe_send_padr(struct pppoe_softc *sc)
}
LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff",
sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff);
- pb = pbuf_alloc(PBUF_LINK, (u16_t)(sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len), PBUF_RAM);
+ 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 + sizeof (struct eth_hdr);
+ p = (u8_t*)pb->payload;
PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, len);
PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
#ifdef PPPOE_TODO
@@ -951,16 +1037,17 @@ pppoe_send_padt(struct netif *outgoing_if, u_int session, const u8_t *dest)
err_t res;
u8_t *p;
- pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN, PBUF_RAM);
+ 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_add_header(pb, 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, ((struct eth_addr *)outgoing_if->hwaddr)->addr, sizeof(ethhdr->src.addr));
+ 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);
@@ -980,22 +1067,18 @@ pppoe_send_pado(struct pppoe_softc *sc)
u8_t *p;
size_t len;
- if (sc->sc_state != PPPOE_STATE_PADO_SENT) {
- return ERR_CONN;
- }
-
/* calc length */
len = 0;
/* include ac_cookie */
len += 2 + 2 + sizeof(sc);
/* include hunique */
len += 2 + 2 + sc->sc_hunique_len;
- pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM);
+ 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 + sizeof (struct eth_hdr);
+ 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));
@@ -1014,10 +1097,6 @@ pppoe_send_pads(struct pppoe_softc *sc)
u8_t *p;
size_t len, l1 = 0; /* XXX: gcc */
- if (sc->sc_state != PPPOE_STATE_PADO_SENT) {
- return ERR_CONN;
- }
-
sc->sc_session = mono_time.tv_sec % 0xff + 1;
/* calc length */
len = 0;
@@ -1027,12 +1106,12 @@ pppoe_send_pads(struct pppoe_softc *sc)
l1 = strlen(sc->sc_service_name);
len += l1;
}
- pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM);
+ 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 + sizeof (struct eth_hdr);
+ 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) {
@@ -1049,31 +1128,24 @@ pppoe_send_pads(struct pppoe_softc *sc)
}
#endif
-err_t
+static err_t
pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb)
{
u8_t *p;
size_t len;
- /* are we ready to process data yet? */
- if (sc->sc_state < PPPOE_STATE_SESSION) {
- /*sppp_flush(&sc->sc_sppp.pp_if);*/
- pbuf_free(pb);
- return ERR_CONN;
- }
-
len = pb->tot_len;
- /* make room for Ethernet header - should not fail */
- if (pbuf_header(pb, sizeof(struct eth_hdr) + PPPOE_HEADERLEN) != 0) {
+ /* make room for PPPoE header - should not fail */
+ if (pbuf_add_header(pb, PPPOE_HEADERLEN) != 0) {
/* bail out */
- PPPDEBUG(LOG_ERR, ("pppoe: %c%c%"U16_F": pppoe_xmit: could not allocate room for header\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+ 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 + sizeof(struct eth_hdr);
+ p = (u8_t*)pb->payload;
PPPOE_ADD_HEADER(p, 0, sc->sc_session, len);
return pppoe_output(sc, pb);
@@ -1096,8 +1168,8 @@ pppoe_ifattach_hook(void *arg, struct pbuf **mp, struct netif *ifp, int dir)
}
if (sc->sc_sppp.pp_if.if_flags & IFF_UP) {
sc->sc_sppp.pp_if.if_flags &= ~(IFF_UP|IFF_RUNNING);
- printf("%c%c%"U16_F": ethernet interface detached, going down\n",
- sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ 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");
@@ -1107,6 +1179,7 @@ pppoe_ifattach_hook(void *arg, struct pbuf **mp, struct netif *ifp, int dir)
}
#endif
+#if 0 /* UNUSED */
static void
pppoe_clear_softc(struct pppoe_softc *sc, const char *message)
{
@@ -1115,18 +1188,8 @@ pppoe_clear_softc(struct pppoe_softc *sc, const char *message)
/* stop timer */
sys_untimeout(pppoe_timeout, sc);
PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x terminated, %s\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_session, message));
-
- /* fix our state */
sc->sc_state = PPPOE_STATE_INITIAL;
-
- /* notify upper layers */
- sc->sc_linkStatusCB(sc->sc_pd, 0);
-
- /* clean up softc */
- MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
- sc->sc_ac_cookie_len = 0;
- sc->sc_session = 0;
+ ppp_link_end(sc->pcb); /* notify upper layers - /!\ dangerous /!\ - see pppoe_disc_input() */
}
-
-#endif /* PPPOE_SUPPORT */
-
+#endif /* UNUSED */
+#endif /* PPP_SUPPORT && PPPOE_SUPPORT */
diff --git a/lwip/src/netif/ppp/pppol2tp.c b/lwip/src/netif/ppp/pppol2tp.c
new file mode 100644
index 0000000..cd49765
--- /dev/null
+++ b/lwip/src/netif/ppp/pppol2tp.c
@@ -0,0 +1,1138 @@
+/**
+ * @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_remove_header(ph, 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_remove_header(pb, 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_remove_header(p, 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_remove_header(p, 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 = pbuf_coalesce(p, PBUF_RAW);
+ 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_remove_header(p, 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));
+ LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+ }
+ 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));
+ LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+ }
+ 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));
+ LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+ }
+ 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));
+ LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+ }
+ 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));
+ LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+ 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));
+ LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+ }
+ 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));
+ LWIP_UNUSED_ARG(err); /* if PPPDEBUG is disabled */
+ }
+ 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_add_header(pb, 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/lwip/src/netif/ppp/pppos.c b/lwip/src/netif/ppp/pppos.c
new file mode 100644
index 0000000..706bc09
--- /dev/null
+++ b/lwip/src/netif/ppp/pppos.c
@@ -0,0 +1,881 @@
+/**
+ * @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;
+ }
+
+ /* Set nb->tot_len to actual payload length */
+ nb->tot_len = p->len;
+
+ /* 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;
+ }
+
+ /* Set nb->tot_len to actual payload length */
+ nb->tot_len = pb->tot_len;
+
+ /* 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_remove_header(inp, PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN);
+#endif /* IP_FORWARD || LWIP_IPV6_FORWARD */
+#if PPP_INPROC_IRQ_SAFE
+ if(tcpip_try_callback(pppos_input_callback, inp) != 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_remove_header(pb, sizeof(struct pppos_input_header))) {
+ LWIP_ASSERT("pbuf_remove_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/lwip/src/netif/ppp/randm.c b/lwip/src/netif/ppp/randm.c
deleted file mode 100644
index b736091..0000000
--- a/lwip/src/netif/ppp/randm.c
+++ /dev/null
@@ -1,249 +0,0 @@
-/*****************************************************************************
-* randm.c - Random number generator program file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* Copyright (c) 1998 by Global Election Systems Inc.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 98-06-03 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
-* Extracted from avos.
-*****************************************************************************/
-
-#include "lwip/opt.h"
-
-#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
-
-#include "md5.h"
-#include "randm.h"
-
-#include "ppp_impl.h"
-#include "pppdebug.h"
-
-#include <string.h>
-
-#if MD5_SUPPORT /* this module depends on MD5 */
-#define RANDPOOLSZ 16 /* Bytes stored in the pool of randomness. */
-
-/*****************************/
-/*** LOCAL DATA STRUCTURES ***/
-/*****************************/
-static char randPool[RANDPOOLSZ]; /* Pool of randomness. */
-static long randCount = 0; /* Pseudo-random incrementer */
-
-
-/***********************************/
-/*** PUBLIC FUNCTION DEFINITIONS ***/
-/***********************************/
-/*
- * Initialize the random number generator.
- *
- * Since this is to be called on power up, we don't have much
- * system randomess to work with. Here all we use is the
- * real-time clock. We'll accumulate more randomness as soon
- * as things start happening.
- */
-void
-avRandomInit()
-{
- avChurnRand(NULL, 0);
-}
-
-/*
- * Churn the randomness pool on a random event. Call this early and often
- * on random and semi-random system events to build randomness in time for
- * usage. For randomly timed events, pass a null pointer and a zero length
- * and this will use the system timer and other sources to add randomness.
- * If new random data is available, pass a pointer to that and it will be
- * included.
- *
- * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427
- */
-void
-avChurnRand(char *randData, u32_t randLen)
-{
- MD5_CTX md5;
-
- /* LWIP_DEBUGF(LOG_INFO, ("churnRand: %u@%P\n", randLen, randData)); */
- MD5Init(&md5);
- MD5Update(&md5, (u_char *)randPool, sizeof(randPool));
- if (randData) {
- MD5Update(&md5, (u_char *)randData, randLen);
- } else {
- struct {
- /* INCLUDE fields for any system sources of randomness */
- char foobar;
- } sysData;
-
- /* Load sysData fields here. */
- MD5Update(&md5, (u_char *)&sysData, sizeof(sysData));
- }
- MD5Final((u_char *)randPool, &md5);
-/* LWIP_DEBUGF(LOG_INFO, ("churnRand: -> 0\n")); */
-}
-
-/*
- * Use the random pool to generate random data. This degrades to pseudo
- * random when used faster than randomness is supplied using churnRand().
- * Note: It's important that there be sufficient randomness in randPool
- * before this is called for otherwise the range of the result may be
- * narrow enough to make a search feasible.
- *
- * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427
- *
- * XXX Why does he not just call churnRand() for each block? Probably
- * so that you don't ever publish the seed which could possibly help
- * predict future values.
- * XXX Why don't we preserve md5 between blocks and just update it with
- * randCount each time? Probably there is a weakness but I wish that
- * it was documented.
- */
-void
-avGenRand(char *buf, u32_t bufLen)
-{
- MD5_CTX md5;
- u_char tmp[16];
- u32_t n;
-
- while (bufLen > 0) {
- n = LWIP_MIN(bufLen, RANDPOOLSZ);
- MD5Init(&md5);
- MD5Update(&md5, (u_char *)randPool, sizeof(randPool));
- MD5Update(&md5, (u_char *)&randCount, sizeof(randCount));
- MD5Final(tmp, &md5);
- randCount++;
- MEMCPY(buf, tmp, n);
- buf += n;
- bufLen -= n;
- }
-}
-
-/*
- * Return a new random number.
- */
-u32_t
-avRandom()
-{
- u32_t newRand;
-
- avGenRand((char *)&newRand, sizeof(newRand));
-
- return newRand;
-}
-
-#else /* MD5_SUPPORT */
-
-/*****************************/
-/*** LOCAL DATA STRUCTURES ***/
-/*****************************/
-static int avRandomized = 0; /* Set when truely randomized. */
-static u32_t avRandomSeed = 0; /* Seed used for random number generation. */
-
-
-/***********************************/
-/*** PUBLIC FUNCTION DEFINITIONS ***/
-/***********************************/
-/*
- * Initialize the random number generator.
- *
- * Here we attempt to compute a random number seed but even if
- * it isn't random, we'll randomize it later.
- *
- * The current method uses the fields from the real time clock,
- * the idle process counter, the millisecond counter, and the
- * hardware timer tick counter. When this is invoked
- * in startup(), then the idle counter and timer values may
- * repeat after each boot and the real time clock may not be
- * operational. Thus we call it again on the first random
- * event.
- */
-void
-avRandomInit()
-{
-#if 0
- /* Get a pointer into the last 4 bytes of clockBuf. */
- u32_t *lptr1 = (u32_t *)((char *)&clockBuf[3]);
-
- /*
- * Initialize our seed using the real-time clock, the idle
- * counter, the millisecond timer, and the hardware timer
- * tick counter. The real-time clock and the hardware
- * tick counter are the best sources of randomness but
- * since the tick counter is only 16 bit (and truncated
- * at that), the idle counter and millisecond timer
- * (which may be small values) are added to help
- * randomize the lower 16 bits of the seed.
- */
- readClk();
- avRandomSeed += *(u32_t *)clockBuf + *lptr1 + OSIdleCtr
- + ppp_mtime() + ((u32_t)TM1 << 16) + TM1;
-#else
- avRandomSeed += sys_jiffies(); /* XXX */
-#endif
-
- /* Initialize the Borland random number generator. */
- srand((unsigned)avRandomSeed);
-}
-
-/*
- * Randomize our random seed value. Here we use the fact that
- * this function is called at *truely random* times by the polling
- * and network functions. Here we only get 16 bits of new random
- * value but we use the previous value to randomize the other 16
- * bits.
- */
-void
-avRandomize(void)
-{
- static u32_t last_jiffies;
-
- if (!avRandomized) {
- avRandomized = !0;
- avRandomInit();
- /* The initialization function also updates the seed. */
- } else {
- /* avRandomSeed += (avRandomSeed << 16) + TM1; */
- avRandomSeed += (sys_jiffies() - last_jiffies); /* XXX */
- }
- last_jiffies = sys_jiffies();
-}
-
-/*
- * Return a new random number.
- * Here we use the Borland rand() function to supply a pseudo random
- * number which we make truely random by combining it with our own
- * seed which is randomized by truely random events.
- * Thus the numbers will be truely random unless there have been no
- * operator or network events in which case it will be pseudo random
- * seeded by the real time clock.
- */
-u32_t
-avRandom()
-{
- return ((((u32_t)rand() << 16) + rand()) + avRandomSeed);
-}
-
-#endif /* MD5_SUPPORT */
-
-#endif /* PPP_SUPPORT */
diff --git a/lwip/src/netif/ppp/randm.h b/lwip/src/netif/ppp/randm.h
deleted file mode 100644
index a0984b0..0000000
--- a/lwip/src/netif/ppp/randm.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*****************************************************************************
-* randm.h - Random number generator header file.
-*
-* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
-* Copyright (c) 1998 Global Election Systems Inc.
-*
-* The authors hereby grant permission to use, copy, modify, distribute,
-* and license this software and its documentation for any purpose, provided
-* that existing copyright notices are retained in all copies and that this
-* notice and the following disclaimer are included verbatim in any
-* distributions. No written agreement, license, or royalty fee is required
-* for any of the authorized uses.
-*
-* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
-* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
-* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
-* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
-* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
-* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
-* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-*
-******************************************************************************
-* REVISION HISTORY
-*
-* 03-01-01 Marc Boucher <marc@mbsi.ca>
-* Ported to lwIP.
-* 98-05-29 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
-* Extracted from avos.
-*****************************************************************************/
-
-#ifndef RANDM_H
-#define RANDM_H
-
-/***********************
-*** PUBLIC FUNCTIONS ***
-***********************/
-/*
- * Initialize the random number generator.
- */
-void avRandomInit(void);
-
-/*
- * Churn the randomness pool on a random event. Call this early and often
- * on random and semi-random system events to build randomness in time for
- * usage. For randomly timed events, pass a null pointer and a zero length
- * and this will use the system timer and other sources to add randomness.
- * If new random data is available, pass a pointer to that and it will be
- * included.
- */
-void avChurnRand(char *randData, u32_t randLen);
-
-/*
- * Randomize our random seed value. To be called for truely random events
- * such as user operations and network traffic.
- */
-#if MD5_SUPPORT
-#define avRandomize() avChurnRand(NULL, 0)
-#else /* MD5_SUPPORT */
-void avRandomize(void);
-#endif /* MD5_SUPPORT */
-
-/*
- * Use the random pool to generate random data. This degrades to pseudo
- * random when used faster than randomness is supplied using churnRand().
- * Thus it's important to make sure that the results of this are not
- * published directly because one could predict the next result to at
- * least some degree. Also, it's important to get a good seed before
- * the first use.
- */
-void avGenRand(char *buf, u32_t bufLen);
-
-/*
- * Return a new random number.
- */
-u32_t avRandom(void);
-
-
-#endif /* RANDM_H */
diff --git a/lwip/src/netif/ppp/readme.txt b/lwip/src/netif/ppp/readme.txt
deleted file mode 100644
index 5be41b9..0000000
--- a/lwip/src/netif/ppp/readme.txt
+++ /dev/null
@@ -1,21 +0,0 @@
-About the PPP code:
-
-The PPP code is not our "own" code - we just copied it from pppd (http://ppp.samba.org/) and adapted it to lwIP.
-Unfortunately, not many here know their way around it too well. Back in 2009, we took the effort to see which
-version of pppd our code relates to and we're pretty much on 2.3.11 with some bugs from 2.4.x backported.
-
-Aside from simple code adaptions, there are some files that are different, however:
-- chpms.c/.h are named chap_ms.c/.h in the original pppd 2.3.11 sources
-- pap.c/.h are named upap.c/.h in the original pppd 2.3.11 sources
-- randm.c is a random generator not included in the original pppd
-- magic.c does not use the C library's random functions, but uses randm.c instead
-- vj.c/.h is an implementation of the Van Jacobson header compression algorithm adapted to lwIP pbufs,
- probably copied from one of the vjcompress.c files from pppd.
-- ppp.c, ppp.h and ppp_impl.h contain the adaption from pppd to lwIP. This is the "OS"-dependent part like there
- is an implementation for linux, xBSD etc. in the pppd sources.
-- ppp_oe.c is Marc Boucher's implementation based on NetBSD's if_pppoe.c
-
-There is of course potential for bugs in it, but when analyzing of reporting bugs, it is strongly encouraged to
-compare the code in question to pppd 2.3.11 (our basis) and newer versions (perhaps it's already fixed?) and to
-share this knowledge with us when reporting a bug.
-
diff --git a/lwip/src/netif/ppp/upap.c b/lwip/src/netif/ppp/upap.c
new file mode 100644
index 0000000..d00c2d7
--- /dev/null
+++ b/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/lwip/src/netif/ppp/utils.c b/lwip/src/netif/ppp/utils.c
new file mode 100644
index 0000000..008c633
--- /dev/null
+++ b/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/lwip/src/netif/ppp/vj.c b/lwip/src/netif/ppp/vj.c
index 40fdad1..62dbf54 100644
--- a/lwip/src/netif/ppp/vj.c
+++ b/lwip/src/netif/ppp/vj.c
@@ -28,19 +28,16 @@
* for a 16 bit processor.
*/
-#include "lwip/opt.h"
+#include "netif/ppp/ppp_opts.h"
+#if PPP_SUPPORT && VJ_SUPPORT /* don't build if not configured for use in lwipopts.h */
-#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+#include "netif/ppp/ppp_impl.h"
+#include "netif/ppp/pppdebug.h"
-#include "ppp_impl.h"
-#include "pppdebug.h"
-
-#include "vj.h"
+#include "netif/ppp/vj.h"
#include <string.h>
-#if VJ_SUPPORT
-
#if LINK_STATS
#define INCR(counter) ++comp->stats.counter
#else
@@ -50,9 +47,9 @@
void
vj_compress_init(struct vjcompress *comp)
{
- register u_char i;
- register struct cstate *tstate = comp->tstate;
-
+ u8_t i;
+ struct cstate *tstate = comp->tstate;
+
#if MAX_SLOTS == 0
memset((char *)comp, 0, sizeof(*comp));
#endif
@@ -76,57 +73,82 @@ vj_compress_init(struct vjcompress *comp)
* form).
*/
#define ENCODE(n) { \
- if ((u_short)(n) >= 256) { \
+ if ((u16_t)(n) >= 256) { \
*cp++ = 0; \
- cp[1] = (u_char)(n); \
- cp[0] = (u_char)((n) >> 8); \
+ cp[1] = (u8_t)(n); \
+ cp[0] = (u8_t)((n) >> 8); \
cp += 2; \
} else { \
- *cp++ = (u_char)(n); \
+ *cp++ = (u8_t)(n); \
} \
}
#define ENCODEZ(n) { \
- if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \
+ if ((u16_t)(n) >= 256 || (u16_t)(n) == 0) { \
*cp++ = 0; \
- cp[1] = (u_char)(n); \
- cp[0] = (u_char)((n) >> 8); \
+ cp[1] = (u8_t)(n); \
+ cp[0] = (u8_t)((n) >> 8); \
cp += 2; \
} else { \
- *cp++ = (u_char)(n); \
+ *cp++ = (u8_t)(n); \
} \
}
#define DECODEL(f) { \
if (*cp == 0) {\
- u32_t tmp = ntohl(f) + ((cp[1] << 8) | cp[2]); \
- (f) = htonl(tmp); \
+ u32_t tmp_ = lwip_ntohl(f) + ((cp[1] << 8) | cp[2]); \
+ (f) = lwip_htonl(tmp_); \
cp += 3; \
} else { \
- u32_t tmp = ntohl(f) + (u32_t)*cp++; \
- (f) = htonl(tmp); \
+ u32_t tmp_ = lwip_ntohl(f) + (u32_t)*cp++; \
+ (f) = lwip_htonl(tmp_); \
} \
}
#define DECODES(f) { \
if (*cp == 0) {\
- u_short tmp = ntohs(f) + (((u_short)cp[1] << 8) | cp[2]); \
- (f) = htons(tmp); \
+ u16_t tmp_ = lwip_ntohs(f) + (((u16_t)cp[1] << 8) | cp[2]); \
+ (f) = lwip_htons(tmp_); \
cp += 3; \
} else { \
- u_short tmp = ntohs(f) + (u_short)*cp++; \
- (f) = htons(tmp); \
+ u16_t tmp_ = lwip_ntohs(f) + (u16_t)*cp++; \
+ (f) = lwip_htons(tmp_); \
} \
}
#define DECODEU(f) { \
if (*cp == 0) {\
- (f) = htons(((u_short)cp[1] << 8) | cp[2]); \
+ (f) = lwip_htons(((u16_t)cp[1] << 8) | cp[2]); \
cp += 3; \
} else { \
- (f) = htons((u_short)*cp++); \
+ (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
@@ -134,21 +156,23 @@ vj_compress_init(struct vjcompress *comp)
* Return the VJ type code indicating whether or not the packet was
* compressed.
*/
-u_int
-vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
+u8_t
+vj_compress_tcp(struct vjcompress *comp, struct pbuf **pb)
{
- register struct ip_hdr *ip = (struct ip_hdr *)pb->payload;
- register struct cstate *cs = comp->last_cs->cs_next;
- register u_short hlen = IPH_HL(ip);
- register struct tcp_hdr *oth;
- register struct tcp_hdr *th;
- register u_short deltaS, deltaA;
- register u_long deltaL;
- register u_int changes = 0;
- u_char new_seq[16];
- register u_char *cp = new_seq;
-
- /*
+ 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) {
@@ -158,15 +182,34 @@ vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
/*
* 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).
+ * set).
*/
- if ((IPH_OFFSET(ip) & PP_HTONS(0x3fff)) || pb->tot_len < 40) {
+ if ((IPH_OFFSET(ip) & PP_HTONS(0x3fff)) || np->tot_len < 40) {
return (TYPE_IP);
}
- th = (struct tcp_hdr *)&((long *)ip)[hlen];
+ 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_clone(PBUF_RAW, PBUF_POOL, *pb);
+ if (!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
@@ -175,9 +218,9 @@ vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
* again & we don't have to do any reordering if it's used.
*/
INCR(vjs_packets);
- if (!ip_addr_cmp(&ip->src, &cs->cs_ip.src)
- || !ip_addr_cmp(&ip->dest, &cs->cs_ip.dest)
- || *(long *)th != ((long *)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]) {
+ 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.
*
@@ -190,15 +233,15 @@ vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
* states via linear search. If we don't find a state
* for the datagram, the oldest state is (re-)used.
*/
- register struct cstate *lcs;
- register struct cstate *lastcs = comp->last_cs;
-
+ struct cstate *lcs;
+ struct cstate *lastcs = comp->last_cs;
+
do {
lcs = cs; cs = cs->cs_next;
INCR(vjs_searches);
- if (ip_addr_cmp(&ip->src, &cs->cs_ip.src)
- && ip_addr_cmp(&ip->dest, &cs->cs_ip.dest)
- && *(long *)th == ((long *)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]) {
+ 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);
@@ -213,12 +256,6 @@ vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
*/
INCR(vjs_misses);
comp->last_cs = lcs;
- hlen += TCPH_HDRLEN(th);
- hlen <<= 2;
- /* Check that the IP/TCP headers are contained in the first buffer. */
- if (hlen > pb->len) {
- return (TYPE_IP);
- }
goto uncompressed;
found:
@@ -234,15 +271,8 @@ vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
}
}
- oth = (struct tcp_hdr *)&((long *)&cs->cs_ip)[hlen];
- deltaS = hlen;
- hlen += TCPH_HDRLEN(th);
- hlen <<= 2;
- /* Check that the IP/TCP headers are contained in the first buffer. */
- if (hlen > pb->len) {
- PPPDEBUG(LOG_INFO, ("vj_compress_tcp: header len %d spans buffers\n", hlen));
- return (TYPE_IP);
- }
+ 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
@@ -255,11 +285,11 @@ vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
* different between the previous & current datagram, we send the
* current datagram `uncompressed'.
*/
- if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0]
- || ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3]
- || ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4]
- || TCPH_HDRLEN(th) != TCPH_HDRLEN(oth)
- || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2))
+ 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;
}
@@ -271,7 +301,7 @@ vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
* needed in this section of code).
*/
if (TCPH_FLAGS(th) & TCP_URG) {
- deltaS = ntohs(th->urgp);
+ deltaS = lwip_ntohs(th->urgp);
ENCODEZ(deltaS);
changes |= NEW_U;
} else if (th->urgp != oth->urgp) {
@@ -282,25 +312,25 @@ vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
goto uncompressed;
}
- if ((deltaS = (u_short)(ntohs(th->wnd) - ntohs(oth->wnd))) != 0) {
+ if ((deltaS = (u16_t)(lwip_ntohs(th->wnd) - lwip_ntohs(oth->wnd))) != 0) {
ENCODE(deltaS);
changes |= NEW_W;
}
- if ((deltaL = ntohl(th->ackno) - ntohl(oth->ackno)) != 0) {
+ if ((deltaL = lwip_ntohl(th->ackno) - lwip_ntohl(oth->ackno)) != 0) {
if (deltaL > 0xffff) {
goto uncompressed;
}
- deltaA = (u_short)deltaL;
+ deltaA = (u16_t)deltaL;
ENCODE(deltaA);
changes |= NEW_A;
}
- if ((deltaL = ntohl(th->seqno) - ntohl(oth->seqno)) != 0) {
+ if ((deltaL = lwip_ntohl(th->seqno) - lwip_ntohl(oth->seqno)) != 0) {
if (deltaL > 0xffff) {
goto uncompressed;
}
- deltaS = (u_short)deltaL;
+ deltaS = (u16_t)deltaL;
ENCODE(deltaS);
changes |= NEW_S;
}
@@ -316,11 +346,11 @@ vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
* in case the other side missed the compressed version.
*/
if (IPH_LEN(ip) != IPH_LEN(&cs->cs_ip) &&
- ntohs(IPH_LEN(&cs->cs_ip)) == hlen) {
+ lwip_ntohs(IPH_LEN(&cs->cs_ip)) == hlen) {
break;
}
-
- /* (fall through) */
+ /* no break */
+ /* fall through */
case SPECIAL_I:
case SPECIAL_D:
@@ -331,7 +361,7 @@ vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
goto uncompressed;
case NEW_S|NEW_A:
- if (deltaS == deltaA && deltaS == ntohs(IPH_LEN(&cs->cs_ip)) - hlen) {
+ if (deltaS == deltaA && deltaS == lwip_ntohs(IPH_LEN(&cs->cs_ip)) - hlen) {
/* special case for echoed terminal traffic */
changes = SPECIAL_I;
cp = new_seq;
@@ -339,15 +369,17 @@ vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
break;
case NEW_S:
- if (deltaS == ntohs(IPH_LEN(&cs->cs_ip)) - hlen) {
+ 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 = (u_short)(ntohs(IPH_ID(ip)) - ntohs(IPH_ID(&cs->cs_ip)));
+ deltaS = (u16_t)(lwip_ntohs(IPH_ID(ip)) - lwip_ntohs(IPH_ID(&cs->cs_ip)));
if (deltaS != 1) {
ENCODEZ(deltaS);
changes |= NEW_I;
@@ -359,8 +391,8 @@ vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
* Grab the cksum before we overwrite it below. Then update our
* state with this packet's header.
*/
- deltaA = ntohs(th->chksum);
- BCOPY(ip, &cs->cs_ip, hlen);
+ deltaA = lwip_ntohs(th->chksum);
+ MEMCPY(&cs->cs_ip, ip, hlen);
/*
* We want to use the original packet as our compressed packet.
@@ -371,29 +403,29 @@ vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
* many bytes of the original packet to toss so subtract the two to
* get the new packet size.
*/
- deltaS = (u_short)(cp - new_seq);
+ 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(pb, -hlen)){
+ if (pbuf_remove_header(np, hlen)){
/* Can we cope with this failing? Just assert for now */
- LWIP_ASSERT("pbuf_header failed\n", 0);
+ LWIP_ASSERT("pbuf_remove_header failed\n", 0);
}
- cp = (u_char *)pb->payload;
- *cp++ = (u_char)(changes | NEW_C);
+ cp = (u8_t*)np->payload;
+ *cp++ = (u8_t)(changes | NEW_C);
*cp++ = cs->cs_id;
} else {
hlen -= deltaS + 3;
- if(pbuf_header(pb, -hlen)) {
+ if (pbuf_remove_header(np, hlen)) {
/* Can we cope with this failing? Just assert for now */
- LWIP_ASSERT("pbuf_header failed\n", 0);
+ LWIP_ASSERT("pbuf_remove_header failed\n", 0);
}
- cp = (u_char *)pb->payload;
- *cp++ = (u_char)changes;
+ cp = (u8_t*)np->payload;
+ *cp++ = (u8_t)changes;
}
- *cp++ = (u_char)(deltaA >> 8);
- *cp++ = (u_char)deltaA;
- BCOPY(new_seq, cp, deltaS);
+ *cp++ = (u8_t)(deltaA >> 8);
+ *cp++ = (u8_t)deltaA;
+ MEMCPY(cp, new_seq, deltaS);
INCR(vjs_compressed);
return (TYPE_COMPRESSED_TCP);
@@ -403,7 +435,7 @@ vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
* to use on future compressed packets in the protocol field).
*/
uncompressed:
- BCOPY(ip, &cs->cs_ip, hlen);
+ MEMCPY(&cs->cs_ip, ip, hlen);
IPH_PROTO_SET(ip, cs->cs_id);
comp->last_xmit = cs->cs_id;
return (TYPE_UNCOMPRESSED_TCP);
@@ -426,28 +458,29 @@ vj_uncompress_err(struct vjcompress *comp)
int
vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp)
{
- register u_int hlen;
- register struct cstate *cs;
- register struct ip_hdr *ip;
-
+ 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)
+ || (hlen += TCPH_HDRLEN_BYTES((struct tcp_hdr *)&((char *)ip)[hlen]))
> nb->len
|| hlen > MAX_HDR) {
- PPPDEBUG(LOG_INFO, ("vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n",
+ 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);
+ vj_uncompress_err(comp);
return -1;
}
cs = &comp->rstate[comp->last_recv = IPH_PROTO(ip)];
comp->flags &=~ VJF_TOSS;
IPH_PROTO_SET(ip, IP_PROTO_TCP);
- BCOPY(ip, &cs->cs_ip, hlen);
- cs->cs_hlen = (u_short)hlen;
+ /* copy from/to bigger buffers checked above instead of cs->cs_ip and ip
+ just to help static code analysis to see this is correct ;-) */
+ MEMCPY(&cs->cs_hdr, nb->payload, hlen);
+ cs->cs_hlen = (u16_t)hlen;
INCR(vjs_uncompressedin);
return 0;
}
@@ -456,28 +489,28 @@ vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp)
* 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.
+ * The first buffer must include the entire compressed TCP/IP header.
* This procedure replaces the compressed header with the uncompressed
* header and returns the length of the VJ header.
*/
int
vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp)
{
- u_char *cp;
+ u8_t *cp;
struct tcp_hdr *th;
struct cstate *cs;
- u_short *bp;
+ struct vj_u16_t *bp;
struct pbuf *n0 = *nb;
u32_t tmp;
- u_int vjlen, hlen, changes;
+ u32_t vjlen, hlen, changes;
INCR(vjs_compressedin);
- cp = (u_char *)n0->payload;
+ 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 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));
@@ -487,10 +520,10 @@ vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp)
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.
+ * explicit state index, we have to toss the packet.
*/
if (comp->flags & VJF_TOSS) {
PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: tossing\n"));
@@ -500,8 +533,8 @@ vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp)
}
cs = &comp->rstate[comp->last_recv];
hlen = IPH_HL(&cs->cs_ip) << 2;
- th = (struct tcp_hdr *)&((u_char *)&cs->cs_ip)[hlen];
- th->chksum = htons((*cp << 8) | cp[1]);
+ 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);
@@ -512,19 +545,19 @@ vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp)
switch (changes & SPECIALS_MASK) {
case SPECIAL_I:
{
- register u32_t i = ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen;
+ u32_t i = lwip_ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen;
/* some compilers can't nest inline assembler.. */
- tmp = ntohl(th->ackno) + i;
- th->ackno = htonl(tmp);
- tmp = ntohl(th->seqno) + i;
- th->seqno = htonl(tmp);
+ 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 = ntohl(th->seqno) + ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen;
- th->seqno = htonl(tmp);
+ tmp = lwip_ntohl(th->seqno) + lwip_ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen;
+ th->seqno = lwip_htonl(tmp);
break;
default:
@@ -548,8 +581,8 @@ vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp)
if (changes & NEW_I) {
DECODES(cs->cs_ip._id);
} else {
- IPH_ID_SET(&cs->cs_ip, ntohs(IPH_ID(&cs->cs_ip)) + 1);
- IPH_ID_SET(&cs->cs_ip, htons(IPH_ID(&cs->cs_ip)));
+ 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)));
}
/*
@@ -557,62 +590,65 @@ vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp)
* packet. Fill in the IP total length and update the IP
* header checksum.
*/
- vjlen = (u_short)(cp - (u_char*)n0->payload);
+ 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)
+ * this but the old slip framing won't)
*/
- PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: head buffer %d too short %d\n",
+ PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: head buffer %d too short %d\n",
n0->len, vjlen));
goto bad;
}
#if BYTE_ORDER == LITTLE_ENDIAN
tmp = n0->tot_len - vjlen + cs->cs_hlen;
- IPH_LEN_SET(&cs->cs_ip, htons((u_short)tmp));
+ IPH_LEN_SET(&cs->cs_ip, lwip_htons((u16_t)tmp));
#else
- IPH_LEN_SET(&cs->cs_ip, htons(n0->tot_len - vjlen + cs->cs_hlen));
+ IPH_LEN_SET(&cs->cs_ip, lwip_htons(n0->tot_len - vjlen + cs->cs_hlen));
#endif
/* recompute the ip header checksum */
- bp = (u_short *) &cs->cs_ip;
+ bp = (struct vj_u16_t*) &cs->cs_ip;
IPH_CHKSUM_SET(&cs->cs_ip, 0);
for (tmp = 0; hlen > 0; hlen -= 2) {
- tmp += *bp++;
+ tmp += (*bp++).v;
}
tmp = (tmp & 0xffff) + (tmp >> 16);
tmp = (tmp & 0xffff) + (tmp >> 16);
- IPH_CHKSUM_SET(&cs->cs_ip, (u_short)(~tmp));
-
+ 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)))) {
+ if (pbuf_remove_header(n0, vjlen)) {
/* Can we cope with this failing? Just assert for now */
- LWIP_ASSERT("pbuf_header failed\n", 0);
+ LWIP_ASSERT("pbuf_remove_header failed\n", 0);
goto bad;
}
if(LWIP_MEM_ALIGN(n0->payload) != n0->payload) {
- struct pbuf *np, *q;
- u8_t *bufptr;
+ struct pbuf *np;
+#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, -cs->cs_hlen)) {
+ if (pbuf_remove_header(np, cs->cs_hlen)) {
/* Can we cope with this failing? Just assert for now */
- LWIP_ASSERT("pbuf_header failed\n", 0);
+ LWIP_ASSERT("pbuf_remove_header failed\n", 0);
goto bad;
}
- bufptr = n0->payload;
- for(q = np; q != NULL; q = q->next) {
- MEMCPY(q->payload, bufptr, q->len);
- bufptr += q->len;
- }
+ pbuf_take(np, n0->payload, n0->len);
if(n0->next) {
pbuf_chain(np, n0->next);
@@ -622,7 +658,7 @@ vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp)
n0 = np;
}
- if(pbuf_header(n0, cs->cs_hlen)) {
+ if (pbuf_add_header(n0, cs->cs_hlen)) {
struct pbuf *np;
LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs->cs_hlen <= PBUF_POOL_BUFSIZE);
@@ -642,11 +678,8 @@ vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp)
return vjlen;
bad:
- comp->flags |= VJF_TOSS;
- INCR(vjs_errorin);
+ vj_uncompress_err(comp);
return (-1);
}
-#endif /* VJ_SUPPORT */
-
-#endif /* PPP_SUPPORT */
+#endif /* PPP_SUPPORT && VJ_SUPPORT */
diff --git a/lwip/src/netif/slipif.c b/lwip/src/netif/slipif.c
index 137ba89..f2a4edb 100644
--- a/lwip/src/netif/slipif.c
+++ b/lwip/src/netif/slipif.c
@@ -6,66 +6,68 @@
/*
* Copyright (c) 2001-2004 Swedish Institute of Computer Science.
- * All rights reserved.
+ * All rights reserved.
*
- * Redistribution and use in source and binary forms, with or without
- * modification, are permitted provided that the following conditions
- * are met:
- * 1. Redistributions of source code must retain the above copyright
- * notice, this list of conditions and the following disclaimer.
- * 2. Redistributions in binary form must reproduce 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.
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 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>
+ * Author: Magnus Ivarsson <magnus.ivarsson(at)volvo.com>
* Simon Goldschmidt
+ */
+
+
+/**
+ * @defgroup slipif SLIP
+ * @ingroup netifs
+ *
+ * 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:
+ * 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.
+ * 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().
+ * 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!).
- *
- */
-
-/*
- * This is an arch independent SLIP netif. The specific serial hooks must be
- * provided by another file. They are sio_open, sio_read/sio_tryread and sio_send
+ *
*/
#include "netif/slipif.h"
#include "lwip/opt.h"
-#if LWIP_HAVE_SLIPIF
-
#include "lwip/def.h"
#include "lwip/pbuf.h"
#include "lwip/stats.h"
#include "lwip/snmp.h"
-#include "lwip/sio.h"
#include "lwip/sys.h"
+#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) */
@@ -87,7 +89,7 @@
enum slipif_recv_state {
SLIP_RECV_NORMAL,
- SLIP_RECV_ESCAPE,
+ SLIP_RECV_ESCAPE
};
struct slipif_priv {
@@ -107,7 +109,7 @@ struct slipif_priv {
* Uses the serial layer's sio_send()
*
* @param netif the lwip network interface structure for this slipif
- * @param p the pbuf chaing packet to send
+ * @param p the pbuf chain packet to send
* @return always returns ERR_OK since the serial layer does not provide return values
*/
static err_t
@@ -122,8 +124,8 @@ slipif_output(struct netif *netif, struct pbuf *p)
LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
LWIP_ASSERT("p != NULL", (p != NULL));
- LWIP_DEBUGF(SLIP_DEBUG, ("slipif_output(%"U16_F"): sending %"U16_F" bytes\n", (u16_t)netif->num, p->tot_len));
- priv = netif->state;
+ LWIP_DEBUGF(SLIP_DEBUG, ("slipif_output: sending %"U16_F" bytes\n", p->tot_len));
+ priv = (struct slipif_priv *)netif->state;
/* Send pbuf out on the serial I/O device. */
/* Start with packet delimiter. */
@@ -155,22 +157,24 @@ slipif_output(struct netif *netif, struct pbuf *p)
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 chaing packet to send
+ * @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, ip_addr_t *ipaddr)
+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
/**
@@ -179,12 +183,12 @@ slipif_output_v4(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr)
* Uses the serial layer's sio_send()
*
* @param netif the lwip network interface structure for this slipif
- * @param p the pbuf chaing packet to send
+ * @param 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, ip6_addr_t *ipaddr)
+slipif_output_v6(struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr)
{
LWIP_UNUSED_ARG(ipaddr);
return slipif_output(netif, p);
@@ -208,7 +212,7 @@ slipif_rxbyte(struct netif *netif, u8_t c)
LWIP_ASSERT("netif != NULL", (netif != NULL));
LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
- priv = netif->state;
+ priv = (struct slipif_priv *)netif->state;
switch (priv->state) {
case SLIP_RECV_NORMAL:
@@ -231,6 +235,8 @@ slipif_rxbyte(struct netif *netif, u8_t c)
case SLIP_ESC:
priv->state = SLIP_RECV_ESCAPE;
return NULL;
+ default:
+ break;
} /* end switch (c) */
break;
case SLIP_RECV_ESCAPE:
@@ -243,16 +249,20 @@ slipif_rxbyte(struct netif *netif, u8_t c)
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_POOL);
+ 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);
@@ -294,7 +304,7 @@ slipif_rxbyte(struct netif *netif, u8_t c)
/** Like slipif_rxbyte, but passes completed packets to netif->input
*
* @param netif The lwip network interface structure for this slipif
- * @param data received character
+ * @param c received character
*/
static void
slipif_rxbyte_input(struct netif *netif, u8_t c)
@@ -342,9 +352,7 @@ slipif_loop_thread(void *nf)
* 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.
+ * @note If netif->state is interpreted as an u8_t serial port number.
*
*/
err_t
@@ -353,7 +361,10 @@ 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));
+ /* netif->state contains serial port number */
+ sio_num = LWIP_PTR_NUMERIC_CAST(u8_t, netif->state);
+
+ LWIP_DEBUGF(SLIP_DEBUG, ("slipif_init: netif->num=%"U16_F"\n", (u16_t)sio_num));
/* Allocate private data */
priv = (struct slipif_priv *)mem_malloc(sizeof(struct slipif_priv));
@@ -363,19 +374,14 @@ slipif_init(struct netif *netif)
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->flags |= NETIF_FLAG_POINTTOPOINT;
- /* netif->state or netif->num contain the port number */
- if (netif->state != NULL) {
- sio_num = *(u8_t*)netif->state;
- } else {
- sio_num = netif->num;
- }
/* Try to open the serial port. */
priv->sd = sio_open(sio_num);
if (!priv->sd) {
@@ -397,7 +403,7 @@ slipif_init(struct netif *netif)
netif->state = priv;
/* initialize the snmp variables and counters inside the struct netif */
- NETIF_INIT_SNMP(netif, snmp_ifType_slip, SLIP_SIO_SPEED(priv->sd));
+ 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. */
@@ -486,7 +492,7 @@ slipif_rxbyte_enqueue(struct netif *netif, u8_t data)
#if SLIP_RX_QUEUE
/* queue multiple pbufs */
struct pbuf *q = p;
- while(q->next != NULL) {
+ while (q->next != NULL) {
q = q->next;
}
q->next = p;
@@ -542,5 +548,3 @@ slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len)
}
}
#endif /* SLIP_RX_FROM_ISR */
-
-#endif /* LWIP_HAVE_SLIPIF */
diff --git a/lwip/test/fuzz/Makefile b/lwip/test/fuzz/Makefile
new file mode 100644
index 0000000..67ffb19
--- /dev/null
+++ b/lwip/test/fuzz/Makefile
@@ -0,0 +1,53 @@
+#
+# 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>
+#
+
+all compile: lwip_fuzz
+.PHONY: all clean
+
+CC=afl-gcc
+LDFLAGS=-lm
+CFLAGS=-O0
+
+CONTRIBDIR=../../../lwip-contrib
+include $(CONTRIBDIR)/ports/unix/Common.mk
+
+clean:
+ rm -f *.o $(LWIPLIBCOMMON) lwip_fuzz *.s .depend* *.core core
+
+depend dep: .depend
+
+include .depend
+
+.depend: fuzz.c $(LWIPFILES) $(APPFILES)
+ $(CCDEP) $(CFLAGS) -MM $^ > .depend || rm -f .depend
+
+lwip_fuzz: .depend $(LWIPLIBCOMMON) fuzz.o
+ $(CC) $(CFLAGS) -o lwip_fuzz fuzz.o $(LWIPLIBCOMMON) $(LDFLAGS)
diff --git a/lwip/test/fuzz/README b/lwip/test/fuzz/README
new file mode 100644
index 0000000..1d1e3d8
--- /dev/null
+++ b/lwip/test/fuzz/README
@@ -0,0 +1,34 @@
+
+Fuzzing the lwIP stack (afl-fuzz requires linux/unix or similar)
+
+This directory contains a small app that reads Ethernet frames from stdin and
+processes them. It is used together with the 'american fuzzy lop' tool (found
+at http://lcamtuf.coredump.cx/afl/) and the sample inputs to test how
+unexpected inputs are handled. The afl tool will read the known inputs, and
+try to modify them to exercise as many code paths as possible, by instrumenting
+the code and keeping track of which code is executed.
+
+Just running make will produce the test program.
+
+Then run afl with:
+
+afl-fuzz -i inputs/<INPUT> -o output ./lwip_fuzz
+
+and it should start working. It will probably complain about CPU scheduler,
+set AFL_SKIP_CPUFREQ=1 to ignore it.
+If it complains about invalid "/proc/sys/kernel/core_pattern" setting, try
+executing "sudo bash -c 'echo core > /proc/sys/kernel/core_pattern'".
+
+The input is split into different subdirectories since they test different
+parts of the code, and since you want to run one instance of afl-fuzz on each
+core.
+
+When afl finds a crash or a hang, the input that caused it will be placed in
+the output directory. If you have hexdump and text2pcap tools installed,
+running output_to_pcap.sh <outputdir> will create pcap files for each input
+file to simplify viewing in wireshark.
+
+The lwipopts.h file needs to have checksum checking off, otherwise almost every
+packet will be discarded because of that. The other options can be tuned to
+expose different parts of the code.
+
diff --git a/lwip/test/fuzz/config.h b/lwip/test/fuzz/config.h
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/lwip/test/fuzz/config.h
diff --git a/lwip/test/fuzz/fuzz.c b/lwip/test/fuzz/fuzz.c
new file mode 100644
index 0000000..a9157f1
--- /dev/null
+++ b/lwip/test/fuzz/fuzz.c
@@ -0,0 +1,136 @@
+/*
+ * 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: Erik Ekman <erik@kryo.se>
+ *
+ */
+
+#include "lwip/init.h"
+#include "lwip/netif.h"
+#include "netif/etharp.h"
+#if LWIP_IPV6
+#include "lwip/ethip6.h"
+#include "lwip/nd6.h"
+#endif
+#include <string.h>
+#include <stdio.h>
+
+/* no-op send function */
+static err_t lwip_tx_func(struct netif *netif, struct pbuf *p)
+{
+ LWIP_UNUSED_ARG(netif);
+ LWIP_UNUSED_ARG(p);
+ return ERR_OK;
+}
+
+static err_t testif_init(struct netif *netif)
+{
+ netif->name[0] = 'f';
+ netif->name[1] = 'z';
+ netif->output = etharp_output;
+ netif->linkoutput = lwip_tx_func;
+ netif->mtu = 1500;
+ netif->hwaddr_len = 6;
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP;
+
+ netif->hwaddr[0] = 0x00;
+ netif->hwaddr[1] = 0x23;
+ netif->hwaddr[2] = 0xC1;
+ netif->hwaddr[3] = 0xDE;
+ netif->hwaddr[4] = 0xD0;
+ netif->hwaddr[5] = 0x0D;
+
+#if LWIP_IPV6
+ netif->output_ip6 = ethip6_output;
+ netif->ip6_autoconfig_enabled = 1;
+ netif_create_ip6_linklocal_address(netif, 1);
+ netif->flags |= NETIF_FLAG_MLD6;
+#endif
+
+ return ERR_OK;
+}
+
+static void input_pkt(struct netif *netif, const u8_t *data, size_t len)
+{
+ struct pbuf *p, *q;
+ err_t err;
+
+ LWIP_ASSERT("pkt too big", len <= 0xFFFF);
+ p = pbuf_alloc(PBUF_RAW, (u16_t)len, PBUF_POOL);
+ LWIP_ASSERT("alloc failed", p);
+ for(q = p; q != NULL; q = q->next) {
+ MEMCPY(q->payload, data, q->len);
+ data += q->len;
+ }
+ err = netif->input(p, netif);
+ if (err != ERR_OK) {
+ pbuf_free(p);
+ }
+}
+
+int main(int argc, char** argv)
+{
+ struct netif net_test;
+ ip4_addr_t addr;
+ ip4_addr_t netmask;
+ ip4_addr_t gw;
+ u8_t pktbuf[2000];
+ size_t len;
+
+ lwip_init();
+
+ IP4_ADDR(&addr, 172, 30, 115, 84);
+ IP4_ADDR(&netmask, 255, 255, 255, 0);
+ IP4_ADDR(&gw, 172, 30, 115, 1);
+
+ netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
+ netif_set_up(&net_test);
+
+#if LWIP_IPV6
+ nd6_tmr(); /* tick nd to join multicast groups */
+#endif
+
+ if(argc > 1) {
+ FILE* f;
+ const char* filename;
+ printf("reading input from file... ");
+ fflush(stdout);
+ filename = argv[1];
+ LWIP_ASSERT("invalid filename", filename != NULL);
+ f = fopen(filename, "rb");
+ LWIP_ASSERT("open failed", f != NULL);
+ len = fread(pktbuf, 1, sizeof(pktbuf), f);
+ fclose(f);
+ printf("testing file: \"%s\"...\r\n", filename);
+ } else {
+ len = fread(pktbuf, 1, sizeof(pktbuf), stdin);
+ }
+ input_pkt(&net_test, pktbuf, len);
+
+ return 0;
+}
diff --git a/lwip/test/fuzz/inputs/arp/arp_req.bin b/lwip/test/fuzz/inputs/arp/arp_req.bin
new file mode 100644
index 0000000..b317334
--- /dev/null
+++ b/lwip/test/fuzz/inputs/arp/arp_req.bin
Binary files differ
diff --git a/lwip/test/fuzz/inputs/icmp/icmp_ping.bin b/lwip/test/fuzz/inputs/icmp/icmp_ping.bin
new file mode 100644
index 0000000..87e1ea7
--- /dev/null
+++ b/lwip/test/fuzz/inputs/icmp/icmp_ping.bin
Binary files differ
diff --git a/lwip/test/fuzz/inputs/ipv6/neighbor_solicitation.bin b/lwip/test/fuzz/inputs/ipv6/neighbor_solicitation.bin
new file mode 100644
index 0000000..d2f921c
--- /dev/null
+++ b/lwip/test/fuzz/inputs/ipv6/neighbor_solicitation.bin
Binary files differ
diff --git a/lwip/test/fuzz/inputs/ipv6/router_adv.bin b/lwip/test/fuzz/inputs/ipv6/router_adv.bin
new file mode 100644
index 0000000..3aa9615
--- /dev/null
+++ b/lwip/test/fuzz/inputs/ipv6/router_adv.bin
Binary files differ
diff --git a/lwip/test/fuzz/inputs/tcp/tcp_syn.bin b/lwip/test/fuzz/inputs/tcp/tcp_syn.bin
new file mode 100644
index 0000000..d77f6d2
--- /dev/null
+++ b/lwip/test/fuzz/inputs/tcp/tcp_syn.bin
Binary files differ
diff --git a/lwip/test/fuzz/inputs/udp/udp_port_5000.bin b/lwip/test/fuzz/inputs/udp/udp_port_5000.bin
new file mode 100644
index 0000000..d77e267
--- /dev/null
+++ b/lwip/test/fuzz/inputs/udp/udp_port_5000.bin
Binary files differ
diff --git a/lwip/test/fuzz/lwipopts.h b/lwip/test/fuzz/lwipopts.h
new file mode 100644
index 0000000..4aff09b
--- /dev/null
+++ b/lwip/test/fuzz/lwipopts.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+#ifndef LWIP_HDR_LWIPOPTS_H__
+#define LWIP_HDR_LWIPOPTS_H__
+
+/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */
+#define NO_SYS 1
+#define LWIP_NETCONN 0
+#define LWIP_SOCKET 0
+#define SYS_LIGHTWEIGHT_PROT 0
+
+#define LWIP_IPV6 1
+#define IPV6_FRAG_COPYHEADER 1
+#define LWIP_IPV6_DUP_DETECT_ATTEMPTS 0
+
+/* Enable DHCP to test it */
+#define LWIP_DHCP 1
+
+/* Turn off checksum verification of fuzzed data */
+#define CHECKSUM_CHECK_IP 0
+#define CHECKSUM_CHECK_UDP 0
+#define CHECKSUM_CHECK_TCP 0
+#define CHECKSUM_CHECK_ICMP 0
+#define CHECKSUM_CHECK_ICMP6 0
+
+/* Minimal changes to opt.h required for tcp unit tests: */
+#define MEM_SIZE 16000
+#define TCP_SND_QUEUELEN 40
+#define MEMP_NUM_TCP_SEG TCP_SND_QUEUELEN
+#define TCP_SND_BUF (12 * TCP_MSS)
+#define TCP_WND (10 * TCP_MSS)
+#define LWIP_WND_SCALE 1
+#define TCP_RCV_SCALE 0
+#define PBUF_POOL_SIZE 400 /* pbuf tests need ~200KByte */
+
+/* Minimal changes to opt.h required for etharp unit tests: */
+#define ETHARP_SUPPORT_STATIC_ENTRIES 1
+
+#endif /* LWIP_HDR_LWIPOPTS_H__ */
diff --git a/lwip/test/fuzz/output_to_pcap.sh b/lwip/test/fuzz/output_to_pcap.sh
new file mode 100644
index 0000000..c999ff0
--- /dev/null
+++ b/lwip/test/fuzz/output_to_pcap.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+
+if [ -z "$1" ]
+then
+ echo "This script will make pcap files from the afl-fuzz crash/hang files"
+ echo "It needs hexdump and text2pcap"
+ echo "Please give output directory as argument"
+ exit 2
+fi
+
+for i in `ls $1/crashes/id*`
+do
+ PCAPNAME=`echo $i | grep pcap`
+ if [ -z "$PCAPNAME" ]; then
+ hexdump -C $i > $1/$$.tmp
+ text2pcap $1/$$.tmp ${i}.pcap
+ fi
+done
+for i in `ls $1/hangs/id*`
+do
+ PCAPNAME=`echo $i | grep pcap`
+ if [ -z "$PCAPNAME" ]; then
+ hexdump -C $i > $1/$$.tmp
+ text2pcap $1/$$.tmp ${i}.pcap
+ fi
+done
+rm -f $1/$$.tmp
+
+echo
+echo "Created pcap files:"
+ls $1/*/*.pcap
diff --git a/lwip/test/unit/Filelists.mk b/lwip/test/unit/Filelists.mk
new file mode 100644
index 0000000..d95923f
--- /dev/null
+++ b/lwip/test/unit/Filelists.mk
@@ -0,0 +1,47 @@
+#
+# 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>
+#
+
+TESTDIR=$(LWIPDIR)/../test/unit
+TESTFILES=$(TESTDIR)/lwip_unittests.c \
+ $(TESTDIR)/api/test_sockets.c \
+ $(TESTDIR)/arch/sys_arch.c \
+ $(TESTDIR)/core/test_mem.c \
+ $(TESTDIR)/core/test_pbuf.c \
+ $(TESTDIR)/dhcp/test_dhcp.c \
+ $(TESTDIR)/etharp/test_etharp.c \
+ $(TESTDIR)/ip4/test_ip4.c \
+ $(TESTDIR)/mdns/test_mdns.c \
+ $(TESTDIR)/mqtt/test_mqtt.c \
+ $(TESTDIR)/tcp/tcp_helper.c \
+ $(TESTDIR)/tcp/test_tcp_oos.c \
+ $(TESTDIR)/tcp/test_tcp.c \
+ $(TESTDIR)/udp/test_udp.c
+
diff --git a/lwip/test/unit/api/test_sockets.c b/lwip/test/unit/api/test_sockets.c
new file mode 100644
index 0000000..f8bfa9f
--- /dev/null
+++ b/lwip/test/unit/api/test_sockets.c
@@ -0,0 +1,809 @@
+#include "test_sockets.h"
+
+#include "lwip/mem.h"
+#include "lwip/opt.h"
+#include "lwip/sockets.h"
+#include "lwip/priv/sockets_priv.h"
+#include "lwip/stats.h"
+
+#include "lwip/tcpip.h"
+#include "lwip/priv/tcp_priv.h"
+#include "lwip/api.h"
+
+
+static int
+test_sockets_get_used_count(void)
+{
+ int used = 0;
+ int i;
+
+ for (i = 0; i < NUM_SOCKETS; i++) {
+ struct lwip_sock* s = lwip_socket_dbg_get_socket(i);
+ if (s != NULL) {
+ if (s->fd_used) {
+ used++;
+ }
+ }
+ }
+ return used;
+}
+
+
+/* Setups/teardown functions */
+
+static void
+sockets_setup(void)
+{
+ /* expect full free heap */
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
+}
+
+static void
+sockets_teardown(void)
+{
+ fail_unless(test_sockets_get_used_count() == 0);
+ /* poll until all memory is released... */
+ tcpip_thread_poll_one();
+ while (tcp_tw_pcbs) {
+ tcp_abort(tcp_tw_pcbs);
+ tcpip_thread_poll_one();
+ }
+ tcpip_thread_poll_one();
+ /* ensure full free heap */
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
+}
+
+#ifndef NUM_SOCKETS
+#define NUM_SOCKETS MEMP_NUM_NETCONN
+#endif
+
+#if LWIP_SOCKET
+static int
+test_sockets_alloc_socket_nonblocking(int domain, int type)
+{
+ int s = lwip_socket(domain, type, 0);
+ if (s >= 0) {
+ int ret = lwip_fcntl(s, F_SETFL, O_NONBLOCK);
+ fail_unless(ret == 0);
+ }
+ return s;
+}
+
+/* Verify basic sockets functionality
+ */
+START_TEST(test_sockets_basics)
+{
+ int s, i, ret;
+ int s2[NUM_SOCKETS];
+ LWIP_UNUSED_ARG(_i);
+
+ s = lwip_socket(AF_INET, SOCK_STREAM, 0);
+ fail_unless(s >= 0);
+ lwip_close(s);
+
+ for (i = 0; i < NUM_SOCKETS; i++) {
+ s2[i] = lwip_socket(AF_INET, SOCK_STREAM, 0);
+ fail_unless(s2[i] >= 0);
+ }
+
+ /* all sockets used, now it should fail */
+ s = lwip_socket(AF_INET, SOCK_STREAM, 0);
+ fail_unless(s == -1);
+ /* close one socket */
+ ret = lwip_close(s2[0]);
+ fail_unless(ret == 0);
+ /* now it should succeed */
+ s2[0] = lwip_socket(AF_INET, SOCK_STREAM, 0);
+ fail_unless(s2[0] >= 0);
+
+ /* close all sockets */
+ for (i = 0; i < NUM_SOCKETS; i++) {
+ ret = lwip_close(s2[i]);
+ fail_unless(ret == 0);
+ }
+}
+END_TEST
+
+static void test_sockets_allfunctions_basic_domain(int domain)
+{
+ int s, s2, s3, ret;
+ struct sockaddr_storage addr, addr2;
+ socklen_t addrlen, addr2len;
+ /* listen socket */
+ s = lwip_socket(domain, SOCK_STREAM, 0);
+ fail_unless(s >= 0);
+
+ ret = lwip_listen(s, 0);
+ fail_unless(ret == 0);
+
+ addrlen = sizeof(addr);
+ ret = lwip_getsockname(s, (struct sockaddr*)&addr, &addrlen);
+ fail_unless(ret == 0);
+
+ s2 = test_sockets_alloc_socket_nonblocking(domain, SOCK_STREAM);
+ fail_unless(s2 >= 0);
+ /* nonblocking connect s2 to s (but use loopback address) */
+ if (domain == AF_INET) {
+#if LWIP_IPV4
+ struct sockaddr_in *addr4 = (struct sockaddr_in *)&addr;
+ addr4->sin_addr.s_addr = PP_HTONL(INADDR_LOOPBACK);
+#endif
+ } else {
+#if LWIP_IPV6
+ struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&addr;
+ struct in6_addr lo6 = IN6ADDR_LOOPBACK_INIT;
+ addr6->sin6_addr = lo6;
+#endif
+ }
+ ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen);
+ fail_unless(ret == -1);
+ fail_unless(errno == EINPROGRESS);
+ ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen);
+ fail_unless(ret == -1);
+ fail_unless(errno == EALREADY);
+
+ while(tcpip_thread_poll_one());
+
+ s3 = lwip_accept(s, (struct sockaddr*)&addr2, &addr2len);
+ fail_unless(s3 >= 0);
+
+ ret = lwip_connect(s2, (struct sockaddr*)&addr, addrlen);
+ fail_unless(ret == -1);
+ fail_unless(errno == EISCONN);
+
+ ret = lwip_close(s);
+ fail_unless(ret == 0);
+ ret = lwip_close(s2);
+ fail_unless(ret == 0);
+ ret = lwip_close(s3);
+ fail_unless(ret == 0);
+}
+
+/* Try to step through all sockets functions once...
+ */
+START_TEST(test_sockets_allfunctions_basic)
+{
+ LWIP_UNUSED_ARG(_i);
+#if LWIP_IPV4
+ test_sockets_allfunctions_basic_domain(AF_INET);
+#endif
+#if LWIP_IPV6
+ test_sockets_allfunctions_basic_domain(AF_INET6);
+#endif
+}
+END_TEST
+
+static void test_sockets_init_loopback_addr(int domain, struct sockaddr_storage *addr_st, socklen_t *sz)
+{
+ memset(addr_st, 0, sizeof(*addr_st));
+ switch(domain) {
+#if LWIP_IPV6
+ case AF_INET6: {
+ struct sockaddr_in6 *addr = (struct sockaddr_in6*)addr_st;
+ struct in6_addr lo6 = IN6ADDR_LOOPBACK_INIT;
+ addr->sin6_family = AF_INET6;
+ addr->sin6_port = 0; /* use ephemeral port */
+ addr->sin6_addr = lo6;
+ *sz = sizeof(*addr);
+ }
+ break;
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+ case AF_INET: {
+ struct sockaddr_in *addr = (struct sockaddr_in*)addr_st;
+ addr->sin_family = AF_INET;
+ addr->sin_port = 0; /* use ephemeral port */
+ addr->sin_addr.s_addr = PP_HTONL(INADDR_LOOPBACK);
+ *sz = sizeof(*addr);
+ }
+ break;
+#endif /* LWIP_IPV4 */
+ default:
+ *sz = 0;
+ fail();
+ break;
+ }
+}
+
+static void test_sockets_msgapi_update_iovs(struct msghdr *msg, size_t bytes)
+{
+ int i;
+
+ /* note: this modifies the underyling iov_base and iov_len for a partial
+ read for an individual vector. This updates the msg->msg_iov pointer
+ to skip fully consumed vecotrs */
+
+ /* process fully consumed vectors */
+ for (i = 0; i < msg->msg_iovlen; i++) {
+ if (msg->msg_iov[i].iov_len <= bytes) {
+ /* reduce bytes by amount of this vector */
+ bytes -= msg->msg_iov[i].iov_len;
+ } else {
+ break; /* iov not fully consumed */
+ }
+ }
+
+ /* slide down over fully consumed vectors */
+ msg->msg_iov = &msg->msg_iov[i];
+ msg->msg_iovlen -= i;
+
+ /* update new first vector with any remaining amount */
+ msg->msg_iov[0].iov_base = ((u8_t *)msg->msg_iov[0].iov_base + bytes);
+ msg->msg_iov[0].iov_len -= bytes;
+}
+
+static void test_sockets_msgapi_tcp(int domain)
+{
+ #define BUF_SZ (TCP_SND_BUF/4)
+ #define TOTAL_DATA_SZ (BUF_SZ*8) /* ~(TCP_SND_BUF*2) that accounts for integer rounding */
+ #define NEED_TRAILER (BUF_SZ % 4 != 0)
+ int listnr, s1, s2, i, ret, opt;
+ int bytes_written, bytes_read;
+ struct sockaddr_storage addr_storage;
+ socklen_t addr_size;
+ struct iovec siovs[8];
+ struct msghdr smsg;
+ u8_t * snd_buf;
+ struct iovec riovs[5];
+ struct iovec riovs_tmp[5];
+ struct msghdr rmsg;
+ u8_t * rcv_buf;
+ int rcv_off;
+ int rcv_trailer = 0;
+ u8_t val;
+
+ test_sockets_init_loopback_addr(domain, &addr_storage, &addr_size);
+
+ listnr = test_sockets_alloc_socket_nonblocking(domain, SOCK_STREAM);
+ fail_unless(listnr >= 0);
+ s1 = test_sockets_alloc_socket_nonblocking(domain, SOCK_STREAM);
+ fail_unless(s1 >= 0);
+
+ /* setup a listener socket on loopback with ephemeral port */
+ ret = lwip_bind(listnr, (struct sockaddr*)&addr_storage, addr_size);
+ fail_unless(ret == 0);
+ ret = lwip_listen(listnr, 0);
+ fail_unless(ret == 0);
+
+ /* update address with ephemeral port */
+ ret = lwip_getsockname(listnr, (struct sockaddr*)&addr_storage, &addr_size);
+ fail_unless(ret == 0);
+
+ /* connect, won't complete until we accept it */
+ ret = lwip_connect(s1, (struct sockaddr*)&addr_storage, addr_size);
+ fail_unless(ret == -1);
+ fail_unless(errno == EINPROGRESS);
+
+ while (tcpip_thread_poll_one());
+
+ /* accept, creating the other side of the connection */
+ s2 = lwip_accept(listnr, NULL, NULL);
+ fail_unless(s2 >= 0);
+
+ /* double check s1 is connected */
+ ret = lwip_connect(s1, (struct sockaddr*)&addr_storage, addr_size);
+ fail_unless(ret == -1);
+ fail_unless(errno == EISCONN);
+
+ /* set s2 to non-blocking, not inherited from listener */
+ opt = lwip_fcntl(s2, F_GETFL, 0);
+ fail_unless(opt == 6);
+ opt = O_NONBLOCK;
+ ret = lwip_fcntl(s2, F_SETFL, opt);
+ fail_unless(ret == 0);
+
+ /* we are done with listener, close it */
+ ret = lwip_close(listnr);
+ fail_unless(ret == 0);
+
+ /* allocate a buffer for a stream of incrementing hex (0x00..0xFF) which we will use
+ to create an input vector set that is larger than the TCP's send buffer. This will
+ force execution of the partial IO vector send case */
+ snd_buf = (u8_t*)mem_malloc(BUF_SZ);
+ val = 0x00;
+ fail_unless(snd_buf != NULL);
+ for (i = 0; i < BUF_SZ; i++,val++) {
+ snd_buf[i] = val;
+ }
+
+ /* send the buffer 8 times in one message, equating to TOTAL_DATA_SZ */
+ for (i = 0; i < 8; i++) {
+ siovs[i].iov_base = snd_buf;
+ siovs[i].iov_len = BUF_SZ;
+ }
+
+ /* allocate a receive buffer, same size as snd_buf for easy verification */
+ rcv_buf = (u8_t*)mem_calloc(1, BUF_SZ);
+ fail_unless(rcv_buf != NULL);
+ /* split across iovs */
+ for (i = 0; i < 4; i++) {
+ riovs[i].iov_base = &rcv_buf[i*(BUF_SZ/4)];
+ riovs[i].iov_len = BUF_SZ/4;
+ }
+ /* handling trailing bytes if buffer doesn't evenly divide by 4 */
+#if NEED_TRAILER
+ if ((BUF_SZ % 4) != 0) {
+ riovs[5].iov_base = &rcv_buf[4*(BUF_SZ/4)];
+ riovs[5].iov_len = BUF_SZ - (4*(BUF_SZ/4));
+ rcv_trailer = 1;
+ }
+#endif /* NEED_TRAILER */
+
+ /* we use a copy of riovs since we'll be modifying base and len during
+ receiving. This gives us an easy way to reset the iovs for next recvmsg */
+ memcpy(riovs_tmp, riovs, sizeof(riovs));
+
+ memset(&smsg, 0, sizeof(smsg));
+ smsg.msg_iov = siovs;
+ smsg.msg_iovlen = 8;
+
+ memset(&rmsg, 0, sizeof(rmsg));
+ rmsg.msg_iov = riovs_tmp;
+ rmsg.msg_iovlen = (rcv_trailer ? 5 : 4);
+
+ bytes_written = 0;
+ bytes_read = 0;
+ rcv_off = 0;
+
+ while (bytes_written < TOTAL_DATA_SZ && (bytes_read < TOTAL_DATA_SZ)) {
+ /* send data */
+ if (bytes_written < TOTAL_DATA_SZ) {
+ ret = lwip_sendmsg(s1, &smsg, 0);
+ /* note: since we always receive after sending, there will be open
+ space in the send buffer */
+ fail_unless(ret > 0);
+
+ bytes_written += ret;
+ if (bytes_written < TOTAL_DATA_SZ) {
+ test_sockets_msgapi_update_iovs(&smsg, (size_t)ret);
+ }
+ }
+
+ while (tcpip_thread_poll_one());
+
+ /* receive and verify data */
+ do {
+ if (bytes_read < TOTAL_DATA_SZ) {
+ ret = lwip_recvmsg(s2, &rmsg, 0);
+ fail_unless(ret > 0 || (ret == -1 && errno == EWOULDBLOCK));
+
+ if (ret > 0) {
+ rcv_off += ret;
+ /* we have received a full buffer */
+ if (rcv_off == BUF_SZ) {
+ /* note: since iovs are just pointers, compare underlying buf */
+ fail_unless(!memcmp(snd_buf, rcv_buf, BUF_SZ));
+ bytes_read += BUF_SZ;
+ /* reset receive state for next buffer */
+ rcv_off = 0;
+ memset(rcv_buf, 0, BUF_SZ);
+ memcpy(riovs_tmp, riovs, sizeof(riovs));
+ rmsg.msg_iov = riovs_tmp;
+ rmsg.msg_iovlen = (rcv_trailer ? 5 : 4);
+ } else { /* partial read */
+ test_sockets_msgapi_update_iovs(&rmsg, (size_t)ret);
+ }
+ }
+ } else {
+ break;
+ }
+ } while(ret > 0);
+ }
+
+ ret = lwip_close(s1);
+ fail_unless(ret == 0);
+ ret = lwip_close(s2);
+ fail_unless(ret == 0);
+ mem_free(snd_buf);
+ mem_free(rcv_buf);
+}
+
+static void test_sockets_msgapi_udp_send_recv_loop(int s, struct msghdr *smsg, struct msghdr *rmsg)
+{
+ int i, ret;
+
+ /* send/receive our datagram of IO vectors 10 times */
+ for (i = 0; i < 10; i++) {
+ ret = lwip_sendmsg(s, smsg, 0);
+ fail_unless(ret == 4);
+
+ while (tcpip_thread_poll_one());
+
+ /* receive the datagram split across 4 buffers */
+ ret = lwip_recvmsg(s, rmsg, 0);
+ fail_unless(ret == 4);
+
+ /* verify data */
+ fail_unless(*((u8_t*)rmsg->msg_iov[0].iov_base) == 0xDE);
+ fail_unless(*((u8_t*)rmsg->msg_iov[1].iov_base) == 0xAD);
+ fail_unless(*((u8_t*)rmsg->msg_iov[2].iov_base) == 0xBE);
+ fail_unless(*((u8_t*)rmsg->msg_iov[3].iov_base) == 0xEF);
+
+ /* clear rcv_buf to ensure no data is being skipped */
+ *((u8_t*)rmsg->msg_iov[0].iov_base) = 0x00;
+ *((u8_t*)rmsg->msg_iov[1].iov_base) = 0x00;
+ *((u8_t*)rmsg->msg_iov[2].iov_base) = 0x00;
+ *((u8_t*)rmsg->msg_iov[3].iov_base) = 0x00;
+ }
+}
+
+static void test_sockets_msgapi_udp(int domain)
+{
+ int s, i, ret;
+ struct sockaddr_storage addr_storage;
+ socklen_t addr_size;
+ struct iovec riovs[4];
+ struct msghdr rmsg;
+ u8_t rcv_buf[4];
+ struct iovec siovs[4];
+ struct msghdr smsg;
+ u8_t snd_buf[4] = {0xDE, 0xAD, 0xBE, 0xEF};
+
+ /* initialize IO vectors with data */
+ for (i = 0; i < 4; i++) {
+ siovs[i].iov_base = &snd_buf[i];
+ siovs[i].iov_len = sizeof(u8_t);
+ riovs[i].iov_base = &rcv_buf[i];
+ riovs[i].iov_len = sizeof(u8_t);
+ }
+
+ test_sockets_init_loopback_addr(domain, &addr_storage, &addr_size);
+
+ s = test_sockets_alloc_socket_nonblocking(domain, SOCK_DGRAM);
+ fail_unless(s >= 0);
+
+ ret = lwip_bind(s, (struct sockaddr*)&addr_storage, addr_size);
+ fail_unless(ret == 0);
+
+ /* Update addr with epehermal port */
+ ret = lwip_getsockname(s, (struct sockaddr*)&addr_storage, &addr_size);
+ fail_unless(ret == 0);
+ switch(domain) {
+#if LWIP_IPV6
+ case AF_INET6:
+ fail_unless(addr_size == sizeof(struct sockaddr_in6));
+ break;
+#endif /* LWIP_IPV6 */
+#if LWIP_IPV4
+ case AF_INET:
+ fail_unless(addr_size == sizeof(struct sockaddr_in));
+ break;
+#endif /* LWIP_IPV6 */
+ default:
+ fail();
+ break;
+ }
+
+ /* send and receive the datagram in 4 pieces */
+ memset(&smsg, 0, sizeof(smsg));
+ smsg.msg_iov = siovs;
+ smsg.msg_iovlen = 4;
+ memset(&rmsg, 0, sizeof(rmsg));
+ rmsg.msg_iov = riovs;
+ rmsg.msg_iovlen = 4;
+
+ /* perform a sendmsg with remote host (self) */
+ smsg.msg_name = &addr_storage;
+ smsg.msg_namelen = addr_size;
+
+ test_sockets_msgapi_udp_send_recv_loop(s, &smsg, &rmsg);
+
+ /* Connect to self, allowing us to not pass message name */
+ ret = lwip_connect(s, (struct sockaddr*)&addr_storage, addr_size);
+ fail_unless(ret == 0);
+
+ smsg.msg_name = NULL;
+ smsg.msg_namelen = 0;
+
+ test_sockets_msgapi_udp_send_recv_loop(s, &smsg, &rmsg);
+
+ ret = lwip_close(s);
+ fail_unless(ret == 0);
+}
+
+#if LWIP_IPV4
+static void test_sockets_msgapi_cmsg(int domain)
+{
+ int s, ret, enable;
+ struct sockaddr_storage addr_storage;
+ socklen_t addr_size;
+ struct iovec iov;
+ struct msghdr msg;
+ struct cmsghdr *cmsg;
+ struct in_pktinfo *pktinfo;
+ u8_t rcv_buf[4];
+ u8_t snd_buf[4] = {0xDE, 0xAD, 0xBE, 0xEF};
+ u8_t cmsg_buf[CMSG_SPACE(sizeof(struct in_pktinfo))];
+
+ test_sockets_init_loopback_addr(domain, &addr_storage, &addr_size);
+
+ s = test_sockets_alloc_socket_nonblocking(domain, SOCK_DGRAM);
+ fail_unless(s >= 0);
+
+ ret = lwip_bind(s, (struct sockaddr*)&addr_storage, addr_size);
+ fail_unless(ret == 0);
+
+ /* Update addr with epehermal port */
+ ret = lwip_getsockname(s, (struct sockaddr*)&addr_storage, &addr_size);
+ fail_unless(ret == 0);
+
+ enable = 1;
+ ret = lwip_setsockopt(s, IPPROTO_IP, IP_PKTINFO, &enable, sizeof(enable));
+ fail_unless(ret == 0);
+
+ /* Receive full message, including control message */
+ iov.iov_base = rcv_buf;
+ iov.iov_len = sizeof(rcv_buf);
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = sizeof(cmsg_buf);
+ msg.msg_flags = 0;
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_name = NULL;
+ msg.msg_namelen = 0;
+
+ memset(rcv_buf, 0, sizeof(rcv_buf));
+ ret = lwip_sendto(s, snd_buf, sizeof(snd_buf), 0, (struct sockaddr*)&addr_storage, addr_size);
+ fail_unless(ret == sizeof(snd_buf));
+
+ tcpip_thread_poll_one();
+
+ ret = lwip_recvmsg(s, &msg, 0);
+ fail_unless(ret == sizeof(rcv_buf));
+ fail_unless(!memcmp(rcv_buf, snd_buf, sizeof(rcv_buf)));
+
+ /* Verify message header */
+ cmsg = CMSG_FIRSTHDR(&msg);
+ fail_unless(cmsg != NULL);
+ fail_unless(cmsg->cmsg_len > 0);
+ fail_unless(cmsg->cmsg_level == IPPROTO_IP);
+ fail_unless(cmsg->cmsg_type == IP_PKTINFO);
+
+ /* Verify message data */
+ pktinfo = (struct in_pktinfo*)CMSG_DATA(cmsg);
+ /* We only have loopback interface enabled */
+ fail_unless(pktinfo->ipi_ifindex == 1);
+ fail_unless(pktinfo->ipi_addr.s_addr == PP_HTONL(INADDR_LOOPBACK));
+
+ /* Verify there are no additional messages */
+ cmsg = CMSG_NXTHDR(&msg, cmsg);
+ fail_unless(cmsg == NULL);
+
+ /* Send datagram again, testing truncation */
+ memset(rcv_buf, 0, sizeof(rcv_buf));
+ ret = lwip_sendto(s, snd_buf, sizeof(snd_buf), 0, (struct sockaddr*)&addr_storage, addr_size);
+ fail_unless(ret == sizeof(snd_buf));
+
+ tcpip_thread_poll_one();
+
+ msg.msg_controllen = 1;
+ msg.msg_flags = 0;
+ ret = lwip_recvmsg(s, &msg, 0);
+ fail_unless(ret == sizeof(rcv_buf));
+ fail_unless(!memcmp(rcv_buf, snd_buf, sizeof(rcv_buf)));
+ /* Ensure truncation was returned */
+ fail_unless(msg.msg_flags & MSG_CTRUNC);
+ /* Ensure no control messages were returned */
+ fail_unless(msg.msg_controllen == 0);
+
+ ret = lwip_close(s);
+ fail_unless(ret == 0);
+}
+#endif /* LWIP_IPV4 */
+
+START_TEST(test_sockets_msgapis)
+{
+ LWIP_UNUSED_ARG(_i);
+#if LWIP_IPV4
+ test_sockets_msgapi_udp(AF_INET);
+ test_sockets_msgapi_tcp(AF_INET);
+ test_sockets_msgapi_cmsg(AF_INET);
+#endif
+#if LWIP_IPV6
+ test_sockets_msgapi_udp(AF_INET6);
+ test_sockets_msgapi_tcp(AF_INET6);
+#endif
+}
+END_TEST
+
+START_TEST(test_sockets_select)
+{
+#if LWIP_SOCKET_SELECT
+ int s;
+ int ret;
+ fd_set readset;
+ fd_set writeset;
+ fd_set errset;
+ struct timeval tv;
+
+ fail_unless(test_sockets_get_used_count() == 0);
+
+ s = lwip_socket(AF_INET, SOCK_STREAM, 0);
+ fail_unless(s >= 0);
+ fail_unless(test_sockets_get_used_count() == 0);
+
+ FD_ZERO(&readset);
+ FD_SET(s, &readset);
+ FD_ZERO(&writeset);
+ FD_SET(s, &writeset);
+ FD_ZERO(&errset);
+ FD_SET(s, &errset);
+
+ tv.tv_sec = tv.tv_usec = 0;
+ ret = lwip_select(s + 1, &readset, &writeset, &errset, &tv);
+ fail_unless(ret == 0);
+ fail_unless(test_sockets_get_used_count() == 0);
+
+ ret = lwip_close(s);
+ fail_unless(ret == 0);
+
+#endif
+ LWIP_UNUSED_ARG(_i);
+}
+END_TEST
+
+START_TEST(test_sockets_recv_after_rst)
+{
+ int sl, sact;
+ int spass = -1;
+ int ret;
+ struct sockaddr_in sa_listen;
+ const u16_t port = 1234;
+ int arg;
+ const char txbuf[] = "something";
+ char rxbuf[16];
+ struct lwip_sock *sact_sock;
+ int err;
+ LWIP_UNUSED_ARG(_i);
+
+ fail_unless(test_sockets_get_used_count() == 0);
+
+ memset(&sa_listen, 0, sizeof(sa_listen));
+ sa_listen.sin_family = AF_INET;
+ sa_listen.sin_port = PP_HTONS(port);
+ sa_listen.sin_addr.s_addr = PP_HTONL(INADDR_LOOPBACK);
+
+ /* set up the listener */
+ sl = lwip_socket(AF_INET, SOCK_STREAM, 0);
+ fail_unless(sl >= 0);
+ fail_unless(test_sockets_get_used_count() == 0);
+
+ ret = lwip_bind(sl, (struct sockaddr *)&sa_listen, sizeof(sa_listen));
+ fail_unless(ret == 0);
+ ret = lwip_listen(sl, 0);
+ fail_unless(ret == 0);
+
+ /* set up the client */
+ sact = lwip_socket(AF_INET, SOCK_STREAM, 0);
+ fail_unless(sact >= 0);
+ fail_unless(test_sockets_get_used_count() == 0);
+ /* set the client to nonblocking to simplify this test */
+ arg = 1;
+ ret = lwip_ioctl(sact, FIONBIO, &arg);
+ fail_unless(ret == 0);
+ /* connect */
+ do {
+ ret = lwip_connect(sact, (struct sockaddr *)&sa_listen, sizeof(sa_listen));
+ err = errno;
+ fail_unless((ret == 0) || (ret == -1));
+ if (ret != 0) {
+ if (err == EISCONN) {
+ /* Although this is not valid, use EISCONN as an indicator for successful connection.
+ This marks us as "connect phase is done". On error, we would either have a different
+ errno code or "send" fails later... -> good enough for this test. */
+ ret = 0;
+ } else {
+ fail_unless(err == EINPROGRESS);
+ if (err != EINPROGRESS) {
+ goto cleanup;
+ }
+ /* we're in progress: little side check: test for EALREADY */
+ ret = lwip_connect(sact, (struct sockaddr *)&sa_listen, sizeof(sa_listen));
+ err = errno;
+ fail_unless(ret == -1);
+ fail_unless(err == EALREADY);
+ if ((ret != -1) || (err != EALREADY)) {
+ goto cleanup;
+ }
+ }
+ tcpip_thread_poll_one();
+ tcpip_thread_poll_one();
+ tcpip_thread_poll_one();
+ tcpip_thread_poll_one();
+ }
+ } while (ret != 0);
+ fail_unless(ret == 0);
+
+ /* accept the server connection part */
+ spass = lwip_accept(sl, NULL, NULL);
+ fail_unless(spass >= 0);
+
+ /* write data from client */
+ ret = lwip_send(sact, txbuf, sizeof(txbuf), 0);
+ fail_unless(ret == sizeof(txbuf));
+
+ tcpip_thread_poll_one();
+ tcpip_thread_poll_one();
+
+ /* issue RST (This is a HACK, don't try this in your own app!) */
+ sact_sock = lwip_socket_dbg_get_socket(sact);
+ fail_unless(sact_sock != NULL);
+ if (sact_sock != NULL) {
+ struct netconn *sact_conn = sact_sock->conn;
+ fail_unless(sact_conn != NULL);
+ if (sact_conn != NULL) {
+ struct tcp_pcb *pcb = sact_conn->pcb.tcp;
+ fail_unless(pcb != NULL);
+ if (pcb != NULL) {
+ tcp_rst(pcb, pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+ pcb->local_port, pcb->remote_port);
+ }
+ }
+ }
+ tcpip_thread_poll_one();
+ tcpip_thread_poll_one();
+
+ /* expect to receive data first */
+ ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0);
+ fail_unless(ret > 0);
+ tcpip_thread_poll_one();
+ tcpip_thread_poll_one();
+
+ /* expect to receive RST indication */
+ ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0);
+ fail_unless(ret == -1);
+ err = errno;
+ fail_unless(err == ECONNRESET);
+ tcpip_thread_poll_one();
+ tcpip_thread_poll_one();
+
+ /* expect to receive ENOTCONN indication */
+ ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0);
+ fail_unless(ret == -1);
+ err = errno;
+ fail_unless(err == ENOTCONN);
+ tcpip_thread_poll_one();
+ tcpip_thread_poll_one();
+
+ /* expect to receive ENOTCONN indication */
+ ret = lwip_recv(spass, rxbuf, sizeof(rxbuf), 0);
+ fail_unless(ret == -1);
+ err = errno;
+ fail_unless(err == ENOTCONN);
+ tcpip_thread_poll_one();
+ tcpip_thread_poll_one();
+
+cleanup:
+ ret = lwip_close(sl);
+ fail_unless(ret == 0);
+ ret = lwip_close(sact);
+ fail_unless(ret == 0);
+ if (spass >= 0) {
+ ret = lwip_close(spass);
+ fail_unless(ret == 0);
+ }
+}
+END_TEST
+
+/** Create the suite including all tests for this module */
+Suite *
+sockets_suite(void)
+{
+ testfunc tests[] = {
+ TESTFUNC(test_sockets_basics),
+ TESTFUNC(test_sockets_allfunctions_basic),
+ TESTFUNC(test_sockets_msgapis),
+ TESTFUNC(test_sockets_select),
+ TESTFUNC(test_sockets_recv_after_rst),
+ };
+ return create_suite("SOCKETS", tests, sizeof(tests)/sizeof(testfunc), sockets_setup, sockets_teardown);
+}
+
+#else /* LWIP_SOCKET */
+
+Suite *
+sockets_suite(void)
+{
+ return create_suite("SOCKETS", NULL, 0, NULL, NULL);
+}
+#endif /* LWIP_SOCKET */
diff --git a/lwip/test/unit/api/test_sockets.h b/lwip/test/unit/api/test_sockets.h
new file mode 100644
index 0000000..0abb7a3
--- /dev/null
+++ b/lwip/test/unit/api/test_sockets.h
@@ -0,0 +1,8 @@
+#ifndef LWIP_HDR_TEST_SOCKETS_H
+#define LWIP_HDR_TEST_SOCKETS_H
+
+#include "../lwip_check.h"
+
+Suite *sockets_suite(void);
+
+#endif
diff --git a/lwip/test/unit/arch/sys_arch.c b/lwip/test/unit/arch/sys_arch.c
new file mode 100644
index 0000000..27c90bc
--- /dev/null
+++ b/lwip/test/unit/arch/sys_arch.c
@@ -0,0 +1,341 @@
+/*
+ * Copyright (c) 2017 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/opt.h>
+#include <lwip/arch.h>
+#if !NO_SYS
+#include "sys_arch.h"
+#endif
+#include <lwip/stats.h>
+#include <lwip/debug.h>
+#include <lwip/sys.h>
+
+#include <string.h>
+
+u32_t sys_jiffies(void)
+{
+ return (u32_t)0; /* todo */
+}
+
+u32_t sys_now(void)
+{
+ return (u32_t)0; /* todo */
+}
+
+void sys_init(void)
+{
+}
+
+#if !NO_SYS
+
+test_sys_arch_waiting_fn the_waiting_fn;
+
+void test_sys_arch_wait_callback(test_sys_arch_waiting_fn waiting_fn)
+{
+ the_waiting_fn = waiting_fn;
+}
+
+err_t sys_sem_new(sys_sem_t *sem, u8_t count)
+{
+ LWIP_ASSERT("sem != NULL", sem != NULL);
+ *sem = count + 1;
+ return ERR_OK;
+}
+
+void sys_sem_free(sys_sem_t *sem)
+{
+ LWIP_ASSERT("sem != NULL", sem != NULL);
+ *sem = 0;
+}
+
+void sys_sem_set_invalid(sys_sem_t *sem)
+{
+ LWIP_ASSERT("sem != NULL", sem != NULL);
+ *sem = 0;
+}
+
+/* semaphores are 1-based because RAM is initialized as 0, which would be valid */
+u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
+{
+ u32_t ret = 0;
+ LWIP_ASSERT("sem != NULL", sem != NULL);
+ LWIP_ASSERT("*sem > 0", *sem > 0);
+ if (*sem == 1) {
+ /* need to wait */
+ if(!timeout)
+ {
+ /* wait infinite */
+ LWIP_ASSERT("cannot wait without waiting callback", the_waiting_fn != NULL);
+ do {
+ int expectSomething = the_waiting_fn(sem, NULL);
+ LWIP_ASSERT("*sem > 0", *sem > 0);
+ LWIP_ASSERT("expecting a semaphore count but it's 0", !expectSomething || (*sem > 1));
+ ret++;
+ if (ret == SYS_ARCH_TIMEOUT) {
+ ret--;
+ }
+ } while(*sem == 1);
+ }
+ else
+ {
+ if (the_waiting_fn) {
+ int expectSomething = the_waiting_fn(sem, NULL);
+ LWIP_ASSERT("expecting a semaphore count but it's 0", !expectSomething || (*sem > 1));
+ }
+ LWIP_ASSERT("*sem > 0", *sem > 0);
+ if (*sem == 1) {
+ return SYS_ARCH_TIMEOUT;
+ }
+ ret = 1;
+ }
+ }
+ LWIP_ASSERT("*sem > 0", *sem > 0);
+ (*sem)--;
+ LWIP_ASSERT("*sem > 0", *sem > 0);
+ /* return the time we waited for the sem */
+ return ret;
+}
+
+void sys_sem_signal(sys_sem_t *sem)
+{
+ LWIP_ASSERT("sem != NULL", sem != NULL);
+ LWIP_ASSERT("*sem > 0", *sem > 0);
+ (*sem)++;
+ LWIP_ASSERT("*sem > 0", *sem > 0);
+}
+
+err_t sys_mutex_new(sys_mutex_t *mutex)
+{
+ LWIP_ASSERT("mutex != NULL", mutex != NULL);
+ *mutex = 1; /* 1 allocated */
+ return ERR_OK;
+}
+
+void sys_mutex_free(sys_mutex_t *mutex)
+{
+ /* parameter check */
+ LWIP_ASSERT("mutex != NULL", mutex != NULL);
+ LWIP_ASSERT("*mutex >= 1", *mutex >= 1);
+ *mutex = 0;
+}
+
+void sys_mutex_set_invalid(sys_mutex_t *mutex)
+{
+ LWIP_ASSERT("mutex != NULL", mutex != NULL);
+ *mutex = 0;
+}
+
+void sys_mutex_lock(sys_mutex_t *mutex)
+{
+ /* nothing to do, no multithreading supported */
+ LWIP_ASSERT("mutex != NULL", mutex != NULL);
+ /* check that the mutext is valid and unlocked (no nested locking) */
+ LWIP_ASSERT("*mutex >= 1", *mutex == 1);
+ /* we count up just to check the correct pairing of lock/unlock */
+ (*mutex)++;
+ LWIP_ASSERT("*mutex >= 1", *mutex >= 1);
+}
+
+void sys_mutex_unlock(sys_mutex_t *mutex)
+{
+ /* nothing to do, no multithreading supported */
+ LWIP_ASSERT("mutex != NULL", mutex != NULL);
+ LWIP_ASSERT("*mutex >= 1", *mutex >= 1);
+ /* we count down just to check the correct pairing of lock/unlock */
+ (*mutex)--;
+ LWIP_ASSERT("*mutex >= 1", *mutex >= 1);
+}
+
+
+sys_thread_t sys_thread_new(const char *name, lwip_thread_fn function, void *arg, int stacksize, int prio)
+{
+ LWIP_UNUSED_ARG(name);
+ LWIP_UNUSED_ARG(function);
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(stacksize);
+ LWIP_UNUSED_ARG(prio);
+ /* threads not supported */
+ return 0;
+}
+
+err_t sys_mbox_new(sys_mbox_t *mbox, int size)
+{
+ int mboxsize = size;
+ LWIP_ASSERT("mbox != NULL", mbox != NULL);
+ LWIP_ASSERT("size >= 0", size >= 0);
+ if (size == 0) {
+ mboxsize = 1024;
+ }
+ mbox->head = mbox->tail = 0;
+ mbox->sem = mbox; /* just point to something for sys_mbox_valid() */
+ mbox->q_mem = (void**)malloc(sizeof(void*)*mboxsize);
+ mbox->size = mboxsize;
+ mbox->used = 0;
+
+ memset(mbox->q_mem, 0, sizeof(void*)*mboxsize);
+ return ERR_OK;
+}
+
+void sys_mbox_free(sys_mbox_t *mbox)
+{
+ /* parameter check */
+ LWIP_ASSERT("mbox != NULL", mbox != NULL);
+ LWIP_ASSERT("mbox->sem != NULL", mbox->sem != NULL);
+ LWIP_ASSERT("mbox->sem == mbox", mbox->sem == mbox);
+ LWIP_ASSERT("mbox->q_mem != NULL", mbox->q_mem != NULL);
+ mbox->sem = NULL;
+ free(mbox->q_mem);
+ mbox->q_mem = NULL;
+}
+
+void sys_mbox_set_invalid(sys_mbox_t *mbox)
+{
+ LWIP_ASSERT("mbox != NULL", mbox != NULL);
+ LWIP_ASSERT("mbox->q_mem == NULL", mbox->q_mem == NULL);
+ mbox->sem = NULL;
+ mbox->q_mem = NULL;
+}
+
+void sys_mbox_post(sys_mbox_t *q, void *msg)
+{
+ LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL);
+ LWIP_ASSERT("q->sem == q", q->sem == q);
+ LWIP_ASSERT("q->q_mem != NULL", q->q_mem != NULL);
+ LWIP_ASSERT("q->used >= 0", q->used >= 0);
+ LWIP_ASSERT("q->size > 0", q->size > 0);
+
+ LWIP_ASSERT("mbox already full", q->used < q->size);
+
+ q->q_mem[q->head] = msg;
+ q->head++;
+ if (q->head >= (unsigned int)q->size) {
+ q->head = 0;
+ }
+ LWIP_ASSERT("mbox is full!", q->head != q->tail);
+ q->used++;
+}
+
+err_t sys_mbox_trypost(sys_mbox_t *q, void *msg)
+{
+ LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL);
+ LWIP_ASSERT("q->sem == q", q->sem == q);
+ LWIP_ASSERT("q->q_mem != NULL", q->q_mem != NULL);
+ LWIP_ASSERT("q->used >= 0", q->used >= 0);
+ LWIP_ASSERT("q->size > 0", q->size > 0);
+ LWIP_ASSERT("q->used <= q->size", q->used <= q->size);
+
+ if (q->used == q->size) {
+ return ERR_MEM;
+ }
+ sys_mbox_post(q, msg);
+ return ERR_OK;
+}
+
+u32_t sys_arch_mbox_fetch(sys_mbox_t *q, void **msg, u32_t timeout)
+{
+ u32_t ret = 0;
+ u32_t ret2;
+ LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL);
+ LWIP_ASSERT("q->sem == q", q->sem == q);
+ LWIP_ASSERT("q->q_mem != NULL", q->q_mem != NULL);
+ LWIP_ASSERT("q->used >= 0", q->used >= 0);
+ LWIP_ASSERT("q->size > 0", q->size > 0);
+
+ if (q->used == 0) {
+ /* need to wait */
+ /* need to wait */
+ if(!timeout)
+ {
+ /* wait infinite */
+ LWIP_ASSERT("cannot wait without waiting callback", the_waiting_fn != NULL);
+ do {
+ int expectSomething = the_waiting_fn(NULL, q);
+ LWIP_ASSERT("q->used >= 0", q->used >= 0);
+ LWIP_ASSERT("expecting item available but it's 0", !expectSomething || (q->used > 0));
+ ret++;
+ if (ret == SYS_ARCH_TIMEOUT) {
+ ret--;
+ }
+ } while(q->used == 0);
+ }
+ else
+ {
+ if (the_waiting_fn) {
+ int expectSomething = the_waiting_fn(NULL, q);
+ LWIP_ASSERT("expecting item available count but it's 0", !expectSomething || (q->used > 0));
+ }
+ LWIP_ASSERT("q->used >= 0", q->used >= 0);
+ if (q->used == 0) {
+ if(msg) {
+ *msg = NULL;
+ }
+ return SYS_ARCH_TIMEOUT;
+ }
+ ret = 1;
+ }
+ }
+ LWIP_ASSERT("q->used > 0", q->used > 0);
+ ret2 = sys_arch_mbox_tryfetch(q, msg);
+ LWIP_ASSERT("got no message", ret2 == 0);
+ return ret;
+}
+
+u32_t sys_arch_mbox_tryfetch(sys_mbox_t *q, void **msg)
+{
+ LWIP_ASSERT("q != SYS_MBOX_NULL", q != SYS_MBOX_NULL);
+ LWIP_ASSERT("q->sem == q", q->sem == q);
+ LWIP_ASSERT("q->q_mem != NULL", q->q_mem != NULL);
+ LWIP_ASSERT("q->used >= 0", q->used >= 0);
+ LWIP_ASSERT("q->size > 0", q->size > 0);
+
+ if (!q->used) {
+ return SYS_ARCH_TIMEOUT;
+ }
+ if(msg) {
+ *msg = q->q_mem[q->tail];
+ }
+
+ q->tail++;
+ if (q->tail >= (unsigned int)q->size) {
+ q->tail = 0;
+ }
+ q->used--;
+ LWIP_ASSERT("q->used >= 0", q->used >= 0);
+ return 0;
+}
+
+#if LWIP_NETCONN_SEM_PER_THREAD
+#error LWIP_NETCONN_SEM_PER_THREAD==1 not supported
+#endif /* LWIP_NETCONN_SEM_PER_THREAD */
+
+#endif /* !NO_SYS */
diff --git a/lwip/test/unit/arch/sys_arch.h b/lwip/test/unit/arch/sys_arch.h
new file mode 100644
index 0000000..752e459
--- /dev/null
+++ b/lwip/test/unit/arch/sys_arch.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (c) 2017 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_TEST_SYS_ARCH_H
+#define LWIP_HDR_TEST_SYS_ARCH_H
+
+typedef int sys_sem_t;
+#define sys_sem_valid(sema) ((sema) != NULL)
+
+typedef int sys_mutex_t;
+#define sys_mutex_valid(mutex) (((mutex) != NULL)
+
+struct lwip_mbox {
+ void* sem;
+ void** q_mem;
+ unsigned int head, tail;
+ int size;
+ int used;
+};
+typedef struct lwip_mbox sys_mbox_t;
+#define SYS_MBOX_NULL NULL
+#define sys_mbox_valid(mbox) ((mbox != NULL) && ((mbox)->sem != NULL) && ((mbox)->sem != (void*)-1))
+#define sys_mbox_valid_val(mbox) (((mbox).sem != NULL) && ((mbox).sem != (void*)-1))
+
+/* DWORD (thread id) is used for sys_thread_t but we won't include windows.h */
+typedef u32_t sys_thread_t;
+
+#define SYS_ARCH_DECL_PROTECT(lev)
+#define SYS_ARCH_PROTECT(lev)
+#define SYS_ARCH_UNPROTECT(lev)
+
+/* to implement doing something while blocking on an mbox or semaphore:
+ * pass a function to test_sys_arch_wait_callback() that returns
+ * '0' if waiting again and
+ * '1' if now there should be something to do (used for asserting)
+ */
+typedef int (*test_sys_arch_waiting_fn)(sys_sem_t* wait_sem, sys_mbox_t* wait_mbox);
+void test_sys_arch_wait_callback(test_sys_arch_waiting_fn waiting_fn);
+
+#endif /* LWIP_HDR_TEST_SYS_ARCH_H */
+
diff --git a/lwip/test/unit/core/test_mem.c b/lwip/test/unit/core/test_mem.c
index d3a5d54..c362758 100644
--- a/lwip/test/unit/core/test_mem.c
+++ b/lwip/test/unit/core/test_mem.c
@@ -15,11 +15,13 @@
static void
mem_setup(void)
{
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
mem_teardown(void)
{
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
@@ -35,10 +37,6 @@ START_TEST(test_mem_one)
mem_size_t s1, s2;
LWIP_UNUSED_ARG(_i);
-#if LWIP_DNS
- fail("This test needs DNS turned off (as it mallocs on init)");
-#endif
-
fail_unless(lwip_stats.mem.used == 0);
p1 = mem_malloc(SIZE1);
@@ -61,13 +59,65 @@ START_TEST(test_mem_one)
}
END_TEST
+static void malloc_keep_x(int x, int num, int size, int freestep)
+{
+ int i;
+ void* p[16];
+ LWIP_ASSERT("invalid size", size >= 0 && size < (mem_size_t)-1);
+ memset(p, 0, sizeof(p));
+ for(i = 0; i < num && i < 16; i++) {
+ p[i] = mem_malloc((mem_size_t)size);
+ fail_unless(p[i] != NULL);
+ }
+ for(i = 0; i < num && i < 16; i += freestep) {
+ if (i == x) {
+ continue;
+ }
+ mem_free(p[i]);
+ p[i] = NULL;
+ }
+ for(i = 0; i < num && i < 16; i++) {
+ if (i == x) {
+ continue;
+ }
+ if (p[i] != NULL) {
+ mem_free(p[i]);
+ p[i] = NULL;
+ }
+ }
+ fail_unless(p[x] != NULL);
+ mem_free(p[x]);
+}
+
+START_TEST(test_mem_random)
+{
+ const int num = 16;
+ int x;
+ int size;
+ int freestep;
+ LWIP_UNUSED_ARG(_i);
+
+ fail_unless(lwip_stats.mem.used == 0);
+
+ for (x = 0; x < num; x++) {
+ for (size = 1; size < 32; size++) {
+ for (freestep = 1; freestep <= 3; freestep++) {
+ fail_unless(lwip_stats.mem.used == 0);
+ malloc_keep_x(x, num, size, freestep);
+ fail_unless(lwip_stats.mem.used == 0);
+ }
+ }
+ }
+}
+END_TEST
/** Create the suite including all tests for this module */
Suite *
mem_suite(void)
{
- TFun tests[] = {
- test_mem_one
+ testfunc tests[] = {
+ TESTFUNC(test_mem_one),
+ TESTFUNC(test_mem_random)
};
- return create_suite("MEM", tests, sizeof(tests)/sizeof(TFun), mem_setup, mem_teardown);
+ return create_suite("MEM", tests, sizeof(tests)/sizeof(testfunc), mem_setup, mem_teardown);
}
diff --git a/lwip/test/unit/core/test_mem.h b/lwip/test/unit/core/test_mem.h
index 13803ed..325134c 100644
--- a/lwip/test/unit/core/test_mem.h
+++ b/lwip/test/unit/core/test_mem.h
@@ -1,5 +1,5 @@
-#ifndef __TEST_MEM_H__
-#define __TEST_MEM_H__
+#ifndef LWIP_HDR_TEST_MEM_H
+#define LWIP_HDR_TEST_MEM_H
#include "../lwip_check.h"
diff --git a/lwip/test/unit/core/test_pbuf.c b/lwip/test/unit/core/test_pbuf.c
index 2911078..86b90b2 100644
--- a/lwip/test/unit/core/test_pbuf.c
+++ b/lwip/test/unit/core/test_pbuf.c
@@ -9,21 +9,66 @@
#if LWIP_DNS
#error "This test needs DNS turned off (as it mallocs on init)"
#endif
+#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !LWIP_WND_SCALE
+#error "This test needs TCP OOSEQ queueing and window scaling enabled"
+#endif
/* Setups/teardown functions */
static void
pbuf_setup(void)
{
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
pbuf_teardown(void)
{
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
+#define TESTBUFSIZE_1 65535
+#define TESTBUFSIZE_2 65530
+#define TESTBUFSIZE_3 50050
+static u8_t testbuf_1[TESTBUFSIZE_1];
+static u8_t testbuf_1a[TESTBUFSIZE_1];
+static u8_t testbuf_2[TESTBUFSIZE_2];
+static u8_t testbuf_2a[TESTBUFSIZE_2];
+static u8_t testbuf_3[TESTBUFSIZE_3];
+static u8_t testbuf_3a[TESTBUFSIZE_3];
+
/* Test functions */
+START_TEST(test_pbuf_alloc_zero_pbufs)
+{
+ struct pbuf *p;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, 0, PBUF_ROM);
+ fail_unless(p != NULL);
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+
+ p = pbuf_alloc(PBUF_RAW, 0, PBUF_RAM);
+ fail_unless(p != NULL);
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+
+ p = pbuf_alloc(PBUF_RAW, 0, PBUF_REF);
+ fail_unless(p != NULL);
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+
+ p = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
+ fail_unless(p != NULL);
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+}
+END_TEST
/** Call pbuf_copy on a pbuf with zero length */
START_TEST(test_pbuf_copy_zero_pbuf)
@@ -33,7 +78,7 @@ START_TEST(test_pbuf_copy_zero_pbuf)
LWIP_UNUSED_ARG(_i);
fail_unless(lwip_stats.mem.used == 0);
- fail_unless(lwip_stats.memp[MEMP_PBUF_POOL].used == 0);
+ fail_unless(MEMP_STATS_GET(used, MEMP_PBUF_POOL) == 0);
p1 = pbuf_alloc(PBUF_RAW, 1024, PBUF_RAM);
fail_unless(p1 != NULL);
@@ -57,17 +102,173 @@ START_TEST(test_pbuf_copy_zero_pbuf)
fail_unless(lwip_stats.mem.used == 0);
fail_unless(lwip_stats.mem.used == 0);
- fail_unless(lwip_stats.memp[MEMP_PBUF_POOL].used == 0);
+ fail_unless(MEMP_STATS_GET(used, MEMP_PBUF_POOL) == 0);
+}
+END_TEST
+
+START_TEST(test_pbuf_split_64k_on_small_pbufs)
+{
+ struct pbuf *p, *rest=NULL;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, 1, PBUF_POOL);
+ pbuf_split_64k(p, &rest);
+ fail_unless(p->tot_len == 1);
+ pbuf_free(p);
+}
+END_TEST
+
+START_TEST(test_pbuf_queueing_bigger_than_64k)
+{
+ int i;
+ err_t err;
+ struct pbuf *p1, *p2, *p3, *rest2=NULL, *rest3=NULL;
+ LWIP_UNUSED_ARG(_i);
+
+ for(i = 0; i < TESTBUFSIZE_1; i++) {
+ testbuf_1[i] = (u8_t)rand();
+ }
+ for(i = 0; i < TESTBUFSIZE_2; i++) {
+ testbuf_2[i] = (u8_t)rand();
+ }
+ for(i = 0; i < TESTBUFSIZE_3; i++) {
+ testbuf_3[i] = (u8_t)rand();
+ }
+
+ p1 = pbuf_alloc(PBUF_RAW, TESTBUFSIZE_1, PBUF_POOL);
+ fail_unless(p1 != NULL);
+ p2 = pbuf_alloc(PBUF_RAW, TESTBUFSIZE_2, PBUF_POOL);
+ fail_unless(p2 != NULL);
+ p3 = pbuf_alloc(PBUF_RAW, TESTBUFSIZE_3, PBUF_POOL);
+ fail_unless(p3 != NULL);
+ err = pbuf_take(p1, testbuf_1, TESTBUFSIZE_1);
+ fail_unless(err == ERR_OK);
+ err = pbuf_take(p2, testbuf_2, TESTBUFSIZE_2);
+ fail_unless(err == ERR_OK);
+ err = pbuf_take(p3, testbuf_3, TESTBUFSIZE_3);
+ fail_unless(err == ERR_OK);
+
+ pbuf_cat(p1, p2);
+ pbuf_cat(p1, p3);
+
+ pbuf_split_64k(p1, &rest2);
+ fail_unless(p1->tot_len == TESTBUFSIZE_1);
+ fail_unless(rest2->tot_len == (u16_t)((TESTBUFSIZE_2+TESTBUFSIZE_3) & 0xFFFF));
+ pbuf_split_64k(rest2, &rest3);
+ fail_unless(rest2->tot_len == TESTBUFSIZE_2);
+ fail_unless(rest3->tot_len == TESTBUFSIZE_3);
+
+ pbuf_copy_partial(p1, testbuf_1a, TESTBUFSIZE_1, 0);
+ pbuf_copy_partial(rest2, testbuf_2a, TESTBUFSIZE_2, 0);
+ pbuf_copy_partial(rest3, testbuf_3a, TESTBUFSIZE_3, 0);
+ for(i = 0; i < TESTBUFSIZE_1; i++)
+ fail_unless(testbuf_1[i] == testbuf_1a[i]);
+ for(i = 0; i < TESTBUFSIZE_2; i++)
+ fail_unless(testbuf_2[i] == testbuf_2a[i]);
+ for(i = 0; i < TESTBUFSIZE_3; i++)
+ fail_unless(testbuf_3[i] == testbuf_3a[i]);
+
+ pbuf_free(p1);
+ pbuf_free(rest2);
+ pbuf_free(rest3);
}
END_TEST
+/* Test for bug that writing with pbuf_take_at() did nothing
+ * and returned ERR_OK when writing at beginning of a pbuf
+ * in the chain.
+ */
+START_TEST(test_pbuf_take_at_edge)
+{
+ err_t res;
+ u8_t *out;
+ int i;
+ u8_t testdata[] = { 0x01, 0x08, 0x82, 0x02 };
+ struct pbuf *p = pbuf_alloc(PBUF_RAW, 1024, PBUF_POOL);
+ struct pbuf *q = p->next;
+ LWIP_UNUSED_ARG(_i);
+ /* alloc big enough to get a chain of pbufs */
+ fail_if(p->tot_len == p->len);
+ memset(p->payload, 0, p->len);
+ memset(q->payload, 0, q->len);
+
+ /* copy data to the beginning of first pbuf */
+ res = pbuf_take_at(p, &testdata, sizeof(testdata), 0);
+ fail_unless(res == ERR_OK);
+
+ out = (u8_t*)p->payload;
+ for (i = 0; i < (int)sizeof(testdata); i++) {
+ fail_unless(out[i] == testdata[i],
+ "Bad data at pos %d, was %02X, expected %02X", i, out[i], testdata[i]);
+ }
+
+ /* copy data to the just before end of first pbuf */
+ res = pbuf_take_at(p, &testdata, sizeof(testdata), p->len - 1);
+ fail_unless(res == ERR_OK);
+
+ out = (u8_t*)p->payload;
+ fail_unless(out[p->len - 1] == testdata[0],
+ "Bad data at pos %d, was %02X, expected %02X", p->len - 1, out[p->len - 1], testdata[0]);
+ out = (u8_t*)q->payload;
+ for (i = 1; i < (int)sizeof(testdata); i++) {
+ fail_unless(out[i-1] == testdata[i],
+ "Bad data at pos %d, was %02X, expected %02X", p->len - 1 + i, out[i-1], testdata[i]);
+ }
+
+ /* copy data to the beginning of second pbuf */
+ res = pbuf_take_at(p, &testdata, sizeof(testdata), p->len);
+ fail_unless(res == ERR_OK);
+
+ out = (u8_t*)p->payload;
+ for (i = 0; i < (int)sizeof(testdata); i++) {
+ fail_unless(out[i] == testdata[i],
+ "Bad data at pos %d, was %02X, expected %02X", p->len+i, out[i], testdata[i]);
+ }
+ pbuf_free(p);
+}
+END_TEST
+
+/* Verify pbuf_put_at()/pbuf_get_at() when using
+ * offsets equal to beginning of new pbuf in chain
+ */
+START_TEST(test_pbuf_get_put_at_edge)
+{
+ u8_t *out;
+ u8_t testdata = 0x01;
+ u8_t getdata;
+ struct pbuf *p = pbuf_alloc(PBUF_RAW, 1024, PBUF_POOL);
+ struct pbuf *q = p->next;
+ LWIP_UNUSED_ARG(_i);
+ /* alloc big enough to get a chain of pbufs */
+ fail_if(p->tot_len == p->len);
+ memset(p->payload, 0, p->len);
+ memset(q->payload, 0, q->len);
+
+ /* put byte at the beginning of second pbuf */
+ pbuf_put_at(p, p->len, testdata);
+
+ out = (u8_t*)q->payload;
+ fail_unless(*out == testdata,
+ "Bad data at pos %d, was %02X, expected %02X", p->len, *out, testdata);
+
+ getdata = pbuf_get_at(p, p->len);
+ fail_unless(*out == getdata,
+ "pbuf_get_at() returned bad data at pos %d, was %02X, expected %02X", p->len, getdata, *out);
+ pbuf_free(p);
+}
+END_TEST
/** Create the suite including all tests for this module */
Suite *
pbuf_suite(void)
{
- TFun tests[] = {
- test_pbuf_copy_zero_pbuf
+ testfunc tests[] = {
+ TESTFUNC(test_pbuf_alloc_zero_pbufs),
+ TESTFUNC(test_pbuf_copy_zero_pbuf),
+ TESTFUNC(test_pbuf_split_64k_on_small_pbufs),
+ TESTFUNC(test_pbuf_queueing_bigger_than_64k),
+ TESTFUNC(test_pbuf_take_at_edge),
+ TESTFUNC(test_pbuf_get_put_at_edge)
};
- return create_suite("PBUF", tests, sizeof(tests)/sizeof(TFun), pbuf_setup, pbuf_teardown);
+ return create_suite("PBUF", tests, sizeof(tests)/sizeof(testfunc), pbuf_setup, pbuf_teardown);
}
diff --git a/lwip/test/unit/core/test_pbuf.h b/lwip/test/unit/core/test_pbuf.h
index b2715ad..da7730a 100644
--- a/lwip/test/unit/core/test_pbuf.h
+++ b/lwip/test/unit/core/test_pbuf.h
@@ -1,5 +1,5 @@
-#ifndef __TEST_PBUF_H__
-#define __TEST_PBUF_H__
+#ifndef LWIP_HDR_TEST_PBUF_H
+#define LWIP_HDR_TEST_PBUF_H
#include "../lwip_check.h"
diff --git a/lwip/test/unit/dhcp/test_dhcp.c b/lwip/test/unit/dhcp/test_dhcp.c
index 4b40de8..1b0c884 100644
--- a/lwip/test/unit/dhcp/test_dhcp.c
+++ b/lwip/test/unit/dhcp/test_dhcp.c
@@ -2,7 +2,9 @@
#include "lwip/netif.h"
#include "lwip/dhcp.h"
-#include "netif/etharp.h"
+#include "lwip/prot/dhcp.h"
+#include "lwip/etharp.h"
+#include "netif/ethernet.h"
struct netif net_test;
@@ -119,6 +121,8 @@ static enum tcase {
TEST_LWIP_DHCP_NAK,
TEST_LWIP_DHCP_RELAY,
TEST_LWIP_DHCP_NAK_NO_ENDMARKER,
+ TEST_LWIP_DHCP_INVALID_OVERLOAD,
+ TEST_NONE
} tcase;
static int debug = 0;
@@ -136,10 +140,11 @@ static void tick_lwip(void)
}
}
-static void send_pkt(struct netif *netif, const u8_t *data, u32_t len)
+static void send_pkt(struct netif *netif, const u8_t *data, size_t len)
{
- struct pbuf *p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
- struct pbuf *q;
+ struct pbuf *p, *q;
+ LWIP_ASSERT("pkt too big", len <= 0xFFFF);
+ p = pbuf_alloc(PBUF_RAW, (u16_t)len, PBUF_POOL);
if (debug) {
/* Dump data */
@@ -184,10 +189,12 @@ static err_t testif_init(struct netif *netif)
static void dhcp_setup(void)
{
txpacket = 0;
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void dhcp_teardown(void)
{
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void check_pkt(struct pbuf *p, u32_t pos, const u8_t *mem, u32_t len)
@@ -202,7 +209,7 @@ static void check_pkt(struct pbuf *p, u32_t pos, const u8_t *mem, u32_t len)
fail_if(p == NULL);
fail_unless(pos + len <= p->len); /* All data we seek within same pbuf */
- data = p->payload;
+ data = (u8_t*)p->payload;
fail_if(memcmp(&data[pos], mem, len), "data at pos %d, len %d in packet %d did not match", pos, len, txpacket);
}
@@ -221,7 +228,7 @@ static void check_pkt_fuzzy(struct pbuf *p, u32_t startpos, const u8_t *mem, u32
fail_unless(startpos + len <= p->len); /* All data we seek within same pbuf */
found = 0;
- data = p->payload;
+ data = (u8_t*)p->payload;
for (i = startpos; i <= (p->len - len); i++) {
if (memcmp(&data[i], mem, len) == 0) {
found = 1;
@@ -300,6 +307,9 @@ static err_t lwip_tx_func(struct netif *netif, struct pbuf *p)
check_pkt(p, 12, arpproto, sizeof(arpproto)); /* eth level proto: ip */
break;
}
+ default:
+ fail();
+ break;
}
break;
@@ -404,6 +414,9 @@ static err_t lwip_tx_func(struct netif *netif, struct pbuf *p)
check_pkt_fuzzy(p, 282, dhcp_request_opt, sizeof(dhcp_request_opt));
break;
}
+ default:
+ fail();
+ break;
}
break;
@@ -420,9 +433,9 @@ static err_t lwip_tx_func(struct netif *netif, struct pbuf *p)
*/
START_TEST(test_dhcp)
{
- struct ip_addr addr;
- struct ip_addr netmask;
- struct ip_addr gw;
+ ip4_addr_t addr;
+ ip4_addr_t netmask;
+ ip4_addr_t gw;
int i;
u32_t xid;
LWIP_UNUSED_ARG(_i);
@@ -435,41 +448,39 @@ START_TEST(test_dhcp)
IP4_ADDR(&gw, 0, 0, 0, 0);
netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
+ netif_set_up(&net_test);
dhcp_start(&net_test);
fail_unless(txpacket == 1); /* DHCP discover sent */
- xid = net_test.dhcp->xid; /* Write bad xid, not using htonl! */
+ xid = netif_dhcp_data(&net_test)->xid; /* Write bad xid, not using htonl! */
memcpy(&dhcp_offer[46], &xid, 4);
send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer));
- /* Interface down */
- fail_if(netif_is_up(&net_test));
-
/* IP addresses should be zero */
- fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr)));
- fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr)));
- fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr)));
+ fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&netmask, &net_test.netmask, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&gw, &net_test.gw, sizeof(ip4_addr_t)));
fail_unless(txpacket == 1, "TX %d packets, expected 1", txpacket); /* Nothing more sent */
- xid = htonl(net_test.dhcp->xid);
+ xid = htonl(netif_dhcp_data(&net_test)->xid);
memcpy(&dhcp_offer[46], &xid, 4); /* insert correct transaction id */
send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer));
fail_unless(txpacket == 2, "TX %d packets, expected 2", txpacket); /* DHCP request sent */
- xid = net_test.dhcp->xid; /* Write bad xid, not using htonl! */
+ xid = netif_dhcp_data(&net_test)->xid; /* Write bad xid, not using htonl! */
memcpy(&dhcp_ack[46], &xid, 4);
send_pkt(&net_test, dhcp_ack, sizeof(dhcp_ack));
fail_unless(txpacket == 2, "TX %d packets, still expected 2", txpacket); /* No more sent */
- xid = htonl(net_test.dhcp->xid); /* xid updated */
+ xid = htonl(netif_dhcp_data(&net_test)->xid); /* xid updated */
memcpy(&dhcp_ack[46], &xid, 4); /* insert transaction id */
send_pkt(&net_test, dhcp_ack, sizeof(dhcp_ack));
for (i = 0; i < 20; i++) {
tick_lwip();
}
- fail_unless(txpacket == 4, "TX %d packets, expected 4", txpacket); /* ARP requests sent */
+ fail_unless(txpacket == 5, "TX %d packets, expected 5", txpacket); /* ARP requests sent */
/* Interface up */
fail_unless(netif_is_up(&net_test));
@@ -478,10 +489,13 @@ START_TEST(test_dhcp)
IP4_ADDR(&addr, 195, 170, 189, 200);
IP4_ADDR(&netmask, 255, 255, 255, 0);
IP4_ADDR(&gw, 195, 170, 189, 171);
- fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr)));
- fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr)));
- fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr)));
+ fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&netmask, &net_test.netmask, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&gw, &net_test.gw, sizeof(ip4_addr_t)));
+ tcase = TEST_NONE;
+ dhcp_stop(&net_test);
+ dhcp_cleanup(&net_test);
netif_remove(&net_test);
}
END_TEST
@@ -492,9 +506,9 @@ END_TEST
*/
START_TEST(test_dhcp_nak)
{
- struct ip_addr addr;
- struct ip_addr netmask;
- struct ip_addr gw;
+ ip4_addr_t addr;
+ ip4_addr_t netmask;
+ ip4_addr_t gw;
u32_t xid;
LWIP_UNUSED_ARG(_i);
@@ -506,34 +520,32 @@ START_TEST(test_dhcp_nak)
IP4_ADDR(&gw, 0, 0, 0, 0);
netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
+ netif_set_up(&net_test);
dhcp_start(&net_test);
fail_unless(txpacket == 1); /* DHCP discover sent */
- xid = net_test.dhcp->xid; /* Write bad xid, not using htonl! */
+ xid = netif_dhcp_data(&net_test)->xid; /* Write bad xid, not using htonl! */
memcpy(&dhcp_offer[46], &xid, 4);
send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer));
- /* Interface down */
- fail_if(netif_is_up(&net_test));
-
/* IP addresses should be zero */
- fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr)));
- fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr)));
- fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr)));
+ fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&netmask, &net_test.netmask, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&gw, &net_test.gw, sizeof(ip4_addr_t)));
fail_unless(txpacket == 1); /* Nothing more sent */
- xid = htonl(net_test.dhcp->xid);
+ xid = htonl(netif_dhcp_data(&net_test)->xid);
memcpy(&dhcp_offer[46], &xid, 4); /* insert correct transaction id */
send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer));
fail_unless(txpacket == 2); /* DHCP request sent */
- xid = net_test.dhcp->xid; /* Write bad xid, not using htonl! */
+ xid = netif_dhcp_data(&net_test)->xid; /* Write bad xid, not using htonl! */
memcpy(&dhcp_ack[46], &xid, 4);
send_pkt(&net_test, dhcp_ack, sizeof(dhcp_ack));
fail_unless(txpacket == 2); /* No more sent */
- xid = htonl(net_test.dhcp->xid); /* xid updated */
+ xid = htonl(netif_dhcp_data(&net_test)->xid); /* xid updated */
memcpy(&dhcp_ack[46], &xid, 4); /* insert transaction id */
send_pkt(&net_test, dhcp_ack, sizeof(dhcp_ack));
@@ -546,6 +558,9 @@ START_TEST(test_dhcp_nak)
fail_unless(txpacket == 4); /* DHCP nak sent */
+ tcase = TEST_NONE;
+ dhcp_stop(&net_test);
+ dhcp_cleanup(&net_test);
netif_remove(&net_test);
}
END_TEST
@@ -557,7 +572,7 @@ END_TEST
*/
START_TEST(test_dhcp_relayed)
{
- const u8_t relay_offer[] = {
+ u8_t relay_offer[] = {
0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d,
0x00, 0x22, 0x93, 0x5a, 0xf7, 0x60,
0x08, 0x00, 0x45, 0x00,
@@ -602,7 +617,7 @@ START_TEST(test_dhcp_relayed)
0x04, 0x0a, 0xb5, 0x04, 0x01, 0xff
};
- const u8_t relay_ack1[] = {
+ u8_t relay_ack1[] = {
0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x22,
0x93, 0x5a, 0xf7, 0x60, 0x08, 0x00, 0x45, 0x00,
0x01, 0x38, 0xfd, 0x55, 0x00, 0x00, 0x40, 0x11,
@@ -646,7 +661,7 @@ START_TEST(test_dhcp_relayed)
0x04, 0x0a, 0xb5, 0x04, 0x01, 0xff
};
- const u8_t relay_ack2[] = {
+ u8_t relay_ack2[] = {
0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d,
0x00, 0x22, 0x93, 0x5a, 0xf7, 0x60,
0x08, 0x00, 0x45, 0x00,
@@ -711,9 +726,9 @@ START_TEST(test_dhcp_relayed)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00 };
- struct ip_addr addr;
- struct ip_addr netmask;
- struct ip_addr gw;
+ ip4_addr_t addr;
+ ip4_addr_t netmask;
+ ip4_addr_t gw;
int i;
u32_t xid;
LWIP_UNUSED_ARG(_i);
@@ -726,34 +741,32 @@ START_TEST(test_dhcp_relayed)
IP4_ADDR(&gw, 0, 0, 0, 0);
netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
+ netif_set_up(&net_test);
dhcp_start(&net_test);
fail_unless(txpacket == 1); /* DHCP discover sent */
- /* Interface down */
- fail_if(netif_is_up(&net_test));
-
/* IP addresses should be zero */
- fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr)));
- fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr)));
- fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr)));
+ fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&netmask, &net_test.netmask, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&gw, &net_test.gw, sizeof(ip4_addr_t)));
fail_unless(txpacket == 1); /* Nothing more sent */
- xid = htonl(net_test.dhcp->xid);
+ xid = htonl(netif_dhcp_data(&net_test)->xid);
memcpy(&relay_offer[46], &xid, 4); /* insert correct transaction id */
send_pkt(&net_test, relay_offer, sizeof(relay_offer));
/* request sent? */
fail_unless(txpacket == 2, "txpkt = %d, should be 2", txpacket);
- xid = htonl(net_test.dhcp->xid); /* xid updated */
+ xid = htonl(netif_dhcp_data(&net_test)->xid); /* xid updated */
memcpy(&relay_ack1[46], &xid, 4); /* insert transaction id */
send_pkt(&net_test, relay_ack1, sizeof(relay_ack1));
for (i = 0; i < 25; i++) {
tick_lwip();
}
- fail_unless(txpacket == 4, "txpkt should be 5, is %d", txpacket); /* ARP requests sent */
+ fail_unless(txpacket == 5, "txpkt should be 5, is %d", txpacket); /* ARP requests sent */
/* Interface up */
fail_unless(netif_is_up(&net_test));
@@ -762,11 +775,11 @@ START_TEST(test_dhcp_relayed)
IP4_ADDR(&addr, 79, 138, 51, 5);
IP4_ADDR(&netmask, 255, 255, 254, 0);
IP4_ADDR(&gw, 79, 138, 50, 1);
- fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr)));
- fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr)));
- fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr)));
+ fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&netmask, &net_test.netmask, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&gw, &net_test.gw, sizeof(ip4_addr_t)));
- fail_unless(txpacket == 4, "txpacket = %d", txpacket);
+ fail_unless(txpacket == 5, "txpacket = %d", txpacket);
for (i = 0; i < 108000 - 25; i++) {
tick_lwip();
@@ -782,7 +795,7 @@ START_TEST(test_dhcp_relayed)
fail_unless(txpacket == 7, "txpacket = %d", txpacket);
fail_unless(netif_is_up(&net_test));
- xid = htonl(net_test.dhcp->xid); /* xid updated */
+ xid = htonl(netif_dhcp_data(&net_test)->xid); /* xid updated */
memcpy(&relay_ack2[46], &xid, 4); /* insert transaction id */
send_pkt(&net_test, relay_ack2, sizeof(relay_ack2));
@@ -792,6 +805,9 @@ START_TEST(test_dhcp_relayed)
fail_unless(txpacket == 7, "txpacket = %d", txpacket);
+ tcase = TEST_NONE;
+ dhcp_stop(&net_test);
+ dhcp_cleanup(&net_test);
netif_remove(&net_test);
}
@@ -799,9 +815,9 @@ END_TEST
START_TEST(test_dhcp_nak_no_endmarker)
{
- struct ip_addr addr;
- struct ip_addr netmask;
- struct ip_addr gw;
+ ip4_addr_t addr;
+ ip4_addr_t netmask;
+ ip4_addr_t gw;
u8_t dhcp_nack_no_endmarker[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x54, 0x75,
@@ -866,51 +882,161 @@ START_TEST(test_dhcp_nak_no_endmarker)
IP4_ADDR(&gw, 0, 0, 0, 0);
netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
+ netif_set_up(&net_test);
dhcp_start(&net_test);
fail_unless(txpacket == 1); /* DHCP discover sent */
- xid = net_test.dhcp->xid; /* Write bad xid, not using htonl! */
+ xid = netif_dhcp_data(&net_test)->xid; /* Write bad xid, not using htonl! */
memcpy(&dhcp_offer[46], &xid, 4);
send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer));
- /* Interface down */
- fail_if(netif_is_up(&net_test));
-
/* IP addresses should be zero */
- fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(struct ip_addr)));
- fail_if(memcmp(&netmask, &net_test.netmask, sizeof(struct ip_addr)));
- fail_if(memcmp(&gw, &net_test.gw, sizeof(struct ip_addr)));
+ fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&netmask, &net_test.netmask, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&gw, &net_test.gw, sizeof(ip4_addr_t)));
fail_unless(txpacket == 1); /* Nothing more sent */
- xid = htonl(net_test.dhcp->xid);
+ xid = htonl(netif_dhcp_data(&net_test)->xid);
memcpy(&dhcp_offer[46], &xid, 4); /* insert correct transaction id */
send_pkt(&net_test, dhcp_offer, sizeof(dhcp_offer));
- fail_unless(net_test.dhcp->state == DHCP_REQUESTING);
+ fail_unless(netif_dhcp_data(&net_test)->state == DHCP_STATE_REQUESTING);
fail_unless(txpacket == 2); /* No more sent */
- xid = htonl(net_test.dhcp->xid); /* xid updated */
+ xid = htonl(netif_dhcp_data(&net_test)->xid); /* xid updated */
memcpy(&dhcp_nack_no_endmarker[46], &xid, 4); /* insert transaction id */
send_pkt(&net_test, dhcp_nack_no_endmarker, sizeof(dhcp_nack_no_endmarker));
/* NAK should put us in another state for a while, no other way detecting it */
- fail_unless(net_test.dhcp->state != DHCP_REQUESTING);
+ fail_unless(netif_dhcp_data(&net_test)->state != DHCP_STATE_REQUESTING);
+ tcase = TEST_NONE;
+ dhcp_stop(&net_test);
+ dhcp_cleanup(&net_test);
netif_remove(&net_test);
}
END_TEST
+START_TEST(test_dhcp_invalid_overload)
+{
+ u8_t dhcp_offer_invalid_overload[] = {
+ 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, /* To unit */
+ 0x00, 0x0F, 0xEE, 0x30, 0xAB, 0x22, /* From Remote host */
+ 0x08, 0x00, /* Protocol: IP */
+ 0x45, 0x10, 0x01, 0x48, 0x00, 0x00, 0x00, 0x00, 0x80, 0x11, 0x36, 0xcc, 0xc3, 0xaa, 0xbd, 0xab, 0xc3, 0xaa, 0xbd, 0xc8, /* IP header */
+ 0x00, 0x43, 0x00, 0x44, 0x01, 0x34, 0x00, 0x00, /* UDP header */
+
+ 0x02, /* Type == Boot reply */
+ 0x01, 0x06, /* Hw Ethernet, 6 bytes addrlen */
+ 0x00, /* 0 hops */
+ 0xAA, 0xAA, 0xAA, 0xAA, /* Transaction id, will be overwritten */
+ 0x00, 0x00, /* 0 seconds elapsed */
+ 0x00, 0x00, /* Flags (unicast) */
+ 0x00, 0x00, 0x00, 0x00, /* Client ip */
+ 0xc3, 0xaa, 0xbd, 0xc8, /* Your IP */
+ 0xc3, 0xaa, 0xbd, 0xab, /* DHCP server ip */
+ 0x00, 0x00, 0x00, 0x00, /* relay agent */
+ 0x00, 0x23, 0xc1, 0xde, 0xd0, 0x0d, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* MAC addr + padding */
+
+ /* Empty server name */
+ 0x34, 0x01, 0x02, 0xff, /* Overload: SNAME + END */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* Empty boot file name */
+ 0x34, 0x01, 0x01, 0xff, /* Overload FILE + END */
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+
+ 0x63, 0x82, 0x53, 0x63, /* Magic cookie */
+ 0x35, 0x01, 0x02, /* Message type: Offer */
+ 0x36, 0x04, 0xc3, 0xaa, 0xbd, 0xab, /* Server identifier (IP) */
+ 0x33, 0x04, 0x00, 0x00, 0x00, 0x78, /* Lease time 2 minutes */
+ 0x03, 0x04, 0xc3, 0xaa, 0xbd, 0xab, /* Router IP */
+ 0x01, 0x04, 0xff, 0xff, 0xff, 0x00, /* Subnet mask */
+ 0x34, 0x01, 0x03, /* Overload: FILE + SNAME */
+ 0xff, /* End option */
+ 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, /* Padding */
+ };
+ ip4_addr_t addr;
+ ip4_addr_t netmask;
+ ip4_addr_t gw;
+ u32_t xid;
+ LWIP_UNUSED_ARG(_i);
+
+ tcase = TEST_LWIP_DHCP_INVALID_OVERLOAD;
+ setdebug(0);
+
+ IP4_ADDR(&addr, 0, 0, 0, 0);
+ IP4_ADDR(&netmask, 0, 0, 0, 0);
+ IP4_ADDR(&gw, 0, 0, 0, 0);
+
+ netif_add(&net_test, &addr, &netmask, &gw, &net_test, testif_init, ethernet_input);
+ netif_set_up(&net_test);
+
+ dhcp_start(&net_test);
+
+ fail_unless(txpacket == 1); /* DHCP discover sent */
+ xid = htonl(netif_dhcp_data(&net_test)->xid);
+ memcpy(&dhcp_offer_invalid_overload[46], &xid, 4); /* insert correct transaction id */
+ dhcp_offer_invalid_overload[311] = 3;
+ send_pkt(&net_test, dhcp_offer_invalid_overload, sizeof(dhcp_offer_invalid_overload));
+ /* IP addresses should be zero */
+ fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&netmask, &net_test.netmask, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&gw, &net_test.gw, sizeof(ip4_addr_t)));
+ fail_unless(txpacket == 1); /* Nothing more sent */
+
+ dhcp_offer_invalid_overload[311] = 2;
+ send_pkt(&net_test, dhcp_offer_invalid_overload, sizeof(dhcp_offer_invalid_overload));
+ /* IP addresses should be zero */
+ fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&netmask, &net_test.netmask, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&gw, &net_test.gw, sizeof(ip4_addr_t)));
+ fail_unless(txpacket == 1); /* Nothing more sent */
+
+ dhcp_offer_invalid_overload[311] = 1;
+ send_pkt(&net_test, dhcp_offer_invalid_overload, sizeof(dhcp_offer_invalid_overload));
+ /* IP addresses should be zero */
+ fail_if(memcmp(&addr, &net_test.ip_addr, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&netmask, &net_test.netmask, sizeof(ip4_addr_t)));
+ fail_if(memcmp(&gw, &net_test.gw, sizeof(ip4_addr_t)));
+ fail_unless(txpacket == 1); /* Nothing more sent */
+
+ dhcp_offer_invalid_overload[311] = 0;
+ send_pkt(&net_test, dhcp_offer_invalid_overload, sizeof(dhcp_offer));
+
+ fail_unless(netif_dhcp_data(&net_test)->state == DHCP_STATE_REQUESTING);
+
+ fail_unless(txpacket == 2); /* No more sent */
+ xid = htonl(netif_dhcp_data(&net_test)->xid); /* xid updated */
+
+ tcase = TEST_NONE;
+ dhcp_stop(&net_test);
+ dhcp_cleanup(&net_test);
+ netif_remove(&net_test);
+}
+END_TEST
/** Create the suite including all tests for this module */
Suite *
dhcp_suite(void)
{
- TFun tests[] = {
- test_dhcp,
- test_dhcp_nak,
- test_dhcp_relayed,
- test_dhcp_nak_no_endmarker
+ testfunc tests[] = {
+ TESTFUNC(test_dhcp),
+ TESTFUNC(test_dhcp_nak),
+ TESTFUNC(test_dhcp_relayed),
+ TESTFUNC(test_dhcp_nak_no_endmarker),
+ TESTFUNC(test_dhcp_invalid_overload)
};
- return create_suite("DHCP", tests, sizeof(tests)/sizeof(TFun), dhcp_setup, dhcp_teardown);
+ return create_suite("DHCP", tests, sizeof(tests)/sizeof(testfunc), dhcp_setup, dhcp_teardown);
}
diff --git a/lwip/test/unit/dhcp/test_dhcp.h b/lwip/test/unit/dhcp/test_dhcp.h
index aff44b7..0d88fa1 100644
--- a/lwip/test/unit/dhcp/test_dhcp.h
+++ b/lwip/test/unit/dhcp/test_dhcp.h
@@ -1,5 +1,5 @@
-#ifndef __TEST_DHCP_H__
-#define __TEST_DHCP_H__
+#ifndef LWIP_HDR_TEST_DHCP_H
+#define LWIP_HDR_TEST_DHCP_H
#include "../lwip_check.h"
diff --git a/lwip/test/unit/etharp/test_etharp.c b/lwip/test/unit/etharp/test_etharp.c
index cbbc950..96a3242 100644
--- a/lwip/test/unit/etharp/test_etharp.c
+++ b/lwip/test/unit/etharp/test_etharp.c
@@ -1,8 +1,10 @@
#include "test_etharp.h"
#include "lwip/udp.h"
-#include "netif/etharp.h"
+#include "lwip/etharp.h"
+#include "netif/ethernet.h"
#include "lwip/stats.h"
+#include "lwip/prot/iana.h"
#if !LWIP_STATS || !UDP_STATS || !MEMP_STATS || !ETHARP_STATS
#error "This tests needs UDP-, MEMP- and ETHARP-statistics enabled"
@@ -12,11 +14,11 @@
#endif
static struct netif test_netif;
-static ip_addr_t test_ipaddr, test_netmask, test_gw;
-struct eth_addr test_ethaddr = {1,1,1,1,1,1};
-struct eth_addr test_ethaddr2 = {1,1,1,1,1,2};
-struct eth_addr test_ethaddr3 = {1,1,1,1,1,3};
-struct eth_addr test_ethaddr4 = {1,1,1,1,1,4};
+static ip4_addr_t test_ipaddr, test_netmask, test_gw;
+struct eth_addr test_ethaddr = {{1,1,1,1,1,1}};
+struct eth_addr test_ethaddr2 = {{1,1,1,1,1,2}};
+struct eth_addr test_ethaddr3 = {{1,1,1,1,1,3}};
+struct eth_addr test_ethaddr4 = {{1,1,1,1,1,4}};
static int linkoutput_ctr;
/* Helper functions */
@@ -72,7 +74,7 @@ default_netif_remove(void)
}
static void
-create_arp_response(ip_addr_t *adr)
+create_arp_response(ip4_addr_t *adr)
{
int k;
struct eth_hdr *ethhdr;
@@ -88,14 +90,14 @@ create_arp_response(ip_addr_t *adr)
ethhdr->src = test_ethaddr2;
ethhdr->type = htons(ETHTYPE_ARP);
- etharphdr->hwtype = htons(/*HWTYPE_ETHERNET*/ 1);
+ etharphdr->hwtype = htons(LWIP_IANA_HWTYPE_ETHERNET);
etharphdr->proto = htons(ETHTYPE_IP);
etharphdr->hwlen = ETHARP_HWADDR_LEN;
- etharphdr->protolen = sizeof(ip_addr_t);
+ etharphdr->protolen = sizeof(ip4_addr_t);
etharphdr->opcode = htons(ARP_REPLY);
- SMEMCPY(&etharphdr->sipaddr, adr, sizeof(ip_addr_t));
- SMEMCPY(&etharphdr->dipaddr, &test_ipaddr, sizeof(ip_addr_t));
+ SMEMCPY(&etharphdr->sipaddr, adr, sizeof(ip4_addr_t));
+ SMEMCPY(&etharphdr->dipaddr, &test_ipaddr, sizeof(ip4_addr_t));
k = 6;
while(k > 0) {
@@ -118,6 +120,7 @@ etharp_setup(void)
{
etharp_remove_all();
default_netif_add();
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
@@ -125,6 +128,7 @@ etharp_teardown(void)
{
etharp_remove_all();
default_netif_remove();
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
@@ -136,7 +140,7 @@ START_TEST(test_etharp_table)
err_t err;
#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
s8_t idx;
- ip_addr_t *unused_ipaddr;
+ const ip4_addr_t *unused_ipaddr;
struct eth_addr *unused_ethaddr;
struct udp_pcb* pcb;
LWIP_UNUSED_ARG(_i);
@@ -150,7 +154,7 @@ START_TEST(test_etharp_table)
pcb = udp_new();
fail_unless(pcb != NULL);
if (pcb != NULL) {
- ip_addr_t adrs[ARP_TABLE_SIZE + 2];
+ ip4_addr_t adrs[ARP_TABLE_SIZE + 2];
int i;
for(i = 0; i < ARP_TABLE_SIZE + 2; i++) {
IP4_ADDR(&adrs[i], 192,168,0,i+2);
@@ -160,8 +164,11 @@ START_TEST(test_etharp_table)
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_RAM);
fail_unless(p != NULL);
if (p != NULL) {
- err_t err = udp_sendto(pcb, p, &adrs[i], 123);
- fail_unless(err == ERR_OK);
+ err_t err2;
+ ip_addr_t dst;
+ ip_addr_copy_from_ip4(dst, adrs[i]);
+ err2 = udp_sendto(pcb, p, &dst, 123);
+ fail_unless(err2 == ERR_OK);
/* etharp request sent? */
fail_unless(linkoutput_ctr == (2*i) + 1);
pbuf_free(p);
@@ -192,8 +199,11 @@ START_TEST(test_etharp_table)
struct pbuf *p = pbuf_alloc(PBUF_TRANSPORT, 10, PBUF_RAM);
fail_unless(p != NULL);
if (p != NULL) {
- err_t err = udp_sendto(pcb, p, &adrs[i], 123);
- fail_unless(err == ERR_OK);
+ err_t err2;
+ ip_addr_t dst;
+ ip_addr_copy_from_ip4(dst, adrs[i]);
+ err2 = udp_sendto(pcb, p, &dst, 123);
+ fail_unless(err2 == ERR_OK);
/* etharp request sent? */
fail_unless(linkoutput_ctr == (2*i) + 1);
pbuf_free(p);
@@ -255,8 +265,8 @@ END_TEST
Suite *
etharp_suite(void)
{
- TFun tests[] = {
- test_etharp_table
+ testfunc tests[] = {
+ TESTFUNC(test_etharp_table)
};
- return create_suite("ETHARP", tests, sizeof(tests)/sizeof(TFun), etharp_setup, etharp_teardown);
+ return create_suite("ETHARP", tests, sizeof(tests)/sizeof(testfunc), etharp_setup, etharp_teardown);
}
diff --git a/lwip/test/unit/etharp/test_etharp.h b/lwip/test/unit/etharp/test_etharp.h
index 96e00c3..2dd772d 100644
--- a/lwip/test/unit/etharp/test_etharp.h
+++ b/lwip/test/unit/etharp/test_etharp.h
@@ -1,5 +1,5 @@
-#ifndef __TEST_ETHARP_H__
-#define __TEST_ETHARP_H__
+#ifndef LWIP_HDR_TEST_ETHARP_H
+#define LWIP_HDR_TEST_ETHARP_H
#include "../lwip_check.h"
diff --git a/lwip/test/unit/ip4/test_ip4.c b/lwip/test/unit/ip4/test_ip4.c
new file mode 100644
index 0000000..a5a7fd9
--- /dev/null
+++ b/lwip/test/unit/ip4/test_ip4.c
@@ -0,0 +1,160 @@
+#include "test_ip4.h"
+
+#include "lwip/ip4.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/stats.h"
+#include "lwip/prot/ip.h"
+#include "lwip/prot/ip4.h"
+
+#include "lwip/tcpip.h"
+
+#if !LWIP_IPV4 || !IP_REASSEMBLY || !MIB2_STATS || !IPFRAG_STATS
+#error "This tests needs LWIP_IPV4, IP_REASSEMBLY; MIB2- and IPFRAG-statistics enabled"
+#endif
+
+/* Helper functions */
+static void
+create_ip4_input_fragment(u16_t ip_id, u16_t start, u16_t len, int last)
+{
+ struct pbuf *p;
+ struct netif *input_netif = netif_list; /* just use any netif */
+ fail_unless((start & 7) == 0);
+ fail_unless(((len & 7) == 0) || last);
+ fail_unless(input_netif != NULL);
+
+ p = pbuf_alloc(PBUF_RAW, len + sizeof(struct ip_hdr), PBUF_RAM);
+ fail_unless(p != NULL);
+ if (p != NULL) {
+ err_t err;
+ struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
+ IPH_VHL_SET(iphdr, 4, sizeof(struct ip_hdr) / 4);
+ IPH_TOS_SET(iphdr, 0);
+ IPH_LEN_SET(iphdr, lwip_htons(p->tot_len));
+ IPH_ID_SET(iphdr, lwip_htons(ip_id));
+ if (last) {
+ IPH_OFFSET_SET(iphdr, lwip_htons(start / 8));
+ } else {
+ IPH_OFFSET_SET(iphdr, lwip_htons((start / 8) | IP_MF));
+ }
+ IPH_TTL_SET(iphdr, 5);
+ IPH_PROTO_SET(iphdr, IP_PROTO_UDP);
+ IPH_CHKSUM_SET(iphdr, 0);
+ ip4_addr_copy(iphdr->src, *netif_ip4_addr(input_netif));
+ iphdr->src.addr = lwip_htonl(lwip_htonl(iphdr->src.addr) + 1);
+ ip4_addr_copy(iphdr->dest, *netif_ip4_addr(input_netif));
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, sizeof(struct ip_hdr)));
+
+ err = ip4_input(p, input_netif);
+ if (err != ERR_OK) {
+ pbuf_free(p);
+ }
+ fail_unless(err == ERR_OK);
+ }
+}
+
+/* Setups/teardown functions */
+
+static void
+ip4_setup(void)
+{
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
+}
+
+static void
+ip4_teardown(void)
+{
+ if (netif_list->loop_first != NULL) {
+ pbuf_free(netif_list->loop_first);
+ netif_list->loop_first = NULL;
+ }
+ netif_list->loop_last = NULL;
+ /* poll until all memory is released... */
+ tcpip_thread_poll_one();
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
+}
+
+
+/* Test functions */
+
+START_TEST(test_ip4_reass)
+{
+ const u16_t ip_id = 128;
+ LWIP_UNUSED_ARG(_i);
+
+ memset(&lwip_stats.mib2, 0, sizeof(lwip_stats.mib2));
+
+ create_ip4_input_fragment(ip_id, 8*200, 200, 1);
+ fail_unless(lwip_stats.ip_frag.recv == 1);
+ fail_unless(lwip_stats.ip_frag.err == 0);
+ fail_unless(lwip_stats.ip_frag.memerr == 0);
+ fail_unless(lwip_stats.ip_frag.drop == 0);
+ fail_unless(lwip_stats.mib2.ipreasmoks == 0);
+
+ create_ip4_input_fragment(ip_id, 0*200, 200, 0);
+ fail_unless(lwip_stats.ip_frag.recv == 2);
+ fail_unless(lwip_stats.ip_frag.err == 0);
+ fail_unless(lwip_stats.ip_frag.memerr == 0);
+ fail_unless(lwip_stats.ip_frag.drop == 0);
+ fail_unless(lwip_stats.mib2.ipreasmoks == 0);
+
+ create_ip4_input_fragment(ip_id, 1*200, 200, 0);
+ fail_unless(lwip_stats.ip_frag.recv == 3);
+ fail_unless(lwip_stats.ip_frag.err == 0);
+ fail_unless(lwip_stats.ip_frag.memerr == 0);
+ fail_unless(lwip_stats.ip_frag.drop == 0);
+ fail_unless(lwip_stats.mib2.ipreasmoks == 0);
+
+ create_ip4_input_fragment(ip_id, 2*200, 200, 0);
+ fail_unless(lwip_stats.ip_frag.recv == 4);
+ fail_unless(lwip_stats.ip_frag.err == 0);
+ fail_unless(lwip_stats.ip_frag.memerr == 0);
+ fail_unless(lwip_stats.ip_frag.drop == 0);
+ fail_unless(lwip_stats.mib2.ipreasmoks == 0);
+
+ create_ip4_input_fragment(ip_id, 3*200, 200, 0);
+ fail_unless(lwip_stats.ip_frag.recv == 5);
+ fail_unless(lwip_stats.ip_frag.err == 0);
+ fail_unless(lwip_stats.ip_frag.memerr == 0);
+ fail_unless(lwip_stats.ip_frag.drop == 0);
+ fail_unless(lwip_stats.mib2.ipreasmoks == 0);
+
+ create_ip4_input_fragment(ip_id, 4*200, 200, 0);
+ fail_unless(lwip_stats.ip_frag.recv == 6);
+ fail_unless(lwip_stats.ip_frag.err == 0);
+ fail_unless(lwip_stats.ip_frag.memerr == 0);
+ fail_unless(lwip_stats.ip_frag.drop == 0);
+ fail_unless(lwip_stats.mib2.ipreasmoks == 0);
+
+ create_ip4_input_fragment(ip_id, 7*200, 200, 0);
+ fail_unless(lwip_stats.ip_frag.recv == 7);
+ fail_unless(lwip_stats.ip_frag.err == 0);
+ fail_unless(lwip_stats.ip_frag.memerr == 0);
+ fail_unless(lwip_stats.ip_frag.drop == 0);
+ fail_unless(lwip_stats.mib2.ipreasmoks == 0);
+
+ create_ip4_input_fragment(ip_id, 6*200, 200, 0);
+ fail_unless(lwip_stats.ip_frag.recv == 8);
+ fail_unless(lwip_stats.ip_frag.err == 0);
+ fail_unless(lwip_stats.ip_frag.memerr == 0);
+ fail_unless(lwip_stats.ip_frag.drop == 0);
+ fail_unless(lwip_stats.mib2.ipreasmoks == 0);
+
+ create_ip4_input_fragment(ip_id, 5*200, 200, 0);
+ fail_unless(lwip_stats.ip_frag.recv == 9);
+ fail_unless(lwip_stats.ip_frag.err == 0);
+ fail_unless(lwip_stats.ip_frag.memerr == 0);
+ fail_unless(lwip_stats.ip_frag.drop == 0);
+ fail_unless(lwip_stats.mib2.ipreasmoks == 1);
+}
+END_TEST
+
+
+/** Create the suite including all tests for this module */
+Suite *
+ip4_suite(void)
+{
+ testfunc tests[] = {
+ TESTFUNC(test_ip4_reass),
+ };
+ return create_suite("IPv4", tests, sizeof(tests)/sizeof(testfunc), ip4_setup, ip4_teardown);
+}
diff --git a/lwip/test/unit/ip4/test_ip4.h b/lwip/test/unit/ip4/test_ip4.h
new file mode 100644
index 0000000..df84a69
--- /dev/null
+++ b/lwip/test/unit/ip4/test_ip4.h
@@ -0,0 +1,8 @@
+#ifndef LWIP_HDR_TEST_IP4_H
+#define LWIP_HDR_TEST_IP4_H
+
+#include "../lwip_check.h"
+
+Suite* ip4_suite(void);
+
+#endif
diff --git a/lwip/test/unit/lwip_check.h b/lwip/test/unit/lwip_check.h
index e27f55a..522a9c3 100644
--- a/lwip/test/unit/lwip_check.h
+++ b/lwip/test/unit/lwip_check.h
@@ -1,5 +1,5 @@
-#ifndef __LWIP_CHECK_H__
-#define __LWIP_CHECK_H__
+#ifndef LWIP_HDR_LWIP_CHECK_H
+#define LWIP_HDR_LWIP_CHECK_H
/* Common header file for lwIP unit tests using the check framework */
@@ -13,25 +13,30 @@
#define EXPECT_RETX(x, y) do { fail_unless(x); if(!(x)) { return y; }} while(0)
#define EXPECT_RETNULL(x) EXPECT_RETX(x, NULL)
+typedef struct {
+ TFun func;
+ const char *name;
+} testfunc;
+
+#define TESTFUNC(x) {(x), "" # x "" }
+
+/* Modified function from check.h, supplying function name */
+#define tcase_add_named_test(tc,tf) \
+ _tcase_add_test((tc),(tf).func,(tf).name,0, 0, 0, 1)
+
/** typedef for a function returning a test suite */
typedef Suite* (suite_getter_fn)(void);
/** Create a test suite */
-static Suite* create_suite(const char* name, TFun *tests, size_t num_tests, SFun setup, SFun teardown)
-{
- size_t i;
- Suite *s = suite_create(name);
-
- for(i = 0; i < num_tests; i++) {
- /* Core test case */
- TCase *tc_core = tcase_create("Core");
- if ((setup != NULL) || (teardown != NULL)) {
- tcase_add_checked_fixture(tc_core, setup, teardown);
- }
- tcase_add_test(tc_core, tests[i]);
- suite_add_tcase(s, tc_core);
- }
- return s;
-}
-
-#endif /* __LWIP_CHECK_H__ */
+Suite* create_suite(const char* name, testfunc *tests, size_t num_tests, SFun setup, SFun teardown);
+
+#ifdef LWIP_UNITTESTS_LIB
+int lwip_unittests_run(void)
+#endif
+
+/* helper functions */
+#define SKIP_POOL(x) (1 << x)
+#define SKIP_HEAP (1 << MEMP_MAX)
+void lwip_check_ensure_no_alloc(unsigned int skip);
+
+#endif /* LWIP_HDR_LWIP_CHECK_H */
diff --git a/lwip/test/unit/lwip_unittests.c b/lwip/test/unit/lwip_unittests.c
index 41d1f36..b371764 100644
--- a/lwip/test/unit/lwip_unittests.c
+++ b/lwip/test/unit/lwip_unittests.c
@@ -1,5 +1,6 @@
#include "lwip_check.h"
+#include "ip4/test_ip4.h"
#include "udp/test_udp.h"
#include "tcp/test_tcp.h"
#include "tcp/test_tcp_oos.h"
@@ -7,28 +8,76 @@
#include "core/test_pbuf.h"
#include "etharp/test_etharp.h"
#include "dhcp/test_dhcp.h"
+#include "mdns/test_mdns.h"
+#include "mqtt/test_mqtt.h"
+#include "api/test_sockets.h"
#include "lwip/init.h"
+#if !NO_SYS
+#include "lwip/tcpip.h"
+#endif
+
+Suite* create_suite(const char* name, testfunc *tests, size_t num_tests, SFun setup, SFun teardown)
+{
+ size_t i;
+ Suite *s = suite_create(name);
+
+ for(i = 0; i < num_tests; i++) {
+ TCase *tc_core = tcase_create(name);
+ if ((setup != NULL) || (teardown != NULL)) {
+ tcase_add_checked_fixture(tc_core, setup, teardown);
+ }
+ tcase_add_named_test(tc_core, tests[i]);
+ suite_add_tcase(s, tc_core);
+ }
+ return s;
+}
+void lwip_check_ensure_no_alloc(unsigned int skip)
+{
+ int i;
+ unsigned int mask;
-int main()
+ if (!(skip & SKIP_HEAP)) {
+ fail_unless(lwip_stats.mem.used == 0);
+ }
+ for (i = 0, mask = 1; i < MEMP_MAX; i++, mask <<= 1) {
+ if (!(skip & mask)) {
+ fail_unless(lwip_stats.memp[i]->used == 0);
+ }
+ }
+}
+
+#ifdef LWIP_UNITTESTS_LIB
+int lwip_unittests_run(void)
+#else
+int main(void)
+#endif
{
int number_failed;
SRunner *sr;
size_t i;
suite_getter_fn* suites[] = {
+ ip4_suite,
udp_suite,
tcp_suite,
tcp_oos_suite,
mem_suite,
pbuf_suite,
etharp_suite,
- dhcp_suite
+ dhcp_suite,
+ mdns_suite,
+ mqtt_suite,
+ sockets_suite
};
size_t num = sizeof(suites)/sizeof(void*);
LWIP_ASSERT("No suites defined", num > 0);
+#if NO_SYS
lwip_init();
+#else
+ tcpip_init(NULL, NULL);
+#endif
sr = srunner_create((suites[0])());
for(i = 1; i < num; i++) {
diff --git a/lwip/test/unit/lwipopts.h b/lwip/test/unit/lwipopts.h
index 0059515..7b82fd6 100644
--- a/lwip/test/unit/lwipopts.h
+++ b/lwip/test/unit/lwipopts.h
@@ -29,13 +29,20 @@
* Author: Simon Goldschmidt
*
*/
-#ifndef __LWIPOPTS_H__
-#define __LWIPOPTS_H__
+#ifndef LWIP_HDR_LWIPOPTS_H
+#define LWIP_HDR_LWIPOPTS_H
-/* Prevent having to link sys_arch.c (we don't test the API layers in unit tests) */
-#define NO_SYS 1
-#define LWIP_NETCONN 0
-#define LWIP_SOCKET 0
+#define LWIP_IPV6 1
+
+/* We link to special sys_arch.c (for basic non-waiting API layers unit tests) */
+#define NO_SYS 0
+#define SYS_LIGHTWEIGHT_PROT 0
+#define LWIP_NETCONN !NO_SYS
+#define LWIP_SOCKET !NO_SYS
+#define LWIP_NETCONN_FULLDUPLEX LWIP_SOCKET
+#define LWIP_NETBUF_RECVINFO 1
+#define LWIP_HAVE_LOOPIF 1
+#define TCPIP_THREAD_TEST
/* Enable DHCP to test it, disable UDP checksum to easier inject packets */
#define LWIP_DHCP 1
@@ -46,8 +53,21 @@
#define MEMP_NUM_TCP_SEG TCP_SND_QUEUELEN
#define TCP_SND_BUF (12 * TCP_MSS)
#define TCP_WND (10 * TCP_MSS)
+#define LWIP_WND_SCALE 1
+#define TCP_RCV_SCALE 0
+#define PBUF_POOL_SIZE 400 /* pbuf tests need ~200KByte */
+
+/* Enable IGMP and MDNS for MDNS tests */
+#define LWIP_IGMP 1
+#define LWIP_MDNS_RESPONDER 1
+#define LWIP_NUM_NETIF_CLIENT_DATA (LWIP_MDNS_RESPONDER)
/* Minimal changes to opt.h required for etharp unit tests: */
#define ETHARP_SUPPORT_STATIC_ENTRIES 1
-#endif /* __LWIPOPTS_H__ */
+#define MEMP_NUM_SYS_TIMEOUT (LWIP_NUM_SYS_TIMEOUT_INTERNAL + 1)
+
+/* MIB2 stats are required to check IPv4 reassembly results */
+#define MIB2_STATS 1
+
+#endif /* LWIP_HDR_LWIPOPTS_H */
diff --git a/lwip/test/unit/mdns/test_mdns.c b/lwip/test/unit/mdns/test_mdns.c
new file mode 100644
index 0000000..6385163
--- /dev/null
+++ b/lwip/test/unit/mdns/test_mdns.c
@@ -0,0 +1,915 @@
+/*
+ * 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 "test_mdns.h"
+
+#include "lwip/pbuf.h"
+#include "lwip/apps/mdns.h"
+#include "lwip/apps/mdns_priv.h"
+
+START_TEST(readname_basic)
+{
+ static const u8_t data[] = { 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00 };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+ offset = mdns_readname(p, 0, &domain);
+ pbuf_free(p);
+ fail_unless(offset == sizeof(data));
+ fail_unless(domain.length == sizeof(data));
+ fail_if(memcmp(&domain.name, data, sizeof(data)));
+}
+END_TEST
+
+START_TEST(readname_anydata)
+{
+ static const u8_t data[] = { 0x05, 0x00, 0xFF, 0x08, 0xc0, 0x0f, 0x04, 0x7f, 0x80, 0x82, 0x88, 0x00 };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+ offset = mdns_readname(p, 0, &domain);
+ pbuf_free(p);
+ fail_unless(offset == sizeof(data));
+ fail_unless(domain.length == sizeof(data));
+ fail_if(memcmp(&domain.name, data, sizeof(data)));
+}
+END_TEST
+
+START_TEST(readname_short_buf)
+{
+ static const u8_t data[] = { 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a' };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+ offset = mdns_readname(p, 0, &domain);
+ pbuf_free(p);
+ fail_unless(offset == MDNS_READNAME_ERROR);
+}
+END_TEST
+
+START_TEST(readname_long_label)
+{
+ static const u8_t data[] = {
+ 0x05, 'm', 'u', 'l', 't', 'i',
+ 0x52, 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
+ 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
+ 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
+ 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
+ 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a',
+ 'a', 'a', 'a', 'a', 'a', 'a', 'a', 'a', 0x00
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+ offset = mdns_readname(p, 0, &domain);
+ pbuf_free(p);
+ fail_unless(offset == MDNS_READNAME_ERROR);
+}
+END_TEST
+
+START_TEST(readname_overflow)
+{
+ static const u8_t data[] = {
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x00
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+ offset = mdns_readname(p, 0, &domain);
+ pbuf_free(p);
+ fail_unless(offset == MDNS_READNAME_ERROR);
+}
+END_TEST
+
+START_TEST(readname_jump_earlier)
+{
+ static const u8_t data[] = {
+ /* Some padding needed, not supported to jump to bytes containing dns header */
+ /* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 10 */ 0x0f, 0x0e, 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xab,
+ /* 20 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x0c
+ };
+ static const u8_t fullname[] = {
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+ offset = mdns_readname(p, 20, &domain);
+ pbuf_free(p);
+ fail_unless(offset == sizeof(data));
+ fail_unless(domain.length == sizeof(fullname));
+
+ fail_if(memcmp(&domain.name, fullname, sizeof(fullname)));
+}
+END_TEST
+
+START_TEST(readname_jump_earlier_jump)
+{
+ static const u8_t data[] = {
+ /* Some padding needed, not supported to jump to bytes containing dns header */
+ /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x0a, 0xf2,
+ /* 0x10 */ 0x04, 'c', 'a', 's', 't', 0x00, 0xc0, 0x10,
+ /* 0x18 */ 0x05, 'm', 'u', 'l', 't', 'i', 0xc0, 0x16
+ };
+ static const u8_t fullname[] = {
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+ offset = mdns_readname(p, 0x18, &domain);
+ pbuf_free(p);
+ fail_unless(offset == sizeof(data));
+ fail_unless(domain.length == sizeof(fullname));
+
+ fail_if(memcmp(&domain.name, fullname, sizeof(fullname)));
+}
+END_TEST
+
+START_TEST(readname_jump_maxdepth)
+{
+ static const u8_t data[] = {
+ /* Some padding needed, not supported to jump to bytes containing dns header */
+ /* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 0x08 */ 0x00, 0x00, 0x00, 0x00, 0x03, 0x0b, 0x0a, 0xf2,
+ /* 0x10 */ 0x04, 'n', 'a', 'm', 'e', 0xc0, 0x27, 0x03,
+ /* 0x18 */ 0x03, 'd', 'n', 's', 0xc0, 0x10, 0xc0, 0x10,
+ /* 0x20 */ 0x04, 'd', 'e', 'e', 'p', 0xc0, 0x18, 0x00,
+ /* 0x28 */ 0x04, 'c', 'a', 's', 't', 0xc0, 0x20, 0xb0,
+ /* 0x30 */ 0x05, 'm', 'u', 'l', 't', 'i', 0xc0, 0x28
+ };
+ static const u8_t fullname[] = {
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't',
+ 0x04, 'd', 'e', 'e', 'p', 0x03, 'd', 'n', 's',
+ 0x04, 'n', 'a', 'm', 'e', 0x00
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+ offset = mdns_readname(p, 0x30, &domain);
+ pbuf_free(p);
+ fail_unless(offset == sizeof(data));
+ fail_unless(domain.length == sizeof(fullname));
+
+ fail_if(memcmp(&domain.name, fullname, sizeof(fullname)));
+}
+END_TEST
+
+START_TEST(readname_jump_later)
+{
+ static const u8_t data[] = {
+ /* 0x00 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x10, 0x00, 0x01, 0x40,
+ /* 0x10 */ 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xab
+ };
+ static const u8_t fullname[] = {
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+ offset = mdns_readname(p, 0, &domain);
+ pbuf_free(p);
+ fail_unless(offset == 13);
+ fail_unless(domain.length == sizeof(fullname));
+
+ fail_if(memcmp(&domain.name, fullname, sizeof(fullname)));
+}
+END_TEST
+
+START_TEST(readname_half_jump)
+{
+ static const u8_t data[] = {
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+ offset = mdns_readname(p, 0, &domain);
+ pbuf_free(p);
+ fail_unless(offset == MDNS_READNAME_ERROR);
+}
+END_TEST
+
+START_TEST(readname_jump_toolong)
+{
+ static const u8_t data[] = {
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc2, 0x10, 0x00, 0x01, 0x40
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+ offset = mdns_readname(p, 0, &domain);
+ pbuf_free(p);
+ fail_unless(offset == MDNS_READNAME_ERROR);
+}
+END_TEST
+
+START_TEST(readname_jump_loop_label)
+{
+ static const u8_t data[] = {
+ /* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 10 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x10
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+ offset = mdns_readname(p, 10, &domain);
+ pbuf_free(p);
+ fail_unless(offset == MDNS_READNAME_ERROR);
+}
+END_TEST
+
+START_TEST(readname_jump_loop_jump)
+{
+ static const u8_t data[] = {
+ /* 0 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 10 */ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0xc0, 0x15
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+ offset = mdns_readname(p, 10, &domain);
+ pbuf_free(p);
+ fail_unless(offset == MDNS_READNAME_ERROR);
+}
+END_TEST
+
+START_TEST(add_label_basic)
+{
+ static const u8_t data[] = { 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00 };
+ struct mdns_domain domain;
+ err_t res;
+ LWIP_UNUSED_ARG(_i);
+
+ memset(&domain, 0, sizeof(domain));
+ res = mdns_domain_add_label(&domain, "multi", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, "cast", 4);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, NULL, 0);
+ fail_unless(res == ERR_OK);
+ fail_unless(domain.length == sizeof(data));
+ fail_if(memcmp(&domain.name, data, sizeof(data)));
+}
+END_TEST
+
+START_TEST(add_label_long_label)
+{
+ static const char *toolong = "abcdefghijklmnopqrstuvwxyz0123456789-abcdefghijklmnopqrstuvwxyz0123456789-abcdefghijklmnopqrstuvwxyz0123456789-";
+ struct mdns_domain domain;
+ err_t res;
+ LWIP_UNUSED_ARG(_i);
+
+ memset(&domain, 0, sizeof(domain));
+ res = mdns_domain_add_label(&domain, "multi", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, toolong, (u8_t)strlen(toolong));
+ fail_unless(res == ERR_VAL);
+}
+END_TEST
+
+START_TEST(add_label_full)
+{
+ static const char *label = "0123456789abcdef0123456789abcdef";
+ struct mdns_domain domain;
+ err_t res;
+ LWIP_UNUSED_ARG(_i);
+
+ memset(&domain, 0, sizeof(domain));
+ res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
+ fail_unless(res == ERR_OK);
+ fail_unless(domain.length == 33);
+ res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
+ fail_unless(res == ERR_OK);
+ fail_unless(domain.length == 66);
+ res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
+ fail_unless(res == ERR_OK);
+ fail_unless(domain.length == 99);
+ res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
+ fail_unless(res == ERR_OK);
+ fail_unless(domain.length == 132);
+ res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
+ fail_unless(res == ERR_OK);
+ fail_unless(domain.length == 165);
+ res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
+ fail_unless(res == ERR_OK);
+ fail_unless(domain.length == 198);
+ res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
+ fail_unless(res == ERR_OK);
+ fail_unless(domain.length == 231);
+ res = mdns_domain_add_label(&domain, label, (u8_t)strlen(label));
+ fail_unless(res == ERR_VAL);
+ fail_unless(domain.length == 231);
+ res = mdns_domain_add_label(&domain, label, 25);
+ fail_unless(res == ERR_VAL);
+ fail_unless(domain.length == 231);
+ res = mdns_domain_add_label(&domain, label, 24);
+ fail_unless(res == ERR_VAL);
+ fail_unless(domain.length == 231);
+ res = mdns_domain_add_label(&domain, label, 23);
+ fail_unless(res == ERR_OK);
+ fail_unless(domain.length == 255);
+ res = mdns_domain_add_label(&domain, NULL, 0);
+ fail_unless(res == ERR_OK);
+ fail_unless(domain.length == 256);
+ res = mdns_domain_add_label(&domain, NULL, 0);
+ fail_unless(res == ERR_VAL);
+ fail_unless(domain.length == 256);
+}
+END_TEST
+
+START_TEST(domain_eq_basic)
+{
+ static const u8_t data[] = {
+ 0x05, 'm', 'u', 'l', 't', 'i', 0x04, 'c', 'a', 's', 't', 0x00
+ };
+ struct mdns_domain domain1, domain2;
+ err_t res;
+ LWIP_UNUSED_ARG(_i);
+
+ memset(&domain1, 0, sizeof(domain1));
+ res = mdns_domain_add_label(&domain1, "multi", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain1, "cast", 4);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain1, NULL, 0);
+ fail_unless(res == ERR_OK);
+ fail_unless(domain1.length == sizeof(data));
+
+ memset(&domain2, 0, sizeof(domain2));
+ res = mdns_domain_add_label(&domain2, "multi", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain2, "cast", 4);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain2, NULL, 0);
+ fail_unless(res == ERR_OK);
+
+ fail_unless(mdns_domain_eq(&domain1, &domain2));
+}
+END_TEST
+
+START_TEST(domain_eq_diff)
+{
+ struct mdns_domain domain1, domain2;
+ err_t res;
+ LWIP_UNUSED_ARG(_i);
+
+ memset(&domain1, 0, sizeof(domain1));
+ res = mdns_domain_add_label(&domain1, "multi", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain1, "base", 4);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain1, NULL, 0);
+ fail_unless(res == ERR_OK);
+
+ memset(&domain2, 0, sizeof(domain2));
+ res = mdns_domain_add_label(&domain2, "multi", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain2, "cast", 4);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain2, NULL, 0);
+ fail_unless(res == ERR_OK);
+
+ fail_if(mdns_domain_eq(&domain1, &domain2));
+}
+END_TEST
+
+START_TEST(domain_eq_case)
+{
+ struct mdns_domain domain1, domain2;
+ err_t res;
+ LWIP_UNUSED_ARG(_i);
+
+ memset(&domain1, 0, sizeof(domain1));
+ res = mdns_domain_add_label(&domain1, "multi", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain1, "cast", 4);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain1, NULL, 0);
+ fail_unless(res == ERR_OK);
+
+ memset(&domain2, 0, sizeof(domain2));
+ res = mdns_domain_add_label(&domain2, "MulTI", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain2, "casT", 4);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain2, NULL, 0);
+ fail_unless(res == ERR_OK);
+
+ fail_unless(mdns_domain_eq(&domain1, &domain2));
+}
+END_TEST
+
+START_TEST(domain_eq_anydata)
+{
+ static const u8_t data1[] = { 0x05, 0xcc, 0xdc, 0x00, 0xa0 };
+ static const u8_t data2[] = { 0x7f, 0x8c, 0x01, 0xff, 0xcf };
+ struct mdns_domain domain1, domain2;
+ err_t res;
+ LWIP_UNUSED_ARG(_i);
+
+ memset(&domain1, 0, sizeof(domain1));
+ res = mdns_domain_add_label(&domain1, (const char*)data1, sizeof(data1));
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain1, "cast", 4);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain1, (const char*)data2, sizeof(data2));
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain1, NULL, 0);
+ fail_unless(res == ERR_OK);
+
+ memset(&domain2, 0, sizeof(domain2));
+ res = mdns_domain_add_label(&domain2, (const char*)data1, sizeof(data1));
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain2, "casT", 4);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain2, (const char*)data2, sizeof(data2));
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain2, NULL, 0);
+ fail_unless(res == ERR_OK);
+
+ fail_unless(mdns_domain_eq(&domain1, &domain2));
+}
+END_TEST
+
+START_TEST(domain_eq_length)
+{
+ struct mdns_domain domain1, domain2;
+ err_t res;
+ LWIP_UNUSED_ARG(_i);
+
+ memset(&domain1, 0, sizeof(domain1));
+ memset(domain1.name, 0xAA, sizeof(MDNS_DOMAIN_MAXLEN));
+ res = mdns_domain_add_label(&domain1, "multi", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain1, "cast", 4);
+ fail_unless(res == ERR_OK);
+
+ memset(&domain2, 0, sizeof(domain2));
+ memset(domain2.name, 0xBB, sizeof(MDNS_DOMAIN_MAXLEN));
+ res = mdns_domain_add_label(&domain2, "multi", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain2, "cast", 4);
+ fail_unless(res == ERR_OK);
+
+ fail_unless(mdns_domain_eq(&domain1, &domain2));
+}
+END_TEST
+
+START_TEST(compress_full_match)
+{
+ static const u8_t data[] = {
+ 0x00, 0x00,
+ 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ u16_t length;
+ err_t res;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+
+ memset(&domain, 0, sizeof(domain));
+ res = mdns_domain_add_label(&domain, "foobar", 6);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, "local", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, NULL, 0);
+ fail_unless(res == ERR_OK);
+
+ offset = 2;
+ length = mdns_compress_domain(p, &offset, &domain);
+ /* Write 0 bytes, then a jump to addr 2 */
+ fail_unless(length == 0);
+ fail_unless(offset == 2);
+
+ pbuf_free(p);
+}
+END_TEST
+
+START_TEST(compress_full_match_subset)
+{
+ static const u8_t data[] = {
+ 0x00, 0x00,
+ 0x02, 'g', 'o', 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ u16_t length;
+ err_t res;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+
+ memset(&domain, 0, sizeof(domain));
+ res = mdns_domain_add_label(&domain, "foobar", 6);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, "local", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, NULL, 0);
+ fail_unless(res == ERR_OK);
+
+ offset = 2;
+ length = mdns_compress_domain(p, &offset, &domain);
+ /* Write 0 bytes, then a jump to addr 5 */
+ fail_unless(length == 0);
+ fail_unless(offset == 5);
+
+ pbuf_free(p);
+}
+END_TEST
+
+START_TEST(compress_full_match_jump)
+{
+ static const u8_t data[] = {
+ /* 0x00 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ /* 0x10 */ 0x04, 'l', 'w', 'i', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xc0, 0x00, 0x02, 0x00,
+ /* 0x20 */ 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0xc0, 0x15
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ u16_t length;
+ err_t res;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+
+ memset(&domain, 0, sizeof(domain));
+ res = mdns_domain_add_label(&domain, "foobar", 6);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, "local", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, NULL, 0);
+ fail_unless(res == ERR_OK);
+
+ offset = 0x20;
+ length = mdns_compress_domain(p, &offset, &domain);
+ /* Write 0 bytes, then a jump to addr 0x20 */
+ fail_unless(length == 0);
+ fail_unless(offset == 0x20);
+
+ pbuf_free(p);
+}
+END_TEST
+
+START_TEST(compress_no_match)
+{
+ static const u8_t data[] = {
+ 0x00, 0x00,
+ 0x04, 'l', 'w', 'i', 'p', 0x05, 'w', 'i', 'k', 'i', 'a', 0x03, 'c', 'o', 'm', 0x00
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ u16_t length;
+ err_t res;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+
+ memset(&domain, 0, sizeof(domain));
+ res = mdns_domain_add_label(&domain, "foobar", 6);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, "local", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, NULL, 0);
+ fail_unless(res == ERR_OK);
+
+ offset = 2;
+ length = mdns_compress_domain(p, &offset, &domain);
+ /* Write all bytes, no jump */
+ fail_unless(length == domain.length);
+
+ pbuf_free(p);
+}
+END_TEST
+
+START_TEST(compress_2nd_label)
+{
+ static const u8_t data[] = {
+ 0x00, 0x00,
+ 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ u16_t length;
+ err_t res;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+
+ memset(&domain, 0, sizeof(domain));
+ res = mdns_domain_add_label(&domain, "lwip", 4);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, "local", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, NULL, 0);
+ fail_unless(res == ERR_OK);
+
+ offset = 2;
+ length = mdns_compress_domain(p, &offset, &domain);
+ /* Write 5 bytes, then a jump to addr 9 */
+ fail_unless(length == 5);
+ fail_unless(offset == 9);
+
+ pbuf_free(p);
+}
+END_TEST
+
+START_TEST(compress_2nd_label_short)
+{
+ static const u8_t data[] = {
+ 0x00, 0x00,
+ 0x04, 'l', 'w', 'i', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ u16_t length;
+ err_t res;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+
+ memset(&domain, 0, sizeof(domain));
+ res = mdns_domain_add_label(&domain, "foobar", 6);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, "local", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, NULL, 0);
+ fail_unless(res == ERR_OK);
+
+ offset = 2;
+ length = mdns_compress_domain(p, &offset, &domain);
+ /* Write 5 bytes, then a jump to addr 7 */
+ fail_unless(length == 7);
+ fail_unless(offset == 7);
+
+ pbuf_free(p);
+}
+END_TEST
+
+START_TEST(compress_jump_to_jump)
+{
+ static const u8_t data[] = {
+ /* 0x00 */ 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+ 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+ /* 0x10 */ 0x04, 'l', 'w', 'i', 'p', 0x05, 'l', 'o', 'c', 'a', 'l', 0x00, 0xc0, 0x00, 0x02, 0x00,
+ /* 0x20 */ 0x07, 'b', 'a', 'n', 'a', 'n', 'a', 's', 0xc0, 0x15
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ u16_t length;
+ err_t res;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+
+ memset(&domain, 0, sizeof(domain));
+ res = mdns_domain_add_label(&domain, "foobar", 6);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, "local", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, NULL, 0);
+ fail_unless(res == ERR_OK);
+
+ offset = 0x20;
+ length = mdns_compress_domain(p, &offset, &domain);
+ /* Dont compress if jump would be to a jump */
+ fail_unless(length == domain.length);
+
+ offset = 0x10;
+ length = mdns_compress_domain(p, &offset, &domain);
+ /* Write 7 bytes, then a jump to addr 0x15 */
+ fail_unless(length == 7);
+ fail_unless(offset == 0x15);
+
+ pbuf_free(p);
+}
+END_TEST
+
+START_TEST(compress_long_match)
+{
+ static const u8_t data[] = {
+ 0x00, 0x00,
+ 0x06, 'f', 'o', 'o', 'b', 'a', 'r', 0x05, 'l', 'o', 'c', 'a', 'l', 0x03, 'c', 'o', 'm', 0x00
+ };
+ struct pbuf *p;
+ struct mdns_domain domain;
+ u16_t offset;
+ u16_t length;
+ err_t res;
+ LWIP_UNUSED_ARG(_i);
+
+ p = pbuf_alloc(PBUF_RAW, sizeof(data), PBUF_ROM);
+ fail_if(p == NULL);
+ p->payload = (void *)(size_t)data;
+
+ memset(&domain, 0, sizeof(domain));
+ res = mdns_domain_add_label(&domain, "foobar", 6);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, "local", 5);
+ fail_unless(res == ERR_OK);
+ res = mdns_domain_add_label(&domain, NULL, 0);
+ fail_unless(res == ERR_OK);
+
+ offset = 2;
+ length = mdns_compress_domain(p, &offset, &domain);
+ fail_unless(length == domain.length);
+
+ pbuf_free(p);
+}
+END_TEST
+
+Suite* mdns_suite(void)
+{
+ testfunc tests[] = {
+ TESTFUNC(readname_basic),
+ TESTFUNC(readname_anydata),
+ TESTFUNC(readname_short_buf),
+ TESTFUNC(readname_long_label),
+ TESTFUNC(readname_overflow),
+ TESTFUNC(readname_jump_earlier),
+ TESTFUNC(readname_jump_earlier_jump),
+ TESTFUNC(readname_jump_maxdepth),
+ TESTFUNC(readname_jump_later),
+ TESTFUNC(readname_half_jump),
+ TESTFUNC(readname_jump_toolong),
+ TESTFUNC(readname_jump_loop_label),
+ TESTFUNC(readname_jump_loop_jump),
+
+ TESTFUNC(add_label_basic),
+ TESTFUNC(add_label_long_label),
+ TESTFUNC(add_label_full),
+
+ TESTFUNC(domain_eq_basic),
+ TESTFUNC(domain_eq_diff),
+ TESTFUNC(domain_eq_case),
+ TESTFUNC(domain_eq_anydata),
+ TESTFUNC(domain_eq_length),
+
+ TESTFUNC(compress_full_match),
+ TESTFUNC(compress_full_match_subset),
+ TESTFUNC(compress_full_match_jump),
+ TESTFUNC(compress_no_match),
+ TESTFUNC(compress_2nd_label),
+ TESTFUNC(compress_2nd_label_short),
+ TESTFUNC(compress_jump_to_jump),
+ TESTFUNC(compress_long_match),
+ };
+ return create_suite("MDNS", tests, sizeof(tests)/sizeof(testfunc), NULL, NULL);
+}
diff --git a/lwip/test/unit/mdns/test_mdns.h b/lwip/test/unit/mdns/test_mdns.h
new file mode 100644
index 0000000..c3df339
--- /dev/null
+++ b/lwip/test/unit/mdns/test_mdns.h
@@ -0,0 +1,8 @@
+#ifndef LWIP_HDR_TEST_MDNS_H__
+#define LWIP_HDR_TEST_MDNS_H__
+
+#include "../lwip_check.h"
+
+Suite* mdns_suite(void);
+
+#endif
diff --git a/lwip/test/unit/mqtt/test_mqtt.c b/lwip/test/unit/mqtt/test_mqtt.c
new file mode 100644
index 0000000..32f08e6
--- /dev/null
+++ b/lwip/test/unit/mqtt/test_mqtt.c
@@ -0,0 +1,113 @@
+#include "test_mqtt.h"
+
+#include "lwip/pbuf.h"
+#include "lwip/apps/mqtt.h"
+#include "lwip/apps/mqtt_priv.h"
+#include "lwip/netif.h"
+
+const ip_addr_t test_mqtt_local_ip = IPADDR4_INIT_BYTES(192, 168, 1, 1);
+const ip_addr_t test_mqtt_remote_ip = IPADDR4_INIT_BYTES(192, 168, 1, 2);
+const ip_addr_t test_mqtt_netmask = IPADDR4_INIT_BYTES(255, 255, 255, 0);
+
+static err_t test_mqtt_netif_output(struct netif *netif, struct pbuf *p,
+ const ip4_addr_t *ipaddr)
+{
+ LWIP_UNUSED_ARG(netif);
+ LWIP_UNUSED_ARG(ipaddr);
+ LWIP_UNUSED_ARG(p);
+ return ERR_OK;
+}
+
+static void
+test_mqtt_init_netif(struct netif *netif, const ip_addr_t *ip_addr, const ip_addr_t *netmask)
+{
+ struct netif *n;
+ memset(netif, 0, sizeof(struct netif));
+ netif->output = test_mqtt_netif_output;
+ netif->flags |= NETIF_FLAG_UP | NETIF_FLAG_LINK_UP;
+ ip_addr_copy_from_ip4(netif->netmask, *ip_2_ip4(netmask));
+ ip_addr_copy_from_ip4(netif->ip_addr, *ip_2_ip4(ip_addr));
+ for (n = netif_list; n != NULL; n = n->next) {
+ if (n == netif) {
+ return;
+ }
+ }
+ netif->next = NULL;
+ netif_list = netif;
+}
+
+/* Setups/teardown functions */
+static struct netif *old_netif_list;
+static struct netif *old_netif_default;
+
+static void
+mqtt_setup(void)
+{
+ old_netif_list = netif_list;
+ old_netif_default = netif_default;
+ netif_list = NULL;
+ netif_default = NULL;
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
+}
+
+static void
+mqtt_teardown(void)
+{
+ netif_list = NULL;
+ netif_default = NULL;
+ /* restore netif_list for next tests (e.g. loopif) */
+ netif_list = old_netif_list;
+ netif_default = old_netif_default;
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
+}
+
+static void test_mqtt_connection_cb(mqtt_client_t *client, void *arg, mqtt_connection_status_t status)
+{
+ LWIP_UNUSED_ARG(client);
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(status);
+}
+
+START_TEST(basic_connect)
+{
+ mqtt_client_t* client;
+ struct netif netif;
+ err_t err;
+ struct mqtt_connect_client_info_t client_info = {
+ "dumm",
+ NULL, NULL,
+ 10,
+ NULL, NULL, 0, 0
+ };
+ struct pbuf *p;
+ unsigned char rxbuf[] = {0x20, 0x02, 0x00, 0x00};
+ LWIP_UNUSED_ARG(_i);
+
+ test_mqtt_init_netif(&netif, &test_mqtt_local_ip, &test_mqtt_netmask);
+
+ client = mqtt_client_new();
+ fail_unless(client != NULL);
+ err = mqtt_client_connect(client, &test_mqtt_remote_ip, 1234, test_mqtt_connection_cb, NULL, &client_info);
+ fail_unless(err == ERR_OK);
+
+ client->conn->connected(client->conn->callback_arg, client->conn, ERR_OK);
+ p = pbuf_alloc(PBUF_RAW, sizeof(rxbuf), PBUF_REF);
+ fail_unless(p != NULL);
+ p->payload = rxbuf;
+ if (client->conn->recv(client->conn->callback_arg, client->conn, p, ERR_OK) != ERR_OK) {
+ pbuf_free(p);
+ }
+
+ mqtt_disconnect(client);
+ /* fixme: mqtt_client_fre() is missing... */
+ mem_free(client);
+}
+END_TEST
+
+Suite* mqtt_suite(void)
+{
+ testfunc tests[] = {
+ TESTFUNC(basic_connect),
+ };
+ return create_suite("MQTT", tests, sizeof(tests)/sizeof(testfunc), mqtt_setup, mqtt_teardown);
+}
diff --git a/lwip/test/unit/mqtt/test_mqtt.h b/lwip/test/unit/mqtt/test_mqtt.h
new file mode 100644
index 0000000..181758c
--- /dev/null
+++ b/lwip/test/unit/mqtt/test_mqtt.h
@@ -0,0 +1,8 @@
+#ifndef LWIP_HDR_TEST_MQTT_H__
+#define LWIP_HDR_TEST_MQTT_H__
+
+#include "../lwip_check.h"
+
+Suite* mqtt_suite(void);
+
+#endif
diff --git a/lwip/test/unit/tcp/tcp_helper.c b/lwip/test/unit/tcp/tcp_helper.c
index ff503db..8689b71 100644
--- a/lwip/test/unit/tcp/tcp_helper.c
+++ b/lwip/test/unit/tcp/tcp_helper.c
@@ -1,14 +1,19 @@
#include "tcp_helper.h"
-#include "lwip/tcp_impl.h"
+#include "lwip/priv/tcp_priv.h"
#include "lwip/stats.h"
#include "lwip/pbuf.h"
#include "lwip/inet_chksum.h"
+#include "lwip/ip_addr.h"
#if !LWIP_STATS || !TCP_STATS || !MEMP_STATS
#error "This tests needs TCP- and MEMP-statistics enabled"
#endif
+const ip_addr_t test_local_ip = IPADDR4_INIT_BYTES(192, 168, 1, 1);
+const ip_addr_t test_remote_ip = IPADDR4_INIT_BYTES(192, 168, 1, 2);
+const ip_addr_t test_netmask = IPADDR4_INIT_BYTES(255, 255, 255, 0);
+
/** Remove all pcbs on the given list. */
static void
tcp_remove(struct tcp_pcb* pcb_list)
@@ -28,12 +33,13 @@ void
tcp_remove_all(void)
{
tcp_remove(tcp_listen_pcbs.pcbs);
+ tcp_remove(tcp_bound_pcbs);
tcp_remove(tcp_active_pcbs);
tcp_remove(tcp_tw_pcbs);
- fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
- fail_unless(lwip_stats.memp[MEMP_TCP_PCB_LISTEN].used == 0);
- fail_unless(lwip_stats.memp[MEMP_TCP_SEG].used == 0);
- fail_unless(lwip_stats.memp[MEMP_PBUF_POOL].used == 0);
+ fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
+ fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB_LISTEN) == 0);
+ fail_unless(MEMP_STATS_GET(used, MEMP_TCP_SEG) == 0);
+ fail_unless(MEMP_STATS_GET(used, MEMP_PBUF_POOL) == 0);
}
/** Create a TCP segment usable for passing to tcp_input */
@@ -46,6 +52,7 @@ tcp_create_segment_wnd(ip_addr_t* src_ip, ip_addr_t* dst_ip,
struct ip_hdr* iphdr;
struct tcp_hdr* tcphdr;
u16_t pbuf_len = (u16_t)(sizeof(struct ip_hdr) + sizeof(struct tcp_hdr) + data_len);
+ LWIP_ASSERT("data_len too big", data_len <= 0xFFFF);
p = pbuf_alloc(PBUF_RAW, pbuf_len, PBUF_POOL);
EXPECT_RETNULL(p != NULL);
@@ -60,10 +67,10 @@ tcp_create_segment_wnd(ip_addr_t* src_ip, ip_addr_t* dst_ip,
memset(q->payload, 0, q->len);
}
- iphdr = p->payload;
+ iphdr = (struct ip_hdr*)p->payload;
/* fill IP header */
- iphdr->dest.addr = dst_ip->addr;
- iphdr->src.addr = src_ip->addr;
+ iphdr->dest.addr = ip_2_ip4(dst_ip)->addr;
+ iphdr->src.addr = ip_2_ip4(src_ip)->addr;
IPH_VHL_SET(iphdr, 4, IP_HLEN / 4);
IPH_TOS_SET(iphdr, 0);
IPH_LEN_SET(iphdr, htons(p->tot_len));
@@ -72,7 +79,7 @@ tcp_create_segment_wnd(ip_addr_t* src_ip, ip_addr_t* dst_ip,
/* let p point to TCP header */
pbuf_header(p, -(s16_t)sizeof(struct ip_hdr));
- tcphdr = p->payload;
+ tcphdr = (struct tcp_hdr*)p->payload;
tcphdr->src = htons(src_port);
tcphdr->dest = htons(dst_port);
tcphdr->seqno = htonl(seqno);
@@ -85,14 +92,14 @@ tcp_create_segment_wnd(ip_addr_t* src_ip, ip_addr_t* dst_ip,
/* let p point to TCP data */
pbuf_header(p, -(s16_t)sizeof(struct tcp_hdr));
/* copy data */
- pbuf_take(p, data, data_len);
+ pbuf_take(p, data, (u16_t)data_len);
/* let p point to TCP header again */
pbuf_header(p, sizeof(struct tcp_hdr));
}
/* calculate checksum */
- tcphdr->chksum = inet_chksum_pseudo(p,
+ tcphdr->chksum = ip_chksum_pseudo(p,
IP_PROTO_TCP, p->tot_len, src_ip, dst_ip);
pbuf_header(p, sizeof(struct ip_hdr));
@@ -136,27 +143,36 @@ struct pbuf* tcp_create_rx_segment_wnd(struct tcp_pcb* pcb, void* data, size_t d
/** Safely bring a tcp_pcb into the requested state */
void
-tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, ip_addr_t* local_ip,
- ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port)
+tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, const ip_addr_t* local_ip,
+ const ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port)
{
+ u32_t iss;
+
/* @todo: are these all states? */
/* @todo: remove from previous list */
pcb->state = state;
+
+ iss = tcp_next_iss(pcb);
+ pcb->snd_wl2 = iss;
+ pcb->snd_nxt = iss;
+ pcb->lastack = iss;
+ pcb->snd_lbb = iss;
+
if (state == ESTABLISHED) {
TCP_REG(&tcp_active_pcbs, pcb);
- pcb->local_ip.addr = local_ip->addr;
+ ip_addr_copy(pcb->local_ip, *local_ip);
pcb->local_port = local_port;
- pcb->remote_ip.addr = remote_ip->addr;
+ ip_addr_copy(pcb->remote_ip, *remote_ip);
pcb->remote_port = remote_port;
} else if(state == LISTEN) {
TCP_REG(&tcp_listen_pcbs.pcbs, pcb);
- pcb->local_ip.addr = local_ip->addr;
+ ip_addr_copy(pcb->local_ip, *local_ip);
pcb->local_port = local_port;
} else if(state == TIME_WAIT) {
TCP_REG(&tcp_tw_pcbs, pcb);
- pcb->local_ip.addr = local_ip->addr;
+ ip_addr_copy(pcb->local_ip, *local_ip);
pcb->local_port = local_port;
- pcb->remote_ip.addr = remote_ip->addr;
+ ip_addr_copy(pcb->remote_ip, *remote_ip);
pcb->remote_port = remote_port;
} else {
fail();
@@ -166,7 +182,7 @@ tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, ip_addr_t* local_ip,
void
test_tcp_counters_err(void* arg, err_t err)
{
- struct test_tcp_counters* counters = arg;
+ struct test_tcp_counters* counters = (struct test_tcp_counters*)arg;
EXPECT_RET(arg != NULL);
counters->err_calls++;
counters->last_err = err;
@@ -184,7 +200,7 @@ test_tcp_counters_check_rxdata(struct test_tcp_counters* counters, struct pbuf*
EXPECT_RET(counters->recved_bytes + p->tot_len <= counters->expected_data_len);
received = counters->recved_bytes;
for(q = p; q != NULL; q = q->next) {
- char *data = q->payload;
+ char *data = (char*)q->payload;
for(i = 0; i < q->len; i++) {
EXPECT_RET(data[i] == counters->expected_data[received]);
received++;
@@ -196,7 +212,7 @@ test_tcp_counters_check_rxdata(struct test_tcp_counters* counters, struct pbuf*
err_t
test_tcp_counters_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err)
{
- struct test_tcp_counters* counters = arg;
+ struct test_tcp_counters* counters = (struct test_tcp_counters*)arg;
EXPECT_RETX(arg != NULL, ERR_OK);
EXPECT_RETX(pcb != NULL, ERR_OK);
EXPECT_RETX(err == ERR_OK, ERR_OK);
@@ -239,24 +255,24 @@ void test_tcp_input(struct pbuf *p, struct netif *inp)
{
struct ip_hdr *iphdr = (struct ip_hdr*)p->payload;
/* these lines are a hack, don't use them as an example :-) */
- ip_addr_copy(*ipX_current_dest_addr(), iphdr->dest);
- ip_addr_copy(*ipX_current_src_addr(), iphdr->src);
+ ip_addr_copy_from_ip4(*ip_current_dest_addr(), iphdr->dest);
+ ip_addr_copy_from_ip4(*ip_current_src_addr(), iphdr->src);
ip_current_netif() = inp;
- ip_current_header() = iphdr;
+ ip_data.current_ip4_header = iphdr;
/* since adding IPv6, p->payload must point to tcp header, not ip header */
pbuf_header(p, -(s16_t)sizeof(struct ip_hdr));
tcp_input(p, inp);
- ipX_current_dest_addr()->addr = 0;
- ipX_current_src_addr()->addr = 0;
+ ip_addr_set_zero(ip_current_dest_addr());
+ ip_addr_set_zero(ip_current_src_addr());
ip_current_netif() = NULL;
- ip_current_header() = NULL;
+ ip_data.current_ip4_header = NULL;
}
static err_t test_tcp_netif_output(struct netif *netif, struct pbuf *p,
- ip_addr_t *ipaddr)
+ const ip4_addr_t *ipaddr)
{
struct test_tcp_txcounters *txcounters = (struct test_tcp_txcounters*)netif->state;
LWIP_UNUSED_ARG(ipaddr);
@@ -281,7 +297,7 @@ static err_t test_tcp_netif_output(struct netif *netif, struct pbuf *p,
}
void test_tcp_init_netif(struct netif *netif, struct test_tcp_txcounters *txcounters,
- ip_addr_t *ip_addr, ip_addr_t *netmask)
+ const ip_addr_t *ip_addr, const ip_addr_t *netmask)
{
struct netif *n;
memset(netif, 0, sizeof(struct netif));
@@ -290,9 +306,9 @@ void test_tcp_init_netif(struct netif *netif, struct test_tcp_txcounters *txcoun
netif->state = txcounters;
}
netif->output = test_tcp_netif_output;
- netif->flags |= NETIF_FLAG_UP;
- ip_addr_copy(netif->netmask, *netmask);
- ip_addr_copy(netif->ip_addr, *ip_addr);
+ netif->flags |= NETIF_FLAG_UP | NETIF_FLAG_LINK_UP;
+ ip_addr_copy_from_ip4(netif->netmask, *ip_2_ip4(netmask));
+ ip_addr_copy_from_ip4(netif->ip_addr, *ip_2_ip4(ip_addr));
for (n = netif_list; n != NULL; n = n->next) {
if (n == netif) {
return;
diff --git a/lwip/test/unit/tcp/tcp_helper.h b/lwip/test/unit/tcp/tcp_helper.h
index 4a72c93..cc72e2a 100644
--- a/lwip/test/unit/tcp/tcp_helper.h
+++ b/lwip/test/unit/tcp/tcp_helper.h
@@ -1,5 +1,5 @@
-#ifndef __TCP_HELPER_H__
-#define __TCP_HELPER_H__
+#ifndef LWIP_HDR_TCP_HELPER_H
+#define LWIP_HDR_TCP_HELPER_H
#include "../lwip_check.h"
#include "lwip/arch.h"
@@ -26,6 +26,12 @@ struct test_tcp_txcounters {
struct pbuf *tx_packets;
};
+extern const ip_addr_t test_local_ip;
+extern const ip_addr_t test_remote_ip;
+extern const ip_addr_t test_netmask;
+#define TEST_REMOTE_PORT 0x100
+#define TEST_LOCAL_PORT 0x101
+
/* Helper functions */
void tcp_remove_all(void);
@@ -36,8 +42,8 @@ struct pbuf* tcp_create_rx_segment(struct tcp_pcb* pcb, void* data, size_t data_
u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags);
struct pbuf* tcp_create_rx_segment_wnd(struct tcp_pcb* pcb, void* data, size_t data_len,
u32_t seqno_offset, u32_t ackno_offset, u8_t headerflags, u16_t wnd);
-void tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, ip_addr_t* local_ip,
- ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port);
+void tcp_set_state(struct tcp_pcb* pcb, enum tcp_state state, const ip_addr_t* local_ip,
+ const ip_addr_t* remote_ip, u16_t local_port, u16_t remote_port);
void test_tcp_counters_err(void* arg, err_t err);
err_t test_tcp_counters_recv(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err);
@@ -46,7 +52,7 @@ struct tcp_pcb* test_tcp_new_counters_pcb(struct test_tcp_counters* counters);
void test_tcp_input(struct pbuf *p, struct netif *inp);
void test_tcp_init_netif(struct netif *netif, struct test_tcp_txcounters *txcounters,
- ip_addr_t *ip_addr, ip_addr_t *netmask);
+ const ip_addr_t *ip_addr, const ip_addr_t *netmask);
#endif
diff --git a/lwip/test/unit/tcp/test_tcp.c b/lwip/test/unit/tcp/test_tcp.c
index 8172098..c272202 100644
--- a/lwip/test/unit/tcp/test_tcp.c
+++ b/lwip/test/unit/tcp/test_tcp.c
@@ -1,8 +1,9 @@
#include "test_tcp.h"
-#include "lwip/tcp_impl.h"
+#include "lwip/priv/tcp_priv.h"
#include "lwip/stats.h"
#include "tcp_helper.h"
+#include "lwip/inet_chksum.h"
#ifdef _MSC_VER
#pragma warning(disable: 4307) /* we explicitly wrap around TCP seqnos */
@@ -15,6 +16,17 @@
#error "This tests needs TCP_SND_BUF to be > TCP_WND"
#endif
+/* used with check_seqnos() */
+#define SEQNO1 (0xFFFFFF00 - TCP_MSS)
+#define ISS 6510
+static u32_t seqnos[] = {
+ SEQNO1,
+ SEQNO1 + (1 * TCP_MSS),
+ SEQNO1 + (2 * TCP_MSS),
+ SEQNO1 + (3 * TCP_MSS),
+ SEQNO1 + (4 * TCP_MSS),
+ SEQNO1 + (5 * TCP_MSS) };
+
static u8_t test_tcp_timer;
/* our own version of tcp_tmr so we can reset fast/slow timer state */
@@ -28,26 +40,37 @@ test_tcp_tmr(void)
}
/* Setups/teardown functions */
+static struct netif *old_netif_list;
+static struct netif *old_netif_default;
static void
tcp_setup(void)
{
+ old_netif_list = netif_list;
+ old_netif_default = netif_default;
+ netif_list = NULL;
+ netif_default = NULL;
/* reset iss to default (6510) */
tcp_ticks = 0;
- tcp_ticks = 0 - (tcp_next_iss() - 6510);
- tcp_next_iss();
+ tcp_ticks = 0 - (tcp_next_iss(NULL) - 6510);
+ tcp_next_iss(NULL);
tcp_ticks = 0;
test_tcp_timer = 0;
tcp_remove_all();
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
tcp_teardown(void)
{
- tcp_remove_all();
netif_list = NULL;
netif_default = NULL;
+ tcp_remove_all();
+ /* restore netif_list for next tests (e.g. loopif) */
+ netif_list = old_netif_list;
+ netif_default = old_netif_default;
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
@@ -59,18 +82,77 @@ START_TEST(test_tcp_new_abort)
struct tcp_pcb* pcb;
LWIP_UNUSED_ARG(_i);
- fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+ fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
pcb = tcp_new();
fail_unless(pcb != NULL);
if (pcb != NULL) {
- fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
tcp_abort(pcb);
- fail_unless(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+ fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
}
}
END_TEST
+/** Call tcp_new() and tcp_abort() and test memp stats */
+START_TEST(test_tcp_listen_passive_open)
+{
+ struct tcp_pcb *pcb, *pcbl;
+ struct tcp_pcb_listen *lpcb;
+ struct netif netif;
+ struct test_tcp_txcounters txcounters;
+ struct test_tcp_counters counters;
+ struct pbuf *p;
+ ip_addr_t src_addr;
+ err_t err;
+ LWIP_UNUSED_ARG(_i);
+
+ fail_unless(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
+
+ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask);
+ /* initialize counter struct */
+ memset(&counters, 0, sizeof(counters));
+
+ pcb = tcp_new();
+ EXPECT_RET(pcb != NULL);
+ err = tcp_bind(pcb, &netif.ip_addr, 1234);
+ EXPECT(err == ERR_OK);
+ pcbl = tcp_listen(pcb);
+ EXPECT_RET(pcbl != NULL);
+ EXPECT_RET(pcbl != pcb);
+ lpcb = (struct tcp_pcb_listen *)pcbl;
+
+ ip_addr_set_ip4_u32_val(src_addr, lwip_htonl(lwip_ntohl(ip_addr_get_ip4_u32(&lpcb->local_ip)) + 1));
+
+ /* check correct syn packet */
+ p = tcp_create_segment(&src_addr, &lpcb->local_ip, 12345,
+ lpcb->local_port, NULL, 0, 12345, 54321, TCP_SYN);
+ EXPECT(p != NULL);
+ if (p != NULL) {
+ /* pass the segment to tcp_input */
+ test_tcp_input(p, &netif);
+ /* check if counters are as expected */
+ EXPECT(txcounters.num_tx_calls == 1);
+ }
+
+ /* chekc syn packet with short length */
+ p = tcp_create_segment(&src_addr, &lpcb->local_ip, 12345,
+ lpcb->local_port, NULL, 0, 12345, 54321, TCP_SYN);
+ EXPECT(p != NULL);
+ EXPECT(p->next == NULL);
+ if ((p != NULL) && (p->next == NULL)) {
+ p->len -= 2;
+ p->tot_len -= 2;
+ /* pass the segment to tcp_input */
+ test_tcp_input(p, &netif);
+ /* check if counters are as expected */
+ EXPECT(txcounters.num_tx_calls == 1);
+ }
+
+ tcp_close(pcbl);
+}
+END_TEST
+
/** Create an ESTABLISHED pcb and check if receive callback is called */
START_TEST(test_tcp_recv_inseq)
{
@@ -78,19 +160,13 @@ START_TEST(test_tcp_recv_inseq)
struct tcp_pcb* pcb;
struct pbuf* p;
char data[] = {1, 2, 3, 4};
- ip_addr_t remote_ip, local_ip, netmask;
u16_t data_len;
- u16_t remote_port = 0x100, local_port = 0x101;
struct netif netif;
struct test_tcp_txcounters txcounters;
LWIP_UNUSED_ARG(_i);
/* initialize local vars */
- memset(&netif, 0, sizeof(netif));
- IP4_ADDR(&local_ip, 192, 168, 1, 1);
- IP4_ADDR(&remote_ip, 192, 168, 1, 2);
- IP4_ADDR(&netmask, 255, 255, 255, 0);
- test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
+ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask);
data_len = sizeof(data);
/* initialize counter struct */
memset(&counters, 0, sizeof(counters));
@@ -100,7 +176,7 @@ START_TEST(test_tcp_recv_inseq)
/* create and initialize the pcb */
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
- tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
/* create a segment */
p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0);
@@ -116,12 +192,206 @@ START_TEST(test_tcp_recv_inseq)
}
/* make sure the pcb is freed */
- EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
tcp_abort(pcb);
- EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
}
END_TEST
+/** Create an ESTABLISHED pcb and check if receive callback is called if a segment
+ * overlapping rcv_nxt is received */
+START_TEST(test_tcp_recv_inseq_trim)
+{
+ struct test_tcp_counters counters;
+ struct tcp_pcb* pcb;
+ struct pbuf* p;
+ char data[PBUF_POOL_BUFSIZE*2];
+ u16_t data_len;
+ struct netif netif;
+ struct test_tcp_txcounters txcounters;
+ const u32_t new_data_len = 40;
+ LWIP_UNUSED_ARG(_i);
+
+ /* initialize local vars */
+ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask);
+ data_len = sizeof(data);
+ memset(data, 0, sizeof(data));
+ /* initialize counter struct */
+ memset(&counters, 0, sizeof(counters));
+ counters.expected_data_len = data_len;
+ counters.expected_data = data;
+
+ /* create and initialize the pcb */
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
+
+ /* create a segment (with an overlapping/old seqno so that the new data begins in the 2nd pbuf) */
+ p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, (u32_t)(0-(data_len-new_data_len)), 0, 0);
+ EXPECT(p != NULL);
+ if (p != NULL) {
+ EXPECT(p->next != NULL);
+ if (p->next != NULL) {
+ EXPECT(p->next->next != NULL);
+ }
+ }
+ if ((p != NULL) && (p->next != NULL) && (p->next->next != NULL)) {
+ /* pass the segment to tcp_input */
+ test_tcp_input(p, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 1);
+ EXPECT(counters.recved_bytes == new_data_len);
+ EXPECT(counters.err_calls == 0);
+ }
+
+ /* make sure the pcb is freed */
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
+ tcp_abort(pcb);
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
+}
+END_TEST
+
+static err_t test_tcp_recv_expect1byte(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err);
+
+static err_t
+test_tcp_recv_expectclose(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err)
+{
+ EXPECT_RETX(pcb != NULL, ERR_OK);
+ EXPECT_RETX(err == ERR_OK, ERR_OK);
+ LWIP_UNUSED_ARG(arg);
+
+ if (p != NULL) {
+ fail();
+ pbuf_free(p);
+ } else {
+ /* correct: FIN received; close our end, too */
+ err_t err2 = tcp_close(pcb);
+ fail_unless(err2 == ERR_OK);
+ /* set back to some other rx function, just to not get here again */
+ tcp_recv(pcb, test_tcp_recv_expect1byte);
+ }
+ return ERR_OK;
+}
+
+static err_t
+test_tcp_recv_expect1byte(void* arg, struct tcp_pcb* pcb, struct pbuf* p, err_t err)
+{
+ EXPECT_RETX(pcb != NULL, ERR_OK);
+ EXPECT_RETX(err == ERR_OK, ERR_OK);
+ LWIP_UNUSED_ARG(arg);
+
+ if (p != NULL) {
+ if ((p->len == 1) && (p->tot_len == 1)) {
+ tcp_recv(pcb, test_tcp_recv_expectclose);
+ } else {
+ fail();
+ }
+ pbuf_free(p);
+ } else {
+ fail();
+ }
+ return ERR_OK;
+}
+
+START_TEST(test_tcp_passive_close)
+{
+ struct test_tcp_counters counters;
+ struct tcp_pcb* pcb;
+ struct pbuf* p;
+ char data = 0x0f;
+ struct netif netif;
+ struct test_tcp_txcounters txcounters;
+ LWIP_UNUSED_ARG(_i);
+
+ /* initialize local vars */
+ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask);
+
+ /* initialize counter struct */
+ memset(&counters, 0, sizeof(counters));
+ counters.expected_data_len = 1;
+ counters.expected_data = &data;
+
+ /* create and initialize the pcb */
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
+
+ /* create a segment without data */
+ p = tcp_create_rx_segment(pcb, &data, 1, 0, 0, TCP_FIN);
+ EXPECT(p != NULL);
+ if (p != NULL) {
+ tcp_recv(pcb, test_tcp_recv_expect1byte);
+ /* pass the segment to tcp_input */
+ test_tcp_input(p, &netif);
+ }
+ /* don't free the pcb here (part of the test!) */
+}
+END_TEST
+
+/** Check that we handle malformed tcp headers, and discard the pbuf(s) */
+START_TEST(test_tcp_malformed_header)
+{
+ struct test_tcp_counters counters;
+ struct tcp_pcb* pcb;
+ struct pbuf* p;
+ char data[] = {1, 2, 3, 4};
+ u16_t data_len, chksum;
+ struct netif netif;
+ struct test_tcp_txcounters txcounters;
+ struct tcp_hdr *hdr;
+ LWIP_UNUSED_ARG(_i);
+
+ /* initialize local vars */
+ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask);
+ data_len = sizeof(data);
+ /* initialize counter struct */
+ memset(&counters, 0, sizeof(counters));
+ counters.expected_data_len = data_len;
+ counters.expected_data = data;
+
+ /* create and initialize the pcb */
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
+
+ /* create a segment */
+ p = tcp_create_rx_segment(pcb, counters.expected_data, data_len, 0, 0, 0);
+
+ pbuf_header(p, -(s16_t)sizeof(struct ip_hdr));
+
+ hdr = (struct tcp_hdr *)p->payload;
+ TCPH_HDRLEN_FLAGS_SET(hdr, 15, 0x3d1);
+
+ hdr->chksum = 0;
+
+ chksum = ip_chksum_pseudo(p, IP_PROTO_TCP, p->tot_len,
+ &test_remote_ip, &test_local_ip);
+
+ hdr->chksum = chksum;
+
+ pbuf_header(p, sizeof(struct ip_hdr));
+
+ EXPECT(p != NULL);
+ EXPECT(p->next == NULL);
+ if (p != NULL) {
+ /* pass the segment to tcp_input */
+ test_tcp_input(p, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ }
+
+ /* make sure the pcb is freed */
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
+ tcp_abort(pcb);
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
+}
+END_TEST
+
+
/** Provoke fast retransmission by duplicate ACKs and then recover by ACKing all sent data.
* At the end, send more data. */
START_TEST(test_tcp_fast_retx_recover)
@@ -136,23 +406,18 @@ START_TEST(test_tcp_fast_retx_recover)
char data3[] = { 9, 10, 11, 12};
char data4[] = {13, 14, 15, 16};
char data5[] = {17, 18, 19, 20};
- char data6[] = {21, 22, 23, 24};
- ip_addr_t remote_ip, local_ip, netmask;
- u16_t remote_port = 0x100, local_port = 0x101;
+ char data6[TCP_MSS] = {21, 22, 23, 24};
err_t err;
LWIP_UNUSED_ARG(_i);
/* initialize local vars */
- IP4_ADDR(&local_ip, 192, 168, 1, 1);
- IP4_ADDR(&remote_ip, 192, 168, 1, 2);
- IP4_ADDR(&netmask, 255, 255, 255, 0);
- test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
+ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask);
memset(&counters, 0, sizeof(counters));
/* create and initialize the pcb */
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
- tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
pcb->mss = TCP_MSS;
/* disable initial congestion window (we don't send a SYN here...) */
pcb->cwnd = pcb->snd_wnd;
@@ -210,7 +475,7 @@ START_TEST(test_tcp_fast_retx_recover)
/*EXPECT_RET(txcounters.num_tx_calls == 1);*/
EXPECT_RET(pcb->dupacks == 3);
memset(&txcounters, 0, sizeof(txcounters));
- /* TODO: check expected data?*/
+ /* @todo: check expected data?*/
/* send data5, not output yet */
err = tcp_write(pcb, data5, sizeof(data5), TCP_WRITE_FLAG_COPY);
@@ -288,9 +553,9 @@ START_TEST(test_tcp_fast_retx_recover)
}
#endif
/* make sure the pcb is freed */
- EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
tcp_abort(pcb);
- EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+ EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
}
END_TEST
@@ -318,19 +583,8 @@ START_TEST(test_tcp_fast_rexmit_wraparound)
struct test_tcp_counters counters;
struct tcp_pcb* pcb;
struct pbuf* p;
- ip_addr_t remote_ip, local_ip, netmask;
- u16_t remote_port = 0x100, local_port = 0x101;
err_t err;
-#define SEQNO1 (0xFFFFFF00 - TCP_MSS)
-#define ISS 6510
u16_t i, sent_total = 0;
- u32_t seqnos[] = {
- SEQNO1,
- SEQNO1 + (1 * TCP_MSS),
- SEQNO1 + (2 * TCP_MSS),
- SEQNO1 + (3 * TCP_MSS),
- SEQNO1 + (4 * TCP_MSS),
- SEQNO1 + (5 * TCP_MSS)};
LWIP_UNUSED_ARG(_i);
for (i = 0; i < sizeof(tx_data); i++) {
@@ -338,21 +592,19 @@ START_TEST(test_tcp_fast_rexmit_wraparound)
}
/* initialize local vars */
- IP4_ADDR(&local_ip, 192, 168, 1, 1);
- IP4_ADDR(&remote_ip, 192, 168, 1, 2);
- IP4_ADDR(&netmask, 255, 255, 255, 0);
- test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
+ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask);
memset(&counters, 0, sizeof(counters));
/* create and initialize the pcb */
tcp_ticks = SEQNO1 - ISS;
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
- EXPECT(pcb->lastack == SEQNO1);
- tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
pcb->mss = TCP_MSS;
/* disable initial congestion window (we don't send a SYN here...) */
pcb->cwnd = 2*TCP_MSS;
+ /* start in congestion advoidance */
+ pcb->ssthresh = pcb->cwnd;
/* send 6 mss-sized segments */
for (i = 0; i < 6; i++) {
@@ -373,7 +625,9 @@ START_TEST(test_tcp_fast_rexmit_wraparound)
/* ACK the first segment */
p = tcp_create_rx_segment(pcb, NULL, 0, 0, TCP_MSS, TCP_ACK);
test_tcp_input(p, &netif);
- /* ensure this didn't trigger a retransmission */
+ /* ensure this didn't trigger a retransmission. Only one
+ segment should be transmitted because cwnd opened up by
+ TCP_MSS and a fraction since we are in congestion avoidance */
EXPECT(txcounters.num_tx_calls == 1);
EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U);
memset(&txcounters, 0, sizeof(txcounters));
@@ -400,9 +654,9 @@ START_TEST(test_tcp_fast_rexmit_wraparound)
check_seqnos(pcb->unacked, 5, &seqnos[1]);
/* make sure the pcb is freed */
- EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
tcp_abort(pcb);
- EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+ EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
}
END_TEST
@@ -415,19 +669,8 @@ START_TEST(test_tcp_rto_rexmit_wraparound)
struct test_tcp_txcounters txcounters;
struct test_tcp_counters counters;
struct tcp_pcb* pcb;
- ip_addr_t remote_ip, local_ip, netmask;
- u16_t remote_port = 0x100, local_port = 0x101;
err_t err;
-#define SEQNO1 (0xFFFFFF00 - TCP_MSS)
-#define ISS 6510
u16_t i, sent_total = 0;
- u32_t seqnos[] = {
- SEQNO1,
- SEQNO1 + (1 * TCP_MSS),
- SEQNO1 + (2 * TCP_MSS),
- SEQNO1 + (3 * TCP_MSS),
- SEQNO1 + (4 * TCP_MSS),
- SEQNO1 + (5 * TCP_MSS)};
LWIP_UNUSED_ARG(_i);
for (i = 0; i < sizeof(tx_data); i++) {
@@ -435,20 +678,16 @@ START_TEST(test_tcp_rto_rexmit_wraparound)
}
/* initialize local vars */
- IP4_ADDR(&local_ip, 192, 168, 1, 1);
- IP4_ADDR(&remote_ip, 192, 168, 1, 2);
- IP4_ADDR(&netmask, 255, 255, 255, 0);
- test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
+ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask);
memset(&counters, 0, sizeof(counters));
/* create and initialize the pcb */
tcp_ticks = 0;
- tcp_ticks = 0 - tcp_next_iss();
- tcp_ticks = SEQNO1 - tcp_next_iss();
+ tcp_ticks = 0 - tcp_next_iss(NULL);
+ tcp_ticks = SEQNO1 - tcp_next_iss(NULL);
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
- EXPECT(pcb->lastack == SEQNO1);
- tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
pcb->mss = TCP_MSS;
/* disable initial congestion window (we don't send a SYN here...) */
pcb->cwnd = 2*TCP_MSS;
@@ -490,9 +729,9 @@ START_TEST(test_tcp_rto_rexmit_wraparound)
check_seqnos(pcb->unacked, 6, seqnos);
/* make sure the pcb is freed */
- EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
tcp_abort(pcb);
- EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+ EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
}
END_TEST
@@ -505,8 +744,6 @@ static void test_tcp_tx_full_window_lost(u8_t zero_window_probe_from_unsent)
struct test_tcp_counters counters;
struct tcp_pcb* pcb;
struct pbuf *p;
- ip_addr_t remote_ip, local_ip, netmask;
- u16_t remote_port = 0x100, local_port = 0x101;
err_t err;
u16_t sent_total, i;
u8_t expected = 0xFE;
@@ -525,17 +762,13 @@ static void test_tcp_tx_full_window_lost(u8_t zero_window_probe_from_unsent)
}
/* initialize local vars */
- IP4_ADDR(&local_ip, 192, 168, 1, 1);
- IP4_ADDR(&remote_ip, 192, 168, 1, 2);
- IP4_ADDR(&netmask, 255, 255, 255, 0);
- test_tcp_init_netif(&netif, &txcounters, &local_ip, &netmask);
+ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask);
memset(&counters, 0, sizeof(counters));
- memset(&txcounters, 0, sizeof(txcounters));
/* create and initialize the pcb */
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
- tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
pcb->mss = TCP_MSS;
/* disable initial congestion window (we don't send a SYN here...) */
pcb->cwnd = pcb->snd_wnd;
@@ -590,7 +823,9 @@ static void test_tcp_tx_full_window_lost(u8_t zero_window_probe_from_unsent)
/* ensure this didn't trigger any transmission */
EXPECT(txcounters.num_tx_calls == 0);
EXPECT(txcounters.num_tx_bytes == 0);
- EXPECT(pcb->persist_backoff == 1);
+ /* window is completely full, but persist timer is off since send buffer is empty */
+ EXPECT(pcb->snd_wnd == 0);
+ EXPECT(pcb->persist_backoff == 0);
}
/* send one byte more (out of window) -> persist timer starts */
@@ -635,9 +870,9 @@ static void test_tcp_tx_full_window_lost(u8_t zero_window_probe_from_unsent)
}
/* make sure the pcb is freed */
- EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
tcp_abort(pcb);
- EXPECT_RET(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+ EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
}
START_TEST(test_tcp_tx_full_window_lost_from_unsent)
@@ -654,18 +889,474 @@ START_TEST(test_tcp_tx_full_window_lost_from_unacked)
}
END_TEST
+START_TEST(test_tcp_rto_tracking)
+{
+ struct netif netif;
+ struct test_tcp_txcounters txcounters;
+ struct test_tcp_counters counters;
+ struct tcp_pcb* pcb;
+ struct pbuf* p;
+ err_t err;
+ u16_t i, sent_total = 0;
+ LWIP_UNUSED_ARG(_i);
+
+ for (i = 0; i < sizeof(tx_data); i++) {
+ tx_data[i] = (u8_t)i;
+ }
+
+ /* initialize local vars */
+ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask);
+ memset(&counters, 0, sizeof(counters));
+
+ /* create and initialize the pcb */
+ tcp_ticks = SEQNO1 - ISS;
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
+ pcb->mss = TCP_MSS;
+ /* Set congestion window large enough to send all our segments */
+ pcb->cwnd = 5*TCP_MSS;
+
+ /* send 5 mss-sized segments */
+ for (i = 0; i < 5; i++) {
+ err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ sent_total += TCP_MSS;
+ }
+ check_seqnos(pcb->unsent, 5, seqnos);
+ EXPECT(pcb->unacked == NULL);
+ err = tcp_output(pcb);
+ EXPECT(txcounters.num_tx_calls == 5);
+ EXPECT(txcounters.num_tx_bytes == 5 * (TCP_MSS + 40U));
+ memset(&txcounters, 0, sizeof(txcounters));
+ /* Check all 5 are in-flight */
+ EXPECT(pcb->unsent == NULL);
+ check_seqnos(pcb->unacked, 5, seqnos);
+
+ /* Force us into retransmisson timeout */
+ while (!(pcb->flags & TF_RTO)) {
+ test_tcp_tmr();
+ }
+ /* Ensure 4 remaining segments are back on unsent, ready for retransmission */
+ check_seqnos(pcb->unsent, 4, &seqnos[1]);
+ /* Ensure 1st segment is on unacked (already retransmitted) */
+ check_seqnos(pcb->unacked, 1, seqnos);
+ EXPECT(txcounters.num_tx_calls == 1);
+ EXPECT(txcounters.num_tx_bytes == TCP_MSS + 40U);
+ memset(&txcounters, 0, sizeof(txcounters));
+ /* Ensure rto_end points to next byte */
+ EXPECT(pcb->rto_end == seqnos[5]);
+ EXPECT(pcb->rto_end == pcb->snd_nxt);
+ /* Check cwnd was reset */
+ EXPECT(pcb->cwnd == pcb->mss);
+
+ /* Add another segment to send buffer which is outside of RTO */
+ err = tcp_write(pcb, &tx_data[sent_total], TCP_MSS, TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ sent_total += TCP_MSS;
+ check_seqnos(pcb->unsent, 5, &seqnos[1]);
+ /* Ensure no new data was sent */
+ EXPECT(txcounters.num_tx_calls == 0);
+ EXPECT(txcounters.num_tx_bytes == 0);
+ EXPECT(pcb->rto_end == pcb->snd_nxt);
+
+ /* ACK first segment */
+ p = tcp_create_rx_segment(pcb, NULL, 0, 0, TCP_MSS, TCP_ACK);
+ test_tcp_input(p, &netif);
+ /* Next two retranmissions should go out, due to cwnd in slow start */
+ EXPECT(txcounters.num_tx_calls == 2);
+ EXPECT(txcounters.num_tx_bytes == 2 * (TCP_MSS + 40U));
+ memset(&txcounters, 0, sizeof(txcounters));
+ check_seqnos(pcb->unacked, 2, &seqnos[1]);
+ check_seqnos(pcb->unsent, 3, &seqnos[3]);
+ /* RTO should still be marked */
+ EXPECT(pcb->flags & TF_RTO);
+ /* cwnd should have only grown by 1 MSS */
+ EXPECT(pcb->cwnd == (tcpwnd_size_t)(2 * pcb->mss));
+ /* Ensure no new data was sent */
+ EXPECT(pcb->rto_end == pcb->snd_nxt);
+
+ /* ACK the next two segments */
+ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 2*TCP_MSS, TCP_ACK);
+ test_tcp_input(p, &netif);
+ /* Final 2 retransmissions and 1 new data should go out */
+ EXPECT(txcounters.num_tx_calls == 3);
+ EXPECT(txcounters.num_tx_bytes == 3 * (TCP_MSS + 40U));
+ memset(&txcounters, 0, sizeof(txcounters));
+ check_seqnos(pcb->unacked, 3, &seqnos[3]);
+ EXPECT(pcb->unsent == NULL);
+ /* RTO should still be marked */
+ EXPECT(pcb->flags & TF_RTO);
+ /* cwnd should have only grown by 1 MSS */
+ EXPECT(pcb->cwnd == (tcpwnd_size_t)(3 * pcb->mss));
+ /* snd_nxt should have been advanced past rto_end */
+ EXPECT(TCP_SEQ_GT(pcb->snd_nxt, pcb->rto_end));
+
+ /* ACK the next two segments, finishing our RTO, leaving new segment unacked */
+ p = tcp_create_rx_segment(pcb, NULL, 0, 0, 2*TCP_MSS, TCP_ACK);
+ test_tcp_input(p, &netif);
+ EXPECT(!(pcb->flags & TF_RTO));
+ check_seqnos(pcb->unacked, 1, &seqnos[5]);
+ /* We should be in ABC congestion avoidance, so no change in cwnd */
+ EXPECT(pcb->cwnd == (tcpwnd_size_t)(3 * pcb->mss));
+ EXPECT(pcb->cwnd >= pcb->ssthresh);
+ /* Ensure ABC congestion avoidance is tracking bytes acked */
+ EXPECT(pcb->bytes_acked == (tcpwnd_size_t)(2 * pcb->mss));
+
+ /* make sure the pcb is freed */
+ EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
+ tcp_abort(pcb);
+ EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
+}
+END_TEST
+
+START_TEST(test_tcp_rto_timeout)
+{
+ struct netif netif;
+ struct test_tcp_txcounters txcounters;
+ struct test_tcp_counters counters;
+ struct tcp_pcb *pcb, *cur;
+ err_t err;
+ u16_t i;
+ LWIP_UNUSED_ARG(_i);
+
+ /* Setup data for a single segment */
+ for (i = 0; i < TCP_MSS; i++) {
+ tx_data[i] = (u8_t)i;
+ }
+
+ /* initialize local vars */
+ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask);
+ memset(&counters, 0, sizeof(counters));
+
+ /* create and initialize the pcb */
+ tcp_ticks = SEQNO1 - ISS;
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
+ pcb->mss = TCP_MSS;
+ pcb->cwnd = TCP_MSS;
+
+ /* send our segment */
+ err = tcp_write(pcb, &tx_data[0], TCP_MSS, TCP_WRITE_FLAG_COPY);
+ EXPECT_RET(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT(txcounters.num_tx_calls == 1);
+ EXPECT(txcounters.num_tx_bytes == 1 * (TCP_MSS + 40U));
+ memset(&txcounters, 0, sizeof(txcounters));
+
+ /* ensure no errors have been recorded */
+ EXPECT(counters.err_calls == 0);
+ EXPECT(counters.last_err == ERR_OK);
+
+ /* Force us into retransmisson timeout */
+ while (!(pcb->flags & TF_RTO)) {
+ test_tcp_tmr();
+ }
+
+ /* check first rexmit */
+ EXPECT(pcb->nrtx == 1);
+ EXPECT(txcounters.num_tx_calls == 1);
+ EXPECT(txcounters.num_tx_bytes == 1 * (TCP_MSS + 40U));
+
+ /* still no error expected */
+ EXPECT(counters.err_calls == 0);
+ EXPECT(counters.last_err == ERR_OK);
+
+ /* keep running the timer till we hit our maximum RTO */
+ while (counters.last_err == ERR_OK) {
+ test_tcp_tmr();
+ }
+
+ /* check number of retransmissions */
+ EXPECT(txcounters.num_tx_calls == TCP_MAXRTX);
+ EXPECT(txcounters.num_tx_bytes == TCP_MAXRTX * (TCP_MSS + 40U));
+
+ /* check the connection (pcb) has been aborted */
+ EXPECT(counters.err_calls == 1);
+ EXPECT(counters.last_err == ERR_ABRT);
+ /* check our pcb is no longer active */
+ for (cur = tcp_active_pcbs; cur != NULL; cur = cur->next) {
+ EXPECT(cur != pcb);
+ }
+ EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
+}
+END_TEST
+
+START_TEST(test_tcp_zwp_timeout)
+{
+ struct netif netif;
+ struct test_tcp_txcounters txcounters;
+ struct test_tcp_counters counters;
+ struct tcp_pcb *pcb, *cur;
+ struct pbuf* p;
+ err_t err;
+ u16_t i;
+ LWIP_UNUSED_ARG(_i);
+
+ /* Setup data for two segments */
+ for (i = 0; i < 2*TCP_MSS; i++) {
+ tx_data[i] = (u8_t)i;
+ }
+
+ /* initialize local vars */
+ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask);
+ memset(&counters, 0, sizeof(counters));
+
+ /* create and initialize the pcb */
+ tcp_ticks = SEQNO1 - ISS;
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
+ pcb->mss = TCP_MSS;
+ pcb->cwnd = TCP_MSS;
+
+ /* send first segment */
+ err = tcp_write(pcb, &tx_data[0], TCP_MSS, TCP_WRITE_FLAG_COPY);
+ EXPECT(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT(err == ERR_OK);
+
+ /* verify segment is in-flight */
+ EXPECT(pcb->unsent == NULL);
+ check_seqnos(pcb->unacked, 1, seqnos);
+ EXPECT(txcounters.num_tx_calls == 1);
+ EXPECT(txcounters.num_tx_bytes == 1 * (TCP_MSS + 40U));
+ memset(&txcounters, 0, sizeof(txcounters));
+
+ /* ACK the segment and close the TX window */
+ p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, TCP_MSS, TCP_ACK, 0);
+ test_tcp_input(p, &netif);
+ EXPECT(pcb->unacked == NULL);
+ EXPECT(pcb->unsent == NULL);
+ /* send buffer empty, persist should be off */
+ EXPECT(pcb->persist_backoff == 0);
+ EXPECT(pcb->snd_wnd == 0);
+
+ /* send second segment, should be buffered */
+ err = tcp_write(pcb, &tx_data[TCP_MSS], TCP_MSS, TCP_WRITE_FLAG_COPY);
+ EXPECT(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT(err == ERR_OK);
+
+ /* ensure it is buffered and persist timer started */
+ EXPECT(pcb->unacked == NULL);
+ check_seqnos(pcb->unsent, 1, &seqnos[1]);
+ EXPECT(txcounters.num_tx_calls == 0);
+ EXPECT(txcounters.num_tx_bytes == 0);
+ EXPECT(pcb->persist_backoff == 1);
+
+ /* ensure no errors have been recorded */
+ EXPECT(counters.err_calls == 0);
+ EXPECT(counters.last_err == ERR_OK);
+
+ /* run timer till first probe */
+ EXPECT(pcb->persist_probe == 0);
+ while (pcb->persist_probe == 0) {
+ test_tcp_tmr();
+ }
+ EXPECT(txcounters.num_tx_calls == 1);
+ EXPECT(txcounters.num_tx_bytes == (1 + 40U));
+ memset(&txcounters, 0, sizeof(txcounters));
+
+ /* respond to probe with remote's current SEQ, ACK, and zero-window */
+ p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, 0, TCP_ACK, 0);
+ test_tcp_input(p, &netif);
+ /* ensure zero-window is still active, but probe count reset */
+ EXPECT(pcb->persist_backoff > 1);
+ EXPECT(pcb->persist_probe == 0);
+ EXPECT(pcb->snd_wnd == 0);
+
+ /* ensure no errors have been recorded */
+ EXPECT(counters.err_calls == 0);
+ EXPECT(counters.last_err == ERR_OK);
+
+ /* now run the timer till we hit our maximum probe count */
+ while (counters.last_err == ERR_OK) {
+ test_tcp_tmr();
+ }
+
+ /* check maximum number of 1 byte probes were sent */
+ EXPECT(txcounters.num_tx_calls == TCP_MAXRTX);
+ EXPECT(txcounters.num_tx_bytes == TCP_MAXRTX * (1 + 40U));
+
+ /* check the connection (pcb) has been aborted */
+ EXPECT(counters.err_calls == 1);
+ EXPECT(counters.last_err == ERR_ABRT);
+ /* check our pcb is no longer active */
+ for (cur = tcp_active_pcbs; cur != NULL; cur = cur->next) {
+ EXPECT(cur != pcb);
+ }
+ EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
+}
+END_TEST
+
+START_TEST(test_tcp_persist_split)
+{
+ struct netif netif;
+ struct test_tcp_txcounters txcounters;
+ struct test_tcp_counters counters;
+ struct tcp_pcb *pcb;
+ struct pbuf* p;
+ err_t err;
+ u16_t i;
+ LWIP_UNUSED_ARG(_i);
+
+ /* Setup data for four segments */
+ for (i = 0; i < 4 * TCP_MSS; i++) {
+ tx_data[i] = (u8_t)i;
+ }
+
+ /* initialize local vars */
+ test_tcp_init_netif(&netif, &txcounters, &test_local_ip, &test_netmask);
+ memset(&counters, 0, sizeof(counters));
+
+ /* create and initialize the pcb */
+ tcp_ticks = SEQNO1 - ISS;
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
+ pcb->mss = TCP_MSS;
+ /* set window to three segments */
+ pcb->cwnd = 3 * TCP_MSS;
+ pcb->snd_wnd = 3 * TCP_MSS;
+ pcb->snd_wnd_max = 3 * TCP_MSS;
+
+ /* send three segments */
+ err = tcp_write(pcb, &tx_data[0], 3 * TCP_MSS, TCP_WRITE_FLAG_COPY);
+ EXPECT(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT(err == ERR_OK);
+
+ /* verify segments are in-flight */
+ EXPECT(pcb->unsent == NULL);
+ EXPECT(pcb->unacked != NULL);
+ check_seqnos(pcb->unacked, 3, seqnos);
+ EXPECT(txcounters.num_tx_calls == 3);
+ EXPECT(txcounters.num_tx_bytes == 3 * (TCP_MSS + 40U));
+ memset(&txcounters, 0, sizeof(txcounters));
+
+ /* ACK the segments and update the window to only 1/2 TCP_MSS */
+ p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, 3 * TCP_MSS, TCP_ACK, TCP_MSS / 2);
+ test_tcp_input(p, &netif);
+ EXPECT(pcb->unacked == NULL);
+ EXPECT(pcb->unsent == NULL);
+ EXPECT(pcb->persist_backoff == 0);
+ EXPECT(pcb->snd_wnd == TCP_MSS / 2);
+
+ /* send fourth segment, which is larger than snd_wnd */
+ err = tcp_write(pcb, &tx_data[3 * TCP_MSS], TCP_MSS, TCP_WRITE_FLAG_COPY);
+ EXPECT(err == ERR_OK);
+ err = tcp_output(pcb);
+ EXPECT(err == ERR_OK);
+
+ /* ensure it is buffered and persist timer started */
+ EXPECT(pcb->unacked == NULL);
+ EXPECT(pcb->unsent != NULL);
+ check_seqnos(pcb->unsent, 1, &seqnos[3]);
+ EXPECT(txcounters.num_tx_calls == 0);
+ EXPECT(txcounters.num_tx_bytes == 0);
+ EXPECT(pcb->persist_backoff == 1);
+
+ /* ensure no errors have been recorded */
+ EXPECT(counters.err_calls == 0);
+ EXPECT(counters.last_err == ERR_OK);
+
+ /* call tcp_timer some more times to let persist timer count up */
+ for (i = 0; i < 4; i++) {
+ test_tcp_tmr();
+ EXPECT(txcounters.num_tx_calls == 0);
+ EXPECT(txcounters.num_tx_bytes == 0);
+ }
+
+ /* this should be the first timer shot, which should split the
+ * segment and send a runt (of the remaining window size) */
+ txcounters.copy_tx_packets = 1;
+ test_tcp_tmr();
+ txcounters.copy_tx_packets = 0;
+ /* persist will be disabled as RTO timer takes over */
+ EXPECT(pcb->persist_backoff == 0);
+ EXPECT(txcounters.num_tx_calls == 1);
+ EXPECT(txcounters.num_tx_bytes == ((TCP_MSS /2) + 40U));
+ /* verify half segment sent, half still buffered */
+ EXPECT(pcb->unsent != NULL);
+ EXPECT(pcb->unsent->len == TCP_MSS / 2);
+ EXPECT(pcb->unacked != NULL);
+ EXPECT(pcb->unacked->len == TCP_MSS / 2);
+
+ /* verify first half segment */
+ EXPECT(txcounters.tx_packets != NULL);
+ if (txcounters.tx_packets != NULL) {
+ u8_t sent[TCP_MSS / 2];
+ u16_t ret;
+ ret = pbuf_copy_partial(txcounters.tx_packets, &sent, TCP_MSS / 2, 40U);
+ EXPECT(ret == TCP_MSS / 2);
+ EXPECT(memcmp(sent, &tx_data[3 * TCP_MSS], TCP_MSS / 2) == 0);
+ }
+ if (txcounters.tx_packets != NULL) {
+ pbuf_free(txcounters.tx_packets);
+ txcounters.tx_packets = NULL;
+ }
+ memset(&txcounters, 0, sizeof(txcounters));
+
+ /* ACK the half segment, leave window at half segment */
+ p = tcp_create_rx_segment_wnd(pcb, NULL, 0, 0, TCP_MSS / 2, TCP_ACK, TCP_MSS / 2);
+ txcounters.copy_tx_packets = 1;
+ test_tcp_input(p, &netif);
+ txcounters.copy_tx_packets = 0;
+ /* ensure remaining half segment was sent */
+ EXPECT(txcounters.num_tx_calls == 1);
+ EXPECT(txcounters.num_tx_bytes == ((TCP_MSS /2 ) + 40U));
+ EXPECT(pcb->unsent == NULL);
+ EXPECT(pcb->unacked != NULL);
+ EXPECT(pcb->unacked->len == TCP_MSS / 2);
+ EXPECT(pcb->snd_wnd == TCP_MSS / 2);
+
+ /* verify second half segment */
+ EXPECT(txcounters.tx_packets != NULL);
+ if (txcounters.tx_packets != NULL) {
+ u8_t sent[TCP_MSS / 2];
+ u16_t ret;
+ ret = pbuf_copy_partial(txcounters.tx_packets, &sent, TCP_MSS / 2, 40U);
+ EXPECT(ret == TCP_MSS / 2);
+ EXPECT(memcmp(sent, &tx_data[(3 * TCP_MSS) + TCP_MSS / 2], TCP_MSS / 2) == 0);
+ }
+ if (txcounters.tx_packets != NULL) {
+ pbuf_free(txcounters.tx_packets);
+ txcounters.tx_packets = NULL;
+ }
+
+ /* ensure no errors have been recorded */
+ EXPECT(counters.err_calls == 0);
+ EXPECT(counters.last_err == ERR_OK);
+
+ /* make sure the pcb is freed */
+ EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
+ tcp_abort(pcb);
+ EXPECT_RET(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
+}
+END_TEST
+
/** Create the suite including all tests for this module */
Suite *
tcp_suite(void)
{
- TFun tests[] = {
- test_tcp_new_abort,
- test_tcp_recv_inseq,
- test_tcp_fast_retx_recover,
- test_tcp_fast_rexmit_wraparound,
- test_tcp_rto_rexmit_wraparound,
- test_tcp_tx_full_window_lost_from_unacked,
- test_tcp_tx_full_window_lost_from_unsent
+ testfunc tests[] = {
+ TESTFUNC(test_tcp_new_abort),
+ TESTFUNC(test_tcp_listen_passive_open),
+ TESTFUNC(test_tcp_recv_inseq),
+ TESTFUNC(test_tcp_recv_inseq_trim),
+ TESTFUNC(test_tcp_passive_close),
+ TESTFUNC(test_tcp_malformed_header),
+ TESTFUNC(test_tcp_fast_retx_recover),
+ TESTFUNC(test_tcp_fast_rexmit_wraparound),
+ TESTFUNC(test_tcp_rto_rexmit_wraparound),
+ TESTFUNC(test_tcp_tx_full_window_lost_from_unacked),
+ TESTFUNC(test_tcp_tx_full_window_lost_from_unsent),
+ TESTFUNC(test_tcp_rto_tracking),
+ TESTFUNC(test_tcp_rto_timeout),
+ TESTFUNC(test_tcp_zwp_timeout),
+ TESTFUNC(test_tcp_persist_split)
};
- return create_suite("TCP", tests, sizeof(tests)/sizeof(TFun), tcp_setup, tcp_teardown);
+ return create_suite("TCP", tests, sizeof(tests)/sizeof(testfunc), tcp_setup, tcp_teardown);
}
diff --git a/lwip/test/unit/tcp/test_tcp.h b/lwip/test/unit/tcp/test_tcp.h
index f1c4a46..f28ee56 100644
--- a/lwip/test/unit/tcp/test_tcp.h
+++ b/lwip/test/unit/tcp/test_tcp.h
@@ -1,5 +1,5 @@
-#ifndef __TEST_TCP_H__
-#define __TEST_TCP_H__
+#ifndef LWIP_HDR_TEST_TCP_H
+#define LWIP_HDR_TEST_TCP_H
#include "../lwip_check.h"
diff --git a/lwip/test/unit/tcp/test_tcp_oos.c b/lwip/test/unit/tcp/test_tcp_oos.c
index 612c468..a190e7d 100644
--- a/lwip/test/unit/tcp/test_tcp_oos.c
+++ b/lwip/test/unit/tcp/test_tcp_oos.c
@@ -1,6 +1,6 @@
#include "test_tcp_oos.h"
-#include "lwip/tcp_impl.h"
+#include "lwip/priv/tcp_priv.h"
#include "lwip/stats.h"
#include "tcp_helper.h"
@@ -36,6 +36,7 @@ static int tcp_oos_count(struct tcp_pcb* pcb)
return num;
}
+#if TCP_OOSEQ_MAX_PBUFS && (TCP_OOSEQ_MAX_PBUFS < ((TCP_WND / TCP_MSS) + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))
/** Get the numbers of pbufs on the ooseq list */
static int tcp_oos_pbuf_count(struct tcp_pcb* pcb)
{
@@ -47,6 +48,7 @@ static int tcp_oos_pbuf_count(struct tcp_pcb* pcb)
}
return num;
}
+#endif
/** Get the seqno of a segment (by index) on the ooseq list
*
@@ -116,19 +118,30 @@ tcp_oos_tcplen(struct tcp_pcb* pcb)
}
/* Setup/teardown functions */
+static struct netif *old_netif_list;
+static struct netif *old_netif_default;
static void
tcp_oos_setup(void)
{
+ old_netif_list = netif_list;
+ old_netif_default = netif_default;
+ netif_list = NULL;
+ netif_default = NULL;
tcp_remove_all();
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
tcp_oos_teardown(void)
{
- tcp_remove_all();
netif_list = NULL;
netif_default = NULL;
+ tcp_remove_all();
+ /* restore netif_list for next tests (e.g. loopif) */
+ netif_list = old_netif_list;
+ netif_default = old_netif_default;
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
@@ -148,18 +161,12 @@ START_TEST(test_tcp_recv_ooseq_FIN_OOSEQ)
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16};
- ip_addr_t remote_ip, local_ip, netmask;
u16_t data_len;
- u16_t remote_port = 0x100, local_port = 0x101;
struct netif netif;
LWIP_UNUSED_ARG(_i);
/* initialize local vars */
- memset(&netif, 0, sizeof(netif));
- IP4_ADDR(&local_ip, 192, 168, 1, 1);
- IP4_ADDR(&remote_ip, 192, 168, 1, 2);
- IP4_ADDR(&netmask, 255, 255, 255, 0);
- test_tcp_init_netif(&netif, NULL, &local_ip, &netmask);
+ test_tcp_init_netif(&netif, NULL, &test_local_ip, &test_netmask);
data_len = sizeof(data);
/* initialize counter struct */
memset(&counters, 0, sizeof(counters));
@@ -169,7 +176,7 @@ START_TEST(test_tcp_recv_ooseq_FIN_OOSEQ)
/* create and initialize the pcb */
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
- tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
/* create segments */
/* pinseq is sent as last segment! */
@@ -270,9 +277,9 @@ START_TEST(test_tcp_recv_ooseq_FIN_OOSEQ)
}
/* make sure the pcb is freed */
- EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
tcp_abort(pcb);
- EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
}
END_TEST
@@ -290,18 +297,12 @@ START_TEST(test_tcp_recv_ooseq_FIN_INSEQ)
5, 6, 7, 8,
9, 10, 11, 12,
13, 14, 15, 16};
- ip_addr_t remote_ip, local_ip, netmask;
u16_t data_len;
- u16_t remote_port = 0x100, local_port = 0x101;
struct netif netif;
LWIP_UNUSED_ARG(_i);
/* initialize local vars */
- memset(&netif, 0, sizeof(netif));
- IP4_ADDR(&local_ip, 192, 168, 1, 1);
- IP4_ADDR(&remote_ip, 192, 168, 1, 2);
- IP4_ADDR(&netmask, 255, 255, 255, 0);
- test_tcp_init_netif(&netif, NULL, &local_ip, &netmask);
+ test_tcp_init_netif(&netif, NULL, &test_local_ip, &test_netmask);
data_len = sizeof(data);
/* initialize counter struct */
memset(&counters, 0, sizeof(counters));
@@ -311,7 +312,7 @@ START_TEST(test_tcp_recv_ooseq_FIN_INSEQ)
/* create and initialize the pcb */
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
- tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
/* create segments */
/* p1: 7 bytes - 2 before FIN */
@@ -446,13 +447,13 @@ START_TEST(test_tcp_recv_ooseq_FIN_INSEQ)
}
/* make sure the pcb is freed */
- EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
tcp_abort(pcb);
- EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
}
END_TEST
-static char data_full_wnd[TCP_WND];
+static char data_full_wnd[TCP_WND + TCP_MSS];
/** create multiple segments and pass them to tcp_input with the first segment missing
* to simulate overruning the rxwin with ooseq queueing enabled */
@@ -463,22 +464,16 @@ START_TEST(test_tcp_recv_ooseq_overrun_rxwin)
struct test_tcp_counters counters;
struct tcp_pcb* pcb;
struct pbuf *pinseq, *p_ovr;
- ip_addr_t remote_ip, local_ip, netmask;
- u16_t remote_port = 0x100, local_port = 0x101;
struct netif netif;
int datalen = 0;
int datalen2;
- for(i = 0; i < sizeof(data_full_wnd); i++) {
+ for(i = 0; i < (int)sizeof(data_full_wnd); i++) {
data_full_wnd[i] = (char)i;
}
/* initialize local vars */
- memset(&netif, 0, sizeof(netif));
- IP4_ADDR(&local_ip, 192, 168, 1, 1);
- IP4_ADDR(&remote_ip, 192, 168, 1, 2);
- IP4_ADDR(&netmask, 255, 255, 255, 0);
- test_tcp_init_netif(&netif, NULL, &local_ip, &netmask);
+ test_tcp_init_netif(&netif, NULL, &test_local_ip, &test_netmask);
/* initialize counter struct */
memset(&counters, 0, sizeof(counters));
counters.expected_data_len = TCP_WND;
@@ -487,7 +482,7 @@ START_TEST(test_tcp_recv_ooseq_overrun_rxwin)
/* create and initialize the pcb */
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
- tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
pcb->rcv_nxt = 0x8000;
/* create segments */
@@ -540,9 +535,96 @@ START_TEST(test_tcp_recv_ooseq_overrun_rxwin)
EXPECT(pcb->ooseq == NULL);
/* make sure the pcb is freed */
- EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
+ tcp_abort(pcb);
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
+#endif /* !TCP_OOSEQ_MAX_BYTES && !TCP_OOSEQ_MAX_PBUFS */
+ LWIP_UNUSED_ARG(_i);
+}
+END_TEST
+
+/** similar to above test, except seqno starts near the max rxwin */
+START_TEST(test_tcp_recv_ooseq_overrun_rxwin_edge)
+{
+#if !TCP_OOSEQ_MAX_BYTES && !TCP_OOSEQ_MAX_PBUFS
+ int i, k;
+ struct test_tcp_counters counters;
+ struct tcp_pcb* pcb;
+ struct pbuf *pinseq, *p_ovr;
+ struct netif netif;
+ int datalen = 0;
+ int datalen2;
+
+ for(i = 0; i < (int)sizeof(data_full_wnd); i++) {
+ data_full_wnd[i] = (char)i;
+ }
+
+ /* initialize local vars */
+ test_tcp_init_netif(&netif, NULL, &test_local_ip, &test_netmask);
+ /* initialize counter struct */
+ memset(&counters, 0, sizeof(counters));
+ counters.expected_data_len = TCP_WND;
+ counters.expected_data = data_full_wnd;
+
+ /* create and initialize the pcb */
+ pcb = test_tcp_new_counters_pcb(&counters);
+ EXPECT_RET(pcb != NULL);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
+ pcb->rcv_nxt = 0xffffffff - (TCP_WND / 2);
+
+ /* create segments */
+ /* pinseq is sent as last segment! */
+ pinseq = tcp_create_rx_segment(pcb, &data_full_wnd[0], TCP_MSS, 0, 0, TCP_ACK);
+
+ for(i = TCP_MSS, k = 0; i < TCP_WND; i += TCP_MSS, k++) {
+ int count, expected_datalen;
+ struct pbuf *p = tcp_create_rx_segment(pcb, &data_full_wnd[TCP_MSS*(k+1)],
+ TCP_MSS, TCP_MSS*(k+1), 0, TCP_ACK);
+ EXPECT_RET(p != NULL);
+ /* pass the segment to tcp_input */
+ test_tcp_input(p, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ /* check ooseq queue */
+ count = tcp_oos_count(pcb);
+ EXPECT_OOSEQ(count == k+1);
+ datalen = tcp_oos_tcplen(pcb);
+ if (i + TCP_MSS < TCP_WND) {
+ expected_datalen = (k+1)*TCP_MSS;
+ } else {
+ expected_datalen = TCP_WND - TCP_MSS;
+ }
+ if (datalen != expected_datalen) {
+ EXPECT_OOSEQ(datalen == expected_datalen);
+ }
+ }
+
+ /* pass in one more segment, cleary overrunning the rxwin */
+ p_ovr = tcp_create_rx_segment(pcb, &data_full_wnd[TCP_MSS*(k+1)], TCP_MSS, TCP_MSS*(k+1), 0, TCP_ACK);
+ EXPECT_RET(p_ovr != NULL);
+ /* pass the segment to tcp_input */
+ test_tcp_input(p_ovr, &netif);
+ /* check if counters are as expected */
+ EXPECT(counters.close_calls == 0);
+ EXPECT(counters.recv_calls == 0);
+ EXPECT(counters.recved_bytes == 0);
+ EXPECT(counters.err_calls == 0);
+ /* check ooseq queue */
+ EXPECT_OOSEQ(tcp_oos_count(pcb) == k);
+ datalen2 = tcp_oos_tcplen(pcb);
+ EXPECT_OOSEQ(datalen == datalen2);
+
+ /* now pass inseq */
+ test_tcp_input(pinseq, &netif);
+ EXPECT(pcb->ooseq == NULL);
+
+ /* make sure the pcb is freed */
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
tcp_abort(pcb);
- EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
#endif /* !TCP_OOSEQ_MAX_BYTES && !TCP_OOSEQ_MAX_PBUFS */
LWIP_UNUSED_ARG(_i);
}
@@ -550,13 +632,11 @@ END_TEST
START_TEST(test_tcp_recv_ooseq_max_bytes)
{
-#if TCP_OOSEQ_MAX_BYTES && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))
+#if TCP_OOSEQ_MAX_BYTES && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))
int i, k;
struct test_tcp_counters counters;
struct tcp_pcb* pcb;
struct pbuf *p_ovr;
- ip_addr_t remote_ip, local_ip, netmask;
- u16_t remote_port = 0x100, local_port = 0x101;
struct netif netif;
int datalen = 0;
int datalen2;
@@ -566,11 +646,7 @@ START_TEST(test_tcp_recv_ooseq_max_bytes)
}
/* initialize local vars */
- memset(&netif, 0, sizeof(netif));
- IP4_ADDR(&local_ip, 192, 168, 1, 1);
- IP4_ADDR(&remote_ip, 192, 168, 1, 2);
- IP4_ADDR(&netmask, 255, 255, 255, 0);
- test_tcp_init_netif(&netif, NULL, &local_ip, &netmask);
+ test_tcp_init_netif(&netif, NULL, &test_local_ip, &test_netmask);
/* initialize counter struct */
memset(&counters, 0, sizeof(counters));
counters.expected_data_len = TCP_WND;
@@ -579,7 +655,7 @@ START_TEST(test_tcp_recv_ooseq_max_bytes)
/* create and initialize the pcb */
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
- tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
pcb->rcv_nxt = 0x8000;
/* don't 'recv' the first segment (1 byte) so that all other segments will be ooseq */
@@ -621,23 +697,21 @@ START_TEST(test_tcp_recv_ooseq_max_bytes)
EXPECT_OOSEQ(datalen2 == ((i-1) * TCP_MSS));
/* make sure the pcb is freed */
- EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
tcp_abort(pcb);
- EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
-#endif /* TCP_OOSEQ_MAX_BYTES && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) */
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
+#endif /* TCP_OOSEQ_MAX_BYTES && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) */
LWIP_UNUSED_ARG(_i);
}
END_TEST
START_TEST(test_tcp_recv_ooseq_max_pbufs)
{
-#if TCP_OOSEQ_MAX_PBUFS && (TCP_OOSEQ_MAX_PBUFS < ((TCP_WND / TCP_MSS) + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))
+#if TCP_OOSEQ_MAX_PBUFS && (TCP_OOSEQ_MAX_PBUFS < ((TCP_WND / TCP_MSS) + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))
int i;
struct test_tcp_counters counters;
struct tcp_pcb* pcb;
struct pbuf *p_ovr;
- ip_addr_t remote_ip, local_ip, netmask;
- u16_t remote_port = 0x100, local_port = 0x101;
struct netif netif;
int datalen = 0;
int datalen2;
@@ -647,11 +721,7 @@ START_TEST(test_tcp_recv_ooseq_max_pbufs)
}
/* initialize local vars */
- memset(&netif, 0, sizeof(netif));
- IP4_ADDR(&local_ip, 192, 168, 1, 1);
- IP4_ADDR(&remote_ip, 192, 168, 1, 2);
- IP4_ADDR(&netmask, 255, 255, 255, 0);
- test_tcp_init_netif(&netif, NULL, &local_ip, &netmask);
+ test_tcp_init_netif(&netif, NULL, &test_local_ip, &test_netmask);
/* initialize counter struct */
memset(&counters, 0, sizeof(counters));
counters.expected_data_len = TCP_WND;
@@ -660,7 +730,7 @@ START_TEST(test_tcp_recv_ooseq_max_pbufs)
/* create and initialize the pcb */
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
- tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
pcb->rcv_nxt = 0x8000;
/* don't 'recv' the first segment (1 byte) so that all other segments will be ooseq */
@@ -702,10 +772,10 @@ START_TEST(test_tcp_recv_ooseq_max_pbufs)
EXPECT_OOSEQ(datalen2 == (i-1));
/* make sure the pcb is freed */
- EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
tcp_abort(pcb);
- EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
-#endif /* TCP_OOSEQ_MAX_PBUFS && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) */
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
+#endif /* TCP_OOSEQ_MAX_PBUFS && (TCP_OOSEQ_MAX_BYTES < (TCP_WND + 1)) && (PBUF_POOL_BUFSIZE >= (TCP_MSS + PBUF_LINK_ENCAPSULATION_HLEN + PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN)) */
LWIP_UNUSED_ARG(_i);
}
END_TEST
@@ -739,23 +809,16 @@ static void test_tcp_recv_ooseq_double_FINs(int delay_packet)
struct test_tcp_counters counters;
struct tcp_pcb* pcb;
struct pbuf *p_normal_fin, *p_data_after_fin, *p, *p_2nd_fin_ooseq;
- ip_addr_t remote_ip, local_ip, netmask;
- u16_t remote_port = 0x100, local_port = 0x101;
struct netif netif;
u32_t exp_rx_calls = 0, exp_rx_bytes = 0, exp_close_calls = 0, exp_oos_pbufs = 0, exp_oos_tcplen = 0;
int first_dropped = 0xff;
- int last_dropped = 0;
- for(i = 0; i < sizeof(data_full_wnd); i++) {
+ for(i = 0; i < (int)sizeof(data_full_wnd); i++) {
data_full_wnd[i] = (char)i;
}
/* initialize local vars */
- memset(&netif, 0, sizeof(netif));
- IP4_ADDR(&local_ip, 192, 168, 1, 1);
- IP4_ADDR(&remote_ip, 192, 168, 1, 2);
- IP4_ADDR(&netmask, 255, 255, 255, 0);
- test_tcp_init_netif(&netif, NULL, &local_ip, &netmask);
+ test_tcp_init_netif(&netif, NULL, &test_local_ip, &test_netmask);
/* initialize counter struct */
memset(&counters, 0, sizeof(counters));
counters.expected_data_len = TCP_WND;
@@ -764,7 +827,7 @@ static void test_tcp_recv_ooseq_double_FINs(int delay_packet)
/* create and initialize the pcb */
pcb = test_tcp_new_counters_pcb(&counters);
EXPECT_RET(pcb != NULL);
- tcp_set_state(pcb, ESTABLISHED, &local_ip, &remote_ip, local_port, remote_port);
+ tcp_set_state(pcb, ESTABLISHED, &test_local_ip, &test_remote_ip, TEST_LOCAL_PORT, TEST_REMOTE_PORT);
pcb->rcv_nxt = 0x8000;
/* create segments */
@@ -777,7 +840,6 @@ static void test_tcp_recv_ooseq_double_FINs(int delay_packet)
if(delay_packet & 1) {
/* drop normal data */
first_dropped = 1;
- last_dropped = 1;
} else {
/* send normal data */
test_tcp_input(p, &netif);
@@ -792,7 +854,6 @@ static void test_tcp_recv_ooseq_double_FINs(int delay_packet)
if(first_dropped > 2) {
first_dropped = 2;
}
- last_dropped = 2;
} else {
/* send FIN */
test_tcp_input(p_normal_fin, &netif);
@@ -813,7 +874,6 @@ static void test_tcp_recv_ooseq_double_FINs(int delay_packet)
if(first_dropped > 3) {
first_dropped = 3;
}
- last_dropped = 3;
} else {
/* send data-after-FIN */
test_tcp_input(p_data_after_fin, &netif);
@@ -836,7 +896,6 @@ static void test_tcp_recv_ooseq_double_FINs(int delay_packet)
if(first_dropped > 4) {
first_dropped = 4;
}
- last_dropped = 4;
} else {
/* send 2nd-FIN */
test_tcp_input(p_2nd_fin_ooseq, &netif);
@@ -895,9 +954,9 @@ static void test_tcp_recv_ooseq_double_FINs(int delay_packet)
EXPECT(pcb->ooseq == NULL);
/* make sure the pcb is freed */
- EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 1);
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 1);
tcp_abort(pcb);
- EXPECT(lwip_stats.memp[MEMP_TCP_PCB].used == 0);
+ EXPECT(MEMP_STATS_GET(used, MEMP_TCP_PCB) == 0);
}
/** create multiple segments and pass them to tcp_input with the first segment missing
@@ -931,28 +990,29 @@ FIN_TEST(test_tcp_recv_ooseq_double_FIN_15, 15)
Suite *
tcp_oos_suite(void)
{
- TFun tests[] = {
- test_tcp_recv_ooseq_FIN_OOSEQ,
- test_tcp_recv_ooseq_FIN_INSEQ,
- test_tcp_recv_ooseq_overrun_rxwin,
- test_tcp_recv_ooseq_max_bytes,
- test_tcp_recv_ooseq_max_pbufs,
- test_tcp_recv_ooseq_double_FIN_0,
- test_tcp_recv_ooseq_double_FIN_1,
- test_tcp_recv_ooseq_double_FIN_2,
- test_tcp_recv_ooseq_double_FIN_3,
- test_tcp_recv_ooseq_double_FIN_4,
- test_tcp_recv_ooseq_double_FIN_5,
- test_tcp_recv_ooseq_double_FIN_6,
- test_tcp_recv_ooseq_double_FIN_7,
- test_tcp_recv_ooseq_double_FIN_8,
- test_tcp_recv_ooseq_double_FIN_9,
- test_tcp_recv_ooseq_double_FIN_10,
- test_tcp_recv_ooseq_double_FIN_11,
- test_tcp_recv_ooseq_double_FIN_12,
- test_tcp_recv_ooseq_double_FIN_13,
- test_tcp_recv_ooseq_double_FIN_14,
- test_tcp_recv_ooseq_double_FIN_15
+ testfunc tests[] = {
+ TESTFUNC(test_tcp_recv_ooseq_FIN_OOSEQ),
+ TESTFUNC(test_tcp_recv_ooseq_FIN_INSEQ),
+ TESTFUNC(test_tcp_recv_ooseq_overrun_rxwin),
+ TESTFUNC(test_tcp_recv_ooseq_overrun_rxwin_edge),
+ TESTFUNC(test_tcp_recv_ooseq_max_bytes),
+ TESTFUNC(test_tcp_recv_ooseq_max_pbufs),
+ TESTFUNC(test_tcp_recv_ooseq_double_FIN_0),
+ TESTFUNC(test_tcp_recv_ooseq_double_FIN_1),
+ TESTFUNC(test_tcp_recv_ooseq_double_FIN_2),
+ TESTFUNC(test_tcp_recv_ooseq_double_FIN_3),
+ TESTFUNC(test_tcp_recv_ooseq_double_FIN_4),
+ TESTFUNC(test_tcp_recv_ooseq_double_FIN_5),
+ TESTFUNC(test_tcp_recv_ooseq_double_FIN_6),
+ TESTFUNC(test_tcp_recv_ooseq_double_FIN_7),
+ TESTFUNC(test_tcp_recv_ooseq_double_FIN_8),
+ TESTFUNC(test_tcp_recv_ooseq_double_FIN_9),
+ TESTFUNC(test_tcp_recv_ooseq_double_FIN_10),
+ TESTFUNC(test_tcp_recv_ooseq_double_FIN_11),
+ TESTFUNC(test_tcp_recv_ooseq_double_FIN_12),
+ TESTFUNC(test_tcp_recv_ooseq_double_FIN_13),
+ TESTFUNC(test_tcp_recv_ooseq_double_FIN_14),
+ TESTFUNC(test_tcp_recv_ooseq_double_FIN_15)
};
- return create_suite("TCP_OOS", tests, sizeof(tests)/sizeof(TFun), tcp_oos_setup, tcp_oos_teardown);
+ return create_suite("TCP_OOS", tests, sizeof(tests)/sizeof(testfunc), tcp_oos_setup, tcp_oos_teardown);
}
diff --git a/lwip/test/unit/tcp/test_tcp_oos.h b/lwip/test/unit/tcp/test_tcp_oos.h
index 5e411f0..5b82013 100644
--- a/lwip/test/unit/tcp/test_tcp_oos.h
+++ b/lwip/test/unit/tcp/test_tcp_oos.h
@@ -1,5 +1,5 @@
-#ifndef __TEST_TCP_OOS_H__
-#define __TEST_TCP_OOS_H__
+#ifndef LWIP_HDR_TEST_TCP_OOS_H
+#define LWIP_HDR_TEST_TCP_OOS_H
#include "../lwip_check.h"
diff --git a/lwip/test/unit/udp/test_udp.c b/lwip/test/unit/udp/test_udp.c
index a2f02af..4ad693e 100644
--- a/lwip/test/unit/udp/test_udp.c
+++ b/lwip/test/unit/udp/test_udp.c
@@ -19,7 +19,7 @@ udp_remove_all(void)
pcb = pcb->next;
udp_remove(pcb2);
}
- fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 0);
+ fail_unless(MEMP_STATS_GET(used, MEMP_UDP_PCB) == 0);
}
/* Setups/teardown functions */
@@ -28,12 +28,14 @@ static void
udp_setup(void)
{
udp_remove_all();
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
static void
udp_teardown(void)
{
udp_remove_all();
+ lwip_check_ensure_no_alloc(SKIP_POOL(MEMP_SYS_TIMEOUT));
}
@@ -44,14 +46,14 @@ START_TEST(test_udp_new_remove)
struct udp_pcb* pcb;
LWIP_UNUSED_ARG(_i);
- fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 0);
+ fail_unless(MEMP_STATS_GET(used, MEMP_UDP_PCB) == 0);
pcb = udp_new();
fail_unless(pcb != NULL);
if (pcb != NULL) {
- fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 1);
+ fail_unless(MEMP_STATS_GET(used, MEMP_UDP_PCB) == 1);
udp_remove(pcb);
- fail_unless(lwip_stats.memp[MEMP_UDP_PCB].used == 0);
+ fail_unless(MEMP_STATS_GET(used, MEMP_UDP_PCB) == 0);
}
}
END_TEST
@@ -61,8 +63,8 @@ END_TEST
Suite *
udp_suite(void)
{
- TFun tests[] = {
- test_udp_new_remove,
+ testfunc tests[] = {
+ TESTFUNC(test_udp_new_remove),
};
- return create_suite("UDP", tests, sizeof(tests)/sizeof(TFun), udp_setup, udp_teardown);
+ return create_suite("UDP", tests, sizeof(tests)/sizeof(testfunc), udp_setup, udp_teardown);
}
diff --git a/lwip/test/unit/udp/test_udp.h b/lwip/test/unit/udp/test_udp.h
index 9335368..5426bf4 100644
--- a/lwip/test/unit/udp/test_udp.h
+++ b/lwip/test/unit/udp/test_udp.h
@@ -1,5 +1,5 @@
-#ifndef __TEST_UDP_H__
-#define __TEST_UDP_H__
+#ifndef LWIP_HDR_TEST_UDP_H
+#define LWIP_HDR_TEST_UDP_H
#include "../lwip_check.h"
diff --git a/tun2socks/tun2socks.c b/tun2socks/tun2socks.c
index 748b8c5..9541bba 100644
--- a/tun2socks/tun2socks.c
+++ b/tun2socks/tun2socks.c
@@ -57,7 +57,8 @@
#include <socksclient/BSocksClient.h>
#include <tuntap/BTap.h>
#include <lwip/init.h>
-#include <lwip/tcp_impl.h>
+#include <lwip/ip_addr.h>
+#include <lwip/priv/tcp_priv.h>
#include <lwip/netif.h>
#include <lwip/tcp.h>
#include <tun2socks/SocksUdpGwClient.h>
@@ -115,8 +116,8 @@ struct {
// TCP client
struct tcp_client {
- dead_t dead;
- dead_t dead_client;
+ int aborted;
+ dead_t dead_aborted;
LinkedList1Node list_node;
BAddr local_addr;
BAddr remote_addr;
@@ -207,15 +208,15 @@ static void print_version (void);
static int parse_arguments (int argc, char *argv[]);
static int process_arguments (void);
static void signal_handler (void *unused);
-static BAddr baddr_from_lwip (int is_ipv6, const ipX_addr_t *ipx_addr, uint16_t port_hostorder);
+static BAddr baddr_from_lwip (const ip_addr_t *ip_addr, uint16_t port_hostorder);
static void lwip_init_job_hadler (void *unused);
static void tcp_timer_handler (void *unused);
static void device_error_handler (void *unused);
static void device_read_handler_send (void *unused, uint8_t *data, int data_len);
static int process_device_udp_packet (uint8_t *data, int data_len);
static err_t netif_init_func (struct netif *netif);
-static err_t netif_output_func (struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr);
-static err_t netif_output_ip6_func (struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr);
+static err_t netif_output_func (struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr);
+static err_t netif_output_ip6_func (struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr);
static err_t common_netif_output (struct netif *netif, struct pbuf *p);
static err_t netif_input_func (struct pbuf *p, struct netif *inp);
static void client_logfunc (struct tcp_client *client);
@@ -224,6 +225,7 @@ static err_t listener_accept_func (void *arg, struct tcp_pcb *newpcb, err_t err)
static void client_handle_freed_client (struct tcp_client *client);
static void client_free_client (struct tcp_client *client);
static void client_abort_client (struct tcp_client *client);
+static void client_abort_pcb (struct tcp_client *client);
static void client_free_socks (struct tcp_client *client);
static void client_murder (struct tcp_client *client);
static void client_dealloc (struct tcp_client *client);
@@ -835,13 +837,13 @@ void signal_handler (void *unused)
terminate();
}
-BAddr baddr_from_lwip (int is_ipv6, const ipX_addr_t *ipx_addr, uint16_t port_hostorder)
+BAddr baddr_from_lwip (const ip_addr_t *ip_addr, uint16_t port_hostorder)
{
BAddr addr;
- if (is_ipv6) {
- BAddr_InitIPv6(&addr, (uint8_t *)ipx_addr->ip6.addr, hton16(port_hostorder));
+ if (IP_IS_V6(ip_addr)) {
+ BAddr_InitIPv6(&addr, (uint8_t *)ip_addr->u_addr.ip6.addr, hton16(port_hostorder));
} else {
- BAddr_InitIPv4(&addr, ipx_addr->ip4.addr, hton16(port_hostorder));
+ BAddr_InitIPv4(&addr, ip_addr->u_addr.ip4.addr, hton16(port_hostorder));
}
return addr;
}
@@ -864,12 +866,12 @@ void lwip_init_job_hadler (void *unused)
lwip_init();
// make addresses for netif
- ip_addr_t addr;
+ ip4_addr_t addr;
addr.addr = netif_ipaddr.ipv4;
- ip_addr_t netmask;
+ ip4_addr_t netmask;
netmask.addr = netif_netmask.ipv4;
- ip_addr_t gw;
- ip_addr_set_any(&gw);
+ ip4_addr_t gw;
+ ip4_addr_set_any(&gw);
// init netif
if (!netif_add(&the_netif, &addr, &netmask, &gw, NULL, netif_init_func, netif_input_func)) {
@@ -881,6 +883,9 @@ void lwip_init_job_hadler (void *unused)
// set netif up
netif_set_up(&the_netif);
+ // set netif link up, otherwise ip route will refuse to route
+ netif_set_link_up(&the_netif);
+
// set netif pretend TCP
netif_set_pretend_tcp(&the_netif, 1);
@@ -889,14 +894,17 @@ void lwip_init_job_hadler (void *unused)
if (options.netif_ip6addr) {
// add IPv6 address
- memcpy(netif_ip6_addr(&the_netif, 0), netif_ip6addr.bytes, sizeof(netif_ip6addr.bytes));
+ ip6_addr_t ip6addr;
+ memset(&ip6addr, 0, sizeof(ip6addr)); // clears any "zone"
+ memcpy(ip6addr.addr, netif_ip6addr.bytes, sizeof(netif_ip6addr.bytes));
+ netif_ip6_addr_set(&the_netif, 0, &ip6addr);
netif_ip6_addr_set_state(&the_netif, 0, IP6_ADDR_VALID);
}
// init listener
- struct tcp_pcb *l = tcp_new();
+ struct tcp_pcb *l = tcp_new_ip_type(IPADDR_TYPE_V4);
if (!l) {
- BLog(BLOG_ERROR, "tcp_new failed");
+ BLog(BLOG_ERROR, "tcp_new_ip_type failed");
goto fail;
}
@@ -907,6 +915,9 @@ void lwip_init_job_hadler (void *unused)
goto fail;
}
+ // ensure the listener only accepts connections from this netif
+ tcp_bind_netif(l, &the_netif);
+
// listen listener
if (!(listener = tcp_listen(l))) {
BLog(BLOG_ERROR, "tcp_listen failed");
@@ -918,9 +929,9 @@ void lwip_init_job_hadler (void *unused)
tcp_accept(listener, listener_accept_func);
if (options.netif_ip6addr) {
- struct tcp_pcb *l_ip6 = tcp_new_ip6();
+ struct tcp_pcb *l_ip6 = tcp_new_ip_type(IPADDR_TYPE_V6);
if (!l_ip6) {
- BLog(BLOG_ERROR, "tcp_new_ip6 failed");
+ BLog(BLOG_ERROR, "tcp_new_ip_type failed");
goto fail;
}
@@ -930,6 +941,8 @@ void lwip_init_job_hadler (void *unused)
goto fail;
}
+ tcp_bind_netif(l_ip6, &the_netif);
+
if (!(listener_ip6 = tcp_listen(l_ip6))) {
BLog(BLOG_ERROR, "tcp_listen failed");
tcp_close(l_ip6);
@@ -954,11 +967,9 @@ void tcp_timer_handler (void *unused)
BLog(BLOG_DEBUG, "TCP timer");
// schedule next timer
- // TODO: calculate timeout so we don't drift
BReactor_SetTimer(&ss, &tcp_timer);
tcp_tmr();
- return;
}
void device_error_handler (void *unused)
@@ -1138,12 +1149,12 @@ err_t netif_init_func (struct netif *netif)
return ERR_OK;
}
-err_t netif_output_func (struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr)
+err_t netif_output_func (struct netif *netif, struct pbuf *p, const ip4_addr_t *ipaddr)
{
return common_netif_output(netif, p);
}
-err_t netif_output_ip6_func (struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr)
+err_t netif_output_ip6_func (struct netif *netif, struct pbuf *p, const ip6_addr_t *ipaddr)
{
return common_netif_output(netif, p);
}
@@ -1232,10 +1243,6 @@ err_t listener_accept_func (void *arg, struct tcp_pcb *newpcb, err_t err)
{
ASSERT(err == ERR_OK)
- // signal accepted
- struct tcp_pcb *this_listener = (PCB_ISIPV6(newpcb) ? listener_ip6 : listener);
- tcp_accepted(this_listener);
-
// allocate client structure
struct tcp_client *client = (struct tcp_client *)malloc(sizeof(*client));
if (!client) {
@@ -1248,8 +1255,8 @@ err_t listener_accept_func (void *arg, struct tcp_pcb *newpcb, err_t err)
SYNC_FROMHERE
// read addresses
- client->local_addr = baddr_from_lwip(PCB_ISIPV6(newpcb), &newpcb->local_ip, newpcb->local_port);
- client->remote_addr = baddr_from_lwip(PCB_ISIPV6(newpcb), &newpcb->remote_ip, newpcb->remote_port);
+ client->local_addr = baddr_from_lwip(&newpcb->local_ip, newpcb->local_port);
+ client->remote_addr = baddr_from_lwip(&newpcb->remote_ip, newpcb->remote_port);
// get destination address
BAddr addr = client->local_addr;
@@ -1276,9 +1283,9 @@ err_t listener_accept_func (void *arg, struct tcp_pcb *newpcb, err_t err)
goto fail1;
}
- // init dead vars
- DEAD_INIT(client->dead);
- DEAD_INIT(client->dead_client);
+ // init aborted and dead_aborted
+ client->aborted = 0;
+ DEAD_INIT(client->dead_aborted);
// add to linked list
LinkedList1_Append(&tcp_clients, &client->list_node);
@@ -1309,14 +1316,12 @@ err_t listener_accept_func (void *arg, struct tcp_pcb *newpcb, err_t err)
client_log(client, BLOG_INFO, "accepted");
- DEAD_ENTER(client->dead_client)
+ DEAD_ENTER(client->dead_aborted)
SYNC_COMMIT
- DEAD_LEAVE2(client->dead_client)
- if (DEAD_KILLED) {
- return ERR_ABRT;
- }
+ DEAD_LEAVE2(client->dead_aborted)
- return ERR_OK;
+ // Return ERR_ABRT if and only if tcp_abort was called from this callback.
+ return (DEAD_KILLED > 0) ? ERR_ABRT : ERR_OK;
fail1:
SYNC_BREAK
@@ -1332,9 +1337,6 @@ void client_handle_freed_client (struct tcp_client *client)
// pcb was taken care of by the caller
- // kill client dead var
- DEAD_KILL(client->dead_client);
-
// set client closed
client->client_closed = 1;
@@ -1363,7 +1365,7 @@ void client_free_client (struct tcp_client *client)
err_t err = tcp_close(client->pcb);
if (err != ERR_OK) {
client_log(client, BLOG_ERROR, "tcp_close failed (%d)", err);
- tcp_abort(client->pcb);
+ client_abort_pcb(client);
}
client_handle_freed_client(client);
@@ -1378,12 +1380,28 @@ void client_abort_client (struct tcp_client *client)
tcp_recv(client->pcb, NULL);
tcp_sent(client->pcb, NULL);
- // free pcb
- tcp_abort(client->pcb);
+ // abort
+ client_abort_pcb(client);
client_handle_freed_client(client);
}
+void client_abort_pcb (struct tcp_client *client)
+{
+ ASSERT(!client->aborted)
+
+ // abort the PCB
+ tcp_abort(client->pcb);
+
+ // mark aborted
+ client->aborted = 1;
+
+ // kill dead_aborted with value 1 signaling that tcp_abort was done;
+ // this is contrasted to killing with value -1 from client_dealloc
+ // signaling that the client was freed without tcp_abort
+ DEAD_KILL_WITH(client->dead_aborted, 1);
+}
+
void client_free_socks (struct tcp_client *client)
{
ASSERT(!client->socks_closed)
@@ -1424,10 +1442,7 @@ void client_murder (struct tcp_client *client)
tcp_sent(client->pcb, NULL);
// abort
- tcp_abort(client->pcb);
-
- // kill client dead var
- DEAD_KILL(client->dead_client);
+ client_abort_pcb(client);
// set client closed
client->client_closed = 1;
@@ -1458,8 +1473,10 @@ void client_dealloc (struct tcp_client *client)
// remove client entry
LinkedList1_Remove(&tcp_clients, &client->list_node);
- // kill dead var
- DEAD_KILL(client->dead);
+ // kill dead_aborted with value -1 unless already aborted
+ if (!client->aborted) {
+ DEAD_KILL_WITH(client->dead_aborted, -1);
+ }
// free memory
free(client->socks_username);
@@ -1484,43 +1501,44 @@ err_t client_recv_func (void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t e
ASSERT(err == ERR_OK) // checked in lwIP source. Otherwise, I've no idea what should
// be done with the pbuf in case of an error.
+ DEAD_ENTER(client->dead_aborted)
+
if (!p) {
client_log(client, BLOG_INFO, "client closed");
client_free_client(client);
- return ERR_ABRT;
- }
-
- ASSERT(p->tot_len > 0)
-
- // check if we have enough buffer
- if (p->tot_len > sizeof(client->buf) - client->buf_used) {
- client_log(client, BLOG_ERROR, "no buffer for data !?!");
- return ERR_MEM;
- }
-
- // copy data to buffer
- ASSERT_EXECUTE(pbuf_copy_partial(p, client->buf + client->buf_used, p->tot_len, 0) == p->tot_len)
- client->buf_used += p->tot_len;
-
- // if there was nothing in the buffer before, and SOCKS is up, start send data
- if (client->buf_used == p->tot_len && client->socks_up) {
- ASSERT(!client->socks_closed) // this callback is removed when SOCKS is closed
+ } else {
+ ASSERT(p->tot_len > 0)
- SYNC_DECL
- SYNC_FROMHERE
- client_send_to_socks(client);
- DEAD_ENTER(client->dead_client)
- SYNC_COMMIT
- DEAD_LEAVE2(client->dead_client)
- if (DEAD_KILLED) {
- return ERR_ABRT;
+ // check if we have enough buffer
+ if (p->tot_len > sizeof(client->buf) - client->buf_used) {
+ client_log(client, BLOG_ERROR, "no buffer for data !?!");
+ DEAD_LEAVE2(client->dead_aborted)
+ return ERR_MEM;
+ }
+
+ // copy data to buffer
+ ASSERT_EXECUTE(pbuf_copy_partial(p, client->buf + client->buf_used, p->tot_len, 0) == p->tot_len)
+ client->buf_used += p->tot_len;
+
+ // free pbuff
+ int p_tot_len = p->tot_len;
+ pbuf_free(p);
+
+ // if there was nothing in the buffer before, and SOCKS is up, start send data
+ if (client->buf_used == p_tot_len && client->socks_up) {
+ ASSERT(!client->socks_closed) // this callback is removed when SOCKS is closed
+
+ SYNC_DECL
+ SYNC_FROMHERE
+ client_send_to_socks(client);
+ SYNC_COMMIT
}
}
- // free pbuff
- pbuf_free(p);
+ DEAD_LEAVE2(client->dead_aborted)
- return ERR_OK;
+ // Return ERR_ABRT if and only if tcp_abort was called from this callback.
+ return (DEAD_KILLED > 0) ? ERR_ABRT : ERR_OK;
}
void client_socks_handler (struct tcp_client *client, int event)
@@ -1728,6 +1746,8 @@ err_t client_sent_func (void *arg, struct tcp_pcb *tpcb, u16_t len)
ASSERT(len > 0)
ASSERT(len <= client->socks_recv_tcp_pending)
+ DEAD_ENTER(client->dead_aborted)
+
// decrement pending
client->socks_recv_tcp_pending -= len;
@@ -1741,7 +1761,7 @@ err_t client_sent_func (void *arg, struct tcp_pcb *tpcb, u16_t len)
// possibly send more data
if (client_socks_recv_send_out(client) < 0) {
- return ERR_ABRT;
+ goto out;
}
// we just queued some data, so it can't have been confirmed yet
@@ -1752,25 +1772,21 @@ err_t client_sent_func (void *arg, struct tcp_pcb *tpcb, u16_t len)
SYNC_DECL
SYNC_FROMHERE
client_socks_recv_initiate(client);
- DEAD_ENTER(client->dead_client)
SYNC_COMMIT
- DEAD_LEAVE2(client->dead_client)
- if (DEAD_KILLED) {
- return ERR_ABRT;
- }
}
-
- return ERR_OK;
+ } else {
+ // have we sent everything after SOCKS was closed?
+ if (client->socks_closed && client->socks_recv_tcp_pending == 0) {
+ client_log(client, BLOG_INFO, "removing after SOCKS went down");
+ client_free_client(client);
+ }
}
- // have we sent everything after SOCKS was closed?
- if (client->socks_closed && client->socks_recv_tcp_pending == 0) {
- client_log(client, BLOG_INFO, "removing after SOCKS went down");
- client_free_client(client);
- return ERR_ABRT;
- }
+out:
+ DEAD_LEAVE2(client->dead_aborted)
- return ERR_OK;
+ // Return ERR_ABRT if and only if tcp_abort was called from this callback.
+ return (DEAD_KILLED > 0) ? ERR_ABRT : ERR_OK;
}
void udpgw_client_handler_received (void *unused, BAddr local_addr, BAddr remote_addr, const uint8_t *data, int data_len)