summaryrefslogtreecommitdiffstats
path: root/src/VBox/Devices/Network
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-11 08:17:27 +0000
commitf215e02bf85f68d3a6106c2a1f4f7f063f819064 (patch)
tree6bb5b92c046312c4e95ac2620b10ddf482d3fa8b /src/VBox/Devices/Network
parentInitial commit. (diff)
downloadvirtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.tar.xz
virtualbox-f215e02bf85f68d3a6106c2a1f4f7f063f819064.zip
Adding upstream version 7.0.14-dfsg.upstream/7.0.14-dfsg
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/VBox/Devices/Network')
-rw-r--r--src/VBox/Devices/Network/.scm-settings57
-rw-r--r--src/VBox/Devices/Network/Dev3C501.cpp2721
-rw-r--r--src/VBox/Devices/Network/DevDP8390.cpp5500
-rw-r--r--src/VBox/Devices/Network/DevE1000.cpp8510
-rw-r--r--src/VBox/Devices/Network/DevE1000Phy.cpp640
-rw-r--r--src/VBox/Devices/Network/DevE1000Phy.h153
-rw-r--r--src/VBox/Devices/Network/DevEEPROM.cpp302
-rw-r--r--src/VBox/Devices/Network/DevEEPROM.h151
-rw-r--r--src/VBox/Devices/Network/DevINIP.cpp773
-rw-r--r--src/VBox/Devices/Network/DevPCNet.cpp5437
-rw-r--r--src/VBox/Devices/Network/DevVirtioNet.cpp3772
-rw-r--r--src/VBox/Devices/Network/DrvCloudTunnel.cpp1825
-rw-r--r--src/VBox/Devices/Network/DrvDedicatedNic.cpp577
-rw-r--r--src/VBox/Devices/Network/DrvIntNet.cpp2140
-rw-r--r--src/VBox/Devices/Network/DrvNAT.cpp1915
-rw-r--r--src/VBox/Devices/Network/DrvNetShaper.cpp641
-rw-r--r--src/VBox/Devices/Network/DrvNetSniffer.cpp575
-rw-r--r--src/VBox/Devices/Network/DrvTAP.cpp1075
-rw-r--r--src/VBox/Devices/Network/DrvUDPTunnel.cpp673
-rw-r--r--src/VBox/Devices/Network/DrvVDE.cpp695
-rw-r--r--src/VBox/Devices/Network/DrvVMNet.m747
-rw-r--r--src/VBox/Devices/Network/Makefile.kup0
-rw-r--r--src/VBox/Devices/Network/Pcap.cpp267
-rw-r--r--src/VBox/Devices/Network/Pcap.h52
-rw-r--r--src/VBox/Devices/Network/SrvIntNetR0.cpp6903
-rw-r--r--src/VBox/Devices/Network/VBoxLibSsh.def54
-rw-r--r--src/VBox/Devices/Network/VDEPlug.cpp39
-rw-r--r--src/VBox/Devices/Network/lwip-new/CHANGELOG3452
-rw-r--r--src/VBox/Devices/Network/lwip-new/COPYING33
-rw-r--r--src/VBox/Devices/Network/lwip-new/Config.kmk107
-rw-r--r--src/VBox/Devices/Network/lwip-new/FILES4
-rw-r--r--src/VBox/Devices/Network/lwip-new/Makefile.kup0
-rw-r--r--src/VBox/Devices/Network/lwip-new/README89
-rw-r--r--src/VBox/Devices/Network/lwip-new/UPGRADING144
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/Makefile.kup0
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/api/Makefile.kup0
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/api/api_lib.c788
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/api/api_msg.c1611
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/api/err.c75
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/api/netbuf.c245
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/api/netdb.c348
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/api/netifapi.c160
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/api/sockets.c2555
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/api/tcpip.c531
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/Makefile.kup0
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/def.c108
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/dhcp.c1771
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/dns.c988
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/inet_chksum.c545
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/init.c345
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv4/Makefile.kup0
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv4/autoip.c528
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv4/icmp.c451
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv4/igmp.c814
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv4/ip4.c1027
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv4/ip4_addr.c312
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv4/ip_frag.c885
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv6/Makefile.kup0
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv6/README1
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv6/dhcp6.c50
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv6/ethip6.c193
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv6/icmp6.c410
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv6/inet6.c51
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv6/ip6.c1182
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv6/ip6_addr.c251
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv6/ip6_frag.c666
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv6/mld6.c589
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/ipv6/nd6.c1845
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/mem.c659
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/memp.c543
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/netif.c943
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/pbuf.c1180
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/raw.c422
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/snmp/Makefile.kup0
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/snmp/asn1_dec.c657
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/snmp/asn1_enc.c611
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/snmp/mib2.c4146
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/snmp/mib_structs.c1174
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/snmp/msg_in.c1453
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/snmp/msg_out.c678
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/stats.c181
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/sys.c68
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/tcp.c1926
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/tcp_in.c1938
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/tcp_out.c1505
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/timers.c606
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/core/udp.c1410
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/autoip.h121
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/icmp.h146
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/igmp.h106
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/inet.h121
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/ip4.h151
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/ip4_addr.h247
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/ip_frag.h91
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/dhcp6.h58
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/ethip6.h68
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/icmp6.h171
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/inet6.h92
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/ip6.h201
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/ip6_addr.h289
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/ip6_frag.h105
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/mld6.h120
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/nd6.h367
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/api.h338
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/api_msg.h177
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/arch.h217
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/debug.h105
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/def.h123
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/dhcp.h242
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/dns.h124
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/err.h85
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/inet_chksum.h112
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/init.h72
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/ip.h262
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/ip_addr.h130
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/mem.h123
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/memp.h116
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/memp_std.h135
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/netbuf.h112
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/netdb.h124
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/netif.h387
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/netifapi.h111
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/opt.h2454
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/pbuf.h185
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/raw.h131
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/sio.h141
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/snmp.h367
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/snmp_asn1.h101
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/snmp_msg.h315
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/snmp_structs.h268
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/sockets.h434
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/stats.h347
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/sys.h336
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/tcp.h432
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/tcp_impl.h520
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/tcpip.h186
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/timers.h100
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/lwip/udp.h242
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/netif/etharp.h228
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/netif/ppp_oe.h190
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/netif/slipif.h81
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/posix/netdb.h33
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/include/posix/sys/socket.h33
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/FILES29
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/etharp.c1440
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ethernetif.c322
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/auth.c1334
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/auth.h111
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/chap.c908
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/chap.h150
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/chpms.c396
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/chpms.h64
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/fsm.c890
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/fsm.h157
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/ipcp.c1411
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/ipcp.h106
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/lcp.c2066
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/lcp.h151
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/magic.c80
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/magic.h63
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/md5.c320
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/md5.h55
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/pap.c628
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/pap.h118
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/ppp.c2052
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/ppp.h201
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/ppp_impl.h363
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/ppp_oe.c1132
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/pppdebug.h73
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/randm.c249
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/randm.h81
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/readme.txt21
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/vj.c652
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/ppp/vj.h156
-rw-r--r--src/VBox/Devices/Network/lwip-new/src/netif/slipif.c546
-rw-r--r--src/VBox/Devices/Network/lwip-new/vbox/Makefile.kup0
-rw-r--r--src/VBox/Devices/Network/lwip-new/vbox/VBoxLwipCore.cpp230
-rw-r--r--src/VBox/Devices/Network/lwip-new/vbox/VBoxLwipCore.h41
-rw-r--r--src/VBox/Devices/Network/lwip-new/vbox/include/arch/bpstruct.h1
-rw-r--r--src/VBox/Devices/Network/lwip-new/vbox/include/arch/cc.h62
-rw-r--r--src/VBox/Devices/Network/lwip-new/vbox/include/arch/epstruct.h1
-rw-r--r--src/VBox/Devices/Network/lwip-new/vbox/include/arch/perf.h7
-rw-r--r--src/VBox/Devices/Network/lwip-new/vbox/include/arch/sys_arch.h39
-rw-r--r--src/VBox/Devices/Network/lwip-new/vbox/include/lwip-log.h104
-rw-r--r--src/VBox/Devices/Network/lwip-new/vbox/include/lwip-namespace.h227
-rw-r--r--src/VBox/Devices/Network/lwip-new/vbox/sys_arch.c583
-rw-r--r--src/VBox/Devices/Network/lwipopts.h186
-rwxr-xr-xsrc/VBox/Devices/Network/scripts/VBoxConvertNATStats.sh123
-rwxr-xr-xsrc/VBox/Devices/Network/scripts/VBoxPortForwarding.py149
-rw-r--r--src/VBox/Devices/Network/slirp/COPYRIGHT64
-rw-r--r--src/VBox/Devices/Network/slirp/Makefile.kup0
-rw-r--r--src/VBox/Devices/Network/slirp/bootp.c969
-rw-r--r--src/VBox/Devices/Network/slirp/bootp.h158
-rw-r--r--src/VBox/Devices/Network/slirp/bsd/Makefile.kup0
-rw-r--r--src/VBox/Devices/Network/slirp/bsd/amd64/Makefile.kup0
-rw-r--r--src/VBox/Devices/Network/slirp/bsd/amd64/in_cksum.c242
-rw-r--r--src/VBox/Devices/Network/slirp/bsd/amd64/include/in_cksum.h84
-rw-r--r--src/VBox/Devices/Network/slirp/bsd/arm64/Makefile.kup0
-rw-r--r--src/VBox/Devices/Network/slirp/bsd/arm64/in_cksum.c242
-rw-r--r--src/VBox/Devices/Network/slirp/bsd/arm64/include/in_cksum.h84
-rw-r--r--src/VBox/Devices/Network/slirp/bsd/i386/Makefile.kup0
-rw-r--r--src/VBox/Devices/Network/slirp/bsd/i386/in_cksum.c499
-rw-r--r--src/VBox/Devices/Network/slirp/bsd/i386/include/in_cksum.h145
-rw-r--r--src/VBox/Devices/Network/slirp/bsd/kern/Makefile.kup0
-rw-r--r--src/VBox/Devices/Network/slirp/bsd/kern/kern_mbuf.c824
-rw-r--r--src/VBox/Devices/Network/slirp/bsd/kern/subr_sbuf.c594
-rw-r--r--src/VBox/Devices/Network/slirp/bsd/kern/uipc_mbuf.c2238
-rw-r--r--src/VBox/Devices/Network/slirp/bsd/kern/uipc_mbuf2.c539
-rw-r--r--src/VBox/Devices/Network/slirp/bsd/sys/mbuf.h1177
-rw-r--r--src/VBox/Devices/Network/slirp/bsd/sys/sbuf.h95
-rw-r--r--src/VBox/Devices/Network/slirp/cksum.c174
-rw-r--r--src/VBox/Devices/Network/slirp/counters.h149
-rw-r--r--src/VBox/Devices/Network/slirp/ctl.h52
-rw-r--r--src/VBox/Devices/Network/slirp/debug.c679
-rw-r--r--src/VBox/Devices/Network/slirp/debug.h77
-rw-r--r--src/VBox/Devices/Network/slirp/dnsproxy/dnsproxy.c764
-rw-r--r--src/VBox/Devices/Network/slirp/dnsproxy/dnsproxy.h157
-rw-r--r--src/VBox/Devices/Network/slirp/dnsproxy/hash.c77
-rw-r--r--src/VBox/Devices/Network/slirp/ext.h105
-rw-r--r--src/VBox/Devices/Network/slirp/hostres.c1529
-rw-r--r--src/VBox/Devices/Network/slirp/icmp_var.h93
-rw-r--r--src/VBox/Devices/Network/slirp/if.h61
-rw-r--r--src/VBox/Devices/Network/slirp/ip.h285
-rw-r--r--src/VBox/Devices/Network/slirp/ip_icmp.c795
-rw-r--r--src/VBox/Devices/Network/slirp/ip_icmp.h220
-rw-r--r--src/VBox/Devices/Network/slirp/ip_icmpwin.c558
-rw-r--r--src/VBox/Devices/Network/slirp/ip_input.c693
-rw-r--r--src/VBox/Devices/Network/slirp/ip_output.c354
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/HISTORY145
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/Makefile.kup0
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/alias.c1758
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/alias.h304
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/alias_cuseeme.c228
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/alias_db.c2966
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/alias_dummy.c153
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/alias_ftp.c773
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/alias_irc.c485
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/alias_local.h370
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/alias_mod.c347
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/alias_mod.h169
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/alias_nbt.c955
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/alias_old.c216
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/alias_pptp.c523
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/alias_proxy.c999
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/alias_skinny.c447
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/alias_smedia.c547
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/alias_util.c182
-rw-r--r--src/VBox/Devices/Network/slirp/libalias/libalias.31458
-rw-r--r--src/VBox/Devices/Network/slirp/libslirp.h200
-rw-r--r--src/VBox/Devices/Network/slirp/main.h41
-rw-r--r--src/VBox/Devices/Network/slirp/misc.c560
-rw-r--r--src/VBox/Devices/Network/slirp/misc.h88
-rw-r--r--src/VBox/Devices/Network/slirp/queue.h653
-rw-r--r--src/VBox/Devices/Network/slirp/resolv_conf_parser.c521
-rw-r--r--src/VBox/Devices/Network/slirp/resolv_conf_parser.h135
-rw-r--r--src/VBox/Devices/Network/slirp/sbuf.c292
-rw-r--r--src/VBox/Devices/Network/slirp/sbuf.h63
-rw-r--r--src/VBox/Devices/Network/slirp/slirp.c2086
-rw-r--r--src/VBox/Devices/Network/slirp/slirp.h555
-rw-r--r--src/VBox/Devices/Network/slirp/slirp_config.h225
-rw-r--r--src/VBox/Devices/Network/slirp/slirp_dns.c323
-rw-r--r--src/VBox/Devices/Network/slirp/slirp_dns.h32
-rw-r--r--src/VBox/Devices/Network/slirp/slirp_state.h548
-rw-r--r--src/VBox/Devices/Network/slirp/socket.c1497
-rw-r--r--src/VBox/Devices/Network/slirp/socket.h205
-rw-r--r--src/VBox/Devices/Network/slirp/tcp.h212
-rw-r--r--src/VBox/Devices/Network/slirp/tcp_input.c2060
-rw-r--r--src/VBox/Devices/Network/slirp/tcp_output.c739
-rw-r--r--src/VBox/Devices/Network/slirp/tcp_subr.c654
-rw-r--r--src/VBox/Devices/Network/slirp/tcp_timer.c361
-rw-r--r--src/VBox/Devices/Network/slirp/tcp_timer.h160
-rw-r--r--src/VBox/Devices/Network/slirp/tcp_var.h269
-rw-r--r--src/VBox/Devices/Network/slirp/tcpip.h102
-rw-r--r--src/VBox/Devices/Network/slirp/tftp.c931
-rw-r--r--src/VBox/Devices/Network/slirp/tftp.h63
-rw-r--r--src/VBox/Devices/Network/slirp/udp.c685
-rw-r--r--src/VBox/Devices/Network/slirp/udp.h142
-rw-r--r--src/VBox/Devices/Network/slirp/zone.h64
-rw-r--r--src/VBox/Devices/Network/testcase/CppUnitEmulation.h71
-rw-r--r--src/VBox/Devices/Network/testcase/Makefile.kup0
-rw-r--r--src/VBox/Devices/Network/testcase/tstDevEEPROM.cpp550
-rw-r--r--src/VBox/Devices/Network/testcase/tstDevPhy.cpp192
-rw-r--r--src/VBox/Devices/Network/testcase/tstIntNet-1.cpp1030
-rw-r--r--src/VBox/Devices/Network/testcase/tstIntNetR0.cpp867
284 files changed, 165176 insertions, 0 deletions
diff --git a/src/VBox/Devices/Network/.scm-settings b/src/VBox/Devices/Network/.scm-settings
new file mode 100644
index 00000000..a423d740
--- /dev/null
+++ b/src/VBox/Devices/Network/.scm-settings
@@ -0,0 +1,57 @@
+# $Id: .scm-settings $
+## @file
+# Source code massager settings for network devices/drivers.
+#
+
+#
+# Copyright (C) 2017-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+
+--filter-out-dirs /lwip-new/src/*
+--filter-out-dirs /lwip-new/test/*
+--filter-out-dirs /lwip-new/doc/*
+--filter-out-dirs /lwip-new/vbox/include/*
+
+--filter-out-files /lwip-new/CHANGELOG
+--filter-out-files /lwip-new/FILES
+--filter-out-files /lwip-new/README
+--filter-out-files /lwip-new/README.vbox
+--filter-out-files /lwip-new/UPGRADING
+--filter-out-files /slirp/libalias/HISTORY
+--filter-out-files /slirp/libalias/*.3
+--filter-out-files /slirp/COPYRIGHT
+
+
+# fun
+/slirp/dnsproxy/dnsproxy.h: --external-copyright --no-convert-tabs
+/slirp/dnsproxy/hash.c: --external-copyright --no-convert-tabs
+/slirp/libalias/*: --external-copyright --no-convert-tabs
+/slirp/bsd/*: --external-copyright --no-convert-tabs --dont-set-svn-keywords
+/slirp/*: --no-fix-header-guards
+
+# Not UTF-8 sources with latin-1/whatever chars in them (mostly names):
+/slirp/bsd/kern/subr_sbuf.c: --skip-unicode-checks
+/slirp/bsd/sys/sbuf.h: --skip-unicode-checks
+
+# well...
+/lwipopts.h: --external-copyright
+
diff --git a/src/VBox/Devices/Network/Dev3C501.cpp b/src/VBox/Devices/Network/Dev3C501.cpp
new file mode 100644
index 00000000..e6e141f1
--- /dev/null
+++ b/src/VBox/Devices/Network/Dev3C501.cpp
@@ -0,0 +1,2721 @@
+/* $Id: Dev3C501.cpp $ */
+/** @file
+ * Dev3C501 - 3Com EtherLink (3C501) Ethernet Adapter Emulation.
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/** @page pg_dev_3c501 3Com 3C501 Ethernet Controller Emulation.
+ *
+ * This software was written based on the following documents:
+ *
+ * - 3Com IBM Ethernet (IE) Controller/Transceiver
+ * External Reference Specification, March 15, 1983
+ * - 3Com EtherLink (3C501) Adapter Technical Reference
+ * Manual Part No. 6405-00, November 26, 1988
+ * - SEEQ 8001 EDLC Ethernet Data Link Controller
+ * Preliminary Data Sheet, December 1982
+ *
+ * The emulation is compatible with 3Com 3C501 EtherLink aka IE4. It also
+ * appears to be compatible with the original 1982 3C500 IBM Ethernet aka
+ * IE; the IE and IE4 documentation is nearly identical.
+ *
+ * The EtherLink is a very early design. It has only a single 2K buffer for
+ * both send and receive, and was desgined long before full-duplex Ethernet
+ * was possible (it is capable of simultaneous send and receive, but only in
+ * loopback mode). If it has just received a packet, the EtherLink can't
+ * receive another packet until the first one has been processed by the
+ * host.
+ *
+ * The above problem is greatly alleviated in a VM because incoming packets
+ * can be buffered for a short while and don't have to be immediately
+ * dropped just because the adapter is currently sending or because the
+ * receive status register has not been read yet.
+ *
+ * The first 8 registers (station address, receive and transmit command and
+ * status) are implemented in the SEEQ 8001 EDLC chip. The remaining 8
+ * registers are provided by the 3Com ASIC (0755-02) on the 3C501 or
+ * discrete chips on the 3C500.
+ *
+ * The '16 collisions' bit in the transmit command/status register is nearly
+ * useless. The SEEQ 8001 could retransmit automatically, but the IE/IE4 can
+ * not because the GP Buffer Pointer needs to be reinitialized by software
+ * prior to each transmit attempt. It is unclear if the 16-collision counter
+ * only rolls over modulo 16 or if it is cleared by something other than
+ * reset.
+ *
+ * The 3C501 supports DMA transfers to/from the packet buffer. Many drivers
+ * don't use DMA by default or at all. Due to the overhead of programming
+ * the DMA controller, direct I/O access (rep insb/outsb) is always faster
+ * in a VM. DMA would only be a win for very ancient drivers which don't use
+ * the rep insb/outsb instructions (those didn't exist on the 8086/8088).
+ *
+ * NB: The default DMA channel (channel 1) conflicts with the default Sound
+ * Blaster settings. If both 3C501 and SB16 are used, then one of them
+ * either needs to be reconfigured to use DMA channel other than 1 or the
+ * 3C501 must not use DMA.
+ *
+ * The 3Com documentation implies that writing the low byte of the Receive
+ * Buffer Pointer is enough to clear the pointer. Yet almost all drivers,
+ * including 3Com's sample code, write zeros to both the low and high bytes
+ * of the Receive Buffer Pointer when clearing it. BSD drivers (if_el.c)
+ * notably only write the low byte. It has been verified on a real 3C501
+ * that the documentation is correct. Writing anything to the Receive Buffer
+ * Pointer LSB clears the pointer (writing to the MSB appears to have no
+ * effect whatsoever).
+ *
+ * If the Receive Buffer Pointer is not explicitly cleared prior to
+ * receiving a packet, it will simply keep incrementing from wherever it
+ * was. Once it hits the end of the buffer (wraps around to zero), a
+ * receive overflow will be triggered (because the EDLC's FIFO will no
+ * longer be serviced) but the buffer will contain however much data there
+ * was room for. Note that the SEEQ 8001 datasheet is not explicit, but the
+ * EDLC can probably receive frames with more than 1,500 octets of payload.
+ *
+ * The GP Buffer Pointer behavior is quite curious. It appears to be
+ * internally a 12-bit pointer, and its top bit (that is, bit 11) is ignored
+ * when addressing into the 2K buffer. When writing the MSB, the top 5 bits
+ * are masked (always written as zero), i.e. only a 11-bit value can be
+ * written. Through auto-increment, the GP Buffer Pointer can reach values
+ * that can be read but not written.
+ *
+ * The implementation was tested for correctness using 3Com's diagnostic
+ * utility (3C501.EXE, Version 2.4, 1986 and also DIAGNOSE.COM, Version 2.0,
+ * 1983) and "passes diagnose with flying colors". Note that the interrupt
+ * test does not pass in V2.3 diagnostics by default because it writes an
+ * EOI to port 0F820h instead of 20h, relying on the system board to decode
+ * only the low 10 bits of the address. PCI-based systems decode all address
+ * bits and writes to address 0F820h do not reach the interrupt controller.
+ * The 3C501.EXE utility can be run with the '-i' switch to skip interrupt
+ * tests; the older DIAGNOSE.COM does not have that problem. In both
+ * versions, the preliminary test fails if the MAC address OID is not
+ * 02:60:8C (the utility thinks the PROM is corrupted).
+ *
+ * 3Com's XNS driver (ETH.SYS) likewise requires the OID to be 02:60:8C,
+ * otherwise the driver uses 00:00:00:00:00:00 as its MAC address, which is
+ * not something that produces useful results. Most old drivers (NetWare,
+ * NDIS, XENIX) don't care about the OID, but some (BSDs, Linux, some SCO
+ * UNIX versions) want to see the 3Com OID.
+ *
+ * The MS Networks Client setup also requires the OID to match 3Com's when
+ * detecting the hardware, but the actual NDIS driver does not care. Note
+ * that the setup fails to detect the emulated 3C501 at the default 0x300
+ * base address, but finds it at 0x310 and other addresses.
+ *
+ * Note that especially newer Linux/BSD OSes are a lost cause. Their 3C501
+ * drivers are very hard to configure, broken in various ways, and likely
+ * untested. For example the Linux driver clears the receive buffer pointer
+ * at the end of the interrupt handler, which may easily happen after a
+ * packet was already received. In FreeBSD 6.4, the kernel crashes when the
+ * el0 driver is loaded. In FreeBSD 5.0, the el0 driver sends packets and
+ * reads packets from the card, but the OS never sees any incoming data
+ * (even though the receive packet counter keeps going up).
+ *
+ * The precise receive logic (when a packet is copied to the buffer, when an
+ * interrupt is signaled, when receive goes idle) is difficult to understand
+ * from the 3Com documentation, but is extensively tested by the diagnostic
+ * utility. The SEEQ 8001 datasheet may be easier to understand than the
+ * EtherLink documentation.
+ *
+ * Some drivers (e.g. NetWare DOS IPX shell and ODI drivers) like to reset
+ * the chip more or less after every packet is sent or received. That leads
+ * to a situation where the NIC is briefly unable to receive anything. If we
+ * drop packets in that case, we end up with well over 10% packet loss and
+ * terrible performance. We have to hold off and not drop packets just
+ * because the receiver is disabled for a moment.
+ *
+ * Note that the reset bit in the auxiliary command register does not nearly
+ * reset the entire chip as the documentation suggests. It may only truly
+ * reset the SEEQ 8001 EDLC chip. It is impossible to say how going out of
+ * reset affects the auxiliary command register itself, since it must be
+ * written to exit the reset state. The reset bit clears the EDLC transmit
+ * and command registers, but not the programmed station address. It also
+ * does not disturb the packet buffer, and it does not clear the GP Buffer
+ * Pointer.
+ *
+ * The default EtherLink configuration uses I/O base 300h, IRQ 3, DMA
+ * channel 1. Prior to May 1983, the default IRQ was 5. On old EtherLink
+ * cards, the I/O address was configurable from 200h-3F0h in increments of
+ * 16, DMA 1 or 3, and IRQ 3 or 5. Newer EtherLinks (starting circa in 1984)
+ * in addition allow DMA 2 and IRQ 2, 4, 6, and 7.
+ *
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_ELNK
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/pdmnetifs.h>
+#include <VBox/vmm/pgm.h>
+#include <VBox/version.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/net.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#ifdef IN_RING3
+# include <iprt/mem.h>
+# include <iprt/semaphore.h>
+# include <iprt/uuid.h>
+#endif
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+#define ELNK_SAVEDSTATE_VERSION 1
+
+/** Maximum number of times we report a link down to the guest (failure to send frame) */
+#define ELNK_MAX_LINKDOWN_REPORTED 3
+
+/** Maximum number of times we postpone restoring a link that is temporarily down. */
+#define ELNK_MAX_LINKRST_POSTPONED 3
+
+/** Maximum frame size we handle */
+#define MAX_FRAME 1536
+
+/* Size of the packet buffer. */
+#define ELNK_BUF_SIZE 2048u
+
+/* The packet buffer address mask. */
+#define ELNK_BUF_ADR_MASK (ELNK_BUF_SIZE - 1)
+
+/* The GP buffer pointer address within the buffer. */
+#define ELNK_GP(pThis) ((pThis)->uGPBufPtr & ELNK_BUF_ADR_MASK)
+
+/* The GP buffer pointer mask.
+ * NB: The GP buffer pointer is internally a 12-bit counter. When addressing into the
+ * packet buffer, bit 11 is ignored. Required to pass 3C501 diagnostics.
+ */
+#define ELNK_GP_MASK 0xfff
+
+/* The EtherLink is an 8-bit adapter, hence DMA channels up to 3 are available. */
+#define ELNK_MAX_VALID_DMA 3
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+
+/**
+ * EtherLink Transmit Command Register.
+ */
+typedef struct ELNK_XMIT_CMD {
+ uint8_t det_ufl : 1; /* Detect underflow. */
+ uint8_t det_coll : 1; /* Detect collision. */
+ uint8_t det_16col : 1; /* Detect collision 16. */
+ uint8_t det_succ : 1; /* Detect successful xmit. */
+ uint8_t unused : 4;
+} EL_XMT_CMD;
+
+/**
+ * EtherLink Transmit Status Register.
+ *
+ * We will never see any real collisions, although collisions (including 16
+ * successive collisions) may be useful to report when the link is down
+ * (something the 3C501 does not have a concept of).
+ */
+typedef struct ELNK_XMIT_STAT {
+ uint8_t uflow : 1; /* Underflow on transmit. */
+ uint8_t coll : 1; /* Collision on transmit. */
+ uint8_t coll16 : 1; /* 16 collisions on transmit. */
+ uint8_t ready : 1; /* Ready for a new frame. */
+ uint8_t undef : 4;
+} EL_XMT_STAT;
+
+/** Address match (adr_match) modes. */
+typedef enum {
+ EL_ADRM_DISABLED = 0, /* Receiver disabled. */
+ EL_ADRM_PROMISC = 1, /* Receive all addresses. */
+ EL_ADRM_BCAST = 2, /* Receive station + broadcast. */
+ EL_ADRM_MCAST = 3 /* Receive station + multicast. */
+} EL_ADDR_MATCH;
+
+/**
+ * EtherLink Receive Command Register.
+ */
+typedef struct ELNK_RECV_CMD {
+ uint8_t det_ofl : 1; /* Detect overflow errors. */
+ uint8_t det_fcs : 1; /* Detect FCS errors. */
+ uint8_t det_drbl : 1; /* Detect dribble error. */
+ uint8_t det_runt : 1; /* Detect short frames. */
+ uint8_t det_eof : 1; /* Detect EOF (frames without overflow). */
+ uint8_t acpt_good : 1; /* Accept good frames. */
+ uint8_t adr_match : 2; /* Address match mode. */
+} EL_RCV_CMD;
+
+/**
+ * EtherLink Receive Status Register.
+ */
+typedef struct ELNK_RECV_STAT {
+ uint8_t oflow : 1; /* Overflow on receive. */
+ uint8_t fcs : 1; /* FCS error. */
+ uint8_t dribble : 1; /* Dribble error. */
+ uint8_t runt : 1; /* Short frame. */
+ uint8_t no_ovf : 1; /* Received packet w/o overflow. */
+ uint8_t good : 1; /* Received good packet. */
+ uint8_t undef : 1;
+ uint8_t stale : 1; /* Stale receive status. */
+} EL_RCV_STAT;
+
+/** Buffer control (buf_ctl) modes. */
+typedef enum {
+ EL_BCTL_SYSTEM = 0, /* Host has buffer access. */
+ EL_BCTL_XMT_RCV = 1, /* Transmit, then receive. */
+ EL_BCTL_RECEIVE = 2, /* Receive. */
+ EL_BCTL_LOOPBACK = 3 /* Loopback. */
+} EL_BUFFER_CONTROL;
+
+/**
+ * EtherLink Auxiliary Status Register.
+ */
+typedef struct ELNK_AUX_CMD {
+ uint8_t ire : 1; /* Interrupt Request Enable. */
+ uint8_t xmit_bf : 1; /* Xmit packets with bad FCS. */
+ uint8_t buf_ctl : 2; /* Packet buffer control. */
+ uint8_t unused : 1;
+ uint8_t dma_req : 1; /* DMA request. */
+ uint8_t ride : 1; /* Request Interrupt and DMA Enable. */
+ uint8_t reset : 1; /* Card in reset while set. */
+} EL_AUX_CMD;
+
+/**
+ * EtherLink Auxiliary Status Register.
+ */
+typedef struct ELNK_AUX_STAT {
+ uint8_t recv_bsy : 1; /* Receive busy. */
+ uint8_t xmit_bf : 1; /* Xmit packets with bad FCS. */
+ uint8_t buf_ctl : 2; /* Packet buffer control. */
+ uint8_t dma_done : 1; /* DMA done. */
+ uint8_t dma_req : 1; /* DMA request. */
+ uint8_t ride : 1; /* Request Interrupt and DMA Enable. */
+ uint8_t xmit_bsy : 1; /* Transmit busy. */
+} EL_AUX_STAT;
+
+/**
+ * Internal interrupt status.
+ */
+typedef struct ELNK_INTR_STAT {
+ uint8_t recv_intr : 1; /* Receive interrupt status. */
+ uint8_t xmit_intr : 1; /* Transmit interrupt status. */
+ uint8_t dma_intr : 1; /* DMA interrupt status. */
+ uint8_t unused : 5;
+} EL_INTR_STAT;
+
+
+/**
+ * EtherLink 3C501 state.
+ */
+typedef struct ELNKSTATE
+{
+ /** Restore timer.
+ * This is used to disconnect and reconnect the link after a restore. */
+ TMTIMERHANDLE hTimerRestore;
+
+ /** Transmit signaller. */
+ PDMTASKHANDLE hXmitTask;
+ /** Receive ready signaller. */
+ PDMTASKHANDLE hCanRxTask;
+
+ /** Internal interrupt flag. */
+ bool fISR;
+ /** Internal DMA active flag. */
+ bool fDMA;
+ /** Internal in-reset flag. */
+ bool fInReset;
+
+ /** The PROM contents. Only 8 bytes addressable, R/O. */
+ uint8_t aPROM[8];
+
+ /** The station address programmed by the guest, W/O. */
+ uint8_t aStationAddr[6];
+ /** General Purpose (GP) Buffer Pointer, R/W. */
+ uint16_t uGPBufPtr;
+
+ /** Receive (RCV) Buffer Pointer, R/WC. */
+ uint16_t uRCVBufPtr;
+ /** Transmit Command Register, W/O. */
+ union {
+ uint8_t XmitCmdReg;
+ EL_XMT_CMD XmitCmd;
+ };
+ /** Transmit Status Register, R/O. */
+ union {
+ uint8_t XmitStatReg;
+ EL_XMT_STAT XmitStat;
+ };
+ /** Receive Command Register, W/O. */
+ union {
+ uint8_t RcvCmdReg;
+ EL_RCV_CMD RcvCmd;
+ };
+ /** Receive Status Register, R/O. */
+ union {
+ uint8_t RcvStatReg;
+ EL_RCV_STAT RcvStat;
+ };
+ /** Auxiliary Command Register, W/O. */
+ union {
+ uint8_t AuxCmdReg;
+ EL_AUX_CMD AuxCmd;
+ };
+ /** Auxiliary Status Register, R/O. */
+ union {
+ uint8_t AuxStatReg;
+ EL_AUX_STAT AuxStat;
+ };
+
+ /** Base port of the I/O space region. */
+ RTIOPORT IOPortBase;
+ /** The configured ISA IRQ. */
+ uint8_t uIsaIrq;
+ /** The configured ISA DMA channel. */
+ uint8_t uIsaDma;
+ /** If set the link is currently up. */
+ bool fLinkUp;
+ /** If set the link is temporarily down because of a saved state load. */
+ bool fLinkTempDown;
+ /** Number of times we've reported the link down. */
+ uint16_t cLinkDownReported;
+ /** Number of times we've postponed the link restore. */
+ uint16_t cLinkRestorePostponed;
+
+ /** The "hardware" MAC address. */
+ RTMAC MacConfigured;
+ /** Internal interrupt state. */
+ union {
+ uint8_t IntrStateReg;
+ EL_INTR_STAT IntrState;
+ };
+
+ /** Set if ELNKSTATER3::pDrv is not NULL. */
+ bool fDriverAttached;
+ /** The LED. */
+ PDMLED Led;
+ /** Status LUN: The LED ports. */
+ PDMILEDPORTS ILeds;
+ /** Partner of ILeds. */
+ R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
+
+ /** Access critical section. */
+ PDMCRITSECT CritSect;
+ /** Event semaphore for blocking on receive. */
+ RTSEMEVENT hEventOutOfRxSpace;
+ /** We are waiting/about to start waiting for more receive buffers. */
+ bool volatile fMaybeOutOfSpace;
+
+ /* MS to wait before we enable the link. */
+ uint32_t cMsLinkUpDelay;
+ /** The device instance number (for logging). */
+ uint32_t iInstance;
+
+ STAMCOUNTER StatReceiveBytes;
+ STAMCOUNTER StatTransmitBytes;
+ STAMCOUNTER StatPktsLostReset;
+#ifdef VBOX_WITH_STATISTICS
+ STAMPROFILEADV StatIOReadRZ;
+ STAMPROFILEADV StatIOReadR3;
+ STAMPROFILEADV StatIOWriteRZ;
+ STAMPROFILEADV StatIOWriteR3;
+ STAMPROFILEADV StatReceive;
+ STAMPROFILEADV StatTransmitR3;
+ STAMPROFILEADV StatTransmitRZ;
+ STAMPROFILE StatTransmitSendR3;
+ STAMPROFILE StatTransmitSendRZ;
+ STAMPROFILE StatRxOverflow;
+ STAMCOUNTER StatRxOverflowWakeup;
+ STAMPROFILEADV StatInterrupt;
+ STAMCOUNTER StatResets;
+ STAMCOUNTER StatDropPktAdrmDis;
+ STAMCOUNTER StatDropPktZeroLen;
+ STAMCOUNTER StatDropPktVMNotRunning;
+ STAMCOUNTER StatDropPktNoLink;
+ STAMCOUNTER StatDropPktStaleRcv;
+#endif /* VBOX_WITH_STATISTICS */
+
+ /** ISA I/O ports. */
+ IOMIOPORTHANDLE hIoPortsIsa;
+
+ /** The loopback transmit buffer (avoid stack allocations). */
+ uint8_t abLoopBuf[ELNK_BUF_SIZE];
+
+ /** The runt pad buffer (only really needs 60 bytes). */
+ uint8_t abRuntBuf[64];
+
+ /** The packet buffer. */
+ uint8_t abPacketBuf[ELNK_BUF_SIZE];
+} ELNKSTATE, *PELNKSTATE;
+
+
+/**
+ * EtherLink state for ring-3.
+ *
+ * @implements PDMIBASE
+ * @implements PDMINETWORKDOWN
+ * @implements PDMINETWORKCONFIG
+ * @implements PDMILEDPORTS
+ */
+typedef struct ELNKSTATER3
+{
+ /** Pointer to the device instance. */
+ PPDMDEVINSR3 pDevIns;
+ /** Pointer to the connector of the attached network driver. */
+ PPDMINETWORKUPR3 pDrv;
+ /** Pointer to the attached network driver. */
+ R3PTRTYPE(PPDMIBASE) pDrvBase;
+ /** LUN\#0 + status LUN: The base interface. */
+ PDMIBASE IBase;
+ /** LUN\#0: The network port interface. */
+ PDMINETWORKDOWN INetworkDown;
+ /** LUN\#0: The network config port interface. */
+ PDMINETWORKCONFIG INetworkConfig;
+
+ /** Status LUN: The LED ports. */
+ PDMILEDPORTS ILeds;
+ /** Partner of ILeds. */
+ R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
+} ELNKSTATER3;
+/** Pointer to an EtherLink state structure for ring-3. */
+typedef ELNKSTATER3 *PELNKSTATER3;
+
+
+/**
+ * EtherLink state for ring-0.
+ */
+typedef struct ELNKSTATER0
+{
+ /** Pointer to the connector of the attached network driver. */
+ PPDMINETWORKUPR0 pDrv;
+} ELNKSTATER0;
+/** Pointer to an EtherLink state structure for ring-0. */
+typedef ELNKSTATER0 *PELNKSTATER0;
+
+
+/**
+ * EtherLink state for raw-mode.
+ */
+typedef struct ELNKSTATERC
+{
+ /** Pointer to the connector of the attached network driver. */
+ PPDMINETWORKUPRC pDrv;
+} ELNKSTATERC;
+/** Pointer to an EtherLink state structure for raw-mode. */
+typedef ELNKSTATERC *PELNKSTATERC;
+
+
+/** The EtherLink state structure for the current context. */
+typedef CTX_SUFF(ELNKSTATE) ELNKSTATECC;
+/** Pointer to an EtherLink state structure for the current
+ * context. */
+typedef CTX_SUFF(PELNKSTATE) PELNKSTATECC;
+
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+static int elnkAsyncTransmit(PPDMDEVINS pDevIns, PELNKSTATE pThis, PELNKSTATECC pThisCC, bool fOnWorkerThread);
+
+/**
+ * Checks if the link is up.
+ * @returns true if the link is up.
+ * @returns false if the link is down.
+ */
+DECLINLINE(bool) elnkIsLinkUp(PELNKSTATE pThis)
+{
+ return pThis->fDriverAttached && !pThis->fLinkTempDown && pThis->fLinkUp;
+}
+
+
+#ifndef ETHER_IS_MULTICAST /* Net/Open BSD macro it seems */
+#define ETHER_IS_MULTICAST(a) ((*(uint8_t *)(a)) & 1)
+#endif
+
+#define ETHER_ADDR_LEN ETH_ALEN
+#define ETH_ALEN 6
+#pragma pack(1)
+struct ether_header /** @todo Use RTNETETHERHDR? */
+{
+ uint8_t ether_dhost[ETH_ALEN]; /**< destination ethernet address */
+ uint8_t ether_shost[ETH_ALEN]; /**< source ethernet address */
+ uint16_t ether_type; /**< packet type ID field */
+};
+#pragma pack()
+
+
+/**
+ * Check if incoming frame matches the station address.
+ */
+DECLINLINE(int) padr_match(PELNKSTATE pThis, const uint8_t *buf)
+{
+ struct ether_header *hdr = (struct ether_header *)buf;
+ int result;
+
+ /* Checks own + broadcast as well as own + multicast. */
+ result = (pThis->RcvCmd.adr_match >= EL_ADRM_BCAST) && !memcmp(hdr->ether_dhost, pThis->aStationAddr, 6);
+
+ return result;
+}
+
+
+/**
+ * Check if incoming frame is an accepted broadcast frame.
+ */
+DECLINLINE(int) padr_bcast(PELNKSTATE pThis, const uint8_t *buf)
+{
+ static uint8_t aBCAST[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ struct ether_header *hdr = (struct ether_header *)buf;
+ int result = (pThis->RcvCmd.adr_match == EL_ADRM_BCAST) && !memcmp(hdr->ether_dhost, aBCAST, 6);
+ return result;
+}
+
+
+/**
+ * Check if incoming frame is an accepted multicast frame.
+ */
+DECLINLINE(int) padr_mcast(PELNKSTATE pThis, const uint8_t *buf)
+{
+ struct ether_header *hdr = (struct ether_header *)buf;
+ int result = (pThis->RcvCmd.adr_match == EL_ADRM_MCAST) && ETHER_IS_MULTICAST(hdr->ether_dhost);
+ return result;
+}
+
+
+/**
+ * Update the device IRQ line based on internal state.
+ */
+static void elnkUpdateIrq(PPDMDEVINS pDevIns, PELNKSTATE pThis)
+{
+ bool fISR = false;
+
+ STAM_PROFILE_ADV_START(&pThis->StatInterrupt, a);
+
+ /* IRQ is active if any interrupt source is active and interrupts
+ * are enabled via RIDE or IRE.
+ */
+ if (pThis->IntrStateReg && (pThis->AuxCmd.ride || pThis->AuxCmd.ire))
+ fISR = true;
+
+ Log2(("#%d set irq fISR=%d\n", pThis->iInstance, fISR));
+
+ /* The IRQ line typically does not change. */
+ if (RT_UNLIKELY(fISR != pThis->fISR))
+ {
+ Log(("#%d IRQ=%d, state=%d\n", pThis->iInstance, pThis->uIsaIrq, fISR));
+ PDMDevHlpISASetIrq(pDevIns, pThis->uIsaIrq, fISR);
+ pThis->fISR = fISR;
+ }
+ STAM_PROFILE_ADV_STOP(&pThis->StatInterrupt, a);
+}
+
+
+/**
+ * Perform a software reset of the NIC.
+ */
+static void elnkSoftReset(PPDMDEVINS pDevIns, PELNKSTATE pThis)
+{
+ LogFlowFunc(("#%d:\n", pThis->iInstance));
+
+ /* Clear some of the user-visible register state. */
+ pThis->XmitCmdReg = 0;
+ pThis->XmitStatReg = 0;
+ pThis->RcvCmdReg = 0;
+ pThis->RcvStatReg = 0;
+ pThis->AuxCmdReg = 0;
+ pThis->AuxStatReg = 0;
+
+ /* The "stale receive status" is cleared by receiving an "interesting" packet. */
+ pThis->RcvStat.stale = 1;
+
+ /* By virtue of setting the buffer control to system, transmit is set to busy. */
+ pThis->AuxStat.xmit_bsy = 1;
+
+ /* Clear internal interrupt state. */
+ pThis->IntrStateReg = 0;
+ elnkUpdateIrq(pDevIns, pThis);
+
+ /* Note that a soft reset does not clear the packet buffer; software often
+ * assumes that it survives soft reset. The programmed station address is
+ * likewise not reset, and the buffer pointers are not reset either.
+ * Verified on a real 3C501.
+ */
+
+ /* No longer in reset state. */
+ pThis->fInReset = false;
+}
+
+#ifdef IN_RING3
+
+static DECLCALLBACK(void) elnkR3WakeupReceive(PPDMDEVINS pDevIns)
+{
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+ STAM_COUNTER_INC(&pThis->StatRxOverflowWakeup);
+ if (pThis->hEventOutOfRxSpace != NIL_RTSEMEVENT)
+ RTSemEventSignal(pThis->hEventOutOfRxSpace);
+}
+
+/**
+ * @callback_method_impl{FNPDMTASKDEV,
+ * Signal to R3 that NIC is ready to receive a packet.
+ */
+static DECLCALLBACK(void) elnkR3CanRxTaskCallback(PPDMDEVINS pDevIns, void *pvUser)
+{
+ RT_NOREF(pvUser);
+ elnkR3WakeupReceive(pDevIns);
+}
+
+#endif /* IN_RING3 */
+
+
+/**
+ * Write incoming data into the packet buffer.
+ */
+static void elnkReceiveLocked(PPDMDEVINS pDevIns, PELNKSTATE pThis, const uint8_t *src, size_t cbToRecv, bool fLoopback)
+{
+ int is_padr = 0, is_bcast = 0, is_mcast = 0;
+ union {
+ uint8_t RcvStatNewReg;
+ EL_RCV_STAT RcvStatNew;
+ };
+
+ /*
+ * Drop all packets if the VM is not running yet/anymore.
+ */
+ VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
+ if ( enmVMState != VMSTATE_RUNNING
+ && enmVMState != VMSTATE_RUNNING_LS)
+ {
+ STAM_COUNTER_INC(&pThis->StatDropPktVMNotRunning);
+ return;
+ }
+
+ /* Drop everything if address matching is disabled. */
+ if (RT_UNLIKELY(pThis->RcvCmd.adr_match == EL_ADRM_DISABLED))
+ {
+ STAM_COUNTER_INC(&pThis->StatDropPktAdrmDis);
+ return;
+ }
+
+ /* Drop zero-length packets (how does that even happen?). */
+ if (RT_UNLIKELY(!cbToRecv))
+ {
+ STAM_COUNTER_INC(&pThis->StatDropPktZeroLen);
+ return;
+ }
+
+ /*
+ * Drop all packets if the cable is not connected (and not in loopback).
+ */
+ if (RT_UNLIKELY(!elnkIsLinkUp(pThis) && !fLoopback))
+ {
+ STAM_COUNTER_INC(&pThis->StatDropPktNoLink);
+ return;
+ }
+
+ /*
+ * Do not receive further packets until receive status was read.
+ */
+ if (RT_UNLIKELY(pThis->RcvStat.stale == 0))
+ {
+ STAM_COUNTER_INC(&pThis->StatDropPktStaleRcv);
+ return;
+ }
+
+ LogFlowFunc(("#%d: size on wire=%d, RCV ptr=%u\n", pThis->iInstance, cbToRecv, pThis->uRCVBufPtr));
+
+ /*
+ * Perform address matching. Packets which do not pass the address
+ * filter are always ignored.
+ */
+ /// @todo cbToRecv must be 6 or more (complete address)
+ if ( pThis->RcvCmd.adr_match == EL_ADRM_PROMISC /* promiscuous enabled */
+ || (is_padr = padr_match(pThis, src))
+ || (is_bcast = padr_bcast(pThis, src))
+ || (is_mcast = padr_mcast(pThis, src)))
+ {
+ uint8_t *dst = pThis->abPacketBuf + pThis->uRCVBufPtr;
+
+ Log2Func(("#%d Packet passed address filter (is_padr=%d, is_bcast=%d, is_mcast=%d), size=%d\n", pThis->iInstance, cbToRecv, is_padr, is_bcast, is_mcast));
+
+ /* Receive status is evaluated from scratch. The stale bit must remain set until we know better. */
+ RcvStatNewReg = 0;
+ RcvStatNew.stale = 1;
+ pThis->RcvStatReg = 0x80;
+
+ /* Detect errors: Runts, overflow, and FCS errors.
+ * NB: Dribble errors can not happen because we can only receive an
+ * integral number of bytes. FCS errors are only possible in loopback
+ * mode in case the FCS is deliberately corrupted.
+ */
+
+ /* See if we need to pad, and how much. Have to be careful because the
+ * Receive Buffer Pointer might be near the end of the buffer.
+ */
+ if (RT_UNLIKELY(cbToRecv < 60))
+ {
+ /* In loopback mode only, short packets are flagged as errors because
+ * diagnostic tools want to see the errors. Otherwise they're padded to
+ * minimum length (if packet came over the wire, it should have been
+ * properly padded).
+ */
+ /// @todo This really is kind of wrong. We shouldn't be doing any
+ /// padding here, it should be done by the sending side!
+ if (!fLoopback)
+ {
+ memset(pThis->abRuntBuf, 0, sizeof(pThis->abRuntBuf));
+ memcpy(pThis->abRuntBuf, src, cbToRecv);
+ cbToRecv = 60;
+ src = pThis->abRuntBuf;
+ }
+ else
+ {
+ LogFunc(("#%d runt, size=%d\n", pThis->iInstance, cbToRecv));
+ RcvStatNew.runt = 1;
+ }
+ }
+
+ /* We don't care how big the frame is; if it fits into the buffer, all is
+ * good. But conversely if the Receive Buffer Pointer is initially near the
+ * end of the buffer, a small frame can trigger an overflow.
+ */
+ if (pThis->uRCVBufPtr + cbToRecv <= ELNK_BUF_SIZE)
+ {
+ RcvStatNew.no_ovf = 1;
+ }
+ else
+ {
+ LogFunc(("#%d overflow, size=%d\n", pThis->iInstance, cbToRecv));
+ RcvStatNew.oflow = 1;
+ }
+
+ if (fLoopback && pThis->AuxCmd.xmit_bf)
+ {
+ LogFunc(("#%d bad FCS\n", pThis->iInstance));
+ RcvStatNew.fcs = 1;
+ }
+
+ /* Error-free packets are considered good. */
+ if (RcvStatNew.no_ovf && !RcvStatNew.fcs && !RcvStatNew.runt)
+ RcvStatNew.good = 1;
+
+ uint16_t cbCopy = (uint16_t)RT_MIN(ELNK_BUF_SIZE - pThis->uRCVBufPtr, cbToRecv);
+
+ /* All packets that passed the address filter are copied to the buffer. */
+
+ STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cbCopy);
+
+ /* Copy incoming data to the packet buffer. NB: Starts at the current
+ * Receive Buffer Pointer position.
+ */
+ memcpy(dst, src, cbCopy);
+
+ /* Packet length is indicated via the receive buffer pointer. */
+ pThis->uRCVBufPtr = (pThis->uRCVBufPtr + cbCopy) & ELNK_GP_MASK;
+
+ Log2Func(("Received packet, size=%d, RP=%u\n", cbCopy, pThis->uRCVBufPtr));
+
+ /*
+ * If one of the "interesting" conditions was hit, stop receiving until
+ * the status register is read (mark it not stale).
+ * NB: The precise receive logic is not very well described in the EtherLink
+ * documentation. It was refined using the 3C501.EXE diagnostic utility.
+ */
+ if ( (RcvStatNew.good && pThis->RcvCmd.acpt_good)
+ || (RcvStatNew.no_ovf && pThis->RcvCmd.det_eof)
+ || (RcvStatNew.runt && pThis->RcvCmd.det_runt)
+ || (RcvStatNew.dribble && pThis->RcvCmd.det_drbl)
+ || (RcvStatNew.fcs && pThis->RcvCmd.det_fcs)
+ || (RcvStatNew.oflow && pThis->RcvCmd.det_ofl))
+ {
+ pThis->AuxStat.recv_bsy = 0;
+ pThis->IntrState.recv_intr = 1;
+ RcvStatNew.stale = 0; /* Prevents further receive until set again. */
+ }
+ /* Finally update the receive status. */
+ pThis->RcvStat = RcvStatNew;
+
+ LogFlowFunc(("#%d: RcvCmd=%02X, RcvStat=%02X, RCVBufPtr=%u\n", pThis->iInstance, pThis->RcvCmdReg, pThis->RcvStatReg, pThis->uRCVBufPtr));
+ elnkUpdateIrq(pDevIns, pThis);
+ }
+}
+
+
+/**
+ * Transmit data from the packet buffer.
+ *
+ * @returns VBox status code. VERR_TRY_AGAIN is returned if we're busy.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The EtherLink shared instance
+ * data.
+ * @param pThisCC The EtherLink state data for the
+ * current context.
+ * @param fOnWorkerThread Whether we're on a worker thread or on an EMT.
+ */
+static int elnkXmitBuffer(PPDMDEVINS pDevIns, PELNKSTATE pThis, PELNKSTATECC pThisCC, bool fOnWorkerThread)
+{
+ RT_NOREF_PV(fOnWorkerThread);
+ int rc;
+
+ /*
+ * Grab the xmit lock of the driver as well as the 3C501 device state.
+ */
+ PPDMINETWORKUP pDrv = pThisCC->pDrv;
+ if (pDrv)
+ {
+ rc = pDrv->pfnBeginXmit(pDrv, false /*fOnWorkerThread*/);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo check if we're supposed to suspend now. */
+ /*
+ * Do the transmitting.
+ */
+ int rc2 = elnkAsyncTransmit(pDevIns, pThis, pThisCC, false /*fOnWorkerThread*/);
+ AssertReleaseRC(rc2);
+
+ /*
+ * Release the locks.
+ */
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ }
+ else
+ AssertLogRelRC(rc);
+ if (pDrv)
+ pDrv->pfnEndXmit(pDrv);
+
+ return rc;
+}
+
+
+#ifdef IN_RING3
+
+/**
+ * @callback_method_impl{FNPDMTASKDEV,
+ * This is just a very simple way of delaying sending to R3.
+ */
+static DECLCALLBACK(void) elnkR3XmitTaskCallback(PPDMDEVINS pDevIns, void *pvUser)
+{
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+ PELNKSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PELNKSTATECC);
+ NOREF(pvUser);
+
+ /*
+ * Transmit if we can.
+ */
+ elnkXmitBuffer(pDevIns, pThis, pThisCC, true /*fOnWorkerThread*/);
+}
+#endif /* IN_RING3 */
+
+
+/**
+ * Allocates a scatter/gather buffer for a transfer.
+ *
+ * @returns See PPDMINETWORKUP::pfnAllocBuf.
+ * @param pThis The shared state data.
+ * @param pThisCC The current context state data.
+ * @param cbMin The minimum buffer size.
+ * @param fLoopback Set if we're in loopback mode.
+ * @param pSgLoop Pointer to stack storage for the loopback SG.
+ * @param ppSgBuf Where to return the SG buffer descriptor on success.
+ * Always set.
+ */
+DECLINLINE(int) elnkXmitAllocBuf(PELNKSTATE pThis, PELNKSTATECC pThisCC, size_t cbMin, bool fLoopback,
+ PPDMSCATTERGATHER pSgLoop, PPPDMSCATTERGATHER ppSgBuf)
+{
+ int rc;
+
+ if (!fLoopback)
+ {
+ PPDMINETWORKUP pDrv = pThisCC->pDrv;
+ if (RT_LIKELY(pDrv))
+ {
+ rc = pDrv->pfnAllocBuf(pDrv, cbMin, NULL /*pGso*/, ppSgBuf);
+ AssertMsg(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN || rc == VERR_NET_DOWN || rc == VERR_NO_MEMORY, ("%Rrc\n", rc));
+ if (RT_FAILURE(rc))
+ *ppSgBuf = NULL;
+ }
+ else
+ {
+ rc = VERR_NET_DOWN;
+ *ppSgBuf = NULL;
+ }
+ }
+ else
+ {
+ /* Fake loopback allocator. */
+ pSgLoop->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
+ pSgLoop->cbUsed = 0;
+ pSgLoop->cbAvailable = sizeof(pThis->abLoopBuf);
+ pSgLoop->pvAllocator = pThis;
+ pSgLoop->pvUser = NULL;
+ pSgLoop->cSegs = 1;
+ pSgLoop->aSegs[0].cbSeg = sizeof(pThis->abLoopBuf);
+ pSgLoop->aSegs[0].pvSeg = pThis->abLoopBuf;
+ *ppSgBuf = pSgLoop;
+ rc = VINF_SUCCESS;
+ }
+ return rc;
+}
+
+
+/**
+ * Sends the scatter/gather buffer.
+ *
+ * Wrapper around PDMINETWORKUP::pfnSendBuf, so check it out for the fine print.
+ *
+ * @returns See PDMINETWORKUP::pfnSendBuf.
+ * @param pDevIns The device instance.
+ * @param pThis The shared EtherLink state data.
+ * @param pThisCC The current context state data.
+ * @param fLoopback Set if we're in loopback mode.
+ * @param pSgBuf The SG to send.
+ * @param fOnWorkerThread Set if we're being called on a work thread. Clear
+ * if an EMT.
+ */
+DECLINLINE(int) elnkXmitSendBuf(PPDMDEVINS pDevIns, PELNKSTATE pThis, PELNKSTATECC pThisCC, bool fLoopback, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
+{
+ int rc;
+ STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, pSgBuf->cbUsed);
+ if (!fLoopback)
+ {
+ STAM_PROFILE_START(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
+ if (pSgBuf->cbUsed > 70) /* unqualified guess */
+ pThis->Led.Asserted.s.fWriting = pThis->Led.Actual.s.fWriting = 1;
+
+ PPDMINETWORKUP pDrv = pThisCC->pDrv;
+ if (RT_LIKELY(pDrv))
+ {
+ rc = pDrv->pfnSendBuf(pDrv, pSgBuf, fOnWorkerThread);
+ AssertMsg(rc == VINF_SUCCESS || rc == VERR_NET_DOWN || rc == VERR_NET_NO_BUFFER_SPACE, ("%Rrc\n", rc));
+ }
+ else
+ rc = VERR_NET_DOWN;
+
+ pThis->Led.Actual.s.fWriting = 0;
+ STAM_PROFILE_STOP(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
+ }
+ else
+ {
+ /* Loopback, immediately send buffer to the receive path. */
+ Assert(pSgBuf->pvAllocator == (void *)pThis);
+ pThis->Led.Asserted.s.fReading = pThis->Led.Actual.s.fReading = 1;
+
+ LogFlowFunc(("#%d: loopback (%u bytes)\n", pThis->iInstance, pSgBuf->cbUsed));
+ elnkReceiveLocked(pDevIns, pThis, pThis->abLoopBuf, pSgBuf->cbUsed, fLoopback);
+ pThis->Led.Actual.s.fReading = 0;
+ rc = VINF_SUCCESS;
+ }
+ return rc;
+}
+
+
+/**
+ * Reads the entire frame into the scatter gather buffer.
+ */
+DECLINLINE(void) elnkXmitRead(PPDMDEVINS pDevIns, PELNKSTATE pThis, const unsigned cbFrame, PPDMSCATTERGATHER pSgBuf)
+{
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect)); RT_NOREF(pDevIns);
+ Assert(pSgBuf->cbAvailable >= cbFrame);
+
+ pSgBuf->cbUsed = cbFrame;
+ memcpy(pSgBuf->aSegs[0].pvSeg, &pThis->abPacketBuf[ELNK_GP(pThis)], cbFrame);
+}
+
+/**
+ * Try to transmit a frame.
+ */
+static void elnkTransmit(PPDMDEVINS pDevIns, PELNKSTATE pThis)
+{
+ PELNKSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PELNKSTATECC);
+
+ /*
+ * Transmit the packet if possible, defer it if we cannot do it
+ * in the current context.
+ */
+#if defined(IN_RING0) || defined(IN_RC)
+ if (!pThisCC->pDrv)
+ {
+ int rc = PDMDevHlpTaskTrigger(pDevIns, pThis->hXmitTask);
+ AssertRC(rc);
+ }
+ else
+#endif
+ {
+ int rc = elnkXmitBuffer(pDevIns, pThis, pThisCC, false /*fOnWorkerThread*/);
+ if (rc == VERR_TRY_AGAIN)
+ rc = VINF_SUCCESS;
+ AssertRC(rc);
+ }
+}
+
+
+/**
+ * If a packet is waiting, poke the receiving machinery.
+ *
+ * @threads EMT.
+ */
+static void elnkKickReceive(PPDMDEVINS pDevIns, PELNKSTATE pThis)
+{
+ /* Some drivers (e.g. NetWare IPX shell/ODI drivers) first go to receive mode through
+ * the aux command register and only then enable address matching.
+ */
+ if ((pThis->AuxStat.recv_bsy == 1) && (pThis->RcvCmd.adr_match != EL_ADRM_DISABLED))
+ {
+ if (pThis->fMaybeOutOfSpace)
+ {
+#ifdef IN_RING3
+ elnkR3WakeupReceive(pDevIns);
+#else
+ int rc = PDMDevHlpTaskTrigger(pDevIns, pThis->hCanRxTask);
+ AssertRC(rc);
+#endif
+ }
+ }
+
+}
+
+/**
+ * Try transmitting a frame.
+ *
+ * @threads TX or EMT.
+ */
+static int elnkAsyncTransmit(PPDMDEVINS pDevIns, PELNKSTATE pThis, PELNKSTATECC pThisCC, bool fOnWorkerThread)
+{
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+
+ /*
+ * Just drop it if not transmitting. Can happen with delayed transmits
+ * if transmit was disabled in the meantime.
+ */
+ if (RT_UNLIKELY(!pThis->AuxStat.xmit_bsy))
+ {
+ LogFunc(("#%d: Nope, xmit disabled (fOnWorkerThread=%RTbool)\n", pThis->iInstance, fOnWorkerThread));
+ return VINF_SUCCESS;
+ }
+
+ if (RT_UNLIKELY((pThis->AuxCmd.buf_ctl != EL_BCTL_XMT_RCV) && (pThis->AuxCmd.buf_ctl != EL_BCTL_LOOPBACK)))
+ {
+ LogFunc(("#%d: Nope, not in xmit-then-receive or loopback state (fOnWorkerThread=%RTbool)\n", pThis->iInstance, fOnWorkerThread));
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Blast out data from the packet buffer.
+ */
+ int rc;
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ do
+ {
+ /* Don't send anything when the link is down. */
+ if (RT_UNLIKELY( !elnkIsLinkUp(pThis)
+ && pThis->cLinkDownReported > ELNK_MAX_LINKDOWN_REPORTED)
+ )
+ break;
+
+ bool const fLoopback = pThis->AuxCmd.buf_ctl == EL_BCTL_LOOPBACK;
+ PDMSCATTERGATHER SgLoop;
+ PPDMSCATTERGATHER pSgBuf;
+
+ /*
+ * Sending is easy peasy, there is by definition always
+ * a complete packet on hand.
+ */
+ const unsigned cb = ELNK_BUF_SIZE - ELNK_GP(pThis); /* Packet size. */
+ LogFunc(("#%d: cb=%d\n", pThis->iInstance, cb));
+
+ pThis->XmitStatReg = 0; /* Clear transmit status before filling it out. */
+
+ if (RT_LIKELY(elnkIsLinkUp(pThis) || fLoopback))
+ {
+ if (RT_LIKELY(cb <= MAX_FRAME))
+ {
+ rc = elnkXmitAllocBuf(pThis, pThisCC, cb, fLoopback, &SgLoop, &pSgBuf);
+ if (RT_SUCCESS(rc))
+ {
+ elnkXmitRead(pDevIns, pThis, cb, pSgBuf);
+ rc = elnkXmitSendBuf(pDevIns, pThis, pThisCC, fLoopback, pSgBuf, fOnWorkerThread);
+ Log2Func(("#%d: rc=%Rrc\n", pThis->iInstance, rc));
+ }
+ else if (rc == VERR_TRY_AGAIN)
+ {
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ LogFunc(("#%d: rc=%Rrc\n", pThis->iInstance, rc));
+ return VINF_SUCCESS;
+ }
+ if (RT_SUCCESS(rc))
+ pThis->XmitStat.ready = 1;
+ else
+ pThis->XmitStat.coll = 1; /* Pretend there was a collision. */
+ }
+ else
+ {
+ /* Signal error, as this violates the Ethernet specs. */
+ /** @todo check if the correct error is generated. */
+ LogRel(("3C501#%d: illegal giant frame (%u bytes) -> signalling error\n", pThis->iInstance, cb));
+ }
+ }
+ else
+ {
+ /* Signal a transmit error pretending there was a collision. */
+ pThis->cLinkDownReported++;
+ pThis->XmitStat.coll = 1;
+ }
+ /* Transmit officially done, update register state. */
+ pThis->AuxStat.xmit_bsy = 0;
+ pThis->IntrState.xmit_intr = !!(pThis->XmitCmdReg & pThis->XmitStatReg);
+ LogFlowFunc(("#%d: XmitCmd=%02X, XmitStat=%02X\n", pThis->iInstance, pThis->XmitCmdReg, pThis->XmitStatReg));
+
+ /* NB: After a transmit, the GP Buffer Pointer points just past
+ * the end of the packet buffer (3C501 diagnostics).
+ */
+ pThis->uGPBufPtr = ELNK_BUF_SIZE;
+
+ /* NB: The buffer control does *not* change to Receive and stays the way it was. */
+ if (RT_UNLIKELY(!fLoopback))
+ {
+ pThis->AuxStat.recv_bsy = 1; /* Receive Busy now set until a packet is received. */
+ elnkKickReceive(pDevIns, pThis);
+ }
+ } while (0); /* No loop, because there isn't ever more than one packet to transmit. */
+
+ elnkUpdateIrq(pDevIns, pThis);
+
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
+
+ return VINF_SUCCESS;
+}
+
+/* -=-=-=-=-=- I/O Port access -=-=-=-=-=- */
+
+
+static int elnkCsrWrite(PPDMDEVINS pDevIns, PELNKSTATE pThis, uint8_t data)
+{
+ int rc = VINF_SUCCESS;
+ bool fTransmit = false;
+ bool fReceive = false;
+ bool fDMAR;
+ union {
+ uint8_t reg;
+ EL_AUX_CMD val;
+ };
+
+ reg = data;
+
+ /* Handle reset first. */
+ if (pThis->AuxCmd.reset != val.reset)
+ {
+ if (val.reset)
+ {
+ /* Card is placed into reset. Just set the flag. NB: When in reset
+ * state, we permit writes to other registers, but those have no
+ * effect and will be overwritten when the card is taken out of reset.
+ */
+ LogFunc(("#%d: Card going into reset\n", pThis->iInstance));
+ pThis->fInReset = true;
+
+ /* Many EtherLink drivers like to reset the card a lot. That can lead to
+ * packet loss if a packet was already received before the card was reset.
+ */
+ if (RT_UNLIKELY(!pThis->RcvStat.stale))
+ STAM_REL_COUNTER_INC(&pThis->StatPktsLostReset);
+ }
+ else
+ {
+ /* Card is being taken out of reset. */
+ LogFunc(("#%d: Card going out of reset\n", pThis->iInstance));
+ STAM_COUNTER_INC(&pThis->StatResets);
+ elnkSoftReset(pDevIns, pThis);
+ }
+ pThis->AuxCmd.reset = val.reset; /* Update the reset bit, if nothing else. */
+ }
+
+ /* If the card is in reset, stop right here. */
+ if (pThis->fInReset)
+ return rc;
+
+ /* Evaluate DMA state. If it changed, we'll have to go back to R3. */
+ fDMAR = val.dma_req && val.ride;
+ if (fDMAR != pThis->fDMA)
+#ifdef IN_RING3
+ {
+ /* Start/stop DMA as requested. */
+ pThis->fDMA = fDMAR;
+ PDMDevHlpDMASetDREQ(pDevIns, pThis->uIsaDma, fDMAR);
+ if (fDMAR)
+ PDMDevHlpDMASchedule(pDevIns);
+ Log(("3C501#%d: DMARQ for channel %u set to %u\n", pThis->iInstance, pThis->uIsaDma, fDMAR));
+ }
+#else
+ return VINF_IOM_R3_IOPORT_WRITE;
+#endif
+
+ /* Interrupt enable changes. */
+ if ((pThis->AuxCmd.ire != val.ire) || (pThis->AuxCmd.ride != val.ride))
+ {
+ pThis->AuxStat.ride = pThis->AuxCmd.ride = val.ride;
+ pThis->AuxCmd.ire = val.ire; /* NB: IRE is not visible in the aux status register. */
+ }
+
+ /* DMA Request changes. */
+ if (pThis->AuxCmd.dma_req != val.dma_req)
+ {
+ pThis->AuxStat.dma_req = pThis->AuxCmd.dma_req = val.dma_req;
+ if (!val.dma_req)
+ {
+ /* Clearing the DMA Request bit also clears the DMA Done status bit and any DMA interrupt. */
+ pThis->IntrState.dma_intr = 0;
+ pThis->AuxStat.dma_done = 0;
+ }
+ }
+
+ /* Packet buffer control changes. */
+ if (pThis->AuxCmd.buf_ctl != val.buf_ctl)
+ {
+#ifdef LOG_ENABLED
+ static const char *apszBuffCntrl[4] = { "System", "Xmit then Recv", "Receive", "Loopback" };
+ Log(("3C501#%d: Packet buffer control `%s' -> `%s'\n", pThis->iInstance, apszBuffCntrl[pThis->AuxCmd.buf_ctl], apszBuffCntrl[val.buf_ctl]));
+#endif
+ if (val.buf_ctl == EL_BCTL_XMT_RCV)
+ {
+ /* Transmit, then receive. */
+ Log2(("3C501#%d: Transmit %u bytes\n%Rhxs\nxmit_bsy=%u\n", pThis->iInstance, ELNK_BUF_SIZE - pThis->uGPBufPtr, &pThis->abPacketBuf[pThis->uGPBufPtr], pThis->AuxStat.xmit_bsy));
+ fTransmit = true;
+ pThis->AuxStat.recv_bsy = 0;
+ }
+ else if (val.buf_ctl == EL_BCTL_SYSTEM)
+ {
+ pThis->AuxStat.xmit_bsy = 1; /* Transmit Busy is set here and cleared once actual transmit completes. */
+ pThis->AuxStat.recv_bsy = 0;
+ }
+ else if (val.buf_ctl == EL_BCTL_RECEIVE)
+ {
+ /* Special case: If going from xmit-then-receive mode to receive mode, and we received
+ * a packet already (right after the receive), don't restart receive and lose the already
+ * received packet.
+ */
+ if (!pThis->uRCVBufPtr)
+ fReceive = true;
+ }
+ else
+ {
+ /* For loopback, we go through the regular transmit and receive path. That may be an
+ * overkill but the receive path is too complex for a special loopback-only case.
+ */
+ fTransmit = true;
+ pThis->AuxStat.recv_bsy = 1; /* Receive Busy now set until a packet is received. */
+ }
+ pThis->AuxStat.buf_ctl = pThis->AuxCmd.buf_ctl = val.buf_ctl;
+ }
+
+ /* NB: Bit 1 (xmit_bf, transmit packets with bad FCS) is a simple control
+ * bit which does not require special handling here. Just copy it over.
+ */
+ pThis->AuxStat.xmit_bf = pThis->AuxCmd.xmit_bf = val.xmit_bf;
+
+ /* There are multiple bits that affect interrupt state. Handle them now. */
+ elnkUpdateIrq(pDevIns, pThis);
+
+ /* After fully updating register state, do a transmit (including loopback) or receive. */
+ if (fTransmit)
+ elnkTransmit(pDevIns, pThis);
+ else if (fReceive)
+ {
+ pThis->AuxStat.recv_bsy = 1; /* Receive Busy now set until a packet is received. */
+ elnkKickReceive(pDevIns, pThis);
+ }
+
+ return rc;
+}
+
+static int elIoWrite(PPDMDEVINS pDevIns, PELNKSTATE pThis, uint32_t addr, uint32_t val)
+{
+ int reg = addr & 0xf;
+ int rc = VINF_SUCCESS;
+
+ Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val & 0xff));
+
+ switch (reg)
+ {
+ case 0x00: /* Six bytes of station address. */
+ case 0x01:
+ case 0x02:
+ case 0x03:
+ case 0x04:
+ case 0x05:
+ pThis->aStationAddr[reg] = val;
+ break;
+
+ case 0x06: /* Receive command. */
+ {
+ EL_RCV_CMD OldRcvCmd = pThis->RcvCmd;
+
+ pThis->RcvCmdReg = val;
+ /* If address filter just got enabled, receive may need a kick. */
+ if (OldRcvCmd.adr_match == EL_ADRM_DISABLED && pThis->RcvCmd.adr_match != EL_ADRM_DISABLED)
+ elnkKickReceive(pDevIns, pThis);
+ Log2(("Receive Command register set to %02X\n", pThis->RcvCmdReg));
+ break;
+ }
+
+ case 0x07: /* Transmit command. */
+ pThis->XmitCmdReg = val;
+ Log2(("Transmit Command register set to %02X\n", pThis->XmitCmdReg));
+ break;
+
+ case 0x08: /* GP Buffer pointer LSB. */
+ pThis->uGPBufPtr = (pThis->uGPBufPtr & 0xff00) | (uint8_t)val;
+ Log2(("GP Buffer Pointer LSB write, now %u\n", pThis->uGPBufPtr));
+ break;
+
+ case 0x09: /* GP Buffer pointer MSB. */
+ pThis->uGPBufPtr = ((uint8_t)val << 8) | RT_LOBYTE(pThis->uGPBufPtr);
+ Log2(("GP Buffer Pointer MSB write, now %u\n", pThis->uGPBufPtr));
+ break;
+
+ case 0x0a: /* RCV Buffer pointer clear. */
+ pThis->uRCVBufPtr = 0;
+ Log2(("RCV Buffer Pointer cleared (%02X)\n", val));
+ break;
+
+ case 0x0b: /* RCV buffer pointer MSB. */
+ case 0x0c: /* Ethernet address PROM window. */
+ case 0x0d: /* Undocumented. */
+ Log(("Writing read-only register %02X!\n", reg));
+ break;
+
+ case 0x0e: /* Auxiliary Command (CSR). */
+ rc = elnkCsrWrite(pDevIns, pThis, val);
+ break;
+
+ case 0x0f: /* Buffer window. */
+ /* Writes use low 11 bits of GP buffer pointer, auto-increment. */
+ if (pThis->AuxCmd.buf_ctl != EL_BCTL_SYSTEM)
+ {
+ Log(("Packet buffer write ignored, buf_ctl=%u!\n", pThis->AuxCmd.buf_ctl));
+ /// @todo Does this still increment GPBufPtr?
+ break;
+ }
+ pThis->abPacketBuf[ELNK_GP(pThis)] = val;
+ pThis->uGPBufPtr = (pThis->uGPBufPtr + 1) & ELNK_GP_MASK;
+ break;
+ }
+
+ return rc;
+}
+
+static uint32_t elIoRead(PPDMDEVINS pDevIns, PELNKSTATE pThis, uint32_t addr, int *pRC)
+{
+ uint32_t val = UINT32_MAX;
+
+ *pRC = VINF_SUCCESS;
+
+ switch (addr & 0x0f)
+ {
+ case 0x00: /* Receive status register aliases. The SEEQ 8001 */
+ case 0x02: /* EDLC clearly only decodes one bit for reads. */
+ case 0x04:
+ case 0x06: /* Receive status register. */
+ val = pThis->RcvStatReg;
+ pThis->RcvStat.stale = 1; /* Allows further reception. */
+ pThis->IntrState.recv_intr = 0; /* Reading clears receive interrupt. */
+ elnkUpdateIrq(pDevIns, pThis);
+ break;
+
+ case 0x01: /* Transmit status register aliases. */
+ case 0x03:
+ case 0x05:
+ case 0x07: /* Transmit status register. */
+ val = pThis->XmitStatReg;
+ pThis->IntrState.xmit_intr = 0; /* Reading clears transmit interrupt. */
+ elnkUpdateIrq(pDevIns, pThis);
+ break;
+
+ case 0x08: /* GP Buffer pointer LSB. */
+ val = RT_LOBYTE(pThis->uGPBufPtr);
+ break;
+
+ case 0x09: /* GP Buffer pointer MSB. */
+ val = RT_HIBYTE(pThis->uGPBufPtr);
+ break;
+
+ case 0x0a: /* RCV Buffer pointer LSB. */
+ val = RT_LOBYTE(pThis->uRCVBufPtr);
+ break;
+
+ case 0x0b: /* RCV Buffer pointer MSB. */
+ val = RT_HIBYTE(pThis->uRCVBufPtr);
+ break;
+
+ case 0x0c: /* Ethernet address PROM window. */
+ case 0x0d: /* Alias. */
+ /* Reads use low 3 bits of GP buffer pointer, no auto-increment. */
+ val = pThis->aPROM[pThis->uGPBufPtr & 7];
+ break;
+
+ case 0x0e: /* Auxiliary status register. */
+ val = pThis->AuxStatReg;
+ break;
+
+ case 0x0f: /* Buffer window. */
+ /* Reads use low 11 bits of GP buffer pointer, auto-increment. */
+ val = pThis->abPacketBuf[ELNK_GP(pThis)];
+ pThis->uGPBufPtr = (pThis->uGPBufPtr + 1) & ELNK_GP_MASK;
+ break;
+ }
+
+ elnkUpdateIrq(pDevIns, pThis);
+
+ Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val & 0xff));
+ return val;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTIN}
+ */
+static DECLCALLBACK(VBOXSTRICTRC)
+elnkIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
+{
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+ int rc = VINF_SUCCESS;
+ uint8_t u8Lo, u8Hi;
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ RT_NOREF_PV(pvUser);
+
+ switch (cb)
+ {
+ case 1:
+ *pu32 = elIoRead(pDevIns, pThis, Port, &rc);
+ break;
+ case 2:
+ /* Manually split word access. */
+ u8Lo = elIoRead(pDevIns, pThis, Port + 0, &rc);
+ Assert(RT_SUCCESS(rc));
+ u8Hi = elIoRead(pDevIns, pThis, Port + 1, &rc);
+ Assert(RT_SUCCESS(rc));
+ *pu32 = RT_MAKE_U16(u8Lo, u8Hi);
+ break;
+ default:
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
+ "elnkIOPortRead: unsupported operation size: offset=%#10x cb=%u\n",
+ Port, cb);
+ }
+
+ Log2Func(("#%d: Port=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, *pu32, cb, rc));
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT}
+ */
+static DECLCALLBACK(VBOXSTRICTRC)
+elnkIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
+{
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+ int rc = VINF_SUCCESS;
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIOWrite), a);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ RT_NOREF_PV(pvUser);
+
+ switch (cb)
+ {
+ case 1:
+ rc = elIoWrite(pDevIns, pThis, Port, RT_LOBYTE(u32));
+ break;
+ case 2:
+ /* Manually split word access. */
+ rc = elIoWrite(pDevIns, pThis, Port + 0, RT_LOBYTE(u32));
+ if (!RT_SUCCESS(rc))
+ break;
+ rc = elIoWrite(pDevIns, pThis, Port + 1, RT_HIBYTE(u32));
+ break;
+ default:
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
+ "elnkIOPortWrite: unsupported operation size: offset=%#10x cb=%u\n",
+ Port, cb);
+ }
+
+ Log2Func(("#%d: Port=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, u32, cb, rc));
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIOWrite), a);
+ return rc;
+}
+
+
+#ifdef IN_RING3
+
+/* Shamelessly stolen from DevDMA.cpp */
+
+/* Test the decrement bit of mode register. */
+#define IS_MODE_DEC(c) ((c) & 0x20)
+/* Test the auto-init bit of mode register. */
+#define IS_MODE_AI(c) ((c) & 0x10)
+/* Extract the transfer type bits of mode register. */
+#define GET_MODE_XTYP(c) (((c) & 0x0c) >> 2)
+
+/* DMA transfer modes. */
+enum {
+ DMODE_DEMAND, /* Demand transfer mode. */
+ DMODE_SINGLE, /* Single transfer mode. */
+ DMODE_BLOCK, /* Block transfer mode. */
+ DMODE_CASCADE /* Cascade mode. */
+};
+
+/* DMA transfer types. */
+enum {
+ DTYPE_VERIFY, /* Verify transfer type. */
+ DTYPE_WRITE, /* Write transfer type. */
+ DTYPE_READ, /* Read transfer type. */
+ DTYPE_ILLEGAL /* Undefined. */
+};
+
+static DECLCALLBACK(uint32_t) elnkR3DMAXferHandler(PPDMDEVINS pDevIns, void *opaque,
+ unsigned nchan, uint32_t dma_pos, uint32_t dma_len)
+{
+ RT_NOREF(pDevIns);
+ PELNKSTATE pThis = (PELNKSTATE)opaque;
+ int dma_mode;
+ int dma_type;
+ uint32_t cbToXfer;
+ uint32_t cbXferred;
+ uint16_t uLastPos;
+ int rc;
+
+ /*
+ * The 3C501 EtherLink uses DMA as an alternative to accessing
+ * the buffer window register. The GP Buffer Pointer controls
+ * the address into the packet buffer for both writing to and
+ * reading from the buffer.
+ */
+ dma_mode = PDMDevHlpDMAGetChannelMode(pDevIns, pThis->uIsaDma);
+ dma_type = GET_MODE_XTYP(dma_mode);
+ LogFlowFunc(("dma_mode=%d, dma_type=%d, dma_pos=%u, dma_len=%u, GPBP=%u\n", dma_mode, dma_type, dma_pos, dma_len, pThis->uGPBufPtr));
+
+ cbToXfer = dma_len;
+
+ if (dma_type == DTYPE_WRITE)
+ {
+ /* Write transfer type. Reading from device, writing to memory. */
+ rc = PDMDevHlpDMAWriteMemory(pDevIns, nchan,
+ &pThis->abPacketBuf[ELNK_GP(pThis)],
+ dma_pos, cbToXfer, &cbXferred);
+ AssertMsgRC(rc, ("DMAWriteMemory -> %Rrc\n", rc));
+ uLastPos = pThis->uRCVBufPtr;
+ }
+ else
+ {
+ /* Read of Verify transfer type. Reading from memory, writing to device. */
+ rc = PDMDevHlpDMAReadMemory(pDevIns, nchan,
+ &pThis->abPacketBuf[ELNK_GP(pThis)],
+ dma_pos, cbToXfer, &cbXferred);
+ AssertMsgRC(rc, ("DMAReadMemory -> %Rrc\n", rc));
+ uLastPos = 0; /* Stop when buffer address wraps back to zero. */
+ }
+ Log2Func(("After DMA transfer: GPBufPtr=%u, lastpos=%u, cbXferred=%u\n", pThis->uGPBufPtr, uLastPos, cbXferred));
+
+ /* Advance the GP buffer pointer and see if transfer completed (it almost certainly did). */
+ pThis->uGPBufPtr = (pThis->uGPBufPtr + cbXferred) & ELNK_GP_MASK;
+ if (ELNK_GP(pThis) == uLastPos || 1)
+ {
+ Log2(("DMA completed\n"));
+ PDMDevHlpDMASetDREQ(pDevIns, pThis->uIsaDma, 0);
+ pThis->IntrState.dma_intr = 1;
+ pThis->AuxStat.dma_done = 1;
+ elnkUpdateIrq(pDevIns, pThis);
+ }
+ else
+ {
+ Log(("DMA continuing: GPBufPtr=%u, lastpos=%u, cbXferred=%u\n", pThis->uGPBufPtr, uLastPos, cbXferred));
+ PDMDevHlpDMASchedule(pDevIns);
+ }
+
+ /* Returns the updated transfer count. */
+ return dma_pos + cbXferred;
+}
+
+
+/* -=-=-=-=-=- Timer Callbacks -=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNTMTIMERDEV, Restore timer callback}
+ *
+ * This is only called when we restore a saved state and temporarily
+ * disconnected the network link to inform the guest that network connections
+ * should be considered lost.
+ */
+static DECLCALLBACK(void) elnkR3TimerRestore(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
+{
+ RT_NOREF(pvUser);
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+ int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ AssertReleaseRC(rc);
+
+ rc = VERR_GENERAL_FAILURE;
+
+ /* The EhterLink cards have no concept of a link state, and cables were assumed to be
+ * permanently attached (AUI or BNC). We can simulate a disconnected cable by reporting
+ * collisions on transmit, but a guest that waits to receive something will never know.
+ * For that reason, the link is temporarily down, we will only postpone restoring it
+ * a couple of times, and then reconnect regardless of whether the guest noticed
+ * anything or not.
+ */
+ if ( (pThis->cLinkDownReported <= ELNK_MAX_LINKDOWN_REPORTED)
+ && (pThis->cLinkRestorePostponed <= ELNK_MAX_LINKRST_POSTPONED))
+ rc = PDMDevHlpTimerSetMillies(pDevIns, hTimer, 1500);
+ if (RT_FAILURE(rc))
+ {
+ pThis->fLinkTempDown = false;
+ if (pThis->fLinkUp)
+ {
+ LogRel(("3C501#%d: The link is back up again after the restore.\n",
+ pThis->iInstance));
+ LogFunc(("#%d: cLinkDownReported=%d\n",
+ pThis->iInstance, pThis->cLinkDownReported));
+ pThis->Led.Actual.s.fError = 0;
+ }
+ }
+ else
+ {
+ LogFunc(("#%d: cLinkDownReported=%d, cLinkRestorePostponed=%d, wait another 1500ms...\n",
+ pThis->iInstance, pThis->cLinkDownReported, pThis->cLinkRestorePostponed));
+ pThis->cLinkRestorePostponed++;
+ }
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+}
+
+
+/* -=-=-=-=-=- Debug Info Handler -=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV}
+ */
+static DECLCALLBACK(void) elnkR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+ bool fStationAddr = false;
+ bool fRecvBuffer = false;
+ bool fSendBuffer = false;
+ static const char *apszAddrMatch[4] = { "Disabled", "Promiscuous", "Broadcast", "Multicast" };
+ static const char *apszBuffCntrl[4] = { "System", "Xmit then Recv", "Receive", "Loopback" };
+ /*
+ * Parse args.
+ */
+ if (pszArgs)
+ {
+ fStationAddr = strstr(pszArgs, "verbose") || strstr(pszArgs, "addr");
+ fRecvBuffer = strstr(pszArgs, "verbose") || strstr(pszArgs, "recvbuf");
+ fSendBuffer = strstr(pszArgs, "verbose") || strstr(pszArgs, "sendbuf");
+ }
+
+ /*
+ * Show info.
+ */
+ pHlp->pfnPrintf(pHlp,
+ "3C501 #%d: port=%RTiop IRQ=%u DMA=%u mac-cfg=%RTmac%s%s %s\n",
+ pThis->iInstance,
+ pThis->IOPortBase, pThis->uIsaIrq, pThis->uIsaDma, &pThis->MacConfigured,
+ pDevIns->fRCEnabled ? " RC" : "", pDevIns->fR0Enabled ? " RZ" : "",
+ pThis->fDriverAttached ? "attached" : "unattached!");
+
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_INTERNAL_ERROR); /* Take it here so we know why we're hanging... */
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
+
+ pHlp->pfnPrintf(pHlp, " GP Buf Ptr : %u (masked %u)\n", pThis->uGPBufPtr, ELNK_GP(pThis));
+ pHlp->pfnPrintf(pHlp, " RCV Buf Ptr: %u\n", pThis->uRCVBufPtr);
+ pHlp->pfnPrintf(pHlp, " Recv Command: %02X Recv Status: %02X\n", pThis->RcvCmdReg, pThis->RcvStatReg);
+ pHlp->pfnPrintf(pHlp, " Xmit Command: %02X Xmit Status: %02X\n", pThis->XmitCmdReg, pThis->XmitStatReg);
+ pHlp->pfnPrintf(pHlp, " Aux Command: %02X Aux Status: %02X\n", pThis->AuxCmdReg, pThis->AuxStatReg);
+
+ pHlp->pfnPrintf(pHlp, " Address matching: %s\n", apszAddrMatch[pThis->RcvCmd.adr_match]);
+ pHlp->pfnPrintf(pHlp, " Buffer control : %s\n", apszBuffCntrl[pThis->AuxCmd.buf_ctl]);
+ pHlp->pfnPrintf(pHlp, " Interrupt state : xmit=%u recv=%u dma=%u\n", pThis->IntrState.xmit_intr, pThis->IntrState.recv_intr, pThis->IntrState.dma_intr);
+ if (pThis->fLinkTempDown)
+ {
+ pHlp->pfnPrintf(pHlp, " Link down count : %d\n", pThis->cLinkDownReported);
+ pHlp->pfnPrintf(pHlp, " Postpone count : %d\n", pThis->cLinkRestorePostponed);
+ }
+
+ /* Dump the station address. */
+ if (fStationAddr)
+ {
+ pHlp->pfnPrintf(pHlp, " Station address : %RTmac\n", &pThis->aStationAddr);
+ }
+
+ /* Dump the beginning of the send buffer. */
+ if (fSendBuffer)
+ {
+ pHlp->pfnPrintf(pHlp, "Send buffer (start at %u):\n", ELNK_GP(pThis));
+ unsigned dump_end = RT_MIN((ELNK_GP(pThis)) + 64, sizeof(pThis->abPacketBuf) - 16);
+ for (unsigned ofs = ELNK_GP(pThis); ofs < dump_end; ofs += 16)
+ pHlp->pfnPrintf(pHlp, " %04X: %Rhxs\n", ofs, &pThis->abPacketBuf[ofs]);
+ pHlp->pfnPrintf(pHlp, "pktbuf at %p, end at %p\n", &pThis->abPacketBuf[ELNK_GP(pThis)], &pThis->abPacketBuf[ELNK_BUF_SIZE]);
+ }
+
+ /* Dump the beginning of the receive buffer. */
+ if (fRecvBuffer)
+ {
+ pHlp->pfnPrintf(pHlp, "Receive buffer (start at 0):\n");
+ unsigned dump_end = RT_MIN(pThis->uRCVBufPtr, 64);
+ for (unsigned ofs = 0; ofs < dump_end; ofs += 16)
+ pHlp->pfnPrintf(pHlp, " %04X: %Rhxs\n", ofs, &pThis->abPacketBuf[ofs]);
+ pHlp->pfnPrintf(pHlp, "pktbuf at %p, end at %p\n", pThis->abPacketBuf, &pThis->abPacketBuf[pThis->uRCVBufPtr]);
+ }
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+}
+
+
+/* -=-=-=-=-=- Helper(s) -=-=-=-=-=- */
+
+
+static void elnkR3HardReset(PPDMDEVINS pDevIns, PELNKSTATE pThis)
+{
+ LogFlowFunc(("#%d:\n", pThis->iInstance));
+
+ /* Initialize the PROM */
+ Assert(sizeof(pThis->MacConfigured) == 6);
+ memcpy(pThis->aPROM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
+ pThis->aPROM[6] = pThis->aPROM[7] = 0; /* The two padding bytes. */
+
+ /* Clear the packet buffer and station address. */
+ memset(pThis->abPacketBuf, 0, sizeof(pThis->abPacketBuf));
+ memset(pThis->aStationAddr, 0, sizeof(pThis->aStationAddr));
+
+ /* Reset the buffer pointers. */
+ pThis->uGPBufPtr = 0;
+ pThis->uRCVBufPtr = 0;
+
+ elnkSoftReset(pDevIns, pThis);
+}
+
+/**
+ * Takes down the link temporarily if it's current status is up.
+ *
+ * This is used during restore and when replumbing the network link.
+ *
+ * The temporary link outage is supposed to indicate to the OS that all network
+ * connections have been lost and that it for instance is appropriate to
+ * renegotiate any DHCP lease.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device instance data.
+ */
+static void elnkTempLinkDown(PPDMDEVINS pDevIns, PELNKSTATE pThis)
+{
+ if (pThis->fLinkUp)
+ {
+ pThis->fLinkTempDown = true;
+ pThis->cLinkDownReported = 0;
+ pThis->cLinkRestorePostponed = 0;
+ pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
+ int rc = PDMDevHlpTimerSetMillies(pDevIns, pThis->hTimerRestore, pThis->cMsLinkUpDelay);
+ AssertRC(rc);
+ }
+}
+
+
+/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNSSMDEVLIVEEXEC, Pass 0 only.}
+ */
+static DECLCALLBACK(int) elnkLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
+{
+ RT_NOREF(uPass);
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+ pDevIns->pHlpR3->pfnSSMPutMem(pSSM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
+ return VINF_SSM_DONT_CALL_AGAIN;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEPREP,
+ * Serializes the receive thread, it may be working inside the critsect.}
+ */
+static DECLCALLBACK(int) elnkSavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ RT_NOREF(pSSM);
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+
+ int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ AssertRC(rc);
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC}
+ */
+static DECLCALLBACK(int) elnkSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ pHlp->pfnSSMPutU16(pSSM, pThis->uGPBufPtr);
+ pHlp->pfnSSMPutU16(pSSM, pThis->uRCVBufPtr);
+ pHlp->pfnSSMPutU8(pSSM, pThis->XmitCmdReg);
+ pHlp->pfnSSMPutU8(pSSM, pThis->XmitStatReg);
+ pHlp->pfnSSMPutU8(pSSM, pThis->RcvCmdReg);
+ pHlp->pfnSSMPutU8(pSSM, pThis->RcvStatReg);
+ pHlp->pfnSSMPutU8(pSSM, pThis->AuxCmdReg);
+ pHlp->pfnSSMPutU8(pSSM, pThis->AuxStatReg);
+
+ pHlp->pfnSSMPutU8(pSSM, pThis->IntrStateReg);
+ pHlp->pfnSSMPutBool(pSSM, pThis->fInReset);
+ pHlp->pfnSSMPutBool(pSSM, pThis->fLinkUp);
+ pHlp->pfnSSMPutBool(pSSM, pThis->fISR);
+ pHlp->pfnSSMPutMem(pSSM, pThis->aStationAddr, sizeof(pThis->aStationAddr));
+
+ /* Save the configured MAC address. */
+ pHlp->pfnSSMPutMem(pSSM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADPREP},
+ * Serializes the receive thread, it may be working inside the critsect.}
+ */
+static DECLCALLBACK(int) elnkLoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+ RT_NOREF(pSSM);
+
+ int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ AssertRC(rc);
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ */
+static DECLCALLBACK(int) elnkLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+ PELNKSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PELNKSTATECC);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ if (SSM_VERSION_MAJOR_CHANGED(uVersion, ELNK_SAVEDSTATE_VERSION))
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+
+ if (uPass == SSM_PASS_FINAL)
+ {
+ /* restore data */
+ pHlp->pfnSSMGetU16(pSSM, &pThis->uGPBufPtr);
+ pHlp->pfnSSMGetU16(pSSM, &pThis->uRCVBufPtr);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->XmitCmdReg);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->XmitStatReg);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->RcvCmdReg);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->RcvStatReg);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->AuxCmdReg);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->AuxStatReg);
+
+ pHlp->pfnSSMGetU8(pSSM, &pThis->IntrStateReg);
+ pHlp->pfnSSMGetBool(pSSM, &pThis->fInReset);
+ pHlp->pfnSSMGetBool(pSSM, &pThis->fLinkUp);
+ pHlp->pfnSSMGetBool(pSSM, &pThis->fISR);
+ pHlp->pfnSSMGetMem(pSSM, &pThis->aStationAddr, sizeof(pThis->aStationAddr));
+ }
+
+ /* check config */
+ RTMAC Mac;
+ int rc = pHlp->pfnSSMGetMem(pSSM, &Mac, sizeof(Mac));
+ AssertRCReturn(rc, rc);
+ if ( memcmp(&Mac, &pThis->MacConfigured, sizeof(Mac))
+ && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)) )
+ LogRel(("3C501#%u: The mac address differs: config=%RTmac saved=%RTmac\n", pThis->iInstance, &pThis->MacConfigured, &Mac));
+
+ if (uPass == SSM_PASS_FINAL)
+ {
+ /* update promiscuous mode. */
+ if (pThisCC->pDrv)
+ pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, 0 /* promiscuous enabled */);
+
+ /* Indicate link down to the guest OS that all network connections have
+ been lost, unless we've been teleported here. */
+ if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
+ elnkTempLinkDown(pDevIns, pThis);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/* -=-=-=-=-=- ELNKSTATE::INetworkDown -=-=-=-=-=- */
+
+/**
+ * Check if the device/driver can receive data now.
+ *
+ * Worker for elnkNet_WaitReceiveAvail(). This must be called before
+ * the pfnRecieve() method is called.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The device instance data.
+ * @param pThis The shared instance data.
+ */
+static int elnkCanReceive(PPDMDEVINS pDevIns, PELNKSTATE pThis)
+{
+ int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ AssertReleaseRC(rc);
+
+ rc = VINF_SUCCESS;
+
+ /*
+ * The real 3C501 is very limited in that the packet buffer can only hold one
+ * frame and and it is shared between transmit and receive, which means the card
+ * frequently drops packets on a busy network. We cheat a bit and try to hold
+ * off when it looks like receive is only temporarily unavailable.
+ *
+ * If the receiver is disabled, accept packet and drop it to avoid
+ * packet pile-ups. If it's enabled, take a closer look.
+ */
+#if 0
+ if (pThis->RcvCmd.adr_match != EL_ADRM_DISABLED) {
+ /* The 3C501 is only prepared to accept a packet if the receiver is busy.
+ * When not busy, try to delay packets.
+ */
+ if (!pThis->AuxStat.recv_bsy)
+ {
+ rc = VERR_NET_NO_BUFFER_SPACE;
+ }
+ }
+#else
+ if (pThis->RcvCmd.adr_match == EL_ADRM_DISABLED || !pThis->AuxStat.recv_bsy)
+ {
+ rc = VERR_NET_NO_BUFFER_SPACE;
+ }
+#endif
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
+ */
+static DECLCALLBACK(int) elnkNet_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
+{
+ PELNKSTATECC pThisCC = RT_FROM_MEMBER(pInterface, ELNKSTATECC, INetworkDown);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+
+ int rc = elnkCanReceive(pDevIns, pThis);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ if (RT_UNLIKELY(cMillies == 0))
+ return VERR_NET_NO_BUFFER_SPACE;
+
+ rc = VERR_INTERRUPTED;
+ ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, true);
+ STAM_PROFILE_START(&pThis->StatRxOverflow, a);
+ VMSTATE enmVMState;
+ while (RT_LIKELY( (enmVMState = PDMDevHlpVMState(pDevIns)) == VMSTATE_RUNNING
+ || enmVMState == VMSTATE_RUNNING_LS))
+ {
+ int rc2 = elnkCanReceive(pDevIns, pThis);
+ if (RT_SUCCESS(rc2))
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+ LogFlowFunc(("waiting cMillies=%u...\n", cMillies));
+
+ /* Start the poll timer once which will remain active as long fMaybeOutOfSpace
+ * is true -- even if (transmit) polling is disabled. */
+ rc2 = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ AssertReleaseRC(rc2);
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ RTSemEventWait(pThis->hEventOutOfRxSpace, cMillies);
+ }
+ STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
+ ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, false);
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
+ */
+static DECLCALLBACK(int) elnkNet_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
+{
+ PELNKSTATECC pThisCC = RT_FROM_MEMBER(pInterface, ELNKSTATECC, INetworkDown);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+ int rc;
+
+ STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
+ rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ AssertReleaseRC(rc);
+
+ if (cb > 50) /* unqualified guess */
+ pThis->Led.Asserted.s.fReading = pThis->Led.Actual.s.fReading = 1;
+ elnkReceiveLocked(pDevIns, pThis, (const uint8_t *)pvBuf, cb, false);
+ pThis->Led.Actual.s.fReading = 0;
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
+ */
+static DECLCALLBACK(void) elnkNet_XmitPending(PPDMINETWORKDOWN pInterface)
+{
+ PELNKSTATECC pThisCC = RT_FROM_MEMBER(pInterface, ELNKSTATECC, INetworkDown);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+
+ elnkXmitBuffer(pDevIns, pThis, pThisCC, true /*fOnWorkerThread*/);
+}
+
+
+/* -=-=-=-=-=- ELNKSTATE::INetworkConfig -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
+ */
+static DECLCALLBACK(int) elnkGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
+{
+ PELNKSTATECC pThisCC = RT_FROM_MEMBER(pInterface, ELNKSTATECC, INetworkConfig);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+
+ LogFlowFunc(("#%d\n", pThis->iInstance));
+ /// @todo This is broken!! We can't properly get the MAC address set by the guest
+#if 0
+ memcpy(pMac, pThis->aStationAddr, sizeof(*pMac));
+#else
+ memcpy(pMac, pThis->aPROM, sizeof(*pMac));
+#endif
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
+ */
+static DECLCALLBACK(PDMNETWORKLINKSTATE) elnkGetLinkState(PPDMINETWORKCONFIG pInterface)
+{
+ PELNKSTATECC pThisCC = RT_FROM_MEMBER(pInterface, ELNKSTATECC, INetworkConfig);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+
+ if (pThis->fLinkUp && !pThis->fLinkTempDown)
+ return PDMNETWORKLINKSTATE_UP;
+ if (!pThis->fLinkUp)
+ return PDMNETWORKLINKSTATE_DOWN;
+ if (pThis->fLinkTempDown)
+ return PDMNETWORKLINKSTATE_DOWN_RESUME;
+ AssertMsgFailed(("Invalid link state!\n"));
+ return PDMNETWORKLINKSTATE_INVALID;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
+ */
+static DECLCALLBACK(int) elnkSetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
+{
+ PELNKSTATECC pThisCC = RT_FROM_MEMBER(pInterface, ELNKSTATECC, INetworkConfig);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+ bool fLinkUp;
+
+ AssertMsgReturn(enmState > PDMNETWORKLINKSTATE_INVALID && enmState <= PDMNETWORKLINKSTATE_DOWN_RESUME,
+ ("Invalid link state: enmState=%d\n", enmState), VERR_INVALID_PARAMETER);
+
+ if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
+ {
+ elnkTempLinkDown(pDevIns, pThis);
+ /*
+ * Note that we do not notify the driver about the link state change because
+ * the change is only temporary and can be disregarded from the driver's
+ * point of view (see @bugref{7057}).
+ */
+ return VINF_SUCCESS;
+ }
+ /* has the state changed? */
+ fLinkUp = enmState == PDMNETWORKLINKSTATE_UP;
+ if (pThis->fLinkUp != fLinkUp)
+ {
+ pThis->fLinkUp = fLinkUp;
+ if (fLinkUp)
+ {
+ /* Connect with a configured delay. */
+ pThis->fLinkTempDown = true;
+ pThis->cLinkDownReported = 0;
+ pThis->cLinkRestorePostponed = 0;
+ pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
+ int rc = PDMDevHlpTimerSetMillies(pDevIns, pThis->hTimerRestore, pThis->cMsLinkUpDelay);
+ AssertRC(rc);
+ }
+ else
+ {
+ /* Disconnect. */
+ pThis->cLinkDownReported = 0;
+ pThis->cLinkRestorePostponed = 0;
+ pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
+ }
+ Assert(!PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ if (pThisCC->pDrv)
+ pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/* -=-=-=-=-=- ELNKSTATE::ILeds (LUN#0) -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
+ */
+static DECLCALLBACK(int) elnkQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
+{
+ PELNKSTATECC pThisCC = RT_FROM_MEMBER(pInterface, ELNKSTATECC, ILeds);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+ if (iLUN == 0)
+ {
+ *ppLed = &pThis->Led;
+ return VINF_SUCCESS;
+ }
+ return VERR_PDM_LUN_NOT_FOUND;
+}
+
+
+/* -=-=-=-=-=- ELNKSTATE::IBase (LUN#0) -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) elnkQueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
+{
+ PELNKSTATECC pThisCC = RT_FROM_MEMBER(pInterface, ELNKSTATECC, IBase);
+ Assert(&pThisCC->IBase == pInterface);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
+ return NULL;
+}
+
+
+/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnPowerOff}
+ */
+static DECLCALLBACK(void) elnkR3PowerOff(PPDMDEVINS pDevIns)
+{
+ /* Poke thread waiting for buffer space. */
+ elnkR3WakeupReceive(pDevIns);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDetach}
+ *
+ * One port on the network card has been disconnected from the network.
+ */
+static DECLCALLBACK(void) elnkR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+ PELNKSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PELNKSTATECC);
+ RT_NOREF(fFlags);
+ LogFlowFunc(("#%d:\n", pThis->iInstance));
+
+ AssertLogRelReturnVoid(iLUN == 0);
+
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
+
+ /*
+ * Zero some important members.
+ */
+ pThis->fDriverAttached = false;
+ pThisCC->pDrvBase = NULL;
+ pThisCC->pDrv = NULL;
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnAttach}
+ * One port on the network card has been connected to a network.
+ */
+static DECLCALLBACK(int) elnkR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+ PELNKSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PELNKSTATECC);
+ RT_NOREF(fFlags);
+ LogFlowFunc(("#%d:\n", pThis->iInstance));
+
+ AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
+
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
+
+ /*
+ * Attach the driver.
+ */
+ int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
+ if (RT_SUCCESS(rc))
+ {
+ pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
+ AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
+ rc = VERR_PDM_MISSING_INTERFACE_BELOW);
+ pThis->fDriverAttached = true;
+ }
+ else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
+ || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
+ {
+ /* This should never happen because this function is not called
+ * if there is no driver to attach! */
+ Log(("#%d: No attached driver!\n", pThis->iInstance));
+ }
+
+ /*
+ * Temporary set the link down if it was up so that the guest
+ * will know that we have change the configuration of the
+ * network card
+ */
+ if (RT_SUCCESS(rc))
+ elnkTempLinkDown(pDevIns, pThis);
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnSuspend}
+ */
+static DECLCALLBACK(void) elnkR3Suspend(PPDMDEVINS pDevIns)
+{
+ /* Poke thread waiting for buffer space. */
+ elnkR3WakeupReceive(pDevIns);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnReset}
+ */
+static DECLCALLBACK(void) elnkR3Reset(PPDMDEVINS pDevIns)
+{
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+ if (pThis->fLinkTempDown)
+ {
+ pThis->cLinkDownReported = 0x1000;
+ pThis->cLinkRestorePostponed = 0x1000;
+ PDMDevHlpTimerStop(pDevIns, pThis->hTimerRestore);
+ elnkR3TimerRestore(pDevIns, pThis->hTimerRestore, pThis);
+ }
+
+ /** @todo How to flush the queues? */
+ elnkR3HardReset(pDevIns, pThis);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnRelocate}
+ */
+static DECLCALLBACK(void) elnkR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
+{
+ PELNKSTATERC pThisRC = PDMINS_2_DATA_RC(pDevIns, PELNKSTATERC);
+ pThisRC->pDrv += offDelta;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) elnkR3Destruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+
+ if (PDMDevHlpCritSectIsInitialized(pDevIns, &pThis->CritSect))
+ {
+ RTSemEventSignal(pThis->hEventOutOfRxSpace);
+ RTSemEventDestroy(pThis->hEventOutOfRxSpace);
+ pThis->hEventOutOfRxSpace = NIL_RTSEMEVENT;
+ PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSect);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) elnkR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+ PELNKSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PELNKSTATECC);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ PPDMIBASE pBase;
+ char szTmp[128];
+ int rc;
+
+ /*
+ * Init what's required to make the destructor safe.
+ */
+ pThis->iInstance = iInstance;
+ pThis->hEventOutOfRxSpace = NIL_RTSEMEVENT;
+ pThis->hIoPortsIsa = NIL_IOMIOPORTHANDLE;
+ pThisCC->pDevIns = pDevIns;
+
+ /*
+ * Validate configuration.
+ */
+ PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC|CableConnected|Port|IRQ|DMA|LinkUpDelay|LineSpeed", "");
+
+ /*
+ * Read the configuration.
+ */
+ rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", &pThis->MacConfigured, sizeof(pThis->MacConfigured));
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the \"MAC\" value"));
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "CableConnected", &pThis->fLinkUp, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the \"CableConnected\" value"));
+
+ /*
+ * Process ISA configuration options.
+ */
+ rc = pHlp->pfnCFGMQueryPortDef(pCfg, "Port", &pThis->IOPortBase, 0x300);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the \"Port\" value"));
+
+ rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IRQ", &pThis->uIsaIrq, 3);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the \"IRQ\" value"));
+
+ rc = pHlp->pfnCFGMQueryU8Def(pCfg, "DMA", &pThis->uIsaDma, 1);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the \"DMA\" value"));
+
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", (uint32_t*)&pThis->cMsLinkUpDelay, 5000); /* ms */
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
+ Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
+ if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
+ {
+ LogRel(("3C501#%d WARNING! Link up delay is set to %u seconds!\n",
+ iInstance, pThis->cMsLinkUpDelay / 1000));
+ }
+ Log(("#%d Link up delay is set to %u seconds\n",
+ iInstance, pThis->cMsLinkUpDelay / 1000));
+
+
+ /*
+ * Initialize data (most of it anyway).
+ */
+ pThis->Led.u32Magic = PDMLED_MAGIC;
+ /* IBase */
+ pThisCC->IBase.pfnQueryInterface = elnkQueryInterface;
+ /* INetworkPort */
+ pThisCC->INetworkDown.pfnWaitReceiveAvail = elnkNet_WaitReceiveAvail;
+ pThisCC->INetworkDown.pfnReceive = elnkNet_Receive;
+ pThisCC->INetworkDown.pfnXmitPending = elnkNet_XmitPending;
+ /* INetworkConfig */
+ pThisCC->INetworkConfig.pfnGetMac = elnkGetMac;
+ pThisCC->INetworkConfig.pfnGetLinkState = elnkGetLinkState;
+ pThisCC->INetworkConfig.pfnSetLinkState = elnkSetLinkState;
+ /* ILeds */
+ pThisCC->ILeds.pfnQueryStatusLed = elnkQueryStatusLed;
+
+ /*
+ * We use our own critical section (historical reasons).
+ */
+ rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "3C501#%u", iInstance);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
+ AssertRCReturn(rc, rc);
+
+ rc = RTSemEventCreate(&pThis->hEventOutOfRxSpace);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Register ISA I/O ranges for the EtherLink 3C501.
+ */
+ rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase, 0x10 /*cPorts*/, elnkIOPortWrite, elnkIOPortRead,
+ "3C501", NULL /*paExtDesc*/, &pThis->hIoPortsIsa);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Register DMA channel.
+ */
+ if (pThis->uIsaDma <= ELNK_MAX_VALID_DMA)
+ {
+ rc = PDMDevHlpDMARegister(pDevIns, pThis->uIsaDma, elnkR3DMAXferHandler, pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+ LogRel(("3C501#%d: Enabling DMA channel %u\n", iInstance, pThis->uIsaDma));
+ }
+ else
+ LogRel(("3C501#%d: Disabling DMA\n", iInstance));
+
+ rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, elnkR3TimerRestore, NULL, TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
+ "3C501 Restore Timer", &pThis->hTimerRestore);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rc = PDMDevHlpSSMRegisterEx(pDevIns, ELNK_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
+ NULL, elnkLiveExec, NULL,
+ elnkSavePrep, elnkSaveExec, NULL,
+ elnkLoadPrep, elnkLoadExec, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Create the transmit queue.
+ */
+ rc = PDMDevHlpTaskCreate(pDevIns, PDMTASK_F_RZ, "3C501-Xmit", elnkR3XmitTaskCallback, NULL /*pvUser*/, &pThis->hXmitTask);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Create the RX notifier signaller.
+ */
+ rc = PDMDevHlpTaskCreate(pDevIns, PDMTASK_F_RZ, "3C501-Rcv", elnkR3CanRxTaskCallback, NULL /*pvUser*/, &pThis->hCanRxTask);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Register the info item.
+ */
+ RTStrPrintf(szTmp, sizeof(szTmp), "elnk%d", pThis->iInstance);
+ PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "3C501 info", elnkR3Info);
+
+ /*
+ * Attach status driver (optional).
+ */
+ rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pBase, "Status Port");
+ if (RT_SUCCESS(rc))
+ pThis->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
+ else if ( rc != VERR_PDM_NO_ATTACHED_DRIVER
+ && rc != VERR_PDM_CFG_MISSING_DRIVER_NAME)
+ {
+ AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ /*
+ * Attach driver.
+ */
+ rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
+ if (RT_SUCCESS(rc))
+ {
+ pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
+ AssertMsgReturn(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
+ VERR_PDM_MISSING_INTERFACE_BELOW);
+ pThis->fDriverAttached = true;
+ }
+ else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
+ || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
+ {
+ /* No error! */
+ Log(("No attached driver!\n"));
+ }
+ else
+ return rc;
+
+ /*
+ * Reset the device state. (Do after attaching.)
+ */
+ elnkR3HardReset(pDevIns, pThis);
+
+ /*
+ * Register statistics counters.
+ */
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Public/Net/EtherLink%u/BytesReceived", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Public/Net/EtherLink%u/BytesTransmitted", iInstance);
+
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Devices/EtherLink%d/ReceiveBytes", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Devices/EtherLink%d/TransmitBytes", iInstance);
+
+#ifdef VBOX_WITH_STATISTICS
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOReadRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in RZ", "/Devices/EtherLink%d/IO/ReadRZ", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOReadR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in R3", "/Devices/EtherLink%d/IO/ReadR3", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOWriteRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in RZ", "/Devices/EtherLink%d/IO/WriteRZ", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOWriteR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in R3", "/Devices/EtherLink%d/IO/WriteR3", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive", "/Devices/EtherLink%d/Receive", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows", "/Devices/EtherLink%d/RxOverflow", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Nr of RX overflow wakeups", "/Devices/EtherLink%d/RxOverflowWakeup", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in RZ", "/Devices/EtherLink%d/Transmit/TotalRZ", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in R3", "/Devices/EtherLink%d/Transmit/TotalR3", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitSendRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in RZ", "/Devices/EtherLink%d/Transmit/SendRZ", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitSendR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in R3", "/Devices/EtherLink%d/Transmit/SendR3", iInstance);
+
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatInterrupt, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling interrupt checks", "/Devices/EtherLink%d/UpdateIRQ", iInstance);
+
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatResets, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of soft resets", "/Devices/EtherLink%d/SoftResets", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktAdrmDis, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, disabled address match", "/Devices/EtherLink%d/DropPktAdrmDis", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktZeroLen, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped zero length packet", "/Devices/EtherLink%d/DropPktZeroLen", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktVMNotRunning,STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, VM not running", "/Devices/EtherLink%d/DropPktVMNotRunning", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktNoLink, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, no link", "/Devices/EtherLink%d/DropPktNoLink", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktStaleRcv, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, status register unread", "/Devices/EtherLink%d/DropPktStaleRcv", iInstance);
+#endif /* VBOX_WITH_STATISTICS */
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatPktsLostReset, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of packets lost due to resets", "/Devices/EtherLink%d/PktsLostByReset", iInstance);
+
+ return VINF_SUCCESS;
+}
+
+#else
+
+/**
+ * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
+ */
+static DECLCALLBACK(int) elnkRZConstruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PELNKSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PELNKSTATE);
+
+ /* Critical section setup: */
+ int rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
+ AssertRCReturn(rc, rc);
+
+ /* ISA I/O ports: */
+ if (pThis->hIoPortsIsa != NIL_IOMIOPORTHANDLE)
+ {
+ rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsIsa, elnkIOPortWrite, elnkIOPortRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+ }
+
+ return VINF_SUCCESS;
+}
+
+#endif /* IN_RING3 */
+
+/**
+ * The device registration structure.
+ */
+const PDMDEVREG g_Device3C501 =
+{
+ /* .u32Version = */ PDM_DEVREG_VERSION,
+ /* .uReserved0 = */ 0,
+ /* .szName = */ "3c501",
+ /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
+ /* .fClass = */ PDM_DEVREG_CLASS_NETWORK,
+ /* .cMaxInstances = */ ~0U,
+ /* .uSharedVersion = */ 42,
+ /* .cbInstanceShared = */ sizeof(ELNKSTATE),
+ /* .cbInstanceCC = */ sizeof(ELNKSTATECC),
+ /* .cbInstanceRC = */ sizeof(ELNKSTATERC),
+ /* .cMaxPciDevices = */ 0,
+ /* .cMaxMsixVectors = */ 0,
+ /* .pszDescription = */ "3Com EtherLink 3C501 adapter.\n",
+#if defined(IN_RING3)
+ /* .pszRCMod = */ "VBoxDDRC.rc",
+ /* .pszR0Mod = */ "VBoxDDR0.r0",
+ /* .pfnConstruct = */ elnkR3Construct,
+ /* .pfnDestruct = */ elnkR3Destruct,
+ /* .pfnRelocate = */ elnkR3Relocate,
+ /* .pfnMemSetup = */ NULL,
+ /* .pfnPowerOn = */ NULL,
+ /* .pfnReset = */ elnkR3Reset,
+ /* .pfnSuspend = */ elnkR3Suspend,
+ /* .pfnResume = */ NULL,
+ /* .pfnAttach = */ elnkR3Attach,
+ /* .pfnDetach = */ elnkR3Detach,
+ /* .pfnQueryInterface = */ NULL,
+ /* .pfnInitComplete = */ NULL,
+ /* .pfnPowerOff = */ elnkR3PowerOff,
+ /* .pfnSoftReset = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RING0)
+ /* .pfnEarlyConstruct = */ NULL,
+ /* .pfnConstruct = */ elnkRZConstruct,
+ /* .pfnDestruct = */ NULL,
+ /* .pfnFinalDestruct = */ NULL,
+ /* .pfnRequest = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RC)
+ /* .pfnConstruct = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#else
+# error "Not in IN_RING3, IN_RING0 or IN_RC!"
+#endif
+ /* .u32VersionEnd = */ PDM_DEVREG_VERSION
+};
+
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
diff --git a/src/VBox/Devices/Network/DevDP8390.cpp b/src/VBox/Devices/Network/DevDP8390.cpp
new file mode 100644
index 00000000..810e3304
--- /dev/null
+++ b/src/VBox/Devices/Network/DevDP8390.cpp
@@ -0,0 +1,5500 @@
+/* $Id: DevDP8390.cpp $ */
+/** @file
+ * DevDP8390 - National Semiconductor DP8390-based Ethernet Adapter Emulation.
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/** @page pg_dev_dp8390 NatSemi DP8390-Based Ethernet NIC Emulation.
+ *
+ * This software was written based on the following documents:
+ *
+ * - National Semiconductor DP8390/NS32490 Network Interface Controller,
+ * 1986
+ * - National Semiconductor DP8390D/NS32490D NIC Network Interface
+ * Controller datasheet, July 1995
+ * - National Semiconductor Application Note 729, DP839EB-ATN IBM PC-AT
+ * Compatible DP83901 SNIC Serial Network Interface Controller
+ * Evaluation Board, 1993
+ * - National Semiconductor Application Note 842, The Design and Operation
+ * of a Low Cost, 8-Bit PC-XT Compatible Ethernet Adapter Using
+ * the DP83902, May 1993
+ * - National Semiconductor Application Note 858, Guide to Loopback Using
+ * the DP8390 Chip Set, October 1992
+ * - National Semiconductor Application Note 875, DP83905EB-AT AT/LANTIC
+ * Evaluation Board, June 1993
+ * - Western Digital WD83C584 Bus Interface Controller Device datasheet,
+ * October 29, 1990
+ * - Western Digital WD83C690 Ethernet LAN Controller datasheet,
+ * November 2, 1990
+ * - 3Com EtherLink II Adapter Technical Reference Manual,
+ * March 1991
+ *
+ * This emulation is compatible with drivers for:
+ * - Novell/Eagle/Anthem NE1000 (8-bit)
+ * - Novell/Eagle/Anthem NE2000 (16-bit)
+ * - Western Digital/SMC WD8003E (8-bit)
+ * - Western Digital/SMC WD8013EBT (16-bit)
+ * - 3Com EtherLink II 3C503 (8-bit)
+ *
+ *
+ * The National Semiconductor DP8390 was an early (circa 1986) low-cost
+ * Ethernet controller, typically accompanied by the DP8391 Serial Network
+ * Interface and the DP8392 Coaxial Transceiver Interface.
+ *
+ * Due to its relatively low cost, the DP8390 NIC was chosen for several
+ * very widespread early PC Ethernet designs, namely the Novell NE1000/NE2000,
+ * Western Digital (later SMC) WD8003 EtherCard Plus, and 3Com EtherLink II.
+ * The popularity of these cards, especially the NE2000, in turn spawned
+ * a bevy of compatible chips from National Semiconductor and many others.
+ *
+ * All common DP8390-based cards have onboard memory. The initial WD8003E and
+ * NE1000 cards have one 8Kx8 SRAM; 16-bit cards like WD8013E or NE2000 have
+ * two 8Kx8 SRAMs wired in 8Kx16 configuration to enable 16-bit wide transfers.
+ * The DP8390 can address up to 64K or local memory and uses "Local DMA"
+ * (similar to bus mastering) to access it. Some newer cards had 32K or more
+ * onboard RAM. Note that an NE2000 in 8-bit mode can only address 8K local
+ * memory, effectively reverting to an NE1000.
+ *
+ * The DP8390 uses "Remote DMA" to move data between local memory and the host
+ * system. Remote DMA is quite similar to 8237-style third party DMA, except
+ * the DMA controller is on the DP8390 chip in this case.
+ *
+ * The DP8390 has a control bit (DCR.WTS) which selects whether all DMA (both
+ * Local and Remote) transfers are 8-bit or 16-bit. Word-wide transfers can
+ * generally only be used on a 16-bit card in a 16-bit slot, because only then
+ * can the host drive 16-bit I/O cycles to the data ports. That is why
+ * an NE2000 in an 8-bit slot can only use half of its local RAM -- remote DMA
+ * simply cannot access half of the 8Kx16 SRAM.
+ *
+ * The DP8390 maps its internal registers as sixteen 8-bit wide I/O ports.
+ * There are four register pages, selectable through the Command Register (CR)
+ * which is accessible at offset 0 in all pages.
+ *
+ * The NE1000/NE2000 cards only use I/O and IRQ resources, not memory
+ * or DMA. In contrast, the Western Digital cards use memory-mapped buffers.
+ * Later AT/LANTIC (DP83905) based NE2000-compatible cards can optionally
+ * use memory as well. The 3Com EtherLink II (3C503) uses a custom gate array
+ * in addition to the DP8390 and can use programmed I/O, 8237 DMA, as well
+ * as optional direct memory mapping.
+ *
+ * Address decoding is typically incomplete, which causes the buffer RAM and
+ * possibly PROM to be aliased multiple times in the DP8390's address space.
+ *
+ * Buffer overflow handling is slightly tricky. The DP8390 assumes that if
+ * the receiver is enabled, there is space for at least one page (256 bytes).
+ * Once it fills up the page and advances the CURR pointer, the DP8390 checks
+ * whether CURR equals BNRY and if so, triggers an overflow condition. Note
+ * that after the NIC is initialized, CURR *will* normally equal BNRY, with
+ * both pointing at the beginning of the receive ring (PSTART). An overflow
+ * is only triggered when CURR equals BNRY right after advancing.
+ *
+ * The documentation of the Send Packet command mentions that when CRDA crosses
+ * the PSTOP register, the current remote DMA address (i.e. CRDA) is set to
+ * the PSTART value, which is rather convenient when reading received packets
+ * out of the ring buffer using remote DMA. The documentation does not mention
+ * that the same logic applies for all remote DMA reads, a feature that several
+ * NE1000/NE2000 drivers (packet drivers, Novell ODI) rely on. This is logical,
+ * because reading out of the receive ring buffer address range always implies
+ * reading received packets, and then the PSTOP->PSTART wraparound becomes
+ * desirable. It is unclear whether the same wraparound handling also applies
+ * for remote DMA writes within the receive ring buffer.
+ *
+ * The documentation is not very clear on how the CRDA register is managed.
+ * One might be led to believe that starting remote DMA copies the remote DMA
+ * start address (i.e. RSAR) to the CRDA register. However, the NE1000 ODI
+ * driver for OS/2 1.0 (NE1000.SYS from early 1988) relies on restarting remote
+ * DMA and continuing where it left off. The DP8390D datasheet only mentions
+ * this in a passing fashion at the end of the "Remote Write with High Speed
+ * Buses" section, saying that if a dummy remote read is executed before a
+ * remote write, RSAR can be set up for the dummy read such that the CRDA
+ * register contains the desired value for the following write.
+ *
+ * Conversely, it is not spelled out that writing RSAR also updates CRDA, but
+ * at least Novell's NE2000 ODI driver v2.12 is known to rely on this behavior
+ * and checks that a write to RSAR is reflected in CRDA.
+ *
+ * Loopback operation is limited in the DP8390. Because it is a half-duplex
+ * device, it cannot truly transmit and receive simultaneously. When loopback
+ * is in effect, the received data is *not* written into memory. Only the last
+ * few bytes of the packet are visible in the FIFO.
+ *
+ * Likewise due to its half-duplex nature, the CRC circuitry during loopback
+ * works either only on the transmit side (FCS is generated but not checked)
+ * or the receive side (FCS is checked but not generated).
+ *
+ * The loopback behavior is even stranger when DCR.WTS is set to enabled 16-bit
+ * DMA transfers. Even though the chip reads 16 bits at a time, only 8 bits are
+ * actually transmitted; the DCR.BOS bit determines whether the low or high
+ * 8 bits of each words are transmitted. As a consequence, the programmed length
+ * of the transmit is also halved.
+ *
+ * Because loopback operation is so different from normal send/receive, loopback
+ * packets are not run through the normal receive path and are treated specially
+ * instead. The WD and especially 3C503 diagnostics exercise the loopback
+ * functionality fairly thoroughly.
+ *
+ *
+ * NE1000 and NE2000
+ * -----------------
+ *
+ * Common NE1000/NE2000 configurations in Novell drivers:
+ * I/O Base = 300h, IRQ = 3 (default)
+ * I/O Base = 320h, IRQ = 2
+ * I/O Base = 340h, IRQ = 4
+ * I/O Base = 360h, IRQ = 5
+ * The I/O base can be set to 300h/320h/340h/360h; the IRQ to 2, 3, 4, 5.
+ * No memory or DMA is used.
+ *
+ * The NE1000/NE2000 adds a data register and a reset register to the I/O
+ * space. A PROM containing the node address is mapped into the DP8390's local
+ * address space.
+ *
+ * The mapping of the 32x8 PROM on an NE2000 card is quite non-obvious but
+ * fortunately well explained in the AN-729 Application Note. Address lines
+ * A4-A1 of the internal bus are connected to lines A3-A0 of the PROM
+ * (enabling 16 distinct bytes of the 32-byte PROM to be addressed). However,
+ * the negated EN16 signal, which is active when the NE2000 is in a 16-bit
+ * slot, is connected to the PROM's address line A4. That means an NE2000 in
+ * a 16-bit slot reads different PROM bytes than when the same card is in an
+ * 8-bit slot. The PROM is structured such that an NE2000 in an 8-bit slot
+ * reads a 'BB' signature (same as NE1000) at PROM offset 1Eh/1Fh, while
+ * an NE2000 in a 16-bit slot returns a 'WW' signature from PROM offset
+ * 0Eh/0Fh instead.
+ *
+ * The original NE1000 boards Assy. #950-054401 actually only had 6 bytes of
+ * MAC address in the PROM, the rest was unused (0FFh). Software supporting the
+ * NE1000 thus should not examine the PROM contents beyond the first 6 bytes.
+ *
+ * Novell's old OUI was 00:00:D8 but drivers are not known to check for it.
+ *
+ * Newer DP83905 AT/LANTIC based NE2000plus cards were optionally capable of
+ * using shared RAM in a manner very similar to the WD8003/WD8013.
+ *
+ *
+ * WD8003 and WD8013 EtherCard Plus
+ * --------------------------------
+ *
+ * Common WD8013 configurations:
+ * I/O Base = 280h, IRQ = 3, RAM D000-D3FF (default)
+ * I/O Base = 330h, IRQ = 10, RAM CC00-CFFF
+ * I/O Base = 240h, IRQ/RAM soft-configurable
+ * The I/O base can be set anywhere in the 2xxh-3xxh range in 20h increments.
+ * The IRQs available on a WD8013 are 2, 3, 4, 5, 7, 10, 11, 15. The shared
+ * RAM can be anywhere between 80000h (512K) to FFC000h (16M-16K) in 16K
+ * increments.
+ *
+ * The Western Digital WD8003E appeared at around the same time as Novell's
+ * NE1000 (1987). It is likewise a short 8-bit ISA card with 8Kx8 onboard
+ * SRAM. The major difference is that rather than using remote DMA to move
+ * data between the host and local RAM, the WD8003 directly mapps the onboard
+ * memory to the host's address space (often called shared memory). A later
+ * 16-bit WD8013 model used 8Kx16 SRAM, and there were follow-on WD8003 models
+ * with 16K or 32K local RAM.
+ *
+ * Instead of mapping the PROM into the DP8390's local address space, the
+ * WD8003/WD8013 exposes the node address through the I/O space; the DP8390's
+ * local address space only contains buffer RAM.
+ *
+ * The WD8003 cannot use remote DMA at all; the host must use shared memory.
+ * Remote DMA can be programmed but there is no way to trigger RDMA transfers.
+ *
+ * Western Digital's brand name for WD8003/WD8013 was EtherCard. Circa 1991,
+ * WD sold the networking business to SMC; SMC continued to sell and further
+ * develop the cards under the Elite brand name, also designated as the
+ * SMC8000 series.
+ *
+ * The original WD8003E/EBT/WT uses very simple glue logic around the DP8390
+ * and must be configured through jumpers. Newer WD8003EB/EP/EW/W/WC uses an
+ * interface chip (WD83C583, WD83C584, or later) with an EEPROM and can be
+ * configured through a software utility.
+ *
+ * Similarly the 16-bit WD8013EBT is configured only though jumpers, while
+ * the newer WD8013EB/W/EW/EWC/WC/EPC are software configurable.
+ *
+ * The "Board ID" byte (at offset 6 in the PROM) is used to distinguish
+ * between the various models.
+ *
+ * Newer WD cards use the WD83C690 controller rather than DP8390. The
+ * WD83C690 is close enough to DP8390 that old WD drivers should work with
+ * it, but it has a number of differences. It has no support for Remote DMA
+ * whatsoever, and does not implement multicast filtering.
+ *
+ * The WD83C690 also handles receive buffer overflows somewhat differently;
+ * the DP8390 never fills the last remaining buffer page, meaning that
+ * CURR=BNRY indicates an empty buffer while CURR=BNRY-1 means buffer full.
+ * The WD83C690 can fill all pages and decides whether it is full or empty
+ * based on whether CURR or BNRY was changed more recently.
+ *
+ * Old Western Digital utilities/drivers may require the card to have WD's
+ * old OUI of 00:00:0C and refuse to recognize the hardware otherwise.
+ *
+ * The emulation passes WD diagnostics with no errors (DIAGNOSE.EXE Ver 1.11,
+ * dated 12/12/1989).
+ *
+ *
+ * 3C503 EtherLink II
+ * ------------------
+ *
+ * Common 3C503 configurations in Novell drivers:
+ * I/O Base = 300h, IRQ = 3 (default)
+ * The I/O base can be set via jumpers to 2E0h, 2A0h, 280h, 250h, 350h, 330h,
+ * 310h, or 300h (default). The ROM/RAM can be optionally mapped to one of
+ * DC000-DFFFF, D8000-DBFFF, CC000-CFFFF, or C8000-CBFFF, again configured
+ * through jumpers. The available IRQs are 2, 3, 4, or 5, and DRQs 1, 2, or 3,
+ * both soft-configurable (no IRQ/DRQ jumpers).
+ *
+ * Yet another design based on the DP8390 was the 3Com 3C503 EtherLink II,
+ * available sometime in 1988. Unlike Novell and WD, 3Com added a custom
+ * host interface ASIC ("Gate Array") which handles all transfers to and from
+ * the 8Kx8 onboard SRAM. The 3C503 can map the card's local RAM directly
+ * into the host's address space, alternatively software can use either PIO
+ * or 8-bit DMA to transfer data.
+ *
+ * For reasons that are not entirely clear, 3Com decided that the Remote DMA
+ * implementation on the DP3890 (successfully used by the NE1000/NE2000) was
+ * too buggy and the Gate Array essentially duplicates the Remote DMA
+ * functionality, while also adding 8327 style DMA support (like the DP839EB
+ * had) and optional shared RAM.
+ *
+ * Just like the NE1000/NE2000 and WD8003/WD8013, the 3C503 exists in an
+ * 8-bit variant (EtherLink II) and a 16-bit variant (EtherLink II/16),
+ * although both types are called 3C503.
+ *
+ * Since the 3C503 does not require shared RAM to operate, 3Com decided to
+ * use a single memory mapping for both a boot ROM (if present) and shared
+ * RAM. It is possible to boot from the ROM utilizing PIO or DMA for data
+ * transfers, and later switch to shared RAM. However, 3Com needed to add
+ * a hack for warm boot; the Vector Pointer Registers (VPTR0/1/2) contain
+ * a 20-bit address and the Gate Array monitors the ISA bus for a read cycle
+ * to that address. When a read cycle from the VPTR address occurs, the
+ * memory mapping is switched from RAM to ROM. The VPTR registers are meant
+ * to be programmed with the warm boot vector (often F000:FFF0 or FFFF0h).
+ *
+ * Some UNIX 3C503 drivers may require the card to have 3Com's old OUI
+ * of 02:60:8C and refuse to detect the hardware otherwise. Likewise the
+ * 3C503 diagnostics fail if the OUI is not 3Com's.
+ *
+ * The emulation passes 3Com diagnostics with flying colors (3C503.EXE Version
+ * 1.5, dated 11/26/1991).
+ *
+ *
+ * Linux Drivers
+ *
+ * The DP8390 driver (shared by NE1000/NE2000, WD8003/WD8013, and 3C503 drivers)
+ * in Linux has severe bugs in the receive path. The driver clears receive
+ * interrupts *after* going through the receive ring; that causes it to race
+ * against the DP8390 chip and sometimes dismiss receive interrupts without
+ * handling them. The driver also only receives at most 9 packets at a time,
+ * which again can cause already received packets to be "hanging" in the receive
+ * queue without the driver processing them.
+ * In addition, prior to Linux 1.3.47, the driver incorrectly cleared the
+ * overflow warning interrupt after any receive, causing it to potentially
+ * miss overflow interrupts.
+ *
+ * The above bugs cause received packets to be lost or retransmitted by sender,
+ * causing major TCP/IP performance issues when the DP8390 receives packets
+ * very quickly. Other operating systems do not exhibit these bugs.
+ *
+ *
+ * BSD Drivers
+ *
+ * For reasons that are not obvious, BSD drivers have configuration defaults far
+ * off from the hardware defaults. For NE2000 (ne1), it is I/O base 300h and
+ * IRQ 10. For WD8003E (we0), it is I/O base 280h, IRQ 9, memory D0000-D1FFF.
+ * For 3C503 (ec0), it is I/O base 250h, IRQ 9, memory D8000-D9FFF (no DMA).
+ *
+ * The resource assigments are difficult to configure (sometimes impossible on
+ * installation CDs) and the high IRQs may clash with PCI devices.
+ *
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_DP8390
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/pdmnetifs.h>
+#include <VBox/vmm/pgm.h>
+#include <VBox/version.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/net.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#ifdef IN_RING3
+# include <iprt/mem.h>
+# include <iprt/semaphore.h>
+# include <iprt/uuid.h>
+#endif
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+#define DPNIC_SAVEDSTATE_VERSION 1
+
+/** Maximum number of times we report a link down to the guest (failure to send frame) */
+#define DPNIC_MAX_LINKDOWN_REPORTED 3
+
+/** Maximum number of times we postpone restoring a link that is temporarily down. */
+#define DPNIC_MAX_LINKRST_POSTPONED 3
+
+/** Maximum frame size we handle */
+#define MAX_FRAME 1536
+
+/* Size of the local RAM. */
+#define DPNIC_MEM_SIZE 16384u
+
+#define DPNIC_MEM_MASK (DPNIC_MEM_SIZE - 1)
+
+/* Although it is a 16-bit adapter, the EtherLink II only supports 8-bit DMA
+ * and therefore DMA channels 1 to 3 are available.
+ */
+#define ELNKII_MIN_VALID_DMA 1
+#define ELNKII_MAX_VALID_DMA 3
+
+/* EtherLink II Gate Array revision. */
+#define ELNKII_GA_REV 1
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+
+/**
+ * Emulated device types.
+ */
+enum DP8390_DEVICE_TYPE
+{
+ DEV_NE1000 = 0, /* Novell NE1000 compatible (8-bit). */
+ DEV_NE2000 = 1, /* Novell NE2000 compatible (16-bit). */
+ DEV_WD8003 = 2, /* Western Digital WD8003 EtherCard Plus compatible (8-bit). */
+ DEV_WD8013 = 3, /* Western Digital WD8013 EtherCard Plus compatible (16-bit). */
+ DEV_3C503 = 4 /* 3Com 3C503 EtherLink II compatible. */
+};
+
+/** WD8003/WD80013 specific register offsets. */
+#define WDR_CTRL1 0 /* Control register 1. */
+#define WDR_ATDET 1 /* 16-bit slot detect. */
+#define WDR_IOBASE 2 /* I/O base register. */
+#define WDR_CTRL2 5 /* Control register 2. */
+#define WDR_JP 6 /* Jumper settings. */
+#define WDR_PROM 8 /* PROM offset in I/O space. */
+
+/** WD8013 Control Register 1. */
+typedef struct WD_CTRL1 {
+ uint8_t A13_18 : 6; /* Shared memory decoding A13-A18. */
+ uint8_t MEME : 1; /* Enable memory access. */
+ uint8_t RESET : 1; /* Reset NIC core. */
+} WD_CTRL1;
+AssertCompile(sizeof(WD_CTRL1) == sizeof(uint8_t));
+
+/** WD8013 Control Register 2. */
+typedef struct WD_CTRL2 {
+ uint8_t A19_23 : 5; /* Shared memory decoding A19-A23. */
+ uint8_t res : 1; /* Reserved. */
+ uint8_t MEMW : 1; /* Memory width (16-bit wide if set). */
+ uint8_t M16 : 1; /* Allow 16-bit host memory cycles if set. */
+} WD_CTRL2;
+AssertCompile(sizeof(WD_CTRL2) == sizeof(uint8_t));
+
+
+/** 3C503 EtherLink II specific register offsets. */
+#define GAR_PSTR 0
+#define GAR_PSPR 1
+#define GAR_DQTR 2
+#define GAR_R_BCFR 3
+#define GAR_R_PCFR 4
+#define GAR_GACFR 5
+#define GAR_GACR 6
+#define GAR_STREG 7
+#define GAR_IDCFR 8
+#define GAR_DAMSB 9
+#define GAR_DALSB 10
+#define GAR_VPTR2 11
+#define GAR_VPTR1 12
+#define GAR_VPTR0 13
+#define GAR_RFMSB 14
+#define GAR_RFLSB 15
+
+/** 3C503 EtherLink II Gate Array registers. */
+
+/** Gate Array DRQ Timer Register. */
+typedef struct EL_DQTR {
+ uint8_t tb : 5; /* Timer bits; should be multiple of 4. */
+ uint8_t res : 3; /* Reserved. */
+} GA_DQTR;
+AssertCompile(sizeof(GA_DQTR) == sizeof(uint8_t));
+
+/** Gate Array Configuration Register. */
+typedef struct EL_GACFR {
+ uint8_t mbs : 3; /* Memory Bank Select. */
+ uint8_t rsel : 1; /* RAM Select. */
+ uint8_t test : 1; /* Makes GA counters run at 10 MHz. */
+ uint8_t ows : 1; /* 0 Wait State for Gate Array. */
+ uint8_t tcm : 1; /* Terminal Count Mask for DMA (block interrupt if set). */
+ uint8_t nim : 1; /* NIC Interrupt Mask (block interrupt if set). */
+} GA_GACFR;
+AssertCompile(sizeof(GA_GACFR) == sizeof(uint8_t));
+
+/** Gate Array Configuration Register. */
+typedef struct EL_GACR {
+ uint8_t rst : 1; /* Hard reset GA/NIC. */
+ uint8_t xsel : 1; /* Transceiver Select. */
+ uint8_t ealo : 1; /* Window low 16 bytes of PROM to I/O space. */
+ uint8_t eahi : 1; /* Window high 16 bytes of PROM to I/O space. */
+ uint8_t share : 1; /* Enable interrupt sharing. */
+ uint8_t dbsel : 1; /* Double Buffer Select for FIFOs. */
+ uint8_t ddir : 1; /* DMA Direction (1=host to adapter). */
+ uint8_t start : 1; /* Start Gate Array DMA. */
+} GA_GACR;
+AssertCompile(sizeof(GA_GACR) == sizeof(uint8_t));
+
+/** Gate Array Status Register. */
+typedef struct EL_STREG {
+ uint8_t rev : 3; /* Gate Array Revision. */
+ uint8_t dip : 1; /* DMA In Progress. */
+ uint8_t dtc : 1; /* DMA Terminal Count. */
+ uint8_t oflw : 1; /* Data Overflow. */
+ uint8_t uflw : 1; /* Data Underflow. */
+ uint8_t dprdy : 1; /* Data Port Ready. */
+} GA_STREG;
+AssertCompile(sizeof(GA_STREG) == sizeof(uint8_t));
+
+/** Gate Array Interrupt/DMA Configuration. */
+typedef struct EL_IDCFR {
+ uint8_t drq1 : 1; /* Enable DRQ 1. */
+ uint8_t drq2 : 1; /* Enable DRQ 2. */
+ uint8_t drq3 : 1; /* Enable DRQ 3. */
+ uint8_t res : 1; /* Unused. */
+ uint8_t irq2 : 1; /* Enable IRQ 2. */
+ uint8_t irq3 : 1; /* Enable IRQ 3. */
+ uint8_t irq4 : 1; /* Enable IRQ 4. */
+ uint8_t irq5 : 1; /* Enable IRQ 5. */
+} GA_IDCFR;
+AssertCompile(sizeof(GA_IDCFR) == sizeof(uint8_t));
+
+/** Current DMA Address. */
+typedef struct EL_CDADR {
+ uint8_t cdadr_lsb; /* Current DMA Address LSB. */
+ uint8_t cdadr_msb; /* Current DMA Address MSB. */
+} GA_CDADR;
+AssertCompile(sizeof(GA_CDADR) == sizeof(uint16_t));
+
+/** 3C503 Gate Array state. */
+typedef struct EL_GA_s {
+ uint8_t PSTR; /* Page Start Register. */
+ uint8_t PSPR; /* Page Stop Register. */
+ union {
+ uint8_t DQTR; /* DRQ Timer Register. */
+ GA_DQTR dqtr;
+ };
+ uint8_t BCFR; /* Base Configuration Register (R/O). */
+ uint8_t PCFR; /* Boot PROM Configuration Register (R/O). */
+ union {
+ uint8_t GACFR;
+ GA_GACFR gacfr; /* Gate Array Configuration Register. */
+ };
+ union {
+ uint8_t GACR; /* Gate Array Control Register. */
+ GA_GACR gacr;
+ };
+ union {
+ uint8_t STREG; /* Gate Array Status Register (R/O). */
+ GA_STREG streg;
+ };
+ union {
+ uint8_t IDCFR; /* Interrupt/DMA Configuration Register. */
+ GA_IDCFR idcfr;
+ };
+ uint8_t DAMSB; /* DMA Address MSB. */
+ uint8_t DALSB; /* DMA Address LSB. */
+ uint8_t VPTR2; /* Vector Pointer 2. */
+ uint8_t VPTR1; /* Vector Pointer 1. */
+ uint8_t VPTR0; /* Vector Pointer 0. */
+ union {
+ uint16_t CDADR; /* Current DMA address (internal state). */
+ GA_CDADR cdadr;
+ };
+ bool fGaIrq; /* Gate Array IRQ (internal state). */
+} EL_GA, *PEL_GA;
+
+/** DP8390 core register offsets. */
+#define DPR_CR 0
+
+#define DPR_P0_R_CLDA0 1
+#define DPR_P0_W_PSTART 1
+#define DPR_P0_R_CLDA1 2
+#define DPR_P0_W_PSTOP 2
+#define DPR_P0_BNRY 3
+#define DPR_P0_R_TSR 4
+#define DPR_P0_W_TPSR 4
+#define DPR_P0_R_NCR 5
+#define DPR_P0_W_TBCR0 5
+#define DPR_P0_R_FIFO 6
+#define DPR_P0_W_TBCR1 6
+#define DPR_P0_ISR 7
+#define DPR_P0_R_CRDA0 8
+#define DPR_P0_W_RSAR0 8
+#define DPR_P0_R_CRDA1 9
+#define DPR_P0_W_RSAR1 9
+#define DPR_P0_W_RBCR0 10
+#define DPR_P0_W_RBCR1 11
+#define DPR_P0_R_RSR 12
+#define DPR_P0_W_RCR 12
+#define DPR_P0_R_CNTR0 13
+#define DPR_P0_W_TCR 13
+#define DPR_P0_R_CNTR1 14
+#define DPR_P0_W_DCR 14
+#define DPR_P0_R_CNTR2 15
+#define DPR_P0_W_IMR 15
+
+#define DPR_P1_CURR 7
+
+#define DPR_P2_R_PSTART 1
+#define DPR_P2_W_CLDA0 1
+#define DPR_P2_R_PSTOP 2
+#define DPR_P2_W_CLDA1 2
+#define DPR_P2_RNXTPP 3 /* Remote Next Packet Pointer. */
+#define DPR_P2_R_TPSR 4
+#define DPR_P2_LNXTPP 5 /* Local Next Packet Pointer. */
+#define DPR_P2_ADRCU 6 /* Address Counter (Upper). */
+#define DPR_P2_ADRCL 7 /* Address Counter (Lower). */
+#define DPR_P2_R_RCR 12
+#define DPR_P2_R_TCR 13
+#define DPR_P2_R_DCR 14
+#define DPR_P2_R_IMR 15
+
+
+/** DP8390 Packet Header. */
+typedef struct DP_PKT_HDR {
+ uint8_t rcv_stat; /* Receive Status. */
+ uint8_t next_ptr; /* Next Packet Pointer. */
+ uint16_t byte_cnt; /* Receive byte count. */
+} DP_PKT_HDR;
+
+/** Select values for CR.RD field. */
+#define DP_CR_RDMA_INVL 0 /* Invalid value. */
+#define DP_CR_RDMA_RD 1 /* Remote Read. */
+#define DP_CR_RDMA_WR 2 /* Remote Write. */
+#define DP_CR_RDMA_SP 3 /* Send Packet. */
+#define DP_CR_RDMA_ABRT 4 /* Abort Remote DMA. */
+
+/** DP8390 Command Register (CR). */
+typedef struct DP_CR {
+ uint8_t STP : 1; /* Stop. */
+ uint8_t STA : 1; /* Start. */
+ uint8_t TXP : 1; /* Transmit Packet. */
+ uint8_t RD : 3; /* Remote DMA Command. */
+ uint8_t PS : 2; /* Page Select. */
+} DP_CR;
+AssertCompile(sizeof(DP_CR) == sizeof(uint8_t));
+
+/** DP8390 Interrupt Status Register (ISR). */
+typedef struct DP_ISR {
+ uint8_t PRX : 1; /* Packet Received. */
+ uint8_t PTX : 1; /* Packet Transmitted. */
+ uint8_t RXE : 1; /* Receive Error. */
+ uint8_t TXE : 1; /* Transmit Error. */
+ uint8_t OVW : 1; /* Overwrite Warning (no receive buffers). */
+ uint8_t CNT : 1; /* Counter Overflow. */
+ uint8_t RDC : 1; /* Remote DMA Complete. */
+ uint8_t RST : 1; /* Reset Status. */
+} DP_ISR;
+AssertCompile(sizeof(DP_ISR) == sizeof(uint8_t));
+
+/** DP8390 Interrupt Mask Register (IMR). */
+typedef struct DP_IMR {
+ uint8_t PRXE : 1; /* Packet Received Interrupt Enable. */
+ uint8_t PTXE : 1; /* Packet Transmitted Interrupt Enable. */
+ uint8_t RXEE : 1; /* Receive Error Interrupt Enable. */
+ uint8_t TXEE : 1; /* Transmit Error Interrupt Enable. */
+ uint8_t OVWE : 1; /* Overwrite Warning Interrupt Enable. */
+ uint8_t CNTE : 1; /* Counter Overflow Interrupt Enable. */
+ uint8_t RDCE : 1; /* DMA Complete Interrupt Enable. */
+ uint8_t res : 1; /* Reserved. */
+} DP_IMR;
+AssertCompile(sizeof(DP_IMR) == sizeof(uint8_t));
+
+/** DP8390 Data Configuration Register (DCR). */
+typedef struct DP_DCR {
+ uint8_t WTS : 1; /* Word Transfer Select. */
+ uint8_t BOS : 1; /* Byte Order Select. */
+ uint8_t LAS : 1; /* Long Address Select. */
+ uint8_t LS : 1; /* Loopback Select. */
+ uint8_t ARM : 1; /* Auto-Initialize Remote. */
+ uint8_t FT : 2; /* Fifo Threshold Select. */
+ uint8_t res : 1; /* Reserved. */
+} DP_DCR;
+AssertCompile(sizeof(DP_DCR) == sizeof(uint8_t));
+
+/** Transmit Configuration Register (TCR). */
+typedef struct DP_TCR {
+ uint8_t CRC : 1; /* Inhibit CRC. */
+ uint8_t LB : 2; /* Loopback Control. */
+ uint8_t ATD : 1; /* Auto Transmit Disable. */
+ uint8_t OFST : 1; /* Collision Offset Enable. */
+ uint8_t res : 3; /* Reserved. */
+} DP_TCR;
+AssertCompile(sizeof(DP_TCR) == sizeof(uint8_t));
+
+/** Transmit Status Register (TSR). */
+typedef struct DP_TSR {
+ uint8_t PTX : 1; /* Packet Transmitted. */
+ uint8_t DFR : 1; /* Non-Deferred Transmission (reserved in DP83901A). */
+ uint8_t COL : 1; /* Transmit Collided. */
+ uint8_t ABT : 1; /* Transmit Aborted. */
+ uint8_t CRS : 1; /* Carrier Sense Lost. */
+ uint8_t FU : 1; /* FIFO Underrun. */
+ uint8_t CDH : 1; /* CD Heartbeat. */
+ uint8_t OWC : 1; /* Out of Window Collision. */
+} DP_TSR;
+AssertCompile(sizeof(DP_TSR) == sizeof(uint8_t));
+
+/** Receive Configuration Register (RCR). */
+typedef struct DP_RCR {
+ uint8_t SEP : 1; /* Save Errored Packets. */
+ uint8_t AR : 1; /* Accept Runt Packets. */
+ uint8_t AB : 1; /* Accept Broadcast. */
+ uint8_t AM : 1; /* Accept Multicast. */
+ uint8_t PRO : 1; /* Promiscuous Physical. */
+ uint8_t MON : 1; /* Monitor Mode. */
+ uint8_t res : 2; /* Reserved. */
+} DP_RCR;
+AssertCompile(sizeof(DP_RCR) == sizeof(uint8_t));
+
+/** Receive Status Register (RSR). */
+typedef struct DP_RSR {
+ uint8_t PRX : 1; /* Packet Received Intact. */
+ uint8_t CRC : 1; /* CRC Error. */
+ uint8_t FAE : 1; /* Frame Alignment Error. */
+ uint8_t FO : 1; /* FIFO Overrun. */
+ uint8_t MPA : 1; /* Missed Packet. */
+ uint8_t PHY : 1; /* Physical/Multicast Address. */
+ uint8_t DIS : 1; /* Receiver Disabled. */
+ uint8_t DFR : 1; /* Deferring. */
+} DP_RSR;
+AssertCompile(sizeof(DP_RSR) == sizeof(uint8_t));
+
+/** Transmit Byte Count Register. */
+typedef struct DP_TBCR {
+ uint8_t TBCR0;
+ uint8_t TBCR1;
+} DP_TBCR;
+AssertCompile(sizeof(DP_TBCR) == sizeof(uint16_t));
+
+/** Current Local DMA Address. */
+typedef struct DP_CLDA {
+ uint8_t CLDA0;
+ uint8_t CLDA1;
+} DP_CLDA;
+AssertCompile(sizeof(DP_CLDA) == sizeof(uint16_t));
+
+/** Remote Start Address Register. */
+typedef struct DP_RSAR {
+ uint8_t RSAR0;
+ uint8_t RSAR1;
+} DP_RSAR;
+AssertCompile(sizeof(DP_RSAR) == sizeof(uint16_t));
+
+/** Remote Byte Count Register. */
+typedef struct DP_RBCR {
+ uint8_t RBCR0;
+ uint8_t RBCR1;
+} DP_RBCR;
+AssertCompile(sizeof(DP_RBCR) == sizeof(uint16_t));
+
+/** Current Remote DMA Address. */
+typedef struct DP_CRDA {
+ uint8_t CRDA0;
+ uint8_t CRDA1;
+} DP_CRDA;
+AssertCompile(sizeof(DP_CRDA) == sizeof(uint16_t));
+
+/** Page 1 registers. */
+/* All registers read/write without side effects, unlike pages 0/2. */
+typedef struct DP_PG1 {
+ uint8_t dummy_cr;
+ uint8_t PAR[6]; /* Physical Address PAR0-PAR5. */
+ uint8_t dummy_curr; /* Current Page Register. */
+ uint8_t MAR[8]; /* Multicast Address Register MAR0-MAR7. */
+} DP_PG1;
+AssertCompile(sizeof(DP_PG1) == 16);
+
+/** DP8390 FIFO. Not all of the state is explicitly accessible. */
+typedef struct DP_FIFO {
+ uint8_t rp; /* Read pointer. */
+ uint8_t wp; /* Write pointer. */
+ uint8_t fifo[16]; /* 16 bytes of FIFO. */
+} DP_FIFO;
+
+/**
+ * Core DP8390 chip state.
+ */
+typedef struct DP8390CORE
+{
+ union {
+ uint8_t CR; /* Command Register. */
+ DP_CR cr;
+ };
+ union {
+ uint8_t DCR; /* Data Control Register. */
+ DP_DCR dcr;
+ };
+ /* Interrupt control. */
+ union {
+ uint8_t ISR; /* Interrupt Status Register. */
+ DP_ISR isr;
+ };
+ union {
+ uint8_t IMR; /* Interrupt Mask Register. */
+ DP_IMR imr;
+ };
+ /* Receive state. */
+ union {
+ uint8_t RCR; /* Receive Control Register. */
+ DP_RCR rcr;
+ };
+ union {
+ uint8_t RSR; /* Receive Status register. */
+ DP_RSR rsr;
+ };
+ /* Transmit State. */
+ union {
+ uint8_t TCR; /* Transmit Control Register. */
+ DP_TCR tcr;
+ };
+ union {
+ uint8_t TSR; /* Transmit Status register. */
+ DP_TSR tsr;
+ };
+ uint8_t NCR; /* Number of Collisions Register. */
+ /* Local DMA transmit state. */
+ uint8_t TPSR; /* Transmit Page Start. */
+ union {
+ uint16_t TBCR; /* Transmit Byte Count. */
+ DP_TBCR tbcr;
+ };
+ /* Local DMA receive state. */
+ union {
+ uint16_t CLDA; /* Current Local DMA Address. */
+ DP_CLDA clda;
+ };
+ uint8_t PSTART; /* Page Start. */
+ uint8_t PSTOP; /* Page Stop. */
+ uint8_t CURR; /* Current Page. */
+ uint8_t BNRY; /* Boundary Page. Also spelled BNDRY. */
+ /* Remote DMA state. */
+ union {
+ uint16_t RSAR; /* Remote Start Address Register. */
+ DP_RSAR rsar;
+ };
+ union {
+ uint16_t RBCR; /* Remote Byte Count Register. */
+ DP_RBCR rbcr;
+ };
+ union {
+ uint16_t CRDA; /* Current Remote DMA Address. */
+ DP_CRDA crda;
+ };
+ /* Miscellaneous state. */
+ uint8_t lnxtpp; /* Local Next Packet Pointer. */
+ uint8_t rnxtpp; /* Remote Next Packet Pointer. */
+ /* Tally counters. */
+ uint8_t CNTR0; /* Frame Alignment Errors. */
+ uint8_t CNTR1; /* CRC Errors. */
+ uint8_t CNTR2; /* Missed Packet Errors. */
+ union {
+ uint8_t PG1[sizeof(DP_PG1)];
+ DP_PG1 pg1; /* All Page 1 Registers. */
+ };
+ DP_FIFO fifo; /* The internal FIFO. */
+} DP8390CORE, *PDP8390CORE;
+
+/**
+ * DP8390-based card state.
+ */
+typedef struct DPNICSTATE
+{
+ /** Restore timer.
+ * This is used to disconnect and reconnect the link after a restore. */
+ TMTIMERHANDLE hTimerRestore;
+
+ /** Transmit signaller. */
+ PDMTASKHANDLE hXmitTask;
+ /** Receive ready signaller. */
+ PDMTASKHANDLE hCanRxTask;
+
+ /** Emulated device type. */
+ uint8_t uDevType;
+ /** State of the card's interrupt request signal. */
+ bool fNicIrqActive;
+
+ /** Core DP8390 chip state. */
+ DP8390CORE core;
+
+ /** WD80x3 Control Register 1. */
+ union {
+ uint8_t CTRL1;
+ WD_CTRL1 ctrl1;
+ };
+ /** WD80x3 Control Register 2. */
+ union {
+ uint8_t CTRL2;
+ WD_CTRL2 ctrl2;
+ };
+
+ /** 3C503 Gate Array state. */
+ EL_GA ga;
+ /** The 3C503 soft-configured ISA DMA channel. */
+ uint8_t uElIsaDma;
+
+ /** The PROM contents. 32 bytes addressable, R/O. */
+ uint8_t aPROM[32];
+
+ /** Shared RAM base. */
+ RTGCPHYS MemBase;
+ /** Shared RAM MMIO region handle. */
+ PGMMMIO2HANDLE hSharedMem;
+ /** Shared RAM size. */
+ RTGCPHYS cbMemSize;
+
+ /** Base port of the I/O space region. */
+ RTIOPORT IOPortBase;
+ /** The configured ISA IRQ. */
+ uint8_t uIsaIrq;
+ /** The configured ISA DMA channel. */
+ uint8_t uIsaDma;
+ /** If set the link is currently up. */
+ bool fLinkUp;
+ /** If set the link is temporarily down because of a saved state load. */
+ bool fLinkTempDown;
+ /** Number of times we've reported the link down. */
+ uint16_t cLinkDownReported;
+ /** Number of times we've postponed the link restore. */
+ uint16_t cLinkRestorePostponed;
+
+ /** The "hardware" MAC address. */
+ RTMAC MacConfigured;
+
+ /** Set if DPNICSTATER3::pDrv is not NULL. */
+ bool fDriverAttached;
+ /** The LED. */
+ PDMLED Led;
+ /** Status LUN: The LED ports. */
+ PDMILEDPORTS ILeds;
+ /** Partner of ILeds. */
+ R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
+
+ /** Access critical section. */
+ PDMCRITSECT CritSect;
+ /** Event semaphore for blocking on receive. */
+ RTSEMEVENT hEventOutOfRxSpace;
+ /** We are waiting/about to start waiting for more receive buffers. */
+ bool volatile fMaybeOutOfSpace;
+
+ /* MS to wait before we enable the link. */
+ uint32_t cMsLinkUpDelay;
+ /** The device instance number (for logging). */
+ uint32_t iInstance;
+
+ STAMCOUNTER StatReceiveBytes;
+ STAMCOUNTER StatTransmitBytes;
+#ifdef VBOX_WITH_STATISTICS
+ STAMPROFILEADV StatIOReadRZ;
+ STAMPROFILEADV StatIOReadR3;
+ STAMPROFILEADV StatIOWriteRZ;
+ STAMPROFILEADV StatIOWriteR3;
+ STAMPROFILEADV StatReceive;
+ STAMPROFILEADV StatTransmitR3;
+ STAMPROFILEADV StatTransmitRZ;
+ STAMPROFILE StatTransmitSendR3;
+ STAMPROFILE StatTransmitSendRZ;
+ STAMPROFILE StatRxOverflow;
+ STAMCOUNTER StatRxOverflowWakeup;
+ STAMCOUNTER StatRxCanReceiveNow;
+ STAMCOUNTER StatRxCannotReceiveNow;
+ STAMPROFILEADV StatInterrupt;
+ STAMCOUNTER StatDropPktMonitor;
+ STAMCOUNTER StatDropPktRcvrDis;
+ STAMCOUNTER StatDropPktVeryShort;
+ STAMCOUNTER StatDropPktVMNotRunning;
+ STAMCOUNTER StatDropPktNoLink;
+ STAMCOUNTER StatDropPktNoMatch;
+ STAMCOUNTER StatDropPktNoBuffer;
+#endif /* VBOX_WITH_STATISTICS */
+
+ /** NIC-specific ISA I/O ports. */
+ IOMIOPORTHANDLE hIoPortsNic;
+
+ /** Common DP8390 core I/O ports. */
+ IOMIOPORTHANDLE hIoPortsCore;
+
+ /** The runt pad buffer (only really needs 60 bytes). */
+ uint8_t abRuntBuf[64];
+
+ /** The packet buffer. */
+ uint8_t abLocalRAM[DPNIC_MEM_SIZE];
+
+ /** The loopback transmit buffer (avoid stack allocations). */
+ uint8_t abLoopBuf[DPNIC_MEM_SIZE]; /// @todo Can this be smaller?
+} DPNICSTATE, *PDPNICSTATE;
+
+
+/**
+ * DP8390 state for ring-3.
+ *
+ * @implements PDMIBASE
+ * @implements PDMINETWORKDOWN
+ * @implements PDMINETWORKCONFIG
+ * @implements PDMILEDPORTS
+ */
+typedef struct DPNICSTATER3
+{
+ /** Pointer to the device instance. */
+ PPDMDEVINSR3 pDevIns;
+ /** Pointer to the connector of the attached network driver. */
+ PPDMINETWORKUPR3 pDrv;
+ /** Pointer to the attached network driver. */
+ R3PTRTYPE(PPDMIBASE) pDrvBase;
+ /** LUN\#0 + status LUN: The base interface. */
+ PDMIBASE IBase;
+ /** LUN\#0: The network port interface. */
+ PDMINETWORKDOWN INetworkDown;
+ /** LUN\#0: The network config port interface. */
+ PDMINETWORKCONFIG INetworkConfig;
+
+ /** Status LUN: The LED ports. */
+ PDMILEDPORTS ILeds;
+ /** Partner of ILeds. */
+ R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
+} DPNICSTATER3;
+/** Pointer to a DP8390 state structure for ring-3. */
+typedef DPNICSTATER3 *PDPNICSTATER3;
+
+
+/**
+ * DP8390 state for ring-0.
+ */
+typedef struct DPNICSTATER0
+{
+ /** Pointer to the connector of the attached network driver. */
+ PPDMINETWORKUPR0 pDrv;
+} DPNICSTATER0;
+/** Pointer to a DP8390 state structure for ring-0. */
+typedef DPNICSTATER0 *PDPNICSTATER0;
+
+
+/**
+ * DP8390 state for raw-mode.
+ */
+typedef struct DPNICSTATERC
+{
+ /** Pointer to the connector of the attached network driver. */
+ PPDMINETWORKUPRC pDrv;
+} DPNICSTATERC;
+/** Pointer to a DP8390 state structure for raw-mode. */
+typedef DPNICSTATERC *PDPNICSTATERC;
+
+
+/** The DP8390 state structure for the current context. */
+typedef CTX_SUFF(DPNICSTATE) DPNICSTATECC;
+/** Pointer to a DP8390 state structure for the current
+ * context. */
+typedef CTX_SUFF(PDPNICSTATE) PDPNICSTATECC;
+
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+static int dp8390CoreAsyncXmitLocked(PPDMDEVINS pDevIns, PDPNICSTATE pThis, PDPNICSTATECC pThisCC, bool fOnWorkerThread);
+
+/**
+ * Checks if the link is up.
+ * @returns true if the link is up.
+ * @returns false if the link is down.
+ */
+DECLINLINE(bool) dp8390IsLinkUp(PDPNICSTATE pThis)
+{
+ return pThis->fDriverAttached && !pThis->fLinkTempDown && pThis->fLinkUp;
+}
+
+
+/* Table and macro borrowed from DevPCNet.cpp. */
+#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff])
+
+/* generated using the AUTODIN II polynomial
+ * x^32 + x^26 + x^23 + x^22 + x^16 +
+ * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
+ */
+static const uint32_t crctab[256] =
+{
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+ 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+ 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+ 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+ 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+ 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+ 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+ 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+ 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+ 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+ 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+ 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+ 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+ 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+ 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+ 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+ 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+ 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+ 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+};
+
+
+#ifndef ETHER_IS_MULTICAST /* Net/Open BSD macro it seems */
+#define ETHER_IS_MULTICAST(a) ((*(uint8_t *)(a)) & 1)
+#endif
+
+
+/**
+ * Check if incoming frame matches the station address.
+ */
+DECLINLINE(int) padr_match(PDPNICSTATE pThis, const uint8_t *buf)
+{
+ RTNETETHERHDR *hdr = (RTNETETHERHDR *)buf;
+ int result;
+
+ /* Checks own address only; always enabled if receiver on. */
+ result = !memcmp(hdr->DstMac.au8, pThis->core.pg1.PAR, 6);
+
+ return result;
+}
+
+
+/**
+ * Check if incoming frame is an accepted broadcast frame.
+ */
+DECLINLINE(int) padr_bcast(PDPNICSTATE pThis, const uint8_t *buf)
+{
+ static uint8_t aBCAST[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ RTNETETHERHDR *hdr = (RTNETETHERHDR *)buf;
+ int result = pThis->core.rcr.AB && !memcmp(hdr->DstMac.au8, aBCAST, 6);
+ return result;
+}
+
+
+/**
+ * Check if incoming frame is an accepted multicast frame.
+ */
+DECLINLINE(int) padr_mcast(PDPNICSTATE pThis, const uint8_t *buf, int *mcast_type)
+{
+ uint32_t crc = UINT32_MAX;
+ RTNETETHERHDR *hdr = (RTNETETHERHDR *)buf;
+ int result = 0;
+
+ /* If multicast addresses are enabled, and the destination
+ * address is in fact multicast, the address must be run through
+ * the CRC generator and matched against the multicast filter
+ * array.
+ */
+ if (pThis->core.rcr.AM && ETHER_IS_MULTICAST(hdr->DstMac.au8))
+ {
+ unsigned i;
+ const uint8_t *p = buf;
+ unsigned crc_frag, crc_rev;
+ unsigned ma_bit_mask, ma_byte_idx;
+
+ /* Indicate to caller that the address is a multicast one, regardless
+ * of whether it's accepted or not.
+ */
+ *mcast_type = 1;
+
+ for (i = 0; i < sizeof(hdr->DstMac); ++i)
+ CRC(crc, *p++);
+
+ /* The top 6 bits of the CRC calculated from the destination address
+ * becomes an index into the 64-bit multicast address register. Sadly
+ * our CRC algorithm is bit-reversed (Ethernet shifts bits out MSB first)
+ * so instead of the top 6 bits of the CRC we have to take the bottom 6
+ * and reverse the bits.
+ */
+ crc_frag = crc & 63;
+
+ for (i = 0, crc_rev = 0; i < 6; ++i)
+ crc_rev |= ((crc_frag >> i) & 1) * (0x20 >> i);
+
+ ma_bit_mask = 1 << (crc_rev & 7);
+ ma_byte_idx = crc_rev / 8;
+ Log3Func(("crc=%08X, crc_frag=%u, crc_rev=%u, ma_byte_idx=%u, ma_bit_mask=%02X\n", crc, crc_frag, crc_rev, ma_byte_idx, ma_bit_mask));
+ Log3Func(("MAR: %02X:%02X:%02X:%02X %02X:%02X:%02X:%02X\n", pThis->core.pg1.MAR[0], pThis->core.pg1.MAR[1], pThis->core.pg1.MAR[2], pThis->core.pg1.MAR[3], pThis->core.pg1.MAR[4], pThis->core.pg1.MAR[5], pThis->core.pg1.MAR[6], pThis->core.pg1.MAR[7]));
+
+ /* The multicast filtering logic is fairly extensively
+ * verified by EtherLink II diagnostics (3C503.EXE).
+ */
+ if (pThis->core.pg1.MAR[ma_byte_idx] & ma_bit_mask)
+ {
+ Log3Func(("Passed multicast filter\n"));
+ result = 1;
+ }
+ }
+
+ return result;
+}
+
+
+/**
+ * Check if incoming frame is an accepted promiscuous frame.
+ */
+DECLINLINE(int) padr_promi(PDPNICSTATE pThis, const uint8_t *buf)
+{
+ RTNETETHERHDR *hdr = (RTNETETHERHDR *)buf;
+ int result = pThis->core.rcr.PRO && !ETHER_IS_MULTICAST(hdr->DstMac.au8);
+ return result;
+}
+
+
+/**
+ * Update the device IRQ line based on internal state.
+ */
+static void dp8390CoreUpdateIrq(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
+{
+ bool fCoreIrqActive = false;
+ bool fNicIrqActive = false;
+
+ STAM_PROFILE_ADV_START(&pThis->StatInterrupt, a);
+
+ /* Set the ISR.CNT bit based on the counter state (top counter bits ANDed together). */
+ pThis->core.isr.CNT = (pThis->core.CNTR0 & pThis->core.CNTR1 & pThis->core.CNTR2) >> 7;
+
+ /* IRQ is active if a bit is set in ISR and the corresponding bit
+ * is set in IMR. No additional internal state needed.
+ */
+ Assert(!pThis->core.imr.res);
+ if (pThis->core.ISR & pThis->core.IMR)
+ fCoreIrqActive = true;
+
+ /* The 3C503 has additional interrupt sources and control. For other device
+ * types, the extras magically work out to be a no-op.
+ */
+ pThis->ga.fGaIrq = pThis->ga.streg.dtc && !pThis->ga.gacfr.tcm;
+ fNicIrqActive = (fCoreIrqActive && !pThis->ga.gacfr.nim) || (pThis->ga.streg.dtc && !pThis->ga.gacfr.tcm);
+
+ Log2Func(("#%d set irq fNicIrqActive=%d (fCoreIrqActive=%d, fGaIrq=%d)\n", pThis->iInstance, fNicIrqActive, fCoreIrqActive, pThis->ga.fGaIrq));
+
+ /* The IRQ line typically does not change. */
+ if (RT_UNLIKELY(fNicIrqActive != pThis->fNicIrqActive))
+ {
+ LogFunc(("#%d IRQ=%d, state=%d\n", pThis->iInstance, pThis->uIsaIrq, fNicIrqActive));
+ /// @todo Handle IRQ 2/9 elsewhere
+ PDMDevHlpISASetIrq(pDevIns, pThis->uIsaIrq == 2 ? 9 : pThis->uIsaIrq, fNicIrqActive);
+ pThis->fNicIrqActive = fNicIrqActive;
+ }
+ STAM_PROFILE_ADV_STOP(&pThis->StatInterrupt, a);
+}
+
+
+/**
+ * Perform a software reset of the NIC.
+ */
+static void dp8390CoreReset(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
+{
+ LogFlowFunc(("#%d:\n", pThis->iInstance));
+
+ /* DP8390 or DP83901A datasheet, section 11.0. */
+ pThis->core.cr.TXP = 0;
+ pThis->core.cr.STA = 0;
+ pThis->core.cr.STP = 1;
+ pThis->core.cr.RD = DP_CR_RDMA_ABRT;
+ pThis->core.isr.RST = 1;
+ pThis->core.IMR = 0;
+ pThis->core.dcr.LAS = 0;
+ pThis->core.tcr.LB = 0;
+
+ /// @todo Check if this really happens on soft reset
+ /* Clear the internal FIFO including r/w pointers. */
+ memset(&pThis->core.fifo, 0, sizeof(pThis->core.fifo));
+
+ /* Make sure the IRQ line us updated. */
+ dp8390CoreUpdateIrq(pDevIns, pThis);
+}
+
+#ifdef IN_RING3
+
+static DECLCALLBACK(void) dp8390R3WakeupReceive(PPDMDEVINS pDevIns)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ LogFlowFunc(("#%d\n", pThis->iInstance));
+ STAM_COUNTER_INC(&pThis->StatRxOverflowWakeup);
+ if (pThis->hEventOutOfRxSpace != NIL_RTSEMEVENT)
+ RTSemEventSignal(pThis->hEventOutOfRxSpace);
+}
+
+/**
+ * @callback_method_impl{FNPDMTASKDEV,
+ * Signal to R3 that NIC is ready to receive a packet.
+ */
+static DECLCALLBACK(void) dpNicR3CanRxTaskCallback(PPDMDEVINS pDevIns, void *pvUser)
+{
+ RT_NOREF(pvUser);
+ dp8390R3WakeupReceive(pDevIns);
+}
+
+#endif /* IN_RING3 */
+
+/**
+ * Read up to 256 bytes from a single page of local RAM.
+ */
+static void dpLocalRAMReadBuf(PDPNICSTATE pThis, uint16_t addr, unsigned cb, uint8_t *pDst)
+{
+ if ((RT_LOBYTE(addr) + cb) > 256)
+ {
+ LogFunc(("#%d: addr=%04X, cb=%X, cb!!\n", pThis->iInstance, addr, cb));
+ cb = 256 - RT_LOBYTE(addr);
+ }
+
+ /* A single page is always either entirely inside or outside local RAM. */
+ if (pThis->uDevType == DEV_NE1000)
+ {
+ /* Only 14 bits of address are decoded. */
+ addr &= 0x3fff;
+ if (addr >= 0x2000)
+ {
+ /* Local RAM is mapped at 2000h-3FFFh. */
+ addr -= 0x2000;
+ memcpy(pDst, &pThis->abLocalRAM[addr], cb);
+ }
+ else
+ LogFunc(("#%d: Ignoring read at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
+ }
+ else if (pThis->uDevType == DEV_NE2000)
+ {
+ /* Only 15 bits of address are decoded. */
+ addr &= 0x7fff;
+ if (addr >= 0x4000)
+ {
+ /* Local RAM is mapped at 4000h-7FFFh. */
+ addr -= 0x4000;
+ memcpy(pDst, &pThis->abLocalRAM[addr], cb);
+ }
+ else
+ LogFunc(("#%d: Ignoring read at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
+ }
+ else if ((pThis->uDevType == DEV_WD8003) || (pThis->uDevType == DEV_WD8013))
+ {
+ /* Local RAM is mapped starting at address zero. */
+ addr &= DPNIC_MEM_MASK;
+ if (addr + cb <= DPNIC_MEM_SIZE)
+ memcpy(pDst, &pThis->abLocalRAM[addr], cb);
+ else
+ LogFunc(("#%d: Ignoring read at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
+ }
+ else if (pThis->uDevType == DEV_3C503)
+ {
+ /* Only 14 bits of address are decoded. */
+ /// @todo Is there any internal wrap-around in the 3C503 too?
+ addr &= 0x3fff;
+ if (addr >= 0x2000)
+ {
+ /* Local RAM is mapped at 2000h-3FFFh. */
+ addr -= 0x2000;
+ memcpy(pDst, &pThis->abLocalRAM[addr], cb);
+ }
+ else
+ LogFunc(("#%d: Ignoring read at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
+ }
+ else
+ {
+ Assert(0);
+ }
+}
+
+
+#ifdef IN_RING3
+
+/**
+ * Write up to 256 bytes into a single page of local RAM.
+ */
+static void dpLocalRAMWriteBuf(PDPNICSTATE pThis, uint16_t addr, unsigned cb, const uint8_t *pSrc)
+{
+ if ((RT_LOBYTE(addr) + cb) > 256)
+ {
+ LogFunc(("#%d: addr=%04X, cb=%X, cb!!\n", pThis->iInstance, addr, cb));
+ cb = 256 - RT_LOBYTE(addr);
+ }
+
+ /* A single page is always either entirely inside or outside local RAM. */
+ if (pThis->uDevType == DEV_NE1000)
+ {
+ /* Only 14 bits of address are decoded. */
+ addr &= 0x3fff;
+ if (addr >= 0x2000)
+ {
+ /* Local RAM is mapped at 2000h-3FFFh. */
+ addr -= 0x2000;
+ memcpy(&pThis->abLocalRAM[addr], pSrc, cb);
+ }
+ else
+ LogFunc(("#%d: Ignoring write at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
+ }
+ else if (pThis->uDevType == DEV_NE2000)
+ {
+ /* Only 14 bits of address are decoded. */
+ addr &= 0x7fff;
+ if (addr >= 0x4000)
+ {
+ /* Local RAM is mapped at 4000h-7FFFh. */
+ addr -= 0x4000;
+ memcpy(&pThis->abLocalRAM[addr], pSrc, cb);
+ }
+ else
+ LogFunc(("#%d: Ignoring write at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
+ }
+ else if ((pThis->uDevType == DEV_WD8003) || (pThis->uDevType == DEV_WD8013))
+ {
+ /* Local RAM is mapped starting at address zero. */
+ addr &= DPNIC_MEM_MASK;
+ if (addr + cb <= DPNIC_MEM_SIZE)
+ memcpy(&pThis->abLocalRAM[addr], pSrc, cb);
+ else
+ LogFunc(("#%d: Ignoring write at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
+ }
+ else if (pThis->uDevType == DEV_3C503)
+ {
+ /* Only 14 bits of address are decoded. */
+ /// @todo Is there any internal wrap-around in the 3C503 too?
+ addr &= 0x3fff;
+ if (addr >= 0x2000)
+ {
+ /* Local RAM is mapped at 2000h-3FFFh. */
+ addr -= 0x2000;
+ memcpy(&pThis->abLocalRAM[addr], pSrc, cb);
+ }
+ else
+ LogFunc(("#%d: Ignoring write at addr=%04X cb=%u!\n", pThis->iInstance, addr, cb));
+ }
+ else
+ {
+ Assert(0);
+ }
+}
+
+
+/**
+ * Receive an arbitrarily long buffer into the receive ring starting at CLDA.
+ * Update RSR, CLDA, and other state in the process.
+ */
+static void dp8390CoreReceiveBuf(PDPNICSTATE pThis, DP_RSR *pRsr, const uint8_t *src, unsigned cbLeft, bool fLast)
+{
+ LogFlow(("#%d: Initial CURR=%02X00 CLDA=%04X\n", pThis->iInstance, pThis->core.CURR, pThis->core.CLDA));
+
+ while (cbLeft)
+ {
+ unsigned cbWrite;
+ unsigned cbPage;
+
+ /* Write at most up to the end of a page. */
+ cbPage = cbWrite = 256 - pThis->core.clda.CLDA0;
+ if (cbWrite > cbLeft)
+ cbWrite = cbLeft;
+ Log2Func(("#%d: cbLeft=%d CURR=%02X00 CLDA=%04X\n", pThis->iInstance, cbLeft, pThis->core.CURR, pThis->core.CLDA));
+ dpLocalRAMWriteBuf(pThis, pThis->core.CLDA, cbWrite, src);
+ src += cbWrite;
+
+ /* If this is the last fragment of a received frame, we need to
+ * round CLDA up to the next page boundary to correctly evaluate
+ * buffer overflows and the next pointer. Otherwise we just
+ * add however much data we had so that we can continue writing
+ * at the CLDA position.
+ */
+ if (fLast && (cbWrite == cbLeft))
+ {
+ Log3Func(("#%d: Round up: CLDA=%04X cbPage=%X\n", pThis->iInstance, pThis->core.CLDA, cbPage));
+ pThis->core.CLDA += cbPage;
+ }
+ else
+ pThis->core.CLDA += cbWrite;
+
+ Log3Func(("#%d: Final CURR=%02X00 CLDA=%04X\n", pThis->iInstance, pThis->core.CURR, pThis->core.CLDA));
+ /* If at end of ring, wrap around. */
+ if (pThis->core.clda.CLDA1 == pThis->core.PSTOP)
+ pThis->core.clda.CLDA1 = pThis->core.PSTART;
+
+ /* Check for buffer overflow. */
+ if (pThis->core.clda.CLDA1 == pThis->core.BNRY)
+ {
+ pThis->core.isr.OVW = 1;
+ pThis->core.isr.RST = 1;
+ pRsr->MPA = 1; /* Indicates to caller that receive was aborted. */
+ STAM_COUNTER_INC(&pThis->StatDropPktNoBuffer);
+ Log3Func(("#%d: PSTART=%02X00 PSTOP=%02X00 BNRY=%02X00 CURR=%02X00 -- overflow!\n", pThis->iInstance, pThis->core.PSTART, pThis->core.PSTOP, pThis->core.BNRY, pThis->core.CURR));
+ break;
+ }
+ cbLeft -= cbWrite;
+ }
+}
+
+/**
+ * Write incoming data into the packet buffer.
+ */
+static void dp8390CoreReceiveLocked(PPDMDEVINS pDevIns, PDPNICSTATE pThis, const uint8_t *src, size_t cbToRecv)
+{
+ int is_padr = 0, is_bcast = 0, is_mcast = 0, is_prom = 0;
+ int mc_type = 0;
+
+ /*
+ * Drop all packets if the VM is not running yet/anymore.
+ */
+ VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
+ if ( enmVMState != VMSTATE_RUNNING
+ && enmVMState != VMSTATE_RUNNING_LS)
+ {
+ STAM_COUNTER_INC(&pThis->StatDropPktVMNotRunning);
+ return;
+ }
+
+ /*
+ * Drop all packets if the cable is not connected.
+ */
+ if (RT_UNLIKELY(!dp8390IsLinkUp(pThis)))
+ {
+ STAM_COUNTER_INC(&pThis->StatDropPktNoLink);
+ return;
+ }
+
+ /*
+ * Drop everything if NIC is not started or in reset.
+ */
+ if (RT_UNLIKELY(!pThis->core.cr.STA || pThis->core.cr.STP))
+ {
+ STAM_COUNTER_INC(&pThis->StatDropPktRcvrDis);
+ return;
+ }
+
+ /* Drop impossibly short packets. The DP8390 requires a packet to have
+ * at least 8 bytes to even qualify as a runt. We can also assume that
+ * there is a complete destination address at that point.
+ */
+ if (RT_UNLIKELY(cbToRecv < 8))
+ {
+ STAM_COUNTER_INC(&pThis->StatDropPktVeryShort);
+ return;
+ }
+
+ LogFlowFunc(("#%d: size on wire=%d\n", pThis->iInstance, cbToRecv));
+
+ /*
+ * Perform address matching. Packets which do not pass any address
+ * matching logic are ignored.
+ */
+ if ( (is_padr = padr_match(pThis, src))
+ || (is_bcast = padr_bcast(pThis, src))
+ || (is_mcast = padr_mcast(pThis, src, &mc_type))
+ || (is_prom = padr_promi(pThis, src)))
+ {
+ union {
+ uint8_t nRSR;
+ DP_RSR nRsr;
+ };
+ uint32_t fcs = 0;
+
+ nRSR = 0;
+ Log2Func(("#%d Packet passed address filter (is_padr=%d, is_bcast=%d, is_mcast=%d, is_prom=%d), size=%d\n", pThis->iInstance, is_padr, is_bcast, is_mcast, is_prom, cbToRecv));
+
+ if (is_bcast || mc_type)
+ nRsr.PHY = 1;
+
+ /* In Monitor Mode, just increment the tally counter. */
+ if (RT_UNLIKELY(pThis->core.rcr.MON))
+ {
+ STAM_COUNTER_INC(&pThis->StatDropPktMonitor);
+ nRsr.MPA = 1;
+ if (pThis->core.CNTR2 <= 192)
+ pThis->core.CNTR2++; /* Relies on UpdateIrq to be run. */
+ }
+ else
+ {
+ /* Error detection: FCS and frame alignment errors cannot happen,
+ * likewise FIFO overruns can't.
+ * Runts are padded up to the required minimum. Note that the DP8390
+ * documentation considers packets smaller than 64 bytes to be runts,
+ * but that includes 32 bits of FCS.
+ */
+
+ /* See if we need to pad, and how much. Note that if there's any
+ * room left in the receive buffers, a runt will fit even after padding.
+ */
+ if (RT_UNLIKELY(cbToRecv < 60))
+ {
+ /// @todo This really is kind of stupid. We shouldn't be doing any
+ /// padding here, it should be done by the sending side!
+ memset(pThis->abRuntBuf, 0, sizeof(pThis->abRuntBuf));
+ memcpy(pThis->abRuntBuf, src, cbToRecv);
+ cbToRecv = 60;
+ src = pThis->abRuntBuf;
+ }
+
+ LogFlowFunc(("#%d: PSTART=%02X00 PSTOP=%02X00 BNRY=%02X00 CURR=%02X00\n", pThis->iInstance, pThis->core.PSTART, pThis->core.PSTOP, pThis->core.BNRY, pThis->core.CURR));
+
+ /* All packets that passed the address filter are copied to local RAM.
+ * Since the DP8390 does not know how long the frame is until it detects
+ * end of frame, it can only detect an out-of-buffer condition after
+ * filling up all available space. It then reports an error and rewinds
+ * back to where it was before.
+ *
+ * We do not limit the incoming frame size except by available buffer space. /// @todo Except we do??
+ */
+
+ STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cbToRecv);
+
+ /* Copy incoming data to the packet buffer. Start by setting CLDA
+ * to CURR + 4, leaving room for header.
+ */
+ pThis->core.CLDA = RT_MAKE_U16(4, pThis->core.CURR);
+
+ /* Receive the incoming frame. */
+ Assert(cbToRecv < MAX_FRAME); /// @todo Can we actually do bigger?
+ dp8390CoreReceiveBuf(pThis, &nRsr, src, (unsigned)cbToRecv, false);
+ /// @todo Use the same method for runt padding?
+
+ /* If there was no overflow, add the FCS. */
+ if (!nRsr.MPA)
+ {
+ fcs = 0xBADF00D; // Just fake it, does anyone care?
+ dp8390CoreReceiveBuf(pThis, &nRsr, (uint8_t *)&fcs, sizeof(fcs), true);
+ }
+
+ /* Error-free packets are considered intact. */
+ if (!nRsr.CRC && !nRsr.FAE && !nRsr.FO && !nRsr.MPA)
+ {
+ nRsr.PRX = 1;
+ pThis->core.isr.PRX = 1;
+ }
+ else
+ pThis->core.isr.RXE = 1;
+
+ /* For 'intact' packets, write the packet header. */
+ if (nRsr.PRX)
+ {
+ DP_PKT_HDR header;
+
+ /* Round up CLDA to the next page. */
+ if (pThis->core.clda.CLDA0)
+ pThis->core.CLDA = RT_MAKE_U16(0, pThis->core.clda.CLDA1 + 1);
+
+ /* If entire frame was successfully received, write the packet header at the old CURR. */
+ header.rcv_stat = nRSR;
+ header.next_ptr = pThis->core.clda.CLDA1;
+ /// @todo big endian (WTS)
+ header.byte_cnt = (uint16_t)cbToRecv + sizeof(fcs);
+
+ pThis->core.CLDA = RT_MAKE_U16(0, pThis->core.CURR);
+ dpLocalRAMWriteBuf(pThis, pThis->core.CLDA, sizeof(header), (uint8_t *)&header);
+ pThis->core.CLDA += sizeof(header);
+
+ pThis->core.CURR = header.next_ptr;
+ }
+ }
+
+ pThis->core.RSR = nRSR;
+
+ Log2Func(("Receive completed, size=%d, CURR=%02X00, RSR=%02X, ISR=%02X\n", cbToRecv, pThis->core.CURR, pThis->core.RSR, pThis->core.ISR));
+ dp8390CoreUpdateIrq(pDevIns, pThis);
+ }
+ else
+ {
+ Log3Func(("#%d Packet did not pass address filter, size=%d\n", pThis->iInstance, cbToRecv));
+ STAM_COUNTER_INC(&pThis->StatDropPktNoMatch);
+ }
+}
+
+#endif /* IN_RING3 */
+
+
+/**
+ * Transmit a packet from local memory.
+ *
+ * @returns VBox status code. VERR_TRY_AGAIN is returned if we're busy.
+ *
+ * @param pDevIns The device instance data.
+ * @param pThis The device state data.
+ * @param fOnWorkerThread Whether we're on a worker thread or on an EMT.
+ */
+static int dp8390CoreXmitPacket(PPDMDEVINS pDevIns, PDPNICSTATE pThis, bool fOnWorkerThread)
+{
+ PDPNICSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDPNICSTATECC);
+ RT_NOREF_PV(fOnWorkerThread);
+ int rc;
+
+ /*
+ * Grab the xmit lock of the driver as well as the DP8390 device state.
+ */
+ PPDMINETWORKUP pDrv = pThisCC->pDrv;
+ if (pDrv)
+ {
+ rc = pDrv->pfnBeginXmit(pDrv, false /*fOnWorkerThread*/);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Do the transmitting.
+ */
+ int rc2 = dp8390CoreAsyncXmitLocked(pDevIns, pThis, pThisCC, false /*fOnWorkerThread*/);
+ AssertReleaseRC(rc2);
+
+ /*
+ * Release the locks.
+ */
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ }
+ else
+ AssertLogRelRC(rc);
+ if (pDrv)
+ pDrv->pfnEndXmit(pDrv);
+
+ return rc;
+}
+
+
+#ifdef IN_RING3
+
+/**
+ * @callback_method_impl{FNPDMTASKDEV,
+ * This is just a very simple way of delaying sending to R3.
+ */
+static DECLCALLBACK(void) dpNicR3XmitTaskCallback(PPDMDEVINS pDevIns, void *pvUser)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ NOREF(pvUser);
+
+ /*
+ * Transmit if we can.
+ */
+ dp8390CoreXmitPacket(pDevIns, pThis, true /*fOnWorkerThread*/);
+}
+
+#endif /* IN_RING3 */
+
+
+/**
+ * Allocates a scatter/gather buffer for a transfer.
+ *
+ * @returns See PPDMINETWORKUP::pfnAllocBuf.
+ * @param pThis The device instance.
+ * @param pThisCC The device state for current context.
+ * @param cbMin The minimum buffer size.
+ * @param fLoopback Set if we're in loopback mode.
+ * @param pSgLoop Pointer to stack storage for the loopback SG.
+ * @param ppSgBuf Where to return the SG buffer descriptor on success.
+ * Always set.
+ */
+DECLINLINE(int) dp8390XmitAllocBuf(PDPNICSTATE pThis, PDPNICSTATECC pThisCC, size_t cbMin, bool fLoopback,
+ PPDMSCATTERGATHER pSgLoop, PPPDMSCATTERGATHER ppSgBuf)
+{
+ int rc;
+
+ if (!fLoopback)
+ {
+ PPDMINETWORKUP pDrv = pThisCC->pDrv;
+ if (RT_LIKELY(pDrv))
+ {
+ rc = pDrv->pfnAllocBuf(pDrv, cbMin, NULL /*pGso*/, ppSgBuf);
+ AssertMsg(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN || rc == VERR_NET_DOWN || rc == VERR_NO_MEMORY, ("%Rrc\n", rc));
+ if (RT_FAILURE(rc))
+ *ppSgBuf = NULL;
+ }
+ else
+ {
+ rc = VERR_NET_DOWN;
+ *ppSgBuf = NULL;
+ }
+ }
+ else
+ {
+ /* Fake loopback allocator. */
+ pSgLoop->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
+ pSgLoop->cbUsed = 0;
+ pSgLoop->cbAvailable = sizeof(pThis->abLoopBuf);
+ pSgLoop->pvAllocator = pThis;
+ pSgLoop->pvUser = NULL;
+ pSgLoop->cSegs = 1;
+ pSgLoop->aSegs[0].cbSeg = sizeof(pThis->abLoopBuf);
+ pSgLoop->aSegs[0].pvSeg = pThis->abLoopBuf;
+ *ppSgBuf = pSgLoop;
+ rc = VINF_SUCCESS;
+ }
+ return rc;
+}
+
+
+/**
+ * Sends the scatter/gather buffer.
+ *
+ * Wrapper around PDMINETWORKUP::pfnSendBuf, so check it out for the fine print.
+ *
+ * @returns See PDMINETWORKUP::pfnSendBuf.
+ * @param pDevIns The device instance.
+ * @param pThisCC The current context device state.
+ * @param fLoopback Set if we're in loopback mode.
+ * @param pSgBuf The SG to send.
+ * @param fOnWorkerThread Set if we're being called on a work thread. Clear
+ * if an EMT.
+ */
+DECLINLINE(int) dp8390CoreXmitSendBuf(PPDMDEVINS pDevIns, PDPNICSTATECC pThisCC, bool fLoopback, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ int rc;
+ STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, pSgBuf->cbUsed);
+ if (!fLoopback)
+ {
+ STAM_PROFILE_START(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
+ if (pSgBuf->cbUsed > 70) /* unqualified guess */
+ pThis->Led.Asserted.s.fWriting = pThis->Led.Actual.s.fWriting = 1;
+
+ PPDMINETWORKUP pDrv = pThisCC->pDrv;
+ if (RT_LIKELY(pDrv))
+ {
+ rc = pDrv->pfnSendBuf(pDrv, pSgBuf, fOnWorkerThread);
+ AssertMsg(rc == VINF_SUCCESS || rc == VERR_NET_DOWN || rc == VERR_NET_NO_BUFFER_SPACE, ("%Rrc\n", rc));
+ }
+ else
+ rc = VERR_NET_DOWN;
+
+ pThis->Led.Actual.s.fWriting = 0;
+ STAM_PROFILE_STOP(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
+ }
+ else
+ {
+ PDP8390CORE pCore = &pThis->core;
+ union {
+ uint8_t nRSR;
+ DP_RSR nRsr;
+ };
+ unsigned ofs;
+ uint32_t fcs = UINT32_MAX;
+
+ nRSR = 0;
+
+ /* Loopback on the DP8390 is so strange that it must be handled specially. */
+ Assert(pSgBuf->pvAllocator == (void *)pThis);
+ pThis->Led.Asserted.s.fReading = pThis->Led.Actual.s.fReading = 1;
+
+ LogFlowFunc(("#%d: loopback (DCR=%02X LB=%u TCR=%02X RCR=%02X, %u bytes)\n", pThis->iInstance, pCore->DCR, pCore->tcr.LB, pCore->TCR, pCore->RCR, pSgBuf->cbUsed));
+ for (ofs = 0; ofs < pSgBuf->cbUsed; ofs += 16)
+ Log((" %04X: %.*Rhxs\n", ofs, ofs + 16 < pSgBuf->cbUsed ? 16 : pSgBuf->cbUsed - ofs, &pThis->abLoopBuf[ofs]));
+
+ /* A packet shorter than 8 bytes is ignored by the receiving side. */
+ if (pSgBuf->cbUsed < 8)
+ return VINF_SUCCESS;
+
+ /* The loopback mode affects transmit status bits. */
+ switch (pCore->tcr.LB)
+ {
+ case 1: /* Internal loopback within DP8390. */
+ pCore->tsr.CDH = 1;
+ pCore->tsr.CRS = 1;
+ break;
+ case 2: /* Loopback through serializer. */
+ pCore->tsr.CDH = 1;
+ break;
+ case 3: /* External loopback. Requires a cable. */
+ break;
+ default:
+ Assert(0);
+ }
+
+ /* The CRC Inhibit controls whether transmit or receive path uses the
+ * CRC circuitry. If transmit side uses CRC, receive always fails.
+ * We always need to calculate the FCS because either the sending or
+ * the receiving side uses it.
+ */
+ uint8_t *p;
+ uint8_t *pktbuf = pThis->abLoopBuf; /// @todo Point into sgbuf instead?
+ uint16_t pktlen = (uint16_t)pSgBuf->cbUsed;
+ uint16_t fcslen = pktlen;
+ uint8_t abFcs[4];
+ bool fAddrMatched = true;
+
+ /* If the receiver side is calculating FCS, it needs to skip the last
+ * bytes (which are the transmit-side FCS).
+ */
+ if (pCore->tcr.CRC && (pktlen > 4))
+ fcslen -= 4;
+
+ p = pktbuf;
+ while (p != &pktbuf[fcslen])
+ CRC(fcs, *p++);
+
+ fcs = ~fcs;
+ Log3Func(("FCS: %08X\n", fcs));
+ for (ofs = 0; ofs < sizeof(abFcs); ++ofs)
+ {
+ abFcs[ofs] = (uint8_t)fcs;
+ fcs >>= 8;
+ }
+
+ /* The FIFO write pointer gets zeroed on each receive,
+ * but the read pointer does not.
+ */
+ pCore->fifo.wp = 0;
+
+ if (pCore->tcr.CRC)
+ {
+ bool fGoodFcs = true;
+ int is_padr = 0, is_bcast = 0, is_mcast = 0, is_prom = 0;
+ int mc_type = 0;
+
+ /* Always put the first 8 bytes of the packet in the FIFO. */
+ for (ofs = 0; ofs < 8; ++ofs)
+ pCore->fifo.fifo[pCore->fifo.wp++ & 7] = pktbuf[ofs];
+
+
+ /* If the receiving side uses the CRC circuitry, it also performs
+ * destination address matching.
+ */
+ if ( (is_padr = padr_match(pThis, pktbuf))
+ || (is_bcast = padr_bcast(pThis, pktbuf))
+ || (is_mcast = padr_mcast(pThis, pktbuf, &mc_type))
+ || (is_prom = padr_promi(pThis, pktbuf)))
+ {
+ /* Receiving side checks the FCS. */
+ fGoodFcs = !memcmp(&pktbuf[pktlen - 4], abFcs, sizeof(abFcs));
+ Log2Func(("#%d: Address matched (is_padr=%d, is_bcast=%d, is_mcast=%d, is_prom=%d), checking FCS (fGoodFcs=%RTbool)\n", pThis->iInstance, is_padr, is_bcast, is_mcast, is_prom, fGoodFcs));
+
+ /* Now we have to update the FIFO. Since only 8 bytes are visible
+ * in the FIFO after a receive, we can skip most of it.
+ */
+ for ( ; ofs < pktlen; ++ofs)
+ pCore->fifo.fifo[pCore->fifo.wp++ & 7] = pktbuf[ofs];
+
+ }
+ else
+ {
+ nRsr.PRX = 1; /* Weird but true, for non-matching address only! */
+ fAddrMatched = false;
+ Log3Func(("#%d: Address NOT matched, ignoring FCS errors.\n", pThis->iInstance));
+ }
+
+ /* The PHY bit is set when when an enabled broadcast packet is accepted,
+ * but also when an enabled multicast packet arrives regardless of whether
+ * it passes the MAR filter or not.
+ */
+ if (is_bcast || mc_type)
+ nRsr.PHY = 1;
+
+ if (!fGoodFcs)
+ nRsr.CRC = 1;
+ }
+ else
+ {
+ nRsr.CRC = 1; /* Always report CRC error if receiver isn't checking. */
+
+ /* Now we have to update the FIFO. Since only 8 bytes are visible
+ * in the FIFO after a receive, we can skip most of it.
+ */
+ for (ofs = 0; ofs < pktlen; ++ofs)
+ pCore->fifo.fifo[pCore->fifo.wp++ & 7] = pktbuf[ofs];
+
+ /* Stuff the generated FCS in the FIFO. */
+ for (ofs = 0; ofs < sizeof(abFcs); ++ofs)
+ pCore->fifo.fifo[pCore->fifo.wp++ & 7] = abFcs[ofs];
+ }
+
+ /* And now put the packet length in the FIFO. */
+ if (fAddrMatched || 1)
+ {
+ pCore->fifo.fifo[pCore->fifo.wp++ & 7] = RT_LOBYTE(pktlen);
+ pCore->fifo.fifo[pCore->fifo.wp++ & 7] = RT_HIBYTE(pktlen);
+ pCore->fifo.fifo[pCore->fifo.wp++ & 7] = RT_HIBYTE(pktlen); /* Yes, written twice! */
+ }
+
+ Log(("FIFO: rp=%u, wp=%u\n", pCore->fifo.rp & 7, pCore->fifo.wp & 7));
+ Log((" %Rhxs\n", &pCore->fifo.fifo));
+
+ if (nRsr.CRC)
+ pCore->isr.RXE = 1;
+ pCore->RSR = nRSR;
+
+ pThis->Led.Actual.s.fReading = 0;
+
+ /* Return success so that caller sets TSR.PTX and ISR.PTX. */
+ rc = VINF_SUCCESS;
+ }
+ return rc;
+}
+
+
+/**
+ * Reads the entire frame into the scatter gather buffer.
+ */
+DECLINLINE(void) dp8390CoreXmitRead(PPDMDEVINS pDevIns, const unsigned uLocalAddr, const unsigned cbFrame, PPDMSCATTERGATHER pSgBuf, bool fLoopback)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ unsigned uOfs = 0;
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ Assert(pSgBuf->cbAvailable >= cbFrame);
+
+ pSgBuf->cbUsed = cbFrame;
+
+ LogFlowFunc(("#%d: uLocalAddr=%04X cbFrame=%d\n", pThis->iInstance, uLocalAddr, cbFrame));
+ /* Have to figure out where the address is in local RAM. */
+ if (pThis->uDevType == DEV_NE1000)
+ {
+ /* Only 14 bits of address are decoded. */
+ uOfs = uLocalAddr & 0x3fff;
+ if (uOfs >= 0x2000)
+ {
+ /* Local RAM is mapped at 2000h-3FFFh. */
+ uOfs -= 0x2000;
+ }
+ else
+ {
+ /// @todo What are we supposed to do?!
+ LogFunc(("#%d: uOfs=%u, don't know what to do!!\n", pThis->iInstance, uOfs));
+ }
+ }
+ else if (pThis->uDevType == DEV_NE2000)
+ {
+ /* Only 15 bits of address are decoded. */
+ uOfs = uLocalAddr & 0x7fff;
+ if (uOfs >= 0x4000)
+ {
+ /* Local RAM is mapped at 4000h-7FFFh. */
+ uOfs -= 0x4000;
+ }
+ else
+ {
+ /// @todo What are we supposed to do?!
+ LogFunc(("#%d: uOfs=%u, don't know what to do!!\n", pThis->iInstance, uOfs));
+ }
+ }
+ else if ((pThis->uDevType == DEV_WD8003) || (pThis->uDevType == DEV_WD8013))
+ {
+ /* Not much to do, WD was nice enough to put the RAM at the start of DP8390's address space. */
+ uOfs = uLocalAddr & DPNIC_MEM_MASK;
+ }
+ else if (pThis->uDevType == DEV_3C503)
+ {
+ /* Only 14 bits of address are decoded. */
+ uOfs = uLocalAddr & 0x3fff;
+ if (uOfs >= 0x2000)
+ {
+ /* Local RAM is mapped at 2000h-3FFFh. */
+ uOfs -= 0x2000;
+ }
+ else
+ {
+ /// @todo What are we supposed to do?!
+ LogFunc(("#%d: uOfs=%u, don't know what to do!!\n", pThis->iInstance, uOfs));
+ }
+ }
+ else
+ {
+ Assert(0);
+ }
+
+ if (!fLoopback)
+ {
+ /* Fast path for normal transmit, ignores DCR.WTS. */
+ if (uOfs + cbFrame <= sizeof(pThis->abLocalRAM))
+ memcpy(pSgBuf->aSegs[0].pvSeg, &pThis->abLocalRAM[uOfs], cbFrame);
+ else
+ memset(pSgBuf->aSegs[0].pvSeg, 0xEE, cbFrame);
+ }
+ else
+ {
+ /* If DCR.WTS is set, only every other byte actually goes through loopback. */
+ const uint8_t *src = &pThis->abLocalRAM[uOfs];
+ uint8_t *dst = (uint8_t *)pSgBuf->aSegs[0].pvSeg;
+ int cbDst = cbFrame;
+ int step = 1 << pThis->core.dcr.WTS;
+
+ /* Depending on DCR.BOS, take either odd or even bytes when DCR.WTS is set. */
+ if (pThis->core.dcr.WTS && !pThis->core.dcr.BOS)
+ ++src;
+
+ while (cbDst-- && (src <= &pThis->abLocalRAM[DPNIC_MEM_SIZE]))
+ {
+ *dst++ = *src;
+ src += step;
+ }
+
+ /* The address should perhaps wrap around -- depends on card design. */
+ if (cbDst != -1)
+ {
+ while (cbDst--)
+ *dst++ = 0xEE;
+ }
+ Assert(cbDst == -1);
+ }
+}
+
+/**
+ * Try to transmit a frame.
+ */
+static void dp8390CoreStartTransmit(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
+{
+ /*
+ * Transmit the packet if possible, defer it if we cannot do it
+ * in the current context.
+ */
+ pThis->core.TSR = 0; /* Clear transmit status. */
+ pThis->core.NCR = 0; /* Clear collision counter. */
+#if defined(IN_RING0) || defined(IN_RC)
+ PDPNICSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDPNICSTATECC);
+ if (!pThisCC->pDrv)
+ {
+ int rc = PDMDevHlpTaskTrigger(pDevIns, pThis->hXmitTask);
+ AssertRC(rc);
+ }
+ else
+#endif
+ {
+ int rc = dp8390CoreXmitPacket(pDevIns, pThis, false /*fOnWorkerThread*/);
+ if (rc == VERR_TRY_AGAIN)
+ rc = VINF_SUCCESS;
+ AssertRC(rc);
+ }
+}
+
+
+/**
+ * If a packet is waiting, poke the receiving machinery.
+ *
+ * @threads EMT.
+ */
+static void dp8390CoreKickReceive(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
+{
+ if (pThis->fMaybeOutOfSpace)
+ {
+ LogFlow(("Poking receive thread.\n"));
+#ifdef IN_RING3
+ dp8390R3WakeupReceive(pDevIns);
+#else
+ int rc = PDMDevHlpTaskTrigger(pDevIns, pThis->hCanRxTask);
+ AssertRC(rc);
+#endif
+ }
+}
+
+/**
+ * Try transmitting a frame.
+ *
+ * @threads TX or EMT.
+ */
+static int dp8390CoreAsyncXmitLocked(PPDMDEVINS pDevIns, PDPNICSTATE pThis, PDPNICSTATECC pThisCC, bool fOnWorkerThread)
+{
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+
+ /*
+ * Just drop it if not transmitting. Can happen with delayed transmits
+ * if transmit was disabled in the meantime.
+ */
+ if (RT_UNLIKELY(!pThis->core.cr.TXP))
+ {
+ LogFunc(("#%d: Nope, CR.TXP is off (fOnWorkerThread=%RTbool)\n", pThis->iInstance, fOnWorkerThread));
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Blast out data from the packet buffer.
+ */
+ int rc;
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ do
+ {
+ /* Don't send anything when the link is down. */
+ if (RT_UNLIKELY( !dp8390IsLinkUp(pThis)
+ && pThis->cLinkDownReported > DPNIC_MAX_LINKDOWN_REPORTED)
+ )
+ break;
+
+ bool const fLoopback = pThis->core.tcr.LB != 0;
+ PDMSCATTERGATHER SgLoop;
+ PPDMSCATTERGATHER pSgBuf;
+
+ /*
+ * Sending is easy peasy, there is by definition always
+ * a complete packet on hand.
+ */
+ unsigned cb = pThis->core.TBCR; /* Packet size. */
+ const int adr = RT_MAKE_U16(0, pThis->core.TPSR);
+ LogFunc(("#%d: cb=%d, adr=%04X\n", pThis->iInstance, cb, adr));
+
+ if (RT_LIKELY(dp8390IsLinkUp(pThis) || fLoopback))
+ {
+ if (RT_LIKELY(cb <= MAX_FRAME))
+ {
+ /* Loopback fun! */
+ if (RT_UNLIKELY(fLoopback && pThis->core.dcr.WTS))
+ {
+ cb /= 2;
+ Log(("Loopback with DCR.WTS set -> cb=%d\n", cb));
+ }
+
+ rc = dp8390XmitAllocBuf(pThis, pThisCC, cb, fLoopback, &SgLoop, &pSgBuf);
+ if (RT_SUCCESS(rc))
+ {
+ dp8390CoreXmitRead(pDevIns, adr, cb, pSgBuf, fLoopback);
+ rc = dp8390CoreXmitSendBuf(pDevIns, pThisCC, fLoopback, pSgBuf, fOnWorkerThread);
+ Log2Func(("#%d: rc=%Rrc\n", pThis->iInstance, rc));
+ }
+ else if (rc == VERR_TRY_AGAIN)
+ {
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ LogFunc(("#%d: rc=%Rrc\n", pThis->iInstance, rc));
+ return VINF_SUCCESS;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ pThis->core.tsr.PTX = 1;
+ pThis->core.isr.PTX = 1;
+ }
+ else
+ {
+ pThis->core.tsr.COL = 1; /* Pretend there was a collision. */
+ pThis->core.isr.TXE = 1;
+ }
+ }
+ else
+ {
+ /* Signal error, as this violates the Ethernet specs. Note that the DP8390
+ * hardware does *not* limit the packet length.
+ */
+ LogRel(("DPNIC#%d: Attempt to transmit illegal giant frame (%u bytes) -> signaling error\n", pThis->iInstance, cb));
+ pThis->core.tsr.OWC = 1; /* Pretend there was an out-of-window collision. */
+ pThis->core.isr.TXE = 1;
+ }
+ }
+ else
+ {
+ /* Signal a transmit error pretending there was a collision. */
+ pThis->core.tsr.COL = 1;
+ pThis->core.isr.TXE = 1;
+ pThis->cLinkDownReported++;
+ }
+ /* Transmit officially done, update register state. */
+ pThis->core.cr.TXP = 0;
+ pThis->core.TBCR = 0;
+ LogFlowFunc(("#%d: TSR=%02X, ISR=%02X\n", pThis->iInstance, pThis->core.TSR, pThis->core.ISR));
+
+ } while (0); /* No loop, because there isn't ever more than one packet to transmit. */
+
+ dp8390CoreUpdateIrq(pDevIns, pThis);
+
+ /* If there's anything waiting, this should be a good time to recheck. */
+ dp8390CoreKickReceive(pDevIns, pThis);
+
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
+
+ return VINF_SUCCESS;
+}
+
+/* -=-=-=-=-=- I/O Port access -=-=-=-=-=- */
+
+
+static uint32_t dp8390CoreRead(PPDMDEVINS pDevIns, PDPNICSTATE pThis, int ofs)
+{
+ uint8_t val;
+
+ /* The 3C503 can read the PROM instead of the DP8390 registers. */
+ if (pThis->ga.gacr.ealo)
+ return pThis->aPROM[ofs % 0xf];
+ else if (pThis->ga.gacr.eahi)
+ return pThis->aPROM[16 + (ofs % 0xf)];
+
+ /* Command Register exists in all pages. */
+ if (ofs == DPR_CR)
+ return pThis->core.CR;
+
+ if (pThis->core.cr.PS == 0)
+ {
+ switch (ofs)
+ {
+ case DPR_P0_R_CLDA0:
+ return pThis->core.clda.CLDA0;
+ case DPR_P0_R_CLDA1:
+ return pThis->core.clda.CLDA1;
+ case DPR_P0_BNRY:
+ return pThis->core.BNRY;
+ case DPR_P0_R_TSR:
+ return pThis->core.TSR;
+ case DPR_P0_R_NCR:
+ return pThis->core.NCR;
+ case DPR_P0_R_FIFO:
+ return pThis->core.fifo.fifo[pThis->core.fifo.rp++ & 7]; /// @todo Abstract the mask somehow?
+ case DPR_P0_ISR:
+ return pThis->core.ISR;
+ case DPR_P0_R_CRDA0:
+ return pThis->core.crda.CRDA0;
+ case DPR_P0_R_CRDA1:
+ return pThis->core.crda.CRDA1;
+ case DPR_P0_R_RSR:
+ return pThis->core.RSR;
+ case DPR_P0_R_CNTR0:
+ val = pThis->core.CNTR0;
+ pThis->core.CNTR0 = 0; /* Cleared by reading. */
+ dp8390CoreUpdateIrq(pDevIns, pThis);
+ return val;
+ case DPR_P0_R_CNTR1:
+ val = pThis->core.CNTR1;
+ pThis->core.CNTR1 = 0; /* Cleared by reading. */
+ dp8390CoreUpdateIrq(pDevIns, pThis);
+ return val;
+ case DPR_P0_R_CNTR2:
+ val = pThis->core.CNTR2;
+ pThis->core.CNTR2 = 0; /* Cleared by reading. */
+ dp8390CoreUpdateIrq(pDevIns, pThis);
+ return val;
+ default:
+ return 0; /// @todo or 0xFF? or something else?
+ }
+ }
+ else if (pThis->core.cr.PS == 1)
+ {
+ /* Page 1 is easy, most registers are stored directly. */
+ if (ofs == DPR_P1_CURR)
+ return pThis->core.CURR;
+ else
+ return pThis->core.PG1[ofs];
+ }
+ else if (pThis->core.cr.PS == 2)
+ {
+ /* Page 2 is for diagnostics. Reads many registers that
+ * are write-only in Page 0.
+ */
+ switch (ofs)
+ {
+ case DPR_P2_R_PSTART:
+ return pThis->core.PSTART;
+ case DPR_P2_R_PSTOP:
+ return pThis->core.PSTOP;
+ case DPR_P2_RNXTPP:
+ return pThis->core.rnxtpp;
+ case DPR_P2_R_TPSR:
+ return pThis->core.TPSR;
+ case DPR_P2_LNXTPP:
+ return pThis->core.lnxtpp;
+ case DPR_P2_ADRCU:
+ case DPR_P2_ADRCL:
+ return 0; /// @todo What's this?
+ case DPR_P2_R_RCR:
+ return pThis->core.RCR;
+ case DPR_P2_R_TCR:
+ return pThis->core.TCR;
+ case DPR_P2_R_DCR:
+ return pThis->core.DCR;
+ case DPR_P2_R_IMR:
+ return pThis->core.IMR;
+ default:
+ return 0; /// @todo Or 0xFF? Or something else?
+ }
+ }
+ else
+ {
+ /* Page 3 is undocumented and unimplemented. */
+ LogFunc(("Reading page 3 register: ofs=%X!\n", ofs));
+ return 0;
+ }
+}
+
+
+static int dp8390CoreWriteCR(PPDMDEVINS pDevIns, PDPNICSTATE pThis, uint32_t val)
+{
+ union {
+ uint8_t nCR;
+ DP_CR nCr;
+ };
+
+ nCR = val;
+ LogFlow(("val=%02X, old=%02X\n", val, pThis->core.CR));
+ if (nCr.STP != pThis->core.cr.STP)
+ {
+ if (nCr.STP)
+ {
+ /* Stop the engine -- software reset. */
+ pThis->core.cr.STP = 1;
+ pThis->core.isr.RST = 1;
+ }
+ else
+ {
+ /* Clear the stop condition. */
+ pThis->core.cr.STP = 0;
+
+ /* And possibly start up right away. */
+ if (nCr.STA)
+ pThis->core.cr.STA = 1;
+
+ /* The STA bit may have been set all along. */
+ if (pThis->core.cr.STA)
+ pThis->core.isr.RST = 0;
+ }
+
+ /* Unblock receive thread if necessary, possibly drop any packets. */
+ dp8390CoreKickReceive(pDevIns, pThis);
+ }
+ if (nCr.STA && !pThis->core.cr.STA)
+ {
+ /* Start the engine. It is not clearly documented but the STA bit is
+ * sticky, and once it's set only a hard reset can clear it. Setting the
+ * STP bit doesn't clear it.
+ */
+ pThis->core.cr.STA = 1;
+ pThis->core.isr.RST = 0;
+
+ /* Unblock receive thread. */
+ dp8390CoreKickReceive(pDevIns, pThis);
+ }
+ if (nCr.TXP && !pThis->core.cr.TXP)
+ {
+ /* Kick off a transmit. */
+ pThis->core.cr.TXP = 1; /* Indicate transmit in progress. */
+ dp8390CoreStartTransmit(pDevIns, pThis);
+ }
+
+ /* It is not possible to write a zero (invalid value) to the RD bits. */
+ if (nCr.RD == DP_CR_RDMA_INVL)
+ nCr.RD = DP_CR_RDMA_ABRT;
+
+ if (nCr.RD != pThis->core.cr.RD)
+ {
+ /* Remote DMA state change. */
+ if (nCr.RD & DP_CR_RDMA_ABRT)
+ {
+ /* Abort. */
+ LogFunc(("RDMA Abort! RD=%d RSAR=%04X RBCR=%04X CRDA=%04X\n", nCr.RD, pThis->core.RSAR, pThis->core.RBCR, pThis->core.CRDA));
+ }
+ else if (nCr.RD == DP_CR_RDMA_SP)
+ {
+ DP_PKT_HDR header;
+
+ /* Read a packet header from memory at BNRY. */
+ dpLocalRAMReadBuf(pThis, pThis->core.BNRY, sizeof(header), (uint8_t*)&header);
+
+ pThis->core.CRDA = RT_MAKE_U16(0, pThis->core.BNRY);
+ pThis->core.RBCR = header.byte_cnt;
+
+ LogFunc(("RDMA SP: RD=%d RSAR=%04X RBCR=%04X CRDA=%04X\n", nCr.RD, pThis->core.RSAR, pThis->core.RBCR, pThis->core.CRDA));
+ }
+ else
+ {
+ /* Starting remote DMA read or write. */
+ LogFunc(("RDMA: RD=%d RSAR=%04X RBCR=%04X\n", nCr.RD, pThis->core.RSAR, pThis->core.RBCR));
+ }
+ pThis->core.cr.RD = nCr.RD;
+ /* NB: The current DMA address (CRDA) is not modified here. */
+ }
+ /* Set the page select bits. */
+ pThis->core.cr.PS = nCr.PS;
+
+ return VINF_SUCCESS;
+}
+
+static int dp8390CoreWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis, int ofs, uint32_t val)
+{
+ int rc = VINF_SUCCESS;
+ bool fUpdateIRQ = false;
+
+ Log2Func(("#%d: page=%d reg=%X val=%02X\n", pThis->iInstance, pThis->core.cr.PS, ofs, val));
+
+ /* Command Register exists in all pages. */
+ if (ofs == DPR_CR)
+ {
+ rc = dp8390CoreWriteCR(pDevIns, pThis, val);
+ }
+ else if (pThis->core.cr.PS == 0)
+ {
+ switch (ofs)
+ {
+ case DPR_P0_W_PSTART:
+ pThis->core.PSTART = val;
+ pThis->core.CURR = val;
+ break;
+ case DPR_P0_W_PSTOP:
+ pThis->core.PSTOP = val;
+ break;
+ case DPR_P0_BNRY:
+ if (pThis->core.BNRY != val)
+ {
+ pThis->core.BNRY = val;
+ /* Probably made more room in receive ring. */
+ dp8390CoreKickReceive(pDevIns, pThis);
+ }
+ break;
+ case DPR_P0_W_TPSR:
+ pThis->core.TPSR = val;
+ break;
+ case DPR_P0_W_TBCR0:
+ pThis->core.tbcr.TBCR0 = val;
+ break;
+ case DPR_P0_W_TBCR1:
+ pThis->core.tbcr.TBCR1 = val;
+ break;
+ case DPR_P0_ISR:
+ /* Bits are cleared by writing 1 to them, except for bit 7 (RST). */
+ pThis->core.ISR &= ~val | RT_BIT(7);
+ fUpdateIRQ = true;
+ break;
+ case DPR_P0_W_RSAR0:
+ /* NE2000 ODI driver v2.12 detects card presence by writing RSAR0
+ * and checking if CRDA0 changes to the same value.
+ */
+ pThis->core.rsar.RSAR0 = val;
+ pThis->core.crda.CRDA0 = val;
+ break;
+ case DPR_P0_W_RSAR1:
+ pThis->core.rsar.RSAR1 = val;
+ pThis->core.crda.CRDA1 = val;
+ break;
+ case DPR_P0_W_RBCR0:
+ pThis->core.rbcr.RBCR0 = val;
+ break;
+ case DPR_P0_W_RBCR1:
+ pThis->core.rbcr.RBCR1 = val;
+ break;
+ case DPR_P0_W_RCR:
+ pThis->core.RCR = val;
+ pThis->core.rsr.DIS = pThis->core.rcr.MON;
+ break;
+ case DPR_P0_W_TCR:
+ pThis->core.TCR = val;
+ break;
+ case DPR_P0_W_DCR:
+ pThis->core.DCR = val;
+ break;
+ case DPR_P0_W_IMR:
+ pThis->core.IMR = val & 0x7f; /* Don't let the high bit get set. */
+ fUpdateIRQ = true;
+ break;
+ default:
+ Assert(0);
+ break;
+ }
+ }
+ else if (pThis->core.cr.PS == 1)
+ {
+ /* Page 1 is easy, most registers are stored directly. */
+ if (ofs == DPR_P1_CURR)
+ {
+ pThis->core.CURR = val;
+ }
+ else
+ pThis->core.PG1[ofs] = val;
+ }
+ else if (pThis->core.cr.PS == 2)
+ {
+ switch (ofs)
+ {
+ case DPR_P2_W_CLDA0:
+ pThis->core.clda.CLDA0 = val;
+ break;
+ case DPR_P2_W_CLDA1:
+ pThis->core.clda.CLDA1 = val;
+ break;
+ case DPR_P2_RNXTPP:
+ pThis->core.rnxtpp = val;
+ break;
+ case DPR_P2_LNXTPP:
+ pThis->core.lnxtpp = val;
+ break;
+ case DPR_P2_ADRCU:
+ case DPR_P2_ADRCL:
+ /// @todo What are these?
+ break;
+ default:
+ LogFunc(("Writing unimplemented register: Page 2, offset=%d, val=%02X!\n", ofs, val));
+ break;
+ }
+ }
+ else
+ {
+ /* Page 3 is undocumented and unimplemented. */
+ LogFunc(("Writing page 3 register: offset=%d, val=%02X!\n", ofs, val));
+ }
+
+ if (fUpdateIRQ)
+ dp8390CoreUpdateIrq(pDevIns, pThis);
+
+ return rc;
+}
+
+
+static void neLocalRAMWrite8(PDPNICSTATE pThis, uint16_t addr, uint8_t val)
+{
+ if (pThis->uDevType == DEV_NE1000)
+ {
+ /* Only 14 bits of address are decoded. */
+ addr &= 0x3fff;
+ if (addr >= 0x2000)
+ {
+ /* Local RAM is mapped at 2000h-3FFFh. */
+ addr -= 0x2000;
+ pThis->abLocalRAM[addr] = val;
+ }
+ }
+ else if (pThis->uDevType == DEV_NE2000)
+ {
+ /* Only 15 bits of address are decoded. */
+ addr &= 0x7fff;
+ if (addr >= 0x4000)
+ {
+ /* Local RAM is mapped at 4000h-7FFFh. */
+ addr -= 0x4000;
+ pThis->abLocalRAM[addr] = val;
+ }
+ }
+ else
+ {
+ Assert(0);
+ }
+}
+
+
+static void neLocalRAMWrite16(PDPNICSTATE pThis, uint16_t addr, uint16_t val)
+{
+ if (pThis->uDevType == DEV_NE2000)
+ {
+ /* Only 14 bits of address are decoded, word aligned. */
+ addr &= 0x7ffe;
+ if (addr >= 0x4000)
+ {
+ /* Local RAM is mapped at 4000h-7FFFh. */
+ addr -= 0x4000;
+ pThis->abLocalRAM[addr+0] = RT_LOBYTE(val);
+ pThis->abLocalRAM[addr+1] = RT_HIBYTE(val);
+ }
+ }
+ else
+ {
+ Assert(0);
+ }
+}
+
+
+static uint8_t neLocalRAMRead8(PDPNICSTATE pThis, uint16_t addr)
+{
+ uint8_t val = 0xff;
+
+ if (pThis->uDevType == DEV_NE1000)
+ {
+ /* Only 14 bits of address are decoded. */
+ addr &= 0x3fff;
+ if (addr >= 0x2000)
+ {
+ /* Local RAM is mapped at 2000h-3FFFh. */
+ addr -= 0x2000;
+ val = pThis->abLocalRAM[addr];
+ }
+ else
+ {
+ /* The PROM is mapped below 2000h, effectively only 4 bits decoded.
+ * NE1000 emulation uses top 16 bytes of the PROM.
+ */
+ val = pThis->aPROM[(addr & 0x0f) + 16]; /// @todo Use a constant
+ }
+ }
+ else if (pThis->uDevType == DEV_NE2000)
+ {
+ /* Only 15 bits of address are decoded. */
+ addr &= 0x7fff;
+ if (addr >= 0x4000)
+ {
+ /* Local RAM is mapped at 4000h-7FFFh. */
+ addr -= 0x4000;
+ val = pThis->abLocalRAM[addr];
+ }
+ else
+ {
+ /* The PROM is mapped below 4000h, effectively only 4 bits decoded.
+ * Address bits 1:4 from the bus are connected to address pins 0:3
+ * on the PROM.
+ */
+ val = pThis->aPROM[(addr & 0x1f) >> 1]; /// @todo use a constant
+ }
+ }
+ else
+ {
+ Assert(0);
+ }
+ return val;
+}
+
+
+static uint16_t neLocalRAMRead16(PDPNICSTATE pThis, uint16_t addr)
+{
+ uint16_t val = 0xffff;
+
+ if (pThis->uDevType == DEV_NE2000)
+ {
+ /* Only 14 bits of address are decoded, word aligned. */
+ addr &= 0x7ffe;
+ if (addr >= 0x4000)
+ {
+ /* Local RAM is mapped at 4000h-7FFFh. */
+ addr -= 0x4000;
+ val = RT_MAKE_U16(pThis->abLocalRAM[addr], pThis->abLocalRAM[addr+1]);
+ }
+ else
+ {
+ uint8_t uPromByte;
+
+ /* The PROM is mapped below 4000h, effectively only 4 bits decoded.
+ * Address bits 1:4 from the bus are connected to address pins 0:3
+ * on the PROM.
+ */
+ uPromByte = pThis->aPROM[(addr & 0x1f) >> 1];
+ val = RT_MAKE_U16(uPromByte, uPromByte);
+ }
+ }
+ else
+ {
+ Assert(0);
+ }
+ return val;
+}
+
+
+static int neDataPortWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis, uint16_t val)
+{
+ /* Remote Write; ignored if Remote DMA command is not 'Write'. */
+ if (pThis->core.cr.RD == DP_CR_RDMA_WR)
+ {
+ /// @todo Also do nothing if DCR.LAS set?
+ if (pThis->core.dcr.WTS)
+ {
+ Log3Func(("RDMA16 write %04X to local addr %04X\n", val, pThis->core.CRDA));
+ neLocalRAMWrite16(pThis, pThis->core.CRDA, val);
+ }
+ else
+ {
+ Log3Func(("RDMA8 write %02X to local addr %04X\n", val, pThis->core.CRDA));
+ neLocalRAMWrite8(pThis, pThis->core.CRDA, val);
+ }
+ pThis->core.CRDA += 1 << pThis->core.dcr.WTS;
+ if ((pThis->core.crda.CRDA1 == pThis->core.PSTOP) && (pThis->core.PSTOP != pThis->core.PSTART))
+ {
+ LogFunc(("RDMA wrap / write!! (CRDA=%04X PSTOP=%02X00 PSTART=%02X00)\n", pThis->core.CRDA, pThis->core.PSTOP, pThis->core.PSTART));
+ Assert(!pThis->core.crda.CRDA0); /// @todo Can misalignment actually happen?
+ pThis->core.crda.CRDA1 = pThis->core.PSTART;
+ }
+ pThis->core.RBCR -= 1;
+
+ /* Carefully decrement if WTS set so we don't overshoot and miss EOP. */
+ if (pThis->core.dcr.WTS && pThis->core.RBCR)
+ pThis->core.RBCR -= 1;
+
+ if (!pThis->core.RBCR)
+ {
+ LogFunc(("RDMA EOP / write\n"));
+ pThis->core.isr.RDC = 1;
+ pThis->core.cr.RD = 0;
+ dp8390CoreUpdateIrq(pDevIns, pThis);
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+static uint16_t neDataPortRead(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
+{
+ uint16_t val = 0x1234;
+
+ /* Remote Read; ignored if Remote DMA command is not 'Read'. */
+ if (pThis->core.cr.RD == DP_CR_RDMA_RD)
+ {
+ /// @todo Also do nothing if DCR.LAS set?
+ if (pThis->core.dcr.WTS)
+ {
+ val = neLocalRAMRead16(pThis, pThis->core.CRDA);
+ Log3Func(("RDMA16 read from local addr %04X: %04X\n", pThis->core.CRDA, val));
+ }
+ else
+ {
+ val = neLocalRAMRead8(pThis, pThis->core.CRDA);
+ Log3Func(("RDMA8 read from local addr %04X: %02X\n", pThis->core.CRDA, val));
+ }
+ pThis->core.CRDA += 1 << pThis->core.dcr.WTS;
+ /// @todo explain that PSTOP=PSTART check is only to reduce logging/busywork
+ if ((pThis->core.crda.CRDA1 == pThis->core.PSTOP) && (pThis->core.PSTOP != pThis->core.PSTART))
+ {
+ Log3Func(("RDMA wrap / read (CRDA=%04X PSTOP=%02X00 PSTART=%02X00)\n", pThis->core.CRDA, pThis->core.PSTOP, pThis->core.PSTART));
+ Assert(!pThis->core.crda.CRDA0); /// @todo can misalignment happen?
+ pThis->core.crda.CRDA1 = pThis->core.PSTART;
+ }
+ pThis->core.RBCR -= 1;
+
+ /* Carefully decrement if WTS set so we don't overshoot and miss EOP. */
+ if (pThis->core.dcr.WTS && pThis->core.RBCR)
+ pThis->core.RBCR -= 1;
+
+ if (!pThis->core.RBCR)
+ {
+ LogFunc(("RDMA EOP / read\n"));
+ pThis->core.isr.RDC = 1;
+ pThis->core.cr.RD = 0;
+ dp8390CoreUpdateIrq(pDevIns, pThis);
+ }
+ }
+ return val;
+}
+
+
+static int neResetPortWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
+{
+ LogFlowFunc(("\n"));
+ dp8390CoreReset(pDevIns, pThis);
+ return VINF_SUCCESS;
+}
+
+
+static int dpNeIoWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis, uint32_t addr, uint32_t val)
+{
+ int reg = addr & 0x0f;
+ int rc = VINF_SUCCESS;
+
+ Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val & 0xff));
+
+ /* The NE2000 has 8 bytes of data port followed by 8 bytes of reset port.
+ * In contrast, the NE1000 has 4 bytes of data port followed by 4 bytes
+ * of reset port, aliased twice within the 16-byte range.
+ */
+ if (pThis->uDevType == DEV_NE2000)
+ reg >>= 1;
+ if (reg & 0x04)
+ rc = neResetPortWrite(pDevIns, pThis);
+ else
+ rc = neDataPortWrite(pDevIns, pThis, val);
+
+ return rc;
+}
+
+
+static uint32_t neIoRead(PPDMDEVINS pDevIns, PDPNICSTATE pThis, uint32_t addr)
+{
+ uint32_t val = UINT32_MAX;
+ int reg = addr & 0x0f;
+
+ /* The NE2000 has 8 bytes of data port followed by 8 bytes of reset port.
+ * In contrast, the NE1000 has 4 bytes of data port followed by 4 bytes
+ * of reset port, aliased twice within the 16-byte range.
+ */
+ if (pThis->uDevType == DEV_NE2000)
+ reg >>= 1;
+ if (reg & 0x04)
+ val = 0x52; /// @todo Check what really happens
+ else
+ val = neDataPortRead(pDevIns, pThis);
+
+ Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val & 0xff));
+ return val;
+}
+
+
+static int wdIoWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis, uint32_t addr, uint32_t val)
+{
+ int reg = addr & 0xf;
+ int rc = VINF_SUCCESS;
+ union {
+ uint8_t nCTRL1;
+ WD_CTRL1 nCtrl1;
+ };
+ union {
+ uint8_t nCTRL2;
+ WD_CTRL2 nCtrl2;
+ };
+
+ Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val & 0xff));
+
+ switch (reg)
+ {
+ case WDR_CTRL1:
+ nCTRL1 = val;
+ if (nCtrl1.MEME != pThis->ctrl1.MEME)
+ {
+ LogFunc(("CTRL1.MEME=%u\n", nCtrl1.MEME));
+ pThis->ctrl1.MEME = nCtrl1.MEME;
+ }
+ if (nCtrl1.RESET)
+ {
+ dp8390CoreReset(pDevIns, pThis);
+ pThis->CTRL1 = 0;
+ }
+ break;
+ case WDR_CTRL2:
+ /* NYI. */
+ nCTRL2 = val;
+ if (nCTRL2 != pThis->CTRL2)
+ {
+ LogFunc(("CTRL2=%02X, new=%02X\n", pThis->CTRL2, nCTRL2));
+ pThis->CTRL2 = nCTRL2;
+ }
+ break;
+ default:
+ /* Most of the WD registers are read-only. */
+ break;
+ }
+
+ return rc;
+}
+
+
+static uint32_t wdIoRead(PDPNICSTATE pThis, uint32_t addr)
+{
+ uint32_t val = UINT32_MAX;
+ int reg = addr & 0x0f;
+
+ if (reg >= WDR_PROM)
+ {
+ val = pThis->aPROM[reg & 7];
+ }
+ else
+ {
+ if (pThis->uDevType == DEV_WD8013)
+ {
+ switch (reg)
+ {
+ case WDR_CTRL1:
+ val = pThis->CTRL1;
+ break;
+ case WDR_ATDET:
+ val = pThis->uDevType == DEV_WD8013 ? 1 : 0;
+ break;
+ case WDR_IOBASE:
+ val = pThis->aPROM[WDR_IOBASE]; //val = pThis->IOPortBase >> 5;
+ break;
+ case WDR_CTRL2:
+ val = pThis->CTRL2;
+ break;
+ case WDR_JP:
+ val = 0xa0;
+ break;
+ default:
+ val = 0x00; /// @todo What should it be really?
+ break;
+ }
+ }
+ else
+ {
+ /* Old WD adapters (including 8003E) aliased the PROM for
+ * unimplemented control register reads.
+ */
+ switch (reg)
+ {
+ case WDR_CTRL2:
+ val = 1; //pThis->CTRL2;
+ break;
+ case WDR_JP:
+ val = 0xa0;
+ break;
+ default:
+ val = pThis->aPROM[reg & 7];
+ break;
+ }
+ }
+
+ }
+
+ Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val & 0xff));
+ return val;
+}
+
+
+static uint8_t elGetIrqFromIdcfr(uint8_t val)
+{
+ union {
+ uint8_t IDCFR;
+ EL_IDCFR idcfr;
+ };
+ uint8_t irq = 0;
+
+ IDCFR = val;
+
+ /* Lowest set IRQ bit wins (might not match hardware).
+ * NB: It is valid to not enable any IRQ line!
+ */
+ if (idcfr.irq2)
+ irq = 2;
+ else if (idcfr.irq3)
+ irq = 3;
+ else if (idcfr.irq4)
+ irq = 4;
+ else if (idcfr.irq5)
+ irq = 5;
+
+ return irq;
+}
+
+static uint8_t elGetDrqFromIdcfr(uint8_t val)
+{
+ union {
+ uint8_t IDCFR;
+ EL_IDCFR idcfr;
+ };
+ uint8_t drq = 0;
+
+ IDCFR = val;
+
+ /* Lowest set DRQ bit wins; it is valid to not set any. */
+ if (idcfr.drq1)
+ drq = 1;
+ else if (idcfr.drq2)
+ drq = 2;
+ else if (idcfr.drq3)
+ drq = 3;
+
+ return drq;
+}
+
+static void elWriteIdcfr(PPDMDEVINS pDevIns, PDPNICSTATE pThis, PEL_GA pGa, uint8_t val)
+{
+ uint8_t uOldIrq = pThis->uIsaIrq;
+ uint8_t uNewIrq;
+ uint8_t uOldDrq = pThis->uElIsaDma;
+ uint8_t uNewDrq;
+
+ /* If the IRQ is currently active, have to switch it. */
+ uNewIrq = elGetIrqFromIdcfr(val);
+ if (uOldIrq != uNewIrq)
+ {
+ LogFunc(("#%d Switching IRQ=%d -> IRQ=%d\n", pThis->iInstance, uOldIrq, uNewIrq));
+ if (pThis->fNicIrqActive)
+ {
+ /* This probably isn't supposed to happen. */
+ LogFunc(("#%d Moving active IRQ!\n", pThis->iInstance));
+ if (uOldIrq)
+ PDMDevHlpISASetIrq(pDevIns, uOldIrq, 0);
+ if (uNewIrq)
+ PDMDevHlpISASetIrq(pDevIns, uNewIrq, 1);
+ }
+ pThis->uIsaIrq = uNewIrq;
+ }
+
+ /* And now the same dance for DMA. */
+ uNewDrq = elGetDrqFromIdcfr(val);
+ if (uOldDrq != uNewDrq)
+ {
+ /// @todo We can't really move the DRQ, what can we do?
+ LogFunc(("#%d Switching DRQ=%d -> DRQ=%d\n", pThis->iInstance, uOldDrq, uNewDrq));
+ pThis->uElIsaDma = uNewDrq;
+ }
+
+ pGa->IDCFR = val;
+}
+
+
+static void elWriteGacfr(PPDMDEVINS pDevIns, PDPNICSTATE pThis, PEL_GA pGa, uint8_t val)
+{
+ union {
+ uint8_t nGACFR;
+ GA_GACFR nGacfr;
+ };
+
+ nGACFR = val;
+
+ if (nGacfr.nim != pGa->gacfr.nim)
+ {
+ /// @todo Should we just run UpdateInterrupts?
+ if (pThis->fNicIrqActive && !nGacfr.nim)
+ {
+ LogFunc(("#%d: Unmasking active IRQ!\n", pThis->iInstance));
+ PDMDevHlpISASetIrq(pDevIns, pThis->uIsaIrq, 1);
+ }
+ else if (pThis->fNicIrqActive && nGacfr.nim)
+ {
+ LogFunc(("#%d: Masking active IRQ\n", pThis->iInstance));
+ PDMDevHlpISASetIrq(pDevIns, pThis->uIsaIrq, 0);
+ }
+ }
+
+ /// @todo rsel/mbs bit change?
+ if (nGacfr.rsel != pGa->gacfr.rsel)
+ {
+ LogFunc(("#%d: rsel=%u mbs=%u\n", pThis->iInstance, nGacfr.rsel, nGacfr.mbs));
+ }
+
+ pGa->GACFR = nGACFR;
+}
+
+
+static void elSoftReset(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
+{
+ PEL_GA pGa = &pThis->ga;
+
+ LogFlow(("Resetting ASIC GA\n"));
+ /* Most GA registers are zeroed. */
+ pGa->PSTR = pGa->PSPR = 0;
+ pGa->DQTR = 0;
+ elWriteGacfr(pDevIns, pThis, pGa, 0);
+ pGa->STREG = ELNKII_GA_REV;
+ pGa->VPTR0 = pGa->VPTR1 = pGa->VPTR2 = 0;
+ pGa->DALSB = pGa->DAMSB = 0;
+ elWriteIdcfr(pDevIns, pThis, pGa, 0);
+ pGa->GACR = 0x0B; /* Low bit set = in reset state. */
+ pGa->fGaIrq = false;
+
+ /* Reset the NIC core. */
+ dp8390CoreReset(pDevIns, pThis);
+}
+
+
+static int elWriteGacr(PPDMDEVINS pDevIns, PDPNICSTATE pThis, PEL_GA pGa, uint8_t val)
+{
+ union {
+ uint8_t nGACR;
+ GA_GACR nGacr;
+ };
+
+ nGACR = val;
+
+ if (nGacr.rst != pGa->gacr.rst)
+ {
+ /* When going out of reset, only clear the rst bit. 3C503 diagnostics checks for this. */
+ if (nGacr.rst)
+ elSoftReset(pDevIns, pThis);
+ else
+ pGa->gacr.rst = 0;
+ }
+ else
+ {
+#ifdef IN_RING0
+ /* Force a trip to R3. */
+ if (pThis->uElIsaDma == pThis->uIsaDma)
+ return VINF_IOM_R3_IOPORT_WRITE;
+#endif
+
+ /* Make the data registers "ready" as long as transfers are started. */
+ if (nGacr.start)
+ {
+ pGa->cdadr.cdadr_lsb = pGa->DALSB;
+ pGa->cdadr.cdadr_msb = pGa->DAMSB;
+ LogFunc(("DMA started, ddir=%u, cdadr=%04X\n", pGa->gacr.ddir, pGa->CDADR));
+ pGa->streg.dprdy = 1;
+ pGa->streg.dip = 1;
+ pGa->streg.dtc = 0;
+ }
+ else
+ {
+ pGa->streg.dprdy = 0;
+ pGa->streg.dip = 0;
+ }
+
+ /* Only do anything if the software configured DMA channel matches the emulation config. */
+ if (pThis->uElIsaDma == pThis->uIsaDma)
+ {
+#ifdef IN_RING3
+ PDMDevHlpDMASetDREQ(pDevIns, pThis->uIsaDma, pGa->streg.dprdy);
+ if (pGa->streg.dprdy)
+ PDMDevHlpDMASchedule(pDevIns);
+ LogFunc(("#%d: DREQ for channel %u set to %u\n", pThis->iInstance, pThis->uIsaDma, pGa->streg.dprdy));
+#else
+ /* Must not get here. */
+ Assert(0);
+#endif
+ }
+
+ pGa->GACR = nGACR;
+ LogFunc(("GACR=%02X ealo=%u eahi=%u\n", pGa->GACR, pGa->gacr.ealo, pGa->gacr.eahi));
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+static int elGaDataWrite(PDPNICSTATE pThis, PEL_GA pGa, uint16_t val)
+{
+ /* Data write; ignored if not started and in "download" mode. */
+ if (pGa->gacr.start && pGa->gacr.ddir)
+ {
+ uint16_t addr = pGa->CDADR;
+
+ addr &= 0x3fff;
+ if (addr >= 0x2000)
+ {
+ /* Local RAM is mapped at 2000h-3FFFh. */
+ addr -= 0x2000;
+ pThis->abLocalRAM[addr] = val;
+ }
+
+ pGa->CDADR++;
+ /// @todo Does this really apply to writes or only reads?
+ if ((pGa->cdadr.cdadr_msb == pGa->PSPR) && (pGa->PSPR != pGa->PSTR))
+ {
+ LogFunc(("GA DMA wrap / write!! (cdadr=%04X PSPR=%02X00 PSTR=%02X00)\n", pGa->CDADR, pGa->PSPR, pGa->PSTR));
+ pGa->cdadr.cdadr_msb = pGa->PSTR;
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+static uint8_t elGaDataRead(PDPNICSTATE pThis, PEL_GA pGa)
+{
+ uint8_t val = 0xcd;
+
+ /* Data read; ignored if not started and in "upload" mode. */
+ if (pGa->gacr.start && !pGa->gacr.ddir)
+ {
+ uint16_t addr = pGa->CDADR;
+
+ addr &= 0x3fff;
+ if (addr >= 0x2000)
+ {
+ /* Local RAM is mapped at 2000h-3FFFh. */
+ addr -= 0x2000;
+ val = pThis->abLocalRAM[addr];
+ }
+
+ pGa->CDADR++;
+ if ((pGa->cdadr.cdadr_msb == pGa->PSPR) && (pGa->PSPR != pGa->PSTR))
+ {
+ LogFunc(("GA DMA wrap / read!! (cdadr=%04X PSPR=%02X00 PSTR=%02X00)\n", pGa->CDADR, pGa->PSPR, pGa->PSTR));
+ pGa->cdadr.cdadr_msb = pGa->PSTR;
+ }
+ }
+ return val;
+}
+
+
+static int elGaIoWrite(PPDMDEVINS pDevIns, PDPNICSTATE pThis, uint32_t addr, uint32_t val)
+{
+ int reg = addr & 0xf;
+ int rc = VINF_SUCCESS;
+ PEL_GA pGa = &pThis->ga;
+
+ Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val & 0xff));
+
+ switch (reg)
+ {
+ case GAR_PSTR:
+ pGa->PSTR = val;
+ break;
+ case GAR_PSPR:
+ pGa->PSPR = val;
+ break;
+ case GAR_DQTR:
+ pGa->DQTR = val;
+ break;
+ case GAR_GACFR:
+ elWriteGacfr(pDevIns, pThis, pGa, val);
+ break;
+ case GAR_GACR:
+ rc = elWriteGacr(pDevIns, pThis, pGa, val);
+ break;
+ case GAR_STREG:
+ /* Writing anything to STREG clears ASIC interrupt. */
+ pThis->ga.streg.dtc = 0;
+ pThis->ga.fGaIrq = false;
+ dp8390CoreUpdateIrq(pDevIns, pThis);
+ break;
+ case GAR_IDCFR:
+ elWriteIdcfr(pDevIns, pThis, pGa, val);
+ break;
+ case GAR_DAMSB:
+ pGa->DAMSB = val;
+ break;
+ case GAR_DALSB:
+ pGa->DALSB = val;
+ break;
+ case GAR_VPTR2:
+ pGa->VPTR2 = val;
+ break;
+ case GAR_VPTR1:
+ pGa->VPTR1 = val;
+ break;
+ case GAR_VPTR0:
+ pGa->VPTR0 = val;
+ break;
+ case GAR_RFMSB:
+ case GAR_RFLSB:
+ elGaDataWrite(pThis, pGa, val);
+ break;
+ case GAR_R_BCFR:
+ case GAR_R_PCFR:
+ /* Read-only registers, ignored. */
+ break;
+ default:
+ Assert(0);
+ break;
+ }
+
+ return rc;
+}
+
+
+static uint32_t elGaIoRead(PDPNICSTATE pThis, uint32_t addr)
+{
+ uint32_t val = UINT32_MAX;
+ int reg = addr & 0x0f;
+ PEL_GA pGa = &pThis->ga;
+
+ switch (reg)
+ {
+ case GAR_PSTR:
+ val = pGa->PSTR;
+ break;
+ case GAR_PSPR:
+ val = pGa->PSPR;
+ break;
+ case GAR_DQTR:
+ val = pGa->DQTR;
+ break;
+ case GAR_R_BCFR:
+ val = pGa->BCFR;
+ break;
+ case GAR_R_PCFR:
+ val = pGa->PCFR;
+ break;
+ case GAR_GACFR:
+ val = pGa->GACFR;
+ break;
+ case GAR_GACR:
+ val = pGa->GACR;
+ break;
+ case GAR_STREG:
+ val = pGa->STREG;
+ break;
+ case GAR_IDCFR:
+ val = pGa->IDCFR;
+ break;
+ case GAR_DAMSB:
+ val = pGa->DAMSB;
+ break;
+ case GAR_DALSB:
+ val = pGa->DALSB;
+ break;
+ case GAR_VPTR2:
+ val = pGa->VPTR2;
+ break;
+ case GAR_VPTR1:
+ val = pGa->VPTR1;
+ break;
+ case GAR_VPTR0:
+ val = pGa->VPTR0;
+ break;
+ case GAR_RFMSB:
+ case GAR_RFLSB:
+ val = elGaDataRead(pThis, pGa);
+ break;
+ default:
+ Assert(0);
+ break;
+ }
+
+ Log2Func(("#%d: addr=%#06x val=%#04x\n", pThis->iInstance, addr, val & 0xff));
+ return val;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTIN}
+ */
+static DECLCALLBACK(VBOXSTRICTRC)
+neIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ int rc = VINF_SUCCESS;
+ int reg = Port & 0xf;
+ uint8_t u8Lo, u8Hi = 0;
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ RT_NOREF_PV(pvUser);
+
+ switch (cb)
+ {
+ case 1:
+ *pu32 = neIoRead(pDevIns, pThis, reg);
+ break;
+ case 2:
+ /* Manually split word access if necessary if it's an NE1000. Perhaps overkill. */
+ if (pThis->uDevType == DEV_NE1000)
+ {
+ u8Lo = neIoRead(pDevIns, pThis, reg);
+ if (reg < 0xf) // This logic is not entirely accurate (wraparound).
+ u8Hi = neIoRead(pDevIns, pThis, reg + 1);
+ *pu32 = RT_MAKE_U16(u8Lo, u8Hi);
+ }
+ else
+ *pu32 = neIoRead(pDevIns, pThis, reg);
+ break;
+ default:
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
+ "neIOPortRead: unsupported operation size: offset=%#10x cb=%u\n",
+ Port, cb);
+ }
+
+ Log2Func(("#%d: NE Port=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, *pu32, cb, rc));
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTIN}
+ */
+static DECLCALLBACK(VBOXSTRICTRC)
+wdIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ int rc = VINF_SUCCESS;
+ int reg = Port & 0xf;
+ uint8_t u8Lo, u8Hi = 0;
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ RT_NOREF_PV(pvUser);
+
+ switch (cb)
+ {
+ case 1:
+ *pu32 = wdIoRead(pThis, reg);
+ break;
+ case 2:
+ /* Manually split word access. */
+ u8Lo = wdIoRead(pThis, reg);
+ if (reg < 0xf) // This logic is not entirely accurate (wraparound).
+ u8Hi = wdIoRead(pThis, reg + 1);
+ *pu32 = RT_MAKE_U16(u8Lo, u8Hi);
+ break;
+ default:
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
+ "wdIOPortRead: unsupported operation size: offset=%#10x cb=%u\n",
+ Port, cb);
+ }
+
+ Log2Func(("#%d: WD Port=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, *pu32, cb, rc));
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTIN}
+ */
+static DECLCALLBACK(VBOXSTRICTRC)
+elIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ int rc = VINF_SUCCESS;
+ int reg = Port & 0xf;
+ uint8_t u8Lo, u8Hi = 0;
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ RT_NOREF_PV(pvUser);
+
+ switch (cb)
+ {
+ case 1:
+ *pu32 = elGaIoRead(pThis, reg);
+ break;
+ case 2:
+ /* Manually split word access. */
+ u8Lo = elGaIoRead(pThis, reg);
+ if (reg < 0xf) // This logic is not entirely accurate (wraparound).
+ u8Hi = elGaIoRead(pThis, reg + 1);
+ *pu32 = RT_MAKE_U16(u8Lo, u8Hi);
+ break;
+ default:
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
+ "elIOPortRead: unsupported operation size: offset=%#10x cb=%u\n",
+ Port, cb);
+ }
+
+ Log2Func(("#%d: EL Port=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, *pu32, cb, rc));
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTIN}
+ */
+static DECLCALLBACK(VBOXSTRICTRC)
+dp8390CoreIOPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t *pu32, unsigned cb)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ int rc = VINF_SUCCESS;
+ int reg = Port & 0xf;
+ uint8_t u8Lo, u8Hi;
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ RT_NOREF_PV(pvUser);
+
+ switch (cb)
+ {
+ case 1:
+ *pu32 = dp8390CoreRead(pDevIns, pThis, reg);
+ break;
+ case 2:
+ /* Manually split word access. */
+ u8Lo = dp8390CoreRead(pDevIns, pThis, reg + 0);
+ /* This logic is not entirely accurate. */
+ if (reg < 0xf)
+ u8Hi = dp8390CoreRead(pDevIns, pThis, reg + 1);
+ else
+ u8Hi = 0;
+ *pu32 = RT_MAKE_U16(u8Lo, u8Hi);
+ break;
+ default:
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
+ "dp8390CoreIOPortRead: unsupported operation size: offset=%#10x cb=%u\n",
+ Port, cb);
+ }
+
+ Log2Func(("#%d: Port=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, *pu32, cb, rc));
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT}
+ */
+static DECLCALLBACK(VBOXSTRICTRC)
+neIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ int rc = VINF_SUCCESS;
+ int reg = Port & 0xf;
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIOWrite), a);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ RT_NOREF_PV(pvUser);
+
+ switch (cb)
+ {
+ case 1:
+ rc = dpNeIoWrite(pDevIns, pThis, Port, RT_LOBYTE(u32));
+ break;
+ case 2:
+ /* Manually split word access if necessary. */
+ if (pThis->uDevType == DEV_NE2000)
+ {
+ rc = dpNeIoWrite(pDevIns, pThis, Port, RT_LOWORD(u32));
+ }
+ else
+ {
+ rc = dpNeIoWrite(pDevIns, pThis, reg + 0, RT_LOBYTE(u32));
+ if (RT_SUCCESS(rc) && (reg < 0xf))
+ rc = dpNeIoWrite(pDevIns, pThis, reg + 1, RT_HIBYTE(u32));
+ }
+ break;
+ default:
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
+ "neIOPortWrite: unsupported operation size: offset=%#10x cb=%u\n",
+ Port, cb);
+ }
+
+ Log2Func(("#%d: NE Port=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, u32, cb, rc));
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIOWrite), a);
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT}
+ */
+static DECLCALLBACK(VBOXSTRICTRC)
+wdIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ int rc = VINF_SUCCESS;
+ int reg = Port & 0xf;
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIOWrite), a);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ RT_NOREF_PV(pvUser);
+
+ switch (cb)
+ {
+ case 1:
+ rc = wdIoWrite(pDevIns, pThis, Port, RT_LOBYTE(u32));
+ break;
+ case 2:
+ /* Manually split word access. */
+ rc = wdIoWrite(pDevIns, pThis, reg + 0, RT_LOBYTE(u32));
+ if (RT_SUCCESS(rc) && (reg < 0xf))
+ rc = wdIoWrite(pDevIns, pThis, reg + 1, RT_HIBYTE(u32));
+ break;
+ default:
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
+ "wdIOPortWrite: unsupported operation size: offset=%#10x cb=%u\n",
+ Port, cb);
+ }
+
+ Log2Func(("#%d: WD Port=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, u32, cb, rc));
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIOWrite), a);
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT}
+ */
+static DECLCALLBACK(VBOXSTRICTRC)
+elIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ int rc = VINF_SUCCESS;
+ int reg = Port & 0xf;
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIOWrite), a);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ RT_NOREF_PV(pvUser);
+
+ switch (cb)
+ {
+ case 1:
+ rc = elGaIoWrite(pDevIns, pThis, Port, RT_LOBYTE(u32));
+ break;
+ case 2:
+ /* Manually split word access. */
+ rc = elGaIoWrite(pDevIns, pThis, reg + 0, RT_LOBYTE(u32));
+ if (RT_SUCCESS(rc) && (reg < 0xf))
+ rc = elGaIoWrite(pDevIns, pThis, reg + 1, RT_HIBYTE(u32));
+ break;
+ default:
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
+ "elIOPortWrite: unsupported operation size: offset=%#10x cb=%u\n",
+ Port, cb);
+ }
+
+ Log2Func(("#%d: EL Port=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, u32, cb, rc));
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIOWrite), a);
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT}
+ */
+static DECLCALLBACK(VBOXSTRICTRC)
+dp8390CoreIOPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT Port, uint32_t u32, unsigned cb)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ int rc = VINF_SUCCESS;
+ int reg = Port & 0xf;
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIOWrite), a);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ RT_NOREF_PV(pvUser);
+
+ switch (cb)
+ {
+ case 1:
+ rc = dp8390CoreWrite(pDevIns, pThis, reg, RT_LOBYTE(u32));
+ break;
+ case 2:
+ /* Manually split word access. */
+ rc = dp8390CoreWrite(pDevIns, pThis, reg + 0, RT_LOBYTE(u32));
+ if (!RT_SUCCESS(rc))
+ break;
+ rc = dp8390CoreWrite(pDevIns, pThis, reg + 1, RT_HIBYTE(u32));
+ break;
+ default:
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
+ "dp8390CoreIOPortWrite: unsupported operation size: offset=%#10x cb=%u\n",
+ Port, cb);
+ }
+
+ Log2Func(("#%d: Port=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n", pThis->iInstance, Port, u32, cb, rc));
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIOWrite), a);
+ return rc;
+}
+
+
+#if 0
+/**
+ * @callback_method_impl{FNIOMMMIONEWFILL,
+ * Local RAM write hook\, to be called from IOM. This is the advanced version of
+ * wdMemWrite function.}
+ */
+static DECLCALLBACK(VBOXSTRICTRC)
+dpWdMmioFill(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, uint32_t u32Item, unsigned cbItem, unsigned cItems)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
+
+ !!
+ return VINF_SUCCESS
+}
+#endif
+
+
+/**
+ * @callback_method_impl{FNIOMMMIONEWREAD,
+ * Local RAM read hook\, to be called from IOM.}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) wdMemRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ uint8_t *pbData = (uint8_t *)pv;
+ NOREF(pvUser);
+
+// STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
+
+ if (pThis->ctrl1.MEME)
+ {
+ Log3Func(("#%d: Reading %u bytes from address %X: [%.*Rhxs]\n", pThis->iInstance, cb, off, cb, &pThis->abLocalRAM[off & DPNIC_MEM_MASK]));
+ while (cb-- > 0)
+ *pbData++ = pThis->abLocalRAM[off++ & DPNIC_MEM_MASK];
+ }
+ else
+ memset(pv, 0xff, cb);
+
+// STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryRead), a);
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNIOMMMIONEWWRITE,
+ * Local RAM write hook\, to be called from IOM.}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) wdMemWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ uint8_t const *pbSrc = (uint8_t const *)pv;
+ NOREF(pvUser);
+
+// STAM_PROFILE_START(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
+
+ if (pThis->ctrl1.MEME)
+ {
+ Log3Func(("#%d: Writing %u bytes to address %X: [%.*Rhxs]\n", pThis->iInstance, cb, off, cb, pbSrc));
+ while (cb-- > 0)
+ pThis->abLocalRAM[off++ & DPNIC_MEM_MASK] = *pbSrc++;
+ }
+
+// STAM_PROFILE_STOP(&pThis->CTX_MID_Z(Stat,MemoryWrite), a);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMMMIONEWREAD,
+ * Local RAM read hook\, to be called from IOM.}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) elMemRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ uint8_t *pbData = (uint8_t *)pv;
+ NOREF(pvUser);
+
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
+
+ if (pThis->ga.gacfr.rsel)
+ {
+ Log3Func(("#%d: Reading %u bytes from address %X\n", pThis->iInstance, cb, off));
+ while (cb-- > 0)
+ *pbData++ = pThis->abLocalRAM[off++ & DPNIC_MEM_MASK];
+ }
+ else
+ {
+ Log3Func(("#%d: Ignoring read of %u bytes from address %X\n", pThis->iInstance, cb, off));
+ memset(pv, 0xff, cb);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMMMIONEWWRITE,
+ * Local RAM write hook\, to be called from IOM.}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) elMemWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ uint8_t const *pbSrc = (uint8_t const *)pv;
+ NOREF(pvUser);
+
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, pDevIns->CTX_SUFF(pCritSectRo)));
+
+ if (pThis->ga.gacfr.rsel)
+ {
+ Log3Func(("#%d: Writing %u bytes to address %X\n", pThis->iInstance, cb, off));
+ while (cb-- > 0)
+ pThis->abLocalRAM[off++ & DPNIC_MEM_MASK] = *pbSrc++;
+ }
+ else
+ {
+ Log3Func(("#%d: Ignoring write of %u bytes to address %X\n", pThis->iInstance, cb, off));
+ }
+ return VINF_SUCCESS;
+}
+
+
+#ifdef IN_RING3
+
+/* Shamelessly stolen from DevDMA.cpp */
+
+/* Test the decrement bit of mode register. */
+#define IS_MODE_DEC(c) ((c) & 0x20)
+/* Test the auto-init bit of mode register. */
+#define IS_MODE_AI(c) ((c) & 0x10)
+/* Extract the transfer type bits of mode register. */
+#define GET_MODE_XTYP(c) (((c) & 0x0c) >> 2)
+
+/* DMA transfer modes. */
+enum {
+ DMODE_DEMAND, /* Demand transfer mode. */
+ DMODE_SINGLE, /* Single transfer mode. */
+ DMODE_BLOCK, /* Block transfer mode. */
+ DMODE_CASCADE /* Cascade mode. */
+};
+
+/* DMA transfer types. */
+enum {
+ DTYPE_VERIFY, /* Verify transfer type. */
+ DTYPE_WRITE, /* Write transfer type. */
+ DTYPE_READ, /* Read transfer type. */
+ DTYPE_ILLEGAL /* Undefined. */
+};
+
+static DECLCALLBACK(uint32_t) elnk3R3DMAXferHandler(PPDMDEVINS pDevIns, void *opaque,
+ unsigned nchan, uint32_t dma_pos, uint32_t dma_len)
+{
+ PDPNICSTATE pThis = (PDPNICSTATE)opaque;
+ int dma_mode;
+ int dma_type;
+ uint16_t cbToXfer;
+ uint32_t cbXferred = 0;
+ uint16_t uDmaAddr;
+ int rc;
+
+ /*
+ * The 3C503 EtherLink II uses DMA as an alternative to shared RAM
+ * or PIO. The Gate Array tracks its own current DMA address within
+ * the adapter's local address space.
+ */
+ dma_mode = PDMDevHlpDMAGetChannelMode(pDevIns, pThis->uIsaDma);
+ dma_type = GET_MODE_XTYP(dma_mode);
+ uDmaAddr = pThis->ga.CDADR;
+ cbToXfer = dma_len;
+ LogFlowFunc(("dma_mode=%d, dma_type=%d, dma_pos=%u, dma_len=%u, cdadr=%04X\n", dma_mode, dma_type, dma_pos, dma_len, uDmaAddr));
+
+ /* Skip any accesses below local memory start. */
+ if ((0x2000 > 0) && (uDmaAddr < 0x2000)) /// @todo Should keep track in variables
+ {
+ uint16_t cbToSkip = 0x2000 - uDmaAddr;
+
+ uDmaAddr += cbToSkip;
+ /// @todo Should this write junk to host memory when reading from device?
+ if (cbToSkip < cbToXfer)
+ {
+ cbToXfer -= cbToSkip;
+ Assert(uDmaAddr == 0x2000);
+ LogFunc(("DMA skipping %u bytes!\n", cbToSkip));
+ }
+ else
+ {
+ cbToXfer = 0; /* Transfer entirely below valid address range. */
+ LogFunc(("DMA below valid address range!\n"));
+ }
+ }
+
+ if (cbToXfer)
+ {
+ uint16_t cbToSkip = 0;
+
+ /* Clip transfer size so it falls within local RAM. */
+ if ((uDmaAddr - 0x2000 + cbToXfer) > (int)sizeof(pThis->abLocalRAM))
+ {
+ /* Calculate how much to skip anything at the end. */
+ cbToSkip = sizeof(pThis->abLocalRAM) - (0x2000 - uDmaAddr + cbToXfer);
+ LogFunc(("DMA above valid address range uDmaAddr=%04X cbToXfer=%u cbToSkip=%u!\n", uDmaAddr, cbToXfer, cbToSkip));
+ cbToXfer -= cbToSkip;
+ }
+
+ if (dma_type == DTYPE_WRITE)
+ {
+ /* Write transfer type. Reading from device, writing to memory. */
+ if (!pThis->ga.gacr.ddir)
+ {
+ Log2Func(("DMAWriteMemory uDmaAddr=%04X cbToXfer=%u\n", uDmaAddr, cbToXfer));
+ rc = PDMDevHlpDMAWriteMemory(pDevIns, nchan,
+ &pThis->abLocalRAM[uDmaAddr - 0x2000],
+ dma_pos, cbToXfer, &cbXferred);
+ AssertMsgRC(rc, ("DMAWriteMemory -> %Rrc\n", rc));
+ }
+ else
+ {
+ // Do nothing, direction does not match.
+ /// @todo Bug in DevDMA?
+ LogFunc(("DTYPE_WRITE but GACR.ddir set, do nothing!\n"));
+ }
+ }
+ else
+ {
+ /* Read of Verify transfer type. Reading from memory, writing to device. */
+ if (pThis->ga.gacr.ddir)
+ {
+ Log2Func(("DMAReadMemory uDmaAddr=%04X cbToXfer=%u\n", uDmaAddr, cbToXfer));
+ rc = PDMDevHlpDMAReadMemory(pDevIns, nchan,
+ &pThis->abLocalRAM[uDmaAddr - 0x2000],
+ dma_pos, cbToXfer, &cbXferred);
+ AssertMsgRC(rc, ("DMAReadMemory -> %Rrc\n", rc));
+ }
+ else
+ {
+ // Do nothing, direction does not match.
+ /// @todo Bug in DevDMA?
+ LogFunc(("DTYPE_READ but GACR.ddir clear, do nothing!\n"));
+ }
+ }
+
+ /* NB: This might wrap. In theory it might wrap back to valid
+ * memory but... just no.
+ */
+ /// @todo Actually... what would really happen?
+ uDmaAddr += cbToXfer + cbToSkip;
+ }
+ Log2Func(("After DMA transfer: uDmaAddr=%04X, cbXferred=%u\n", uDmaAddr, cbXferred));
+
+ /* Advance the DMA address and see if transfer completed (it almost certainly did). */
+ if (1)
+ {
+ Log2Func(("DMA completed\n"));
+ PDMDevHlpDMASetDREQ(pDevIns, pThis->uIsaDma, 0);
+ pThis->ga.streg.dtc = 1;
+ pThis->ga.fGaIrq = true;
+ dp8390CoreUpdateIrq(pDevIns, pThis);
+ }
+ else
+ {
+ LogFunc(("DMA continuing: uDmaAddr=%04X, cbXferred=%u\n", uDmaAddr, cbXferred));
+ PDMDevHlpDMASchedule(pDevIns);
+ }
+
+ /* Returns the updated transfer count. */
+ return dma_pos + dma_len;
+}
+
+
+/* -=-=-=-=-=- Timer Callbacks -=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNTMTIMERDEV, Restore timer callback}
+ *
+ * This is only called when we restore a saved state and temporarily
+ * disconnected the network link to inform the guest that network connections
+ * should be considered lost.
+ */
+static DECLCALLBACK(void) dpNicR3TimerRestore(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
+{
+ RT_NOREF(pvUser);
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ AssertReleaseRC(rc);
+
+ rc = VERR_GENERAL_FAILURE;
+
+ /* The DP8390 based cards have no concept of link state. Reporting collisions on all transmits
+ * is the best approximation of a disconnected cable that we can do. Some drivers (3C503) warn
+ * of possible disconnected cable, some don't. Many cards with DP8390 chips had permanently
+ * attached cables (AUI or BNC) and their drivers do not expect cables to be disconnected and
+ * re-connected at runtime. Guests which are waiting for a receive have no way to notice any
+ * problem, therefore we only postpone restoring a link a couple of times, and then reconnect
+ * regardless of whether the guest noticed anything or not.
+ */
+ if ( (pThis->cLinkDownReported <= DPNIC_MAX_LINKDOWN_REPORTED)
+ && (pThis->cLinkRestorePostponed <= DPNIC_MAX_LINKRST_POSTPONED))
+ rc = PDMDevHlpTimerSetMillies(pDevIns, hTimer, 1500);
+ if (RT_FAILURE(rc))
+ {
+ pThis->fLinkTempDown = false;
+ if (pThis->fLinkUp)
+ {
+ LogRel(("DPNIC#%d: The link is back up again after the restore.\n",
+ pThis->iInstance));
+ LogFunc(("#%d: cLinkDownReported=%d\n", pThis->iInstance, pThis->cLinkDownReported));
+ pThis->Led.Actual.s.fError = 0;
+ }
+ }
+ else
+ {
+ LogFunc(("#%d: cLinkDownReported=%d, cLinkRestorePostponed=%d, wait another 1500ms...\n",
+ pThis->iInstance, pThis->cLinkDownReported, pThis->cLinkRestorePostponed));
+ pThis->cLinkRestorePostponed++;
+ }
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+}
+
+
+/* -=-=-=-=-=- Debug Info Handler -=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV}
+ */
+static DECLCALLBACK(void) dpNicR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ bool fRecvBuffer = false;
+ bool fSendBuffer = false;
+ unsigned uFreePages;
+ DP8390CORE *pCore = &pThis->core;
+ const char *aszModels[] = {"NE1000", "NE2000", "WD8003E", "WD8013E", "3C503"};
+
+ /*
+ * Parse args.
+ */
+ if (pszArgs)
+ {
+ fRecvBuffer = strstr(pszArgs, "verbose") || strstr(pszArgs, "recvbuf");
+ fSendBuffer = strstr(pszArgs, "verbose") || strstr(pszArgs, "sendbuf");
+ }
+
+ /*
+ * Show device information.
+ */
+ pHlp->pfnPrintf(pHlp, "DPNIC #%d: %s port=%RTiop IRQ=%u",
+ pThis->iInstance,
+ aszModels[pThis->uDevType],
+ pThis->IOPortBase,
+ pThis->uIsaIrq);
+ if (pThis->MemBase)
+ pHlp->pfnPrintf(pHlp, " mem=%05X-%05X", pThis->MemBase, pThis->MemBase + pThis->cbMemSize - 1);
+ if (pThis->uIsaDma)
+ pHlp->pfnPrintf(pHlp, " DMA=%u", pThis->uIsaDma);
+ pHlp->pfnPrintf(pHlp, " mac-cfg=%RTmac%s %s\n",
+ &pThis->MacConfigured,
+ pDevIns->fR0Enabled ? " RZ" : "",
+ pThis->fDriverAttached ? "attached" : "unattached!");
+
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_INTERNAL_ERROR); /* Take it here so we know why we're hanging... */
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
+
+ pHlp->pfnPrintf(pHlp, "\nDP3890 NIC Core\n");
+ pHlp->pfnPrintf(pHlp, " CR=%02X: %s%s%s RD=%d PS=%d\n", pCore->CR,
+ pCore->cr.STP ? "STP " : "",
+ pCore->cr.STA ? "STA " : "",
+ pCore->cr.TXP ? "TXP " : "",
+ pCore->cr.RD, pCore->cr.PS);
+ pHlp->pfnPrintf(pHlp, " ISR=%02X: %s%s%s%s%s%s%s%s\n", pCore->ISR,
+ pCore->isr.PRX ? "PRX " : "",
+ pCore->isr.PTX ? "PTX " : "",
+ pCore->isr.RXE ? "RXE " : "",
+ pCore->isr.TXE ? "TXE " : "",
+ pCore->isr.OVW ? "OVW " : "",
+ pCore->isr.CNT ? "CNT " : "",
+ pCore->isr.RDC ? "RDC " : "",
+ pCore->isr.RST ? "RST " : "");
+ pHlp->pfnPrintf(pHlp, " IMR=%02X: %s%s%s%s%s%s%s%s\n", pCore->IMR,
+ pCore->imr.PRXE ? "PRXE " : "",
+ pCore->imr.PTXE ? "PTXE " : "",
+ pCore->imr.RXEE ? "RXEE " : "",
+ pCore->imr.TXEE ? "TXEE " : "",
+ pCore->imr.OVWE ? "OVWE " : "",
+ pCore->imr.CNTE ? "CNTE " : "",
+ pCore->imr.RDCE ? "RDCE " : "",
+ pCore->imr.res ? "Reserved bit set!!" : "");
+ pHlp->pfnPrintf(pHlp, " DCR=%02X: %s%s%s%s%sFT=%d %s\n", pCore->DCR,
+ pCore->dcr.WTS ? "WTS " : "",
+ pCore->dcr.BOS ? "BOS " : "",
+ pCore->dcr.LAS ? "LAS " : "",
+ pCore->dcr.LS ? "LS " : "",
+ pCore->dcr.ARM ? "ARM " : "",
+ pCore->dcr.FT,
+ pCore->dcr.res ? "Reserved bit set!!" : "");
+ pHlp->pfnPrintf(pHlp, " TCR=%02X: %sLB=%d %s%s\n", pCore->TCR,
+ pCore->tcr.CRC ? "CRC " : "",
+ pCore->tcr.LB,
+ pCore->tcr.ATD ? "ATD " : "",
+ pCore->tcr.OFST ? "OFST" : "");
+ pHlp->pfnPrintf(pHlp, " TSR=%02X: %s%s%s%s%s%s%s%s\n", pCore->TSR,
+ pCore->tsr.PTX ? "PTX " : "",
+ pCore->tsr.DFR ? "DFR " : "",
+ pCore->tsr.COL ? "COL " : "",
+ pCore->tsr.ABT ? "ABT " : "",
+ pCore->tsr.CRS ? "CRS " : "",
+ pCore->tsr.FU ? "FU " : "",
+ pCore->tsr.CDH ? "CDH " : "",
+ pCore->tsr.OWC ? "OWC " : "");
+ pHlp->pfnPrintf(pHlp, " RCR=%02X: %s%s%s%s%s%s\n", pCore->RCR,
+ pCore->rcr.SEP ? "SEP " : "",
+ pCore->rcr.AR ? "AR " : "",
+ pCore->rcr.AB ? "AB " : "",
+ pCore->rcr.AM ? "AM " : "",
+ pCore->rcr.PRO ? "PRO " : "",
+ pCore->rcr.MON ? "MON " : "");
+ pHlp->pfnPrintf(pHlp, " RSR=%02X: %s%s%s%s%s%s%s%s\n", pCore->RSR,
+ pCore->rsr.PRX ? "PRX " : "",
+ pCore->rsr.CRC ? "CRC " : "",
+ pCore->rsr.FAE ? "FAE " : "",
+ pCore->rsr.FO ? "FO " : "",
+ pCore->rsr.MPA ? "MPA " : "",
+ pCore->rsr.PHY ? "PHY " : "",
+ pCore->rsr.DIS ? "DIS " : "",
+ pCore->rsr.DFR ? "DFR " : "");
+ pHlp->pfnPrintf(pHlp, " ActIntSrc: %02X\n", pCore->ISR & pCore->IMR);
+ pHlp->pfnPrintf(pHlp, " Receiving: %s%s%s%s%s%s\n",
+ pCore->rcr.AB ? "Broadcast " : "",
+ pCore->rcr.AM ? "Multicast " : "",
+ pCore->rcr.PRO ? "Promiscuous " : "",
+ pCore->rcr.MON ? "Monitor " : "",
+ pCore->cr.STA ? "Started " : "Not started ",
+ pCore->isr.RST ? "Reset!" : "");
+
+ /* Dump the currently programmed station address. */
+ pHlp->pfnPrintf(pHlp, " MAC Addr : %RTmac\n", &pCore->pg1.PAR);
+
+ /* Dump the currently programmed multicast filter. */
+ pHlp->pfnPrintf(pHlp, " Multicast: %02X:%02X:%02X:%02X %02X:%02X:%02X:%02X\n",
+ pCore->pg1.MAR[0], pCore->pg1.MAR[1], pCore->pg1.MAR[2], pCore->pg1.MAR[3],
+ pCore->pg1.MAR[4], pCore->pg1.MAR[5], pCore->pg1.MAR[6], pCore->pg1.MAR[7]);
+
+ /* Dump the DMA state. */
+ pHlp->pfnPrintf(pHlp, " Local DMA : TPSR=%02X00 TBCR=%04X CLDA=%04X\n",
+ pCore->TPSR, pCore->TBCR, pCore->CLDA);
+ pHlp->pfnPrintf(pHlp, " : PSTART=%02X00 PSTOP=%02X00 CURR=%02X00 BNRY=%02X00\n",
+ pCore->PSTART, pCore->PSTOP, pCore->CURR, pCore->BNRY);
+ pHlp->pfnPrintf(pHlp, " Remote DMA: RSAR=%04X RBCR=%04X CRDA=%04X\n",
+ pCore->RSAR, pCore->RBCR, pCore->CRDA);
+
+ /* Try to figure out how much available space there is in the receive ring. */
+ if (pCore->BNRY <= pCore->CURR)
+ uFreePages = pCore->PSTOP - pCore->PSTART - (pCore->CURR - pCore->BNRY);
+ else
+ uFreePages = pCore->BNRY - pCore->CURR;
+ pHlp->pfnPrintf(pHlp, " Estimated %u free pages (%u bytes) in receive ring\n", uFreePages, uFreePages * 256);
+
+ if (pThis->fMaybeOutOfSpace)
+ pHlp->pfnPrintf(pHlp, " Waiting for receive space\n");
+ if (pThis->fLinkTempDown)
+ {
+ pHlp->pfnPrintf(pHlp, " Link down count %d\n", pThis->cLinkDownReported);
+ pHlp->pfnPrintf(pHlp, " Postpone count %d\n", pThis->cLinkRestorePostponed);
+ }
+
+ if ((pThis->uDevType == DEV_WD8003) || (pThis->uDevType == DEV_WD8013))
+ {
+ /* Dump the WD specific registers. */
+ pHlp->pfnPrintf(pHlp, "\nWD80x3 Control Registers\n");
+ pHlp->pfnPrintf(pHlp, " CTRL1=%02X: %s%s A18-A13=%02X\n", pThis->CTRL1,
+ pThis->ctrl1.RESET ? "RESET " : "",
+ pThis->ctrl1.MEME ? "MEME " : "",
+ pThis->ctrl1.A13_18);
+ pHlp->pfnPrintf(pHlp, " CTRL2=%02X: %s%s A23-A19=%02X\n", pThis->CTRL2,
+ pThis->ctrl2.M16 ? "M16 " : "",
+ pThis->ctrl2.MEMW ? "MEMW " : "",
+ pThis->ctrl2.A19_23);
+ }
+
+ if (pThis->uDevType == DEV_3C503)
+ {
+ PEL_GA pGa = &pThis->ga;
+
+ /* Dump the Gate Array state. */
+ pHlp->pfnPrintf(pHlp, "\n3C503 ASIC Gate Array\n");
+ pHlp->pfnPrintf(pHlp, " PSTR=%02X00 PSPR=%02X00 cdadr=%04X\n",
+ pGa->PSTR, pGa->PSTR, pGa->CDADR);
+ pHlp->pfnPrintf(pHlp, " DQTR=%02X: tb=%d\n", pGa->DQTR,
+ pGa->dqtr.tb);
+ pHlp->pfnPrintf(pHlp, " BCFR=%02X PCFR=%02X\n",
+ pGa->BCFR, pGa->PCFR);
+ pHlp->pfnPrintf(pHlp, " GACFR=%02X: mbs=%d %s%s%s%s%s\n", pGa->GACFR,
+ pGa->gacfr.mbs,
+ pGa->gacfr.rsel ? "rsel " : "",
+ pGa->gacfr.test ? "test " : "",
+ pGa->gacfr.ows ? "ows " : "",
+ pGa->gacfr.tcm ? "tcm " : "",
+ pGa->gacfr.nim ? "nim " : "");
+ pHlp->pfnPrintf(pHlp, " GACR=%02X: %s%s%s%s%s%s%s%s\n", pGa->GACR,
+ pGa->gacr.rst ? "rst " : "",
+ pGa->gacr.xsel ? "xsel " : "",
+ pGa->gacr.ealo ? "ealo " : "",
+ pGa->gacr.eahi ? "eahi " : "",
+ pGa->gacr.share ? "share " : "",
+ pGa->gacr.dbsel ? "dbsel " : "",
+ pGa->gacr.ddir ? "ddir " : "",
+ pGa->gacr.start ? "start " : "");
+ pHlp->pfnPrintf(pHlp, " STREG=%02X: rev=%d %s%s%s%s%s\n", pGa->STREG,
+ pGa->streg.rev,
+ pGa->streg.dip ? "dip " : "",
+ pGa->streg.dtc ? "dtc " : "",
+ pGa->streg.oflw ? "oflw " : "",
+ pGa->streg.uflw ? "uflw " : "",
+ pGa->streg.dprdy ? "dprdy " : "");
+ pHlp->pfnPrintf(pHlp, " IDCFR=%02X: %s%s%s%s%s%s%s\n", pGa->IDCFR,
+ pGa->idcfr.drq1 ? "drq1 " : "",
+ pGa->idcfr.drq2 ? "drq2 " : "",
+ pGa->idcfr.drq3 ? "drq3 " : "",
+ pGa->idcfr.irq2 ? "irq2 " : "",
+ pGa->idcfr.irq3 ? "irq3 " : "",
+ pGa->idcfr.irq4 ? "irq4 " : "",
+ pGa->idcfr.irq5 ? "irq5 " : "");
+ pHlp->pfnPrintf(pHlp, " DALSB=%02X DAMSB=%02X addr=%04X\n",
+ pGa->DALSB, pGa->DAMSB,
+ RT_MAKE_U16(pGa->DALSB, pGa->DAMSB));
+ pHlp->pfnPrintf(pHlp, " VPTR0=%02X VPTR1=%02X VPTR2=%02X, VPTR=%X\n",
+ pGa->VPTR0, pGa->VPTR1, pGa->VPTR2,
+ (pGa->VPTR2 << 12) | (pGa->VPTR1 << 4) | (pGa->VPTR0 >> 4));
+
+
+
+ }
+
+ /* Dump the beginning of the send buffer. */
+ if (fSendBuffer)
+ {
+ pHlp->pfnPrintf(pHlp, "Send buffer (start at %u):\n", 0);
+ unsigned dump_end = RT_MIN(0 + 64, sizeof(pThis->abLocalRAM) - 16);
+ for (unsigned ofs = 0; ofs < dump_end; ofs += 16)
+ pHlp->pfnPrintf(pHlp, " %04X: %Rhxs\n", ofs, &pThis->abLocalRAM[ofs]);
+ }
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+}
+
+
+/* -=-=-=-=-=- Helper(s) -=-=-=-=-=- */
+
+
+static void dpNicR3HardReset(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
+{
+ LogFlowFunc(("#%d:\n", pThis->iInstance));
+
+ /* Initialize the PROM. Covers both NE1000 and NE2000. */
+ Assert(sizeof(pThis->MacConfigured) == 6);
+ memset(pThis->aPROM, 0, sizeof(pThis->aPROM));
+ /* The first 6 bytes of PROM always contain the configured MAC address. */
+ memcpy(&pThis->aPROM[0x00], &pThis->MacConfigured, sizeof(pThis->MacConfigured));
+
+ if ((pThis->uDevType == DEV_NE1000) || (pThis->uDevType == DEV_NE2000))
+ {
+ /* The NE1000/NE2000 repeats the MAC address and also includes BB/WW signature. */
+ memcpy(&pThis->aPROM[0x10], &pThis->MacConfigured, sizeof(pThis->MacConfigured));
+ pThis->aPROM[0x0E] = pThis->aPROM[0x0F] = 'W'; /* Word-wide. */
+ pThis->aPROM[0x1E] = pThis->aPROM[0x1F] = 'B'; /* Byte-wide. */
+ }
+ else if ((pThis->uDevType == DEV_WD8003) || (pThis->uDevType == DEV_WD8013))
+ {
+ /* The WD8003/WD8013 only uses 8 bytes of the PROM. The 7th byte
+ * contains a board ID and the last byte is a checksum calculated
+ * such that a two's complement sum of the 8 bytes equals FFh.
+ */
+ int i;
+ uint8_t sum;
+
+ /* The board ID is 2 for 8003S, 3 for 8003E, 4 for 8003WT, 5 for 8013EBT. */
+ pThis->aPROM[0x06] = 3;
+ if (pThis->uDevType == DEV_WD8013)
+ pThis->aPROM[0x06] = 5;
+
+ for (i = 0, sum = 0; i < 7; ++i)
+ sum += pThis->aPROM[i];
+
+ pThis->aPROM[0x07] = 0xff - sum;
+ }
+ else if (pThis->uDevType == DEV_3C503)
+ {
+ const uint16_t el_io_bases[] = { 0x2E0, 0x2A0, 0x280, 0x250, 0x350, 0x330, 0x310, 0x300, 0 };
+ const uint32_t el_mem_bases[] = { 0xDC000, 0xD8000, 0xCC000, 0xC8000, 0 };
+ int i;
+
+ /* Zap the Gate Array state. */
+ memset(&pThis->ga, 0, sizeof(pThis->ga));
+
+ /* Find the BCFR value. */
+ for (i = 0; el_io_bases[i]; ++i)
+ {
+ if (pThis->IOPortBase == el_io_bases[i])
+ break;
+ }
+ /// @todo Make sure we somehow disallow values that a 3C503 can't do
+ if (i < 8)
+ pThis->ga.BCFR = 1 << i;
+
+ /* Find the PCFR value. */
+ for (i = 0; el_mem_bases[i]; ++i)
+ {
+ if (pThis->MemBase == el_mem_bases[i])
+ break;
+ }
+ /// @todo Make sure we somehow disallow values that a 3C503 can't do
+ if (i < 4)
+ pThis->ga.PCFR = RT_BIT(7) >> i;
+ }
+
+ /* Clear the local RAM. */
+ memset(pThis->abLocalRAM, 0, sizeof(pThis->abLocalRAM));
+
+ /* Wipe out all of the DP8390 core state. */
+ memset(&pThis->core, 0, sizeof(pThis->core));
+
+ dp8390CoreReset(pDevIns, pThis);
+}
+
+/**
+ * Takes down the link temporarily if it's current status is up.
+ *
+ * This is used during restore and when replumbing the network link.
+ *
+ * The temporary link outage is supposed to indicate to the OS that all network
+ * connections have been lost and that it for instance is appropriate to
+ * renegotiate any DHCP lease.
+ *
+ * @param pDevIns The device instance data.
+ * @param pThis The device state.
+ */
+static void dp8390TempLinkDown(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
+{
+ if (pThis->fLinkUp)
+ {
+ pThis->fLinkTempDown = true;
+ pThis->cLinkDownReported = 0;
+ pThis->cLinkRestorePostponed = 0;
+ pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
+ int rc = PDMDevHlpTimerSetMillies(pDevIns, pThis->hTimerRestore, pThis->cMsLinkUpDelay);
+ AssertRC(rc);
+ }
+}
+
+
+/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNSSMDEVLIVEEXEC, Pass 0 only.}
+ */
+static DECLCALLBACK(int) dpNicLiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
+{
+ RT_NOREF(uPass);
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ pDevIns->pHlpR3->pfnSSMPutMem(pSSM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
+ return VINF_SSM_DONT_CALL_AGAIN;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEPREP,
+ * Serializes the receive thread, it may be working inside the critsect.}
+ */
+static DECLCALLBACK(int) dpNicSavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ RT_NOREF(pSSM);
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+
+ int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ AssertRC(rc);
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC}
+ */
+static DECLCALLBACK(int) dpNicSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ /* Start with saving the generic bits. */
+ pHlp->pfnSSMPutBool(pSSM, pThis->fLinkUp);
+ pHlp->pfnSSMPutBool(pSSM, pThis->fNicIrqActive);
+
+ /* Continue with DP8390 core. */
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.CR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.DCR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.ISR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.IMR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.RCR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.RSR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.TCR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.TSR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.NCR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.TPSR);
+ pHlp->pfnSSMPutU16(pSSM, pThis->core.TBCR);
+ pHlp->pfnSSMPutU16(pSSM, pThis->core.CLDA);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.PSTART);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.PSTOP);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.CURR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.BNRY);
+ pHlp->pfnSSMPutU16(pSSM, pThis->core.RSAR);
+ pHlp->pfnSSMPutU16(pSSM, pThis->core.RBCR);
+ pHlp->pfnSSMPutU16(pSSM, pThis->core.CRDA);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.lnxtpp);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.rnxtpp);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.CNTR0);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.CNTR1);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.CNTR2);
+ pHlp->pfnSSMPutMem(pSSM, &pThis->core.pg1.PAR, sizeof(pThis->core.pg1.PAR));
+ pHlp->pfnSSMPutMem(pSSM, &pThis->core.pg1.MAR, sizeof(pThis->core.pg1.MAR));
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.fifo.rp);
+ pHlp->pfnSSMPutU8(pSSM, pThis->core.fifo.wp);
+ pHlp->pfnSSMPutMem(pSSM, &pThis->core.fifo.fifo, sizeof(pThis->core.fifo.fifo));
+
+ /* Now the WD80x3 state. */
+ pHlp->pfnSSMPutU8(pSSM, pThis->CTRL1);
+ pHlp->pfnSSMPutU8(pSSM, pThis->CTRL2);
+
+ /* Finally the 3C503-specific state. */
+ pHlp->pfnSSMPutU8(pSSM, pThis->ga.PSTR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->ga.PSPR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->ga.DQTR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->ga.BCFR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->ga.PCFR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->ga.GACFR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->ga.GACR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->ga.STREG);
+ pHlp->pfnSSMPutU8(pSSM, pThis->ga.IDCFR);
+ pHlp->pfnSSMPutU8(pSSM, pThis->ga.DAMSB);
+ pHlp->pfnSSMPutU8(pSSM, pThis->ga.DALSB);
+ pHlp->pfnSSMPutU8(pSSM, pThis->ga.VPTR2);
+ pHlp->pfnSSMPutU8(pSSM, pThis->ga.VPTR1);
+ pHlp->pfnSSMPutU8(pSSM, pThis->ga.VPTR0);
+ pHlp->pfnSSMPutU16(pSSM, pThis->ga.CDADR);
+ pHlp->pfnSSMPutBool(pSSM, pThis->ga.fGaIrq);
+
+ /* Save the configured MAC address. */
+ pHlp->pfnSSMPutMem(pSSM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADPREP},
+ * Serializes the receive thread, it may be working inside the critsect.}
+ */
+static DECLCALLBACK(int) dpNicLoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ RT_NOREF(pSSM);
+
+ int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ AssertRC(rc);
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ */
+static DECLCALLBACK(int) dpNicLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ PDPNICSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDPNICSTATECC);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ if (SSM_VERSION_MAJOR_CHANGED(uVersion, DPNIC_SAVEDSTATE_VERSION))
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+
+ if (uPass == SSM_PASS_FINAL)
+ {
+ /* Restore data, first the generic bits. */
+ pHlp->pfnSSMGetBool(pSSM, &pThis->fLinkUp);
+ pHlp->pfnSSMGetBool(pSSM, &pThis->fNicIrqActive);
+
+ /* Now the DP8390 core. */
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.CR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.DCR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.ISR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.IMR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.RCR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.RSR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.TCR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.TSR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.NCR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.TPSR);
+ pHlp->pfnSSMGetU16(pSSM, &pThis->core.TBCR);
+ pHlp->pfnSSMGetU16(pSSM, &pThis->core.CLDA);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.PSTART);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.PSTOP);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.CURR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.BNRY);
+ pHlp->pfnSSMGetU16(pSSM, &pThis->core.RSAR);
+ pHlp->pfnSSMGetU16(pSSM, &pThis->core.RBCR);
+ pHlp->pfnSSMGetU16(pSSM, &pThis->core.CRDA);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.lnxtpp);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.rnxtpp);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.CNTR0);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.CNTR1);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.CNTR2);
+ pHlp->pfnSSMGetMem(pSSM, &pThis->core.pg1.PAR, sizeof(pThis->core.pg1.PAR));
+ pHlp->pfnSSMGetMem(pSSM, &pThis->core.pg1.MAR, sizeof(pThis->core.pg1.MAR));
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.fifo.rp);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->core.fifo.wp);
+ pHlp->pfnSSMGetMem(pSSM, &pThis->core.fifo.fifo, sizeof(pThis->core.fifo.fifo));
+
+ /* WD80x3-specific state. */
+ pHlp->pfnSSMGetU8(pSSM, &pThis->CTRL1);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->CTRL2);
+
+ /* 3C503-specific state. */
+ pHlp->pfnSSMGetU8(pSSM, &pThis->ga.PSTR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->ga.PSPR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->ga.DQTR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->ga.BCFR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->ga.PCFR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->ga.GACFR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->ga.GACR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->ga.STREG);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->ga.IDCFR);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->ga.DAMSB);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->ga.DALSB);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->ga.VPTR2);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->ga.VPTR1);
+ pHlp->pfnSSMGetU8(pSSM, &pThis->ga.VPTR0);
+ pHlp->pfnSSMGetU16(pSSM, &pThis->ga.CDADR);
+ pHlp->pfnSSMGetBool(pSSM, &pThis->ga.fGaIrq);
+
+ /* Set IRQ and DMA based on IDCFR if this is a 3C503. */
+ if (pThis->uDevType == DEV_3C503)
+ {
+ pThis->uIsaIrq = elGetIrqFromIdcfr(pThis->ga.IDCFR);
+ pThis->uElIsaDma = elGetDrqFromIdcfr(pThis->ga.IDCFR);
+ }
+ }
+
+ /* check config */
+ RTMAC Mac;
+ int rc = pHlp->pfnSSMGetMem(pSSM, &Mac, sizeof(Mac));
+ AssertRCReturn(rc, rc);
+ if ( memcmp(&Mac, &pThis->MacConfigured, sizeof(Mac))
+ && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)) )
+ LogRel(("DPNIC#%u: The mac address differs: config=%RTmac saved=%RTmac\n", pThis->iInstance, &pThis->MacConfigured, &Mac));
+
+ if (uPass == SSM_PASS_FINAL)
+ {
+ /* update promiscuous mode. */
+ if (pThisCC->pDrv)
+ pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, 0 /* promiscuous enabled */);
+
+ /* Indicate link down to the guest OS that all network connections have
+ been lost, unless we've been teleported here. */
+ if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
+ dp8390TempLinkDown(pDevIns, pThis);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/* -=-=-=-=-=- DPNICSTATE::INetworkDown -=-=-=-=-=- */
+
+/**
+ * Check if the device/driver can receive data now.
+ *
+ * Worker for dpNicNet_WaitReceiveAvail(). This must be called before
+ * the pfnRecieve() method is called.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The device instance.
+ * @param pThis The device instance data.
+ */
+static int dp8390CanReceive(PPDMDEVINS pDevIns, PDPNICSTATE pThis)
+{
+ DP8390CORE *pCore = &pThis->core;
+ int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ AssertReleaseRC(rc);
+
+ rc = VINF_SUCCESS;
+
+ /*
+ * The card has typically room for several full-size Ethernet frames but
+ * the buffers can overflow. We cheat a bit and try to hold off when it
+ * looks like there is temporarily not enough buffer spave.
+ *
+ * If the receiver is disabled, accept packets and drop them to avoid
+ * pile-ups. If the receiver is enabled, take a closer look.
+ */
+ if (pCore->cr.STA && !pCore->cr.STP)
+ {
+ /* Receiver is enabled. Find out if we're low on buffer space.
+ * But if the receive buffer isn't at least 4K big (16 pages),
+ * don't bother. Typically there will be 5K or more in the
+ * receive buffer.
+ */
+ if (pCore->PSTART + 16 <= pCore->PSTOP)
+ {
+ uint16_t free_pages;
+
+ /* Free space is between BNRY (host's read pointer) and CURR
+ * (NIC's write pointer).
+ */
+ if (pCore->BNRY <= pCore->CURR)
+ {
+ /* Free space wraps around. This might technically give
+ * the wrong answer if the buffer is empty (BNRY = CURR)
+ * but in that case there's plenty of room anyway.
+ */
+ free_pages = pCore->PSTOP - pCore->PSTART - (pCore->CURR - pCore->BNRY);
+ }
+ else
+ {
+ /* Free space does not wrap. */
+ free_pages = pCore->BNRY - pCore->CURR;
+ }
+ Log2Func(("#%d: %u free pages (%u bytes)\n", pThis->iInstance, free_pages, free_pages * 256));
+
+ /* Six pages (1,536 bytes) is enough for the longest standard Ethernet frame
+ * (1522 bytes including FCS) plus packet header (4 bytes).
+ */
+ if (free_pages < 6)
+ {
+ rc = VERR_NET_NO_BUFFER_SPACE;
+ Log2Func(("#%d: Buffer space low, returning %Rrc!\n", pThis->iInstance, rc));
+ }
+ }
+ }
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
+ */
+static DECLCALLBACK(int) dpNicNet_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
+{
+ PDPNICSTATECC pThisCC = RT_FROM_MEMBER(pInterface, DPNICSTATECC, INetworkDown);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+
+ int rc = dp8390CanReceive(pDevIns, pThis);
+ if (RT_SUCCESS(rc))
+ {
+ STAM_COUNTER_INC(&pThis->StatRxCanReceiveNow);
+ return VINF_SUCCESS;
+ }
+ if (RT_UNLIKELY(cMillies == 0))
+ {
+ STAM_COUNTER_INC(&pThis->StatRxCannotReceiveNow);
+ return VINF_SUCCESS; //VERR_NET_NO_BUFFER_SPACE;
+ }
+
+ rc = VERR_INTERRUPTED;
+ ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, true);
+ STAM_PROFILE_START(&pThis->StatRxOverflow, a);
+ VMSTATE enmVMState;
+ while (RT_LIKELY( (enmVMState = PDMDevHlpVMState(pDevIns)) == VMSTATE_RUNNING
+ || enmVMState == VMSTATE_RUNNING_LS))
+ {
+ int rc2 = dp8390CanReceive(pDevIns, pThis);
+ if (RT_SUCCESS(rc2))
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+ if (cMillies > 666)
+ cMillies = 666;
+ LogFlowFunc(("Waiting cMillies=%u...\n", cMillies));
+
+ rc2 = RTSemEventWait(pThis->hEventOutOfRxSpace, cMillies);
+//LogRelFunc(("RTSemEventWait: rc=%Rrc\n", rc2));
+// if (rc2 == VERR_TIMEOUT)
+// break;
+ }
+ STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
+ ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, false);
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
+ */
+static DECLCALLBACK(int) dpNicNet_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
+{
+ PDPNICSTATECC pThisCC = RT_FROM_MEMBER(pInterface, DPNICSTATECC, INetworkDown);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ int rc;
+
+ STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
+ rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ AssertReleaseRC(rc);
+
+ if (cb > 50) /* unqualified guess */
+ pThis->Led.Asserted.s.fReading = pThis->Led.Actual.s.fReading = 1;
+ dp8390CoreReceiveLocked(pDevIns, pThis, (const uint8_t *)pvBuf, cb);
+ pThis->Led.Actual.s.fReading = 0;
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
+ */
+static DECLCALLBACK(void) dpNicNet_XmitPending(PPDMINETWORKDOWN pInterface)
+{
+ PDPNICSTATECC pThisCC = RT_FROM_MEMBER(pInterface, DPNICSTATECC, INetworkDown);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ dp8390CoreXmitPacket(pDevIns, pThis, true /*fOnWorkerThread*/);
+}
+
+
+/* -=-=-=-=-=- DPNICSTATE::INetworkConfig -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
+ */
+static DECLCALLBACK(int) dpNicGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
+{
+ PDPNICSTATECC pThisCC = RT_FROM_MEMBER(pInterface, DPNICSTATECC, INetworkConfig);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+
+ LogFlowFunc(("#%d\n", pThis->iInstance));
+ /// @todo This is broken!! We can't properly get the MAC address set by the guest
+#if 0
+ memcpy(pMac, pThis->core.pg1.PAR, sizeof(*pMac));
+#else
+ memcpy(pMac, pThis->aPROM, sizeof(*pMac));
+#endif
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
+ */
+static DECLCALLBACK(PDMNETWORKLINKSTATE) dpNicGetLinkState(PPDMINETWORKCONFIG pInterface)
+{
+ PDPNICSTATECC pThisCC = RT_FROM_MEMBER(pInterface, DPNICSTATECC, INetworkConfig);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+
+ if (pThis->fLinkUp && !pThis->fLinkTempDown)
+ return PDMNETWORKLINKSTATE_UP;
+ if (!pThis->fLinkUp)
+ return PDMNETWORKLINKSTATE_DOWN;
+ if (pThis->fLinkTempDown)
+ return PDMNETWORKLINKSTATE_DOWN_RESUME;
+ AssertMsgFailed(("Invalid link state!\n"));
+ return PDMNETWORKLINKSTATE_INVALID;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
+ */
+static DECLCALLBACK(int) dpNicSetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
+{
+ PDPNICSTATECC pThisCC = RT_FROM_MEMBER(pInterface, DPNICSTATECC, INetworkConfig);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ bool fLinkUp;
+
+ LogFlowFunc(("#%d\n", pThis->iInstance));
+ AssertMsgReturn(enmState > PDMNETWORKLINKSTATE_INVALID && enmState <= PDMNETWORKLINKSTATE_DOWN_RESUME,
+ ("Invalid link state: enmState=%d\n", enmState), VERR_INVALID_PARAMETER);
+
+ if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
+ {
+ dp8390TempLinkDown(pDevIns, pThis);
+ /*
+ * Note that we do not notify the driver about the link state change because
+ * the change is only temporary and can be disregarded from the driver's
+ * point of view (see @bugref{7057}).
+ */
+ return VINF_SUCCESS;
+ }
+ /* has the state changed? */
+ fLinkUp = enmState == PDMNETWORKLINKSTATE_UP;
+ if (pThis->fLinkUp != fLinkUp)
+ {
+ pThis->fLinkUp = fLinkUp;
+ if (fLinkUp)
+ {
+ /* Connect with a configured delay. */
+ pThis->fLinkTempDown = true;
+ pThis->cLinkDownReported = 0;
+ pThis->cLinkRestorePostponed = 0;
+ pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
+ int rc = PDMDevHlpTimerSetMillies(pDevIns, pThis->hTimerRestore, pThis->cMsLinkUpDelay);
+ AssertRC(rc);
+ }
+ else
+ {
+ /* Disconnect. */
+ pThis->cLinkDownReported = 0;
+ pThis->cLinkRestorePostponed = 0;
+ pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
+ }
+ Assert(!PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ if (pThisCC->pDrv)
+ pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/* -=-=-=-=-=- DPNICSTATE::ILeds (LUN#0) -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
+ */
+static DECLCALLBACK(int) dpNicQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
+{
+ PDPNICSTATECC pThisCC = RT_FROM_MEMBER(pInterface, DPNICSTATECC, ILeds);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ if (iLUN == 0)
+ {
+ *ppLed = &pThis->Led;
+ return VINF_SUCCESS;
+ }
+ return VERR_PDM_LUN_NOT_FOUND;
+}
+
+
+/* -=-=-=-=-=- DPNICSTATE::IBase (LUN#0) -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) dpNicQueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
+{
+ PDPNICSTATECC pThisCC = RT_FROM_MEMBER(pInterface, DPNICSTATECC, IBase);
+ Assert(&pThisCC->IBase == pInterface);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
+ return NULL;
+}
+
+
+/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnPowerOff}
+ */
+static DECLCALLBACK(void) dpNicR3PowerOff(PPDMDEVINS pDevIns)
+{
+ /* Poke thread waiting for buffer space. */
+ dp8390R3WakeupReceive(pDevIns);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDetach}
+ *
+ * One port on the network card has been disconnected from the network.
+ */
+static DECLCALLBACK(void) dpNicR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ PDPNICSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDPNICSTATECC);
+ RT_NOREF(fFlags);
+ LogFlowFunc(("#%d\n", pThis->iInstance));
+
+ AssertLogRelReturnVoid(iLUN == 0);
+
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
+
+ /*
+ * Zero some important members.
+ */
+ pThis->fDriverAttached = false;
+ pThisCC->pDrvBase = NULL;
+ pThisCC->pDrv = NULL;
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnAttach}
+ * One port on the network card has been connected to a network.
+ */
+static DECLCALLBACK(int) dpNicR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ PDPNICSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDPNICSTATECC);
+ RT_NOREF(fFlags);
+ LogFlowFunc(("#%d\n", pThis->iInstance));
+
+ AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
+
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
+
+ /*
+ * Attach the driver.
+ */
+ int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
+ if (RT_SUCCESS(rc))
+ {
+ pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
+ AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
+ rc = VERR_PDM_MISSING_INTERFACE_BELOW);
+ pThis->fDriverAttached = true;
+ }
+ else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
+ || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
+ {
+ /* This should never happen because this function is not called
+ * if there is no driver to attach! */
+ LogFunc(("#%d No attached driver!\n", pThis->iInstance));
+ }
+
+ /*
+ * Temporarily drop the link if it was up so that the guest
+ * will know that we have changed the configuration of the
+ * network card
+ */
+ if (RT_SUCCESS(rc))
+ dp8390TempLinkDown(pDevIns, pThis);
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnSuspend}
+ */
+static DECLCALLBACK(void) dpNicR3Suspend(PPDMDEVINS pDevIns)
+{
+ /* Poke thread waiting for buffer space. */
+ dp8390R3WakeupReceive(pDevIns);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnReset}
+ */
+static DECLCALLBACK(void) dpNicR3Reset(PPDMDEVINS pDevIns)
+{
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ LogFlowFunc(("#%d\n", pThis->iInstance));
+ if (pThis->fLinkTempDown)
+ {
+ pThis->cLinkDownReported = 0x1000;
+ pThis->cLinkRestorePostponed = 0x1000;
+ PDMDevHlpTimerStop(pDevIns, pThis->hTimerRestore);
+ dpNicR3TimerRestore(pDevIns, pThis->hTimerRestore, pThis);
+ }
+
+ dpNicR3HardReset(pDevIns, pThis);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnRelocate}
+ */
+static DECLCALLBACK(void) dpNicR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
+{
+ PDPNICSTATERC pThisRC = PDMINS_2_DATA_RC(pDevIns, PDPNICSTATERC);
+ pThisRC->pDrv += offDelta;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) dpNicR3Destruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+
+ if (PDMDevHlpCritSectIsInitialized(pDevIns, &pThis->CritSect))
+ {
+ RTSemEventSignal(pThis->hEventOutOfRxSpace);
+ RTSemEventDestroy(pThis->hEventOutOfRxSpace);
+ pThis->hEventOutOfRxSpace = NIL_RTSEMEVENT;
+ PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSect);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) dpNicR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+ PDPNICSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PDPNICSTATECC);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ PPDMIBASE pBase;
+ char szTmp[128];
+ int rc;
+
+ /*
+ * Init what's required to make the destructor safe.
+ */
+ pThis->iInstance = iInstance;
+ pThis->hEventOutOfRxSpace = NIL_RTSEMEVENT;
+ pThis->hIoPortsNic = NIL_IOMIOPORTHANDLE;
+ pThis->hIoPortsCore = NIL_IOMIOPORTHANDLE;
+ pThisCC->pDevIns = pDevIns;
+
+ /*
+ * Validate configuration.
+ */
+ PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC|CableConnected|Port|MemBase|IRQ|DMA|DeviceType|LinkUpDelay|LineSpeed", "");
+
+ /*
+ * Read the configuration.
+ */
+ rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", &pThis->MacConfigured, sizeof(pThis->MacConfigured));
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the \"MAC\" value"));
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "CableConnected", &pThis->fLinkUp, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the \"CableConnected\" value"));
+
+ /*
+ * Determine the model.
+ */
+ char szDeviceType[16];
+ rc = pHlp->pfnCFGMQueryStringDef(pCfg, "DeviceType", &szDeviceType[0], sizeof(szDeviceType), "NE2000");
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"ChipType\" as string failed"));
+
+ if (!strcmp(szDeviceType, "NE1000"))
+ pThis->uDevType = DEV_NE1000; /* Novell NE1000. */
+ else if (!strcmp(szDeviceType, "NE2000"))
+ pThis->uDevType = DEV_NE2000; /* Novell NE2000. */
+ else if (!strcmp(szDeviceType, "WD8003"))
+ pThis->uDevType = DEV_WD8003; /* WD EtherCard Plus. */
+ else if (!strcmp(szDeviceType, "WD8013"))
+ pThis->uDevType = DEV_WD8013; /* WD EtherCard Plus 16. */
+ else if (!strcmp(szDeviceType, "3C503"))
+ pThis->uDevType = DEV_3C503; /* 3Com 3C503 EtherLink II. */
+ else
+ return PDMDevHlpVMSetError(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS,
+ N_("Configuration error: The \"DeviceType\" value \"%s\" is unsupported"),
+ szDeviceType);
+
+
+ /*
+ * Default resource assignments depend on the device type.
+ */
+ unsigned uDefIoPort = 0; /* To be overridden. */
+ unsigned uDefIrq = 0;
+ unsigned uDefDma = 0; /* Default to no DMA. */
+ unsigned uDefMemBase = 0; /* Default to no shared memory. */
+
+ if ((pThis->uDevType == DEV_NE1000) || (pThis->uDevType == DEV_NE2000))
+ {
+ uDefIoPort = 0x300;
+ uDefIrq = 3;
+ }
+ else if ((pThis->uDevType == DEV_WD8003) || (pThis->uDevType == DEV_WD8013))
+ {
+ uDefIoPort = 0x280;
+ uDefIrq = 3;
+ uDefMemBase = 0xd0000;
+ pThis->cbMemSize = _8K;
+ if (pThis->uDevType == DEV_WD8013)
+ pThis->cbMemSize = _16K;
+ }
+ else if (pThis->uDevType == DEV_3C503)
+ {
+ uDefIoPort = 0x300;
+ uDefIrq = 3;
+ uDefDma = 1;
+ uDefMemBase = 0xdc000;
+ pThis->cbMemSize = _8K;
+ }
+
+ /*
+ * Process ISA configuration options.
+ */
+ rc = pHlp->pfnCFGMQueryPortDef(pCfg, "Port", &pThis->IOPortBase, uDefIoPort);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the \"Port\" value"));
+
+ rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IRQ", &pThis->uIsaIrq, uDefIrq);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the \"IRQ\" value"));
+
+ rc = pHlp->pfnCFGMQueryU8Def(pCfg, "DMA", &pThis->uIsaDma, uDefDma);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the \"DMA\" value"));
+
+ rc = pHlp->pfnCFGMQueryGCPtrDef(pCfg, "MemBase", &pThis->MemBase, uDefMemBase);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the \"MemBase\" value"));
+
+
+
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", (uint32_t*)&pThis->cMsLinkUpDelay, 5000); /* ms */
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
+ Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
+ if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
+ {
+ LogRel(("DPNIC#%d WARNING! Link up delay is set to %u seconds!\n",
+ iInstance, pThis->cMsLinkUpDelay / 1000));
+ }
+ LogFunc(("#%d Link up delay is set to %u seconds\n",
+ iInstance, pThis->cMsLinkUpDelay / 1000));
+
+
+ /*
+ * Initialize data (most of it anyway).
+ */
+ pThis->Led.u32Magic = PDMLED_MAGIC;
+ /* IBase */
+ pThisCC->IBase.pfnQueryInterface = dpNicQueryInterface;
+ /* INetworkPort */
+ pThisCC->INetworkDown.pfnWaitReceiveAvail = dpNicNet_WaitReceiveAvail;
+ pThisCC->INetworkDown.pfnReceive = dpNicNet_Receive;
+ pThisCC->INetworkDown.pfnXmitPending = dpNicNet_XmitPending;
+ /* INetworkConfig */
+ pThisCC->INetworkConfig.pfnGetMac = dpNicGetMac;
+ pThisCC->INetworkConfig.pfnGetLinkState = dpNicGetLinkState;
+ pThisCC->INetworkConfig.pfnSetLinkState = dpNicSetLinkState;
+ /* ILeds */
+ pThisCC->ILeds.pfnQueryStatusLed = dpNicQueryStatusLed;
+
+ pThis->hIoPortsCore = NIL_IOMIOPORTHANDLE;
+ pThis->hIoPortsNic = NIL_IOMIOPORTHANDLE;
+ pThis->hSharedMem = NIL_IOMMMIOHANDLE;
+
+ /*
+ * We use our own critical section (historical reasons).
+ */
+ rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "DPNIC#%u", iInstance);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
+ AssertRCReturn(rc, rc);
+
+ rc = RTSemEventCreate(&pThis->hEventOutOfRxSpace);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Register ISA I/O ranges. This depends on the device type.
+ */
+ if ((pThis->uDevType == DEV_NE1000) || (pThis->uDevType == DEV_NE2000))
+ {
+ /* The NE1000 and NE2000 map the DP8390 at the beginning of the port range,
+ * followed by the data/reset ports.
+ */
+ rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase, 0x10 /*cPorts*/, dp8390CoreIOPortWrite, dp8390CoreIOPortRead,
+ "DP8390-Core", NULL /*paExtDesc*/, &pThis->hIoPortsCore);
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase + 0x10, 0x10 /*cPorts*/, neIOPortWrite, neIOPortRead,
+ "DPNIC-NE", NULL /*paExtDesc*/, &pThis->hIoPortsNic);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ else if ((pThis->uDevType == DEV_WD8003) || (pThis->uDevType == DEV_WD8013))
+ {
+ /* The WD8003 and WD8013 map the DP8390 at the end of the port range
+ * (16 bytes into it). The first 8 bytes of the range are largely unused
+ * while the second 8 bytes map the PROM.
+ */
+ rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase, 0x10 /*cPorts*/, wdIOPortWrite, wdIOPortRead,
+ "DPNIC-WD", NULL /*paExtDesc*/, &pThis->hIoPortsNic);
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase + 0x10, 0x10 /*cPorts*/, dp8390CoreIOPortWrite, dp8390CoreIOPortRead,
+ "DP8390-Core", NULL /*paExtDesc*/, &pThis->hIoPortsCore);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Shared memory MMIO area. This is rather lame.
+ */
+ rc = PDMDevHlpMmioCreateExAndMap(pDevIns, pThis->MemBase, pThis->cbMemSize,
+ IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU | IOMMMIO_FLAGS_ABS,
+ NULL /*pPciDev*/, UINT32_MAX /*iPciRegion*/,
+ wdMemWrite, wdMemRead, NULL /*wdMmioFill*/, NULL /*pvUser*/,
+ "DPNIC - WD Shared RAM", &pThis->hSharedMem);
+ AssertRCReturn(rc, rc);
+
+ /* Hack to make WD drivers happy. */
+ memcpy(&pThis->MacConfigured, "\x00\x00\xC0", 3);
+ }
+ else if (pThis->uDevType == DEV_3C503)
+ {
+ /* The 3C503 maps the DP8390 at the base I/O address, except the first
+ * or second 16 bytes of PROM can be mapped into the same space. The
+ * custom Gate Array is mapped at I/O base + 400h.
+ */
+ rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase, 0x10 /*cPorts*/, dp8390CoreIOPortWrite, dp8390CoreIOPortRead,
+ "DP8390-Core", NULL /*paExtDesc*/, &pThis->hIoPortsCore);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase + 0x400, 0x10 /*cPorts*/, elIOPortWrite, elIOPortRead,
+ "DPNIC-EL", NULL /*paExtDesc*/, &pThis->hIoPortsNic);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Shared memory MMIO area. The same lame thing.
+ */
+ rc = PDMDevHlpMmioCreateExAndMap(pDevIns, pThis->MemBase, pThis->cbMemSize,
+ IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU | IOMMMIO_FLAGS_ABS,
+ NULL /*pPciDev*/, UINT32_MAX /*iPciRegion*/,
+ elMemWrite, elMemRead, NULL /*elMmioFill*/, NULL /*pvUser*/,
+ "DPNIC - 3C503 Shared RAM", &pThis->hSharedMem);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Register DMA channel.
+ */
+ if ((pThis->uIsaDma >= ELNKII_MIN_VALID_DMA) && (pThis->uIsaDma <= ELNKII_MAX_VALID_DMA))
+ {
+ rc = PDMDevHlpDMARegister(pDevIns, pThis->uIsaDma, elnk3R3DMAXferHandler, pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+ LogRel(("DPNIC#%d: Enabling 3C503 DMA channel %u\n", iInstance, pThis->uIsaDma));
+ }
+ else
+ LogRel(("DPNIC#%d: Disabling 3C503 DMA\n", iInstance));
+
+ /* Hack to make 3C503 diagnostics happy. */
+ memcpy(&pThis->MacConfigured, "\x02\x60\x8C", 3);
+ }
+
+
+ rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, dpNicR3TimerRestore, NULL, TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
+ "DPNIC Link Restore Timer", &pThis->hTimerRestore);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rc = PDMDevHlpSSMRegisterEx(pDevIns, DPNIC_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
+ NULL, dpNicLiveExec, NULL,
+ dpNicSavePrep, dpNicSaveExec, NULL,
+ dpNicLoadPrep, dpNicLoadExec, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Create the transmit notifier signaller.
+ */
+ rc = PDMDevHlpTaskCreate(pDevIns, PDMTASK_F_RZ, "DPNIC-Xmit", dpNicR3XmitTaskCallback, NULL /*pvUser*/, &pThis->hXmitTask);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Create the RX notifier signaller.
+ */
+ rc = PDMDevHlpTaskCreate(pDevIns, PDMTASK_F_RZ, "DPNIC-Rcv", dpNicR3CanRxTaskCallback, NULL /*pvUser*/, &pThis->hCanRxTask);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Register the info item.
+ */
+ RTStrPrintf(szTmp, sizeof(szTmp), "dpnic%d", pThis->iInstance);
+ PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "dpnic info", dpNicR3Info);
+
+ /*
+ * Attach status driver (optional).
+ */
+ rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pBase, "Status Port");
+ if (RT_SUCCESS(rc))
+ pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
+ else if ( rc != VERR_PDM_NO_ATTACHED_DRIVER
+ && rc != VERR_PDM_CFG_MISSING_DRIVER_NAME)
+ {
+ AssertMsgFailed(("Failed to attach to status driver. rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ /*
+ * Attach driver.
+ */
+ rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
+ if (RT_SUCCESS(rc))
+ {
+ pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
+ AssertMsgReturn(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
+ VERR_PDM_MISSING_INTERFACE_BELOW);
+ pThis->fDriverAttached = true;
+ }
+ else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
+ || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
+ {
+ /* No error! */
+ LogFunc(("No attached driver!\n"));
+ }
+ else
+ return rc;
+
+ /*
+ * Reset the device state. (Do after attaching.)
+ */
+ dpNicR3HardReset(pDevIns, pThis);
+
+ /*
+ * Register statistics counters.
+ */
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Public/Net/DPNIC%u/BytesReceived", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Public/Net/DPNIC%u/BytesTransmitted", iInstance);
+
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data received", "/Devices/DPNIC%d/ReceiveBytes", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Amount of data transmitted", "/Devices/DPNIC%d/TransmitBytes", iInstance);
+
+#ifdef VBOX_WITH_STATISTICS
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOReadRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in RZ", "/Devices/DPNIC%d/IO/ReadRZ", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOReadR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in R3", "/Devices/DPNIC%d/IO/ReadR3", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOWriteRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in RZ", "/Devices/DPNIC%d/IO/WriteRZ", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatIOWriteR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in R3", "/Devices/DPNIC%d/IO/WriteR3", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling receive", "/Devices/DPNIC%d/Receive", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows", "/Devices/DPNIC%d/RxOverflow", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES , "Nr of RX overflow wakeups", "/Devices/DPNIC%d/RxOverflowWakeup", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxCanReceiveNow, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES , "Can receive immediately", "/Devices/DPNIC%d/RxCanReceiveNow", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatRxCannotReceiveNow, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES , "Cannot receive, not waiting", "/Devices/DPNIC%d/RxCannotReceiveNow", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in RZ", "/Devices/DPNIC%d/Transmit/TotalRZ", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling transmits in R3", "/Devices/DPNIC%d/Transmit/TotalR3", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitSendRZ, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in RZ", "/Devices/DPNIC%d/Transmit/SendRZ", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitSendR3, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in R3", "/Devices/DPNIC%d/Transmit/SendR3", iInstance);
+
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatInterrupt, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling interrupt checks", "/Devices/DPNIC%d/UpdateIRQ", iInstance);
+
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktMonitor, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, monitor mode", "/Devices/DPNIC%d/DropPktMonitor", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktRcvrDis, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, receiver not enabled", "/Devices/DPNIC%d/DropPktRcvrDis", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktVeryShort, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet less than 8 bytes long", "/Devices/DPNIC%d/DropPktVeryShort", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktVMNotRunning,STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, VM not running", "/Devices/DPNIC%d/DropPktVMNotRunning", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktNoLink, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, no link", "/Devices/DPNIC%d/DropPktNoLink", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktNoMatch, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, address match reject", "/Devices/DPNIC%d/DropPktNoMatch", iInstance);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatDropPktNoBuffer, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Dropped packet, DP8390 buffer overflow", "/Devices/DPNIC%d/DropPktNoBuffer", iInstance);
+#endif /* VBOX_WITH_STATISTICS */
+
+ return VINF_SUCCESS;
+}
+
+#else
+
+/**
+ * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
+ */
+static DECLCALLBACK(int) dpNicRZConstruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PDPNICSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PDPNICSTATE);
+
+ /* Critical section setup: */
+ int rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
+ AssertRCReturn(rc, rc);
+
+ /* NIC-specific ISA I/O ports: */
+ if (pThis->hIoPortsNic != NIL_IOMIOPORTHANDLE)
+ {
+ switch (pThis->uDevType)
+ {
+ case DEV_NE1000:
+ case DEV_NE2000:
+ rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsNic, neIOPortWrite, neIOPortRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+ break;
+ case DEV_WD8003:
+ case DEV_WD8013:
+ rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsNic, wdIOPortWrite, wdIOPortRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+ break;
+ case DEV_3C503:
+ rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsNic, elIOPortWrite, elIOPortRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+ break;
+ default:
+ /* Must not happen. */
+ return VERR_INTERNAL_ERROR;
+ }
+ }
+
+ /* Common DP8390 core I/O ports: */
+ if (pThis->hIoPortsCore != NIL_IOMIOPORTHANDLE)
+ {
+ rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsCore, dp8390CoreIOPortWrite, dp8390CoreIOPortRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+ }
+
+ /* Shared RAM, if used: */
+ if (pThis->hSharedMem != NIL_IOMMMIOHANDLE)
+ {
+ AssertRCReturn(rc, rc);
+ switch (pThis->uDevType)
+ {
+ case DEV_WD8003:
+ case DEV_WD8013:
+ rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hSharedMem, wdMemWrite, wdMemRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+ break;
+ case DEV_3C503:
+ rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hSharedMem, elMemWrite, elMemRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+ break;
+ case DEV_NE1000:
+ case DEV_NE2000:
+ default:
+ /* Must not happen. */
+ return VERR_INTERNAL_ERROR;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+#endif /* IN_RING3 */
+
+/**
+ * The device registration structure.
+ */
+const PDMDEVREG g_DeviceDP8390 =
+{
+ /* .u32Version = */ PDM_DEVREG_VERSION,
+ /* .uReserved0 = */ 0,
+ /* .szName = */ "dp8390",
+ /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
+ /* .fClass = */ PDM_DEVREG_CLASS_NETWORK,
+ /* .cMaxInstances = */ ~0U,
+ /* .uSharedVersion = */ 42,
+ /* .cbInstanceShared = */ sizeof(DPNICSTATE),
+ /* .cbInstanceCC = */ sizeof(DPNICSTATECC),
+ /* .cbInstanceRC = */ sizeof(DPNICSTATERC),
+ /* .cMaxPciDevices = */ 0,
+ /* .cMaxMsixVectors = */ 0,
+ /* .pszDescription = */ "National Semiconductor DP8390 based adapter.\n",
+#if defined(IN_RING3)
+ /* .pszRCMod = */ "VBoxDDRC.rc",
+ /* .pszR0Mod = */ "VBoxDDR0.r0",
+ /* .pfnConstruct = */ dpNicR3Construct,
+ /* .pfnDestruct = */ dpNicR3Destruct,
+ /* .pfnRelocate = */ dpNicR3Relocate,
+ /* .pfnMemSetup = */ NULL,
+ /* .pfnPowerOn = */ NULL,
+ /* .pfnReset = */ dpNicR3Reset,
+ /* .pfnSuspend = */ dpNicR3Suspend,
+ /* .pfnResume = */ NULL,
+ /* .pfnAttach = */ dpNicR3Attach,
+ /* .pfnDetach = */ dpNicR3Detach,
+ /* .pfnQueryInterface = */ NULL,
+ /* .pfnInitComplete = */ NULL,
+ /* .pfnPowerOff = */ dpNicR3PowerOff,
+ /* .pfnSoftReset = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RING0)
+ /* .pfnEarlyConstruct = */ NULL,
+ /* .pfnConstruct = */ dpNicRZConstruct,
+ /* .pfnDestruct = */ NULL,
+ /* .pfnFinalDestruct = */ NULL,
+ /* .pfnRequest = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RC)
+ /* .pfnConstruct = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#else
+# error "Not in IN_RING3, IN_RING0 or IN_RC!"
+#endif
+ /* .u32VersionEnd = */ PDM_DEVREG_VERSION
+};
+
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
diff --git a/src/VBox/Devices/Network/DevE1000.cpp b/src/VBox/Devices/Network/DevE1000.cpp
new file mode 100644
index 00000000..94cf49ab
--- /dev/null
+++ b/src/VBox/Devices/Network/DevE1000.cpp
@@ -0,0 +1,8510 @@
+/* $Id: DevE1000.cpp $ */
+/** @file
+ * DevE1000 - Intel 82540EM Ethernet Controller Emulation.
+ *
+ * Implemented in accordance with the specification:
+ *
+ * PCI/PCI-X Family of Gigabit Ethernet Controllers Software Developer's Manual
+ * 82540EP/EM, 82541xx, 82544GC/EI, 82545GM/EM, 82546GB/EB, and 82547xx
+ *
+ * 317453-002 Revision 3.5
+ *
+ * @todo IPv6 checksum offloading support
+ * @todo Flexible Filter / Wakeup (optional?)
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_E1000
+#include <iprt/crc.h>
+#include <iprt/ctype.h>
+#include <iprt/net.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/uuid.h>
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/pdmnetifs.h>
+#include <VBox/vmm/pdmnetinline.h>
+#include <VBox/param.h>
+#include "VBoxDD.h"
+
+#include "DevEEPROM.h"
+#include "DevE1000Phy.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @name E1000 Build Options
+ * @{ */
+/** @def E1K_INIT_RA0
+ * E1K_INIT_RA0 forces E1000 to set the first entry in Receive Address filter
+ * table to MAC address obtained from CFGM. Most guests read MAC address from
+ * EEPROM and write it to RA[0] explicitly, but Mac OS X seems to depend on it
+ * being already set (see @bugref{4657}).
+ */
+#define E1K_INIT_RA0
+/** @def E1K_LSC_ON_RESET
+ * E1K_LSC_ON_RESET causes e1000 to generate Link Status Change
+ * interrupt after hard reset. This makes the E1K_LSC_ON_SLU option unnecessary.
+ * With unplugged cable, LSC is triggerred for 82543GC only.
+ */
+#define E1K_LSC_ON_RESET
+/** @def E1K_LSC_ON_SLU
+ * E1K_LSC_ON_SLU causes E1000 to generate Link Status Change interrupt when
+ * the guest driver brings up the link via STATUS.LU bit. Again the only guest
+ * that requires it is Mac OS X (see @bugref{4657}).
+ */
+//#define E1K_LSC_ON_SLU
+/** @def E1K_INIT_LINKUP_DELAY
+ * E1K_INIT_LINKUP_DELAY prevents the link going up while the driver is still
+ * in init (see @bugref{8624}).
+ */
+#define E1K_INIT_LINKUP_DELAY_US (2000 * 1000)
+/** @def E1K_IMS_INT_DELAY_NS
+ * E1K_IMS_INT_DELAY_NS prevents interrupt storms in Windows guests on enabling
+ * interrupts (see @bugref{8624}).
+ */
+#define E1K_IMS_INT_DELAY_NS 100
+/** @def E1K_TX_DELAY
+ * E1K_TX_DELAY aims to improve guest-host transfer rate for TCP streams by
+ * preventing packets to be sent immediately. It allows to send several
+ * packets in a batch reducing the number of acknowledgments. Note that it
+ * effectively disables R0 TX path, forcing sending in R3.
+ */
+//#define E1K_TX_DELAY 150
+/** @def E1K_USE_TX_TIMERS
+ * E1K_USE_TX_TIMERS aims to reduce the number of generated TX interrupts if a
+ * guest driver set the delays via the Transmit Interrupt Delay Value (TIDV)
+ * register. Enabling it showed no positive effects on existing guests so it
+ * stays disabled. See sections 3.2.7.1 and 3.4.3.1 in "8254x Family of Gigabit
+ * Ethernet Controllers Software Developer’s Manual" for more detailed
+ * explanation.
+ */
+//#define E1K_USE_TX_TIMERS
+/** @def E1K_NO_TAD
+ * E1K_NO_TAD disables one of two timers enabled by E1K_USE_TX_TIMERS, the
+ * Transmit Absolute Delay time. This timer sets the maximum time interval
+ * during which TX interrupts can be postponed (delayed). It has no effect
+ * if E1K_USE_TX_TIMERS is not defined.
+ */
+//#define E1K_NO_TAD
+/** @def E1K_REL_DEBUG
+ * E1K_REL_DEBUG enables debug logging of l1, l2, l3 in release build.
+ */
+//#define E1K_REL_DEBUG
+/** @def E1K_INT_STATS
+ * E1K_INT_STATS enables collection of internal statistics used for
+ * debugging of delayed interrupts, etc.
+ */
+#define E1K_INT_STATS
+/** @def E1K_WITH_MSI
+ * E1K_WITH_MSI enables rudimentary MSI support. Not implemented.
+ */
+//#define E1K_WITH_MSI
+/** @def E1K_WITH_TX_CS
+ * E1K_WITH_TX_CS protects e1kXmitPending with a critical section.
+ */
+#define E1K_WITH_TX_CS
+/** @def E1K_WITH_TXD_CACHE
+ * E1K_WITH_TXD_CACHE causes E1000 to fetch multiple TX descriptors in a
+ * single physical memory read (or two if it wraps around the end of TX
+ * descriptor ring). It is required for proper functioning of bandwidth
+ * resource control as it allows to compute exact sizes of packets prior
+ * to allocating their buffers (see @bugref{5582}).
+ */
+#define E1K_WITH_TXD_CACHE
+/** @def E1K_WITH_RXD_CACHE
+ * E1K_WITH_RXD_CACHE causes E1000 to fetch multiple RX descriptors in a
+ * single physical memory read (or two if it wraps around the end of RX
+ * descriptor ring). Intel's packet driver for DOS needs this option in
+ * order to work properly (see @bugref{6217}).
+ */
+#define E1K_WITH_RXD_CACHE
+/** @def E1K_WITH_PREREG_MMIO
+ * E1K_WITH_PREREG_MMIO enables a new style MMIO registration and is
+ * currently only done for testing the relateted PDM, IOM and PGM code. */
+//#define E1K_WITH_PREREG_MMIO
+/* @} */
+/* End of Options ************************************************************/
+
+#ifdef E1K_WITH_TXD_CACHE
+/**
+ * E1K_TXD_CACHE_SIZE specifies the maximum number of TX descriptors stored
+ * in the state structure. It limits the amount of descriptors loaded in one
+ * batch read. For example, Linux guest may use up to 20 descriptors per
+ * TSE packet. The largest TSE packet seen (Windows guest) was 45 descriptors.
+ */
+# define E1K_TXD_CACHE_SIZE 64u
+#endif /* E1K_WITH_TXD_CACHE */
+
+#ifdef E1K_WITH_RXD_CACHE
+/**
+ * E1K_RXD_CACHE_SIZE specifies the maximum number of RX descriptors stored
+ * in the state structure. It limits the amount of descriptors loaded in one
+ * batch read. For example, XP guest adds 15 RX descriptors at a time.
+ */
+# define E1K_RXD_CACHE_SIZE 16u
+#endif /* E1K_WITH_RXD_CACHE */
+
+
+/* Little helpers ************************************************************/
+#undef htons
+#undef ntohs
+#undef htonl
+#undef ntohl
+#define htons(x) ((((x) & 0xff00) >> 8) | (((x) & 0x00ff) << 8))
+#define ntohs(x) htons(x)
+#define htonl(x) ASMByteSwapU32(x)
+#define ntohl(x) htonl(x)
+
+#ifndef DEBUG
+# ifdef E1K_REL_DEBUG
+# define DEBUG
+# define E1kLog(a) LogRel(a)
+# define E1kLog2(a) LogRel(a)
+# define E1kLog3(a) LogRel(a)
+# define E1kLogX(x, a) LogRel(a)
+//# define E1kLog3(a) do {} while (0)
+# else
+# define E1kLog(a) do {} while (0)
+# define E1kLog2(a) do {} while (0)
+# define E1kLog3(a) do {} while (0)
+# define E1kLogX(x, a) do {} while (0)
+# endif
+#else
+# define E1kLog(a) Log(a)
+# define E1kLog2(a) Log2(a)
+# define E1kLog3(a) Log3(a)
+# define E1kLogX(x, a) LogIt(x, LOG_GROUP, a)
+//# define E1kLog(a) do {} while (0)
+//# define E1kLog2(a) do {} while (0)
+//# define E1kLog3(a) do {} while (0)
+#endif
+
+#if 0
+# define LOG_ENABLED
+# define E1kLogRel(a) LogRel(a)
+# undef Log6
+# define Log6(a) LogRel(a)
+#else
+# define E1kLogRel(a) do { } while (0)
+#endif
+
+//#undef DEBUG
+
+#define E1K_RELOCATE(p, o) *(RTHCUINTPTR *)&p += o
+
+#define E1K_INC_CNT32(cnt) \
+do { \
+ if (cnt < UINT32_MAX) \
+ cnt++; \
+} while (0)
+
+#define E1K_ADD_CNT64(cntLo, cntHi, val) \
+do { \
+ uint64_t u64Cnt = RT_MAKE_U64(cntLo, cntHi); \
+ uint64_t tmp = u64Cnt; \
+ u64Cnt += val; \
+ if (tmp > u64Cnt ) \
+ u64Cnt = UINT64_MAX; \
+ cntLo = (uint32_t)u64Cnt; \
+ cntHi = (uint32_t)(u64Cnt >> 32); \
+} while (0)
+
+#ifdef E1K_INT_STATS
+# define E1K_INC_ISTAT_CNT(cnt) do { ++cnt; } while (0)
+#else /* E1K_INT_STATS */
+# define E1K_INC_ISTAT_CNT(cnt) do { } while (0)
+#endif /* E1K_INT_STATS */
+
+
+/*****************************************************************************/
+
+typedef uint32_t E1KCHIP;
+#define E1K_CHIP_82540EM 0
+#define E1K_CHIP_82543GC 1
+#define E1K_CHIP_82545EM 2
+
+#ifdef IN_RING3
+/** Different E1000 chips. */
+static const struct E1kChips
+{
+ uint16_t uPCIVendorId;
+ uint16_t uPCIDeviceId;
+ uint16_t uPCISubsystemVendorId;
+ uint16_t uPCISubsystemId;
+ const char *pcszName;
+} g_aChips[] =
+{
+ /* Vendor Device SSVendor SubSys Name */
+ { 0x8086,
+ /* Temporary code, as MSI-aware driver dislike 0x100E. How to do that right? */
+# ifdef E1K_WITH_MSI
+ 0x105E,
+# else
+ 0x100E,
+# endif
+ 0x8086, 0x001E, "82540EM" }, /* Intel 82540EM-A in Intel PRO/1000 MT Desktop */
+ { 0x8086, 0x1004, 0x8086, 0x1004, "82543GC" }, /* Intel 82543GC in Intel PRO/1000 T Server */
+ { 0x8086, 0x100F, 0x15AD, 0x0750, "82545EM" } /* Intel 82545EM-A in VMWare Network Adapter */
+};
+#endif /* IN_RING3 */
+
+
+/* The size of register area mapped to I/O space */
+#define E1K_IOPORT_SIZE 0x8
+/* The size of memory-mapped register area */
+#define E1K_MM_SIZE 0x20000
+
+#define E1K_MAX_TX_PKT_SIZE 16288
+#define E1K_MAX_RX_PKT_SIZE 16384
+
+/*****************************************************************************/
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+/** Gets the specfieid bits from the register. */
+#define GET_BITS(reg, bits) ((reg & reg##_##bits##_MASK) >> reg##_##bits##_SHIFT)
+#define GET_BITS_V(val, reg, bits) ((val & reg##_##bits##_MASK) >> reg##_##bits##_SHIFT)
+#define BITS(reg, bits, bitval) (bitval << reg##_##bits##_SHIFT)
+#define SET_BITS(reg, bits, bitval) do { reg = (reg & ~reg##_##bits##_MASK) | (bitval << reg##_##bits##_SHIFT); } while (0)
+#define SET_BITS_V(val, reg, bits, bitval) do { val = (val & ~reg##_##bits##_MASK) | (bitval << reg##_##bits##_SHIFT); } while (0)
+
+#define CTRL_SLU UINT32_C(0x00000040)
+#define CTRL_MDIO UINT32_C(0x00100000)
+#define CTRL_MDC UINT32_C(0x00200000)
+#define CTRL_MDIO_DIR UINT32_C(0x01000000)
+#define CTRL_MDC_DIR UINT32_C(0x02000000)
+#define CTRL_RESET UINT32_C(0x04000000)
+#define CTRL_VME UINT32_C(0x40000000)
+
+#define STATUS_LU UINT32_C(0x00000002)
+#define STATUS_TXOFF UINT32_C(0x00000010)
+
+#define EECD_EE_WIRES UINT32_C(0x0F)
+#define EECD_EE_REQ UINT32_C(0x40)
+#define EECD_EE_GNT UINT32_C(0x80)
+
+#define EERD_START UINT32_C(0x00000001)
+#define EERD_DONE UINT32_C(0x00000010)
+#define EERD_DATA_MASK UINT32_C(0xFFFF0000)
+#define EERD_DATA_SHIFT 16
+#define EERD_ADDR_MASK UINT32_C(0x0000FF00)
+#define EERD_ADDR_SHIFT 8
+
+#define MDIC_DATA_MASK UINT32_C(0x0000FFFF)
+#define MDIC_DATA_SHIFT 0
+#define MDIC_REG_MASK UINT32_C(0x001F0000)
+#define MDIC_REG_SHIFT 16
+#define MDIC_PHY_MASK UINT32_C(0x03E00000)
+#define MDIC_PHY_SHIFT 21
+#define MDIC_OP_WRITE UINT32_C(0x04000000)
+#define MDIC_OP_READ UINT32_C(0x08000000)
+#define MDIC_READY UINT32_C(0x10000000)
+#define MDIC_INT_EN UINT32_C(0x20000000)
+#define MDIC_ERROR UINT32_C(0x40000000)
+
+#define TCTL_EN UINT32_C(0x00000002)
+#define TCTL_PSP UINT32_C(0x00000008)
+
+#define RCTL_EN UINT32_C(0x00000002)
+#define RCTL_UPE UINT32_C(0x00000008)
+#define RCTL_MPE UINT32_C(0x00000010)
+#define RCTL_LPE UINT32_C(0x00000020)
+#define RCTL_LBM_MASK UINT32_C(0x000000C0)
+#define RCTL_LBM_SHIFT 6
+#define RCTL_RDMTS_MASK UINT32_C(0x00000300)
+#define RCTL_RDMTS_SHIFT 8
+#define RCTL_LBM_TCVR UINT32_C(3) /**< PHY or external SerDes loopback. */
+#define RCTL_MO_MASK UINT32_C(0x00003000)
+#define RCTL_MO_SHIFT 12
+#define RCTL_BAM UINT32_C(0x00008000)
+#define RCTL_BSIZE_MASK UINT32_C(0x00030000)
+#define RCTL_BSIZE_SHIFT 16
+#define RCTL_VFE UINT32_C(0x00040000)
+#define RCTL_CFIEN UINT32_C(0x00080000)
+#define RCTL_CFI UINT32_C(0x00100000)
+#define RCTL_BSEX UINT32_C(0x02000000)
+#define RCTL_SECRC UINT32_C(0x04000000)
+
+#define ICR_TXDW UINT32_C(0x00000001)
+#define ICR_TXQE UINT32_C(0x00000002)
+#define ICR_LSC UINT32_C(0x00000004)
+#define ICR_RXDMT0 UINT32_C(0x00000010)
+#define ICR_RXT0 UINT32_C(0x00000080)
+#define ICR_TXD_LOW UINT32_C(0x00008000)
+#define RDTR_FPD UINT32_C(0x80000000)
+
+#define PBA_st ((PBAST*)(pThis->auRegs + PBA_IDX))
+typedef struct
+{
+ unsigned rxa : 7;
+ unsigned rxa_r : 9;
+ unsigned txa : 16;
+} PBAST;
+AssertCompileSize(PBAST, 4);
+
+#define TXDCTL_WTHRESH_MASK 0x003F0000
+#define TXDCTL_WTHRESH_SHIFT 16
+#define TXDCTL_LWTHRESH_MASK 0xFE000000
+#define TXDCTL_LWTHRESH_SHIFT 25
+
+#define RXCSUM_PCSS_MASK UINT32_C(0x000000FF)
+#define RXCSUM_PCSS_SHIFT 0
+
+/** @name Register access macros
+ * @remarks These ASSUME alocal variable @a pThis of type PE1KSTATE.
+ * @{ */
+#define CTRL pThis->auRegs[CTRL_IDX]
+#define STATUS pThis->auRegs[STATUS_IDX]
+#define EECD pThis->auRegs[EECD_IDX]
+#define EERD pThis->auRegs[EERD_IDX]
+#define CTRL_EXT pThis->auRegs[CTRL_EXT_IDX]
+#define FLA pThis->auRegs[FLA_IDX]
+#define MDIC pThis->auRegs[MDIC_IDX]
+#define FCAL pThis->auRegs[FCAL_IDX]
+#define FCAH pThis->auRegs[FCAH_IDX]
+#define FCT pThis->auRegs[FCT_IDX]
+#define VET pThis->auRegs[VET_IDX]
+#define ICR pThis->auRegs[ICR_IDX]
+#define ITR pThis->auRegs[ITR_IDX]
+#define ICS pThis->auRegs[ICS_IDX]
+#define IMS pThis->auRegs[IMS_IDX]
+#define IMC pThis->auRegs[IMC_IDX]
+#define RCTL pThis->auRegs[RCTL_IDX]
+#define FCTTV pThis->auRegs[FCTTV_IDX]
+#define TXCW pThis->auRegs[TXCW_IDX]
+#define RXCW pThis->auRegs[RXCW_IDX]
+#define TCTL pThis->auRegs[TCTL_IDX]
+#define TIPG pThis->auRegs[TIPG_IDX]
+#define AIFS pThis->auRegs[AIFS_IDX]
+#define LEDCTL pThis->auRegs[LEDCTL_IDX]
+#define PBA pThis->auRegs[PBA_IDX]
+#define FCRTL pThis->auRegs[FCRTL_IDX]
+#define FCRTH pThis->auRegs[FCRTH_IDX]
+#define RDFH pThis->auRegs[RDFH_IDX]
+#define RDFT pThis->auRegs[RDFT_IDX]
+#define RDFHS pThis->auRegs[RDFHS_IDX]
+#define RDFTS pThis->auRegs[RDFTS_IDX]
+#define RDFPC pThis->auRegs[RDFPC_IDX]
+#define RDBAL pThis->auRegs[RDBAL_IDX]
+#define RDBAH pThis->auRegs[RDBAH_IDX]
+#define RDLEN pThis->auRegs[RDLEN_IDX]
+#define RDH pThis->auRegs[RDH_IDX]
+#define RDT pThis->auRegs[RDT_IDX]
+#define RDTR pThis->auRegs[RDTR_IDX]
+#define RXDCTL pThis->auRegs[RXDCTL_IDX]
+#define RADV pThis->auRegs[RADV_IDX]
+#define RSRPD pThis->auRegs[RSRPD_IDX]
+#define TXDMAC pThis->auRegs[TXDMAC_IDX]
+#define TDFH pThis->auRegs[TDFH_IDX]
+#define TDFT pThis->auRegs[TDFT_IDX]
+#define TDFHS pThis->auRegs[TDFHS_IDX]
+#define TDFTS pThis->auRegs[TDFTS_IDX]
+#define TDFPC pThis->auRegs[TDFPC_IDX]
+#define TDBAL pThis->auRegs[TDBAL_IDX]
+#define TDBAH pThis->auRegs[TDBAH_IDX]
+#define TDLEN pThis->auRegs[TDLEN_IDX]
+#define TDH pThis->auRegs[TDH_IDX]
+#define TDT pThis->auRegs[TDT_IDX]
+#define TIDV pThis->auRegs[TIDV_IDX]
+#define TXDCTL pThis->auRegs[TXDCTL_IDX]
+#define TADV pThis->auRegs[TADV_IDX]
+#define TSPMT pThis->auRegs[TSPMT_IDX]
+#define CRCERRS pThis->auRegs[CRCERRS_IDX]
+#define ALGNERRC pThis->auRegs[ALGNERRC_IDX]
+#define SYMERRS pThis->auRegs[SYMERRS_IDX]
+#define RXERRC pThis->auRegs[RXERRC_IDX]
+#define MPC pThis->auRegs[MPC_IDX]
+#define SCC pThis->auRegs[SCC_IDX]
+#define ECOL pThis->auRegs[ECOL_IDX]
+#define MCC pThis->auRegs[MCC_IDX]
+#define LATECOL pThis->auRegs[LATECOL_IDX]
+#define COLC pThis->auRegs[COLC_IDX]
+#define DC pThis->auRegs[DC_IDX]
+#define TNCRS pThis->auRegs[TNCRS_IDX]
+/* #define SEC pThis->auRegs[SEC_IDX] Conflict with sys/time.h */
+#define CEXTERR pThis->auRegs[CEXTERR_IDX]
+#define RLEC pThis->auRegs[RLEC_IDX]
+#define XONRXC pThis->auRegs[XONRXC_IDX]
+#define XONTXC pThis->auRegs[XONTXC_IDX]
+#define XOFFRXC pThis->auRegs[XOFFRXC_IDX]
+#define XOFFTXC pThis->auRegs[XOFFTXC_IDX]
+#define FCRUC pThis->auRegs[FCRUC_IDX]
+#define PRC64 pThis->auRegs[PRC64_IDX]
+#define PRC127 pThis->auRegs[PRC127_IDX]
+#define PRC255 pThis->auRegs[PRC255_IDX]
+#define PRC511 pThis->auRegs[PRC511_IDX]
+#define PRC1023 pThis->auRegs[PRC1023_IDX]
+#define PRC1522 pThis->auRegs[PRC1522_IDX]
+#define GPRC pThis->auRegs[GPRC_IDX]
+#define BPRC pThis->auRegs[BPRC_IDX]
+#define MPRC pThis->auRegs[MPRC_IDX]
+#define GPTC pThis->auRegs[GPTC_IDX]
+#define GORCL pThis->auRegs[GORCL_IDX]
+#define GORCH pThis->auRegs[GORCH_IDX]
+#define GOTCL pThis->auRegs[GOTCL_IDX]
+#define GOTCH pThis->auRegs[GOTCH_IDX]
+#define RNBC pThis->auRegs[RNBC_IDX]
+#define RUC pThis->auRegs[RUC_IDX]
+#define RFC pThis->auRegs[RFC_IDX]
+#define ROC pThis->auRegs[ROC_IDX]
+#define RJC pThis->auRegs[RJC_IDX]
+#define MGTPRC pThis->auRegs[MGTPRC_IDX]
+#define MGTPDC pThis->auRegs[MGTPDC_IDX]
+#define MGTPTC pThis->auRegs[MGTPTC_IDX]
+#define TORL pThis->auRegs[TORL_IDX]
+#define TORH pThis->auRegs[TORH_IDX]
+#define TOTL pThis->auRegs[TOTL_IDX]
+#define TOTH pThis->auRegs[TOTH_IDX]
+#define TPR pThis->auRegs[TPR_IDX]
+#define TPT pThis->auRegs[TPT_IDX]
+#define PTC64 pThis->auRegs[PTC64_IDX]
+#define PTC127 pThis->auRegs[PTC127_IDX]
+#define PTC255 pThis->auRegs[PTC255_IDX]
+#define PTC511 pThis->auRegs[PTC511_IDX]
+#define PTC1023 pThis->auRegs[PTC1023_IDX]
+#define PTC1522 pThis->auRegs[PTC1522_IDX]
+#define MPTC pThis->auRegs[MPTC_IDX]
+#define BPTC pThis->auRegs[BPTC_IDX]
+#define TSCTC pThis->auRegs[TSCTC_IDX]
+#define TSCTFC pThis->auRegs[TSCTFC_IDX]
+#define RXCSUM pThis->auRegs[RXCSUM_IDX]
+#define WUC pThis->auRegs[WUC_IDX]
+#define WUFC pThis->auRegs[WUFC_IDX]
+#define WUS pThis->auRegs[WUS_IDX]
+#define MANC pThis->auRegs[MANC_IDX]
+#define IPAV pThis->auRegs[IPAV_IDX]
+#define WUPL pThis->auRegs[WUPL_IDX]
+/** @} */
+#endif /* VBOX_DEVICE_STRUCT_TESTCASE */
+
+/**
+ * Indices of memory-mapped registers in register table.
+ */
+typedef enum
+{
+ CTRL_IDX,
+ STATUS_IDX,
+ EECD_IDX,
+ EERD_IDX,
+ CTRL_EXT_IDX,
+ FLA_IDX,
+ MDIC_IDX,
+ FCAL_IDX,
+ FCAH_IDX,
+ FCT_IDX,
+ VET_IDX,
+ ICR_IDX,
+ ITR_IDX,
+ ICS_IDX,
+ IMS_IDX,
+ IMC_IDX,
+ RCTL_IDX,
+ FCTTV_IDX,
+ TXCW_IDX,
+ RXCW_IDX,
+ TCTL_IDX,
+ TIPG_IDX,
+ AIFS_IDX,
+ LEDCTL_IDX,
+ PBA_IDX,
+ FCRTL_IDX,
+ FCRTH_IDX,
+ RDFH_IDX,
+ RDFT_IDX,
+ RDFHS_IDX,
+ RDFTS_IDX,
+ RDFPC_IDX,
+ RDBAL_IDX,
+ RDBAH_IDX,
+ RDLEN_IDX,
+ RDH_IDX,
+ RDT_IDX,
+ RDTR_IDX,
+ RXDCTL_IDX,
+ RADV_IDX,
+ RSRPD_IDX,
+ TXDMAC_IDX,
+ TDFH_IDX,
+ TDFT_IDX,
+ TDFHS_IDX,
+ TDFTS_IDX,
+ TDFPC_IDX,
+ TDBAL_IDX,
+ TDBAH_IDX,
+ TDLEN_IDX,
+ TDH_IDX,
+ TDT_IDX,
+ TIDV_IDX,
+ TXDCTL_IDX,
+ TADV_IDX,
+ TSPMT_IDX,
+ CRCERRS_IDX,
+ ALGNERRC_IDX,
+ SYMERRS_IDX,
+ RXERRC_IDX,
+ MPC_IDX,
+ SCC_IDX,
+ ECOL_IDX,
+ MCC_IDX,
+ LATECOL_IDX,
+ COLC_IDX,
+ DC_IDX,
+ TNCRS_IDX,
+ SEC_IDX,
+ CEXTERR_IDX,
+ RLEC_IDX,
+ XONRXC_IDX,
+ XONTXC_IDX,
+ XOFFRXC_IDX,
+ XOFFTXC_IDX,
+ FCRUC_IDX,
+ PRC64_IDX,
+ PRC127_IDX,
+ PRC255_IDX,
+ PRC511_IDX,
+ PRC1023_IDX,
+ PRC1522_IDX,
+ GPRC_IDX,
+ BPRC_IDX,
+ MPRC_IDX,
+ GPTC_IDX,
+ GORCL_IDX,
+ GORCH_IDX,
+ GOTCL_IDX,
+ GOTCH_IDX,
+ RNBC_IDX,
+ RUC_IDX,
+ RFC_IDX,
+ ROC_IDX,
+ RJC_IDX,
+ MGTPRC_IDX,
+ MGTPDC_IDX,
+ MGTPTC_IDX,
+ TORL_IDX,
+ TORH_IDX,
+ TOTL_IDX,
+ TOTH_IDX,
+ TPR_IDX,
+ TPT_IDX,
+ PTC64_IDX,
+ PTC127_IDX,
+ PTC255_IDX,
+ PTC511_IDX,
+ PTC1023_IDX,
+ PTC1522_IDX,
+ MPTC_IDX,
+ BPTC_IDX,
+ TSCTC_IDX,
+ TSCTFC_IDX,
+ RXCSUM_IDX,
+ WUC_IDX,
+ WUFC_IDX,
+ WUS_IDX,
+ MANC_IDX,
+ IPAV_IDX,
+ WUPL_IDX,
+ MTA_IDX,
+ RA_IDX,
+ VFTA_IDX,
+ IP4AT_IDX,
+ IP6AT_IDX,
+ WUPM_IDX,
+ FFLT_IDX,
+ FFMT_IDX,
+ FFVT_IDX,
+ PBM_IDX,
+ RA_82542_IDX,
+ MTA_82542_IDX,
+ VFTA_82542_IDX,
+ E1K_NUM_OF_REGS
+} E1kRegIndex;
+
+#define E1K_NUM_OF_32BIT_REGS MTA_IDX
+/** The number of registers with strictly increasing offset. */
+#define E1K_NUM_OF_BINARY_SEARCHABLE (WUPL_IDX + 1)
+
+
+/**
+ * Define E1000-specific EEPROM layout.
+ */
+struct E1kEEPROM
+{
+ public:
+ EEPROM93C46 eeprom;
+
+#ifdef IN_RING3
+ /**
+ * Initialize EEPROM content.
+ *
+ * @param macAddr MAC address of E1000.
+ */
+ void init(RTMAC &macAddr)
+ {
+ eeprom.init();
+ memcpy(eeprom.m_au16Data, macAddr.au16, sizeof(macAddr.au16));
+ eeprom.m_au16Data[0x04] = 0xFFFF;
+ /*
+ * bit 3 - full support for power management
+ * bit 10 - full duplex
+ */
+ eeprom.m_au16Data[0x0A] = 0x4408;
+ eeprom.m_au16Data[0x0B] = 0x001E;
+ eeprom.m_au16Data[0x0C] = 0x8086;
+ eeprom.m_au16Data[0x0D] = 0x100E;
+ eeprom.m_au16Data[0x0E] = 0x8086;
+ eeprom.m_au16Data[0x0F] = 0x3040;
+ eeprom.m_au16Data[0x21] = 0x7061;
+ eeprom.m_au16Data[0x22] = 0x280C;
+ eeprom.m_au16Data[0x23] = 0x00C8;
+ eeprom.m_au16Data[0x24] = 0x00C8;
+ eeprom.m_au16Data[0x2F] = 0x0602;
+ updateChecksum();
+ };
+
+ /**
+ * Compute the checksum as required by E1000 and store it
+ * in the last word.
+ */
+ void updateChecksum()
+ {
+ uint16_t u16Checksum = 0;
+
+ for (int i = 0; i < eeprom.SIZE-1; i++)
+ u16Checksum += eeprom.m_au16Data[i];
+ eeprom.m_au16Data[eeprom.SIZE-1] = 0xBABA - u16Checksum;
+ };
+
+ /**
+ * First 6 bytes of EEPROM contain MAC address.
+ *
+ * @returns MAC address of E1000.
+ */
+ void getMac(PRTMAC pMac)
+ {
+ memcpy(pMac->au16, eeprom.m_au16Data, sizeof(pMac->au16));
+ };
+
+ uint32_t read()
+ {
+ return eeprom.read();
+ }
+
+ void write(uint32_t u32Wires)
+ {
+ eeprom.write(u32Wires);
+ }
+
+ bool readWord(uint32_t u32Addr, uint16_t *pu16Value)
+ {
+ return eeprom.readWord(u32Addr, pu16Value);
+ }
+
+ int load(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM)
+ {
+ return eeprom.load(pHlp, pSSM);
+ }
+
+ void save(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM)
+ {
+ eeprom.save(pHlp, pSSM);
+ }
+#endif /* IN_RING3 */
+};
+
+
+#define E1K_SPEC_VLAN(s) (s & 0xFFF)
+#define E1K_SPEC_CFI(s) (!!((s>>12) & 0x1))
+#define E1K_SPEC_PRI(s) ((s>>13) & 0x7)
+
+struct E1kRxDStatus
+{
+ /** @name Descriptor Status field (3.2.3.1)
+ * @{ */
+ unsigned fDD : 1; /**< Descriptor Done. */
+ unsigned fEOP : 1; /**< End of packet. */
+ unsigned fIXSM : 1; /**< Ignore checksum indication. */
+ unsigned fVP : 1; /**< VLAN, matches VET. */
+ unsigned : 1;
+ unsigned fTCPCS : 1; /**< RCP Checksum calculated on the packet. */
+ unsigned fIPCS : 1; /**< IP Checksum calculated on the packet. */
+ unsigned fPIF : 1; /**< Passed in-exact filter */
+ /** @} */
+ /** @name Descriptor Errors field (3.2.3.2)
+ * (Only valid when fEOP and fDD are set.)
+ * @{ */
+ unsigned fCE : 1; /**< CRC or alignment error. */
+ unsigned : 4; /**< Reserved, varies with different models... */
+ unsigned fTCPE : 1; /**< TCP/UDP checksum error. */
+ unsigned fIPE : 1; /**< IP Checksum error. */
+ unsigned fRXE : 1; /**< RX Data error. */
+ /** @} */
+ /** @name Descriptor Special field (3.2.3.3)
+ * @{ */
+ unsigned u16Special : 16; /**< VLAN: Id, Canonical form, Priority. */
+ /** @} */
+};
+typedef struct E1kRxDStatus E1KRXDST;
+
+struct E1kRxDesc_st
+{
+ uint64_t u64BufAddr; /**< Address of data buffer */
+ uint16_t u16Length; /**< Length of data in buffer */
+ uint16_t u16Checksum; /**< Packet checksum */
+ E1KRXDST status;
+};
+typedef struct E1kRxDesc_st E1KRXDESC;
+AssertCompileSize(E1KRXDESC, 16);
+
+#define E1K_DTYP_LEGACY -1
+#define E1K_DTYP_CONTEXT 0
+#define E1K_DTYP_DATA 1
+#define E1K_DTYP_INVALID 2
+
+struct E1kTDLegacy
+{
+ uint64_t u64BufAddr; /**< Address of data buffer */
+ struct TDLCmd_st
+ {
+ unsigned u16Length : 16;
+ unsigned u8CSO : 8;
+ /* CMD field : 8 */
+ unsigned fEOP : 1;
+ unsigned fIFCS : 1;
+ unsigned fIC : 1;
+ unsigned fRS : 1;
+ unsigned fRPS : 1;
+ unsigned fDEXT : 1;
+ unsigned fVLE : 1;
+ unsigned fIDE : 1;
+ } cmd;
+ struct TDLDw3_st
+ {
+ /* STA field */
+ unsigned fDD : 1;
+ unsigned fEC : 1;
+ unsigned fLC : 1;
+ unsigned fTURSV : 1;
+ /* RSV field */
+ unsigned u4RSV : 4;
+ /* CSS field */
+ unsigned u8CSS : 8;
+ /* Special field*/
+ unsigned u16Special: 16;
+ } dw3;
+};
+
+/**
+ * TCP/IP Context Transmit Descriptor, section 3.3.6.
+ */
+struct E1kTDContext
+{
+ struct CheckSum_st
+ {
+ /** TSE: Header start. !TSE: Checksum start. */
+ unsigned u8CSS : 8;
+ /** Checksum offset - where to store it. */
+ unsigned u8CSO : 8;
+ /** Checksum ending (inclusive) offset, 0 = end of packet. */
+ unsigned u16CSE : 16;
+ } ip;
+ struct CheckSum_st tu;
+ struct TDCDw2_st
+ {
+ /** TSE: The total number of payload bytes for this context. Sans header. */
+ unsigned u20PAYLEN : 20;
+ /** The descriptor type - E1K_DTYP_CONTEXT (0). */
+ unsigned u4DTYP : 4;
+ /** TUCMD field, 8 bits
+ * @{ */
+ /** TSE: TCP (set) or UDP (clear). */
+ unsigned fTCP : 1;
+ /** TSE: IPv4 (set) or IPv6 (clear) - for finding the payload length field in
+ * the IP header. Does not affect the checksumming.
+ * @remarks 82544GC/EI interprets a cleared field differently. */
+ unsigned fIP : 1;
+ /** TSE: TCP segmentation enable. When clear the context describes */
+ unsigned fTSE : 1;
+ /** Report status (only applies to dw3.fDD for here). */
+ unsigned fRS : 1;
+ /** Reserved, MBZ. */
+ unsigned fRSV1 : 1;
+ /** Descriptor extension, must be set for this descriptor type. */
+ unsigned fDEXT : 1;
+ /** Reserved, MBZ. */
+ unsigned fRSV2 : 1;
+ /** Interrupt delay enable. */
+ unsigned fIDE : 1;
+ /** @} */
+ } dw2;
+ struct TDCDw3_st
+ {
+ /** Descriptor Done. */
+ unsigned fDD : 1;
+ /** Reserved, MBZ. */
+ unsigned u7RSV : 7;
+ /** TSO: The header (prototype) length (Ethernet[, VLAN tag], IP, TCP/UDP. */
+ unsigned u8HDRLEN : 8;
+ /** TSO: Maximum segment size. */
+ unsigned u16MSS : 16;
+ } dw3;
+};
+typedef struct E1kTDContext E1KTXCTX;
+
+/**
+ * TCP/IP Data Transmit Descriptor, section 3.3.7.
+ */
+struct E1kTDData
+{
+ uint64_t u64BufAddr; /**< Address of data buffer */
+ struct TDDCmd_st
+ {
+ /** The total length of data pointed to by this descriptor. */
+ unsigned u20DTALEN : 20;
+ /** The descriptor type - E1K_DTYP_DATA (1). */
+ unsigned u4DTYP : 4;
+ /** @name DCMD field, 8 bits (3.3.7.1).
+ * @{ */
+ /** End of packet. Note TSCTFC update. */
+ unsigned fEOP : 1;
+ /** Insert Ethernet FCS/CRC (requires fEOP to be set). */
+ unsigned fIFCS : 1;
+ /** Use the TSE context when set and the normal when clear. */
+ unsigned fTSE : 1;
+ /** Report status (dw3.STA). */
+ unsigned fRS : 1;
+ /** Reserved. 82544GC/EI defines this report packet set (RPS). */
+ unsigned fRPS : 1;
+ /** Descriptor extension, must be set for this descriptor type. */
+ unsigned fDEXT : 1;
+ /** VLAN enable, requires CTRL.VME, auto enables FCS/CRC.
+ * Insert dw3.SPECIAL after ethernet header. */
+ unsigned fVLE : 1;
+ /** Interrupt delay enable. */
+ unsigned fIDE : 1;
+ /** @} */
+ } cmd;
+ struct TDDDw3_st
+ {
+ /** @name STA field (3.3.7.2)
+ * @{ */
+ unsigned fDD : 1; /**< Descriptor done. */
+ unsigned fEC : 1; /**< Excess collision. */
+ unsigned fLC : 1; /**< Late collision. */
+ /** Reserved, except for the usual oddball (82544GC/EI) where it's called TU. */
+ unsigned fTURSV : 1;
+ /** @} */
+ unsigned u4RSV : 4; /**< Reserved field, MBZ. */
+ /** @name POPTS (Packet Option) field (3.3.7.3)
+ * @{ */
+ unsigned fIXSM : 1; /**< Insert IP checksum. */
+ unsigned fTXSM : 1; /**< Insert TCP/UDP checksum. */
+ unsigned u6RSV : 6; /**< Reserved, MBZ. */
+ /** @} */
+ /** @name SPECIAL field - VLAN tag to be inserted after ethernet header.
+ * Requires fEOP, fVLE and CTRL.VME to be set.
+ * @{ */
+ unsigned u16Special: 16; /**< VLAN: Id, Canonical form, Priority. */
+ /** @} */
+ } dw3;
+};
+typedef struct E1kTDData E1KTXDAT;
+
+union E1kTxDesc
+{
+ struct E1kTDLegacy legacy;
+ struct E1kTDContext context;
+ struct E1kTDData data;
+};
+typedef union E1kTxDesc E1KTXDESC;
+AssertCompileSize(E1KTXDESC, 16);
+
+#define RA_CTL_AS 0x0003
+#define RA_CTL_AV 0x8000
+
+union E1kRecAddr
+{
+ uint32_t au32[32];
+ struct RAArray
+ {
+ uint8_t addr[6];
+ uint16_t ctl;
+ } array[16];
+};
+typedef struct E1kRecAddr::RAArray E1KRAELEM;
+typedef union E1kRecAddr E1KRA;
+AssertCompileSize(E1KRA, 8*16);
+
+#define E1K_IP_RF UINT16_C(0x8000) /**< reserved fragment flag */
+#define E1K_IP_DF UINT16_C(0x4000) /**< dont fragment flag */
+#define E1K_IP_MF UINT16_C(0x2000) /**< more fragments flag */
+#define E1K_IP_OFFMASK UINT16_C(0x1fff) /**< mask for fragmenting bits */
+
+/** @todo use+extend RTNETIPV4 */
+struct E1kIpHeader
+{
+ /* type of service / version / header length */
+ uint16_t tos_ver_hl;
+ /* total length */
+ uint16_t total_len;
+ /* identification */
+ uint16_t ident;
+ /* fragment offset field */
+ uint16_t offset;
+ /* time to live / protocol*/
+ uint16_t ttl_proto;
+ /* checksum */
+ uint16_t chksum;
+ /* source IP address */
+ uint32_t src;
+ /* destination IP address */
+ uint32_t dest;
+};
+AssertCompileSize(struct E1kIpHeader, 20);
+
+#define E1K_TCP_FIN UINT16_C(0x01)
+#define E1K_TCP_SYN UINT16_C(0x02)
+#define E1K_TCP_RST UINT16_C(0x04)
+#define E1K_TCP_PSH UINT16_C(0x08)
+#define E1K_TCP_ACK UINT16_C(0x10)
+#define E1K_TCP_URG UINT16_C(0x20)
+#define E1K_TCP_ECE UINT16_C(0x40)
+#define E1K_TCP_CWR UINT16_C(0x80)
+#define E1K_TCP_FLAGS UINT16_C(0x3f)
+
+/** @todo use+extend RTNETTCP */
+struct E1kTcpHeader
+{
+ uint16_t src;
+ uint16_t dest;
+ uint32_t seqno;
+ uint32_t ackno;
+ uint16_t hdrlen_flags;
+ uint16_t wnd;
+ uint16_t chksum;
+ uint16_t urgp;
+};
+AssertCompileSize(struct E1kTcpHeader, 20);
+
+
+#ifdef E1K_WITH_TXD_CACHE
+/** The current Saved state version. */
+# define E1K_SAVEDSTATE_VERSION 4
+/** Saved state version for VirtualBox 4.2 with VLAN tag fields. */
+# define E1K_SAVEDSTATE_VERSION_VBOX_42_VTAG 3
+#else /* !E1K_WITH_TXD_CACHE */
+/** The current Saved state version. */
+# define E1K_SAVEDSTATE_VERSION 3
+#endif /* !E1K_WITH_TXD_CACHE */
+/** Saved state version for VirtualBox 4.1 and earlier.
+ * These did not include VLAN tag fields. */
+#define E1K_SAVEDSTATE_VERSION_VBOX_41 2
+/** Saved state version for VirtualBox 3.0 and earlier.
+ * This did not include the configuration part nor the E1kEEPROM. */
+#define E1K_SAVEDSTATE_VERSION_VBOX_30 1
+
+/**
+ * E1000 shared device state.
+ *
+ * This is shared between ring-0 and ring-3.
+ */
+typedef struct E1KSTATE
+{
+ char szPrf[8]; /**< Log prefix, e.g. E1000#1. */
+
+ /** Handle to PCI region \#0, the MMIO region. */
+ IOMIOPORTHANDLE hMmioRegion;
+ /** Handle to PCI region \#2, the I/O ports. */
+ IOMIOPORTHANDLE hIoPorts;
+
+ /** Receive Interrupt Delay Timer. */
+ TMTIMERHANDLE hRIDTimer;
+ /** Receive Absolute Delay Timer. */
+ TMTIMERHANDLE hRADTimer;
+ /** Transmit Interrupt Delay Timer. */
+ TMTIMERHANDLE hTIDTimer;
+ /** Transmit Absolute Delay Timer. */
+ TMTIMERHANDLE hTADTimer;
+ /** Transmit Delay Timer. */
+ TMTIMERHANDLE hTXDTimer;
+ /** Late Interrupt Timer. */
+ TMTIMERHANDLE hIntTimer;
+ /** Link Up(/Restore) Timer. */
+ TMTIMERHANDLE hLUTimer;
+
+ /** Transmit task. */
+ PDMTASKHANDLE hTxTask;
+
+ /** Critical section - what is it protecting? */
+ PDMCRITSECT cs;
+ /** RX Critical section. */
+ PDMCRITSECT csRx;
+#ifdef E1K_WITH_TX_CS
+ /** TX Critical section. */
+ PDMCRITSECT csTx;
+#endif /* E1K_WITH_TX_CS */
+ /** MAC address obtained from the configuration. */
+ RTMAC macConfigured;
+ uint16_t u16Padding0;
+ /** EMT: Last time the interrupt was acknowledged. */
+ uint64_t u64AckedAt;
+ /** All: Used for eliminating spurious interrupts. */
+ bool fIntRaised;
+ /** EMT: false if the cable is disconnected by the GUI. */
+ bool fCableConnected;
+ /** true if the device is attached to a driver. */
+ bool fIsAttached;
+ /** EMT: Compute Ethernet CRC for RX packets. */
+ bool fEthernetCRC;
+ /** All: throttle interrupts. */
+ bool fItrEnabled;
+ /** All: throttle RX interrupts. */
+ bool fItrRxEnabled;
+ /** All: Delay TX interrupts using TIDV/TADV. */
+ bool fTidEnabled;
+ bool afPadding[2];
+ /** Link up delay (in milliseconds). */
+ uint32_t cMsLinkUpDelay;
+
+ /** All: Device register storage. */
+ uint32_t auRegs[E1K_NUM_OF_32BIT_REGS];
+ /** TX/RX: Status LED. */
+ PDMLED led;
+ /** TX/RX: Number of packet being sent/received to show in debug log. */
+ uint32_t u32PktNo;
+
+ /** EMT: Offset of the register to be read via IO. */
+ uint32_t uSelectedReg;
+ /** EMT: Multicast Table Array. */
+ uint32_t auMTA[128];
+ /** EMT: Receive Address registers. */
+ E1KRA aRecAddr;
+ /** EMT: VLAN filter table array. */
+ uint32_t auVFTA[128];
+ /** EMT: Receive buffer size. */
+ uint16_t u16RxBSize;
+ /** EMT: Locked state -- no state alteration possible. */
+ bool fLocked;
+ /** EMT: */
+ bool fDelayInts;
+ /** All: */
+ bool fIntMaskUsed;
+
+ /** N/A: */
+ bool volatile fMaybeOutOfSpace;
+ /** EMT: Gets signalled when more RX descriptors become available. */
+ SUPSEMEVENT hEventMoreRxDescAvail;
+#ifdef E1K_WITH_RXD_CACHE
+ /** RX: Fetched RX descriptors. */
+ E1KRXDESC aRxDescriptors[E1K_RXD_CACHE_SIZE];
+ //uint64_t aRxDescAddr[E1K_RXD_CACHE_SIZE];
+ /** RX: Actual number of fetched RX descriptors. */
+ uint32_t nRxDFetched;
+ /** RX: Index in cache of RX descriptor being processed. */
+ uint32_t iRxDCurrent;
+#endif /* E1K_WITH_RXD_CACHE */
+
+ /** TX: Context used for TCP segmentation packets. */
+ E1KTXCTX contextTSE;
+ /** TX: Context used for ordinary packets. */
+ E1KTXCTX contextNormal;
+#ifdef E1K_WITH_TXD_CACHE
+ /** TX: Fetched TX descriptors. */
+ E1KTXDESC aTxDescriptors[E1K_TXD_CACHE_SIZE];
+ /** TX: Validity of TX descriptors. Set by e1kLocateTxPacket, used by e1kXmitPacket. */
+ bool afTxDValid[E1K_TXD_CACHE_SIZE];
+ /** TX: Actual number of fetched TX descriptors. */
+ uint8_t nTxDFetched;
+ /** TX: Index in cache of TX descriptor being processed. */
+ uint8_t iTxDCurrent;
+ /** TX: Will this frame be sent as GSO. */
+ bool fGSO;
+ /** Alignment padding. */
+ bool fReserved;
+ /** TX: Number of bytes in next packet. */
+ uint32_t cbTxAlloc;
+
+#endif /* E1K_WITH_TXD_CACHE */
+ /** GSO context. u8Type is set to PDMNETWORKGSOTYPE_INVALID when not
+ * applicable to the current TSE mode. */
+ PDMNETWORKGSO GsoCtx;
+ /** Scratch space for holding the loopback / fallback scatter / gather
+ * descriptor. */
+ union
+ {
+ PDMSCATTERGATHER Sg;
+ uint8_t padding[8 * sizeof(RTUINTPTR)];
+ } uTxFallback;
+ /** TX: Transmit packet buffer use for TSE fallback and loopback. */
+ uint8_t aTxPacketFallback[E1K_MAX_TX_PKT_SIZE];
+ /** TX: Number of bytes assembled in TX packet buffer. */
+ uint16_t u16TxPktLen;
+ /** TX: False will force segmentation in e1000 instead of sending frames as GSO. */
+ bool fGSOEnabled;
+ /** TX: IP checksum has to be inserted if true. */
+ bool fIPcsum;
+ /** TX: TCP/UDP checksum has to be inserted if true. */
+ bool fTCPcsum;
+ /** TX: VLAN tag has to be inserted if true. */
+ bool fVTag;
+ /** TX: TCI part of VLAN tag to be inserted. */
+ uint16_t u16VTagTCI;
+ /** TX TSE fallback: Number of payload bytes remaining in TSE context. */
+ uint32_t u32PayRemain;
+ /** TX TSE fallback: Number of header bytes remaining in TSE context. */
+ uint16_t u16HdrRemain;
+ /** TX TSE fallback: Flags from template header. */
+ uint16_t u16SavedFlags;
+ /** TX TSE fallback: Partial checksum from template header. */
+ uint32_t u32SavedCsum;
+ /** ?: Emulated controller type. */
+ E1KCHIP eChip;
+
+ /** EMT: Physical interface emulation. */
+ PHY phy;
+
+#if 0
+ /** Alignment padding. */
+ uint8_t Alignment[HC_ARCH_BITS == 64 ? 8 : 4];
+#endif
+
+ STAMCOUNTER StatReceiveBytes;
+ STAMCOUNTER StatTransmitBytes;
+#if defined(VBOX_WITH_STATISTICS)
+ STAMPROFILEADV StatMMIOReadRZ;
+ STAMPROFILEADV StatMMIOReadR3;
+ STAMPROFILEADV StatMMIOWriteRZ;
+ STAMPROFILEADV StatMMIOWriteR3;
+ STAMPROFILEADV StatEEPROMRead;
+ STAMPROFILEADV StatEEPROMWrite;
+ STAMPROFILEADV StatIOReadRZ;
+ STAMPROFILEADV StatIOReadR3;
+ STAMPROFILEADV StatIOWriteRZ;
+ STAMPROFILEADV StatIOWriteR3;
+ STAMPROFILEADV StatLateIntTimer;
+ STAMCOUNTER StatLateInts;
+ STAMCOUNTER StatIntsRaised;
+ STAMCOUNTER StatIntsPrevented;
+ STAMPROFILEADV StatReceive;
+ STAMPROFILEADV StatReceiveCRC;
+ STAMPROFILEADV StatReceiveFilter;
+ STAMPROFILEADV StatReceiveStore;
+ STAMPROFILEADV StatTransmitRZ;
+ STAMPROFILEADV StatTransmitR3;
+ STAMPROFILE StatTransmitSendRZ;
+ STAMPROFILE StatTransmitSendR3;
+ STAMPROFILE StatRxOverflow;
+ STAMCOUNTER StatRxOverflowWakeupRZ;
+ STAMCOUNTER StatRxOverflowWakeupR3;
+ STAMCOUNTER StatTxDescCtxNormal;
+ STAMCOUNTER StatTxDescCtxTSE;
+ STAMCOUNTER StatTxDescLegacy;
+ STAMCOUNTER StatTxDescData;
+ STAMCOUNTER StatTxDescTSEData;
+ STAMCOUNTER StatTxPathFallback;
+ STAMCOUNTER StatTxPathGSO;
+ STAMCOUNTER StatTxPathRegular;
+ STAMCOUNTER StatPHYAccesses;
+ STAMCOUNTER aStatRegWrites[E1K_NUM_OF_REGS];
+ STAMCOUNTER aStatRegReads[E1K_NUM_OF_REGS];
+#endif /* VBOX_WITH_STATISTICS */
+
+#ifdef E1K_INT_STATS
+ /* Internal stats */
+ uint64_t u64ArmedAt;
+ uint64_t uStatMaxTxDelay;
+ uint32_t uStatInt;
+ uint32_t uStatIntTry;
+ uint32_t uStatIntLower;
+ uint32_t uStatNoIntICR;
+ int32_t iStatIntLost;
+ int32_t iStatIntLostOne;
+ uint32_t uStatIntIMS;
+ uint32_t uStatIntSkip;
+ uint32_t uStatIntLate;
+ uint32_t uStatIntMasked;
+ uint32_t uStatIntEarly;
+ uint32_t uStatIntRx;
+ uint32_t uStatIntTx;
+ uint32_t uStatIntICS;
+ uint32_t uStatIntRDTR;
+ uint32_t uStatIntRXDMT0;
+ uint32_t uStatIntTXQE;
+ uint32_t uStatTxNoRS;
+ uint32_t uStatTxIDE;
+ uint32_t uStatTxDelayed;
+ uint32_t uStatTxDelayExp;
+ uint32_t uStatTAD;
+ uint32_t uStatTID;
+ uint32_t uStatRAD;
+ uint32_t uStatRID;
+ uint32_t uStatRxFrm;
+ uint32_t uStatTxFrm;
+ uint32_t uStatDescCtx;
+ uint32_t uStatDescDat;
+ uint32_t uStatDescLeg;
+ uint32_t uStatTx1514;
+ uint32_t uStatTx2962;
+ uint32_t uStatTx4410;
+ uint32_t uStatTx5858;
+ uint32_t uStatTx7306;
+ uint32_t uStatTx8754;
+ uint32_t uStatTx16384;
+ uint32_t uStatTx32768;
+ uint32_t uStatTxLarge;
+ uint32_t uStatAlign;
+#endif /* E1K_INT_STATS */
+} E1KSTATE;
+/** Pointer to the E1000 device state. */
+typedef E1KSTATE *PE1KSTATE;
+
+/**
+ * E1000 ring-3 device state
+ *
+ * @implements PDMINETWORKDOWN
+ * @implements PDMINETWORKCONFIG
+ * @implements PDMILEDPORTS
+ */
+typedef struct E1KSTATER3
+{
+ PDMIBASE IBase;
+ PDMINETWORKDOWN INetworkDown;
+ PDMINETWORKCONFIG INetworkConfig;
+ /** LED interface */
+ PDMILEDPORTS ILeds;
+ /** Attached network driver. */
+ R3PTRTYPE(PPDMIBASE) pDrvBase;
+ R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
+
+ /** Pointer to the shared state. */
+ R3PTRTYPE(PE1KSTATE) pShared;
+
+ /** Device instance. */
+ PPDMDEVINSR3 pDevInsR3;
+ /** Attached network driver. */
+ PPDMINETWORKUPR3 pDrvR3;
+ /** The scatter / gather buffer used for the current outgoing packet. */
+ R3PTRTYPE(PPDMSCATTERGATHER) pTxSgR3;
+
+ /** EMT: EEPROM emulation */
+ E1kEEPROM eeprom;
+} E1KSTATER3;
+/** Pointer to the E1000 ring-3 device state. */
+typedef E1KSTATER3 *PE1KSTATER3;
+
+
+/**
+ * E1000 ring-0 device state
+ */
+typedef struct E1KSTATER0
+{
+ /** Device instance. */
+ PPDMDEVINSR0 pDevInsR0;
+ /** Attached network driver. */
+ PPDMINETWORKUPR0 pDrvR0;
+ /** The scatter / gather buffer used for the current outgoing packet - R0. */
+ R0PTRTYPE(PPDMSCATTERGATHER) pTxSgR0;
+} E1KSTATER0;
+/** Pointer to the E1000 ring-0 device state. */
+typedef E1KSTATER0 *PE1KSTATER0;
+
+
+/**
+ * E1000 raw-mode device state
+ */
+typedef struct E1KSTATERC
+{
+ /** Device instance. */
+ PPDMDEVINSRC pDevInsRC;
+ /** Attached network driver. */
+ PPDMINETWORKUPRC pDrvRC;
+ /** The scatter / gather buffer used for the current outgoing packet. */
+ RCPTRTYPE(PPDMSCATTERGATHER) pTxSgRC;
+} E1KSTATERC;
+/** Pointer to the E1000 raw-mode device state. */
+typedef E1KSTATERC *PE1KSTATERC;
+
+
+/** @def PE1KSTATECC
+ * Pointer to the instance data for the current context. */
+#ifdef IN_RING3
+typedef E1KSTATER3 E1KSTATECC;
+typedef PE1KSTATER3 PE1KSTATECC;
+#elif defined(IN_RING0)
+typedef E1KSTATER0 E1KSTATECC;
+typedef PE1KSTATER0 PE1KSTATECC;
+#elif defined(IN_RC)
+typedef E1KSTATERC E1KSTATECC;
+typedef PE1KSTATERC PE1KSTATECC;
+#else
+# error "Not IN_RING3, IN_RING0 or IN_RC"
+#endif
+
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+/* Forward declarations ******************************************************/
+static int e1kXmitPending(PPDMDEVINS pDevIns, PE1KSTATE pThis, bool fOnWorkerThread);
+
+/**
+ * E1000 register read handler.
+ */
+typedef int (FNE1KREGREAD)(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t *pu32Value);
+/**
+ * E1000 register write handler.
+ */
+typedef int (FNE1KREGWRITE)(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t u32Value);
+
+static FNE1KREGREAD e1kRegReadUnimplemented;
+static FNE1KREGWRITE e1kRegWriteUnimplemented;
+static FNE1KREGREAD e1kRegReadAutoClear;
+static FNE1KREGREAD e1kRegReadDefault;
+static FNE1KREGWRITE e1kRegWriteDefault;
+#if 0 /* unused */
+static FNE1KREGREAD e1kRegReadCTRL;
+#endif
+static FNE1KREGWRITE e1kRegWriteCTRL;
+static FNE1KREGREAD e1kRegReadEECD;
+static FNE1KREGWRITE e1kRegWriteEECD;
+static FNE1KREGWRITE e1kRegWriteEERD;
+static FNE1KREGWRITE e1kRegWriteMDIC;
+static FNE1KREGREAD e1kRegReadICR;
+static FNE1KREGWRITE e1kRegWriteICR;
+static FNE1KREGREAD e1kRegReadICS;
+static FNE1KREGWRITE e1kRegWriteICS;
+static FNE1KREGWRITE e1kRegWriteIMS;
+static FNE1KREGWRITE e1kRegWriteIMC;
+static FNE1KREGWRITE e1kRegWriteRCTL;
+static FNE1KREGWRITE e1kRegWritePBA;
+static FNE1KREGWRITE e1kRegWriteRDT;
+static FNE1KREGWRITE e1kRegWriteRDTR;
+static FNE1KREGWRITE e1kRegWriteTDT;
+static FNE1KREGREAD e1kRegReadMTA;
+static FNE1KREGWRITE e1kRegWriteMTA;
+static FNE1KREGREAD e1kRegReadRA;
+static FNE1KREGWRITE e1kRegWriteRA;
+static FNE1KREGREAD e1kRegReadVFTA;
+static FNE1KREGWRITE e1kRegWriteVFTA;
+
+/**
+ * Register map table.
+ *
+ * Override pfnRead and pfnWrite to get register-specific behavior.
+ */
+static const struct E1kRegMap_st
+{
+ /** Register offset in the register space. */
+ uint32_t offset;
+ /** Size in bytes. Registers of size > 4 are in fact tables. */
+ uint32_t size;
+ /** Readable bits. */
+ uint32_t readable;
+ /** Writable bits. */
+ uint32_t writable;
+ /** Read callback. */
+ FNE1KREGREAD *pfnRead;
+ /** Write callback. */
+ FNE1KREGWRITE *pfnWrite;
+ /** Abbreviated name. */
+ const char *abbrev;
+ /** Full name. */
+ const char *name;
+} g_aE1kRegMap[E1K_NUM_OF_REGS] =
+{
+ /* offset size read mask write mask read callback write callback abbrev full name */
+ /*------- ------- ---------- ---------- ----------------------- ------------------------ ---------- ------------------------------*/
+ { 0x00000, 0x00004, 0xDBF31BE9, 0xDBF31BE9, e1kRegReadDefault , e1kRegWriteCTRL , "CTRL" , "Device Control" },
+ { 0x00008, 0x00004, 0x0000FDFF, 0x00000000, e1kRegReadDefault , e1kRegWriteUnimplemented, "STATUS" , "Device Status" },
+ { 0x00010, 0x00004, 0x000027F0, 0x00000070, e1kRegReadEECD , e1kRegWriteEECD , "EECD" , "EEPROM/Flash Control/Data" },
+ { 0x00014, 0x00004, 0xFFFFFF10, 0xFFFFFF00, e1kRegReadDefault , e1kRegWriteEERD , "EERD" , "EEPROM Read" },
+ { 0x00018, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "CTRL_EXT", "Extended Device Control" },
+ { 0x0001c, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "FLA" , "Flash Access (N/A)" },
+ { 0x00020, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadDefault , e1kRegWriteMDIC , "MDIC" , "MDI Control" },
+ { 0x00028, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "FCAL" , "Flow Control Address Low" },
+ { 0x0002c, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "FCAH" , "Flow Control Address High" },
+ { 0x00030, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "FCT" , "Flow Control Type" },
+ { 0x00038, 0x00004, 0x0000FFFF, 0x0000FFFF, e1kRegReadDefault , e1kRegWriteDefault , "VET" , "VLAN EtherType" },
+ { 0x000c0, 0x00004, 0x0001F6DF, 0x0001F6DF, e1kRegReadICR , e1kRegWriteICR , "ICR" , "Interrupt Cause Read" },
+ { 0x000c4, 0x00004, 0x0000FFFF, 0x0000FFFF, e1kRegReadDefault , e1kRegWriteDefault , "ITR" , "Interrupt Throttling" },
+ { 0x000c8, 0x00004, 0x0001F6DF, 0xFFFFFFFF, e1kRegReadICS , e1kRegWriteICS , "ICS" , "Interrupt Cause Set" },
+ { 0x000d0, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadDefault , e1kRegWriteIMS , "IMS" , "Interrupt Mask Set/Read" },
+ { 0x000d8, 0x00004, 0x00000000, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteIMC , "IMC" , "Interrupt Mask Clear" },
+ { 0x00100, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadDefault , e1kRegWriteRCTL , "RCTL" , "Receive Control" },
+ { 0x00170, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "FCTTV" , "Flow Control Transmit Timer Value" },
+ { 0x00178, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "TXCW" , "Transmit Configuration Word (N/A)" },
+ { 0x00180, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "RXCW" , "Receive Configuration Word (N/A)" },
+ { 0x00400, 0x00004, 0x017FFFFA, 0x017FFFFA, e1kRegReadDefault , e1kRegWriteDefault , "TCTL" , "Transmit Control" },
+ { 0x00410, 0x00004, 0x3FFFFFFF, 0x3FFFFFFF, e1kRegReadDefault , e1kRegWriteDefault , "TIPG" , "Transmit IPG" },
+ { 0x00458, 0x00004, 0x0000FFFF, 0x0000FFFF, e1kRegReadDefault , e1kRegWriteDefault , "AIFS" , "Adaptive IFS Throttle - AIT" },
+ { 0x00e00, 0x00004, 0xCFCFCFCF, 0xCFCFCFCF, e1kRegReadDefault , e1kRegWriteDefault , "LEDCTL" , "LED Control" },
+ { 0x01000, 0x00004, 0xFFFF007F, 0x0000007F, e1kRegReadDefault , e1kRegWritePBA , "PBA" , "Packet Buffer Allocation" },
+ { 0x02160, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "FCRTL" , "Flow Control Receive Threshold Low" },
+ { 0x02168, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "FCRTH" , "Flow Control Receive Threshold High" },
+ { 0x02410, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "RDFH" , "Receive Data FIFO Head" },
+ { 0x02418, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "RDFT" , "Receive Data FIFO Tail" },
+ { 0x02420, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "RDFHS" , "Receive Data FIFO Head Saved Register" },
+ { 0x02428, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "RDFTS" , "Receive Data FIFO Tail Saved Register" },
+ { 0x02430, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "RDFPC" , "Receive Data FIFO Packet Count" },
+ { 0x02800, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadDefault , e1kRegWriteDefault , "RDBAL" , "Receive Descriptor Base Low" },
+ { 0x02804, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadDefault , e1kRegWriteDefault , "RDBAH" , "Receive Descriptor Base High" },
+ { 0x02808, 0x00004, 0x000FFF80, 0x000FFF80, e1kRegReadDefault , e1kRegWriteDefault , "RDLEN" , "Receive Descriptor Length" },
+ { 0x02810, 0x00004, 0x0000FFFF, 0x0000FFFF, e1kRegReadDefault , e1kRegWriteDefault , "RDH" , "Receive Descriptor Head" },
+ { 0x02818, 0x00004, 0x0000FFFF, 0x0000FFFF, e1kRegReadDefault , e1kRegWriteRDT , "RDT" , "Receive Descriptor Tail" },
+ { 0x02820, 0x00004, 0x0000FFFF, 0x0000FFFF, e1kRegReadDefault , e1kRegWriteRDTR , "RDTR" , "Receive Delay Timer" },
+ { 0x02828, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "RXDCTL" , "Receive Descriptor Control" },
+ { 0x0282c, 0x00004, 0x0000FFFF, 0x0000FFFF, e1kRegReadDefault , e1kRegWriteDefault , "RADV" , "Receive Interrupt Absolute Delay Timer" },
+ { 0x02c00, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "RSRPD" , "Receive Small Packet Detect Interrupt" },
+ { 0x03000, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "TXDMAC" , "TX DMA Control (N/A)" },
+ { 0x03410, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "TDFH" , "Transmit Data FIFO Head" },
+ { 0x03418, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "TDFT" , "Transmit Data FIFO Tail" },
+ { 0x03420, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "TDFHS" , "Transmit Data FIFO Head Saved Register" },
+ { 0x03428, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "TDFTS" , "Transmit Data FIFO Tail Saved Register" },
+ { 0x03430, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "TDFPC" , "Transmit Data FIFO Packet Count" },
+ { 0x03800, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadDefault , e1kRegWriteDefault , "TDBAL" , "Transmit Descriptor Base Low" },
+ { 0x03804, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadDefault , e1kRegWriteDefault , "TDBAH" , "Transmit Descriptor Base High" },
+ { 0x03808, 0x00004, 0x000FFF80, 0x000FFF80, e1kRegReadDefault , e1kRegWriteDefault , "TDLEN" , "Transmit Descriptor Length" },
+ { 0x03810, 0x00004, 0x0000FFFF, 0x0000FFFF, e1kRegReadDefault , e1kRegWriteDefault , "TDH" , "Transmit Descriptor Head" },
+ { 0x03818, 0x00004, 0x0000FFFF, 0x0000FFFF, e1kRegReadDefault , e1kRegWriteTDT , "TDT" , "Transmit Descriptor Tail" },
+ { 0x03820, 0x00004, 0x0000FFFF, 0x0000FFFF, e1kRegReadDefault , e1kRegWriteDefault , "TIDV" , "Transmit Interrupt Delay Value" },
+ { 0x03828, 0x00004, 0xFF3F3F3F, 0xFF3F3F3F, e1kRegReadDefault , e1kRegWriteDefault , "TXDCTL" , "Transmit Descriptor Control" },
+ { 0x0382c, 0x00004, 0x0000FFFF, 0x0000FFFF, e1kRegReadDefault , e1kRegWriteDefault , "TADV" , "Transmit Absolute Interrupt Delay Timer" },
+ { 0x03830, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadDefault , e1kRegWriteDefault , "TSPMT" , "TCP Segmentation Pad and Threshold" },
+ { 0x04000, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "CRCERRS" , "CRC Error Count" },
+ { 0x04004, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "ALGNERRC", "Alignment Error Count" },
+ { 0x04008, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "SYMERRS" , "Symbol Error Count" },
+ { 0x0400c, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "RXERRC" , "RX Error Count" },
+ { 0x04010, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "MPC" , "Missed Packets Count" },
+ { 0x04014, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "SCC" , "Single Collision Count" },
+ { 0x04018, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "ECOL" , "Excessive Collisions Count" },
+ { 0x0401c, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "MCC" , "Multiple Collision Count" },
+ { 0x04020, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "LATECOL" , "Late Collisions Count" },
+ { 0x04028, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "COLC" , "Collision Count" },
+ { 0x04030, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "DC" , "Defer Count" },
+ { 0x04034, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "TNCRS" , "Transmit - No CRS" },
+ { 0x04038, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "SEC" , "Sequence Error Count" },
+ { 0x0403c, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "CEXTERR" , "Carrier Extension Error Count" },
+ { 0x04040, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "RLEC" , "Receive Length Error Count" },
+ { 0x04048, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "XONRXC" , "XON Received Count" },
+ { 0x0404c, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "XONTXC" , "XON Transmitted Count" },
+ { 0x04050, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "XOFFRXC" , "XOFF Received Count" },
+ { 0x04054, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "XOFFTXC" , "XOFF Transmitted Count" },
+ { 0x04058, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "FCRUC" , "FC Received Unsupported Count" },
+ { 0x0405c, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "PRC64" , "Packets Received (64 Bytes) Count" },
+ { 0x04060, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "PRC127" , "Packets Received (65-127 Bytes) Count" },
+ { 0x04064, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "PRC255" , "Packets Received (128-255 Bytes) Count" },
+ { 0x04068, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "PRC511" , "Packets Received (256-511 Bytes) Count" },
+ { 0x0406c, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "PRC1023" , "Packets Received (512-1023 Bytes) Count" },
+ { 0x04070, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "PRC1522" , "Packets Received (1024-Max Bytes)" },
+ { 0x04074, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "GPRC" , "Good Packets Received Count" },
+ { 0x04078, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "BPRC" , "Broadcast Packets Received Count" },
+ { 0x0407c, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "MPRC" , "Multicast Packets Received Count" },
+ { 0x04080, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "GPTC" , "Good Packets Transmitted Count" },
+ { 0x04088, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "GORCL" , "Good Octets Received Count (Low)" },
+ { 0x0408c, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "GORCH" , "Good Octets Received Count (Hi)" },
+ { 0x04090, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "GOTCL" , "Good Octets Transmitted Count (Low)" },
+ { 0x04094, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "GOTCH" , "Good Octets Transmitted Count (Hi)" },
+ { 0x040a0, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "RNBC" , "Receive No Buffers Count" },
+ { 0x040a4, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "RUC" , "Receive Undersize Count" },
+ { 0x040a8, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "RFC" , "Receive Fragment Count" },
+ { 0x040ac, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "ROC" , "Receive Oversize Count" },
+ { 0x040b0, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "RJC" , "Receive Jabber Count" },
+ { 0x040b4, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "MGTPRC" , "Management Packets Received Count" },
+ { 0x040b8, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "MGTPDC" , "Management Packets Dropped Count" },
+ { 0x040bc, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "MGTPTC" , "Management Pkts Transmitted Count" },
+ { 0x040c0, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "TORL" , "Total Octets Received (Lo)" },
+ { 0x040c4, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "TORH" , "Total Octets Received (Hi)" },
+ { 0x040c8, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "TOTL" , "Total Octets Transmitted (Lo)" },
+ { 0x040cc, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "TOTH" , "Total Octets Transmitted (Hi)" },
+ { 0x040d0, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "TPR" , "Total Packets Received" },
+ { 0x040d4, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "TPT" , "Total Packets Transmitted" },
+ { 0x040d8, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "PTC64" , "Packets Transmitted (64 Bytes) Count" },
+ { 0x040dc, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "PTC127" , "Packets Transmitted (65-127 Bytes) Count" },
+ { 0x040e0, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "PTC255" , "Packets Transmitted (128-255 Bytes) Count" },
+ { 0x040e4, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "PTC511" , "Packets Transmitted (256-511 Bytes) Count" },
+ { 0x040e8, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "PTC1023" , "Packets Transmitted (512-1023 Bytes) Count" },
+ { 0x040ec, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "PTC1522" , "Packets Transmitted (1024 Bytes or Greater) Count" },
+ { 0x040f0, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "MPTC" , "Multicast Packets Transmitted Count" },
+ { 0x040f4, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "BPTC" , "Broadcast Packets Transmitted Count" },
+ { 0x040f8, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "TSCTC" , "TCP Segmentation Context Transmitted Count" },
+ { 0x040fc, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadAutoClear , e1kRegWriteUnimplemented, "TSCTFC" , "TCP Segmentation Context Tx Fail Count" },
+ { 0x05000, 0x00004, 0x000007FF, 0x000007FF, e1kRegReadDefault , e1kRegWriteDefault , "RXCSUM" , "Receive Checksum Control" },
+ { 0x05800, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "WUC" , "Wakeup Control" },
+ { 0x05808, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "WUFC" , "Wakeup Filter Control" },
+ { 0x05810, 0x00004, 0xFFFFFFFF, 0x00000000, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "WUS" , "Wakeup Status" },
+ { 0x05820, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadDefault , e1kRegWriteDefault , "MANC" , "Management Control" },
+ { 0x05838, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "IPAV" , "IP Address Valid" },
+ { 0x05900, 0x00004, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "WUPL" , "Wakeup Packet Length" },
+ { 0x05200, 0x00200, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadMTA , e1kRegWriteMTA , "MTA" , "Multicast Table Array (n)" },
+ { 0x05400, 0x00080, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadRA , e1kRegWriteRA , "RA" , "Receive Address (64-bit) (n)" },
+ { 0x05600, 0x00200, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadVFTA , e1kRegWriteVFTA , "VFTA" , "VLAN Filter Table Array (n)" },
+ { 0x05840, 0x0001c, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "IP4AT" , "IPv4 Address Table" },
+ { 0x05880, 0x00010, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "IP6AT" , "IPv6 Address Table" },
+ { 0x05a00, 0x00080, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "WUPM" , "Wakeup Packet Memory" },
+ { 0x05f00, 0x0001c, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "FFLT" , "Flexible Filter Length Table" },
+ { 0x09000, 0x003fc, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "FFMT" , "Flexible Filter Mask Table" },
+ { 0x09800, 0x003fc, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "FFVT" , "Flexible Filter Value Table" },
+ { 0x10000, 0x10000, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadUnimplemented, e1kRegWriteUnimplemented, "PBM" , "Packet Buffer Memory (n)" },
+ { 0x00040, 0x00080, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadRA , e1kRegWriteRA , "RA82542" , "Receive Address (64-bit) (n) (82542)" },
+ { 0x00200, 0x00200, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadMTA , e1kRegWriteMTA , "MTA82542", "Multicast Table Array (n) (82542)" },
+ { 0x00600, 0x00200, 0xFFFFFFFF, 0xFFFFFFFF, e1kRegReadVFTA , e1kRegWriteVFTA , "VFTA82542", "VLAN Filter Table Array (n) (82542)" }
+};
+
+#ifdef LOG_ENABLED
+
+/**
+ * Convert U32 value to hex string. Masked bytes are replaced with dots.
+ *
+ * @remarks The mask has half-byte byte (not bit) granularity (e.g. 0000000F).
+ *
+ * @returns The buffer.
+ *
+ * @param u32 The word to convert into string.
+ * @param mask Selects which bytes to convert.
+ * @param buf Where to put the result.
+ */
+static char *e1kU32toHex(uint32_t u32, uint32_t mask, char *buf)
+{
+ for (char *ptr = buf + 7; ptr >= buf; --ptr, u32 >>=4, mask >>=4)
+ {
+ if (mask & 0xF)
+ *ptr = (u32 & 0xF) + ((u32 & 0xF) > 9 ? '7' : '0');
+ else
+ *ptr = '.';
+ }
+ buf[8] = 0;
+ return buf;
+}
+
+/**
+ * Returns timer name for debug purposes.
+ *
+ * @returns The timer name.
+ *
+ * @param pThis The device state structure.
+ * @param hTimer The timer to name.
+ */
+DECLINLINE(const char *) e1kGetTimerName(PE1KSTATE pThis, TMTIMERHANDLE hTimer)
+{
+ if (hTimer == pThis->hTIDTimer)
+ return "TID";
+ if (hTimer == pThis->hTADTimer)
+ return "TAD";
+ if (hTimer == pThis->hRIDTimer)
+ return "RID";
+ if (hTimer == pThis->hRADTimer)
+ return "RAD";
+ if (hTimer == pThis->hIntTimer)
+ return "Int";
+ if (hTimer == pThis->hTXDTimer)
+ return "TXD";
+ if (hTimer == pThis->hLUTimer)
+ return "LinkUp";
+ return "unknown";
+}
+
+#endif /* LOG_ENABLED */
+
+/**
+ * Arm a timer.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis Pointer to the device state structure.
+ * @param hTimer The timer to arm.
+ * @param uExpireIn Expiration interval in microseconds.
+ */
+DECLINLINE(void) e1kArmTimer(PPDMDEVINS pDevIns, PE1KSTATE pThis, TMTIMERHANDLE hTimer, uint32_t uExpireIn)
+{
+ if (pThis->fLocked)
+ return;
+
+ E1kLog2(("%s Arming %s timer to fire in %d usec...\n",
+ pThis->szPrf, e1kGetTimerName(pThis, hTimer), uExpireIn));
+ int rc = PDMDevHlpTimerSetMicro(pDevIns, hTimer, uExpireIn);
+ AssertRC(rc);
+}
+
+#ifdef IN_RING3
+/**
+ * Cancel a timer.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis Pointer to the device state structure.
+ * @param pTimer Pointer to the timer.
+ */
+DECLINLINE(void) e1kCancelTimer(PPDMDEVINS pDevIns, PE1KSTATE pThis, TMTIMERHANDLE hTimer)
+{
+ E1kLog2(("%s Stopping %s timer...\n",
+ pThis->szPrf, e1kGetTimerName(pThis, hTimer)));
+ int rc = PDMDevHlpTimerStop(pDevIns, hTimer);
+ if (RT_FAILURE(rc))
+ E1kLog2(("%s e1kCancelTimer: TMTimerStop(%s) failed with %Rrc\n",
+ pThis->szPrf, e1kGetTimerName(pThis, hTimer), rc));
+ RT_NOREF_PV(pThis);
+}
+#endif /* IN_RING3 */
+
+
+#define e1kCsEnter(ps, rcBusy) PDMDevHlpCritSectEnter(pDevIns, &(ps)->cs, (rcBusy))
+#define e1kCsEnterReturn(ps, rcBusy) do { \
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &(ps)->cs, (rcBusy)); \
+ if (rcLock == VINF_SUCCESS) { /* likely */ } \
+ else return rcLock; \
+ } while (0)
+#define e1kR3CsEnterAsserted(ps) do { \
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &(ps)->cs, VERR_SEM_BUSY); \
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &(ps)->cs, rcLock); \
+ } while (0)
+#define e1kCsLeave(ps) PDMDevHlpCritSectLeave(pDevIns, &(ps)->cs)
+
+
+#define e1kCsRxEnter(ps, rcBusy) PDMDevHlpCritSectEnter(pDevIns, &(ps)->csRx, (rcBusy))
+#define e1kCsRxEnterReturn(ps) do { \
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &(ps)->csRx, VERR_SEM_BUSY); \
+ AssertRCReturn(rcLock, rcLock); \
+ } while (0)
+#define e1kR3CsRxEnterAsserted(ps) do { \
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &(ps)->csRx, VERR_SEM_BUSY); \
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &(ps)->csRx, rcLock); \
+ } while (0)
+#define e1kCsRxLeave(ps) PDMDevHlpCritSectLeave(pDevIns, &(ps)->csRx)
+#define e1kCsRxIsOwner(ps) PDMDevHlpCritSectIsOwner(pDevIns, &(ps)->csRx)
+
+
+#ifndef E1K_WITH_TX_CS
+# define e1kCsTxEnter(ps, rcBusy) VINF_SUCCESS
+# define e1kR3CsTxEnterAsserted(ps) do { } while (0)
+# define e1kCsTxLeave(ps) do { } while (0)
+#else /* E1K_WITH_TX_CS */
+# define e1kCsTxEnter(ps, rcBusy) PDMDevHlpCritSectEnter(pDevIns, &(ps)->csTx, (rcBusy))
+# define e1kR3CsTxEnterAsserted(ps) do { \
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &(ps)->csTx, VERR_SEM_BUSY); \
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &(ps)->csTx, rcLock); \
+ } while (0)
+# define e1kCsTxLeave(ps) PDMDevHlpCritSectLeave(pDevIns, &(ps)->csTx)
+# define e1kCsTxIsOwner(ps) PDMDevHlpCritSectIsOwner(pDevIns, &(ps)->csTx)
+#endif /* E1K_WITH_TX_CS */
+
+
+#ifdef E1K_WITH_TXD_CACHE
+/*
+ * Transmit Descriptor Register Context
+ */
+struct E1kTxDContext
+{
+ uint32_t tdlen;
+ uint32_t tdh;
+ uint32_t tdt;
+ uint8_t nextPacket;
+};
+typedef struct E1kTxDContext E1KTXDC, *PE1KTXDC;
+
+DECLINLINE(bool) e1kUpdateTxDContext(PPDMDEVINS pDevIns, PE1KSTATE pThis, PE1KTXDC pContext)
+{
+ Assert(e1kCsTxIsOwner(pThis));
+ if (!e1kCsTxIsOwner(pThis))
+ {
+ memset(pContext, 0, sizeof(E1KTXDC));
+ return false;
+ }
+ pContext->tdlen = TDLEN;
+ pContext->tdh = TDH;
+ pContext->tdt = TDT;
+ uint32_t cTxRingSize = pContext->tdlen / sizeof(E1KTXDESC);
+#ifdef DEBUG
+ if (pContext->tdh >= cTxRingSize)
+ {
+ Log(("%s e1kUpdateTxDContext: will return false because TDH too big (%u >= %u)\n",
+ pThis->szPrf, pContext->tdh, cTxRingSize));
+ return VINF_SUCCESS;
+ }
+ if (pContext->tdt >= cTxRingSize)
+ {
+ Log(("%s e1kUpdateTxDContext: will return false because TDT too big (%u >= %u)\n",
+ pThis->szPrf, pContext->tdt, cTxRingSize));
+ return VINF_SUCCESS;
+ }
+#endif /* DEBUG */
+ return pContext->tdh < cTxRingSize && pContext->tdt < cTxRingSize;
+}
+#endif /* E1K_WITH_TXD_CACHE */
+#ifdef E1K_WITH_RXD_CACHE
+/*
+ * Receive Descriptor Register Context
+ */
+struct E1kRxDContext
+{
+ uint32_t rdlen;
+ uint32_t rdh;
+ uint32_t rdt;
+};
+typedef struct E1kRxDContext E1KRXDC, *PE1KRXDC;
+
+DECLINLINE(bool) e1kUpdateRxDContext(PPDMDEVINS pDevIns, PE1KSTATE pThis, PE1KRXDC pContext, const char *pcszCallee)
+{
+ Assert(e1kCsRxIsOwner(pThis));
+ if (!e1kCsRxIsOwner(pThis))
+ return false;
+ pContext->rdlen = RDLEN;
+ pContext->rdh = RDH;
+ pContext->rdt = RDT;
+ uint32_t cRxRingSize = pContext->rdlen / sizeof(E1KRXDESC);
+ /*
+ * Note that the checks for RDT are a bit different. Some guests, OS/2 for
+ * example, intend to use all descriptors in RX ring, so they point RDT
+ * right beyond the last descriptor in the ring. While this is not
+ * acceptable for other registers, it works out fine for RDT.
+ */
+#ifdef DEBUG
+ if (pContext->rdh >= cRxRingSize)
+ {
+ Log(("%s e1kUpdateRxDContext: called from %s, will return false because RDH too big (%u >= %u)\n",
+ pThis->szPrf, pcszCallee, pContext->rdh, cRxRingSize));
+ return VINF_SUCCESS;
+ }
+ if (pContext->rdt > cRxRingSize)
+ {
+ Log(("%s e1kUpdateRxDContext: called from %s, will return false because RDT too big (%u > %u)\n",
+ pThis->szPrf, pcszCallee, pContext->rdt, cRxRingSize));
+ return VINF_SUCCESS;
+ }
+#else /* !DEBUG */
+ RT_NOREF(pcszCallee);
+#endif /* !DEBUG */
+ return pContext->rdh < cRxRingSize && pContext->rdt <= cRxRingSize; // && (RCTL & RCTL_EN);
+}
+#endif /* E1K_WITH_RXD_CACHE */
+
+/**
+ * Wakeup the RX thread.
+ */
+static void e1kWakeupReceive(PPDMDEVINS pDevIns, PE1KSTATE pThis)
+{
+ if ( pThis->fMaybeOutOfSpace
+ && pThis->hEventMoreRxDescAvail != NIL_SUPSEMEVENT)
+ {
+ STAM_COUNTER_INC(&pThis->CTX_SUFF_Z(StatRxOverflowWakeup));
+ E1kLog(("%s Waking up Out-of-RX-space semaphore\n", pThis->szPrf));
+ int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventMoreRxDescAvail);
+ AssertRC(rc);
+ }
+}
+
+#ifdef IN_RING3
+
+/**
+ * Hardware reset. Revert all registers to initial values.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param pThisCC The current context instance data.
+ */
+static void e1kR3HardReset(PPDMDEVINS pDevIns, PE1KSTATE pThis, PE1KSTATECC pThisCC)
+{
+ E1kLog(("%s Hard reset triggered\n", pThis->szPrf));
+ /* No interrupts should survive device reset, see @bugref(9556). */
+ if (pThis->fIntRaised)
+ {
+ /* Lower(0) INTA(0) */
+ PDMDevHlpPCISetIrq(pDevIns, 0, 0);
+ pThis->fIntRaised = false;
+ E1kLog(("%s e1kR3HardReset: Lowered IRQ: ICR=%08x\n", pThis->szPrf, ICR));
+ }
+ memset(pThis->auRegs, 0, sizeof(pThis->auRegs));
+ memset(pThis->aRecAddr.au32, 0, sizeof(pThis->aRecAddr.au32));
+# ifdef E1K_INIT_RA0
+ memcpy(pThis->aRecAddr.au32, pThis->macConfigured.au8,
+ sizeof(pThis->macConfigured.au8));
+ pThis->aRecAddr.array[0].ctl |= RA_CTL_AV;
+# endif /* E1K_INIT_RA0 */
+ STATUS = 0x0081; /* SPEED=10b (1000 Mb/s), FD=1b (Full Duplex) */
+ EECD = 0x0100; /* EE_PRES=1b (EEPROM present) */
+ CTRL = 0x0a09; /* FRCSPD=1b SPEED=10b LRST=1b FD=1b */
+ TSPMT = 0x01000400;/* TSMT=0400h TSPBP=0100h */
+ Assert(GET_BITS(RCTL, BSIZE) == 0);
+ pThis->u16RxBSize = 2048;
+
+ uint16_t u16LedCtl = 0x0602; /* LED0/LINK_UP#, LED2/LINK100# */
+ pThisCC->eeprom.readWord(0x2F, &u16LedCtl); /* Read LEDCTL defaults from EEPROM */
+ LEDCTL = 0x07008300 | (((uint32_t)u16LedCtl & 0xCF00) << 8) | (u16LedCtl & 0xCF); /* Only LED0 and LED2 defaults come from EEPROM */
+
+ /* Reset promiscuous mode */
+ if (pThisCC->pDrvR3)
+ pThisCC->pDrvR3->pfnSetPromiscuousMode(pThisCC->pDrvR3, false);
+
+# ifdef E1K_WITH_TXD_CACHE
+ e1kR3CsTxEnterAsserted(pThis);
+ pThis->nTxDFetched = 0;
+ pThis->iTxDCurrent = 0;
+ pThis->fGSO = false;
+ pThis->cbTxAlloc = 0;
+ e1kCsTxLeave(pThis);
+# endif /* E1K_WITH_TXD_CACHE */
+# ifdef E1K_WITH_RXD_CACHE
+ e1kR3CsRxEnterAsserted(pThis);
+ pThis->iRxDCurrent = pThis->nRxDFetched = 0;
+ e1kCsRxLeave(pThis);
+# endif /* E1K_WITH_RXD_CACHE */
+# ifdef E1K_LSC_ON_RESET
+ E1kLog(("%s Will trigger LSC in %d seconds...\n",
+ pThis->szPrf, pThis->cMsLinkUpDelay / 1000));
+ e1kArmTimer(pDevIns, pThis, pThis->hLUTimer, pThis->cMsLinkUpDelay * 1000);
+# endif /* E1K_LSC_ON_RESET */
+}
+
+#endif /* IN_RING3 */
+
+/**
+ * Compute Internet checksum.
+ *
+ * @remarks Refer to http://www.netfor2.com/checksum.html for short intro.
+ *
+ * @param pThis The device state structure.
+ * @param cpPacket The packet.
+ * @param cb The size of the packet.
+ * @param pszText A string denoting direction of packet transfer.
+ *
+ * @return The 1's complement of the 1's complement sum.
+ *
+ * @thread E1000_TX
+ */
+static uint16_t e1kCSum16(const void *pvBuf, size_t cb)
+{
+ uint32_t csum = 0;
+ uint16_t *pu16 = (uint16_t *)pvBuf;
+
+ while (cb > 1)
+ {
+ csum += *pu16++;
+ cb -= 2;
+ }
+ if (cb)
+ csum += *(uint8_t*)pu16;
+ while (csum >> 16)
+ csum = (csum >> 16) + (csum & 0xFFFF);
+ Assert(csum < 65536);
+ return (uint16_t)~csum;
+}
+
+/**
+ * Dump a packet to debug log.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param cpPacket The packet.
+ * @param cb The size of the packet.
+ * @param pszText A string denoting direction of packet transfer.
+ * @thread E1000_TX
+ */
+DECLINLINE(void) e1kPacketDump(PPDMDEVINS pDevIns, PE1KSTATE pThis, const uint8_t *cpPacket, size_t cb, const char *pszText)
+{
+#ifdef DEBUG
+ if (RT_LIKELY(e1kCsEnter(pThis, VERR_SEM_BUSY) == VINF_SUCCESS))
+ {
+ Log4(("%s --- %s packet #%d: %RTmac => %RTmac (%d bytes) ---\n",
+ pThis->szPrf, pszText, ++pThis->u32PktNo, cpPacket+6, cpPacket, cb));
+ if (ntohs(*(uint16_t*)(cpPacket+12)) == 0x86DD)
+ {
+ Log4(("%s --- IPv6: %RTnaipv6 => %RTnaipv6\n",
+ pThis->szPrf, cpPacket+14+8, cpPacket+14+24));
+ if (*(cpPacket+14+6) == 0x6)
+ Log4(("%s --- TCP: seq=%x ack=%x\n", pThis->szPrf,
+ ntohl(*(uint32_t*)(cpPacket+14+40+4)), ntohl(*(uint32_t*)(cpPacket+14+40+8))));
+ }
+ else if (ntohs(*(uint16_t*)(cpPacket+12)) == 0x800)
+ {
+ Log4(("%s --- IPv4: %RTnaipv4 => %RTnaipv4\n",
+ pThis->szPrf, *(uint32_t*)(cpPacket+14+12), *(uint32_t*)(cpPacket+14+16)));
+ if (*(cpPacket+14+6) == 0x6)
+ Log4(("%s --- TCP: seq=%x ack=%x\n", pThis->szPrf,
+ ntohl(*(uint32_t*)(cpPacket+14+20+4)), ntohl(*(uint32_t*)(cpPacket+14+20+8))));
+ }
+ E1kLog3(("%.*Rhxd\n", cb, cpPacket));
+ e1kCsLeave(pThis);
+ }
+#else
+ if (RT_LIKELY(e1kCsEnter(pThis, VERR_SEM_BUSY) == VINF_SUCCESS))
+ {
+ if (ntohs(*(uint16_t*)(cpPacket+12)) == 0x86DD)
+ E1kLogRel(("E1000: %s packet #%d, %RTmac => %RTmac, %RTnaipv6 => %RTnaipv6, seq=%x ack=%x\n",
+ pszText, ++pThis->u32PktNo, cpPacket+6, cpPacket, cpPacket+14+8, cpPacket+14+24,
+ ntohl(*(uint32_t*)(cpPacket+14+40+4)), ntohl(*(uint32_t*)(cpPacket+14+40+8))));
+ else
+ E1kLogRel(("E1000: %s packet #%d, %RTmac => %RTmac, %RTnaipv4 => %RTnaipv4, seq=%x ack=%x\n",
+ pszText, ++pThis->u32PktNo, cpPacket+6, cpPacket,
+ *(uint32_t*)(cpPacket+14+12), *(uint32_t*)(cpPacket+14+16),
+ ntohl(*(uint32_t*)(cpPacket+14+20+4)), ntohl(*(uint32_t*)(cpPacket+14+20+8))));
+ e1kCsLeave(pThis);
+ }
+ RT_NOREF2(cb, pszText);
+#endif
+}
+
+/**
+ * Determine the type of transmit descriptor.
+ *
+ * @returns Descriptor type. See E1K_DTYP_XXX defines.
+ *
+ * @param pDesc Pointer to descriptor union.
+ * @thread E1000_TX
+ */
+DECLINLINE(int) e1kGetDescType(E1KTXDESC *pDesc)
+{
+ if (pDesc->legacy.cmd.fDEXT)
+ return pDesc->context.dw2.u4DTYP;
+ return E1K_DTYP_LEGACY;
+}
+
+
+#ifdef E1K_WITH_RXD_CACHE
+/**
+ * Return the number of RX descriptor that belong to the hardware.
+ *
+ * @returns the number of available descriptors in RX ring.
+ * @param pRxdc The receive descriptor register context.
+ * @thread ???
+ */
+DECLINLINE(uint32_t) e1kGetRxLen(PE1KRXDC pRxdc)
+{
+ /**
+ * Make sure RDT won't change during computation. EMT may modify RDT at
+ * any moment.
+ */
+ uint32_t rdt = pRxdc->rdt;
+ return (pRxdc->rdh > rdt ? pRxdc->rdlen/sizeof(E1KRXDESC) : 0) + rdt - pRxdc->rdh;
+}
+
+DECLINLINE(unsigned) e1kRxDInCache(PE1KSTATE pThis)
+{
+ return pThis->nRxDFetched > pThis->iRxDCurrent ?
+ pThis->nRxDFetched - pThis->iRxDCurrent : 0;
+}
+
+DECLINLINE(unsigned) e1kRxDIsCacheEmpty(PE1KSTATE pThis)
+{
+ return pThis->iRxDCurrent >= pThis->nRxDFetched;
+}
+
+/**
+ * Load receive descriptors from guest memory. The caller needs to be in Rx
+ * critical section.
+ *
+ * We need two physical reads in case the tail wrapped around the end of RX
+ * descriptor ring.
+ *
+ * @returns the actual number of descriptors fetched.
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @thread EMT, RX
+ */
+DECLINLINE(unsigned) e1kRxDPrefetch(PPDMDEVINS pDevIns, PE1KSTATE pThis, PE1KRXDC pRxdc)
+{
+ E1kLog3(("%s e1kRxDPrefetch: RDH=%x RDT=%x RDLEN=%x "
+ "iRxDCurrent=%x nRxDFetched=%x\n",
+ pThis->szPrf, pRxdc->rdh, pRxdc->rdt, pRxdc->rdlen, pThis->iRxDCurrent, pThis->nRxDFetched));
+ /* We've already loaded pThis->nRxDFetched descriptors past RDH. */
+ unsigned nDescsAvailable = e1kGetRxLen(pRxdc) - e1kRxDInCache(pThis);
+ unsigned nDescsToFetch = RT_MIN(nDescsAvailable, E1K_RXD_CACHE_SIZE - pThis->nRxDFetched);
+ unsigned nDescsTotal = pRxdc->rdlen / sizeof(E1KRXDESC);
+ Assert(nDescsTotal != 0);
+ if (nDescsTotal == 0)
+ return 0;
+ unsigned nFirstNotLoaded = (pRxdc->rdh + e1kRxDInCache(pThis)) % nDescsTotal;
+ unsigned nDescsInSingleRead = RT_MIN(nDescsToFetch, nDescsTotal - nFirstNotLoaded);
+ E1kLog3(("%s e1kRxDPrefetch: nDescsAvailable=%u nDescsToFetch=%u "
+ "nDescsTotal=%u nFirstNotLoaded=0x%x nDescsInSingleRead=%u\n",
+ pThis->szPrf, nDescsAvailable, nDescsToFetch, nDescsTotal,
+ nFirstNotLoaded, nDescsInSingleRead));
+ if (nDescsToFetch == 0)
+ return 0;
+ E1KRXDESC* pFirstEmptyDesc = &pThis->aRxDescriptors[pThis->nRxDFetched];
+ PDMDevHlpPCIPhysRead(pDevIns,
+ ((uint64_t)RDBAH << 32) + RDBAL + nFirstNotLoaded * sizeof(E1KRXDESC),
+ pFirstEmptyDesc, nDescsInSingleRead * sizeof(E1KRXDESC));
+ // uint64_t addrBase = ((uint64_t)RDBAH << 32) + RDBAL;
+ // unsigned i, j;
+ // for (i = pThis->nRxDFetched; i < pThis->nRxDFetched + nDescsInSingleRead; ++i)
+ // {
+ // pThis->aRxDescAddr[i] = addrBase + (nFirstNotLoaded + i - pThis->nRxDFetched) * sizeof(E1KRXDESC);
+ // E1kLog3(("%s aRxDescAddr[%d] = %p\n", pThis->szPrf, i, pThis->aRxDescAddr[i]));
+ // }
+ E1kLog3(("%s Fetched %u RX descriptors at %08x%08x(0x%x), RDLEN=%08x, RDH=%08x, RDT=%08x\n",
+ pThis->szPrf, nDescsInSingleRead,
+ RDBAH, RDBAL + pRxdc->rdh * sizeof(E1KRXDESC),
+ nFirstNotLoaded, pRxdc->rdlen, pRxdc->rdh, pRxdc->rdt));
+ if (nDescsToFetch > nDescsInSingleRead)
+ {
+ PDMDevHlpPCIPhysRead(pDevIns,
+ ((uint64_t)RDBAH << 32) + RDBAL,
+ pFirstEmptyDesc + nDescsInSingleRead,
+ (nDescsToFetch - nDescsInSingleRead) * sizeof(E1KRXDESC));
+ // Assert(i == pThis->nRxDFetched + nDescsInSingleRead);
+ // for (j = 0; i < pThis->nRxDFetched + nDescsToFetch; ++i, ++j)
+ // {
+ // pThis->aRxDescAddr[i] = addrBase + j * sizeof(E1KRXDESC);
+ // E1kLog3(("%s aRxDescAddr[%d] = %p\n", pThis->szPrf, i, pThis->aRxDescAddr[i]));
+ // }
+ E1kLog3(("%s Fetched %u RX descriptors at %08x%08x\n",
+ pThis->szPrf, nDescsToFetch - nDescsInSingleRead,
+ RDBAH, RDBAL));
+ }
+ pThis->nRxDFetched += nDescsToFetch;
+ return nDescsToFetch;
+}
+
+# ifdef IN_RING3 /* currently only used in ring-3 due to stack space requirements of the caller */
+/**
+ * Dump receive descriptor to debug log.
+ *
+ * @param pThis The device state structure.
+ * @param pDesc Pointer to the descriptor.
+ * @thread E1000_RX
+ */
+static void e1kPrintRDesc(PE1KSTATE pThis, E1KRXDESC *pDesc)
+{
+ RT_NOREF2(pThis, pDesc);
+ E1kLog2(("%s <-- Receive Descriptor (%d bytes):\n", pThis->szPrf, pDesc->u16Length));
+ E1kLog2((" Address=%16LX Length=%04X Csum=%04X\n",
+ pDesc->u64BufAddr, pDesc->u16Length, pDesc->u16Checksum));
+ E1kLog2((" STA: %s %s %s %s %s %s %s ERR: %s %s %s %s SPECIAL: %s VLAN=%03x PRI=%x\n",
+ pDesc->status.fPIF ? "PIF" : "pif",
+ pDesc->status.fIPCS ? "IPCS" : "ipcs",
+ pDesc->status.fTCPCS ? "TCPCS" : "tcpcs",
+ pDesc->status.fVP ? "VP" : "vp",
+ pDesc->status.fIXSM ? "IXSM" : "ixsm",
+ pDesc->status.fEOP ? "EOP" : "eop",
+ pDesc->status.fDD ? "DD" : "dd",
+ pDesc->status.fRXE ? "RXE" : "rxe",
+ pDesc->status.fIPE ? "IPE" : "ipe",
+ pDesc->status.fTCPE ? "TCPE" : "tcpe",
+ pDesc->status.fCE ? "CE" : "ce",
+ E1K_SPEC_CFI(pDesc->status.u16Special) ? "CFI" :"cfi",
+ E1K_SPEC_VLAN(pDesc->status.u16Special),
+ E1K_SPEC_PRI(pDesc->status.u16Special)));
+}
+# endif /* IN_RING3 */
+#endif /* E1K_WITH_RXD_CACHE */
+
+/**
+ * Dump transmit descriptor to debug log.
+ *
+ * @param pThis The device state structure.
+ * @param pDesc Pointer to descriptor union.
+ * @param pszDir A string denoting direction of descriptor transfer
+ * @thread E1000_TX
+ */
+static void e1kPrintTDesc(PE1KSTATE pThis, E1KTXDESC *pDesc, const char *pszDir,
+ unsigned uLevel = RTLOGGRPFLAGS_LEVEL_2)
+{
+ RT_NOREF4(pThis, pDesc, pszDir, uLevel);
+
+ /*
+ * Unfortunately we cannot use our format handler here, we want R0 logging
+ * as well.
+ */
+ switch (e1kGetDescType(pDesc))
+ {
+ case E1K_DTYP_CONTEXT:
+ E1kLogX(uLevel, ("%s %s Context Transmit Descriptor %s\n",
+ pThis->szPrf, pszDir, pszDir));
+ E1kLogX(uLevel, (" IPCSS=%02X IPCSO=%02X IPCSE=%04X TUCSS=%02X TUCSO=%02X TUCSE=%04X\n",
+ pDesc->context.ip.u8CSS, pDesc->context.ip.u8CSO, pDesc->context.ip.u16CSE,
+ pDesc->context.tu.u8CSS, pDesc->context.tu.u8CSO, pDesc->context.tu.u16CSE));
+ E1kLogX(uLevel, (" TUCMD:%s%s%s %s %s PAYLEN=%04x HDRLEN=%04x MSS=%04x STA: %s\n",
+ pDesc->context.dw2.fIDE ? " IDE":"",
+ pDesc->context.dw2.fRS ? " RS" :"",
+ pDesc->context.dw2.fTSE ? " TSE":"",
+ pDesc->context.dw2.fIP ? "IPv4":"IPv6",
+ pDesc->context.dw2.fTCP ? "TCP":"UDP",
+ pDesc->context.dw2.u20PAYLEN,
+ pDesc->context.dw3.u8HDRLEN,
+ pDesc->context.dw3.u16MSS,
+ pDesc->context.dw3.fDD?"DD":""));
+ break;
+ case E1K_DTYP_DATA:
+ E1kLogX(uLevel, ("%s %s Data Transmit Descriptor (%d bytes) %s\n",
+ pThis->szPrf, pszDir, pDesc->data.cmd.u20DTALEN, pszDir));
+ E1kLogX(uLevel, (" Address=%16LX DTALEN=%05X\n",
+ pDesc->data.u64BufAddr,
+ pDesc->data.cmd.u20DTALEN));
+ E1kLogX(uLevel, (" DCMD:%s%s%s%s%s%s%s STA:%s%s%s POPTS:%s%s SPECIAL:%s VLAN=%03x PRI=%x\n",
+ pDesc->data.cmd.fIDE ? " IDE" :"",
+ pDesc->data.cmd.fVLE ? " VLE" :"",
+ pDesc->data.cmd.fRPS ? " RPS" :"",
+ pDesc->data.cmd.fRS ? " RS" :"",
+ pDesc->data.cmd.fTSE ? " TSE" :"",
+ pDesc->data.cmd.fIFCS? " IFCS":"",
+ pDesc->data.cmd.fEOP ? " EOP" :"",
+ pDesc->data.dw3.fDD ? " DD" :"",
+ pDesc->data.dw3.fEC ? " EC" :"",
+ pDesc->data.dw3.fLC ? " LC" :"",
+ pDesc->data.dw3.fTXSM? " TXSM":"",
+ pDesc->data.dw3.fIXSM? " IXSM":"",
+ E1K_SPEC_CFI(pDesc->data.dw3.u16Special) ? "CFI" :"cfi",
+ E1K_SPEC_VLAN(pDesc->data.dw3.u16Special),
+ E1K_SPEC_PRI(pDesc->data.dw3.u16Special)));
+ break;
+ case E1K_DTYP_LEGACY:
+ E1kLogX(uLevel, ("%s %s Legacy Transmit Descriptor (%d bytes) %s\n",
+ pThis->szPrf, pszDir, pDesc->legacy.cmd.u16Length, pszDir));
+ E1kLogX(uLevel, (" Address=%16LX DTALEN=%05X\n",
+ pDesc->data.u64BufAddr,
+ pDesc->legacy.cmd.u16Length));
+ E1kLogX(uLevel, (" CMD:%s%s%s%s%s%s%s STA:%s%s%s CSO=%02x CSS=%02x SPECIAL:%s VLAN=%03x PRI=%x\n",
+ pDesc->legacy.cmd.fIDE ? " IDE" :"",
+ pDesc->legacy.cmd.fVLE ? " VLE" :"",
+ pDesc->legacy.cmd.fRPS ? " RPS" :"",
+ pDesc->legacy.cmd.fRS ? " RS" :"",
+ pDesc->legacy.cmd.fIC ? " IC" :"",
+ pDesc->legacy.cmd.fIFCS? " IFCS":"",
+ pDesc->legacy.cmd.fEOP ? " EOP" :"",
+ pDesc->legacy.dw3.fDD ? " DD" :"",
+ pDesc->legacy.dw3.fEC ? " EC" :"",
+ pDesc->legacy.dw3.fLC ? " LC" :"",
+ pDesc->legacy.cmd.u8CSO,
+ pDesc->legacy.dw3.u8CSS,
+ E1K_SPEC_CFI(pDesc->legacy.dw3.u16Special) ? "CFI" :"cfi",
+ E1K_SPEC_VLAN(pDesc->legacy.dw3.u16Special),
+ E1K_SPEC_PRI(pDesc->legacy.dw3.u16Special)));
+ break;
+ default:
+ E1kLog(("%s %s Invalid Transmit Descriptor %s\n",
+ pThis->szPrf, pszDir, pszDir));
+ break;
+ }
+}
+
+/**
+ * Raise an interrupt later.
+ *
+ * @param pThis The device state structure.
+ */
+DECLINLINE(void) e1kPostponeInterrupt(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint64_t nsDeadline)
+{
+ if (!PDMDevHlpTimerIsActive(pDevIns, pThis->hIntTimer))
+ PDMDevHlpTimerSetNano(pDevIns, pThis->hIntTimer, nsDeadline);
+}
+
+/**
+ * Raise interrupt if not masked.
+ *
+ * @param pThis The device state structure.
+ */
+static int e1kRaiseInterrupt(PPDMDEVINS pDevIns, PE1KSTATE pThis, int rcBusy, uint32_t u32IntCause)
+{
+ /* Do NOT use e1kCsEnterReturn here as most callers doesn't check the
+ status code. They'll pass a negative rcBusy. */
+ int rc = e1kCsEnter(pThis, rcBusy);
+ if (RT_LIKELY(rc == VINF_SUCCESS))
+ { /* likely */ }
+ else
+ {
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->cs, rc);
+ return rc;
+ }
+
+ E1K_INC_ISTAT_CNT(pThis->uStatIntTry);
+ ICR |= u32IntCause;
+ if (ICR & IMS)
+ {
+ if (pThis->fIntRaised)
+ {
+ E1K_INC_ISTAT_CNT(pThis->uStatIntSkip);
+ E1kLog2(("%s e1kRaiseInterrupt: Already raised, skipped. ICR&IMS=%08x\n",
+ pThis->szPrf, ICR & IMS));
+ }
+ else
+ {
+ uint64_t tsNow = PDMDevHlpTimerGet(pDevIns, pThis->hIntTimer);
+ if (!!ITR && tsNow - pThis->u64AckedAt < ITR * 256
+ && pThis->fItrEnabled && (pThis->fItrRxEnabled || !(ICR & ICR_RXT0)))
+ {
+ E1K_INC_ISTAT_CNT(pThis->uStatIntEarly);
+ E1kLog2(("%s e1kRaiseInterrupt: Too early to raise again: %d ns < %d ns.\n",
+ pThis->szPrf, (uint32_t)(tsNow - pThis->u64AckedAt), ITR * 256));
+ e1kPostponeInterrupt(pDevIns, pThis, ITR * 256);
+ }
+ else
+ {
+
+ /* Since we are delivering the interrupt now
+ * there is no need to do it later -- stop the timer.
+ */
+ PDMDevHlpTimerStop(pDevIns, pThis->hIntTimer);
+ E1K_INC_ISTAT_CNT(pThis->uStatInt);
+ STAM_COUNTER_INC(&pThis->StatIntsRaised);
+ /* Got at least one unmasked interrupt cause */
+ pThis->fIntRaised = true;
+ /* Raise(1) INTA(0) */
+ E1kLogRel(("E1000: irq RAISED icr&mask=0x%x, icr=0x%x\n", ICR & IMS, ICR));
+ PDMDevHlpPCISetIrq(pDevIns, 0, 1);
+ E1kLog(("%s e1kRaiseInterrupt: Raised. ICR&IMS=%08x\n",
+ pThis->szPrf, ICR & IMS));
+ }
+ }
+ }
+ else
+ {
+ E1K_INC_ISTAT_CNT(pThis->uStatIntMasked);
+ E1kLog2(("%s e1kRaiseInterrupt: Not raising, ICR=%08x, IMS=%08x\n",
+ pThis->szPrf, ICR, IMS));
+ }
+ e1kCsLeave(pThis);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Compute the physical address of the descriptor.
+ *
+ * @returns the physical address of the descriptor.
+ *
+ * @param baseHigh High-order 32 bits of descriptor table address.
+ * @param baseLow Low-order 32 bits of descriptor table address.
+ * @param idxDesc The descriptor index in the table.
+ */
+DECLINLINE(RTGCPHYS) e1kDescAddr(uint32_t baseHigh, uint32_t baseLow, uint32_t idxDesc)
+{
+ AssertCompile(sizeof(E1KRXDESC) == sizeof(E1KTXDESC));
+ return ((uint64_t)baseHigh << 32) + baseLow + idxDesc * sizeof(E1KRXDESC);
+}
+
+#ifdef IN_RING3 /* currently only used in ring-3 due to stack space requirements of the caller */
+/**
+ * Advance the head pointer of the receive descriptor queue.
+ *
+ * @remarks RDH always points to the next available RX descriptor.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ */
+DECLINLINE(void) e1kAdvanceRDH(PPDMDEVINS pDevIns, PE1KSTATE pThis, PE1KRXDC pRxdc)
+{
+ Assert(e1kCsRxIsOwner(pThis));
+ //e1kR3CsEnterAsserted(pThis);
+ if (++pRxdc->rdh * sizeof(E1KRXDESC) >= pRxdc->rdlen)
+ pRxdc->rdh = 0;
+ RDH = pRxdc->rdh; /* Sync the actual register and RXDC */
+#ifdef E1K_WITH_RXD_CACHE
+ /*
+ * We need to fetch descriptors now as the guest may advance RDT all the way
+ * to RDH as soon as we generate RXDMT0 interrupt. This is mostly to provide
+ * compatibility with Phar Lap ETS, see @bugref(7346). Note that we do not
+ * check if the receiver is enabled. It must be, otherwise we won't get here
+ * in the first place.
+ *
+ * Note that we should have moved both RDH and iRxDCurrent by now.
+ */
+ if (e1kRxDIsCacheEmpty(pThis))
+ {
+ /* Cache is empty, reset it and check if we can fetch more. */
+ pThis->iRxDCurrent = pThis->nRxDFetched = 0;
+ E1kLog3(("%s e1kAdvanceRDH: Rx cache is empty, RDH=%x RDT=%x "
+ "iRxDCurrent=%x nRxDFetched=%x\n",
+ pThis->szPrf, pRxdc->rdh, pRxdc->rdt, pThis->iRxDCurrent, pThis->nRxDFetched));
+ e1kRxDPrefetch(pDevIns, pThis, pRxdc);
+ }
+#endif /* E1K_WITH_RXD_CACHE */
+ /*
+ * Compute current receive queue length and fire RXDMT0 interrupt
+ * if we are low on receive buffers
+ */
+ uint32_t uRQueueLen = pRxdc->rdh>pRxdc->rdt ? pRxdc->rdlen/sizeof(E1KRXDESC)-pRxdc->rdh+pRxdc->rdt : pRxdc->rdt-pRxdc->rdh;
+ /*
+ * The minimum threshold is controlled by RDMTS bits of RCTL:
+ * 00 = 1/2 of RDLEN
+ * 01 = 1/4 of RDLEN
+ * 10 = 1/8 of RDLEN
+ * 11 = reserved
+ */
+ uint32_t uMinRQThreshold = pRxdc->rdlen / sizeof(E1KRXDESC) / (2 << GET_BITS(RCTL, RDMTS));
+ if (uRQueueLen <= uMinRQThreshold)
+ {
+ E1kLogRel(("E1000: low on RX descriptors, RDH=%x RDT=%x len=%x threshold=%x\n", pRxdc->rdh, pRxdc->rdt, uRQueueLen, uMinRQThreshold));
+ E1kLog2(("%s Low on RX descriptors, RDH=%x RDT=%x len=%x threshold=%x, raise an interrupt\n",
+ pThis->szPrf, pRxdc->rdh, pRxdc->rdt, uRQueueLen, uMinRQThreshold));
+ E1K_INC_ISTAT_CNT(pThis->uStatIntRXDMT0);
+ e1kRaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, ICR_RXDMT0);
+ }
+ E1kLog2(("%s e1kAdvanceRDH: at exit RDH=%x RDT=%x len=%x\n",
+ pThis->szPrf, pRxdc->rdh, pRxdc->rdt, uRQueueLen));
+ //e1kCsLeave(pThis);
+}
+#endif /* IN_RING3 */
+
+#ifdef E1K_WITH_RXD_CACHE
+
+# ifdef IN_RING3 /* currently only used in ring-3 due to stack space requirements of the caller */
+
+/**
+ * Obtain the next RX descriptor from RXD cache, fetching descriptors from the
+ * RX ring if the cache is empty.
+ *
+ * Note that we cannot advance the cache pointer (iRxDCurrent) yet as it will
+ * go out of sync with RDH which will cause trouble when EMT checks if the
+ * cache is empty to do pre-fetch @bugref(6217).
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @thread RX
+ */
+DECLINLINE(E1KRXDESC *) e1kRxDGet(PPDMDEVINS pDevIns, PE1KSTATE pThis, PE1KRXDC pRxdc)
+{
+ Assert(e1kCsRxIsOwner(pThis));
+ /* Check the cache first. */
+ if (pThis->iRxDCurrent < pThis->nRxDFetched)
+ return &pThis->aRxDescriptors[pThis->iRxDCurrent];
+ /* Cache is empty, reset it and check if we can fetch more. */
+ pThis->iRxDCurrent = pThis->nRxDFetched = 0;
+ if (e1kRxDPrefetch(pDevIns, pThis, pRxdc))
+ return &pThis->aRxDescriptors[pThis->iRxDCurrent];
+ /* Out of Rx descriptors. */
+ return NULL;
+}
+
+
+/**
+ * Return the RX descriptor obtained with e1kRxDGet() and advance the cache
+ * pointer. The descriptor gets written back to the RXD ring.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param pDesc The descriptor being "returned" to the RX ring.
+ * @thread RX
+ */
+DECLINLINE(void) e1kRxDPut(PPDMDEVINS pDevIns, PE1KSTATE pThis, E1KRXDESC* pDesc, PE1KRXDC pRxdc)
+{
+ Assert(e1kCsRxIsOwner(pThis));
+ pThis->iRxDCurrent++;
+ // Assert(pDesc >= pThis->aRxDescriptors);
+ // Assert(pDesc < pThis->aRxDescriptors + E1K_RXD_CACHE_SIZE);
+ // uint64_t addr = e1kDescAddr(RDBAH, RDBAL, RDH);
+ // uint32_t rdh = RDH;
+ // Assert(pThis->aRxDescAddr[pDesc - pThis->aRxDescriptors] == addr);
+ PDMDevHlpPCIPhysWrite(pDevIns, e1kDescAddr(RDBAH, RDBAL, pRxdc->rdh), pDesc, sizeof(E1KRXDESC));
+ /*
+ * We need to print the descriptor before advancing RDH as it may fetch new
+ * descriptors into the cache.
+ */
+ e1kPrintRDesc(pThis, pDesc);
+ e1kAdvanceRDH(pDevIns, pThis, pRxdc);
+}
+
+/**
+ * Store a fragment of received packet at the specifed address.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param pDesc The next available RX descriptor.
+ * @param pvBuf The fragment.
+ * @param cb The size of the fragment.
+ */
+static void e1kStoreRxFragment(PPDMDEVINS pDevIns, PE1KSTATE pThis, E1KRXDESC *pDesc, const void *pvBuf, size_t cb)
+{
+ STAM_PROFILE_ADV_START(&pThis->StatReceiveStore, a);
+ E1kLog2(("%s e1kStoreRxFragment: store fragment of %04X at %016LX, EOP=%d\n",
+ pThis->szPrf, cb, pDesc->u64BufAddr, pDesc->status.fEOP));
+ PDMDevHlpPCIPhysWrite(pDevIns, pDesc->u64BufAddr, pvBuf, cb);
+ pDesc->u16Length = (uint16_t)cb;
+ Assert(pDesc->u16Length == cb);
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceiveStore, a);
+ RT_NOREF(pThis);
+}
+
+# endif /* IN_RING3 */
+
+#else /* !E1K_WITH_RXD_CACHE */
+
+/**
+ * Store a fragment of received packet that fits into the next available RX
+ * buffer.
+ *
+ * @remarks Trigger the RXT0 interrupt if it is the last fragment of the packet.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param pDesc The next available RX descriptor.
+ * @param pvBuf The fragment.
+ * @param cb The size of the fragment.
+ */
+static void e1kStoreRxFragment(PPDMDEVINS pDevIns, PE1KSTATE pThis, E1KRXDESC *pDesc, const void *pvBuf, size_t cb)
+{
+ STAM_PROFILE_ADV_START(&pThis->StatReceiveStore, a);
+ E1kLog2(("%s e1kStoreRxFragment: store fragment of %04X at %016LX, EOP=%d\n", pThis->szPrf, cb, pDesc->u64BufAddr, pDesc->status.fEOP));
+ PDMDevHlpPCIPhysWrite(pDevIns, pDesc->u64BufAddr, pvBuf, cb);
+ pDesc->u16Length = (uint16_t)cb; Assert(pDesc->u16Length == cb);
+ /* Write back the descriptor */
+ PDMDevHlpPCIPhysWrite(pDevIns, e1kDescAddr(RDBAH, RDBAL, RDH), pDesc, sizeof(E1KRXDESC));
+ e1kPrintRDesc(pThis, pDesc);
+ E1kLogRel(("E1000: Wrote back RX desc, RDH=%x\n", RDH));
+ /* Advance head */
+ e1kAdvanceRDH(pDevIns, pThis);
+ //E1kLog2(("%s e1kStoreRxFragment: EOP=%d RDTR=%08X RADV=%08X\n", pThis->szPrf, pDesc->fEOP, RDTR, RADV));
+ if (pDesc->status.fEOP)
+ {
+ /* Complete packet has been stored -- it is time to let the guest know. */
+#ifdef E1K_USE_RX_TIMERS
+ if (RDTR)
+ {
+ /* Arm the timer to fire in RDTR usec (discard .024) */
+ e1kArmTimer(pDevIns, pThis, pThis->hRIDTimer, RDTR);
+ /* If absolute timer delay is enabled and the timer is not running yet, arm it. */
+ if (RADV != 0 && !PDMDevHlpTimerIsActive(pDevIns, pThis->CTX_SUFF(pRADTimer)))
+ e1kArmTimer(pThis, pThis->hRADTimer, RADV);
+ }
+ else
+ {
+#endif
+ /* 0 delay means immediate interrupt */
+ E1K_INC_ISTAT_CNT(pThis->uStatIntRx);
+ e1kRaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, ICR_RXT0);
+#ifdef E1K_USE_RX_TIMERS
+ }
+#endif
+ }
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceiveStore, a);
+}
+
+#endif /* !E1K_WITH_RXD_CACHE */
+
+/**
+ * Returns true if it is a broadcast packet.
+ *
+ * @returns true if destination address indicates broadcast.
+ * @param pvBuf The ethernet packet.
+ */
+DECLINLINE(bool) e1kIsBroadcast(const void *pvBuf)
+{
+ static const uint8_t s_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+ return memcmp(pvBuf, s_abBcastAddr, sizeof(s_abBcastAddr)) == 0;
+}
+
+/**
+ * Returns true if it is a multicast packet.
+ *
+ * @remarks returns true for broadcast packets as well.
+ * @returns true if destination address indicates multicast.
+ * @param pvBuf The ethernet packet.
+ */
+DECLINLINE(bool) e1kIsMulticast(const void *pvBuf)
+{
+ return (*(char*)pvBuf) & 1;
+}
+
+#ifdef IN_RING3 /* currently only used in ring-3 due to stack space requirements of the caller */
+/**
+ * Set IXSM, IPCS and TCPCS flags according to the packet type.
+ *
+ * @remarks We emulate checksum offloading for major packets types only.
+ *
+ * @returns VBox status code.
+ * @param pThis The device state structure.
+ * @param pFrame The available data.
+ * @param cb Number of bytes available in the buffer.
+ * @param status Bit fields containing status info.
+ */
+static int e1kRxChecksumOffload(PE1KSTATE pThis, const uint8_t *pFrame, size_t cb, E1KRXDST *pStatus)
+{
+ /** @todo
+ * It is not safe to bypass checksum verification for packets coming
+ * from real wire. We currently unable to tell where packets are
+ * coming from so we tell the driver to ignore our checksum flags
+ * and do verification in software.
+ */
+# if 0
+ uint16_t uEtherType = ntohs(*(uint16_t*)(pFrame + 12));
+
+ E1kLog2(("%s e1kRxChecksumOffload: EtherType=%x\n", pThis->szPrf, uEtherType));
+
+ switch (uEtherType)
+ {
+ case 0x800: /* IPv4 */
+ {
+ pStatus->fIXSM = false;
+ pStatus->fIPCS = true;
+ PRTNETIPV4 pIpHdr4 = (PRTNETIPV4)(pFrame + 14);
+ /* TCP/UDP checksum offloading works with TCP and UDP only */
+ pStatus->fTCPCS = pIpHdr4->ip_p == 6 || pIpHdr4->ip_p == 17;
+ break;
+ }
+ case 0x86DD: /* IPv6 */
+ pStatus->fIXSM = false;
+ pStatus->fIPCS = false;
+ pStatus->fTCPCS = true;
+ break;
+ default: /* ARP, VLAN, etc. */
+ pStatus->fIXSM = true;
+ break;
+ }
+# else
+ pStatus->fIXSM = true;
+ RT_NOREF_PV(pThis); RT_NOREF_PV(pFrame); RT_NOREF_PV(cb);
+# endif
+ return VINF_SUCCESS;
+}
+#endif /* IN_RING3 */
+
+/**
+ * Pad and store received packet.
+ *
+ * @remarks Make sure that the packet appears to upper layer as one coming
+ * from real Ethernet: pad it and insert FCS.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param pvBuf The available data.
+ * @param cb Number of bytes available in the buffer.
+ * @param status Bit fields containing status info.
+ */
+static int e1kHandleRxPacket(PPDMDEVINS pDevIns, PE1KSTATE pThis, const void *pvBuf, size_t cb, E1KRXDST status)
+{
+#if defined(IN_RING3) /** @todo Remove this extra copying, it's gonna make us run out of kernel / hypervisor stack! */
+ uint8_t rxPacket[E1K_MAX_RX_PKT_SIZE];
+ uint8_t *ptr = rxPacket;
+# ifdef E1K_WITH_RXD_CACHE
+ E1KRXDC rxdc;
+# endif /* E1K_WITH_RXD_CACHE */
+
+ e1kCsRxEnterReturn(pThis);
+# ifdef E1K_WITH_RXD_CACHE
+ if (RT_UNLIKELY(!e1kUpdateRxDContext(pDevIns, pThis, &rxdc, "e1kHandleRxPacket")))
+ {
+ e1kCsRxLeave(pThis);
+ E1kLog(("%s e1kHandleRxPacket: failed to update Rx context, returning VINF_SUCCESS\n", pThis->szPrf));
+ return VINF_SUCCESS;
+ }
+# endif /* E1K_WITH_RXD_CACHE */
+
+ if (cb > 70) /* unqualified guess */
+ pThis->led.Asserted.s.fReading = pThis->led.Actual.s.fReading = 1;
+
+ Assert(cb <= E1K_MAX_RX_PKT_SIZE);
+ Assert(cb > 16);
+ size_t cbMax = ((RCTL & RCTL_LPE) ? E1K_MAX_RX_PKT_SIZE - 4 : 1518) - (status.fVP ? 0 : 4);
+ E1kLog3(("%s Max RX packet size is %u\n", pThis->szPrf, cbMax));
+ if (status.fVP)
+ {
+ /* VLAN packet -- strip VLAN tag in VLAN mode */
+ if ((CTRL & CTRL_VME) && cb > 16)
+ {
+ uint16_t *u16Ptr = (uint16_t*)pvBuf;
+ memcpy(rxPacket, pvBuf, 12); /* Copy src and dst addresses */
+ status.u16Special = RT_BE2H_U16(u16Ptr[7]); /* Extract VLAN tag */
+ memcpy(rxPacket + 12, (uint8_t*)pvBuf + 16, cb - 16); /* Copy the rest of the packet */
+ cb -= 4;
+ E1kLog3(("%s Stripped tag for VLAN %u (cb=%u)\n",
+ pThis->szPrf, status.u16Special, cb));
+ }
+ else
+ {
+ status.fVP = false; /* Set VP only if we stripped the tag */
+ memcpy(rxPacket, pvBuf, cb);
+ }
+ }
+ else
+ memcpy(rxPacket, pvBuf, cb);
+ /* Pad short packets */
+ if (cb < 60)
+ {
+ memset(rxPacket + cb, 0, 60 - cb);
+ cb = 60;
+ }
+ if (!(RCTL & RCTL_SECRC) && cb <= cbMax)
+ {
+ STAM_PROFILE_ADV_START(&pThis->StatReceiveCRC, a);
+ /*
+ * Add FCS if CRC stripping is not enabled. Since the value of CRC
+ * is ignored by most of drivers we may as well save us the trouble
+ * of calculating it (see EthernetCRC CFGM parameter).
+ */
+ if (pThis->fEthernetCRC)
+ *(uint32_t*)(rxPacket + cb) = RTCrc32(rxPacket, cb);
+ cb += sizeof(uint32_t);
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceiveCRC, a);
+ E1kLog3(("%s Added FCS (cb=%u)\n", pThis->szPrf, cb));
+ }
+ /* Compute checksum of complete packet */
+ size_t cbCSumStart = RT_MIN(GET_BITS(RXCSUM, PCSS), cb);
+ uint16_t checksum = e1kCSum16(rxPacket + cbCSumStart, cb - cbCSumStart);
+ e1kRxChecksumOffload(pThis, rxPacket, cb, &status);
+
+ /* Update stats */
+ E1K_INC_CNT32(GPRC);
+ if (e1kIsBroadcast(pvBuf))
+ E1K_INC_CNT32(BPRC);
+ else if (e1kIsMulticast(pvBuf))
+ E1K_INC_CNT32(MPRC);
+ /* Update octet receive counter */
+ E1K_ADD_CNT64(GORCL, GORCH, cb);
+ STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cb);
+ if (cb == 64)
+ E1K_INC_CNT32(PRC64);
+ else if (cb < 128)
+ E1K_INC_CNT32(PRC127);
+ else if (cb < 256)
+ E1K_INC_CNT32(PRC255);
+ else if (cb < 512)
+ E1K_INC_CNT32(PRC511);
+ else if (cb < 1024)
+ E1K_INC_CNT32(PRC1023);
+ else
+ E1K_INC_CNT32(PRC1522);
+
+ E1K_INC_ISTAT_CNT(pThis->uStatRxFrm);
+
+# ifdef E1K_WITH_RXD_CACHE
+ while (cb > 0)
+ {
+ E1KRXDESC *pDesc = e1kRxDGet(pDevIns, pThis, &rxdc);
+
+ if (pDesc == NULL)
+ {
+ E1kLog(("%s Out of receive buffers, dropping the packet "
+ "(cb=%u, in_cache=%u, RDH=%x RDT=%x)\n",
+ pThis->szPrf, cb, e1kRxDInCache(pThis), rxdc.rdh, rxdc.rdt));
+ break;
+ }
+# else /* !E1K_WITH_RXD_CACHE */
+ if (RDH == RDT)
+ {
+ E1kLog(("%s Out of receive buffers, dropping the packet\n",
+ pThis->szPrf));
+ }
+ /* Store the packet to receive buffers */
+ while (RDH != RDT)
+ {
+ /* Load the descriptor pointed by head */
+ E1KRXDESC desc, *pDesc = &desc;
+ PDMDevHlpPCIPhysRead(pDevIns, e1kDescAddr(RDBAH, RDBAL, RDH), &desc, sizeof(desc));
+# endif /* !E1K_WITH_RXD_CACHE */
+ if (pDesc->u64BufAddr)
+ {
+ uint16_t u16RxBufferSize = pThis->u16RxBSize; /* see @bugref{9427} */
+
+ /* Update descriptor */
+ pDesc->status = status;
+ pDesc->u16Checksum = checksum;
+ pDesc->status.fDD = true;
+
+ /*
+ * We need to leave Rx critical section here or we risk deadlocking
+ * with EMT in e1kRegWriteRDT when the write is to an unallocated
+ * page or has an access handler associated with it.
+ * Note that it is safe to leave the critical section here since
+ * e1kRegWriteRDT() never modifies RDH. It never touches already
+ * fetched RxD cache entries either.
+ */
+ if (cb > u16RxBufferSize)
+ {
+ pDesc->status.fEOP = false;
+ e1kCsRxLeave(pThis);
+ e1kStoreRxFragment(pDevIns, pThis, pDesc, ptr, u16RxBufferSize);
+ e1kCsRxEnterReturn(pThis);
+# ifdef E1K_WITH_RXD_CACHE
+ if (RT_UNLIKELY(!e1kUpdateRxDContext(pDevIns, pThis, &rxdc, "e1kHandleRxPacket")))
+ {
+ e1kCsRxLeave(pThis);
+ E1kLog(("%s e1kHandleRxPacket: failed to update Rx context, returning VINF_SUCCESS\n", pThis->szPrf));
+ return VINF_SUCCESS;
+ }
+# endif /* E1K_WITH_RXD_CACHE */
+ ptr += u16RxBufferSize;
+ cb -= u16RxBufferSize;
+ }
+ else
+ {
+ pDesc->status.fEOP = true;
+ e1kCsRxLeave(pThis);
+ e1kStoreRxFragment(pDevIns, pThis, pDesc, ptr, cb);
+# ifdef E1K_WITH_RXD_CACHE
+ e1kCsRxEnterReturn(pThis);
+ if (RT_UNLIKELY(!e1kUpdateRxDContext(pDevIns, pThis, &rxdc, "e1kHandleRxPacket")))
+ {
+ e1kCsRxLeave(pThis);
+ E1kLog(("%s e1kHandleRxPacket: failed to update Rx context, returning VINF_SUCCESS\n", pThis->szPrf));
+ return VINF_SUCCESS;
+ }
+ cb = 0;
+# else /* !E1K_WITH_RXD_CACHE */
+ pThis->led.Actual.s.fReading = 0;
+ return VINF_SUCCESS;
+# endif /* !E1K_WITH_RXD_CACHE */
+ }
+ /*
+ * Note: RDH is advanced by e1kStoreRxFragment if E1K_WITH_RXD_CACHE
+ * is not defined.
+ */
+ }
+# ifdef E1K_WITH_RXD_CACHE
+ /* Write back the descriptor. */
+ pDesc->status.fDD = true;
+ e1kRxDPut(pDevIns, pThis, pDesc, &rxdc);
+# else /* !E1K_WITH_RXD_CACHE */
+ else
+ {
+ /* Write back the descriptor. */
+ pDesc->status.fDD = true;
+ PDMDevHlpPCIPhysWrite(pDevIns, e1kDescAddr(RDBAH, RDBAL, RDH), pDesc, sizeof(E1KRXDESC));
+ e1kAdvanceRDH(pDevIns, pThis);
+ }
+# endif /* !E1K_WITH_RXD_CACHE */
+ }
+
+ if (cb > 0)
+ E1kLog(("%s Out of receive buffers, dropping %u bytes", pThis->szPrf, cb));
+
+ pThis->led.Actual.s.fReading = 0;
+
+ e1kCsRxLeave(pThis);
+# ifdef E1K_WITH_RXD_CACHE
+ /* Complete packet has been stored -- it is time to let the guest know. */
+# ifdef E1K_USE_RX_TIMERS
+ if (RDTR)
+ {
+ /* Arm the timer to fire in RDTR usec (discard .024) */
+ e1kArmTimer(pThis, pThis->hRIDTimer, RDTR);
+ /* If absolute timer delay is enabled and the timer is not running yet, arm it. */
+ if (RADV != 0 && !PDMDevHlpTimerIsActive(pDevIns, pThis->hRADTimer))
+ e1kArmTimer(pThis, pThis->hRADTimer, RADV);
+ }
+ else
+ {
+# endif /* E1K_USE_RX_TIMERS */
+ /* 0 delay means immediate interrupt */
+ E1K_INC_ISTAT_CNT(pThis->uStatIntRx);
+ e1kRaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, ICR_RXT0);
+# ifdef E1K_USE_RX_TIMERS
+ }
+# endif /* E1K_USE_RX_TIMERS */
+# endif /* E1K_WITH_RXD_CACHE */
+
+ return VINF_SUCCESS;
+#else /* !IN_RING3 */
+ RT_NOREF(pDevIns, pThis, pvBuf, cb, status);
+ return VERR_INTERNAL_ERROR_2;
+#endif /* !IN_RING3 */
+}
+
+
+#ifdef IN_RING3
+/**
+ * Bring the link up after the configured delay, 5 seconds by default.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @thread any
+ */
+DECLINLINE(void) e1kBringLinkUpDelayed(PPDMDEVINS pDevIns, PE1KSTATE pThis)
+{
+ E1kLog(("%s Will bring up the link in %d seconds...\n",
+ pThis->szPrf, pThis->cMsLinkUpDelay / 1000));
+ e1kArmTimer(pDevIns, pThis, pThis->hLUTimer, pThis->cMsLinkUpDelay * 1000);
+}
+
+/**
+ * Bring up the link immediately.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param pThisCC The current context instance data.
+ */
+DECLINLINE(void) e1kR3LinkUp(PPDMDEVINS pDevIns, PE1KSTATE pThis, PE1KSTATECC pThisCC)
+{
+ E1kLog(("%s Link is up\n", pThis->szPrf));
+ STATUS |= STATUS_LU;
+ Phy::setLinkStatus(&pThis->phy, true);
+ e1kRaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, ICR_LSC);
+ if (pThisCC->pDrvR3)
+ pThisCC->pDrvR3->pfnNotifyLinkChanged(pThisCC->pDrvR3, PDMNETWORKLINKSTATE_UP);
+ /* Trigger processing of pending TX descriptors (see @bugref{8942}). */
+ PDMDevHlpTaskTrigger(pDevIns, pThis->hTxTask);
+}
+
+/**
+ * Bring down the link immediately.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param pThisCC The current context instance data.
+ */
+DECLINLINE(void) e1kR3LinkDown(PPDMDEVINS pDevIns, PE1KSTATE pThis, PE1KSTATECC pThisCC)
+{
+ E1kLog(("%s Link is down\n", pThis->szPrf));
+ STATUS &= ~STATUS_LU;
+#ifdef E1K_LSC_ON_RESET
+ Phy::setLinkStatus(&pThis->phy, false);
+#endif /* E1K_LSC_ON_RESET */
+ e1kRaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, ICR_LSC);
+ if (pThisCC->pDrvR3)
+ pThisCC->pDrvR3->pfnNotifyLinkChanged(pThisCC->pDrvR3, PDMNETWORKLINKSTATE_DOWN);
+}
+
+/**
+ * Bring down the link temporarily.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param pThisCC The current context instance data.
+ */
+DECLINLINE(void) e1kR3LinkDownTemp(PPDMDEVINS pDevIns, PE1KSTATE pThis, PE1KSTATECC pThisCC)
+{
+ E1kLog(("%s Link is down temporarily\n", pThis->szPrf));
+ STATUS &= ~STATUS_LU;
+ Phy::setLinkStatus(&pThis->phy, false);
+ e1kRaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, ICR_LSC);
+ /*
+ * Notifying the associated driver that the link went down (even temporarily)
+ * seems to be the right thing, but it was not done before. This may cause
+ * a regression if the driver does not expect the link to go down as a result
+ * of sending PDMNETWORKLINKSTATE_DOWN_RESUME to this device. Earlier versions
+ * of code notified the driver that the link was up! See @bugref{7057}.
+ */
+ if (pThisCC->pDrvR3)
+ pThisCC->pDrvR3->pfnNotifyLinkChanged(pThisCC->pDrvR3, PDMNETWORKLINKSTATE_DOWN);
+ e1kBringLinkUpDelayed(pDevIns, pThis);
+}
+#endif /* IN_RING3 */
+
+#if 0 /* unused */
+/**
+ * Read handler for Device Status register.
+ *
+ * Get the link status from PHY.
+ *
+ * @returns VBox status code.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param mask Used to implement partial reads (8 and 16-bit).
+ */
+static int e1kRegReadCTRL(PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t *pu32Value)
+{
+ E1kLog(("%s e1kRegReadCTRL: mdio dir=%s mdc dir=%s mdc=%d\n",
+ pThis->szPrf, (CTRL & CTRL_MDIO_DIR)?"OUT":"IN ",
+ (CTRL & CTRL_MDC_DIR)?"OUT":"IN ", !!(CTRL & CTRL_MDC)));
+ if ((CTRL & CTRL_MDIO_DIR) == 0 && (CTRL & CTRL_MDC))
+ {
+ /* MDC is high and MDIO pin is used for input, read MDIO pin from PHY */
+ if (Phy::readMDIO(&pThis->phy))
+ *pu32Value = CTRL | CTRL_MDIO;
+ else
+ *pu32Value = CTRL & ~CTRL_MDIO;
+ E1kLog(("%s e1kRegReadCTRL: Phy::readMDIO(%d)\n",
+ pThis->szPrf, !!(*pu32Value & CTRL_MDIO)));
+ }
+ else
+ {
+ /* MDIO pin is used for output, ignore it */
+ *pu32Value = CTRL;
+ }
+ return VINF_SUCCESS;
+}
+#endif /* unused */
+
+/**
+ * A helper function to detect the link state to the other side of "the wire".
+ *
+ * When deciding to bring up the link we need to take into account both if the
+ * cable is connected and if our device is actually connected to the outside
+ * world. If no driver is attached we won't be able to allocate TX buffers,
+ * which will prevent us from TX descriptor processing, which will result in
+ * "TX unit hang" in the guest.
+ *
+ * @returns true if the device is connected to something.
+ *
+ * @param pDevIns The device instance.
+ */
+DECLINLINE(bool) e1kIsConnected(PPDMDEVINS pDevIns)
+{
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+ return pThis->fCableConnected && pThis->fIsAttached;
+}
+
+/**
+ * A callback used by PHY to indicate that the link needs to be updated due to
+ * reset of PHY.
+ *
+ * @param pDevIns The device instance.
+ * @thread any
+ */
+void e1kPhyLinkResetCallback(PPDMDEVINS pDevIns)
+{
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+
+ /* Make sure we have cable connected and MAC can talk to PHY */
+ if (e1kIsConnected(pDevIns) && (CTRL & CTRL_SLU))
+ e1kArmTimer(pDevIns, pThis, pThis->hLUTimer, E1K_INIT_LINKUP_DELAY_US);
+ else
+ Log(("%s PHY link reset callback ignored (cable %sconnected, driver %stached, CTRL_SLU=%u)\n", pThis->szPrf,
+ pThis->fCableConnected ? "" : "dis", pThis->fIsAttached ? "at" : "de", CTRL & CTRL_SLU ? 1 : 0));
+}
+
+/**
+ * Write handler for Device Control register.
+ *
+ * Handles reset.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @param mask Used to implement partial writes (8 and 16-bit).
+ * @thread EMT
+ */
+static int e1kRegWriteCTRL(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+ int rc = VINF_SUCCESS;
+
+ if (value & CTRL_RESET)
+ { /* RST */
+#ifndef IN_RING3
+ return VINF_IOM_R3_MMIO_WRITE;
+#else
+ e1kR3HardReset(pDevIns, pThis, PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC));
+#endif
+ }
+ else
+ {
+#ifdef E1K_LSC_ON_SLU
+ /*
+ * When the guest changes 'Set Link Up' bit from 0 to 1 we check if
+ * the link is down and the cable is connected, and if they are we
+ * bring the link up, see @bugref{8624}.
+ */
+ if ( (value & CTRL_SLU)
+ && !(CTRL & CTRL_SLU)
+ && pThis->fCableConnected
+ && !(STATUS & STATUS_LU))
+ {
+ /* It should take about 2 seconds for the link to come up */
+ e1kArmTimer(pDevIns, pThis, pThis->hLUTimer, E1K_INIT_LINKUP_DELAY_US);
+ }
+#else /* !E1K_LSC_ON_SLU */
+ if ( (value & CTRL_SLU)
+ && !(CTRL & CTRL_SLU)
+ && e1kIsConnected(pDevIns)
+ && !PDMDevHlpTimerIsActive(pDevIns, pThis->hLUTimer))
+ {
+ /* PXE does not use LSC interrupts, see @bugref{9113}. */
+ STATUS |= STATUS_LU;
+ }
+#endif /* !E1K_LSC_ON_SLU */
+ if ((value & CTRL_VME) != (CTRL & CTRL_VME))
+ {
+ E1kLog(("%s VLAN Mode %s\n", pThis->szPrf, (value & CTRL_VME) ? "Enabled" : "Disabled"));
+ }
+ Log7(("%s e1kRegWriteCTRL: mdio dir=%s mdc dir=%s mdc=%s mdio=%d\n",
+ pThis->szPrf, (value & CTRL_MDIO_DIR)?"OUT":"IN ",
+ (value & CTRL_MDC_DIR)?"OUT":"IN ", (value & CTRL_MDC)?"HIGH":"LOW ", !!(value & CTRL_MDIO)));
+ if (value & CTRL_MDC)
+ {
+ if (value & CTRL_MDIO_DIR)
+ {
+ Log7(("%s e1kRegWriteCTRL: Phy::writeMDIO(%d)\n", pThis->szPrf, !!(value & CTRL_MDIO)));
+ /* MDIO direction pin is set to output and MDC is high, write MDIO pin value to PHY */
+ Phy::writeMDIO(&pThis->phy, !!(value & CTRL_MDIO), pDevIns);
+ }
+ else
+ {
+ if (Phy::readMDIO(&pThis->phy))
+ value |= CTRL_MDIO;
+ else
+ value &= ~CTRL_MDIO;
+ Log7(("%s e1kRegWriteCTRL: Phy::readMDIO(%d)\n", pThis->szPrf, !!(value & CTRL_MDIO)));
+ }
+ }
+ rc = e1kRegWriteDefault(pDevIns, pThis, offset, index, value);
+ }
+
+ return rc;
+}
+
+/**
+ * Write handler for EEPROM/Flash Control/Data register.
+ *
+ * Handles EEPROM access requests; forwards writes to EEPROM device if access has been granted.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @param mask Used to implement partial writes (8 and 16-bit).
+ * @thread EMT
+ */
+static int e1kRegWriteEECD(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+ RT_NOREF(pDevIns, offset, index);
+#ifdef IN_RING3
+ /* So far we are concerned with lower byte only */
+ if ((EECD & EECD_EE_GNT) || pThis->eChip == E1K_CHIP_82543GC)
+ {
+ /* Access to EEPROM granted -- forward 4-wire bits to EEPROM device */
+ /* Note: 82543GC does not need to request EEPROM access */
+ STAM_PROFILE_ADV_START(&pThis->StatEEPROMWrite, a);
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+ pThisCC->eeprom.write(value & EECD_EE_WIRES);
+ STAM_PROFILE_ADV_STOP(&pThis->StatEEPROMWrite, a);
+ }
+ if (value & EECD_EE_REQ)
+ EECD |= EECD_EE_REQ|EECD_EE_GNT;
+ else
+ EECD &= ~EECD_EE_GNT;
+ //e1kRegWriteDefault(pThis, offset, index, value );
+
+ return VINF_SUCCESS;
+#else /* !IN_RING3 */
+ RT_NOREF(pThis, value);
+ return VINF_IOM_R3_MMIO_WRITE;
+#endif /* !IN_RING3 */
+}
+
+/**
+ * Read handler for EEPROM/Flash Control/Data register.
+ *
+ * Lower 4 bits come from EEPROM device if EEPROM access has been granted.
+ *
+ * @returns VBox status code.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param mask Used to implement partial reads (8 and 16-bit).
+ * @thread EMT
+ */
+static int e1kRegReadEECD(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t *pu32Value)
+{
+#ifdef IN_RING3
+ uint32_t value = 0; /* Get rid of false positive in parfait. */
+ int rc = e1kRegReadDefault(pDevIns, pThis, offset, index, &value);
+ if (RT_SUCCESS(rc))
+ {
+ if ((value & EECD_EE_GNT) || pThis->eChip == E1K_CHIP_82543GC)
+ {
+ /* Note: 82543GC does not need to request EEPROM access */
+ /* Access to EEPROM granted -- get 4-wire bits to EEPROM device */
+ STAM_PROFILE_ADV_START(&pThis->StatEEPROMRead, a);
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+ value |= pThisCC->eeprom.read();
+ STAM_PROFILE_ADV_STOP(&pThis->StatEEPROMRead, a);
+ }
+ *pu32Value = value;
+ }
+
+ return rc;
+#else /* !IN_RING3 */
+ RT_NOREF_PV(pDevIns); RT_NOREF_PV(pThis); RT_NOREF_PV(offset); RT_NOREF_PV(index); RT_NOREF_PV(pu32Value);
+ return VINF_IOM_R3_MMIO_READ;
+#endif /* !IN_RING3 */
+}
+
+/**
+ * Write handler for EEPROM Read register.
+ *
+ * Handles EEPROM word access requests, reads EEPROM and stores the result
+ * into DATA field.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @param mask Used to implement partial writes (8 and 16-bit).
+ * @thread EMT
+ */
+static int e1kRegWriteEERD(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+#ifdef IN_RING3
+ /* Make use of 'writable' and 'readable' masks. */
+ e1kRegWriteDefault(pDevIns, pThis, offset, index, value);
+ /* DONE and DATA are set only if read was triggered by START. */
+ if (value & EERD_START)
+ {
+ STAM_PROFILE_ADV_START(&pThis->StatEEPROMRead, a);
+ uint16_t tmp;
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+ if (pThisCC->eeprom.readWord(GET_BITS_V(value, EERD, ADDR), &tmp))
+ SET_BITS(EERD, DATA, tmp);
+ EERD |= EERD_DONE;
+ STAM_PROFILE_ADV_STOP(&pThis->StatEEPROMRead, a);
+ }
+
+ return VINF_SUCCESS;
+#else /* !IN_RING3 */
+ RT_NOREF_PV(pDevIns); RT_NOREF_PV(pThis); RT_NOREF_PV(offset); RT_NOREF_PV(index); RT_NOREF_PV(value);
+ return VINF_IOM_R3_MMIO_WRITE;
+#endif /* !IN_RING3 */
+}
+
+
+/**
+ * Write handler for MDI Control register.
+ *
+ * Handles PHY read/write requests; forwards requests to internal PHY device.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @param mask Used to implement partial writes (8 and 16-bit).
+ * @thread EMT
+ */
+static int e1kRegWriteMDIC(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+ if (value & MDIC_INT_EN)
+ {
+ E1kLog(("%s ERROR! Interrupt at the end of an MDI cycle is not supported yet.\n",
+ pThis->szPrf));
+ }
+ else if (value & MDIC_READY)
+ {
+ E1kLog(("%s ERROR! Ready bit is not reset by software during write operation.\n",
+ pThis->szPrf));
+ }
+ else if (GET_BITS_V(value, MDIC, PHY) != 1)
+ {
+ E1kLog(("%s WARNING! Access to invalid PHY detected, phy=%d.\n",
+ pThis->szPrf, GET_BITS_V(value, MDIC, PHY)));
+ /*
+ * Some drivers scan the MDIO bus for a PHY. We can work with these
+ * drivers if we set MDIC_READY and MDIC_ERROR when there isn't a PHY
+ * at the requested address, see @bugref{7346}.
+ */
+ MDIC = MDIC_READY | MDIC_ERROR;
+ }
+ else
+ {
+ /* Store the value */
+ e1kRegWriteDefault(pDevIns, pThis, offset, index, value);
+ STAM_COUNTER_INC(&pThis->StatPHYAccesses);
+ /* Forward op to PHY */
+ if (value & MDIC_OP_READ)
+ SET_BITS(MDIC, DATA, Phy::readRegister(&pThis->phy, GET_BITS_V(value, MDIC, REG), pDevIns));
+ else
+ Phy::writeRegister(&pThis->phy, GET_BITS_V(value, MDIC, REG), value & MDIC_DATA_MASK, pDevIns);
+ /* Let software know that we are done */
+ MDIC |= MDIC_READY;
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Write handler for Interrupt Cause Read register.
+ *
+ * Bits corresponding to 1s in 'value' will be cleared in ICR register.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @param mask Used to implement partial writes (8 and 16-bit).
+ * @thread EMT
+ */
+static int e1kRegWriteICR(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+ ICR &= ~value;
+
+ RT_NOREF_PV(pDevIns); RT_NOREF_PV(pThis); RT_NOREF_PV(offset); RT_NOREF_PV(index);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Read handler for Interrupt Cause Read register.
+ *
+ * Reading this register acknowledges all interrupts.
+ *
+ * @returns VBox status code.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param mask Not used.
+ * @thread EMT
+ */
+static int e1kRegReadICR(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t *pu32Value)
+{
+ e1kCsEnterReturn(pThis, VINF_IOM_R3_MMIO_READ);
+
+ uint32_t value = 0;
+ int rc = e1kRegReadDefault(pDevIns, pThis, offset, index, &value);
+ if (RT_SUCCESS(rc))
+ {
+ if (value)
+ {
+ if (!pThis->fIntRaised)
+ E1K_INC_ISTAT_CNT(pThis->uStatNoIntICR);
+ /*
+ * Not clearing ICR causes QNX to hang as it reads ICR in a loop
+ * with disabled interrupts.
+ */
+ //if (IMS)
+ if (1)
+ {
+ /*
+ * Interrupts were enabled -- we are supposedly at the very
+ * beginning of interrupt handler
+ */
+ E1kLogRel(("E1000: irq lowered, icr=0x%x\n", ICR));
+ E1kLog(("%s e1kRegReadICR: Lowered IRQ (%08x)\n", pThis->szPrf, ICR));
+ /* Clear all pending interrupts */
+ ICR = 0;
+ pThis->fIntRaised = false;
+ /* Lower(0) INTA(0) */
+ PDMDevHlpPCISetIrq(pDevIns, 0, 0);
+
+ pThis->u64AckedAt = PDMDevHlpTimerGet(pDevIns, pThis->hIntTimer);
+ if (pThis->fIntMaskUsed)
+ pThis->fDelayInts = true;
+ }
+ else
+ {
+ /*
+ * Interrupts are disabled -- in windows guests ICR read is done
+ * just before re-enabling interrupts
+ */
+ E1kLog(("%s e1kRegReadICR: Suppressing auto-clear due to disabled interrupts (%08x)\n", pThis->szPrf, ICR));
+ }
+ }
+ *pu32Value = value;
+ }
+ e1kCsLeave(pThis);
+
+ return rc;
+}
+
+/**
+ * Read handler for Interrupt Cause Set register.
+ *
+ * VxWorks driver uses this undocumented feature of real H/W to read ICR without acknowledging interrupts.
+ *
+ * @returns VBox status code.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param pu32Value Where to store the value of the register.
+ * @thread EMT
+ */
+static int e1kRegReadICS(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t *pu32Value)
+{
+ RT_NOREF_PV(index);
+ return e1kRegReadDefault(pDevIns, pThis, offset, ICR_IDX, pu32Value);
+}
+
+/**
+ * Write handler for Interrupt Cause Set register.
+ *
+ * Bits corresponding to 1s in 'value' will be set in ICR register.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @param mask Used to implement partial writes (8 and 16-bit).
+ * @thread EMT
+ */
+static int e1kRegWriteICS(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+ RT_NOREF_PV(offset); RT_NOREF_PV(index);
+ E1K_INC_ISTAT_CNT(pThis->uStatIntICS);
+ return e1kRaiseInterrupt(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE, value & g_aE1kRegMap[ICS_IDX].writable);
+}
+
+/**
+ * Write handler for Interrupt Mask Set register.
+ *
+ * Will trigger pending interrupts.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @param mask Used to implement partial writes (8 and 16-bit).
+ * @thread EMT
+ */
+static int e1kRegWriteIMS(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+ RT_NOREF_PV(offset); RT_NOREF_PV(index);
+
+ IMS |= value;
+ E1kLogRel(("E1000: irq enabled, RDH=%x RDT=%x TDH=%x TDT=%x\n", RDH, RDT, TDH, TDT));
+ E1kLog(("%s e1kRegWriteIMS: IRQ enabled\n", pThis->szPrf));
+ /*
+ * We cannot raise an interrupt here as it will occasionally cause an interrupt storm
+ * in Windows guests (see @bugref{8624}, @bugref{5023}).
+ */
+ if ((ICR & IMS) && !pThis->fLocked)
+ {
+ E1K_INC_ISTAT_CNT(pThis->uStatIntIMS);
+ e1kPostponeInterrupt(pDevIns, pThis, E1K_IMS_INT_DELAY_NS);
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Write handler for Interrupt Mask Clear register.
+ *
+ * Bits corresponding to 1s in 'value' will be cleared in IMS register.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @param mask Used to implement partial writes (8 and 16-bit).
+ * @thread EMT
+ */
+static int e1kRegWriteIMC(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+ RT_NOREF_PV(offset); RT_NOREF_PV(index);
+
+ e1kCsEnterReturn(pThis, VINF_IOM_R3_MMIO_WRITE);
+ if (pThis->fIntRaised)
+ {
+ /*
+ * Technically we should reset fIntRaised in ICR read handler, but it will cause
+ * Windows to freeze since it may receive an interrupt while still in the very beginning
+ * of interrupt handler.
+ */
+ E1K_INC_ISTAT_CNT(pThis->uStatIntLower);
+ STAM_COUNTER_INC(&pThis->StatIntsPrevented);
+ E1kLogRel(("E1000: irq lowered (IMC), icr=0x%x\n", ICR));
+ /* Lower(0) INTA(0) */
+ PDMDevHlpPCISetIrq(pDevIns, 0, 0);
+ pThis->fIntRaised = false;
+ E1kLog(("%s e1kRegWriteIMC: Lowered IRQ: ICR=%08x\n", pThis->szPrf, ICR));
+ }
+ IMS &= ~value;
+ E1kLog(("%s e1kRegWriteIMC: IRQ disabled\n", pThis->szPrf));
+ e1kCsLeave(pThis);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Write handler for Receive Control register.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @param mask Used to implement partial writes (8 and 16-bit).
+ * @thread EMT
+ */
+static int e1kRegWriteRCTL(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+ /* Update promiscuous mode */
+ bool fBecomePromiscous = !!(value & (RCTL_UPE | RCTL_MPE));
+ if (fBecomePromiscous != !!( RCTL & (RCTL_UPE | RCTL_MPE)))
+ {
+ /* Promiscuity has changed, pass the knowledge on. */
+#ifndef IN_RING3
+ return VINF_IOM_R3_MMIO_WRITE;
+#else
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+ if (pThisCC->pDrvR3)
+ pThisCC->pDrvR3->pfnSetPromiscuousMode(pThisCC->pDrvR3, fBecomePromiscous);
+#endif
+ }
+
+ /* Adjust receive buffer size */
+ unsigned cbRxBuf = 2048 >> GET_BITS_V(value, RCTL, BSIZE);
+ if (value & RCTL_BSEX)
+ cbRxBuf *= 16;
+ if (cbRxBuf > E1K_MAX_RX_PKT_SIZE)
+ cbRxBuf = E1K_MAX_RX_PKT_SIZE;
+ if (cbRxBuf != pThis->u16RxBSize)
+ E1kLog2(("%s e1kRegWriteRCTL: Setting receive buffer size to %d (old %d)\n",
+ pThis->szPrf, cbRxBuf, pThis->u16RxBSize));
+ Assert(cbRxBuf < 65536);
+ pThis->u16RxBSize = (uint16_t)cbRxBuf;
+
+ /* Update the register */
+ return e1kRegWriteDefault(pDevIns, pThis, offset, index, value);
+}
+
+/**
+ * Write handler for Packet Buffer Allocation register.
+ *
+ * TXA = 64 - RXA.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @param mask Used to implement partial writes (8 and 16-bit).
+ * @thread EMT
+ */
+static int e1kRegWritePBA(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+ e1kRegWriteDefault(pDevIns, pThis, offset, index, value);
+ PBA_st->txa = 64 - PBA_st->rxa;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Write handler for Receive Descriptor Tail register.
+ *
+ * @remarks Write into RDT forces switch to HC and signal to
+ * e1kR3NetworkDown_WaitReceiveAvail().
+ *
+ * @returns VBox status code.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @param mask Used to implement partial writes (8 and 16-bit).
+ * @thread EMT
+ */
+static int e1kRegWriteRDT(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+#ifndef IN_RING3
+ /* XXX */
+// return VINF_IOM_R3_MMIO_WRITE;
+#endif
+ int rc = e1kCsRxEnter(pThis, VINF_IOM_R3_MMIO_WRITE);
+ if (RT_LIKELY(rc == VINF_SUCCESS))
+ {
+ E1kLog(("%s e1kRegWriteRDT\n", pThis->szPrf));
+#ifndef E1K_WITH_RXD_CACHE
+ /*
+ * Some drivers advance RDT too far, so that it equals RDH. This
+ * somehow manages to work with real hardware but not with this
+ * emulated device. We can work with these drivers if we just
+ * write 1 less when we see a driver writing RDT equal to RDH,
+ * see @bugref{7346}.
+ */
+ if (value == RDH)
+ {
+ if (RDH == 0)
+ value = (RDLEN / sizeof(E1KRXDESC)) - 1;
+ else
+ value = RDH - 1;
+ }
+#endif /* !E1K_WITH_RXD_CACHE */
+ rc = e1kRegWriteDefault(pDevIns, pThis, offset, index, value);
+#ifdef E1K_WITH_RXD_CACHE
+ E1KRXDC rxdc;
+ if (RT_UNLIKELY(!e1kUpdateRxDContext(pDevIns, pThis, &rxdc, "e1kRegWriteRDT")))
+ {
+ e1kCsRxLeave(pThis);
+ E1kLog(("%s e1kRegWriteRDT: failed to update Rx context, returning VINF_SUCCESS\n", pThis->szPrf));
+ return VINF_SUCCESS;
+ }
+ /*
+ * We need to fetch descriptors now as RDT may go whole circle
+ * before we attempt to store a received packet. For example,
+ * Intel's DOS drivers use 2 (!) RX descriptors with the total ring
+ * size being only 8 descriptors! Note that we fetch descriptors
+ * only when the cache is empty to reduce the number of memory reads
+ * in case of frequent RDT writes. Don't fetch anything when the
+ * receiver is disabled either as RDH, RDT, RDLEN can be in some
+ * messed up state.
+ * Note that despite the cache may seem empty, meaning that there are
+ * no more available descriptors in it, it may still be used by RX
+ * thread which has not yet written the last descriptor back but has
+ * temporarily released the RX lock in order to write the packet body
+ * to descriptor's buffer. At this point we still going to do prefetch
+ * but it won't actually fetch anything if there are no unused slots in
+ * our "empty" cache (nRxDFetched==E1K_RXD_CACHE_SIZE). We must not
+ * reset the cache here even if it appears empty. It will be reset at
+ * a later point in e1kRxDGet().
+ */
+ if (e1kRxDIsCacheEmpty(pThis) && (RCTL & RCTL_EN))
+ e1kRxDPrefetch(pDevIns, pThis, &rxdc);
+#endif /* E1K_WITH_RXD_CACHE */
+ e1kCsRxLeave(pThis);
+ if (RT_SUCCESS(rc))
+ {
+ /* Signal that we have more receive descriptors available. */
+ e1kWakeupReceive(pDevIns, pThis);
+ }
+ }
+ return rc;
+}
+
+/**
+ * Write handler for Receive Delay Timer register.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @param mask Used to implement partial writes (8 and 16-bit).
+ * @thread EMT
+ */
+static int e1kRegWriteRDTR(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+ e1kRegWriteDefault(pDevIns, pThis, offset, index, value);
+ if (value & RDTR_FPD)
+ {
+ /* Flush requested, cancel both timers and raise interrupt */
+#ifdef E1K_USE_RX_TIMERS
+ e1kCancelTimer(pDevIns, pThis, pThis->hRIDTimer);
+ e1kCancelTimer(pDevIns, pThis, pThis->hRADTimer);
+#endif
+ E1K_INC_ISTAT_CNT(pThis->uStatIntRDTR);
+ return e1kRaiseInterrupt(pDevIns, pThis, VINF_IOM_R3_MMIO_WRITE, ICR_RXT0);
+ }
+
+ return VINF_SUCCESS;
+}
+
+DECLINLINE(uint32_t) e1kGetTxLen(PE1KTXDC pTxdc)
+{
+ /**
+ * Make sure TDT won't change during computation. EMT may modify TDT at
+ * any moment.
+ */
+ uint32_t tdt = pTxdc->tdt;
+ return (pTxdc->tdh > tdt ? pTxdc->tdlen/sizeof(E1KTXDESC) : 0) + tdt - pTxdc->tdh;
+}
+
+#ifdef IN_RING3
+
+# ifdef E1K_TX_DELAY
+/**
+ * @callback_method_impl{FNTMTIMERDEV, Transmit Delay Timer handler.}
+ */
+static DECLCALLBACK(void) e1kR3TxDelayTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
+{
+ PE1KSTATE pThis = (PE1KSTATE)pvUser;
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->csTx));
+ RT_NOREF(hTimer);
+
+ E1K_INC_ISTAT_CNT(pThis->uStatTxDelayExp);
+# ifdef E1K_INT_STATS
+ uint64_t u64Elapsed = RTTimeNanoTS() - pThis->u64ArmedAt;
+ if (u64Elapsed > pThis->uStatMaxTxDelay)
+ pThis->uStatMaxTxDelay = u64Elapsed;
+# endif
+ int rc = e1kXmitPending(pDevIns, pThis, false /*fOnWorkerThread*/);
+ AssertMsg(RT_SUCCESS(rc) || rc == VERR_TRY_AGAIN, ("%Rrc\n", rc));
+}
+# endif /* E1K_TX_DELAY */
+
+//# ifdef E1K_USE_TX_TIMERS
+
+/**
+ * @callback_method_impl{FNTMTIMERDEV, Transmit Interrupt Delay Timer handler.}
+ */
+static DECLCALLBACK(void) e1kR3TxIntDelayTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
+{
+ PE1KSTATE pThis = (PE1KSTATE)pvUser;
+ Assert(hTimer == pThis->hTIDTimer); RT_NOREF(hTimer);
+
+ E1K_INC_ISTAT_CNT(pThis->uStatTID);
+ /* Cancel absolute delay timer as we have already got attention */
+# ifndef E1K_NO_TAD
+ e1kCancelTimer(pDevIns, pThis, pThis->hTADTimer);
+# endif
+ e1kRaiseInterrupt(pDevIns, pThis, VERR_IGNORED, ICR_TXDW);
+}
+
+/**
+ * @callback_method_impl{FNTMTIMERDEV, Transmit Absolute Delay Timer handler.}
+ */
+static DECLCALLBACK(void) e1kR3TxAbsDelayTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
+{
+ PE1KSTATE pThis = (PE1KSTATE)pvUser;
+ Assert(hTimer == pThis->hTADTimer); RT_NOREF(hTimer);
+
+ E1K_INC_ISTAT_CNT(pThis->uStatTAD);
+ /* Cancel interrupt delay timer as we have already got attention */
+ e1kCancelTimer(pDevIns, pThis, pThis->hTIDTimer);
+ e1kRaiseInterrupt(pDevIns, pThis, VERR_IGNORED, ICR_TXDW);
+}
+
+//# endif /* E1K_USE_TX_TIMERS */
+# ifdef E1K_USE_RX_TIMERS
+
+/**
+ * @callback_method_impl{FNTMTIMERDEV, Receive Interrupt Delay Timer handler.}
+ */
+static DECLCALLBACK(void) e1kR3RxIntDelayTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
+{
+ PE1KSTATE pThis = (PE1KSTATE)pvUser;
+ Assert(hTimer == pThis->hRIDTimer); RT_NOREF(hTimer);
+
+ E1K_INC_ISTAT_CNT(pThis->uStatRID);
+ /* Cancel absolute delay timer as we have already got attention */
+ e1kCancelTimer(pDevIns, pThis, pThis->hRADTimer);
+ e1kRaiseInterrupt(pDevIns, pThis, VERR_IGNORED, ICR_RXT0);
+}
+
+/**
+ * @callback_method_impl{FNTMTIMERDEV, Receive Absolute Delay Timer handler.}
+ */
+static DECLCALLBACK(void) e1kR3RxAbsDelayTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
+{
+ PE1KSTATE pThis = (PE1KSTATE)pvUser;
+ Assert(hTimer == pThis->hRADTimer); RT_NOREF(hTimer);
+
+ E1K_INC_ISTAT_CNT(pThis->uStatRAD);
+ /* Cancel interrupt delay timer as we have already got attention */
+ e1kCancelTimer(pDevIns, pThis, pThis->hRIDTimer);
+ e1kRaiseInterrupt(pDevIns, pThis, VERR_IGNORED, ICR_RXT0);
+}
+
+# endif /* E1K_USE_RX_TIMERS */
+
+/**
+ * @callback_method_impl{FNTMTIMERDEV, Late Interrupt Timer handler.}
+ */
+static DECLCALLBACK(void) e1kR3LateIntTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
+{
+ PE1KSTATE pThis = (PE1KSTATE)pvUser;
+ Assert(hTimer == pThis->hIntTimer); RT_NOREF(hTimer);
+ RT_NOREF(hTimer);
+
+ STAM_PROFILE_ADV_START(&pThis->StatLateIntTimer, a);
+ STAM_COUNTER_INC(&pThis->StatLateInts);
+ E1K_INC_ISTAT_CNT(pThis->uStatIntLate);
+# if 0
+ if (pThis->iStatIntLost > -100)
+ pThis->iStatIntLost--;
+# endif
+ e1kRaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, 0);
+ STAM_PROFILE_ADV_STOP(&pThis->StatLateIntTimer, a);
+}
+
+/**
+ * @callback_method_impl{FNTMTIMERDEV, Link Up Timer handler.}
+ */
+static DECLCALLBACK(void) e1kR3LinkUpTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
+{
+ PE1KSTATE pThis = (PE1KSTATE)pvUser;
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+ Assert(hTimer == pThis->hLUTimer); RT_NOREF(hTimer);
+
+ /*
+ * This can happen if we set the link status to down when the Link up timer was
+ * already armed (shortly after e1kR3LoadDone() or when the cable was disconnected
+ * and connect+disconnect the cable very quick. Moreover, 82543GC triggers LSC
+ * on reset even if the cable is unplugged (see @bugref{8942}).
+ */
+ if (e1kIsConnected(pDevIns))
+ {
+ /* 82543GC does not have an internal PHY */
+ if (pThis->eChip == E1K_CHIP_82543GC || (CTRL & CTRL_SLU))
+ e1kR3LinkUp(pDevIns, pThis, pThisCC);
+ }
+# ifdef E1K_LSC_ON_RESET
+ else if (pThis->eChip == E1K_CHIP_82543GC)
+ e1kR3LinkDown(pDevIns, pThis, pThisCC);
+# endif /* E1K_LSC_ON_RESET */
+}
+
+#endif /* IN_RING3 */
+
+/**
+ * Sets up the GSO context according to the TSE new context descriptor.
+ *
+ * @param pGso The GSO context to setup.
+ * @param pCtx The context descriptor.
+ */
+DECLINLINE(bool) e1kSetupGsoCtx(PPDMNETWORKGSO pGso, E1KTXCTX const *pCtx)
+{
+ pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
+
+ /*
+ * See if the context descriptor describes something that could be TCP or
+ * UDP over IPv[46].
+ */
+ /* Check the header ordering and spacing: 1. Ethernet, 2. IP, 3. TCP/UDP. */
+ if (RT_UNLIKELY( pCtx->ip.u8CSS < sizeof(RTNETETHERHDR) ))
+ {
+ E1kLog(("e1kSetupGsoCtx: IPCSS=%#x\n", pCtx->ip.u8CSS));
+ return false;
+ }
+ if (RT_UNLIKELY( pCtx->tu.u8CSS < (size_t)pCtx->ip.u8CSS + (pCtx->dw2.fIP ? RTNETIPV4_MIN_LEN : RTNETIPV6_MIN_LEN) ))
+ {
+ E1kLog(("e1kSetupGsoCtx: TUCSS=%#x\n", pCtx->tu.u8CSS));
+ return false;
+ }
+ if (RT_UNLIKELY( pCtx->dw2.fTCP
+ ? pCtx->dw3.u8HDRLEN < (size_t)pCtx->tu.u8CSS + RTNETTCP_MIN_LEN
+ : pCtx->dw3.u8HDRLEN != (size_t)pCtx->tu.u8CSS + RTNETUDP_MIN_LEN ))
+ {
+ E1kLog(("e1kSetupGsoCtx: HDRLEN=%#x TCP=%d\n", pCtx->dw3.u8HDRLEN, pCtx->dw2.fTCP));
+ return false;
+ }
+
+ /* The end of the TCP/UDP checksum should stop at the end of the packet or at least after the headers. */
+ if (RT_UNLIKELY( pCtx->tu.u16CSE > 0 && pCtx->tu.u16CSE <= pCtx->dw3.u8HDRLEN ))
+ {
+ E1kLog(("e1kSetupGsoCtx: TUCSE=%#x HDRLEN=%#x\n", pCtx->tu.u16CSE, pCtx->dw3.u8HDRLEN));
+ return false;
+ }
+
+ /* IPv4 checksum offset. */
+ if (RT_UNLIKELY( pCtx->dw2.fIP && (size_t)pCtx->ip.u8CSO - pCtx->ip.u8CSS != RT_UOFFSETOF(RTNETIPV4, ip_sum) ))
+ {
+ E1kLog(("e1kSetupGsoCtx: IPCSO=%#x IPCSS=%#x\n", pCtx->ip.u8CSO, pCtx->ip.u8CSS));
+ return false;
+ }
+
+ /* TCP/UDP checksum offsets. */
+ if (RT_UNLIKELY( (size_t)pCtx->tu.u8CSO - pCtx->tu.u8CSS
+ != ( pCtx->dw2.fTCP
+ ? RT_UOFFSETOF(RTNETTCP, th_sum)
+ : RT_UOFFSETOF(RTNETUDP, uh_sum) ) ))
+ {
+ E1kLog(("e1kSetupGsoCtx: TUCSO=%#x TUCSS=%#x TCP=%d\n", pCtx->ip.u8CSO, pCtx->ip.u8CSS, pCtx->dw2.fTCP));
+ return false;
+ }
+
+ /*
+ * Because of internal networking using a 16-bit size field for GSO context
+ * plus frame, we have to make sure we don't exceed this.
+ */
+ if (RT_UNLIKELY( pCtx->dw3.u8HDRLEN + pCtx->dw2.u20PAYLEN > VBOX_MAX_GSO_SIZE ))
+ {
+ E1kLog(("e1kSetupGsoCtx: HDRLEN(=%#x) + PAYLEN(=%#x) = %#x, max is %#x\n",
+ pCtx->dw3.u8HDRLEN, pCtx->dw2.u20PAYLEN, pCtx->dw3.u8HDRLEN + pCtx->dw2.u20PAYLEN, VBOX_MAX_GSO_SIZE));
+ return false;
+ }
+
+ /*
+ * We're good for now - we'll do more checks when seeing the data.
+ * So, figure the type of offloading and setup the context.
+ */
+ if (pCtx->dw2.fIP)
+ {
+ if (pCtx->dw2.fTCP)
+ {
+ pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_TCP;
+ pGso->cbHdrsSeg = pCtx->dw3.u8HDRLEN;
+ }
+ else
+ {
+ pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_UDP;
+ pGso->cbHdrsSeg = pCtx->tu.u8CSS; /* IP header only */
+ }
+ /** @todo Detect IPv4-IPv6 tunneling (need test setup since linux doesn't do
+ * this yet it seems)... */
+ }
+ else
+ {
+ pGso->cbHdrsSeg = pCtx->dw3.u8HDRLEN; /** @todo IPv6 UFO */
+ if (pCtx->dw2.fTCP)
+ pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_TCP;
+ else
+ pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_UDP;
+ }
+ pGso->offHdr1 = pCtx->ip.u8CSS;
+ pGso->offHdr2 = pCtx->tu.u8CSS;
+ pGso->cbHdrsTotal = pCtx->dw3.u8HDRLEN;
+ pGso->cbMaxSeg = pCtx->dw3.u16MSS + (pGso->u8Type == PDMNETWORKGSOTYPE_IPV4_UDP ? pGso->offHdr2 : 0);
+ Assert(PDMNetGsoIsValid(pGso, sizeof(*pGso), pGso->cbMaxSeg * 5));
+ E1kLog2(("e1kSetupGsoCtx: mss=%#x hdr=%#x hdrseg=%#x hdr1=%#x hdr2=%#x %s\n",
+ pGso->cbMaxSeg, pGso->cbHdrsTotal, pGso->cbHdrsSeg, pGso->offHdr1, pGso->offHdr2, PDMNetGsoTypeName((PDMNETWORKGSOTYPE)pGso->u8Type) ));
+ return PDMNetGsoIsValid(pGso, sizeof(*pGso), pGso->cbMaxSeg * 5);
+}
+
+/**
+ * Checks if we can use GSO processing for the current TSE frame.
+ *
+ * @param pThis The device state structure.
+ * @param pGso The GSO context.
+ * @param pData The first data descriptor of the frame.
+ * @param pCtx The TSO context descriptor.
+ */
+DECLINLINE(bool) e1kCanDoGso(PE1KSTATE pThis, PCPDMNETWORKGSO pGso, E1KTXDAT const *pData, E1KTXCTX const *pCtx)
+{
+ if (!pData->cmd.fTSE)
+ {
+ E1kLog2(("e1kCanDoGso: !TSE\n"));
+ return false;
+ }
+ if (pData->cmd.fVLE) /** @todo VLAN tagging. */
+ {
+ E1kLog(("e1kCanDoGso: VLE\n"));
+ return false;
+ }
+ if (RT_UNLIKELY(!pThis->fGSOEnabled))
+ {
+ E1kLog3(("e1kCanDoGso: GSO disabled via CFGM\n"));
+ return false;
+ }
+
+ switch ((PDMNETWORKGSOTYPE)pGso->u8Type)
+ {
+ case PDMNETWORKGSOTYPE_IPV4_TCP:
+ case PDMNETWORKGSOTYPE_IPV4_UDP:
+ if (!pData->dw3.fIXSM)
+ {
+ E1kLog(("e1kCanDoGso: !IXSM (IPv4)\n"));
+ return false;
+ }
+ if (!pData->dw3.fTXSM)
+ {
+ E1kLog(("e1kCanDoGso: !TXSM (IPv4)\n"));
+ return false;
+ }
+ /** @todo what more check should we perform here? Ethernet frame type? */
+ E1kLog2(("e1kCanDoGso: OK, IPv4\n"));
+ return true;
+
+ case PDMNETWORKGSOTYPE_IPV6_TCP:
+ case PDMNETWORKGSOTYPE_IPV6_UDP:
+ if (pData->dw3.fIXSM && pCtx->ip.u8CSO)
+ {
+ E1kLog(("e1kCanDoGso: IXSM (IPv6)\n"));
+ return false;
+ }
+ if (!pData->dw3.fTXSM)
+ {
+ E1kLog(("e1kCanDoGso: TXSM (IPv6)\n"));
+ return false;
+ }
+ /** @todo what more check should we perform here? Ethernet frame type? */
+ E1kLog2(("e1kCanDoGso: OK, IPv4\n"));
+ return true;
+
+ default:
+ Assert(pGso->u8Type == PDMNETWORKGSOTYPE_INVALID);
+ E1kLog2(("e1kCanDoGso: e1kSetupGsoCtx failed\n"));
+ return false;
+ }
+}
+
+/**
+ * Frees the current xmit buffer.
+ *
+ * @param pThis The device state structure.
+ */
+static void e1kXmitFreeBuf(PE1KSTATE pThis, PE1KSTATECC pThisCC)
+{
+ PPDMSCATTERGATHER pSg = pThisCC->CTX_SUFF(pTxSg);
+ if (pSg)
+ {
+ pThisCC->CTX_SUFF(pTxSg) = NULL;
+
+ if (pSg->pvAllocator != pThis)
+ {
+ PPDMINETWORKUP pDrv = pThisCC->CTX_SUFF(pDrv);
+ if (pDrv)
+ pDrv->pfnFreeBuf(pDrv, pSg);
+ }
+ else
+ {
+ /* loopback */
+ AssertCompileMemberSize(E1KSTATE, uTxFallback.Sg, 8 * sizeof(size_t));
+ Assert(pSg->fFlags == (PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_3));
+ pSg->fFlags = 0;
+ pSg->pvAllocator = NULL;
+ }
+ }
+}
+
+#ifndef E1K_WITH_TXD_CACHE
+/**
+ * Allocates an xmit buffer.
+ *
+ * @returns See PDMINETWORKUP::pfnAllocBuf.
+ * @param pThis The device state structure.
+ * @param cbMin The minimum frame size.
+ * @param fExactSize Whether cbMin is exact or if we have to max it
+ * out to the max MTU size.
+ * @param fGso Whether this is a GSO frame or not.
+ */
+DECLINLINE(int) e1kXmitAllocBuf(PE1KSTATE pThis, PE1KSTATECC pThisCC, size_t cbMin, bool fExactSize, bool fGso)
+{
+ /* Adjust cbMin if necessary. */
+ if (!fExactSize)
+ cbMin = RT_MAX(cbMin, E1K_MAX_TX_PKT_SIZE);
+
+ /* Deal with existing buffer (descriptor screw up, reset, etc). */
+ if (RT_UNLIKELY(pThisCC->CTX_SUFF(pTxSg)))
+ e1kXmitFreeBuf(pThis, pThisCC);
+ Assert(pThisCC->CTX_SUFF(pTxSg) == NULL);
+
+ /*
+ * Allocate the buffer.
+ */
+ PPDMSCATTERGATHER pSg;
+ if (RT_LIKELY(GET_BITS(RCTL, LBM) != RCTL_LBM_TCVR))
+ {
+ PPDMINETWORKUP pDrv = pThisCC->CTX_SUFF(pDrv);
+ if (RT_UNLIKELY(!pDrv))
+ return VERR_NET_DOWN;
+ int rc = pDrv->pfnAllocBuf(pDrv, cbMin, fGso ? &pThis->GsoCtx : NULL, &pSg);
+ if (RT_FAILURE(rc))
+ {
+ /* Suspend TX as we are out of buffers atm */
+ STATUS |= STATUS_TXOFF;
+ return rc;
+ }
+ }
+ else
+ {
+ /* Create a loopback using the fallback buffer and preallocated SG. */
+ AssertCompileMemberSize(E1KSTATE, uTxFallback.Sg, 8 * sizeof(size_t));
+ pSg = &pThis->uTxFallback.Sg;
+ pSg->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_3;
+ pSg->cbUsed = 0;
+ pSg->cbAvailable = 0;
+ pSg->pvAllocator = pThis;
+ pSg->pvUser = NULL; /* No GSO here. */
+ pSg->cSegs = 1;
+ pSg->aSegs[0].pvSeg = pThis->aTxPacketFallback;
+ pSg->aSegs[0].cbSeg = sizeof(pThis->aTxPacketFallback);
+ }
+
+ pThisCC->CTX_SUFF(pTxSg) = pSg;
+ return VINF_SUCCESS;
+}
+#else /* E1K_WITH_TXD_CACHE */
+/**
+ * Allocates an xmit buffer.
+ *
+ * @returns See PDMINETWORKUP::pfnAllocBuf.
+ * @param pThis The device state structure.
+ * @param cbMin The minimum frame size.
+ * @param fExactSize Whether cbMin is exact or if we have to max it
+ * out to the max MTU size.
+ * @param fGso Whether this is a GSO frame or not.
+ */
+DECLINLINE(int) e1kXmitAllocBuf(PE1KSTATE pThis, PE1KSTATECC pThisCC, bool fGso)
+{
+ /* Deal with existing buffer (descriptor screw up, reset, etc). */
+ if (RT_UNLIKELY(pThisCC->CTX_SUFF(pTxSg)))
+ e1kXmitFreeBuf(pThis, pThisCC);
+ Assert(pThisCC->CTX_SUFF(pTxSg) == NULL);
+
+ /*
+ * Allocate the buffer.
+ */
+ PPDMSCATTERGATHER pSg;
+ if (RT_LIKELY(GET_BITS(RCTL, LBM) != RCTL_LBM_TCVR))
+ {
+ if (pThis->cbTxAlloc == 0)
+ {
+ /* Zero packet, no need for the buffer */
+ return VINF_SUCCESS;
+ }
+ if (fGso && pThis->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID)
+ {
+ E1kLog3(("Invalid GSO context, won't allocate this packet, cb=%u %s%s\n",
+ pThis->cbTxAlloc, pThis->fVTag ? "VLAN " : "", pThis->fGSO ? "GSO " : ""));
+ /* No valid GSO context is available, ignore this packet. */
+ pThis->cbTxAlloc = 0;
+ return VINF_SUCCESS;
+ }
+
+ PPDMINETWORKUP pDrv = pThisCC->CTX_SUFF(pDrv);
+ if (RT_UNLIKELY(!pDrv))
+ return VERR_NET_DOWN;
+ int rc = pDrv->pfnAllocBuf(pDrv, pThis->cbTxAlloc, fGso ? &pThis->GsoCtx : NULL, &pSg);
+ if (RT_FAILURE(rc))
+ {
+ /* Suspend TX as we are out of buffers atm */
+ STATUS |= STATUS_TXOFF;
+ return rc;
+ }
+ E1kLog3(("%s Allocated buffer for TX packet: cb=%u %s%s\n",
+ pThis->szPrf, pThis->cbTxAlloc,
+ pThis->fVTag ? "VLAN " : "",
+ pThis->fGSO ? "GSO " : ""));
+ }
+ else
+ {
+ /* Create a loopback using the fallback buffer and preallocated SG. */
+ AssertCompileMemberSize(E1KSTATE, uTxFallback.Sg, 8 * sizeof(size_t));
+ pSg = &pThis->uTxFallback.Sg;
+ pSg->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_3;
+ pSg->cbUsed = 0;
+ pSg->cbAvailable = sizeof(pThis->aTxPacketFallback);
+ pSg->pvAllocator = pThis;
+ pSg->pvUser = NULL; /* No GSO here. */
+ pSg->cSegs = 1;
+ pSg->aSegs[0].pvSeg = pThis->aTxPacketFallback;
+ pSg->aSegs[0].cbSeg = sizeof(pThis->aTxPacketFallback);
+ }
+ pThis->cbTxAlloc = 0;
+
+ pThisCC->CTX_SUFF(pTxSg) = pSg;
+ return VINF_SUCCESS;
+}
+#endif /* E1K_WITH_TXD_CACHE */
+
+/**
+ * Checks if it's a GSO buffer or not.
+ *
+ * @returns true / false.
+ * @param pTxSg The scatter / gather buffer.
+ */
+DECLINLINE(bool) e1kXmitIsGsoBuf(PDMSCATTERGATHER const *pTxSg)
+{
+#if 0
+ if (!pTxSg)
+ E1kLog(("e1kXmitIsGsoBuf: pTxSG is NULL\n"));
+ if (pTxSg && pTxSg->pvUser)
+ E1kLog(("e1kXmitIsGsoBuf: pvUser is NULL\n"));
+#endif
+ return pTxSg && pTxSg->pvUser /* GSO indicator */;
+}
+
+#ifndef E1K_WITH_TXD_CACHE
+/**
+ * Load transmit descriptor from guest memory.
+ *
+ * @param pDevIns The device instance.
+ * @param pDesc Pointer to descriptor union.
+ * @param addr Physical address in guest context.
+ * @thread E1000_TX
+ */
+DECLINLINE(void) e1kLoadDesc(PPDMDEVINS pDevIns, E1KTXDESC *pDesc, RTGCPHYS addr)
+{
+ PDMDevHlpPCIPhysRead(pDevIns, addr, pDesc, sizeof(E1KTXDESC));
+}
+#else /* E1K_WITH_TXD_CACHE */
+/**
+ * Load transmit descriptors from guest memory.
+ *
+ * We need two physical reads in case the tail wrapped around the end of TX
+ * descriptor ring.
+ *
+ * @returns the actual number of descriptors fetched.
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @thread E1000_TX
+ */
+DECLINLINE(unsigned) e1kTxDLoadMore(PPDMDEVINS pDevIns, PE1KSTATE pThis, PE1KTXDC pTxdc)
+{
+ Assert(pThis->iTxDCurrent == 0);
+ /* We've already loaded pThis->nTxDFetched descriptors past TDH. */
+ unsigned nDescsAvailable = e1kGetTxLen(pTxdc) - pThis->nTxDFetched;
+ /* The following two lines ensure that pThis->nTxDFetched never overflows. */
+ AssertCompile(E1K_TXD_CACHE_SIZE < (256 * sizeof(pThis->nTxDFetched)));
+ unsigned nDescsToFetch = RT_MIN(nDescsAvailable, E1K_TXD_CACHE_SIZE - pThis->nTxDFetched);
+ unsigned nDescsTotal = pTxdc->tdlen / sizeof(E1KTXDESC);
+ Assert(nDescsTotal != 0);
+ if (nDescsTotal == 0)
+ return 0;
+ unsigned nFirstNotLoaded = (pTxdc->tdh + pThis->nTxDFetched) % nDescsTotal;
+ unsigned nDescsInSingleRead = RT_MIN(nDescsToFetch, nDescsTotal - nFirstNotLoaded);
+ E1kLog3(("%s e1kTxDLoadMore: nDescsAvailable=%u nDescsToFetch=%u nDescsTotal=%u nFirstNotLoaded=0x%x nDescsInSingleRead=%u\n",
+ pThis->szPrf, nDescsAvailable, nDescsToFetch, nDescsTotal,
+ nFirstNotLoaded, nDescsInSingleRead));
+ if (nDescsToFetch == 0)
+ return 0;
+ E1KTXDESC* pFirstEmptyDesc = &pThis->aTxDescriptors[pThis->nTxDFetched];
+ PDMDevHlpPCIPhysRead(pDevIns,
+ ((uint64_t)TDBAH << 32) + TDBAL + nFirstNotLoaded * sizeof(E1KTXDESC),
+ pFirstEmptyDesc, nDescsInSingleRead * sizeof(E1KTXDESC));
+ E1kLog3(("%s Fetched %u TX descriptors at %08x%08x(0x%x), TDLEN=%08x, TDH=%08x, TDT=%08x\n",
+ pThis->szPrf, nDescsInSingleRead,
+ TDBAH, TDBAL + pTxdc->tdh * sizeof(E1KTXDESC),
+ nFirstNotLoaded, pTxdc->tdlen, pTxdc->tdh, pTxdc->tdt));
+ if (nDescsToFetch > nDescsInSingleRead)
+ {
+ PDMDevHlpPCIPhysRead(pDevIns,
+ ((uint64_t)TDBAH << 32) + TDBAL,
+ pFirstEmptyDesc + nDescsInSingleRead,
+ (nDescsToFetch - nDescsInSingleRead) * sizeof(E1KTXDESC));
+ E1kLog3(("%s Fetched %u TX descriptors at %08x%08x\n",
+ pThis->szPrf, nDescsToFetch - nDescsInSingleRead,
+ TDBAH, TDBAL));
+ }
+ pThis->nTxDFetched += (uint8_t)nDescsToFetch;
+ return nDescsToFetch;
+}
+
+/**
+ * Load transmit descriptors from guest memory only if there are no loaded
+ * descriptors.
+ *
+ * @returns true if there are descriptors in cache.
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @thread E1000_TX
+ */
+DECLINLINE(bool) e1kTxDLazyLoad(PPDMDEVINS pDevIns, PE1KSTATE pThis, PE1KTXDC pTxdc)
+{
+ if (pThis->nTxDFetched == 0)
+ return e1kTxDLoadMore(pDevIns, pThis, pTxdc) != 0;
+ return true;
+}
+#endif /* E1K_WITH_TXD_CACHE */
+
+/**
+ * Write back transmit descriptor to guest memory.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param pDesc Pointer to descriptor union.
+ * @param addr Physical address in guest context.
+ * @thread E1000_TX
+ */
+DECLINLINE(void) e1kWriteBackDesc(PPDMDEVINS pDevIns, PE1KSTATE pThis, E1KTXDESC *pDesc, RTGCPHYS addr)
+{
+ /* Only the last half of the descriptor has to be written back. */
+ e1kPrintTDesc(pThis, pDesc, "^^^");
+ PDMDevHlpPCIPhysWrite(pDevIns, addr, pDesc, sizeof(E1KTXDESC));
+}
+
+/**
+ * Transmit complete frame.
+ *
+ * @remarks We skip the FCS since we're not responsible for sending anything to
+ * a real ethernet wire.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param pThisCC The current context instance data.
+ * @param fOnWorkerThread Whether we're on a worker thread or an EMT.
+ * @thread E1000_TX
+ */
+static void e1kTransmitFrame(PPDMDEVINS pDevIns, PE1KSTATE pThis, PE1KSTATECC pThisCC, bool fOnWorkerThread)
+{
+ PPDMSCATTERGATHER pSg = pThisCC->CTX_SUFF(pTxSg);
+ uint32_t cbFrame = pSg ? (uint32_t)pSg->cbUsed : 0;
+ Assert(!pSg || pSg->cSegs == 1);
+
+ if (cbFrame < 14)
+ {
+ Log(("%s Ignoring invalid frame (%u bytes)\n", pThis->szPrf, cbFrame));
+ return;
+ }
+ if (cbFrame > 70) /* unqualified guess */
+ pThis->led.Asserted.s.fWriting = pThis->led.Actual.s.fWriting = 1;
+
+#ifdef E1K_INT_STATS
+ if (cbFrame <= 1514)
+ E1K_INC_ISTAT_CNT(pThis->uStatTx1514);
+ else if (cbFrame <= 2962)
+ E1K_INC_ISTAT_CNT(pThis->uStatTx2962);
+ else if (cbFrame <= 4410)
+ E1K_INC_ISTAT_CNT(pThis->uStatTx4410);
+ else if (cbFrame <= 5858)
+ E1K_INC_ISTAT_CNT(pThis->uStatTx5858);
+ else if (cbFrame <= 7306)
+ E1K_INC_ISTAT_CNT(pThis->uStatTx7306);
+ else if (cbFrame <= 8754)
+ E1K_INC_ISTAT_CNT(pThis->uStatTx8754);
+ else if (cbFrame <= 16384)
+ E1K_INC_ISTAT_CNT(pThis->uStatTx16384);
+ else if (cbFrame <= 32768)
+ E1K_INC_ISTAT_CNT(pThis->uStatTx32768);
+ else
+ E1K_INC_ISTAT_CNT(pThis->uStatTxLarge);
+#endif /* E1K_INT_STATS */
+
+ /* Add VLAN tag */
+ if (cbFrame > 12 && pThis->fVTag && pSg->cbUsed + 4 <= pSg->cbAvailable)
+ {
+ E1kLog3(("%s Inserting VLAN tag %08x\n",
+ pThis->szPrf, RT_BE2H_U16((uint16_t)VET) | (RT_BE2H_U16(pThis->u16VTagTCI) << 16)));
+ memmove((uint8_t*)pSg->aSegs[0].pvSeg + 16, (uint8_t*)pSg->aSegs[0].pvSeg + 12, cbFrame - 12);
+ *((uint32_t*)pSg->aSegs[0].pvSeg + 3) = RT_BE2H_U16((uint16_t)VET) | (RT_BE2H_U16(pThis->u16VTagTCI) << 16);
+ pSg->cbUsed += 4;
+ cbFrame += 4;
+ Assert(pSg->cbUsed == cbFrame);
+ Assert(pSg->cbUsed <= pSg->cbAvailable);
+ }
+/* E1kLog2(("%s < < < Outgoing packet. Dump follows: > > >\n"
+ "%.*Rhxd\n"
+ "%s < < < < < < < < < < < < < End of dump > > > > > > > > > > > >\n",
+ pThis->szPrf, cbFrame, pSg->aSegs[0].pvSeg, pThis->szPrf));*/
+
+ /* Update the stats */
+ E1K_INC_CNT32(TPT);
+ E1K_ADD_CNT64(TOTL, TOTH, cbFrame);
+ E1K_INC_CNT32(GPTC);
+ if (pSg && e1kIsBroadcast(pSg->aSegs[0].pvSeg))
+ E1K_INC_CNT32(BPTC);
+ else if (pSg && e1kIsMulticast(pSg->aSegs[0].pvSeg))
+ E1K_INC_CNT32(MPTC);
+ /* Update octet transmit counter */
+ E1K_ADD_CNT64(GOTCL, GOTCH, cbFrame);
+ if (pThisCC->CTX_SUFF(pDrv))
+ STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, cbFrame);
+ if (cbFrame == 64)
+ E1K_INC_CNT32(PTC64);
+ else if (cbFrame < 128)
+ E1K_INC_CNT32(PTC127);
+ else if (cbFrame < 256)
+ E1K_INC_CNT32(PTC255);
+ else if (cbFrame < 512)
+ E1K_INC_CNT32(PTC511);
+ else if (cbFrame < 1024)
+ E1K_INC_CNT32(PTC1023);
+ else
+ E1K_INC_CNT32(PTC1522);
+
+ E1K_INC_ISTAT_CNT(pThis->uStatTxFrm);
+
+ /*
+ * Dump and send the packet.
+ */
+ int rc = VERR_NET_DOWN;
+ if (pSg && pSg->pvAllocator != pThis)
+ {
+ e1kPacketDump(pDevIns, pThis, (uint8_t const *)pSg->aSegs[0].pvSeg, cbFrame, "--> Outgoing");
+
+ pThisCC->CTX_SUFF(pTxSg) = NULL;
+ PPDMINETWORKUP pDrv = pThisCC->CTX_SUFF(pDrv);
+ if (pDrv)
+ {
+ /* Release critical section to avoid deadlock in CanReceive */
+ //e1kCsLeave(pThis);
+ STAM_PROFILE_START(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
+ rc = pDrv->pfnSendBuf(pDrv, pSg, fOnWorkerThread);
+ STAM_PROFILE_STOP(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
+ //e1kR3CsEnterAsserted(pThis);
+ }
+ }
+ else if (pSg)
+ {
+ Assert(pSg->aSegs[0].pvSeg == pThis->aTxPacketFallback);
+ e1kPacketDump(pDevIns, pThis, (uint8_t const *)pSg->aSegs[0].pvSeg, cbFrame, "--> Loopback");
+
+ /** @todo do we actually need to check that we're in loopback mode here? */
+ if (GET_BITS(RCTL, LBM) == RCTL_LBM_TCVR)
+ {
+ E1KRXDST status;
+ RT_ZERO(status);
+ status.fPIF = true;
+ e1kHandleRxPacket(pDevIns, pThis, pSg->aSegs[0].pvSeg, cbFrame, status);
+ rc = VINF_SUCCESS;
+ }
+ e1kXmitFreeBuf(pThis, pThisCC);
+ }
+ else
+ rc = VERR_NET_DOWN;
+ if (RT_FAILURE(rc))
+ {
+ E1kLogRel(("E1000: ERROR! pfnSend returned %Rrc\n", rc));
+ /** @todo handle VERR_NET_DOWN and VERR_NET_NO_BUFFER_SPACE. Signal error ? */
+ }
+
+ pThis->led.Actual.s.fWriting = 0;
+}
+
+/**
+ * Compute and write internet checksum (e1kCSum16) at the specified offset.
+ *
+ * @param pThis The device state structure.
+ * @param pPkt Pointer to the packet.
+ * @param u16PktLen Total length of the packet.
+ * @param cso Offset in packet to write checksum at.
+ * @param css Offset in packet to start computing
+ * checksum from.
+ * @param cse Offset in packet to stop computing
+ * checksum at.
+ * @param fUdp Replace 0 checksum with all 1s.
+ * @thread E1000_TX
+ */
+static void e1kInsertChecksum(PE1KSTATE pThis, uint8_t *pPkt, uint16_t u16PktLen, uint8_t cso, uint8_t css, uint16_t cse, bool fUdp = false)
+{
+ RT_NOREF1(pThis);
+
+ if (css >= u16PktLen)
+ {
+ E1kLog2(("%s css(%X) is greater than packet length-1(%X), checksum is not inserted\n",
+ pThis->szPrf, cso, u16PktLen));
+ return;
+ }
+
+ if (cso >= u16PktLen - 1)
+ {
+ E1kLog2(("%s cso(%X) is greater than packet length-2(%X), checksum is not inserted\n",
+ pThis->szPrf, cso, u16PktLen));
+ return;
+ }
+
+ if (cse == 0 || cse >= u16PktLen)
+ cse = u16PktLen - 1;
+ else if (cse < css)
+ {
+ E1kLog2(("%s css(%X) is greater than cse(%X), checksum is not inserted\n",
+ pThis->szPrf, css, cse));
+ return;
+ }
+
+ uint16_t u16ChkSum = e1kCSum16(pPkt + css, cse - css + 1);
+ if (fUdp && u16ChkSum == 0)
+ u16ChkSum = ~u16ChkSum; /* 0 means no checksum computed in case of UDP (see @bugref{9883}) */
+ E1kLog2(("%s Inserting csum: %04X at %02X, old value: %04X\n", pThis->szPrf,
+ u16ChkSum, cso, *(uint16_t*)(pPkt + cso)));
+ *(uint16_t*)(pPkt + cso) = u16ChkSum;
+}
+
+/**
+ * Add a part of descriptor's buffer to transmit frame.
+ *
+ * @remarks data.u64BufAddr is used unconditionally for both data
+ * and legacy descriptors since it is identical to
+ * legacy.u64BufAddr.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param pDesc Pointer to the descriptor to transmit.
+ * @param u16Len Length of buffer to the end of segment.
+ * @param fSend Force packet sending.
+ * @param fOnWorkerThread Whether we're on a worker thread or an EMT.
+ * @thread E1000_TX
+ */
+#ifndef E1K_WITH_TXD_CACHE
+static void e1kFallbackAddSegment(PPDMDEVINS pDevIns, PE1KSTATE pThis, RTGCPHYS PhysAddr, uint16_t u16Len, bool fSend, bool fOnWorkerThread)
+{
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+ /* TCP header being transmitted */
+ struct E1kTcpHeader *pTcpHdr = (struct E1kTcpHeader *)(pThis->aTxPacketFallback + pThis->contextTSE.tu.u8CSS);
+ /* IP header being transmitted */
+ struct E1kIpHeader *pIpHdr = (struct E1kIpHeader *)(pThis->aTxPacketFallback + pThis->contextTSE.ip.u8CSS);
+
+ E1kLog3(("%s e1kFallbackAddSegment: Length=%x, remaining payload=%x, header=%x, send=%RTbool\n",
+ pThis->szPrf, u16Len, pThis->u32PayRemain, pThis->u16HdrRemain, fSend));
+ Assert(pThis->u32PayRemain + pThis->u16HdrRemain > 0);
+
+ PDMDevHlpPCIPhysRead(pDevIns, PhysAddr, pThis->aTxPacketFallback + pThis->u16TxPktLen, u16Len);
+ E1kLog3(("%s Dump of the segment:\n"
+ "%.*Rhxd\n"
+ "%s --- End of dump ---\n",
+ pThis->szPrf, u16Len, pThis->aTxPacketFallback + pThis->u16TxPktLen, pThis->szPrf));
+ pThis->u16TxPktLen += u16Len;
+ E1kLog3(("%s e1kFallbackAddSegment: pThis->u16TxPktLen=%x\n",
+ pThis->szPrf, pThis->u16TxPktLen));
+ if (pThis->u16HdrRemain > 0)
+ {
+ /* The header was not complete, check if it is now */
+ if (u16Len >= pThis->u16HdrRemain)
+ {
+ /* The rest is payload */
+ u16Len -= pThis->u16HdrRemain;
+ pThis->u16HdrRemain = 0;
+ /* Save partial checksum and flags */
+ pThis->u32SavedCsum = pTcpHdr->chksum;
+ pThis->u16SavedFlags = pTcpHdr->hdrlen_flags;
+ /* Clear FIN and PSH flags now and set them only in the last segment */
+ pTcpHdr->hdrlen_flags &= ~htons(E1K_TCP_FIN | E1K_TCP_PSH);
+ }
+ else
+ {
+ /* Still not */
+ pThis->u16HdrRemain -= u16Len;
+ E1kLog3(("%s e1kFallbackAddSegment: Header is still incomplete, 0x%x bytes remain.\n",
+ pThis->szPrf, pThis->u16HdrRemain));
+ return;
+ }
+ }
+
+ pThis->u32PayRemain -= u16Len;
+
+ if (fSend)
+ {
+ /* Leave ethernet header intact */
+ /* IP Total Length = payload + headers - ethernet header */
+ pIpHdr->total_len = htons(pThis->u16TxPktLen - pThis->contextTSE.ip.u8CSS);
+ E1kLog3(("%s e1kFallbackAddSegment: End of packet, pIpHdr->total_len=%x\n",
+ pThis->szPrf, ntohs(pIpHdr->total_len)));
+ /* Update IP Checksum */
+ pIpHdr->chksum = 0;
+ e1kInsertChecksum(pThis, pThis->aTxPacketFallback, pThis->u16TxPktLen,
+ pThis->contextTSE.ip.u8CSO,
+ pThis->contextTSE.ip.u8CSS,
+ pThis->contextTSE.ip.u16CSE);
+
+ /* Update TCP flags */
+ /* Restore original FIN and PSH flags for the last segment */
+ if (pThis->u32PayRemain == 0)
+ {
+ pTcpHdr->hdrlen_flags = pThis->u16SavedFlags;
+ E1K_INC_CNT32(TSCTC);
+ }
+ /* Add TCP length to partial pseudo header sum */
+ uint32_t csum = pThis->u32SavedCsum
+ + htons(pThis->u16TxPktLen - pThis->contextTSE.tu.u8CSS);
+ while (csum >> 16)
+ csum = (csum >> 16) + (csum & 0xFFFF);
+ pTcpHdr->chksum = csum;
+ /* Compute final checksum */
+ e1kInsertChecksum(pThis, pThis->aTxPacketFallback, pThis->u16TxPktLen,
+ pThis->contextTSE.tu.u8CSO,
+ pThis->contextTSE.tu.u8CSS,
+ pThis->contextTSE.tu.u16CSE);
+
+ /*
+ * Transmit it. If we've use the SG already, allocate a new one before
+ * we copy of the data.
+ */
+ PPDMSCATTERGATHER pTxSg = pThisCC->CTX_SUFF(pTxSg);
+ if (!pTxSg)
+ {
+ e1kXmitAllocBuf(pThis, pThisCC, pThis->u16TxPktLen + (pThis->fVTag ? 4 : 0), true /*fExactSize*/, false /*fGso*/);
+ pTxSg = pThisCC->CTX_SUFF(pTxSg);
+ }
+ if (pTxSg)
+ {
+ Assert(pThis->u16TxPktLen <= pThisCC->CTX_SUFF(pTxSg)->cbAvailable);
+ Assert(pTxSg->cSegs == 1);
+ if (pThis->CCCTX_SUFF(pTxSg)->aSegs[0].pvSeg != pThis->aTxPacketFallback)
+ memcpy(pTxSg->aSegs[0].pvSeg, pThis->aTxPacketFallback, pThis->u16TxPktLen);
+ pTxSg->cbUsed = pThis->u16TxPktLen;
+ pTxSg->aSegs[0].cbSeg = pThis->u16TxPktLen;
+ }
+ e1kTransmitFrame(pDevIns, pThis, pThisCC, fOnWorkerThread);
+
+ /* Update Sequence Number */
+ pTcpHdr->seqno = htonl(ntohl(pTcpHdr->seqno) + pThis->u16TxPktLen
+ - pThis->contextTSE.dw3.u8HDRLEN);
+ /* Increment IP identification */
+ pIpHdr->ident = htons(ntohs(pIpHdr->ident) + 1);
+ }
+}
+#else /* E1K_WITH_TXD_CACHE */
+static int e1kFallbackAddSegment(PPDMDEVINS pDevIns, PE1KSTATE pThis, RTGCPHYS PhysAddr, uint16_t u16Len, bool fSend, bool fOnWorkerThread)
+{
+ int rc = VINF_SUCCESS;
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+ /* TCP header being transmitted */
+ struct E1kTcpHeader *pTcpHdr = (struct E1kTcpHeader *)(pThis->aTxPacketFallback + pThis->contextTSE.tu.u8CSS);
+ /* IP header being transmitted */
+ struct E1kIpHeader *pIpHdr = (struct E1kIpHeader *)(pThis->aTxPacketFallback + pThis->contextTSE.ip.u8CSS);
+
+ E1kLog3(("%s e1kFallbackAddSegment: Length=%x, remaining payload=%x, header=%x, send=%RTbool\n",
+ pThis->szPrf, u16Len, pThis->u32PayRemain, pThis->u16HdrRemain, fSend));
+ AssertReturn(pThis->u32PayRemain + pThis->u16HdrRemain > 0, VINF_SUCCESS);
+
+ if (pThis->u16TxPktLen + u16Len <= sizeof(pThis->aTxPacketFallback))
+ PDMDevHlpPCIPhysRead(pDevIns, PhysAddr, pThis->aTxPacketFallback + pThis->u16TxPktLen, u16Len);
+ else
+ E1kLog(("%s e1kFallbackAddSegment: writing beyond aTxPacketFallback, u16TxPktLen=%d(0x%x) + u16Len=%d(0x%x) > %d\n",
+ pThis->szPrf, pThis->u16TxPktLen, pThis->u16TxPktLen, u16Len, u16Len, sizeof(pThis->aTxPacketFallback)));
+ E1kLog3(("%s Dump of the segment:\n"
+ "%.*Rhxd\n"
+ "%s --- End of dump ---\n",
+ pThis->szPrf, u16Len, pThis->aTxPacketFallback + pThis->u16TxPktLen, pThis->szPrf));
+ pThis->u16TxPktLen += u16Len;
+ E1kLog3(("%s e1kFallbackAddSegment: pThis->u16TxPktLen=%x\n",
+ pThis->szPrf, pThis->u16TxPktLen));
+ if (pThis->u16HdrRemain > 0)
+ {
+ /* The header was not complete, check if it is now */
+ if (u16Len >= pThis->u16HdrRemain)
+ {
+ /* The rest is payload */
+ u16Len -= pThis->u16HdrRemain;
+ pThis->u16HdrRemain = 0;
+ /* Save partial checksum and flags */
+ pThis->u32SavedCsum = pTcpHdr->chksum;
+ pThis->u16SavedFlags = pTcpHdr->hdrlen_flags;
+ /* Clear FIN and PSH flags now and set them only in the last segment */
+ pTcpHdr->hdrlen_flags &= ~htons(E1K_TCP_FIN | E1K_TCP_PSH);
+ }
+ else
+ {
+ /* Still not */
+ pThis->u16HdrRemain -= u16Len;
+ E1kLog3(("%s e1kFallbackAddSegment: Header is still incomplete, 0x%x bytes remain.\n",
+ pThis->szPrf, pThis->u16HdrRemain));
+ return rc;
+ }
+ }
+
+ if (u16Len > pThis->u32PayRemain)
+ pThis->u32PayRemain = 0;
+ else
+ pThis->u32PayRemain -= u16Len;
+
+ if (fSend)
+ {
+ /* Leave ethernet header intact */
+ /* IP Total Length = payload + headers - ethernet header */
+ pIpHdr->total_len = htons(pThis->u16TxPktLen - pThis->contextTSE.ip.u8CSS);
+ E1kLog3(("%s e1kFallbackAddSegment: End of packet, pIpHdr->total_len=%x\n",
+ pThis->szPrf, ntohs(pIpHdr->total_len)));
+ /* Update IP Checksum */
+ pIpHdr->chksum = 0;
+ e1kInsertChecksum(pThis, pThis->aTxPacketFallback, pThis->u16TxPktLen,
+ pThis->contextTSE.ip.u8CSO,
+ pThis->contextTSE.ip.u8CSS,
+ pThis->contextTSE.ip.u16CSE);
+
+ /* Update TCP flags */
+ /* Restore original FIN and PSH flags for the last segment */
+ if (pThis->u32PayRemain == 0)
+ {
+ pTcpHdr->hdrlen_flags = pThis->u16SavedFlags;
+ E1K_INC_CNT32(TSCTC);
+ }
+ /* Add TCP length to partial pseudo header sum */
+ uint32_t csum = pThis->u32SavedCsum
+ + htons(pThis->u16TxPktLen - pThis->contextTSE.tu.u8CSS);
+ while (csum >> 16)
+ csum = (csum >> 16) + (csum & 0xFFFF);
+ Assert(csum < 65536);
+ pTcpHdr->chksum = (uint16_t)csum;
+ /* Compute final checksum */
+ e1kInsertChecksum(pThis, pThis->aTxPacketFallback, pThis->u16TxPktLen,
+ pThis->contextTSE.tu.u8CSO,
+ pThis->contextTSE.tu.u8CSS,
+ pThis->contextTSE.tu.u16CSE);
+
+ /*
+ * Transmit it.
+ */
+ PPDMSCATTERGATHER pTxSg = pThisCC->CTX_SUFF(pTxSg);
+ if (pTxSg)
+ {
+ /* Make sure the packet fits into the allocated buffer */
+ size_t cbCopy = RT_MIN(pThis->u16TxPktLen, pThisCC->CTX_SUFF(pTxSg)->cbAvailable);
+#ifdef DEBUG
+ if (pThis->u16TxPktLen > pTxSg->cbAvailable)
+ E1kLog(("%s e1kFallbackAddSegment: truncating packet, u16TxPktLen=%d(0x%x) > cbAvailable=%d(0x%x)\n",
+ pThis->szPrf, pThis->u16TxPktLen, pThis->u16TxPktLen, pTxSg->cbAvailable, pTxSg->cbAvailable));
+#endif /* DEBUG */
+ Assert(pTxSg->cSegs == 1);
+ if (pTxSg->aSegs[0].pvSeg != pThis->aTxPacketFallback)
+ memcpy(pTxSg->aSegs[0].pvSeg, pThis->aTxPacketFallback, cbCopy);
+ pTxSg->cbUsed = cbCopy;
+ pTxSg->aSegs[0].cbSeg = cbCopy;
+ }
+ e1kTransmitFrame(pDevIns, pThis, pThisCC, fOnWorkerThread);
+
+ /* Update Sequence Number */
+ pTcpHdr->seqno = htonl(ntohl(pTcpHdr->seqno) + pThis->u16TxPktLen
+ - pThis->contextTSE.dw3.u8HDRLEN);
+ /* Increment IP identification */
+ pIpHdr->ident = htons(ntohs(pIpHdr->ident) + 1);
+
+ /* Allocate new buffer for the next segment. */
+ if (pThis->u32PayRemain)
+ {
+ pThis->cbTxAlloc = RT_MIN(pThis->u32PayRemain,
+ pThis->contextTSE.dw3.u16MSS)
+ + pThis->contextTSE.dw3.u8HDRLEN;
+ /* Do not add VLAN tags to empty packets. */
+ if (pThis->fVTag && pThis->cbTxAlloc > 0)
+ pThis->cbTxAlloc += 4;
+ rc = e1kXmitAllocBuf(pThis, pThisCC, false /* fGSO */);
+ }
+ }
+
+ return rc;
+}
+#endif /* E1K_WITH_TXD_CACHE */
+
+#ifndef E1K_WITH_TXD_CACHE
+/**
+ * TCP segmentation offloading fallback: Add descriptor's buffer to transmit
+ * frame.
+ *
+ * We construct the frame in the fallback buffer first and the copy it to the SG
+ * buffer before passing it down to the network driver code.
+ *
+ * @returns true if the frame should be transmitted, false if not.
+ *
+ * @param pThis The device state structure.
+ * @param pDesc Pointer to the descriptor to transmit.
+ * @param cbFragment Length of descriptor's buffer.
+ * @param fOnWorkerThread Whether we're on a worker thread or an EMT.
+ * @thread E1000_TX
+ */
+static bool e1kFallbackAddToFrame(PE1KSTATE pThis, E1KTXDESC *pDesc, uint32_t cbFragment, bool fOnWorkerThread)
+{
+ PPDMSCATTERGATHER pTxSg = pThisCC->CTX_SUFF(pTxSg);
+ Assert(e1kGetDescType(pDesc) == E1K_DTYP_DATA);
+ Assert(pDesc->data.cmd.fTSE);
+ Assert(!e1kXmitIsGsoBuf(pTxSg));
+
+ uint16_t u16MaxPktLen = pThis->contextTSE.dw3.u8HDRLEN + pThis->contextTSE.dw3.u16MSS;
+ Assert(u16MaxPktLen != 0);
+ Assert(u16MaxPktLen < E1K_MAX_TX_PKT_SIZE);
+
+ /*
+ * Carve out segments.
+ */
+ do
+ {
+ /* Calculate how many bytes we have left in this TCP segment */
+ uint32_t cb = u16MaxPktLen - pThis->u16TxPktLen;
+ if (cb > cbFragment)
+ {
+ /* This descriptor fits completely into current segment */
+ cb = cbFragment;
+ e1kFallbackAddSegment(pDevIns, pThis, pDesc->data.u64BufAddr, cb, pDesc->data.cmd.fEOP /*fSend*/, fOnWorkerThread);
+ }
+ else
+ {
+ e1kFallbackAddSegment(pDevIns, pThis, pDesc->data.u64BufAddr, cb, true /*fSend*/, fOnWorkerThread);
+ /*
+ * Rewind the packet tail pointer to the beginning of payload,
+ * so we continue writing right beyond the header.
+ */
+ pThis->u16TxPktLen = pThis->contextTSE.dw3.u8HDRLEN;
+ }
+
+ pDesc->data.u64BufAddr += cb;
+ cbFragment -= cb;
+ } while (cbFragment > 0);
+
+ if (pDesc->data.cmd.fEOP)
+ {
+ /* End of packet, next segment will contain header. */
+ if (pThis->u32PayRemain != 0)
+ E1K_INC_CNT32(TSCTFC);
+ pThis->u16TxPktLen = 0;
+ e1kXmitFreeBuf(pThis, PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC));
+ }
+
+ return false;
+}
+#else /* E1K_WITH_TXD_CACHE */
+/**
+ * TCP segmentation offloading fallback: Add descriptor's buffer to transmit
+ * frame.
+ *
+ * We construct the frame in the fallback buffer first and the copy it to the SG
+ * buffer before passing it down to the network driver code.
+ *
+ * @returns error code
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param pDesc Pointer to the descriptor to transmit.
+ * @param cbFragment Length of descriptor's buffer.
+ * @param fOnWorkerThread Whether we're on a worker thread or an EMT.
+ * @thread E1000_TX
+ */
+static int e1kFallbackAddToFrame(PPDMDEVINS pDevIns, PE1KSTATE pThis, E1KTXDESC *pDesc, bool fOnWorkerThread)
+{
+#ifdef VBOX_STRICT
+ PPDMSCATTERGATHER pTxSg = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC)->CTX_SUFF(pTxSg);
+ Assert(e1kGetDescType(pDesc) == E1K_DTYP_DATA);
+ Assert(pDesc->data.cmd.fTSE);
+ Assert(!e1kXmitIsGsoBuf(pTxSg));
+#endif
+
+ uint16_t u16MaxPktLen = pThis->contextTSE.dw3.u8HDRLEN + pThis->contextTSE.dw3.u16MSS;
+ /* We cannot produce empty packets, ignore all TX descriptors (see @bugref{9571}) */
+ if (u16MaxPktLen == 0)
+ return VINF_SUCCESS;
+
+ /*
+ * Carve out segments.
+ */
+ int rc = VINF_SUCCESS;
+ do
+ {
+ /* Calculate how many bytes we have left in this TCP segment */
+ uint16_t cb = u16MaxPktLen - pThis->u16TxPktLen;
+ if (cb > pDesc->data.cmd.u20DTALEN)
+ {
+ /* This descriptor fits completely into current segment */
+ cb = (uint16_t)pDesc->data.cmd.u20DTALEN; /* u20DTALEN at this point is guarantied to fit into 16 bits. */
+ rc = e1kFallbackAddSegment(pDevIns, pThis, pDesc->data.u64BufAddr, cb, pDesc->data.cmd.fEOP /*fSend*/, fOnWorkerThread);
+ }
+ else
+ {
+ rc = e1kFallbackAddSegment(pDevIns, pThis, pDesc->data.u64BufAddr, cb, true /*fSend*/, fOnWorkerThread);
+ /*
+ * Rewind the packet tail pointer to the beginning of payload,
+ * so we continue writing right beyond the header.
+ */
+ pThis->u16TxPktLen = pThis->contextTSE.dw3.u8HDRLEN;
+ }
+
+ pDesc->data.u64BufAddr += cb;
+ pDesc->data.cmd.u20DTALEN -= cb;
+ } while (pDesc->data.cmd.u20DTALEN > 0 && RT_SUCCESS(rc));
+
+ if (pDesc->data.cmd.fEOP)
+ {
+ /* End of packet, next segment will contain header. */
+ if (pThis->u32PayRemain != 0)
+ E1K_INC_CNT32(TSCTFC);
+ pThis->u16TxPktLen = 0;
+ e1kXmitFreeBuf(pThis, PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC));
+ }
+
+ return VINF_SUCCESS; /// @todo consider rc;
+}
+#endif /* E1K_WITH_TXD_CACHE */
+
+
+/**
+ * Add descriptor's buffer to transmit frame.
+ *
+ * This deals with GSO and normal frames, e1kFallbackAddToFrame deals with the
+ * TSE frames we cannot handle as GSO.
+ *
+ * @returns true on success, false on failure.
+ *
+ * @param pDevIns The device instance.
+ * @param pThisCC The current context instance data.
+ * @param pThis The device state structure.
+ * @param PhysAddr The physical address of the descriptor buffer.
+ * @param cbFragment Length of descriptor's buffer.
+ * @thread E1000_TX
+ */
+static bool e1kAddToFrame(PPDMDEVINS pDevIns, PE1KSTATE pThis, PE1KSTATECC pThisCC, RTGCPHYS PhysAddr, uint32_t cbFragment)
+{
+ PPDMSCATTERGATHER pTxSg = pThisCC->CTX_SUFF(pTxSg);
+ bool const fGso = e1kXmitIsGsoBuf(pTxSg);
+ uint32_t const cbNewPkt = cbFragment + pThis->u16TxPktLen;
+
+ LogFlow(("%s e1kAddToFrame: ENTER cbFragment=%d u16TxPktLen=%d cbUsed=%d cbAvailable=%d fGSO=%s\n",
+ pThis->szPrf, cbFragment, pThis->u16TxPktLen, pTxSg->cbUsed, pTxSg->cbAvailable,
+ fGso ? "true" : "false"));
+ PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pTxSg->pvUser;
+ if (pGso)
+ {
+ if (RT_UNLIKELY(pGso->cbMaxSeg == 0))
+ {
+ E1kLog(("%s zero-sized fragments are not allowed\n", pThis->szPrf));
+ return false;
+ }
+ if (RT_UNLIKELY(pGso->u8Type == PDMNETWORKGSOTYPE_IPV4_UDP))
+ {
+ E1kLog(("%s UDP fragmentation is no longer supported\n", pThis->szPrf));
+ return false;
+ }
+ }
+ if (RT_UNLIKELY( !fGso && cbNewPkt > E1K_MAX_TX_PKT_SIZE ))
+ {
+ E1kLog(("%s Transmit packet is too large: %u > %u(max)\n", pThis->szPrf, cbNewPkt, E1K_MAX_TX_PKT_SIZE));
+ return false;
+ }
+ if (RT_UNLIKELY( cbNewPkt > pTxSg->cbAvailable ))
+ {
+ E1kLog(("%s Transmit packet is too large: %u > %u(max)\n", pThis->szPrf, cbNewPkt, pTxSg->cbAvailable));
+ return false;
+ }
+
+ if (RT_LIKELY(pTxSg))
+ {
+ Assert(pTxSg->cSegs == 1);
+ if (pTxSg->cbUsed != pThis->u16TxPktLen)
+ E1kLog(("%s e1kAddToFrame: pTxSg->cbUsed=%d(0x%x) != u16TxPktLen=%d(0x%x)\n",
+ pThis->szPrf, pTxSg->cbUsed, pTxSg->cbUsed, pThis->u16TxPktLen, pThis->u16TxPktLen));
+
+ PDMDevHlpPCIPhysRead(pDevIns, PhysAddr, (uint8_t *)pTxSg->aSegs[0].pvSeg + pThis->u16TxPktLen, cbFragment);
+
+ pTxSg->cbUsed = cbNewPkt;
+ }
+ pThis->u16TxPktLen = cbNewPkt;
+
+ return true;
+}
+
+
+/**
+ * Write the descriptor back to guest memory and notify the guest.
+ *
+ * @param pThis The device state structure.
+ * @param pDesc Pointer to the descriptor have been transmitted.
+ * @param addr Physical address of the descriptor in guest memory.
+ * @thread E1000_TX
+ */
+static void e1kDescReport(PPDMDEVINS pDevIns, PE1KSTATE pThis, E1KTXDESC *pDesc, RTGCPHYS addr)
+{
+ /*
+ * We fake descriptor write-back bursting. Descriptors are written back as they are
+ * processed.
+ */
+ /* Let's pretend we process descriptors. Write back with DD set. */
+ /*
+ * Prior to r71586 we tried to accomodate the case when write-back bursts
+ * are enabled without actually implementing bursting by writing back all
+ * descriptors, even the ones that do not have RS set. This caused kernel
+ * panics with Linux SMP kernels, as the e1000 driver tried to free up skb
+ * associated with written back descriptor if it happened to be a context
+ * descriptor since context descriptors do not have skb associated to them.
+ * Starting from r71586 we write back only the descriptors with RS set,
+ * which is a little bit different from what the real hardware does in
+ * case there is a chain of data descritors where some of them have RS set
+ * and others do not. It is very uncommon scenario imho.
+ * We need to check RPS as well since some legacy drivers use it instead of
+ * RS even with newer cards.
+ */
+ if (pDesc->legacy.cmd.fRS || pDesc->legacy.cmd.fRPS)
+ {
+ pDesc->legacy.dw3.fDD = 1; /* Descriptor Done */
+ e1kWriteBackDesc(pDevIns, pThis, pDesc, addr);
+ if (pDesc->legacy.cmd.fEOP)
+ {
+//#ifdef E1K_USE_TX_TIMERS
+ if (pThis->fTidEnabled && pDesc->legacy.cmd.fIDE)
+ {
+ E1K_INC_ISTAT_CNT(pThis->uStatTxIDE);
+ //if (pThis->fIntRaised)
+ //{
+ // /* Interrupt is already pending, no need for timers */
+ // ICR |= ICR_TXDW;
+ //}
+ //else {
+ /* Arm the timer to fire in TIVD usec (discard .024) */
+ e1kArmTimer(pDevIns, pThis, pThis->hTIDTimer, TIDV);
+# ifndef E1K_NO_TAD
+ /* If absolute timer delay is enabled and the timer is not running yet, arm it. */
+ E1kLog2(("%s Checking if TAD timer is running\n",
+ pThis->szPrf));
+ if (TADV != 0 && !PDMDevHlpTimerIsActive(pDevIns, pThis->hTADTimer))
+ e1kArmTimer(pDevIns, pThis, pThis->hTADTimer, TADV);
+# endif /* E1K_NO_TAD */
+ }
+ else
+ {
+ if (pThis->fTidEnabled)
+ {
+ E1kLog2(("%s No IDE set, cancel TAD timer and raise interrupt\n",
+ pThis->szPrf));
+ /* Cancel both timers if armed and fire immediately. */
+# ifndef E1K_NO_TAD
+ PDMDevHlpTimerStop(pDevIns, pThis->hTADTimer);
+# endif
+ PDMDevHlpTimerStop(pDevIns, pThis->hTIDTimer);
+ }
+//#endif /* E1K_USE_TX_TIMERS */
+ E1K_INC_ISTAT_CNT(pThis->uStatIntTx);
+ e1kRaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, ICR_TXDW);
+//#ifdef E1K_USE_TX_TIMERS
+ }
+//#endif /* E1K_USE_TX_TIMERS */
+ }
+ }
+ else
+ {
+ E1K_INC_ISTAT_CNT(pThis->uStatTxNoRS);
+ }
+}
+
+#ifndef E1K_WITH_TXD_CACHE
+
+/**
+ * Process Transmit Descriptor.
+ *
+ * E1000 supports three types of transmit descriptors:
+ * - legacy data descriptors of older format (context-less).
+ * - data the same as legacy but providing new offloading capabilities.
+ * - context sets up the context for following data descriptors.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param pThisCC The current context instance data.
+ * @param pDesc Pointer to descriptor union.
+ * @param addr Physical address of descriptor in guest memory.
+ * @param fOnWorkerThread Whether we're on a worker thread or an EMT.
+ * @thread E1000_TX
+ */
+static int e1kXmitDesc(PPDMDEVINS pDevIns, PE1KSTATE pThis, PE1KSTATECC pThisCC, E1KTXDESC *pDesc,
+ RTGCPHYS addr, bool fOnWorkerThread)
+{
+ int rc = VINF_SUCCESS;
+ uint32_t cbVTag = 0;
+
+ e1kPrintTDesc(pThis, pDesc, "vvv");
+
+//#ifdef E1K_USE_TX_TIMERS
+ if (pThis->fTidEnabled)
+ e1kCancelTimer(pDevIns, pThis, pThis->hTIDTimer);
+//#endif /* E1K_USE_TX_TIMERS */
+
+ switch (e1kGetDescType(pDesc))
+ {
+ case E1K_DTYP_CONTEXT:
+ if (pDesc->context.dw2.fTSE)
+ {
+ pThis->contextTSE = pDesc->context;
+ pThis->u32PayRemain = pDesc->context.dw2.u20PAYLEN;
+ pThis->u16HdrRemain = pDesc->context.dw3.u8HDRLEN;
+ e1kSetupGsoCtx(&pThis->GsoCtx, &pDesc->context);
+ STAM_COUNTER_INC(&pThis->StatTxDescCtxTSE);
+ }
+ else
+ {
+ pThis->contextNormal = pDesc->context;
+ STAM_COUNTER_INC(&pThis->StatTxDescCtxNormal);
+ }
+ E1kLog2(("%s %s context updated: IP CSS=%02X, IP CSO=%02X, IP CSE=%04X"
+ ", TU CSS=%02X, TU CSO=%02X, TU CSE=%04X\n", pThis->szPrf,
+ pDesc->context.dw2.fTSE ? "TSE" : "Normal",
+ pDesc->context.ip.u8CSS,
+ pDesc->context.ip.u8CSO,
+ pDesc->context.ip.u16CSE,
+ pDesc->context.tu.u8CSS,
+ pDesc->context.tu.u8CSO,
+ pDesc->context.tu.u16CSE));
+ E1K_INC_ISTAT_CNT(pThis->uStatDescCtx);
+ e1kDescReport(pThis, pDesc, addr);
+ break;
+
+ case E1K_DTYP_DATA:
+ {
+ if (pDesc->data.cmd.u20DTALEN == 0 || pDesc->data.u64BufAddr == 0)
+ {
+ E1kLog2(("% Empty data descriptor, skipped.\n", pThis->szPrf));
+ /** @todo Same as legacy when !TSE. See below. */
+ break;
+ }
+ STAM_COUNTER_INC(pDesc->data.cmd.fTSE?
+ &pThis->StatTxDescTSEData:
+ &pThis->StatTxDescData);
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ E1K_INC_ISTAT_CNT(pThis->uStatDescDat);
+
+ /*
+ * The last descriptor of non-TSE packet must contain VLE flag.
+ * TSE packets have VLE flag in the first descriptor. The later
+ * case is taken care of a bit later when cbVTag gets assigned.
+ *
+ * 1) pDesc->data.cmd.fEOP && !pDesc->data.cmd.fTSE
+ */
+ if (pDesc->data.cmd.fEOP && !pDesc->data.cmd.fTSE)
+ {
+ pThis->fVTag = pDesc->data.cmd.fVLE;
+ pThis->u16VTagTCI = pDesc->data.dw3.u16Special;
+ }
+ /*
+ * First fragment: Allocate new buffer and save the IXSM and TXSM
+ * packet options as these are only valid in the first fragment.
+ */
+ if (pThis->u16TxPktLen == 0)
+ {
+ pThis->fIPcsum = pDesc->data.dw3.fIXSM;
+ pThis->fTCPcsum = pDesc->data.dw3.fTXSM;
+ E1kLog2(("%s Saving checksum flags:%s%s; \n", pThis->szPrf,
+ pThis->fIPcsum ? " IP" : "",
+ pThis->fTCPcsum ? " TCP/UDP" : ""));
+ if (pDesc->data.cmd.fTSE)
+ {
+ /* 2) pDesc->data.cmd.fTSE && pThis->u16TxPktLen == 0 */
+ pThis->fVTag = pDesc->data.cmd.fVLE;
+ pThis->u16VTagTCI = pDesc->data.dw3.u16Special;
+ cbVTag = pThis->fVTag ? 4 : 0;
+ }
+ else if (pDesc->data.cmd.fEOP)
+ cbVTag = pDesc->data.cmd.fVLE ? 4 : 0;
+ else
+ cbVTag = 4;
+ E1kLog3(("%s About to allocate TX buffer: cbVTag=%u\n", pThis->szPrf, cbVTag));
+ if (e1kCanDoGso(pThis, &pThis->GsoCtx, &pDesc->data, &pThis->contextTSE))
+ rc = e1kXmitAllocBuf(pThis, pThisCC, pThis->contextTSE.dw2.u20PAYLEN + pThis->contextTSE.dw3.u8HDRLEN + cbVTag,
+ true /*fExactSize*/, true /*fGso*/);
+ else if (pDesc->data.cmd.fTSE)
+ rc = e1kXmitAllocBuf(pThis, pThisCC, , pThis->contextTSE.dw3.u16MSS + pThis->contextTSE.dw3.u8HDRLEN + cbVTag,
+ pDesc->data.cmd.fTSE /*fExactSize*/, false /*fGso*/);
+ else
+ rc = e1kXmitAllocBuf(pThis, pThisCC, pDesc->data.cmd.u20DTALEN + cbVTag,
+ pDesc->data.cmd.fEOP /*fExactSize*/, false /*fGso*/);
+
+ /**
+ * @todo: Perhaps it is not that simple for GSO packets! We may
+ * need to unwind some changes.
+ */
+ if (RT_FAILURE(rc))
+ {
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ break;
+ }
+ /** @todo Is there any way to indicating errors other than collisions? Like
+ * VERR_NET_DOWN. */
+ }
+
+ /*
+ * Add the descriptor data to the frame. If the frame is complete,
+ * transmit it and reset the u16TxPktLen field.
+ */
+ if (e1kXmitIsGsoBuf(pThisCC->CTX_SUFF(pTxSg)))
+ {
+ STAM_COUNTER_INC(&pThis->StatTxPathGSO);
+ bool fRc = e1kAddToFrame(pDevIns, pThis, pThisCC, pDesc->data.u64BufAddr, pDesc->data.cmd.u20DTALEN);
+ if (pDesc->data.cmd.fEOP)
+ {
+ if ( fRc
+ && pThisCC->CTX_SUFF(pTxSg)
+ && pThisCC->CTX_SUFF(pTxSg)->cbUsed == (size_t)pThis->contextTSE.dw3.u8HDRLEN + pThis->contextTSE.dw2.u20PAYLEN)
+ {
+ e1kTransmitFrame(pDevIns, pThis, pThisCC, fOnWorkerThread);
+ E1K_INC_CNT32(TSCTC);
+ }
+ else
+ {
+ if (fRc)
+ E1kLog(("%s bad GSO/TSE %p or %u < %u\n" , pThis->szPrf,
+ pThisCC->CTX_SUFF(pTxSg), pThisCC->CTX_SUFF(pTxSg) ? pThisCC->CTX_SUFF(pTxSg)->cbUsed : 0,
+ pThis->contextTSE.dw3.u8HDRLEN + pThis->contextTSE.dw2.u20PAYLEN));
+ e1kXmitFreeBuf(pThis);
+ E1K_INC_CNT32(TSCTFC);
+ }
+ pThis->u16TxPktLen = 0;
+ }
+ }
+ else if (!pDesc->data.cmd.fTSE)
+ {
+ STAM_COUNTER_INC(&pThis->StatTxPathRegular);
+ bool fRc = e1kAddToFrame(pDevIns, pThis, pThisCC, pDesc->data.u64BufAddr, pDesc->data.cmd.u20DTALEN);
+ if (pDesc->data.cmd.fEOP)
+ {
+ if (fRc && pThisCC->CTX_SUFF(pTxSg))
+ {
+ Assert(pThisCC->CTX_SUFF(pTxSg)->cSegs == 1);
+ if (pThis->fIPcsum)
+ e1kInsertChecksum(pThis, (uint8_t *)pThisCC->CTX_SUFF(pTxSg)->aSegs[0].pvSeg, pThis->u16TxPktLen,
+ pThis->contextNormal.ip.u8CSO,
+ pThis->contextNormal.ip.u8CSS,
+ pThis->contextNormal.ip.u16CSE);
+ if (pThis->fTCPcsum)
+ e1kInsertChecksum(pThis, (uint8_t *)pThisCC->CTX_SUFF(pTxSg)->aSegs[0].pvSeg, pThis->u16TxPktLen,
+ pThis->contextNormal.tu.u8CSO,
+ pThis->contextNormal.tu.u8CSS,
+ pThis->contextNormal.tu.u16CSE,
+ !pThis->contextNormal.dw2.fTCP);
+ e1kTransmitFrame(pDevIns, pThis, pThisCC, fOnWorkerThread);
+ }
+ else
+ e1kXmitFreeBuf(pThis);
+ pThis->u16TxPktLen = 0;
+ }
+ }
+ else
+ {
+ STAM_COUNTER_INC(&pThis->StatTxPathFallback);
+ e1kFallbackAddToFrame(pDevIns, pThis, pDesc, pDesc->data.cmd.u20DTALEN, fOnWorkerThread);
+ }
+
+ e1kDescReport(pThis, pDesc, addr);
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ break;
+ }
+
+ case E1K_DTYP_LEGACY:
+ if (pDesc->legacy.cmd.u16Length == 0 || pDesc->legacy.u64BufAddr == 0)
+ {
+ E1kLog(("%s Empty legacy descriptor, skipped.\n", pThis->szPrf));
+ /** @todo 3.3.3, Length/Buffer Address: RS set -> write DD when processing. */
+ break;
+ }
+ STAM_COUNTER_INC(&pThis->StatTxDescLegacy);
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatTransmit), a);
+
+ /* First fragment: allocate new buffer. */
+ if (pThis->u16TxPktLen == 0)
+ {
+ if (pDesc->legacy.cmd.fEOP)
+ cbVTag = pDesc->legacy.cmd.fVLE ? 4 : 0;
+ else
+ cbVTag = 4;
+ E1kLog3(("%s About to allocate TX buffer: cbVTag=%u\n", pThis->szPrf, cbVTag));
+ /** @todo reset status bits? */
+ rc = e1kXmitAllocBuf(pThis, pThisCC, pDesc->legacy.cmd.u16Length + cbVTag, pDesc->legacy.cmd.fEOP, false /*fGso*/);
+ if (RT_FAILURE(rc))
+ {
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ break;
+ }
+
+ /** @todo Is there any way to indicating errors other than collisions? Like
+ * VERR_NET_DOWN. */
+ }
+
+ /* Add fragment to frame. */
+ if (e1kAddToFrame(pDevIns, pThis, pThisCC, pDesc->data.u64BufAddr, pDesc->legacy.cmd.u16Length))
+ {
+ E1K_INC_ISTAT_CNT(pThis->uStatDescLeg);
+
+ /* Last fragment: Transmit and reset the packet storage counter. */
+ if (pDesc->legacy.cmd.fEOP)
+ {
+ pThis->fVTag = pDesc->legacy.cmd.fVLE;
+ pThis->u16VTagTCI = pDesc->legacy.dw3.u16Special;
+ /** @todo Offload processing goes here. */
+ e1kTransmitFrame(pDevIns, pThis, pThisCC, fOnWorkerThread);
+ pThis->u16TxPktLen = 0;
+ }
+ }
+ /* Last fragment + failure: free the buffer and reset the storage counter. */
+ else if (pDesc->legacy.cmd.fEOP)
+ {
+ e1kXmitFreeBuf(pThis);
+ pThis->u16TxPktLen = 0;
+ }
+
+ e1kDescReport(pThis, pDesc, addr);
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ break;
+
+ default:
+ E1kLog(("%s ERROR Unsupported transmit descriptor type: 0x%04x\n",
+ pThis->szPrf, e1kGetDescType(pDesc)));
+ break;
+ }
+
+ return rc;
+}
+
+#else /* E1K_WITH_TXD_CACHE */
+
+/**
+ * Process Transmit Descriptor.
+ *
+ * E1000 supports three types of transmit descriptors:
+ * - legacy data descriptors of older format (context-less).
+ * - data the same as legacy but providing new offloading capabilities.
+ * - context sets up the context for following data descriptors.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param pThisCC The current context instance data.
+ * @param pDesc Pointer to descriptor union.
+ * @param addr Physical address of descriptor in guest memory.
+ * @param fOnWorkerThread Whether we're on a worker thread or an EMT.
+ * @param cbPacketSize Size of the packet as previously computed.
+ * @thread E1000_TX
+ */
+static int e1kXmitDesc(PPDMDEVINS pDevIns, PE1KSTATE pThis, PE1KSTATECC pThisCC, E1KTXDESC *pDesc,
+ RTGCPHYS addr, bool fOnWorkerThread)
+{
+ int rc = VINF_SUCCESS;
+
+ e1kPrintTDesc(pThis, pDesc, "vvv");
+
+//#ifdef E1K_USE_TX_TIMERS
+ if (pThis->fTidEnabled)
+ PDMDevHlpTimerStop(pDevIns, pThis->hTIDTimer);
+//#endif /* E1K_USE_TX_TIMERS */
+
+ switch (e1kGetDescType(pDesc))
+ {
+ case E1K_DTYP_CONTEXT:
+ /* The caller have already updated the context */
+ E1K_INC_ISTAT_CNT(pThis->uStatDescCtx);
+ e1kDescReport(pDevIns, pThis, pDesc, addr);
+ break;
+
+ case E1K_DTYP_DATA:
+ {
+ STAM_COUNTER_INC(pDesc->data.cmd.fTSE?
+ &pThis->StatTxDescTSEData:
+ &pThis->StatTxDescData);
+ E1K_INC_ISTAT_CNT(pThis->uStatDescDat);
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ if (pDesc->data.cmd.u20DTALEN == 0 || pDesc->data.u64BufAddr == 0)
+ {
+ E1kLog2(("%s Empty data descriptor, skipped.\n", pThis->szPrf));
+ if (pDesc->data.cmd.fEOP)
+ {
+ e1kTransmitFrame(pDevIns, pThis, pThisCC, fOnWorkerThread);
+ pThis->u16TxPktLen = 0;
+ }
+ }
+ else
+ {
+ /*
+ * Add the descriptor data to the frame. If the frame is complete,
+ * transmit it and reset the u16TxPktLen field.
+ */
+ if (e1kXmitIsGsoBuf(pThisCC->CTX_SUFF(pTxSg)))
+ {
+ STAM_COUNTER_INC(&pThis->StatTxPathGSO);
+ bool fRc = e1kAddToFrame(pDevIns, pThis, pThisCC, pDesc->data.u64BufAddr, pDesc->data.cmd.u20DTALEN);
+ if (pDesc->data.cmd.fEOP)
+ {
+ if ( fRc
+ && pThisCC->CTX_SUFF(pTxSg)
+ && pThisCC->CTX_SUFF(pTxSg)->cbUsed == (size_t)pThis->contextTSE.dw3.u8HDRLEN + pThis->contextTSE.dw2.u20PAYLEN)
+ {
+ e1kTransmitFrame(pDevIns, pThis, pThisCC, fOnWorkerThread);
+ E1K_INC_CNT32(TSCTC);
+ }
+ else
+ {
+ if (fRc)
+ E1kLog(("%s bad GSO/TSE %p or %u < %u\n" , pThis->szPrf,
+ pThisCC->CTX_SUFF(pTxSg), pThisCC->CTX_SUFF(pTxSg) ? pThisCC->CTX_SUFF(pTxSg)->cbUsed : 0,
+ pThis->contextTSE.dw3.u8HDRLEN + pThis->contextTSE.dw2.u20PAYLEN));
+ e1kXmitFreeBuf(pThis, pThisCC);
+ E1K_INC_CNT32(TSCTFC);
+ }
+ pThis->u16TxPktLen = 0;
+ }
+ }
+ else if (!pDesc->data.cmd.fTSE)
+ {
+ STAM_COUNTER_INC(&pThis->StatTxPathRegular);
+ bool fRc = e1kAddToFrame(pDevIns, pThis, pThisCC, pDesc->data.u64BufAddr, pDesc->data.cmd.u20DTALEN);
+ if (pDesc->data.cmd.fEOP)
+ {
+ if (fRc && pThisCC->CTX_SUFF(pTxSg))
+ {
+ Assert(pThisCC->CTX_SUFF(pTxSg)->cSegs == 1);
+ if (pThis->fIPcsum)
+ e1kInsertChecksum(pThis, (uint8_t *)pThisCC->CTX_SUFF(pTxSg)->aSegs[0].pvSeg, pThis->u16TxPktLen,
+ pThis->contextNormal.ip.u8CSO,
+ pThis->contextNormal.ip.u8CSS,
+ pThis->contextNormal.ip.u16CSE);
+ if (pThis->fTCPcsum)
+ e1kInsertChecksum(pThis, (uint8_t *)pThisCC->CTX_SUFF(pTxSg)->aSegs[0].pvSeg, pThis->u16TxPktLen,
+ pThis->contextNormal.tu.u8CSO,
+ pThis->contextNormal.tu.u8CSS,
+ pThis->contextNormal.tu.u16CSE,
+ !pThis->contextNormal.dw2.fTCP);
+ e1kTransmitFrame(pDevIns, pThis, pThisCC, fOnWorkerThread);
+ }
+ else
+ e1kXmitFreeBuf(pThis, pThisCC);
+ pThis->u16TxPktLen = 0;
+ }
+ }
+ else
+ {
+ STAM_COUNTER_INC(&pThis->StatTxPathFallback);
+ rc = e1kFallbackAddToFrame(pDevIns, pThis, pDesc, fOnWorkerThread);
+ }
+ }
+ e1kDescReport(pDevIns, pThis, pDesc, addr);
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ break;
+ }
+
+ case E1K_DTYP_LEGACY:
+ STAM_COUNTER_INC(&pThis->StatTxDescLegacy);
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ if (pDesc->legacy.cmd.u16Length == 0 || pDesc->legacy.u64BufAddr == 0)
+ {
+ E1kLog(("%s Empty legacy descriptor, skipped.\n", pThis->szPrf));
+ if (pDesc->data.cmd.fEOP)
+ {
+ e1kTransmitFrame(pDevIns, pThis, pThisCC, fOnWorkerThread);
+ pThis->u16TxPktLen = 0;
+ }
+ }
+ else
+ {
+ /* Add fragment to frame. */
+ if (e1kAddToFrame(pDevIns, pThis, pThisCC, pDesc->data.u64BufAddr, pDesc->legacy.cmd.u16Length))
+ {
+ E1K_INC_ISTAT_CNT(pThis->uStatDescLeg);
+
+ /* Last fragment: Transmit and reset the packet storage counter. */
+ if (pDesc->legacy.cmd.fEOP)
+ {
+ if (pDesc->legacy.cmd.fIC)
+ {
+ e1kInsertChecksum(pThis,
+ (uint8_t *)pThisCC->CTX_SUFF(pTxSg)->aSegs[0].pvSeg,
+ pThis->u16TxPktLen,
+ pDesc->legacy.cmd.u8CSO,
+ pDesc->legacy.dw3.u8CSS,
+ 0);
+ }
+ e1kTransmitFrame(pDevIns, pThis, pThisCC, fOnWorkerThread);
+ pThis->u16TxPktLen = 0;
+ }
+ }
+ /* Last fragment + failure: free the buffer and reset the storage counter. */
+ else if (pDesc->legacy.cmd.fEOP)
+ {
+ e1kXmitFreeBuf(pThis, pThisCC);
+ pThis->u16TxPktLen = 0;
+ }
+ }
+ e1kDescReport(pDevIns, pThis, pDesc, addr);
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ break;
+
+ default:
+ E1kLog(("%s ERROR Unsupported transmit descriptor type: 0x%04x\n",
+ pThis->szPrf, e1kGetDescType(pDesc)));
+ break;
+ }
+
+ return rc;
+}
+
+DECLINLINE(bool) e1kUpdateTxContext(PE1KSTATE pThis, E1KTXDESC *pDesc)
+{
+ if (pDesc->context.dw2.fTSE)
+ {
+ if (!e1kSetupGsoCtx(&pThis->GsoCtx, &pDesc->context))
+ {
+ pThis->contextTSE.dw2.u4DTYP = E1K_DTYP_INVALID;
+ return false;
+ }
+ pThis->contextTSE = pDesc->context;
+ uint32_t cbMaxSegmentSize = pThis->contextTSE.dw3.u16MSS + pThis->contextTSE.dw3.u8HDRLEN + 4; /*VTAG*/
+ if (RT_UNLIKELY(cbMaxSegmentSize > E1K_MAX_TX_PKT_SIZE))
+ {
+ pThis->contextTSE.dw3.u16MSS = E1K_MAX_TX_PKT_SIZE - pThis->contextTSE.dw3.u8HDRLEN - 4; /*VTAG*/
+ LogRelMax(10, ("%s: Transmit packet is too large: %u > %u(max). Adjusted MSS to %u.\n",
+ pThis->szPrf, cbMaxSegmentSize, E1K_MAX_TX_PKT_SIZE, pThis->contextTSE.dw3.u16MSS));
+ }
+ pThis->u32PayRemain = pThis->contextTSE.dw2.u20PAYLEN;
+ pThis->u16HdrRemain = pThis->contextTSE.dw3.u8HDRLEN;
+ e1kSetupGsoCtx(&pThis->GsoCtx, &pThis->contextTSE);
+ STAM_COUNTER_INC(&pThis->StatTxDescCtxTSE);
+ }
+ else
+ {
+ pThis->contextNormal = pDesc->context;
+ STAM_COUNTER_INC(&pThis->StatTxDescCtxNormal);
+ }
+ E1kLog2(("%s %s context updated: IP CSS=%02X, IP CSO=%02X, IP CSE=%04X"
+ ", TU CSS=%02X, TU CSO=%02X, TU CSE=%04X\n", pThis->szPrf,
+ pDesc->context.dw2.fTSE ? "TSE" : "Normal",
+ pDesc->context.ip.u8CSS,
+ pDesc->context.ip.u8CSO,
+ pDesc->context.ip.u16CSE,
+ pDesc->context.tu.u8CSS,
+ pDesc->context.tu.u8CSO,
+ pDesc->context.tu.u16CSE));
+ return true; /* Consider returning false for invalid descriptors */
+}
+
+enum E1kPacketType
+{
+ E1K_PACKET_NONE = 0,
+ E1K_PACKET_LEGACY,
+ E1K_PACKET_NORMAL,
+ E1K_PACKET_TSE
+};
+
+static int e1kLocateTxPacket(PE1KSTATE pThis, PE1KTXDC pTxdc)
+{
+ LogFlow(("%s e1kLocateTxPacket: ENTER cbTxAlloc=%d\n",
+ pThis->szPrf, pThis->cbTxAlloc));
+ /* Check if we have located the packet already. */
+ if (pThis->cbTxAlloc)
+ {
+ LogFlow(("%s e1kLocateTxPacket: RET true cbTxAlloc=%d\n",
+ pThis->szPrf, pThis->cbTxAlloc));
+ return true;
+ }
+
+ pThis->fGSO = false;
+ pThis->fVTag = false;
+ pThis->fIPcsum = false;
+ pThis->fTCPcsum = false;
+ pThis->u16TxPktLen = 0;
+
+ enum E1kPacketType packetType = E1K_PACKET_NONE;
+ enum E1kPacketType expectedPacketType = E1K_PACKET_NONE;
+ /*
+ * Valid packets start with 1 or 0 context descriptors, followed by 1 or
+ * more data descriptors of the same type: legacy, normal or TSE. Note
+ * that legacy descriptors do not belong to neither normal nor segmentation
+ * contexts rendering the sequence (context_descriptor, legacy_descriptor)
+ * invalid, but the context descriptor will still be applied and the legacy
+ * descriptor will be treated as the beginning of next packet.
+ */
+ bool fInvalidPacket = false;
+ bool fTSE = false;
+ uint32_t cbPacket = 0;
+
+ /* Since we process one packet at a time we will only mark current packet's descriptors as valid */
+ memset(pThis->afTxDValid, 0, sizeof(pThis->afTxDValid));
+ for (int i = pThis->iTxDCurrent; i < pThis->nTxDFetched; ++i)
+ {
+ E1KTXDESC *pDesc = &pThis->aTxDescriptors[i];
+
+ switch (e1kGetDescType(pDesc))
+ {
+ case E1K_DTYP_CONTEXT:
+ /* There can be only one context per packet. Each context descriptor starts a new packet. */
+ if (packetType != E1K_PACKET_NONE)
+ {
+ fInvalidPacket = true;
+ break;
+ }
+ packetType = (pDesc->context.dw2.fTSE) ? E1K_PACKET_TSE : E1K_PACKET_NORMAL;
+ if (cbPacket == 0)
+ pThis->afTxDValid[i] = e1kUpdateTxContext(pThis, pDesc);
+ else
+ E1kLog(("%s e1kLocateTxPacket: ignoring a context descriptor in the middle of a packet, cbPacket=%d\n",
+ pThis->szPrf, cbPacket));
+ continue;
+ case E1K_DTYP_LEGACY:
+ if (packetType != E1K_PACKET_NONE && packetType != E1K_PACKET_LEGACY)
+ {
+ fInvalidPacket = true;
+ break;
+ }
+ packetType = E1K_PACKET_LEGACY;
+ /* Skip invalid descriptors. */
+ if (cbPacket > 0 && (pThis->fGSO || fTSE))
+ {
+ E1kLog(("%s e1kLocateTxPacket: ignoring a legacy descriptor in the segmentation context, cbPacket=%d\n",
+ pThis->szPrf, cbPacket));
+ continue;
+ }
+ pThis->afTxDValid[i] = true; /* Passed all checks, process it */
+
+ /* Skip empty descriptors. */
+ if (!pDesc->legacy.u64BufAddr || !pDesc->legacy.cmd.u16Length)
+ break;
+ cbPacket += pDesc->legacy.cmd.u16Length;
+ pThis->fGSO = false;
+ break;
+ case E1K_DTYP_DATA:
+ expectedPacketType = pDesc->data.cmd.fTSE ? E1K_PACKET_TSE : E1K_PACKET_NORMAL;
+ if (packetType != E1K_PACKET_NONE && packetType != expectedPacketType)
+ {
+ fInvalidPacket = true;
+ break;
+ }
+ /* Skip invalid descriptors. */
+ if (pDesc->data.cmd.fTSE)
+ {
+ if (pThis->contextTSE.dw2.u4DTYP == E1K_DTYP_INVALID)
+ {
+ E1kLog(("%s e1kLocateTxPacket: ignoring TSE descriptor in invalid segmentation context, cbPacket=%d\n",
+ pThis->szPrf, cbPacket));
+ continue;
+ }
+ }
+ else /* !TSE */
+ {
+ if (pThis->contextNormal.dw2.u4DTYP == E1K_DTYP_INVALID)
+ {
+ E1kLog(("%s e1kLocateTxPacket: ignoring non-TSE descriptor in invalid normal context, cbPacket=%d\n",
+ pThis->szPrf, cbPacket));
+ continue;
+ }
+ }
+ if (cbPacket > 0 && (bool)pDesc->data.cmd.fTSE != fTSE)
+ {
+ E1kLog(("%s e1kLocateTxPacket: ignoring %sTSE descriptor in the %ssegmentation context, cbPacket=%d\n",
+ pThis->szPrf, pDesc->data.cmd.fTSE ? "" : "non-", fTSE ? "" : "non-", cbPacket));
+ continue;
+ }
+ pThis->afTxDValid[i] = true; /* Passed all checks, process it */
+
+ /* Skip empty descriptors. */
+ if (!pDesc->data.u64BufAddr || !pDesc->data.cmd.u20DTALEN)
+ break;
+ if (cbPacket == 0)
+ {
+ /*
+ * The first fragment: save IXSM and TXSM options
+ * as these are only valid in the first fragment.
+ */
+ pThis->fIPcsum = pDesc->data.dw3.fIXSM;
+ pThis->fTCPcsum = pDesc->data.dw3.fTXSM;
+ fTSE = pDesc->data.cmd.fTSE;
+ /*
+ * TSE descriptors have VLE bit properly set in
+ * the first fragment.
+ */
+ if (fTSE)
+ {
+ pThis->fVTag = pDesc->data.cmd.fVLE;
+ pThis->u16VTagTCI = pDesc->data.dw3.u16Special;
+ }
+ pThis->fGSO = e1kCanDoGso(pThis, &pThis->GsoCtx, &pDesc->data, &pThis->contextTSE);
+ }
+ cbPacket += pDesc->data.cmd.u20DTALEN;
+ break;
+ default:
+ AssertMsgFailed(("Impossible descriptor type!"));
+ continue;
+ }
+ if (fInvalidPacket)
+ {
+ for (int index = pThis->iTxDCurrent; index < i; ++index)
+ pThis->afTxDValid[index] = false; /* Make sure all descriptors for this packet are skipped by processing */
+ LogFlow(("%s e1kLocateTxPacket: marked %d descriptors as invalid\n", pThis->szPrf, i - pThis->iTxDCurrent));
+ LogFlow(("%s e1kLocateTxPacket: RET true cbTxAlloc=%d cbPacket=%d%s%s\n",
+ pThis->szPrf, pThis->cbTxAlloc, cbPacket,
+ pThis->fGSO ? " GSO" : "", fTSE ? " TSE" : ""));
+ pTxdc->nextPacket = i;
+ return true;
+ }
+ if (pDesc->legacy.cmd.fEOP)
+ {
+ /*
+ * Non-TSE descriptors have VLE bit properly set in
+ * the last fragment.
+ */
+ if (!fTSE)
+ {
+ pThis->fVTag = pDesc->data.cmd.fVLE;
+ pThis->u16VTagTCI = pDesc->data.dw3.u16Special;
+ }
+ /*
+ * Compute the required buffer size. If we cannot do GSO but still
+ * have to do segmentation we allocate the first segment only.
+ */
+ pThis->cbTxAlloc = (!fTSE || pThis->fGSO) ?
+ cbPacket :
+ RT_MIN(cbPacket, pThis->contextTSE.dw3.u16MSS + pThis->contextTSE.dw3.u8HDRLEN);
+ /* Do not add VLAN tags to empty packets. */
+ if (pThis->fVTag && pThis->cbTxAlloc > 0)
+ pThis->cbTxAlloc += 4;
+ LogFlow(("%s e1kLocateTxPacket: RET true cbTxAlloc=%d cbPacket=%d%s%s\n",
+ pThis->szPrf, pThis->cbTxAlloc, cbPacket,
+ pThis->fGSO ? " GSO" : "", fTSE ? " TSE" : ""));
+ pTxdc->nextPacket = i + 1;
+ return true;
+ }
+ }
+
+ if (cbPacket == 0 && pThis->nTxDFetched - pThis->iTxDCurrent > 0)
+ {
+ /* All descriptors were empty, we need to process them as a dummy packet */
+ LogFlow(("%s e1kLocateTxPacket: RET true cbTxAlloc=%d, zero packet!\n",
+ pThis->szPrf, pThis->cbTxAlloc));
+ pTxdc->nextPacket = pThis->nTxDFetched;
+ return true;
+ }
+ LogFlow(("%s e1kLocateTxPacket: RET false cbTxAlloc=%d cbPacket=%d\n",
+ pThis->szPrf, pThis->cbTxAlloc, cbPacket));
+ return false;
+}
+
+static int e1kXmitPacket(PPDMDEVINS pDevIns, PE1KSTATE pThis, bool fOnWorkerThread, PE1KTXDC pTxdc)
+{
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("%s e1kXmitPacket: ENTER current=%d fetched=%d\n",
+ pThis->szPrf, pThis->iTxDCurrent, pThis->nTxDFetched));
+
+ while (pThis->iTxDCurrent < pTxdc->nextPacket && pThis->iTxDCurrent < pThis->nTxDFetched)
+ {
+ E1KTXDESC *pDesc = &pThis->aTxDescriptors[pThis->iTxDCurrent];
+ E1kLog3(("%s About to process new TX descriptor at %08x%08x, TDLEN=%08x, TDH=%08x, TDT=%08x\n",
+ pThis->szPrf, TDBAH, TDBAL + pTxdc->tdh * sizeof(E1KTXDESC), pTxdc->tdlen, pTxdc->tdh, pTxdc->tdt));
+ if (!pThis->afTxDValid[pThis->iTxDCurrent])
+ {
+ e1kPrintTDesc(pThis, pDesc, "vvv");
+ E1kLog(("%s e1kXmitDesc: skipping bad descriptor ^^^\n", pThis->szPrf));
+ e1kDescReport(pDevIns, pThis, pDesc, e1kDescAddr(TDBAH, TDBAL, pTxdc->tdh));
+ rc = VINF_SUCCESS;
+ }
+ else
+ rc = e1kXmitDesc(pDevIns, pThis, pThisCC, pDesc, e1kDescAddr(TDBAH, TDBAL, pTxdc->tdh), fOnWorkerThread);
+ if (RT_FAILURE(rc))
+ break;
+ if (++pTxdc->tdh * sizeof(E1KTXDESC) >= pTxdc->tdlen)
+ pTxdc->tdh = 0;
+ TDH = pTxdc->tdh; /* Sync the actual register and TXDC */
+ uint32_t uLowThreshold = GET_BITS(TXDCTL, LWTHRESH)*8;
+ if (uLowThreshold != 0 && e1kGetTxLen(pTxdc) <= uLowThreshold)
+ {
+ E1kLog2(("%s Low on transmit descriptors, raise ICR.TXD_LOW, len=%x thresh=%x\n",
+ pThis->szPrf, e1kGetTxLen(pTxdc), GET_BITS(TXDCTL, LWTHRESH)*8));
+ e1kRaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, ICR_TXD_LOW);
+ }
+ ++pThis->iTxDCurrent;
+ if (e1kGetDescType(pDesc) != E1K_DTYP_CONTEXT && pDesc->legacy.cmd.fEOP)
+ break;
+ }
+
+ LogFlow(("%s e1kXmitPacket: RET %Rrc current=%d fetched=%d\n",
+ pThis->szPrf, rc, pThis->iTxDCurrent, pThis->nTxDFetched));
+ return rc;
+}
+
+#endif /* E1K_WITH_TXD_CACHE */
+#ifndef E1K_WITH_TXD_CACHE
+
+/**
+ * Transmit pending descriptors.
+ *
+ * @returns VBox status code. VERR_TRY_AGAIN is returned if we're busy.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The E1000 state.
+ * @param fOnWorkerThread Whether we're on a worker thread or on an EMT.
+ */
+static int e1kXmitPending(PPDMDEVINS pDevIns, PE1KSTATE pThis, bool fOnWorkerThread)
+{
+ int rc = VINF_SUCCESS;
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+
+ /* Check if transmitter is enabled. */
+ if (!(TCTL & TCTL_EN))
+ return VINF_SUCCESS;
+ /*
+ * Grab the xmit lock of the driver as well as the E1K device state.
+ */
+ rc = e1kCsTxEnter(pThis, VERR_SEM_BUSY);
+ if (RT_LIKELY(rc == VINF_SUCCESS))
+ {
+ PPDMINETWORKUP pDrv = pThis->CTX_SUFF(pDrv);
+ if (pDrv)
+ {
+ rc = pDrv->pfnBeginXmit(pDrv, fOnWorkerThread);
+ if (RT_FAILURE(rc))
+ {
+ e1kCsTxLeave(pThis);
+ return rc;
+ }
+ }
+ /*
+ * Process all pending descriptors.
+ * Note! Do not process descriptors in locked state
+ */
+ while (TDH != TDT && !pThis->fLocked)
+ {
+ E1KTXDESC desc;
+ E1kLog3(("%s About to process new TX descriptor at %08x%08x, TDLEN=%08x, TDH=%08x, TDT=%08x\n",
+ pThis->szPrf, TDBAH, TDBAL + TDH * sizeof(desc), TDLEN, TDH, TDT));
+
+ e1kLoadDesc(pDevIns, &desc, ((uint64_t)TDBAH << 32) + TDBAL + TDH * sizeof(desc));
+ rc = e1kXmitDesc(pDevIns, pThis, pThisCC, &desc, e1kDescAddr(TDBAH, TDBAL, TDH), fOnWorkerThread);
+ /* If we failed to transmit descriptor we will try it again later */
+ if (RT_FAILURE(rc))
+ break;
+ if (++TDH * sizeof(desc) >= TDLEN)
+ TDH = 0;
+
+ if (e1kGetTxLen(pThis) <= GET_BITS(TXDCTL, LWTHRESH)*8)
+ {
+ E1kLog2(("%s Low on transmit descriptors, raise ICR.TXD_LOW, len=%x thresh=%x\n",
+ pThis->szPrf, e1kGetTxLen(pThis), GET_BITS(TXDCTL, LWTHRESH)*8));
+ e1kRaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, ICR_TXD_LOW);
+ }
+
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ }
+
+ /// @todo uncomment: pThis->uStatIntTXQE++;
+ /// @todo uncomment: e1kRaiseInterrupt(pDevIns, pThis, ICR_TXQE);
+ /*
+ * Release the lock.
+ */
+ if (pDrv)
+ pDrv->pfnEndXmit(pDrv);
+ e1kCsTxLeave(pThis);
+ }
+
+ return rc;
+}
+
+#else /* E1K_WITH_TXD_CACHE */
+
+static void e1kDumpTxDCache(PPDMDEVINS pDevIns, PE1KSTATE pThis, PE1KTXDC pTxdc)
+{
+ unsigned i, cDescs = pTxdc->tdlen / sizeof(E1KTXDESC);
+ uint32_t tdh = pTxdc->tdh;
+ LogRel(("E1000: -- Transmit Descriptors (%d total) --\n", cDescs));
+ for (i = 0; i < cDescs; ++i)
+ {
+ E1KTXDESC desc;
+ PDMDevHlpPCIPhysRead(pDevIns , e1kDescAddr(TDBAH, TDBAL, i), &desc, sizeof(desc));
+ if (i == tdh)
+ LogRel(("E1000: >>> "));
+ LogRel(("E1000: %RGp: %R[e1ktxd]\n", e1kDescAddr(TDBAH, TDBAL, i), &desc));
+ }
+ LogRel(("E1000: -- Transmit Descriptors in Cache (at %d (TDH %d)/ fetched %d / max %d) --\n",
+ pThis->iTxDCurrent, pTxdc->tdh, pThis->nTxDFetched, E1K_TXD_CACHE_SIZE));
+ if (tdh > pThis->iTxDCurrent)
+ tdh -= pThis->iTxDCurrent;
+ else
+ tdh = cDescs + tdh - pThis->iTxDCurrent;
+ for (i = 0; i < pThis->nTxDFetched; ++i)
+ {
+ if (i == pThis->iTxDCurrent)
+ LogRel(("E1000: >>> "));
+ if (cDescs)
+ LogRel(("E1000: %RGp: %R[e1ktxd]\n", e1kDescAddr(TDBAH, TDBAL, tdh++ % cDescs), &pThis->aTxDescriptors[i]));
+ else
+ LogRel(("E1000: <lost>: %R[e1ktxd]\n", &pThis->aTxDescriptors[i]));
+ }
+}
+
+/**
+ * Transmit pending descriptors.
+ *
+ * @returns VBox status code. VERR_TRY_AGAIN is returned if we're busy.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The E1000 state.
+ * @param fOnWorkerThread Whether we're on a worker thread or on an EMT.
+ */
+static int e1kXmitPending(PPDMDEVINS pDevIns, PE1KSTATE pThis, bool fOnWorkerThread)
+{
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+ int rc = VINF_SUCCESS;
+
+ /* Check if transmitter is enabled. */
+ if (!(TCTL & TCTL_EN))
+ return VINF_SUCCESS;
+ /*
+ * Grab the xmit lock of the driver as well as the E1K device state.
+ */
+ PPDMINETWORKUP pDrv = pThisCC->CTX_SUFF(pDrv);
+ if (pDrv)
+ {
+ rc = pDrv->pfnBeginXmit(pDrv, fOnWorkerThread);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ /*
+ * Process all pending descriptors.
+ * Note! Do not process descriptors in locked state
+ */
+ rc = e1kCsTxEnter(pThis, VERR_SEM_BUSY);
+ if (RT_LIKELY(rc == VINF_SUCCESS && (TCTL & TCTL_EN)))
+ {
+ E1KTXDC txdc;
+ bool fTxContextValid = e1kUpdateTxDContext(pDevIns, pThis, &txdc);
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ /*
+ * fIncomplete is set whenever we try to fetch additional descriptors
+ * for an incomplete packet. If fail to locate a complete packet on
+ * the next iteration we need to reset the cache or we risk to get
+ * stuck in this loop forever.
+ */
+ bool fIncomplete = false;
+ while (fTxContextValid && !pThis->fLocked && e1kTxDLazyLoad(pDevIns, pThis, &txdc))
+ {
+ while (e1kLocateTxPacket(pThis, &txdc))
+ {
+ Log4(("%s e1kXmitPending: Located packet at %d. Next packet at %d\n",
+ pThis->szPrf, pThis->iTxDCurrent, txdc.nextPacket));
+ fIncomplete = false;
+ /* Found a complete packet, allocate it. */
+ rc = e1kXmitAllocBuf(pThis, pThisCC, pThis->fGSO);
+ /* If we're out of bandwidth we'll come back later. */
+ if (RT_FAILURE(rc))
+ goto out;
+ /* Copy the packet to allocated buffer and send it. */
+ rc = e1kXmitPacket(pDevIns, pThis, fOnWorkerThread, &txdc);
+ /* If we're out of bandwidth we'll come back later. */
+ if (RT_FAILURE(rc))
+ goto out;
+ }
+ uint8_t u8Remain = pThis->nTxDFetched - pThis->iTxDCurrent;
+ if (RT_UNLIKELY(fIncomplete))
+ {
+ static bool fTxDCacheDumped = false;
+ /*
+ * The descriptor cache is full, but we were unable to find
+ * a complete packet in it. Drop the cache and hope that
+ * the guest driver can recover from network card error.
+ */
+ LogRel(("%s: No complete packets in%s TxD cache! "
+ "Fetched=%d, current=%d, TX len=%d.\n",
+ pThis->szPrf,
+ u8Remain == E1K_TXD_CACHE_SIZE ? " full" : "",
+ pThis->nTxDFetched, pThis->iTxDCurrent,
+ e1kGetTxLen(&txdc)));
+ if (!fTxDCacheDumped)
+ {
+ fTxDCacheDumped = true;
+ e1kDumpTxDCache(pDevIns, pThis, &txdc);
+ }
+ pThis->iTxDCurrent = pThis->nTxDFetched = 0;
+ /*
+ * Returning an error at this point means Guru in R0
+ * (see @bugref{6428}).
+ */
+# ifdef IN_RING3
+ rc = VERR_NET_INCOMPLETE_TX_PACKET;
+# else /* !IN_RING3 */
+ rc = VINF_IOM_R3_MMIO_WRITE;
+# endif /* !IN_RING3 */
+ goto out;
+ }
+ if (u8Remain > 0)
+ {
+ Log4(("%s Incomplete packet at %d. Already fetched %d, "
+ "%d more are available\n",
+ pThis->szPrf, pThis->iTxDCurrent, u8Remain,
+ e1kGetTxLen(&txdc) - u8Remain));
+
+ /*
+ * A packet was partially fetched. Move incomplete packet to
+ * the beginning of cache buffer, then load more descriptors.
+ */
+ memmove(pThis->aTxDescriptors,
+ &pThis->aTxDescriptors[pThis->iTxDCurrent],
+ u8Remain * sizeof(E1KTXDESC));
+ pThis->iTxDCurrent = 0;
+ pThis->nTxDFetched = u8Remain;
+ e1kTxDLoadMore(pDevIns, pThis, &txdc);
+ fIncomplete = true;
+ }
+ else
+ pThis->nTxDFetched = 0;
+ pThis->iTxDCurrent = 0;
+ }
+ if (!pThis->fLocked && GET_BITS(TXDCTL, LWTHRESH) == 0)
+ {
+ E1kLog2(("%s Out of transmit descriptors, raise ICR.TXD_LOW\n",
+ pThis->szPrf));
+ e1kRaiseInterrupt(pDevIns, pThis, VERR_SEM_BUSY, ICR_TXD_LOW);
+ }
+out:
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
+
+ /// @todo uncomment: pThis->uStatIntTXQE++;
+ /// @todo uncomment: e1kRaiseInterrupt(pDevIns, pThis, ICR_TXQE);
+
+ e1kCsTxLeave(pThis);
+ }
+
+
+ /*
+ * Release the lock.
+ */
+ if (pDrv)
+ pDrv->pfnEndXmit(pDrv);
+ return rc;
+}
+
+#endif /* E1K_WITH_TXD_CACHE */
+#ifdef IN_RING3
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
+ */
+static DECLCALLBACK(void) e1kR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
+{
+ PE1KSTATECC pThisCC = RT_FROM_MEMBER(pInterface, E1KSTATECC, INetworkDown);
+ PE1KSTATE pThis = pThisCC->pShared;
+ /* Resume suspended transmission */
+ STATUS &= ~STATUS_TXOFF;
+ e1kXmitPending(pThisCC->pDevInsR3, pThis, true /*fOnWorkerThread*/);
+}
+
+/**
+ * @callback_method_impl{FNPDMTASKDEV,
+ * Executes e1kXmitPending at the behest of ring-0/raw-mode.}
+ * @note Not executed on EMT.
+ */
+static DECLCALLBACK(void) e1kR3TxTaskCallback(PPDMDEVINS pDevIns, void *pvUser)
+{
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+ E1kLog2(("%s e1kR3TxTaskCallback:\n", pThis->szPrf));
+
+ int rc = e1kXmitPending(pDevIns, pThis, false /*fOnWorkerThread*/);
+ AssertMsg(RT_SUCCESS(rc) || rc == VERR_TRY_AGAIN || rc == VERR_NET_DOWN, ("%Rrc\n", rc));
+
+ RT_NOREF(rc, pvUser);
+}
+
+#endif /* IN_RING3 */
+
+/**
+ * Write handler for Transmit Descriptor Tail register.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @param mask Used to implement partial writes (8 and 16-bit).
+ * @thread EMT
+ */
+static int e1kRegWriteTDT(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+ int rc = e1kRegWriteDefault(pDevIns, pThis, offset, index, value);
+
+ /* All descriptors starting with head and not including tail belong to us. */
+ /* Process them. */
+ E1kLog2(("%s e1kRegWriteTDT: TDBAL=%08x, TDBAH=%08x, TDLEN=%08x, TDH=%08x, TDT=%08x\n",
+ pThis->szPrf, TDBAL, TDBAH, TDLEN, TDH, TDT));
+
+ /* Compose a temporary TX context, breaking TX CS rule, for debugging purposes. */
+ /* If we decide to transmit, the TX critical section will be entered later in e1kXmitPending(). */
+ E1KTXDC txdc;
+ txdc.tdlen = TDLEN;
+ txdc.tdh = TDH;
+ txdc.tdt = TDT;
+ /* Ignore TDT writes when the link is down. */
+ if (txdc.tdh != txdc.tdt && (STATUS & STATUS_LU))
+ {
+ Log5(("E1000: TDT write: TDH=%08x, TDT=%08x, %d descriptors to process\n", txdc.tdh, txdc.tdt, e1kGetTxLen(&txdc)));
+ E1kLog(("%s e1kRegWriteTDT: %d descriptors to process\n",
+ pThis->szPrf, e1kGetTxLen(&txdc)));
+
+ /* Transmit pending packets if possible, defer it if we cannot do it
+ in the current context. */
+#ifdef E1K_TX_DELAY
+ rc = e1kCsTxEnter(pThis, VERR_SEM_BUSY);
+ if (RT_LIKELY(rc == VINF_SUCCESS))
+ {
+ if (!PDMDevInsTimerIsActive(pDevIns, pThis->hTXDTimer))
+ {
+# ifdef E1K_INT_STATS
+ pThis->u64ArmedAt = RTTimeNanoTS();
+# endif
+ e1kArmTimer(pDevIns, pThis, pThis->hTXDTimer, E1K_TX_DELAY);
+ }
+ E1K_INC_ISTAT_CNT(pThis->uStatTxDelayed);
+ e1kCsTxLeave(pThis);
+ return rc;
+ }
+ /* We failed to enter the TX critical section -- transmit as usual. */
+#endif /* E1K_TX_DELAY */
+#ifndef IN_RING3
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+ if (!pThisCC->CTX_SUFF(pDrv))
+ {
+ PDMDevHlpTaskTrigger(pDevIns, pThis->hTxTask);
+ rc = VINF_SUCCESS;
+ }
+ else
+#endif
+ {
+ rc = e1kXmitPending(pDevIns, pThis, false /*fOnWorkerThread*/);
+ if ( rc == VERR_TRY_AGAIN
+ || rc == VERR_NET_DOWN)
+ rc = VINF_SUCCESS;
+#ifndef IN_RING3
+ else if (rc == VERR_SEM_BUSY)
+ rc = VINF_IOM_R3_MMIO_WRITE;
+#endif
+ AssertRC(rc);
+ }
+ }
+
+ return rc;
+}
+
+/**
+ * Write handler for Multicast Table Array registers.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @thread EMT
+ */
+static int e1kRegWriteMTA(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+ RT_NOREF_PV(pDevIns);
+ AssertReturn(offset - g_aE1kRegMap[index].offset < sizeof(pThis->auMTA), VERR_DEV_IO_ERROR);
+ pThis->auMTA[(offset - g_aE1kRegMap[index].offset) / sizeof(pThis->auMTA[0])] = value;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Read handler for Multicast Table Array registers.
+ *
+ * @returns VBox status code.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @thread EMT
+ */
+static int e1kRegReadMTA(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t *pu32Value)
+{
+ RT_NOREF_PV(pDevIns);
+ AssertReturn(offset - g_aE1kRegMap[index].offset < sizeof(pThis->auMTA), VERR_DEV_IO_ERROR);
+ *pu32Value = pThis->auMTA[(offset - g_aE1kRegMap[index].offset)/sizeof(pThis->auMTA[0])];
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Write handler for Receive Address registers.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @thread EMT
+ */
+static int e1kRegWriteRA(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+ RT_NOREF_PV(pDevIns);
+ AssertReturn(offset - g_aE1kRegMap[index].offset < sizeof(pThis->aRecAddr.au32), VERR_DEV_IO_ERROR);
+ pThis->aRecAddr.au32[(offset - g_aE1kRegMap[index].offset)/sizeof(pThis->aRecAddr.au32[0])] = value;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Read handler for Receive Address registers.
+ *
+ * @returns VBox status code.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @thread EMT
+ */
+static int e1kRegReadRA(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t *pu32Value)
+{
+ RT_NOREF_PV(pDevIns);
+ AssertReturn(offset - g_aE1kRegMap[index].offset< sizeof(pThis->aRecAddr.au32), VERR_DEV_IO_ERROR);
+ *pu32Value = pThis->aRecAddr.au32[(offset - g_aE1kRegMap[index].offset)/sizeof(pThis->aRecAddr.au32[0])];
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Write handler for VLAN Filter Table Array registers.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @thread EMT
+ */
+static int e1kRegWriteVFTA(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+ RT_NOREF_PV(pDevIns);
+ AssertReturn(offset - g_aE1kRegMap[index].offset < sizeof(pThis->auVFTA), VINF_SUCCESS);
+ pThis->auVFTA[(offset - g_aE1kRegMap[index].offset)/sizeof(pThis->auVFTA[0])] = value;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Read handler for VLAN Filter Table Array registers.
+ *
+ * @returns VBox status code.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @thread EMT
+ */
+static int e1kRegReadVFTA(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t *pu32Value)
+{
+ RT_NOREF_PV(pDevIns);
+ AssertReturn(offset - g_aE1kRegMap[index].offset< sizeof(pThis->auVFTA), VERR_DEV_IO_ERROR);
+ *pu32Value = pThis->auVFTA[(offset - g_aE1kRegMap[index].offset)/sizeof(pThis->auVFTA[0])];
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Read handler for unimplemented registers.
+ *
+ * Merely reports reads from unimplemented registers.
+ *
+ * @returns VBox status code.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @thread EMT
+ */
+static int e1kRegReadUnimplemented(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t *pu32Value)
+{
+ RT_NOREF(pDevIns, pThis, offset, index);
+ E1kLog(("%s At %08X read (00000000) attempt from unimplemented register %s (%s)\n",
+ pThis->szPrf, offset, g_aE1kRegMap[index].abbrev, g_aE1kRegMap[index].name));
+ *pu32Value = 0;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Default register read handler with automatic clear operation.
+ *
+ * Retrieves the value of register from register array in device state structure.
+ * Then resets all bits.
+ *
+ * @remarks The 'mask' parameter is simply ignored as masking and shifting is
+ * done in the caller.
+ *
+ * @returns VBox status code.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @thread EMT
+ */
+static int e1kRegReadAutoClear(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t *pu32Value)
+{
+ AssertReturn(index < E1K_NUM_OF_32BIT_REGS, VERR_DEV_IO_ERROR);
+ int rc = e1kRegReadDefault(pDevIns, pThis, offset, index, pu32Value);
+ pThis->auRegs[index] = 0;
+
+ return rc;
+}
+
+/**
+ * Default register read handler.
+ *
+ * Retrieves the value of register from register array in device state structure.
+ * Bits corresponding to 0s in 'readable' mask will always read as 0s.
+ *
+ * @remarks The 'mask' parameter is simply ignored as masking and shifting is
+ * done in the caller.
+ *
+ * @returns VBox status code.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @thread EMT
+ */
+static int e1kRegReadDefault(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t *pu32Value)
+{
+ RT_NOREF_PV(pDevIns); RT_NOREF_PV(offset);
+
+ AssertReturn(index < E1K_NUM_OF_32BIT_REGS, VERR_DEV_IO_ERROR);
+ *pu32Value = pThis->auRegs[index] & g_aE1kRegMap[index].readable;
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Write handler for unimplemented registers.
+ *
+ * Merely reports writes to unimplemented registers.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @thread EMT
+ */
+
+ static int e1kRegWriteUnimplemented(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+ RT_NOREF_PV(pDevIns); RT_NOREF_PV(pThis); RT_NOREF_PV(offset); RT_NOREF_PV(index); RT_NOREF_PV(value);
+
+ E1kLog(("%s At %08X write attempt (%08X) to unimplemented register %s (%s)\n",
+ pThis->szPrf, offset, value, g_aE1kRegMap[index].abbrev, g_aE1kRegMap[index].name));
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Default register write handler.
+ *
+ * Stores the value to the register array in device state structure. Only bits
+ * corresponding to 1s both in 'writable' and 'mask' will be stored.
+ *
+ * @returns VBox status code.
+ *
+ * @param pThis The device state structure.
+ * @param offset Register offset in memory-mapped frame.
+ * @param index Register index in register array.
+ * @param value The value to store.
+ * @param mask Used to implement partial writes (8 and 16-bit).
+ * @thread EMT
+ */
+
+static int e1kRegWriteDefault(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offset, uint32_t index, uint32_t value)
+{
+ RT_NOREF(pDevIns, offset);
+
+ AssertReturn(index < E1K_NUM_OF_32BIT_REGS, VERR_DEV_IO_ERROR);
+ pThis->auRegs[index] = (value & g_aE1kRegMap[index].writable)
+ | (pThis->auRegs[index] & ~g_aE1kRegMap[index].writable);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Search register table for matching register.
+ *
+ * @returns Index in the register table or -1 if not found.
+ *
+ * @param offReg Register offset in memory-mapped region.
+ * @thread EMT
+ */
+static int e1kRegLookup(uint32_t offReg)
+{
+
+#if 0
+ int index;
+
+ for (index = 0; index < E1K_NUM_OF_REGS; index++)
+ {
+ if (g_aE1kRegMap[index].offset <= offReg && offReg < g_aE1kRegMap[index].offset + g_aE1kRegMap[index].size)
+ {
+ return index;
+ }
+ }
+#else
+ int iStart = 0;
+ int iEnd = E1K_NUM_OF_BINARY_SEARCHABLE;
+ for (;;)
+ {
+ int i = (iEnd - iStart) / 2 + iStart;
+ uint32_t offCur = g_aE1kRegMap[i].offset;
+ if (offReg < offCur)
+ {
+ if (i == iStart)
+ break;
+ iEnd = i;
+ }
+ else if (offReg >= offCur + g_aE1kRegMap[i].size)
+ {
+ i++;
+ if (i == iEnd)
+ break;
+ iStart = i;
+ }
+ else
+ return i;
+ Assert(iEnd > iStart);
+ }
+
+ for (unsigned i = E1K_NUM_OF_BINARY_SEARCHABLE; i < RT_ELEMENTS(g_aE1kRegMap); i++)
+ if (offReg - g_aE1kRegMap[i].offset < g_aE1kRegMap[i].size)
+ return (int)i;
+
+# ifdef VBOX_STRICT
+ for (unsigned i = 0; i < RT_ELEMENTS(g_aE1kRegMap); i++)
+ Assert(offReg - g_aE1kRegMap[i].offset >= g_aE1kRegMap[i].size);
+# endif
+
+#endif
+
+ return -1;
+}
+
+/**
+ * Handle unaligned register read operation.
+ *
+ * Looks up and calls appropriate handler.
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param offReg Register offset in memory-mapped frame.
+ * @param pv Where to store the result.
+ * @param cb Number of bytes to read.
+ * @thread EMT
+ * @remarks IOM takes care of unaligned and small reads via MMIO. For I/O port
+ * accesses we have to take care of that ourselves.
+ */
+static int e1kRegReadUnaligned(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offReg, void *pv, uint32_t cb)
+{
+ uint32_t u32 = 0;
+ uint32_t shift;
+ int rc = VINF_SUCCESS;
+ int index = e1kRegLookup(offReg);
+#ifdef LOG_ENABLED
+ char buf[9];
+#endif
+
+ /*
+ * From the spec:
+ * For registers that should be accessed as 32-bit double words, partial writes (less than a 32-bit
+ * double word) is ignored. Partial reads return all 32 bits of data regardless of the byte enables.
+ */
+
+ /*
+ * To be able to read bytes and short word we convert them to properly
+ * shifted 32-bit words and masks. The idea is to keep register-specific
+ * handlers simple. Most accesses will be 32-bit anyway.
+ */
+ uint32_t mask;
+ switch (cb)
+ {
+ case 4: mask = 0xFFFFFFFF; break;
+ case 2: mask = 0x0000FFFF; break;
+ case 1: mask = 0x000000FF; break;
+ default:
+ return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "unsupported op size: offset=%#10x cb=%#10x\n", offReg, cb);
+ }
+ if (index >= 0)
+ {
+ RT_UNTRUSTED_VALIDATED_FENCE(); /* paranoia because of port I/O. */
+ if (g_aE1kRegMap[index].readable)
+ {
+ /* Make the mask correspond to the bits we are about to read. */
+ shift = (offReg - g_aE1kRegMap[index].offset) % sizeof(uint32_t) * 8;
+ mask <<= shift;
+ if (!mask)
+ return PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "Zero mask: offset=%#10x cb=%#10x\n", offReg, cb);
+ /*
+ * Read it. Pass the mask so the handler knows what has to be read.
+ * Mask out irrelevant bits.
+ */
+ //e1kCsEnterReturn(pThis, VERR_SEM_BUSY);
+ //pThis->fDelayInts = false;
+ //pThis->iStatIntLost += pThis->iStatIntLostOne;
+ //pThis->iStatIntLostOne = 0;
+ rc = g_aE1kRegMap[index].pfnRead(pDevIns, pThis, offReg & 0xFFFFFFFC, (uint32_t)index, &u32);
+ u32 &= mask;
+ //e1kCsLeave(pThis);
+ E1kLog2(("%s At %08X read %s from %s (%s)\n",
+ pThis->szPrf, offReg, e1kU32toHex(u32, mask, buf), g_aE1kRegMap[index].abbrev, g_aE1kRegMap[index].name));
+ Log6(("%s At %08X read %s from %s (%s) [UNALIGNED]\n",
+ pThis->szPrf, offReg, e1kU32toHex(u32, mask, buf), g_aE1kRegMap[index].abbrev, g_aE1kRegMap[index].name));
+ /* Shift back the result. */
+ u32 >>= shift;
+ }
+ else
+ E1kLog(("%s At %08X read (%s) attempt from write-only register %s (%s)\n",
+ pThis->szPrf, offReg, e1kU32toHex(u32, mask, buf), g_aE1kRegMap[index].abbrev, g_aE1kRegMap[index].name));
+ if (IOM_SUCCESS(rc))
+ STAM_COUNTER_INC(&pThis->aStatRegReads[index]);
+ }
+ else
+ E1kLog(("%s At %08X read (%s) attempt from non-existing register\n",
+ pThis->szPrf, offReg, e1kU32toHex(u32, mask, buf)));
+
+ memcpy(pv, &u32, cb);
+ return rc;
+}
+
+/**
+ * Handle 4 byte aligned and sized read operation.
+ *
+ * Looks up and calls appropriate handler.
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param offReg Register offset in memory-mapped frame.
+ * @param pu32 Where to store the result.
+ * @thread EMT
+ */
+static VBOXSTRICTRC e1kRegReadAlignedU32(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offReg, uint32_t *pu32)
+{
+ Assert(!(offReg & 3));
+
+ /*
+ * Lookup the register and check that it's readable.
+ */
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+ int idxReg = e1kRegLookup(offReg);
+ if (RT_LIKELY(idxReg >= 0))
+ {
+ RT_UNTRUSTED_VALIDATED_FENCE(); /* paranoia because of port I/O. */
+ if (RT_UNLIKELY(g_aE1kRegMap[idxReg].readable))
+ {
+ /*
+ * Read it. Pass the mask so the handler knows what has to be read.
+ * Mask out irrelevant bits.
+ */
+ //e1kCsEnterReturn(pThis, VERR_SEM_BUSY);
+ //pThis->fDelayInts = false;
+ //pThis->iStatIntLost += pThis->iStatIntLostOne;
+ //pThis->iStatIntLostOne = 0;
+ rc = g_aE1kRegMap[idxReg].pfnRead(pDevIns, pThis, offReg & 0xFFFFFFFC, (uint32_t)idxReg, pu32);
+ //e1kCsLeave(pThis);
+ Log6(("%s At %08X read %08X from %s (%s)\n",
+ pThis->szPrf, offReg, *pu32, g_aE1kRegMap[idxReg].abbrev, g_aE1kRegMap[idxReg].name));
+ if (IOM_SUCCESS(rc))
+ STAM_COUNTER_INC(&pThis->aStatRegReads[idxReg]);
+ }
+ else
+ E1kLog(("%s At %08X read attempt from non-readable register %s (%s)\n",
+ pThis->szPrf, offReg, g_aE1kRegMap[idxReg].abbrev, g_aE1kRegMap[idxReg].name));
+ }
+ else
+ E1kLog(("%s At %08X read attempt from non-existing register\n", pThis->szPrf, offReg));
+ return rc;
+}
+
+/**
+ * Handle 4 byte sized and aligned register write operation.
+ *
+ * Looks up and calls appropriate handler.
+ *
+ * @returns VBox status code.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The device state structure.
+ * @param offReg Register offset in memory-mapped frame.
+ * @param u32Value The value to write.
+ * @thread EMT
+ */
+static VBOXSTRICTRC e1kRegWriteAlignedU32(PPDMDEVINS pDevIns, PE1KSTATE pThis, uint32_t offReg, uint32_t u32Value)
+{
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+ int index = e1kRegLookup(offReg);
+ if (RT_LIKELY(index >= 0))
+ {
+ RT_UNTRUSTED_VALIDATED_FENCE(); /* paranoia because of port I/O. */
+ if (RT_LIKELY(g_aE1kRegMap[index].writable))
+ {
+ /*
+ * Write it. Pass the mask so the handler knows what has to be written.
+ * Mask out irrelevant bits.
+ */
+ Log6(("%s At %08X write %08X to %s (%s)\n",
+ pThis->szPrf, offReg, u32Value, g_aE1kRegMap[index].abbrev, g_aE1kRegMap[index].name));
+ //e1kCsEnterReturn(pThis, VERR_SEM_BUSY);
+ //pThis->fDelayInts = false;
+ //pThis->iStatIntLost += pThis->iStatIntLostOne;
+ //pThis->iStatIntLostOne = 0;
+ rc = g_aE1kRegMap[index].pfnWrite(pDevIns, pThis, offReg, (uint32_t)index, u32Value);
+ //e1kCsLeave(pThis);
+ }
+ else
+ E1kLog(("%s At %08X write attempt (%08X) to read-only register %s (%s)\n",
+ pThis->szPrf, offReg, u32Value, g_aE1kRegMap[index].abbrev, g_aE1kRegMap[index].name));
+ if (IOM_SUCCESS(rc))
+ STAM_COUNTER_INC(&pThis->aStatRegWrites[index]);
+ }
+ else
+ E1kLog(("%s At %08X write attempt (%08X) to non-existing register\n",
+ pThis->szPrf, offReg, u32Value));
+ return rc;
+}
+
+
+/* -=-=-=-=- MMIO and I/O Port Callbacks -=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNIOMMMIONEWREAD}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) e1kMMIORead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, uint32_t cb)
+{
+ RT_NOREF2(pvUser, cb);
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatMMIORead), a);
+
+ Assert(off < E1K_MM_SIZE);
+ Assert(cb == 4);
+ Assert(!(off & 3));
+
+ VBOXSTRICTRC rcStrict = e1kRegReadAlignedU32(pDevIns, pThis, (uint32_t)off, (uint32_t *)pv);
+
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatMMIORead), a);
+ return rcStrict;
+}
+
+/**
+ * @callback_method_impl{FNIOMMMIONEWWRITE}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) e1kMMIOWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, uint32_t cb)
+{
+ RT_NOREF2(pvUser, cb);
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatMMIOWrite), a);
+
+ Assert(off < E1K_MM_SIZE);
+ Assert(cb == 4);
+ Assert(!(off & 3));
+
+ VBOXSTRICTRC rcStrict = e1kRegWriteAlignedU32(pDevIns, pThis, (uint32_t)off, *(uint32_t const *)pv);
+
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatMMIOWrite), a);
+ return rcStrict;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTNEWIN}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) e1kIOPortIn(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
+{
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+ VBOXSTRICTRC rc;
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
+ RT_NOREF_PV(pvUser);
+
+ if (RT_LIKELY(cb == 4))
+ switch (offPort)
+ {
+ case 0x00: /* IOADDR */
+ *pu32 = pThis->uSelectedReg;
+ Log9(("%s e1kIOPortIn: IOADDR(0), selecting register %#010x, val=%#010x\n", pThis->szPrf, pThis->uSelectedReg, *pu32));
+ rc = VINF_SUCCESS;
+ break;
+
+ case 0x04: /* IODATA */
+ if (!(pThis->uSelectedReg & 3))
+ rc = e1kRegReadAlignedU32(pDevIns, pThis, pThis->uSelectedReg, pu32);
+ else /** @todo r=bird: I wouldn't be surprised if this unaligned branch wasn't necessary. */
+ rc = e1kRegReadUnaligned(pDevIns, pThis, pThis->uSelectedReg, pu32, cb);
+ if (rc == VINF_IOM_R3_MMIO_READ)
+ rc = VINF_IOM_R3_IOPORT_READ;
+ Log9(("%s e1kIOPortIn: IODATA(4), reading from selected register %#010x, val=%#010x\n", pThis->szPrf, pThis->uSelectedReg, *pu32));
+ break;
+
+ default:
+ E1kLog(("%s e1kIOPortIn: invalid port %#010x\n", pThis->szPrf, offPort));
+ /** @todo r=bird: Check what real hardware returns here. */
+ //rc = VERR_IOM_IOPORT_UNUSED; /* Why not? */
+ rc = VINF_IOM_MMIO_UNUSED_00; /* used to return VINF_SUCCESS and not touch *pu32, which amounted to this. */
+ break;
+ }
+ else
+ {
+ E1kLog(("%s e1kIOPortIn: invalid op size: offPort=%RTiop cb=%08x", pThis->szPrf, offPort, cb));
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "%s e1kIOPortIn: invalid op size: offPort=%RTiop cb=%08x\n", pThis->szPrf, offPort, cb);
+ *pu32 = 0; /** @todo r=bird: Check what real hardware returns here. (Didn't used to set a value here, picked zero as that's what we'd end up in most cases.) */
+ }
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTNEWOUT}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) e1kIOPortOut(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
+{
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+ VBOXSTRICTRC rc;
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIOWrite), a);
+ RT_NOREF_PV(pvUser);
+
+ Log9(("%s e1kIOPortOut: offPort=%RTiop value=%08x\n", pThis->szPrf, offPort, u32));
+ if (RT_LIKELY(cb == 4))
+ {
+ switch (offPort)
+ {
+ case 0x00: /* IOADDR */
+ pThis->uSelectedReg = u32;
+ Log9(("%s e1kIOPortOut: IOADDR(0), selected register %08x\n", pThis->szPrf, pThis->uSelectedReg));
+ rc = VINF_SUCCESS;
+ break;
+
+ case 0x04: /* IODATA */
+ Log9(("%s e1kIOPortOut: IODATA(4), writing to selected register %#010x, value=%#010x\n", pThis->szPrf, pThis->uSelectedReg, u32));
+ if (RT_LIKELY(!(pThis->uSelectedReg & 3)))
+ {
+ rc = e1kRegWriteAlignedU32(pDevIns, pThis, pThis->uSelectedReg, u32);
+ if (rc == VINF_IOM_R3_MMIO_WRITE)
+ rc = VINF_IOM_R3_IOPORT_WRITE;
+ }
+ else
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS,
+ "Spec violation: misaligned offset: %#10x, ignored.\n", pThis->uSelectedReg);
+ break;
+
+ default:
+ E1kLog(("%s e1kIOPortOut: invalid port %#010x\n", pThis->szPrf, offPort));
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "invalid port %#010x\n", offPort);
+ }
+ }
+ else
+ {
+ E1kLog(("%s e1kIOPortOut: invalid op size: offPort=%RTiop cb=%08x\n", pThis->szPrf, offPort, cb));
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "%s: invalid op size: offPort=%RTiop cb=%#x\n", pThis->szPrf, offPort, cb);
+ }
+
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIOWrite), a);
+ return rc;
+}
+
+#ifdef IN_RING3
+
+/**
+ * Dump complete device state to log.
+ *
+ * @param pThis Pointer to device state.
+ */
+static void e1kDumpState(PE1KSTATE pThis)
+{
+ RT_NOREF(pThis);
+ for (int i = 0; i < E1K_NUM_OF_32BIT_REGS; ++i)
+ E1kLog2(("%s: %8.8s = %08x\n", pThis->szPrf, g_aE1kRegMap[i].abbrev, pThis->auRegs[i]));
+# ifdef E1K_INT_STATS
+ LogRel(("%s: Interrupt attempts: %d\n", pThis->szPrf, pThis->uStatIntTry));
+ LogRel(("%s: Interrupts raised : %d\n", pThis->szPrf, pThis->uStatInt));
+ LogRel(("%s: Interrupts lowered: %d\n", pThis->szPrf, pThis->uStatIntLower));
+ LogRel(("%s: ICR outside ISR : %d\n", pThis->szPrf, pThis->uStatNoIntICR));
+ LogRel(("%s: IMS raised ints : %d\n", pThis->szPrf, pThis->uStatIntIMS));
+ LogRel(("%s: Interrupts skipped: %d\n", pThis->szPrf, pThis->uStatIntSkip));
+ LogRel(("%s: Masked interrupts : %d\n", pThis->szPrf, pThis->uStatIntMasked));
+ LogRel(("%s: Early interrupts : %d\n", pThis->szPrf, pThis->uStatIntEarly));
+ LogRel(("%s: Late interrupts : %d\n", pThis->szPrf, pThis->uStatIntLate));
+ LogRel(("%s: Lost interrupts : %d\n", pThis->szPrf, pThis->iStatIntLost));
+ LogRel(("%s: Interrupts by RX : %d\n", pThis->szPrf, pThis->uStatIntRx));
+ LogRel(("%s: Interrupts by TX : %d\n", pThis->szPrf, pThis->uStatIntTx));
+ LogRel(("%s: Interrupts by ICS : %d\n", pThis->szPrf, pThis->uStatIntICS));
+ LogRel(("%s: Interrupts by RDTR: %d\n", pThis->szPrf, pThis->uStatIntRDTR));
+ LogRel(("%s: Interrupts by RDMT: %d\n", pThis->szPrf, pThis->uStatIntRXDMT0));
+ LogRel(("%s: Interrupts by TXQE: %d\n", pThis->szPrf, pThis->uStatIntTXQE));
+ LogRel(("%s: TX int delay asked: %d\n", pThis->szPrf, pThis->uStatTxIDE));
+ LogRel(("%s: TX delayed: %d\n", pThis->szPrf, pThis->uStatTxDelayed));
+ LogRel(("%s: TX delay expired: %d\n", pThis->szPrf, pThis->uStatTxDelayExp));
+ LogRel(("%s: TX no report asked: %d\n", pThis->szPrf, pThis->uStatTxNoRS));
+ LogRel(("%s: TX abs timer expd : %d\n", pThis->szPrf, pThis->uStatTAD));
+ LogRel(("%s: TX int timer expd : %d\n", pThis->szPrf, pThis->uStatTID));
+ LogRel(("%s: RX abs timer expd : %d\n", pThis->szPrf, pThis->uStatRAD));
+ LogRel(("%s: RX int timer expd : %d\n", pThis->szPrf, pThis->uStatRID));
+ LogRel(("%s: TX CTX descriptors: %d\n", pThis->szPrf, pThis->uStatDescCtx));
+ LogRel(("%s: TX DAT descriptors: %d\n", pThis->szPrf, pThis->uStatDescDat));
+ LogRel(("%s: TX LEG descriptors: %d\n", pThis->szPrf, pThis->uStatDescLeg));
+ LogRel(("%s: Received frames : %d\n", pThis->szPrf, pThis->uStatRxFrm));
+ LogRel(("%s: Transmitted frames: %d\n", pThis->szPrf, pThis->uStatTxFrm));
+ LogRel(("%s: TX frames up to 1514: %d\n", pThis->szPrf, pThis->uStatTx1514));
+ LogRel(("%s: TX frames up to 2962: %d\n", pThis->szPrf, pThis->uStatTx2962));
+ LogRel(("%s: TX frames up to 4410: %d\n", pThis->szPrf, pThis->uStatTx4410));
+ LogRel(("%s: TX frames up to 5858: %d\n", pThis->szPrf, pThis->uStatTx5858));
+ LogRel(("%s: TX frames up to 7306: %d\n", pThis->szPrf, pThis->uStatTx7306));
+ LogRel(("%s: TX frames up to 8754: %d\n", pThis->szPrf, pThis->uStatTx8754));
+ LogRel(("%s: TX frames up to 16384: %d\n", pThis->szPrf, pThis->uStatTx16384));
+ LogRel(("%s: TX frames up to 32768: %d\n", pThis->szPrf, pThis->uStatTx32768));
+ LogRel(("%s: Larger TX frames : %d\n", pThis->szPrf, pThis->uStatTxLarge));
+ LogRel(("%s: Max TX Delay : %lld\n", pThis->szPrf, pThis->uStatMaxTxDelay));
+# endif /* E1K_INT_STATS */
+}
+
+
+/* -=-=-=-=- PDMINETWORKDOWN -=-=-=-=- */
+
+/**
+ * Check if the device can receive data now.
+ * This must be called before the pfnRecieve() method is called.
+ *
+ * @returns VBox status code.
+ * @retval VERR_NET_NO_BUFFER_SPACE if we cannot receive.
+ * @param pDevIns The device instance.
+ * @param pThis The instance data.
+ * @thread EMT
+ */
+static int e1kR3CanReceive(PPDMDEVINS pDevIns, PE1KSTATE pThis)
+{
+# ifndef E1K_WITH_RXD_CACHE
+ size_t cb;
+
+ e1kCsRxEnterReturn(pThis);
+
+ if (RT_UNLIKELY(RDLEN == sizeof(E1KRXDESC)))
+ {
+ E1KRXDESC desc;
+ PDMDevHlpPCIPhysRead(pDevIns, e1kDescAddr(RDBAH, RDBAL, RDH), &desc, sizeof(desc));
+ if (desc.status.fDD)
+ cb = 0;
+ else
+ cb = pThis->u16RxBSize;
+ }
+ else if (RDH < RDT)
+ cb = (RDT - RDH) * pThis->u16RxBSize;
+ else if (RDH > RDT)
+ cb = (RDLEN / sizeof(E1KRXDESC) - RDH + RDT) * pThis->u16RxBSize;
+ else
+ {
+ cb = 0;
+ E1kLogRel(("E1000: OUT of RX descriptors!\n"));
+ }
+ E1kLog2(("%s e1kR3CanReceive: at exit RDH=%d RDT=%d RDLEN=%d u16RxBSize=%d cb=%lu\n",
+ pThis->szPrf, RDH, RDT, RDLEN, pThis->u16RxBSize, cb));
+
+ e1kCsRxLeave(pThis);
+ return cb > 0 ? VINF_SUCCESS : VERR_NET_NO_BUFFER_SPACE;
+# else /* E1K_WITH_RXD_CACHE */
+
+ e1kCsRxEnterReturn(pThis);
+
+ E1KRXDC rxdc;
+ if (RT_UNLIKELY(!e1kUpdateRxDContext(pDevIns, pThis, &rxdc, "e1kR3CanReceive")))
+ {
+ e1kCsRxLeave(pThis);
+ E1kLog(("%s e1kR3CanReceive: failed to update Rx context, returning VERR_NET_NO_BUFFER_SPACE\n", pThis->szPrf));
+ return VERR_NET_NO_BUFFER_SPACE;
+ }
+
+ int rc = VINF_SUCCESS;
+ if (RT_UNLIKELY(rxdc.rdlen == sizeof(E1KRXDESC)))
+ {
+ E1KRXDESC desc;
+ PDMDevHlpPCIPhysRead(pDevIns, e1kDescAddr(RDBAH, RDBAL, rxdc.rdh), &desc, sizeof(desc));
+ if (desc.status.fDD)
+ rc = VERR_NET_NO_BUFFER_SPACE;
+ }
+ else if (e1kRxDIsCacheEmpty(pThis) && rxdc.rdh == rxdc.rdt)
+ {
+ /* Cache is empty, so is the RX ring. */
+ rc = VERR_NET_NO_BUFFER_SPACE;
+ }
+ E1kLog2(("%s e1kR3CanReceive: at exit in_cache=%d RDH=%d RDT=%d RDLEN=%d u16RxBSize=%d rc=%Rrc\n", pThis->szPrf,
+ e1kRxDInCache(pThis), rxdc.rdh, rxdc.rdt, rxdc.rdlen, pThis->u16RxBSize, rc));
+
+ e1kCsRxLeave(pThis);
+ return rc;
+# endif /* E1K_WITH_RXD_CACHE */
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
+ */
+static DECLCALLBACK(int) e1kR3NetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
+{
+ PE1KSTATECC pThisCC = RT_FROM_MEMBER(pInterface, E1KSTATECC, INetworkDown);
+ PE1KSTATE pThis = pThisCC->pShared;
+ PPDMDEVINS pDevIns = pThisCC->pDevInsR3;
+
+ int rc = e1kR3CanReceive(pDevIns, pThis);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+
+ if (RT_UNLIKELY(cMillies == 0))
+ return VERR_NET_NO_BUFFER_SPACE;
+
+ rc = VERR_INTERRUPTED;
+ ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, true);
+ STAM_PROFILE_START(&pThis->StatRxOverflow, a);
+ VMSTATE enmVMState;
+ while (RT_LIKELY( (enmVMState = PDMDevHlpVMState(pDevIns)) == VMSTATE_RUNNING
+ || enmVMState == VMSTATE_RUNNING_LS))
+ {
+ int rc2 = e1kR3CanReceive(pDevIns, pThis);
+ if (RT_SUCCESS(rc2))
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+ E1kLogRel(("E1000: e1kR3NetworkDown_WaitReceiveAvail: waiting cMillies=%u...\n", cMillies));
+ E1kLog(("%s: e1kR3NetworkDown_WaitReceiveAvail: waiting cMillies=%u...\n", pThis->szPrf, cMillies));
+ PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEventMoreRxDescAvail, cMillies);
+ }
+ STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
+ ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, false);
+
+ return rc;
+}
+
+
+/**
+ * Matches the packet addresses against Receive Address table. Looks for
+ * exact matches only.
+ *
+ * @returns true if address matches.
+ * @param pThis Pointer to the state structure.
+ * @param pvBuf The ethernet packet.
+ * @param cb Number of bytes available in the packet.
+ * @thread EMT
+ */
+static bool e1kPerfectMatch(PE1KSTATE pThis, const void *pvBuf)
+{
+ for (unsigned i = 0; i < RT_ELEMENTS(pThis->aRecAddr.array); i++)
+ {
+ E1KRAELEM* ra = pThis->aRecAddr.array + i;
+
+ /* Valid address? */
+ if (ra->ctl & RA_CTL_AV)
+ {
+ Assert((ra->ctl & RA_CTL_AS) < 2);
+ //unsigned char *pAddr = (unsigned char*)pvBuf + sizeof(ra->addr)*(ra->ctl & RA_CTL_AS);
+ //E1kLog3(("%s Matching %02x:%02x:%02x:%02x:%02x:%02x against %02x:%02x:%02x:%02x:%02x:%02x...\n",
+ // pThis->szPrf, pAddr[0], pAddr[1], pAddr[2], pAddr[3], pAddr[4], pAddr[5],
+ // ra->addr[0], ra->addr[1], ra->addr[2], ra->addr[3], ra->addr[4], ra->addr[5]));
+ /*
+ * Address Select:
+ * 00b = Destination address
+ * 01b = Source address
+ * 10b = Reserved
+ * 11b = Reserved
+ * Since ethernet header is (DA, SA, len) we can use address
+ * select as index.
+ */
+ if (memcmp((char*)pvBuf + sizeof(ra->addr)*(ra->ctl & RA_CTL_AS),
+ ra->addr, sizeof(ra->addr)) == 0)
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/**
+ * Matches the packet addresses against Multicast Table Array.
+ *
+ * @remarks This is imperfect match since it matches not exact address but
+ * a subset of addresses.
+ *
+ * @returns true if address matches.
+ * @param pThis Pointer to the state structure.
+ * @param pvBuf The ethernet packet.
+ * @param cb Number of bytes available in the packet.
+ * @thread EMT
+ */
+static bool e1kImperfectMatch(PE1KSTATE pThis, const void *pvBuf)
+{
+ /* Get bits 32..47 of destination address */
+ uint16_t u16Bit = ((uint16_t*)pvBuf)[2];
+
+ unsigned offset = GET_BITS(RCTL, MO);
+ /*
+ * offset means:
+ * 00b = bits 36..47
+ * 01b = bits 35..46
+ * 10b = bits 34..45
+ * 11b = bits 32..43
+ */
+ if (offset < 3)
+ u16Bit = u16Bit >> (4 - offset);
+ return ASMBitTest(pThis->auMTA, u16Bit & 0xFFF);
+}
+
+/**
+ * Determines if the packet is to be delivered to upper layer.
+ *
+ * The following filters supported:
+ * - Exact Unicast/Multicast
+ * - Promiscuous Unicast/Multicast
+ * - Multicast
+ * - VLAN
+ *
+ * @returns true if packet is intended for this node.
+ * @param pThis Pointer to the state structure.
+ * @param pvBuf The ethernet packet.
+ * @param cb Number of bytes available in the packet.
+ * @param pStatus Bit field to store status bits.
+ * @thread EMT
+ */
+static bool e1kAddressFilter(PE1KSTATE pThis, const void *pvBuf, size_t cb, E1KRXDST *pStatus)
+{
+ Assert(cb > 14);
+ /* Assume that we fail to pass exact filter. */
+ pStatus->fPIF = false;
+ pStatus->fVP = false;
+ /* Discard oversized packets */
+ if (cb > E1K_MAX_RX_PKT_SIZE)
+ {
+ E1kLog(("%s ERROR: Incoming packet is too big, cb=%d > max=%d\n",
+ pThis->szPrf, cb, E1K_MAX_RX_PKT_SIZE));
+ E1K_INC_CNT32(ROC);
+ return false;
+ }
+ else if (!(RCTL & RCTL_LPE) && cb > 1522)
+ {
+ /* When long packet reception is disabled packets over 1522 are discarded */
+ E1kLog(("%s Discarding incoming packet (LPE=0), cb=%d\n",
+ pThis->szPrf, cb));
+ E1K_INC_CNT32(ROC);
+ return false;
+ }
+
+ uint16_t *u16Ptr = (uint16_t*)pvBuf;
+ /* Compare TPID with VLAN Ether Type */
+ if (RT_BE2H_U16(u16Ptr[6]) == VET)
+ {
+ pStatus->fVP = true;
+ /* Is VLAN filtering enabled? */
+ if (RCTL & RCTL_VFE)
+ {
+ /* It is 802.1q packet indeed, let's filter by VID */
+ if (RCTL & RCTL_CFIEN)
+ {
+ E1kLog3(("%s VLAN filter: VLAN=%d CFI=%d RCTL_CFI=%d\n", pThis->szPrf,
+ E1K_SPEC_VLAN(RT_BE2H_U16(u16Ptr[7])),
+ E1K_SPEC_CFI(RT_BE2H_U16(u16Ptr[7])),
+ !!(RCTL & RCTL_CFI)));
+ if (E1K_SPEC_CFI(RT_BE2H_U16(u16Ptr[7])) != !!(RCTL & RCTL_CFI))
+ {
+ E1kLog2(("%s Packet filter: CFIs do not match in packet and RCTL (%d!=%d)\n",
+ pThis->szPrf, E1K_SPEC_CFI(RT_BE2H_U16(u16Ptr[7])), !!(RCTL & RCTL_CFI)));
+ return false;
+ }
+ }
+ else
+ E1kLog3(("%s VLAN filter: VLAN=%d\n", pThis->szPrf,
+ E1K_SPEC_VLAN(RT_BE2H_U16(u16Ptr[7]))));
+ if (!ASMBitTest(pThis->auVFTA, E1K_SPEC_VLAN(RT_BE2H_U16(u16Ptr[7]))))
+ {
+ E1kLog2(("%s Packet filter: no VLAN match (id=%d)\n",
+ pThis->szPrf, E1K_SPEC_VLAN(RT_BE2H_U16(u16Ptr[7]))));
+ return false;
+ }
+ }
+ }
+ /* Broadcast filtering */
+ if (e1kIsBroadcast(pvBuf) && (RCTL & RCTL_BAM))
+ return true;
+ E1kLog2(("%s Packet filter: not a broadcast\n", pThis->szPrf));
+ if (e1kIsMulticast(pvBuf))
+ {
+ /* Is multicast promiscuous enabled? */
+ if (RCTL & RCTL_MPE)
+ return true;
+ E1kLog2(("%s Packet filter: no promiscuous multicast\n", pThis->szPrf));
+ /* Try perfect matches first */
+ if (e1kPerfectMatch(pThis, pvBuf))
+ {
+ pStatus->fPIF = true;
+ return true;
+ }
+ E1kLog2(("%s Packet filter: no perfect match\n", pThis->szPrf));
+ if (e1kImperfectMatch(pThis, pvBuf))
+ return true;
+ E1kLog2(("%s Packet filter: no imperfect match\n", pThis->szPrf));
+ }
+ else {
+ /* Is unicast promiscuous enabled? */
+ if (RCTL & RCTL_UPE)
+ return true;
+ E1kLog2(("%s Packet filter: no promiscuous unicast\n", pThis->szPrf));
+ if (e1kPerfectMatch(pThis, pvBuf))
+ {
+ pStatus->fPIF = true;
+ return true;
+ }
+ E1kLog2(("%s Packet filter: no perfect match\n", pThis->szPrf));
+ }
+ E1kLog2(("%s Packet filter: packet discarded\n", pThis->szPrf));
+ return false;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
+ */
+static DECLCALLBACK(int) e1kR3NetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
+{
+ PE1KSTATECC pThisCC = RT_FROM_MEMBER(pInterface, E1KSTATECC, INetworkDown);
+ PE1KSTATE pThis = pThisCC->pShared;
+ PPDMDEVINS pDevIns = pThisCC->pDevInsR3;
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Drop packets if the VM is not running yet/anymore.
+ */
+ VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
+ if ( enmVMState != VMSTATE_RUNNING
+ && enmVMState != VMSTATE_RUNNING_LS)
+ {
+ E1kLog(("%s Dropping incoming packet as VM is not running.\n", pThis->szPrf));
+ return VINF_SUCCESS;
+ }
+
+ /* Discard incoming packets in locked state */
+ if (!(RCTL & RCTL_EN) || pThis->fLocked || !(STATUS & STATUS_LU))
+ {
+ E1kLog(("%s Dropping incoming packet as receive operation is disabled.\n", pThis->szPrf));
+ return VINF_SUCCESS;
+ }
+
+ STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
+
+ //e1kR3CsEnterAsserted(pThis);
+
+ e1kPacketDump(pDevIns, pThis, (const uint8_t*)pvBuf, cb, "<-- Incoming");
+
+ /* Update stats */
+ e1kR3CsEnterAsserted(pThis);
+ E1K_INC_CNT32(TPR);
+ E1K_ADD_CNT64(TORL, TORH, cb < 64? 64 : cb);
+ e1kCsLeave(pThis);
+
+ STAM_PROFILE_ADV_START(&pThis->StatReceiveFilter, a);
+ E1KRXDST status;
+ RT_ZERO(status);
+ bool fPassed = e1kAddressFilter(pThis, pvBuf, cb, &status);
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceiveFilter, a);
+ if (fPassed)
+ {
+ rc = e1kHandleRxPacket(pDevIns, pThis, pvBuf, cb, status);
+ }
+ //e1kCsLeave(pThis);
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+
+ return rc;
+}
+
+
+/* -=-=-=-=- PDMILEDPORTS -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
+ */
+static DECLCALLBACK(int) e1kR3QueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
+{
+ if (iLUN == 0)
+ {
+ PE1KSTATECC pThisCC = RT_FROM_MEMBER(pInterface, E1KSTATECC, ILeds);
+ *ppLed = &pThisCC->pShared->led;
+ return VINF_SUCCESS;
+ }
+ return VERR_PDM_LUN_NOT_FOUND;
+}
+
+
+/* -=-=-=-=- PDMINETWORKCONFIG -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
+ */
+static DECLCALLBACK(int) e1kR3GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
+{
+ PE1KSTATECC pThisCC = RT_FROM_MEMBER(pInterface, E1KSTATECC, INetworkConfig);
+ pThisCC->eeprom.getMac(pMac);
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
+ */
+static DECLCALLBACK(PDMNETWORKLINKSTATE) e1kR3GetLinkState(PPDMINETWORKCONFIG pInterface)
+{
+ PE1KSTATECC pThisCC = RT_FROM_MEMBER(pInterface, E1KSTATECC, INetworkConfig);
+ PE1KSTATE pThis = pThisCC->pShared;
+ if (STATUS & STATUS_LU)
+ return PDMNETWORKLINKSTATE_UP;
+ return PDMNETWORKLINKSTATE_DOWN;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
+ */
+static DECLCALLBACK(int) e1kR3SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
+{
+ PE1KSTATECC pThisCC = RT_FROM_MEMBER(pInterface, E1KSTATECC, INetworkConfig);
+ PE1KSTATE pThis = pThisCC->pShared;
+ PPDMDEVINS pDevIns = pThisCC->pDevInsR3;
+
+ E1kLog(("%s e1kR3SetLinkState: enmState=%d\n", pThis->szPrf, enmState));
+ switch (enmState)
+ {
+ case PDMNETWORKLINKSTATE_UP:
+ pThis->fCableConnected = true;
+ /* If link was down, bring it up after a while. */
+ if (!(STATUS & STATUS_LU))
+ e1kBringLinkUpDelayed(pDevIns, pThis);
+ break;
+ case PDMNETWORKLINKSTATE_DOWN:
+ pThis->fCableConnected = false;
+ /* Always set the phy link state to down, regardless of the STATUS_LU bit.
+ * We might have to set the link state before the driver initializes us. */
+ Phy::setLinkStatus(&pThis->phy, false);
+ /* If link was up, bring it down. */
+ if (STATUS & STATUS_LU)
+ e1kR3LinkDown(pDevIns, pThis, pThisCC);
+ break;
+ case PDMNETWORKLINKSTATE_DOWN_RESUME:
+ /*
+ * There is not much sense in bringing down the link if it has not come up yet.
+ * If it is up though, we bring it down temporarely, then bring it up again.
+ */
+ if (STATUS & STATUS_LU)
+ e1kR3LinkDownTemp(pDevIns, pThis, pThisCC);
+ break;
+ default:
+ ;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/* -=-=-=-=- PDMIBASE -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) e1kR3QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
+{
+ PE1KSTATECC pThisCC = RT_FROM_MEMBER(pInterface, E1KSTATECC, IBase);
+ Assert(&pThisCC->IBase == pInterface);
+
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
+ return NULL;
+}
+
+
+/* -=-=-=-=- Saved State -=-=-=-=- */
+
+/**
+ * Saves the configuration.
+ *
+ * @param pThis The E1K state.
+ * @param pSSM The handle to the saved state.
+ */
+static void e1kR3SaveConfig(PCPDMDEVHLPR3 pHlp, PE1KSTATE pThis, PSSMHANDLE pSSM)
+{
+ pHlp->pfnSSMPutMem(pSSM, &pThis->macConfigured, sizeof(pThis->macConfigured));
+ pHlp->pfnSSMPutU32(pSSM, pThis->eChip);
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVLIVEEXEC,Save basic configuration.}
+ */
+static DECLCALLBACK(int) e1kR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
+{
+ RT_NOREF(uPass);
+ e1kR3SaveConfig(pDevIns->pHlpR3, PDMDEVINS_2_DATA(pDevIns, PE1KSTATE), pSSM);
+ return VINF_SSM_DONT_CALL_AGAIN;
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEPREP,Synchronize.}
+ */
+static DECLCALLBACK(int) e1kR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ RT_NOREF(pSSM);
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+
+ e1kCsEnterReturn(pThis, VERR_SEM_BUSY);
+ e1kCsLeave(pThis);
+ return VINF_SUCCESS;
+#if 0
+ /* 1) Prevent all threads from modifying the state and memory */
+ //pThis->fLocked = true;
+ /* 2) Cancel all timers */
+#ifdef E1K_TX_DELAY
+ e1kCancelTimer(pThis, pThis->CTX_SUFF(pTXDTimer));
+#endif /* E1K_TX_DELAY */
+//#ifdef E1K_USE_TX_TIMERS
+ if (pThis->fTidEnabled)
+ {
+ e1kCancelTimer(pThis, pThis->CTX_SUFF(pTIDTimer));
+#ifndef E1K_NO_TAD
+ e1kCancelTimer(pThis, pThis->CTX_SUFF(pTADTimer));
+#endif /* E1K_NO_TAD */
+ }
+//#endif /* E1K_USE_TX_TIMERS */
+#ifdef E1K_USE_RX_TIMERS
+ e1kCancelTimer(pThis, pThis->CTX_SUFF(pRIDTimer));
+ e1kCancelTimer(pThis, pThis->CTX_SUFF(pRADTimer));
+#endif /* E1K_USE_RX_TIMERS */
+ e1kCancelTimer(pThis, pThis->CTX_SUFF(pIntTimer));
+ /* 3) Did I forget anything? */
+ E1kLog(("%s Locked\n", pThis->szPrf));
+ return VINF_SUCCESS;
+#endif
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC}
+ */
+static DECLCALLBACK(int) e1kR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ e1kR3SaveConfig(pHlp, pThis, pSSM);
+ pThisCC->eeprom.save(pHlp, pSSM);
+ e1kDumpState(pThis);
+ pHlp->pfnSSMPutMem(pSSM, pThis->auRegs, sizeof(pThis->auRegs));
+ pHlp->pfnSSMPutBool(pSSM, pThis->fIntRaised);
+ Phy::saveState(pHlp, pSSM, &pThis->phy);
+ pHlp->pfnSSMPutU32(pSSM, pThis->uSelectedReg);
+ pHlp->pfnSSMPutMem(pSSM, pThis->auMTA, sizeof(pThis->auMTA));
+ pHlp->pfnSSMPutMem(pSSM, &pThis->aRecAddr, sizeof(pThis->aRecAddr));
+ pHlp->pfnSSMPutMem(pSSM, pThis->auVFTA, sizeof(pThis->auVFTA));
+ pHlp->pfnSSMPutU64(pSSM, pThis->u64AckedAt);
+ pHlp->pfnSSMPutU16(pSSM, pThis->u16RxBSize);
+ //pHlp->pfnSSMPutBool(pSSM, pThis->fDelayInts);
+ //pHlp->pfnSSMPutBool(pSSM, pThis->fIntMaskUsed);
+ pHlp->pfnSSMPutU16(pSSM, pThis->u16TxPktLen);
+/** @todo State wrt to the TSE buffer is incomplete, so little point in
+ * saving this actually. */
+ pHlp->pfnSSMPutMem(pSSM, pThis->aTxPacketFallback, pThis->u16TxPktLen);
+ pHlp->pfnSSMPutBool(pSSM, pThis->fIPcsum);
+ pHlp->pfnSSMPutBool(pSSM, pThis->fTCPcsum);
+ pHlp->pfnSSMPutMem(pSSM, &pThis->contextTSE, sizeof(pThis->contextTSE));
+ pHlp->pfnSSMPutMem(pSSM, &pThis->contextNormal, sizeof(pThis->contextNormal));
+ pHlp->pfnSSMPutBool(pSSM, pThis->fVTag);
+ pHlp->pfnSSMPutU16(pSSM, pThis->u16VTagTCI);
+#ifdef E1K_WITH_TXD_CACHE
+# if 0
+ pHlp->pfnSSMPutU8(pSSM, pThis->nTxDFetched);
+ pHlp->pfnSSMPutMem(pSSM, pThis->aTxDescriptors,
+ pThis->nTxDFetched * sizeof(pThis->aTxDescriptors[0]));
+# else
+ /*
+ * There is no point in storing TX descriptor cache entries as we can simply
+ * fetch them again. Moreover, normally the cache is always empty when we
+ * save the state. Store zero entries for compatibility.
+ */
+ pHlp->pfnSSMPutU8(pSSM, 0);
+# endif
+#endif /* E1K_WITH_TXD_CACHE */
+/** @todo GSO requires some more state here. */
+ E1kLog(("%s State has been saved\n", pThis->szPrf));
+ return VINF_SUCCESS;
+}
+
+#if 0
+/**
+ * @callback_method_impl{FNSSMDEVSAVEDONE}
+ */
+static DECLCALLBACK(int) e1kSaveDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+
+ /* If VM is being powered off unlocking will result in assertions in PGM */
+ if (PDMDevHlpGetVM(pDevIns)->enmVMState == VMSTATE_RUNNING)
+ pThis->fLocked = false;
+ else
+ E1kLog(("%s VM is not running -- remain locked\n", pThis->szPrf));
+ E1kLog(("%s Unlocked\n", pThis->szPrf));
+ return VINF_SUCCESS;
+}
+#endif
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADPREP,Synchronize.}
+ */
+static DECLCALLBACK(int) e1kR3LoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ RT_NOREF(pSSM);
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+
+ e1kCsEnterReturn(pThis, VERR_SEM_BUSY);
+ e1kCsLeave(pThis);
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ */
+static DECLCALLBACK(int) e1kR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ int rc;
+
+ if ( uVersion != E1K_SAVEDSTATE_VERSION
+#ifdef E1K_WITH_TXD_CACHE
+ && uVersion != E1K_SAVEDSTATE_VERSION_VBOX_42_VTAG
+#endif /* E1K_WITH_TXD_CACHE */
+ && uVersion != E1K_SAVEDSTATE_VERSION_VBOX_41
+ && uVersion != E1K_SAVEDSTATE_VERSION_VBOX_30)
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+
+ if ( uVersion > E1K_SAVEDSTATE_VERSION_VBOX_30
+ || uPass != SSM_PASS_FINAL)
+ {
+ /* config checks */
+ RTMAC macConfigured;
+ rc = pHlp->pfnSSMGetMem(pSSM, &macConfigured, sizeof(macConfigured));
+ AssertRCReturn(rc, rc);
+ if ( memcmp(&macConfigured, &pThis->macConfigured, sizeof(macConfigured))
+ && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)) )
+ LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n", pThis->szPrf, &pThis->macConfigured, &macConfigured));
+
+ E1KCHIP eChip;
+ rc = pHlp->pfnSSMGetU32(pSSM, &eChip);
+ AssertRCReturn(rc, rc);
+ if (eChip != pThis->eChip)
+ return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("The chip type differs: config=%u saved=%u"), pThis->eChip, eChip);
+ }
+
+ if (uPass == SSM_PASS_FINAL)
+ {
+ if (uVersion > E1K_SAVEDSTATE_VERSION_VBOX_30)
+ {
+ rc = pThisCC->eeprom.load(pHlp, pSSM);
+ AssertRCReturn(rc, rc);
+ }
+ /* the state */
+ pHlp->pfnSSMGetMem(pSSM, &pThis->auRegs, sizeof(pThis->auRegs));
+ pHlp->pfnSSMGetBool(pSSM, &pThis->fIntRaised);
+ /** @todo PHY could be made a separate device with its own versioning */
+ Phy::loadState(pHlp, pSSM, &pThis->phy);
+ pHlp->pfnSSMGetU32(pSSM, &pThis->uSelectedReg);
+ pHlp->pfnSSMGetMem(pSSM, &pThis->auMTA, sizeof(pThis->auMTA));
+ pHlp->pfnSSMGetMem(pSSM, &pThis->aRecAddr, sizeof(pThis->aRecAddr));
+ pHlp->pfnSSMGetMem(pSSM, &pThis->auVFTA, sizeof(pThis->auVFTA));
+ pHlp->pfnSSMGetU64(pSSM, &pThis->u64AckedAt);
+ pHlp->pfnSSMGetU16(pSSM, &pThis->u16RxBSize);
+ //pHlp->pfnSSMGetBool(pSSM, pThis->fDelayInts);
+ //pHlp->pfnSSMGetBool(pSSM, pThis->fIntMaskUsed);
+ rc = pHlp->pfnSSMGetU16(pSSM, &pThis->u16TxPktLen);
+ AssertRCReturn(rc, rc);
+ if (pThis->u16TxPktLen > sizeof(pThis->aTxPacketFallback))
+ pThis->u16TxPktLen = sizeof(pThis->aTxPacketFallback);
+ pHlp->pfnSSMGetMem(pSSM, &pThis->aTxPacketFallback[0], pThis->u16TxPktLen);
+ pHlp->pfnSSMGetBool(pSSM, &pThis->fIPcsum);
+ pHlp->pfnSSMGetBool(pSSM, &pThis->fTCPcsum);
+ pHlp->pfnSSMGetMem(pSSM, &pThis->contextTSE, sizeof(pThis->contextTSE));
+ rc = pHlp->pfnSSMGetMem(pSSM, &pThis->contextNormal, sizeof(pThis->contextNormal));
+ AssertRCReturn(rc, rc);
+ if (uVersion > E1K_SAVEDSTATE_VERSION_VBOX_41)
+ {
+ pHlp->pfnSSMGetBool(pSSM, &pThis->fVTag);
+ rc = pHlp->pfnSSMGetU16(pSSM, &pThis->u16VTagTCI);
+ AssertRCReturn(rc, rc);
+ }
+ else
+ {
+ pThis->fVTag = false;
+ pThis->u16VTagTCI = 0;
+ }
+#ifdef E1K_WITH_TXD_CACHE
+ if (uVersion > E1K_SAVEDSTATE_VERSION_VBOX_42_VTAG)
+ {
+ rc = pHlp->pfnSSMGetU8(pSSM, &pThis->nTxDFetched);
+ AssertRCReturn(rc, rc);
+ if (pThis->nTxDFetched)
+ pHlp->pfnSSMGetMem(pSSM, pThis->aTxDescriptors,
+ pThis->nTxDFetched * sizeof(pThis->aTxDescriptors[0]));
+ }
+ else
+ pThis->nTxDFetched = 0;
+ /**
+ * @todo Perhaps we should not store TXD cache as the entries can be
+ * simply fetched again from guest's memory. Or can't they?
+ */
+#endif /* E1K_WITH_TXD_CACHE */
+#ifdef E1K_WITH_RXD_CACHE
+ /*
+ * There is no point in storing the RX descriptor cache in the saved
+ * state, we just need to make sure it is empty.
+ */
+ pThis->iRxDCurrent = pThis->nRxDFetched = 0;
+#endif /* E1K_WITH_RXD_CACHE */
+ rc = pHlp->pfnSSMHandleGetStatus(pSSM);
+ AssertRCReturn(rc, rc);
+
+ /* derived state */
+ e1kSetupGsoCtx(&pThis->GsoCtx, &pThis->contextTSE);
+
+ E1kLog(("%s State has been restored\n", pThis->szPrf));
+ e1kDumpState(pThis);
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADDONE, Link status adjustments after loading.}
+ */
+static DECLCALLBACK(int) e1kR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+ RT_NOREF(pSSM);
+
+ /* Update promiscuous mode */
+ if (pThisCC->pDrvR3)
+ pThisCC->pDrvR3->pfnSetPromiscuousMode(pThisCC->pDrvR3, !!(RCTL & (RCTL_UPE | RCTL_MPE)));
+
+ /*
+ * Force the link down here, since PDMNETWORKLINKSTATE_DOWN_RESUME is never
+ * passed to us. We go through all this stuff if the link was up and we
+ * wasn't teleported.
+ */
+ if ( (STATUS & STATUS_LU)
+ && !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)
+ && pThis->cMsLinkUpDelay)
+ {
+ e1kR3LinkDownTemp(pDevIns, pThis, pThisCC);
+ }
+ return VINF_SUCCESS;
+}
+
+
+
+/* -=-=-=-=- Debug Info + Log Types -=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNRTSTRFORMATTYPE}
+ */
+static DECLCALLBACK(size_t) e1kR3FmtRxDesc(PFNRTSTROUTPUT pfnOutput,
+ void *pvArgOutput,
+ const char *pszType,
+ void const *pvValue,
+ int cchWidth,
+ int cchPrecision,
+ unsigned fFlags,
+ void *pvUser)
+{
+ RT_NOREF(cchWidth, cchPrecision, fFlags, pvUser);
+ AssertReturn(strcmp(pszType, "e1krxd") == 0, 0);
+ E1KRXDESC* pDesc = (E1KRXDESC*)pvValue;
+ if (!pDesc)
+ return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "NULL_RXD");
+
+ size_t cbPrintf = 0;
+ cbPrintf += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "Address=%16LX Length=%04X Csum=%04X\n",
+ pDesc->u64BufAddr, pDesc->u16Length, pDesc->u16Checksum);
+ cbPrintf += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, " STA: %s %s %s %s %s %s %s ERR: %s %s %s %s SPECIAL: %s VLAN=%03x PRI=%x",
+ pDesc->status.fPIF ? "PIF" : "pif",
+ pDesc->status.fIPCS ? "IPCS" : "ipcs",
+ pDesc->status.fTCPCS ? "TCPCS" : "tcpcs",
+ pDesc->status.fVP ? "VP" : "vp",
+ pDesc->status.fIXSM ? "IXSM" : "ixsm",
+ pDesc->status.fEOP ? "EOP" : "eop",
+ pDesc->status.fDD ? "DD" : "dd",
+ pDesc->status.fRXE ? "RXE" : "rxe",
+ pDesc->status.fIPE ? "IPE" : "ipe",
+ pDesc->status.fTCPE ? "TCPE" : "tcpe",
+ pDesc->status.fCE ? "CE" : "ce",
+ E1K_SPEC_CFI(pDesc->status.u16Special) ? "CFI" :"cfi",
+ E1K_SPEC_VLAN(pDesc->status.u16Special),
+ E1K_SPEC_PRI(pDesc->status.u16Special));
+ return cbPrintf;
+}
+
+/**
+ * @callback_method_impl{FNRTSTRFORMATTYPE}
+ */
+static DECLCALLBACK(size_t) e1kR3FmtTxDesc(PFNRTSTROUTPUT pfnOutput,
+ void *pvArgOutput,
+ const char *pszType,
+ void const *pvValue,
+ int cchWidth,
+ int cchPrecision,
+ unsigned fFlags,
+ void *pvUser)
+{
+ RT_NOREF(cchWidth, cchPrecision, fFlags, pvUser);
+ AssertReturn(strcmp(pszType, "e1ktxd") == 0, 0);
+ E1KTXDESC *pDesc = (E1KTXDESC*)pvValue;
+ if (!pDesc)
+ return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "NULL_TXD");
+
+ size_t cbPrintf = 0;
+ switch (e1kGetDescType(pDesc))
+ {
+ case E1K_DTYP_CONTEXT:
+ cbPrintf += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "Type=Context\n"
+ " IPCSS=%02X IPCSO=%02X IPCSE=%04X TUCSS=%02X TUCSO=%02X TUCSE=%04X\n"
+ " TUCMD:%s%s%s %s %s PAYLEN=%04x HDRLEN=%04x MSS=%04x STA: %s",
+ pDesc->context.ip.u8CSS, pDesc->context.ip.u8CSO, pDesc->context.ip.u16CSE,
+ pDesc->context.tu.u8CSS, pDesc->context.tu.u8CSO, pDesc->context.tu.u16CSE,
+ pDesc->context.dw2.fIDE ? " IDE":"",
+ pDesc->context.dw2.fRS ? " RS" :"",
+ pDesc->context.dw2.fTSE ? " TSE":"",
+ pDesc->context.dw2.fIP ? "IPv4":"IPv6",
+ pDesc->context.dw2.fTCP ? "TCP":"UDP",
+ pDesc->context.dw2.u20PAYLEN,
+ pDesc->context.dw3.u8HDRLEN,
+ pDesc->context.dw3.u16MSS,
+ pDesc->context.dw3.fDD?"DD":"");
+ break;
+ case E1K_DTYP_DATA:
+ cbPrintf += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "Type=Data Address=%16LX DTALEN=%05X\n"
+ " DCMD:%s%s%s%s%s%s%s STA:%s%s%s POPTS:%s%s SPECIAL:%s VLAN=%03x PRI=%x",
+ pDesc->data.u64BufAddr,
+ pDesc->data.cmd.u20DTALEN,
+ pDesc->data.cmd.fIDE ? " IDE" :"",
+ pDesc->data.cmd.fVLE ? " VLE" :"",
+ pDesc->data.cmd.fRPS ? " RPS" :"",
+ pDesc->data.cmd.fRS ? " RS" :"",
+ pDesc->data.cmd.fTSE ? " TSE" :"",
+ pDesc->data.cmd.fIFCS? " IFCS":"",
+ pDesc->data.cmd.fEOP ? " EOP" :"",
+ pDesc->data.dw3.fDD ? " DD" :"",
+ pDesc->data.dw3.fEC ? " EC" :"",
+ pDesc->data.dw3.fLC ? " LC" :"",
+ pDesc->data.dw3.fTXSM? " TXSM":"",
+ pDesc->data.dw3.fIXSM? " IXSM":"",
+ E1K_SPEC_CFI(pDesc->data.dw3.u16Special) ? "CFI" :"cfi",
+ E1K_SPEC_VLAN(pDesc->data.dw3.u16Special),
+ E1K_SPEC_PRI(pDesc->data.dw3.u16Special));
+ break;
+ case E1K_DTYP_LEGACY:
+ cbPrintf += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "Type=Legacy Address=%16LX DTALEN=%05X\n"
+ " CMD:%s%s%s%s%s%s%s STA:%s%s%s CSO=%02x CSS=%02x SPECIAL:%s VLAN=%03x PRI=%x",
+ pDesc->data.u64BufAddr,
+ pDesc->legacy.cmd.u16Length,
+ pDesc->legacy.cmd.fIDE ? " IDE" :"",
+ pDesc->legacy.cmd.fVLE ? " VLE" :"",
+ pDesc->legacy.cmd.fRPS ? " RPS" :"",
+ pDesc->legacy.cmd.fRS ? " RS" :"",
+ pDesc->legacy.cmd.fIC ? " IC" :"",
+ pDesc->legacy.cmd.fIFCS? " IFCS":"",
+ pDesc->legacy.cmd.fEOP ? " EOP" :"",
+ pDesc->legacy.dw3.fDD ? " DD" :"",
+ pDesc->legacy.dw3.fEC ? " EC" :"",
+ pDesc->legacy.dw3.fLC ? " LC" :"",
+ pDesc->legacy.cmd.u8CSO,
+ pDesc->legacy.dw3.u8CSS,
+ E1K_SPEC_CFI(pDesc->legacy.dw3.u16Special) ? "CFI" :"cfi",
+ E1K_SPEC_VLAN(pDesc->legacy.dw3.u16Special),
+ E1K_SPEC_PRI(pDesc->legacy.dw3.u16Special));
+ break;
+ default:
+ cbPrintf += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "Invalid Transmit Descriptor");
+ break;
+ }
+
+ return cbPrintf;
+}
+
+/** Initializes debug helpers (logging format types). */
+static int e1kR3InitDebugHelpers(void)
+{
+ int rc = VINF_SUCCESS;
+ static bool s_fHelpersRegistered = false;
+ if (!s_fHelpersRegistered)
+ {
+ s_fHelpersRegistered = true;
+ rc = RTStrFormatTypeRegister("e1krxd", e1kR3FmtRxDesc, NULL);
+ AssertRCReturn(rc, rc);
+ rc = RTStrFormatTypeRegister("e1ktxd", e1kR3FmtTxDesc, NULL);
+ AssertRCReturn(rc, rc);
+ }
+ return rc;
+}
+
+/**
+ * Status info callback.
+ *
+ * @param pDevIns The device instance.
+ * @param pHlp The output helpers.
+ * @param pszArgs The arguments.
+ */
+static DECLCALLBACK(void) e1kR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ RT_NOREF(pszArgs);
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+ unsigned i;
+ // bool fRcvRing = false;
+ // bool fXmtRing = false;
+
+ /*
+ * Parse args.
+ if (pszArgs)
+ {
+ fRcvRing = strstr(pszArgs, "verbose") || strstr(pszArgs, "rcv");
+ fXmtRing = strstr(pszArgs, "verbose") || strstr(pszArgs, "xmt");
+ }
+ */
+
+ /*
+ * Show info.
+ */
+ pHlp->pfnPrintf(pHlp, "E1000 #%d: port=%04x mmio=%RGp mac-cfg=%RTmac %s%s%s\n",
+ pDevIns->iInstance,
+ PDMDevHlpIoPortGetMappingAddress(pDevIns, pThis->hIoPorts),
+ PDMDevHlpMmioGetMappingAddress(pDevIns, pThis->hMmioRegion),
+ &pThis->macConfigured, g_aChips[pThis->eChip].pcszName,
+ pDevIns->fRCEnabled ? " RC" : "", pDevIns->fR0Enabled ? " R0" : "");
+
+ e1kR3CsEnterAsserted(pThis); /* Not sure why but PCNet does it */
+
+ for (i = 0; i < E1K_NUM_OF_32BIT_REGS; ++i)
+ pHlp->pfnPrintf(pHlp, "%8.8s = %08x\n", g_aE1kRegMap[i].abbrev, pThis->auRegs[i]);
+
+ for (i = 0; i < RT_ELEMENTS(pThis->aRecAddr.array); i++)
+ {
+ E1KRAELEM* ra = pThis->aRecAddr.array + i;
+ if (ra->ctl & RA_CTL_AV)
+ {
+ const char *pcszTmp;
+ switch (ra->ctl & RA_CTL_AS)
+ {
+ case 0: pcszTmp = "DST"; break;
+ case 1: pcszTmp = "SRC"; break;
+ default: pcszTmp = "reserved";
+ }
+ pHlp->pfnPrintf(pHlp, "RA%02d: %s %RTmac\n", i, pcszTmp, ra->addr);
+ }
+ }
+ unsigned cDescs = RDLEN / sizeof(E1KRXDESC);
+ uint32_t rdh = RDH;
+ pHlp->pfnPrintf(pHlp, "\n-- Receive Descriptors (%d total) --\n", cDescs);
+ for (i = 0; i < cDescs; ++i)
+ {
+ E1KRXDESC desc;
+ PDMDevHlpPCIPhysRead(pDevIns, e1kDescAddr(RDBAH, RDBAL, i),
+ &desc, sizeof(desc));
+ if (i == rdh)
+ pHlp->pfnPrintf(pHlp, ">>> ");
+ pHlp->pfnPrintf(pHlp, "%RGp: %R[e1krxd]\n", e1kDescAddr(RDBAH, RDBAL, i), &desc);
+ }
+#ifdef E1K_WITH_RXD_CACHE
+ pHlp->pfnPrintf(pHlp, "\n-- Receive Descriptors in Cache (at %d (RDH %d)/ fetched %d / max %d) --\n",
+ pThis->iRxDCurrent, RDH, pThis->nRxDFetched, E1K_RXD_CACHE_SIZE);
+ if (rdh > pThis->iRxDCurrent)
+ rdh -= pThis->iRxDCurrent;
+ else
+ rdh = cDescs + rdh - pThis->iRxDCurrent;
+ for (i = 0; i < pThis->nRxDFetched; ++i)
+ {
+ if (i == pThis->iRxDCurrent)
+ pHlp->pfnPrintf(pHlp, ">>> ");
+ if (cDescs)
+ pHlp->pfnPrintf(pHlp, "%RGp: %R[e1krxd]\n",
+ e1kDescAddr(RDBAH, RDBAL, rdh++ % cDescs),
+ &pThis->aRxDescriptors[i]);
+ else
+ pHlp->pfnPrintf(pHlp, "<lost>: %R[e1krxd]\n",
+ &pThis->aRxDescriptors[i]);
+ }
+#endif /* E1K_WITH_RXD_CACHE */
+
+ cDescs = TDLEN / sizeof(E1KTXDESC);
+ uint32_t tdh = TDH;
+ pHlp->pfnPrintf(pHlp, "\n-- Transmit Descriptors (%d total) --\n", cDescs);
+ for (i = 0; i < cDescs; ++i)
+ {
+ E1KTXDESC desc;
+ PDMDevHlpPCIPhysRead(pDevIns, e1kDescAddr(TDBAH, TDBAL, i),
+ &desc, sizeof(desc));
+ if (i == tdh)
+ pHlp->pfnPrintf(pHlp, ">>> ");
+ pHlp->pfnPrintf(pHlp, "%RGp: %R[e1ktxd]\n", e1kDescAddr(TDBAH, TDBAL, i), &desc);
+ }
+#ifdef E1K_WITH_TXD_CACHE
+ pHlp->pfnPrintf(pHlp, "\n-- Transmit Descriptors in Cache (at %d (TDH %d)/ fetched %d / max %d) --\n",
+ pThis->iTxDCurrent, TDH, pThis->nTxDFetched, E1K_TXD_CACHE_SIZE);
+ if (tdh > pThis->iTxDCurrent)
+ tdh -= pThis->iTxDCurrent;
+ else
+ tdh = cDescs + tdh - pThis->iTxDCurrent;
+ for (i = 0; i < pThis->nTxDFetched; ++i)
+ {
+ if (i == pThis->iTxDCurrent)
+ pHlp->pfnPrintf(pHlp, ">>> ");
+ if (cDescs)
+ pHlp->pfnPrintf(pHlp, "%RGp: %R[e1ktxd]\n",
+ e1kDescAddr(TDBAH, TDBAL, tdh++ % cDescs),
+ &pThis->aTxDescriptors[i]);
+ else
+ pHlp->pfnPrintf(pHlp, "<lost>: %R[e1ktxd]\n",
+ &pThis->aTxDescriptors[i]);
+ }
+#endif /* E1K_WITH_TXD_CACHE */
+
+
+#ifdef E1K_INT_STATS
+ pHlp->pfnPrintf(pHlp, "Interrupt attempts: %d\n", pThis->uStatIntTry);
+ pHlp->pfnPrintf(pHlp, "Interrupts raised : %d\n", pThis->uStatInt);
+ pHlp->pfnPrintf(pHlp, "Interrupts lowered: %d\n", pThis->uStatIntLower);
+ pHlp->pfnPrintf(pHlp, "ICR outside ISR : %d\n", pThis->uStatNoIntICR);
+ pHlp->pfnPrintf(pHlp, "IMS raised ints : %d\n", pThis->uStatIntIMS);
+ pHlp->pfnPrintf(pHlp, "Interrupts skipped: %d\n", pThis->uStatIntSkip);
+ pHlp->pfnPrintf(pHlp, "Masked interrupts : %d\n", pThis->uStatIntMasked);
+ pHlp->pfnPrintf(pHlp, "Early interrupts : %d\n", pThis->uStatIntEarly);
+ pHlp->pfnPrintf(pHlp, "Late interrupts : %d\n", pThis->uStatIntLate);
+ pHlp->pfnPrintf(pHlp, "Lost interrupts : %d\n", pThis->iStatIntLost);
+ pHlp->pfnPrintf(pHlp, "Interrupts by RX : %d\n", pThis->uStatIntRx);
+ pHlp->pfnPrintf(pHlp, "Interrupts by TX : %d\n", pThis->uStatIntTx);
+ pHlp->pfnPrintf(pHlp, "Interrupts by ICS : %d\n", pThis->uStatIntICS);
+ pHlp->pfnPrintf(pHlp, "Interrupts by RDTR: %d\n", pThis->uStatIntRDTR);
+ pHlp->pfnPrintf(pHlp, "Interrupts by RDMT: %d\n", pThis->uStatIntRXDMT0);
+ pHlp->pfnPrintf(pHlp, "Interrupts by TXQE: %d\n", pThis->uStatIntTXQE);
+ pHlp->pfnPrintf(pHlp, "TX int delay asked: %d\n", pThis->uStatTxIDE);
+ pHlp->pfnPrintf(pHlp, "TX delayed: %d\n", pThis->uStatTxDelayed);
+ pHlp->pfnPrintf(pHlp, "TX delayed expired: %d\n", pThis->uStatTxDelayExp);
+ pHlp->pfnPrintf(pHlp, "TX no report asked: %d\n", pThis->uStatTxNoRS);
+ pHlp->pfnPrintf(pHlp, "TX abs timer expd : %d\n", pThis->uStatTAD);
+ pHlp->pfnPrintf(pHlp, "TX int timer expd : %d\n", pThis->uStatTID);
+ pHlp->pfnPrintf(pHlp, "RX abs timer expd : %d\n", pThis->uStatRAD);
+ pHlp->pfnPrintf(pHlp, "RX int timer expd : %d\n", pThis->uStatRID);
+ pHlp->pfnPrintf(pHlp, "TX CTX descriptors: %d\n", pThis->uStatDescCtx);
+ pHlp->pfnPrintf(pHlp, "TX DAT descriptors: %d\n", pThis->uStatDescDat);
+ pHlp->pfnPrintf(pHlp, "TX LEG descriptors: %d\n", pThis->uStatDescLeg);
+ pHlp->pfnPrintf(pHlp, "Received frames : %d\n", pThis->uStatRxFrm);
+ pHlp->pfnPrintf(pHlp, "Transmitted frames: %d\n", pThis->uStatTxFrm);
+ pHlp->pfnPrintf(pHlp, "TX frames up to 1514: %d\n", pThis->uStatTx1514);
+ pHlp->pfnPrintf(pHlp, "TX frames up to 2962: %d\n", pThis->uStatTx2962);
+ pHlp->pfnPrintf(pHlp, "TX frames up to 4410: %d\n", pThis->uStatTx4410);
+ pHlp->pfnPrintf(pHlp, "TX frames up to 5858: %d\n", pThis->uStatTx5858);
+ pHlp->pfnPrintf(pHlp, "TX frames up to 7306: %d\n", pThis->uStatTx7306);
+ pHlp->pfnPrintf(pHlp, "TX frames up to 8754: %d\n", pThis->uStatTx8754);
+ pHlp->pfnPrintf(pHlp, "TX frames up to 16384: %d\n", pThis->uStatTx16384);
+ pHlp->pfnPrintf(pHlp, "TX frames up to 32768: %d\n", pThis->uStatTx32768);
+ pHlp->pfnPrintf(pHlp, "Larger TX frames : %d\n", pThis->uStatTxLarge);
+#endif /* E1K_INT_STATS */
+
+ e1kCsLeave(pThis);
+}
+
+
+
+/* -=-=-=-=- PDMDEVREG -=-=-=-=- */
+
+/**
+ * Detach notification.
+ *
+ * One port on the network card has been disconnected from the network.
+ *
+ * @param pDevIns The device instance.
+ * @param iLUN The logical unit which is being detached.
+ * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ */
+static DECLCALLBACK(void) e1kR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+ Log(("%s e1kR3Detach:\n", pThis->szPrf));
+ RT_NOREF(fFlags);
+
+ AssertLogRelReturnVoid(iLUN == 0);
+
+ e1kR3CsEnterAsserted(pThis);
+
+ /* Mark device as detached. */
+ pThis->fIsAttached = false;
+ /*
+ * Zero some important members.
+ */
+ pThisCC->pDrvBase = NULL;
+ pThisCC->pDrvR3 = NULL;
+#if 0 /** @todo @bugref{9218} ring-0 driver stuff */
+ pThisR0->pDrvR0 = NIL_RTR0PTR;
+ pThisRC->pDrvRC = NIL_RTRCPTR;
+#endif
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->cs);
+}
+
+/**
+ * Attach the Network attachment.
+ *
+ * One port on the network card has been connected to a network.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The device instance.
+ * @param iLUN The logical unit which is being attached.
+ * @param fFlags Flags, combination of the PDMDEVATT_FLAGS_* \#defines.
+ *
+ * @remarks This code path is not used during construction.
+ */
+static DECLCALLBACK(int) e1kR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+ LogFlow(("%s e1kR3Attach:\n", pThis->szPrf));
+ RT_NOREF(fFlags);
+
+ AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
+
+ e1kR3CsEnterAsserted(pThis);
+
+ /*
+ * Attach the driver.
+ */
+ int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
+ if (RT_SUCCESS(rc))
+ {
+ pThisCC->pDrvR3 = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
+ AssertMsgStmt(pThisCC->pDrvR3, ("Failed to obtain the PDMINETWORKUP interface!\n"),
+ rc = VERR_PDM_MISSING_INTERFACE_BELOW);
+ if (RT_SUCCESS(rc))
+ {
+#if 0 /** @todo @bugref{9218} ring-0 driver stuff */
+ pThisR0->pDrvR0 = PDMIBASER0_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMIBASER0), PDMINETWORKUP);
+ pThisRC->pDrvRC = PDMIBASERC_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMIBASERC), PDMINETWORKUP);
+#endif
+ /* Mark device as attached. */
+ pThis->fIsAttached = true;
+ }
+ }
+ else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
+ || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
+ {
+ /* This should never happen because this function is not called
+ * if there is no driver to attach! */
+ Log(("%s No attached driver!\n", pThis->szPrf));
+ }
+
+ /*
+ * Temporary set the link down if it was up so that the guest will know
+ * that we have change the configuration of the network card
+ */
+ if ((STATUS & STATUS_LU) && RT_SUCCESS(rc))
+ e1kR3LinkDownTemp(pDevIns, pThis, pThisCC);
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->cs);
+ return rc;
+}
+
+/**
+ * @copydoc FNPDMDEVPOWEROFF
+ */
+static DECLCALLBACK(void) e1kR3PowerOff(PPDMDEVINS pDevIns)
+{
+ /* Poke thread waiting for buffer space. */
+ e1kWakeupReceive(pDevIns, PDMDEVINS_2_DATA(pDevIns, PE1KSTATE));
+}
+
+/**
+ * @copydoc FNPDMDEVRESET
+ */
+static DECLCALLBACK(void) e1kR3Reset(PPDMDEVINS pDevIns)
+{
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+#ifdef E1K_TX_DELAY
+ e1kCancelTimer(pDevIns, pThis, pThis->hTXDTimer);
+#endif /* E1K_TX_DELAY */
+ e1kCancelTimer(pDevIns, pThis, pThis->hIntTimer);
+ e1kCancelTimer(pDevIns, pThis, pThis->hLUTimer);
+ e1kXmitFreeBuf(pThis, pThisCC);
+ pThis->u16TxPktLen = 0;
+ pThis->fIPcsum = false;
+ pThis->fTCPcsum = false;
+ pThis->fIntMaskUsed = false;
+ pThis->fDelayInts = false;
+ pThis->fLocked = false;
+ pThis->u64AckedAt = 0;
+ e1kR3HardReset(pDevIns, pThis, pThisCC);
+}
+
+/**
+ * @copydoc FNPDMDEVSUSPEND
+ */
+static DECLCALLBACK(void) e1kR3Suspend(PPDMDEVINS pDevIns)
+{
+ /* Poke thread waiting for buffer space. */
+ e1kWakeupReceive(pDevIns, PDMDEVINS_2_DATA(pDevIns, PE1KSTATE));
+}
+
+/**
+ * Device relocation callback.
+ *
+ * When this callback is called the device instance data, and if the
+ * device have a GC component, is being relocated, or/and the selectors
+ * have been changed. The device must use the chance to perform the
+ * necessary pointer relocations and data updates.
+ *
+ * Before the GC code is executed the first time, this function will be
+ * called with a 0 delta so GC pointer calculations can be one in one place.
+ *
+ * @param pDevIns Pointer to the device instance.
+ * @param offDelta The relocation delta relative to the old location.
+ *
+ * @remark A relocation CANNOT fail.
+ */
+static DECLCALLBACK(void) e1kR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
+{
+ PE1KSTATERC pThisRC = PDMINS_2_DATA_RC(pDevIns, PE1KSTATERC);
+ if (pThisRC)
+ pThisRC->pDevInsRC = PDMDEVINS_2_RCPTR(pDevIns);
+ RT_NOREF(offDelta);
+}
+
+/**
+ * Destruct a device instance.
+ *
+ * We need to free non-VM resources only.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The device instance data.
+ * @thread EMT
+ */
+static DECLCALLBACK(int) e1kR3Destruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+
+ e1kDumpState(pThis);
+ E1kLog(("%s Destroying instance\n", pThis->szPrf));
+ if (PDMDevHlpCritSectIsInitialized(pDevIns, &pThis->cs))
+ {
+ if (pThis->hEventMoreRxDescAvail != NIL_SUPSEMEVENT)
+ {
+ PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventMoreRxDescAvail);
+ RTThreadYield();
+ PDMDevHlpSUPSemEventClose(pDevIns, pThis->hEventMoreRxDescAvail);
+ pThis->hEventMoreRxDescAvail = NIL_SUPSEMEVENT;
+ }
+#ifdef E1K_WITH_TX_CS
+ PDMDevHlpCritSectDelete(pDevIns, &pThis->csTx);
+#endif /* E1K_WITH_TX_CS */
+ PDMDevHlpCritSectDelete(pDevIns, &pThis->csRx);
+ PDMDevHlpCritSectDelete(pDevIns, &pThis->cs);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Set PCI configuration space registers.
+ *
+ * @param pci Reference to PCI device structure.
+ * @thread EMT
+ */
+static void e1kR3ConfigurePciDev(PPDMPCIDEV pPciDev, E1KCHIP eChip)
+{
+ Assert(eChip < RT_ELEMENTS(g_aChips));
+ /* Configure PCI Device, assume 32-bit mode ******************************/
+ PDMPciDevSetVendorId(pPciDev, g_aChips[eChip].uPCIVendorId);
+ PDMPciDevSetDeviceId(pPciDev, g_aChips[eChip].uPCIDeviceId);
+ PDMPciDevSetWord( pPciDev, VBOX_PCI_SUBSYSTEM_VENDOR_ID, g_aChips[eChip].uPCISubsystemVendorId);
+ PDMPciDevSetWord( pPciDev, VBOX_PCI_SUBSYSTEM_ID, g_aChips[eChip].uPCISubsystemId);
+
+ PDMPciDevSetWord( pPciDev, VBOX_PCI_COMMAND, 0x0000);
+ /* DEVSEL Timing (medium device), 66 MHz Capable, New capabilities */
+ PDMPciDevSetWord( pPciDev, VBOX_PCI_STATUS,
+ VBOX_PCI_STATUS_DEVSEL_MEDIUM | VBOX_PCI_STATUS_CAP_LIST | VBOX_PCI_STATUS_66MHZ);
+ /* Stepping A2 */
+ PDMPciDevSetByte( pPciDev, VBOX_PCI_REVISION_ID, 0x02);
+ /* Ethernet adapter */
+ PDMPciDevSetByte( pPciDev, VBOX_PCI_CLASS_PROG, 0x00);
+ PDMPciDevSetWord( pPciDev, VBOX_PCI_CLASS_DEVICE, 0x0200);
+ /* normal single function Ethernet controller */
+ PDMPciDevSetByte( pPciDev, VBOX_PCI_HEADER_TYPE, 0x00);
+ /* Memory Register Base Address */
+ PDMPciDevSetDWord(pPciDev, VBOX_PCI_BASE_ADDRESS_0, 0x00000000);
+ /* Memory Flash Base Address */
+ PDMPciDevSetDWord(pPciDev, VBOX_PCI_BASE_ADDRESS_1, 0x00000000);
+ /* IO Register Base Address */
+ PDMPciDevSetDWord(pPciDev, VBOX_PCI_BASE_ADDRESS_2, 0x00000001);
+ /* Expansion ROM Base Address */
+ PDMPciDevSetDWord(pPciDev, VBOX_PCI_ROM_ADDRESS, 0x00000000);
+ /* Capabilities Pointer */
+ PDMPciDevSetByte( pPciDev, VBOX_PCI_CAPABILITY_LIST, 0xDC);
+ /* Interrupt Pin: INTA# */
+ PDMPciDevSetByte( pPciDev, VBOX_PCI_INTERRUPT_PIN, 0x01);
+ /* Max_Lat/Min_Gnt: very high priority and time slice */
+ PDMPciDevSetByte( pPciDev, VBOX_PCI_MIN_GNT, 0xFF);
+ PDMPciDevSetByte( pPciDev, VBOX_PCI_MAX_LAT, 0x00);
+
+ /* PCI Power Management Registers ****************************************/
+ /* Capability ID: PCI Power Management Registers */
+ PDMPciDevSetByte( pPciDev, 0xDC, VBOX_PCI_CAP_ID_PM);
+ /* Next Item Pointer: PCI-X */
+ PDMPciDevSetByte( pPciDev, 0xDC + 1, 0xE4);
+ /* Power Management Capabilities: PM disabled, DSI */
+ PDMPciDevSetWord( pPciDev, 0xDC + 2,
+ 0x0002 | VBOX_PCI_PM_CAP_DSI);
+ /* Power Management Control / Status Register: PM disabled */
+ PDMPciDevSetWord( pPciDev, 0xDC + 4, 0x0000);
+ /* PMCSR_BSE Bridge Support Extensions: Not supported */
+ PDMPciDevSetByte( pPciDev, 0xDC + 6, 0x00);
+ /* Data Register: PM disabled, always 0 */
+ PDMPciDevSetByte( pPciDev, 0xDC + 7, 0x00);
+
+ /* PCI-X Configuration Registers *****************************************/
+ /* Capability ID: PCI-X Configuration Registers */
+ PDMPciDevSetByte( pPciDev, 0xE4, VBOX_PCI_CAP_ID_PCIX);
+#ifdef E1K_WITH_MSI
+ PDMPciDevSetByte( pPciDev, 0xE4 + 1, 0x80);
+#else
+ /* Next Item Pointer: None (Message Signalled Interrupts are disabled) */
+ PDMPciDevSetByte( pPciDev, 0xE4 + 1, 0x00);
+#endif
+ /* PCI-X Command: Enable Relaxed Ordering */
+ PDMPciDevSetWord( pPciDev, 0xE4 + 2, VBOX_PCI_X_CMD_ERO);
+ /* PCI-X Status: 32-bit, 66MHz*/
+ /** @todo is this value really correct? fff8 doesn't look like actual PCI address */
+ PDMPciDevSetDWord(pPciDev, 0xE4 + 4, 0x0040FFF8);
+}
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) e1kR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+ int rc;
+
+ /*
+ * Initialize the instance data (state).
+ * Note! Caller has initialized it to ZERO already.
+ */
+ RTStrPrintf(pThis->szPrf, sizeof(pThis->szPrf), "E1000#%d", iInstance);
+ E1kLog(("%s Constructing new instance sizeof(E1KRXDESC)=%d\n", pThis->szPrf, sizeof(E1KRXDESC)));
+ pThis->hEventMoreRxDescAvail = NIL_SUPSEMEVENT;
+ pThis->u16TxPktLen = 0;
+ pThis->fIPcsum = false;
+ pThis->fTCPcsum = false;
+ pThis->fIntMaskUsed = false;
+ pThis->fDelayInts = false;
+ pThis->fLocked = false;
+ pThis->u64AckedAt = 0;
+ pThis->led.u32Magic = PDMLED_MAGIC;
+ pThis->u32PktNo = 1;
+ pThis->fIsAttached = false;
+
+ pThisCC->pDevInsR3 = pDevIns;
+ pThisCC->pShared = pThis;
+
+ /* Interfaces */
+ pThisCC->IBase.pfnQueryInterface = e1kR3QueryInterface;
+
+ pThisCC->INetworkDown.pfnWaitReceiveAvail = e1kR3NetworkDown_WaitReceiveAvail;
+ pThisCC->INetworkDown.pfnReceive = e1kR3NetworkDown_Receive;
+ pThisCC->INetworkDown.pfnXmitPending = e1kR3NetworkDown_XmitPending;
+
+ pThisCC->ILeds.pfnQueryStatusLed = e1kR3QueryStatusLed;
+
+ pThisCC->INetworkConfig.pfnGetMac = e1kR3GetMac;
+ pThisCC->INetworkConfig.pfnGetLinkState = e1kR3GetLinkState;
+ pThisCC->INetworkConfig.pfnSetLinkState = e1kR3SetLinkState;
+
+ /*
+ * Internal validations.
+ */
+ for (uint32_t iReg = 1; iReg < E1K_NUM_OF_BINARY_SEARCHABLE; iReg++)
+ AssertLogRelMsgReturn( g_aE1kRegMap[iReg].offset > g_aE1kRegMap[iReg - 1].offset
+ && g_aE1kRegMap[iReg].offset + g_aE1kRegMap[iReg].size
+ >= g_aE1kRegMap[iReg - 1].offset + g_aE1kRegMap[iReg - 1].size,
+ ("%s@%#xLB%#x vs %s@%#xLB%#x\n",
+ g_aE1kRegMap[iReg].abbrev, g_aE1kRegMap[iReg].offset, g_aE1kRegMap[iReg].size,
+ g_aE1kRegMap[iReg - 1].abbrev, g_aE1kRegMap[iReg - 1].offset, g_aE1kRegMap[iReg - 1].size),
+ VERR_INTERNAL_ERROR_4);
+
+ /*
+ * Validate configuration.
+ */
+ PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns,
+ "MAC|"
+ "CableConnected|"
+ "AdapterType|"
+ "LineSpeed|"
+ "ItrEnabled|"
+ "ItrRxEnabled|"
+ "EthernetCRC|"
+ "GSOEnabled|"
+ "LinkUpDelay|"
+ "StatNo",
+ "");
+
+ /** @todo LineSpeed unused! */
+
+ /*
+ * Get config params
+ */
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", pThis->macConfigured.au8, sizeof(pThis->macConfigured.au8));
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get MAC address"));
+ rc = pHlp->pfnCFGMQueryBool(pCfg, "CableConnected", &pThis->fCableConnected);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the value of 'CableConnected'"));
+ rc = pHlp->pfnCFGMQueryU32(pCfg, "AdapterType", (uint32_t*)&pThis->eChip);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the value of 'AdapterType'"));
+ Assert(pThis->eChip <= E1K_CHIP_82545EM);
+
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "EthernetCRC", &pThis->fEthernetCRC, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the value of 'EthernetCRC'"));
+
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "GSOEnabled", &pThis->fGSOEnabled, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the value of 'GSOEnabled'"));
+
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "ItrEnabled", &pThis->fItrEnabled, false);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the value of 'ItrEnabled'"));
+
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "ItrRxEnabled", &pThis->fItrRxEnabled, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the value of 'ItrRxEnabled'"));
+
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "TidEnabled", &pThis->fTidEnabled, false);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the value of 'TidEnabled'"));
+
+ /*
+ * Increased the link up delay from 3 to 5 seconds to make sure a guest notices the link loss
+ * and updates its network configuration when the link is restored. See @bugref{10114}.
+ */
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", (uint32_t*)&pThis->cMsLinkUpDelay, 5000); /* ms */
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc,
+ N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
+ Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
+ if (pThis->cMsLinkUpDelay > 5000)
+ LogRel(("%s: WARNING! Link up delay is set to %u seconds!\n", pThis->szPrf, pThis->cMsLinkUpDelay / 1000));
+ else if (pThis->cMsLinkUpDelay == 0)
+ LogRel(("%s: WARNING! Link up delay is disabled!\n", pThis->szPrf));
+
+ uint32_t uStatNo = (uint32_t)iInstance;
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "StatNo", &uStatNo, (uint32_t)iInstance);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"StatNo\" value"));
+
+ LogRel(("%s: Chip=%s LinkUpDelay=%ums EthernetCRC=%s GSO=%s Itr=%s ItrRx=%s TID=%s R0=%s RC=%s\n", pThis->szPrf,
+ g_aChips[pThis->eChip].pcszName, pThis->cMsLinkUpDelay,
+ pThis->fEthernetCRC ? "on" : "off",
+ pThis->fGSOEnabled ? "enabled" : "disabled",
+ pThis->fItrEnabled ? "enabled" : "disabled",
+ pThis->fItrRxEnabled ? "enabled" : "disabled",
+ pThis->fTidEnabled ? "enabled" : "disabled",
+ pDevIns->fR0Enabled ? "enabled" : "disabled",
+ pDevIns->fRCEnabled ? "enabled" : "disabled"));
+
+ /*
+ * Initialize sub-components and register everything with the VMM.
+ */
+
+ /* Initialize the EEPROM. */
+ pThisCC->eeprom.init(pThis->macConfigured);
+
+ /* Initialize internal PHY. */
+ Phy::init(&pThis->phy, iInstance, pThis->eChip == E1K_CHIP_82543GC ? PHY_EPID_M881000 : PHY_EPID_M881011);
+
+ /* Initialize critical sections. We do our own locking. */
+ rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
+ AssertRCReturn(rc, rc);
+
+ rc = PDMDevHlpCritSectInit(pDevIns, &pThis->cs, RT_SRC_POS, "E1000#%d", iInstance);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpCritSectInit(pDevIns, &pThis->csRx, RT_SRC_POS, "E1000#%dRX", iInstance);
+ AssertRCReturn(rc, rc);
+#ifdef E1K_WITH_TX_CS
+ rc = PDMDevHlpCritSectInit(pDevIns, &pThis->csTx, RT_SRC_POS, "E1000#%dTX", iInstance);
+ AssertRCReturn(rc, rc);
+#endif
+
+ /* Saved state registration. */
+ rc = PDMDevHlpSSMRegisterEx(pDevIns, E1K_SAVEDSTATE_VERSION, sizeof(E1KSTATE), NULL,
+ NULL, e1kR3LiveExec, NULL,
+ e1kR3SavePrep, e1kR3SaveExec, NULL,
+ e1kR3LoadPrep, e1kR3LoadExec, e1kR3LoadDone);
+ AssertRCReturn(rc, rc);
+
+ /* Set PCI config registers and register ourselves with the PCI bus. */
+ PDMPCIDEV_ASSERT_VALID(pDevIns, pDevIns->apPciDevs[0]);
+ e1kR3ConfigurePciDev(pDevIns->apPciDevs[0], pThis->eChip);
+ rc = PDMDevHlpPCIRegister(pDevIns, pDevIns->apPciDevs[0]);
+ AssertRCReturn(rc, rc);
+
+#ifdef E1K_WITH_MSI
+ PDMMSIREG MsiReg;
+ RT_ZERO(MsiReg);
+ MsiReg.cMsiVectors = 1;
+ MsiReg.iMsiCapOffset = 0x80;
+ MsiReg.iMsiNextOffset = 0x0;
+ MsiReg.fMsi64bit = false;
+ rc = PDMDevHlpPCIRegisterMsi(pDevIns, &MsiReg);
+ AssertRCReturn(rc, rc);
+#endif
+
+ /*
+ * Map our registers to memory space (region 0, see e1kR3ConfigurePciDev)
+ * From the spec (regarding flags):
+ * For registers that should be accessed as 32-bit double words,
+ * partial writes (less than a 32-bit double word) is ignored.
+ * Partial reads return all 32 bits of data regardless of the
+ * byte enables.
+ */
+ rc = PDMDevHlpMmioCreateEx(pDevIns, E1K_MM_SIZE, IOMMMIO_FLAGS_READ_DWORD | IOMMMIO_FLAGS_WRITE_ONLY_DWORD,
+ pDevIns->apPciDevs[0], 0 /*iPciRegion*/,
+ e1kMMIOWrite, e1kMMIORead, NULL /*pfnFill*/, NULL /*pvUser*/, "E1000", &pThis->hMmioRegion);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpPCIIORegionRegisterMmio(pDevIns, 0, E1K_MM_SIZE, PCI_ADDRESS_SPACE_MEM, pThis->hMmioRegion, NULL);
+ AssertRCReturn(rc, rc);
+
+ /* Map our registers to IO space (region 2, see e1kR3ConfigurePciDev) */
+ static IOMIOPORTDESC const s_aExtDescs[] =
+ {
+ { "IOADDR", "IOADDR", NULL, NULL }, { "unused", "unused", NULL, NULL }, { "unused", "unused", NULL, NULL }, { "unused", "unused", NULL, NULL },
+ { "IODATA", "IODATA", NULL, NULL }, { "unused", "unused", NULL, NULL }, { "unused", "unused", NULL, NULL }, { "unused", "unused", NULL, NULL },
+ { NULL, NULL, NULL, NULL }
+ };
+ rc = PDMDevHlpIoPortCreate(pDevIns, E1K_IOPORT_SIZE, pDevIns->apPciDevs[0], 2 /*iPciRegion*/,
+ e1kIOPortOut, e1kIOPortIn, NULL /*pvUser*/, "E1000", s_aExtDescs, &pThis->hIoPorts);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpPCIIORegionRegisterIo(pDevIns, 2, E1K_IOPORT_SIZE, pThis->hIoPorts);
+ AssertRCReturn(rc, rc);
+
+ /* Create transmit queue */
+ rc = PDMDevHlpTaskCreate(pDevIns, PDMTASK_F_RZ, "E1000-Xmit", e1kR3TxTaskCallback, NULL, &pThis->hTxTask);
+ AssertRCReturn(rc, rc);
+
+#ifdef E1K_TX_DELAY
+ /* Create Transmit Delay Timer */
+ rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, e1kR3TxDelayTimer, pThis,
+ TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0, "E1000 Xmit Delay", &pThis->hTXDTimer);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpTimerSetCritSect(pDevIns, pThis->hTXDTimer, &pThis->csTx);
+ AssertRCReturn(rc, rc);
+#endif /* E1K_TX_DELAY */
+
+//#ifdef E1K_USE_TX_TIMERS
+ if (pThis->fTidEnabled)
+ {
+ /* Create Transmit Interrupt Delay Timer */
+ rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, e1kR3TxIntDelayTimer, pThis,
+ TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0, "E1000 Xmit IRQ Delay", &pThis->hTIDTimer);
+ AssertRCReturn(rc, rc);
+
+# ifndef E1K_NO_TAD
+ /* Create Transmit Absolute Delay Timer */
+ rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, e1kR3TxAbsDelayTimer, pThis,
+ TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0, "E1000 Xmit Abs Delay", &pThis->hTADTimer);
+ AssertRCReturn(rc, rc);
+# endif /* E1K_NO_TAD */
+ }
+//#endif /* E1K_USE_TX_TIMERS */
+
+#ifdef E1K_USE_RX_TIMERS
+ /* Create Receive Interrupt Delay Timer */
+ rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, e1kR3RxIntDelayTimer, pThis,
+ TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0, "E1000 Recv IRQ Delay", &pThis->hRIDTimer);
+ AssertRCReturn(rc, rc);
+
+ /* Create Receive Absolute Delay Timer */
+ rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, e1kR3RxAbsDelayTimer, pThis,
+ TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0, "E1000 Recv Abs Delay", &pThis->hRADTimer);
+ AssertRCReturn(rc, rc);
+#endif /* E1K_USE_RX_TIMERS */
+
+ /* Create Late Interrupt Timer */
+ rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, e1kR3LateIntTimer, pThis,
+ TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0, "E1000 Late IRQ", &pThis->hIntTimer);
+ AssertRCReturn(rc, rc);
+
+ /* Create Link Up Timer */
+ rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, e1kR3LinkUpTimer, pThis,
+ TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0, "E1000 Link Up", &pThis->hLUTimer);
+ AssertRCReturn(rc, rc);
+
+ /* Register the info item */
+ char szTmp[20];
+ RTStrPrintf(szTmp, sizeof(szTmp), "e1k%d", iInstance);
+ PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "E1000 info.", e1kR3Info);
+
+ /* Status driver */
+ PPDMIBASE pBase;
+ rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pBase, "Status Port");
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
+ pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
+
+ /* Network driver */
+ rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
+ if (RT_SUCCESS(rc))
+ {
+ pThisCC->pDrvR3 = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
+ AssertMsgReturn(pThisCC->pDrvR3, ("Failed to obtain the PDMINETWORKUP interface!\n"), VERR_PDM_MISSING_INTERFACE_BELOW);
+
+#if 0 /** @todo @bugref{9218} ring-0 driver stuff */
+ pThisR0->pDrvR0 = PDMIBASER0_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMIBASER0), PDMINETWORKUP);
+ pThisRC->pDrvRC = PDMIBASERC_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMIBASERC), PDMINETWORKUP);
+#endif
+ /* Mark device as attached. */
+ pThis->fIsAttached = true;
+ }
+ else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
+ || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
+ {
+ /* No error! */
+ E1kLog(("%s This adapter is not attached to any network!\n", pThis->szPrf));
+ }
+ else
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the network LUN"));
+
+ rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hEventMoreRxDescAvail);
+ AssertRCReturn(rc, rc);
+
+ rc = e1kR3InitDebugHelpers();
+ AssertRCReturn(rc, rc);
+
+ e1kR3HardReset(pDevIns, pThis, pThisCC);
+
+ /*
+ * Register statistics.
+ * The /Public/ bits are official and used by session info in the GUI.
+ */
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
+ "Amount of data received", "/Public/NetAdapter/%u/BytesReceived", uStatNo);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
+ "Amount of data transmitted", "/Public/NetAdapter/%u/BytesTransmitted", uStatNo);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pDevIns->iInstance, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
+ "Device instance number", "/Public/NetAdapter/%u/%s", uStatNo, pDevIns->pReg->szName);
+
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, "ReceiveBytes", STAMUNIT_BYTES, "Amount of data received");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, "TransmitBytes", STAMUNIT_BYTES, "Amount of data transmitted");
+
+#if defined(VBOX_WITH_STATISTICS)
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOReadRZ, STAMTYPE_PROFILE, "MMIO/ReadRZ", STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in RZ");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOReadR3, STAMTYPE_PROFILE, "MMIO/ReadR3", STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in R3");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOWriteRZ, STAMTYPE_PROFILE, "MMIO/WriteRZ", STAMUNIT_TICKS_PER_CALL, "Profiling MMIO writes in RZ");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOWriteR3, STAMTYPE_PROFILE, "MMIO/WriteR3", STAMUNIT_TICKS_PER_CALL, "Profiling MMIO writes in R3");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatEEPROMRead, STAMTYPE_PROFILE, "EEPROM/Read", STAMUNIT_TICKS_PER_CALL, "Profiling EEPROM reads");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatEEPROMWrite, STAMTYPE_PROFILE, "EEPROM/Write", STAMUNIT_TICKS_PER_CALL, "Profiling EEPROM writes");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOReadRZ, STAMTYPE_PROFILE, "IO/ReadRZ", STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in RZ");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOReadR3, STAMTYPE_PROFILE, "IO/ReadR3", STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in R3");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOWriteRZ, STAMTYPE_PROFILE, "IO/WriteRZ", STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in RZ");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOWriteR3, STAMTYPE_PROFILE, "IO/WriteR3", STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in R3");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatLateIntTimer, STAMTYPE_PROFILE, "LateInt/Timer", STAMUNIT_TICKS_PER_CALL, "Profiling late int timer");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatLateInts, STAMTYPE_COUNTER, "LateInt/Occured", STAMUNIT_OCCURENCES, "Number of late interrupts");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIntsRaised, STAMTYPE_COUNTER, "Interrupts/Raised", STAMUNIT_OCCURENCES, "Number of raised interrupts");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIntsPrevented, STAMTYPE_COUNTER, "Interrupts/Prevented", STAMUNIT_OCCURENCES, "Number of prevented interrupts");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, "Receive/Total", STAMUNIT_TICKS_PER_CALL, "Profiling receive");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveCRC, STAMTYPE_PROFILE, "Receive/CRC", STAMUNIT_TICKS_PER_CALL, "Profiling receive checksumming");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveFilter, STAMTYPE_PROFILE, "Receive/Filter", STAMUNIT_TICKS_PER_CALL, "Profiling receive filtering");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveStore, STAMTYPE_PROFILE, "Receive/Store", STAMUNIT_TICKS_PER_CALL, "Profiling receive storing");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, "RxOverflow", STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflowWakeupRZ, STAMTYPE_COUNTER, "RxOverflowWakeupRZ", STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups in RZ");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflowWakeupR3, STAMTYPE_COUNTER, "RxOverflowWakeupR3", STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups in R3");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitRZ, STAMTYPE_PROFILE, "Transmit/TotalRZ", STAMUNIT_TICKS_PER_CALL, "Profiling transmits in RZ");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitR3, STAMTYPE_PROFILE, "Transmit/TotalR3", STAMUNIT_TICKS_PER_CALL, "Profiling transmits in R3");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitSendRZ, STAMTYPE_PROFILE, "Transmit/SendRZ", STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in RZ");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitSendR3, STAMTYPE_PROFILE, "Transmit/SendR3", STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in R3");
+
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTxDescCtxNormal, STAMTYPE_COUNTER, "TxDesc/ContexNormal", STAMUNIT_OCCURENCES, "Number of normal context descriptors");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTxDescCtxTSE, STAMTYPE_COUNTER, "TxDesc/ContextTSE", STAMUNIT_OCCURENCES, "Number of TSE context descriptors");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTxDescData, STAMTYPE_COUNTER, "TxDesc/Data", STAMUNIT_OCCURENCES, "Number of TX data descriptors");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTxDescLegacy, STAMTYPE_COUNTER, "TxDesc/Legacy", STAMUNIT_OCCURENCES, "Number of TX legacy descriptors");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTxDescTSEData, STAMTYPE_COUNTER, "TxDesc/TSEData", STAMUNIT_OCCURENCES, "Number of TX TSE data descriptors");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTxPathFallback, STAMTYPE_COUNTER, "TxPath/Fallback", STAMUNIT_OCCURENCES, "Fallback TSE descriptor path");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTxPathGSO, STAMTYPE_COUNTER, "TxPath/GSO", STAMUNIT_OCCURENCES, "GSO TSE descriptor path");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTxPathRegular, STAMTYPE_COUNTER, "TxPath/Normal", STAMUNIT_OCCURENCES, "Regular descriptor path");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatPHYAccesses, STAMTYPE_COUNTER, "PHYAccesses", STAMUNIT_OCCURENCES, "Number of PHY accesses");
+ for (unsigned iReg = 0; iReg < E1K_NUM_OF_REGS; iReg++)
+ {
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatRegReads[iReg], STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
+ g_aE1kRegMap[iReg].name, "Regs/%s-Reads", g_aE1kRegMap[iReg].abbrev);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatRegWrites[iReg], STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES,
+ g_aE1kRegMap[iReg].name, "Regs/%s-Writes", g_aE1kRegMap[iReg].abbrev);
+ }
+#endif /* VBOX_WITH_STATISTICS */
+
+#ifdef E1K_INT_STATS
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->u64ArmedAt, STAMTYPE_U64, "u64ArmedAt", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatMaxTxDelay, STAMTYPE_U64, "uStatMaxTxDelay", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatInt, STAMTYPE_U32, "uStatInt", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatIntTry, STAMTYPE_U32, "uStatIntTry", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatIntLower, STAMTYPE_U32, "uStatIntLower", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatNoIntICR, STAMTYPE_U32, "uStatNoIntICR", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->iStatIntLost, STAMTYPE_U32, "iStatIntLost", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->iStatIntLostOne, STAMTYPE_U32, "iStatIntLostOne", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatIntIMS, STAMTYPE_U32, "uStatIntIMS", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatIntSkip, STAMTYPE_U32, "uStatIntSkip", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatIntLate, STAMTYPE_U32, "uStatIntLate", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatIntMasked, STAMTYPE_U32, "uStatIntMasked", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatIntEarly, STAMTYPE_U32, "uStatIntEarly", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatIntRx, STAMTYPE_U32, "uStatIntRx", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatIntTx, STAMTYPE_U32, "uStatIntTx", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatIntICS, STAMTYPE_U32, "uStatIntICS", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatIntRDTR, STAMTYPE_U32, "uStatIntRDTR", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatIntRXDMT0, STAMTYPE_U32, "uStatIntRXDMT0", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatIntTXQE, STAMTYPE_U32, "uStatIntTXQE", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatTxNoRS, STAMTYPE_U32, "uStatTxNoRS", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatTxIDE, STAMTYPE_U32, "uStatTxIDE", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatTxDelayed, STAMTYPE_U32, "uStatTxDelayed", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatTxDelayExp, STAMTYPE_U32, "uStatTxDelayExp", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatTAD, STAMTYPE_U32, "uStatTAD", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatTID, STAMTYPE_U32, "uStatTID", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatRAD, STAMTYPE_U32, "uStatRAD", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatRID, STAMTYPE_U32, "uStatRID", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatRxFrm, STAMTYPE_U32, "uStatRxFrm", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatTxFrm, STAMTYPE_U32, "uStatTxFrm", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatDescCtx, STAMTYPE_U32, "uStatDescCtx", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatDescDat, STAMTYPE_U32, "uStatDescDat", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatDescLeg, STAMTYPE_U32, "uStatDescLeg", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatTx1514, STAMTYPE_U32, "uStatTx1514", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatTx2962, STAMTYPE_U32, "uStatTx2962", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatTx4410, STAMTYPE_U32, "uStatTx4410", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatTx5858, STAMTYPE_U32, "uStatTx5858", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatTx7306, STAMTYPE_U32, "uStatTx7306", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatTx8754, STAMTYPE_U32, "uStatTx8754", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatTx16384, STAMTYPE_U32, "uStatTx16384", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatTx32768, STAMTYPE_U32, "uStatTx32768", STAMUNIT_NS, NULL);
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->uStatTxLarge, STAMTYPE_U32, "uStatTxLarge", STAMUNIT_NS, NULL);
+#endif /* E1K_INT_STATS */
+
+ return VINF_SUCCESS;
+}
+
+#else /* !IN_RING3 */
+
+/**
+ * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
+ */
+static DECLCALLBACK(int) e1kRZConstruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PE1KSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PE1KSTATE);
+ PE1KSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PE1KSTATECC);
+
+ /* Initialize context specific state data: */
+ pThisCC->CTX_SUFF(pDevIns) = pDevIns;
+ /** @todo @bugref{9218} ring-0 driver stuff */
+ pThisCC->CTX_SUFF(pDrv) = NULL;
+ pThisCC->CTX_SUFF(pTxSg) = NULL;
+
+ /* Configure critical sections the same way: */
+ int rc = PDMDevHlpSetDeviceCritSect(pDevIns, PDMDevHlpCritSectGetNop(pDevIns));
+ AssertRCReturn(rc, rc);
+
+ /* Set up MMIO and I/O port callbacks for this context: */
+ rc = PDMDevHlpMmioSetUpContext(pDevIns, pThis->hMmioRegion, e1kMMIOWrite, e1kMMIORead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+
+ rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPorts, e1kIOPortOut, e1kIOPortIn, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+
+ return VINF_SUCCESS;
+}
+
+#endif /* !IN_RING3 */
+
+/**
+ * The device registration structure.
+ */
+const PDMDEVREG g_DeviceE1000 =
+{
+ /* .u32version = */ PDM_DEVREG_VERSION,
+ /* .uReserved0 = */ 0,
+ /* .szName = */ "e1000",
+ /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
+ /* .fClass = */ PDM_DEVREG_CLASS_NETWORK,
+ /* .cMaxInstances = */ ~0U,
+ /* .uSharedVersion = */ 42,
+ /* .cbInstanceShared = */ sizeof(E1KSTATE),
+ /* .cbInstanceCC = */ sizeof(E1KSTATECC),
+ /* .cbInstanceRC = */ sizeof(E1KSTATERC),
+ /* .cMaxPciDevices = */ 1,
+ /* .cMaxMsixVectors = */ 0,
+ /* .pszDescription = */ "Intel PRO/1000 MT Desktop Ethernet.",
+#if defined(IN_RING3)
+ /* .pszRCMod = */ "VBoxDDRC.rc",
+ /* .pszR0Mod = */ "VBoxDDR0.r0",
+ /* .pfnConstruct = */ e1kR3Construct,
+ /* .pfnDestruct = */ e1kR3Destruct,
+ /* .pfnRelocate = */ e1kR3Relocate,
+ /* .pfnMemSetup = */ NULL,
+ /* .pfnPowerOn = */ NULL,
+ /* .pfnReset = */ e1kR3Reset,
+ /* .pfnSuspend = */ e1kR3Suspend,
+ /* .pfnResume = */ NULL,
+ /* .pfnAttach = */ e1kR3Attach,
+ /* .pfnDeatch = */ e1kR3Detach,
+ /* .pfnQueryInterface = */ NULL,
+ /* .pfnInitComplete = */ NULL,
+ /* .pfnPowerOff = */ e1kR3PowerOff,
+ /* .pfnSoftReset = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RING0)
+ /* .pfnEarlyConstruct = */ NULL,
+ /* .pfnConstruct = */ e1kRZConstruct,
+ /* .pfnDestruct = */ NULL,
+ /* .pfnFinalDestruct = */ NULL,
+ /* .pfnRequest = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RC)
+ /* .pfnConstruct = */ e1kRZConstruct,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#else
+# error "Not in IN_RING3, IN_RING0 or IN_RC!"
+#endif
+ /* .u32VersionEnd = */ PDM_DEVREG_VERSION
+};
+
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
diff --git a/src/VBox/Devices/Network/DevE1000Phy.cpp b/src/VBox/Devices/Network/DevE1000Phy.cpp
new file mode 100644
index 00000000..ade9d49a
--- /dev/null
+++ b/src/VBox/Devices/Network/DevE1000Phy.cpp
@@ -0,0 +1,640 @@
+/** $Id: DevE1000Phy.cpp $ */
+/** @file
+ * DevE1000Phy - Intel 82540EM Ethernet Controller Internal PHY Emulation.
+ *
+ * Implemented in accordance with the specification:
+ * PCI/PCI-X Family of Gigabit Ethernet Controllers Software Developer's
+ * Manual 82540EP/EM, 82541xx, 82544GC/EI, 82545GM/EM, 82546GB/EB, and
+ * 82547xx
+ *
+ * 317453-002 Revision 3.5
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_DEV_E1000
+
+/** @todo Remove me! For now I want asserts to work in release code. */
+// #ifndef RT_STRICT
+// #define RT_STRICT
+#include <iprt/assert.h>
+// #undef RT_STRICT
+// #endif
+
+#include <iprt/errcore.h>
+#include <VBox/log.h>
+#ifdef IN_RING3
+# include <VBox/vmm/pdmdev.h>
+#endif
+#include "DevE1000Phy.h"
+
+/* Little helpers ************************************************************/
+#ifdef PHY_UNIT_TEST
+# ifdef CPP_UNIT
+# include <stdio.h>
+# define PhyLog(a) printf a
+# else
+# include <iprt/test.h>
+# define PhyLogC99(...) RTTestIPrintf(RTTESTLVL_ALWAYS, __VA_ARGS__)
+# define PhyLog(a) PhyLogC99 a
+# endif
+#else /* !PHY_UNIT_TEST */
+# define PhyLog(a) Log(a)
+#endif /* !PHY_UNIT_TEST */
+
+#define REG(x) pPhy->au16Regs[x##_IDX]
+
+
+/* Internals */
+namespace Phy {
+#if defined(LOG_ENABLED) || defined(PHY_UNIT_TEST)
+ /** Retrieves state name by id */
+ static const char * getStateName(uint16_t u16State);
+#endif
+ /** Look up register index by address. */
+ static int lookupRegister(uint32_t u32Address);
+ /** Software-triggered reset. */
+ static void softReset(PPHY pPhy, PPDMDEVINS pDevIns);
+
+ /** Read callback. */
+ typedef uint16_t FNREAD(PPHY pPhy, uint32_t index, PPDMDEVINS pDevIns);
+ /** Write callback. */
+ typedef void FNWRITE(PPHY pPhy, uint32_t index, uint16_t u16Value, PPDMDEVINS pDevIns);
+
+ /** @name Generic handlers
+ * @{ */
+ static FNREAD regReadDefault;
+ static FNWRITE regWriteDefault;
+ static FNREAD regReadForbidden;
+ static FNWRITE regWriteForbidden;
+ static FNREAD regReadUnimplemented;
+ static FNWRITE regWriteUnimplemented;
+ /** @} */
+ /** @name Register-specific handlers
+ * @{ */
+ static FNWRITE regWritePCTRL;
+ static FNREAD regReadPSTATUS;
+ static FNREAD regReadGSTATUS;
+ /** @} */
+
+ /**
+ * PHY register map table.
+ *
+ * Override pfnRead and pfnWrite to implement register-specific behavior.
+ */
+ static struct RegMap_st
+ {
+ /** PHY register address. */
+ uint32_t u32Address;
+ /** Read callback. */
+ FNREAD *pfnRead;
+ /** Write callback. */
+ FNWRITE *pfnWrite;
+ /** Abbreviated name. */
+ const char *pszAbbrev;
+ /** Full name. */
+ const char *pszName;
+ } s_regMap[NUM_OF_PHY_REGS] =
+ {
+ /*ra read callback write callback abbrev full name */
+ /*-- ------------------------- -------------------------- ---------- ------------------------------*/
+ { 0, Phy::regReadDefault , Phy::regWritePCTRL , "PCTRL" , "PHY Control" },
+ { 1, Phy::regReadPSTATUS , Phy::regWriteForbidden , "PSTATUS" , "PHY Status" },
+ { 2, Phy::regReadDefault , Phy::regWriteForbidden , "PID" , "PHY Identifier" },
+ { 3, Phy::regReadDefault , Phy::regWriteForbidden , "EPID" , "Extended PHY Identifier" },
+ { 4, Phy::regReadDefault , Phy::regWriteDefault , "ANA" , "Auto-Negotiation Advertisement" },
+ { 5, Phy::regReadDefault , Phy::regWriteForbidden , "LPA" , "Link Partner Ability" },
+ { 6, Phy::regReadUnimplemented, Phy::regWriteForbidden , "ANE" , "Auto-Negotiation Expansion" },
+ { 7, Phy::regReadUnimplemented, Phy::regWriteUnimplemented, "NPT" , "Next Page Transmit" },
+ { 8, Phy::regReadUnimplemented, Phy::regWriteForbidden , "LPN" , "Link Partner Next Page" },
+ { 9, Phy::regReadDefault , Phy::regWriteUnimplemented, "GCON" , "1000BASE-T Control" },
+ { 10, Phy::regReadGSTATUS , Phy::regWriteForbidden , "GSTATUS" , "1000BASE-T Status" },
+ { 15, Phy::regReadUnimplemented, Phy::regWriteForbidden , "EPSTATUS" , "Extended PHY Status" },
+ { 16, Phy::regReadDefault , Phy::regWriteDefault , "PSCON" , "PHY Specific Control" },
+ { 17, Phy::regReadDefault , Phy::regWriteForbidden , "PSSTAT" , "PHY Specific Status" },
+ { 18, Phy::regReadUnimplemented, Phy::regWriteUnimplemented, "PINTE" , "PHY Interrupt Enable" },
+ { 19, Phy::regReadUnimplemented, Phy::regWriteForbidden , "PINTS" , "PHY Interrupt Status" },
+ { 20, Phy::regReadUnimplemented, Phy::regWriteUnimplemented, "EPSCON1" , "Extended PHY Specific Control 1" },
+ { 21, Phy::regReadUnimplemented, Phy::regWriteForbidden , "PREC" , "PHY Receive Error Counter" },
+ { 26, Phy::regReadUnimplemented, Phy::regWriteUnimplemented, "EPSCON2" , "Extended PHY Specific Control 2" },
+ { 29, Phy::regReadForbidden , Phy::regWriteUnimplemented, "R30PS" , "MDI Register 30 Page Select" },
+ { 30, Phy::regReadUnimplemented, Phy::regWriteUnimplemented, "R30AW" , "MDI Register 30 Access Window" }
+ };
+}
+
+/**
+ * Default read handler.
+ *
+ * Fetches register value from the state structure.
+ *
+ * @returns Register value
+ *
+ * @param index Register index in register array.
+ */
+static uint16_t Phy::regReadDefault(PPHY pPhy, uint32_t index, PPDMDEVINS pDevIns)
+{
+ RT_NOREF(pDevIns);
+ AssertReturn(index<Phy::NUM_OF_PHY_REGS, 0);
+ return pPhy->au16Regs[index];
+}
+
+/**
+ * Default write handler.
+ *
+ * Writes the specified register value to the state structure.
+ *
+ * @param index Register index in register array.
+ * @param value The value to store (ignored).
+ */
+static void Phy::regWriteDefault(PPHY pPhy, uint32_t index, uint16_t u16Value, PPDMDEVINS pDevIns)
+{
+ RT_NOREF(pDevIns);
+ AssertReturnVoid(index < NUM_OF_PHY_REGS);
+ pPhy->au16Regs[index] = u16Value;
+}
+
+/**
+ * Read handler for write-only registers.
+ *
+ * Merely reports reads from write-only registers.
+ *
+ * @returns Register value (always 0)
+ *
+ * @param index Register index in register array.
+ */
+static uint16_t Phy::regReadForbidden(PPHY pPhy, uint32_t index, PPDMDEVINS pDevIns)
+{
+ RT_NOREF(pPhy, index, pDevIns);
+ PhyLog(("PHY#%d At %02d read attempted from write-only '%s'\n",
+ pPhy->iInstance, s_regMap[index].u32Address, s_regMap[index].pszName));
+ return 0;
+}
+
+/**
+ * Write handler for read-only registers.
+ *
+ * Merely reports writes to read-only registers.
+ *
+ * @param index Register index in register array.
+ * @param value The value to store (ignored).
+ */
+static void Phy::regWriteForbidden(PPHY pPhy, uint32_t index, uint16_t u16Value, PPDMDEVINS pDevIns)
+{
+ RT_NOREF(pPhy, index, u16Value, pDevIns);
+ PhyLog(("PHY#%d At %02d write attempted to read-only '%s'\n",
+ pPhy->iInstance, s_regMap[index].u32Address, s_regMap[index].pszName));
+}
+
+/**
+ * Read handler for unimplemented registers.
+ *
+ * Merely reports reads from unimplemented registers.
+ *
+ * @returns Register value (always 0)
+ *
+ * @param index Register index in register array.
+ */
+static uint16_t Phy::regReadUnimplemented(PPHY pPhy, uint32_t index, PPDMDEVINS pDevIns)
+{
+ RT_NOREF(pPhy, index, pDevIns);
+ PhyLog(("PHY#%d At %02d read attempted from unimplemented '%s'\n",
+ pPhy->iInstance, s_regMap[index].u32Address, s_regMap[index].pszName));
+ return 0;
+}
+
+/**
+ * Write handler for unimplemented registers.
+ *
+ * Merely reports writes to unimplemented registers.
+ *
+ * @param index Register index in register array.
+ * @param value The value to store (ignored).
+ */
+static void Phy::regWriteUnimplemented(PPHY pPhy, uint32_t index, uint16_t u16Value, PPDMDEVINS pDevIns)
+{
+ RT_NOREF(pPhy, index, u16Value, pDevIns);
+ PhyLog(("PHY#%d At %02d write attempted to unimplemented '%s'\n",
+ pPhy->iInstance, s_regMap[index].u32Address, s_regMap[index].pszName));
+}
+
+
+/**
+ * Search PHY register table for register with matching address.
+ *
+ * @returns Index in the register table or -1 if not found.
+ *
+ * @param u32Address Register address.
+ */
+static int Phy::lookupRegister(uint32_t u32Address)
+{
+ unsigned int index;
+
+ for (index = 0; index < RT_ELEMENTS(s_regMap); index++)
+ {
+ if (s_regMap[index].u32Address == u32Address)
+ {
+ return (int)index;
+ }
+ }
+
+ return -1;
+}
+
+/**
+ * Read PHY register.
+ *
+ * @returns Value of specified PHY register.
+ *
+ * @param u32Address Register address.
+ */
+uint16_t Phy::readRegister(PPHY pPhy, uint32_t u32Address, PPDMDEVINS pDevIns)
+{
+ int index = Phy::lookupRegister(u32Address);
+ uint16_t u16 = 0;
+
+ if (index >= 0)
+ {
+ u16 = s_regMap[index].pfnRead(pPhy, (uint32_t)index, pDevIns);
+ PhyLog(("PHY#%d At %02d read %04X from %s (%s)\n",
+ pPhy->iInstance, s_regMap[index].u32Address, u16,
+ s_regMap[index].pszAbbrev, s_regMap[index].pszName));
+ }
+ else
+ {
+ PhyLog(("PHY#%d read attempted from non-existing register %08x\n",
+ pPhy->iInstance, u32Address));
+ }
+ return u16;
+}
+
+/**
+ * Write to PHY register.
+ *
+ * @param u32Address Register address.
+ * @param u16Value Value to store.
+ */
+void Phy::writeRegister(PPHY pPhy, uint32_t u32Address, uint16_t u16Value, PPDMDEVINS pDevIns)
+{
+ int index = Phy::lookupRegister(u32Address);
+
+ if (index >= 0)
+ {
+ PhyLog(("PHY#%d At %02d write %04X to %s (%s)\n",
+ pPhy->iInstance, s_regMap[index].u32Address, u16Value,
+ s_regMap[index].pszAbbrev, s_regMap[index].pszName));
+ s_regMap[index].pfnWrite(pPhy, (uint32_t)index, u16Value, pDevIns);
+ }
+ else
+ {
+ PhyLog(("PHY#%d write attempted to non-existing register %08x\n",
+ pPhy->iInstance, u32Address));
+ }
+}
+
+/**
+ * PHY constructor.
+ *
+ * Stores E1000 instance internally. Triggers PHY hard reset.
+ *
+ * @param iNICInstance Number of network controller instance this PHY is
+ * attached to.
+ * @param u16EPid Extended PHY Id.
+ */
+void Phy::init(PPHY pPhy, int iNICInstance, uint16_t u16EPid)
+{
+ pPhy->iInstance = iNICInstance;
+ /* The PHY identifier composed of bits 3 through 18 of the OUI */
+ /* (Organizationally Unique Identifier). OUI is 0x05043. */
+ REG(PID) = 0x0141;
+ /* Extended PHY identifier */
+ REG(EPID) = u16EPid;
+ hardReset(pPhy);
+}
+
+/**
+ * Hardware PHY reset.
+ *
+ * Sets all PHY registers to their initial values.
+ */
+void Phy::hardReset(PPHY pPhy)
+{
+ PhyLog(("PHY#%d Hard reset\n", pPhy->iInstance));
+ REG(PCTRL) = PCTRL_SPDSELM | PCTRL_DUPMOD | PCTRL_ANEG;
+ /*
+ * 100 and 10 FD/HD, Extended Status, MF Preamble Suppression,
+ * AUTO NEG AB, EXT CAP
+ */
+ REG(PSTATUS) = 0x7949;
+ REG(ANA) = 0x01E1;
+ /* No flow control by our link partner, all speeds */
+ REG(LPA) = 0x01E0;
+ REG(ANE) = 0x0000;
+ REG(NPT) = 0x2001;
+ REG(LPN) = 0x0000;
+ REG(GCON) = 0x1E00;
+ REG(GSTATUS) = 0x0000;
+ REG(EPSTATUS) = 0x3000;
+ REG(PSCON) = 0x0068;
+ REG(PSSTAT) = 0x0000;
+ REG(PINTE) = 0x0000;
+ REG(PINTS) = 0x0000;
+ REG(EPSCON1) = 0x0D60;
+ REG(PREC) = 0x0000;
+ REG(EPSCON2) = 0x000C;
+ REG(R30PS) = 0x0000;
+ REG(R30AW) = 0x0000;
+
+ pPhy->u16State = MDIO_IDLE;
+}
+
+/**
+ * Software PHY reset.
+ */
+static void Phy::softReset(PPHY pPhy, PPDMDEVINS pDevIns)
+{
+ PhyLog(("PHY#%d Soft reset\n", pPhy->iInstance));
+
+ REG(PCTRL) = REG(PCTRL) & (PCTRL_SPDSELM | PCTRL_DUPMOD | PCTRL_ANEG | PCTRL_SPDSELL);
+ /*
+ * 100 and 10 FD/HD, Extended Status, MF Preamble Suppression,
+ * AUTO NEG AB, EXT CAP
+ */
+ REG(PSTATUS) = 0x7949;
+ REG(PSSTAT) &= 0xe001;
+ PhyLog(("PHY#%d PSTATUS=%04x PSSTAT=%04x\n", pPhy->iInstance, REG(PSTATUS), REG(PSSTAT)));
+
+#ifndef PHY_UNIT_TEST
+ e1kPhyLinkResetCallback(pDevIns);
+#else
+ RT_NOREF(pDevIns);
+#endif
+}
+
+/**
+ * Get the current state of the link.
+ *
+ * @returns true if link is up.
+ */
+bool Phy::isLinkUp(PPHY pPhy)
+{
+ return (REG(PSSTAT) & PSSTAT_LINK) != 0;
+}
+
+/**
+ * Set the current state of the link.
+ *
+ * @remarks Link Status bit in PHY Status register is latched-low and does
+ * not change the state when the link goes up.
+ *
+ * @param fLinkIsUp New state of the link.
+ */
+void Phy::setLinkStatus(PPHY pPhy, bool fLinkIsUp)
+{
+ if (fLinkIsUp)
+ {
+ REG(PSSTAT) |= PSSTAT_LINK_ALL;
+ REG(PSTATUS) |= PSTATUS_NEGCOMP; /* PSTATUS_LNKSTAT is latched low */
+ }
+ else
+ {
+ REG(PSSTAT) &= ~PSSTAT_LINK_ALL;
+ REG(PSTATUS) &= ~(PSTATUS_LNKSTAT | PSTATUS_NEGCOMP);
+ }
+ PhyLog(("PHY#%d setLinkStatus: PSTATUS=%04x PSSTAT=%04x\n", pPhy->iInstance, REG(PSTATUS), REG(PSSTAT)));
+}
+
+#ifdef IN_RING3
+
+/**
+ * Save PHY state.
+ *
+ * @remarks Since PHY is aggregated into E1K it does not currently supports
+ * versioning of its own.
+ *
+ * @returns VBox status code.
+ * @param pHlp Device helper table.
+ * @param pSSM The handle to save the state to.
+ * @param pPhy The pointer to this instance.
+ */
+int Phy::saveState(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PPHY pPhy)
+{
+ pHlp->pfnSSMPutMem(pSSM, pPhy->au16Regs, sizeof(pPhy->au16Regs));
+ return VINF_SUCCESS;
+}
+
+/**
+ * Restore previously saved PHY state.
+ *
+ * @remarks Since PHY is aggregated into E1K it does not currently supports
+ * versioning of its own.
+ *
+ * @returns VBox status code.
+ * @param pHlp Device helper table.
+ * @param pSSM The handle to save the state to.
+ * @param pPhy The pointer to this instance.
+ */
+int Phy::loadState(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM, PPHY pPhy)
+{
+ return pHlp->pfnSSMGetMem(pSSM, pPhy->au16Regs, sizeof(pPhy->au16Regs));
+}
+
+#endif /* IN_RING3 */
+
+/* Register-specific handlers ************************************************/
+
+/**
+ * Write handler for PHY Control register.
+ *
+ * Handles reset.
+ *
+ * @param index Register index in register array.
+ * @param value The value to store (ignored).
+ */
+static void Phy::regWritePCTRL(PPHY pPhy, uint32_t index, uint16_t u16Value, PPDMDEVINS pDevIns)
+{
+ if (u16Value & PCTRL_RESET)
+ softReset(pPhy, pDevIns);
+ else
+ regWriteDefault(pPhy, index, u16Value, pDevIns);
+}
+
+/**
+ * Read handler for PHY Status register.
+ *
+ * Handles Latched-Low Link Status bit.
+ *
+ * @returns Register value
+ *
+ * @param index Register index in register array.
+ */
+static uint16_t Phy::regReadPSTATUS(PPHY pPhy, uint32_t index, PPDMDEVINS pDevIns)
+{
+ RT_NOREF(pPhy, index, pDevIns);
+
+ /* Read latched value */
+ uint16_t u16 = REG(PSTATUS);
+ if (REG(PSSTAT) & PSSTAT_LINK)
+ REG(PSTATUS) |= PSTATUS_LNKSTAT;
+ else
+ REG(PSTATUS) &= ~PSTATUS_LNKSTAT;
+ return u16;
+}
+
+/**
+ * Read handler for 1000BASE-T Status register.
+ *
+ * @returns Register value
+ *
+ * @param index Register index in register array.
+ */
+static uint16_t Phy::regReadGSTATUS(PPHY pPhy, uint32_t index, PPDMDEVINS pDevIns)
+{
+ RT_NOREF(pPhy, index, pDevIns);
+
+ /*
+ * - Link partner is capable of 1000BASE-T half duplex
+ * - Link partner is capable of 1000BASE-T full duplex
+ * - Remote receiver OK
+ * - Local receiver OK
+ * - Local PHY config resolved to SLAVE
+ */
+ return 0x3C00;
+}
+
+#if defined(LOG_ENABLED) || defined(PHY_UNIT_TEST)
+static const char * Phy::getStateName(uint16_t u16State)
+{
+ static const char *pcszState[] =
+ {
+ "MDIO_IDLE",
+ "MDIO_ST",
+ "MDIO_OP_ADR",
+ "MDIO_TA_RD",
+ "MDIO_TA_WR",
+ "MDIO_READ",
+ "MDIO_WRITE"
+ };
+
+ return (u16State < RT_ELEMENTS(pcszState)) ? pcszState[u16State] : "<invalid>";
+}
+#endif
+
+bool Phy::readMDIO(PPHY pPhy)
+{
+ bool fPin = false;
+
+ switch (pPhy->u16State)
+ {
+ case MDIO_TA_RD:
+ Assert(pPhy->u16Cnt == 1);
+ fPin = false;
+ pPhy->u16State = MDIO_READ;
+ pPhy->u16Cnt = 16;
+ break;
+ case MDIO_READ:
+ /* Bits are shifted out in MSB to LSB order */
+ fPin = (pPhy->u16Acc & 0x8000) != 0;
+ pPhy->u16Acc <<= 1;
+ if (--pPhy->u16Cnt == 0)
+ pPhy->u16State = MDIO_IDLE;
+ break;
+ default:
+ PhyLog(("PHY#%d WARNING! MDIO pin read in %s state\n", pPhy->iInstance, Phy::getStateName(pPhy->u16State)));
+ pPhy->u16State = MDIO_IDLE;
+ }
+ return fPin;
+}
+
+/** Set the value of MDIO pin. */
+void Phy::writeMDIO(PPHY pPhy, bool fPin, PPDMDEVINS pDevIns)
+{
+ switch (pPhy->u16State)
+ {
+ case MDIO_IDLE:
+ if (!fPin)
+ pPhy->u16State = MDIO_ST;
+ break;
+ case MDIO_ST:
+ if (fPin)
+ {
+ pPhy->u16State = MDIO_OP_ADR;
+ pPhy->u16Cnt = 12; /* OP + PHYADR + REGADR */
+ pPhy->u16Acc = 0;
+ }
+ break;
+ case MDIO_OP_ADR:
+ Assert(pPhy->u16Cnt);
+ /* Shift in 'u16Cnt' bits into accumulator */
+ pPhy->u16Acc <<= 1;
+ if (fPin)
+ pPhy->u16Acc |= 1;
+ if (--pPhy->u16Cnt == 0)
+ {
+ /* Got OP(2) + PHYADR(5) + REGADR(5) */
+ /* Note: A single PHY is supported, ignore PHYADR */
+ switch (pPhy->u16Acc >> 10)
+ {
+ case MDIO_READ_OP:
+ pPhy->u16Acc = readRegister(pPhy, pPhy->u16Acc & 0x1F, pDevIns);
+ pPhy->u16State = MDIO_TA_RD;
+ pPhy->u16Cnt = 1;
+ break;
+ case MDIO_WRITE_OP:
+ pPhy->u16RegAdr = pPhy->u16Acc & 0x1F;
+ pPhy->u16State = MDIO_TA_WR;
+ pPhy->u16Cnt = 2;
+ break;
+ default:
+ PhyLog(("PHY#%d ERROR! Invalid MDIO op: %d\n", pPhy->iInstance, pPhy->u16Acc >> 10));
+ pPhy->u16State = MDIO_IDLE;
+ break;
+ }
+ }
+ break;
+ case MDIO_TA_WR:
+ Assert(pPhy->u16Cnt <= 2);
+ Assert(pPhy->u16Cnt > 0);
+ if (--pPhy->u16Cnt == 0)
+ {
+ pPhy->u16State = MDIO_WRITE;
+ pPhy->u16Cnt = 16;
+ }
+ break;
+ case MDIO_WRITE:
+ Assert(pPhy->u16Cnt);
+ pPhy->u16Acc <<= 1;
+ if (fPin)
+ pPhy->u16Acc |= 1;
+ if (--pPhy->u16Cnt == 0)
+ {
+ writeRegister(pPhy, pPhy->u16RegAdr, pPhy->u16Acc, pDevIns);
+ pPhy->u16State = MDIO_IDLE;
+ }
+ break;
+ default:
+ PhyLog(("PHY#%d ERROR! MDIO pin write in %s state\n", pPhy->iInstance, Phy::getStateName(pPhy->u16State)));
+ pPhy->u16State = MDIO_IDLE;
+ break;
+ }
+}
+
diff --git a/src/VBox/Devices/Network/DevE1000Phy.h b/src/VBox/Devices/Network/DevE1000Phy.h
new file mode 100644
index 00000000..17925c18
--- /dev/null
+++ b/src/VBox/Devices/Network/DevE1000Phy.h
@@ -0,0 +1,153 @@
+/** $Id: DevE1000Phy.h $ */
+/** @file
+ * DevE1000Phy - Intel 82540EM Ethernet Controller Internal PHY Emulation, Header.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Network_DevE1000Phy_h
+#define VBOX_INCLUDED_SRC_Network_DevE1000Phy_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/types.h>
+
+#define PHY_EPID_M881000 0xC50
+#define PHY_EPID_M881011 0xC24
+
+#define PCTRL_SPDSELM 0x0040
+#define PCTRL_DUPMOD 0x0100
+#define PCTRL_ANEG 0x1000
+#define PCTRL_SPDSELL 0x2000
+#define PCTRL_RESET 0x8000
+
+#define PSTATUS_LNKSTAT 0x0004
+#define PSTATUS_NEGCOMP 0x0020
+
+/*
+ * Speed: 1000 Mb/s
+ * Duplex: full
+ * Page received
+ * Resolved
+ * Link up
+ * Receive Pause Enable
+ */
+#define PSSTAT_LINK_ALL 0xBC08
+#define PSSTAT_LINK 0x0400
+
+namespace Phy
+{
+ /**
+ * Indices of memory-mapped registers in register table
+ */
+ enum enmRegIdx
+ {
+ PCTRL_IDX,
+ PSTATUS_IDX,
+ PID_IDX,
+ EPID_IDX,
+ ANA_IDX,
+ LPA_IDX,
+ ANE_IDX,
+ NPT_IDX,
+ LPN_IDX,
+ GCON_IDX,
+ GSTATUS_IDX,
+ EPSTATUS_IDX,
+ PSCON_IDX,
+ PSSTAT_IDX,
+ PINTE_IDX,
+ PINTS_IDX,
+ EPSCON1_IDX,
+ PREC_IDX,
+ EPSCON2_IDX,
+ R30PS_IDX,
+ R30AW_IDX,
+ NUM_OF_PHY_REGS
+ };
+ /**
+ * Emulation state of PHY.
+ */
+ struct Phy_st
+ {
+ /** Network controller instance this PHY is attached to. */
+ int iInstance;
+ /** Register storage. */
+ uint16_t au16Regs[NUM_OF_PHY_REGS];
+ /** Current state of serial MDIO interface. */
+ uint16_t u16State;
+ /** Current state of serial MDIO interface. */
+ uint16_t u16Acc;
+ /** Number of bits remaining to be shifted into/out of accumulator. */
+ uint16_t u16Cnt;
+ /** PHY register offset selected for MDIO operation. */
+ uint16_t u16RegAdr;
+ };
+}
+
+#define MDIO_IDLE 0
+#define MDIO_ST 1
+#define MDIO_OP_ADR 2
+#define MDIO_TA_RD 3
+#define MDIO_TA_WR 4
+#define MDIO_READ 5
+#define MDIO_WRITE 6
+
+#define MDIO_READ_OP 2
+#define MDIO_WRITE_OP 1
+
+/* External callback declaration */
+void e1kPhyLinkResetCallback(PPDMDEVINS pDevIns);
+
+
+typedef struct Phy::Phy_st PHY;
+typedef PHY *PPHY;
+
+/* Interface *****************************************************************/
+namespace Phy
+{
+ /** Initialize PHY. */
+ void init(PPHY pPhy, int iNICInstance, uint16_t u16EPid);
+ /** Read PHY register at specified address. */
+ uint16_t readRegister(PPHY pPhy, uint32_t u32Address, PPDMDEVINS pDevIns);
+ /** Write to PHY register at specified address. */
+ void writeRegister(PPHY pPhy, uint32_t u32Address, uint16_t u16Value, PPDMDEVINS pDevIns);
+ /** Read the value on MDIO pin. */
+ bool readMDIO(PPHY pPhy);
+ /** Set the value of MDIO pin. */
+ void writeMDIO(PPHY pPhy, bool fPin, PPDMDEVINS pDevIns);
+ /** Hardware reset. */
+ void hardReset(PPHY pPhy);
+ /** Query link status. */
+ bool isLinkUp(PPHY pPhy);
+ /** Set link status. */
+ void setLinkStatus(PPHY pPhy, bool fLinkIsUp);
+ /** Save PHY state. */
+ int saveState(struct PDMDEVHLPR3 const *pHlp, PSSMHANDLE pSSM, PPHY pPhy);
+ /** Restore previously saved PHY state. */
+ int loadState(struct PDMDEVHLPR3 const *pHlp, PSSMHANDLE pSSM, PPHY pPhy);
+}
+
+#endif /* !VBOX_INCLUDED_SRC_Network_DevE1000Phy_h */
+
diff --git a/src/VBox/Devices/Network/DevEEPROM.cpp b/src/VBox/Devices/Network/DevEEPROM.cpp
new file mode 100644
index 00000000..9353df89
--- /dev/null
+++ b/src/VBox/Devices/Network/DevEEPROM.cpp
@@ -0,0 +1,302 @@
+/* $Id: DevEEPROM.cpp $ */
+/** @file
+ * DevEEPROM - Microwire-compatible 64x16-bit 93C46 EEPROM Emulation.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#define LOG_GROUP LOG_GROUP_DEV_E1000 /// @todo Add a EEPROM logging group.
+#include <VBox/log.h>
+#include <VBox/vmm/pdmdev.h>
+#include <iprt/string.h>
+#include "DevEEPROM.h"
+
+#define E1kLog(a) Log(a)
+
+/**
+ * Initialize EEPROM device.
+ *
+ * @param pu16Initial Initial EEPROM content (optional). The size of initial
+ * content must be sizeof(uint16_t)*EEPROM93C46::SIZE
+ * bytes.
+ */
+void EEPROM93C46::init(const uint16_t *pu16Initial)
+{
+ if ( pu16Initial )
+ memcpy(this->m_au16Data, pu16Initial, sizeof(this->m_au16Data));
+ else
+ memset(this->m_au16Data, 0, sizeof(this->m_au16Data));
+ m_fWriteEnabled = false;
+ m_u32InternalWires = 0;
+ m_eState = STANDBY;
+}
+
+/**
+ * Writes one word to specified location if write is enabled.
+ *
+ * @param u32Addr Address to write at
+ * @param u16Value Value to store
+ */
+void EEPROM93C46::storeWord(uint32_t u32Addr, uint16_t u16Value)
+{
+ if (m_fWriteEnabled) {
+ E1kLog(("EEPROM: Stored word %04x at %08x\n", u16Value, u32Addr));
+ m_au16Data[u32Addr] = u16Value;
+ }
+ m_u16Mask = DATA_MSB;
+}
+
+/**
+ * Reads one word at specified location.
+ *
+ * @returns True if read was successful.
+ *
+ * @param u32Addr Address to read from
+ * @param pu16Value Placeholder to store the value
+ */
+bool EEPROM93C46::readWord(uint32_t u32Addr, uint16_t *pu16Value)
+{
+ if (u32Addr < SIZE)
+ {
+ *pu16Value = m_au16Data[u32Addr];
+ return true;
+ }
+
+ return false;
+}
+
+/**
+ * Fetch next word pointer by m_u16Addr.
+ *
+ * m_u16Addr is advanced and mask is reset to support sequential reads.
+ *
+ * @returns New state
+ */
+EEPROM93C46::State EEPROM93C46::opRead()
+{
+ m_u16Word = m_au16Data[m_u16Addr];
+ E1kLog(("EEPROM: Reading word %04x at %08x\n", m_u16Word, m_u16Addr));
+ m_u16Addr = (m_u16Addr + 1) & ADDR_MASK;
+ m_u16Mask = DATA_MSB;
+ return WRITING_DO;
+}
+
+/**
+ * Write the value of m_u16Word to the location specified by m_u16Addr.
+ *
+ * @returns New state
+ *
+ * @remarks Need to wait for CS lower/raise to show busy/ready indication.
+ */
+EEPROM93C46::State EEPROM93C46::opWrite()
+{
+ storeWord(m_u16Addr, m_u16Word);
+ return WAITING_CS_FALL;
+}
+
+/**
+ * Overwrite the entire contents of EEPROM with the value of m_u16Word.
+ *
+ * @returns New state
+ *
+ * @remarks Need to wait for CS lower/raise to show busy/ready indication.
+ */
+EEPROM93C46::State EEPROM93C46::opWriteAll()
+{
+ for (unsigned i = 0; i < SIZE; i++)
+ storeWord(i, m_u16Word);
+ return WAITING_CS_FALL;
+}
+
+/**
+ * Decode opcode and address from 'opAddr' member.
+ *
+ * Decodes operation and executes it immediately if possible; otherwise, stores
+ * the decoded operation and address.
+ *
+ * @returns New state
+ */
+EEPROM93C46::State EEPROM93C46::opDecode()
+{
+ switch (m_u16Word>>6) {
+ case 3: /* ERASE */
+ storeWord(m_u16Word & ADDR_MASK, 0xFFFF);
+ return WAITING_CS_FALL;
+ case 2: /* READ */
+ m_eOp = OP_READ;
+ m_u16Addr = m_u16Word & ADDR_MASK;
+ return opRead(); /* Load first word */
+ case 1: /* WRITE */
+ m_eOp = OP_WRITE;
+ m_u16Addr = m_u16Word & ADDR_MASK;
+ m_u16Word = 0;
+ m_u16Mask = DATA_MSB;
+ return READING_DI;
+ case 0:
+ switch (m_u16Word>>4) {
+ case 0: /* ERASE/WRITE DISABLE */
+ m_fWriteEnabled = false;
+ return STANDBY;
+ case 1: /* WRITE ALL */
+ m_eOp = OP_WRITE_ALL;
+ m_u16Word = 0;
+ m_u16Mask = DATA_MSB;
+ return READING_DI;
+ case 2: /* ERASE ALL */
+ /* Re-use opWriteAll */
+ m_u16Word = 0xFFFF;
+ return opWriteAll();
+ case 3: /* ERASE/WRITE ENABLE */
+ m_fWriteEnabled = true;
+ return STANDBY;
+ }
+ }
+ return m_eState;
+}
+
+/**
+ * Set bits in EEPROM 4-wire interface.
+ *
+ * @param u32Wires Values of DI, CS, SK.
+ * @remarks The value of DO bit in 'u32Wires' is ignored.
+ */
+void EEPROM93C46::write(uint32_t u32Wires)
+{
+ if (u32Wires & WIRES_CS) {
+ if (!(m_u32InternalWires & WIRES_SK) && (u32Wires & WIRES_SK)) {
+ /* Positive edge of clock */
+ if (m_eState == STANDBY) {
+ if (u32Wires & WIRES_DI) {
+ m_eState = READING_DI;
+ m_eOp = OP_DECODE;
+ m_u16Mask = OPADDR_MSB;
+ m_u16Word = 0;
+ }
+ }
+ else {
+ if (m_eState == READING_DI) {
+ if (u32Wires & WIRES_DI) {
+ m_u16Word |= m_u16Mask;
+ }
+ }
+ else if (m_eState == WRITING_DO) {
+ m_u32InternalWires &= ~WIRES_DO;
+ if (m_u16Word & m_u16Mask) {
+ m_u32InternalWires |= WIRES_DO;
+ }
+ }
+ else return;
+ /* Next bit */
+ m_u16Mask >>= 1;
+ if (m_u16Mask == 0)
+ {
+ switch (this->m_eOp)
+ {
+ case OP_READ:
+ m_eState = opRead();
+ break;
+ case OP_WRITE:
+ m_eState = opWrite();
+ break;
+ case OP_WRITE_ALL:
+ m_eState = opWriteAll();
+ break;
+ case OP_DECODE:
+ m_eState = opDecode();
+ break;
+ default:
+ ;
+ }
+ }
+ }
+ }
+ else if (m_eState == WAITING_CS_RISE) {
+ m_u32InternalWires |= WIRES_DO; /* ready */
+ m_eState = STANDBY;
+ }
+ }
+ else {
+ switch(m_eState) {
+ case WAITING_CS_FALL:
+ m_eState = WAITING_CS_RISE;
+ m_u32InternalWires &= ~WIRES_DO; /* busy */
+ break;
+ case WAITING_CS_RISE:
+ break;
+ case READING_DI:
+ m_u32InternalWires &= ~WIRES_DO; /* Clear ready/busy status from DO. */
+ RT_FALL_THRU();
+ default:
+ m_eState = STANDBY;
+ break;
+ }
+ }
+ m_u32InternalWires &= WIRES_DO;
+ m_u32InternalWires |= u32Wires & ~WIRES_DO; /* Do not overwrite DO */
+}
+
+/**
+ * Read bits in EEPROM 4-wire interface.
+ *
+ * @returns Current values of DO, DI, CS, SK.
+ *
+ * @remarks Only DO is controlled by EEPROM, other bits are returned as they
+ * were written by 'write'.
+ */
+uint32_t EEPROM93C46::read()
+{
+ return m_u32InternalWires;
+}
+
+void EEPROM93C46::save(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM)
+{
+ pHlp->pfnSSMPutU8( pSSM, EEPROM93C46_SAVEDSTATE_VERSION);
+ Assert((uint32_t)m_eState < UINT32_C(256));
+ pHlp->pfnSSMPutU8( pSSM, (uint8_t)m_eState);
+ Assert((uint32_t)m_eOp < UINT32_C(256));
+ pHlp->pfnSSMPutU8( pSSM, (uint8_t)m_eOp);
+ pHlp->pfnSSMPutBool(pSSM, m_fWriteEnabled);
+ pHlp->pfnSSMPutU32( pSSM, m_u32InternalWires);
+ pHlp->pfnSSMPutU16( pSSM, m_u16Word);
+ pHlp->pfnSSMPutU16( pSSM, m_u16Mask);
+ pHlp->pfnSSMPutU16( pSSM, m_u16Addr);
+ pHlp->pfnSSMPutMem( pSSM, m_au16Data, sizeof(m_au16Data));
+}
+
+int EEPROM93C46::load(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM)
+{
+ uint8_t uVersion;
+ int rc = pHlp->pfnSSMGetU8(pSSM, &uVersion);
+ AssertRCReturn(rc, rc);
+ if (uVersion != EEPROM93C46_SAVEDSTATE_VERSION)
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+
+ PDMDEVHLP_SSM_GET_ENUM8_RET(pHlp, pSSM, m_eState, EEPROM93C46::State);
+ PDMDEVHLP_SSM_GET_ENUM8_RET(pHlp, pSSM, m_eOp, EEPROM93C46::OP);
+ pHlp->pfnSSMGetBool(pSSM, &m_fWriteEnabled);
+ pHlp->pfnSSMGetU32( pSSM, &m_u32InternalWires);
+ pHlp->pfnSSMGetU16( pSSM, &m_u16Word);
+ pHlp->pfnSSMGetU16( pSSM, &m_u16Mask);
+ pHlp->pfnSSMGetU16( pSSM, &m_u16Addr);
+ return pHlp->pfnSSMGetMem( pSSM, m_au16Data, sizeof(m_au16Data));
+}
diff --git a/src/VBox/Devices/Network/DevEEPROM.h b/src/VBox/Devices/Network/DevEEPROM.h
new file mode 100644
index 00000000..943dc70d
--- /dev/null
+++ b/src/VBox/Devices/Network/DevEEPROM.h
@@ -0,0 +1,151 @@
+/* $Id: DevEEPROM.h $ */
+/** @file
+ * DevEEPROM - Microwire-compatible 64x16-bit 93C46 EEPROM Emulation, Header.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Network_DevEEPROM_h
+#define VBOX_INCLUDED_SRC_Network_DevEEPROM_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/types.h>
+
+/** The current Saved state version. */
+#define EEPROM93C46_SAVEDSTATE_VERSION 1
+
+/**
+ * 93C46-compatible EEPROM device emulation.
+ *
+ * @remarks This class is intended to be used in device
+ * emulation which imposes some restrictions if the
+ * device supports GC execution. This is why it is a
+ * plain-old-data structure.
+ */
+struct EEPROM93C46
+{
+ /** General definitions */
+ enum {
+ /** Size of EEPROM in words */
+ SIZE = 64,
+ /** Number of bits per word */
+ WORD_SIZE = 16,
+ /** Number of address bits */
+ ADDR_SIZE = 6,
+ /** Number of bits in opcode */
+ OPCODE_SIZE = 2,
+ /** The most significant bit mask in data word */
+ DATA_MSB = 1<<(WORD_SIZE-1),
+ /** Address mask */
+ ADDR_MASK = (1<<ADDR_SIZE)-1,
+ /** The most significant bit mask in op+addr bit sequence */
+ OPADDR_MSB = 1<<(OPCODE_SIZE+ADDR_SIZE-1)
+ };
+
+ enum OP {
+ OP_READ,
+ OP_WRITE,
+ OP_WRITE_ALL,
+ OP_DECODE,
+ OP_32BIT_HACK = 0x7fffffff
+ };
+
+ /**
+ * Names of signal wires
+ */
+ enum Wires {
+ WIRES_SK=0x1, ///< Clock
+ WIRES_CS=0x2, ///< Chip Select
+ WIRES_DI=0x4, ///< Data In
+ WIRES_DO=0x8 ///< Data Out
+ };
+
+
+ /** @todo save and load methods */
+ void save(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM);
+ int load(PCPDMDEVHLPR3 pHlp, PSSMHANDLE pSSM);
+
+ /** Actual content of EEPROM */
+ uint16_t m_au16Data[SIZE];
+
+ /** current state.
+ *
+ * EEPROM operates as a simple state machine. Events are primarily
+ * triggered at positive edge of clock signal (SK). Refer to the
+ * timing diagrams of 93C46 to get better understanding.
+ */
+ enum State {
+ /** Initial state. Waiting for start condition (CS, SK, DI high). */
+ STANDBY,
+ /** Reading data in, shifting in the bits into 'word'. */
+ READING_DI,
+ /** Writing data out, shifting out the bits from 'word'. */
+ WRITING_DO,
+ /** Waiting for CS=0 to indicate we are busy (DO=0). */
+ WAITING_CS_FALL,
+ /** Waiting for CS=1 to indicate we are ready (DO=1). */
+ WAITING_CS_RISE,
+ /** Make this enum 4-byte */
+ STATE_MAKE_32BIT_HACK = 0x7fffffff
+ } m_eState;
+ /** setting writeEnable to false prevents write and erase operations */
+ bool m_fWriteEnabled;
+ uint8_t Alignment1;
+ /** intermediate storage */
+ uint16_t m_u16Word;
+ /** currently processed bit in 'word' */
+ uint16_t m_u16Mask;
+ /** decoded address */
+ uint16_t m_u16Addr;
+ /** Data Out, Data In, Chip Select, Clock */
+ uint32_t m_u32InternalWires;
+
+ /** Current opcode decoder. When no operation has been decoded yet
+ * it is set to OP_DECODE.
+ */
+ OP m_eOp;
+#if HC_ARCH_BITS == 64
+ uint32_t Alignment2;
+#endif
+
+#ifdef IN_RING3
+ uint32_t read();
+ void write(uint32_t u32Wires);
+ bool readWord(uint32_t u32Addr, uint16_t *pu16Value);
+
+ void init(const uint16_t *pu16Initial = 0);
+
+ // Operation handlers
+ State opDecode();
+ State opRead();
+ State opWrite();
+ State opWriteAll();
+
+ /** Helper method to implement write protection */
+ void storeWord(uint32_t u32Addr, uint16_t u16Value);
+#endif /* IN_RING3 */
+};
+
+#endif /* !VBOX_INCLUDED_SRC_Network_DevEEPROM_h */
diff --git a/src/VBox/Devices/Network/DevINIP.cpp b/src/VBox/Devices/Network/DevINIP.cpp
new file mode 100644
index 00000000..462fd829
--- /dev/null
+++ b/src/VBox/Devices/Network/DevINIP.cpp
@@ -0,0 +1,773 @@
+/* $Id: DevINIP.cpp $ */
+/** @file
+ * DevINIP - Internal Network IP stack device/service.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_INIP
+#include <iprt/cdefs.h> /* include early to allow RT_C_DECLS_BEGIN hack */
+#include <iprt/mem.h> /* include anything of ours that the lwip headers use. */
+#include <iprt/semaphore.h>
+#include <iprt/thread.h>
+#include <iprt/alloca.h>
+/* All lwip header files are not C++ safe. So hack around this. */
+RT_C_DECLS_BEGIN
+#include "lwip/sys.h"
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/api.h"
+#include "lwip/tcp_impl.h"
+# if LWIP_IPV6
+# include "ipv6/lwip/ethip6.h"
+# endif
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/tcpip.h"
+#include "lwip/sockets.h"
+#include "netif/etharp.h"
+RT_C_DECLS_END
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/pdmnetifs.h>
+#include <VBox/vmm/tm.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+
+#include "VBoxDD.h"
+#include "VBoxLwipCore.h"
+
+
+/*********************************************************************************************************************************
+* Macros and Defines *
+*********************************************************************************************************************************/
+/** Maximum frame size this device can handle. */
+#define DEVINIP_MAX_FRAME 1514
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Internal Network IP stack device instance data.
+ *
+ * @implements PDMIBASE
+ * @implements PDMINETWORKDOWN
+ */
+typedef struct DEVINTNETIP
+{
+ /** The base interface for LUN\#0. */
+ PDMIBASE IBase;
+ /** The network port this device provides (LUN\#0). */
+ PDMINETWORKDOWN INetworkDown;
+ /** The network configuration port this device provides (LUN\#0). */
+ PDMINETWORKCONFIG INetworkConfig;
+ /** The base interface of the network driver below us. */
+ PPDMIBASE pDrvBase;
+ /** The connector of the network driver below us. */
+ PPDMINETWORKUP pDrv;
+ /** Pointer to the device instance. */
+ PPDMDEVINSR3 pDevIns;
+ /** MAC address. */
+ RTMAC MAC;
+ /** Static IP address of the interface. */
+ char *pszIP;
+ /** Netmask of the interface. */
+ char *pszNetmask;
+ /** Gateway for the interface. */
+ char *pszGateway;
+ /** lwIP network interface description. */
+ struct netif IntNetIF;
+ /** lwIP ARP timer. */
+ PTMTIMERR3 ARPTimer;
+ /** lwIP TCP fast timer. */
+ PTMTIMERR3 TCPFastTimer;
+ /** lwIP TCP slow timer. */
+ PTMTIMERR3 TCPSlowTimer;
+ /** lwIP semaphore to coordinate TCPIP init/terminate. */
+ sys_sem_t LWIPTcpInitSem;
+ /** hack: get linking right. remove this eventually, once the device
+ * provides a proper interface to all IP stack functions. */
+ const void *pLinkHack;
+ /** Flag whether the link is up. */
+ bool fLnkUp;
+ /**
+ * In callback we're getting status of interface adding operation (TCPIP thread),
+ * but we need inform constructing routine whether it was success or not(EMT thread).
+ */
+ int rcInitialization;
+} DEVINTNETIP;
+/** Pointer to the instance data for an Internal Network IP stack. */
+typedef DEVINTNETIP *PDEVINTNETIP;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/**
+ * Pointer to the (only) instance data in this device.
+ */
+static PDEVINTNETIP g_pDevINIPData = NULL;
+
+/*
+ * really ugly hack to avoid linking problems on unix style platforms
+ * using .a libraries for now.
+ */
+static const struct CLANG11WEIRDNESS { PFNRT pfn; } g_pDevINILinkHack[] =
+{
+ { (PFNRT)lwip_socket },
+ { (PFNRT)lwip_close },
+ { (PFNRT)lwip_setsockopt },
+ { (PFNRT)lwip_recv },
+ { (PFNRT)lwip_send },
+ { (PFNRT)lwip_select },
+};
+
+
+#if 0 /* unused */
+/**
+ * Output a TCP/IP packet on the interface. Uses the generic lwIP ARP
+ * code to resolve the address and call the link-level packet function.
+ *
+ * @returns lwIP error code
+ * @param netif Interface on which to send IP packet.
+ * @param p Packet data.
+ * @param ipaddr Destination IP address.
+ */
+static err_t devINIPOutput(struct netif *netif, struct pbuf *p, struct ip_addr *ipaddr)
+{
+ err_t lrc;
+ LogFlow(("%s: netif=%p p=%p ipaddr=%#04x\n", __FUNCTION__, netif, p,
+ ipaddr->addr));
+
+ lrc = lwip_etharp_output(netif, p, ipaddr);
+
+ LogFlow(("%s: return %d\n", __FUNCTION__, lrc));
+ return lrc;
+}
+#endif
+
+/**
+ * Output a raw packet on the interface.
+ *
+ * @returns lwIP error code
+ * @param netif Interface on which to send frame.
+ * @param p Frame data.
+ */
+static err_t devINIPOutputRaw(struct netif *netif, struct pbuf *p)
+{
+ NOREF(netif);
+ int rc = VINF_SUCCESS;
+
+ LogFlow(("%s: netif=%p p=%p\n", __FUNCTION__, netif, p));
+ Assert(g_pDevINIPData);
+ Assert(g_pDevINIPData->pDrv);
+
+ /* Silently ignore packets being sent while lwIP isn't set up. */
+ if (g_pDevINIPData)
+ {
+ PPDMSCATTERGATHER pSgBuf;
+
+ rc = g_pDevINIPData->pDrv->pfnBeginXmit(g_pDevINIPData->pDrv, true /* fOnWorkerThread */);
+ if (RT_FAILURE(rc))
+ return ERR_IF;
+
+ rc = g_pDevINIPData->pDrv->pfnAllocBuf(g_pDevINIPData->pDrv, DEVINIP_MAX_FRAME, NULL /*pGso*/, &pSgBuf);
+ if (RT_SUCCESS(rc))
+ {
+#if ETH_PAD_SIZE
+ lwip_pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
+#endif
+
+ uint8_t *pbBuf = pSgBuf ? (uint8_t *)pSgBuf->aSegs[0].pvSeg : NULL;
+ size_t cbBuf = 0;
+ for (struct pbuf *q = p; q != NULL; q = q->next)
+ {
+ if (cbBuf + q->len <= DEVINIP_MAX_FRAME)
+ {
+ if (RT_LIKELY(pbBuf))
+ {
+ memcpy(pbBuf, q->payload, q->len);
+ pbBuf += q->len;
+ }
+ cbBuf += q->len;
+ }
+ else
+ {
+ LogRel(("INIP: exceeded frame size\n"));
+ break;
+ }
+ }
+ if (cbBuf)
+ {
+ pSgBuf->cbUsed = cbBuf;
+ rc = g_pDevINIPData->pDrv->pfnSendBuf(g_pDevINIPData->pDrv, pSgBuf, true /* fOnWorkerThread */);
+ }
+ else
+ rc = g_pDevINIPData->pDrv->pfnFreeBuf(g_pDevINIPData->pDrv, pSgBuf);
+
+#if ETH_PAD_SIZE
+ lwip_pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
+#endif
+ }
+
+ g_pDevINIPData->pDrv->pfnEndXmit(g_pDevINIPData->pDrv);
+ }
+
+ err_t lrc = ERR_OK;
+ if (RT_FAILURE(rc))
+ lrc = ERR_IF;
+ LogFlow(("%s: return %d (vbox: %Rrc)\n", __FUNCTION__, rc, lrc));
+ return lrc;
+}
+
+/**
+ * Implements the ethernet interface backend initialization for lwIP.
+ *
+ * @returns lwIP error code
+ * @param netif Interface to configure.
+ */
+static err_t devINIPInterface(struct netif *netif) RT_NOTHROW_DEF
+{
+ LogFlow(("%s: netif=%p\n", __FUNCTION__, netif));
+ Assert(g_pDevINIPData != NULL);
+ netif->state = g_pDevINIPData;
+ netif->hwaddr_len = sizeof(g_pDevINIPData->MAC);
+ memcpy(netif->hwaddr, &g_pDevINIPData->MAC, sizeof(g_pDevINIPData->MAC));
+ netif->mtu = DEVINIP_MAX_FRAME;
+ netif->flags = NETIF_FLAG_BROADCAST;
+ netif->flags |= NETIF_FLAG_ETHARP;
+ netif->flags |= NETIF_FLAG_ETHERNET;
+
+#if LWIP_IPV6
+ netif_create_ip6_linklocal_address(netif, 0);
+ netif_ip6_addr_set_state(netif, 0, IP6_ADDR_VALID);
+ netif->output_ip6 = ethip6_output;
+ netif->ip6_autoconfig_enabled=1;
+ LogFunc(("netif: ipv6:%RTnaipv6\n", &netif->ip6_addr[0].addr[0]));
+#endif
+
+ netif->output = lwip_etharp_output;
+ netif->linkoutput = devINIPOutputRaw;
+
+ LogFlow(("%s: success\n", __FUNCTION__));
+ return ERR_OK;
+}
+
+/**
+ * Parses CFGM parameters related to network connection
+ */
+static DECLCALLBACK(int) devINIPNetworkConfiguration(PPDMDEVINS pDevIns, PDEVINTNETIP pThis, PCFGMNODE pCfg)
+{
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ int rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "IP", &pThis->pszIP);
+ if (RT_FAILURE(rc))
+ /** @todo perhaps we should panic if IPv4 address isn't specify, with assumtion that
+ * ISCSI target specified in IPv6 form. */
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"IP\" value"));
+
+ rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "Netmask", &pThis->pszNetmask);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"Netmask\" value"));
+
+ rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "Gateway", &pThis->pszGateway);
+ if ( RT_FAILURE(rc)
+ && rc != VERR_CFGM_VALUE_NOT_FOUND)
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"Gateway\" value"));
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Wait until data can be received.
+ *
+ * @returns VBox status code. VINF_SUCCESS means there is at least one receive descriptor available.
+ * @param pInterface PDM network port interface pointer.
+ * @param cMillies Number of milliseconds to wait. 0 means return immediately.
+ */
+static DECLCALLBACK(int) devINIPNetworkDown_WaitInputAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
+{
+ RT_NOREF(pInterface, cMillies);
+ LogFlow(("%s: pInterface=%p\n", __FUNCTION__, pInterface));
+ LogFlow(("%s: return VINF_SUCCESS\n", __FUNCTION__));
+ return VINF_SUCCESS;
+}
+
+/**
+ * Receive data and pass it to lwIP for processing.
+ *
+ * @returns VBox status code
+ * @param pInterface PDM network port interface pointer.
+ * @param pvBuf Pointer to frame data.
+ * @param cb Frame size.
+ */
+static DECLCALLBACK(int) devINIPNetworkDown_Input(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
+{
+ RT_NOREF(pInterface);
+ const uint8_t *pbBuf = (const uint8_t *)pvBuf;
+ size_t len = cb;
+ const struct eth_hdr *ethhdr;
+ struct pbuf *p, *q;
+
+ LogFlow(("%s: pInterface=%p pvBuf=%p cb=%lu\n", __FUNCTION__, pInterface, pvBuf, cb));
+ Assert(g_pDevINIPData);
+ Assert(g_pDevINIPData->pDrv);
+
+ /* Silently ignore packets being received while lwIP isn't set up. */
+ if (!g_pDevINIPData)
+ {
+ LogFlow(("%s: return %Rrc (no global)\n", __FUNCTION__, VINF_SUCCESS));
+ return VINF_SUCCESS;
+ }
+
+#if ETH_PAD_SIZE
+ len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
+#endif
+
+ /* We allocate a pbuf chain of pbufs from the pool. */
+ Assert((u16_t)len == len);
+ p = lwip_pbuf_alloc(PBUF_RAW, (u16_t)len, PBUF_POOL);
+ if (p != NULL)
+ {
+#if ETH_PAD_SIZE
+ lwip_pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
+#endif
+
+ for (q = p; q != NULL; q = q->next)
+ {
+ /* Fill the buffers, and clean out unused buffer space. */
+ memcpy(q->payload, pbBuf, RT_MIN(cb, q->len));
+ pbBuf += RT_MIN(cb, q->len);
+ if (q->len > cb)
+ memset(((uint8_t *)q->payload) + cb, '\0', q->len - cb);
+ cb -= RT_MIN(cb, q->len);
+ }
+
+ ethhdr = (const struct eth_hdr *)p->payload;
+ struct netif *iface = &g_pDevINIPData->IntNetIF;
+
+ /* We've setup flags NETIF_FLAG_ETHARP and NETIF_FLAG_ETHERNET
+ so this should be thread-safe. */
+ tcpip_input(p,iface);
+ }
+
+ LogFlow(("%s: return %Rrc\n", __FUNCTION__, VINF_SUCCESS));
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
+ */
+static DECLCALLBACK(void) devINIPNetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
+{
+ NOREF(pInterface);
+}
+
+
+/**
+ * Signals the end of lwIP TCPIP initialization.
+ *
+ * @param arg opaque argument, here the pointer to the PDEVINTNETIP.
+ * @note TCPIP thread, corresponding EMT waiting on semaphore.
+ */
+static DECLCALLBACK(void) devINIPTcpipInitDone(void *arg)
+{
+ PDEVINTNETIP pThis = (PDEVINTNETIP)arg;
+ AssertPtrReturnVoid(arg);
+
+ pThis->rcInitialization = VINF_SUCCESS;
+ struct in_addr ip;
+ if (!inet_aton(pThis->pszIP, &ip))
+ {
+ pThis->rcInitialization = VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
+ PDMDEV_SET_ERROR(pThis->pDevIns, pThis->rcInitialization, N_("Configuration error: Invalid \"IP\" value"));
+ return;
+ }
+ struct ip_addr ipaddr;
+ memcpy(&ipaddr, &ip, sizeof(ipaddr));
+
+ if (!inet_aton(pThis->pszNetmask, &ip))
+ {
+ pThis->rcInitialization = VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
+ PDMDEV_SET_ERROR(pThis->pDevIns, pThis->rcInitialization, N_("Configuration error: Invalid \"Netmask\" value"));
+ return;
+ }
+ struct ip_addr netmask;
+ memcpy(&netmask, &ip, sizeof(netmask));
+
+ if (pThis->pszGateway)
+ {
+ if (!inet_aton(pThis->pszGateway, &ip))
+ {
+ pThis->rcInitialization = VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES;
+ PDMDEV_SET_ERROR(pThis->pDevIns, pThis->rcInitialization, N_("Configuration error: Invalid \"Gateway\" value"));
+ return;
+ }
+ }
+ else
+ inet_aton(pThis->pszIP, &ip);
+ struct ip_addr gw;
+ memcpy(&gw, &ip, sizeof(gw));
+
+ pThis->IntNetIF.name[0] = 'I';
+ pThis->IntNetIF.name[1] = 'N';
+
+ struct netif *ret = netif_add(&pThis->IntNetIF, &ipaddr, &netmask, &gw, NULL, devINIPInterface, lwip_tcpip_input);
+ if (!ret)
+ {
+
+ pThis->rcInitialization = VERR_NET_NO_NETWORK;
+ PDMDEV_SET_ERROR(pThis->pDevIns, pThis->rcInitialization, N_("netif_add failed"));
+ return;
+ }
+
+ lwip_netif_set_default(&pThis->IntNetIF);
+ lwip_netif_set_up(&pThis->IntNetIF);
+}
+
+
+/**
+ * This callback is for finitializing our activity on TCPIP thread.
+ * @todo XXX: We do it only for new LWIP, old LWIP will stay broken for now.
+ */
+static DECLCALLBACK(void) devINIPTcpipFiniDone(void *arg)
+{
+ PDEVINTNETIP pThis = (PDEVINTNETIP)arg;
+ AssertPtrReturnVoid(arg);
+
+ netif_set_link_down(&pThis->IntNetIF);
+ netif_set_down(&pThis->IntNetIF);
+ netif_remove(&pThis->IntNetIF);
+}
+
+
+/**
+ * Gets the current Media Access Control (MAC) address.
+ *
+ * @returns VBox status code.
+ * @param pInterface Pointer to the interface structure containing the called function pointer.
+ * @param pMac Where to store the MAC address.
+ * @thread EMT
+ */
+static DECLCALLBACK(int) devINIPGetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
+{
+ PDEVINTNETIP pThis = RT_FROM_MEMBER(pInterface, DEVINTNETIP, INetworkConfig);
+ memcpy(pMac, pThis->MAC.au8, sizeof(RTMAC));
+ return VINF_SUCCESS;
+}
+
+/**
+ * Gets the new link state.
+ *
+ * @returns The current link state.
+ * @param pInterface Pointer to the interface structure containing the called function pointer.
+ * @thread EMT
+ */
+static DECLCALLBACK(PDMNETWORKLINKSTATE) devINIPGetLinkState(PPDMINETWORKCONFIG pInterface)
+{
+ PDEVINTNETIP pThis = RT_FROM_MEMBER(pInterface, DEVINTNETIP, INetworkConfig);
+ if (pThis->fLnkUp)
+ return PDMNETWORKLINKSTATE_UP;
+ return PDMNETWORKLINKSTATE_DOWN;
+}
+
+
+/**
+ * Sets the new link state.
+ *
+ * @returns VBox status code.
+ * @param pInterface Pointer to the interface structure containing the called function pointer.
+ * @param enmState The new link state
+ */
+static DECLCALLBACK(int) devINIPSetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
+{
+ PDEVINTNETIP pThis = RT_FROM_MEMBER(pInterface, DEVINTNETIP, INetworkConfig);
+ bool fNewUp = enmState == PDMNETWORKLINKSTATE_UP;
+
+ if (fNewUp != pThis->fLnkUp)
+ {
+ if (fNewUp)
+ {
+ LogFlowFunc(("Link is up\n"));
+ pThis->fLnkUp = true;
+ }
+ else
+ {
+ LogFlowFunc(("Link is down\n"));
+ pThis->fLnkUp = false;
+ }
+ if (pThis->pDrv)
+ pThis->pDrv->pfnNotifyLinkChanged(pThis->pDrv, enmState);
+ }
+ return VINF_SUCCESS;
+}
+
+/* -=-=-=-=- PDMIBASE -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) devINIPQueryInterface(PPDMIBASE pInterface,
+ const char *pszIID)
+{
+ PDEVINTNETIP pThis = RT_FROM_MEMBER(pInterface, DEVINTNETIP, IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThis->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThis->INetworkDown);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThis->INetworkConfig);
+ return NULL;
+}
+
+/* -=-=-=-=- PDMDEVREG -=-=-=-=- */
+
+/**
+ * Destruct a device instance.
+ *
+ * Most VM resources are freed by the VM. This callback is provided so that any non-VM
+ * resources can be freed correctly.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The device instance data.
+ */
+static DECLCALLBACK(int) devINIPDestruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
+ LogFlow(("devINIPDestruct: pDevIns=%p\n", pDevIns));
+ PDEVINTNETIP pThis = PDMDEVINS_2_DATA(pDevIns, PDEVINTNETIP);
+
+ if (g_pDevINIPData != NULL)
+ vboxLwipCoreFinalize(devINIPTcpipFiniDone, pThis);
+
+ PDMDevHlpMMHeapFree(pDevIns, pThis->pszIP);
+ pThis->pszIP = NULL;
+ PDMDevHlpMMHeapFree(pDevIns, pThis->pszNetmask);
+ pThis->pszNetmask = NULL;
+ PDMDevHlpMMHeapFree(pDevIns, pThis->pszGateway);
+ pThis->pszGateway = NULL;
+
+ LogFlow(("%s: success\n", __FUNCTION__));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) devINIPConstruct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PDEVINTNETIP pThis = PDMDEVINS_2_DATA(pDevIns, PDEVINTNETIP);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ LogFlow(("devINIPConstruct: pDevIns=%p iInstance=%d pCfg=%p\n", pDevIns, iInstance, pCfg));
+ RT_NOREF(iInstance);
+
+ Assert(iInstance == 0);
+
+ /*
+ * Init the static parts.
+ */
+ //pThis->pszIP = NULL;
+ //pThis->pszNetmask = NULL;
+ //pThis->pszGateway = NULL;
+ /* Pointer to device instance */
+ pThis->pDevIns = pDevIns;
+ /* IBase */
+ pThis->IBase.pfnQueryInterface = devINIPQueryInterface;
+ /* INetworkDown */
+ pThis->INetworkDown.pfnWaitReceiveAvail = devINIPNetworkDown_WaitInputAvail;
+ pThis->INetworkDown.pfnReceive = devINIPNetworkDown_Input;
+ pThis->INetworkDown.pfnXmitPending = devINIPNetworkDown_XmitPending;
+ /* INetworkConfig */
+ pThis->INetworkConfig.pfnGetMac = devINIPGetMac;
+ pThis->INetworkConfig.pfnGetLinkState = devINIPGetLinkState;
+ pThis->INetworkConfig.pfnSetLinkState = devINIPSetLinkState;
+
+
+ /*
+ * Validate the config.
+ */
+ PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC|IP|IPv6|Netmask|Gateway", "");
+
+ /*
+ * Get the configuration settings.
+ */
+ int rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", &pThis->MAC, sizeof(pThis->MAC));
+ if (rc == VERR_CFGM_NOT_BYTES)
+ {
+ char szMAC[64];
+ rc = pHlp->pfnCFGMQueryString(pCfg, "MAC", &szMAC[0], sizeof(szMAC));
+ if (RT_SUCCESS(rc))
+ {
+ char *macStr = &szMAC[0];
+ char *pMac = (char *)&pThis->MAC;
+ for (uint32_t i = 0; i < 6; i++)
+ {
+ if (!*macStr || !macStr[1] || *macStr == ':' || macStr[1] == ':')
+ return PDMDEV_SET_ERROR(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES,
+ N_("Configuration error: Invalid \"MAC\" value"));
+ char c1 = *macStr++ - '0';
+ if (c1 > 9)
+ c1 -= 7;
+ char c2 = *macStr++ - '0';
+ if (c2 > 9)
+ c2 -= 7;
+ *pMac++ = ((c1 & 0x0f) << 4) | (c2 & 0x0f);
+ if (i != 5 && *macStr == ':')
+ macStr++;
+ }
+ }
+ }
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"MAC\" value"));
+ rc = devINIPNetworkConfiguration(pDevIns, pThis, pCfg);
+ AssertLogRelRCReturn(rc, rc);
+
+ /*
+ * Attach driver and query the network connector interface.
+ */
+ rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThis->IBase, &pThis->pDrvBase, "Network Port");
+ if (RT_FAILURE(rc))
+ {
+ pThis->pDrvBase = NULL;
+ pThis->pDrv = NULL;
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Error attaching device below us"));
+ }
+ pThis->pDrv = PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMINETWORKUP);
+ AssertMsgReturn(pThis->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"), VERR_PDM_MISSING_INTERFACE_BELOW);
+
+
+ /*
+ * Set up global pointer to interface data.
+ */
+ g_pDevINIPData = pThis;
+
+
+ /* link hack */
+ pThis->pLinkHack = g_pDevINILinkHack;
+
+ /*
+ * Initialize lwIP.
+ */
+ vboxLwipCoreInitialize(devINIPTcpipInitDone, pThis);
+
+ /* this rc could be updated in devINIPTcpInitDone thread */
+ AssertRCReturn(pThis->rcInitialization, pThis->rcInitialization);
+
+
+ LogFlow(("devINIPConstruct: return %Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Query whether lwIP is initialized or not. Since there is only a single
+ * instance of this device ever for a VM, it can be a global function.
+ *
+ * @returns True if lwIP is initialized.
+ */
+bool DevINIPConfigured(void)
+{
+ return g_pDevINIPData != NULL;
+}
+
+
+/**
+ * Internal network IP stack device registration record.
+ */
+const PDMDEVREG g_DeviceINIP =
+{
+ /* .u32Version = */ PDM_DEVREG_VERSION,
+ /* .uReserved0 = */ 0,
+ /* .szName = */ "IntNetIP",
+ /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE,
+ /* .fClass = */ PDM_DEVREG_CLASS_VMM_DEV, /* As this is used by the storage devices, it must come earlier. */
+ /* .cMaxInstances = */ 1,
+ /* .uSharedVersion = */ 42,
+ /* .cbInstanceShared = */ sizeof(DEVINTNETIP),
+ /* .cbInstanceCC = */ 0,
+ /* .cbInstanceRC = */ 0,
+ /* .cMaxPciDevices = */ 0,
+ /* .cMaxMsixVectors = */ 0,
+ /* .pszDescription = */ "Internal Network IP stack device",
+#if defined(IN_RING3)
+ /* .pszRCMod = */ "",
+ /* .pszR0Mod = */ "",
+ /* .pfnConstruct = */ devINIPConstruct,
+ /* .pfnDestruct = */ devINIPDestruct,
+ /* .pfnRelocate = */ NULL,
+ /* .pfnMemSetup = */ NULL,
+ /* .pfnPowerOn = */ NULL,
+ /* .pfnReset = */ NULL,
+ /* .pfnSuspend = */ NULL,
+ /* .pfnResume = */ NULL,
+ /* .pfnAttach = */ NULL,
+ /* .pfnDetach = */ NULL,
+ /* .pfnQueryInterface = */ NULL,
+ /* .pfnInitComplete = */ NULL,
+ /* .pfnPowerOff = */ NULL,
+ /* .pfnSoftReset = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RING0)
+ /* .pfnEarlyConstruct = */ NULL,
+ /* .pfnConstruct = */ NULL,
+ /* .pfnDestruct = */ NULL,
+ /* .pfnFinalDestruct = */ NULL,
+ /* .pfnRequest = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RC)
+ /* .pfnConstruct = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#else
+# error "Not in IN_RING3, IN_RING0 or IN_RC!"
+#endif
+ /* .u32VersionEnd = */ PDM_DEVREG_VERSION
+};
+
diff --git a/src/VBox/Devices/Network/DevPCNet.cpp b/src/VBox/Devices/Network/DevPCNet.cpp
new file mode 100644
index 00000000..49140775
--- /dev/null
+++ b/src/VBox/Devices/Network/DevPCNet.cpp
@@ -0,0 +1,5437 @@
+/* $Id: DevPCNet.cpp $ */
+/** @file
+ * DevPCNet - AMD PCnet-PCI II / PCnet-FAST III (Am79C970A / Am79C973) Ethernet Controller Emulation.
+ *
+ * This software was written to be compatible with the specifications:
+ * AMD Am79C970A PCnet-PCI II Ethernet Controller Data-Sheet
+ * AMD Publication# 19436 Rev:E Amendment/0 Issue Date: June 2000
+ * and
+ * AMD Am79C973/Am79C975 PCnet-FAST III Single-Chip 10/100 Mbps PCI Ethernet Controller datasheet
+ * AMD publication# 20510 Rev:E Amendment/0 Issue Date: August 2000
+ * and
+ * AMD Am79C960 PCnet-ISA Single-Chip Ethernet Controller datasheet
+ * AMD publication# 16907 Rev:B Amendment/0 Issue Date: May 1994
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ * --------------------------------------------------------------------
+ *
+ * This code is based on:
+ *
+ * AMD PCnet-PCI II (Am79C970A) emulation
+ *
+ * Copyright (c) 2004 Antony T Curtis
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEV_PCNET
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/pdmnetifs.h>
+#include <VBox/vmm/pgm.h>
+#include <VBox/version.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/net.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#ifdef IN_RING3
+# include <iprt/mem.h>
+# include <iprt/semaphore.h>
+# include <iprt/uuid.h>
+#endif
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+/* Enable to handle frequent io reads in the guest context (recommended) */
+#define PCNET_GC_ENABLED
+
+/* PCNET_DEBUG_IO - Log6 */
+/* PCNET_DEBUG_BCR - Log7 */
+/* PCNET_DEBUG_CSR - Log8 */
+/* PCNET_DEBUG_RMD - Log9 */
+/* PCNET_DEBUG_TMD - Log10 */
+/* PCNET_DEBUG_MATCH - Log11 */
+/* PCNET_DEBUG_MII - Log12 */
+
+#define PCNET_IOPORT_SIZE 0x20
+#define PCNET_PNPMMIO_SIZE 0x20
+
+#define PCNET_SAVEDSTATE_VERSION 10
+
+#define BCR_MAX_RAP 50
+#define MII_MAX_REG 32
+#define CSR_MAX_REG 128
+
+/** Maximum number of times we report a link down to the guest (failure to send frame) */
+#define PCNET_MAX_LINKDOWN_REPORTED 3
+
+/** Maximum frame size we handle */
+#define MAX_FRAME 1536
+
+#define PCNET_INST_NR (pThis->iInstance)
+
+/** @name Bus configuration registers
+ * @{ */
+#define BCR_MSRDA 0
+#define BCR_MSWRA 1
+#define BCR_MC 2
+#define BCR_RESERVED3 3
+#define BCR_LNKST 4
+#define BCR_LED1 5
+#define BCR_LED2 6
+#define BCR_LED3 7
+#define BCR_RESERVED8 8
+#define BCR_FDC 9
+/* 10 - 15 = reserved */
+#define BCR_IOBASEL 16 /* Reserved */
+#define BCR_IOBASEU 16 /* Reserved */
+#define BCR_BSBC 18
+#define BCR_EECAS 19
+#define BCR_SWS 20
+#define BCR_INTCON 21 /* Reserved */
+#define BCR_PLAT 22
+#define BCR_PCISVID 23
+#define BCR_PCISID 24
+#define BCR_SRAMSIZ 25
+#define BCR_SRAMB 26
+#define BCR_SRAMIC 27
+#define BCR_EBADDRL 28
+#define BCR_EBADDRU 29
+#define BCR_EBD 30
+#define BCR_STVAL 31
+#define BCR_MIICAS 32
+#define BCR_MIIADDR 33
+#define BCR_MIIMDR 34
+#define BCR_PCIVID 35
+#define BCR_PMC_A 36
+#define BCR_DATA0 37
+#define BCR_DATA1 38
+#define BCR_DATA2 39
+#define BCR_DATA3 40
+#define BCR_DATA4 41
+#define BCR_DATA5 42
+#define BCR_DATA6 43
+#define BCR_DATA7 44
+#define BCR_PMR1 45
+#define BCR_PMR2 46
+#define BCR_PMR3 47
+/** @} */
+
+/** @name Bus configuration sub register accessors.
+ * @{ */
+#define BCR_DWIO(S) !!((S)->aBCR[BCR_BSBC] & 0x0080)
+#define BCR_SSIZE32(S) !!((S)->aBCR[BCR_SWS ] & 0x0100)
+#define BCR_SWSTYLE(S) ((S)->aBCR[BCR_SWS ] & 0x00FF)
+/** @} */
+
+/** @name CSR subregister accessors.
+ * @{ */
+#define CSR_INIT(S) !!((S)->aCSR[0] & 0x0001) /**< Init assertion */
+#define CSR_STRT(S) !!((S)->aCSR[0] & 0x0002) /**< Start assertion */
+#define CSR_STOP(S) !!((S)->aCSR[0] & 0x0004) /**< Stop assertion */
+#define CSR_TDMD(S) !!((S)->aCSR[0] & 0x0008) /**< Transmit demand. (perform xmit poll now (readable, settable, not clearable) */
+#define CSR_TXON(S) !!((S)->aCSR[0] & 0x0010) /**< Transmit on (readonly) */
+#define CSR_RXON(S) !!((S)->aCSR[0] & 0x0020) /**< Receive On */
+#define CSR_INEA(S) !!((S)->aCSR[0] & 0x0040) /**< Interrupt Enable */
+#define CSR_LAPPEN(S) !!((S)->aCSR[3] & 0x0020) /**< Look Ahead Packet Processing Enable */
+#define CSR_DXSUFLO(S) !!((S)->aCSR[3] & 0x0040) /**< Disable Transmit Stop on Underflow error */
+#define CSR_ASTRP_RCV(S) !!((S)->aCSR[4] & 0x0400) /**< Auto Strip Receive */
+#define CSR_DPOLL(S) !!((S)->aCSR[4] & 0x1000) /**< Disable Transmit Polling */
+#define CSR_SPND(S) !!((S)->aCSR[5] & 0x0001) /**< Suspend */
+#define CSR_LTINTEN(S) !!((S)->aCSR[5] & 0x4000) /**< Last Transmit Interrupt Enable */
+#define CSR_TOKINTD(S) !!((S)->aCSR[5] & 0x8000) /**< Transmit OK Interrupt Disable */
+
+#define CSR_STINT !!((S)->aCSR[7] & 0x0800) /**< Software Timer Interrupt */
+#define CSR_STINTE !!((S)->aCSR[7] & 0x0400) /**< Software Timer Interrupt Enable */
+
+#define CSR_DRX(S) !!((S)->aCSR[15] & 0x0001) /**< Disable Receiver */
+#define CSR_DTX(S) !!((S)->aCSR[15] & 0x0002) /**< Disable Transmit */
+#define CSR_LOOP(S) !!((S)->aCSR[15] & 0x0004) /**< Loopback Enable */
+#define CSR_DRCVPA(S) !!((S)->aCSR[15] & 0x2000) /**< Disable Receive Physical Address */
+#define CSR_DRCVBC(S) !!((S)->aCSR[15] & 0x4000) /**< Disable Receive Broadcast */
+#define CSR_PROM(S) !!((S)->aCSR[15] & 0x8000) /**< Promiscuous Mode */
+/** @} */
+
+#if defined(RT_BIG_ENDIAN) || !defined(RT_LITTLE_ENDIAN)
+# error fix macros (and more in this file) for big-endian machines
+#endif
+
+/** @name CSR register accessors.
+ * @{ */
+#define CSR_IADR(S) (*(uint32_t*)((S)->aCSR + 1)) /**< Initialization Block Address */
+#define CSR_CRBA(S) (*(uint32_t*)((S)->aCSR + 18)) /**< Current Receive Buffer Address */
+#define CSR_CXBA(S) (*(uint32_t*)((S)->aCSR + 20)) /**< Current Transmit Buffer Address */
+#define CSR_NRBA(S) (*(uint32_t*)((S)->aCSR + 22)) /**< Next Receive Buffer Address */
+#define CSR_BADR(S) (*(uint32_t*)((S)->aCSR + 24)) /**< Base Address of Receive Ring */
+#define CSR_NRDA(S) (*(uint32_t*)((S)->aCSR + 26)) /**< Next Receive Descriptor Address */
+#define CSR_CRDA(S) (*(uint32_t*)((S)->aCSR + 28)) /**< Current Receive Descriptor Address */
+#define CSR_BADX(S) (*(uint32_t*)((S)->aCSR + 30)) /**< Base Address of Transmit Descriptor */
+#define CSR_NXDA(S) (*(uint32_t*)((S)->aCSR + 32)) /**< Next Transmit Descriptor Address */
+#define CSR_CXDA(S) (*(uint32_t*)((S)->aCSR + 34)) /**< Current Transmit Descriptor Address */
+#define CSR_NNRD(S) (*(uint32_t*)((S)->aCSR + 36)) /**< Next Next Receive Descriptor Address */
+#define CSR_NNXD(S) (*(uint32_t*)((S)->aCSR + 38)) /**< Next Next Transmit Descriptor Address */
+#define CSR_CRBC(S) ((S)->aCSR[40]) /**< Current Receive Byte Count */
+#define CSR_CRST(S) ((S)->aCSR[41]) /**< Current Receive Status */
+#define CSR_CXBC(S) ((S)->aCSR[42]) /**< Current Transmit Byte Count */
+#define CSR_CXST(S) ((S)->aCSR[43]) /**< Current transmit status */
+#define CSR_NRBC(S) ((S)->aCSR[44]) /**< Next Receive Byte Count */
+#define CSR_NRST(S) ((S)->aCSR[45]) /**< Next Receive Status */
+#define CSR_POLL(S) ((S)->aCSR[46]) /**< Transmit Poll Time Counter */
+#define CSR_PINT(S) ((S)->aCSR[47]) /**< Transmit Polling Interval */
+#define CSR_PXDA(S) (*(uint32_t*)((S)->aCSR + 60)) /**< Previous Transmit Descriptor Address*/
+#define CSR_PXBC(S) ((S)->aCSR[62]) /**< Previous Transmit Byte Count */
+#define CSR_PXST(S) ((S)->aCSR[63]) /**< Previous Transmit Status */
+#define CSR_NXBA(S) (*(uint32_t*)((S)->aCSR + 64)) /**< Next Transmit Buffer Address */
+#define CSR_NXBC(S) ((S)->aCSR[66]) /**< Next Transmit Byte Count */
+#define CSR_NXST(S) ((S)->aCSR[67]) /**< Next Transmit Status */
+#define CSR_RCVRC(S) ((S)->aCSR[72]) /**< Receive Descriptor Ring Counter */
+#define CSR_XMTRC(S) ((S)->aCSR[74]) /**< Transmit Descriptor Ring Counter */
+#define CSR_RCVRL(S) ((S)->aCSR[76]) /**< Receive Descriptor Ring Length */
+#define CSR_XMTRL(S) ((S)->aCSR[78]) /**< Transmit Descriptor Ring Length */
+#define CSR_MISSC(S) ((S)->aCSR[112]) /**< Missed Frame Count */
+/** @} */
+
+/** @name Version for the PCnet/FAST III 79C973 card
+ * @{ */
+#define CSR_VERSION_LOW_79C973 0x5003 /* the lower two bits are always 11b for AMD (JEDEC) */
+#define CSR_VERSION_LOW_79C970A 0x1003
+#define CSR_VERSION_LOW_79C960 0x3003
+#define CSR_VERSION_HIGH 0x0262
+/** @} */
+
+/** Calculates the full physical address. */
+#define PHYSADDR(S,A) ((A) | (S)->GCUpperPhys)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/**
+ * Emulated device types.
+ */
+enum PCNET_DEVICE_TYPE
+{
+ DEV_AM79C970A = 0, /* PCnet-PCI II (PCI, 10 Mbps). */
+ DEV_AM79C973 = 1, /* PCnet-FAST III (PCI, 10/100 Mbps). */
+ DEV_AM79C960 = 2, /* PCnet-ISA (ISA, 10 Mbps, NE2100/NE1500T compatible) */
+ DEV_AM79C960_EB = 3 /* PCnet-ISA (ISA, 10 Mbps, Racal InterLan EtherBlaster compatible) */
+};
+
+#define PCNET_IS_PCI(pcnet) ((pcnet->uDevType == DEV_AM79C970A) || (pcnet->uDevType == DEV_AM79C973))
+#define PCNET_IS_ISA(pcnet) ((pcnet->uDevType == DEV_AM79C960) || (pcnet->uDevType == DEV_AM79C960_EB))
+
+/*
+ * Note: Old adapters, including the original NE2100/NE1500T, used the LANCE (Am7990) or
+ * possibly C-LANCE (Am79C90) chips. Those only had the RAP/RDP register and any PROMs were
+ * decoded by external circuitry; as a consequence, different vendors used different layouts.
+ * Several different variants are known:
+ *
+ * 1) Racal Interlan NI6510 (before PCnet-based EtherBlaster variants):
+ * - RDP at iobase + 0x00 (16-bit)
+ * - RAP at iobase + 0x02 (16-bit)
+ * - reset at iobase + 0x04 (8-bit)
+ * - config at iobase + 0x05 (8-bit)
+ * - PROM at iobase + 0x08
+ *
+ * 2) BICC Data Networks ISOLAN 4110-2 (ISA) / 4110-3 (MCA):
+ * - PROM at iobase + 0x00
+ * - reset at iobase + 0x04
+ * - config at iobase + 0x05
+ * - RDP at iobase + 0x0c (16-bit)
+ * - RAP at iobase + 0x0e (16-bit)
+ *
+ * 3) Novell NE2100/NE1500T, AMD Am2100, all PCnet chips:
+ * - PROM at iobase + 0x00
+ * - RDP at iobase + 0x10 (16-bit)
+ * - RAP at iobase + 0x12 (16-bit)
+ * - reset at iobase + 0x14 (16-bit)
+ * - BDP/IDP at iobase + 0x16 (16-bit)
+ *
+ * There were also non-busmastering LANCE adapters, such as the BICC 4110-1, DEC DEPCA,
+ * or NTI 16. Those are uninteresting.
+ *
+ * Newer adapters based on the integrated PCnet-ISA (Am79C960) and later have a fixed
+ * register layout compatible with the NE2100. However, they may still require different
+ * drivers. At least two different incompatible drivers exist for PCnet-based cards:
+ * Those from AMD and those from Racal InterLan for their EtherBlaster cards. The PROM
+ * on EtherBlaster cards uses a different signature ('RD' instead of 'WW') and Racal's
+ * drivers insist on the MAC address having the Racal-Datacom OUI (02 07 01).
+ */
+
+/**
+ * PCNET state.
+ *
+ * @implements PDMIBASE
+ * @implements PDMINETWORKDOWN
+ * @implements PDMINETWORKCONFIG
+ * @implements PDMILEDPORTS
+ */
+typedef struct PCNETSTATE
+{
+ /** Software Interrupt timer - R3. */
+ TMTIMERHANDLE hTimerSoftInt;
+ /** Poll timer - R3. */
+ TMTIMERHANDLE hTimerPoll;
+ /** Restore timer.
+ * This is used to disconnect and reconnect the link after a restore. */
+ TMTIMERHANDLE hTimerRestore;
+
+ /** Transmit signaller. */
+ PDMTASKHANDLE hXmitTask;
+
+ /** Register Address Pointer */
+ uint32_t u32RAP;
+ /** Internal interrupt service */
+ int32_t iISR;
+ /** ??? */
+ uint32_t u32Lnkst;
+ /** Address of the RX descriptor table (ring). Loaded at init. */
+ RTGCPHYS32 GCRDRA;
+ /** Address of the TX descriptor table (ring). Loaded at init. */
+ RTGCPHYS32 GCTDRA;
+ uint8_t aPROM[16];
+ uint16_t aCSR[CSR_MAX_REG];
+ uint16_t aBCR[BCR_MAX_RAP];
+ uint16_t aMII[MII_MAX_REG];
+
+ /** Holds the bits which were really seen by the guest. Relevant are bits
+ * 8..14 (IDON, TINT, RINT, MERR, MISS, CERR, BABL). We don't allow the
+ * guest to clear any of these bits (by writing a ONE) before a bit was
+ * seen by the guest. */
+ uint16_t u16CSR0LastSeenByGuest;
+ /** The configured IRQ for ISA operation. */
+ uint8_t uIsaIrq;
+ /** Alignment padding. */
+ uint8_t Alignment2[1+4];
+
+ /** Last time we polled the queues */
+ uint64_t u64LastPoll;
+
+ /** Size of a RX/TX descriptor (8 or 16 bytes according to SWSTYLE */
+ int32_t iLog2DescSize;
+ /** Bits 16..23 in 16-bit mode */
+ RTGCPHYS32 GCUpperPhys;
+
+ /** Base port of the I/O space region. */
+ RTIOPORT IOPortBase;
+ /** If set the link is currently up. */
+ bool fLinkUp;
+ /** If set the link is temporarily down because of a saved state load. */
+ bool fLinkTempDown;
+
+ /** Number of times we've reported the link down. */
+ uint32_t cLinkDownReported;
+ /** The configured MAC address. */
+ RTMAC MacConfigured;
+ /** Set if PCNETSTATER3::pDrvR3 is not NULL. */
+ bool fDriverAttached;
+ /** Alignment padding. */
+ uint8_t bAlignment3;
+
+ /** The LED. */
+ PDMLED Led;
+
+ /** Access critical section. */
+ PDMCRITSECT CritSect;
+ /** Event semaphore for blocking on receive. */
+ SUPSEMEVENT hEventOutOfRxSpace;
+ /** We are waiting/about to start waiting for more receive buffers. */
+ bool volatile fMaybeOutOfSpace;
+ /** True if we signal the guest that RX packets are missing. */
+ bool fSignalRxMiss;
+ /** Alignment padding. */
+ uint8_t Alignment4[HC_ARCH_BITS == 64 ? 2 : 6];
+
+ /** Error counter for bad receive descriptors. */
+ uint32_t uCntBadRMD;
+ /** Emulated device type. */
+ uint8_t uDevType;
+ bool afAlignment5[3];
+ /** Link speed to be reported through CSR68. */
+ uint32_t u32LinkSpeed;
+ /** MS to wait before we enable the link. */
+ uint32_t cMsLinkUpDelay;
+ /** The device instance number (for logging). */
+ uint32_t iInstance;
+
+ /** PCI Region \#0: I/O ports offset 0x10-0x1f. */
+ IOMIOPORTHANDLE hIoPortsPci;
+ /** PCI Region \#0: I/O ports offset 0x00-0x0f. */
+ IOMIOPORTHANDLE hIoPortsPciAProm;
+ /** PCI Region \#1: MMIO alternative to the I/O ports in region \#0. */
+ IOMMMIOHANDLE hMmioPci;
+
+ /** ISA I/O ports offset 0x10-0x1f. */
+ IOMIOPORTHANDLE hIoPortsIsa;
+ /** ISA I/O ports offset 0x00-0x0f. */
+ IOMIOPORTHANDLE hIoPortsIsaAProm;
+
+ /** Backwards compatible shared memory region during state loading (before 4.3.6). */
+ PGMMMIO2HANDLE hMmio2Shared;
+
+ /** The loopback transmit buffer (avoid stack allocations). */
+ uint8_t abLoopBuf[4096];
+ /** The recv buffer. */
+ uint8_t abRecvBuf[4096];
+
+ STAMCOUNTER StatReceiveBytes;
+ STAMCOUNTER StatTransmitBytes;
+#ifdef VBOX_WITH_STATISTICS
+ STAMPROFILEADV StatMMIOReadRZ;
+ STAMPROFILEADV StatMMIOReadR3;
+ STAMPROFILEADV StatMMIOWriteRZ;
+ STAMPROFILEADV StatMMIOWriteR3;
+ STAMPROFILEADV StatAPROMRead;
+ STAMPROFILEADV StatAPROMWrite;
+ STAMPROFILEADV StatIOReadRZ;
+ STAMPROFILEADV StatIOReadR3;
+ STAMPROFILEADV StatIOWriteRZ;
+ STAMPROFILEADV StatIOWriteR3;
+ STAMPROFILEADV StatTimer;
+ STAMPROFILEADV StatReceive;
+ STAMPROFILEADV StatTransmitR3;
+ STAMPROFILEADV StatTransmitRZ;
+ STAMCOUNTER StatTransmitCase1;
+ STAMCOUNTER StatTransmitCase2;
+ STAMPROFILE StatTransmitSendR3;
+ STAMPROFILE StatTransmitSendRZ;
+ STAMPROFILEADV StatTdtePollRZ;
+ STAMPROFILEADV StatTdtePollR3;
+ STAMPROFILEADV StatTmdStoreRZ;
+ STAMPROFILEADV StatTmdStoreR3;
+ STAMPROFILEADV StatRdtePollR3;
+ STAMPROFILEADV StatRdtePollRZ;
+ STAMPROFILE StatRxOverflow;
+ STAMCOUNTER StatRxOverflowWakeup;
+ STAMCOUNTER aStatXmitFlush[16];
+ STAMCOUNTER aStatXmitChainCounts[16];
+ STAMCOUNTER StatXmitSkipCurrent;
+ STAMPROFILEADV StatInterrupt;
+ STAMPROFILEADV StatPollTimer;
+ STAMCOUNTER StatMIIReads;
+#endif /* VBOX_WITH_STATISTICS */
+} PCNETSTATE;
+/** Pointer to a shared PCnet state structure. */
+typedef PCNETSTATE *PPCNETSTATE;
+
+
+/**
+ * PCNET state for ring-3.
+ *
+ * @implements PDMIBASE
+ * @implements PDMINETWORKDOWN
+ * @implements PDMINETWORKCONFIG
+ * @implements PDMILEDPORTS
+ */
+typedef struct PCNETSTATER3
+{
+ /** Pointer to the device instance. */
+ PPDMDEVINSR3 pDevIns;
+ /** Pointer to the connector of the attached network driver. */
+ PPDMINETWORKUPR3 pDrv;
+ /** Pointer to the attached network driver. */
+ R3PTRTYPE(PPDMIBASE) pDrvBase;
+ /** LUN\#0 + status LUN: The base interface. */
+ PDMIBASE IBase;
+ /** LUN\#0: The network port interface. */
+ PDMINETWORKDOWN INetworkDown;
+ /** LUN\#0: The network config port interface. */
+ PDMINETWORKCONFIG INetworkConfig;
+
+ /** Status LUN: The LED ports. */
+ PDMILEDPORTS ILeds;
+ /** Partner of ILeds. */
+ R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
+} PCNETSTATER3;
+/** Pointer to a PCnet state structure for ring-3. */
+typedef PCNETSTATER3 *PPCNETSTATER3;
+
+
+/**
+ * PCNET state for ring-0.
+ */
+typedef struct PCNETSTATER0
+{
+ /** Pointer to the connector of the attached network driver. */
+ PPDMINETWORKUPR0 pDrv;
+} PCNETSTATER0;
+/** Pointer to a PCnet state structure for ring-0. */
+typedef PCNETSTATER0 *PPCNETSTATER0;
+
+
+/**
+ * PCNET state for raw-mode.
+ */
+typedef struct PCNETSTATERC
+{
+ /** Pointer to the connector of the attached network driver. */
+ PPDMINETWORKUPRC pDrv;
+} PCNETSTATERC;
+/** Pointer to a PCnet state structure for raw-mode. */
+typedef PCNETSTATERC *PPCNETSTATERC;
+
+
+/** The PCnet state structure for the current context. */
+typedef CTX_SUFF(PCNETSTATE) PCNETSTATECC;
+/** Pointer to a PCnet state structure for the current context. */
+typedef CTX_SUFF(PPCNETSTATE) PPCNETSTATECC;
+
+
+/** @todo All structs: big endian? */
+
+struct INITBLK16
+{
+ uint16_t mode; /**< copied into csr15 */
+ uint16_t padr1; /**< MAC 0..15 */
+ uint16_t padr2; /**< MAC 16..32 */
+ uint16_t padr3; /**< MAC 33..47 */
+ uint16_t ladrf1; /**< logical address filter 0..15 */
+ uint16_t ladrf2; /**< logical address filter 16..31 */
+ uint16_t ladrf3; /**< logical address filter 32..47 */
+ uint16_t ladrf4; /**< logical address filter 48..63 */
+ uint32_t rdra:24; /**< address of receive descriptor ring */
+ uint32_t res1:5; /**< reserved */
+ uint32_t rlen:3; /**< number of receive descriptor ring entries */
+ uint32_t tdra:24; /**< address of transmit descriptor ring */
+ uint32_t res2:5; /**< reserved */
+ uint32_t tlen:3; /**< number of transmit descriptor ring entries */
+};
+AssertCompileSize(INITBLK16, 24);
+
+/** bird: I've changed the type for the bitfields. They should only be 16-bit all together.
+ * frank: I've changed the bitfiled types to uint32_t to prevent compiler warnings. */
+struct INITBLK32
+{
+ uint16_t mode; /**< copied into csr15 */
+ uint16_t res1:4; /**< reserved */
+ uint16_t rlen:4; /**< number of receive descriptor ring entries */
+ uint16_t res2:4; /**< reserved */
+ uint16_t tlen:4; /**< number of transmit descriptor ring entries */
+ uint16_t padr1; /**< MAC 0..15 */
+ uint16_t padr2; /**< MAC 16..31 */
+ uint16_t padr3; /**< MAC 32..47 */
+ uint16_t res3; /**< reserved */
+ uint16_t ladrf1; /**< logical address filter 0..15 */
+ uint16_t ladrf2; /**< logical address filter 16..31 */
+ uint16_t ladrf3; /**< logical address filter 32..47 */
+ uint16_t ladrf4; /**< logical address filter 48..63 */
+ uint32_t rdra; /**< address of receive descriptor ring */
+ uint32_t tdra; /**< address of transmit descriptor ring */
+};
+AssertCompileSize(INITBLK32, 28);
+
+/** Transmit Message Descriptor */
+typedef struct TMD
+{
+ struct
+ {
+ uint32_t tbadr; /**< transmit buffer address */
+ } tmd0;
+ struct
+ {
+ uint32_t bcnt:12; /**< buffer byte count (two's complement) */
+ uint32_t ones:4; /**< must be 1111b */
+ uint32_t res:7; /**< reserved */
+ uint32_t bpe:1; /**< bus parity error */
+ uint32_t enp:1; /**< end of packet */
+ uint32_t stp:1; /**< start of packet */
+ uint32_t def:1; /**< deferred */
+ uint32_t one:1; /**< exactly one retry was needed to transmit a frame */
+ uint32_t ltint:1; /**< suppress interrupts after successful transmission */
+ uint32_t nofcs:1; /**< when set, the state of DXMTFCS is ignored and
+ transmitter FCS generation is activated. */
+ uint32_t err:1; /**< error occurred */
+ uint32_t own:1; /**< 0=owned by guest driver, 1=owned by controller */
+ } tmd1;
+ struct
+ {
+ uint32_t trc:4; /**< transmit retry count */
+ uint32_t res:12; /**< reserved */
+ uint32_t tdr:10; /**< time domain reflectometry */
+ uint32_t rtry:1; /**< retry error */
+ uint32_t lcar:1; /**< loss of carrier */
+ uint32_t lcol:1; /**< late collision */
+ uint32_t exdef:1; /**< excessive deferral */
+ uint32_t uflo:1; /**< underflow error */
+ uint32_t buff:1; /**< out of buffers (ENP not found) */
+ } tmd2;
+ struct
+ {
+ uint32_t res; /**< reserved for user defined space */
+ } tmd3;
+} TMD;
+AssertCompileSize(TMD, 16);
+
+/** Receive Message Descriptor */
+typedef struct RMD
+{
+ struct
+ {
+ uint32_t rbadr; /**< receive buffer address */
+ } rmd0;
+ struct
+ {
+ uint32_t bcnt:12; /**< buffer byte count (two's complement) */
+ uint32_t ones:4; /**< must be 1111b */
+ uint32_t res:4; /**< reserved */
+ uint32_t bam:1; /**< broadcast address match */
+ uint32_t lafm:1; /**< logical filter address match */
+ uint32_t pam:1; /**< physical address match */
+ uint32_t bpe:1; /**< bus parity error */
+ uint32_t enp:1; /**< end of packet */
+ uint32_t stp:1; /**< start of packet */
+ uint32_t buff:1; /**< buffer error */
+ uint32_t crc:1; /**< crc error on incoming frame */
+ uint32_t oflo:1; /**< overflow error (lost all or part of incoming frame) */
+ uint32_t fram:1; /**< frame error */
+ uint32_t err:1; /**< error occurred */
+ uint32_t own:1; /**< 0=owned by guest driver, 1=owned by controller */
+ } rmd1;
+ struct
+ {
+ uint32_t mcnt:12; /**< message byte count */
+ uint32_t zeros:4; /**< 0000b */
+ uint32_t rpc:8; /**< receive frame tag */
+ uint32_t rcc:8; /**< receive frame tag + reserved */
+ } rmd2;
+ struct
+ {
+ uint32_t res; /**< reserved for user defined space */
+ } rmd3;
+} RMD;
+AssertCompileSize(RMD, 16);
+
+
+#ifndef VBOX_DEVICE_STRUCT_TESTCASE
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void pcnetPollRxTx(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC);
+static void pcnetPollTimer(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC);
+static void pcnetUpdateIrq(PPDMDEVINS pDevIns, PPCNETSTATE pThis);
+static uint32_t pcnetBCRReadU16(PPCNETSTATE pThis, uint32_t u32RAP);
+static VBOXSTRICTRC pcnetBCRWriteU16(PPDMDEVINS pDevIns, PPCNETSTATE pThis, uint32_t u32RAP, uint32_t val);
+static void pcnetPollTimerStart(PPDMDEVINS pDevIns, PPCNETSTATE pThis);
+static int pcnetXmitPending(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, bool fOnWorkerThread);
+
+#define PRINT_TMD(T) Log10(( \
+ "TMD0 : TBADR=%#010x\n" \
+ "TMD1 : OWN=%d, ERR=%d, FCS=%d, LTI=%d, " \
+ "ONE=%d, DEF=%d, STP=%d, ENP=%d,\n" \
+ " BPE=%d, BCNT=%d\n" \
+ "TMD2 : BUF=%d, UFL=%d, EXD=%d, LCO=%d, " \
+ "LCA=%d, RTR=%d,\n" \
+ " TDR=%d, TRC=%d\n", \
+ (T)->tmd0.tbadr, \
+ (T)->tmd1.own, (T)->tmd1.err, (T)->tmd1.nofcs, \
+ (T)->tmd1.ltint, (T)->tmd1.one, (T)->tmd1.def, \
+ (T)->tmd1.stp, (T)->tmd1.enp, (T)->tmd1.bpe, \
+ 4096-(T)->tmd1.bcnt, \
+ (T)->tmd2.buff, (T)->tmd2.uflo, (T)->tmd2.exdef,\
+ (T)->tmd2.lcol, (T)->tmd2.lcar, (T)->tmd2.rtry, \
+ (T)->tmd2.tdr, (T)->tmd2.trc))
+
+#define PRINT_RMD(R) Log9(( \
+ "RMD0 : RBADR=%#010x\n" \
+ "RMD1 : OWN=%d, ERR=%d, FRAM=%d, OFLO=%d, " \
+ "CRC=%d, BUFF=%d, STP=%d, ENP=%d,\n " \
+ "BPE=%d, PAM=%d, LAFM=%d, BAM=%d, ONES=%d, BCNT=%d\n" \
+ "RMD2 : RCC=%d, RPC=%d, MCNT=%d, ZEROS=%d\n", \
+ (R)->rmd0.rbadr, \
+ (R)->rmd1.own, (R)->rmd1.err, (R)->rmd1.fram, \
+ (R)->rmd1.oflo, (R)->rmd1.crc, (R)->rmd1.buff, \
+ (R)->rmd1.stp, (R)->rmd1.enp, (R)->rmd1.bpe, \
+ (R)->rmd1.pam, (R)->rmd1.lafm, (R)->rmd1.bam, \
+ (R)->rmd1.ones, 4096-(R)->rmd1.bcnt, \
+ (R)->rmd2.rcc, (R)->rmd2.rpc, (R)->rmd2.mcnt, \
+ (R)->rmd2.zeros))
+
+
+
+/**
+ * Checks if the link is up.
+ * @returns true if the link is up.
+ * @returns false if the link is down.
+ */
+DECLINLINE(bool) pcnetIsLinkUp(PPCNETSTATE pThis)
+{
+ return pThis->fDriverAttached && !pThis->fLinkTempDown && pThis->fLinkUp;
+}
+
+/**
+ * Memory write helper to handle PCI/ISA differences.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis Pointer to the PCNet device instance
+ * @param GCPhys Guest physical memory address
+ * @param pvBuf Host side buffer address
+ * @param cbWrite Number of bytes to write
+ */
+static void pcnetPhysWrite(PPDMDEVINS pDevIns, PPCNETSTATE pThis, RTGCPHYS GCPhys, const void *pvBuf, size_t cbWrite)
+{
+ if (!PCNET_IS_ISA(pThis))
+ PDMDevHlpPCIPhysWrite(pDevIns, GCPhys, pvBuf, cbWrite);
+ else
+ PDMDevHlpPhysWrite(pDevIns, GCPhys, pvBuf, cbWrite);
+}
+
+
+/**
+ * Memory read helper to handle PCI/ISA differences.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis Pointer to the PCNet device instance.
+ * @param GCPhys Guest physical memory address.
+ * @param pvBuf Host side buffer address.
+ * @param cbRead Number of bytes to read.
+ */
+static void pcnetPhysRead(PPDMDEVINS pDevIns, PPCNETSTATE pThis, RTGCPHYS GCPhys, void *pvBuf, size_t cbRead)
+{
+ if (!PCNET_IS_ISA(pThis))
+ PDMDevHlpPCIPhysRead(pDevIns, GCPhys, pvBuf, cbRead);
+ else
+ PDMDevHlpPhysRead(pDevIns, GCPhys, pvBuf, cbRead);
+}
+
+
+/**
+ * Load transmit message descriptor (TMD) if we own it.
+ * Makes sure we read the OWN bit first, which requires issuing two reads if
+ * the OWN bit is laid out in the second (D)WORD in memory.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis adapter private data
+ * @param addr physical address of the descriptor
+ * @return true if we own the descriptor, false otherwise
+ */
+DECLINLINE(bool) pcnetTmdTryLoad(PPDMDEVINS pDevIns, PPCNETSTATE pThis, TMD *tmd, RTGCPHYS32 addr)
+{
+ /* Convert the in-memory format to the internal layout which corresponds to SWSTYLE=2.
+ * Do not touch tmd if the OWN bit is not set (i.e. we don't own the descriptor).
+ */
+ if (RT_UNLIKELY(BCR_SWSTYLE(pThis) == 0))
+ {
+ uint16_t xda[4]; /* Corresponds to memory layout, last word unused. */
+
+ /* For SWSTYLE=0, the OWN bit is in the second WORD we need and must be read before the first WORD. */
+ pcnetPhysRead(pDevIns, pThis, addr + sizeof(uint16_t), (void*)&xda[1], 2 * sizeof(uint16_t));
+ if (!(xda[1] & RT_BIT(15)))
+ return false;
+ pcnetPhysRead(pDevIns, pThis, addr, (void*)&xda[0], sizeof(uint16_t));
+ ((uint32_t *)tmd)[0] = (uint32_t)xda[0] | ((uint32_t)(xda[1] & 0x00ff) << 16); /* TMD0, buffer address. */
+ ((uint32_t *)tmd)[1] = (uint32_t)xda[2] | ((uint32_t)(xda[1] & 0xff00) << 16); /* TMD1, buffer size and control bits. */
+ ((uint32_t *)tmd)[2] = 0; /* TMD2, status bits, set on error but not read. */
+ ((uint32_t *)tmd)[3] = 0; /* TMD3, user data, never accessed. */
+ }
+ else if (RT_LIKELY(BCR_SWSTYLE(pThis) != 3))
+ {
+ /* For SWSTYLE=2, the OWN bit is in the second DWORD we need and must be read first. */
+ uint32_t xda[2];
+ pcnetPhysRead(pDevIns, pThis, addr + sizeof(uint32_t), (void*)&xda[1], sizeof(uint32_t));
+ if (!(xda[1] & RT_BIT(31)))
+ return false;
+ pcnetPhysRead(pDevIns, pThis, addr, (void*)&xda[0], sizeof(uint32_t));
+ ((uint32_t *)tmd)[0] = xda[0]; /* TMD0, buffer address. */
+ ((uint32_t *)tmd)[1] = xda[1]; /* TMD1, buffer size and control bits. */
+ ((uint32_t *)tmd)[2] = 0; /* TMD2, status bits, set on error but not read. */
+ ((uint32_t *)tmd)[3] = 0; /* TMD3, user data, never accessed. */
+ }
+ else
+ {
+ /* For SWSTYLE=3, the OWN bit is in the first DWORD we need, therefore a single read suffices. */
+ uint32_t xda[2];
+ pcnetPhysRead(pDevIns, pThis, addr + sizeof(uint32_t), (void*)&xda, sizeof(xda));
+ if (!(xda[0] & RT_BIT(31)))
+ return false;
+ ((uint32_t *)tmd)[0] = xda[1]; /* TMD0, buffer address. */
+ ((uint32_t *)tmd)[1] = xda[0]; /* TMD1, buffer size and control bits. */
+ ((uint32_t *)tmd)[2] = 0; /* TMD2, status bits, set on error but not read. */
+ ((uint32_t *)tmd)[3] = 0; /* TMD3, user data, never accessed. */
+ }
+
+ return !!tmd->tmd1.own;
+}
+
+#if defined(IN_RING3) || defined(LOG_ENABLED)
+
+/**
+ * Loads an entire transmit message descriptor. Used for logging/debugging.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis Adapter private data
+ * @param addr Physical address of the descriptor
+ */
+DECLINLINE(void) pcnetTmdLoadAll(PPDMDEVINS pDevIns, PPCNETSTATE pThis, TMD *tmd, RTGCPHYS32 addr)
+{
+ /* Convert the in-memory format to the internal layout which corresponds to SWSTYLE=2. */
+ if (RT_UNLIKELY(BCR_SWSTYLE(pThis) == 0))
+ {
+ uint16_t xda[4];
+
+ /* For SWSTYLE=0, we have to do a bit of work. */
+ pcnetPhysRead(pDevIns, pThis, addr, (void*)&xda, sizeof(xda));
+ ((uint32_t *)tmd)[0] = (uint32_t)xda[0] | ((uint32_t)(xda[1] & 0x00ff) << 16); /* TMD0, buffer address. */
+ ((uint32_t *)tmd)[1] = (uint32_t)xda[2] | ((uint32_t)(xda[1] & 0xff00) << 16); /* TMD1, buffer size and control bits. */
+ ((uint32_t *)tmd)[2] = (uint32_t)xda[3] << 16; /* TMD2, status bits. */
+ ((uint32_t *)tmd)[3] = 0; /* TMD3, user data, not present for SWSTYLE=1. */
+ }
+ else if (RT_LIKELY(BCR_SWSTYLE(pThis) != 3))
+ {
+ /* For SWSTYLE=2, simply read the TMD as is. */
+ pcnetPhysRead(pDevIns, pThis, addr, (void*)tmd, sizeof(*tmd));
+ }
+ else
+ {
+ /* For SWSTYLE=3, swap the first and third DWORD around. */
+ uint32_t xda[4];
+ pcnetPhysRead(pDevIns, pThis, addr, (void*)&xda, sizeof(xda));
+ ((uint32_t *)tmd)[0] = xda[2]; /* TMD0, buffer address. */
+ ((uint32_t *)tmd)[1] = xda[1]; /* TMD1, buffer size and control bits. */
+ ((uint32_t *)tmd)[2] = xda[0]; /* TMD2, status bits. */
+ ((uint32_t *)tmd)[3] = xda[3]; /* TMD3, user data. */
+ }
+}
+
+#endif
+
+/**
+ * Store transmit message descriptor and hand it over to the host (the VM guest).
+ * Make sure the cleared OWN bit gets written last.
+ */
+DECLINLINE(void) pcnetTmdStorePassHost(PPDMDEVINS pDevIns, PPCNETSTATE pThis, TMD *tmd, RTGCPHYS32 addr)
+{
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatTmdStore), a);
+ if (RT_UNLIKELY(BCR_SWSTYLE(pThis) == 0))
+ {
+ uint16_t xda[4]; /* Corresponds to memory layout, only two words used. */
+
+ /* For SWSTYLE=0, write the status word first, then the word containing the OWN bit. */
+ xda[1] = ((((uint32_t *)tmd)[0] >> 16) & 0xff) | ((((uint32_t *)tmd)[1]>>16) & 0xff00);
+ xda[1] &= ~RT_BIT(15);
+ xda[3] = ((uint32_t *)tmd)[2] >> 16;
+ /** @todo The WORD containing status bits may not need to be written unless the ERR bit is set. */
+ pcnetPhysWrite(pDevIns, pThis, addr + 3 * sizeof(uint16_t), (void*)&xda[3], sizeof(uint16_t));
+ pcnetPhysWrite(pDevIns, pThis, addr + 1 * sizeof(uint16_t), (void*)&xda[1], sizeof(uint16_t));
+ }
+ else if (RT_LIKELY(BCR_SWSTYLE(pThis) != 3))
+ {
+ /* For SWSTYLE=0, write TMD2 first, then TMD1. */
+ /** @todo The DWORD containing status bits may not need to be written unless the ERR bit is set. */
+ pcnetPhysWrite(pDevIns, pThis, addr + 2 * sizeof(uint32_t), (uint32_t*)tmd + 2, sizeof(uint32_t));
+ ((uint32_t*)tmd)[1] &= ~RT_BIT(31);
+ pcnetPhysWrite(pDevIns, pThis, addr + 1 * sizeof(uint32_t), (uint32_t*)tmd + 1, sizeof(uint32_t));
+ }
+ else
+ {
+ uint32_t xda[2];
+
+ /* For SWSTYLE=3, two DWORDs can be written in one go because the OWN bit is last. */
+ xda[0] = ((uint32_t *)tmd)[2];
+ xda[1] = ((uint32_t *)tmd)[1];
+ xda[1] &= ~RT_BIT(31);
+ pcnetPhysWrite(pDevIns, pThis, addr, (void*)&xda, sizeof(xda));
+ }
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTmdStore), a);
+}
+
+/**
+ * Load receive message descriptor
+ *
+ * Make sure we read the own flag first.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis Adapter private data.
+ * @param addr Physical address of the descriptor.
+ * @param fRetIfNotOwn Return immediately after reading the own flag if we
+ * don't own the descriptor.
+ * @returns true if we own the descriptor, false otherwise
+ */
+DECLINLINE(bool) pcnetRmdLoad(PPDMDEVINS pDevIns, PPCNETSTATE pThis, RMD *rmd, RTGCPHYS32 addr, bool fRetIfNotOwn)
+{
+ uint8_t ownbyte;
+
+ if (RT_UNLIKELY(BCR_SWSTYLE(pThis) == 0))
+ {
+ uint16_t rda[4];
+ pcnetPhysRead(pDevIns, pThis, addr+3, &ownbyte, 1);
+ if (!(ownbyte & 0x80) && fRetIfNotOwn)
+ return false;
+ pcnetPhysRead(pDevIns, pThis, addr, (void*)&rda[0], sizeof(rda));
+ ((uint32_t *)rmd)[0] = (uint32_t)rda[0] | ((rda[1] & 0x00ff) << 16);
+ ((uint32_t *)rmd)[1] = (uint32_t)rda[2] | ((rda[1] & 0xff00) << 16);
+ ((uint32_t *)rmd)[2] = (uint32_t)rda[3];
+ ((uint32_t *)rmd)[3] = 0;
+ }
+ else if (RT_LIKELY(BCR_SWSTYLE(pThis) != 3))
+ {
+ pcnetPhysRead(pDevIns, pThis, addr+7, &ownbyte, 1);
+ if (!(ownbyte & 0x80) && fRetIfNotOwn)
+ return false;
+ pcnetPhysRead(pDevIns, pThis, addr, (void*)rmd, 16);
+ }
+ else
+ {
+ uint32_t rda[4];
+ pcnetPhysRead(pDevIns, pThis, addr+7, &ownbyte, 1);
+ if (!(ownbyte & 0x80) && fRetIfNotOwn)
+ return false;
+ pcnetPhysRead(pDevIns, pThis, addr, (void*)&rda[0], sizeof(rda));
+ ((uint32_t *)rmd)[0] = rda[2];
+ ((uint32_t *)rmd)[1] = rda[1];
+ ((uint32_t *)rmd)[2] = rda[0];
+ ((uint32_t *)rmd)[3] = rda[3];
+ }
+ /* Double check the own bit; guest drivers might be buggy and lock prefixes in the recompiler are ignored by other threads. */
+#ifdef DEBUG
+ if (rmd->rmd1.own == 1 && !(ownbyte & 0x80))
+ Log(("pcnetRmdLoad: own bit flipped while reading!!\n"));
+#endif
+ if (!(ownbyte & 0x80))
+ rmd->rmd1.own = 0;
+
+ return !!rmd->rmd1.own;
+}
+
+
+/**
+ * Store receive message descriptor and hand it over to the host (the VM guest).
+ * Make sure that all data are transmitted before we clear the own flag.
+ */
+DECLINLINE(void) pcnetRmdStorePassHost(PPDMDEVINS pDevIns, PPCNETSTATE pThis, RMD *rmd, RTGCPHYS32 addr)
+{
+ if (RT_UNLIKELY(BCR_SWSTYLE(pThis) == 0))
+ {
+ uint16_t rda[4];
+ rda[0] = ((uint32_t *)rmd)[0] & 0xffff;
+ rda[1] = ((((uint32_t *)rmd)[0]>>16) & 0xff) | ((((uint32_t *)rmd)[1]>>16) & 0xff00);
+ rda[2] = ((uint32_t *)rmd)[1] & 0xffff;
+ rda[3] = ((uint32_t *)rmd)[2] & 0xffff;
+ rda[1] |= 0x8000;
+ pcnetPhysWrite(pDevIns, pThis, addr, (void*)&rda[0], sizeof(rda));
+ rda[1] &= ~0x8000;
+ pcnetPhysWrite(pDevIns, pThis, addr+3, (uint8_t*)rda + 3, 1);
+ }
+ else if (RT_LIKELY(BCR_SWSTYLE(pThis) != 3))
+ {
+ ((uint32_t*)rmd)[1] |= 0x80000000;
+ pcnetPhysWrite(pDevIns, pThis, addr, (void*)rmd, 12);
+ ((uint32_t*)rmd)[1] &= ~0x80000000;
+ pcnetPhysWrite(pDevIns, pThis, addr+7, (uint8_t*)rmd + 7, 1);
+ }
+ else
+ {
+ uint32_t rda[3];
+ rda[0] = ((uint32_t *)rmd)[2];
+ rda[1] = ((uint32_t *)rmd)[1];
+ rda[2] = ((uint32_t *)rmd)[0];
+ rda[1] |= 0x80000000;
+ pcnetPhysWrite(pDevIns, pThis, addr, (void*)&rda[0], sizeof(rda));
+ rda[1] &= ~0x80000000;
+ pcnetPhysWrite(pDevIns, pThis, addr+7, (uint8_t*)rda + 7, 1);
+ }
+}
+
+#ifdef IN_RING3
+/**
+ * Read+Write a TX/RX descriptor to prevent PDMDevHlpPCIPhysWrite() allocating
+ * pages later when we shouldn't schedule to EMT. Temporarily hack.
+ */
+static void pcnetDescTouch(PPDMDEVINS pDevIns, PPCNETSTATE pThis, RTGCPHYS32 addr)
+{
+ uint8_t aBuf[16];
+ size_t cbDesc;
+ if (RT_UNLIKELY(BCR_SWSTYLE(pThis) == 0))
+ cbDesc = 8;
+ else
+ cbDesc = 16;
+ pcnetPhysRead(pDevIns, pThis, addr, aBuf, cbDesc);
+ pcnetPhysWrite(pDevIns, pThis, addr, aBuf, cbDesc);
+}
+#endif /* IN_RING3 */
+
+/** Checks if it's a bad (as in invalid) RMD.*/
+#define IS_RMD_BAD(rmd) ((rmd).rmd1.ones != 15)
+
+/** The network card is the owner of the RDTE/TDTE, actually it is this driver */
+#define CARD_IS_OWNER(desc) (((desc) & 0x8000))
+
+/** The host is the owner of the RDTE/TDTE -- actually the VM guest. */
+#define HOST_IS_OWNER(desc) (!((desc) & 0x8000))
+
+#ifndef ETHER_IS_MULTICAST /* Net/Open BSD macro it seems */
+#define ETHER_IS_MULTICAST(a) ((*(uint8_t *)(a)) & 1)
+#endif
+
+#define ETHER_ADDR_LEN ETH_ALEN
+#define ETH_ALEN 6
+#pragma pack(1)
+struct ether_header /** @todo Use RTNETETHERHDR */
+{
+ uint8_t ether_dhost[ETH_ALEN]; /**< destination ethernet address */
+ uint8_t ether_shost[ETH_ALEN]; /**< source ethernet address */
+ uint16_t ether_type; /**< packet type ID field */
+};
+#pragma pack()
+
+#define PRINT_PKTHDR(BUF) do { \
+ struct ether_header *hdr = (struct ether_header *)(BUF); \
+ Log12(("#%d packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, shost=%02x:%02x:%02x:%02x:%02x:%02x, type=%#06x (bcast=%d)\n", \
+ PCNET_INST_NR, \
+ hdr->ether_dhost[0],hdr->ether_dhost[1],hdr->ether_dhost[2], \
+ hdr->ether_dhost[3],hdr->ether_dhost[4],hdr->ether_dhost[5], \
+ hdr->ether_shost[0],hdr->ether_shost[1],hdr->ether_shost[2], \
+ hdr->ether_shost[3],hdr->ether_shost[4],hdr->ether_shost[5], \
+ htons(hdr->ether_type), \
+ !!ETHER_IS_MULTICAST(hdr->ether_dhost))); \
+} while (0)
+
+
+#define MULTICAST_FILTER_LEN 8
+
+DECLINLINE(uint32_t) lnc_mchash(const uint8_t *ether_addr)
+{
+#define LNC_POLYNOMIAL 0xEDB88320UL
+ uint32_t crc = 0xFFFFFFFF;
+ int idx, bit;
+ uint8_t data;
+
+ for (idx = 0; idx < ETHER_ADDR_LEN; idx++)
+ {
+ for (data = *ether_addr++, bit = 0; bit < MULTICAST_FILTER_LEN; bit++)
+ {
+ crc = (crc >> 1) ^ (((crc ^ data) & 1) ? LNC_POLYNOMIAL : 0);
+ data >>= 1;
+ }
+ }
+ return crc;
+#undef LNC_POLYNOMIAL
+}
+
+#define CRC(crc, ch) (crc = (crc >> 8) ^ crctab[(crc ^ (ch)) & 0xff])
+
+/* generated using the AUTODIN II polynomial
+ * x^32 + x^26 + x^23 + x^22 + x^16 +
+ * x^12 + x^11 + x^10 + x^8 + x^7 + x^5 + x^4 + x^2 + x^1 + 1
+ */
+static const uint32_t crctab[256] =
+{
+ 0x00000000, 0x77073096, 0xee0e612c, 0x990951ba,
+ 0x076dc419, 0x706af48f, 0xe963a535, 0x9e6495a3,
+ 0x0edb8832, 0x79dcb8a4, 0xe0d5e91e, 0x97d2d988,
+ 0x09b64c2b, 0x7eb17cbd, 0xe7b82d07, 0x90bf1d91,
+ 0x1db71064, 0x6ab020f2, 0xf3b97148, 0x84be41de,
+ 0x1adad47d, 0x6ddde4eb, 0xf4d4b551, 0x83d385c7,
+ 0x136c9856, 0x646ba8c0, 0xfd62f97a, 0x8a65c9ec,
+ 0x14015c4f, 0x63066cd9, 0xfa0f3d63, 0x8d080df5,
+ 0x3b6e20c8, 0x4c69105e, 0xd56041e4, 0xa2677172,
+ 0x3c03e4d1, 0x4b04d447, 0xd20d85fd, 0xa50ab56b,
+ 0x35b5a8fa, 0x42b2986c, 0xdbbbc9d6, 0xacbcf940,
+ 0x32d86ce3, 0x45df5c75, 0xdcd60dcf, 0xabd13d59,
+ 0x26d930ac, 0x51de003a, 0xc8d75180, 0xbfd06116,
+ 0x21b4f4b5, 0x56b3c423, 0xcfba9599, 0xb8bda50f,
+ 0x2802b89e, 0x5f058808, 0xc60cd9b2, 0xb10be924,
+ 0x2f6f7c87, 0x58684c11, 0xc1611dab, 0xb6662d3d,
+ 0x76dc4190, 0x01db7106, 0x98d220bc, 0xefd5102a,
+ 0x71b18589, 0x06b6b51f, 0x9fbfe4a5, 0xe8b8d433,
+ 0x7807c9a2, 0x0f00f934, 0x9609a88e, 0xe10e9818,
+ 0x7f6a0dbb, 0x086d3d2d, 0x91646c97, 0xe6635c01,
+ 0x6b6b51f4, 0x1c6c6162, 0x856530d8, 0xf262004e,
+ 0x6c0695ed, 0x1b01a57b, 0x8208f4c1, 0xf50fc457,
+ 0x65b0d9c6, 0x12b7e950, 0x8bbeb8ea, 0xfcb9887c,
+ 0x62dd1ddf, 0x15da2d49, 0x8cd37cf3, 0xfbd44c65,
+ 0x4db26158, 0x3ab551ce, 0xa3bc0074, 0xd4bb30e2,
+ 0x4adfa541, 0x3dd895d7, 0xa4d1c46d, 0xd3d6f4fb,
+ 0x4369e96a, 0x346ed9fc, 0xad678846, 0xda60b8d0,
+ 0x44042d73, 0x33031de5, 0xaa0a4c5f, 0xdd0d7cc9,
+ 0x5005713c, 0x270241aa, 0xbe0b1010, 0xc90c2086,
+ 0x5768b525, 0x206f85b3, 0xb966d409, 0xce61e49f,
+ 0x5edef90e, 0x29d9c998, 0xb0d09822, 0xc7d7a8b4,
+ 0x59b33d17, 0x2eb40d81, 0xb7bd5c3b, 0xc0ba6cad,
+ 0xedb88320, 0x9abfb3b6, 0x03b6e20c, 0x74b1d29a,
+ 0xead54739, 0x9dd277af, 0x04db2615, 0x73dc1683,
+ 0xe3630b12, 0x94643b84, 0x0d6d6a3e, 0x7a6a5aa8,
+ 0xe40ecf0b, 0x9309ff9d, 0x0a00ae27, 0x7d079eb1,
+ 0xf00f9344, 0x8708a3d2, 0x1e01f268, 0x6906c2fe,
+ 0xf762575d, 0x806567cb, 0x196c3671, 0x6e6b06e7,
+ 0xfed41b76, 0x89d32be0, 0x10da7a5a, 0x67dd4acc,
+ 0xf9b9df6f, 0x8ebeeff9, 0x17b7be43, 0x60b08ed5,
+ 0xd6d6a3e8, 0xa1d1937e, 0x38d8c2c4, 0x4fdff252,
+ 0xd1bb67f1, 0xa6bc5767, 0x3fb506dd, 0x48b2364b,
+ 0xd80d2bda, 0xaf0a1b4c, 0x36034af6, 0x41047a60,
+ 0xdf60efc3, 0xa867df55, 0x316e8eef, 0x4669be79,
+ 0xcb61b38c, 0xbc66831a, 0x256fd2a0, 0x5268e236,
+ 0xcc0c7795, 0xbb0b4703, 0x220216b9, 0x5505262f,
+ 0xc5ba3bbe, 0xb2bd0b28, 0x2bb45a92, 0x5cb36a04,
+ 0xc2d7ffa7, 0xb5d0cf31, 0x2cd99e8b, 0x5bdeae1d,
+ 0x9b64c2b0, 0xec63f226, 0x756aa39c, 0x026d930a,
+ 0x9c0906a9, 0xeb0e363f, 0x72076785, 0x05005713,
+ 0x95bf4a82, 0xe2b87a14, 0x7bb12bae, 0x0cb61b38,
+ 0x92d28e9b, 0xe5d5be0d, 0x7cdcefb7, 0x0bdbdf21,
+ 0x86d3d2d4, 0xf1d4e242, 0x68ddb3f8, 0x1fda836e,
+ 0x81be16cd, 0xf6b9265b, 0x6fb077e1, 0x18b74777,
+ 0x88085ae6, 0xff0f6a70, 0x66063bca, 0x11010b5c,
+ 0x8f659eff, 0xf862ae69, 0x616bffd3, 0x166ccf45,
+ 0xa00ae278, 0xd70dd2ee, 0x4e048354, 0x3903b3c2,
+ 0xa7672661, 0xd06016f7, 0x4969474d, 0x3e6e77db,
+ 0xaed16a4a, 0xd9d65adc, 0x40df0b66, 0x37d83bf0,
+ 0xa9bcae53, 0xdebb9ec5, 0x47b2cf7f, 0x30b5ffe9,
+ 0xbdbdf21c, 0xcabac28a, 0x53b39330, 0x24b4a3a6,
+ 0xbad03605, 0xcdd70693, 0x54de5729, 0x23d967bf,
+ 0xb3667a2e, 0xc4614ab8, 0x5d681b02, 0x2a6f2b94,
+ 0xb40bbe37, 0xc30c8ea1, 0x5a05df1b, 0x2d02ef8d,
+};
+
+DECLINLINE(int) padr_match(PPCNETSTATE pThis, const uint8_t *buf, size_t size)
+{
+ struct ether_header *hdr = (struct ether_header *)buf;
+ int result;
+#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
+ result = !CSR_DRCVPA(pThis) && !memcmp(hdr->ether_dhost, pThis->aCSR + 12, 6);
+#else
+ uint8_t padr[6];
+ padr[0] = pThis->aCSR[12] & 0xff;
+ padr[1] = pThis->aCSR[12] >> 8;
+ padr[2] = pThis->aCSR[13] & 0xff;
+ padr[3] = pThis->aCSR[13] >> 8;
+ padr[4] = pThis->aCSR[14] & 0xff;
+ padr[5] = pThis->aCSR[14] >> 8;
+ result = !CSR_DRCVPA(pThis) && !memcmp(hdr->ether_dhost, padr, 6);
+#endif
+
+ Log11(("#%d packet dhost=%02x:%02x:%02x:%02x:%02x:%02x, padr=%02x:%02x:%02x:%02x:%02x:%02x => %d\n", PCNET_INST_NR,
+ hdr->ether_dhost[0],hdr->ether_dhost[1],hdr->ether_dhost[2],
+ hdr->ether_dhost[3],hdr->ether_dhost[4],hdr->ether_dhost[5],
+ pThis->aCSR[12] & 0xff, pThis->aCSR[12] >> 8,
+ pThis->aCSR[13] & 0xff, pThis->aCSR[13] >> 8,
+ pThis->aCSR[14] & 0xff, pThis->aCSR[14] >> 8, result));
+ RT_NOREF_PV(size);
+ return result;
+}
+
+DECLINLINE(int) padr_bcast(PPCNETSTATE pThis, const uint8_t *buf, size_t size)
+{
+ static uint8_t aBCAST[6] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff };
+ struct ether_header *hdr = (struct ether_header *)buf;
+ int result = !CSR_DRCVBC(pThis) && !memcmp(hdr->ether_dhost, aBCAST, 6);
+ Log11(("#%d padr_bcast result=%d\n", PCNET_INST_NR, result));
+ RT_NOREF_PV(size);
+ return result;
+}
+
+static int ladr_match(PPCNETSTATE pThis, const uint8_t *buf, size_t size)
+{
+ struct ether_header *hdr = (struct ether_header *)buf;
+ if (RT_UNLIKELY(hdr->ether_dhost[0] & 0x01) && ((uint64_t *)&pThis->aCSR[8])[0] != 0LL)
+ {
+ int index;
+#if defined(RT_ARCH_X86) || defined(RT_ARCH_AMD64)
+ index = lnc_mchash(hdr->ether_dhost) >> 26;
+ return ((uint8_t*)(pThis->aCSR + 8))[index >> 3] & (1 << (index & 7));
+#else
+ uint8_t ladr[8];
+ ladr[0] = pThis->aCSR[8] & 0xff;
+ ladr[1] = pThis->aCSR[8] >> 8;
+ ladr[2] = pThis->aCSR[9] & 0xff;
+ ladr[3] = pThis->aCSR[9] >> 8;
+ ladr[4] = pThis->aCSR[10] & 0xff;
+ ladr[5] = pThis->aCSR[10] >> 8;
+ ladr[6] = pThis->aCSR[11] & 0xff;
+ ladr[7] = pThis->aCSR[11] >> 8;
+ index = lnc_mchash(hdr->ether_dhost) >> 26;
+ return (ladr[index >> 3] & (1 << (index & 7)));
+#endif
+ }
+ RT_NOREF_PV(size);
+ return 0;
+}
+
+
+/**
+ * Get the receive descriptor ring address with a given index.
+ */
+DECLINLINE(RTGCPHYS32) pcnetRdraAddr(PPCNETSTATE pThis, int idx)
+{
+ return pThis->GCRDRA + ((CSR_RCVRL(pThis) - idx) << pThis->iLog2DescSize);
+}
+
+/**
+ * Get the transmit descriptor ring address with a given index.
+ */
+DECLINLINE(RTGCPHYS32) pcnetTdraAddr(PPCNETSTATE pThis, int idx)
+{
+ return pThis->GCTDRA + ((CSR_XMTRL(pThis) - idx) << pThis->iLog2DescSize);
+}
+
+
+#undef htonl
+#define htonl(x) ASMByteSwapU32(x)
+#undef htons
+#define htons(x) ( (((x) & 0xff00) >> 8) | (((x) & 0x00ff) << 8) )
+
+
+static void pcnetSoftReset(PPCNETSTATE pThis)
+{
+ Log(("#%d pcnetSoftReset:\n", PCNET_INST_NR));
+
+ pThis->u32Lnkst = 0x40;
+ pThis->GCRDRA = 0;
+ pThis->GCTDRA = 0;
+ pThis->u32RAP = 0;
+
+ pThis->aCSR[0] = 0x0004;
+ pThis->aCSR[3] = 0x0000;
+ pThis->aCSR[4] = 0x0115;
+ pThis->aCSR[5] = 0x0000;
+ pThis->aCSR[6] = 0x0000;
+ pThis->aCSR[8] = 0;
+ pThis->aCSR[9] = 0;
+ pThis->aCSR[10] = 0;
+ pThis->aCSR[11] = 0;
+ pThis->aCSR[12] = RT_LE2H_U16(((uint16_t *)&pThis->aPROM[0])[0]);
+ pThis->aCSR[13] = RT_LE2H_U16(((uint16_t *)&pThis->aPROM[0])[1]);
+ pThis->aCSR[14] = RT_LE2H_U16(((uint16_t *)&pThis->aPROM[0])[2]);
+ pThis->aCSR[15] &= 0x21c4;
+ CSR_RCVRC(pThis) = 1;
+ CSR_XMTRC(pThis) = 1;
+ CSR_RCVRL(pThis) = 1;
+ CSR_XMTRL(pThis) = 1;
+ pThis->aCSR[80] = 0x1410;
+ switch (pThis->uDevType)
+ {
+ default:
+ case DEV_AM79C970A:
+ pThis->aCSR[88] = CSR_VERSION_LOW_79C970A;
+ pThis->aCSR[89] = CSR_VERSION_HIGH;
+ break;
+ case DEV_AM79C973:
+ pThis->aCSR[88] = CSR_VERSION_LOW_79C973;
+ pThis->aCSR[89] = CSR_VERSION_HIGH;
+ break;
+ case DEV_AM79C960:
+ case DEV_AM79C960_EB:
+ pThis->aCSR[88] = CSR_VERSION_LOW_79C960;
+ pThis->aCSR[89] = 0x0000;
+ break;
+ }
+ pThis->aCSR[94] = 0x0000;
+ pThis->aCSR[100] = 0x0200;
+ pThis->aCSR[103] = 0x0105;
+ CSR_MISSC(pThis) = 0;
+ pThis->aCSR[114] = 0x0000;
+ pThis->aCSR[122] = 0x0000;
+ pThis->aCSR[124] = 0x0000;
+}
+
+/**
+ * Check if we have to send an interrupt to the guest. An interrupt can occur on
+ * - csr0 (written quite often)
+ * - csr4 (only written by pcnetSoftReset(), pcnetStop() or by the guest driver)
+ * - csr5 (only written by pcnetSoftReset(), pcnetStop or by the driver guest)
+ */
+static void pcnetUpdateIrq(PPDMDEVINS pDevIns, PPCNETSTATE pThis)
+{
+ int iISR = 0;
+ uint16_t csr0 = pThis->aCSR[0];
+
+ csr0 &= ~0x0080; /* clear INTR */
+
+ STAM_PROFILE_ADV_START(&pThis->StatInterrupt, a);
+
+ /* Linux guests set csr4=0x0915
+ * W2k guests set csr3=0x4940 (disable BABL, MERR, IDON, DXSUFLO */
+
+#if 1
+ if ( ( (csr0 & ~pThis->aCSR[3]) & 0x5f00)
+ || (((pThis->aCSR[4]>>1) & ~pThis->aCSR[4]) & 0x0115)
+ || (((pThis->aCSR[5]>>1) & pThis->aCSR[5]) & 0x0048))
+#else
+ if ( ( !(pThis->aCSR[3] & 0x4000) && !!(csr0 & 0x4000)) /* BABL */
+ ||( !(pThis->aCSR[3] & 0x1000) && !!(csr0 & 0x1000)) /* MISS */
+ ||( !(pThis->aCSR[3] & 0x0100) && !!(csr0 & 0x0100)) /* IDON */
+ ||( !(pThis->aCSR[3] & 0x0200) && !!(csr0 & 0x0200)) /* TINT */
+ ||( !(pThis->aCSR[3] & 0x0400) && !!(csr0 & 0x0400)) /* RINT */
+ ||( !(pThis->aCSR[3] & 0x0800) && !!(csr0 & 0x0800)) /* MERR */
+ ||( !(pThis->aCSR[4] & 0x0001) && !!(pThis->aCSR[4] & 0x0002)) /* JAB */
+ ||( !(pThis->aCSR[4] & 0x0004) && !!(pThis->aCSR[4] & 0x0008)) /* TXSTRT */
+ ||( !(pThis->aCSR[4] & 0x0010) && !!(pThis->aCSR[4] & 0x0020)) /* RCVO */
+ ||( !(pThis->aCSR[4] & 0x0100) && !!(pThis->aCSR[4] & 0x0200)) /* MFCO */
+ ||(!!(pThis->aCSR[5] & 0x0040) && !!(pThis->aCSR[5] & 0x0080)) /* EXDINT */
+ ||(!!(pThis->aCSR[5] & 0x0008) && !!(pThis->aCSR[5] & 0x0010)) /* MPINT */)
+#endif
+ {
+ iISR = !!(csr0 & 0x0040); /* CSR_INEA */
+ csr0 |= 0x0080; /* set INTR */
+ }
+
+#ifdef VBOX
+ if (pThis->aCSR[4] & 0x0080) /* UINTCMD */
+ {
+ pThis->aCSR[4] &= ~0x0080; /* clear UINTCMD */
+ pThis->aCSR[4] |= 0x0040; /* set UINT */
+ Log(("#%d user int\n", PCNET_INST_NR));
+ }
+ if (pThis->aCSR[4] & csr0 & 0x0040 /* CSR_INEA */)
+ {
+ csr0 |= 0x0080; /* set INTR */
+ iISR = 1;
+ }
+#else /* !VBOX */
+ if (!!(pThis->aCSR[4] & 0x0080) && CSR_INEA(pThis)) /* UINTCMD */
+ {
+ pThis->aCSR[4] &= ~0x0080;
+ pThis->aCSR[4] |= 0x0040; /* set UINT */
+ csr0 |= 0x0080; /* set INTR */
+ iISR = 1;
+ Log(("#%d user int\n", PCNET_INST_NR));
+ }
+#endif /* !VBOX */
+
+#if 1
+ if (((pThis->aCSR[5]>>1) & pThis->aCSR[5]) & 0x0500)
+#else
+ if ( (!!(pThis->aCSR[5] & 0x0400) && !!(pThis->aCSR[5] & 0x0800)) /* SINT */
+ ||(!!(pThis->aCSR[5] & 0x0100) && !!(pThis->aCSR[5] & 0x0200)) /* SLPINT */)
+#endif
+ {
+ iISR = 1;
+ csr0 |= 0x0080; /* INTR */
+ }
+
+ if ((pThis->aCSR[7] & 0x0C00) == 0x0C00) /* STINT + STINTE */
+ iISR = 1;
+
+ pThis->aCSR[0] = csr0;
+
+ Log2(("#%d set irq iISR=%d\n", PCNET_INST_NR, iISR));
+
+ /* normal path is to _not_ change the IRQ status */
+ if (RT_UNLIKELY(iISR != pThis->iISR))
+ {
+ if (!PCNET_IS_ISA(pThis))
+ {
+ Log(("#%d INTA=%d\n", PCNET_INST_NR, iISR));
+ PDMDevHlpPCISetIrq(pDevIns, 0, iISR);
+ }
+ else
+ {
+ Log(("#%d IRQ=%d, state=%d\n", PCNET_INST_NR, pThis->uIsaIrq, iISR));
+ PDMDevHlpISASetIrq(pDevIns, pThis->uIsaIrq, iISR);
+ }
+ pThis->iISR = iISR;
+ }
+ STAM_PROFILE_ADV_STOP(&pThis->StatInterrupt, a);
+}
+
+#ifdef IN_RING3
+
+static void pcnetR3Init(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC)
+{
+ Log(("#%d pcnetR3Init: init_addr=%#010x\n", PCNET_INST_NR, PHYSADDR(pThis, CSR_IADR(pThis))));
+
+ /* If intialization was invoked with PCI bus mastering disabled, it's not going to
+ * go very well. Better report an error.
+ */
+ if (PCNET_IS_PCI(pThis))
+ {
+ PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
+ uint8_t uCmd = PDMPciDevGetByte(pPciDev, 0x04);
+
+ if (!(uCmd & 4))
+ {
+ pThis->aCSR[0] |= 0x0801; /* Set the MERR bit instead of IDON. */
+ LogRel(("PCnet#%d: Warning: Initialization failed due to disabled PCI bus mastering.\n", PCNET_INST_NR));
+ return;
+ }
+ }
+
+ /** @todo Documentation says that RCVRL and XMTRL are stored as two's complement!
+ * Software is allowed to write these registers directly. */
+# define PCNET_INIT() do { \
+ pcnetPhysRead(pDevIns, pThis, PHYSADDR(pThis, CSR_IADR(pThis)), \
+ (uint8_t *)&initblk, sizeof(initblk)); \
+ pThis->aCSR[15] = RT_LE2H_U16(initblk.mode); \
+ CSR_RCVRL(pThis) = (initblk.rlen < 9) ? (1 << initblk.rlen) : 512; \
+ CSR_XMTRL(pThis) = (initblk.tlen < 9) ? (1 << initblk.tlen) : 512; \
+ pThis->aCSR[ 6] = (initblk.tlen << 12) | (initblk.rlen << 8); \
+ pThis->aCSR[ 8] = RT_LE2H_U16(initblk.ladrf1); \
+ pThis->aCSR[ 9] = RT_LE2H_U16(initblk.ladrf2); \
+ pThis->aCSR[10] = RT_LE2H_U16(initblk.ladrf3); \
+ pThis->aCSR[11] = RT_LE2H_U16(initblk.ladrf4); \
+ pThis->aCSR[12] = RT_LE2H_U16(initblk.padr1); \
+ pThis->aCSR[13] = RT_LE2H_U16(initblk.padr2); \
+ pThis->aCSR[14] = RT_LE2H_U16(initblk.padr3); \
+ pThis->GCRDRA = PHYSADDR(pThis, initblk.rdra); \
+ pThis->GCTDRA = PHYSADDR(pThis, initblk.tdra); \
+ } while (0)
+
+ if (BCR_SSIZE32(pThis))
+ {
+ struct INITBLK32 initblk;
+ pThis->GCUpperPhys = 0;
+ PCNET_INIT();
+ Log(("#%d initblk.rlen=%#04x, initblk.tlen=%#04x\n",
+ PCNET_INST_NR, initblk.rlen, initblk.tlen));
+ }
+ else
+ {
+ struct INITBLK16 initblk;
+ pThis->GCUpperPhys = (0xff00 & (uint32_t)pThis->aCSR[2]) << 16;
+ PCNET_INIT();
+ Log(("#%d initblk.rlen=%#04x, initblk.tlen=%#04x\n",
+ PCNET_INST_NR, initblk.rlen, initblk.tlen));
+ }
+
+# undef PCNET_INIT
+
+ size_t cbRxBuffers = 0;
+ for (int i = CSR_RCVRL(pThis); i >= 1; i--)
+ {
+ RMD rmd;
+ RTGCPHYS32 rdaddr = PHYSADDR(pThis, pcnetRdraAddr(pThis, i));
+
+ pcnetDescTouch(pDevIns, pThis, rdaddr);
+ /* At this time it is not guaranteed that the buffers are already initialized. */
+ if (pcnetRmdLoad(pDevIns, pThis, &rmd, rdaddr, false))
+ {
+ uint32_t cbBuf = 4096U-rmd.rmd1.bcnt;
+ cbRxBuffers += cbBuf;
+ }
+ }
+
+ for (int i = CSR_XMTRL(pThis); i >= 1; i--)
+ {
+ RTGCPHYS32 tdaddr = PHYSADDR(pThis, pcnetTdraAddr(pThis, i));
+
+ pcnetDescTouch(pDevIns, pThis, tdaddr);
+ }
+
+ /*
+ * Heuristics: The Solaris pcn driver allocates too few RX buffers (128 buffers of a
+ * size of 128 bytes are 16KB in summary) leading to frequent RX buffer overflows. In
+ * that case we don't signal RX overflows through the CSR0_MISS flag as the driver
+ * re-initializes the device on every miss. Other guests use at least 32 buffers of
+ * usually 1536 bytes and should therefore not run into condition. If they are still
+ * short in RX buffers we notify this condition.
+ */
+ pThis->fSignalRxMiss = (cbRxBuffers == 0 || cbRxBuffers >= 32*_1K);
+
+ if (pThisCC->pDrv)
+ pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, CSR_PROM(pThis));
+
+ CSR_RCVRC(pThis) = CSR_RCVRL(pThis);
+ CSR_XMTRC(pThis) = CSR_XMTRL(pThis);
+
+ /* Reset cached RX and TX states */
+ CSR_CRST(pThis) = CSR_CRBC(pThis) = CSR_NRST(pThis) = CSR_NRBC(pThis) = 0;
+ CSR_CXST(pThis) = CSR_CXBC(pThis) = CSR_NXST(pThis) = CSR_NXBC(pThis) = 0;
+
+ LogRel(("PCnet#%d: Init: SWSTYLE=%d GCRDRA=%#010x[%d] GCTDRA=%#010x[%d]%s\n",
+ PCNET_INST_NR, BCR_SWSTYLE(pThis),
+ pThis->GCRDRA, CSR_RCVRL(pThis), pThis->GCTDRA, CSR_XMTRL(pThis),
+ !pThis->fSignalRxMiss ? " (CSR0_MISS disabled)" : ""));
+
+ if (pThis->GCRDRA & (pThis->iLog2DescSize - 1))
+ LogRel(("PCnet#%d: Warning: Misaligned RDRA\n", PCNET_INST_NR));
+ if (pThis->GCTDRA & (pThis->iLog2DescSize - 1))
+ LogRel(("PCnet#%d: Warning: Misaligned TDRA\n", PCNET_INST_NR));
+
+ pThis->aCSR[0] |= 0x0101; /* Initialization done */
+ pThis->aCSR[0] &= ~0x0004; /* clear STOP bit */
+}
+
+#endif /* IN_RING3 */
+
+/**
+ * Start RX/TX operation.
+ */
+static void pcnetStart(PPDMDEVINS pDevIns, PPCNETSTATE pThis)
+{
+ Log(("#%d pcnetStart:\n", PCNET_INST_NR));
+
+ /* Reset any cached RX/TX descriptor state. */
+ CSR_CRDA(pThis) = CSR_CRBA(pThis) = CSR_NRDA(pThis) = CSR_NRBA(pThis) = 0;
+ CSR_CRBC(pThis) = CSR_NRBC(pThis) = CSR_CRST(pThis) = 0;
+
+ if (!CSR_DTX(pThis))
+ pThis->aCSR[0] |= 0x0010; /* set TXON */
+ if (!CSR_DRX(pThis))
+ pThis->aCSR[0] |= 0x0020; /* set RXON */
+ pThis->aCSR[0] &= ~0x0004; /* clear STOP bit */
+ pThis->aCSR[0] |= 0x0002; /* STRT */
+
+ pcnetPollTimerStart(pDevIns, pThis); /* start timer if it was stopped */
+}
+
+/**
+ * Stop RX/TX operation.
+ */
+static void pcnetStop(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC)
+{
+ Log(("#%d pcnetStop:\n", PCNET_INST_NR));
+ pThis->aCSR[0] = 0x0004;
+ pThis->aCSR[4] &= ~0x02c2;
+ pThis->aCSR[5] &= ~0x0011;
+ pcnetPollTimer(pDevIns, pThis, pThisCC);
+}
+
+/**
+ * Wakes up a receive thread stuck waiting for buffers.
+ */
+static void pcnetWakeupReceive(PPDMDEVINS pDevIns)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ STAM_COUNTER_INC(&pThis->StatRxOverflowWakeup);
+ if (pThis->hEventOutOfRxSpace != NIL_SUPSEMEVENT)
+ {
+ int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventOutOfRxSpace);
+ AssertRC(rc);
+ }
+}
+
+/**
+ * Poll Receive Descriptor Table Entry and cache the results in the appropriate registers.
+ *
+ * @note Once a descriptor belongs to the network card (this driver), it
+ * cannot be changed by the host (the guest driver) anymore. Well, it
+ * could but the results are undefined by definition.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The shared PCnet state data.
+ * @param fSkipCurrent if true, don't scan the current RDTE.
+ */
+static void pcnetRdtePoll(PPDMDEVINS pDevIns, PPCNETSTATE pThis, bool fSkipCurrent=false)
+{
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatRdtePoll), a);
+ /* assume lack of a next receive descriptor */
+ CSR_NRST(pThis) = 0;
+
+ if (RT_LIKELY(pThis->GCRDRA))
+ {
+ /*
+ * The current receive message descriptor.
+ */
+ RMD rmd;
+ int i = CSR_RCVRC(pThis);
+ RTGCPHYS32 addr;
+
+ if (i < 1)
+ i = CSR_RCVRL(pThis);
+
+ if (!fSkipCurrent)
+ {
+ addr = pcnetRdraAddr(pThis, i);
+ CSR_CRDA(pThis) = CSR_CRBA(pThis) = 0;
+ CSR_CRBC(pThis) = CSR_CRST(pThis) = 0;
+ if (!pcnetRmdLoad(pDevIns, pThis, &rmd, PHYSADDR(pThis, addr), true))
+ {
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatRdtePoll), a);
+ return;
+ }
+ if (RT_LIKELY(!IS_RMD_BAD(rmd)))
+ {
+ CSR_CRDA(pThis) = addr; /* Receive Descriptor Address */
+ CSR_CRBA(pThis) = rmd.rmd0.rbadr; /* Receive Buffer Address */
+ CSR_CRBC(pThis) = rmd.rmd1.bcnt; /* Receive Byte Count */
+ CSR_CRST(pThis) = ((uint32_t *)&rmd)[1] >> 16; /* Receive Status */
+ if (pThis->fMaybeOutOfSpace)
+ pcnetWakeupReceive(pDevIns);
+ }
+ else
+ {
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatRdtePoll), a);
+ /* This is not problematic since we don't own the descriptor
+ * We actually do own it, otherwise pcnetRmdLoad would have returned false.
+ * Don't flood the release log with errors.
+ */
+ if (++pThis->uCntBadRMD < 50)
+ LogRel(("PCnet#%d: BAD RMD ENTRIES AT %#010x (i=%d)\n",
+ PCNET_INST_NR, addr, i));
+ return;
+ }
+ }
+
+ /*
+ * The next descriptor.
+ */
+ if (--i < 1)
+ i = CSR_RCVRL(pThis);
+ addr = pcnetRdraAddr(pThis, i);
+ CSR_NRDA(pThis) = CSR_NRBA(pThis) = 0;
+ CSR_NRBC(pThis) = 0;
+ if (!pcnetRmdLoad(pDevIns, pThis, &rmd, PHYSADDR(pThis, addr), true))
+ {
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatRdtePoll), a);
+ return;
+ }
+ if (RT_LIKELY(!IS_RMD_BAD(rmd)))
+ {
+ CSR_NRDA(pThis) = addr; /* Receive Descriptor Address */
+ CSR_NRBA(pThis) = rmd.rmd0.rbadr; /* Receive Buffer Address */
+ CSR_NRBC(pThis) = rmd.rmd1.bcnt; /* Receive Byte Count */
+ CSR_NRST(pThis) = ((uint32_t *)&rmd)[1] >> 16; /* Receive Status */
+ }
+ else
+ {
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatRdtePoll), a);
+ /* This is not problematic since we don't own the descriptor
+ * We actually do own it, otherwise pcnetRmdLoad would have returned false.
+ * Don't flood the release log with errors.
+ */
+ if (++pThis->uCntBadRMD < 50)
+ LogRel(("PCnet#%d: BAD RMD ENTRIES + AT %#010x (i=%d)\n",
+ PCNET_INST_NR, addr, i));
+ return;
+ }
+
+ /**
+ * @todo NNRD
+ */
+ }
+ else
+ {
+ CSR_CRDA(pThis) = CSR_CRBA(pThis) = CSR_NRDA(pThis) = CSR_NRBA(pThis) = 0;
+ CSR_CRBC(pThis) = CSR_NRBC(pThis) = CSR_CRST(pThis) = 0;
+ }
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatRdtePoll), a);
+}
+
+/**
+ * Poll Transmit Descriptor Table Entry
+ * @return true if transmit descriptors available
+ */
+static int pcnetTdtePoll(PPDMDEVINS pDevIns, PPCNETSTATE pThis, TMD *tmd)
+{
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatTdtePoll), a);
+ if (RT_LIKELY(pThis->GCTDRA))
+ {
+ RTGCPHYS32 cxda = pcnetTdraAddr(pThis, CSR_XMTRC(pThis));
+
+ if (!pcnetTmdTryLoad(pDevIns, pThis, tmd, PHYSADDR(pThis, cxda)))
+ {
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTdtePoll), a);
+ return 0;
+ }
+
+ if (RT_UNLIKELY(tmd->tmd1.ones != 15))
+ {
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTdtePoll), a);
+ LogRel(("PCnet#%d: BAD TMD XDA=%#010x\n",
+ PCNET_INST_NR, PHYSADDR(pThis, cxda)));
+ return 0;
+ }
+
+ /* previous xmit descriptor */
+ CSR_PXDA(pThis) = CSR_CXDA(pThis);
+ CSR_PXBC(pThis) = CSR_CXBC(pThis);
+ CSR_PXST(pThis) = CSR_CXST(pThis);
+
+ /* set current transmit descriptor. */
+ CSR_CXDA(pThis) = cxda;
+ CSR_CXBC(pThis) = tmd->tmd1.bcnt;
+ CSR_CXST(pThis) = ((uint32_t *)tmd)[1] >> 16;
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTdtePoll), a);
+ return CARD_IS_OWNER(CSR_CXST(pThis));
+ }
+ else
+ {
+ /** @todo consistency with previous receive descriptor */
+ CSR_CXDA(pThis) = 0;
+ CSR_CXBC(pThis) = CSR_CXST(pThis) = 0;
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTdtePoll), a);
+ return 0;
+ }
+}
+
+
+/**
+ * Write data into guest receive buffers.
+ */
+static void pcnetReceiveNoSync(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC,
+ const uint8_t *buf, size_t cbToRecv, bool fAddFCS, bool fLoopback)
+{
+ int is_padr = 0;
+ int is_bcast = 0;
+ int is_ladr = 0;
+ unsigned iRxDesc;
+ int cbPacket;
+
+ if (RT_UNLIKELY(CSR_DRX(pThis) || CSR_STOP(pThis) || CSR_SPND(pThis) || !cbToRecv))
+ return;
+
+ /*
+ * Drop packets if the VM is not running yet/anymore.
+ */
+ VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
+ if ( enmVMState != VMSTATE_RUNNING
+ && enmVMState != VMSTATE_RUNNING_LS)
+ return;
+
+ /*
+ * Drop packets if the cable is not connected
+ */
+ if (!pcnetIsLinkUp(pThis))
+ return;
+
+ Log(("#%d pcnetReceiveNoSync: size=%d\n", PCNET_INST_NR, cbToRecv));
+
+ /*
+ * Perform address matching.
+ */
+ if ( CSR_PROM(pThis)
+ || (is_padr = padr_match(pThis, buf, cbToRecv))
+ || (is_bcast = padr_bcast(pThis, buf, cbToRecv))
+ || (is_ladr = ladr_match(pThis, buf, cbToRecv)))
+ {
+ if (HOST_IS_OWNER(CSR_CRST(pThis)))
+ pcnetRdtePoll(pDevIns, pThis);
+ if (RT_UNLIKELY(HOST_IS_OWNER(CSR_CRST(pThis))))
+ {
+ /* Not owned by controller. This should not be possible as
+ * we already called pcnetR3CanReceive(). */
+ LogRel(("PCnet#%d: no buffer: RCVRC=%d\n", PCNET_INST_NR, CSR_RCVRC(pThis)));
+ /* Dump the status of all RX descriptors */
+ const unsigned cb = 1 << pThis->iLog2DescSize;
+ RTGCPHYS32 GCPhys = pThis->GCRDRA;
+ iRxDesc = CSR_RCVRL(pThis);
+ while (iRxDesc-- > 0)
+ {
+ RMD rmd;
+ pcnetRmdLoad(pDevIns, pThis, &rmd, PHYSADDR(pThis, GCPhys), false);
+ LogRel((" %#010x\n", rmd.rmd1));
+ GCPhys += cb;
+ }
+ pThis->aCSR[0] |= 0x1000; /* Set MISS flag */
+ CSR_MISSC(pThis)++;
+ }
+ else
+ {
+ PCRTNETETHERHDR pEth = (PCRTNETETHERHDR)buf;
+ bool fStrip = false;
+ size_t len_802_3;
+ uint8_t *src = &pThis->abRecvBuf[8];
+ RTGCPHYS32 crda = CSR_CRDA(pThis);
+ RTGCPHYS32 next_crda;
+ RMD rmd, next_rmd;
+
+ /*
+ * Ethernet framing considers these two octets to be
+ * payload type; 802.3 framing considers them to be
+ * payload length. IEEE 802.3x-1997 restricts Ethernet
+ * type to be greater than or equal to 1536 (0x0600), so
+ * that both framings can coexist on the wire.
+ *
+ * NB: CSR_ASTRP_RCV bit affects only 802.3 frames!
+ */
+ len_802_3 = RT_BE2H_U16(pEth->EtherType);
+ if (len_802_3 < 46 && CSR_ASTRP_RCV(pThis))
+ {
+ cbToRecv = RT_MIN(sizeof(RTNETETHERHDR) + len_802_3, cbToRecv);
+ fStrip = true;
+ fAddFCS = false;
+ }
+
+ memcpy(src, buf, cbToRecv);
+
+ if (!fStrip) {
+ /* In loopback mode, Runt Packed Accept is always enabled internally;
+ * don't do any padding because guest may be looping back very short packets.
+ */
+ if (!fLoopback)
+ while (cbToRecv < 60)
+ src[cbToRecv++] = 0;
+
+ if (fAddFCS)
+ {
+ uint32_t fcs = UINT32_MAX;
+ uint8_t *p = src;
+
+ while (p != &src[cbToRecv])
+ CRC(fcs, *p++);
+
+ /* FCS at the end of the packet */
+ ((uint32_t *)&src[cbToRecv])[0] = htonl(fcs);
+ cbToRecv += 4;
+ }
+ }
+
+ cbPacket = (int)cbToRecv; Assert((size_t)cbPacket == cbToRecv);
+
+#ifdef LOG_ENABLED
+ PRINT_PKTHDR(buf);
+#endif
+
+ pcnetRmdLoad(pDevIns, pThis, &rmd, PHYSADDR(pThis, crda), false);
+ /*if (!CSR_LAPPEN(pThis))*/
+ rmd.rmd1.stp = 1;
+
+ size_t cbBuf = RT_MIN(4096 - (size_t)rmd.rmd1.bcnt, cbToRecv);
+ RTGCPHYS32 rbadr = PHYSADDR(pThis, rmd.rmd0.rbadr);
+
+ /* save the old value to check if it was changed as long as we didn't
+ * hold the critical section */
+ iRxDesc = CSR_RCVRC(pThis);
+
+ /* We have to leave the critical section here or we risk deadlocking
+ * with EMT when the write is to an unallocated page or has an access
+ * handler associated with it.
+ *
+ * This shouldn't be a problem because:
+ * - any modification to the RX descriptor by the driver is
+ * forbidden as long as it is owned by the device
+ * - we don't cache any register state beyond this point
+ */
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ pcnetPhysWrite(pDevIns, pThis, rbadr, src, cbBuf);
+ int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc);
+
+ /* RX disabled in the meantime? If so, abort RX. */
+ if (RT_UNLIKELY(CSR_DRX(pThis) || CSR_STOP(pThis) || CSR_SPND(pThis)))
+ return;
+
+ /* Was the register modified in the meantime? If so, don't touch the
+ * register but still update the RX descriptor. */
+ if (RT_LIKELY(iRxDesc == CSR_RCVRC(pThis)))
+ {
+ if (iRxDesc-- < 2)
+ iRxDesc = CSR_RCVRL(pThis);
+ CSR_RCVRC(pThis) = iRxDesc;
+ }
+ else
+ iRxDesc = CSR_RCVRC(pThis);
+
+ src += cbBuf;
+ cbToRecv -= cbBuf;
+
+ while (cbToRecv > 0)
+ {
+ /* Read the entire next descriptor as we're likely to need it. */
+ next_crda = pcnetRdraAddr(pThis, iRxDesc);
+
+ /* Check next descriptor's own bit. If we don't own it, we have
+ * to quit and write error status into the last descriptor we own.
+ */
+ if (!pcnetRmdLoad(pDevIns, pThis, &next_rmd, PHYSADDR(pThis, next_crda), true))
+ break;
+
+ /* Write back current descriptor, clear the own bit. */
+ pcnetRmdStorePassHost(pDevIns, pThis, &rmd, PHYSADDR(pThis, crda));
+
+ /* Switch to the next descriptor */
+ crda = next_crda;
+ rmd = next_rmd;
+
+ cbBuf = RT_MIN(4096 - (size_t)rmd.rmd1.bcnt, cbToRecv);
+ RTGCPHYS32 rbadr2 = PHYSADDR(pThis, rmd.rmd0.rbadr);
+
+ /* We have to leave the critical section here or we risk deadlocking
+ * with EMT when the write is to an unallocated page or has an access
+ * handler associated with it. See above for additional comments. */
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ pcnetPhysWrite(pDevIns, pThis, rbadr2, src, cbBuf);
+ rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc);
+
+ /* RX disabled in the meantime? If so, abort RX. */
+ if (RT_UNLIKELY(CSR_DRX(pThis) || CSR_STOP(pThis) || CSR_SPND(pThis)))
+ return;
+
+ /* Was the register modified in the meantime? If so, don't touch the
+ * register but still update the RX descriptor. */
+ if (RT_LIKELY(iRxDesc == CSR_RCVRC(pThis)))
+ {
+ if (iRxDesc-- < 2)
+ iRxDesc = CSR_RCVRL(pThis);
+ CSR_RCVRC(pThis) = iRxDesc;
+ }
+ else
+ iRxDesc = CSR_RCVRC(pThis);
+
+ src += cbBuf;
+ cbToRecv -= cbBuf;
+ }
+
+ if (RT_LIKELY(cbToRecv == 0))
+ {
+ rmd.rmd1.enp = 1;
+ rmd.rmd1.pam = !CSR_PROM(pThis) && is_padr;
+ rmd.rmd1.lafm = !CSR_PROM(pThis) && is_ladr;
+ rmd.rmd1.bam = !CSR_PROM(pThis) && is_bcast;
+ rmd.rmd2.mcnt = cbPacket;
+ rmd.rmd2.zeros = 0;
+
+ STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cbPacket);
+ }
+ else
+ {
+ Log(("#%d: Overflow by %ubytes\n", PCNET_INST_NR, cbToRecv));
+ rmd.rmd1.oflo = 1;
+ rmd.rmd1.buff = 1;
+ rmd.rmd1.err = 1;
+ }
+
+ /* write back, clear the own bit */
+ pcnetRmdStorePassHost(pDevIns, pThis, &rmd, PHYSADDR(pThis, crda));
+
+ pThis->aCSR[0] |= 0x0400;
+
+ Log(("#%d RCVRC=%d CRDA=%#010x\n", PCNET_INST_NR, CSR_RCVRC(pThis), PHYSADDR(pThis, CSR_CRDA(pThis))));
+#ifdef LOG_ENABLED
+ PRINT_RMD(&rmd);
+#endif
+
+ /* guest driver is owner: force repoll of current and next RDTEs */
+ CSR_CRST(pThis) = 0;
+ }
+ }
+
+ /* see description of TXDPOLL:
+ * ``transmit polling will take place following receive activities'' */
+ if (!fLoopback)
+ pcnetPollRxTx(pDevIns, pThis, pThisCC);
+ pcnetUpdateIrq(pDevIns, pThis);
+}
+
+
+#ifdef IN_RING3
+/**
+ * @callback_method_impl{FNPDMTASKDEV,
+ * This is just a very simple way of delaying sending to R3.}
+ */
+static DECLCALLBACK(void) pcnetR3XmitTaskCallback(PPDMDEVINS pDevIns, void *pvUser)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
+ NOREF(pvUser);
+
+ /*
+ * Transmit as much as we can.
+ */
+ pcnetXmitPending(pDevIns, pThis, pThisCC, true /*fOnWorkerThread*/);
+}
+#endif /* IN_RING3 */
+
+
+/**
+ * Allocates a scatter/gather buffer for a transfer.
+ *
+ * @returns See PPDMINETWORKUP::pfnAllocBuf.
+ * @param pThis The shared PCNet state data.
+ * @param cbMin The minimum buffer size.
+ * @param fLoopback Set if we're in loopback mode.
+ * @param pSgLoop Pointer to stack storage for the loopback SG.
+ * @param ppSgBuf Where to return the SG buffer descriptor on success.
+ * Always set.
+ */
+DECLINLINE(int) pcnetXmitAllocBuf(PPCNETSTATE pThis, PPCNETSTATECC pThisCC, size_t cbMin, bool fLoopback,
+ PPDMSCATTERGATHER pSgLoop, PPPDMSCATTERGATHER ppSgBuf)
+{
+ int rc;
+
+ if (RT_UNLIKELY(fLoopback)) /* hope that loopback mode is rare */
+ {
+ pSgLoop->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
+ pSgLoop->cbUsed = 0;
+ pSgLoop->cbAvailable = sizeof(pThis->abLoopBuf);
+ pSgLoop->pvAllocator = pThis;
+ pSgLoop->pvUser = NULL;
+ pSgLoop->cSegs = 1;
+ pSgLoop->aSegs[0].cbSeg = sizeof(pThis->abLoopBuf);
+ pSgLoop->aSegs[0].pvSeg = pThis->abLoopBuf;
+ *ppSgBuf = pSgLoop;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ PPDMINETWORKUP pDrv = pThisCC->pDrv;
+ if (RT_LIKELY(pDrv))
+ {
+ rc = pDrv->pfnAllocBuf(pDrv, cbMin, NULL /*pGso*/, ppSgBuf);
+ AssertMsg(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN || rc == VERR_NET_DOWN || rc == VERR_NO_MEMORY, ("%Rrc\n", rc));
+ if (RT_FAILURE(rc))
+ *ppSgBuf = NULL;
+ }
+ else
+ {
+ rc = VERR_NET_DOWN;
+ *ppSgBuf = NULL;
+ }
+ }
+ return rc;
+}
+
+
+/**
+ * Frees an unsent buffer.
+ *
+ * @param pThisCC The PCNet state data for the current context.
+ * @param fLoopback Set if we're in loopback mode.
+ * @param pSgBuf The SG to free. Can be NULL.
+ */
+DECLINLINE(void) pcnetXmitFreeBuf(PPCNETSTATECC pThisCC, bool fLoopback, PPDMSCATTERGATHER pSgBuf)
+{
+ if (pSgBuf)
+ {
+ if (RT_UNLIKELY(fLoopback))
+ pSgBuf->pvAllocator = NULL;
+ else
+ {
+ PPDMINETWORKUP pDrv = pThisCC->pDrv;
+ if (RT_LIKELY(pDrv))
+ pDrv->pfnFreeBuf(pDrv, pSgBuf);
+ }
+ }
+}
+
+
+/**
+ * Sends the scatter/gather buffer.
+ *
+ * Wrapper around PDMINETWORKUP::pfnSendBuf, so check it out for the fine print.
+ *
+ * @returns See PDMINETWORKUP::pfnSendBuf.
+ * @param pDevIns The device instance.
+ * @param pThis The shared PCNet state data.
+ * @param pThisCC The PCNet state data for the current context.
+ * @param fLoopback Set if we're in loopback mode.
+ * @param pSgBuf The SG to send.
+ * @param fOnWorkerThread Set if we're being called on a work thread. Clear
+ * if an EMT.
+ */
+DECLINLINE(int) pcnetXmitSendBuf(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, bool fLoopback,
+ PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
+{
+ int rc;
+ STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, pSgBuf->cbUsed);
+ if (RT_UNLIKELY(fLoopback)) /* hope that loopback mode is rare */
+ {
+ Assert(pSgBuf->pvAllocator == (void *)pThis);
+ pThis->Led.Asserted.s.fReading = pThis->Led.Actual.s.fReading = 1;
+ if (HOST_IS_OWNER(CSR_CRST(pThis)))
+ pcnetRdtePoll(pDevIns, pThis);
+
+ pcnetReceiveNoSync(pDevIns, pThis, pThisCC, pThis->abLoopBuf, pSgBuf->cbUsed, true /* fAddFCS */, fLoopback);
+ pThis->Led.Actual.s.fReading = 0;
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ /** @todo We used to leave the critsect here, not sure if that's necessary any
+ * longer. If we could avoid that we could cache a bit more info in
+ * the loop and make it part of the driver<->device contract, saving
+ * critsect mess down in DrvIntNet. */
+ STAM_PROFILE_START(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
+ if (pSgBuf->cbUsed > 70) /* unqualified guess */
+ pThis->Led.Asserted.s.fWriting = pThis->Led.Actual.s.fWriting = 1;
+
+ PPDMINETWORKUP pDrv = pThisCC->pDrv;
+ if (RT_LIKELY(pDrv))
+ {
+ rc = pDrv->pfnSendBuf(pDrv, pSgBuf, fOnWorkerThread);
+ AssertMsg(rc == VINF_SUCCESS || rc == VERR_NET_DOWN || rc == VERR_NET_NO_BUFFER_SPACE, ("%Rrc\n", rc));
+ }
+ else
+ rc = VERR_NET_DOWN;
+
+ pThis->Led.Actual.s.fWriting = 0;
+ STAM_PROFILE_STOP(&pThis->CTX_SUFF_Z(StatTransmitSend), a);
+ }
+ return rc;
+}
+
+
+/**
+ * pcnetXmitRead1st worker that handles the unlikely + slower segmented code
+ * path.
+ */
+static void pcnetXmitRead1stSlow(PPDMDEVINS pDevIns, RTGCPHYS32 GCPhysFrame, unsigned cbFrame, PPDMSCATTERGATHER pSgBuf)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ pSgBuf->cbUsed = cbFrame;
+ for (uint32_t iSeg = 0; ; iSeg++)
+ {
+ if (iSeg >= pSgBuf->cSegs)
+ {
+ AssertFailed();
+ LogRelMax(10, ("PCnet: pcnetXmitRead1stSlow: segment overflow -> ignoring\n"));
+ return;
+ }
+
+ uint32_t cbRead = (uint32_t)RT_MIN(cbFrame, pSgBuf->aSegs[iSeg].cbSeg);
+ pcnetPhysRead(pDevIns, pThis, GCPhysFrame, pSgBuf->aSegs[iSeg].pvSeg, cbRead);
+ cbFrame -= cbRead;
+ if (!cbFrame)
+ return;
+ GCPhysFrame += cbRead;
+ }
+}
+
+
+/**
+ * pcnetXmitSgReadMore worker that handles the unlikely + slower segmented code
+ * path.
+ */
+static void pcnetXmitReadMoreSlow(PPDMDEVINS pDevIns, RTGCPHYS32 GCPhysFrame, unsigned cbFrame, PPDMSCATTERGATHER pSgBuf)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+
+ /* Find the segment which we'll put the next byte into. */
+ size_t off = pSgBuf->cbUsed;
+ size_t offSeg = 0;
+ uint32_t iSeg = 0;
+ while (offSeg + pSgBuf->aSegs[iSeg].cbSeg <= off)
+ {
+ offSeg += pSgBuf->aSegs[iSeg].cbSeg;
+ iSeg++;
+ if (iSeg >= pSgBuf->cSegs)
+ {
+ AssertFailed();
+ LogRelMax(10, ("PCnet: pcnetXmitReadMoreSlow: segment overflow #1 -> ignoring\n"));
+ return;
+ }
+ }
+
+ /* Commit before we start copying so we can decrement cbFrame. */
+ pSgBuf->cbUsed = off + cbFrame;
+
+ /* Deal with the first segment if we at an offset into it. */
+ if (off != offSeg)
+ {
+ size_t offIntoSeg = off - offSeg;
+ uint32_t cbRead = (uint32_t)RT_MIN(pSgBuf->aSegs[iSeg].cbSeg - offIntoSeg, cbFrame);
+ pcnetPhysRead(pDevIns, pThis, GCPhysFrame,
+ (uint8_t *)pSgBuf->aSegs[iSeg].pvSeg + offIntoSeg, cbRead);
+ cbFrame -= cbRead;
+ if (!cbFrame)
+ return;
+ GCPhysFrame += cbRead;
+ iSeg++;
+ }
+
+ /* For the remainder, we've got whole segments. */
+ for (;; iSeg++)
+ {
+ if (iSeg >= pSgBuf->cSegs)
+ {
+ AssertFailed();
+ LogRelMax(10, ("PCnet: pcnetXmitReadMoreSlow: segment overflow #2 -> ignoring\n"));
+ return;
+ }
+
+ uint32_t cbRead = (uint32_t)RT_MIN(pSgBuf->aSegs[iSeg].cbSeg, cbFrame);
+ pcnetPhysRead(pDevIns, pThis, GCPhysFrame, pSgBuf->aSegs[iSeg].pvSeg, cbRead);
+ cbFrame -= cbRead;
+ if (!cbFrame)
+ return;
+ GCPhysFrame += cbFrame;
+ }
+}
+
+
+/**
+ * Reads the first part of a frame into the scatter gather buffer.
+ */
+DECLINLINE(void) pcnetXmitRead1st(PPDMDEVINS pDevIns, PPCNETSTATE pThis, RTGCPHYS32 GCPhysFrame, const unsigned cbFrame,
+ PPDMSCATTERGATHER pSgBuf)
+{
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect)); RT_NOREF(pThis);
+ Assert(pSgBuf->cbAvailable >= cbFrame);
+
+ if (RT_LIKELY(pSgBuf->aSegs[0].cbSeg >= cbFrame)) /* justification: all drivers returns a single segment atm. */
+ {
+ pSgBuf->cbUsed = cbFrame;
+ pcnetPhysRead(pDevIns, pThis, GCPhysFrame, pSgBuf->aSegs[0].pvSeg, cbFrame);
+ }
+ else
+ pcnetXmitRead1stSlow(pDevIns, GCPhysFrame, cbFrame, pSgBuf);
+}
+
+/**
+ * Reads more into the current frame.
+ */
+DECLINLINE(void) pcnetXmitReadMore(PPDMDEVINS pDevIns, RTGCPHYS32 GCPhysFrame, const unsigned cbFrame, PPDMSCATTERGATHER pSgBuf)
+{
+ size_t off = pSgBuf->cbUsed;
+ Assert(pSgBuf->cbAvailable >= cbFrame + off);
+
+ if (RT_LIKELY(pSgBuf->aSegs[0].cbSeg >= cbFrame + off))
+ {
+ pSgBuf->cbUsed = cbFrame + off;
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ pcnetPhysRead(pDevIns, pThis, GCPhysFrame, (uint8_t *)pSgBuf->aSegs[0].pvSeg + off, cbFrame);
+ }
+ else
+ pcnetXmitReadMoreSlow(pDevIns, GCPhysFrame, cbFrame, pSgBuf);
+}
+
+
+/**
+ * Fails a TMD with a link down error.
+ */
+static void pcnetXmitFailTMDLinkDown(PPCNETSTATE pThis, TMD *pTmd)
+{
+ /* make carrier error - hope this is correct. */
+ pThis->cLinkDownReported++;
+ pTmd->tmd2.lcar = pTmd->tmd1.err = 1;
+ pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR */
+ pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
+ Log(("#%d pcnetTransmit: Signaling send error. swstyle=%#x\n",
+ PCNET_INST_NR, pThis->aBCR[BCR_SWS]));
+}
+
+/**
+ * Fails a TMD with a generic error.
+ */
+static void pcnetXmitFailTMDGeneric(PPCNETSTATE pThis, TMD *pTmd)
+{
+ /* make carrier error - hope this is correct. */
+ pTmd->tmd2.lcar = pTmd->tmd1.err = 1;
+ pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR */
+ pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
+ Log(("#%d pcnetTransmit: Signaling send error. swstyle=%#x\n",
+ PCNET_INST_NR, pThis->aBCR[BCR_SWS]));
+}
+
+
+/**
+ * Try to transmit frames
+ */
+static void pcnetTransmit(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC)
+{
+ if (RT_UNLIKELY(!CSR_TXON(pThis)))
+ {
+ pThis->aCSR[0] &= ~0x0008; /* Clear TDMD */
+ return;
+ }
+
+ /*
+ * Check the current transmit descriptors.
+ */
+ TMD tmd;
+ if (!pcnetTdtePoll(pDevIns, pThis, &tmd))
+ return;
+
+ /*
+ * Clear TDMD.
+ */
+ pThis->aCSR[0] &= ~0x0008;
+
+ /*
+ * Transmit pending packets if possible, defer it if we cannot do it
+ * in the current context.
+ */
+#if defined(IN_RING0) || defined(IN_RC)
+ if (!pThisCC->pDrv)
+ {
+ int rc = PDMDevHlpTaskTrigger(pDevIns, pThis->hXmitTask);
+ AssertRC(rc);
+ }
+ else
+#endif
+ {
+ int rc = pcnetXmitPending(pDevIns, pThis, pThisCC, false /*fOnWorkerThread*/);
+ if (rc == VERR_TRY_AGAIN)
+ rc = VINF_SUCCESS;
+ AssertRC(rc);
+ }
+}
+
+
+/**
+ * Actually try transmit frames.
+ *
+ * @threads TX or EMT.
+ */
+static int pcnetAsyncTransmit(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, bool fOnWorkerThread)
+{
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+
+ /*
+ * Just cleared transmit demand if the transmitter is off.
+ */
+ if (RT_UNLIKELY(!CSR_TXON(pThis)))
+ {
+ pThis->aCSR[0] &= ~0x0008; /* Clear TDMD */
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Iterate the transmit descriptors.
+ */
+ int rc;
+ unsigned cFlushIrq = 0;
+ int cMax = 32;
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ do
+ {
+#ifdef VBOX_WITH_STATISTICS
+ unsigned cBuffers = 1;
+#endif
+ TMD tmd;
+ if (!pcnetTdtePoll(pDevIns, pThis, &tmd))
+ break;
+
+#ifdef LOG_ENABLED
+ Log10(("#%d TMDLOAD %#010x\n", PCNET_INST_NR, PHYSADDR(pThis, CSR_CXDA(pThis))));
+ PRINT_TMD(&tmd);
+#endif
+ bool const fLoopback = CSR_LOOP(pThis);
+ PDMSCATTERGATHER SgLoop;
+ PPDMSCATTERGATHER pSgBuf;
+
+ /*
+ * The typical case - a complete packet.
+ */
+ if (tmd.tmd1.stp && tmd.tmd1.enp)
+ {
+ const unsigned cb = 4096 - tmd.tmd1.bcnt;
+ Log(("#%d pcnetAsyncTransmit: stp&enp: cb=%d xmtrc=%#x\n", PCNET_INST_NR, cb, CSR_XMTRC(pThis)));
+ STAM_COUNTER_INC(&pThis->StatTransmitCase1);
+
+ if (RT_LIKELY(pcnetIsLinkUp(pThis) || fLoopback))
+ {
+ /* From the manual: ``A zero length buffer is acceptable as
+ * long as it is not the last buffer in a chain (STP = 0 and
+ * ENP = 1).'' That means that the first buffer might have a
+ * zero length if it is not the last one in the chain. */
+ if (RT_LIKELY(cb <= MAX_FRAME))
+ {
+ rc = pcnetXmitAllocBuf(pThis, pThisCC, cb, fLoopback, &SgLoop, &pSgBuf);
+ if (RT_SUCCESS(rc))
+ {
+ pcnetXmitRead1st(pDevIns, pThis, PHYSADDR(pThis, tmd.tmd0.tbadr), cb, pSgBuf);
+ rc = pcnetXmitSendBuf(pDevIns, pThis, pThisCC, fLoopback, pSgBuf, fOnWorkerThread);
+ }
+ else if (rc == VERR_TRY_AGAIN)
+ {
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ return VINF_SUCCESS;
+ }
+ if (RT_FAILURE(rc))
+ pcnetXmitFailTMDLinkDown(pThis, &tmd);
+ }
+ else if (cb == 4096)
+ {
+ /* The Windows NT4 pcnet driver sometimes marks the first
+ * unused descriptor as owned by us. Ignore that (by
+ * passing it back). Do not update the ring counter in this
+ * case (otherwise that driver becomes even more confused,
+ * which causes transmit to stall for about 10 seconds).
+ * This is just a workaround, not a final solution. */
+ /* r=frank: IMHO this is the correct implementation. The
+ * manual says: ``If the OWN bit is set and the buffer
+ * length is 0, the OWN bit will be cleared. In the C-LANCE
+ * the buffer length of 0 is interpreted as a 4096-byte
+ * buffer.'' */
+ /* r=michaln: Perhaps not quite right. The C-LANCE (Am79C90)
+ * datasheet explains that the old LANCE (Am7990) ignored
+ * the top four bits next to BCNT and a count of 0 was
+ * interpreted as 4096. In the C-LANCE, that is still the
+ * case if the top bits are all ones. If all 16 bits are
+ * zero, the C-LANCE interprets it as zero-length transmit
+ * buffer. It's not entirely clear if the later models
+ * (PCnet-ISA, PCnet-PCI) behave like the C-LANCE or not.
+ * It is possible that the actual behavior of the C-LANCE
+ * and later hardware is that the buffer lengths are *16-bit*
+ * two's complement numbers between 0 and 4096. AMD's drivers
+ * in fact generally treat the length as a 16-bit quantity. */
+ LogRel(("PCnet#%d: pcnetAsyncTransmit: illegal 4kb frame -> ignoring\n", PCNET_INST_NR));
+ pcnetTmdStorePassHost(pDevIns, pThis, &tmd, PHYSADDR(pThis, CSR_CXDA(pThis)));
+ break;
+ }
+ else
+ {
+ /* Signal error, as this violates the Ethernet specs. */
+ /** @todo check if the correct error is generated. */
+ LogRel(("PCnet#%d: pcnetAsyncTransmit: illegal 4kb frame -> signalling error\n", PCNET_INST_NR));
+
+ pcnetXmitFailTMDGeneric(pThis, &tmd);
+ }
+ }
+ else
+ pcnetXmitFailTMDLinkDown(pThis, &tmd);
+
+ /* Write back the TMD and pass it to the host (clear own bit). */
+ pcnetTmdStorePassHost(pDevIns, pThis, &tmd, PHYSADDR(pThis, CSR_CXDA(pThis)));
+
+ /* advance the ring counter register */
+ if (CSR_XMTRC(pThis) < 2)
+ CSR_XMTRC(pThis) = CSR_XMTRL(pThis);
+ else
+ CSR_XMTRC(pThis)--;
+ }
+ else if (tmd.tmd1.stp)
+ {
+ STAM_COUNTER_INC(&pThis->StatTransmitCase2);
+
+ /*
+ * Read TMDs until end-of-packet or tdte poll fails (underflow).
+ *
+ * We allocate a maximum sized buffer here since we do not wish to
+ * waste time finding out how much space we actually need even if
+ * we could reliably do that on SMP guests.
+ */
+ unsigned cb = 4096 - tmd.tmd1.bcnt;
+ rc = pcnetXmitAllocBuf(pThis, pThisCC, RT_MAX(MAX_FRAME, cb), fLoopback, &SgLoop, &pSgBuf);
+ if (rc == VERR_TRY_AGAIN)
+ {
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
+ return VINF_SUCCESS;
+ }
+
+ bool fDropFrame = RT_FAILURE(rc);
+ if (!fDropFrame)
+ pcnetXmitRead1st(pDevIns, pThis, PHYSADDR(pThis, tmd.tmd0.tbadr), cb, pSgBuf);
+
+ for (;;)
+ {
+ /*
+ * Advance the ring counter register and check the next tmd.
+ */
+#ifdef LOG_ENABLED
+ const uint32_t iStart = CSR_XMTRC(pThis);
+#endif
+ const uint32_t GCPhysPrevTmd = PHYSADDR(pThis, CSR_CXDA(pThis));
+ if (CSR_XMTRC(pThis) < 2)
+ CSR_XMTRC(pThis) = CSR_XMTRL(pThis);
+ else
+ CSR_XMTRC(pThis)--;
+
+ TMD tmdNext;
+ if (!pcnetTdtePoll(pDevIns, pThis, &tmdNext))
+ {
+ /*
+ * Underflow!
+ */
+ tmd.tmd2.buff = tmd.tmd2.uflo = tmd.tmd1.err = 1;
+ pThis->aCSR[0] |= 0x0200; /* set TINT */
+ /* Don't allow the guest to clear TINT before reading it */
+ pThis->u16CSR0LastSeenByGuest &= ~0x0200;
+ if (!CSR_DXSUFLO(pThis)) /* stop on xmit underflow */
+ pThis->aCSR[0] &= ~0x0010; /* clear TXON */
+ pcnetTmdStorePassHost(pDevIns, pThis, &tmd, GCPhysPrevTmd);
+ AssertMsgFailed(("pcnetAsyncTransmit: Underflow!!!\n"));
+ pcnetXmitFreeBuf(pThisCC, fLoopback, pSgBuf);
+ break;
+ }
+
+ /* release & save the previous tmd, pass it to the host */
+ pcnetTmdStorePassHost(pDevIns, pThis, &tmd, GCPhysPrevTmd);
+
+ /*
+ * The next tmd is already loaded.
+ */
+#ifdef VBOX_WITH_STATISTICS
+ cBuffers++;
+#endif
+ tmd = tmdNext;
+ cb = 4096 - tmd.tmd1.bcnt;
+ if ( !fDropFrame
+ && pSgBuf->cbUsed + cb <= pSgBuf->cbAvailable)
+ pcnetXmitReadMore(pDevIns, PHYSADDR(pThis, tmd.tmd0.tbadr), cb, pSgBuf);
+ else
+ {
+ AssertMsg(fDropFrame, ("pcnetAsyncTransmit: Frame is too big!!! %d bytes\n", pSgBuf->cbUsed + cb));
+ fDropFrame = true;
+ }
+
+ /*
+ * Done already?
+ */
+ if (tmd.tmd1.enp)
+ {
+ Log(("#%d pcnetAsyncTransmit: stp: cb=%d xmtrc=%#x-%#x\n", PCNET_INST_NR,
+ pSgBuf ? pSgBuf->cbUsed : 0, iStart, CSR_XMTRC(pThis)));
+ if (!fDropFrame && (pcnetIsLinkUp(pThis) || fLoopback))
+ {
+ rc = pcnetXmitSendBuf(pDevIns, pThis, pThisCC, fLoopback, pSgBuf, fOnWorkerThread);
+ fDropFrame = RT_FAILURE(rc);
+ }
+ else
+ pcnetXmitFreeBuf(pThisCC, fLoopback, pSgBuf);
+ if (fDropFrame)
+ pcnetXmitFailTMDLinkDown(pThis, &tmd);
+
+ /* Write back the TMD, pass it to the host */
+ pcnetTmdStorePassHost(pDevIns, pThis, &tmd, PHYSADDR(pThis, CSR_CXDA(pThis)));
+
+ /* advance the ring counter register */
+ if (CSR_XMTRC(pThis) < 2)
+ CSR_XMTRC(pThis) = CSR_XMTRL(pThis);
+ else
+ CSR_XMTRC(pThis)--;
+ break;
+ }
+ } /* the loop */
+ }
+ else
+ {
+ /*
+ * We underflowed in a previous transfer, or the driver is giving us shit.
+ * Simply stop the transmitting for now.
+ */
+ /** @todo according to the specs we're supposed to clear the own bit and move on to the next one. */
+ Log(("#%d pcnetAsyncTransmit: guest is giving us shit!\n", PCNET_INST_NR));
+ break;
+ }
+ /* Update TDMD, TXSTRT and TINT. */
+ pThis->aCSR[0] &= ~0x0008; /* clear TDMD */
+
+ pThis->aCSR[4] |= 0x0008; /* set TXSTRT */
+ if ( !CSR_TOKINTD(pThis) /* Transmit OK Interrupt Disable, no infl. on errors. */
+ || (CSR_LTINTEN(pThis) && tmd.tmd1.ltint)
+ || tmd.tmd1.err)
+ {
+ cFlushIrq++;
+ }
+
+ /** @todo should we continue after an error (tmd.tmd1.err) or not? */
+
+ STAM_COUNTER_INC(&pThis->aStatXmitChainCounts[RT_MIN(cBuffers,
+ RT_ELEMENTS(pThis->aStatXmitChainCounts)) - 1]);
+ if (--cMax == 0)
+ break;
+ } while (CSR_TXON(pThis)); /* transfer on */
+
+ if (cFlushIrq)
+ {
+ STAM_COUNTER_INC(&pThis->aStatXmitFlush[RT_MIN(cFlushIrq, RT_ELEMENTS(pThis->aStatXmitFlush)) - 1]);
+ /* The WinXP PCnet driver has apparently a bug: It sets CSR0.TDMD _before_
+ * it clears CSR0.TINT. This can lead to a race where the driver clears
+ * CSR0.TINT right after it was set by the device. The driver waits until
+ * CSR0.TINT is set again but this will never happen. So prevent clearing
+ * this bit as long as the driver didn't read it. See @bugref{5288}. */
+ pThis->aCSR[0] |= 0x0200; /* set TINT */
+ /* Don't allow the guest to clear TINT before reading it */
+ pThis->u16CSR0LastSeenByGuest &= ~0x0200;
+ pcnetUpdateIrq(pDevIns, pThis);
+ }
+
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatTransmit), a);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Transmit pending descriptors.
+ *
+ * @returns VBox status code. VERR_TRY_AGAIN is returned if we're busy.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The PCnet shared instance data.
+ * @param pThisCC The PCNet state data for the current context.
+ * @param fOnWorkerThread Whether we're on a worker thread or on an EMT.
+ */
+static int pcnetXmitPending(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, bool fOnWorkerThread)
+{
+ RT_NOREF_PV(fOnWorkerThread);
+ int rc;
+
+ /*
+ * Grab the xmit lock of the driver as well as the PCnet device state.
+ */
+ PPDMINETWORKUP pDrv = pThisCC->pDrv;
+ if (pDrv)
+ {
+ rc = pDrv->pfnBeginXmit(pDrv, false /*fOnWorkerThread*/);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo check if we're supposed to suspend now. */
+ /*
+ * Do the transmitting.
+ */
+ int rc2 = pcnetAsyncTransmit(pDevIns, pThis, pThisCC, false /*fOnWorkerThread*/);
+ AssertReleaseRC(rc2);
+
+ /*
+ * Release the locks.
+ */
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ }
+ else
+ AssertLogRelRC(rc);
+ if (pDrv)
+ pDrv->pfnEndXmit(pDrv);
+
+ return rc;
+}
+
+
+/**
+ * Poll for changes in RX and TX descriptor rings.
+ */
+static void pcnetPollRxTx(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC)
+{
+ if (CSR_RXON(pThis))
+ {
+ /*
+ * The second case is important for pcnetWaitReceiveAvail(): If CSR_CRST(pThis) was
+ * true but pcnetR3CanReceive() returned false for some other reason we need to check
+ * _now_ if we have to wakeup pcnetWaitReceiveAvail().
+ */
+ if ( HOST_IS_OWNER(CSR_CRST(pThis)) /* only poll RDTEs if none available or ... */
+ || pThis->fMaybeOutOfSpace) /* ... for waking up pcnetWaitReceiveAvail() */
+ pcnetRdtePoll(pDevIns, pThis);
+ }
+
+ if (CSR_TDMD(pThis) || (CSR_TXON(pThis) && !CSR_DPOLL(pThis)))
+ pcnetTransmit(pDevIns, pThis, pThisCC);
+}
+
+
+/**
+ * Start the poller timer.
+ * Poll timer interval is fixed to 500Hz. Don't stop it.
+ * @thread EMT, TAP.
+ */
+static void pcnetPollTimerStart(PPDMDEVINS pDevIns, PPCNETSTATE pThis)
+{
+ PDMDevHlpTimerSetMillies(pDevIns, pThis->hTimerPoll, 2);
+}
+
+
+/**
+ * Update the poller timer.
+ * @thread EMT.
+ */
+static void pcnetPollTimer(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC)
+{
+ STAM_PROFILE_ADV_START(&pThis->StatPollTimer, a);
+
+#ifdef LOG_ENABLED
+ TMD dummy;
+ if (CSR_STOP(pThis) || CSR_SPND(pThis))
+ Log2(("#%d pcnetPollTimer time=%#010llx CSR_STOP=%d CSR_SPND=%d\n",
+ PCNET_INST_NR, RTTimeMilliTS(), CSR_STOP(pThis), CSR_SPND(pThis)));
+ else
+ Log2(("#%d pcnetPollTimer time=%#010llx TDMD=%d TXON=%d POLL=%d TDTE=%d TDRA=%#x\n",
+ PCNET_INST_NR, RTTimeMilliTS(), CSR_TDMD(pThis), CSR_TXON(pThis),
+ !CSR_DPOLL(pThis), pcnetTdtePoll(pDevIns, pThis, &dummy), pThis->GCTDRA));
+ Log2(("#%d pcnetPollTimer: CSR_CXDA=%#x CSR_XMTRL=%d CSR_XMTRC=%d\n",
+ PCNET_INST_NR, CSR_CXDA(pThis), CSR_XMTRL(pThis), CSR_XMTRC(pThis)));
+#endif
+#ifdef LOG_ENABLED
+ if (CSR_CXDA(pThis))
+ {
+ TMD tmd;
+ pcnetTmdLoadAll(pDevIns, pThis, &tmd, PHYSADDR(pThis, CSR_CXDA(pThis)));
+ Log10(("#%d pcnetPollTimer: TMDLOAD %#010x\n", PCNET_INST_NR, PHYSADDR(pThis, CSR_CXDA(pThis))));
+ PRINT_TMD(&tmd);
+ }
+#endif
+ if (CSR_TDMD(pThis))
+ pcnetTransmit(pDevIns, pThis, pThisCC);
+
+ pcnetUpdateIrq(pDevIns, pThis);
+
+ /* If the receive thread is waiting for new descriptors, poll TX/RX even if polling
+ * disabled. We wouldn't need to poll for new TX descriptors in that case but it will
+ * not hurt as waiting for RX descriptors should happen very seldom */
+ if (RT_LIKELY( !CSR_STOP(pThis)
+ && !CSR_SPND(pThis)
+ && ( !CSR_DPOLL(pThis)
+ || pThis->fMaybeOutOfSpace)))
+ {
+ /* We ensure that we poll at least every 2ms (500Hz) but not more often than
+ * 5000 times per second. This way we completely prevent the overhead from
+ * heavy reprogramming the timer which turned out to be very CPU-intensive.
+ * The drawback is that csr46 and csr47 are not updated properly anymore
+ * but so far I have not seen any guest depending on these values. The 2ms
+ * interval is the default polling interval of the PCnet card (65536/33MHz). */
+ uint64_t u64Now = PDMDevHlpTimerGet(pDevIns, pThis->hTimerPoll);
+ if (RT_UNLIKELY(u64Now - pThis->u64LastPoll > 200000))
+ {
+ pThis->u64LastPoll = u64Now;
+ pcnetPollRxTx(pDevIns, pThis, pThisCC);
+ }
+ if (!PDMDevHlpTimerIsActive(pDevIns, pThis->hTimerPoll))
+ pcnetPollTimerStart(pDevIns, pThis);
+ }
+ STAM_PROFILE_ADV_STOP(&pThis->StatPollTimer, a);
+}
+
+
+static VBOXSTRICTRC pcnetCSRWriteU16(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, uint32_t u32RAP, uint32_t val)
+{
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+ Log8(("#%d pcnetCSRWriteU16: rap=%d val=%#06x\n", PCNET_INST_NR, u32RAP, val));
+ switch (u32RAP)
+ {
+ case 0:
+ {
+ uint16_t csr0 = pThis->aCSR[0];
+ /* Clear any interrupt flags.
+ * Don't clear an interrupt flag which was not seen by the guest yet. */
+ csr0 &= ~(val & 0x7f00 & pThis->u16CSR0LastSeenByGuest);
+ csr0 = (csr0 & ~0x0040) | (val & 0x0048);
+ val = (val & 0x007f) | (csr0 & 0x7f00);
+
+ /* Iff STOP, STRT and INIT are set, clear STRT and INIT */
+ if ((val & 7) == 7)
+ val &= ~3;
+
+ Log(("#%d CSR0: old=%#06x new=%#06x\n", PCNET_INST_NR, pThis->aCSR[0], csr0));
+
+#ifndef IN_RING3
+ if (!(csr0 & 0x0001/*init*/) && (val & 1))
+ {
+ Log(("#%d pcnetCSRWriteU16: pcnetR3Init requested => HC\n", PCNET_INST_NR));
+ return VINF_IOM_R3_IOPORT_WRITE;
+ }
+#endif
+ pThis->aCSR[0] = csr0;
+
+ if (!CSR_STOP(pThis) && (val & 4))
+ pcnetStop(pDevIns, pThis, pThisCC);
+
+#ifdef IN_RING3
+ if (!CSR_INIT(pThis) && (val & 1))
+ {
+ bool fDelayInit = false;
+
+ /* Many PCnet drivers disable PCI bus mastering before setting the INIT bit and
+ * then immediately enable it back again. This is done to work around a silicon
+ * bug that could cause a PCI bus hang under some circumstances. The bug only
+ * existed in the early PCI chips (Am79C970 PCnet-PCI) but many drivers apply the
+ * workaround to all PCnet PCI models. Drivers written by AMD generally do this
+ * (DOS, Windows, OS/2). PCnet drivers in Windows 2000 and XP are new enough to
+ * not apply the workaround to our emulated PCnet-PCI II (Am79C970A) and
+ * PCnet-FAST III (Am79C973).
+ *
+ * The AMDPCnet32 drivers for NeXTSTEP/OpenStep (notably OS 4.2) completely fail
+ * unless we delay the initialization until after bus mastering is re-enabled.
+ */
+ if (PCNET_IS_PCI(pThis))
+ {
+ PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
+ uint8_t uCmd = PDMPciDevGetByte(pPciDev, 0x04);
+
+ /* Recognize situation with PCI bus mastering disabled and setting
+ * INIT bit without also setting STRT.
+ */
+ if (!(uCmd & 4) && !(val & 2))
+ fDelayInit = true;
+ }
+
+ if (!fDelayInit)
+ pcnetR3Init(pDevIns, pThis, pThisCC);
+ else
+ {
+ LogRel(("PCnet#%d: Delaying INIT due to disabled PCI bus mastering\n", PCNET_INST_NR));
+ pThis->aCSR[0] |= 0x0001; /* Set INIT and MERR bits. */
+ pThis->aCSR[6] = 1; /* Set a flag in read-only CSR6. */
+ }
+ }
+#endif
+
+ if (!CSR_STRT(pThis) && (val & 2))
+ pcnetStart(pDevIns, pThis);
+
+ if (CSR_TDMD(pThis))
+ pcnetTransmit(pDevIns, pThis, pThisCC);
+
+ return rc;
+ }
+ case 2: /* IADRH */
+ if (PCNET_IS_ISA(pThis))
+ val &= 0x00ff; /* Upper 8 bits ignored on ISA chips. */
+ RT_FALL_THRU();
+ case 1: /* IADRL */
+ case 8: /* LADRF 0..15 */
+ case 9: /* LADRF 16..31 */
+ case 10: /* LADRF 32..47 */
+ case 11: /* LADRF 48..63 */
+ case 12: /* PADR 0..15 */
+ case 13: /* PADR 16..31 */
+ case 14: /* PADR 32..47 */
+ case 18: /* CRBAL */
+ case 19: /* CRBAU */
+ case 20: /* CXBAL */
+ case 21: /* CXBAU */
+ case 22: /* NRBAL */
+ case 23: /* NRBAU */
+ case 26: /* NRDAL */
+ case 27: /* NRDAU */
+ case 28: /* CRDAL */
+ case 29: /* CRDAU */
+ case 32: /* NXDAL */
+ case 33: /* NXDAU */
+ case 34: /* CXDAL */
+ case 35: /* CXDAU */
+ case 36: /* NNRDL */
+ case 37: /* NNRDU */
+ case 38: /* NNXDL */
+ case 39: /* NNXDU */
+ case 40: /* CRBCL */
+ case 41: /* CRBCU */
+ case 42: /* CXBCL */
+ case 43: /* CXBCU */
+ case 44: /* NRBCL */
+ case 45: /* NRBCU */
+ case 46: /* POLL */
+ case 47: /* POLLINT */
+ case 72: /* RCVRC */
+ case 74: /* XMTRC */
+ case 112: /* MISSC */
+ if (CSR_STOP(pThis) || CSR_SPND(pThis))
+ break;
+ else
+ {
+ Log(("#%d: WRITE CSR%d, %#06x, ignoring!!\n", PCNET_INST_NR, u32RAP, val));
+ return rc;
+ }
+ case 3: /* Interrupt Mask and Deferral Control */
+ break;
+ case 4: /* Test and Features Control */
+ pThis->aCSR[4] &= ~(val & 0x026a);
+ val &= ~0x026a;
+ val |= pThis->aCSR[4] & 0x026a;
+ break;
+ case 5: /* Extended Control and Interrupt 1 */
+ pThis->aCSR[5] &= ~(val & 0x0a90);
+ val &= ~0x0a90;
+ val |= pThis->aCSR[5] & 0x0a90;
+ break;
+ case 7: /* Extended Control and Interrupt 2 */
+ {
+ uint16_t csr7 = pThis->aCSR[7];
+ csr7 &= ~0x0400 ;
+ csr7 &= ~(val & 0x0800);
+ csr7 |= (val & 0x0400);
+ pThis->aCSR[7] = csr7;
+ return rc;
+ }
+ case 15: /* Mode */
+ if ((pThis->aCSR[15] & 0x8000) != (uint16_t)(val & 0x8000) && pThis->fDriverAttached)
+ {
+ Log(("#%d: promiscuous mode changed to %d\n", PCNET_INST_NR, !!(val & 0x8000)));
+#ifndef IN_RING3
+ return VINF_IOM_R3_IOPORT_WRITE;
+#else
+ /* check for promiscuous mode change */
+ if (pThisCC->pDrv)
+ pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, !!(val & 0x8000));
+#endif
+ }
+ break;
+ case 16: /* IADRL */
+ return pcnetCSRWriteU16(pDevIns, pThis, pThisCC, 1, val);
+ case 17: /* IADRH */
+ return pcnetCSRWriteU16(pDevIns, pThis, pThisCC, 2, val);
+
+ /*
+ * 24 and 25 are the Base Address of Receive Descriptor.
+ * We combine and mirror these in GCRDRA.
+ */
+ case 24: /* BADRL */
+ case 25: /* BADRU */
+ if (!CSR_STOP(pThis) && !CSR_SPND(pThis))
+ {
+ Log(("#%d: WRITE CSR%d, %#06x, ignoring!!\n", PCNET_INST_NR, u32RAP, val));
+ return rc;
+ }
+ if (u32RAP == 24)
+ pThis->GCRDRA = (pThis->GCRDRA & 0xffff0000) | (val & 0x0000ffff);
+ else
+ pThis->GCRDRA = (pThis->GCRDRA & 0x0000ffff) | ((val & 0x0000ffff) << 16);
+ Log(("#%d: WRITE CSR%d, %#06x => GCRDRA=%08x (alt init)\n", PCNET_INST_NR, u32RAP, val, pThis->GCRDRA));
+ if (pThis->GCRDRA & (pThis->iLog2DescSize - 1))
+ LogRel(("PCnet#%d: Warning: Misaligned RDRA (GCRDRA=%#010x)\n", PCNET_INST_NR, pThis->GCRDRA));
+ break;
+
+ /*
+ * 30 & 31 are the Base Address of Transmit Descriptor.
+ * We combine and mirrorthese in GCTDRA.
+ */
+ case 30: /* BADXL */
+ case 31: /* BADXU */
+ if (!CSR_STOP(pThis) && !CSR_SPND(pThis))
+ {
+ Log(("#%d: WRITE CSR%d, %#06x !!\n", PCNET_INST_NR, u32RAP, val));
+ return rc;
+ }
+ if (u32RAP == 30)
+ pThis->GCTDRA = (pThis->GCTDRA & 0xffff0000) | (val & 0x0000ffff);
+ else
+ pThis->GCTDRA = (pThis->GCTDRA & 0x0000ffff) | ((val & 0x0000ffff) << 16);
+ Log(("#%d: WRITE CSR%d, %#06x => GCTDRA=%08x (alt init)\n", PCNET_INST_NR, u32RAP, val, pThis->GCTDRA));
+ if (pThis->GCTDRA & (pThis->iLog2DescSize - 1))
+ LogRel(("PCnet#%d: Warning: Misaligned TDRA (GCTDRA=%#010x)\n", PCNET_INST_NR, pThis->GCTDRA));
+ break;
+
+ case 58: /* Software Style */
+ rc = pcnetBCRWriteU16(pDevIns, pThis, BCR_SWS, val);
+ break;
+
+ /*
+ * Registers 76 and 78 aren't stored correctly (see todos), but I'm don't dare
+ * try fix that right now. So, as a quick hack for 'alt init' I'll just correct them here.
+ */
+ case 76: /* RCVRL */ /** @todo call pcnetR3UpdateRingHandlers */
+ /** @todo receive ring length is stored in two's complement! */
+ case 78: /* XMTRL */ /** @todo call pcnetR3UpdateRingHandlers */
+ /** @todo transmit ring length is stored in two's complement! */
+ if (!CSR_STOP(pThis) && !CSR_SPND(pThis))
+ {
+ Log(("#%d: WRITE CSR%d, %#06x !!\n", PCNET_INST_NR, u32RAP, val));
+ return rc;
+ }
+ Log(("#%d: WRITE CSR%d, %#06x (hacked %#06x) (alt init)\n", PCNET_INST_NR,
+ u32RAP, val, 1 + ~(uint16_t)val));
+ val = 1 + ~(uint16_t)val;
+
+ /*
+ * HACK ALERT! Set the counter registers too.
+ */
+ pThis->aCSR[u32RAP - 4] = val;
+ break;
+
+ default:
+ return rc;
+ }
+ pThis->aCSR[u32RAP] = val;
+ return rc;
+}
+
+/**
+ * Encode a 32-bit link speed into a custom 16-bit floating-point value
+ */
+static uint32_t pcnetLinkSpd(uint32_t speed)
+{
+ unsigned exp = 0;
+
+ while (speed & 0xFFFFE000)
+ {
+ speed /= 10;
+ ++exp;
+ }
+ return (exp << 13) | speed;
+}
+
+static VBOXSTRICTRC pcnetCSRReadU16(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, uint32_t u32RAP, uint32_t *pu32)
+{
+ uint32_t val;
+ switch (u32RAP)
+ {
+ case 0:
+ /* Check if delayed initialization needs to run. */
+ if (RT_UNLIKELY(pThis->aCSR[6] == 1))
+ {
+#ifndef IN_RING3
+ return VINF_IOM_R3_IOPORT_READ;
+#else
+ /* This is the second half of delayed initialization required
+ * to work around guest drivers that temporarily disable PCI bus
+ * mastering around setting the INIT bit in CSR0.
+ * See pcnetCSRWriteU16() for details.
+ */
+ pcnetR3Init(pDevIns, pThis, pThisCC);
+ Assert(pThis->aCSR[6] != 1);
+#endif
+ }
+ pcnetUpdateIrq(pDevIns, pThis);
+ val = pThis->aCSR[0];
+ val |= (val & 0x7800) ? 0x8000 : 0;
+ pThis->u16CSR0LastSeenByGuest = val;
+ break;
+ case 16:
+ return pcnetCSRReadU16(pDevIns, pThis, pThisCC, 1, pu32);
+ case 17:
+ return pcnetCSRReadU16(pDevIns, pThis, pThisCC, 2, pu32);
+ case 58:
+ *pu32 = pcnetBCRReadU16(pThis, BCR_SWS);
+ return VINF_SUCCESS;
+ case 68: /* Custom register to pass link speed to driver */
+ *pu32 = pcnetLinkSpd(pThis->u32LinkSpeed);
+ return VINF_SUCCESS;
+ case 88:
+ val = pThis->aCSR[89];
+ val <<= 16;
+ val |= pThis->aCSR[88];
+ break;
+ default:
+ val = pThis->aCSR[u32RAP];
+ }
+ *pu32 = val;
+ Log8(("#%d pcnetCSRReadU16: rap=%d val=%#06x\n", PCNET_INST_NR, u32RAP, val));
+ return VINF_SUCCESS;
+}
+
+static VBOXSTRICTRC pcnetBCRWriteU16(PPDMDEVINS pDevIns, PPCNETSTATE pThis, uint32_t u32RAP, uint32_t val)
+{
+ u32RAP &= 0x7f;
+ Log7(("#%d pcnetBCRWriteU16: rap=%d val=%#06x\n", PCNET_INST_NR, u32RAP, val));
+ switch (u32RAP)
+ {
+ case BCR_SWS:
+ if (!(CSR_STOP(pThis) || CSR_SPND(pThis)))
+ return VINF_SUCCESS;
+ val &= ~0x0300;
+ switch (val & 0x00ff)
+ {
+ default:
+ Log(("#%d Bad SWSTYLE=%#04x\n", PCNET_INST_NR, val & 0xff));
+ RT_FALL_THRU();
+ case 0:
+ val |= 0x0200; /* 16 bit */
+ pThis->iLog2DescSize = 3;
+ pThis->GCUpperPhys = (0xff00 & (uint32_t)pThis->aCSR[2]) << 16;
+ break;
+ case 1:
+ val |= 0x0100; /* 32 bit */
+ pThis->iLog2DescSize = 4;
+ pThis->GCUpperPhys = 0;
+ break;
+ case 2:
+ case 3:
+ val |= 0x0300; /* 32 bit */
+ pThis->iLog2DescSize = 4;
+ pThis->GCUpperPhys = 0;
+ break;
+ }
+ Log(("#%d BCR_SWS=%#06x\n", PCNET_INST_NR, val));
+ pThis->aCSR[58] = val;
+ RT_FALL_THRU();
+ case BCR_LNKST:
+ case BCR_LED1:
+ case BCR_LED2:
+ case BCR_LED3:
+ case BCR_MC:
+ case BCR_FDC:
+ case BCR_BSBC:
+ case BCR_EECAS:
+ case BCR_PLAT:
+ case BCR_MIICAS:
+ case BCR_MIIADDR:
+ pThis->aBCR[u32RAP] = val;
+ break;
+
+ case BCR_STVAL:
+ val &= 0xffff;
+ pThis->aBCR[BCR_STVAL] = val;
+ if (pThis->uDevType == DEV_AM79C973)
+ PDMDevHlpTimerSetNano(pDevIns, pThis->hTimerSoftInt, 12800U * val);
+ break;
+
+ case BCR_MIIMDR:
+ pThis->aMII[pThis->aBCR[BCR_MIIADDR] & 0x1f] = val;
+ Log12(("#%d pcnet: mii write %d <- %#x\n", PCNET_INST_NR, pThis->aBCR[BCR_MIIADDR] & 0x1f, val));
+ break;
+
+ default:
+ break;
+ }
+ return VINF_SUCCESS;
+}
+
+static uint32_t pcnetMIIReadU16(PPCNETSTATE pThis, uint32_t miiaddr)
+{
+ uint32_t val;
+ bool autoneg, duplex, fast, isolate;
+ STAM_COUNTER_INC(&pThis->StatMIIReads);
+
+ /* If the DANAS (BCR32.7) bit is set, the MAC does not do any
+ * auto-negotiation and the PHY must be set up explicitly. DANAS
+ * effectively disables most other BCR32 bits.
+ */
+ if (pThis->aBCR[BCR_MIICAS] & 0x80)
+ {
+ /* PHY controls auto-negotiation. */
+ autoneg = duplex = fast = 1;
+ }
+ else
+ {
+ /* BCR32 controls auto-negotiation. */
+ autoneg = (pThis->aBCR[BCR_MIICAS] & 0x20) != 0;
+ duplex = (pThis->aBCR[BCR_MIICAS] & 0x10) != 0;
+ fast = (pThis->aBCR[BCR_MIICAS] & 0x08) != 0;
+ }
+
+ /* Electrically isolating the PHY mostly disables it. */
+ isolate = (pThis->aMII[0] & RT_BIT(10)) != 0;
+
+ switch (miiaddr)
+ {
+ case 0:
+ /* MII basic mode control register. */
+ val = 0;
+ if (autoneg)
+ val |= 0x1000; /* Enable auto negotiation. */
+ if (fast)
+ val |= 0x2000; /* 100 Mbps */
+ if (duplex) /* Full duplex forced */
+ val |= 0x0100; /* Full duplex */
+ if (isolate) /* PHY electrically isolated. */
+ val |= 0x0400; /* Isolated */
+ break;
+
+ case 1:
+ /* MII basic mode status register. */
+ val = 0x7800 /* Can do 100mbps FD/HD and 10mbps FD/HD. */
+ | 0x0040 /* Mgmt frame preamble not required. */
+ | 0x0020 /* Auto-negotiation complete. */
+ | 0x0008 /* Able to do auto-negotiation. */
+ | 0x0004 /* Link up. */
+ | 0x0001; /* Extended Capability, i.e. registers 4+ valid. */
+ if (!pcnetIsLinkUp(pThis) || isolate) {
+ val &= ~(0x0020 | 0x0004);
+ pThis->cLinkDownReported++;
+ }
+ if (!autoneg) {
+ /* Auto-negotiation disabled. */
+ val &= ~(0x0020 | 0x0008);
+ if (duplex)
+ /* Full duplex forced. */
+ val &= ~0x2800;
+ else
+ /* Half duplex forced. */
+ val &= ~0x5000;
+
+ if (fast)
+ /* 100 Mbps forced */
+ val &= ~0x1800;
+ else
+ /* 10 Mbps forced */
+ val &= ~0x6000;
+ }
+ break;
+
+ case 2:
+ /* PHY identifier 1. */
+ val = 0x22; /* Am79C874/AC101 PHY */
+ break;
+
+ case 3:
+ /* PHY identifier 2. */
+ val = 0x561b; /* Am79C874/AC101 PHY */
+ break;
+
+ case 4:
+ /* Advertisement control register. */
+ val = 0x01e0 /* Try 100mbps FD/HD and 10mbps FD/HD. */
+#if 0
+ // Advertising flow control is a) not the default, and b) confuses
+ // the link speed detection routine in Windows PCnet driver
+ | 0x0400 /* Try flow control. */
+#endif
+ | 0x0001; /* CSMA selector. */
+ break;
+
+ case 5:
+ /* Link partner ability register. */
+ if (pcnetIsLinkUp(pThis) && !isolate)
+ val = 0x8000 /* Next page bit. */
+ | 0x4000 /* Link partner acked us. */
+ | 0x0400 /* Can do flow control. */
+ | 0x01e0 /* Can do 100mbps FD/HD and 10mbps FD/HD. */
+ | 0x0001; /* Use CSMA selector. */
+ else
+ {
+ val = 0;
+ pThis->cLinkDownReported++;
+ }
+ break;
+
+ case 6:
+ /* Auto negotiation expansion register. */
+ if (pcnetIsLinkUp(pThis) && !isolate)
+ val = 0x0008 /* Link partner supports npage. */
+ | 0x0004 /* Enable npage words. */
+ | 0x0001; /* Can do N-way auto-negotiation. */
+ else
+ {
+ val = 0;
+ pThis->cLinkDownReported++;
+ }
+ break;
+
+ case 18:
+ /* Diagnostic Register (FreeBSD pcn/ac101 driver reads this). */
+ if (pcnetIsLinkUp(pThis) && !isolate)
+ {
+ val = 0x0100 /* Receive PLL locked. */
+ | 0x0200; /* Signal detected. */
+
+ if (autoneg)
+ {
+ val |= 0x0400 /* 100Mbps rate. */
+ | 0x0800; /* Full duplex. */
+ }
+ else
+ {
+ if (fast)
+ val |= 0x0400; /* 100Mbps rate. */
+ if (duplex)
+ val |= 0x0800; /* Full duplex. */
+ }
+ }
+ else
+ {
+ val = 0;
+ pThis->cLinkDownReported++;
+ }
+ break;
+
+ default:
+ val = 0;
+ break;
+ }
+
+ Log12(("#%d pcnet: mii read %d -> %#x\n", PCNET_INST_NR, miiaddr, val));
+ return val;
+}
+
+static uint32_t pcnetBCRReadU16(PPCNETSTATE pThis, uint32_t u32RAP)
+{
+ uint32_t val;
+ u32RAP &= 0x7f;
+ switch (u32RAP)
+ {
+ case BCR_LNKST:
+ case BCR_LED1:
+ case BCR_LED2:
+ case BCR_LED3:
+ val = pThis->aBCR[u32RAP] & ~0x8000;
+ /* Clear LNKSTE if we're not connected or if we've just loaded a VM state. */
+ if (!pThis->fDriverAttached || pThis->fLinkTempDown || !pThis->fLinkUp)
+ {
+ if (u32RAP == 4)
+ pThis->cLinkDownReported++;
+ val &= ~0x40;
+ }
+ /* AMD NDIS 5.0 driver programs BCR4 to indicate link state and polls
+ * the LED bit (bit 15) to determine current link status.
+ */
+ val |= (val & 0x017f & pThis->u32Lnkst) ? 0x8000 : 0;
+ break;
+
+ case BCR_MIIMDR:
+ if ((pThis->uDevType == DEV_AM79C973) && (pThis->aBCR[BCR_MIIADDR] >> 5 & 0x1f) == 0)
+ {
+ uint32_t miiaddr = pThis->aBCR[BCR_MIIADDR] & 0x1f;
+ val = pcnetMIIReadU16(pThis, miiaddr);
+ }
+ else
+ val = 0xffff;
+ break;
+
+ default:
+ val = u32RAP < BCR_MAX_RAP ? pThis->aBCR[u32RAP] : 0;
+ break;
+ }
+ Log7(("#%d pcnetBCRReadU16: rap=%d val=%#06x\n", PCNET_INST_NR, u32RAP, val));
+ return val;
+}
+
+#ifdef IN_RING3 /* move down */
+static void pcnetR3HardReset(PPDMDEVINS pDevIns, PPCNETSTATE pThis)
+{
+ int i;
+ uint16_t checksum;
+
+ /* Lower any raised interrupts, see @bugref(9556) */
+ if (RT_UNLIKELY(pThis->iISR))
+ {
+ pThis->iISR = 0;
+ if (!PCNET_IS_ISA(pThis))
+ {
+ Log(("#%d INTA=%d\n", PCNET_INST_NR, pThis->iISR));
+ PDMDevHlpPCISetIrq(pDevIns, 0, pThis->iISR);
+ }
+ else
+ {
+ Log(("#%d IRQ=%d, state=%d\n", PCNET_INST_NR, pThis->uIsaIrq, pThis->iISR));
+ PDMDevHlpISASetIrq(pDevIns, pThis->uIsaIrq, pThis->iISR);
+ }
+ }
+ /* Initialize the PROM */
+ Assert(sizeof(pThis->MacConfigured) == 6);
+ memcpy(pThis->aPROM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
+ pThis->aPROM[ 8] = 0x00;
+ pThis->aPROM[12] = pThis->aPROM[13] = 0x00;
+ if (pThis->uDevType == DEV_AM79C960_EB)
+ {
+ pThis->aPROM[14] = 0x52;
+ pThis->aPROM[15] = 0x44; /* NI6510 EtherBlaster 'RD' signature. */
+ }
+ else
+ pThis->aPROM[14] = pThis->aPROM[15] = 0x57; /* NE2100 'WW' signature. */
+
+ /* 0x00/0xFF=ISA, 0x01=PnP, 0x10=VLB, 0x11=PCI */
+ if (PCNET_IS_PCI(pThis))
+ pThis->aPROM[ 9] = 0x11;
+ else
+ pThis->aPROM[ 9] = 0x00;
+
+ for (i = 0, checksum = 0; i < 16; i++)
+ checksum += pThis->aPROM[i];
+ *(uint16_t *)&pThis->aPROM[12] = RT_H2LE_U16(checksum);
+
+ /* Many of the BCR values would normally be read from the EEPROM. */
+ pThis->aBCR[BCR_MSRDA] = 0x0005;
+ pThis->aBCR[BCR_MSWRA] = 0x0005;
+ pThis->aBCR[BCR_MC ] = 0x0002;
+ pThis->aBCR[BCR_LNKST] = 0x00c0;
+ pThis->aBCR[BCR_LED1 ] = 0x0084;
+ pThis->aBCR[BCR_LED2 ] = 0x0088;
+ pThis->aBCR[BCR_LED3 ] = 0x0090;
+ /* For ISA PnP cards, BCR8 reports IRQ/DMA (e.g. 0x0035 means IRQ 3, DMA 5). */
+ pThis->aBCR[BCR_FDC ] = 0x0000;
+ pThis->aBCR[BCR_BSBC ] = 0x9001;
+ pThis->aBCR[BCR_EECAS] = 0x0002;
+ pThis->aBCR[BCR_STVAL] = 0xffff;
+ pThis->aCSR[58 ] = /* CSR58 is an alias for BCR20 */
+ pThis->aBCR[BCR_SWS ] = 0x0200;
+ pThis->iLog2DescSize = 3;
+ pThis->aBCR[BCR_PLAT ] = 0xff06;
+ pThis->aBCR[BCR_MIICAS ] = 0x20; /* Auto-negotiation on. */
+ pThis->aBCR[BCR_MIIADDR ] = 0; /* Internal PHY on Am79C973 would be (0x1e << 5) */
+ PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
+ pThis->aBCR[BCR_PCIVID] = PCIDevGetVendorId(pPciDev);
+ pThis->aBCR[BCR_PCISID] = PCIDevGetSubSystemId(pPciDev);
+ pThis->aBCR[BCR_PCISVID] = PCIDevGetSubSystemVendorId(pPciDev);
+
+ /* Reset the error counter. */
+ pThis->uCntBadRMD = 0;
+
+ pcnetSoftReset(pThis);
+}
+#endif /* IN_RING3 */
+
+
+/* -=-=-=-=-=- APROM I/O Port access -=-=-=-=-=- */
+
+static void pcnetAPROMWriteU8(PPCNETSTATE pThis, uint32_t addr, uint32_t val)
+{
+ addr &= 0x0f;
+ val &= 0xff;
+ Log(("#%d pcnetAPROMWriteU8: addr=%#010x val=%#04x\n", PCNET_INST_NR, addr, val));
+ /* Check APROMWE bit to enable write access */
+ if (pcnetBCRReadU16(pThis, 2) & 0x80)
+ pThis->aPROM[addr] = val;
+}
+
+static uint32_t pcnetAPROMReadU8(PPCNETSTATE pThis, uint32_t addr)
+{
+ uint32_t val = pThis->aPROM[addr &= 0x0f];
+ Log(("#%d pcnetAPROMReadU8: addr=%#010x val=%#04x\n", PCNET_INST_NR, addr, val));
+ return val;
+}
+
+/**
+ * @callback_method_impl{FNIOMIOPORTIN, APROM}
+ */
+static DECLCALLBACK(VBOXSTRICTRC)
+pcnetIOPortAPromRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+ STAM_PROFILE_ADV_START(&pThis->StatAPROMRead, a);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ RT_NOREF_PV(pvUser);
+
+ /* FreeBSD is accessing in dwords. */
+ if (cb == 1)
+ *pu32 = pcnetAPROMReadU8(pThis, offPort);
+ else if (cb == 2 && !BCR_DWIO(pThis))
+ *pu32 = pcnetAPROMReadU8(pThis, offPort)
+ | (pcnetAPROMReadU8(pThis, offPort + 1) << 8);
+ else if (cb == 4 && BCR_DWIO(pThis))
+ *pu32 = pcnetAPROMReadU8(pThis, offPort)
+ | (pcnetAPROMReadU8(pThis, offPort + 1) << 8)
+ | (pcnetAPROMReadU8(pThis, offPort + 2) << 16)
+ | (pcnetAPROMReadU8(pThis, offPort + 3) << 24);
+ else
+ {
+ Log(("#%d pcnetIOPortAPromRead: offPort=%RTiop cb=%d BCR_DWIO !!\n", PCNET_INST_NR, offPort, cb));
+ rc = VERR_IOM_IOPORT_UNUSED;
+ }
+
+ STAM_PROFILE_ADV_STOP(&pThis->StatAPROMRead, a);
+ LogFlow(("#%d pcnetIOPortAPromRead: offPort=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n",
+ PCNET_INST_NR, offPort, *pu32, cb, VBOXSTRICTRC_VAL(rc)));
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT, APROM}
+ */
+static DECLCALLBACK(VBOXSTRICTRC)
+pcnetIoPortAPromWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ RT_NOREF_PV(pvUser);
+
+ if (cb == 1)
+ {
+ STAM_PROFILE_ADV_START(&pThis->StatAPROMWrite, a);
+ pcnetAPROMWriteU8(pThis, offPort, u32);
+ STAM_PROFILE_ADV_STOP(&pThis->StatAPROMWrite, a);
+ }
+ else
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "offPort=%#x cb=%d u32=%#x\n", offPort, cb, u32);
+
+ LogFlow(("#%d pcnetIoPortAPromWrite: offPort=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n",
+ PCNET_INST_NR, offPort, u32, cb, VBOXSTRICTRC_VAL(rc)));
+ return rc;
+}
+
+
+/* -=-=-=-=-=- I/O Port access -=-=-=-=-=- */
+
+
+static VBOXSTRICTRC pcnetIoPortWriteU8(PPCNETSTATE pThis, uint32_t addr, uint32_t val)
+{
+ RT_NOREF1(val);
+ Log6(("#%d pcnetIoPortWriteU8: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, val));
+ if (RT_LIKELY(!BCR_DWIO(pThis)))
+ {
+ switch (addr & 0x0f)
+ {
+ case 0x04: /* RESET */
+ break;
+ }
+ }
+ else
+ Log(("#%d pcnetIoPortWriteU8: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNET_INST_NR, addr, val));
+
+ return VINF_SUCCESS;
+}
+
+static VBOXSTRICTRC pcnetIoPortReadU8(PPDMDEVINS pDevIns, PPCNETSTATE pThis, uint32_t addr, uint32_t *val)
+{
+ *val = UINT32_MAX;
+
+ if (RT_LIKELY(!BCR_DWIO(pThis)))
+ {
+ switch (addr & 0x0f)
+ {
+ case 0x04: /* RESET */
+ pcnetSoftReset(pThis);
+ *val = 0;
+ break;
+ }
+ }
+ else
+ Log(("#%d pcnetIoPortReadU8: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNET_INST_NR, addr, *val & 0xff));
+
+ pcnetUpdateIrq(pDevIns, pThis);
+
+ Log6(("#%d pcnetIoPortReadU8: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, *val & 0xff));
+ return VINF_SUCCESS;
+}
+
+static VBOXSTRICTRC pcnetIoPortWriteU16(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, uint32_t addr, uint32_t val)
+{
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+
+ Log6(("#%d pcnetIoPortWriteU16: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, val));
+ if (RT_LIKELY(!BCR_DWIO(pThis)))
+ {
+ switch (addr & 0x0f)
+ {
+ case 0x00: /* RDP */
+ pcnetPollTimer(pDevIns, pThis, pThisCC);
+ rc = pcnetCSRWriteU16(pDevIns, pThis, pThisCC, pThis->u32RAP, val);
+ pcnetUpdateIrq(pDevIns, pThis);
+ break;
+ case 0x02: /* RAP */
+ pThis->u32RAP = val & 0x7f;
+ break;
+ case 0x06: /* BDP */
+ rc = pcnetBCRWriteU16(pDevIns, pThis, pThis->u32RAP, val);
+ break;
+ }
+ }
+ else
+ Log(("#%d pcnetIoPortWriteU16: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNET_INST_NR, addr, val));
+
+ return rc;
+}
+
+static VBOXSTRICTRC pcnetIoPortReadU16(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, uint32_t addr, uint32_t *val)
+{
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+
+ *val = ~0U;
+
+ if (RT_LIKELY(!BCR_DWIO(pThis)))
+ {
+ switch (addr & 0x0f)
+ {
+ case 0x00: /* RDP */
+ /** @note if we're not polling, then the guest will tell us when to poll by setting TDMD in CSR0 */
+ /** Polling is then useless here and possibly expensive. */
+ if (!CSR_DPOLL(pThis))
+ pcnetPollTimer(pDevIns, pThis, pThisCC);
+
+ rc = pcnetCSRReadU16(pDevIns, pThis, pThisCC, pThis->u32RAP, val);
+ if (pThis->u32RAP == 0) // pcnetUpdateIrq() already called by pcnetCSRReadU16()
+ goto skip_update_irq;
+ break;
+ case 0x02: /* RAP */
+ *val = pThis->u32RAP;
+ goto skip_update_irq;
+ case 0x04: /* RESET */
+ pcnetSoftReset(pThis);
+ *val = 0;
+ break;
+ case 0x06: /* BDP */
+ *val = pcnetBCRReadU16(pThis, pThis->u32RAP);
+ break;
+ }
+ }
+ else
+ Log(("#%d pcnetIoPortReadU16: addr=%#010x val=%#06x BCR_DWIO !!\n", PCNET_INST_NR, addr, *val & 0xffff));
+
+ pcnetUpdateIrq(pDevIns, pThis);
+
+skip_update_irq:
+ Log6(("#%d pcnetIoPortReadU16: addr=%#010x val=%#06x\n", PCNET_INST_NR, addr, *val & 0xffff));
+ return rc;
+}
+
+static VBOXSTRICTRC pcnetIoPortWriteU32(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, uint32_t addr, uint32_t val)
+{
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+
+ Log6(("#%d pcnetIoPortWriteU32: addr=%#010x val=%#010x\n", PCNET_INST_NR, addr, val));
+ if (RT_LIKELY(BCR_DWIO(pThis)))
+ {
+ switch (addr & 0x0f)
+ {
+ case 0x00: /* RDP */
+ pcnetPollTimer(pDevIns, pThis, pThisCC);
+ rc = pcnetCSRWriteU16(pDevIns, pThis, pThisCC, pThis->u32RAP, val & 0xffff);
+ pcnetUpdateIrq(pDevIns, pThis);
+ break;
+ case 0x04: /* RAP */
+ pThis->u32RAP = val & 0x7f;
+ break;
+ case 0x0c: /* BDP */
+ rc = pcnetBCRWriteU16(pDevIns, pThis, pThis->u32RAP, val & 0xffff);
+ break;
+ }
+ }
+ else if ((addr & 0x0f) == 0)
+ {
+ /* switch device to dword I/O mode */
+ pcnetBCRWriteU16(pDevIns, pThis, BCR_BSBC, pcnetBCRReadU16(pThis, BCR_BSBC) | 0x0080);
+ Log6(("device switched into dword i/o mode\n"));
+ }
+ else
+ Log(("#%d pcnetIoPortWriteU32: addr=%#010x val=%#010x !BCR_DWIO !!\n", PCNET_INST_NR, addr, val));
+
+ return rc;
+}
+
+static VBOXSTRICTRC pcnetIoPortReadU32(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, uint32_t addr, uint32_t *val)
+{
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+
+ *val = ~0U;
+
+ if (RT_LIKELY(BCR_DWIO(pThis)))
+ {
+ switch (addr & 0x0f)
+ {
+ case 0x00: /* RDP */
+ /** @note if we're not polling, then the guest will tell us when to poll by setting TDMD in CSR0 */
+ /** Polling is then useless here and possibly expensive. */
+ if (!CSR_DPOLL(pThis))
+ pcnetPollTimer(pDevIns, pThis, pThisCC);
+
+ rc = pcnetCSRReadU16(pDevIns, pThis, pThisCC, pThis->u32RAP, val);
+ if (pThis->u32RAP == 0) // pcnetUpdateIrq() already called by pcnetCSRReadU16()
+ goto skip_update_irq;
+ break;
+ case 0x04: /* RAP */
+ *val = pThis->u32RAP;
+ goto skip_update_irq;
+ case 0x08: /* RESET */
+ pcnetSoftReset(pThis);
+ *val = 0;
+ break;
+ case 0x0c: /* BDP */
+ *val = pcnetBCRReadU16(pThis, pThis->u32RAP);
+ break;
+ }
+ }
+ else
+ Log(("#%d pcnetIoPortReadU32: addr=%#010x val=%#010x !BCR_DWIO !!\n", PCNET_INST_NR, addr, *val));
+ pcnetUpdateIrq(pDevIns, pThis);
+
+skip_update_irq:
+ Log6(("#%d pcnetIoPortReadU32: addr=%#010x val=%#010x\n", PCNET_INST_NR, addr, *val));
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTIN}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) pcnetIoPortRead(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t *pu32, unsigned cb)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
+ VBOXSTRICTRC rc;
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIORead), a);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ RT_NOREF_PV(pvUser);
+
+ switch (cb)
+ {
+ case 1: rc = pcnetIoPortReadU8(pDevIns, pThis, offPort, pu32); break;
+ case 2: rc = pcnetIoPortReadU16(pDevIns, pThis, pThisCC, offPort, pu32); break;
+ case 4: rc = pcnetIoPortReadU32(pDevIns, pThis, pThisCC, offPort, pu32); break;
+ default:
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "pcnetIoPortRead: unsupported op size: offset=%#10x cb=%u\n", offPort, cb);
+ }
+
+ Log2(("#%d pcnetIoPortRead: offPort=%RTiop *pu32=%#RX32 cb=%d rc=%Rrc\n", PCNET_INST_NR, offPort, *pu32, cb, VBOXSTRICTRC_VAL(rc)));
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIORead), a);
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMIOPORTOUT}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) pcnetIoPortWrite(PPDMDEVINS pDevIns, void *pvUser, RTIOPORT offPort, uint32_t u32, unsigned cb)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
+ VBOXSTRICTRC rc;
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatIOWrite), a);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ RT_NOREF_PV(pvUser);
+
+ switch (cb)
+ {
+ case 1: rc = pcnetIoPortWriteU8(pThis, offPort, u32); break;
+ case 2: rc = pcnetIoPortWriteU16(pDevIns, pThis, pThisCC, offPort, u32); break;
+ case 4: rc = pcnetIoPortWriteU32(pDevIns, pThis, pThisCC, offPort, u32); break;
+ default:
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "pcnetIoPortWrite: unsupported op size: offset=%#10x cb=%u\n", offPort, cb);
+ }
+
+ Log2(("#%d pcnetIoPortWrite: offPort=%RTiop u32=%#RX32 cb=%d rc=%Rrc\n", PCNET_INST_NR, offPort, u32, cb, VBOXSTRICTRC_VAL(rc)));
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatIOWrite), a);
+ return rc;
+}
+
+
+/* -=-=-=-=-=- MMIO -=-=-=-=-=- */
+
+#ifdef IN_RING3
+
+static void pcnetR3MmioWriteU8(PPCNETSTATE pThis, RTGCPHYS off, uint32_t val)
+{
+ Log6(("#%d pcnetR3MmioWriteU8: off=%#010x val=%#04x\n", PCNET_INST_NR, off, val));
+ if (!(off & 0x10))
+ pcnetAPROMWriteU8(pThis, off, val);
+}
+
+static VBOXSTRICTRC pcnetR3MmioReadU8(PPCNETSTATE pThis, RTGCPHYS addr, uint8_t *val)
+{
+ *val = 0xff;
+ if (!(addr & 0x10))
+ *val = pcnetAPROMReadU8(pThis, addr);
+ Log6(("#%d pcnetR3MmioReadU8: addr=%#010x val=%#04x\n", PCNET_INST_NR, addr, *val));
+ return VINF_SUCCESS;
+}
+
+static VBOXSTRICTRC pcnetR3MmioWriteU16(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, RTGCPHYS off, uint32_t val)
+{
+ VBOXSTRICTRC rcStrict;
+ Log6(("#%d pcnetR3MmioWriteU16: off=%#010x val=%#06x\n", PCNET_INST_NR, off, val));
+ if (off & 0x10)
+ {
+ rcStrict = pcnetIoPortWriteU16(pDevIns, pThis, pThisCC, off & 0x0f, val);
+ if (rcStrict == VINF_IOM_R3_IOPORT_WRITE)
+ rcStrict = VINF_IOM_R3_MMIO_WRITE;
+ }
+ else
+ {
+ pcnetAPROMWriteU8(pThis, off, val );
+ pcnetAPROMWriteU8(pThis, off + 1, val >> 8);
+ rcStrict = VINF_SUCCESS;
+ }
+ return rcStrict;
+}
+
+static VBOXSTRICTRC pcnetR3MmioReadU16(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, RTGCPHYS addr, uint16_t *val)
+{
+ VBOXSTRICTRC rcStrict;
+ uint32_t val32 = ~0U;
+
+ if (addr & 0x10)
+ {
+ rcStrict = pcnetIoPortReadU16(pDevIns, pThis, pThisCC, addr & 0x0f, &val32);
+ if (rcStrict == VINF_IOM_R3_IOPORT_READ)
+ rcStrict = VINF_IOM_R3_MMIO_READ;
+ }
+ else
+ {
+ val32 = pcnetAPROMReadU8(pThis, addr+1);
+ val32 <<= 8;
+ val32 |= pcnetAPROMReadU8(pThis, addr);
+ rcStrict = VINF_SUCCESS;
+ }
+ *val = (uint16_t)val32;
+ Log6(("#%d pcnetR3MmioReadU16: addr=%#010x val = %#06x\n", PCNET_INST_NR, addr, *val));
+ return rcStrict;
+}
+
+static VBOXSTRICTRC pcnetR3MmioWriteU32(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, RTGCPHYS off, uint32_t val)
+{
+ VBOXSTRICTRC rcStrict;
+ Log6(("#%d pcnetR3MmioWriteU32: off=%#010x val=%#010x\n", PCNET_INST_NR, off, val));
+ if (off & 0x10)
+ {
+ rcStrict = pcnetIoPortWriteU32(pDevIns, pThis, pThisCC, off & 0x0f, val);
+ if (rcStrict == VINF_IOM_R3_IOPORT_WRITE)
+ rcStrict = VINF_IOM_R3_MMIO_WRITE;
+ }
+ else
+ {
+ pcnetAPROMWriteU8(pThis, off, val );
+ pcnetAPROMWriteU8(pThis, off + 1, val >> 8);
+ pcnetAPROMWriteU8(pThis, off + 2, val >> 16);
+ pcnetAPROMWriteU8(pThis, off + 3, val >> 24);
+ rcStrict = VINF_SUCCESS;
+ }
+ return rcStrict;
+}
+
+static VBOXSTRICTRC pcnetR3MmioReadU32(PPDMDEVINS pDevIns, PPCNETSTATE pThis, PPCNETSTATECC pThisCC, RTGCPHYS addr, uint32_t *val)
+{
+ VBOXSTRICTRC rcStrict;
+
+ if (addr & 0x10)
+ {
+ rcStrict = pcnetIoPortReadU32(pDevIns, pThis, pThisCC, addr & 0x0f, val);
+ if (rcStrict == VINF_IOM_R3_IOPORT_READ)
+ rcStrict = VINF_IOM_R3_MMIO_READ;
+ }
+ else
+ {
+ uint32_t val32;
+
+ val32 = pcnetAPROMReadU8(pThis, addr+3);
+ val32 <<= 8;
+ val32 |= pcnetAPROMReadU8(pThis, addr+2);
+ val32 <<= 8;
+ val32 |= pcnetAPROMReadU8(pThis, addr+1);
+ val32 <<= 8;
+ val32 |= pcnetAPROMReadU8(pThis, addr );
+ *val = val32;
+ rcStrict = VINF_SUCCESS;
+ }
+ Log6(("#%d pcnetR3MmioReadU32: addr=%#010x val=%#010x\n", PCNET_INST_NR, addr, *val));
+ return rcStrict;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMMMIONEWREAD}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) pcnetR3MmioRead(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void *pv, unsigned cb)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ RT_NOREF_PV(pvUser);
+
+ /*
+ * We have to check the range, because we're page aligning the MMIO.
+ */
+ if (off < PCNET_PNPMMIO_SIZE)
+ {
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatMMIORead), a);
+ switch (cb)
+ {
+ case 1: rc = pcnetR3MmioReadU8 (pThis, off, (uint8_t *)pv); break;
+ case 2: rc = pcnetR3MmioReadU16(pDevIns, pThis, pThisCC, off, (uint16_t *)pv); break;
+ case 4: rc = pcnetR3MmioReadU32(pDevIns, pThis, pThisCC, off, (uint32_t *)pv); break;
+ default:
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "pcnetR3MmioRead: unsupported op size: address=%RGp cb=%u\n", off, cb);
+ }
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatMMIORead), a);
+ }
+ else
+ memset(pv, 0, cb);
+
+ LogFlow(("#%d pcnetR3MmioRead: pvUser=%p:{%.*Rhxs} cb=%d off=%RGp rc=%Rrc\n",
+ PCNET_INST_NR, pv, cb, pv, cb, off, VBOXSTRICTRC_VAL(rc)));
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNIOMMMIONEWWRITE}
+ */
+static DECLCALLBACK(VBOXSTRICTRC) pcnetR3MmioWrite(PPDMDEVINS pDevIns, void *pvUser, RTGCPHYS off, void const *pv, unsigned cb)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
+ VBOXSTRICTRC rc = VINF_SUCCESS;
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ RT_NOREF_PV(pvUser);
+
+ /*
+ * We have to check the range, because we're page aligning the MMIO stuff presently.
+ */
+ if (off < PCNET_PNPMMIO_SIZE)
+ {
+ STAM_PROFILE_ADV_START(&pThis->CTX_SUFF_Z(StatMMIOWrite), a);
+ switch (cb)
+ {
+ case 1: pcnetR3MmioWriteU8(pThis, off, *(uint8_t *)pv); break;
+ case 2: rc = pcnetR3MmioWriteU16(pDevIns, pThis, pThisCC, off, *(uint16_t *)pv); break;
+ case 4: rc = pcnetR3MmioWriteU32(pDevIns, pThis, pThisCC, off, *(uint32_t *)pv); break;
+ default:
+ rc = PDMDevHlpDBGFStop(pDevIns, RT_SRC_POS, "pcnetR3MmioWrite: unsupported op size: address=%RGp cb=%u\n", off, cb);
+ }
+
+ STAM_PROFILE_ADV_STOP(&pThis->CTX_SUFF_Z(StatMMIOWrite), a);
+ }
+ LogFlow(("#%d pcnetR3MmioWrite: pvUser=%p:{%.*Rhxs} cb=%d off=%RGp rc=%Rrc\n",
+ PCNET_INST_NR, pv, cb, pv, cb, off, VBOXSTRICTRC_VAL(rc)));
+ return rc;
+}
+
+
+
+/* -=-=-=-=-=- Timer Callbacks -=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNTMTIMERDEV, Poll timer}
+ */
+static DECLCALLBACK(void) pcnetR3Timer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ Assert(hTimer == pThis->hTimerPoll); RT_NOREF(pvUser, hTimer);
+
+ STAM_PROFILE_ADV_START(&pThis->StatTimer, a);
+ pcnetPollTimer(pDevIns, pThis, pThisCC);
+ STAM_PROFILE_ADV_STOP(&pThis->StatTimer, a);
+}
+
+
+/**
+ * @callback_method_impl{FNTMTIMERDEV,
+ * Software interrupt timer callback function.}
+ */
+static DECLCALLBACK(void) pcnetR3TimerSoftInt(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ Assert(PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ Assert(hTimer == pThis->hTimerSoftInt); RT_NOREF(pvUser, hTimer);
+
+ pThis->aCSR[7] |= 0x0800; /* STINT */
+ pcnetUpdateIrq(pDevIns, pThis);
+ PDMDevHlpTimerSetNano(pDevIns, pThis->hTimerSoftInt, 12800U * (pThis->aBCR[BCR_STVAL] & 0xffff));
+}
+
+
+/**
+ * @callback_method_impl{FNTMTIMERDEV, Restore timer callback}
+ *
+ * This is only called when we restore a saved state and temporarily
+ * disconnected the network link to inform the guest that network connections
+ * should be considered lost.
+ */
+static DECLCALLBACK(void) pcnetR3TimerRestore(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ Assert(hTimer == pThis->hTimerRestore); RT_NOREF(pvUser);
+
+ int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc);
+
+ rc = VERR_GENERAL_FAILURE;
+
+ /* 10 Mbps models (up to and including Am79C970A) have no MII and no way to get
+ * an MII management auto-poll interrupt (MAPINT) indicating link state changes.
+ * In some cases we want to make sure the guest really noticed the link going down;
+ * the cLinkDownReported counter is incremented every time the guest did something
+ * that might have made it notice the link loss, and we only bring the link back
+ * up once we're reasonably certain the guest knows it was down.
+ */
+ if (pThis->cLinkDownReported <= PCNET_MAX_LINKDOWN_REPORTED)
+ {
+ rc = PDMDevHlpTimerSetMillies(pDevIns, hTimer, 1500);
+ AssertRC(rc);
+ }
+ if (RT_FAILURE(rc))
+ {
+ pThis->fLinkTempDown = false;
+ if (pThis->fLinkUp)
+ {
+ LogRel(("PCnet#%d: The link is back up again after the restore.\n", pDevIns->iInstance));
+ Log(("#%d pcnetR3TimerRestore: Clearing ERR and CERR after load. cLinkDownReported=%d\n",
+ pDevIns->iInstance, pThis->cLinkDownReported));
+ pThis->aCSR[0] &= ~(RT_BIT(15) | RT_BIT(13)); /* ERR | CERR - probably not 100% correct either... */
+ pThis->Led.Actual.s.fError = 0;
+ }
+ }
+ else
+ Log(("#%d pcnetR3TimerRestore: cLinkDownReported=%d, wait another 1500ms...\n",
+ pDevIns->iInstance, pThis->cLinkDownReported));
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+}
+
+
+/* -=-=-=-=-=- PCI Device Callbacks -=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNPCIIOREGIONMAP, For the PCnet I/O Ports.}
+ */
+static DECLCALLBACK(int) pcnetR3PciMapUnmapIoPorts(PPDMDEVINS pDevIns, PPDMPCIDEV pPciDev, uint32_t iRegion,
+ RTGCPHYS GCPhysAddress, RTGCPHYS cb, PCIADDRESSSPACE enmType)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ int rc;
+ RT_NOREF(iRegion, cb, enmType, pPciDev);
+
+ Assert(pDevIns->apPciDevs[0] == pPciDev);
+ Assert(enmType == PCI_ADDRESS_SPACE_IO);
+ Assert(cb >= 0x20);
+
+ if (GCPhysAddress != NIL_RTGCPHYS)
+ {
+ RTIOPORT Port = (RTIOPORT)GCPhysAddress;
+ rc = PDMDevHlpIoPortMap(pDevIns, pThis->hIoPortsPciAProm, Port);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpIoPortMap(pDevIns, pThis->hIoPortsPci, Port + 0x10);
+ AssertRCReturn(rc, rc);
+ pThis->IOPortBase = Port;
+ }
+ else
+ {
+ rc = PDMDevHlpIoPortUnmap(pDevIns, pThis->hIoPortsPciAProm);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpIoPortUnmap(pDevIns, pThis->hIoPortsPci);
+ AssertRCReturn(rc, rc);
+ pThis->IOPortBase = 0;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/* -=-=-=-=-=- Debug Info Handler -=-=-=-=-=- */
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV}
+ */
+static DECLCALLBACK(void) pcnetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ bool fRcvRing = false;
+ bool fXmtRing = false;
+ bool fAPROM = false;
+
+ /*
+ * Parse args.
+ */
+ if (pszArgs)
+ {
+ fRcvRing = strstr(pszArgs, "verbose") || strstr(pszArgs, "rcv");
+ fXmtRing = strstr(pszArgs, "verbose") || strstr(pszArgs, "xmt");
+ fAPROM = strstr(pszArgs, "verbose") || strstr(pszArgs, "aprom");
+ }
+
+ /*
+ * Show info.
+ */
+ const char *pcszModel;
+ switch (pThis->uDevType)
+ {
+ case DEV_AM79C970A: pcszModel = "AM79C970A"; break;
+ case DEV_AM79C973: pcszModel = "AM79C973"; break;
+ case DEV_AM79C960: pcszModel = "AM79C960/NE2100"; break;
+ case DEV_AM79C960_EB: pcszModel = "AM79C960/EtherBlaster"; break;
+ default: pcszModel = "Unknown"; break;
+ }
+ pHlp->pfnPrintf(pHlp,
+ "pcnet #%d: port=%RTiop", pDevIns->iInstance, pThis->IOPortBase);
+ if (PCNET_IS_ISA(pThis))
+ pHlp->pfnPrintf(pHlp, " irq=%RX32", pThis->uIsaIrq);
+ else
+ pHlp->pfnPrintf(pHlp, " mmio=%RX32", PDMDevHlpMmioGetMappingAddress(pDevIns, pThis->hMmioPci));
+
+ pHlp->pfnPrintf(pHlp,
+ " mac-cfg=%RTmac %s%s%s\n",
+ &pThis->MacConfigured, pcszModel, pDevIns->fRCEnabled ? " RC" : "", pDevIns->fR0Enabled ? " R0" : "");
+
+
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_INTERNAL_ERROR); /* Take it here so we know why we're hanging... */
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
+
+ pHlp->pfnPrintf(pHlp,
+ "CSR0=%#06x: INIT=%d STRT=%d STOP=%d TDMD=%d TXON=%d RXON=%d IENA=%d INTR=%d IDON=%d TINT=%d RINT=%d MERR=%d\n"
+ " MISS=%d CERR=%d BABL=%d ERR=%d\n",
+ pThis->aCSR[0],
+ !!(pThis->aCSR[0] & RT_BIT(0)), !!(pThis->aCSR[0] & RT_BIT(1)), !!(pThis->aCSR[0] & RT_BIT(2)), !!(pThis->aCSR[0] & RT_BIT(3)), !!(pThis->aCSR[0] & RT_BIT(4)),
+ !!(pThis->aCSR[0] & RT_BIT(5)), !!(pThis->aCSR[0] & RT_BIT(6)), !!(pThis->aCSR[0] & RT_BIT(7)), !!(pThis->aCSR[0] & RT_BIT(8)), !!(pThis->aCSR[0] & RT_BIT(9)),
+ !!(pThis->aCSR[0] & RT_BIT(10)), !!(pThis->aCSR[0] & RT_BIT(11)), !!(pThis->aCSR[0] & RT_BIT(12)), !!(pThis->aCSR[0] & RT_BIT(13)),
+ !!(pThis->aCSR[0] & RT_BIT(14)), !!(pThis->aCSR[0] & RT_BIT(15)));
+
+ pHlp->pfnPrintf(pHlp,
+ "CSR1=%#06x:\n",
+ pThis->aCSR[1]);
+
+ pHlp->pfnPrintf(pHlp,
+ "CSR2=%#06x:\n",
+ pThis->aCSR[2]);
+
+ pHlp->pfnPrintf(pHlp,
+ "CSR3=%#06x: BSWP=%d EMBA=%d DXMT2PD=%d LAPPEN=%d DXSUFLO=%d IDONM=%d TINTM=%d RINTM=%d MERRM=%d MISSM=%d BABLM=%d\n",
+ pThis->aCSR[3],
+ !!(pThis->aCSR[3] & RT_BIT(2)), !!(pThis->aCSR[3] & RT_BIT(3)), !!(pThis->aCSR[3] & RT_BIT(4)), CSR_LAPPEN(pThis),
+ CSR_DXSUFLO(pThis), !!(pThis->aCSR[3] & RT_BIT(8)), !!(pThis->aCSR[3] & RT_BIT(9)), !!(pThis->aCSR[3] & RT_BIT(10)),
+ !!(pThis->aCSR[3] & RT_BIT(11)), !!(pThis->aCSR[3] & RT_BIT(12)), !!(pThis->aCSR[3] & RT_BIT(14)));
+
+ pHlp->pfnPrintf(pHlp,
+ "CSR4=%#06x: JABM=%d JAB=%d TXSTRM=%d TXSTRT=%d RCVCOOM=%d RCVCCO=%d UINT=%d UINTCMD=%d\n"
+ " MFCOM=%d MFCO=%d ASTRP_RCV=%d APAD_XMT=%d DPOLL=%d TIMER=%d EMAPLUS=%d EN124=%d\n",
+ pThis->aCSR[4],
+ !!(pThis->aCSR[4] & RT_BIT( 0)), !!(pThis->aCSR[4] & RT_BIT( 1)), !!(pThis->aCSR[4] & RT_BIT( 2)), !!(pThis->aCSR[4] & RT_BIT( 3)),
+ !!(pThis->aCSR[4] & RT_BIT( 4)), !!(pThis->aCSR[4] & RT_BIT( 5)), !!(pThis->aCSR[4] & RT_BIT( 6)), !!(pThis->aCSR[4] & RT_BIT( 7)),
+ !!(pThis->aCSR[4] & RT_BIT( 8)), !!(pThis->aCSR[4] & RT_BIT( 9)), !!(pThis->aCSR[4] & RT_BIT(10)), !!(pThis->aCSR[4] & RT_BIT(11)),
+ !!(pThis->aCSR[4] & RT_BIT(12)), !!(pThis->aCSR[4] & RT_BIT(13)), !!(pThis->aCSR[4] & RT_BIT(14)), !!(pThis->aCSR[4] & RT_BIT(15)));
+
+ pHlp->pfnPrintf(pHlp,
+ "CSR5=%#06x:\n",
+ pThis->aCSR[5]);
+
+ pHlp->pfnPrintf(pHlp,
+ "CSR6=%#06x: RLEN=%#x* TLEN=%#x* [* encoded]\n",
+ pThis->aCSR[6],
+ (pThis->aCSR[6] >> 8) & 0xf, (pThis->aCSR[6] >> 12) & 0xf);
+
+ pHlp->pfnPrintf(pHlp,
+ "CSR8..11=%#06x,%#06x,%#06x,%#06x: LADRF=%#018llx\n",
+ pThis->aCSR[8], pThis->aCSR[9], pThis->aCSR[10], pThis->aCSR[11],
+ (uint64_t)(pThis->aCSR[ 8] & 0xffff)
+ | (uint64_t)(pThis->aCSR[ 9] & 0xffff) << 16
+ | (uint64_t)(pThis->aCSR[10] & 0xffff) << 32
+ | (uint64_t)(pThis->aCSR[11] & 0xffff) << 48);
+
+ pHlp->pfnPrintf(pHlp,
+ "CSR12..14=%#06x,%#06x,%#06x: PADR=%02x:%02x:%02x:%02x:%02x:%02x (Current MAC Address)\n",
+ pThis->aCSR[12], pThis->aCSR[13], pThis->aCSR[14],
+ pThis->aCSR[12] & 0xff,
+ (pThis->aCSR[12] >> 8) & 0xff,
+ pThis->aCSR[13] & 0xff,
+ (pThis->aCSR[13] >> 8) & 0xff,
+ pThis->aCSR[14] & 0xff,
+ (pThis->aCSR[14] >> 8) & 0xff);
+
+ pHlp->pfnPrintf(pHlp,
+ "CSR15=%#06x: DXR=%d DTX=%d LOOP=%d DXMTFCS=%d FCOLL=%d DRTY=%d INTL=%d PORTSEL=%d LTR=%d\n"
+ " MENDECL=%d DAPC=%d DLNKTST=%d DRCVPV=%d DRCVBC=%d PROM=%d\n",
+ pThis->aCSR[15],
+ !!(pThis->aCSR[15] & RT_BIT( 0)), !!(pThis->aCSR[15] & RT_BIT( 1)), !!(pThis->aCSR[15] & RT_BIT( 2)), !!(pThis->aCSR[15] & RT_BIT( 3)),
+ !!(pThis->aCSR[15] & RT_BIT( 4)), !!(pThis->aCSR[15] & RT_BIT( 5)), !!(pThis->aCSR[15] & RT_BIT( 6)), (pThis->aCSR[15] >> 7) & 3,
+ !!(pThis->aCSR[15] & RT_BIT( 9)), !!(pThis->aCSR[15] & RT_BIT(10)), !!(pThis->aCSR[15] & RT_BIT(11)),
+ !!(pThis->aCSR[15] & RT_BIT(12)), !!(pThis->aCSR[15] & RT_BIT(13)), !!(pThis->aCSR[15] & RT_BIT(14)), !!(pThis->aCSR[15] & RT_BIT(15)));
+
+ pHlp->pfnPrintf(pHlp,
+ "CSR46=%#06x: POLL=%#06x (Poll Time Counter)\n",
+ pThis->aCSR[46], pThis->aCSR[46] & 0xffff);
+
+ pHlp->pfnPrintf(pHlp,
+ "CSR47=%#06x: POLLINT=%#06x (Poll Time Interval)\n",
+ pThis->aCSR[47], pThis->aCSR[47] & 0xffff);
+
+ pHlp->pfnPrintf(pHlp,
+ "CSR58=%#06x: SWSTYLE=%d %s SSIZE32=%d CSRPCNET=%d APERRENT=%d\n",
+ pThis->aCSR[58],
+ pThis->aCSR[58] & 0x7f,
+ (pThis->aCSR[58] & 0x7f) == 0 ? "C-LANCE / PCnet-ISA"
+ : (pThis->aCSR[58] & 0x7f) == 1 ? "ILACC"
+ : (pThis->aCSR[58] & 0x7f) == 2 ? "PCnet-32"
+ : (pThis->aCSR[58] & 0x7f) == 3 ? "PCnet-PCI II"
+ : "!!reserved!!",
+ !!(pThis->aCSR[58] & RT_BIT(8)), !!(pThis->aCSR[58] & RT_BIT(9)), !!(pThis->aCSR[58] & RT_BIT(10)));
+
+ pHlp->pfnPrintf(pHlp,
+ "CSR112=%04RX32: MFC=%04x (Missed receive Frame Count)\n",
+ pThis->aCSR[112], pThis->aCSR[112] & 0xffff);
+
+ pHlp->pfnPrintf(pHlp,
+ "CSR122=%04RX32: RCVALGN=%04x (Receive Frame Align)\n",
+ pThis->aCSR[122], !!(pThis->aCSR[122] & RT_BIT(0)));
+
+ pHlp->pfnPrintf(pHlp,
+ "CSR124=%04RX32: RPA=%04x (Runt Packet Accept)\n",
+ pThis->aCSR[122], !!(pThis->aCSR[122] & RT_BIT(3)));
+
+ if (pThis->uDevType == DEV_AM79C970A || pThis->uDevType == DEV_AM79C973)
+ {
+ /* Print the Burst and Bus Control Register; the DWIO bit is quite important. */
+ pHlp->pfnPrintf(pHlp,
+ "BCR18=%#04x: ROMTMG=%u MEMCMD=%u EXTREQ=%u\n"
+ " DWIO=%u BREADE=%u BWRITE=%u\n",
+ pThis->aBCR[18],
+ (pThis->aBCR[18] >> 12) & 0xf, !!(pThis->aBCR[18] & RT_BIT(9)), !!(pThis->aBCR[18] & RT_BIT(8)),
+ !!(pThis->aBCR[18] & RT_BIT(7)), !!(pThis->aBCR[18] & RT_BIT(6)), !!(pThis->aBCR[18] & RT_BIT(5)));
+ }
+
+ if (pThis->uDevType == DEV_AM79C973)
+ {
+ /* Print a bit of the MII state. */
+ pHlp->pfnPrintf(pHlp,
+ "BCR32=%#06x: MIIILP=%d XPHYSP=%d XPHYFD=%d XPHYANE=%d XPHYRST=%d\n"
+ " DANAS=%d APDW=%u APEP=%d FMDC=%u MIIPD=%d ANTST=%d\n",
+ pThis->aBCR[32],
+ !!(pThis->aBCR[32] & RT_BIT( 1)), !!(pThis->aBCR[32] & RT_BIT( 3)), !!(pThis->aBCR[32] & RT_BIT( 4)),
+ !!(pThis->aBCR[32] & RT_BIT( 5)), !!(pThis->aBCR[32] & RT_BIT( 6)), !!(pThis->aBCR[32] & RT_BIT( 7)),
+ (pThis->aBCR[32] >> 8) & 0x7,
+ !!(pThis->aBCR[32] & RT_BIT(11)),
+ (pThis->aBCR[32] >> 12) & 0x3,
+ !!(pThis->aBCR[32] & RT_BIT(14)), !!(pThis->aBCR[32] & RT_BIT(15)));
+ }
+
+ /*
+ * Dump the receive ring.
+ */
+ pHlp->pfnPrintf(pHlp,
+ "RCVRL=%04x RCVRC=%04x GCRDRA=%RX32 \n"
+ "CRDA=%08RX32 CRBA=%08RX32 CRBC=%03x CRST=%04x\n"
+ "NRDA=%08RX32 NRBA=%08RX32 NRBC=%03x NRST=%04x\n"
+ "NNRDA=%08RX32\n"
+ ,
+ CSR_RCVRL(pThis), CSR_RCVRC(pThis), pThis->GCRDRA,
+ CSR_CRDA(pThis), CSR_CRBA(pThis), CSR_CRBC(pThis), CSR_CRST(pThis),
+ CSR_NRDA(pThis), CSR_NRBA(pThis), CSR_NRBC(pThis), CSR_NRST(pThis),
+ CSR_NNRD(pThis));
+ if (fRcvRing)
+ {
+ const unsigned cb = 1 << pThis->iLog2DescSize;
+ RTGCPHYS32 GCPhys = pThis->GCRDRA;
+ unsigned i = CSR_RCVRL(pThis);
+ while (i-- > 0)
+ {
+ RMD rmd;
+ pcnetRmdLoad(pDevIns, pThis, &rmd, PHYSADDR(pThis, GCPhys), false);
+ pHlp->pfnPrintf(pHlp,
+ "%04x %RX32:%c%c RBADR=%08RX32 BCNT=%03x MCNT=%03x "
+ "OWN=%d ERR=%d FRAM=%d OFLO=%d CRC=%d BUFF=%d STP=%d ENP=%d BPE=%d "
+ "PAM=%d LAFM=%d BAM=%d RCC=%02x RPC=%02x ONES=%#x ZEROS=%d\n",
+ i, GCPhys, i + 1 == CSR_RCVRC(pThis) ? '*' : ' ', GCPhys == CSR_CRDA(pThis) ? '*' : ' ',
+ rmd.rmd0.rbadr, 4096 - rmd.rmd1.bcnt, rmd.rmd2.mcnt,
+ rmd.rmd1.own, rmd.rmd1.err, rmd.rmd1.fram, rmd.rmd1.oflo, rmd.rmd1.crc, rmd.rmd1.buff,
+ rmd.rmd1.stp, rmd.rmd1.enp, rmd.rmd1.bpe,
+ rmd.rmd1.pam, rmd.rmd1.lafm, rmd.rmd1.bam, rmd.rmd2.rcc, rmd.rmd2.rpc,
+ rmd.rmd1.ones, rmd.rmd2.zeros);
+
+ GCPhys += cb;
+ }
+ }
+
+ /*
+ * Dump the transmit ring.
+ */
+ pHlp->pfnPrintf(pHlp,
+ "XMTRL=%04x XMTRC=%04x GCTDRA=%08RX32 BADX=%08RX32\n"
+ "PXDA=%08RX32 PXBC=%03x PXST=%04x\n"
+ "CXDA=%08RX32 CXBA=%08RX32 CXBC=%03x CXST=%04x\n"
+ "NXDA=%08RX32 NXBA=%08RX32 NXBC=%03x NXST=%04x\n"
+ "NNXDA=%08RX32\n"
+ ,
+ CSR_XMTRL(pThis), CSR_XMTRC(pThis),
+ pThis->GCTDRA, CSR_BADX(pThis),
+ CSR_PXDA(pThis), CSR_PXBC(pThis), CSR_PXST(pThis),
+ CSR_CXDA(pThis), CSR_CXBA(pThis), CSR_CXBC(pThis), CSR_CXST(pThis),
+ CSR_NXDA(pThis), CSR_NXBA(pThis), CSR_NXBC(pThis), CSR_NXST(pThis),
+ CSR_NNXD(pThis));
+ if (fXmtRing)
+ {
+ const unsigned cb = 1 << pThis->iLog2DescSize;
+ RTGCPHYS32 GCPhys = pThis->GCTDRA;
+ unsigned i = CSR_XMTRL(pThis);
+ while (i-- > 0)
+ {
+ TMD tmd;
+ pcnetTmdLoadAll(pDevIns, pThis, &tmd, PHYSADDR(pThis, GCPhys));
+ pHlp->pfnPrintf(pHlp,
+ "%04x %RX32:%c%c TBADR=%08RX32 BCNT=%03x OWN=%d "
+ "ERR=%d NOFCS=%d LTINT=%d ONE=%d DEF=%d STP=%d ENP=%d BPE=%d "
+ "BUFF=%d UFLO=%d EXDEF=%d LCOL=%d LCAR=%d RTRY=%d TDR=%03x TRC=%#x ONES=%#x\n"
+ ,
+ i,
+ GCPhys,
+ i + 1 == CSR_XMTRC(pThis) ? '*' : ' ',
+ GCPhys == CSR_CXDA(pThis) ? '*' : ' ',
+ tmd.tmd0.tbadr,
+ 4096 - tmd.tmd1.bcnt,
+ tmd.tmd1.own,
+ tmd.tmd1.err,
+ tmd.tmd1.nofcs,
+ tmd.tmd1.ltint,
+ tmd.tmd1.one,
+ tmd.tmd1.def,
+ tmd.tmd1.stp,
+ tmd.tmd1.enp,
+ tmd.tmd1.bpe,
+ tmd.tmd2.buff,
+ tmd.tmd2.uflo,
+ tmd.tmd2.exdef,
+ tmd.tmd2.lcol,
+ tmd.tmd2.lcar,
+ tmd.tmd2.rtry,
+ tmd.tmd2.tdr,
+ tmd.tmd2.trc,
+ tmd.tmd1.ones);
+
+ GCPhys += cb;
+ }
+ }
+
+ /* Dump the Addres PROM (APROM). */
+ if (fAPROM)
+ {
+ pHlp->pfnPrintf(pHlp,
+ "Address PROM:\n %Rhxs\n", pThis->aPROM);
+
+
+ }
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+}
+
+
+/* -=-=-=-=-=- Helper(s) -=-=-=-=-=- */
+
+/**
+ * Takes down the link temporarily if it's current status is up.
+ *
+ * This is used during restore and when replumbing the network link.
+ *
+ * The temporary link outage is supposed to indicate to the OS that all network
+ * connections have been lost and that it for instance is appropriate to
+ * renegotiate any DHCP lease.
+ *
+ * @param pThis The PCnet shared instance data.
+ */
+static void pcnetR3TempLinkDown(PPDMDEVINS pDevIns, PPCNETSTATE pThis)
+{
+ if (pThis->fLinkUp)
+ {
+ pThis->fLinkTempDown = true;
+ pThis->cLinkDownReported = 0;
+ pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR (this is probably wrong) */
+ pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
+ int rc = PDMDevHlpTimerSetMillies(pDevIns, pThis->hTimerRestore, pThis->cMsLinkUpDelay);
+ AssertRC(rc);
+ }
+}
+
+
+/* -=-=-=-=-=- Saved State -=-=-=-=-=- */
+
+/**
+ * Saves the configuration.
+ *
+ * @param pHlp The device helpers.
+ * @param pThis The PCnet shared instance data.
+ * @param pSSM The saved state handle.
+ */
+static void pcnetR3SaveConfig(PCPDMDEVHLPR3 pHlp, PPCNETSTATE pThis, PSSMHANDLE pSSM)
+{
+ pHlp->pfnSSMPutMem(pSSM, &pThis->MacConfigured, sizeof(pThis->MacConfigured));
+ pHlp->pfnSSMPutU8(pSSM, pThis->uDevType);
+ pHlp->pfnSSMPutU32(pSSM, pThis->u32LinkSpeed);
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVLIVEEXEC, Pass 0 only.}
+ */
+static DECLCALLBACK(int) pcnetR3LiveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uPass)
+{
+ RT_NOREF(uPass);
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ pcnetR3SaveConfig(pDevIns->pHlpR3, pThis, pSSM);
+ return VINF_SSM_DONT_CALL_AGAIN;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEPREP,
+ * Serializes the receive thread, it may be working inside the critsect.}
+ */
+static DECLCALLBACK(int) pcnetR3SavePrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ RT_NOREF(pSSM);
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+
+ int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ AssertRCReturn(rc, rc);
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC}
+ */
+static DECLCALLBACK(int) pcnetR3SaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ pHlp->pfnSSMPutBool(pSSM, pThis->fLinkUp);
+ pHlp->pfnSSMPutU32(pSSM, pThis->u32RAP);
+ pHlp->pfnSSMPutS32(pSSM, pThis->iISR);
+ pHlp->pfnSSMPutU32(pSSM, pThis->u32Lnkst);
+ pHlp->pfnSSMPutBool(pSSM, false/* was ffPrivIfEnabled */); /* >= If version 0.9 */
+ pHlp->pfnSSMPutBool(pSSM, pThis->fSignalRxMiss); /* >= If version 0.10 */
+ pHlp->pfnSSMPutGCPhys32(pSSM, pThis->GCRDRA);
+ pHlp->pfnSSMPutGCPhys32(pSSM, pThis->GCTDRA);
+ pHlp->pfnSSMPutMem(pSSM, pThis->aPROM, sizeof(pThis->aPROM));
+ pHlp->pfnSSMPutMem(pSSM, pThis->aCSR, sizeof(pThis->aCSR));
+ pHlp->pfnSSMPutMem(pSSM, pThis->aBCR, sizeof(pThis->aBCR));
+ pHlp->pfnSSMPutMem(pSSM, pThis->aMII, sizeof(pThis->aMII));
+ pHlp->pfnSSMPutU16(pSSM, pThis->u16CSR0LastSeenByGuest);
+ pHlp->pfnSSMPutU64(pSSM, pThis->u64LastPoll);
+ pcnetR3SaveConfig(pHlp, pThis, pSSM);
+
+ int rc = VINF_SUCCESS;
+ rc = PDMDevHlpTimerSave(pDevIns, pThis->hTimerPoll, pSSM);
+ if (RT_FAILURE(rc))
+ return rc;
+ if (pThis->uDevType == DEV_AM79C973)
+ rc = PDMDevHlpTimerSave(pDevIns, pThis->hTimerSoftInt, pSSM);
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADPREP},
+ * Serializes the receive thread, it may be working inside the critsect.}
+ */
+static DECLCALLBACK(int) pcnetR3LoadPrep(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ AssertRCReturn(rc, rc);
+
+ uint32_t uVer = pHlp->pfnSSMHandleVersion(pSSM);
+ if ( uVer < VBOX_FULL_VERSION_MAKE(4, 3, 6)
+ || ( uVer >= VBOX_FULL_VERSION_MAKE(4, 3, 51)
+ && uVer < VBOX_FULL_VERSION_MAKE(4, 3, 53)))
+ {
+ /* older saved states contain the shared memory region which was never used for ages. */
+ void *pvSharedMMIOR3;
+ rc = PDMDevHlpMmio2Create(pDevIns, pDevIns->apPciDevs[0], 2, _512K, 0, "PCnetSh", &pvSharedMMIOR3, &pThis->hMmio2Shared);
+ if (RT_FAILURE(rc))
+ rc = PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+ N_("Failed to allocate the dummy shmem region for the PCnet device"));
+ }
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ */
+static DECLCALLBACK(int) pcnetR3LoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ if ( SSM_VERSION_MAJOR_CHANGED(uVersion, PCNET_SAVEDSTATE_VERSION)
+ || SSM_VERSION_MINOR(uVersion) < 7)
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+
+ if (uPass == SSM_PASS_FINAL)
+ {
+ /* restore data */
+ pHlp->pfnSSMGetBool(pSSM, &pThis->fLinkUp);
+ int rc = pHlp->pfnSSMGetU32(pSSM, &pThis->u32RAP);
+ AssertRCReturn(rc, rc);
+ AssertLogRelMsgReturn(pThis->u32RAP < RT_ELEMENTS(pThis->aCSR), ("%#x\n", pThis->u32RAP), VERR_SSM_LOAD_CONFIG_MISMATCH);
+ pHlp->pfnSSMGetS32(pSSM, &pThis->iISR);
+ pHlp->pfnSSMGetU32(pSSM, &pThis->u32Lnkst);
+ if ( SSM_VERSION_MAJOR(uVersion) > 0
+ || SSM_VERSION_MINOR(uVersion) >= 9)
+ {
+ bool fPrivIfEnabled = false;
+ pHlp->pfnSSMGetBool(pSSM, &fPrivIfEnabled);
+ if (fPrivIfEnabled)
+ {
+ /* no longer implemented */
+ LogRel(("PCnet#%d: Cannot enable private interface!\n", PCNET_INST_NR));
+ return VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION;
+ }
+ }
+ if ( SSM_VERSION_MAJOR(uVersion) > 0
+ || SSM_VERSION_MINOR(uVersion) >= 10)
+ pHlp->pfnSSMGetBool(pSSM, &pThis->fSignalRxMiss);
+ pHlp->pfnSSMGetGCPhys32(pSSM, &pThis->GCRDRA);
+ pHlp->pfnSSMGetGCPhys32(pSSM, &pThis->GCTDRA);
+ pHlp->pfnSSMGetMem(pSSM, &pThis->aPROM, sizeof(pThis->aPROM));
+ pHlp->pfnSSMGetMem(pSSM, &pThis->aCSR, sizeof(pThis->aCSR));
+ pHlp->pfnSSMGetMem(pSSM, &pThis->aBCR, sizeof(pThis->aBCR));
+ pHlp->pfnSSMGetMem(pSSM, &pThis->aMII, sizeof(pThis->aMII));
+ pHlp->pfnSSMGetU16(pSSM, &pThis->u16CSR0LastSeenByGuest);
+ pHlp->pfnSSMGetU64(pSSM, &pThis->u64LastPoll);
+ }
+
+ /* check config */
+ RTMAC Mac;
+ int rc = pHlp->pfnSSMGetMem(pSSM, &Mac, sizeof(Mac));
+ AssertRCReturn(rc, rc);
+ if ( memcmp(&Mac, &pThis->MacConfigured, sizeof(Mac))
+ && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)) )
+ LogRel(("PCnet#%u: The mac address differs: config=%RTmac saved=%RTmac\n", PCNET_INST_NR, &pThis->MacConfigured, &Mac));
+
+ uint8_t uDevType;
+ rc = pHlp->pfnSSMGetU8(pSSM, &uDevType);
+ AssertRCReturn(rc, rc);
+ if (pThis->uDevType != uDevType)
+ return pHlp->pfnSSMSetCfgError(pSSM, RT_SRC_POS, N_("The uDevType setting differs: config=%u saved=%u"), pThis->uDevType, uDevType);
+
+ uint32_t u32LinkSpeed;
+ rc = pHlp->pfnSSMGetU32(pSSM, &u32LinkSpeed);
+ AssertRCReturn(rc, rc);
+ if ( pThis->u32LinkSpeed != u32LinkSpeed
+ && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)) )
+ LogRel(("PCnet#%u: The mac link speed differs: config=%u saved=%u\n", PCNET_INST_NR, pThis->u32LinkSpeed, u32LinkSpeed));
+
+ if (uPass == SSM_PASS_FINAL)
+ {
+ /* restore timers and stuff */
+ PDMDevHlpTimerLoad(pDevIns, pThis->hTimerPoll, pSSM);
+ if (pThis->uDevType == DEV_AM79C973)
+ {
+ if ( SSM_VERSION_MAJOR(uVersion) > 0
+ || SSM_VERSION_MINOR(uVersion) >= 8)
+ PDMDevHlpTimerLoad(pDevIns, pThis->hTimerSoftInt, pSSM);
+ }
+
+ pThis->iLog2DescSize = BCR_SWSTYLE(pThis)
+ ? 4
+ : 3;
+ pThis->GCUpperPhys = BCR_SSIZE32(pThis)
+ ? 0
+ : (0xff00 & (uint32_t)pThis->aCSR[2]) << 16;
+
+ /* update promiscuous mode. */
+ if (pThisCC->pDrv)
+ pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, CSR_PROM(pThis));
+
+ /* Indicate link down to the guest OS that all network connections have
+ been lost, unless we've been teleported here. */
+ if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
+ pcnetR3TempLinkDown(pDevIns, pThis);
+ }
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADDONE}
+ */
+static DECLCALLBACK(int) pcnetR3LoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ RT_NOREF(pSSM);
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ int rc = VINF_SUCCESS;
+ if (pThis->hMmio2Shared != NIL_PGMMMIO2HANDLE)
+ {
+ /* drop this dummy region */
+ rc = pDevIns->pHlpR3->pfnMmio2Destroy(pDevIns, pThis->hMmio2Shared);
+ AssertLogRelRC(rc);
+ pThis->hMmio2Shared = NIL_PGMMMIO2HANDLE;
+ }
+ return rc;
+}
+
+/* -=-=-=-=-=- PCNETSTATE::INetworkDown -=-=-=-=-=- */
+
+/**
+ * Check if the device/driver can receive data now.
+ *
+ * Worker for pcnetR3NetworkDown_WaitReceiveAvail(). This must be called before
+ * the pfnRecieve() method is called.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The device instance.
+ * @param pThis The PCnet shared instance data.
+ */
+static int pcnetR3CanReceive(PPDMDEVINS pDevIns, PPCNETSTATE pThis)
+{
+ int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc);
+
+ rc = VERR_NET_NO_BUFFER_SPACE;
+
+ if (RT_LIKELY(!CSR_DRX(pThis) && !CSR_STOP(pThis) && !CSR_SPND(pThis)))
+ {
+ if (HOST_IS_OWNER(CSR_CRST(pThis)) && pThis->GCRDRA)
+ pcnetRdtePoll(pDevIns, pThis);
+
+ if (RT_UNLIKELY(HOST_IS_OWNER(CSR_CRST(pThis))))
+ {
+ /** @todo Notify the guest _now_. Will potentially increase the interrupt load */
+ if (pThis->fSignalRxMiss)
+ pThis->aCSR[0] |= 0x1000; /* Set MISS flag */
+ }
+ else
+ rc = VINF_SUCCESS;
+ }
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
+ */
+static DECLCALLBACK(int) pcnetR3NetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
+{
+ PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkDown);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+
+ int rc = pcnetR3CanReceive(pDevIns, pThis);
+ if (RT_SUCCESS(rc))
+ return VINF_SUCCESS;
+ if (RT_UNLIKELY(cMillies == 0))
+ return VERR_NET_NO_BUFFER_SPACE;
+
+ rc = VERR_INTERRUPTED;
+ ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, true);
+ STAM_PROFILE_START(&pThis->StatRxOverflow, a);
+ VMSTATE enmVMState;
+ while (RT_LIKELY( (enmVMState = PDMDevHlpVMState(pDevIns)) == VMSTATE_RUNNING
+ || enmVMState == VMSTATE_RUNNING_LS))
+ {
+ int rc2 = pcnetR3CanReceive(pDevIns, pThis);
+ if (RT_SUCCESS(rc2))
+ {
+ rc = VINF_SUCCESS;
+ break;
+ }
+ LogFlow(("pcnetR3NetworkDown_WaitReceiveAvail: waiting cMillies=%u...\n", cMillies));
+ /* Start the poll timer once which will remain active as long fMaybeOutOfSpace
+ * is true -- even if (transmit) polling is disabled (CSR_DPOLL). */
+ rc2 = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc2);
+ pcnetPollTimerStart(pDevIns, pThis);
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEventOutOfRxSpace, cMillies);
+ }
+ STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
+ ASMAtomicXchgBool(&pThis->fMaybeOutOfSpace, false);
+
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
+ */
+static DECLCALLBACK(int) pcnetR3NetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
+{
+ PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkDown);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+
+ STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
+ int rc = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rc);
+
+ /*
+ * Check for the max ethernet frame size, taking the IEEE 802.1Q (VLAN) tag into
+ * account. Note that the CRC Checksum is optional.
+ * Ethernet frames consist of a 14-byte header [+ 4-byte vlan tag] + a 1500-byte body [+ 4-byte CRC].
+ */
+ if (RT_LIKELY( cb <= 1518
+ || ( cb <= 1522
+ && ((PCRTNETETHERHDR)pvBuf)->EtherType == RT_H2BE_U16_C(RTNET_ETHERTYPE_VLAN))))
+ {
+ bool fAddFCS = cb <= 1514
+ || ( cb <= 1518
+ && ((PCRTNETETHERHDR)pvBuf)->EtherType == RT_H2BE_U16_C(RTNET_ETHERTYPE_VLAN));
+ if (cb > 70) /* unqualified guess */
+ pThis->Led.Asserted.s.fReading = pThis->Led.Actual.s.fReading = 1;
+ pcnetReceiveNoSync(pDevIns, pThis, pThisCC, (const uint8_t *)pvBuf, cb, fAddFCS, false);
+ pThis->Led.Actual.s.fReading = 0;
+ }
+#ifdef LOG_ENABLED
+ else
+ {
+ static bool s_fFirstBigFrameLoss = true;
+ unsigned cbMaxFrame = ((PCRTNETETHERHDR)pvBuf)->EtherType == RT_H2BE_U16_C(RTNET_ETHERTYPE_VLAN)
+ ? 1522 : 1518;
+ if (s_fFirstBigFrameLoss)
+ {
+ s_fFirstBigFrameLoss = false;
+ Log(("PCnet#%d: Received giant frame %zu, max %u. (Further giants will be reported at level5.)\n",
+ PCNET_INST_NR, cb, cbMaxFrame));
+ }
+ else
+ Log5(("PCnet#%d: Received giant frame %zu bytes, max %u.\n",
+ PCNET_INST_NR, cb, cbMaxFrame));
+ }
+#endif /* LOG_ENABLED */
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
+ */
+static DECLCALLBACK(void) pcnetR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
+{
+ PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkDown);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ pcnetXmitPending(pDevIns, pThis, pThisCC, true /*fOnWorkerThread*/);
+}
+
+
+/* -=-=-=-=-=- PCNETSTATE::INetworkConfig -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
+ */
+static DECLCALLBACK(int) pcnetR3NetworkConfig_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
+{
+ PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkConfig);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ memcpy(pMac, pThis->aPROM, sizeof(*pMac));
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
+ */
+static DECLCALLBACK(PDMNETWORKLINKSTATE) pcnetR3NetworkConfig_GetLinkState(PPDMINETWORKCONFIG pInterface)
+{
+ PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkConfig);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ if (pThis->fLinkUp && !pThis->fLinkTempDown)
+ return PDMNETWORKLINKSTATE_UP;
+ if (!pThis->fLinkUp)
+ return PDMNETWORKLINKSTATE_DOWN;
+ if (pThis->fLinkTempDown)
+ return PDMNETWORKLINKSTATE_DOWN_RESUME;
+ AssertMsgFailed(("Invalid link state!\n"));
+ return PDMNETWORKLINKSTATE_INVALID;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
+ */
+static DECLCALLBACK(int) pcnetR3NetworkConfig_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
+{
+ PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, INetworkConfig);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ bool fLinkUp;
+
+ AssertMsgReturn(enmState > PDMNETWORKLINKSTATE_INVALID && enmState <= PDMNETWORKLINKSTATE_DOWN_RESUME,
+ ("Invalid link state: enmState=%d\n", enmState), VERR_INVALID_PARAMETER);
+
+ if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
+ {
+ pcnetR3TempLinkDown(pDevIns, pThis);
+ /*
+ * Note that we do not notify the driver about the link state change because
+ * the change is only temporary and can be disregarded from the driver's
+ * point of view (see @bugref{7057}).
+ */
+ return VINF_SUCCESS;
+ }
+ /* has the state changed? */
+ fLinkUp = enmState == PDMNETWORKLINKSTATE_UP;
+ if (pThis->fLinkUp != fLinkUp)
+ {
+ pThis->fLinkUp = fLinkUp;
+ if (fLinkUp)
+ {
+ /* Connect with a configured delay. */
+ pThis->fLinkTempDown = true;
+ pThis->cLinkDownReported = 0;
+ pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR (this is probably wrong) */
+ pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
+ int rc = PDMDevHlpTimerSetMillies(pDevIns, pThis->hTimerRestore, pThis->cMsLinkUpDelay);
+ AssertRC(rc);
+ }
+ else
+ {
+ /* disconnect */
+ pThis->cLinkDownReported = 0;
+ pThis->aCSR[0] |= RT_BIT(15) | RT_BIT(13); /* ERR | CERR (this is probably wrong) */
+ pThis->Led.Asserted.s.fError = pThis->Led.Actual.s.fError = 1;
+ }
+ Assert(!PDMDevHlpCritSectIsOwner(pDevIns, &pThis->CritSect));
+ if (pThisCC->pDrv)
+ pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/* -=-=-=-=-=- PCNETSTATE::ILeds (LUN#0) -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
+ */
+static DECLCALLBACK(int) pcnetQueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
+{
+ PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, ILeds);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ if (iLUN == 0)
+ {
+ *ppLed = &pThis->Led;
+ return VINF_SUCCESS;
+ }
+ return VERR_PDM_LUN_NOT_FOUND;
+}
+
+
+/* -=-=-=-=-=- PCNETSTATE::IBase (LUN#0) -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) pcnetQueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
+{
+ PPCNETSTATECC pThisCC = RT_FROM_MEMBER(pInterface, PCNETSTATECC, IBase);
+ Assert(&pThisCC->IBase == pInterface);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
+ return NULL;
+}
+
+
+/* -=-=-=-=-=- PDMDEVREG -=-=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnPowerOff}
+ */
+static DECLCALLBACK(void) pcnetR3PowerOff(PPDMDEVINS pDevIns)
+{
+ /* Poke thread waiting for buffer space. */
+ pcnetWakeupReceive(pDevIns);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDetach}
+ *
+ * One port on the network card has been disconnected from the network.
+ */
+static DECLCALLBACK(void) pcnetR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
+ Log(("#%d pcnetR3Detach:\n", PCNET_INST_NR));
+ RT_NOREF(fFlags);
+
+ AssertLogRelReturnVoid(iLUN == 0);
+
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
+
+ /** @todo r=pritesh still need to check if i missed
+ * to clean something in this function
+ */
+
+ /*
+ * Zero some important members.
+ */
+ pThis->fDriverAttached = false;
+ pThisCC->pDrvBase = NULL;
+ pThisCC->pDrv = NULL;
+ /// @todo figure this out
+ //pThis->pDrvR0 = NIL_RTR0PTR;
+ //pThis->pDrvRC = NIL_RTRCPTR;
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnAttach}
+ * One port on the network card has been connected to a network.
+ */
+static DECLCALLBACK(int) pcnetR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
+ LogFlow(("#%d pcnetR3Attach:\n", PCNET_INST_NR));
+ RT_NOREF(fFlags);
+
+ AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
+
+ int const rcLock = PDMDevHlpCritSectEnter(pDevIns, &pThis->CritSect, VERR_SEM_BUSY);
+ PDM_CRITSECT_RELEASE_ASSERT_RC_DEV(pDevIns, &pThis->CritSect, rcLock);
+
+ /*
+ * Attach the driver.
+ */
+ int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
+ if (RT_SUCCESS(rc))
+ {
+ pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
+ AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
+ rc = VERR_PDM_MISSING_INTERFACE_BELOW);
+ pThis->fDriverAttached = true;
+ /// @todo figoure out this
+ //pThis->pDrvR0 = PDMIBASER0_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASER0), PDMINETWORKUP);
+ //pThis->pDrvRC = PDMIBASERC_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASERC), PDMINETWORKUP);
+ }
+ else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
+ || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
+ {
+ /* This should never happen because this function is not called
+ * if there is no driver to attach! */
+ Log(("#%d No attached driver!\n", PCNET_INST_NR));
+ }
+
+ /*
+ * Temporary set the link down if it was up so that the guest
+ * will know that we have change the configuration of the
+ * network card
+ */
+ if (RT_SUCCESS(rc))
+ pcnetR3TempLinkDown(pDevIns, pThis);
+
+ PDMDevHlpCritSectLeave(pDevIns, &pThis->CritSect);
+ return rc;
+
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnSuspend}
+ */
+static DECLCALLBACK(void) pcnetR3Suspend(PPDMDEVINS pDevIns)
+{
+ /* Poke thread waiting for buffer space. */
+ pcnetWakeupReceive(pDevIns);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnReset}
+ */
+static DECLCALLBACK(void) pcnetR3Reset(PPDMDEVINS pDevIns)
+{
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ if (pThis->fLinkTempDown)
+ {
+ pThis->cLinkDownReported = 0x10000;
+ PDMDevHlpTimerStop(pDevIns, pThis->hTimerRestore);
+ pcnetR3TimerRestore(pDevIns, pThis->hTimerRestore, pThis);
+ }
+
+ /** @todo How to flush the queues? */
+ pcnetR3HardReset(pDevIns, pThis);
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnRelocate}
+ */
+static DECLCALLBACK(void) pcnetR3Relocate(PPDMDEVINS pDevIns, RTGCINTPTR offDelta)
+{
+ PPCNETSTATERC pThisRC = PDMINS_2_DATA_RC(pDevIns, PPCNETSTATERC);
+ pThisRC->pDrv += offDelta;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) pcnetR3Destruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+
+ if (pThis->hEventOutOfRxSpace == NIL_SUPSEMEVENT)
+ {
+ PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventOutOfRxSpace);
+ PDMDevHlpSUPSemEventClose(pDevIns, pThis->hEventOutOfRxSpace);
+ pThis->hEventOutOfRxSpace = NIL_SUPSEMEVENT;
+ }
+
+ if (PDMDevHlpCritSectIsInitialized(pDevIns, &pThis->CritSect))
+ PDMDevHlpCritSectDelete(pDevIns, &pThis->CritSect);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDEVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) pcnetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+ PPCNETSTATECC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PPCNETSTATECC);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ PPDMIBASE pBase;
+ char szTmp[128];
+ int rc;
+
+ Assert(RT_ELEMENTS(pThis->aBCR) == BCR_MAX_RAP);
+ Assert(RT_ELEMENTS(pThis->aMII) == MII_MAX_REG);
+ Assert(sizeof(pThis->abLoopBuf) == RT_ALIGN_Z(sizeof(pThis->abLoopBuf), 16));
+
+ /*
+ * Init what's required to make the destructor safe.
+ */
+ pThis->iInstance = iInstance;
+ pThis->hEventOutOfRxSpace = NIL_SUPSEMEVENT;
+ pThis->hIoPortsPci = NIL_IOMIOPORTHANDLE;
+ pThis->hIoPortsPciAProm = NIL_IOMIOPORTHANDLE;
+ pThis->hIoPortsIsa = NIL_IOMIOPORTHANDLE;
+ pThis->hIoPortsIsaAProm = NIL_IOMIOPORTHANDLE;
+ pThis->hMmio2Shared = NIL_PGMMMIO2HANDLE;
+ pThisCC->pDevIns = pDevIns;
+
+ /*
+ * Validate configuration.
+ */
+ PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns,
+ "MAC|CableConnected|Am79C973|ChipType|Port|IRQ|LineSpeed|PrivIfEnabled|LinkUpDelay|StatNo",
+ "");
+ /*
+ * Read the configuration.
+ */
+ rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", &pThis->MacConfigured, sizeof(pThis->MacConfigured));
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"MAC\" value"));
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "CableConnected", &pThis->fLinkUp, true);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"CableConnected\" value"));
+
+ /*
+ * Determine the model.
+ */
+ char szChipType[16];
+ rc = pHlp->pfnCFGMQueryStringDef(pCfg, "ChipType", &szChipType[0], sizeof(szChipType), "Am79C970A");
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Querying \"ChipType\" as string failed"));
+
+ if (!strcmp(szChipType, "Am79C970A"))
+ pThis->uDevType = DEV_AM79C970A; /* 10 Mbps PCnet-PCI II. */
+ else if (!strcmp(szChipType, "Am79C973"))
+ pThis->uDevType = DEV_AM79C973; /* 10/100 Mbps PCnet-FAST III. */
+ else if (!strcmp(szChipType, "Am79C960"))
+ pThis->uDevType = DEV_AM79C960; /* 10 Mbps PCnet-ISA, NE2100/Am2100 compatible. */
+ else if (!strcmp(szChipType, "Am79C960_EB"))
+ {
+ pThis->uDevType = DEV_AM79C960_EB; /* 10 Mbps PCnet-ISA, Racal InterLink NI6510 EtherBlaster compatible. */
+ /* NI6510 drivers (at least Racal's and Linux) require the OUI to be InterLan's (Racal-Datacom).
+ * Refuse loading if OUI doesn't match, because otherwise drivers won't load in the guest. */
+ if (memcmp(&pThis->MacConfigured, "\x02\x07\x01", 3))
+ return PDMDevHlpVMSetError(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS,
+ N_("Configuration error: MAC address OUI for EtherBlaster must be 02 07 01"));
+ }
+ else
+ return PDMDevHlpVMSetError(pDevIns, VERR_PDM_DEVINS_UNKNOWN_CFG_VALUES, RT_SRC_POS,
+ N_("Configuration error: The \"ChipType\" value \"%s\" is unsupported"),
+ szChipType);
+
+
+ /*
+ * Process the old model configuration. If present, it must take precedence for saved state compatibility.
+ */
+ bool fAm79C973;
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "Am79C973", &fAm79C973, false);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"Am79C973\" value"));
+ if (fAm79C973)
+ pThis->uDevType = DEV_AM79C973;
+
+ /*
+ * Process ISA configuration options. The defaults are chosen to be NE2100/Am2100 compatible.
+ */
+ rc = pHlp->pfnCFGMQueryPortDef(pCfg, "Port", &pThis->IOPortBase, 0x300);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"Port\" value"));
+
+ rc = pHlp->pfnCFGMQueryU8Def(pCfg, "IRQ", &pThis->uIsaIrq, 3);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"IRQ\" value"));
+
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LineSpeed", &pThis->u32LinkSpeed, 1000000); /* 1GBit/s (in kbps units)*/
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"LineSpeed\" value"));
+
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", (uint32_t*)&pThis->cMsLinkUpDelay, 5000); /* ms */
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
+ Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
+ if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
+ LogRel(("PCnet#%d WARNING! Link up delay is set to %u seconds!\n", iInstance, pThis->cMsLinkUpDelay / 1000));
+ Log(("#%d Link up delay is set to %u seconds\n", iInstance, pThis->cMsLinkUpDelay / 1000));
+
+ uint32_t uStatNo = iInstance;
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "StatNo", &uStatNo, iInstance);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"StatNo\" value"));
+
+ /*
+ * Initialize data (most of it anyway).
+ */
+ pThis->Led.u32Magic = PDMLED_MAGIC;
+ /* IBase */
+ pThisCC->IBase.pfnQueryInterface = pcnetQueryInterface;
+ /* INeworkPort */
+ pThisCC->INetworkDown.pfnWaitReceiveAvail = pcnetR3NetworkDown_WaitReceiveAvail;
+ pThisCC->INetworkDown.pfnReceive = pcnetR3NetworkDown_Receive;
+ pThisCC->INetworkDown.pfnXmitPending = pcnetR3NetworkDown_XmitPending;
+ /* INetworkConfig */
+ pThisCC->INetworkConfig.pfnGetMac = pcnetR3NetworkConfig_GetMac;
+ pThisCC->INetworkConfig.pfnGetLinkState = pcnetR3NetworkConfig_GetLinkState;
+ pThisCC->INetworkConfig.pfnSetLinkState = pcnetR3NetworkConfig_SetLinkState;
+ /* ILeds */
+ pThisCC->ILeds.pfnQueryStatusLed = pcnetQueryStatusLed;
+
+ /* PCI Device */
+ PPDMPCIDEV pPciDev = pDevIns->apPciDevs[0];
+ PDMPCIDEV_ASSERT_VALID(pDevIns, pPciDev);
+
+ PDMPciDevSetVendorId(pPciDev, 0x1022);
+ PDMPciDevSetDeviceId(pPciDev, 0x2000);
+ PDMPciDevSetByte(pPciDev, 0x04, 0x07); /* command */
+ PDMPciDevSetByte(pPciDev, 0x05, 0x00);
+ PDMPciDevSetByte(pPciDev, 0x06, 0x80); /* status */
+ PDMPciDevSetByte(pPciDev, 0x07, 0x02);
+ PDMPciDevSetByte(pPciDev, 0x08, pThis->uDevType == DEV_AM79C973 ? 0x40 : 0x16); /* revision */
+ PDMPciDevSetByte(pPciDev, 0x09, 0x00);
+ PDMPciDevSetByte(pPciDev, 0x0a, 0x00); /* ethernet network controller */
+ PDMPciDevSetByte(pPciDev, 0x0b, 0x02);
+ PDMPciDevSetByte(pPciDev, 0x0e, 0x00); /* header_type */
+ PDMPciDevSetByte(pPciDev, 0x10, 0x01); /* IO Base */
+ PDMPciDevSetByte(pPciDev, 0x11, 0x00);
+ PDMPciDevSetByte(pPciDev, 0x12, 0x00);
+ PDMPciDevSetByte(pPciDev, 0x13, 0x00);
+ PDMPciDevSetByte(pPciDev, 0x14, 0x00); /* MMIO Base */
+ PDMPciDevSetByte(pPciDev, 0x15, 0x00);
+ PDMPciDevSetByte(pPciDev, 0x16, 0x00);
+ PDMPciDevSetByte(pPciDev, 0x17, 0x00);
+
+ /* subsystem and subvendor IDs */
+ PDMPciDevSetByte(pPciDev, 0x2c, 0x22); /* subsystem vendor id */
+ PDMPciDevSetByte(pPciDev, 0x2d, 0x10);
+ PDMPciDevSetByte(pPciDev, 0x2e, 0x00); /* subsystem id */
+ PDMPciDevSetByte(pPciDev, 0x2f, 0x20);
+ PDMPciDevSetByte(pPciDev, 0x3d, 1); /* interrupt pin 0 */
+ PDMPciDevSetByte(pPciDev, 0x3e, 0x06);
+ PDMPciDevSetByte(pPciDev, 0x3f, 0xff);
+
+ /*
+ * We use our own critical section (historical reasons).
+ */
+ rc = PDMDevHlpCritSectInit(pDevIns, &pThis->CritSect, RT_SRC_POS, "PCnet#%u", iInstance);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Register the PCI device, its I/O regions, the timer and the saved state item.
+ */
+ Assert(PCNET_IS_PCI(pThis) != PCNET_IS_ISA(pThis)); /* IOPortBase is shared, so it's either one or the other! */
+
+ if (PCNET_IS_PCI(pThis))
+ {
+ rc = PDMDevHlpPCIRegister(pDevIns, pPciDev);
+ AssertRCReturn(rc, rc);
+
+ /* Region #0: I/O ports - two handlers: */
+ rc = PDMDevHlpIoPortCreate(pDevIns, 0x10 /*cPorts*/, pPciDev, 0 /*iPciRegion*/,
+ pcnetIoPortAPromWrite, pcnetIOPortAPromRead, NULL /*pvUser*/,
+ "PCnet APROM", NULL /*paExtDescs*/, &pThis->hIoPortsPciAProm);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpIoPortCreate(pDevIns, 0x10 /*cPorts*/, pPciDev, 0 /*iPciRegion*/,
+ pcnetIoPortWrite, pcnetIoPortRead, NULL /*pvUser*/,
+ "PCnet", NULL /*paExtDescs*/, &pThis->hIoPortsPci);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpPCIIORegionRegisterIoCustom(pDevIns, 0, PCNET_IOPORT_SIZE, pcnetR3PciMapUnmapIoPorts);
+ AssertRCReturn(rc, rc);
+
+ /* Region #1: MMIO */
+ rc = PDMDevHlpPCIIORegionCreateMmio(pDevIns, 1 /*iPciRegion*/, PCNET_PNPMMIO_SIZE, PCI_ADDRESS_SPACE_MEM,
+ pcnetR3MmioWrite, pcnetR3MmioRead, NULL /*pvUser*/,
+ IOMMMIO_FLAGS_READ_PASSTHRU | IOMMMIO_FLAGS_WRITE_PASSTHRU,
+ "PCnet", &pThis->hMmioPci);
+ AssertRCReturn(rc, rc);
+ }
+
+ /*
+ * Register ISA I/O ranges for PCnet-ISA.
+ */
+ if (PCNET_IS_ISA(pThis))
+ {
+ rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase, 0x10 /*cPorts*/, pcnetIoPortAPromWrite, pcnetIOPortAPromRead,
+ "PCnet APROM", NULL /*paExtDesc*/, &pThis->hIoPortsIsaAProm);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpIoPortCreateAndMap(pDevIns, pThis->IOPortBase + 0x10, 0x10 /*cPorts*/, pcnetIoPortWrite, pcnetIoPortRead,
+ "PCnet", NULL /*paExtDesc*/, &pThis->hIoPortsIsa);
+ AssertRCReturn(rc, rc);
+ }
+
+
+ /* Transmit descriptor polling timer */
+ rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetR3Timer, NULL, TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0,
+ "PCnet Poll", &pThis->hTimerPoll);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpTimerSetCritSect(pDevIns, pThis->hTimerPoll, &pThis->CritSect);
+ AssertRCReturn(rc, rc);
+
+ if (pThis->uDevType == DEV_AM79C973)
+ {
+ /* Software Interrupt timer */
+ rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetR3TimerSoftInt, NULL,
+ TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_RING0, "PCnet SoftInt", &pThis->hTimerSoftInt);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpTimerSetCritSect(pDevIns, pThis->hTimerSoftInt, &pThis->CritSect);
+ AssertRCReturn(rc, rc);
+ }
+ rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, pcnetR3TimerRestore, pThis,
+ TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0, "PCnet Restore", &pThis->hTimerRestore);
+ AssertRCReturn(rc, rc);
+
+ rc = PDMDevHlpSSMRegisterEx(pDevIns, PCNET_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
+ NULL, pcnetR3LiveExec, NULL,
+ pcnetR3SavePrep, pcnetR3SaveExec, NULL,
+ pcnetR3LoadPrep, pcnetR3LoadExec, pcnetR3LoadDone);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Create the transmit queue.
+ */
+ rc = PDMDevHlpTaskCreate(pDevIns, PDMTASK_F_RZ, "PCnet-Xmit", pcnetR3XmitTaskCallback, NULL /*pvUser*/, &pThis->hXmitTask);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Create the RX notifier semaphore.
+ */
+ rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hEventOutOfRxSpace);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Attach status driver (optional).
+ */
+ rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pBase, "Status Port");
+ if (RT_SUCCESS(rc))
+ pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pBase, PDMILEDCONNECTORS);
+ else
+ AssertMsgReturn( rc == VERR_PDM_NO_ATTACHED_DRIVER
+ || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME,
+ ("Failed to attach to status driver. rc=%Rrc\n", rc),
+ rc);
+
+ /*
+ * Attach driver.
+ */
+ rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
+ if (RT_SUCCESS(rc))
+ {
+ pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
+ AssertMsgReturn(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
+ VERR_PDM_MISSING_INTERFACE_BELOW);
+ pThis->fDriverAttached = true;
+ /// @todo figure out this!
+ //pThis->pDrvR0 = PDMIBASER0_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASER0), PDMINETWORKUP);
+ //pThis->pDrvRC = PDMIBASERC_QUERY_INTERFACE(PDMIBASE_QUERY_INTERFACE(pThis->pDrvBase, PDMIBASERC), PDMINETWORKUP);
+ }
+ else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
+ || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
+ {
+ /* No error! */
+ Log(("No attached driver!\n"));
+ }
+ else
+ return rc;
+
+ /*
+ * Reset the device state. (Do after attaching.)
+ */
+ pcnetR3HardReset(pDevIns, pThis);
+
+ /*
+ * Register the info item.
+ */
+ RTStrPrintf(szTmp, sizeof(szTmp), "pcnet%d", pDevIns->iInstance);
+ PDMDevHlpDBGFInfoRegister(pDevIns, szTmp, "PCNET info.", pcnetR3Info);
+
+ /*
+ * Register statistics.
+ * The /Public/ bits are official and used by session info in the GUI.
+ */
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
+ "Amount of data received", "/Public/NetAdapter/%u/BytesReceived", uStatNo);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
+ "Amount of data transmitted", "/Public/NetAdapter/%u/BytesTransmitted", uStatNo);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pDevIns->iInstance, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
+ "Device instance number", "/Public/NetAdapter/%u/%s", uStatNo, pDevIns->pReg->szName);
+
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, "ReceiveBytes", STAMUNIT_BYTES, "Amount of data received");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, "TransmitBytes", STAMUNIT_BYTES, "Amount of data transmitted");
+
+#ifdef VBOX_WITH_STATISTICS
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOReadRZ, STAMTYPE_PROFILE, "MMIO/ReadRZ", STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in RZ");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOReadR3, STAMTYPE_PROFILE, "MMIO/ReadR3", STAMUNIT_TICKS_PER_CALL, "Profiling MMIO reads in R3");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOWriteRZ, STAMTYPE_PROFILE, "MMIO/WriteRZ", STAMUNIT_TICKS_PER_CALL, "Profiling MMIO writes in RZ");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMMIOWriteR3, STAMTYPE_PROFILE, "MMIO/WriteR3", STAMUNIT_TICKS_PER_CALL, "Profiling MMIO writes in R3");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatAPROMRead, STAMTYPE_PROFILE, "IO/APROMRead", STAMUNIT_TICKS_PER_CALL, "Profiling APROM reads");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatAPROMWrite, STAMTYPE_PROFILE, "IO/APROMWrite", STAMUNIT_TICKS_PER_CALL, "Profiling APROM writes");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOReadRZ, STAMTYPE_PROFILE, "IO/ReadRZ", STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in RZ");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOReadR3, STAMTYPE_PROFILE, "IO/ReadR3", STAMUNIT_TICKS_PER_CALL, "Profiling IO reads in R3");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOWriteRZ, STAMTYPE_PROFILE, "IO/WriteRZ", STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in RZ");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatIOWriteR3, STAMTYPE_PROFILE, "IO/WriteR3", STAMUNIT_TICKS_PER_CALL, "Profiling IO writes in R3");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTimer, STAMTYPE_PROFILE, "Timer", STAMUNIT_TICKS_PER_CALL, "Profiling Timer");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, "Receive", STAMUNIT_TICKS_PER_CALL, "Profiling receive");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, "RxOverflow", STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_PROFILE, "RxOverflowWakeup", STAMUNIT_TICKS_PER_OCCURENCE, "Nr of RX overflow wakeups");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitCase1, STAMTYPE_COUNTER, "Transmit/Case1", STAMUNIT_OCCURENCES, "Single descriptor transmit");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitCase2, STAMTYPE_COUNTER, "Transmit/Case2", STAMUNIT_OCCURENCES, "Multi descriptor transmit");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitRZ, STAMTYPE_PROFILE, "Transmit/TotalRZ", STAMUNIT_TICKS_PER_CALL, "Profiling transmits in RZ");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitR3, STAMTYPE_PROFILE, "Transmit/TotalR3", STAMUNIT_TICKS_PER_CALL, "Profiling transmits in R3");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitSendRZ, STAMTYPE_PROFILE, "Transmit/SendRZ", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet send transmit in RZ");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitSendR3, STAMTYPE_PROFILE, "Transmit/SendR3", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet send transmit in R3");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTdtePollRZ, STAMTYPE_PROFILE, "TdtePollRZ", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TdtePoll in RZ");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTdtePollR3, STAMTYPE_PROFILE, "TdtePollR3", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TdtePoll in R3");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRdtePollRZ, STAMTYPE_PROFILE, "RdtePollRZ", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet RdtePoll in RZ");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRdtePollR3, STAMTYPE_PROFILE, "RdtePollR3", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet RdtePoll in R3");
+
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTmdStoreRZ, STAMTYPE_PROFILE, "TmdStoreRZ", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TmdStore in RZ");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTmdStoreR3, STAMTYPE_PROFILE, "TmdStoreR3", STAMUNIT_TICKS_PER_CALL, "Profiling PCnet TmdStore in R3");
+
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatXmitSkipCurrent, STAMTYPE_COUNTER, "Xmit/Skipped", STAMUNIT_OCCURENCES, "");
+
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatInterrupt, STAMTYPE_PROFILE, "UpdateIRQ", STAMUNIT_TICKS_PER_CALL, "Profiling interrupt checks");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatPollTimer, STAMTYPE_PROFILE, "PollTimer", STAMUNIT_TICKS_PER_CALL, "Profiling poll timer");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatMIIReads, STAMTYPE_COUNTER, "MIIReads", STAMUNIT_OCCURENCES, "Number of MII reads");
+ unsigned i;
+ for (i = 0; i < RT_ELEMENTS(pThis->aStatXmitFlush) - 1; i++)
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitFlush[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
+ "", "XmitFlushIrq/%02u", i + 1);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitFlush[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
+ "", "XmitFlushIrq/%02u-inf", i + 1);
+
+ for (i = 0; i < RT_ELEMENTS(pThis->aStatXmitChainCounts) - 1; i++)
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitChainCounts[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
+ "", "XmitChainCounts/%02u", i + 1);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->aStatXmitChainCounts[i], STAMTYPE_COUNTER, STAMVISIBILITY_USED, STAMUNIT_OCCURENCES,
+ "", "XmitChainCounts/%02u-inf", i + 1);
+#endif /* VBOX_WITH_STATISTICS */
+
+ return VINF_SUCCESS;
+}
+
+#else /* !IN_RING3 */
+
+/**
+ * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
+ */
+static DECLCALLBACK(int) pcnetRZConstruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PPCNETSTATE pThis = PDMDEVINS_2_DATA(pDevIns, PPCNETSTATE);
+
+ /* Critical section setup: */
+ int rc = PDMDevHlpSetDeviceCritSect(pDevIns, &pThis->CritSect);
+ AssertRCReturn(rc, rc);
+
+ /* PCI I/O ports: */
+ if (pThis->hIoPortsPciAProm != NIL_IOMIOPORTHANDLE)
+ {
+ rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsPciAProm, pcnetIoPortAPromWrite, pcnetIOPortAPromRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsPci, pcnetIoPortWrite, pcnetIoPortRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+ }
+ else
+ Assert(pThis->hIoPortsPci == NIL_IOMIOPORTHANDLE);
+
+ /** @todo PCI MMIO */
+
+ /* ISA I/O ports: */
+ if (pThis->hIoPortsIsaAProm != NIL_IOMIOPORTHANDLE)
+ {
+ rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsIsaAProm, pcnetIoPortAPromWrite, pcnetIOPortAPromRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+ rc = PDMDevHlpIoPortSetUpContext(pDevIns, pThis->hIoPortsIsa, pcnetIoPortWrite, pcnetIoPortRead, NULL /*pvUser*/);
+ AssertRCReturn(rc, rc);
+ }
+ else
+ Assert(pThis->hIoPortsIsa == NIL_IOMIOPORTHANDLE);
+
+ return VINF_SUCCESS;
+}
+
+#endif /* !IN_RING3 */
+
+/**
+ * The device registration structure.
+ */
+const PDMDEVREG g_DevicePCNet =
+{
+ /* .u32Version = */ PDM_DEVREG_VERSION,
+ /* .uReserved0 = */ 0,
+ /* .szName = */ "pcnet",
+#ifdef PCNET_GC_ENABLED
+ /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_RZ | PDM_DEVREG_FLAGS_NEW_STYLE,
+#else
+ /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS,
+#endif
+ /* .fClass = */ PDM_DEVREG_CLASS_NETWORK,
+ /* .cMaxInstances = */ ~0U,
+ /* .uSharedVersion = */ 42,
+ /* .cbInstanceShared = */ sizeof(PCNETSTATE),
+ /* .cbInstanceCC = */ sizeof(PCNETSTATECC),
+ /* .cbInstanceRC = */ sizeof(PCNETSTATERC),
+ /* .cMaxPciDevices = */ 1,
+ /* .cMaxMsixVectors = */ 0,
+ /* .pszDescription = */ "AMD PCnet Ethernet controller.\n",
+#if defined(IN_RING3)
+ /* .pszRCMod = */ "VBoxDDRC.rc",
+ /* .pszR0Mod = */ "VBoxDDR0.r0",
+ /* .pfnConstruct = */ pcnetR3Construct,
+ /* .pfnDestruct = */ pcnetR3Destruct,
+ /* .pfnRelocate = */ pcnetR3Relocate,
+ /* .pfnMemSetup = */ NULL,
+ /* .pfnPowerOn = */ NULL,
+ /* .pfnReset = */ pcnetR3Reset,
+ /* .pfnSuspend = */ pcnetR3Suspend,
+ /* .pfnResume = */ NULL,
+ /* .pfnAttach = */ pcnetR3Attach,
+ /* .pfnDetach = */ pcnetR3Detach,
+ /* .pfnQueryInterface = */ NULL,
+ /* .pfnInitComplete = */ NULL,
+ /* .pfnPowerOff = */ pcnetR3PowerOff,
+ /* .pfnSoftReset = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RING0)
+ /* .pfnEarlyConstruct = */ NULL,
+ /* .pfnConstruct = */ pcnetRZConstruct,
+ /* .pfnDestruct = */ NULL,
+ /* .pfnFinalDestruct = */ NULL,
+ /* .pfnRequest = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RC)
+ /* .pfnConstruct = */ pcnetRZConstruct,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#else
+# error "Not in IN_RING3, IN_RING0 or IN_RC!"
+#endif
+ /* .u32VersionEnd = */ PDM_DEVREG_VERSION
+};
+
+#endif /* !VBOX_DEVICE_STRUCT_TESTCASE */
+
diff --git a/src/VBox/Devices/Network/DevVirtioNet.cpp b/src/VBox/Devices/Network/DevVirtioNet.cpp
new file mode 100644
index 00000000..66d2cbf1
--- /dev/null
+++ b/src/VBox/Devices/Network/DevVirtioNet.cpp
@@ -0,0 +1,3772 @@
+/* $Id: DevVirtioNet.cpp $ */
+
+/** @file
+ * VBox storage devices - Virtio NET Driver
+ *
+ * Log-levels used:
+ * - Level 1: The most important (but usually rare) things to note
+ * - Level 2: NET command logging
+ * - Level 3: Vector and I/O transfer summary (shows what client sent an expects and fulfillment)
+ * - Level 6: Device <-> Guest Driver negotation, traffic, notifications and state handling
+ * - Level 12: Brief formatted hex dumps of I/O data
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*******************************************************************************************************************************
+* Header Files *
+***************************************************************************************************************************** **/
+#define LOG_GROUP LOG_GROUP_DEV_VIRTIO
+#define VIRTIONET_WITH_GSO
+
+#include <iprt/types.h>
+#include <iprt/errcore.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+
+#include <VBox/sup.h>
+#include <VBox/vmm/pdmdev.h>
+#include <VBox/vmm/stam.h>
+#include <VBox/vmm/pdmcritsect.h>
+#include <VBox/vmm/pdmnetifs.h>
+#include <VBox/msi.h>
+#include <VBox/version.h>
+#include <VBox/log.h>
+#include <VBox/pci.h>
+
+
+#ifdef IN_RING3
+# include <VBox/VBoxPktDmp.h>
+# include <iprt/alloc.h>
+# include <iprt/memcache.h>
+# include <iprt/semaphore.h>
+# include <iprt/sg.h>
+# include <iprt/param.h>
+# include <iprt/uuid.h>
+#endif
+#include "../VirtIO/VirtioCore.h"
+
+#include "VBoxDD.h"
+
+#define VIRTIONET_TRANSITIONAL_ENABLE_FLAG 1 /** < If set behave as VirtIO "transitional" device */
+
+/** The current saved state version for the virtio core. */
+#define VIRTIONET_SAVEDSTATE_VERSION UINT32_C(1)
+#define VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY UINT32_C(1) /**< Grandfathered in from DevVirtioNet.cpp */
+#define VIRTIONET_SAVEDSTATE_VERSION_LEGACY UINT32_C(2) /**< Grandfathered in from DevVirtioNet.cpp */
+#define VIRTIONET_VERSION_MARKER_MAC_ADDR { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff } /** SSM handling */
+
+/*
+ * Glossary of networking acronyms used in feature names below:
+ *
+ * GSO = Generic Segmentation Offload
+ * TSO = TCP Segmentation Offload
+ * UFO = UDP Fragmentation Offload
+ * ECN = Explicit Congestion Notification
+ */
+
+/** @name VirtIO 1.0 NET Host feature bits (See VirtIO 1.0 specification, Section 5.6.3)
+ * @{ */
+#define VIRTIONET_F_CSUM RT_BIT_64(0) /**< Handle packets with partial checksum */
+#define VIRTIONET_F_GUEST_CSUM RT_BIT_64(1) /**< Handles packets with partial checksum */
+#define VIRTIONET_F_CTRL_GUEST_OFFLOADS RT_BIT_64(2) /**< Control channel offloads reconfig support */
+#define VIRTIONET_F_MAC RT_BIT_64(5) /**< Device has given MAC address */
+#define VIRTIONET_F_GUEST_TSO4 RT_BIT_64(7) /**< Driver can receive TSOv4 */
+#define VIRTIONET_F_GUEST_TSO6 RT_BIT_64(8) /**< Driver can receive TSOv6 */
+#define VIRTIONET_F_GUEST_ECN RT_BIT_64(9) /**< Driver can receive TSO with ECN */
+#define VIRTIONET_F_GUEST_UFO RT_BIT_64(10) /**< Driver can receive UFO */
+#define VIRTIONET_F_HOST_TSO4 RT_BIT_64(11) /**< Device can receive TSOv4 */
+#define VIRTIONET_F_HOST_TSO6 RT_BIT_64(12) /**< Device can receive TSOv6 */
+#define VIRTIONET_F_HOST_ECN RT_BIT_64(13) /**< Device can receive TSO with ECN */
+#define VIRTIONET_F_HOST_UFO RT_BIT_64(14) /**< Device can receive UFO */
+#define VIRTIONET_F_MRG_RXBUF RT_BIT_64(15) /**< Driver can merge receive buffers */
+#define VIRTIONET_F_STATUS RT_BIT_64(16) /**< Config status field is available */
+#define VIRTIONET_F_CTRL_VQ RT_BIT_64(17) /**< Control channel is available */
+#define VIRTIONET_F_CTRL_RX RT_BIT_64(18) /**< Control channel RX mode + MAC addr filtering */
+#define VIRTIONET_F_CTRL_VLAN RT_BIT_64(19) /**< Control channel VLAN filtering */
+#define VIRTIONET_F_CTRL_RX_EXTRA RT_BIT_64(20) /**< Control channel RX mode extra functions */
+#define VIRTIONET_F_GUEST_ANNOUNCE RT_BIT_64(21) /**< Driver can send gratuitous packets */
+#define VIRTIONET_F_MQ RT_BIT_64(22) /**< Support ultiqueue with auto receive steering */
+#define VIRTIONET_F_CTRL_MAC_ADDR RT_BIT_64(23) /**< Set MAC address through control channel */
+/** @} */
+
+#ifdef IN_RING3
+static const VIRTIO_FEATURES_LIST s_aDevSpecificFeatures[] =
+{
+ { VIRTIONET_F_STATUS, " STATUS Configuration status field is available.\n" },
+ { VIRTIONET_F_MAC, " MAC Host has given MAC address.\n" },
+ { VIRTIONET_F_CTRL_VQ, " CTRL_VQ Control channel is available.\n" },
+ { VIRTIONET_F_CTRL_MAC_ADDR, " CTRL_MAC_ADDR Set MAC address through control channel.\n" },
+ { VIRTIONET_F_CTRL_RX, " CTRL_RX Control channel RX mode support.\n" },
+ { VIRTIONET_F_CTRL_VLAN, " CTRL_VLAN Control channel VLAN filtering.\n" },
+ { VIRTIONET_F_CTRL_GUEST_OFFLOADS, " CTRL_GUEST_OFFLOADS Control channel offloads reconfiguration support.\n" },
+ { VIRTIONET_F_GUEST_CSUM, " GUEST_CSUM Guest handles packets with partial checksum.\n" },
+ { VIRTIONET_F_GUEST_ANNOUNCE, " GUEST_ANNOUNCE Guest can send gratuitous packets.\n" },
+ { VIRTIONET_F_GUEST_TSO4, " GUEST_TSO4 Guest can receive TSOv4.\n" },
+ { VIRTIONET_F_GUEST_TSO6, " GUEST_TSO6 Guest can receive TSOv6.\n" },
+ { VIRTIONET_F_GUEST_ECN, " GUEST_ECN Guest can receive TSO with ECN.\n" },
+ { VIRTIONET_F_GUEST_UFO, " GUEST_UFO Guest can receive UFO.\n" },
+ { VIRTIONET_F_HOST_TSO4, " HOST_TSO4 Host can receive TSOv4.\n" },
+ { VIRTIONET_F_HOST_TSO6, " HOST_TSO6 Host can receive TSOv6.\n" },
+ { VIRTIONET_F_HOST_ECN, " HOST_ECN Host can receive TSO with ECN.\n" },
+ { VIRTIONET_F_HOST_UFO, " HOST_UFO Host can receive UFO.\n" },
+ { VIRTIONET_F_MQ, " MQ Host supports multiqueue with automatic receive steering.\n" },
+ { VIRTIONET_F_CSUM, " CSUM Host handles packets with partial checksum.\n" },
+ { VIRTIONET_F_MRG_RXBUF, " MRG_RXBUF Guest can merge receive buffers.\n" },
+};
+#endif
+
+#ifdef VIRTIONET_WITH_GSO
+# define VIRTIONET_HOST_FEATURES_GSO \
+ VIRTIONET_F_CSUM \
+ | VIRTIONET_F_HOST_TSO4 \
+ | VIRTIONET_F_HOST_TSO6 \
+ | VIRTIONET_F_HOST_UFO \
+ | VIRTIONET_F_GUEST_TSO4 \
+ | VIRTIONET_F_GUEST_TSO6 \
+ | VIRTIONET_F_GUEST_UFO \
+ | VIRTIONET_F_GUEST_CSUM /* @bugref(4796) Guest must handle partial chksums */
+#else
+# define VIRTIONET_HOST_FEATURES_GSO
+#endif
+
+#define VIRTIONET_HOST_FEATURES_OFFERED \
+ VIRTIONET_F_STATUS \
+ | VIRTIONET_F_GUEST_ANNOUNCE \
+ | VIRTIONET_F_MAC \
+ | VIRTIONET_F_CTRL_VQ \
+ | VIRTIONET_F_CTRL_RX \
+ | VIRTIONET_F_CTRL_VLAN \
+ | VIRTIONET_HOST_FEATURES_GSO \
+ | VIRTIONET_F_MRG_RXBUF
+
+#define FEATURE_ENABLED(feature) RT_BOOL(!!(pThis->fNegotiatedFeatures & VIRTIONET_F_##feature))
+#define FEATURE_DISABLED(feature) (!FEATURE_ENABLED(feature))
+#define FEATURE_OFFERED(feature) VIRTIONET_HOST_FEATURES_OFFERED & VIRTIONET_F_##feature
+
+#if FEATURE_OFFERED(MQ)
+/* Instance data doesn't allow an array large enough to contain VIRTIONET_CTRL_MQ_VQ_PAIRS_MAX entries */
+# define VIRTIONET_MAX_QPAIRS 1 /* This should be increased at some point and made to work */
+#else
+# define VIRTIONET_MAX_QPAIRS VIRTIONET_CTRL_MQ_VQ_PAIRS_MIN /* default, VirtIO 1.0, 5.1.6.5.5 */
+#endif
+
+#define VIRTIONET_CTRL_MQ_VQ_PAIRS 64
+#define VIRTIONET_MAX_WORKERS VIRTIONET_MAX_QPAIRS + 1
+#define VIRTIONET_MAX_VIRTQS (VIRTIONET_MAX_QPAIRS * 2 + 1)
+#define VIRTIONET_MAX_FRAME_SIZE 65535 + 18 /**< Max IP pkt size + Eth. header w/VLAN tag */
+#define VIRTIONET_MAC_FILTER_LEN 64
+#define VIRTIONET_MAX_VLAN_ID 4096
+#define VIRTIONET_RX_SEG_COUNT 32
+
+#define VIRTQNAME(uVirtqNbr) (pThis->aVirtqs[uVirtqNbr]->szName)
+#define CBVIRTQNAME(uVirtqNbr) RTStrNLen(VIRTQNAME(uVirtqNbr), sizeof(VIRTQNAME(uVirtqNbr)))
+
+#define IS_TX_VIRTQ(n) ((n) != CTRLQIDX && ((n) & 1))
+#define IS_RX_VIRTQ(n) ((n) != CTRLQIDX && !IS_TX_VIRTQ(n))
+#define IS_CTRL_VIRTQ(n) ((n) == CTRLQIDX)
+
+/*
+ * Macros to calculate queue type-pecific index number regardless of scale. VirtIO 1.0, 5.1.2
+ */
+#define RXQIDX(qPairIdx) (qPairIdx * 2)
+#define TXQIDX(qPairIdx) (RXQIDX(qPairIdx) + 1)
+#define CTRLQIDX (FEATURE_ENABLED(MQ) ? ((VIRTIONET_MAX_QPAIRS - 1) * 2 + 2) : 2)
+
+#define IS_LINK_UP(pState) !!(pState->virtioNetConfig.uStatus & VIRTIONET_F_LINK_UP)
+#define IS_LINK_DOWN(pState) !IS_LINK_UP(pState)
+
+#define SET_LINK_UP(pState) \
+ LogFunc(("SET_LINK_UP\n")); \
+ pState->virtioNetConfig.uStatus |= VIRTIONET_F_LINK_UP; \
+ virtioCoreNotifyConfigChanged(&pThis->Virtio)
+
+#define SET_LINK_DOWN(pState) \
+ LogFunc(("SET_LINK_DOWN\n")); \
+ pState->virtioNetConfig.uStatus &= ~VIRTIONET_F_LINK_UP; \
+ virtioCoreNotifyConfigChanged(&pThis->Virtio)
+
+#define IS_VIRTQ_EMPTY(pDevIns, pVirtio, uVirtqNbr) \
+ (virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr) == 0)
+
+#define PCI_DEVICE_ID_VIRTIONET_HOST 0x1000 /**< VirtIO transitional device ID for network card */
+#define PCI_CLASS_PROG_UNSPECIFIED 0x00 /**< Programming interface. N/A. */
+#define VIRTIONET_PCI_CLASS 0x01 /**< Base class Mass Storage? */
+
+/**
+ * VirtIO Network (virtio-net) device-specific configuration subregion (VirtIO 1.0, 5.1.4)
+ * Guest MMIO is processed through callback to VirtIO core which forwards references to network configuration
+ * fields to this device-specific code through a callback.
+ */
+#pragma pack(1)
+
+ typedef struct virtio_net_config
+ {
+ RTMAC uMacAddress; /**< mac */
+
+#if FEATURE_OFFERED(STATUS)
+ uint16_t uStatus; /**< status */
+#endif
+
+#if FEATURE_OFFERED(MQ)
+ uint16_t uMaxVirtqPairs; /**< max_virtq_pairs */
+#endif
+
+ } VIRTIONET_CONFIG_T, PVIRTIONET_CONFIG_T;
+
+#pragma pack()
+
+#define VIRTIONET_F_LINK_UP 1 /**< config status: Link is up */
+#define VIRTIONET_F_ANNOUNCE 2 /**< config status: Announce */
+
+/** @name VirtIO 1.0 NET Host Device device specific control types
+ * @{ */
+#define VIRTIONET_HDR_F_NEEDS_CSUM 1 /**< flags: Packet needs checksum */
+#define VIRTIONET_HDR_GSO_NONE 0 /**< gso_type: No Global Segmentation Offset */
+#define VIRTIONET_HDR_GSO_TCPV4 1 /**< gso_type: Global Segment Offset for TCPV4 */
+#define VIRTIONET_HDR_GSO_UDP 3 /**< gso_type: Global Segment Offset for UDP */
+#define VIRTIONET_HDR_GSO_TCPV6 4 /**< gso_type: Global Segment Offset for TCPV6 */
+#define VIRTIONET_HDR_GSO_ECN 0x80 /**< gso_type: Explicit Congestion Notification */
+/** @} */
+
+/* Device operation: Net header packet (VirtIO 1.0, 5.1.6) */
+#pragma pack(1)
+struct virtio_net_pkt_hdr {
+ uint8_t uFlags; /**< flags */
+ uint8_t uGsoType; /**< gso_type */
+ uint16_t uHdrLen; /**< hdr_len */
+ uint16_t uGsoSize; /**< gso_size */
+ uint16_t uChksumStart; /**< Chksum_start */
+ uint16_t uChksumOffset; /**< Chksum_offset */
+ uint16_t uNumBuffers; /**< num_buffers */
+};
+#pragma pack()
+typedef virtio_net_pkt_hdr VIRTIONETPKTHDR, *PVIRTIONETPKTHDR;
+AssertCompileSize(VIRTIONETPKTHDR, 12);
+
+/* Control virtq: Command entry (VirtIO 1.0, 5.1.6.5) */
+#pragma pack(1)
+struct virtio_net_ctrl_hdr {
+ uint8_t uClass; /**< class */
+ uint8_t uCmd; /**< command */
+};
+#pragma pack()
+typedef virtio_net_ctrl_hdr VIRTIONET_CTRL_HDR_T, *PVIRTIONET_CTRL_HDR_T;
+
+typedef uint8_t VIRTIONET_CTRL_HDR_T_ACK;
+
+/* Command entry fAck values */
+#define VIRTIONET_OK 0 /**< Internal success status */
+#define VIRTIONET_ERROR 1 /**< Internal failure status */
+
+/** @name Control virtq: Receive filtering flags (VirtIO 1.0, 5.1.6.5.1)
+ * @{ */
+#define VIRTIONET_CTRL_RX 0 /**< Control class: Receive filtering */
+#define VIRTIONET_CTRL_RX_PROMISC 0 /**< Promiscuous mode */
+#define VIRTIONET_CTRL_RX_ALLMULTI 1 /**< All-multicast receive */
+#define VIRTIONET_CTRL_RX_ALLUNI 2 /**< All-unicast receive */
+#define VIRTIONET_CTRL_RX_NOMULTI 3 /**< No multicast receive */
+#define VIRTIONET_CTRL_RX_NOUNI 4 /**< No unicast receive */
+#define VIRTIONET_CTRL_RX_NOBCAST 5 /**< No broadcast receive */
+/** @} */
+
+typedef uint8_t VIRTIONET_MAC_ADDRESS[6];
+typedef uint32_t VIRTIONET_CTRL_MAC_TABLE_LEN;
+typedef uint8_t VIRTIONET_CTRL_MAC_ENTRIES[][6];
+
+/** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.2)
+ * @{ */
+#define VIRTIONET_CTRL_MAC 1 /**< Control class: MAC address filtering */
+#define VIRTIONET_CTRL_MAC_TABLE_SET 0 /**< Set MAC table */
+#define VIRTIONET_CTRL_MAC_ADDR_SET 1 /**< Set default MAC address */
+/** @} */
+
+/** @name Control virtq: MAC address filtering flags (VirtIO 1.0, 5.1.6.5.3)
+ * @{ */
+#define VIRTIONET_CTRL_VLAN 2 /**< Control class: VLAN filtering */
+#define VIRTIONET_CTRL_VLAN_ADD 0 /**< Add VLAN to filter table */
+#define VIRTIONET_CTRL_VLAN_DEL 1 /**< Delete VLAN from filter table */
+/** @} */
+
+/** @name Control virtq: Gratuitous packet sending (VirtIO 1.0, 5.1.6.5.4)
+ * @{ */
+#define VIRTIONET_CTRL_ANNOUNCE 3 /**< Control class: Gratuitous Packet Sending */
+#define VIRTIONET_CTRL_ANNOUNCE_ACK 0 /**< Gratuitous Packet Sending ACK */
+/** @} */
+
+struct virtio_net_ctrl_mq {
+ uint16_t uVirtqueuePairs; /**< virtqueue_pairs */
+};
+
+/** @name Control virtq: Receive steering in multiqueue mode (VirtIO 1.0, 5.1.6.5.5)
+ * @{ */
+#define VIRTIONET_CTRL_MQ 4 /**< Control class: Receive steering */
+#define VIRTIONET_CTRL_MQ_VQ_PAIRS_SET 0 /**< Set number of TX/RX queues */
+#define VIRTIONET_CTRL_MQ_VQ_PAIRS_MIN 1 /**< Minimum number of TX/RX queues */
+#define VIRTIONET_CTRL_MQ_VQ_PAIRS_MAX 0x8000 /**< Maximum number of TX/RX queues */
+/** @} */
+
+uint64_t uOffloads; /**< offloads */
+
+/** @name Control virtq: Setting Offloads State (VirtIO 1.0, 5.1.6.5.6.1)
+ * @{ */
+#define VIRTIONET_CTRL_GUEST_OFFLOADS 5 /**< Control class: Offloads state configuration */
+#define VIRTIONET_CTRL_GUEST_OFFLOADS_SET 0 /**< Apply new offloads configuration */
+/** @} */
+
+typedef enum VIRTIONETPKTHDRTYPE
+{
+ kVirtioNetUninitializedPktHdrType = 0, /**< Uninitialized (default) packet header type */
+ kVirtioNetModernPktHdrWithoutMrgRx = 1, /**< Packets should not be merged (modern driver) */
+ kVirtioNetModernPktHdrWithMrgRx = 2, /**< Packets should be merged (modern driver) */
+ kVirtioNetLegacyPktHdrWithoutMrgRx = 3, /**< Packets should not be merged (legacy driver) */
+ kVirtioNetLegacyPktHdrWithMrgRx = 4, /**< Packets should be merged (legacy driver) */
+ kVirtioNetFor32BitHack = 0x7fffffff
+} VIRTIONETPKTHDRTYPE;
+
+/**
+ * device-specific queue info
+ */
+struct VIRTIONETWORKER;
+struct VIRTIONETWORKERR3;
+
+typedef struct VIRTIONETVIRTQ
+{
+ uint16_t uIdx; /**< Index of this queue */
+ uint16_t align;
+ bool fCtlVirtq; /**< If set this queue is the control queue */
+ bool fHasWorker; /**< If set this queue has an associated worker */
+ bool fAttachedToVirtioCore; /**< Set if queue attached to virtio core */
+ char szName[VIRTIO_MAX_VIRTQ_NAME_SIZE]; /**< Virtq name */
+} VIRTIONETVIRTQ, *PVIRTIONETVIRTQ;
+
+/**
+ * Worker thread context, shared state.
+ */
+typedef struct VIRTIONETWORKER
+{
+ SUPSEMEVENT hEvtProcess; /**< handle of associated sleep/wake-up semaphore */
+ uint16_t uIdx; /**< Index of this worker */
+ bool volatile fSleeping; /**< Flags whether worker thread is sleeping or not */
+ bool volatile fNotified; /**< Flags whether worker thread notified */
+ bool fAssigned; /**< Flags whether worker thread has been set up */
+ uint8_t pad;
+} VIRTIONETWORKER;
+/** Pointer to a virtio net worker. */
+typedef VIRTIONETWORKER *PVIRTIONETWORKER;
+
+/**
+ * Worker thread context, ring-3 state.
+ */
+typedef struct VIRTIONETWORKERR3
+{
+ R3PTRTYPE(PPDMTHREAD) pThread; /**< pointer to worker thread's handle */
+ uint16_t uIdx; /**< Index of this worker */
+ uint16_t pad;
+} VIRTIONETWORKERR3;
+/** Pointer to a virtio net worker. */
+typedef VIRTIONETWORKERR3 *PVIRTIONETWORKERR3;
+
+/**
+ * VirtIO Host NET device state, shared edition.
+ *
+ * @extends VIRTIOCORE
+ */
+typedef struct VIRTIONET
+{
+ /** The core virtio state. */
+ VIRTIOCORE Virtio;
+
+ /** Virtio device-specific configuration */
+ VIRTIONET_CONFIG_T virtioNetConfig;
+
+ /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
+ VIRTIONETWORKER aWorkers[VIRTIONET_MAX_VIRTQS];
+
+ /** Track which VirtIO queues we've attached to */
+ VIRTIONETVIRTQ aVirtqs[VIRTIONET_MAX_VIRTQS];
+
+ /** PDM device Instance name */
+ char szInst[16];
+
+ /** VirtIO features negotiated with the guest, including generic core and device specific */
+ uint64_t fNegotiatedFeatures;
+
+ /** Number of Rx/Tx queue pairs (only one if MQ feature not negotiated */
+ uint16_t cVirtqPairs;
+
+ /** Number of Rx/Tx queue pairs that have already been initialized */
+ uint16_t cInitializedVirtqPairs;
+
+ /** Number of virtqueues total (which includes each queue of each pair plus one control queue */
+ uint16_t cVirtqs;
+
+ /** Number of worker threads (one for the control queue and one for each Tx queue) */
+ uint16_t cWorkers;
+
+ /** Alignment */
+ uint16_t alignment;
+
+ /** Indicates transmission in progress -- only one thread is allowed. */
+ uint32_t uIsTransmitting;
+
+ /** Link up delay (in milliseconds). */
+ uint32_t cMsLinkUpDelay;
+
+ /** The number of actually used slots in aMacMulticastFilter. */
+ uint32_t cMulticastFilterMacs;
+
+ /** The number of actually used slots in aMacUniicastFilter. */
+ uint32_t cUnicastFilterMacs;
+
+ /** Semaphore leaf device's thread waits on until guest driver sends empty Rx bufs */
+ SUPSEMEVENT hEventRxDescAvail;
+
+ /** Array of MAC multicast addresses accepted by RX filter. */
+ RTMAC aMacMulticastFilter[VIRTIONET_MAC_FILTER_LEN];
+
+ /** Array of MAC unicast addresses accepted by RX filter. */
+ RTMAC aMacUnicastFilter[VIRTIONET_MAC_FILTER_LEN];
+
+ /** Default MAC address which rx filtering accepts */
+ RTMAC rxFilterMacDefault;
+
+ /** MAC address obtained from the configuration. */
+ RTMAC macConfigured;
+
+ /** Bit array of VLAN filter, one bit per VLAN ID. */
+ uint8_t aVlanFilter[VIRTIONET_MAX_VLAN_ID / sizeof(uint8_t)];
+
+ /** Set if PDM leaf device at the network interface is starved for Rx buffers */
+ bool volatile fLeafWantsEmptyRxBufs;
+
+ /** Number of packet being sent/received to show in debug log. */
+ uint32_t uPktNo;
+
+ /** Flags whether VirtIO core is in ready state */
+ uint8_t fVirtioReady;
+
+ /** Resetting flag */
+ uint8_t fResetting;
+
+ /** Promiscuous mode -- RX filter accepts all packets. */
+ uint8_t fPromiscuous;
+
+ /** All multicast mode -- RX filter accepts all multicast packets. */
+ uint8_t fAllMulticast;
+
+ /** All unicast mode -- RX filter accepts all unicast packets. */
+ uint8_t fAllUnicast;
+
+ /** No multicast mode - Supresses multicast receive */
+ uint8_t fNoMulticast;
+
+ /** No unicast mode - Suppresses unicast receive */
+ uint8_t fNoUnicast;
+
+ /** No broadcast mode - Supresses broadcast receive */
+ uint8_t fNoBroadcast;
+
+ /** Type of network pkt header based on guest driver version/features */
+ VIRTIONETPKTHDRTYPE ePktHdrType;
+
+ /** Size of network pkt header based on guest driver version/features */
+ uint16_t cbPktHdr;
+
+ /** True if physical cable is attached in configuration. */
+ bool fCableConnected;
+
+ /** True if this device should offer legacy virtio support to the guest */
+ bool fOfferLegacy;
+
+ /** @name Statistic
+ * @{ */
+ STAMCOUNTER StatReceiveBytes;
+ STAMCOUNTER StatTransmitBytes;
+ STAMCOUNTER StatReceiveGSO;
+ STAMCOUNTER StatTransmitPackets;
+ STAMCOUNTER StatTransmitGSO;
+ STAMCOUNTER StatTransmitCSum;
+#ifdef VBOX_WITH_STATISTICS
+ STAMPROFILE StatReceive;
+ STAMPROFILE StatReceiveStore;
+ STAMPROFILEADV StatTransmit;
+ STAMPROFILE StatTransmitSend;
+ STAMPROFILE StatRxOverflow;
+ STAMCOUNTER StatRxOverflowWakeup;
+ STAMCOUNTER StatTransmitByNetwork;
+ STAMCOUNTER StatTransmitByThread;
+ /** @} */
+#endif
+} VIRTIONET;
+/** Pointer to the shared state of the VirtIO Host NET device. */
+typedef VIRTIONET *PVIRTIONET;
+
+/**
+ * VirtIO Host NET device state, ring-3 edition.
+ *
+ * @extends VIRTIOCORER3
+ */
+typedef struct VIRTIONETR3
+{
+ /** The core virtio ring-3 state. */
+ VIRTIOCORER3 Virtio;
+
+ /** Per device-bound virtq worker-thread contexts (eventq slot unused) */
+ VIRTIONETWORKERR3 aWorkers[VIRTIONET_MAX_VIRTQS];
+
+ /** The device instance.
+ * @note This is _only_ for use whxen dealing with interface callbacks. */
+ PPDMDEVINSR3 pDevIns;
+
+ /** Status LUN: Base interface. */
+ PDMIBASE IBase;
+
+ /** Status LUN: LED port interface. */
+ PDMILEDPORTS ILeds;
+
+ /** Status LUN: LED connector (peer). */
+ R3PTRTYPE(PPDMILEDCONNECTORS) pLedsConnector;
+
+ /** Status: LED */
+ PDMLED led;
+
+ /** Attached network driver. */
+ R3PTRTYPE(PPDMIBASE) pDrvBase;
+
+ /** Network port interface (down) */
+ PDMINETWORKDOWN INetworkDown;
+
+ /** Network config port interface (main). */
+ PDMINETWORKCONFIG INetworkConfig;
+
+ /** Connector of attached network driver. */
+ R3PTRTYPE(PPDMINETWORKUP) pDrv;
+
+ /** Link Up(/Restore) Timer. */
+ TMTIMERHANDLE hLinkUpTimer;
+
+} VIRTIONETR3;
+
+/** Pointer to the ring-3 state of the VirtIO Host NET device. */
+typedef VIRTIONETR3 *PVIRTIONETR3;
+
+/**
+ * VirtIO Host NET device state, ring-0 edition.
+ */
+typedef struct VIRTIONETR0
+{
+ /** The core virtio ring-0 state. */
+ VIRTIOCORER0 Virtio;
+} VIRTIONETR0;
+/** Pointer to the ring-0 state of the VirtIO Host NET device. */
+typedef VIRTIONETR0 *PVIRTIONETR0;
+
+/**
+ * VirtIO Host NET device state, raw-mode edition.
+ */
+typedef struct VIRTIONETRC
+{
+ /** The core virtio raw-mode state. */
+ VIRTIOCORERC Virtio;
+} VIRTIONETRC;
+/** Pointer to the ring-0 state of the VirtIO Host NET device. */
+typedef VIRTIONETRC *PVIRTIONETRC;
+
+/** @typedef VIRTIONETCC
+ * The instance data for the current context. */
+typedef CTX_SUFF(VIRTIONET) VIRTIONETCC;
+
+/** @typedef PVIRTIONETCC
+ * Pointer to the instance data for the current context. */
+typedef CTX_SUFF(PVIRTIONET) PVIRTIONETCC;
+
+#ifdef IN_RING3
+static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread);
+static int virtioNetR3CreateWorkerThreads(PPDMDEVINS, PVIRTIONET, PVIRTIONETCC);
+
+/**
+ * Helper function used when logging state of a VM thread.
+ *
+ * @param Thread
+ *
+ * @return Associated name of thread as a pointer to a zero-terminated string.
+ */
+DECLINLINE(const char *) virtioNetThreadStateName(PPDMTHREAD pThread)
+{
+ if (!pThread)
+ return "<null>";
+
+ switch(pThread->enmState)
+ {
+ case PDMTHREADSTATE_INVALID:
+ return "invalid state";
+ case PDMTHREADSTATE_INITIALIZING:
+ return "initializing";
+ case PDMTHREADSTATE_SUSPENDING:
+ return "suspending";
+ case PDMTHREADSTATE_SUSPENDED:
+ return "suspended";
+ case PDMTHREADSTATE_RESUMING:
+ return "resuming";
+ case PDMTHREADSTATE_RUNNING:
+ return "running";
+ case PDMTHREADSTATE_TERMINATING:
+ return "terminating";
+ case PDMTHREADSTATE_TERMINATED:
+ return "terminated";
+ default:
+ return "unknown state";
+ }
+}
+#endif
+
+/**
+ * Wakeup PDM managed downstream (e.g. hierarchically inferior device's) RX thread
+ */
+static DECLCALLBACK(void) virtioNetWakeupRxBufWaiter(PPDMDEVINS pDevIns)
+{
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+
+ AssertReturnVoid(pThis->hEventRxDescAvail != NIL_SUPSEMEVENT);
+
+ STAM_COUNTER_INC(&pThis->StatRxOverflowWakeup);
+ if (pThis->hEventRxDescAvail != NIL_SUPSEMEVENT)
+ {
+ Log10Func(("[%s] Waking downstream device's Rx buf waiter thread\n", pThis->szInst));
+ int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventRxDescAvail);
+ AssertRC(rc);
+ }
+}
+
+/**
+ * Guest notifying us of its activity with a queue. Figure out which queue and respond accordingly.
+ *
+ * @callback_method_impl{VIRTIOCORER0,pfnVirtqNotified}
+ */
+static DECLCALLBACK(void) virtioNetVirtqNotified(PPDMDEVINS pDevIns, PVIRTIOCORE pVirtio, uint16_t uVirtqNbr)
+{
+ RT_NOREF(pVirtio);
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+
+ PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
+ PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
+
+#if defined (IN_RING3) && defined (LOG_ENABLED)
+ RTLogFlush(NULL);
+#endif
+ if (IS_RX_VIRTQ(uVirtqNbr))
+ {
+ uint16_t cBufsAvailable = virtioCoreVirtqAvailBufCount(pDevIns, pVirtio, uVirtqNbr);
+
+ if (cBufsAvailable)
+ {
+ Log10Func(("%s %u empty bufs added to %s by guest (notifying leaf device)\n",
+ pThis->szInst, cBufsAvailable, pVirtq->szName));
+ virtioNetWakeupRxBufWaiter(pDevIns);
+ }
+ else
+ Log10Func(("%s \n\n***WARNING: %s notified but no empty bufs added by guest! (skip leaf dev. notification)\n\n",
+ pThis->szInst, pVirtq->szName));
+ }
+ else if (IS_TX_VIRTQ(uVirtqNbr) || IS_CTRL_VIRTQ(uVirtqNbr))
+ {
+ /* Wake queue's worker thread up if sleeping (e.g. a Tx queue, or the control queue */
+ if (!ASMAtomicXchgBool(&pWorker->fNotified, true))
+ {
+ if (ASMAtomicReadBool(&pWorker->fSleeping))
+ {
+ Log10Func(("[%s] %s has available buffers - waking worker.\n", pThis->szInst, pVirtq->szName));
+
+ int rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
+ AssertRC(rc);
+ }
+ else
+ Log10Func(("[%s] %s has available buffers - worker already awake\n", pThis->szInst, pVirtq->szName));
+ }
+ else
+ Log10Func(("[%s] %s has available buffers - waking worker.\n", pThis->szInst, pVirtq->szName));
+ }
+ else
+ LogRelFunc(("[%s] unrecognized queue %s (idx=%d) notified\n", pThis->szInst, pVirtq->szName, uVirtqNbr));
+}
+
+#ifdef IN_RING3 /* spans most of the file, at the moment. */
+
+/**
+ * @callback_method_impl{FNPDMTHREADWAKEUPDEV}
+ */
+static DECLCALLBACK(int) virtioNetR3WakeupWorker(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
+{
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+ PVIRTIONETWORKER pWorker = (PVIRTIONETWORKER)pThread->pvUser;
+
+ Log10Func(("[%s]\n", pThis->szInst));
+ RT_NOREF(pThis);
+ return PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
+}
+
+/**
+ * Set queue names, distinguishing between modern or legacy mode.
+ *
+ * @note This makes it obvious during logging which mode this transitional device is
+ * operating in, legacy or modern.
+ *
+ * @param pThis Device specific device state
+ * @param fLegacy (input) true if running in legacy mode
+ * false if running in modern mode
+ */
+DECLINLINE(void) virtioNetR3SetVirtqNames(PVIRTIONET pThis, uint32_t fLegacy)
+{
+ RTStrCopy(pThis->aVirtqs[CTRLQIDX].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, fLegacy ? "legacy-ctrlq" : " modern-ctrlq");
+ for (uint16_t qPairIdx = 0; qPairIdx < pThis->cVirtqPairs; qPairIdx++)
+ {
+ RTStrPrintf(pThis->aVirtqs[RXQIDX(qPairIdx)].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, "%s-recvq<%d>", fLegacy ? "legacy" : "modern", qPairIdx);
+ RTStrPrintf(pThis->aVirtqs[TXQIDX(qPairIdx)].szName, VIRTIO_MAX_VIRTQ_NAME_SIZE, "%s-xmitq<%d>", fLegacy ? "legacy" : "modern", qPairIdx);
+ }
+}
+
+/**
+ * Dump a packet to debug log.
+ *
+ * @param pThis The virtio-net shared instance data.
+ * @param pbPacket The packet.
+ * @param cb The size of the packet.
+ * @param pszText A string denoting direction of packet transfer.
+ */
+DECLINLINE(void) virtioNetR3PacketDump(PVIRTIONET pThis, const uint8_t *pbPacket, size_t cb, const char *pszText)
+{
+#ifdef LOG_ENABLED
+ if (!LogIs12Enabled())
+ return;
+#endif
+ vboxEthPacketDump(pThis->szInst, pszText, pbPacket, (uint32_t)cb);
+}
+
+#ifdef LOG_ENABLED
+void virtioNetDumpGcPhysRxBuf(PPDMDEVINS pDevIns, PVIRTIONETPKTHDR pRxPktHdr,
+ uint16_t cVirtqBufs, uint8_t *pvBuf, uint16_t cb, RTGCPHYS GCPhysRxBuf, uint8_t cbRxBuf)
+{
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+ pRxPktHdr->uNumBuffers = cVirtqBufs;
+ if (pRxPktHdr)
+ {
+ LogFunc(("%*c\nrxPktHdr\n"
+ " uFlags ......... %2.2x\n uGsoType ....... %2.2x\n uHdrLen ........ %4.4x\n"
+ " uGsoSize ....... %4.4x\n uChksumStart ... %4.4x\n uChksumOffset .. %4.4x\n",
+ 60, ' ', pRxPktHdr->uFlags, pRxPktHdr->uGsoType, pRxPktHdr->uHdrLen, pRxPktHdr->uGsoSize,
+ pRxPktHdr->uChksumStart, pRxPktHdr->uChksumOffset));
+ if (!virtioCoreIsLegacyMode(&pThis->Virtio) || FEATURE_ENABLED(MRG_RXBUF))
+ LogFunc((" uNumBuffers .... %4.4x\n", pRxPktHdr->uNumBuffers));
+ virtioCoreHexDump((uint8_t *)pRxPktHdr, sizeof(VIRTIONETPKTHDR), 0, "Dump of virtual rPktHdr");
+ }
+ virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
+ LogFunc((". . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .\n"));
+ virtioCoreGCPhysHexDump(pDevIns, GCPhysRxBuf, cbRxBuf, 0, "Phys Mem Dump of Rx pkt");
+ LogFunc(("%*c", 60, '-'));
+}
+
+#endif /* LOG_ENABLED */
+
+/**
+ * @callback_method_impl{FNDBGFHANDLERDEV, virtio-net debugger info callback.}
+ */
+static DECLCALLBACK(void) virtioNetR3Info(PPDMDEVINS pDevIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+ PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+
+ bool fNone = pszArgs && *pszArgs == '\0';
+ bool fAll = pszArgs && (*pszArgs == 'a' || *pszArgs == 'A'); /* "all" */
+ bool fNetwork = pszArgs && (*pszArgs == 'n' || *pszArgs == 'N'); /* "network" */
+ bool fFeatures = pszArgs && (*pszArgs == 'f' || *pszArgs == 'F'); /* "features" */
+ bool fState = pszArgs && (*pszArgs == 's' || *pszArgs == 'S'); /* "state" */
+ bool fPointers = pszArgs && (*pszArgs == 'p' || *pszArgs == 'P'); /* "pointers" */
+ bool fVirtqs = pszArgs && (*pszArgs == 'q' || *pszArgs == 'Q'); /* "queues */
+
+ /* Show basic information. */
+ pHlp->pfnPrintf(pHlp,
+ "\n"
+ "---------------------------------------------------------------------------\n"
+ "Debug Info: %s\n"
+ " (options: [a]ll, [n]et, [f]eatures, [s]tate, [p]ointers, [q]ueues)\n"
+ "---------------------------------------------------------------------------\n\n",
+ pThis->szInst);
+
+ if (fNone)
+ return;
+
+ /* Show offered/unoffered, accepted/rejected features */
+ if (fAll || fFeatures)
+ {
+ virtioCorePrintDeviceFeatures(&pThis->Virtio, pHlp, s_aDevSpecificFeatures,
+ RT_ELEMENTS(s_aDevSpecificFeatures));
+ pHlp->pfnPrintf(pHlp, "\n");
+ }
+
+ /* Show queues (and associate worker info if applicable) */
+ if (fAll || fVirtqs)
+ {
+ pHlp->pfnPrintf(pHlp, "Virtq information:\n\n");
+ for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
+ {
+ PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
+
+ if (pVirtq->fHasWorker)
+ {
+ PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
+ PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uVirtqNbr];
+
+ Assert((pWorker->uIdx == pVirtq->uIdx));
+ Assert((pWorkerR3->uIdx == pVirtq->uIdx));
+
+ if (pWorker->fAssigned)
+ {
+ pHlp->pfnPrintf(pHlp, " %-15s (pThread: %p %s) ",
+ pVirtq->szName,
+ pWorkerR3->pThread,
+ virtioNetThreadStateName(pWorkerR3->pThread));
+ if (pVirtq->fAttachedToVirtioCore)
+ {
+ pHlp->pfnPrintf(pHlp, "worker: ");
+ pHlp->pfnPrintf(pHlp, "%s", pWorker->fSleeping ? "blocking" : "unblocked");
+ pHlp->pfnPrintf(pHlp, "%s", pWorker->fNotified ? ", notified" : "");
+ }
+ else
+ if (pWorker->fNotified)
+ pHlp->pfnPrintf(pHlp, "not attached to virtio core");
+ }
+ }
+ else
+ {
+ pHlp->pfnPrintf(pHlp, " %-15s (INetworkDown's thread) %s", pVirtq->szName,
+ pVirtq->fAttachedToVirtioCore ? "" : "not attached to virtio core");
+ }
+ pHlp->pfnPrintf(pHlp, "\n");
+ virtioCoreR3VirtqInfo(pDevIns, pHlp, pszArgs, uVirtqNbr);
+ pHlp->pfnPrintf(pHlp, " ---------------------------------------------------------------------\n");
+ pHlp->pfnPrintf(pHlp, "\n");
+ }
+ pHlp->pfnPrintf(pHlp, "\n");
+ }
+
+ /* Show various pointers */
+ if (fAll || fPointers)
+ {
+ pHlp->pfnPrintf(pHlp, "Internal Pointers (for instance \"%s\"):\n\n", pThis->szInst);
+ pHlp->pfnPrintf(pHlp, " pDevIns ................... %p\n", pDevIns);
+ pHlp->pfnPrintf(pHlp, " PVIRTIOCORE ............... %p\n", &pThis->Virtio);
+ pHlp->pfnPrintf(pHlp, " PVIRTIONET ................ %p\n", pThis);
+ pHlp->pfnPrintf(pHlp, " PVIRTIONETCC .............. %p\n", pThisCC);
+ pHlp->pfnPrintf(pHlp, " VIRTIONETVIRTQ[] .......... %p\n", pThis->aVirtqs);
+ pHlp->pfnPrintf(pHlp, " pDrvBase .................. %p\n", pThisCC->pDrvBase);
+ pHlp->pfnPrintf(pHlp, " pDrv ...................... %p\n", pThisCC->pDrv);
+ pHlp->pfnPrintf(pHlp, "\n");
+ }
+
+ /* Show device state info */
+ if (fAll || fState)
+ {
+ pHlp->pfnPrintf(pHlp, "Device state:\n\n");
+ uint32_t fTransmitting = ASMAtomicReadU32(&pThis->uIsTransmitting);
+
+ pHlp->pfnPrintf(pHlp, " Transmitting: ............. %s\n", fTransmitting ? "true" : "false");
+ pHlp->pfnPrintf(pHlp, "\n");
+ pHlp->pfnPrintf(pHlp, "Misc state\n");
+ pHlp->pfnPrintf(pHlp, "\n");
+ pHlp->pfnPrintf(pHlp, " fOfferLegacy .............. %d\n", pThis->fOfferLegacy);
+ pHlp->pfnPrintf(pHlp, " fVirtioReady .............. %d\n", pThis->fVirtioReady);
+ pHlp->pfnPrintf(pHlp, " fResetting ................ %d\n", pThis->fResetting);
+ pHlp->pfnPrintf(pHlp, " fGenUpdatePending ......... %d\n", pThis->Virtio.fGenUpdatePending);
+ pHlp->pfnPrintf(pHlp, " fMsiSupport ............... %d\n", pThis->Virtio.fMsiSupport);
+ pHlp->pfnPrintf(pHlp, " uConfigGeneration ......... %d\n", pThis->Virtio.uConfigGeneration);
+ pHlp->pfnPrintf(pHlp, " uDeviceStatus ............. 0x%x\n", pThis->Virtio.fDeviceStatus);
+ pHlp->pfnPrintf(pHlp, " cVirtqPairs .,............. %d\n", pThis->cVirtqPairs);
+ pHlp->pfnPrintf(pHlp, " cVirtqs .,................. %d\n", pThis->cVirtqs);
+ pHlp->pfnPrintf(pHlp, " cWorkers .................. %d\n", pThis->cWorkers);
+ pHlp->pfnPrintf(pHlp, " MMIO mapping name ......... %d\n", pThisCC->Virtio.szMmioName);
+ pHlp->pfnPrintf(pHlp, "\n");
+ }
+
+ /* Show network related information */
+ if (fAll || fNetwork)
+ {
+ pHlp->pfnPrintf(pHlp, "Network configuration:\n\n");
+ pHlp->pfnPrintf(pHlp, " MAC: ...................... %RTmac\n", &pThis->macConfigured);
+ pHlp->pfnPrintf(pHlp, "\n");
+ pHlp->pfnPrintf(pHlp, " Cable: .................... %s\n", pThis->fCableConnected ? "connected" : "disconnected");
+ pHlp->pfnPrintf(pHlp, " Link-up delay: ............ %d ms\n", pThis->cMsLinkUpDelay);
+ pHlp->pfnPrintf(pHlp, "\n");
+ pHlp->pfnPrintf(pHlp, " Accept all multicast: ..... %s\n", pThis->fAllMulticast ? "true" : "false");
+ pHlp->pfnPrintf(pHlp, " Suppress broadcast: ....... %s\n", pThis->fNoBroadcast ? "true" : "false");
+ pHlp->pfnPrintf(pHlp, " Suppress unicast: ......... %s\n", pThis->fNoUnicast ? "true" : "false");
+ pHlp->pfnPrintf(pHlp, " Suppress multicast: ....... %s\n", pThis->fNoMulticast ? "true" : "false");
+ pHlp->pfnPrintf(pHlp, " Promiscuous: .............. %s\n", pThis->fPromiscuous ? "true" : "false");
+ pHlp->pfnPrintf(pHlp, "\n");
+ pHlp->pfnPrintf(pHlp, " Default Rx MAC filter: .... %RTmac\n", pThis->rxFilterMacDefault);
+ pHlp->pfnPrintf(pHlp, "\n");
+
+ pHlp->pfnPrintf(pHlp, " Unicast filter MACs:\n");
+
+ if (!pThis->cUnicastFilterMacs)
+ pHlp->pfnPrintf(pHlp, " <none>\n");
+
+ for (uint32_t i = 0; i < pThis->cUnicastFilterMacs; i++)
+ pHlp->pfnPrintf(pHlp, " %RTmac\n", &pThis->aMacUnicastFilter[i]);
+
+ pHlp->pfnPrintf(pHlp, "\n Multicast filter MACs:\n");
+
+ if (!pThis->cMulticastFilterMacs)
+ pHlp->pfnPrintf(pHlp, " <none>\n");
+
+ for (uint32_t i = 0; i < pThis->cMulticastFilterMacs; i++)
+ pHlp->pfnPrintf(pHlp, " %RTmac\n", &pThis->aMacMulticastFilter[i]);
+
+ pHlp->pfnPrintf(pHlp, "\n\n");
+ pHlp->pfnPrintf(pHlp, " Leaf starved: ............. %s\n", pThis->fLeafWantsEmptyRxBufs ? "true" : "false");
+ pHlp->pfnPrintf(pHlp, "\n");
+ }
+ /** @todo implement this
+ * pHlp->pfnPrintf(pHlp, "\n");
+ * virtioCoreR3Info(pDevIns, pHlp, pszArgs);
+ */
+ pHlp->pfnPrintf(pHlp, "\n");
+}
+
+/**
+ * Checks whether certain mutually dependent negotiated features are clustered in required combinations.
+ *
+ * @note See VirtIO 1.0 spec, Section 5.1.3.1
+ *
+ * @param fFeatures Bitmask of negotiated features to evaluate
+ *
+ * @returns true if valid feature combination(s) found.
+ * false if non-valid feature set.
+ */
+DECLINLINE(bool) virtioNetValidateRequiredFeatures(uint32_t fFeatures)
+{
+ uint32_t fGuestChksumRequired = fFeatures & VIRTIONET_F_GUEST_TSO4
+ || fFeatures & VIRTIONET_F_GUEST_TSO6
+ || fFeatures & VIRTIONET_F_GUEST_UFO;
+
+ uint32_t fHostChksumRequired = fFeatures & VIRTIONET_F_HOST_TSO4
+ || fFeatures & VIRTIONET_F_HOST_TSO6
+ || fFeatures & VIRTIONET_F_HOST_UFO;
+
+ uint32_t fCtrlVqRequired = fFeatures & VIRTIONET_F_CTRL_RX
+ || fFeatures & VIRTIONET_F_CTRL_VLAN
+ || fFeatures & VIRTIONET_F_GUEST_ANNOUNCE
+ || fFeatures & VIRTIONET_F_MQ
+ || fFeatures & VIRTIONET_F_CTRL_MAC_ADDR;
+
+ if (fGuestChksumRequired && !(fFeatures & VIRTIONET_F_GUEST_CSUM))
+ return false;
+
+ if (fHostChksumRequired && !(fFeatures & VIRTIONET_F_CSUM))
+ return false;
+
+ if (fCtrlVqRequired && !(fFeatures & VIRTIONET_F_CTRL_VQ))
+ return false;
+
+ if ( fFeatures & VIRTIONET_F_GUEST_ECN
+ && !( fFeatures & VIRTIONET_F_GUEST_TSO4
+ || fFeatures & VIRTIONET_F_GUEST_TSO6))
+ return false;
+
+ if ( fFeatures & VIRTIONET_F_HOST_ECN
+ && !( fFeatures & VIRTIONET_F_HOST_TSO4
+ || fFeatures & VIRTIONET_F_HOST_TSO6))
+ return false;
+ return true;
+}
+
+/**
+ * Read or write device-specific configuration parameters.
+ * This is called by VirtIO core code a guest-initiated MMIO access is made to access device-specific
+ * configuration
+ *
+ * @note See VirtIO 1.0 spec, 2.3 Device Configuration Space
+ *
+ * @param pThis Pointer to device-specific state
+ * @param uOffsetOfAccess Offset (within VIRTIONET_CONFIG_T)
+ * @param pv Pointer to data to read or write
+ * @param cb Number of bytes to read or write
+ * @param fWrite True if writing, false if reading
+ *
+ * @returns VINF_SUCCESS if successful, or VINF_IOM_MMIO_UNUSED if fails (bad offset or size)
+ */
+static int virtioNetR3DevCfgAccess(PVIRTIONET pThis, uint32_t uOffsetOfAccess, void *pv, uint32_t cb, bool fWrite)
+{
+ AssertReturn(pv && cb <= sizeof(uint32_t), fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00);
+
+ if (VIRTIO_DEV_CONFIG_SUBMATCH_MEMBER( uMacAddress, VIRTIONET_CONFIG_T, uOffsetOfAccess))
+ VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMacAddress, VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
+#if FEATURE_OFFERED(STATUS)
+ else
+ if (VIRTIO_DEV_CONFIG_SUBMATCH_MEMBER( uStatus, VIRTIONET_CONFIG_T, uOffsetOfAccess))
+ VIRTIO_DEV_CONFIG_ACCESS_READONLY( uStatus, VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
+#endif
+#if FEATURE_OFFERED(MQ)
+ else
+ if (VIRTIO_DEV_CONFIG_MATCH_MEMBER( uMaxVirtqPairs, VIRTIONET_CONFIG_T, uOffsetOfAccess))
+ VIRTIO_DEV_CONFIG_ACCESS_READONLY( uMaxVirtqPairs, VIRTIONET_CONFIG_T, uOffsetOfAccess, &pThis->virtioNetConfig);
+#endif
+ else
+ {
+ LogFunc(("%s Bad access by guest to virtio_net_config: off=%u (%#x), cb=%u\n",
+ pThis->szInst, uOffsetOfAccess, uOffsetOfAccess, cb));
+ return fWrite ? VINF_SUCCESS : VINF_IOM_MMIO_UNUSED_00;
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{VIRTIOCORER3,pfnDevCapRead}
+ */
+static DECLCALLBACK(int) virtioNetR3DevCapRead(PPDMDEVINS pDevIns, uint32_t uOffset, void *pv, uint32_t cb)
+{
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+
+ RT_NOREF(pThis);
+ return virtioNetR3DevCfgAccess(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, pv, cb, false /*fRead*/);
+}
+
+/**
+ * @callback_method_impl{VIRTIOCORER3,pfnDevCapWrite}
+ */
+static DECLCALLBACK(int) virtioNetR3DevCapWrite(PPDMDEVINS pDevIns, uint32_t uOffset, const void *pv, uint32_t cb)
+{
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+
+ Log10Func(("[%s] uOffset: %u, cb: %u: %.*Rhxs\n", pThis->szInst, uOffset, cb, cb, pv));
+ RT_NOREF(pThis);
+ return virtioNetR3DevCfgAccess(PDMDEVINS_2_DATA(pDevIns, PVIRTIONET), uOffset, (void *)pv, cb, true /*fWrite*/);
+}
+
+static int virtioNetR3VirtqDestroy(PVIRTIOCORE pVirtio, PVIRTIONETVIRTQ pVirtq)
+{
+ PVIRTIONET pThis = RT_FROM_MEMBER(pVirtio, VIRTIONET, Virtio);
+ PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pVirtio->pDevInsR3, PVIRTIONETCC);
+ PVIRTIONETWORKER pWorker = &pThis->aWorkers[pVirtq->uIdx];
+ PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[pVirtq->uIdx];
+
+ int rc = VINF_SUCCESS, rcThread;
+ Log10Func(("[%s] Destroying \"%s\"", pThis->szInst, pVirtq->szName));
+ if (pVirtq->fHasWorker)
+ {
+ Log10((" and its worker"));
+ rc = PDMDevHlpSUPSemEventClose(pVirtio->pDevInsR3, pWorker->hEvtProcess);
+ AssertRCReturn(rc, rc);
+ pWorker->hEvtProcess = 0;
+ rc = PDMDevHlpThreadDestroy(pVirtio->pDevInsR3, pWorkerR3->pThread, &rcThread);
+ AssertRCReturn(rc, rc);
+ pWorkerR3->pThread = 0;
+ pVirtq->fHasWorker = false;
+ }
+ pWorker->fAssigned = false;
+ pVirtq->fCtlVirtq = false;
+ Log10(("\n"));
+ return rc;
+}
+
+/**
+ * Takes down the link temporarily if its current status is up.
+ *
+ * This is used during restore and when replumbing the network link.
+ *
+ * The temporary link outage is supposed to indicate to the OS that all network
+ * connections have been lost and that it for instance is appropriate to
+ * renegotiate any DHCP lease.
+ *
+ * @param pDevIns The device instance.
+ * @param pThis The virtio-net shared instance data.
+ * @param pThisCC The virtio-net ring-3 instance data.
+ */
+static void virtioNetR3TempLinkDown(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
+{
+ if (IS_LINK_UP(pThis))
+ {
+ SET_LINK_DOWN(pThis);
+
+ /* Re-establish link in 5 seconds. */
+ int rc = PDMDevHlpTimerSetMillies(pDevIns, pThisCC->hLinkUpTimer, pThis->cMsLinkUpDelay);
+ AssertRC(rc);
+
+ LogFunc(("[%s] Link is down temporarily\n", pThis->szInst));
+ }
+}
+
+
+static void virtioNetConfigurePktHdr(PVIRTIONET pThis, uint32_t fLegacy)
+{
+ /* Calculate network packet header type and size based on what we know now */
+ pThis->cbPktHdr = sizeof(VIRTIONETPKTHDR);
+ if (!fLegacy)
+ /* Modern (e.g. >= VirtIO 1.0) device specification's pkt size rules */
+ if (FEATURE_ENABLED(MRG_RXBUF))
+ pThis->ePktHdrType = kVirtioNetModernPktHdrWithMrgRx;
+ else /* Modern guest driver with MRG_RX feature disabled */
+ pThis->ePktHdrType = kVirtioNetModernPktHdrWithoutMrgRx;
+ else
+ {
+ /* Legacy (e.g. < VirtIO 1.0) device specification's pkt size rules */
+ if (FEATURE_ENABLED(MRG_RXBUF))
+ pThis->ePktHdrType = kVirtioNetLegacyPktHdrWithMrgRx;
+ else /* Legacy guest with MRG_RX feature disabled */
+ {
+ pThis->ePktHdrType = kVirtioNetLegacyPktHdrWithoutMrgRx;
+ pThis->cbPktHdr -= RT_SIZEOFMEMB(VIRTIONETPKTHDR, uNumBuffers);
+ }
+ }
+}
+
+
+/*********************************************************************************************************************************
+* Saved state *
+*********************************************************************************************************************************/
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ *
+ * @note: This is included to accept and migrate VMs that had used the original VirtualBox legacy-only virtio-net (network card)
+ * controller device emulator ("DevVirtioNet.cpp") to work with this superset of VirtIO compatibility known
+ * as a transitional device (see PDM-invoked device constructor comments for more information)
+ */
+static DECLCALLBACK(int) virtioNetR3LegacyDeviceLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass,
+ RTMAC uMacLoaded)
+{
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+ PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ int rc;
+
+ Log7Func(("[%s] LOAD EXEC (LEGACY)!!\n", pThis->szInst));
+
+ if ( memcmp(&uMacLoaded.au8, &pThis->macConfigured.au8, sizeof(uMacLoaded))
+ && ( uPass == 0
+ || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
+ LogRelFunc(("[%s]: The mac address differs: config=%RTmac saved=%RTmac\n",
+ pThis->szInst, &pThis->macConfigured, &uMacLoaded));
+
+ if (uPass == SSM_PASS_FINAL)
+ {
+ /* Call the virtio core to have it load legacy device state */
+ rc = virtioCoreR3LegacyDeviceLoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, uVersion, VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY);
+ AssertRCReturn(rc, rc);
+ /*
+ * Scan constructor-determined virtqs to determine if they are all valid-as-restored.
+ * If so, nudge them with a signal, otherwise destroy the unusable queue(s)
+ * to avoid tripping up the other queue processing logic.
+ */
+ int cVirtqsToRemove = 0;
+ for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
+ {
+ PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
+ if (pVirtq->fHasWorker)
+ {
+ if (!virtioCoreR3VirtqIsEnabled(&pThis->Virtio, uVirtqNbr))
+ {
+ virtioNetR3VirtqDestroy(&pThis->Virtio, pVirtq);
+ ++cVirtqsToRemove;
+ }
+ else
+ {
+ if (virtioCoreR3VirtqIsAttached(&pThis->Virtio, uVirtqNbr))
+ {
+ Log7Func(("[%s] Waking %s worker.\n", pThis->szInst, pVirtq->szName));
+ rc = PDMDevHlpSUPSemEventSignal(pDevIns, pThis->aWorkers[pVirtq->uIdx].hEvtProcess);
+ AssertRCReturn(rc, rc);
+ }
+ }
+ }
+ }
+ AssertMsg(cVirtqsToRemove < 2, ("Multiple unusable queues in saved state unexpected\n"));
+ pThis->cVirtqs -= cVirtqsToRemove;
+
+ pThis->virtioNetConfig.uStatus = pThis->Virtio.fDeviceStatus;
+ pThis->fVirtioReady = pThis->Virtio.fDeviceStatus & VIRTIO_STATUS_DRIVER_OK;
+
+ rc = pHlp->pfnSSMGetMem(pSSM, pThis->virtioNetConfig.uMacAddress.au8, sizeof(pThis->virtioNetConfig.uMacAddress));
+ AssertRCReturn(rc, rc);
+
+ if (uVersion > VIRTIONET_SAVEDSTATE_VERSION_3_1_BETA1_LEGACY)
+ {
+ /* Zero-out the the Unicast/Multicast filter table */
+ memset(&pThis->aMacUnicastFilter[0], 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
+
+ rc = pHlp->pfnSSMGetU8( pSSM, &pThis->fPromiscuous);
+ AssertRCReturn(rc, rc);
+ rc = pHlp->pfnSSMGetU8( pSSM, &pThis->fAllMulticast);
+ AssertRCReturn(rc, rc);
+ /*
+ * The 0.95 legacy virtio spec defines a control queue command VIRTIO_NET_CTRL_MAC_TABLE_SET,
+ * wherein guest driver configures two variable length mac filter tables: A unicast filter,
+ * and a multicast filter. However original VBox virtio-net saved both sets of filter entries
+ * in a single table, abandoning the distinction between unicast and multicast filters. It preserved
+ * only *one* filter's table length, leaving no way to separate table back out into respective unicast
+ * and multicast tables this device implementation preserves. Deduced from legacy code, the original
+ * assumption was that the both MAC filters are whitelists that can be processed identically
+ * (from the standpoint of a *single* host receiver), such that the distinction between unicast and
+ * multicast doesn't matter in any one VM's context. Little choice here but to save the undifferentiated
+ * unicast & multicast MACs to the unicast filter table and leave multicast table empty/unused.
+ */
+ uint32_t cCombinedUnicastMulticastEntries;
+ rc = pHlp->pfnSSMGetU32(pSSM, &cCombinedUnicastMulticastEntries);
+ AssertRCReturn(rc, rc);
+ AssertReturn(cCombinedUnicastMulticastEntries <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
+ pThis->cUnicastFilterMacs = cCombinedUnicastMulticastEntries;
+ rc = pHlp->pfnSSMGetMem(pSSM, pThis->aMacUnicastFilter, cCombinedUnicastMulticastEntries * sizeof(RTMAC));
+ AssertRCReturn(rc, rc);
+ rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
+ AssertRCReturn(rc, rc);
+ }
+ else
+ {
+ pThis->fAllMulticast = false;
+ pThis->cUnicastFilterMacs = 0;
+ memset(&pThis->aMacUnicastFilter, 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
+
+ memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
+
+ pThis->fPromiscuous = true;
+ if (pThisCC->pDrv)
+ pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
+ }
+
+ /*
+ * Log the restored VirtIO feature selection.
+ */
+ pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
+ /** @todo shouldn't we update the virtio header size here? it depends on the negotiated features. */
+ virtioCorePrintDeviceFeatures(&pThis->Virtio, NULL, s_aDevSpecificFeatures, RT_ELEMENTS(s_aDevSpecificFeatures));
+
+ /*
+ * Configure remaining transitional device parameters presumably or deductively
+ * as these weren't part of the legacy device code thus it didn't save them to SSM
+ */
+ pThis->fCableConnected = 1;
+ pThis->fAllUnicast = 0;
+ pThis->fNoMulticast = 0;
+ pThis->fNoUnicast = 0;
+ pThis->fNoBroadcast = 0;
+
+ /* Zero out the multicast table and count, all MAC filters, if any, are in the unicast filter table */
+ pThis->cMulticastFilterMacs = 0;
+ memset(&pThis->aMacMulticastFilter, 0, VIRTIONET_MAC_FILTER_LEN * sizeof(RTMAC));
+ }
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADEXEC}
+ *
+ * @note: This loads state saved by a Modern (VirtIO 1.0+) device, of which this transitional device is one,
+ * and thus supports both legacy and modern guest virtio drivers.
+ */
+static DECLCALLBACK(int) virtioNetR3ModernLoadExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM, uint32_t uVersion, uint32_t uPass)
+{
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+ PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+ int rc;
+
+ RT_NOREF(pThisCC);
+
+ RTMAC uMacLoaded, uVersionMarkerMac = { VIRTIONET_VERSION_MARKER_MAC_ADDR };
+ rc = pHlp->pfnSSMGetMem(pSSM, &uMacLoaded.au8, sizeof(uMacLoaded.au8));
+ AssertRCReturn(rc, rc);
+ if (memcmp(&uMacLoaded.au8, uVersionMarkerMac.au8, sizeof(uVersionMarkerMac.au8)))
+ {
+ rc = virtioNetR3LegacyDeviceLoadExec(pDevIns, pSSM, uVersion, uPass, uMacLoaded);
+ return rc;
+ }
+
+ Log7Func(("[%s] LOAD EXEC!!\n", pThis->szInst));
+
+ AssertReturn(uPass == SSM_PASS_FINAL, VERR_SSM_UNEXPECTED_PASS);
+ AssertLogRelMsgReturn(uVersion == VIRTIONET_SAVEDSTATE_VERSION,
+ ("uVersion=%u\n", uVersion), VERR_SSM_UNSUPPORTED_DATA_UNIT_VERSION);
+
+ virtioNetR3SetVirtqNames(pThis, false /* fLegacy */);
+
+ pHlp->pfnSSMGetU64( pSSM, &pThis->fNegotiatedFeatures);
+
+ pHlp->pfnSSMGetU16( pSSM, &pThis->cVirtqs);
+ AssertReturn(pThis->cVirtqs <= (VIRTIONET_MAX_QPAIRS * 2) + 1, VERR_OUT_OF_RANGE);
+ pHlp->pfnSSMGetU16( pSSM, &pThis->cWorkers);
+ AssertReturn(pThis->cWorkers <= VIRTIONET_MAX_WORKERS , VERR_OUT_OF_RANGE);
+
+ for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
+ pHlp->pfnSSMGetBool(pSSM, &pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore);
+
+ /* Config checks */
+ RTMAC macConfigured;
+ rc = pHlp->pfnSSMGetMem(pSSM, &macConfigured.au8, sizeof(macConfigured.au8));
+ AssertRCReturn(rc, rc);
+ if (memcmp(&macConfigured.au8, &pThis->macConfigured.au8, sizeof(macConfigured.au8))
+ && (uPass == 0 || !PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns)))
+ LogRel(("%s: The mac address differs: config=%RTmac saved=%RTmac\n",
+ pThis->szInst, &pThis->macConfigured, &macConfigured));
+ memcpy(pThis->virtioNetConfig.uMacAddress.au8, macConfigured.au8, sizeof(macConfigured.au8));
+
+#if FEATURE_OFFERED(STATUS)
+ uint16_t fChkStatus;
+ pHlp->pfnSSMGetU16( pSSM, &fChkStatus);
+ if (fChkStatus == 0xffff)
+ {
+ /* Dummy value in saved state because status feature wasn't enabled at the time */
+ pThis->virtioNetConfig.uStatus = 0; /* VIRTIO_NET_S_ANNOUNCE disabled */
+ pThis->virtioNetConfig.uStatus = !!IS_LINK_UP(pThis); /* VIRTIO_NET_IS_LINK_UP (bit 0) */
+ }
+ else
+ pThis->virtioNetConfig.uStatus = fChkStatus;
+#else
+ uint16_t fDiscard;
+ pHlp->pfnSSMGetU16( pSSM, &fDiscard);
+#endif
+
+#if FEATURE_OFFERED(MQ)
+ uint16_t uCheckMaxVirtqPairs;
+ pHlp->pfnSSMGetU16( pSSM, &uCheckMaxVirtqPairs);
+ if (uCheckMaxVirtqPairs)
+ pThis->virtioNetConfig.uMaxVirtqPairs = uCheckMaxVirtqPairs;
+ else
+ pThis->virtioNetConfig.uMaxVirtqPairs = VIRTIONET_CTRL_MQ_VQ_PAIRS;
+#else
+ uint16_t fDiscard;
+ pHlp->pfnSSMGetU16( pSSM, &fDiscard);
+#endif
+
+ /* Save device-specific part */
+ pHlp->pfnSSMGetBool( pSSM, &pThis->fCableConnected);
+ pHlp->pfnSSMGetU8( pSSM, &pThis->fPromiscuous);
+ pHlp->pfnSSMGetU8( pSSM, &pThis->fAllMulticast);
+ pHlp->pfnSSMGetU8( pSSM, &pThis->fAllUnicast);
+ pHlp->pfnSSMGetU8( pSSM, &pThis->fNoMulticast);
+ pHlp->pfnSSMGetU8( pSSM, &pThis->fNoUnicast);
+ pHlp->pfnSSMGetU8( pSSM, &pThis->fNoBroadcast);
+
+ pHlp->pfnSSMGetU32( pSSM, &pThis->cMulticastFilterMacs);
+ AssertReturn(pThis->cMulticastFilterMacs <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
+ pHlp->pfnSSMGetMem( pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
+
+ if (pThis->cMulticastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
+ memset(&pThis->aMacMulticastFilter[pThis->cMulticastFilterMacs], 0,
+ (VIRTIONET_MAC_FILTER_LEN - pThis->cMulticastFilterMacs) * sizeof(RTMAC));
+
+ pHlp->pfnSSMGetU32( pSSM, &pThis->cUnicastFilterMacs);
+ AssertReturn(pThis->cUnicastFilterMacs <= VIRTIONET_MAC_FILTER_LEN, VERR_OUT_OF_RANGE);
+ pHlp->pfnSSMGetMem( pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
+
+ if (pThis->cUnicastFilterMacs < VIRTIONET_MAC_FILTER_LEN)
+ memset(&pThis->aMacUnicastFilter[pThis->cUnicastFilterMacs], 0,
+ (VIRTIONET_MAC_FILTER_LEN - pThis->cUnicastFilterMacs) * sizeof(RTMAC));
+
+ rc = pHlp->pfnSSMGetMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
+ AssertRCReturn(rc, rc);
+ /*
+ * Call the virtio core to let it load its state.
+ */
+ rc = virtioCoreR3ModernDeviceLoadExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, uVersion,
+ VIRTIONET_SAVEDSTATE_VERSION, pThis->cVirtqs);
+ AssertRCReturn(rc, rc);
+ /*
+ * Since the control queue is created proactively in the constructor to accomodate worst-case
+ * legacy guests, even though the queue may have been deducted from queue count while saving state,
+ * we must explicitly remove queue and associated worker thread and context at this point,
+ * or presence of bogus control queue will confuse operations.
+ */
+ PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[CTRLQIDX];
+ if (FEATURE_DISABLED(CTRL_VQ) || !virtioCoreIsVirtqEnabled(&pThis->Virtio, CTRLQIDX))
+ {
+ virtioCoreR3VirtqDetach(&pThis->Virtio, CTRLQIDX);
+ virtioNetR3VirtqDestroy(&pThis->Virtio, pVirtq);
+ pVirtq->fAttachedToVirtioCore = false;
+ --pThis->cWorkers;
+ }
+ /*
+ * Nudge queue workers
+ */
+ for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
+ {
+ pVirtq = &pThis->aVirtqs[uVirtqNbr];
+ if (pVirtq->fAttachedToVirtioCore)
+ {
+ if (pVirtq->fHasWorker)
+ {
+ PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
+ Log7Func(("[%s] Waking %s worker.\n", pThis->szInst, pVirtq->szName));
+ rc = PDMDevHlpSUPSemEventSignal(pDevIns, pWorker->hEvtProcess);
+ AssertRCReturn(rc, rc);
+ }
+ }
+ }
+ pThis->virtioNetConfig.uStatus = pThis->Virtio.fDeviceStatus; /* reflects state to guest driver */
+ pThis->fVirtioReady = pThis->Virtio.fDeviceStatus & VIRTIO_STATUS_DRIVER_OK;
+ virtioNetConfigurePktHdr(pThis, pThis->Virtio.fLegacyDriver);
+ return rc;
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVLOADDONE, Link status adjustments after
+ * loading.}
+ */
+static DECLCALLBACK(int) virtioNetR3ModernLoadDone(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+ PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+ RT_NOREF(pSSM);
+
+ if (pThisCC->pDrv)
+ pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, (pThis->fPromiscuous | pThis->fAllMulticast));
+
+ /*
+ * Indicate link down to the guest OS that all network connections have
+ * been lost, unless we've been teleported here.
+ */
+ if (!PDMDevHlpVMTeleportedAndNotFullyResumedYet(pDevIns))
+ virtioNetR3TempLinkDown(pDevIns, pThis, pThisCC);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{FNSSMDEVSAVEEXEC}
+ */
+static DECLCALLBACK(int) virtioNetR3ModernSaveExec(PPDMDEVINS pDevIns, PSSMHANDLE pSSM)
+{
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+ PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ RT_NOREF(pThisCC);
+ Log7Func(("[%s] SAVE EXEC!!\n", pThis->szInst));
+
+ /* Store a dummy MAC address that would never be actually assigned to a NIC
+ * so that when load exec handler is called it can be easily determined
+ * whether saved state is modern or legacy. This works because original
+ * legacy code stored assigned NIC address as the first item of SSM state
+ */
+ RTMAC uVersionMarkerMac = { VIRTIONET_VERSION_MARKER_MAC_ADDR };
+ pHlp->pfnSSMPutMem(pSSM, &uVersionMarkerMac.au8, sizeof(uVersionMarkerMac.au8));
+
+ pHlp->pfnSSMPutU64( pSSM, pThis->fNegotiatedFeatures);
+
+ pHlp->pfnSSMPutU16( pSSM, pThis->cVirtqs);
+ pHlp->pfnSSMPutU16( pSSM, pThis->cWorkers);
+
+ for (int uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
+ pHlp->pfnSSMPutBool(pSSM, pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore);
+ /*
+
+ * Save device config area (accessed via MMIO)
+ */
+ pHlp->pfnSSMPutMem( pSSM, pThis->virtioNetConfig.uMacAddress.au8,
+ sizeof(pThis->virtioNetConfig.uMacAddress.au8));
+#if FEATURE_OFFERED(STATUS)
+ pHlp->pfnSSMPutU16( pSSM, pThis->virtioNetConfig.uStatus);
+#else
+ /*
+ * Relevant values are lower bits. Forcing this to 0xffff let's loadExec know this
+ * feature was not enabled in saved state. VirtIO 1.0, 5.1.4
+ */
+ pHlp->pfnSSMPutU16( pSSM, 0xffff);
+
+#endif
+#if FEATURE_OFFERED(MQ)
+ pHlp->pfnSSMPutU16( pSSM, pThis->virtioNetConfig.uMaxVirtqPairs);
+#else
+ /*
+ * Legal values for max_virtqueue_pairs are 0x1 -> 0x8000 *. Forcing zero let's loadExec know this
+ * feature was not enabled in saved state. VirtIO 1.0, 5.1.4.1
+ */
+ pHlp->pfnSSMPutU16( pSSM, 0);
+#endif
+
+ /* Save device-specific part */
+ pHlp->pfnSSMPutBool( pSSM, pThis->fCableConnected);
+ pHlp->pfnSSMPutU8( pSSM, pThis->fPromiscuous);
+ pHlp->pfnSSMPutU8( pSSM, pThis->fAllMulticast);
+ pHlp->pfnSSMPutU8( pSSM, pThis->fAllUnicast);
+ pHlp->pfnSSMPutU8( pSSM, pThis->fNoMulticast);
+ pHlp->pfnSSMPutU8( pSSM, pThis->fNoUnicast);
+ pHlp->pfnSSMPutU8( pSSM, pThis->fNoBroadcast);
+
+ pHlp->pfnSSMPutU32( pSSM, pThis->cMulticastFilterMacs);
+ pHlp->pfnSSMPutMem( pSSM, pThis->aMacMulticastFilter, pThis->cMulticastFilterMacs * sizeof(RTMAC));
+
+ pHlp->pfnSSMPutU32( pSSM, pThis->cUnicastFilterMacs);
+ pHlp->pfnSSMPutMem( pSSM, pThis->aMacUnicastFilter, pThis->cUnicastFilterMacs * sizeof(RTMAC));
+
+ int rc = pHlp->pfnSSMPutMem(pSSM, pThis->aVlanFilter, sizeof(pThis->aVlanFilter));
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Call the virtio core to let it save its state.
+ */
+ return virtioCoreR3SaveExec(&pThis->Virtio, pDevIns->pHlpR3, pSSM, VIRTIONET_SAVEDSTATE_VERSION, pThis->cVirtqs);
+}
+
+
+/*********************************************************************************************************************************
+* Device interface. *
+*********************************************************************************************************************************/
+
+#ifdef IN_RING3
+
+/**
+ * Perform 16-bit 1's compliment checksum on provided packet in accordance with VirtIO specification,
+ * pertinent to VIRTIO_NET_F_CSUM feature, which 'offloads' the Checksum feature from the driver
+ * to save processor cycles, which is ironic in our case, where the controller device ('network card')
+ * is emulated on the virtualization host.
+ *
+ * @note See VirtIO 1.0 spec, 5.1.6.2 Packet Transmission
+ *
+ * @param pBuf Pointer to r/w buffer with any portion to calculate checksum for
+ * @param cbSize Number of bytes to checksum
+ * @param uStart Where to start the checksum within the buffer
+ * @param uOffset Offset past uStart point in the buffer to store checksum result
+ *
+ */
+DECLINLINE(void) virtioNetR3Calc16BitChecksum(uint8_t *pBuf, size_t cb, uint16_t uStart, uint16_t uOffset)
+{
+ AssertReturnVoid(uStart < cb);
+ AssertReturnVoid(uStart + uOffset + sizeof(uint16_t) <= cb);
+
+ uint32_t chksum = 0;
+ uint16_t *pu = (uint16_t *)(pBuf + uStart);
+
+ cb -= uStart;
+ while (cb > 1)
+ {
+ chksum += *pu++;
+ cb -= 2;
+ }
+ if (cb)
+ chksum += *(uint8_t *)pu;
+ while (chksum >> 16)
+ chksum = (chksum >> 16) + (chksum & 0xFFFF);
+
+ /* Store 1's compliment of calculated sum */
+ *(uint16_t *)(pBuf + uStart + uOffset) = ~chksum;
+}
+
+/**
+ * Turns on/off the read status LED.
+ *
+ * @returns VBox status code.
+ * @param pThis Pointer to the device state structure.
+ * @param fOn New LED state.
+ */
+void virtioNetR3SetReadLed(PVIRTIONETR3 pThisR3, bool fOn)
+{
+ if (fOn)
+ pThisR3->led.Asserted.s.fReading = pThisR3->led.Actual.s.fReading = 1;
+ else
+ pThisR3->led.Actual.s.fReading = fOn;
+}
+
+/**
+ * Turns on/off the write status LED.
+ *
+ * @returns VBox status code.
+ * @param pThis Pointer to the device state structure.
+ * @param fOn New LED state.
+ */
+void virtioNetR3SetWriteLed(PVIRTIONETR3 pThisR3, bool fOn)
+{
+ if (fOn)
+ pThisR3->led.Asserted.s.fWriting = pThisR3->led.Actual.s.fWriting = 1;
+ else
+ pThisR3->led.Actual.s.fWriting = fOn;
+}
+
+/**
+ * Check that the core is setup and ready and co-configured with guest virtio driver,
+ * and verifies that the VM is running.
+ *
+ * @returns true if VirtIO core and device are in a running and operational state
+ */
+DECLINLINE(bool) virtioNetIsOperational(PVIRTIONET pThis, PPDMDEVINS pDevIns)
+{
+ if (RT_LIKELY(pThis->fVirtioReady))
+ {
+ VMSTATE enmVMState = PDMDevHlpVMState(pDevIns);
+ if (RT_LIKELY(enmVMState == VMSTATE_RUNNING || enmVMState == VMSTATE_RUNNING_LS))
+ return true;
+ }
+ return false;
+}
+
+/**
+ * Check whether specific queue is ready and has Rx buffers (virtqueue descriptors)
+ * available. This must be called before the pfnRecieve() method is called.
+ *
+ * @remarks As a side effect this function enables queue notification
+ * if it cannot receive because the queue is empty.
+ * It disables notification if it can receive.
+ *
+ * @returns VERR_NET_NO_BUFFER_SPACE if it cannot.
+ * @thread RX
+ */
+static int virtioNetR3CheckRxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETVIRTQ pRxVirtq)
+{
+ int rc = VERR_INVALID_STATE;
+ Log8Func(("[%s] ", pThis->szInst));
+ if (!virtioNetIsOperational(pThis, pDevIns))
+ Log8(("No Rx bufs available. (VirtIO core not ready)\n"));
+
+ else if (!virtioCoreIsVirtqEnabled(&pThis->Virtio, pRxVirtq->uIdx))
+ Log8(("[No Rx bufs available. (%s not enabled)\n", pRxVirtq->szName));
+
+ else if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pRxVirtq->uIdx))
+ Log8(("No Rx bufs available. (%s empty)\n", pRxVirtq->szName));
+
+ else
+ {
+ Log8(("%s has %d empty guest bufs in avail ring\n", pRxVirtq->szName,
+ virtioCoreVirtqAvailBufCount(pDevIns, &pThis->Virtio, pRxVirtq->uIdx)));
+ rc = VINF_SUCCESS;
+ }
+ virtioCoreVirtqEnableNotify(&pThis->Virtio, pRxVirtq->uIdx, rc == VERR_INVALID_STATE /* fEnable */);
+ return rc;
+}
+
+/**
+ * Find an Rx queue that has Rx packets in it, if *any* do.
+ *
+ * @todo When multiqueue (MQ) mode is fully supported and tested, some kind of round-robin
+ * or randomization scheme should probably be incorporated here.
+ *
+ * @returns true if Rx pkts avail on queue and sets pRxVirtq to point to queue w/pkts found
+ * @thread RX
+ *
+ */
+static bool virtioNetR3AreRxBufsAvail(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETVIRTQ *pRxVirtq)
+{
+ for (int uVirtqPair = 0; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
+ {
+ PVIRTIONETVIRTQ pThisRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
+ if (RT_SUCCESS(virtioNetR3CheckRxBufsAvail(pDevIns, pThis, pThisRxVirtq)))
+ {
+ if (pRxVirtq)
+ *pRxVirtq = pThisRxVirtq;
+ return true;
+ }
+ }
+ return false;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
+ */
+static DECLCALLBACK(int) virtioNetR3NetworkDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL timeoutMs)
+{
+ PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+
+ if (!virtioNetIsOperational(pThis, pDevIns))
+ return VERR_INTERRUPTED;
+
+ if (virtioNetR3AreRxBufsAvail(pDevIns, pThis, NULL /* pRxVirtq */))
+ {
+ Log10Func(("[%s] Rx bufs available, releasing waiter...\n", pThis->szInst));
+ return VINF_SUCCESS;
+ }
+ if (!timeoutMs)
+ return VERR_NET_NO_BUFFER_SPACE;
+
+ LogFunc(("[%s] %s\n", pThis->szInst, timeoutMs == RT_INDEFINITE_WAIT ? "<indefinite wait>" : ""));
+
+ ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, true);
+ STAM_PROFILE_START(&pThis->StatRxOverflow, a);
+
+ do {
+ if (virtioNetR3AreRxBufsAvail(pDevIns, pThis, NULL /* pRxVirtq */))
+ {
+ Log10Func(("[%s] Rx bufs now available, releasing waiter...\n", pThis->szInst));
+ ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, false);
+ return VINF_SUCCESS;
+ }
+ Log9Func(("[%s] Starved for empty guest Rx bufs. Waiting...\n", pThis->szInst));
+
+ int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pThis->hEventRxDescAvail, timeoutMs);
+
+ if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
+ {
+ LogFunc(("Woken due to %s\n", rc == VERR_TIMEOUT ? "timeout" : "getting interrupted"));
+
+ if (!virtioNetIsOperational(pThis, pDevIns))
+ break;
+
+ continue;
+ }
+ if (RT_FAILURE(rc)) {
+ LogFunc(("Waken due to failure %Rrc\n", rc));
+ RTThreadSleep(1);
+ }
+ } while (virtioNetIsOperational(pThis, pDevIns));
+
+ STAM_PROFILE_STOP(&pThis->StatRxOverflow, a);
+ ASMAtomicXchgBool(&pThis->fLeafWantsEmptyRxBufs, false);
+
+ Log7Func(("[%s] Wait for Rx buffers available was interrupted\n", pThis->szInst));
+ return VERR_INTERRUPTED;
+}
+
+/**
+ * Sets up the GSO context according to the Virtio header.
+ *
+ * @param pGso The GSO context to setup.
+ * @param pCtx The context descriptor.
+ */
+DECLINLINE(PPDMNETWORKGSO) virtioNetR3SetupGsoCtx(PPDMNETWORKGSO pGso, VIRTIONETPKTHDR const *pPktHdr)
+{
+ pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
+
+ if (pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)
+ {
+ AssertMsgFailed(("Unsupported flag in virtio header: ECN\n"));
+ return NULL;
+ }
+ switch (pPktHdr->uGsoType & ~VIRTIONET_HDR_GSO_ECN)
+ {
+ case VIRTIONET_HDR_GSO_TCPV4:
+ pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_TCP;
+ pGso->cbHdrsSeg = pPktHdr->uHdrLen;
+ break;
+ case VIRTIONET_HDR_GSO_TCPV6:
+ pGso->u8Type = PDMNETWORKGSOTYPE_IPV6_TCP;
+ pGso->cbHdrsSeg = pPktHdr->uHdrLen;
+ break;
+ case VIRTIONET_HDR_GSO_UDP:
+ pGso->u8Type = PDMNETWORKGSOTYPE_IPV4_UDP;
+ pGso->cbHdrsSeg = pPktHdr->uChksumStart;
+ break;
+ default:
+ return NULL;
+ }
+ if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
+ pGso->offHdr2 = pPktHdr->uChksumStart;
+ else
+ {
+ AssertMsgFailed(("GSO without checksum offloading!\n"));
+ return NULL;
+ }
+ pGso->offHdr1 = sizeof(RTNETETHERHDR);
+ pGso->cbHdrsTotal = pPktHdr->uHdrLen;
+ pGso->cbMaxSeg = pPktHdr->uGsoSize;
+ /* Mark GSO frames with zero MSS as PDMNETWORKGSOTYPE_INVALID, so they will be ignored by send. */
+ if (pPktHdr->uGsoType != VIRTIONET_HDR_GSO_NONE && pPktHdr->uGsoSize == 0)
+ pGso->u8Type = PDMNETWORKGSOTYPE_INVALID;
+ return pGso;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnGetMac}
+ */
+static DECLCALLBACK(int) virtioNetR3NetworkConfig_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
+{
+ PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
+ memcpy(pMac, pThis->virtioNetConfig.uMacAddress.au8, sizeof(RTMAC));
+ return VINF_SUCCESS;
+}
+
+/**
+ * Returns true if it is a broadcast packet.
+ *
+ * @returns true if destination address indicates broadcast.
+ * @param pvBuf The ethernet packet.
+ */
+DECLINLINE(bool) virtioNetR3IsBroadcast(const void *pvBuf)
+{
+ static const uint8_t s_abBcastAddr[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF };
+ return memcmp(pvBuf, s_abBcastAddr, sizeof(s_abBcastAddr)) == 0;
+}
+
+/**
+ * Returns true if it is a multicast packet.
+ *
+ * @remarks returns true for broadcast packets as well.
+ * @returns true if destination address indicates multicast.
+ * @param pvBuf The ethernet packet.
+ */
+DECLINLINE(bool) virtioNetR3IsMulticast(const void *pvBuf)
+{
+ return (*(char*)pvBuf) & 1;
+}
+
+/**
+ * Determines if the packet is to be delivered to upper layer.
+ *
+ * @returns true if packet is intended for this node.
+ * @param pThis Pointer to the state structure.
+ * @param pvBuf The ethernet packet.
+ * @param cb Number of bytes available in the packet.
+ */
+static bool virtioNetR3AddressFilter(PVIRTIONET pThis, const void *pvBuf, size_t cb)
+{
+
+RT_NOREF(cb);
+
+#ifdef LOG_ENABLED
+ if (LogIs11Enabled())
+ {
+ char *pszType;
+ if (virtioNetR3IsMulticast(pvBuf))
+ pszType = (char *)"mcast";
+ else if (virtioNetR3IsBroadcast(pvBuf))
+ pszType = (char *)"bcast";
+ else
+ pszType = (char *)"ucast";
+
+ LogFunc(("node(%RTmac%s%s), pkt(%RTmac, %s) ",
+ pThis->virtioNetConfig.uMacAddress.au8,
+ pThis->fPromiscuous ? " promisc" : "",
+ pThis->fAllMulticast ? " all-mcast" : "",
+ pvBuf, pszType));
+ }
+#endif
+
+ if (pThis->fPromiscuous) {
+ Log11(("\n"));
+ return true;
+ }
+
+ /* Ignore everything outside of our VLANs */
+ uint16_t *uPtr = (uint16_t *)pvBuf;
+
+ /* Compare TPID with VLAN Ether Type */
+ if ( uPtr[6] == RT_H2BE_U16(0x8100)
+ && !ASMBitTest(pThis->aVlanFilter, RT_BE2H_U16(uPtr[7]) & 0xFFF))
+ {
+ Log11Func(("\n[%s] not our VLAN, returning false\n", pThis->szInst));
+ return false;
+ }
+
+ if (virtioNetR3IsBroadcast(pvBuf))
+ {
+ Log11(("acpt (bcast)\n"));
+#ifdef LOG_ENABLED
+ if (LogIs12Enabled())
+ virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
+#endif
+ return true;
+ }
+ if (pThis->fAllMulticast && virtioNetR3IsMulticast(pvBuf))
+ {
+ Log11(("acpt (all-mcast)\n"));
+#ifdef LOG_ENABLED
+ if (LogIs12Enabled())
+ virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
+#endif
+ return true;
+ }
+
+ if (!memcmp(pThis->virtioNetConfig.uMacAddress.au8, pvBuf, sizeof(RTMAC)))
+ {
+ Log11(("acpt (to-node)\n"));
+#ifdef LOG_ENABLED
+ if (LogIs12Enabled())
+ virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
+#endif
+ return true;
+ }
+
+ for (uint16_t i = 0; i < pThis->cMulticastFilterMacs; i++)
+ {
+ if (!memcmp(&pThis->aMacMulticastFilter[i], pvBuf, sizeof(RTMAC)))
+ {
+ Log11(("acpt (mcast whitelist)\n"));
+#ifdef LOG_ENABLED
+ if (LogIs12Enabled())
+ virtioNetR3PacketDump(pThis, (const uint8_t *)pvBuf, cb, "<-- Incoming");
+#endif
+ return true;
+ }
+ }
+
+ for (uint16_t i = 0; i < pThis->cUnicastFilterMacs; i++)
+ if (!memcmp(&pThis->aMacUnicastFilter[i], pvBuf, sizeof(RTMAC)))
+ {
+ Log11(("acpt (ucast whitelist)\n"));
+ return true;
+ }
+#ifdef LOG_ENABLED
+ if (LogIs11Enabled())
+ Log(("... reject\n"));
+#endif
+
+ return false;
+}
+
+
+/**
+ * This handles the case where Rx packet must be transfered to guest driver via multiple buffers using
+ * copy tactics slower than preferred method using a single virtq buf. Yet this is an available option
+ * for guests. Although cited in the spec it's to accomodate guest that perhaps have memory constraints
+ * wherein guest may benefit from smaller buffers (see MRG_RXBUF feature), in practice it is seen
+ * that without MRG_RXBUF the linux guest enqueues 'huge' multi-segment buffers so that the largest
+ * conceivable Rx packet can be contained in a single buffer, where for most transactions most of that
+ * memory will be unfilled, so it is typically both wasteful and *slower* to avoid MRG_RXBUF.
+ *
+ * As an optimization, this multi-buffer copy is only used when:
+ *
+ * A. Guest has negotiated MRG_RXBUF
+ * B. Next packet in the Rx avail queue isn't big enough to contain Rx pkt hdr+data.
+ *
+ * Architecture is defined in VirtIO 1.1 5.1.6 (Device Operations), which has improved
+ * wording over the VirtIO 1.0 specification, but, as an implementation note, there is one
+ * ambiguity that needs clarification:
+ *
+ * The VirtIO 1.1, 5.1.6.4 explains something in a potentially misleading way. And note,
+ * the VirtIO spec makes a document-wide assertion that the distinction between
+ * "SHOULD" and "MUST" is to be taken quite literally.
+ *
+ * The confusion is that VirtIO 1.1, 5.1.6.3.1 essentially says guest driver "SHOULD" populate
+ * Rx queue with buffers large enough to accomodate full pkt hdr + data. That's a grammatical
+ * error (dangling participle).
+ *
+ * In practice we MUST assume "SHOULD" strictly applies to the word *populate*, -not- to buffer
+ * size, because ultimately buffer minimum size is predicated on configuration parameters,
+ * specifically, when MRG_RXBUF feature is disabled, the driver *MUST* provide Rx bufs
+ * (if and when it can provide them), that are *large enough* to hold pkt hdr + payload.
+ *
+ * Therefore, proper interpretation of 5.1.6.3.1 is, the guest *should* (ideally) keep Rx virtq
+ * populated with appropriately sized buffers to *prevent starvation* (i.e. starvation may be
+ * unavoidable thus can't be prohibited). As it would be a ludicrous to presume 5.1.6.3.1 is
+ * giving guests leeway to violate MRG_RXBUF feature buf size constraints.
+ *
+ * @param pDevIns PDM instance
+ * @param pThis Device instance
+ * @param pvBuf Pointer to incoming GSO Rx data from downstream device
+ * @param cb Amount of data given
+ * @param rxPktHdr Rx pkt Header that's been massaged into VirtIO semantics
+ * @param pRxVirtq Pointer to Rx virtq
+ * @param pVirtqBuf Initial virtq buffer to start copying Rx hdr/pkt to guest into
+ *
+ */
+static int virtioNetR3RxPktMultibufXfer(PPDMDEVINS pDevIns, PVIRTIONET pThis, uint8_t *pvPktBuf, size_t cb,
+ PVIRTIONETPKTHDR pRxPktHdr, PVIRTIONETVIRTQ pRxVirtq, PVIRTQBUF pVirtqBuf)
+{
+
+ size_t cbBufRemaining = pVirtqBuf->cbPhysReturn;
+ size_t cbPktHdr = pThis->cbPktHdr;
+
+ AssertMsgReturn(cbBufRemaining >= pThis->cbPktHdr,
+ ("guest-provided Rx buf not large enough to store pkt hdr"), VERR_INTERNAL_ERROR);
+
+ Log7Func((" Sending packet header to guest...\n"));
+
+ /* Copy packet header to rx buf provided by caller. */
+ size_t cbHdrEnqueued = pVirtqBuf->cbPhysReturn == cbPktHdr ? cbPktHdr : 0;
+ virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbPktHdr, pRxPktHdr, pVirtqBuf, cbHdrEnqueued);
+
+ /* Cache address of uNumBuffers field of pkthdr to update ex post facto */
+ RTGCPHYS GCPhysNumBuffers = pVirtqBuf->pSgPhysReturn->paSegs[0].GCPhys + RT_UOFFSETOF(VIRTIONETPKTHDR, uNumBuffers);
+ uint16_t cVirtqBufsUsed = 0;
+ cbBufRemaining -= cbPktHdr;
+ /*
+ * Copy packet to guest using as many buffers as necessary, tracking and handling whether
+ * the buf containing the packet header was already written to the Rx queue's used buffer ring.
+ */
+ uint64_t uPktOffset = 0;
+ while(uPktOffset < cb)
+ {
+ Log7Func((" Sending packet data (in buffer #%d) to guest...\n", cVirtqBufsUsed));
+ size_t cbBounded = RT_MIN(cbBufRemaining, cb - uPktOffset);
+ (void) virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbBounded,
+ pvPktBuf + uPktOffset, pVirtqBuf, cbBounded + (cbPktHdr - cbHdrEnqueued) /* cbEnqueue */);
+ ++cVirtqBufsUsed;
+ cbBufRemaining -= cbBounded;
+ uPktOffset += cbBounded;
+ if (uPktOffset < cb)
+ {
+ cbHdrEnqueued = cbPktHdr;
+#ifdef VIRTIO_VBUF_ON_STACK
+ int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, pVirtqBuf, true);
+#else /* !VIRTIO_VBUF_ON_STACK */
+ virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
+ int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, &pVirtqBuf, true);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+
+ AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
+
+#ifdef VIRTIO_VBUF_ON_STACK
+ AssertMsgReturn(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
+ ("Not enough Rx buffers in queue to accomodate ethernet packet\n"),
+ VERR_INTERNAL_ERROR);
+#else /* !VIRTIO_VBUF_ON_STACK */
+ AssertMsgReturnStmt(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
+ ("Not enough Rx buffers in queue to accomodate ethernet packet\n"),
+ virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf),
+ VERR_INTERNAL_ERROR);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+ cbBufRemaining = pVirtqBuf->cbPhysReturn;
+ }
+ }
+
+ /* Fix-up pkthdr (in guest phys. memory) with number of buffers (descriptors) that were processed */
+ int rc = virtioCoreGCPhysWrite(&pThis->Virtio, pDevIns, GCPhysNumBuffers, &cVirtqBufsUsed, sizeof(cVirtqBufsUsed));
+ AssertMsgRCReturn(rc, ("Failure updating descriptor count in pkt hdr in guest physical memory\n"), rc);
+
+#ifndef VIRTIO_VBUF_ON_STACK
+ virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+ virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, pRxVirtq->uIdx);
+ Log7(("\n"));
+ return rc;
+}
+
+/**
+ * Pad and store received packet.
+ *
+ * @remarks Make sure that the packet appears to upper layer as one coming
+ * from real Ethernet: pad it and insert FCS.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The device instance.
+ * @param pThis The virtio-net shared instance data.
+ * @param pvBuf The available data.
+ * @param cb Number of bytes available in the buffer.
+ * @param pGso Pointer to Global Segmentation Offload structure
+ * @param pRxVirtq Pointer to Rx virtqueue
+ * @thread RX
+ */
+
+static int virtioNetR3CopyRxPktToGuest(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC, const void *pvBuf, size_t cb,
+ PVIRTIONETPKTHDR pRxPktHdr, uint8_t cbPktHdr, PVIRTIONETVIRTQ pRxVirtq)
+{
+ RT_NOREF(pThisCC);
+#ifdef VIRTIO_VBUF_ON_STACK
+ VIRTQBUF_T VirtqBuf;
+
+ VirtqBuf.u32Magic = VIRTQBUF_MAGIC;
+ VirtqBuf.cRefs = 1;
+
+ PVIRTQBUF pVirtqBuf = &VirtqBuf;
+ int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, pVirtqBuf, true);
+#else /* !VIRTIO_VBUF_ON_STACK */
+ PVIRTQBUF pVirtqBuf;
+ int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, &pVirtqBuf, true);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+
+ AssertMsgReturn(rc == VINF_SUCCESS || rc == VERR_NOT_AVAILABLE, ("%Rrc\n", rc), rc);
+
+#ifdef VIRTIO_VBUF_ON_STACK
+ AssertMsgReturn(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
+ ("Not enough Rx buffers or capacity to accommodate ethernet packet\n"),
+ VERR_INTERNAL_ERROR);
+#else /* !VIRTIO_VBUF_ON_STACK */
+ AssertMsgReturnStmt(rc == VINF_SUCCESS && pVirtqBuf->cbPhysReturn,
+ ("Not enough Rx buffers or capacity to accommodate ethernet packet\n"),
+ virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf),
+ VERR_INTERNAL_ERROR);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+ /*
+ * Try to do fast (e.g. single-buffer) copy to guest, even if MRG_RXBUF feature is enabled
+ */
+ STAM_PROFILE_START(&pThis->StatReceiveStore, a);
+ if (RT_LIKELY(FEATURE_DISABLED(MRG_RXBUF))
+ || RT_LIKELY(pVirtqBuf->cbPhysReturn > cb + cbPktHdr))
+ {
+ Log7Func(("Send Rx packet header and data to guest (single-buffer copy)...\n"));
+ pRxPktHdr->uNumBuffers = 1;
+ rc = virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cbPktHdr, pRxPktHdr, pVirtqBuf, 0 /* cbEnqueue */);
+ if (rc == VINF_SUCCESS)
+ rc = virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, pRxVirtq->uIdx, cb, pvBuf, pVirtqBuf, cbPktHdr + cb /* cbEnqueue */);
+#ifndef VIRTIO_VBUF_ON_STACK
+ virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+ virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, pRxVirtq->uIdx);
+ AssertMsgReturn(rc == VINF_SUCCESS, ("%Rrc\n", rc), rc);
+ }
+ else
+ {
+ Log7Func(("Send Rx pkt to guest (merged-buffer copy [MRG_RXBUF feature])...\n"));
+ rc = virtioNetR3RxPktMultibufXfer(pDevIns, pThis, (uint8_t *)pvBuf, cb, pRxPktHdr, pRxVirtq, pVirtqBuf);
+ return rc;
+ }
+ STAM_PROFILE_STOP(&pThis->StatReceiveStore, a);
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
+ */
+static DECLCALLBACK(int) virtioNetR3NetworkDown_ReceiveGso(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb,
+ PCPDMNETWORKGSO pGso)
+{
+ PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+ VIRTIONETPKTHDR rxPktHdr = { 0, VIRTIONET_HDR_GSO_NONE, 0, 0, 0, 0, 0 };
+
+ if (!pThis->fVirtioReady)
+ {
+ LogRelFunc(("VirtIO not ready, aborting downstream receive\n"));
+ return VERR_INTERRUPTED;
+ }
+ /*
+ * If GSO (Global Segment Offloading) was received from downstream PDM network device, massage the
+ * PDM-provided GSO parameters into VirtIO semantics, which get passed to guest virtio-net via
+ * Rx pkt header. See VirtIO 1.1, 5.1.6 Device Operation for more information.
+ */
+ if (pGso)
+ {
+ LogFunc(("[%s] (%RTmac) \n", pThis->szInst, pvBuf));
+
+ rxPktHdr.uFlags = VIRTIONET_HDR_F_NEEDS_CSUM;
+ rxPktHdr.uHdrLen = pGso->cbHdrsTotal;
+ rxPktHdr.uGsoSize = pGso->cbMaxSeg;
+ rxPktHdr.uChksumStart = pGso->offHdr2;
+
+ switch (pGso->u8Type)
+ {
+ case PDMNETWORKGSOTYPE_IPV4_TCP:
+ rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV4;
+ rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
+ break;
+ case PDMNETWORKGSOTYPE_IPV6_TCP:
+ rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_TCPV6;
+ rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETTCP, th_sum);
+ break;
+ case PDMNETWORKGSOTYPE_IPV4_UDP:
+ rxPktHdr.uGsoType = VIRTIONET_HDR_GSO_UDP;
+ rxPktHdr.uChksumOffset = RT_OFFSETOF(RTNETUDP, uh_sum);
+ break;
+ default:
+ LogFunc(("[%s] GSO type (0x%x) not supported\n", pThis->szInst, pGso->u8Type));
+ return VERR_NOT_SUPPORTED;
+ }
+ STAM_REL_COUNTER_INC(&pThis->StatReceiveGSO);
+ Log2Func(("[%s] gso type=%#x, cbHdrsTotal=%u cbHdrsSeg=%u mss=%u offHdr1=%#x offHdr2=%#x\n",
+ pThis->szInst, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
+ pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
+ }
+
+ /*
+ * Find a virtq with Rx bufs on avail ring, if any, and copy the packet to the guest's Rx buffer.
+ * @todo pk: PROBABLY NOT A SOPHISTICATED ENOUGH QUEUE SELECTION ALGORTITH FOR OPTIMAL MQ (FEATURE) SUPPORT
+ */
+ for (int uVirtqPair = 0; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
+ {
+ PVIRTIONETVIRTQ pRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
+ if (RT_SUCCESS(virtioNetR3CheckRxBufsAvail(pDevIns, pThis, pRxVirtq)))
+ {
+ int rc = VINF_SUCCESS;
+ STAM_PROFILE_START(&pThis->StatReceive, a);
+ virtioNetR3SetReadLed(pThisCC, true);
+ if (virtioNetR3AddressFilter(pThis, pvBuf, cb))
+ {
+ /* rxPktHdr is local stack variable that should not go out of scope in this use */
+ rc = virtioNetR3CopyRxPktToGuest(pDevIns, pThis, pThisCC, pvBuf, cb, &rxPktHdr, pThis->cbPktHdr, pRxVirtq);
+ STAM_REL_COUNTER_ADD(&pThis->StatReceiveBytes, cb);
+ }
+ virtioNetR3SetReadLed(pThisCC, false);
+ STAM_PROFILE_STOP(&pThis->StatReceive, a);
+ return rc;
+ }
+ }
+ return VERR_INTERRUPTED;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
+ */
+static DECLCALLBACK(int) virtioNetR3NetworkDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
+{
+
+#ifdef LOG_ENABLED
+ PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+ LogFunc(("[%s] (%RTmac)\n", pThis->szInst, pvBuf));
+#endif
+
+ return virtioNetR3NetworkDown_ReceiveGso(pInterface, pvBuf, cb, NULL);
+}
+
+/*
+ * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's Rx packet receive filtering.
+ * See VirtIO 1.0, 5.1.6.5.1
+ *
+ * @param pThis virtio-net instance
+ * @param pCtrlPktHdr Control packet header (which includes command parameters)
+ * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
+ */
+static uint8_t virtioNetR3CtrlRx(PVIRTIONET pThis, PVIRTIONETCC pThisCC,
+ PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
+{
+
+#define LOG_VIRTIONET_FLAG(fld) LogFunc(("[%s] Setting %s=%d\n", pThis->szInst, #fld, pThis->fld))
+
+ LogFunc(("[%s] Processing CTRL Rx command\n", pThis->szInst));
+ switch(pCtrlPktHdr->uCmd)
+ {
+ case VIRTIONET_CTRL_RX_PROMISC:
+ break;
+ case VIRTIONET_CTRL_RX_ALLMULTI:
+ break;
+ case VIRTIONET_CTRL_RX_ALLUNI:
+ /* fallthrough */
+ case VIRTIONET_CTRL_RX_NOMULTI:
+ /* fallthrough */
+ case VIRTIONET_CTRL_RX_NOUNI:
+ /* fallthrough */
+ case VIRTIONET_CTRL_RX_NOBCAST:
+ AssertMsgReturn(FEATURE_ENABLED(CTRL_RX_EXTRA),
+ ("CTRL 'extra' cmd w/o VIRTIONET_F_CTRL_RX_EXTRA feature negotiated - skipping\n"),
+ VIRTIONET_ERROR);
+ /* fall out */
+ }
+
+ uint8_t fOn, fPromiscChanged = false;
+ virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &fOn, (size_t)RT_MIN(pVirtqBuf->cbPhysSend, sizeof(fOn)));
+
+ switch(pCtrlPktHdr->uCmd)
+ {
+ case VIRTIONET_CTRL_RX_PROMISC:
+ pThis->fPromiscuous = RT_BOOL(fOn);
+ fPromiscChanged = true;
+ LOG_VIRTIONET_FLAG(fPromiscuous);
+ break;
+ case VIRTIONET_CTRL_RX_ALLMULTI:
+ pThis->fAllMulticast = RT_BOOL(fOn);
+ fPromiscChanged = true;
+ LOG_VIRTIONET_FLAG(fAllMulticast);
+ break;
+ case VIRTIONET_CTRL_RX_ALLUNI:
+ pThis->fAllUnicast = RT_BOOL(fOn);
+ LOG_VIRTIONET_FLAG(fAllUnicast);
+ break;
+ case VIRTIONET_CTRL_RX_NOMULTI:
+ pThis->fNoMulticast = RT_BOOL(fOn);
+ LOG_VIRTIONET_FLAG(fNoMulticast);
+ break;
+ case VIRTIONET_CTRL_RX_NOUNI:
+ pThis->fNoUnicast = RT_BOOL(fOn);
+ LOG_VIRTIONET_FLAG(fNoUnicast);
+ break;
+ case VIRTIONET_CTRL_RX_NOBCAST:
+ pThis->fNoBroadcast = RT_BOOL(fOn);
+ LOG_VIRTIONET_FLAG(fNoBroadcast);
+ break;
+ }
+
+ if (pThisCC->pDrv && fPromiscChanged)
+ pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, (pThis->fPromiscuous || pThis->fAllMulticast));
+
+ return VIRTIONET_OK;
+}
+
+/*
+ * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's MAC filter tables
+ * See VirtIO 1.0, 5.1.6.5.2
+ *
+ * @param pThis virtio-net instance
+ * @param pCtrlPktHdr Control packet header (which includes command parameters)
+ * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
+ */
+static uint8_t virtioNetR3CtrlMac(PVIRTIONET pThis, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
+{
+ LogFunc(("[%s] Processing CTRL MAC command\n", pThis->szInst));
+
+
+ AssertMsgReturn(pVirtqBuf->cbPhysSend >= sizeof(*pCtrlPktHdr),
+ ("insufficient descriptor space for ctrl pkt hdr"),
+ VIRTIONET_ERROR);
+
+ size_t cbRemaining = pVirtqBuf->cbPhysSend;
+ switch(pCtrlPktHdr->uCmd)
+ {
+ case VIRTIONET_CTRL_MAC_ADDR_SET:
+ {
+ /* Set default Rx filter MAC */
+ AssertMsgReturn(cbRemaining >= sizeof(pThis->rxFilterMacDefault),
+ ("DESC chain too small to process CTRL_MAC_ADDR_SET cmd\n"), VIRTIONET_ERROR);
+
+ virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->rxFilterMacDefault, sizeof(RTMAC));
+ break;
+ }
+ case VIRTIONET_CTRL_MAC_TABLE_SET:
+ {
+ VIRTIONET_CTRL_MAC_TABLE_LEN cMacs;
+
+ /* Load unicast MAC filter table */
+ AssertMsgReturn(cbRemaining >= sizeof(cMacs),
+ ("DESC chain too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
+
+ /* Fetch count of unicast filter MACs from guest buffer */
+ virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
+ cbRemaining -= sizeof(cMacs);
+
+ Log7Func(("[%s] Guest provided %d unicast MAC Table entries\n", pThis->szInst, cMacs));
+
+ AssertMsgReturn(cMacs <= RT_ELEMENTS(pThis->aMacUnicastFilter),
+ ("Guest provided Unicast MAC filter table exceeds hardcoded table size"), VIRTIONET_ERROR);
+
+ if (cMacs)
+ {
+ uint32_t cbMacs = cMacs * sizeof(RTMAC);
+
+ AssertMsgReturn(cbRemaining >= cbMacs,
+ ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
+
+
+ /* Fetch unicast table contents from guest buffer */
+ virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacUnicastFilter, cbMacs);
+ cbRemaining -= cbMacs;
+ }
+ pThis->cUnicastFilterMacs = cMacs;
+
+ /* Load multicast MAC filter table */
+ AssertMsgReturn(cbRemaining >= sizeof(cMacs),
+ ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
+
+ /* Fetch count of multicast filter MACs from guest buffer */
+ virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cMacs, sizeof(cMacs));
+ cbRemaining -= sizeof(cMacs);
+
+ Log10Func(("[%s] Guest provided %d multicast MAC Table entries\n", pThis->szInst, cMacs));
+
+ AssertMsgReturn(cMacs <= RT_ELEMENTS(pThis->aMacMulticastFilter),
+ ("Guest provided Unicast MAC filter table exceeds hardcoded table size"), VIRTIONET_ERROR);
+
+ if (cMacs)
+ {
+ uint32_t cbMacs = cMacs * sizeof(RTMAC);
+
+ AssertMsgReturn(cbRemaining >= cbMacs,
+ ("Virtq buffer too small to process CTRL_MAC_TABLE_SET cmd\n"), VIRTIONET_ERROR);
+
+ /* Fetch multicast table contents from guest buffer */
+ virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &pThis->aMacMulticastFilter, cbMacs);
+ cbRemaining -= cbMacs;
+ }
+ pThis->cMulticastFilterMacs = cMacs;
+
+#ifdef LOG_ENABLED
+ LogFunc(("[%s] unicast MACs:\n", pThis->szInst));
+ for(unsigned i = 0; i < pThis->cUnicastFilterMacs; i++)
+ LogFunc((" %RTmac\n", &pThis->aMacUnicastFilter[i]));
+
+ LogFunc(("[%s] multicast MACs:\n", pThis->szInst));
+ for(unsigned i = 0; i < pThis->cMulticastFilterMacs; i++)
+ LogFunc((" %RTmac\n", &pThis->aMacMulticastFilter[i]));
+#endif
+ break;
+ }
+ default:
+ LogRelFunc(("Unrecognized MAC subcommand in CTRL pkt from guest\n"));
+ return VIRTIONET_ERROR;
+ }
+ return VIRTIONET_OK;
+}
+
+/*
+ * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's MQ (multiqueue) operations.
+ * See VirtIO 1.0, 5.1.6.5.5
+ *
+ * @param pThis virtio-net instance
+ * @param pCtrlPktHdr Control packet header (which includes command parameters)
+ * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
+ */
+static uint8_t virtioNetR3CtrlMultiQueue(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMDEVINS pDevIns, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
+{
+ LogFunc(("[%s] Processing CTRL MQ command\n", pThis->szInst));
+
+ uint16_t cVirtqPairs;
+ switch(pCtrlPktHdr->uCmd)
+ {
+ case VIRTIONET_CTRL_MQ_VQ_PAIRS_SET:
+ {
+ size_t cbRemaining = pVirtqBuf->cbPhysSend;
+
+ AssertMsgReturn(cbRemaining >= sizeof(cVirtqPairs),
+ ("DESC chain too small for VIRTIONET_CTRL_MQ cmd processing"), VIRTIONET_ERROR);
+
+ /* Fetch number of virtq pairs from guest buffer */
+ virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &cVirtqPairs, sizeof(cVirtqPairs));
+
+ AssertMsgReturn(cVirtqPairs <= VIRTIONET_MAX_QPAIRS,
+ ("[%s] Guest CTRL MQ virtq pair count out of range [%d])\n", pThis->szInst, cVirtqPairs), VIRTIONET_ERROR);
+
+ LogFunc(("[%s] Guest specifies %d VQ pairs in use\n", pThis->szInst, cVirtqPairs));
+ pThis->cVirtqPairs = cVirtqPairs;
+ break;
+ }
+ default:
+ LogRelFunc(("Unrecognized multiqueue subcommand in CTRL pkt from guest\n"));
+ return VIRTIONET_ERROR;
+ }
+
+ /*
+ * The MQ control function is invoked by the guest in an RPC like manner to change
+ * the Rx/Tx queue pair count. If the new value exceeds the number of queues
+ * (and associated workers) already initialized initialize only the new queues and
+ * respective workers.
+ */
+ if (pThis->cVirtqPairs > pThis->cInitializedVirtqPairs)
+ {
+ virtioNetR3SetVirtqNames(pThis, virtioCoreIsLegacyMode(&pThis->Virtio));
+ int rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
+ if (RT_FAILURE(rc))
+ {
+ LogRelFunc(("Failed to create worker threads\n"));
+ return VIRTIONET_ERROR;
+ }
+ }
+ return VIRTIONET_OK;
+}
+
+/*
+ * Dispatched to here from virtioNetR3Ctrl() to configure this virtio-net device's VLAN filtering.
+ * See VirtIO 1.0, 5.1.6.5.3
+ *
+ * @param pThis virtio-net instance
+ * @param pCtrlPktHdr Control packet header (which includes command parameters)
+ * @param pVirtqBuf Buffer from ctrlq buffer (contains command data)
+ */
+static uint8_t virtioNetR3CtrlVlan(PVIRTIONET pThis, PVIRTIONET_CTRL_HDR_T pCtrlPktHdr, PVIRTQBUF pVirtqBuf)
+{
+ LogFunc(("[%s] Processing CTRL VLAN command\n", pThis->szInst));
+
+ uint16_t uVlanId;
+ size_t cbRemaining = pVirtqBuf->cbPhysSend;
+
+ AssertMsgReturn(cbRemaining >= sizeof(uVlanId),
+ ("DESC chain too small for VIRTIONET_CTRL_VLAN cmd processing"), VIRTIONET_ERROR);
+
+ /* Fetch VLAN ID from guest buffer */
+ virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &uVlanId, sizeof(uVlanId));
+
+ AssertMsgReturn(uVlanId < VIRTIONET_MAX_VLAN_ID,
+ ("%s VLAN ID out of range (VLAN ID=%u)\n", pThis->szInst, uVlanId), VIRTIONET_ERROR);
+
+ LogFunc(("[%s] uCommand=%u VLAN ID=%u\n", pThis->szInst, pCtrlPktHdr->uCmd, uVlanId));
+
+ switch (pCtrlPktHdr->uCmd)
+ {
+ case VIRTIONET_CTRL_VLAN_ADD:
+ ASMBitSet(pThis->aVlanFilter, uVlanId);
+ break;
+ case VIRTIONET_CTRL_VLAN_DEL:
+ ASMBitClear(pThis->aVlanFilter, uVlanId);
+ break;
+ default:
+ LogRelFunc(("Unrecognized VLAN subcommand in CTRL pkt from guest\n"));
+ return VIRTIONET_ERROR;
+ }
+ return VIRTIONET_OK;
+}
+
+/**
+ * Processes control command from guest.
+ * See VirtIO 1.0 spec, 5.1.6 "Device Operation" and 5.1.6.5 "Control Virtqueue".
+ *
+ * The control command is contained in a virtio buffer pulled from the virtio-net defined control queue (ctrlq).
+ * Command type is parsed is dispatched to a command-specific device-configuration handler function (e.g. RX, MAC, VLAN, MQ
+ * and ANNOUNCE).
+ *
+ * This function handles all parts of the host-side of the ctrlq round-trip buffer processing.
+ *
+ * Invoked by worker for virtio-net control queue to process a queued control command buffer.
+ *
+ * @param pDevIns PDM device instance
+ * @param pThis virtio-net device instance
+ * @param pThisCC virtio-net device instance
+ * @param pVirtqBuf pointer to buffer pulled from virtq (input to this function)
+ */
+static void virtioNetR3Ctrl(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
+ PVIRTQBUF pVirtqBuf)
+{
+ if (!(pThis->fNegotiatedFeatures & VIRTIONET_F_CTRL_VQ))
+ LogFunc(("[%s] WARNING: Guest using CTRL queue w/o negotiating VIRTIONET_F_CTRL_VQ feature\n", pThis->szInst));
+
+ LogFunc(("[%s] Received CTRL packet from guest\n", pThis->szInst));
+
+ if (pVirtqBuf->cbPhysSend < 2)
+ {
+ LogFunc(("[%s] CTRL packet from guest driver incomplete. Skipping ctrl cmd\n", pThis->szInst));
+ return;
+ }
+ else if (pVirtqBuf->cbPhysReturn < sizeof(VIRTIONET_CTRL_HDR_T_ACK))
+ {
+ LogFunc(("[%s] Guest driver didn't allocate memory to receive ctrl pkt ACK. Skipping ctrl cmd\n", pThis->szInst));
+ return;
+ }
+
+ /*
+ * Allocate buffer and read in the control command
+ */
+ VIRTIONET_CTRL_HDR_T CtrlPktHdr; RT_ZERO(CtrlPktHdr);
+ AssertLogRelMsgReturnVoid(pVirtqBuf->cbPhysSend >= sizeof(CtrlPktHdr),
+ ("DESC chain too small for CTRL pkt header"));
+ virtioCoreR3VirtqBufDrain(&pThis->Virtio, pVirtqBuf, &CtrlPktHdr, sizeof(CtrlPktHdr));
+
+ Log7Func(("[%s] CTRL COMMAND: class=%d command=%d\n", pThis->szInst, CtrlPktHdr.uClass, CtrlPktHdr.uCmd));
+
+ uint8_t uAck;
+ switch (CtrlPktHdr.uClass)
+ {
+ case VIRTIONET_CTRL_RX:
+ uAck = virtioNetR3CtrlRx(pThis, pThisCC, &CtrlPktHdr, pVirtqBuf);
+ break;
+ case VIRTIONET_CTRL_MAC:
+ uAck = virtioNetR3CtrlMac(pThis, &CtrlPktHdr, pVirtqBuf);
+ break;
+ case VIRTIONET_CTRL_VLAN:
+ uAck = virtioNetR3CtrlVlan(pThis, &CtrlPktHdr, pVirtqBuf);
+ break;
+ case VIRTIONET_CTRL_MQ:
+ uAck = virtioNetR3CtrlMultiQueue(pThis, pThisCC, pDevIns, &CtrlPktHdr, pVirtqBuf);
+ break;
+ case VIRTIONET_CTRL_ANNOUNCE:
+ uAck = VIRTIONET_OK;
+ if (FEATURE_DISABLED(STATUS) || FEATURE_DISABLED(GUEST_ANNOUNCE))
+ {
+ LogFunc(("%s Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE.\n"
+ "VIRTIO_F_STATUS or VIRTIO_F_GUEST_ANNOUNCE feature not enabled\n", pThis->szInst));
+ break;
+ }
+ if (CtrlPktHdr.uCmd != VIRTIONET_CTRL_ANNOUNCE_ACK)
+ {
+ LogFunc(("[%s] Ignoring CTRL class VIRTIONET_CTRL_ANNOUNCE. Unrecognized uCmd\n", pThis->szInst));
+ break;
+ }
+#if FEATURE_OFFERED(STATUS)
+ pThis->virtioNetConfig.uStatus &= ~VIRTIONET_F_ANNOUNCE;
+#endif
+ Log7Func(("[%s] Clearing VIRTIONET_F_ANNOUNCE in config status\n", pThis->szInst));
+ break;
+ default:
+ LogRelFunc(("Unrecognized CTRL pkt hdr class (%d)\n", CtrlPktHdr.uClass));
+ uAck = VIRTIONET_ERROR;
+ }
+
+ /* Return CTRL packet Ack byte (result code) to guest driver */
+ RTSGSEG aStaticSegs[] = { { &uAck, sizeof(uAck) } };
+ RTSGBUF SgBuf;
+
+ RTSgBufInit(&SgBuf, aStaticSegs, RT_ELEMENTS(aStaticSegs));
+ virtioCoreR3VirtqUsedBufPut(pDevIns, &pThis->Virtio, CTRLQIDX, &SgBuf, pVirtqBuf, true /* fFence */);
+ virtioCoreVirtqUsedRingSync(pDevIns, &pThis->Virtio, CTRLQIDX);
+
+ LogFunc(("%s Finished processing CTRL command with status %s\n",
+ pThis->szInst, uAck == VIRTIONET_OK ? "VIRTIONET_OK" : "VIRTIONET_ERROR"));
+}
+
+/**
+ * Reads virtio-net pkt header from provided Phy. addr of virtio descriptor chain
+ * (e.g. S/G segment from guest-driver provided buffer pulled from Tx virtq)
+ * Verifies state and supported modes, sets TCP header size.
+ *
+ * @param pVirtio VirtIO core instance data
+ * @param pThis virtio-net instance
+ * @param pDevIns PDM device instance
+ * @param GCPhys Phys. Address from where to read virtio-net pkt header
+ * @param pPktHdr Where to store read Tx pkt hdr (virtio pkt hdr size is determined from instance configuration)
+ * @param cbFrame Total pkt frame size to inform bounds check
+ */
+static int virtioNetR3ReadVirtioTxPktHdr(PVIRTIOCORE pVirtio, PVIRTIONET pThis, PPDMDEVINS pDevIns, RTGCPHYS GCPhys, PVIRTIONETPKTHDR pPktHdr, size_t cbFrame)
+{
+ int rc = virtioCoreGCPhysRead(pVirtio, pDevIns, GCPhys, pPktHdr, pThis->cbPktHdr);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ LogFunc(("pktHdr (flags=%x gso-type=%x len=%x gso-size=%x Chksum-start=%x Chksum-offset=%x) cbFrame=%d\n",
+ pPktHdr->uFlags, pPktHdr->uGsoType, pPktHdr->uHdrLen,
+ pPktHdr->uGsoSize, pPktHdr->uChksumStart, pPktHdr->uChksumOffset, cbFrame));
+
+ if (pPktHdr->uGsoType)
+ {
+ /* Segmentation offloading cannot be done without checksumming, and we do not support ECN */
+ AssertMsgReturn( RT_LIKELY(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
+ && !(RT_UNLIKELY(pPktHdr->uGsoType & VIRTIONET_HDR_GSO_ECN)),
+ ("Unsupported ECN request in pkt header\n"), VERR_NOT_SUPPORTED);
+
+ uint32_t uTcpHdrSize;
+ switch (pPktHdr->uGsoType)
+ {
+ case VIRTIONET_HDR_GSO_TCPV4:
+ case VIRTIONET_HDR_GSO_TCPV6:
+ uTcpHdrSize = sizeof(RTNETTCP);
+ break;
+ case VIRTIONET_HDR_GSO_UDP:
+ uTcpHdrSize = 0;
+ break;
+ default:
+ LogFunc(("Bad GSO type in packet header\n"));
+ return VERR_INVALID_PARAMETER;
+ }
+ /* Header + MSS must not exceed the packet size. */
+ AssertMsgReturn(RT_LIKELY(uTcpHdrSize + pPktHdr->uChksumStart + pPktHdr->uGsoSize <= cbFrame),
+ ("Header plus message exceeds packet size"), VERR_BUFFER_OVERFLOW);
+ }
+
+ AssertMsgReturn( !(pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
+ || sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset <= cbFrame,
+ ("Checksum (%d bytes) doesn't fit into pkt header (%d bytes)\n",
+ sizeof(uint16_t) + pPktHdr->uChksumStart + pPktHdr->uChksumOffset, cbFrame),
+ VERR_BUFFER_OVERFLOW);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Transmits single GSO frame via PDM framework to downstream PDM device, to emit from virtual NIC.
+ *
+ * This does final prep of GSO parameters including checksum calculation if configured
+ * (e.g. if VIRTIONET_HDR_F_NEEDS_CSUM flag is set).
+ *
+ * @param pThis virtio-net instance
+ * @param pThisCC virtio-net instance
+ * @param pSgBuf PDM S/G buffer containing pkt and hdr to transmit
+ * @param pGso GSO parameters used for the packet
+ * @param pPktHdr virtio-net pkt header to adapt to PDM semantics
+ */
+static int virtioNetR3TransmitFrame(PVIRTIONET pThis, PVIRTIONETCC pThisCC, PPDMSCATTERGATHER pSgBuf,
+ PPDMNETWORKGSO pGso, PVIRTIONETPKTHDR pPktHdr)
+{
+
+ virtioNetR3PacketDump(pThis, (uint8_t *)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, "--> Outgoing");
+ if (pGso)
+ {
+ /* Some guests (RHEL) may report HdrLen excluding transport layer header!
+ * Thus cannot use cdHdrs provided by the guest because of different ways
+ * it gets filled out by different versions of kernels. */
+ Log4Func(("%s HdrLen before adjustment %d.\n", pThis->szInst, pGso->cbHdrsTotal));
+ switch (pGso->u8Type)
+ {
+ case PDMNETWORKGSOTYPE_IPV4_TCP:
+ case PDMNETWORKGSOTYPE_IPV6_TCP:
+ pGso->cbHdrsTotal = pPktHdr->uChksumStart +
+ ((PRTNETTCP)(((uint8_t*)pSgBuf->aSegs[0].pvSeg) + pPktHdr->uChksumStart))->th_off * 4;
+ AssertMsgReturn(pSgBuf->cbUsed > pGso->cbHdrsTotal,
+ ("cbHdrsTotal exceeds size of frame"), VERR_BUFFER_OVERFLOW);
+ pGso->cbHdrsSeg = pGso->cbHdrsTotal;
+ break;
+ case PDMNETWORKGSOTYPE_IPV4_UDP:
+ pGso->cbHdrsTotal = (uint8_t)(pPktHdr->uChksumStart + sizeof(RTNETUDP));
+ pGso->cbHdrsSeg = pPktHdr->uChksumStart;
+ break;
+ case PDMNETWORKGSOTYPE_INVALID:
+ LogFunc(("%s ignoring invalid GSO frame\n", pThis->szInst));
+ return VERR_INVALID_PARAMETER;
+ }
+ /* Update GSO structure embedded into the frame */
+ ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsTotal = pGso->cbHdrsTotal;
+ ((PPDMNETWORKGSO)pSgBuf->pvUser)->cbHdrsSeg = pGso->cbHdrsSeg;
+ Log4Func(("%s adjusted HdrLen to %d.\n",
+ pThis->szInst, pGso->cbHdrsTotal));
+ Log2Func(("%s gso type=%x cbHdrsTotal=%u cbHdrsSeg=%u mss=%u off1=0x%x off2=0x%x\n",
+ pThis->szInst, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg,
+ pGso->cbMaxSeg, pGso->offHdr1, pGso->offHdr2));
+ STAM_REL_COUNTER_INC(&pThis->StatTransmitGSO);
+ }
+ else if (pPktHdr->uFlags & VIRTIONET_HDR_F_NEEDS_CSUM)
+ {
+ STAM_REL_COUNTER_INC(&pThis->StatTransmitCSum);
+ /*
+ * This is not GSO frame but checksum offloading is requested.
+ */
+ virtioNetR3Calc16BitChecksum((uint8_t*)pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed,
+ pPktHdr->uChksumStart, pPktHdr->uChksumOffset);
+ }
+
+ return pThisCC->pDrv->pfnSendBuf(pThisCC->pDrv, pSgBuf, true /* fOnWorkerThread */);
+}
+
+/**
+ * Non-reentrant function transmits all available packets from specified Tx virtq to downstream
+ * PDM device (if cable is connected). For each Tx pkt, virtio-net pkt header is converted
+ * to required GSO information (VBox host network stack semantics)
+ *
+ * @param pDevIns PDM device instance
+ * @param pThis virtio-net device instance
+ * @param pThisCC virtio-net device instance
+ * @param pTxVirtq Address of transmit virtq
+ * @param fOnWorkerThread Flag to PDM whether to use caller's or or PDM transmit worker's thread.
+ */
+static int virtioNetR3TransmitPkts(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC,
+ PVIRTIONETVIRTQ pTxVirtq, bool fOnWorkerThread)
+{
+ PVIRTIOCORE pVirtio = &pThis->Virtio;
+
+
+ if (!pThis->fVirtioReady)
+ {
+ LogFunc(("%s Ignoring Tx requests. VirtIO not ready (status=0x%x)\n",
+ pThis->szInst, pThis->virtioNetConfig.uStatus));
+ return VERR_IGNORED;
+ }
+
+ if (!pThis->fCableConnected)
+ {
+ Log(("[%s] Ignoring transmit requests while cable is disconnected.\n", pThis->szInst));
+ return VERR_IGNORED;
+ }
+
+ /*
+ * Only one thread is allowed to transmit at a time, others should skip transmission as the packets
+ * will be picked up by the transmitting thread.
+ */
+ if (!ASMAtomicCmpXchgU32(&pThis->uIsTransmitting, 1, 0))
+ return VERR_IGNORED;
+
+ PPDMINETWORKUP pDrv = pThisCC->pDrv;
+ if (pDrv)
+ {
+ int rc = pDrv->pfnBeginXmit(pDrv, fOnWorkerThread);
+ Assert(rc == VINF_SUCCESS || rc == VERR_TRY_AGAIN);
+ if (rc == VERR_TRY_AGAIN)
+ {
+ ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
+ return VERR_TRY_AGAIN;
+ }
+ }
+ int cPkts = virtioCoreVirtqAvailBufCount(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
+ if (!cPkts)
+ {
+ LogFunc(("[%s] No packets to send found on %s\n", pThis->szInst, pTxVirtq->szName));
+
+ if (pDrv)
+ pDrv->pfnEndXmit(pDrv);
+
+ ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
+ return VERR_MISSING;
+ }
+ LogFunc(("[%s] About to transmit %d pending packet%c\n", pThis->szInst, cPkts, cPkts == 1 ? ' ' : 's'));
+
+ virtioNetR3SetWriteLed(pThisCC, true);
+
+ /* Disable notifications until all available descriptors have been processed */
+ if (!(pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX))
+ virtioCoreVirtqEnableNotify(&pThis->Virtio, pTxVirtq->uIdx, false /* fEnable */);
+
+ int rc;
+#ifdef VIRTIO_VBUF_ON_STACK
+ VIRTQBUF_T VirtqBuf;
+
+ VirtqBuf.u32Magic = VIRTQBUF_MAGIC;
+ VirtqBuf.cRefs = 1;
+
+ PVIRTQBUF pVirtqBuf = &VirtqBuf;
+ while ((rc = virtioCoreR3VirtqAvailBufPeek(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, pVirtqBuf)) == VINF_SUCCESS)
+#else /* !VIRTIO_VBUF_ON_STACK */
+ PVIRTQBUF pVirtqBuf = NULL;
+ while ((rc = virtioCoreR3VirtqAvailBufPeek(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, &pVirtqBuf)) == VINF_SUCCESS)
+#endif /* !VIRTIO_VBUF_ON_STACK */
+ {
+ Log10Func(("[%s] fetched descriptor chain from %s\n", pThis->szInst, pTxVirtq->szName));
+
+ PVIRTIOSGBUF pSgPhysSend = pVirtqBuf->pSgPhysSend;
+ PVIRTIOSGSEG paSegsFromGuest = pSgPhysSend->paSegs;
+ uint32_t cSegsFromGuest = pSgPhysSend->cSegs;
+ size_t uFrameSize = 0;
+
+ AssertMsgReturn(paSegsFromGuest[0].cbSeg >= pThis->cbPktHdr,
+ ("Desc chain's first seg has insufficient space for pkt header!\n"),
+ VERR_INTERNAL_ERROR);
+
+#ifdef VIRTIO_VBUF_ON_STACK
+ VIRTIONETPKTHDR PktHdr;
+ PVIRTIONETPKTHDR pPktHdr = &PktHdr;
+#else /* !VIRTIO_VBUF_ON_STACK */
+ PVIRTIONETPKTHDR pPktHdr = (PVIRTIONETPKTHDR)RTMemAllocZ(pThis->cbPktHdr);
+ AssertMsgReturn(pPktHdr, ("Out of Memory\n"), VERR_NO_MEMORY);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+
+ /* Compute total frame size from guest (including virtio-net pkt hdr) */
+ for (unsigned i = 0; i < cSegsFromGuest && uFrameSize < VIRTIONET_MAX_FRAME_SIZE; i++)
+ uFrameSize += paSegsFromGuest[i].cbSeg;
+
+ Log5Func(("[%s] complete frame is %u bytes.\n", pThis->szInst, uFrameSize));
+ Assert(uFrameSize <= VIRTIONET_MAX_FRAME_SIZE);
+
+ /* Truncate oversized frames. */
+ if (uFrameSize > VIRTIONET_MAX_FRAME_SIZE)
+ uFrameSize = VIRTIONET_MAX_FRAME_SIZE;
+
+ if (pThisCC->pDrv)
+ {
+ uFrameSize -= pThis->cbPktHdr;
+ /*
+ * Peel off pkt header and convert to PDM/GSO semantics.
+ */
+ rc = virtioNetR3ReadVirtioTxPktHdr(pVirtio, pThis, pDevIns, paSegsFromGuest[0].GCPhys, pPktHdr, uFrameSize /* cbFrame */);
+ if (RT_FAILURE(rc))
+ return rc;
+ virtioCoreGCPhysChainAdvance(pSgPhysSend, pThis->cbPktHdr);
+
+ PDMNETWORKGSO Gso, *pGso = virtioNetR3SetupGsoCtx(&Gso, pPktHdr);
+
+ /* Allocate PDM transmit buffer to send guest provided network frame from to VBox network leaf device */
+ PPDMSCATTERGATHER pSgBufToPdmLeafDevice;
+ rc = pThisCC->pDrv->pfnAllocBuf(pThisCC->pDrv, uFrameSize, pGso, &pSgBufToPdmLeafDevice);
+
+ /*
+ * Copy virtio-net guest S/G buffer to PDM leaf driver S/G buffer
+ * converting from GCphys to virt memory at the same time
+ */
+ if (RT_SUCCESS(rc))
+ {
+ STAM_REL_COUNTER_INC(&pThis->StatTransmitPackets);
+ STAM_PROFILE_START(&pThis->StatTransmitSend, a);
+
+ size_t cbCopied = 0;
+ size_t cbRemain = pSgBufToPdmLeafDevice->cbUsed = uFrameSize;
+ uint64_t uOffset = 0;
+ while (cbRemain)
+ {
+ PVIRTIOSGSEG paSeg = &pSgPhysSend->paSegs[pSgPhysSend->idxSeg];
+ uint64_t srcSgStart = (uint64_t)paSeg->GCPhys;
+ uint64_t srcSgLen = (uint64_t)paSeg->cbSeg;
+ uint64_t srcSgCur = (uint64_t)pSgPhysSend->GCPhysCur;
+ cbCopied = RT_MIN((uint64_t)cbRemain, srcSgLen - (srcSgCur - srcSgStart));
+ /*
+ * Guest sent a bogus S/G chain, there doesn't seem to be a way to report an error but
+ * as this shouldn't happen anyway we just stop proccessing this chain.
+ */
+ if (RT_UNLIKELY(!cbCopied))
+ break;
+ virtioCoreGCPhysRead(pVirtio, pDevIns,
+ (RTGCPHYS)pSgPhysSend->GCPhysCur,
+ ((uint8_t *)pSgBufToPdmLeafDevice->aSegs[0].pvSeg) + uOffset, cbCopied);
+ virtioCoreGCPhysChainAdvance(pSgPhysSend, cbCopied);
+ cbRemain -= cbCopied;
+ uOffset += cbCopied;
+ }
+
+ LogFunc((".... Copied %lu/%lu bytes to %lu byte guest buffer. Buf residual=%lu\n",
+ uOffset, uFrameSize, pVirtqBuf->cbPhysSend, virtioCoreGCPhysChainCalcLengthLeft(pSgPhysSend)));
+
+ rc = virtioNetR3TransmitFrame(pThis, pThisCC, pSgBufToPdmLeafDevice, pGso, pPktHdr);
+ if (RT_FAILURE(rc))
+ {
+ LogFunc(("[%s] Failed to transmit frame, rc = %Rrc\n", pThis->szInst, rc));
+ STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
+ STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
+ pThisCC->pDrv->pfnFreeBuf(pThisCC->pDrv, pSgBufToPdmLeafDevice);
+ }
+ STAM_PROFILE_STOP(&pThis->StatTransmitSend, a);
+ STAM_REL_COUNTER_ADD(&pThis->StatTransmitBytes, uOffset);
+ }
+ else
+ {
+ Log4Func(("Failed to allocate S/G buffer: frame size=%u rc=%Rrc\n", uFrameSize, rc));
+ /* Stop trying to fetch TX descriptors until we get more bandwidth. */
+#ifndef VIRTIO_VBUF_ON_STACK
+ virtioCoreR3VirtqBufRelease(pVirtio, pVirtqBuf);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+ break;
+ }
+
+ virtioCoreR3VirtqAvailBufNext(pVirtio, pTxVirtq->uIdx);
+
+ /* No data to return to guest, but necessary to put elem (e.g. desc chain head idx) on used ring */
+ virtioCoreR3VirtqUsedBufPut(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx, NULL, pVirtqBuf, true /* fFence */);
+ virtioCoreVirtqUsedRingSync(pVirtio->pDevInsR3, pVirtio, pTxVirtq->uIdx);
+ }
+
+#ifndef VIRTIO_VBUF_ON_STACK
+ virtioCoreR3VirtqBufRelease(pVirtio, pVirtqBuf);
+ pVirtqBuf = NULL;
+#endif /* !VIRTIO_VBUF_ON_STACK */
+ /* Before we break the loop we need to check if the queue is empty,
+ * re-enable notifications, and then re-check again to avoid missing
+ * a notification for the descriptor that is added to the queue
+ * after we have checked it on being empty, but before we re-enabled
+ * notifications.
+ */
+ if (!(pVirtio->uDriverFeatures & VIRTIO_F_EVENT_IDX)
+ && IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pTxVirtq->uIdx))
+ virtioCoreVirtqEnableNotify(&pThis->Virtio, pTxVirtq->uIdx, true /* fEnable */);
+ }
+ virtioNetR3SetWriteLed(pThisCC, false);
+
+ if (pDrv)
+ pDrv->pfnEndXmit(pDrv);
+
+ ASMAtomicWriteU32(&pThis->uIsTransmitting, 0);
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
+ */
+static DECLCALLBACK(void) virtioNetR3NetworkDown_XmitPending(PPDMINETWORKDOWN pInterface)
+{
+ LogFunc(("\n"));
+ PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkDown);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
+ PVIRTIONETVIRTQ pTxVirtq = &pThis->aVirtqs[TXQIDX(0)];
+ STAM_COUNTER_INC(&pThis->StatTransmitByNetwork);
+
+ (void)virtioNetR3TransmitPkts(pDevIns, pThis, pThisCC, pTxVirtq, true /*fOnWorkerThread*/);
+}
+
+/**
+ * @callback_method_impl{FNTMTIMERDEV, Link Up Timer handler.}
+ */
+static DECLCALLBACK(void) virtioNetR3LinkUpTimer(PPDMDEVINS pDevIns, TMTIMERHANDLE hTimer, void *pvUser)
+{
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+ PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+
+ SET_LINK_UP(pThis);
+ virtioNetWakeupRxBufWaiter(pDevIns);
+
+ if (pThisCC->pDrv)
+ pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, PDMNETWORKLINKSTATE_UP);
+
+ LogFunc(("[%s] Link is up\n", pThis->szInst));
+ RT_NOREF(hTimer, pvUser);
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnSetLinkState}
+ */
+static DECLCALLBACK(int) virtioNetR3NetworkConfig_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
+{
+ PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
+ PPDMDEVINS pDevIns = pThisCC->pDevIns;
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+
+ bool fRequestedLinkStateIsUp = (enmState == PDMNETWORKLINKSTATE_UP);
+
+#ifdef LOG_ENABLED
+ if (LogIs7Enabled())
+ {
+ LogFunc(("[%s]", pThis->szInst));
+ switch(enmState)
+ {
+ case PDMNETWORKLINKSTATE_UP:
+ Log(("UP\n"));
+ break;
+ case PDMNETWORKLINKSTATE_DOWN:
+ Log(("DOWN\n"));
+ break;
+ case PDMNETWORKLINKSTATE_DOWN_RESUME:
+ Log(("DOWN (RESUME)\n"));
+ break;
+ default:
+ Log(("UNKNOWN)\n"));
+ }
+ }
+#endif
+
+ if (enmState == PDMNETWORKLINKSTATE_DOWN_RESUME)
+ {
+ if (IS_LINK_UP(pThis))
+ {
+ /*
+ * We bother to bring the link down only if it was up previously. The UP link state
+ * notification will be sent when the link actually goes up in virtioNetR3LinkUpTimer().
+ */
+ virtioNetR3TempLinkDown(pDevIns, pThis, pThisCC);
+ if (pThisCC->pDrv)
+ pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
+ }
+ }
+ else if (fRequestedLinkStateIsUp != IS_LINK_UP(pThis))
+ {
+ if (fRequestedLinkStateIsUp)
+ {
+ Log(("[%s] Link is up\n", pThis->szInst));
+ pThis->fCableConnected = true;
+ SET_LINK_UP(pThis);
+ }
+ else /* Link requested to be brought down */
+ {
+ /* The link was brought down explicitly, make sure it won't come up by timer. */
+ PDMDevHlpTimerStop(pDevIns, pThisCC->hLinkUpTimer);
+ Log(("[%s] Link is down\n", pThis->szInst));
+ pThis->fCableConnected = false;
+ SET_LINK_DOWN(pThis);
+ }
+ if (pThisCC->pDrv)
+ pThisCC->pDrv->pfnNotifyLinkChanged(pThisCC->pDrv, enmState);
+ }
+ return VINF_SUCCESS;
+}
+/**
+ * @interface_method_impl{PDMINETWORKCONFIG,pfnGetLinkState}
+ */
+static DECLCALLBACK(PDMNETWORKLINKSTATE) virtioNetR3NetworkConfig_GetLinkState(PPDMINETWORKCONFIG pInterface)
+{
+ PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, INetworkConfig);
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pThisCC->pDevIns, PVIRTIONET);
+
+ return IS_LINK_UP(pThis) ? PDMNETWORKLINKSTATE_UP : PDMNETWORKLINKSTATE_DOWN;
+}
+
+static int virtioNetR3DestroyWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
+{
+ Log10Func(("[%s]\n", pThis->szInst));
+ int rc = VINF_SUCCESS;
+ for (unsigned uIdxWorker = 0; uIdxWorker < pThis->cWorkers; uIdxWorker++)
+ {
+ PVIRTIONETWORKER pWorker = &pThis->aWorkers[uIdxWorker];
+ PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uIdxWorker];
+
+ if (pWorker->hEvtProcess != NIL_SUPSEMEVENT)
+ {
+ PDMDevHlpSUPSemEventClose(pDevIns, pWorker->hEvtProcess);
+ pWorker->hEvtProcess = NIL_SUPSEMEVENT;
+ }
+ if (pWorkerR3->pThread)
+ {
+ int rcThread;
+ rc = PDMDevHlpThreadDestroy(pDevIns, pWorkerR3->pThread, &rcThread);
+ if (RT_FAILURE(rc) || RT_FAILURE(rcThread))
+ AssertMsgFailed(("%s Failed to destroythread rc=%Rrc rcThread=%Rrc\n", __FUNCTION__, rc, rcThread));
+ pWorkerR3->pThread = NULL;
+ }
+ }
+ return rc;
+}
+
+/**
+ * Creates a worker for specified queue, along with semaphore to throttle the worker.
+ *
+ * @param pDevIns - PDM device instance
+ * @param pThis - virtio-net instance
+ * @param pWorker - Pointer to worker state
+ * @param pWorkerR3 - Pointer to worker state
+ * @param pVirtq - Pointer to virtq
+ */
+static int virtioNetR3CreateOneWorkerThread(PPDMDEVINS pDevIns, PVIRTIONET pThis,
+ PVIRTIONETWORKER pWorker, PVIRTIONETWORKERR3 pWorkerR3,
+ PVIRTIONETVIRTQ pVirtq)
+{
+ Log10Func(("[%s]\n", pThis->szInst));
+ RT_NOREF(pThis);
+
+ int rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pWorker->hEvtProcess);
+
+ if (RT_FAILURE(rc))
+ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+ N_("DevVirtioNET: Failed to create SUP event semaphore"));
+
+ LogFunc(("creating thread for queue %s\n", pVirtq->szName));
+
+ rc = PDMDevHlpThreadCreate(pDevIns, &pWorkerR3->pThread,
+ (void *)pWorker, virtioNetR3WorkerThread,
+ virtioNetR3WakeupWorker, 0, RTTHREADTYPE_IO, pVirtq->szName);
+ if (RT_FAILURE(rc))
+ return PDMDevHlpVMSetError(pDevIns, rc, RT_SRC_POS,
+ N_("Error creating thread for Virtual Virtq %s\n"), pVirtq->uIdx);
+
+ pWorker->fAssigned = true; /* Because worker's state in fixed-size array initialized w/empty slots */
+
+ LogFunc(("%s pThread: %p\n", pVirtq->szName, pWorkerR3->pThread));
+
+ return rc;
+}
+
+static int virtioNetR3CreateWorkerThreads(PPDMDEVINS pDevIns, PVIRTIONET pThis, PVIRTIONETCC pThisCC)
+{
+ Log10Func(("[%s]\n", pThis->szInst));
+ int rc;
+
+ /* Create the Control Queue worker anyway whether or not it is feature-negotiated or utilized by the guest.
+ * See related comment for queue construction in the device constructor function for more context.
+ */
+
+ PVIRTIONETVIRTQ pCtlVirtq = &pThis->aVirtqs[CTRLQIDX];
+ rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis,
+ &pThis->aWorkers[CTRLQIDX], &pThisCC->aWorkers[CTRLQIDX], pCtlVirtq);
+ AssertRCReturn(rc, rc);
+
+ pCtlVirtq->fHasWorker = true;
+
+ for (uint16_t uVirtqPair = pThis->cInitializedVirtqPairs; uVirtqPair < pThis->cVirtqPairs; uVirtqPair++)
+ {
+ PVIRTIONETVIRTQ pTxVirtq = &pThis->aVirtqs[TXQIDX(uVirtqPair)];
+ PVIRTIONETVIRTQ pRxVirtq = &pThis->aVirtqs[RXQIDX(uVirtqPair)];
+
+ rc = virtioNetR3CreateOneWorkerThread(pDevIns, pThis, &pThis->aWorkers[TXQIDX(uVirtqPair)],
+ &pThisCC->aWorkers[TXQIDX(uVirtqPair)], pTxVirtq);
+ AssertRCReturn(rc, rc);
+
+ pTxVirtq->fHasWorker = true;
+ pRxVirtq->fHasWorker = false;
+ }
+
+ if (pThis->cVirtqPairs > pThis->cInitializedVirtqPairs)
+ pThis->cInitializedVirtqPairs = pThis->cVirtqPairs;
+
+ pThis->cWorkers = pThis->cVirtqPairs + 1 /* One control virtq */;
+
+ return rc;
+}
+
+
+/**
+ * @callback_method_impl{FNPDMTHREADDEV}
+ */
+static DECLCALLBACK(int) virtioNetR3WorkerThread(PPDMDEVINS pDevIns, PPDMTHREAD pThread)
+{
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+ PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+ PVIRTIONETWORKER pWorker = (PVIRTIONETWORKER)pThread->pvUser;
+ PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[pWorker->uIdx];
+ uint16_t uIdx = pWorker->uIdx;
+
+ ASMAtomicWriteBool(&pWorker->fSleeping, false);
+
+ Assert(pWorker->uIdx == pVirtq->uIdx);
+
+ if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
+ return VINF_SUCCESS;
+
+ LogFunc(("[%s] worker thread idx=%d started for %s (virtq idx=%d)\n", pThis->szInst, pWorker->uIdx, pVirtq->szName, pVirtq->uIdx));
+
+ /** @todo Race w/guest enabling/disabling guest notifications cyclically.
+ See BugRef #8651, Comment #82 */
+ virtioCoreVirtqEnableNotify(&pThis->Virtio, uIdx, true /* fEnable */);
+
+ while ( pThread->enmState != PDMTHREADSTATE_TERMINATING
+ && pThread->enmState != PDMTHREADSTATE_TERMINATED)
+ {
+ if (IS_VIRTQ_EMPTY(pDevIns, &pThis->Virtio, pVirtq->uIdx))
+ {
+ /* Precisely coordinated atomic interlocks avoid a race condition that results in hung thread
+ * wherein a sloppily coordinated wake-up notification during a transition into or out
+ * of sleep leaves notifier and target mutually confused about actual & intended state.
+ */
+ ASMAtomicWriteBool(&pWorker->fSleeping, true);
+ bool fNotificationSent = ASMAtomicXchgBool(&pWorker->fNotified, false);
+ if (!fNotificationSent)
+ {
+ Log10Func(("[%s] %s worker sleeping...\n\n", pThis->szInst, pVirtq->szName));
+ Assert(ASMAtomicReadBool(&pWorker->fSleeping));
+
+ int rc = PDMDevHlpSUPSemEventWaitNoResume(pDevIns, pWorker->hEvtProcess, RT_INDEFINITE_WAIT);
+ STAM_COUNTER_INC(&pThis->StatTransmitByThread);
+ AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
+ if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
+ return VINF_SUCCESS;
+ if (rc == VERR_INTERRUPTED)
+ continue;
+ ASMAtomicWriteBool(&pWorker->fNotified, false);
+ }
+ ASMAtomicWriteBool(&pWorker->fSleeping, false);
+ }
+ /*
+ * Dispatch to the handler for the queue this worker is set up to drive
+ */
+ if (pVirtq->fCtlVirtq)
+ {
+ Log10Func(("[%s] %s worker woken. Fetching desc chain\n", pThis->szInst, pVirtq->szName));
+#ifdef VIRTIO_VBUF_ON_STACK
+ VIRTQBUF_T VirtqBuf;
+ PVIRTQBUF pVirtqBuf = &VirtqBuf;
+ int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pVirtq->uIdx, pVirtqBuf, true);
+#else /* !VIRTIO_VBUF_ON_STACK */
+ PVIRTQBUF pVirtqBuf = NULL;
+ int rc = virtioCoreR3VirtqAvailBufGet(pDevIns, &pThis->Virtio, pVirtq->uIdx, &pVirtqBuf, true);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+ if (rc == VERR_NOT_AVAILABLE)
+ {
+ Log10Func(("[%s] %s worker woken. Nothing found in queue\n", pThis->szInst, pVirtq->szName));
+ continue;
+ }
+ virtioNetR3Ctrl(pDevIns, pThis, pThisCC, pVirtqBuf);
+#ifndef VIRTIO_VBUF_ON_STACK
+ virtioCoreR3VirtqBufRelease(&pThis->Virtio, pVirtqBuf);
+#endif /* !VIRTIO_VBUF_ON_STACK */
+ }
+ else /* Must be Tx queue */
+ {
+ Log10Func(("[%s] %s worker woken. Virtq has data to transmit\n", pThis->szInst, pVirtq->szName));
+ virtioNetR3TransmitPkts(pDevIns, pThis, pThisCC, pVirtq, false /* fOnWorkerThread */);
+ }
+ /* Note: Surprise! Rx queues aren't handled by local worker threads. Instead, the PDM network leaf driver
+ * invokes PDMINETWORKDOWN.pfnWaitReceiveAvail() callback, which waits until woken by virtioNetVirtqNotified()
+ * indicating that guest IN buffers have been added to Rx virt queue.
+ */
+ }
+ Log10(("[%s] %s worker thread exiting\n", pThis->szInst, pVirtq->szName));
+ return VINF_SUCCESS;
+}
+
+/**
+ * @callback_method_impl{VIRTIOCORER3,pfnStatusChanged}
+ *
+ * Called back by the core code when VirtIO's ready state has changed.
+ */
+static DECLCALLBACK(void) virtioNetR3StatusChg(PVIRTIOCORE pVirtio, PVIRTIOCORECC pVirtioCC, uint32_t fVirtioReady)
+{
+ PVIRTIONET pThis = RT_FROM_MEMBER(pVirtio, VIRTIONET, Virtio);
+ PVIRTIONETCC pThisCC = RT_FROM_MEMBER(pVirtioCC, VIRTIONETCC, Virtio);
+
+ pThis->fVirtioReady = fVirtioReady;
+
+ if (fVirtioReady)
+ {
+#ifdef LOG_ENABLED
+ Log(("\n%-23s: %s *** VirtIO Ready ***\n\n", __FUNCTION__, pThis->szInst));
+ virtioCorePrintDeviceFeatures(&pThis->Virtio, NULL, s_aDevSpecificFeatures, RT_ELEMENTS(s_aDevSpecificFeatures));
+#endif
+ pThis->fResetting = false;
+ pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(pVirtio);
+ /* Now we can properly figure out the size of virtio header! */
+ virtioNetConfigurePktHdr(pThis, pThis->Virtio.fLegacyDriver);
+ pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
+
+ for (unsigned uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
+ {
+ PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
+ PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
+
+ Assert(pWorker->uIdx == uVirtqNbr);
+ RT_NOREF(pWorker);
+
+ Assert(pVirtq->uIdx == pWorker->uIdx);
+
+ (void) virtioCoreR3VirtqAttach(&pThis->Virtio, pVirtq->uIdx, pVirtq->szName);
+ pVirtq->fAttachedToVirtioCore = true;
+ if (IS_VIRTQ_EMPTY(pThisCC->pDevIns, &pThis->Virtio, pVirtq->uIdx))
+ virtioCoreVirtqEnableNotify(&pThis->Virtio, pVirtq->uIdx, true /* fEnable */);
+ }
+
+ virtioNetWakeupRxBufWaiter(pThisCC->pDevIns);
+ }
+ else
+ {
+ Log(("\n%-23s: %s VirtIO is resetting ***\n", __FUNCTION__, pThis->szInst));
+
+ pThis->virtioNetConfig.uStatus = pThis->fCableConnected ? VIRTIONET_F_LINK_UP : 0;
+ Log7(("%-23s: %s Link is %s\n", __FUNCTION__, pThis->szInst, pThis->fCableConnected ? "up" : "down"));
+
+ pThis->fPromiscuous = true;
+ pThis->fAllMulticast = false;
+ pThis->fAllUnicast = false;
+ pThis->fNoMulticast = false;
+ pThis->fNoUnicast = false;
+ pThis->fNoBroadcast = false;
+ pThis->uIsTransmitting = 0;
+ pThis->cUnicastFilterMacs = 0;
+ pThis->cMulticastFilterMacs = 0;
+
+ memset(pThis->aMacMulticastFilter, 0, sizeof(pThis->aMacMulticastFilter));
+ memset(pThis->aMacUnicastFilter, 0, sizeof(pThis->aMacUnicastFilter));
+ memset(pThis->aVlanFilter, 0, sizeof(pThis->aVlanFilter));
+
+ if (pThisCC->pDrv)
+ pThisCC->pDrv->pfnSetPromiscuousMode(pThisCC->pDrv, true);
+
+ for (uint16_t uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
+ {
+ virtioCoreR3VirtqDetach(&pThis->Virtio, uVirtqNbr);
+ pThis->aVirtqs[uVirtqNbr].fAttachedToVirtioCore = false;
+ }
+ }
+}
+
+/**
+ * @callback_method_impl{VIRTIOCORER3,pfnFeatureNegotiationComplete}
+ */
+static DECLCALLBACK(void) pfnFeatureNegotiationComplete(PVIRTIOCORE pVirtio, uint64_t fDriverFeatures, uint32_t fLegacy)
+{
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pVirtio->pDevInsR3, PVIRTIONET);
+
+ LogFunc(("[Feature Negotiation Complete] Guest Driver version is: %s\n", fLegacy ? "legacy" : "modern"));
+ virtioNetConfigurePktHdr(pThis, fLegacy);
+ virtioNetR3SetVirtqNames(pThis, fLegacy);
+
+ /* Senseless for modern guest to use control queue in this case. (See Note 1 in PDM-invoked device constructor) */
+ if (!fLegacy && !(fDriverFeatures & VIRTIONET_F_CTRL_VQ))
+ virtioNetR3VirtqDestroy(pVirtio, &pThis->aVirtqs[CTRLQIDX]);
+}
+
+#endif /* IN_RING3 */
+
+/**
+ * @interface_method_impl{PDMDEVREGR3,pfnDetach}
+ *
+ * The VM is suspended at this point.
+ */
+static DECLCALLBACK(void) virtioNetR3Detach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ RT_NOREF(fFlags);
+
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+ PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+
+ Log7Func(("[%s]\n", pThis->szInst));
+ RT_NOREF(pThis);
+
+ AssertLogRelReturnVoid(iLUN == 0);
+
+ pThisCC->pDrvBase = NULL;
+ pThisCC->pDrv = NULL;
+}
+
+/**
+ * @interface_method_impl{PDMDEVREGR3,pfnAttach}
+ *
+ * This is called when we change block driver.
+ */
+static DECLCALLBACK(int) virtioNetR3Attach(PPDMDEVINS pDevIns, unsigned iLUN, uint32_t fFlags)
+{
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+ PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+
+ Log7Func(("[%s]", pThis->szInst));
+ AssertLogRelReturn(iLUN == 0, VERR_PDM_NO_SUCH_LUN);
+
+ int rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
+ if (RT_SUCCESS(rc))
+ {
+ pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
+ AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
+ rc = VERR_PDM_MISSING_INTERFACE_BELOW);
+ }
+ else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
+ || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
+ {
+ /* This should never happen because this function is not called
+ * if there is no driver to attach! */
+ Log(("[%s] No attached driver!\n", pThis->szInst));
+ }
+
+ RT_NOREF2(pThis, fFlags);
+ return rc;
+}
+
+/**
+ * @interface_method_impl{PDMILEDPORTS,pfnQueryStatusLed}
+ */
+static DECLCALLBACK(int) virtioNetR3QueryStatusLed(PPDMILEDPORTS pInterface, unsigned iLUN, PPDMLED *ppLed)
+{
+ PVIRTIONETR3 pThisR3 = RT_FROM_MEMBER(pInterface, VIRTIONETR3, ILeds);
+ if (iLUN)
+ return VERR_PDM_LUN_NOT_FOUND;
+ *ppLed = &pThisR3->led;
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) virtioNetR3QueryInterface(struct PDMIBASE *pInterface, const char *pszIID)
+{
+ PVIRTIONETR3 pThisCC = RT_FROM_MEMBER(pInterface, VIRTIONETCC, IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThisCC->INetworkDown);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThisCC->INetworkConfig);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pThisCC->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDPORTS, &pThisCC->ILeds);
+ return NULL;
+}
+
+/**
+ * @interface_method_impl{PDMDEVREGR3,pfnReset}
+ */
+static DECLCALLBACK(void) virtioNetR3Reset(PPDMDEVINS pDevIns)
+{
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+ PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+
+ virtioCoreR3ResetDevice(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
+}
+
+/**
+ * @interface_method_impl{PDMDEVREGR3,pfnDestruct}
+ */
+static DECLCALLBACK(int) virtioNetR3Destruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN_QUIET(pDevIns);
+
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+ PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+
+ Log(("[%s] Destroying instance\n", pThis->szInst));
+ if (pThis->hEventRxDescAvail != NIL_SUPSEMEVENT)
+ {
+ PDMDevHlpSUPSemEventSignal(pDevIns, pThis->hEventRxDescAvail);
+ PDMDevHlpSUPSemEventClose(pDevIns, pThis->hEventRxDescAvail);
+ pThis->hEventRxDescAvail = NIL_SUPSEMEVENT;
+ }
+
+ virtioNetR3DestroyWorkerThreads(pDevIns, pThis, pThisCC);
+ virtioCoreR3Term(pDevIns, &pThis->Virtio, &pThisCC->Virtio);
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMDEVREGR3,pfnConstruct}
+ *
+ * Notes about revising originally VirtIO 1.0+ only virtio-net device emulator to be "transitional",
+ * a VirtIO term meaning this now interoperates with both "legacy" (e.g. pre-1.0) and "modern" (1.0+)
+ * guest virtio-net drivers. The changes include migrating VMs saved using prior DevVirtioNet.cpp (0.95)
+ * saveExec/loadExec semantics to use 1.0 save/load semantics.
+ *
+ * Regardless of the 1.0 spec's overall helpful guidance for implementing transitional devices,
+ * A bit is left to the imagination, e.g. some things have to be determined deductively
+ * (AKA "the hard way").
+ *
+ * Case in point: According to VirtIO 0.95 ("legacy") specification, section 2.2.1, "historically"
+ * drivers may start driving prior to feature negotiation and prior to drivers setting DRIVER_OK
+ * status, "provided driver doesn't use features that alter early use of this device". Interpreted
+ * here to mean a virtio-net driver must respect default settings (such as implicit pkt header default
+ * size, as determined per Note 1 below).
+ *
+ * ----------------------------------------------------------------------------------------------
+ * Transitional device initialization Note 1: Identifying default value for network Rx pkt hdr size.
+ * (VirtIO 1.0 specification section 5.1.6.1)
+ *
+ * Guest virtio legacy drivers may begin operations prematurely, regardless of early spec's
+ * initialization sequence (see note 2 below). Legacy drivers implicitly default to using the
+ * (historically) shortest-length network packet header *unless* VIRTIONET_F_MRG_RXBUF feature is
+ * negotiated. If feature negotiation phase is [optionally] enacted by a legacy guest (i.e. we strictly
+ * enforce full initialization protocol for modern guests), virtioNetConfigurePktHdr() is invoked again to
+ * finalize device's network packet header size. Best-guess at default packet header size is deduced, e.g.
+ * isn't documented, as follows: A legacy guest with VIRTIONET_F_MRG_RXBUF not-yet-negotiated is the only
+ * case where network I/O could possibly occur with any reasonable assumption about packet type/size,
+ * because logically other permutations couldn't possibly be inferred until feature negotiation
+ * is complete. Specifically, those cases are:
+ *
+ * 1. A modern driver (detected only when VIRTIONET_F_VERSION_1 feature is ack'd by guest, and,
+ * simultaneously, VIRTIONET_F_MRG_RXBUF feature is accepted or declined (determining network receive-packet
+ * processing behavior).
+ *
+ * 2. A legacy driver that has agreed to use VIRTIONET_F_MRG_RXBUF feature, resulting in a two-byte larger pkt hdr,
+ * (as well as deciding Rx packet processing behavior).
+ *
+ * ----------------------------------------------------------------------------------------------
+ * Transitional device initialization Note 2: Creating unnegotiated control queue.
+ * (VirtIO 1.0 spec, sections 5.1.5 and 5.1.6.5)
+ *
+ * Create all queues immediately, prior to feature negotiation, including control queue (irrespective
+ * of the fact it's too early in initialization for control feature to be approved by guest). This
+ * transitional device must deal with legacy guests which *can* (and on linux have been seen to) use
+ * the control queue prior to feature negotiation.
+ *
+ * The initial assumption is *modern" guest virtio-net drivers out in the wild could never reasonably
+ * attempt something as obviously risky as using ctrlq without first acking VIRTIO_NET_F_CTRL_VQ
+ * feature to establish it. For now, we create the control queue proactively to accomodate a potentially
+ * badly behaved but officially sanctioned legacy virtio-net driver, but *destroy* that same queue
+ * if a driver announces as 'modern' during feature finalization yet leaves VIRTIO_NET_F_CTRL_VQ un-ack'd.
+ */
+static DECLCALLBACK(int) virtioNetR3Construct(PPDMDEVINS pDevIns, int iInstance, PCFGMNODE pCfg)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+ PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+ PCPDMDEVHLPR3 pHlp = pDevIns->pHlpR3;
+
+ /*
+ * Quickly initialize state data to ensure destructor always works.
+ */
+ Log7Func(("PDM device instance: %d\n", iInstance));
+ RTStrPrintf(pThis->szInst, sizeof(pThis->szInst), "virtio-net #%d", iInstance);
+
+ pThisCC->pDevIns = pDevIns;
+ pThisCC->IBase.pfnQueryInterface = virtioNetR3QueryInterface;
+ pThisCC->ILeds.pfnQueryStatusLed = virtioNetR3QueryStatusLed;
+ pThisCC->led.u32Magic = PDMLED_MAGIC;
+
+ /* Interfaces */
+ pThisCC->INetworkDown.pfnWaitReceiveAvail = virtioNetR3NetworkDown_WaitReceiveAvail;
+ pThisCC->INetworkDown.pfnReceive = virtioNetR3NetworkDown_Receive;
+ pThisCC->INetworkDown.pfnReceiveGso = virtioNetR3NetworkDown_ReceiveGso;
+ pThisCC->INetworkDown.pfnXmitPending = virtioNetR3NetworkDown_XmitPending;
+ pThisCC->INetworkConfig.pfnGetMac = virtioNetR3NetworkConfig_GetMac;
+ pThisCC->INetworkConfig.pfnGetLinkState = virtioNetR3NetworkConfig_GetLinkState;
+ pThisCC->INetworkConfig.pfnSetLinkState = virtioNetR3NetworkConfig_SetLinkState;
+
+ pThis->hEventRxDescAvail = NIL_SUPSEMEVENT;
+
+ /*
+ * Validate configuration.
+ */
+ PDMDEV_VALIDATE_CONFIG_RETURN(pDevIns, "MAC|CableConnected|LineSpeed|LinkUpDelay|StatNo|Legacy", "");
+
+ /* Get config params */
+ int rc = pHlp->pfnCFGMQueryBytes(pCfg, "MAC", pThis->macConfigured.au8, sizeof(pThis->macConfigured));
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get MAC address"));
+
+ rc = pHlp->pfnCFGMQueryBool(pCfg, "CableConnected", &pThis->fCableConnected);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'CableConnected'"));
+
+ uint32_t uStatNo = iInstance;
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "StatNo", &uStatNo, iInstance);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the \"StatNo\" value"));
+
+ rc = pHlp->pfnCFGMQueryU32Def(pCfg, "LinkUpDelay", &pThis->cMsLinkUpDelay, 5000); /* ms */
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Configuration error: Failed to get the value of 'LinkUpDelay'"));
+
+ Assert(pThis->cMsLinkUpDelay <= 300000); /* less than 5 minutes */
+
+ if (pThis->cMsLinkUpDelay > 5000 || pThis->cMsLinkUpDelay < 100)
+ LogRel(("%s WARNING! Link up delay is set to %u seconds!\n",
+ pThis->szInst, pThis->cMsLinkUpDelay / 1000));
+
+ Log(("[%s] Link up delay is set to %u seconds\n", pThis->szInst, pThis->cMsLinkUpDelay / 1000));
+
+ /* Copy the MAC address configured for the VM to the MMIO accessible Virtio dev-specific config area */
+ memcpy(pThis->virtioNetConfig.uMacAddress.au8, pThis->macConfigured.au8, sizeof(pThis->virtioNetConfig.uMacAddress)); /* TBD */
+
+ Log(("Using MAC address for %s: %2x:%2x:%2x:%2x:%2x:%2x\n", pThis->szInst,
+ pThis->macConfigured.au8[0], pThis->macConfigured.au8[1], pThis->macConfigured.au8[2],
+ pThis->macConfigured.au8[3], pThis->macConfigured.au8[4], pThis->macConfigured.au8[5]));
+
+ LogFunc(("RC=%RTbool R0=%RTbool\n", pDevIns->fRCEnabled, pDevIns->fR0Enabled));
+
+ /*
+ * Configure Virtio core (generic Virtio queue and infrastructure management) parameters.
+ */
+# if FEATURE_OFFERED(STATUS)
+ pThis->virtioNetConfig.uStatus = 0;
+# endif
+
+ pThis->virtioNetConfig.uMaxVirtqPairs = VIRTIONET_MAX_QPAIRS;
+ pThisCC->Virtio.pfnFeatureNegotiationComplete = pfnFeatureNegotiationComplete;
+ pThisCC->Virtio.pfnVirtqNotified = virtioNetVirtqNotified;
+ pThisCC->Virtio.pfnStatusChanged = virtioNetR3StatusChg;
+ pThisCC->Virtio.pfnDevCapRead = virtioNetR3DevCapRead;
+ pThisCC->Virtio.pfnDevCapWrite = virtioNetR3DevCapWrite;
+
+ VIRTIOPCIPARAMS VirtioPciParams;
+ VirtioPciParams.uDeviceId = PCI_DEVICE_ID_VIRTIONET_HOST;
+ VirtioPciParams.uClassBase = VBOX_PCI_CLASS_NETWORK;
+ VirtioPciParams.uClassSub = VBOX_PCI_SUB_NETWORK_ETHERNET;
+ VirtioPciParams.uClassProg = PCI_CLASS_PROG_UNSPECIFIED;
+ VirtioPciParams.uSubsystemId = DEVICE_PCI_NETWORK_SUBSYSTEM; /* VirtIO 1.0 allows PCI Device ID here */
+ VirtioPciParams.uInterruptLine = 0x00;
+ VirtioPciParams.uInterruptPin = 0x01;
+
+ /* Create semaphore used to synchronize/throttle the downstream LUN's Rx waiter thread. */
+ rc = PDMDevHlpSUPSemEventCreate(pDevIns, &pThis->hEventRxDescAvail);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create event semaphore"));
+
+ pThis->fOfferLegacy = VIRTIONET_TRANSITIONAL_ENABLE_FLAG;
+ virtioNetConfigurePktHdr(pThis, pThis->fOfferLegacy); /* set defaults */
+
+ /* Initialize VirtIO core. (*pfnStatusChanged)() callback occurs when both host VirtIO core & guest driver are ready) */
+ rc = virtioCoreR3Init(pDevIns, &pThis->Virtio, &pThisCC->Virtio, &VirtioPciParams, pThis->szInst,
+ VIRTIONET_HOST_FEATURES_OFFERED, pThis->fOfferLegacy,
+ &pThis->virtioNetConfig /*pvDevSpecificCap*/, sizeof(pThis->virtioNetConfig));
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: failed to initialize VirtIO"));
+
+ pThis->fNegotiatedFeatures = virtioCoreGetNegotiatedFeatures(&pThis->Virtio);
+ /** @todo validating features at this point is most probably pointless, as the negotiation hasn't started yet. */
+ if (!virtioNetValidateRequiredFeatures(pThis->fNegotiatedFeatures))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("virtio-net: Required features not successfully negotiated."));
+ pThis->cVirtqPairs = pThis->virtioNetConfig.uMaxVirtqPairs;
+ pThis->cVirtqs += pThis->cVirtqPairs * 2 + 1;
+ pThis->aVirtqs[CTRLQIDX].fCtlVirtq = true;
+
+ virtioNetR3SetVirtqNames(pThis, pThis->fOfferLegacy);
+ for (unsigned uVirtqNbr = 0; uVirtqNbr < pThis->cVirtqs; uVirtqNbr++)
+ {
+ PVIRTIONETVIRTQ pVirtq = &pThis->aVirtqs[uVirtqNbr];
+ PVIRTIONETWORKER pWorker = &pThis->aWorkers[uVirtqNbr];
+ PVIRTIONETWORKERR3 pWorkerR3 = &pThisCC->aWorkers[uVirtqNbr];
+ pVirtq->uIdx = pWorker->uIdx = pWorkerR3->uIdx = uVirtqNbr;
+ }
+ /*
+ * Create queue workers for life of instance. (I.e. they persist through VirtIO bounces)
+ */
+ rc = virtioNetR3CreateWorkerThreads(pDevIns, pThis, pThisCC);
+ if (RT_FAILURE(rc))
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to create worker threads"));
+
+ /* Create Link Up Timer */
+ rc = PDMDevHlpTimerCreate(pDevIns, TMCLOCK_VIRTUAL, virtioNetR3LinkUpTimer, NULL,
+ TMTIMER_FLAGS_NO_CRIT_SECT | TMTIMER_FLAGS_NO_RING0,
+ "VirtioNet Link Up", &pThisCC->hLinkUpTimer);
+ /*
+ * Attach network driver instance
+ */
+ rc = PDMDevHlpDriverAttach(pDevIns, 0, &pThisCC->IBase, &pThisCC->pDrvBase, "Network Port");
+ if (RT_SUCCESS(rc))
+ {
+ pThisCC->pDrv = PDMIBASE_QUERY_INTERFACE(pThisCC->pDrvBase, PDMINETWORKUP);
+ AssertMsgStmt(pThisCC->pDrv, ("Failed to obtain the PDMINETWORKUP interface!\n"),
+ rc = VERR_PDM_MISSING_INTERFACE_BELOW);
+ }
+ else if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
+ || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
+ {
+ /* No error! */
+ Log(("[%s] No attached driver!\n", pThis->szInst));
+ }
+ else
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the network LUN"));
+ /*
+ * Status driver
+ */
+ PPDMIBASE pUpBase;
+ rc = PDMDevHlpDriverAttach(pDevIns, PDM_STATUS_LUN, &pThisCC->IBase, &pUpBase, "Status Port");
+ if (RT_FAILURE(rc) && rc != VERR_PDM_NO_ATTACHED_DRIVER)
+ return PDMDEV_SET_ERROR(pDevIns, rc, N_("Failed to attach the status LUN"));
+
+ pThisCC->pLedsConnector = PDMIBASE_QUERY_INTERFACE(pUpBase, PDMILEDCONNECTORS);
+ /*
+ * Register saved state.
+ */
+ rc = PDMDevHlpSSMRegisterEx(pDevIns, VIRTIONET_SAVEDSTATE_VERSION, sizeof(*pThis), NULL,
+ NULL, NULL, NULL, /** @todo r=aeichner Teleportation? */
+ NULL, virtioNetR3ModernSaveExec, NULL,
+ NULL, virtioNetR3ModernLoadExec, virtioNetR3ModernLoadDone);
+ AssertRCReturn(rc, rc);
+ /*
+ * Statistics and debug stuff.
+ * The /Public/ bits are official and used by session info in the GUI.
+ */
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
+ "Amount of data received", "/Public/NetAdapter/%u/BytesReceived", uStatNo);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES,
+ "Amount of data transmitted", "/Public/NetAdapter/%u/BytesTransmitted", uStatNo);
+ PDMDevHlpSTAMRegisterF(pDevIns, &pDevIns->iInstance, STAMTYPE_U32, STAMVISIBILITY_ALWAYS, STAMUNIT_NONE,
+ "Device instance number", "/Public/NetAdapter/%u/%s", uStatNo, pDevIns->pReg->szName);
+
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveBytes, STAMTYPE_COUNTER, "ReceiveBytes", STAMUNIT_BYTES, "Amount of data received");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitBytes, STAMTYPE_COUNTER, "TransmitBytes", STAMUNIT_BYTES, "Amount of data transmitted");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveGSO, STAMTYPE_COUNTER, "Packets/ReceiveGSO", STAMUNIT_COUNT, "Number of received GSO packets");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitPackets, STAMTYPE_COUNTER, "Packets/Transmit", STAMUNIT_COUNT, "Number of sent packets");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitGSO, STAMTYPE_COUNTER, "Packets/Transmit-Gso", STAMUNIT_COUNT, "Number of sent GSO packets");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitCSum, STAMTYPE_COUNTER, "Packets/Transmit-Csum", STAMUNIT_COUNT, "Number of completed TX checksums");
+# ifdef VBOX_WITH_STATISTICS
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceive, STAMTYPE_PROFILE, "Receive/Total", STAMUNIT_TICKS_PER_CALL, "Profiling receive");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatReceiveStore, STAMTYPE_PROFILE, "Receive/Store", STAMUNIT_TICKS_PER_CALL, "Profiling receive storing");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflow, STAMTYPE_PROFILE, "RxOverflow", STAMUNIT_TICKS_PER_OCCURENCE, "Profiling RX overflows");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatRxOverflowWakeup, STAMTYPE_COUNTER, "RxOverflowWakeup", STAMUNIT_OCCURENCES, "Nr of RX overflow wakeups");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmit, STAMTYPE_PROFILE, "Transmit/Total", STAMUNIT_TICKS_PER_CALL, "Profiling transmits in HC");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitSend, STAMTYPE_PROFILE, "Transmit/Send", STAMUNIT_TICKS_PER_CALL, "Profiling send transmit in HC");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByNetwork, STAMTYPE_COUNTER, "Transmit/ByNetwork", STAMUNIT_COUNT, "Network-initiated transmissions");
+ PDMDevHlpSTAMRegister(pDevIns, &pThis->StatTransmitByThread, STAMTYPE_COUNTER, "Transmit/ByThread", STAMUNIT_COUNT, "Thread-initiated transmissions");
+# endif
+ /*
+ * Register the debugger info callback (ignore errors).
+ */
+ char szTmp[128];
+ rc = PDMDevHlpDBGFInfoRegister(pDevIns, "virtio-net", "Display virtio-net info (help, net, features, state, pointers, queues, all)", virtioNetR3Info);
+ if (RT_FAILURE(rc))
+ LogRel(("Failed to register DBGF info for device %s\n", szTmp));
+ return rc;
+}
+
+#else /* !IN_RING3 */
+
+/**
+ * @callback_method_impl{PDMDEVREGR0,pfnConstruct}
+ */
+static DECLCALLBACK(int) virtioNetRZConstruct(PPDMDEVINS pDevIns)
+{
+ PDMDEV_CHECK_VERSIONS_RETURN(pDevIns);
+ PVIRTIONET pThis = PDMDEVINS_2_DATA(pDevIns, PVIRTIONET);
+ PVIRTIONETCC pThisCC = PDMDEVINS_2_DATA_CC(pDevIns, PVIRTIONETCC);
+ pThisCC->Virtio.pfnVirtqNotified = virtioNetVirtqNotified;
+ return virtioCoreRZInit(pDevIns, &pThis->Virtio);
+}
+
+#endif /* !IN_RING3 */
+
+/**
+ * The device registration structure.
+ */
+const PDMDEVREG g_DeviceVirtioNet =
+{
+ /* .uVersion = */ PDM_DEVREG_VERSION,
+ /* .uReserved0 = */ 0,
+ /* .szName = */ "virtio-net",
+ /* .fFlags = */ PDM_DEVREG_FLAGS_DEFAULT_BITS | PDM_DEVREG_FLAGS_NEW_STYLE | PDM_DEVREG_FLAGS_RZ,
+ /* .fClass = */ PDM_DEVREG_CLASS_NETWORK,
+ /* .cMaxInstances = */ ~0U,
+ /* .uSharedVersion = */ 42,
+ /* .cbInstanceShared = */ sizeof(VIRTIONET),
+ /* .cbInstanceCC = */ sizeof(VIRTIONETCC),
+ /* .cbInstanceRC = */ sizeof(VIRTIONETRC),
+ /* .cMaxPciDevices = */ 1,
+ /* .cMaxMsixVectors = */ VBOX_MSIX_MAX_ENTRIES,
+ /* .pszDescription = */ "Virtio Host NET.\n",
+#if defined(IN_RING3)
+ /* .pszRCMod = */ "VBoxDDRC.rc",
+ /* .pszR0Mod = */ "VBoxDDR0.r0",
+ /* .pfnConstruct = */ virtioNetR3Construct,
+ /* .pfnDestruct = */ virtioNetR3Destruct,
+ /* .pfnRelocate = */ NULL,
+ /* .pfnMemSetup = */ NULL,
+ /* .pfnPowerOn = */ NULL,
+ /* .pfnReset = */ virtioNetR3Reset,
+ /* .pfnSuspend = */ virtioNetWakeupRxBufWaiter,
+ /* .pfnResume = */ NULL,
+ /* .pfnAttach = */ virtioNetR3Attach,
+ /* .pfnDetach = */ virtioNetR3Detach,
+ /* .pfnQueryInterface = */ NULL,
+ /* .pfnInitComplete = */ NULL,
+ /* .pfnPowerOff = */ virtioNetWakeupRxBufWaiter,
+ /* .pfnSoftReset = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RING0)
+ /* .pfnEarlyConstruct = */ NULL,
+ /* .pfnConstruct = */ virtioNetRZConstruct,
+ /* .pfnDestruct = */ NULL,
+ /* .pfnFinalDestruct = */ NULL,
+ /* .pfnRequest = */ NULL,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#elif defined(IN_RC)
+ /* .pfnConstruct = */ virtioNetRZConstruct,
+ /* .pfnReserved0 = */ NULL,
+ /* .pfnReserved1 = */ NULL,
+ /* .pfnReserved2 = */ NULL,
+ /* .pfnReserved3 = */ NULL,
+ /* .pfnReserved4 = */ NULL,
+ /* .pfnReserved5 = */ NULL,
+ /* .pfnReserved6 = */ NULL,
+ /* .pfnReserved7 = */ NULL,
+#else
+# error "Not in IN_RING3, IN_RING0 or IN_RC!"
+#endif
+ /* .uVersionEnd = */ PDM_DEVREG_VERSION
+};
+
diff --git a/src/VBox/Devices/Network/DrvCloudTunnel.cpp b/src/VBox/Devices/Network/DrvCloudTunnel.cpp
new file mode 100644
index 00000000..458d89b9
--- /dev/null
+++ b/src/VBox/Devices/Network/DrvCloudTunnel.cpp
@@ -0,0 +1,1825 @@
+/* $Id: DrvCloudTunnel.cpp $ */
+/** @file
+ * DrvCloudTunnel - Cloud tunnel network transport driver
+ *
+ * Based on code contributed by Christophe Devriese
+ */
+
+/*
+ * Copyright (C) 2022-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_CTUN
+#include <VBox/log.h>
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmnetifs.h>
+#include <VBox/vmm/pdmnetinline.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/uuid.h>
+#include <iprt/req.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/critsect.h>
+
+#include "VBoxDD.h"
+
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+typedef int socklen_t;
+#else
+# include <errno.h>
+ typedef int SOCKET;
+# define closesocket close
+# define INVALID_SOCKET -1
+# define SOCKET_ERROR -1
+DECLINLINE(int) WSAGetLastError() { return errno; }
+#endif
+
+/* Prevent inclusion of Winsock2.h */
+#define _WINSOCK2API_
+#include <libssh/libssh.h>
+#include <libssh/callbacks.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Cloud tunnel driver instance data.
+ *
+ * @implements PDMINETWORKUP
+ */
+typedef struct DRVCLOUDTUNNEL
+{
+ /** The network interface. */
+ PDMINETWORKUP INetworkUp;
+ /** The network interface. */
+ PPDMINETWORKDOWN pIAboveNet;
+ /** Pointer to the driver instance. */
+ PPDMDRVINS pDrvIns;
+ /** Cloud instance private key. */
+ ssh_key SshKey;
+ /** Cloud instance user. */
+ char *pszUser;
+ /** Cloud instance primary IP address. */
+ char *pszPrimaryIP;
+ /** Cloud instance primary IP address. */
+ char *pszSecondaryIP;
+ /** MAC address to set on cloud primary interface. */
+ RTMAC targetMac;
+ /** SSH connection timeout in seconds. */
+ long ulTimeoutInSecounds;
+
+ /** Primary proxy type. */
+ char *pszPrimaryProxyType;
+ /** Primary proxy server IP address. */
+ char *pszPrimaryProxyHost;
+ /** Primary proxy server port. */
+ uint16_t u16PrimaryProxyPort;
+ /** Primary proxy user. */
+ char *pszPrimaryProxyUser;
+ /** Primary proxy password. */
+ char *pszPrimaryProxyPassword;
+
+ /** Secondary proxy type. */
+ char *pszSecondaryProxyType;
+ /** Secondary proxy server IP address. */
+ char *pszSecondaryProxyHost;
+ /** Secondary proxy server port. */
+ uint16_t u16SecondaryProxyPort;
+ /** Secondary proxy user. */
+ char *pszSecondaryProxyUser;
+ /** Secondary proxy password. */
+ char *pszSecondaryProxyPassword;
+
+ /** Cloud tunnel instance string. */
+ char *pszInstance;
+ /** Cloud tunnel I/O thread unique name. */
+ char *pszInstanceIo;
+ /** Cloud tunnel device thread unique name. */
+ char *pszInstanceDev;
+
+ /** Command assembly buffer. */
+ char *pszCommandBuffer;
+ /** Command output buffer. */
+ char *pszOutputBuffer;
+ /** Name of primary interface of cloud instance. */
+ char *pszCloudPrimaryInterface;
+
+ /** Cloud destination address. */
+ RTNETADDR DestAddress;
+ /** Transmit lock used by drvCloudTunnelUp_BeginXmit. */
+ RTCRITSECT XmitLock;
+ /** Server data structure for Cloud communication. */
+// PRTCLOUDSERVER pServer;
+
+ /** RX thread for delivering packets to attached device. */
+ PPDMTHREAD pDevThread;
+ /** Queue for device-thread requests. */
+ RTREQQUEUE hDevReqQueue;
+ /** I/O thread for tunnel channel. */
+ PPDMTHREAD pIoThread;
+ /** Queue for I/O-thread requests. */
+ RTREQQUEUE hIoReqQueue;
+ /** I/O thread notification socket pair (in). */
+ SOCKET iSocketIn;
+ /** I/O thread notification socket pair (out). */
+ SOCKET iSocketOut;
+
+ /** SSH private key. */
+
+ /** SSH Log Verbosity: 0 - No log, 1 - warnings, 2 - protocol, 3 - packet, 4 - functions */
+ int iSshVerbosity;
+ /** SSH Session. */
+ ssh_session pSshSession;
+ /** SSH Tunnel Channel. */
+ ssh_channel pSshChannel;
+ /** SSH Packet Receive Callback Structure. */
+ struct ssh_channel_callbacks_struct Callbacks;
+
+ /** Flag whether the link is down. */
+ bool volatile fLinkDown;
+
+#ifdef VBOX_WITH_STATISTICS
+ /** Number of sent packets. */
+ STAMCOUNTER StatPktSent;
+ /** Number of sent bytes. */
+ STAMCOUNTER StatPktSentBytes;
+ /** Number of received packets. */
+ STAMCOUNTER StatPktRecv;
+ /** Number of received bytes. */
+ STAMCOUNTER StatPktRecvBytes;
+ /** Profiling packet transmit runs. */
+ STAMPROFILEADV StatTransmit;
+ /** Profiling packet receive runs. */
+ STAMPROFILEADV StatReceive;
+ /** Profiling packet receive device (both actual receive and waiting). */
+ STAMPROFILE StatDevRecv;
+ /** Profiling packet receive device waiting. */
+ STAMPROFILE StatDevRecvWait;
+#endif /* VBOX_WITH_STATISTICS */
+
+#ifdef LOG_ENABLED
+ /** The nano ts of the last transfer. */
+ uint64_t u64LastTransferTS;
+ /** The nano ts of the last receive. */
+ uint64_t u64LastReceiveTS;
+#endif
+} DRVCLOUDTUNNEL, *PDRVCLOUDTUNNEL;
+
+
+/** Converts a pointer to CLOUDTUNNEL::INetworkUp to a PRDVCLOUDTUNNEL. */
+#define PDMINETWORKUP_2_DRVCLOUDTUNNEL(pInterface) ( (PDRVCLOUDTUNNEL)((uintptr_t)pInterface - RT_UOFFSETOF(DRVCLOUDTUNNEL, INetworkUp)) )
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
+ */
+static DECLCALLBACK(int) drvCloudTunnelUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
+{
+ RT_NOREF(fOnWorkerThread);
+ PDRVCLOUDTUNNEL pThis = PDMINETWORKUP_2_DRVCLOUDTUNNEL(pInterface);
+ int rc = RTCritSectTryEnter(&pThis->XmitLock);
+ if (RT_FAILURE(rc))
+ {
+ /** @todo XMIT thread */
+ rc = VERR_TRY_AGAIN;
+ }
+ return rc;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
+ */
+static DECLCALLBACK(int) drvCloudTunnelUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
+ PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
+{
+ PDRVCLOUDTUNNEL pThis = PDMINETWORKUP_2_DRVCLOUDTUNNEL(pInterface);
+ Assert(RTCritSectIsOwner(&pThis->XmitLock)); NOREF(pThis);
+
+ /*
+ * Allocate a scatter / gather buffer descriptor that is immediately
+ * followed by the buffer space of its single segment. The GSO context
+ * comes after that again.
+ */
+ PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAlloc( RT_ALIGN_Z(sizeof(*pSgBuf), 16)
+ + RT_ALIGN_Z(cbMin, 16)
+ + (pGso ? RT_ALIGN_Z(sizeof(*pGso), 16) : 0));
+ if (!pSgBuf)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize the S/G buffer and return.
+ */
+ pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
+ pSgBuf->cbUsed = 0;
+ pSgBuf->cbAvailable = RT_ALIGN_Z(cbMin, 16);
+ pSgBuf->pvAllocator = NULL;
+ if (!pGso)
+ pSgBuf->pvUser = NULL;
+ else
+ {
+ pSgBuf->pvUser = (uint8_t *)(pSgBuf + 1) + pSgBuf->cbAvailable;
+ *(PPDMNETWORKGSO)pSgBuf->pvUser = *pGso;
+ }
+ pSgBuf->cSegs = 1;
+ pSgBuf->aSegs[0].cbSeg = pSgBuf->cbAvailable;
+ pSgBuf->aSegs[0].pvSeg = pSgBuf + 1;
+
+#if 0 /* poison */
+ memset(pSgBuf->aSegs[0].pvSeg, 'F', pSgBuf->aSegs[0].cbSeg);
+#endif
+ *ppSgBuf = pSgBuf;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
+ */
+static DECLCALLBACK(int) drvCloudTunnelUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
+{
+ PDRVCLOUDTUNNEL pThis = PDMINETWORKUP_2_DRVCLOUDTUNNEL(pInterface);
+ Assert(RTCritSectIsOwner(&pThis->XmitLock)); NOREF(pThis);
+ if (pSgBuf)
+ {
+ Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
+ pSgBuf->fFlags = 0;
+ RTMemFree(pSgBuf);
+ }
+ return VINF_SUCCESS;
+}
+
+static int createConnectedSockets(PDRVCLOUDTUNNEL pThis)
+{
+ LogFlow(("%s: creating a pair of connected sockets...\n", pThis->pszInstance));
+ struct sockaddr_in inaddr;
+ struct sockaddr addr;
+ SOCKET lst = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+ memset(&inaddr, 0, sizeof(inaddr));
+ memset(&addr, 0, sizeof(addr));
+ inaddr.sin_family = AF_INET;
+ inaddr.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ inaddr.sin_port = 0;
+ int yes = 1;
+ setsockopt(lst, SOL_SOCKET, SO_REUSEADDR, (char*)&yes, sizeof(yes));
+ bind(lst, (struct sockaddr *)&inaddr, sizeof(inaddr));
+ listen(lst, 1);
+ socklen_t len=sizeof(inaddr);
+ getsockname(lst, &addr, &len);
+ pThis->iSocketOut = socket(AF_INET, SOCK_STREAM, 0);
+ connect(pThis->iSocketOut, &addr, len);
+ pThis->iSocketIn = accept(lst, 0, 0);
+ closesocket(lst);
+ Log2(("%s: socket(%d) <= socket(%d) created successfully.\n", pThis->pszInstance, pThis->iSocketIn, pThis->iSocketOut));
+ return VINF_SUCCESS;
+}
+
+
+static void destroyConnectedSockets(PDRVCLOUDTUNNEL pThis)
+{
+ if (pThis->iSocketOut != INVALID_SOCKET)
+ {
+ LogFlow(("%s: destroying output socket (%d)...\n", pThis->pszInstance, pThis->iSocketOut));
+ closesocket(pThis->iSocketOut);
+ }
+ if (pThis->iSocketIn != INVALID_SOCKET)
+ {
+ LogFlow(("%s: destroying input socket (%d)...\n", pThis->pszInstance, pThis->iSocketIn));
+ closesocket(pThis->iSocketIn);
+ }
+}
+
+
+DECLINLINE(void) drvCloudTunnelFreeSgBuf(PDRVCLOUDTUNNEL pThis, PPDMSCATTERGATHER pSgBuf)
+{
+ RT_NOREF(pThis);
+ RTMemFree(pSgBuf);
+}
+
+DECLINLINE(void) drvCloudTunnelNotifyIoThread(PDRVCLOUDTUNNEL pThis, const char *pszWho)
+{
+ RT_NOREF(pszWho);
+ int cBytes = send(pThis->iSocketOut, " ", 1, 0);
+ if (cBytes == SOCKET_ERROR)
+ LogRel(("Failed to send a signalling packet, error code %d", WSAGetLastError())); // @todo!
+
+}
+
+
+/**
+ * Worker function for sending packets on I/O thread.
+ *
+ * @param pThis Pointer to the cloud tunnel instance.
+ * @param pSgBuf The scatter/gather buffer.
+ * @thread I/O
+ */
+static DECLCALLBACK(void) drvCloudTunnelSendWorker(PDRVCLOUDTUNNEL pThis, PPDMSCATTERGATHER pSgBuf)
+{
+ // int rc = VINF_SUCCESS;
+ if (!pSgBuf->pvUser)
+ {
+#ifdef LOG_ENABLED
+ uint64_t u64Now = RTTimeProgramNanoTS();
+ LogFunc(("%-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
+ pSgBuf->cbUsed, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS));
+ pThis->u64LastTransferTS = u64Now;
+#endif
+ Log2(("writing to tunnel channel: pSgBuf->aSegs[0].pvSeg=%p pSgBuf->cbUsed=%#x\n%.*Rhxd\n",
+ pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, pSgBuf->cbUsed, pSgBuf->aSegs[0].pvSeg));
+
+ int cBytes = ssh_channel_write(pThis->pSshChannel, pSgBuf->aSegs[0].pvSeg, (uint32_t)pSgBuf->cbUsed);
+ if (cBytes == SSH_ERROR)
+ LogRel(("%s: ssh_channel_write failed\n", pThis->pszInstance));
+ }
+ else
+ {
+ uint8_t abHdrScratch[256];
+ uint8_t const *pbFrame = (uint8_t const *)pSgBuf->aSegs[0].pvSeg;
+ PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pSgBuf->pvUser;
+ uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, pSgBuf->cbUsed); Assert(cSegs > 1);
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ uint32_t cbSegFrame;
+ void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)pbFrame, pSgBuf->cbUsed, abHdrScratch,
+ iSeg, cSegs, &cbSegFrame);
+ Log2(("writing to tunnel channel: pvSegFrame=%p cbSegFrame=%#x\n%.*Rhxd\n",
+ pvSegFrame, cbSegFrame, cbSegFrame, pvSegFrame));
+ int cBytes = ssh_channel_write(pThis->pSshChannel, pvSegFrame, cbSegFrame);
+ if (cBytes == SSH_ERROR)
+ LogRel(("%s: ssh_channel_write failed\n", pThis->pszInstance));
+ }
+ }
+
+ pSgBuf->fFlags = 0;
+ RTMemFree(pSgBuf);
+
+ STAM_PROFILE_ADV_STOP(&pThis->StatTransmit, a);
+ // AssertRC(rc);
+ // if (RT_FAILURE(rc))
+ // {
+ // if (rc == VERR_NO_MEMORY)
+ // rc = VERR_NET_NO_BUFFER_SPACE;
+ // else
+ // rc = VERR_NET_DOWN;
+ // }
+ // return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
+ */
+static DECLCALLBACK(int) drvCloudTunnelUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
+{
+ RT_NOREF(fOnWorkerThread);
+ PDRVCLOUDTUNNEL pThis = PDMINETWORKUP_2_DRVCLOUDTUNNEL(pInterface);
+ STAM_COUNTER_INC(&pThis->StatPktSent);
+ STAM_COUNTER_ADD(&pThis->StatPktSentBytes, pSgBuf->cbUsed);
+ STAM_PROFILE_ADV_START(&pThis->StatTransmit, a);
+
+ AssertPtr(pSgBuf);
+ Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
+ Assert(RTCritSectIsOwner(&pThis->XmitLock));
+
+ int rc = VINF_SUCCESS;
+ if (pThis->pIoThread && pThis->pIoThread->enmState == PDMTHREADSTATE_RUNNING)
+ {
+ Log2(("%s: submitting TX request (pvSeg=%p, %u bytes) to I/O queue...\n",
+ pThis->pszInstance, pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed));
+ rc = RTReqQueueCallEx(pThis->hIoReqQueue, NULL /*ppReq*/, 0 /*cMillies*/,
+ RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
+ (PFNRT)drvCloudTunnelSendWorker, 2, pThis, pSgBuf);
+
+ if (RT_SUCCESS(rc))
+ {
+ drvCloudTunnelNotifyIoThread(pThis, "drvCloudTunnelUp_SendBuf");
+ return VINF_SUCCESS;
+ }
+
+ rc = VERR_NET_NO_BUFFER_SPACE;
+ }
+ else
+ rc = VERR_NET_DOWN;
+ drvCloudTunnelFreeSgBuf(pThis, pSgBuf);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
+ */
+static DECLCALLBACK(void) drvCloudTunnelUp_EndXmit(PPDMINETWORKUP pInterface)
+{
+ PDRVCLOUDTUNNEL pThis = PDMINETWORKUP_2_DRVCLOUDTUNNEL(pInterface);
+ RTCritSectLeave(&pThis->XmitLock);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
+ */
+static DECLCALLBACK(void) drvCloudTunnelUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
+{
+ RT_NOREF(pInterface, fPromiscuous);
+ LogFlowFunc(("fPromiscuous=%d\n", fPromiscuous));
+ /* nothing to do */
+}
+
+
+/**
+ * Notification on link status changes.
+ *
+ * @param pInterface Pointer to the interface structure containing the called function pointer.
+ * @param enmLinkState The new link state.
+ * @thread EMT
+ */
+static DECLCALLBACK(void) drvCloudTunnelUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
+{
+ LogFlowFunc(("enmLinkState=%d\n", enmLinkState));
+ PDRVCLOUDTUNNEL pThis = PDMINETWORKUP_2_DRVCLOUDTUNNEL(pInterface);
+
+ bool fLinkDown;
+ switch (enmLinkState)
+ {
+ case PDMNETWORKLINKSTATE_DOWN:
+ case PDMNETWORKLINKSTATE_DOWN_RESUME:
+ fLinkDown = true;
+ break;
+ default:
+ AssertMsgFailed(("enmLinkState=%d\n", enmLinkState));
+ RT_FALL_THRU();
+ case PDMNETWORKLINKSTATE_UP:
+ fLinkDown = false;
+ break;
+ }
+ ASMAtomicXchgSize(&pThis->fLinkDown, fLinkDown);
+}
+
+
+
+/* -=-=-=-=- PDMIBASE -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvCloudTunnelQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+ PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
+
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
+ return NULL;
+}
+
+
+/**
+ * I/O thread handling the libssh I/O.
+ *
+ * The libssh implementation is single-threaded so we perform I/O in a
+ * dedicated thread. We take care that this thread does not become the
+ * bottleneck: If the guest wants to send, a request is enqueued into the
+ * hIoReqQueue and is handled asynchronously by this thread. TODO:If this thread
+ * wants to deliver packets to the guest, it enqueues a request into
+ * hRecvReqQueue which is later handled by the Recv thread.
+ */
+static DECLCALLBACK(int) drvCloudTunnelIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
+ // int nFDs = -1;
+
+ LogFlow(("%s: started I/O thread %p\n", pThis->pszInstance, pThread));
+
+ if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
+ return VINF_SUCCESS;
+
+ // if (pThis->enmLinkStateWant != pThis->enmLinkState)
+ // drvNATNotifyLinkChangedWorker(pThis, pThis->enmLinkStateWant);
+
+ /*
+ * Polling loop.
+ */
+ while (pThread->enmState == PDMTHREADSTATE_RUNNING)
+ {
+ /*
+ * To prevent concurrent execution of sending/receiving threads
+ */
+//#ifndef RT_OS_WINDOWS
+ // /* process _all_ outstanding requests but don't wait */
+ // RTReqQueueProcess(pThis->hIoReqQueue, 0);
+ // RTMemFree(polls);
+//#else /* RT_OS_WINDOWS */
+
+ struct timeval timeout;
+ ssh_channel in_channels[2], out_channels[2];
+ fd_set fds;
+ int maxfd;
+
+ timeout.tv_sec = 30;
+ timeout.tv_usec = 0;
+ in_channels[0] = pThis->pSshChannel;
+ in_channels[1] = NULL;
+ FD_ZERO(&fds);
+ FD_SET(pThis->iSocketIn, &fds);
+ maxfd = pThis->iSocketIn + 1;
+
+ ssh_select(in_channels, out_channels, maxfd, &fds, &timeout);
+
+ /* Poll will call the receive callback on each packet coming from the tunnel. */
+ if (out_channels[0] != NULL)
+ ssh_channel_poll(pThis->pSshChannel, false);
+
+ /* Did we get notified by drvCloudTunnelNotifyIoThread() via connected sockets? */
+ if (FD_ISSET(pThis->iSocketIn, &fds))
+ {
+ char buf[2];
+ recv(pThis->iSocketIn, buf, 1, 0);
+ /* process all outstanding requests but don't wait */
+ RTReqQueueProcess(pThis->hIoReqQueue, 0);
+ }
+//#endif /* RT_OS_WINDOWS */
+ }
+
+ LogFlow(("%s: I/O thread %p terminated\n", pThis->pszInstance, pThread));
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Unblock the I/O thread so it can respond to a state change.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The pcnet device instance.
+ * @param pThread The send thread.
+ */
+static DECLCALLBACK(int) drvCloudTunnelIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ RT_NOREF(pThread);
+ PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
+
+ LogFlow(("%s: waking up I/O thread %p...\n", pThis->pszInstance, pThread));
+
+ drvCloudTunnelNotifyIoThread(pThis, "drvCloudTunnelIoWakeup");
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * Remove the following cut&paste code after a while, when
+ * we are positive that no frames get coalesced!
+ */
+#define VBOX_CTUN_COALESCED_FRAME_DETECTION
+#ifdef VBOX_CTUN_COALESCED_FRAME_DETECTION
+struct ssh_buffer_struct {
+ bool secure;
+ size_t used;
+ size_t allocated;
+ size_t pos;
+ uint8_t *data;
+};
+
+/** @internal
+ * Describes the different possible states in a
+ * outgoing (client) channel request
+ */
+enum ssh_channel_request_state_e {
+ /** No request has been made */
+ SSH_CHANNEL_REQ_STATE_NONE = 0,
+ /** A request has been made and answer is pending */
+ SSH_CHANNEL_REQ_STATE_PENDING,
+ /** A request has been replied and accepted */
+ SSH_CHANNEL_REQ_STATE_ACCEPTED,
+ /** A request has been replied and refused */
+ SSH_CHANNEL_REQ_STATE_DENIED,
+ /** A request has been replied and an error happend */
+ SSH_CHANNEL_REQ_STATE_ERROR
+};
+
+enum ssh_channel_state_e {
+ SSH_CHANNEL_STATE_NOT_OPEN = 0,
+ SSH_CHANNEL_STATE_OPENING,
+ SSH_CHANNEL_STATE_OPEN_DENIED,
+ SSH_CHANNEL_STATE_OPEN,
+ SSH_CHANNEL_STATE_CLOSED
+};
+
+/* The channel has been closed by the remote side */
+#define SSH_CHANNEL_FLAG_CLOSED_REMOTE 0x0001
+
+/* The channel has been closed locally */
+#define SSH_CHANNEL_FLAG_CLOSED_LOCAL 0x0002
+
+/* The channel has been freed by the calling program */
+#define SSH_CHANNEL_FLAG_FREED_LOCAL 0x0004
+
+/* the channel has not yet been bound to a remote one */
+#define SSH_CHANNEL_FLAG_NOT_BOUND 0x0008
+
+struct ssh_channel_struct {
+ ssh_session session; /* SSH_SESSION pointer */
+ uint32_t local_channel;
+ uint32_t local_window;
+ int local_eof;
+ uint32_t local_maxpacket;
+
+ uint32_t remote_channel;
+ uint32_t remote_window;
+ int remote_eof; /* end of file received */
+ uint32_t remote_maxpacket;
+ enum ssh_channel_state_e state;
+ int delayed_close;
+ int flags;
+ ssh_buffer stdout_buffer;
+ ssh_buffer stderr_buffer;
+ void *userarg;
+ int exit_status;
+ enum ssh_channel_request_state_e request_state;
+ struct ssh_list *callbacks; /* list of ssh_channel_callbacks */
+
+ /* counters */
+ ssh_counter counter;
+};
+#endif /* VBOX_CTUN_COALESCED_FRAME_DETECTION */
+
+/**
+ * Worker function for delivering receive packets to the attached device.
+ *
+ * @param pThis Pointer to the cloud tunnel instance.
+ * @param pbData Packet data.
+ * @param u32Len Packet length.
+ * @thread Dev
+ */
+static DECLCALLBACK(void) drvCloudTunnelReceiveWorker(PDRVCLOUDTUNNEL pThis, uint8_t *pbData, uint32_t u32Len)
+{
+ AssertPtrReturnVoid(pbData);
+ AssertReturnVoid(u32Len!=0);
+
+ STAM_PROFILE_START(&pThis->StatDevRecv, a);
+
+ Log2(("%s: waiting until device is ready to receive...\n", pThis->pszInstance));
+ STAM_PROFILE_START(&pThis->StatDevRecvWait, b);
+ int rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
+ STAM_PROFILE_STOP(&pThis->StatDevRecvWait, b);
+
+ if (RT_SUCCESS(rc))
+ {
+ Log2(("%s: delivering %u-byte packet to attached device...\n", pThis->pszInstance, u32Len));
+ rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pbData, u32Len);
+ AssertRC(rc);
+ }
+
+ RTMemFree(pbData);
+ STAM_PROFILE_STOP(&pThis->StatDevRecv, a);
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+}
+
+static int drvCloudTunnelReceiveCallback(ssh_session session, ssh_channel channel, void* data, uint32_t len, int is_stderr, void* userdata)
+{
+ RT_NOREF(session);
+ PDRVCLOUDTUNNEL pThis = (PDRVCLOUDTUNNEL)userdata;
+
+ Log2(("drvCloudTunnelReceiveCallback: len=%d is_stderr=%s\n", len, is_stderr ? "true" : "false"));
+ if (ASMAtomicReadBool(&pThis->fLinkDown))
+ {
+ Log2(("drvCloudTunnelReceiveCallback: ignoring packet as the link is down\n"));
+ return len;
+ }
+
+#ifdef VBOX_CTUN_COALESCED_FRAME_DETECTION
+ if (channel->stdout_buffer->data != data)
+ LogRel(("drvCloudTunnelReceiveCallback: coalesced frames!\n"));
+#endif /* VBOX_CTUN_COALESCED_FRAME_DETECTION */
+
+ if (is_stderr)
+ {
+ LogRel(("%s: [REMOTE] %.*s", pThis->pszInstance, len, data));
+ return 0;
+ }
+
+ STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
+
+ if (pThis->iSshVerbosity >= SSH_LOG_PACKET)
+ Log2(("%.*Rhxd\n", len, data));
+
+ /** @todo Validate len! */
+ void *pvPacket = RTMemDup(data, len);
+ if (!pvPacket)
+ {
+ LogRel(("%s: failed to allocate %d bytes\n", pThis->pszInstance, len));
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+ return len;
+ }
+ int rc = RTReqQueueCallEx(pThis->hDevReqQueue, NULL /*ppReq*/, 0 /*cMillies*/,
+ RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
+ (PFNRT)drvCloudTunnelReceiveWorker, 3, pThis, pvPacket, len);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("%s: failed to enqueue device request - %Rrc\n", pThis->pszInstance, rc));
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+ }
+
+ return len;
+}
+
+
+/* See ssh_channel_write_wontblock_callback in libssh/callbacks.h. */
+#if LIBSSH_VERSION_INT >= SSH_VERSION_INT(0,10,0)
+static int channelWriteWontblockCallback(ssh_session, ssh_channel, uint32_t, void *)
+#else
+static int channelWriteWontblockCallback(ssh_session, ssh_channel, size_t, void *)
+#endif
+{
+ return 0;
+}
+
+
+
+/**
+ * This thread feeds the attached device with the packets received from the tunnel.
+ *
+ * This thread is needed because we cannot block I/O thread waiting for the attached
+ * device to become ready to receive packets coming from the tunnel.
+ */
+static DECLCALLBACK(int) drvCloudTunnelDevThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
+
+ LogFlow(("%s: device thread %p started\n", pThis->pszInstance, pThread));
+
+ if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
+ return VINF_SUCCESS;
+
+ /*
+ * Request processing loop.
+ */
+ while (pThread->enmState == PDMTHREADSTATE_RUNNING)
+ {
+ int rc = RTReqQueueProcess(pThis->hDevReqQueue, RT_INDEFINITE_WAIT);
+ Log2(("drvCloudTunnelDevThread: RTReqQueueProcess returned '%Rrc'\n", rc));
+ if (RT_FAILURE(rc))
+ LogRel(("%s: failed to process device request with '%Rrc'\n", pThis->pszInstance, rc));
+ }
+
+ LogFlow(("%s: device thread %p terminated\n", pThis->pszInstance, pThread));
+ return VINF_SUCCESS;
+}
+
+
+static DECLCALLBACK(int) drvCloudTunnelReceiveWakeup(PDRVCLOUDTUNNEL pThis)
+{
+ NOREF(pThis);
+ /* Returning a VINF_* will cause RTReqQueueProcess return. */
+ return VWRN_STATE_CHANGED;
+}
+
+/**
+ * Unblock the I/O thread so it can respond to a state change.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The pcnet device instance.
+ * @param pThread The send thread.
+ */
+static DECLCALLBACK(int) drvCloudTunnelDevWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ RT_NOREF(pThread);
+ PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
+ LogFlow(("%s: waking up device thread %p...\n", pThis->pszInstance, pThread));
+
+ /* Wake up device thread. */
+ PRTREQ pReq;
+ int rc = RTReqQueueCall(pThis->hDevReqQueue, &pReq, 10000 /*cMillies*/,
+ (PFNRT)drvCloudTunnelReceiveWakeup, 1, pThis);
+ if (RT_FAILURE(rc))
+ LogRel(("%s: failed to wake up device thread - %Rrc\n", pThis->pszInstance, rc));
+ if (RT_SUCCESS(rc))
+ RTReqRelease(pReq);
+
+ return rc;
+}
+
+#define DRVCLOUDTUNNEL_COMMAND_BUFFER_SIZE 1024
+#define DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE 65536
+
+static int drvCloudTunnelExecuteRemoteCommandNoOutput(PDRVCLOUDTUNNEL pThis, const char *pcszCommand, ...)
+{
+ va_list va;
+ va_start(va, pcszCommand);
+
+ size_t cb = RTStrPrintfV(pThis->pszCommandBuffer, DRVCLOUDTUNNEL_COMMAND_BUFFER_SIZE, pcszCommand, va);
+ if (cb == 0)
+ {
+ Log(("%s: Failed to process '%s'\n", pThis->pszInstance, pcszCommand));
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to compose command line"));
+ }
+
+ LogFlow(("%s: [REMOTE] executing '%s'...\n", pThis->pszInstance, pThis->pszCommandBuffer));
+
+ ssh_channel channel = ssh_channel_new(pThis->pSshSession);
+ if (channel == NULL)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to allocate new channel"));
+
+ int rc = ssh_channel_open_session(channel);
+ if (rc != SSH_OK)
+ rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to open session channel"));
+ else
+ {
+ rc = ssh_channel_request_exec(channel, pThis->pszCommandBuffer);
+ if (rc != SSH_OK)
+ {
+ LogRel(("%s: Failed to execute '%s'\n", pThis->pszInstance, pThis->pszCommandBuffer));
+ Log(("%s: Failed to execute '%s'\n", pThis->pszInstance, pThis->pszCommandBuffer));
+ rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Execute request failed with %d"), rc);
+ }
+ ssh_channel_close(channel);
+ }
+ ssh_channel_free(channel);
+
+ return VINF_SUCCESS;
+}
+
+
+static int drvCloudTunnelExecuteRemoteCommand(PDRVCLOUDTUNNEL pThis, const char *pcszCommand, ...)
+{
+ va_list va;
+ va_start(va, pcszCommand);
+
+ size_t cb = RTStrPrintfV(pThis->pszCommandBuffer, DRVCLOUDTUNNEL_COMMAND_BUFFER_SIZE, pcszCommand, va);
+ if (cb == 0)
+ {
+ Log(("%s: Failed to process '%s'\n", pThis->pszInstance, pcszCommand));
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to compose command line"));
+ }
+
+ LogFlow(("%s: [REMOTE] executing '%s'...\n", pThis->pszInstance, pThis->pszCommandBuffer));
+
+ ssh_channel channel = ssh_channel_new(pThis->pSshSession);
+ if (channel == NULL)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to allocate new channel"));
+
+ int rc = ssh_channel_open_session(channel);
+ if (rc != SSH_OK)
+ rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to open session channel"));
+ else
+ {
+ rc = ssh_channel_request_exec(channel, pThis->pszCommandBuffer);
+ if (rc != SSH_OK)
+ {
+ LogRel(("%s: Failed to execute '%s'\n", pThis->pszInstance, pThis->pszCommandBuffer));
+ Log(("%s: Failed to execute '%s'\n", pThis->pszInstance, pThis->pszCommandBuffer));
+ rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Execute request failed with %d"), rc);
+ }
+ else
+ {
+ int cbSpaceLeft = DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE;
+ int cbStdOut = 0;
+ char *pszBuffer = pThis->pszOutputBuffer;
+ int cBytes = ssh_channel_read_timeout(channel, pszBuffer, cbSpaceLeft, 0, 60000 /* ms */); /* Is 60 seconds really enough? */
+ while (cBytes > 0)
+ {
+ cbStdOut += cBytes;
+ pszBuffer += cBytes;
+ cbSpaceLeft -= cBytes;
+ if (cbSpaceLeft <= 0)
+ break;
+ cBytes = ssh_channel_read_timeout(channel, pszBuffer, cbSpaceLeft, 0, 60000 /* ms */); /* Is 60 seconds really enough? */
+ }
+ if (cBytes < 0)
+ {
+ LogRel(("%s: while executing '%s' ssh_channel_read_timeout returned error\n", pThis->pszInstance, pThis->pszCommandBuffer));
+ Log(("%s: while executing '%s' ssh_channel_read_timeout returned error\n", pThis->pszInstance, pThis->pszCommandBuffer));
+ rc = VERR_INTERNAL_ERROR;
+ }
+ else
+ {
+ /* Make sure the buffer is terminated. */
+ if (cbStdOut < DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE)
+ if (cbStdOut > 1 && pThis->pszOutputBuffer[cbStdOut - 1] == '\n')
+ pThis->pszOutputBuffer[cbStdOut - 1] = 0; /* Trim newline */
+ else
+ pThis->pszOutputBuffer[cbStdOut] = 0;
+ else
+ pThis->pszOutputBuffer[DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE - 1] = 0; /* No choice but to eat up last character. Could have returned warning though. */
+ if (cbStdOut == 0)
+ Log(("%s: received no output from remote console\n", pThis->pszInstance));
+ else
+ Log(("%s: received output from remote console:\n%s\n", pThis->pszInstance, pThis->pszOutputBuffer));
+ rc = VINF_SUCCESS;
+
+ char *pszErrorBuffer = (char *)RTMemAlloc(DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE);
+ if (pszErrorBuffer == NULL)
+ {
+ LogRel(("%s: Failed to allocate error buffer\n", pThis->pszInstance));
+ rc = VERR_INTERNAL_ERROR;
+ }
+ else
+ {
+ /* Report errors if there were any */
+ cBytes = ssh_channel_read_timeout(channel, pszErrorBuffer, DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE, 1, 0); /* Peek at stderr */
+ if (cBytes > 0)
+ {
+ LogRel(("%s: WARNING! While executing '%s' remote console reported errors:\n", pThis->pszInstance, pThis->pszCommandBuffer));
+ Log(("%s: WARNING! While executing '%s' remote console reported errors:\n", pThis->pszInstance, pThis->pszCommandBuffer));
+ }
+ while (cBytes > 0)
+ {
+ LogRel(("%.*s", cBytes, pszErrorBuffer));
+ Log(("%.*s", cBytes, pszErrorBuffer));
+ cBytes = ssh_channel_read_timeout(channel, pszErrorBuffer, DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE, 1, 1000); /* Wait for a second for more error output */
+ }
+ RTMemFree(pszErrorBuffer);
+ }
+ }
+ ssh_channel_send_eof(channel);
+ }
+ ssh_channel_close(channel);
+ }
+ ssh_channel_free(channel);
+
+ return VINF_SUCCESS;
+}
+
+
+static int drvCloudTunnelCloudInstanceInitialConfig(PDRVCLOUDTUNNEL pThis)
+{
+ LogFlow(("%s: configuring cloud instance...\n", pThis->pszInstance));
+
+ int rc = drvCloudTunnelExecuteRemoteCommand(pThis, "python3 -c \"from oci_utils.vnicutils import VNICUtils; cfg = VNICUtils().get_network_config(); print('CONFIG:', [i['IFACE'] for i in cfg if 'IS_PRIMARY' in i][0], [i['IFACE']+' '+i['VIRTRT'] for i in cfg if not 'IS_PRIMARY' in i][0])\"");
+ if (RT_FAILURE(rc))
+ rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to get network config via console channel"));
+ else
+ {
+ char *pszConfig = RTStrStr(pThis->pszOutputBuffer, "CONFIG: ");
+ if (!pszConfig)
+ rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to parse network config"));
+ else
+ {
+ char **ppapszTokens;
+ size_t cTokens;
+ rc = RTStrSplit(pszConfig + 8, DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE - (pszConfig - pThis->pszOutputBuffer) - 8,
+ " ", &ppapszTokens, &cTokens);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * There should be exactly three tokens:
+ * 1) Primary network interface name;
+ * 2) Secondary network interface name;
+ * 3) Secondary network gateway address.
+ */
+ if (cTokens != 3)
+ Log(("%s: Got %u tokes instead of three while parsing '%s'\n", pThis->pszInstance, cTokens, pThis->pszOutputBuffer));
+ else
+ {
+ char *pszSecondaryInterface = NULL;
+ char *pszSecondaryGateway = NULL;
+
+ if (pThis->pszCloudPrimaryInterface)
+ RTStrFree(pThis->pszCloudPrimaryInterface);
+ pThis->pszCloudPrimaryInterface = RTStrDup(ppapszTokens[0]);
+ pszSecondaryInterface = ppapszTokens[1];
+ pszSecondaryGateway = ppapszTokens[2];
+ Log(("%s: primary=%s secondary=%s gateway=%s\n", pThis->pszInstance, pThis->pszCloudPrimaryInterface, pszSecondaryInterface, pszSecondaryGateway));
+
+ rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo oci-network-config -c");
+ if (RT_SUCCESS(rc))
+ rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip tuntap add dev tap0 mod tap user opc");
+ if (RT_SUCCESS(rc))
+ rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo sh -c 'echo \"PermitTunnel yes\" >> /etc/ssh/sshd_config'");
+ if (RT_SUCCESS(rc))
+ rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo kill -SIGHUP $(pgrep -f \"sshd -D\")");
+ if (RT_SUCCESS(rc))
+ rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link add name br0 type bridge");
+ if (RT_SUCCESS(rc))
+ rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev tap0 master br0");
+ if (RT_SUCCESS(rc))
+ rc = drvCloudTunnelExecuteRemoteCommandNoOutput(pThis, "sudo ip route change default via %s dev %s", pszSecondaryGateway, pszSecondaryInterface);
+ if (RT_FAILURE(rc))
+ rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to execute network config command via console channel"));
+ }
+
+ for (size_t i = 0; i < cTokens; i++)
+ RTStrFree(ppapszTokens[i]);
+ RTMemFree(ppapszTokens);
+ }
+ }
+ }
+
+ return rc;
+}
+
+
+static int drvCloudTunnelCloudInstanceFinalConfig(PDRVCLOUDTUNNEL pThis)
+{
+ if (pThis->pszCloudPrimaryInterface == NULL)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to finalize cloud instance config because of unknown primary interface name!"));
+
+ LogFlow(("%s: finalizing cloud instance configuration...\n", pThis->pszInstance));
+
+ int rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev %s down", pThis->pszCloudPrimaryInterface);
+ if (RT_SUCCESS(rc))
+ rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev %s address %RTmac", pThis->pszCloudPrimaryInterface, pThis->targetMac.au8);
+ if (RT_SUCCESS(rc))
+ rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ifconfig %s 0.0.0.0", pThis->pszCloudPrimaryInterface); /* Make sure no IP is configured on primary */
+ if (RT_SUCCESS(rc))
+ rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev %s master br0", pThis->pszCloudPrimaryInterface);
+ if (RT_SUCCESS(rc))
+ rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev %s up", pThis->pszCloudPrimaryInterface);
+ if (RT_SUCCESS(rc))
+ rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev tap0 up");
+ if (RT_SUCCESS(rc))
+ rc = drvCloudTunnelExecuteRemoteCommand(pThis, "sudo ip link set dev br0 up");
+ if (RT_FAILURE(rc))
+ rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to execute network config command via console channel"));
+
+ return rc;
+}
+
+
+static int drvCloudTunnelOpenTunnelChannel(PDRVCLOUDTUNNEL pThis)
+{
+ LogFlow(("%s: opening tunnel channel...\n", pThis->pszInstance));
+ pThis->pSshChannel = ssh_channel_new(pThis->pSshSession);
+ if (pThis->pSshChannel == NULL)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to allocate new channel"));
+ int rc = ssh_channel_open_tunnel(pThis->pSshChannel, 0);
+ if (rc < 0)
+ rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to open tunnel channel"));
+ else
+ {
+ /* Set packet receive callback. */
+ rc = ssh_set_channel_callbacks(pThis->pSshChannel, &pThis->Callbacks);
+ if (rc != SSH_OK)
+ rc = PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to set packet receive callback"));
+ }
+
+ return rc;
+}
+
+
+static void closeTunnelChannel(PDRVCLOUDTUNNEL pThis)
+{
+ if (pThis->pSshChannel)
+ {
+ LogFlow(("%s: closing tunnel channel %p\n", pThis->pszInstance, pThis->pSshChannel));
+ ssh_channel_close(pThis->pSshChannel);
+ ssh_channel_free(pThis->pSshChannel);
+ pThis->pSshChannel = NULL;
+ }
+}
+
+
+static int drvCloudTunnelStartIoThread(PDRVCLOUDTUNNEL pThis)
+{
+ LogFlow(("%s: starting I/O thread...\n", pThis->pszInstance));
+ int rc = createConnectedSockets(pThis);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("CloudTunnel: Failed to create a pair of connected sockets"));
+
+ /*
+ * Start the cloud I/O thread.
+ */
+ rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pIoThread,
+ pThis, drvCloudTunnelIoThread, drvCloudTunnelIoWakeup,
+ 64 * _1K, RTTHREADTYPE_IO, pThis->pszInstanceIo);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("CloudTunnel: Failed to start I/O thread"));
+
+ return rc;
+}
+
+static void drvCloudTunnelStopIoThread(PDRVCLOUDTUNNEL pThis)
+{
+ if (pThis->pIoThread)
+ {
+ LogFlow(("%s: stopping I/O thread...\n", pThis->pszInstance));
+ int rc = PDMDrvHlpThreadDestroy(pThis->pDrvIns, pThis->pIoThread, NULL);
+ AssertRC(rc);
+ pThis->pIoThread = NULL;
+ }
+ destroyConnectedSockets(pThis);
+
+}
+
+static int destroyTunnel(PDRVCLOUDTUNNEL pThis)
+{
+ if (pThis->pSshChannel)
+ {
+ int rc = ssh_remove_channel_callbacks(pThis->pSshChannel, &pThis->Callbacks);
+ if (rc != SSH_OK)
+ LogRel(("%s: WARNING! Failed to remove tunnel channel callbacks.\n", pThis->pszInstance));
+ }
+ drvCloudTunnelStopIoThread(pThis);
+ closeTunnelChannel(pThis);
+ ssh_disconnect(pThis->pSshSession);
+ ssh_free(pThis->pSshSession);
+ pThis->pSshSession = NULL;
+ return VINF_SUCCESS;
+}
+
+
+static int drvCloudTunnelNewSession(PDRVCLOUDTUNNEL pThis, bool fPrimary)
+{
+ pThis->pSshSession = ssh_new();
+ if (pThis->pSshSession == NULL)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("CloudTunnel: Failed to allocate new SSH session"));
+ if (ssh_options_set(pThis->pSshSession, SSH_OPTIONS_LOG_VERBOSITY, &pThis->iSshVerbosity) < 0)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to set SSH_OPTIONS_LOG_VERBOSITY"));
+ if (ssh_options_set(pThis->pSshSession, SSH_OPTIONS_USER, pThis->pszUser) < 0)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to set SSH_OPTIONS_USER"));
+ if (ssh_options_set(pThis->pSshSession, SSH_OPTIONS_HOST, fPrimary ? pThis->pszPrimaryIP : pThis->pszSecondaryIP) < 0)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to set SSH_OPTIONS_HOST"));
+
+ if (ssh_options_set(pThis->pSshSession, SSH_OPTIONS_TIMEOUT, &pThis->ulTimeoutInSecounds) < 0)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to set SSH_OPTIONS_TIMEOUT"));
+
+ const char *pcszProxyType = fPrimary ? pThis->pszPrimaryProxyType : pThis->pszSecondaryProxyType;
+ if (pcszProxyType)
+ {
+ char szProxyCmd[1024];
+
+ const char *pcszProxyUser = fPrimary ? pThis->pszPrimaryProxyUser : pThis->pszSecondaryProxyUser;
+ if (pcszProxyUser)
+ RTStrPrintf(szProxyCmd, sizeof(szProxyCmd), "#VBoxProxy%s %s %u %s %s",
+ fPrimary ? pThis->pszPrimaryProxyType : pThis->pszSecondaryProxyType,
+ fPrimary ? pThis->pszPrimaryProxyHost : pThis->pszSecondaryProxyHost,
+ fPrimary ? pThis->u16PrimaryProxyPort : pThis->u16SecondaryProxyPort,
+ fPrimary ? pThis->pszPrimaryProxyUser : pThis->pszSecondaryProxyUser,
+ fPrimary ? pThis->pszPrimaryProxyPassword : pThis->pszSecondaryProxyPassword);
+ else
+ RTStrPrintf(szProxyCmd, sizeof(szProxyCmd), "#VBoxProxy%s %s %u",
+ fPrimary ? pThis->pszPrimaryProxyType : pThis->pszSecondaryProxyType,
+ fPrimary ? pThis->pszPrimaryProxyHost : pThis->pszSecondaryProxyHost,
+ fPrimary ? pThis->u16PrimaryProxyPort : pThis->u16SecondaryProxyPort);
+ LogRel(("%s: using proxy command '%s'\n", pThis->pszInstance, szProxyCmd));
+ if (ssh_options_set(pThis->pSshSession, SSH_OPTIONS_PROXYCOMMAND, szProxyCmd) < 0)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to set SSH_OPTIONS_PROXYCOMMAND"));
+ }
+
+ int rc = ssh_connect(pThis->pSshSession);
+ for (int cAttempt = 1; rc != SSH_OK && cAttempt <= 5; cAttempt++)
+ {
+ ssh_disconnect(pThis->pSshSession);
+ /* One more time, just to be sure. */
+ LogRel(("%s: failed to connect to %s, retrying(#%d)...\n", pThis->pszInstance,
+ fPrimary ? pThis->pszPrimaryIP : pThis->pszSecondaryIP, cAttempt));
+ RTThreadSleep(10000); /* Sleep 10 seconds, then retry */
+ rc = ssh_connect(pThis->pSshSession);
+ }
+ if (rc != SSH_OK)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("CloudTunnel: Failed to connect to %s interface"), fPrimary ? "primary" : "secondary");
+
+ rc = ssh_userauth_publickey(pThis->pSshSession, NULL, pThis->SshKey);
+ if (rc != SSH_AUTH_SUCCESS)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to authenticate with public key"));
+
+ return VINF_SUCCESS;
+}
+
+static int drvCloudTunnelSwitchToSecondary(PDRVCLOUDTUNNEL pThis)
+{
+ int rc = drvCloudTunnelNewSession(pThis, true /* fPrimary */);
+ /*
+ * Establish temporary console channel and configure the cloud instance
+ * to bridge the tunnel channel to instance's primary interface.
+ */
+ if (RT_SUCCESS(rc))
+ rc = drvCloudTunnelCloudInstanceInitialConfig(pThis);
+
+ ssh_disconnect(pThis->pSshSession);
+ ssh_free(pThis->pSshSession);
+ pThis->pSshSession = NULL;
+
+ return rc;
+}
+
+
+static int establishTunnel(PDRVCLOUDTUNNEL pThis)
+{
+ int rc = drvCloudTunnelNewSession(pThis, false /* fPrimary */);
+ if (RT_SUCCESS(rc))
+ rc = drvCloudTunnelCloudInstanceFinalConfig(pThis);
+ if (RT_SUCCESS(rc))
+ rc = drvCloudTunnelOpenTunnelChannel(pThis);
+ if (RT_SUCCESS(rc))
+ rc = drvCloudTunnelStartIoThread(pThis);
+ if (RT_FAILURE(rc))
+ {
+ destroyTunnel(pThis);
+ return rc;
+ }
+
+ return rc;
+}
+
+
+static DECL_NOTHROW(void) drvCloudTunnelSshLogCallback(int priority, const char *function, const char *buffer, void *userdata)
+{
+ PDRVCLOUDTUNNEL pThis = (PDRVCLOUDTUNNEL)userdata;
+#ifdef LOG_ENABLED
+ const char *pcszVerbosity;
+ switch (priority)
+ {
+ case SSH_LOG_WARNING:
+ pcszVerbosity = "WARNING";
+ break;
+ case SSH_LOG_PROTOCOL:
+ pcszVerbosity = "PROTOCOL";
+ break;
+ case SSH_LOG_PACKET:
+ pcszVerbosity = "PACKET";
+ break;
+ case SSH_LOG_FUNCTIONS:
+ pcszVerbosity = "FUNCTIONS";
+ break;
+ default:
+ pcszVerbosity = "UNKNOWN";
+ break;
+ }
+ Log3(("%s: SSH-%s: %s: %s\n", pThis->pszInstance, pcszVerbosity, function, buffer));
+#else
+ RT_NOREF(priority);
+ LogRel(("%s: SSH %s: %s\n", pThis->pszInstance, function, buffer));
+#endif
+}
+
+/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
+
+DECLINLINE(void) drvCloudTunnelStrFree(char **ppszString)
+{
+ if (*ppszString)
+ {
+ RTStrFree(*ppszString);
+ *ppszString = NULL;
+ }
+}
+
+DECLINLINE(void) drvCloudTunnelHeapFree(PPDMDRVINS pDrvIns, char **ppszString)
+{
+ if (*ppszString)
+ {
+ PDMDrvHlpMMHeapFree(pDrvIns, *ppszString);
+ *ppszString = NULL;
+ }
+}
+
+/**
+ * Destruct a driver instance.
+ *
+ * Most VM resources are freed by the VM. This callback is provided so that any non-VM
+ * resources can be freed correctly.
+ *
+ * @param pDrvIns The driver instance data.
+ */
+static DECLCALLBACK(void) drvCloudTunnelDestruct(PPDMDRVINS pDrvIns)
+{
+ LogFlowFunc(("\n"));
+ PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+
+ ASMAtomicXchgSize(&pThis->fLinkDown, true);
+
+ destroyTunnel(pThis);
+
+ if (pThis->hIoReqQueue != NIL_RTREQQUEUE)
+ {
+ RTReqQueueDestroy(pThis->hIoReqQueue);
+ pThis->hIoReqQueue = NIL_RTREQQUEUE;
+ }
+
+ drvCloudTunnelStrFree(&pThis->pszCloudPrimaryInterface);
+
+ drvCloudTunnelHeapFree(pDrvIns, &pThis->pszPrimaryProxyType);
+ drvCloudTunnelStrFree(&pThis->pszPrimaryProxyHost);
+ drvCloudTunnelHeapFree(pDrvIns, &pThis->pszPrimaryProxyUser);
+ drvCloudTunnelStrFree(&pThis->pszPrimaryProxyPassword);
+
+ drvCloudTunnelHeapFree(pDrvIns, &pThis->pszSecondaryProxyType);
+ drvCloudTunnelStrFree(&pThis->pszSecondaryProxyHost);
+ drvCloudTunnelHeapFree(pDrvIns, &pThis->pszSecondaryProxyUser);
+ drvCloudTunnelStrFree(&pThis->pszSecondaryProxyPassword);
+
+ drvCloudTunnelStrFree(&pThis->pszSecondaryIP);
+ drvCloudTunnelStrFree(&pThis->pszPrimaryIP);
+ drvCloudTunnelStrFree(&pThis->pszUser);
+
+ drvCloudTunnelStrFree(&pThis->pszInstanceDev);
+ drvCloudTunnelStrFree(&pThis->pszInstanceIo);
+ drvCloudTunnelStrFree(&pThis->pszInstance);
+
+ drvCloudTunnelStrFree(&pThis->pszOutputBuffer);
+ drvCloudTunnelStrFree(&pThis->pszCommandBuffer);
+
+ ssh_key_free(pThis->SshKey);
+
+ ssh_finalize();
+ //OPENSSL_cleanup();
+
+ // if (pThis->pServer)
+ // {
+ // RTUdpServerDestroy(pThis->pServer);
+ // pThis->pServer = NULL;
+ // }
+
+ /*
+ * Kill the xmit lock.
+ */
+ if (RTCritSectIsInitialized(&pThis->XmitLock))
+ RTCritSectDelete(&pThis->XmitLock);
+
+#ifdef VBOX_WITH_STATISTICS
+ /*
+ * Deregister statistics.
+ */
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktSent);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktSentBytes);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktRecv);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktRecvBytes);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatTransmit);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReceive);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatDevRecv);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatDevRecvWait);
+#endif /* VBOX_WITH_STATISTICS */
+}
+
+
+/**
+ * Construct a Cloud tunnel network transport driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(int) drvCloudTunnelConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ RT_NOREF(fFlags);
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ /*
+ * Init the static parts.
+ */
+ pThis->pDrvIns = pDrvIns;
+ pThis->pszCommandBuffer = NULL;
+ pThis->pszOutputBuffer = NULL;
+ pThis->pszInstance = NULL;
+ pThis->pszPrimaryIP = NULL;
+ pThis->pszSecondaryIP = NULL;
+ pThis->pszUser = NULL;
+ pThis->SshKey = 0;
+
+ /* IBase */
+ pDrvIns->IBase.pfnQueryInterface = drvCloudTunnelQueryInterface;
+ /* INetwork */
+ pThis->INetworkUp.pfnBeginXmit = drvCloudTunnelUp_BeginXmit;
+ pThis->INetworkUp.pfnAllocBuf = drvCloudTunnelUp_AllocBuf;
+ pThis->INetworkUp.pfnFreeBuf = drvCloudTunnelUp_FreeBuf;
+ pThis->INetworkUp.pfnSendBuf = drvCloudTunnelUp_SendBuf;
+ pThis->INetworkUp.pfnEndXmit = drvCloudTunnelUp_EndXmit;
+ pThis->INetworkUp.pfnSetPromiscuousMode = drvCloudTunnelUp_SetPromiscuousMode;
+ pThis->INetworkUp.pfnNotifyLinkChanged = drvCloudTunnelUp_NotifyLinkChanged;
+
+ /* ??? */
+ pThis->iSocketIn = INVALID_SOCKET;
+ pThis->iSocketOut = INVALID_SOCKET;
+ pThis->pSshSession = 0;
+ pThis->pSshChannel = 0;
+
+ pThis->pDevThread = 0;
+ pThis->pIoThread = 0;
+ pThis->hIoReqQueue = NIL_RTREQQUEUE;
+
+ pThis->fLinkDown = false;
+
+ pThis->pszCloudPrimaryInterface = NULL;
+
+#ifdef VBOX_WITH_STATISTICS
+ /*
+ * Statistics.
+ */
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktSent, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of sent packets.", "/Drivers/CloudTunnel%d/Packets/Sent", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktSentBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of sent bytes.", "/Drivers/CloudTunnel%d/Bytes/Sent", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktRecv, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of received packets.", "/Drivers/CloudTunnel%d/Packets/Received", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktRecvBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of received bytes.", "/Drivers/CloudTunnel%d/Bytes/Received", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet transmit runs.", "/Drivers/CloudTunnel%d/Transmit", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet receive runs.", "/Drivers/CloudTunnel%d/Receive", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatDevRecv, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling device receive runs.", "/Drivers/CloudTunnel%d/DeviceReceive", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatDevRecvWait, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling device receive waits.", "/Drivers/CloudTunnel%d/DeviceReceiveWait", pDrvIns->iInstance);
+#endif /* VBOX_WITH_STATISTICS */
+
+ /*
+ * Validate the config.
+ */
+ PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "SshKey"
+ "|PrimaryIP"
+ "|SecondaryIP"
+ "|TargetMAC"
+
+ "|PrimaryProxyType"
+ "|PrimaryProxyHost"
+ "|PrimaryProxyPort"
+ "|PrimaryProxyUser"
+ "|PrimaryProxyPassword"
+ "|SecondaryProxyType"
+ "|SecondaryProxyHost"
+ "|SecondaryProxyPort"
+ "|SecondaryProxyUser"
+ "|SecondaryProxyPassword"
+
+ ,"");
+
+ /*
+ * Check that no-one is attached to us.
+ */
+ AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
+ ("Configuration error: Not possible to attach anything to this driver!\n"),
+ VERR_PDM_DRVINS_NO_ATTACH);
+
+ /*
+ * Query the network port interface.
+ */
+ pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
+ if (!pThis->pIAboveNet)
+ return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
+ N_("Configuration error: The above device/driver didn't export the network port interface"));
+
+ /*
+ * Read the configuration.
+ */
+ int rc;
+
+ char szVal[2048];
+ RTNETADDRIPV4 tmpAddr;
+ rc = pHlp->pfnCFGMQueryString(pCfg, "PrimaryIP", szVal, sizeof(szVal));
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryIP\" as string failed"));
+ rc = RTNetStrToIPv4Addr(szVal, &tmpAddr);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: \"PrimaryIP\" is not valid"));
+ else
+ pThis->pszPrimaryIP = RTStrDup(szVal);
+
+ rc = pHlp->pfnCFGMQueryString(pCfg, "SecondaryIP", szVal, sizeof(szVal));
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryIP\" as string failed"));
+ rc = RTNetStrToIPv4Addr(szVal, &tmpAddr);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: \"SecondaryIP\" is not valid"));
+ else
+ pThis->pszSecondaryIP = RTStrDup(szVal);
+ rc = pHlp->pfnCFGMQueryBytes(pCfg, "TargetMAC", pThis->targetMac.au8, sizeof(pThis->targetMac.au8));
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: Failed to get target MAC address"));
+ /** @todo In the near future we will want to include proxy settings here! */
+ // Do we want to pass the user name via CFGM?
+ pThis->pszUser = RTStrDup("opc");
+ // Is it safe to expose verbosity via CFGM?
+#ifdef LOG_ENABLED
+ pThis->iSshVerbosity = SSH_LOG_PACKET; //SSH_LOG_FUNCTIONS;
+#else
+ pThis->iSshVerbosity = SSH_LOG_WARNING;
+#endif
+
+ pThis->ulTimeoutInSecounds = 30; /* The default 10-second timeout is too short? */
+
+ rc = pHlp->pfnCFGMQueryPassword(pCfg, "SshKey", szVal, sizeof(szVal));
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: Querying \"SshKey\" as password failed"));
+ rc = ssh_pki_import_privkey_base64(szVal, NULL, NULL, NULL, &pThis->SshKey);
+ RTMemWipeThoroughly(szVal, sizeof(szVal), 10);
+ if (rc != SSH_OK)
+ return PDMDRV_SET_ERROR(pDrvIns, VERR_INVALID_BASE64_ENCODING,
+ N_("DrvCloudTunnel: Configuration error: Converting \"SshKey\" from base64 failed"));
+
+ /* PrimaryProxyType is optional */
+ rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "PrimaryProxyType", &pThis->pszPrimaryProxyType, NULL);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryProxyType\" as string failed"));
+ if (pThis->pszPrimaryProxyType)
+ {
+ rc = pHlp->pfnCFGMQueryString(pCfg, "PrimaryProxyHost", szVal, sizeof(szVal));
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryProxyHost\" as string failed"));
+ rc = RTNetStrToIPv4Addr(szVal, &tmpAddr);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: \"PrimaryProxyHost\" is not valid"));
+ else
+ pThis->pszPrimaryProxyHost = RTStrDup(szVal);
+
+ uint64_t u64Val;
+ rc = pHlp->pfnCFGMQueryInteger(pCfg, "PrimaryProxyPort", &u64Val);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryProxyPort\" as integer failed"));
+ if (u64Val > 0xFFFF)
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: \"PrimaryProxyPort\" is not valid"));
+ pThis->u16PrimaryProxyPort = (uint16_t)u64Val;
+
+ /* PrimaryProxyUser is optional */
+ rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "PrimaryProxyUser", &pThis->pszPrimaryProxyUser, NULL);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryProxyUser\" as string failed"));
+ /* PrimaryProxyPassword must be present if PrimaryProxyUser is present */
+ if (pThis->pszPrimaryProxyUser)
+ {
+ rc = pHlp->pfnCFGMQueryPassword(pCfg, "PrimaryProxyPassword", szVal, sizeof(szVal));
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: Querying \"PrimaryProxyPassword\" as string failed"));
+ pThis->pszPrimaryProxyPassword = RTStrDup(szVal);
+ }
+ }
+
+ /* SecondaryProxyType is optional */
+ rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "SecondaryProxyType", &pThis->pszSecondaryProxyType, NULL);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyType\" as string failed"));
+ if (pThis->pszSecondaryProxyType)
+ {
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyType\" as string failed"));
+
+ rc = pHlp->pfnCFGMQueryString(pCfg, "SecondaryProxyHost", szVal, sizeof(szVal));
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyHost\" as string failed"));
+ rc = RTNetStrToIPv4Addr(szVal, &tmpAddr);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: \"SecondaryProxyHost\" is not valid"));
+ else
+ pThis->pszSecondaryProxyHost = RTStrDup(szVal);
+
+ uint64_t u64Val;
+ rc = pHlp->pfnCFGMQueryInteger(pCfg, "SecondaryProxyPort", &u64Val);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyPort\" as integer failed"));
+ if (u64Val > 0xFFFF)
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: \"SecondaryProxyPort\" is not valid"));
+ pThis->u16SecondaryProxyPort = (uint16_t)u64Val;
+
+ /* SecondaryProxyUser is optional */
+ rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "SecondaryProxyUser", &pThis->pszSecondaryProxyUser, NULL);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyUser\" as string failed"));
+ /* SecondaryProxyPassword must be present if SecondaryProxyUser is present */
+ if (pThis->pszSecondaryProxyUser)
+ {
+ rc = pHlp->pfnCFGMQueryPassword(pCfg, "SecondaryProxyPassword", szVal, sizeof(szVal));
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvCloudTunnel: Configuration error: Querying \"SecondaryProxyPassword\" as string failed"));
+ pThis->pszSecondaryProxyPassword = RTStrDup(szVal);
+ }
+ }
+
+ pThis->pszCommandBuffer = (char *)RTMemAlloc(DRVCLOUDTUNNEL_COMMAND_BUFFER_SIZE);
+ if (pThis->pszCommandBuffer == NULL)
+ return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_HIF_OPEN_FAILED,
+ N_("DrvCloudTunnel: Failed to allocate command buffer"));
+ pThis->pszOutputBuffer = (char *)RTMemAlloc(DRVCLOUDTUNNEL_OUTPUT_BUFFER_SIZE);
+ if (pThis->pszOutputBuffer == NULL)
+ return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_HIF_OPEN_FAILED,
+ N_("DrvCloudTunnel: Failed to allocate output buffer"));
+ /*
+ * Create unique instance name for logging.
+ */
+ rc = RTStrAPrintf(&pThis->pszInstance, "CT#%d", pDrvIns->iInstance);
+ AssertRC(rc);
+
+ LogRel(("%s: primary=%s secondary=%s target-mac=%RTmac\n", pThis->pszInstance, pThis->pszPrimaryIP, pThis->pszSecondaryIP, pThis->targetMac.au8));
+
+ /*
+ * Create unique thread name for cloud I/O.
+ */
+ rc = RTStrAPrintf(&pThis->pszInstanceIo, "CTunIO%d", pDrvIns->iInstance);
+ AssertRC(rc);
+
+ /*
+ * Create unique thread name for device receive function.
+ */
+ rc = RTStrAPrintf(&pThis->pszInstanceDev, "CTunDev%d", pDrvIns->iInstance);
+ AssertRC(rc);
+
+ /*
+ * Create the transmit lock.
+ */
+ rc = RTCritSectInit(&pThis->XmitLock);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Create the request queue for I/O requests.
+ */
+ rc = RTReqQueueCreate(&pThis->hIoReqQueue);
+ AssertLogRelRCReturn(rc, rc);
+
+ /*
+ * Create the request queue for attached device requests.
+ */
+ rc = RTReqQueueCreate(&pThis->hDevReqQueue);
+ AssertLogRelRCReturn(rc, rc);
+
+ /*
+ * Start the device output thread.
+ */
+ rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pDevThread,
+ pThis, drvCloudTunnelDevThread, drvCloudTunnelDevWakeup,
+ 64 * _1K, RTTHREADTYPE_IO, pThis->pszInstanceDev);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("CloudTunnel: Failed to start device thread"));
+
+ rc = ssh_init();
+ if (rc != SSH_OK)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("CloudTunnel: Failed to initialize libssh"));
+
+ memset(&pThis->Callbacks, 0, sizeof(pThis->Callbacks));
+#ifdef PACKET_CAPTURE_ENABLED
+ pThis->Callbacks.channel_data_function = drvCloudTunnelReceiveCallbackWithPacketCapture;
+#else
+ pThis->Callbacks.channel_data_function = drvCloudTunnelReceiveCallback;
+#endif
+ pThis->Callbacks.userdata = pThis;
+ pThis->Callbacks.channel_write_wontblock_function = channelWriteWontblockCallback;
+ ssh_callbacks_init(&pThis->Callbacks);
+
+ rc = ssh_set_log_callback(drvCloudTunnelSshLogCallback);
+ if (rc != SSH_OK)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("CloudTunnel: Failed to set libssh log callback"));
+ rc = ssh_set_log_userdata(pThis);
+ if (rc != SSH_OK)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("CloudTunnel: Failed to set libssh log userdata"));
+
+ rc = drvCloudTunnelSwitchToSecondary(pThis);
+ if (RT_SUCCESS(rc))
+ rc = establishTunnel(pThis);
+
+ return rc;
+}
+
+
+#if 0
+/**
+ * Suspend notification.
+ *
+ * @param pDrvIns The driver instance.
+ */
+static DECLCALLBACK(void) drvCloudTunnelSuspend(PPDMDRVINS pDrvIns)
+{
+ LogFlowFunc(("\n"));
+ PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
+
+ RT_NOREF(pThis);
+ // if (pThis->pServer)
+ // {
+ // RTUdpServerDestroy(pThis->pServer);
+ // pThis->pServer = NULL;
+ // }
+}
+
+
+/**
+ * Resume notification.
+ *
+ * @param pDrvIns The driver instance.
+ */
+static DECLCALLBACK(void) drvCloudTunnelResume(PPDMDRVINS pDrvIns)
+{
+ LogFlowFunc(("\n"));
+ PDRVCLOUDTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVCLOUDTUNNEL);
+
+ int rc = RTUdpServerCreate("", pThis->uSrcPort, RTTHREADTYPE_IO, pThis->pszInstance,
+ drvCloudTunnelReceive, pDrvIns, &pThis->pServer);
+ if (RT_FAILURE(rc))
+ PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("CloudTunnel: Failed to start the Cloud tunnel server"));
+
+}
+#endif
+
+/**
+ * Cloud tunnel network transport driver registration record.
+ */
+const PDMDRVREG g_DrvCloudTunnel =
+{
+ /* u32Version */
+ PDM_DRVREG_VERSION,
+ /* szName */
+ "CloudTunnel",
+ /* szRCMod */
+ "",
+ /* szR0Mod */
+ "",
+ /* pszDescription */
+ "Cloud Tunnel Network Transport Driver",
+ /* fFlags */
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+ /* fClass. */
+ PDM_DRVREG_CLASS_NETWORK,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(DRVCLOUDTUNNEL),
+ /* pfnConstruct */
+ drvCloudTunnelConstruct,
+ /* pfnDestruct */
+ drvCloudTunnelDestruct,
+ /* pfnRelocate */
+ NULL,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ NULL,
+ /* pfnSuspend */
+ NULL, // drvCloudTunnelSuspend,
+ /* pfnResume */
+ NULL, // drvCloudTunnelResume,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32EndVersion */
+ PDM_DRVREG_VERSION
+};
+
diff --git a/src/VBox/Devices/Network/DrvDedicatedNic.cpp b/src/VBox/Devices/Network/DrvDedicatedNic.cpp
new file mode 100644
index 00000000..c1de8109
--- /dev/null
+++ b/src/VBox/Devices/Network/DrvDedicatedNic.cpp
@@ -0,0 +1,577 @@
+/* $Id: DrvDedicatedNic.cpp $ */
+/** @file
+ * DrvDedicatedNic - Experimental network driver for using a dedicated (V)NIC.
+ */
+
+/*
+ * Copyright (C) 2010-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DEFAULT
+#include <VBox/log.h>
+#include <VBox/vmm/pdmcritsect.h>
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmnetifs.h>
+#include <VBox/vmm/pdmnetinline.h>
+#include <VBox/intnet.h>
+#include <VBox/intnetinline.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/uuid.h>
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Instance data for the dedicated (V)NIC driver.
+ *
+ * @implements PDMINETWORKUP
+ */
+typedef struct DRVDEDICATEDNIC
+{
+ /** The network interface. */
+ PDMINETWORKUP INetworkUpR3;
+ /** The network interface. */
+ R3PTRTYPE(PPDMINETWORKDOWN) pIAboveNet;
+ /** The network config interface.
+ * Can (in theory at least) be NULL. */
+ R3PTRTYPE(PPDMINETWORKCONFIG) pIAboveConfigR3;
+ /** Pointer to the driver instance. */
+ PPDMDRVINSR3 pDrvInsR3;
+ /** Ring-3 base interface for the ring-0 context. */
+ PDMIBASER0 IBaseR0;
+ /** Ring-3 base interface for the raw-mode context. */
+ PDMIBASERC IBaseRC;
+ RTR3PTR R3PtrAlignment;
+
+
+ /** The network interface for the ring-0 context. */
+ PDMINETWORKUPR0 INetworkUpR0;
+ /** Pointer to the driver instance. */
+ PPDMDRVINSR0 pDrvInsR0;
+ RTR0PTR R0PtrAlignment;
+
+ /** The interface we're talking to. */
+ R0PTRTYPE(PINTNETTRUNKIFPORT) pIfPortR0;
+ /** Set if the link is up, clear if its down. */
+ bool fLinkDown;
+ /** Set if the current transmit operation is done the XMIT thread. If clear,
+ * we assume its an EMT. */
+ bool fXmitOnXmitThread;
+ /** The name of the interface that we're connected to. */
+ char szIfName[128 + 8 - 2];
+
+ /** Critical section serializing transmission. */
+ PDMCRITSECT XmitLock;
+ /** The transmit scatter gather buffer (ring-3 -> ring-0). */
+ PDMSCATTERGATHER XmitSg;
+ /** The transmit GSO context (when applicable). */
+ PDMNETWORKGSO XmitGso;
+ /** The transmit buffer (ring-3 -> ring-0). */
+ uint8_t abXmitBuf[_64K];
+
+ /** The receive scatter gather buffer. */
+ PDMSCATTERGATHER RecvSg;
+ /** The receive buffer (ring-0 -> ring-3). */
+ uint8_t abRecvBuf[_64K];
+
+} DRVDEDICATEDNIC;
+/** Pointer to the instance data for the dedicated (V)NIC driver. */
+typedef DRVDEDICATEDNIC *PDRVDEDICATEDNIC;
+
+/**
+ * Ring-0 operations.
+ */
+typedef enum DRVDEDICATEDNICR0OP
+{
+ /** Invalid zero value.. */
+ DRVDEDICATEDNICR0OP_INVALID = 0,
+ /** Initialize the connection to the NIC. */
+ DRVDEDICATEDNICR0OP_INIT,
+ /** Terminate the connection to the NIC. */
+ DRVDEDICATEDNICR0OP_TERM,
+ /** Suspend the operation. */
+ DRVDEDICATEDNICR0OP_SUSPEND,
+ /** Resume the operation. */
+ DRVDEDICATEDNICR0OP_RESUME,
+ /** Wait for and do receive work.
+ * We do this in ring-0 instead of ring-3 to save 1-2 buffer copies and
+ * unnecessary context switching. */
+ DRVDEDICATEDNICR0OP_RECV,
+ /** Wait for and do transmit work.
+ * We do this in ring-0 instead of ring-3 to save 1-2 buffer copies and
+ * unnecessary context switching. */
+ DRVDEDICATEDNICR0OP_SEND,
+ /** Changes the promiscuousness of the interface (guest point of view). */
+ DRVDEDICATEDNICR0OP_PROMISC,
+ /** End of the valid operations. */
+ DRVDEDICATEDNICR0OP_END,
+ /** The usual 32-bit hack. */
+ DRVDEDICATEDNICR0OP_32BIT_HACK = 0x7fffffff
+} DRVDEDICATEDNICR0OP;
+
+
+
+#ifdef IN_RING0
+
+/**
+ * @interface_method_impl{FNPDMDRVREQHANDLERR0}
+ */
+PDMBOTHCBDECL(int) drvR0DedicatedNicReqHandler(PPDMDRVINS pDrvIns, uint32_t uOperation, uint64_t u64Arg)
+{
+ RT_NOREF_PV(pDrvIns); RT_NOREF_PV(u64Arg);
+ switch ((DRVDEDICATEDNICR0OP)uOperation)
+ {
+ case DRVDEDICATEDNICR0OP_INIT:
+ return VERR_NOT_IMPLEMENTED;//drvR0DedicatedNicReqInit(pDrvIns, u64Arg);
+
+ case DRVDEDICATEDNICR0OP_TERM:
+ return VERR_NOT_IMPLEMENTED;//drvR0DedicatedNicReqTerm(pDrvIns);
+
+ case DRVDEDICATEDNICR0OP_SUSPEND:
+ return VERR_NOT_IMPLEMENTED;//drvR0DedicatedNicReqSuspend(pDrvIns);
+
+ case DRVDEDICATEDNICR0OP_RESUME:
+ return VERR_NOT_IMPLEMENTED;//drvR0DedicatedNicReqResume(pDrvIns);
+
+ case DRVDEDICATEDNICR0OP_RECV:
+ return VERR_NOT_IMPLEMENTED;//drvR0DedicatedNicReqRecv(pDrvIns);
+
+ case DRVDEDICATEDNICR0OP_SEND:
+ return VERR_NOT_IMPLEMENTED;//drvR0DedicatedNicReqSend(pDrvIns);
+
+ case DRVDEDICATEDNICR0OP_PROMISC:
+ return VERR_NOT_IMPLEMENTED;//drvR0DedicatedNicReqPromisc(pDrvIns, !!u64Arg);
+
+ case DRVDEDICATEDNICR0OP_END:
+ default:
+ return VERR_INVALID_FUNCTION;
+ }
+}
+
+#endif /* IN_RING0 */
+
+
+
+#if 0 /* currently unused */
+
+/* -=-=-=-=- PDMINETWORKUP -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
+ */
+PDMBOTHCBDECL(int) drvDedicatedNicUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
+{
+ PDRVDEDICATEDNIC pThis = RT_FROM_MEMBER(pInterface, DRVDEDICATEDNIC, CTX_SUFF(INetworkUp));
+ int rc = PDMCritSectTryEnter(&pThis->XmitLock);
+ if (RT_SUCCESS(rc))
+ ASMAtomicUoWriteBool(&pThis->fXmitOnXmitThread, fOnWorkerThread);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
+ */
+PDMBOTHCBDECL(int) drvDedicatedNicUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
+ PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
+{
+ PDRVDEDICATEDNIC pThis = RT_FROM_MEMBER(pInterface, DRVDEDICATEDNIC, CTX_SUFF(INetworkUp));
+ Assert(PDMCritSectIsOwner(&pThis->XmitLock));
+
+ /*
+ * If the net is down, we can return immediately.
+ */
+ if (pThis->fLinkDown)
+ return VERR_NET_DOWN;
+
+#ifdef IN_RING0
+ /** @todo Ask the driver for a buffer, atomically if we're called on EMT. */
+ RT_NOREF_PV(cbMin); RT_NOREF_PV(pGso); RT_NOREF_PV(ppSgBuf);
+ return VERR_TRY_AGAIN;
+
+#else /* IN_RING3 */
+ /*
+ * Are we busy or is the request too big?
+ */
+ if (RT_UNLIKELY((pThis->XmitSg.fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC))
+ return VERR_TRY_AGAIN;
+ if (cbMin > sizeof(pThis->abXmitBuf))
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize the S/G buffer and return.
+ */
+ pThis->XmitSg.fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
+ pThis->XmitSg.cbUsed = 0;
+ pThis->XmitSg.cbAvailable = sizeof(pThis->abXmitBuf);
+ pThis->XmitSg.pvAllocator = NULL;
+ if (!pGso)
+ {
+ pThis->XmitSg.pvUser = NULL;
+ pThis->XmitGso.u8Type = PDMNETWORKGSOTYPE_INVALID;
+ }
+ else
+ {
+ pThis->XmitSg.pvUser = &pThis->XmitGso;
+ pThis->XmitGso = *pGso;
+ }
+ pThis->XmitSg.cSegs = 1;
+ pThis->XmitSg.aSegs[0].cbSeg = pThis->XmitSg.cbAvailable;
+ pThis->XmitSg.aSegs[0].pvSeg = &pThis->abXmitBuf[0];
+
+# if 0 /* poison */
+ memset(pThis->XmitSg.aSegs[0].pvSeg, 'F', pThis->XmitSg.aSegs[0].cbSeg);
+# endif
+
+ *ppSgBuf = &pThis->XmitSg;
+ return VINF_SUCCESS;
+#endif /* IN_RING3 */
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
+ */
+PDMBOTHCBDECL(int) drvDedicatedNicUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
+{
+#ifdef VBOX_STRICT
+ PDRVDEDICATEDNIC pThis = RT_FROM_MEMBER(pInterface, DRVDEDICATEDNIC, CTX_SUFF(INetworkUp));
+ Assert(pSgBuf->fFlags == (PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1));
+ Assert(pSgBuf->cbUsed <= pSgBuf->cbAvailable);
+ Assert(PDMCritSectIsOwner(&pThis->XmitLock));
+#else
+ RT_NOREF1(pInterface);
+#endif
+
+ if (pSgBuf)
+ {
+#ifdef IN_RING0
+ // ...
+#else
+ Assert(pSgBuf == &pThis->XmitSg);
+ Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
+ pSgBuf->fFlags = 0;
+#endif
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
+ */
+PDMBOTHCBDECL(int) drvDedicatedNicUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
+{
+ PDRVDEDICATEDNIC pThis = RT_FROM_MEMBER(pInterface, DRVDEDICATEDNIC, CTX_SUFF(INetworkUp));
+ STAM_PROFILE_START(&pThis->StatTransmit, a);
+
+ AssertPtr(pSgBuf);
+ Assert(pSgBuf->fFlags == (PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1));
+ Assert(pSgBuf->cbUsed <= pSgBuf->cbAvailable);
+#ifdef IN_RING0
+ Assert(pSgBuf == &pThis->XmitSg);
+#endif
+ Assert(PDMCritSectIsOwner(&pThis->XmitLock));
+
+#ifdef IN_RING0
+ /*
+ * Tell the driver to send the packet.
+ */
+ RT_NOREF_PV(pThis); RT_NOREF_PV(pSgBuf); RT_NOREF_PV(fOnWorkerThread);
+ return VERR_INTERNAL_ERROR_4;
+
+#else /* IN_RING3 */
+ NOREF(fOnWorkerThread);
+
+ /*
+ * Call ring-0 to start the transfer.
+ */
+ int rc = PDMDrvHlpCallR0(pThis->pDrvInsR3, DRVDEDICATEDNICR0OP_SEND, pSgBuf->cbUsed);
+ if (RT_FAILURE(rc) && rc != VERR_NET_DOWN)
+ rc = VERR_NET_NO_BUFFER_SPACE;
+ pSgBuf->fFlags = 0;
+ return rc;
+#endif /* IN_RING3 */
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
+ */
+PDMBOTHCBDECL(void) drvDedicatedNicUp_EndXmit(PPDMINETWORKUP pInterface)
+{
+ PDRVDEDICATEDNIC pThis = RT_FROM_MEMBER(pInterface, DRVDEDICATEDNIC, CTX_SUFF(INetworkUp));
+ ASMAtomicUoWriteBool(&pThis->fXmitOnXmitThread, false);
+ PDMCritSectLeave(&pThis->XmitLock);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
+ */
+PDMBOTHCBDECL(void) drvDedicatedNicUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
+{
+ PDRVDEDICATEDNIC pThis = RT_FROM_MEMBER(pInterface, DRVDEDICATEDNIC, CTX_SUFF(INetworkUp));
+ /** @todo enable/disable promiscuous mode (should be easy) */
+ NOREF(pThis); RT_NOREF_PV(fPromiscuous);
+}
+
+#endif /* unused */
+#ifdef IN_RING3
+# if 0 /* currently unused */
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnNotifyLinkChanged}
+ */
+static DECLCALLBACK(void) drvR3DedicatedNicUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
+{
+ PDRVDEDICATEDNIC pThis = RT_FROM_MEMBER(pInterface, DRVDEDICATEDNIC, CTX_SUFF(INetworkUp));
+ bool fLinkDown;
+ switch (enmLinkState)
+ {
+ case PDMNETWORKLINKSTATE_DOWN:
+ case PDMNETWORKLINKSTATE_DOWN_RESUME:
+ fLinkDown = true;
+ break;
+ default:
+ AssertMsgFailed(("enmLinkState=%d\n", enmLinkState));
+ case PDMNETWORKLINKSTATE_UP:
+ fLinkDown = false;
+ break;
+ }
+ LogFlow(("drvR3DedicatedNicUp_NotifyLinkChanged: enmLinkState=%d %d->%d\n", enmLinkState, pThis->fLinkDown, fLinkDown));
+ ASMAtomicWriteBool(&pThis->fLinkDown, fLinkDown);
+}
+
+
+/* -=-=-=-=- PDMIBASER0 -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASER0,pfnQueryInterface}
+ */
+static DECLCALLBACK(RTR0PTR) drvR3DedicatedNicIBaseR0_QueryInterface(PPDMIBASER0 pInterface, const char *pszIID)
+{
+ PDRVDEDICATEDNIC pThis = RT_FROM_MEMBER(pInterface, DRVDEDICATEDNIC, IBaseR0);
+ PDMIBASER0_RETURN_INTERFACE(pThis->pDrvInsR3, pszIID, PDMINETWORKUP, &pThis->INetworkUpR0);
+ return NIL_RTR0PTR;
+}
+
+
+/* -=-=-=-=- PDMIBASE -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvR3DedicatedNicIBase_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+ PDRVDEDICATEDNIC pThis = PDMINS_2_DATA(pDrvIns, PDRVDEDICATEDNIC);
+
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASER0, &pThis->IBaseR0);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUpR3);
+ return NULL;
+}
+
+# endif /* Currently unused */
+
+
+/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnPowerOff}
+ */
+static DECLCALLBACK(void) drvR3DedicatedNicPowerOff(PPDMDRVINS pDrvIns)
+{
+ LogFlow(("drvR3DedicatedNicPowerOff\n"));
+ int rc = PDMDrvHlpCallR0(pDrvIns, DRVDEDICATEDNICR0OP_SUSPEND, 0);
+ AssertRC(rc);
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnResume}
+ */
+static DECLCALLBACK(void) drvR3DedicatedNicResume(PPDMDRVINS pDrvIns)
+{
+ LogFlow(("drvR3DedicatedNicPowerResume\n"));
+ int rc = PDMDrvHlpCallR0(pDrvIns, DRVDEDICATEDNICR0OP_RESUME, 0);
+ AssertRC(rc);
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnSuspend}
+ */
+static DECLCALLBACK(void) drvR3DedicatedNicSuspend(PPDMDRVINS pDrvIns)
+{
+ LogFlow(("drvR3DedicatedNicPowerSuspend\n"));
+ int rc = PDMDrvHlpCallR0(pDrvIns, DRVDEDICATEDNICR0OP_SUSPEND, 0);
+ AssertRC(rc);
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnPowerOn}
+ */
+static DECLCALLBACK(void) drvR3DedicatedNicPowerOn(PPDMDRVINS pDrvIns)
+{
+ LogFlow(("drvR3DedicatedNicPowerOn\n"));
+ int rc = PDMDrvHlpCallR0(pDrvIns, DRVDEDICATEDNICR0OP_RESUME, 0);
+ AssertRC(rc);
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnDestruct}
+ */
+static DECLCALLBACK(void) drvR3DedicatedNicDestruct(PPDMDRVINS pDrvIns)
+{
+ LogFlow(("drvR3DedicatedNicDestruct\n"));
+ PDRVDEDICATEDNIC pThis = PDMINS_2_DATA(pDrvIns, PDRVDEDICATEDNIC);
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+
+ if (pThis->pIfPortR0)
+ {
+ int rc = PDMDrvHlpCallR0(pDrvIns, DRVDEDICATEDNICR0OP_TERM, 0);
+ AssertRC(rc);;
+ }
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnConstruct}
+ */
+static DECLCALLBACK(int) drvR3DedicatedNicConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ RT_NOREF(pCfg, fFlags);
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVDEDICATEDNIC pThis = PDMINS_2_DATA(pDrvIns, PDRVDEDICATEDNIC);
+
+ /*
+ * Init the static parts.
+ */
+ pThis->pDrvInsR3 = pDrvIns;
+ pThis->pDrvInsR0 = PDMDRVINS_2_R0PTR(pDrvIns);
+#if 0
+ pThis->hRecvThread = NIL_RTTHREAD;
+ pThis->hRecvEvt = NIL_RTSEMEVENT;
+ pThis->pXmitThread = NULL;
+ pThis->hXmitEvt = NIL_SUPSEMEVENT;
+ pThis->pSupDrvSession = PDMDrvHlpGetSupDrvSession(pDrvIns);
+ pThis->hSgCache = NIL_RTMEMCACHE;
+ pThis->enmRecvState = RECVSTATE_SUSPENDED;
+ pThis->fActivateEarlyDeactivateLate = false;
+ /* IBase* */
+ pDrvIns->IBase.pfnQueryInterface = drvR3DedicatedNicIBase_QueryInterface;
+ pThis->IBaseR0.pfnQueryInterface = drvR3DedicatedNicIBaseR0_QueryInterface;
+ pThis->IBaseRC.pfnQueryInterface = drvR3DedicatedNicIBaseRC_QueryInterface;
+ /* INetworkUp */
+ pThis->INetworkUpR3.pfnBeginXmit = drvDedicatedNic_BeginXmit;
+ pThis->INetworkUpR3.pfnAllocBuf = drvDedicatedNic_AllocBuf;
+ pThis->INetworkUpR3.pfnFreeBuf = drvDedicatedNic_FreeBuf;
+ pThis->INetworkUpR3.pfnSendBuf = drvDedicatedNic_SendBuf;
+ pThis->INetworkUpR3.pfnEndXmit = drvDedicatedNic_EndXmit;
+ pThis->INetworkUpR3.pfnSetPromiscuousMode = drvDedicatedNic_SetPromiscuousMode;
+ pThis->INetworkUpR3.pfnNotifyLinkChanged = drvR3DedicatedNicUp_NotifyLinkChanged;
+#endif
+
+ /** @todo
+ * Need to create a generic way of calling into the ring-0 side of the driver so
+ * we can initialize the thing as well as send and receive. Hmm ... the
+ * sending could be done more efficiently from a ring-0 kernel thread actually
+ * (saves context switching and 1-2 copy operations). Ditto for receive, except
+ * we need to tie the thread to the process or we cannot access the guest ram so
+ * easily.
+ */
+
+ return VERR_NOT_IMPLEMENTED;
+}
+
+
+
+/**
+ * Internal networking transport driver registration record.
+ */
+const PDMDRVREG g_DrvDedicatedNic =
+{
+ /* u32Version */
+ PDM_DRVREG_VERSION,
+ /* szName */
+ "DedicatedNic",
+ /* szRCMod */
+ "",
+ /* szR0Mod */
+ "VBoxDDR0.r0",
+ /* pszDescription */
+ "Dedicated (V)NIC Driver",
+ /* fFlags */
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DRVREG_FLAGS_R0,
+ /* fClass. */
+ PDM_DRVREG_CLASS_NETWORK,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(DRVDEDICATEDNIC),
+ /* pfnConstruct */
+ drvR3DedicatedNicConstruct,
+ /* pfnDestruct */
+ drvR3DedicatedNicDestruct,
+ /* pfnRelocate */
+ NULL,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ drvR3DedicatedNicPowerOn,
+ /* pfnReset */
+ NULL,
+ /* pfnSuspend */
+ drvR3DedicatedNicSuspend,
+ /* pfnResume */
+ drvR3DedicatedNicResume,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnPowerOff */
+ drvR3DedicatedNicPowerOff,
+ /* pfnSoftReset */
+ NULL,
+ /* u32EndVersion */
+ PDM_DRVREG_VERSION
+};
+
+#endif /* IN_RING3 */
+
diff --git a/src/VBox/Devices/Network/DrvIntNet.cpp b/src/VBox/Devices/Network/DrvIntNet.cpp
new file mode 100644
index 00000000..44da805a
--- /dev/null
+++ b/src/VBox/Devices/Network/DrvIntNet.cpp
@@ -0,0 +1,2140 @@
+/* $Id: DrvIntNet.cpp $ */
+/** @file
+ * DrvIntNet - Internal network transport driver.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_INTNET
+#if defined(RT_OS_DARWIN) && defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
+# include <xpc/xpc.h> /* This needs to be here because it drags PVM in and cdefs.h needs to undefine it... */
+#endif
+#include <iprt/cdefs.h>
+
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmnetinline.h>
+#include <VBox/vmm/pdmnetifs.h>
+#include <VBox/vmm/cfgm.h>
+#include <VBox/intnet.h>
+#include <VBox/intnetinline.h>
+#include <VBox/vmm/vmm.h>
+#include <VBox/sup.h>
+#include <VBox/err.h>
+
+#include <VBox/param.h>
+#include <VBox/log.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/memcache.h>
+#include <iprt/net.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/thread.h>
+#include <iprt/uuid.h>
+#if defined(RT_OS_DARWIN) && defined(IN_RING3)
+# include <iprt/system.h>
+#endif
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#if 0
+/** Enables the ring-0 part. */
+#define VBOX_WITH_DRVINTNET_IN_R0
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * The state of the asynchronous thread.
+ */
+typedef enum RECVSTATE
+{
+ /** The thread is suspended. */
+ RECVSTATE_SUSPENDED = 1,
+ /** The thread is running. */
+ RECVSTATE_RUNNING,
+ /** The thread must (/has) terminate. */
+ RECVSTATE_TERMINATE,
+ /** The usual 32-bit type blowup. */
+ RECVSTATE_32BIT_HACK = 0x7fffffff
+} RECVSTATE;
+
+/**
+ * Internal networking driver instance data.
+ *
+ * @implements PDMINETWORKUP
+ */
+typedef struct DRVINTNET
+{
+ /** The network interface. */
+ PDMINETWORKUP INetworkUpR3;
+ /** The network interface. */
+ R3PTRTYPE(PPDMINETWORKDOWN) pIAboveNet;
+ /** The network config interface.
+ * Can (in theory at least) be NULL. */
+ R3PTRTYPE(PPDMINETWORKCONFIG) pIAboveConfigR3;
+ /** Pointer to the driver instance (ring-3). */
+ PPDMDRVINSR3 pDrvInsR3;
+ /** Pointer to the communication buffer (ring-3). */
+ R3PTRTYPE(PINTNETBUF) pBufR3;
+#ifdef VBOX_WITH_DRVINTNET_IN_R0
+ /** Ring-3 base interface for the ring-0 context. */
+ PDMIBASER0 IBaseR0;
+ /** Ring-3 base interface for the raw-mode context. */
+ PDMIBASERC IBaseRC;
+ RTR3PTR R3PtrAlignment;
+
+ /** The network interface for the ring-0 context. */
+ PDMINETWORKUPR0 INetworkUpR0;
+ /** Pointer to the driver instance (ring-0). */
+ PPDMDRVINSR0 pDrvInsR0;
+ /** Pointer to the communication buffer (ring-0). */
+ R0PTRTYPE(PINTNETBUF) pBufR0;
+
+ /** The network interface for the raw-mode context. */
+ PDMINETWORKUPRC INetworkUpRC;
+ /** Pointer to the driver instance. */
+ PPDMDRVINSRC pDrvInsRC;
+ RTRCPTR RCPtrAlignment;
+#endif
+
+ /** The transmit lock. */
+ PDMCRITSECT XmitLock;
+ /** Interface handle. */
+ INTNETIFHANDLE hIf;
+ /** The receive thread state. */
+ RECVSTATE volatile enmRecvState;
+ /** The receive thread. */
+ RTTHREAD hRecvThread;
+ /** The event semaphore that the receive thread waits on. */
+ RTSEMEVENT hRecvEvt;
+ /** The transmit thread. */
+ PPDMTHREAD pXmitThread;
+ /** The event semaphore that the transmit thread waits on. */
+ SUPSEMEVENT hXmitEvt;
+ /** The support driver session handle. */
+ PSUPDRVSESSION pSupDrvSession;
+ /** Scatter/gather descriptor cache. */
+ RTMEMCACHE hSgCache;
+ /** Set if the link is down.
+ * When the link is down all incoming packets will be dropped. */
+ bool volatile fLinkDown;
+ /** Set when the xmit thread has been signalled. (atomic) */
+ bool volatile fXmitSignalled;
+ /** Set if the transmit thread the one busy transmitting. */
+ bool volatile fXmitOnXmitThread;
+ /** The xmit thread should process the ring ASAP. */
+ bool fXmitProcessRing;
+ /** Set if data transmission should start immediately and deactivate
+ * as late as possible. */
+ bool fActivateEarlyDeactivateLate;
+ /** Padding. */
+ bool afReserved[HC_ARCH_BITS == 64 ? 3 : 3];
+ /** Scratch space for holding the ring-0 scatter / gather descriptor.
+ * The PDMSCATTERGATHER::fFlags member is used to indicate whether it is in
+ * use or not. Always accessed while owning the XmitLock. */
+ union
+ {
+ PDMSCATTERGATHER Sg;
+ uint8_t padding[8 * sizeof(RTUINTPTR)];
+ } u;
+ /** The network name. */
+ char szNetwork[INTNET_MAX_NETWORK_NAME];
+
+ /** Number of GSO packets sent. */
+ STAMCOUNTER StatSentGso;
+ /** Number of GSO packets received. */
+ STAMCOUNTER StatReceivedGso;
+ /** Number of packets send from ring-0. */
+ STAMCOUNTER StatSentR0;
+ /** The number of times we've had to wake up the xmit thread to continue the
+ * ring-0 job. */
+ STAMCOUNTER StatXmitWakeupR0;
+ /** The number of times we've had to wake up the xmit thread to continue the
+ * ring-3 job. */
+ STAMCOUNTER StatXmitWakeupR3;
+ /** The times the xmit thread has been told to process the ring. */
+ STAMCOUNTER StatXmitProcessRing;
+#ifdef VBOX_WITH_STATISTICS
+ /** Profiling packet transmit runs. */
+ STAMPROFILE StatTransmit;
+ /** Profiling packet receive runs. */
+ STAMPROFILEADV StatReceive;
+#endif /* VBOX_WITH_STATISTICS */
+#ifdef LOG_ENABLED
+ /** The nano ts of the last transfer. */
+ uint64_t u64LastTransferTS;
+ /** The nano ts of the last receive. */
+ uint64_t u64LastReceiveTS;
+#endif
+#if defined(RT_OS_DARWIN) && defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
+ /** XPC connection handle to the R3 internal network switch service. */
+ xpc_connection_t hXpcCon;
+ /** Flag whether the R3 internal network service is being used. */
+ bool fIntNetR3Svc;
+ /** Size of the communication buffer in bytes. */
+ size_t cbBuf;
+#endif
+} DRVINTNET;
+AssertCompileMemberAlignment(DRVINTNET, XmitLock, 8);
+AssertCompileMemberAlignment(DRVINTNET, StatSentGso, 8);
+/** Pointer to instance data of the internal networking driver. */
+typedef DRVINTNET *PDRVINTNET;
+
+/**
+ * Config value to flag translation structure.
+ */
+typedef struct DRVINTNETFLAG
+{
+ /** The value. */
+ const char *pszChoice;
+ /** The corresponding flag. */
+ uint32_t fFlag;
+} DRVINTNETFLAG;
+/** Pointer to a const flag value translation. */
+typedef DRVINTNETFLAG const *PCDRVINTNETFLAG;
+
+
+#ifdef IN_RING3
+
+
+/**
+ * Calls the internal networking switch service living in either R0 or in another R3 process.
+ *
+ * @returns VBox status code.
+ * @param pThis The internal network driver instance data.
+ * @param uOperation The operation to execute.
+ * @param pvArg Pointer to the argument data.
+ * @param cbArg Size of the argument data in bytes.
+ */
+static int drvR3IntNetCallSvc(PDRVINTNET pThis, uint32_t uOperation, void *pvArg, unsigned cbArg)
+{
+#if defined(RT_OS_DARWIN) && defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
+ if (pThis->fIntNetR3Svc)
+ {
+ xpc_object_t hObj = xpc_dictionary_create(NULL, NULL, 0);
+ xpc_dictionary_set_uint64(hObj, "req-id", uOperation);
+ xpc_dictionary_set_data(hObj, "req", pvArg, cbArg);
+ xpc_object_t hObjReply = xpc_connection_send_message_with_reply_sync(pThis->hXpcCon, hObj);
+ xpc_release(hObj);
+
+ uint64_t u64Rc = xpc_dictionary_get_uint64(hObjReply, "rc");
+ if (INTNET_R3_SVC_IS_VALID_RC(u64Rc))
+ {
+ size_t cbReply = 0;
+ const void *pvData = xpc_dictionary_get_data(hObjReply, "reply", &cbReply);
+ AssertRelease(cbReply == cbArg);
+ memcpy(pvArg, pvData, cbArg);
+ xpc_release(hObjReply);
+
+ return INTNET_R3_SVC_GET_RC(u64Rc);
+ }
+
+ xpc_release(hObjReply);
+ return VERR_INVALID_STATE;
+ }
+ else
+#endif
+ return PDMDrvHlpSUPCallVMMR0Ex(pThis->pDrvInsR3, uOperation, pvArg, cbArg);
+}
+
+
+#if defined(RT_OS_DARWIN) && defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
+/**
+ * Calls the internal networking switch service living in either R0 or in another R3 process.
+ *
+ * @returns VBox status code.
+ * @param pThis The internal network driver instance data.
+ * @param uOperation The operation to execute.
+ * @param pvArg Pointer to the argument data.
+ * @param cbArg Size of the argument data in bytes.
+ */
+static int drvR3IntNetCallSvcAsync(PDRVINTNET pThis, uint32_t uOperation, void *pvArg, unsigned cbArg)
+{
+ if (pThis->fIntNetR3Svc)
+ {
+ xpc_object_t hObj = xpc_dictionary_create(NULL, NULL, 0);
+ xpc_dictionary_set_uint64(hObj, "req-id", uOperation);
+ xpc_dictionary_set_data(hObj, "req", pvArg, cbArg);
+ xpc_connection_send_message(pThis->hXpcCon, hObj);
+ return VINF_SUCCESS;
+ }
+ else
+ return PDMDrvHlpSUPCallVMMR0Ex(pThis->pDrvInsR3, uOperation, pvArg, cbArg);
+}
+#endif
+
+
+/**
+ * Map the ring buffer pointer into this process R3 address space.
+ *
+ * @returns VBox status code.
+ * @param pThis The internal network driver instance data.
+ */
+static int drvR3IntNetMapBufferPointers(PDRVINTNET pThis)
+{
+ int rc = VINF_SUCCESS;
+
+ INTNETIFGETBUFFERPTRSREQ GetBufferPtrsReq;
+ GetBufferPtrsReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ GetBufferPtrsReq.Hdr.cbReq = sizeof(GetBufferPtrsReq);
+ GetBufferPtrsReq.pSession = NIL_RTR0PTR;
+ GetBufferPtrsReq.hIf = pThis->hIf;
+ GetBufferPtrsReq.pRing3Buf = NULL;
+ GetBufferPtrsReq.pRing0Buf = NIL_RTR0PTR;
+
+#if defined(RT_OS_DARWIN) && defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
+ if (pThis->fIntNetR3Svc)
+ {
+ xpc_object_t hObj = xpc_dictionary_create(NULL, NULL, 0);
+ xpc_dictionary_set_uint64(hObj, "req-id", VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS);
+ xpc_dictionary_set_data(hObj, "req", &GetBufferPtrsReq, sizeof(GetBufferPtrsReq));
+ xpc_object_t hObjReply = xpc_connection_send_message_with_reply_sync(pThis->hXpcCon, hObj);
+ xpc_release(hObj);
+
+ uint64_t u64Rc = xpc_dictionary_get_uint64(hObjReply, "rc");
+ if (INTNET_R3_SVC_IS_VALID_RC(u64Rc))
+ rc = INTNET_R3_SVC_GET_RC(u64Rc);
+ else
+ rc = VERR_INVALID_STATE;
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Get the shared memory object. */
+ xpc_object_t hObjShMem = xpc_dictionary_get_value(hObjReply, "buf-ptr");
+ size_t cbMem = xpc_shmem_map(hObjShMem, (void **)&pThis->pBufR3);
+ if (!cbMem)
+ rc = VERR_NO_MEMORY;
+ else
+ pThis->cbBuf = cbMem;
+ }
+
+ xpc_release(hObjReply);
+ }
+ else
+#endif
+ {
+ rc = PDMDrvHlpSUPCallVMMR0Ex(pThis->pDrvInsR3, VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS, &GetBufferPtrsReq, sizeof(GetBufferPtrsReq));
+ if (RT_SUCCESS(rc))
+ {
+ AssertRelease(RT_VALID_PTR(GetBufferPtrsReq.pRing3Buf));
+ pThis->pBufR3 = GetBufferPtrsReq.pRing3Buf;
+#ifdef VBOX_WITH_DRVINTNET_IN_R0
+ pThis->pBufR0 = GetBufferPtrsReq.pRing0Buf;
+#endif
+ }
+ }
+
+ return rc;
+}
+
+
+/**
+ * Updates the MAC address on the kernel side.
+ *
+ * @returns VBox status code.
+ * @param pThis The driver instance.
+ */
+static int drvR3IntNetUpdateMacAddress(PDRVINTNET pThis)
+{
+ if (!pThis->pIAboveConfigR3)
+ return VINF_SUCCESS;
+
+ INTNETIFSETMACADDRESSREQ SetMacAddressReq;
+ SetMacAddressReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ SetMacAddressReq.Hdr.cbReq = sizeof(SetMacAddressReq);
+ SetMacAddressReq.pSession = NIL_RTR0PTR;
+ SetMacAddressReq.hIf = pThis->hIf;
+ int rc = pThis->pIAboveConfigR3->pfnGetMac(pThis->pIAboveConfigR3, &SetMacAddressReq.Mac);
+ if (RT_SUCCESS(rc))
+ rc = drvR3IntNetCallSvc(pThis, VMMR0_DO_INTNET_IF_SET_MAC_ADDRESS,
+ &SetMacAddressReq, sizeof(SetMacAddressReq));
+
+ Log(("drvR3IntNetUpdateMacAddress: %.*Rhxs rc=%Rrc\n", sizeof(SetMacAddressReq.Mac), &SetMacAddressReq.Mac, rc));
+ return rc;
+}
+
+
+/**
+ * Sets the kernel interface active or inactive.
+ *
+ * Worker for poweron, poweroff, suspend and resume.
+ *
+ * @returns VBox status code.
+ * @param pThis The driver instance.
+ * @param fActive The new state.
+ */
+static int drvR3IntNetSetActive(PDRVINTNET pThis, bool fActive)
+{
+ if (!pThis->pIAboveConfigR3)
+ return VINF_SUCCESS;
+
+ INTNETIFSETACTIVEREQ SetActiveReq;
+ SetActiveReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ SetActiveReq.Hdr.cbReq = sizeof(SetActiveReq);
+ SetActiveReq.pSession = NIL_RTR0PTR;
+ SetActiveReq.hIf = pThis->hIf;
+ SetActiveReq.fActive = fActive;
+ int rc = drvR3IntNetCallSvc(pThis, VMMR0_DO_INTNET_IF_SET_ACTIVE,
+ &SetActiveReq, sizeof(SetActiveReq));
+
+ Log(("drvR3IntNetSetActive: fActive=%d rc=%Rrc\n", fActive, rc));
+ AssertRC(rc);
+ return rc;
+}
+
+#endif /* IN_RING3 */
+
+/* -=-=-=-=- PDMINETWORKUP -=-=-=-=- */
+
+#ifndef IN_RING3
+/**
+ * Helper for signalling the xmit thread.
+ *
+ * @returns VERR_TRY_AGAIN (convenience).
+ * @param pThis The instance data..
+ */
+DECLINLINE(int) drvR0IntNetSignalXmit(PDRVINTNET pThis)
+{
+ /// @todo if (!ASMAtomicXchgBool(&pThis->fXmitSignalled, true)) - needs careful optimizing.
+ {
+ int rc = SUPSemEventSignal(pThis->pSupDrvSession, pThis->hXmitEvt);
+ AssertRC(rc);
+ STAM_REL_COUNTER_INC(&pThis->CTX_SUFF(StatXmitWakeup));
+ }
+ return VERR_TRY_AGAIN;
+}
+#endif /* !IN_RING3 */
+
+
+/**
+ * Helper for processing the ring-0 consumer side of the xmit ring.
+ *
+ * The caller MUST own the xmit lock.
+ *
+ * @returns Status code from IntNetR0IfSend, except for VERR_TRY_AGAIN.
+ * @param pThis The instance data..
+ */
+DECLINLINE(int) drvIntNetProcessXmit(PDRVINTNET pThis)
+{
+ Assert(PDMDrvHlpCritSectIsOwner(pThis->CTX_SUFF(pDrvIns), &pThis->XmitLock));
+
+#ifdef IN_RING3
+ INTNETIFSENDREQ SendReq;
+ SendReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ SendReq.Hdr.cbReq = sizeof(SendReq);
+ SendReq.pSession = NIL_RTR0PTR;
+ SendReq.hIf = pThis->hIf;
+ int rc = drvR3IntNetCallSvc(pThis, VMMR0_DO_INTNET_IF_SEND, &SendReq, sizeof(SendReq));
+#else
+ int rc = IntNetR0IfSend(pThis->hIf, pThis->pSupDrvSession);
+ if (rc == VERR_TRY_AGAIN)
+ {
+ ASMAtomicUoWriteBool(&pThis->fXmitProcessRing, true);
+ drvR0IntNetSignalXmit(pThis);
+ rc = VINF_SUCCESS;
+ }
+#endif
+ return rc;
+}
+
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
+ */
+PDMBOTHCBDECL(int) drvIntNetUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
+{
+ PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, CTX_SUFF(INetworkUp));
+#ifndef IN_RING3
+ Assert(!fOnWorkerThread);
+#endif
+
+ int rc = PDMDrvHlpCritSectTryEnter(pThis->CTX_SUFF(pDrvIns), &pThis->XmitLock);
+ if (RT_SUCCESS(rc))
+ {
+ if (fOnWorkerThread)
+ {
+ ASMAtomicUoWriteBool(&pThis->fXmitOnXmitThread, true);
+ ASMAtomicWriteBool(&pThis->fXmitSignalled, false);
+ }
+ }
+ else if (rc == VERR_SEM_BUSY)
+ {
+ /** @todo Does this actually make sense if the other dude is an EMT and so
+ * forth? I seriously think this is ring-0 only...
+ * We might end up waking up the xmit thread unnecessarily here, even when in
+ * ring-0... This needs some more thought and optimizations when the ring-0 bits
+ * are working. */
+#ifdef IN_RING3
+ if ( !fOnWorkerThread
+ /*&& !ASMAtomicUoReadBool(&pThis->fXmitOnXmitThread)
+ && ASMAtomicCmpXchgBool(&pThis->fXmitSignalled, true, false)*/)
+ {
+ rc = SUPSemEventSignal(pThis->pSupDrvSession, pThis->hXmitEvt);
+ AssertRC(rc);
+ }
+ rc = VERR_TRY_AGAIN;
+#else /* IN_RING0 */
+ rc = drvR0IntNetSignalXmit(pThis);
+#endif /* IN_RING0 */
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
+ */
+PDMBOTHCBDECL(int) drvIntNetUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
+ PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
+{
+ PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, CTX_SUFF(INetworkUp));
+ int rc = VINF_SUCCESS;
+ Assert(cbMin < UINT32_MAX / 2);
+ Assert(PDMDrvHlpCritSectIsOwner(pThis->CTX_SUFF(pDrvIns), &pThis->XmitLock));
+
+ /*
+ * Allocate a S/G descriptor.
+ * This shouldn't normally fail as the NICs usually won't allocate more
+ * than one buffer at a time and the SG gets freed on sending.
+ */
+#ifdef IN_RING3
+ PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemCacheAlloc(pThis->hSgCache);
+ if (!pSgBuf)
+ return VERR_NO_MEMORY;
+#else
+ PPDMSCATTERGATHER pSgBuf = &pThis->u.Sg;
+ if (RT_UNLIKELY(pSgBuf->fFlags != 0))
+ return drvR0IntNetSignalXmit(pThis);
+#endif
+
+ /*
+ * Allocate room in the ring buffer.
+ *
+ * In ring-3 we may have to process the xmit ring before there is
+ * sufficient buffer space since we might have stacked up a few frames to the
+ * trunk while in ring-0. (There is not point of doing this in ring-0.)
+ */
+ PINTNETHDR pHdr = NULL; /* gcc silliness */
+ if (pGso)
+ rc = IntNetRingAllocateGsoFrame(&pThis->CTX_SUFF(pBuf)->Send, (uint32_t)cbMin, pGso,
+ &pHdr, &pSgBuf->aSegs[0].pvSeg);
+ else
+ rc = IntNetRingAllocateFrame(&pThis->CTX_SUFF(pBuf)->Send, (uint32_t)cbMin,
+ &pHdr, &pSgBuf->aSegs[0].pvSeg);
+#ifdef IN_RING3
+ if ( RT_FAILURE(rc)
+ && pThis->CTX_SUFF(pBuf)->cbSend >= cbMin * 2 + sizeof(INTNETHDR))
+ {
+ drvIntNetProcessXmit(pThis);
+ if (pGso)
+ rc = IntNetRingAllocateGsoFrame(&pThis->CTX_SUFF(pBuf)->Send, (uint32_t)cbMin, pGso,
+ &pHdr, &pSgBuf->aSegs[0].pvSeg);
+ else
+ rc = IntNetRingAllocateFrame(&pThis->CTX_SUFF(pBuf)->Send, (uint32_t)cbMin,
+ &pHdr, &pSgBuf->aSegs[0].pvSeg);
+ }
+#endif
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Set up the S/G descriptor and return successfully.
+ */
+ pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
+ pSgBuf->cbUsed = 0;
+ pSgBuf->cbAvailable = cbMin;
+ pSgBuf->pvAllocator = pHdr;
+ pSgBuf->pvUser = pGso ? (PPDMNETWORKGSO)pSgBuf->aSegs[0].pvSeg - 1 : NULL;
+ pSgBuf->cSegs = 1;
+ pSgBuf->aSegs[0].cbSeg = cbMin;
+
+ *ppSgBuf = pSgBuf;
+ return VINF_SUCCESS;
+ }
+
+#ifdef IN_RING3
+ /*
+ * If the above fails, then we're really out of space. There are nobody
+ * competing with us here because of the xmit lock.
+ */
+ rc = VERR_NO_MEMORY;
+ RTMemCacheFree(pThis->hSgCache, pSgBuf);
+
+#else /* IN_RING0 */
+ /*
+ * If the request is reasonable, kick the xmit thread and tell it to
+ * process the xmit ring ASAP.
+ */
+ if (pThis->CTX_SUFF(pBuf)->cbSend >= cbMin * 2 + sizeof(INTNETHDR))
+ {
+ pThis->fXmitProcessRing = true;
+ rc = drvR0IntNetSignalXmit(pThis);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ pSgBuf->fFlags = 0;
+#endif /* IN_RING0 */
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
+ */
+PDMBOTHCBDECL(int) drvIntNetUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
+{
+ PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, CTX_SUFF(INetworkUp));
+ PINTNETHDR pHdr = (PINTNETHDR)pSgBuf->pvAllocator;
+#ifdef IN_RING0
+ Assert(pSgBuf == &pThis->u.Sg);
+#endif
+ Assert(pSgBuf->fFlags == (PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1));
+ Assert(pSgBuf->cbUsed <= pSgBuf->cbAvailable);
+ Assert( pHdr->u8Type == INTNETHDR_TYPE_FRAME
+ || pHdr->u8Type == INTNETHDR_TYPE_GSO);
+ Assert(PDMDrvHlpCritSectIsOwner(pThis->CTX_SUFF(pDrvIns), &pThis->XmitLock));
+
+ /** @todo LATER: try unalloc the frame. */
+ pHdr->u8Type = INTNETHDR_TYPE_PADDING;
+ IntNetRingCommitFrame(&pThis->CTX_SUFF(pBuf)->Send, pHdr);
+
+#ifdef IN_RING3
+ RTMemCacheFree(pThis->hSgCache, pSgBuf);
+#else
+ pSgBuf->fFlags = 0;
+#endif
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
+ */
+PDMBOTHCBDECL(int) drvIntNetUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
+{
+ PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, CTX_SUFF(INetworkUp));
+ STAM_PROFILE_START(&pThis->StatTransmit, a);
+ RT_NOREF_PV(fOnWorkerThread);
+
+ AssertPtr(pSgBuf);
+ Assert(pSgBuf->fFlags == (PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1));
+ Assert(pSgBuf->cbUsed <= pSgBuf->cbAvailable);
+ Assert(PDMDrvHlpCritSectIsOwner(pThis->CTX_SUFF(pDrvIns), &pThis->XmitLock));
+
+ if (pSgBuf->pvUser)
+ STAM_COUNTER_INC(&pThis->StatSentGso);
+
+ /*
+ * Commit the frame and push it thru the switch.
+ */
+ PINTNETHDR pHdr = (PINTNETHDR)pSgBuf->pvAllocator;
+ IntNetRingCommitFrameEx(&pThis->CTX_SUFF(pBuf)->Send, pHdr, pSgBuf->cbUsed);
+ int rc = drvIntNetProcessXmit(pThis);
+ STAM_PROFILE_STOP(&pThis->StatTransmit, a);
+
+ /*
+ * Free the descriptor and return.
+ */
+#ifdef IN_RING3
+ RTMemCacheFree(pThis->hSgCache, pSgBuf);
+#else
+ STAM_REL_COUNTER_INC(&pThis->StatSentR0);
+ pSgBuf->fFlags = 0;
+#endif
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
+ */
+PDMBOTHCBDECL(void) drvIntNetUp_EndXmit(PPDMINETWORKUP pInterface)
+{
+ PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, CTX_SUFF(INetworkUp));
+ ASMAtomicUoWriteBool(&pThis->fXmitOnXmitThread, false);
+ PDMDrvHlpCritSectLeave(pThis->CTX_SUFF(pDrvIns), &pThis->XmitLock);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
+ */
+PDMBOTHCBDECL(void) drvIntNetUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
+{
+ PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, CTX_SUFF(INetworkUp));
+
+#ifdef IN_RING3
+ INTNETIFSETPROMISCUOUSMODEREQ Req;
+ Req.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ Req.Hdr.cbReq = sizeof(Req);
+ Req.pSession = NIL_RTR0PTR;
+ Req.hIf = pThis->hIf;
+ Req.fPromiscuous = fPromiscuous;
+ int rc = drvR3IntNetCallSvc(pThis, VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE, &Req, sizeof(Req));
+#else /* IN_RING0 */
+ int rc = IntNetR0IfSetPromiscuousMode(pThis->hIf, pThis->pSupDrvSession, fPromiscuous);
+#endif /* IN_RING0 */
+
+ LogFlow(("drvIntNetUp_SetPromiscuousMode: fPromiscuous=%RTbool\n", fPromiscuous));
+ AssertRC(rc);
+}
+
+#ifdef IN_RING3
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnNotifyLinkChanged}
+ */
+static DECLCALLBACK(void) drvR3IntNetUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
+{
+ PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, CTX_SUFF(INetworkUp));
+ bool fLinkDown;
+ switch (enmLinkState)
+ {
+ case PDMNETWORKLINKSTATE_DOWN:
+ case PDMNETWORKLINKSTATE_DOWN_RESUME:
+ fLinkDown = true;
+ break;
+ default:
+ AssertMsgFailed(("enmLinkState=%d\n", enmLinkState));
+ RT_FALL_THRU();
+ case PDMNETWORKLINKSTATE_UP:
+ fLinkDown = false;
+ break;
+ }
+ LogFlow(("drvR3IntNetUp_NotifyLinkChanged: enmLinkState=%d %d->%d\n", enmLinkState, pThis->fLinkDown, fLinkDown));
+ ASMAtomicXchgSize(&pThis->fLinkDown, fLinkDown);
+}
+
+
+/* -=-=-=-=- Transmit Thread -=-=-=-=- */
+
+/**
+ * Async I/O thread for deferred packet transmission.
+ *
+ * @returns VBox status code. Returning failure will naturally terminate the thread.
+ * @param pDrvIns The internal networking driver instance.
+ * @param pThread The thread.
+ */
+static DECLCALLBACK(int) drvR3IntNetXmitThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
+
+ while (pThread->enmState == PDMTHREADSTATE_RUNNING)
+ {
+ /*
+ * Transmit any pending packets.
+ */
+ /** @todo Optimize this. We shouldn't call pfnXmitPending unless asked for.
+ * Also there is no need to call drvIntNetProcessXmit if we also
+ * called pfnXmitPending and send one or more frames. */
+ if (ASMAtomicXchgBool(&pThis->fXmitProcessRing, false))
+ {
+ STAM_REL_COUNTER_INC(&pThis->StatXmitProcessRing);
+ PDMDrvHlpCritSectEnter(pDrvIns, &pThis->XmitLock, VERR_IGNORED);
+ drvIntNetProcessXmit(pThis);
+ PDMDrvHlpCritSectLeave(pDrvIns, &pThis->XmitLock);
+ }
+
+ pThis->pIAboveNet->pfnXmitPending(pThis->pIAboveNet);
+
+ if (ASMAtomicXchgBool(&pThis->fXmitProcessRing, false))
+ {
+ STAM_REL_COUNTER_INC(&pThis->StatXmitProcessRing);
+ PDMDrvHlpCritSectEnter(pDrvIns, &pThis->XmitLock, VERR_IGNORED);
+ drvIntNetProcessXmit(pThis);
+ PDMDrvHlpCritSectLeave(pDrvIns, &pThis->XmitLock);
+ }
+
+ /*
+ * Block until we've got something to send or is supposed
+ * to leave the running state.
+ */
+ int rc = SUPSemEventWaitNoResume(pThis->pSupDrvSession, pThis->hXmitEvt, RT_INDEFINITE_WAIT);
+ AssertLogRelMsgReturn(RT_SUCCESS(rc) || rc == VERR_INTERRUPTED, ("%Rrc\n", rc), rc);
+ if (RT_UNLIKELY(pThread->enmState != PDMTHREADSTATE_RUNNING))
+ break;
+
+ }
+
+ /* The thread is being initialized, suspended or terminated. */
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @copydoc FNPDMTHREADWAKEUPDRV
+ */
+static DECLCALLBACK(int) drvR3IntNetXmitWakeUp(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ RT_NOREF(pThread);
+ PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
+ return SUPSemEventSignal(pThis->pSupDrvSession, pThis->hXmitEvt);
+}
+
+
+/* -=-=-=-=- Receive Thread -=-=-=-=- */
+
+/**
+ * Wait for space to become available up the driver/device chain.
+ *
+ * @returns VINF_SUCCESS if space is available.
+ * @returns VERR_STATE_CHANGED if the state changed.
+ * @returns VBox status code on other errors.
+ * @param pThis Pointer to the instance data.
+ */
+static int drvR3IntNetRecvWaitForSpace(PDRVINTNET pThis)
+{
+ LogFlow(("drvR3IntNetRecvWaitForSpace:\n"));
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+ int rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
+ STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
+ LogFlow(("drvR3IntNetRecvWaitForSpace: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Executes async I/O (RUNNING mode).
+ *
+ * @returns VERR_STATE_CHANGED if the state changed.
+ * @returns Appropriate VBox status code (error) on fatal error.
+ * @param pThis The driver instance data.
+ */
+static int drvR3IntNetRecvRun(PDRVINTNET pThis)
+{
+ LogFlow(("drvR3IntNetRecvRun: pThis=%p\n", pThis));
+
+ /*
+ * The running loop - processing received data and waiting for more to arrive.
+ */
+ STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
+ PINTNETBUF pBuf = pThis->CTX_SUFF(pBuf);
+ PINTNETRINGBUF pRingBuf = &pBuf->Recv;
+ for (;;)
+ {
+ /*
+ * Process the receive buffer.
+ */
+ PINTNETHDR pHdr;
+ while ((pHdr = IntNetRingGetNextFrameToRead(pRingBuf)) != NULL)
+ {
+ /*
+ * Check the state and then inspect the packet.
+ */
+ if (pThis->enmRecvState != RECVSTATE_RUNNING)
+ {
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+ LogFlow(("drvR3IntNetRecvRun: returns VERR_STATE_CHANGED (state changed - #0)\n"));
+ return VERR_STATE_CHANGED;
+ }
+
+ Log2(("pHdr=%p offRead=%#x: %.8Rhxs\n", pHdr, pRingBuf->offReadX, pHdr));
+ uint8_t u8Type = pHdr->u8Type;
+ if ( ( u8Type == INTNETHDR_TYPE_FRAME
+ || u8Type == INTNETHDR_TYPE_GSO)
+ && !pThis->fLinkDown)
+ {
+ /*
+ * Check if there is room for the frame and pass it up.
+ */
+ size_t cbFrame = pHdr->cbFrame;
+ int rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, 0);
+ if (rc == VINF_SUCCESS)
+ {
+ if (u8Type == INTNETHDR_TYPE_FRAME)
+ {
+ /*
+ * Normal frame.
+ */
+#ifdef LOG_ENABLED
+ if (LogIsEnabled())
+ {
+ uint64_t u64Now = RTTimeProgramNanoTS();
+ LogFlow(("drvR3IntNetRecvRun: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
+ cbFrame, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS));
+ pThis->u64LastReceiveTS = u64Now;
+ Log2(("drvR3IntNetRecvRun: cbFrame=%#x\n"
+ "%.*Rhxd\n",
+ cbFrame, cbFrame, IntNetHdrGetFramePtr(pHdr, pBuf)));
+ }
+#endif
+ rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, IntNetHdrGetFramePtr(pHdr, pBuf), cbFrame);
+ AssertRC(rc);
+
+ /* skip to the next frame. */
+ IntNetRingSkipFrame(pRingBuf);
+ }
+ else
+ {
+ /*
+ * Generic segment offload frame (INTNETHDR_TYPE_GSO).
+ */
+ STAM_COUNTER_INC(&pThis->StatReceivedGso);
+ PCPDMNETWORKGSO pGso = IntNetHdrGetGsoContext(pHdr, pBuf);
+ if (PDMNetGsoIsValid(pGso, cbFrame, cbFrame - sizeof(PDMNETWORKGSO)))
+ {
+ if ( !pThis->pIAboveNet->pfnReceiveGso
+ || RT_FAILURE(pThis->pIAboveNet->pfnReceiveGso(pThis->pIAboveNet,
+ (uint8_t *)(pGso + 1),
+ pHdr->cbFrame - sizeof(PDMNETWORKGSO),
+ pGso)))
+ {
+ /*
+ * This is where we do the offloading since this NIC
+ * does not support large receive offload (LRO).
+ */
+ cbFrame -= sizeof(PDMNETWORKGSO);
+
+ uint8_t abHdrScratch[256];
+ uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, cbFrame);
+#ifdef LOG_ENABLED
+ if (LogIsEnabled())
+ {
+ uint64_t u64Now = RTTimeProgramNanoTS();
+ LogFlow(("drvR3IntNetRecvRun: %-4d bytes at %llu ns deltas: r=%llu t=%llu; GSO - %u segs\n",
+ cbFrame, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS, cSegs));
+ pThis->u64LastReceiveTS = u64Now;
+ Log2(("drvR3IntNetRecvRun: cbFrame=%#x type=%d cbHdrsTotal=%#x cbHdrsSeg=%#x Hdr1=%#x Hdr2=%#x MMS=%#x\n"
+ "%.*Rhxd\n",
+ cbFrame, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg, pGso->offHdr1, pGso->offHdr2, pGso->cbMaxSeg,
+ cbFrame - sizeof(*pGso), pGso + 1));
+ }
+#endif
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ uint32_t cbSegFrame;
+ void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)(pGso + 1), cbFrame,
+ abHdrScratch, iSeg, cSegs, &cbSegFrame);
+ rc = drvR3IntNetRecvWaitForSpace(pThis);
+ if (RT_FAILURE(rc))
+ {
+ Log(("drvR3IntNetRecvRun: drvR3IntNetRecvWaitForSpace -> %Rrc; iSeg=%u cSegs=%u\n", rc, iSeg, cSegs));
+ break; /* we drop the rest. */
+ }
+ rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pvSegFrame, cbSegFrame);
+ AssertRC(rc);
+ }
+ }
+ }
+ else
+ {
+ AssertMsgFailed(("cbFrame=%#x type=%d cbHdrsTotal=%#x cbHdrsSeg=%#x Hdr1=%#x Hdr2=%#x MMS=%#x\n",
+ cbFrame, pGso->u8Type, pGso->cbHdrsTotal, pGso->cbHdrsSeg, pGso->offHdr1, pGso->offHdr2, pGso->cbMaxSeg));
+ STAM_REL_COUNTER_INC(&pBuf->cStatBadFrames);
+ }
+
+ IntNetRingSkipFrame(pRingBuf);
+ }
+ }
+ else
+ {
+ /*
+ * Wait for sufficient space to become available and then retry.
+ */
+ rc = drvR3IntNetRecvWaitForSpace(pThis);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_INTERRUPTED)
+ {
+ /*
+ * NIC is going down, likely because the VM is being reset. Skip the frame.
+ */
+ AssertMsg(IntNetIsValidFrameType(pHdr->u8Type), ("Unknown frame type %RX16! offRead=%#x\n", pHdr->u8Type, pRingBuf->offReadX));
+ IntNetRingSkipFrame(pRingBuf);
+ }
+ else
+ {
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+ LogFlow(("drvR3IntNetRecvRun: returns %Rrc (wait-for-space)\n", rc));
+ return rc;
+ }
+ }
+ }
+ }
+ else
+ {
+ /*
+ * Link down or unknown frame - skip to the next frame.
+ */
+ AssertMsg(IntNetIsValidFrameType(pHdr->u8Type), ("Unknown frame type %RX16! offRead=%#x\n", pHdr->u8Type, pRingBuf->offReadX));
+ IntNetRingSkipFrame(pRingBuf);
+ STAM_REL_COUNTER_INC(&pBuf->cStatBadFrames);
+ }
+ } /* while more received data */
+
+ /*
+ * Wait for data, checking the state before we block.
+ */
+ if (pThis->enmRecvState != RECVSTATE_RUNNING)
+ {
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+ LogFlow(("drvR3IntNetRecvRun: returns VINF_SUCCESS (state changed - #1)\n"));
+ return VERR_STATE_CHANGED;
+ }
+ INTNETIFWAITREQ WaitReq;
+ WaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ WaitReq.Hdr.cbReq = sizeof(WaitReq);
+ WaitReq.pSession = NIL_RTR0PTR;
+ WaitReq.hIf = pThis->hIf;
+ WaitReq.cMillies = 30000; /* 30s - don't wait forever, timeout now and then. */
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+
+#if defined(RT_OS_DARWIN) && defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
+ if (pThis->fIntNetR3Svc)
+ {
+ /* Send an asynchronous message. */
+ int rc = drvR3IntNetCallSvcAsync(pThis, VMMR0_DO_INTNET_IF_WAIT, &WaitReq, sizeof(WaitReq));
+ if (RT_SUCCESS(rc))
+ {
+ /* Wait on the receive semaphore. */
+ rc = RTSemEventWait(pThis->hRecvEvt, 30 * RT_MS_1SEC);
+ if ( RT_FAILURE(rc)
+ && rc != VERR_TIMEOUT
+ && rc != VERR_INTERRUPTED)
+ {
+ LogFlow(("drvR3IntNetRecvRun: returns %Rrc\n", rc));
+ return rc;
+ }
+ }
+ }
+ else
+#endif
+ {
+ int rc = PDMDrvHlpSUPCallVMMR0Ex(pThis->pDrvInsR3, VMMR0_DO_INTNET_IF_WAIT, &WaitReq, sizeof(WaitReq));
+ if ( RT_FAILURE(rc)
+ && rc != VERR_TIMEOUT
+ && rc != VERR_INTERRUPTED)
+ {
+ LogFlow(("drvR3IntNetRecvRun: returns %Rrc\n", rc));
+ return rc;
+ }
+ }
+ STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
+ }
+}
+
+
+/**
+ * Asynchronous I/O thread for handling receive.
+ *
+ * @returns VINF_SUCCESS (ignored).
+ * @param hThreadSelf Thread handle.
+ * @param pvUser Pointer to a DRVINTNET structure.
+ */
+static DECLCALLBACK(int) drvR3IntNetRecvThread(RTTHREAD hThreadSelf, void *pvUser)
+{
+ RT_NOREF(hThreadSelf);
+ PDRVINTNET pThis = (PDRVINTNET)pvUser;
+ LogFlow(("drvR3IntNetRecvThread: pThis=%p\n", pThis));
+ STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
+
+ /*
+ * The main loop - acting on state.
+ */
+ for (;;)
+ {
+ RECVSTATE enmRecvState = pThis->enmRecvState;
+ switch (enmRecvState)
+ {
+ case RECVSTATE_SUSPENDED:
+ {
+ int rc = RTSemEventWait(pThis->hRecvEvt, 30000);
+ if ( RT_FAILURE(rc)
+ && rc != VERR_TIMEOUT)
+ {
+ LogFlow(("drvR3IntNetRecvThread: returns %Rrc\n", rc));
+ return rc;
+ }
+ break;
+ }
+
+ case RECVSTATE_RUNNING:
+ {
+ int rc = drvR3IntNetRecvRun(pThis);
+ if ( rc != VERR_STATE_CHANGED
+ && RT_FAILURE(rc))
+ {
+ LogFlow(("drvR3IntNetRecvThread: returns %Rrc\n", rc));
+ return rc;
+ }
+ break;
+ }
+
+ default:
+ AssertMsgFailed(("Invalid state %d\n", enmRecvState));
+ RT_FALL_THRU();
+ case RECVSTATE_TERMINATE:
+ LogFlow(("drvR3IntNetRecvThread: returns VINF_SUCCESS\n"));
+ return VINF_SUCCESS;
+ }
+ }
+}
+
+
+#ifdef VBOX_WITH_DRVINTNET_IN_R0
+
+/* -=-=-=-=- PDMIBASERC -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASERC,pfnQueryInterface}
+ */
+static DECLCALLBACK(RTRCPTR) drvR3IntNetIBaseRC_QueryInterface(PPDMIBASERC pInterface, const char *pszIID)
+{
+ PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, IBaseRC);
+#if 0
+ PDMIBASERC_RETURN_INTERFACE(pThis->pDrvInsR3, pszIID, PDMINETWORKUP, &pThis->INetworkUpRC);
+#else
+ RT_NOREF(pThis, pszIID);
+#endif
+ return NIL_RTRCPTR;
+}
+
+
+/* -=-=-=-=- PDMIBASER0 -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASER0,pfnQueryInterface}
+ */
+static DECLCALLBACK(RTR0PTR) drvR3IntNetIBaseR0_QueryInterface(PPDMIBASER0 pInterface, const char *pszIID)
+{
+ PDRVINTNET pThis = RT_FROM_MEMBER(pInterface, DRVINTNET, IBaseR0);
+ PDMIBASER0_RETURN_INTERFACE(pThis->pDrvInsR3, pszIID, PDMINETWORKUP, &pThis->INetworkUpR0);
+ return NIL_RTR0PTR;
+}
+
+#endif /* VBOX_WITH_DRVINTNET_IN_R0 */
+
+/* -=-=-=-=- PDMIBASE -=-=-=-=- */
+
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvR3IntNetIBase_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+ PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
+
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+#ifdef VBOX_WITH_DRVINTNET_IN_R0
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASER0, &pThis->IBaseR0);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASERC, &pThis->IBaseRC);
+#endif
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUpR3);
+ return NULL;
+}
+
+
+/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
+
+/**
+ * Power Off notification.
+ *
+ * @param pDrvIns The driver instance.
+ */
+static DECLCALLBACK(void) drvR3IntNetPowerOff(PPDMDRVINS pDrvIns)
+{
+ LogFlow(("drvR3IntNetPowerOff\n"));
+ PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
+ if (!pThis->fActivateEarlyDeactivateLate)
+ {
+ ASMAtomicXchgSize(&pThis->enmRecvState, RECVSTATE_SUSPENDED);
+ drvR3IntNetSetActive(pThis, false /* fActive */);
+ }
+}
+
+
+/**
+ * drvR3IntNetResume helper.
+ */
+static int drvR3IntNetResumeSend(PDRVINTNET pThis, const void *pvBuf, size_t cb)
+{
+ /*
+ * Add the frame to the send buffer and push it onto the network.
+ */
+ int rc = IntNetRingWriteFrame(&pThis->pBufR3->Send, pvBuf, (uint32_t)cb);
+ if ( rc == VERR_BUFFER_OVERFLOW
+ && pThis->pBufR3->cbSend < cb)
+ {
+ INTNETIFSENDREQ SendReq;
+ SendReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ SendReq.Hdr.cbReq = sizeof(SendReq);
+ SendReq.pSession = NIL_RTR0PTR;
+ SendReq.hIf = pThis->hIf;
+ drvR3IntNetCallSvc(pThis, VMMR0_DO_INTNET_IF_SEND, &SendReq, sizeof(SendReq));
+
+ rc = IntNetRingWriteFrame(&pThis->pBufR3->Send, pvBuf, (uint32_t)cb);
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ INTNETIFSENDREQ SendReq;
+ SendReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ SendReq.Hdr.cbReq = sizeof(SendReq);
+ SendReq.pSession = NIL_RTR0PTR;
+ SendReq.hIf = pThis->hIf;
+ rc = drvR3IntNetCallSvc(pThis, VMMR0_DO_INTNET_IF_SEND, &SendReq, sizeof(SendReq));
+ }
+
+ AssertRC(rc);
+ return rc;
+}
+
+
+/**
+ * Resume notification.
+ *
+ * @param pDrvIns The driver instance.
+ */
+static DECLCALLBACK(void) drvR3IntNetResume(PPDMDRVINS pDrvIns)
+{
+ LogFlow(("drvR3IntNetPowerResume\n"));
+ PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
+ VMRESUMEREASON enmReason = PDMDrvHlpVMGetResumeReason(pDrvIns);
+
+ if (!pThis->fActivateEarlyDeactivateLate)
+ {
+ ASMAtomicXchgSize(&pThis->enmRecvState, RECVSTATE_RUNNING);
+ RTSemEventSignal(pThis->hRecvEvt);
+ drvR3IntNetUpdateMacAddress(pThis); /* (could be a state restore) */
+ drvR3IntNetSetActive(pThis, true /* fActive */);
+ }
+
+ switch (enmReason)
+ {
+ case VMRESUMEREASON_HOST_RESUME:
+ {
+ uint32_t u32TrunkType;
+ int rc = pDrvIns->pHlpR3->pfnCFGMQueryU32(pDrvIns->pCfg, "TrunkType", &u32TrunkType);
+ AssertRC(rc);
+
+ /*
+ * Only do the disconnect for bridged networking. Host-only and
+ * internal networks are not affected by a host resume.
+ */
+ if ( RT_SUCCESS(rc)
+ && u32TrunkType == kIntNetTrunkType_NetFlt)
+ {
+ rc = pThis->pIAboveConfigR3->pfnSetLinkState(pThis->pIAboveConfigR3,
+ PDMNETWORKLINKSTATE_DOWN_RESUME);
+ AssertRC(rc);
+ }
+ break;
+ }
+ case VMRESUMEREASON_TELEPORTED:
+ case VMRESUMEREASON_TELEPORT_FAILED:
+ {
+ if ( PDMDrvHlpVMTeleportedAndNotFullyResumedYet(pDrvIns)
+ && pThis->pIAboveConfigR3)
+ {
+ /*
+ * We've just been teleported and need to drop a hint to the switch
+ * since we're likely to have changed to a different port. We just
+ * push out some ethernet frame that doesn't mean anything to anyone.
+ * For this purpose ethertype 0x801e was chosen since it was registered
+ * to Sun (dunno what it is/was used for though).
+ */
+ union
+ {
+ RTNETETHERHDR Hdr;
+ uint8_t ab[128];
+ } Frame;
+ RT_ZERO(Frame);
+ Frame.Hdr.DstMac.au16[0] = 0xffff;
+ Frame.Hdr.DstMac.au16[1] = 0xffff;
+ Frame.Hdr.DstMac.au16[2] = 0xffff;
+ Frame.Hdr.EtherType = RT_H2BE_U16_C(0x801e);
+ int rc = pThis->pIAboveConfigR3->pfnGetMac(pThis->pIAboveConfigR3,
+ &Frame.Hdr.SrcMac);
+ if (RT_SUCCESS(rc))
+ rc = drvR3IntNetResumeSend(pThis, &Frame, sizeof(Frame));
+ if (RT_FAILURE(rc))
+ LogRel(("IntNet#%u: Sending dummy frame failed: %Rrc\n",
+ pDrvIns->iInstance, rc));
+ }
+ break;
+ }
+ default: /* ignore every other resume reason else */
+ break;
+ } /* end of switch(enmReason) */
+}
+
+
+/**
+ * Suspend notification.
+ *
+ * @param pDrvIns The driver instance.
+ */
+static DECLCALLBACK(void) drvR3IntNetSuspend(PPDMDRVINS pDrvIns)
+{
+ LogFlow(("drvR3IntNetPowerSuspend\n"));
+ PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
+ if (!pThis->fActivateEarlyDeactivateLate)
+ {
+ ASMAtomicXchgSize(&pThis->enmRecvState, RECVSTATE_SUSPENDED);
+ drvR3IntNetSetActive(pThis, false /* fActive */);
+ }
+}
+
+
+/**
+ * Power On notification.
+ *
+ * @param pDrvIns The driver instance.
+ */
+static DECLCALLBACK(void) drvR3IntNetPowerOn(PPDMDRVINS pDrvIns)
+{
+ LogFlow(("drvR3IntNetPowerOn\n"));
+ PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
+ if (!pThis->fActivateEarlyDeactivateLate)
+ {
+ ASMAtomicXchgSize(&pThis->enmRecvState, RECVSTATE_RUNNING);
+ RTSemEventSignal(pThis->hRecvEvt);
+ drvR3IntNetUpdateMacAddress(pThis);
+ drvR3IntNetSetActive(pThis, true /* fActive */);
+ }
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnRelocate}
+ */
+static DECLCALLBACK(void) drvR3IntNetRelocate(PPDMDRVINS pDrvIns, RTGCINTPTR offDelta)
+{
+ /* nothing to do here yet */
+ RT_NOREF(pDrvIns, offDelta);
+}
+
+
+/**
+ * Destruct a driver instance.
+ *
+ * Most VM resources are freed by the VM. This callback is provided so that any non-VM
+ * resources can be freed correctly.
+ *
+ * @param pDrvIns The driver instance data.
+ */
+static DECLCALLBACK(void) drvR3IntNetDestruct(PPDMDRVINS pDrvIns)
+{
+ LogFlow(("drvR3IntNetDestruct\n"));
+ PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+
+ /*
+ * Indicate to the receive thread that it's time to quit.
+ */
+ ASMAtomicXchgSize(&pThis->enmRecvState, RECVSTATE_TERMINATE);
+ ASMAtomicXchgSize(&pThis->fLinkDown, true);
+ RTSEMEVENT hRecvEvt = pThis->hRecvEvt;
+ pThis->hRecvEvt = NIL_RTSEMEVENT;
+
+ if (hRecvEvt != NIL_RTSEMEVENT)
+ RTSemEventSignal(hRecvEvt);
+
+ if (pThis->hIf != INTNET_HANDLE_INVALID)
+ {
+#if defined(RT_OS_DARWIN) && defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
+ if (!pThis->fIntNetR3Svc) /* The R3 service case is handled b the hRecEvt event semaphore. */
+#endif
+ {
+ INTNETIFABORTWAITREQ AbortWaitReq;
+ AbortWaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ AbortWaitReq.Hdr.cbReq = sizeof(AbortWaitReq);
+ AbortWaitReq.pSession = NIL_RTR0PTR;
+ AbortWaitReq.hIf = pThis->hIf;
+ AbortWaitReq.fNoMoreWaits = true;
+ int rc = drvR3IntNetCallSvc(pThis, VMMR0_DO_INTNET_IF_ABORT_WAIT, &AbortWaitReq, sizeof(AbortWaitReq));
+ AssertMsg(RT_SUCCESS(rc) || rc == VERR_SEM_DESTROYED, ("%Rrc\n", rc)); RT_NOREF_PV(rc);
+ }
+ }
+
+ /*
+ * Wait for the threads to terminate.
+ */
+ if (pThis->pXmitThread)
+ {
+ int rc = PDMDrvHlpThreadDestroy(pDrvIns, pThis->pXmitThread, NULL);
+ AssertRC(rc);
+ pThis->pXmitThread = NULL;
+ }
+
+ if (pThis->hRecvThread != NIL_RTTHREAD)
+ {
+ int rc = RTThreadWait(pThis->hRecvThread, 5000, NULL);
+ AssertRC(rc);
+ pThis->hRecvThread = NIL_RTTHREAD;
+ }
+
+ /*
+ * Deregister statistics in case we're being detached.
+ */
+ if (pThis->pBufR3)
+ {
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->Recv.cStatFrames);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->Recv.cbStatWritten);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->Recv.cOverflows);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->Send.cStatFrames);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->Send.cbStatWritten);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->Send.cOverflows);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->cStatYieldsOk);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->cStatYieldsNok);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->cStatLost);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->cStatBadFrames);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->StatSend1);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->StatSend2);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->StatRecv1);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->StatRecv2);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->pBufR3->StatReserved);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReceivedGso);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatSentGso);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatSentR0);
+#ifdef VBOX_WITH_STATISTICS
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReceive);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatTransmit);
+#endif
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatXmitWakeupR0);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatXmitWakeupR3);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatXmitProcessRing);
+ }
+
+ /*
+ * Close the interface
+ */
+ if (pThis->hIf != INTNET_HANDLE_INVALID)
+ {
+ INTNETIFCLOSEREQ CloseReq;
+ CloseReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ CloseReq.Hdr.cbReq = sizeof(CloseReq);
+ CloseReq.pSession = NIL_RTR0PTR;
+ CloseReq.hIf = pThis->hIf;
+ pThis->hIf = INTNET_HANDLE_INVALID;
+ int rc = drvR3IntNetCallSvc(pThis, VMMR0_DO_INTNET_IF_CLOSE, &CloseReq, sizeof(CloseReq));
+ AssertRC(rc);
+ }
+
+#if defined(RT_OS_DARWIN) && defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
+ if (pThis->fIntNetR3Svc)
+ {
+ /* Unmap the shared buffer. */
+ munmap(pThis->pBufR3, pThis->cbBuf);
+ xpc_connection_cancel(pThis->hXpcCon);
+ pThis->fIntNetR3Svc = false;
+ pThis->hXpcCon = NULL;
+ }
+#endif
+
+ /*
+ * Destroy the semaphores, S/G cache and xmit lock.
+ */
+ if (hRecvEvt != NIL_RTSEMEVENT)
+ RTSemEventDestroy(hRecvEvt);
+
+ if (pThis->hXmitEvt != NIL_SUPSEMEVENT)
+ {
+ SUPSemEventClose(pThis->pSupDrvSession, pThis->hXmitEvt);
+ pThis->hXmitEvt = NIL_SUPSEMEVENT;
+ }
+
+ RTMemCacheDestroy(pThis->hSgCache);
+ pThis->hSgCache = NIL_RTMEMCACHE;
+
+ if (PDMDrvHlpCritSectIsInitialized(pDrvIns, &pThis->XmitLock))
+ PDMDrvHlpCritSectDelete(pDrvIns, &pThis->XmitLock);
+}
+
+
+/**
+ * Queries a policy config value and translates it into open network flag.
+ *
+ * @returns VBox status code (error set on failure).
+ * @param pDrvIns The driver instance.
+ * @param pszName The value name.
+ * @param paFlags The open network flag descriptors.
+ * @param cFlags The number of descriptors.
+ * @param fFlags The fixed flag.
+ * @param pfFlags The flags variable to update.
+ */
+static int drvIntNetR3CfgGetPolicy(PPDMDRVINS pDrvIns, const char *pszName, PCDRVINTNETFLAG paFlags, size_t cFlags,
+ uint32_t fFixedFlag, uint32_t *pfFlags)
+{
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ char szValue[64];
+ int rc = pHlp->pfnCFGMQueryString(pDrvIns->pCfg, pszName, szValue, sizeof(szValue));
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+ return VINF_SUCCESS;
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("Configuration error: Failed to query value of \"%s\""), pszName);
+ }
+
+ /*
+ * Check for +fixed first, so it can be stripped off.
+ */
+ char *pszSep = strpbrk(szValue, "+,;");
+ if (pszSep)
+ {
+ *pszSep++ = '\0';
+ const char *pszFixed = RTStrStripL(pszSep);
+ if (strcmp(pszFixed, "fixed"))
+ {
+ *pszSep = '+';
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
+ N_("Configuration error: The value of \"%s\" is unknown: \"%s\""), pszName, szValue);
+ }
+ *pfFlags |= fFixedFlag;
+ RTStrStripR(szValue);
+ }
+
+ /*
+ * Match against the flag values.
+ */
+ size_t i = cFlags;
+ while (i-- > 0)
+ if (!strcmp(paFlags[i].pszChoice, szValue))
+ {
+ *pfFlags |= paFlags[i].fFlag;
+ return VINF_SUCCESS;
+ }
+
+ if (!strcmp(szValue, "none"))
+ return VINF_SUCCESS;
+
+ if (!strcmp(szValue, "fixed"))
+ {
+ *pfFlags |= fFixedFlag;
+ return VINF_SUCCESS;
+ }
+
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
+ N_("Configuration error: The value of \"%s\" is unknown: \"%s\""), pszName, szValue);
+}
+
+
+/**
+ * Construct a TAP network transport driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(int) drvR3IntNetConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ RT_NOREF(fFlags);
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVINTNET pThis = PDMINS_2_DATA(pDrvIns, PDRVINTNET);
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+ bool f;
+
+ /*
+ * Init the static parts.
+ */
+ pThis->pDrvInsR3 = pDrvIns;
+#ifdef VBOX_WITH_DRVINTNET_IN_R0
+ pThis->pDrvInsR0 = PDMDRVINS_2_R0PTR(pDrvIns);
+#endif
+ pThis->hIf = INTNET_HANDLE_INVALID;
+ pThis->hRecvThread = NIL_RTTHREAD;
+ pThis->hRecvEvt = NIL_RTSEMEVENT;
+ pThis->pXmitThread = NULL;
+ pThis->hXmitEvt = NIL_SUPSEMEVENT;
+ pThis->pSupDrvSession = PDMDrvHlpGetSupDrvSession(pDrvIns);
+ pThis->hSgCache = NIL_RTMEMCACHE;
+ pThis->enmRecvState = RECVSTATE_SUSPENDED;
+ pThis->fActivateEarlyDeactivateLate = false;
+ /* IBase* */
+ pDrvIns->IBase.pfnQueryInterface = drvR3IntNetIBase_QueryInterface;
+#ifdef VBOX_WITH_DRVINTNET_IN_R0
+ pThis->IBaseR0.pfnQueryInterface = drvR3IntNetIBaseR0_QueryInterface;
+ pThis->IBaseRC.pfnQueryInterface = drvR3IntNetIBaseRC_QueryInterface;
+#endif
+ /* INetworkUp */
+ pThis->INetworkUpR3.pfnBeginXmit = drvIntNetUp_BeginXmit;
+ pThis->INetworkUpR3.pfnAllocBuf = drvIntNetUp_AllocBuf;
+ pThis->INetworkUpR3.pfnFreeBuf = drvIntNetUp_FreeBuf;
+ pThis->INetworkUpR3.pfnSendBuf = drvIntNetUp_SendBuf;
+ pThis->INetworkUpR3.pfnEndXmit = drvIntNetUp_EndXmit;
+ pThis->INetworkUpR3.pfnSetPromiscuousMode = drvIntNetUp_SetPromiscuousMode;
+ pThis->INetworkUpR3.pfnNotifyLinkChanged = drvR3IntNetUp_NotifyLinkChanged;
+
+ /*
+ * Validate the config.
+ */
+ PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns,
+ "Network"
+ "|Trunk"
+ "|TrunkType"
+ "|ReceiveBufferSize"
+ "|SendBufferSize"
+ "|SharedMacOnWire"
+ "|RestrictAccess"
+ "|RequireExactPolicyMatch"
+ "|RequireAsRestrictivePolicy"
+ "|AccessPolicy"
+ "|PromiscPolicyClients"
+ "|PromiscPolicyHost"
+ "|PromiscPolicyWire"
+ "|IfPolicyPromisc"
+ "|TrunkPolicyHost"
+ "|TrunkPolicyWire"
+ "|IsService"
+ "|IgnoreConnectFailure"
+ "|Workaround1",
+ "");
+
+ /*
+ * Check that no-one is attached to us.
+ */
+ AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
+ ("Configuration error: Not possible to attach anything to this driver!\n"),
+ VERR_PDM_DRVINS_NO_ATTACH);
+
+ /*
+ * Query the network port interface.
+ */
+ pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
+ if (!pThis->pIAboveNet)
+ {
+ AssertMsgFailed(("Configuration error: the above device/driver didn't export the network port interface!\n"));
+ return VERR_PDM_MISSING_INTERFACE_ABOVE;
+ }
+ pThis->pIAboveConfigR3 = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
+
+ /*
+ * Read the configuration.
+ */
+ INTNETOPENREQ OpenReq;
+ RT_ZERO(OpenReq);
+ OpenReq.Hdr.cbReq = sizeof(OpenReq);
+ OpenReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ OpenReq.pSession = NIL_RTR0PTR;
+
+ /** @cfgm{Network, string}
+ * The name of the internal network to connect to.
+ */
+ int rc = pHlp->pfnCFGMQueryString(pCfg, "Network", OpenReq.szNetwork, sizeof(OpenReq.szNetwork));
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"Network\" value"));
+ strcpy(pThis->szNetwork, OpenReq.szNetwork);
+
+ /** @cfgm{TrunkType, uint32_t, kIntNetTrunkType_None}
+ * The trunk connection type see INTNETTRUNKTYPE.
+ */
+ uint32_t u32TrunkType;
+ rc = pHlp->pfnCFGMQueryU32(pCfg, "TrunkType", &u32TrunkType);
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+ u32TrunkType = kIntNetTrunkType_None;
+ else if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"TrunkType\" value"));
+ OpenReq.enmTrunkType = (INTNETTRUNKTYPE)u32TrunkType;
+
+ /** @cfgm{Trunk, string, ""}
+ * The name of the trunk connection.
+ */
+ rc = pHlp->pfnCFGMQueryString(pCfg, "Trunk", OpenReq.szTrunk, sizeof(OpenReq.szTrunk));
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+ OpenReq.szTrunk[0] = '\0';
+ else if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"Trunk\" value"));
+
+ OpenReq.fFlags = 0;
+
+ /** @cfgm{SharedMacOnWire, boolean, false}
+ * Whether to shared the MAC address of the host interface when using the wire. When
+ * attaching to a wireless NIC this option is usually a requirement.
+ */
+ bool fSharedMacOnWire;
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "SharedMacOnWire", &fSharedMacOnWire, false);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"SharedMacOnWire\" value"));
+ if (fSharedMacOnWire)
+ OpenReq.fFlags |= INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE;
+
+ /** @cfgm{RestrictAccess, boolean, true}
+ * Whether to restrict the access to the network or if it should be public.
+ * Everyone on the computer can connect to a public network.
+ * @deprecated Use AccessPolicy instead.
+ */
+ rc = pHlp->pfnCFGMQueryBool(pCfg, "RestrictAccess", &f);
+ if (RT_SUCCESS(rc))
+ {
+ if (f)
+ OpenReq.fFlags |= INTNET_OPEN_FLAGS_ACCESS_RESTRICTED;
+ else
+ OpenReq.fFlags |= INTNET_OPEN_FLAGS_ACCESS_PUBLIC;
+ OpenReq.fFlags |= INTNET_OPEN_FLAGS_ACCESS_FIXED;
+ }
+ else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"RestrictAccess\" value"));
+
+ /** @cfgm{RequireExactPolicyMatch, boolean, false}
+ * Whether to require that the current security and promiscuous policies of
+ * the network is exactly as the ones specified in this open network
+ * request. Use this with RequireAsRestrictivePolicy to prevent
+ * restrictions from being lifted. If no further policy changes are
+ * desired, apply the relevant fixed flags. */
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "RequireExactPolicyMatch", &f, false);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"RequireExactPolicyMatch\" value"));
+ if (f)
+ OpenReq.fFlags |= INTNET_OPEN_FLAGS_REQUIRE_EXACT;
+
+ /** @cfgm{RequireAsRestrictivePolicy, boolean, false}
+ * Whether to require that the security and promiscuous policies of the
+ * network is at least as restrictive as specified this request specifies
+ * and prevent them being lifted later on.
+ */
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "RequireAsRestrictivePolicy", &f, false);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"RequireAsRestrictivePolicy\" value"));
+ if (f)
+ OpenReq.fFlags |= INTNET_OPEN_FLAGS_REQUIRE_AS_RESTRICTIVE_POLICIES;
+
+ /** @cfgm{AccessPolicy, string, "none"}
+ * The access policy of the network:
+ * public, public+fixed, restricted, restricted+fixed, none or fixed.
+ *
+ * A "public" network is accessible to everyone on the same host, while a
+ * "restricted" one is only accessible to VMs & services started by the
+ * same user. The "none" policy, which is the default, means no policy
+ * change or choice is made and that the current (existing network) or
+ * default (new) policy should be used. */
+ static const DRVINTNETFLAG s_aAccessPolicyFlags[] =
+ {
+ { "public", INTNET_OPEN_FLAGS_ACCESS_PUBLIC },
+ { "restricted", INTNET_OPEN_FLAGS_ACCESS_RESTRICTED }
+ };
+ rc = drvIntNetR3CfgGetPolicy(pDrvIns, "AccessPolicy", &s_aAccessPolicyFlags[0], RT_ELEMENTS(s_aAccessPolicyFlags),
+ INTNET_OPEN_FLAGS_ACCESS_FIXED, &OpenReq.fFlags);
+ AssertRCReturn(rc, rc);
+
+ /** @cfgm{PromiscPolicyClients, string, "none"}
+ * The network wide promiscuous mode policy for client (non-trunk)
+ * interfaces: allow, allow+fixed, deny, deny+fixed, none or fixed. */
+ static const DRVINTNETFLAG s_aPromiscPolicyClient[] =
+ {
+ { "allow", INTNET_OPEN_FLAGS_PROMISC_ALLOW_CLIENTS },
+ { "deny", INTNET_OPEN_FLAGS_PROMISC_DENY_CLIENTS }
+ };
+ rc = drvIntNetR3CfgGetPolicy(pDrvIns, "PromiscPolicyClients", &s_aPromiscPolicyClient[0], RT_ELEMENTS(s_aPromiscPolicyClient),
+ INTNET_OPEN_FLAGS_PROMISC_FIXED, &OpenReq.fFlags);
+ AssertRCReturn(rc, rc);
+ /** @cfgm{PromiscPolicyHost, string, "none"}
+ * The promiscuous mode policy for the trunk-host
+ * connection: allow, allow+fixed, deny, deny+fixed, none or fixed. */
+ static const DRVINTNETFLAG s_aPromiscPolicyHost[] =
+ {
+ { "allow", INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_HOST },
+ { "deny", INTNET_OPEN_FLAGS_PROMISC_DENY_TRUNK_HOST }
+ };
+ rc = drvIntNetR3CfgGetPolicy(pDrvIns, "PromiscPolicyHost", &s_aPromiscPolicyHost[0], RT_ELEMENTS(s_aPromiscPolicyHost),
+ INTNET_OPEN_FLAGS_PROMISC_FIXED, &OpenReq.fFlags);
+ AssertRCReturn(rc, rc);
+ /** @cfgm{PromiscPolicyWire, string, "none"}
+ * The promiscuous mode policy for the trunk-host
+ * connection: allow, allow+fixed, deny, deny+fixed, none or fixed. */
+ static const DRVINTNETFLAG s_aPromiscPolicyWire[] =
+ {
+ { "allow", INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_WIRE },
+ { "deny", INTNET_OPEN_FLAGS_PROMISC_DENY_TRUNK_WIRE }
+ };
+ rc = drvIntNetR3CfgGetPolicy(pDrvIns, "PromiscPolicyWire", &s_aPromiscPolicyWire[0], RT_ELEMENTS(s_aPromiscPolicyWire),
+ INTNET_OPEN_FLAGS_PROMISC_FIXED, &OpenReq.fFlags);
+ AssertRCReturn(rc, rc);
+
+
+ /** @cfgm{IfPolicyPromisc, string, "none"}
+ * The promiscuous mode policy for this
+ * interface: deny, deny+fixed, allow-all, allow-all+fixed, allow-network,
+ * allow-network+fixed, none or fixed. */
+ static const DRVINTNETFLAG s_aIfPolicyPromisc[] =
+ {
+ { "allow-all", INTNET_OPEN_FLAGS_IF_PROMISC_ALLOW | INTNET_OPEN_FLAGS_IF_PROMISC_SEE_TRUNK },
+ { "allow-network", INTNET_OPEN_FLAGS_IF_PROMISC_ALLOW | INTNET_OPEN_FLAGS_IF_PROMISC_NO_TRUNK },
+ { "deny", INTNET_OPEN_FLAGS_IF_PROMISC_DENY }
+ };
+ rc = drvIntNetR3CfgGetPolicy(pDrvIns, "IfPolicyPromisc", &s_aIfPolicyPromisc[0], RT_ELEMENTS(s_aIfPolicyPromisc),
+ INTNET_OPEN_FLAGS_IF_FIXED, &OpenReq.fFlags);
+ AssertRCReturn(rc, rc);
+
+
+ /** @cfgm{TrunkPolicyHost, string, "none"}
+ * The trunk-host policy: promisc, promisc+fixed, enabled, enabled+fixed,
+ * disabled, disabled+fixed, none or fixed
+ *
+ * This can be used to prevent packages to be routed to the host. */
+ static const DRVINTNETFLAG s_aTrunkPolicyHost[] =
+ {
+ { "promisc", INTNET_OPEN_FLAGS_TRUNK_HOST_ENABLED | INTNET_OPEN_FLAGS_TRUNK_HOST_PROMISC_MODE },
+ { "enabled", INTNET_OPEN_FLAGS_TRUNK_HOST_ENABLED },
+ { "disabled", INTNET_OPEN_FLAGS_TRUNK_HOST_DISABLED }
+ };
+ rc = drvIntNetR3CfgGetPolicy(pDrvIns, "TrunkPolicyHost", &s_aTrunkPolicyHost[0], RT_ELEMENTS(s_aTrunkPolicyHost),
+ INTNET_OPEN_FLAGS_TRUNK_FIXED, &OpenReq.fFlags);
+ AssertRCReturn(rc, rc);
+ /** @cfgm{TrunkPolicyWire, string, "none"}
+ * The trunk-host policy: promisc, promisc+fixed, enabled, enabled+fixed,
+ * disabled, disabled+fixed, none or fixed.
+ *
+ * This can be used to prevent packages to be routed to the wire. */
+ static const DRVINTNETFLAG s_aTrunkPolicyWire[] =
+ {
+ { "promisc", INTNET_OPEN_FLAGS_TRUNK_WIRE_ENABLED | INTNET_OPEN_FLAGS_TRUNK_WIRE_PROMISC_MODE },
+ { "enabled", INTNET_OPEN_FLAGS_TRUNK_WIRE_ENABLED },
+ { "disabled", INTNET_OPEN_FLAGS_TRUNK_WIRE_DISABLED }
+ };
+ rc = drvIntNetR3CfgGetPolicy(pDrvIns, "TrunkPolicyWire", &s_aTrunkPolicyWire[0], RT_ELEMENTS(s_aTrunkPolicyWire),
+ INTNET_OPEN_FLAGS_TRUNK_FIXED, &OpenReq.fFlags);
+ AssertRCReturn(rc, rc);
+
+
+ /** @cfgm{ReceiveBufferSize, uint32_t, 318 KB}
+ * The size of the receive buffer.
+ */
+ rc = pHlp->pfnCFGMQueryU32(pCfg, "ReceiveBufferSize", &OpenReq.cbRecv);
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+ OpenReq.cbRecv = 318 * _1K ;
+ else if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"ReceiveBufferSize\" value"));
+
+ /** @cfgm{SendBufferSize, uint32_t, 196 KB}
+ * The size of the send (transmit) buffer.
+ * This should be more than twice the size of the larges frame size because
+ * the ring buffer is very simple and doesn't support splitting up frames
+ * nor inserting padding. So, if this is too close to the frame size the
+ * header will fragment the buffer such that the frame won't fit on either
+ * side of it and the code will get very upset about it all.
+ */
+ rc = pHlp->pfnCFGMQueryU32(pCfg, "SendBufferSize", &OpenReq.cbSend);
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+ OpenReq.cbSend = RT_ALIGN_Z(VBOX_MAX_GSO_SIZE * 3, _1K);
+ else if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"SendBufferSize\" value"));
+ if (OpenReq.cbSend < 128)
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: The \"SendBufferSize\" value is too small"));
+ if (OpenReq.cbSend < VBOX_MAX_GSO_SIZE * 3)
+ LogRel(("DrvIntNet: Warning! SendBufferSize=%u, Recommended minimum size %u butes.\n", OpenReq.cbSend, VBOX_MAX_GSO_SIZE * 4));
+
+ /** @cfgm{IsService, boolean, true}
+ * This alterns the way the thread is suspended and resumed. When it's being used by
+ * a service such as LWIP/iSCSI it shouldn't suspend immediately like for a NIC.
+ */
+ rc = pHlp->pfnCFGMQueryBool(pCfg, "IsService", &pThis->fActivateEarlyDeactivateLate);
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+ pThis->fActivateEarlyDeactivateLate = false;
+ else if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"IsService\" value"));
+
+
+ /** @cfgm{IgnoreConnectFailure, boolean, false}
+ * When set only raise a runtime error if we cannot connect to the internal
+ * network. */
+ bool fIgnoreConnectFailure;
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "IgnoreConnectFailure", &fIgnoreConnectFailure, false);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"IgnoreConnectFailure\" value"));
+
+ /** @cfgm{Workaround1, boolean, depends}
+ * Enables host specific workarounds, the default is depends on the whether
+ * we think the host requires it or not.
+ */
+ bool fWorkaround1 = false;
+#ifdef RT_OS_DARWIN
+ if (OpenReq.fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
+ {
+ char szKrnlVer[256];
+ RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szKrnlVer, sizeof(szKrnlVer));
+ if (strcmp(szKrnlVer, "10.7.0") >= 0)
+ {
+ LogRel(("IntNet#%u: Enables the workaround (ip_tos=0) for the little endian ip header checksum problem\n"));
+ fWorkaround1 = true;
+ }
+ }
+#endif
+ rc = pHlp->pfnCFGMQueryBoolDef(pCfg, "Workaround1", &fWorkaround1, fWorkaround1);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"Workaround1\" value"));
+ if (fWorkaround1)
+ OpenReq.fFlags |= INTNET_OPEN_FLAGS_WORKAROUND_1;
+
+ LogRel(("IntNet#%u: szNetwork={%s} enmTrunkType=%d szTrunk={%s} fFlags=%#x cbRecv=%u cbSend=%u fIgnoreConnectFailure=%RTbool\n",
+ pDrvIns->iInstance, OpenReq.szNetwork, OpenReq.enmTrunkType, OpenReq.szTrunk, OpenReq.fFlags,
+ OpenReq.cbRecv, OpenReq.cbSend, fIgnoreConnectFailure));
+
+#ifdef RT_OS_DARWIN
+ /* Temporary hack: attach to a network with the name 'if=en0' and you're hitting the wire. */
+ if ( !OpenReq.szTrunk[0]
+ && OpenReq.enmTrunkType == kIntNetTrunkType_None
+ && !strncmp(pThis->szNetwork, RT_STR_TUPLE("if=en"))
+ && RT_C_IS_DIGIT(pThis->szNetwork[sizeof("if=en") - 1])
+ && !pThis->szNetwork[sizeof("if=en")])
+ {
+ OpenReq.enmTrunkType = kIntNetTrunkType_NetFlt;
+ strcpy(OpenReq.szTrunk, &pThis->szNetwork[sizeof("if=") - 1]);
+ }
+ /* Temporary hack: attach to a network with the name 'wif=en0' and you're on the air. */
+ if ( !OpenReq.szTrunk[0]
+ && OpenReq.enmTrunkType == kIntNetTrunkType_None
+ && !strncmp(pThis->szNetwork, RT_STR_TUPLE("wif=en"))
+ && RT_C_IS_DIGIT(pThis->szNetwork[sizeof("wif=en") - 1])
+ && !pThis->szNetwork[sizeof("wif=en")])
+ {
+ OpenReq.enmTrunkType = kIntNetTrunkType_NetFlt;
+ OpenReq.fFlags |= INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE;
+ strcpy(OpenReq.szTrunk, &pThis->szNetwork[sizeof("wif=") - 1]);
+ }
+#endif /* DARWIN */
+
+ /*
+ * Create the event semaphore, S/G cache and xmit critsect.
+ */
+ rc = RTSemEventCreate(&pThis->hRecvEvt);
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = RTMemCacheCreate(&pThis->hSgCache, sizeof(PDMSCATTERGATHER), 0, UINT32_MAX, NULL, NULL, pThis, 0);
+ if (RT_FAILURE(rc))
+ return rc;
+ rc = PDMDrvHlpCritSectInit(pDrvIns, &pThis->XmitLock, RT_SRC_POS, "IntNetXmit");
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Create the interface.
+ */
+ if (SUPR3IsDriverless())
+ {
+#if defined(RT_OS_DARWIN) && defined(VBOX_WITH_INTNET_SERVICE_IN_R3)
+ xpc_connection_t hXpcCon = xpc_connection_create(INTNET_R3_SVC_NAME, NULL);
+ xpc_connection_set_event_handler(hXpcCon, ^(xpc_object_t hObj) {
+ if (xpc_get_type(hObj) == XPC_TYPE_ERROR)
+ {
+ /** @todo Error handling - reconnecting. */
+ }
+ else
+ {
+ /* Out of band messages should only come when there is something to receive. */
+ RTSemEventSignal(pThis->hRecvEvt);
+ }
+ });
+
+ xpc_connection_resume(hXpcCon);
+ pThis->hXpcCon = hXpcCon;
+ pThis->fIntNetR3Svc = true;
+#else
+ /** @todo This is probably not good enough for doing fuzz testing, but later... */
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_SUP_DRIVERLESS, RT_SRC_POS,
+ N_("Cannot attach to '%s' in driverless mode"), pThis->szNetwork);
+#endif
+ }
+ OpenReq.hIf = INTNET_HANDLE_INVALID;
+ rc = drvR3IntNetCallSvc(pThis, VMMR0_DO_INTNET_OPEN, &OpenReq, sizeof(OpenReq));
+ if (RT_FAILURE(rc))
+ {
+ if (fIgnoreConnectFailure)
+ {
+ /*
+ * During VM restore it is fatal if the network is not available because the
+ * VM settings are locked and the user has no chance to fix network settings.
+ * Therefore don't abort but just raise a runtime warning.
+ */
+ PDMDrvHlpVMSetRuntimeError(pDrvIns, 0 /*fFlags*/, "HostIfNotConnecting",
+ N_ ("Cannot connect to the network interface '%s'. The virtual "
+ "network card will appear to work but the guest will not "
+ "be able to connect. Please choose a different network in the "
+ "network settings"), OpenReq.szTrunk);
+
+ return VERR_PDM_NO_ATTACHED_DRIVER;
+ }
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("Failed to open/create the internal network '%s'"), pThis->szNetwork);
+ }
+
+ AssertRelease(OpenReq.hIf != INTNET_HANDLE_INVALID);
+ pThis->hIf = OpenReq.hIf;
+ Log(("IntNet%d: hIf=%RX32 '%s'\n", pDrvIns->iInstance, pThis->hIf, pThis->szNetwork));
+
+ /*
+ * Get default buffer.
+ */
+ rc = drvR3IntNetMapBufferPointers(pThis);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("Failed to get ring-3 buffer for the newly created interface to '%s'"), pThis->szNetwork);
+
+ /*
+ * Register statistics.
+ */
+ PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->pBufR3->Recv.cbStatWritten, "Bytes/Received", STAMUNIT_BYTES, "Number of received bytes.");
+ PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->pBufR3->Send.cbStatWritten, "Bytes/Sent", STAMUNIT_BYTES, "Number of sent bytes.");
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->pBufR3->Recv.cOverflows, "Overflows/Recv", "Number overflows.");
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->pBufR3->Send.cOverflows, "Overflows/Sent", "Number overflows.");
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->pBufR3->Recv.cStatFrames, "Packets/Received", "Number of received packets.");
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->pBufR3->Send.cStatFrames, "Packets/Sent", "Number of sent packets.");
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->StatReceivedGso, "Packets/Received-Gso", "The GSO portion of the received packets.");
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->StatSentGso, "Packets/Sent-Gso", "The GSO portion of the sent packets.");
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->StatSentR0, "Packets/Sent-R0", "The ring-0 portion of the sent packets.");
+
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->pBufR3->cStatLost, "Packets/Lost", "Number of lost packets.");
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->pBufR3->cStatYieldsNok, "YieldOk", "Number of times yielding helped fix an overflow.");
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->pBufR3->cStatYieldsOk, "YieldNok", "Number of times yielding didn't help fix an overflow.");
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->pBufR3->cStatBadFrames, "BadFrames", "Number of bad frames seed by the consumers.");
+ PDMDrvHlpSTAMRegProfile(pDrvIns, &pThis->pBufR3->StatSend1, "Send1", "Profiling IntNetR0IfSend.");
+ PDMDrvHlpSTAMRegProfile(pDrvIns, &pThis->pBufR3->StatSend2, "Send2", "Profiling sending to the trunk.");
+ PDMDrvHlpSTAMRegProfile(pDrvIns, &pThis->pBufR3->StatRecv1, "Recv1", "Reserved for future receive profiling.");
+ PDMDrvHlpSTAMRegProfile(pDrvIns, &pThis->pBufR3->StatRecv2, "Recv2", "Reserved for future receive profiling.");
+ PDMDrvHlpSTAMRegProfile(pDrvIns, &pThis->pBufR3->StatReserved, "Reserved", "Reserved for future use.");
+#ifdef VBOX_WITH_STATISTICS
+ PDMDrvHlpSTAMRegProfileAdv(pDrvIns, &pThis->StatReceive, "Receive", "Profiling packet receive runs.");
+ PDMDrvHlpSTAMRegProfile(pDrvIns, &pThis->StatTransmit, "Transmit", "Profiling packet transmit runs.");
+#endif
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->StatXmitWakeupR0, "XmitWakeup-R0", "Xmit thread wakeups from ring-0.");
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->StatXmitWakeupR3, "XmitWakeup-R3", "Xmit thread wakeups from ring-3.");
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->StatXmitProcessRing, "XmitProcessRing", "Time xmit thread was told to process the ring.");
+
+ /*
+ * Create the async I/O threads.
+ * Note! Using a PDM thread here doesn't fit with the IsService=true operation.
+ */
+ rc = RTThreadCreate(&pThis->hRecvThread, drvR3IntNetRecvThread, pThis, 0,
+ RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "INTNET-RECV");
+ if (RT_FAILURE(rc))
+ {
+ AssertRC(rc);
+ return rc;
+ }
+
+ rc = SUPSemEventCreate(pThis->pSupDrvSession, &pThis->hXmitEvt);
+ AssertRCReturn(rc, rc);
+
+ rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pXmitThread, pThis,
+ drvR3IntNetXmitThread, drvR3IntNetXmitWakeUp, 0, RTTHREADTYPE_IO, "INTNET-XMIT");
+ AssertRCReturn(rc, rc);
+
+#ifdef VBOX_WITH_DRVINTNET_IN_R0
+ /*
+ * Resolve the ring-0 context interface addresses.
+ */
+ rc = pDrvIns->pHlpR3->pfnLdrGetR0InterfaceSymbols(pDrvIns, &pThis->INetworkUpR0, sizeof(pThis->INetworkUpR0),
+ "drvIntNetUp_", PDMINETWORKUP_SYM_LIST);
+ AssertLogRelRCReturn(rc, rc);
+#endif
+
+ /*
+ * Activate data transmission as early as possible
+ */
+ if (pThis->fActivateEarlyDeactivateLate)
+ {
+ ASMAtomicXchgSize(&pThis->enmRecvState, RECVSTATE_RUNNING);
+ RTSemEventSignal(pThis->hRecvEvt);
+
+ drvR3IntNetUpdateMacAddress(pThis);
+ drvR3IntNetSetActive(pThis, true /* fActive */);
+ }
+
+ return rc;
+}
+
+
+
+/**
+ * Internal networking transport driver registration record.
+ */
+const PDMDRVREG g_DrvIntNet =
+{
+ /* u32Version */
+ PDM_DRVREG_VERSION,
+ /* szName */
+ "IntNet",
+ /* szRCMod */
+ "VBoxDDRC.rc",
+ /* szR0Mod */
+ "VBoxDDR0.r0",
+ /* pszDescription */
+ "Internal Networking Transport Driver",
+ /* fFlags */
+#ifdef VBOX_WITH_DRVINTNET_IN_R0
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DRVREG_FLAGS_R0,
+#else
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+#endif
+ /* fClass. */
+ PDM_DRVREG_CLASS_NETWORK,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(DRVINTNET),
+ /* pfnConstruct */
+ drvR3IntNetConstruct,
+ /* pfnDestruct */
+ drvR3IntNetDestruct,
+ /* pfnRelocate */
+ drvR3IntNetRelocate,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ drvR3IntNetPowerOn,
+ /* pfnReset */
+ NULL,
+ /* pfnSuspend */
+ drvR3IntNetSuspend,
+ /* pfnResume */
+ drvR3IntNetResume,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnPowerOff */
+ drvR3IntNetPowerOff,
+ /* pfnSoftReset */
+ NULL,
+ /* u32EndVersion */
+ PDM_DRVREG_VERSION
+};
+
+#endif /* IN_RING3 */
+
diff --git a/src/VBox/Devices/Network/DrvNAT.cpp b/src/VBox/Devices/Network/DrvNAT.cpp
new file mode 100644
index 00000000..16e36e5a
--- /dev/null
+++ b/src/VBox/Devices/Network/DrvNAT.cpp
@@ -0,0 +1,1915 @@
+/* $Id: DrvNAT.cpp $ */
+/** @file
+ * DrvNAT - NAT network transport driver.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_NAT
+#define __STDC_LIMIT_MACROS
+#define __STDC_CONSTANT_MACROS
+#include "slirp/libslirp.h"
+extern "C" {
+#include "slirp/slirp_dns.h"
+}
+#include "slirp/ctl.h"
+
+#include <VBox/vmm/dbgf.h>
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmnetifs.h>
+#include <VBox/vmm/pdmnetinline.h>
+
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/cidr.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/pipe.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/uuid.h>
+
+#include "VBoxDD.h"
+
+#ifndef RT_OS_WINDOWS
+# include <unistd.h>
+# include <fcntl.h>
+# include <poll.h>
+# include <errno.h>
+#endif
+#ifdef RT_OS_FREEBSD
+# include <netinet/in.h>
+#endif
+#include <iprt/semaphore.h>
+#include <iprt/req.h>
+#ifdef RT_OS_DARWIN
+# include <SystemConfiguration/SystemConfiguration.h>
+# include <CoreFoundation/CoreFoundation.h>
+#endif
+
+#define COUNTERS_INIT
+#include "counters.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+
+#define DRVNAT_MAXFRAMESIZE (16 * 1024)
+
+/**
+ * @todo: This is a bad hack to prevent freezing the guest during high network
+ * activity. Windows host only. This needs to be fixed properly.
+ */
+#define VBOX_NAT_DELAY_HACK
+
+#define GET_EXTRADATA(pdrvins, node, name, rc, type, type_name, var) \
+do { \
+ (rc) = (pdrvins)->pHlpR3->pfnCFGMQuery ## type((node), name, &(var)); \
+ if (RT_FAILURE((rc)) && (rc) != VERR_CFGM_VALUE_NOT_FOUND) \
+ return PDMDrvHlpVMSetError((pdrvins), (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \"" name "\" " #type_name " failed"), \
+ (pdrvins)->iInstance); \
+} while (0)
+
+#define GET_ED_STRICT(pdrvins, node, name, rc, type, type_name, var) \
+do { \
+ (rc) = (pdrvins)->pHlpR3->pfnCFGMQuery ## type((node), name, &(var)); \
+ if (RT_FAILURE((rc))) \
+ return PDMDrvHlpVMSetError((pdrvins), (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \"" name "\" " #type_name " failed"), \
+ (pdrvins)->iInstance); \
+} while (0)
+
+#define GET_EXTRADATA_N(pdrvins, node, name, rc, type, type_name, var, var_size) \
+do { \
+ (rc) = (pdrvins)->pHlpR3->pfnCFGMQuery ## type((node), name, &(var), var_size); \
+ if (RT_FAILURE((rc)) && (rc) != VERR_CFGM_VALUE_NOT_FOUND) \
+ return PDMDrvHlpVMSetError((pdrvins), (rc), RT_SRC_POS, N_("NAT#%d: configuration query for \"" name "\" " #type_name " failed"), \
+ (pdrvins)->iInstance); \
+} while (0)
+
+#define GET_BOOL(rc, pdrvins, node, name, var) \
+ GET_EXTRADATA(pdrvins, node, name, (rc), Bool, bolean, (var))
+#define GET_STRING(rc, pdrvins, node, name, var, var_size) \
+ GET_EXTRADATA_N(pdrvins, node, name, (rc), String, string, (var), (var_size))
+#define GET_STRING_ALLOC(rc, pdrvins, node, name, var) \
+ GET_EXTRADATA(pdrvins, node, name, (rc), StringAlloc, string, (var))
+#define GET_S32(rc, pdrvins, node, name, var) \
+ GET_EXTRADATA(pdrvins, node, name, (rc), S32, int, (var))
+#define GET_S32_STRICT(rc, pdrvins, node, name, var) \
+ GET_ED_STRICT(pdrvins, node, name, (rc), S32, int, (var))
+
+
+
+#define DO_GET_IP(rc, node, instance, status, x) \
+do { \
+ char sz##x[32]; \
+ GET_STRING((rc), (node), (instance), #x, sz ## x[0], sizeof(sz ## x)); \
+ if (rc != VERR_CFGM_VALUE_NOT_FOUND) \
+ (status) = inet_aton(sz ## x, &x); \
+} while (0)
+
+#define GETIP_DEF(rc, node, instance, x, def) \
+do \
+{ \
+ int status = 0; \
+ DO_GET_IP((rc), (node), (instance), status, x); \
+ if (status == 0 || rc == VERR_CFGM_VALUE_NOT_FOUND) \
+ x.s_addr = def; \
+} while (0)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * NAT network transport driver instance data.
+ *
+ * @implements PDMINETWORKUP
+ */
+typedef struct DRVNAT
+{
+ /** The network interface. */
+ PDMINETWORKUP INetworkUp;
+ /** The network NAT Engine configureation. */
+ PDMINETWORKNATCONFIG INetworkNATCfg;
+ /** The port we're attached to. */
+ PPDMINETWORKDOWN pIAboveNet;
+ /** The network config of the port we're attached to. */
+ PPDMINETWORKCONFIG pIAboveConfig;
+ /** Pointer to the driver instance. */
+ PPDMDRVINS pDrvIns;
+ /** Link state */
+ PDMNETWORKLINKSTATE enmLinkState;
+ /** NAT state for this instance. */
+ PNATState pNATState;
+ /** TFTP directory prefix. */
+ char *pszTFTPPrefix;
+ /** Boot file name to provide in the DHCP server response. */
+ char *pszBootFile;
+ /** tftp server name to provide in the DHCP server response. */
+ char *pszNextServer;
+ /** Polling thread. */
+ PPDMTHREAD pSlirpThread;
+ /** Queue for NAT-thread-external events. */
+ RTREQQUEUE hSlirpReqQueue;
+ /** The guest IP for port-forwarding. */
+ uint32_t GuestIP;
+ /** Link state set when the VM is suspended. */
+ PDMNETWORKLINKSTATE enmLinkStateWant;
+
+#ifndef RT_OS_WINDOWS
+ /** The write end of the control pipe. */
+ RTPIPE hPipeWrite;
+ /** The read end of the control pipe. */
+ RTPIPE hPipeRead;
+# if HC_ARCH_BITS == 32
+ uint32_t u32Padding;
+# endif
+#else
+ /** for external notification */
+ HANDLE hWakeupEvent;
+#endif
+
+#define DRV_PROFILE_COUNTER(name, dsc) STAMPROFILE Stat ## name
+#define DRV_COUNTING_COUNTER(name, dsc) STAMCOUNTER Stat ## name
+#include "counters.h"
+ /** thread delivering packets for receiving by the guest */
+ PPDMTHREAD pRecvThread;
+ /** thread delivering urg packets for receiving by the guest */
+ PPDMTHREAD pUrgRecvThread;
+ /** event to wakeup the guest receive thread */
+ RTSEMEVENT EventRecv;
+ /** event to wakeup the guest urgent receive thread */
+ RTSEMEVENT EventUrgRecv;
+ /** Receive Req queue (deliver packets to the guest) */
+ RTREQQUEUE hRecvReqQueue;
+ /** Receive Urgent Req queue (deliver packets to the guest). */
+ RTREQQUEUE hUrgRecvReqQueue;
+
+ /** makes access to device func RecvAvail and Recv atomical. */
+ RTCRITSECT DevAccessLock;
+ /** Number of in-flight urgent packets. */
+ volatile uint32_t cUrgPkts;
+ /** Number of in-flight regular packets. */
+ volatile uint32_t cPkts;
+
+ /** Transmit lock taken by BeginXmit and released by EndXmit. */
+ RTCRITSECT XmitLock;
+
+ /** Request queue for the async host resolver. */
+ RTREQQUEUE hHostResQueue;
+ /** Async host resolver thread. */
+ PPDMTHREAD pHostResThread;
+
+#ifdef RT_OS_DARWIN
+ /* Handle of the DNS watcher runloop source. */
+ CFRunLoopSourceRef hRunLoopSrcDnsWatcher;
+#endif
+} DRVNAT;
+AssertCompileMemberAlignment(DRVNAT, StatNATRecvWakeups, 8);
+/** Pointer to the NAT driver instance data. */
+typedef DRVNAT *PDRVNAT;
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+static void drvNATNotifyNATThread(PDRVNAT pThis, const char *pszWho);
+DECLINLINE(void) drvNATUpdateDNS(PDRVNAT pThis, bool fFlapLink);
+static DECLCALLBACK(int) drvNATReinitializeHostNameResolving(PDRVNAT pThis);
+
+
+/**
+ * @callback_method_impl{FNPDMTHREADDRV}
+ */
+static DECLCALLBACK(int) drvNATRecv(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
+
+ if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
+ return VINF_SUCCESS;
+
+ while (pThread->enmState == PDMTHREADSTATE_RUNNING)
+ {
+ RTReqQueueProcess(pThis->hRecvReqQueue, 0);
+ if (ASMAtomicReadU32(&pThis->cPkts) == 0)
+ RTSemEventWait(pThis->EventRecv, RT_INDEFINITE_WAIT);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNPDMTHREADWAKEUPDRV}
+ */
+static DECLCALLBACK(int) drvNATRecvWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ RT_NOREF(pThread);
+ PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
+ int rc;
+ rc = RTSemEventSignal(pThis->EventRecv);
+
+ STAM_COUNTER_INC(&pThis->StatNATRecvWakeups);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNPDMTHREADDRV}
+ */
+static DECLCALLBACK(int) drvNATUrgRecv(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
+
+ if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
+ return VINF_SUCCESS;
+
+ while (pThread->enmState == PDMTHREADSTATE_RUNNING)
+ {
+ RTReqQueueProcess(pThis->hUrgRecvReqQueue, 0);
+ if (ASMAtomicReadU32(&pThis->cUrgPkts) == 0)
+ {
+ int rc = RTSemEventWait(pThis->EventUrgRecv, RT_INDEFINITE_WAIT);
+ AssertRC(rc);
+ }
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @callback_method_impl{FNPDMTHREADWAKEUPDRV}
+ */
+static DECLCALLBACK(int) drvNATUrgRecvWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ RT_NOREF(pThread);
+ PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
+ int rc = RTSemEventSignal(pThis->EventUrgRecv);
+ AssertRC(rc);
+
+ return VINF_SUCCESS;
+}
+
+
+static DECLCALLBACK(void) drvNATUrgRecvWorker(PDRVNAT pThis, uint8_t *pu8Buf, int cb, struct mbuf *m)
+{
+ int rc = RTCritSectEnter(&pThis->DevAccessLock);
+ AssertRC(rc);
+ rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pu8Buf, cb);
+ AssertRC(rc);
+ }
+ else if ( rc != VERR_TIMEOUT
+ && rc != VERR_INTERRUPTED)
+ {
+ AssertRC(rc);
+ }
+
+ rc = RTCritSectLeave(&pThis->DevAccessLock);
+ AssertRC(rc);
+
+ slirp_ext_m_free(pThis->pNATState, m, pu8Buf);
+ if (ASMAtomicDecU32(&pThis->cUrgPkts) == 0)
+ {
+ drvNATRecvWakeup(pThis->pDrvIns, pThis->pRecvThread);
+ drvNATNotifyNATThread(pThis, "drvNATUrgRecvWorker");
+ }
+}
+
+
+static DECLCALLBACK(void) drvNATRecvWorker(PDRVNAT pThis, uint8_t *pu8Buf, int cb, struct mbuf *m)
+{
+ int rc;
+ STAM_PROFILE_START(&pThis->StatNATRecv, a);
+
+
+ while (ASMAtomicReadU32(&pThis->cUrgPkts) != 0)
+ {
+ rc = RTSemEventWait(pThis->EventRecv, RT_INDEFINITE_WAIT);
+ if ( RT_FAILURE(rc)
+ && ( rc == VERR_TIMEOUT
+ || rc == VERR_INTERRUPTED))
+ goto done_unlocked;
+ }
+
+ rc = RTCritSectEnter(&pThis->DevAccessLock);
+ AssertRC(rc);
+
+ STAM_PROFILE_START(&pThis->StatNATRecvWait, b);
+ rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
+ STAM_PROFILE_STOP(&pThis->StatNATRecvWait, b);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pu8Buf, cb);
+ AssertRC(rc);
+ }
+ else if ( rc != VERR_TIMEOUT
+ && rc != VERR_INTERRUPTED)
+ {
+ AssertRC(rc);
+ }
+
+ rc = RTCritSectLeave(&pThis->DevAccessLock);
+ AssertRC(rc);
+
+done_unlocked:
+ slirp_ext_m_free(pThis->pNATState, m, pu8Buf);
+ ASMAtomicDecU32(&pThis->cPkts);
+
+ drvNATNotifyNATThread(pThis, "drvNATRecvWorker");
+
+ STAM_PROFILE_STOP(&pThis->StatNATRecv, a);
+}
+
+/**
+ * Frees a S/G buffer allocated by drvNATNetworkUp_AllocBuf.
+ *
+ * @param pThis Pointer to the NAT instance.
+ * @param pSgBuf The S/G buffer to free.
+ */
+static void drvNATFreeSgBuf(PDRVNAT pThis, PPDMSCATTERGATHER pSgBuf)
+{
+ Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
+ pSgBuf->fFlags = 0;
+ if (pSgBuf->pvAllocator)
+ {
+ Assert(!pSgBuf->pvUser);
+ slirp_ext_m_free(pThis->pNATState, (struct mbuf *)pSgBuf->pvAllocator, NULL);
+ pSgBuf->pvAllocator = NULL;
+ }
+ else if (pSgBuf->pvUser)
+ {
+ RTMemFree(pSgBuf->aSegs[0].pvSeg);
+ pSgBuf->aSegs[0].pvSeg = NULL;
+ RTMemFree(pSgBuf->pvUser);
+ pSgBuf->pvUser = NULL;
+ }
+ RTMemFree(pSgBuf);
+}
+
+/**
+ * Worker function for drvNATSend().
+ *
+ * @param pThis Pointer to the NAT instance.
+ * @param pSgBuf The scatter/gather buffer.
+ * @thread NAT
+ */
+static DECLCALLBACK(void) drvNATSendWorker(PDRVNAT pThis, PPDMSCATTERGATHER pSgBuf)
+{
+#if 0 /* Assertion happens often to me after resuming a VM -- no time to investigate this now. */
+ Assert(pThis->enmLinkState == PDMNETWORKLINKSTATE_UP);
+#endif
+ if (pThis->enmLinkState == PDMNETWORKLINKSTATE_UP)
+ {
+ struct mbuf *m = (struct mbuf *)pSgBuf->pvAllocator;
+ if (m)
+ {
+ /*
+ * A normal frame.
+ */
+ pSgBuf->pvAllocator = NULL;
+ slirp_input(pThis->pNATState, m, pSgBuf->cbUsed);
+ }
+ else
+ {
+ /*
+ * GSO frame, need to segment it.
+ */
+ /** @todo Make the NAT engine grok large frames? Could be more efficient... */
+#if 0 /* this is for testing PDMNetGsoCarveSegmentQD. */
+ uint8_t abHdrScratch[256];
+#endif
+ uint8_t const *pbFrame = (uint8_t const *)pSgBuf->aSegs[0].pvSeg;
+ PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pSgBuf->pvUser;
+ /* Do not attempt to segment frames with invalid GSO parameters. */
+ if (PDMNetGsoIsValid(pGso, sizeof(*pGso), pSgBuf->cbUsed))
+ {
+ uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, pSgBuf->cbUsed); Assert(cSegs > 1);
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ size_t cbSeg;
+ void *pvSeg;
+ m = slirp_ext_m_get(pThis->pNATState, pGso->cbHdrsTotal + pGso->cbMaxSeg, &pvSeg, &cbSeg);
+ if (!m)
+ break;
+
+#if 1
+ uint32_t cbPayload, cbHdrs;
+ uint32_t offPayload = PDMNetGsoCarveSegment(pGso, pbFrame, pSgBuf->cbUsed,
+ iSeg, cSegs, (uint8_t *)pvSeg, &cbHdrs, &cbPayload);
+ memcpy((uint8_t *)pvSeg + cbHdrs, pbFrame + offPayload, cbPayload);
+
+ slirp_input(pThis->pNATState, m, cbPayload + cbHdrs);
+#else
+ uint32_t cbSegFrame;
+ void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)pbFrame, pSgBuf->cbUsed, abHdrScratch,
+ iSeg, cSegs, &cbSegFrame);
+ memcpy((uint8_t *)pvSeg, pvSegFrame, cbSegFrame);
+
+ slirp_input(pThis->pNATState, m, cbSegFrame);
+#endif
+ }
+ }
+ }
+ }
+ drvNATFreeSgBuf(pThis, pSgBuf);
+
+ /** @todo Implement the VERR_TRY_AGAIN drvNATNetworkUp_AllocBuf semantics. */
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
+ */
+static DECLCALLBACK(int) drvNATNetworkUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
+{
+ RT_NOREF(fOnWorkerThread);
+ PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
+ int rc = RTCritSectTryEnter(&pThis->XmitLock);
+ if (RT_FAILURE(rc))
+ {
+ /** @todo Kick the worker thread when we have one... */
+ rc = VERR_TRY_AGAIN;
+ }
+ return rc;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
+ */
+static DECLCALLBACK(int) drvNATNetworkUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
+ PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
+{
+ PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
+ Assert(RTCritSectIsOwner(&pThis->XmitLock));
+
+ /*
+ * Drop the incoming frame if the NAT thread isn't running.
+ */
+ if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
+ {
+ Log(("drvNATNetowrkUp_AllocBuf: returns VERR_NET_NO_NETWORK\n"));
+ return VERR_NET_NO_NETWORK;
+ }
+
+ /*
+ * Allocate a scatter/gather buffer and an mbuf.
+ */
+ PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAlloc(sizeof(*pSgBuf));
+ if (!pSgBuf)
+ return VERR_NO_MEMORY;
+ if (!pGso)
+ {
+ /*
+ * Drop the frame if it is too big.
+ */
+ if (cbMin >= DRVNAT_MAXFRAMESIZE)
+ {
+ Log(("drvNATNetowrkUp_AllocBuf: drops over-sized frame (%u bytes), returns VERR_INVALID_PARAMETER\n",
+ cbMin));
+ RTMemFree(pSgBuf);
+ return VERR_INVALID_PARAMETER;
+ }
+
+ pSgBuf->pvUser = NULL;
+ pSgBuf->pvAllocator = slirp_ext_m_get(pThis->pNATState, cbMin,
+ &pSgBuf->aSegs[0].pvSeg, &pSgBuf->aSegs[0].cbSeg);
+ if (!pSgBuf->pvAllocator)
+ {
+ RTMemFree(pSgBuf);
+ return VERR_TRY_AGAIN;
+ }
+ }
+ else
+ {
+ /*
+ * Drop the frame if its segment is too big.
+ */
+ if (pGso->cbHdrsTotal + pGso->cbMaxSeg >= DRVNAT_MAXFRAMESIZE)
+ {
+ Log(("drvNATNetowrkUp_AllocBuf: drops over-sized frame (%u bytes), returns VERR_INVALID_PARAMETER\n",
+ pGso->cbHdrsTotal + pGso->cbMaxSeg));
+ RTMemFree(pSgBuf);
+ return VERR_INVALID_PARAMETER;
+ }
+
+ pSgBuf->pvUser = RTMemDup(pGso, sizeof(*pGso));
+ pSgBuf->pvAllocator = NULL;
+ pSgBuf->aSegs[0].cbSeg = RT_ALIGN_Z(cbMin, 16);
+ pSgBuf->aSegs[0].pvSeg = RTMemAlloc(pSgBuf->aSegs[0].cbSeg);
+ if (!pSgBuf->pvUser || !pSgBuf->aSegs[0].pvSeg)
+ {
+ RTMemFree(pSgBuf->aSegs[0].pvSeg);
+ RTMemFree(pSgBuf->pvUser);
+ RTMemFree(pSgBuf);
+ return VERR_TRY_AGAIN;
+ }
+ }
+
+ /*
+ * Initialize the S/G buffer and return.
+ */
+ pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
+ pSgBuf->cbUsed = 0;
+ pSgBuf->cbAvailable = pSgBuf->aSegs[0].cbSeg;
+ pSgBuf->cSegs = 1;
+
+#if 0 /* poison */
+ memset(pSgBuf->aSegs[0].pvSeg, 'F', pSgBuf->aSegs[0].cbSeg);
+#endif
+ *ppSgBuf = pSgBuf;
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
+ */
+static DECLCALLBACK(int) drvNATNetworkUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
+{
+ PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
+ Assert(RTCritSectIsOwner(&pThis->XmitLock));
+ drvNATFreeSgBuf(pThis, pSgBuf);
+ return VINF_SUCCESS;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
+ */
+static DECLCALLBACK(int) drvNATNetworkUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
+{
+ RT_NOREF(fOnWorkerThread);
+ PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
+ Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_OWNER_MASK) == PDMSCATTERGATHER_FLAGS_OWNER_1);
+ Assert(RTCritSectIsOwner(&pThis->XmitLock));
+
+ int rc;
+ if (pThis->pSlirpThread->enmState == PDMTHREADSTATE_RUNNING)
+ {
+ rc = RTReqQueueCallEx(pThis->hSlirpReqQueue, NULL /*ppReq*/, 0 /*cMillies*/,
+ RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
+ (PFNRT)drvNATSendWorker, 2, pThis, pSgBuf);
+ if (RT_SUCCESS(rc))
+ {
+ drvNATNotifyNATThread(pThis, "drvNATNetworkUp_SendBuf");
+ return VINF_SUCCESS;
+ }
+
+ rc = VERR_NET_NO_BUFFER_SPACE;
+ }
+ else
+ rc = VERR_NET_DOWN;
+ drvNATFreeSgBuf(pThis, pSgBuf);
+ return rc;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
+ */
+static DECLCALLBACK(void) drvNATNetworkUp_EndXmit(PPDMINETWORKUP pInterface)
+{
+ PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
+ RTCritSectLeave(&pThis->XmitLock);
+}
+
+/**
+ * Get the NAT thread out of poll/WSAWaitForMultipleEvents
+ */
+static void drvNATNotifyNATThread(PDRVNAT pThis, const char *pszWho)
+{
+ RT_NOREF(pszWho);
+ int rc;
+#ifndef RT_OS_WINDOWS
+ /* kick poll() */
+ size_t cbIgnored;
+ rc = RTPipeWrite(pThis->hPipeWrite, "", 1, &cbIgnored);
+#else
+ /* kick WSAWaitForMultipleEvents */
+ rc = WSASetEvent(pThis->hWakeupEvent);
+#endif
+ AssertRC(rc);
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
+ */
+static DECLCALLBACK(void) drvNATNetworkUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
+{
+ RT_NOREF(pInterface, fPromiscuous);
+ LogFlow(("drvNATNetworkUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
+ /* nothing to do */
+}
+
+/**
+ * Worker function for drvNATNetworkUp_NotifyLinkChanged().
+ * @thread "NAT" thread.
+ */
+static DECLCALLBACK(void) drvNATNotifyLinkChangedWorker(PDRVNAT pThis, PDMNETWORKLINKSTATE enmLinkState)
+{
+ pThis->enmLinkState = pThis->enmLinkStateWant = enmLinkState;
+ switch (enmLinkState)
+ {
+ case PDMNETWORKLINKSTATE_UP:
+ LogRel(("NAT: Link up\n"));
+ slirp_link_up(pThis->pNATState);
+ break;
+
+ case PDMNETWORKLINKSTATE_DOWN:
+ case PDMNETWORKLINKSTATE_DOWN_RESUME:
+ LogRel(("NAT: Link down\n"));
+ slirp_link_down(pThis->pNATState);
+ break;
+
+ default:
+ AssertMsgFailed(("drvNATNetworkUp_NotifyLinkChanged: unexpected link state %d\n", enmLinkState));
+ }
+}
+
+/**
+ * Notification on link status changes.
+ *
+ * @param pInterface Pointer to the interface structure containing the called function pointer.
+ * @param enmLinkState The new link state.
+ * @thread EMT
+ */
+static DECLCALLBACK(void) drvNATNetworkUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
+{
+ PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkUp);
+
+ LogFlow(("drvNATNetworkUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
+
+ /* Don't queue new requests if the NAT thread is not running (e.g. paused,
+ * stopping), otherwise we would deadlock. Memorize the change. */
+ if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
+ {
+ pThis->enmLinkStateWant = enmLinkState;
+ return;
+ }
+
+ PRTREQ pReq;
+ int rc = RTReqQueueCallEx(pThis->hSlirpReqQueue, &pReq, 0 /*cMillies*/, RTREQFLAGS_VOID,
+ (PFNRT)drvNATNotifyLinkChangedWorker, 2, pThis, enmLinkState);
+ if (rc == VERR_TIMEOUT)
+ {
+ drvNATNotifyNATThread(pThis, "drvNATNetworkUp_NotifyLinkChanged");
+ rc = RTReqWait(pReq, RT_INDEFINITE_WAIT);
+ AssertRC(rc);
+ }
+ else
+ AssertRC(rc);
+ RTReqRelease(pReq);
+}
+
+static DECLCALLBACK(void) drvNATNotifyApplyPortForwardCommand(PDRVNAT pThis, bool fRemove,
+ bool fUdp, const char *pHostIp,
+ uint16_t u16HostPort, const char *pGuestIp, uint16_t u16GuestPort)
+{
+ struct in_addr guestIp, hostIp;
+
+ if ( pHostIp == NULL
+ || inet_aton(pHostIp, &hostIp) == 0)
+ hostIp.s_addr = INADDR_ANY;
+
+ if ( pGuestIp == NULL
+ || inet_aton(pGuestIp, &guestIp) == 0)
+ guestIp.s_addr = pThis->GuestIP;
+
+ if (fRemove)
+ slirp_remove_redirect(pThis->pNATState, fUdp, hostIp, u16HostPort, guestIp, u16GuestPort);
+ else
+ slirp_add_redirect(pThis->pNATState, fUdp, hostIp, u16HostPort, guestIp, u16GuestPort);
+}
+
+static DECLCALLBACK(int) drvNATNetworkNatConfigRedirect(PPDMINETWORKNATCONFIG pInterface, bool fRemove,
+ bool fUdp, const char *pHostIp, uint16_t u16HostPort,
+ const char *pGuestIp, uint16_t u16GuestPort)
+{
+ LogFlowFunc(("fRemove=%d, fUdp=%d, pHostIp=%s, u16HostPort=%u, pGuestIp=%s, u16GuestPort=%u\n",
+ RT_BOOL(fRemove), RT_BOOL(fUdp), pHostIp, u16HostPort, pGuestIp, u16GuestPort));
+ PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkNATCfg);
+ /* Execute the command directly if the VM is not running. */
+ int rc;
+ if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
+ {
+ drvNATNotifyApplyPortForwardCommand(pThis, fRemove, fUdp, pHostIp,
+ u16HostPort, pGuestIp,u16GuestPort);
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ PRTREQ pReq;
+ rc = RTReqQueueCallEx(pThis->hSlirpReqQueue, &pReq, 0 /*cMillies*/, RTREQFLAGS_VOID,
+ (PFNRT)drvNATNotifyApplyPortForwardCommand, 7, pThis, fRemove,
+ fUdp, pHostIp, u16HostPort, pGuestIp, u16GuestPort);
+ if (rc == VERR_TIMEOUT)
+ {
+ drvNATNotifyNATThread(pThis, "drvNATNetworkNatConfigRedirect");
+ rc = RTReqWait(pReq, RT_INDEFINITE_WAIT);
+ AssertRC(rc);
+ }
+ else
+ AssertRC(rc);
+
+ RTReqRelease(pReq);
+ }
+ return rc;
+}
+
+/**
+ * NAT thread handling the slirp stuff.
+ *
+ * The slirp implementation is single-threaded so we execute this enginre in a
+ * dedicated thread. We take care that this thread does not become the
+ * bottleneck: If the guest wants to send, a request is enqueued into the
+ * hSlirpReqQueue and handled asynchronously by this thread. If this thread
+ * wants to deliver packets to the guest, it enqueues a request into
+ * hRecvReqQueue which is later handled by the Recv thread.
+ */
+static DECLCALLBACK(int) drvNATAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
+ int nFDs = -1;
+#ifdef RT_OS_WINDOWS
+ HANDLE *phEvents = slirp_get_events(pThis->pNATState);
+ unsigned int cBreak = 0;
+#else /* RT_OS_WINDOWS */
+ unsigned int cPollNegRet = 0;
+#endif /* !RT_OS_WINDOWS */
+
+ LogFlow(("drvNATAsyncIoThread: pThis=%p\n", pThis));
+
+ if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
+ return VINF_SUCCESS;
+
+ if (pThis->enmLinkStateWant != pThis->enmLinkState)
+ drvNATNotifyLinkChangedWorker(pThis, pThis->enmLinkStateWant);
+
+ /*
+ * Polling loop.
+ */
+ while (pThread->enmState == PDMTHREADSTATE_RUNNING)
+ {
+ /*
+ * To prevent concurrent execution of sending/receiving threads
+ */
+#ifndef RT_OS_WINDOWS
+ nFDs = slirp_get_nsock(pThis->pNATState);
+ /* allocation for all sockets + Management pipe */
+ struct pollfd *polls = (struct pollfd *)RTMemAlloc((1 + nFDs) * sizeof(struct pollfd) + sizeof(uint32_t));
+ if (polls == NULL)
+ return VERR_NO_MEMORY;
+
+ /* don't pass the management pipe */
+ slirp_select_fill(pThis->pNATState, &nFDs, &polls[1]);
+
+ polls[0].fd = RTPipeToNative(pThis->hPipeRead);
+ /* POLLRDBAND usually doesn't used on Linux but seems used on Solaris */
+ polls[0].events = POLLRDNORM | POLLPRI | POLLRDBAND;
+ polls[0].revents = 0;
+
+ int cChangedFDs = poll(polls, nFDs + 1, slirp_get_timeout_ms(pThis->pNATState));
+ if (cChangedFDs < 0)
+ {
+ if (errno == EINTR)
+ {
+ Log2(("NAT: signal was caught while sleep on poll\n"));
+ /* No error, just process all outstanding requests but don't wait */
+ cChangedFDs = 0;
+ }
+ else if (cPollNegRet++ > 128)
+ {
+ LogRel(("NAT: Poll returns (%s) suppressed %d\n", strerror(errno), cPollNegRet));
+ cPollNegRet = 0;
+ }
+ }
+
+ if (cChangedFDs >= 0)
+ {
+ slirp_select_poll(pThis->pNATState, &polls[1], nFDs);
+ if (polls[0].revents & (POLLRDNORM|POLLPRI|POLLRDBAND))
+ {
+ /* drain the pipe
+ *
+ * Note! drvNATSend decoupled so we don't know how many times
+ * device's thread sends before we've entered multiplex,
+ * so to avoid false alarm drain pipe here to the very end
+ *
+ * @todo: Probably we should counter drvNATSend to count how
+ * deep pipe has been filed before drain.
+ *
+ */
+ /** @todo XXX: Make it reading exactly we need to drain the
+ * pipe.*/
+ char ch;
+ size_t cbRead;
+ RTPipeRead(pThis->hPipeRead, &ch, 1, &cbRead);
+ }
+ }
+ /* process _all_ outstanding requests but don't wait */
+ RTReqQueueProcess(pThis->hSlirpReqQueue, 0);
+ RTMemFree(polls);
+
+#else /* RT_OS_WINDOWS */
+ nFDs = -1;
+ slirp_select_fill(pThis->pNATState, &nFDs);
+ DWORD dwEvent = WSAWaitForMultipleEvents(nFDs, phEvents, FALSE,
+ slirp_get_timeout_ms(pThis->pNATState),
+ /* :fAlertable */ TRUE);
+ AssertCompile(WSA_WAIT_EVENT_0 == 0);
+ if ( (/*dwEvent < WSA_WAIT_EVENT_0 ||*/ dwEvent > WSA_WAIT_EVENT_0 + nFDs - 1)
+ && dwEvent != WSA_WAIT_TIMEOUT && dwEvent != WSA_WAIT_IO_COMPLETION)
+ {
+ int error = WSAGetLastError();
+ LogRel(("NAT: WSAWaitForMultipleEvents returned %d (error %d)\n", dwEvent, error));
+ RTAssertPanic();
+ }
+
+ if (dwEvent == WSA_WAIT_TIMEOUT)
+ {
+ /* only check for slow/fast timers */
+ slirp_select_poll(pThis->pNATState, /* fTimeout=*/true);
+ continue;
+ }
+ /* poll the sockets in any case */
+ Log2(("%s: poll\n", __FUNCTION__));
+ slirp_select_poll(pThis->pNATState, /* fTimeout=*/false);
+ /* process _all_ outstanding requests but don't wait */
+ RTReqQueueProcess(pThis->hSlirpReqQueue, 0);
+# ifdef VBOX_NAT_DELAY_HACK
+ if (cBreak++ > 128)
+ {
+ cBreak = 0;
+ RTThreadSleep(2);
+ }
+# endif
+#endif /* RT_OS_WINDOWS */
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Unblock the send thread so it can respond to a state change.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The pcnet device instance.
+ * @param pThread The send thread.
+ */
+static DECLCALLBACK(int) drvNATAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ RT_NOREF(pThread);
+ PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
+
+ drvNATNotifyNATThread(pThis, "drvNATAsyncIoWakeup");
+ return VINF_SUCCESS;
+}
+
+
+static DECLCALLBACK(int) drvNATHostResThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
+
+ if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
+ return VINF_SUCCESS;
+
+ while (pThread->enmState == PDMTHREADSTATE_RUNNING)
+ {
+ RTReqQueueProcess(pThis->hHostResQueue, RT_INDEFINITE_WAIT);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+static DECLCALLBACK(int) drvNATReqQueueInterrupt()
+{
+ /*
+ * RTReqQueueProcess loops until request returns a warning or info
+ * status code (other than VINF_SUCCESS).
+ */
+ return VINF_INTERRUPTED;
+}
+
+
+static DECLCALLBACK(int) drvNATHostResWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ RT_NOREF(pThread);
+ PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
+ Assert(pThis != NULL);
+
+ int rc;
+ rc = RTReqQueueCallEx(pThis->hHostResQueue, NULL /*ppReq*/, 0 /*cMillies*/,
+ RTREQFLAGS_IPRT_STATUS | RTREQFLAGS_NO_WAIT,
+ (PFNRT)drvNATReqQueueInterrupt, 0);
+ return rc;
+}
+
+
+/**
+ * Function called by slirp to check if it's possible to feed incoming data to the network port.
+ * @returns 1 if possible.
+ * @returns 0 if not possible.
+ */
+int slirp_can_output(void *pvUser)
+{
+ RT_NOREF(pvUser);
+ return 1;
+}
+
+void slirp_push_recv_thread(void *pvUser)
+{
+ PDRVNAT pThis = (PDRVNAT)pvUser;
+ Assert(pThis);
+ drvNATUrgRecvWakeup(pThis->pDrvIns, pThis->pUrgRecvThread);
+}
+
+void slirp_urg_output(void *pvUser, struct mbuf *m, const uint8_t *pu8Buf, int cb)
+{
+ PDRVNAT pThis = (PDRVNAT)pvUser;
+ Assert(pThis);
+
+ /* don't queue new requests when the NAT thread is about to stop */
+ if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
+ return;
+
+ ASMAtomicIncU32(&pThis->cUrgPkts);
+ int rc = RTReqQueueCallEx(pThis->hUrgRecvReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
+ (PFNRT)drvNATUrgRecvWorker, 4, pThis, pu8Buf, cb, m);
+ AssertRC(rc);
+ drvNATUrgRecvWakeup(pThis->pDrvIns, pThis->pUrgRecvThread);
+}
+
+/**
+ * Function called by slirp to wake up device after VERR_TRY_AGAIN
+ */
+void slirp_output_pending(void *pvUser)
+{
+ PDRVNAT pThis = (PDRVNAT)pvUser;
+ Assert(pThis);
+ LogFlowFuncEnter();
+ pThis->pIAboveNet->pfnXmitPending(pThis->pIAboveNet);
+ LogFlowFuncLeave();
+}
+
+/**
+ * Function called by slirp to feed incoming data to the NIC.
+ */
+void slirp_output(void *pvUser, struct mbuf *m, const uint8_t *pu8Buf, int cb)
+{
+ PDRVNAT pThis = (PDRVNAT)pvUser;
+ Assert(pThis);
+
+ LogFlow(("slirp_output BEGIN %p %d\n", pu8Buf, cb));
+ Log6(("slirp_output: pu8Buf=%p cb=%#x (pThis=%p)\n%.*Rhxd\n", pu8Buf, cb, pThis, cb, pu8Buf));
+
+ /* don't queue new requests when the NAT thread is about to stop */
+ if (pThis->pSlirpThread->enmState != PDMTHREADSTATE_RUNNING)
+ return;
+
+ ASMAtomicIncU32(&pThis->cPkts);
+ int rc = RTReqQueueCallEx(pThis->hRecvReqQueue, NULL /*ppReq*/, 0 /*cMillies*/, RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
+ (PFNRT)drvNATRecvWorker, 4, pThis, pu8Buf, cb, m);
+ AssertRC(rc);
+ drvNATRecvWakeup(pThis->pDrvIns, pThis->pRecvThread);
+ STAM_COUNTER_INC(&pThis->StatQueuePktSent);
+ LogFlowFuncLeave();
+}
+
+
+/*
+ * Call a function on the slirp thread.
+ */
+int slirp_call(void *pvUser, PRTREQ *ppReq, RTMSINTERVAL cMillies,
+ unsigned fFlags, PFNRT pfnFunction, unsigned cArgs, ...)
+{
+ PDRVNAT pThis = (PDRVNAT)pvUser;
+ Assert(pThis);
+
+ int rc;
+
+ va_list va;
+ va_start(va, cArgs);
+
+ rc = RTReqQueueCallV(pThis->hSlirpReqQueue, ppReq, cMillies, fFlags, pfnFunction, cArgs, va);
+
+ va_end(va);
+
+ if (RT_SUCCESS(rc))
+ drvNATNotifyNATThread(pThis, "slirp_vcall");
+
+ return rc;
+}
+
+
+/*
+ * Call a function on the host resolver thread.
+ */
+int slirp_call_hostres(void *pvUser, PRTREQ *ppReq, RTMSINTERVAL cMillies,
+ unsigned fFlags, PFNRT pfnFunction, unsigned cArgs, ...)
+{
+ PDRVNAT pThis = (PDRVNAT)pvUser;
+ Assert(pThis);
+
+ int rc;
+
+ AssertReturn((pThis->hHostResQueue != NIL_RTREQQUEUE), VERR_INVALID_STATE);
+ AssertReturn((pThis->pHostResThread != NULL), VERR_INVALID_STATE);
+
+ va_list va;
+ va_start(va, cArgs);
+
+ rc = RTReqQueueCallV(pThis->hHostResQueue, ppReq, cMillies, fFlags,
+ pfnFunction, cArgs, va);
+
+ va_end(va);
+ return rc;
+}
+
+
+#if HAVE_NOTIFICATION_FOR_DNS_UPDATE && !defined(RT_OS_DARWIN)
+/**
+ * @interface_method_impl{PDMINETWORKNATCONFIG,pfnNotifyDnsChanged}
+ *
+ * We are notified that host's resolver configuration has changed. In
+ * the current setup we don't get any details and just reread that
+ * information ourselves.
+ */
+static DECLCALLBACK(void) drvNATNotifyDnsChanged(PPDMINETWORKNATCONFIG pInterface)
+{
+ PDRVNAT pThis = RT_FROM_MEMBER(pInterface, DRVNAT, INetworkNATCfg);
+ drvNATUpdateDNS(pThis, /* fFlapLink */ true);
+}
+#endif
+
+#ifdef RT_OS_DARWIN
+/**
+ * Callback for the SystemConfiguration framework to notify us whenever the DNS
+ * server changes.
+ *
+ * @param hDynStor The DynamicStore handle.
+ * @param hChangedKey Array of changed keys we watch for.
+ * @param pvUser Opaque user data (NAT driver instance).
+ */
+static DECLCALLBACK(void) drvNatDnsChanged(SCDynamicStoreRef hDynStor, CFArrayRef hChangedKeys, void *pvUser)
+{
+ PDRVNAT pThis = (PDRVNAT)pvUser;
+
+ Log2(("NAT: System configuration has changed\n"));
+
+ /* Check if any of parameters we are interested in were actually changed. If the size
+ * of hChangedKeys is 0, it means that SCDynamicStore has been restarted. */
+ if (hChangedKeys && CFArrayGetCount(hChangedKeys) > 0)
+ {
+ /* Look to the updated parameters in particular. */
+ CFStringRef pDNSKey = CFSTR("State:/Network/Global/DNS");
+
+ if (CFArrayContainsValue(hChangedKeys, CFRangeMake(0, CFArrayGetCount(hChangedKeys)), pDNSKey))
+ {
+ LogRel(("NAT: DNS servers changed, triggering reconnect\n"));
+#if 0
+ CFDictionaryRef hDnsDict = (CFDictionaryRef)SCDynamicStoreCopyValue(hDynStor, pDNSKey);
+ if (hDnsDict)
+ {
+ CFArrayRef hArrAddresses = (CFArrayRef)CFDictionaryGetValue(hDnsDict, kSCPropNetDNSServerAddresses);
+ if (hArrAddresses && CFArrayGetCount(hArrAddresses) > 0)
+ {
+ /* Dump DNS servers list. */
+ for (int i = 0; i < CFArrayGetCount(hArrAddresses); i++)
+ {
+ CFStringRef pDNSAddrStr = (CFStringRef)CFArrayGetValueAtIndex(hArrAddresses, i);
+ const char *pszDNSAddr = pDNSAddrStr ? CFStringGetCStringPtr(pDNSAddrStr, CFStringGetSystemEncoding()) : NULL;
+ LogRel(("NAT: New DNS server#%d: %s\n", i, pszDNSAddr ? pszDNSAddr : "None"));
+ }
+ }
+ else
+ LogRel(("NAT: DNS server list is empty (1)\n"));
+
+ CFRelease(hDnsDict);
+ }
+ else
+ LogRel(("NAT: DNS server list is empty (2)\n"));
+#else
+ RT_NOREF(hDynStor);
+#endif
+ drvNATUpdateDNS(pThis, /* fFlapLink */ true);
+ }
+ else
+ Log2(("NAT: No DNS changes detected\n"));
+ }
+ else
+ Log2(("NAT: SCDynamicStore has been restarted\n"));
+}
+#endif
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvNATQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+ PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
+
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKNATCONFIG, &pThis->INetworkNATCfg);
+ return NULL;
+}
+
+
+/**
+ * Get the MAC address into the slirp stack.
+ *
+ * Called by drvNATLoadDone and drvNATPowerOn.
+ */
+static void drvNATSetMac(PDRVNAT pThis)
+{
+#if 0 /* XXX: do we still need this for anything? */
+ if (pThis->pIAboveConfig)
+ {
+ RTMAC Mac;
+ pThis->pIAboveConfig->pfnGetMac(pThis->pIAboveConfig, &Mac);
+ }
+#else
+ RT_NOREF(pThis);
+#endif
+}
+
+
+/**
+ * After loading we have to pass the MAC address of the ethernet device to the slirp stack.
+ * Otherwise the guest is not reachable until it performs a DHCP request or an ARP request
+ * (usually done during guest boot).
+ */
+static DECLCALLBACK(int) drvNATLoadDone(PPDMDRVINS pDrvIns, PSSMHANDLE pSSM)
+{
+ RT_NOREF(pSSM);
+ PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
+ drvNATSetMac(pThis);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Some guests might not use DHCP to retrieve an IP but use a static IP.
+ */
+static DECLCALLBACK(void) drvNATPowerOn(PPDMDRVINS pDrvIns)
+{
+ PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
+ drvNATSetMac(pThis);
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnResume}
+ */
+static DECLCALLBACK(void) drvNATResume(PPDMDRVINS pDrvIns)
+{
+ PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
+ VMRESUMEREASON enmReason = PDMDrvHlpVMGetResumeReason(pDrvIns);
+
+ switch (enmReason)
+ {
+ case VMRESUMEREASON_HOST_RESUME:
+ bool fFlapLink;
+#if HAVE_NOTIFICATION_FOR_DNS_UPDATE
+ /* let event handler do it if necessary */
+ fFlapLink = false;
+#else
+ /* XXX: when in doubt, use brute force */
+ fFlapLink = true;
+#endif
+ drvNATUpdateDNS(pThis, fFlapLink);
+ return;
+ default: /* Ignore every other resume reason. */
+ /* do nothing */
+ return;
+ }
+}
+
+
+static DECLCALLBACK(int) drvNATReinitializeHostNameResolving(PDRVNAT pThis)
+{
+ slirpReleaseDnsSettings(pThis->pNATState);
+ slirpInitializeDnsSettings(pThis->pNATState);
+ return VINF_SUCCESS;
+}
+
+/**
+ * This function at this stage could be called from two places, but both from non-NAT thread,
+ * - drvNATResume (EMT?)
+ * - drvNatDnsChanged (darwin, GUI or main) "listener"
+ * When Main's interface IHost will support host network configuration change event on every host,
+ * we won't call it from drvNATResume, but from listener of Main event in the similar way it done
+ * for port-forwarding, and it wan't be on GUI/main thread, but on EMT thread only.
+ *
+ * Thread here is important, because we need to change DNS server list and domain name (+ perhaps,
+ * search string) at runtime (VBOX_NAT_ENFORCE_INTERNAL_DNS_UPDATE), we can do it safely on NAT thread,
+ * so with changing other variables (place where we handle update) the main mechanism of update
+ * _won't_ be changed, the only thing will change is drop of fFlapLink parameter.
+ */
+DECLINLINE(void) drvNATUpdateDNS(PDRVNAT pThis, bool fFlapLink)
+{
+ int strategy = slirp_host_network_configuration_change_strategy_selector(pThis->pNATState);
+ switch (strategy)
+ {
+ case VBOX_NAT_DNS_DNSPROXY:
+ {
+ /**
+ * XXX: Here or in _strategy_selector we should deal with network change
+ * in "network change" scenario domain name change we have to update guest lease
+ * forcibly.
+ * Note at that built-in dhcp also updates DNS information on NAT thread.
+ */
+ /**
+ * It's unsafe to to do it directly on non-NAT thread
+ * so we schedule the worker and kick the NAT thread.
+ */
+ int rc = RTReqQueueCallEx(pThis->hSlirpReqQueue, NULL /*ppReq*/, 0 /*cMillies*/,
+ RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
+ (PFNRT)drvNATReinitializeHostNameResolving, 1, pThis);
+ if (RT_SUCCESS(rc))
+ drvNATNotifyNATThread(pThis, "drvNATUpdateDNS");
+
+ return;
+ }
+
+ case VBOX_NAT_DNS_EXTERNAL:
+ /*
+ * Host resumed from a suspend and the network might have changed.
+ * Disconnect the guest from the network temporarily to let it pick up the changes.
+ */
+ if (fFlapLink)
+ pThis->pIAboveConfig->pfnSetLinkState(pThis->pIAboveConfig,
+ PDMNETWORKLINKSTATE_DOWN_RESUME);
+ return;
+
+ case VBOX_NAT_DNS_HOSTRESOLVER:
+ default:
+ return;
+ }
+}
+
+
+/**
+ * Info handler.
+ */
+static DECLCALLBACK(void) drvNATInfo(PPDMDRVINS pDrvIns, PCDBGFINFOHLP pHlp, const char *pszArgs)
+{
+ PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
+ slirp_info(pThis->pNATState, pHlp, pszArgs);
+}
+
+#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
+static int drvNATConstructDNSMappings(unsigned iInstance, PDRVNAT pThis, PCFGMNODE pMappingsCfg)
+{
+ PPDMDRVINS pDrvIns = pThis->pDrvIns;
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ RT_NOREF(iInstance);
+ int rc = VINF_SUCCESS;
+ LogFlowFunc(("ENTER: iInstance:%d\n", iInstance));
+ for (PCFGMNODE pNode = pHlp->pfnCFGMGetFirstChild(pMappingsCfg); pNode; pNode = pHlp->pfnCFGMGetNextChild(pNode))
+ {
+ if (!pHlp->pfnCFGMAreValuesValid(pNode, "HostName\0HostNamePattern\0HostIP\0"))
+ return PDMDRV_SET_ERROR(pThis->pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
+ N_("Unknown configuration in dns mapping"));
+ char szHostNameOrPattern[255];
+ bool fPattern = false;
+ RT_ZERO(szHostNameOrPattern);
+ GET_STRING(rc, pDrvIns, pNode, "HostName", szHostNameOrPattern[0], sizeof(szHostNameOrPattern));
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+ {
+ GET_STRING(rc, pDrvIns, pNode, "HostNamePattern", szHostNameOrPattern[0], sizeof(szHostNameOrPattern));
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+ {
+ char szNodeName[225];
+ RT_ZERO(szNodeName);
+ pHlp->pfnCFGMGetName(pNode, szNodeName, sizeof(szNodeName));
+ LogRel(("NAT: Neither 'HostName' nor 'HostNamePattern' is specified for mapping %s\n", szNodeName));
+ continue;
+ }
+ fPattern = true;
+ }
+ struct in_addr HostIP;
+ RT_ZERO(HostIP);
+ GETIP_DEF(rc, pDrvIns, pNode, HostIP, INADDR_ANY);
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+ {
+ LogRel(("NAT: DNS mapping %s is ignored (address not pointed)\n", szHostNameOrPattern));
+ continue;
+ }
+ slirp_add_host_resolver_mapping(pThis->pNATState, szHostNameOrPattern, fPattern, HostIP.s_addr);
+ }
+ LogFlowFunc(("LEAVE: %Rrc\n", rc));
+ return rc;
+}
+#endif /* !VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER */
+
+
+/**
+ * Sets up the redirectors.
+ *
+ * @returns VBox status code.
+ * @param pCfg The configuration handle.
+ */
+static int drvNATConstructRedir(unsigned iInstance, PDRVNAT pThis, PCFGMNODE pCfg, PRTNETADDRIPV4 pNetwork)
+{
+ PPDMDRVINS pDrvIns = pThis->pDrvIns;
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ RT_NOREF(pNetwork); /** @todo figure why pNetwork isn't used */
+
+ PCFGMNODE pPFTree = pHlp->pfnCFGMGetChild(pCfg, "PortForwarding");
+ if (pPFTree == NULL)
+ return VINF_SUCCESS;
+
+ /*
+ * Enumerate redirections.
+ */
+ for (PCFGMNODE pNode = pHlp->pfnCFGMGetFirstChild(pPFTree); pNode; pNode = pHlp->pfnCFGMGetNextChild(pNode))
+ {
+ /*
+ * Validate the port forwarding config.
+ */
+ if (!pHlp->pfnCFGMAreValuesValid(pNode, "Name\0Protocol\0UDP\0HostPort\0GuestPort\0GuestIP\0BindIP\0"))
+ return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES,
+ N_("Unknown configuration in port forwarding"));
+
+ /* protocol type */
+ bool fUDP;
+ char szProtocol[32];
+ int rc;
+ GET_STRING(rc, pDrvIns, pNode, "Protocol", szProtocol[0], sizeof(szProtocol));
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+ {
+ fUDP = false;
+ GET_BOOL(rc, pDrvIns, pNode, "UDP", fUDP);
+ }
+ else if (RT_SUCCESS(rc))
+ {
+ if (!RTStrICmp(szProtocol, "TCP"))
+ fUDP = false;
+ else if (!RTStrICmp(szProtocol, "UDP"))
+ fUDP = true;
+ else
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
+ N_("NAT#%d: Invalid configuration value for \"Protocol\": \"%s\""),
+ iInstance, szProtocol);
+ }
+ else
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("NAT#%d: configuration query for \"Protocol\" failed"),
+ iInstance);
+ /* host port */
+ int32_t iHostPort;
+ GET_S32_STRICT(rc, pDrvIns, pNode, "HostPort", iHostPort);
+
+ /* guest port */
+ int32_t iGuestPort;
+ GET_S32_STRICT(rc, pDrvIns, pNode, "GuestPort", iGuestPort);
+
+ /* host address ("BindIP" name is rather unfortunate given "HostPort" to go with it) */
+ struct in_addr BindIP;
+ RT_ZERO(BindIP);
+ GETIP_DEF(rc, pDrvIns, pNode, BindIP, INADDR_ANY);
+
+ /* guest address */
+ struct in_addr GuestIP;
+ RT_ZERO(GuestIP);
+ GETIP_DEF(rc, pDrvIns, pNode, GuestIP, INADDR_ANY);
+
+ /*
+ * Call slirp about it.
+ */
+ if (slirp_add_redirect(pThis->pNATState, fUDP, BindIP, iHostPort, GuestIP, iGuestPort) < 0)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_NAT_REDIR_SETUP, RT_SRC_POS,
+ N_("NAT#%d: configuration error: failed to set up "
+ "redirection of %d to %d. Probably a conflict with "
+ "existing services or other rules"), iInstance, iHostPort,
+ iGuestPort);
+ } /* for each redir rule */
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Destruct a driver instance.
+ *
+ * Most VM resources are freed by the VM. This callback is provided so that any non-VM
+ * resources can be freed correctly.
+ *
+ * @param pDrvIns The driver instance data.
+ */
+static DECLCALLBACK(void) drvNATDestruct(PPDMDRVINS pDrvIns)
+{
+ PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
+ LogFlow(("drvNATDestruct:\n"));
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+
+ if (pThis->pNATState)
+ {
+ slirp_term(pThis->pNATState);
+ slirp_deregister_statistics(pThis->pNATState, pDrvIns);
+#ifdef VBOX_WITH_STATISTICS
+# define DRV_PROFILE_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
+# define DRV_COUNTING_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pThis)
+# include "counters.h"
+#endif
+ pThis->pNATState = NULL;
+ }
+
+ RTReqQueueDestroy(pThis->hHostResQueue);
+ pThis->hHostResQueue = NIL_RTREQQUEUE;
+
+ RTReqQueueDestroy(pThis->hSlirpReqQueue);
+ pThis->hSlirpReqQueue = NIL_RTREQQUEUE;
+
+ RTReqQueueDestroy(pThis->hUrgRecvReqQueue);
+ pThis->hUrgRecvReqQueue = NIL_RTREQQUEUE;
+
+ RTReqQueueDestroy(pThis->hRecvReqQueue);
+ pThis->hRecvReqQueue = NIL_RTREQQUEUE;
+
+ RTSemEventDestroy(pThis->EventRecv);
+ pThis->EventRecv = NIL_RTSEMEVENT;
+
+ RTSemEventDestroy(pThis->EventUrgRecv);
+ pThis->EventUrgRecv = NIL_RTSEMEVENT;
+
+ if (RTCritSectIsInitialized(&pThis->DevAccessLock))
+ RTCritSectDelete(&pThis->DevAccessLock);
+
+ if (RTCritSectIsInitialized(&pThis->XmitLock))
+ RTCritSectDelete(&pThis->XmitLock);
+
+#ifndef RT_OS_WINDOWS
+ RTPipeClose(pThis->hPipeRead);
+ RTPipeClose(pThis->hPipeWrite);
+#endif
+
+#ifdef RT_OS_DARWIN
+ /* Cleanup the DNS watcher. */
+ if (pThis->hRunLoopSrcDnsWatcher != NULL)
+ {
+ CFRunLoopRef hRunLoopMain = CFRunLoopGetMain();
+ CFRetain(hRunLoopMain);
+ CFRunLoopRemoveSource(hRunLoopMain, pThis->hRunLoopSrcDnsWatcher, kCFRunLoopCommonModes);
+ CFRelease(hRunLoopMain);
+ CFRelease(pThis->hRunLoopSrcDnsWatcher);
+ pThis->hRunLoopSrcDnsWatcher = NULL;
+ }
+#endif
+}
+
+
+/**
+ * Construct a NAT network transport driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(int) drvNATConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ RT_NOREF(fFlags);
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVNAT pThis = PDMINS_2_DATA(pDrvIns, PDRVNAT);
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ LogFlow(("drvNATConstruct:\n"));
+
+ /*
+ * Init the static parts.
+ */
+ pThis->pDrvIns = pDrvIns;
+ pThis->pNATState = NULL;
+ pThis->pszTFTPPrefix = NULL;
+ pThis->pszBootFile = NULL;
+ pThis->pszNextServer = NULL;
+ pThis->hSlirpReqQueue = NIL_RTREQQUEUE;
+ pThis->hUrgRecvReqQueue = NIL_RTREQQUEUE;
+ pThis->hHostResQueue = NIL_RTREQQUEUE;
+ pThis->EventRecv = NIL_RTSEMEVENT;
+ pThis->EventUrgRecv = NIL_RTSEMEVENT;
+#ifdef RT_OS_DARWIN
+ pThis->hRunLoopSrcDnsWatcher = NULL;
+#endif
+
+ /* IBase */
+ pDrvIns->IBase.pfnQueryInterface = drvNATQueryInterface;
+
+ /* INetwork */
+ pThis->INetworkUp.pfnBeginXmit = drvNATNetworkUp_BeginXmit;
+ pThis->INetworkUp.pfnAllocBuf = drvNATNetworkUp_AllocBuf;
+ pThis->INetworkUp.pfnFreeBuf = drvNATNetworkUp_FreeBuf;
+ pThis->INetworkUp.pfnSendBuf = drvNATNetworkUp_SendBuf;
+ pThis->INetworkUp.pfnEndXmit = drvNATNetworkUp_EndXmit;
+ pThis->INetworkUp.pfnSetPromiscuousMode = drvNATNetworkUp_SetPromiscuousMode;
+ pThis->INetworkUp.pfnNotifyLinkChanged = drvNATNetworkUp_NotifyLinkChanged;
+
+ /* NAT engine configuration */
+ pThis->INetworkNATCfg.pfnRedirectRuleCommand = drvNATNetworkNatConfigRedirect;
+#if HAVE_NOTIFICATION_FOR_DNS_UPDATE && !defined(RT_OS_DARWIN)
+ /*
+ * On OS X we stick to the old OS X specific notifications for
+ * now. Elsewhere use IHostNameResolutionConfigurationChangeEvent
+ * by enbaling HAVE_NOTIFICATION_FOR_DNS_UPDATE in libslirp.h.
+ * This code is still in a bit of flux and is implemented and
+ * enabled in steps to simplify more conservative backporting.
+ */
+ pThis->INetworkNATCfg.pfnNotifyDnsChanged = drvNATNotifyDnsChanged;
+#else
+ pThis->INetworkNATCfg.pfnNotifyDnsChanged = NULL;
+#endif
+
+ /*
+ * Validate the config.
+ */
+ PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns,
+ "PassDomain"
+ "|TFTPPrefix"
+ "|BootFile"
+ "|Network"
+ "|NextServer"
+ "|DNSProxy"
+ "|BindIP"
+ "|UseHostResolver"
+ "|SlirpMTU"
+ "|AliasMode"
+ "|SockRcv"
+ "|SockSnd"
+ "|TcpRcv"
+ "|TcpSnd"
+ "|ICMPCacheLimit"
+ "|SoMaxConnection"
+ "|LocalhostReachable"
+//#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
+ "|HostResolverMappings"
+//#endif
+ , "PortForwarding");
+
+ /*
+ * Get the configuration settings.
+ */
+ int rc;
+ bool fPassDomain = true;
+ GET_BOOL(rc, pDrvIns, pCfg, "PassDomain", fPassDomain);
+
+ GET_STRING_ALLOC(rc, pDrvIns, pCfg, "TFTPPrefix", pThis->pszTFTPPrefix);
+ GET_STRING_ALLOC(rc, pDrvIns, pCfg, "BootFile", pThis->pszBootFile);
+ GET_STRING_ALLOC(rc, pDrvIns, pCfg, "NextServer", pThis->pszNextServer);
+
+ int fDNSProxy = 0;
+ GET_S32(rc, pDrvIns, pCfg, "DNSProxy", fDNSProxy);
+ int fUseHostResolver = 0;
+ GET_S32(rc, pDrvIns, pCfg, "UseHostResolver", fUseHostResolver);
+ int MTU = 1500;
+ GET_S32(rc, pDrvIns, pCfg, "SlirpMTU", MTU);
+ int i32AliasMode = 0;
+ int i32MainAliasMode = 0;
+ GET_S32(rc, pDrvIns, pCfg, "AliasMode", i32MainAliasMode);
+ int iIcmpCacheLimit = 100;
+ GET_S32(rc, pDrvIns, pCfg, "ICMPCacheLimit", iIcmpCacheLimit);
+ bool fLocalhostReachable = false;
+ GET_BOOL(rc, pDrvIns, pCfg, "LocalhostReachable", fLocalhostReachable);
+
+ i32AliasMode |= (i32MainAliasMode & 0x1 ? 0x1 : 0);
+ i32AliasMode |= (i32MainAliasMode & 0x2 ? 0x40 : 0);
+ i32AliasMode |= (i32MainAliasMode & 0x4 ? 0x4 : 0);
+ int i32SoMaxConn = 10;
+ GET_S32(rc, pDrvIns, pCfg, "SoMaxConnection", i32SoMaxConn);
+ /*
+ * Query the network port interface.
+ */
+ pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
+ if (!pThis->pIAboveNet)
+ return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
+ N_("Configuration error: the above device/driver didn't "
+ "export the network port interface"));
+ pThis->pIAboveConfig = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
+ if (!pThis->pIAboveConfig)
+ return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
+ N_("Configuration error: the above device/driver didn't "
+ "export the network config interface"));
+
+ /* Generate a network address for this network card. */
+ char szNetwork[32]; /* xxx.xxx.xxx.xxx/yy */
+ GET_STRING(rc, pDrvIns, pCfg, "Network", szNetwork[0], sizeof(szNetwork));
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS, N_("NAT%d: Configuration error: missing network"),
+ pDrvIns->iInstance);
+
+ RTNETADDRIPV4 Network, Netmask;
+
+ rc = RTCidrStrToIPv4(szNetwork, &Network, &Netmask);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("NAT#%d: Configuration error: network '%s' describes not a valid IPv4 network"),
+ pDrvIns->iInstance, szNetwork);
+
+ /*
+ * Initialize slirp.
+ */
+ rc = slirp_init(&pThis->pNATState, RT_H2N_U32(Network.u), Netmask.u,
+ fPassDomain, !!fUseHostResolver, i32AliasMode,
+ iIcmpCacheLimit, fLocalhostReachable, pThis);
+ if (RT_SUCCESS(rc))
+ {
+ slirp_set_dhcp_TFTP_prefix(pThis->pNATState, pThis->pszTFTPPrefix);
+ slirp_set_dhcp_TFTP_bootfile(pThis->pNATState, pThis->pszBootFile);
+ slirp_set_dhcp_next_server(pThis->pNATState, pThis->pszNextServer);
+ slirp_set_dhcp_dns_proxy(pThis->pNATState, !!fDNSProxy);
+ slirp_set_mtu(pThis->pNATState, MTU);
+ slirp_set_somaxconn(pThis->pNATState, i32SoMaxConn);
+
+ char *pszBindIP = NULL;
+ GET_STRING_ALLOC(rc, pDrvIns, pCfg, "BindIP", pszBindIP);
+ slirp_set_binding_address(pThis->pNATState, pszBindIP);
+ if (pszBindIP != NULL)
+ PDMDrvHlpMMHeapFree(pDrvIns, pszBindIP);
+
+#define SLIRP_SET_TUNING_VALUE(name, setter) \
+ do \
+ { \
+ int len = 0; \
+ rc = pHlp->pfnCFGMQueryS32(pCfg, name, &len); \
+ if (RT_SUCCESS(rc)) \
+ setter(pThis->pNATState, len); \
+ } while(0)
+
+ SLIRP_SET_TUNING_VALUE("SockRcv", slirp_set_rcvbuf);
+ SLIRP_SET_TUNING_VALUE("SockSnd", slirp_set_sndbuf);
+ SLIRP_SET_TUNING_VALUE("TcpRcv", slirp_set_tcp_rcvspace);
+ SLIRP_SET_TUNING_VALUE("TcpSnd", slirp_set_tcp_sndspace);
+
+ slirp_register_statistics(pThis->pNATState, pDrvIns);
+#ifdef VBOX_WITH_STATISTICS
+# define DRV_PROFILE_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_PROFILE, STAMUNIT_TICKS_PER_CALL, dsc)
+# define DRV_COUNTING_COUNTER(name, dsc) REGISTER_COUNTER(name, pThis, STAMTYPE_COUNTER, STAMUNIT_COUNT, dsc)
+# include "counters.h"
+#endif
+
+#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
+ PCFGMNODE pMappingsCfg = pHlp->pfnCFGMGetChild(pCfg, "HostResolverMappings");
+
+ if (pMappingsCfg)
+ {
+ rc = drvNATConstructDNSMappings(pDrvIns->iInstance, pThis, pMappingsCfg);
+ AssertRC(rc);
+ }
+#endif
+ rc = drvNATConstructRedir(pDrvIns->iInstance, pThis, pCfg, &Network);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Register a load done notification to get the MAC address into the slirp
+ * engine after we loaded a guest state.
+ */
+ rc = PDMDrvHlpSSMRegisterLoadDone(pDrvIns, drvNATLoadDone);
+ AssertLogRelRCReturn(rc, rc);
+
+ rc = RTReqQueueCreate(&pThis->hSlirpReqQueue);
+ AssertLogRelRCReturn(rc, rc);
+
+ rc = RTReqQueueCreate(&pThis->hRecvReqQueue);
+ AssertLogRelRCReturn(rc, rc);
+
+ rc = RTReqQueueCreate(&pThis->hUrgRecvReqQueue);
+ AssertLogRelRCReturn(rc, rc);
+
+ rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pRecvThread, pThis, drvNATRecv,
+ drvNATRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATRX");
+ AssertRCReturn(rc, rc);
+
+ rc = RTSemEventCreate(&pThis->EventRecv);
+ AssertRCReturn(rc, rc);
+
+ rc = RTSemEventCreate(&pThis->EventUrgRecv);
+ AssertRCReturn(rc, rc);
+
+ rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pUrgRecvThread, pThis, drvNATUrgRecv,
+ drvNATUrgRecvWakeup, 128 * _1K, RTTHREADTYPE_IO, "NATURGRX");
+ AssertRCReturn(rc, rc);
+
+ rc = RTReqQueueCreate(&pThis->hHostResQueue);
+ AssertRCReturn(rc, rc);
+
+ rc = PDMDrvHlpThreadCreate(pThis->pDrvIns, &pThis->pHostResThread,
+ pThis, drvNATHostResThread, drvNATHostResWakeup,
+ 64 * _1K, RTTHREADTYPE_IO, "HOSTRES");
+ AssertRCReturn(rc, rc);
+
+ rc = RTCritSectInit(&pThis->DevAccessLock);
+ AssertRCReturn(rc, rc);
+
+ rc = RTCritSectInit(&pThis->XmitLock);
+ AssertRCReturn(rc, rc);
+
+ char szTmp[128];
+ RTStrPrintf(szTmp, sizeof(szTmp), "nat%d", pDrvIns->iInstance);
+ PDMDrvHlpDBGFInfoRegister(pDrvIns, szTmp, "NAT info.", drvNATInfo);
+
+#ifndef RT_OS_WINDOWS
+ /*
+ * Create the control pipe.
+ */
+ rc = RTPipeCreate(&pThis->hPipeRead, &pThis->hPipeWrite, 0 /*fFlags*/);
+ AssertRCReturn(rc, rc);
+#else
+ pThis->hWakeupEvent = CreateEvent(NULL, FALSE, FALSE, NULL); /* auto-reset event */
+ slirp_register_external_event(pThis->pNATState, pThis->hWakeupEvent,
+ VBOX_WAKEUP_EVENT_INDEX);
+#endif
+
+ rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pSlirpThread, pThis, drvNATAsyncIoThread,
+ drvNATAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "NAT");
+ AssertRCReturn(rc, rc);
+
+ pThis->enmLinkState = pThis->enmLinkStateWant = PDMNETWORKLINKSTATE_UP;
+
+#ifdef RT_OS_DARWIN
+ /* Set up a watcher which notifies us everytime the DNS server changes. */
+ int rc2 = VINF_SUCCESS;
+ SCDynamicStoreContext SCDynStorCtx;
+
+ SCDynStorCtx.version = 0;
+ SCDynStorCtx.info = pThis;
+ SCDynStorCtx.retain = NULL;
+ SCDynStorCtx.release = NULL;
+ SCDynStorCtx.copyDescription = NULL;
+
+ SCDynamicStoreRef hDynStor = SCDynamicStoreCreate(NULL, CFSTR("org.virtualbox.drvnat"), drvNatDnsChanged, &SCDynStorCtx);
+ if (hDynStor)
+ {
+ CFRunLoopSourceRef hRunLoopSrc = SCDynamicStoreCreateRunLoopSource(NULL, hDynStor, 0);
+ if (hRunLoopSrc)
+ {
+ CFStringRef aWatchKeys[] =
+ {
+ CFSTR("State:/Network/Global/DNS")
+ };
+ CFArrayRef hArray = CFArrayCreate(NULL, (const void **)aWatchKeys, 1, &kCFTypeArrayCallBacks);
+
+ if (hArray)
+ {
+ if (SCDynamicStoreSetNotificationKeys(hDynStor, hArray, NULL))
+ {
+ CFRunLoopRef hRunLoopMain = CFRunLoopGetMain();
+ CFRetain(hRunLoopMain);
+ CFRunLoopAddSource(hRunLoopMain, hRunLoopSrc, kCFRunLoopCommonModes);
+ CFRelease(hRunLoopMain);
+ pThis->hRunLoopSrcDnsWatcher = hRunLoopSrc;
+ }
+ else
+ rc2 = VERR_NO_MEMORY;
+
+ CFRelease(hArray);
+ }
+ else
+ rc2 = VERR_NO_MEMORY;
+
+ if (RT_FAILURE(rc2)) /* Keep the runloop source referenced for destruction. */
+ CFRelease(hRunLoopSrc);
+ }
+ CFRelease(hDynStor);
+ }
+ else
+ rc2 = VERR_NO_MEMORY;
+
+ if (RT_FAILURE(rc2))
+ LogRel(("NAT#%d: Failed to install DNS change notifier. The guest might loose DNS access when switching networks on the host\n",
+ pDrvIns->iInstance));
+#endif
+ return rc;
+ }
+
+ /* failure path */
+ slirp_term(pThis->pNATState);
+ pThis->pNATState = NULL;
+ }
+ else
+ {
+ PDMDRV_SET_ERROR(pDrvIns, rc, N_("Unknown error during NAT networking setup: "));
+ AssertMsgFailed(("Add error message for rc=%d (%Rrc)\n", rc, rc));
+ }
+
+ return rc;
+}
+
+
+/**
+ * NAT network transport driver registration record.
+ */
+const PDMDRVREG g_DrvNAT =
+{
+ /* u32Version */
+ PDM_DRVREG_VERSION,
+ /* szName */
+ "NAT",
+ /* szRCMod */
+ "",
+ /* szR0Mod */
+ "",
+ /* pszDescription */
+ "NAT Network Transport Driver",
+ /* fFlags */
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+ /* fClass. */
+ PDM_DRVREG_CLASS_NETWORK,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(DRVNAT),
+ /* pfnConstruct */
+ drvNATConstruct,
+ /* pfnDestruct */
+ drvNATDestruct,
+ /* pfnRelocate */
+ NULL,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ drvNATPowerOn,
+ /* pfnReset */
+ NULL,
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ drvNATResume,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32EndVersion */
+ PDM_DRVREG_VERSION
+};
+
diff --git a/src/VBox/Devices/Network/DrvNetShaper.cpp b/src/VBox/Devices/Network/DrvNetShaper.cpp
new file mode 100644
index 00000000..2e8a505a
--- /dev/null
+++ b/src/VBox/Devices/Network/DrvNetShaper.cpp
@@ -0,0 +1,641 @@
+/* $Id: DrvNetShaper.cpp $ */
+/** @file
+ * NetShaperFilter - Network shaper filter driver.
+ */
+
+/*
+ * Copyright (C) 2011-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_NET_SHAPER
+
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmnetifs.h>
+#include <VBox/vmm/pdmnetshaper.h>
+
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/string.h>
+#include <iprt/uuid.h>
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+#if 0
+#define VBOX_WITH_DRVNETSHAPER_IN_R0
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Block driver instance data.
+ *
+ * @implements PDMINETWORKUP
+ * @implements PDMINETWORKDOWN
+ * @implements PDMINETWORKCONFIG
+ */
+typedef struct DRVNETSHAPER
+{
+ /** Pointer to the driver instance. */
+ PPDMDRVINS pDrvInsR3;
+ /** The network interface. */
+ PDMINETWORKUP INetworkUpR3;
+ /** The connector that's attached to us. */
+ PPDMINETWORKUP pIBelowNetR3;
+
+#ifdef VBOX_WITH_DRVNETSHAPER_IN_R0
+ /** Pointer to the driver instance. */
+ PPDMDRVINSR0 pDrvInsR0;
+ /** The network interface. */
+ PDMINETWORKUPR0 INetworkUpR0;
+ /** The connector that's attached to us. */
+ PPDMINETWORKUPR0 pIBelowNetR0;
+
+ /** Ring-3 base interface for the ring-0 context. */
+ PDMIBASER0 IBaseR0;
+ /** Ring-3 base interface for the raw-mode context. */
+ PDMIBASERC IBaseRC;
+#endif
+
+ /** For when we're the leaf driver. */
+ PDMCRITSECT XmitLock;
+
+ /** The network interface. */
+ PDMINETWORKDOWN INetworkDown;
+ /** The network config interface.
+ * @todo this is a main interface and shouldn't be here... */
+ PDMINETWORKCONFIG INetworkConfig;
+ /** The port we're attached to. */
+ PPDMINETWORKDOWN pIAboveNet;
+ /** The config port interface we're attached to. */
+ PPDMINETWORKCONFIG pIAboveConfig;
+ /** The filter that represents us at bandwidth group. */
+ PDMNSFILTER Filter;
+ /** The name of bandwidth group we are attached to. */
+ char * pszBwGroup;
+
+ /** TX: Total number of bytes to allocate. */
+ STAMCOUNTER StatXmitBytesRequested;
+ /** TX: Number of bytes delayed. */
+ STAMCOUNTER StatXmitBytesDenied;
+ /** TX: Number of bytes allowed to pass. */
+ STAMCOUNTER StatXmitBytesGranted;
+ /** TX: Total number of packets being sent. */
+ STAMCOUNTER StatXmitPktsRequested;
+ /** TX: Number of packets delayed. */
+ STAMCOUNTER StatXmitPktsDenied;
+ /** TX: Number of packets allowed to pass. */
+ STAMCOUNTER StatXmitPktsGranted;
+ /** TX: Number of calls to pfnXmitPending. */
+ STAMCOUNTER StatXmitPendingCalled;
+} DRVNETSHAPER, *PDRVNETSHAPER;
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
+ */
+PDMBOTHCBDECL(int) drvNetShaperUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
+{
+ PDRVNETSHAPER pThis = RT_FROM_MEMBER(pInterface, DRVNETSHAPER, CTX_SUFF(INetworkUp));
+ if (RT_UNLIKELY(!pThis->CTX_SUFF(pIBelowNet)))
+ {
+ int rc = PDMDrvHlpCritSectTryEnter(pThis->CTX_SUFF(pDrvIns), &pThis->XmitLock);
+ if (RT_UNLIKELY(rc == VERR_SEM_BUSY))
+ rc = VERR_TRY_AGAIN;
+ return rc;
+ }
+ return pThis->CTX_SUFF(pIBelowNet)->pfnBeginXmit(pThis->CTX_SUFF(pIBelowNet), fOnWorkerThread);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
+ */
+PDMBOTHCBDECL(int) drvNetShaperUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
+ PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
+{
+ PDRVNETSHAPER pThis = RT_FROM_MEMBER(pInterface, DRVNETSHAPER, CTX_SUFF(INetworkUp));
+ if (pThis->CTX_SUFF(pIBelowNet))
+ {
+ //LogFlow(("drvNetShaperUp_AllocBuf: cb=%d\n", cbMin));
+ STAM_REL_COUNTER_ADD(&pThis->StatXmitBytesRequested, cbMin);
+ STAM_REL_COUNTER_INC(&pThis->StatXmitPktsRequested);
+ if (!PDMDrvHlpNetShaperAllocateBandwidth(pThis->CTX_SUFF(pDrvIns), &pThis->Filter, cbMin))
+ {
+ STAM_REL_COUNTER_ADD(&pThis->StatXmitBytesDenied, cbMin);
+ STAM_REL_COUNTER_INC(&pThis->StatXmitPktsDenied);
+ return VERR_TRY_AGAIN;
+ }
+ STAM_REL_COUNTER_ADD(&pThis->StatXmitBytesGranted, cbMin);
+ STAM_REL_COUNTER_INC(&pThis->StatXmitPktsGranted);
+ //LogFlow(("drvNetShaperUp_AllocBuf: got cb=%d\n", cbMin));
+ return pThis->CTX_SUFF(pIBelowNet)->pfnAllocBuf(pThis->CTX_SUFF(pIBelowNet), cbMin, pGso, ppSgBuf);
+ }
+ return VERR_NET_DOWN;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
+ */
+PDMBOTHCBDECL(int) drvNetShaperUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
+{
+ PDRVNETSHAPER pThis = RT_FROM_MEMBER(pInterface, DRVNETSHAPER, CTX_SUFF(INetworkUp));
+ if (RT_UNLIKELY(!pThis->CTX_SUFF(pIBelowNet)))
+ return VERR_NET_DOWN;
+ return pThis->CTX_SUFF(pIBelowNet)->pfnFreeBuf(pThis->CTX_SUFF(pIBelowNet), pSgBuf);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
+ */
+PDMBOTHCBDECL(int) drvNetShaperUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
+{
+ PDRVNETSHAPER pThis = RT_FROM_MEMBER(pInterface, DRVNETSHAPER, CTX_SUFF(INetworkUp));
+ if (RT_UNLIKELY(!pThis->CTX_SUFF(pIBelowNet)))
+ return VERR_NET_DOWN;
+
+ return pThis->CTX_SUFF(pIBelowNet)->pfnSendBuf(pThis->CTX_SUFF(pIBelowNet), pSgBuf, fOnWorkerThread);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
+ */
+PDMBOTHCBDECL(void) drvNetShaperUp_EndXmit(PPDMINETWORKUP pInterface)
+{
+ //LogFlow(("drvNetShaperUp_EndXmit:\n"));
+ PDRVNETSHAPER pThis = RT_FROM_MEMBER(pInterface, DRVNETSHAPER, CTX_SUFF(INetworkUp));
+ if (RT_LIKELY(pThis->CTX_SUFF(pIBelowNet)))
+ pThis->CTX_SUFF(pIBelowNet)->pfnEndXmit(pThis->CTX_SUFF(pIBelowNet));
+ else
+ PDMDrvHlpCritSectLeave(pThis->CTX_SUFF(pDrvIns), &pThis->XmitLock);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
+ */
+PDMBOTHCBDECL(void) drvNetShaperUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
+{
+ LogFlow(("drvNetShaperUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
+ PDRVNETSHAPER pThis = RT_FROM_MEMBER(pInterface, DRVNETSHAPER, CTX_SUFF(INetworkUp));
+ if (pThis->CTX_SUFF(pIBelowNet))
+ pThis->CTX_SUFF(pIBelowNet)->pfnSetPromiscuousMode(pThis->CTX_SUFF(pIBelowNet), fPromiscuous);
+}
+
+
+#ifdef IN_RING3
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnNotifyLinkChanged}
+ */
+static DECLCALLBACK(void) drvR3NetShaperUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
+{
+ LogFlow(("drvNetShaperUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
+ PDRVNETSHAPER pThis = RT_FROM_MEMBER(pInterface, DRVNETSHAPER, CTX_SUFF(INetworkUp));
+ if (pThis->pIBelowNetR3)
+ pThis->pIBelowNetR3->pfnNotifyLinkChanged(pThis->pIBelowNetR3, enmLinkState);
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
+ */
+static DECLCALLBACK(int) drvR3NetShaperDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
+{
+ PDRVNETSHAPER pThis = RT_FROM_MEMBER(pInterface, DRVNETSHAPER, INetworkDown);
+ return pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, cMillies);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
+ */
+static DECLCALLBACK(int) drvR3NetShaperDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
+{
+ PDRVNETSHAPER pThis = RT_FROM_MEMBER(pInterface, DRVNETSHAPER, INetworkDown);
+ return pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pvBuf, cb);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnReceiveGso}
+ */
+static DECLCALLBACK(int) drvR3NetShaperDown_ReceiveGso(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb, PCPDMNETWORKGSO pGso)
+{
+ PDRVNETSHAPER pThis = RT_FROM_MEMBER(pInterface, DRVNETSHAPER, INetworkDown);
+ if (pThis->pIAboveNet->pfnReceiveGso)
+ return pThis->pIAboveNet->pfnReceiveGso(pThis->pIAboveNet, pvBuf, cb, pGso);
+ return VERR_NOT_SUPPORTED;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
+ */
+static DECLCALLBACK(void) drvR3NetShaperDown_XmitPending(PPDMINETWORKDOWN pInterface)
+{
+ PDRVNETSHAPER pThis = RT_FROM_MEMBER(pInterface, DRVNETSHAPER, INetworkDown);
+ STAM_REL_COUNTER_INC(&pThis->StatXmitPendingCalled);
+ pThis->pIAboveNet->pfnXmitPending(pThis->pIAboveNet);
+}
+
+
+/**
+ * Gets the current Media Access Control (MAC) address.
+ *
+ * @returns VBox status code.
+ * @param pInterface Pointer to the interface structure containing the called function pointer.
+ * @param pMac Where to store the MAC address.
+ * @thread EMT
+ */
+static DECLCALLBACK(int) drvR3NetShaperDownCfg_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
+{
+ PDRVNETSHAPER pThis = RT_FROM_MEMBER(pInterface, DRVNETSHAPER, INetworkConfig);
+ return pThis->pIAboveConfig->pfnGetMac(pThis->pIAboveConfig, pMac);
+}
+
+/**
+ * Gets the new link state.
+ *
+ * @returns The current link state.
+ * @param pInterface Pointer to the interface structure containing the called function pointer.
+ * @thread EMT
+ */
+static DECLCALLBACK(PDMNETWORKLINKSTATE) drvR3NetShaperDownCfg_GetLinkState(PPDMINETWORKCONFIG pInterface)
+{
+ PDRVNETSHAPER pThis = RT_FROM_MEMBER(pInterface, DRVNETSHAPER, INetworkConfig);
+ return pThis->pIAboveConfig->pfnGetLinkState(pThis->pIAboveConfig);
+}
+
+/**
+ * Sets the new link state.
+ *
+ * @returns VBox status code.
+ * @param pInterface Pointer to the interface structure containing the called function pointer.
+ * @param enmState The new link state
+ * @thread EMT
+ */
+static DECLCALLBACK(int) drvR3NetShaperDownCfg_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
+{
+ PDRVNETSHAPER pThis = RT_FROM_MEMBER(pInterface, DRVNETSHAPER, INetworkConfig);
+ return pThis->pIAboveConfig->pfnSetLinkState(pThis->pIAboveConfig, enmState);
+}
+
+
+#ifdef VBOX_WITH_DRVNETSHAPER_IN_R0
+
+/**
+ * @interface_method_impl{PDMIBASER0,pfnQueryInterface}
+ */
+static DECLCALLBACK(RTR0PTR) drvR3NetShaperIBaseR0_QueryInterface(PPDMIBASER0 pInterface, const char *pszIID)
+{
+ PDRVNETSHAPER pThis = RT_FROM_MEMBER(pInterface, DRVNETSHAPER, IBaseR0);
+ /*
+ * We need to check if the underlying driver supports R0. If it does not,
+ * then it is useless and even harmful to support R0 here, as we will end up
+ * returning errors when a network adapter tries to allocate a buffer in R0.
+ */
+ if (pThis->pIBelowNetR0)
+ PDMIBASER0_RETURN_INTERFACE(pThis->pDrvInsR3, pszIID, PDMINETWORKUP, &pThis->INetworkUpR0);
+ return NIL_RTR0PTR;
+}
+
+/**
+ * @interface_method_impl{PDMIBASERC,pfnQueryInterface}
+ */
+static DECLCALLBACK(RTRCPTR) drvR3NetShaperIBaseRC_QueryInterface(PPDMIBASERC pInterface, const char *pszIID)
+{
+ RT_NOREF(pInterface, pszIID);
+ return NIL_RTRCPTR;
+}
+
+#endif
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvR3NetShaperIBase_QueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+ PDRVNETSHAPER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSHAPER);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+#ifdef VBOX_WITH_DRVNETSHAPER_IN_R0
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASER0, &pThis->IBaseR0);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASERC, &pThis->IBaseRC);
+#endif
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUpR3);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThis->INetworkDown);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThis->INetworkConfig);
+ return NULL;
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnDetach}
+ */
+static DECLCALLBACK(void) drvR3NetShaperDetach(PPDMDRVINS pDrvIns, uint32_t fFlags)
+{
+ RT_NOREF(fFlags);
+ PDRVNETSHAPER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSHAPER);
+
+ LogFlow(("drvNetShaperDetach: pDrvIns: %p, fFlags: %u\n", pDrvIns, fFlags));
+ PDMDrvHlpCritSectEnter(pDrvIns, &pThis->XmitLock, VERR_IGNORED);
+ pThis->pIBelowNetR3 = NULL;
+#ifdef VBOX_WITH_DRVNETSHAPER_IN_R0
+ pThis->pIBelowNetR0 = NIL_RTR0PTR;
+#endif
+ PDMDrvHlpCritSectLeave(pDrvIns, &pThis->XmitLock);
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnAttach}
+ */
+static DECLCALLBACK(int) drvR3NetShaperAttach(PPDMDRVINS pDrvIns, uint32_t fFlags)
+{
+ PDRVNETSHAPER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSHAPER);
+ LogFlow(("drvNetShaperAttach/#%#x: fFlags=%#x\n", pDrvIns->iInstance, fFlags));
+ PDMDrvHlpCritSectEnter(pDrvIns, &pThis->XmitLock, VERR_IGNORED);
+
+ /*
+ * Query the network connector interface.
+ */
+ PPDMIBASE pBaseDown;
+ int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBaseDown);
+ if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
+ || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
+ {
+ pThis->pIBelowNetR3 = NULL;
+#ifdef VBOX_WITH_DRVNETSHAPER_IN_R0
+ pThis->pIBelowNetR0 = NIL_RTR0PTR;
+#endif
+ rc = VINF_SUCCESS;
+ }
+ else if (RT_SUCCESS(rc))
+ {
+ pThis->pIBelowNetR3 = PDMIBASE_QUERY_INTERFACE(pBaseDown, PDMINETWORKUP);
+ if (pThis->pIBelowNetR3)
+ {
+#ifdef VBOX_WITH_DRVNETSHAPER_IN_R0
+ PPDMIBASER0 pBaseR0 = PDMIBASE_QUERY_INTERFACE(pBaseDown, PDMIBASER0);
+ pThis->pIBelowNetR0 = pBaseR0 ? pBaseR0->pfnQueryInterface(pBaseR0, PDMINETWORKUP_IID) : NIL_RTR0PTR;
+#endif
+ rc = VINF_SUCCESS;
+ }
+ else
+ {
+ AssertMsgFailed(("Configuration error: the driver below didn't export the network connector interface!\n"));
+ rc = VERR_PDM_MISSING_INTERFACE_BELOW;
+ }
+ }
+ else
+ AssertMsgFailed(("Failed to attach to driver below! rc=%Rrc\n", rc));
+
+ PDMDrvHlpCritSectLeave(pDrvIns, &pThis->XmitLock);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnDestruct}
+ */
+static DECLCALLBACK(void) drvR3NetShaperDestruct(PPDMDRVINS pDrvIns)
+{
+ PDRVNETSHAPER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSHAPER);
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+
+ PDMDrvHlpNetShaperDetach(pDrvIns, &pThis->Filter);
+
+ if (PDMDrvHlpCritSectIsInitialized(pDrvIns, &pThis->XmitLock))
+ PDMDrvHlpCritSectDelete(pDrvIns, &pThis->XmitLock);
+}
+
+
+/**
+ * @interface_method_impl{Construct a NAT network transport driver instance,
+ * PDMDRVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) drvR3NetShaperConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVNETSHAPER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSHAPER);
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ LogFlow(("drvNetShaperConstruct:\n"));
+
+ /*
+ * Init the static parts.
+ */
+ pThis->pDrvInsR3 = pDrvIns;
+#ifdef VBOX_WITH_DRVNETSHAPER_IN_R0
+ pThis->pDrvInsR0 = PDMDRVINS_2_R0PTR(pDrvIns);
+#endif
+ /* IBase */
+ pDrvIns->IBase.pfnQueryInterface = drvR3NetShaperIBase_QueryInterface;
+#ifdef VBOX_WITH_DRVNETSHAPER_IN_R0
+ pThis->IBaseR0.pfnQueryInterface = drvR3NetShaperIBaseR0_QueryInterface;
+ pThis->IBaseRC.pfnQueryInterface = drvR3NetShaperIBaseRC_QueryInterface;
+#endif
+ /* INetworkUp */
+ pThis->INetworkUpR3.pfnBeginXmit = drvNetShaperUp_BeginXmit;
+ pThis->INetworkUpR3.pfnAllocBuf = drvNetShaperUp_AllocBuf;
+ pThis->INetworkUpR3.pfnFreeBuf = drvNetShaperUp_FreeBuf;
+ pThis->INetworkUpR3.pfnSendBuf = drvNetShaperUp_SendBuf;
+ pThis->INetworkUpR3.pfnEndXmit = drvNetShaperUp_EndXmit;
+ pThis->INetworkUpR3.pfnSetPromiscuousMode = drvNetShaperUp_SetPromiscuousMode;
+ pThis->INetworkUpR3.pfnNotifyLinkChanged = drvR3NetShaperUp_NotifyLinkChanged;
+#ifdef VBOX_WITH_DRVNETSHAPER_IN_R0
+ /* Resolve the ring-0 context interface addresses. */
+ if (true)
+ {
+ int rc = pDrvIns->pHlpR3->pfnLdrGetR0InterfaceSymbols(pDrvIns, &pThis->INetworkUpR0, sizeof(pThis->INetworkUpR0),
+ "drvNetShaperUp_", PDMINETWORKUP_SYM_LIST);
+ AssertLogRelRCReturn(rc, rc);
+ }
+#endif
+ /* INetworkDown */
+ pThis->INetworkDown.pfnWaitReceiveAvail = drvR3NetShaperDown_WaitReceiveAvail;
+ pThis->INetworkDown.pfnReceive = drvR3NetShaperDown_Receive;
+ pThis->INetworkDown.pfnReceiveGso = drvR3NetShaperDown_ReceiveGso;
+ pThis->INetworkDown.pfnXmitPending = drvR3NetShaperDown_XmitPending;
+ /* INetworkConfig */
+ pThis->INetworkConfig.pfnGetMac = drvR3NetShaperDownCfg_GetMac;
+ pThis->INetworkConfig.pfnGetLinkState = drvR3NetShaperDownCfg_GetLinkState;
+ pThis->INetworkConfig.pfnSetLinkState = drvR3NetShaperDownCfg_SetLinkState;
+
+ /*
+ * Create the locks.
+ */
+ int rc = PDMDrvHlpCritSectInit(pDrvIns, &pThis->XmitLock, RT_SRC_POS, "NetShaper");
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Validate the config.
+ */
+ PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "BwGroup", "");
+
+ /*
+ * Find the bandwidth group we have to attach to.
+ */
+ rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "BwGroup", &pThis->pszBwGroup);
+ if (RT_FAILURE(rc) && rc != VERR_CFGM_VALUE_NOT_FOUND)
+ return PDMDRV_SET_ERROR(pDrvIns, rc, N_("DrvNetShaper: Configuration error: Querying \"BwGroup\" as string failed"));
+
+ pThis->Filter.pIDrvNetR3 = &pThis->INetworkDown;
+ rc = PDMDrvHlpNetShaperAttach(pDrvIns, pThis->pszBwGroup, &pThis->Filter);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc, N_("DrvNetShaper: Configuration error: Failed to attach to bandwidth group"));
+
+ /*
+ * Query the network port interface.
+ */
+ pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
+ if (!pThis->pIAboveNet)
+ {
+ AssertMsgFailed(("Configuration error: the above device/driver didn't export the network port interface!\n"));
+ return VERR_PDM_MISSING_INTERFACE_ABOVE;
+ }
+
+ /*
+ * Query the network config interface.
+ */
+ pThis->pIAboveConfig = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
+ if (!pThis->pIAboveConfig)
+ {
+ AssertMsgFailed(("Configuration error: the above device/driver didn't export the network config interface!\n"));
+ return VERR_PDM_MISSING_INTERFACE_ABOVE;
+ }
+
+ /*
+ * Query the network connector interface.
+ */
+ PPDMIBASE pBaseDown = NULL;
+ rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBaseDown);
+ if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
+ || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
+ {
+ pThis->pIBelowNetR3 = NULL;
+#ifdef VBOX_WITH_DRVNETSHAPER_IN_R0
+ pThis->pIBelowNetR0 = NIL_RTR0PTR;
+#endif
+ }
+ else if (RT_SUCCESS(rc))
+ {
+ pThis->pIBelowNetR3 = PDMIBASE_QUERY_INTERFACE(pBaseDown, PDMINETWORKUP);
+ if (!pThis->pIBelowNetR3)
+ {
+ AssertMsgFailed(("Configuration error: the driver below didn't export the network connector interface!\n"));
+ return VERR_PDM_MISSING_INTERFACE_BELOW;
+ }
+#ifdef VBOX_WITH_DRVNETSHAPER_IN_R0
+ PPDMIBASER0 pBaseR0 = PDMIBASE_QUERY_INTERFACE(pBaseDown, PDMIBASER0);
+ pThis->pIBelowNetR0 = pBaseR0 ? pBaseR0->pfnQueryInterface(pBaseR0, PDMINETWORKUP_IID) : NIL_RTR0PTR;
+#endif
+ }
+ else
+ {
+ AssertMsgFailed(("Failed to attach to driver below! rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ /*
+ * Register statistics.
+ */
+ PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->StatXmitBytesRequested, "Bytes/Tx/Requested", STAMUNIT_BYTES, "Number of requested TX bytes.");
+ PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->StatXmitBytesDenied, "Bytes/Tx/Denied", STAMUNIT_BYTES, "Number of denied TX bytes.");
+ PDMDrvHlpSTAMRegCounterEx(pDrvIns, &pThis->StatXmitBytesGranted, "Bytes/Tx/Granted", STAMUNIT_BYTES, "Number of granted TX bytes.");
+
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->StatXmitPktsRequested, "Packets/Tx/Requested", "Number of requested TX packets.");
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->StatXmitPktsDenied, "Packets/Tx/Denied", "Number of denied TX packets.");
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->StatXmitPktsGranted, "Packets/Tx/Granted", "Number of granted TX packets.");
+ PDMDrvHlpSTAMRegCounter(pDrvIns, &pThis->StatXmitPendingCalled, "Tx/WakeUp", "Number of wakeup TX calls.");
+
+ return VINF_SUCCESS;
+}
+
+
+
+/**
+ * Network sniffer filter driver registration record.
+ */
+const PDMDRVREG g_DrvNetShaper =
+{
+ /* u32Version */
+ PDM_DRVREG_VERSION,
+ /* szName */
+ "NetShaper",
+ /* szRCMod */
+ "",
+ /* szR0Mod */
+ "VBoxDDR0.r0",
+ /* pszDescription */
+ "Network Shaper Filter Driver",
+ /* fFlags */
+#ifdef VBOX_WITH_DRVNETSHAPER_IN_R0
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT | PDM_DRVREG_FLAGS_R0,
+#else
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+#endif
+ /* fClass. */
+ PDM_DRVREG_CLASS_NETWORK,
+ /* cMaxInstances */
+ UINT32_MAX,
+ /* cbInstance */
+ sizeof(DRVNETSHAPER),
+ /* pfnConstruct */
+ drvR3NetShaperConstruct,
+ /* pfnDestruct */
+ drvR3NetShaperDestruct,
+ /* pfnRelocate */
+ NULL,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ NULL,
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ drvR3NetShaperAttach,
+ /* pfnDetach */
+ drvR3NetShaperDetach,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32EndVersion */
+ PDM_DRVREG_VERSION
+};
+#endif /* IN_RING3 */
diff --git a/src/VBox/Devices/Network/DrvNetSniffer.cpp b/src/VBox/Devices/Network/DrvNetSniffer.cpp
new file mode 100644
index 00000000..b863f9eb
--- /dev/null
+++ b/src/VBox/Devices/Network/DrvNetSniffer.cpp
@@ -0,0 +1,575 @@
+/* $Id: DrvNetSniffer.cpp $ */
+/** @file
+ * DrvNetSniffer - Network sniffer filter driver.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_NAT
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmnetifs.h>
+
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/file.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/uuid.h>
+#include <iprt/path.h>
+#include <VBox/param.h>
+
+#include "Pcap.h"
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Block driver instance data.
+ *
+ * @implements PDMINETWORKUP
+ * @implements PDMINETWORKDOWN
+ * @implements PDMINETWORKCONFIG
+ */
+typedef struct DRVNETSNIFFER
+{
+ /** The network interface. */
+ PDMINETWORKUP INetworkUp;
+ /** The network interface. */
+ PDMINETWORKDOWN INetworkDown;
+ /** The network config interface.
+ * @todo this is a main interface and shouldn't be here... */
+ PDMINETWORKCONFIG INetworkConfig;
+ /** The port we're attached to. */
+ PPDMINETWORKDOWN pIAboveNet;
+ /** The config port interface we're attached to. */
+ PPDMINETWORKCONFIG pIAboveConfig;
+ /** The connector that's attached to us. */
+ PPDMINETWORKUP pIBelowNet;
+ /** The filename. */
+ char szFilename[RTPATH_MAX];
+ /** The filehandle. */
+ RTFILE hFile;
+ /** The lock serializing the file access. */
+ RTCRITSECT Lock;
+ /** The NanoTS delta we pass to the pcap writers. */
+ uint64_t StartNanoTS;
+ /** Pointer to the driver instance. */
+ PPDMDRVINS pDrvIns;
+ /** For when we're the leaf driver. */
+ RTCRITSECT XmitLock;
+
+} DRVNETSNIFFER, *PDRVNETSNIFFER;
+
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
+ */
+static DECLCALLBACK(int) drvNetSnifferUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
+{
+ PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp);
+ if (RT_UNLIKELY(!pThis->pIBelowNet))
+ {
+ int rc = RTCritSectTryEnter(&pThis->XmitLock);
+ if (RT_UNLIKELY(rc == VERR_SEM_BUSY))
+ rc = VERR_TRY_AGAIN;
+ return rc;
+ }
+ return pThis->pIBelowNet->pfnBeginXmit(pThis->pIBelowNet, fOnWorkerThread);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
+ */
+static DECLCALLBACK(int) drvNetSnifferUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
+ PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
+{
+ PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp);
+ if (RT_UNLIKELY(!pThis->pIBelowNet))
+ return VERR_NET_DOWN;
+ return pThis->pIBelowNet->pfnAllocBuf(pThis->pIBelowNet, cbMin, pGso, ppSgBuf);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
+ */
+static DECLCALLBACK(int) drvNetSnifferUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
+{
+ PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp);
+ if (RT_UNLIKELY(!pThis->pIBelowNet))
+ return VERR_NET_DOWN;
+ return pThis->pIBelowNet->pfnFreeBuf(pThis->pIBelowNet, pSgBuf);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
+ */
+static DECLCALLBACK(int) drvNetSnifferUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
+{
+ PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp);
+ if (RT_UNLIKELY(!pThis->pIBelowNet))
+ return VERR_NET_DOWN;
+
+ /* output to sniffer */
+ RTCritSectEnter(&pThis->Lock);
+ if (!pSgBuf->pvUser)
+ PcapFileFrame(pThis->hFile, pThis->StartNanoTS,
+ pSgBuf->aSegs[0].pvSeg,
+ pSgBuf->cbUsed,
+ RT_MIN(pSgBuf->cbUsed, pSgBuf->aSegs[0].cbSeg));
+ else
+ PcapFileGsoFrame(pThis->hFile, pThis->StartNanoTS, (PCPDMNETWORKGSO)pSgBuf->pvUser,
+ pSgBuf->aSegs[0].pvSeg,
+ pSgBuf->cbUsed,
+ RT_MIN(pSgBuf->cbUsed, pSgBuf->aSegs[0].cbSeg));
+ RTCritSectLeave(&pThis->Lock);
+
+ return pThis->pIBelowNet->pfnSendBuf(pThis->pIBelowNet, pSgBuf, fOnWorkerThread);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
+ */
+static DECLCALLBACK(void) drvNetSnifferUp_EndXmit(PPDMINETWORKUP pInterface)
+{
+ LogFlow(("drvNetSnifferUp_EndXmit:\n"));
+ PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp);
+ if (RT_LIKELY(pThis->pIBelowNet))
+ pThis->pIBelowNet->pfnEndXmit(pThis->pIBelowNet);
+ else
+ RTCritSectLeave(&pThis->XmitLock);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
+ */
+static DECLCALLBACK(void) drvNetSnifferUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
+{
+ LogFlow(("drvNetSnifferUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
+ PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp);
+ if (pThis->pIBelowNet)
+ pThis->pIBelowNet->pfnSetPromiscuousMode(pThis->pIBelowNet, fPromiscuous);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnNotifyLinkChanged}
+ */
+static DECLCALLBACK(void) drvNetSnifferUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
+{
+ LogFlow(("drvNetSnifferUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
+ PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkUp);
+ if (pThis->pIBelowNet)
+ pThis->pIBelowNet->pfnNotifyLinkChanged(pThis->pIBelowNet, enmLinkState);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnWaitReceiveAvail}
+ */
+static DECLCALLBACK(int) drvNetSnifferDown_WaitReceiveAvail(PPDMINETWORKDOWN pInterface, RTMSINTERVAL cMillies)
+{
+ PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkDown);
+ return pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, cMillies);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnReceive}
+ */
+static DECLCALLBACK(int) drvNetSnifferDown_Receive(PPDMINETWORKDOWN pInterface, const void *pvBuf, size_t cb)
+{
+ PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkDown);
+
+ /* output to sniffer */
+ RTCritSectEnter(&pThis->Lock);
+ PcapFileFrame(pThis->hFile, pThis->StartNanoTS, pvBuf, cb, cb);
+ RTCritSectLeave(&pThis->Lock);
+
+ /* pass up */
+ int rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pvBuf, cb);
+#if 0
+ RTCritSectEnter(&pThis->Lock);
+ u64TS = RTTimeProgramNanoTS();
+ Hdr.ts_sec = (uint32_t)(u64TS / 1000000000);
+ Hdr.ts_usec = (uint32_t)((u64TS / 1000) % 1000000);
+ Hdr.incl_len = 0;
+ RTFileWrite(pThis->hFile, &Hdr, sizeof(Hdr), NULL);
+ RTCritSectLeave(&pThis->Lock);
+#endif
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKDOWN,pfnXmitPending}
+ */
+static DECLCALLBACK(void) drvNetSnifferDown_XmitPending(PPDMINETWORKDOWN pInterface)
+{
+ PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkDown);
+ pThis->pIAboveNet->pfnXmitPending(pThis->pIAboveNet);
+}
+
+
+/**
+ * Gets the current Media Access Control (MAC) address.
+ *
+ * @returns VBox status code.
+ * @param pInterface Pointer to the interface structure containing the called function pointer.
+ * @param pMac Where to store the MAC address.
+ * @thread EMT
+ */
+static DECLCALLBACK(int) drvNetSnifferDownCfg_GetMac(PPDMINETWORKCONFIG pInterface, PRTMAC pMac)
+{
+ PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkConfig);
+ return pThis->pIAboveConfig->pfnGetMac(pThis->pIAboveConfig, pMac);
+}
+
+/**
+ * Gets the new link state.
+ *
+ * @returns The current link state.
+ * @param pInterface Pointer to the interface structure containing the called function pointer.
+ * @thread EMT
+ */
+static DECLCALLBACK(PDMNETWORKLINKSTATE) drvNetSnifferDownCfg_GetLinkState(PPDMINETWORKCONFIG pInterface)
+{
+ PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkConfig);
+ return pThis->pIAboveConfig->pfnGetLinkState(pThis->pIAboveConfig);
+}
+
+/**
+ * Sets the new link state.
+ *
+ * @returns VBox status code.
+ * @param pInterface Pointer to the interface structure containing the called function pointer.
+ * @param enmState The new link state
+ * @thread EMT
+ */
+static DECLCALLBACK(int) drvNetSnifferDownCfg_SetLinkState(PPDMINETWORKCONFIG pInterface, PDMNETWORKLINKSTATE enmState)
+{
+ PDRVNETSNIFFER pThis = RT_FROM_MEMBER(pInterface, DRVNETSNIFFER, INetworkConfig);
+ return pThis->pIAboveConfig->pfnSetLinkState(pThis->pIAboveConfig, enmState);
+}
+
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvNetSnifferQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+ PDRVNETSNIFFER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSNIFFER);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKDOWN, &pThis->INetworkDown);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKCONFIG, &pThis->INetworkConfig);
+ return NULL;
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnDetach}
+ */
+static DECLCALLBACK(void) drvNetSnifferDetach(PPDMDRVINS pDrvIns, uint32_t fFlags)
+{
+ RT_NOREF(fFlags);
+ PDRVNETSNIFFER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSNIFFER);
+
+ LogFlow(("drvNetSnifferDetach: pDrvIns: %p, fFlags: %u\n", pDrvIns, fFlags));
+ RTCritSectEnter(&pThis->XmitLock);
+ pThis->pIBelowNet = NULL;
+ RTCritSectLeave(&pThis->XmitLock);
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnAttach}
+ */
+static DECLCALLBACK(int) drvNetSnifferAttach(PPDMDRVINS pDrvIns, uint32_t fFlags)
+{
+ PDRVNETSNIFFER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSNIFFER);
+ LogFlow(("drvNetSnifferAttach/#%#x: fFlags=%#x\n", pDrvIns->iInstance, fFlags));
+ RTCritSectEnter(&pThis->XmitLock);
+
+ /*
+ * Query the network connector interface.
+ */
+ PPDMIBASE pBaseDown;
+ int rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBaseDown);
+ if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
+ || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
+ {
+ pThis->pIBelowNet = NULL;
+ rc = VINF_SUCCESS;
+ }
+ else if (RT_SUCCESS(rc))
+ {
+ pThis->pIBelowNet = PDMIBASE_QUERY_INTERFACE(pBaseDown, PDMINETWORKUP);
+ if (pThis->pIBelowNet)
+ rc = VINF_SUCCESS;
+ else
+ {
+ AssertMsgFailed(("Configuration error: the driver below didn't export the network connector interface!\n"));
+ rc = VERR_PDM_MISSING_INTERFACE_BELOW;
+ }
+ }
+ else
+ AssertMsgFailed(("Failed to attach to driver below! rc=%Rrc\n", rc));
+
+ RTCritSectLeave(&pThis->XmitLock);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnDestruct}
+ */
+static DECLCALLBACK(void) drvNetSnifferDestruct(PPDMDRVINS pDrvIns)
+{
+ PDRVNETSNIFFER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSNIFFER);
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+
+ if (RTCritSectIsInitialized(&pThis->Lock))
+ RTCritSectDelete(&pThis->Lock);
+
+ if (RTCritSectIsInitialized(&pThis->XmitLock))
+ RTCritSectDelete(&pThis->XmitLock);
+
+ if (pThis->hFile != NIL_RTFILE)
+ {
+ RTFileClose(pThis->hFile);
+ pThis->hFile = NIL_RTFILE;
+ }
+}
+
+
+/**
+ * @interface_method_impl{Construct a NAT network transport driver instance,
+ * PDMDRVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) drvNetSnifferConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVNETSNIFFER pThis = PDMINS_2_DATA(pDrvIns, PDRVNETSNIFFER);
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ LogFlow(("drvNetSnifferConstruct:\n"));
+
+ /*
+ * Init the static parts.
+ */
+ pThis->pDrvIns = pDrvIns;
+ pThis->hFile = NIL_RTFILE;
+ /* The pcap file *must* start at time offset 0,0. */
+ pThis->StartNanoTS = RTTimeNanoTS() - RTTimeProgramNanoTS();
+ /* IBase */
+ pDrvIns->IBase.pfnQueryInterface = drvNetSnifferQueryInterface;
+ /* INetworkUp */
+ pThis->INetworkUp.pfnBeginXmit = drvNetSnifferUp_BeginXmit;
+ pThis->INetworkUp.pfnAllocBuf = drvNetSnifferUp_AllocBuf;
+ pThis->INetworkUp.pfnFreeBuf = drvNetSnifferUp_FreeBuf;
+ pThis->INetworkUp.pfnSendBuf = drvNetSnifferUp_SendBuf;
+ pThis->INetworkUp.pfnEndXmit = drvNetSnifferUp_EndXmit;
+ pThis->INetworkUp.pfnSetPromiscuousMode = drvNetSnifferUp_SetPromiscuousMode;
+ pThis->INetworkUp.pfnNotifyLinkChanged = drvNetSnifferUp_NotifyLinkChanged;
+ /* INetworkDown */
+ pThis->INetworkDown.pfnWaitReceiveAvail = drvNetSnifferDown_WaitReceiveAvail;
+ pThis->INetworkDown.pfnReceive = drvNetSnifferDown_Receive;
+ pThis->INetworkDown.pfnXmitPending = drvNetSnifferDown_XmitPending;
+ /* INetworkConfig */
+ pThis->INetworkConfig.pfnGetMac = drvNetSnifferDownCfg_GetMac;
+ pThis->INetworkConfig.pfnGetLinkState = drvNetSnifferDownCfg_GetLinkState;
+ pThis->INetworkConfig.pfnSetLinkState = drvNetSnifferDownCfg_SetLinkState;
+
+ /*
+ * Create the locks.
+ */
+ int rc = RTCritSectInit(&pThis->Lock);
+ AssertRCReturn(rc, rc);
+ rc = RTCritSectInit(&pThis->XmitLock);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Validate the config.
+ */
+ PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "File", "");
+
+ if (pHlp->pfnCFGMGetFirstChild(pCfg))
+ LogRel(("NetSniffer: Found child config entries -- are you trying to redirect ports?\n"));
+
+ /*
+ * Get the filename.
+ */
+ rc = pHlp->pfnCFGMQueryString(pCfg, "File", pThis->szFilename, sizeof(pThis->szFilename));
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+ {
+ if (pDrvIns->iInstance > 0)
+ RTStrPrintf(pThis->szFilename, sizeof(pThis->szFilename), "./VBox-%x-%u.pcap", RTProcSelf(), pDrvIns->iInstance);
+ else
+ RTStrPrintf(pThis->szFilename, sizeof(pThis->szFilename), "./VBox-%x.pcap", RTProcSelf());
+ }
+
+ else if (RT_FAILURE(rc))
+ {
+ AssertMsgFailed(("Failed to query \"File\", rc=%Rrc.\n", rc));
+ return rc;
+ }
+
+ /*
+ * Query the network port interface.
+ */
+ pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
+ if (!pThis->pIAboveNet)
+ {
+ AssertMsgFailed(("Configuration error: the above device/driver didn't export the network port interface!\n"));
+ return VERR_PDM_MISSING_INTERFACE_ABOVE;
+ }
+
+ /*
+ * Query the network config interface.
+ */
+ pThis->pIAboveConfig = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
+ if (!pThis->pIAboveConfig)
+ {
+ AssertMsgFailed(("Configuration error: the above device/driver didn't export the network config interface!\n"));
+ return VERR_PDM_MISSING_INTERFACE_ABOVE;
+ }
+
+ /*
+ * Query the network connector interface.
+ */
+ PPDMIBASE pBaseDown;
+ rc = PDMDrvHlpAttach(pDrvIns, fFlags, &pBaseDown);
+ if ( rc == VERR_PDM_NO_ATTACHED_DRIVER
+ || rc == VERR_PDM_CFG_MISSING_DRIVER_NAME)
+ pThis->pIBelowNet = NULL;
+ else if (RT_SUCCESS(rc))
+ {
+ pThis->pIBelowNet = PDMIBASE_QUERY_INTERFACE(pBaseDown, PDMINETWORKUP);
+ if (!pThis->pIBelowNet)
+ {
+ AssertMsgFailed(("Configuration error: the driver below didn't export the network connector interface!\n"));
+ return VERR_PDM_MISSING_INTERFACE_BELOW;
+ }
+ }
+ else
+ {
+ AssertMsgFailed(("Failed to attach to driver below! rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ /*
+ * Open output file / pipe.
+ */
+ rc = RTFileOpen(&pThis->hFile, pThis->szFilename,
+ RTFILE_O_WRITE | RTFILE_O_CREATE_REPLACE | RTFILE_O_DENY_WRITE);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, rc, RT_SRC_POS,
+ N_("Netsniffer cannot open '%s' for writing. The directory must exist and it must be writable for the current user"), pThis->szFilename);
+
+ char *pszPathReal = RTPathRealDup(pThis->szFilename);
+ if (pszPathReal)
+ {
+ LogRel(("NetSniffer: Sniffing to '%s'\n", pszPathReal));
+ RTStrFree(pszPathReal);
+ }
+ else
+ LogRel(("NetSniffer: Sniffing to '%s'\n", pThis->szFilename));
+
+ /*
+ * Write pcap header.
+ * Some time has gone by since capturing pThis->StartNanoTS so get the
+ * current time again.
+ */
+ PcapFileHdr(pThis->hFile, RTTimeNanoTS());
+
+ return VINF_SUCCESS;
+}
+
+
+
+/**
+ * Network sniffer filter driver registration record.
+ */
+const PDMDRVREG g_DrvNetSniffer =
+{
+ /* u32Version */
+ PDM_DRVREG_VERSION,
+ /* szName */
+ "NetSniffer",
+ /* szRCMod */
+ "",
+ /* szR0Mod */
+ "",
+ /* pszDescription */
+ "Network Sniffer Filter Driver",
+ /* fFlags */
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+ /* fClass. */
+ PDM_DRVREG_CLASS_NETWORK,
+ /* cMaxInstances */
+ UINT32_MAX,
+ /* cbInstance */
+ sizeof(DRVNETSNIFFER),
+ /* pfnConstruct */
+ drvNetSnifferConstruct,
+ /* pfnDestruct */
+ drvNetSnifferDestruct,
+ /* pfnRelocate */
+ NULL,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ NULL,
+ /* pfnSuspend */
+ NULL,
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ drvNetSnifferAttach,
+ /* pfnDetach */
+ drvNetSnifferDetach,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32EndVersion */
+ PDM_DRVREG_VERSION
+};
+
diff --git a/src/VBox/Devices/Network/DrvTAP.cpp b/src/VBox/Devices/Network/DrvTAP.cpp
new file mode 100644
index 00000000..839cd4c1
--- /dev/null
+++ b/src/VBox/Devices/Network/DrvTAP.cpp
@@ -0,0 +1,1075 @@
+/* $Id: DrvTAP.cpp $ */
+/** @file
+ * DrvTAP - Universal TAP network transport driver.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_TUN
+#include <VBox/log.h>
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmnetifs.h>
+#include <VBox/vmm/pdmnetinline.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/pipe.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/uuid.h>
+#ifdef RT_OS_SOLARIS
+# include <iprt/process.h>
+# include <iprt/env.h>
+#endif
+
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#ifdef RT_OS_SOLARIS
+# include <sys/stat.h>
+# include <sys/ethernet.h>
+# include <sys/sockio.h>
+# include <netinet/in.h>
+# include <netinet/in_systm.h>
+# include <netinet/ip.h>
+# include <netinet/ip_icmp.h>
+# include <netinet/udp.h>
+# include <netinet/tcp.h>
+# include <net/if.h>
+# include <stropts.h>
+# include <fcntl.h>
+# include <stdlib.h>
+# include <stdio.h>
+#else
+# include <sys/fcntl.h>
+#endif
+#include <errno.h>
+#include <unistd.h>
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * TAP driver instance data.
+ *
+ * @implements PDMINETWORKUP
+ */
+typedef struct DRVTAP
+{
+ /** The network interface. */
+ PDMINETWORKUP INetworkUp;
+ /** The network interface. */
+ PPDMINETWORKDOWN pIAboveNet;
+ /** Pointer to the driver instance. */
+ PPDMDRVINS pDrvIns;
+ /** TAP device file handle. */
+ RTFILE hFileDevice;
+ /** The configured TAP device name. */
+ char *pszDeviceName;
+#ifdef RT_OS_SOLARIS
+ /** IP device file handle (/dev/udp). */
+ int iIPFileDes;
+ /** Whether device name is obtained from setup application. */
+ bool fStatic;
+#endif
+ /** TAP setup application. */
+ char *pszSetupApplication;
+ /** TAP terminate application. */
+ char *pszTerminateApplication;
+ /** The write end of the control pipe. */
+ RTPIPE hPipeWrite;
+ /** The read end of the control pipe. */
+ RTPIPE hPipeRead;
+ /** Reader thread. */
+ PPDMTHREAD pThread;
+
+ /** @todo The transmit thread. */
+ /** Transmit lock used by drvTAPNetworkUp_BeginXmit. */
+ RTCRITSECT XmitLock;
+
+#ifdef VBOX_WITH_STATISTICS
+ /** Number of sent packets. */
+ STAMCOUNTER StatPktSent;
+ /** Number of sent bytes. */
+ STAMCOUNTER StatPktSentBytes;
+ /** Number of received packets. */
+ STAMCOUNTER StatPktRecv;
+ /** Number of received bytes. */
+ STAMCOUNTER StatPktRecvBytes;
+ /** Profiling packet transmit runs. */
+ STAMPROFILE StatTransmit;
+ /** Profiling packet receive runs. */
+ STAMPROFILEADV StatReceive;
+#endif /* VBOX_WITH_STATISTICS */
+
+#ifdef LOG_ENABLED
+ /** The nano ts of the last transfer. */
+ uint64_t u64LastTransferTS;
+ /** The nano ts of the last receive. */
+ uint64_t u64LastReceiveTS;
+#endif
+} DRVTAP, *PDRVTAP;
+
+
+/** Converts a pointer to TAP::INetworkUp to a PRDVTAP. */
+#define PDMINETWORKUP_2_DRVTAP(pInterface) ( (PDRVTAP)((uintptr_t)pInterface - RT_UOFFSETOF(DRVTAP, INetworkUp)) )
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+#ifdef RT_OS_SOLARIS
+static int SolarisTAPAttach(PDRVTAP pThis);
+#endif
+
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
+ */
+static DECLCALLBACK(int) drvTAPNetworkUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
+{
+ RT_NOREF(fOnWorkerThread);
+ PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface);
+ int rc = RTCritSectTryEnter(&pThis->XmitLock);
+ if (RT_FAILURE(rc))
+ {
+ /** @todo XMIT thread */
+ rc = VERR_TRY_AGAIN;
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
+ */
+static DECLCALLBACK(int) drvTAPNetworkUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
+ PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
+{
+ RT_NOREF(pInterface);
+#ifdef VBOX_STRICT
+ PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface);
+ Assert(RTCritSectIsOwner(&pThis->XmitLock));
+#endif
+
+ /*
+ * Allocate a scatter / gather buffer descriptor that is immediately
+ * followed by the buffer space of its single segment. The GSO context
+ * comes after that again.
+ */
+ PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAlloc( RT_ALIGN_Z(sizeof(*pSgBuf), 16)
+ + RT_ALIGN_Z(cbMin, 16)
+ + (pGso ? RT_ALIGN_Z(sizeof(*pGso), 16) : 0));
+ if (!pSgBuf)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize the S/G buffer and return.
+ */
+ pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
+ pSgBuf->cbUsed = 0;
+ pSgBuf->cbAvailable = RT_ALIGN_Z(cbMin, 16);
+ pSgBuf->pvAllocator = NULL;
+ if (!pGso)
+ pSgBuf->pvUser = NULL;
+ else
+ {
+ pSgBuf->pvUser = (uint8_t *)(pSgBuf + 1) + pSgBuf->cbAvailable;
+ *(PPDMNETWORKGSO)pSgBuf->pvUser = *pGso;
+ }
+ pSgBuf->cSegs = 1;
+ pSgBuf->aSegs[0].cbSeg = pSgBuf->cbAvailable;
+ pSgBuf->aSegs[0].pvSeg = pSgBuf + 1;
+
+#if 0 /* poison */
+ memset(pSgBuf->aSegs[0].pvSeg, 'F', pSgBuf->aSegs[0].cbSeg);
+#endif
+ *ppSgBuf = pSgBuf;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
+ */
+static DECLCALLBACK(int) drvTAPNetworkUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
+{
+ RT_NOREF(pInterface);
+#ifdef VBOX_STRICT
+ PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface);
+ Assert(RTCritSectIsOwner(&pThis->XmitLock));
+#endif
+
+ if (pSgBuf)
+ {
+ Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
+ pSgBuf->fFlags = 0;
+ RTMemFree(pSgBuf);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
+ */
+static DECLCALLBACK(int) drvTAPNetworkUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
+{
+ RT_NOREF(fOnWorkerThread);
+ PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface);
+ STAM_COUNTER_INC(&pThis->StatPktSent);
+ STAM_COUNTER_ADD(&pThis->StatPktSentBytes, pSgBuf->cbUsed);
+ STAM_PROFILE_START(&pThis->StatTransmit, a);
+
+ AssertPtr(pSgBuf);
+ Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
+ Assert(RTCritSectIsOwner(&pThis->XmitLock));
+
+ int rc;
+ if (!pSgBuf->pvUser)
+ {
+#ifdef LOG_ENABLED
+ uint64_t u64Now = RTTimeProgramNanoTS();
+ LogFlow(("drvTAPSend: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
+ pSgBuf->cbUsed, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS));
+ pThis->u64LastTransferTS = u64Now;
+#endif
+ Log2(("drvTAPSend: pSgBuf->aSegs[0].pvSeg=%p pSgBuf->cbUsed=%#x\n"
+ "%.*Rhxd\n",
+ pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, pSgBuf->cbUsed, pSgBuf->aSegs[0].pvSeg));
+
+ rc = RTFileWrite(pThis->hFileDevice, pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, NULL);
+ }
+ else
+ {
+ uint8_t abHdrScratch[256];
+ uint8_t const *pbFrame = (uint8_t const *)pSgBuf->aSegs[0].pvSeg;
+ PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pSgBuf->pvUser;
+ uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, pSgBuf->cbUsed); Assert(cSegs > 1);
+ rc = VINF_SUCCESS;
+ for (size_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ uint32_t cbSegFrame;
+ void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)pbFrame, pSgBuf->cbUsed, abHdrScratch,
+ iSeg, cSegs, &cbSegFrame);
+ rc = RTFileWrite(pThis->hFileDevice, pvSegFrame, cbSegFrame, NULL);
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+
+ pSgBuf->fFlags = 0;
+ RTMemFree(pSgBuf);
+
+ STAM_PROFILE_STOP(&pThis->StatTransmit, a);
+ AssertRC(rc);
+ if (RT_FAILURE(rc))
+ rc = rc == VERR_NO_MEMORY ? VERR_NET_NO_BUFFER_SPACE : VERR_NET_DOWN;
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
+ */
+static DECLCALLBACK(void) drvTAPNetworkUp_EndXmit(PPDMINETWORKUP pInterface)
+{
+ PDRVTAP pThis = PDMINETWORKUP_2_DRVTAP(pInterface);
+ RTCritSectLeave(&pThis->XmitLock);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
+ */
+static DECLCALLBACK(void) drvTAPNetworkUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
+{
+ RT_NOREF(pInterface, fPromiscuous);
+ LogFlow(("drvTAPNetworkUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
+ /* nothing to do */
+}
+
+
+/**
+ * Notification on link status changes.
+ *
+ * @param pInterface Pointer to the interface structure containing the called function pointer.
+ * @param enmLinkState The new link state.
+ * @thread EMT
+ */
+static DECLCALLBACK(void) drvTAPNetworkUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
+{
+ RT_NOREF(pInterface, enmLinkState);
+ LogFlow(("drvTAPNetworkUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
+ /** @todo take action on link down and up. Stop the polling and such like. */
+}
+
+
+/**
+ * Asynchronous I/O thread for handling receive.
+ *
+ * @returns VINF_SUCCESS (ignored).
+ * @param Thread Thread handle.
+ * @param pvUser Pointer to a DRVTAP structure.
+ */
+static DECLCALLBACK(int) drvTAPAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP);
+ LogFlow(("drvTAPAsyncIoThread: pThis=%p\n", pThis));
+
+ if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
+ return VINF_SUCCESS;
+
+ STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
+
+ /*
+ * Polling loop.
+ */
+ while (pThread->enmState == PDMTHREADSTATE_RUNNING)
+ {
+ /*
+ * Wait for something to become available.
+ */
+ struct pollfd aFDs[2];
+ aFDs[0].fd = RTFileToNative(pThis->hFileDevice);
+ aFDs[0].events = POLLIN | POLLPRI;
+ aFDs[0].revents = 0;
+ aFDs[1].fd = RTPipeToNative(pThis->hPipeRead);
+ aFDs[1].events = POLLIN | POLLPRI | POLLERR | POLLHUP;
+ aFDs[1].revents = 0;
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+ errno=0;
+ int rc = poll(&aFDs[0], RT_ELEMENTS(aFDs), -1 /* infinite */);
+
+ /* this might have changed in the meantime */
+ if (pThread->enmState != PDMTHREADSTATE_RUNNING)
+ break;
+
+ STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
+ if ( rc > 0
+ && (aFDs[0].revents & (POLLIN | POLLPRI))
+ && !aFDs[1].revents)
+ {
+ /*
+ * Read the frame.
+ */
+ char achBuf[16384];
+ size_t cbRead = 0;
+ /** @note At least on Linux we will never receive more than one network packet
+ * after poll() returned successfully. I don't know why but a second
+ * RTFileRead() operation will return with VERR_TRY_AGAIN in any case. */
+ rc = RTFileRead(pThis->hFileDevice, achBuf, sizeof(achBuf), &cbRead);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Wait for the device to have space for this frame.
+ * Most guests use frame-sized receive buffers, hence non-zero cbMax
+ * automatically means there is enough room for entire frame. Some
+ * guests (eg. Solaris) use large chains of small receive buffers
+ * (each 128 or so bytes large). We will still start receiving as soon
+ * as cbMax is non-zero because:
+ * - it would be quite expensive for pfnCanReceive to accurately
+ * determine free receive buffer space
+ * - if we were waiting for enough free buffers, there is a risk
+ * of deadlocking because the guest could be waiting for a receive
+ * overflow error to allocate more receive buffers
+ */
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+ int rc1 = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
+ STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
+
+ /*
+ * A return code != VINF_SUCCESS means that we were woken up during a VM
+ * state transition. Drop the packet and wait for the next one.
+ */
+ if (RT_FAILURE(rc1))
+ continue;
+
+ /*
+ * Pass the data up.
+ */
+#ifdef LOG_ENABLED
+ uint64_t u64Now = RTTimeProgramNanoTS();
+ LogFlow(("drvTAPAsyncIoThread: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
+ cbRead, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS));
+ pThis->u64LastReceiveTS = u64Now;
+#endif
+ Log2(("drvTAPAsyncIoThread: cbRead=%#x\n" "%.*Rhxd\n", cbRead, cbRead, achBuf));
+ STAM_COUNTER_INC(&pThis->StatPktRecv);
+ STAM_COUNTER_ADD(&pThis->StatPktRecvBytes, cbRead);
+ rc1 = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, achBuf, cbRead);
+ AssertRC(rc1);
+ }
+ else
+ {
+ LogFlow(("drvTAPAsyncIoThread: RTFileRead -> %Rrc\n", rc));
+ if (rc == VERR_INVALID_HANDLE)
+ break;
+ RTThreadYield();
+ }
+ }
+ else if ( rc > 0
+ && aFDs[1].revents)
+ {
+ LogFlow(("drvTAPAsyncIoThread: Control message: enmState=%d revents=%#x\n", pThread->enmState, aFDs[1].revents));
+ if (aFDs[1].revents & (POLLHUP | POLLERR | POLLNVAL))
+ break;
+
+ /* drain the pipe */
+ char ch;
+ size_t cbRead;
+ RTPipeRead(pThis->hPipeRead, &ch, 1, &cbRead);
+ }
+ else
+ {
+ /*
+ * poll() failed for some reason. Yield to avoid eating too much CPU.
+ *
+ * EINTR errors have been seen frequently. They should be harmless, even
+ * if they are not supposed to occur in our setup.
+ */
+ if (errno == EINTR)
+ Log(("rc=%d revents=%#x,%#x errno=%p %s\n", rc, aFDs[0].revents, aFDs[1].revents, errno, strerror(errno)));
+ else
+ AssertMsgFailed(("rc=%d revents=%#x,%#x errno=%p %s\n", rc, aFDs[0].revents, aFDs[1].revents, errno, strerror(errno)));
+ RTThreadYield();
+ }
+ }
+
+
+ LogFlow(("drvTAPAsyncIoThread: returns %Rrc\n", VINF_SUCCESS));
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Unblock the send thread so it can respond to a state change.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The pcnet device instance.
+ * @param pThread The send thread.
+ */
+static DECLCALLBACK(int) drvTapAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ RT_NOREF(pThread);
+ PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP);
+
+ size_t cbIgnored;
+ int rc = RTPipeWrite(pThis->hPipeWrite, "", 1, &cbIgnored);
+ AssertRC(rc);
+
+ return VINF_SUCCESS;
+}
+
+
+#if defined(RT_OS_SOLARIS)
+/**
+ * Calls OS-specific TAP setup application/script.
+ *
+ * @returns VBox error code.
+ * @param pThis The instance data.
+ */
+static int drvTAPSetupApplication(PDRVTAP pThis)
+{
+ char szCommand[4096];
+
+ RTStrPrintf(szCommand, sizeof(szCommand), "%s %s", pThis->pszSetupApplication,
+ pThis->fStatic ? pThis->pszDeviceName : "");
+
+ /* Pipe open the setup application. */
+ Log2(("Starting TAP setup application: %s\n", szCommand));
+ FILE* pfSetupHandle = popen(szCommand, "r");
+ if (pfSetupHandle == 0)
+ {
+ LogRel(("TAP#%d: Failed to run TAP setup application: %s\n", pThis->pDrvIns->iInstance,
+ pThis->pszSetupApplication, strerror(errno)));
+ return VERR_HOSTIF_INIT_FAILED;
+ }
+ if (!pThis->fStatic)
+ {
+ /* Obtain device name from setup application. */
+ char acBuffer[64];
+ size_t cBufSize;
+ fgets(acBuffer, sizeof(acBuffer), pfSetupHandle);
+ cBufSize = strlen(acBuffer);
+ /* The script must return the name of the interface followed by a carriage return as the
+ first line of its output. We need a null-terminated string. */
+ if ((cBufSize < 2) || (acBuffer[cBufSize - 1] != '\n'))
+ {
+ pclose(pfSetupHandle);
+ LogRel(("The TAP interface setup script did not return the name of a TAP device.\n"));
+ return VERR_HOSTIF_INIT_FAILED;
+ }
+ /* Overwrite the terminating newline character. */
+ acBuffer[cBufSize - 1] = 0;
+ RTStrAPrintf(&pThis->pszDeviceName, "%s", acBuffer);
+ }
+ int rc = pclose(pfSetupHandle);
+ if (!WIFEXITED(rc))
+ {
+ LogRel(("The TAP interface setup script terminated abnormally.\n"));
+ return VERR_HOSTIF_INIT_FAILED;
+ }
+ if (WEXITSTATUS(rc) != 0)
+ {
+ LogRel(("The TAP interface setup script returned a non-zero exit code.\n"));
+ return VERR_HOSTIF_INIT_FAILED;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Calls OS-specific TAP terminate application/script.
+ *
+ * @returns VBox error code.
+ * @param pThis The instance data.
+ */
+static int drvTAPTerminateApplication(PDRVTAP pThis)
+{
+ char *pszArgs[3];
+ pszArgs[0] = pThis->pszTerminateApplication;
+ pszArgs[1] = pThis->pszDeviceName;
+ pszArgs[2] = NULL;
+
+ Log2(("Starting TAP terminate application: %s %s\n", pThis->pszTerminateApplication, pThis->pszDeviceName));
+ RTPROCESS pid = NIL_RTPROCESS;
+ int rc = RTProcCreate(pszArgs[0], pszArgs, RTENV_DEFAULT, 0, &pid);
+ if (RT_SUCCESS(rc))
+ {
+ RTPROCSTATUS Status;
+ rc = RTProcWait(pid, 0, &Status);
+ if (RT_SUCCESS(rc))
+ {
+ if ( Status.iStatus == 0
+ && Status.enmReason == RTPROCEXITREASON_NORMAL)
+ return VINF_SUCCESS;
+
+ LogRel(("TAP#%d: Error running TAP terminate application: %s\n", pThis->pDrvIns->iInstance, pThis->pszTerminateApplication));
+ }
+ else
+ LogRel(("TAP#%d: RTProcWait failed for: %s\n", pThis->pDrvIns->iInstance, pThis->pszTerminateApplication));
+ }
+ else
+ {
+ /* Bad. RTProcCreate() failed! */
+ LogRel(("TAP#%d: Failed to fork() process for running TAP terminate application: %s\n", pThis->pDrvIns->iInstance,
+ pThis->pszTerminateApplication, strerror(errno)));
+ }
+ return VERR_HOSTIF_TERM_FAILED;
+}
+
+#endif /* RT_OS_SOLARIS */
+
+
+#ifdef RT_OS_SOLARIS
+/** From net/if_tun.h, installed by Universal TUN/TAP driver */
+# define TUNNEWPPA (('T'<<16) | 0x0001)
+/** Whether to enable ARP for TAP. */
+# define VBOX_SOLARIS_TAP_ARP 1
+
+/**
+ * Creates/Attaches TAP device to IP.
+ *
+ * @returns VBox error code.
+ * @param pThis The instance data.
+ */
+static DECLCALLBACK(int) SolarisTAPAttach(PDRVTAP pThis)
+{
+ LogFlow(("SolarisTapAttach: pThis=%p\n", pThis));
+
+
+ int IPFileDes = open("/dev/udp", O_RDWR, 0);
+ if (IPFileDes < 0)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to open /dev/udp. errno=%d"), errno);
+
+ int TapFileDes = open("/dev/tap", O_RDWR, 0);
+ if (TapFileDes < 0)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to open /dev/tap for TAP. errno=%d"), errno);
+
+ /* Use the PPA from the ifname if possible (e.g "tap2", then use 2 as PPA) */
+ int iPPA = -1;
+ if (pThis->pszDeviceName)
+ {
+ size_t cch = strlen(pThis->pszDeviceName);
+ if (cch > 1 && RT_C_IS_DIGIT(pThis->pszDeviceName[cch - 1]) != 0)
+ iPPA = pThis->pszDeviceName[cch - 1] - '0';
+ }
+
+ struct strioctl ioIF;
+ ioIF.ic_cmd = TUNNEWPPA;
+ ioIF.ic_len = sizeof(iPPA);
+ ioIF.ic_dp = (char *)(&iPPA);
+ ioIF.ic_timout = 0;
+ iPPA = ioctl(TapFileDes, I_STR, &ioIF);
+ if (iPPA < 0)
+ {
+ close(TapFileDes);
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_HOSTIF_IOCTL, RT_SRC_POS,
+ N_("Failed to get new interface. errno=%d"), errno);
+ }
+
+ int InterfaceFD = open("/dev/tap", O_RDWR, 0);
+ if (!InterfaceFD)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to open interface /dev/tap. errno=%d"), errno);
+
+ if (ioctl(InterfaceFD, I_PUSH, "ip") == -1)
+ {
+ close(InterfaceFD);
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_HOSTIF_IOCTL, RT_SRC_POS,
+ N_("Failed to push IP. errno=%d"), errno);
+ }
+
+ struct lifreq ifReq;
+ memset(&ifReq, 0, sizeof(ifReq));
+ if (ioctl(InterfaceFD, SIOCGLIFFLAGS, &ifReq) == -1)
+ LogRel(("TAP#%d: Failed to get interface flags.\n", pThis->pDrvIns->iInstance));
+
+ ifReq.lifr_ppa = iPPA;
+ RTStrCopy(ifReq.lifr_name, sizeof(ifReq.lifr_name), pThis->pszDeviceName);
+
+ if (ioctl(InterfaceFD, SIOCSLIFNAME, &ifReq) == -1)
+ LogRel(("TAP#%d: Failed to set PPA. errno=%d\n", pThis->pDrvIns->iInstance, errno));
+
+ if (ioctl(InterfaceFD, SIOCGLIFFLAGS, &ifReq) == -1)
+ LogRel(("TAP#%d: Failed to get interface flags after setting PPA. errno=%d\n", pThis->pDrvIns->iInstance, errno));
+
+# ifdef VBOX_SOLARIS_TAP_ARP
+ /* Interface */
+ if (ioctl(InterfaceFD, I_PUSH, "arp") == -1)
+ LogRel(("TAP#%d: Failed to push ARP to Interface FD. errno=%d\n", pThis->pDrvIns->iInstance, errno));
+
+ /* IP */
+ if (ioctl(IPFileDes, I_POP, NULL) == -1)
+ LogRel(("TAP#%d: Failed I_POP from IP FD. errno=%d\n", pThis->pDrvIns->iInstance, errno));
+
+ if (ioctl(IPFileDes, I_PUSH, "arp") == -1)
+ LogRel(("TAP#%d: Failed to push ARP to IP FD. errno=%d\n", pThis->pDrvIns->iInstance, errno));
+
+ /* ARP */
+ int ARPFileDes = open("/dev/tap", O_RDWR, 0);
+ if (ARPFileDes < 0)
+ LogRel(("TAP#%d: Failed to open for /dev/tap for ARP. errno=%d", pThis->pDrvIns->iInstance, errno));
+
+ if (ioctl(ARPFileDes, I_PUSH, "arp") == -1)
+ LogRel(("TAP#%d: Failed to push ARP to ARP FD. errno=%d\n", pThis->pDrvIns->iInstance, errno));
+
+ ioIF.ic_cmd = SIOCSLIFNAME;
+ ioIF.ic_timout = 0;
+ ioIF.ic_len = sizeof(ifReq);
+ ioIF.ic_dp = (char *)&ifReq;
+ if (ioctl(ARPFileDes, I_STR, &ioIF) == -1)
+ LogRel(("TAP#%d: Failed to set interface name to ARP.\n", pThis->pDrvIns->iInstance));
+# endif
+
+ /* We must use I_LINK and not I_PLINK as I_PLINK makes the link persistent.
+ * Then we would not be able unlink the interface if we reuse it.
+ * Even 'unplumb' won't work after that.
+ */
+ int IPMuxID = ioctl(IPFileDes, I_LINK, InterfaceFD);
+ if (IPMuxID == -1)
+ {
+ close(InterfaceFD);
+# ifdef VBOX_SOLARIS_TAP_ARP
+ close(ARPFileDes);
+# endif
+ LogRel(("TAP#%d: Cannot link TAP device to IP.\n", pThis->pDrvIns->iInstance));
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_HOSTIF_IOCTL, RT_SRC_POS,
+ N_("Failed to link TAP device to IP. Check TAP interface name. errno=%d"), errno);
+ }
+
+# ifdef VBOX_SOLARIS_TAP_ARP
+ int ARPMuxID = ioctl(IPFileDes, I_LINK, ARPFileDes);
+ if (ARPMuxID == -1)
+ LogRel(("TAP#%d: Failed to link TAP device to ARP\n", pThis->pDrvIns->iInstance));
+
+ close(ARPFileDes);
+# endif
+ close(InterfaceFD);
+
+ /* Reuse ifReq */
+ memset(&ifReq, 0, sizeof(ifReq));
+ RTStrCopy(ifReq.lifr_name, sizeof(ifReq.lifr_name), pThis->pszDeviceName);
+ ifReq.lifr_ip_muxid = IPMuxID;
+# ifdef VBOX_SOLARIS_TAP_ARP
+ ifReq.lifr_arp_muxid = ARPMuxID;
+# endif
+
+ if (ioctl(IPFileDes, SIOCSLIFMUXID, &ifReq) == -1)
+ {
+# ifdef VBOX_SOLARIS_TAP_ARP
+ ioctl(IPFileDes, I_PUNLINK, ARPMuxID);
+# endif
+ ioctl(IPFileDes, I_PUNLINK, IPMuxID);
+ close(IPFileDes);
+ LogRel(("TAP#%d: Failed to set Mux ID.\n", pThis->pDrvIns->iInstance));
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_HOSTIF_IOCTL, RT_SRC_POS,
+ N_("Failed to set Mux ID. Check TAP interface name. errno=%d"), errno);
+ }
+
+ int rc = RTFileFromNative(&pThis->hFileDevice, TapFileDes);
+ AssertLogRelRC(rc);
+ if (RT_FAILURE(rc))
+ {
+ close(IPFileDes);
+ close(TapFileDes);
+ }
+ pThis->iIPFileDes = IPFileDes;
+
+ return VINF_SUCCESS;
+}
+
+#endif /* RT_OS_SOLARIS */
+
+/* -=-=-=-=- PDMIBASE -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvTAPQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+ PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP);
+
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
+ return NULL;
+}
+
+/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
+
+/**
+ * Destruct a driver instance.
+ *
+ * Most VM resources are freed by the VM. This callback is provided so that any non-VM
+ * resources can be freed correctly.
+ *
+ * @param pDrvIns The driver instance data.
+ */
+static DECLCALLBACK(void) drvTAPDestruct(PPDMDRVINS pDrvIns)
+{
+ LogFlow(("drvTAPDestruct\n"));
+ PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP);
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+
+ /*
+ * Terminate the control pipe.
+ */
+ int rc;
+ if (pThis->hPipeWrite != NIL_RTPIPE)
+ {
+ rc = RTPipeClose(pThis->hPipeWrite); AssertRC(rc);
+ pThis->hPipeWrite = NIL_RTPIPE;
+ }
+ if (pThis->hPipeRead != NIL_RTPIPE)
+ {
+ rc = RTPipeClose(pThis->hPipeRead); AssertRC(rc);
+ pThis->hPipeRead = NIL_RTPIPE;
+ }
+
+#ifdef RT_OS_SOLARIS
+ /** @todo r=bird: This *does* need checking against ConsoleImpl2.cpp if used on non-solaris systems. */
+ if (pThis->hFileDevice != NIL_RTFILE)
+ {
+ int rc = RTFileClose(pThis->hFileDevice); AssertRC(rc);
+ pThis->hFileDevice = NIL_RTFILE;
+ }
+
+ /*
+ * Call TerminateApplication after closing the device otherwise
+ * TerminateApplication would not be able to unplumb it.
+ */
+ if (pThis->pszTerminateApplication)
+ drvTAPTerminateApplication(pThis);
+
+#endif /* RT_OS_SOLARIS */
+
+#ifdef RT_OS_SOLARIS
+ if (!pThis->fStatic)
+ RTStrFree(pThis->pszDeviceName); /* allocated by drvTAPSetupApplication */
+ else
+ PDMDrvHlpMMHeapFree(pDrvIns, pThis->pszDeviceName);
+#else
+ PDMDrvHlpMMHeapFree(pDrvIns, pThis->pszDeviceName);
+#endif
+ pThis->pszDeviceName = NULL;
+ PDMDrvHlpMMHeapFree(pDrvIns, pThis->pszSetupApplication);
+ pThis->pszSetupApplication = NULL;
+ PDMDrvHlpMMHeapFree(pDrvIns, pThis->pszTerminateApplication);
+ pThis->pszTerminateApplication = NULL;
+
+ /*
+ * Kill the xmit lock.
+ */
+ if (RTCritSectIsInitialized(&pThis->XmitLock))
+ RTCritSectDelete(&pThis->XmitLock);
+
+#ifdef VBOX_WITH_STATISTICS
+ /*
+ * Deregister statistics.
+ */
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktSent);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktSentBytes);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktRecv);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktRecvBytes);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatTransmit);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReceive);
+#endif /* VBOX_WITH_STATISTICS */
+}
+
+
+/**
+ * Construct a TAP network transport driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(int) drvTAPConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ RT_NOREF(fFlags);
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVTAP pThis = PDMINS_2_DATA(pDrvIns, PDRVTAP);
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ /*
+ * Init the static parts.
+ */
+ pThis->pDrvIns = pDrvIns;
+ pThis->hFileDevice = NIL_RTFILE;
+ pThis->hPipeWrite = NIL_RTPIPE;
+ pThis->hPipeRead = NIL_RTPIPE;
+ pThis->pszDeviceName = NULL;
+#ifdef RT_OS_SOLARIS
+ pThis->iIPFileDes = -1;
+ pThis->fStatic = true;
+#endif
+ pThis->pszSetupApplication = NULL;
+ pThis->pszTerminateApplication = NULL;
+
+ /* IBase */
+ pDrvIns->IBase.pfnQueryInterface = drvTAPQueryInterface;
+ /* INetwork */
+ pThis->INetworkUp.pfnBeginXmit = drvTAPNetworkUp_BeginXmit;
+ pThis->INetworkUp.pfnAllocBuf = drvTAPNetworkUp_AllocBuf;
+ pThis->INetworkUp.pfnFreeBuf = drvTAPNetworkUp_FreeBuf;
+ pThis->INetworkUp.pfnSendBuf = drvTAPNetworkUp_SendBuf;
+ pThis->INetworkUp.pfnEndXmit = drvTAPNetworkUp_EndXmit;
+ pThis->INetworkUp.pfnSetPromiscuousMode = drvTAPNetworkUp_SetPromiscuousMode;
+ pThis->INetworkUp.pfnNotifyLinkChanged = drvTAPNetworkUp_NotifyLinkChanged;
+
+#ifdef VBOX_WITH_STATISTICS
+ /*
+ * Statistics.
+ */
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktSent, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of sent packets.", "/Drivers/TAP%d/Packets/Sent", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktSentBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of sent bytes.", "/Drivers/TAP%d/Bytes/Sent", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktRecv, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of received packets.", "/Drivers/TAP%d/Packets/Received", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktRecvBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of received bytes.", "/Drivers/TAP%d/Bytes/Received", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet transmit runs.", "/Drivers/TAP%d/Transmit", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet receive runs.", "/Drivers/TAP%d/Receive", pDrvIns->iInstance);
+#endif /* VBOX_WITH_STATISTICS */
+
+ /*
+ * Validate the config.
+ */
+ PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "Device"
+ "|FileHandle"
+ "|TAPSetupApplication"
+ "|TAPTerminateApplication"
+ "|MAC",
+ "");
+
+ /*
+ * Check that no-one is attached to us.
+ */
+ AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
+ ("Configuration error: Not possible to attach anything to this driver!\n"),
+ VERR_PDM_DRVINS_NO_ATTACH);
+
+ /*
+ * Query the network port interface.
+ */
+ pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
+ if (!pThis->pIAboveNet)
+ return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
+ N_("Configuration error: The above device/driver didn't export the network port interface"));
+
+ /*
+ * Read the configuration.
+ */
+ int rc;
+#if defined(RT_OS_SOLARIS) /** @todo Other platforms' TAP code should be moved here from ConsoleImpl. */
+ rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "TAPSetupApplication", &pThis->pszSetupApplication);
+ if (RT_SUCCESS(rc))
+ {
+ if (!RTPathExists(pThis->pszSetupApplication))
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
+ N_("Invalid TAP setup program path: %s"), pThis->pszSetupApplication);
+ }
+ else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
+ return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Configuration error: failed to query \"TAPTerminateApplication\""));
+
+ rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "TAPTerminateApplication", &pThis->pszTerminateApplication);
+ if (RT_SUCCESS(rc))
+ {
+ if (!RTPathExists(pThis->pszTerminateApplication))
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
+ N_("Invalid TAP terminate program path: %s"), pThis->pszTerminateApplication);
+ }
+ else if (rc != VERR_CFGM_VALUE_NOT_FOUND)
+ return PDMDRV_SET_ERROR(pDrvIns, rc, N_("Configuration error: failed to query \"TAPTerminateApplication\""));
+
+ rc = pHlp->pfnCFGMQueryStringAlloc(pCfg, "Device", &pThis->pszDeviceName);
+ if (RT_FAILURE(rc))
+ pThis->fStatic = false;
+
+ /* Obtain the device name from the setup application (if none was specified). */
+ if (pThis->pszSetupApplication)
+ {
+ rc = drvTAPSetupApplication(pThis);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_INIT_FAILED, RT_SRC_POS,
+ N_("Error running TAP setup application. rc=%d"), rc);
+ }
+
+ /*
+ * Do the setup.
+ */
+ rc = SolarisTAPAttach(pThis);
+ if (RT_FAILURE(rc))
+ return rc;
+
+#else /* !RT_OS_SOLARIS */
+
+ uint64_t u64File;
+ rc = pHlp->pfnCFGMQueryU64(pCfg, "FileHandle", &u64File);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Query for \"FileHandle\" 32-bit signed integer failed"));
+ pThis->hFileDevice = (RTFILE)(uintptr_t)u64File;
+ if (!RTFileIsValid(pThis->hFileDevice))
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_HANDLE, RT_SRC_POS,
+ N_("The TAP file handle %RTfile is not valid"), pThis->hFileDevice);
+#endif /* !RT_OS_SOLARIS */
+
+ /*
+ * Create the transmit lock.
+ */
+ rc = RTCritSectInit(&pThis->XmitLock);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Make sure the descriptor is non-blocking and valid.
+ *
+ * We should actually query if it's a TAP device, but I haven't
+ * found any way to do that.
+ */
+ if (fcntl(RTFileToNative(pThis->hFileDevice), F_SETFL, O_NONBLOCK) == -1)
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_HOSTIF_IOCTL, RT_SRC_POS,
+ N_("Configuration error: Failed to configure /dev/net/tun. errno=%d"), errno);
+ /** @todo determine device name. This can be done by reading the link /proc/<pid>/fd/<fd> */
+ Log(("drvTAPContruct: %d (from fd)\n", (intptr_t)pThis->hFileDevice));
+ rc = VINF_SUCCESS;
+
+ /*
+ * Create the control pipe.
+ */
+ rc = RTPipeCreate(&pThis->hPipeRead, &pThis->hPipeWrite, 0 /*fFlags*/);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Create the async I/O thread.
+ */
+ rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pThread, pThis, drvTAPAsyncIoThread, drvTapAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "TAP");
+ AssertRCReturn(rc, rc);
+
+ return rc;
+}
+
+
+/**
+ * TAP network transport driver registration record.
+ */
+const PDMDRVREG g_DrvHostInterface =
+{
+ /* u32Version */
+ PDM_DRVREG_VERSION,
+ /* szName */
+ "HostInterface",
+ /* szRCMod */
+ "",
+ /* szR0Mod */
+ "",
+ /* pszDescription */
+ "TAP Network Transport Driver",
+ /* fFlags */
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+ /* fClass. */
+ PDM_DRVREG_CLASS_NETWORK,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(DRVTAP),
+ /* pfnConstruct */
+ drvTAPConstruct,
+ /* pfnDestruct */
+ drvTAPDestruct,
+ /* pfnRelocate */
+ NULL,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ NULL,
+ /* pfnSuspend */
+ NULL, /** @todo Do power on, suspend and resume handlers! */
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32EndVersion */
+ PDM_DRVREG_VERSION
+};
+
diff --git a/src/VBox/Devices/Network/DrvUDPTunnel.cpp b/src/VBox/Devices/Network/DrvUDPTunnel.cpp
new file mode 100644
index 00000000..490c1b7e
--- /dev/null
+++ b/src/VBox/Devices/Network/DrvUDPTunnel.cpp
@@ -0,0 +1,673 @@
+/* $Id: DrvUDPTunnel.cpp $ */
+/** @file
+ * DrvUDPTunnel - UDP tunnel network transport driver
+ *
+ * Based on code contributed by Christophe Devriese
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_UDPTUNNEL
+#include <VBox/log.h>
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmnetifs.h>
+#include <VBox/vmm/pdmnetinline.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/udp.h>
+#include <iprt/mem.h>
+#include <iprt/path.h>
+#include <iprt/uuid.h>
+#include <iprt/string.h>
+#include <iprt/critsect.h>
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * UDP tunnel driver instance data.
+ *
+ * @implements PDMINETWORKUP
+ */
+typedef struct DRVUDPTUNNEL
+{
+ /** The network interface. */
+ PDMINETWORKUP INetworkUp;
+ /** The network interface. */
+ PPDMINETWORKDOWN pIAboveNet;
+ /** Pointer to the driver instance. */
+ PPDMDRVINS pDrvIns;
+ /** UDP tunnel source port. */
+ uint16_t uSrcPort;
+ /** UDP tunnel destination port. */
+ uint16_t uDestPort;
+ /** UDP tunnel destination IP address. */
+ char *pszDestIP;
+ /** UDP tunnel instance string. */
+ char *pszInstance;
+
+ /** UDP destination address. */
+ RTNETADDR DestAddress;
+ /** Transmit lock used by drvUDPTunnelUp_BeginXmit. */
+ RTCRITSECT XmitLock;
+ /** Server data structure for UDP communication. */
+ PRTUDPSERVER pServer;
+
+ /** Flag whether the link is down. */
+ bool volatile fLinkDown;
+
+#ifdef VBOX_WITH_STATISTICS
+ /** Number of sent packets. */
+ STAMCOUNTER StatPktSent;
+ /** Number of sent bytes. */
+ STAMCOUNTER StatPktSentBytes;
+ /** Number of received packets. */
+ STAMCOUNTER StatPktRecv;
+ /** Number of received bytes. */
+ STAMCOUNTER StatPktRecvBytes;
+ /** Profiling packet transmit runs. */
+ STAMPROFILE StatTransmit;
+ /** Profiling packet receive runs. */
+ STAMPROFILEADV StatReceive;
+#endif /* VBOX_WITH_STATISTICS */
+
+#ifdef LOG_ENABLED
+ /** The nano ts of the last transfer. */
+ uint64_t u64LastTransferTS;
+ /** The nano ts of the last receive. */
+ uint64_t u64LastReceiveTS;
+#endif
+} DRVUDPTUNNEL, *PDRVUDPTUNNEL;
+
+
+/** Converts a pointer to UDPTUNNEL::INetworkUp to a PRDVUDPTUNNEL. */
+#define PDMINETWORKUP_2_DRVUDPTUNNEL(pInterface) ( (PDRVUDPTUNNEL)((uintptr_t)pInterface - RT_UOFFSETOF(DRVUDPTUNNEL, INetworkUp)) )
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
+ */
+static DECLCALLBACK(int) drvUDPTunnelUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
+{
+ RT_NOREF(fOnWorkerThread);
+ PDRVUDPTUNNEL pThis = PDMINETWORKUP_2_DRVUDPTUNNEL(pInterface);
+ int rc = RTCritSectTryEnter(&pThis->XmitLock);
+ if (RT_FAILURE(rc))
+ {
+ /** @todo XMIT thread */
+ rc = VERR_TRY_AGAIN;
+ }
+ return rc;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
+ */
+static DECLCALLBACK(int) drvUDPTunnelUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
+ PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
+{
+ PDRVUDPTUNNEL pThis = PDMINETWORKUP_2_DRVUDPTUNNEL(pInterface);
+ Assert(RTCritSectIsOwner(&pThis->XmitLock)); NOREF(pThis);
+
+ /*
+ * Allocate a scatter / gather buffer descriptor that is immediately
+ * followed by the buffer space of its single segment. The GSO context
+ * comes after that again.
+ */
+ PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAlloc( RT_ALIGN_Z(sizeof(*pSgBuf), 16)
+ + RT_ALIGN_Z(cbMin, 16)
+ + (pGso ? RT_ALIGN_Z(sizeof(*pGso), 16) : 0));
+ if (!pSgBuf)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize the S/G buffer and return.
+ */
+ pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
+ pSgBuf->cbUsed = 0;
+ pSgBuf->cbAvailable = RT_ALIGN_Z(cbMin, 16);
+ pSgBuf->pvAllocator = NULL;
+ if (!pGso)
+ pSgBuf->pvUser = NULL;
+ else
+ {
+ pSgBuf->pvUser = (uint8_t *)(pSgBuf + 1) + pSgBuf->cbAvailable;
+ *(PPDMNETWORKGSO)pSgBuf->pvUser = *pGso;
+ }
+ pSgBuf->cSegs = 1;
+ pSgBuf->aSegs[0].cbSeg = pSgBuf->cbAvailable;
+ pSgBuf->aSegs[0].pvSeg = pSgBuf + 1;
+
+#if 0 /* poison */
+ memset(pSgBuf->aSegs[0].pvSeg, 'F', pSgBuf->aSegs[0].cbSeg);
+#endif
+ *ppSgBuf = pSgBuf;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
+ */
+static DECLCALLBACK(int) drvUDPTunnelUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
+{
+ PDRVUDPTUNNEL pThis = PDMINETWORKUP_2_DRVUDPTUNNEL(pInterface);
+ Assert(RTCritSectIsOwner(&pThis->XmitLock)); NOREF(pThis);
+ if (pSgBuf)
+ {
+ Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
+ pSgBuf->fFlags = 0;
+ RTMemFree(pSgBuf);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
+ */
+static DECLCALLBACK(int) drvUDPTunnelUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
+{
+ RT_NOREF(fOnWorkerThread);
+ PDRVUDPTUNNEL pThis = PDMINETWORKUP_2_DRVUDPTUNNEL(pInterface);
+ STAM_COUNTER_INC(&pThis->StatPktSent);
+ STAM_COUNTER_ADD(&pThis->StatPktSentBytes, pSgBuf->cbUsed);
+ STAM_PROFILE_START(&pThis->StatTransmit, a);
+
+ AssertPtr(pSgBuf);
+ Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
+ Assert(RTCritSectIsOwner(&pThis->XmitLock));
+
+ int rc;
+ if (!pSgBuf->pvUser)
+ {
+#ifdef LOG_ENABLED
+ uint64_t u64Now = RTTimeProgramNanoTS();
+ LogFunc(("%-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
+ pSgBuf->cbUsed, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS));
+ pThis->u64LastTransferTS = u64Now;
+#endif
+ Log2(("pSgBuf->aSegs[0].pvSeg=%p pSgBuf->cbUsed=%#x\n%.*Rhxd\n",
+ pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, pSgBuf->cbUsed, pSgBuf->aSegs[0].pvSeg));
+
+ rc = RTUdpWrite(pThis->pServer, pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, &pThis->DestAddress);
+ }
+ else
+ {
+ uint8_t abHdrScratch[256];
+ uint8_t const *pbFrame = (uint8_t const *)pSgBuf->aSegs[0].pvSeg;
+ PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pSgBuf->pvUser;
+ uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, pSgBuf->cbUsed); Assert(cSegs > 1);
+ rc = VINF_SUCCESS;
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ uint32_t cbSegFrame;
+ void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)pbFrame, pSgBuf->cbUsed, abHdrScratch,
+ iSeg, cSegs, &cbSegFrame);
+ rc = RTUdpWrite(pThis->pServer, pvSegFrame, cbSegFrame, &pThis->DestAddress);
+ }
+ }
+
+ pSgBuf->fFlags = 0;
+ RTMemFree(pSgBuf);
+
+ STAM_PROFILE_STOP(&pThis->StatTransmit, a);
+ AssertRC(rc);
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_NO_MEMORY)
+ rc = VERR_NET_NO_BUFFER_SPACE;
+ else
+ rc = VERR_NET_DOWN;
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
+ */
+static DECLCALLBACK(void) drvUDPTunnelUp_EndXmit(PPDMINETWORKUP pInterface)
+{
+ PDRVUDPTUNNEL pThis = PDMINETWORKUP_2_DRVUDPTUNNEL(pInterface);
+ RTCritSectLeave(&pThis->XmitLock);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
+ */
+static DECLCALLBACK(void) drvUDPTunnelUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
+{
+ RT_NOREF(pInterface, fPromiscuous);
+ LogFlowFunc(("fPromiscuous=%d\n", fPromiscuous));
+ /* nothing to do */
+}
+
+
+/**
+ * Notification on link status changes.
+ *
+ * @param pInterface Pointer to the interface structure containing the called function pointer.
+ * @param enmLinkState The new link state.
+ * @thread EMT
+ */
+static DECLCALLBACK(void) drvUDPTunnelUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
+{
+ LogFlowFunc(("enmLinkState=%d\n", enmLinkState));
+ PDRVUDPTUNNEL pThis = PDMINETWORKUP_2_DRVUDPTUNNEL(pInterface);
+
+ bool fLinkDown;
+ switch (enmLinkState)
+ {
+ case PDMNETWORKLINKSTATE_DOWN:
+ case PDMNETWORKLINKSTATE_DOWN_RESUME:
+ fLinkDown = true;
+ break;
+ default:
+ AssertMsgFailed(("enmLinkState=%d\n", enmLinkState));
+ RT_FALL_THRU();
+ case PDMNETWORKLINKSTATE_UP:
+ fLinkDown = false;
+ break;
+ }
+ ASMAtomicXchgSize(&pThis->fLinkDown, fLinkDown);
+}
+
+
+static DECLCALLBACK(int) drvUDPTunnelReceive(RTSOCKET Sock, void *pvUser)
+{
+ PDRVUDPTUNNEL pThis = PDMINS_2_DATA((PPDMDRVINS)pvUser, PDRVUDPTUNNEL);
+ LogFlowFunc(("pThis=%p\n", pThis));
+
+ STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
+
+ /*
+ * Read the frame.
+ */
+ char achBuf[16384];
+ size_t cbRead = 0;
+ int rc = RTUdpRead(Sock, achBuf, sizeof(achBuf), &cbRead, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ if (!pThis->fLinkDown)
+ {
+ /*
+ * Wait for the device to have space for this frame.
+ * Most guests use frame-sized receive buffers, hence non-zero cbMax
+ * automatically means there is enough room for entire frame. Some
+ * guests (eg. Solaris) use large chains of small receive buffers
+ * (each 128 or so bytes large). We will still start receiving as soon
+ * as cbMax is non-zero because:
+ * - it would be quite expensive for pfnCanReceive to accurately
+ * determine free receive buffer space
+ * - if we were waiting for enough free buffers, there is a risk
+ * of deadlocking because the guest could be waiting for a receive
+ * overflow error to allocate more receive buffers
+ */
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+ rc = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
+ STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
+
+ /*
+ * A return code != VINF_SUCCESS means that we were woken up during a VM
+ * state transition. Drop the packet and wait for the next one.
+ */
+ if (RT_FAILURE(rc))
+ {
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+ return VINF_SUCCESS;
+ }
+
+ /*
+ * Pass the data up.
+ */
+#ifdef LOG_ENABLED
+ uint64_t u64Now = RTTimeProgramNanoTS();
+ LogFunc(("%-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
+ cbRead, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS));
+ pThis->u64LastReceiveTS = u64Now;
+#endif
+ Log2(("cbRead=%#x\n" "%.*Rhxd\n", cbRead, cbRead, achBuf));
+ STAM_COUNTER_INC(&pThis->StatPktRecv);
+ STAM_COUNTER_ADD(&pThis->StatPktRecvBytes, cbRead);
+ rc = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, achBuf, cbRead);
+ AssertRC(rc);
+ }
+ }
+ else
+ {
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+ LogFunc(("RTUdpRead -> %Rrc\n", rc));
+ if (rc == VERR_INVALID_HANDLE)
+ return VERR_UDP_SERVER_STOP;
+ }
+
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+ return VINF_SUCCESS;
+}
+
+
+/* -=-=-=-=- PDMIBASE -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvUDPTunnelQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+ PDRVUDPTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVUDPTUNNEL);
+
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
+ return NULL;
+}
+
+
+/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
+
+/**
+ * Destruct a driver instance.
+ *
+ * Most VM resources are freed by the VM. This callback is provided so that any non-VM
+ * resources can be freed correctly.
+ *
+ * @param pDrvIns The driver instance data.
+ */
+static DECLCALLBACK(void) drvUDPTunnelDestruct(PPDMDRVINS pDrvIns)
+{
+ LogFlowFunc(("\n"));
+ PDRVUDPTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVUDPTUNNEL);
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+
+ ASMAtomicXchgSize(&pThis->fLinkDown, true);
+
+ if (pThis->pszInstance)
+ {
+ RTStrFree(pThis->pszInstance);
+ pThis->pszInstance = NULL;
+ }
+
+ if (pThis->pszDestIP)
+ {
+ PDMDrvHlpMMHeapFree(pDrvIns, pThis->pszDestIP);
+ pThis->pszDestIP = NULL;
+ }
+
+ if (pThis->pServer)
+ {
+ RTUdpServerDestroy(pThis->pServer);
+ pThis->pServer = NULL;
+ }
+
+ /*
+ * Kill the xmit lock.
+ */
+ if (RTCritSectIsInitialized(&pThis->XmitLock))
+ RTCritSectDelete(&pThis->XmitLock);
+
+#ifdef VBOX_WITH_STATISTICS
+ /*
+ * Deregister statistics.
+ */
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktSent);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktSentBytes);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktRecv);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktRecvBytes);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatTransmit);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReceive);
+#endif /* VBOX_WITH_STATISTICS */
+}
+
+
+/**
+ * Construct a UDP tunnel network transport driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(int) drvUDPTunnelConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ RT_NOREF(fFlags);
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVUDPTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVUDPTUNNEL);
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ /*
+ * Init the static parts.
+ */
+ pThis->pDrvIns = pDrvIns;
+ pThis->pszDestIP = NULL;
+ pThis->pszInstance = NULL;
+
+ /* IBase */
+ pDrvIns->IBase.pfnQueryInterface = drvUDPTunnelQueryInterface;
+ /* INetwork */
+ pThis->INetworkUp.pfnBeginXmit = drvUDPTunnelUp_BeginXmit;
+ pThis->INetworkUp.pfnAllocBuf = drvUDPTunnelUp_AllocBuf;
+ pThis->INetworkUp.pfnFreeBuf = drvUDPTunnelUp_FreeBuf;
+ pThis->INetworkUp.pfnSendBuf = drvUDPTunnelUp_SendBuf;
+ pThis->INetworkUp.pfnEndXmit = drvUDPTunnelUp_EndXmit;
+ pThis->INetworkUp.pfnSetPromiscuousMode = drvUDPTunnelUp_SetPromiscuousMode;
+ pThis->INetworkUp.pfnNotifyLinkChanged = drvUDPTunnelUp_NotifyLinkChanged;
+
+#ifdef VBOX_WITH_STATISTICS
+ /*
+ * Statistics.
+ */
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktSent, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of sent packets.", "/Drivers/UDPTunnel%d/Packets/Sent", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktSentBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of sent bytes.", "/Drivers/UDPTunnel%d/Bytes/Sent", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktRecv, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of received packets.", "/Drivers/UDPTunnel%d/Packets/Received", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktRecvBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of received bytes.", "/Drivers/UDPTunnel%d/Bytes/Received", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet transmit runs.", "/Drivers/UDPTunnel%d/Transmit", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet receive runs.", "/Drivers/UDPTunnel%d/Receive", pDrvIns->iInstance);
+#endif /* VBOX_WITH_STATISTICS */
+
+ /*
+ * Validate the config.
+ */
+ PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "sport"
+ "|dest"
+ "|dport",
+ "");
+
+ /*
+ * Check that no-one is attached to us.
+ */
+ AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
+ ("Configuration error: Not possible to attach anything to this driver!\n"),
+ VERR_PDM_DRVINS_NO_ATTACH);
+
+ /*
+ * Query the network port interface.
+ */
+ pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
+ if (!pThis->pIAboveNet)
+ return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
+ N_("Configuration error: The above device/driver didn't export the network port interface"));
+
+ /*
+ * Read the configuration.
+ */
+ int rc;
+ char szVal[16];
+ rc = pHlp->pfnCFGMQueryStringDef(pCfg, "sport", szVal, sizeof(szVal), "4444");
+ if (RT_FAILURE(rc))
+ rc = PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvUDPTunnel: Configuration error: Querying \"sport\" as string failed"));
+ rc = RTStrToUInt16Full(szVal, 0, &pThis->uSrcPort);
+ if (RT_FAILURE(rc))
+ rc = PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvUDPTunnel: Configuration error: Converting \"sport\" to integer failed"));
+ if (!pThis->uSrcPort)
+ pThis->uSrcPort = 4444;
+
+ rc = pHlp->pfnCFGMQueryStringDef(pCfg, "dport", szVal, sizeof(szVal), "4445");
+ if (RT_FAILURE(rc))
+ rc = PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvUDPTunnel: Configuration error: Querying \"dport\" as string failed"));
+ rc = RTStrToUInt16Full(szVal, 0, &pThis->uDestPort);
+ if (RT_FAILURE(rc))
+ rc = PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvUDPTunnel: Configuration error: Converting \"dport\" to integer failed"));
+ if (!pThis->uDestPort)
+ pThis->uDestPort = 4445;
+
+ rc = pHlp->pfnCFGMQueryStringAllocDef(pCfg, "dest", &pThis->pszDestIP, "127.0.0.1");
+ if (RT_FAILURE(rc))
+ rc = PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("DrvUDPTunnel: Configuration error: Querying \"dest\" as string failed"));
+
+ LogRel(("UDPTunnel#%d: sport=%d;dest=%s;dport=%d\n", pDrvIns->iInstance, pThis->uSrcPort, pThis->pszDestIP, pThis->uDestPort));
+
+ /*
+ * Set up destination address for UDP.
+ */
+ rc = RTSocketParseInetAddress(pThis->pszDestIP, pThis->uDestPort, &pThis->DestAddress);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Create unique thread name for the UDP receiver.
+ */
+ rc = RTStrAPrintf(&pThis->pszInstance, "UDPTunnel%d", pDrvIns->iInstance);
+ AssertRC(rc);
+
+ /*
+ * Start the UDP receiving thread.
+ */
+ rc = RTUdpServerCreate("", pThis->uSrcPort, RTTHREADTYPE_IO, pThis->pszInstance,
+ drvUDPTunnelReceive, pDrvIns, &pThis->pServer);
+ if (RT_FAILURE(rc))
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("UDPTunnel: Failed to start the UDP tunnel server"));
+
+ /*
+ * Create the transmit lock.
+ */
+ rc = RTCritSectInit(&pThis->XmitLock);
+ AssertRCReturn(rc, rc);
+
+ return rc;
+}
+
+
+/**
+ * Suspend notification.
+ *
+ * @param pDrvIns The driver instance.
+ */
+static DECLCALLBACK(void) drvUDPTunnelSuspend(PPDMDRVINS pDrvIns)
+{
+ LogFlowFunc(("\n"));
+ PDRVUDPTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVUDPTUNNEL);
+
+ if (pThis->pServer)
+ {
+ RTUdpServerDestroy(pThis->pServer);
+ pThis->pServer = NULL;
+ }
+}
+
+
+/**
+ * Resume notification.
+ *
+ * @param pDrvIns The driver instance.
+ */
+static DECLCALLBACK(void) drvUDPTunnelResume(PPDMDRVINS pDrvIns)
+{
+ LogFlowFunc(("\n"));
+ PDRVUDPTUNNEL pThis = PDMINS_2_DATA(pDrvIns, PDRVUDPTUNNEL);
+
+ int rc = RTUdpServerCreate("", pThis->uSrcPort, RTTHREADTYPE_IO, pThis->pszInstance,
+ drvUDPTunnelReceive, pDrvIns, &pThis->pServer);
+ if (RT_FAILURE(rc))
+ PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("UDPTunnel: Failed to start the UDP tunnel server"));
+
+}
+
+
+/**
+ * UDP tunnel network transport driver registration record.
+ */
+const PDMDRVREG g_DrvUDPTunnel =
+{
+ /* u32Version */
+ PDM_DRVREG_VERSION,
+ /* szName */
+ "UDPTunnel",
+ /* szRCMod */
+ "",
+ /* szR0Mod */
+ "",
+ /* pszDescription */
+ "UDP Tunnel Network Transport Driver",
+ /* fFlags */
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+ /* fClass. */
+ PDM_DRVREG_CLASS_NETWORK,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(DRVUDPTUNNEL),
+ /* pfnConstruct */
+ drvUDPTunnelConstruct,
+ /* pfnDestruct */
+ drvUDPTunnelDestruct,
+ /* pfnRelocate */
+ NULL,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ NULL,
+ /* pfnSuspend */
+ drvUDPTunnelSuspend,
+ /* pfnResume */
+ drvUDPTunnelResume,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32EndVersion */
+ PDM_DRVREG_VERSION
+};
+
diff --git a/src/VBox/Devices/Network/DrvVDE.cpp b/src/VBox/Devices/Network/DrvVDE.cpp
new file mode 100644
index 00000000..e3b6bb82
--- /dev/null
+++ b/src/VBox/Devices/Network/DrvVDE.cpp
@@ -0,0 +1,695 @@
+/* $Id: DrvVDE.cpp $ */
+/** @file
+ * VDE network transport driver.
+ */
+
+/*
+ * Contributed by Renzo Davoli. VirtualSquare. University of Bologna, 2010
+ *
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_TUN
+#include <VBox/log.h>
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmnetifs.h>
+#include <VBox/vmm/pdmnetinline.h>
+#include <VBox/VDEPlug.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/param.h>
+#include <iprt/path.h>
+#include <iprt/pipe.h>
+#include <iprt/semaphore.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/uuid.h>
+
+#include <sys/ioctl.h>
+#include <sys/poll.h>
+#include <sys/fcntl.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "VBoxDD.h"
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * VDE driver instance data.
+ *
+ * @implements PDMINETWORKUP
+ */
+typedef struct DRVVDE
+{
+ /** The network interface. */
+ PDMINETWORKUP INetworkUp;
+ /** The network interface. */
+ PPDMINETWORKDOWN pIAboveNet;
+ /** Pointer to the driver instance. */
+ PPDMDRVINS pDrvIns;
+ /** The configured VDE device name. */
+ char *pszDeviceName;
+ /** The write end of the control pipe. */
+ RTPIPE hPipeWrite;
+ /** The read end of the control pipe. */
+ RTPIPE hPipeRead;
+ /** Reader thread. */
+ PPDMTHREAD pThread;
+ /** The connection to the VDE switch */
+ VDECONN *pVdeConn;
+
+ /** @todo The transmit thread. */
+ /** Transmit lock used by drvTAPNetworkUp_BeginXmit. */
+ RTCRITSECT XmitLock;
+
+#ifdef VBOX_WITH_STATISTICS
+ /** Number of sent packets. */
+ STAMCOUNTER StatPktSent;
+ /** Number of sent bytes. */
+ STAMCOUNTER StatPktSentBytes;
+ /** Number of received packets. */
+ STAMCOUNTER StatPktRecv;
+ /** Number of received bytes. */
+ STAMCOUNTER StatPktRecvBytes;
+ /** Profiling packet transmit runs. */
+ STAMPROFILE StatTransmit;
+ /** Profiling packet receive runs. */
+ STAMPROFILEADV StatReceive;
+#endif /* VBOX_WITH_STATISTICS */
+
+#ifdef LOG_ENABLED
+ /** The nano ts of the last transfer. */
+ uint64_t u64LastTransferTS;
+ /** The nano ts of the last receive. */
+ uint64_t u64LastReceiveTS;
+#endif
+} DRVVDE, *PDRVVDE;
+
+
+/** Converts a pointer to VDE::INetworkUp to a PRDVVDE. */
+#define PDMINETWORKUP_2_DRVVDE(pInterface) ( (PDRVVDE)((uintptr_t)pInterface - RT_UOFFSETOF(DRVVDE, INetworkUp)) )
+
+
+/*********************************************************************************************************************************
+* Internal Functions *
+*********************************************************************************************************************************/
+
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
+ */
+static DECLCALLBACK(int) drvVDENetworkUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
+{
+ RT_NOREF(fOnWorkerThread);
+ PDRVVDE pThis = PDMINETWORKUP_2_DRVVDE(pInterface);
+ int rc = RTCritSectTryEnter(&pThis->XmitLock);
+ if (RT_FAILURE(rc))
+ {
+ /** @todo XMIT thread */
+ rc = VERR_TRY_AGAIN;
+ }
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
+ */
+static DECLCALLBACK(int) drvVDENetworkUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
+ PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
+{
+ RT_NOREF(pInterface);
+#ifdef VBOX_STRICT
+ PDRVVDE pThis = PDMINETWORKUP_2_DRVVDE(pInterface);
+ Assert(RTCritSectIsOwner(&pThis->XmitLock));
+#endif
+
+ /*
+ * Allocate a scatter / gather buffer descriptor that is immediately
+ * followed by the buffer space of its single segment. The GSO context
+ * comes after that again.
+ */
+ PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAlloc( RT_ALIGN_Z(sizeof(*pSgBuf), 16)
+ + RT_ALIGN_Z(cbMin, 16)
+ + (pGso ? RT_ALIGN_Z(sizeof(*pGso), 16) : 0));
+ if (!pSgBuf)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize the S/G buffer and return.
+ */
+ pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
+ pSgBuf->cbUsed = 0;
+ pSgBuf->cbAvailable = RT_ALIGN_Z(cbMin, 16);
+ pSgBuf->pvAllocator = NULL;
+ if (!pGso)
+ pSgBuf->pvUser = NULL;
+ else
+ {
+ pSgBuf->pvUser = (uint8_t *)(pSgBuf + 1) + pSgBuf->cbAvailable;
+ *(PPDMNETWORKGSO)pSgBuf->pvUser = *pGso;
+ }
+ pSgBuf->cSegs = 1;
+ pSgBuf->aSegs[0].cbSeg = pSgBuf->cbAvailable;
+ pSgBuf->aSegs[0].pvSeg = pSgBuf + 1;
+
+#if 0 /* poison */
+ memset(pSgBuf->aSegs[0].pvSeg, 'F', pSgBuf->aSegs[0].cbSeg);
+#endif
+ *ppSgBuf = pSgBuf;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
+ */
+static DECLCALLBACK(int) drvVDENetworkUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
+{
+ RT_NOREF(pInterface);
+#ifdef VBOX_STRICT
+ PDRVVDE pThis = PDMINETWORKUP_2_DRVVDE(pInterface);
+ Assert(RTCritSectIsOwner(&pThis->XmitLock));
+#endif
+ if (pSgBuf)
+ {
+ Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
+ pSgBuf->fFlags = 0;
+ RTMemFree(pSgBuf);
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
+ */
+static DECLCALLBACK(int) drvVDENetworkUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
+{
+ RT_NOREF(fOnWorkerThread);
+ PDRVVDE pThis = PDMINETWORKUP_2_DRVVDE(pInterface);
+ STAM_COUNTER_INC(&pThis->StatPktSent);
+ STAM_COUNTER_ADD(&pThis->StatPktSentBytes, pSgBuf->cbUsed);
+ STAM_PROFILE_START(&pThis->StatTransmit, a);
+
+ AssertPtr(pSgBuf);
+ Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
+ Assert(RTCritSectIsOwner(&pThis->XmitLock));
+
+ int rc;
+ if (!pSgBuf->pvUser)
+ {
+#ifdef LOG_ENABLED
+ uint64_t u64Now = RTTimeProgramNanoTS();
+ LogFlow(("drvVDESend: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
+ pSgBuf->cbUsed, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS));
+ pThis->u64LastTransferTS = u64Now;
+#endif
+ Log2(("drvVDESend: pSgBuf->aSegs[0].pvSeg=%p pSgBuf->cbUsed=%#x\n"
+ "%.*Rhxd\n",
+ pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, pSgBuf->cbUsed, pSgBuf->aSegs[0].pvSeg));
+
+ ssize_t cbSent;
+ cbSent = vde_send(pThis->pVdeConn, pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed, 0);
+ rc = cbSent < 0 ? RTErrConvertFromErrno(-cbSent) : VINF_SUCCESS;
+ }
+ else
+ {
+ uint8_t abHdrScratch[256];
+ uint8_t const *pbFrame = (uint8_t const *)pSgBuf->aSegs[0].pvSeg;
+ PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pSgBuf->pvUser;
+ uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, pSgBuf->cbUsed); Assert(cSegs > 1);
+ rc = 0;
+ for (size_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ uint32_t cbSegFrame;
+ void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)pbFrame, pSgBuf->cbUsed, abHdrScratch,
+ iSeg, cSegs, &cbSegFrame);
+ ssize_t cbSent;
+ cbSent = vde_send(pThis->pVdeConn, pvSegFrame, cbSegFrame, 0);
+ rc = cbSent < 0 ? RTErrConvertFromErrno(-cbSent) : VINF_SUCCESS;
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+
+ pSgBuf->fFlags = 0;
+ RTMemFree(pSgBuf);
+
+ STAM_PROFILE_STOP(&pThis->StatTransmit, a);
+ AssertRC(rc);
+ if (RT_FAILURE(rc))
+ rc = rc == VERR_NO_MEMORY ? VERR_NET_NO_BUFFER_SPACE : VERR_NET_DOWN;
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
+ */
+static DECLCALLBACK(void) drvVDENetworkUp_EndXmit(PPDMINETWORKUP pInterface)
+{
+ PDRVVDE pThis = PDMINETWORKUP_2_DRVVDE(pInterface);
+ RTCritSectLeave(&pThis->XmitLock);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
+ */
+static DECLCALLBACK(void) drvVDENetworkUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
+{
+ RT_NOREF(pInterface, fPromiscuous);
+ LogFlow(("drvVDESetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
+ /* nothing to do */
+}
+
+
+/**
+ * Notification on link status changes.
+ *
+ * @param pInterface Pointer to the interface structure containing the called function pointer.
+ * @param enmLinkState The new link state.
+ * @thread EMT
+ */
+static DECLCALLBACK(void) drvVDENetworkUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
+{
+ RT_NOREF(pInterface, enmLinkState);
+ LogFlow(("drvNATNetworkUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
+ /** @todo take action on link down and up. Stop the polling and such like. */
+}
+
+
+/**
+ * Asynchronous I/O thread for handling receive.
+ *
+ * @returns VINF_SUCCESS (ignored).
+ * @param Thread Thread handle.
+ * @param pvUser Pointer to a DRVVDE structure.
+ */
+static DECLCALLBACK(int) drvVDEAsyncIoThread(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ PDRVVDE pThis = PDMINS_2_DATA(pDrvIns, PDRVVDE);
+ LogFlow(("drvVDEAsyncIoThread: pThis=%p\n", pThis));
+
+ if (pThread->enmState == PDMTHREADSTATE_INITIALIZING)
+ return VINF_SUCCESS;
+
+ STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
+
+ /*
+ * Polling loop.
+ */
+ while (pThread->enmState == PDMTHREADSTATE_RUNNING)
+ {
+ /*
+ * Wait for something to become available.
+ */
+ struct pollfd aFDs[2];
+ aFDs[0].fd = vde_datafd(pThis->pVdeConn);
+ aFDs[0].events = POLLIN | POLLPRI;
+ aFDs[0].revents = 0;
+ aFDs[1].fd = RTPipeToNative(pThis->hPipeRead);
+ aFDs[1].events = POLLIN | POLLPRI | POLLERR | POLLHUP;
+ aFDs[1].revents = 0;
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+ errno=0;
+ int rc = poll(&aFDs[0], RT_ELEMENTS(aFDs), -1 /* infinite */);
+
+ /* this might have changed in the meantime */
+ if (pThread->enmState != PDMTHREADSTATE_RUNNING)
+ break;
+
+ STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
+ if ( rc > 0
+ && (aFDs[0].revents & (POLLIN | POLLPRI))
+ && !aFDs[1].revents)
+ {
+ /*
+ * Read the frame.
+ */
+ char achBuf[16384];
+ ssize_t cbRead = 0;
+ cbRead = vde_recv(pThis->pVdeConn, achBuf, sizeof(achBuf), 0);
+ rc = cbRead < 0 ? RTErrConvertFromErrno(-cbRead) : VINF_SUCCESS;
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Wait for the device to have space for this frame.
+ * Most guests use frame-sized receive buffers, hence non-zero cbMax
+ * automatically means there is enough room for entire frame. Some
+ * guests (eg. Solaris) use large chains of small receive buffers
+ * (each 128 or so bytes large). We will still start receiving as soon
+ * as cbMax is non-zero because:
+ * - it would be quite expensive for pfnCanReceive to accurately
+ * determine free receive buffer space
+ * - if we were waiting for enough free buffers, there is a risk
+ * of deadlocking because the guest could be waiting for a receive
+ * overflow error to allocate more receive buffers
+ */
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+ int rc1 = pThis->pIAboveNet->pfnWaitReceiveAvail(pThis->pIAboveNet, RT_INDEFINITE_WAIT);
+ STAM_PROFILE_ADV_START(&pThis->StatReceive, a);
+
+ /*
+ * A return code != VINF_SUCCESS means that we were woken up during a VM
+ * state transition. Drop the packet and wait for the next one.
+ */
+ if (RT_FAILURE(rc1))
+ continue;
+
+ /*
+ * Pass the data up.
+ */
+#ifdef LOG_ENABLED
+ uint64_t u64Now = RTTimeProgramNanoTS();
+ LogFlow(("drvVDEAsyncIoThread: %-4d bytes at %llu ns deltas: r=%llu t=%llu\n",
+ cbRead, u64Now, u64Now - pThis->u64LastReceiveTS, u64Now - pThis->u64LastTransferTS));
+ pThis->u64LastReceiveTS = u64Now;
+#endif
+ Log2(("drvVDEAsyncIoThread: cbRead=%#x\n" "%.*Rhxd\n", cbRead, cbRead, achBuf));
+ STAM_COUNTER_INC(&pThis->StatPktRecv);
+ STAM_COUNTER_ADD(&pThis->StatPktRecvBytes, cbRead);
+ rc1 = pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, achBuf, cbRead);
+ AssertRC(rc1);
+ }
+ else
+ {
+ LogFlow(("drvVDEAsyncIoThread: RTFileRead -> %Rrc\n", rc));
+ if (rc == VERR_INVALID_HANDLE)
+ break;
+ RTThreadYield();
+ }
+ }
+ else if ( rc > 0
+ && aFDs[1].revents)
+ {
+ LogFlow(("drvVDEAsyncIoThread: Control message: enmState=%d revents=%#x\n", pThread->enmState, aFDs[1].revents));
+ if (aFDs[1].revents & (POLLHUP | POLLERR | POLLNVAL))
+ break;
+
+ /* drain the pipe */
+ char ch;
+ size_t cbRead;
+ RTPipeRead(pThis->hPipeRead, &ch, 1, &cbRead);
+ }
+ else
+ {
+ /*
+ * poll() failed for some reason. Yield to avoid eating too much CPU.
+ *
+ * EINTR errors have been seen frequently. They should be harmless, even
+ * if they are not supposed to occur in our setup.
+ */
+ if (errno == EINTR)
+ Log(("rc=%d revents=%#x,%#x errno=%p %s\n", rc, aFDs[0].revents, aFDs[1].revents, errno, strerror(errno)));
+ else
+ AssertMsgFailed(("rc=%d revents=%#x,%#x errno=%p %s\n", rc, aFDs[0].revents, aFDs[1].revents, errno, strerror(errno)));
+ RTThreadYield();
+ }
+ }
+
+
+ LogFlow(("drvVDEAsyncIoThread: returns %Rrc\n", VINF_SUCCESS));
+ STAM_PROFILE_ADV_STOP(&pThis->StatReceive, a);
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Unblock the send thread so it can respond to a state change.
+ *
+ * @returns VBox status code.
+ * @param pDevIns The pcnet device instance.
+ * @param pThread The send thread.
+ */
+static DECLCALLBACK(int) drvVDEAsyncIoWakeup(PPDMDRVINS pDrvIns, PPDMTHREAD pThread)
+{
+ RT_NOREF(pThread);
+ PDRVVDE pThis = PDMINS_2_DATA(pDrvIns, PDRVVDE);
+
+ size_t cbIgnored;
+ int rc = RTPipeWrite(pThis->hPipeWrite, "", 1, &cbIgnored);
+ AssertRC(rc);
+
+ return VINF_SUCCESS;
+}
+
+
+/* -=-=-=-=- PDMIBASE -=-=-=-=- */
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvVDEQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+ PDRVVDE pThis = PDMINS_2_DATA(pDrvIns, PDRVVDE);
+
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
+ return NULL;
+}
+
+/* -=-=-=-=- PDMDRVREG -=-=-=-=- */
+
+/**
+ * Destruct a driver instance.
+ *
+ * Most VM resources are freed by the VM. This callback is provided so that any non-VM
+ * resources can be freed correctly.
+ *
+ * @param pDrvIns The driver instance data.
+ */
+static DECLCALLBACK(void) drvVDEDestruct(PPDMDRVINS pDrvIns)
+{
+ LogFlow(("drvVDEDestruct\n"));
+ PDRVVDE pThis = PDMINS_2_DATA(pDrvIns, PDRVVDE);
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+
+ /*
+ * Terminate the control pipe.
+ */
+ if (pThis->hPipeWrite != NIL_RTPIPE)
+ {
+ RTPipeClose(pThis->hPipeWrite);
+ pThis->hPipeWrite = NIL_RTPIPE;
+ }
+ if (pThis->hPipeRead != NIL_RTPIPE)
+ {
+ RTPipeClose(pThis->hPipeRead);
+ pThis->hPipeRead = NIL_RTPIPE;
+ }
+
+ PDMDrvHlpMMHeapFree(pDrvIns, pThis->pszDeviceName);
+ pThis->pszDeviceName = NULL;
+
+ /*
+ * Kill the xmit lock.
+ */
+ if (RTCritSectIsInitialized(&pThis->XmitLock))
+ RTCritSectDelete(&pThis->XmitLock);
+
+ if (pThis->pVdeConn)
+ {
+ vde_close(pThis->pVdeConn);
+ pThis->pVdeConn = NULL;
+ }
+
+#ifdef VBOX_WITH_STATISTICS
+ /*
+ * Deregister statistics.
+ */
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktSent);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktSentBytes);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktRecv);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatPktRecvBytes);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatTransmit);
+ PDMDrvHlpSTAMDeregister(pDrvIns, &pThis->StatReceive);
+#endif /* VBOX_WITH_STATISTICS */
+}
+
+
+/**
+ * Construct a VDE network transport driver instance.
+ *
+ * @copydoc FNPDMDRVCONSTRUCT
+ */
+static DECLCALLBACK(int) drvVDEConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ RT_NOREF(fFlags);
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVVDE pThis = PDMINS_2_DATA(pDrvIns, PDRVVDE);
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ /*
+ * Init the static parts.
+ */
+ pThis->pDrvIns = pDrvIns;
+ pThis->pszDeviceName = NULL;
+ pThis->hPipeRead = NIL_RTPIPE;
+ pThis->hPipeWrite = NIL_RTPIPE;
+
+ /* IBase */
+ pDrvIns->IBase.pfnQueryInterface = drvVDEQueryInterface;
+ /* INetwork */
+ pThis->INetworkUp.pfnBeginXmit = drvVDENetworkUp_BeginXmit;
+ pThis->INetworkUp.pfnAllocBuf = drvVDENetworkUp_AllocBuf;
+ pThis->INetworkUp.pfnFreeBuf = drvVDENetworkUp_FreeBuf;
+ pThis->INetworkUp.pfnSendBuf = drvVDENetworkUp_SendBuf;
+ pThis->INetworkUp.pfnEndXmit = drvVDENetworkUp_EndXmit;
+ pThis->INetworkUp.pfnSetPromiscuousMode = drvVDENetworkUp_SetPromiscuousMode;
+ pThis->INetworkUp.pfnNotifyLinkChanged = drvVDENetworkUp_NotifyLinkChanged;
+
+#ifdef VBOX_WITH_STATISTICS
+ /*
+ * Statistics.
+ */
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktSent, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of sent packets.", "/Drivers/VDE%d/Packets/Sent", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktSentBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of sent bytes.", "/Drivers/VDE%d/Bytes/Sent", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktRecv, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_OCCURENCES, "Number of received packets.", "/Drivers/VDE%d/Packets/Received", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatPktRecvBytes, STAMTYPE_COUNTER, STAMVISIBILITY_ALWAYS, STAMUNIT_BYTES, "Number of received bytes.", "/Drivers/VDE%d/Bytes/Received", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatTransmit, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet transmit runs.", "/Drivers/VDE%d/Transmit", pDrvIns->iInstance);
+ PDMDrvHlpSTAMRegisterF(pDrvIns, &pThis->StatReceive, STAMTYPE_PROFILE, STAMVISIBILITY_ALWAYS, STAMUNIT_TICKS_PER_CALL, "Profiling packet receive runs.", "/Drivers/VDE%d/Receive", pDrvIns->iInstance);
+#endif /* VBOX_WITH_STATISTICS */
+
+ /*
+ * Validate the config.
+ */
+ PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns, "network", "");
+
+ /*
+ * Check that no-one is attached to us.
+ */
+ AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
+ ("Configuration error: Not possible to attach anything to this driver!\n"),
+ VERR_PDM_DRVINS_NO_ATTACH);
+
+ /*
+ * Query the network port interface.
+ */
+ pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
+ if (!pThis->pIAboveNet)
+ return PDMDRV_SET_ERROR(pDrvIns, VERR_PDM_MISSING_INTERFACE_ABOVE,
+ N_("Configuration error: The above device/driver didn't export the network port interface"));
+
+ /*
+ * Read the configuration.
+ */
+ int rc;
+ char szNetwork[RTPATH_MAX];
+ rc = pHlp->pfnCFGMQueryString(pCfg, "network", szNetwork, sizeof(szNetwork));
+ if (RT_FAILURE(rc))
+ *szNetwork=0;
+
+ if (RT_FAILURE(DrvVDELoadVDEPlug()))
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("VDEplug library: not found"));
+ pThis->pVdeConn = vde_open(szNetwork, "VirtualBOX", NULL);
+ if (pThis->pVdeConn == NULL)
+ return PDMDrvHlpVMSetError(pThis->pDrvIns, VERR_PDM_HIF_OPEN_FAILED, RT_SRC_POS,
+ N_("Failed to connect to the VDE SWITCH"));
+
+ /*
+ * Create the transmit lock.
+ */
+ rc = RTCritSectInit(&pThis->XmitLock);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Create the control pipe.
+ */
+ rc = RTPipeCreate(&pThis->hPipeRead, &pThis->hPipeWrite, 0 /*fFlags*/);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Create the async I/O thread.
+ */
+ rc = PDMDrvHlpThreadCreate(pDrvIns, &pThis->pThread, pThis, drvVDEAsyncIoThread, drvVDEAsyncIoWakeup, 128 * _1K, RTTHREADTYPE_IO, "VDE");
+ AssertRCReturn(rc, rc);
+
+ return rc;
+}
+
+
+/**
+ * VDE network transport driver registration record.
+ */
+const PDMDRVREG g_DrvVDE =
+{
+ /* u32Version */
+ PDM_DRVREG_VERSION,
+ /* szName */
+ "VDE",
+ /* szRCMod */
+ "",
+ /* szR0Mod */
+ "",
+ /* pszDescription */
+ "VDE Network Transport Driver",
+ /* fFlags */
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+ /* fClass. */
+ PDM_DRVREG_CLASS_NETWORK,
+ /* cMaxInstances */
+ ~0U,
+ /* cbInstance */
+ sizeof(DRVVDE),
+ /* pfnConstruct */
+ drvVDEConstruct,
+ /* pfnDestruct */
+ drvVDEDestruct,
+ /* pfnRelocate */
+ NULL,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ NULL,
+ /* pfnReset */
+ NULL,
+ /* pfnSuspend */
+ NULL, /** @todo Do power on, suspend and resume handlers! */
+ /* pfnResume */
+ NULL,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32EndVersion */
+ PDM_DRVREG_VERSION
+};
+
diff --git a/src/VBox/Devices/Network/DrvVMNet.m b/src/VBox/Devices/Network/DrvVMNet.m
new file mode 100644
index 00000000..7981a734
--- /dev/null
+++ b/src/VBox/Devices/Network/DrvVMNet.m
@@ -0,0 +1,747 @@
+/* $Id: DrvVMNet.m $ */
+/** @file
+ * DrvVMNet - Network filter driver that uses MAC OS VMNET API.
+ */
+
+/*
+ * Copyright (C) 2021-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_DRV_VMNET
+#include <VBox/vmm/pdmdrv.h>
+#include <VBox/vmm/pdmnetifs.h>
+#include <VBox/vmm/pdmnetinline.h>
+#include <VBox/intnet.h>
+
+#include <VBox/log.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <iprt/file.h>
+#include <iprt/mem.h>
+#include <iprt/process.h>
+#include <iprt/string.h>
+#include <iprt/time.h>
+#include <iprt/uuid.h>
+#include <iprt/path.h>
+#include <VBox/param.h>
+
+#include "Pcap.h"
+#include "VBoxDD.h"
+
+#include <sys/uio.h>
+#import <vmnet/vmnet.h>
+
+#define VMNET_MAX_HOST_INTERFACE_NAME_LENGTH 16
+#define VMNET_MAX_IP_ADDRESS_STRING_LENGTH 48
+
+/* Force release logging for debug builds */
+#if 0
+# undef Log
+# undef LogFlow
+# undef Log2
+# undef Log3
+# define Log LogRel
+# define LogFlow LogRel
+# define Log2 LogRel
+# define Log3 LogRel
+#endif
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * VMNET driver states.
+ */
+ typedef enum VMNETSTATE
+{
+ /** The driver is suspended. */
+ VMNETSTATE_SUSPENDED = 1,
+ /** The driver is running. */
+ VMNETSTATE_RUNNING,
+ /** The usual 32-bit type blowup. */
+ VMNETSTATE_32BIT_HACK = 0x7fffffff
+} VMNETSTATE;
+
+/**
+ * VMNET driver instance data.
+ *
+ * @implements PDMINETWORKUP
+ * @implements PDMINETWORKCONFIG
+ */
+typedef struct DRVVMNET
+{
+ /** The network interface. */
+ PDMINETWORKUP INetworkUp;
+ /** The port we're attached to. */
+ PPDMINETWORKDOWN pIAboveNet;
+ /** The config port interface we're attached to. */
+ PPDMINETWORKCONFIG pIAboveConfig;
+ /** Pointer to the driver instance. */
+ PPDMDRVINS pDrvIns;
+ /** For when we're the leaf driver. */
+ RTCRITSECT XmitLock;
+ /** VMNET interface queue handle. */
+ dispatch_queue_t InterfaceQueue;
+ /** VMNET interface handle. */
+ interface_ref Interface;
+ /** The unique id for this network. */
+ uuid_t uuid;
+ /** The operation mode: bridged or host. */
+ uint32_t uMode;
+ /** The operational state: suspended or running. */
+ VMNETSTATE volatile enmState;
+ /** The host interface name for bridge mode. */
+ char szHostInterface[VMNET_MAX_HOST_INTERFACE_NAME_LENGTH];
+ /** The network mask for host mode. */
+ char szNetworkMask[VMNET_MAX_IP_ADDRESS_STRING_LENGTH];
+ /** The lower IP address of DHCP range for host mode. */
+ char szLowerIP[VMNET_MAX_IP_ADDRESS_STRING_LENGTH];
+ /** The upper IP address of DHCP range for host mode. */
+ char szUpperIP[VMNET_MAX_IP_ADDRESS_STRING_LENGTH];
+} DRVVMNET, *PDRVVMNET;
+
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnBeginXmit}
+ */
+static DECLCALLBACK(int) drvVMNetUp_BeginXmit(PPDMINETWORKUP pInterface, bool fOnWorkerThread)
+{
+ RT_NOREF(fOnWorkerThread);
+ PDRVVMNET pThis = RT_FROM_MEMBER(pInterface, DRVVMNET, INetworkUp);
+ LogFlow(("drvVMNetUp_BeginXmit:\n"));
+ int rc = RTCritSectTryEnter(&pThis->XmitLock);
+ if (RT_UNLIKELY(rc == VERR_SEM_BUSY))
+ rc = VERR_TRY_AGAIN;
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnAllocBuf}
+ */
+static DECLCALLBACK(int) drvVMNetUp_AllocBuf(PPDMINETWORKUP pInterface, size_t cbMin,
+ PCPDMNETWORKGSO pGso, PPPDMSCATTERGATHER ppSgBuf)
+{
+ RT_NOREF(pInterface);
+ //PDRVVMNET pThis = RT_FROM_MEMBER(pInterface, DRVVMNET, INetworkUp);
+ LogFlow(("drvVMNetUp_AllocBuf: cb=%llu%s\n", cbMin, pGso == NULL ? "" : " GSO"));
+ /*
+ * Allocate a scatter / gather buffer descriptor that is immediately
+ * followed by the buffer space of its single segment. The GSO context
+ * comes after that again.
+ */
+ PPDMSCATTERGATHER pSgBuf = (PPDMSCATTERGATHER)RTMemAlloc( RT_ALIGN_Z(sizeof(*pSgBuf), 16)
+ + RT_ALIGN_Z(cbMin, 16)
+ + (pGso ? RT_ALIGN_Z(sizeof(*pGso), 16) : 0));
+ if (!pSgBuf)
+ return VERR_NO_MEMORY;
+
+ /*
+ * Initialize the S/G buffer and return.
+ */
+ pSgBuf->fFlags = PDMSCATTERGATHER_FLAGS_MAGIC | PDMSCATTERGATHER_FLAGS_OWNER_1;
+ pSgBuf->cbUsed = 0;
+ pSgBuf->cbAvailable = RT_ALIGN_Z(cbMin, 16);
+ pSgBuf->pvAllocator = NULL;
+ if (!pGso)
+ pSgBuf->pvUser = NULL;
+ else
+ {
+ pSgBuf->pvUser = (uint8_t *)(pSgBuf + 1) + pSgBuf->cbAvailable;
+ *(PPDMNETWORKGSO)pSgBuf->pvUser = *pGso;
+ }
+ pSgBuf->cSegs = 1;
+ pSgBuf->aSegs[0].cbSeg = pSgBuf->cbAvailable;
+ pSgBuf->aSegs[0].pvSeg = pSgBuf + 1;
+
+ LogFlow(("drvVMNetUp_AllocBuf: successful %p\n", pSgBuf));
+ *ppSgBuf = pSgBuf;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnFreeBuf}
+ */
+static DECLCALLBACK(int) drvVMNetUp_FreeBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf)
+{
+ PDRVVMNET pThis = RT_FROM_MEMBER(pInterface, DRVVMNET, INetworkUp);
+ LogFlow(("drvVMNetUp_FreeBuf: %p\n", pSgBuf));
+ Assert(RTCritSectIsOwner(&pThis->XmitLock));
+ RT_NOREF(pThis);
+ if (pSgBuf)
+ {
+ Assert((pSgBuf->fFlags & PDMSCATTERGATHER_FLAGS_MAGIC_MASK) == PDMSCATTERGATHER_FLAGS_MAGIC);
+ pSgBuf->fFlags = 0;
+ RTMemFree(pSgBuf);
+ }
+ return VINF_SUCCESS;
+}
+
+
+static int drvVMNetReceive(PDRVVMNET pThis, const uint8_t *pbFrame, uint32_t cbFrame)
+{
+ if (pThis->enmState != VMNETSTATE_RUNNING)
+ {
+ Log(("drvVMNetReceive: Ignoring incoming packet (%d bytes) in suspended state\n", cbFrame));
+ return VINF_SUCCESS;
+ }
+
+ Log(("drvVMNetReceive: Incoming packet: %RTmac <= %RTmac (%d bytes)\n", pbFrame, pbFrame + 6, cbFrame));
+ Log2(("%.*Rhxd\n", cbFrame, pbFrame));
+ if (pThis->pIAboveNet && pThis->pIAboveNet->pfnReceive)
+ return pThis->pIAboveNet->pfnReceive(pThis->pIAboveNet, pbFrame, cbFrame);
+ return VERR_TRY_AGAIN;
+}
+
+
+static int drvVMNetSend(PDRVVMNET pThis, const uint8_t *pbFrame, uint32_t cbFrame)
+{
+ if (pThis->enmState != VMNETSTATE_RUNNING)
+ {
+ Log(("drvVMNetReceive: Ignoring outgoing packet (%d bytes) in suspended state\n", cbFrame));
+ return VINF_SUCCESS;
+ }
+
+ Log(("drvVMNetSend: Outgoing packet (%d bytes)\n", cbFrame));
+ Log2(("%.*Rhxd\n", cbFrame, pbFrame));
+
+ struct iovec io;
+ struct vmpktdesc packets;
+ int packet_count = 1;
+
+ io.iov_base = (void*)pbFrame;
+ io.iov_len = cbFrame;
+ packets.vm_pkt_size = cbFrame;
+ packets.vm_pkt_iov = &io;
+ packets.vm_pkt_iovcnt = 1;
+ packets.vm_flags = 0;
+
+ vmnet_return_t rc = vmnet_write(pThis->Interface, &packets, &packet_count);
+ if (rc != VMNET_SUCCESS)
+ Log(("drvVMNetSend: Failed to send a packet with error code %d\n", rc));
+ return (rc == VMNET_SUCCESS) ? VINF_SUCCESS : VERR_GENERAL_FAILURE;
+}
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSendBuf}
+ */
+static DECLCALLBACK(int) drvVMNetUp_SendBuf(PPDMINETWORKUP pInterface, PPDMSCATTERGATHER pSgBuf, bool fOnWorkerThread)
+{
+ RT_NOREF(fOnWorkerThread);
+ PDRVVMNET pThis = RT_FROM_MEMBER(pInterface, DRVVMNET, INetworkUp);
+
+ LogFlow(("drvVMNetUp_SendBuf: %p\n", pSgBuf));
+ Assert(RTCritSectIsOwner(&pThis->XmitLock));
+
+ int rc;
+ if (!pSgBuf->pvUser)
+ {
+ rc = drvVMNetSend(pThis, pSgBuf->aSegs[0].pvSeg, pSgBuf->cbUsed);
+ }
+ else
+ {
+ uint8_t abHdrScratch[256];
+ uint8_t const *pbFrame = (uint8_t const *)pSgBuf->aSegs[0].pvSeg;
+ PCPDMNETWORKGSO pGso = (PCPDMNETWORKGSO)pSgBuf->pvUser;
+ uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, pSgBuf->cbUsed); Assert(cSegs > 1);
+ rc = VINF_SUCCESS;
+ for (uint32_t iSeg = 0; iSeg < cSegs && RT_SUCCESS(rc); iSeg++)
+ {
+ uint32_t cbSegFrame;
+ void *pvSegFrame = PDMNetGsoCarveSegmentQD(pGso, (uint8_t *)pbFrame, pSgBuf->cbUsed, abHdrScratch,
+ iSeg, cSegs, &cbSegFrame);
+ rc = drvVMNetSend(pThis, pvSegFrame, cbSegFrame);
+ }
+ }
+
+ LogFlow(("drvVMNetUp_SendBuf: free %p\n", pSgBuf));
+ pSgBuf->fFlags = 0;
+ RTMemFree(pSgBuf);
+ return rc;
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnEndXmit}
+ */
+static DECLCALLBACK(void) drvVMNetUp_EndXmit(PPDMINETWORKUP pInterface)
+{
+ LogFlow(("drvVMNetUp_EndXmit:\n"));
+ PDRVVMNET pThis = RT_FROM_MEMBER(pInterface, DRVVMNET, INetworkUp);
+ RTCritSectLeave(&pThis->XmitLock);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnSetPromiscuousMode}
+ */
+static DECLCALLBACK(void) drvVMNetUp_SetPromiscuousMode(PPDMINETWORKUP pInterface, bool fPromiscuous)
+{
+ RT_NOREF(pInterface, fPromiscuous);
+ LogFlow(("drvVMNetUp_SetPromiscuousMode: fPromiscuous=%d\n", fPromiscuous));
+ // PDRVVMNET pThis = RT_FROM_MEMBER(pInterface, DRVVMNET, INetworkUp);
+}
+
+
+/**
+ * @interface_method_impl{PDMINETWORKUP,pfnNotifyLinkChanged}
+ */
+static DECLCALLBACK(void) drvVMNetUp_NotifyLinkChanged(PPDMINETWORKUP pInterface, PDMNETWORKLINKSTATE enmLinkState)
+{
+ RT_NOREF(pInterface, enmLinkState);
+ LogFlow(("drvVMNetUp_NotifyLinkChanged: enmLinkState=%d\n", enmLinkState));
+ // PDRVVMNET pThis = RT_FROM_MEMBER(pInterface, DRVVMNET, INetworkUp);
+}
+
+
+/**
+ * @interface_method_impl{PDMIBASE,pfnQueryInterface}
+ */
+static DECLCALLBACK(void *) drvVMNetQueryInterface(PPDMIBASE pInterface, const char *pszIID)
+{
+ PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pInterface);
+ PDRVVMNET pThis = PDMINS_2_DATA(pDrvIns, PDRVVMNET);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase);
+ PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKUP, &pThis->INetworkUp);
+ //PDMIBASE_RETURN_INTERFACE(pszIID, PDMINETWORKVMNETCONFIG, &pThis->INetworkVmnetConfig);
+ return NULL;
+}
+
+
+static vmnet_return_t drvVMNetAttach(PDRVVMNET pThis)
+{
+ xpc_object_t interface_desc;
+ dispatch_semaphore_t operation_done;
+ __block vmnet_return_t vmnet_status = VMNET_FAILURE;
+ __block size_t max_packet_size = 0;
+ //__block RTMAC MacAddress;
+
+ pThis->InterfaceQueue = dispatch_queue_create("VMNET", DISPATCH_QUEUE_SERIAL);
+ operation_done = dispatch_semaphore_create(0);
+ interface_desc = xpc_dictionary_create(NULL, NULL, 0);
+ xpc_dictionary_set_uuid(interface_desc, vmnet_interface_id_key, pThis->uuid);
+ xpc_dictionary_set_bool(interface_desc, vmnet_allocate_mac_address_key, false);
+ xpc_dictionary_set_uint64(interface_desc, vmnet_operation_mode_key, pThis->uMode);
+ if (pThis->uMode == VMNET_BRIDGED_MODE)
+ {
+ LogFlow(("drvVMNetAttach: mode=briged hostInterface='%s'\n", pThis->szHostInterface));
+ xpc_dictionary_set_string(interface_desc, vmnet_shared_interface_name_key, pThis->szHostInterface);
+ }
+ else
+ {
+#ifdef LOG_ENABLED
+ char szUUID[40];
+ uuid_unparse(pThis->uuid, szUUID);
+ LogFlow(("drvVMNetAttach: mode=host id='%s' netmask='%s' start='%s' end='%s'\n", szUUID, pThis->szNetworkMask, pThis->szLowerIP, pThis->szUpperIP));
+#endif
+ xpc_dictionary_set_string(interface_desc, vmnet_subnet_mask_key, pThis->szNetworkMask);
+ xpc_dictionary_set_string(interface_desc, vmnet_start_address_key, pThis->szLowerIP);
+ xpc_dictionary_set_string(interface_desc, vmnet_end_address_key, pThis->szUpperIP);
+ }
+ pThis->Interface = vmnet_start_interface(interface_desc, pThis->InterfaceQueue,
+ ^(vmnet_return_t status, xpc_object_t interface_param)
+ {
+ // Log(("Callback reached!\n"));
+ vmnet_status = status;
+ if (status != VMNET_SUCCESS)
+ Log(("Failed to start VMNET interface. Status = %d.\n", status));
+ else if (interface_param == NULL)
+ Log(("No interface parameters provided!\n"));
+ else
+ {
+ Log(("VMNET interface has been started. Status = %d.\n", status));
+#if 0
+ const char *pcszMacAddress = xpc_dictionary_get_string(interface_param, vmnet_mac_address_key);
+ int rc = VERR_NOT_FOUND;
+ if (pcszMacAddress)
+ rc = RTNetStrToMacAddr(pcszMacAddress, &pThis->MacAddress);
+ if (RT_FAILURE(rc))
+ Log(("drvVMNetAttachBridged: Failed to convert '%s' to MAC address (%Rrc)\n", pcszMacAddress ? pcszMacAddress : "(null)", rc));
+#endif
+ max_packet_size = xpc_dictionary_get_uint64(interface_param, vmnet_max_packet_size_key);
+ if (max_packet_size == 0)
+ {
+ max_packet_size = 1518;
+ LogRel(("VMNet: Failed to retrieve max packet size, assuming %d bytes.\n", max_packet_size));
+ LogRel(("VMNet: Avaliable interface parameter keys:\n"));
+ xpc_dictionary_apply(interface_param, ^bool(const char * _Nonnull key, xpc_object_t _Nonnull value) {
+ RT_NOREF(value);
+ LogRel(("VMNet: %s\n", key));
+ return true;
+ });
+ }
+#ifdef LOG_ENABLED
+ // Log(("MAC address: %s\n", xpc_dictionary_get_string(interface_param, vmnet_mac_address_key)));
+ Log(("Max packet size: %zu\n", max_packet_size));
+ Log(("MTU size: %llu\n", xpc_dictionary_get_uint64(interface_param, vmnet_mtu_key)));
+ Log(("Avaliable keys:\n"));
+ xpc_dictionary_apply(interface_param, ^bool(const char * _Nonnull key, xpc_object_t _Nonnull value) {
+ RT_NOREF(value);
+ Log(("%s\n", key));
+ return true;
+ });
+#endif /* LOG_ENABLED */
+ }
+ dispatch_semaphore_signal(operation_done);
+ });
+ if (dispatch_semaphore_wait(operation_done, dispatch_time(DISPATCH_TIME_NOW, RT_NS_1MIN)))
+ {
+ LogRel(("VMNet: Failed to start VMNET interface due to time out!\n"));
+ return VMNET_FAILURE;
+ }
+
+ if (vmnet_status != VMNET_SUCCESS)
+ return vmnet_status;
+
+ if (pThis->Interface == NULL)
+ {
+ Log(("Failed to start VMNET interface with unknown status!\n"));
+ return VMNET_FAILURE;
+ }
+
+ LogRel(("VMNet: Max packet size is %zu\n", max_packet_size));
+
+ vmnet_interface_set_event_callback(pThis->Interface, VMNET_INTERFACE_PACKETS_AVAILABLE, pThis->InterfaceQueue, ^(interface_event_t event_mask, xpc_object_t _Nonnull event) {
+ if (event_mask & VMNET_INTERFACE_PACKETS_AVAILABLE)
+ {
+ int rc;
+ struct vmpktdesc packets;
+ struct iovec io;
+ int packet_count = (int)xpc_dictionary_get_uint64(event, vmnet_estimated_packets_available_key);
+ if (packet_count == 1)
+ Log3(("Incoming packets available: %d\n", packet_count));
+ else
+ Log(("WARNING! %d incoming packets available, but we will fetch just one.\n", packet_count));
+ packet_count = 1;
+ io.iov_base = malloc(max_packet_size);
+ io.iov_len = max_packet_size;
+ packets.vm_pkt_iov = &io;
+ packets.vm_pkt_iovcnt = 1;
+ packets.vm_pkt_size = max_packet_size;
+ packets.vm_flags = 0;
+ rc = vmnet_read(pThis->Interface, &packets, &packet_count);
+ if (rc != VMNET_SUCCESS)
+ Log(("Failed to read packets with error code %d\n", rc));
+ else
+ {
+ Log3(("Successfully read %d packets:\n", packet_count));
+ for (int i = 0; i < packet_count; ++i)
+ {
+ rc = drvVMNetReceive(pThis, io.iov_base, packets.vm_pkt_size);
+ }
+ }
+ free(io.iov_base);
+ }
+ });
+
+ return vmnet_status;
+}
+
+static int drvVMNetDetach(PDRVVMNET pThis)
+{
+ if (pThis->Interface)
+ {
+ vmnet_stop_interface(pThis->Interface, pThis->InterfaceQueue, ^(vmnet_return_t status){
+ RT_NOREF(status);
+ Log(("VMNET interface has been stopped. Status = %d.\n", status));
+ });
+ pThis->Interface = 0;
+ }
+ if (pThis->InterfaceQueue)
+ {
+ dispatch_release(pThis->InterfaceQueue);
+ pThis->InterfaceQueue = 0;
+ }
+
+ return 0;
+}
+
+
+/**
+ * @interface_method_impl{PDMDRVREG,pfnDestruct}
+ */
+static DECLCALLBACK(void) drvVMNetDestruct(PPDMDRVINS pDrvIns)
+{
+ PDRVVMNET pThis = PDMINS_2_DATA(pDrvIns, PDRVVMNET);
+ PDMDRV_CHECK_VERSIONS_RETURN_VOID(pDrvIns);
+
+ LogFlow(("drvVMNetDestruct: %p\n", pDrvIns));
+ drvVMNetDetach(pThis);
+ if (RTCritSectIsInitialized(&pThis->XmitLock))
+ RTCritSectDelete(&pThis->XmitLock);
+}
+
+
+/**
+ * @interface_method_impl{Construct a NAT network transport driver instance,
+ * PDMDRVREG,pfnDestruct}
+ */
+static DECLCALLBACK(int) drvVMNetConstruct(PPDMDRVINS pDrvIns, PCFGMNODE pCfg, uint32_t fFlags)
+{
+ RT_NOREF(fFlags);
+ PDMDRV_CHECK_VERSIONS_RETURN(pDrvIns);
+ PDRVVMNET pThis = PDMINS_2_DATA(pDrvIns, PDRVVMNET);
+ PCPDMDRVHLPR3 pHlp = pDrvIns->pHlpR3;
+
+ LogFlow(("drvVMNetConstruct: %p\n", pDrvIns));
+
+ /*
+ * Init the static parts.
+ */
+ pThis->pDrvIns = pDrvIns;
+ /* IBase */
+ pDrvIns->IBase.pfnQueryInterface = drvVMNetQueryInterface;
+ /* INetworkUp */
+ pThis->INetworkUp.pfnBeginXmit = drvVMNetUp_BeginXmit;
+ pThis->INetworkUp.pfnAllocBuf = drvVMNetUp_AllocBuf;
+ pThis->INetworkUp.pfnFreeBuf = drvVMNetUp_FreeBuf;
+ pThis->INetworkUp.pfnSendBuf = drvVMNetUp_SendBuf;
+ pThis->INetworkUp.pfnEndXmit = drvVMNetUp_EndXmit;
+ pThis->INetworkUp.pfnSetPromiscuousMode = drvVMNetUp_SetPromiscuousMode;
+ pThis->INetworkUp.pfnNotifyLinkChanged = drvVMNetUp_NotifyLinkChanged;
+
+ /* Initialize the state. */
+ pThis->enmState = VMNETSTATE_SUSPENDED;
+
+ /*
+ * Create the locks.
+ */
+ int rc = RTCritSectInit(&pThis->XmitLock);
+ AssertRCReturn(rc, rc);
+
+ /*
+ * Validate the config.
+ */
+ PDMDRV_VALIDATE_CONFIG_RETURN(pDrvIns,
+ "Network"
+ "|Id"
+ "|Trunk"
+ "|TrunkType"
+ "|NetworkMask"
+ "|LowerIP"
+ "|UpperIP",
+ "");
+
+ /** @cfgm{GUID, string}
+ * The unique id of the VMNET interface.
+ */
+ char szUUID[40];
+ rc = pHlp->pfnCFGMQueryString(pCfg, "Id", szUUID, sizeof(szUUID));
+ if (rc == VERR_CFGM_VALUE_NOT_FOUND)
+ uuid_generate_random(pThis->uuid);
+ else if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"Id\" value"));
+ else if (uuid_parse(szUUID, pThis->uuid))
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
+ N_("Configuration error: Invalid \"Id\" value: %s"), szUUID);
+
+ /** @cfgm{TrunkType, uint32_t}
+ * The trunk connection type see INTNETTRUNKTYPE.
+ */
+ uint32_t u32TrunkType;
+ rc = pHlp->pfnCFGMQueryU32(pCfg, "TrunkType", &u32TrunkType);
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"TrunkType\" value"));
+
+ switch ((INTNETTRUNKTYPE)u32TrunkType)
+ {
+ case kIntNetTrunkType_NetAdp:
+ /*
+ * Get the network mask.
+ */
+ rc = pHlp->pfnCFGMQueryString(pCfg, "NetworkMask", pThis->szNetworkMask, sizeof(pThis->szNetworkMask));
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"NetworkMask\" value"));
+
+ /*
+ * Get the network mask.
+ */
+ rc = pHlp->pfnCFGMQueryString(pCfg, "LowerIP", pThis->szLowerIP, sizeof(pThis->szLowerIP));
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"LowerIP\" value"));
+
+ /*
+ * Get the network mask.
+ */
+ rc = pHlp->pfnCFGMQueryString(pCfg, "UpperIP", pThis->szUpperIP, sizeof(pThis->szUpperIP));
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"UpperIP\" value"));
+
+ pThis->uMode = VMNET_HOST_MODE;
+ LogRel(("VMNet: Host network with mask %s (%s to %s)\n", pThis->szNetworkMask, pThis->szLowerIP, pThis->szUpperIP));
+ break;
+
+ case kIntNetTrunkType_NetFlt:
+ /** @cfgm{Trunk, string}
+ * The name of the host interface to use for bridging.
+ */
+ rc = pHlp->pfnCFGMQueryString(pCfg, "Trunk", pThis->szHostInterface, sizeof(pThis->szHostInterface));
+ if (RT_FAILURE(rc))
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Failed to get the \"Trunk\" value"));
+ pThis->uMode = VMNET_BRIDGED_MODE;
+ LogRel(("VMNet: Bridge to %s\n", pThis->szHostInterface));
+ break;
+
+ default:
+ return PDMDRV_SET_ERROR(pDrvIns, rc,
+ N_("Configuration error: Unsupported \"TrunkType\" value"));
+ }
+
+ /*
+ * Check that no-one is attached to us.
+ */
+ AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER,
+ ("Configuration error: Not possible to attach anything to this driver!\n"),
+ VERR_PDM_DRVINS_NO_ATTACH);
+
+ /*
+ * Query the network port interface.
+ */
+ pThis->pIAboveNet = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKDOWN);
+ if (!pThis->pIAboveNet)
+ {
+ AssertMsgFailed(("Configuration error: the above device/driver didn't export the network port interface!\n"));
+ return VERR_PDM_MISSING_INTERFACE_ABOVE;
+ }
+
+ /*
+ * Query the network config interface.
+ */
+ pThis->pIAboveConfig = PDMIBASE_QUERY_INTERFACE(pDrvIns->pUpBase, PDMINETWORKCONFIG);
+ if (!pThis->pIAboveConfig)
+ {
+ AssertMsgFailed(("Configuration error: the above device/driver didn't export the network config interface!\n"));
+ return VERR_PDM_MISSING_INTERFACE_ABOVE;
+ }
+
+ vmnet_return_t status = drvVMNetAttach(pThis);
+ if (status != VMNET_SUCCESS)
+ return PDMDrvHlpVMSetError(pDrvIns, VERR_INVALID_PARAMETER, RT_SRC_POS,
+ N_("Error: vmnet_start_interface returned %d"), status);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Power On notification.
+ *
+ * @param pDrvIns The driver instance.
+ */
+static DECLCALLBACK(void) drvVMNetPowerOn(PPDMDRVINS pDrvIns)
+{
+ LogFlow(("drvVMNetPowerOn\n"));
+ PDRVVMNET pThis = PDMINS_2_DATA(pDrvIns, PDRVVMNET);
+ ASMAtomicXchgSize(&pThis->enmState, VMNETSTATE_RUNNING);
+}
+
+
+/**
+ * Suspend notification.
+ *
+ * @param pDrvIns The driver instance.
+ */
+static DECLCALLBACK(void) drvVMNetSuspend(PPDMDRVINS pDrvIns)
+{
+ LogFlow(("drvVMNetSuspend\n"));
+ PDRVVMNET pThis = PDMINS_2_DATA(pDrvIns, PDRVVMNET);
+ ASMAtomicXchgSize(&pThis->enmState, VMNETSTATE_SUSPENDED);
+}
+
+
+/**
+ * Resume notification.
+ *
+ * @param pDrvIns The driver instance.
+ */
+static DECLCALLBACK(void) drvVMNetResume(PPDMDRVINS pDrvIns)
+{
+ LogFlow(("drvVMNetResume\n"));
+ PDRVVMNET pThis = PDMINS_2_DATA(pDrvIns, PDRVVMNET);
+ ASMAtomicXchgSize(&pThis->enmState, VMNETSTATE_RUNNING);
+}
+
+
+
+/**
+ * Network sniffer filter driver registration record.
+ */
+const PDMDRVREG g_DrvVMNet =
+{
+ /* u32Version */
+ PDM_DRVREG_VERSION,
+ /* szName */
+ "VMNet",
+ /* szRCMod */
+ "",
+ /* szR0Mod */
+ "",
+ /* pszDescription */
+ "VMNET Filter Driver",
+ /* fFlags */
+ PDM_DRVREG_FLAGS_HOST_BITS_DEFAULT,
+ /* fClass. */
+ PDM_DRVREG_CLASS_NETWORK,
+ /* cMaxInstances */
+ UINT32_MAX,
+ /* cbInstance */
+ sizeof(DRVVMNET),
+ /* pfnConstruct */
+ drvVMNetConstruct,
+ /* pfnDestruct */
+ drvVMNetDestruct,
+ /* pfnRelocate */
+ NULL,
+ /* pfnIOCtl */
+ NULL,
+ /* pfnPowerOn */
+ drvVMNetPowerOn,
+ /* pfnReset */
+ NULL,
+ /* pfnSuspend */
+ drvVMNetSuspend,
+ /* pfnResume */
+ drvVMNetResume,
+ /* pfnAttach */
+ NULL,
+ /* pfnDetach */
+ NULL,
+ /* pfnPowerOff */
+ NULL,
+ /* pfnSoftReset */
+ NULL,
+ /* u32EndVersion */
+ PDM_DRVREG_VERSION
+};
+
diff --git a/src/VBox/Devices/Network/Makefile.kup b/src/VBox/Devices/Network/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Network/Makefile.kup
diff --git a/src/VBox/Devices/Network/Pcap.cpp b/src/VBox/Devices/Network/Pcap.cpp
new file mode 100644
index 00000000..e622e768
--- /dev/null
+++ b/src/VBox/Devices/Network/Pcap.cpp
@@ -0,0 +1,267 @@
+/* $Id: Pcap.cpp $ */
+/** @file
+ * Helpers for writing libpcap files.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include "Pcap.h"
+
+#include <iprt/file.h>
+#include <iprt/stream.h>
+#include <iprt/time.h>
+#include <iprt/errcore.h>
+#include <VBox/vmm/pdmnetinline.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+
+/* "libpcap" magic */
+#define PCAP_MAGIC 0xa1b2c3d4
+
+/* "libpcap" file header (minus magic number). */
+struct pcap_hdr
+{
+ uint16_t version_major; /* major version number = 2 */
+ uint16_t version_minor; /* minor version number = 4 */
+ int32_t thiszone; /* GMT to local correction = 0 */
+ uint32_t sigfigs; /* accuracy of timestamps = 0 */
+ uint32_t snaplen; /* max length of captured packets, in octets = 0xffff */
+ uint32_t network; /* data link type = 01 */
+};
+
+/* "libpcap" record header. */
+struct pcaprec_hdr
+{
+ uint32_t ts_sec; /* timestamp seconds */
+ uint32_t ts_usec; /* timestamp microseconds */
+ uint32_t incl_len; /* number of octets of packet saved in file */
+ uint32_t orig_len; /* actual length of packet */
+};
+
+struct pcaprec_hdr_init
+{
+ uint32_t u32Magic;
+ struct pcap_hdr pcap;
+};
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static pcaprec_hdr_init const s_Hdr =
+{
+ PCAP_MAGIC,
+ { 2, 4, 0, 0, 0xffff, 1 },
+};
+
+static const char s_szDummyData[] = { 0, 0, 0, 0 };
+
+/**
+ * Internal helper.
+ */
+static void pcapCalcHeader(struct pcaprec_hdr *pHdr, uint64_t StartNanoTS, size_t cbFrame, size_t cbMax)
+{
+ uint64_t u64TS = RTTimeNanoTS() - StartNanoTS;
+ pHdr->ts_sec = (uint32_t)(u64TS / 1000000000);
+ pHdr->ts_usec = (uint32_t)((u64TS / 1000) % 1000000);
+ pHdr->incl_len = (uint32_t)RT_MIN(cbFrame, cbMax);
+ pHdr->orig_len = (uint32_t)cbFrame;
+}
+
+
+/**
+ * Internal helper.
+ */
+static void pcapUpdateHeader(struct pcaprec_hdr *pHdr, size_t cbFrame, size_t cbMax)
+{
+ pHdr->incl_len = (uint32_t)RT_MIN(cbFrame, cbMax);
+ pHdr->orig_len = (uint32_t)cbFrame;
+}
+
+
+/**
+ * Writes the stream header.
+ *
+ * @returns IPRT status code, @see RTStrmWrite.
+ *
+ * @param pStream The stream handle.
+ * @param StartNanoTS What to subtract from the RTTimeNanoTS output.
+ */
+int PcapStreamHdr(PRTSTREAM pStream, uint64_t StartNanoTS)
+{
+ int rc1 = RTStrmWrite(pStream, &s_Hdr, sizeof(s_Hdr));
+ int rc2 = PcapStreamFrame(pStream, StartNanoTS, s_szDummyData, 60, sizeof(s_szDummyData));
+ return RT_SUCCESS(rc1) ? rc2 : rc1;
+}
+
+
+/**
+ * Writes a frame to a stream.
+ *
+ * @returns IPRT status code, @see RTStrmWrite.
+ *
+ * @param pStream The stream handle.
+ * @param StartNanoTS What to subtract from the RTTimeNanoTS output.
+ * @param pvFrame The start of the frame.
+ * @param cbFrame The size of the frame.
+ * @param cbMax The max number of bytes to include in the file.
+ */
+int PcapStreamFrame(PRTSTREAM pStream, uint64_t StartNanoTS, const void *pvFrame, size_t cbFrame, size_t cbMax)
+{
+ struct pcaprec_hdr Hdr;
+ pcapCalcHeader(&Hdr, StartNanoTS, cbFrame, cbMax);
+ int rc1 = RTStrmWrite(pStream, &Hdr, sizeof(Hdr));
+ int rc2 = RTStrmWrite(pStream, pvFrame, Hdr.incl_len);
+ return RT_SUCCESS(rc1) ? rc2 : rc1;
+}
+
+
+/**
+ * Writes a GSO frame to a stream.
+ *
+ * @returns IPRT status code, @see RTStrmWrite.
+ *
+ * @param pStream The stream handle.
+ * @param StartNanoTS What to subtract from the RTTimeNanoTS output.
+ * @param pGso Pointer to the GSO context.
+ * @param pvFrame The start of the GSO frame.
+ * @param cbFrame The size of the GSO frame.
+ * @param cbSegMax The max number of bytes to include in the file for
+ * each segment.
+ */
+int PcapStreamGsoFrame(PRTSTREAM pStream, uint64_t StartNanoTS, PCPDMNETWORKGSO pGso,
+ const void *pvFrame, size_t cbFrame, size_t cbSegMax)
+{
+ struct pcaprec_hdr Hdr;
+ pcapCalcHeader(&Hdr, StartNanoTS, 0, 0);
+
+ uint8_t const *pbFrame = (uint8_t const *)pvFrame;
+ uint8_t abHdrs[256];
+ uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, cbFrame);
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ uint32_t cbSegPayload, cbHdrs;
+ uint32_t offSegPayload = PDMNetGsoCarveSegment(pGso, pbFrame, cbFrame, iSeg, cSegs, abHdrs, &cbHdrs, &cbSegPayload);
+
+ pcapUpdateHeader(&Hdr, cbHdrs + cbSegPayload, cbSegMax);
+ int rc = RTStrmWrite(pStream, &Hdr, sizeof(Hdr));
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rc = RTStrmWrite(pStream, abHdrs, RT_MIN(Hdr.incl_len, cbHdrs));
+ if (RT_SUCCESS(rc) && Hdr.incl_len > cbHdrs)
+ rc = RTStrmWrite(pStream, pbFrame + offSegPayload, Hdr.incl_len - cbHdrs);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Writes the file header.
+ *
+ * @returns IPRT status code, @see RTFileWrite.
+ *
+ * @param File The file handle.
+ * @param StartNanoTS What to subtract from the RTTimeNanoTS output.
+ */
+int PcapFileHdr(RTFILE File, uint64_t StartNanoTS)
+{
+ int rc1 = RTFileWrite(File, &s_Hdr, sizeof(s_Hdr), NULL);
+ int rc2 = PcapFileFrame(File, StartNanoTS, s_szDummyData, 60, sizeof(s_szDummyData));
+ return RT_SUCCESS(rc1) ? rc2 : rc1;
+}
+
+
+/**
+ * Writes a frame to a file.
+ *
+ * @returns IPRT status code, @see RTFileWrite.
+ *
+ * @param File The file handle.
+ * @param StartNanoTS What to subtract from the RTTimeNanoTS output.
+ * @param pvFrame The start of the frame.
+ * @param cbFrame The size of the frame.
+ * @param cbMax The max number of bytes to include in the file.
+ */
+int PcapFileFrame(RTFILE File, uint64_t StartNanoTS, const void *pvFrame, size_t cbFrame, size_t cbMax)
+{
+ struct pcaprec_hdr Hdr;
+ pcapCalcHeader(&Hdr, StartNanoTS, cbFrame, cbMax);
+ int rc1 = RTFileWrite(File, &Hdr, sizeof(Hdr), NULL);
+ int rc2 = RTFileWrite(File, pvFrame, Hdr.incl_len, NULL);
+ return RT_SUCCESS(rc1) ? rc2 : rc1;
+}
+
+
+/**
+ * Writes a GSO frame to a file.
+ *
+ * @returns IPRT status code, @see RTFileWrite.
+ *
+ * @param File The file handle.
+ * @param StartNanoTS What to subtract from the RTTimeNanoTS output.
+ * @param pGso Pointer to the GSO context.
+ * @param pvFrame The start of the GSO frame.
+ * @param cbFrame The size of the GSO frame.
+ * @param cbSegMax The max number of bytes to include in the file for
+ * each segment.
+ */
+int PcapFileGsoFrame(RTFILE File, uint64_t StartNanoTS, PCPDMNETWORKGSO pGso,
+ const void *pvFrame, size_t cbFrame, size_t cbSegMax)
+{
+ struct pcaprec_hdr Hdr;
+ pcapCalcHeader(&Hdr, StartNanoTS, 0, 0);
+
+ uint8_t const *pbFrame = (uint8_t const *)pvFrame;
+ uint8_t abHdrs[256];
+ uint32_t const cSegs = PDMNetGsoCalcSegmentCount(pGso, cbFrame);
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ uint32_t cbSegPayload, cbHdrs;
+ uint32_t offSegPayload = PDMNetGsoCarveSegment(pGso, pbFrame, cbFrame, iSeg, cSegs, abHdrs, &cbHdrs, &cbSegPayload);
+
+ pcapUpdateHeader(&Hdr, cbHdrs + cbSegPayload, cbSegMax);
+ int rc = RTFileWrite(File, &Hdr, sizeof(Hdr), NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ rc = RTFileWrite(File, abHdrs, RT_MIN(Hdr.incl_len, cbHdrs), NULL);
+ if (RT_SUCCESS(rc) && Hdr.incl_len > cbHdrs)
+ rc = RTFileWrite(File, pbFrame + offSegPayload, Hdr.incl_len - cbHdrs, NULL);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+ return VINF_SUCCESS;
+}
+
diff --git a/src/VBox/Devices/Network/Pcap.h b/src/VBox/Devices/Network/Pcap.h
new file mode 100644
index 00000000..b424c298
--- /dev/null
+++ b/src/VBox/Devices/Network/Pcap.h
@@ -0,0 +1,52 @@
+/* $Id: Pcap.h $ */
+/** @file
+ * Helpers for writing libpcap files.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Network_Pcap_h
+#define VBOX_INCLUDED_SRC_Network_Pcap_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/stream.h>
+#include <VBox/types.h>
+
+RT_C_DECLS_BEGIN
+
+int PcapStreamHdr(PRTSTREAM pStream, uint64_t StartNanoTS);
+int PcapStreamFrame(PRTSTREAM pStream, uint64_t StartNanoTS, const void *pvFrame, size_t cbFrame, size_t cbMax);
+int PcapStreamGsoFrame(PRTSTREAM pStream, uint64_t StartNanoTS, PCPDMNETWORKGSO pGso,
+ const void *pvFrame, size_t cbFrame, size_t cbMax);
+
+int PcapFileHdr(RTFILE File, uint64_t StartNanoTS);
+int PcapFileFrame(RTFILE File, uint64_t StartNanoTS, const void *pvFrame, size_t cbFrame, size_t cbMax);
+int PcapFileGsoFrame(RTFILE File, uint64_t StartNanoTS, PCPDMNETWORKGSO pGso,
+ const void *pvFrame, size_t cbFrame, size_t cbSegMax);
+
+RT_C_DECLS_END
+
+#endif /* !VBOX_INCLUDED_SRC_Network_Pcap_h */
+
diff --git a/src/VBox/Devices/Network/SrvIntNetR0.cpp b/src/VBox/Devices/Network/SrvIntNetR0.cpp
new file mode 100644
index 00000000..1a2b055d
--- /dev/null
+++ b/src/VBox/Devices/Network/SrvIntNetR0.cpp
@@ -0,0 +1,6903 @@
+/* $Id: SrvIntNetR0.cpp $ */
+/** @file
+ * Internal networking - The ring 0 service.
+ *
+ * @remarks No lazy code changes. If you don't understand exactly what you're
+ * doing, get an understanding or forget it.
+ * All changes shall be reviewed by bird before commit. If not around,
+ * email and let Frank and/or Klaus OK the changes before committing.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP LOG_GROUP_SRV_INTNET
+#include <VBox/intnet.h>
+#include <VBox/intnetinline.h>
+#include <VBox/vmm/pdmnetinline.h>
+#include <VBox/sup.h>
+#include <VBox/vmm/pdm.h>
+#include <VBox/log.h>
+
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/handletable.h>
+#include <iprt/mp.h>
+#include <iprt/mem.h>
+#include <iprt/net.h>
+#include <iprt/semaphore.h>
+#include <iprt/spinlock.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+
+/*********************************************************************************************************************************
+* Defined Constants And Macros *
+*********************************************************************************************************************************/
+/** @def INTNET_WITH_DHCP_SNOOPING
+ * Enabled DHCP snooping when in shared-mac-on-the-wire mode. */
+#define INTNET_WITH_DHCP_SNOOPING
+
+/** The maximum number of interface in a network. */
+#define INTNET_MAX_IFS (1023 + 1 + 16)
+
+/** The number of entries to grow the destination tables with. */
+#if 0
+# define INTNET_GROW_DSTTAB_SIZE 16
+#else
+# define INTNET_GROW_DSTTAB_SIZE 1
+#endif
+
+/** The wakeup bit in the INTNETIF::cBusy and INTNETRUNKIF::cBusy counters. */
+#define INTNET_BUSY_WAKEUP_MASK RT_BIT_32(30)
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * MAC address lookup table entry.
+ */
+typedef struct INTNETMACTABENTRY
+{
+ /** The MAC address of this entry. */
+ RTMAC MacAddr;
+ /** Is it is effectively promiscuous mode. */
+ bool fPromiscuousEff;
+ /** Is it promiscuous and should it see unrelated trunk traffic. */
+ bool fPromiscuousSeeTrunk;
+ /** Is it active.
+ * We ignore the entry if this is clear and may end up sending packets addressed
+ * to this interface onto the trunk. The reasoning for this is that this could
+ * be the interface of a VM that just has been teleported to a different host. */
+ bool fActive;
+ /** Pointer to the network interface. */
+ struct INTNETIF *pIf;
+} INTNETMACTABENTRY;
+/** Pointer to a MAC address lookup table entry. */
+typedef INTNETMACTABENTRY *PINTNETMACTABENTRY;
+
+/**
+ * MAC address lookup table.
+ *
+ * @todo Having this in a separate structure didn't work out as well as it
+ * should. Consider merging it into INTNETNETWORK.
+ */
+typedef struct INTNETMACTAB
+{
+ /** The current number of entries. */
+ uint32_t cEntries;
+ /** The number of entries we've allocated space for. */
+ uint32_t cEntriesAllocated;
+ /** Table entries. */
+ PINTNETMACTABENTRY paEntries;
+
+ /** The number of interface entries currently in promicuous mode. */
+ uint32_t cPromiscuousEntries;
+ /** The number of interface entries currently in promicuous mode that
+ * shall not see unrelated trunk traffic. */
+ uint32_t cPromiscuousNoTrunkEntries;
+
+ /** The host MAC address (reported). */
+ RTMAC HostMac;
+ /** The effective host promiscuous setting (reported). */
+ bool fHostPromiscuousEff;
+ /** The real host promiscuous setting (reported). */
+ bool fHostPromiscuousReal;
+ /** Whether the host is active. */
+ bool fHostActive;
+
+ /** Whether the wire is promiscuous (config). */
+ bool fWirePromiscuousEff;
+ /** Whether the wire is promiscuous (config).
+ * (Shadows INTNET_OPEN_FLAGS_TRUNK_WIRE_PROMISC_MODE in
+ * INTNETNETWORK::fFlags.) */
+ bool fWirePromiscuousReal;
+ /** Whether the wire is active. */
+ bool fWireActive;
+
+ /** Pointer to the trunk interface. */
+ struct INTNETTRUNKIF *pTrunk;
+} INTNETMACTAB;
+/** Pointer to a MAC address . */
+typedef INTNETMACTAB *PINTNETMACTAB;
+
+/**
+ * Destination table.
+ */
+typedef struct INTNETDSTTAB
+{
+ /** The trunk destinations. */
+ uint32_t fTrunkDst;
+ /** Pointer to the trunk interface (referenced) if fTrunkDst is non-zero. */
+ struct INTNETTRUNKIF *pTrunk;
+ /** The number of destination interfaces. */
+ uint32_t cIfs;
+ /** The interfaces (referenced). Variable sized array. */
+ struct
+ {
+ /** The destination interface. */
+ struct INTNETIF *pIf;
+ /** Whether to replace the destination MAC address.
+ * This is used when sharing MAC address with the host on the wire(less). */
+ bool fReplaceDstMac;
+ } aIfs[1];
+} INTNETDSTTAB;
+/** Pointer to a destination table. */
+typedef INTNETDSTTAB *PINTNETDSTTAB;
+/** Pointer to a const destination table. */
+typedef INTNETDSTTAB const *PCINTNETDSTTAB;
+
+/**
+ * Address and type.
+ */
+typedef struct INTNETADDR
+{
+ /** The address type. */
+ INTNETADDRTYPE enmType;
+ /** The address. */
+ RTNETADDRU Addr;
+} INTNETADDR;
+/** Pointer to an address. */
+typedef INTNETADDR *PINTNETADDR;
+/** Pointer to a const address. */
+typedef INTNETADDR const *PCINTNETADDR;
+
+
+/**
+ * Address cache for a specific network layer.
+ */
+typedef struct INTNETADDRCACHE
+{
+ /** Pointer to the table of addresses. */
+ uint8_t *pbEntries;
+ /** The number of valid address entries. */
+ uint8_t cEntries;
+ /** The number of allocated address entries. */
+ uint8_t cEntriesAlloc;
+ /** The address size. */
+ uint8_t cbAddress;
+ /** The size of an entry. */
+ uint8_t cbEntry;
+} INTNETADDRCACHE;
+/** Pointer to an address cache. */
+typedef INTNETADDRCACHE *PINTNETADDRCACHE;
+/** Pointer to a const address cache. */
+typedef INTNETADDRCACHE const *PCINTNETADDRCACHE;
+
+
+/**
+ * A network interface.
+ *
+ * Unless explicitly stated, all members are protect by the network semaphore.
+ */
+typedef struct INTNETIF
+{
+ /** The MAC address.
+ * This is shadowed by INTNETMACTABENTRY::MacAddr. */
+ RTMAC MacAddr;
+ /** Set if the INTNET::MacAddr member has been explicitly set. */
+ bool fMacSet;
+ /** Tracks the desired promiscuous setting of the interface. */
+ bool fPromiscuousReal;
+ /** Whether the interface is active or not.
+ * This is shadowed by INTNETMACTABENTRY::fActive. */
+ bool fActive;
+ /** Whether someone has indicated that the end is nigh by means of IntNetR0IfAbortWait. */
+ bool volatile fNoMoreWaits;
+ /** The flags specified when opening this interface. */
+ uint32_t fOpenFlags;
+ /** Number of yields done to try make the interface read pending data.
+ * We will stop yielding when this reaches a threshold assuming that the VM is
+ * paused or that it simply isn't worth all the delay. It is cleared when a
+ * successful send has been done. */
+ uint32_t cYields;
+ /** Pointer to the current exchange buffer (ring-0). */
+ PINTNETBUF pIntBuf;
+ /** Pointer to ring-3 mapping of the current exchange buffer. */
+ R3PTRTYPE(PINTNETBUF) pIntBufR3;
+ /** Pointer to the default exchange buffer for the interface. */
+ PINTNETBUF pIntBufDefault;
+ /** Pointer to ring-3 mapping of the default exchange buffer. */
+ R3PTRTYPE(PINTNETBUF) pIntBufDefaultR3;
+#if !defined(VBOX_WITH_INTNET_SERVICE_IN_R3) || !defined(IN_RING3)
+ /** Event semaphore which a receiver/consumer thread will sleep on while
+ * waiting for data to arrive. */
+ RTSEMEVENT volatile hRecvEvent;
+ /** Number of threads sleeping on the event semaphore. */
+ uint32_t volatile cSleepers;
+#else
+ /** The callback to call when there is something to receive/consume. */
+ PFNINTNETIFRECVAVAIL pfnRecvAvail;
+ /** Opaque user data to pass to the receive avail callback (pfnRecvAvail). */
+ void *pvUserRecvAvail;
+#endif
+ /** The interface handle.
+ * When this is INTNET_HANDLE_INVALID a sleeper which is waking up
+ * should return with the appropriate error condition. */
+ INTNETIFHANDLE volatile hIf;
+ /** The native handle of the destructor thread. This is NIL_RTNATIVETHREAD when
+ * the object is valid and set when intnetR0IfDestruct is in progress. This is
+ * used to cover an unlikely (impossible?) race between SUPDRVSESSION cleanup
+ * and lingering threads waiting for recv or similar. */
+ RTNATIVETHREAD volatile hDestructorThread;
+ /** Pointer to the network this interface is connected to.
+ * This is protected by the INTNET::hMtxCreateOpenDestroy. */
+ struct INTNETNETWORK *pNetwork;
+ /** The session this interface is associated with. */
+ PSUPDRVSESSION pSession;
+ /** The SUPR0 object id. */
+ void *pvObj;
+ /** The network layer address cache. (Indexed by type, 0 entry isn't used.)
+ * This is protected by the address spinlock of the network. */
+ INTNETADDRCACHE aAddrCache[kIntNetAddrType_End];
+ /** Spinlock protecting the input (producer) side of the receive ring. */
+ RTSPINLOCK hRecvInSpinlock;
+ /** Busy count for tracking destination table references and active sends.
+ * Usually incremented while owning the switch table spinlock. The 30th bit
+ * is used to indicate wakeup. */
+ uint32_t volatile cBusy;
+ /** The preallocated destination table.
+ * This is NULL when it's in use as a precaution against unserialized
+ * transmitting. This is grown when new interfaces are added to the network. */
+ PINTNETDSTTAB volatile pDstTab;
+ /** Pointer to the trunk's per interface data. Can be NULL. */
+ void *pvIfData;
+ /** Header buffer for when we're carving GSO frames. */
+ uint8_t abGsoHdrs[256];
+} INTNETIF;
+/** Pointer to an internal network interface. */
+typedef INTNETIF *PINTNETIF;
+
+
+/**
+ * A trunk interface.
+ */
+typedef struct INTNETTRUNKIF
+{
+ /** The port interface we present to the component. */
+ INTNETTRUNKSWPORT SwitchPort;
+ /** The port interface we get from the component. */
+ PINTNETTRUNKIFPORT pIfPort;
+ /** Pointer to the network we're connect to.
+ * This may be NULL if we're orphaned? */
+ struct INTNETNETWORK *pNetwork;
+ /** The current MAC address for the interface. (reported)
+ * Updated while owning the switch table spinlock. */
+ RTMAC MacAddr;
+ /** Whether to supply physical addresses with the outbound SGs. (reported) */
+ bool fPhysSG;
+ /** Explicit alignment. */
+ bool fUnused;
+ /** Busy count for tracking destination table references and active sends.
+ * Usually incremented while owning the switch table spinlock. The 30th bit
+ * is used to indicate wakeup. */
+ uint32_t volatile cBusy;
+ /** Mask of destinations that pfnXmit cope with disabled preemption for. */
+ uint32_t fNoPreemptDsts;
+ /** The GSO capabilities of the wire destination. (reported) */
+ uint32_t fWireGsoCapabilites;
+ /** The GSO capabilities of the host destination. (reported)
+ * This is as bit map where each bit represents the GSO type with the same
+ * number. */
+ uint32_t fHostGsoCapabilites;
+ /** The destination table spinlock, interrupt safe.
+ * Protects apTaskDstTabs and apIntDstTabs. */
+ RTSPINLOCK hDstTabSpinlock;
+ /** The number of entries in apIntDstTabs. */
+ uint32_t cIntDstTabs;
+ /** The task time destination tables.
+ * @remarks intnetR0NetworkEnsureTabSpace and others ASSUMES this immediately
+ * precedes apIntDstTabs so that these two tables can be used as one
+ * contiguous one. */
+ PINTNETDSTTAB apTaskDstTabs[2];
+ /** The interrupt / disabled-preemption time destination tables.
+ * This is a variable sized array. */
+ PINTNETDSTTAB apIntDstTabs[1];
+} INTNETTRUNKIF;
+/** Pointer to a trunk interface. */
+typedef INTNETTRUNKIF *PINTNETTRUNKIF;
+
+/** Converts a pointer to INTNETTRUNKIF::SwitchPort to a PINTNETTRUNKIF. */
+#define INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort) ((PINTNETTRUNKIF)(pSwitchPort))
+
+
+/**
+ * Internal representation of a network.
+ */
+typedef struct INTNETNETWORK
+{
+ /** The Next network in the chain.
+ * This is protected by the INTNET::hMtxCreateOpenDestroy. */
+ struct INTNETNETWORK *pNext;
+
+ /** The spinlock protecting MacTab, aAddrBlacklist and INTNETIF::aAddrCache.
+ * Interrupt safe. */
+ RTSPINLOCK hAddrSpinlock;
+ /** MAC address table.
+ * This doubles as interface collection. */
+ INTNETMACTAB MacTab;
+
+ /** The network layer address cache. (Indexed by type, 0 entry isn't used.
+ * Contains host addresses. We don't let guests spoof them. */
+ INTNETADDRCACHE aAddrBlacklist[kIntNetAddrType_End];
+
+ /** Wait for an interface to stop being busy so it can be removed or have its
+ * destination table replaced. We have to wait upon this while owning the
+ * network mutex. Will only ever have one waiter because of the big mutex. */
+ RTSEMEVENT hEvtBusyIf;
+ /** Pointer to the instance data. */
+ struct INTNET *pIntNet;
+ /** The SUPR0 object id. */
+ void *pvObj;
+ /** The trunk reconnection system thread. The thread gets started at trunk
+ * disconnection. It tries to reconnect the trunk to the bridged filter instance.
+ * The thread erases this handle right before it terminates.
+ */
+ RTTHREAD hTrunkReconnectThread;
+ /** Trunk reconnection thread termination flag. */
+ bool volatile fTerminateReconnectThread;
+ /** Pointer to the temporary buffer that is used when snooping fragmented packets.
+ * This is allocated after this structure if we're sharing the MAC address with
+ * the host. The buffer is INTNETNETWORK_TMP_SIZE big and aligned on a 64-byte boundary. */
+ uint8_t *pbTmp;
+ /** Network creation flags (INTNET_OPEN_FLAGS_*). */
+ uint32_t fFlags;
+ /** Any restrictive policies required as a minimum by some interface.
+ * (INTNET_OPEN_FLAGS_REQUIRE_AS_RESTRICTIVE_POLICIES) */
+ uint32_t fMinFlags;
+ /** The number of active interfaces (excluding the trunk). */
+ uint32_t cActiveIFs;
+ /** The length of the network name. */
+ uint8_t cchName;
+ /** The network name. */
+ char szName[INTNET_MAX_NETWORK_NAME];
+ /** The trunk type. */
+ INTNETTRUNKTYPE enmTrunkType;
+ /** The trunk name. */
+ char szTrunk[INTNET_MAX_TRUNK_NAME];
+} INTNETNETWORK;
+/** Pointer to an internal network. */
+typedef INTNETNETWORK *PINTNETNETWORK;
+/** Pointer to a const internal network. */
+typedef const INTNETNETWORK *PCINTNETNETWORK;
+
+/** The size of the buffer INTNETNETWORK::pbTmp points at. */
+#define INTNETNETWORK_TMP_SIZE 2048
+
+
+/**
+ * Internal networking instance.
+ */
+typedef struct INTNET
+{
+ /** Magic number (INTNET_MAGIC). */
+ uint32_t volatile u32Magic;
+ /** Mutex protecting the creation, opening and destruction of both networks and
+ * interfaces. (This means all operations affecting the pNetworks list.) */
+ RTSEMMUTEX hMtxCreateOpenDestroy;
+ /** List of networks. Protected by INTNET::Spinlock. */
+ PINTNETNETWORK volatile pNetworks;
+ /** Handle table for the interfaces. */
+ RTHANDLETABLE hHtIfs;
+} INTNET;
+/** Pointer to an internal network ring-0 instance. */
+typedef struct INTNET *PINTNET;
+
+/** Magic number for the internal network instance data (Hayao Miyazaki). */
+#define INTNET_MAGIC UINT32_C(0x19410105)
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** Pointer to the internal network instance data. */
+static PINTNET volatile g_pIntNet = NULL;
+
+static const struct INTNETOPENNETWORKFLAGS
+{
+ uint32_t fRestrictive; /**< The restrictive flag (deny/disabled). */
+ uint32_t fRelaxed; /**< The relaxed flag (allow/enabled). */
+ uint32_t fFixed; /**< The config-fixed flag. */
+ uint32_t fPair; /**< The pair of restrictive and relaxed flags. */
+}
+/** Open network policy flags relating to the network. */
+g_afIntNetOpenNetworkNetFlags[] =
+{
+ { INTNET_OPEN_FLAGS_ACCESS_RESTRICTED, INTNET_OPEN_FLAGS_ACCESS_PUBLIC, INTNET_OPEN_FLAGS_ACCESS_FIXED, INTNET_OPEN_FLAGS_ACCESS_RESTRICTED | INTNET_OPEN_FLAGS_ACCESS_PUBLIC },
+ { INTNET_OPEN_FLAGS_PROMISC_DENY_CLIENTS, INTNET_OPEN_FLAGS_PROMISC_ALLOW_CLIENTS, INTNET_OPEN_FLAGS_PROMISC_FIXED, INTNET_OPEN_FLAGS_PROMISC_DENY_CLIENTS | INTNET_OPEN_FLAGS_PROMISC_ALLOW_CLIENTS },
+ { INTNET_OPEN_FLAGS_PROMISC_DENY_TRUNK_HOST, INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_HOST, INTNET_OPEN_FLAGS_PROMISC_FIXED, INTNET_OPEN_FLAGS_PROMISC_DENY_TRUNK_HOST | INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_HOST },
+ { INTNET_OPEN_FLAGS_PROMISC_DENY_TRUNK_WIRE, INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_WIRE, INTNET_OPEN_FLAGS_PROMISC_FIXED, INTNET_OPEN_FLAGS_PROMISC_DENY_TRUNK_WIRE | INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_WIRE },
+ { INTNET_OPEN_FLAGS_TRUNK_HOST_DISABLED, INTNET_OPEN_FLAGS_TRUNK_HOST_ENABLED, INTNET_OPEN_FLAGS_TRUNK_FIXED, INTNET_OPEN_FLAGS_TRUNK_HOST_DISABLED | INTNET_OPEN_FLAGS_TRUNK_HOST_ENABLED },
+ { INTNET_OPEN_FLAGS_TRUNK_HOST_CHASTE_MODE, INTNET_OPEN_FLAGS_TRUNK_HOST_PROMISC_MODE, INTNET_OPEN_FLAGS_TRUNK_FIXED, INTNET_OPEN_FLAGS_TRUNK_HOST_CHASTE_MODE | INTNET_OPEN_FLAGS_TRUNK_HOST_PROMISC_MODE },
+ { INTNET_OPEN_FLAGS_TRUNK_WIRE_DISABLED, INTNET_OPEN_FLAGS_TRUNK_WIRE_ENABLED, INTNET_OPEN_FLAGS_TRUNK_FIXED, INTNET_OPEN_FLAGS_TRUNK_WIRE_DISABLED | INTNET_OPEN_FLAGS_TRUNK_WIRE_ENABLED },
+ { INTNET_OPEN_FLAGS_TRUNK_WIRE_CHASTE_MODE, INTNET_OPEN_FLAGS_TRUNK_WIRE_PROMISC_MODE, INTNET_OPEN_FLAGS_TRUNK_FIXED, INTNET_OPEN_FLAGS_TRUNK_WIRE_CHASTE_MODE | INTNET_OPEN_FLAGS_TRUNK_WIRE_PROMISC_MODE },
+},
+/** Open network policy flags relating to the new interface. */
+g_afIntNetOpenNetworkIfFlags[] =
+{
+ { INTNET_OPEN_FLAGS_IF_PROMISC_DENY, INTNET_OPEN_FLAGS_IF_PROMISC_ALLOW, INTNET_OPEN_FLAGS_IF_FIXED, INTNET_OPEN_FLAGS_IF_PROMISC_DENY | INTNET_OPEN_FLAGS_IF_PROMISC_ALLOW },
+ { INTNET_OPEN_FLAGS_IF_PROMISC_NO_TRUNK, INTNET_OPEN_FLAGS_IF_PROMISC_SEE_TRUNK, INTNET_OPEN_FLAGS_IF_FIXED, INTNET_OPEN_FLAGS_IF_PROMISC_NO_TRUNK | INTNET_OPEN_FLAGS_IF_PROMISC_SEE_TRUNK },
+};
+
+
+/*********************************************************************************************************************************
+* Forward Declarations *
+*********************************************************************************************************************************/
+static void intnetR0TrunkIfDestroy(PINTNETTRUNKIF pThis, PINTNETNETWORK pNetwork);
+
+
+/**
+ * Checks if a pointer belongs to the list of known networks without
+ * accessing memory it points to.
+ *
+ * @returns true, if such network is in the list.
+ * @param pIntNet The pointer to the internal network instance (global).
+ * @param pNetwork The pointer that must be validated.
+ */
+DECLINLINE(bool) intnetR0NetworkIsValid(PINTNET pIntNet, PINTNETNETWORK pNetwork)
+{
+ for (PINTNETNETWORK pCurr = pIntNet->pNetworks; pCurr; pCurr = pCurr->pNext)
+ if (pCurr == pNetwork)
+ return true;
+ return false;
+}
+
+
+/**
+ * Worker for intnetR0SgWritePart that deals with the case where the
+ * request doesn't fit into the first segment.
+ *
+ * @returns true, unless the request or SG invalid.
+ * @param pSG The SG list to write to.
+ * @param off Where to start writing (offset into the SG).
+ * @param cb How much to write.
+ * @param pvBuf The buffer to containing the bits to write.
+ */
+static bool intnetR0SgWritePartSlow(PCINTNETSG pSG, uint32_t off, uint32_t cb, void const *pvBuf)
+{
+ if (RT_UNLIKELY(off + cb > pSG->cbTotal))
+ return false;
+
+ /*
+ * Skip ahead to the segment where off starts.
+ */
+ unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
+ unsigned iSeg = 0;
+ while (off > pSG->aSegs[iSeg].cb)
+ {
+ off -= pSG->aSegs[iSeg++].cb;
+ AssertReturn(iSeg < cSegs, false);
+ }
+
+ /*
+ * Copy the data, hoping that it's all from one segment...
+ */
+ uint32_t cbCanCopy = pSG->aSegs[iSeg].cb - off;
+ if (cbCanCopy >= cb)
+ memcpy((uint8_t *)pSG->aSegs[iSeg].pv + off, pvBuf, cb);
+ else
+ {
+ /* copy the portion in the current segment. */
+ memcpy((uint8_t *)pSG->aSegs[iSeg].pv + off, pvBuf, cbCanCopy);
+ cb -= cbCanCopy;
+
+ /* copy the portions in the other segments. */
+ do
+ {
+ pvBuf = (uint8_t const *)pvBuf + cbCanCopy;
+ iSeg++;
+ AssertReturn(iSeg < cSegs, false);
+
+ cbCanCopy = RT_MIN(cb, pSG->aSegs[iSeg].cb);
+ memcpy(pSG->aSegs[iSeg].pv, pvBuf, cbCanCopy);
+
+ cb -= cbCanCopy;
+ } while (cb > 0);
+ }
+
+ return true;
+}
+
+
+/**
+ * Writes to a part of an SG.
+ *
+ * @returns true on success, false on failure (out of bounds).
+ * @param pSG The SG list to write to.
+ * @param off Where to start writing (offset into the SG).
+ * @param cb How much to write.
+ * @param pvBuf The buffer to containing the bits to write.
+ */
+DECLINLINE(bool) intnetR0SgWritePart(PCINTNETSG pSG, uint32_t off, uint32_t cb, void const *pvBuf)
+{
+ Assert(off + cb > off);
+
+ /* The optimized case. */
+ if (RT_LIKELY( pSG->cSegsUsed == 1
+ || pSG->aSegs[0].cb >= off + cb))
+ {
+ Assert(pSG->cbTotal == pSG->aSegs[0].cb);
+ memcpy((uint8_t *)pSG->aSegs[0].pv + off, pvBuf, cb);
+ return true;
+ }
+ return intnetR0SgWritePartSlow(pSG, off, cb, pvBuf);
+}
+
+
+/**
+ * Reads a byte from a SG list.
+ *
+ * @returns The byte on success. 0xff on failure.
+ * @param pSG The SG list to read.
+ * @param off The offset (into the SG) off the byte.
+ */
+DECLINLINE(uint8_t) intnetR0SgReadByte(PCINTNETSG pSG, uint32_t off)
+{
+ if (RT_LIKELY(pSG->aSegs[0].cb > off))
+ return ((uint8_t const *)pSG->aSegs[0].pv)[off];
+
+ off -= pSG->aSegs[0].cb;
+ unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
+ for (unsigned iSeg = 1; iSeg < cSegs; iSeg++)
+ {
+ if (pSG->aSegs[iSeg].cb > off)
+ return ((uint8_t const *)pSG->aSegs[iSeg].pv)[off];
+ off -= pSG->aSegs[iSeg].cb;
+ }
+ return false;
+}
+
+
+/**
+ * Worker for intnetR0SgReadPart that deals with the case where the
+ * requested data isn't in the first segment.
+ *
+ * @returns true, unless the SG is invalid.
+ * @param pSG The SG list to read.
+ * @param off Where to start reading (offset into the SG).
+ * @param cb How much to read.
+ * @param pvBuf The buffer to read into.
+ */
+static bool intnetR0SgReadPartSlow(PCINTNETSG pSG, uint32_t off, uint32_t cb, void *pvBuf)
+{
+ if (RT_UNLIKELY(off + cb > pSG->cbTotal))
+ return false;
+
+ /*
+ * Skip ahead to the segment where off starts.
+ */
+ unsigned const cSegs = pSG->cSegsUsed; Assert(cSegs == pSG->cSegsUsed);
+ unsigned iSeg = 0;
+ while (off > pSG->aSegs[iSeg].cb)
+ {
+ off -= pSG->aSegs[iSeg++].cb;
+ AssertReturn(iSeg < cSegs, false);
+ }
+
+ /*
+ * Copy the data, hoping that it's all from one segment...
+ */
+ uint32_t cbCanCopy = pSG->aSegs[iSeg].cb - off;
+ if (cbCanCopy >= cb)
+ memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv + off, cb);
+ else
+ {
+ /* copy the portion in the current segment. */
+ memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv + off, cbCanCopy);
+ cb -= cbCanCopy;
+
+ /* copy the portions in the other segments. */
+ do
+ {
+ pvBuf = (uint8_t *)pvBuf + cbCanCopy;
+ iSeg++;
+ AssertReturn(iSeg < cSegs, false);
+
+ cbCanCopy = RT_MIN(cb, pSG->aSegs[iSeg].cb);
+ memcpy(pvBuf, (uint8_t const *)pSG->aSegs[iSeg].pv, cbCanCopy);
+
+ cb -= cbCanCopy;
+ } while (cb > 0);
+ }
+
+ return true;
+}
+
+
+/**
+ * Reads a part of an SG into a buffer.
+ *
+ * @returns true on success, false on failure (out of bounds).
+ * @param pSG The SG list to read.
+ * @param off Where to start reading (offset into the SG).
+ * @param cb How much to read.
+ * @param pvBuf The buffer to read into.
+ */
+DECLINLINE(bool) intnetR0SgReadPart(PCINTNETSG pSG, uint32_t off, uint32_t cb, void *pvBuf)
+{
+ Assert(off + cb > off);
+
+ /* The optimized case. */
+ if (RT_LIKELY(pSG->aSegs[0].cb >= off + cb))
+ {
+ AssertMsg(pSG->cbTotal >= pSG->aSegs[0].cb, ("%#x vs %#x\n", pSG->cbTotal, pSG->aSegs[0].cb));
+ memcpy(pvBuf, (uint8_t const *)pSG->aSegs[0].pv + off, cb);
+ return true;
+ }
+ return intnetR0SgReadPartSlow(pSG, off, cb, pvBuf);
+}
+
+
+/**
+ * Wait for a busy counter to reach zero.
+ *
+ * @param pNetwork The network.
+ * @param pcBusy The busy counter.
+ */
+static void intnetR0BusyWait(PINTNETNETWORK pNetwork, uint32_t volatile *pcBusy)
+{
+ if (ASMAtomicReadU32(pcBusy) == 0)
+ return;
+
+ /*
+ * We have to be a bit cautious here so we don't destroy the network or the
+ * semaphore before intnetR0BusyDec has signalled us.
+ */
+
+ /* Reset the semaphore and flip the wakeup bit. */
+ RTSemEventWait(pNetwork->hEvtBusyIf, 0); /* clear it */
+ uint32_t cCurBusy = ASMAtomicReadU32(pcBusy);
+ do
+ {
+ if (cCurBusy == 0)
+ return;
+ AssertMsg(!(cCurBusy & INTNET_BUSY_WAKEUP_MASK), ("%#x\n", cCurBusy));
+ AssertMsg((cCurBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cCurBusy));
+ } while (!ASMAtomicCmpXchgExU32(pcBusy, cCurBusy | INTNET_BUSY_WAKEUP_MASK, cCurBusy, &cCurBusy));
+
+ /* Wait for the count to reach zero. */
+ do
+ {
+ int rc2 = RTSemEventWait(pNetwork->hEvtBusyIf, 30000); NOREF(rc2);
+ //AssertMsg(RT_SUCCESS(rc2), ("rc=%Rrc *pcBusy=%#x (%#x)\n", rc2, ASMAtomicReadU32(pcBusy), cCurBusy ));
+ cCurBusy = ASMAtomicReadU32(pcBusy);
+ AssertMsg((cCurBusy & INTNET_BUSY_WAKEUP_MASK), ("%#x\n", cCurBusy));
+ AssertMsg((cCurBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cCurBusy));
+ } while ( cCurBusy != INTNET_BUSY_WAKEUP_MASK
+ || !ASMAtomicCmpXchgU32(pcBusy, 0, INTNET_BUSY_WAKEUP_MASK));
+}
+
+
+/**
+ * Decrements the busy counter and maybe wakes up any threads waiting for it to
+ * reach zero.
+ *
+ * @param pNetwork The network.
+ * @param pcBusy The busy counter.
+ */
+DECLINLINE(void) intnetR0BusyDec(PINTNETNETWORK pNetwork, uint32_t volatile *pcBusy)
+{
+ uint32_t cNewBusy = ASMAtomicDecU32(pcBusy);
+ if (RT_UNLIKELY( cNewBusy == INTNET_BUSY_WAKEUP_MASK
+ && pNetwork))
+ RTSemEventSignal(pNetwork->hEvtBusyIf);
+ AssertMsg((cNewBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cNewBusy));
+}
+
+
+/**
+ * Increments the busy count of the specified interface.
+ *
+ * The caller must own the MAC address table spinlock.
+ *
+ * @param pIf The interface.
+ */
+DECLINLINE(void) intnetR0BusyDecIf(PINTNETIF pIf)
+{
+ intnetR0BusyDec(pIf->pNetwork, &pIf->cBusy);
+}
+
+
+/**
+ * Increments the busy count of the specified interface.
+ *
+ * The caller must own the MAC address table spinlock or an explicity reference.
+ *
+ * @param pTrunk The trunk.
+ */
+DECLINLINE(void) intnetR0BusyDecTrunk(PINTNETTRUNKIF pTrunk)
+{
+ if (pTrunk)
+ intnetR0BusyDec(pTrunk->pNetwork, &pTrunk->cBusy);
+}
+
+
+/**
+ * Increments the busy count of the specified interface.
+ *
+ * The caller must own the MAC address table spinlock or an explicity reference.
+ *
+ * @param pIf The interface.
+ */
+DECLINLINE(void) intnetR0BusyIncIf(PINTNETIF pIf)
+{
+ uint32_t cNewBusy = ASMAtomicIncU32(&pIf->cBusy);
+ AssertMsg((cNewBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cNewBusy));
+ NOREF(cNewBusy);
+}
+
+
+/**
+ * Increments the busy count of the specified interface.
+ *
+ * The caller must own the MAC address table spinlock or an explicity reference.
+ *
+ * @param pTrunk The trunk.
+ */
+DECLINLINE(void) intnetR0BusyIncTrunk(PINTNETTRUNKIF pTrunk)
+{
+ if (!pTrunk) return;
+ uint32_t cNewBusy = ASMAtomicIncU32(&pTrunk->cBusy);
+ AssertMsg((cNewBusy & ~INTNET_BUSY_WAKEUP_MASK) < INTNET_MAX_IFS * 3, ("%#x\n", cNewBusy));
+ NOREF(cNewBusy);
+}
+
+
+/**
+ * Retain an interface.
+ *
+ * @returns VBox status code, can assume success in most situations.
+ * @param pIf The interface instance.
+ * @param pSession The current session.
+ */
+DECLINLINE(int) intnetR0IfRetain(PINTNETIF pIf, PSUPDRVSESSION pSession)
+{
+ Assert(pIf->hDestructorThread == NIL_RTNATIVETHREAD);
+
+ int rc = SUPR0ObjAddRefEx(pIf->pvObj, pSession, true /* fNoBlocking */);
+ AssertRCReturn(rc, rc);
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Release an interface previously retained by intnetR0IfRetain or
+ * by handle lookup/freeing.
+ *
+ * @returns true if destroyed, false if not.
+ * @param pIf The interface instance.
+ * @param pSession The current session.
+ */
+DECLINLINE(bool) intnetR0IfRelease(PINTNETIF pIf, PSUPDRVSESSION pSession)
+{
+ Assert(pIf->hDestructorThread == NIL_RTNATIVETHREAD);
+
+ int rc = SUPR0ObjRelease(pIf->pvObj, pSession);
+ AssertRC(rc);
+
+ return rc == VINF_OBJECT_DESTROYED;
+}
+
+
+/**
+ * RTHandleCreateEx callback that retains an object in the
+ * handle table before returning it.
+ *
+ * (Avoids racing the freeing of the handle.)
+ *
+ * @returns VBox status code.
+ * @param hHandleTable The handle table (ignored).
+ * @param pvObj The object (INTNETIF).
+ * @param pvCtx The context (SUPDRVSESSION).
+ * @param pvUser The user context (ignored).
+ */
+static DECLCALLBACK(int) intnetR0IfRetainHandle(RTHANDLETABLE hHandleTable, void *pvObj, void *pvCtx, void *pvUser)
+{
+ NOREF(pvUser);
+ NOREF(hHandleTable);
+
+ PINTNETIF pIf = (PINTNETIF)pvObj;
+ RTNATIVETHREAD hDtorThrd;
+ ASMAtomicUoReadHandle(&pIf->hDestructorThread, &hDtorThrd);
+ if (hDtorThrd == NIL_RTNATIVETHREAD)
+ return intnetR0IfRetain(pIf, (PSUPDRVSESSION)pvCtx);
+
+ /* Allow intnetR0IfDestruct to call RTHandleTableFreeWithCtx to free
+ the handle, but not even think about retaining a referenceas we don't
+ want to confuse SUPDrv and risk having the destructor called twice. */
+ if (hDtorThrd == RTThreadNativeSelf())
+ return VINF_SUCCESS;
+
+ return VERR_SEM_DESTROYED;
+}
+
+
+
+/**
+ * Checks if the interface has a usable MAC address or not.
+ *
+ * @returns true if MacAddr is usable, false if not.
+ * @param pIf The interface.
+ */
+DECL_FORCE_INLINE(bool) intnetR0IfHasMacAddr(PINTNETIF pIf)
+{
+ return pIf->fMacSet || !(pIf->MacAddr.au8[0] & 1);
+}
+
+
+/**
+ * Locates the MAC address table entry for the given interface.
+ *
+ * The caller holds the MAC address table spinlock, obviously.
+ *
+ * @returns Pointer to the entry on if found, NULL if not.
+ * @param pNetwork The network.
+ * @param pIf The interface.
+ */
+DECLINLINE(PINTNETMACTABENTRY) intnetR0NetworkFindMacAddrEntry(PINTNETNETWORK pNetwork, PINTNETIF pIf)
+{
+ uint32_t iIf = pNetwork->MacTab.cEntries;
+ while (iIf-- > 0)
+ {
+ if (pNetwork->MacTab.paEntries[iIf].pIf == pIf)
+ return &pNetwork->MacTab.paEntries[iIf];
+ }
+ return NULL;
+}
+
+
+/**
+ * Checks if the IPv6 address is a good interface address.
+ * @returns true/false.
+ * @param addr The address, network endian.
+ */
+DECLINLINE(bool) intnetR0IPv6AddrIsGood(RTNETADDRIPV6 addr)
+{
+ return !( ( addr.QWords.qw0 == 0 && addr.QWords.qw1 == 0) /* :: */
+ || ( (addr.Words.w0 & RT_H2BE_U16(0xff00)) == RT_H2BE_U16(0xff00)) /* multicast */
+ || ( addr.Words.w0 == 0 && addr.Words.w1 == 0
+ && addr.Words.w2 == 0 && addr.Words.w3 == 0
+ && addr.Words.w4 == 0 && addr.Words.w5 == 0
+ && addr.Words.w6 == 0 && addr.Words.w7 == RT_H2BE_U16(0x0001))); /* ::1 */
+}
+
+
+#if 0 /* unused */
+/**
+ * Checks if the IPv4 address is a broadcast address.
+ * @returns true/false.
+ * @param Addr The address, network endian.
+ */
+DECLINLINE(bool) intnetR0IPv4AddrIsBroadcast(RTNETADDRIPV4 Addr)
+{
+ /* Just check for 255.255.255.255 atm. */
+ return Addr.u == UINT32_MAX;
+}
+#endif /* unused */
+
+
+/**
+ * Checks if the IPv4 address is a good interface address.
+ * @returns true/false.
+ * @param Addr The address, network endian.
+ */
+DECLINLINE(bool) intnetR0IPv4AddrIsGood(RTNETADDRIPV4 Addr)
+{
+ /* Usual suspects. */
+ if ( Addr.u == UINT32_MAX /* 255.255.255.255 - broadcast. */
+ || Addr.au8[0] == 0) /* Current network, can be used as source address. */
+ return false;
+
+ /* Unusual suspects. */
+ if (RT_UNLIKELY( Addr.au8[0] == 127 /* Loopback */
+ || (Addr.au8[0] & 0xf0) == 224 /* Multicast */
+ ))
+ return false;
+ return true;
+}
+
+
+/**
+ * Gets the address size of a network layer type.
+ *
+ * @returns size in bytes.
+ * @param enmType The type.
+ */
+DECLINLINE(uint8_t) intnetR0AddrSize(INTNETADDRTYPE enmType)
+{
+ switch (enmType)
+ {
+ case kIntNetAddrType_IPv4: return 4;
+ case kIntNetAddrType_IPv6: return 16;
+ case kIntNetAddrType_IPX: return 4 + 6;
+ default: AssertFailedReturn(0);
+ }
+}
+
+
+/**
+ * Compares two address to see if they are equal, assuming naturally align structures.
+ *
+ * @returns true if equal, false if not.
+ * @param pAddr1 The first address.
+ * @param pAddr2 The second address.
+ * @param cbAddr The address size.
+ */
+DECLINLINE(bool) intnetR0AddrUIsEqualEx(PCRTNETADDRU pAddr1, PCRTNETADDRU pAddr2, uint8_t const cbAddr)
+{
+ switch (cbAddr)
+ {
+ case 4: /* IPv4 */
+ return pAddr1->au32[0] == pAddr2->au32[0];
+ case 16: /* IPv6 */
+ return pAddr1->au64[0] == pAddr2->au64[0]
+ && pAddr1->au64[1] == pAddr2->au64[1];
+ case 10: /* IPX */
+ return pAddr1->au64[0] == pAddr2->au64[0]
+ && pAddr1->au16[4] == pAddr2->au16[4];
+ default:
+ AssertFailedReturn(false);
+ }
+}
+
+
+/**
+ * Worker for intnetR0IfAddrCacheLookup that performs the lookup
+ * in the remaining cache entries after the caller has check the
+ * most likely ones.
+ *
+ * @returns -1 if not found, the index of the cache entry if found.
+ * @param pCache The cache.
+ * @param pAddr The address.
+ * @param cbAddr The address size (optimization).
+ */
+static int intnetR0IfAddrCacheLookupSlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
+{
+ unsigned i = pCache->cEntries - 2;
+ uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
+ while (i >= 1)
+ {
+ if (intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr))
+ return i;
+ pbEntry -= pCache->cbEntry;
+ i--;
+ }
+
+ return -1;
+}
+
+/**
+ * Lookup an address in a cache without any expectations.
+ *
+ * @returns -1 if not found, the index of the cache entry if found.
+ * @param pCache The cache.
+ * @param pAddr The address.
+ * @param cbAddr The address size (optimization).
+ */
+DECLINLINE(int) intnetR0IfAddrCacheLookup(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
+{
+ Assert(pCache->cbAddress == cbAddr);
+
+ /*
+ * The optimized case is when there is one cache entry and
+ * it doesn't match.
+ */
+ unsigned i = pCache->cEntries;
+ if ( i > 0
+ && intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr))
+ return 0;
+ if (i <= 1)
+ return -1;
+
+ /*
+ * Check the last entry.
+ */
+ i--;
+ if (intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr))
+ return i;
+ if (i <= 1)
+ return -1;
+
+ return intnetR0IfAddrCacheLookupSlow(pCache, pAddr, cbAddr);
+}
+
+
+/** Same as intnetR0IfAddrCacheLookup except we expect the address to be present already. */
+DECLINLINE(int) intnetR0IfAddrCacheLookupLikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
+{
+ /** @todo implement this. */
+ return intnetR0IfAddrCacheLookup(pCache, pAddr, cbAddr);
+}
+
+#if 0 /* unused */
+
+/**
+ * Worker for intnetR0IfAddrCacheLookupUnlikely that performs
+ * the lookup in the remaining cache entries after the caller
+ * has check the most likely ones.
+ *
+ * The routine is expecting not to find the address.
+ *
+ * @returns -1 if not found, the index of the cache entry if found.
+ * @param pCache The cache.
+ * @param pAddr The address.
+ * @param cbAddr The address size (optimization).
+ */
+static int intnetR0IfAddrCacheInCacheUnlikelySlow(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
+{
+ /*
+ * Perform a full table lookup.
+ */
+ unsigned i = pCache->cEntries - 2;
+ uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
+ while (i >= 1)
+ {
+ if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr)))
+ return i;
+ pbEntry -= pCache->cbEntry;
+ i--;
+ }
+
+ return -1;
+}
+
+
+/**
+ * Lookup an address in a cache expecting not to find it.
+ *
+ * @returns -1 if not found, the index of the cache entry if found.
+ * @param pCache The cache.
+ * @param pAddr The address.
+ * @param cbAddr The address size (optimization).
+ */
+DECLINLINE(int) intnetR0IfAddrCacheLookupUnlikely(PCINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr)
+{
+ Assert(pCache->cbAddress == cbAddr);
+
+ /*
+ * The optimized case is when there is one cache entry and
+ * it doesn't match.
+ */
+ unsigned i = pCache->cEntries;
+ if (RT_UNLIKELY( i > 0
+ && intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr)))
+ return 0;
+ if (RT_LIKELY(i <= 1))
+ return -1;
+
+ /*
+ * Then check the last entry and return if there are just two cache entries.
+ */
+ i--;
+ if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * i), pAddr, cbAddr)))
+ return i;
+ if (i <= 1)
+ return -1;
+
+ return intnetR0IfAddrCacheInCacheUnlikelySlow(pCache, pAddr, cbAddr);
+}
+
+#endif /* unused */
+
+
+/**
+ * Deletes a specific cache entry.
+ *
+ * Worker for intnetR0NetworkAddrCacheDelete and intnetR0NetworkAddrCacheDeleteMinusIf.
+ *
+ * @param pIf The interface (for logging).
+ * @param pCache The cache.
+ * @param iEntry The entry to delete.
+ * @param pszMsg Log message.
+ */
+static void intnetR0IfAddrCacheDeleteIt(PINTNETIF pIf, PINTNETADDRCACHE pCache, int iEntry, const char *pszMsg)
+{
+ AssertReturnVoid(iEntry < pCache->cEntries);
+ AssertReturnVoid(iEntry >= 0);
+#ifdef LOG_ENABLED
+ INTNETADDRTYPE enmAddrType = (INTNETADDRTYPE)(uintptr_t)(pCache - &pIf->aAddrCache[0]);
+ PCRTNETADDRU pAddr = (PCRTNETADDRU)(pCache->pbEntries + iEntry * pCache->cbEntry);
+ switch (enmAddrType)
+ {
+ case kIntNetAddrType_IPv4:
+ Log(("intnetR0IfAddrCacheDeleteIt: hIf=%#x MAC=%.6Rhxs IPv4 deleted #%d %RTnaipv4 %s\n",
+ pIf->hIf, &pIf->MacAddr, iEntry, pAddr->IPv4, pszMsg));
+ break;
+ case kIntNetAddrType_IPv6:
+ Log(("intnetR0IfAddrCacheDeleteIt: hIf=%#x MAC=%.6Rhxs IPv6 deleted #%d %RTnaipv6 %s\n",
+ pIf->hIf, &pIf->MacAddr, iEntry, &pAddr->IPv6, pszMsg));
+ break;
+ default:
+ Log(("intnetR0IfAddrCacheDeleteIt: hIf=%RX32 MAC=%.6Rhxs type=%d #%d %.*Rhxs %s\n",
+ pIf->hIf, &pIf->MacAddr, enmAddrType, iEntry, pCache->cbAddress, pAddr, pszMsg));
+ break;
+ }
+#else
+ RT_NOREF2(pIf, pszMsg);
+#endif
+
+ pCache->cEntries--;
+ if (iEntry < pCache->cEntries)
+ memmove(pCache->pbEntries + iEntry * pCache->cbEntry,
+ pCache->pbEntries + (iEntry + 1) * pCache->cbEntry,
+ (pCache->cEntries - iEntry) * pCache->cbEntry);
+}
+
+
+/**
+ * Deletes an address from the cache, assuming it isn't actually in the cache.
+ *
+ * May or may not own the spinlock when calling this.
+ *
+ * @param pIf The interface (for logging).
+ * @param pCache The cache.
+ * @param pAddr The address.
+ * @param cbAddr The address size (optimization).
+ */
+DECLINLINE(void) intnetR0IfAddrCacheDelete(PINTNETIF pIf, PINTNETADDRCACHE pCache, PCRTNETADDRU pAddr, uint8_t const cbAddr, const char *pszMsg)
+{
+ int i = intnetR0IfAddrCacheLookup(pCache, pAddr, cbAddr);
+ if (RT_UNLIKELY(i >= 0))
+ intnetR0IfAddrCacheDeleteIt(pIf, pCache, i, pszMsg);
+}
+
+
+/**
+ * Deletes the address from all the interface caches.
+ *
+ * This is used to remove stale entries that has been reassigned to
+ * other machines on the network.
+ *
+ * @param pNetwork The network.
+ * @param pAddr The address.
+ * @param enmType The address type.
+ * @param cbAddr The address size (optimization).
+ * @param pszMsg Log message.
+ */
+DECLINLINE(void) intnetR0NetworkAddrCacheDeleteLocked(PINTNETNETWORK pNetwork,
+ PCRTNETADDRU pAddr, INTNETADDRTYPE enmType,
+ uint8_t const cbAddr,
+ const char *pszMsg)
+{
+ uint32_t iIf = pNetwork->MacTab.cEntries;
+ while (iIf--)
+ {
+ PINTNETIF pIf = pNetwork->MacTab.paEntries[iIf].pIf;
+
+ int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
+ if (RT_UNLIKELY(i >= 0))
+ intnetR0IfAddrCacheDeleteIt(pIf, &pIf->aAddrCache[enmType], i, pszMsg);
+ }
+}
+
+
+/**
+ * Deletes the address from all the interface caches.
+ *
+ * This is used to remove stale entries that has been reassigned to
+ * other machines on the network.
+ *
+ * @param pNetwork The network.
+ * @param pAddr The address.
+ * @param enmType The address type.
+ * @param cbAddr The address size (optimization).
+ * @param pszMsg Log message.
+ */
+DECLINLINE(void) intnetR0NetworkAddrCacheDelete(PINTNETNETWORK pNetwork, PCRTNETADDRU pAddr, INTNETADDRTYPE const enmType,
+ uint8_t const cbAddr, const char *pszMsg)
+{
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ intnetR0NetworkAddrCacheDeleteLocked(pNetwork, pAddr, enmType, cbAddr, pszMsg);
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+}
+
+
+#if 0 /* unused */
+/**
+ * Deletes the address from all the interface caches except the specified one.
+ *
+ * This is used to remove stale entries that has been reassigned to
+ * other machines on the network.
+ *
+ * @param pNetwork The network.
+ * @param pAddr The address.
+ * @param enmType The address type.
+ * @param cbAddr The address size (optimization).
+ * @param pszMsg Log message.
+ */
+DECLINLINE(void) intnetR0NetworkAddrCacheDeleteMinusIf(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, PCRTNETADDRU pAddr,
+ INTNETADDRTYPE const enmType, uint8_t const cbAddr, const char *pszMsg)
+{
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ uint32_t iIf = pNetwork->MacTab.cEntries;
+ while (iIf--)
+ {
+ PINTNETIF pIf = pNetwork->MacTab.paEntries[iIf].pIf;
+ if (pIf != pIfSender)
+ {
+ int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
+ if (RT_UNLIKELY(i >= 0))
+ intnetR0IfAddrCacheDeleteIt(pIf, &pIf->aAddrCache[enmType], i, pszMsg);
+ }
+ }
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+}
+#endif /* unused */
+
+
+/**
+ * Lookup an address on the network, returning the (first) interface having it
+ * in its address cache.
+ *
+ * @returns Pointer to the interface on success, NULL if not found. The caller
+ * must release the interface by calling intnetR0BusyDecIf.
+ * @param pNetwork The network.
+ * @param pAddr The address to lookup.
+ * @param enmType The address type.
+ * @param cbAddr The size of the address.
+ */
+DECLINLINE(PINTNETIF) intnetR0NetworkAddrCacheLookupIf(PINTNETNETWORK pNetwork, PCRTNETADDRU pAddr, INTNETADDRTYPE const enmType, uint8_t const cbAddr)
+{
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ uint32_t iIf = pNetwork->MacTab.cEntries;
+ while (iIf--)
+ {
+ PINTNETIF pIf = pNetwork->MacTab.paEntries[iIf].pIf;
+ int i = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmType], pAddr, cbAddr);
+ if (i >= 0)
+ {
+ intnetR0BusyIncIf(pIf);
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+ return pIf;
+ }
+ }
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+ return NULL;
+}
+
+
+/**
+ * Look up specified address in the network's blacklist.
+ *
+ * @param pNetwork The network.
+ * @param enmType The address type.
+ * @param pAddr The address.
+ */
+static bool intnetR0NetworkBlacklistLookup(PINTNETNETWORK pNetwork,
+ PCRTNETADDRU pAddr, INTNETADDRTYPE enmType)
+{
+ PINTNETADDRCACHE pCache = &pNetwork->aAddrBlacklist[enmType];
+
+ if (RT_UNLIKELY(pCache->cEntriesAlloc == 0))
+ return false;
+
+ const uint8_t cbAddr = pCache->cbAddress;
+ Assert(cbAddr == intnetR0AddrSize(enmType));
+
+ for (unsigned i = 0; i < pCache->cEntries; ++i)
+ {
+ uint8_t *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
+ if (intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr))
+ return true;
+ }
+
+ return false;
+}
+
+
+/**
+ * Deletes specified address from network's blacklist.
+ *
+ * @param pNetwork The network.
+ * @param enmType The address type.
+ * @param pAddr The address.
+ */
+static void intnetR0NetworkBlacklistDelete(PINTNETNETWORK pNetwork,
+ PCRTNETADDRU pAddr, INTNETADDRTYPE enmType)
+{
+ PINTNETADDRCACHE pCache = &pNetwork->aAddrBlacklist[enmType];
+
+ if (RT_UNLIKELY(pCache->cEntriesAlloc == 0))
+ return;
+
+ const uint8_t cbAddr = pCache->cbAddress;
+ Assert(cbAddr == intnetR0AddrSize(enmType));
+
+ for (unsigned i = 0; i < pCache->cEntries; ++i)
+ {
+ uint8_t *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
+ if (!intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr))
+ continue;
+
+ --pCache->cEntries;
+ memmove(pCache->pbEntries + i * pCache->cbEntry,
+ pCache->pbEntries + (i + 1) * pCache->cbEntry,
+ (pCache->cEntries - i) * pCache->cbEntry);
+ return;
+ }
+}
+
+
+/**
+ * Adds specified address from network's blacklist.
+ *
+ * @param pNetwork The network.
+ * @param enmType The address type.
+ * @param pAddr The address.
+ */
+static void intnetR0NetworkBlacklistAdd(PINTNETNETWORK pNetwork,
+ PCRTNETADDRU pAddr, INTNETADDRTYPE enmType)
+{
+ PINTNETADDRCACHE pCache = &pNetwork->aAddrBlacklist[enmType];
+
+ if (RT_UNLIKELY(pCache->cEntriesAlloc == 0))
+ return;
+
+ const uint8_t cbAddr = pCache->cbAddress;
+ Assert(cbAddr == intnetR0AddrSize(enmType));
+
+ /* lookup */
+ for (unsigned i = 0; i < pCache->cEntries; ++i)
+ {
+ uint8_t *pbEntry = pCache->pbEntries + pCache->cbEntry * i;
+ if (RT_UNLIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr)))
+ return; /* already exists */
+ }
+
+ if (pCache->cEntries >= pCache->cEntriesAlloc)
+ {
+ /* shift */
+ memmove(pCache->pbEntries, pCache->pbEntries + pCache->cbEntry,
+ pCache->cbEntry * (pCache->cEntries - 1));
+ --pCache->cEntries;
+ }
+
+ Assert(pCache->cEntries < pCache->cEntriesAlloc);
+
+ /* push */
+ uint8_t *pbEntry = pCache->pbEntries + pCache->cEntries * pCache->cbEntry;
+ memcpy(pbEntry, pAddr, cbAddr);
+ memset(pbEntry + pCache->cbAddress, '\0', pCache->cbEntry - cbAddr);
+ ++pCache->cEntries;
+
+ Assert(pCache->cEntries <= pCache->cEntriesAlloc);
+}
+
+
+/**
+ * Adds an address to the cache, the caller is responsible for making sure it's
+ * not already in the cache.
+ *
+ * The caller must not
+ *
+ * @param pIf The interface (for logging).
+ * @param pCache The address cache.
+ * @param pAddr The address.
+ * @param pszMsg log message.
+ */
+static void intnetR0IfAddrCacheAddIt(PINTNETIF pIf, INTNETADDRTYPE enmAddrType, PCRTNETADDRU pAddr,
+ const char *pszMsg)
+{
+ PINTNETNETWORK pNetwork = pIf->pNetwork;
+ AssertReturnVoid(pNetwork);
+
+ PINTNETADDRCACHE pCache = &pIf->aAddrCache[enmAddrType];
+
+#if defined(LOG_ENABLED) || defined(VBOX_STRICT)
+ const uint8_t cbAddr = pCache->cbAddress;
+ Assert(cbAddr == intnetR0AddrSize(enmAddrType));
+#endif
+
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ bool fBlacklisted = intnetR0NetworkBlacklistLookup(pNetwork, pAddr, enmAddrType);
+ if (fBlacklisted)
+ {
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+
+#ifdef LOG_ENABLED
+ switch (enmAddrType)
+ {
+ case kIntNetAddrType_IPv4:
+ Log(("%s: spoofing attempt for %RTnaipv4\n",
+ __FUNCTION__, pAddr->IPv4));
+ break;
+ case kIntNetAddrType_IPv6:
+ Log(("%s: spoofing attempt for %RTnaipv6\n",
+ __FUNCTION__, &pAddr->IPv6));
+ break;
+ default:
+ Log(("%s: spoofing attempt for %.*Rhxs (type %d)\n",
+ __FUNCTION__, cbAddr, pAddr, enmAddrType));
+ break;
+ }
+#endif
+ return;
+ }
+
+ if (RT_UNLIKELY(!pCache->cEntriesAlloc))
+ {
+ /* This shouldn't happen*/
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+ return;
+ }
+
+ /* When the table is full, drop the older entry (FIFO). Do proper ageing? */
+ if (pCache->cEntries >= pCache->cEntriesAlloc)
+ {
+ Log(("intnetR0IfAddrCacheAddIt: type=%d replacing %.*Rhxs\n",
+ (int)(uintptr_t)(pCache - &pIf->aAddrCache[0]), pCache->cbAddress, pCache->pbEntries));
+ memmove(pCache->pbEntries, pCache->pbEntries + pCache->cbEntry, pCache->cbEntry * (pCache->cEntries - 1));
+ pCache->cEntries--;
+ Assert(pCache->cEntries < pCache->cEntriesAlloc);
+ }
+
+ /*
+ * Add the new entry to the end of the array.
+ */
+ uint8_t *pbEntry = pCache->pbEntries + pCache->cEntries * pCache->cbEntry;
+ memcpy(pbEntry, pAddr, pCache->cbAddress);
+ memset(pbEntry + pCache->cbAddress, '\0', pCache->cbEntry - pCache->cbAddress);
+
+#ifdef LOG_ENABLED
+ switch (enmAddrType)
+ {
+ case kIntNetAddrType_IPv4:
+ Log(("intnetR0IfAddrCacheAddIt: hIf=%#x MAC=%.6Rhxs IPv4 added #%d %RTnaipv4 %s\n",
+ pIf->hIf, &pIf->MacAddr, pCache->cEntries, pAddr->IPv4, pszMsg));
+ break;
+ case kIntNetAddrType_IPv6:
+ Log(("intnetR0IfAddrCacheAddIt: hIf=%#x MAC=%.6Rhxs IPv6 added #%d %RTnaipv6 %s\n",
+ pIf->hIf, &pIf->MacAddr, pCache->cEntries, &pAddr->IPv6, pszMsg));
+ break;
+ default:
+ Log(("intnetR0IfAddrCacheAddIt: hIf=%#x MAC=%.6Rhxs type=%d added #%d %.*Rhxs %s\n",
+ pIf->hIf, &pIf->MacAddr, enmAddrType, pCache->cEntries, pCache->cbAddress, pAddr, pszMsg));
+ break;
+ }
+#else
+ RT_NOREF1(pszMsg);
+#endif
+ pCache->cEntries++;
+ Assert(pCache->cEntries <= pCache->cEntriesAlloc);
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+}
+
+
+/**
+ * A intnetR0IfAddrCacheAdd worker that performs the rest of the lookup.
+ *
+ * @param pIf The interface (for logging).
+ * @param pCache The address cache.
+ * @param pAddr The address.
+ * @param cbAddr The size of the address (optimization).
+ * @param pszMsg Log message.
+ */
+static void intnetR0IfAddrCacheAddSlow(PINTNETIF pIf, INTNETADDRTYPE enmAddrType, PCRTNETADDRU pAddr,
+ const char *pszMsg)
+{
+ PINTNETADDRCACHE pCache = &pIf->aAddrCache[enmAddrType];
+
+ const uint8_t cbAddr = pCache->cbAddress;
+ Assert(cbAddr == intnetR0AddrSize(enmAddrType));
+
+ /*
+ * Check all but the first and last entries, the caller
+ * has already checked those.
+ */
+ int i = pCache->cEntries - 2;
+ uint8_t const *pbEntry = pCache->pbEntries + pCache->cbEntry;
+ while (i >= 1)
+ {
+ if (RT_LIKELY(intnetR0AddrUIsEqualEx((PCRTNETADDRU)pbEntry, pAddr, cbAddr)))
+ return;
+ pbEntry += pCache->cbEntry;
+ i--;
+ }
+
+ /*
+ * Not found, add it.
+ */
+ intnetR0IfAddrCacheAddIt(pIf, enmAddrType, pAddr, pszMsg);
+}
+
+
+/**
+ * Adds an address to the cache if it's not already there.
+ *
+ * Must not own any spinlocks when calling this function.
+ *
+ * @param pIf The interface (for logging).
+ * @param pCache The address cache.
+ * @param pAddr The address.
+ * @param cbAddr The size of the address (optimization).
+ * @param pszMsg Log message.
+ */
+DECLINLINE(void) intnetR0IfAddrCacheAdd(PINTNETIF pIf, INTNETADDRTYPE enmAddrType, PCRTNETADDRU pAddr,
+ const char *pszMsg)
+{
+ PINTNETADDRCACHE pCache = &pIf->aAddrCache[enmAddrType];
+
+ const uint8_t cbAddr = pCache->cbAddress;
+ Assert(cbAddr == intnetR0AddrSize(enmAddrType));
+
+ /*
+ * The optimized case is when the address the first or last cache entry.
+ */
+ unsigned i = pCache->cEntries;
+ if (RT_LIKELY( i > 0
+ && ( intnetR0AddrUIsEqualEx((PCRTNETADDRU)pCache->pbEntries, pAddr, cbAddr)
+ || (i > 1
+ && intnetR0AddrUIsEqualEx((PCRTNETADDRU)(pCache->pbEntries + pCache->cbEntry * (i-1)), pAddr, cbAddr))) ))
+ return;
+
+ intnetR0IfAddrCacheAddSlow(pIf, enmAddrType, pAddr, pszMsg);
+}
+
+
+/**
+ * Destroys the specified address cache.
+ * @param pCache The address cache.
+ */
+static void intnetR0IfAddrCacheDestroy(PINTNETADDRCACHE pCache)
+{
+ void *pvFree = pCache->pbEntries;
+ pCache->pbEntries = NULL;
+ pCache->cEntries = 0;
+ pCache->cEntriesAlloc = 0;
+ RTMemFree(pvFree);
+}
+
+
+/**
+ * Initialize the address cache for the specified address type.
+ *
+ * The cache storage is preallocated and fixed size so that we can handle
+ * inserts from problematic contexts.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_MEMORY.
+ * @param pCache The cache to initialize.
+ * @param enmAddrType The address type.
+ * @param fEnabled Whether the address cache is enabled or not.
+ */
+static int intnetR0IfAddrCacheInit(PINTNETADDRCACHE pCache, INTNETADDRTYPE enmAddrType, bool fEnabled)
+{
+ pCache->cEntries = 0;
+ pCache->cbAddress = intnetR0AddrSize(enmAddrType);
+ pCache->cbEntry = RT_ALIGN(pCache->cbAddress, 4);
+ if (fEnabled)
+ {
+ pCache->cEntriesAlloc = 32;
+ pCache->pbEntries = (uint8_t *)RTMemAllocZ(pCache->cEntriesAlloc * pCache->cbEntry);
+ if (!pCache->pbEntries)
+ return VERR_NO_MEMORY;
+ }
+ else
+ {
+ pCache->cEntriesAlloc = 0;
+ pCache->pbEntries = NULL;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Is it a multicast or broadcast MAC address?
+ *
+ * @returns true if multicast, false if not.
+ * @param pMacAddr The address to inspect.
+ */
+DECL_FORCE_INLINE(bool) intnetR0IsMacAddrMulticast(PCRTMAC pMacAddr)
+{
+ return !!(pMacAddr->au8[0] & 0x01);
+}
+
+
+/**
+ * Is it a dummy MAC address?
+ *
+ * We use dummy MAC addresses for interfaces which we don't know the MAC
+ * address of because they haven't sent anything (learning) or explicitly set
+ * it.
+ *
+ * @returns true if dummy, false if not.
+ * @param pMacAddr The address to inspect.
+ */
+DECL_FORCE_INLINE(bool) intnetR0IsMacAddrDummy(PCRTMAC pMacAddr)
+{
+ /* The dummy address are broadcast addresses, don't bother check it all. */
+ return pMacAddr->au16[0] == 0xffff;
+}
+
+
+/**
+ * Compares two MAC addresses.
+ *
+ * @returns true if equal, false if not.
+ * @param pDstAddr1 Address 1.
+ * @param pDstAddr2 Address 2.
+ */
+DECL_FORCE_INLINE(bool) intnetR0AreMacAddrsEqual(PCRTMAC pDstAddr1, PCRTMAC pDstAddr2)
+{
+ return pDstAddr1->au16[2] == pDstAddr2->au16[2]
+ && pDstAddr1->au16[1] == pDstAddr2->au16[1]
+ && pDstAddr1->au16[0] == pDstAddr2->au16[0];
+}
+
+
+/**
+ * Switch a unicast frame based on the network layer address (OSI level 3) and
+ * return a destination table.
+ *
+ * @returns INTNETSWDECISION_DROP, INTNETSWDECISION_TRUNK,
+ * INTNETSWDECISION_INTNET or INTNETSWDECISION_BROADCAST (misnomer).
+ * @param pNetwork The network to switch on.
+ * @param pDstMacAddr The destination MAC address.
+ * @param enmL3AddrType The level-3 destination address type.
+ * @param pL3Addr The level-3 destination address.
+ * @param cbL3Addr The size of the level-3 destination address.
+ * @param fSrc The frame source (INTNETTRUNKDIR_WIRE).
+ * @param pDstTab The destination output table.
+ */
+static INTNETSWDECISION intnetR0NetworkSwitchLevel3(PINTNETNETWORK pNetwork, PCRTMAC pDstMacAddr,
+ INTNETADDRTYPE enmL3AddrType, PCRTNETADDRU pL3Addr, uint8_t cbL3Addr,
+ uint32_t fSrc, PINTNETDSTTAB pDstTab)
+{
+ Assert(fSrc == INTNETTRUNKDIR_WIRE);
+
+ /*
+ * Grab the spinlock first and do the switching.
+ */
+ PINTNETMACTAB pTab = &pNetwork->MacTab;
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ pDstTab->fTrunkDst = 0;
+ pDstTab->pTrunk = 0;
+ pDstTab->cIfs = 0;
+
+ /* Find exactly matching or promiscuous interfaces. */
+ uint32_t cExactHits = 0;
+ uint32_t iIfMac = pTab->cEntries;
+ while (iIfMac-- > 0)
+ {
+ if (pTab->paEntries[iIfMac].fActive)
+ {
+ PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
+ bool fExact = intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmL3AddrType], pL3Addr, cbL3Addr) >= 0;
+ if (fExact || pTab->paEntries[iIfMac].fPromiscuousSeeTrunk)
+ {
+ cExactHits += fExact;
+
+ uint32_t iIfDst = pDstTab->cIfs++;
+ pDstTab->aIfs[iIfDst].pIf = pIf;
+ pDstTab->aIfs[iIfDst].fReplaceDstMac = fExact;
+ intnetR0BusyIncIf(pIf);
+
+ if (fExact)
+ pDstMacAddr = &pIf->MacAddr; /* Avoids duplicates being sent to the host. */
+ }
+ }
+ }
+
+ /* Network only promicuous mode ifs should see related trunk traffic. */
+ if ( cExactHits
+ && fSrc
+ && pNetwork->MacTab.cPromiscuousNoTrunkEntries)
+ {
+ iIfMac = pTab->cEntries;
+ while (iIfMac-- > 0)
+ {
+ if ( pTab->paEntries[iIfMac].fActive
+ && pTab->paEntries[iIfMac].fPromiscuousEff
+ && !pTab->paEntries[iIfMac].fPromiscuousSeeTrunk)
+ {
+ PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
+ if (intnetR0IfAddrCacheLookup(&pIf->aAddrCache[enmL3AddrType], pL3Addr, cbL3Addr) < 0)
+ {
+ uint32_t iIfDst = pDstTab->cIfs++;
+ pDstTab->aIfs[iIfDst].pIf = pIf;
+ pDstTab->aIfs[iIfDst].fReplaceDstMac = false;
+ intnetR0BusyIncIf(pIf);
+ }
+ }
+ }
+ }
+
+ /* Does it match the host, or is the host promiscuous? */
+ if (pTab->fHostActive)
+ {
+ bool fExact = intnetR0AreMacAddrsEqual(&pTab->HostMac, pDstMacAddr);
+ if ( fExact
+ || intnetR0IsMacAddrDummy(&pTab->HostMac)
+ || pTab->fHostPromiscuousEff)
+ {
+ cExactHits += fExact;
+ pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
+ }
+ }
+
+ /* Hit the wire if there are no exact matches or if it's in promiscuous mode. */
+ if (pTab->fWireActive && (!cExactHits || pTab->fWirePromiscuousEff))
+ pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
+ pDstTab->fTrunkDst &= ~fSrc;
+ if (pDstTab->fTrunkDst)
+ {
+ PINTNETTRUNKIF pTrunk = pTab->pTrunk;
+ pDstTab->pTrunk = pTrunk;
+ intnetR0BusyIncTrunk(pTrunk);
+ }
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+ return pDstTab->cIfs
+ ? (!pDstTab->fTrunkDst ? INTNETSWDECISION_INTNET : INTNETSWDECISION_BROADCAST)
+ : (!pDstTab->fTrunkDst ? INTNETSWDECISION_DROP : INTNETSWDECISION_TRUNK);
+}
+
+
+/**
+ * Pre-switch a unicast MAC address.
+ *
+ * @returns INTNETSWDECISION_DROP, INTNETSWDECISION_TRUNK,
+ * INTNETSWDECISION_INTNET or INTNETSWDECISION_BROADCAST (misnomer).
+ * @param pNetwork The network to switch on.
+ * @param fSrc The frame source.
+ * @param pSrcAddr The source address of the frame.
+ * @param pDstAddr The destination address of the frame.
+ */
+static INTNETSWDECISION intnetR0NetworkPreSwitchUnicast(PINTNETNETWORK pNetwork, uint32_t fSrc, PCRTMAC pSrcAddr,
+ PCRTMAC pDstAddr)
+{
+ Assert(!intnetR0IsMacAddrMulticast(pDstAddr));
+ Assert(fSrc);
+
+ /*
+ * Grab the spinlock first and do the switching.
+ */
+ INTNETSWDECISION enmSwDecision = INTNETSWDECISION_BROADCAST;
+ PINTNETMACTAB pTab = &pNetwork->MacTab;
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ /* Iterate the internal network interfaces and look for matching source and
+ destination addresses. */
+ uint32_t iIfMac = pTab->cEntries;
+ while (iIfMac-- > 0)
+ {
+ if (pTab->paEntries[iIfMac].fActive)
+ {
+ /* Unknown interface address? */
+ if (intnetR0IsMacAddrDummy(&pTab->paEntries[iIfMac].MacAddr))
+ break;
+
+ /* Paranoia - this shouldn't happen, right? */
+ if ( pSrcAddr
+ && intnetR0AreMacAddrsEqual(&pTab->paEntries[iIfMac].MacAddr, pSrcAddr))
+ break;
+
+ /* Exact match? */
+ if (intnetR0AreMacAddrsEqual(&pTab->paEntries[iIfMac].MacAddr, pDstAddr))
+ {
+ enmSwDecision = pTab->fHostPromiscuousEff && fSrc == INTNETTRUNKDIR_WIRE
+ ? INTNETSWDECISION_BROADCAST
+ : INTNETSWDECISION_INTNET;
+ break;
+ }
+ }
+ }
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+ return enmSwDecision;
+}
+
+
+/**
+ * Switch a unicast MAC address and return a destination table.
+ *
+ * @returns INTNETSWDECISION_DROP, INTNETSWDECISION_TRUNK,
+ * INTNETSWDECISION_INTNET or INTNETSWDECISION_BROADCAST (misnomer).
+ * @param pNetwork The network to switch on.
+ * @param fSrc The frame source.
+ * @param pIfSender The sender interface, NULL if trunk. Used to
+ * prevent sending an echo to the sender.
+ * @param pDstAddr The destination address of the frame.
+ * @param pDstTab The destination output table.
+ */
+static INTNETSWDECISION intnetR0NetworkSwitchUnicast(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETIF pIfSender,
+ PCRTMAC pDstAddr, PINTNETDSTTAB pDstTab)
+{
+ AssertPtr(pDstTab);
+ Assert(!intnetR0IsMacAddrMulticast(pDstAddr));
+
+ /*
+ * Grab the spinlock first and do the switching.
+ */
+ PINTNETMACTAB pTab = &pNetwork->MacTab;
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ pDstTab->fTrunkDst = 0;
+ pDstTab->pTrunk = 0;
+ pDstTab->cIfs = 0;
+
+ /* Find exactly matching or promiscuous interfaces. */
+ uint32_t cExactHits = 0;
+ uint32_t iIfMac = pTab->cEntries;
+ while (iIfMac-- > 0)
+ {
+ if (pTab->paEntries[iIfMac].fActive)
+ {
+ bool fExact = intnetR0AreMacAddrsEqual(&pTab->paEntries[iIfMac].MacAddr, pDstAddr);
+ if ( fExact
+ || intnetR0IsMacAddrDummy(&pTab->paEntries[iIfMac].MacAddr)
+ || ( pTab->paEntries[iIfMac].fPromiscuousSeeTrunk
+ || (!fSrc && pTab->paEntries[iIfMac].fPromiscuousEff) )
+ )
+ {
+ cExactHits += fExact;
+
+ PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
+ if (RT_LIKELY(pIf != pIfSender)) /* paranoia */
+ {
+ uint32_t iIfDst = pDstTab->cIfs++;
+ pDstTab->aIfs[iIfDst].pIf = pIf;
+ pDstTab->aIfs[iIfDst].fReplaceDstMac = false;
+ intnetR0BusyIncIf(pIf);
+ }
+ }
+ }
+ }
+
+ /* Network only promicuous mode ifs should see related trunk traffic. */
+ if ( cExactHits
+ && fSrc
+ && pNetwork->MacTab.cPromiscuousNoTrunkEntries)
+ {
+ iIfMac = pTab->cEntries;
+ while (iIfMac-- > 0)
+ {
+ if ( pTab->paEntries[iIfMac].fPromiscuousEff
+ && !pTab->paEntries[iIfMac].fPromiscuousSeeTrunk
+ && pTab->paEntries[iIfMac].fActive
+ && !intnetR0AreMacAddrsEqual(&pTab->paEntries[iIfMac].MacAddr, pDstAddr)
+ && !intnetR0IsMacAddrDummy(&pTab->paEntries[iIfMac].MacAddr) )
+ {
+ PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
+ uint32_t iIfDst = pDstTab->cIfs++;
+ pDstTab->aIfs[iIfDst].pIf = pIf;
+ pDstTab->aIfs[iIfDst].fReplaceDstMac = false;
+ intnetR0BusyIncIf(pIf);
+ }
+ }
+ }
+
+ /* Does it match the host, or is the host promiscuous? */
+ if ( fSrc != INTNETTRUNKDIR_HOST
+ && pTab->fHostActive)
+ {
+ bool fExact = intnetR0AreMacAddrsEqual(&pTab->HostMac, pDstAddr);
+ if ( fExact
+ || intnetR0IsMacAddrDummy(&pTab->HostMac)
+ || pTab->fHostPromiscuousEff)
+ {
+ cExactHits += fExact;
+ pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
+ }
+ }
+
+ /* Hit the wire if there are no exact matches or if it's in promiscuous mode. */
+ if ( fSrc != INTNETTRUNKDIR_WIRE
+ && pTab->fWireActive
+ && (!cExactHits || pTab->fWirePromiscuousEff)
+ )
+ pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
+
+ /* Grab the trunk if we're sending to it. */
+ if (pDstTab->fTrunkDst)
+ {
+ PINTNETTRUNKIF pTrunk = pTab->pTrunk;
+ pDstTab->pTrunk = pTrunk;
+ intnetR0BusyIncTrunk(pTrunk);
+ }
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+ return pDstTab->cIfs
+ ? (!pDstTab->fTrunkDst ? INTNETSWDECISION_INTNET : INTNETSWDECISION_BROADCAST)
+ : (!pDstTab->fTrunkDst ? INTNETSWDECISION_DROP : INTNETSWDECISION_TRUNK);
+}
+
+
+/**
+ * Create a destination table for a broadcast frame.
+ *
+ * @returns INTNETSWDECISION_BROADCAST.
+ * @param pNetwork The network to switch on.
+ * @param fSrc The frame source.
+ * @param pIfSender The sender interface, NULL if trunk. Used to
+ * prevent sending an echo to the sender.
+ * @param pDstTab The destination output table.
+ */
+static INTNETSWDECISION intnetR0NetworkSwitchBroadcast(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETIF pIfSender,
+ PINTNETDSTTAB pDstTab)
+{
+ AssertPtr(pDstTab);
+
+ /*
+ * Grab the spinlock first and record all active interfaces.
+ */
+ PINTNETMACTAB pTab = &pNetwork->MacTab;
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ pDstTab->fTrunkDst = 0;
+ pDstTab->pTrunk = 0;
+ pDstTab->cIfs = 0;
+
+ /* Regular interfaces. */
+ uint32_t iIfMac = pTab->cEntries;
+ while (iIfMac-- > 0)
+ {
+ if (pTab->paEntries[iIfMac].fActive)
+ {
+ PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
+ if (pIf != pIfSender)
+ {
+ uint32_t iIfDst = pDstTab->cIfs++;
+ pDstTab->aIfs[iIfDst].pIf = pIf;
+ pDstTab->aIfs[iIfDst].fReplaceDstMac = false;
+ intnetR0BusyIncIf(pIf);
+ }
+ }
+ }
+
+ /* The trunk interface. */
+ if (pTab->fHostActive)
+ pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
+ if (pTab->fWireActive)
+ pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
+ pDstTab->fTrunkDst &= ~fSrc;
+ if (pDstTab->fTrunkDst)
+ {
+ PINTNETTRUNKIF pTrunk = pTab->pTrunk;
+ pDstTab->pTrunk = pTrunk;
+ intnetR0BusyIncTrunk(pTrunk);
+ }
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+ return INTNETSWDECISION_BROADCAST;
+}
+
+
+/**
+ * Create a destination table with the trunk and any promiscuous interfaces.
+ *
+ * This is only used in a fallback case of the level-3 switching, so we can
+ * assume the wire as source and skip the sender interface filtering.
+ *
+ * @returns INTNETSWDECISION_DROP, INTNETSWDECISION_TRUNK,
+ * INTNETSWDECISION_INTNET or INTNETSWDECISION_BROADCAST (misnomer).
+ * @param pNetwork The network to switch on.
+ * @param fSrc The frame source.
+ * @param pDstTab The destination output table.
+ */
+static INTNETSWDECISION intnetR0NetworkSwitchTrunkAndPromisc(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETDSTTAB pDstTab)
+{
+ Assert(fSrc == INTNETTRUNKDIR_WIRE);
+
+ /*
+ * Grab the spinlock first and do the switching.
+ */
+ PINTNETMACTAB pTab = &pNetwork->MacTab;
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ pDstTab->fTrunkDst = 0;
+ pDstTab->pTrunk = 0;
+ pDstTab->cIfs = 0;
+
+ /* Find promiscuous interfaces. */
+ uint32_t iIfMac = pTab->cEntries;
+ while (iIfMac-- > 0)
+ {
+ if ( pTab->paEntries[iIfMac].fActive
+ && ( pTab->paEntries[iIfMac].fPromiscuousSeeTrunk
+ || (!fSrc && pTab->paEntries[iIfMac].fPromiscuousEff) )
+ )
+ {
+ PINTNETIF pIf = pTab->paEntries[iIfMac].pIf; AssertPtr(pIf); Assert(pIf->pNetwork == pNetwork);
+ uint32_t iIfDst = pDstTab->cIfs++;
+ pDstTab->aIfs[iIfDst].pIf = pIf;
+ pDstTab->aIfs[iIfDst].fReplaceDstMac = false;
+ intnetR0BusyIncIf(pIf);
+ }
+ }
+
+ /* The trunk interface. */
+ if (pTab->fHostActive)
+ pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
+ if (pTab->fWireActive)
+ pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
+ pDstTab->fTrunkDst &= ~fSrc;
+ if (pDstTab->fTrunkDst)
+ {
+ PINTNETTRUNKIF pTrunk = pTab->pTrunk;
+ pDstTab->pTrunk = pTrunk;
+ intnetR0BusyIncTrunk(pTrunk);
+ }
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+ return !pDstTab->cIfs
+ ? (!pDstTab->fTrunkDst ? INTNETSWDECISION_DROP : INTNETSWDECISION_TRUNK)
+ : (!pDstTab->fTrunkDst ? INTNETSWDECISION_INTNET : INTNETSWDECISION_BROADCAST);
+}
+
+
+/**
+ * Create a destination table for a trunk frame.
+ *
+ * @returns INTNETSWDECISION_BROADCAST.
+ * @param pNetwork The network to switch on.
+ * @param fSrc The frame source.
+ * @param pDstTab The destination output table.
+ */
+static INTNETSWDECISION intnetR0NetworkSwitchTrunk(PINTNETNETWORK pNetwork, uint32_t fSrc, PINTNETDSTTAB pDstTab)
+{
+ AssertPtr(pDstTab);
+
+ /*
+ * Grab the spinlock first and record all active interfaces.
+ */
+ PINTNETMACTAB pTab= &pNetwork->MacTab;
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ pDstTab->fTrunkDst = 0;
+ pDstTab->pTrunk = 0;
+ pDstTab->cIfs = 0;
+
+ /* The trunk interface. */
+ if (pTab->fHostActive)
+ pDstTab->fTrunkDst |= INTNETTRUNKDIR_HOST;
+ if (pTab->fWireActive)
+ pDstTab->fTrunkDst |= INTNETTRUNKDIR_WIRE;
+ pDstTab->fTrunkDst &= ~fSrc;
+ if (pDstTab->fTrunkDst)
+ {
+ PINTNETTRUNKIF pTrunk = pTab->pTrunk;
+ pDstTab->pTrunk = pTrunk;
+ intnetR0BusyIncTrunk(pTrunk);
+ }
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+ return pDstTab->fTrunkDst ? INTNETSWDECISION_TRUNK : INTNETSWDECISION_DROP;
+}
+
+
+/**
+ * Wrapper around RTMemAlloc for allocating a destination table.
+ *
+ * @returns VINF_SUCCESS or VERR_NO_MEMORY.
+ * @param cEntries The size given as an entry count.
+ * @param ppDstTab Where to store the pointer (always).
+ */
+DECLINLINE(int) intnetR0AllocDstTab(uint32_t cEntries, PINTNETDSTTAB *ppDstTab)
+{
+ PINTNETDSTTAB pDstTab;
+ *ppDstTab = pDstTab = (PINTNETDSTTAB)RTMemAlloc(RT_UOFFSETOF_DYN(INTNETDSTTAB, aIfs[cEntries]));
+ if (RT_UNLIKELY(!pDstTab))
+ return VERR_NO_MEMORY;
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Ensures that there is space for another interface in the MAC address lookup
+ * table as well as all the destination tables.
+ *
+ * The caller must own the create/open/destroy mutex.
+ *
+ * @returns VINF_SUCCESS, VERR_NO_MEMORY or VERR_OUT_OF_RANGE.
+ * @param pNetwork The network to operate on.
+ */
+static int intnetR0NetworkEnsureTabSpace(PINTNETNETWORK pNetwork)
+{
+ /*
+ * The cEntries and cEntriesAllocated members are only updated while
+ * owning the big mutex, so we only need the spinlock when doing the
+ * actual table replacing.
+ */
+ PINTNETMACTAB pTab = &pNetwork->MacTab;
+ int rc = VINF_SUCCESS;
+ AssertReturn(pTab->cEntries <= pTab->cEntriesAllocated, VERR_INTERNAL_ERROR_2);
+ if (pTab->cEntries + 1 > pTab->cEntriesAllocated)
+ {
+ uint32_t const cAllocated = pTab->cEntriesAllocated + INTNET_GROW_DSTTAB_SIZE;
+ if (cAllocated <= INTNET_MAX_IFS)
+ {
+ /*
+ * Resize the destination tables first, this can be kind of tedious.
+ */
+ for (uint32_t i = 0; i < pTab->cEntries; i++)
+ {
+ PINTNETIF pIf = pTab->paEntries[i].pIf; AssertPtr(pIf);
+ PINTNETDSTTAB pNew;
+ rc = intnetR0AllocDstTab(cAllocated, &pNew);
+ if (RT_FAILURE(rc))
+ break;
+
+ for (;;)
+ {
+ PINTNETDSTTAB pOld = pIf->pDstTab;
+ if ( pOld
+ && ASMAtomicCmpXchgPtr(&pIf->pDstTab, pNew, pOld))
+ {
+ RTMemFree(pOld);
+ break;
+ }
+ intnetR0BusyWait(pNetwork, &pIf->cBusy);
+ }
+ }
+
+ /*
+ * The trunk.
+ */
+ if ( RT_SUCCESS(rc)
+ && pNetwork->MacTab.pTrunk)
+ {
+ AssertCompileAdjacentMembers(INTNETTRUNKIF, apTaskDstTabs, apIntDstTabs);
+ PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
+ PINTNETDSTTAB * const ppEndDstTab = &pTrunk->apIntDstTabs[pTrunk->cIntDstTabs];
+ for (PINTNETDSTTAB *ppDstTab = &pTrunk->apTaskDstTabs[0];
+ ppDstTab != ppEndDstTab && RT_SUCCESS(rc);
+ ppDstTab++)
+ {
+ PINTNETDSTTAB pNew;
+ rc = intnetR0AllocDstTab(cAllocated, &pNew);
+ if (RT_FAILURE(rc))
+ break;
+
+ for (;;)
+ {
+ RTSpinlockAcquire(pTrunk->hDstTabSpinlock);
+ void *pvOld = *ppDstTab;
+ if (pvOld)
+ *ppDstTab = pNew;
+ RTSpinlockRelease(pTrunk->hDstTabSpinlock);
+ if (pvOld)
+ {
+ RTMemFree(pvOld);
+ break;
+ }
+ intnetR0BusyWait(pNetwork, &pTrunk->cBusy);
+ }
+ }
+ }
+
+ /*
+ * The MAC Address table itself.
+ */
+ if (RT_SUCCESS(rc))
+ {
+ PINTNETMACTABENTRY paNew = (PINTNETMACTABENTRY)RTMemAlloc(sizeof(INTNETMACTABENTRY) * cAllocated);
+ if (paNew)
+ {
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ PINTNETMACTABENTRY paOld = pTab->paEntries;
+ uint32_t i = pTab->cEntries;
+ while (i-- > 0)
+ {
+ paNew[i] = paOld[i];
+
+ paOld[i].fActive = false;
+ paOld[i].pIf = NULL;
+ }
+
+ pTab->paEntries = paNew;
+ pTab->cEntriesAllocated = cAllocated;
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+
+ RTMemFree(paOld);
+ }
+ else
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ rc = VERR_OUT_OF_RANGE;
+ }
+ return rc;
+}
+
+
+
+
+#ifdef INTNET_WITH_DHCP_SNOOPING
+
+/**
+ * Snoops IP assignments and releases from the DHCPv4 traffic.
+ *
+ * The caller is responsible for making sure this traffic between the
+ * BOOTPS and BOOTPC ports and validate the IP header. The UDP packet
+ * need not be validated beyond the ports.
+ *
+ * @param pNetwork The network this frame was seen on.
+ * @param pIpHdr Pointer to a valid IP header. This is for pseudo
+ * header validation, so only the minimum header size
+ * needs to be available and valid here.
+ * @param pUdpHdr Pointer to the UDP header in the frame.
+ * @param cbUdpPkt What's left of the frame when starting at the UDP header.
+ * @param fGso Set if this is a GSO frame, clear if regular.
+ */
+static void intnetR0NetworkSnoopDhcp(PINTNETNETWORK pNetwork, PCRTNETIPV4 pIpHdr, PCRTNETUDP pUdpHdr, uint32_t cbUdpPkt)
+{
+ /*
+ * Check if the DHCP message is valid and get the type.
+ */
+ if (!RTNetIPv4IsUDPValid(pIpHdr, pUdpHdr, pUdpHdr + 1, cbUdpPkt, true /*fCheckSum*/))
+ {
+ Log6(("Bad UDP packet\n"));
+ return;
+ }
+ PCRTNETBOOTP pDhcp = (PCRTNETBOOTP)(pUdpHdr + 1);
+ uint8_t MsgType;
+ if (!RTNetIPv4IsDHCPValid(pUdpHdr, pDhcp, cbUdpPkt - sizeof(*pUdpHdr), &MsgType))
+ {
+ Log6(("Bad DHCP packet\n"));
+ return;
+ }
+
+#ifdef LOG_ENABLED
+ /*
+ * Log it.
+ */
+ const char *pszType = "unknown";
+ switch (MsgType)
+ {
+ case RTNET_DHCP_MT_DISCOVER: pszType = "discover"; break;
+ case RTNET_DHCP_MT_OFFER: pszType = "offer"; break;
+ case RTNET_DHCP_MT_REQUEST: pszType = "request"; break;
+ case RTNET_DHCP_MT_DECLINE: pszType = "decline"; break;
+ case RTNET_DHCP_MT_ACK: pszType = "ack"; break;
+ case RTNET_DHCP_MT_NAC: pszType = "nac"; break;
+ case RTNET_DHCP_MT_RELEASE: pszType = "release"; break;
+ case RTNET_DHCP_MT_INFORM: pszType = "inform"; break;
+ }
+ Log6(("DHCP msg: %d (%s) client %.6Rhxs ciaddr=%d.%d.%d.%d yiaddr=%d.%d.%d.%d\n", MsgType, pszType, &pDhcp->bp_chaddr,
+ pDhcp->bp_ciaddr.au8[0], pDhcp->bp_ciaddr.au8[1], pDhcp->bp_ciaddr.au8[2], pDhcp->bp_ciaddr.au8[3],
+ pDhcp->bp_yiaddr.au8[0], pDhcp->bp_yiaddr.au8[1], pDhcp->bp_yiaddr.au8[2], pDhcp->bp_yiaddr.au8[3]));
+#endif /* LOG_EANBLED */
+
+ /*
+ * Act upon the message.
+ */
+ switch (MsgType)
+ {
+#if 0
+ case RTNET_DHCP_MT_REQUEST:
+ /** @todo Check for valid non-broadcast requests w/ IP for any of the MACs we
+ * know, and add the IP to the cache. */
+ break;
+#endif
+
+
+ /*
+ * Lookup the interface by its MAC address and insert the IPv4 address into the cache.
+ * Delete the old client address first, just in case it changed in a renewal.
+ */
+ case RTNET_DHCP_MT_ACK:
+ if (intnetR0IPv4AddrIsGood(pDhcp->bp_yiaddr))
+ {
+ PINTNETIF pMatchingIf = NULL;
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ uint32_t iIf = pNetwork->MacTab.cEntries;
+ while (iIf-- > 0)
+ {
+ PINTNETIF pCur = pNetwork->MacTab.paEntries[iIf].pIf;
+ if ( intnetR0IfHasMacAddr(pCur)
+ && !memcmp(&pCur->MacAddr, &pDhcp->bp_chaddr, sizeof(RTMAC)))
+ {
+ intnetR0IfAddrCacheDelete(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
+ (PCRTNETADDRU)&pDhcp->bp_ciaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_ACK");
+ if (!pMatchingIf)
+ {
+ pMatchingIf = pCur;
+ intnetR0BusyIncIf(pMatchingIf);
+ }
+ }
+ }
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+
+ if (pMatchingIf)
+ {
+ intnetR0IfAddrCacheAdd(pMatchingIf, kIntNetAddrType_IPv4,
+ (PCRTNETADDRU)&pDhcp->bp_yiaddr, "DHCP_MT_ACK");
+ intnetR0BusyDecIf(pMatchingIf);
+ }
+ }
+ return;
+
+
+ /*
+ * Lookup the interface by its MAC address and remove the IPv4 address(es) from the cache.
+ */
+ case RTNET_DHCP_MT_RELEASE:
+ {
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ uint32_t iIf = pNetwork->MacTab.cEntries;
+ while (iIf-- > 0)
+ {
+ PINTNETIF pCur = pNetwork->MacTab.paEntries[iIf].pIf;
+ if ( intnetR0IfHasMacAddr(pCur)
+ && !memcmp(&pCur->MacAddr, &pDhcp->bp_chaddr, sizeof(RTMAC)))
+ {
+ intnetR0IfAddrCacheDelete(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
+ (PCRTNETADDRU)&pDhcp->bp_ciaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_RELEASE");
+ intnetR0IfAddrCacheDelete(pCur, &pCur->aAddrCache[kIntNetAddrType_IPv4],
+ (PCRTNETADDRU)&pDhcp->bp_yiaddr, sizeof(RTNETADDRIPV4), "DHCP_MT_RELEASE");
+ }
+ }
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+ break;
+ }
+ }
+
+}
+
+
+/**
+ * Worker for intnetR0TrunkIfSnoopAddr that takes care of what
+ * is likely to be a DHCP message.
+ *
+ * The caller has already check that the UDP source and destination ports
+ * are BOOTPS or BOOTPC.
+ *
+ * @param pNetwork The network this frame was seen on.
+ * @param pSG The gather list for the frame.
+ */
+static void intnetR0TrunkIfSnoopDhcp(PINTNETNETWORK pNetwork, PCINTNETSG pSG)
+{
+ /*
+ * Get a pointer to a linear copy of the full packet, using the
+ * temporary buffer if necessary.
+ */
+ PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)((PCRTNETETHERHDR)pSG->aSegs[0].pv + 1);
+ uint32_t cbPacket = pSG->cbTotal - sizeof(RTNETETHERHDR);
+ if (pSG->cSegsUsed > 1)
+ {
+ cbPacket = RT_MIN(cbPacket, INTNETNETWORK_TMP_SIZE);
+ Log6(("intnetR0TrunkIfSnoopDhcp: Copying IPv4/UDP/DHCP pkt %u\n", cbPacket));
+ if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), cbPacket, pNetwork->pbTmp))
+ return;
+ //pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP;
+ pIpHdr = (PCRTNETIPV4)pNetwork->pbTmp;
+ }
+
+ /*
+ * Validate the IP header and find the UDP packet.
+ */
+ if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, pSG->cbTotal - sizeof(RTNETETHERHDR), true /*fChecksum*/))
+ {
+ Log(("intnetR0TrunkIfSnoopDhcp: bad ip header\n"));
+ return;
+ }
+ uint32_t cbIpHdr = pIpHdr->ip_hl * 4;
+
+ /*
+ * Hand it over to the common DHCP snooper.
+ */
+ intnetR0NetworkSnoopDhcp(pNetwork, pIpHdr, (PCRTNETUDP)((uintptr_t)pIpHdr + cbIpHdr), cbPacket - cbIpHdr);
+}
+
+#endif /* INTNET_WITH_DHCP_SNOOPING */
+
+
+/**
+ * Snoops up source addresses from ARP requests and purge these from the address
+ * caches.
+ *
+ * The purpose of this purging is to get rid of stale addresses.
+ *
+ * @param pNetwork The network this frame was seen on.
+ * @param pSG The gather list for the frame.
+ */
+static void intnetR0TrunkIfSnoopArp(PINTNETNETWORK pNetwork, PCINTNETSG pSG)
+{
+ /*
+ * Check the minimum size first.
+ */
+ if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4)))
+ return;
+
+ /*
+ * Copy to temporary buffer if necessary.
+ */
+ uint32_t cbPacket = RT_MIN(pSG->cbTotal, sizeof(RTNETARPIPV4));
+ PCRTNETARPIPV4 pArpIPv4 = (PCRTNETARPIPV4)((uintptr_t)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
+ if ( pSG->cSegsUsed != 1
+ && pSG->aSegs[0].cb < cbPacket)
+ {
+ if ( (pSG->fFlags & (INTNETSG_FLAGS_ARP_IPV4 | INTNETSG_FLAGS_PKT_CP_IN_TMP))
+ != (INTNETSG_FLAGS_ARP_IPV4 | INTNETSG_FLAGS_PKT_CP_IN_TMP)
+ && !intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), cbPacket, pNetwork->pbTmp))
+ return;
+ pArpIPv4 = (PCRTNETARPIPV4)pNetwork->pbTmp;
+ }
+
+ /*
+ * Ignore packets which doesn't interest us or we perceive as malformed.
+ */
+ if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
+ || pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
+ || pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
+ || pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
+ return;
+ uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
+ if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
+ && ar_oper != RTNET_ARPOP_REPLY))
+ {
+ Log6(("ts-ar: op=%#x\n", ar_oper));
+ return;
+ }
+
+ /*
+ * Delete the source address if it's OK.
+ */
+ if ( !intnetR0IsMacAddrMulticast(&pArpIPv4->ar_sha)
+ && ( pArpIPv4->ar_sha.au16[0]
+ || pArpIPv4->ar_sha.au16[1]
+ || pArpIPv4->ar_sha.au16[2])
+ && intnetR0IPv4AddrIsGood(pArpIPv4->ar_spa))
+ {
+ Log6(("ts-ar: %d.%d.%d.%d / %.6Rhxs\n", pArpIPv4->ar_spa.au8[0], pArpIPv4->ar_spa.au8[1],
+ pArpIPv4->ar_spa.au8[2], pArpIPv4->ar_spa.au8[3], &pArpIPv4->ar_sha));
+ intnetR0NetworkAddrCacheDelete(pNetwork, (PCRTNETADDRU)&pArpIPv4->ar_spa,
+ kIntNetAddrType_IPv4, sizeof(pArpIPv4->ar_spa), "tif/arp");
+ }
+}
+
+
+#ifdef INTNET_WITH_DHCP_SNOOPING
+/**
+ * Snoop up addresses from ARP and DHCP traffic from frames coming
+ * over the trunk connection.
+ *
+ * The caller is responsible for do some basic filtering before calling
+ * this function.
+ * For IPv4 this means checking against the minimum DHCPv4 frame size.
+ *
+ * @param pNetwork The network.
+ * @param pSG The SG list for the frame.
+ * @param EtherType The Ethertype of the frame.
+ */
+static void intnetR0TrunkIfSnoopAddr(PINTNETNETWORK pNetwork, PCINTNETSG pSG, uint16_t EtherType)
+{
+ switch (EtherType)
+ {
+ case RTNET_ETHERTYPE_IPV4:
+ {
+ uint32_t cbIpHdr;
+ uint8_t b;
+
+ Assert(pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN);
+ if (pSG->aSegs[0].cb >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN)
+ {
+ /* check if the protocol is UDP */
+ PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)((uint8_t const *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
+ if (pIpHdr->ip_p != RTNETIPV4_PROT_UDP)
+ return;
+
+ /* get the TCP header length */
+ cbIpHdr = pIpHdr->ip_hl * 4;
+ }
+ else
+ {
+ /* check if the protocol is UDP */
+ if ( intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + RT_UOFFSETOF(RTNETIPV4, ip_p))
+ != RTNETIPV4_PROT_UDP)
+ return;
+
+ /* get the TCP header length */
+ b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + 0); /* (IPv4 first byte, a bitfield) */
+ cbIpHdr = (b & 0x0f) * 4;
+ }
+ if (cbIpHdr < RTNETIPV4_MIN_LEN)
+ return;
+
+ /* compare the ports. */
+ if (pSG->aSegs[0].cb >= sizeof(RTNETETHERHDR) + cbIpHdr + RTNETUDP_MIN_LEN)
+ {
+ PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint8_t const *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR) + cbIpHdr);
+ if ( ( RT_BE2H_U16(pUdpHdr->uh_sport) != RTNETIPV4_PORT_BOOTPS
+ && RT_BE2H_U16(pUdpHdr->uh_dport) != RTNETIPV4_PORT_BOOTPS)
+ || ( RT_BE2H_U16(pUdpHdr->uh_dport) != RTNETIPV4_PORT_BOOTPC
+ && RT_BE2H_U16(pUdpHdr->uh_sport) != RTNETIPV4_PORT_BOOTPC))
+ return;
+ }
+ else
+ {
+ /* get the lower byte of the UDP source port number. */
+ b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_UOFFSETOF(RTNETUDP, uh_sport) + 1);
+ if ( b != RTNETIPV4_PORT_BOOTPS
+ && b != RTNETIPV4_PORT_BOOTPC)
+ return;
+ uint8_t SrcPort = b;
+ b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_UOFFSETOF(RTNETUDP, uh_sport));
+ if (b)
+ return;
+
+ /* get the lower byte of the UDP destination port number. */
+ b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_UOFFSETOF(RTNETUDP, uh_dport) + 1);
+ if ( b != RTNETIPV4_PORT_BOOTPS
+ && b != RTNETIPV4_PORT_BOOTPC)
+ return;
+ if (b == SrcPort)
+ return;
+ b = intnetR0SgReadByte(pSG, sizeof(RTNETETHERHDR) + cbIpHdr + RT_UOFFSETOF(RTNETUDP, uh_dport));
+ if (b)
+ return;
+ }
+ intnetR0TrunkIfSnoopDhcp(pNetwork, pSG);
+ break;
+ }
+
+ case RTNET_ETHERTYPE_ARP:
+ intnetR0TrunkIfSnoopArp(pNetwork, pSG);
+ break;
+ }
+}
+#endif /* INTNET_WITH_DHCP_SNOOPING */
+
+/**
+ * Deals with an IPv6 packet.
+ *
+ * This will fish out the source IP address and add it to the cache.
+ * Then it will look for DHCPRELEASE requests (?) and anything else
+ * that we might find useful later.
+ *
+ * @param pIf The interface that's sending the frame.
+ * @param pIpHdr Pointer to the IPv4 header in the frame.
+ * @param cbPacket The size of the packet, or more correctly the
+ * size of the frame without the ethernet header.
+ * @param fGso Set if this is a GSO frame, clear if regular.
+ */
+static void intnetR0IfSnoopIPv6SourceAddr(PINTNETIF pIf, PCRTNETIPV6 pIpHdr, uint32_t cbPacket, bool fGso)
+{
+ NOREF(fGso);
+
+ /*
+ * Check the header size first to prevent access invalid data.
+ */
+ if (cbPacket < RTNETIPV6_MIN_LEN)
+ return;
+
+ /*
+ * If the source address is good (not multicast) and
+ * not already in the address cache of the sender, add it.
+ */
+ RTNETADDRU Addr;
+ Addr.IPv6 = pIpHdr->ip6_src;
+
+ if ( intnetR0IPv6AddrIsGood(Addr.IPv6) && (pIpHdr->ip6_hlim == 0xff)
+ && intnetR0IfAddrCacheLookupLikely(&pIf->aAddrCache[kIntNetAddrType_IPv6], &Addr, sizeof(Addr.IPv6)) < 0)
+ {
+ intnetR0IfAddrCacheAdd(pIf, kIntNetAddrType_IPv6, &Addr, "if/ipv6");
+ }
+}
+
+
+/**
+ * Deals with an IPv4 packet.
+ *
+ * This will fish out the source IP address and add it to the cache.
+ * Then it will look for DHCPRELEASE requests (?) and anything else
+ * that we might find useful later.
+ *
+ * @param pIf The interface that's sending the frame.
+ * @param pIpHdr Pointer to the IPv4 header in the frame.
+ * @param cbPacket The size of the packet, or more correctly the
+ * size of the frame without the ethernet header.
+ * @param fGso Set if this is a GSO frame, clear if regular.
+ */
+static void intnetR0IfSnoopIPv4SourceAddr(PINTNETIF pIf, PCRTNETIPV4 pIpHdr, uint32_t cbPacket, bool fGso)
+{
+ /*
+ * Check the header size first to prevent access invalid data.
+ */
+ if (cbPacket < RTNETIPV4_MIN_LEN)
+ return;
+ uint32_t cbHdr = (uint32_t)pIpHdr->ip_hl * 4;
+ if ( cbHdr < RTNETIPV4_MIN_LEN
+ || cbPacket < cbHdr)
+ return;
+
+ /*
+ * If the source address is good (not broadcast or my network) and
+ * not already in the address cache of the sender, add it. Validate
+ * the IP header before adding it.
+ */
+ bool fValidatedIpHdr = false;
+ RTNETADDRU Addr;
+ Addr.IPv4 = pIpHdr->ip_src;
+ if ( intnetR0IPv4AddrIsGood(Addr.IPv4)
+ && intnetR0IfAddrCacheLookupLikely(&pIf->aAddrCache[kIntNetAddrType_IPv4], &Addr, sizeof(Addr.IPv4)) < 0)
+ {
+ if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, cbPacket, !fGso /*fChecksum*/))
+ {
+ Log(("intnetR0IfSnoopIPv4SourceAddr: bad ip header\n"));
+ return;
+ }
+
+ intnetR0IfAddrCacheAddIt(pIf, kIntNetAddrType_IPv4, &Addr, "if/ipv4");
+ fValidatedIpHdr = true;
+ }
+
+#ifdef INTNET_WITH_DHCP_SNOOPING
+ /*
+ * Check for potential DHCP packets.
+ */
+ if ( pIpHdr->ip_p == RTNETIPV4_PROT_UDP /* DHCP is UDP. */
+ && cbPacket >= cbHdr + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN /* Min DHCP packet len. */
+ && !fGso) /* GSO is not applicable to DHCP traffic. */
+ {
+ PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint8_t const *)pIpHdr + cbHdr);
+ if ( ( RT_BE2H_U16(pUdpHdr->uh_dport) == RTNETIPV4_PORT_BOOTPS
+ || RT_BE2H_U16(pUdpHdr->uh_sport) == RTNETIPV4_PORT_BOOTPS)
+ && ( RT_BE2H_U16(pUdpHdr->uh_sport) == RTNETIPV4_PORT_BOOTPC
+ || RT_BE2H_U16(pUdpHdr->uh_dport) == RTNETIPV4_PORT_BOOTPC))
+ {
+ if ( fValidatedIpHdr
+ || RTNetIPv4IsHdrValid(pIpHdr, cbPacket, cbPacket, !fGso /*fChecksum*/))
+ intnetR0NetworkSnoopDhcp(pIf->pNetwork, pIpHdr, pUdpHdr, cbPacket - cbHdr);
+ else
+ Log(("intnetR0IfSnoopIPv4SourceAddr: bad ip header (dhcp)\n"));
+ }
+ }
+#endif /* INTNET_WITH_DHCP_SNOOPING */
+}
+
+
+/**
+ * Snoop up source addresses from an ARP request or reply.
+ *
+ * @param pIf The interface that's sending the frame.
+ * @param pHdr The ARP header.
+ * @param cbPacket The size of the packet (might be larger than the ARP
+ * request 'cause of min ethernet frame size).
+ * @param pfSgFlags Pointer to the SG flags. This is used to tag the packet so we
+ * don't have to repeat the frame parsing in intnetR0TrunkIfSend.
+ */
+static void intnetR0IfSnoopArpAddr(PINTNETIF pIf, PCRTNETARPIPV4 pArpIPv4, uint32_t cbPacket, uint16_t *pfSgFlags)
+{
+ /*
+ * Ignore packets which doesn't interest us or we perceive as malformed.
+ */
+ if (RT_UNLIKELY(cbPacket < sizeof(RTNETARPIPV4)))
+ return;
+ if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
+ || pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
+ || pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
+ || pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
+ return;
+ uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
+ if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
+ && ar_oper != RTNET_ARPOP_REPLY))
+ {
+ Log6(("ar_oper=%#x\n", ar_oper));
+ return;
+ }
+
+ /*
+ * Tag the SG as ARP IPv4 for later editing, then check for addresses
+ * which can be removed or added to the address cache of the sender.
+ */
+ *pfSgFlags |= INTNETSG_FLAGS_ARP_IPV4;
+
+ if ( ar_oper == RTNET_ARPOP_REPLY
+ && !intnetR0IsMacAddrMulticast(&pArpIPv4->ar_tha)
+ && ( pArpIPv4->ar_tha.au16[0]
+ || pArpIPv4->ar_tha.au16[1]
+ || pArpIPv4->ar_tha.au16[2])
+ && intnetR0IPv4AddrIsGood(pArpIPv4->ar_tpa))
+ intnetR0IfAddrCacheDelete(pIf, &pIf->aAddrCache[kIntNetAddrType_IPv4],
+ (PCRTNETADDRU)&pArpIPv4->ar_tpa, sizeof(RTNETADDRIPV4), "if/arp");
+
+ if ( !memcmp(&pArpIPv4->ar_sha, &pIf->MacAddr, sizeof(RTMAC))
+ && intnetR0IPv4AddrIsGood(pArpIPv4->ar_spa))
+ {
+ intnetR0IfAddrCacheAdd(pIf, kIntNetAddrType_IPv4, (PCRTNETADDRU)&pArpIPv4->ar_spa, "if/arp");
+ }
+}
+
+
+
+/**
+ * Checks packets send by a normal interface for new network
+ * layer addresses.
+ *
+ * @param pIf The interface that's sending the frame.
+ * @param pbFrame The frame.
+ * @param cbFrame The size of the frame.
+ * @param fGso Set if this is a GSO frame, clear if regular.
+ * @param pfSgFlags Pointer to the SG flags. This is used to tag the packet so we
+ * don't have to repeat the frame parsing in intnetR0TrunkIfSend.
+ */
+static void intnetR0IfSnoopAddr(PINTNETIF pIf, uint8_t const *pbFrame, uint32_t cbFrame, bool fGso, uint16_t *pfSgFlags)
+{
+ /*
+ * Fish out the ethertype and look for stuff we can handle.
+ */
+ if (cbFrame <= sizeof(RTNETETHERHDR))
+ return;
+ cbFrame -= sizeof(RTNETETHERHDR);
+
+ uint16_t EtherType = RT_H2BE_U16(((PCRTNETETHERHDR)pbFrame)->EtherType);
+ switch (EtherType)
+ {
+ case RTNET_ETHERTYPE_IPV4:
+ intnetR0IfSnoopIPv4SourceAddr(pIf, (PCRTNETIPV4)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, fGso);
+ break;
+
+ case RTNET_ETHERTYPE_IPV6:
+ intnetR0IfSnoopIPv6SourceAddr(pIf, (PCRTNETIPV6)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, fGso);
+ break;
+
+#if 0 /** @todo IntNet: implement IPX for wireless MAC sharing? */
+ case RTNET_ETHERTYPE_IPX_1:
+ case RTNET_ETHERTYPE_IPX_2:
+ case RTNET_ETHERTYPE_IPX_3:
+ intnetR0IfSnoopIpxSourceAddr(pIf, (PCINTNETIPX)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
+ break;
+#endif
+ case RTNET_ETHERTYPE_ARP:
+ intnetR0IfSnoopArpAddr(pIf, (PCRTNETARPIPV4)((PCRTNETETHERHDR)pbFrame + 1), cbFrame, pfSgFlags);
+ break;
+ }
+}
+
+
+/**
+ * Writes a frame packet to the ring buffer.
+ *
+ * @returns VBox status code.
+ * @param pBuf The buffer.
+ * @param pRingBuf The ring buffer to read from.
+ * @param pSG The gather list.
+ * @param pNewDstMac Set the destination MAC address to the address if specified.
+ */
+static int intnetR0RingWriteFrame(PINTNETRINGBUF pRingBuf, PCINTNETSG pSG, PCRTMAC pNewDstMac)
+{
+ PINTNETHDR pHdr = NULL; /* shut up gcc*/
+ void *pvDst = NULL; /* ditto */
+ int rc;
+ if (pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID)
+ rc = IntNetRingAllocateFrame(pRingBuf, pSG->cbTotal, &pHdr, &pvDst);
+ else
+ rc = IntNetRingAllocateGsoFrame(pRingBuf, pSG->cbTotal, &pSG->GsoCtx, &pHdr, &pvDst);
+ if (RT_SUCCESS(rc))
+ {
+ IntNetSgRead(pSG, pvDst);
+ if (pNewDstMac)
+ ((PRTNETETHERHDR)pvDst)->DstMac = *pNewDstMac;
+
+ IntNetRingCommitFrame(pRingBuf, pHdr);
+ return VINF_SUCCESS;
+ }
+ return rc;
+}
+
+
+/**
+ * Notifies consumers of incoming data from @a pIf that data is available.
+ */
+DECL_FORCE_INLINE(void) intnetR0IfNotifyRecv(PINTNETIF pIf)
+{
+#if !defined(VBOX_WITH_INTNET_SERVICE_IN_R3) || !defined(IN_RING3)
+ RTSemEventSignal(pIf->hRecvEvent);
+#else
+ pIf->pfnRecvAvail(pIf->hIf, pIf->pvUserRecvAvail);
+#endif
+}
+
+
+/**
+ * Sends a frame to a specific interface.
+ *
+ * @param pIf The interface.
+ * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
+ * @param pSG The gather buffer which data is being sent to the interface.
+ * @param pNewDstMac Set the destination MAC address to the address if specified.
+ */
+static void intnetR0IfSend(PINTNETIF pIf, PINTNETIF pIfSender, PINTNETSG pSG, PCRTMAC pNewDstMac)
+{
+ /*
+ * Grab the receive/producer lock and copy over the frame.
+ */
+ RTSpinlockAcquire(pIf->hRecvInSpinlock);
+ int rc = intnetR0RingWriteFrame(&pIf->pIntBuf->Recv, pSG, pNewDstMac);
+ RTSpinlockRelease(pIf->hRecvInSpinlock);
+ if (RT_SUCCESS(rc))
+ {
+ pIf->cYields = 0;
+ intnetR0IfNotifyRecv(pIf);
+ return;
+ }
+
+ Log(("intnetR0IfSend: overflow cb=%d hIf=%RX32\n", pSG->cbTotal, pIf->hIf));
+
+ /*
+ * Scheduling hack, for unicore machines primarily.
+ */
+ if ( pIf->fActive
+ && pIf->cYields < 4 /* just twice */
+ && pIfSender /* but not if it's from the trunk */
+ && RTThreadPreemptIsEnabled(NIL_RTTHREAD)
+ )
+ {
+ unsigned cYields = 2;
+ while (--cYields > 0)
+ {
+ intnetR0IfNotifyRecv(pIf);
+ RTThreadYield();
+
+ RTSpinlockAcquire(pIf->hRecvInSpinlock);
+ rc = intnetR0RingWriteFrame(&pIf->pIntBuf->Recv, pSG, pNewDstMac);
+ RTSpinlockRelease(pIf->hRecvInSpinlock);
+ if (RT_SUCCESS(rc))
+ {
+ STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsOk);
+ intnetR0IfNotifyRecv(pIf);
+ return;
+ }
+ pIf->cYields++;
+ }
+ STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatYieldsNok);
+ }
+
+ /* ok, the frame is lost. */
+ STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatLost);
+ intnetR0IfNotifyRecv(pIf);
+}
+
+
+/**
+ * Fallback path that does the GSO segmenting before passing the frame on to the
+ * trunk interface.
+ *
+ * The caller holds the trunk lock.
+ *
+ * @param pThis The trunk.
+ * @param pIfSender The IF sending the frame.
+ * @param pSG Pointer to the gather list.
+ * @param fDst The destination flags.
+ */
+static int intnetR0TrunkIfSendGsoFallback(PINTNETTRUNKIF pThis, PINTNETIF pIfSender, PINTNETSG pSG, uint32_t fDst)
+{
+ /*
+ * Since we're only using this for GSO frame coming from the internal
+ * network interfaces and never the trunk, we can assume there is only
+ * one segment. This simplifies the code quite a bit.
+ */
+ Assert(PDMNetGsoIsValid(&pSG->GsoCtx, sizeof(pSG->GsoCtx), pSG->cbTotal));
+ AssertReturn(pSG->cSegsUsed == 1, VERR_INTERNAL_ERROR_4);
+
+ union
+ {
+ uint8_t abBuf[sizeof(INTNETSG) + sizeof(INTNETSEG)];
+ INTNETSG SG;
+ } u;
+
+ /** @todo We have to adjust MSS so it does not exceed the value configured for
+ * the host's interface.
+ */
+
+ /*
+ * Carve out the frame segments with the header and frame in different
+ * scatter / gather segments.
+ */
+ uint32_t const cSegs = PDMNetGsoCalcSegmentCount(&pSG->GsoCtx, pSG->cbTotal);
+ for (uint32_t iSeg = 0; iSeg < cSegs; iSeg++)
+ {
+ uint32_t cbSegPayload, cbSegHdrs;
+ uint32_t offSegPayload = PDMNetGsoCarveSegment(&pSG->GsoCtx, (uint8_t *)pSG->aSegs[0].pv, pSG->cbTotal, iSeg, cSegs,
+ pIfSender->abGsoHdrs, &cbSegHdrs, &cbSegPayload);
+
+ IntNetSgInitTempSegs(&u.SG, cbSegHdrs + cbSegPayload, 2, 2);
+ u.SG.aSegs[0].Phys = NIL_RTHCPHYS;
+ u.SG.aSegs[0].pv = pIfSender->abGsoHdrs;
+ u.SG.aSegs[0].cb = cbSegHdrs;
+ u.SG.aSegs[1].Phys = NIL_RTHCPHYS;
+ u.SG.aSegs[1].pv = (uint8_t *)pSG->aSegs[0].pv + offSegPayload;
+ u.SG.aSegs[1].cb = (uint32_t)cbSegPayload;
+
+ int rc = pThis->pIfPort->pfnXmit(pThis->pIfPort, pIfSender->pvIfData, &u.SG, fDst);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Checks if any of the given trunk destinations can handle this kind of GSO SG.
+ *
+ * @returns true if it can, false if it cannot.
+ * @param pThis The trunk.
+ * @param pSG The scatter / gather buffer.
+ * @param fDst The destination mask.
+ */
+DECLINLINE(bool) intnetR0TrunkIfCanHandleGsoFrame(PINTNETTRUNKIF pThis, PINTNETSG pSG, uint32_t fDst)
+{
+ uint8_t u8Type = pSG->GsoCtx.u8Type;
+ AssertReturn(u8Type < 32, false); /* paranoia */
+ uint32_t fMask = RT_BIT_32(u8Type);
+
+ if (fDst == INTNETTRUNKDIR_HOST)
+ return !!(pThis->fHostGsoCapabilites & fMask);
+ if (fDst == INTNETTRUNKDIR_WIRE)
+ return !!(pThis->fWireGsoCapabilites & fMask);
+ Assert(fDst == (INTNETTRUNKDIR_WIRE | INTNETTRUNKDIR_HOST));
+ return !!(pThis->fHostGsoCapabilites & pThis->fWireGsoCapabilites & fMask);
+}
+
+
+/**
+ * Calculates the checksum of a full ipv6 frame.
+ *
+ * @returns 16-bit hecksum value.
+ * @param pIpHdr The IPv6 header (network endian (big)).
+ * @param bProtocol The protocol number. This can be the same as the
+ * ip6_nxt field, but doesn't need to be.
+ * @param cbPkt The packet size (host endian of course). This can
+ * be the same as the ip6_plen field, but as with @a
+ * bProtocol it won't be when extension headers are
+ * present. For UDP this will be uh_ulen converted to
+ * host endian.
+ */
+static uint16_t computeIPv6FullChecksum(PCRTNETIPV6 pIpHdr)
+{
+ uint16_t const *data;
+ int len = RT_BE2H_U16(pIpHdr->ip6_plen);
+ uint32_t sum = RTNetIPv6PseudoChecksum(pIpHdr);
+
+ /* add the payload */
+ data = (uint16_t *) (pIpHdr + 1);
+ while(len > 1)
+ {
+ sum += *(data);
+ data++;
+ len -= 2;
+ }
+
+ if(len > 0)
+ sum += *((uint8_t *) data);
+
+ while(sum >> 16)
+ sum = (sum & 0xffff) + (sum >> 16);
+
+ return (uint16_t) ~sum;
+}
+
+
+/**
+ * Rewrite VM MAC address with shared host MAC address inside IPv6
+ * Neighbor Discovery datagrams.
+ */
+static void intnetR0TrunkSharedMacEditIPv6FromIntNet(PINTNETTRUNKIF pThis, PINTNETIF pIfSender,
+ PRTNETETHERHDR pEthHdr, uint32_t cb)
+{
+ if (RT_UNLIKELY(cb < sizeof(*pEthHdr)))
+ return;
+
+ /* have IPv6 header */
+ PRTNETIPV6 pIPv6 = (PRTNETIPV6)(pEthHdr + 1);
+ cb -= sizeof(*pEthHdr);
+ if (RT_UNLIKELY(cb < sizeof(*pIPv6)))
+ return;
+
+ if ( pIPv6->ip6_nxt != RTNETIPV6_PROT_ICMPV6
+ || pIPv6->ip6_hlim != 0xff)
+ return;
+
+ PRTNETICMPV6HDR pICMPv6 = (PRTNETICMPV6HDR)(pIPv6 + 1);
+ cb -= sizeof(*pIPv6);
+ if (RT_UNLIKELY(cb < sizeof(*pICMPv6)))
+ return;
+
+ uint32_t hdrlen = 0;
+ uint8_t llaopt = RTNETIPV6_ICMP_ND_SLLA_OPT;
+
+ uint8_t type = pICMPv6->icmp6_type;
+ switch (type)
+ {
+ case RTNETIPV6_ICMP_TYPE_RS:
+ hdrlen = 8;
+ break;
+
+ case RTNETIPV6_ICMP_TYPE_RA:
+ hdrlen = 16;
+ break;
+
+ case RTNETIPV6_ICMP_TYPE_NS:
+ hdrlen = 24;
+ break;
+
+ case RTNETIPV6_ICMP_TYPE_NA:
+ hdrlen = 24;
+ llaopt = RTNETIPV6_ICMP_ND_TLLA_OPT;
+ break;
+
+ default:
+ return;
+ }
+
+ AssertReturnVoid(hdrlen > 0);
+ if (RT_UNLIKELY(cb < hdrlen))
+ return;
+
+ if (RT_UNLIKELY(pICMPv6->icmp6_code != 0))
+ return;
+
+ PRTNETNDP_LLA_OPT pLLAOpt = NULL;
+ char *pOpt = (char *)pICMPv6 + hdrlen;
+ cb -= hdrlen;
+
+ while (cb >= 8)
+ {
+ uint8_t opt = ((uint8_t *)pOpt)[0];
+ uint32_t optlen = (uint32_t)((uint8_t *)pOpt)[1] * 8;
+ if (RT_UNLIKELY(cb < optlen))
+ return;
+
+ if (opt == llaopt)
+ {
+ if (RT_UNLIKELY(optlen != 8))
+ return;
+ pLLAOpt = (PRTNETNDP_LLA_OPT)pOpt;
+ break;
+ }
+
+ pOpt += optlen;
+ cb -= optlen;
+ }
+
+ if (pLLAOpt == NULL)
+ return;
+
+ if (memcmp(&pLLAOpt->lla, &pIfSender->MacAddr, sizeof(RTMAC)) != 0)
+ return;
+
+ /* overwrite VM's MAC with host's MAC */
+ pLLAOpt->lla = pThis->MacAddr;
+
+ /* recompute the checksum */
+ pICMPv6->icmp6_cksum = 0;
+ pICMPv6->icmp6_cksum = computeIPv6FullChecksum(pIPv6);
+}
+
+
+/**
+ * Sends a frame down the trunk.
+ *
+ * @param pThis The trunk.
+ * @param pNetwork The network the frame is being sent to.
+ * @param pIfSender The IF sending the frame. Used for MAC address
+ * checks in shared MAC mode.
+ * @param fDst The destination flags.
+ * @param pSG Pointer to the gather list.
+ */
+static void intnetR0TrunkIfSend(PINTNETTRUNKIF pThis, PINTNETNETWORK pNetwork, PINTNETIF pIfSender,
+ uint32_t fDst, PINTNETSG pSG)
+{
+ /*
+ * Quick sanity check.
+ */
+ AssertPtr(pThis);
+ AssertPtr(pNetwork);
+ AssertPtr(pIfSender);
+ AssertPtr(pSG);
+ Assert(fDst);
+ AssertReturnVoid(pThis->pIfPort);
+
+ /*
+ * Edit the frame if we're sharing the MAC address with the host on the wire.
+ *
+ * If the frame is headed for both the host and the wire, we'll have to send
+ * it to the host before making any modifications, and force the OS specific
+ * backend to copy it. We do this by marking it as TEMP (which is always the
+ * case right now).
+ */
+ if ( (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
+ && (fDst & INTNETTRUNKDIR_WIRE))
+ {
+ /*
+ * Dispatch it to the host before making changes.
+ */
+ if (fDst & INTNETTRUNKDIR_HOST)
+ {
+ Assert(pSG->fFlags & INTNETSG_FLAGS_TEMP); /* make sure copy is forced */
+ intnetR0TrunkIfSend(pThis, pNetwork, pIfSender, INTNETTRUNKDIR_HOST, pSG);
+ fDst &= ~INTNETTRUNKDIR_HOST;
+ }
+
+ /*
+ * Edit the source address so that it it's the same as the host.
+ */
+ /* ASSUME frame from IntNetR0IfSend! */
+ AssertReturnVoid(pSG->cSegsUsed == 1);
+ AssertReturnVoid(pSG->cbTotal >= sizeof(RTNETETHERHDR));
+ AssertReturnVoid(pIfSender);
+ PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)pSG->aSegs[0].pv;
+
+ pEthHdr->SrcMac = pThis->MacAddr;
+
+ /*
+ * Deal with tags from the snooping phase.
+ */
+ if (pSG->fFlags & INTNETSG_FLAGS_ARP_IPV4)
+ {
+ /*
+ * APR IPv4: replace hardware (MAC) addresses because these end up
+ * in ARP caches. So, if we don't the other machines will
+ * send the packets to the MAC address of the guest
+ * instead of the one of the host, which won't work on
+ * wireless of course...
+ */
+ PRTNETARPIPV4 pArp = (PRTNETARPIPV4)(pEthHdr + 1);
+ if (!memcmp(&pArp->ar_sha, &pIfSender->MacAddr, sizeof(RTMAC)))
+ {
+ Log6(("tw: ar_sha %.6Rhxs -> %.6Rhxs\n", &pArp->ar_sha, &pThis->MacAddr));
+ pArp->ar_sha = pThis->MacAddr;
+ }
+ if (!memcmp(&pArp->ar_tha, &pIfSender->MacAddr, sizeof(RTMAC))) /* just in case... */
+ {
+ Log6(("tw: ar_tha %.6Rhxs -> %.6Rhxs\n", &pArp->ar_tha, &pThis->MacAddr));
+ pArp->ar_tha = pThis->MacAddr;
+ }
+ }
+ else if (pEthHdr->EtherType == RT_H2N_U16_C(RTNET_ETHERTYPE_IPV6))
+ {
+ intnetR0TrunkSharedMacEditIPv6FromIntNet(pThis, pIfSender, pEthHdr, pSG->cbTotal);
+ }
+ }
+
+ /*
+ * Send the frame, handling the GSO fallback.
+ *
+ * Note! The trunk implementation will re-check that the trunk is active
+ * before sending, so we don't have to duplicate that effort here.
+ */
+ STAM_REL_PROFILE_START(&pIfSender->pIntBuf->StatSend2, a);
+ int rc;
+ if ( pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID
+ || intnetR0TrunkIfCanHandleGsoFrame(pThis, pSG, fDst) )
+ rc = pThis->pIfPort->pfnXmit(pThis->pIfPort, pIfSender->pvIfData, pSG, fDst);
+ else
+ rc = intnetR0TrunkIfSendGsoFallback(pThis, pIfSender, pSG, fDst);
+ STAM_REL_PROFILE_STOP(&pIfSender->pIntBuf->StatSend2, a);
+
+ /** @todo failure statistics? */
+ Log2(("intnetR0TrunkIfSend: %Rrc fDst=%d\n", rc, fDst)); NOREF(rc);
+}
+
+
+/**
+ * Detect broadcasts packaged as unicast and convert them back to broadcast.
+ *
+ * WiFi routers try to use ethernet unicast instead of broadcast or
+ * multicast when possible. Look inside the packet and fix up
+ * ethernet destination to be proper broadcast or multicast if
+ * necessary.
+ *
+ * @returns true broadcast (pEthHdr & pSG are modified), false if not.
+ * @param pNetwork The network the frame is being sent to.
+ * @param pSG Pointer to the gather list for the frame. The
+ * ethernet destination address is modified when
+ * returning true.
+ * @param pEthHdr Pointer to the ethernet header. The ethernet
+ * destination address is modified when returning true.
+ */
+static bool intnetR0NetworkSharedMacDetectAndFixBroadcast(PINTNETNETWORK pNetwork, PINTNETSG pSG, PRTNETETHERHDR pEthHdr)
+{
+ NOREF(pNetwork);
+
+ switch (pEthHdr->EtherType)
+ {
+ case RT_H2N_U16_C(RTNET_ETHERTYPE_ARP):
+ {
+ uint16_t ar_oper;
+ if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_UOFFSETOF(RTNETARPHDR, ar_oper),
+ sizeof(ar_oper), &ar_oper))
+ return false;
+
+ if (ar_oper == RT_H2N_U16_C(RTNET_ARPOP_REQUEST))
+ {
+ /* change to broadcast */
+ pEthHdr->DstMac.au16[0] = 0xffff;
+ pEthHdr->DstMac.au16[1] = 0xffff;
+ pEthHdr->DstMac.au16[2] = 0xffff;
+ }
+ else
+ return false;
+ break;
+ }
+
+ case RT_H2N_U16_C(RTNET_ETHERTYPE_IPV4):
+ {
+ RTNETADDRIPV4 ip_dst;
+ if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_UOFFSETOF(RTNETIPV4, ip_dst),
+ sizeof(ip_dst), &ip_dst))
+ return false;
+
+ if (ip_dst.u == 0xffffffff) /* 255.255.255.255? */
+ {
+ /* change to broadcast */
+ pEthHdr->DstMac.au16[0] = 0xffff;
+ pEthHdr->DstMac.au16[1] = 0xffff;
+ pEthHdr->DstMac.au16[2] = 0xffff;
+ }
+ else if ((ip_dst.au8[0] & 0xf0) == 0xe0) /* IPv4 multicast? */
+ {
+ /* change to 01:00:5e:xx:xx:xx multicast ... */
+ pEthHdr->DstMac.au8[0] = 0x01;
+ pEthHdr->DstMac.au8[1] = 0x00;
+ pEthHdr->DstMac.au8[2] = 0x5e;
+ /* ... with lower 23 bits from the multicast IP address */
+ pEthHdr->DstMac.au8[3] = ip_dst.au8[1] & 0x7f;
+ pEthHdr->DstMac.au8[4] = ip_dst.au8[2];
+ pEthHdr->DstMac.au8[5] = ip_dst.au8[3];
+ }
+ else
+ return false;
+ break;
+ }
+
+ case RT_H2N_U16_C(RTNET_ETHERTYPE_IPV6):
+ {
+ RTNETADDRIPV6 ip6_dst;
+ if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_UOFFSETOF(RTNETIPV6, ip6_dst),
+ sizeof(ip6_dst), &ip6_dst))
+ return false;
+
+ if (ip6_dst.au8[0] == 0xff) /* IPv6 multicast? */
+ {
+ pEthHdr->DstMac.au16[0] = 0x3333;
+ pEthHdr->DstMac.au16[1] = ip6_dst.au16[6];
+ pEthHdr->DstMac.au16[2] = ip6_dst.au16[7];
+ }
+ else
+ return false;
+ break;
+ }
+
+ default:
+ return false;
+ }
+
+
+ /*
+ * Update ethernet destination in the segment.
+ */
+ intnetR0SgWritePart(pSG, RT_UOFFSETOF(RTNETETHERHDR, DstMac), sizeof(pEthHdr->DstMac), &pEthHdr->DstMac);
+
+ return true;
+}
+
+
+/**
+ * Snoops a multicast ICMPv6 ND DAD from the wire via the trunk connection.
+ *
+ * @param pNetwork The network the frame is being sent to.
+ * @param pSG Pointer to the gather list for the frame.
+ * @param pEthHdr Pointer to the ethernet header.
+ */
+static void intnetR0NetworkSnoopNAFromWire(PINTNETNETWORK pNetwork, PINTNETSG pSG, PRTNETETHERHDR pEthHdr)
+{
+ NOREF(pEthHdr);
+
+ /*
+ * Check the minimum size and get a linear copy of the thing to work on,
+ * using the temporary buffer if necessary.
+ */
+ if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + sizeof(RTNETIPV6) +
+ sizeof(RTNETNDP)))
+ return;
+ PRTNETIPV6 pIPv6 = (PRTNETIPV6)((uint8_t *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
+ if ( pSG->cSegsUsed != 1
+ && pSG->aSegs[0].cb < sizeof(RTNETETHERHDR) + sizeof(RTNETIPV6) +
+ sizeof(RTNETNDP))
+ {
+ Log6(("fw: Copying IPv6 pkt %u\n", sizeof(RTNETIPV6)));
+ if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), sizeof(RTNETIPV6)
+ + sizeof(RTNETNDP), pNetwork->pbTmp))
+ return;
+ pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP;
+ pIPv6 = (PRTNETIPV6)pNetwork->pbTmp;
+ }
+
+ PCRTNETNDP pNd = (PCRTNETNDP) (pIPv6 + 1);
+
+ /*
+ * a multicast NS with :: as source address means a DAD packet.
+ * if it comes from the wire and we have the DAD'd address in our cache,
+ * flush the entry as the address is being acquired by someone else on
+ * the network.
+ */
+ if ( pIPv6->ip6_hlim == 0xff
+ && pIPv6->ip6_nxt == RTNETIPV6_PROT_ICMPV6
+ && pNd->Hdr.icmp6_type == RTNETIPV6_ICMP_TYPE_NS
+ && pNd->Hdr.icmp6_code == 0
+ && pIPv6->ip6_src.QWords.qw0 == 0
+ && pIPv6->ip6_src.QWords.qw1 == 0)
+ {
+
+ intnetR0NetworkAddrCacheDelete(pNetwork, (PCRTNETADDRU) &pNd->target_address,
+ kIntNetAddrType_IPv6, sizeof(RTNETADDRIPV6), "tif/ip6");
+ }
+}
+/**
+ * Edits an ARP packet arriving from the wire via the trunk connection.
+ *
+ * @param pNetwork The network the frame is being sent to.
+ * @param pSG Pointer to the gather list for the frame.
+ * The flags and data content may be updated.
+ * @param pEthHdr Pointer to the ethernet header. This may also be
+ * updated if it's a unicast...
+ */
+static void intnetR0NetworkEditArpFromWire(PINTNETNETWORK pNetwork, PINTNETSG pSG, PRTNETETHERHDR pEthHdr)
+{
+ /*
+ * Check the minimum size and get a linear copy of the thing to work on,
+ * using the temporary buffer if necessary.
+ */
+ if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4)))
+ return;
+ PRTNETARPIPV4 pArpIPv4 = (PRTNETARPIPV4)((uint8_t *)pSG->aSegs[0].pv + sizeof(RTNETETHERHDR));
+ if ( pSG->cSegsUsed != 1
+ && pSG->aSegs[0].cb < sizeof(RTNETETHERHDR) + sizeof(RTNETARPIPV4))
+ {
+ Log6(("fw: Copying ARP pkt %u\n", sizeof(RTNETARPIPV4)));
+ if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), sizeof(RTNETARPIPV4), pNetwork->pbTmp))
+ return;
+ pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP;
+ pArpIPv4 = (PRTNETARPIPV4)pNetwork->pbTmp;
+ }
+
+ /*
+ * Ignore packets which doesn't interest us or we perceive as malformed.
+ */
+ if (RT_UNLIKELY( pArpIPv4->Hdr.ar_hlen != sizeof(RTMAC)
+ || pArpIPv4->Hdr.ar_plen != sizeof(RTNETADDRIPV4)
+ || pArpIPv4->Hdr.ar_htype != RT_H2BE_U16(RTNET_ARP_ETHER)
+ || pArpIPv4->Hdr.ar_ptype != RT_H2BE_U16(RTNET_ETHERTYPE_IPV4)))
+ return;
+ uint16_t ar_oper = RT_H2BE_U16(pArpIPv4->Hdr.ar_oper);
+ if (RT_UNLIKELY( ar_oper != RTNET_ARPOP_REQUEST
+ && ar_oper != RTNET_ARPOP_REPLY))
+ {
+ Log6(("ar_oper=%#x\n", ar_oper));
+ return;
+ }
+
+ /* Tag it as ARP IPv4. */
+ pSG->fFlags |= INTNETSG_FLAGS_ARP_IPV4;
+
+ /*
+ * The thing we're interested in here is a reply to a query made by a guest
+ * since we modified the MAC in the initial request the guest made.
+ */
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+ RTMAC MacAddrTrunk;
+ if (pNetwork->MacTab.pTrunk)
+ MacAddrTrunk = pNetwork->MacTab.pTrunk->MacAddr;
+ else
+ memset(&MacAddrTrunk, 0, sizeof(MacAddrTrunk));
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+ if ( ar_oper == RTNET_ARPOP_REPLY
+ && !memcmp(&pArpIPv4->ar_tha, &MacAddrTrunk, sizeof(RTMAC)))
+ {
+ PINTNETIF pIf = intnetR0NetworkAddrCacheLookupIf(pNetwork, (PCRTNETADDRU)&pArpIPv4->ar_tpa,
+ kIntNetAddrType_IPv4, sizeof(pArpIPv4->ar_tpa));
+ if (pIf)
+ {
+ Log6(("fw: ar_tha %.6Rhxs -> %.6Rhxs\n", &pArpIPv4->ar_tha, &pIf->MacAddr));
+ pArpIPv4->ar_tha = pIf->MacAddr;
+ if (!memcmp(&pEthHdr->DstMac, &MacAddrTrunk, sizeof(RTMAC)))
+ {
+ Log6(("fw: DstMac %.6Rhxs -> %.6Rhxs\n", &pEthHdr->DstMac, &pIf->MacAddr));
+ pEthHdr->DstMac = pIf->MacAddr;
+ if ((void *)pEthHdr != pSG->aSegs[0].pv)
+ intnetR0SgWritePart(pSG, RT_UOFFSETOF(RTNETETHERHDR, DstMac), sizeof(RTMAC), &pIf->MacAddr);
+ }
+ intnetR0BusyDecIf(pIf);
+
+ /* Write back the packet if we've been making changes to a buffered copy. */
+ if (pSG->fFlags & INTNETSG_FLAGS_PKT_CP_IN_TMP)
+ intnetR0SgWritePart(pSG, sizeof(RTNETETHERHDR), sizeof(PRTNETARPIPV4), pArpIPv4);
+ }
+ }
+}
+
+
+/**
+ * Detects and edits an DHCP packet arriving from the internal net.
+ *
+ * @param pNetwork The network the frame is being sent to.
+ * @param pSG Pointer to the gather list for the frame.
+ * The flags and data content may be updated.
+ * @param pEthHdr Pointer to the ethernet header. This may also be
+ * updated if it's a unicast...
+ */
+static void intnetR0NetworkEditDhcpFromIntNet(PINTNETNETWORK pNetwork, PINTNETSG pSG, PRTNETETHERHDR pEthHdr)
+{
+ NOREF(pEthHdr);
+
+ /*
+ * Check the minimum size and get a linear copy of the thing to work on,
+ * using the temporary buffer if necessary.
+ */
+ if (RT_UNLIKELY(pSG->cbTotal < sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN))
+ return;
+ /*
+ * Get a pointer to a linear copy of the full packet, using the
+ * temporary buffer if necessary.
+ */
+ PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)((PCRTNETETHERHDR)pSG->aSegs[0].pv + 1);
+ uint32_t cbPacket = pSG->cbTotal - sizeof(RTNETETHERHDR);
+ if (pSG->cSegsUsed > 1)
+ {
+ cbPacket = RT_MIN(cbPacket, INTNETNETWORK_TMP_SIZE);
+ Log6(("intnetR0NetworkEditDhcpFromIntNet: Copying IPv4/UDP/DHCP pkt %u\n", cbPacket));
+ if (!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR), cbPacket, pNetwork->pbTmp))
+ return;
+ //pSG->fFlags |= INTNETSG_FLAGS_PKT_CP_IN_TMP;
+ pIpHdr = (PCRTNETIPV4)pNetwork->pbTmp;
+ }
+
+ /*
+ * Validate the IP header and find the UDP packet.
+ */
+ if (!RTNetIPv4IsHdrValid(pIpHdr, cbPacket, pSG->cbTotal - sizeof(RTNETETHERHDR), true /*fCheckSum*/))
+ {
+ Log6(("intnetR0NetworkEditDhcpFromIntNet: bad ip header\n"));
+ return;
+ }
+ size_t cbIpHdr = pIpHdr->ip_hl * 4;
+ if ( pIpHdr->ip_p != RTNETIPV4_PROT_UDP /* DHCP is UDP. */
+ || cbPacket < cbIpHdr + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN) /* Min DHCP packet len */
+ return;
+
+ size_t cbUdpPkt = cbPacket - cbIpHdr;
+ PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uintptr_t)pIpHdr + cbIpHdr);
+ /* We are only interested in DHCP packets coming from client to server. */
+ if ( RT_BE2H_U16(pUdpHdr->uh_dport) != RTNETIPV4_PORT_BOOTPS
+ || RT_BE2H_U16(pUdpHdr->uh_sport) != RTNETIPV4_PORT_BOOTPC)
+ return;
+
+ /*
+ * Check if the DHCP message is valid and get the type.
+ */
+ if (!RTNetIPv4IsUDPValid(pIpHdr, pUdpHdr, pUdpHdr + 1, cbUdpPkt, true /*fCheckSum*/))
+ {
+ Log6(("intnetR0NetworkEditDhcpFromIntNet: Bad UDP packet\n"));
+ return;
+ }
+ PCRTNETBOOTP pDhcp = (PCRTNETBOOTP)(pUdpHdr + 1);
+ uint8_t bMsgType;
+ if (!RTNetIPv4IsDHCPValid(pUdpHdr, pDhcp, cbUdpPkt - sizeof(*pUdpHdr), &bMsgType))
+ {
+ Log6(("intnetR0NetworkEditDhcpFromIntNet: Bad DHCP packet\n"));
+ return;
+ }
+
+ switch (bMsgType)
+ {
+ case RTNET_DHCP_MT_DISCOVER:
+ case RTNET_DHCP_MT_REQUEST:
+ /*
+ * Must set the broadcast flag or we won't catch the respons.
+ */
+ if (!(pDhcp->bp_flags & RT_H2BE_U16_C(RTNET_DHCP_FLAG_BROADCAST)))
+ {
+ Log6(("intnetR0NetworkEditDhcpFromIntNet: Setting broadcast flag in DHCP %#x, previously %x\n",
+ bMsgType, pDhcp->bp_flags));
+
+ /* Patch flags */
+ uint16_t uFlags = pDhcp->bp_flags | RT_H2BE_U16_C(RTNET_DHCP_FLAG_BROADCAST);
+ intnetR0SgWritePart(pSG, (uintptr_t)&pDhcp->bp_flags - (uintptr_t)pIpHdr + sizeof(RTNETETHERHDR), sizeof(uFlags), &uFlags);
+
+ /* Patch UDP checksum */
+ if (pUdpHdr->uh_sum != 0)
+ {
+ uint32_t uChecksum = (uint32_t)~pUdpHdr->uh_sum + RT_H2BE_U16_C(RTNET_DHCP_FLAG_BROADCAST);
+ while (uChecksum >> 16)
+ uChecksum = (uChecksum >> 16) + (uChecksum & 0xFFFF);
+ uChecksum = ~uChecksum;
+ intnetR0SgWritePart(pSG,
+ (uintptr_t)&pUdpHdr->uh_sum - (uintptr_t)pIpHdr + sizeof(RTNETETHERHDR),
+ sizeof(pUdpHdr->uh_sum),
+ &uChecksum);
+ }
+ }
+
+#ifdef RT_OS_DARWIN
+ /*
+ * Work around little endian checksum issue in mac os x 10.7.0 GM.
+ */
+ if ( pIpHdr->ip_tos
+ && (pNetwork->fFlags & INTNET_OPEN_FLAGS_WORKAROUND_1))
+ {
+ /* Patch it. */
+ uint8_t uTos = pIpHdr->ip_tos;
+ uint8_t uZero = 0;
+ intnetR0SgWritePart(pSG, sizeof(RTNETETHERHDR) + 1, sizeof(uZero), &uZero);
+
+ /* Patch the IP header checksum. */
+ uint32_t uChecksum = (uint32_t)~pIpHdr->ip_sum - (uTos << 8);
+ while (uChecksum >> 16)
+ uChecksum = (uChecksum >> 16) + (uChecksum & 0xFFFF);
+ uChecksum = ~uChecksum;
+
+ Log(("intnetR0NetworkEditDhcpFromIntNet: cleared ip_tos (was %#04x); ip_sum=%#06x -> %#06x\n",
+ uTos, RT_BE2H_U16(pIpHdr->ip_sum), RT_BE2H_U16(uChecksum) ));
+ intnetR0SgWritePart(pSG, sizeof(RTNETETHERHDR) + RT_UOFFSETOF(RTNETIPV4, ip_sum),
+ sizeof(pIpHdr->ip_sum), &uChecksum);
+ }
+#endif
+ break;
+ }
+}
+
+
+/**
+ * Checks if the callers context is okay for sending to the specified
+ * destinations.
+ *
+ * @returns true if it's okay, false if it isn't.
+ * @param pNetwork The network.
+ * @param pIfSender The interface sending or NULL if it's the trunk.
+ * @param pDstTab The destination table.
+ */
+DECLINLINE(bool) intnetR0NetworkIsContextOk(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, PCINTNETDSTTAB pDstTab)
+{
+ NOREF(pNetwork);
+
+ /* Sending to the trunk is the problematic path. If the trunk is the
+ sender we won't be sending to it, so no problem..
+ Note! fTrunkDst may be set event if if the trunk is the sender. */
+ if (!pIfSender)
+ return true;
+
+ uint32_t const fTrunkDst = pDstTab->fTrunkDst;
+ if (!fTrunkDst)
+ return true;
+
+ /* ASSUMES: that the trunk won't change its report while we're checking. */
+ PINTNETTRUNKIF pTrunk = pDstTab->pTrunk;
+ if (pTrunk && (fTrunkDst & pTrunk->fNoPreemptDsts) == fTrunkDst)
+ return true;
+
+ /* ASSUMES: That a preemption test detects HM contexts. (Will work on
+ non-preemptive systems as well.) */
+ if (RTThreadPreemptIsEnabled(NIL_RTTHREAD))
+ return true;
+ return false;
+}
+
+
+/**
+ * Checks if the callers context is okay for doing a broadcast given the
+ * specified source.
+ *
+ * @returns true if it's okay, false if it isn't.
+ * @param pNetwork The network.
+ * @param fSrc The source of the packet. (0 (intnet),
+ * INTNETTRUNKDIR_HOST or INTNETTRUNKDIR_WIRE).
+ */
+DECLINLINE(bool) intnetR0NetworkIsContextOkForBroadcast(PINTNETNETWORK pNetwork, uint32_t fSrc)
+{
+ /* Sending to the trunk is the problematic path. If the trunk is the
+ sender we won't be sending to it, so no problem. */
+ if (fSrc)
+ return true;
+
+ /* ASSUMES: That a preemption test detects HM contexts. (Will work on
+ non-preemptive systems as well.) */
+ if (RTThreadPreemptIsEnabled(NIL_RTTHREAD))
+ return true;
+
+ /* PARANOIA: Grab the spinlock to make sure the trunk structure cannot be
+ freed while we're touching it. */
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+ PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
+
+ bool fRc = !pTrunk
+ || pTrunk->fNoPreemptDsts == (INTNETTRUNKDIR_HOST | INTNETTRUNKDIR_WIRE)
+ || ( (!pNetwork->MacTab.fHostActive || (pTrunk->fNoPreemptDsts & INTNETTRUNKDIR_HOST) )
+ && (!pNetwork->MacTab.fWireActive || (pTrunk->fNoPreemptDsts & INTNETTRUNKDIR_WIRE) ) );
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+
+ return fRc;
+}
+
+
+/**
+ * Check context, edit, snoop and switch a broadcast frame when sharing MAC
+ * address on the wire.
+ *
+ * The caller must hold at least one interface on the network busy to prevent it
+ * from destructing beath us.
+ *
+ * @param pNetwork The network the frame is being sent to.
+ * @param fSrc The source of the packet. (0 (intnet),
+ * INTNETTRUNKDIR_HOST or INTNETTRUNKDIR_WIRE).
+ * @param pIfSender The sender interface, NULL if trunk. Used to
+ * prevent sending an echo to the sender.
+ * @param pSG Pointer to the gather list.
+ * @param pEthHdr Pointer to the ethernet header.
+ * @param pDstTab The destination output table.
+ */
+static INTNETSWDECISION intnetR0NetworkSharedMacFixAndSwitchBroadcast(PINTNETNETWORK pNetwork,
+ uint32_t fSrc, PINTNETIF pIfSender,
+ PINTNETSG pSG, PRTNETETHERHDR pEthHdr,
+ PINTNETDSTTAB pDstTab)
+{
+ /*
+ * Before doing any work here, we need to figure out if we can handle it
+ * in the current context. The restrictions are solely on the trunk.
+ *
+ * Note! Since at least one interface is busy, there won't be any changes
+ * to the parameters here (unless the trunk changes its capability
+ * report, which it shouldn't).
+ */
+ if (!intnetR0NetworkIsContextOkForBroadcast(pNetwork, fSrc))
+ return INTNETSWDECISION_BAD_CONTEXT;
+
+ /*
+ * Check for ICMPv6 Neighbor Advertisements coming from the trunk.
+ * If we see an advertisement for an IP in our cache, we can safely remove
+ * it as the IP has probably moved.
+ */
+ if ( (fSrc & INTNETTRUNKDIR_WIRE)
+ && RT_BE2H_U16(pEthHdr->EtherType) == RTNET_ETHERTYPE_IPV6
+ && pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID)
+ intnetR0NetworkSnoopNAFromWire(pNetwork, pSG, pEthHdr);
+
+
+ /*
+ * Check for ARP packets from the wire since we'll have to make
+ * modification to them if we're sharing the MAC address with the host.
+ */
+ if ( (fSrc & INTNETTRUNKDIR_WIRE)
+ && RT_BE2H_U16(pEthHdr->EtherType) == RTNET_ETHERTYPE_ARP
+ && pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID)
+ intnetR0NetworkEditArpFromWire(pNetwork, pSG, pEthHdr);
+
+ /*
+ * Check for DHCP packets from the internal net since we'll have to set
+ * broadcast flag in DHCP requests if we're sharing the MAC address with
+ * the host. GSO is not applicable to DHCP traffic.
+ */
+ if ( !fSrc
+ && RT_BE2H_U16(pEthHdr->EtherType) == RTNET_ETHERTYPE_IPV4
+ && pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID)
+ intnetR0NetworkEditDhcpFromIntNet(pNetwork, pSG, pEthHdr);
+
+ /*
+ * Snoop address info from packet originating from the trunk connection.
+ */
+ if (fSrc)
+ {
+#ifdef INTNET_WITH_DHCP_SNOOPING
+ uint16_t EtherType = RT_BE2H_U16(pEthHdr->EtherType);
+ if ( ( EtherType == RTNET_ETHERTYPE_IPV4 /* for DHCP */
+ && pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN
+ && pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID )
+ || (pSG->fFlags & INTNETSG_FLAGS_ARP_IPV4) )
+ intnetR0TrunkIfSnoopAddr(pNetwork, pSG, EtherType);
+#else
+ if (pSG->fFlags & INTNETSG_FLAGS_ARP_IPV4)
+ intnetR0TrunkIfSnoopArp(pNetwork, pSG);
+#endif
+ }
+
+ /*
+ * Create the broadcast destination table.
+ */
+ return intnetR0NetworkSwitchBroadcast(pNetwork, fSrc, pIfSender, pDstTab);
+}
+
+
+/**
+ * Check context, snoop and switch a unicast frame using the network layer
+ * address of the link layer one (when sharing MAC address on the wire).
+ *
+ * This function is only used for frames coming from the wire (trunk).
+ *
+ * @returns true if it's addressed to someone on the network, otherwise false.
+ * @param pNetwork The network the frame is being sent to.
+ * @param pSG Pointer to the gather list.
+ * @param pEthHdr Pointer to the ethernet header.
+ * @param pDstTab The destination output table.
+ */
+static INTNETSWDECISION intnetR0NetworkSharedMacFixAndSwitchUnicast(PINTNETNETWORK pNetwork, PINTNETSG pSG,
+ PRTNETETHERHDR pEthHdr, PINTNETDSTTAB pDstTab)
+{
+ /*
+ * Extract the network address from the packet.
+ */
+ RTNETADDRU Addr;
+ INTNETADDRTYPE enmAddrType;
+ uint8_t cbAddr;
+ switch (RT_BE2H_U16(pEthHdr->EtherType))
+ {
+ case RTNET_ETHERTYPE_IPV4:
+ if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_UOFFSETOF(RTNETIPV4, ip_dst), sizeof(Addr.IPv4), &Addr)))
+ {
+ Log(("intnetshareduni: failed to read ip_dst! cbTotal=%#x\n", pSG->cbTotal));
+ return intnetR0NetworkSwitchTrunk(pNetwork, INTNETTRUNKDIR_WIRE, pDstTab);
+ }
+ enmAddrType = kIntNetAddrType_IPv4;
+ cbAddr = sizeof(Addr.IPv4);
+ Log6(("intnetshareduni: IPv4 %d.%d.%d.%d\n", Addr.au8[0], Addr.au8[1], Addr.au8[2], Addr.au8[3]));
+ break;
+
+ case RTNET_ETHERTYPE_IPV6:
+ if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_UOFFSETOF(RTNETIPV6, ip6_dst), sizeof(Addr.IPv6), &Addr)))
+ {
+ Log(("intnetshareduni: failed to read ip6_dst! cbTotal=%#x\n", pSG->cbTotal));
+ return intnetR0NetworkSwitchTrunk(pNetwork, INTNETTRUNKDIR_WIRE, pDstTab);
+ }
+ enmAddrType = kIntNetAddrType_IPv6;
+ cbAddr = sizeof(Addr.IPv6);
+ break;
+#if 0 /** @todo IntNet: implement IPX for wireless MAC sharing? */
+ case RTNET_ETHERTYPE_IPX_1:
+ case RTNET_ETHERTYPE_IPX_2:
+ case RTNET_ETHERTYPE_IPX_3:
+ if (RT_UNLIKELY(!intnetR0SgReadPart(pSG, sizeof(RTNETETHERHDR) + RT_OFFSETOF(RTNETIPX, ipx_dstnet), sizeof(Addr.IPX), &Addr)))
+ {
+ Log(("intnetshareduni: failed to read ipx_dstnet! cbTotal=%#x\n", pSG->cbTotal));
+ return intnetR0NetworkSwitchTrunk(pNetwork, INTNETTRUNKDIR_WIRE, pDstTab);
+ }
+ enmAddrType = kIntNetAddrType_IPX;
+ cbAddr = sizeof(Addr.IPX);
+ break;
+#endif
+
+ /*
+ * Treat ARP as broadcast (it shouldn't end up here normally,
+ * so it goes last in the switch).
+ */
+ case RTNET_ETHERTYPE_ARP:
+ Log6(("intnetshareduni: ARP\n"));
+ /** @todo revisit this broadcasting of unicast ARP frames! */
+ return intnetR0NetworkSharedMacFixAndSwitchBroadcast(pNetwork, INTNETTRUNKDIR_WIRE, NULL, pSG, pEthHdr, pDstTab);
+
+ /*
+ * Unknown packets are sent to the trunk and any promiscuous interfaces.
+ */
+ default:
+ {
+ Log6(("intnetshareduni: unknown ethertype=%#x\n", RT_BE2H_U16(pEthHdr->EtherType)));
+ return intnetR0NetworkSwitchTrunkAndPromisc(pNetwork, INTNETTRUNKDIR_WIRE, pDstTab);
+ }
+ }
+
+ /*
+ * Do level-3 switching.
+ */
+ INTNETSWDECISION enmSwDecision = intnetR0NetworkSwitchLevel3(pNetwork, &pEthHdr->DstMac,
+ enmAddrType, &Addr, cbAddr,
+ INTNETTRUNKDIR_WIRE, pDstTab);
+
+#ifdef INTNET_WITH_DHCP_SNOOPING
+ /*
+ * Perform DHCP snooping. GSO is not applicable to DHCP traffic
+ */
+ if ( enmAddrType == kIntNetAddrType_IPv4
+ && pSG->cbTotal >= sizeof(RTNETETHERHDR) + RTNETIPV4_MIN_LEN + RTNETUDP_MIN_LEN + RTNETBOOTP_DHCP_MIN_LEN
+ && pSG->GsoCtx.u8Type == PDMNETWORKGSOTYPE_INVALID)
+ intnetR0TrunkIfSnoopAddr(pNetwork, pSG, RT_BE2H_U16(pEthHdr->EtherType));
+#endif /* INTNET_WITH_DHCP_SNOOPING */
+
+ return enmSwDecision;
+}
+
+
+/**
+ * Release all the interfaces in the destination table when we realize that
+ * we're in a context where we cannot get the job done.
+ *
+ * @param pNetwork The network.
+ * @param pDstTab The destination table.
+ */
+static void intnetR0NetworkReleaseDstTab(PINTNETNETWORK pNetwork, PINTNETDSTTAB pDstTab)
+{
+ /* The trunk interface. */
+ if (pDstTab->fTrunkDst)
+ {
+ PINTNETTRUNKIF pTrunk = pDstTab->pTrunk;
+ if (pTrunk)
+ intnetR0BusyDec(pNetwork, &pTrunk->cBusy);
+ pDstTab->pTrunk = NULL;
+ pDstTab->fTrunkDst = 0;
+ }
+
+ /* Regular interfaces. */
+ uint32_t iIf = pDstTab->cIfs;
+ while (iIf-- > 0)
+ {
+ PINTNETIF pIf = pDstTab->aIfs[iIf].pIf;
+ intnetR0BusyDecIf(pIf);
+ pDstTab->aIfs[iIf].pIf = NULL;
+ }
+ pDstTab->cIfs = 0;
+}
+
+
+/**
+ * Deliver the frame to the interfaces specified in the destination table.
+ *
+ * @param pNetwork The network.
+ * @param pDstTab The destination table.
+ * @param pSG The frame to send.
+ * @param pIfSender The sender interface. NULL if it originated via
+ * the trunk.
+ */
+static void intnetR0NetworkDeliver(PINTNETNETWORK pNetwork, PINTNETDSTTAB pDstTab, PINTNETSG pSG, PINTNETIF pIfSender)
+{
+ /*
+ * Do the interfaces first before sending it to the wire and risk having to
+ * modify it.
+ */
+ uint32_t iIf = pDstTab->cIfs;
+ while (iIf-- > 0)
+ {
+ PINTNETIF pIf = pDstTab->aIfs[iIf].pIf;
+ intnetR0IfSend(pIf, pIfSender, pSG,
+ pDstTab->aIfs[iIf].fReplaceDstMac ? &pIf->MacAddr: NULL);
+ intnetR0BusyDecIf(pIf);
+ pDstTab->aIfs[iIf].pIf = NULL;
+ }
+ pDstTab->cIfs = 0;
+
+ /*
+ * Send to the trunk.
+ *
+ * Note! The switching functions will include the trunk even when the frame
+ * source is the trunk. This is because we need it to figure out
+ * whether the other half of the trunk should see the frame or not
+ * and let the caller know.
+ *
+ * So, we'll ignore trunk sends here if the frame origin is
+ * INTNETTRUNKSWPORT::pfnRecv.
+ */
+ if (pDstTab->fTrunkDst)
+ {
+ PINTNETTRUNKIF pTrunk = pDstTab->pTrunk;
+ if (pTrunk)
+ {
+ if (pIfSender)
+ intnetR0TrunkIfSend(pTrunk, pNetwork, pIfSender, pDstTab->fTrunkDst, pSG);
+ intnetR0BusyDec(pNetwork, &pTrunk->cBusy);
+ }
+ pDstTab->pTrunk = NULL;
+ pDstTab->fTrunkDst = 0;
+ }
+}
+
+
+/**
+ * Sends a frame.
+ *
+ * This function will distribute the frame to the interfaces it is addressed to.
+ * It will also update the MAC address of the sender.
+ *
+ * The caller must own the network mutex.
+ *
+ * @returns The switching decision.
+ * @param pNetwork The network the frame is being sent to.
+ * @param pIfSender The interface sending the frame. This is NULL if it's the trunk.
+ * @param fSrc The source flags. This 0 if it's not from the trunk.
+ * @param pSG Pointer to the gather list.
+ * @param pDstTab The destination table to use.
+ */
+static INTNETSWDECISION intnetR0NetworkSend(PINTNETNETWORK pNetwork, PINTNETIF pIfSender, uint32_t fSrc,
+ PINTNETSG pSG, PINTNETDSTTAB pDstTab)
+{
+ /*
+ * Assert reality.
+ */
+ AssertPtr(pNetwork);
+ AssertPtrNull(pIfSender);
+ Assert(pIfSender ? fSrc == 0 : fSrc != 0);
+ Assert(!pIfSender || pNetwork == pIfSender->pNetwork);
+ AssertPtr(pSG);
+ Assert(pSG->cSegsUsed >= 1);
+ Assert(pSG->cSegsUsed <= pSG->cSegsAlloc);
+ if (pSG->cbTotal < sizeof(RTNETETHERHDR))
+ return INTNETSWDECISION_INVALID;
+
+ /*
+ * Get the ethernet header (might theoretically involve multiple segments).
+ */
+ RTNETETHERHDR EthHdr;
+ if (pSG->aSegs[0].cb >= sizeof(EthHdr))
+ EthHdr = *(PCRTNETETHERHDR)pSG->aSegs[0].pv;
+ else if (!intnetR0SgReadPart(pSG, 0, sizeof(EthHdr), &EthHdr))
+ return INTNETSWDECISION_INVALID;
+ if ( (EthHdr.DstMac.au8[0] == 0x08 && EthHdr.DstMac.au8[1] == 0x00 && EthHdr.DstMac.au8[2] == 0x27)
+ || (EthHdr.SrcMac.au8[0] == 0x08 && EthHdr.SrcMac.au8[1] == 0x00 && EthHdr.SrcMac.au8[2] == 0x27)
+ || (EthHdr.DstMac.au8[0] == 0x00 && EthHdr.DstMac.au8[1] == 0x16 && EthHdr.DstMac.au8[2] == 0xcb)
+ || (EthHdr.SrcMac.au8[0] == 0x00 && EthHdr.SrcMac.au8[1] == 0x16 && EthHdr.SrcMac.au8[2] == 0xcb)
+ || EthHdr.DstMac.au8[0] == 0xff
+ || EthHdr.SrcMac.au8[0] == 0xff)
+ Log2(("D=%.6Rhxs S=%.6Rhxs T=%04x f=%x z=%x\n",
+ &EthHdr.DstMac, &EthHdr.SrcMac, RT_BE2H_U16(EthHdr.EtherType), fSrc, pSG->cbTotal));
+
+ /*
+ * Learn the MAC address of the sender. No re-learning as the interface
+ * user will normally tell us the right MAC address.
+ *
+ * Note! We don't notify the trunk about these mainly because of the
+ * problematic contexts we might be called in.
+ */
+ if (RT_UNLIKELY( pIfSender
+ && !pIfSender->fMacSet
+ && memcmp(&EthHdr.SrcMac, &pIfSender->MacAddr, sizeof(pIfSender->MacAddr))
+ && !intnetR0IsMacAddrMulticast(&EthHdr.SrcMac)
+ ))
+ {
+ Log2(("IF MAC: %.6Rhxs -> %.6Rhxs\n", &pIfSender->MacAddr, &EthHdr.SrcMac));
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ PINTNETMACTABENTRY pIfEntry = intnetR0NetworkFindMacAddrEntry(pNetwork, pIfSender);
+ if (pIfEntry)
+ pIfEntry->MacAddr = EthHdr.SrcMac;
+ pIfSender->MacAddr = EthHdr.SrcMac;
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+ }
+
+ /*
+ * Deal with MAC address sharing as that may required editing of the
+ * packets before we dispatch them anywhere.
+ */
+ INTNETSWDECISION enmSwDecision;
+ if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
+ {
+ if (intnetR0IsMacAddrMulticast(&EthHdr.DstMac))
+ enmSwDecision = intnetR0NetworkSharedMacFixAndSwitchBroadcast(pNetwork, fSrc, pIfSender, pSG, &EthHdr, pDstTab);
+ else if (fSrc & INTNETTRUNKDIR_WIRE)
+ {
+ if (intnetR0NetworkSharedMacDetectAndFixBroadcast(pNetwork, pSG, &EthHdr))
+ enmSwDecision = intnetR0NetworkSharedMacFixAndSwitchBroadcast(pNetwork, fSrc, pIfSender, pSG, &EthHdr, pDstTab);
+ else
+ enmSwDecision = intnetR0NetworkSharedMacFixAndSwitchUnicast(pNetwork, pSG, &EthHdr, pDstTab);
+ }
+ else
+ enmSwDecision = intnetR0NetworkSwitchUnicast(pNetwork, fSrc, pIfSender, &EthHdr.DstMac, pDstTab);
+ }
+ else if (intnetR0IsMacAddrMulticast(&EthHdr.DstMac))
+ enmSwDecision = intnetR0NetworkSwitchBroadcast(pNetwork, fSrc, pIfSender, pDstTab);
+ else
+ enmSwDecision = intnetR0NetworkSwitchUnicast(pNetwork, fSrc, pIfSender, &EthHdr.DstMac, pDstTab);
+
+ /*
+ * Deliver to the destinations if we can.
+ */
+ if (enmSwDecision != INTNETSWDECISION_BAD_CONTEXT)
+ {
+ if (intnetR0NetworkIsContextOk(pNetwork, pIfSender, pDstTab))
+ intnetR0NetworkDeliver(pNetwork, pDstTab, pSG, pIfSender);
+ else
+ {
+ intnetR0NetworkReleaseDstTab(pNetwork, pDstTab);
+ enmSwDecision = INTNETSWDECISION_BAD_CONTEXT;
+ }
+ }
+
+ return enmSwDecision;
+}
+
+
+/**
+ * Sends one or more frames.
+ *
+ * The function will first the frame which is passed as the optional arguments
+ * pvFrame and cbFrame. These are optional since it also possible to chain
+ * together one or more frames in the send buffer which the function will
+ * process after considering it's arguments.
+ *
+ * The caller is responsible for making sure that there are no concurrent calls
+ * to this method (with the same handle).
+ *
+ * @returns VBox status code.
+ * @param hIf The interface handle.
+ * @param pSession The caller's session.
+ */
+INTNETR0DECL(int) IntNetR0IfSend(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession)
+{
+ Log5(("IntNetR0IfSend: hIf=%RX32\n", hIf));
+
+ /*
+ * Validate input and translate the handle.
+ */
+ PINTNET pIntNet = g_pIntNet;
+ AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
+ AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
+
+ PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
+ if (!pIf)
+ return VERR_INVALID_HANDLE;
+ STAM_REL_PROFILE_START(&pIf->pIntBuf->StatSend1, a);
+
+ /*
+ * Make sure we've got a network.
+ */
+ int rc = VINF_SUCCESS;
+ intnetR0BusyIncIf(pIf);
+ PINTNETNETWORK pNetwork = pIf->pNetwork;
+ if (RT_LIKELY(pNetwork))
+ {
+ /*
+ * Grab the destination table.
+ */
+ PINTNETDSTTAB pDstTab = ASMAtomicXchgPtrT(&pIf->pDstTab, NULL, PINTNETDSTTAB);
+ if (RT_LIKELY(pDstTab))
+ {
+ /*
+ * Process the send buffer.
+ */
+ INTNETSWDECISION enmSwDecision = INTNETSWDECISION_BROADCAST;
+ INTNETSG Sg; /** @todo this will have to be changed if we're going to use async sending
+ * with buffer sharing for some OS or service. Darwin copies everything so
+ * I won't bother allocating and managing SGs right now. Sorry. */
+ PINTNETHDR pHdr;
+ while ((pHdr = IntNetRingGetNextFrameToRead(&pIf->pIntBuf->Send)) != NULL)
+ {
+ uint8_t const u8Type = pHdr->u8Type;
+ if (u8Type == INTNETHDR_TYPE_FRAME)
+ {
+ /* Send regular frame. */
+ void *pvCurFrame = IntNetHdrGetFramePtr(pHdr, pIf->pIntBuf);
+ IntNetSgInitTemp(&Sg, pvCurFrame, pHdr->cbFrame);
+ if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
+ intnetR0IfSnoopAddr(pIf, (uint8_t *)pvCurFrame, pHdr->cbFrame, false /*fGso*/, (uint16_t *)&Sg.fFlags);
+ enmSwDecision = intnetR0NetworkSend(pNetwork, pIf, 0 /*fSrc*/, &Sg, pDstTab);
+ }
+ else if (u8Type == INTNETHDR_TYPE_GSO)
+ {
+ /* Send GSO frame if sane. */
+ PPDMNETWORKGSO pGso = IntNetHdrGetGsoContext(pHdr, pIf->pIntBuf);
+ uint32_t cbFrame = pHdr->cbFrame - sizeof(*pGso);
+ if (RT_LIKELY(PDMNetGsoIsValid(pGso, pHdr->cbFrame, cbFrame)))
+ {
+ void *pvCurFrame = pGso + 1;
+ IntNetSgInitTempGso(&Sg, pvCurFrame, cbFrame, pGso);
+ if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
+ intnetR0IfSnoopAddr(pIf, (uint8_t *)pvCurFrame, cbFrame, true /*fGso*/, (uint16_t *)&Sg.fFlags);
+ enmSwDecision = intnetR0NetworkSend(pNetwork, pIf, 0 /*fSrc*/, &Sg, pDstTab);
+ }
+ else
+ {
+ STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatBadFrames); /* ignore */
+ enmSwDecision = INTNETSWDECISION_DROP;
+ }
+ }
+ /* Unless it's a padding frame, we're getting babble from the producer. */
+ else
+ {
+ if (u8Type != INTNETHDR_TYPE_PADDING)
+ STAM_REL_COUNTER_INC(&pIf->pIntBuf->cStatBadFrames); /* ignore */
+ enmSwDecision = INTNETSWDECISION_DROP;
+ }
+ if (enmSwDecision == INTNETSWDECISION_BAD_CONTEXT)
+ {
+ rc = VERR_TRY_AGAIN;
+ break;
+ }
+
+ /* Skip to the next frame. */
+ IntNetRingSkipFrame(&pIf->pIntBuf->Send);
+ }
+
+ /*
+ * Put back the destination table.
+ */
+ Assert(!pIf->pDstTab);
+ ASMAtomicWritePtr(&pIf->pDstTab, pDstTab);
+ }
+ else
+ rc = VERR_INTERNAL_ERROR_4;
+ }
+ else
+ rc = VERR_INTERNAL_ERROR_3;
+
+ /*
+ * Release the interface.
+ */
+ intnetR0BusyDecIf(pIf);
+ STAM_REL_PROFILE_STOP(&pIf->pIntBuf->StatSend1, a);
+ intnetR0IfRelease(pIf, pSession);
+ return rc;
+}
+
+
+/**
+ * VMMR0 request wrapper for IntNetR0IfSend.
+ *
+ * @returns see IntNetR0IfSend.
+ * @param pSession The caller's session.
+ * @param pReq The request packet.
+ */
+INTNETR0DECL(int) IntNetR0IfSendReq(PSUPDRVSESSION pSession, PINTNETIFSENDREQ pReq)
+{
+ if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
+ return VERR_INVALID_PARAMETER;
+ return IntNetR0IfSend(pReq->hIf, pSession);
+}
+
+
+/**
+ * Maps the default buffer into ring 3.
+ *
+ * @returns VBox status code.
+ * @param hIf The interface handle.
+ * @param pSession The caller's session.
+ * @param ppRing3Buf Where to store the address of the ring-3 mapping
+ * (optional).
+ * @param ppRing0Buf Where to store the address of the ring-0 mapping
+ * (optional).
+ */
+INTNETR0DECL(int) IntNetR0IfGetBufferPtrs(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession,
+ R3PTRTYPE(PINTNETBUF) *ppRing3Buf, R0PTRTYPE(PINTNETBUF) *ppRing0Buf)
+{
+ LogFlow(("IntNetR0IfGetBufferPtrs: hIf=%RX32 ppRing3Buf=%p ppRing0Buf=%p\n", hIf, ppRing3Buf, ppRing0Buf));
+
+ /*
+ * Validate input.
+ */
+ PINTNET pIntNet = g_pIntNet;
+ AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
+ AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
+
+ AssertPtrNullReturn(ppRing3Buf, VERR_INVALID_PARAMETER);
+ AssertPtrNullReturn(ppRing0Buf, VERR_INVALID_PARAMETER);
+ if (ppRing3Buf)
+ *ppRing3Buf = 0;
+ if (ppRing0Buf)
+ *ppRing0Buf = 0;
+
+ PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
+ if (!pIf)
+ return VERR_INVALID_HANDLE;
+
+ /*
+ * ASSUMES that only the process that created an interface can use it.
+ * ASSUMES that we created the ring-3 mapping when selecting or
+ * allocating the buffer.
+ */
+ int rc = RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ if (ppRing3Buf)
+ *ppRing3Buf = pIf->pIntBufR3;
+ if (ppRing0Buf)
+ *ppRing0Buf = (R0PTRTYPE(PINTNETBUF))pIf->pIntBuf; /* tstIntNetR0 mess */
+
+ rc = RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
+ }
+
+ intnetR0IfRelease(pIf, pSession);
+ LogFlow(("IntNetR0IfGetBufferPtrs: returns %Rrc *ppRing3Buf=%p *ppRing0Buf=%p\n",
+ rc, ppRing3Buf ? *ppRing3Buf : NIL_RTR3PTR, ppRing0Buf ? *ppRing0Buf : NIL_RTR0PTR));
+ return rc;
+}
+
+
+/**
+ * VMMR0 request wrapper for IntNetR0IfGetBufferPtrs.
+ *
+ * @returns see IntNetR0IfGetRing3Buffer.
+ * @param pSession The caller's session.
+ * @param pReq The request packet.
+ */
+INTNETR0DECL(int) IntNetR0IfGetBufferPtrsReq(PSUPDRVSESSION pSession, PINTNETIFGETBUFFERPTRSREQ pReq)
+{
+ if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
+ return VERR_INVALID_PARAMETER;
+ return IntNetR0IfGetBufferPtrs(pReq->hIf, pSession, &pReq->pRing3Buf, &pReq->pRing0Buf);
+}
+
+
+#if 0
+/**
+ * Gets the physical addresses of the default interface buffer.
+ *
+ * @returns VBox status code.
+ * @param hIF The interface handle.
+ * @param paPages Where to store the addresses. (The reserved fields will be set to zero.)
+ * @param cPages
+ */
+INTNETR0DECL(int) IntNetR0IfGetPhysBuffer(INTNETIFHANDLE hIf, PSUPPAGE paPages, unsigned cPages)
+{
+ /*
+ * Validate input.
+ */
+ PINTNET pIntNet = g_pIntNet;
+ AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
+ AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
+
+ AssertPtrReturn(paPages, VERR_INVALID_PARAMETER);
+ AssertPtrReturn((uint8_t *)&paPages[cPages] - 1, VERR_INVALID_PARAMETER);
+ PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
+ if (!pIf)
+ return VERR_INVALID_HANDLE;
+
+ /*
+ * Grab the lock and get the data.
+ * ASSUMES that the handle isn't closed while we're here.
+ */
+ int rc = RTSemFastMutexRequest(pIf->pNetwork->FastMutex);
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo make a SUPR0 api for obtaining the array. SUPR0/IPRT is keeping track of everything, there
+ * is no need for any extra bookkeeping here.. */
+
+ rc = RTSemFastMutexRelease(pIf->pNetwork->FastMutex);
+ }
+ intnetR0IfRelease(pIf, pSession);
+ return VERR_NOT_IMPLEMENTED;
+}
+#endif
+
+
+/**
+ * Sets the promiscuous mode property of an interface.
+ *
+ * @returns VBox status code.
+ * @param hIf The interface handle.
+ * @param pSession The caller's session.
+ * @param fPromiscuous Set if the interface should be in promiscuous mode, clear if not.
+ */
+INTNETR0DECL(int) IntNetR0IfSetPromiscuousMode(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fPromiscuous)
+{
+ LogFlow(("IntNetR0IfSetPromiscuousMode: hIf=%RX32 fPromiscuous=%d\n", hIf, fPromiscuous));
+
+ /*
+ * Validate & translate input.
+ */
+ PINTNET pIntNet = g_pIntNet;
+ AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
+ AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
+
+ PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
+ if (!pIf)
+ {
+ Log(("IntNetR0IfSetPromiscuousMode: returns VERR_INVALID_HANDLE\n"));
+ return VERR_INVALID_HANDLE;
+ }
+
+ /*
+ * Get the network, take the address spinlock, and make the change.
+ * Paranoia^2: Mark ourselves busy to prevent anything from being destroyed.
+ */
+ int rc = VINF_SUCCESS;
+ intnetR0BusyIncIf(pIf);
+ PINTNETNETWORK pNetwork = pIf->pNetwork;
+ if (pNetwork)
+ {
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ if (pIf->fPromiscuousReal != fPromiscuous)
+ {
+ const bool fPromiscuousEff = fPromiscuous
+ && (pIf->fOpenFlags & INTNET_OPEN_FLAGS_IF_PROMISC_ALLOW)
+ && (pNetwork->fFlags & INTNET_OPEN_FLAGS_PROMISC_ALLOW_CLIENTS);
+ Log(("IntNetR0IfSetPromiscuousMode: hIf=%RX32: Changed from %d -> %d (%d)\n",
+ hIf, !fPromiscuous, !!fPromiscuous, fPromiscuousEff));
+
+ pIf->fPromiscuousReal = fPromiscuous;
+
+ PINTNETMACTABENTRY pEntry = intnetR0NetworkFindMacAddrEntry(pNetwork, pIf); Assert(pEntry);
+ if (RT_LIKELY(pEntry))
+ {
+ if (pEntry->fPromiscuousEff)
+ {
+ pNetwork->MacTab.cPromiscuousEntries--;
+ if (!pEntry->fPromiscuousSeeTrunk)
+ pNetwork->MacTab.cPromiscuousNoTrunkEntries--;
+ Assert(pNetwork->MacTab.cPromiscuousEntries < pNetwork->MacTab.cEntries);
+ Assert(pNetwork->MacTab.cPromiscuousNoTrunkEntries < pNetwork->MacTab.cEntries);
+ }
+
+ pEntry->fPromiscuousEff = fPromiscuousEff;
+ pEntry->fPromiscuousSeeTrunk = fPromiscuousEff
+ && (pIf->fOpenFlags & INTNET_OPEN_FLAGS_IF_PROMISC_SEE_TRUNK);
+
+ if (pEntry->fPromiscuousEff)
+ {
+ pNetwork->MacTab.cPromiscuousEntries++;
+ if (!pEntry->fPromiscuousSeeTrunk)
+ pNetwork->MacTab.cPromiscuousNoTrunkEntries++;
+ }
+ Assert(pNetwork->MacTab.cPromiscuousEntries <= pNetwork->MacTab.cEntries);
+ Assert(pNetwork->MacTab.cPromiscuousNoTrunkEntries <= pNetwork->MacTab.cEntries);
+ }
+ }
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+
+ intnetR0BusyDecIf(pIf);
+ intnetR0IfRelease(pIf, pSession);
+ return rc;
+}
+
+
+/**
+ * VMMR0 request wrapper for IntNetR0IfSetPromiscuousMode.
+ *
+ * @returns see IntNetR0IfSetPromiscuousMode.
+ * @param pSession The caller's session.
+ * @param pReq The request packet.
+ */
+INTNETR0DECL(int) IntNetR0IfSetPromiscuousModeReq(PSUPDRVSESSION pSession, PINTNETIFSETPROMISCUOUSMODEREQ pReq)
+{
+ if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
+ return VERR_INVALID_PARAMETER;
+ return IntNetR0IfSetPromiscuousMode(pReq->hIf, pSession, pReq->fPromiscuous);
+}
+
+
+/**
+ * Sets the MAC address of an interface.
+ *
+ * @returns VBox status code.
+ * @param hIf The interface handle.
+ * @param pSession The caller's session.
+ * @param pMAC The new MAC address.
+ */
+INTNETR0DECL(int) IntNetR0IfSetMacAddress(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PCRTMAC pMac)
+{
+ LogFlow(("IntNetR0IfSetMacAddress: hIf=%RX32 pMac=%p:{%.6Rhxs}\n", hIf, pMac, pMac));
+
+ /*
+ * Validate & translate input.
+ */
+ PINTNET pIntNet = g_pIntNet;
+ AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
+ AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
+
+ AssertPtrReturn(pMac, VERR_INVALID_PARAMETER);
+ PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
+ if (!pIf)
+ {
+ Log(("IntNetR0IfSetMacAddress: returns VERR_INVALID_HANDLE\n"));
+ return VERR_INVALID_HANDLE;
+ }
+
+ /*
+ * Get the network, take the address spinlock, and make the change.
+ * Paranoia^2: Mark ourselves busy to prevent anything from being destroyed.
+ */
+ int rc = VINF_SUCCESS;
+ intnetR0BusyIncIf(pIf);
+ PINTNETNETWORK pNetwork = pIf->pNetwork;
+ if (pNetwork)
+ {
+ PINTNETTRUNKIF pTrunk = NULL;
+
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ if (memcmp(&pIf->MacAddr, pMac, sizeof(pIf->MacAddr)))
+ {
+ Log(("IntNetR0IfSetMacAddress: hIf=%RX32: Changed from %.6Rhxs -> %.6Rhxs\n",
+ hIf, &pIf->MacAddr, pMac));
+
+ /* Update the two copies. */
+ PINTNETMACTABENTRY pEntry = intnetR0NetworkFindMacAddrEntry(pNetwork, pIf); Assert(pEntry);
+ if (RT_LIKELY(pEntry))
+ pEntry->MacAddr = *pMac;
+ pIf->MacAddr = *pMac;
+ pIf->fMacSet = true;
+
+ /* Grab a busy reference to the trunk so we release the lock before notifying it. */
+ pTrunk = pNetwork->MacTab.pTrunk;
+ if (pTrunk)
+ intnetR0BusyIncTrunk(pTrunk);
+ }
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+
+ if (pTrunk)
+ {
+ Log(("IntNetR0IfSetMacAddress: pfnNotifyMacAddress hIf=%RX32\n", hIf));
+ PINTNETTRUNKIFPORT pIfPort = pTrunk->pIfPort;
+ if (pIfPort)
+ pIfPort->pfnNotifyMacAddress(pIfPort, pIf->pvIfData, pMac);
+ intnetR0BusyDecTrunk(pTrunk);
+ }
+ }
+ else
+ rc = VERR_WRONG_ORDER;
+
+ intnetR0BusyDecIf(pIf);
+ intnetR0IfRelease(pIf, pSession);
+ return rc;
+}
+
+
+/**
+ * VMMR0 request wrapper for IntNetR0IfSetMacAddress.
+ *
+ * @returns see IntNetR0IfSetMacAddress.
+ * @param pSession The caller's session.
+ * @param pReq The request packet.
+ */
+INTNETR0DECL(int) IntNetR0IfSetMacAddressReq(PSUPDRVSESSION pSession, PINTNETIFSETMACADDRESSREQ pReq)
+{
+ if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
+ return VERR_INVALID_PARAMETER;
+ return IntNetR0IfSetMacAddress(pReq->hIf, pSession, &pReq->Mac);
+}
+
+
+/**
+ * Worker for intnetR0IfSetActive and intnetR0IfDestruct.
+ *
+ * This function will update the active interface count on the network and
+ * activate or deactivate the trunk connection if necessary.
+ *
+ * The call must own the giant lock (we cannot take it here).
+ *
+ * @returns VBox status code.
+ * @param pNetwork The network.
+ * @param fIf The interface.
+ * @param fActive What to do.
+ */
+static int intnetR0NetworkSetIfActive(PINTNETNETWORK pNetwork, PINTNETIF pIf, bool fActive)
+{
+ /* quick sanity check */
+ AssertPtr(pNetwork);
+ AssertPtr(pIf);
+
+ /*
+ * The address spinlock of the network protects the variables, while the
+ * big lock protects the calling of pfnSetState. Grab both lock at once
+ * to save us the extra hassle.
+ */
+ PINTNETTRUNKIF pTrunk = NULL;
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ /*
+ * Do the update.
+ */
+ if (pIf->fActive != fActive)
+ {
+ PINTNETMACTABENTRY pEntry = intnetR0NetworkFindMacAddrEntry(pNetwork, pIf); Assert(pEntry);
+ if (RT_LIKELY(pEntry))
+ {
+ pEntry->fActive = fActive;
+ pIf->fActive = fActive;
+
+ if (fActive)
+ {
+ pNetwork->cActiveIFs++;
+ if (pNetwork->cActiveIFs == 1)
+ {
+ pTrunk = pNetwork->MacTab.pTrunk;
+ if (pTrunk)
+ {
+ pNetwork->MacTab.fHostActive = RT_BOOL(pNetwork->fFlags & INTNET_OPEN_FLAGS_TRUNK_HOST_ENABLED);
+ pNetwork->MacTab.fWireActive = RT_BOOL(pNetwork->fFlags & INTNET_OPEN_FLAGS_TRUNK_WIRE_ENABLED);
+ }
+ }
+ }
+ else
+ {
+ pNetwork->cActiveIFs--;
+ if (pNetwork->cActiveIFs == 0)
+ {
+ pTrunk = pNetwork->MacTab.pTrunk;
+ pNetwork->MacTab.fHostActive = false;
+ pNetwork->MacTab.fWireActive = false;
+ }
+ }
+ }
+ }
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+
+ /*
+ * Tell the trunk if necessary.
+ * The wait for !busy is for the Solaris streams trunk driver (mostly).
+ */
+ if (pTrunk && pTrunk->pIfPort)
+ {
+ if (!fActive)
+ intnetR0BusyWait(pNetwork, &pTrunk->cBusy);
+
+ pTrunk->pIfPort->pfnSetState(pTrunk->pIfPort, fActive ? INTNETTRUNKIFSTATE_ACTIVE : INTNETTRUNKIFSTATE_INACTIVE);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Sets the active property of an interface.
+ *
+ * @returns VBox status code.
+ * @param hIf The interface handle.
+ * @param pSession The caller's session.
+ * @param fActive The new state.
+ */
+INTNETR0DECL(int) IntNetR0IfSetActive(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fActive)
+{
+ LogFlow(("IntNetR0IfSetActive: hIf=%RX32 fActive=%RTbool\n", hIf, fActive));
+
+ /*
+ * Validate & translate input.
+ */
+ PINTNET pIntNet = g_pIntNet;
+ AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
+ AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
+
+ PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
+ if (!pIf)
+ {
+ Log(("IntNetR0IfSetActive: returns VERR_INVALID_HANDLE\n"));
+ return VERR_INVALID_HANDLE;
+ }
+
+ /*
+ * Hand it to the network since it might involve the trunk and things are
+ * tricky there wrt to locking order.
+ *
+ * 1. We take the giant lock here. This makes sure nobody is re-enabling
+ * the network while we're pausing it and vice versa. This also enables
+ * us to wait for the network to become idle before telling the trunk.
+ * (Important on Solaris.)
+ *
+ * 2. For paranoid reasons, we grab a busy reference to the calling
+ * interface. This is totally unnecessary but should hurt (when done
+ * after grabbing the giant lock).
+ */
+ int rc = RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
+ if (RT_SUCCESS(rc))
+ {
+ intnetR0BusyIncIf(pIf);
+
+ PINTNETNETWORK pNetwork = pIf->pNetwork;
+ if (pNetwork)
+ rc = intnetR0NetworkSetIfActive(pNetwork, pIf, fActive);
+ else
+ rc = VERR_WRONG_ORDER;
+
+ intnetR0BusyDecIf(pIf);
+ RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
+ }
+
+ intnetR0IfRelease(pIf, pSession);
+ LogFlow(("IntNetR0IfSetActive: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * VMMR0 request wrapper for IntNetR0IfSetActive.
+ *
+ * @returns see IntNetR0IfSetActive.
+ * @param pIntNet The internal networking instance.
+ * @param pSession The caller's session.
+ * @param pReq The request packet.
+ */
+INTNETR0DECL(int) IntNetR0IfSetActiveReq(PSUPDRVSESSION pSession, PINTNETIFSETACTIVEREQ pReq)
+{
+ if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
+ return VERR_INVALID_PARAMETER;
+ return IntNetR0IfSetActive(pReq->hIf, pSession, pReq->fActive);
+}
+
+
+/**
+ * Wait for the interface to get signaled.
+ * The interface will be signaled when is put into the receive buffer.
+ *
+ * @returns VBox status code.
+ * @param hIf The interface handle.
+ * @param pSession The caller's session.
+ * @param cMillies Number of milliseconds to wait. RT_INDEFINITE_WAIT should be
+ * used if indefinite wait is desired.
+ */
+INTNETR0DECL(int) IntNetR0IfWait(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, uint32_t cMillies)
+{
+ Log4(("IntNetR0IfWait: hIf=%RX32 cMillies=%u\n", hIf, cMillies));
+
+ /*
+ * Get and validate essential handles.
+ */
+ PINTNET pIntNet = g_pIntNet;
+ AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
+ AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
+
+ PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
+ if (!pIf)
+ {
+ Log(("IntNetR0IfWait: returns VERR_INVALID_HANDLE\n"));
+ return VERR_INVALID_HANDLE;
+ }
+
+#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3) && defined(IN_RING3)
+ AssertReleaseFailed(); /* Should never be called. */
+ RT_NOREF(cMillies);
+ return VERR_NOT_SUPPORTED;
+#else
+ const RTSEMEVENT hRecvEvent = pIf->hRecvEvent;
+ const bool fNoMoreWaits = ASMAtomicUoReadBool(&pIf->fNoMoreWaits);
+ RTNATIVETHREAD hDtorThrd;
+ ASMAtomicReadHandle(&pIf->hDestructorThread, &hDtorThrd);
+ if (hDtorThrd != NIL_RTNATIVETHREAD)
+ {
+ /* See IntNetR0IfAbortWait for an explanation of hDestructorThread. */
+ Log(("IntNetR0IfWait: returns VERR_SEM_DESTROYED\n"));
+ return VERR_SEM_DESTROYED;
+ }
+
+ /* Check whether further waits have been barred by IntNetR0IfAbortWait. */
+ int rc;
+ if ( !fNoMoreWaits
+ && hRecvEvent != NIL_RTSEMEVENT)
+ {
+ /*
+ * It is tempting to check if there is data to be read here,
+ * but the problem with such an approach is that it will cause
+ * one unnecessary supervisor->user->supervisor trip. There is
+ * already a slight risk for such, so no need to increase it.
+ */
+
+ /*
+ * Increment the number of waiters before starting the wait.
+ * Upon wakeup we must assert reality, checking that we're not
+ * already destroyed or in the process of being destroyed. This
+ * code must be aligned with the waiting code in intnetR0IfDestruct.
+ */
+ ASMAtomicIncU32(&pIf->cSleepers);
+ rc = RTSemEventWaitNoResume(hRecvEvent, cMillies);
+ if (pIf->hRecvEvent == hRecvEvent)
+ {
+ ASMAtomicDecU32(&pIf->cSleepers);
+ ASMAtomicReadHandle(&pIf->hDestructorThread, &hDtorThrd);
+ if (hDtorThrd == NIL_RTNATIVETHREAD)
+ {
+ if (intnetR0IfRelease(pIf, pSession))
+ rc = VERR_SEM_DESTROYED;
+ }
+ else
+ rc = VERR_SEM_DESTROYED;
+ }
+ else
+ rc = VERR_SEM_DESTROYED;
+ }
+ else
+ {
+ rc = VERR_SEM_DESTROYED;
+ intnetR0IfRelease(pIf, pSession);
+ }
+
+ Log4(("IntNetR0IfWait: returns %Rrc\n", rc));
+ return rc;
+#endif
+}
+
+
+/**
+ * VMMR0 request wrapper for IntNetR0IfWait.
+ *
+ * @returns see IntNetR0IfWait.
+ * @param pSession The caller's session.
+ * @param pReq The request packet.
+ */
+INTNETR0DECL(int) IntNetR0IfWaitReq(PSUPDRVSESSION pSession, PINTNETIFWAITREQ pReq)
+{
+ if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
+ return VERR_INVALID_PARAMETER;
+ return IntNetR0IfWait(pReq->hIf, pSession, pReq->cMillies);
+}
+
+
+/**
+ * Wake up any threads waiting on the interface.
+ *
+ * @returns VBox status code.
+ * @param hIf The interface handle.
+ * @param pSession The caller's session.
+ * @param fNoMoreWaits When set, no more waits are permitted.
+ */
+INTNETR0DECL(int) IntNetR0IfAbortWait(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, bool fNoMoreWaits)
+{
+ Log4(("IntNetR0IfAbortWait: hIf=%RX32 fNoMoreWaits=%RTbool\n", hIf, fNoMoreWaits));
+
+ /*
+ * Get and validate essential handles.
+ */
+ PINTNET pIntNet = g_pIntNet;
+ AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
+ AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
+
+ PINTNETIF pIf = (PINTNETIF)RTHandleTableLookupWithCtx(pIntNet->hHtIfs, hIf, pSession);
+ if (!pIf)
+ {
+ Log(("IntNetR0IfAbortWait: returns VERR_INVALID_HANDLE\n"));
+ return VERR_INVALID_HANDLE;
+ }
+
+#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3) && defined(IN_RING3)
+ AssertReleaseFailed();
+ RT_NOREF(fNoMoreWaits);
+ return VERR_NOT_SUPPORTED;
+#else
+ const RTSEMEVENT hRecvEvent = pIf->hRecvEvent;
+ RTNATIVETHREAD hDtorThrd;
+ ASMAtomicReadHandle(&pIf->hDestructorThread, &hDtorThrd);
+ if (hDtorThrd != NIL_RTNATIVETHREAD)
+ {
+ /* This can only happen if we for some reason race SUPDRVSESSION cleanup,
+ i.e. the object count is set to zero without yet having removed it from
+ the object table, so we got a spurious "reference". We must drop that
+ reference and let the destructor get on with its work. (Not entirely sure
+ if this is practically possible on any of the platforms, i.e. whether it's
+ we can actually close a SUPDrv handle/descriptor with active threads still
+ in NtDeviceIoControlFile/ioctl, but better safe than sorry.) */
+ Log(("IntNetR0IfAbortWait: returns VERR_SEM_DESTROYED\n"));
+ return VERR_SEM_DESTROYED;
+ }
+
+ /* a bit of paranoia */
+ int rc = VINF_SUCCESS;
+ if (hRecvEvent != NIL_RTSEMEVENT)
+ {
+ /*
+ * Set fNoMoreWaits if requested to do so and then wake up all the sleeping
+ * threads (usually just one). We leave the semaphore in the signalled
+ * state so the next caller will return immediately.
+ */
+ if (fNoMoreWaits)
+ ASMAtomicWriteBool(&pIf->fNoMoreWaits, true);
+
+ uint32_t cSleepers = ASMAtomicReadU32(&pIf->cSleepers) + 1;
+ while (cSleepers-- > 0)
+ {
+ int rc2 = RTSemEventSignal(pIf->hRecvEvent);
+ AssertRC(rc2);
+ }
+ }
+ else
+ rc = VERR_SEM_DESTROYED;
+
+ intnetR0IfRelease(pIf, pSession);
+
+ Log4(("IntNetR0IfWait: returns %Rrc\n", VINF_SUCCESS));
+ return VINF_SUCCESS;
+#endif
+}
+
+
+/**
+ * VMMR0 request wrapper for IntNetR0IfAbortWait.
+ *
+ * @returns see IntNetR0IfWait.
+ * @param pSession The caller's session.
+ * @param pReq The request packet.
+ */
+INTNETR0DECL(int) IntNetR0IfAbortWaitReq(PSUPDRVSESSION pSession, PINTNETIFABORTWAITREQ pReq)
+{
+ if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
+ return VERR_INVALID_PARAMETER;
+ return IntNetR0IfAbortWait(pReq->hIf, pSession, pReq->fNoMoreWaits);
+}
+
+
+/**
+ * Close an interface.
+ *
+ * @returns VBox status code.
+ * @param pIntNet The instance handle.
+ * @param hIf The interface handle.
+ * @param pSession The caller's session.
+ */
+INTNETR0DECL(int) IntNetR0IfClose(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession)
+{
+ LogFlow(("IntNetR0IfClose: hIf=%RX32\n", hIf));
+
+ /*
+ * Validate and free the handle.
+ */
+ PINTNET pIntNet = g_pIntNet;
+ AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
+ AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
+
+ PINTNETIF pIf = (PINTNETIF)RTHandleTableFreeWithCtx(pIntNet->hHtIfs, hIf, pSession);
+ if (!pIf)
+ return VERR_INVALID_HANDLE;
+
+ /* Mark the handle as freed so intnetR0IfDestruct won't free it again. */
+ ASMAtomicWriteU32(&pIf->hIf, INTNET_HANDLE_INVALID);
+
+#if !defined(VBOX_WITH_INTNET_SERVICE_IN_R3) || !defined(IN_RING3)
+ /*
+ * Signal the event semaphore to wake up any threads in IntNetR0IfWait
+ * and give them a moment to get out and release the interface.
+ */
+ uint32_t i = pIf->cSleepers;
+ while (i-- > 0)
+ {
+ RTSemEventSignal(pIf->hRecvEvent);
+ RTThreadYield();
+ }
+ RTSemEventSignal(pIf->hRecvEvent);
+#endif
+
+ /*
+ * Release the references to the interface object (handle + free lookup).
+ */
+ void *pvObj = pIf->pvObj;
+ intnetR0IfRelease(pIf, pSession); /* (RTHandleTableFreeWithCtx) */
+
+ int rc = SUPR0ObjRelease(pvObj, pSession);
+ LogFlow(("IntNetR0IfClose: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * VMMR0 request wrapper for IntNetR0IfCloseReq.
+ *
+ * @returns see IntNetR0IfClose.
+ * @param pSession The caller's session.
+ * @param pReq The request packet.
+ */
+INTNETR0DECL(int) IntNetR0IfCloseReq(PSUPDRVSESSION pSession, PINTNETIFCLOSEREQ pReq)
+{
+ if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
+ return VERR_INVALID_PARAMETER;
+ return IntNetR0IfClose(pReq->hIf, pSession);
+}
+
+
+/**
+ * Interface destructor callback.
+ * This is called for reference counted objectes when the count reaches 0.
+ *
+ * @param pvObj The object pointer.
+ * @param pvUser1 Pointer to the interface.
+ * @param pvUser2 Pointer to the INTNET instance data.
+ */
+static DECLCALLBACK(void) intnetR0IfDestruct(void *pvObj, void *pvUser1, void *pvUser2)
+{
+ PINTNETIF pIf = (PINTNETIF)pvUser1;
+ PINTNET pIntNet = (PINTNET)pvUser2;
+ Log(("intnetR0IfDestruct: pvObj=%p pIf=%p pIntNet=%p hIf=%RX32\n", pvObj, pIf, pIntNet, pIf->hIf));
+ RT_NOREF1(pvObj);
+
+ /*
+ * For paranoid reasons we must now mark the interface as destroyed.
+ * This is so that any waiting threads can take evasive action (kind
+ * of theoretical case), and we can reject everyone else referencing
+ * the object via the handle table before we get around to removing it.
+ */
+ ASMAtomicWriteHandle(&pIf->hDestructorThread, RTThreadNativeSelf());
+
+ /*
+ * We grab the INTNET create/open/destroy semaphore to make sure nobody is
+ * adding or removing interfaces while we're in here.
+ */
+ RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
+
+ /*
+ * Delete the interface handle so the object no longer can be used.
+ * (Can happen if the client didn't close its session.)
+ */
+ INTNETIFHANDLE hIf = ASMAtomicXchgU32(&pIf->hIf, INTNET_HANDLE_INVALID);
+ if (hIf != INTNET_HANDLE_INVALID)
+ {
+ void *pvObj2 = RTHandleTableFreeWithCtx(pIntNet->hHtIfs, hIf, pIf->pSession); NOREF(pvObj2);
+ AssertMsg(pvObj2 == pIf, ("%p, %p, hIf=%RX32 pSession=%p\n", pvObj2, pIf, hIf, pIf->pSession));
+ }
+
+ /*
+ * If we've got a network deactivate and detach ourselves from it. Because
+ * of cleanup order we might have been orphaned by the network destructor.
+ */
+ PINTNETNETWORK pNetwork = pIf->pNetwork;
+ if (pNetwork)
+ {
+ /* set inactive. */
+ intnetR0NetworkSetIfActive(pNetwork, pIf, false /*fActive*/);
+
+ /* remove ourselves from the switch table. */
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ uint32_t iIf = pNetwork->MacTab.cEntries;
+ while (iIf-- > 0)
+ if (pNetwork->MacTab.paEntries[iIf].pIf == pIf)
+ {
+ if (pNetwork->MacTab.paEntries[iIf].fPromiscuousEff)
+ {
+ pNetwork->MacTab.cPromiscuousEntries--;
+ if (!pNetwork->MacTab.paEntries[iIf].fPromiscuousSeeTrunk)
+ pNetwork->MacTab.cPromiscuousNoTrunkEntries--;
+ }
+ Assert(pNetwork->MacTab.cPromiscuousEntries < pNetwork->MacTab.cEntries);
+ Assert(pNetwork->MacTab.cPromiscuousNoTrunkEntries < pNetwork->MacTab.cEntries);
+
+ if (iIf + 1 < pNetwork->MacTab.cEntries)
+ memmove(&pNetwork->MacTab.paEntries[iIf],
+ &pNetwork->MacTab.paEntries[iIf + 1],
+ (pNetwork->MacTab.cEntries - iIf - 1) * sizeof(pNetwork->MacTab.paEntries[0]));
+ pNetwork->MacTab.cEntries--;
+ break;
+ }
+
+ /* recalc the min flags. */
+ if (pIf->fOpenFlags & INTNET_OPEN_FLAGS_REQUIRE_AS_RESTRICTIVE_POLICIES)
+ {
+ uint32_t fMinFlags = 0;
+ iIf = pNetwork->MacTab.cEntries;
+ while (iIf-- > 0)
+ {
+ PINTNETIF pIf2 = pNetwork->MacTab.paEntries[iIf].pIf;
+ if ( pIf2 /* paranoia */
+ && (pIf2->fOpenFlags & INTNET_OPEN_FLAGS_REQUIRE_AS_RESTRICTIVE_POLICIES))
+ fMinFlags |= pIf2->fOpenFlags & INTNET_OPEN_FLAGS_STRICT_MASK;
+ }
+ pNetwork->fMinFlags = fMinFlags;
+ }
+
+ PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+
+ /* Notify the trunk about the interface being destroyed. */
+ if (pTrunk && pTrunk->pIfPort)
+ pTrunk->pIfPort->pfnDisconnectInterface(pTrunk->pIfPort, pIf->pvIfData);
+
+ /* Wait for the interface to quiesce while we still can. */
+ intnetR0BusyWait(pNetwork, &pIf->cBusy);
+
+ /* Release our reference to the network. */
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+ pIf->pNetwork = NULL;
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+
+ SUPR0ObjRelease(pNetwork->pvObj, pIf->pSession);
+ }
+
+ RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
+
+#if !defined(VBOX_WITH_INTNET_SERVICE_IN_R3) || !defined(IN_RING3)
+ /*
+ * Wakeup anyone waiting on this interface. (Kind of unlikely, but perhaps
+ * not quite impossible.)
+ *
+ * We *must* make sure they have woken up properly and realized
+ * that the interface is no longer valid.
+ */
+ if (pIf->hRecvEvent != NIL_RTSEMEVENT)
+ {
+ RTSEMEVENT hRecvEvent = pIf->hRecvEvent;
+ unsigned cMaxWait = 0x1000;
+ while (pIf->cSleepers && cMaxWait-- > 0)
+ {
+ RTSemEventSignal(hRecvEvent);
+ RTThreadYield();
+ }
+ if (pIf->cSleepers)
+ {
+ RTThreadSleep(1);
+
+ cMaxWait = pIf->cSleepers;
+ while (pIf->cSleepers && cMaxWait-- > 0)
+ {
+ RTSemEventSignal(hRecvEvent);
+ RTThreadSleep(10);
+ }
+ }
+
+ RTSemEventDestroy(hRecvEvent);
+ pIf->hRecvEvent = NIL_RTSEMEVENT;
+ }
+#endif
+
+ /*
+ * Unmap user buffer.
+ */
+ if (pIf->pIntBuf != pIf->pIntBufDefault)
+ {
+ /** @todo user buffer */
+ }
+
+ /*
+ * Unmap and Free the default buffer.
+ */
+ if (pIf->pIntBufDefault)
+ {
+ SUPR0MemFree(pIf->pSession, (RTHCUINTPTR)pIf->pIntBufDefault);
+ pIf->pIntBufDefault = NULL;
+ pIf->pIntBufDefaultR3 = 0;
+ pIf->pIntBuf = NULL;
+ pIf->pIntBufR3 = 0;
+ }
+
+ /*
+ * Free remaining resources
+ */
+ RTSpinlockDestroy(pIf->hRecvInSpinlock);
+ pIf->hRecvInSpinlock = NIL_RTSPINLOCK;
+
+ RTMemFree(pIf->pDstTab);
+ pIf->pDstTab = NULL;
+
+ for (int i = kIntNetAddrType_Invalid + 1; i < kIntNetAddrType_End; i++)
+ intnetR0IfAddrCacheDestroy(&pIf->aAddrCache[i]);
+
+ pIf->pvObj = NULL;
+ RTMemFree(pIf);
+}
+
+
+/* Forward declaration of trunk reconnection thread function. */
+static DECLCALLBACK(int) intnetR0TrunkReconnectThread(RTTHREAD hThread, void *pvUser);
+
+/**
+ * Creates a new network interface.
+ *
+ * The call must have opened the network for the new interface and is
+ * responsible for closing it on failure. On success it must leave the network
+ * opened so the interface destructor can close it.
+ *
+ * @returns VBox status code.
+ * @param pNetwork The network, referenced. The reference is consumed
+ * on success.
+ * @param pSession The session handle.
+ * @param cbSend The size of the send buffer.
+ * @param cbRecv The size of the receive buffer.
+ * @param fFlags The open network flags.
+ * @param pfnRecvAvail The receive available callback to call instead of
+ * signalling the semaphore (R3 service only).
+ * @param pvUser The opaque user data to pass to the callback.
+ * @param phIf Where to store the interface handle.
+ */
+static int intnetR0NetworkCreateIf(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession, unsigned cbSend, unsigned cbRecv,
+ uint32_t fFlags, PFNINTNETIFRECVAVAIL pfnRecvAvail, void *pvUser, PINTNETIFHANDLE phIf)
+{
+ LogFlow(("intnetR0NetworkCreateIf: pNetwork=%p pSession=%p cbSend=%u cbRecv=%u fFlags=%#x phIf=%p\n",
+ pNetwork, pSession, cbSend, cbRecv, fFlags, phIf));
+
+ /*
+ * Assert input.
+ */
+ AssertPtr(pNetwork);
+ AssertPtr(phIf);
+#if !defined(VBOX_WITH_INTNET_SERVICE_IN_R3) || !defined(IN_RING3)
+ Assert(pfnRecvAvail == NULL);
+ Assert(pvUser == NULL);
+ RT_NOREF(pfnRecvAvail, pvUser);
+#endif
+
+ /*
+ * Adjust the flags with defaults for the interface policies.
+ * Note: Main restricts promiscuous mode per interface.
+ */
+ uint32_t const fDefFlags = INTNET_OPEN_FLAGS_IF_PROMISC_ALLOW
+ | INTNET_OPEN_FLAGS_IF_PROMISC_SEE_TRUNK;
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_afIntNetOpenNetworkIfFlags); i++)
+ if (!(fFlags & g_afIntNetOpenNetworkIfFlags[i].fPair))
+ fFlags |= g_afIntNetOpenNetworkIfFlags[i].fPair & fDefFlags;
+
+ /*
+ * Make sure that all destination tables as well as the have space of
+ */
+ int rc = intnetR0NetworkEnsureTabSpace(pNetwork);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Allocate the interface and initialize it.
+ */
+ PINTNETIF pIf = (PINTNETIF)RTMemAllocZ(sizeof(*pIf));
+ if (!pIf)
+ return VERR_NO_MEMORY;
+
+ memset(&pIf->MacAddr, 0xff, sizeof(pIf->MacAddr)); /* broadcast */
+ //pIf->fMacSet = false;
+ //pIf->fPromiscuousReal = false;
+ //pIf->fActive = false;
+ //pIf->fNoMoreWaits = false;
+ pIf->fOpenFlags = fFlags;
+ //pIf->cYields = 0;
+ //pIf->pIntBuf = 0;
+ //pIf->pIntBufR3 = NIL_RTR3PTR;
+ //pIf->pIntBufDefault = 0;
+ //pIf->pIntBufDefaultR3 = NIL_RTR3PTR;
+#if !defined(VBOX_WITH_INTNET_SERVICE_IN_R3) || !defined(IN_RING3)
+ pIf->hRecvEvent = NIL_RTSEMEVENT;
+#else
+ pIf->pfnRecvAvail = pfnRecvAvail;
+ pIf->pvUserRecvAvail = pvUser;
+#endif
+ //pIf->cSleepers = 0;
+ pIf->hIf = INTNET_HANDLE_INVALID;
+ pIf->hDestructorThread = NIL_RTNATIVETHREAD;
+ pIf->pNetwork = pNetwork;
+ pIf->pSession = pSession;
+ //pIf->pvObj = NULL;
+ //pIf->aAddrCache = {0};
+ pIf->hRecvInSpinlock = NIL_RTSPINLOCK;
+ pIf->cBusy = 0;
+ //pIf->pDstTab = NULL;
+ //pIf->pvIfData = NULL;
+
+ for (int i = kIntNetAddrType_Invalid + 1; i < kIntNetAddrType_End && RT_SUCCESS(rc); i++)
+ rc = intnetR0IfAddrCacheInit(&pIf->aAddrCache[i], (INTNETADDRTYPE)i,
+ !!(pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE));
+ if (RT_SUCCESS(rc))
+ rc = intnetR0AllocDstTab(pNetwork->MacTab.cEntriesAllocated, (PINTNETDSTTAB *)&pIf->pDstTab);
+#if !defined(VBOX_WITH_INTNET_SERVICE_IN_R3) || !defined(IN_RING3)
+ if (RT_SUCCESS(rc))
+ rc = RTSemEventCreate((PRTSEMEVENT)&pIf->hRecvEvent);
+#endif
+ if (RT_SUCCESS(rc))
+ rc = RTSpinlockCreate(&pIf->hRecvInSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "hRecvInSpinlock");
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Create the default buffer.
+ */
+ /** @todo adjust with minimums and apply defaults here. */
+ cbRecv = RT_ALIGN(RT_MAX(cbRecv, sizeof(INTNETHDR) * 4), INTNETRINGBUF_ALIGNMENT);
+ cbSend = RT_ALIGN(RT_MAX(cbSend, sizeof(INTNETHDR) * 4), INTNETRINGBUF_ALIGNMENT);
+ const unsigned cbBuf = RT_ALIGN(sizeof(*pIf->pIntBuf), INTNETRINGBUF_ALIGNMENT) + cbRecv + cbSend;
+ rc = SUPR0MemAlloc(pIf->pSession, cbBuf, (PRTR0PTR)&pIf->pIntBufDefault, (PRTR3PTR)&pIf->pIntBufDefaultR3);
+ if (RT_SUCCESS(rc))
+ {
+ ASMMemZero32(pIf->pIntBufDefault, cbBuf); /** @todo I thought I specified these buggers as clearing the memory... */
+
+ pIf->pIntBuf = pIf->pIntBufDefault;
+ pIf->pIntBufR3 = pIf->pIntBufDefaultR3;
+ IntNetBufInit(pIf->pIntBuf, cbBuf, cbRecv, cbSend);
+
+ /*
+ * Register the interface with the session and create a handle for it.
+ */
+ pIf->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK_INTERFACE,
+ intnetR0IfDestruct, pIf, pNetwork->pIntNet);
+ if (pIf->pvObj)
+ {
+ rc = RTHandleTableAllocWithCtx(pNetwork->pIntNet->hHtIfs, pIf, pSession, (uint32_t *)&pIf->hIf);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Finally add the interface to the network, consuming the
+ * network reference of the caller.
+ */
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ uint32_t iIf = pNetwork->MacTab.cEntries;
+ Assert(iIf + 1 <= pNetwork->MacTab.cEntriesAllocated);
+
+ pNetwork->MacTab.paEntries[iIf].MacAddr = pIf->MacAddr;
+ pNetwork->MacTab.paEntries[iIf].fActive = false;
+ pNetwork->MacTab.paEntries[iIf].fPromiscuousEff = false;
+ pNetwork->MacTab.paEntries[iIf].fPromiscuousSeeTrunk = false;
+ pNetwork->MacTab.paEntries[iIf].pIf = pIf;
+
+ pNetwork->MacTab.cEntries = iIf + 1;
+ pIf->pNetwork = pNetwork;
+
+ /*
+ * Grab a busy reference (paranoia) to the trunk before releasing
+ * the spinlock and then notify it about the new interface.
+ */
+ PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
+ if (pTrunk)
+ intnetR0BusyIncTrunk(pTrunk);
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+
+ if (pTrunk)
+ {
+ Log(("intnetR0NetworkCreateIf: pfnConnectInterface hIf=%RX32\n", pIf->hIf));
+ if (pTrunk->pIfPort)
+ rc = pTrunk->pIfPort->pfnConnectInterface(pTrunk->pIfPort, pIf, &pIf->pvIfData);
+ intnetR0BusyDecTrunk(pTrunk);
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * We're good!
+ */
+ *phIf = pIf->hIf;
+ Log(("intnetR0NetworkCreateIf: returns VINF_SUCCESS *phIf=%RX32 cbSend=%u cbRecv=%u cbBuf=%u\n",
+ *phIf, pIf->pIntBufDefault->cbSend, pIf->pIntBufDefault->cbRecv, pIf->pIntBufDefault->cbBuf));
+ return VINF_SUCCESS;
+ }
+ }
+
+ SUPR0ObjAddRef(pNetwork->pvObj, pSession);
+ SUPR0ObjRelease(pIf->pvObj, pSession);
+ LogFlow(("intnetR0NetworkCreateIf: returns %Rrc\n", rc));
+ return rc;
+ }
+
+ /* clean up */
+ SUPR0MemFree(pIf->pSession, (RTHCUINTPTR)pIf->pIntBufDefault);
+ pIf->pIntBufDefault = NULL;
+ pIf->pIntBuf = NULL;
+ }
+ }
+
+ RTSpinlockDestroy(pIf->hRecvInSpinlock);
+ pIf->hRecvInSpinlock = NIL_RTSPINLOCK;
+#if !defined(VBOX_WITH_INTNET_SERVICE_IN_R3) || !defined(IN_RING3)
+ RTSemEventDestroy(pIf->hRecvEvent);
+ pIf->hRecvEvent = NIL_RTSEMEVENT;
+#else
+ pIf->pfnRecvAvail = NULL;
+ pIf->pvUserRecvAvail = NULL;
+#endif
+ RTMemFree(pIf->pDstTab);
+ for (int i = kIntNetAddrType_Invalid + 1; i < kIntNetAddrType_End; i++)
+ intnetR0IfAddrCacheDestroy(&pIf->aAddrCache[i]);
+ RTMemFree(pIf);
+ LogFlow(("intnetR0NetworkCreateIf: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+/** @interface_method_impl{INTNETTRUNKSWPORT,pfnSetSGPhys} */
+static DECLCALLBACK(bool) intnetR0TrunkIfPortSetSGPhys(PINTNETTRUNKSWPORT pSwitchPort, bool fEnable)
+{
+ PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
+ AssertMsgFailed(("Not implemented because it wasn't required on Darwin\n"));
+ return ASMAtomicXchgBool(&pThis->fPhysSG, fEnable);
+}
+
+
+/** @interface_method_impl{INTNETTRUNKSWPORT,pfnReportMacAddress} */
+static DECLCALLBACK(void) intnetR0TrunkIfPortReportMacAddress(PINTNETTRUNKSWPORT pSwitchPort, PCRTMAC pMacAddr)
+{
+ PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
+
+ /*
+ * Get the network instance and grab the address spinlock before making
+ * any changes.
+ */
+ intnetR0BusyIncTrunk(pThis);
+ PINTNETNETWORK pNetwork = pThis->pNetwork;
+ if (pNetwork)
+ {
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ pNetwork->MacTab.HostMac = *pMacAddr;
+ pThis->MacAddr = *pMacAddr;
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+ }
+ else
+ pThis->MacAddr = *pMacAddr;
+ intnetR0BusyDecTrunk(pThis);
+}
+
+
+/** @interface_method_impl{INTNETTRUNKSWPORT,pfnReportPromiscuousMode} */
+static DECLCALLBACK(void) intnetR0TrunkIfPortReportPromiscuousMode(PINTNETTRUNKSWPORT pSwitchPort, bool fPromiscuous)
+{
+ PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
+
+ /*
+ * Get the network instance and grab the address spinlock before making
+ * any changes.
+ */
+ intnetR0BusyIncTrunk(pThis);
+ PINTNETNETWORK pNetwork = pThis->pNetwork;
+ if (pNetwork)
+ {
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ pNetwork->MacTab.fHostPromiscuousReal = fPromiscuous
+ || (pNetwork->fFlags & INTNET_OPEN_FLAGS_TRUNK_HOST_PROMISC_MODE);
+ pNetwork->MacTab.fHostPromiscuousEff = pNetwork->MacTab.fHostPromiscuousReal
+ && (pNetwork->fFlags & INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_HOST);
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+ }
+ intnetR0BusyDecTrunk(pThis);
+}
+
+
+/** @interface_method_impl{INTNETTRUNKSWPORT,pfnReportGsoCapabilities} */
+static DECLCALLBACK(void) intnetR0TrunkIfPortReportGsoCapabilities(PINTNETTRUNKSWPORT pSwitchPort,
+ uint32_t fGsoCapabilities, uint32_t fDst)
+{
+ PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
+
+ for (unsigned iBit = PDMNETWORKGSOTYPE_END; iBit < 32; iBit++)
+ Assert(!(fGsoCapabilities & RT_BIT_32(iBit)));
+ Assert(!(fDst & ~INTNETTRUNKDIR_VALID_MASK));
+ Assert(fDst);
+
+ if (fDst & INTNETTRUNKDIR_HOST)
+ pThis->fHostGsoCapabilites = fGsoCapabilities;
+
+ if (fDst & INTNETTRUNKDIR_WIRE)
+ pThis->fWireGsoCapabilites = fGsoCapabilities;
+}
+
+
+/** @interface_method_impl{INTNETTRUNKSWPORT,pfnReportNoPreemptDsts} */
+static DECLCALLBACK(void) intnetR0TrunkIfPortReportNoPreemptDsts(PINTNETTRUNKSWPORT pSwitchPort, uint32_t fNoPreemptDsts)
+{
+ PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
+ Assert(!(fNoPreemptDsts & ~INTNETTRUNKDIR_VALID_MASK));
+
+ pThis->fNoPreemptDsts = fNoPreemptDsts;
+}
+
+
+/** @interface_method_impl{INTNETTRUNKSWPORT,pfnDisconnect} */
+static DECLCALLBACK(void) intnetR0TrunkIfPortDisconnect(PINTNETTRUNKSWPORT pSwitchPort, PINTNETTRUNKIFPORT pIfPort,
+ PFNINTNETTRUNKIFPORTRELEASEBUSY pfnReleaseBusy)
+{
+ PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
+
+ /*
+ * The caller has marked the trunk instance busy on his side before making
+ * the call (see method docs) to let us safely grab the network and internal
+ * network instance pointers without racing the network destruction code
+ * (intnetR0TrunkIfDestroy (called by intnetR0TrunkIfDestroy) will wait for
+ * the interface to stop being busy before setting pNetwork to NULL and
+ * freeing up the resources).
+ */
+ PINTNETNETWORK pNetwork = pThis->pNetwork;
+ if (pNetwork)
+ {
+ PINTNET pIntNet = pNetwork->pIntNet;
+ Assert(pNetwork->pIntNet);
+
+ /*
+ * We must decrease the callers busy count here to prevent deadlocking
+ * when requesting the big mutex ownership. This will of course
+ * unblock anyone stuck in intnetR0TrunkIfDestroy doing pfnWaitForIdle
+ * (the other deadlock party), so we have to revalidate the network
+ * pointer after taking ownership of the big mutex.
+ */
+ if (pfnReleaseBusy)
+ pfnReleaseBusy(pIfPort);
+
+ RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
+
+ if (intnetR0NetworkIsValid(pIntNet, pNetwork))
+ {
+ Assert(pNetwork->MacTab.pTrunk == pThis); /* Must be valid as long as tehre are no concurrent calls to this method. */
+ Assert(pThis->pIfPort == pIfPort); /* Ditto */
+
+ /*
+ * Disconnect the trunk and destroy it, similar to what is done int
+ * intnetR0NetworkDestruct.
+ */
+ pIfPort->pfnSetState(pIfPort, INTNETTRUNKIFSTATE_DISCONNECTING);
+
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+ pNetwork->MacTab.pTrunk = NULL;
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+
+ /*
+ * Create a system thread that will attempt to re-connect this trunk periodically
+ * hoping that the corresponding filter module reappears in the system. The thread
+ * will go away if it succeeds in re-connecting the trunk or if it is signalled.
+ */
+ int rc = RTThreadCreate(&pNetwork->hTrunkReconnectThread, intnetR0TrunkReconnectThread, pNetwork,
+ 0, RTTHREADTYPE_INFREQUENT_POLLER, RTTHREADFLAGS_WAITABLE, "TRNKRECON");
+ AssertRC(rc);
+
+ intnetR0TrunkIfDestroy(pThis, pNetwork);
+ }
+
+ RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
+ }
+ /*
+ * We must always release the busy reference.
+ */
+ else if (pfnReleaseBusy)
+ pfnReleaseBusy(pIfPort);
+}
+
+
+/** @interface_method_impl{INTNETTRUNKSWPORT,pfnPreRecv} */
+static DECLCALLBACK(INTNETSWDECISION) intnetR0TrunkIfPortPreRecv(PINTNETTRUNKSWPORT pSwitchPort,
+ void const *pvSrc, size_t cbSrc, uint32_t fSrc)
+{
+ PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
+
+ /* assert some sanity */
+ AssertPtr(pvSrc);
+ AssertReturn(cbSrc >= 6, INTNETSWDECISION_BROADCAST);
+ Assert(fSrc);
+
+ /*
+ * Mark the trunk as busy, make sure we've got a network and that there are
+ * some active interfaces around.
+ */
+ INTNETSWDECISION enmSwDecision = INTNETSWDECISION_TRUNK;
+ intnetR0BusyIncTrunk(pThis);
+ PINTNETNETWORK pNetwork = pThis->pNetwork;
+ if (RT_LIKELY( pNetwork
+ && pNetwork->cActiveIFs > 0 ))
+ {
+ /*
+ * Lazy bird! No pre-switching of multicast and shared-MAC-on-wire.
+ */
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvSrc;
+ if (intnetR0IsMacAddrMulticast(&pEthHdr->DstMac))
+ enmSwDecision = INTNETSWDECISION_BROADCAST;
+ else if ( fSrc == INTNETTRUNKDIR_WIRE
+ && (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE))
+ enmSwDecision = INTNETSWDECISION_BROADCAST;
+ else
+ enmSwDecision = intnetR0NetworkPreSwitchUnicast(pNetwork,
+ fSrc,
+ cbSrc >= 12 ? &pEthHdr->SrcMac : NULL,
+ &pEthHdr->DstMac);
+ }
+
+ intnetR0BusyDecTrunk(pThis);
+ return enmSwDecision;
+}
+
+
+/** @interface_method_impl{INTNETTRUNKSWPORT,pfnRecv} */
+static DECLCALLBACK(bool) intnetR0TrunkIfPortRecv(PINTNETTRUNKSWPORT pSwitchPort, void *pvIf, PINTNETSG pSG, uint32_t fSrc)
+{
+ PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
+
+ /* assert some sanity */
+ AssertPtr(pSG);
+ Assert(fSrc);
+ NOREF(pvIf); /* later */
+
+ /*
+ * Mark the trunk as busy, make sure we've got a network and that there are
+ * some active interfaces around.
+ */
+ bool fRc = false /* don't drop it */;
+ intnetR0BusyIncTrunk(pThis);
+ PINTNETNETWORK pNetwork = pThis->pNetwork;
+ if (RT_LIKELY( pNetwork
+ && pNetwork->cActiveIFs > 0 ))
+ {
+ /*
+ * Grab or allocate a destination table.
+ */
+ bool const fIntCtx = RTThreadPreemptIsEnabled(NIL_RTTHREAD) || RTThreadIsInInterrupt(NIL_RTTHREAD);
+ unsigned iDstTab = 0;
+ PINTNETDSTTAB pDstTab = NULL;
+ RTSpinlockAcquire(pThis->hDstTabSpinlock);
+ if (fIntCtx)
+ {
+ /* Interrupt or restricted context. */
+ iDstTab = RTMpCpuIdToSetIndex(RTMpCpuId());
+ iDstTab %= pThis->cIntDstTabs;
+ pDstTab = pThis->apIntDstTabs[iDstTab];
+ if (RT_LIKELY(pDstTab))
+ pThis->apIntDstTabs[iDstTab] = NULL;
+ else
+ {
+ iDstTab = pThis->cIntDstTabs;
+ while (iDstTab-- > 0)
+ {
+ pDstTab = pThis->apIntDstTabs[iDstTab];
+ if (pDstTab)
+ {
+ pThis->apIntDstTabs[iDstTab] = NULL;
+ break;
+ }
+ }
+ }
+ RTSpinlockRelease(pThis->hDstTabSpinlock);
+ Assert(!pDstTab || iDstTab < pThis->cIntDstTabs);
+ }
+ else
+ {
+ /* Task context, fallback is to allocate a table. */
+ AssertCompile(RT_ELEMENTS(pThis->apTaskDstTabs) == 2); /* for loop rollout */
+ pDstTab = pThis->apIntDstTabs[iDstTab = 0];
+ if (!pDstTab)
+ pDstTab = pThis->apIntDstTabs[iDstTab = 1];
+ if (pDstTab)
+ {
+ pThis->apIntDstTabs[iDstTab] = NULL;
+ RTSpinlockRelease(pThis->hDstTabSpinlock);
+ Assert(iDstTab < RT_ELEMENTS(pThis->apTaskDstTabs));
+ }
+ else
+ {
+ RTSpinlockRelease(pThis->hDstTabSpinlock);
+ intnetR0AllocDstTab(pNetwork->MacTab.cEntriesAllocated, &pDstTab);
+ iDstTab = 65535;
+ }
+ }
+ if (RT_LIKELY(pDstTab))
+ {
+ /*
+ * Finally, get down to business of sending the frame.
+ */
+ INTNETSWDECISION enmSwDecision = intnetR0NetworkSend(pNetwork, NULL, fSrc, pSG, pDstTab);
+ AssertMsg(enmSwDecision != INTNETSWDECISION_BAD_CONTEXT, ("fSrc=%#x fTrunkDst=%#x hdr=%.14Rhxs\n", fSrc, pDstTab->fTrunkDst, pSG->aSegs[0].pv));
+ if (enmSwDecision == INTNETSWDECISION_INTNET)
+ fRc = true; /* drop it */
+
+ /*
+ * Free the destination table.
+ */
+ if (iDstTab == 65535)
+ RTMemFree(pDstTab);
+ else
+ {
+ RTSpinlockAcquire(pThis->hDstTabSpinlock);
+ if (fIntCtx && !pThis->apIntDstTabs[iDstTab])
+ pThis->apIntDstTabs[iDstTab] = pDstTab;
+ else if (!fIntCtx && !pThis->apTaskDstTabs[iDstTab])
+ pThis->apTaskDstTabs[iDstTab] = pDstTab;
+ else
+ {
+ /* this shouldn't happen! */
+ PINTNETDSTTAB *papDstTabs = fIntCtx ? &pThis->apIntDstTabs[0] : &pThis->apTaskDstTabs[0];
+ iDstTab = fIntCtx ? pThis->cIntDstTabs : RT_ELEMENTS(pThis->apTaskDstTabs);
+ while (iDstTab-- > 0)
+ if (!papDstTabs[iDstTab])
+ {
+ papDstTabs[iDstTab] = pDstTab;
+ break;
+ }
+ }
+ RTSpinlockRelease(pThis->hDstTabSpinlock);
+ Assert(iDstTab < RT_MAX(RT_ELEMENTS(pThis->apTaskDstTabs), pThis->cIntDstTabs));
+ }
+ }
+ }
+
+ intnetR0BusyDecTrunk(pThis);
+ return fRc;
+}
+
+
+/** @interface_method_impl{INTNETTRUNKSWPORT,pfnSGRetain} */
+static DECLCALLBACK(void) intnetR0TrunkIfPortSGRetain(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
+{
+ PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
+ PINTNETNETWORK pNetwork = pThis->pNetwork;
+
+ /* assert some sanity */
+ AssertPtrReturnVoid(pNetwork);
+ AssertReturnVoid(pNetwork->hEvtBusyIf != NIL_RTSEMEVENT);
+ AssertPtr(pSG);
+ Assert(pSG->cUsers > 0 && pSG->cUsers < 256);
+
+ /* do it. */
+ ++pSG->cUsers;
+}
+
+
+/** @interface_method_impl{INTNETTRUNKSWPORT,pfnSGRelease} */
+static DECLCALLBACK(void) intnetR0TrunkIfPortSGRelease(PINTNETTRUNKSWPORT pSwitchPort, PINTNETSG pSG)
+{
+ PINTNETTRUNKIF pThis = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
+ PINTNETNETWORK pNetwork = pThis->pNetwork;
+
+ /* assert some sanity */
+ AssertPtrReturnVoid(pNetwork);
+ AssertReturnVoid(pNetwork->hEvtBusyIf != NIL_RTSEMEVENT);
+ AssertPtr(pSG);
+ Assert(pSG->cUsers > 0);
+
+ /*
+ * Free it?
+ */
+ if (!--pSG->cUsers)
+ {
+ /** @todo later */
+ }
+}
+
+
+/** @interface_method_impl{INTNETTRUNKSWPORT,pfnNotifyHostAddress} */
+static DECLCALLBACK(void) intnetR0NetworkNotifyHostAddress(PINTNETTRUNKSWPORT pSwitchPort,
+ bool fAdded,
+ INTNETADDRTYPE enmType, const void *pvAddr)
+{
+ PINTNETTRUNKIF pTrunkIf = INTNET_SWITCHPORT_2_TRUNKIF(pSwitchPort);
+ PINTNETNETWORK pNetwork = pTrunkIf->pNetwork;
+ PCRTNETADDRU pAddr = (PCRTNETADDRU)pvAddr;
+ uint8_t cbAddr;
+
+ if (enmType == kIntNetAddrType_IPv4)
+ {
+ Log(("%s: %s %RTnaipv4\n",
+ __FUNCTION__, (fAdded ? "add" : "del"),
+ pAddr->IPv4));
+ cbAddr = 4;
+ }
+ else if (enmType == kIntNetAddrType_IPv6)
+ {
+ Log(("%s: %s %RTnaipv6\n",
+ __FUNCTION__, (fAdded ? "add" : "del"),
+ pAddr));
+ cbAddr = 16;
+ }
+ else
+ {
+ Log(("%s: unexpected address type %d\n", __FUNCTION__, enmType));
+ return;
+ }
+
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+ if (fAdded) /* one of host interfaces got a new address */
+ {
+ /* blacklist it to prevent spoofing by guests */
+ intnetR0NetworkBlacklistAdd(pNetwork, pAddr, enmType);
+
+ /* kick out any guest that uses it */
+ intnetR0NetworkAddrCacheDeleteLocked(pNetwork, pAddr, enmType, cbAddr, "tif/host");
+ }
+ else /* address deleted from one of host interfaces */
+ {
+ /* stop blacklisting it, guests may use it now */
+ intnetR0NetworkBlacklistDelete(pNetwork, pAddr, enmType);
+ }
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+}
+
+
+/**
+ * Shutdown the trunk interface.
+ *
+ * @param pThis The trunk.
+ * @param pNetworks The network.
+ *
+ * @remarks The caller must hold the global lock.
+ */
+static void intnetR0TrunkIfDestroy(PINTNETTRUNKIF pThis, PINTNETNETWORK pNetwork)
+{
+ /* assert sanity */
+ if (!pThis)
+ return;
+ AssertPtr(pThis);
+ Assert(pThis->pNetwork == pNetwork);
+ AssertPtrNull(pThis->pIfPort);
+
+ /*
+ * The interface has already been deactivated, we just to wait for
+ * it to become idle before we can disconnect and release it.
+ */
+ PINTNETTRUNKIFPORT pIfPort = pThis->pIfPort;
+ if (pIfPort)
+ {
+ /* unset it */
+ pThis->pIfPort = NULL;
+
+ /* wait in portions so we can complain every now an then. */
+ uint64_t StartTS = RTTimeSystemNanoTS();
+ int rc = pIfPort->pfnWaitForIdle(pIfPort, 10*1000);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("intnet: '%s' didn't become idle in %RU64 ns (%Rrc).\n",
+ pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
+ Assert(rc == VERR_TIMEOUT);
+ while ( RT_FAILURE(rc)
+ && RTTimeSystemNanoTS() - StartTS < UINT64_C(30000000000)) /* 30 sec */
+ rc = pIfPort->pfnWaitForIdle(pIfPort, 10*1000);
+ if (rc == VERR_TIMEOUT)
+ {
+ LogRel(("intnet: '%s' didn't become idle in %RU64 ns (%Rrc).\n",
+ pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
+ while ( rc == VERR_TIMEOUT
+ && RTTimeSystemNanoTS() - StartTS < UINT64_C(360000000000)) /* 360 sec */
+ rc = pIfPort->pfnWaitForIdle(pIfPort, 30*1000);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("intnet: '%s' didn't become idle in %RU64 ns (%Rrc), giving up.\n",
+ pNetwork->szName, RTTimeSystemNanoTS() - StartTS, rc));
+ AssertRC(rc);
+ }
+ }
+ }
+
+ /* disconnect & release it. */
+ pIfPort->pfnDisconnectAndRelease(pIfPort);
+ }
+
+ /*
+ * Free up the resources.
+ */
+ pThis->pNetwork = NULL; /* Must not be cleared while busy, see intnetR0TrunkIfPortDisconnect. */
+ RTSpinlockDestroy(pThis->hDstTabSpinlock);
+ for (unsigned i = 0; i < RT_ELEMENTS(pThis->apTaskDstTabs); i++)
+ {
+ Assert(pThis->apTaskDstTabs[i]);
+ RTMemFree(pThis->apTaskDstTabs[i]);
+ pThis->apTaskDstTabs[i] = NULL;
+ }
+ for (unsigned i = 0; i < pThis->cIntDstTabs; i++)
+ {
+ Assert(pThis->apIntDstTabs[i]);
+ RTMemFree(pThis->apIntDstTabs[i]);
+ pThis->apIntDstTabs[i] = NULL;
+ }
+ RTMemFree(pThis);
+}
+
+
+/**
+ * Creates the trunk connection (if any).
+ *
+ * @returns VBox status code.
+ *
+ * @param pNetwork The newly created network.
+ * @param pSession The session handle.
+ */
+static int intnetR0NetworkCreateTrunkIf(PINTNETNETWORK pNetwork, PSUPDRVSESSION pSession)
+{
+ const char *pszName;
+ switch (pNetwork->enmTrunkType)
+ {
+ /*
+ * The 'None' case, simple.
+ */
+ case kIntNetTrunkType_None:
+ case kIntNetTrunkType_WhateverNone:
+#ifdef VBOX_WITH_NAT_SERVICE
+ /*
+ * Well, here we don't want load anything special,
+ * just communicate between processes via internal network.
+ */
+ case kIntNetTrunkType_SrvNat:
+#endif
+ return VINF_SUCCESS;
+
+ /* Can't happen, but makes GCC happy. */
+ default:
+ return VERR_NOT_IMPLEMENTED;
+
+ /*
+ * Translate enum to component factory name.
+ */
+ case kIntNetTrunkType_NetFlt:
+ pszName = "VBoxNetFlt";
+ break;
+ case kIntNetTrunkType_NetAdp:
+#if defined(RT_OS_DARWIN) && !defined(VBOXNETADP_DO_NOT_USE_NETFLT)
+ pszName = "VBoxNetFlt";
+#else /* VBOXNETADP_DO_NOT_USE_NETFLT */
+ pszName = "VBoxNetAdp";
+#endif /* VBOXNETADP_DO_NOT_USE_NETFLT */
+ break;
+#ifndef VBOX_WITH_NAT_SERVICE
+ case kIntNetTrunkType_SrvNat:
+ pszName = "VBoxSrvNat";
+ break;
+#endif
+ }
+
+ /*
+ * Allocate the trunk interface and associated destination tables.
+ *
+ * We take a very optimistic view on the parallelism of the host
+ * network stack and NIC driver. So, we allocate one table for each
+ * possible CPU to deal with interrupt time requests and one for task
+ * time calls.
+ */
+ RTCPUID cCpus = RTMpGetCount(); Assert(cCpus > 0);
+ PINTNETTRUNKIF pTrunk = (PINTNETTRUNKIF)RTMemAllocZ(RT_UOFFSETOF_DYN(INTNETTRUNKIF, apIntDstTabs[cCpus]));
+ if (!pTrunk)
+ return VERR_NO_MEMORY;
+
+ Assert(pNetwork->MacTab.cEntriesAllocated > 0);
+ int rc = VINF_SUCCESS;
+ pTrunk->cIntDstTabs = cCpus;
+ for (unsigned i = 0; i < cCpus && RT_SUCCESS(rc); i++)
+ rc = intnetR0AllocDstTab(pNetwork->MacTab.cEntriesAllocated, &pTrunk->apIntDstTabs[i]);
+ for (unsigned i = 0; i < RT_ELEMENTS(pTrunk->apTaskDstTabs) && RT_SUCCESS(rc); i++)
+ rc = intnetR0AllocDstTab(pNetwork->MacTab.cEntriesAllocated, &pTrunk->apTaskDstTabs[i]);
+
+ if (RT_SUCCESS(rc))
+ {
+ pTrunk->SwitchPort.u32Version = INTNETTRUNKSWPORT_VERSION;
+ pTrunk->SwitchPort.pfnPreRecv = intnetR0TrunkIfPortPreRecv;
+ pTrunk->SwitchPort.pfnRecv = intnetR0TrunkIfPortRecv;
+ pTrunk->SwitchPort.pfnSGRetain = intnetR0TrunkIfPortSGRetain;
+ pTrunk->SwitchPort.pfnSGRelease = intnetR0TrunkIfPortSGRelease;
+ pTrunk->SwitchPort.pfnSetSGPhys = intnetR0TrunkIfPortSetSGPhys;
+ pTrunk->SwitchPort.pfnReportMacAddress = intnetR0TrunkIfPortReportMacAddress;
+ pTrunk->SwitchPort.pfnReportPromiscuousMode = intnetR0TrunkIfPortReportPromiscuousMode;
+ pTrunk->SwitchPort.pfnReportGsoCapabilities = intnetR0TrunkIfPortReportGsoCapabilities;
+ pTrunk->SwitchPort.pfnReportNoPreemptDsts = intnetR0TrunkIfPortReportNoPreemptDsts;
+ if (pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
+ pTrunk->SwitchPort.pfnNotifyHostAddress = intnetR0NetworkNotifyHostAddress;
+ pTrunk->SwitchPort.pfnDisconnect = intnetR0TrunkIfPortDisconnect;
+ pTrunk->SwitchPort.u32VersionEnd = INTNETTRUNKSWPORT_VERSION;
+ //pTrunk->pIfPort = NULL;
+ pTrunk->pNetwork = pNetwork;
+ pTrunk->MacAddr.au8[0] = 0xff;
+ pTrunk->MacAddr.au8[1] = 0xff;
+ pTrunk->MacAddr.au8[2] = 0xff;
+ pTrunk->MacAddr.au8[3] = 0xff;
+ pTrunk->MacAddr.au8[4] = 0xff;
+ pTrunk->MacAddr.au8[5] = 0xff;
+ //pTrunk->fPhysSG = false;
+ //pTrunk->fUnused = false;
+ //pTrunk->cBusy = 0;
+ //pTrunk->fNoPreemptDsts = 0;
+ //pTrunk->fWireGsoCapabilites = 0;
+ //pTrunk->fHostGsoCapabilites = 0;
+ //pTrunk->abGsoHdrs = {0};
+ pTrunk->hDstTabSpinlock = NIL_RTSPINLOCK;
+ //pTrunk->apTaskDstTabs = above;
+ //pTrunk->cIntDstTabs = above;
+ //pTrunk->apIntDstTabs = above;
+
+ /*
+ * Create the lock (we've NIL'ed the members above to simplify cleanup).
+ */
+ rc = RTSpinlockCreate(&pTrunk->hDstTabSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "hDstTabSpinlock");
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * There are a couple of bits in MacTab as well pertaining to the
+ * trunk. We have to set this before it's reported.
+ *
+ * Note! We don't need to lock the MacTab here - creation time.
+ */
+ pNetwork->MacTab.pTrunk = pTrunk;
+ pNetwork->MacTab.HostMac = pTrunk->MacAddr;
+ pNetwork->MacTab.fHostPromiscuousReal = false;
+ pNetwork->MacTab.fHostPromiscuousEff = (pNetwork->fFlags & INTNET_OPEN_FLAGS_TRUNK_HOST_PROMISC_MODE)
+ && (pNetwork->fFlags & INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_HOST);
+ pNetwork->MacTab.fHostActive = false;
+ pNetwork->MacTab.fWirePromiscuousReal = RT_BOOL(pNetwork->fFlags & INTNET_OPEN_FLAGS_TRUNK_WIRE_PROMISC_MODE);
+ pNetwork->MacTab.fWirePromiscuousEff = pNetwork->MacTab.fWirePromiscuousReal
+ && (pNetwork->fFlags & INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_WIRE);
+ pNetwork->MacTab.fWireActive = false;
+
+#ifdef IN_RING0 /* (testcase is ring-3) */
+ /*
+ * Query the factory we want, then use it create and connect the trunk.
+ */
+ PINTNETTRUNKFACTORY pTrunkFactory = NULL;
+ rc = SUPR0ComponentQueryFactory(pSession, pszName, INTNETTRUNKFACTORY_UUID_STR, (void **)&pTrunkFactory);
+ if (RT_SUCCESS(rc))
+ {
+ rc = pTrunkFactory->pfnCreateAndConnect(pTrunkFactory,
+ pNetwork->szTrunk,
+ &pTrunk->SwitchPort,
+ pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE
+ ? INTNETTRUNKFACTORY_FLAG_NO_PROMISC
+ : 0,
+ &pTrunk->pIfPort);
+ pTrunkFactory->pfnRelease(pTrunkFactory);
+ if (RT_SUCCESS(rc))
+ {
+ Assert(pTrunk->pIfPort);
+
+ Log(("intnetR0NetworkCreateTrunkIf: VINF_SUCCESS - pszName=%s szTrunk=%s%s Network=%s\n",
+ pszName, pNetwork->szTrunk, pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE ? " shared-mac" : "", pNetwork->szName));
+ return VINF_SUCCESS;
+ }
+ }
+#else /* IN_RING3 */
+ NOREF(pSession);
+ rc = VERR_NOT_SUPPORTED;
+#endif /* IN_RING3 */
+
+ pNetwork->MacTab.pTrunk = NULL;
+ }
+
+ /* bail out and clean up. */
+ RTSpinlockDestroy(pTrunk->hDstTabSpinlock);
+ }
+
+ for (unsigned i = 0; i < RT_ELEMENTS(pTrunk->apTaskDstTabs); i++)
+ RTMemFree(pTrunk->apTaskDstTabs[i]);
+ for (unsigned i = 0; i < pTrunk->cIntDstTabs; i++)
+ RTMemFree(pTrunk->apIntDstTabs[i]);
+ RTMemFree(pTrunk);
+
+ LogFlow(("intnetR0NetworkCreateTrunkIf: %Rrc - pszName=%s szTrunk=%s Network=%s\n",
+ rc, pszName, pNetwork->szTrunk, pNetwork->szName));
+ return rc;
+}
+
+
+/**
+ * Trunk reconnection thread function. It runs until signalled by another thread or by itself (upon
+ * successful trunk re-connection).
+ *
+ * Note that this function erases pNetwork->hTrunkReconnectThread right before it terminates!
+ */
+static DECLCALLBACK(int) intnetR0TrunkReconnectThread(RTTHREAD hThread, void *pvUser)
+{
+ RT_NOREF1(hThread);
+ PINTNETNETWORK pNetwork = (PINTNETNETWORK)pvUser;
+ PINTNET pIntNet = pNetwork->pIntNet;
+ Assert(pNetwork->pIntNet);
+
+ /*
+ * We attempt to reconnect the trunk every 5 seconds until somebody signals us.
+ */
+ while (!pNetwork->fTerminateReconnectThread && RTThreadUserWait(hThread, 5 * RT_MS_1SEC) == VERR_TIMEOUT)
+ {
+ /*
+ * Make sure nobody else is modifying networks.
+ * It is essential we give up on waiting for the big mutex much earlier than intnetR0NetworkDestruct
+ * gives up on waiting for us to terminate! This is why we wait for 1 second while network destruction
+ * code waits for 5 seconds. Otherwise the network may be already gone by the time we get the mutex.
+ */
+ if (RT_FAILURE(RTSemMutexRequestNoResume(pIntNet->hMtxCreateOpenDestroy, RT_MS_1SEC)))
+ continue;
+#if 0
+ /*
+ * This thread should be long gone by the time the network has been destroyed, but if we are
+ * really paranoid we should include the following code.
+ */
+ /*
+ * The network could have been destroyed while we were waiting on the big mutex, let us verify
+ * it is still valid by going over the list of existing networks.
+ */
+ PINTNETNETWORK pExistingNetwork = pIntNet->pNetworks;
+ for (; pExistingNetwork; pExistingNetwork = pExistingNetwork->pNext)
+ if (pExistingNetwork == pNetwork)
+ break;
+ /* We need the network to exist and to have at least one interface. */
+ if (pExistingNetwork && pNetwork->MacTab.cEntries)
+#else
+ /* We need the network to have at least one interface. */
+ if (pNetwork->MacTab.cEntries)
+#endif
+ {
+ PINTNETIF pAnyIf = pNetwork->MacTab.paEntries[0].pIf;
+ PSUPDRVSESSION pAnySession = pAnyIf ? pAnyIf->pSession : NULL;
+ if (pAnySession)
+ {
+ /* Attempt to re-connect trunk and if successful, terminate thread. */
+ if (RT_SUCCESS(intnetR0NetworkCreateTrunkIf(pNetwork, pAnySession)))
+ {
+ /* The network has active interfaces, we need to activate the trunk. */
+ if (pNetwork->cActiveIFs)
+ {
+ PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
+ /* The intnetR0NetworkCreateTrunkIf call resets fHostActive and fWireActive. */
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+ pNetwork->MacTab.fHostActive = RT_BOOL(pNetwork->fFlags & INTNET_OPEN_FLAGS_TRUNK_HOST_ENABLED);
+ pNetwork->MacTab.fWireActive = RT_BOOL(pNetwork->fFlags & INTNET_OPEN_FLAGS_TRUNK_WIRE_ENABLED);
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+ pTrunk->pIfPort->pfnSetState(pTrunk->pIfPort, INTNETTRUNKIFSTATE_ACTIVE);
+ }
+ pNetwork->fTerminateReconnectThread = true;
+ RTThreadUserSignal(hThread); /* Signal ourselves, so we break the loop after releasing the mutex */
+ }
+ }
+ }
+ RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
+ }
+
+ /*
+ * Destroy our handle in INTNETNETWORK so everyone knows we are gone.
+ * Note that this is the only place where this handle gets wiped out.
+ */
+ pNetwork->hTrunkReconnectThread = NIL_RTTHREAD;
+
+ return VINF_SUCCESS;
+}
+
+
+
+/**
+ * Object destructor callback.
+ * This is called for reference counted objectes when the count reaches 0.
+ *
+ * @param pvObj The object pointer.
+ * @param pvUser1 Pointer to the network.
+ * @param pvUser2 Pointer to the INTNET instance data.
+ */
+static DECLCALLBACK(void) intnetR0NetworkDestruct(void *pvObj, void *pvUser1, void *pvUser2)
+{
+ PINTNETNETWORK pNetwork = (PINTNETNETWORK)pvUser1;
+ PINTNET pIntNet = (PINTNET)pvUser2;
+ Log(("intnetR0NetworkDestruct: pvObj=%p pNetwork=%p pIntNet=%p %s\n", pvObj, pNetwork, pIntNet, pNetwork->szName));
+ Assert(pNetwork->pIntNet == pIntNet);
+ RT_NOREF1(pvObj);
+
+ /* Take the big create/open/destroy sem. */
+ RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
+
+ /*
+ * Tell the trunk, if present, that we're about to disconnect it and wish
+ * no further calls from it.
+ */
+ PINTNETTRUNKIF pTrunk = pNetwork->MacTab.pTrunk;
+ if (pTrunk)
+ pTrunk->pIfPort->pfnSetState(pTrunk->pIfPort, INTNETTRUNKIFSTATE_DISCONNECTING);
+
+ /*
+ * Deactivate and orphan any remaining interfaces and wait for them to idle.
+ *
+ * Note! Normally there are no more interfaces at this point, however, when
+ * supdrvCloseSession / supdrvCleanupSession release the objects the
+ * order is undefined. So, it's quite possible that the network will
+ * be dereference and destroyed before the interfaces.
+ */
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ uint32_t iIf = pNetwork->MacTab.cEntries;
+ while (iIf-- > 0)
+ {
+ pNetwork->MacTab.paEntries[iIf].fActive = false;
+ pNetwork->MacTab.paEntries[iIf].pIf->fActive = false;
+ }
+
+ pNetwork->MacTab.fHostActive = false;
+ pNetwork->MacTab.fWireActive = false;
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+
+ /* Wait for all the interfaces to quiesce. (Interfaces cannot be
+ removed / added since we're holding the big lock.) */
+ if (pTrunk)
+ intnetR0BusyWait(pNetwork, &pTrunk->cBusy);
+ else if (pNetwork->hTrunkReconnectThread != NIL_RTTHREAD)
+ {
+ /*
+ * There is no trunk and we have the trunk reconnection thread running.
+ * Signal the thread and wait for it to terminate.
+ */
+ pNetwork->fTerminateReconnectThread = true;
+ RTThreadUserSignal(pNetwork->hTrunkReconnectThread);
+ /*
+ * The tread cannot be re-connecting the trunk at the moment since we hold the big
+ * mutex, thus 5 second wait is definitely enough. Note that the wait time must
+ * exceed the time the reconnection thread waits on acquiring the big mutex, otherwise
+ * we will give up waiting for thread termination prematurely. Unfortunately it seems
+ * we have no way to terminate the thread if it failed to stop gracefully.
+ *
+ * Note that it is ok if the thread has already wiped out hTrunkReconnectThread by now,
+ * this means we no longer need to wait for it.
+ */
+ RTThreadWait(pNetwork->hTrunkReconnectThread, 5 * RT_MS_1SEC, NULL);
+ }
+
+ iIf = pNetwork->MacTab.cEntries;
+ while (iIf-- > 0)
+ intnetR0BusyWait(pNetwork, &pNetwork->MacTab.paEntries[iIf].pIf->cBusy);
+
+ /* Orphan the interfaces (not trunk). Don't bother with calling
+ pfnDisconnectInterface here since the networking is going away. */
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+ while ((iIf = pNetwork->MacTab.cEntries) > 0)
+ {
+ PINTNETIF pIf = pNetwork->MacTab.paEntries[iIf - 1].pIf;
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+
+ intnetR0BusyWait(pNetwork, &pIf->cBusy);
+
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+ if ( iIf == pNetwork->MacTab.cEntries /* paranoia */
+ && pIf->cBusy)
+ {
+ pIf->pNetwork = NULL;
+ pNetwork->MacTab.cEntries--;
+ }
+ }
+
+ /*
+ * Zap the trunk pointer while we still own the spinlock, destroy the
+ * trunk after we've left it. Note that this might take a while...
+ */
+ pNetwork->MacTab.pTrunk = NULL;
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+
+ if (pTrunk)
+ intnetR0TrunkIfDestroy(pTrunk, pNetwork);
+
+ /*
+ * Unlink the network.
+ * Note that it needn't be in the list if we failed during creation.
+ */
+ PINTNETNETWORK pPrev = pIntNet->pNetworks;
+ if (pPrev == pNetwork)
+ pIntNet->pNetworks = pNetwork->pNext;
+ else
+ {
+ for (; pPrev; pPrev = pPrev->pNext)
+ if (pPrev->pNext == pNetwork)
+ {
+ pPrev->pNext = pNetwork->pNext;
+ break;
+ }
+ }
+ pNetwork->pNext = NULL;
+ pNetwork->pvObj = NULL;
+
+ /*
+ * Free resources.
+ */
+ RTSemEventDestroy(pNetwork->hEvtBusyIf);
+ pNetwork->hEvtBusyIf = NIL_RTSEMEVENT;
+ RTSpinlockDestroy(pNetwork->hAddrSpinlock);
+ pNetwork->hAddrSpinlock = NIL_RTSPINLOCK;
+ RTMemFree(pNetwork->MacTab.paEntries);
+ pNetwork->MacTab.paEntries = NULL;
+ for (int i = kIntNetAddrType_Invalid + 1; i < kIntNetAddrType_End; i++)
+ intnetR0IfAddrCacheDestroy(&pNetwork->aAddrBlacklist[i]);
+ RTMemFree(pNetwork);
+
+ /* Release the create/destroy sem. */
+ RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
+}
+
+
+/**
+ * Checks if the open network flags are compatible.
+ *
+ * @returns VBox status code.
+ * @param pNetwork The network.
+ * @param fFlags The open network flags.
+ */
+static int intnetR0CheckOpenNetworkFlags(PINTNETNETWORK pNetwork, uint32_t fFlags)
+{
+ uint32_t const fNetFlags = pNetwork->fFlags;
+
+ if ( (fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
+ ^ (fNetFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE))
+ return VERR_INTNET_INCOMPATIBLE_FLAGS;
+
+ if (fFlags & INTNET_OPEN_FLAGS_REQUIRE_EXACT)
+ {
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_afIntNetOpenNetworkNetFlags); i++)
+ if ( (fFlags & g_afIntNetOpenNetworkNetFlags[i].fPair)
+ && (fFlags & g_afIntNetOpenNetworkNetFlags[i].fPair)
+ != (fNetFlags & g_afIntNetOpenNetworkNetFlags[i].fPair) )
+ return VERR_INTNET_INCOMPATIBLE_FLAGS;
+ }
+
+ if (fFlags & INTNET_OPEN_FLAGS_REQUIRE_AS_RESTRICTIVE_POLICIES)
+ {
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_afIntNetOpenNetworkNetFlags); i++)
+ if ( (fFlags & g_afIntNetOpenNetworkNetFlags[i].fRestrictive)
+ && !(fNetFlags & g_afIntNetOpenNetworkNetFlags[i].fRestrictive)
+ && (fNetFlags & g_afIntNetOpenNetworkNetFlags[i].fFixed) )
+ return VERR_INTNET_INCOMPATIBLE_FLAGS;
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Adapts flag changes on network opening.
+ *
+ * @returns VBox status code.
+ * @param pNetwork The network.
+ * @param fFlags The open network flags.
+ */
+static int intnetR0AdaptOpenNetworkFlags(PINTNETNETWORK pNetwork, uint32_t fFlags)
+{
+ /*
+ * Upgrade the minimum policy flags.
+ */
+ uint32_t fNetMinFlags = pNetwork->fMinFlags;
+ Assert(!(fNetMinFlags & INTNET_OPEN_FLAGS_RELAXED_MASK));
+ if (fFlags & INTNET_OPEN_FLAGS_REQUIRE_AS_RESTRICTIVE_POLICIES)
+ {
+ fNetMinFlags |= fFlags & INTNET_OPEN_FLAGS_STRICT_MASK;
+ if (fNetMinFlags != pNetwork->fMinFlags)
+ {
+ LogRel(("INTNET: %s - min flags changed %#x -> %#x\n", pNetwork->szName, pNetwork->fMinFlags, fNetMinFlags));
+ pNetwork->fMinFlags = fNetMinFlags;
+ }
+ }
+
+ /*
+ * Calculate the new network flags.
+ * (Depends on fNetMinFlags being recalculated first.)
+ */
+ uint32_t fNetFlags = pNetwork->fFlags;
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_afIntNetOpenNetworkNetFlags); i++)
+ {
+ Assert(fNetFlags & g_afIntNetOpenNetworkNetFlags[i].fPair);
+ Assert(!(fNetMinFlags & g_afIntNetOpenNetworkNetFlags[i].fRelaxed));
+
+ if (!(fFlags & g_afIntNetOpenNetworkNetFlags[i].fPair))
+ continue;
+ if (fNetFlags & g_afIntNetOpenNetworkNetFlags[i].fFixed)
+ continue;
+
+ if ( (fNetMinFlags & g_afIntNetOpenNetworkNetFlags[i].fRestrictive)
+ || (fFlags & g_afIntNetOpenNetworkNetFlags[i].fRestrictive) )
+ {
+ fNetFlags &= ~g_afIntNetOpenNetworkNetFlags[i].fPair;
+ fNetFlags |= g_afIntNetOpenNetworkNetFlags[i].fRestrictive;
+ }
+ else if (!(fFlags & INTNET_OPEN_FLAGS_REQUIRE_AS_RESTRICTIVE_POLICIES))
+ {
+ fNetFlags &= ~g_afIntNetOpenNetworkNetFlags[i].fPair;
+ fNetFlags |= g_afIntNetOpenNetworkNetFlags[i].fRelaxed;
+ }
+ }
+
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_afIntNetOpenNetworkNetFlags); i++)
+ {
+ Assert(fNetFlags & g_afIntNetOpenNetworkNetFlags[i].fPair);
+ fNetFlags |= fFlags & g_afIntNetOpenNetworkNetFlags[i].fFixed;
+ }
+
+ /*
+ * Apply the flags if they changed.
+ */
+ uint32_t const fOldNetFlags = pNetwork->fFlags;
+ if (fOldNetFlags != fNetFlags)
+ {
+ LogRel(("INTNET: %s - flags changed %#x -> %#x\n", pNetwork->szName, fOldNetFlags, fNetFlags));
+
+ RTSpinlockAcquire(pNetwork->hAddrSpinlock);
+
+ pNetwork->fFlags = fNetFlags;
+
+ /* Recalculate some derived switcher variables. */
+ bool fActiveTrunk = pNetwork->MacTab.pTrunk
+ && pNetwork->cActiveIFs > 0;
+ pNetwork->MacTab.fHostActive = fActiveTrunk
+ && (fNetFlags & INTNET_OPEN_FLAGS_TRUNK_HOST_ENABLED);
+ pNetwork->MacTab.fHostPromiscuousEff = ( pNetwork->MacTab.fHostPromiscuousReal
+ || (fNetFlags & INTNET_OPEN_FLAGS_TRUNK_HOST_PROMISC_MODE))
+ && (fNetFlags & INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_HOST);
+
+ pNetwork->MacTab.fWireActive = fActiveTrunk
+ && (fNetFlags & INTNET_OPEN_FLAGS_TRUNK_HOST_ENABLED);
+ pNetwork->MacTab.fWirePromiscuousReal= RT_BOOL(fNetFlags & INTNET_OPEN_FLAGS_TRUNK_WIRE_PROMISC_MODE);
+ pNetwork->MacTab.fWirePromiscuousEff = pNetwork->MacTab.fWirePromiscuousReal
+ && (fNetFlags & INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_WIRE);
+
+ if ((fOldNetFlags ^ fNetFlags) & INTNET_OPEN_FLAGS_PROMISC_ALLOW_CLIENTS)
+ {
+ pNetwork->MacTab.cPromiscuousEntries = 0;
+ pNetwork->MacTab.cPromiscuousNoTrunkEntries = 0;
+
+ uint32_t iIf = pNetwork->MacTab.cEntries;
+ while (iIf-- > 0)
+ {
+ PINTNETMACTABENTRY pEntry = &pNetwork->MacTab.paEntries[iIf];
+ PINTNETIF pIf2 = pEntry->pIf;
+ if ( pIf2 /* paranoia */
+ && pIf2->fPromiscuousReal)
+ {
+ bool fPromiscuousEff = (fNetFlags & INTNET_OPEN_FLAGS_PROMISC_ALLOW_CLIENTS)
+ && (pIf2->fOpenFlags & INTNET_OPEN_FLAGS_IF_PROMISC_ALLOW);
+ pEntry->fPromiscuousEff = fPromiscuousEff;
+ pEntry->fPromiscuousSeeTrunk = fPromiscuousEff
+ && (pIf2->fOpenFlags & INTNET_OPEN_FLAGS_IF_PROMISC_SEE_TRUNK);
+
+ if (pEntry->fPromiscuousEff)
+ {
+ pNetwork->MacTab.cPromiscuousEntries++;
+ if (!pEntry->fPromiscuousSeeTrunk)
+ pNetwork->MacTab.cPromiscuousNoTrunkEntries++;
+ }
+ }
+ }
+ }
+
+ RTSpinlockRelease(pNetwork->hAddrSpinlock);
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/**
+ * Opens an existing network.
+ *
+ * The call must own the INTNET::hMtxCreateOpenDestroy.
+ *
+ * @returns VBox status code.
+ * @param pIntNet The instance data.
+ * @param pSession The current session.
+ * @param pszNetwork The network name. This has a valid length.
+ * @param enmTrunkType The trunk type.
+ * @param pszTrunk The trunk name. Its meaning is specific to the type.
+ * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
+ * @param ppNetwork Where to store the pointer to the network on success.
+ */
+static int intnetR0OpenNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
+ const char *pszTrunk, uint32_t fFlags, PINTNETNETWORK *ppNetwork)
+{
+ LogFlow(("intnetR0OpenNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
+ pIntNet, pSession, pszNetwork, pszNetwork, enmTrunkType, pszTrunk, pszTrunk, fFlags, ppNetwork));
+
+ /* just pro forma validation, the caller is internal. */
+ AssertPtr(pIntNet);
+ AssertPtr(pSession);
+ AssertPtr(pszNetwork);
+ Assert(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End);
+ AssertPtr(pszTrunk);
+ Assert(!(fFlags & ~INTNET_OPEN_FLAGS_MASK));
+ AssertPtr(ppNetwork);
+ *ppNetwork = NULL;
+
+ /*
+ * Search networks by name.
+ */
+ PINTNETNETWORK pCur;
+ uint8_t cchName = (uint8_t)strlen(pszNetwork);
+ Assert(cchName && cchName < sizeof(pCur->szName)); /* caller ensures this */
+
+ pCur = pIntNet->pNetworks;
+ while (pCur)
+ {
+ if ( pCur->cchName == cchName
+ && !memcmp(pCur->szName, pszNetwork, cchName))
+ {
+ /*
+ * Found the network, now check that we have the same ideas
+ * about the trunk setup and security.
+ */
+ int rc;
+ if ( enmTrunkType == kIntNetTrunkType_WhateverNone
+#ifdef VBOX_WITH_NAT_SERVICE
+ || enmTrunkType == kIntNetTrunkType_SrvNat /** @todo what does it mean */
+#endif
+ || ( pCur->enmTrunkType == enmTrunkType
+ && !strcmp(pCur->szTrunk, pszTrunk)))
+ {
+ rc = intnetR0CheckOpenNetworkFlags(pCur, fFlags);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Increment the reference and check that the session
+ * can access this network.
+ */
+ rc = SUPR0ObjAddRef(pCur->pvObj, pSession);
+ if (RT_SUCCESS(rc))
+ {
+ if (pCur->fFlags & INTNET_OPEN_FLAGS_ACCESS_RESTRICTED)
+ rc = SUPR0ObjVerifyAccess(pCur->pvObj, pSession, pCur->szName);
+ if (RT_SUCCESS(rc))
+ *ppNetwork = pCur;
+ else
+ SUPR0ObjRelease(pCur->pvObj, pSession);
+ }
+ else if (rc == VERR_WRONG_ORDER)
+ rc = VERR_NOT_FOUND; /* destruction race, pretend the other isn't there. */
+ }
+ }
+ else
+ {
+ rc = VERR_INTNET_INCOMPATIBLE_TRUNK;
+ LogRel(("intnetR0OpenNetwork failed. rc=%Rrc pCur->szTrunk=%s pszTrunk=%s pCur->enmTrunkType=%d enmTrunkType=%d\n",
+ rc, pCur->szTrunk, pszTrunk, pCur->enmTrunkType, enmTrunkType));
+ }
+
+ LogFlow(("intnetR0OpenNetwork: returns %Rrc *ppNetwork=%p\n", rc, *ppNetwork));
+ return rc;
+ }
+
+ pCur = pCur->pNext;
+ }
+
+ LogFlow(("intnetR0OpenNetwork: returns VERR_NOT_FOUND\n"));
+ return VERR_NOT_FOUND;
+}
+
+
+/**
+ * Creates a new network.
+ *
+ * The call must own the INTNET::hMtxCreateOpenDestroy and has already attempted
+ * opening the network and found it to be non-existing.
+ *
+ * @returns VBox status code.
+ * @param pIntNet The instance data.
+ * @param pSession The session handle.
+ * @param pszNetwork The name of the network. This must be at least one character long and no longer
+ * than the INTNETNETWORK::szName.
+ * @param enmTrunkType The trunk type.
+ * @param pszTrunk The trunk name. Its meaning is specific to the type.
+ * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
+ * @param ppNetwork Where to store the network. In the case of failure
+ * whatever is returned here should be dereferenced
+ * outside the INTNET::hMtxCreateOpenDestroy.
+ */
+static int intnetR0CreateNetwork(PINTNET pIntNet, PSUPDRVSESSION pSession, const char *pszNetwork, INTNETTRUNKTYPE enmTrunkType,
+ const char *pszTrunk, uint32_t fFlags, PINTNETNETWORK *ppNetwork)
+{
+ LogFlow(("intnetR0CreateNetwork: pIntNet=%p pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x ppNetwork=%p\n",
+ pIntNet, pSession, pszNetwork, pszNetwork, enmTrunkType, pszTrunk, pszTrunk, fFlags, ppNetwork));
+
+ /* just pro forma validation, the caller is internal. */
+ AssertPtr(pIntNet);
+ AssertPtr(pSession);
+ AssertPtr(pszNetwork);
+ Assert(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End);
+ AssertPtr(pszTrunk);
+ Assert(!(fFlags & ~INTNET_OPEN_FLAGS_MASK));
+ AssertPtr(ppNetwork);
+
+ *ppNetwork = NULL;
+
+ /*
+ * Adjust the flags with defaults for the network policies.
+ * Note: Main restricts promiscuous mode on the per interface level.
+ */
+ fFlags &= ~( INTNET_OPEN_FLAGS_IF_FIXED
+ | INTNET_OPEN_FLAGS_IF_PROMISC_ALLOW
+ | INTNET_OPEN_FLAGS_IF_PROMISC_DENY
+ | INTNET_OPEN_FLAGS_IF_PROMISC_SEE_TRUNK
+ | INTNET_OPEN_FLAGS_IF_PROMISC_NO_TRUNK
+ | INTNET_OPEN_FLAGS_REQUIRE_AS_RESTRICTIVE_POLICIES
+ | INTNET_OPEN_FLAGS_REQUIRE_EXACT);
+ uint32_t fDefFlags = INTNET_OPEN_FLAGS_PROMISC_ALLOW_CLIENTS
+ | INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_HOST
+ | INTNET_OPEN_FLAGS_PROMISC_ALLOW_TRUNK_WIRE
+ | INTNET_OPEN_FLAGS_TRUNK_HOST_ENABLED
+ | INTNET_OPEN_FLAGS_TRUNK_HOST_CHASTE_MODE
+ | INTNET_OPEN_FLAGS_TRUNK_WIRE_ENABLED
+ | INTNET_OPEN_FLAGS_TRUNK_WIRE_CHASTE_MODE;
+ if ( enmTrunkType == kIntNetTrunkType_WhateverNone
+#ifdef VBOX_WITH_NAT_SERVICE
+ || enmTrunkType == kIntNetTrunkType_SrvNat /* simialar security */
+#endif
+ || enmTrunkType == kIntNetTrunkType_None)
+ fDefFlags |= INTNET_OPEN_FLAGS_ACCESS_RESTRICTED;
+ else
+ fDefFlags |= INTNET_OPEN_FLAGS_ACCESS_PUBLIC;
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_afIntNetOpenNetworkNetFlags); i++)
+ if (!(fFlags & g_afIntNetOpenNetworkNetFlags[i].fPair))
+ fFlags |= g_afIntNetOpenNetworkNetFlags[i].fPair & fDefFlags;
+
+ /*
+ * Allocate and initialize.
+ */
+ size_t cb = sizeof(INTNETNETWORK);
+ if (fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
+ cb += INTNETNETWORK_TMP_SIZE + 64;
+ PINTNETNETWORK pNetwork = (PINTNETNETWORK)RTMemAllocZ(cb);
+ if (!pNetwork)
+ return VERR_NO_MEMORY;
+ //pNetwork->pNext = NULL;
+ //pNetwork->pIfs = NULL;
+ //pNetwork->fTerminateReconnectThread = false;
+ pNetwork->hTrunkReconnectThread = NIL_RTTHREAD;
+ pNetwork->hAddrSpinlock = NIL_RTSPINLOCK;
+ pNetwork->MacTab.cEntries = 0;
+ pNetwork->MacTab.cEntriesAllocated = INTNET_GROW_DSTTAB_SIZE;
+ //pNetwork->MacTab.cPromiscuousEntries = 0;
+ //pNetwork->MacTab.cPromiscuousNoTrunkEntries = 0;
+ pNetwork->MacTab.paEntries = NULL;
+ pNetwork->MacTab.fHostPromiscuousReal = false;
+ pNetwork->MacTab.fHostPromiscuousEff = false;
+ pNetwork->MacTab.fHostActive = false;
+ pNetwork->MacTab.fWirePromiscuousReal = false;
+ pNetwork->MacTab.fWirePromiscuousEff = false;
+ pNetwork->MacTab.fWireActive = false;
+ pNetwork->MacTab.pTrunk = NULL;
+ pNetwork->hEvtBusyIf = NIL_RTSEMEVENT;
+ pNetwork->pIntNet = pIntNet;
+ //pNetwork->pvObj = NULL;
+ if (fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE)
+ pNetwork->pbTmp = RT_ALIGN_PT(pNetwork + 1, 64, uint8_t *);
+ //else
+ // pNetwork->pbTmp = NULL;
+ pNetwork->fFlags = fFlags;
+ //pNetwork->fMinFlags = 0;
+ //pNetwork->cActiveIFs = 0;
+ size_t cchName = strlen(pszNetwork);
+ pNetwork->cchName = (uint8_t)cchName;
+ Assert(cchName && cchName < sizeof(pNetwork->szName)); /* caller's responsibility. */
+ memcpy(pNetwork->szName, pszNetwork, cchName); /* '\0' at courtesy of alloc. */
+ pNetwork->enmTrunkType = enmTrunkType;
+ Assert(strlen(pszTrunk) < sizeof(pNetwork->szTrunk)); /* caller's responsibility. */
+ strcpy(pNetwork->szTrunk, pszTrunk);
+
+ /*
+ * Create the semaphore, spinlock and allocate the interface table.
+ */
+ int rc = RTSemEventCreate(&pNetwork->hEvtBusyIf);
+ if (RT_SUCCESS(rc))
+ rc = RTSpinlockCreate(&pNetwork->hAddrSpinlock, RTSPINLOCK_FLAGS_INTERRUPT_SAFE, "hAddrSpinlock");
+ if (RT_SUCCESS(rc))
+ {
+ pNetwork->MacTab.paEntries = (PINTNETMACTABENTRY)RTMemAlloc(sizeof(INTNETMACTABENTRY) * pNetwork->MacTab.cEntriesAllocated);
+ if (!pNetwork->MacTab.paEntries)
+ rc = VERR_NO_MEMORY;
+ }
+ if (RT_SUCCESS(rc))
+ {
+ for (int i = kIntNetAddrType_Invalid + 1; i < kIntNetAddrType_End && RT_SUCCESS(rc); i++)
+ rc = intnetR0IfAddrCacheInit(&pNetwork->aAddrBlacklist[i], (INTNETADDRTYPE)i,
+ !!(pNetwork->fFlags & INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE));
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Register the object in the current session and link it into the network list.
+ */
+ pNetwork->pvObj = SUPR0ObjRegister(pSession, SUPDRVOBJTYPE_INTERNAL_NETWORK, intnetR0NetworkDestruct, pNetwork, pIntNet);
+ if (pNetwork->pvObj)
+ {
+ pNetwork->pNext = pIntNet->pNetworks;
+ pIntNet->pNetworks = pNetwork;
+
+ /*
+ * Check if the current session is actually allowed to create and
+ * open the network. It is possible to implement network name
+ * based policies and these must be checked now. SUPR0ObjRegister
+ * does no such checks.
+ */
+ rc = SUPR0ObjVerifyAccess(pNetwork->pvObj, pSession, pNetwork->szName);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Connect the trunk.
+ */
+ rc = intnetR0NetworkCreateTrunkIf(pNetwork, pSession);
+ if (RT_SUCCESS(rc))
+ {
+ *ppNetwork = pNetwork;
+ LogFlow(("intnetR0CreateNetwork: returns VINF_SUCCESS *ppNetwork=%p\n", pNetwork));
+ return VINF_SUCCESS;
+ }
+ }
+
+ SUPR0ObjRelease(pNetwork->pvObj, pSession);
+ LogFlow(("intnetR0CreateNetwork: returns %Rrc\n", rc));
+ return rc;
+ }
+
+ /* cleanup */
+ rc = VERR_NO_MEMORY;
+ }
+
+ RTSemEventDestroy(pNetwork->hEvtBusyIf);
+ pNetwork->hEvtBusyIf = NIL_RTSEMEVENT;
+ RTSpinlockDestroy(pNetwork->hAddrSpinlock);
+ pNetwork->hAddrSpinlock = NIL_RTSPINLOCK;
+ RTMemFree(pNetwork->MacTab.paEntries);
+ pNetwork->MacTab.paEntries = NULL;
+ RTMemFree(pNetwork);
+
+ LogFlow(("intnetR0CreateNetwork: returns %Rrc\n", rc));
+ return rc;
+}
+
+
+/**
+ * Opens a network interface and connects it to the specified network.
+ *
+ * @returns VBox status code.
+ * @param pSession The session handle.
+ * @param pszNetwork The network name.
+ * @param enmTrunkType The trunk type.
+ * @param pszTrunk The trunk name. Its meaning is specific to the type.
+ * @param fFlags Flags, see INTNET_OPEN_FLAGS_*.
+ * @param fRestrictAccess Whether new participants should be subjected to
+ * access check or not.
+ * @param cbSend The send buffer size.
+ * @param cbRecv The receive buffer size.
+ * @param pfnRecvAvail The receive available callback to call instead of
+ * signalling the semaphore (R3 service only).
+ * @param pvUser The opaque user data to pass to the callback.
+ * @param phIf Where to store the handle to the network interface.
+ */
+INTNETR0DECL(int) IntNetR0Open(PSUPDRVSESSION pSession, const char *pszNetwork,
+ INTNETTRUNKTYPE enmTrunkType, const char *pszTrunk, uint32_t fFlags,
+ uint32_t cbSend, uint32_t cbRecv, PFNINTNETIFRECVAVAIL pfnRecvAvail, void *pvUser,
+ PINTNETIFHANDLE phIf)
+{
+ LogFlow(("IntNetR0Open: pSession=%p pszNetwork=%p:{%s} enmTrunkType=%d pszTrunk=%p:{%s} fFlags=%#x cbSend=%u cbRecv=%u phIf=%p\n",
+ pSession, pszNetwork, pszNetwork, enmTrunkType, pszTrunk, pszTrunk, fFlags, cbSend, cbRecv, phIf));
+
+ /*
+ * Validate input.
+ */
+ PINTNET pIntNet = g_pIntNet;
+ AssertPtrReturn(pIntNet, VERR_INVALID_PARAMETER);
+ AssertReturn(pIntNet->u32Magic, VERR_INVALID_MAGIC);
+
+ AssertPtrReturn(pszNetwork, VERR_INVALID_PARAMETER);
+ const char *pszNetworkEnd = RTStrEnd(pszNetwork, INTNET_MAX_NETWORK_NAME);
+ AssertReturn(pszNetworkEnd, VERR_INVALID_PARAMETER);
+ size_t cchNetwork = pszNetworkEnd - pszNetwork;
+ AssertReturn(cchNetwork, VERR_INVALID_PARAMETER);
+
+ if (pszTrunk)
+ {
+ AssertPtrReturn(pszTrunk, VERR_INVALID_PARAMETER);
+ const char *pszTrunkEnd = RTStrEnd(pszTrunk, INTNET_MAX_TRUNK_NAME);
+ AssertReturn(pszTrunkEnd, VERR_INVALID_PARAMETER);
+ }
+ else
+ pszTrunk = "";
+
+ AssertMsgReturn(enmTrunkType > kIntNetTrunkType_Invalid && enmTrunkType < kIntNetTrunkType_End,
+ ("%d\n", enmTrunkType), VERR_INVALID_PARAMETER);
+ switch (enmTrunkType)
+ {
+ case kIntNetTrunkType_None:
+ case kIntNetTrunkType_WhateverNone:
+#ifdef VBOX_WITH_NAT_SERVICE
+ case kIntNetTrunkType_SrvNat:
+#endif
+ if (*pszTrunk)
+ return VERR_INVALID_PARAMETER;
+ break;
+
+ case kIntNetTrunkType_NetFlt:
+ case kIntNetTrunkType_NetAdp:
+ if (!*pszTrunk)
+ return VERR_INVALID_PARAMETER;
+ break;
+
+ default:
+ return VERR_NOT_IMPLEMENTED;
+ }
+
+ AssertMsgReturn(!(fFlags & ~INTNET_OPEN_FLAGS_MASK), ("%#x\n", fFlags), VERR_INVALID_PARAMETER);
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_afIntNetOpenNetworkNetFlags); i++)
+ AssertMsgReturn((fFlags & g_afIntNetOpenNetworkNetFlags[i].fPair) != g_afIntNetOpenNetworkNetFlags[i].fPair,
+ ("%#x (%#x)\n", fFlags, g_afIntNetOpenNetworkNetFlags[i].fPair), VERR_INVALID_PARAMETER);
+ for (uint32_t i = 0; i < RT_ELEMENTS(g_afIntNetOpenNetworkIfFlags); i++)
+ AssertMsgReturn((fFlags & g_afIntNetOpenNetworkIfFlags[i].fPair) != g_afIntNetOpenNetworkIfFlags[i].fPair,
+ ("%#x (%#x)\n", fFlags, g_afIntNetOpenNetworkIfFlags[i].fPair), VERR_INVALID_PARAMETER);
+ AssertPtrReturn(phIf, VERR_INVALID_PARAMETER);
+
+ /*
+ * Acquire the mutex to serialize open/create/close.
+ */
+ int rc = RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /*
+ * Try open / create the network and create an interface on it for the
+ * caller to use.
+ */
+ PINTNETNETWORK pNetwork = NULL;
+ rc = intnetR0OpenNetwork(pIntNet, pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, &pNetwork);
+ if (RT_SUCCESS(rc))
+ {
+ rc = intnetR0NetworkCreateIf(pNetwork, pSession, cbSend, cbRecv, fFlags, pfnRecvAvail, pvUser, phIf);
+ if (RT_SUCCESS(rc))
+ {
+ intnetR0AdaptOpenNetworkFlags(pNetwork, fFlags);
+ rc = VINF_ALREADY_INITIALIZED;
+ }
+ else
+ SUPR0ObjRelease(pNetwork->pvObj, pSession);
+ }
+ else if (rc == VERR_NOT_FOUND)
+ {
+ rc = intnetR0CreateNetwork(pIntNet, pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, &pNetwork);
+ if (RT_SUCCESS(rc))
+ {
+ rc = intnetR0NetworkCreateIf(pNetwork, pSession, cbSend, cbRecv, fFlags, pfnRecvAvail, pvUser, phIf);
+ if (RT_FAILURE(rc))
+ SUPR0ObjRelease(pNetwork->pvObj, pSession);
+ }
+ }
+
+ RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
+ LogFlow(("IntNetR0Open: return %Rrc *phIf=%RX32\n", rc, *phIf));
+ return rc;
+}
+
+
+/**
+ * VMMR0 request wrapper for IntNetR0Open.
+ *
+ * @returns see GMMR0MapUnmapChunk.
+ * @param pSession The caller's session.
+ * @param pReq The request packet.
+ */
+INTNETR0DECL(int) IntNetR0OpenReq(PSUPDRVSESSION pSession, PINTNETOPENREQ pReq)
+{
+ if (RT_UNLIKELY(pReq->Hdr.cbReq != sizeof(*pReq)))
+ return VERR_INVALID_PARAMETER;
+ return IntNetR0Open(pSession, &pReq->szNetwork[0], pReq->enmTrunkType, pReq->szTrunk,
+ pReq->fFlags, pReq->cbSend, pReq->cbRecv, NULL /*pfnRecvAvail*/, NULL /*pvUser*/, &pReq->hIf);
+}
+
+
+#if defined(VBOX_WITH_INTNET_SERVICE_IN_R3) && defined(IN_RING3)
+INTNETR3DECL(int) IntNetR3Open(PSUPDRVSESSION pSession, const char *pszNetwork,
+ INTNETTRUNKTYPE enmTrunkType, const char *pszTrunk, uint32_t fFlags,
+ uint32_t cbSend, uint32_t cbRecv, PFNINTNETIFRECVAVAIL pfnRecvAvail, void *pvUser,
+ PINTNETIFHANDLE phIf)
+{
+ return IntNetR0Open(pSession, pszNetwork, enmTrunkType, pszTrunk, fFlags, cbSend, cbRecv, pfnRecvAvail, pvUser, phIf);
+}
+#endif
+
+
+/**
+ * Count the internal networks.
+ *
+ * This is mainly for providing the testcase with some introspection to validate
+ * behavior when closing interfaces.
+ *
+ * @returns The number of networks.
+ */
+INTNETR0DECL(uint32_t) IntNetR0GetNetworkCount(void)
+{
+ /*
+ * Grab the instance.
+ */
+ PINTNET pIntNet = g_pIntNet;
+ if (!pIntNet)
+ return 0;
+ AssertPtrReturn(pIntNet, 0);
+ AssertReturn(pIntNet->u32Magic == INTNET_MAGIC, 0);
+
+ /*
+ * Grab the mutex and count the networks.
+ */
+ int rc = RTSemMutexRequest(pIntNet->hMtxCreateOpenDestroy, RT_INDEFINITE_WAIT);
+ if (RT_FAILURE(rc))
+ return 0;
+
+ uint32_t cNetworks = 0;
+ for (PINTNETNETWORK pCur = pIntNet->pNetworks; pCur; pCur = pCur->pNext)
+ cNetworks++;
+
+ RTSemMutexRelease(pIntNet->hMtxCreateOpenDestroy);
+
+ return cNetworks;
+}
+
+
+
+/**
+ * Destroys an instance of the Ring-0 internal networking service.
+ */
+INTNETR0DECL(void) IntNetR0Term(void)
+{
+ LogFlow(("IntNetR0Term:\n"));
+
+ /*
+ * Zap the global pointer and validate it.
+ */
+ PINTNET pIntNet = g_pIntNet;
+ g_pIntNet = NULL;
+ if (!pIntNet)
+ return;
+ AssertPtrReturnVoid(pIntNet);
+ AssertReturnVoid(pIntNet->u32Magic == INTNET_MAGIC);
+
+ /*
+ * There is not supposed to be any networks hanging around at this time.
+ */
+ AssertReturnVoid(ASMAtomicCmpXchgU32(&pIntNet->u32Magic, ~INTNET_MAGIC, INTNET_MAGIC));
+ Assert(pIntNet->pNetworks == NULL);
+ /*
+ * @todo Do we really need to be paranoid enough to go over the list of networks here,
+ * trying to terminate trunk re-connection threads here?
+ */
+ if (pIntNet->hMtxCreateOpenDestroy != NIL_RTSEMMUTEX)
+ {
+ RTSemMutexDestroy(pIntNet->hMtxCreateOpenDestroy);
+ pIntNet->hMtxCreateOpenDestroy = NIL_RTSEMMUTEX;
+ }
+ if (pIntNet->hHtIfs != NIL_RTHANDLETABLE)
+ {
+ /** @todo does it make sense to have a deleter here? */
+ RTHandleTableDestroy(pIntNet->hHtIfs, NULL, NULL);
+ pIntNet->hHtIfs = NIL_RTHANDLETABLE;
+ }
+
+ RTMemFree(pIntNet);
+}
+
+
+/**
+ * Initializes the internal network ring-0 service.
+ *
+ * @returns VBox status code.
+ */
+INTNETR0DECL(int) IntNetR0Init(void)
+{
+ LogFlow(("IntNetR0Init:\n"));
+ int rc = VERR_NO_MEMORY;
+ PINTNET pIntNet = (PINTNET)RTMemAllocZ(sizeof(*pIntNet));
+ if (pIntNet)
+ {
+ //pIntNet->pNetworks = NULL;
+
+ rc = RTSemMutexCreate(&pIntNet->hMtxCreateOpenDestroy);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTHandleTableCreateEx(&pIntNet->hHtIfs, RTHANDLETABLE_FLAGS_LOCKED | RTHANDLETABLE_FLAGS_CONTEXT,
+ UINT32_C(0x8ffe0000), 4096, intnetR0IfRetainHandle, NULL);
+ if (RT_SUCCESS(rc))
+ {
+ pIntNet->u32Magic = INTNET_MAGIC;
+ g_pIntNet = pIntNet;
+ LogFlow(("IntNetR0Init: returns VINF_SUCCESS pIntNet=%p\n", pIntNet));
+ return VINF_SUCCESS;
+ }
+
+ RTSemMutexDestroy(pIntNet->hMtxCreateOpenDestroy);
+ }
+ RTMemFree(pIntNet);
+ }
+ LogFlow(("IntNetR0Init: returns %Rrc\n", rc));
+ return rc;
+}
+
diff --git a/src/VBox/Devices/Network/VBoxLibSsh.def b/src/VBox/Devices/Network/VBoxLibSsh.def
new file mode 100644
index 00000000..3d096c17
--- /dev/null
+++ b/src/VBox/Devices/Network/VBoxLibSsh.def
@@ -0,0 +1,54 @@
+; $Id: VBoxLibSsh.def $
+;; @file
+; VBoxLibSsh - Definition file for lazy import generation for VBoxDD.
+;
+
+;
+; Copyright (C) 2022-2023 Oracle and/or its affiliates.
+;
+; This file is part of VirtualBox base platform packages, as
+; available from https://www.virtualbox.org.
+;
+; This program is free software; you can redistribute it and/or
+; modify it under the terms of the GNU General Public License
+; as published by the Free Software Foundation, in version 3 of the
+; License.
+;
+; This program is distributed in the hope that it will be useful, but
+; WITHOUT ANY WARRANTY; without even the implied warranty of
+; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+; General Public License for more details.
+;
+; You should have received a copy of the GNU General Public License
+; along with this program; if not, see <https://www.gnu.org/licenses>.
+;
+; SPDX-License-Identifier: GPL-3.0-only
+;
+
+LIBRARY VBoxLibSsh
+EXPORTS
+ ssh_channel_close
+ ssh_channel_free
+ ssh_channel_new
+ ssh_channel_open_session
+ ssh_channel_open_tunnel
+ ssh_channel_poll
+ ssh_channel_read_timeout
+ ssh_channel_request_exec
+ ssh_channel_send_eof
+ ssh_channel_write
+ ssh_connect
+ ssh_disconnect
+ ssh_finalize
+ ssh_free
+ ssh_init
+ ssh_key_free
+ ssh_new
+ ssh_options_set
+ ssh_pki_import_privkey_base64
+ ssh_remove_channel_callbacks
+ ssh_select
+ ssh_set_channel_callbacks
+ ssh_set_log_callback
+ ssh_set_log_userdata
+ ssh_userauth_publickey
diff --git a/src/VBox/Devices/Network/VDEPlug.cpp b/src/VBox/Devices/Network/VDEPlug.cpp
new file mode 100644
index 00000000..5d5a38c9
--- /dev/null
+++ b/src/VBox/Devices/Network/VDEPlug.cpp
@@ -0,0 +1,39 @@
+/** @file
+ *
+ * Module to dynamically load libvdeplug and load all symbols
+ * which are needed by VirtualBox.
+ */
+
+/*
+ * Copyright (C) 2008-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define LOG_GROUP RTLOGGROUP_LDR
+#include <VBox/VDEPlug.h>
+
+
+/* Declarations of the functions that we need. */
+#define VDEPLUG_GENERATE_BODY
+#include <VBox/VDEPlugSymDefs.h>
diff --git a/src/VBox/Devices/Network/lwip-new/CHANGELOG b/src/VBox/Devices/Network/lwip-new/CHANGELOG
new file mode 100644
index 00000000..cf772863
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/CHANGELOG
@@ -0,0 +1,3452 @@
+HISTORY
+
+(CVS HEAD)
+
+ * [Enter new changes just after this line - do not remove this line]
+
+ ++ New features:
+
+ 2014-01-17: Jiri Engelthaler
+ * icmp, icmp6, opt.h: patch #8027: Completed HW checksuming for IPv4 and
+ IPv6 ICMP's
+
+ 2012-03-25: Simon Goldschmidt (idea by Mason)
+ * posix/*: added posix-compatibility include files posix/netdb.h and posix/sys/socket.h
+ which are a simple wrapper to the correct lwIP include files.
+
+ 2012-01-16: Simon Goldschmidt
+ * opt.h, icmp.c: Added option CHECKSUM_GEN_ICMP
+
+ 2011-12-17: Simon Goldschmidt
+ * ip.h: implemented API functions to access so_options of IP pcbs (UDP, TCP, RAW)
+ (fixes bug #35061)
+
+ 2011-09-27: Simon Goldschmidt
+ * opt.h, tcp.c, tcp_in.c: Implemented limiting data on ooseq queue (task #9989)
+ (define TCP_OOSEQ_MAX_BYTES / TCP_OOSEQ_MAX_PBUFS in lwipopts.h)
+
+ 2011-09-21: Simon Goldschmidt
+ * opt.h, api.h, api_lib.c, api_msg.h/.c, sockets.c: Implemented timeout on
+ send (TCP only, bug #33820)
+
+ 2011-09-21: Simon Goldschmidt
+ * init.c: Converted runtime-sanity-checks into compile-time checks that can
+ be disabled (since runtime checks can often not be seen on embedded targets)
+
+ 2011-09-11: Simon Goldschmidt
+ * ppp.h, ppp_impl.h: splitted ppp.h to an internal and external header file
+ to get a clear separation of which functions an application or port may use
+ (task #11281)
+
+ 2011-09-11: Simon Goldschmidt
+ * opt.h, tcp_impl.h, tcp.c, udp.h/.c: Added a config option to randomize
+ initial local TCP/UDP ports (so that different port ranges are used after
+ a reboot; bug #33818; this one added tcp_init/udp_init functions again)
+
+ 2011-09-03: Simon Goldschmidt
+ * dhcp.c: DHCP uses LWIP_RAND() for xid's (bug #30302)
+
+ 2011-08-24: Simon Goldschmidt
+ * opt.h, netif.h/.c: added netif remove callback (bug #32397)
+
+ 2011-07-26: Simon Goldschmidt
+ * etharp.c: ETHARP_SUPPORT_VLAN: add support for an external VLAN filter
+ function instead of only checking for one VLAN (define ETHARP_VLAN_CHECK_FN)
+
+ 2011-07-21: Simon Goldschmidt (patch by hanhui)
+ * ip4.c, etharp.c, pbuf.h: bug #33634 ip_forward() have a faulty behaviour:
+ Added pbuf flags to mark incoming packets as link-layer broadcast/multicast.
+ Also added code to allow ip_forward() to forward non-broadcast packets to
+ the input netif (set IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1).
+
+ 2011-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)
+ * tcpip.c/.h: patch #7449 allow tcpip callback from interrupt with static
+ memory message
+
+
+ ++ Bugfixes:
+
+ 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-09-26: Simon Goldschmidt
+ * api_msg.c: fixed bug #37405 'err_tcp()' uses already freed 'netconn' object
+
+ 2012-09-26: patch by Henrik Persson
+ * dhcp.c: patch #7843 Fix corner case with dhcp timeouts
+
+ 2012-09-26: patch by Henrik Persson
+ * dhcp.c: patch #7840 Segfault in dhcp_parse_reply if no end marker in dhcp packet
+
+ 2012-08-22: Simon Goldschmidt
+ * memp.c: fixed bug #37166: memp_sanity check loops itself
+
+ 2012-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
+ 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
+
+ 2012-03-27: Simon Goldschmidt (patch by Mason)
+ * tcp_out.c: fixed bug #35945: SYN packet should provide the recv MSS not the
+ send MSS
+
+ 2012-03-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
+
+ 2012-03-20: Simon Goldschmidt (patch by Mason)
+ * netdb.c: fixed bug #35907: lwip_gethostbyname_r returns an invalid h_addr_list
+
+ 2012-03-12: Simon Goldschmidt (patch by Bostjan Meglic)
+ * ppp.c: fixed bug #35809: PPP GetMask(): Compiler warning on big endian,
+ possible bug on little endian system
+
+ 2012-02-23: Simon Goldschmidt
+ * etharp.c: fixed bug #35595: Impossible to send broadcast without a gateway
+ (introduced when fixing bug# 33551)
+
+ 2012-02-16: Simon Goldschmidt
+ * ppp.c: fixed pbuf leak when PPP session is aborted through pppSigHUP()
+ (bug #35541: PPP Memory Leak)
+
+ 2012-02-16: Simon Goldschmidt
+ * etharp.c: fixed bug #35531: Impossible to send multicast without a gateway
+ (introduced when fixing bug# 33551)
+
+ 2012-02-16: Simon Goldschmidt (patch by Stéphane Lesage)
+ * msg_in.c, msg_out.c: fixed bug #35536 SNMP: error too big response is malformed
+
+ 2012-02-15: Simon Goldschmidt
+ * init.c: fixed bug #35537: MEMP_NUM_* sanity checks should be disabled with
+ MEMP_MEM_MALLOC==1
+
+ 2012-02-12: Simon Goldschmidt
+ * tcp.h, tcp_in.c, tcp_out.c: partly fixed bug #25882: TCP hangs on
+ MSS > pcb->snd_wnd (by not creating segments bigger than half the window)
+
+ 2012-02-11: Simon Goldschmidt
+ * tcp.c: fixed bug #35435: No pcb state check before adding it to time-wait
+ queue while closing
+
+ 2012-01-22: Simon Goldschmidt
+ * tcp.c, tcp_in.c: fixed bug #35305: pcb may be freed too early on shutdown(WR)
+
+ 2012-01-21: Simon Goldschmidt
+ * tcp.c: fixed bug #34636: FIN_WAIT_2 - Incorrect shutdown of TCP pcb
+
+ 2012-01-20: Simon Goldschmidt
+ * dhcp.c: fixed bug #35151: DHCP asserts on incoming option lengths
+
+ 2012-01-20: Simon Goldschmidt
+ * pbuf.c: fixed bug #35291: NULL pointer in pbuf_copy
+
+ 2011-11-25: Simon Goldschmidt
+ * tcp.h/.c, tcp_impl.h, tcp_in.c: fixed bug #31177: tcp timers can corrupt
+ tcp_active_pcbs in some cases
+
+ 2011-11-23: Simon Goldschmidt
+ * sys.c: fixed bug #34884: sys_msleep() body needs to be surrounded with
+ '#ifndef sys_msleep'
+
+ 2011-11-22: Simon Goldschmidt
+ * netif.c, etharp.h/.c: fixed bug #34684: Clear the arp table cache when
+ netif is brought down
+
+ 2011-10-28: Simon Goldschmidt
+ * tcp_in.c: fixed bug #34638: Dead code in tcp_receive - pcb->dupacks
+
+ 2011-10-23: Simon Goldschmidt
+ * mem.c: fixed bug #34429: possible memory corruption with
+ LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT set to 1
+
+ 2011-10-18: Simon Goldschmidt
+ * arch.h, netdb.c: fixed bug #34592: lwip_gethostbyname_r uses nonstandard
+ error value
+
+ 2011-10-18: Simon Goldschmidt
+ * opt.h: fixed default values of TCP_SNDLOWAT and TCP_SNDQUEUELOWAT for small
+ windows (bug #34176 select after non-blocking send times out)
+
+ 2011-10-18: Simon Goldschmidt
+ * tcp_impl.h, tcp_out.c: fixed bug #34587: TCP_BUILD_MSS_OPTION doesn't
+ consider netif->mtu, causes slow network
+
+ 2011-10-18: Simon Goldschmidt
+ * sockets.c: fixed bug #34581 missing parentheses in udplite sockets code
+
+ 2011-10-18: Simon Goldschmidt
+ * sockets.h: fixed bug #34580 fcntl() is missing in LWIP_COMPAT_SOCKETS
+
+ 2011-10-17: Simon Goldschmidt
+ * api_msg.c: fixed bug #34569: shutdown(SHUT_WR) crashes netconn/socket api
+
+ 2011-10-13: Simon Goldschmidt
+ * tcp_in.c, tcp_out.c: fixed bug #34517 (persist timer is started although no
+ zero window is received) by starting the persist timer when a zero window is
+ received, not when we have more data queued for sending than fits into the
+ window
+
+ 2011-10-13: Simon Goldschmidt
+ * def.h, timers.c: fixed bug #34541: LWIP_U32_DIFF is unnecessarily complex
+
+ 2011-10-13: Simon Goldschmidt
+ * sockets.c, api_lib.c: fixed bug #34540: compiler error when CORE_LOCKING is
+ used and not all protocols are enabled
+
+ 2011-10-12: Simon Goldschmidt
+ * pbuf.c: fixed bug #34534: Error in sending fragmented IP if MEM_ALIGNMENT > 4
+
+ 2011-10-09: Simon Goldschmidt
+ * tcp_out.c: fixed bug #34426: tcp_zero_window_probe() transmits incorrect
+ byte value when pcb->unacked != NULL
+
+ 2011-10-09: Simon Goldschmidt
+ * ip4.c: fixed bug #34447 LWIP_IP_ACCEPT_UDP_PORT(dst_port) wrong
+
+ 2011-09-27: Simon Goldschmidt
+ * tcp_in.c, tcp_out.c: Reset pcb->unsent_oversize in 2 more places...
+
+ 2011-09-27: Simon Goldschmidt
+ * tcp_in.c: fixed bug #28288: Data after FIN in oos queue
+
+ 2011-09-27: Simon Goldschmidt
+ * dhcp.c: fixed bug #34406 dhcp_option_hostname() can overflow the pbuf
+
+ 2011-09-24: Simon Goldschmidt
+ * mem.h: fixed bug #34377 MEM_SIZE_F is not defined if MEM_LIBC_MALLOC==1
+
+ 2011-09-23: Simon Goldschmidt
+ * pbuf.h, tcp.c, tcp_in.c: fixed bug #33871: rejecting TCP_EVENT_RECV() for
+ the last packet including FIN can lose data
+
+ 2011-09-22: Simon Goldschmidt
+ * tcp_impl.h: fixed bug #34355: nagle does not take snd_buf/snd_queuelen into
+ account
+
+ 2011-09-21: Simon Goldschmidt
+ * opt.h: fixed default value of TCP_SND_BUF to not violate the sanity checks
+ in init.c
+
+ 2011-09-20: Simon Goldschmidt
+ * timers.c: fixed bug #34337 (possible NULL pointer in sys_check_timeouts)
+
+ 2011-09-11: Simon Goldschmidt
+ * tcp_out.c: use pcb->mss instead of TCP_MSS for preallocate mss-sized pbufs
+ (bug #34019)
+
+ 2011-09-09: Simon Goldschmidt
+ * udp.c: fixed bug #34072: UDP broadcast is received from wrong UDP pcb if
+ udp port matches
+
+ 2011-09-03: Simon Goldschmidt
+ * tcp_in.c: fixed bug #33952 PUSH flag in incoming packet is lost when packet
+ is aggregated and sent to application
+
+ 2011-09-01: Simon Goldschmidt
+ * opt.h: fixed bug #31809 LWIP_EVENT_API in opts.h is inconsistent compared
+ to other options
+
+ 2011-09-01: Simon Goldschmidt
+ * tcp_in.c: fixed bug #34111 RST for ACK to listening pcb has wrong seqno
+
+ 2011-08-24: Simon Goldschmidt
+ * 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
+
+ 2011-08-24: Simon Goldschmidt
+ * sockets.h: fixed bug #34057 socklen_t should be a typedef
+
+ 2011-08-24: Simon Goldschmidt
+ * pbuf.c: fixed bug #34112 Odd check in pbuf_alloced_custom (typo)
+
+ 2011-08-24: Simon Goldschmidt
+ * dhcp.c: fixed bug #34122 dhcp: hostname can overflow
+
+ 2011-08-24: Simon Goldschmidt
+ * netif.c: fixed bug #34121 netif_add/netif_set_ipaddr fail on NULL ipaddr
+
+ 2011-08-22: Simon Goldschmidt
+ * tcp_out.c: fixed bug #33962 TF_FIN not always set after FIN is sent. (This
+ merely prevents nagle from not transmitting fast after closing.)
+
+ 2011-07-22: Simon Goldschmidt
+ * api_lib.c, api_msg.c, sockets.c, api.h: fixed bug #31084 (socket API returns
+ always EMSGSIZE on non-blocking sockets if data size > send buffers) -> now
+ lwip_send() sends as much as possible for non-blocking sockets
+
+ 2011-07-22: Simon Goldschmidt
+ * pbuf.c/.h, timers.c: freeing ooseq pbufs when the pbuf pool is empty implemented
+ for NO_SYS==1: when not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ()
+ at regular intervals from main level.
+
+ 2011-07-21: Simon Goldschmidt
+ * etharp.c: fixed bug #33551 (ARP entries may time out although in use) by
+ sending an ARP request when an ARP entry is used in the last minute before
+ it would time out.
+
+ 2011-07-04: Simon Goldschmidt
+ * sys_arch.txt: Fixed documentation after changing sys arch prototypes for 1.4.0.
+
+ 2011-06-26: Simon Goldschmidt
+ * tcp.c: fixed bug #31723 (tcp_kill_prio() kills pcbs with the same prio) by
+ updating its documentation only.
+
+ 2011-06-26: Simon Goldschmidt
+ * mem.c: fixed bug #33545: With MEM_USE_POOLS==1, mem_malloc can return an
+ unaligned pointer.
+
+ 2011-06-26: Simon Goldschmidt
+ * mem.c: fixed bug #33544 "warning in mem.c in lwip 1.4.0 with NO_SYS=1"
+
+
+
+(STABLE-1.4.0)
+
+ ++ New features:
+
+ 2011-03-27: Simon Goldschmidt
+ * tcp_impl.h, tcp_in.c, tcp_out.c: Removed 'dataptr' from 'struct tcp_seg' and
+ calculate it in tcp_zero_window_probe (the only place where it was used).
+
+ 2010-11-21: Simon Goldschmidt
+ * dhcp.c/.h: Added a function to deallocate the struct dhcp from a netif
+ (fixes bug #31525).
+
+ 2010-07-12: Simon Goldschmidt (patch by Stephane Lesage)
+ * ip.c, udp.c/.h, pbuf.h, sockets.c: task #10495: Added support for
+ IP_MULTICAST_LOOP at socket- and raw-API level.
+
+ 2010-06-16: Simon Goldschmidt
+ * ip.c: Added an optional define (LWIP_IP_ACCEPT_UDP_PORT) that can allow
+ link-layer-addressed UDP traffic to be received while a netif is down (just
+ like DHCP during configuration)
+
+ 2010-05-22: Simon Goldschmidt
+ * many many files: bug #27352: removed packing from ip_addr_t, the packed
+ version is now only used in protocol headers. Added global storage for
+ current src/dest IP address while in input functions.
+
+ 2010-05-16: Simon Goldschmidt
+ * def.h: task #10391: Add preprocessor-macros for compile-time htonl
+ calculation (and use them throughout the stack where applicable)
+
+ 2010-05-16: Simon Goldschmidt
+ * opt.h, memp_std.h, memp.c, ppp_oe.h/.c: PPPoE now uses its own MEMP pool
+ instead of the heap (moved struct pppoe_softc from ppp_oe.c to ppp_oe.h)
+
+ 2010-05-16: Simon Goldschmidt
+ * opt.h, memp_std.h, dns.h/.c: DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses its own
+ MEMP pool instead of the heap
+
+ 2010-05-13: Simon Goldschmidt
+ * tcp.c, udp.c: task #6995: Implement SO_REUSEADDR (correctly), added
+ new option SO_REUSE_RXTOALL to pass received UDP broadcast/multicast
+ packets to more than one pcb.
+
+ 2010-05-02: Simon Goldschmidt
+ * netbuf.h/.c, sockets.c, api_msg.c: use checksum-on-copy for sending
+ UDP data for LWIP_NETIF_TX_SINGLE_PBUF==1
+
+ 2010-04-30: Simon Goldschmidt
+ * udp.h/.c, pbuf.h/.c: task #6849: added udp_send(_to/_if) functions that
+ take a precalculated checksum, added pbuf_fill_chksum() to copy data
+ into a pbuf and at the same time calculating the checksum for that data
+
+ 2010-04-29: Simon Goldschmidt
+ * ip_addr.h, etharp.h/.c, autoip.c: Create overridable macros for copying
+ 2-byte-aligned IP addresses and MAC addresses
+
+ 2010-04-28: Patch by Bill Auerbach
+ * ip.c: Inline generating IP checksum to save a function call
+
+ 2010-04-14: Simon Goldschmidt
+ * tcpip.h/.c, timers.c: Added an overridable define to get informed when the
+ tcpip_thread processes messages or timeouts to implement a watchdog.
+
+ 2010-03-28: Simon Goldschmidt
+ * ip_frag.c: create a new (contiguous) PBUF_RAM for every outgoing
+ fragment if LWIP_NETIF_TX_SINGLE_PBUF==1
+
+ 2010-03-27: Simon Goldschmidt
+ * etharp.c: Speedup TX by moving code from find_entry to etharp_output/
+ etharp_query to prevent unnecessary function calls (inspired by
+ patch #7135).
+
+ 2010-03-20: Simon Goldschmidt
+ * opt.h, tcpip.c/.h: Added an option to disable tcpip_(un)timeout code
+ since the linker cannot do this automatically to save space.
+
+ 2010-03-20: Simon Goldschmidt
+ * opt.h, etharp.c/.h: Added support for static ARP table entries
+
+ 2010-03-14: Simon Goldschmidt
+ * tcp_impl.h, tcp_out.c, inet_chksum.h/.c: task #6849: Calculate checksum
+ when creating TCP segments, not when (re-)transmitting them.
+
+ 2010-03-07: Simon Goldschmidt
+ * sockets.c: bug #28775 (select/event_callback: only check select_cb_list
+ on change) plus use SYS_LIGHTWEIGHT_PROT to protect the select code.
+ This should speed up receiving data on sockets as the select code in
+ event_callback is only executed when select is waiting.
+
+ 2010-03-06: Simon Goldschmidt
+ * tcp_out.c: task #7013 (Create option to have all packets delivered to
+ netif->output in one piece): Always copy to try to create single pbufs
+ in tcp_write.
+
+ 2010-03-06: Simon Goldschmidt
+ * api.h, api_lib.c, sockets.c: task #10167 (sockets: speed up TCP recv
+ by not allocating a netbuf): added function netconn_recv_tcp_pbuf()
+ for tcp netconns to receive pbufs, not netbufs; use that function
+ for tcp sockets.
+
+ 2010-03-05: Jakob Ole Stoklundsen / Simon Goldschmidt
+ * opt.h, tcp.h, tcp_impl.h, tcp.c, tcp_in.c, tcp_out.c: task #7040:
+ Work on tcp_enqueue: Don't waste memory when chaining segments,
+ added option TCP_OVERSIZE to prevent creating many small pbufs when
+ calling tcp_write with many small blocks of data. Instead, pbufs are
+ allocated larger than needed and the space is used for later calls to
+ tcp_write.
+
+ 2010-02-21: Simon Goldschmidt
+ * stats.c/.h: Added const char* name to mem- and memp-stats for easier
+ debugging.
+
+ 2010-02-21: Simon Goldschmidt
+ * tcp.h (and usages), added tcp_impl.h: Splitted API and internal
+ implementation of tcp to make API usage cleare to application programmers
+
+ 2010-02-14: Simon Goldschmidt/Stephane Lesage
+ * ip_addr.h: Improved some defines working on ip addresses, added faster
+ macro to copy addresses that cannot be NULL
+
+ 2010-02-13: Simon Goldschmidt
+ * api.h, api_lib.c, api_msg.c, sockets.c: task #7865 (implement non-
+ blocking send operation)
+
+ 2010-02-12: Simon Goldschmidt
+ * sockets.c/.h: Added a minimal version of posix fctl() to have a
+ standardised way to set O_NONBLOCK for nonblocking sockets.
+
+ 2010-02-12: Simon Goldschmidt
+ * dhcp.c/.h, autoip.c/.h: task #10139 (Prefer statically allocated
+ memory): added autoip_set_struct() and dhcp_set_struct() to let autoip
+ and dhcp work with user-allocated structs instead of callin mem_malloc
+
+ 2010-02-12: Simon Goldschmidt/Jeff Barber
+ * tcp.c/h: patch #6865 (SO_REUSEADDR for TCP): if pcb.so_options has
+ SOF_REUSEADDR set, allow binding to endpoint in TIME_WAIT
+
+ 2010-02-12: Simon Goldschmidt
+ * sys layer: task #10139 (Prefer statically allocated memory): converted
+ mbox and semaphore functions to take pointers to sys_mbox_t/sys_sem_t;
+ converted sys_mbox_new/sys_sem_new to take pointers and return err_t;
+ task #7212: Add Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX
+ to let sys.h use binary semaphores instead of mutexes - as before)
+
+ 2010-02-09: Simon Goldschmidt (Simon Kallweit)
+ * timers.c/.h: Added function sys_restart_timeouts() from patch #7085
+ (Restart system timeout handling)
+
+ 2010-02-09: Simon Goldschmidt
+ * netif.c/.h, removed loopif.c/.h: task #10153 (Integrate loopif into
+ netif.c) - loopif does not have to be created by the port any more,
+ just define LWIP_HAVE_LOOPIF to 1.
+
+ 2010-02-08: Simon Goldschmidt
+ * inet.h, ip_addr.c/.h: Added reentrant versions of inet_ntoa/ipaddr_ntoa
+ inet_ntoa_r/ipaddr_ntoa_r
+
+ 2010-02-08: Simon Goldschmidt
+ * netif.h: Added netif_s/get_igmp_mac_filter() macros
+
+ 2010-02-05: Simon Goldschmidt
+ * netif.h: Added function-like macros to get/set the hostname on a netif
+
+ 2010-02-04: Simon Goldschmidt
+ * nearly every file: Replaced struct ip_addr by typedef ip_addr_t to
+ make changing the actual implementation behind the typedef easier.
+
+ 2010-02-01: Simon Goldschmidt
+ * opt.h, memp_std.h, dns.h, netdb.c, memp.c: Let netdb use a memp pool
+ for allocating memory when getaddrinfo() is called.
+
+ 2010-01-31: Simon Goldschmidt
+ * dhcp.h, dhcp.c: Reworked the code that parses DHCP options: parse
+ them once instead of parsing for every option. This also removes
+ the need for mem_malloc from dhcp_recv and makes it possible to
+ correctly retrieve the BOOTP file.
+
+ 2010-01-30: simon Goldschmidt
+ * sockets.c: Use SYS_LIGHTWEIGHT_PROT instead of a semaphore to protect
+ the sockets array.
+
+ 2010-01-29: Simon Goldschmidt (patch by Laura Garrett)
+ * api.h, api_msg.c, sockets.c: Added except set support in select
+ (patch #6860)
+
+ 2010-01-29: Simon Goldschmidt (patch by Laura Garrett)
+ * api.h, sockets.h, err.h, api_lib.c, api_msg.c, sockets.c, err.c:
+ Add non-blocking support for connect (partly from patch #6860),
+ plus many cleanups in socket & netconn API.
+
+ 2010-01-27: Simon Goldschmidt
+ * opt.h, tcp.h, init.c, api_msg.c: Added TCP_SNDQUEUELOWAT corresponding
+ to TCP_SNDLOWAT and added tcp_sndqueuelen() - this fixes bug #28605
+
+ 2010-01-26: Simon Goldschmidt
+ * snmp: Use memp pools for snmp instead of the heap; added 4 new pools.
+
+ 2010-01-14: Simon Goldschmidt
+ * ppp.c/.h: Fixed bug #27856: PPP: Set netif link- and status-callback
+ by adding ppp_set_netif_statuscallback()/ppp_set_netif_linkcallback()
+
+ 2010-01-13: Simon Goldschmidt
+ * mem.c: The heap now may be moved to user-defined memory by defining
+ LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address
+ (patch #6966 and bug #26133)
+
+ 2010-01-10: Simon Goldschmidt (Bill Auerbach)
+ * opt.h, memp.c: patch #6822 (Add option to place memory pools in
+ separate arrays)
+
+ 2010-01-10: Simon Goldschmidt
+ * init.c, igmp.c: patch #6463 (IGMP - Adding Random Delay): added define
+ LWIP_RAND() for lwip-wide randomization (to be defined in cc.h)
+
+ 2009-12-31: Simon Goldschmidt
+ * tcpip.c, init.c, memp.c, sys.c, memp_std.h, sys.h, tcpip.h
+ added timers.c/.h: Separated timer implementation from semaphore/mbox
+ implementation, moved timer implementation to timers.c/.h, timers are
+ now only called from tcpip_thread or by explicitly checking them.
+ (TASK#7235)
+
+ 2009-12-27: Simon Goldschmidt
+ * opt.h, etharp.h/.c, init.c, tcpip.c: Added an additional option
+ LWIP_ETHERNET to support ethernet without ARP (necessary for pure PPPoE)
+
+
+ ++ Bugfixes:
+
+ 2011-04-20: Simon Goldschmidt
+ * sys_arch.txt: sys_arch_timeouts() is not needed any more.
+
+ 2011-04-13: Simon Goldschmidt
+ * tcp.c, udp.c: Fixed bug #33048 (Bad range for IP source port numbers) by
+ using ports in the IANA private/dynamic range (49152 through 65535).
+
+ 2011-03-29: Simon Goldschmidt, patch by Emil Lhungdahl:
+ * etharp.h/.c: Fixed broken VLAN support.
+
+ 2011-03-27: Simon Goldschmidt
+ * tcp.c: Fixed bug #32926 (TCP_RMV(&tcp_bound_pcbs) is called on unbound tcp
+ pcbs) by checking if the pcb was bound (local_port != 0).
+
+ 2011-03-27: Simon Goldschmidt
+ * ppp.c: Fixed bug #32280 (ppp: a pbuf is freed twice)
+
+ 2011-03-27: Simon Goldschmidt
+ * sockets.c: Fixed bug #32906: lwip_connect+lwip_send did not work for udp and
+ raw pcbs with LWIP_TCPIP_CORE_LOCKING==1.
+
+ 2011-03-27: Simon Goldschmidt
+ * tcp_out.c: Fixed bug #32820 (Outgoing TCP connections created before route
+ is present never times out) by starting retransmission timer before checking
+ route.
+
+ 2011-03-22: Simon Goldschmidt
+ * ppp.c: Fixed bug #32648 (PPP code crashes when terminating a link) by only
+ calling sio_read_abort() if the file descriptor is valid.
+
+ 2011-03-14: Simon Goldschmidt
+ * err.h/.c, sockets.c, api_msg.c: fixed bug #31748 (Calling non-blocking connect
+ more than once can render a socket useless) since it mainly involves changing
+ "FATAL" classification of error codes: ERR_USE and ERR_ISCONN just aren't fatal.
+
+ 2011-03-13: Simon Goldschmidt
+ * sockets.c: fixed bug #32769 (ESHUTDOWN is linux-specific) by fixing
+ err_to_errno_table (ERR_CLSD: ENOTCONN instead of ESHUTDOWN), ERR_ISCONN:
+ use EALRADY instead of -1
+
+ 2011-03-13: Simon Goldschmidt
+ * api_lib.c: netconn_accept: return ERR_ABRT instead of ERR_CLSD if the
+ connection has been aborted by err_tcp (since this is not a normal closing
+ procedure).
+
+ 2011-03-13: Simon Goldschmidt
+ * tcp.c: tcp_bind: return ERR_VAL instead of ERR_ISCONN when trying to bind
+ with pcb->state != CLOSED
+
+ 2011-02-17: Simon Goldschmidt
+ * rawapi.txt: Fixed bug #32561 tcp_poll argument definition out-of-order in
+ documentation
+
+ 2011-02-17: Simon Goldschmidt
+ * many files: Added missing U/UL modifiers to fix 16-bit-arch portability.
+
+ 2011-01-24: Simon Goldschmidt
+ * sockets.c: Fixed bug #31741: lwip_select seems to have threading problems
+
+ 2010-12-02: Simon Goldschmidt
+ * err.h: Fixed ERR_IS_FATAL so that ERR_WOULDBLOCK is not fatal.
+
+ 2010-11-23: Simon Goldschmidt
+ * api.h, api_lib.c, api_msg.c, sockets.c: netconn.recv_avail is only used for
+ LWIP_SO_RCVBUF and ioctl/FIONREAD.
+
+ 2010-11-23: Simon Goldschmidt
+ * etharp.c: Fixed bug #31720: ARP-queueing: RFC 1122 recommends to queue at
+ least 1 packet -> ARP_QUEUEING==0 now queues the most recent packet.
+
+ 2010-11-23: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #30577: tcp_input: don't discard ACK-only packets after
+ refusing 'refused_data' again.
+
+ 2010-11-22: Simon Goldschmidt
+ * sockets.c: Fixed bug #31590: getsockopt(... SO_ERROR ...) gives EINPROGRESS
+ after a successful nonblocking connection.
+
+ 2010-11-22: Simon Goldschmidt
+ * etharp.c: Fixed bug #31722: IP packets sent with an AutoIP source addr
+ must be sent link-local
+
+ 2010-11-22: Simon Goldschmidt
+ * timers.c: patch #7329: tcp_timer_needed prototype was ifdef'ed out for
+ LWIP_TIMERS==0
+
+ 2010-11-20: Simon Goldschmidt
+ * sockets.c: Fixed bug #31170: lwip_setsockopt() does not set socket number
+
+ 2010-11-20: Simon Goldschmidt
+ * sockets.h: Fixed bug #31304: Changed SHUT_RD, SHUT_WR and SHUT_RDWR to
+ resemble other stacks.
+
+ 2010-11-20: Simon Goldschmidt
+ * dns.c: Fixed bug #31535: TCP_SND_QUEUELEN must be at least 2 or else
+ no-copy TCP writes will never succeed.
+
+ 2010-11-20: Simon Goldschmidt
+ * dns.c: Fixed bug #31701: Error return value from dns_gethostbyname() does
+ not match documentation: return ERR_ARG instead of ERR_VAL if not
+ initialized or wrong argument.
+
+ 2010-10-20: Simon Goldschmidt
+ * sockets.h: Fixed bug #31385: sizeof(struct sockaddr) is 30 but should be 16
+
+ 2010-10-05: Simon Goldschmidt
+ * dhcp.c: Once again fixed #30038: DHCP/AutoIP cooperation failed when
+ replugging the network cable after an AutoIP address was assigned.
+
+ 2010-08-10: Simon Goldschmidt
+ * tcp.c: Fixed bug #30728: tcp_new_port() did not check listen pcbs
+
+ 2010-08-03: Simon Goldschmidt
+ * udp.c, raw.c: Don't chain empty pbufs when sending them (fixes bug #30625)
+
+ 2010-08-01: Simon Goldschmidt (patch by Greg Renda)
+ * ppp.c: Applied patch #7264 (PPP protocols are rejected incorrectly on big
+ endian architectures)
+
+ 2010-07-28: Simon Goldschmidt
+ * api_lib.c, api_msg.c, sockets.c, mib2.c: Fixed compilation with TCP or UDP
+ disabled.
+
+ 2010-07-27: Simon Goldschmidt
+ * tcp.c: Fixed bug #30565 (tcp_connect() check bound list): that check did no
+ harm but never did anything
+
+ 2010-07-21: Simon Goldschmidt
+ * ip.c: Fixed invalid fix for bug #30402 (CHECKSUM_GEN_IP_INLINE does not
+ add IP options)
+
+ 2010-07-16: Kieran Mansley
+ * msg_in.c: Fixed SNMP ASN constant defines to not use ! operator
+
+ 2010-07-10: Simon Goldschmidt
+ * ip.c: Fixed bug #30402: CHECKSUM_GEN_IP_INLINE does not add IP options
+
+ 2010-06-30: Simon Goldschmidt
+ * api_msg.c: fixed bug #30300 (shutdown parameter was not initialized in
+ netconn_delete)
+
+ 2010-06-28: Kieran Mansley
+ * timers.c remove unportable printing of C function pointers
+
+ 2010-06-24: Simon Goldschmidt
+ * init.c, timers.c/.h, opt.h, memp_std.h: From patch #7221: added flag
+ NO_SYS_NO_TIMERS to drop timer support for NO_SYS==1 for easier upgrading
+
+ 2010-06-24: Simon Goldschmidt
+ * api(_lib).c/.h, api_msg.c/.h, sockets.c/.h: Fixed bug #10088: Correctly
+ implemented shutdown at socket level.
+
+ 2010-06-21: Simon Goldschmidt
+ * pbuf.c/.h, ip_frag.c/.h, opt.h, memp_std.h: Fixed bug #29361 (ip_frag has
+ problems with zero-copy DMA MACs) by adding custom pbufs and implementing
+ custom pbufs that reference other (original) pbufs. Additionally set
+ IP_FRAG_USES_STATIC_BUF=0 as default to be on the safe side.
+
+ 2010-06-15: Simon Goldschmidt
+ * dhcp.c: Fixed bug #29970: DHCP endian issue parsing option responses
+
+ 2010-06-14: Simon Goldschmidt
+ * autoip.c: Fixed bug #30039: AutoIP does not reuse previous addresses
+
+ 2010-06-12: Simon Goldschmidt
+ * dhcp.c: Fixed bug #30038: dhcp_network_changed doesn't reset AUTOIP coop
+ state
+
+ 2010-05-17: Simon Goldschmidt
+ * netdb.c: Correctly NULL-terminate h_addr_list
+
+ 2010-05-16: Simon Goldschmidt
+ * def.h/.c: changed the semantics of LWIP_PREFIX_BYTEORDER_FUNCS to prevent
+ "symbol already defined" i.e. when linking to winsock
+
+ 2010-05-05: Simon Goldschmidt
+ * def.h, timers.c: Fixed bug #29769 (sys_check_timeouts: sys_now() may
+ overflow)
+
+ 2010-04-21: Simon Goldschmidt
+ * api_msg.c: Fixed bug #29617 (sometime cause stall on delete listening
+ connection)
+
+ 2010-03-28: Luca Ceresoli
+ * ip_addr.c/.h: patch #7143: Add a few missing const qualifiers
+
+ 2010-03-27: Luca Ceresoli
+ * mib2.c: patch #7130: remove meaningless const qualifiers
+
+ 2010-03-26: Simon Goldschmidt
+ * tcp_out.c: Make LWIP_NETIF_TX_SINGLE_PBUF work for TCP, too
+
+ 2010-03-26: Simon Goldschmidt
+ * various files: Fixed compiling with different options disabled (TCP/UDP),
+ triggered by bug #29345; don't allocate acceptmbox if LWIP_TCP is disabled
+
+ 2010-03-25: Simon Goldschmidt
+ * sockets.c: Fixed bug #29332: lwip_select() processes readset incorrectly
+
+ 2010-03-25: Simon Goldschmidt
+ * tcp_in.c, test_tcp_oos.c: Fixed bug #29080: Correctly handle remote side
+ overrunning our rcv_wnd in ooseq case.
+
+ 2010-03-22: Simon Goldschmidt
+ * tcp.c: tcp_listen() did not copy the pcb's prio.
+
+ 2010-03-19: Simon Goldschmidt
+ * snmp_msg.c: Fixed bug #29256: SNMP Trap address was not correctly set
+
+ 2010-03-14: Simon Goldschmidt
+ * opt.h, etharp.h: Fixed bug #29148 (Incorrect PBUF_POOL_BUFSIZE for ports
+ where ETH_PAD_SIZE > 0) by moving definition of ETH_PAD_SIZE to opt.h
+ and basing PBUF_LINK_HLEN on it.
+
+ 2010-03-08: Simon Goldschmidt
+ * netif.c, ipv4/ip.c: task #10241 (AutoIP: don't break existing connections
+ when assiging routable address): when checking incoming packets and
+ aborting existing connection on address change, filter out link-local
+ addresses.
+
+ 2010-03-06: Simon Goldschmidt
+ * sockets.c: Fixed LWIP_NETIF_TX_SINGLE_PBUF for LWIP_TCPIP_CORE_LOCKING
+
+ 2010-03-06: Simon Goldschmidt
+ * ipv4/ip.c: Don't try to forward link-local addresses
+
+ 2010-03-06: Simon Goldschmidt
+ * etharp.c: Fixed bug #29087: etharp: don't send packets for LinkLocal-
+ addresses to gw
+
+ 2010-03-05: Simon Goldschmidt
+ * dhcp.c: Fixed bug #29072: Correctly set ciaddr based on message-type
+ and state.
+
+ 2010-03-05: Simon Goldschmidt
+ * api_msg.c: Correctly set TCP_WRITE_FLAG_MORE when netconn_write is split
+ into multiple calls to tcp_write.
+
+ 2010-02-21: Simon Goldschmidt
+ * opt.h, mem.h, dns.c: task #10140: Remove DNS_USES_STATIC_BUF (keep
+ the implementation of DNS_USES_STATIC_BUF==1)
+
+ 2010-02-20: Simon Goldschmidt
+ * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Task #10088: Correctly implement
+ close() vs. shutdown(). Now the application does not get any more
+ recv callbacks after calling tcp_close(). Added tcp_shutdown().
+
+ 2010-02-19: Simon Goldschmidt
+ * mem.c/.h, pbuf.c: Renamed mem_realloc() to mem_trim() to prevent
+ confusion with realloc()
+
+ 2010-02-15: Simon Goldschmidt/Stephane Lesage
+ * netif.c/.h: Link status does not depend on LWIP_NETIF_LINK_CALLBACK
+ (fixes bug #28899)
+
+ 2010-02-14: Simon Goldschmidt
+ * netif.c: Fixed bug #28877 (Duplicate ARP gratuitous packet with
+ LWIP_NETIF_LINK_CALLBACK set on) by only sending if both link- and
+ admin-status of a netif are up
+
+ 2010-02-14: Simon Goldschmidt
+ * opt.h: Disable ETHARP_TRUST_IP_MAC by default since it slows down packet
+ reception and is not really necessary
+
+ 2010-02-14: Simon Goldschmidt
+ * etharp.c/.h: Fixed ARP input processing: only add a new entry if a
+ request was directed as us (RFC 826, Packet Reception), otherwise
+ only update existing entries; internalized some functions
+
+ 2010-02-14: Simon Goldschmidt
+ * netif.h, etharp.c, tcpip.c: Fixed bug #28183 (ARP and TCP/IP cannot be
+ disabled on netif used for PPPoE) by adding a new netif flag
+ (NETIF_FLAG_ETHERNET) that tells the stack the device is an ethernet
+ device but prevents usage of ARP (so that ethernet_input can be used
+ for PPPoE).
+
+ 2010-02-12: Simon Goldschmidt
+ * netif.c: netif_set_link_up/down: only do something if the link state
+ actually changes
+
+ 2010-02-12: Simon Goldschmidt/Stephane Lesage
+ * api_msg.c: Fixed bug #28865 (Cannot close socket/netconn in non-blocking
+ connect)
+
+ 2010-02-12: Simon Goldschmidt
+ * mem.h: Fixed bug #28866 (mem_realloc function defined in mem.h)
+
+ 2010-02-09: Simon Goldschmidt
+ * api_lib.c, api_msg.c, sockets.c, api.h, api_msg.h: Fixed bug #22110
+ (recv() makes receive window update for data that wasn't received by
+ application)
+
+ 2010-02-09: Simon Goldschmidt/Stephane Lesage
+ * sockets.c: Fixed bug #28853 (lwip_recvfrom() returns 0 on receive time-out
+ or any netconn_recv() error)
+
+ 2010-02-09: Simon Goldschmidt
+ * ppp.c: task #10154 (PPP: Update snmp in/out counters for tx/rx packets)
+
+ 2010-02-09: Simon Goldschmidt
+ * netif.c: For loopback packets, adjust the stats- and snmp-counters
+ for the loopback netif.
+
+ 2010-02-08: Simon Goldschmidt
+ * igmp.c/.h, ip.h: Moved most defines from igmp.h to igmp.c for clarity
+ since they are not used anywhere else.
+
+ 2010-02-08: Simon Goldschmidt (Stéphane Lesage)
+ * igmp.c, igmp.h, stats.c, stats.h: Improved IGMP stats
+ (patch from bug #28798)
+
+ 2010-02-08: Simon Goldschmidt (Stéphane Lesage)
+ * igmp.c: Fixed bug #28798 (Error in "Max Response Time" processing) and
+ another bug when LWIP_RAND() returns zero.
+
+ 2010-02-04: Simon Goldschmidt
+ * nearly every file: Use macros defined in ip_addr.h (some of them new)
+ to work with IP addresses (preparation for bug #27352 - Change ip_addr
+ from struct to typedef (u32_t) - and better code).
+
+ 2010-01-31: Simon Goldschmidt
+ * netif.c: Don't call the link-callback from netif_set_up/down() since
+ this invalidly retriggers DHCP.
+
+ 2010-01-29: Simon Goldschmidt
+ * ip_addr.h, inet.h, def.h, inet.c, def.c, more: Cleanly separate the
+ portability file inet.h and its contents from the stack: moved htonX-
+ functions to def.h (and the new def.c - they are not ipv4 dependent),
+ let inet.h depend on ip_addr.h and not the other way round.
+ This fixes bug #28732.
+
+ 2010-01-28: Kieran Mansley
+ * tcp.c: Ensure ssthresh >= 2*MSS
+
+ 2010-01-27: Simon Goldschmidt
+ * tcp.h, tcp.c, tcp_in.c: Fixed bug #27871: Calling tcp_abort() in recv
+ callback can lead to accessing unallocated memory. As a consequence,
+ ERR_ABRT means the application has called tcp_abort()!
+
+ 2010-01-25: Simon Goldschmidt
+ * snmp_structs.h, msg_in.c: Partly fixed bug #22070 (MIB_OBJECT_WRITE_ONLY
+ not implemented in SNMP): write-only or not-accessible are still
+ returned by getnext (though not by get)
+
+ 2010-01-24: Simon Goldschmidt
+ * snmp: Renamed the private mib node from 'private' to 'mib_private' to
+ not use reserved C/C++ keywords
+
+ 2010-01-23: Simon Goldschmidt
+ * sockets.c: Fixed bug #28716: select() returns 0 after waiting for less
+ than 1 ms
+
+ 2010-01-21: Simon Goldschmidt
+ * tcp.c, api_msg.c: Fixed bug #28651 (tcp_connect: no callbacks called
+ if tcp_enqueue fails) both in raw- and netconn-API
+
+ 2010-01-19: Simon Goldschmidt
+ * api_msg.c: Fixed bug #27316: netconn: Possible deadlock in err_tcp
+
+ 2010-01-18: Iordan Neshev/Simon Goldschmidt
+ * src/netif/ppp: reorganised PPP sourcecode to 2.3.11 including some
+ bugfix backports from 2.4.x.
+
+ 2010-01-18: Simon Goldschmidt
+ * mem.c: Fixed bug #28679: mem_realloc calculates mem_stats wrong
+
+ 2010-01-17: Simon Goldschmidt
+ * api_lib.c, api_msg.c, (api_msg.h, api.h, sockets.c, tcpip.c):
+ task #10102: "netconn: clean up conn->err threading issues" by adding
+ error return value to struct api_msg_msg
+
+ 2010-01-17: Simon Goldschmidt
+ * api.h, api_lib.c, sockets.c: Changed netconn_recv() and netconn_accept()
+ to return err_t (bugs #27709 and #28087)
+
+ 2010-01-14: Simon Goldschmidt
+ * ...: Use typedef for function prototypes throughout the stack.
+
+ 2010-01-13: Simon Goldschmidt
+ * api_msg.h/.c, api_lib.c: Fixed bug #26672 (close connection when receive
+ window = 0) by correctly draining recvmbox/acceptmbox
+
+ 2010-01-11: Simon Goldschmidt
+ * pap.c: Fixed bug #13315 (PPP PAP authentication can result in
+ erroneous callbacks) by copying the code from recent pppd
+
+ 2010-01-10: Simon Goldschmidt
+ * raw.c: Fixed bug #28506 (raw_bind should filter received packets)
+
+ 2010-01-10: Simon Goldschmidt
+ * tcp.h/.c: bug #28127 (remove call to tcp_output() from tcp_ack(_now)())
+
+ 2010-01-08: Simon Goldschmidt
+ * sockets.c: Fixed bug #28519 (lwip_recvfrom bug with len > 65535)
+
+ 2010-01-08: Simon Goldschmidt
+ * dns.c: Copy hostname for DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1 since string
+ passed to dns_local_addhost() might be volatile
+
+ 2010-01-07: Simon Goldschmidt
+ * timers.c, tcp.h: Call tcp_timer_needed() with NO_SYS==1, too
+
+ 2010-01-06: Simon Goldschmidt
+ * netdb.h: Fixed bug #28496: missing include guards in netdb.h
+
+ 2009-12-31: Simon Goldschmidt
+ * many ppp files: Reorganised PPP source code from ucip structure to pppd
+ structure to easily compare our code against the pppd code (around v2.3.1)
+
+ 2009-12-27: Simon Goldschmidt
+ * tcp_in.c: Another fix for bug #28241 (ooseq processing) and adapted
+ unit test
+
+
+(STABLE-1.3.2)
+
+ ++ New features:
+
+ 2009-10-27 Simon Goldschmidt/Stephan Lesage
+ * netifapi.c/.h: Added netifapi_netif_set_addr()
+
+ 2009-10-07 Simon Goldschmidt/Fabian Koch
+ * api_msg.c, netbuf.c/.h, opt.h: patch #6888: Patch for UDP Netbufs to
+ support dest-addr and dest-port (optional: LWIP_NETBUF_RECVINFO)
+
+ 2009-08-26 Simon Goldschmidt/Simon Kallweit
+ * slipif.c/.h: bug #26397: SLIP polling support
+
+ 2009-08-25 Simon Goldschmidt
+ * opt.h, etharp.h/.c: task #9033: Support IEEE 802.1q tagged frame (VLAN),
+ New configuration options ETHARP_SUPPORT_VLAN and ETHARP_VLAN_CHECK.
+
+ 2009-08-25 Simon Goldschmidt
+ * ip_addr.h, netdb.c: patch #6900: added define ip_ntoa(struct ip_addr*)
+
+ 2009-08-24 Jakob Stoklund Olesen
+ * autoip.c, dhcp.c, netif.c: patch #6725: Teach AutoIP and DHCP to respond
+ to netif_set_link_up().
+
+ 2009-08-23 Simon Goldschmidt
+ * tcp.h/.c: Added function tcp_debug_state_str() to convert a tcp state
+ to a human-readable string.
+
+ ++ Bugfixes:
+
+ 2009-12-24: Kieran Mansley
+ * tcp_in.c Apply patches from Oleg Tyshev to improve OOS processing
+ (BUG#28241)
+
+ 2009-12-06: Simon Goldschmidt
+ * ppp.h/.c: Fixed bug #27079 (Yet another leak in PPP): outpacket_buf can
+ be statically allocated (like in ucip)
+
+ 2009-12-04: Simon Goldschmidt (patch by Ioardan Neshev)
+ * pap.c: patch #6969: PPP: missing PAP authentication UNTIMEOUT
+
+ 2009-12-03: Simon Goldschmidt
+ * tcp.h, tcp_in.c, tcp_out.c: Fixed bug #28106: dup ack for fast retransmit
+ could have non-zero length
+
+ 2009-12-02: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #27904: TCP sends too many ACKs: delay resetting
+ tcp_input_pcb until after calling the pcb's callbacks
+
+ 2009-11-29: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #28054: Two segments with FIN flag on the out-of-
+ sequence queue, also fixed PBUF_POOL leak in the out-of-sequence code
+
+ 2009-11-29: Simon Goldschmidt
+ * pbuf.c: Fixed bug #28064: pbuf_alloc(PBUF_POOL) is not thread-safe by
+ queueing a call into tcpip_thread to free ooseq-bufs if the pool is empty
+
+ 2009-11-26: Simon Goldschmidt
+ * tcp.h: Fixed bug #28098: Nagle can prevent fast retransmit from sending
+ segment
+
+ 2009-11-26: Simon Goldschmidt
+ * tcp.h, sockets.c: Fixed bug #28099: API required to disable Nagle
+ algorithm at PCB level
+
+ 2009-11-22: Simon Goldschmidt
+ * tcp_out.c: Fixed bug #27905: FIN isn't combined with data on unsent
+
+ 2009-11-22: Simon Goldschmidt (suggested by Bill Auerbach)
+ * tcp.c: tcp_alloc: prevent increasing stats.err for MEMP_TCP_PCB when
+ reusing time-wait pcb
+
+ 2009-11-20: Simon Goldschmidt (patch by Albert Bartel)
+ * sockets.c: Fixed bug #28062: Data received directly after accepting
+ does not wake up select
+
+ 2009-11-11: Simon Goldschmidt
+ * netdb.h: Fixed bug #27994: incorrect define for freeaddrinfo(addrinfo)
+
+ 2009-10-30: Simon Goldschmidt
+ * opt.h: Increased default value for TCP_MSS to 536, updated default
+ value for TCP_WND to 4*TCP_MSS to keep delayed ACK working.
+
+ 2009-10-28: Kieran Mansley
+ * tcp_in.c, tcp_out.c, tcp.h: re-work the fast retransmission code
+ to follow algorithm from TCP/IP Illustrated
+
+ 2009-10-27: Kieran Mansley
+ * tcp_in.c: fix BUG#27445: grow cwnd with every duplicate ACK
+
+ 2009-10-25: Simon Goldschmidt
+ * tcp.h: bug-fix in the TCP_EVENT_RECV macro (has to call tcp_recved if
+ pcb->recv is NULL to keep rcv_wnd correct)
+
+ 2009-10-25: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #26251: RST process in TIME_WAIT TCP state
+
+ 2009-10-23: Simon Goldschmidt (David Empson)
+ * tcp.c: Fixed bug #27783: Silly window avoidance for small window sizes
+
+ 2009-10-21: Simon Goldschmidt
+ * tcp_in.c: Fixed bug #27215: TCP sent() callback gives leading and
+ trailing 1 byte len (SYN/FIN)
+
+ 2009-10-21: Simon Goldschmidt
+ * tcp_out.c: Fixed bug #27315: zero window probe and FIN
+
+ 2009-10-19: Simon Goldschmidt
+ * dhcp.c/.h: Minor code simplification (don't store received pbuf, change
+ conditional code to assert where applicable), check pbuf length before
+ testing for valid reply
+
+ 2009-10-19: Simon Goldschmidt
+ * dhcp.c: Removed most calls to udp_connect since they aren't necessary
+ when using udp_sendto_if() - always stay connected to IP_ADDR_ANY.
+
+ 2009-10-16: Simon Goldschmidt
+ * ip.c: Fixed bug #27390: Source IP check in ip_input() causes it to drop
+ valid DHCP packets -> allow 0.0.0.0 as source address when LWIP_DHCP is
+ enabled
+
+ 2009-10-15: Simon Goldschmidt (Oleg Tyshev)
+ * tcp_in.c: Fixed bug #27329: dupacks by unidirectional data transmit
+
+ 2009-10-15: Simon Goldschmidt
+ * api_lib.c: Fixed bug #27709: conn->err race condition on netconn_recv()
+ timeout
+
+ 2009-10-15: Simon Goldschmidt
+ * autoip.c: Fixed bug #27704: autoip starts with wrong address
+ LWIP_AUTOIP_CREATE_SEED_ADDR() returned address in host byte order instead
+ of network byte order
+
+ 2009-10-11 Simon Goldschmidt (Jörg Kesten)
+ * tcp_out.c: Fixed bug #27504: tcp_enqueue wrongly concatenates segments
+ which are not consecutive when retransmitting unacked segments
+
+ 2009-10-09 Simon Goldschmidt
+ * opt.h: Fixed default values of some stats to only be enabled if used
+ Fixes bug #27338: sys_stats is defined when NO_SYS = 1
+
+ 2009-08-30 Simon Goldschmidt
+ * ip.c: Fixed bug bug #27345: "ip_frag() does not use the LWIP_NETIF_LOOPBACK
+ function" by checking for loopback before calling ip_frag
+
+ 2009-08-25 Simon Goldschmidt
+ * dhcp.c: fixed invalid dependency to etharp_query if DHCP_DOES_ARP_CHECK==0
+
+ 2009-08-23 Simon Goldschmidt
+ * ppp.c: bug #27078: Possible memory leak in pppInit()
+
+ 2009-08-23 Simon Goldschmidt
+ * netdb.c, dns.c: bug #26657: DNS, if host name is "localhost", result
+ is error.
+
+ 2009-08-23 Simon Goldschmidt
+ * opt.h, init.c: bug #26649: TCP fails when TCP_MSS > TCP_SND_BUF
+ Fixed wrong parenthesis, added check in init.c
+
+ 2009-08-23 Simon Goldschmidt
+ * ppp.c: bug #27266: wait-state debug message in pppMain occurs every ms
+
+ 2009-08-23 Simon Goldschmidt
+ * many ppp files: bug #27267: Added include to string.h where needed
+
+ 2009-08-23 Simon Goldschmidt
+ * tcp.h: patch #6843: tcp.h macro optimization patch (for little endian)
+
+
+(STABLE-1.3.1)
+
+ ++ New features:
+
+ 2009-05-10 Simon Goldschmidt
+ * opt.h, sockets.c, pbuf.c, netbuf.h, pbuf.h: task #7013: Added option
+ LWIP_NETIF_TX_SINGLE_PBUF to try to create transmit packets from only
+ one pbuf to help MACs that don't support scatter-gather DMA.
+
+ 2009-05-09 Simon Goldschmidt
+ * icmp.h, icmp.c: Shrinked ICMP code, added option to NOT check icoming
+ ECHO pbuf for size (just use it): LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+
+ 2009-05-05 Simon Goldschmidt, Jakob Stoklund Olesen
+ * ip.h, ip.c: Added ip_current_netif() & ip_current_header() to receive
+ extended info about the currently received packet.
+
+ 2009-04-27 Simon Goldschmidt
+ * sys.h: Made SYS_LIGHTWEIGHT_PROT and sys_now() work with NO_SYS=1
+
+ 2009-04-25 Simon Goldschmidt
+ * mem.c, opt.h: Added option MEM_USE_POOLS_TRY_BIGGER_POOL to try the next
+ bigger malloc pool if one is empty (only usable with MEM_USE_POOLS).
+
+ 2009-04-21 Simon Goldschmidt
+ * dns.c, init.c, dns.h, opt.h: task #7507, patch #6786: DNS supports static
+ hosts table. New configuration options DNS_LOCAL_HOSTLIST and
+ DNS_LOCAL_HOSTLIST_IS_DYNAMIC. Also, DNS_LOOKUP_LOCAL_EXTERN() can be defined
+ as an external function for lookup.
+
+ 2009-04-15 Simon Goldschmidt
+ * dhcp.c: patch #6763: Global DHCP XID can be redefined to something more unique
+
+ 2009-03-31 Kieran Mansley
+ * tcp.c, tcp_out.c, tcp_in.c, sys.h, tcp.h, opts.h: add support for
+ TCP timestamp options, off by default. Rework tcp_enqueue() to
+ take option flags rather than specified option data
+
+ 2009-02-18 Simon Goldschmidt
+ * cc.h: Added printf formatter for size_t: SZT_F
+
+ 2009-02-16 Simon Goldschmidt (patch by Rishi Khan)
+ * icmp.c, opt.h: patch #6539: (configurable) response to broadcast- and multicast
+ pings
+
+ 2009-02-12 Simon Goldschmidt
+ * init.h: Added LWIP_VERSION to get the current version of the stack
+
+ 2009-02-11 Simon Goldschmidt (suggested by Gottfried Spitaler)
+ * opt.h, memp.h/.c: added MEMP_MEM_MALLOC to use mem_malloc/mem_free instead
+ of the pool allocator (can save code size with MEM_LIBC_MALLOC if libc-malloc
+ is otherwise used)
+
+ 2009-01-28 Jonathan Larmour (suggested by Bill Bauerbach)
+ * ipv4/inet_chksum.c, ipv4/lwip/inet_chksum.h: inet_chksum_pseudo_partial()
+ is only used by UDPLITE at present, so conditionalise it.
+
+ 2008-12-03 Simon Goldschmidt (base on patch from Luca Ceresoli)
+ * autoip.c: checked in (slightly modified) patch #6683: Customizable AUTOIP
+ "seed" address. This should reduce AUTOIP conflicts if
+ LWIP_AUTOIP_CREATE_SEED_ADDR is overridden.
+
+ 2008-10-02 Jonathan Larmour and Rishi Khan
+ * sockets.c (lwip_accept): Return EWOULDBLOCK if would block on non-blocking
+ socket.
+
+ 2008-06-30 Simon Goldschmidt
+ * mem.c, opt.h, stats.h: fixed bug #21433: Calling mem_free/pbuf_free from
+ interrupt context isn't safe: LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT allows
+ mem_free to run between mem_malloc iterations. Added illegal counter for
+ mem stats.
+
+ 2008-06-27 Simon Goldschmidt
+ * stats.h/.c, some other files: patch #6483: stats module improvement:
+ Added defines to display each module's statistic individually, added stats
+ defines for MEM, MEMP and SYS modules, removed (unused) rexmit counter.
+
+ 2008-06-17 Simon Goldschmidt
+ * err.h: patch #6459: Made err_t overridable to use a more efficient type
+ (define LWIP_ERR_T in cc.h)
+
+ 2008-06-17 Simon Goldschmidt
+ * slipif.c: patch #6480: Added a configuration option for slipif for symmetry
+ to loopif
+
+ 2008-06-17 Simon Goldschmidt (patch by Luca Ceresoli)
+ * netif.c, loopif.c, ip.c, netif.h, loopif.h, opt.h: Checked in slightly
+ modified version of patch # 6370: Moved loopif code to netif.c so that
+ loopback traffic is supported on all netifs (all local IPs).
+ Added option to limit loopback packets for each netifs.
+
+
+ ++ Bugfixes:
+ 2009-08-12 Kieran Mansley
+ * tcp_in.c, tcp.c: Fix bug #27209: handle trimming of segments when
+ out of window or out of order properly
+
+ 2009-08-12 Kieran Mansley
+ * tcp_in.c: Fix bug #27199: use snd_wl2 instead of snd_wl1
+
+ 2009-07-28 Simon Goldschmidt
+ * mem.h: Fixed bug #27105: "realloc() cannot replace mem_realloc()"s
+
+ 2009-07-27 Kieran Mansley
+ * api.h api_msg.h netdb.h sockets.h: add missing #include directives
+
+ 2009-07-09 Kieran Mansley
+ * api_msg.c, sockets.c, api.h: BUG23240 use signed counters for
+ recv_avail and don't increment counters until message successfully
+ sent to mbox
+
+ 2009-06-25 Kieran Mansley
+ * api_msg.c api.h: BUG26722: initialise netconn write variables
+ in netconn_alloc
+
+ 2009-06-25 Kieran Mansley
+ * tcp.h: BUG26879: set ret value in TCP_EVENT macros when function is not set
+
+ 2009-06-25 Kieran Mansley
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h: BUG26301 and BUG26267: correct
+ simultaneous close behaviour, and make snd_nxt have the same meaning
+ as in the RFCs.
+
+ 2009-05-12 Simon Goldschmidt
+ * etharp.h, etharp.c, netif.c: fixed bug #26507: "Gratuitous ARP depends on
+ arp_table / uses etharp_query" by adding etharp_gratuitous()
+
+ 2009-05-12 Simon Goldschmidt
+ * ip.h, ip.c, igmp.c: bug #26487: Added ip_output_if_opt that can add IP options
+ to the IP header (used by igmp_ip_output_if)
+
+ 2009-05-06 Simon Goldschmidt
+ * inet_chksum.c: On little endian architectures, use LWIP_PLATFORM_HTONS (if
+ defined) for SWAP_BYTES_IN_WORD to speed up checksumming.
+
+ 2009-05-05 Simon Goldschmidt
+ * sockets.c: bug #26405: Prematurely released semaphore causes lwip_select()
+ to crash
+
+ 2009-05-04 Simon Goldschmidt
+ * init.c: snmp was not initialized in lwip_init()
+
+ 2009-05-04 Frédéric Bernon
+ * dhcp.c, netbios.c: Changes if IP_SOF_BROADCAST is enabled.
+
+ 2009-05-03 Simon Goldschmidt
+ * tcp.h: bug #26349: Nagle algorithm doesn't send although segment is full
+ (and unsent->next == NULL)
+
+ 2009-05-02 Simon Goldschmidt
+ * tcpip.h, tcpip.c: fixed tcpip_untimeout (does not need the time, broken after
+ 1.3.0 in CVS only) - fixes compilation of ppp_oe.c
+
+ 2009-05-02 Simon Goldschmidt
+ * msg_in.c: fixed bug #25636: SNMPSET value is ignored for integer fields
+
+ 2009-05-01 Simon Goldschmidt
+ * pap.c: bug #21680: PPP upap_rauthnak() drops legal NAK packets
+
+ 2009-05-01 Simon Goldschmidt
+ * ppp.c: bug #24228: Memory corruption with PPP and DHCP
+
+ 2009-04-29 Frédéric Bernon
+ * raw.c, udp.c, init.c, opt.h, ip.h, sockets.h: bug #26309: Implement the
+ SO(F)_BROADCAST filter for all API layers. Avoid the unindented reception
+ of broadcast packets even when this option wasn't set. Port maintainers
+ which want to enable this filter have to set IP_SOF_BROADCAST=1 in opt.h.
+ If you want this option also filter broadcast on recv operations, you also
+ have to set IP_SOF_BROADCAST_RECV=1 in opt.h.
+
+ 2009-04-28 Simon Goldschmidt, Jakob Stoklund Olesen
+ * dhcp.c: patch #6721, bugs #25575, #25576: Some small fixes to DHCP and
+ DHCP/AUTOIP cooperation
+
+ 2009-04-25 Simon Goldschmidt, Oleg Tyshev
+ * tcp_out.c: bug #24212: Deadlocked tcp_retransmit due to exceeded pcb->cwnd
+ Fixed by sorting the unsent and unacked queues (segments are inserted at the
+ right place in tcp_output and tcp_rexmit).
+
+ 2009-04-25 Simon Goldschmidt
+ * memp.c, mem.c, memp.h, mem_std.h: bug #26213 "Problem with memory allocation
+ when debugging": memp_sizes contained the wrong sizes (including sanity
+ regions); memp pools for MEM_USE_POOLS were too small
+
+ 2009-04-24 Simon Goldschmidt, Frédéric Bernon
+ * inet.c: patch #6765: Fix a small problem with the last changes (incorrect
+ behavior, with with ip address string not ended by a '\0', a space or a
+ end of line)
+
+ 2009-04-19 Simon Goldschmidt
+ * rawapi.txt: Fixed bug #26069: Corrected documentation: if tcp_connect fails,
+ pcb->err is called, not pcb->connected (with an error code).
+
+ 2009-04-19 Simon Goldschmidt
+ * tcp_out.c: Fixed bug #26236: "TCP options (timestamp) don't work with
+ no-copy-tcpwrite": deallocate option data, only concat segments with same flags
+
+ 2009-04-19 Simon Goldschmidt
+ * tcp_out.c: Fixed bug #25094: "Zero-length pbuf" (options are now allocated
+ in the header pbuf, not the data pbuf)
+
+ 2009-04-18 Simon Goldschmidt
+ * api_msg.c: fixed bug #25695: Segmentation fault in do_writemore()
+
+ 2009-04-15 Simon Goldschmidt
+ * sockets.c: tried to fix bug #23559: lwip_recvfrom problem with tcp
+
+ 2009-04-15 Simon Goldschmidt
+ * dhcp.c: task #9192: mem_free of dhcp->options_in and dhcp->msg_in
+
+ 2009-04-15 Simon Goldschmidt
+ * ip.c, ip6.c, tcp_out.c, ip.h: patch #6808: Add a utility function
+ ip_hinted_output() (for smaller code mainly)
+
+ 2009-04-15 Simon Goldschmidt
+ * inet.c: patch #6765: Supporting new line characters in inet_aton()
+
+ 2009-04-15 Simon Goldschmidt
+ * dhcp.c: patch #6764: DHCP rebind and renew did not send hostnam option;
+ Converted constant OPTION_MAX_MSG_SIZE to netif->mtu, check if netif->mtu
+ is big enough in dhcp_start
+
+ 2009-04-15 Simon Goldschmidt
+ * netbuf.c: bug #26027: netbuf_chain resulted in pbuf memory leak
+
+ 2009-04-15 Simon Goldschmidt
+ * sockets.c, ppp.c: bug #25763: corrected 4 occurrences of SMEMCPY to MEMCPY
+
+ 2009-04-15 Simon Goldschmidt
+ * sockets.c: bug #26121: set_errno can be overridden
+
+ 2009-04-09 Kieran Mansley (patch from Luca Ceresoli <lucaceresoli>)
+ * init.c, opt.h: Patch#6774 TCP_QUEUE_OOSEQ breaks compilation when
+ LWIP_TCP==0
+
+ 2009-04-09 Kieran Mansley (patch from Roy Lee <roylee17>)
+ * tcp.h: Patch#6802 Add do-while-clauses to those function like
+ macros in tcp.h
+
+ 2009-03-31 Kieran Mansley
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h, opt.h: Rework the way window
+ updates are calculated and sent (BUG20515)
+
+ * tcp_in.c: cope with SYN packets received during established states,
+ and retransmission of initial SYN.
+
+ * tcp_out.c: set push bit correctly when tcp segments are merged
+
+ 2009-03-27 Kieran Mansley
+ * tcp_out.c set window correctly on probes (correcting change made
+ yesterday)
+
+ 2009-03-26 Kieran Mansley
+ * tcp.c, tcp_in.c, tcp.h: add tcp_abandon() to cope with dropping
+ connections where no reset required (bug #25622)
+
+ * tcp_out.c: set TCP_ACK flag on keepalive and zero window probes
+ (bug #20779)
+
+ 2009-02-18 Simon Goldschmidt (Jonathan Larmour and Bill Auerbach)
+ * ip_frag.c: patch #6528: the buffer used for IP_FRAG_USES_STATIC_BUF could be
+ too small depending on MEM_ALIGNMENT
+
+ 2009-02-16 Simon Goldschmidt
+ * sockets.h/.c, api_*.h/.c: fixed arguments of socket functions to match the standard;
+ converted size argument of netconn_write to 'size_t'
+
+ 2009-02-16 Simon Goldschmidt
+ * tcp.h, tcp.c: fixed bug #24440: TCP connection close problem on 64-bit host
+ by moving accept callback function pointer to TCP_PCB_COMMON
+
+ 2009-02-12 Simon Goldschmidt
+ * dhcp.c: fixed bug #25345 (DHCPDECLINE is sent with "Maximum message size"
+ option)
+
+ 2009-02-11 Simon Goldschmidt
+ * dhcp.c: fixed bug #24480 (releasing old udp_pdb and pbuf in dhcp_start)
+
+ 2009-02-11 Simon Goldschmidt
+ * opt.h, api_msg.c: added configurable default valud for netconn->recv_bufsize:
+ RECV_BUFSIZE_DEFAULT (fixes bug #23726: pbuf pool exhaustion on slow recv())
+
+ 2009-02-10 Simon Goldschmidt
+ * tcp.c: fixed bug #25467: Listen backlog is not reset on timeout in SYN_RCVD:
+ Accepts_pending is decrease on a corresponding listen pcb when a connection
+ in state SYN_RCVD is close.
+
+ 2009-01-28 Jonathan Larmour
+ * pbuf.c: reclaim pbufs from TCP out-of-sequence segments if we run
+ out of pool pbufs.
+
+ 2008-12-19 Simon Goldschmidt
+ * many files: patch #6699: fixed some warnings on platform where sizeof(int) == 2
+
+ 2008-12-10 Tamas Somogyi, Frédéric Bernon
+ * sockets.c: fixed bug #25051: lwip_recvfrom problem with udp: fromaddr and
+ port uses deleted netbuf.
+
+ 2008-10-18 Simon Goldschmidt
+ * tcp_in.c: fixed bug ##24596: Vulnerability on faulty TCP options length
+ in tcp_parseopt
+
+ 2008-10-15 Simon Goldschmidt
+ * ip_frag.c: fixed bug #24517: IP reassembly crashes on unaligned IP headers
+ by packing the struct ip_reass_helper.
+
+ 2008-10-03 David Woodhouse, Jonathan Larmour
+ * etharp.c (etharp_arp_input): Fix type aliasing problem copying ip address.
+
+ 2008-10-02 Jonathan Larmour
+ * dns.c: Hard-code structure sizes, to avoid issues on some compilers where
+ padding is included.
+
+ 2008-09-30 Jonathan Larmour
+ * sockets.c (lwip_accept): check addr isn't NULL. If it's valid, do an
+ assertion check that addrlen isn't NULL.
+
+ 2008-09-30 Jonathan Larmour
+ * tcp.c: Fix bug #24227, wrong error message in tcp_bind.
+
+ 2008-08-26 Simon Goldschmidt
+ * inet.h, ip_addr.h: fixed bug #24132: Cross-dependency between ip_addr.h and
+ inet.h -> moved declaration of struct in_addr from ip_addr.h to inet.h
+
+ 2008-08-14 Simon Goldschmidt
+ * api_msg.c: fixed bug #23847: do_close_internal references freed memory (when
+ tcp_close returns != ERR_OK)
+
+ 2008-07-08 Frédéric Bernon
+ * stats.h: Fix some build bugs introduced with patch #6483 (missing some parameters
+ in macros, mainly if MEM_STATS=0 and MEMP_STATS=0).
+
+ 2008-06-24 Jonathan Larmour
+ * tcp_in.c: Fix for bug #23693 as suggested by Art R. Ensure cseg is unused
+ if tcp_seg_copy fails.
+
+ 2008-06-17 Simon Goldschmidt
+ * inet_chksum.c: Checked in some ideas of patch #6460 (loop optimizations)
+ and created defines for swapping bytes and folding u32 to u16.
+
+ 2008-05-30 Kieran Mansley
+ * tcp_in.c Remove redundant "if" statement, and use real rcv_wnd
+ rather than rcv_ann_wnd when deciding if packets are in-window.
+ Contributed by <arasmussen@consultant.datasys.swri.edu>
+
+ 2008-05-30 Kieran Mansley
+ * mem.h: Fix BUG#23254. Change macro definition of mem_* to allow
+ passing as function pointers when MEM_LIBC_MALLOC is defined.
+
+ 2008-05-09 Jonathan Larmour
+ * err.h, err.c, sockets.c: Fix bug #23119: Reorder timeout error code to
+ stop it being treated as a fatal error.
+
+ 2008-04-15 Simon Goldschmidt
+ * dhcp.c: fixed bug #22804: dhcp_stop doesn't clear NETIF_FLAG_DHCP
+ (flag now cleared)
+
+ 2008-03-27 Simon Goldschmidt
+ * mem.c, tcpip.c, tcpip.h, opt.h: fixed bug #21433 (Calling mem_free/pbuf_free
+ from interrupt context isn't safe): set LWIP_USE_HEAP_FROM_INTERRUPT to 1
+ in lwipopts.h or use pbuf_free_callback(p)/mem_free_callback(m) to free pbufs
+ or heap memory from interrupt context
+
+ 2008-03-26 Simon Goldschmidt
+ * tcp_in.c, tcp.c: fixed bug #22249: division by zero could occur if a remote
+ host sent a zero mss as TCP option.
+
+
+(STABLE-1.3.0)
+
+ ++ New features:
+
+ 2008-03-10 Jonathan Larmour
+ * inet_chksum.c: Allow choice of one of the sample algorithms to be
+ made from lwipopts.h. Fix comment on how to override LWIP_CHKSUM.
+
+ 2008-01-22 Frédéric Bernon
+ * tcp.c, tcp_in.c, tcp.h, opt.h: Rename LWIP_CALCULATE_EFF_SEND_MSS in
+ TCP_CALCULATE_EFF_SEND_MSS to have coherent TCP options names.
+
+ 2008-01-14 Frédéric Bernon
+ * rawapi.txt, api_msg.c, tcp.c, tcp_in.c, tcp.h: changes for task #7675 "Enable
+ to refuse data on a TCP_EVENT_RECV call". Important, behavior changes for the
+ tcp_recv callback (see rawapi.txt).
+
+ 2008-01-14 Frédéric Bernon, Marc Chaland
+ * ip.c: Integrate patch #6369" ip_input : checking before realloc".
+
+ 2008-01-12 Frédéric Bernon
+ * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field
+ netconn::sem per netconn::op_completed like suggested for the task #7490
+ "Add return value to sys_mbox_post".
+
+ 2008-01-12 Frédéric Bernon
+ * api_msg.c, opt.h: replace DEFAULT_RECVMBOX_SIZE per DEFAULT_TCP_RECVMBOX_SIZE,
+ DEFAULT_UDP_RECVMBOX_SIZE and DEFAULT_RAW_RECVMBOX_SIZE (to optimize queues
+ sizes), like suggested for the task #7490 "Add return value to sys_mbox_post".
+
+ 2008-01-10 Frédéric Bernon
+ * tcpip.h, tcpip.c: add tcpip_callback_with_block function for the task #7490
+ "Add return value to sys_mbox_post". tcpip_callback is always defined as
+ "blocking" ("block" parameter = 1).
+
+ 2008-01-10 Frédéric Bernon
+ * tcpip.h, tcpip.c, api.h, api_lib.c, api_msg.c, sockets.c: replace the field
+ netconn::mbox (sys_mbox_t) per netconn::sem (sys_sem_t) for the task #7490
+ "Add return value to sys_mbox_post".
+
+ 2008-01-05 Frédéric Bernon
+ * sys_arch.txt, api.h, api_lib.c, api_msg.h, api_msg.c, tcpip.c, sys.h, opt.h:
+ Introduce changes for task #7490 "Add return value to sys_mbox_post" with some
+ modifications in the sys_mbox api: sys_mbox_new take a "size" parameters which
+ indicate the number of pointers query by the mailbox. There is three defines
+ in opt.h to indicate sizes for tcpip::mbox, netconn::recvmbox, and for the
+ netconn::acceptmbox. Port maintainers, you can decide to just add this new
+ parameter in your implementation, but to ignore it to keep the previous behavior.
+ The new sys_mbox_trypost function return a value to know if the mailbox is
+ full or if the message is posted. Take a look to sys_arch.txt for more details.
+ This new function is used in tcpip_input (so, can be called in an interrupt
+ context since the function is not blocking), and in recv_udp and recv_raw.
+
+ 2008-01-04 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour
+ * rawapi.txt, api.h, api_lib.c, api_msg.h, api_msg.c, sockets.c, tcp.h, tcp.c,
+ tcp_in.c, init.c, opt.h: rename backlog options with TCP_ prefix, limit the
+ "backlog" parameter in an u8_t, 0 is interpreted as "smallest queue", add
+ documentation in the rawapi.txt file.
+
+ 2007-12-31 Kieran Mansley (based on patch from Per-Henrik Lundbolm)
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Add TCP persist timer
+
+ 2007-12-31 Frédéric Bernon, Luca Ceresoli
+ * autoip.c, etharp.c: ip_addr.h: Integrate patch #6348: "Broadcast ARP packets
+ in autoip". The change in etharp_raw could be removed, since all calls to
+ etharp_raw use ethbroadcast for the "ethdst_addr" parameter. But it could be
+ wrong in the future.
+
+ 2007-12-30 Frédéric Bernon, Tom Evans
+ * ip.c: Fix bug #21846 "LwIP doesn't appear to perform any IP Source Address
+ Filtering" reported by Tom Evans.
+
+ 2007-12-21 Frédéric Bernon, Simon Goldschmidt, Jonathan Larmour
+ * tcp.h, opt.h, api.h, api_msg.h, tcp.c, tcp_in.c, api_lib.c, api_msg.c,
+ sockets.c, init.c: task #7252: Implement TCP listen backlog: Warning: raw API
+ applications have to call 'tcp_accepted(pcb)' in their accept callback to
+ keep accepting new connections.
+
+ 2007-12-13 Frédéric Bernon
+ * api_msg.c, err.h, err.c, sockets.c, dns.c, dns.h: replace "enum dns_result"
+ by err_t type. Add a new err_t code "ERR_INPROGRESS".
+
+ 2007-12-12 Frédéric Bernon
+ * dns.h, dns.c, opt.h: move DNS options to the "right" place. Most visibles
+ are the one which have ram usage.
+
+ 2007-12-05 Frédéric Bernon
+ * netdb.c: add a LWIP_DNS_API_HOSTENT_STORAGE option to decide to use a static
+ set of variables (=0) or a local one (=1). In this last case, your port should
+ provide a function "struct hostent* sys_thread_hostent( struct hostent* h)"
+ which have to do a copy of "h" and return a pointer ont the "per-thread" copy.
+
+ 2007-12-03 Simon Goldschmidt
+ * ip.c: ip_input: check if a packet is for inp first before checking all other
+ netifs on netif_list (speeds up packet receiving in most cases)
+
+ 2007-11-30 Simon Goldschmidt
+ * udp.c, raw.c: task #7497: Sort lists (pcb, netif, ...) for faster access
+ UDP: move a (connected) pcb selected for input to the front of the list of
+ pcbs so that it is found faster next time. Same for RAW pcbs that have eaten
+ a packet.
+
+ 2007-11-28 Simon Goldschmidt
+ * etharp.c, stats.c, stats.h, opt.h: Introduced ETHARP_STATS
+
+ 2007-11-25 Simon Goldschmidt
+ * dhcp.c: dhcp_unfold_reply() uses pbuf_copy_partial instead of its own copy
+ algorithm.
+
+ 2007-11-24 Simon Goldschmidt
+ * netdb.h, netdb.c, sockets.h/.c: Moved lwip_gethostbyname from sockets.c
+ to the new file netdb.c; included lwip_getaddrinfo.
+
+ 2007-11-21 Simon Goldschmidt
+ * tcp.h, opt.h, tcp.c, tcp_in.c: implemented calculating the effective send-mss
+ based on the MTU of the netif used to send. Enabled by default. Disable by
+ setting LWIP_CALCULATE_EFF_SEND_MSS to 0. This fixes bug #21492.
+
+ 2007-11-19 Frédéric Bernon
+ * api_msg.c, dns.h, dns.c: Implement DNS_DOES_NAME_CHECK option (check if name
+ received match the name query), implement DNS_USES_STATIC_BUF (the place where
+ copy dns payload to parse the response), return an error if there is no place
+ for a new query, and fix some minor problems.
+
+ 2007-11-16 Simon Goldschmidt
+ * new files: ipv4/inet.c, ipv4/inet_chksum.c, ipv6/inet6.c
+ removed files: core/inet.c, core/inet6.c
+ Moved inet files into ipv4/ipv6 directory; splitted inet.c/inet.h into
+ inet and chksum part; changed includes in all lwIP files as appropriate
+
+ 2007-11-16 Simon Goldschmidt
+ * api.h, api_msg.h, api_lib.c, api_msg.c, socket.h, socket.c: Added sequential
+ dns resolver function for netconn api (netconn_gethostbyname) and socket api
+ (gethostbyname/gethostbyname_r).
+
+ 2007-11-15 Jim Pettinato, Frédéric Bernon
+ * opt.h, init.c, tcpip.c, dhcp.c, dns.h, dns.c: add DNS client for simple name
+ requests with RAW api interface. Initialization is done in lwip_init() with
+ build time options. DNS timer is added in tcpip_thread context. DHCP can set
+ DNS server ip addresses when options are received. You need to set LWIP_DNS=1
+ in your lwipopts.h file (LWIP_DNS=0 in opt.h). DNS_DEBUG can be set to get
+ some traces with LWIP_DEBUGF. Sanity check have been added. There is a "todo"
+ list with points to improve.
+
+ 2007-11-06 Simon Goldschmidt
+ * opt.h, mib2.c: Patch #6215: added ifAdminStatus write support (if explicitly
+ enabled by defining SNMP_SAFE_REQUESTS to 0); added code to check link status
+ for ifOperStatus if LWIP_NETIF_LINK_CALLBACK is defined.
+
+ 2007-11-06 Simon Goldschmidt
+ * api.h, api_msg.h and dependent files: Task #7410: Removed the need to include
+ core header files in api.h (ip/tcp/udp/raw.h) to hide the internal
+ implementation from netconn api applications.
+
+ 2007-11-03 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.c, sockets.c, opt.h: add SO_RCVBUF option for UDP &
+ RAW netconn. You need to set LWIP_SO_RCVBUF=1 in your lwipopts.h (it's disabled
+ by default). Netconn API users can use the netconn_recv_bufsize macro to access
+ it. This is a first release which have to be improve for TCP. Note it used the
+ netconn::recv_avail which need to be more "thread-safe" (note there is already
+ the problem for FIONREAD with lwip_ioctl/ioctlsocket).
+
+ 2007-11-01 Frédéric Bernon, Marc Chaland
+ * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, tcp.h, tcp_out.c:
+ Integrate "patch #6250 : MSG_MORE flag for send". MSG_MORE is used at socket api
+ layer, NETCONN_MORE at netconn api layer, and TCP_WRITE_FLAG_MORE at raw api
+ layer. This option enable to delayed TCP PUSH flag on multiple "write" calls.
+ Note that previous "copy" parameter for "write" APIs is now called "apiflags".
+
+ 2007-10-24 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.c: Add macro API_EVENT in the same spirit than
+ TCP_EVENT_xxx macros to get a code more readable. It could also help to remove
+ some code (like we have talk in "patch #5919 : Create compile switch to remove
+ select code"), but it could be done later.
+
+ 2007-10-08 Simon Goldschmidt
+ * many files: Changed initialization: many init functions are not needed any
+ more since we now rely on the compiler initializing global and static
+ variables to zero!
+
+ 2007-10-06 Simon Goldschmidt
+ * ip_frag.c, memp.c, mib2.c, ip_frag.h, memp_std.h, opt.h: Changed IP_REASSEMBLY
+ to enqueue the received pbufs so that multiple packets can be reassembled
+ simultaneously and no static reassembly buffer is needed.
+
+ 2007-10-05 Simon Goldschmidt
+ * tcpip.c, etharp.h, etharp.c: moved ethernet_input from tcpip.c to etharp.c so
+ all netifs (or ports) can use it.
+
+ 2007-10-05 Frédéric Bernon
+ * netifapi.h, netifapi.c: add function netifapi_netif_set_default. Change the
+ common function to reduce a little bit the footprint (for all functions using
+ only the "netif" parameter).
+
+ 2007-10-03 Frédéric Bernon
+ * netifapi.h, netifapi.c: add functions netifapi_netif_set_up, netifapi_netif_set_down,
+ netifapi_autoip_start and netifapi_autoip_stop. Use a common function to reduce
+ a little bit the footprint (for all functions using only the "netif" parameter).
+
+ 2007-09-15 Frédéric Bernon
+ * udp.h, udp.c, sockets.c: Changes for "#20503 IGMP Improvement". Add IP_MULTICAST_IF
+ option in socket API, and a new field "multicast_ip" in "struct udp_pcb" (for
+ netconn and raw API users), only if LWIP_IGMP=1. Add getsockopt processing for
+ IP_MULTICAST_TTL and IP_MULTICAST_IF.
+
+ 2007-09-10 Frédéric Bernon
+ * snmp.h, mib2.c: enable to remove SNMP timer (which consumne several cycles
+ even when it's not necessary). snmp_agent.txt tell to call snmp_inc_sysuptime()
+ each 10ms (but, it's intrusive if you use sys_timeout feature). Now, you can
+ decide to call snmp_add_sysuptime(100) each 1000ms (which is bigger "step", but
+ call to a lower frequency). Or, you can decide to not call snmp_inc_sysuptime()
+ or snmp_add_sysuptime(), and to define the SNMP_GET_SYSUPTIME(sysuptime) macro.
+ This one is undefined by default in mib2.c. SNMP_GET_SYSUPTIME is called inside
+ snmp_get_sysuptime(u32_t *value), and enable to change "sysuptime" value only
+ when it's queried (any direct call to "sysuptime" is changed by a call to
+ snmp_get_sysuptime).
+
+ 2007-09-09 Frédéric Bernon, Bill Florac
+ * igmp.h, igmp.c, netif.h, netif.c, ip.c: To enable to have interfaces with IGMP,
+ and others without it, there is a new NETIF_FLAG_IGMP flag to set in netif->flags
+ if you want IGMP on an interface. igmp_stop() is now called inside netif_remove().
+ igmp_report_groups() is now called inside netif_set_link_up() (need to have
+ LWIP_NETIF_LINK_CALLBACK=1) to resend reports once the link is up (avoid to wait
+ the next query message to receive the matching multicast streams).
+
+ 2007-09-08 Frédéric Bernon
+ * sockets.c, ip.h, api.h, tcp.h: declare a "struct ip_pcb" which only contains
+ IP_PCB. Add in the netconn's "pcb" union a "struct ip_pcb *ip;" (no size change).
+ Use this new field to access to common pcb fields (ttl, tos, so_options, etc...).
+ Enable to access to these fields with LWIP_TCP=0.
+
+ 2007-09-05 Frédéric Bernon
+ * udp.c, ipv4/icmp.c, ipv4/ip.c, ipv6/icmp.c, ipv6/ip6.c, ipv4/icmp.h,
+ ipv6/icmp.h, opt.h: Integrate "task #7272 : LWIP_ICMP option". The new option
+ LWIP_ICMP enable/disable ICMP module inside the IP stack (enable per default).
+ Be careful, disabling ICMP make your product non-compliant to RFC1122, but
+ help to reduce footprint, and to reduce "visibility" on the Internet.
+
+ 2007-09-05 Frédéric Bernon, Bill Florac
+ * opt.h, sys.h, tcpip.c, slipif.c, ppp.c, sys_arch.txt: Change parameters list
+ for sys_thread_new (see "task #7252 : Create sys_thread_new_ex()"). Two new
+ parameters have to be provided: a task name, and a task stack size. For this
+ one, since it's platform dependant, you could define the best one for you in
+ your lwipopts.h. For port maintainers, you can just add these new parameters
+ in your sys_arch.c file, and but it's not mandatory, use them in your OS
+ specific functions.
+
+ 2007-09-05 Frédéric Bernon
+ * inet.c, autoip.c, msg_in.c, msg_out.c, init.c: Move some build time checkings
+ inside init.c for task #7142 "Sanity check user-configurable values".
+
+ 2007-09-04 Frédéric Bernon, Bill Florac
+ * igmp.h, igmp.c, memp_std.h, memp.c, init.c, opt.h: Replace mem_malloc call by
+ memp_malloc, and use a new MEMP_NUM_IGMP_GROUP option (see opt.h to define the
+ value). It will avoid potential fragmentation problems, use a counter to know
+ how many times a group is used on an netif, and free it when all applications
+ leave it. MEMP_NUM_IGMP_GROUP got 8 as default value (and init.c got a sanity
+ check if LWIP_IGMP!=0).
+
+ 2007-09-03 Frédéric Bernon
+ * igmp.h, igmp.c, sockets.c, api_msg.c: Changes for "#20503 IGMP Improvement".
+ Initialize igmp_mac_filter to NULL in netif_add (this field should be set in
+ the netif's "init" function). Use the "imr_interface" field (for socket layer)
+ and/or the "interface" field (for netconn layer), for join/leave operations.
+ The igmp_join/leavegroup first parameter change from a netif to an ipaddr.
+ This field could be a netif's ipaddr, or "any" (same meaning than ip_addr_isany).
+
+ 2007-08-30 Frédéric Bernon
+ * Add netbuf.h, netbuf.c, Change api.h, api_lib.c: #7249 "Split netbuf functions
+ from api/api_lib". Now netbuf API is independant of netconn, and can be used
+ with other API (application based on raw API, or future "socket2" API). Ports
+ maintainers just have to add src/api/netbuf.c in their makefile/projects.
+
+ 2007-08-30 Frédéric Bernon, Jonathan Larmour
+ * init.c: Add first version of lwip_sanity_check for task #7142 "Sanity check
+ user-configurable values".
+
+ 2007-08-29 Frédéric Bernon
+ * igmp.h, igmp.c, tcpip.c, init.c, netif.c: change igmp_init and add igmp_start.
+ igmp_start is call inside netif_add. Now, igmp initialization is in the same
+ spirit than the others modules. Modify some IGMP debug traces.
+
+ 2007-08-29 Frédéric Bernon
+ * Add init.h, init.c, Change opt.h, tcpip.c: Task #7213 "Add a lwip_init function"
+ Add lwip_init function to regroup all modules initializations, and to provide
+ a place to add code for task #7142 "Sanity check user-configurable values".
+ Ports maintainers should remove direct initializations calls from their code,
+ and add init.c in their makefiles. Note that lwip_init() function is called
+ inside tcpip_init, but can also be used by raw api users since all calls are
+ disabled when matching options are disabled. Also note that their is new options
+ in opt.h, you should configure in your lwipopts.h (they are enabled per default).
+
+ 2007-08-26 Marc Boucher
+ * api_msg.c: do_close_internal(): Reset the callbacks and arg (conn) to NULL
+ since they can under certain circumstances be called with an invalid conn
+ pointer after the connection has been closed (and conn has been freed).
+
+ 2007-08-25 Frédéric Bernon (Artem Migaev's Patch)
+ * netif.h, netif.c: Integrate "patch #6163 : Function to check if link layer is up".
+ Add a netif_is_link_up() function if LWIP_NETIF_LINK_CALLBACK option is set.
+
+ 2007-08-22 Frédéric Bernon
+ * netif.h, netif.c, opt.h: Rename LWIP_NETIF_CALLBACK in LWIP_NETIF_STATUS_CALLBACK
+ to be coherent with new LWIP_NETIF_LINK_CALLBACK option before next release.
+
+ 2007-08-22 Frédéric Bernon
+ * tcpip.h, tcpip.c, ethernetif.c, opt.h: remove options ETHARP_TCPIP_INPUT &
+ ETHARP_TCPIP_ETHINPUT, now, only "ethinput" code is supported, even if the
+ name is tcpip_input (we keep the name of 1.2.0 function).
+
+ 2007-08-17 Jared Grubb
+ * memp_std.h, memp.h, memp.c, mem.c, stats.c: (Task #7136) Centralize mempool
+ settings into new memp_std.h and optional user file lwippools.h. This adds
+ more dynamic mempools, and allows the user to create an arbitrary number of
+ mempools for mem_malloc.
+
+ 2007-08-16 Marc Boucher
+ * api_msg.c: Initialize newconn->state to NETCONN_NONE in accept_function;
+ otherwise it was left to NETCONN_CLOSE and sent_tcp() could prematurely
+ close the connection.
+
+ 2007-08-16 Marc Boucher
+ * sockets.c: lwip_accept(): check netconn_peer() error return.
+
+ 2007-08-16 Marc Boucher
+ * mem.c, mem.h: Added mem_calloc().
+
+ 2007-08-16 Marc Boucher
+ * tcpip.c, tcpip.h memp.c, memp.h: Added distinct memp (MEMP_TCPIP_MSG_INPKT)
+ for input packets to prevent floods from consuming all of MEMP_TCPIP_MSG
+ and starving other message types.
+ Renamed MEMP_TCPIP_MSG to MEMP_TCPIP_MSG_API
+
+ 2007-08-16 Marc Boucher
+ * pbuf.c, pbuf.h, etharp.c, tcp_in.c, sockets.c: Split pbuf flags in pbuf
+ type and flgs (later renamed to flags).
+ Use enum pbuf_flag as pbuf_type. Renumber PBUF_FLAG_*.
+ Improved lwip_recvfrom(). TCP push now propagated.
+
+ 2007-08-16 Marc Boucher
+ * ethernetif.c, contrib/ports/various: ethbroadcast now a shared global
+ provided by etharp.
+
+ 2007-08-16 Marc Boucher
+ * ppp_oe.c ppp_oe.h, auth.c chap.c fsm.c lcp.c ppp.c ppp.h,
+ etharp.c ethernetif.c, etharp.h, opt.h tcpip.h, tcpip.c:
+ Added PPPoE support and various PPP improvements.
+
+ 2007-07-25 Simon Goldschmidt
+ * api_lib.c, ip_frag.c, pbuf.c, api.h, pbuf.h: Introduced pbuf_copy_partial,
+ making netbuf_copy_partial use this function.
+
+ 2007-07-25 Simon Goldschmidt
+ * tcp_in.c: Fix bug #20506: Slow start / initial congestion window starts with
+ 2 * mss (instead of 1 * mss previously) to comply with some newer RFCs and
+ other stacks.
+
+ 2007-07-13 Jared Grubb (integrated by Frédéric Bernon)
+ * opt.h, netif.h, netif.c, ethernetif.c: Add new configuration option to add
+ a link callback in the netif struct, and functions to handle it. Be carefull
+ for port maintainers to add the NETIF_FLAG_LINK_UP flag (like in ethernetif.c)
+ if you want to be sure to be compatible with future changes...
+
+ 2007-06-30 Frédéric Bernon
+ * sockets.h, sockets.c: Implement MSG_PEEK flag for recv/recvfrom functions.
+
+ 2007-06-21 Simon Goldschmidt
+ * etharp.h, etharp.c: Combined etharp_request with etharp_raw for both
+ LWIP_AUTOIP =0 and =1 to remove redundant code.
+
+ 2007-06-21 Simon Goldschmidt
+ * mem.c, memp.c, mem.h, memp.h, opt.h: task #6863: Introduced the option
+ MEM_USE_POOLS to use 4 pools with different sized elements instead of a
+ heap. This both prevents memory fragmentation and gives a higher speed
+ at the cost of more memory consumption. Turned off by default.
+
+ 2007-06-21 Simon Goldschmidt
+ * api_lib.c, api_msg.c, api.h, api_msg.h: Converted the length argument of
+ netconn_write (and therefore also api_msg_msg.msg.w.len) from u16_t into
+ int to be able to send a bigger buffer than 64K with one time (mainly
+ used from lwip_send).
+
+ 2007-06-21 Simon Goldschmidt
+ * tcp.h, api_msg.c: Moved the nagle algorithm from netconn_write/do_write
+ into a define (tcp_output_nagle) in tcp.h to provide it to raw api users, too.
+
+ 2007-06-21 Simon Goldschmidt
+ * api.h, api_lib.c, api_msg.c: Fixed bug #20021: Moved sendbuf-processing in
+ netconn_write from api_lib.c to api_msg.c to also prevent multiple context-
+ changes on low memory or empty send-buffer.
+
+ 2007-06-18 Simon Goldschmidt
+ * etharp.c, etharp.h: Changed etharp to use a defined hardware address length
+ of 6 to avoid loading netif->hwaddr_len every time (since this file is only
+ used for ethernet and struct eth_addr already had a defined length of 6).
+
+ 2007-06-17 Simon Goldschmidt
+ * sockets.c, sockets.h: Implemented socket options SO_NO_CHECK for UDP sockets
+ to disable UDP checksum generation on transmit.
+
+ 2007-06-13 Frédéric Bernon, Simon Goldschmidt
+ * debug.h, api_msg.c: change LWIP_ERROR to use it to check errors like invalid
+ pointers or parameters, and let the possibility to redefined it in cc.h. Use
+ this macro to check "conn" parameter in api_msg.c functions.
+
+ 2007-06-11 Simon Goldschmidt
+ * sockets.c, sockets.h: Added UDP lite support for sockets
+
+ 2007-06-10 Simon Goldschmidt
+ * udp.h, opt.h, api_msg.c, ip.c, udp.c: Included switch LWIP_UDPLITE (enabled
+ by default) to switch off UDP-Lite support if not needed (reduces udp.c code
+ size)
+
+ 2007-06-09 Dominik Spies (integrated by Frédéric Bernon)
+ * autoip.h, autoip.c, dhcp.h, dhcp.c, netif.h, netif.c, etharp.h, etharp.c, opt.h:
+ AutoIP implementation available for IPv4, with new options LWIP_AUTOIP and
+ LWIP_DHCP_AUTOIP_COOP if you want to cooperate with DHCP. Some tips to adapt
+ (see TODO mark in the source code).
+
+ 2007-06-09 Simon Goldschmidt
+ * etharp.h, etharp.c, ethernetif.c: Modified order of parameters for
+ etharp_output() to match netif->output so etharp_output() can be used
+ directly as netif->output to save one function call.
+
+ 2007-06-08 Simon Goldschmidt
+ * netif.h, ethernetif.c, slipif.c, loopif.c: Added define
+ NETIF_INIT_SNMP(netif, type, speed) to initialize per-netif snmp variables,
+ added initialization of those to ethernetif, slipif and loopif.
+
+ 2007-05-18 Simon Goldschmidt
+ * opt.h, ip_frag.c, ip_frag.h, ip.c: Added option IP_FRAG_USES_STATIC_BUF
+ (defaulting to off for now) that can be set to 0 to send fragmented
+ packets by passing PBUF_REFs down the stack.
+
+ 2007-05-23 Frédéric Bernon
+ * api_lib.c: Implement SO_RCVTIMEO for accept and recv on TCP
+ connections, such present in patch #5959.
+
+ 2007-05-23 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.c, sockets.c: group the different NETCONN_UDPxxx
+ code in only one part...
+
+ 2007-05-18 Simon Goldschmidt
+ * opt.h, memp.h, memp.c: Added option MEMP_OVERFLOW_CHECK to check for memp
+ elements to overflow. This is achieved by adding some bytes before and after
+ each pool element (increasing their size, of course), filling them with a
+ prominent value and checking them on freeing the element.
+ Set it to 2 to also check every element in every pool each time memp_malloc()
+ or memp_free() is called (slower but more helpful).
+
+ 2007-05-10 Simon Goldschmidt
+ * opt.h, memp.h, memp.c, pbuf.c (see task #6831): use a new memp pool for
+ PBUF_POOL pbufs instead of the old pool implementation in pbuf.c to reduce
+ code size.
+
+ 2007-05-11 Frédéric Bernon
+ * sockets.c, api_lib.c, api_msg.h, api_msg.c, netifapi.h, netifapi.c, tcpip.c:
+ Include a function pointer instead of a table index in the message to reduce
+ footprint. Disable some part of lwip_send and lwip_sendto if some options are
+ not set (LWIP_TCP, LWIP_UDP, LWIP_RAW).
+
+ 2007-05-10 Simon Goldschmidt
+ * *.h (except netif/ppp/*.h): Included patch #5448: include '#ifdef __cplusplus
+ \ extern "C" {' in all header files. Now you can write your application using
+ the lwIP stack in C++ and simply #include the core files. Note I have left
+ out the netif/ppp/*h header files for now, since I don't know which files are
+ included by applications and which are for internal use only.
+
+ 2007-05-09 Simon Goldschmidt
+ * opt.h, *.c/*.h: Included patch #5920: Create define to override C-library
+ memcpy. 2 Defines are created: MEMCPY() for normal memcpy, SMEMCPY() for
+ situations where some compilers might inline the copy and save a function
+ call. Also replaced all calls to memcpy() with calls to (S)MEMCPY().
+
+ 2007-05-08 Simon Goldschmidt
+ * mem.h: If MEM_LIBC_MALLOC==1, allow the defines (e.g. mem_malloc() -> malloc())
+ to be overriden in case the C-library malloc implementation is not protected
+ against concurrent access.
+
+ 2007-05-04 Simon Goldschmidt (Atte Kojo)
+ * etharp.c: Introduced fast one-entry-cache to speed up ARP lookup when sending
+ multiple packets to the same host.
+
+ 2007-05-04 Frédéric Bernon, Jonathan Larmour
+ * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fix bug #19162 "lwip_sento: a possible
+ to corrupt remote addr/port connection state". Reduce problems "not enought memory" with
+ netbuf (if we receive lot of datagrams). Improve lwip_sendto (only one exchange between
+ sockets api and api_msg which run in tcpip_thread context). Add netconn_sento function.
+ Warning, if you directly access to "fromaddr" & "fromport" field from netbuf struct,
+ these fields are now renamed "addr" & "port".
+
+ 2007-04-11 Jonathan Larmour
+ * sys.h, api_lib.c: Provide new sys_mbox_tryfetch function. Require ports to provide new
+ sys_arch_mbox_tryfetch function to get a message if one is there, otherwise return
+ with SYS_MBOX_EMPTY. sys_arch_mbox_tryfetch can be implemented as a function-like macro
+ by the port in sys_arch.h if desired.
+
+ 2007-04-06 Frédéric Bernon, Simon Goldschmidt
+ * opt.h, tcpip.h, tcpip.c, netifapi.h, netifapi.c: New configuration option LWIP_NETIF_API
+ allow to use thread-safe functions to add/remove netif in list, and to start/stop dhcp
+ clients, using new functions from netifapi.h. Disable as default (no port change to do).
+
+ 2007-04-05 Frédéric Bernon
+ * sockets.c: remplace ENOBUFS errors on alloc_socket by ENFILE to be more BSD compliant.
+
+ 2007-04-04 Simon Goldschmidt
+ * arch.h, api_msg.c, dhcp.c, msg_in.c, sockets.c: Introduced #define LWIP_UNUSED_ARG(x)
+ use this for and architecture-independent form to tell the compiler you intentionally
+ are not using this variable. Can be overriden in cc.h.
+
+ 2007-03-28 Frédéric Bernon
+ * opt.h, netif.h, dhcp.h, dhcp.c: New configuration option LWIP_NETIF_HOSTNAME allow to
+ define a hostname in netif struct (this is just a pointer, so, you can use a hardcoded
+ string, point on one of your's ethernetif field, or alloc a string you will free yourself).
+ It will be used by DHCP to register a client hostname, but can also be use when you call
+ snmp_set_sysname.
+
+ 2007-03-28 Frédéric Bernon
+ * netif.h, netif.c: A new NETIF_FLAG_ETHARP flag is defined in netif.h, to allow to
+ initialize a network interface's flag with. It tell this interface is an ethernet
+ device, and we can use ARP with it to do a "gratuitous ARP" (RFC 3220 "IP Mobility
+ Support for IPv4" section 4.6) when interface is "up" with netif_set_up().
+
+ 2007-03-26 Frédéric Bernon, Jonathan Larmour
+ * opt.h, tcpip.c: New configuration option LWIP_ARP allow to disable ARP init at build
+ time if you only use PPP or SLIP. The default is enable. Note we don't have to call
+ etharp_init in your port's initilization sequence if you use tcpip.c, because this call
+ is done in tcpip_init function.
+
+ 2007-03-22 Frédéric Bernon
+ * stats.h, stats.c, msg_in.c: Stats counters can be change to u32_t if necessary with the
+ new option LWIP_STATS_LARGE. If you need this option, define LWIP_STATS_LARGE to 1 in
+ your lwipopts.h. More, unused counters are not defined in the stats structs, and not
+ display by stats_display(). Note that some options (SYS_STATS and RAW_STATS) are defined
+ but never used. Fix msg_in.c with the correct #if test for a stat display.
+
+ 2007-03-21 Kieran Mansley
+ * netif.c, netif.h: Apply patch#4197 with some changes (originator: rireland@hmgsl.com).
+ Provides callback on netif up/down state change.
+
+ 2007-03-11 Frédéric Bernon, Mace Gael, Steve Reynolds
+ * sockets.h, sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c, igmp.h, igmp.c,
+ ip.c, netif.h, tcpip.c, opt.h:
+ New configuration option LWIP_IGMP to enable IGMP processing. Based on only one
+ filter per all network interfaces. Declare a new function in netif to enable to
+ control the MAC filter (to reduce lwIP traffic processing).
+
+ 2007-03-11 Frédéric Bernon
+ * tcp.h, tcp.c, sockets.c, tcp_out.c, tcp_in.c, opt.h: Keepalive values can
+ be configured at run time with LWIP_TCP_KEEPALIVE, but don't change this
+ unless you know what you're doing (default are RFC1122 compliant). Note
+ that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set in seconds.
+
+ 2007-03-08 Frédéric Bernon
+ * tcp.h: Keepalive values can be configured at compile time, but don't change
+ this unless you know what you're doing (default are RFC1122 compliant).
+
+ 2007-03-08 Frédéric Bernon
+ * sockets.c, api.h, api_lib.c, tcpip.c, sys.h, sys.c, err.c, opt.h:
+ Implement LWIP_SO_RCVTIMEO configuration option to enable/disable SO_RCVTIMEO
+ on UDP sockets/netconn.
+
+ 2007-03-08 Simon Goldschmidt
+ * snmp_msg.h, msg_in.c: SNMP UDP ports can be configured at compile time.
+
+ 2007-03-06 Frédéric Bernon
+ * api.h, api_lib.c, sockets.h, sockets.c, tcpip.c, sys.h, sys.c, err.h:
+ Implement SO_RCVTIMEO on UDP sockets/netconn.
+
+ 2007-02-28 Kieran Mansley (based on patch from Simon Goldschmidt)
+ * api_lib.c, tcpip.c, memp.c, memp.h: make API msg structs allocated
+ on the stack and remove the API msg type from memp
+
+ 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt)
+ * sockets.h, sockets.c: Move socket initialization to new
+ lwip_socket_init() function.
+ NOTE: this changes the API with ports. Ports will have to be
+ updated to call lwip_socket_init() now.
+
+ 2007-02-26 Jonathan Larmour (based on patch from Simon Goldschmidt)
+ * api_lib.c: Use memcpy in netbuf_copy_partial.
+
+
+ ++ Bug fixes:
+
+ 2008-03-17 Frédéric Bernon, Ed Kerekes
+ * igmp.h, igmp.c: Fix bug #22613 "IGMP iphdr problem" (could have
+ some problems to fill the IP header on some targets, use now the
+ ip.h macros to do it).
+
+ 2008-03-13 Frédéric Bernon
+ * sockets.c: Fix bug #22435 "lwip_recvfrom with TCP break;". Using
+ (lwip_)recvfrom with valid "from" and "fromlen" parameters, on a
+ TCP connection caused a crash. Note that using (lwip_)recvfrom
+ like this is a bit slow and that using (lwip)getpeername is the
+ good lwip way to do it (so, using recv is faster on tcp sockets).
+
+ 2008-03-12 Frédéric Bernon, Jonathan Larmour
+ * api_msg.c, contrib/apps/ping.c: Fix bug #22530 "api_msg.c's
+ recv_raw() does not consume data", and the ping sample (with
+ LWIP_SOCKET=1, the code did the wrong supposition that lwip_recvfrom
+ returned the IP payload, without the IP header).
+
+ 2008-03-04 Jonathan Larmour
+ * mem.c, stats.c, mem.h: apply patch #6414 to avoid compiler errors
+ and/or warnings on some systems where mem_size_t and size_t differ.
+ * pbuf.c, ppp.c: Fix warnings on some systems with mem_malloc.
+
+ 2008-03-04 Kieran Mansley (contributions by others)
+ * Numerous small compiler error/warning fixes from contributions to
+ mailing list after 1.3.0 release candidate made.
+
+ 2008-01-25 Cui hengbin (integrated by Frédéric Bernon)
+ * dns.c: Fix bug #22108 "DNS problem" caused by unaligned structures.
+
+ 2008-01-15 Kieran Mansley
+ * tcp_out.c: BUG20511. Modify persist timer to start when we are
+ prevented from sending by a small send window, not just a zero
+ send window.
+
+ 2008-01-09 Jonathan Larmour
+ * opt.h, ip.c: Rename IP_OPTIONS define to IP_OPTIONS_ALLOWED to avoid
+ conflict with Linux system headers.
+
+ 2008-01-06 Jonathan Larmour
+ * dhcp.c: fix bug #19927: "DHCP NACK problem" by clearing any existing set IP
+ address entirely on receiving a DHCPNAK, and restarting discovery.
+
+ 2007-12-21 Simon Goldschmidt
+ * sys.h, api_lib.c, api_msg.c, sockets.c: fix bug #21698: "netconn->recv_avail
+ is not protected" by using new macros for interlocked access to modify/test
+ netconn->recv_avail.
+
+ 2007-12-20 Kieran Mansley (based on patch from Oleg Tyshev)
+ * tcp_in.c: fix bug# 21535 (nrtx not reset correctly in SYN_SENT state)
+
+ 2007-12-20 Kieran Mansley (based on patch from Per-Henrik Lundbolm)
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h: fix bug #20199 (better handling
+ of silly window avoidance and prevent lwIP from shrinking the window)
+
+ 2007-12-04 Simon Goldschmidt
+ * tcp.c, tcp_in.c: fix bug #21699 (segment leak in ooseq processing when last
+ data packet was lost): add assert that all segment lists are empty in
+ tcp_pcb_remove before setting pcb to CLOSED state; don't directly set CLOSED
+ state from LAST_ACK in tcp_process
+
+ 2007-12-02 Simon Goldschmidt
+ * sockets.h: fix bug #21654: exclude definition of struct timeval from #ifndef FD_SET
+ If including <sys/time.h> for system-struct timeval, LWIP_TIMEVAL_PRIVATE now
+ has to be set to 0 in lwipopts.h
+
+ 2007-12-02 Simon Goldschmidt
+ * api_msg.c, api_lib.c: fix bug #21656 (recvmbox problem in netconn API): always
+ allocate a recvmbox in netconn_new_with_proto_and_callback. For a tcp-listen
+ netconn, this recvmbox is later freed and a new mbox is allocated for acceptmbox.
+ This is a fix for thread-safety and allocates all items needed for a netconn
+ when the netconn is created.
+
+ 2007-11-30 Simon Goldschmidt
+ * udp.c: first attempt to fix bug #21655 (DHCP doesn't work reliably with multiple
+ netifs): if LWIP_DHCP is enabled, UDP packets to DHCP_CLIENT_PORT are passed
+ to netif->dhcp->pcb only (if that exists) and not to any other pcb for the same
+ port (only solution to let UDP pcbs 'bind' to a netif instead of an IP address)
+
+ 2007-11-27 Simon Goldschmidt
+ * ip.c: fixed bug #21643 (udp_send/raw_send don't fail if netif is down) by
+ letting ip_route only use netifs that are up.
+
+ 2007-11-27 Simon Goldschmidt
+ * err.h, api_lib.c, api_msg.c, sockets.c: Changed error handling: ERR_MEM, ERR_BUF
+ and ERR_RTE are seen as non-fatal, all other errors are fatal. netconns and
+ sockets block most operations once they have seen a fatal error.
+
+ 2007-11-27 Simon Goldschmidt
+ * udp.h, udp.c, dhcp.c: Implemented new function udp_sendto_if which takes the
+ netif to send as an argument (to be able to send on netifs that are down).
+
+ 2007-11-26 Simon Goldschmidt
+ * tcp_in.c: Fixed bug #21582: pcb->acked accounting can be wrong when ACKs
+ arrive out-of-order
+
+ 2007-11-21 Simon Goldschmidt
+ * tcp.h, tcp_out.c, api_msg.c: Fixed bug #20287: tcp_output_nagle sends too early
+ Fixed the nagle algorithm; nagle now also works for all raw API applications
+ and has to be explicitly disabled with 'tcp_pcb->flags |= TF_NODELAY'
+
+ 2007-11-12 Frédéric Bernon
+ * sockets.c, api.h, api_lib.c, api_msg.h, api_msg.c: Fixed bug #20900. Now, most
+ of the netconn_peer and netconn_addr processing is done inside tcpip_thread
+ context in do_getaddr.
+
+ 2007-11-10 Simon Goldschmidt
+ * etharp.c: Fixed bug: assert fired when MEMP_ARP_QUEUE was empty (which can
+ happen any time). Now the packet simply isn't enqueued when out of memory.
+
+ 2007-11-01 Simon Goldschmidt
+ * tcp.c, tcp_in.c: Fixed bug #21494: The send mss (pcb->mss) is set to 536 (or
+ TCP_MSS if that is smaller) as long as no MSS option is received from the
+ remote host.
+
+ 2007-11-01 Simon Goldschmidt
+ * tcp.h, tcp.c, tcp_in.c: Fixed bug #21491: The MSS option sent (with SYN)
+ is now based on TCP_MSS instead of pcb->mss (on passive open now effectively
+ sending our configured TCP_MSS instead of the one received).
+
+ 2007-11-01 Simon Goldschmidt
+ * tcp_in.c: Fixed bug #21181: On active open, the initial congestion window was
+ calculated based on the configured TCP_MSS, not on the MSS option received
+ with SYN+ACK.
+
+ 2007-10-09 Simon Goldschmidt
+ * udp.c, inet.c, inet.h: Fixed UDPLite: send: Checksum was always generated too
+ short and also was generated wrong if checksum coverage != tot_len;
+ receive: checksum was calculated wrong if checksum coverage != tot_len
+
+ 2007-10-08 Simon Goldschmidt
+ * mem.c: lfree was not updated in mem_realloc!
+
+ 2007-10-07 Frédéric Bernon
+ * sockets.c, api.h, api_lib.c: First step to fix "bug #20900 : Potential
+ crash error problem with netconn_peer & netconn_addr". VERY IMPORTANT:
+ this change cause an API breakage for netconn_addr, since a parameter
+ type change. Any compiler should cause an error without any changes in
+ yours netconn_peer calls (so, it can't be a "silent change"). It also
+ reduce a little bit the footprint for socket layer (lwip_getpeername &
+ lwip_getsockname use now a common lwip_getaddrname function since
+ netconn_peer & netconn_addr have the same parameters).
+
+ 2007-09-20 Simon Goldschmidt
+ * tcp.c: Fixed bug #21080 (tcp_bind without check pcbs in TIME_WAIT state)
+ by checking tcp_tw_pcbs also
+
+ 2007-09-19 Simon Goldschmidt
+ * icmp.c: Fixed bug #21107 (didn't reset IP TTL in ICMP echo replies)
+
+ 2007-09-15 Mike Kleshov
+ * mem.c: Fixed bug #21077 (inaccuracy in calculation of lwip_stat.mem.used)
+
+ 2007-09-06 Frédéric Bernon
+ * several-files: replace some #include "arch/cc.h" by "lwip/arch.h", or simply remove
+ it as long as "lwip/opt.h" is included before (this one include "lwip/debug.h" which
+ already include "lwip/arch.h"). Like that, default defines are provided by "lwip/arch.h"
+ if they are not defined in cc.h, in the same spirit than "lwip/opt.h" for lwipopts.h.
+
+ 2007-08-30 Frédéric Bernon
+ * igmp.h, igmp.c: Some changes to remove some redundant code, add some traces,
+ and fix some coding style.
+
+ 2007-08-28 Frédéric Bernon
+ * tcpip.c: Fix TCPIP_MSG_INPKT processing: now, tcpip_input can be used for any
+ kind of packets. These packets are considered like Ethernet packets (payload
+ pointing to ethhdr) if the netif got the NETIF_FLAG_ETHARP flag. Else, packets
+ are considered like IP packets (payload pointing to iphdr).
+
+ 2007-08-27 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.c: First fix for "bug #20900 : Potential crash error
+ problem with netconn_peer & netconn_addr". Introduce NETCONN_LISTEN netconn_state
+ and remove obsolete ones (NETCONN_RECV & NETCONN_ACCEPT).
+
+ 2007-08-24 Kieran Mansley
+ * inet.c Modify (acc >> 16) test to ((acc >> 16) != 0) to help buggy
+ compiler (Paradigm C++)
+
+ 2007-08-09 Frédéric Bernon, Bill Florac
+ * stats.h, stats.c, igmp.h, igmp.c, opt.h: Fix for bug #20503 : IGMP Improvement.
+ Introduce IGMP_STATS to centralize statistics management.
+
+ 2007-08-09 Frédéric Bernon, Bill Florac
+ * udp.c: Fix for bug #20503 : IGMP Improvement. Enable to receive a multicast
+ packet on a udp pcb binded on an netif's IP address, and not on "any".
+
+ 2007-08-09 Frédéric Bernon, Bill Florac
+ * igmp.h, igmp.c, ip.c: Fix minor changes from bug #20503 : IGMP Improvement.
+ This is mainly on using lookup/lookfor, and some coding styles...
+
+ 2007-07-26 Frédéric Bernon (and "thedoctor")
+ * igmp.c: Fix bug #20595 to accept IGMPv3 "Query" messages.
+
+ 2007-07-25 Simon Goldschmidt
+ * api_msg.c, tcp.c: Another fix for bug #20021: by not returning an error if
+ tcp_output fails in tcp_close, the code in do_close_internal gets simpler
+ (tcp_output is called again later from tcp timers).
+
+ 2007-07-25 Simon Goldschmidt
+ * ip_frag.c: Fixed bug #20429: use the new pbuf_copy_partial instead of the old
+ copy_from_pbuf, which illegally modified the given pbuf.
+
+ 2007-07-25 Simon Goldschmidt
+ * tcp_out.c: tcp_enqueue: pcb->snd_queuelen didn't work for chaine PBUF_RAMs:
+ changed snd_queuelen++ to snd_queuelen += pbuf_clen(p).
+
+ 2007-07-24 Simon Goldschmidt
+ * api_msg.c, tcp.c: Fix bug #20480: Check the pcb passed to tcp_listen() for the
+ correct state (must be CLOSED).
+
+ 2007-07-13 Thomas Taranowski (commited by Jared Grubb)
+ * memp.c: Fix bug #20478: memp_malloc returned NULL+MEMP_SIZE on failed
+ allocation. It now returns NULL.
+
+ 2007-07-13 Frédéric Bernon
+ * api_msg.c: Fix bug #20318: api_msg "recv" callbacks don't call pbuf_free in
+ all error cases.
+
+ 2007-07-13 Frédéric Bernon
+ * api_msg.c: Fix bug #20315: possible memory leak problem if tcp_listen failed,
+ because current code doesn't follow rawapi.txt documentation.
+
+ 2007-07-13 Kieran Mansley
+ * src/core/tcp_in.c Apply patch#5741 from Oleg Tyshev to fix bug in
+ out of sequence processing of received packets
+
+ 2007-07-03 Simon Goldschmidt
+ * nearly-all-files: Added assertions where PBUF_RAM pbufs are used and an
+ assumption is made that this pbuf is in one piece (i.e. not chained). These
+ assumptions clash with the possibility of converting to fully pool-based
+ pbuf implementations, where PBUF_RAM pbufs might be chained.
+
+ 2007-07-03 Simon Goldschmidt
+ * api.h, api_lib.c, api_msg.c: Final fix for bug #20021 and some other problems
+ when closing tcp netconns: removed conn->sem, less context switches when
+ closing, both netconn_close and netconn_delete should safely close tcp
+ connections.
+
+ 2007-07-02 Simon Goldschmidt
+ * ipv4/ip.h, ipv6/ip.h, opt.h, netif.h, etharp.h, ipv4/ip.c, netif.c, raw.c,
+ tcp_out.c, udp.c, etharp.c: Added option LWIP_NETIF_HWADDRHINT (default=off)
+ to cache ARP table indices with each pcb instead of single-entry cache for
+ the complete stack.
+
+ 2007-07-02 Simon Goldschmidt
+ * tcp.h, tcp.c, tcp_in.c, tcp_out.c: Added some ASSERTS and casts to prevent
+ warnings when assigning to smaller types.
+
+ 2007-06-28 Simon Goldschmidt
+ * tcp_out.c: Added check to prevent tcp_pcb->snd_queuelen from overflowing.
+
+ 2007-06-28 Simon Goldschmidt
+ * tcp.h: Fixed bug #20287: Fixed nagle algorithm (sending was done too early if
+ a segment contained chained pbufs)
+
+ 2007-06-28 Frédéric Bernon
+ * autoip.c: replace most of rand() calls by a macro LWIP_AUTOIP_RAND which compute
+ a "pseudo-random" value based on netif's MAC and some autoip fields. It's always
+ possible to define this macro in your own lwipopts.h to always use C library's
+ rand(). Note that autoip_create_rand_addr doesn't use this macro.
+
+ 2007-06-28 Frédéric Bernon
+ * netifapi.h, netifapi.c, tcpip.h, tcpip.c: Update code to handle the option
+ LWIP_TCPIP_CORE_LOCKING, and do some changes to be coherent with last modifications
+ in api_lib/api_msg (use pointers and not type with table, etc...)
+
+ 2007-06-26 Simon Goldschmidt
+ * udp.h: Fixed bug #20259: struct udp_hdr was lacking the packin defines.
+
+ 2007-06-25 Simon Goldschmidt
+ * udp.c: Fixed bug #20253: icmp_dest_unreach was called with a wrong p->payload
+ for udp packets with no matching pcb.
+
+ 2007-06-25 Simon Goldschmidt
+ * udp.c: Fixed bug #20220: UDP PCB search in udp_input(): a non-local match
+ could get udp input packets if the remote side matched.
+
+ 2007-06-13 Simon Goldschmidt
+ * netif.c: Fixed bug #20180 (TCP pcbs listening on IP_ADDR_ANY could get
+ changed in netif_set_ipaddr if previous netif->ip_addr.addr was 0.
+
+ 2007-06-13 Simon Goldschmidt
+ * api_msg.c: pcb_new sets conn->err if protocol is not implemented
+ -> netconn_new_..() does not allocate a new connection for unsupported
+ protocols.
+
+ 2007-06-13 Frédéric Bernon, Simon Goldschmidt
+ * api_lib.c: change return expression in netconn_addr and netconn_peer, because
+ conn->err was reset to ERR_OK without any reasons (and error was lost)...
+
+ 2007-06-13 Frédéric Bernon, Matthias Weisser
+ * opt.h, mem.h, mem.c, memp.c, pbuf.c, ip_frag.c, vj.c: Fix bug #20162. Rename
+ MEM_ALIGN in LWIP_MEM_ALIGN and MEM_ALIGN_SIZE in LWIP_MEM_ALIGN_SIZE to avoid
+ some macro names collision with some OS macros.
+
+ 2007-06-11 Simon Goldschmidt
+ * udp.c: UDP Lite: corrected the use of chksum_len (based on RFC3828: if it's 0,
+ create checksum over the complete packet. On RX, if it's < 8 (and not 0),
+ discard the packet. Also removed the duplicate 'udphdr->chksum = 0' for both
+ UDP & UDP Lite.
+
+ 2007-06-11 Srinivas Gollakota & Oleg Tyshev
+ * tcp_out.c: Fix for bug #20075 : "A problem with keep-alive timer and TCP flags"
+ where TCP flags wasn't initialized in tcp_keepalive.
+
+ 2007-06-03 Simon Goldschmidt
+ * udp.c: udp_input(): Input pbuf was not freed if pcb had no recv function
+ registered, p->payload was modified without modifying p->len if sending
+ icmp_dest_unreach() (had no negative effect but was definitively wrong).
+
+ 2007-06-03 Simon Goldschmidt
+ * icmp.c: Corrected bug #19937: For responding to an icmp echo request, icmp
+ re-used the input pbuf even if that didn't have enough space to include the
+ link headers. Now the space is tested and a new pbuf is allocated for the
+ echo response packet if the echo request pbuf isn't big enough.
+
+ 2007-06-01 Simon Goldschmidt
+ * sockets.c: Checked in patch #5914: Moved sockopt processing into tcpip_thread.
+
+ 2007-05-23 Frédéric Bernon
+ * api_lib.c, sockets.c: Fixed bug #5958 for netconn_listen (acceptmbox only
+ allocated by do_listen if success) and netconn_accept errors handling. In
+ most of api_lib functions, we replace some errors checkings like "if (conn==NULL)"
+ by ASSERT, except for netconn_delete.
+
+ 2007-05-23 Frédéric Bernon
+ * api_lib.c: Fixed bug #5957 "Safe-thread problem inside netconn_recv" to return
+ an error code if it's impossible to fetch a pbuf on a TCP connection (and not
+ directly close the recvmbox).
+
+ 2007-05-22 Simon Goldschmidt
+ * tcp.c: Fixed bug #1895 (tcp_bind not correct) by introducing a list of
+ bound but unconnected (and non-listening) tcp_pcbs.
+
+ 2007-05-22 Frédéric Bernon
+ * sys.h, sys.c, api_lib.c, tcpip.c: remove sys_mbox_fetch_timeout() (was only
+ used for LWIP_SO_RCVTIMEO option) and use sys_arch_mbox_fetch() instead of
+ sys_mbox_fetch() in api files. Now, users SHOULD NOT use internal lwIP features
+ like "sys_timeout" in their application threads.
+
+ 2007-05-22 Frédéric Bernon
+ * api.h, api_lib.c, api_msg.h, api_msg.c: change the struct api_msg_msg to see
+ which parameters are used by which do_xxx function, and to avoid "misusing"
+ parameters (patch #5938).
+
+ 2007-05-22 Simon Goldschmidt
+ * api_lib.c, api_msg.c, raw.c, api.h, api_msg.h, raw.h: Included patch #5938:
+ changed raw_pcb.protocol from u16_t to u8_t since for IPv4 and IPv6, proto
+ is only 8 bits wide. This affects the api, as there, the protocol was
+ u16_t, too.
+
+ 2007-05-18 Simon Goldschmidt
+ * memp.c: addition to patch #5913: smaller pointer was returned but
+ memp_memory was the same size -> did not save memory.
+
+ 2007-05-16 Simon Goldschmidt
+ * loopif.c, slipif.c: Fix bug #19729: free pbuf if netif->input() returns
+ != ERR_OK.
+
+ 2007-05-16 Simon Goldschmidt
+ * api_msg.c, udp.c: If a udp_pcb has a local_ip set, check if it is the same
+ as the one of the netif used for sending to prevent sending from old
+ addresses after a netif address gets changed (partly fixes bug #3168).
+
+ 2007-05-16 Frédéric Bernon
+ * tcpip.c, igmp.h, igmp.c: Fixed bug "#19800 : IGMP: igmp_tick() will not work
+ with NO_SYS=1". Note that igmp_init is always in tcpip_thread (and not in
+ tcpip_init) because we have to be sure that network interfaces are already
+ added (mac filter is updated only in igmp_init for the moment).
+
+ 2007-05-16 Simon Goldschmidt
+ * mem.c, memp.c: Removed semaphores from memp, changed sys_sem_wait calls
+ into sys_arch_sem_wait calls to prevent timers from running while waiting
+ for the heap. This fixes bug #19167.
+
+ 2007-05-13 Simon Goldschmidt
+ * tcp.h, sockets.h, sockets.c: Fixed bug from patch #5865 by moving the defines
+ for socket options (lwip_set/-getsockopt) used with level IPPROTO_TCP from
+ tcp.h to sockets.h.
+
+ 2007-05-07 Simon Goldschmidt
+ * mem.c: Another attempt to fix bug #17922.
+
+ 2007-05-04 Simon Goldschmidt
+ * pbuf.c, pbuf.h, etharp.c: Further update to ARP queueing: Changed pbuf_copy()
+ implementation so that it can be reused (don't allocate the target
+ pbuf inside pbuf_copy()).
+
+ 2007-05-04 Simon Goldschmidt
+ * memp.c: checked in patch #5913: in memp_malloc() we can return memp as mem
+ to save a little RAM (next pointer of memp is not used while not in pool).
+
+ 2007-05-03 "maq"
+ * sockets.c: Fix ioctl FIONREAD when some data remains from last recv.
+ (patch #3574).
+
+ 2007-04-23 Simon Goldschmidt
+ * loopif.c, loopif.h, opt.h, src/netif/FILES: fix bug #2595: "loopif results
+ in NULL reference for incoming TCP packets". Loopif has to be configured
+ (using LWIP_LOOPIF_MULTITHREADING) to directly call netif->input()
+ (multithreading environments, e.g. netif->input() = tcpip_input()) or
+ putting packets on a list that is fed to the stack by calling loopif_poll()
+ (single-thread / NO_SYS / polling environment where e.g.
+ netif->input() = ip_input).
+
+ 2007-04-17 Jonathan Larmour
+ * pbuf.c: Use s32_t in pbuf_realloc(), as an s16_t can't reliably hold
+ the difference between two u16_t's.
+ * sockets.h: FD_SETSIZE needs to match number of sockets, which is
+ MEMP_NUM_NETCONN in sockets.c right now.
+
+ 2007-04-12 Jonathan Larmour
+ * icmp.c: Reset IP header TTL in ICMP ECHO responses (bug #19580).
+
+ 2007-04-12 Kieran Mansley
+ * tcp.c, tcp_in.c, tcp_out.c, tcp.h: Modify way the retransmission
+ timer is reset to fix bug#19434, with help from Oleg Tyshev.
+
+ 2007-04-11 Simon Goldschmidt
+ * etharp.c, pbuf.c, pbuf.h: 3rd fix for bug #11400 (arp-queuing): More pbufs than
+ previously thought need to be copied (everything but PBUF_ROM!). Cleaned up
+ pbuf.c: removed functions no needed any more (by etharp).
+
+ 2007-04-11 Kieran Mansley
+ * inet.c, ip_addr.h, sockets.h, sys.h, tcp.h: Apply patch #5745: Fix
+ "Constant is long" warnings with 16bit compilers. Contributed by
+ avatar@mmlab.cse.yzu.edu.tw
+
+ 2007-04-05 Frédéric Bernon, Jonathan Larmour
+ * api_msg.c: Fix bug #16830: "err_tcp() posts to connection mailbox when no pend on
+ the mailbox is active". Now, the post is only done during a connect, and do_send,
+ do_write and do_join_leave_group don't do anything if a previous error was signaled.
+
+ 2007-04-03 Frédéric Bernon
+ * ip.c: Don't set the IP_DF ("Don't fragment") flag in the IP header in IP output
+ packets. See patch #5834.
+
+ 2007-03-30 Frédéric Bernon
+ * api_msg.c: add a "pcb_new" helper function to avoid redundant code, and to add
+ missing pcb allocations checking (in do_bind, and for each raw_new). Fix style.
+
+ 2007-03-30 Frédéric Bernon
+ * most of files: prefix all debug.h define with "LWIP_" to avoid any conflict with
+ others environment defines (these were too "generic").
+
+ 2007-03-28 Frédéric Bernon
+ * api.h, api_lib.c, sockets.c: netbuf_ref doesn't check its internal pbuf_alloc call
+ result and can cause a crash. lwip_send now check netbuf_ref result.
+
+ 2007-03-28 Simon Goldschmidt
+ * sockets.c Remove "#include <errno.h>" from sockets.c to avoid multiple
+ definition of macros (in errno.h and lwip/arch.h) if LWIP_PROVIDE_ERRNO is
+ defined. This is the way it should have been already (looking at
+ doc/sys_arch.txt)
+
+ 2007-03-28 Kieran Mansley
+ * opt.h Change default PBUF_POOL_BUFSIZE (again) to accomodate default MSS +
+ IP and TCP headers *and* physical link headers
+
+ 2007-03-26 Frédéric Bernon (based on patch from Dmitry Potapov)
+ * api_lib.c: patch for netconn_write(), fixes a possible race condition which cause
+ to send some garbage. It is not a definitive solution, but the patch does solve
+ the problem for most cases.
+
+ 2007-03-22 Frédéric Bernon
+ * api_msg.h, api_msg.c: Remove obsolete API_MSG_ACCEPT and do_accept (never used).
+
+ 2007-03-22 Frédéric Bernon
+ * api_lib.c: somes resources couldn't be freed if there was errors during
+ netconn_new_with_proto_and_callback.
+
+ 2007-03-22 Frédéric Bernon
+ * ethernetif.c: update netif->input calls to check return value. In older ports,
+ it's a good idea to upgrade them, even if before, there could be another problem
+ (access to an uninitialized mailbox).
+
+ 2007-03-21 Simon Goldschmidt
+ * sockets.c: fixed bug #5067 (essentialy a signed/unsigned warning fixed
+ by casting to unsigned).
+
+ 2007-03-21 Frédéric Bernon
+ * api_lib.c, api_msg.c, tcpip.c: integrate sys_mbox_fetch(conn->mbox, NULL) calls from
+ api_lib.c to tcpip.c's tcpip_apimsg(). Now, use a local variable and not a
+ dynamic one from memp to send tcpip_msg to tcpip_thread in a synchrone call.
+ Free tcpip_msg from tcpip_apimsg is not done in tcpip_thread. This give a
+ faster and more reliable communication between api_lib and tcpip.
+
+ 2007-03-21 Frédéric Bernon
+ * opt.h: Add LWIP_NETIF_CALLBACK (to avoid compiler warning) and set it to 0.
+
+ 2007-03-21 Frédéric Bernon
+ * api_msg.c, igmp.c, igmp.h: Fix C++ style comments
+
+ 2007-03-21 Kieran Mansley
+ * opt.h Change default PBUF_POOL_BUFSIZE to accomodate default MSS +
+ IP and TCP headers
+
+ 2007-03-21 Kieran Mansley
+ * Fix all uses of pbuf_header to check the return value. In some
+ cases just assert if it fails as I'm not sure how to fix them, but
+ this is no worse than before when they would carry on regardless
+ of the failure.
+
+ 2007-03-21 Kieran Mansley
+ * sockets.c, igmp.c, igmp.h, memp.h: Fix C++ style comments and
+ comment out missing header include in icmp.c
+
+ 2007-03-20 Frédéric Bernon
+ * memp.h, stats.c: Fix stats_display function where memp_names table wasn't
+ synchronized with memp.h.
+
+ 2007-03-20 Frédéric Bernon
+ * tcpip.c: Initialize tcpip's mbox, and verify if initialized in tcpip_input,
+ tcpip_ethinput, tcpip_callback, tcpip_apimsg, to fix a init problem with
+ network interfaces. Also fix a compiler warning.
+
+ 2007-03-20 Kieran Mansley
+ * udp.c: Only try and use pbuf_header() to make space for headers if
+ not a ROM or REF pbuf.
+
+ 2007-03-19 Frédéric Bernon
+ * api_msg.h, api_msg.c, tcpip.h, tcpip.c: Add return types to tcpip_apimsg()
+ and api_msg_post().
+
+ 2007-03-19 Frédéric Bernon
+ * Remove unimplemented "memp_realloc" function from memp.h.
+
+ 2007-03-11 Simon Goldschmidt
+ * pbuf.c: checked in patch #5796: pbuf_alloc: len field claculation caused
+ memory corruption.
+
+ 2007-03-11 Simon Goldschmidt (based on patch from Dmitry Potapov)
+ * api_lib.c, sockets.c, api.h, api_msg.h, sockets.h: Fixed bug #19251
+ (missing `const' qualifier in socket functions), to get more compatible to
+ standard POSIX sockets.
+
+ 2007-03-11 Frédéric Bernon (based on patch from Dmitry Potapov)
+ * sockets.c: Add asserts inside bind, connect and sendto to check input
+ parameters. Remove excessive set_errno() calls after get_socket(), because
+ errno is set inside of get_socket(). Move last sock_set_errno() inside
+ lwip_close.
+
+ 2007-03-09 Simon Goldschmidt
+ * memp.c: Fixed bug #11400: New etharp queueing introduced bug: memp_memory
+ was allocated too small.
+
+ 2007-03-06 Simon Goldschmidt
+ * tcpip.c: Initialize dhcp timers in tcpip_thread (if LWIP_DHCP) to protect
+ the stack from concurrent access.
+
+ 2007-03-06 Frédéric Bernon, Dmitry Potapov
+ * tcpip.c, ip_frag.c, ethernetif.c: Fix some build problems, and a redundancy
+ call to "lwip_stats.link.recv++;" in low_level_input() & ethernetif_input().
+
+ 2007-03-06 Simon Goldschmidt
+ * ip_frag.c, ip_frag.h: Reduce code size: don't include code in those files
+ if IP_FRAG == 0 and IP_REASSEMBLY == 0
+
+ 2007-03-06 Frédéric Bernon, Simon Goldschmidt
+ * opt.h, ip_frag.h, tcpip.h, tcpip.c, ethernetif.c: add new configuration
+ option named ETHARP_TCPIP_ETHINPUT, which enable the new tcpip_ethinput.
+ Allow to do ARP processing for incoming packets inside tcpip_thread
+ (protecting ARP layer against concurrent access). You can also disable
+ old code using tcp_input with new define ETHARP_TCPIP_INPUT set to 0.
+ Older ports have to use tcpip_ethinput.
+
+ 2007-03-06 Simon Goldschmidt (based on patch from Dmitry Potapov)
+ * err.h, err.c: fixed compiler warning "initialization dircards qualifiers
+ from pointer target type"
+
+ 2007-03-05 Frédéric Bernon
+ * opt.h, sockets.h: add new configuration options (LWIP_POSIX_SOCKETS_IO_NAMES,
+ ETHARP_TRUST_IP_MAC, review SO_REUSE)
+
+ 2007-03-04 Frédéric Bernon
+ * api_msg.c: Remove some compiler warnings : parameter "pcb" was never
+ referenced.
+
+ 2007-03-04 Frédéric Bernon
+ * api_lib.c: Fix "[patch #5764] api_lib.c cleanup: after patch #5687" (from
+ Dmitry Potapov).
+ The api_msg struct stay on the stack (not moved to netconn struct).
+
+ 2007-03-04 Simon Goldschmidt (based on patch from Dmitry Potapov)
+ * pbuf.c: Fix BUG#19168 - pbuf_free can cause deadlock (if
+ SYS_LIGHTWEIGHT_PROT=1 & freeing PBUF_RAM when mem_sem is not available)
+ Also fixed cast warning in pbuf_alloc()
+
+ 2007-03-04 Simon Goldschmidt
+ * etharp.c, etharp.h, memp.c, memp.h, opt.h: Fix BUG#11400 - don't corrupt
+ existing pbuf chain when enqueuing multiple pbufs to a pending ARP request
+
+ 2007-03-03 Frédéric Bernon
+ * udp.c: remove obsolete line "static struct udp_pcb *pcb_cache = NULL;"
+ It is static, and never used in udp.c except udp_init().
+
+ 2007-03-02 Simon Goldschmidt
+ * tcpip.c: Moved call to ip_init(), udp_init() and tcp_init() from
+ tcpip_thread() to tcpip_init(). This way, raw API connections can be
+ initialized before tcpip_thread is running (e.g. before OS is started)
+
+ 2007-03-02 Frédéric Bernon
+ * rawapi.txt: Fix documentation mismatch with etharp.h about etharp_tmr's call
+ interval.
+
+ 2007-02-28 Kieran Mansley
+ * pbuf.c: Fix BUG#17645 - ensure pbuf payload pointer is not moved
+ outside the region of the pbuf by pbuf_header()
+
+ 2007-02-28 Kieran Mansley
+ * sockets.c: Fix BUG#19161 - ensure milliseconds timeout is non-zero
+ when supplied timeout is also non-zero
+
+(STABLE-1.2.0)
+
+ 2006-12-05 Leon Woestenberg
+ * CHANGELOG: Mention STABLE-1.2.0 release.
+
+ ++ New features:
+
+ 2006-12-01 Christiaan Simons
+ * mem.h, opt.h: Added MEM_LIBC_MALLOC option.
+ Note this is a workaround. Currently I have no other options left.
+
+ 2006-10-26 Christiaan Simons (accepted patch by Jonathan Larmour)
+ * ipv4/ip_frag.c: rename MAX_MTU to IP_FRAG_MAX_MTU and move define
+ to include/lwip/opt.h.
+ * ipv4/lwip/ip_frag.h: Remove unused IP_REASS_INTERVAL.
+ Move IP_REASS_MAXAGE and IP_REASS_BUFSIZE to include/lwip/opt.h.
+ * opt.h: Add above new options.
+
+ 2006-08-18 Christiaan Simons
+ * tcp_{in,out}.c: added SNMP counters.
+ * ipv4/ip.c: added SNMP counters.
+ * ipv4/ip_frag.c: added SNMP counters.
+
+ 2006-08-08 Christiaan Simons
+ * etharp.{c,h}: added etharp_find_addr() to read
+ (stable) ethernet/IP address pair from ARP table
+
+ 2006-07-14 Christiaan Simons
+ * mib_structs.c: added
+ * include/lwip/snmp_structs.h: added
+ * netif.{c,h}, netif/ethernetif.c: added SNMP statistics to netif struct
+
+ 2006-07-06 Christiaan Simons
+ * snmp/asn1_{enc,dec}.c added
+ * snmp/mib2.c added
+ * snmp/msg_{in,out}.c added
+ * include/lwip/snmp_asn1.h added
+ * include/lwip/snmp_msg.h added
+ * doc/snmp_agent.txt added
+
+ 2006-03-29 Christiaan Simons
+ * inet.c, inet.h: Added platform byteswap support.
+ Added LWIP_PLATFORM_BYTESWAP define (defaults to 0) and
+ optional LWIP_PLATFORM_HTONS(), LWIP_PLATFORM_HTONL() macros.
+
+ ++ Bug fixes:
+
+ 2006-11-30 Christiaan Simons
+ * dhcp.c: Fixed false triggers of request_timeout.
+
+ 2006-11-28 Christiaan Simons
+ * netif.c: In netif_add() fixed missing clear of ip_addr, netmask, gw and flags.
+
+ 2006-10-11 Christiaan Simons
+ * api_lib.c etharp.c, ip.c, memp.c, stats.c, sys.{c,h} tcp.h:
+ Partially accepted patch #5449 for ANSI C compatibility / build fixes.
+ * ipv4/lwip/ip.h ipv6/lwip/ip.h: Corrected UDP-Lite protocol
+ identifier from 170 to 136 (bug #17574).
+
+ 2006-10-10 Christiaan Simons
+ * api_msg.c: Fixed Nagle algorithm as reported by Bob Grice.
+
+ 2006-08-17 Christiaan Simons
+ * udp.c: Fixed bug #17200, added check for broadcast
+ destinations for PCBs bound to a unicast address.
+
+ 2006-08-07 Christiaan Simons
+ * api_msg.c: Flushing TCP output in do_close() (bug #15926).
+
+ 2006-06-27 Christiaan Simons
+ * api_msg.c: Applied patch for cold case (bug #11135).
+ In accept_function() ensure newconn->callback is always initialized.
+
+ 2006-06-15 Christiaan Simons
+ * mem.h: added MEM_SIZE_F alias to fix an ancient cold case (bug #1748),
+ facilitate printing of mem_size_t and u16_t statistics.
+
+ 2006-06-14 Christiaan Simons
+ * api_msg.c: Applied patch #5146 to handle allocation failures
+ in accept() by Kevin Lawson.
+
+ 2006-05-26 Christiaan Simons
+ * api_lib.c: Removed conn->sem creation and destruction
+ from netconn_write() and added sys_sem_new to netconn_new_*.
+
+(STABLE-1_1_1)
+
+ 2006-03-03 Christiaan Simons
+ * ipv4/ip_frag.c: Added bound-checking assertions on ip_reassbitmap
+ access and added pbuf_alloc() return value checks.
+
+ 2006-01-01 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp_{in,out}.c, tcp_out.c: Removed 'even sndbuf' fix in TCP, which is
+ now handled by the checksum routine properly.
+
+ 2006-02-27 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * pbuf.c: Fix alignment; pbuf_init() would not work unless
+ pbuf_pool_memory[] was properly aligned. (Patch by Curt McDowell.)
+
+ 2005-12-20 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp.c: Remove PCBs which stay in LAST_ACK state too long. Patch
+ submitted by Mitrani Hiroshi.
+
+ 2005-12-15 Christiaan Simons
+ * inet.c: Disabled the added summing routine to preserve code space.
+
+ 2005-12-14 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp_in.c: Duplicate FIN ACK race condition fix by Kelvin Lawson.
+ Added Curt McDowell's optimized checksumming routine for future
+ inclusion. Need to create test case for unaliged, aligned, odd,
+ even length combination of cases on various endianess machines.
+
+ 2005-12-09 Christiaan Simons
+ * inet.c: Rewrote standard checksum routine in proper portable C.
+
+ 2005-11-25 Christiaan Simons
+ * udp.c tcp.c: Removed SO_REUSE hack. Should reside in socket code only.
+ * *.c: introduced cc.h LWIP_DEBUG formatters matching the u16_t, s16_t,
+ u32_t, s32_t typedefs. This solves most debug word-length assumes.
+
+ 2005-07-17 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * inet.c: Fixed unaligned 16-bit access in the standard checksum
+ routine by Peter Jolasson.
+ * slipif.c: Fixed implementation assumption of single-pbuf datagrams.
+
+ 2005-02-04 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp_out.c: Fixed uninitialized 'queue' referenced in memerr branch.
+ * tcp_{out|in}.c: Applied patch fixing unaligned access.
+
+ 2005-01-04 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * pbuf.c: Fixed missing semicolon after LWIP_DEBUG statement.
+
+ 2005-01-03 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * udp.c: UDP pcb->recv() was called even when it was NULL.
+
+(STABLE-1_1_0)
+
+ 2004-12-28 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * etharp.*: Disabled multiple packets on the ARP queue.
+ This clashes with TCP queueing.
+
+ 2004-11-28 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * etharp.*: Fixed race condition from ARP request to ARP timeout.
+ Halved the ARP period, doubled the period counts.
+ ETHARP_MAX_PENDING now should be at least 2. This prevents
+ the counter from reaching 0 right away (which would allow
+ too little time for ARP responses to be received).
+
+ 2004-11-25 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * dhcp.c: Decline messages were not multicast but unicast.
+ * etharp.c: ETHARP_CREATE is renamed to ETHARP_TRY_HARD.
+ Do not try hard to insert arbitrary packet's source address,
+ etharp_ip_input() now calls etharp_update() without ETHARP_TRY_HARD.
+ etharp_query() now always DOES call ETHARP_TRY_HARD so that users
+ querying an address will see it appear in the cache (DHCP could
+ suffer from this when a server invalidly gave an in-use address.)
+ * ipv4/ip_addr.h: Renamed ip_addr_maskcmp() to _netcmp() as we are
+ comparing network addresses (identifiers), not the network masks
+ themselves.
+ * ipv4/ip_addr.c: ip_addr_isbroadcast() now checks that the given
+ IP address actually belongs to the network of the given interface.
+
+ 2004-11-24 Kieran Mansley <kjm25@cam.ac.uk>
+ * tcp.c: Increment pcb->snd_buf when ACK is received in SYN_SENT state.
+
+(STABLE-1_1_0-RC1)
+
+ 2004-10-16 Kieran Mansley <kjm25@cam.ac.uk>
+ * tcp.c: Add code to tcp_recved() to send an ACK (window update) immediately,
+ even if one is already pending, if the rcv_wnd is above a threshold
+ (currently TCP_WND/2). This avoids waiting for a timer to expire to send a
+ delayed ACK in order to open the window if the stack is only receiving data.
+
+ 2004-09-12 Kieran Mansley <kjm25@cam.ac.uk>
+ * tcp*.*: Retransmit time-out handling improvement by Sam Jansen.
+
+ 2004-08-20 Tony Mountifield <tony@softins.co.uk>
+ * etharp.c: Make sure the first pbuf queued on an ARP entry
+ is properly ref counted.
+
+ 2004-07-27 Tony Mountifield <tony@softins.co.uk>
+ * debug.h: Added (int) cast in LWIP_DEBUGF() to avoid compiler
+ warnings about comparison.
+ * pbuf.c: Stopped compiler complaining of empty if statement
+ when LWIP_DEBUGF() empty. Closed an unclosed comment.
+ * tcp.c: Stopped compiler complaining of empty if statement
+ when LWIP_DEBUGF() empty.
+ * ip.h Corrected IPH_TOS() macro: returns a byte, so doesn't need htons().
+ * inet.c: Added a couple of casts to quiet the compiler.
+ No need to test isascii(c) before isdigit(c) or isxdigit(c).
+
+ 2004-07-22 Tony Mountifield <tony@softins.co.uk>
+ * inet.c: Made data types consistent in inet_ntoa().
+ Added casts for return values of checksum routines, to pacify compiler.
+ * ip_frag.c, tcp_out.c, sockets.c, pbuf.c
+ Small corrections to some debugging statements, to pacify compiler.
+
+ 2004-07-21 Tony Mountifield <tony@softins.co.uk>
+ * etharp.c: Removed spurious semicolon and added missing end-of-comment.
+ * ethernetif.c Updated low_level_output() to match prototype for
+ netif->linkoutput and changed low_level_input() similarly for consistency.
+ * api_msg.c: Changed recv_raw() from int to u8_t, to match prototype
+ of raw_recv() in raw.h and so avoid compiler error.
+ * sockets.c: Added trivial (int) cast to keep compiler happier.
+ * ip.c, netif.c Changed debug statements to use the tidier ip4_addrN() macros.
+
+(STABLE-1_0_0)
+
+ ++ Changes:
+
+ 2004-07-05 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * sockets.*: Restructured LWIP_PRIVATE_TIMEVAL. Make sure
+ your cc.h file defines this either 1 or 0. If non-defined,
+ defaults to 1.
+ * .c: Added <string.h> and <errno.h> includes where used.
+ * etharp.c: Made some array indices unsigned.
+
+ 2004-06-27 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * netif.*: Added netif_set_up()/down().
+ * dhcp.c: Changes to restart program flow.
+
+ 2004-05-07 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * etharp.c: In find_entry(), instead of a list traversal per candidate, do a
+ single-pass lookup for different candidates. Should exploit locality.
+
+ 2004-04-29 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * tcp*.c: Cleaned up source comment documentation for Doxygen processing.
+ * opt.h: ETHARP_ALWAYS_INSERT option removed to comply with ARP RFC.
+ * etharp.c: update_arp_entry() only adds new ARP entries when adviced to by
+ the caller. This deprecates the ETHARP_ALWAYS_INSERT overrule option.
+
+ ++ Bug fixes:
+
+ 2004-04-27 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * etharp.c: Applied patch of bug #8708 by Toni Mountifield with a solution
+ suggested by Timmy Brolin. Fix for 32-bit processors that cannot access
+ non-aligned 32-bit words, such as soms 32-bit TCP/IP header fields. Fix
+ is to prefix the 14-bit Ethernet headers with two padding bytes.
+
+ 2004-04-23 Leon Woestenberg <leon.woestenberg@gmx.net>
+ * ip_addr.c: Fix in the ip_addr_isbroadcast() check.
+ * etharp.c: Fixed the case where the packet that initiates the ARP request
+ is not queued, and gets lost. Fixed the case where the packets destination
+ address is already known; we now always queue the packet and perform an ARP
+ request.
+
+(STABLE-0_7_0)
+
+ ++ Bug fixes:
+
+ * Fixed TCP bug for SYN_SENT to ESTABLISHED state transition.
+ * Fixed TCP bug in dequeueing of FIN from out of order segment queue.
+ * Fixed two possible NULL references in rare cases.
+
+(STABLE-0_6_6)
+
+ ++ Bug fixes:
+
+ * Fixed DHCP which did not include the IP address in DECLINE messages.
+
+ ++ Changes:
+
+ * etharp.c has been hauled over a bit.
+
+(STABLE-0_6_5)
+
+ ++ Bug fixes:
+
+ * Fixed TCP bug induced by bad window resizing with unidirectional TCP traffic.
+ * Packets sent from ARP queue had invalid source hardware address.
+
+ ++ Changes:
+
+ * Pass-by ARP requests do now update the cache.
+
+ ++ New features:
+
+ * No longer dependent on ctype.h.
+ * New socket options.
+ * Raw IP pcb support.
+
+(STABLE-0_6_4)
+
+ ++ Bug fixes:
+
+ * Some debug formatters and casts fixed.
+ * Numereous fixes in PPP.
+
+ ++ Changes:
+
+ * DEBUGF now is LWIP_DEBUGF
+ * pbuf_dechain() has been re-enabled.
+ * Mentioned the changed use of CVS branches in README.
+
+(STABLE-0_6_3)
+
+ ++ Bug fixes:
+
+ * Fixed pool pbuf memory leak in pbuf_alloc().
+ Occured if not enough PBUF_POOL pbufs for a packet pbuf chain.
+ Reported by Savin Zlobec.
+
+ * PBUF_POOL chains had their tot_len field not set for non-first
+ pbufs. Fixed in pbuf_alloc().
+
+ ++ New features:
+
+ * Added PPP stack contributed by Marc Boucher
+
+ ++ Changes:
+
+ * Now drops short packets for ICMP/UDP/TCP protocols. More robust.
+
+ * ARP queueuing now queues the latest packet instead of the first.
+ This is the RFC recommended behaviour, but can be overridden in
+ lwipopts.h.
+
+(0.6.2)
+
+ ++ Bugfixes:
+
+ * TCP has been fixed to deal with the new use of the pbuf->ref
+ counter.
+
+ * DHCP dhcp_inform() crash bug fixed.
+
+ ++ Changes:
+
+ * Removed pbuf_pool_free_cache and pbuf_pool_alloc_cache. Also removed
+ pbuf_refresh(). This has sped up pbuf pool operations considerably.
+ Implemented by David Haas.
+
+(0.6.1)
+
+ ++ New features:
+
+ * The packet buffer implementation has been enhanced to support
+ zero-copy and copy-on-demand for packet buffers which have their
+ payloads in application-managed memory.
+ Implemented by David Haas.
+
+ Use PBUF_REF to make a pbuf refer to RAM. lwIP will use zero-copy
+ if an outgoing packet can be directly sent on the link, or perform
+ a copy-on-demand when necessary.
+
+ The application can safely assume the packet is sent, and the RAM
+ is available to the application directly after calling udp_send()
+ or similar function.
+
+ ++ Bugfixes:
+
+ * ARP_QUEUEING should now correctly work for all cases, including
+ PBUF_REF.
+ Implemented by Leon Woestenberg.
+
+ ++ Changes:
+
+ * IP_ADDR_ANY is no longer a NULL pointer. Instead, it is a pointer
+ to a '0.0.0.0' IP address.
+
+ * The packet buffer implementation is changed. The pbuf->ref counter
+ meaning has changed, and several pbuf functions have been
+ adapted accordingly.
+
+ * netif drivers have to be changed to set the hardware address length field
+ that must be initialized correctly by the driver (hint: 6 for Ethernet MAC).
+ See the contrib/ports/c16x cs8900 driver as a driver example.
+
+ * netif's have a dhcp field that must be initialized to NULL by the driver.
+ See the contrib/ports/c16x cs8900 driver as a driver example.
+
+(0.5.x) This file has been unmaintained up to 0.6.1. All changes are
+ logged in CVS but have not been explained here.
+
+(0.5.3) Changes since version 0.5.2
+
+ ++ Bugfixes:
+
+ * memp_malloc(MEMP_API_MSG) could fail with multiple application
+ threads because it wasn't protected by semaphores.
+
+ ++ Other changes:
+
+ * struct ip_addr now packed.
+
+ * The name of the time variable in arp.c has been changed to ctime
+ to avoid conflicts with the time() function.
+
+(0.5.2) Changes since version 0.5.1
+
+ ++ New features:
+
+ * A new TCP function, tcp_tmr(), now handles both TCP timers.
+
+ ++ Bugfixes:
+
+ * A bug in tcp_parseopt() could cause the stack to hang because of a
+ malformed TCP option.
+
+ * The address of new connections in the accept() function in the BSD
+ socket library was not handled correctly.
+
+ * pbuf_dechain() did not update the ->tot_len field of the tail.
+
+ * Aborted TCP connections were not handled correctly in all
+ situations.
+
+ ++ Other changes:
+
+ * All protocol header structs are now packed.
+
+ * The ->len field in the tcp_seg structure now counts the actual
+ amount of data, and does not add one for SYN and FIN segments.
+
+(0.5.1) Changes since version 0.5.0
+
+ ++ New features:
+
+ * Possible to run as a user process under Linux.
+
+ * Preliminary support for cross platform packed structs.
+
+ * ARP timer now implemented.
+
+ ++ Bugfixes:
+
+ * TCP output queue length was badly initialized when opening
+ connections.
+
+ * TCP delayed ACKs were not sent correctly.
+
+ * Explicit initialization of BSS segment variables.
+
+ * read() in BSD socket library could drop data.
+
+ * Problems with memory alignment.
+
+ * Situations when all TCP buffers were used could lead to
+ starvation.
+
+ * TCP MSS option wasn't parsed correctly.
+
+ * Problems with UDP checksum calculation.
+
+ * IP multicast address tests had endianess problems.
+
+ * ARP requests had wrong destination hardware address.
+
+ ++ Other changes:
+
+ * struct eth_addr changed from u16_t[3] array to u8_t[6].
+
+ * A ->linkoutput() member was added to struct netif.
+
+ * TCP and UDP ->dest_* struct members where changed to ->remote_*.
+
+ * ntoh* macros are now null definitions for big endian CPUs.
+
+(0.5.0) Changes since version 0.4.2
+
+ ++ New features:
+
+ * Redesigned operating system emulation layer to make porting easier.
+
+ * Better control over TCP output buffers.
+
+ * Documenation added.
+
+ ++ Bugfixes:
+
+ * Locking issues in buffer management.
+
+ * Bugfixes in the sequential API.
+
+ * IP forwarding could cause memory leakage. This has been fixed.
+
+ ++ Other changes:
+
+ * Directory structure somewhat changed; the core/ tree has been
+ collapsed.
+
+(0.4.2) Changes since version 0.4.1
+
+ ++ New features:
+
+ * Experimental ARP implementation added.
+
+ * Skeleton Ethernet driver added.
+
+ * Experimental BSD socket API library added.
+
+ ++ Bugfixes:
+
+ * In very intense situations, memory leakage could occur. This has
+ been fixed.
+
+ ++ Other changes:
+
+ * Variables named "data" and "code" have been renamed in order to
+ avoid name conflicts in certain compilers.
+
+ * Variable++ have in appliciable cases been translated to ++variable
+ since some compilers generate better code in the latter case.
+
+(0.4.1) Changes since version 0.4
+
+ ++ New features:
+
+ * TCP: Connection attempts time out earlier than data
+ transmissions. Nagle algorithm implemented. Push flag set on the
+ last segment in a burst.
+
+ * UDP: experimental support for UDP-Lite extensions.
+
+ ++ Bugfixes:
+
+ * TCP: out of order segments were in some cases handled incorrectly,
+ and this has now been fixed. Delayed acknowledgements was broken
+ in 0.4, has now been fixed. Binding to an address that is in use
+ now results in an error. Reset connections sometimes hung an
+ application; this has been fixed.
+
+ * Checksum calculation sometimes failed for chained pbufs with odd
+ lengths. This has been fixed.
+
+ * API: a lot of bug fixes in the API. The UDP API has been improved
+ and tested. Error reporting and handling has been
+ improved. Logical flaws and race conditions for incoming TCP
+ connections has been found and removed.
+
+ * Memory manager: alignment issues. Reallocating memory sometimes
+ failed, this has been fixed.
+
+ * Generic library: bcopy was flawed and has been fixed.
+
+ ++ Other changes:
+
+ * API: all datatypes has been changed from generic ones such as
+ ints, to specified ones such as u16_t. Functions that return
+ errors now have the correct type (err_t).
+
+ * General: A lot of code cleaned up and debugging code removed. Many
+ portability issues have been fixed.
+
+ * The license was changed; the advertising clause was removed.
+
+ * C64 port added.
+
+ * Thanks: Huge thanks go to Dagan Galarneau, Horst Garnetzke, Petri
+ Kosunen, Mikael Caleres, and Frits Wilmink for reporting and
+ fixing bugs!
+
+(0.4) Changes since version 0.3.1
+
+ * Memory management has been radically changed; instead of
+ allocating memory from a shared heap, memory for objects that are
+ rapidly allocated and deallocated is now kept in pools. Allocation
+ and deallocation from those memory pools is very fast. The shared
+ heap is still present but is used less frequently.
+
+ * The memory, memory pool, and packet buffer subsystems now support
+ 4-, 2-, or 1-byte alignment.
+
+ * "Out of memory" situations are handled in a more robust way.
+
+ * Stack usage has been reduced.
+
+ * Easier configuration of lwIP parameters such as memory usage,
+ TTLs, statistics gathering, etc. All configuration parameters are
+ now kept in a single header file "lwipopts.h".
+
+ * The directory structure has been changed slightly so that all
+ architecture specific files are kept under the src/arch
+ hierarchy.
+
+ * Error propagation has been improved, both in the protocol modules
+ and in the API.
+
+ * The code for the RTXC architecture has been implemented, tested
+ and put to use.
+
+ * Bugs have been found and corrected in the TCP, UDP, IP, API, and
+ the Internet checksum modules.
+
+ * Bugs related to porting between a 32-bit and a 16-bit architecture
+ have been found and corrected.
+
+ * The license has been changed slightly to conform more with the
+ original BSD license, including the advertisement clause.
+
+(0.3.1) Changes since version 0.3
+
+ * Fix of a fatal bug in the buffer management. Pbufs with allocated
+ RAM never returned the RAM when the pbuf was deallocated.
+
+ * TCP congestion control, window updates and retransmissions did not
+ work correctly. This has now been fixed.
+
+ * Bugfixes in the API.
+
+(0.3) Changes since version 0.2
+
+ * New and improved directory structure. All include files are now
+ kept in a dedicated include/ directory.
+
+ * The API now has proper error handling. A new function,
+ netconn_err(), now returns an error code for the connection in
+ case of errors.
+
+ * Improvements in the memory management subsystem. The system now
+ keeps a pointer to the lowest free memory block. A new function,
+ mem_malloc2() tries to allocate memory once, and if it fails tries
+ to free some memory and retry the allocation.
+
+ * Much testing has been done with limited memory
+ configurations. lwIP now does a better job when overloaded.
+
+ * Some bugfixes and improvements to the buffer (pbuf) subsystem.
+
+ * Many bugfixes in the TCP code:
+
+ - Fixed a bug in tcp_close().
+
+ - The TCP receive window was incorrectly closed when out of
+ sequence segments was received. This has been fixed.
+
+ - Connections are now timed-out of the FIN-WAIT-2 state.
+
+ - The initial congestion window could in some cases be too
+ large. This has been fixed.
+
+ - The retransmission queue could in some cases be screwed up. This
+ has been fixed.
+
+ - TCP RST flag now handled correctly.
+
+ - Out of sequence data was in some cases never delivered to the
+ application. This has been fixed.
+
+ - Retransmitted segments now contain the correct acknowledgment
+ number and advertised window.
+
+ - TCP retransmission timeout backoffs are not correctly computed
+ (ala BSD). After a number of retransmissions, TCP now gives up
+ the connection.
+
+ * TCP connections now are kept on three lists, one for active
+ connections, one for listening connections, and one for
+ connections that are in TIME-WAIT. This greatly speeds up the fast
+ timeout processing for sending delayed ACKs.
+
+ * TCP now provides proper feedback to the application when a
+ connection has been successfully set up.
+
+ * More comments have been added to the code. The code has also been
+ somewhat cleaned up.
+
+(0.2) Initial public release.
diff --git a/src/VBox/Devices/Network/lwip-new/COPYING b/src/VBox/Devices/Network/lwip-new/COPYING
new file mode 100644
index 00000000..e23898b5
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/COPYING
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2001, 2002 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+
diff --git a/src/VBox/Devices/Network/lwip-new/Config.kmk b/src/VBox/Devices/Network/lwip-new/Config.kmk
new file mode 100644
index 00000000..cce3e0ed
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/Config.kmk
@@ -0,0 +1,107 @@
+# $Id: Config.kmk $
+## @file
+# Define the include dirs and source files for LWIP pre-1.5.0.
+#
+# This file is included by Devices/Makefile.kmk and NetworkServices/NAT/Makefile.kmk.
+#
+
+#
+# Copyright (C) 2006-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+LWIP_INCS += \
+ src/include \
+ src/include/ipv4 \
+ src/include/ipv6 \
+ vbox/include \
+ vbox
+
+LWIP_SOURCES += \
+ src/api/api_lib.c \
+ src/api/api_msg.c \
+ src/api/err.c \
+ src/api/netbuf.c \
+ src/api/netdb.c \
+ src/api/netifapi.c \
+ src/api/sockets.c \
+ src/api/tcpip.c \
+ src/core/def.c \
+ src/core/dhcp.c \
+ src/core/dns.c \
+ src/core/inet_chksum.c \
+ src/core/init.c \
+ src/core/ipv4/autoip.c \
+ src/core/ipv4/icmp.c \
+ src/core/ipv4/igmp.c \
+ src/core/ipv4/ip4.c \
+ src/core/ipv4/ip4_addr.c \
+ src/core/ipv4/ip_frag.c \
+ src/core/ipv6/dhcp6.c \
+ src/core/ipv6/ethip6.c \
+ src/core/ipv6/icmp6.c \
+ src/core/ipv6/inet6.c \
+ src/core/ipv6/ip6.c \
+ src/core/ipv6/ip6_addr.c \
+ src/core/ipv6/ip6_frag.c \
+ src/core/ipv6/mld6.c \
+ src/core/ipv6/nd6.c \
+ src/core/mem.c \
+ src/core/memp.c \
+ src/core/netif.c \
+ src/core/pbuf.c \
+ src/core/raw.c \
+ src/core/stats.c \
+ src/core/sys.c \
+ src/core/tcp.c \
+ src/core/tcp_in.c \
+ src/core/tcp_out.c \
+ src/core/timers.c \
+ src/core/udp.c \
+ src/netif/etharp.c \
+ vbox/sys_arch.c \
+ vbox/VBoxLwipCore.cpp
+
+# LWIP_SOURCES += \
+# src/core/snmp/asn1_dec.c \
+# src/core/snmp/asn1_enc.c \
+# src/core/snmp/mib2.c \
+# src/core/snmp/mib_structs.c \
+# src/core/snmp/msg_in.c \
+# src/core/snmp/msg_out.c \
+
+# LWIP_SOURCES += \
+# src/netif/slipif.c \
+
+# LWIP_SOURCES += \
+# src/netif/ppp/auth.c \
+# src/netif/ppp/chap.c \
+# src/netif/ppp/chpms.c \
+# src/netif/ppp/fsm.c \
+# src/netif/ppp/ipcp.c \
+# src/netif/ppp/lcp.c \
+# src/netif/ppp/magic.c \
+# src/netif/ppp/md5.c \
+# src/netif/ppp/pap.c \
+# src/netif/ppp/ppp.c \
+# src/netif/ppp/ppp_oe.c \
+# src/netif/ppp/randm.c \
+# src/netif/ppp/vj.c
+
diff --git a/src/VBox/Devices/Network/lwip-new/FILES b/src/VBox/Devices/Network/lwip-new/FILES
new file mode 100644
index 00000000..66253196
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/FILES
@@ -0,0 +1,4 @@
+src/ - The source code for the lwIP TCP/IP stack.
+doc/ - The documentation for lwIP.
+
+See also the FILES file in each subdirectory.
diff --git a/src/VBox/Devices/Network/lwip-new/Makefile.kup b/src/VBox/Devices/Network/lwip-new/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/Makefile.kup
diff --git a/src/VBox/Devices/Network/lwip-new/README b/src/VBox/Devices/Network/lwip-new/README
new file mode 100644
index 00000000..a62cc4f3
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/README
@@ -0,0 +1,89 @@
+INTRODUCTION
+
+lwIP is a small independent implementation of the TCP/IP protocol
+suite that has been developed by Adam Dunkels at the Computer and
+Networks Architectures (CNA) lab at the Swedish Institute of Computer
+Science (SICS).
+
+The focus of the lwIP TCP/IP implementation is to reduce the RAM usage
+while still having a full scale TCP. This making lwIP suitable for use
+in embedded systems with tens of kilobytes of free RAM and room for
+around 40 kilobytes of code ROM.
+
+FEATURES
+
+ * IP (Internet Protocol) including packet forwarding over multiple network
+ interfaces
+ * ICMP (Internet Control Message Protocol) for network maintenance and debugging
+ * IGMP (Internet Group Management Protocol) for multicast traffic management
+ * UDP (User Datagram Protocol) including experimental UDP-lite extensions
+ * TCP (Transmission Control Protocol) with congestion control, RTT estimation
+ and fast recovery/fast retransmit
+ * Specialized raw/native API for enhanced performance
+ * Optional Berkeley-like socket API
+ * DNS (Domain names resolver)
+ * SNMP (Simple Network Management Protocol)
+ * DHCP (Dynamic Host Configuration Protocol)
+ * AUTOIP (for IPv4, conform with RFC 3927)
+ * PPP (Point-to-Point Protocol)
+ * ARP (Address Resolution Protocol) for Ethernet
+
+LICENSE
+
+lwIP is freely available under a BSD license.
+
+DEVELOPMENT
+
+lwIP has grown into an excellent TCP/IP stack for embedded devices,
+and developers using the stack often submit bug fixes, improvements,
+and additions to the stack to further increase its usefulness.
+
+Development of lwIP is hosted on Savannah, a central point for
+software development, maintenance and distribution. Everyone can
+help improve lwIP by use of Savannah's interface, CVS and the
+mailing list. A core team of developers will commit changes to the
+CVS source tree.
+
+The lwIP TCP/IP stack is maintained in the 'lwip' CVS module and
+contributions (such as platform ports) are in the 'contrib' module.
+
+See doc/savannah.txt for details on CVS server access for users and
+developers.
+
+Last night's CVS tar ball can be downloaded from:
+ http://savannah.gnu.org/cvs.backups/lwip.tar.gz [CHANGED - NEEDS FIXING]
+
+The current CVS trees are web-browsable:
+ http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/lwip/
+ http://savannah.nongnu.org/cgi-bin/viewcvs/lwip/contrib/
+
+Submit patches and bugs via the lwIP project page:
+ http://savannah.nongnu.org/projects/lwip/
+
+
+DOCUMENTATION
+
+The original out-dated homepage of lwIP and Adam Dunkels' papers on
+lwIP are at the official lwIP home page:
+ http://www.sics.se/~adam/lwip/
+
+Self documentation of the source code is regularly extracted from the
+current CVS sources and is available from this web page:
+ http://www.nongnu.org/lwip/
+
+There is now a constantly growin wiki about lwIP at
+ http://lwip.wikia.com/wiki/LwIP_Wiki
+
+Also, there are mailing lists you can subscribe at
+ http://savannah.nongnu.org/mail/?group=lwip
+plus searchable archives:
+ http://lists.nongnu.org/archive/html/lwip-users/
+ http://lists.nongnu.org/archive/html/lwip-devel/
+
+Reading Adam's papers, the files in docs/, browsing the source code
+documentation and browsing the mailing list archives is a good way to
+become familiar with the design of lwIP.
+
+Adam Dunkels <adam@sics.se>
+Leon Woestenberg <leon.woestenberg@gmx.net>
+
diff --git a/src/VBox/Devices/Network/lwip-new/UPGRADING b/src/VBox/Devices/Network/lwip-new/UPGRADING
new file mode 100644
index 00000000..6501107a
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/UPGRADING
@@ -0,0 +1,144 @@
+This file lists major changes between release versions that require
+ports or applications to be changed. Use it to update a port or an
+application written for an older version of lwIP to correctly work
+with newer versions.
+
+
+(CVS HEAD)
+
+ * [Enter new changes just after this line - do not remove this line]
+
+ ++ Application changes:
+
+ * Replaced struct ip_addr by typedef ip_addr_t (struct ip_addr is kept for
+ compatibility to old applications, but will be removed in the future).
+
+ * Renamed mem_realloc() to mem_trim() to prevent confusion with realloc()
+
+ +++ Raw API:
+ * Changed the semantics of tcp_close() (since it was rather a
+ shutdown before): Now the application does *NOT* get any calls to the recv
+ callback (aside from NULL/closed) after calling tcp_close()
+
+ * When calling tcp_abort() from a raw API TCP callback function,
+ make sure you return ERR_ABRT to prevent accessing unallocated memory.
+ (ERR_ABRT now means the applicaiton has called tcp_abort!)
+
+ +++ Netconn API:
+ * Changed netconn_receive() and netconn_accept() to return
+ err_t, not a pointer to new data/netconn.
+
+ +++ Socket API:
+ * LWIP_SO_RCVTIMEO: when accept() or recv() time out, they
+ now set errno to EWOULDBLOCK/EAGAIN, not ETIMEDOUT.
+
+ * Added a minimal version of posix fctl() to have a
+ standardised way to set O_NONBLOCK for nonblocking sockets.
+
+ +++ all APIs:
+ * correctly implemented SO(F)_REUSEADDR
+
+ ++ Port changes
+
+ +++ new files:
+
+ * Added 4 new files: def.c, timers.c, timers.h, tcp_impl.h:
+
+ * Moved stack-internal parts of tcp.h to tcp_impl.h, tcp.h now only contains
+ the actual application programmer's API
+
+ * Separated timer implementation from sys.h/.c, moved to timers.h/.c;
+ Added timer implementation for NO_SYS==1, set NO_SYS_NO_TIMERS==1 if you
+ still want to use your own timer implementation for NO_SYS==0 (as before).
+
+ +++ sys layer:
+
+ * Converted mbox- and semaphore-functions to take pointers to sys_mbox_t/
+ sys_sem_t;
+
+ * Converted sys_mbox_new/sys_sem_new to take pointers and return err_t;
+
+ * Added Mutex concept in sys_arch (define LWIP_COMPAT_MUTEX to let sys.h use
+ binary semaphores instead of mutexes - as before)
+
+ +++ new options:
+
+ * Don't waste memory when chaining segments, added option TCP_OVERSIZE to
+ prevent creating many small pbufs when calling tcp_write with many small
+ blocks of data. Instead, pbufs are allocated larger than needed and the
+ space is used for later calls to tcp_write.
+
+ * Added LWIP_NETIF_TX_SINGLE_PBUF to always copy to try to create single pbufs
+ in tcp_write/udp_send.
+
+ * Added an additional option LWIP_ETHERNET to support ethernet without ARP
+ (necessary for pure PPPoE)
+
+ * Add MEMP_SEPARATE_POOLS to place memory pools in separate arrays. This may
+ be used to place these pools into user-defined memory by using external
+ declaration.
+
+ * Added TCP_SNDQUEUELOWAT corresponding to TCP_SNDLOWAT
+
+ +++ new pools:
+
+ * Netdb uses a memp pool for allocating memory when getaddrinfo() is called,
+ so MEMP_NUM_NETDB has to be set accordingly.
+
+ * DNS_LOCAL_HOSTLIST_IS_DYNAMIC uses a memp pool instead of the heap, so
+ MEMP_NUM_LOCALHOSTLIST has to be set accordingly.
+
+ * Snmp-agent uses a memp pools instead of the heap, so MEMP_NUM_SNMP_* have
+ to be set accordingly.
+
+ * PPPoE uses a MEMP pool instead of the heap, so MEMP_NUM_PPPOE_INTERFACES
+ has to be set accordingly
+
+ * Integrated loopif into netif.c - loopif does not have to be created by the
+ port any more, just define LWIP_HAVE_LOOPIF to 1.
+
+ * Added define LWIP_RAND() for lwip-wide randomization (needs to be defined
+ in cc.h, e.g. used by igmp)
+
+ * Added printf-formatter X8_F to printf u8_t as hex
+
+ * The heap now may be moved to user-defined memory by defining
+ LWIP_RAM_HEAP_POINTER as a void pointer to that memory's address
+
+ * added autoip_set_struct() and dhcp_set_struct() to let autoip and dhcp work
+ with user-allocated structs instead of calling mem_malloc
+
+ * Added const char* name to mem- and memp-stats for easier debugging.
+
+ * Calculate the TCP/UDP checksum while copying to only fetch data once:
+ Define LWIP_CHKSUM_COPY to a memcpy-like function that returns the checksum
+
+ * Added SO_REUSE_RXTOALL to pass received UDP broadcast/multicast packets to
+ more than one pcb.
+
+ * Changed the semantics of ARP_QUEUEING==0: ARP_QUEUEING now cannot be turned
+ off any more, if this is set to 0, only one packet (the most recent one) is
+ queued (like demanded by RFC 1122).
+
+
+ ++ Major bugfixes/improvements
+
+ * Implemented tcp_shutdown() to only shut down one end of a connection
+ * Implemented shutdown() at socket- and netconn-level
+ * Added errorset support to select() + improved select speed overhead
+ * Merged pppd to v2.3.11 (including some backported bugfixes from 2.4.x)
+ * Added timer implementation for NO_SYS==1 (may be disabled with NO_SYS_NO_TIMERS==1
+ * Use macros defined in ip_addr.h to work with IP addresses
+ * Implemented many nonblocking socket/netconn functions
+ * Fixed ARP input processing: only add a new entry if a request was directed as us
+ * mem_realloc() to mem_trim() to prevent confusion with realloc()
+ * Some improvements for AutoIP (don't route/forward link-local addresses, don't break
+ existing connections when assigning a routable address)
+ * Correctly handle remote side overrunning our rcv_wnd in ooseq case
+ * Removed packing from ip_addr_t, the packed version is now only used in protocol headers
+ * Corrected PBUF_POOL_BUFSIZE for ports where ETH_PAD_SIZE > 0
+ * Added support for static ARP table entries
+
+(STABLE-1.3.2)
+
+ * initial version of this file
diff --git a/src/VBox/Devices/Network/lwip-new/src/Makefile.kup b/src/VBox/Devices/Network/lwip-new/src/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/Makefile.kup
diff --git a/src/VBox/Devices/Network/lwip-new/src/api/Makefile.kup b/src/VBox/Devices/Network/lwip-new/src/api/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/api/Makefile.kup
diff --git a/src/VBox/Devices/Network/lwip-new/src/api/api_lib.c b/src/VBox/Devices/Network/lwip-new/src/api/api_lib.c
new file mode 100644
index 00000000..adaaad43
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/api/api_lib.c
@@ -0,0 +1,788 @@
+/**
+ * @file
+ * Sequential API External module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/* This is the part of the API that is linked with
+ the application */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/api.h"
+#include "lwip/tcpip.h"
+#include "lwip/memp.h"
+
+#include "lwip/ip.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+
+#include <string.h>
+
+/**
+ * Create a new netconn (of a specific type) that has a callback function.
+ * The corresponding pcb is also created.
+ *
+ * @param t the type of 'connection' to create (@see enum netconn_type)
+ * @param proto the IP protocol for RAW IP pcbs
+ * @param callback a function to call on status changes (RX available, TX'ed)
+ * @return a newly allocated struct netconn or
+ * NULL on memory error
+ */
+struct netconn*
+netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto, netconn_callback callback)
+{
+ struct netconn *conn;
+ struct api_msg msg;
+
+ conn = netconn_alloc(t, callback);
+ if (conn != NULL) {
+ err_t err;
+ msg.msg.msg.n.proto = proto;
+ msg.msg.conn = conn;
+ TCPIP_APIMSG((&msg), lwip_netconn_do_newconn, err);
+ if (err != ERR_OK) {
+ LWIP_ASSERT("freeing conn without freeing pcb", conn->pcb.tcp == NULL);
+ LWIP_ASSERT("conn has no op_completed", sys_sem_valid(&conn->op_completed));
+ LWIP_ASSERT("conn has no recvmbox", sys_mbox_valid(&conn->recvmbox));
+#if LWIP_TCP
+ LWIP_ASSERT("conn->acceptmbox shouldn't exist", !sys_mbox_valid(&conn->acceptmbox));
+#endif /* LWIP_TCP */
+ sys_sem_free(&conn->op_completed);
+ sys_mbox_free(&conn->recvmbox);
+ memp_free(MEMP_NETCONN, conn);
+ return NULL;
+ }
+ }
+ return conn;
+}
+
+/**
+ * Close a netconn 'connection' and free its resources.
+ * UDP and RAW connection are completely closed, TCP pcbs might still be in a waitstate
+ * after this returns.
+ *
+ * @param conn the netconn to delete
+ * @return ERR_OK if the connection was deleted
+ */
+err_t
+netconn_delete(struct netconn *conn)
+{
+ struct api_msg msg;
+
+ /* No ASSERT here because possible to get a (conn == NULL) if we got an accept error */
+ if (conn == NULL) {
+ return ERR_OK;
+ }
+
+ msg.function = lwip_netconn_do_delconn;
+ msg.msg.conn = conn;
+ tcpip_apimsg(&msg);
+
+ netconn_free(conn);
+
+ /* don't care for return value of lwip_netconn_do_delconn since it only calls void functions */
+
+ return ERR_OK;
+}
+
+/**
+ * Get the local or remote IP address and port of a netconn.
+ * For RAW netconns, this returns the protocol instead of a port!
+ *
+ * @param conn the netconn to query
+ * @param addr a pointer to which to save the IP address
+ * @param port a pointer to which to save the port (or protocol for RAW)
+ * @param local 1 to get the local IP address, 0 to get the remote one
+ * @return ERR_CONN for invalid connections
+ * ERR_OK if the information was retrieved
+ */
+err_t
+netconn_getaddr(struct netconn *conn, ip_addr_t *addr, u16_t *port, u8_t local)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_getaddr: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_getaddr: invalid addr", (addr != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_getaddr: invalid port", (port != NULL), return ERR_ARG;);
+
+ msg.msg.conn = conn;
+ msg.msg.msg.ad.ipaddr = ip_2_ipX(addr);
+ msg.msg.msg.ad.port = port;
+ msg.msg.msg.ad.local = local;
+ TCPIP_APIMSG(&msg, lwip_netconn_do_getaddr, err);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Bind a netconn to a specific local IP address and port.
+ * Binding one netconn twice might not always be checked correctly!
+ *
+ * @param conn the netconn to bind
+ * @param addr the local IP address to bind the netconn to (use IP_ADDR_ANY
+ * to bind to all addresses)
+ * @param port the local port to bind the netconn to (not used for RAW)
+ * @return ERR_OK if bound, any other err_t on failure
+ */
+err_t
+netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_bind: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.msg.conn = conn;
+ msg.msg.msg.bc.ipaddr = addr;
+ msg.msg.msg.bc.port = port;
+ TCPIP_APIMSG(&msg, lwip_netconn_do_bind, err);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Connect a netconn to a specific remote IP address and port.
+ *
+ * @param conn the netconn to connect
+ * @param addr the remote IP address to connect to
+ * @param port the remote port to connect to (no used for RAW)
+ * @return ERR_OK if connected, return value of tcp_/udp_/raw_connect otherwise
+ */
+err_t
+netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_connect: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.msg.conn = conn;
+ msg.msg.msg.bc.ipaddr = addr;
+ msg.msg.msg.bc.port = port;
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+ if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ {
+ /* The TCP version waits for the connect to succeed,
+ so always needs to use message passing. */
+ msg.function = lwip_netconn_do_connect;
+ err = tcpip_apimsg(&msg);
+ }
+#endif /* LWIP_TCP */
+#if (LWIP_UDP || LWIP_RAW) && LWIP_TCP
+ else
+#endif /* (LWIP_UDP || LWIP_RAW) && LWIP_TCP */
+#if (LWIP_UDP || LWIP_RAW)
+ {
+ /* UDP and RAW only set flags, so we can use core-locking. */
+ TCPIP_APIMSG(&msg, lwip_netconn_do_connect, err);
+ }
+#endif /* (LWIP_UDP || LWIP_RAW) */
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Disconnect a netconn from its current peer (only valid for UDP netconns).
+ *
+ * @param conn the netconn to disconnect
+ * @return TODO: return value is not set here...
+ */
+err_t
+netconn_disconnect(struct netconn *conn)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_disconnect: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.msg.conn = conn;
+ TCPIP_APIMSG(&msg, lwip_netconn_do_disconnect, err);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Set a TCP netconn into listen mode
+ *
+ * @param conn the tcp netconn to set to listen mode
+ * @param backlog the listen backlog, only used if TCP_LISTEN_BACKLOG==1
+ * @return ERR_OK if the netconn was set to listen (UDP and RAW netconns
+ * don't return any error (yet?))
+ */
+err_t
+netconn_listen_with_backlog(struct netconn *conn, u8_t backlog)
+{
+#if LWIP_TCP
+ struct api_msg msg;
+ err_t err;
+
+ /* This does no harm. If TCP_LISTEN_BACKLOG is off, backlog is unused. */
+ LWIP_UNUSED_ARG(backlog);
+
+ LWIP_ERROR("netconn_listen: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.msg.conn = conn;
+#if TCP_LISTEN_BACKLOG
+ msg.msg.msg.lb.backlog = backlog;
+#endif /* TCP_LISTEN_BACKLOG */
+ TCPIP_APIMSG(&msg, lwip_netconn_do_listen, err);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(conn);
+ LWIP_UNUSED_ARG(backlog);
+ return ERR_ARG;
+#endif /* LWIP_TCP */
+}
+
+/**
+ * Accept a new connection on a TCP listening netconn.
+ *
+ * @param conn the TCP listen netconn
+ * @param new_conn pointer where the new connection is stored
+ * @return ERR_OK if a new connection has been received or an error
+ * code otherwise
+ */
+err_t
+netconn_accept(struct netconn *conn, struct netconn **new_conn)
+{
+#if LWIP_TCP
+ struct netconn *newconn;
+ err_t err;
+#if TCP_LISTEN_BACKLOG
+ struct api_msg msg;
+#endif /* TCP_LISTEN_BACKLOG */
+
+ LWIP_ERROR("netconn_accept: invalid pointer", (new_conn != NULL), return ERR_ARG;);
+ *new_conn = NULL;
+ LWIP_ERROR("netconn_accept: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_accept: invalid acceptmbox", sys_mbox_valid(&conn->acceptmbox), return ERR_ARG;);
+
+ err = conn->last_err;
+ if (ERR_IS_FATAL(err)) {
+ /* don't recv on fatal errors: this might block the application task
+ waiting on acceptmbox forever! */
+ return err;
+ }
+
+#if LWIP_SO_RCVTIMEO
+ if (sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+ NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
+ return ERR_TIMEOUT;
+ }
+#else
+ sys_arch_mbox_fetch(&conn->acceptmbox, (void **)&newconn, 0);
+#endif /* LWIP_SO_RCVTIMEO*/
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
+
+ if (newconn == NULL) {
+ /* connection has been aborted */
+ NETCONN_SET_SAFE_ERR(conn, ERR_ABRT);
+ return ERR_ABRT;
+ }
+#if TCP_LISTEN_BACKLOG
+ /* Let the stack know that we have accepted the connection. */
+ msg.msg.conn = conn;
+ /* don't care for the return value of lwip_netconn_do_recv */
+ TCPIP_APIMSG_NOERR(&msg, lwip_netconn_do_recv);
+#endif /* TCP_LISTEN_BACKLOG */
+
+ *new_conn = newconn;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(conn);
+ LWIP_UNUSED_ARG(new_conn);
+ return ERR_ARG;
+#endif /* LWIP_TCP */
+}
+
+/**
+ * Receive data: actual implementation that doesn't care whether pbuf or netbuf
+ * is received
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf/netbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ */
+static err_t
+netconn_recv_data(struct netconn *conn, void **new_buf)
+{
+ void *buf = NULL;
+ u16_t len;
+ err_t err;
+#if LWIP_TCP
+ struct api_msg msg;
+#endif /* LWIP_TCP */
+
+ LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
+ *new_buf = NULL;
+ LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
+
+ err = conn->last_err;
+ if (ERR_IS_FATAL(err)) {
+ /* don't recv on fatal errors: this might block the application task
+ waiting on recvmbox forever! */
+ /* @todo: this does not allow us to fetch data that has been put into recvmbox
+ before the fatal error occurred - is that a problem? */
+ return err;
+ }
+
+#if LWIP_SO_RCVTIMEO
+ if (sys_arch_mbox_fetch(&conn->recvmbox, &buf, conn->recv_timeout) == SYS_ARCH_TIMEOUT) {
+ NETCONN_SET_SAFE_ERR(conn, ERR_TIMEOUT);
+ return ERR_TIMEOUT;
+ }
+#else
+ sys_arch_mbox_fetch(&conn->recvmbox, &buf, 0);
+#endif /* LWIP_SO_RCVTIMEO*/
+
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+ if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ {
+ if (!netconn_get_noautorecved(conn) || (buf == NULL)) {
+ /* Let the stack know that we have taken the data. */
+ /* TODO: Speedup: Don't block and wait for the answer here
+ (to prevent multiple thread-switches). */
+ msg.msg.conn = conn;
+ if (buf != NULL) {
+ msg.msg.msg.r.len = ((struct pbuf *)buf)->tot_len;
+ } else {
+ msg.msg.msg.r.len = 1;
+ }
+ /* don't care for the return value of lwip_netconn_do_recv */
+ TCPIP_APIMSG_NOERR(&msg, lwip_netconn_do_recv);
+ }
+
+ /* If we are closed, we indicate that we no longer wish to use the socket */
+ if (buf == NULL) {
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, 0);
+ /* Avoid to lose any previous error code */
+ NETCONN_SET_SAFE_ERR(conn, ERR_CLSD);
+ return ERR_CLSD;
+ }
+ len = ((struct pbuf *)buf)->tot_len;
+ }
+#endif /* LWIP_TCP */
+#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
+ else
+#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
+#if (LWIP_UDP || LWIP_RAW)
+ {
+ LWIP_ASSERT("buf != NULL", buf != NULL);
+ len = netbuf_len((struct netbuf *)buf);
+ }
+#endif /* (LWIP_UDP || LWIP_RAW) */
+
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_DEC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVMINUS, len);
+
+ LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_recv_data: received %p, len=%"U16_F"\n", buf, len));
+
+ *new_buf = buf;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+}
+
+/**
+ * Receive data (in form of a pbuf) from a TCP netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new pbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ * ERR_ARG if conn is not a TCP netconn
+ */
+err_t
+netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf)
+{
+ LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL) &&
+ NETCONNTYPE_GROUP(netconn_type(conn)) == NETCONN_TCP, return ERR_ARG;);
+
+ return netconn_recv_data(conn, (void **)new_buf);
+}
+
+/**
+ * Receive data (in form of a netbuf containing a packet buffer) from a netconn
+ *
+ * @param conn the netconn from which to receive data
+ * @param new_buf pointer where a new netbuf is stored when received data
+ * @return ERR_OK if data has been received, an error code otherwise (timeout,
+ * memory error or another error)
+ */
+err_t
+netconn_recv(struct netconn *conn, struct netbuf **new_buf)
+{
+#if LWIP_TCP
+ struct netbuf *buf = NULL;
+ err_t err;
+#endif /* LWIP_TCP */
+
+ LWIP_ERROR("netconn_recv: invalid pointer", (new_buf != NULL), return ERR_ARG;);
+ *new_buf = NULL;
+ LWIP_ERROR("netconn_recv: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_accept: invalid recvmbox", sys_mbox_valid(&conn->recvmbox), return ERR_CONN;);
+
+#if LWIP_TCP
+#if (LWIP_UDP || LWIP_RAW)
+ if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP)
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ {
+ struct pbuf *p = NULL;
+ /* This is not a listening netconn, since recvmbox is set */
+
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf == NULL) {
+ NETCONN_SET_SAFE_ERR(conn, ERR_MEM);
+ return ERR_MEM;
+ }
+
+ err = netconn_recv_data(conn, (void **)&p);
+ if (err != ERR_OK) {
+ memp_free(MEMP_NETBUF, buf);
+ return err;
+ }
+ LWIP_ASSERT("p != NULL", p != NULL);
+
+ buf->p = p;
+ buf->ptr = p;
+ buf->port = 0;
+ ipX_addr_set_any(LWIP_IPV6, &buf->addr);
+ *new_buf = buf;
+ /* don't set conn->last_err: it's only ERR_OK, anyway */
+ return ERR_OK;
+ }
+#endif /* LWIP_TCP */
+#if LWIP_TCP && (LWIP_UDP || LWIP_RAW)
+ else
+#endif /* LWIP_TCP && (LWIP_UDP || LWIP_RAW) */
+ {
+#if (LWIP_UDP || LWIP_RAW)
+ return netconn_recv_data(conn, (void **)new_buf);
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+}
+
+/**
+ * TCP: update the receive window: by calling this, the application
+ * tells the stack that it has processed data and is able to accept
+ * new data.
+ * ATTENTION: use with care, this is mainly used for sockets!
+ * Can only be used when calling netconn_set_noautorecved(conn, 1) before.
+ *
+ * @param conn the netconn for which to update the receive window
+ * @param length amount of data processed (ATTENTION: this must be accurate!)
+ */
+void
+netconn_recved(struct netconn *conn, u32_t length)
+{
+#if LWIP_TCP
+ if ((conn != NULL) && (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) &&
+ (netconn_get_noautorecved(conn))) {
+ struct api_msg msg;
+ /* Let the stack know that we have taken the data. */
+ /* TODO: Speedup: Don't block and wait for the answer here
+ (to prevent multiple thread-switches). */
+ msg.msg.conn = conn;
+ msg.msg.msg.r.len = length;
+ /* don't care for the return value of lwip_netconn_do_recv */
+ TCPIP_APIMSG_NOERR(&msg, lwip_netconn_do_recv);
+ }
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(conn);
+ LWIP_UNUSED_ARG(length);
+#endif /* LWIP_TCP */
+}
+
+/**
+ * Send data (in form of a netbuf) to a specific remote IP address and port.
+ * Only to be used for UDP and RAW netconns (not TCP).
+ *
+ * @param conn the netconn over which to send data
+ * @param buf a netbuf containing the data to send
+ * @param addr the remote IP address to which to send the data
+ * @param port the remote port to which to send the data
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_sendto(struct netconn *conn, struct netbuf *buf, ip_addr_t *addr, u16_t port)
+{
+ if (buf != NULL) {
+ ipX_addr_set_ipaddr(PCB_ISIPV6(conn->pcb.ip), &buf->addr, addr);
+ buf->port = port;
+ return netconn_send(conn, buf);
+ }
+ return ERR_VAL;
+}
+
+/**
+ * Send data over a UDP or RAW netconn (that is already connected).
+ *
+ * @param conn the UDP or RAW netconn over which to send data
+ * @param buf a netbuf containing the data to send
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_send(struct netconn *conn, struct netbuf *buf)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_send: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ LWIP_DEBUGF(API_LIB_DEBUG, ("netconn_send: sending %"U16_F" bytes\n", buf->p->tot_len));
+ msg.msg.conn = conn;
+ msg.msg.msg.b = buf;
+ TCPIP_APIMSG(&msg, lwip_netconn_do_send, err);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Send data over a TCP netconn.
+ *
+ * @param conn the TCP netconn over which to send data
+ * @param dataptr pointer to the application buffer that contains the data to send
+ * @param size size of the application data to send
+ * @param apiflags combination of following flags :
+ * - NETCONN_COPY: data will be copied into memory belonging to the stack
+ * - NETCONN_MORE: for TCP connection, PSH flag will be set on last segment sent
+ * - NETCONN_DONTBLOCK: only write the data if all dat can be written at once
+ * @param bytes_written pointer to a location that receives the number of written bytes
+ * @return ERR_OK if data was sent, any other err_t on error
+ */
+err_t
+netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
+ u8_t apiflags, size_t *bytes_written)
+{
+ struct api_msg msg;
+ err_t err;
+ u8_t dontblock;
+
+ LWIP_ERROR("netconn_write: invalid conn", (conn != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_write: invalid conn->type", (NETCONNTYPE_GROUP(conn->type)== NETCONN_TCP), return ERR_VAL;);
+ if (size == 0) {
+ return ERR_OK;
+ }
+ dontblock = netconn_is_nonblocking(conn) || (apiflags & NETCONN_DONTBLOCK);
+ if (dontblock && !bytes_written) {
+ /* This implies netconn_write() cannot be used for non-blocking send, since
+ it has no way to return the number of bytes written. */
+ return ERR_VAL;
+ }
+
+ /* non-blocking write sends as much */
+ msg.msg.conn = conn;
+ msg.msg.msg.w.dataptr = dataptr;
+ msg.msg.msg.w.apiflags = apiflags;
+ msg.msg.msg.w.len = size;
+#if LWIP_SO_SNDTIMEO
+ if (conn->send_timeout != 0) {
+ /* get the time we started, which is later compared to
+ sys_now() + conn->send_timeout */
+ msg.msg.msg.w.time_started = sys_now();
+ } else {
+ msg.msg.msg.w.time_started = 0;
+ }
+#endif /* LWIP_SO_SNDTIMEO */
+
+ /* For locking the core: this _can_ be delayed on low memory/low send buffer,
+ but if it is, this is done inside api_msg.c:do_write(), so we can use the
+ non-blocking version here. */
+ TCPIP_APIMSG(&msg, lwip_netconn_do_write, err);
+ if ((err == ERR_OK) && (bytes_written != NULL)) {
+ if (dontblock
+#if LWIP_SO_SNDTIMEO
+ || (conn->send_timeout != 0)
+#endif /* LWIP_SO_SNDTIMEO */
+ ) {
+ /* nonblocking write: maybe the data has been sent partly */
+ *bytes_written = msg.msg.msg.w.len;
+ } else {
+ /* blocking call succeeded: all data has been sent if it */
+ *bytes_written = size;
+ }
+ }
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Close ot shutdown a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to close or shutdown
+ * @param how fully close or only shutdown one side?
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+static err_t
+netconn_close_shutdown(struct netconn *conn, u8_t how)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_close: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.function = lwip_netconn_do_close;
+ msg.msg.conn = conn;
+ /* shutting down both ends is the same as closing */
+ msg.msg.msg.sd.shut = how;
+ /* because of the LWIP_TCPIP_CORE_LOCKING implementation of lwip_netconn_do_close,
+ don't use TCPIP_APIMSG here */
+ err = tcpip_apimsg(&msg);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+
+/**
+ * Close a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to close
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+err_t
+netconn_close(struct netconn *conn)
+{
+ /* shutting down both ends is the same as closing */
+ return netconn_close_shutdown(conn, NETCONN_SHUT_RDWR);
+}
+
+/**
+ * Shut down one or both sides of a TCP netconn (doesn't delete it).
+ *
+ * @param conn the TCP netconn to shut down
+ * @return ERR_OK if the netconn was closed, any other err_t on error
+ */
+err_t
+netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx)
+{
+ return netconn_close_shutdown(conn, (shut_rx ? NETCONN_SHUT_RD : 0) | (shut_tx ? NETCONN_SHUT_WR : 0));
+}
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+/**
+ * Join multicast groups for UDP netconns.
+ *
+ * @param conn the UDP netconn for which to change multicast addresses
+ * @param multiaddr IP address of the multicast group to join or leave
+ * @param netif_addr the IP address of the network interface on which to send
+ * the igmp message
+ * @param join_or_leave flag whether to send a join- or leave-message
+ * @return ERR_OK if the action was taken, any err_t on error
+ */
+err_t
+netconn_join_leave_group(struct netconn *conn,
+ ip_addr_t *multiaddr,
+ ip_addr_t *netif_addr,
+ enum netconn_igmp join_or_leave)
+{
+ struct api_msg msg;
+ err_t err;
+
+ LWIP_ERROR("netconn_join_leave_group: invalid conn", (conn != NULL), return ERR_ARG;);
+
+ msg.msg.conn = conn;
+ msg.msg.msg.jl.multiaddr = ip_2_ipX(multiaddr);
+ msg.msg.msg.jl.netif_addr = ip_2_ipX(netif_addr);
+ msg.msg.msg.jl.join_or_leave = join_or_leave;
+ TCPIP_APIMSG(&msg, lwip_netconn_do_join_leave_group, err);
+
+ NETCONN_SET_SAFE_ERR(conn, err);
+ return err;
+}
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+/**
+ * Execute a DNS query, only one IP address is returned
+ *
+ * @param name a string representation of the DNS host name to query
+ * @param addr a preallocated ip_addr_t where to store the resolved IP address
+ * @return ERR_OK: resolving succeeded
+ * ERR_MEM: memory error, try again later
+ * ERR_ARG: dns client not initialized or invalid hostname
+ * ERR_VAL: dns server response was invalid
+ */
+err_t
+netconn_gethostbyname(const char *name, ip_addr_t *addr)
+{
+ struct dns_api_msg msg;
+ err_t err;
+ sys_sem_t sem;
+
+ LWIP_ERROR("netconn_gethostbyname: invalid name", (name != NULL), return ERR_ARG;);
+ LWIP_ERROR("netconn_gethostbyname: invalid addr", (addr != NULL), return ERR_ARG;);
+
+ err = sys_sem_new(&sem, 0);
+ if (err != ERR_OK) {
+ return err;
+ }
+
+ msg.name = name;
+ msg.addr = addr;
+ msg.err = &err;
+ msg.sem = &sem;
+
+ tcpip_callback(lwip_netconn_do_gethostbyname, &msg);
+ sys_sem_wait(&sem);
+ sys_sem_free(&sem);
+
+ return err;
+}
+#endif /* LWIP_DNS*/
+
+#endif /* LWIP_NETCONN */
diff --git a/src/VBox/Devices/Network/lwip-new/src/api/api_msg.c b/src/VBox/Devices/Network/lwip-new/src/api/api_msg.c
new file mode 100644
index 00000000..02973389
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/api/api_msg.c
@@ -0,0 +1,1611 @@
+/**
+ * @file
+ * Sequential API Internal module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/api_msg.h"
+
+#include "lwip/ip.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/raw.h"
+
+#include "lwip/memp.h"
+#include "lwip/tcpip.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/mld6.h"
+
+#include <string.h>
+
+#define SET_NONBLOCKING_CONNECT(conn, val) do { if(val) { \
+ (conn)->flags |= NETCONN_FLAG_IN_NONBLOCKING_CONNECT; \
+} else { \
+ (conn)->flags &= ~ NETCONN_FLAG_IN_NONBLOCKING_CONNECT; }} while(0)
+#define IN_NONBLOCKING_CONNECT(conn) (((conn)->flags & NETCONN_FLAG_IN_NONBLOCKING_CONNECT) != 0)
+
+/* forward declarations */
+#if LWIP_TCP
+static err_t lwip_netconn_do_writemore(struct netconn *conn);
+static void lwip_netconn_do_close_internal(struct netconn *conn);
+#endif
+
+#if LWIP_RAW
+/**
+ * Receive callback function for RAW netconns.
+ * Doesn't 'eat' the packet, only references it and sends it to
+ * conn->recvmbox
+ *
+ * @see raw.h (struct raw_pcb.recv) for parameters and return value
+ */
+static u8_t
+recv_raw(void *arg, struct raw_pcb *pcb, struct pbuf *p,
+ ip_addr_t *addr)
+{
+ struct pbuf *q;
+ struct netbuf *buf;
+ struct netconn *conn;
+
+ LWIP_UNUSED_ARG(addr);
+ conn = (struct netconn *)arg;
+
+ if ((conn != NULL) && sys_mbox_valid(&conn->recvmbox)) {
+#if LWIP_SO_RCVBUF
+ int recv_avail;
+ SYS_ARCH_GET(conn->recv_avail, recv_avail);
+ if ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize) {
+ return 0;
+ }
+#endif /* LWIP_SO_RCVBUF */
+ /* copy the whole packet into new pbufs */
+ q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if(q != NULL) {
+ if (pbuf_copy(q, p) != ERR_OK) {
+ pbuf_free(q);
+ q = NULL;
+ }
+ }
+
+ if (q != NULL) {
+ u16_t len;
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf == NULL) {
+ pbuf_free(q);
+ return 0;
+ }
+
+ buf->p = q;
+ buf->ptr = q;
+ ipX_addr_copy(PCB_ISIPV6(pcb), buf->addr, *ipX_current_src_addr());
+ buf->port = pcb->protocol;
+
+ len = q->tot_len;
+ if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
+ netbuf_delete(buf);
+ return 0;
+ } else {
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+ }
+ }
+ }
+
+ return 0; /* do not eat the packet */
+}
+#endif /* LWIP_RAW*/
+
+#if LWIP_UDP
+/**
+ * Receive callback function for UDP netconns.
+ * Posts the packet to conn->recvmbox or deletes it on memory error.
+ *
+ * @see udp.h (struct udp_pcb.recv) for parameters
+ */
+static void
+recv_udp(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *addr, u16_t port)
+{
+ struct netbuf *buf;
+ struct netconn *conn;
+ u16_t len;
+#if LWIP_SO_RCVBUF
+ int recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+
+ LWIP_UNUSED_ARG(pcb); /* only used for asserts... */
+ LWIP_ASSERT("recv_udp must have a pcb argument", pcb != NULL);
+ LWIP_ASSERT("recv_udp must have an argument", arg != NULL);
+ conn = (struct netconn *)arg;
+ LWIP_ASSERT("recv_udp: recv for wrong pcb!", conn->pcb.udp == pcb);
+
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_GET(conn->recv_avail, recv_avail);
+ if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox) ||
+ ((recv_avail + (int)(p->tot_len)) > conn->recv_bufsize)) {
+#else /* LWIP_SO_RCVBUF */
+ if ((conn == NULL) || !sys_mbox_valid(&conn->recvmbox)) {
+#endif /* LWIP_SO_RCVBUF */
+ pbuf_free(p);
+ return;
+ }
+
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf == NULL) {
+ pbuf_free(p);
+ return;
+ } else {
+ buf->p = p;
+ buf->ptr = p;
+ ipX_addr_set_ipaddr(ip_current_is_v6(), &buf->addr, addr);
+ buf->port = port;
+#if LWIP_NETBUF_RECVINFO
+ {
+ /* get the UDP header - always in the first pbuf, ensured by udp_input */
+ const struct udp_hdr* udphdr = ipX_next_header_ptr();
+#if LWIP_CHECKSUM_ON_COPY
+ buf->flags = NETBUF_FLAG_DESTADDR;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ ipX_addr_set(ip_current_is_v6(), &buf->toaddr, ipX_current_dest_addr());
+ buf->toport_chksum = udphdr->dest;
+ }
+#endif /* LWIP_NETBUF_RECVINFO */
+ }
+
+ len = p->tot_len;
+ if (sys_mbox_trypost(&conn->recvmbox, buf) != ERR_OK) {
+ netbuf_delete(buf);
+ return;
+ } else {
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+ }
+}
+#endif /* LWIP_UDP */
+
+#if LWIP_TCP
+/**
+ * Receive callback function for TCP netconns.
+ * Posts the packet to conn->recvmbox, but doesn't delete it on errors.
+ *
+ * @see tcp.h (struct tcp_pcb.recv) for parameters and return value
+ */
+static err_t
+recv_tcp(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ struct netconn *conn;
+ u16_t len;
+
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_ASSERT("recv_tcp must have a pcb argument", pcb != NULL);
+ LWIP_ASSERT("recv_tcp must have an argument", arg != NULL);
+ conn = (struct netconn *)arg;
+
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+ LWIP_ASSERT("recv_tcp: recv for wrong pcb!", conn->pcb.tcp == pcb);
+
+ if (!sys_mbox_valid(&conn->recvmbox)) {
+ /* recvmbox already deleted */
+ if (p != NULL) {
+ tcp_recved(pcb, p->tot_len);
+ pbuf_free(p);
+ }
+ return ERR_OK;
+ }
+ /* Unlike for UDP or RAW pcbs, don't check for available space
+ using recv_avail since that could break the connection
+ (data is already ACKed) */
+
+ /* don't overwrite fatal errors! */
+ NETCONN_SET_SAFE_ERR(conn, err);
+
+ if (p != NULL) {
+ len = p->tot_len;
+ } else {
+ len = 0;
+ }
+
+ if (sys_mbox_trypost(&conn->recvmbox, p) != ERR_OK) {
+ /* don't deallocate p: it is presented to us later again from tcp_fasttmr! */
+ return ERR_MEM;
+ } else {
+#if LWIP_SO_RCVBUF
+ SYS_ARCH_INC(conn->recv_avail, len);
+#endif /* LWIP_SO_RCVBUF */
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, len);
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Poll callback function for TCP netconns.
+ * Wakes up an application thread that waits for a connection to close
+ * or data to be sent. The application thread then takes the
+ * appropriate action to go on.
+ *
+ * Signals the conn->sem.
+ * netconn_close waits for conn->sem if closing failed.
+ *
+ * @see tcp.h (struct tcp_pcb.poll) for parameters and return value
+ */
+static err_t
+poll_tcp(void *arg, struct tcp_pcb *pcb)
+{
+ struct netconn *conn = (struct netconn *)arg;
+
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+ if (conn->state == NETCONN_WRITE) {
+ lwip_netconn_do_writemore(conn);
+ } else if (conn->state == NETCONN_CLOSE) {
+ lwip_netconn_do_close_internal(conn);
+ }
+ /* @todo: implement connect timeout here? */
+
+ /* Did a nonblocking write fail before? Then check available write-space. */
+ if (conn->flags & NETCONN_FLAG_CHECK_WRITESPACE) {
+ /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+ let select mark this pcb as writable again. */
+ if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+ (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+ conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE;
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+ }
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Sent callback function for TCP netconns.
+ * Signals the conn->sem and calls API_EVENT.
+ * netconn_write waits for conn->sem if send buffer is low.
+ *
+ * @see tcp.h (struct tcp_pcb.sent) for parameters and return value
+ */
+static err_t
+sent_tcp(void *arg, struct tcp_pcb *pcb, u16_t len)
+{
+ struct netconn *conn = (struct netconn *)arg;
+
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+ if (conn->state == NETCONN_WRITE) {
+ lwip_netconn_do_writemore(conn);
+ } else if (conn->state == NETCONN_CLOSE) {
+ lwip_netconn_do_close_internal(conn);
+ }
+
+ if (conn) {
+ /* If the queued byte- or pbuf-count drops below the configured low-water limit,
+ let select mark this pcb as writable again. */
+ if ((conn->pcb.tcp != NULL) && (tcp_sndbuf(conn->pcb.tcp) > TCP_SNDLOWAT) &&
+ (tcp_sndqueuelen(conn->pcb.tcp) < TCP_SNDQUEUELOWAT)) {
+ conn->flags &= ~NETCONN_FLAG_CHECK_WRITESPACE;
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, len);
+ }
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Error callback function for TCP netconns.
+ * Signals conn->sem, posts to all conn mboxes and calls API_EVENT.
+ * The application thread has then to decide what to do.
+ *
+ * @see tcp.h (struct tcp_pcb.err) for parameters
+ */
+static void
+err_tcp(void *arg, err_t err)
+{
+ struct netconn *conn;
+ enum netconn_state old_state;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ conn = (struct netconn *)arg;
+ LWIP_ASSERT("conn != NULL", (conn != NULL));
+
+ conn->pcb.tcp = NULL;
+
+ /* no check since this is always fatal! */
+ SYS_ARCH_PROTECT(lev);
+ conn->last_err = err;
+ SYS_ARCH_UNPROTECT(lev);
+
+ /* reset conn->state now before waking up other threads */
+ old_state = conn->state;
+ conn->state = NETCONN_NONE;
+
+ /* Notify the user layer about a connection error. Used to signal
+ select. */
+ API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+ /* Try to release selects pending on 'read' or 'write', too.
+ They will get an error if they actually try to read or write. */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+ /* pass NULL-message to recvmbox to wake up pending recv */
+ if (sys_mbox_valid(&conn->recvmbox)) {
+ /* use trypost to prevent deadlock */
+ sys_mbox_trypost(&conn->recvmbox, NULL);
+ }
+ /* pass NULL-message to acceptmbox to wake up pending accept */
+ if (sys_mbox_valid(&conn->acceptmbox)) {
+ /* use trypost to preven deadlock */
+ sys_mbox_trypost(&conn->acceptmbox, NULL);
+ }
+
+ if ((old_state == NETCONN_WRITE) || (old_state == NETCONN_CLOSE) ||
+ (old_state == NETCONN_CONNECT)) {
+ /* calling lwip_netconn_do_writemore/lwip_netconn_do_close_internal is not necessary
+ since the pcb has already been deleted! */
+ int was_nonblocking_connect = IN_NONBLOCKING_CONNECT(conn);
+ SET_NONBLOCKING_CONNECT(conn, 0);
+
+ if (!was_nonblocking_connect) {
+ /* set error return code */
+ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+ conn->current_msg->err = err;
+ conn->current_msg = NULL;
+ /* wake up the waiting task */
+ sys_sem_signal(&conn->op_completed);
+ }
+ } else {
+ LWIP_ASSERT("conn->current_msg == NULL", conn->current_msg == NULL);
+ }
+}
+
+/**
+ * Setup a tcp_pcb with the correct callback function pointers
+ * and their arguments.
+ *
+ * @param conn the TCP netconn to setup
+ */
+static void
+setup_tcp(struct netconn *conn)
+{
+ struct tcp_pcb *pcb;
+
+ pcb = conn->pcb.tcp;
+ tcp_arg(pcb, conn);
+ tcp_recv(pcb, recv_tcp);
+ tcp_sent(pcb, sent_tcp);
+ tcp_poll(pcb, poll_tcp, 4);
+ tcp_err(pcb, err_tcp);
+}
+
+/**
+ * Accept callback function for TCP netconns.
+ * Allocates a new netconn and posts that to conn->acceptmbox.
+ *
+ * @see tcp.h (struct tcp_pcb_listen.accept) for parameters and return value
+ */
+static err_t
+accept_function(void *arg, struct tcp_pcb *newpcb, err_t err)
+{
+ struct netconn *newconn;
+ struct netconn *conn = (struct netconn *)arg;
+
+ LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: newpcb->tate: %s\n", tcp_debug_state_str(newpcb->state)));
+
+ if (!sys_mbox_valid(&conn->acceptmbox)) {
+ LWIP_DEBUGF(API_MSG_DEBUG, ("accept_function: acceptmbox already deleted\n"));
+ return ERR_VAL;
+ }
+
+ /* We have to set the callback here even though
+ * the new socket is unknown. conn->socket is marked as -1. */
+ newconn = netconn_alloc(conn->type, conn->callback);
+ if (newconn == NULL) {
+ return ERR_MEM;
+ }
+ newconn->pcb.tcp = newpcb;
+ setup_tcp(newconn);
+ /* no protection: when creating the pcb, the netconn is not yet known
+ to the application thread */
+ newconn->last_err = err;
+
+ if (sys_mbox_trypost(&conn->acceptmbox, newconn) != ERR_OK) {
+ /* When returning != ERR_OK, the pcb is aborted in tcp_process(),
+ so do nothing here! */
+ /* remove all references to this netconn from the pcb */
+ struct tcp_pcb* pcb = newconn->pcb.tcp;
+ tcp_arg(pcb, NULL);
+ tcp_recv(pcb, NULL);
+ tcp_sent(pcb, NULL);
+ tcp_poll(pcb, NULL, 4);
+ tcp_err(pcb, NULL);
+ /* remove reference from to the pcb from this netconn */
+ newconn->pcb.tcp = NULL;
+ /* no need to drain since we know the recvmbox is empty. */
+ sys_mbox_free(&newconn->recvmbox);
+ sys_mbox_set_invalid(&newconn->recvmbox);
+ netconn_free(newconn);
+ return ERR_MEM;
+ } else {
+ /* Register event with callback */
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ }
+
+ return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Create a new pcb of a specific type.
+ * Called from lwip_netconn_do_newconn().
+ *
+ * @param msg the api_msg_msg describing the connection type
+ * @return msg->conn->err, but the return value is currently ignored
+ */
+static void
+pcb_new(struct api_msg_msg *msg)
+{
+ LWIP_ASSERT("pcb_new: pcb already allocated", msg->conn->pcb.tcp == NULL);
+
+ /* Allocate a PCB for this connection */
+ switch(NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ msg->conn->pcb.raw = raw_new(msg->msg.n.proto);
+ if(msg->conn->pcb.raw != NULL) {
+ raw_recv(msg->conn->pcb.raw, recv_raw, msg->conn);
+ }
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->conn->pcb.udp = udp_new();
+ if(msg->conn->pcb.udp != NULL) {
+#if LWIP_UDPLITE
+ if (NETCONNTYPE_ISUDPLITE(msg->conn->type)) {
+ udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_UDPLITE);
+ }
+#endif /* LWIP_UDPLITE */
+ if (NETCONNTYPE_ISUDPNOCHKSUM(msg->conn->type)) {
+ udp_setflags(msg->conn->pcb.udp, UDP_FLAGS_NOCHKSUM);
+ }
+ udp_recv(msg->conn->pcb.udp, recv_udp, msg->conn);
+ }
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ msg->conn->pcb.tcp = tcp_new();
+ if(msg->conn->pcb.tcp != NULL) {
+ setup_tcp(msg->conn);
+ }
+ break;
+#endif /* LWIP_TCP */
+ default:
+ /* Unsupported netconn type, e.g. protocol disabled */
+ msg->err = ERR_VAL;
+ return;
+ }
+ if (msg->conn->pcb.ip == NULL) {
+ msg->err = ERR_MEM;
+ }
+#if LWIP_IPV6
+ else {
+ if (NETCONNTYPE_ISIPV6(msg->conn->type)) {
+ ip_set_v6(msg->conn->pcb.ip, 1);
+ }
+ }
+#endif /* LWIP_IPV6 */
+}
+
+/**
+ * Create a new pcb of a specific type inside a netconn.
+ * Called from netconn_new_with_proto_and_callback.
+ *
+ * @param msg the api_msg_msg describing the connection type
+ */
+void
+lwip_netconn_do_newconn(struct api_msg_msg *msg)
+{
+ msg->err = ERR_OK;
+ if(msg->conn->pcb.tcp == NULL) {
+ pcb_new(msg);
+ }
+ /* Else? This "new" connection already has a PCB allocated. */
+ /* Is this an error condition? Should it be deleted? */
+ /* We currently just are happy and return. */
+
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Create a new netconn (of a specific type) that has a callback function.
+ * The corresponding pcb is NOT created!
+ *
+ * @param t the type of 'connection' to create (@see enum netconn_type)
+ * @param proto the IP protocol for RAW IP pcbs
+ * @param callback a function to call on status changes (RX available, TX'ed)
+ * @return a newly allocated struct netconn or
+ * NULL on memory error
+ */
+struct netconn*
+netconn_alloc(enum netconn_type t, netconn_callback callback)
+{
+ struct netconn *conn;
+ int size;
+
+ conn = (struct netconn *)memp_malloc(MEMP_NETCONN);
+ if (conn == NULL) {
+ return NULL;
+ }
+
+ conn->last_err = ERR_OK;
+ conn->type = t;
+ conn->pcb.tcp = NULL;
+
+ /* If all sizes are the same, every compiler should optimize this switch to nothing, */
+ switch(NETCONNTYPE_GROUP(t)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ size = DEFAULT_RAW_RECVMBOX_SIZE;
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ size = DEFAULT_UDP_RECVMBOX_SIZE;
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ size = DEFAULT_TCP_RECVMBOX_SIZE;
+ break;
+#endif /* LWIP_TCP */
+ default:
+ LWIP_ASSERT("netconn_alloc: undefined netconn_type", 0);
+ goto free_and_return;
+ }
+
+ if (sys_sem_new(&conn->op_completed, 0) != ERR_OK) {
+ goto free_and_return;
+ }
+ if (sys_mbox_new(&conn->recvmbox, size) != ERR_OK) {
+ sys_sem_free(&conn->op_completed);
+ goto free_and_return;
+ }
+
+#if LWIP_TCP
+ sys_mbox_set_invalid(&conn->acceptmbox);
+#endif
+ conn->state = NETCONN_NONE;
+#if LWIP_SOCKET
+ /* initialize socket to -1 since 0 is a valid socket */
+ conn->socket = -1;
+#endif /* LWIP_SOCKET */
+ conn->callback = callback;
+#if LWIP_TCP
+ conn->current_msg = NULL;
+ conn->write_offset = 0;
+#endif /* LWIP_TCP */
+#if LWIP_SO_SNDTIMEO
+ conn->send_timeout = 0;
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+ conn->recv_timeout = 0;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ conn->recv_bufsize = RECV_BUFSIZE_DEFAULT;
+ conn->recv_avail = 0;
+#endif /* LWIP_SO_RCVBUF */
+ conn->flags = 0;
+ return conn;
+free_and_return:
+ memp_free(MEMP_NETCONN, conn);
+ return NULL;
+}
+
+/**
+ * Delete a netconn and all its resources.
+ * The pcb is NOT freed (since we might not be in the right thread context do this).
+ *
+ * @param conn the netconn to free
+ */
+void
+netconn_free(struct netconn *conn)
+{
+ LWIP_ASSERT("PCB must be deallocated outside this function", conn->pcb.tcp == NULL);
+ LWIP_ASSERT("recvmbox must be deallocated before calling this function",
+ !sys_mbox_valid(&conn->recvmbox));
+#if LWIP_TCP
+ LWIP_ASSERT("acceptmbox must be deallocated before calling this function",
+ !sys_mbox_valid(&conn->acceptmbox));
+#endif /* LWIP_TCP */
+
+ sys_sem_free(&conn->op_completed);
+ sys_sem_set_invalid(&conn->op_completed);
+
+ memp_free(MEMP_NETCONN, conn);
+}
+
+/**
+ * Delete rcvmbox and acceptmbox of a netconn and free the left-over data in
+ * these mboxes
+ *
+ * @param conn the netconn to free
+ * @bytes_drained bytes drained from recvmbox
+ * @accepts_drained pending connections drained from acceptmbox
+ */
+static void
+netconn_drain(struct netconn *conn)
+{
+ void *mem;
+#if LWIP_TCP
+ struct pbuf *p;
+#endif /* LWIP_TCP */
+
+ /* This runs in tcpip_thread, so we don't need to lock against rx packets */
+
+ /* Delete and drain the recvmbox. */
+ if (sys_mbox_valid(&conn->recvmbox)) {
+ while (sys_mbox_tryfetch(&conn->recvmbox, &mem) != SYS_MBOX_EMPTY) {
+#if LWIP_TCP
+ if (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) {
+ if(mem != NULL) {
+ p = (struct pbuf*)mem;
+ /* pcb might be set to NULL already by err_tcp() */
+ if (conn->pcb.tcp != NULL) {
+ tcp_recved(conn->pcb.tcp, p->tot_len);
+ }
+ pbuf_free(p);
+ }
+ } else
+#endif /* LWIP_TCP */
+ {
+ netbuf_delete((struct netbuf *)mem);
+ }
+ }
+ sys_mbox_free(&conn->recvmbox);
+ sys_mbox_set_invalid(&conn->recvmbox);
+ }
+
+ /* Delete and drain the acceptmbox. */
+#if LWIP_TCP
+ if (sys_mbox_valid(&conn->acceptmbox)) {
+ while (sys_mbox_tryfetch(&conn->acceptmbox, &mem) != SYS_MBOX_EMPTY) {
+ struct netconn *newconn = (struct netconn *)mem;
+ /* Only tcp pcbs have an acceptmbox, so no need to check conn->type */
+ /* pcb might be set to NULL already by err_tcp() */
+ if (conn->pcb.tcp != NULL) {
+ tcp_accepted(conn->pcb.tcp);
+ }
+ /* drain recvmbox */
+ netconn_drain(newconn);
+ if (newconn->pcb.tcp != NULL) {
+ tcp_abort(newconn->pcb.tcp);
+ newconn->pcb.tcp = NULL;
+ }
+ netconn_free(newconn);
+ }
+ sys_mbox_free(&conn->acceptmbox);
+ sys_mbox_set_invalid(&conn->acceptmbox);
+ }
+#endif /* LWIP_TCP */
+}
+
+#if LWIP_TCP
+/**
+ * Internal helper function to close a TCP netconn: since this sometimes
+ * doesn't work at the first attempt, this function is called from multiple
+ * places.
+ *
+ * @param conn the TCP netconn to close
+ */
+static void
+lwip_netconn_do_close_internal(struct netconn *conn)
+{
+ err_t err;
+ u8_t shut, shut_rx, shut_tx, close;
+
+ LWIP_ASSERT("invalid conn", (conn != NULL));
+ LWIP_ASSERT("this is for tcp netconns only", (NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP));
+ LWIP_ASSERT("conn must be in state NETCONN_CLOSE", (conn->state == NETCONN_CLOSE));
+ LWIP_ASSERT("pcb already closed", (conn->pcb.tcp != NULL));
+ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+
+ shut = conn->current_msg->msg.sd.shut;
+ shut_rx = shut & NETCONN_SHUT_RD;
+ shut_tx = shut & NETCONN_SHUT_WR;
+ /* shutting down both ends is the same as closing */
+ close = shut == NETCONN_SHUT_RDWR;
+
+ /* Set back some callback pointers */
+ if (close) {
+ tcp_arg(conn->pcb.tcp, NULL);
+ }
+ if (conn->pcb.tcp->state == LISTEN) {
+ tcp_accept(conn->pcb.tcp, NULL);
+ } else {
+ /* some callbacks have to be reset if tcp_close is not successful */
+ if (shut_rx) {
+ tcp_recv(conn->pcb.tcp, NULL);
+ tcp_accept(conn->pcb.tcp, NULL);
+ }
+ if (shut_tx) {
+ tcp_sent(conn->pcb.tcp, NULL);
+ }
+ if (close) {
+ tcp_poll(conn->pcb.tcp, NULL, 4);
+ tcp_err(conn->pcb.tcp, NULL);
+ }
+ }
+ /* Try to close the connection */
+ if (close) {
+ err = tcp_close(conn->pcb.tcp);
+ } else {
+ err = tcp_shutdown(conn->pcb.tcp, shut_rx, shut_tx);
+ }
+ if (err == ERR_OK) {
+ /* Closing succeeded */
+ conn->current_msg->err = ERR_OK;
+ conn->current_msg = NULL;
+ conn->state = NETCONN_NONE;
+ if (close) {
+ /* Set back some callback pointers as conn is going away */
+ conn->pcb.tcp = NULL;
+ /* Trigger select() in socket layer. Make sure everybody notices activity
+ on the connection, error first! */
+ API_EVENT(conn, NETCONN_EVT_ERROR, 0);
+ }
+ if (shut_rx) {
+ API_EVENT(conn, NETCONN_EVT_RCVPLUS, 0);
+ }
+ if (shut_tx) {
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+ }
+ /* wake up the application task */
+ sys_sem_signal(&conn->op_completed);
+ } else {
+ /* Closing failed, restore some of the callbacks */
+ /* Closing of listen pcb will never fail! */
+ LWIP_ASSERT("Closing a listen pcb may not fail!", (conn->pcb.tcp->state != LISTEN));
+ tcp_sent(conn->pcb.tcp, sent_tcp);
+ tcp_poll(conn->pcb.tcp, poll_tcp, 4);
+ tcp_err(conn->pcb.tcp, err_tcp);
+ tcp_arg(conn->pcb.tcp, conn);
+ /* don't restore recv callback: we don't want to receive any more data */
+ }
+ /* If closing didn't succeed, we get called again either
+ from poll_tcp or from sent_tcp */
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Delete the pcb inside a netconn.
+ * Called from netconn_delete.
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_delconn(struct api_msg_msg *msg)
+{
+ /* @todo TCP: abort running write/connect? */
+ if ((msg->conn->state != NETCONN_NONE) &&
+ (msg->conn->state != NETCONN_LISTEN) &&
+ (msg->conn->state != NETCONN_CONNECT)) {
+ /* this only happens for TCP netconns */
+ LWIP_ASSERT("NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP",
+ NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP);
+ msg->err = ERR_INPROGRESS;
+ } else {
+ LWIP_ASSERT("blocking connect in progress",
+ (msg->conn->state != NETCONN_CONNECT) || IN_NONBLOCKING_CONNECT(msg->conn));
+ /* Drain and delete mboxes */
+ netconn_drain(msg->conn);
+
+ if (msg->conn->pcb.tcp != NULL) {
+
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ raw_remove(msg->conn->pcb.raw);
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->conn->pcb.udp->recv_arg = NULL;
+ udp_remove(msg->conn->pcb.udp);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+ msg->conn->write_offset == 0);
+ msg->conn->state = NETCONN_CLOSE;
+ msg->msg.sd.shut = NETCONN_SHUT_RDWR;
+ msg->conn->current_msg = msg;
+ lwip_netconn_do_close_internal(msg->conn);
+ /* API_EVENT is called inside lwip_netconn_do_close_internal, before releasing
+ the application thread, so we can return at this point! */
+ return;
+#endif /* LWIP_TCP */
+ default:
+ break;
+ }
+ msg->conn->pcb.tcp = NULL;
+ }
+ /* tcp netconns don't come here! */
+
+ /* @todo: this lets select make the socket readable and writable,
+ which is wrong! errfd instead? */
+ API_EVENT(msg->conn, NETCONN_EVT_RCVPLUS, 0);
+ API_EVENT(msg->conn, NETCONN_EVT_SENDPLUS, 0);
+ }
+ if (sys_sem_valid(&msg->conn->op_completed)) {
+ sys_sem_signal(&msg->conn->op_completed);
+ }
+}
+
+/**
+ * Bind a pcb contained in a netconn
+ * Called from netconn_bind.
+ *
+ * @param msg the api_msg_msg pointing to the connection and containing
+ * the IP address and port to bind to
+ */
+void
+lwip_netconn_do_bind(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ msg->err = ERR_VAL;
+ if (msg->conn->pcb.tcp != NULL) {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ msg->err = raw_bind(msg->conn->pcb.raw, msg->msg.bc.ipaddr);
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->err = udp_bind(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ msg->err = tcp_bind(msg->conn->pcb.tcp, msg->msg.bc.ipaddr, msg->msg.bc.port);
+ break;
+#endif /* LWIP_TCP */
+ default:
+ break;
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * TCP callback function if a connection (opened by tcp_connect/lwip_netconn_do_connect) has
+ * been established (or reset by the remote host).
+ *
+ * @see tcp.h (struct tcp_pcb.connected) for parameters and return values
+ */
+static err_t
+lwip_netconn_do_connected(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+ struct netconn *conn;
+ int was_blocking;
+
+ LWIP_UNUSED_ARG(pcb);
+
+ conn = (struct netconn *)arg;
+
+ if (conn == NULL) {
+ return ERR_VAL;
+ }
+
+ LWIP_ASSERT("conn->state == NETCONN_CONNECT", conn->state == NETCONN_CONNECT);
+ LWIP_ASSERT("(conn->current_msg != NULL) || conn->in_non_blocking_connect",
+ (conn->current_msg != NULL) || IN_NONBLOCKING_CONNECT(conn));
+
+ if (conn->current_msg != NULL) {
+ conn->current_msg->err = err;
+ }
+ if ((NETCONNTYPE_GROUP(conn->type) == NETCONN_TCP) && (err == ERR_OK)) {
+ setup_tcp(conn);
+ }
+ was_blocking = !IN_NONBLOCKING_CONNECT(conn);
+ SET_NONBLOCKING_CONNECT(conn, 0);
+ conn->current_msg = NULL;
+ conn->state = NETCONN_NONE;
+ if (!was_blocking) {
+ NETCONN_SET_SAFE_ERR(conn, ERR_OK);
+ }
+ API_EVENT(conn, NETCONN_EVT_SENDPLUS, 0);
+
+ if (was_blocking) {
+ sys_sem_signal(&conn->op_completed);
+ }
+ return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Connect a pcb contained inside a netconn
+ * Called from netconn_connect.
+ *
+ * @param msg the api_msg_msg pointing to the connection and containing
+ * the IP address and port to connect to
+ */
+void
+lwip_netconn_do_connect(struct api_msg_msg *msg)
+{
+ if (msg->conn->pcb.tcp == NULL) {
+ /* This may happen when calling netconn_connect() a second time */
+ msg->err = ERR_CLSD;
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+ /* For TCP, netconn_connect() calls tcpip_apimsg(), so signal op_completed here. */
+ sys_sem_signal(&msg->conn->op_completed);
+ return;
+ }
+ } else {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ msg->err = raw_connect(msg->conn->pcb.raw, msg->msg.bc.ipaddr);
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ msg->err = udp_connect(msg->conn->pcb.udp, msg->msg.bc.ipaddr, msg->msg.bc.port);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ /* Prevent connect while doing any other action. */
+ if (msg->conn->state != NETCONN_NONE) {
+ msg->err = ERR_ISCONN;
+ } else {
+ setup_tcp(msg->conn);
+ msg->err = tcp_connect(msg->conn->pcb.tcp, msg->msg.bc.ipaddr,
+ msg->msg.bc.port, lwip_netconn_do_connected);
+ if (msg->err == ERR_OK) {
+ u8_t non_blocking = netconn_is_nonblocking(msg->conn);
+ msg->conn->state = NETCONN_CONNECT;
+ SET_NONBLOCKING_CONNECT(msg->conn, non_blocking);
+ if (non_blocking) {
+ msg->err = ERR_INPROGRESS;
+ } else {
+ msg->conn->current_msg = msg;
+ /* sys_sem_signal() is called from lwip_netconn_do_connected (or err_tcp()),
+ * when the connection is established! */
+ return;
+ }
+ }
+ }
+ /* For TCP, netconn_connect() calls tcpip_apimsg(), so signal op_completed here. */
+ sys_sem_signal(&msg->conn->op_completed);
+ return;
+#endif /* LWIP_TCP */
+ default:
+ LWIP_ERROR("Invalid netconn type", 0, do{ msg->err = ERR_VAL; }while(0));
+ break;
+ }
+ }
+ /* For all other protocols, netconn_connect() calls TCPIP_APIMSG(),
+ so use TCPIP_APIMSG_ACK() here. */
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Connect a pcb contained inside a netconn
+ * Only used for UDP netconns.
+ * Called from netconn_disconnect.
+ *
+ * @param msg the api_msg_msg pointing to the connection to disconnect
+ */
+void
+lwip_netconn_do_disconnect(struct api_msg_msg *msg)
+{
+#if LWIP_UDP
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+ udp_disconnect(msg->conn->pcb.udp);
+ msg->err = ERR_OK;
+ } else
+#endif /* LWIP_UDP */
+ {
+ msg->err = ERR_VAL;
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Set a TCP pcb contained in a netconn into listen mode
+ * Called from netconn_listen.
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_listen(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ msg->err = ERR_CONN;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+ if (msg->conn->state == NETCONN_NONE) {
+ struct tcp_pcb* lpcb;
+#if LWIP_IPV6
+ if ((msg->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) == 0) {
+#if TCP_LISTEN_BACKLOG
+ lpcb = tcp_listen_dual_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog);
+#else /* TCP_LISTEN_BACKLOG */
+ lpcb = tcp_listen_dual(msg->conn->pcb.tcp);
+#endif /* TCP_LISTEN_BACKLOG */
+ } else
+#endif /* LWIP_IPV6 */
+ {
+#if TCP_LISTEN_BACKLOG
+ lpcb = tcp_listen_with_backlog(msg->conn->pcb.tcp, msg->msg.lb.backlog);
+#else /* TCP_LISTEN_BACKLOG */
+ lpcb = tcp_listen(msg->conn->pcb.tcp);
+#endif /* TCP_LISTEN_BACKLOG */
+ }
+ if (lpcb == NULL) {
+ /* in this case, the old pcb is still allocated */
+ msg->err = ERR_MEM;
+ } else {
+ /* delete the recvmbox and allocate the acceptmbox */
+ if (sys_mbox_valid(&msg->conn->recvmbox)) {
+ /** @todo: should we drain the recvmbox here? */
+ sys_mbox_free(&msg->conn->recvmbox);
+ sys_mbox_set_invalid(&msg->conn->recvmbox);
+ }
+ msg->err = ERR_OK;
+ if (!sys_mbox_valid(&msg->conn->acceptmbox)) {
+ msg->err = sys_mbox_new(&msg->conn->acceptmbox, DEFAULT_ACCEPTMBOX_SIZE);
+ }
+ if (msg->err == ERR_OK) {
+ msg->conn->state = NETCONN_LISTEN;
+ msg->conn->pcb.tcp = lpcb;
+ tcp_arg(msg->conn->pcb.tcp, msg->conn);
+ tcp_accept(msg->conn->pcb.tcp, accept_function);
+ } else {
+ /* since the old pcb is already deallocated, free lpcb now */
+ tcp_close(lpcb);
+ msg->conn->pcb.tcp = NULL;
+ }
+ }
+ }
+ } else {
+ msg->err = ERR_ARG;
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a RAW or UDP pcb contained in a netconn
+ * Called from netconn_send
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_send(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ msg->err = ERR_CONN;
+ if (msg->conn->pcb.tcp != NULL) {
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &msg->msg.b->addr)) {
+ msg->err = raw_send(msg->conn->pcb.raw, msg->msg.b->p);
+ } else {
+ msg->err = raw_sendto(msg->conn->pcb.raw, msg->msg.b->p, ipX_2_ip(&msg->msg.b->addr));
+ }
+ break;
+#endif
+#if LWIP_UDP
+ case NETCONN_UDP:
+#if LWIP_CHECKSUM_ON_COPY
+ if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &msg->msg.b->addr)) {
+ msg->err = udp_send_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+ msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+ } else {
+ msg->err = udp_sendto_chksum(msg->conn->pcb.udp, msg->msg.b->p,
+ ipX_2_ip(&msg->msg.b->addr), msg->msg.b->port,
+ msg->msg.b->flags & NETBUF_FLAG_CHKSUM, msg->msg.b->toport_chksum);
+ }
+#else /* LWIP_CHECKSUM_ON_COPY */
+ if (ipX_addr_isany(PCB_ISIPV6(msg->conn->pcb.ip), &msg->msg.b->addr)) {
+ msg->err = udp_send(msg->conn->pcb.udp, msg->msg.b->p);
+ } else {
+ msg->err = udp_sendto(msg->conn->pcb.udp, msg->msg.b->p, ipX_2_ip(&msg->msg.b->addr), msg->msg.b->port);
+ }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ break;
+#endif /* LWIP_UDP */
+ default:
+ break;
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+#if LWIP_TCP
+/**
+ * Indicate data has been received from a TCP pcb contained in a netconn
+ * Called from netconn_recv
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_recv(struct api_msg_msg *msg)
+{
+ msg->err = ERR_OK;
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+#if TCP_LISTEN_BACKLOG
+ if (msg->conn->pcb.tcp->state == LISTEN) {
+ tcp_accepted(msg->conn->pcb.tcp);
+ } else
+#endif /* TCP_LISTEN_BACKLOG */
+ {
+ u32_t remaining = msg->msg.r.len;
+ do {
+ u16_t recved = (remaining > 0xffff) ? 0xffff : (u16_t)remaining;
+ tcp_recved(msg->conn->pcb.tcp, recved);
+ remaining -= recved;
+ }while(remaining != 0);
+ }
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * See if more data needs to be written from a previous call to netconn_write.
+ * Called initially from lwip_netconn_do_write. If the first call can't send all data
+ * (because of low memory or empty send-buffer), this function is called again
+ * from sent_tcp() or poll_tcp() to send more data. If all data is sent, the
+ * blocking application thread (waiting in netconn_write) is released.
+ *
+ * @param conn netconn (that is currently in state NETCONN_WRITE) to process
+ * @return ERR_OK
+ * ERR_MEM if LWIP_TCPIP_CORE_LOCKING=1 and sending hasn't yet finished
+ */
+static err_t
+lwip_netconn_do_writemore(struct netconn *conn)
+{
+ err_t err;
+ void *dataptr;
+ u16_t len, available;
+ u8_t write_finished = 0;
+ size_t diff;
+ u8_t dontblock = netconn_is_nonblocking(conn) ||
+ (conn->current_msg->msg.w.apiflags & NETCONN_DONTBLOCK);
+ u8_t apiflags = conn->current_msg->msg.w.apiflags;
+
+ LWIP_ASSERT("conn != NULL", conn != NULL);
+ LWIP_ASSERT("conn->state == NETCONN_WRITE", (conn->state == NETCONN_WRITE));
+ LWIP_ASSERT("conn->current_msg != NULL", conn->current_msg != NULL);
+ LWIP_ASSERT("conn->pcb.tcp != NULL", conn->pcb.tcp != NULL);
+ LWIP_ASSERT("conn->write_offset < conn->current_msg->msg.w.len",
+ conn->write_offset < conn->current_msg->msg.w.len);
+
+#if LWIP_SO_SNDTIMEO
+ if ((conn->send_timeout != 0) &&
+ ((s32_t)(sys_now() - conn->current_msg->msg.w.time_started) >= conn->send_timeout)) {
+ write_finished = 1;
+ if (conn->write_offset == 0) {
+ /* nothing has been written */
+ err = ERR_WOULDBLOCK;
+ conn->current_msg->msg.w.len = 0;
+ } else {
+ /* partial write */
+ err = ERR_OK;
+ conn->current_msg->msg.w.len = conn->write_offset;
+ }
+ } else
+#endif /* LWIP_SO_SNDTIMEO */
+ {
+ dataptr = (u8_t*)conn->current_msg->msg.w.dataptr + conn->write_offset;
+ diff = conn->current_msg->msg.w.len - conn->write_offset;
+ if (diff > 0xffffUL) { /* max_u16_t */
+ len = 0xffff;
+#if LWIP_TCPIP_CORE_LOCKING
+ conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
+#endif
+ apiflags |= TCP_WRITE_FLAG_MORE;
+ } else {
+ len = (u16_t)diff;
+ }
+ available = tcp_sndbuf(conn->pcb.tcp);
+ if (available < len) {
+ /* don't try to write more than sendbuf */
+ len = available;
+ if (dontblock){
+ if (!len) {
+ err = ERR_WOULDBLOCK;
+ goto err_mem;
+ }
+ } else {
+#if LWIP_TCPIP_CORE_LOCKING
+ conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
+#endif
+ apiflags |= TCP_WRITE_FLAG_MORE;
+ }
+ }
+ LWIP_ASSERT("lwip_netconn_do_writemore: invalid length!", ((conn->write_offset + len) <= conn->current_msg->msg.w.len));
+ err = tcp_write(conn->pcb.tcp, dataptr, len, apiflags);
+ /* if OK or memory error, check available space */
+ if ((err == ERR_OK) || (err == ERR_MEM)) {
+err_mem:
+ if (dontblock && (len < conn->current_msg->msg.w.len)) {
+ /* non-blocking write did not write everything: mark the pcb non-writable
+ and let poll_tcp check writable space to mark the pcb writable again */
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+ conn->flags |= NETCONN_FLAG_CHECK_WRITESPACE;
+ } else if ((tcp_sndbuf(conn->pcb.tcp) <= TCP_SNDLOWAT) ||
+ (tcp_sndqueuelen(conn->pcb.tcp) >= TCP_SNDQUEUELOWAT)) {
+ /* The queued byte- or pbuf-count exceeds the configured low-water limit,
+ let select mark this pcb as non-writable. */
+ API_EVENT(conn, NETCONN_EVT_SENDMINUS, len);
+ }
+ }
+
+ if (err == ERR_OK) {
+ conn->write_offset += len;
+ if ((conn->write_offset == conn->current_msg->msg.w.len) || dontblock) {
+ /* return sent length */
+ conn->current_msg->msg.w.len = conn->write_offset;
+ /* everything was written */
+ write_finished = 1;
+ conn->write_offset = 0;
+ }
+ tcp_output(conn->pcb.tcp);
+ } else if ((err == ERR_MEM) && !dontblock) {
+ /* If ERR_MEM, we wait for sent_tcp or poll_tcp to be called
+ we do NOT return to the application thread, since ERR_MEM is
+ only a temporary error! */
+
+ /* tcp_write returned ERR_MEM, try tcp_output anyway */
+ tcp_output(conn->pcb.tcp);
+
+#if LWIP_TCPIP_CORE_LOCKING
+ conn->flags |= NETCONN_FLAG_WRITE_DELAYED;
+#endif
+ } else {
+ /* On errors != ERR_MEM, we don't try writing any more but return
+ the error to the application thread. */
+ write_finished = 1;
+ conn->current_msg->msg.w.len = 0;
+ }
+ }
+ if (write_finished) {
+ /* everything was written: set back connection state
+ and back to application task */
+ conn->current_msg->err = err;
+ conn->current_msg = NULL;
+ conn->state = NETCONN_NONE;
+#if LWIP_TCPIP_CORE_LOCKING
+ if ((conn->flags & NETCONN_FLAG_WRITE_DELAYED) != 0)
+#endif
+ {
+ sys_sem_signal(&conn->op_completed);
+ }
+ }
+#if LWIP_TCPIP_CORE_LOCKING
+ else
+ return ERR_MEM;
+#endif
+ return ERR_OK;
+}
+#endif /* LWIP_TCP */
+
+/**
+ * Send some data on a TCP pcb contained in a netconn
+ * Called from netconn_write
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_write(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP) {
+#if LWIP_TCP
+ if (msg->conn->state != NETCONN_NONE) {
+ /* netconn is connecting, closing or in blocking write */
+ msg->err = ERR_INPROGRESS;
+ } else if (msg->conn->pcb.tcp != NULL) {
+ msg->conn->state = NETCONN_WRITE;
+ /* set all the variables used by lwip_netconn_do_writemore */
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+ msg->conn->write_offset == 0);
+ LWIP_ASSERT("msg->msg.w.len != 0", msg->msg.w.len != 0);
+ msg->conn->current_msg = msg;
+ msg->conn->write_offset = 0;
+#if LWIP_TCPIP_CORE_LOCKING
+ msg->conn->flags &= ~NETCONN_FLAG_WRITE_DELAYED;
+ if (lwip_netconn_do_writemore(msg->conn) != ERR_OK) {
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_WRITE);
+ UNLOCK_TCPIP_CORE();
+ sys_arch_sem_wait(&msg->conn->op_completed, 0);
+ LOCK_TCPIP_CORE();
+ LWIP_ASSERT("state!", msg->conn->state == NETCONN_NONE);
+ }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ lwip_netconn_do_writemore(msg->conn);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+ /* for both cases: if lwip_netconn_do_writemore was called, don't ACK the APIMSG
+ since lwip_netconn_do_writemore ACKs it! */
+ return;
+ } else {
+ msg->err = ERR_CONN;
+ }
+#else /* LWIP_TCP */
+ msg->err = ERR_VAL;
+#endif /* LWIP_TCP */
+#if (LWIP_UDP || LWIP_RAW)
+ } else {
+ msg->err = ERR_VAL;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Return a connection's local or remote address
+ * Called from netconn_getaddr
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_getaddr(struct api_msg_msg *msg)
+{
+ if (msg->conn->pcb.ip != NULL) {
+ if (msg->msg.ad.local) {
+ ipX_addr_copy(PCB_ISIPV6(msg->conn->pcb.ip), *(msg->msg.ad.ipaddr),
+ msg->conn->pcb.ip->local_ip);
+ } else {
+ ipX_addr_copy(PCB_ISIPV6(msg->conn->pcb.ip), *(msg->msg.ad.ipaddr),
+ msg->conn->pcb.ip->remote_ip);
+ }
+ msg->err = ERR_OK;
+ switch (NETCONNTYPE_GROUP(msg->conn->type)) {
+#if LWIP_RAW
+ case NETCONN_RAW:
+ if (msg->msg.ad.local) {
+ *(msg->msg.ad.port) = msg->conn->pcb.raw->protocol;
+ } else {
+ /* return an error as connecting is only a helper for upper layers */
+ msg->err = ERR_CONN;
+ }
+ break;
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ case NETCONN_UDP:
+ if (msg->msg.ad.local) {
+ *(msg->msg.ad.port) = msg->conn->pcb.udp->local_port;
+ } else {
+ if ((msg->conn->pcb.udp->flags & UDP_FLAGS_CONNECTED) == 0) {
+ msg->err = ERR_CONN;
+ } else {
+ *(msg->msg.ad.port) = msg->conn->pcb.udp->remote_port;
+ }
+ }
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case NETCONN_TCP:
+ *(msg->msg.ad.port) = (msg->msg.ad.local?msg->conn->pcb.tcp->local_port:msg->conn->pcb.tcp->remote_port);
+ break;
+#endif /* LWIP_TCP */
+ default:
+ LWIP_ASSERT("invalid netconn_type", 0);
+ break;
+ }
+ } else {
+ msg->err = ERR_CONN;
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+
+/**
+ * Close a TCP pcb contained in a netconn
+ * Called from netconn_close
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_close(struct api_msg_msg *msg)
+{
+#if LWIP_TCP
+ /* @todo: abort running write/connect? */
+ if ((msg->conn->state != NETCONN_NONE) && (msg->conn->state != NETCONN_LISTEN)) {
+ /* this only happens for TCP netconns */
+ LWIP_ASSERT("NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP",
+ NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP);
+ msg->err = ERR_INPROGRESS;
+ } else if ((msg->conn->pcb.tcp != NULL) && (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_TCP)) {
+ if ((msg->msg.sd.shut != NETCONN_SHUT_RDWR) && (msg->conn->state == NETCONN_LISTEN)) {
+ /* LISTEN doesn't support half shutdown */
+ msg->err = ERR_CONN;
+ } else {
+ if (msg->msg.sd.shut & NETCONN_SHUT_RD) {
+ /* Drain and delete mboxes */
+ netconn_drain(msg->conn);
+ }
+ LWIP_ASSERT("already writing or closing", msg->conn->current_msg == NULL &&
+ msg->conn->write_offset == 0);
+ msg->conn->state = NETCONN_CLOSE;
+ msg->conn->current_msg = msg;
+ lwip_netconn_do_close_internal(msg->conn);
+ /* for tcp netconns, lwip_netconn_do_close_internal ACKs the message */
+ return;
+ }
+ } else
+#endif /* LWIP_TCP */
+ {
+ msg->err = ERR_VAL;
+ }
+ sys_sem_signal(&msg->conn->op_completed);
+}
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+/**
+ * Join multicast groups for UDP netconns.
+ * Called from netconn_join_leave_group
+ *
+ * @param msg the api_msg_msg pointing to the connection
+ */
+void
+lwip_netconn_do_join_leave_group(struct api_msg_msg *msg)
+{
+ if (ERR_IS_FATAL(msg->conn->last_err)) {
+ msg->err = msg->conn->last_err;
+ } else {
+ if (msg->conn->pcb.tcp != NULL) {
+ if (NETCONNTYPE_GROUP(msg->conn->type) == NETCONN_UDP) {
+#if LWIP_UDP
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+ if (PCB_ISIPV6(msg->conn->pcb.udp)) {
+ if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+ msg->err = mld6_joingroup(ipX_2_ip6(msg->msg.jl.netif_addr),
+ ipX_2_ip6(msg->msg.jl.multiaddr));
+ } else {
+ msg->err = mld6_leavegroup(ipX_2_ip6(msg->msg.jl.netif_addr),
+ ipX_2_ip6(msg->msg.jl.multiaddr));
+ }
+ }
+ else
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+ {
+#if LWIP_IGMP
+ if (msg->msg.jl.join_or_leave == NETCONN_JOIN) {
+ msg->err = igmp_joingroup(ipX_2_ip(msg->msg.jl.netif_addr),
+ ipX_2_ip(msg->msg.jl.multiaddr));
+ } else {
+ msg->err = igmp_leavegroup(ipX_2_ip(msg->msg.jl.netif_addr),
+ ipX_2_ip(msg->msg.jl.multiaddr));
+ }
+#endif /* LWIP_IGMP */
+ }
+#endif /* LWIP_UDP */
+#if (LWIP_TCP || LWIP_RAW)
+ } else {
+ msg->err = ERR_VAL;
+#endif /* (LWIP_TCP || LWIP_RAW) */
+ }
+ } else {
+ msg->err = ERR_CONN;
+ }
+ }
+ TCPIP_APIMSG_ACK(msg);
+}
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+/**
+ * Callback function that is called when DNS name is resolved
+ * (or on timeout). A waiting application thread is waked up by
+ * signaling the semaphore.
+ */
+static void
+lwip_netconn_do_dns_found(const char *name, ip_addr_t *ipaddr, void *arg)
+{
+ struct dns_api_msg *msg = (struct dns_api_msg*)arg;
+
+ LWIP_ASSERT("DNS response for wrong host name", strcmp(msg->name, name) == 0);
+ LWIP_UNUSED_ARG(name);
+
+ if (ipaddr == NULL) {
+ /* timeout or memory error */
+ *msg->err = ERR_VAL;
+ } else {
+ /* address was resolved */
+ *msg->err = ERR_OK;
+ *msg->addr = *ipaddr;
+ }
+ /* wake up the application task waiting in netconn_gethostbyname */
+ sys_sem_signal(msg->sem);
+}
+
+/**
+ * Execute a DNS query
+ * Called from netconn_gethostbyname
+ *
+ * @param arg the dns_api_msg pointing to the query
+ */
+void
+lwip_netconn_do_gethostbyname(void *arg)
+{
+ struct dns_api_msg *msg = (struct dns_api_msg*)arg;
+
+ *msg->err = dns_gethostbyname(msg->name, msg->addr, lwip_netconn_do_dns_found, msg);
+ if (*msg->err != ERR_INPROGRESS) {
+ /* on error or immediate success, wake up the application
+ * task waiting in netconn_gethostbyname */
+ sys_sem_signal(msg->sem);
+ }
+}
+#endif /* LWIP_DNS */
+
+#endif /* LWIP_NETCONN */
diff --git a/src/VBox/Devices/Network/lwip-new/src/api/err.c b/src/VBox/Devices/Network/lwip-new/src/api/err.c
new file mode 100644
index 00000000..92fa8b7d
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/api/err.c
@@ -0,0 +1,75 @@
+/**
+ * @file
+ * Error Management module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/err.h"
+
+#ifdef LWIP_DEBUG
+
+static const char *err_strerr[] = {
+ "Ok.", /* ERR_OK 0 */
+ "Out of memory error.", /* ERR_MEM -1 */
+ "Buffer error.", /* ERR_BUF -2 */
+ "Timeout.", /* ERR_TIMEOUT -3 */
+ "Routing problem.", /* ERR_RTE -4 */
+ "Operation in progress.", /* ERR_INPROGRESS -5 */
+ "Illegal value.", /* ERR_VAL -6 */
+ "Operation would block.", /* ERR_WOULDBLOCK -7 */
+ "Address in use.", /* ERR_USE -8 */
+ "Already connected.", /* ERR_ISCONN -9 */
+ "Connection aborted.", /* ERR_ABRT -10 */
+ "Connection reset.", /* ERR_RST -11 */
+ "Connection closed.", /* ERR_CLSD -12 */
+ "Not connected.", /* ERR_CONN -13 */
+ "Illegal argument.", /* ERR_ARG -14 */
+ "Low-level netif error.", /* ERR_IF -15 */
+};
+
+/**
+ * Convert an lwip internal error to a string representation.
+ *
+ * @param err an lwip internal err_t
+ * @return a string representation for err
+ */
+const char *
+lwip_strerr(err_t err)
+{
+ return err_strerr[-err];
+
+}
+
+#endif /* LWIP_DEBUG */
diff --git a/src/VBox/Devices/Network/lwip-new/src/api/netbuf.c b/src/VBox/Devices/Network/lwip-new/src/api/netbuf.c
new file mode 100644
index 00000000..0ccd2bce
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/api/netbuf.c
@@ -0,0 +1,245 @@
+/**
+ * @file
+ * Network buffer management
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netbuf.h"
+#include "lwip/memp.h"
+
+#include <string.h>
+
+/**
+ * Create (allocate) and initialize a new netbuf.
+ * The netbuf doesn't yet contain a packet buffer!
+ *
+ * @return a pointer to a new netbuf
+ * NULL on lack of memory
+ */
+struct
+netbuf *netbuf_new(void)
+{
+ struct netbuf *buf;
+
+ buf = (struct netbuf *)memp_malloc(MEMP_NETBUF);
+ if (buf != NULL) {
+ buf->p = NULL;
+ buf->ptr = NULL;
+ ipX_addr_set_any(LWIP_IPV6, &buf->addr);
+ buf->port = 0;
+#if LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY
+#if LWIP_CHECKSUM_ON_COPY
+ buf->flags = 0;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ buf->toport_chksum = 0;
+#if LWIP_NETBUF_RECVINFO
+ ipX_addr_set_any(LWIP_IPV6, &buf->toaddr);
+#endif /* LWIP_NETBUF_RECVINFO */
+#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */
+ return buf;
+ } else {
+ return NULL;
+ }
+}
+
+/**
+ * Deallocate a netbuf allocated by netbuf_new().
+ *
+ * @param buf pointer to a netbuf allocated by netbuf_new()
+ */
+void
+netbuf_delete(struct netbuf *buf)
+{
+ if (buf != NULL) {
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ buf->p = buf->ptr = NULL;
+ }
+ memp_free(MEMP_NETBUF, buf);
+ }
+}
+
+/**
+ * Allocate memory for a packet buffer for a given netbuf.
+ *
+ * @param buf the netbuf for which to allocate a packet buffer
+ * @param size the size of the packet buffer to allocate
+ * @return pointer to the allocated memory
+ * NULL if no memory could be allocated
+ */
+void *
+netbuf_alloc(struct netbuf *buf, u16_t size)
+{
+ LWIP_ERROR("netbuf_alloc: invalid buf", (buf != NULL), return NULL;);
+
+ /* Deallocate any previously allocated memory. */
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ }
+ buf->p = pbuf_alloc(PBUF_TRANSPORT, size, PBUF_RAM);
+ if (buf->p == NULL) {
+ return NULL;
+ }
+ LWIP_ASSERT("check that first pbuf can hold size",
+ (buf->p->len >= size));
+ buf->ptr = buf->p;
+ return buf->p->payload;
+}
+
+/**
+ * Free the packet buffer included in a netbuf
+ *
+ * @param buf pointer to the netbuf which contains the packet buffer to free
+ */
+void
+netbuf_free(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ }
+ buf->p = buf->ptr = NULL;
+}
+
+/**
+ * Let a netbuf reference existing (non-volatile) data.
+ *
+ * @param buf netbuf which should reference the data
+ * @param dataptr pointer to the data to reference
+ * @param size size of the data
+ * @return ERR_OK if data is referenced
+ * ERR_MEM if data couldn't be referenced due to lack of memory
+ */
+err_t
+netbuf_ref(struct netbuf *buf, const void *dataptr, u16_t size)
+{
+ LWIP_ERROR("netbuf_ref: invalid buf", (buf != NULL), return ERR_ARG;);
+ if (buf->p != NULL) {
+ pbuf_free(buf->p);
+ }
+ buf->p = pbuf_alloc(PBUF_TRANSPORT, 0, PBUF_REF);
+ if (buf->p == NULL) {
+ buf->ptr = NULL;
+ return ERR_MEM;
+ }
+ buf->p->payload = (void*)dataptr;
+ buf->p->len = buf->p->tot_len = size;
+ buf->ptr = buf->p;
+ return ERR_OK;
+}
+
+/**
+ * Chain one netbuf to another (@see pbuf_chain)
+ *
+ * @param head the first netbuf
+ * @param tail netbuf to chain after head, freed by this function, may not be reference after returning
+ */
+void
+netbuf_chain(struct netbuf *head, struct netbuf *tail)
+{
+ LWIP_ERROR("netbuf_ref: invalid head", (head != NULL), return;);
+ LWIP_ERROR("netbuf_chain: invalid tail", (tail != NULL), return;);
+ pbuf_cat(head->p, tail->p);
+ head->ptr = head->p;
+ memp_free(MEMP_NETBUF, tail);
+}
+
+/**
+ * Get the data pointer and length of the data inside a netbuf.
+ *
+ * @param buf netbuf to get the data from
+ * @param dataptr pointer to a void pointer where to store the data pointer
+ * @param len pointer to an u16_t where the length of the data is stored
+ * @return ERR_OK if the information was retreived,
+ * ERR_BUF on error.
+ */
+err_t
+netbuf_data(struct netbuf *buf, void **dataptr, u16_t *len)
+{
+ LWIP_ERROR("netbuf_data: invalid buf", (buf != NULL), return ERR_ARG;);
+ LWIP_ERROR("netbuf_data: invalid dataptr", (dataptr != NULL), return ERR_ARG;);
+ LWIP_ERROR("netbuf_data: invalid len", (len != NULL), return ERR_ARG;);
+
+ if (buf->ptr == NULL) {
+ return ERR_BUF;
+ }
+ *dataptr = buf->ptr->payload;
+ *len = buf->ptr->len;
+ return ERR_OK;
+}
+
+/**
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the next part.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ * @return -1 if there is no next part
+ * 1 if moved to the next part but now there is no next part
+ * 0 if moved to the next part and there are still more parts
+ */
+s8_t
+netbuf_next(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return -1;);
+ if (buf->ptr->next == NULL) {
+ return -1;
+ }
+ buf->ptr = buf->ptr->next;
+ if (buf->ptr->next == NULL) {
+ return 1;
+ }
+ return 0;
+}
+
+/**
+ * Move the current data pointer of a packet buffer contained in a netbuf
+ * to the beginning of the packet.
+ * The packet buffer itself is not modified.
+ *
+ * @param buf the netbuf to modify
+ */
+void
+netbuf_first(struct netbuf *buf)
+{
+ LWIP_ERROR("netbuf_free: invalid buf", (buf != NULL), return;);
+ buf->ptr = buf->p;
+}
+
+#endif /* LWIP_NETCONN */
diff --git a/src/VBox/Devices/Network/lwip-new/src/api/netdb.c b/src/VBox/Devices/Network/lwip-new/src/api/netdb.c
new file mode 100644
index 00000000..9f38ef91
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/api/netdb.c
@@ -0,0 +1,348 @@
+/**
+ * @file
+ * API functions for name resolving
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/netdb.h"
+
+#if LWIP_DNS && LWIP_SOCKET
+
+#include "lwip/err.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/api.h"
+#include "lwip/dns.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+/** helper struct for gethostbyname_r to access the char* buffer */
+struct gethostbyname_r_helper {
+ ip_addr_t *addr_list[2];
+ ip_addr_t addr;
+ char *aliases;
+};
+
+/** h_errno is exported in netdb.h for access by applications. */
+#if LWIP_DNS_API_DECLARE_H_ERRNO
+int h_errno;
+#endif /* LWIP_DNS_API_DECLARE_H_ERRNO */
+
+/** define "hostent" variables storage: 0 if we use a static (but unprotected)
+ * set of variables for lwip_gethostbyname, 1 if we use a local storage */
+#ifndef LWIP_DNS_API_HOSTENT_STORAGE
+#define LWIP_DNS_API_HOSTENT_STORAGE 0
+#endif
+
+/** define "hostent" variables storage */
+#if LWIP_DNS_API_HOSTENT_STORAGE
+#define HOSTENT_STORAGE
+#else
+#define HOSTENT_STORAGE static
+#endif /* LWIP_DNS_API_STATIC_HOSTENT */
+
+/**
+ * Returns an entry containing addresses of address family AF_INET
+ * for the host with name name.
+ * Due to dns_gethostbyname limitations, only one address is returned.
+ *
+ * @param name the hostname to resolve
+ * @return an entry containing addresses of address family AF_INET
+ * for the host with name name
+ */
+struct hostent*
+lwip_gethostbyname(const char *name)
+{
+ err_t err;
+ ip_addr_t addr;
+
+ /* buffer variables for lwip_gethostbyname() */
+ HOSTENT_STORAGE struct hostent s_hostent;
+ HOSTENT_STORAGE char *s_aliases;
+ HOSTENT_STORAGE ip_addr_t s_hostent_addr;
+ HOSTENT_STORAGE ip_addr_t *s_phostent_addr[2];
+
+ /* query host IP address */
+ err = netconn_gethostbyname(name, &addr);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+ h_errno = HOST_NOT_FOUND;
+ return NULL;
+ }
+
+ /* fill hostent */
+ s_hostent_addr = addr;
+ s_phostent_addr[0] = &s_hostent_addr;
+ s_phostent_addr[1] = NULL;
+ s_hostent.h_name = (char*)name;
+ s_hostent.h_aliases = &s_aliases;
+ s_hostent.h_addrtype = AF_INET;
+ s_hostent.h_length = sizeof(ip_addr_t);
+ s_hostent.h_addr_list = (char**)&s_phostent_addr;
+
+#if DNS_DEBUG
+ /* dump hostent */
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_name == %s\n", s_hostent.h_name));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases == %p\n", s_hostent.h_aliases));
+ if (s_hostent.h_aliases != NULL) {
+ u8_t idx;
+ for ( idx=0; s_hostent.h_aliases[idx]; idx++) {
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %p\n", idx, s_hostent.h_aliases[idx]));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_aliases[%i]-> == %s\n", idx, s_hostent.h_aliases[idx]));
+ }
+ }
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addrtype == %d\n", s_hostent.h_addrtype));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_length == %d\n", s_hostent.h_length));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list == %p\n", s_hostent.h_addr_list));
+ if (s_hostent.h_addr_list != NULL) {
+ u8_t idx;
+ for ( idx=0; s_hostent.h_addr_list[idx]; idx++) {
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i] == %p\n", idx, s_hostent.h_addr_list[idx]));
+ LWIP_DEBUGF(DNS_DEBUG, ("hostent.h_addr_list[%i]-> == %s\n", idx, ip_ntoa((ip_addr_t*)s_hostent.h_addr_list[idx])));
+ }
+ }
+#endif /* DNS_DEBUG */
+
+#if LWIP_DNS_API_HOSTENT_STORAGE
+ /* this function should return the "per-thread" hostent after copy from s_hostent */
+ return sys_thread_hostent(&s_hostent);
+#else
+ return &s_hostent;
+#endif /* LWIP_DNS_API_HOSTENT_STORAGE */
+}
+
+/**
+ * Thread-safe variant of lwip_gethostbyname: instead of using a static
+ * buffer, this function takes buffer and errno pointers as arguments
+ * and uses these for the result.
+ *
+ * @param name the hostname to resolve
+ * @param ret pre-allocated struct where to store the result
+ * @param buf pre-allocated buffer where to store additional data
+ * @param buflen the size of buf
+ * @param result pointer to a hostent pointer that is set to ret on success
+ * and set to zero on error
+ * @param h_errnop pointer to an int where to store errors (instead of modifying
+ * the global h_errno)
+ * @return 0 on success, non-zero on error, additional error information
+ * is stored in *h_errnop instead of h_errno to be thread-safe
+ */
+int
+lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
+ size_t buflen, struct hostent **result, int *h_errnop)
+{
+ err_t err;
+ struct gethostbyname_r_helper *h;
+ char *hostname;
+ size_t namelen;
+ int lh_errno;
+
+ if (h_errnop == NULL) {
+ /* ensure h_errnop is never NULL */
+ h_errnop = &lh_errno;
+ }
+
+ if (result == NULL) {
+ /* not all arguments given */
+ *h_errnop = EINVAL;
+ return -1;
+ }
+ /* first thing to do: set *result to nothing */
+ *result = NULL;
+ if ((name == NULL) || (ret == NULL) || (buf == NULL)) {
+ /* not all arguments given */
+ *h_errnop = EINVAL;
+ return -1;
+ }
+
+ namelen = strlen(name);
+ if (buflen < (sizeof(struct gethostbyname_r_helper) + namelen + 1 + (MEM_ALIGNMENT - 1))) {
+ /* buf can't hold the data needed + a copy of name */
+ *h_errnop = ERANGE;
+ return -1;
+ }
+
+ h = (struct gethostbyname_r_helper*)LWIP_MEM_ALIGN(buf);
+ hostname = ((char*)h) + sizeof(struct gethostbyname_r_helper);
+
+ /* query host IP address */
+ err = netconn_gethostbyname(name, &h->addr);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG, ("lwip_gethostbyname(%s) failed, err=%d\n", name, err));
+ *h_errnop = HOST_NOT_FOUND;
+ return -1;
+ }
+
+ /* copy the hostname into buf */
+ MEMCPY(hostname, name, namelen);
+ hostname[namelen] = 0;
+
+ /* fill hostent */
+ h->addr_list[0] = &h->addr;
+ h->addr_list[1] = NULL;
+ h->aliases = NULL;
+ ret->h_name = hostname;
+ ret->h_aliases = &h->aliases;
+ ret->h_addrtype = AF_INET;
+ ret->h_length = sizeof(ip_addr_t);
+ ret->h_addr_list = (char**)&h->addr_list;
+
+ /* set result != NULL */
+ *result = ret;
+
+ /* return success */
+ return 0;
+}
+
+/**
+ * Frees one or more addrinfo structures returned by getaddrinfo(), along with
+ * any additional storage associated with those structures. If the ai_next field
+ * of the structure is not null, the entire list of structures is freed.
+ *
+ * @param ai struct addrinfo to free
+ */
+void
+lwip_freeaddrinfo(struct addrinfo *ai)
+{
+ struct addrinfo *next;
+
+ while (ai != NULL) {
+ next = ai->ai_next;
+ memp_free(MEMP_NETDB, ai);
+ ai = next;
+ }
+}
+
+/**
+ * Translates the name of a service location (for example, a host name) and/or
+ * a service name and returns a set of socket addresses and associated
+ * information to be used in creating a socket with which to address the
+ * specified service.
+ * Memory for the result is allocated internally and must be freed by calling
+ * lwip_freeaddrinfo()!
+ *
+ * Due to a limitation in dns_gethostbyname, only the first address of a
+ * host is returned.
+ * Also, service names are not supported (only port numbers)!
+ *
+ * @param nodename descriptive name or address string of the host
+ * (may be NULL -> local address)
+ * @param servname port number as string of NULL
+ * @param hints structure containing input values that set socktype and protocol
+ * @param res pointer to a pointer where to store the result (set to NULL on failure)
+ * @return 0 on success, non-zero on failure
+ */
+int
+lwip_getaddrinfo(const char *nodename, const char *servname,
+ const struct addrinfo *hints, struct addrinfo **res)
+{
+ err_t err;
+ ip_addr_t addr;
+ struct addrinfo *ai;
+ struct sockaddr_in *sa = NULL;
+ int port_nr = 0;
+ size_t total_size;
+ size_t namelen = 0;
+
+ if (res == NULL) {
+ return EAI_FAIL;
+ }
+ *res = NULL;
+ if ((nodename == NULL) && (servname == NULL)) {
+ return EAI_NONAME;
+ }
+
+ if (servname != NULL) {
+ /* service name specified: convert to port number
+ * @todo?: currently, only ASCII integers (port numbers) are supported! */
+ port_nr = atoi(servname);
+ if ((port_nr <= 0) || (port_nr > 0xffff)) {
+ return EAI_SERVICE;
+ }
+ }
+
+ if (nodename != NULL) {
+ /* service location specified, try to resolve */
+ err = netconn_gethostbyname(nodename, &addr);
+ if (err != ERR_OK) {
+ return EAI_FAIL;
+ }
+ } else {
+ /* service location specified, use loopback address */
+ ip_addr_set_loopback(&addr);
+ }
+
+ total_size = sizeof(struct addrinfo) + sizeof(struct sockaddr_in);
+ if (nodename != NULL) {
+ namelen = strlen(nodename);
+ LWIP_ASSERT("namelen is too long", (namelen + 1) <= (mem_size_t)-1);
+ total_size += namelen + 1;
+ }
+ /* If this fails, please report to lwip-devel! :-) */
+ LWIP_ASSERT("total_size <= NETDB_ELEM_SIZE: please report this!",
+ total_size <= NETDB_ELEM_SIZE);
+ ai = (struct addrinfo *)memp_malloc(MEMP_NETDB);
+ if (ai == NULL) {
+ 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);
+
+ /* set up addrinfo */
+ ai->ai_family = AF_INET;
+ if (hints != NULL) {
+ /* copy socktype & protocol from hints if specified */
+ ai->ai_socktype = hints->ai_socktype;
+ ai->ai_protocol = hints->ai_protocol;
+ }
+ if (nodename != NULL) {
+ /* copy nodename to canonname if specified */
+ ai->ai_canonname = ((char*)ai + sizeof(struct addrinfo) + sizeof(struct sockaddr_in));
+ MEMCPY(ai->ai_canonname, nodename, namelen);
+ ai->ai_canonname[namelen] = 0;
+ }
+ ai->ai_addrlen = sizeof(struct sockaddr_in);
+ ai->ai_addr = (struct sockaddr*)sa;
+
+ *res = ai;
+
+ return 0;
+}
+
+#endif /* LWIP_DNS && LWIP_SOCKET */
diff --git a/src/VBox/Devices/Network/lwip-new/src/api/netifapi.c b/src/VBox/Devices/Network/lwip-new/src/api/netifapi.c
new file mode 100644
index 00000000..81403f82
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/api/netifapi.c
@@ -0,0 +1,160 @@
+/**
+ * @file
+ * Network Interface Sequential API module
+ *
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netifapi.h"
+#include "lwip/tcpip.h"
+
+/**
+ * Call netif_add() inside the tcpip_thread context.
+ */
+static void
+netifapi_do_netif_add(struct netifapi_msg_msg *msg)
+{
+ if (!netif_add( msg->netif,
+ msg->msg.add.ipaddr,
+ msg->msg.add.netmask,
+ msg->msg.add.gw,
+ msg->msg.add.state,
+ msg->msg.add.init,
+ msg->msg.add.input)) {
+ msg->err = ERR_IF;
+ } else {
+ msg->err = ERR_OK;
+ }
+ TCPIP_NETIFAPI_ACK(msg);
+}
+
+/**
+ * Call netif_set_addr() inside the tcpip_thread context.
+ */
+static void
+netifapi_do_netif_set_addr(struct netifapi_msg_msg *msg)
+{
+ netif_set_addr( msg->netif,
+ msg->msg.add.ipaddr,
+ msg->msg.add.netmask,
+ msg->msg.add.gw);
+ msg->err = ERR_OK;
+ TCPIP_NETIFAPI_ACK(msg);
+}
+
+/**
+ * Call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) inside the
+ * tcpip_thread context.
+ */
+static void
+netifapi_do_netif_common(struct netifapi_msg_msg *msg)
+{
+ if (msg->msg.common.errtfunc != NULL) {
+ msg->err = msg->msg.common.errtfunc(msg->netif);
+ } else {
+ msg->err = ERR_OK;
+ msg->msg.common.voidfunc(msg->netif);
+ }
+ TCPIP_NETIFAPI_ACK(msg);
+}
+
+/**
+ * Call netif_add() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_add()
+ */
+err_t
+netifapi_netif_add(struct netif *netif,
+ ip_addr_t *ipaddr,
+ ip_addr_t *netmask,
+ ip_addr_t *gw,
+ void *state,
+ netif_init_fn init,
+ netif_input_fn input)
+{
+ struct netifapi_msg msg;
+ msg.function = netifapi_do_netif_add;
+ msg.msg.netif = netif;
+ msg.msg.msg.add.ipaddr = ipaddr;
+ msg.msg.msg.add.netmask = netmask;
+ msg.msg.msg.add.gw = gw;
+ msg.msg.msg.add.state = state;
+ msg.msg.msg.add.init = init;
+ msg.msg.msg.add.input = input;
+ TCPIP_NETIFAPI(&msg);
+ return msg.msg.err;
+}
+
+/**
+ * Call netif_set_addr() in a thread-safe way by running that function inside the
+ * tcpip_thread context.
+ *
+ * @note for params @see netif_set_addr()
+ */
+err_t
+netifapi_netif_set_addr(struct netif *netif,
+ ip_addr_t *ipaddr,
+ ip_addr_t *netmask,
+ ip_addr_t *gw)
+{
+ struct netifapi_msg msg;
+ msg.function = netifapi_do_netif_set_addr;
+ msg.msg.netif = netif;
+ msg.msg.msg.add.ipaddr = ipaddr;
+ msg.msg.msg.add.netmask = netmask;
+ msg.msg.msg.add.gw = gw;
+ TCPIP_NETIFAPI(&msg);
+ return msg.msg.err;
+}
+
+/**
+ * call the "errtfunc" (or the "voidfunc" if "errtfunc" is NULL) in a thread-safe
+ * way by running that function inside the tcpip_thread context.
+ *
+ * @note use only for functions where there is only "netif" parameter.
+ */
+err_t
+netifapi_netif_common(struct netif *netif, netifapi_void_fn voidfunc,
+ netifapi_errt_fn errtfunc)
+{
+ struct netifapi_msg msg;
+ msg.function = netifapi_do_netif_common;
+ msg.msg.netif = netif;
+ msg.msg.msg.common.voidfunc = voidfunc;
+ msg.msg.msg.common.errtfunc = errtfunc;
+ TCPIP_NETIFAPI(&msg);
+ return msg.msg.err;
+}
+
+#endif /* LWIP_NETIF_API */
diff --git a/src/VBox/Devices/Network/lwip-new/src/api/sockets.c b/src/VBox/Devices/Network/lwip-new/src/api/sockets.c
new file mode 100644
index 00000000..66036712
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/api/sockets.c
@@ -0,0 +1,2555 @@
+/**
+ * @file
+ * Sockets BSD-Like API module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ * Improved by Marc Boucher <marc@mbsi.ca> and David Haas <dhaas@alum.rpi.edu>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sockets.h"
+#include "lwip/api.h"
+#include "lwip/sys.h"
+#include "lwip/igmp.h"
+#include "lwip/inet.h"
+#include "lwip/tcp.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcpip.h"
+#include "lwip/pbuf.h"
+#if LWIP_CHECKSUM_ON_COPY
+#include "lwip/inet_chksum.h"
+#endif
+
+#include <string.h>
+
+#define IP4ADDR_PORT_TO_SOCKADDR(sin, ipXaddr, port) do { \
+ (sin)->sin_len = sizeof(struct sockaddr_in); \
+ (sin)->sin_family = AF_INET; \
+ (sin)->sin_port = htons((port)); \
+ inet_addr_from_ipaddr(&(sin)->sin_addr, ipX_2_ip(ipXaddr)); \
+ memset((sin)->sin_zero, 0, SIN_ZERO_LEN); }while(0)
+#define SOCKADDR4_TO_IP4ADDR_PORT(sin, ipXaddr, port) do { \
+ inet_addr_to_ipaddr(ipX_2_ip(ipXaddr), &((sin)->sin_addr)); \
+ (port) = ntohs((sin)->sin_port); }while(0)
+
+#if LWIP_IPV6
+#define IS_SOCK_ADDR_LEN_VALID(namelen) (((namelen) == sizeof(struct sockaddr_in)) || \
+ ((namelen) == sizeof(struct sockaddr_in6)))
+#define IS_SOCK_ADDR_TYPE_VALID(name) (((name)->sa_family == AF_INET) || \
+ ((name)->sa_family == AF_INET6))
+#define SOCK_ADDR_TYPE_MATCH(name, sock) \
+ ((((name)->sa_family == AF_INET) && !(NETCONNTYPE_ISIPV6((sock)->conn->type))) || \
+ (((name)->sa_family == AF_INET6) && (NETCONNTYPE_ISIPV6((sock)->conn->type))))
+#define IP6ADDR_PORT_TO_SOCKADDR(sin6, ipXaddr, port) do { \
+ (sin6)->sin6_len = sizeof(struct sockaddr_in6); \
+ (sin6)->sin6_family = AF_INET6; \
+ (sin6)->sin6_port = htons((port)); \
+ (sin6)->sin6_flowinfo = 0; \
+ inet6_addr_from_ip6addr(&(sin6)->sin6_addr, ipX_2_ip6(ipXaddr)); }while(0)
+#define IPXADDR_PORT_TO_SOCKADDR(isipv6, sockaddr, ipXaddr, port) do { \
+ if (isipv6) { \
+ IP6ADDR_PORT_TO_SOCKADDR((struct sockaddr_in6*)(void*)(sockaddr), ipXaddr, port); \
+ } else { \
+ IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port); \
+ } } while(0)
+#define SOCKADDR6_TO_IP6ADDR_PORT(sin6, ipXaddr, port) do { \
+ inet6_addr_to_ip6addr(ipX_2_ip6(ipXaddr), &((sin6)->sin6_addr)); \
+ (port) = ntohs((sin6)->sin6_port); }while(0)
+#define SOCKADDR_TO_IPXADDR_PORT(isipv6, sockaddr, ipXaddr, port) do { \
+ if (isipv6) { \
+ SOCKADDR6_TO_IP6ADDR_PORT((struct sockaddr_in6*)(void*)(sockaddr), ipXaddr, port); \
+ } else { \
+ SOCKADDR4_TO_IP4ADDR_PORT((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port); \
+ } } while(0)
+#define DOMAIN_TO_NETCONN_TYPE(domain, type) (((domain) == AF_INET) ? \
+ (type) : (enum netconn_type)((type) | NETCONN_TYPE_IPV6))
+#else /* LWIP_IPV6 */
+#define IS_SOCK_ADDR_LEN_VALID(namelen) ((namelen) == sizeof(struct sockaddr_in))
+#define IS_SOCK_ADDR_TYPE_VALID(name) ((name)->sa_family == AF_INET)
+#define SOCK_ADDR_TYPE_MATCH(name, sock) 1
+#define IPXADDR_PORT_TO_SOCKADDR(isipv6, sockaddr, ipXaddr, port) \
+ IP4ADDR_PORT_TO_SOCKADDR((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port)
+#define SOCKADDR_TO_IPXADDR_PORT(isipv6, sockaddr, ipXaddr, port) \
+ SOCKADDR4_TO_IP4ADDR_PORT((struct sockaddr_in*)(void*)(sockaddr), ipXaddr, port)
+#define DOMAIN_TO_NETCONN_TYPE(domain, netconn_type) (netconn_type)
+#endif /* LWIP_IPV6 */
+
+#define IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) (((name)->sa_family == AF_UNSPEC) || \
+ IS_SOCK_ADDR_TYPE_VALID(name))
+#define SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock) (((name)->sa_family == AF_UNSPEC) || \
+ SOCK_ADDR_TYPE_MATCH(name, sock))
+#define IS_SOCK_ADDR_ALIGNED(name) ((((mem_ptr_t)(name)) % 4) == 0)
+
+
+
+#define NUM_SOCKETS MEMP_NUM_NETCONN
+
+/** Contains all internal pointers and states used for a socket */
+struct lwip_sock {
+ /** sockets currently are built on netconns, each socket has one netconn */
+ struct netconn *conn;
+ /** data that was left from the previous read */
+ void *lastdata;
+ /** offset in the data that was left from the previous read */
+ u16_t lastoffset;
+ /** number of times data was received, set by event_callback(),
+ tested by the receive and select functions */
+ s16_t rcvevent;
+ /** number of times data was ACKed (free send buffer), set by event_callback(),
+ tested by select */
+ u16_t sendevent;
+ /** error happened for this socket, set by event_callback(), tested by select */
+ u16_t errevent;
+ /** last error that occurred on this socket */
+ int err;
+ /** counter of how many threads are waiting for this socket using select */
+ int select_waiting;
+};
+
+/** Description for a task waiting in select */
+struct lwip_select_cb {
+ /** Pointer to the next waiting task */
+ struct lwip_select_cb *next;
+ /** Pointer to the previous waiting task */
+ struct lwip_select_cb *prev;
+ /** readset passed to select */
+ fd_set *readset;
+ /** writeset passed to select */
+ fd_set *writeset;
+ /** unimplemented: exceptset passed to select */
+ fd_set *exceptset;
+ /** don't signal the same semaphore twice: set to 1 when signalled */
+ int sem_signalled;
+ /** semaphore to wake up a task waiting for select */
+ sys_sem_t sem;
+};
+
+/** This struct is used to pass data to the set/getsockopt_internal
+ * functions running in tcpip_thread context (only a void* is allowed) */
+struct lwip_setgetsockopt_data {
+ /** socket struct for which to change options */
+ struct lwip_sock *sock;
+#ifdef LWIP_DEBUG
+ /** socket index for which to change options */
+ int s;
+#endif /* LWIP_DEBUG */
+ /** level of the option to process */
+ int level;
+ /** name of the option to process */
+ int optname;
+ /** set: value to set the option to
+ * get: value of the option is stored here */
+ void *optval;
+ /** size of *optval */
+ socklen_t *optlen;
+ /** if an error occures, it is temporarily stored here */
+ err_t err;
+};
+
+/** A struct sockaddr replacement that has the same alignment as sockaddr_in/
+ * sockaddr_in6 if instantiated.
+ */
+union sockaddr_aligned {
+ struct sockaddr sa;
+#if LWIP_IPV6
+ struct sockaddr_in6 sin6;
+#endif /* LWIP_IPV6 */
+ struct sockaddr_in sin;
+};
+
+
+/** The global array of available sockets */
+static struct lwip_sock sockets[NUM_SOCKETS];
+/** The global list of tasks waiting for select */
+static struct lwip_select_cb *select_cb_list;
+/** This counter is increased from lwip_select when the list is chagned
+ and checked in event_callback to see if it has changed. */
+static volatile int select_cb_ctr;
+
+/** Table to quickly map an lwIP error (err_t) to a socket error
+ * by using -err as an index */
+static const int err_to_errno_table[] = {
+ 0, /* ERR_OK 0 No error, everything OK. */
+ ENOMEM, /* ERR_MEM -1 Out of memory error. */
+ ENOBUFS, /* ERR_BUF -2 Buffer error. */
+ EWOULDBLOCK, /* ERR_TIMEOUT -3 Timeout */
+ EHOSTUNREACH, /* ERR_RTE -4 Routing problem. */
+ EINPROGRESS, /* ERR_INPROGRESS -5 Operation in progress */
+ EINVAL, /* ERR_VAL -6 Illegal value. */
+ EWOULDBLOCK, /* ERR_WOULDBLOCK -7 Operation would block. */
+ EADDRINUSE, /* ERR_USE -8 Address in use. */
+ EALREADY, /* ERR_ISCONN -9 Already connected. */
+ ECONNABORTED, /* ERR_ABRT -10 Connection aborted. */
+ ECONNRESET, /* ERR_RST -11 Connection reset. */
+ ENOTCONN, /* ERR_CLSD -12 Connection closed. */
+ ENOTCONN, /* ERR_CONN -13 Not connected. */
+ EIO, /* ERR_ARG -14 Illegal argument. */
+ -1, /* ERR_IF -15 Low-level netif error */
+};
+
+#define ERR_TO_ERRNO_TABLE_SIZE \
+ (sizeof(err_to_errno_table)/sizeof(err_to_errno_table[0]))
+
+#define err_to_errno(err) \
+ ((unsigned)(-(err)) < ERR_TO_ERRNO_TABLE_SIZE ? \
+ err_to_errno_table[-(err)] : EIO)
+
+#ifdef ERRNO
+#ifndef set_errno
+#define set_errno(err) errno = (err)
+#endif
+#else /* ERRNO */
+#define set_errno(err)
+#endif /* ERRNO */
+
+#define sock_set_errno(sk, e) do { \
+ sk->err = (e); \
+ set_errno(sk->err); \
+} while (0)
+
+/* Forward delcaration of some functions */
+static void event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len);
+static void lwip_getsockopt_internal(void *arg);
+static void lwip_setsockopt_internal(void *arg);
+
+/**
+ * Initialize this module. This function has to be called before any other
+ * functions in this module!
+ */
+void
+lwip_socket_init(void)
+{
+}
+
+/**
+ * Map a externally used socket index to the internal socket representation.
+ *
+ * @param s externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+get_socket(int s)
+{
+ struct lwip_sock *sock;
+
+ if ((s < 0) || (s >= NUM_SOCKETS)) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): invalid\n", s));
+ set_errno(EBADF);
+ return NULL;
+ }
+
+ sock = &sockets[s];
+
+ if (!sock->conn) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("get_socket(%d): not active\n", s));
+ set_errno(EBADF);
+ return NULL;
+ }
+
+ return sock;
+}
+
+/**
+ * Same as get_socket but doesn't set errno
+ *
+ * @param s externally used socket index
+ * @return struct lwip_sock for the socket or NULL if not found
+ */
+static struct lwip_sock *
+tryget_socket(int s)
+{
+ if ((s < 0) || (s >= NUM_SOCKETS)) {
+ return NULL;
+ }
+ if (!sockets[s].conn) {
+ return NULL;
+ }
+ return &sockets[s];
+}
+
+/**
+ * Allocate a new socket for a given netconn.
+ *
+ * @param newconn the netconn for which to allocate a socket
+ * @param accepted 1 if socket has been created by accept(),
+ * 0 if socket has been created by socket()
+ * @return the index of the new socket; -1 on error
+ */
+static int
+alloc_socket(struct netconn *newconn, int accepted)
+{
+ int i;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ /* allocate a new socket identifier */
+ for (i = 0; i < NUM_SOCKETS; ++i) {
+ /* Protect socket array */
+ SYS_ARCH_PROTECT(lev);
+ if (!sockets[i].conn) {
+ sockets[i].conn = newconn;
+ /* The socket is not yet known to anyone, so no need to protect
+ after having marked it as used. */
+ SYS_ARCH_UNPROTECT(lev);
+ sockets[i].lastdata = NULL;
+ sockets[i].lastoffset = 0;
+ sockets[i].rcvevent = 0;
+ /* TCP sendbuf is empty, but the socket is not yet writable until connected
+ * (unless it has been created by accept()). */
+ sockets[i].sendevent = (NETCONNTYPE_GROUP(newconn->type) == NETCONN_TCP ? (accepted != 0) : 1);
+ sockets[i].errevent = 0;
+ sockets[i].err = 0;
+ sockets[i].select_waiting = 0;
+ return i;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ return -1;
+}
+
+/** Free a socket. The socket's netconn must have been
+ * delete before!
+ *
+ * @param sock the socket to free
+ * @param is_tcp != 0 for TCP sockets, used to free lastdata
+ */
+static void
+free_socket(struct lwip_sock *sock, int is_tcp)
+{
+ void *lastdata;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ lastdata = sock->lastdata;
+ sock->lastdata = NULL;
+ sock->lastoffset = 0;
+ sock->err = 0;
+
+ /* Protect socket array */
+ SYS_ARCH_PROTECT(lev);
+ sock->conn = NULL;
+ SYS_ARCH_UNPROTECT(lev);
+ /* don't use 'sock' after this line, as another task might have allocated it */
+
+ if (lastdata != NULL) {
+ if (is_tcp) {
+ pbuf_free((struct pbuf *)lastdata);
+ } else {
+ netbuf_delete((struct netbuf *)lastdata);
+ }
+ }
+}
+
+/* Below this, the well-known socket functions are implemented.
+ * Use google.com or opengroup.org to get a good description :-)
+ *
+ * Exceptions are documented!
+ */
+
+int
+lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen)
+{
+ struct lwip_sock *sock, *nsock;
+ struct netconn *newconn;
+ ipX_addr_t naddr;
+ u16_t port = 0;
+ int newsock;
+ err_t err;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d)...\n", s));
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (netconn_is_nonblocking(sock->conn) && (sock->rcvevent <= 0)) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): returning EWOULDBLOCK\n", s));
+ sock_set_errno(sock, EWOULDBLOCK);
+ return -1;
+ }
+
+ /* wait for a new connection */
+ err = netconn_accept(sock->conn, &newconn);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_acept failed, err=%d\n", s, err));
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+ sock_set_errno(sock, EOPNOTSUPP);
+ return EOPNOTSUPP;
+ }
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+ LWIP_ASSERT("newconn != NULL", newconn != NULL);
+ /* Prevent automatic window updates, we do this on our own! */
+ netconn_set_noautorecved(newconn, 1);
+
+ /* Note that POSIX only requires us to check addr is non-NULL. addrlen must
+ * not be NULL if addr is valid.
+ */
+ if (addr != NULL) {
+ union sockaddr_aligned tempaddr;
+ /* get the IP address and port of the remote host */
+ err = netconn_peer(newconn, ipX_2_ip(&naddr), &port);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d): netconn_peer failed, err=%d\n", s, err));
+ netconn_delete(newconn);
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+ LWIP_ASSERT("addr valid but addrlen NULL", addrlen != NULL);
+
+ IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(newconn->type), &tempaddr, &naddr, port);
+ if (*addrlen > tempaddr.sa.sa_len) {
+ *addrlen = tempaddr.sa.sa_len;
+ }
+ MEMCPY(addr, &tempaddr, *addrlen);
+ }
+
+ newsock = alloc_socket(newconn, 1);
+ if (newsock == -1) {
+ netconn_delete(newconn);
+ sock_set_errno(sock, ENFILE);
+ return -1;
+ }
+ LWIP_ASSERT("invalid socket index", (newsock >= 0) && (newsock < NUM_SOCKETS));
+ LWIP_ASSERT("newconn->callback == event_callback", newconn->callback == event_callback);
+ nsock = &sockets[newsock];
+
+ /* See event_callback: If data comes in right away after an accept, even
+ * though the server task might not have created a new socket yet.
+ * In that case, newconn->socket is counted down (newconn->socket--),
+ * so nsock->rcvevent is >= 1 here!
+ */
+ SYS_ARCH_PROTECT(lev);
+ nsock->rcvevent += (s16_t)(-1 - newconn->socket);
+ newconn->socket = newsock;
+ SYS_ARCH_UNPROTECT(lev);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_accept(%d) returning new sock=%d", s, newsock));
+ if (addr != NULL) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" addr="));
+ ipX_addr_debug_print(NETCONNTYPE_ISIPV6(newconn->type), SOCKETS_DEBUG, &naddr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", port));
+ }
+
+ sock_set_errno(sock, 0);
+ return newsock;
+}
+
+int
+lwip_bind(int s, const struct sockaddr *name, socklen_t namelen)
+{
+ struct lwip_sock *sock;
+ ipX_addr_t local_addr;
+ u16_t local_port;
+ err_t err;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (!SOCK_ADDR_TYPE_MATCH(name, sock)) {
+ /* sockaddr does not match socket type (IPv4/IPv6) */
+ sock_set_errno(sock, err_to_errno(ERR_VAL));
+ return -1;
+ }
+
+ /* check size, familiy and alignment of 'name' */
+ LWIP_ERROR("lwip_bind: invalid address", (IS_SOCK_ADDR_LEN_VALID(namelen) &&
+ IS_SOCK_ADDR_TYPE_VALID(name) && IS_SOCK_ADDR_ALIGNED(name)),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+ LWIP_UNUSED_ARG(namelen);
+
+ SOCKADDR_TO_IPXADDR_PORT((name->sa_family == AF_INET6), name, &local_addr, local_port);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d, addr=", s));
+ ipX_addr_debug_print(name->sa_family == AF_INET6, SOCKETS_DEBUG, &local_addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", local_port));
+
+ err = netconn_bind(sock->conn, ipX_2_ip(&local_addr), local_port);
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) failed, err=%d\n", s, err));
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_bind(%d) succeeded\n", s));
+ sock_set_errno(sock, 0);
+ return 0;
+}
+
+int
+lwip_close(int s)
+{
+ struct lwip_sock *sock;
+ int is_tcp = 0;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_close(%d)\n", s));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if(sock->conn != NULL) {
+ is_tcp = NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP;
+ } else {
+ LWIP_ASSERT("sock->lastdata == NULL", sock->lastdata == NULL);
+ }
+
+ netconn_delete(sock->conn);
+
+ free_socket(sock, is_tcp);
+ set_errno(0);
+ return 0;
+}
+
+int
+lwip_connect(int s, const struct sockaddr *name, socklen_t namelen)
+{
+ struct lwip_sock *sock;
+ err_t err;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (!SOCK_ADDR_TYPE_MATCH_OR_UNSPEC(name, sock)) {
+ /* sockaddr does not match socket type (IPv4/IPv6) */
+ sock_set_errno(sock, err_to_errno(ERR_VAL));
+ return -1;
+ }
+
+ /* check size, familiy and alignment of 'name' */
+ LWIP_ERROR("lwip_connect: invalid address", IS_SOCK_ADDR_LEN_VALID(namelen) &&
+ IS_SOCK_ADDR_TYPE_VALID_OR_UNSPEC(name) && IS_SOCK_ADDR_ALIGNED(name),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+ LWIP_UNUSED_ARG(namelen);
+ if (name->sa_family == AF_UNSPEC) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, AF_UNSPEC)\n", s));
+ err = netconn_disconnect(sock->conn);
+ } else {
+ ipX_addr_t remote_addr;
+ u16_t remote_port;
+ SOCKADDR_TO_IPXADDR_PORT((name->sa_family == AF_INET6), name, &remote_addr, remote_port);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d, addr=", s));
+ ipX_addr_debug_print(name->sa_family == AF_INET6, SOCKETS_DEBUG, &remote_addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", remote_port));
+
+ err = netconn_connect(sock->conn, ipX_2_ip(&remote_addr), remote_port);
+ }
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) failed, err=%d\n", s, err));
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_connect(%d) succeeded\n", s));
+ sock_set_errno(sock, 0);
+ return 0;
+}
+
+/**
+ * Set a socket into listen mode.
+ * The socket may not have been used for another connection previously.
+ *
+ * @param s the socket to set to listening mode
+ * @param backlog (ATTENTION: needs TCP_LISTEN_BACKLOG=1)
+ * @return 0 on success, non-zero on failure
+ */
+int
+lwip_listen(int s, int backlog)
+{
+ struct lwip_sock *sock;
+ err_t err;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d, backlog=%d)\n", s, backlog));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ /* limit the "backlog" parameter to fit in an u8_t */
+ backlog = LWIP_MIN(LWIP_MAX(backlog, 0), 0xff);
+
+ err = netconn_listen_with_backlog(sock->conn, (u8_t)backlog);
+
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_listen(%d) failed, err=%d\n", s, err));
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+ sock_set_errno(sock, EOPNOTSUPP);
+ return EOPNOTSUPP;
+ }
+ sock_set_errno(sock, err_to_errno(err));
+ return -1;
+ }
+
+ sock_set_errno(sock, 0);
+ return 0;
+}
+
+int
+lwip_recvfrom(int s, void *mem, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromlen)
+{
+ struct lwip_sock *sock;
+ void *buf = NULL;
+ struct pbuf *p;
+ u16_t buflen, copylen;
+ int off = 0;
+ u8_t done = 0;
+ err_t err;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d, %p, %"SZT_F", 0x%x, ..)\n", s, mem, len, flags));
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ do {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: top while sock->lastdata=%p\n", sock->lastdata));
+ /* Check if there is data left from the last recv operation. */
+ if (sock->lastdata) {
+ buf = sock->lastdata;
+ } else {
+ /* If this is non-blocking call, then check first */
+ if (((flags & MSG_DONTWAIT) || netconn_is_nonblocking(sock->conn)) &&
+ (sock->rcvevent <= 0)) {
+ if (off > 0) {
+ /* update receive window */
+ netconn_recved(sock->conn, (u32_t)off);
+ /* already received data, return that */
+ sock_set_errno(sock, 0);
+ return off;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): returning EWOULDBLOCK\n", s));
+ sock_set_errno(sock, EWOULDBLOCK);
+ return -1;
+ }
+
+ /* No data was left from the previous operation, so we try to get
+ some from the network. */
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+ err = netconn_recv_tcp_pbuf(sock->conn, (struct pbuf **)&buf);
+ } else {
+ err = netconn_recv(sock->conn, (struct netbuf **)&buf);
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: netconn_recv err=%d, netbuf=%p\n",
+ err, buf));
+
+ if (err != ERR_OK) {
+ if (off > 0) {
+ /* update receive window */
+ netconn_recved(sock->conn, (u32_t)off);
+ /* already received data, return that */
+ sock_set_errno(sock, 0);
+ return off;
+ }
+ /* We should really do some error checking here. */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): buf == NULL, error is \"%s\"!\n",
+ s, lwip_strerr(err)));
+ sock_set_errno(sock, err_to_errno(err));
+ if (err == ERR_CLSD) {
+ return 0;
+ } else {
+ return -1;
+ }
+ }
+ LWIP_ASSERT("buf != NULL", buf != NULL);
+ sock->lastdata = buf;
+ }
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+ p = (struct pbuf *)buf;
+ } else {
+ p = ((struct netbuf *)buf)->p;
+ }
+ buflen = p->tot_len;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: buflen=%"U16_F" len=%"SZT_F" off=%d sock->lastoffset=%"U16_F"\n",
+ buflen, len, off, sock->lastoffset));
+
+ buflen -= sock->lastoffset;
+
+ if (len > buflen) {
+ copylen = buflen;
+ } else {
+ copylen = (u16_t)len;
+ }
+
+ /* copy the contents of the received buffer into
+ the supplied memory pointer mem */
+ pbuf_copy_partial(p, (u8_t*)mem + off, copylen, sock->lastoffset);
+
+ off += copylen;
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+ LWIP_ASSERT("invalid copylen, len would underflow", len >= copylen);
+ len -= copylen;
+ if ( (len <= 0) ||
+ (p->flags & PBUF_FLAG_PUSH) ||
+ (sock->rcvevent <= 0) ||
+ ((flags & MSG_PEEK)!=0)) {
+ done = 1;
+ }
+ } else {
+ done = 1;
+ }
+
+ /* Check to see from where the data was.*/
+ if (done) {
+#if !SOCKETS_DEBUG
+ if (from && fromlen)
+#endif /* !SOCKETS_DEBUG */
+ {
+ u16_t port;
+ ipX_addr_t tmpaddr;
+ ipX_addr_t *fromaddr;
+ union sockaddr_aligned saddr;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom(%d): addr=", s));
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+ fromaddr = &tmpaddr;
+ /* @todo: this does not work for IPv6, yet */
+ netconn_getaddr(sock->conn, ipX_2_ip(fromaddr), &port, 0);
+ } else {
+ port = netbuf_fromport((struct netbuf *)buf);
+ fromaddr = netbuf_fromaddr_ipX((struct netbuf *)buf);
+ }
+ IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)),
+ &saddr, fromaddr, port);
+ ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)),
+ SOCKETS_DEBUG, fromaddr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F" len=%d\n", port, off));
+#if SOCKETS_DEBUG
+ if (from && fromlen)
+#endif /* SOCKETS_DEBUG */
+ {
+ if (*fromlen > saddr.sa.sa_len) {
+ *fromlen = saddr.sa.sa_len;
+ }
+ MEMCPY(from, &saddr, *fromlen);
+ }
+ }
+ }
+
+ /* If we don't peek the incoming message... */
+ if ((flags & MSG_PEEK) == 0) {
+ /* If this is a TCP socket, check if there is data left in the
+ buffer. If so, it should be saved in the sock structure for next
+ time around. */
+ if ((NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) && (buflen - copylen > 0)) {
+ sock->lastdata = buf;
+ sock->lastoffset += copylen;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: lastdata now netbuf=%p\n", buf));
+ } else {
+ sock->lastdata = NULL;
+ sock->lastoffset = 0;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_recvfrom: deleting netbuf=%p\n", buf));
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+ pbuf_free((struct pbuf *)buf);
+ } else {
+ netbuf_delete((struct netbuf *)buf);
+ }
+ }
+ }
+ } while (!done);
+
+ if ((off > 0) && (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP)) {
+ /* update receive window */
+ netconn_recved(sock->conn, (u32_t)off);
+ }
+ sock_set_errno(sock, 0);
+ return off;
+}
+
+int
+lwip_read(int s, void *mem, size_t len)
+{
+ return lwip_recvfrom(s, mem, len, 0, NULL, NULL);
+}
+
+int
+lwip_recv(int s, void *mem, size_t len, int flags)
+{
+ return lwip_recvfrom(s, mem, len, flags, NULL, NULL);
+}
+
+int
+lwip_send(int s, const void *data, size_t size, int flags)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ u8_t write_flags;
+ size_t written;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d, data=%p, size=%"SZT_F", flags=0x%x)\n",
+ s, data, size, flags));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+#if (LWIP_UDP || LWIP_RAW)
+ return lwip_sendto(s, data, size, flags, NULL, 0);
+#else /* (LWIP_UDP || LWIP_RAW) */
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ return -1;
+#endif /* (LWIP_UDP || LWIP_RAW) */
+ }
+
+ write_flags = NETCONN_COPY |
+ ((flags & MSG_MORE) ? NETCONN_MORE : 0) |
+ ((flags & MSG_DONTWAIT) ? NETCONN_DONTBLOCK : 0);
+ written = 0;
+ err = netconn_write_partly(sock->conn, data, size, write_flags, &written);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_send(%d) err=%d written=%"SZT_F"\n", s, err, written));
+ sock_set_errno(sock, err_to_errno(err));
+ return (err == ERR_OK ? (int)written : -1);
+}
+
+int
+lwip_sendto(int s, const void *data, size_t size, int flags,
+ const struct sockaddr *to, socklen_t tolen)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ u16_t short_size;
+ u16_t remote_port;
+#if !LWIP_TCPIP_CORE_LOCKING
+ struct netbuf buf;
+#endif
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_TCP) {
+#if LWIP_TCP
+ return lwip_send(s, data, size, flags);
+#else /* LWIP_TCP */
+ LWIP_UNUSED_ARG(flags);
+ sock_set_errno(sock, err_to_errno(ERR_ARG));
+ return -1;
+#endif /* LWIP_TCP */
+ }
+
+ if ((to != NULL) && !SOCK_ADDR_TYPE_MATCH(to, sock)) {
+ /* sockaddr does not match socket type (IPv4/IPv6) */
+ sock_set_errno(sock, err_to_errno(ERR_VAL));
+ return -1;
+ }
+
+ /* @todo: split into multiple sendto's? */
+ LWIP_ASSERT("lwip_sendto: size must fit in u16_t", size <= 0xffff);
+ short_size = (u16_t)size;
+ LWIP_ERROR("lwip_sendto: invalid address", (((to == NULL) && (tolen == 0)) ||
+ (IS_SOCK_ADDR_LEN_VALID(tolen) &&
+ IS_SOCK_ADDR_TYPE_VALID(to) && IS_SOCK_ADDR_ALIGNED(to))),
+ sock_set_errno(sock, err_to_errno(ERR_ARG)); return -1;);
+ LWIP_UNUSED_ARG(tolen);
+
+#if LWIP_TCPIP_CORE_LOCKING
+ /* Special speedup for fast UDP/RAW sending: call the raw API directly
+ instead of using the netconn functions. */
+ {
+ struct pbuf* p;
+ ipX_addr_t *remote_addr;
+ ipX_addr_t remote_addr_tmp;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_RAM);
+ if (p != NULL) {
+#if LWIP_CHECKSUM_ON_COPY
+ u16_t chksum = 0;
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) {
+ chksum = LWIP_CHKSUM_COPY(p->payload, data, short_size);
+ } else
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ MEMCPY(p->payload, data, size);
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ p = pbuf_alloc(PBUF_TRANSPORT, short_size, PBUF_REF);
+ if (p != NULL) {
+ p->payload = (void*)data;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+ if (to != NULL) {
+ SOCKADDR_TO_IPXADDR_PORT(to->sa_family == AF_INET6,
+ to, &remote_addr_tmp, remote_port);
+ remote_addr = &remote_addr_tmp;
+ } else {
+ remote_addr = &sock->conn->pcb.ip->remote_ip;
+#if LWIP_UDP
+ if (NETCONNTYPE_GROUP(sock->conn->type) == NETCONN_UDP) {
+ remote_port = sock->conn->pcb.udp->remote_port;
+ } else
+#endif /* LWIP_UDP */
+ {
+ remote_port = 0;
+ }
+ }
+
+ LOCK_TCPIP_CORE();
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) == NETCONN_RAW) {
+#if LWIP_RAW
+ err = sock->conn->last_err = raw_sendto(sock->conn->pcb.raw, p, ipX_2_ip(remote_addr));
+#else /* LWIP_RAW */
+ err = ERR_ARG;
+#endif /* LWIP_RAW */
+ }
+#if LWIP_UDP && LWIP_RAW
+ else
+#endif /* LWIP_UDP && LWIP_RAW */
+ {
+#if LWIP_UDP
+#if LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF
+ err = sock->conn->last_err = udp_sendto_chksum(sock->conn->pcb.udp, p,
+ ipX_2_ip(remote_addr), remote_port, 1, chksum);
+#else /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */
+ err = sock->conn->last_err = udp_sendto(sock->conn->pcb.udp, p,
+ ipX_2_ip(remote_addr), remote_port);
+#endif /* LWIP_CHECKSUM_ON_COPY && LWIP_NETIF_TX_SINGLE_PBUF */
+#else /* LWIP_UDP */
+ err = ERR_ARG;
+#endif /* LWIP_UDP */
+ }
+ UNLOCK_TCPIP_CORE();
+
+ pbuf_free(p);
+ } else {
+ err = ERR_MEM;
+ }
+ }
+#else /* LWIP_TCPIP_CORE_LOCKING */
+ /* initialize a buffer */
+ buf.p = buf.ptr = NULL;
+#if LWIP_CHECKSUM_ON_COPY
+ buf.flags = 0;
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ if (to) {
+ SOCKADDR_TO_IPXADDR_PORT((to->sa_family) == AF_INET6, to, &buf.addr, remote_port);
+ } else {
+ remote_port = 0;
+ ipX_addr_set_any(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)), &buf.addr);
+ }
+ netbuf_fromport(&buf) = remote_port;
+
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_sendto(%d, data=%p, short_size=%"U16_F", flags=0x%x to=",
+ s, data, short_size, flags));
+ ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)),
+ SOCKETS_DEBUG, &buf.addr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F"\n", remote_port));
+
+ /* make the buffer point to the data that should be sent */
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ /* Allocate a new netbuf and copy the data into it. */
+ if (netbuf_alloc(&buf, short_size) == NULL) {
+ err = ERR_MEM;
+ } else {
+#if LWIP_CHECKSUM_ON_COPY
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_RAW) {
+ u16_t chksum = LWIP_CHKSUM_COPY(buf.p->payload, data, short_size);
+ netbuf_set_chksum(&buf, chksum);
+ err = ERR_OK;
+ } else
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ {
+ err = netbuf_take(&buf, data, short_size);
+ }
+ }
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ err = netbuf_ref(&buf, data, short_size);
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+ if (err == ERR_OK) {
+ /* send the data */
+ err = netconn_send(sock->conn, &buf);
+ }
+
+ /* deallocated the buffer */
+ netbuf_free(&buf);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+ sock_set_errno(sock, err_to_errno(err));
+ return (err == ERR_OK ? short_size : -1);
+}
+
+int
+lwip_socket(int domain, int type, int protocol)
+{
+ struct netconn *conn;
+ int i;
+
+#if !LWIP_IPV6
+ LWIP_UNUSED_ARG(domain); /* @todo: check this */
+#endif /* LWIP_IPV6 */
+
+ /* create a netconn */
+ switch (type) {
+ case SOCK_RAW:
+ conn = netconn_new_with_proto_and_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_RAW),
+ (u8_t)protocol, event_callback);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_RAW, %d) = ",
+ domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+ break;
+ case SOCK_DGRAM:
+ conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain,
+ ((protocol == IPPROTO_UDPLITE) ? NETCONN_UDPLITE : NETCONN_UDP)) ,
+ event_callback);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_DGRAM, %d) = ",
+ domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+ break;
+ case SOCK_STREAM:
+ conn = netconn_new_with_callback(DOMAIN_TO_NETCONN_TYPE(domain, NETCONN_TCP), event_callback);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%s, SOCK_STREAM, %d) = ",
+ domain == PF_INET ? "PF_INET" : "UNKNOWN", protocol));
+ if (conn != NULL) {
+ /* Prevent automatic window updates, we do this on our own! */
+ netconn_set_noautorecved(conn, 1);
+ }
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_socket(%d, %d/UNKNOWN, %d) = -1\n",
+ domain, type, protocol));
+ set_errno(EINVAL);
+ return -1;
+ }
+
+ if (!conn) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("-1 / ENOBUFS (could not create netconn)\n"));
+ set_errno(ENOBUFS);
+ return -1;
+ }
+
+ i = alloc_socket(conn, 0);
+
+ if (i == -1) {
+ netconn_delete(conn);
+ set_errno(ENFILE);
+ return -1;
+ }
+ conn->socket = i;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("%d\n", i));
+ set_errno(0);
+ return i;
+}
+
+int
+lwip_write(int s, const void *data, size_t size)
+{
+ return lwip_send(s, data, size, 0);
+}
+
+/**
+ * Go through the readset and writeset lists and see which socket of the sockets
+ * set in the sets has events. On return, readset, writeset and exceptset have
+ * the sockets enabled that had events.
+ *
+ * exceptset is not used for now!!!
+ *
+ * @param maxfdp1 the highest socket index in the sets
+ * @param readset_in: set of sockets to check for read events
+ * @param writeset_in: set of sockets to check for write events
+ * @param exceptset_in: set of sockets to check for error events
+ * @param readset_out: set of sockets that had read events
+ * @param writeset_out: set of sockets that had write events
+ * @param exceptset_out: set os sockets that had error events
+ * @return number of sockets that had events (read/write/exception) (>= 0)
+ */
+static int
+lwip_selscan(int maxfdp1, fd_set *readset_in, fd_set *writeset_in, fd_set *exceptset_in,
+ fd_set *readset_out, fd_set *writeset_out, fd_set *exceptset_out)
+{
+ int i, nready = 0;
+ fd_set lreadset, lwriteset, lexceptset;
+ struct lwip_sock *sock;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ FD_ZERO(&lreadset);
+ FD_ZERO(&lwriteset);
+ FD_ZERO(&lexceptset);
+
+ /* Go through each socket in each list to count number of sockets which
+ currently match */
+ for(i = 0; i < maxfdp1; i++) {
+ void* lastdata = NULL;
+ s16_t rcvevent = 0;
+ u16_t sendevent = 0;
+ u16_t errevent = 0;
+ /* First get the socket's status (protected)... */
+ SYS_ARCH_PROTECT(lev);
+ sock = tryget_socket(i);
+ if (sock != NULL) {
+ lastdata = sock->lastdata;
+ rcvevent = sock->rcvevent;
+ sendevent = sock->sendevent;
+ errevent = sock->errevent;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ /* ... then examine it: */
+ /* See if netconn of this socket is ready for read */
+ if (readset_in && FD_ISSET(i, readset_in) && ((lastdata != NULL) || (rcvevent > 0))) {
+ FD_SET(i, &lreadset);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for reading\n", i));
+ nready++;
+ }
+ /* See if netconn of this socket is ready for write */
+ if (writeset_in && FD_ISSET(i, writeset_in) && (sendevent != 0)) {
+ FD_SET(i, &lwriteset);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for writing\n", i));
+ nready++;
+ }
+ /* See if netconn of this socket had an error */
+ if (exceptset_in && FD_ISSET(i, exceptset_in) && (errevent != 0)) {
+ FD_SET(i, &lexceptset);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_selscan: fd=%d ready for exception\n", i));
+ nready++;
+ }
+ }
+ /* copy local sets to the ones provided as arguments */
+ *readset_out = lreadset;
+ *writeset_out = lwriteset;
+ *exceptset_out = lexceptset;
+
+ LWIP_ASSERT("nready >= 0", nready >= 0);
+ return nready;
+}
+
+/**
+ * Processing exceptset is not yet implemented.
+ */
+int
+lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
+ struct timeval *timeout)
+{
+ u32_t waitres = 0;
+ int nready;
+ fd_set lreadset, lwriteset, lexceptset;
+ u32_t msectimeout;
+ struct lwip_select_cb select_cb;
+ err_t err;
+ int i;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select(%d, %p, %p, %p, tvsec=%"S32_F" tvusec=%"S32_F")\n",
+ maxfdp1, (void *)readset, (void *) writeset, (void *) exceptset,
+ timeout ? (s32_t)timeout->tv_sec : (s32_t)-1,
+ timeout ? (s32_t)timeout->tv_usec : (s32_t)-1));
+
+ /* Go through each socket in each list to count number of sockets which
+ currently match */
+ nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+
+ /* If we don't have any current events, then suspend if we are supposed to */
+ if (!nready) {
+ if (timeout && timeout->tv_sec == 0 && timeout->tv_usec == 0) {
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: no timeout, returning 0\n"));
+ /* This is OK as the local fdsets are empty and nready is zero,
+ or we would have returned earlier. */
+ goto return_copy_fdsets;
+ }
+
+ /* None ready: add our semaphore to list:
+ We don't actually need any dynamic memory. Our entry on the
+ list is only valid while we are in this function, so it's ok
+ to use local variables. */
+
+ select_cb.next = NULL;
+ select_cb.prev = NULL;
+ select_cb.readset = readset;
+ select_cb.writeset = writeset;
+ select_cb.exceptset = exceptset;
+ select_cb.sem_signalled = 0;
+ err = sys_sem_new(&select_cb.sem, 0);
+ if (err != ERR_OK) {
+ /* failed to create semaphore */
+ set_errno(ENOMEM);
+ return -1;
+ }
+
+ /* Protect the select_cb_list */
+ SYS_ARCH_PROTECT(lev);
+
+ /* Put this select_cb on top of list */
+ select_cb.next = select_cb_list;
+ if (select_cb_list != NULL) {
+ select_cb_list->prev = &select_cb;
+ }
+ select_cb_list = &select_cb;
+ /* Increasing this counter tells even_callback that the list has changed. */
+ select_cb_ctr++;
+
+ /* Now we can safely unprotect */
+ SYS_ARCH_UNPROTECT(lev);
+
+ /* Increase select_waiting for each socket we are interested in */
+ for(i = 0; i < maxfdp1; i++) {
+ if ((readset && FD_ISSET(i, readset)) ||
+ (writeset && FD_ISSET(i, writeset)) ||
+ (exceptset && FD_ISSET(i, exceptset))) {
+ struct lwip_sock *sock = tryget_socket(i);
+ LWIP_ASSERT("sock != NULL", sock != NULL);
+ SYS_ARCH_PROTECT(lev);
+ sock->select_waiting++;
+ LWIP_ASSERT("sock->select_waiting > 0", sock->select_waiting > 0);
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ }
+
+ /* Call lwip_selscan again: there could have been events between
+ the last scan (whithout us on the list) and putting us on the list! */
+ nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+ if (!nready) {
+ /* Still none ready, just wait to be woken */
+ if (timeout == 0) {
+ /* Wait forever */
+ msectimeout = 0;
+ } else {
+ msectimeout = ((timeout->tv_sec * 1000) + ((timeout->tv_usec + 500)/1000));
+ if (msectimeout == 0) {
+ /* Wait 1ms at least (0 means wait forever) */
+ msectimeout = 1;
+ }
+ }
+
+ waitres = sys_arch_sem_wait(&select_cb.sem, msectimeout);
+ }
+ /* Increase select_waiting for each socket we are interested in */
+ for(i = 0; i < maxfdp1; i++) {
+ if ((readset && FD_ISSET(i, readset)) ||
+ (writeset && FD_ISSET(i, writeset)) ||
+ (exceptset && FD_ISSET(i, exceptset))) {
+ struct lwip_sock *sock = tryget_socket(i);
+ LWIP_ASSERT("sock != NULL", sock != NULL);
+ SYS_ARCH_PROTECT(lev);
+ sock->select_waiting--;
+ LWIP_ASSERT("sock->select_waiting >= 0", sock->select_waiting >= 0);
+ SYS_ARCH_UNPROTECT(lev);
+ }
+ }
+ /* Take us off the list */
+ SYS_ARCH_PROTECT(lev);
+ if (select_cb.next != NULL) {
+ select_cb.next->prev = select_cb.prev;
+ }
+ if (select_cb_list == &select_cb) {
+ LWIP_ASSERT("select_cb.prev == NULL", select_cb.prev == NULL);
+ select_cb_list = select_cb.next;
+ } else {
+ LWIP_ASSERT("select_cb.prev != NULL", select_cb.prev != NULL);
+ select_cb.prev->next = select_cb.next;
+ }
+ /* Increasing this counter tells even_callback that the list has changed. */
+ select_cb_ctr++;
+ SYS_ARCH_UNPROTECT(lev);
+
+ sys_sem_free(&select_cb.sem);
+ if (waitres == SYS_ARCH_TIMEOUT) {
+ /* Timeout */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: timeout expired\n"));
+ /* This is OK as the local fdsets are empty and nready is zero,
+ or we would have returned earlier. */
+ goto return_copy_fdsets;
+ }
+
+ /* See what's set */
+ nready = lwip_selscan(maxfdp1, readset, writeset, exceptset, &lreadset, &lwriteset, &lexceptset);
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_select: nready=%d\n", nready));
+return_copy_fdsets:
+ set_errno(0);
+ if (readset) {
+ *readset = lreadset;
+ }
+ if (writeset) {
+ *writeset = lwriteset;
+ }
+ if (exceptset) {
+ *exceptset = lexceptset;
+ }
+ return nready;
+}
+
+/**
+ * Callback registered in the netconn layer for each socket-netconn.
+ * Processes recvevent (data available) and wakes up tasks waiting for select.
+ */
+static void
+event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
+{
+ int s;
+ struct lwip_sock *sock;
+ struct lwip_select_cb *scb;
+ int last_select_cb_ctr;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_UNUSED_ARG(len);
+
+ /* Get socket */
+ if (conn) {
+ s = conn->socket;
+ if (s < 0) {
+ /* Data comes in right away after an accept, even though
+ * the server task might not have created a new socket yet.
+ * Just count down (or up) if that's the case and we
+ * will use the data later. Note that only receive events
+ * can happen before the new socket is set up. */
+ SYS_ARCH_PROTECT(lev);
+ if (conn->socket < 0) {
+ if (evt == NETCONN_EVT_RCVPLUS) {
+ conn->socket--;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ return;
+ }
+ s = conn->socket;
+ SYS_ARCH_UNPROTECT(lev);
+ }
+
+ sock = get_socket(s);
+ if (!sock) {
+ return;
+ }
+ } else {
+ return;
+ }
+
+ SYS_ARCH_PROTECT(lev);
+ /* Set event as required */
+ switch (evt) {
+ case NETCONN_EVT_RCVPLUS:
+ sock->rcvevent++;
+ break;
+ case NETCONN_EVT_RCVMINUS:
+ sock->rcvevent--;
+ break;
+ case NETCONN_EVT_SENDPLUS:
+ sock->sendevent = 1;
+ break;
+ case NETCONN_EVT_SENDMINUS:
+ sock->sendevent = 0;
+ break;
+ case NETCONN_EVT_ERROR:
+ sock->errevent = 1;
+ break;
+ default:
+ LWIP_ASSERT("unknown event", 0);
+ break;
+ }
+
+ if (sock->select_waiting == 0) {
+ /* noone is waiting for this socket, no need to check select_cb_list */
+ SYS_ARCH_UNPROTECT(lev);
+ return;
+ }
+
+ /* Now decide if anyone is waiting for this socket */
+ /* NOTE: This code goes through the select_cb_list list multiple times
+ ONLY IF a select was actually waiting. We go through the list the number
+ of waiting select calls + 1. This list is expected to be small. */
+
+ /* At this point, SYS_ARCH is still protected! */
+again:
+ for (scb = select_cb_list; scb != NULL; scb = scb->next) {
+ if (scb->sem_signalled == 0) {
+ /* semaphore not signalled yet */
+ int do_signal = 0;
+ /* Test this select call for our socket */
+ if (sock->rcvevent > 0) {
+ if (scb->readset && FD_ISSET(s, scb->readset)) {
+ do_signal = 1;
+ }
+ }
+ if (sock->sendevent != 0) {
+ if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
+ do_signal = 1;
+ }
+ }
+ if (sock->errevent != 0) {
+ if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
+ do_signal = 1;
+ }
+ }
+ if (do_signal) {
+ scb->sem_signalled = 1;
+ /* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might
+ lead to the select thread taking itself off the list, invalidagin the semaphore. */
+ sys_sem_signal(&scb->sem);
+ }
+ }
+ /* unlock interrupts with each step */
+ last_select_cb_ctr = select_cb_ctr;
+ SYS_ARCH_UNPROTECT(lev);
+ /* this makes sure interrupt protection time is short */
+ SYS_ARCH_PROTECT(lev);
+ if (last_select_cb_ctr != select_cb_ctr) {
+ /* someone has changed select_cb_list, restart at the beginning */
+ goto again;
+ }
+ }
+ SYS_ARCH_UNPROTECT(lev);
+}
+
+/**
+ * Unimplemented: Close one end of a full-duplex connection.
+ * Currently, the full connection is closed.
+ */
+int
+lwip_shutdown(int s, int how)
+{
+ struct lwip_sock *sock;
+ err_t err;
+ u8_t shut_rx = 0, shut_tx = 0;
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_shutdown(%d, how=%d)\n", s, how));
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ if (sock->conn != NULL) {
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+ sock_set_errno(sock, EOPNOTSUPP);
+ return EOPNOTSUPP;
+ }
+ } else {
+ sock_set_errno(sock, ENOTCONN);
+ return ENOTCONN;
+ }
+
+ if (how == SHUT_RD) {
+ shut_rx = 1;
+ } else if (how == SHUT_WR) {
+ shut_tx = 1;
+ } else if(how == SHUT_RDWR) {
+ shut_rx = 1;
+ shut_tx = 1;
+ } else {
+ sock_set_errno(sock, EINVAL);
+ return EINVAL;
+ }
+ err = netconn_shutdown(sock->conn, shut_rx, shut_tx);
+
+ sock_set_errno(sock, err_to_errno(err));
+ return (err == ERR_OK ? 0 : -1);
+}
+
+static int
+lwip_getaddrname(int s, struct sockaddr *name, socklen_t *namelen, u8_t local)
+{
+ struct lwip_sock *sock;
+ union sockaddr_aligned saddr;
+ ipX_addr_t naddr;
+ u16_t port;
+
+ sock = get_socket(s);
+ if (!sock) {
+ return -1;
+ }
+
+ /* get the IP address and port */
+ /* @todo: this does not work for IPv6, yet */
+ netconn_getaddr(sock->conn, ipX_2_ip(&naddr), &port, local);
+ IPXADDR_PORT_TO_SOCKADDR(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)),
+ &saddr, &naddr, port);
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getaddrname(%d, addr=", s));
+ ipX_addr_debug_print(NETCONNTYPE_ISIPV6(netconn_type(sock->conn)),
+ SOCKETS_DEBUG, &naddr);
+ LWIP_DEBUGF(SOCKETS_DEBUG, (" port=%"U16_F")\n", port));
+
+ if (*namelen > saddr.sa.sa_len) {
+ *namelen = saddr.sa.sa_len;
+ }
+ MEMCPY(name, &saddr, *namelen);
+
+ sock_set_errno(sock, 0);
+ return 0;
+}
+
+int
+lwip_getpeername(int s, struct sockaddr *name, socklen_t *namelen)
+{
+ return lwip_getaddrname(s, name, namelen, 0);
+}
+
+int
+lwip_getsockname(int s, struct sockaddr *name, socklen_t *namelen)
+{
+ return lwip_getaddrname(s, name, namelen, 1);
+}
+
+int
+lwip_getsockopt(int s, int level, int optname, void *optval, socklen_t *optlen)
+{
+ err_t err = ERR_OK;
+ struct lwip_sock *sock = get_socket(s);
+ struct lwip_setgetsockopt_data data;
+
+ if (!sock) {
+ return -1;
+ }
+
+ if ((NULL == optval) || (NULL == optlen)) {
+ sock_set_errno(sock, EFAULT);
+ return -1;
+ }
+
+ /* Do length and type checks for the various options first, to keep it readable. */
+ switch (level) {
+
+/* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
+
+ case SO_ACCEPTCONN:
+ case SO_BROADCAST:
+ /* UNIMPL case SO_DEBUG: */
+ /* UNIMPL case SO_DONTROUTE: */
+ case SO_ERROR:
+ case SO_KEEPALIVE:
+ /* UNIMPL case SO_CONTIMEO: */
+#if LWIP_SO_SNDTIMEO
+ case SO_SNDTIMEO:
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+ case SO_RCVTIMEO:
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ case SO_RCVBUF:
+#endif /* LWIP_SO_RCVBUF */
+ /* UNIMPL case SO_OOBINLINE: */
+ /* UNIMPL case SO_SNDBUF: */
+ /* UNIMPL case SO_RCVLOWAT: */
+ /* UNIMPL case SO_SNDLOWAT: */
+#if SO_REUSE
+ case SO_REUSEADDR:
+ case SO_REUSEPORT:
+#endif /* SO_REUSE */
+ case SO_TYPE:
+ /* UNIMPL case SO_USELOOPBACK: */
+ if (*optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+ break;
+
+ case SO_NO_CHECK:
+ if (*optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+#if LWIP_UDP
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP ||
+ ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) {
+ /* this flag is only available for UDP, not for UDP lite */
+ err = EAFNOSUPPORT;
+ }
+#endif /* LWIP_UDP */
+ break;
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+
+/* Level: IPPROTO_IP */
+ case IPPROTO_IP:
+ switch (optname) {
+ /* UNIMPL case IP_HDRINCL: */
+ /* UNIMPL case IP_RCVDSTADDR: */
+ /* UNIMPL case IP_RCVIF: */
+ case IP_TTL:
+ case IP_TOS:
+ if (*optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+ break;
+#if LWIP_IGMP
+ case IP_MULTICAST_TTL:
+ if (*optlen < sizeof(u8_t)) {
+ err = EINVAL;
+ }
+ break;
+ case IP_MULTICAST_IF:
+ if (*optlen < sizeof(struct in_addr)) {
+ err = EINVAL;
+ }
+ break;
+ case IP_MULTICAST_LOOP:
+ if (*optlen < sizeof(u8_t)) {
+ err = EINVAL;
+ }
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+ err = EAFNOSUPPORT;
+ }
+ break;
+#endif /* LWIP_IGMP */
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+ case IPPROTO_TCP:
+ if (*optlen < sizeof(int)) {
+ err = EINVAL;
+ break;
+ }
+
+ /* If this is no TCP socket, ignore any options. */
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP)
+ return 0;
+
+ switch (optname) {
+ case TCP_NODELAY:
+ case TCP_KEEPALIVE:
+#if LWIP_TCP_KEEPALIVE
+ case TCP_KEEPIDLE:
+ case TCP_KEEPINTVL:
+ case TCP_KEEPCNT:
+#endif /* LWIP_TCP_KEEPALIVE */
+ break;
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_TCP */
+
+#if LWIP_IPV6
+/* Level: IPPROTO_IPV6 */
+ case IPPROTO_IPV6:
+ switch (optname) {
+ case IPV6_V6ONLY:
+ if (*optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+ /* @todo: this does not work for datagram sockets, yet */
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP)
+ return 0;
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_IPV6 */
+
+#if LWIP_UDP && LWIP_UDPLITE
+/* Level: IPPROTO_UDPLITE */
+ case IPPROTO_UDPLITE:
+ if (*optlen < sizeof(int)) {
+ err = EINVAL;
+ break;
+ }
+
+ /* If this is no UDP lite socket, ignore any options. */
+ if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn))) {
+ return 0;
+ }
+
+ switch (optname) {
+ case UDPLITE_SEND_CSCOV:
+ case UDPLITE_RECV_CSCOV:
+ break;
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_UDP && LWIP_UDPLITE*/
+/* UNDEFINED LEVEL */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+ s, level, optname));
+ err = ENOPROTOOPT;
+ } /* switch */
+
+
+ if (err != ERR_OK) {
+ sock_set_errno(sock, err);
+ return -1;
+ }
+
+ /* Now do the actual option processing */
+ data.sock = sock;
+#ifdef LWIP_DEBUG
+ data.s = s;
+#endif /* LWIP_DEBUG */
+ data.level = level;
+ data.optname = optname;
+ data.optval = optval;
+ data.optlen = optlen;
+ data.err = err;
+ tcpip_callback(lwip_getsockopt_internal, &data);
+ sys_arch_sem_wait(&sock->conn->op_completed, 0);
+ /* maybe lwip_getsockopt_internal has changed err */
+ err = data.err;
+
+ sock_set_errno(sock, err);
+ return err ? -1 : 0;
+}
+
+static void
+lwip_getsockopt_internal(void *arg)
+{
+ struct lwip_sock *sock;
+#ifdef LWIP_DEBUG
+ int s;
+#endif /* LWIP_DEBUG */
+ int level, optname;
+ void *optval;
+ struct lwip_setgetsockopt_data *data;
+
+ LWIP_ASSERT("arg != NULL", arg != NULL);
+
+ data = (struct lwip_setgetsockopt_data*)arg;
+ sock = data->sock;
+#ifdef LWIP_DEBUG
+ s = data->s;
+#endif /* LWIP_DEBUG */
+ level = data->level;
+ optname = data->optname;
+ optval = data->optval;
+
+ switch (level) {
+
+/* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
+
+ /* The option flags */
+ case SO_ACCEPTCONN:
+ case SO_BROADCAST:
+ /* UNIMPL case SO_DEBUG: */
+ /* UNIMPL case SO_DONTROUTE: */
+ case SO_KEEPALIVE:
+ /* UNIMPL case SO_OOBINCLUDE: */
+#if SO_REUSE
+ case SO_REUSEADDR:
+ case SO_REUSEPORT:
+#endif /* SO_REUSE */
+ /*case SO_USELOOPBACK: UNIMPL */
+ *(int*)optval = ip_get_option(sock->conn->pcb.ip, optname);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, optname=0x%x, ..) = %s\n",
+ s, optname, (*(int*)optval?"on":"off")));
+ break;
+
+ case SO_TYPE:
+ switch (NETCONNTYPE_GROUP(netconn_type(sock->conn))) {
+ case NETCONN_RAW:
+ *(int*)optval = SOCK_RAW;
+ break;
+ case NETCONN_TCP:
+ *(int*)optval = SOCK_STREAM;
+ break;
+ case NETCONN_UDP:
+ *(int*)optval = SOCK_DGRAM;
+ break;
+ default: /* unrecognized socket type */
+ *(int*)optval = netconn_type(sock->conn);
+ LWIP_DEBUGF(SOCKETS_DEBUG,
+ ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE): unrecognized socket type %d\n",
+ s, *(int *)optval));
+ } /* switch (netconn_type(sock->conn)) */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_TYPE) = %d\n",
+ s, *(int *)optval));
+ break;
+
+ case SO_ERROR:
+ /* only overwrite ERR_OK or tempoary errors */
+ if ((sock->err == 0) || (sock->err == EINPROGRESS)) {
+ sock_set_errno(sock, err_to_errno(sock->conn->last_err));
+ }
+ *(int *)optval = sock->err;
+ sock->err = 0;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, SOL_SOCKET, SO_ERROR) = %d\n",
+ s, *(int *)optval));
+ break;
+
+#if LWIP_SO_SNDTIMEO
+ case SO_SNDTIMEO:
+ *(int *)optval = netconn_get_sendtimeout(sock->conn);
+ break;
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+ case SO_RCVTIMEO:
+ *(int *)optval = netconn_get_recvtimeout(sock->conn);
+ break;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ case SO_RCVBUF:
+ *(int *)optval = netconn_get_recvbufsize(sock->conn);
+ break;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_UDP
+ case SO_NO_CHECK:
+ *(int*)optval = (udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_NOCHKSUM) ? 1 : 0;
+ break;
+#endif /* LWIP_UDP*/
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+
+/* Level: IPPROTO_IP */
+ case IPPROTO_IP:
+ switch (optname) {
+ case IP_TTL:
+ *(int*)optval = sock->conn->pcb.ip->ttl;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TTL) = %d\n",
+ s, *(int *)optval));
+ break;
+ case IP_TOS:
+ *(int*)optval = sock->conn->pcb.ip->tos;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_TOS) = %d\n",
+ s, *(int *)optval));
+ break;
+#if LWIP_IGMP
+ case IP_MULTICAST_TTL:
+ *(u8_t*)optval = sock->conn->pcb.ip->ttl;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_TTL) = %d\n",
+ s, *(int *)optval));
+ break;
+ case IP_MULTICAST_IF:
+ inet_addr_from_ipaddr((struct in_addr*)optval, &sock->conn->pcb.udp->multicast_ip);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_IF) = 0x%"X32_F"\n",
+ s, *(u32_t *)optval));
+ break;
+ case IP_MULTICAST_LOOP:
+ if ((sock->conn->pcb.udp->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) {
+ *(u8_t*)optval = 1;
+ } else {
+ *(u8_t*)optval = 0;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, IP_MULTICAST_LOOP) = %d\n",
+ s, *(int *)optval));
+ break;
+#endif /* LWIP_IGMP */
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+ case IPPROTO_TCP:
+ switch (optname) {
+ case TCP_NODELAY:
+ *(int*)optval = tcp_nagle_disabled(sock->conn->pcb.tcp);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_TCP, TCP_NODELAY) = %s\n",
+ s, (*(int*)optval)?"on":"off") );
+ break;
+ case TCP_KEEPALIVE:
+ *(int*)optval = (int)sock->conn->pcb.tcp->keep_idle;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPALIVE) = %d\n",
+ s, *(int *)optval));
+ break;
+
+#if LWIP_TCP_KEEPALIVE
+ case TCP_KEEPIDLE:
+ *(int*)optval = (int)(sock->conn->pcb.tcp->keep_idle/1000);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPIDLE) = %d\n",
+ s, *(int *)optval));
+ break;
+ case TCP_KEEPINTVL:
+ *(int*)optval = (int)(sock->conn->pcb.tcp->keep_intvl/1000);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPINTVL) = %d\n",
+ s, *(int *)optval));
+ break;
+ case TCP_KEEPCNT:
+ *(int*)optval = (int)sock->conn->pcb.tcp->keep_cnt;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IP, TCP_KEEPCNT) = %d\n",
+ s, *(int *)optval));
+ break;
+#endif /* LWIP_TCP_KEEPALIVE */
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_TCP */
+
+#if LWIP_IPV6
+/* Level: IPPROTO_IPV6 */
+ case IPPROTO_IPV6:
+ switch (optname) {
+ case IPV6_V6ONLY:
+ *(int*)optval = ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY) = %d\n",
+ s, *(int *)optval));
+ break;
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_IPV6 */
+
+#if LWIP_UDP && LWIP_UDPLITE
+ /* Level: IPPROTO_UDPLITE */
+ case IPPROTO_UDPLITE:
+ switch (optname) {
+ case UDPLITE_SEND_CSCOV:
+ *(int*)optval = sock->conn->pcb.udp->chksum_len_tx;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) = %d\n",
+ s, (*(int*)optval)) );
+ break;
+ case UDPLITE_RECV_CSCOV:
+ *(int*)optval = sock->conn->pcb.udp->chksum_len_rx;
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_getsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) = %d\n",
+ s, (*(int*)optval)) );
+ break;
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_UDP */
+ default:
+ LWIP_ASSERT("unhandled level", 0);
+ break;
+ } /* switch (level) */
+ sys_sem_signal(&sock->conn->op_completed);
+}
+
+int
+lwip_setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen)
+{
+ struct lwip_sock *sock = get_socket(s);
+ err_t err = ERR_OK;
+ struct lwip_setgetsockopt_data data;
+
+ if (!sock) {
+ return -1;
+ }
+
+ if (NULL == optval) {
+ sock_set_errno(sock, EFAULT);
+ return -1;
+ }
+
+ /* Do length and type checks for the various options first, to keep it readable. */
+ switch (level) {
+
+/* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
+
+ case SO_BROADCAST:
+ /* UNIMPL case SO_DEBUG: */
+ /* UNIMPL case SO_DONTROUTE: */
+ case SO_KEEPALIVE:
+ /* UNIMPL case case SO_CONTIMEO: */
+#if LWIP_SO_SNDTIMEO
+ case SO_SNDTIMEO:
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+ case SO_RCVTIMEO:
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ case SO_RCVBUF:
+#endif /* LWIP_SO_RCVBUF */
+ /* UNIMPL case SO_OOBINLINE: */
+ /* UNIMPL case SO_SNDBUF: */
+ /* UNIMPL case SO_RCVLOWAT: */
+ /* UNIMPL case SO_SNDLOWAT: */
+#if SO_REUSE
+ case SO_REUSEADDR:
+ case SO_REUSEPORT:
+#endif /* SO_REUSE */
+ /* UNIMPL case SO_USELOOPBACK: */
+ if (optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+ break;
+ case SO_NO_CHECK:
+ if (optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+#if LWIP_UDP
+ if ((NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) ||
+ ((udp_flags(sock->conn->pcb.udp) & UDP_FLAGS_UDPLITE) != 0)) {
+ /* this flag is only available for UDP, not for UDP lite */
+ err = EAFNOSUPPORT;
+ }
+#endif /* LWIP_UDP */
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+
+/* Level: IPPROTO_IP */
+ case IPPROTO_IP:
+ switch (optname) {
+ /* UNIMPL case IP_HDRINCL: */
+ /* UNIMPL case IP_RCVDSTADDR: */
+ /* UNIMPL case IP_RCVIF: */
+ case IP_TTL:
+ case IP_TOS:
+ if (optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+ break;
+#if LWIP_IGMP
+ case IP_MULTICAST_TTL:
+ if (optlen < sizeof(u8_t)) {
+ err = EINVAL;
+ }
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+ err = EAFNOSUPPORT;
+ }
+ break;
+ case IP_MULTICAST_IF:
+ if (optlen < sizeof(struct in_addr)) {
+ err = EINVAL;
+ }
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+ err = EAFNOSUPPORT;
+ }
+ break;
+ case IP_MULTICAST_LOOP:
+ if (optlen < sizeof(u8_t)) {
+ err = EINVAL;
+ }
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+ err = EAFNOSUPPORT;
+ }
+ break;
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ if (optlen < sizeof(struct ip_mreq)) {
+ err = EINVAL;
+ }
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_UDP) {
+ err = EAFNOSUPPORT;
+ }
+ break;
+#endif /* LWIP_IGMP */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+ case IPPROTO_TCP:
+ if (optlen < sizeof(int)) {
+ err = EINVAL;
+ break;
+ }
+
+ /* If this is no TCP socket, ignore any options. */
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP)
+ return 0;
+
+ switch (optname) {
+ case TCP_NODELAY:
+ case TCP_KEEPALIVE:
+#if LWIP_TCP_KEEPALIVE
+ case TCP_KEEPIDLE:
+ case TCP_KEEPINTVL:
+ case TCP_KEEPCNT:
+#endif /* LWIP_TCP_KEEPALIVE */
+ break;
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_TCP */
+
+#if LWIP_IPV6
+/* Level: IPPROTO_IPV6 */
+ case IPPROTO_IPV6:
+ switch (optname) {
+ case IPV6_V6ONLY:
+ if (optlen < sizeof(int)) {
+ err = EINVAL;
+ }
+
+ /* @todo: this does not work for datagram sockets, yet */
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP)
+ return 0;
+
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_IPV6 */
+
+#if LWIP_UDP && LWIP_UDPLITE
+/* Level: IPPROTO_UDPLITE */
+ case IPPROTO_UDPLITE:
+ if (optlen < sizeof(int)) {
+ err = EINVAL;
+ break;
+ }
+
+ /* If this is no UDP lite socket, ignore any options. */
+ if (!NETCONNTYPE_ISUDPLITE(netconn_type(sock->conn)))
+ return 0;
+
+ switch (optname) {
+ case UDPLITE_SEND_CSCOV:
+ case UDPLITE_RECV_CSCOV:
+ break;
+
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UNIMPL: optname=0x%x, ..)\n",
+ s, optname));
+ err = ENOPROTOOPT;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_UDP && LWIP_UDPLITE */
+/* UNDEFINED LEVEL */
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, level=0x%x, UNIMPL: optname=0x%x, ..)\n",
+ s, level, optname));
+ err = ENOPROTOOPT;
+ } /* switch (level) */
+
+
+ if (err != ERR_OK) {
+ sock_set_errno(sock, err);
+ return -1;
+ }
+
+
+ /* Now do the actual option processing */
+ data.sock = sock;
+#ifdef LWIP_DEBUG
+ data.s = s;
+#endif /* LWIP_DEBUG */
+ data.level = level;
+ data.optname = optname;
+ data.optval = (void*)optval;
+ data.optlen = &optlen;
+ data.err = err;
+ tcpip_callback(lwip_setsockopt_internal, &data);
+ sys_arch_sem_wait(&sock->conn->op_completed, 0);
+ /* maybe lwip_setsockopt_internal has changed err */
+ err = data.err;
+
+ sock_set_errno(sock, err);
+ return err ? -1 : 0;
+}
+
+static void
+lwip_setsockopt_internal(void *arg)
+{
+ struct lwip_sock *sock;
+#ifdef LWIP_DEBUG
+ int s;
+#endif /* LWIP_DEBUG */
+ int level, optname;
+ const void *optval;
+ struct lwip_setgetsockopt_data *data;
+
+ LWIP_ASSERT("arg != NULL", arg != NULL);
+
+ data = (struct lwip_setgetsockopt_data*)arg;
+ sock = data->sock;
+#ifdef LWIP_DEBUG
+ s = data->s;
+#endif /* LWIP_DEBUG */
+ level = data->level;
+ optname = data->optname;
+ optval = data->optval;
+
+ switch (level) {
+
+/* Level: SOL_SOCKET */
+ case SOL_SOCKET:
+ switch (optname) {
+
+ /* The option flags */
+ case SO_BROADCAST:
+ /* UNIMPL case SO_DEBUG: */
+ /* UNIMPL case SO_DONTROUTE: */
+ case SO_KEEPALIVE:
+ /* UNIMPL case SO_OOBINCLUDE: */
+#if SO_REUSE
+ case SO_REUSEADDR:
+ case SO_REUSEPORT:
+#endif /* SO_REUSE */
+ /* UNIMPL case SO_USELOOPBACK: */
+ if (*(int*)optval) {
+ ip_set_option(sock->conn->pcb.ip, optname);
+ } else {
+ ip_reset_option(sock->conn->pcb.ip, optname);
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, SOL_SOCKET, optname=0x%x, ..) -> %s\n",
+ s, optname, (*(int*)optval?"on":"off")));
+ break;
+#if LWIP_SO_SNDTIMEO
+ case SO_SNDTIMEO:
+ netconn_set_sendtimeout(sock->conn, (s32_t)*(int*)optval);
+ break;
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+ case SO_RCVTIMEO:
+ netconn_set_recvtimeout(sock->conn, *(int*)optval);
+ break;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ case SO_RCVBUF:
+ netconn_set_recvbufsize(sock->conn, *(int*)optval);
+ break;
+#endif /* LWIP_SO_RCVBUF */
+#if LWIP_UDP
+ case SO_NO_CHECK:
+ if (*(int*)optval) {
+ udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_NOCHKSUM);
+ } else {
+ udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_NOCHKSUM);
+ }
+ break;
+#endif /* LWIP_UDP */
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+
+/* Level: IPPROTO_IP */
+ case IPPROTO_IP:
+ switch (optname) {
+ case IP_TTL:
+ sock->conn->pcb.ip->ttl = (u8_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TTL, ..) -> %d\n",
+ s, sock->conn->pcb.ip->ttl));
+ break;
+ case IP_TOS:
+ sock->conn->pcb.ip->tos = (u8_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IP, IP_TOS, ..)-> %d\n",
+ s, sock->conn->pcb.ip->tos));
+ break;
+#if LWIP_IGMP
+ case IP_MULTICAST_TTL:
+ sock->conn->pcb.udp->ttl = (u8_t)(*(u8_t*)optval);
+ break;
+ case IP_MULTICAST_IF:
+ inet_addr_to_ipaddr(&sock->conn->pcb.udp->multicast_ip, (struct in_addr*)optval);
+ break;
+ case IP_MULTICAST_LOOP:
+ if (*(u8_t*)optval) {
+ udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) | UDP_FLAGS_MULTICAST_LOOP);
+ } else {
+ udp_setflags(sock->conn->pcb.udp, udp_flags(sock->conn->pcb.udp) & ~UDP_FLAGS_MULTICAST_LOOP);
+ }
+ break;
+ case IP_ADD_MEMBERSHIP:
+ case IP_DROP_MEMBERSHIP:
+ {
+ /* If this is a TCP or a RAW socket, ignore these options. */
+ struct ip_mreq *imr = (struct ip_mreq *)optval;
+ ip_addr_t if_addr;
+ ip_addr_t multi_addr;
+ inet_addr_to_ipaddr(&if_addr, &imr->imr_interface);
+ inet_addr_to_ipaddr(&multi_addr, &imr->imr_multiaddr);
+ if(optname == IP_ADD_MEMBERSHIP){
+ data->err = igmp_joingroup(&if_addr, &multi_addr);
+ } else {
+ data->err = igmp_leavegroup(&if_addr, &multi_addr);
+ }
+ if(data->err != ERR_OK) {
+ data->err = EADDRNOTAVAIL;
+ }
+ }
+ break;
+#endif /* LWIP_IGMP */
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+
+#if LWIP_TCP
+/* Level: IPPROTO_TCP */
+ case IPPROTO_TCP:
+ switch (optname) {
+ case TCP_NODELAY:
+ if (*(int*)optval) {
+ tcp_nagle_disable(sock->conn->pcb.tcp);
+ } else {
+ tcp_nagle_enable(sock->conn->pcb.tcp);
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_NODELAY) -> %s\n",
+ s, (*(int *)optval)?"on":"off") );
+ break;
+ case TCP_KEEPALIVE:
+ sock->conn->pcb.tcp->keep_idle = (u32_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPALIVE) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_idle));
+ break;
+
+#if LWIP_TCP_KEEPALIVE
+ case TCP_KEEPIDLE:
+ sock->conn->pcb.tcp->keep_idle = 1000*(u32_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPIDLE) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_idle));
+ break;
+ case TCP_KEEPINTVL:
+ sock->conn->pcb.tcp->keep_intvl = 1000*(u32_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPINTVL) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_intvl));
+ break;
+ case TCP_KEEPCNT:
+ sock->conn->pcb.tcp->keep_cnt = (u32_t)(*(int*)optval);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_TCP, TCP_KEEPCNT) -> %"U32_F"\n",
+ s, sock->conn->pcb.tcp->keep_cnt));
+ break;
+#endif /* LWIP_TCP_KEEPALIVE */
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_TCP*/
+
+#if LWIP_IPV6
+/* Level: IPPROTO_IPV6 */
+ case IPPROTO_IPV6:
+ switch (optname) {
+ case IPV6_V6ONLY:
+ if (*(int*)optval) {
+ sock->conn->flags |= NETCONN_FLAG_IPV6_V6ONLY;
+ } else {
+ sock->conn->flags &= ~NETCONN_FLAG_IPV6_V6ONLY;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_IPV6, IPV6_V6ONLY, ..) -> %d\n",
+ s, ((sock->conn->flags & NETCONN_FLAG_IPV6_V6ONLY) ? 1 : 0)));
+ break;
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_IPV6 */
+
+#if LWIP_UDP && LWIP_UDPLITE
+ /* Level: IPPROTO_UDPLITE */
+ case IPPROTO_UDPLITE:
+ switch (optname) {
+ case UDPLITE_SEND_CSCOV:
+ if ((*(int*)optval != 0) && ((*(int*)optval < 8) || (*(int*)optval > 0xffff))) {
+ /* don't allow illegal values! */
+ sock->conn->pcb.udp->chksum_len_tx = 8;
+ } else {
+ sock->conn->pcb.udp->chksum_len_tx = (u16_t)*(int*)optval;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_SEND_CSCOV) -> %d\n",
+ s, (*(int*)optval)) );
+ break;
+ case UDPLITE_RECV_CSCOV:
+ if ((*(int*)optval != 0) && ((*(int*)optval < 8) || (*(int*)optval > 0xffff))) {
+ /* don't allow illegal values! */
+ sock->conn->pcb.udp->chksum_len_rx = 8;
+ } else {
+ sock->conn->pcb.udp->chksum_len_rx = (u16_t)*(int*)optval;
+ }
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_setsockopt(%d, IPPROTO_UDPLITE, UDPLITE_RECV_CSCOV) -> %d\n",
+ s, (*(int*)optval)) );
+ break;
+ default:
+ LWIP_ASSERT("unhandled optname", 0);
+ break;
+ } /* switch (optname) */
+ break;
+#endif /* LWIP_UDP */
+ default:
+ LWIP_ASSERT("unhandled level", 0);
+ break;
+ } /* switch (level) */
+ sys_sem_signal(&sock->conn->op_completed);
+}
+
+int
+lwip_ioctl(int s, long cmd, void *argp)
+{
+ struct lwip_sock *sock = get_socket(s);
+ u8_t val;
+#if LWIP_SO_RCVBUF
+ u16_t buflen = 0;
+ s16_t recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+
+ if (!sock) {
+ return -1;
+ }
+
+ switch (cmd) {
+#if LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE
+ case FIONREAD:
+ if (!argp) {
+ sock_set_errno(sock, EINVAL);
+ return -1;
+ }
+#if LWIP_FIONREAD_LINUXMODE
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+ struct pbuf *p;
+ if (sock->lastdata) {
+ p = ((struct netbuf *)sock->lastdata)->p;
+ } else {
+ struct netbuf *rxbuf;
+ err_t err;
+ if (sock->rcvevent <= 0) {
+ *((u16_t*)argp) = 0;
+ } else {
+ err = netconn_recv(sock->conn, &rxbuf);
+ if (err != ERR_OK) {
+ *((u16_t*)argp) = 0;
+ } else {
+ sock->lastdata = rxbuf;
+ *((u16_t*)argp) = rxbuf->p->tot_len;
+ }
+ }
+ }
+ return 0;
+ }
+#endif /* LWIP_FIONREAD_LINUXMODE */
+
+#if LWIP_SO_RCVBUF
+ /* we come here if either LWIP_FIONREAD_LINUXMODE==0 or this is a TCP socket */
+ SYS_ARCH_GET(sock->conn->recv_avail, recv_avail);
+ if (recv_avail < 0) {
+ recv_avail = 0;
+ }
+ *((u16_t*)argp) = (u16_t)recv_avail;
+
+ /* Check if there is data left from the last recv operation. /maq 041215 */
+ if (sock->lastdata) {
+ struct pbuf *p = (struct pbuf *)sock->lastdata;
+ if (NETCONNTYPE_GROUP(netconn_type(sock->conn)) != NETCONN_TCP) {
+ p = ((struct netbuf *)p)->p;
+ }
+ buflen = p->tot_len;
+ buflen -= sock->lastoffset;
+
+ *((u16_t*)argp) += buflen;
+ }
+
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONREAD, %p) = %"U16_F"\n", s, argp, *((u16_t*)argp)));
+ sock_set_errno(sock, 0);
+ return 0;
+#else /* LWIP_SO_RCVBUF */
+ break;
+#endif /* LWIP_SO_RCVBUF */
+#endif /* LWIP_SO_RCVBUF || LWIP_FIONREAD_LINUXMODE */
+
+ case FIONBIO:
+ val = 0;
+ if (argp && *(u32_t*)argp) {
+ val = 1;
+ }
+ netconn_set_nonblocking(sock->conn, val);
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, FIONBIO, %d)\n", s, val));
+ sock_set_errno(sock, 0);
+ return 0;
+
+ default:
+ break;
+ } /* switch (cmd) */
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_ioctl(%d, UNIMPL: 0x%lx, %p)\n", s, cmd, argp));
+ sock_set_errno(sock, ENOSYS); /* not yet implemented */
+ return -1;
+}
+
+/** A minimal implementation of fcntl.
+ * Currently only the commands F_GETFL and F_SETFL are implemented.
+ * Only the flag O_NONBLOCK is implemented.
+ */
+int
+lwip_fcntl(int s, int cmd, int val)
+{
+ struct lwip_sock *sock = get_socket(s);
+ int ret = -1;
+
+ if (!sock || !sock->conn) {
+ return -1;
+ }
+
+ switch (cmd) {
+ case F_GETFL:
+ ret = netconn_is_nonblocking(sock->conn) ? O_NONBLOCK : 0;
+ break;
+ case F_SETFL:
+ if ((val & ~O_NONBLOCK) == 0) {
+ /* only O_NONBLOCK, all other bits are zero */
+ netconn_set_nonblocking(sock->conn, val & O_NONBLOCK);
+ ret = 0;
+ }
+ break;
+ default:
+ LWIP_DEBUGF(SOCKETS_DEBUG, ("lwip_fcntl(%d, UNIMPL: %d, %d)\n", s, cmd, val));
+ break;
+ }
+ return ret;
+}
+
+#endif /* LWIP_SOCKET */
diff --git a/src/VBox/Devices/Network/lwip-new/src/api/tcpip.c b/src/VBox/Devices/Network/lwip-new/src/api/tcpip.c
new file mode 100644
index 00000000..fa79d73c
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/api/tcpip.c
@@ -0,0 +1,531 @@
+/**
+ * @file
+ * Sequential API Main thread module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sys.h"
+#include "lwip/memp.h"
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/tcpip.h"
+#include "lwip/init.h"
+#include "lwip/ip.h"
+#include "netif/etharp.h"
+#include "netif/ppp_oe.h"
+
+/* global variables */
+static tcpip_init_done_fn tcpip_init_done;
+static void *tcpip_init_done_arg;
+static sys_mbox_t mbox;
+
+#if LWIP_TCPIP_CORE_LOCKING
+/** The global semaphore to lock the stack. */
+sys_mutex_t lock_tcpip_core;
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+
+/**
+ * The main lwIP thread. This thread has exclusive access to lwIP core functions
+ * (unless access to them is not locked). Other threads communicate with this
+ * thread using message boxes.
+ *
+ * It also starts all the timers to make sure they are running in the right
+ * thread context.
+ *
+ * @param arg unused argument
+ */
+static void
+tcpip_thread(void *arg)
+{
+ struct tcpip_msg *msg;
+ LWIP_UNUSED_ARG(arg);
+
+ if (tcpip_init_done != NULL) {
+ tcpip_init_done(tcpip_init_done_arg);
+ }
+
+ LOCK_TCPIP_CORE();
+ while (1) { /* MAIN Loop */
+ UNLOCK_TCPIP_CORE();
+ LWIP_TCPIP_THREAD_ALIVE();
+ /* wait for a message, timeouts are processed while waiting */
+ sys_timeouts_mbox_fetch(&mbox, (void **)&msg);
+ LOCK_TCPIP_CORE();
+ switch (msg->type) {
+#if LWIP_NETCONN
+ case TCPIP_MSG_API:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: API message %p\n", (void *)msg));
+ msg->msg.apimsg->function(&(msg->msg.apimsg->msg));
+ break;
+#endif /* LWIP_NETCONN */
+
+#if !LWIP_TCPIP_CORE_LOCKING_INPUT
+ case TCPIP_MSG_INPKT:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: PACKET %p\n", (void *)msg));
+#if LWIP_ETHERNET
+ if (msg->msg.inp.netif->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
+ ethernet_input(msg->msg.inp.p, msg->msg.inp.netif);
+ } else
+#endif /* LWIP_ETHERNET */
+#if LWIP_IPV6
+ if ((*((unsigned char *)(msg->msg.inp.p->payload)) & 0xf0) == 0x60) {
+ ip6_input(msg->msg.inp.p, msg->msg.inp.netif);
+ } else
+#endif /* LWIP_IPV6 */
+ {
+ ip_input(msg->msg.inp.p, msg->msg.inp.netif);
+ }
+ memp_free(MEMP_TCPIP_MSG_INPKT, msg);
+ break;
+#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+
+#if LWIP_NETIF_API
+ case TCPIP_MSG_NETIFAPI:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: Netif API message %p\n", (void *)msg));
+ msg->msg.netifapimsg->function(&(msg->msg.netifapimsg->msg));
+ break;
+#endif /* LWIP_NETIF_API */
+
+#if LWIP_TCPIP_TIMEOUT
+ case TCPIP_MSG_TIMEOUT:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: TIMEOUT %p\n", (void *)msg));
+ sys_timeout(msg->msg.tmo.msecs, msg->msg.tmo.h, msg->msg.tmo.arg);
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ break;
+ case TCPIP_MSG_UNTIMEOUT:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: UNTIMEOUT %p\n", (void *)msg));
+ sys_untimeout(msg->msg.tmo.h, msg->msg.tmo.arg);
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ break;
+#endif /* LWIP_TCPIP_TIMEOUT */
+
+ case TCPIP_MSG_CALLBACK:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK %p\n", (void *)msg));
+ msg->msg.cb.function(msg->msg.cb.ctx);
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ break;
+
+ case TCPIP_MSG_CALLBACK_STATIC:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_STATIC %p\n", (void *)msg));
+ msg->msg.cb.function(msg->msg.cb.ctx);
+ break;
+
+#ifdef VBOX
+ case TCPIP_MSG_CALLBACK_TERMINATE:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: CALLBACK_TERMINATE %p\n", (void *)msg));
+ if (msg->msg.cb.function != NULL) {
+ msg->msg.cb.function(msg->msg.cb.ctx);
+ }
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ goto terminate;
+#endif
+
+ default:
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_thread: invalid message: %d\n", msg->type));
+ LWIP_ASSERT("tcpip_thread: invalid message", 0);
+ break;
+ }
+ }
+#ifdef VBOX
+ terminate:
+ /* XXX: TODO: lwip cleanup? */
+ UNLOCK_TCPIP_CORE();
+#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 inp the network interface on which the packet was received
+ */
+err_t
+tcpip_input(struct pbuf *p, struct netif *inp)
+{
+#if LWIP_TCPIP_CORE_LOCKING_INPUT
+ err_t ret;
+ LWIP_DEBUGF(TCPIP_DEBUG, ("tcpip_input: PACKET %p/%p\n", (void *)p, (void *)inp));
+ LOCK_TCPIP_CORE();
+#if LWIP_ETHERNET
+ if (inp->flags & (NETIF_FLAG_ETHARP | NETIF_FLAG_ETHERNET)) {
+ ret = ethernet_input(p, inp);
+ } else
+#endif /* LWIP_ETHERNET */
+ {
+ ret = ip_input(p, inp);
+ }
+ UNLOCK_TCPIP_CORE();
+ return ret;
+#else /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+ struct tcpip_msg *msg;
+
+ if (!sys_mbox_valid(&mbox)) {
+ return ERR_VAL;
+ }
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_INPKT);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_INPKT;
+ msg->msg.inp.p = p;
+ msg->msg.inp.netif = inp;
+ if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
+ memp_free(MEMP_TCPIP_MSG_INPKT, msg);
+ return ERR_MEM;
+ }
+ return ERR_OK;
+#endif /* LWIP_TCPIP_CORE_LOCKING_INPUT */
+}
+
+/**
+ * Call a specific function in the thread context of
+ * tcpip_thread for easy access synchronization.
+ * A function called in that way may access lwIP core code
+ * without fearing concurrent access.
+ *
+ * @param f the function to call
+ * @param ctx parameter passed to f
+ * @param block 1 to block until the request is posted, 0 to non-blocking mode
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+err_t
+tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block)
+{
+ struct tcpip_msg *msg;
+
+ if (sys_mbox_valid(&mbox)) {
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_CALLBACK;
+ msg->msg.cb.function = function;
+ msg->msg.cb.ctx = ctx;
+ if (block) {
+ sys_mbox_post(&mbox, msg);
+ } else {
+ if (sys_mbox_trypost(&mbox, msg) != ERR_OK) {
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+ return ERR_MEM;
+ }
+ }
+ return ERR_OK;
+ }
+ return ERR_VAL;
+}
+
+#if LWIP_TCPIP_TIMEOUT
+/**
+ * call sys_timeout in tcpip_thread
+ *
+ * @param msec time in milliseconds for timeout
+ * @param h function to be called on timeout
+ * @param arg argument to pass to timeout function h
+ * @return ERR_MEM on memory error, ERR_OK otherwise
+ */
+err_t
+tcpip_timeout(u32_t msecs, sys_timeout_handler h, void *arg)
+{
+ struct tcpip_msg *msg;
+
+ if (sys_mbox_valid(&mbox)) {
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_TIMEOUT;
+ msg->msg.tmo.msecs = msecs;
+ msg->msg.tmo.h = h;
+ msg->msg.tmo.arg = arg;
+ sys_mbox_post(&mbox, msg);
+ return ERR_OK;
+ }
+ return ERR_VAL;
+}
+
+/**
+ * call sys_untimeout in tcpip_thread
+ *
+ * @param msec time in milliseconds for timeout
+ * @param h function to be called on timeout
+ * @param arg argument to pass to timeout function h
+ * @return ERR_MEM on memory error, ERR_OK otherwise
+ */
+err_t
+tcpip_untimeout(sys_timeout_handler h, void *arg)
+{
+ struct tcpip_msg *msg;
+
+ if (sys_mbox_valid(&mbox)) {
+ msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return ERR_MEM;
+ }
+
+ msg->type = TCPIP_MSG_UNTIMEOUT;
+ msg->msg.tmo.h = h;
+ msg->msg.tmo.arg = arg;
+ sys_mbox_post(&mbox, msg);
+ return ERR_OK;
+ }
+ return ERR_VAL;
+}
+#endif /* LWIP_TCPIP_TIMEOUT */
+
+#if LWIP_NETCONN
+/**
+ * Call the lower part of a netconn_* function
+ * This function is then running in the thread context
+ * of tcpip_thread and has exclusive access to lwIP core code.
+ *
+ * @param apimsg a struct containing the function to call and its parameters
+ * @return ERR_OK if the function was called, another err_t if not
+ */
+err_t
+tcpip_apimsg(struct api_msg *apimsg)
+{
+ struct tcpip_msg msg;
+#ifdef LWIP_DEBUG
+ /* catch functions that don't set err */
+ apimsg->msg.err = ERR_VAL;
+#endif
+
+ if (sys_mbox_valid(&mbox)) {
+ msg.type = TCPIP_MSG_API;
+ msg.msg.apimsg = apimsg;
+ sys_mbox_post(&mbox, &msg);
+ sys_arch_sem_wait(&apimsg->msg.conn->op_completed, 0);
+ return apimsg->msg.err;
+ }
+ return ERR_VAL;
+}
+
+#endif /* LWIP_NETCONN */
+
+#if LWIP_NETIF_API
+#if !LWIP_TCPIP_CORE_LOCKING
+/**
+ * Much like tcpip_apimsg, but calls the lower part of a netifapi_*
+ * function.
+ *
+ * @param netifapimsg a struct containing the function to call and its parameters
+ * @return error code given back by the function that was called
+ */
+err_t
+tcpip_netifapi(struct netifapi_msg* netifapimsg)
+{
+ struct tcpip_msg msg;
+
+ if (sys_mbox_valid(&mbox)) {
+ err_t err = sys_sem_new(&netifapimsg->msg.sem, 0);
+ if (err != ERR_OK) {
+ netifapimsg->msg.err = err;
+ return err;
+ }
+
+ msg.type = TCPIP_MSG_NETIFAPI;
+ msg.msg.netifapimsg = netifapimsg;
+ sys_mbox_post(&mbox, &msg);
+ sys_sem_wait(&netifapimsg->msg.sem);
+ sys_sem_free(&netifapimsg->msg.sem);
+ return netifapimsg->msg.err;
+ }
+ return ERR_VAL;
+}
+#else /* !LWIP_TCPIP_CORE_LOCKING */
+/**
+ * Call the lower part of a netifapi_* function
+ * This function has exclusive access to lwIP core code by locking it
+ * before the function is called.
+ *
+ * @param netifapimsg a struct containing the function to call and its parameters
+ * @return ERR_OK (only for compatibility fo tcpip_netifapi())
+ */
+err_t
+tcpip_netifapi_lock(struct netifapi_msg* netifapimsg)
+{
+ LOCK_TCPIP_CORE();
+ netifapimsg->function(&(netifapimsg->msg));
+ UNLOCK_TCPIP_CORE();
+ return netifapimsg->msg.err;
+}
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+#endif /* LWIP_NETIF_API */
+
+/**
+ * Allocate a structure for a static callback message and initialize it.
+ * This is intended to be used to send "static" messages from interrupt context.
+ *
+ * @param function the function to call
+ * @param ctx parameter passed to function
+ * @return a struct pointer to pass to tcpip_trycallback().
+ */
+struct tcpip_callback_msg* tcpip_callbackmsg_new(tcpip_callback_fn function, void *ctx)
+{
+ struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg == NULL) {
+ return NULL;
+ }
+ msg->type = TCPIP_MSG_CALLBACK_STATIC;
+ msg->msg.cb.function = function;
+ msg->msg.cb.ctx = ctx;
+ return (struct tcpip_callback_msg*)msg;
+}
+
+/**
+ * Free a callback message allocated by tcpip_callbackmsg_new().
+ *
+ * @param msg the message to free
+ */
+void tcpip_callbackmsg_delete(struct tcpip_callback_msg* msg)
+{
+ memp_free(MEMP_TCPIP_MSG_API, msg);
+}
+
+/**
+ * Try to post a callback-message to the tcpip_thread mbox
+ * This is intended to be used to send "static" messages from interrupt context.
+ *
+ * @param msg pointer to the message to post
+ * @return sys_mbox_trypost() return code
+ */
+err_t
+tcpip_trycallback(struct tcpip_callback_msg* msg)
+{
+ if (!sys_mbox_valid(&mbox)) {
+ return ERR_VAL;
+ }
+ return sys_mbox_trypost(&mbox, msg);
+}
+
+/**
+ * Post a callback-message to the tcpip_thread mbox.
+ *
+ * This is used to send "static" messages. Not necessarily "from
+ * interrupt context" - avoiding unnecessary malloc() is always good.
+ *
+ * To prevent confusion and to provide API symmetry there's a
+ * counterpart macro tcpip_trycallbackmsg() that aliases
+ * tcpip_trycallback() above.
+ *
+ * @param msg pointer to the message to post
+ * @return sys_mbox_trypost() return code
+ */
+err_t
+tcpip_callbackmsg(struct tcpip_callback_msg* msg)
+{
+ if (!sys_mbox_valid(&mbox)) {
+ return ERR_VAL;
+ }
+ sys_mbox_post(&mbox, msg);
+ return ERR_OK;
+}
+
+/**
+ * Initialize this module:
+ * - initialize all sub modules
+ * - start the tcpip_thread
+ *
+ * @param initfunc a function to call when tcpip_thread is running and finished initializing
+ * @param arg argument to pass to initfunc
+ */
+void
+tcpip_init(tcpip_init_done_fn initfunc, void *arg)
+{
+ lwip_init();
+
+ tcpip_init_done = initfunc;
+ tcpip_init_done_arg = arg;
+ if(sys_mbox_new(&mbox, TCPIP_MBOX_SIZE) != ERR_OK) {
+ LWIP_ASSERT("failed to create tcpip_thread mbox", 0);
+ }
+#if LWIP_TCPIP_CORE_LOCKING
+ if(sys_mutex_new(&lock_tcpip_core) != ERR_OK) {
+ LWIP_ASSERT("failed to create lock_tcpip_core", 0);
+ }
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+
+ sys_thread_new(TCPIP_THREAD_NAME, tcpip_thread, NULL, TCPIP_THREAD_STACKSIZE, TCPIP_THREAD_PRIO);
+}
+
+/**
+ * Simple callback function used with tcpip_callback to free a pbuf
+ * (pbuf_free has a wrong signature for tcpip_callback)
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ */
+static void
+pbuf_free_int(void *p)
+{
+ struct pbuf *q = (struct pbuf *)p;
+ pbuf_free(q);
+}
+
+/**
+ * A simple wrapper function that allows you to free a pbuf from interrupt context.
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ * @return ERR_OK if callback could be enqueued, an err_t if not
+ */
+err_t
+pbuf_free_callback(struct pbuf *p)
+{
+ return tcpip_callback_with_block(pbuf_free_int, p, 0);
+}
+
+/**
+ * A simple wrapper function that allows you to free heap memory from
+ * interrupt context.
+ *
+ * @param m the heap memory to free
+ * @return ERR_OK if callback could be enqueued, an err_t if not
+ */
+err_t
+mem_free_callback(void *m)
+{
+ return tcpip_callback_with_block(mem_free, m, 0);
+}
+
+#endif /* !NO_SYS */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/Makefile.kup b/src/VBox/Devices/Network/lwip-new/src/core/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/Makefile.kup
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/def.c b/src/VBox/Devices/Network/lwip-new/src/core/def.c
new file mode 100644
index 00000000..352b5524
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/def.c
@@ -0,0 +1,108 @@
+/**
+ * @file
+ * Common functions used throughout the stack.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+
+/**
+ * These are reference implementations of the byte swapping functions.
+ * Again with the aim of being simple, correct and fully portable.
+ * Byte swapping is the second thing you would want to optimize. You will
+ * need to port it to your architecture and in your cc.h:
+ *
+ * #define LWIP_PLATFORM_BYTESWAP 1
+ * #define LWIP_PLATFORM_HTONS(x) <your_htons>
+ * #define LWIP_PLATFORM_HTONL(x) <your_htonl>
+ *
+ * Note ntohs() and ntohl() are merely references to the htonx counterparts.
+ */
+
+#if (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN)
+
+/**
+ * Convert an u16_t from host- to network byte order.
+ *
+ * @param n u16_t in host byte order
+ * @return n in network byte order
+ */
+u16_t
+lwip_htons(u16_t n)
+{
+ return ((n & 0xff) << 8) | ((n & 0xff00) >> 8);
+}
+
+/**
+ * Convert an u16_t from network- to host byte order.
+ *
+ * @param n u16_t in network byte order
+ * @return n in host byte order
+ */
+u16_t
+lwip_ntohs(u16_t n)
+{
+ return lwip_htons(n);
+}
+
+/**
+ * Convert an u32_t from host- to network byte order.
+ *
+ * @param n u32_t in host byte order
+ * @return n in network byte order
+ */
+u32_t
+lwip_htonl(u32_t n)
+{
+ return ((n & 0xff) << 24) |
+ ((n & 0xff00) << 8) |
+ ((n & 0xff0000UL) >> 8) |
+ ((n & 0xff000000UL) >> 24);
+}
+
+/**
+ * Convert an u32_t from network- to host byte order.
+ *
+ * @param n u32_t in network byte order
+ * @return n in host byte order
+ */
+u32_t
+lwip_ntohl(u32_t n)
+{
+ return lwip_htonl(n);
+}
+
+#endif /* (LWIP_PLATFORM_BYTESWAP == 0) && (BYTE_ORDER == LITTLE_ENDIAN) */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/dhcp.c b/src/VBox/Devices/Network/lwip-new/src/core/dhcp.c
new file mode 100644
index 00000000..21fd7845
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/dhcp.c
@@ -0,0 +1,1771 @@
+/**
+ * @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/src/VBox/Devices/Network/lwip-new/src/core/dns.c b/src/VBox/Devices/Network/lwip-new/src/core/dns.c
new file mode 100644
index 00000000..90821a66
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/dns.c
@@ -0,0 +1,988 @@
+/**
+ * @file
+ * DNS - host name to IP address resolver.
+ *
+ */
+
+/**
+
+ * This file implements a DNS host name to IP address resolver.
+
+ * Port to lwIP from uIP
+ * by Jim Pettinato April 2007
+
+ * uIP version Copyright (c) 2002-2003, Adam Dunkels.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * DNS.C
+ *
+ * The lwIP DNS resolver functions are used to lookup a host name and
+ * map it to a numerical IP address. It maintains a list of resolved
+ * hostnames that can be queried with the dns_lookup() function.
+ * New hostnames can be resolved using the dns_query() function.
+ *
+ * The lwIP version of the resolver also adds a non-blocking version of
+ * gethostbyname() that will work with a raw API application. This function
+ * checks for an IP address string first and converts it if it is valid.
+ * gethostbyname() then does a dns_lookup() to see if the name is
+ * already in the table. If so, the IP is returned. If not, a query is
+ * issued and the function returns with a ERR_INPROGRESS status. The app
+ * using the dns client must then go into a waiting state.
+ *
+ * Once a hostname has been resolved (or found to be non-existent),
+ * the resolver code calls a specified callback function (which
+ * must be implemented by the module that uses the resolver).
+ */
+
+/*-----------------------------------------------------------------------------
+ * RFC 1035 - Domain names - implementation and specification
+ * RFC 2181 - Clarifications to the DNS Specification
+ *----------------------------------------------------------------------------*/
+
+/** @todo: define good default values (rfc compliance) */
+/** @todo: improve answer parsing, more checkings... */
+/** @todo: check RFC1035 - 7.3. Processing responses */
+
+/*-----------------------------------------------------------------------------
+ * Includes
+ *----------------------------------------------------------------------------*/
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/udp.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/dns.h"
+
+#include <string.h>
+
+/** DNS server IP address */
+#ifndef DNS_SERVER_ADDRESS
+#define DNS_SERVER_ADDRESS(ipaddr) (ip4_addr_set_u32(ipaddr, ipaddr_addr("208.67.222.222"))) /* resolver1.opendns.com */
+#endif
+
+/** DNS server port address */
+#ifndef DNS_SERVER_PORT
+#define DNS_SERVER_PORT 53
+#endif
+
+/** DNS maximum number of retries when asking for a name, before "timeout". */
+#ifndef DNS_MAX_RETRIES
+#define DNS_MAX_RETRIES 4
+#endif
+
+/** DNS resource record max. TTL (one week as default) */
+#ifndef DNS_MAX_TTL
+#define DNS_MAX_TTL 604800
+#endif
+
+/* DNS protocol flags */
+#define DNS_FLAG1_RESPONSE 0x80
+#define DNS_FLAG1_OPCODE_STATUS 0x10
+#define DNS_FLAG1_OPCODE_INVERSE 0x08
+#define DNS_FLAG1_OPCODE_STANDARD 0x00
+#define DNS_FLAG1_AUTHORATIVE 0x04
+#define DNS_FLAG1_TRUNC 0x02
+#define DNS_FLAG1_RD 0x01
+#define DNS_FLAG2_RA 0x80
+#define DNS_FLAG2_ERR_MASK 0x0f
+#define DNS_FLAG2_ERR_NONE 0x00
+#define DNS_FLAG2_ERR_NAME 0x03
+
+/* DNS protocol states */
+#define DNS_STATE_UNUSED 0
+#define DNS_STATE_NEW 1
+#define DNS_STATE_ASKING 2
+#define DNS_STATE_DONE 3
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** DNS message header */
+struct dns_hdr {
+ PACK_STRUCT_FIELD(u16_t id);
+ PACK_STRUCT_FIELD(u8_t flags1);
+ PACK_STRUCT_FIELD(u8_t flags2);
+ PACK_STRUCT_FIELD(u16_t numquestions);
+ PACK_STRUCT_FIELD(u16_t numanswers);
+ PACK_STRUCT_FIELD(u16_t numauthrr);
+ PACK_STRUCT_FIELD(u16_t numextrarr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+#define SIZEOF_DNS_HDR 12
+
+/** DNS query message structure.
+ No packing needed: only used locally on the stack. */
+struct dns_query {
+ /* DNS query record starts with either a domain name or a pointer
+ to a name already present somewhere in the packet. */
+ u16_t type;
+ u16_t cls;
+};
+#define SIZEOF_DNS_QUERY 4
+
+/** DNS answer message structure.
+ No packing needed: only used locally on the stack. */
+struct dns_answer {
+ /* DNS answer record starts with either a domain name or a pointer
+ to a name already present somewhere in the packet. */
+ u16_t type;
+ u16_t cls;
+ u32_t ttl;
+ u16_t len;
+};
+#define SIZEOF_DNS_ANSWER 10
+
+/** DNS table entry */
+struct dns_table_entry {
+ u8_t state;
+ u8_t numdns;
+ u8_t tmr;
+ u8_t retries;
+ u8_t seqno;
+ u8_t err;
+ u32_t ttl;
+ char name[DNS_MAX_NAME_LENGTH];
+ ip_addr_t ipaddr;
+ /* pointer to callback on DNS query done */
+ dns_found_callback found;
+ void *arg;
+};
+
+#if DNS_LOCAL_HOSTLIST
+
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+/** Local host-list. For hostnames in this list, no
+ * external name resolution is performed */
+static struct local_hostlist_entry *local_hostlist_dynamic;
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+/** Defining this allows the local_hostlist_static to be placed in a different
+ * linker section (e.g. FLASH) */
+#ifndef DNS_LOCAL_HOSTLIST_STORAGE_PRE
+#define DNS_LOCAL_HOSTLIST_STORAGE_PRE static
+#endif /* DNS_LOCAL_HOSTLIST_STORAGE_PRE */
+/** Defining this allows the local_hostlist_static to be placed in a different
+ * linker section (e.g. FLASH) */
+#ifndef DNS_LOCAL_HOSTLIST_STORAGE_POST
+#define DNS_LOCAL_HOSTLIST_STORAGE_POST
+#endif /* DNS_LOCAL_HOSTLIST_STORAGE_POST */
+DNS_LOCAL_HOSTLIST_STORAGE_PRE struct local_hostlist_entry local_hostlist_static[]
+ DNS_LOCAL_HOSTLIST_STORAGE_POST = DNS_LOCAL_HOSTLIST_INIT;
+
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+static void dns_init_local();
+#endif /* DNS_LOCAL_HOSTLIST */
+
+
+/* forward declarations */
+static void dns_recv(void *s, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
+static void dns_check_entries(void);
+
+/*-----------------------------------------------------------------------------
+ * Globales
+ *----------------------------------------------------------------------------*/
+
+/* DNS variables */
+static struct udp_pcb *dns_pcb;
+static u8_t dns_seqno;
+static struct dns_table_entry dns_table[DNS_TABLE_SIZE];
+static ip_addr_t dns_servers[DNS_MAX_SERVERS];
+/** Contiguous buffer for processing responses */
+static u8_t dns_payload_buffer[LWIP_MEM_ALIGN_BUFFER(DNS_MSG_SIZE)];
+static u8_t* dns_payload;
+
+/**
+ * Initialize the resolver: set up the UDP pcb and configure the default server
+ * (DNS_SERVER_ADDRESS).
+ */
+void
+dns_init()
+{
+ ip_addr_t dnsserver;
+
+ dns_payload = (u8_t *)LWIP_MEM_ALIGN(dns_payload_buffer);
+
+ /* initialize default DNS server address */
+ DNS_SERVER_ADDRESS(&dnsserver);
+
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_init: initializing\n"));
+
+ /* if dns client not yet initialized... */
+ if (dns_pcb == NULL) {
+ dns_pcb = udp_new();
+
+ if (dns_pcb != NULL) {
+ /* initialize DNS table not needed (initialized to zero since it is a
+ * global variable) */
+ LWIP_ASSERT("For implicit initialization to work, DNS_STATE_UNUSED needs to be 0",
+ DNS_STATE_UNUSED == 0);
+
+ /* initialize DNS client */
+ udp_bind(dns_pcb, IP_ADDR_ANY, 0);
+ udp_recv(dns_pcb, dns_recv, NULL);
+
+ /* initialize default DNS primary server */
+ dns_setserver(0, &dnsserver);
+ }
+ }
+#if DNS_LOCAL_HOSTLIST
+ dns_init_local();
+#endif
+}
+
+/**
+ * Initialize one of the DNS servers.
+ *
+ * @param numdns the index of the DNS server to set must be < DNS_MAX_SERVERS
+ * @param dnsserver IP address of the DNS server to set
+ */
+void
+dns_setserver(u8_t numdns, ip_addr_t *dnsserver)
+{
+ if ((numdns < DNS_MAX_SERVERS) && (dns_pcb != NULL) &&
+ (dnsserver != NULL) && !ip_addr_isany(dnsserver)) {
+ dns_servers[numdns] = (*dnsserver);
+ }
+}
+
+/**
+ * Obtain one of the currently configured DNS server.
+ *
+ * @param numdns the index of the DNS server
+ * @return IP address of the indexed DNS server or "ip_addr_any" if the DNS
+ * server has not been configured.
+ */
+ip_addr_t
+dns_getserver(u8_t numdns)
+{
+ if (numdns < DNS_MAX_SERVERS) {
+ return dns_servers[numdns];
+ } else {
+ return *IP_ADDR_ANY;
+ }
+}
+
+/**
+ * The DNS resolver client timer - handle retries and timeouts and should
+ * be called every DNS_TMR_INTERVAL milliseconds (every second by default).
+ */
+void
+dns_tmr(void)
+{
+ if (dns_pcb != NULL) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_tmr: dns_check_entries\n"));
+ dns_check_entries();
+ }
+}
+
+#if DNS_LOCAL_HOSTLIST
+static void
+dns_init_local()
+{
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT)
+ int i;
+ struct local_hostlist_entry *entry;
+ /* Dynamic: copy entries from DNS_LOCAL_HOSTLIST_INIT to list */
+ struct local_hostlist_entry local_hostlist_init[] = DNS_LOCAL_HOSTLIST_INIT;
+ size_t namelen;
+ for (i = 0; i < sizeof(local_hostlist_init) / sizeof(struct local_hostlist_entry); i++) {
+ struct local_hostlist_entry *init_entry = &local_hostlist_init[i];
+ LWIP_ASSERT("invalid host name (NULL)", init_entry->name != NULL);
+ namelen = strlen(init_entry->name);
+ LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
+ entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
+ LWIP_ASSERT("mem-error in dns_init_local", entry != NULL);
+ if (entry != NULL) {
+ entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
+ MEMCPY((char*)entry->name, init_entry->name, namelen);
+ ((char*)entry->name)[namelen] = 0;
+ entry->addr = init_entry->addr;
+ entry->next = local_hostlist_dynamic;
+ local_hostlist_dynamic = entry;
+ }
+ }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC && defined(DNS_LOCAL_HOSTLIST_INIT) */
+}
+
+/**
+ * Scans the local host-list for a hostname.
+ *
+ * @param hostname Hostname to look for in the local host-list
+ * @return The first IP address for the hostname in the local host-list or
+ * IPADDR_NONE if not found.
+ */
+static u32_t
+dns_lookup_local(const char *hostname)
+{
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+ struct local_hostlist_entry *entry = local_hostlist_dynamic;
+ while(entry != NULL) {
+ if(strcmp(entry->name, hostname) == 0) {
+ return ip4_addr_get_u32(&entry->addr);
+ }
+ entry = entry->next;
+ }
+#else /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+ int i;
+ for (i = 0; i < sizeof(local_hostlist_static) / sizeof(struct local_hostlist_entry); i++) {
+ if(strcmp(local_hostlist_static[i].name, hostname) == 0) {
+ return ip4_addr_get_u32(&local_hostlist_static[i].addr);
+ }
+ }
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+ return IPADDR_NONE;
+}
+
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+/** Remove all entries from the local host-list for a specific hostname
+ * and/or IP addess
+ *
+ * @param hostname hostname for which entries shall be removed from the local
+ * host-list
+ * @param addr address for which entries shall be removed from the local host-list
+ * @return the number of removed entries
+ */
+int
+dns_local_removehost(const char *hostname, const ip_addr_t *addr)
+{
+ int removed = 0;
+ struct local_hostlist_entry *entry = local_hostlist_dynamic;
+ struct local_hostlist_entry *last_entry = NULL;
+ while (entry != NULL) {
+ if (((hostname == NULL) || !strcmp(entry->name, hostname)) &&
+ ((addr == NULL) || ip_addr_cmp(&entry->addr, addr))) {
+ struct local_hostlist_entry *free_entry;
+ if (last_entry != NULL) {
+ last_entry->next = entry->next;
+ } else {
+ local_hostlist_dynamic = entry->next;
+ }
+ free_entry = entry;
+ entry = entry->next;
+ memp_free(MEMP_LOCALHOSTLIST, free_entry);
+ removed++;
+ } else {
+ last_entry = entry;
+ entry = entry->next;
+ }
+ }
+ return removed;
+}
+
+/**
+ * Add a hostname/IP address pair to the local host-list.
+ * Duplicates are not checked.
+ *
+ * @param hostname hostname of the new entry
+ * @param addr IP address of the new entry
+ * @return ERR_OK if succeeded or ERR_MEM on memory error
+ */
+err_t
+dns_local_addhost(const char *hostname, const ip_addr_t *addr)
+{
+ struct local_hostlist_entry *entry;
+ size_t namelen;
+ LWIP_ASSERT("invalid host name (NULL)", hostname != NULL);
+ namelen = strlen(hostname);
+ LWIP_ASSERT("namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN", namelen <= DNS_LOCAL_HOSTLIST_MAX_NAMELEN);
+ entry = (struct local_hostlist_entry *)memp_malloc(MEMP_LOCALHOSTLIST);
+ if (entry == NULL) {
+ return ERR_MEM;
+ }
+ entry->name = (char*)entry + sizeof(struct local_hostlist_entry);
+ MEMCPY((char*)entry->name, hostname, namelen);
+ ((char*)entry->name)[namelen] = 0;
+ ip_addr_copy(entry->addr, *addr);
+ entry->next = local_hostlist_dynamic;
+ local_hostlist_dynamic = entry;
+ return ERR_OK;
+}
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC*/
+#endif /* DNS_LOCAL_HOSTLIST */
+
+/**
+ * Look up a hostname in the array of known hostnames.
+ *
+ * @note This function only looks in the internal array of known
+ * hostnames, it does not send out a query for the hostname if none
+ * was found. The function dns_enqueue() can be used to send a query
+ * for a hostname.
+ *
+ * @param name the hostname to look up
+ * @return the hostname's IP address, as u32_t (instead of ip_addr_t to
+ * better check for failure: != IPADDR_NONE) or IPADDR_NONE if the hostname
+ * was not found in the cached dns_table.
+ */
+static u32_t
+dns_lookup(const char *name)
+{
+ u8_t i;
+#if DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN)
+ u32_t addr;
+#endif /* DNS_LOCAL_HOSTLIST || defined(DNS_LOOKUP_LOCAL_EXTERN) */
+#if DNS_LOCAL_HOSTLIST
+ if ((addr = dns_lookup_local(name)) != IPADDR_NONE) {
+ return addr;
+ }
+#endif /* DNS_LOCAL_HOSTLIST */
+#ifdef DNS_LOOKUP_LOCAL_EXTERN
+ if((addr = DNS_LOOKUP_LOCAL_EXTERN(name)) != IPADDR_NONE) {
+ return addr;
+ }
+#endif /* DNS_LOOKUP_LOCAL_EXTERN */
+
+ /* Walk through name list, return entry if found. If not, return NULL. */
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ if ((dns_table[i].state == DNS_STATE_DONE) &&
+ (strcmp(name, dns_table[i].name) == 0)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_lookup: \"%s\": found = ", name));
+ ip_addr_debug_print(DNS_DEBUG, &(dns_table[i].ipaddr));
+ LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+ return ip4_addr_get_u32(&dns_table[i].ipaddr);
+ }
+ }
+
+ return IPADDR_NONE;
+}
+
+#if DNS_DOES_NAME_CHECK
+/**
+ * Compare the "dotted" name "query" with the encoded name "response"
+ * to make sure an answer from the DNS server matches the current dns_table
+ * entry (otherwise, answers might arrive late for hostname not on the list
+ * any more).
+ *
+ * @param query hostname (not encoded) from the dns_table
+ * @param response encoded hostname in the DNS response
+ * @return 0: names equal; 1: names differ
+ */
+static u8_t
+dns_compare_name(unsigned char *query, unsigned char *response)
+{
+ unsigned char n;
+
+ do {
+ n = *response++;
+ /** @see RFC 1035 - 4.1.4. Message compression */
+ if ((n & 0xc0) == 0xc0) {
+ /* Compressed name */
+ break;
+ } else {
+ /* Not compressed name */
+ while (n > 0) {
+ if ((*query) != (*response)) {
+ return 1;
+ }
+ ++response;
+ ++query;
+ --n;
+ };
+ ++query;
+ }
+ } while (*response != 0);
+
+ return 0;
+}
+#endif /* DNS_DOES_NAME_CHECK */
+
+/**
+ * Walk through a compact encoded DNS name and return the end of the name.
+ *
+ * @param query encoded DNS name in the DNS server response
+ * @return end of the name
+ */
+static unsigned char *
+dns_parse_name(unsigned char *query)
+{
+ unsigned char n;
+
+ do {
+ n = *query++;
+ /** @see RFC 1035 - 4.1.4. Message compression */
+ if ((n & 0xc0) == 0xc0) {
+ /* Compressed name */
+ break;
+ } else {
+ /* Not compressed name */
+ while (n > 0) {
+ ++query;
+ --n;
+ };
+ }
+ } while (*query != 0);
+
+ return query + 1;
+}
+
+/**
+ * Send a DNS query packet.
+ *
+ * @param numdns index of the DNS server in the dns_servers table
+ * @param name hostname to query
+ * @param id index of the hostname in dns_table, used as transaction ID in the
+ * DNS query packet
+ * @return ERR_OK if packet is sent; an err_t indicating the problem otherwise
+ */
+static err_t
+dns_send(u8_t numdns, const char* name, u8_t id)
+{
+ err_t err;
+ struct dns_hdr *hdr;
+ struct dns_query qry;
+ struct pbuf *p;
+ char *query, *nptr;
+ const char *pHostname;
+ u8_t n;
+
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_send: dns_servers[%"U16_F"] \"%s\": request\n",
+ (u16_t)(numdns), name));
+ LWIP_ASSERT("dns server out of array", numdns < DNS_MAX_SERVERS);
+ LWIP_ASSERT("dns server has no IP address set", !ip_addr_isany(&dns_servers[numdns]));
+
+ /* if here, we have either a new query or a retry on a previous query to process */
+ p = pbuf_alloc(PBUF_TRANSPORT, SIZEOF_DNS_HDR + DNS_MAX_NAME_LENGTH + 1 +
+ SIZEOF_DNS_QUERY, PBUF_RAM);
+ if (p != NULL) {
+ u16_t realloc_size;
+ LWIP_ASSERT("pbuf must be in one piece", p->next == NULL);
+ /* fill dns header */
+ hdr = (struct dns_hdr*)p->payload;
+ memset(hdr, 0, SIZEOF_DNS_HDR);
+ hdr->id = htons(id);
+ hdr->flags1 = DNS_FLAG1_RD;
+ hdr->numquestions = PP_HTONS(1);
+ query = (char*)hdr + SIZEOF_DNS_HDR;
+ pHostname = name;
+ --pHostname;
+
+ /* convert hostname into suitable query format. */
+ do {
+ ++pHostname;
+ nptr = query;
+ ++query;
+ for(n = 0; *pHostname != '.' && *pHostname != 0; ++pHostname) {
+ *query = *pHostname;
+ ++query;
+ ++n;
+ }
+ *nptr = n;
+ } while(*pHostname != 0);
+ *query++='\0';
+
+ /* fill dns query */
+ qry.type = PP_HTONS(DNS_RRTYPE_A);
+ qry.cls = PP_HTONS(DNS_RRCLASS_IN);
+ SMEMCPY(query, &qry, SIZEOF_DNS_QUERY);
+
+ /* resize pbuf to the exact dns query */
+ realloc_size = (u16_t)((query + SIZEOF_DNS_QUERY) - ((char*)(p->payload)));
+ LWIP_ASSERT("p->tot_len >= realloc_size", p->tot_len >= realloc_size);
+ pbuf_realloc(p, realloc_size);
+
+ /* connect to the server for faster receiving */
+ udp_connect(dns_pcb, &dns_servers[numdns], DNS_SERVER_PORT);
+ /* send dns packet */
+ err = udp_sendto(dns_pcb, p, &dns_servers[numdns], DNS_SERVER_PORT);
+
+ /* free pbuf */
+ pbuf_free(p);
+ } else {
+ err = ERR_MEM;
+ }
+
+ return err;
+}
+
+/**
+ * dns_check_entry() - see if pEntry has not yet been queried and, if so, sends out a query.
+ * Check an entry in the dns_table:
+ * - send out query for new entries
+ * - retry old pending entries on timeout (also with different servers)
+ * - remove completed entries from the table if their TTL has expired
+ *
+ * @param i index of the dns_table entry to check
+ */
+static void
+dns_check_entry(u8_t i)
+{
+ err_t err;
+ struct dns_table_entry *pEntry = &dns_table[i];
+
+ LWIP_ASSERT("array index out of bounds", i < DNS_TABLE_SIZE);
+
+ switch(pEntry->state) {
+
+ case DNS_STATE_NEW: {
+ /* initialize new entry */
+ pEntry->state = DNS_STATE_ASKING;
+ pEntry->numdns = 0;
+ pEntry->tmr = 1;
+ pEntry->retries = 0;
+
+ /* send DNS packet for this entry */
+ err = dns_send(pEntry->numdns, pEntry->name, i);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
+ ("dns_send returned error: %s\n", lwip_strerr(err)));
+ }
+ break;
+ }
+
+ case DNS_STATE_ASKING: {
+ if (--pEntry->tmr == 0) {
+ if (++pEntry->retries == DNS_MAX_RETRIES) {
+ if ((pEntry->numdns+1<DNS_MAX_SERVERS) && !ip_addr_isany(&dns_servers[pEntry->numdns+1])) {
+ /* change of server */
+ pEntry->numdns++;
+ pEntry->tmr = 1;
+ pEntry->retries = 0;
+ break;
+ } else {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": timeout\n", pEntry->name));
+ /* call specified callback function if provided */
+ if (pEntry->found)
+ (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
+ /* flush this entry */
+ pEntry->state = DNS_STATE_UNUSED;
+ pEntry->found = NULL;
+ break;
+ }
+ }
+
+ /* wait longer for the next retry */
+ pEntry->tmr = pEntry->retries;
+
+ /* send DNS packet for this entry */
+ err = dns_send(pEntry->numdns, pEntry->name, i);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(DNS_DEBUG | LWIP_DBG_LEVEL_WARNING,
+ ("dns_send returned error: %s\n", lwip_strerr(err)));
+ }
+ }
+ break;
+ }
+
+ case DNS_STATE_DONE: {
+ /* if the time to live is nul */
+ if ((pEntry->ttl == 0) || (--pEntry->ttl == 0)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_check_entry: \"%s\": flush\n", pEntry->name));
+ /* flush this entry */
+ pEntry->state = DNS_STATE_UNUSED;
+ pEntry->found = NULL;
+ }
+ break;
+ }
+ case DNS_STATE_UNUSED:
+ /* nothing to do */
+ break;
+ default:
+ LWIP_ASSERT("unknown dns_table entry state:", 0);
+ break;
+ }
+}
+
+/**
+ * Call dns_check_entry for each entry in dns_table - check all entries.
+ */
+static void
+dns_check_entries(void)
+{
+ u8_t i;
+
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ dns_check_entry(i);
+ }
+}
+
+/**
+ * Receive input function for DNS response packets arriving for the dns UDP pcb.
+ *
+ * @params see udp.h
+ */
+static void
+dns_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
+{
+ u16_t i;
+ char *pHostname;
+ struct dns_hdr *hdr;
+ struct dns_answer ans;
+ struct dns_table_entry *pEntry;
+ u16_t nquestions, nanswers;
+
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(addr);
+ LWIP_UNUSED_ARG(port);
+
+ /* is the dns message too big ? */
+ if (p->tot_len > DNS_MSG_SIZE) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too big\n"));
+ /* free pbuf and return */
+ goto memerr;
+ }
+
+ /* is the dns message big enough ? */
+ if (p->tot_len < (SIZEOF_DNS_HDR + SIZEOF_DNS_QUERY + SIZEOF_DNS_ANSWER)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: pbuf too small\n"));
+ /* free pbuf and return */
+ goto memerr;
+ }
+
+ /* copy dns payload inside static buffer for processing */
+ if (pbuf_copy_partial(p, dns_payload, p->tot_len, 0) == p->tot_len) {
+ /* The ID in the DNS header should be our entry into the name table. */
+ hdr = (struct dns_hdr*)dns_payload;
+ i = htons(hdr->id);
+ if (i < DNS_TABLE_SIZE) {
+ pEntry = &dns_table[i];
+ if(pEntry->state == DNS_STATE_ASKING) {
+ /* This entry is now completed. */
+ pEntry->state = DNS_STATE_DONE;
+ pEntry->err = hdr->flags2 & DNS_FLAG2_ERR_MASK;
+
+ /* We only care about the question(s) and the answers. The authrr
+ and the extrarr are simply discarded. */
+ nquestions = htons(hdr->numquestions);
+ nanswers = htons(hdr->numanswers);
+
+ /* Check for error. If so, call callback to inform. */
+ if (((hdr->flags1 & DNS_FLAG1_RESPONSE) == 0) || (pEntry->err != 0) || (nquestions != 1)) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in flags\n", pEntry->name));
+ /* call callback to indicate error, clean up memory and return */
+ goto responseerr;
+ }
+
+#if DNS_DOES_NAME_CHECK
+ /* Check if the name in the "question" part match with the name in the entry. */
+ if (dns_compare_name((unsigned char *)(pEntry->name), (unsigned char *)dns_payload + SIZEOF_DNS_HDR) != 0) {
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response not match to query\n", pEntry->name));
+ /* call callback to indicate error, clean up memory and return */
+ goto responseerr;
+ }
+#endif /* DNS_DOES_NAME_CHECK */
+
+ /* Skip the name in the "question" part */
+ pHostname = (char *) dns_parse_name((unsigned char *)dns_payload + SIZEOF_DNS_HDR) + SIZEOF_DNS_QUERY;
+
+ while (nanswers > 0) {
+ /* skip answer resource record's host name */
+ pHostname = (char *) dns_parse_name((unsigned char *)pHostname);
+
+ /* Check for IP address type and Internet class. Others are discarded. */
+ SMEMCPY(&ans, pHostname, SIZEOF_DNS_ANSWER);
+ if((ans.type == PP_HTONS(DNS_RRTYPE_A)) && (ans.cls == PP_HTONS(DNS_RRCLASS_IN)) &&
+ (ans.len == PP_HTONS(sizeof(ip_addr_t))) ) {
+ /* read the answer resource record's TTL, and maximize it if needed */
+ pEntry->ttl = ntohl(ans.ttl);
+ if (pEntry->ttl > DNS_MAX_TTL) {
+ pEntry->ttl = DNS_MAX_TTL;
+ }
+ /* read the IP address after answer resource record's header */
+ SMEMCPY(&(pEntry->ipaddr), (pHostname+SIZEOF_DNS_ANSWER), sizeof(ip_addr_t));
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": response = ", pEntry->name));
+ ip_addr_debug_print(DNS_DEBUG, (&(pEntry->ipaddr)));
+ LWIP_DEBUGF(DNS_DEBUG, ("\n"));
+ /* call specified callback function if provided */
+ if (pEntry->found) {
+ (*pEntry->found)(pEntry->name, &pEntry->ipaddr, pEntry->arg);
+ }
+ if (pEntry->ttl == 0) {
+ /* RFC 883, page 29: "Zero values are
+ interpreted to mean that the RR can only be used for the
+ transaction in progress, and should not be cached."
+ -> flush this entry now */
+ goto flushentry;
+ }
+ /* deallocate memory and return */
+ goto memerr;
+ } else {
+ pHostname = pHostname + SIZEOF_DNS_ANSWER + htons(ans.len);
+ }
+ --nanswers;
+ }
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_recv: \"%s\": error in response\n", pEntry->name));
+ /* call callback to indicate error, clean up memory and return */
+ goto responseerr;
+ }
+ }
+ }
+
+ /* deallocate memory and return */
+ goto memerr;
+
+responseerr:
+ /* ERROR: call specified callback function with NULL as name to indicate an error */
+ if (pEntry->found) {
+ (*pEntry->found)(pEntry->name, NULL, pEntry->arg);
+ }
+flushentry:
+ /* flush this entry */
+ pEntry->state = DNS_STATE_UNUSED;
+ pEntry->found = NULL;
+
+memerr:
+ /* free pbuf */
+ pbuf_free(p);
+ return;
+}
+
+/**
+ * Queues a new hostname to resolve and sends out a DNS query for that hostname
+ *
+ * @param name the hostname that is to be queried
+ * @param hostnamelen length of the hostname
+ * @param found a callback founction to be called on success, failure or timeout
+ * @param callback_arg argument to pass to the callback function
+ * @return @return a err_t return code.
+ */
+static err_t
+dns_enqueue(const char *name, size_t hostnamelen, dns_found_callback found,
+ void *callback_arg)
+{
+ u8_t i;
+ u8_t lseq, lseqi;
+ struct dns_table_entry *pEntry = NULL;
+ size_t namelen;
+
+ /* search an unused entry, or the oldest one */
+ lseq = lseqi = 0;
+ for (i = 0; i < DNS_TABLE_SIZE; ++i) {
+ pEntry = &dns_table[i];
+ /* is it an unused entry ? */
+ if (pEntry->state == DNS_STATE_UNUSED)
+ break;
+
+ /* check if this is the oldest completed entry */
+ if (pEntry->state == DNS_STATE_DONE) {
+ if ((dns_seqno - pEntry->seqno) > lseq) {
+ lseq = dns_seqno - pEntry->seqno;
+ lseqi = i;
+ }
+ }
+ }
+
+ /* if we don't have found an unused entry, use the oldest completed one */
+ if (i == DNS_TABLE_SIZE) {
+ if ((lseqi >= DNS_TABLE_SIZE) || (dns_table[lseqi].state != DNS_STATE_DONE)) {
+ /* no entry can't be used now, table is full */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": DNS entries table is full\n", name));
+ return ERR_MEM;
+ } else {
+ /* use the oldest completed one */
+ i = lseqi;
+ pEntry = &dns_table[i];
+ }
+ }
+
+ /* use this entry */
+ LWIP_DEBUGF(DNS_DEBUG, ("dns_enqueue: \"%s\": use DNS entry %"U16_F"\n", name, (u16_t)(i)));
+
+ /* fill the entry */
+ pEntry->state = DNS_STATE_NEW;
+ pEntry->seqno = dns_seqno++;
+ pEntry->found = found;
+ pEntry->arg = callback_arg;
+ namelen = LWIP_MIN(hostnamelen, DNS_MAX_NAME_LENGTH-1);
+ MEMCPY(pEntry->name, name, namelen);
+ pEntry->name[namelen] = 0;
+
+ /* force to send query without waiting timer */
+ dns_check_entry(i);
+
+ /* dns query is enqueued */
+ return ERR_INPROGRESS;
+}
+
+/**
+ * Resolve a hostname (string) into an IP address.
+ * NON-BLOCKING callback version for use with raw API!!!
+ *
+ * Returns immediately with one of err_t return codes:
+ * - ERR_OK if hostname is a valid IP address string or the host
+ * name is already in the local names table.
+ * - ERR_INPROGRESS enqueue a request to be sent to the DNS server
+ * for resolution if no errors are present.
+ * - ERR_ARG: dns client not initialized or invalid hostname
+ *
+ * @param hostname the hostname that is to be queried
+ * @param addr pointer to a ip_addr_t where to store the address if it is already
+ * cached in the dns_table (only valid if ERR_OK is returned!)
+ * @param found a callback function to be called on success, failure or timeout (only if
+ * ERR_INPROGRESS is returned!)
+ * @param callback_arg argument to pass to the callback function
+ * @return a err_t return code.
+ */
+err_t
+dns_gethostbyname(const char *hostname, ip_addr_t *addr, dns_found_callback found,
+ void *callback_arg)
+{
+ u32_t ipaddr;
+ size_t hostnamelen;
+ /* not initialized or no valid server yet, or invalid addr pointer
+ * or invalid hostname or invalid hostname length */
+ if ((dns_pcb == NULL) || (addr == NULL) ||
+ (!hostname) || (!hostname[0])) {
+ return ERR_ARG;
+ }
+ hostnamelen = strlen(hostname);
+ if (hostnamelen >= DNS_MAX_NAME_LENGTH) {
+ return ERR_ARG;
+ }
+
+
+#if LWIP_HAVE_LOOPIF
+ if (strcmp(hostname, "localhost")==0) {
+ ip_addr_set_loopback(addr);
+ return ERR_OK;
+ }
+#endif /* LWIP_HAVE_LOOPIF */
+
+ /* host name already in octet notation? set ip addr and return ERR_OK */
+ ipaddr = ipaddr_addr(hostname);
+ if (ipaddr == IPADDR_NONE) {
+ /* already have this address cached? */
+ ipaddr = dns_lookup(hostname);
+ }
+ if (ipaddr != IPADDR_NONE) {
+ ip4_addr_set_u32(addr, ipaddr);
+ return ERR_OK;
+ }
+
+ /* queue query with specified callback */
+ return dns_enqueue(hostname, hostnamelen, found, callback_arg);
+}
+
+#endif /* LWIP_DNS */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/inet_chksum.c b/src/VBox/Devices/Network/lwip-new/src/core/inet_chksum.c
new file mode 100644
index 00000000..8bc42c14
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/inet_chksum.c
@@ -0,0 +1,545 @@
+/**
+ * @file
+ * Incluse internet checksum functions.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/inet_chksum.h"
+#include "lwip/def.h"
+
+#include <stddef.h>
+#include <string.h>
+
+/* These are some reference implementations of the checksum algorithm, with the
+ * aim of being simple, correct and fully portable. Checksumming is the
+ * first thing you would want to optimize for your platform. If you create
+ * your own version, link it in and in your cc.h put:
+ *
+ * #define LWIP_CHKSUM <your_checksum_routine>
+ *
+ * Or you can select from the implementations below by defining
+ * LWIP_CHKSUM_ALGORITHM to 1, 2 or 3.
+ */
+
+#ifndef LWIP_CHKSUM
+# define LWIP_CHKSUM lwip_standard_chksum
+# ifndef LWIP_CHKSUM_ALGORITHM
+# define LWIP_CHKSUM_ALGORITHM 2
+# endif
+u16_t lwip_standard_chksum(void *dataptr, int len);
+#endif
+/* If none set: */
+#ifndef LWIP_CHKSUM_ALGORITHM
+# define LWIP_CHKSUM_ALGORITHM 0
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 1) /* Version #1 */
+/**
+ * lwip checksum
+ *
+ * @param dataptr points to start of data to be summed at any boundary
+ * @param len length of data to be summed
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ *
+ * @note accumulator size limits summable length to 64k
+ * @note host endianess is irrelevant (p3 RFC1071)
+ */
+u16_t
+lwip_standard_chksum(void *dataptr, u16_t len)
+{
+ u32_t acc;
+ u16_t src;
+ u8_t *octetptr;
+
+ acc = 0;
+ /* dataptr may be at odd or even addresses */
+ octetptr = (u8_t*)dataptr;
+ while (len > 1) {
+ /* declare first octet as most significant
+ thus assume network order, ignoring host order */
+ src = (*octetptr) << 8;
+ octetptr++;
+ /* declare second octet as least significant */
+ src |= (*octetptr);
+ octetptr++;
+ acc += src;
+ len -= 2;
+ }
+ if (len > 0) {
+ /* accumulate remaining octet */
+ src = (*octetptr) << 8;
+ acc += src;
+ }
+ /* add deferred carry bits */
+ acc = (acc >> 16) + (acc & 0x0000ffffUL);
+ if ((acc & 0xffff0000UL) != 0) {
+ acc = (acc >> 16) + (acc & 0x0000ffffUL);
+ }
+ /* This maybe a little confusing: reorder sum using htons()
+ instead of ntohs() since it has a little less call overhead.
+ The caller must invert bits for Internet sum ! */
+ return htons((u16_t)acc);
+}
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 2) /* Alternative version #2 */
+/*
+ * Curt McDowell
+ * Broadcom Corp.
+ * csm@broadcom.com
+ *
+ * IP checksum two bytes at a time with support for
+ * unaligned buffer.
+ * Works for len up to and including 0x20000.
+ * by Curt McDowell, Broadcom Corp. 12/08/2005
+ *
+ * @param dataptr points to start of data to be summed at any boundary
+ * @param len length of data to be summed
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ */
+
+u16_t
+lwip_standard_chksum(void *dataptr, int len)
+{
+ u8_t *pb = (u8_t *)dataptr;
+ u16_t *ps, t = 0;
+ u32_t sum = 0;
+ int odd = ((mem_ptr_t)pb & 1);
+
+ /* Get aligned to u16_t */
+ if (odd && len > 0) {
+ ((u8_t *)&t)[1] = *pb++;
+ len--;
+ }
+
+ /* Add the bulk of the data */
+ ps = (u16_t *)(void *)pb;
+ while (len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ /* Consume left-over byte, if any */
+ if (len > 0) {
+ ((u8_t *)&t)[0] = *(u8_t *)ps;
+ }
+
+ /* Add end bytes */
+ sum += t;
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is propably faster than if statements... */
+ sum = FOLD_U32T(sum);
+ sum = FOLD_U32T(sum);
+
+ /* Swap if alignment was odd */
+ if (odd) {
+ sum = SWAP_BYTES_IN_WORD(sum);
+ }
+
+ return (u16_t)sum;
+}
+#endif
+
+#if (LWIP_CHKSUM_ALGORITHM == 3) /* Alternative version #3 */
+/**
+ * An optimized checksum routine. Basically, it uses loop-unrolling on
+ * the checksum loop, treating the head and tail bytes specially, whereas
+ * the inner loop acts on 8 bytes at a time.
+ *
+ * @arg start of buffer to be checksummed. May be an odd byte address.
+ * @len number of bytes in the buffer to be checksummed.
+ * @return host order (!) lwip checksum (non-inverted Internet sum)
+ *
+ * by Curt McDowell, Broadcom Corp. December 8th, 2005
+ */
+
+u16_t
+lwip_standard_chksum(void *dataptr, int len)
+{
+ u8_t *pb = (u8_t *)dataptr;
+ u16_t *ps, t = 0;
+ u32_t *pl;
+ u32_t sum = 0, tmp;
+ /* starts at odd byte address? */
+ int odd = ((mem_ptr_t)pb & 1);
+
+ if (odd && len > 0) {
+ ((u8_t *)&t)[1] = *pb++;
+ len--;
+ }
+
+ ps = (u16_t *)pb;
+
+ if (((mem_ptr_t)ps & 3) && len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ pl = (u32_t *)ps;
+
+ while (len > 7) {
+ tmp = sum + *pl++; /* ping */
+ if (tmp < sum) {
+ tmp++; /* add back carry */
+ }
+
+ sum = tmp + *pl++; /* pong */
+ if (sum < tmp) {
+ sum++; /* add back carry */
+ }
+
+ len -= 8;
+ }
+
+ /* make room in upper bits */
+ sum = FOLD_U32T(sum);
+
+ ps = (u16_t *)pl;
+
+ /* 16-bit aligned word remaining? */
+ while (len > 1) {
+ sum += *ps++;
+ len -= 2;
+ }
+
+ /* dangling tail byte remaining? */
+ if (len > 0) { /* include odd byte */
+ ((u8_t *)&t)[0] = *(u8_t *)ps;
+ }
+
+ sum += t; /* add end bytes */
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is propably faster than if statements... */
+ sum = FOLD_U32T(sum);
+ sum = FOLD_U32T(sum);
+
+ if (odd) {
+ sum = SWAP_BYTES_IN_WORD(sum);
+ }
+
+ return (u16_t)sum;
+}
+#endif
+
+/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */
+static u16_t
+inet_cksum_pseudo_base(struct pbuf *p, u8_t proto, u16_t proto_len, u32_t acc)
+{
+ struct pbuf *q;
+ u8_t swapped = 0;
+
+ /* iterate through all pbuf in chain */
+ for(q = p; q != NULL; q = q->next) {
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
+ (void *)q, (void *)q->next));
+ acc += LWIP_CHKSUM(q->payload, q->len);
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ /* just executing this next line is probably faster that the if statement needed
+ to check whether we really need to execute it, and does no harm */
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = 1 - swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+
+ acc += (u32_t)htons((u16_t)proto);
+ acc += (u32_t)htons(proto_len);
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is propably faster than if statements... */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+/* inet_chksum_pseudo:
+ *
+ * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+ ip_addr_t *src, ip_addr_t *dest)
+{
+ u32_t acc;
+ u32_t addr;
+
+ addr = ip4_addr_get_u32(src);
+ acc = (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ addr = ip4_addr_get_u32(dest);
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ /* fold down to 16 bits */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+
+ return inet_cksum_pseudo_base(p, proto, proto_len, acc);
+}
+#if LWIP_IPV6
+/**
+ * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain.
+ * IPv6 addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ipv6 address (used for checksum of pseudo header)
+ * @param dst destination ipv6 address (used for checksum of pseudo header)
+ * @param proto ipv6 protocol/next header (used for checksum of pseudo header)
+ * @param proto_len length of the ipv6 payload (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+ ip6_addr_t *src, ip6_addr_t *dest)
+{
+ u32_t acc = 0;
+ u32_t addr;
+ u8_t addr_part;
+
+ for (addr_part = 0; addr_part < 4; addr_part++) {
+ addr = src->addr[addr_part];
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ addr = dest->addr[addr_part];
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ }
+ /* fold down to 16 bits */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+
+ return inet_cksum_pseudo_base(p, proto, proto_len, acc);
+}
+#endif /* LWIP_IPV6 */
+
+/** Parts of the pseudo checksum which are common to IPv4 and IPv6 */
+static u16_t
+inet_cksum_pseudo_partial_base(struct pbuf *p, u8_t proto, u16_t proto_len,
+ u16_t chksum_len, u32_t acc)
+{
+ struct pbuf *q;
+ u8_t swapped = 0;
+ u16_t chklen;
+
+ /* iterate through all pbuf in chain */
+ for(q = p; (q != NULL) && (chksum_len > 0); q = q->next) {
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): checksumming pbuf %p (has next %p) \n",
+ (void *)q, (void *)q->next));
+ chklen = q->len;
+ if (chklen > chksum_len) {
+ chklen = chksum_len;
+ }
+ acc += LWIP_CHKSUM(q->payload, chklen);
+ chksum_len -= chklen;
+ LWIP_ASSERT("delete me", chksum_len < 0x7fff);
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): unwrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ /* fold the upper bit down */
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = 1 - swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ /*LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): wrapped lwip_chksum()=%"X32_F" \n", acc));*/
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+
+ acc += (u32_t)htons((u16_t)proto);
+ acc += (u32_t)htons(proto_len);
+
+ /* Fold 32-bit sum to 16 bits
+ calling this twice is propably faster than if statements... */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+ LWIP_DEBUGF(INET_DEBUG, ("inet_chksum_pseudo(): pbuf chain lwip_chksum()=%"X32_F"\n", acc));
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+/* inet_chksum_pseudo_partial:
+ *
+ * Calculates the pseudo Internet checksum used by TCP and UDP for a pbuf chain.
+ * IP addresses are expected to be in network byte order.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ip address (used for checksum of pseudo header)
+ * @param dst destination ip address (used for checksum of pseudo header)
+ * @param proto ip protocol (used for checksum of pseudo header)
+ * @param proto_len length of the ip data part (used for checksum of pseudo header)
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+ u16_t chksum_len, ip_addr_t *src, ip_addr_t *dest)
+{
+ u32_t acc;
+ u32_t addr;
+
+ addr = ip4_addr_get_u32(src);
+ acc = (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ addr = ip4_addr_get_u32(dest);
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ /* fold down to 16 bits */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+
+ return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc);
+}
+
+#if LWIP_IPV6
+/**
+ * Calculates the checksum with IPv6 pseudo header used by TCP and UDP for a pbuf chain.
+ * IPv6 addresses are expected to be in network byte order. Will only compute for a
+ * portion of the payload.
+ *
+ * @param p chain of pbufs over that a checksum should be calculated (ip data part)
+ * @param src source ipv6 address (used for checksum of pseudo header)
+ * @param dst destination ipv6 address (used for checksum of pseudo header)
+ * @param proto ipv6 protocol/next header (used for checksum of pseudo header)
+ * @param proto_len length of the ipv6 payload (used for checksum of pseudo header)
+ * @param chksum_len number of payload bytes used to compute chksum
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+ip6_chksum_pseudo_partial(struct pbuf *p, u8_t proto, u16_t proto_len,
+ u16_t chksum_len, ip6_addr_t *src, ip6_addr_t *dest)
+{
+ u32_t acc = 0;
+ u32_t addr;
+ u8_t addr_part;
+
+ for (addr_part = 0; addr_part < 4; addr_part++) {
+ addr = src->addr[addr_part];
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ addr = dest->addr[addr_part];
+ acc += (addr & 0xffffUL);
+ acc += ((addr >> 16) & 0xffffUL);
+ }
+ /* fold down to 16 bits */
+ acc = FOLD_U32T(acc);
+ acc = FOLD_U32T(acc);
+
+ return inet_cksum_pseudo_partial_base(p, proto, proto_len, chksum_len, acc);
+}
+#endif /* LWIP_IPV6 */
+
+/* inet_chksum:
+ *
+ * Calculates the Internet checksum over a portion of memory. Used primarily for IP
+ * and ICMP.
+ *
+ * @param dataptr start of the buffer to calculate the checksum (no alignment needed)
+ * @param len length of the buffer to calculate the checksum
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+
+u16_t
+inet_chksum(void *dataptr, u16_t len)
+{
+ return ~LWIP_CHKSUM(dataptr, len);
+}
+
+/**
+ * Calculate a checksum over a chain of pbufs (without pseudo-header, much like
+ * inet_chksum only pbufs are used).
+ *
+ * @param p pbuf chain over that the checksum should be calculated
+ * @return checksum (as u16_t) to be saved directly in the protocol header
+ */
+u16_t
+inet_chksum_pbuf(struct pbuf *p)
+{
+ u32_t acc;
+ struct pbuf *q;
+ u8_t swapped;
+
+ acc = 0;
+ swapped = 0;
+ for(q = p; q != NULL; q = q->next) {
+ acc += LWIP_CHKSUM(q->payload, q->len);
+ acc = FOLD_U32T(acc);
+ if (q->len % 2 != 0) {
+ swapped = 1 - swapped;
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ }
+
+ if (swapped) {
+ acc = SWAP_BYTES_IN_WORD(acc);
+ }
+ return (u16_t)~(acc & 0xffffUL);
+}
+
+/* These are some implementations for LWIP_CHKSUM_COPY, which copies data
+ * like MEMCPY but generates a checksum at the same time. Since this is a
+ * performance-sensitive function, you might want to create your own version
+ * in assembly targeted at your hardware by defining it in lwipopts.h:
+ * #define LWIP_CHKSUM_COPY(dst, src, len) your_chksum_copy(dst, src, len)
+ */
+
+#if (LWIP_CHKSUM_COPY_ALGORITHM == 1) /* Version #1 */
+/** Safe but slow: first call MEMCPY, then call LWIP_CHKSUM.
+ * For architectures with big caches, data might still be in cache when
+ * generating the checksum after copying.
+ */
+u16_t
+lwip_chksum_copy(void *dst, const void *src, u16_t len)
+{
+ MEMCPY(dst, src, len);
+ return LWIP_CHKSUM(dst, len);
+}
+#endif /* (LWIP_CHKSUM_COPY_ALGORITHM == 1) */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/init.c b/src/VBox/Devices/Network/lwip-new/src/core/init.c
new file mode 100644
index 00000000..80d5d649
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/init.c
@@ -0,0 +1,345 @@
+/**
+ * @file
+ * Modules initialization
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/init.h"
+#include "lwip/stats.h"
+#include "lwip/sys.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/sockets.h"
+#include "lwip/ip.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/snmp_msg.h"
+#include "lwip/autoip.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/timers.h"
+#include "netif/etharp.h"
+#include "lwip/ip6.h"
+#include "lwip/nd6.h"
+#include "lwip/mld6.h"
+#include "lwip/api.h"
+
+/* Compile-time sanity checks for configuration errors.
+ * These can be done independently of LWIP_DEBUG, without penalty.
+ */
+#ifndef BYTE_ORDER
+ #error "BYTE_ORDER is not defined, you have to define it in your cc.h"
+#endif
+#if (!IP_SOF_BROADCAST && IP_SOF_BROADCAST_RECV)
+ #error "If you want to use broadcast filter per pcb on recv operations, you have to define IP_SOF_BROADCAST=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_UDPLITE)
+ #error "If you want to use UDP Lite, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_SNMP)
+ #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_DHCP)
+ #error "If you want to use DHCP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_IGMP)
+ #error "If you want to use IGMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_SNMP)
+ #error "If you want to use SNMP, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_UDP && LWIP_DNS)
+ #error "If you want to use DNS, you have to define LWIP_UDP=1 in your lwipopts.h"
+#endif
+#if !MEMP_MEM_MALLOC /* MEMP_NUM_* checks are disabled when not using the pool allocator */
+#if (LWIP_ARP && ARP_QUEUEING && (MEMP_NUM_ARP_QUEUE<=0))
+ #error "If you want to use ARP Queueing, you have to define MEMP_NUM_ARP_QUEUE>=1 in your lwipopts.h"
+#endif
+#if (LWIP_RAW && (MEMP_NUM_RAW_PCB<=0))
+ #error "If you want to use RAW, you have to define MEMP_NUM_RAW_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_UDP && (MEMP_NUM_UDP_PCB<=0))
+ #error "If you want to use UDP, you have to define MEMP_NUM_UDP_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (MEMP_NUM_TCP_PCB<=0))
+ #error "If you want to use TCP, you have to define MEMP_NUM_TCP_PCB>=1 in your lwipopts.h"
+#endif
+#if (LWIP_IGMP && (MEMP_NUM_IGMP_GROUP<=1))
+ #error "If you want to use IGMP, you have to define MEMP_NUM_IGMP_GROUP>1 in your lwipopts.h"
+#endif
+#if ((LWIP_NETCONN || LWIP_SOCKET) && (MEMP_NUM_TCPIP_MSG_API<=0))
+ #error "If you want to use Sequential API, you have to define MEMP_NUM_TCPIP_MSG_API>=1 in your lwipopts.h"
+#endif
+/* There must be sufficient timeouts, taking into account requirements of the subsystems. */
+#if LWIP_TIMERS && (MEMP_NUM_SYS_TIMEOUT < (LWIP_TCP + IP_REASSEMBLY + LWIP_ARP + (2*LWIP_DHCP) + LWIP_AUTOIP + LWIP_IGMP + LWIP_DNS + PPP_SUPPORT + (LWIP_IPV6 ? (1 + LWIP_IPV6_REASS + LWIP_IPV6_MLD) : 0) + LWIP_CONNECTION_PROXY))
+ #error "MEMP_NUM_SYS_TIMEOUT is too low to accomodate all required timeouts"
+#endif
+#if (IP_REASSEMBLY && (MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS))
+ #error "MEMP_NUM_REASSDATA > IP_REASS_MAX_PBUFS doesn't make sense since each struct ip_reassdata must hold 2 pbufs at least!"
+#endif
+#endif /* !MEMP_MEM_MALLOC */
+#if (LWIP_TCP && (TCP_WND > 0xffff))
+ #error "If you want to use TCP, TCP_WND must fit in an u16_t, so, you have to reduce it in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (TCP_SND_QUEUELEN > 0xffff))
+ #error "If you want to use TCP, TCP_SND_QUEUELEN must fit in an u16_t, so, you have to reduce it in your lwipopts.h"
+#endif
+#if (LWIP_TCP && (TCP_SND_QUEUELEN < 2))
+ #error "TCP_SND_QUEUELEN must be at least 2 for no-copy TCP writes to work"
+#endif
+#if (LWIP_TCP && ((TCP_MAXRTX > 12) || (TCP_SYNMAXRTX > 12)))
+ #error "If you want to use TCP, TCP_MAXRTX and TCP_SYNMAXRTX must less or equal to 12 (due to tcp_backoff table), so, you have to reduce them in your lwipopts.h"
+#endif
+#if (LWIP_TCP && TCP_LISTEN_BACKLOG && (TCP_DEFAULT_LISTEN_BACKLOG < 0) || (TCP_DEFAULT_LISTEN_BACKLOG > 0xff))
+ #error "If you want to use TCP backlog, TCP_DEFAULT_LISTEN_BACKLOG must fit into an u8_t"
+#endif
+#if (LWIP_NETIF_API && (NO_SYS==1))
+ #error "If you want to use NETIF API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if ((LWIP_SOCKET || LWIP_NETCONN) && (NO_SYS==1))
+ #error "If you want to use Sequential API, you have to define NO_SYS=0 in your lwipopts.h"
+#endif
+#if (!LWIP_NETCONN && LWIP_SOCKET)
+ #error "If you want to use Socket API, you have to define LWIP_NETCONN=1 in your lwipopts.h"
+#endif
+#if (((!LWIP_DHCP) || (!LWIP_AUTOIP)) && LWIP_DHCP_AUTOIP_COOP)
+ #error "If you want to use DHCP/AUTOIP cooperation mode, you have to define LWIP_DHCP=1 and LWIP_AUTOIP=1 in your lwipopts.h"
+#endif
+#if (((!LWIP_DHCP) || (!LWIP_ARP)) && DHCP_DOES_ARP_CHECK)
+ #error "If you want to use DHCP ARP checking, you have to define LWIP_DHCP=1 and LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (!LWIP_ARP && LWIP_AUTOIP)
+ #error "If you want to use AUTOIP, you have to define LWIP_ARP=1 in your lwipopts.h"
+#endif
+#if (LWIP_SNMP && (SNMP_CONCURRENT_REQUESTS<=0))
+ #error "If you want to use SNMP, you have to define SNMP_CONCURRENT_REQUESTS>=1 in your lwipopts.h"
+#endif
+#if (LWIP_SNMP && (SNMP_TRAP_DESTINATIONS<=0))
+ #error "If you want to use SNMP, you have to define SNMP_TRAP_DESTINATIONS>=1 in your lwipopts.h"
+#endif
+#if (LWIP_TCP && ((LWIP_EVENT_API && LWIP_CALLBACK_API) || (!LWIP_EVENT_API && !LWIP_CALLBACK_API)))
+ #error "One and exactly one of LWIP_EVENT_API and LWIP_CALLBACK_API has to be enabled in your lwipopts.h"
+#endif
+#if (MEM_LIBC_MALLOC && MEM_USE_POOLS)
+ #error "MEM_LIBC_MALLOC and MEM_USE_POOLS may not both be simultaneously enabled in your lwipopts.h"
+#endif
+#if (MEM_USE_POOLS && !MEMP_USE_CUSTOM_POOLS)
+ #error "MEM_USE_POOLS requires custom pools (MEMP_USE_CUSTOM_POOLS) to be enabled in your lwipopts.h"
+#endif
+#if (PBUF_POOL_BUFSIZE <= MEM_ALIGNMENT)
+ #error "PBUF_POOL_BUFSIZE must be greater than MEM_ALIGNMENT or the offset may take the full first pbuf"
+#endif
+#if (DNS_LOCAL_HOSTLIST && !DNS_LOCAL_HOSTLIST_IS_DYNAMIC && !(defined(DNS_LOCAL_HOSTLIST_INIT)))
+ #error "you have to define define DNS_LOCAL_HOSTLIST_INIT {{'host1', 0x123}, {'host2', 0x234}} to initialize DNS_LOCAL_HOSTLIST"
+#endif
+#if PPP_SUPPORT && !PPPOS_SUPPORT & !PPPOE_SUPPORT
+ #error "PPP_SUPPORT needs either PPPOS_SUPPORT or PPPOE_SUPPORT turned on"
+#endif
+#if !LWIP_ETHERNET && (LWIP_ARP || PPPOE_SUPPORT)
+ #error "LWIP_ETHERNET needs to be turned on for LWIP_ARP or PPPOE_SUPPORT"
+#endif
+#if (LWIP_IGMP || LWIP_IPV6) && !defined(LWIP_RAND)
+ #error "When using IGMP or IPv6, LWIP_RAND() needs to be defined to a random-function returning an u32_t random value"
+#endif
+#if LWIP_TCPIP_CORE_LOCKING_INPUT && !LWIP_TCPIP_CORE_LOCKING
+ #error "When using LWIP_TCPIP_CORE_LOCKING_INPUT, LWIP_TCPIP_CORE_LOCKING must be enabled, too"
+#endif
+#if LWIP_TCP && LWIP_NETIF_TX_SINGLE_PBUF && !TCP_OVERSIZE
+ #error "LWIP_NETIF_TX_SINGLE_PBUF needs TCP_OVERSIZE enabled to create single-pbuf TCP packets"
+#endif
+#if IP_FRAG && IP_FRAG_USES_STATIC_BUF && LWIP_NETIF_TX_SINGLE_PBUF
+ #error "LWIP_NETIF_TX_SINGLE_PBUF does not work with IP_FRAG_USES_STATIC_BUF==1 as that creates pbuf queues"
+#endif
+#if LWIP_NETCONN && LWIP_TCP
+#if NETCONN_COPY != TCP_WRITE_FLAG_COPY
+ #error "NETCONN_COPY != TCP_WRITE_FLAG_COPY"
+#endif
+#if NETCONN_MORE != TCP_WRITE_FLAG_MORE
+ #error "NETCONN_MORE != TCP_WRITE_FLAG_MORE"
+#endif
+#endif /* LWIP_NETCONN && LWIP_TCP */
+#if LWIP_SOCKET
+/* Check that the SO_* socket options and SOF_* lwIP-internal flags match */
+#if SO_ACCEPTCONN != SOF_ACCEPTCONN
+ #error "SO_ACCEPTCONN != SOF_ACCEPTCONN"
+#endif
+#if SO_REUSEADDR != SOF_REUSEADDR
+ #error "WARNING: SO_REUSEADDR != SOF_REUSEADDR"
+#endif
+#if SO_KEEPALIVE != SOF_KEEPALIVE
+ #error "WARNING: SO_KEEPALIVE != SOF_KEEPALIVE"
+#endif
+#if SO_BROADCAST != SOF_BROADCAST
+ #error "WARNING: SO_BROADCAST != SOF_BROADCAST"
+#endif
+#if SO_LINGER != SOF_LINGER
+ #error "WARNING: SO_LINGER != SOF_LINGER"
+#endif
+#endif /* LWIP_SOCKET */
+
+
+/* Compile-time checks for deprecated options.
+ */
+#ifdef MEMP_NUM_TCPIP_MSG
+ #error "MEMP_NUM_TCPIP_MSG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef MEMP_NUM_API_MSG
+ #error "MEMP_NUM_API_MSG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef TCP_REXMIT_DEBUG
+ #error "TCP_REXMIT_DEBUG option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef RAW_STATS
+ #error "RAW_STATS option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef ETHARP_QUEUE_FIRST
+ #error "ETHARP_QUEUE_FIRST option is deprecated. Remove it from your lwipopts.h."
+#endif
+#ifdef ETHARP_ALWAYS_INSERT
+ #error "ETHARP_ALWAYS_INSERT option is deprecated. Remove it from your lwipopts.h."
+#endif
+
+#ifndef LWIP_DISABLE_TCP_SANITY_CHECKS
+#define LWIP_DISABLE_TCP_SANITY_CHECKS 0
+#endif
+#ifndef LWIP_DISABLE_MEMP_SANITY_CHECKS
+#define LWIP_DISABLE_MEMP_SANITY_CHECKS 0
+#endif
+
+/* MEMP sanity checks */
+#if !LWIP_DISABLE_MEMP_SANITY_CHECKS
+#if LWIP_NETCONN
+#if MEMP_MEM_MALLOC
+#if !MEMP_NUM_NETCONN && LWIP_SOCKET
+#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN cannot be 0 when using sockets!"
+#endif
+#else /* MEMP_MEM_MALLOC */
+#if MEMP_NUM_NETCONN > (MEMP_NUM_TCP_PCB+MEMP_NUM_TCP_PCB_LISTEN+MEMP_NUM_UDP_PCB+MEMP_NUM_RAW_PCB)
+#error "lwip_sanity_check: WARNING: MEMP_NUM_NETCONN should be less than the sum of MEMP_NUM_{TCP,RAW,UDP}_PCB+MEMP_NUM_TCP_PCB_LISTEN. If you know what you are doing, define LWIP_DISABLE_MEMP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#endif /* MEMP_MEM_MALLOC */
+#endif /* LWIP_NETCONN */
+#endif /* !LWIP_DISABLE_MEMP_SANITY_CHECKS */
+
+/* TCP sanity checks */
+#if !LWIP_DISABLE_TCP_SANITY_CHECKS
+#if LWIP_TCP
+#if !MEMP_MEM_MALLOC && (MEMP_NUM_TCP_SEG < TCP_SND_QUEUELEN)
+ #error "lwip_sanity_check: WARNING: MEMP_NUM_TCP_SEG should be at least as big as TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SND_BUF < (2 * TCP_MSS)
+ #error "lwip_sanity_check: WARNING: TCP_SND_BUF must be at least as much as (2 * TCP_MSS) for things to work smoothly. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SND_QUEUELEN < (2 * (TCP_SND_BUF / TCP_MSS))
+ #error "lwip_sanity_check: WARNING: TCP_SND_QUEUELEN must be at least as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SNDLOWAT >= TCP_SND_BUF
+ #error "lwip_sanity_check: WARNING: TCP_SNDLOWAT must be less than TCP_SND_BUF. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_SNDQUEUELOWAT >= TCP_SND_QUEUELEN
+ #error "lwip_sanity_check: WARNING: TCP_SNDQUEUELOWAT must be less than TCP_SND_QUEUELEN. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if !MEMP_MEM_MALLOC && (PBUF_POOL_BUFSIZE <= (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))
+ #error "lwip_sanity_check: WARNING: PBUF_POOL_BUFSIZE does not provide enough space for protocol headers. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if !MEMP_MEM_MALLOC && (TCP_WND > (PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - (PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN))))
+ #error "lwip_sanity_check: WARNING: TCP_WND is larger than space provided by PBUF_POOL_SIZE * (PBUF_POOL_BUFSIZE - protocol headers). If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#if TCP_WND < TCP_MSS
+ #error "lwip_sanity_check: WARNING: TCP_WND is smaller than MSS. If you know what you are doing, define LWIP_DISABLE_TCP_SANITY_CHECKS to 1 to disable this error."
+#endif
+#endif /* LWIP_TCP */
+#endif /* !LWIP_DISABLE_TCP_SANITY_CHECKS */
+
+/**
+ * Perform Sanity check of user-configurable values, and initialize all modules.
+ */
+void
+lwip_init(void)
+{
+ /* Modules initialization */
+ stats_init();
+#if !NO_SYS
+ sys_init();
+#endif /* !NO_SYS */
+ mem_init();
+ memp_init();
+ pbuf_init();
+ netif_init();
+#if LWIP_SOCKET
+ lwip_socket_init();
+#endif /* LWIP_SOCKET */
+ ip_init();
+#if LWIP_ARP
+ etharp_init();
+#endif /* LWIP_ARP */
+#if LWIP_RAW
+ raw_init();
+#endif /* LWIP_RAW */
+#if LWIP_UDP
+ udp_init();
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ tcp_init();
+#endif /* LWIP_TCP */
+#if LWIP_SNMP
+ snmp_init();
+#endif /* LWIP_SNMP */
+#if LWIP_AUTOIP
+ autoip_init();
+#endif /* LWIP_AUTOIP */
+#if LWIP_IGMP
+ igmp_init();
+#endif /* LWIP_IGMP */
+#if LWIP_DNS
+ dns_init();
+#endif /* LWIP_DNS */
+#if LWIP_IPV6
+ ip6_init();
+ nd6_init();
+#if LWIP_IPV6_MLD
+ mld6_init();
+#endif /* LWIP_IPV6_MLD */
+#endif /* LWIP_IPV6 */
+
+#if LWIP_TIMERS
+ sys_timeouts_init();
+#endif /* LWIP_TIMERS */
+}
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv4/Makefile.kup b/src/VBox/Devices/Network/lwip-new/src/core/ipv4/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv4/Makefile.kup
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv4/autoip.c b/src/VBox/Devices/Network/lwip-new/src/core/ipv4/autoip.c
new file mode 100644
index 00000000..b122da27
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv4/autoip.c
@@ -0,0 +1,528 @@
+/**
+ * @file
+ * AutoIP Automatic LinkLocal IP Configuration
+ *
+ */
+
+/*
+ *
+ * Copyright (c) 2007 Dominik Spies <kontakt@dspies.de>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Dominik Spies <kontakt@dspies.de>
+ *
+ * This is a AutoIP implementation for the lwIP TCP/IP stack. It aims to conform
+ * with RFC 3927.
+ *
+ *
+ * Please coordinate changes and requests with Dominik Spies
+ * <kontakt@dspies.de>
+ */
+
+/*******************************************************************************
+ * USAGE:
+ *
+ * define LWIP_AUTOIP 1 in your lwipopts.h
+ *
+ * If you don't use tcpip.c (so, don't call, you don't call tcpip_init):
+ * - First, call autoip_init().
+ * - call autoip_tmr() all AUTOIP_TMR_INTERVAL msces,
+ * that should be defined in autoip.h.
+ * I recommend a value of 100. The value must divide 1000 with a remainder almost 0.
+ * Possible values are 1000, 500, 333, 250, 200, 166, 142, 125, 111, 100 ....
+ *
+ * Without DHCP:
+ * - Call autoip_start() after netif_add().
+ *
+ * With DHCP:
+ * - define LWIP_DHCP_AUTOIP_COOP 1 in your lwipopts.h.
+ * - Configure your DHCP Client.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_AUTOIP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/mem.h"
+#include "lwip/udp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/autoip.h"
+#include "netif/etharp.h"
+
+#include <stdlib.h>
+#include <string.h>
+
+/* 169.254.0.0 */
+#define AUTOIP_NET 0xA9FE0000
+/* 169.254.1.0 */
+#define AUTOIP_RANGE_START (AUTOIP_NET | 0x0100)
+/* 169.254.254.255 */
+#define AUTOIP_RANGE_END (AUTOIP_NET | 0xFEFF)
+
+
+/** Pseudo random macro based on netif informations.
+ * You could use "rand()" from the C Library if you define LWIP_AUTOIP_RAND in lwipopts.h */
+#ifndef LWIP_AUTOIP_RAND
+#define LWIP_AUTOIP_RAND(netif) ( (((u32_t)((netif->hwaddr[5]) & 0xff) << 24) | \
+ ((u32_t)((netif->hwaddr[3]) & 0xff) << 16) | \
+ ((u32_t)((netif->hwaddr[2]) & 0xff) << 8) | \
+ ((u32_t)((netif->hwaddr[4]) & 0xff))) + \
+ (netif->autoip?netif->autoip->tried_llipaddr:0))
+#endif /* LWIP_AUTOIP_RAND */
+
+/**
+ * Macro that generates the initial IP address to be tried by AUTOIP.
+ * If you want to override this, define it to something else in lwipopts.h.
+ */
+#ifndef LWIP_AUTOIP_CREATE_SEED_ADDR
+#define LWIP_AUTOIP_CREATE_SEED_ADDR(netif) \
+ htonl(AUTOIP_RANGE_START + ((u32_t)(((u8_t)(netif->hwaddr[4])) | \
+ ((u32_t)((u8_t)(netif->hwaddr[5]))) << 8)))
+#endif /* LWIP_AUTOIP_CREATE_SEED_ADDR */
+
+/* static functions */
+static void autoip_handle_arp_conflict(struct netif *netif);
+
+/* creates a pseudo random LL IP-Address for a network interface */
+static void autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr);
+
+/* sends an ARP probe */
+static err_t autoip_arp_probe(struct netif *netif);
+
+/* sends an ARP announce */
+static err_t autoip_arp_announce(struct netif *netif);
+
+/* configure interface for use with current LL IP-Address */
+static err_t autoip_bind(struct netif *netif);
+
+/* start sending probes for llipaddr */
+static void autoip_start_probing(struct netif *netif);
+
+
+/** Set a statically allocated struct autoip to work with.
+ * Using this prevents autoip_start to allocate it using mem_malloc.
+ *
+ * @param netif the netif for which to set the struct autoip
+ * @param dhcp (uninitialised) dhcp struct allocated by the application
+ */
+void
+autoip_set_struct(struct netif *netif, struct autoip *autoip)
+{
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("autoip != NULL", autoip != NULL);
+ LWIP_ASSERT("netif already has a struct autoip set", netif->autoip == NULL);
+
+ /* clear data structure */
+ memset(autoip, 0, sizeof(struct autoip));
+ /* autoip->state = AUTOIP_STATE_OFF; */
+ netif->autoip = autoip;
+}
+
+/** Restart AutoIP client and check the next address (conflict detected)
+ *
+ * @param netif The netif under AutoIP control
+ */
+static void
+autoip_restart(struct netif *netif)
+{
+ netif->autoip->tried_llipaddr++;
+ autoip_start(netif);
+}
+
+/**
+ * Handle a IP address conflict after an ARP conflict detection
+ */
+static void
+autoip_handle_arp_conflict(struct netif *netif)
+{
+ /* Somehow detect if we are defending or retreating */
+ unsigned char defend = 1; /* tbd */
+
+ if (defend) {
+ if (netif->autoip->lastconflict > 0) {
+ /* retreat, there was a conflicting ARP in the last
+ * DEFEND_INTERVAL seconds
+ */
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_handle_arp_conflict(): we are defending, but in DEFEND_INTERVAL, retreating\n"));
+
+ /* TODO: close all TCP sessions */
+ autoip_restart(netif);
+ } else {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_handle_arp_conflict(): we are defend, send ARP Announce\n"));
+ autoip_arp_announce(netif);
+ netif->autoip->lastconflict = DEFEND_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ }
+ } else {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_handle_arp_conflict(): we do not defend, retreating\n"));
+ /* TODO: close all TCP sessions */
+ autoip_restart(netif);
+ }
+}
+
+/**
+ * Create an IP-Address out of range 169.254.1.0 to 169.254.254.255
+ *
+ * @param netif network interface on which create the IP-Address
+ * @param ipaddr ip address to initialize
+ */
+static void
+autoip_create_addr(struct netif *netif, ip_addr_t *ipaddr)
+{
+ /* Here we create an IP-Address out of range 169.254.1.0 to 169.254.254.255
+ * compliant to RFC 3927 Section 2.1
+ * We have 254 * 256 possibilities */
+
+ u32_t addr = ntohl(LWIP_AUTOIP_CREATE_SEED_ADDR(netif));
+ addr += netif->autoip->tried_llipaddr;
+ addr = AUTOIP_NET | (addr & 0xffff);
+ /* Now, 169.254.0.0 <= addr <= 169.254.255.255 */
+
+ if (addr < AUTOIP_RANGE_START) {
+ addr += AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
+ }
+ if (addr > AUTOIP_RANGE_END) {
+ addr -= AUTOIP_RANGE_END - AUTOIP_RANGE_START + 1;
+ }
+ LWIP_ASSERT("AUTOIP address not in range", (addr >= AUTOIP_RANGE_START) &&
+ (addr <= AUTOIP_RANGE_END));
+ ip4_addr_set_u32(ipaddr, htonl(addr));
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_create_addr(): tried_llipaddr=%"U16_F", %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ (u16_t)(netif->autoip->tried_llipaddr), ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr),
+ ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+}
+
+/**
+ * Sends an ARP probe from a network interface
+ *
+ * @param netif network interface used to send the probe
+ */
+static err_t
+autoip_arp_probe(struct netif *netif)
+{
+ return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,
+ (struct eth_addr *)netif->hwaddr, IP_ADDR_ANY, &ethzero,
+ &netif->autoip->llipaddr, ARP_REQUEST);
+}
+
+/**
+ * Sends an ARP announce from a network interface
+ *
+ * @param netif network interface used to send the announce
+ */
+static err_t
+autoip_arp_announce(struct netif *netif)
+{
+ return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,
+ (struct eth_addr *)netif->hwaddr, &netif->autoip->llipaddr, &ethzero,
+ &netif->autoip->llipaddr, ARP_REQUEST);
+}
+
+/**
+ * Configure interface for use with current LL IP-Address
+ *
+ * @param netif network interface to configure with current LL IP-Address
+ */
+static err_t
+autoip_bind(struct netif *netif)
+{
+ struct autoip *autoip = netif->autoip;
+ ip_addr_t sn_mask, gw_addr;
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_bind(netif=%p) %c%c%"U16_F" %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ (void*)netif, netif->name[0], netif->name[1], (u16_t)netif->num,
+ ip4_addr1_16(&autoip->llipaddr), ip4_addr2_16(&autoip->llipaddr),
+ ip4_addr3_16(&autoip->llipaddr), ip4_addr4_16(&autoip->llipaddr)));
+
+ IP4_ADDR(&sn_mask, 255, 255, 0, 0);
+ IP4_ADDR(&gw_addr, 0, 0, 0, 0);
+
+ netif_set_ipaddr(netif, &autoip->llipaddr);
+ netif_set_netmask(netif, &sn_mask);
+ netif_set_gw(netif, &gw_addr);
+
+ /* bring the interface up */
+ netif_set_up(netif);
+
+ return ERR_OK;
+}
+
+/**
+ * Start AutoIP client
+ *
+ * @param netif network interface on which start the AutoIP client
+ */
+err_t
+autoip_start(struct netif *netif)
+{
+ struct autoip *autoip = netif->autoip;
+ err_t result = ERR_OK;
+
+ if (netif_is_up(netif)) {
+ netif_set_down(netif);
+ }
+
+ /* Set IP-Address, Netmask and Gateway to 0 to make sure that
+ * ARP Packets are formed correctly
+ */
+ ip_addr_set_zero(&netif->ip_addr);
+ ip_addr_set_zero(&netif->netmask);
+ ip_addr_set_zero(&netif->gw);
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_start(netif=%p) %c%c%"U16_F"\n", (void*)netif, netif->name[0],
+ netif->name[1], (u16_t)netif->num));
+ if (autoip == NULL) {
+ /* no AutoIP client attached yet? */
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_start(): starting new AUTOIP client\n"));
+ autoip = (struct autoip *)mem_malloc(sizeof(struct autoip));
+ if (autoip == NULL) {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_start(): could not allocate autoip\n"));
+ return ERR_MEM;
+ }
+ memset(autoip, 0, sizeof(struct autoip));
+ /* store this AutoIP client in the netif */
+ netif->autoip = autoip;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_start(): allocated autoip"));
+ } else {
+ autoip->state = AUTOIP_STATE_OFF;
+ autoip->ttw = 0;
+ autoip->sent_num = 0;
+ ip_addr_set_zero(&autoip->llipaddr);
+ autoip->lastconflict = 0;
+ }
+
+ autoip_create_addr(netif, &(autoip->llipaddr));
+ autoip_start_probing(netif);
+
+ return result;
+}
+
+static void
+autoip_start_probing(struct netif *netif)
+{
+ struct autoip *autoip = netif->autoip;
+
+ autoip->state = AUTOIP_STATE_PROBING;
+ autoip->sent_num = 0;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_start_probing(): changing state to PROBING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
+ ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
+
+ /* time to wait to first probe, this is randomly
+ * choosen out of 0 to PROBE_WAIT seconds.
+ * compliant to RFC 3927 Section 2.2.1
+ */
+ autoip->ttw = (u16_t)(LWIP_AUTOIP_RAND(netif) % (PROBE_WAIT * AUTOIP_TICKS_PER_SECOND));
+
+ /*
+ * if we tried more then MAX_CONFLICTS we must limit our rate for
+ * accquiring and probing address
+ * compliant to RFC 3927 Section 2.2.1
+ */
+ if (autoip->tried_llipaddr > MAX_CONFLICTS) {
+ autoip->ttw = RATE_LIMIT_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ }
+}
+
+/**
+ * Handle a possible change in the network configuration.
+ *
+ * If there is an AutoIP address configured, take the interface down
+ * and begin probing with the same address.
+ */
+void
+autoip_network_changed(struct netif *netif)
+{
+ if (netif->autoip && netif->autoip->state != AUTOIP_STATE_OFF) {
+ netif_set_down(netif);
+ autoip_start_probing(netif);
+ }
+}
+
+/**
+ * Stop AutoIP client
+ *
+ * @param netif network interface on which stop the AutoIP client
+ */
+err_t
+autoip_stop(struct netif *netif)
+{
+ netif->autoip->state = AUTOIP_STATE_OFF;
+ netif_set_down(netif);
+ return ERR_OK;
+}
+
+/**
+ * Has to be called in loop every AUTOIP_TMR_INTERVAL milliseconds
+ */
+void
+autoip_tmr()
+{
+ struct netif *netif = netif_list;
+ /* loop through netif's */
+ while (netif != NULL) {
+ /* only act on AutoIP configured interfaces */
+ if (netif->autoip != NULL) {
+ if (netif->autoip->lastconflict > 0) {
+ netif->autoip->lastconflict--;
+ }
+
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_tmr() AutoIP-State: %"U16_F", ttw=%"U16_F"\n",
+ (u16_t)(netif->autoip->state), netif->autoip->ttw));
+
+ switch(netif->autoip->state) {
+ case AUTOIP_STATE_PROBING:
+ if (netif->autoip->ttw > 0) {
+ netif->autoip->ttw--;
+ } else {
+ if (netif->autoip->sent_num >= PROBE_NUM) {
+ netif->autoip->state = AUTOIP_STATE_ANNOUNCING;
+ netif->autoip->sent_num = 0;
+ netif->autoip->ttw = ANNOUNCE_WAIT * AUTOIP_TICKS_PER_SECOND;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_tmr(): changing state to ANNOUNCING: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
+ ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
+ } else {
+ autoip_arp_probe(netif);
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_tmr() PROBING Sent Probe\n"));
+ netif->autoip->sent_num++;
+ /* calculate time to wait to next probe */
+ netif->autoip->ttw = (u16_t)((LWIP_AUTOIP_RAND(netif) %
+ ((PROBE_MAX - PROBE_MIN) * AUTOIP_TICKS_PER_SECOND) ) +
+ PROBE_MIN * AUTOIP_TICKS_PER_SECOND);
+ }
+ }
+ break;
+
+ case AUTOIP_STATE_ANNOUNCING:
+ if (netif->autoip->ttw > 0) {
+ netif->autoip->ttw--;
+ } else {
+ if (netif->autoip->sent_num == 0) {
+ /* We are here the first time, so we waited ANNOUNCE_WAIT seconds
+ * Now we can bind to an IP address and use it.
+ *
+ * autoip_bind calls netif_set_up. This triggers a gratuitous ARP
+ * which counts as an announcement.
+ */
+ autoip_bind(netif);
+ } else {
+ autoip_arp_announce(netif);
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE,
+ ("autoip_tmr() ANNOUNCING Sent Announce\n"));
+ }
+ netif->autoip->ttw = ANNOUNCE_INTERVAL * AUTOIP_TICKS_PER_SECOND;
+ netif->autoip->sent_num++;
+
+ if (netif->autoip->sent_num >= ANNOUNCE_NUM) {
+ netif->autoip->state = AUTOIP_STATE_BOUND;
+ netif->autoip->sent_num = 0;
+ netif->autoip->ttw = 0;
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("autoip_tmr(): changing state to BOUND: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(&netif->autoip->llipaddr), ip4_addr2_16(&netif->autoip->llipaddr),
+ ip4_addr3_16(&netif->autoip->llipaddr), ip4_addr4_16(&netif->autoip->llipaddr)));
+ }
+ }
+ break;
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+}
+
+/**
+ * Handles every incoming ARP Packet, called by etharp_arp_input.
+ *
+ * @param netif network interface to use for autoip processing
+ * @param hdr Incoming ARP packet
+ */
+void
+autoip_arp_reply(struct netif *netif, struct etharp_hdr *hdr)
+{
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE, ("autoip_arp_reply()\n"));
+ if ((netif->autoip != NULL) && (netif->autoip->state != AUTOIP_STATE_OFF)) {
+ /* when ip.src == llipaddr && hw.src != netif->hwaddr
+ *
+ * when probing ip.dst == llipaddr && hw.src != netif->hwaddr
+ * we have a conflict and must solve it
+ */
+ ip_addr_t sipaddr, dipaddr;
+ struct eth_addr netifaddr;
+ ETHADDR16_COPY(netifaddr.addr, netif->hwaddr);
+
+ /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
+ * structure packing (not using structure copy which breaks strict-aliasing rules).
+ */
+ IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
+ IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
+
+ if ((netif->autoip->state == AUTOIP_STATE_PROBING) ||
+ ((netif->autoip->state == AUTOIP_STATE_ANNOUNCING) &&
+ (netif->autoip->sent_num == 0))) {
+ /* RFC 3927 Section 2.2.1:
+ * from beginning to after ANNOUNCE_WAIT
+ * seconds we have a conflict if
+ * ip.src == llipaddr OR
+ * ip.dst == llipaddr && hw.src != own hwaddr
+ */
+ if ((ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr)) ||
+ (ip_addr_cmp(&dipaddr, &netif->autoip->llipaddr) &&
+ !eth_addr_cmp(&netifaddr, &hdr->shwaddr))) {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("autoip_arp_reply(): Probe Conflict detected\n"));
+ autoip_restart(netif);
+ }
+ } else {
+ /* RFC 3927 Section 2.5:
+ * in any state we have a conflict if
+ * ip.src == llipaddr && hw.src != own hwaddr
+ */
+ if (ip_addr_cmp(&sipaddr, &netif->autoip->llipaddr) &&
+ !eth_addr_cmp(&netifaddr, &hdr->shwaddr)) {
+ LWIP_DEBUGF(AUTOIP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE | LWIP_DBG_LEVEL_WARNING,
+ ("autoip_arp_reply(): Conflicting ARP-Packet detected\n"));
+ autoip_handle_arp_conflict(netif);
+ }
+ }
+ }
+}
+
+#endif /* LWIP_AUTOIP */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv4/icmp.c b/src/VBox/Devices/Network/lwip-new/src/core/ipv4/icmp.c
new file mode 100644
index 00000000..475f75cb
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv4/icmp.c
@@ -0,0 +1,451 @@
+/**
+ * @file
+ * ICMP - Internet Control Message Protocol
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/* Some ICMP messages should be passed to the transport protocols. This
+ is not implemented. */
+
+#include "lwip/opt.h"
+
+#if LWIP_ICMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/icmp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+
+#include <string.h>
+
+/* see comment in "lwip/ip.h" */
+#ifdef IP_HDRINCL
+#undef IP_HDRINCL
+#endif
+#define IP_HDRINCL LWIP_IP_HDRINCL
+
+/** Small optimization: set to 0 if incoming PBUF_POOL pbuf always can be
+ * used to modify and send a response packet (and to 1 if this is not the case,
+ * e.g. when link header is stripped of when receiving) */
+#ifndef LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+#define LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN 1
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+
+/* The amount of data from the original packet to return in a dest-unreachable */
+#define ICMP_DEST_UNREACH_DATASIZE 8
+
+static void icmp_send_response(struct pbuf *p, u8_t type, u8_t code);
+
+#if LWIP_CONNECTION_PROXY
+static ping_proxy_fn ping_proxy_accept_callback;
+static void* ping_proxy_accept_arg;
+#endif
+
+
+/**
+ * Processes ICMP input packets, called from ip_input().
+ *
+ * Currently only processes icmp echo requests and sends
+ * out the echo response.
+ *
+ * @param p the icmp echo request packet, p->payload pointing to the icmp header
+ * @param inp the netif on which this packet was received
+ */
+void
+icmp_input(struct pbuf *p, struct netif *inp)
+{
+ u8_t type;
+#ifdef LWIP_DEBUG
+ u8_t code;
+#endif /* LWIP_DEBUG */
+ struct icmp_echo_hdr *iecho;
+ struct ip_hdr *iphdr;
+ s16_t hlen;
+
+ ICMP_STATS_INC(icmp.recv);
+ snmp_inc_icmpinmsgs();
+
+ iphdr = (struct ip_hdr *)ip_current_header();
+ hlen = IPH_HL(iphdr) * 4;
+ if (p->len < sizeof(u16_t)*2) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: short ICMP (%"U16_F" bytes) received\n", p->tot_len));
+ goto lenerr;
+ }
+
+ type = *((u8_t *)p->payload);
+#ifdef LWIP_DEBUG
+ code = *(((u8_t *)p->payload)+1);
+#endif /* LWIP_DEBUG */
+ switch (type) {
+ case ICMP_ER:
+ /* This is OK, echo reply might have been parsed by a raw PCB
+ (as obviously, an echo request has been sent, too). */
+ break;
+ case ICMP_ECHO:
+#if !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING
+ {
+ int accepted = 1;
+#if !LWIP_MULTICAST_PING
+ /* multicast destination address? */
+ if (ip_addr_ismulticast(ip_current_dest_addr())) {
+ accepted = 0;
+ }
+#endif /* LWIP_MULTICAST_PING */
+#if !LWIP_BROADCAST_PING
+ /* broadcast destination address? */
+ if (ip_addr_isbroadcast(ip_current_dest_addr(), inp)) {
+ accepted = 0;
+ }
+#endif /* LWIP_BROADCAST_PING */
+ /* broadcast or multicast destination address not acceptd? */
+ if (!accepted) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: Not echoing to multicast or broadcast pings\n"));
+ ICMP_STATS_INC(icmp.err);
+ pbuf_free(p);
+ return;
+ }
+ }
+#endif /* !LWIP_MULTICAST_PING || !LWIP_BROADCAST_PING */
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ping\n"));
+ if (p->tot_len < sizeof(struct icmp_echo_hdr)) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: bad ICMP echo received\n"));
+ goto lenerr;
+ }
+#if 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);
+ snmp_inc_icmpinerrors();
+ return;
+ }
+#endif
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+ if (pbuf_header(p, (PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
+ /* p is not big enough to contain link headers
+ * allocate a new one and copy p into it
+ */
+ struct pbuf *r;
+ /* switch p->payload to ip header */
+ if (pbuf_header(p, hlen)) {
+ LWIP_ASSERT("icmp_input: moving p->payload to ip header failed\n", 0);
+ goto memerr;
+ }
+ /* allocate new packet buffer with space for link headers */
+ r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
+ if (r == NULL) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: allocating new pbuf failed\n"));
+ goto memerr;
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct the ICMP header",
+ (r->len >= hlen + sizeof(struct icmp_echo_hdr)));
+ /* copy the whole packet including ip header */
+ if (pbuf_copy(r, p) != ERR_OK) {
+ LWIP_ASSERT("icmp_input: copying to new pbuf failed\n", 0);
+ goto memerr;
+ }
+ iphdr = (struct ip_hdr *)r->payload;
+ /* switch r->payload back to icmp header */
+ if (pbuf_header(r, -hlen)) {
+ LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
+ goto memerr;
+ }
+ /* free the original p */
+ pbuf_free(p);
+ /* we now have an identical copy of p that has room for link headers */
+ p = r;
+ } else {
+ /* restore p->payload to point to icmp header */
+ if (pbuf_header(p, -(s16_t)(PBUF_IP_HLEN + PBUF_LINK_HLEN))) {
+ LWIP_ASSERT("icmp_input: restoring original p->payload failed\n", 0);
+ goto memerr;
+ }
+ }
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+ /* At this point, all checks are OK. */
+ /* We generate an answer by switching the dest and src ip addresses,
+ * setting the icmp type to ECHO_RESPONSE and updating the checksum. */
+ iecho = (struct icmp_echo_hdr *)p->payload;
+ ip_addr_copy(iphdr->src, *ip_current_dest_addr());
+ ip_addr_copy(iphdr->dest, *ip_current_src_addr());
+ ICMPH_TYPE_SET(iecho, ICMP_ER);
+#if CHECKSUM_GEN_ICMP
+ /* adjust the checksum */
+ if (iecho->chksum > PP_HTONS(0xffffU - (ICMP_ECHO << 8))) {
+ iecho->chksum += PP_HTONS(ICMP_ECHO << 8) + 1;
+ } else {
+ iecho->chksum += PP_HTONS(ICMP_ECHO << 8);
+ }
+#else /* CHECKSUM_GEN_ICMP */
+ iecho->chksum = 0;
+#endif /* CHECKSUM_GEN_ICMP */
+
+ /* Set the correct TTL and recalculate the header checksum. */
+ IPH_TTL_SET(iphdr, ICMP_TTL);
+ IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+#endif /* CHECKSUM_GEN_IP */
+
+ ICMP_STATS_INC(icmp.xmit);
+ /* increase number of messages attempted to send */
+ snmp_inc_icmpoutmsgs();
+ /* increase number of echo replies attempted to send */
+ snmp_inc_icmpoutechoreps();
+
+ if(pbuf_header(p, hlen)) {
+ LWIP_ASSERT("Can't move over header in packet", 0);
+ } else {
+ err_t ret;
+ /* send an ICMP packet, src addr is the dest addr of the curren packet */
+ ret = ip_output_if(p, ip_current_dest_addr(), IP_HDRINCL,
+ ICMP_TTL, 0, IP_PROTO_ICMP, inp);
+ if (ret != ERR_OK) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ip_output_if returned an error: %c.\n", ret));
+ }
+ }
+ break;
+ default:
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_input: ICMP type %"S16_F" code %"S16_F" not supported.\n",
+ (s16_t)type, (s16_t)code));
+ ICMP_STATS_INC(icmp.proterr);
+ ICMP_STATS_INC(icmp.drop);
+ }
+ pbuf_free(p);
+ return;
+lenerr:
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.lenerr);
+ snmp_inc_icmpinerrors();
+ return;
+#if LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN
+memerr:
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.err);
+ snmp_inc_icmpinerrors();
+ return;
+#endif /* LWIP_ICMP_ECHO_CHECK_INPUT_PBUF_LEN */
+}
+
+#if LWIP_CONNECTION_PROXY
+
+void
+ping_proxy_accept(ping_proxy_fn callback, void *arg)
+{
+ ping_proxy_accept_callback = callback;
+ ping_proxy_accept_arg = arg;
+}
+
+
+/**
+ * Proxy ICMP input packets, called from ip_input().
+ *
+ * @param p the icmp echo request packet, p->payload pointing to the icmp header
+ * @param inp the netif on which this packet was received
+ */
+void
+icmp_proxy_input(struct pbuf *p, struct netif *inp)
+{
+ u8_t type, code;
+# ifdef VBOX
+ (void)inp;
+# endif
+
+ ICMP_STATS_INC(icmp.recv);
+ snmp_inc_icmpinmsgs();
+
+ if (p->tot_len < 4) { /* type(1), code(1), checksum(2) */
+ LWIP_DEBUGF(ICMP_DEBUG,
+ ("icmp_proxy_input: short ICMP (%"U16_F" bytes) received\n",
+ p->tot_len));
+ goto lenerr;
+ }
+
+ if (inet_chksum_pbuf(p) != 0) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_proxy_input: bad checksum\n"));
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.chkerr);
+ snmp_inc_icmpinerrors();
+ return;
+ }
+
+ type = *((u8_t *)p->payload);
+ code = *(((u8_t *)p->payload)+1);
+ switch (type) {
+
+ case ICMP_ER:
+ /* ignore silently */
+ pbuf_free(p);
+ break;
+
+ case ICMP_DUR:
+ /* TODO: anything useful we can do? */
+ pbuf_free(p);
+ break;
+
+ case ICMP_ECHO:
+ if (code != 0) {
+ goto proterr;
+ }
+ if (p->tot_len < 8) {
+ goto lenerr;
+ }
+
+ if (ping_proxy_accept_callback != NULL) {
+ (*ping_proxy_accept_callback)(ping_proxy_accept_arg, p);
+ } else {
+ /* ignore silently */
+ pbuf_free(p);
+ }
+ break;
+
+ default:
+ goto proterr;
+ }
+ return;
+
+ lenerr:
+ ICMP_STATS_INC(icmp.lenerr);
+ goto drop;
+
+ proterr:
+ LWIP_DEBUGF(ICMP_DEBUG,
+ ("icmp_proxy_input: ICMP type %"S16_F" code %"S16_F
+ " not supported.\n",
+ (s16_t)type, (s16_t)code));
+ ICMP_STATS_INC(icmp.proterr);
+ goto drop;
+
+ drop:
+ pbuf_free(p);
+ ICMP_STATS_INC(icmp.drop);
+ snmp_inc_icmpinerrors();
+ return;
+}
+#endif /* LWIP_CONNECTION_PROXY */
+
+/**
+ * Send an icmp 'destination unreachable' packet, called from ip_input() if
+ * the transport layer protocol is unknown and from udp_input() if the local
+ * port is not bound.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ * p->payload pointing to the IP header
+ * @param t type of the 'unreachable' packet
+ */
+void
+icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t)
+{
+ icmp_send_response(p, ICMP_DUR, t);
+}
+
+#if IP_FORWARD || IP_REASSEMBLY
+/**
+ * Send a 'time exceeded' packet, called from ip_forward() if TTL is 0.
+ *
+ * @param p the input packet for which the 'time exceeded' should be sent,
+ * p->payload pointing to the IP header
+ * @param t type of the 'time exceeded' packet
+ */
+void
+icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t)
+{
+ icmp_send_response(p, ICMP_TE, t);
+}
+
+#endif /* IP_FORWARD || IP_REASSEMBLY */
+
+/**
+ * Send an icmp packet in response to an incoming packet.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ * p->payload pointing to the IP header
+ * @param type Type of the ICMP header
+ * @param code Code of the ICMP header
+ */
+static void
+icmp_send_response(struct pbuf *p, u8_t type, u8_t code)
+{
+ struct pbuf *q;
+ struct ip_hdr *iphdr;
+ /* we can use the echo header here */
+ struct icmp_echo_hdr *icmphdr;
+ ip_addr_t iphdr_src;
+
+ /* ICMP header + IP header + 8 bytes of data */
+ q = pbuf_alloc(PBUF_IP, sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE,
+ PBUF_RAM);
+ if (q == NULL) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMP packet.\n"));
+ return;
+ }
+ LWIP_ASSERT("check that first pbuf can hold icmp message",
+ (q->len >= (sizeof(struct icmp_echo_hdr) + IP_HLEN + ICMP_DEST_UNREACH_DATASIZE)));
+
+ iphdr = (struct ip_hdr *)p->payload;
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded from "));
+ ip_addr_debug_print(ICMP_DEBUG, &(iphdr->src));
+ LWIP_DEBUGF(ICMP_DEBUG, (" to "));
+ ip_addr_debug_print(ICMP_DEBUG, &(iphdr->dest));
+ LWIP_DEBUGF(ICMP_DEBUG, ("\n"));
+
+ icmphdr = (struct icmp_echo_hdr *)q->payload;
+ icmphdr->type = type;
+ icmphdr->code = code;
+ icmphdr->id = 0;
+ icmphdr->seqno = 0;
+
+ /* copy fields from original packet */
+ SMEMCPY((u8_t *)q->payload + sizeof(struct icmp_echo_hdr), (u8_t *)p->payload,
+ IP_HLEN + ICMP_DEST_UNREACH_DATASIZE);
+
+ /* calculate checksum */
+ icmphdr->chksum = 0;
+#if CHECKSUM_GEN_ICMP
+ icmphdr->chksum = inet_chksum(icmphdr, q->len);
+#endif
+ ICMP_STATS_INC(icmp.xmit);
+ /* increase number of messages attempted to send */
+ snmp_inc_icmpoutmsgs();
+ /* increase number of destination unreachable messages attempted to send */
+ snmp_inc_icmpouttimeexcds();
+ ip_addr_copy(iphdr_src, iphdr->src);
+ ip_output(q, NULL, &iphdr_src, ICMP_TTL, 0, IP_PROTO_ICMP);
+ pbuf_free(q);
+}
+
+#endif /* LWIP_ICMP */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv4/igmp.c b/src/VBox/Devices/Network/lwip-new/src/core/ipv4/igmp.c
new file mode 100644
index 00000000..13f90304
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv4/igmp.c
@@ -0,0 +1,814 @@
+/**
+ * @file
+ * IGMP - Internet Group Management Protocol
+ *
+ */
+
+/*
+ * Copyright (c) 2002 CITEL Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is a contribution to the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+*/
+
+/*-------------------------------------------------------------
+Note 1)
+Although the rfc requires V1 AND V2 capability
+we will only support v2 since now V1 is very old (August 1989)
+V1 can be added if required
+
+a debug print and statistic have been implemented to
+show this up.
+-------------------------------------------------------------
+-------------------------------------------------------------
+Note 2)
+A query for a specific group address (as opposed to ALLHOSTS)
+has now been implemented as I am unsure if it is required
+
+a debug print and statistic have been implemented to
+show this up.
+-------------------------------------------------------------
+-------------------------------------------------------------
+Note 3)
+The router alert rfc 2113 is implemented in outgoing packets
+but not checked rigorously incoming
+-------------------------------------------------------------
+Steve Reynolds
+------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ * RFC 988 - Host extensions for IP multicasting - V0
+ * RFC 1054 - Host extensions for IP multicasting -
+ * RFC 1112 - Host extensions for IP multicasting - V1
+ * RFC 2236 - Internet Group Management Protocol, Version 2 - V2 <- this code is based on this RFC (it's the "de facto" standard)
+ * RFC 3376 - Internet Group Management Protocol, Version 3 - V3
+ * RFC 4604 - Using Internet Group Management Protocol Version 3... - V3+
+ * RFC 2113 - IP Router Alert Option -
+ *----------------------------------------------------------------------------*/
+
+/*-----------------------------------------------------------------------------
+ * Includes
+ *----------------------------------------------------------------------------*/
+
+#include "lwip/opt.h"
+
+#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/igmp.h"
+#include "lwip/debug.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/udp.h"
+#include "lwip/tcp.h"
+#include "lwip/stats.h"
+
+#include "string.h"
+
+/*
+ * IGMP constants
+ */
+#define IGMP_TTL 1
+#define IGMP_MINLEN 8
+#define ROUTER_ALERT 0x9404U
+#define ROUTER_ALERTLEN 4
+
+/*
+ * IGMP message types, including version number.
+ */
+#define IGMP_MEMB_QUERY 0x11 /* Membership query */
+#define IGMP_V1_MEMB_REPORT 0x12 /* Ver. 1 membership report */
+#define IGMP_V2_MEMB_REPORT 0x16 /* Ver. 2 membership report */
+#define IGMP_LEAVE_GROUP 0x17 /* Leave-group message */
+
+/* Group membership states */
+#define IGMP_GROUP_NON_MEMBER 0
+#define IGMP_GROUP_DELAYING_MEMBER 1
+#define IGMP_GROUP_IDLE_MEMBER 2
+
+/**
+ * IGMP packet format.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct igmp_msg {
+ PACK_STRUCT_FIELD(u8_t igmp_msgtype);
+ PACK_STRUCT_FIELD(u8_t igmp_maxresp);
+ PACK_STRUCT_FIELD(u16_t igmp_checksum);
+ PACK_STRUCT_FIELD(ip_addr_p_t igmp_group_address);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+
+static struct igmp_group *igmp_lookup_group(struct netif *ifp, ip_addr_t *addr);
+static err_t igmp_remove_group(struct igmp_group *group);
+static void igmp_timeout( struct igmp_group *group);
+static void igmp_start_timer(struct igmp_group *group, u8_t max_time);
+static void igmp_delaying_member(struct igmp_group *group, u8_t maxresp);
+static err_t igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif);
+static void igmp_send(struct igmp_group *group, u8_t type);
+
+
+static struct igmp_group* igmp_group_list;
+static ip_addr_t allsystems;
+static ip_addr_t allrouters;
+
+
+/**
+ * Initialize the IGMP module
+ */
+void
+igmp_init(void)
+{
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_init: initializing\n"));
+
+ IP4_ADDR(&allsystems, 224, 0, 0, 1);
+ IP4_ADDR(&allrouters, 224, 0, 0, 2);
+}
+
+#ifdef LWIP_DEBUG
+/**
+ * Dump global IGMP groups list
+ */
+void
+igmp_dump_group_list()
+{
+ struct igmp_group *group = igmp_group_list;
+
+ while (group != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_dump_group_list: [%"U32_F"] ", (u32_t)(group->group_state)));
+ ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
+ group = group->next;
+ }
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+}
+#else
+#define igmp_dump_group_list()
+#endif /* LWIP_DEBUG */
+
+/**
+ * Start IGMP processing on interface
+ *
+ * @param netif network interface on which start IGMP processing
+ */
+err_t
+igmp_start(struct netif *netif)
+{
+ struct igmp_group* group;
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: starting IGMP processing on if %p\n", netif));
+
+ group = igmp_lookup_group(netif, &allsystems);
+
+ if (group != NULL) {
+ group->group_state = IGMP_GROUP_IDLE_MEMBER;
+ group->use++;
+
+ /* Allow the igmp messages at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_start: igmp_mac_filter(ADD "));
+ ip_addr_debug_print(IGMP_DEBUG, &allsystems);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
+ netif->igmp_mac_filter(netif, &allsystems, IGMP_ADD_MAC_FILTER);
+ }
+
+ return ERR_OK;
+ }
+
+ return ERR_MEM;
+}
+
+/**
+ * Stop IGMP processing on interface
+ *
+ * @param netif network interface on which stop IGMP processing
+ */
+err_t
+igmp_stop(struct netif *netif)
+{
+ struct igmp_group *group = igmp_group_list;
+ struct igmp_group *prev = NULL;
+ struct igmp_group *next;
+
+ /* look for groups joined on this interface further down the list */
+ while (group != NULL) {
+ next = group->next;
+ /* is it a group joined on this interface? */
+ if (group->netif == netif) {
+ /* is it the first group of the list? */
+ if (group == igmp_group_list) {
+ igmp_group_list = next;
+ }
+ /* is there a "previous" group defined? */
+ if (prev != NULL) {
+ prev->next = next;
+ }
+ /* disable the group at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_stop: igmp_mac_filter(DEL "));
+ ip_addr_debug_print(IGMP_DEBUG, &group->group_address);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
+ netif->igmp_mac_filter(netif, &(group->group_address), IGMP_DEL_MAC_FILTER);
+ }
+ /* free group */
+ memp_free(MEMP_IGMP_GROUP, group);
+ } else {
+ /* change the "previous" */
+ prev = group;
+ }
+ /* move to "next" */
+ group = next;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Report IGMP memberships for this interface
+ *
+ * @param netif network interface on which report IGMP memberships
+ */
+void
+igmp_report_groups(struct netif *netif)
+{
+ struct igmp_group *group = igmp_group_list;
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_report_groups: sending IGMP reports on if %p\n", netif));
+
+ while (group != NULL) {
+ if (group->netif == netif) {
+ igmp_delaying_member(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
+ }
+ group = group->next;
+ }
+}
+
+/**
+ * Search for a group in the global igmp_group_list
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ip address to search for
+ * @return a struct igmp_group* if the group has been found,
+ * NULL if the group wasn't found.
+ */
+struct igmp_group *
+igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr)
+{
+ struct igmp_group *group = igmp_group_list;
+
+ while (group != NULL) {
+ if ((group->netif == ifp) && (ip_addr_cmp(&(group->group_address), addr))) {
+ return group;
+ }
+ group = group->next;
+ }
+
+ /* to be clearer, we return NULL here instead of
+ * 'group' (which is also NULL at this point).
+ */
+ return NULL;
+}
+
+/**
+ * Search for a specific igmp group and create a new one if not found-
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ip address to search
+ * @return a struct igmp_group*,
+ * NULL on memory error.
+ */
+struct igmp_group *
+igmp_lookup_group(struct netif *ifp, ip_addr_t *addr)
+{
+ struct igmp_group *group = igmp_group_list;
+
+ /* Search if the group already exists */
+ group = igmp_lookfor_group(ifp, addr);
+ if (group != NULL) {
+ /* Group already exists. */
+ return group;
+ }
+
+ /* Group doesn't exist yet, create a new one */
+ group = (struct igmp_group *)memp_malloc(MEMP_IGMP_GROUP);
+ if (group != NULL) {
+ group->netif = ifp;
+ ip_addr_set(&(group->group_address), addr);
+ group->timer = 0; /* Not running */
+ group->group_state = IGMP_GROUP_NON_MEMBER;
+ group->last_reporter_flag = 0;
+ group->use = 0;
+ group->next = igmp_group_list;
+
+ igmp_group_list = group;
+ }
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_lookup_group: %sallocated a new group with address ", (group?"":"impossible to ")));
+ ip_addr_debug_print(IGMP_DEBUG, addr);
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", ifp));
+
+ return group;
+}
+
+/**
+ * Remove a group in the global igmp_group_list
+ *
+ * @param group the group to remove from the global igmp_group_list
+ * @return ERR_OK if group was removed from the list, an err_t otherwise
+ */
+static err_t
+igmp_remove_group(struct igmp_group *group)
+{
+ err_t err = ERR_OK;
+
+ /* Is it the first group? */
+ if (igmp_group_list == group) {
+ igmp_group_list = group->next;
+ } else {
+ /* look for group further down the list */
+ struct igmp_group *tmpGroup;
+ for (tmpGroup = igmp_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
+ if (tmpGroup->next == group) {
+ tmpGroup->next = group->next;
+ break;
+ }
+ }
+ /* Group not found in the global igmp_group_list */
+ if (tmpGroup == NULL)
+ err = ERR_ARG;
+ }
+ /* free group */
+ memp_free(MEMP_IGMP_GROUP, group);
+
+ return err;
+}
+
+/**
+ * Called from ip_input() if a new IGMP packet is received.
+ *
+ * @param p received igmp packet, p->payload pointing to the igmp header
+ * @param inp network interface on which the packet was received
+ * @param dest destination ip address of the igmp packet
+ */
+void
+igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest)
+{
+ struct igmp_msg* igmp;
+ struct igmp_group* group;
+ struct igmp_group* groupref;
+
+ IGMP_STATS_INC(igmp.recv);
+
+ /* Note that the length CAN be greater than 8 but only 8 are used - All are included in the checksum */
+ if (p->len < IGMP_MINLEN) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.lenerr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: length error\n"));
+ return;
+ }
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: message from "));
+ ip_addr_debug_print(IGMP_DEBUG, &(ip_current_header()->src));
+ LWIP_DEBUGF(IGMP_DEBUG, (" to address "));
+ ip_addr_debug_print(IGMP_DEBUG, &(ip_current_header()->dest));
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", inp));
+
+ /* Now calculate and check the checksum */
+ igmp = (struct igmp_msg *)p->payload;
+ if (inet_chksum(igmp, p->len)) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.chkerr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: checksum error\n"));
+ return;
+ }
+
+ /* Packet is ok so find an existing group */
+ group = igmp_lookfor_group(inp, dest); /* use the destination IP address of incoming packet */
+
+ /* If group can be found or create... */
+ if (!group) {
+ pbuf_free(p);
+ IGMP_STATS_INC(igmp.drop);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP frame not for us\n"));
+ return;
+ }
+
+ /* NOW ACT ON THE INCOMING MESSAGE TYPE... */
+ switch (igmp->igmp_msgtype) {
+ case IGMP_MEMB_QUERY: {
+ /* IGMP_MEMB_QUERY to the "all systems" address ? */
+ if ((ip_addr_cmp(dest, &allsystems)) && ip_addr_isany(&igmp->igmp_group_address)) {
+ /* THIS IS THE GENERAL QUERY */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: General IGMP_MEMB_QUERY on \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+
+ if (igmp->igmp_maxresp == 0) {
+ IGMP_STATS_INC(igmp.rx_v1);
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: got an all hosts query with time== 0 - this is V1 and not implemented - treat as v2\n"));
+ igmp->igmp_maxresp = IGMP_V1_DELAYING_MEMBER_TMR;
+ } else {
+ IGMP_STATS_INC(igmp.rx_general);
+ }
+
+ groupref = igmp_group_list;
+ while (groupref) {
+ /* Do not send messages on the all systems group address! */
+ if ((groupref->netif == inp) && (!(ip_addr_cmp(&(groupref->group_address), &allsystems)))) {
+ igmp_delaying_member(groupref, igmp->igmp_maxresp);
+ }
+ groupref = groupref->next;
+ }
+ } else {
+ /* IGMP_MEMB_QUERY to a specific group ? */
+ if (!ip_addr_isany(&igmp->igmp_group_address)) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_MEMB_QUERY to a specific group "));
+ ip_addr_debug_print(IGMP_DEBUG, &igmp->igmp_group_address);
+ if (ip_addr_cmp(dest, &allsystems)) {
+ ip_addr_t groupaddr;
+ LWIP_DEBUGF(IGMP_DEBUG, (" using \"ALL SYSTEMS\" address (224.0.0.1) [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+ /* we first need to re-look for the group since we used dest last time */
+ ip_addr_copy(groupaddr, igmp->igmp_group_address);
+ group = igmp_lookfor_group(inp, &groupaddr);
+ } else {
+ LWIP_DEBUGF(IGMP_DEBUG, (" with the group address as destination [igmp_maxresp=%i]\n", (int)(igmp->igmp_maxresp)));
+ }
+
+ if (group != NULL) {
+ IGMP_STATS_INC(igmp.rx_group);
+ igmp_delaying_member(group, igmp->igmp_maxresp);
+ } else {
+ IGMP_STATS_INC(igmp.drop);
+ }
+ } else {
+ IGMP_STATS_INC(igmp.proterr);
+ }
+ }
+ break;
+ }
+ case IGMP_V2_MEMB_REPORT: {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: IGMP_V2_MEMB_REPORT\n"));
+ IGMP_STATS_INC(igmp.rx_report);
+ if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
+ /* This is on a specific group we have already looked up */
+ group->timer = 0; /* stopped */
+ group->group_state = IGMP_GROUP_IDLE_MEMBER;
+ group->last_reporter_flag = 0;
+ }
+ break;
+ }
+ default: {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_input: unexpected msg %d in state %d on group %p on if %p\n",
+ igmp->igmp_msgtype, group->group_state, &group, group->netif));
+ IGMP_STATS_INC(igmp.proterr);
+ break;
+ }
+ }
+
+ pbuf_free(p);
+ return;
+}
+
+/**
+ * Join a group on one network interface.
+ *
+ * @param ifaddr ip address of the network interface which should join a new group
+ * @param groupaddr the ip address of the group which to join
+ * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
+ */
+err_t
+igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
+{
+ err_t err = ERR_VAL; /* no matching interface */
+ struct igmp_group *group;
+ struct netif *netif;
+
+ /* make sure it is multicast address */
+ LWIP_ERROR("igmp_joingroup: attempt to join non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
+ LWIP_ERROR("igmp_joingroup: attempt to join allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+ /* loop through netif's */
+ netif = netif_list;
+ while (netif != NULL) {
+ /* Should we join this interface ? */
+ if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
+ /* find group or create a new one if not found */
+ group = igmp_lookup_group(netif, groupaddr);
+
+ if (group != NULL) {
+ /* This should create a new group, check the state to make sure */
+ if (group->group_state != IGMP_GROUP_NON_MEMBER) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to group not in state IGMP_GROUP_NON_MEMBER\n"));
+ } else {
+ /* OK - it was new group */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: join to new group: "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+ /* If first use of the group, allow the group at the MAC level */
+ if ((group->use==0) && (netif->igmp_mac_filter != NULL)) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: igmp_mac_filter(ADD "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
+ netif->igmp_mac_filter(netif, groupaddr, IGMP_ADD_MAC_FILTER);
+ }
+
+ IGMP_STATS_INC(igmp.tx_join);
+ igmp_send(group, IGMP_V2_MEMB_REPORT);
+
+ igmp_start_timer(group, IGMP_JOIN_DELAYING_MEMBER_TMR);
+
+ /* Need to work out where this timer comes from */
+ group->group_state = IGMP_GROUP_DELAYING_MEMBER;
+ }
+ /* Increment group use */
+ group->use++;
+ /* Join on this interface */
+ err = ERR_OK;
+ } else {
+ /* Return an error even if some network interfaces are joined */
+ /** @todo undo any other netif already joined */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_joingroup: Not enought memory to join to group\n"));
+ return ERR_MEM;
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+
+ return err;
+}
+
+/**
+ * Leave a group on one network interface.
+ *
+ * @param ifaddr ip address of the network interface which should leave a group
+ * @param groupaddr the ip address of the group which to leave
+ * @return ERR_OK if group was left on the netif(s), an err_t otherwise
+ */
+err_t
+igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr)
+{
+ err_t err = ERR_VAL; /* no matching interface */
+ struct igmp_group *group;
+ struct netif *netif;
+
+ /* make sure it is multicast address */
+ LWIP_ERROR("igmp_leavegroup: attempt to leave non-multicast address", ip_addr_ismulticast(groupaddr), return ERR_VAL;);
+ LWIP_ERROR("igmp_leavegroup: attempt to leave allsystems address", (!ip_addr_cmp(groupaddr, &allsystems)), return ERR_VAL;);
+
+ /* loop through netif's */
+ netif = netif_list;
+ while (netif != NULL) {
+ /* Should we leave this interface ? */
+ if ((netif->flags & NETIF_FLAG_IGMP) && ((ip_addr_isany(ifaddr) || ip_addr_cmp(&(netif->ip_addr), ifaddr)))) {
+ /* find group */
+ group = igmp_lookfor_group(netif, groupaddr);
+
+ if (group != NULL) {
+ /* Only send a leave if the flag is set according to the state diagram */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: Leaving group: "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+ /* If there is no other use of the group */
+ if (group->use <= 1) {
+ /* If we are the last reporter for this group */
+ if (group->last_reporter_flag) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: sending leaving group\n"));
+ IGMP_STATS_INC(igmp.tx_leave);
+ igmp_send(group, IGMP_LEAVE_GROUP);
+ }
+
+ /* Disable the group at the MAC level */
+ if (netif->igmp_mac_filter != NULL) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: igmp_mac_filter(DEL "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, (") on if %p\n", netif));
+ netif->igmp_mac_filter(netif, groupaddr, IGMP_DEL_MAC_FILTER);
+ }
+
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: remove group: "));
+ ip_addr_debug_print(IGMP_DEBUG, groupaddr);
+ LWIP_DEBUGF(IGMP_DEBUG, ("\n"));
+
+ /* Free the group */
+ igmp_remove_group(group);
+ } else {
+ /* Decrement group use */
+ group->use--;
+ }
+ /* Leave on this interface */
+ err = ERR_OK;
+ } else {
+ /* It's not a fatal error on "leavegroup" */
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_leavegroup: not member of group\n"));
+ }
+ }
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+
+ return err;
+}
+
+/**
+ * The igmp timer function (both for NO_SYS=1 and =0)
+ * Should be called every IGMP_TMR_INTERVAL milliseconds (100 ms is default).
+ */
+void
+igmp_tmr(void)
+{
+ struct igmp_group *group = igmp_group_list;
+
+ while (group != NULL) {
+ if (group->timer > 0) {
+ group->timer--;
+ if (group->timer == 0) {
+ igmp_timeout(group);
+ }
+ }
+ group = group->next;
+ }
+}
+
+/**
+ * Called if a timeout for one group is reached.
+ * Sends a report for this group.
+ *
+ * @param group an igmp_group for which a timeout is reached
+ */
+static void
+igmp_timeout(struct igmp_group *group)
+{
+ /* If the state is IGMP_GROUP_DELAYING_MEMBER then we send a report for this group */
+ if (group->group_state == IGMP_GROUP_DELAYING_MEMBER) {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_timeout: report membership for group with address "));
+ ip_addr_debug_print(IGMP_DEBUG, &(group->group_address));
+ LWIP_DEBUGF(IGMP_DEBUG, (" on if %p\n", group->netif));
+
+ IGMP_STATS_INC(igmp.tx_report);
+ igmp_send(group, IGMP_V2_MEMB_REPORT);
+ }
+}
+
+/**
+ * Start a timer for an igmp group
+ *
+ * @param group the igmp_group for which to start a timer
+ * @param max_time the time in multiples of IGMP_TMR_INTERVAL (decrease with
+ * every call to igmp_tmr())
+ */
+static void
+igmp_start_timer(struct igmp_group *group, u8_t max_time)
+{
+ /* ensure the input value is > 0 */
+#ifdef LWIP_RAND
+ if (max_time == 0) {
+ max_time = 1;
+ }
+ /* ensure the random value is > 0 */
+ group->timer = (LWIP_RAND() % max_time);
+ if (group->timer == 0) {
+ group->timer = 1;
+ }
+#else /* LWIP_RAND */
+ /* ATTENTION: use this only if absolutely necessary! */
+ group->timer = max_time / 2;
+ if (group->timer == 0) {
+ group->timer = 1;
+ }
+#endif /* LWIP_RAND */
+}
+
+/**
+ * Delaying membership report for a group if necessary
+ *
+ * @param group the igmp_group for which "delaying" membership report
+ * @param maxresp query delay
+ */
+static void
+igmp_delaying_member(struct igmp_group *group, u8_t maxresp)
+{
+ if ((group->group_state == IGMP_GROUP_IDLE_MEMBER) ||
+ ((group->group_state == IGMP_GROUP_DELAYING_MEMBER) &&
+ ((group->timer == 0) || (maxresp < group->timer)))) {
+ igmp_start_timer(group, maxresp);
+ group->group_state = IGMP_GROUP_DELAYING_MEMBER;
+ }
+}
+
+
+/**
+ * Sends an IP packet on a network interface. This function constructs the IP header
+ * and calculates the IP header checksum. If the source IP address is NULL,
+ * the IP address of the outgoing network interface is filled in as source address.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an IP
+ header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ * ERR_BUF if p doesn't have enough space for IP/LINK headers
+ * returns errors returned by netif->output
+ */
+static err_t
+igmp_ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest, struct netif *netif)
+{
+ /* This is the "router alert" option */
+ u16_t ra[2];
+ ra[0] = PP_HTONS(ROUTER_ALERT);
+ ra[1] = 0x0000; /* Router shall examine packet */
+ IGMP_STATS_INC(igmp.xmit);
+ return ip_output_if_opt(p, src, dest, IGMP_TTL, 0, IP_PROTO_IGMP, netif, ra, ROUTER_ALERTLEN);
+}
+
+/**
+ * Send an igmp packet to a specific group.
+ *
+ * @param group the group to which to send the packet
+ * @param type the type of igmp packet to send
+ */
+static void
+igmp_send(struct igmp_group *group, u8_t type)
+{
+ struct pbuf* p = NULL;
+ struct igmp_msg* igmp = NULL;
+ ip_addr_t src = *IP_ADDR_ANY;
+ ip_addr_t* dest = NULL;
+
+ /* IP header + "router alert" option + IGMP header */
+ p = pbuf_alloc(PBUF_TRANSPORT, IGMP_MINLEN, PBUF_RAM);
+
+ if (p) {
+ igmp = (struct igmp_msg *)p->payload;
+ LWIP_ASSERT("igmp_send: check that first pbuf can hold struct igmp_msg",
+ (p->len >= sizeof(struct igmp_msg)));
+ ip_addr_copy(src, group->netif->ip_addr);
+
+ if (type == IGMP_V2_MEMB_REPORT) {
+ dest = &(group->group_address);
+ ip_addr_copy(igmp->igmp_group_address, group->group_address);
+ group->last_reporter_flag = 1; /* Remember we were the last to report */
+ } else {
+ if (type == IGMP_LEAVE_GROUP) {
+ dest = &allrouters;
+ ip_addr_copy(igmp->igmp_group_address, group->group_address);
+ }
+ }
+
+ if ((type == IGMP_V2_MEMB_REPORT) || (type == IGMP_LEAVE_GROUP)) {
+ igmp->igmp_msgtype = type;
+ igmp->igmp_maxresp = 0;
+ igmp->igmp_checksum = 0;
+ igmp->igmp_checksum = inet_chksum(igmp, IGMP_MINLEN);
+
+ igmp_ip_output_if(p, &src, dest, group->netif);
+ }
+
+ pbuf_free(p);
+ } else {
+ LWIP_DEBUGF(IGMP_DEBUG, ("igmp_send: not enough memory for igmp_send\n"));
+ IGMP_STATS_INC(igmp.memerr);
+ }
+}
+
+#endif /* LWIP_IGMP */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv4/ip4.c b/src/VBox/Devices/Network/lwip-new/src/core/ipv4/ip4.c
new file mode 100644
index 00000000..19942b0b
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv4/ip4.c
@@ -0,0 +1,1027 @@
+/**
+ * @file
+ * This is the IPv4 layer implementation for incoming and outgoing IP traffic.
+ *
+ * @see ip_frag.c
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/ip_frag.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp.h"
+#include "lwip/igmp.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/snmp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/stats.h"
+#include "arch/perf.h"
+
+#include <string.h>
+
+/* see comment in "lwip/ip.h" */
+#ifdef IP_HDRINCL
+#undef IP_HDRINCL
+#endif
+#define IP_HDRINCL LWIP_IP_HDRINCL
+
+/** Set this to 0 in the rare case of wanting to call an extra function to
+ * generate the IP checksum (in contrast to calculating it on-the-fly). */
+#ifndef LWIP_INLINE_IP_CHKSUM
+#define LWIP_INLINE_IP_CHKSUM 1
+#endif
+#if LWIP_INLINE_IP_CHKSUM && CHECKSUM_GEN_IP
+#define CHECKSUM_GEN_IP_INLINE 1
+#else
+#define CHECKSUM_GEN_IP_INLINE 0
+#endif
+
+#if LWIP_DHCP || defined(LWIP_IP_ACCEPT_UDP_PORT)
+#define IP_ACCEPT_LINK_LAYER_ADDRESSING 1
+
+/** Some defines for DHCP to let link-layer-addressed packets through while the
+ * netif is down.
+ * To use this in your own application/protocol, define LWIP_IP_ACCEPT_UDP_PORT
+ * to return 1 if the port is accepted and 0 if the port is not accepted.
+ */
+#if LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT)
+/* accept DHCP client port and custom port */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (((port) == PP_NTOHS(DHCP_CLIENT_PORT)) \
+ || (LWIP_IP_ACCEPT_UDP_PORT(port)))
+#elif defined(LWIP_IP_ACCEPT_UDP_PORT) /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+/* accept custom port only */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) (LWIP_IP_ACCEPT_UDP_PORT(port))
+#else /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+/* accept DHCP client port only */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(port) ((port) == PP_NTOHS(DHCP_CLIENT_PORT))
+#endif /* LWIP_DHCP && defined(LWIP_IP_ACCEPT_UDP_PORT) */
+
+#else /* LWIP_DHCP */
+#define IP_ACCEPT_LINK_LAYER_ADDRESSING 0
+#endif /* LWIP_DHCP */
+
+/** Global data for both IPv4 and IPv6 */
+struct ip_globals ip_data;
+
+/** The IP header ID of the next outgoing IP packet */
+static u16_t ip_id;
+
+#if LWIP_CONNECTION_PROXY
+proxy_ip4_divert_hook_fn proxy_ip4_divert_hook;
+#endif
+
+
+/**
+ * Finds the appropriate network interface for a given IP address. It
+ * searches the list of network interfaces linearly. A match is found
+ * if the masked IP address of the network interface equals the masked
+ * IP address given to the function.
+ *
+ * @param dest the destination IP address for which to find the route
+ * @return the netif on which to send to reach dest
+ */
+struct netif *
+ip_route(ip_addr_t *dest)
+{
+ struct netif *netif;
+
+#ifdef LWIP_HOOK_IP4_ROUTE
+ netif = LWIP_HOOK_IP4_ROUTE(dest);
+ if (netif != NULL) {
+ return netif;
+ }
+#endif
+
+ /* iterate through netifs */
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ /* network mask matches? */
+ if (netif_is_up(netif)) {
+ if (ip_addr_netcmp(dest, &(netif->ip_addr), &(netif->netmask))) {
+ /* return netif on which to forward IP packet */
+ return netif;
+ }
+ }
+ }
+ if ((netif_default == NULL) || (!netif_is_up(netif_default))) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_route: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ snmp_inc_ipoutnoroutes();
+ return NULL;
+ }
+ /* no matching netif found, use default netif */
+ return netif_default;
+}
+
+#if IP_FORWARD
+/**
+ * Determine whether an IP address is in a reserved set of addresses
+ * that may not be forwarded, or whether datagrams to that destination
+ * may be forwarded.
+ * @param p the packet to forward
+ * @param dest the destination IP address
+ * @return 1: can forward 0: discard
+ */
+static int
+ip_canforward(struct pbuf *p)
+{
+ u32_t addr = ntohl(ip4_addr_get_u32(ip_current_dest_addr()));
+
+ if (p->flags & PBUF_FLAG_LLBCAST) {
+ /* don't route link-layer broadcasts */
+ return 0;
+ }
+ if ((p->flags & PBUF_FLAG_LLMCAST) && !IP_MULTICAST(addr)) {
+ /* don't route link-layer multicasts unless the destination address is an IP
+ multicast address */
+ return 0;
+ }
+ if (IP_EXPERIMENTAL(addr)) {
+ return 0;
+ }
+ if (IP_CLASSA(addr)) {
+ u32_t net = addr & IP_CLASSA_NET;
+ if ((net == 0) || (net == ((u32_t)IP_LOOPBACKNET << IP_CLASSA_NSHIFT))) {
+ /* don't route loopback packets */
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/**
+ * Forwards an IP packet. It finds an appropriate route for the
+ * packet, decrements the TTL value of the packet, adjusts the
+ * checksum and outputs the packet on the appropriate interface.
+ *
+ * @param p the packet to forward (p->payload points to IP header)
+ * @param iphdr the IP header of the input packet
+ * @param inp the netif on which this packet was received
+ * @return -1 if packet is to be dropped, 0 if there's no route, 1 if forwarded
+ */
+static int
+ip_forward(struct pbuf *p, struct ip_hdr *iphdr, struct netif *inp)
+{
+ struct netif *netif;
+ int forwarded = -1;
+
+ PERF_START;
+
+ if (!ip_canforward(p)) {
+ goto return_noroute;
+ }
+
+ /* RFC3927 2.7: do not forward link-local addresses */
+ if (ip_addr_islinklocal(ip_current_dest_addr())) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not forwarding LLA %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()),
+ ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr())));
+ goto return_noroute;
+ }
+
+ /* Find network interface where to forward this IP packet to. */
+ netif = ip_route(ip_current_dest_addr());
+ if (netif == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: no forwarding route for %"U16_F".%"U16_F".%"U16_F".%"U16_F" found\n",
+ ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()),
+ ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr())));
+ /* @todo: send ICMP_DUR_NET? */
+ forwarded = 0; /* but try connection proxy if configured */
+ goto return_noroute;
+ }
+
+#if LWIP_CONNECTION_PROXY
+ /* The packet is for a destination on a directly connected network.
+ * Check for addresses in that address space that proxy wants to
+ * remap (e.g. to host loopback address/es) and hand it off to
+ * proxy */
+ if (netif != netif_default
+ && proxy_ip4_divert_hook != NULL
+ && (*proxy_ip4_divert_hook)(netif, ip_current_dest_addr()))
+ {
+ forwarded = 0;
+ goto return_noroute;
+ }
+#endif /* LWIP_CONNECTION_PROXY */
+
+#if !IP_FORWARD_ALLOW_TX_ON_RX_NETIF
+ /* Do not forward packets onto the same network interface on which
+ * they arrived. */
+ if (netif == inp) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: not bouncing packets back on incoming interface.\n"));
+ goto return_noroute;
+ }
+#endif /* IP_FORWARD_ALLOW_TX_ON_RX_NETIF */
+
+ /* decrement TTL */
+ IPH_TTL_SET(iphdr, IPH_TTL(iphdr) - 1);
+ /* send ICMP if TTL == 0 */
+ if (IPH_TTL(iphdr) == 0) {
+ snmp_inc_ipinhdrerrors();
+#if LWIP_ICMP
+ /* Don't send ICMP messages in response to ICMP messages */
+ if (IPH_PROTO(iphdr) != IP_PROTO_ICMP) {
+ icmp_time_exceeded(p, ICMP_TE_TTL);
+ }
+#endif /* LWIP_ICMP */
+ return -1;
+ }
+
+ /* Incrementally update the IP checksum. */
+ if (IPH_CHKSUM(iphdr) >= PP_HTONS(0xffffU - 0x100)) {
+ IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100) + 1);
+ } else {
+ IPH_CHKSUM_SET(iphdr, IPH_CHKSUM(iphdr) + PP_HTONS(0x100));
+ }
+
+ LWIP_DEBUGF(IP_DEBUG, ("ip_forward: forwarding packet to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(ip_current_dest_addr()), ip4_addr2_16(ip_current_dest_addr()),
+ ip4_addr3_16(ip_current_dest_addr()), ip4_addr4_16(ip_current_dest_addr())));
+
+ IP_STATS_INC(ip.fw);
+ IP_STATS_INC(ip.xmit);
+ snmp_inc_ipforwdatagrams();
+
+ PERF_STOP("ip_forward");
+ /* don't fragment if interface has mtu set to 0 [loopif] */
+ if (netif->mtu && (p->tot_len > netif->mtu)) {
+ if ((IPH_OFFSET(iphdr) & PP_NTOHS(IP_DF)) == 0) {
+#if IP_FRAG
+ ip_frag(p, netif, ip_current_dest_addr());
+ forwarded = 1;
+#else /* IP_FRAG */
+ /* @todo: send ICMP Destination Unreacheable code 13 "Communication administratively prohibited"? */
+ forwarded = -1;
+#endif /* IP_FRAG */
+ } else {
+ /* send ICMP Destination Unreacheable code 4: "Fragmentation Needed and DF Set" */
+ icmp_dest_unreach(p, ICMP_DUR_FRAG);
+ forwarded = -1;
+ }
+ return forwarded;
+ }
+ /* transmit pbuf on chosen interface */
+ netif->output(netif, p, ip_current_dest_addr());
+ return 1;
+
+return_noroute:
+#if LWIP_CONNECTION_PROXY
+ if (forwarded < 0)
+#endif
+ {
+ snmp_inc_ipoutnoroutes();
+ }
+ return forwarded;
+}
+#endif /* IP_FORWARD */
+
+/**
+ * This function is called by the network interface device driver when
+ * an IP packet is received. The function does the basic checks of the
+ * IP header such as packet size being at least larger than the header
+ * size etc. If the packet was not destined for us, the packet is
+ * forwarded (using ip_forward). The IP checksum is always checked.
+ *
+ * Finally, the packet is sent to the upper layer protocol input function.
+ *
+ * @param p the received IP packet (p->payload points to IP header)
+ * @param inp the netif on which this packet was received
+ * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
+ * processed, but currently always returns ERR_OK)
+ */
+err_t
+ip_input(struct pbuf *p, struct netif *inp)
+{
+ struct ip_hdr *iphdr;
+ struct netif *netif;
+ u16_t iphdr_hlen;
+ u16_t iphdr_len;
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+ int check_ip_src=1;
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+#if LWIP_CONNECTION_PROXY
+ int proxy = 0;
+#endif
+
+ IP_STATS_INC(ip.recv);
+ snmp_inc_ipinreceives();
+
+ /* identify the IP header */
+ iphdr = (struct ip_hdr *)p->payload;
+ if (IPH_V(iphdr) != 4) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IP packet dropped due to bad version number %"U16_F"\n", IPH_V(iphdr)));
+ ip_debug_print(p);
+ pbuf_free(p);
+ IP_STATS_INC(ip.err);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipinhdrerrors();
+ return ERR_OK;
+ }
+
+#ifdef LWIP_HOOK_IP4_INPUT
+ if (LWIP_HOOK_IP4_INPUT(p, inp)) {
+ /* the packet has been eaten */
+ return ERR_OK;
+ }
+#endif
+
+ /* obtain IP header length in number of 32-bit words */
+ iphdr_hlen = IPH_HL(iphdr);
+ /* calculate IP header length in bytes */
+ iphdr_hlen *= 4;
+ /* obtain ip length in bytes */
+ iphdr_len = ntohs(IPH_LEN(iphdr));
+
+ /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */
+ if ((iphdr_hlen > p->len) || (iphdr_len > p->tot_len)) {
+ if (iphdr_hlen > p->len) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IP header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n",
+ iphdr_hlen, p->len));
+ }
+ if (iphdr_len > p->tot_len) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IP (len %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n",
+ iphdr_len, p->tot_len));
+ }
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP_STATS_INC(ip.lenerr);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipindiscards();
+ return ERR_OK;
+ }
+
+ /* verify checksum */
+#if CHECKSUM_CHECK_IP
+ if (inet_chksum(iphdr, iphdr_hlen) != 0) {
+
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("Checksum (0x%"X16_F") failed, IP packet dropped.\n", inet_chksum(iphdr, iphdr_hlen)));
+ ip_debug_print(p);
+ pbuf_free(p);
+ IP_STATS_INC(ip.chkerr);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipinhdrerrors();
+ return ERR_OK;
+ }
+#endif
+
+ /* Trim pbuf. This should have been done at the netif layer,
+ * but we'll do it anyway just to be sure that its done. */
+ pbuf_realloc(p, iphdr_len);
+
+ /* copy IP addresses to aligned ip_addr_t */
+ ip_addr_copy(*ipX_2_ip(&ip_data.current_iphdr_dest), iphdr->dest);
+ ip_addr_copy(*ipX_2_ip(&ip_data.current_iphdr_src), iphdr->src);
+
+ /* match packet against an interface, i.e. is this packet for us? */
+#if LWIP_IGMP
+ if (ip_addr_ismulticast(ip_current_dest_addr())) {
+ if ((inp->flags & NETIF_FLAG_IGMP) && (igmp_lookfor_group(inp, ip_current_dest_addr()))) {
+ netif = inp;
+ } else {
+ netif = NULL;
+ }
+ } else
+#endif /* LWIP_IGMP */
+ {
+ /* start trying with inp. if that's not acceptable, start walking the
+ list of configured netifs.
+ 'first' is used as a boolean to mark whether we started walking the list */
+ int first = 1;
+ netif = inp;
+ do {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input:"
+ " iphdr->dest %"U16_F".%"U16_F".%"U16_F".%"U16_F
+ " netif->ip_addr %"U16_F".%"U16_F".%"U16_F".%"U16_F
+ "/%"U16_F".%"U16_F".%"U16_F".%"U16_F
+ " (0x%08"X32_F", 0x%08"X32_F", 0x%08"X32_F")\n",
+ ip4_addr1_16(&iphdr->dest), ip4_addr2_16(&iphdr->dest),
+ ip4_addr3_16(&iphdr->dest), ip4_addr4_16(&iphdr->dest),
+ ip4_addr1_16(&netif->ip_addr), ip4_addr2_16(&netif->ip_addr),
+ ip4_addr3_16(&netif->ip_addr), ip4_addr4_16(&netif->ip_addr),
+ ip4_addr1_16(&netif->netmask), ip4_addr2_16(&netif->netmask),
+ ip4_addr3_16(&netif->netmask), ip4_addr4_16(&netif->netmask),
+ ntohl(ip4_addr_get_u32(&iphdr->dest)
+ & ip4_addr_get_u32(&netif->netmask)),
+ ntohl(ip4_addr_get_u32(&netif->ip_addr)
+ & ip4_addr_get_u32(&netif->netmask)),
+ ntohl(ip4_addr_get_u32(&iphdr->dest)
+ & ~ip4_addr_get_u32(&netif->netmask))));
+
+ /* interface is up and configured? */
+ if ((netif_is_up(netif)) && (!ip_addr_isany(&(netif->ip_addr)))) {
+ /* unicast to this interface address? */
+ if (ip_addr_cmp(ip_current_dest_addr(), &(netif->ip_addr)) ||
+ /* or broadcast on this interface network address? */
+ ip_addr_isbroadcast(ip_current_dest_addr(), netif)) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: packet accepted on interface %c%c\n",
+ netif->name[0], netif->name[1]));
+ /* break out of for loop */
+ break;
+ }
+#if LWIP_AUTOIP
+ /* connections to link-local addresses must persist after changing
+ the netif's address (RFC3927 ch. 1.9) */
+ if ((netif->autoip != NULL) &&
+ ip_addr_cmp(ip_current_dest_addr(), &(netif->autoip->llipaddr))) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: LLA packet accepted on interface %c%c\n",
+ netif->name[0], netif->name[1]));
+ /* break out of for loop */
+ break;
+ }
+#endif /* LWIP_AUTOIP */
+ }
+ if (first) {
+ first = 0;
+ netif = netif_list;
+ } else {
+ netif = netif->next;
+ }
+ if (netif == inp) {
+ netif = netif->next;
+ }
+ } while(netif != NULL);
+ }
+
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+ /* Pass DHCP messages regardless of destination address. DHCP traffic is addressed
+ * using link layer addressing (such as Ethernet MAC) so we must not filter on IP.
+ * According to RFC 1542 section 3.1.1, referred by RFC 2131).
+ *
+ * If you want to accept private broadcast communication while a netif is down,
+ * define LWIP_IP_ACCEPT_UDP_PORT(dst_port), e.g.:
+ *
+ * #define LWIP_IP_ACCEPT_UDP_PORT(dst_port) ((dst_port) == PP_NTOHS(12345))
+ */
+ if (netif == NULL) {
+ /* remote port is DHCP server? */
+ if (IPH_PROTO(iphdr) == IP_PROTO_UDP) {
+ struct udp_hdr *udphdr = (struct udp_hdr *)((u8_t *)iphdr + iphdr_hlen);
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: UDP packet to DHCP client port %"U16_F"\n",
+ ntohs(udphdr->dest)));
+ if (IP_ACCEPT_LINK_LAYER_ADDRESSED_PORT(udphdr->dest)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: DHCP packet accepted.\n"));
+ netif = inp;
+ check_ip_src = 0;
+ }
+ }
+ }
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+
+ /* broadcast or multicast packet source address? Compliant with RFC 1122: 3.2.1.3 */
+#if IP_ACCEPT_LINK_LAYER_ADDRESSING
+ /* DHCP servers need 0.0.0.0 to be allowed as source address (RFC 1.1.2.2: 3.2.1.3/a) */
+ if (check_ip_src && !ip_addr_isany(ip_current_src_addr()))
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+ { if ((ip_addr_isbroadcast(ip_current_src_addr(), inp)) ||
+ (ip_addr_ismulticast(ip_current_src_addr()))) {
+ /* packet source is not valid */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING, ("ip_input: packet source is not valid.\n"));
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipinaddrerrors();
+ snmp_inc_ipindiscards();
+ return ERR_OK;
+ }
+ }
+
+ /* packet not for us? */
+ if (netif == NULL) {
+ /* packet not for us, route or discard */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_TRACE, ("ip_input: packet not for us.\n"));
+#if IP_FORWARD
+ /* non-broadcast packet? */
+ if (!ip_addr_isbroadcast(ip_current_dest_addr(), inp)) {
+ /* try to forward IP packet on (other) interfaces */
+#if LWIP_CONNECTION_PROXY
+ int forwarded =
+#endif
+ ip_forward(p, iphdr, inp);
+#if LWIP_CONNECTION_PROXY
+ if (forwarded == 0) {
+ proxy = 1;
+ netif = inp;
+ }
+#endif
+ } else
+#endif /* IP_FORWARD */
+ {
+ snmp_inc_ipinaddrerrors();
+ snmp_inc_ipindiscards();
+ }
+
+#if LWIP_CONNECTION_PROXY
+ if (!proxy)
+#endif
+ {
+ pbuf_free(p);
+ return ERR_OK;
+ }
+ }
+
+ /* packet consists of multiple fragments? */
+ if ((IPH_OFFSET(iphdr) & PP_HTONS(IP_OFFMASK | IP_MF)) != 0) {
+#if IP_REASSEMBLY /* packet fragment reassembly code present? */
+ LWIP_DEBUGF(IP_DEBUG, ("IP packet is a fragment (id=0x%04"X16_F" tot_len=%"U16_F" len=%"U16_F" MF=%"U16_F" offset=%"U16_F"), calling ip_reass()\n",
+ ntohs(IPH_ID(iphdr)), p->tot_len, ntohs(IPH_LEN(iphdr)), !!(IPH_OFFSET(iphdr) & PP_HTONS(IP_MF)), (ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK)*8));
+ /* reassemble the packet*/
+ p = ip_reass(p);
+ /* packet not fully reassembled yet? */
+ if (p == NULL) {
+ return ERR_OK;
+ }
+ iphdr = (struct ip_hdr *)p->payload;
+#else /* IP_REASSEMBLY == 0, no packet fragment reassembly code present */
+ pbuf_free(p);
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since it was fragmented (0x%"X16_F") (while IP_REASSEMBLY == 0).\n",
+ ntohs(IPH_OFFSET(iphdr))));
+ IP_STATS_INC(ip.opterr);
+ IP_STATS_INC(ip.drop);
+ /* unsupported protocol feature */
+ snmp_inc_ipinunknownprotos();
+ return ERR_OK;
+#endif /* IP_REASSEMBLY */
+ }
+
+#if IP_OPTIONS_ALLOWED == 0 /* no support for IP options in the IP header? */
+
+#if LWIP_IGMP
+ /* there is an extra "router alert" option in IGMP messages which we allow for but do not police */
+ if((iphdr_hlen > IP_HLEN) && (IPH_PROTO(iphdr) != IP_PROTO_IGMP)) {
+#else
+ if (iphdr_hlen > IP_HLEN) {
+#endif /* LWIP_IGMP */
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("IP packet dropped since there were IP options (while IP_OPTIONS_ALLOWED == 0).\n"));
+ pbuf_free(p);
+ IP_STATS_INC(ip.opterr);
+ IP_STATS_INC(ip.drop);
+ /* unsupported protocol feature */
+ snmp_inc_ipinunknownprotos();
+ return ERR_OK;
+ }
+#endif /* IP_OPTIONS_ALLOWED == 0 */
+
+ /* send to upper layers */
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: \n"));
+ ip_debug_print(p);
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
+
+ ip_data.current_netif = inp;
+ ip_data.current_ip4_header = iphdr;
+ ip_data.current_ip_header_tot_len = IPH_HL(iphdr) * 4;
+
+#if LWIP_CONNECTION_PROXY
+ if (proxy) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_input: proxying\n"));
+
+ pbuf_header(p, -iphdr_hlen); /* Move to payload, no check necessary. */
+
+ switch (IPH_PROTO(iphdr)) {
+
+#if LWIP_ICMP
+ case IP_PROTO_ICMP:
+ icmp_proxy_input(p, inp);
+ break;
+#endif
+
+#if LWIP_UDP
+ case IP_PROTO_UDP:
+#if LWIP_UDPLITE
+ case IP_PROTO_UDPLITE:
+#endif /* LWIP_UDPLITE */
+ udp_proxy_input(p, inp);
+ break;
+#endif /* LWIP_UDP */
+
+#if LWIP_TCP
+ case IP_PROTO_TCP:
+ tcp_proxy_input(p, inp);
+ break;
+#endif /* LWIP_TCP */
+
+ default:
+ /* no proxy support for this protocol */
+ /* XXX: TODO: icmp administratively prohibited? */
+ pbuf_free(p);
+ break;
+ }
+ } else
+#endif /* LWIP_CONNECTION_PROXY */
+#if LWIP_RAW
+ /* raw input did not eat the packet? */
+ if (raw_input(p, inp) == 0)
+#endif /* LWIP_RAW */
+ {
+ pbuf_header(p, -iphdr_hlen); /* Move to payload, no check necessary. */
+
+ switch (IPH_PROTO(iphdr)) {
+#if LWIP_UDP
+ case IP_PROTO_UDP:
+#if LWIP_UDPLITE
+ case IP_PROTO_UDPLITE:
+#endif /* LWIP_UDPLITE */
+ snmp_inc_ipindelivers();
+ udp_input(p, inp);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case IP_PROTO_TCP:
+ snmp_inc_ipindelivers();
+ tcp_input(p, inp);
+ break;
+#endif /* LWIP_TCP */
+#if LWIP_ICMP
+ case IP_PROTO_ICMP:
+ snmp_inc_ipindelivers();
+ icmp_input(p, inp);
+ break;
+#endif /* LWIP_ICMP */
+#if LWIP_IGMP
+ case IP_PROTO_IGMP:
+ igmp_input(p, inp, ip_current_dest_addr());
+ break;
+#endif /* LWIP_IGMP */
+ default:
+#if LWIP_ICMP
+ /* send ICMP destination protocol unreachable unless is was a broadcast */
+ if (!ip_addr_isbroadcast(ip_current_dest_addr(), inp) &&
+ !ip_addr_ismulticast(ip_current_dest_addr())) {
+ pbuf_header(p, iphdr_hlen); /* Move to ip header, no check necessary. */
+ p->payload = iphdr;
+ icmp_dest_unreach(p, ICMP_DUR_PROTO);
+ }
+#endif /* LWIP_ICMP */
+ pbuf_free(p);
+
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("Unsupported transport protocol %"U16_F"\n", IPH_PROTO(iphdr)));
+
+ IP_STATS_INC(ip.proterr);
+ IP_STATS_INC(ip.drop);
+ snmp_inc_ipinunknownprotos();
+ }
+ }
+
+ /* @todo: this is not really necessary... */
+ ip_data.current_netif = NULL;
+ ip_data.current_ip4_header = NULL;
+ ip_data.current_ip_header_tot_len = 0;
+ ip_addr_set_any(ip_current_src_addr());
+ ip_addr_set_any(ip_current_dest_addr());
+
+ return ERR_OK;
+}
+
+/**
+ * Sends an IP packet on a network interface. This function constructs
+ * the IP header and calculates the IP header checksum. If the source
+ * IP address is NULL, the IP address of the outgoing network
+ * interface is filled in as source address.
+ * If the destination IP address is IP_HDRINCL, p is assumed to already
+ * include an IP header and p->payload points to it instead of the data.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an IP
+ header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ * ERR_BUF if p doesn't have enough space for IP/LINK headers
+ * returns errors returned by netif->output
+ *
+ * @note ip_id: RFC791 "some host may be able to simply use
+ * unique identifiers independent of destination"
+ */
+err_t
+ip_output_if(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos,
+ u8_t proto, struct netif *netif)
+{
+#if IP_OPTIONS_SEND
+ return ip_output_if_opt(p, src, dest, ttl, tos, proto, netif, NULL, 0);
+}
+
+/**
+ * Same as ip_output_if() but with the possibility to include IP options:
+ *
+ * @ param ip_options pointer to the IP options, copied into the IP header
+ * @ param optlen length of ip_options
+ */
+err_t ip_output_if_opt(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, struct netif *netif, void *ip_options,
+ u16_t optlen)
+{
+#endif /* IP_OPTIONS_SEND */
+ struct ip_hdr *iphdr;
+ ip_addr_t dest_addr;
+#if CHECKSUM_GEN_IP_INLINE
+ u32_t chk_sum = 0;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
+ gets altered as the packet is passed down the stack */
+ LWIP_ASSERT("p->ref == 1", p->ref == 1);
+
+ snmp_inc_ipoutrequests();
+
+ /* Should the IP header be generated or is it already included in p? */
+ if (dest != IP_HDRINCL) {
+ u16_t ip_hlen = IP_HLEN;
+#if IP_OPTIONS_SEND
+ u16_t optlen_aligned = 0;
+ if (optlen != 0) {
+#if CHECKSUM_GEN_IP_INLINE
+ int i;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ /* round up to a multiple of 4 */
+ optlen_aligned = ((optlen + 3) & ~3);
+ ip_hlen += optlen_aligned;
+ /* First write in the IP options */
+ if (pbuf_header(p, optlen_aligned)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output_if_opt: not enough room for IP options in pbuf\n"));
+ IP_STATS_INC(ip.err);
+ snmp_inc_ipoutdiscards();
+ return ERR_BUF;
+ }
+ MEMCPY(p->payload, ip_options, optlen);
+ if (optlen < optlen_aligned) {
+ /* zero the remaining bytes */
+ memset(((char*)p->payload) + optlen, 0, optlen_aligned - optlen);
+ }
+#if CHECKSUM_GEN_IP_INLINE
+ for (i = 0; i < optlen_aligned/2; i++) {
+ chk_sum += ((u16_t*)p->payload)[i];
+ }
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ }
+#endif /* IP_OPTIONS_SEND */
+ /* generate IP header */
+ if (pbuf_header(p, IP_HLEN)) {
+ LWIP_DEBUGF(IP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip_output: not enough room for IP header in pbuf\n"));
+
+ IP_STATS_INC(ip.err);
+ snmp_inc_ipoutdiscards();
+ return ERR_BUF;
+ }
+
+ iphdr = (struct ip_hdr *)p->payload;
+ LWIP_ASSERT("check that first pbuf can hold struct ip_hdr",
+ (p->len >= sizeof(struct ip_hdr)));
+
+ IPH_TTL_SET(iphdr, ttl);
+ IPH_PROTO_SET(iphdr, proto);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += LWIP_MAKE_U16(proto, ttl);
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ /* dest cannot be NULL here */
+ ip_addr_copy(iphdr->dest, *dest);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += ip4_addr_get_u32(&iphdr->dest) & 0xFFFF;
+ chk_sum += ip4_addr_get_u32(&iphdr->dest) >> 16;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+
+ IPH_VHL_SET(iphdr, 4, ip_hlen / 4);
+ IPH_TOS_SET(iphdr, tos);
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += LWIP_MAKE_U16(tos, iphdr->_v_hl);
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ IPH_LEN_SET(iphdr, htons(p->tot_len));
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += iphdr->_len;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ IPH_OFFSET_SET(iphdr, 0);
+ IPH_ID_SET(iphdr, htons(ip_id));
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += iphdr->_id;
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ ++ip_id;
+
+ if (ip_addr_isany(src)) {
+ ip_addr_copy(iphdr->src, netif->ip_addr);
+ } else {
+ /* src cannot be NULL here */
+ ip_addr_copy(iphdr->src, *src);
+ }
+
+#if CHECKSUM_GEN_IP_INLINE
+ chk_sum += ip4_addr_get_u32(&iphdr->src) & 0xFFFF;
+ chk_sum += ip4_addr_get_u32(&iphdr->src) >> 16;
+ chk_sum = (chk_sum >> 16) + (chk_sum & 0xFFFF);
+ chk_sum = (chk_sum >> 16) + chk_sum;
+ chk_sum = ~chk_sum;
+ iphdr->_chksum = chk_sum; /* network order */
+#else /* CHECKSUM_GEN_IP_INLINE */
+ IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, ip_hlen));
+#endif
+#endif /* CHECKSUM_GEN_IP_INLINE */
+ } else {
+ /* IP header already included in p */
+ iphdr = (struct ip_hdr *)p->payload;
+ ip_addr_copy(dest_addr, iphdr->dest);
+ dest = &dest_addr;
+ }
+
+ IP_STATS_INC(ip.xmit);
+
+ LWIP_DEBUGF(IP_DEBUG, ("ip_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num));
+ ip_debug_print(p);
+
+#if ENABLE_LOOPBACK
+ if (ip_addr_cmp(dest, &netif->ip_addr)) {
+ /* Packet to self, enqueue it for loopback */
+ LWIP_DEBUGF(IP_DEBUG, ("netif_loop_output()\n"));
+ return netif_loop_output(netif, p);
+ }
+#if LWIP_IGMP
+ if ((p->flags & PBUF_FLAG_MCASTLOOP) != 0) {
+ netif_loop_output(netif, p);
+ }
+#endif /* LWIP_IGMP */
+#endif /* ENABLE_LOOPBACK */
+#if IP_FRAG
+ /* don't fragment if interface has mtu set to 0 [loopif] */
+ if (netif->mtu && (p->tot_len > netif->mtu)) {
+ return ip_frag(p, netif, dest);
+ }
+#endif /* IP_FRAG */
+
+ LWIP_DEBUGF(IP_DEBUG, ("netif->output()\n"));
+ return netif->output(netif, p, dest);
+}
+
+/**
+ * Simple interface to ip_output_if. It finds the outgoing network
+ * interface and calls upon ip_output_if to do the actual work.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an IP
+ header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ *
+ * @return ERR_RTE if no route is found
+ * see ip_output_if() for more return values
+ */
+err_t
+ip_output(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto)
+{
+ struct netif *netif;
+
+ /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
+ gets altered as the packet is passed down the stack */
+ LWIP_ASSERT("p->ref == 1", p->ref == 1);
+
+ if ((netif = ip_route(dest)) == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ return ERR_RTE;
+ }
+
+ return ip_output_if(p, src, dest, ttl, tos, proto, netif);
+}
+
+#if LWIP_NETIF_HWADDRHINT
+/** Like ip_output, but takes and addr_hint pointer that is passed on to netif->addr_hint
+ * before calling ip_output_if.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an IP
+ header and p->payload points to that IP header)
+ * @param src the source IP address to send from (if src == IP_ADDR_ANY, the
+ * IP address of the netif used to send is used as source address)
+ * @param dest the destination IP address to send the packet to
+ * @param ttl the TTL value to be set in the IP header
+ * @param tos the TOS value to be set in the IP header
+ * @param proto the PROTOCOL to be set in the IP header
+ * @param addr_hint address hint pointer set to netif->addr_hint before
+ * calling ip_output_if()
+ *
+ * @return ERR_RTE if no route is found
+ * see ip_output_if() for more return values
+ */
+err_t
+ip_output_hinted(struct pbuf *p, ip_addr_t *src, ip_addr_t *dest,
+ u8_t ttl, u8_t tos, u8_t proto, u8_t *addr_hint)
+{
+ struct netif *netif;
+ err_t err;
+
+ /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
+ gets altered as the packet is passed down the stack */
+ LWIP_ASSERT("p->ref == 1", p->ref == 1);
+
+ if ((netif = ip_route(dest)) == NULL) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip_output: No route to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(dest), ip4_addr2_16(dest), ip4_addr3_16(dest), ip4_addr4_16(dest)));
+ IP_STATS_INC(ip.rterr);
+ return ERR_RTE;
+ }
+
+ NETIF_SET_HWADDRHINT(netif, addr_hint);
+ err = ip_output_if(p, src, dest, ttl, tos, proto, netif);
+ NETIF_SET_HWADDRHINT(netif, NULL);
+
+ return err;
+}
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+#if IP_DEBUG
+/* Print an IP header by using LWIP_DEBUGF
+ * @param p an IP packet, p->payload pointing to the IP header
+ */
+void
+ip_debug_print(struct pbuf *p)
+{
+ struct ip_hdr *iphdr = (struct ip_hdr *)p->payload;
+
+ LWIP_UNUSED_ARG(iphdr);
+
+ LWIP_DEBUGF(IP_DEBUG, ("IP header:\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("|%2"S16_F" |%2"S16_F" | 0x%02"X16_F" | %5"U16_F" | (v, hl, tos, len)\n",
+ IPH_V(iphdr),
+ IPH_HL(iphdr),
+ IPH_TOS(iphdr),
+ ntohs(IPH_LEN(iphdr))));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %5"U16_F" |%"U16_F"%"U16_F"%"U16_F"| %4"U16_F" | (id, flags, offset)\n",
+ ntohs(IPH_ID(iphdr)),
+ ntohs(IPH_OFFSET(iphdr)) >> 15 & 1,
+ ntohs(IPH_OFFSET(iphdr)) >> 14 & 1,
+ ntohs(IPH_OFFSET(iphdr)) >> 13 & 1,
+ ntohs(IPH_OFFSET(iphdr)) & IP_OFFMASK));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | 0x%04"X16_F" | (ttl, proto, chksum)\n",
+ IPH_TTL(iphdr),
+ IPH_PROTO(iphdr),
+ ntohs(IPH_CHKSUM(iphdr))));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (src)\n",
+ ip4_addr1_16(&iphdr->src),
+ ip4_addr2_16(&iphdr->src),
+ ip4_addr3_16(&iphdr->src),
+ ip4_addr4_16(&iphdr->src)));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP_DEBUG, ("| %3"U16_F" | %3"U16_F" | %3"U16_F" | %3"U16_F" | (dest)\n",
+ ip4_addr1_16(&iphdr->dest),
+ ip4_addr2_16(&iphdr->dest),
+ ip4_addr3_16(&iphdr->dest),
+ ip4_addr4_16(&iphdr->dest)));
+ LWIP_DEBUGF(IP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* IP_DEBUG */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv4/ip4_addr.c b/src/VBox/Devices/Network/lwip-new/src/core/ipv4/ip4_addr.c
new file mode 100644
index 00000000..8f633ff2
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv4/ip4_addr.c
@@ -0,0 +1,312 @@
+/**
+ * @file
+ * This is the IPv4 address tools implementation.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+
+/* used by IP_ADDR_ANY and IP_ADDR_BROADCAST in ip_addr.h */
+const ip_addr_t ip_addr_any = { IPADDR_ANY };
+const ip_addr_t ip_addr_broadcast = { IPADDR_BROADCAST };
+
+/**
+ * Determine if an address is a broadcast address on a network interface
+ *
+ * @param addr address to be checked
+ * @param netif the network interface against which the address is checked
+ * @return returns non-zero if the address is a broadcast address
+ */
+u8_t
+ip4_addr_isbroadcast(u32_t addr, const struct netif *netif)
+{
+ ip_addr_t ipaddr;
+ ip4_addr_set_u32(&ipaddr, addr);
+
+ /* all ones (broadcast) or all zeroes (old skool broadcast) */
+ if ((~addr == IPADDR_ANY) ||
+ (addr == IPADDR_ANY)) {
+ return 1;
+ /* no broadcast support on this network interface? */
+ } else if ((netif->flags & NETIF_FLAG_BROADCAST) == 0) {
+ /* the given address cannot be a broadcast address
+ * nor can we check against any broadcast addresses */
+ return 0;
+ /* address matches network interface address exactly? => no broadcast */
+ } else if (addr == ip4_addr_get_u32(&netif->ip_addr)) {
+ return 0;
+ /* on the same (sub) network... */
+ } else if (ip_addr_netcmp(&ipaddr, &(netif->ip_addr), &(netif->netmask))
+ /* ...and host identifier bits are all ones? =>... */
+ && ((addr & ~ip4_addr_get_u32(&netif->netmask)) ==
+ (IPADDR_BROADCAST & ~ip4_addr_get_u32(&netif->netmask)))) {
+ /* => network broadcast address */
+ return 1;
+ } else {
+ return 0;
+ }
+}
+
+/** Checks if a netmask is valid (starting with ones, then only zeros)
+ *
+ * @param netmask the IPv4 netmask to check (in network byte order!)
+ * @return 1 if the netmask is valid, 0 if it is not
+ */
+u8_t
+ip4_addr_netmask_valid(u32_t netmask)
+{
+ u32_t mask;
+ u32_t nm_hostorder = lwip_htonl(netmask);
+
+ /* first, check for the first zero */
+ for (mask = 1UL << 31 ; mask != 0; mask >>= 1) {
+ if ((nm_hostorder & mask) == 0) {
+ break;
+ }
+ }
+ /* then check that there is no one */
+ for (; mask != 0; mask >>= 1) {
+ if ((nm_hostorder & mask) != 0) {
+ /* there is a one after the first zero -> invalid */
+ return 0;
+ }
+ }
+ /* no one after the first zero -> valid */
+ return 1;
+}
+
+/* Here for now until needed in other places in lwIP */
+#ifndef isprint
+#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up)
+#define isprint(c) in_range(c, 0x20, 0x7f)
+#define isdigit(c) in_range(c, '0', '9')
+#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F'))
+#define islower(c) in_range(c, 'a', 'z')
+#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v')
+#endif
+
+/**
+ * Ascii internet address interpretation routine.
+ * The value returned is in network order.
+ *
+ * @param cp IP address in ascii represenation (e.g. "127.0.0.1")
+ * @return ip address in network order
+ */
+u32_t
+ipaddr_addr(const char *cp)
+{
+ ip_addr_t val;
+
+ if (ipaddr_aton(cp, &val)) {
+ return ip4_addr_get_u32(&val);
+ }
+ return (IPADDR_NONE);
+}
+
+/**
+ * Check whether "cp" is a valid ascii representation
+ * of an Internet address and convert to a binary address.
+ * Returns 1 if the address is valid, 0 if not.
+ * This replaces inet_addr, the return value from which
+ * cannot distinguish between failure and a local broadcast address.
+ *
+ * @param cp IP address in ascii represenation (e.g. "127.0.0.1")
+ * @param addr pointer to which to save the ip address in network order
+ * @return 1 if cp could be converted to addr, 0 on failure
+ */
+int
+ipaddr_aton(const char *cp, ip_addr_t *addr)
+{
+ u32_t val;
+ u8_t base;
+ char c;
+ u32_t parts[4];
+ u32_t *pp = parts;
+
+ c = *cp;
+ for (;;) {
+ /*
+ * Collect number up to ``.''.
+ * Values are specified as for C:
+ * 0x=hex, 0=octal, 1-9=decimal.
+ */
+ if (!isdigit(c))
+ return (0);
+ val = 0;
+ base = 10;
+ if (c == '0') {
+ c = *++cp;
+ if (c == 'x' || c == 'X') {
+ base = 16;
+ c = *++cp;
+ } else
+ base = 8;
+ }
+ for (;;) {
+ if (isdigit(c)) {
+ val = (val * base) + (int)(c - '0');
+ c = *++cp;
+ } else if (base == 16 && isxdigit(c)) {
+ val = (val << 4) | (int)(c + 10 - (islower(c) ? 'a' : 'A'));
+ c = *++cp;
+ } else
+ break;
+ }
+ if (c == '.') {
+ /*
+ * Internet format:
+ * a.b.c.d
+ * a.b.c (with c treated as 16 bits)
+ * a.b (with b treated as 24 bits)
+ */
+ if (pp >= parts + 3) {
+ return (0);
+ }
+ *pp++ = val;
+ c = *++cp;
+ } else
+ break;
+ }
+ /*
+ * Check for trailing characters.
+ */
+ if (c != '\0' && !isspace(c)) {
+ return (0);
+ }
+ /*
+ * Concoct the address according to
+ * the number of parts specified.
+ */
+ switch (pp - parts + 1) {
+
+ case 0:
+ return (0); /* initial nondigit */
+
+ case 1: /* a -- 32 bits */
+ break;
+
+ case 2: /* a.b -- 8.24 bits */
+ if (val > 0xffffffUL) {
+ return (0);
+ }
+ val |= parts[0] << 24;
+ break;
+
+ case 3: /* a.b.c -- 8.8.16 bits */
+ if (val > 0xffff) {
+ return (0);
+ }
+ val |= (parts[0] << 24) | (parts[1] << 16);
+ break;
+
+ case 4: /* a.b.c.d -- 8.8.8.8 bits */
+ if (val > 0xff) {
+ return (0);
+ }
+ val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+ break;
+ default:
+ LWIP_ASSERT("unhandled", 0);
+ break;
+ }
+ if (addr) {
+ ip4_addr_set_u32(addr, htonl(val));
+ }
+ return (1);
+}
+
+/**
+ * Convert numeric IP address into decimal dotted ASCII representation.
+ * returns ptr to static buffer; not reentrant!
+ *
+ * @param addr ip address in network order to convert
+ * @return pointer to a global static (!) buffer that holds the ASCII
+ * represenation of addr
+ */
+char *
+ipaddr_ntoa(const ip_addr_t *addr)
+{
+ static char str[16];
+ return ipaddr_ntoa_r(addr, str, 16);
+}
+
+/**
+ * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
+ *
+ * @param addr ip address in network order to convert
+ * @param buf target buffer where the string is stored
+ * @param buflen length of buf
+ * @return either pointer to buf which now holds the ASCII
+ * representation of addr or NULL if buf was too small
+ */
+char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen)
+{
+ u32_t s_addr;
+ char inv[3];
+ char *rp;
+ u8_t *ap;
+ u8_t rem;
+ u8_t n;
+ u8_t i;
+ int len = 0;
+
+ s_addr = ip4_addr_get_u32(addr);
+
+ rp = buf;
+ ap = (u8_t *)&s_addr;
+ for(n = 0; n < 4; n++) {
+ i = 0;
+ do {
+ rem = *ap % (u8_t)10;
+ *ap /= (u8_t)10;
+ inv[i++] = '0' + rem;
+ } while(*ap);
+ while(i--) {
+ if (len++ >= buflen) {
+ return NULL;
+ }
+ *rp++ = inv[i];
+ }
+ if (len++ >= buflen) {
+ return NULL;
+ }
+ *rp++ = '.';
+ ap++;
+ }
+ *--rp = 0;
+ return buf;
+}
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv4/ip_frag.c b/src/VBox/Devices/Network/lwip-new/src/core/ipv4/ip_frag.c
new file mode 100644
index 00000000..c8de8752
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv4/ip_frag.c
@@ -0,0 +1,885 @@
+/**
+ * @file
+ * This is the IPv4 packet segmentation and reassembly implementation.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jani Monoses <jani@iv.ro>
+ * Simon Goldschmidt
+ * original reassembly code by Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip_frag.h"
+#include "lwip/def.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/snmp.h"
+#include "lwip/stats.h"
+#include "lwip/icmp.h"
+
+#include <string.h>
+
+#if IP_REASSEMBLY
+/**
+ * The IP reassembly code currently has the following limitations:
+ * - IP header options are not supported
+ * - fragments must not overlap (e.g. due to different routes),
+ * currently, overlapping or duplicate fragments are thrown away
+ * if IP_REASS_CHECK_OVERLAP=1 (the default)!
+ *
+ * @todo: work with IP header options
+ */
+
+/** Setting this to 0, you can turn off checking the fragments for overlapping
+ * regions. The code gets a little smaller. Only use this if you know that
+ * overlapping won't occur on your network! */
+#ifndef IP_REASS_CHECK_OVERLAP
+#define IP_REASS_CHECK_OVERLAP 1
+#endif /* IP_REASS_CHECK_OVERLAP */
+
+/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
+ * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
+ * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
+ * is set to 1, so one datagram can be reassembled at a time, only. */
+#ifndef IP_REASS_FREE_OLDEST
+#define IP_REASS_FREE_OLDEST 1
+#endif /* IP_REASS_FREE_OLDEST */
+
+#define IP_REASS_FLAG_LASTFRAG 0x01
+
+/** This is a helper struct which holds the starting
+ * offset and the ending offset of this fragment to
+ * easily chain the fragments.
+ * It has the same packing requirements as the IP header, since it replaces
+ * the IP header in memory in incoming fragments (after copying it) to keep
+ * track of the various fragments. (-> If the IP header doesn't need packing,
+ * this struct doesn't need packing, too.)
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip_reass_helper {
+ PACK_STRUCT_FIELD(struct pbuf *next_pbuf);
+ PACK_STRUCT_FIELD(u16_t start);
+ PACK_STRUCT_FIELD(u16_t end);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#ifndef VBOX
+#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \
+ (ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
+ ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
+ IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0
+#else
+/*
+ * Put parantheses around the whole construct so the ternary operator works
+ * as anticipated.
+ */
+#define IP_ADDRESSES_AND_ID_MATCH(iphdrA, iphdrB) \
+ ((ip_addr_cmp(&(iphdrA)->src, &(iphdrB)->src) && \
+ ip_addr_cmp(&(iphdrA)->dest, &(iphdrB)->dest) && \
+ IPH_ID(iphdrA) == IPH_ID(iphdrB)) ? 1 : 0)
+#endif
+
+/* global variables */
+static struct ip_reassdata *reassdatagrams;
+static u16_t ip_reass_pbufcount;
+
+/* function prototypes */
+static void ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
+static int ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev);
+
+/**
+ * Reassembly timer base function
+ * for both NO_SYS == 0 and 1 (!).
+ *
+ * Should be called every 1000 msec (defined by IP_TMR_INTERVAL).
+ */
+void
+ip_reass_tmr(void)
+{
+ struct ip_reassdata *r, *prev = NULL;
+
+ r = reassdatagrams;
+ while (r != NULL) {
+ /* Decrement the timer. Once it reaches 0,
+ * clean up the incomplete fragment assembly */
+ if (r->timer > 0) {
+ r->timer--;
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer dec %"U16_F"\n",(u16_t)r->timer));
+ prev = r;
+ r = r->next;
+ } else {
+ /* reassembly timed out */
+ struct ip_reassdata *tmp;
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass_tmr: timer timed out\n"));
+ tmp = r;
+ /* get the next pointer before freeing */
+ r = r->next;
+ /* free the helper struct and all enqueued pbufs */
+ ip_reass_free_complete_datagram(tmp, prev);
+ }
+ }
+}
+
+/**
+ * Free a datagram (struct ip_reassdata) and all its pbufs.
+ * Updates the total count of enqueued pbufs (ip_reass_pbufcount),
+ * SNMP counters and sends an ICMP time exceeded packet.
+ *
+ * @param ipr datagram to free
+ * @param prev the previous datagram in the linked list
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_free_complete_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+ u16_t pbufs_freed = 0;
+ u8_t clen;
+ struct pbuf *p;
+ struct ip_reass_helper *iprh;
+
+ LWIP_ASSERT("prev != ipr", prev != ipr);
+ if (prev != NULL) {
+ LWIP_ASSERT("prev->next == ipr", prev->next == ipr);
+ }
+
+ snmp_inc_ipreasmfails();
+#if LWIP_ICMP
+ iprh = (struct ip_reass_helper *)ipr->p->payload;
+ if (iprh->start == 0) {
+ /* The first fragment was received, send ICMP time exceeded. */
+ /* First, de-queue the first pbuf from r->p. */
+ p = ipr->p;
+ ipr->p = iprh->next_pbuf;
+ /* Then, copy the original header into it. */
+ SMEMCPY(p->payload, &ipr->iphdr, IP_HLEN);
+ icmp_time_exceeded(p, ICMP_TE_FRAG);
+ clen = pbuf_clen(p);
+ LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+ pbufs_freed += clen;
+ pbuf_free(p);
+ }
+#endif /* LWIP_ICMP */
+
+ /* First, free all received pbufs. The individual pbufs need to be released
+ separately as they have not yet been chained */
+ p = ipr->p;
+ while (p != NULL) {
+ struct pbuf *pcur;
+ iprh = (struct ip_reass_helper *)p->payload;
+ pcur = p;
+ /* get the next pointer before freeing */
+ p = iprh->next_pbuf;
+ clen = pbuf_clen(pcur);
+ LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+ pbufs_freed += clen;
+ pbuf_free(pcur);
+ }
+ /* Then, unchain the struct ip_reassdata from the list and free it. */
+ ip_reass_dequeue_datagram(ipr, prev);
+ LWIP_ASSERT("ip_reass_pbufcount >= clen", ip_reass_pbufcount >= pbufs_freed);
+ ip_reass_pbufcount -= pbufs_freed;
+
+ return pbufs_freed;
+}
+
+#if IP_REASS_FREE_OLDEST
+/**
+ * Free the oldest datagram to make room for enqueueing new fragments.
+ * The datagram 'fraghdr' belongs to is not freed!
+ *
+ * @param fraghdr IP header of the current fragment
+ * @param pbufs_needed number of pbufs needed to enqueue
+ * (used for freeing other datagrams if not enough space)
+ * @return the number of pbufs freed
+ */
+static int
+ip_reass_remove_oldest_datagram(struct ip_hdr *fraghdr, int pbufs_needed)
+{
+ /* @todo Can't we simply remove the last datagram in the
+ * linked list behind reassdatagrams?
+ */
+ struct ip_reassdata *r, *oldest, *prev;
+ int pbufs_freed = 0, pbufs_freed_current;
+ int other_datagrams;
+
+ /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
+ * but don't free the datagram that 'fraghdr' belongs to! */
+ do {
+ oldest = NULL;
+ prev = NULL;
+ other_datagrams = 0;
+ r = reassdatagrams;
+ while (r != NULL) {
+ if (!IP_ADDRESSES_AND_ID_MATCH(&r->iphdr, fraghdr)) {
+ /* Not the same datagram as fraghdr */
+ other_datagrams++;
+ if (oldest == NULL) {
+ oldest = r;
+ } else if (r->timer <= oldest->timer) {
+ /* older than the previous oldest */
+ oldest = r;
+ }
+ }
+ if (r->next != NULL) {
+ prev = r;
+ }
+ r = r->next;
+ }
+ if (oldest != NULL) {
+ pbufs_freed_current = ip_reass_free_complete_datagram(oldest, prev);
+ pbufs_freed += pbufs_freed_current;
+ }
+ } while ((pbufs_freed < pbufs_needed) && (other_datagrams > 1));
+ return pbufs_freed;
+}
+#endif /* IP_REASS_FREE_OLDEST */
+
+/**
+ * Enqueues a new fragment into the fragment queue
+ * @param fraghdr points to the new fragments IP hdr
+ * @param clen number of pbufs needed to enqueue (used for freeing other datagrams if not enough space)
+ * @return A pointer to the queue location into which the fragment was enqueued
+ */
+static struct ip_reassdata*
+ip_reass_enqueue_new_datagram(struct ip_hdr *fraghdr, int clen)
+{
+ struct ip_reassdata* ipr;
+ /* No matching previous fragment found, allocate a new reassdata struct */
+ ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+ if (ipr == NULL) {
+#if IP_REASS_FREE_OLDEST
+ if (ip_reass_remove_oldest_datagram(fraghdr, clen) >= clen) {
+ ipr = (struct ip_reassdata *)memp_malloc(MEMP_REASSDATA);
+ }
+ if (ipr == NULL)
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ IPFRAG_STATS_INC(ip_frag.memerr);
+ LWIP_DEBUGF(IP_REASS_DEBUG,("Failed to alloc reassdata struct\n"));
+ return NULL;
+ }
+ }
+ memset(ipr, 0, sizeof(struct ip_reassdata));
+ ipr->timer = IP_REASS_MAXAGE;
+
+ /* enqueue the new structure to the front of the list */
+ ipr->next = reassdatagrams;
+ reassdatagrams = ipr;
+ /* copy the ip header for later tests and input */
+ /* @todo: no ip options supported? */
+ SMEMCPY(&(ipr->iphdr), fraghdr, IP_HLEN);
+ return ipr;
+}
+
+/**
+ * Dequeues a datagram from the datagram queue. Doesn't deallocate the pbufs.
+ * @param ipr points to the queue entry to dequeue
+ */
+static void
+ip_reass_dequeue_datagram(struct ip_reassdata *ipr, struct ip_reassdata *prev)
+{
+
+ /* dequeue the reass struct */
+ if (reassdatagrams == ipr) {
+ /* it was the first in the list */
+ reassdatagrams = ipr->next;
+ } else {
+ /* it wasn't the first, so it must have a valid 'prev' */
+ LWIP_ASSERT("sanity check linked list", prev != NULL);
+ prev->next = ipr->next;
+ }
+
+ /* now we can free the ip_reass struct */
+ memp_free(MEMP_REASSDATA, ipr);
+}
+
+/**
+ * Chain a new pbuf into the pbuf list that composes the datagram. The pbuf list
+ * will grow over time as new pbufs are rx.
+ * Also checks that the datagram passes basic continuity checks (if the last
+ * fragment was received at least once).
+ * @param root_p points to the 'root' pbuf for the current datagram being assembled.
+ * @param new_p points to the pbuf for the current fragment
+ * @return 0 if invalid, >0 otherwise
+ */
+static int
+ip_reass_chain_frag_into_datagram_and_validate(struct ip_reassdata *ipr, struct pbuf *new_p)
+{
+ struct ip_reass_helper *iprh, *iprh_tmp, *iprh_prev=NULL;
+ struct pbuf *q;
+ u16_t offset,len;
+ struct ip_hdr *fraghdr;
+ int valid = 1;
+
+ /* Extract length and fragment offset from current fragment */
+ fraghdr = (struct ip_hdr*)new_p->payload;
+ len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
+ offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
+
+ /* overwrite the fragment's ip header from the pbuf with our helper struct,
+ * and setup the embedded helper structure. */
+ /* make sure the struct ip_reass_helper fits into the IP header */
+ LWIP_ASSERT("sizeof(struct ip_reass_helper) <= IP_HLEN",
+ sizeof(struct ip_reass_helper) <= IP_HLEN);
+ iprh = (struct ip_reass_helper*)new_p->payload;
+ iprh->next_pbuf = NULL;
+ iprh->start = offset;
+ iprh->end = offset + len;
+
+ /* Iterate through until we either get to the end of the list (append),
+ * or we find on with a larger offset (insert). */
+ for (q = ipr->p; q != NULL;) {
+ iprh_tmp = (struct ip_reass_helper*)q->payload;
+ if (iprh->start < iprh_tmp->start) {
+ /* the new pbuf should be inserted before this */
+ iprh->next_pbuf = q;
+ if (iprh_prev != NULL) {
+ /* not the fragment with the lowest offset */
+#if IP_REASS_CHECK_OVERLAP
+ if ((iprh->start < iprh_prev->end) || (iprh->end > iprh_tmp->start)) {
+ /* fragment overlaps with previous or following, throw away */
+ goto freepbuf;
+ }
+#endif /* IP_REASS_CHECK_OVERLAP */
+ iprh_prev->next_pbuf = new_p;
+ } else {
+ /* fragment with the lowest offset */
+ ipr->p = new_p;
+ }
+ break;
+ } else if(iprh->start == iprh_tmp->start) {
+ /* received the same datagram twice: no need to keep the datagram */
+ goto freepbuf;
+#if IP_REASS_CHECK_OVERLAP
+ } else if(iprh->start < iprh_tmp->end) {
+ /* overlap: no need to keep the new datagram */
+ goto freepbuf;
+#endif /* IP_REASS_CHECK_OVERLAP */
+ } else {
+ /* Check if the fragments received so far have no wholes. */
+ if (iprh_prev != NULL) {
+ if (iprh_prev->end != iprh_tmp->start) {
+ /* There is a fragment missing between the current
+ * and the previous fragment */
+ valid = 0;
+ }
+ }
+ }
+ q = iprh_tmp->next_pbuf;
+ iprh_prev = iprh_tmp;
+ }
+
+ /* If q is NULL, then we made it to the end of the list. Determine what to do now */
+ if (q == NULL) {
+ if (iprh_prev != NULL) {
+ /* this is (for now), the fragment with the highest offset:
+ * chain it to the last fragment */
+#if IP_REASS_CHECK_OVERLAP
+ LWIP_ASSERT("check fragments don't overlap", iprh_prev->end <= iprh->start);
+#endif /* IP_REASS_CHECK_OVERLAP */
+ iprh_prev->next_pbuf = new_p;
+ if (iprh_prev->end != iprh->start) {
+ valid = 0;
+ }
+ } else {
+#if IP_REASS_CHECK_OVERLAP
+ LWIP_ASSERT("no previous fragment, this must be the first fragment!",
+ ipr->p == NULL);
+#endif /* IP_REASS_CHECK_OVERLAP */
+ /* this is the first fragment we ever received for this ip datagram */
+ ipr->p = new_p;
+ }
+ }
+
+ /* At this point, the validation part begins: */
+ /* If we already received the last fragment */
+ if ((ipr->flags & IP_REASS_FLAG_LASTFRAG) != 0) {
+ /* and had no wholes so far */
+ if (valid) {
+ /* then check if the rest of the fragments is here */
+ /* Check if the queue starts with the first datagram */
+ if (((struct ip_reass_helper*)ipr->p->payload)->start != 0) {
+ valid = 0;
+ } else {
+ /* and check that there are no wholes after this datagram */
+ iprh_prev = iprh;
+ q = iprh->next_pbuf;
+ while (q != NULL) {
+ iprh = (struct ip_reass_helper*)q->payload;
+ if (iprh_prev->end != iprh->start) {
+ valid = 0;
+ break;
+ }
+ iprh_prev = iprh;
+ q = iprh->next_pbuf;
+ }
+ /* if still valid, all fragments are received
+ * (because to the MF==0 already arrived */
+ if (valid) {
+ LWIP_ASSERT("sanity check", ipr->p != NULL);
+ LWIP_ASSERT("sanity check",
+ ((struct ip_reass_helper*)ipr->p->payload) != iprh);
+ LWIP_ASSERT("validate_datagram:next_pbuf!=NULL",
+ iprh->next_pbuf == NULL);
+ LWIP_ASSERT("validate_datagram:datagram end!=datagram len",
+ iprh->end == ipr->datagram_len);
+ }
+ }
+ }
+ /* If valid is 0 here, there are some fragments missing in the middle
+ * (since MF == 0 has already arrived). Such datagrams simply time out if
+ * no more fragments are received... */
+ return valid;
+ }
+ /* If we come here, not all fragments were received, yet! */
+ return 0; /* not yet valid! */
+#if IP_REASS_CHECK_OVERLAP
+freepbuf:
+ ip_reass_pbufcount -= pbuf_clen(new_p);
+ pbuf_free(new_p);
+ return 0;
+#endif /* IP_REASS_CHECK_OVERLAP */
+}
+
+/**
+ * Reassembles incoming IP fragments into an IP datagram.
+ *
+ * @param p points to a pbuf chain of the fragment
+ * @return NULL if reassembly is incomplete, ? otherwise
+ */
+struct pbuf *
+ip_reass(struct pbuf *p)
+{
+ struct pbuf *r;
+ struct ip_hdr *fraghdr;
+ struct ip_reassdata *ipr;
+ struct ip_reass_helper *iprh;
+ u16_t offset, len;
+ u8_t clen;
+
+ IPFRAG_STATS_INC(ip_frag.recv);
+ snmp_inc_ipreasmreqds();
+
+ fraghdr = (struct ip_hdr*)p->payload;
+
+ if ((IPH_HL(fraghdr) * 4) != IP_HLEN) {
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: IP options currently not supported!\n"));
+ IPFRAG_STATS_INC(ip_frag.err);
+ goto nullreturn;
+ }
+
+ offset = (ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) * 8;
+ len = ntohs(IPH_LEN(fraghdr)) - IPH_HL(fraghdr) * 4;
+
+ /* Check if we are allowed to enqueue more datagrams. */
+ clen = pbuf_clen(p);
+ if ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
+#if IP_REASS_FREE_OLDEST
+ if (!ip_reass_remove_oldest_datagram(fraghdr, clen) ||
+ ((ip_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS))
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ /* No datagram could be freed and still too many pbufs enqueued */
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: Overflow condition: pbufct=%d, clen=%d, MAX=%d\n",
+ ip_reass_pbufcount, clen, IP_REASS_MAX_PBUFS));
+ IPFRAG_STATS_INC(ip_frag.memerr);
+ /* @todo: send ICMP time exceeded here? */
+ /* drop this pbuf */
+ goto nullreturn;
+ }
+ }
+
+ /* Look for the datagram the fragment belongs to in the current datagram queue,
+ * remembering the previous in the queue for later dequeueing. */
+ for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
+ /* Check if the incoming fragment matches the one currently present
+ in the reassembly buffer. If so, we proceed with copying the
+ fragment into the buffer. */
+ if (IP_ADDRESSES_AND_ID_MATCH(&ipr->iphdr, fraghdr)) {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_reass: matching previous fragment ID=%"X16_F"\n",
+ ntohs(IPH_ID(fraghdr))));
+ IPFRAG_STATS_INC(ip_frag.cachehit);
+ break;
+ }
+ }
+
+ if (ipr == NULL) {
+ /* Enqueue a new datagram into the datagram queue */
+ ipr = ip_reass_enqueue_new_datagram(fraghdr, clen);
+ /* Bail if unable to enqueue */
+ if(ipr == NULL) {
+ goto nullreturn;
+ }
+ } else {
+ if (((ntohs(IPH_OFFSET(fraghdr)) & IP_OFFMASK) == 0) &&
+ ((ntohs(IPH_OFFSET(&ipr->iphdr)) & IP_OFFMASK) != 0)) {
+ /* ipr->iphdr is not the header from the first fragment, but fraghdr is
+ * -> copy fraghdr into ipr->iphdr since we want to have the header
+ * of the first fragment (for ICMP time exceeded and later, for copying
+ * all options, if supported)*/
+ SMEMCPY(&ipr->iphdr, fraghdr, IP_HLEN);
+ }
+ }
+ /* Track the current number of pbufs current 'in-flight', in order to limit
+ the number of fragments that may be enqueued at any one time */
+ ip_reass_pbufcount += clen;
+
+ /* At this point, we have either created a new entry or pointing
+ * to an existing one */
+
+ /* check for 'no more fragments', and update queue entry*/
+ if ((IPH_OFFSET(fraghdr) & PP_NTOHS(IP_MF)) == 0) {
+ ipr->flags |= IP_REASS_FLAG_LASTFRAG;
+ ipr->datagram_len = offset + len;
+ LWIP_DEBUGF(IP_REASS_DEBUG,
+ ("ip_reass: last fragment seen, total len %"S16_F"\n",
+ ipr->datagram_len));
+ }
+ /* find the right place to insert this pbuf */
+ /* @todo: trim pbufs if fragments are overlapping */
+ if (ip_reass_chain_frag_into_datagram_and_validate(ipr, p)) {
+ struct ip_reassdata *ipr_prev;
+ /* the totally last fragment (flag more fragments = 0) was received at least
+ * once AND all fragments are received */
+ ipr->datagram_len += IP_HLEN;
+
+ /* save the second pbuf before copying the header over the pointer */
+ r = ((struct ip_reass_helper*)ipr->p->payload)->next_pbuf;
+
+ /* copy the original ip header back to the first pbuf */
+ fraghdr = (struct ip_hdr*)(ipr->p->payload);
+ SMEMCPY(fraghdr, &ipr->iphdr, IP_HLEN);
+ IPH_LEN_SET(fraghdr, htons(ipr->datagram_len));
+ IPH_OFFSET_SET(fraghdr, 0);
+ IPH_CHKSUM_SET(fraghdr, 0);
+ /* @todo: do we need to set calculate the correct checksum? */
+#if CHECKSUM_GEN_IP
+ IPH_CHKSUM_SET(fraghdr, inet_chksum(fraghdr, IP_HLEN));
+#endif /* CHECKSUM_GEN_IP */
+
+ p = ipr->p;
+
+ /* chain together the pbufs contained within the reass_data list. */
+ while(r != NULL) {
+ iprh = (struct ip_reass_helper*)r->payload;
+
+ /* hide the ip header for every succeding fragment */
+ pbuf_header(r, -IP_HLEN);
+ pbuf_cat(p, r);
+ r = iprh->next_pbuf;
+ }
+
+ /* find the previous entry in the linked list */
+ for (ipr_prev = reassdatagrams; ipr_prev != NULL; ipr_prev = ipr->next) {
+ if (ipr_prev->next == ipr) {
+ break;
+ }
+ }
+
+ /* release the sources allocate for the fragment queue entry */
+ ip_reass_dequeue_datagram(ipr, ipr_prev);
+
+ /* and adjust the number of pbufs currently queued for reassembly. */
+ ip_reass_pbufcount -= pbuf_clen(p);
+
+ /* Return the pbuf chain */
+ return p;
+ }
+ /* the datagram is not (yet?) reassembled completely */
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass_pbufcount: %d out\n", ip_reass_pbufcount));
+ return NULL;
+
+nullreturn:
+ LWIP_DEBUGF(IP_REASS_DEBUG,("ip_reass: nullreturn\n"));
+ IPFRAG_STATS_INC(ip_frag.drop);
+ pbuf_free(p);
+ return NULL;
+}
+#endif /* IP_REASSEMBLY */
+
+#if IP_FRAG
+#if IP_FRAG_USES_STATIC_BUF
+static u8_t buf[LWIP_MEM_ALIGN_SIZE(IP_FRAG_MAX_MTU + MEM_ALIGNMENT - 1)];
+#else /* IP_FRAG_USES_STATIC_BUF */
+
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+/** Allocate a new struct pbuf_custom_ref */
+static struct pbuf_custom_ref*
+ip_frag_alloc_pbuf_custom_ref(void)
+{
+ return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
+}
+
+/** Free a struct pbuf_custom_ref */
+static void
+ip_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
+{
+ LWIP_ASSERT("p != NULL", p != NULL);
+ memp_free(MEMP_FRAG_PBUF, p);
+}
+
+/** Free-callback function to free a 'struct pbuf_custom_ref', called by
+ * pbuf_free. */
+static void
+ipfrag_free_pbuf_custom(struct pbuf *p)
+{
+ struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
+ LWIP_ASSERT("pcr != NULL", pcr != NULL);
+ LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
+ if (pcr->original != NULL) {
+ pbuf_free(pcr->original);
+ }
+ ip_frag_free_pbuf_custom_ref(pcr);
+}
+#endif /* !LWIP_NETIF_TX_SINGLE_PBUF */
+#endif /* IP_FRAG_USES_STATIC_BUF */
+
+/**
+ * Fragment an IP datagram if too large for the netif.
+ *
+ * Chop the datagram in MTU sized chunks and send them in order
+ * by using a fixed size static memory buffer (PBUF_REF) or
+ * point PBUF_REFs into p (depending on IP_FRAG_USES_STATIC_BUF).
+ *
+ * @param p ip packet to send
+ * @param netif the netif on which to send
+ * @param dest destination ip address to which to send
+ *
+ * @return ERR_OK if sent successfully, err_t otherwise
+ */
+err_t
+ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest)
+{
+ struct pbuf *rambuf;
+#if IP_FRAG_USES_STATIC_BUF
+ struct pbuf *header;
+#else
+#if !LWIP_NETIF_TX_SINGLE_PBUF
+ struct pbuf *newpbuf;
+#endif
+ struct ip_hdr *original_iphdr;
+#endif
+ struct ip_hdr *iphdr;
+ u16_t nfb;
+ u16_t left, cop;
+ u16_t mtu = netif->mtu;
+ u16_t ofo, omf;
+ u16_t last;
+ u16_t poff = IP_HLEN;
+ u16_t tmp;
+#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF
+ u16_t newpbuflen = 0;
+ u16_t left_to_copy;
+#endif
+
+ /* Get a RAM based MTU sized pbuf */
+#if IP_FRAG_USES_STATIC_BUF
+ /* When using a static buffer, we use a PBUF_REF, which we will
+ * use to reference the packet (without link header).
+ * Layer and length is irrelevant.
+ */
+ rambuf = pbuf_alloc(PBUF_LINK, 0, PBUF_REF);
+ if (rambuf == NULL) {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc(PBUF_LINK, 0, PBUF_REF) failed\n"));
+ return ERR_MEM;
+ }
+ rambuf->tot_len = rambuf->len = mtu;
+ rambuf->payload = LWIP_MEM_ALIGN((void *)buf);
+
+ /* Copy the IP header in it */
+ iphdr = (struct ip_hdr *)rambuf->payload;
+ SMEMCPY(iphdr, p->payload, IP_HLEN);
+#else /* IP_FRAG_USES_STATIC_BUF */
+ original_iphdr = (struct ip_hdr *)p->payload;
+ iphdr = original_iphdr;
+#endif /* IP_FRAG_USES_STATIC_BUF */
+
+ /* Save original offset */
+ tmp = ntohs(IPH_OFFSET(iphdr));
+ ofo = tmp & IP_OFFMASK;
+ omf = tmp & IP_MF;
+
+ left = p->tot_len - IP_HLEN;
+
+ nfb = (mtu - IP_HLEN) / 8;
+
+ while (left) {
+ last = (left <= mtu - IP_HLEN);
+
+ /* Set new offset and MF flag */
+ tmp = omf | (IP_OFFMASK & (ofo));
+ if (!last) {
+ tmp = tmp | IP_MF;
+ }
+
+ /* Fill this fragment */
+ cop = last ? left : nfb * 8;
+
+#if IP_FRAG_USES_STATIC_BUF
+ poff += pbuf_copy_partial(p, (u8_t*)iphdr + IP_HLEN, cop, poff);
+#else /* IP_FRAG_USES_STATIC_BUF */
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ rambuf = pbuf_alloc(PBUF_IP, cop, PBUF_RAM);
+ if (rambuf == NULL) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (rambuf->len == rambuf->tot_len) && (rambuf->next == NULL));
+ poff += pbuf_copy_partial(p, rambuf->payload, cop, poff);
+ /* make room for the IP header */
+ if(pbuf_header(rambuf, IP_HLEN)) {
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+ /* fill in the IP header */
+ SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+ iphdr = rambuf->payload;
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ /* When not using a static buffer, create a chain of pbufs.
+ * The first will be a PBUF_RAM holding the link and IP header.
+ * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
+ * but limited to the size of an mtu.
+ */
+ rambuf = pbuf_alloc(PBUF_LINK, IP_HLEN, PBUF_RAM);
+ if (rambuf == NULL) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (p->len >= (IP_HLEN)));
+ SMEMCPY(rambuf->payload, original_iphdr, IP_HLEN);
+ iphdr = (struct ip_hdr *)rambuf->payload;
+
+ /* Can just adjust p directly for needed offset. */
+ p->payload = (u8_t *)p->payload + poff;
+ p->len -= poff;
+
+ left_to_copy = cop;
+ while (left_to_copy) {
+ struct pbuf_custom_ref *pcr;
+ newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
+ /* Is this pbuf already empty? */
+ if (!newpbuflen) {
+ p = p->next;
+ continue;
+ }
+ pcr = ip_frag_alloc_pbuf_custom_ref();
+ if (pcr == NULL) {
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+ /* Mirror this pbuf, although we might not need all of it. */
+ newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
+ if (newpbuf == NULL) {
+ ip_frag_free_pbuf_custom_ref(pcr);
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+ pbuf_ref(p);
+ pcr->original = p;
+ pcr->pc.custom_free_function = ipfrag_free_pbuf_custom;
+
+ /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
+ * so that it is removed when pbuf_dechain is later called on rambuf.
+ */
+ pbuf_cat(rambuf, newpbuf);
+ left_to_copy -= newpbuflen;
+ if (left_to_copy) {
+ p = p->next;
+ }
+ }
+ poff = newpbuflen;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+#endif /* IP_FRAG_USES_STATIC_BUF */
+
+ /* Correct header */
+ IPH_OFFSET_SET(iphdr, htons(tmp));
+ IPH_LEN_SET(iphdr, htons(cop + IP_HLEN));
+ IPH_CHKSUM_SET(iphdr, 0);
+#if CHECKSUM_GEN_IP
+ IPH_CHKSUM_SET(iphdr, inet_chksum(iphdr, IP_HLEN));
+#endif /* CHECKSUM_GEN_IP */
+
+#if IP_FRAG_USES_STATIC_BUF
+ if (last) {
+ pbuf_realloc(rambuf, left + IP_HLEN);
+ }
+
+ /* This part is ugly: we alloc a RAM based pbuf for
+ * the link level header for each chunk and then
+ * free it.A PBUF_ROM style pbuf for which pbuf_header
+ * worked would make things simpler.
+ */
+ header = pbuf_alloc(PBUF_LINK, 0, PBUF_RAM);
+ if (header != NULL) {
+ pbuf_chain(header, rambuf);
+ netif->output(netif, header, dest);
+ IPFRAG_STATS_INC(ip_frag.xmit);
+ snmp_inc_ipfragcreates();
+ pbuf_free(header);
+ } else {
+ LWIP_DEBUGF(IP_REASS_DEBUG, ("ip_frag: pbuf_alloc() for header failed\n"));
+ pbuf_free(rambuf);
+ return ERR_MEM;
+ }
+#else /* IP_FRAG_USES_STATIC_BUF */
+ /* No need for separate header pbuf - we allowed room for it in rambuf
+ * when allocated.
+ */
+ netif->output(netif, rambuf, dest);
+ IPFRAG_STATS_INC(ip_frag.xmit);
+
+ /* Unfortunately we can't reuse rambuf - the hardware may still be
+ * using the buffer. Instead we free it (and the ensuing chain) and
+ * recreate it next time round the loop. If we're lucky the hardware
+ * will have already sent the packet, the free will really free, and
+ * there will be zero memory penalty.
+ */
+
+ pbuf_free(rambuf);
+#endif /* IP_FRAG_USES_STATIC_BUF */
+ left -= cop;
+ ofo += nfb;
+ }
+#if IP_FRAG_USES_STATIC_BUF
+ pbuf_free(rambuf);
+#endif /* IP_FRAG_USES_STATIC_BUF */
+ snmp_inc_ipfragoks();
+ return ERR_OK;
+}
+#endif /* IP_FRAG */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv6/Makefile.kup b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/Makefile.kup
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv6/README b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/README
new file mode 100644
index 00000000..36200048
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/README
@@ -0,0 +1 @@
+IPv6 support in lwIP is very experimental.
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv6/dhcp6.c b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/dhcp6.c
new file mode 100644
index 00000000..9656c3b2
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/dhcp6.c
@@ -0,0 +1,50 @@
+/**
+ * @file
+ *
+ * DHCPv6.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip6_addr.h"
+#include "lwip/def.h"
+
+
+#endif /* LWIP_IPV6_DHCP6 */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv6/ethip6.c b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/ethip6.c
new file mode 100644
index 00000000..ab9783a0
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/ethip6.c
@@ -0,0 +1,193 @@
+/**
+ * @file
+ *
+ * Ethernet output for IPv6. Uses ND tables for link-layer addressing.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_ETHERNET
+
+#include "lwip/ethip6.h"
+#include "lwip/nd6.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp6.h"
+
+#include <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, ...
+ *
+ * @TODO anycast addresses
+ *
+ * @param netif The lwIP network interface which the IP packet will be sent on.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ip6addr The IP address of the packet destination.
+ *
+ * @return
+ * - ERR_RTE No route to destination (no gateway to external networks),
+ * or the return type of either etharp_query() or etharp_send_ip().
+ */
+err_t
+ethip6_output(struct netif *netif, struct pbuf *q, ip6_addr_t *ip6addr)
+{
+ struct eth_addr dest;
+ s8_t i;
+
+ /* make room for Ethernet header - should not fail */
+ if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
+ /* bail out */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("etharp_output: could not allocate room for header.\n"));
+ return ERR_BUF;
+ }
+
+ /* multicast destination IP address? */
+ if (ip6_addr_ismulticast(ip6addr)) {
+ /* Hash IP multicast address to MAC address.*/
+ dest.addr[0] = 0x33;
+ dest.addr[1] = 0x33;
+ dest.addr[2] = ((u8_t *)(&(ip6addr->addr[3])))[0];
+ dest.addr[3] = ((u8_t *)(&(ip6addr->addr[3])))[1];
+ dest.addr[4] = ((u8_t *)(&(ip6addr->addr[3])))[2];
+ dest.addr[5] = ((u8_t *)(&(ip6addr->addr[3])))[3];
+
+ /* Send out. */
+ return ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest);
+ }
+
+ /* We have a unicast destination IP address */
+ /* TODO anycast? */
+ /* Get next hop record. */
+ i = nd6_get_next_hop_entry(ip6addr, netif);
+ if (i < 0) {
+ /* failed to get a next hop neighbor record. */
+ return ERR_MEM;
+ }
+
+ /* Now that we have a destination record, send or queue the packet. */
+ if (neighbor_cache[i].state == ND6_STALE) {
+ /* Switch to delay state. */
+ neighbor_cache[i].state = ND6_DELAY;
+ neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME;
+ }
+ /* TODO should we send or queue if PROBE? send for now, to let unicast NS pass. */
+ if ((neighbor_cache[i].state == ND6_REACHABLE) ||
+ (neighbor_cache[i].state == ND6_DELAY) ||
+ (neighbor_cache[i].state == ND6_PROBE)) {
+
+ /* Send out. */
+ SMEMCPY(dest.addr, neighbor_cache[i].lladdr, 6);
+ return ethip6_send(netif, q, (struct eth_addr*)(netif->hwaddr), &dest);
+ }
+
+ /* We should queue packet on this interface. */
+ pbuf_header(q, -(s16_t)SIZEOF_ETH_HDR);
+ return nd6_queue_packet(i, q);
+}
+
+#endif /* LWIP_IPV6 && LWIP_ETHERNET */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv6/icmp6.c b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/icmp6.c
new file mode 100644
index 00000000..66f5f90e
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/icmp6.c
@@ -0,0 +1,410 @@
+/**
+ * @file
+ *
+ * IPv6 version of ICMP, as per RFC 4443.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ICMP6 && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/icmp6.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/nd6.h"
+#include "lwip/mld6.h"
+#include "lwip/ip.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#ifndef LWIP_ICMP6_DATASIZE
+#define LWIP_ICMP6_DATASIZE 8
+#endif
+#if LWIP_ICMP6_DATASIZE == 0
+#define LWIP_ICMP6_DATASIZE 8
+#endif
+
+/* Forward declarations */
+static void icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type);
+
+#if LWIP_CONNECTION_PROXY
+static ping6_proxy_fn ping6_proxy_accept_callback;
+static void *ping6_proxy_accept_arg;
+#endif
+
+
+/**
+ * Process an input ICMPv6 message. Called by ip6_input.
+ *
+ * Will generate a reply for echo requests. Other messages are forwarded
+ * to nd6_input, or mld6_input.
+ *
+ * @param p the mld packet, p->payload pointing to the icmpv6 header
+ * @param inp the netif on which this packet was received
+ */
+void
+icmp6_input(struct pbuf *p, struct netif *inp)
+{
+ struct icmp6_hdr *icmp6hdr;
+ struct pbuf * r;
+ ip6_addr_t * reply_src;
+
+ ICMP6_STATS_INC(icmp6.recv);
+
+ /* Check that ICMPv6 header fits in payload */
+ if (p->len < sizeof(struct icmp6_hdr)) {
+ /* drop short packets */
+ pbuf_free(p);
+ ICMP6_STATS_INC(icmp6.lenerr);
+ ICMP6_STATS_INC(icmp6.drop);
+ return;
+ }
+
+ icmp6hdr = (struct icmp6_hdr *)p->payload;
+
+#if CHECKSUM_CHECK_ICMP6
+ if (ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->tot_len, ip6_current_src_addr(),
+ ip6_current_dest_addr()) != 0) {
+ /* Checksum failed */
+ pbuf_free(p);
+ ICMP6_STATS_INC(icmp6.chkerr);
+ ICMP6_STATS_INC(icmp6.drop);
+ return;
+ }
+#endif /* CHECKSUM_CHECK_ICMP6 */
+
+ switch (icmp6hdr->type) {
+ case ICMP6_TYPE_NA: /* Neighbor advertisement */
+ case ICMP6_TYPE_NS: /* Neighbor solicitation */
+ case ICMP6_TYPE_RA: /* Router advertisement */
+ case ICMP6_TYPE_RD: /* Redirect */
+ case ICMP6_TYPE_PTB: /* Packet too big */
+ nd6_input(p, inp);
+ return;
+ break;
+ case ICMP6_TYPE_RS:
+#if LWIP_IPV6_FORWARD
+ /* TODO implement router functionality */
+#endif
+ break;
+#if LWIP_IPV6_MLD
+ case ICMP6_TYPE_MLQ:
+ case ICMP6_TYPE_MLR:
+ case ICMP6_TYPE_MLD:
+ mld6_input(p, inp);
+ return;
+ break;
+#endif
+ case ICMP6_TYPE_EREQ:
+#if !LWIP_MULTICAST_PING
+ /* multicast destination address? */
+ if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
+ /* drop */
+ pbuf_free(p);
+ ICMP6_STATS_INC(icmp6.drop);
+ return;
+ }
+#endif /* LWIP_MULTICAST_PING */
+
+ /* Allocate reply. */
+ r = pbuf_alloc(PBUF_IP, p->tot_len, PBUF_RAM);
+ if (r == NULL) {
+ /* drop */
+ pbuf_free(p);
+ ICMP6_STATS_INC(icmp6.memerr);
+ return;
+ }
+
+ /* Copy echo request. */
+ if (pbuf_copy(r, p) != ERR_OK) {
+ /* drop */
+ pbuf_free(p);
+ pbuf_free(r);
+ ICMP6_STATS_INC(icmp6.err);
+ return;
+ }
+
+ /* Determine reply source IPv6 address. */
+#if LWIP_MULTICAST_PING
+ if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
+ reply_src = ip6_select_source_address(inp, ip6_current_src_addr());
+ if (reply_src == NULL) {
+ /* drop */
+ pbuf_free(p);
+ pbuf_free(r);
+ ICMP6_STATS_INC(icmp6.rterr);
+ return;
+ }
+ }
+ else
+#endif /* LWIP_MULTICAST_PING */
+ {
+ reply_src = ip6_current_dest_addr();
+ }
+
+ /* Set fields in reply. */
+ ((struct icmp6_echo_hdr *)(r->payload))->type = ICMP6_TYPE_EREP;
+ ((struct icmp6_echo_hdr *)(r->payload))->chksum = 0;
+#if CHECKSUM_GEN_ICMP6
+ ((struct icmp6_echo_hdr *)(r->payload))->chksum = ip6_chksum_pseudo(r,
+ IP6_NEXTH_ICMP6, r->tot_len, reply_src, ip6_current_src_addr());
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+ /* Send reply. */
+ ICMP6_STATS_INC(icmp6.xmit);
+ ip6_output_if(r, reply_src, ip6_current_src_addr(),
+ LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, inp);
+ pbuf_free(r);
+
+ break;
+ default:
+ ICMP6_STATS_INC(icmp6.proterr);
+ ICMP6_STATS_INC(icmp6.drop);
+ break;
+ }
+
+ pbuf_free(p);
+}
+
+
+#if LWIP_CONNECTION_PROXY
+
+void
+ping6_proxy_accept(ping6_proxy_fn callback, void *arg)
+{
+ ping6_proxy_accept_callback = callback;
+ ping6_proxy_accept_arg = arg;
+}
+
+
+void
+icmp6_proxy_input(struct pbuf *p, struct netif *inp)
+{
+ struct icmp6_hdr *icmp6hdr;
+# ifdef VBOX
+ (void)inp;
+# endif
+
+ ICMP6_STATS_INC(icmp6.recv);
+
+ /* Check that ICMPv6 header fits in payload */
+ if (p->len < sizeof(struct icmp6_hdr)) {
+ /* drop short packets */
+ pbuf_free(p);
+ ICMP6_STATS_INC(icmp6.lenerr);
+ ICMP6_STATS_INC(icmp6.drop);
+ return;
+ }
+
+ icmp6hdr = (struct icmp6_hdr *)p->payload;
+ if (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;
+ }
+
+ switch (icmp6hdr->type) {
+
+ case ICMP6_TYPE_EREQ:
+ if (ping6_proxy_accept_callback != NULL) {
+ (*ping6_proxy_accept_callback)(ping6_proxy_accept_arg, p);
+ return;
+ }
+ ICMP6_STATS_INC(icmp6.drop);
+ break;
+
+ case ICMP6_TYPE_EREP:
+ /* ignore silently */
+ ICMP6_STATS_INC(icmp6.drop);
+ break;
+
+ default:
+ ICMP6_STATS_INC(icmp6.drop);
+ break;
+ }
+
+ pbuf_free(p);
+}
+#endif /* LWIP_CONNECTION_PROXY */
+
+/**
+ * Send an icmpv6 'destination unreachable' packet.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ * p->payload pointing to the IPv6 header
+ * @param c ICMPv6 code for the unreachable type
+ */
+void
+icmp6_dest_unreach(struct pbuf *p, enum icmp6_dur_code c)
+{
+ icmp6_send_response(p, c, 0, ICMP6_TYPE_DUR);
+}
+
+/**
+ * Send an icmpv6 'packet too big' packet.
+ *
+ * @param p the input packet for which the 'packet too big' should be sent,
+ * p->payload pointing to the IPv6 header
+ * @param mtu the maximum mtu that we can accept
+ */
+void
+icmp6_packet_too_big(struct pbuf *p, u32_t mtu)
+{
+ icmp6_send_response(p, 0, mtu, ICMP6_TYPE_PTB);
+}
+
+/**
+ * Send an icmpv6 'time exceeded' packet.
+ *
+ * @param p the input packet for which the 'unreachable' should be sent,
+ * p->payload pointing to the IPv6 header
+ * @param c ICMPv6 code for the time exceeded type
+ */
+void
+icmp6_time_exceeded(struct pbuf *p, enum icmp6_te_code c)
+{
+ icmp6_send_response(p, c, 0, ICMP6_TYPE_TE);
+}
+
+/**
+ * Send an icmpv6 'parameter problem' packet.
+ *
+ * @param p the input packet for which the 'param problem' should be sent,
+ * p->payload pointing to the IP header
+ * @param c ICMPv6 code for the param problem type
+ * @param pointer the pointer to the byte where the parameter is found
+ */
+void
+icmp6_param_problem(struct pbuf *p, enum icmp6_pp_code c, u32_t pointer)
+{
+ icmp6_send_response(p, c, pointer, ICMP6_TYPE_PP);
+}
+
+/**
+ * Send an ICMPv6 packet in response to an incoming packet.
+ *
+ * @param p the input packet for which the response should be sent,
+ * p->payload pointing to the IPv6 header
+ * @param code Code of the ICMPv6 header
+ * @param data Additional 32-bit parameter in the ICMPv6 header
+ * @param type Type of the ICMPv6 header
+ */
+static void
+icmp6_send_response(struct pbuf *p, u8_t code, u32_t data, u8_t type)
+{
+ struct pbuf *q;
+ struct icmp6_hdr *icmp6hdr;
+ ip6_addr_t *reply_src, *reply_dest;
+ ip6_addr_t reply_src_local, reply_dest_local;
+ struct ip6_hdr *ip6hdr;
+ struct netif *netif;
+
+ /* ICMPv6 header + IPv6 header + data */
+ q = pbuf_alloc(PBUF_IP, sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE,
+ PBUF_RAM);
+ if (q == NULL) {
+ LWIP_DEBUGF(ICMP_DEBUG, ("icmp_time_exceeded: failed to allocate pbuf for ICMPv6 packet.\n"));
+ ICMP6_STATS_INC(icmp6.memerr);
+ return;
+ }
+ LWIP_ASSERT("check that first pbuf can hold icmp 6message",
+ (q->len >= (sizeof(struct icmp6_hdr) + IP6_HLEN + LWIP_ICMP6_DATASIZE)));
+
+ icmp6hdr = (struct icmp6_hdr *)q->payload;
+ icmp6hdr->type = type;
+ icmp6hdr->code = code;
+ icmp6hdr->data = data;
+
+ /* copy fields from original packet */
+ SMEMCPY((u8_t *)q->payload + sizeof(struct icmp6_hdr), (u8_t *)p->payload,
+ IP6_HLEN + LWIP_ICMP6_DATASIZE);
+
+ /* Get the destination address and netif for this ICMP message. */
+ if ((ip_current_netif() == NULL) ||
+ ((code == ICMP6_TE_FRAG) && (type == ICMP6_TYPE_TE))) {
+ /* Special case, as ip6_current_xxx is either NULL, or points
+ * to a different packet than the one that expired.
+ * We must use the addresses that are stored in the expired packet. */
+ ip6hdr = (struct ip6_hdr *)p->payload;
+ /* copy from packed address to aligned address */
+ ip6_addr_copy(reply_dest_local, ip6hdr->src);
+ ip6_addr_copy(reply_src_local, ip6hdr->dest);
+ reply_dest = &reply_dest_local;
+ reply_src = &reply_src_local;
+ netif = ip6_route(reply_src, reply_dest);
+ if (netif == NULL) {
+ /* drop */
+ pbuf_free(q);
+ ICMP6_STATS_INC(icmp6.rterr);
+ return;
+ }
+ }
+ else {
+ netif = ip_current_netif();
+ reply_dest = ip6_current_src_addr();
+
+ /* Select an address to use as source. */
+ reply_src = ip6_select_source_address(netif, reply_dest);
+ if (reply_src == NULL) {
+ /* drop */
+ pbuf_free(q);
+ ICMP6_STATS_INC(icmp6.rterr);
+ return;
+ }
+ }
+
+ /* calculate checksum */
+ icmp6hdr->chksum = 0;
+#if CHECKSUM_GEN_ICMP6
+ icmp6hdr->chksum = ip6_chksum_pseudo(q, IP6_NEXTH_ICMP6, q->tot_len,
+ reply_src, reply_dest);
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+ ICMP6_STATS_INC(icmp6.xmit);
+ ip6_output_if(q, reply_src, reply_dest, LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
+ pbuf_free(q);
+}
+
+#endif /* LWIP_ICMP6 && LWIP_IPV6 */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv6/inet6.c b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/inet6.c
new file mode 100644
index 00000000..bdf4ff4f
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/inet6.c
@@ -0,0 +1,51 @@
+/**
+ * @file
+ *
+ * INET v6 addresses.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/inet6.h"
+
+/** @see ip6_addr.c for implementation of functions. */
+
+#endif /* LWIP_IPV6 */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv6/ip6.c b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/ip6.c
new file mode 100644
index 00000000..43a1d216
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/ip6.c
@@ -0,0 +1,1182 @@
+/**
+ * @file
+ *
+ * IPv6 layer.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/netif.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/ip6_frag.h"
+#include "lwip/icmp6.h"
+#include "lwip/raw.h"
+#include "lwip/udp.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/dhcp6.h"
+#include "lwip/nd6.h"
+#include "lwip/mld6.h"
+#include "lwip/debug.h"
+#include "lwip/stats.h"
+
+/* see comment in "lwip/ip.h" */
+#ifdef IP_HDRINCL
+#undef IP_HDRINCL
+#endif
+#define IP_HDRINCL LWIP_IP_HDRINCL
+
+#if LWIP_CONNECTION_PROXY
+proxy_ip6_divert_hook_fn proxy_ip6_divert_hook;
+#endif
+
+
+/**
+ * Finds the appropriate network interface for a given IPv6 address. It tries to select
+ * a netif following a sequence of heuristics:
+ * 1) if there is only 1 netif, return it
+ * 2) if the destination is a link-local address, try to match the src address to a netif.
+ * this is a tricky case because with multiple netifs, link-local addresses only have
+ * meaning within a particular subnet/link.
+ * 3) tries to match the destination subnet to a configured address
+ * 4) tries to find a router
+ * 5) tries to match the source address to the netif
+ * 6) returns the default netif, if configured
+ *
+ * @param src the source IPv6 address, if known
+ * @param dest the destination IPv6 address for which to find the route
+ * @return the netif on which to send to reach dest
+ */
+struct netif *
+ip6_route(struct ip6_addr *src, struct ip6_addr *dest)
+{
+ struct netif *netif;
+ s8_t i;
+
+ /* If single netif configuration, fast return. */
+ if ((netif_list != NULL) && (netif_list->next == NULL)) {
+ return netif_list;
+ }
+
+ /* Special processing for link-local addresses. */
+ if (ip6_addr_islinklocal(dest)) {
+ if (ip6_addr_isany(src)) {
+ /* Use default netif. */
+ return netif_default;
+ }
+
+ /* Try to find the netif for the source address. */
+ for(netif = netif_list; netif != NULL; netif = netif->next) {
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_cmp(src, netif_ip6_addr(netif, i))) {
+ return netif;
+ }
+ }
+ }
+
+ /* netif not found, use default netif */
+ return netif_default;
+ }
+
+ /* See if the destination subnet matches a configured address. */
+ for(netif = netif_list; netif != NULL; netif = netif->next) {
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
+ return netif;
+ }
+ }
+ }
+
+ /* Get the netif for a suitable router. */
+ i = nd6_select_router(dest, NULL);
+ if (i >= 0) {
+ if (default_router_list[i].neighbor_entry != NULL) {
+ if (default_router_list[i].neighbor_entry->netif != NULL) {
+ return default_router_list[i].neighbor_entry->netif;
+ }
+ }
+ }
+
+ /* try with the netif that matches the source address. */
+ if (!ip6_addr_isany(src)) {
+ for(netif = netif_list; netif != NULL; netif = netif->next) {
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_cmp(src, netif_ip6_addr(netif, i))) {
+ return netif;
+ }
+ }
+ }
+ }
+
+ /* no matching netif found, use default netif */
+ return netif_default;
+}
+
+#if LWIP_IPV6_FORWARD
+/*
+ * XXX: ip6_route is too eager to return single/default netif, and
+ * forwarding logic is different from the logic to select outgoing
+ * interface for our own packets anyway.
+ */
+static struct netif *
+ip6_route_fwd(struct ip6_addr *dest)
+{
+ struct netif *netif;
+ s8_t i;
+
+ /* Link-local addresses are not routable. */
+ if (ip6_addr_islinklocal(dest)) {
+ return NULL;
+ }
+
+ /* See if the destination subnet matches a configured address. */
+ for(netif = netif_list; netif != NULL; netif = netif->next) {
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
+ return netif;
+ }
+ }
+ }
+
+ /* Get the netif for a suitable router. */
+ i = nd6_select_router(dest, NULL);
+ if (i >= 0) {
+ if (default_router_list[i].neighbor_entry != NULL) {
+ if (default_router_list[i].neighbor_entry->netif != NULL) {
+ return default_router_list[i].neighbor_entry->netif;
+ }
+ }
+ }
+
+ /* no matching netif found */
+ return NULL;
+}
+#endif /* LWIP_IPV6_FORWARD */
+
+/**
+ * Select the best IPv6 source address for a given destination
+ * IPv6 address. Loosely follows RFC 3484. "Strong host" behavior
+ * is assumed.
+ *
+ * @param netif the netif on which to send a packet
+ * @param dest the destination we are trying to reach
+ * @return the most suitable source address to use, or NULL if no suitable
+ * source address is found
+ */
+ip6_addr_t *
+ip6_select_source_address(struct netif *netif, ip6_addr_t * dest)
+{
+ ip6_addr_t * src = NULL;
+ u8_t i;
+
+ /* If dest is link-local, choose a link-local source. */
+ if (ip6_addr_islinklocal(dest) || ip6_addr_ismulticast_linklocal(dest) || ip6_addr_ismulticast_iflocal(dest)) {
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_islinklocal(netif_ip6_addr(netif, i))) {
+ return netif_ip6_addr(netif, i);
+ }
+ }
+ }
+
+ /* Choose a site-local with matching prefix. */
+ if (ip6_addr_issitelocal(dest) || ip6_addr_ismulticast_sitelocal(dest)) {
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_issitelocal(netif_ip6_addr(netif, i)) &&
+ ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
+ return netif_ip6_addr(netif, i);
+ }
+ }
+ }
+
+ /* Choose a unique-local with matching prefix. */
+ if (ip6_addr_isuniquelocal(dest) || ip6_addr_ismulticast_orglocal(dest)) {
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_isuniquelocal(netif_ip6_addr(netif, i)) &&
+ ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
+ return netif_ip6_addr(netif, i);
+ }
+ }
+ }
+
+ /* Choose a global with best matching prefix. */
+ if (ip6_addr_isglobal(dest) || ip6_addr_ismulticast_global(dest)) {
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_isglobal(netif_ip6_addr(netif, i))) {
+ if (src == NULL) {
+ src = netif_ip6_addr(netif, i);
+ }
+ else {
+ /* Replace src only if we find a prefix match. */
+ /* TODO find longest matching prefix. */
+ if ((!(ip6_addr_netcmp(src, dest))) &&
+ ip6_addr_netcmp(netif_ip6_addr(netif, i), dest)) {
+ src = netif_ip6_addr(netif, i);
+ }
+ }
+ }
+ }
+ if (src != NULL) {
+ return src;
+ }
+ }
+
+ /* Last resort: see if arbitrary prefix matches. */
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_netcmp(dest, netif_ip6_addr(netif, i))) {
+ return netif_ip6_addr(netif, i);
+ }
+ }
+
+ return NULL;
+}
+
+#if LWIP_IPV6_FORWARD
+/**
+ * Forwards an IPv6 packet. It finds an appropriate route for the
+ * packet, decrements the HL value of the packet, and outputs
+ * the packet on the appropriate interface.
+ *
+ * @param p the packet to forward (p->payload points to IP header)
+ * @param iphdr the IPv6 header of the input packet
+ * @param inp the netif on which this packet was received
+ * @return -1 if packet is to be dropped, 0 if there's no route, 1 if forwarded
+ */
+static int
+ip6_forward(struct pbuf *p, struct ip6_hdr *iphdr, struct netif *inp)
+{
+ enum { FWD_DROP = -1, FWD_PROXY = 0, FWD_FORWARDED = 1 };
+ struct netif *netif;
+
+ /* do not forward link-local addresses */
+ if (ip6_addr_islinklocal(ip6_current_dest_addr())) {
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not forwarding link-local address.\n"));
+ IP6_STATS_INC(ip6.rterr);
+ IP6_STATS_INC(ip6.drop);
+ return FWD_DROP;
+ }
+
+ /* Find network interface where to forward this IP packet to. */
+ netif = ip6_route_fwd(ip6_current_dest_addr());
+ if (netif == NULL) {
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n",
+ IP6_ADDR_BLOCK1(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK2(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK3(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK4(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK5(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK6(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK7(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK8(ip6_current_dest_addr())));
+#if LWIP_CONNECTION_PROXY
+ return FWD_PROXY;
+#else /* !LWIP_CONNECTION_PROXY */
+#if LWIP_ICMP6
+ /* Don't send ICMP messages in response to ICMP messages */
+ if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) {
+ icmp6_dest_unreach(p, ICMP6_DUR_NO_ROUTE);
+ }
+#endif /* LWIP_ICMP6 */
+ IP6_STATS_INC(ip6.rterr);
+ IP6_STATS_INC(ip6.drop);
+ return FWD_DROP;
+#endif /* !LWIP_CONNECTION_PROXY */
+ }
+
+#if LWIP_CONNECTION_PROXY
+ /* The packet is for a destination on a directly connected network.
+ * Check for addresses in that address space that proxy wants to
+ * remap (e.g. to host loopback address/es) and hand it off to
+ * proxy */
+ if (netif != netif_default
+ && proxy_ip6_divert_hook != NULL
+ && (*proxy_ip6_divert_hook)(netif, ip6_current_dest_addr()))
+ {
+ return FWD_PROXY;
+ }
+#endif /* LWIP_CONNECTION_PROXY */
+
+ /* Do not forward packets onto the same network interface on which
+ * they arrived. */
+ if (netif == inp) {
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: not bouncing packets back on incoming interface.\n"));
+ IP6_STATS_INC(ip6.rterr);
+ IP6_STATS_INC(ip6.drop);
+ return FWD_DROP;
+ }
+
+ /* decrement HL */
+ IP6H_HOPLIM_SET(iphdr, IP6H_HOPLIM(iphdr) - 1);
+ /* send ICMP6 if HL == 0 */
+ if (IP6H_HOPLIM(iphdr) == 0) {
+#if LWIP_ICMP6
+ /* Don't send ICMP messages in response to ICMP messages */
+ if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) {
+ icmp6_time_exceeded(p, ICMP6_TE_HL);
+ }
+#endif /* LWIP_ICMP6 */
+ IP6_STATS_INC(ip6.drop);
+ return FWD_DROP;
+ }
+
+ if (netif->mtu && (p->tot_len > netif->mtu)) {
+#if LWIP_ICMP6
+ /* Don't send ICMP messages in response to ICMP messages */
+ if (IP6H_NEXTH(iphdr) != IP6_NEXTH_ICMP6) {
+ icmp6_packet_too_big(p, netif->mtu);
+ }
+#endif /* LWIP_ICMP6 */
+ IP6_STATS_INC(ip6.drop);
+ return FWD_DROP;
+ }
+
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_forward: forwarding packet to %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n",
+ IP6_ADDR_BLOCK1(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK2(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK3(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK4(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK5(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK6(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK7(ip6_current_dest_addr()),
+ IP6_ADDR_BLOCK8(ip6_current_dest_addr())));
+
+ /* transmit pbuf on chosen interface */
+ netif->output_ip6(netif, p, ip6_current_dest_addr());
+ IP6_STATS_INC(ip6.fw);
+ IP6_STATS_INC(ip6.xmit);
+ return FWD_FORWARDED;
+}
+#endif /* LWIP_IPV6_FORWARD */
+
+
+/**
+ * This function is called by the network interface device driver when
+ * an IPv6 packet is received. The function does the basic checks of the
+ * IP header such as packet size being at least larger than the header
+ * size etc. If the packet was not destined for us, the packet is
+ * forwarded (using ip6_forward).
+ *
+ * Finally, the packet is sent to the upper layer protocol input function.
+ *
+ * @param p the received IPv6 packet (p->payload points to IPv6 header)
+ * @param inp the netif on which this packet was received
+ * @return ERR_OK if the packet was processed (could return ERR_* if it wasn't
+ * processed, but currently always returns ERR_OK)
+ */
+err_t
+ip6_input(struct pbuf *p, struct netif *inp)
+{
+ struct ip6_hdr *ip6hdr;
+ struct netif *netif;
+#if LWIP_CONNECTION_PROXY
+ int proxy = 0;
+#endif
+ u8_t nexth;
+ u16_t hlen; /* the current header length */
+ u8_t i;
+#if 0 /*IP_ACCEPT_LINK_LAYER_ADDRESSING*/
+ @todo
+ int check_ip_src=1;
+#endif /* IP_ACCEPT_LINK_LAYER_ADDRESSING */
+
+ IP6_STATS_INC(ip6.recv);
+
+ /* drop if incoming interface doesn't have IPv6 configured */
+ if (ip6_addr_isinvalid(netif_ip6_addr_state(inp, 0))) {
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.drop);
+ return ERR_OK;
+ }
+
+ /* identify the IP header */
+ ip6hdr = (struct ip6_hdr *)p->payload;
+ if (IP6H_V(ip6hdr) != 6) {
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_WARNING, ("IPv6 packet dropped due to bad version number %"U32_F"\n",
+ IP6H_V(ip6hdr)));
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.err);
+ IP6_STATS_INC(ip6.drop);
+ return ERR_OK;
+ }
+
+ /* header length exceeds first pbuf length, or ip length exceeds total pbuf length? */
+ if ((IP6_HLEN > p->len) || ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len)) {
+ if (IP6_HLEN > p->len) {
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IPv6 header (len %"U16_F") does not fit in first pbuf (len %"U16_F"), IP packet dropped.\n",
+ IP6_HLEN, p->len));
+ }
+ if ((IP6H_PLEN(ip6hdr) + IP6_HLEN) > p->tot_len) {
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IPv6 (plen %"U16_F") is longer than pbuf (len %"U16_F"), IP packet dropped.\n",
+ IP6H_PLEN(ip6hdr) + IP6_HLEN, p->tot_len));
+ }
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.lenerr);
+ IP6_STATS_INC(ip6.drop);
+ return ERR_OK;
+ }
+
+ /* Trim pbuf. This should have been done at the netif layer,
+ * but we'll do it anyway just to be sure that its done. */
+ pbuf_realloc(p, IP6_HLEN + IP6H_PLEN(ip6hdr));
+
+ /* copy IP addresses to aligned ip6_addr_t */
+ ip6_addr_copy(ip_data.current_iphdr_dest.ip6, ip6hdr->dest);
+ ip6_addr_copy(ip_data.current_iphdr_src.ip6, ip6hdr->src);
+
+ /* current header pointer. */
+ ip_data.current_ip6_header = ip6hdr;
+
+ /* In netif, used in case we need to send ICMPv6 packets back. */
+ ip_data.current_netif = inp;
+
+ /* match packet against an interface, i.e. is this packet for us? */
+ if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
+ /* Always joined to multicast if-local and link-local all-nodes group. */
+ if (ip6_addr_isallnodes_iflocal(ip6_current_dest_addr()) ||
+ ip6_addr_isallnodes_linklocal(ip6_current_dest_addr())) {
+ netif = inp;
+ }
+#if LWIP_IPV6_FORWARD
+ /* Always joined to multicast link-local all-routers group. */
+ /* XXX: Need a NETIF_FLAG_??? to indicate if this is an
+ * advertising interface. */
+ else if (ip6_addr_isallrouters_linklocal(ip6_current_dest_addr())) {
+ netif = inp;
+ }
+#endif
+#if LWIP_IPV6_MLD
+ else if (mld6_lookfor_group(inp, ip6_current_dest_addr())) {
+ netif = inp;
+ }
+#else /* LWIP_IPV6_MLD */
+ else if (ip6_addr_issolicitednode(ip6_current_dest_addr())) {
+ /* Filter solicited node packets when MLD is not enabled
+ * (for Neighbor discovery). */
+ netif = NULL;
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) &&
+ ip6_addr_cmp_solicitednode(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) {
+ netif = inp;
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: solicited node packet accepted on interface %c%c\n",
+ netif->name[0], netif->name[1]));
+ break;
+ }
+ }
+ }
+#endif /* LWIP_IPV6_MLD */
+ else {
+ netif = NULL;
+ }
+ }
+ else {
+ /* start trying with inp. if that's not acceptable, start walking the
+ list of configured netifs.
+ 'first' is used as a boolean to mark whether we started walking the list */
+ int first = 1;
+ netif = inp;
+ do {
+ /* interface is up? */
+ if (netif_is_up(netif)) {
+ /* unicast to this interface address? address configured? */
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(netif, i))) {
+ /* exit outer loop */
+ goto netif_found;
+ }
+ }
+ }
+ if (ip6_addr_islinklocal(ip6_current_dest_addr())) {
+ /* Do not match link-local addresses to other netifs. */
+ netif = NULL;
+ break;
+ }
+ if (first) {
+ first = 0;
+ netif = netif_list;
+ } else {
+ netif = netif->next;
+ }
+ if (netif == inp) {
+ netif = netif->next;
+ }
+ } while(netif != NULL);
+netif_found:
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet accepted on interface %c%c\n",
+ netif ? netif->name[0] : 'X', netif? netif->name[1] : 'X'));
+ }
+
+ /* "::" packet source address? (used in duplicate address detection) */
+ if (ip6_addr_isany(ip6_current_src_addr()) &&
+ (!ip6_addr_issolicitednode(ip6_current_dest_addr()))) {
+ /* packet source is not valid */
+ /* free (drop) packet pbufs */
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with src ANY_ADDRESS dropped\n"));
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.drop);
+ goto ip6_input_cleanup;
+ }
+
+ /* packet not for us? */
+ if (netif == NULL) {
+ /* packet not for us, route or discard */
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_TRACE, ("ip6_input: packet not for us.\n"));
+#if LWIP_IPV6_FORWARD
+ /* non-multicast packet? */
+ if (!ip6_addr_ismulticast(ip6_current_dest_addr())) {
+ /* try to forward IP packet on (other) interfaces */
+#if LWIP_CONNECTION_PROXY
+ int forwarded =
+#endif
+ ip6_forward(p, ip6hdr, inp);
+#if LWIP_CONNECTION_PROXY
+ if (forwarded == 0) {
+ proxy = 1;
+ netif = inp;
+ }
+#endif
+ }
+#endif /* LWIP_IPV6_FORWARD */
+#if LWIP_CONNECTION_PROXY
+ if (!proxy)
+#endif
+ {
+ pbuf_free(p);
+ goto ip6_input_cleanup;
+ }
+ }
+
+ /* current netif pointer. */
+ ip_data.current_netif = netif;
+
+ /* Save next header type. */
+ nexth = IP6H_NEXTH(ip6hdr);
+
+ /* Init header length. */
+ hlen = ip_data.current_ip_header_tot_len = IP6_HLEN;
+
+ /* Move to payload. */
+ pbuf_header(p, -IP6_HLEN);
+
+ /* Process known option extension headers, if present. */
+ while (nexth != IP6_NEXTH_NONE)
+ {
+ switch (nexth) {
+ case IP6_NEXTH_HOPBYHOP:
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Hop-by-Hop options header\n"));
+ /* Get next header type. */
+ nexth = *((u8_t *)p->payload);
+
+ /* Get the header length. */
+ hlen = 8 * (1 + *((u8_t *)p->payload + 1));
+ ip_data.current_ip_header_tot_len += hlen;
+
+ /* Skip over this header. */
+ if (hlen > p->len) {
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
+ hlen, p->len));
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.lenerr);
+ IP6_STATS_INC(ip6.drop);
+ goto ip6_input_cleanup;
+ }
+
+ pbuf_header(p, -hlen);
+ break;
+ case IP6_NEXTH_DESTOPTS:
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Destination options header\n"));
+ /* Get next header type. */
+ nexth = *((u8_t *)p->payload);
+
+ /* Get the header length. */
+ hlen = 8 * (1 + *((u8_t *)p->payload + 1));
+ ip_data.current_ip_header_tot_len += hlen;
+
+ /* Skip over this header. */
+ if (hlen > p->len) {
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
+ hlen, p->len));
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.lenerr);
+ IP6_STATS_INC(ip6.drop);
+ goto ip6_input_cleanup;
+ }
+
+ pbuf_header(p, -hlen);
+ break;
+ case IP6_NEXTH_ROUTING:
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Routing header\n"));
+ /* Get next header type. */
+ nexth = *((u8_t *)p->payload);
+
+ /* Get the header length. */
+ hlen = 8 * (1 + *((u8_t *)p->payload + 1));
+ ip_data.current_ip_header_tot_len += hlen;
+
+ /* Skip over this header. */
+ if (hlen > p->len) {
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
+ hlen, p->len));
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.lenerr);
+ IP6_STATS_INC(ip6.drop);
+ goto ip6_input_cleanup;
+ }
+
+ pbuf_header(p, -hlen);
+ break;
+
+ case IP6_NEXTH_FRAGMENT:
+ {
+ struct ip6_frag_hdr * frag_hdr;
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header\n"));
+
+ frag_hdr = (struct ip6_frag_hdr *)p->payload;
+
+ /* Get next header type. */
+ nexth = frag_hdr->_nexth;
+
+ /* Fragment Header length. */
+ hlen = 8;
+ ip_data.current_ip_header_tot_len += hlen;
+
+ /* Make sure this header fits in current pbuf. */
+ if (hlen > p->len) {
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("IPv6 options header (hlen %"U16_F") does not fit in first pbuf (len %"U16_F"), IPv6 packet dropped.\n",
+ hlen, p->len));
+ /* free (drop) packet pbufs */
+ pbuf_free(p);
+ IP6_FRAG_STATS_INC(ip6_frag.lenerr);
+ IP6_FRAG_STATS_INC(ip6_frag.drop);
+ goto ip6_input_cleanup;
+ }
+
+ /* Offset == 0 and more_fragments == 0? */
+ if ((frag_hdr->_fragment_offset
+ & PP_HTONS(IP6_FRAG_OFFSET_MASK | IP6_FRAG_MORE_FLAG)) == 0)
+ {
+ /* This is a 1-fragment packet, usually a packet that we have
+ * already reassembled. Skip this header anc continue. */
+ pbuf_header(p, -hlen);
+ }
+ else {
+#if LWIP_IPV6_REASS
+
+ /* reassemble the packet */
+ p = ip6_reass(p);
+ /* packet not fully reassembled yet? */
+ if (p == NULL) {
+ goto ip6_input_cleanup;
+ }
+
+ /* Returned p point to IPv6 header.
+ * Update all our variables and pointers and continue. */
+ ip6hdr = (struct ip6_hdr *)p->payload;
+ nexth = IP6H_NEXTH(ip6hdr);
+ hlen = ip_data.current_ip_header_tot_len = IP6_HLEN;
+ pbuf_header(p, -IP6_HLEN);
+
+#else /* LWIP_IPV6_REASS */
+ /* free (drop) packet pbufs */
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: packet with Fragment header dropped (with LWIP_IPV6_REASS==0)\n"));
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.opterr);
+ IP6_STATS_INC(ip6.drop);
+ goto ip6_input_cleanup;
+#endif /* LWIP_IPV6_REASS */
+ }
+ break;
+ }
+ default:
+ goto options_done;
+ break;
+ }
+ }
+options_done:
+
+ /* p points to IPv6 header again. */
+ pbuf_header(p, ip_data.current_ip_header_tot_len);
+
+ /* send to upper layers */
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: \n"));
+ ip6_debug_print(p);
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_input: p->len %"U16_F" p->tot_len %"U16_F"\n", p->len, p->tot_len));
+
+#if LWIP_CONNECTION_PROXY
+ if (proxy) {
+ LWIP_DEBUGF(IP_DEBUG, ("ip6_input: proxying\n"));
+
+ switch (nexth) {
+
+#if LWIP_UDP
+ case IP6_NEXTH_UDP:
+#if LWIP_UDPLITE
+ case IP6_NEXTH_UDPLITE:
+#endif /* LWIP_UDPLITE */
+ /* Point to payload. */
+ pbuf_header(p, -ip_data.current_ip_header_tot_len);
+ udp_proxy_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_proxy_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_proxy_input(p, inp);
+ break;
+#endif /* LWIP_ICMP */
+
+ default:
+ /* no proxy support for this protocol */
+ /* XXX: TODO: icmp administratively prohibited? */
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.drop);
+ }
+ } else
+#endif /* LWIP_CONNECTION_PROXY */
+#if LWIP_RAW
+ /* raw input did not eat the packet? */
+ if (raw_input(p, inp) == 0)
+#endif /* LWIP_RAW */
+ {
+ switch (nexth) {
+ case IP6_NEXTH_NONE:
+ pbuf_free(p);
+ break;
+#if LWIP_UDP
+ case IP6_NEXTH_UDP:
+#if LWIP_UDPLITE
+ case IP6_NEXTH_UDPLITE:
+#endif /* LWIP_UDPLITE */
+ /* Point to payload. */
+ pbuf_header(p, -ip_data.current_ip_header_tot_len);
+ udp_input(p, inp);
+ break;
+#endif /* LWIP_UDP */
+#if LWIP_TCP
+ case IP6_NEXTH_TCP:
+ /* Point to payload. */
+ pbuf_header(p, -ip_data.current_ip_header_tot_len);
+ tcp_input(p, inp);
+ break;
+#endif /* LWIP_TCP */
+#if LWIP_ICMP6
+ case IP6_NEXTH_ICMP6:
+ /* Point to payload. */
+ pbuf_header(p, -ip_data.current_ip_header_tot_len);
+ icmp6_input(p, inp);
+ break;
+#endif /* LWIP_ICMP */
+ default:
+#if LWIP_ICMP6
+ /* send ICMP parameter problem unless it was a multicast or ICMPv6 */
+ if ((!ip6_addr_ismulticast(ip6_current_dest_addr())) &&
+ (IP6H_NEXTH(ip6hdr) != IP6_NEXTH_ICMP6)) {
+ icmp6_param_problem(p, ICMP6_PP_HEADER, ip_data.current_ip_header_tot_len - hlen);
+ }
+#endif /* LWIP_ICMP */
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_input: Unsupported transport protocol %"U16_F"\n", IP6H_NEXTH(ip6hdr)));
+ pbuf_free(p);
+ IP6_STATS_INC(ip6.proterr);
+ IP6_STATS_INC(ip6.drop);
+ break;
+ }
+ }
+
+ip6_input_cleanup:
+ ip_data.current_netif = NULL;
+ ip_data.current_ip6_header = NULL;
+ ip_data.current_ip_header_tot_len = 0;
+ ip6_addr_set_any(&ip_data.current_iphdr_src.ip6);
+ ip6_addr_set_any(&ip_data.current_iphdr_dest.ip6);
+
+ return ERR_OK;
+}
+
+
+/**
+ * Sends an IPv6 packet on a network interface. This function constructs
+ * the IPv6 header. If the source IPv6 address is NULL, the IPv6 "ANY" address is
+ * used as source (usually during network startup). If the source IPv6 address it
+ * IP6_ADDR_ANY, the most appropriate IPv6 address of the outgoing network
+ * interface is filled in as source address. If the destination IPv6 address is
+ * IP_HDRINCL, p is assumed to already include an IPv6 header and p->payload points
+ * to it instead of the data.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an
+ IPv6 header and p->payload points to that IPv6 header)
+ * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an
+ * IP address of the netif is selected and used as source address.
+ * if src == NULL, IP6_ADDR_ANY is used as source)
+ * @param dest the destination IPv6 address to send the packet to
+ * @param hl the Hop Limit value to be set in the IPv6 header
+ * @param tc the Traffic Class value to be set in the IPv6 header
+ * @param nexth the Next Header to be set in the IPv6 header
+ * @param netif the netif on which to send this packet
+ * @return ERR_OK if the packet was sent OK
+ * ERR_BUF if p doesn't have enough space for IPv6/LINK headers
+ * returns errors returned by netif->output
+ */
+err_t
+ip6_output_if(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest,
+ u8_t hl, u8_t tc,
+ u8_t nexth, struct netif *netif)
+{
+ struct ip6_hdr *ip6hdr;
+ ip6_addr_t dest_addr;
+
+ /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
+ gets altered as the packet is passed down the stack */
+ LWIP_ASSERT("p->ref == 1", p->ref == 1);
+
+ /* Should the IPv6 header be generated or is it already included in p? */
+ if (dest != IP_HDRINCL) {
+ /* generate IPv6 header */
+ if (pbuf_header(p, IP6_HLEN)) {
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: not enough room for IPv6 header in pbuf\n"));
+ IP6_STATS_INC(ip6.err);
+ return ERR_BUF;
+ }
+
+ ip6hdr = (struct ip6_hdr *)p->payload;
+ LWIP_ASSERT("check that first pbuf can hold struct ip6_hdr",
+ (p->len >= sizeof(struct ip6_hdr)));
+
+ IP6H_HOPLIM_SET(ip6hdr, hl);
+ IP6H_NEXTH_SET(ip6hdr, nexth);
+
+ /* dest cannot be NULL here */
+ ip6_addr_copy(ip6hdr->dest, *dest);
+
+ IP6H_VTCFL_SET(ip6hdr, 6, tc, 0);
+ IP6H_PLEN_SET(ip6hdr, p->tot_len - IP6_HLEN);
+
+ if (src == NULL) {
+ src = IP6_ADDR_ANY;
+ }
+ else if (ip6_addr_isany(src)) {
+ src = ip6_select_source_address(netif, dest);
+ if ((src == NULL) || ip6_addr_isany(src)) {
+ /* No appropriate source address was found for this packet. */
+ LWIP_DEBUGF(IP6_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("ip6_output: No suitable source address for packet.\n"));
+ IP6_STATS_INC(ip6.rterr);
+ return ERR_RTE;
+ }
+ }
+ /* src cannot be NULL here */
+ ip6_addr_copy(ip6hdr->src, *src);
+
+ } else {
+ /* IP header already included in p */
+ ip6hdr = (struct ip6_hdr *)p->payload;
+ ip6_addr_copy(dest_addr, ip6hdr->dest);
+ dest = &dest_addr;
+ }
+
+ IP6_STATS_INC(ip6.xmit);
+
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_output_if: %c%c%"U16_F"\n", netif->name[0], netif->name[1], netif->num));
+ ip6_debug_print(p);
+
+#if ENABLE_LOOPBACK
+ {
+ int i;
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_cmp(dest, netif_ip6_addr(netif, i))) {
+ /* Packet to self, enqueue it for loopback */
+ LWIP_DEBUGF(IP6_DEBUG, ("netif_loop_output()\n"));
+ return netif_loop_output(netif, p);
+ }
+ }
+ }
+#endif /* ENABLE_LOOPBACK */
+#if LWIP_IPV6_FRAG
+ /* don't fragment if interface has mtu set to 0 [loopif] */
+ if (netif->mtu && (p->tot_len > nd6_get_destination_mtu(dest, netif))) {
+ return ip6_frag(p, netif, dest);
+ }
+#endif /* LWIP_IPV6_FRAG */
+
+ LWIP_DEBUGF(IP6_DEBUG, ("netif->output_ip6()\n"));
+ return netif->output_ip6(netif, p, dest);
+}
+
+/**
+ * Simple interface to ip6_output_if. It finds the outgoing network
+ * interface and calls upon ip6_output_if to do the actual work.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an
+ IPv6 header and p->payload points to that IPv6 header)
+ * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an
+ * IP address of the netif is selected and used as source address.
+ * if src == NULL, IP6_ADDR_ANY is used as source)
+ * @param dest the destination IPv6 address to send the packet to
+ * @param hl the Hop Limit value to be set in the IPv6 header
+ * @param tc the Traffic Class value to be set in the IPv6 header
+ * @param nexth the Next Header to be set in the IPv6 header
+ *
+ * @return ERR_RTE if no route is found
+ * see ip_output_if() for more return values
+ */
+err_t
+ip6_output(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest,
+ u8_t hl, u8_t tc, u8_t nexth)
+{
+ struct netif *netif;
+ struct ip6_hdr *ip6hdr;
+ ip6_addr_t src_addr, dest_addr;
+
+ /* pbufs passed to IPv6 must have a ref-count of 1 as their payload pointer
+ gets altered as the packet is passed down the stack */
+ LWIP_ASSERT("p->ref == 1", p->ref == 1);
+
+ if (dest != IP_HDRINCL) {
+ netif = ip6_route(src, dest);
+ } else {
+ /* IP header included in p, read addresses. */
+ ip6hdr = (struct ip6_hdr *)p->payload;
+ ip6_addr_copy(src_addr, ip6hdr->src);
+ ip6_addr_copy(dest_addr, ip6hdr->dest);
+ netif = ip6_route(&src_addr, &dest_addr);
+ }
+
+ if (netif == NULL) {
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n",
+ IP6_ADDR_BLOCK1(dest),
+ IP6_ADDR_BLOCK2(dest),
+ IP6_ADDR_BLOCK3(dest),
+ IP6_ADDR_BLOCK4(dest),
+ IP6_ADDR_BLOCK5(dest),
+ IP6_ADDR_BLOCK6(dest),
+ IP6_ADDR_BLOCK7(dest),
+ IP6_ADDR_BLOCK8(dest)));
+ IP6_STATS_INC(ip6.rterr);
+ return ERR_RTE;
+ }
+
+ return ip6_output_if(p, src, dest, hl, tc, nexth, netif);
+}
+
+
+#if LWIP_NETIF_HWADDRHINT
+/** Like ip6_output, but takes and addr_hint pointer that is passed on to netif->addr_hint
+ * before calling ip6_output_if.
+ *
+ * @param p the packet to send (p->payload points to the data, e.g. next
+ protocol header; if dest == IP_HDRINCL, p already includes an
+ IPv6 header and p->payload points to that IPv6 header)
+ * @param src the source IPv6 address to send from (if src == IP6_ADDR_ANY, an
+ * IP address of the netif is selected and used as source address.
+ * if src == NULL, IP6_ADDR_ANY is used as source)
+ * @param dest the destination IPv6 address to send the packet to
+ * @param hl the Hop Limit value to be set in the IPv6 header
+ * @param tc the Traffic Class value to be set in the IPv6 header
+ * @param nexth the Next Header to be set in the IPv6 header
+ * @param addr_hint address hint pointer set to netif->addr_hint before
+ * calling ip_output_if()
+ *
+ * @return ERR_RTE if no route is found
+ * see ip_output_if() for more return values
+ */
+err_t
+ip6_output_hinted(struct pbuf *p, ip6_addr_t *src, ip6_addr_t *dest,
+ u8_t hl, u8_t tc, u8_t nexth, u8_t *addr_hint)
+{
+ struct netif *netif;
+ struct ip6_hdr *ip6hdr;
+ ip6_addr_t src_addr, dest_addr;
+ err_t err;
+
+ /* pbufs passed to IP must have a ref-count of 1 as their payload pointer
+ gets altered as the packet is passed down the stack */
+ LWIP_ASSERT("p->ref == 1", p->ref == 1);
+
+ if (dest != IP_HDRINCL) {
+ netif = ip6_route(src, dest);
+ } else {
+ /* IP header included in p, read addresses. */
+ ip6hdr = (struct ip6_hdr *)p->payload;
+ ip6_addr_copy(src_addr, ip6hdr->src);
+ ip6_addr_copy(dest_addr, ip6hdr->dest);
+ netif = ip6_route(&src_addr, &dest_addr);
+ }
+
+ if (netif == NULL) {
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_output: no route for %"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F":%"X16_F"\n",
+ IP6_ADDR_BLOCK1(dest),
+ IP6_ADDR_BLOCK2(dest),
+ IP6_ADDR_BLOCK3(dest),
+ IP6_ADDR_BLOCK4(dest),
+ IP6_ADDR_BLOCK5(dest),
+ IP6_ADDR_BLOCK6(dest),
+ IP6_ADDR_BLOCK7(dest),
+ IP6_ADDR_BLOCK8(dest)));
+ IP6_STATS_INC(ip6.rterr);
+ return ERR_RTE;
+ }
+
+ NETIF_SET_HWADDRHINT(netif, addr_hint);
+ err = ip6_output_if(p, src, dest, hl, tc, nexth, netif);
+ NETIF_SET_HWADDRHINT(netif, NULL);
+
+ return err;
+}
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+#if LWIP_IPV6_MLD
+/**
+ * Add a hop-by-hop options header with a router alert option and padding.
+ *
+ * Used by MLD when sending a Multicast listener report/done message.
+ *
+ * @param p the packet to which we will prepend the options header
+ * @param nexth the next header protocol number (e.g. IP6_NEXTH_ICMP6)
+ * @param value the value of the router alert option data (e.g. IP6_ROUTER_ALERT_VALUE_MLD)
+ * @return ERR_OK if hop-by-hop header was added, ERR_* otherwise
+ */
+err_t
+ip6_options_add_hbh_ra(struct pbuf * p, u8_t nexth, u8_t value)
+{
+ struct ip6_hbh_hdr * hbh_hdr;
+
+ /* Move pointer to make room for hop-by-hop options header. */
+ if (pbuf_header(p, sizeof(struct ip6_hbh_hdr))) {
+ LWIP_DEBUGF(IP6_DEBUG, ("ip6_options: no space for options header\n"));
+ IP6_STATS_INC(ip6.err);
+ return ERR_BUF;
+ }
+
+ hbh_hdr = (struct ip6_hbh_hdr *)p->payload;
+
+ /* Set fields. */
+ hbh_hdr->_nexth = nexth;
+ hbh_hdr->_hlen = 0;
+ hbh_hdr->_ra_opt_type = IP6_ROUTER_ALERT_OPTION;
+ hbh_hdr->_ra_opt_dlen = 2;
+ hbh_hdr->_ra_opt_data = value;
+ hbh_hdr->_padn_opt_type = IP6_PADN_ALERT_OPTION;
+ hbh_hdr->_padn_opt_dlen = 0;
+
+ return ERR_OK;
+}
+#endif /* LWIP_IPV6_MLD */
+
+#if IP6_DEBUG
+/* Print an IPv6 header by using LWIP_DEBUGF
+ * @param p an IPv6 packet, p->payload pointing to the IPv6 header
+ */
+void
+ip6_debug_print(struct pbuf *p)
+{
+ struct ip6_hdr *ip6hdr = (struct ip6_hdr *)p->payload;
+
+ LWIP_UNUSED_ARG(ip6hdr);
+
+ LWIP_DEBUGF(IP6_DEBUG, ("IPv6 header:\n"));
+ LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP6_DEBUG, ("| %2"U16_F" | %3"U16_F" | %7"U32_F" | (ver, class, flow)\n",
+ IP6H_V(ip6hdr),
+ IP6H_TC(ip6hdr),
+ IP6H_FL(ip6hdr)));
+ LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP6_DEBUG, ("| %5"U16_F" | %3"U16_F" | %3"U16_F" | (plen, nexth, hopl)\n",
+ IP6H_PLEN(ip6hdr),
+ IP6H_NEXTH(ip6hdr),
+ IP6H_HOPLIM(ip6hdr)));
+ LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (src)\n",
+ IP6_ADDR_BLOCK1(&(ip6hdr->src)),
+ IP6_ADDR_BLOCK2(&(ip6hdr->src)),
+ IP6_ADDR_BLOCK3(&(ip6hdr->src)),
+ IP6_ADDR_BLOCK4(&(ip6hdr->src))));
+ LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n",
+ IP6_ADDR_BLOCK5(&(ip6hdr->src)),
+ IP6_ADDR_BLOCK6(&(ip6hdr->src)),
+ IP6_ADDR_BLOCK7(&(ip6hdr->src)),
+ IP6_ADDR_BLOCK8(&(ip6hdr->src))));
+ LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" | (dest)\n",
+ IP6_ADDR_BLOCK1(&(ip6hdr->dest)),
+ IP6_ADDR_BLOCK2(&(ip6hdr->dest)),
+ IP6_ADDR_BLOCK3(&(ip6hdr->dest)),
+ IP6_ADDR_BLOCK4(&(ip6hdr->dest))));
+ LWIP_DEBUGF(IP6_DEBUG, ("| %4"X32_F" | %4"X32_F" | %4"X32_F" | %4"X32_F" |\n",
+ IP6_ADDR_BLOCK5(&(ip6hdr->dest)),
+ IP6_ADDR_BLOCK6(&(ip6hdr->dest)),
+ IP6_ADDR_BLOCK7(&(ip6hdr->dest)),
+ IP6_ADDR_BLOCK8(&(ip6hdr->dest))));
+ LWIP_DEBUGF(IP6_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* IP6_DEBUG */
+
+#endif /* LWIP_IPV6 */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv6/ip6_addr.c b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/ip6_addr.c
new file mode 100644
index 00000000..65d27980
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/ip6_addr.c
@@ -0,0 +1,251 @@
+/**
+ * @file
+ *
+ * IPv6 addresses.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ * Functions for handling IPv6 addresses.
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/ip6_addr.h"
+#include "lwip/def.h"
+
+/* used by IP6_ADDR_ANY in ip6_addr.h */
+const ip6_addr_t ip6_addr_any = { { 0ul, 0ul, 0ul, 0ul } };
+
+#ifndef isprint
+#define in_range(c, lo, up) ((u8_t)c >= lo && (u8_t)c <= up)
+#define isprint(c) in_range(c, 0x20, 0x7f)
+#define isdigit(c) in_range(c, '0', '9')
+#define isxdigit(c) (isdigit(c) || in_range(c, 'a', 'f') || in_range(c, 'A', 'F'))
+#define islower(c) in_range(c, 'a', 'z')
+#define isspace(c) (c == ' ' || c == '\f' || c == '\n' || c == '\r' || c == '\t' || c == '\v')
+#define xchar(i) ((i) < 10 ? '0' + (i) : 'A' + (i) - 10)
+#endif
+
+/**
+ * Check whether "cp" is a valid ascii representation
+ * of an IPv6 address and convert to a binary address.
+ * Returns 1 if the address is valid, 0 if not.
+ *
+ * @param cp IPv6 address in ascii represenation (e.g. "FF01::1")
+ * @param addr pointer to which to save the ip address in network order
+ * @return 1 if cp could be converted to addr, 0 on failure
+ */
+int
+ip6addr_aton(const char *cp, ip6_addr_t *addr)
+{
+ u32_t addr_index, zero_blocks, current_block_index, current_block_value;
+ const char * s;
+
+ /* Count the number of colons, to count the number of blocks in a "::" sequence
+ zero_blocks may be 1 even if there are no :: sequences */
+ zero_blocks = 8;
+ for (s = cp; *s != 0; s++) {
+ if (*s == ':')
+ zero_blocks--;
+ else if (!isxdigit(*s))
+ break;
+ }
+
+ /* parse each block */
+ addr_index = 0;
+ current_block_index = 0;
+ current_block_value = 0;
+ for (s = cp; *s != 0; s++) {
+ if (*s == ':') {
+ if (addr) {
+ if (current_block_index & 0x1) {
+ addr->addr[addr_index++] |= current_block_value;
+ }
+ else {
+ addr->addr[addr_index] = current_block_value << 16;
+ }
+ }
+ current_block_index++;
+ current_block_value = 0;
+ if (current_block_index > 7) {
+ /* address too long! */
+ return 0;
+ } if (s[1] == ':') {
+ s++;
+ /* "::" found, set zeros */
+ while (zero_blocks-- > 0) {
+ if (current_block_index & 0x1) {
+ addr_index++;
+ }
+ else {
+ if (addr) {
+ addr->addr[addr_index] = 0;
+ }
+ }
+ current_block_index++;
+ }
+ }
+ } else if (isxdigit(*s)) {
+ /* add current digit */
+ current_block_value = (current_block_value << 4) +
+ (isdigit(*s) ? *s - '0' :
+ 10 + (islower(*s) ? *s - 'a' : *s - 'A'));
+ } else {
+ /* unexpected digit, space? CRLF? */
+ break;
+ }
+ }
+
+ if (addr) {
+ if (current_block_index & 0x1) {
+ addr->addr[addr_index++] |= current_block_value;
+ }
+ else {
+ addr->addr[addr_index] = current_block_value << 16;
+ }
+ }
+
+ /* convert to network byte order. */
+ if (addr) {
+ for (addr_index = 0; addr_index < 4; addr_index++) {
+ addr->addr[addr_index] = htonl(addr->addr[addr_index]);
+ }
+ }
+
+ if (current_block_index != 7) {
+ return 0;
+ }
+
+ return 1;
+}
+
+/**
+ * Convert numeric IPv6 address into ASCII representation.
+ * returns ptr to static buffer; not reentrant!
+ *
+ * @param addr ip6 address in network order to convert
+ * @return pointer to a global static (!) buffer that holds the ASCII
+ * represenation of addr
+ */
+char *
+ip6addr_ntoa(const ip6_addr_t *addr)
+{
+ static char str[40];
+ return ip6addr_ntoa_r(addr, str, 40);
+}
+
+/**
+ * Same as ipaddr_ntoa, but reentrant since a user-supplied buffer is used.
+ *
+ * @param addr ip6 address in network order to convert
+ * @param buf target buffer where the string is stored
+ * @param buflen length of buf
+ * @return either pointer to buf which now holds the ASCII
+ * representation of addr or NULL if buf was too small
+ */
+char *
+ip6addr_ntoa_r(const ip6_addr_t *addr, char *buf, int buflen)
+{
+ u32_t current_block_index, current_block_value;
+ s32_t zero_flag, i;
+
+ i = 0;
+ zero_flag = 0; /* used to indicate a zero chain for "::' */
+
+ for (current_block_index = 0; current_block_index < 8; current_block_index++) {
+ /* get the current 16-bit block */
+ current_block_value = htonl(addr->addr[current_block_index >> 1]);
+ if ((current_block_index & 0x1) == 0) {
+ current_block_value = current_block_value >> 16;
+ }
+ current_block_value &= 0xffff;
+
+ if (current_block_value == 0) {
+ /* generate empty block "::" */
+ if (!zero_flag) {
+ if (current_block_index > 0) {
+ zero_flag = 1;
+ buf[i++] = ':';
+ if (i >= buflen) return NULL;
+ }
+ }
+ }
+ else {
+ if (current_block_index > 0) {
+ buf[i++] = ':';
+ if (i >= buflen) return NULL;
+ }
+
+ if ((current_block_value & 0xf000) == 0) {
+ zero_flag = 1;
+ }
+ else {
+ buf[i++] = xchar(((current_block_value & 0xf000) >> 12));
+ zero_flag = 0;
+ if (i >= buflen) return NULL;
+ }
+
+ if (((current_block_value & 0xf00) == 0) && (zero_flag)) {
+ /* do nothing */
+ }
+ else {
+ buf[i++] = xchar(((current_block_value & 0xf00) >> 8));
+ zero_flag = 0;
+ if (i >= buflen) return NULL;
+ }
+
+ if (((current_block_value & 0xf0) == 0) && (zero_flag)) {
+ /* do nothing */
+ }
+ else {
+ buf[i++] = xchar(((current_block_value & 0xf0) >> 4));
+ zero_flag = 0;
+ if (i >= buflen) return NULL;
+ }
+
+ buf[i++] = xchar((current_block_value & 0xf));
+ if (i >= buflen) return NULL;
+
+ zero_flag = 0;
+ }
+ }
+
+ buf[i] = 0;
+
+ return buf;
+}
+#endif /* LWIP_IPV6 */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv6/ip6_frag.c b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/ip6_frag.c
new file mode 100644
index 00000000..b3cba0e7
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/ip6_frag.c
@@ -0,0 +1,666 @@
+/**
+ * @file
+ *
+ * IPv6 fragmentation and reassembly.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+#include "lwip/ip6_frag.h"
+#include "lwip/ip6.h"
+#include "lwip/icmp6.h"
+#include "lwip/nd6.h"
+#include "lwip/ip.h"
+
+#include "lwip/pbuf.h"
+#include "lwip/memp.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+#if LWIP_IPV6 && LWIP_IPV6_REASS /* don't build if not configured for use in lwipopts.h */
+
+
+/** Setting this to 0, you can turn off checking the fragments for overlapping
+ * regions. The code gets a little smaller. Only use this if you know that
+ * overlapping won't occur on your network! */
+#ifndef IP_REASS_CHECK_OVERLAP
+#define IP_REASS_CHECK_OVERLAP 1
+#endif /* IP_REASS_CHECK_OVERLAP */
+
+/** Set to 0 to prevent freeing the oldest datagram when the reassembly buffer is
+ * full (IP_REASS_MAX_PBUFS pbufs are enqueued). The code gets a little smaller.
+ * Datagrams will be freed by timeout only. Especially useful when MEMP_NUM_REASSDATA
+ * is set to 1, so one datagram can be reassembled at a time, only. */
+#ifndef IP_REASS_FREE_OLDEST
+#define IP_REASS_FREE_OLDEST 1
+#endif /* IP_REASS_FREE_OLDEST */
+
+#define IP_REASS_FLAG_LASTFRAG 0x01
+
+/** This is a helper struct which holds the starting
+ * offset and the ending offset of this fragment to
+ * easily chain the fragments.
+ */
+struct ip6_reass_helper {
+ struct ip6_reass_helper *next;
+ struct pbuf *p;
+ u16_t start;
+ u16_t end;
+};
+
+/* static variables */
+static struct ip6_reassdata *reassdatagrams;
+static u16_t ip6_reass_pbufcount;
+
+/* Forward declarations. */
+static void ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr);
+#if IP_REASS_FREE_OLDEST
+static void ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed);
+#endif /* IP_REASS_FREE_OLDEST */
+
+void
+ip6_reass_tmr(void)
+{
+ struct ip6_reassdata *r, *tmp;
+
+ r = reassdatagrams;
+ while (r != NULL) {
+ /* Decrement the timer. Once it reaches 0,
+ * clean up the incomplete fragment assembly */
+ if (r->timer > 0) {
+ r->timer--;
+ r = r->next;
+ } else {
+ /* reassembly timed out */
+ tmp = r;
+ /* get the next pointer before freeing */
+ r = r->next;
+ /* free the helper struct and all enqueued pbufs */
+ ip6_reass_free_complete_datagram(tmp);
+ }
+ }
+}
+
+/**
+ * Free a datagram (struct ip6_reassdata) and all its pbufs.
+ * Updates the total count of enqueued pbufs (ip6_reass_pbufcount),
+ * sends an ICMP time exceeded packet.
+ *
+ * @param ipr datagram to free
+ */
+static void
+ip6_reass_free_complete_datagram(struct ip6_reassdata *ipr)
+{
+ struct ip6_reassdata **pipr;
+ u16_t pbufs_freed = 0;
+ u8_t clen;
+ struct pbuf *p;
+ struct ip6_reass_helper *iprh;
+
+ /* First, free all received pbufs. The individual pbufs need to be released
+ separately as they have not yet been chained */
+ iprh = ipr->iprh;
+ while (iprh != NULL) {
+ struct ip6_reass_helper *next = iprh->next;
+ p = iprh->p;
+
+#if LWIP_ICMP6
+ /* If the first fragment was received, send ICMP time exceeded. */
+ if (iprh->start == 0) {
+ SMEMCPY(ipr->iphdr0, &ipr->iphdr, IP6_HLEN);
+ if (pbuf_header(p, (u8_t *)p->payload - (u8_t *)ipr->iphdr0) == 0) {
+ icmp6_time_exceeded(p, ICMP6_TE_FRAG);
+ }
+ else {
+ LWIP_ASSERT("ip6_reass_free: moving p->payload to ip6 header failed\n", 0);
+ }
+ }
+#endif /* LWIP_ICMP6 */
+
+ clen = pbuf_clen(p);
+ LWIP_ASSERT("pbufs_freed + clen <= 0xffff", pbufs_freed + clen <= 0xffff);
+ pbufs_freed += clen;
+ pbuf_free(p);
+
+ iprh = next;
+ }
+
+ /* Then, unchain the struct ip6_reassdata from the list and free it. */
+ for (pipr = &reassdatagrams; *pipr != NULL; pipr = &(*pipr)->next) {
+ if (*pipr == ipr) {
+ (*pipr) = ipr->next;
+ break;
+ }
+ }
+ memp_free(MEMP_IP6_REASSDATA, ipr);
+
+ /* Finally, update number of pbufs in reassembly queue */
+ LWIP_ASSERT("ip_reass_pbufcount >= clen", ip6_reass_pbufcount >= pbufs_freed);
+ ip6_reass_pbufcount -= pbufs_freed;
+}
+
+#if IP_REASS_FREE_OLDEST
+/**
+ * Free the oldest datagram to make room for enqueueing new fragments.
+ * The datagram ipr is not freed!
+ *
+ * @param ipr ip6_reassdata for the current fragment
+ * @param pbufs_needed number of pbufs needed to enqueue
+ * (used for freeing other datagrams if not enough space)
+ */
+static void
+ip6_reass_remove_oldest_datagram(struct ip6_reassdata *ipr, int pbufs_needed)
+{
+ struct ip6_reassdata *r, *oldest;
+
+ /* Free datagrams until being allowed to enqueue 'pbufs_needed' pbufs,
+ * but don't free the current datagram! */
+ do {
+ r = oldest = reassdatagrams;
+ while (r != NULL) {
+ if (r != ipr) {
+ if (r->timer <= oldest->timer) {
+ /* older than the previous oldest */
+ oldest = r;
+ }
+ }
+ r = r->next;
+ }
+ if (oldest != NULL) {
+ ip6_reass_free_complete_datagram(oldest);
+ }
+ } while (((ip6_reass_pbufcount + pbufs_needed) > IP_REASS_MAX_PBUFS) && (reassdatagrams != NULL));
+}
+#endif /* IP_REASS_FREE_OLDEST */
+
+/**
+ * Reassembles incoming IPv6 fragments into an IPv6 datagram.
+ *
+ * @param p points to the IPv6 Fragment Header
+ * @param len the length of the payload (after Fragment Header)
+ * @return NULL if reassembly is incomplete, pbuf pointing to
+ * IPv6 Header if reassembly is complete
+ */
+struct pbuf *
+ip6_reass(struct pbuf *p)
+{
+ struct ip6_reassdata *ipr, **pipr;
+ struct ip6_reass_helper *iprh, *iprh_tmp;
+ struct ip6_reass_helper **pnext;
+ struct ip6_frag_hdr * frag_hdr;
+ size_t unfrag_len;
+ u16_t offset, len, start, end, validlen;
+ u8_t clen;
+
+ IP6_FRAG_STATS_INC(ip6_frag.recv);
+
+ frag_hdr = (struct ip6_frag_hdr *) p->payload;
+
+ clen = pbuf_clen(p);
+
+ offset = ntohs(frag_hdr->_fragment_offset);
+
+ /* Calculate fragment length from IPv6 payload length.
+ * Adjust for headers before Fragment Header.
+ * And finally adjust by Fragment Header length. */
+ len = ntohs(ip6_current_header()->_plen);
+ len -= ((u8_t*)p->payload - (u8_t*)ip6_current_header()) - IP6_HLEN;
+ len -= IP6_FRAG_HLEN;
+
+ start = (offset & IP6_FRAG_OFFSET_MASK);
+ end = start + len;
+
+
+ /* Look for the datagram the fragment belongs to in the current datagram queue,
+ * remembering the previous in the queue for later dequeueing. */
+ for (ipr = reassdatagrams; ipr != NULL; ipr = ipr->next) {
+ /* Check if the incoming fragment matches the one currently present
+ in the reassembly buffer. If so, we proceed with copying the
+ fragment into the buffer. */
+ if ((frag_hdr->_identification == ipr->identification) &&
+ ip6_addr_cmp(ip6_current_src_addr(), &(ipr->iphdr.src)) &&
+ ip6_addr_cmp(ip6_current_dest_addr(), &(ipr->iphdr.dest))) {
+ IP6_FRAG_STATS_INC(ip6_frag.cachehit);
+ break;
+ }
+ }
+
+ if (ipr == NULL) {
+ /* Enqueue a new datagram into the datagram queue */
+ ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
+ if (ipr == NULL) {
+#if IP_REASS_FREE_OLDEST
+ /* Make room and try again. */
+ ip6_reass_remove_oldest_datagram(ipr, clen);
+ ipr = (struct ip6_reassdata *)memp_malloc(MEMP_IP6_REASSDATA);
+ if (ipr == NULL)
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ IP6_FRAG_STATS_INC(ip6_frag.memerr);
+ IP6_FRAG_STATS_INC(ip6_frag.drop);
+ goto nullreturn;
+ }
+ }
+
+ memset(ipr, 0, sizeof(struct ip6_reassdata));
+ ipr->timer = IP_REASS_MAXAGE;
+
+ /* enqueue the new structure to the front of the list */
+ ipr->next = reassdatagrams;
+ reassdatagrams = ipr;
+
+ /* Use the current IPv6 header for src/dest address reference.
+ * Eventually, we will replace it when we get the first fragment
+ * (it might be this one, in any case, it is done later). */
+ SMEMCPY(&ipr->iphdr, ip6_current_header(), IP6_HLEN);
+ if (start == 0) {
+ ipr->iphdr0 = (struct ip6_hdr *)ip6_current_header();
+ }
+
+ /* copy the fragmented packet id. */
+ ipr->identification = frag_hdr->_identification;
+ }
+
+ /* If this is the last fragment, save total packet length. */
+ if ((offset & IP6_FRAG_MORE_FLAG) == 0) {
+#if IP_REASS_CHECK_OVERLAP
+ if (ipr->datagram_len != 0) {
+ IP6_FRAG_STATS_INC(ip6_frag.proterr);
+ IP6_FRAG_STATS_INC(ip6_frag.drop);
+ goto nullreturn;
+ }
+#endif /* IP_REASS_CHECK_OVERLAP */
+ ipr->datagram_len = end;
+ }
+
+ /* find the place to insert this pbuf */
+ validlen = 0;
+ for (pnext = &ipr->iprh; *pnext != NULL; pnext = &(*pnext)->next) {
+ iprh_tmp = *pnext;
+
+ if (start < iprh_tmp->start) {
+ /* the new pbuf should be inserted before this */
+#if IP_REASS_CHECK_OVERLAP
+ 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;
+ }
+#endif /* IP_REASS_CHECK_OVERLAP */
+ break;
+ }
+ 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 (start < iprh_tmp->end) {
+ /* overlap: no need to keep the new datagram */
+ IP6_FRAG_STATS_INC(ip6_frag.proterr);
+ IP6_FRAG_STATS_INC(ip6_frag.drop);
+ goto nullreturn;
+ }
+#endif /* IP_REASS_CHECK_OVERLAP */
+ else {
+ /* Check if the fragments received so far have no gaps. */
+ if (validlen == iprh_tmp->start) {
+ validlen = iprh_tmp->end;
+ }
+ else {
+ validlen = 0;
+ }
+ }
+ }
+
+ /* Check if we are allowed to enqueue more datagrams. */
+ if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS) {
+#if IP_REASS_FREE_OLDEST
+ ip6_reass_remove_oldest_datagram(ipr, clen);
+ if ((ip6_reass_pbufcount + clen) > IP_REASS_MAX_PBUFS)
+#endif /* IP_REASS_FREE_OLDEST */
+ {
+ /* @todo: send ICMPv6 time exceeded here? */
+ /* drop this pbuf */
+ IP6_FRAG_STATS_INC(ip6_frag.memerr);
+ IP6_FRAG_STATS_INC(ip6_frag.drop);
+ goto nullreturn;
+ }
+ }
+
+ if (start == 0 && ipr->iphdr0 == NULL) {
+ /*
+ * We've got the fragment with offset 0 out of order, remember its
+ * IPv6 header location (in the hidden part of the current pbuf)
+ * and update the copy in ip6_reassdata::iphdr. We don't need to
+ * copy complete header since src and dest are the same as in the
+ * first fragment we received.
+ */
+ ipr->iphdr0 = (struct ip6_hdr *)ip6_current_header();
+ SMEMCPY(&ipr->iphdr, ip6_current_header(),
+ IP6_HLEN - 2 * sizeof(ip_addr_p_t));
+ }
+
+ /* Overwrite IPv6 Header with our own helper struct (aligned). */
+ iprh = (struct ip6_reass_helper *)
+ (((uintptr_t)(u8_t *)ip6_current_header() + sizeof(void *) - 1)
+ & ~(sizeof(void *) - 1));
+ iprh->p = p;
+ iprh->start = start;
+ iprh->end = end;
+
+ /* insert it into the list */
+ iprh->next = *pnext;
+ *pnext = iprh;
+
+ /* 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;
+
+ if (ipr->datagram_len == 0) {
+ /* We still don't have the last fragment. */
+ return NULL;
+ }
+
+ if (validlen == start) {
+ validlen = end;
+ }
+ else {
+ /* There are gaps before this fragment. */
+ return NULL;
+ }
+
+ if (validlen != 0) {
+ /*
+ * We know we have all the data up to the end of this fragment and
+ * we know the total length. Check if the reassembly is complete.
+ */
+ for (iprh_tmp = iprh->next; iprh_tmp != NULL; iprh_tmp = iprh_tmp->next) {
+ if (validlen == iprh_tmp->start) {
+ validlen = iprh_tmp->end;
+ }
+ else {
+ validlen = 0;
+ break;
+ }
+ }
+
+ if (validlen != ipr->datagram_len) {
+ /* the datagram is not yet reassembled completely */
+ return NULL;
+ }
+ }
+
+ /*
+ * All fragments have been received. Reassemble original datagram
+ * and return it to ip6_input() to be processed instead of the final
+ * fragment that completed the reassembly.
+ */
+
+ /* chain together the pbufs contained within the ip6_reassdata list. */
+ p = NULL;
+ for (iprh = ipr->iprh; iprh != NULL; iprh = iprh->next) {
+ if (p == NULL) {
+ p = iprh->p;
+ }
+ else {
+ /* hide the fragment header for every succeeding fragment */
+ pbuf_header(iprh->p, -IP6_FRAG_HLEN);
+ pbuf_cat(p, iprh->p);
+ }
+ }
+
+ /* Adjust datagram length by adding preceding header lengths. */
+ unfrag_len = (u8_t *)p->payload - (u8_t *)ipr->iphdr0;
+# ifndef VBOX
+ ipr->datagram_len += unfrag_len - IP6_HLEN + IP6_FRAG_HLEN;
+# else
+ LWIP_ASSERT("overflow", (s16_t)unfrag_len == (ssize_t)unfrag_len); /* s16_t because of pbuf_header call */
+ ipr->datagram_len += (u16_t)(unfrag_len - IP6_HLEN + IP6_FRAG_HLEN);
+# endif
+
+ /* Set payload length in ip header. */
+ ipr->iphdr._plen = htons(ipr->datagram_len);
+
+ /* restore IPv6 header (overwritten with ip6_reass_helper) */
+ SMEMCPY(ipr->iphdr0, &ipr->iphdr, IP6_HLEN);
+
+ /* Mark as "single fragment" packet (see caller). */
+ frag_hdr = (struct ip6_frag_hdr *) p->payload;
+ frag_hdr->_fragment_offset = 0;
+
+ /* Unlink from the reassdatagrams list */
+ for (pipr = &reassdatagrams; *pipr != NULL; pipr = &(*pipr)->next) {
+ if (*pipr == ipr) {
+ (*pipr) = ipr->next;
+ break;
+ }
+ }
+ memp_free(MEMP_IP6_REASSDATA, ipr);
+
+ /* adjust the number of pbufs currently queued for reassembly. */
+ ip6_reass_pbufcount -= pbuf_clen(p);
+
+ /* Move pbuf back to IPv6 header. */
+# ifndef VBOX
+ if (pbuf_header(p, unfrag_len) != 0) {
+# else
+ if (pbuf_header(p, (s16_t)unfrag_len) != 0) {
+# endif
+ LWIP_ASSERT("ip6_reass: moving p->payload to ip6 header failed\n", 0);
+ goto nullreturn;
+ }
+
+ /* Return the pbuf chain */
+ return p;
+
+nullreturn:
+ pbuf_free(p);
+ return NULL;
+}
+
+#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */
+
+#if LWIP_IPV6 && LWIP_IPV6_FRAG
+
+/** Allocate a new struct pbuf_custom_ref */
+static struct pbuf_custom_ref*
+ip6_frag_alloc_pbuf_custom_ref(void)
+{
+ return (struct pbuf_custom_ref*)memp_malloc(MEMP_FRAG_PBUF);
+}
+
+/** Free a struct pbuf_custom_ref */
+static void
+ip6_frag_free_pbuf_custom_ref(struct pbuf_custom_ref* p)
+{
+ LWIP_ASSERT("p != NULL", p != NULL);
+ memp_free(MEMP_FRAG_PBUF, p);
+}
+
+/** Free-callback function to free a 'struct pbuf_custom_ref', called by
+ * pbuf_free. */
+static void
+ip6_frag_free_pbuf_custom(struct pbuf *p)
+{
+ struct pbuf_custom_ref *pcr = (struct pbuf_custom_ref*)p;
+ LWIP_ASSERT("pcr != NULL", pcr != NULL);
+ LWIP_ASSERT("pcr == p", (void*)pcr == (void*)p);
+ if (pcr->original != NULL) {
+ pbuf_free(pcr->original);
+ }
+ ip6_frag_free_pbuf_custom_ref(pcr);
+}
+
+/**
+ * Fragment an IPv6 datagram if too large for the netif or path MTU.
+ *
+ * Chop the datagram in MTU sized chunks and send them in order
+ * by pointing PBUF_REFs into p
+ *
+ * @param p ipv6 packet to send
+ * @param netif the netif on which to send
+ * @param dest destination ipv6 address to which to send
+ *
+ * @return ERR_OK if sent successfully, err_t otherwise
+ */
+err_t
+ip6_frag(struct pbuf *p, struct netif *netif, ip6_addr_t *dest)
+{
+ struct ip6_hdr *original_ip6hdr;
+ struct ip6_hdr *ip6hdr;
+ struct ip6_frag_hdr * frag_hdr;
+ struct pbuf *rambuf;
+ struct pbuf *newpbuf;
+ static u32_t identification;
+ u16_t nfb;
+ u16_t left, cop;
+ u16_t mtu;
+ u16_t fragment_offset = 0;
+ u16_t last;
+ u16_t poff = IP6_HLEN;
+ u16_t newpbuflen = 0;
+ u16_t left_to_copy;
+
+ identification++;
+
+ original_ip6hdr = (struct ip6_hdr *)p->payload;
+
+ mtu = nd6_get_destination_mtu(dest, netif);
+
+ /* TODO we assume there are no options in the unfragmentable part (IPv6 header). */
+ left = p->tot_len - IP6_HLEN;
+
+ nfb = (mtu - (IP6_HLEN + IP6_FRAG_HLEN)) & IP6_FRAG_OFFSET_MASK;
+
+ while (left) {
+ last = (left <= nfb);
+
+ /* Fill this fragment */
+ cop = last ? left : nfb;
+
+ /* When not using a static buffer, create a chain of pbufs.
+ * The first will be a PBUF_RAM holding the link, IPv6, and Fragment header.
+ * The rest will be PBUF_REFs mirroring the pbuf chain to be fragged,
+ * but limited to the size of an mtu.
+ */
+ rambuf = pbuf_alloc(PBUF_LINK, IP6_HLEN + IP6_FRAG_HLEN, PBUF_RAM);
+ if (rambuf == NULL) {
+ IP6_FRAG_STATS_INC(ip6_frag.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("this needs a pbuf in one piece!",
+ (p->len >= (IP6_HLEN + IP6_FRAG_HLEN)));
+ SMEMCPY(rambuf->payload, original_ip6hdr, IP6_HLEN);
+ ip6hdr = (struct ip6_hdr *)rambuf->payload;
+ frag_hdr = (struct ip6_frag_hdr *)((u8_t*)rambuf->payload + IP6_HLEN);
+
+ /* Can just adjust p directly for needed offset. */
+ p->payload = (u8_t *)p->payload + poff;
+ p->len -= poff;
+ p->tot_len -= poff;
+
+ left_to_copy = cop;
+ while (left_to_copy) {
+ struct pbuf_custom_ref *pcr;
+ newpbuflen = (left_to_copy < p->len) ? left_to_copy : p->len;
+ /* Is this pbuf already empty? */
+ if (!newpbuflen) {
+ p = p->next;
+ continue;
+ }
+ pcr = ip6_frag_alloc_pbuf_custom_ref();
+ if (pcr == NULL) {
+ pbuf_free(rambuf);
+ IP6_FRAG_STATS_INC(ip6_frag.memerr);
+ return ERR_MEM;
+ }
+ /* Mirror this pbuf, although we might not need all of it. */
+ newpbuf = pbuf_alloced_custom(PBUF_RAW, newpbuflen, PBUF_REF, &pcr->pc, p->payload, newpbuflen);
+ if (newpbuf == NULL) {
+ ip6_frag_free_pbuf_custom_ref(pcr);
+ pbuf_free(rambuf);
+ IP6_FRAG_STATS_INC(ip6_frag.memerr);
+ return ERR_MEM;
+ }
+ pbuf_ref(p);
+ pcr->original = p;
+ pcr->pc.custom_free_function = ip6_frag_free_pbuf_custom;
+
+ /* Add it to end of rambuf's chain, but using pbuf_cat, not pbuf_chain
+ * so that it is removed when pbuf_dechain is later called on rambuf.
+ */
+ pbuf_cat(rambuf, newpbuf);
+ left_to_copy -= newpbuflen;
+ if (left_to_copy) {
+ p = p->next;
+ }
+ }
+ poff = newpbuflen;
+
+ /* Set headers */
+ frag_hdr->_nexth = original_ip6hdr->_nexth;
+ frag_hdr->reserved = 0;
+ frag_hdr->_fragment_offset = htons((fragment_offset & IP6_FRAG_OFFSET_MASK) | (last ? 0 : IP6_FRAG_MORE_FLAG));
+ frag_hdr->_identification = htonl(identification);
+
+ IP6H_NEXTH_SET(ip6hdr, IP6_NEXTH_FRAGMENT);
+ IP6H_PLEN_SET(ip6hdr, cop + IP6_FRAG_HLEN);
+
+ /* No need for separate header pbuf - we allowed room for it in rambuf
+ * when allocated.
+ */
+ IP6_FRAG_STATS_INC(ip6_frag.xmit);
+ netif->output_ip6(netif, rambuf, dest);
+
+ /* Unfortunately we can't reuse rambuf - the hardware may still be
+ * using the buffer. Instead we free it (and the ensuing chain) and
+ * recreate it next time round the loop. If we're lucky the hardware
+ * will have already sent the packet, the free will really free, and
+ * there will be zero memory penalty.
+ */
+
+ pbuf_free(rambuf);
+ left -= cop;
+ fragment_offset += cop;
+ }
+ return ERR_OK;
+}
+
+#endif /* LWIP_IPV6 && LWIP_IPV6_FRAG */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv6/mld6.c b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/mld6.c
new file mode 100644
index 00000000..31705f1b
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/mld6.c
@@ -0,0 +1,589 @@
+/**
+ * @file
+ *
+ * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710.
+ * No support for MLDv2.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+/* Based on igmp.c implementation of igmp v2 protocol */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_IPV6_MLD /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/mld6.h"
+#include "lwip/icmp6.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/ip.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/memp.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+
+/*
+ * MLD constants
+ */
+#define MLD6_HL 1
+#define MLD6_JOIN_DELAYING_MEMBER_TMR_MS (500)
+
+#define MLD6_GROUP_NON_MEMBER 0
+#define MLD6_GROUP_DELAYING_MEMBER 1
+#define MLD6_GROUP_IDLE_MEMBER 2
+
+
+/* The list of joined groups. */
+static struct mld_group* mld_group_list;
+
+
+/* Forward declarations. */
+static struct mld_group * mld6_new_group(struct netif *ifp, ip6_addr_t *addr);
+static err_t mld6_free_group(struct mld_group *group);
+static void mld6_delayed_report(struct mld_group *group, u16_t maxresp);
+static void mld6_send(struct mld_group *group, u8_t type);
+
+
+/**
+ * Stop MLD processing on interface
+ *
+ * @param netif network interface on which stop MLD processing
+ */
+err_t
+mld6_stop(struct netif *netif)
+{
+ struct mld_group *group = mld_group_list;
+ struct mld_group *prev = NULL;
+ struct mld_group *next;
+
+ /* look for groups joined on this interface further down the list */
+ while (group != NULL) {
+ next = group->next;
+ /* is it a group joined on this interface? */
+ if (group->netif == netif) {
+ /* is it the first group of the list? */
+ if (group == mld_group_list) {
+ mld_group_list = next;
+ }
+ /* is there a "previous" group defined? */
+ if (prev != NULL) {
+ prev->next = next;
+ }
+ /* disable the group at the MAC level */
+ if (netif->mld_mac_filter != NULL) {
+ netif->mld_mac_filter(netif, &(group->group_address), MLD6_DEL_MAC_FILTER);
+ }
+ /* free group */
+ memp_free(MEMP_MLD6_GROUP, group);
+ } else {
+ /* change the "previous" */
+ prev = group;
+ }
+ /* move to "next" */
+ group = next;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Report MLD memberships for this interface
+ *
+ * @param netif network interface on which report MLD memberships
+ */
+void
+mld6_report_groups(struct netif *netif)
+{
+ struct mld_group *group = mld_group_list;
+
+ while (group != NULL) {
+ if (group->netif == netif) {
+ mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
+ }
+ group = group->next;
+ }
+}
+
+/**
+ * Search for a group that is joined on a netif
+ *
+ * @param ifp the network interface for which to look
+ * @param addr the group ipv6 address to search for
+ * @return a struct mld_group* if the group has been found,
+ * NULL if the group wasn't found.
+ */
+struct mld_group *
+mld6_lookfor_group(struct netif *ifp, ip6_addr_t *addr)
+{
+ struct mld_group *group = mld_group_list;
+
+ while (group != NULL) {
+ if ((group->netif == ifp) && (ip6_addr_cmp(&(group->group_address), addr))) {
+ return group;
+ }
+ group = group->next;
+ }
+
+ return NULL;
+}
+
+
+/**
+ * create a new group
+ *
+ * @param ifp the network interface for which to create
+ * @param addr the new group ipv6
+ * @return a struct mld_group*,
+ * NULL on memory error.
+ */
+static struct mld_group *
+mld6_new_group(struct netif *ifp, ip6_addr_t *addr)
+{
+ struct mld_group *group;
+
+ group = (struct mld_group *)memp_malloc(MEMP_MLD6_GROUP);
+ if (group != NULL) {
+ group->netif = ifp;
+ ip6_addr_set(&(group->group_address), addr);
+ group->timer = 0; /* Not running */
+ group->group_state = MLD6_GROUP_IDLE_MEMBER;
+ group->last_reporter_flag = 0;
+ group->use = 0;
+ group->next = mld_group_list;
+
+ mld_group_list = group;
+ }
+
+ return group;
+}
+
+/**
+ * Remove a group in the mld_group_list and free
+ *
+ * @param group the group to remove
+ * @return ERR_OK if group was removed from the list, an err_t otherwise
+ */
+static err_t
+mld6_free_group(struct mld_group *group)
+{
+ err_t err = ERR_OK;
+
+ /* Is it the first group? */
+ if (mld_group_list == group) {
+ mld_group_list = group->next;
+ } else {
+ /* look for group further down the list */
+ struct mld_group *tmpGroup;
+ for (tmpGroup = mld_group_list; tmpGroup != NULL; tmpGroup = tmpGroup->next) {
+ if (tmpGroup->next == group) {
+ tmpGroup->next = group->next;
+ break;
+ }
+ }
+ /* Group not find group */
+ if (tmpGroup == NULL)
+ err = ERR_ARG;
+ }
+ /* free group */
+ memp_free(MEMP_MLD6_GROUP, group);
+
+ return err;
+}
+
+
+/**
+ * Process an input MLD message. Called by icmp6_input.
+ *
+ * @param p the mld packet, p->payload pointing to the icmpv6 header
+ * @param inp the netif on which this packet was received
+ */
+void
+mld6_input(struct pbuf *p, struct netif *inp)
+{
+ struct mld_header * mld_hdr;
+ struct mld_group* group;
+
+ MLD6_STATS_INC(mld6.recv);
+
+ /* Check that mld header fits in packet. */
+ if (p->len < sizeof(struct mld_header)) {
+ /* TODO debug message */
+ pbuf_free(p);
+ MLD6_STATS_INC(mld6.lenerr);
+ MLD6_STATS_INC(mld6.drop);
+ return;
+ }
+
+ mld_hdr = (struct mld_header *)p->payload;
+
+ switch (mld_hdr->type) {
+ case ICMP6_TYPE_MLQ: /* Multicast listener query. */
+ {
+ /* Is it a general query? */
+ if (ip6_addr_isallnodes_linklocal(ip6_current_dest_addr()) &&
+ ip6_addr_isany(&(mld_hdr->multicast_address))) {
+ MLD6_STATS_INC(mld6.rx_general);
+ /* Report all groups, except all nodes group, and if-local groups. */
+ group = mld_group_list;
+ while (group != NULL) {
+ if ((group->netif == inp) &&
+ (!(ip6_addr_ismulticast_iflocal(&(group->group_address)))) &&
+ (!(ip6_addr_isallnodes_linklocal(&(group->group_address))))) {
+ mld6_delayed_report(group, mld_hdr->max_resp_delay);
+ }
+ group = group->next;
+ }
+ }
+ else {
+ /* Have we joined this group?
+ * We use IP6 destination address to have a memory aligned copy.
+ * mld_hdr->multicast_address should be the same. */
+ MLD6_STATS_INC(mld6.rx_group);
+ group = mld6_lookfor_group(inp, ip6_current_dest_addr());
+ if (group != NULL) {
+ /* Schedule a report. */
+ mld6_delayed_report(group, mld_hdr->max_resp_delay);
+ }
+ }
+ break; /* ICMP6_TYPE_MLQ */
+ }
+ case ICMP6_TYPE_MLR: /* Multicast listener report. */
+ {
+ /* Have we joined this group?
+ * We use IP6 destination address to have a memory aligned copy.
+ * mld_hdr->multicast_address should be the same. */
+ MLD6_STATS_INC(mld6.rx_report);
+ group = mld6_lookfor_group(inp, ip6_current_dest_addr());
+ if (group != NULL) {
+ /* If we are waiting to report, cancel it. */
+ if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
+ group->timer = 0; /* stopped */
+ group->group_state = MLD6_GROUP_IDLE_MEMBER;
+ group->last_reporter_flag = 0;
+ }
+ }
+ break; /* ICMP6_TYPE_MLR */
+ }
+ case ICMP6_TYPE_MLD: /* Multicast listener done. */
+ {
+ /* Do nothing, router will query us. */
+ break; /* ICMP6_TYPE_MLD */
+ }
+ default:
+ MLD6_STATS_INC(mld6.proterr);
+ MLD6_STATS_INC(mld6.drop);
+ break;
+ }
+
+ pbuf_free(p);
+}
+
+/**
+ * Join a group on a network interface.
+ *
+ * @param srcaddr ipv6 address of the network interface which should
+ * join a new group. If IP6_ADDR_ANY, join on all netifs
+ * @param groupaddr the ipv6 address of the group to join
+ * @return ERR_OK if group was joined on the netif(s), an err_t otherwise
+ */
+err_t
+mld6_joingroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr)
+{
+ err_t err = ERR_VAL; /* no matching interface */
+ struct netif *netif;
+
+ if (ip6_addr_isany(srcaddr)) {
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ err = mld6_netif_joingroup(netif, groupaddr);
+ }
+ }
+ else {
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ if (netif_matches_ip6_addr(netif, srcaddr) >= 0) {
+ return mld6_netif_joingroup(netif, groupaddr);
+ }
+ }
+ }
+
+ return err;
+}
+
+/**
+ * Join a group on the specified network interface.
+ *
+ * @param netif the network interface which should join a new group.
+ * @param groupaddr the ipv6 address of the group to join
+ * @return ERR_OK if group was joined, an err_t otherwise
+ */
+err_t
+mld6_netif_joingroup(struct netif *netif, ip6_addr_t *groupaddr)
+{
+ struct mld_group *group;
+
+ /* find group or create a new one if not found */
+ group = mld6_lookfor_group(netif, groupaddr);
+
+ if (group == NULL) {
+ /* Joining a new group. Create a new group entry. */
+ group = mld6_new_group(netif, groupaddr);
+ if (group == NULL) {
+ return ERR_MEM;
+ }
+
+ /* Activate this address on the MAC layer. */
+ if (netif->mld_mac_filter != NULL) {
+ netif->mld_mac_filter(netif, groupaddr, MLD6_ADD_MAC_FILTER);
+ }
+
+ /* Report our membership. */
+ MLD6_STATS_INC(mld6.tx_report);
+ mld6_send(group, ICMP6_TYPE_MLR);
+ mld6_delayed_report(group, MLD6_JOIN_DELAYING_MEMBER_TMR_MS);
+ }
+
+ /* Increment group use */
+ group->use++;
+ return ERR_OK;
+}
+
+/**
+ * Leave a group on a network interface.
+ *
+ * @param srcaddr ipv6 address of the network interface which should
+ * leave the group. If IP6_ISANY, leave on all netifs
+ * @param groupaddr the ipv6 address of the group to leave
+ * @return ERR_OK if group was left on the netif(s), an err_t otherwise
+ */
+err_t
+mld6_leavegroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr)
+{
+ err_t err = ERR_VAL; /* no matching interface */
+ struct netif *netif;
+
+ if (ip6_addr_isany(srcaddr)) {
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ err = mld6_netif_leavegroup(netif, groupaddr);
+ }
+ }
+ else {
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ if (netif_matches_ip6_addr(netif, srcaddr) >= 0) {
+ return mld6_netif_leavegroup(netif, groupaddr);
+ }
+ }
+ }
+
+ return err;
+}
+
+/**
+ * Leave a group on the specified network interface.
+ *
+ * @param netif the network interface which should leave the group.
+ * @param groupaddr the ipv6 address of the group to leave
+ * @return ERR_OK if group was left, an err_t otherwise
+ */
+err_t
+mld6_netif_leavegroup(struct netif *netif, ip6_addr_t *groupaddr)
+{
+ struct mld_group *group;
+
+ /* find group */
+ group = mld6_lookfor_group(netif, groupaddr);
+ if (group == NULL) {
+ return ERR_VAL;
+ }
+
+ /* Leave if there is no other use of the group */
+ if (group->use <= 1) {
+ /* If we are the last reporter for this group */
+ if (group->last_reporter_flag) {
+ MLD6_STATS_INC(mld6.tx_leave);
+ mld6_send(group, ICMP6_TYPE_MLD);
+ }
+
+ /* Disable the group at the MAC level */
+ if (netif->mld_mac_filter != NULL) {
+ netif->mld_mac_filter(netif, groupaddr, MLD6_DEL_MAC_FILTER);
+ }
+
+ /* Free the group */
+ mld6_free_group(group);
+ } else {
+ /* Decrement group use */
+ group->use--;
+ }
+
+ return ERR_OK;
+}
+
+/**
+ * Periodic timer for mld processing. Must be called every
+ * MLD6_TMR_INTERVAL milliseconds (100).
+ *
+ * When a delaying member expires, a membership report is sent.
+ */
+void
+mld6_tmr(void)
+{
+ struct mld_group *group = mld_group_list;
+
+ while (group != NULL) {
+ if (group->timer > 0) {
+ group->timer--;
+ if (group->timer == 0) {
+ /* If the state is MLD6_GROUP_DELAYING_MEMBER then we send a report for this group */
+ if (group->group_state == MLD6_GROUP_DELAYING_MEMBER) {
+ MLD6_STATS_INC(mld6.tx_report);
+ mld6_send(group, ICMP6_TYPE_MLR);
+ group->group_state = MLD6_GROUP_IDLE_MEMBER;
+ }
+ }
+ }
+ group = group->next;
+ }
+}
+
+/**
+ * Schedule a delayed membership report for a group
+ *
+ * @param group the mld_group for which "delaying" membership report
+ * should be sent
+ * @param maxresp the max resp delay provided in the query
+ */
+static void
+mld6_delayed_report(struct mld_group *group, u16_t maxresp)
+{
+ /* Convert maxresp from milliseconds to tmr ticks */
+ maxresp = maxresp / MLD6_TMR_INTERVAL;
+ if (maxresp == 0) {
+ maxresp = 1;
+ }
+
+#ifdef LWIP_RAND
+ /* Randomize maxresp. (if LWIP_RAND is supported) */
+ maxresp = LWIP_RAND() % maxresp;
+ if (maxresp == 0) {
+ maxresp = 1;
+ }
+#endif /* LWIP_RAND */
+
+ /* Apply timer value if no report has been scheduled already. */
+ if ((group->group_state == MLD6_GROUP_IDLE_MEMBER) ||
+ ((group->group_state == MLD6_GROUP_DELAYING_MEMBER) &&
+ ((group->timer == 0) || (maxresp < group->timer)))) {
+ group->timer = maxresp;
+ group->group_state = MLD6_GROUP_DELAYING_MEMBER;
+ }
+}
+
+/**
+ * Send a MLD message (report or done).
+ *
+ * An IPv6 hop-by-hop options header with a router alert option
+ * is prepended.
+ *
+ * @param group the group to report or quit
+ * @param type ICMP6_TYPE_MLR (report) or ICMP6_TYPE_MLD (done)
+ */
+static void
+mld6_send(struct mld_group *group, u8_t type)
+{
+ struct mld_header * mld_hdr;
+ struct pbuf * p;
+ ip6_addr_t * src_addr;
+
+ /* Allocate a packet. Size is MLD header + IPv6 Hop-by-hop options header. */
+ p = pbuf_alloc(PBUF_IP, sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr), PBUF_RAM);
+ if ((p == NULL) || (p->len < (sizeof(struct mld_header) + sizeof(struct ip6_hbh_hdr)))) {
+ /* We couldn't allocate a suitable pbuf. drop it. */
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+ MLD6_STATS_INC(mld6.memerr);
+ return;
+ }
+
+ /* Move to make room for Hop-by-hop options header. */
+ if (pbuf_header(p, -IP6_HBH_HLEN)) {
+ pbuf_free(p);
+ MLD6_STATS_INC(mld6.lenerr);
+ return;
+ }
+
+ /* Select our source address. */
+ if (!ip6_addr_isvalid(netif_ip6_addr_state(group->netif, 0))) {
+ /* This is a special case, when we are performing duplicate address detection.
+ * We must join the multicast group, but we don't have a valid address yet. */
+ src_addr = IP6_ADDR_ANY;
+ } else {
+ /* Use link-local address as source address. */
+ src_addr = netif_ip6_addr(group->netif, 0);
+ }
+
+ /* MLD message header pointer. */
+ mld_hdr = (struct mld_header *)p->payload;
+
+ /* Set fields. */
+ mld_hdr->type = type;
+ mld_hdr->code = 0;
+ mld_hdr->chksum = 0;
+ mld_hdr->max_resp_delay = 0;
+ mld_hdr->reserved = 0;
+ ip6_addr_set(&(mld_hdr->multicast_address), &(group->group_address));
+
+#if 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);
+
+ /* Send the packet out. */
+ MLD6_STATS_INC(mld6.xmit);
+ ip6_output_if(p, (ip6_addr_isany(src_addr)) ? NULL : src_addr, &(group->group_address),
+ MLD6_HL, 0, IP6_NEXTH_HOPBYHOP, group->netif);
+ pbuf_free(p);
+}
+
+
+
+#endif /* LWIP_IPV6 */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/ipv6/nd6.c b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/nd6.c
new file mode 100644
index 00000000..960958ec
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/ipv6/nd6.c
@@ -0,0 +1,1845 @@
+/**
+ * @file
+ *
+ * Neighbor discovery and stateless address autoconfiguration for IPv6.
+ * Aims to be compliant with RFC 4861 (Neighbor discovery) and RFC 4862
+ * (Address autoconfiguration).
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/nd6.h"
+#include "lwip/pbuf.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/netif.h"
+#include "lwip/icmp6.h"
+#include "lwip/mld6.h"
+#include "lwip/ip.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+
+/* Router tables. */
+struct nd6_neighbor_cache_entry neighbor_cache[LWIP_ND6_NUM_NEIGHBORS];
+struct nd6_destination_cache_entry destination_cache[LWIP_ND6_NUM_DESTINATIONS];
+struct nd6_prefix_list_entry prefix_list[LWIP_ND6_NUM_PREFIXES];
+struct nd6_router_list_entry default_router_list[LWIP_ND6_NUM_ROUTERS];
+
+/* Default values, can be updated by a RA message. */
+u32_t reachable_time = LWIP_ND6_REACHABLE_TIME;
+u32_t retrans_timer = LWIP_ND6_RETRANS_TIMER; /* TODO implement this value in timer */
+
+/* Index for cache entries. */
+static u8_t nd6_cached_neighbor_index;
+static u8_t nd6_cached_destination_index;
+
+/* Multicast address holder. */
+static ip6_addr_t multicast_address;
+
+/* Static buffer to parse RA packet options (size of a prefix option, biggest option) */
+static u8_t nd6_ra_buffer[sizeof(struct prefix_option)];
+
+#if LWIP_ND6_PROXY
+proxy_na_hook_fn proxy_na_hook;
+#endif
+
+/* Forward declarations. */
+static s8_t nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr);
+static s8_t nd6_new_neighbor_cache_entry(void);
+static void nd6_free_neighbor_cache_entry(s8_t i);
+static s8_t nd6_find_destination_cache_entry(ip6_addr_t * ip6addr);
+static s8_t nd6_new_destination_cache_entry(void);
+static s8_t nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif);
+static s8_t nd6_get_router(ip6_addr_t * router_addr, struct netif * netif);
+static s8_t nd6_new_router(ip6_addr_t * router_addr, struct netif * netif);
+static s8_t nd6_get_onlink_prefix(ip6_addr_t * prefix, struct netif * netif);
+static s8_t nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif);
+
+#define ND6_SEND_FLAG_MULTICAST_DEST 0x01
+#define ND6_SEND_FLAG_ALLNODES_DEST 0x02
+static void nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags);
+static void nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags);
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+static void nd6_send_rs(struct netif * netif);
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+
+#if LWIP_ND6_QUEUEING
+static void nd6_free_q(struct nd6_q_entry *q);
+#else /* LWIP_ND6_QUEUEING */
+#define nd6_free_q(q) pbuf_free(q)
+#endif /* LWIP_ND6_QUEUEING */
+static void nd6_send_q(s8_t i);
+
+
+/**
+ * Process an incoming neighbor discovery message
+ *
+ * @param p the nd packet, p->payload pointing to the icmpv6 header
+ * @param inp the netif on which this packet was received
+ */
+void
+nd6_input(struct pbuf *p, struct netif *inp)
+{
+ u8_t msg_type;
+ s8_t i;
+
+ ND6_STATS_INC(nd6.recv);
+
+ msg_type = *((u8_t *)p->payload);
+ switch (msg_type) {
+ case ICMP6_TYPE_NA: /* Neighbor Advertisement. */
+ {
+ struct na_header * na_hdr;
+ struct lladdr_option * lladdr_opt;
+
+ /* Check that na header fits in packet. */
+ if (p->len < (sizeof(struct na_header))) {
+ /* TODO debug message */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ na_hdr = (struct na_header *)p->payload;
+
+ /* Unsolicited NA?*/
+ if (ip6_addr_ismulticast(ip6_current_dest_addr())) {
+ /* This is an unsolicited NA.
+ * link-layer changed?
+ * part of DAD mechanism? */
+
+ /* Check that link-layer address option also fits in packet. */
+ if (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option))) {
+ /* TODO debug message */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
+
+ /* Override ip6_current_dest_addr() so that we have an aligned copy. */
+ ip6_addr_set(ip6_current_dest_addr(), &(na_hdr->target_address));
+
+#if LWIP_IPV6_DUP_DETECT_ATTEMPTS
+ /* If the target address matches this netif, it is a DAD response. */
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) &&
+ ip6_addr_cmp(ip6_current_dest_addr(), netif_ip6_addr(inp, i))) {
+ /* We are using a duplicate address. */
+ netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID);
+
+#if LWIP_IPV6_MLD
+ /* Leave solicited node multicast group. */
+ ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(inp, i)->addr[3]);
+ mld6_leavegroup(netif_ip6_addr(inp, i), &multicast_address);
+#endif /* LWIP_IPV6_MLD */
+
+
+
+
+#if LWIP_IPV6_AUTOCONFIG
+ /* Check to see if this address was autoconfigured. */
+ if (!ip6_addr_islinklocal(ip6_current_dest_addr())) {
+ i = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp);
+ if (i >= 0) {
+ /* Mark this prefix as duplicate, so that we don't use it
+ * to generate this address again. */
+ prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_DUPLICATE;
+ }
+ }
+#endif /* LWIP_IPV6_AUTOCONFIG */
+
+ pbuf_free(p);
+ return;
+ }
+ }
+#endif /* LWIP_IPV6_DUP_DETECT_ATTEMPTS */
+
+ /* This is an unsolicited NA, most likely there was a LLADDR change. */
+ i = nd6_find_neighbor_cache_entry(ip6_current_dest_addr());
+ if (i >= 0) {
+ if (na_hdr->flags & ND6_FLAG_OVERRIDE) {
+ MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+ }
+ }
+ }
+ else {
+ /* This is a solicited NA.
+ * neighbor address resolution response?
+ * neighbor unreachability detection response? */
+
+ /* Override ip6_current_dest_addr() so that we have an aligned copy. */
+ ip6_addr_set(ip6_current_dest_addr(), &(na_hdr->target_address));
+
+ /* Find the cache entry corresponding to this na. */
+ i = nd6_find_neighbor_cache_entry(ip6_current_dest_addr());
+ if (i < 0) {
+ /* We no longer care about this target address. drop it. */
+ pbuf_free(p);
+ return;
+ }
+
+ /* Update cache entry. */
+ neighbor_cache[i].netif = inp;
+ neighbor_cache[i].counter.reachable_time = reachable_time;
+ if ((na_hdr->flags & ND6_FLAG_OVERRIDE) ||
+ (neighbor_cache[i].state == ND6_INCOMPLETE)) {
+ /* Check that link-layer address option also fits in packet. */
+ if (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option))) {
+ /* TODO debug message */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
+
+ MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+ }
+ neighbor_cache[i].state = ND6_REACHABLE;
+
+ /* Send queued packets, if any. */
+ if (neighbor_cache[i].q != NULL) {
+ nd6_send_q(i);
+ }
+ }
+
+ break; /* ICMP6_TYPE_NA */
+ }
+ case ICMP6_TYPE_NS: /* Neighbor solicitation. */
+ {
+ struct ns_header * ns_hdr;
+ struct lladdr_option * lladdr_opt;
+ u8_t accepted;
+#if LWIP_ND6_PROXY
+ u8_t proxy;
+#endif
+ u8_t na_flags;
+
+ /* Check that ns header fits in packet. */
+ if (p->len < sizeof(struct ns_header)) {
+ /* TODO debug message */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ ns_hdr = (struct ns_header *)p->payload;
+
+ /* Check if there is a link-layer address provided. Only point to it if in this buffer. */
+ lladdr_opt = NULL;
+ if (p->len >= (sizeof(struct ns_header) + sizeof(struct lladdr_option))) {
+ lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header));
+ }
+
+ /* Check if the target address is configured on the receiving netif. */
+ accepted = 0;
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+ if ((ip6_addr_isvalid(netif_ip6_addr_state(inp, i)) ||
+ (ip6_addr_istentative(netif_ip6_addr_state(inp, i)) &&
+ ip6_addr_isany(ip6_current_src_addr()))) &&
+ ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) {
+ accepted = 1;
+ break;
+ }
+ }
+
+#if LWIP_ND6_PROXY
+ /* 7.2.8. Proxy Neighbor Advertisements */
+ proxy = 0;
+ if (!accepted && proxy_na_hook != NULL) {
+ ip6_addr_t target_address; /* aligned / not packed */
+ ip6_addr_copy(target_address, ns_hdr->target_address);
+ if ((*proxy_na_hook)(inp, &target_address)) {
+ accepted = proxy = 1;
+ }
+ }
+#endif
+
+ /* NS not for us? */
+ if (!accepted) {
+ pbuf_free(p);
+ return;
+ }
+
+ /* Check for ANY address in src (DAD algorithm). */
+ if (ip6_addr_isany(ip6_current_src_addr())) {
+ /* Sender is validating this address. */
+#if LWIP_ND6_PROXY
+ if (proxy) {
+ /* Send a NA back so that the sender does not use this address. */
+ ip6_addr_t target_address; /* aligned / not packed */
+ ip6_addr_copy(target_address, ns_hdr->target_address);
+ nd6_send_na(inp, &target_address, ND6_SEND_FLAG_ALLNODES_DEST);
+ break;
+ }
+#endif
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+ if (!ip6_addr_isinvalid(netif_ip6_addr_state(inp, i)) &&
+ ip6_addr_cmp(&(ns_hdr->target_address), netif_ip6_addr(inp, i))) {
+ /* Send a NA back so that the sender does not use this address. */
+ na_flags = ND6_FLAG_OVERRIDE | ND6_SEND_FLAG_ALLNODES_DEST;
+#if LWIP_CONNECTION_PROXY
+ /* XXX: Need a NETIF_FLAG_??? to indicate if this is an
+ * advertising interface. */
+ na_flags |= ND6_FLAG_ROUTER;
+#endif
+ nd6_send_na(inp, netif_ip6_addr(inp, i), na_flags);
+ if (ip6_addr_istentative(netif_ip6_addr_state(inp, i))) {
+ /* We shouldn't use this address either. */
+ netif_ip6_addr_set_state(inp, i, IP6_ADDR_INVALID);
+ }
+ }
+ }
+ }
+ else {
+ /* Sender is trying to resolve our address. */
+ /* Verify that they included their own link-layer address. */
+ if (lladdr_opt == NULL) {
+ /* Not a valid message. */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.proterr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ i = nd6_find_neighbor_cache_entry(ip6_current_src_addr());
+ if ( i>= 0) {
+ /* We already have a record for the solicitor. */
+ if (neighbor_cache[i].state == ND6_INCOMPLETE) {
+ neighbor_cache[i].netif = inp;
+ MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+
+ /* Delay probe in case we get confirmation of reachability from upper layer (TCP). */
+ neighbor_cache[i].state = ND6_DELAY;
+ neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME;
+ }
+ }
+ else
+ {
+ /* Add their IPv6 address and link-layer address to neighbor cache.
+ * We will need it at least to send a unicast NA message, but most
+ * likely we will also be communicating with this node soon. */
+ i = nd6_new_neighbor_cache_entry();
+ if (i < 0) {
+ /* We couldn't assign a cache entry for this neighbor.
+ * we won't be able to reply. drop it. */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.memerr);
+ return;
+ }
+ neighbor_cache[i].netif = inp;
+ MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+ ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr());
+
+ /* Receiving a message does not prove reachability: only in one direction.
+ * Delay probe in case we get confirmation of reachability from upper layer (TCP). */
+ neighbor_cache[i].state = ND6_DELAY;
+ neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME;
+ }
+
+ /* Override ip6_current_dest_addr() so that we have an aligned copy. */
+ ip6_addr_set(ip6_current_dest_addr(), &(ns_hdr->target_address));
+
+ na_flags = ND6_FLAG_SOLICITED;
+#if LWIP_ND6_PROXY
+ if (!proxy)
+#endif
+ {
+ na_flags |= ND6_FLAG_OVERRIDE;
+#if LWIP_IPV6_FORWARD
+ /* XXX: Need a NETIF_FLAG_??? to indicate if this is
+ * an advertising interface. */
+ na_flags |= ND6_FLAG_ROUTER;
+#endif
+ }
+ /* Send back a NA for us. Allocate the reply pbuf. */
+ nd6_send_na(inp, ip6_current_dest_addr(), na_flags);
+ }
+
+ break; /* ICMP6_TYPE_NS */
+ }
+ case ICMP6_TYPE_RA: /* Router Advertisement. */
+ {
+ struct ra_header * ra_hdr;
+ u8_t * buffer; /* Used to copy options. */
+ u16_t offset;
+
+ /* Check that RA header fits in packet. */
+ if (p->len < sizeof(struct ra_header)) {
+ /* TODO debug message */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ ra_hdr = (struct ra_header *)p->payload;
+
+ /* If we are sending RS messages, stop. */
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+ inp->rs_count = 0;
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+
+ /* Get the matching default router entry. */
+ i = nd6_get_router(ip6_current_src_addr(), inp);
+ if (i < 0) {
+ /* Create a new router entry. */
+ i = nd6_new_router(ip6_current_src_addr(), inp);
+ }
+
+ if (i < 0) {
+ /* Could not create a new router entry. */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.memerr);
+ return;
+ }
+
+ /* Re-set invalidation timer. */
+ default_router_list[i].invalidation_timer = ra_hdr->router_lifetime;
+
+ /* Re-set default timer values. */
+#if LWIP_ND6_ALLOW_RA_UPDATES
+ if (ra_hdr->retrans_timer > 0) {
+ retrans_timer = ra_hdr->retrans_timer;
+ }
+ if (ra_hdr->reachable_time > 0) {
+ reachable_time = ra_hdr->reachable_time;
+ }
+#endif /* LWIP_ND6_ALLOW_RA_UPDATES */
+
+ /* TODO set default hop limit... */
+ /* ra_hdr->current_hop_limit;*/
+
+ /* Update flags in local entry (incl. preference). */
+ default_router_list[i].flags = ra_hdr->flags;
+
+ /* Offset to options. */
+ offset = sizeof(struct ra_header);
+
+ /* Process each option. */
+ while ((p->tot_len - offset) > 0) {
+ if (p->len == p->tot_len) {
+ /* no need to copy from contiguous pbuf */
+ buffer = &((u8_t*)p->payload)[offset];
+ } else {
+ buffer = nd6_ra_buffer;
+ pbuf_copy_partial(p, buffer, sizeof(struct prefix_option), offset);
+ }
+ switch (buffer[0]) {
+ case ND6_OPTION_TYPE_SOURCE_LLADDR:
+ {
+ struct lladdr_option * lladdr_opt;
+ lladdr_opt = (struct lladdr_option *)buffer;
+ if ((default_router_list[i].neighbor_entry != NULL) &&
+ (default_router_list[i].neighbor_entry->state == ND6_INCOMPLETE)) {
+ SMEMCPY(default_router_list[i].neighbor_entry->lladdr, lladdr_opt->addr, inp->hwaddr_len);
+ default_router_list[i].neighbor_entry->state = ND6_REACHABLE;
+ default_router_list[i].neighbor_entry->counter.reachable_time = reachable_time;
+ }
+ break;
+ }
+ case ND6_OPTION_TYPE_MTU:
+ {
+ struct mtu_option * mtu_opt;
+ mtu_opt = (struct mtu_option *)buffer;
+ if (mtu_opt->mtu >= 1280) {
+#if LWIP_ND6_ALLOW_RA_UPDATES
+ inp->mtu = mtu_opt->mtu;
+#endif /* LWIP_ND6_ALLOW_RA_UPDATES */
+ }
+ break;
+ }
+ case ND6_OPTION_TYPE_PREFIX_INFO:
+ {
+ struct prefix_option * prefix_opt;
+ prefix_opt = (struct prefix_option *)buffer;
+
+ if (prefix_opt->flags & ND6_PREFIX_FLAG_ON_LINK) {
+ /* Add to on-link prefix list. */
+ s8_t prefix;
+
+ /* Get a memory-aligned copy of the prefix. */
+ ip6_addr_set(ip6_current_dest_addr(), &(prefix_opt->prefix));
+
+ /* find cache entry for this prefix. */
+ prefix = nd6_get_onlink_prefix(ip6_current_dest_addr(), inp);
+ if (prefix < 0) {
+ /* Create a new cache entry. */
+ prefix = nd6_new_onlink_prefix(ip6_current_dest_addr(), inp);
+ }
+ if (prefix >= 0) {
+ prefix_list[prefix].invalidation_timer = prefix_opt->valid_lifetime;
+
+#if LWIP_IPV6_AUTOCONFIG
+ if (prefix_opt->flags & ND6_PREFIX_FLAG_AUTONOMOUS) {
+ /* Mark prefix as autonomous, so that address autoconfiguration can take place.
+ * Only OR flag, so that we don't over-write other flags (such as ADDRESS_DUPLICATE)*/
+ prefix_list[prefix].flags |= ND6_PREFIX_AUTOCONFIG_AUTONOMOUS;
+ }
+#endif /* LWIP_IPV6_AUTOCONFIG */
+ }
+ }
+
+ break;
+ }
+ case ND6_OPTION_TYPE_ROUTE_INFO:
+ {
+ /* TODO implement preferred routes.
+ struct route_option * route_opt;
+ route_opt = (struct route_option *)buffer;*/
+
+ break;
+ }
+ default:
+ /* Unrecognized option, abort. */
+ ND6_STATS_INC(nd6.proterr);
+ break;
+ }
+ offset += 8 * ((u16_t)buffer[1]);
+ }
+
+ break; /* ICMP6_TYPE_RA */
+ }
+ case ICMP6_TYPE_RD: /* Redirect */
+ {
+ struct redirect_header * redir_hdr;
+ struct lladdr_option * lladdr_opt;
+
+ /* Check that Redir header fits in packet. */
+ if (p->len < sizeof(struct redirect_header)) {
+ /* TODO debug message */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ redir_hdr = (struct redirect_header *)p->payload;
+
+ lladdr_opt = NULL;
+ if (p->len >= (sizeof(struct redirect_header) + sizeof(struct lladdr_option))) {
+ lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct redirect_header));
+ }
+
+ /* Copy original destination address to current source address, to have an aligned copy. */
+ ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->destination_address));
+
+ /* Find dest address in cache */
+ i = nd6_find_destination_cache_entry(ip6_current_src_addr());
+ if (i < 0) {
+ /* Destination not in cache, drop packet. */
+ pbuf_free(p);
+ return;
+ }
+
+ /* Set the new target address. */
+ ip6_addr_set(&(destination_cache[i].next_hop_addr), &(redir_hdr->target_address));
+
+ /* If Link-layer address of other router is given, try to add to neighbor cache. */
+ if (lladdr_opt != NULL) {
+ if (lladdr_opt->type == ND6_OPTION_TYPE_TARGET_LLADDR) {
+ /* Copy target address to current source address, to have an aligned copy. */
+ ip6_addr_set(ip6_current_src_addr(), &(redir_hdr->target_address));
+
+ i = nd6_find_neighbor_cache_entry(ip6_current_src_addr());
+ if (i < 0) {
+ i = nd6_new_neighbor_cache_entry();
+ if (i >= 0) {
+ neighbor_cache[i].netif = inp;
+ MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+ ip6_addr_set(&(neighbor_cache[i].next_hop_address), ip6_current_src_addr());
+
+ /* Receiving a message does not prove reachability: only in one direction.
+ * Delay probe in case we get confirmation of reachability from upper layer (TCP). */
+ neighbor_cache[i].state = ND6_DELAY;
+ neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME;
+ }
+ }
+ if (i >= 0) {
+ if (neighbor_cache[i].state == ND6_INCOMPLETE) {
+ MEMCPY(neighbor_cache[i].lladdr, lladdr_opt->addr, inp->hwaddr_len);
+ /* Receiving a message does not prove reachability: only in one direction.
+ * Delay probe in case we get confirmation of reachability from upper layer (TCP). */
+ neighbor_cache[i].state = ND6_DELAY;
+ neighbor_cache[i].counter.delay_time = LWIP_ND6_DELAY_FIRST_PROBE_TIME;
+ }
+ }
+ }
+ }
+ break; /* ICMP6_TYPE_RD */
+ }
+ case ICMP6_TYPE_PTB: /* Packet too big */
+ {
+ struct icmp6_hdr *icmp6hdr; /* Packet too big message */
+ struct ip6_hdr * ip6hdr; /* IPv6 header of the packet which caused the error */
+
+ /* Check that ICMPv6 header + IPv6 header fit in payload */
+ if (p->len < (sizeof(struct icmp6_hdr) + IP6_HLEN)) {
+ /* drop short packets */
+ pbuf_free(p);
+ ND6_STATS_INC(nd6.lenerr);
+ ND6_STATS_INC(nd6.drop);
+ return;
+ }
+
+ icmp6hdr = (struct icmp6_hdr *)p->payload;
+ ip6hdr = (struct ip6_hdr *)((u8_t*)p->payload + sizeof(struct icmp6_hdr));
+
+ /* Copy original destination address to current source address, to have an aligned copy. */
+ ip6_addr_set(ip6_current_src_addr(), &(ip6hdr->dest));
+
+ /* Look for entry in destination cache. */
+ i = nd6_find_destination_cache_entry(ip6_current_src_addr());
+ if (i < 0) {
+ /* Destination not in cache, drop packet. */
+ pbuf_free(p);
+ return;
+ }
+
+ /* Change the Path MTU. */
+ destination_cache[i].pmtu = icmp6hdr->data;
+
+ break; /* ICMP6_TYPE_PTB */
+ }
+
+ default:
+ ND6_STATS_INC(nd6.proterr);
+ ND6_STATS_INC(nd6.drop);
+ break; /* default */
+ }
+
+ pbuf_free(p);
+}
+
+
+/**
+ * Periodic timer for Neighbor discovery functions:
+ *
+ * - Update neighbor reachability states
+ * - Update destination cache entries age
+ * - Update invalidation timers of default routers and on-link prefixes
+ * - Perform duplicate address detection (DAD) for our addresses
+ * - Send router solicitations
+ */
+void
+nd6_tmr(void)
+{
+ s8_t i;
+ struct netif * netif;
+
+ /* Process neighbor entries. */
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ switch (neighbor_cache[i].state) {
+ case ND6_INCOMPLETE:
+ if (neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) {
+ /* Retries exceeded. */
+ nd6_free_neighbor_cache_entry(i);
+ }
+ else {
+ /* Send a NS for this entry. */
+ neighbor_cache[i].counter.probes_sent++;
+ nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), ND6_SEND_FLAG_MULTICAST_DEST);
+ }
+ break;
+ case ND6_REACHABLE:
+ /* Send queued packets, if any are left. Should have been sent already. */
+ if (neighbor_cache[i].q != NULL) {
+ nd6_send_q(i);
+ }
+ if (neighbor_cache[i].counter.reachable_time <= ND6_TMR_INTERVAL) {
+ /* Change to stale state. */
+ neighbor_cache[i].state = ND6_STALE;
+ neighbor_cache[i].counter.stale_time = 0;
+ }
+ else {
+ neighbor_cache[i].counter.reachable_time -= ND6_TMR_INTERVAL;
+ }
+ break;
+ case ND6_STALE:
+ neighbor_cache[i].counter.stale_time += ND6_TMR_INTERVAL;
+ break;
+ case ND6_DELAY:
+ if (neighbor_cache[i].counter.delay_time <= ND6_TMR_INTERVAL) {
+ /* Change to PROBE state. */
+ neighbor_cache[i].state = ND6_PROBE;
+ neighbor_cache[i].counter.probes_sent = 0;
+ }
+ else {
+ neighbor_cache[i].counter.delay_time -= ND6_TMR_INTERVAL;
+ }
+ break;
+ case ND6_PROBE:
+ if (neighbor_cache[i].counter.probes_sent >= LWIP_ND6_MAX_MULTICAST_SOLICIT) {
+ /* Retries exceeded. */
+ nd6_free_neighbor_cache_entry(i);
+ }
+ else {
+ /* Send a NS for this entry. */
+ neighbor_cache[i].counter.probes_sent++;
+ nd6_send_ns(neighbor_cache[i].netif, &(neighbor_cache[i].next_hop_address), 0);
+ }
+ break;
+ case ND6_NO_ENTRY:
+ default:
+ /* Do nothing. */
+ break;
+ }
+ }
+
+ /* Process destination entries. */
+ for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+ destination_cache[i].age++;
+ }
+
+ /* Process router entries. */
+ for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+ if (default_router_list[i].neighbor_entry != NULL) {
+ /* Active entry. */
+ if (default_router_list[i].invalidation_timer > 0) {
+ default_router_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
+ }
+ if (default_router_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) {
+ /* Less than 1 second remainig. Clear this entry. */
+ default_router_list[i].neighbor_entry->isrouter = 0;
+ default_router_list[i].neighbor_entry = NULL;
+ default_router_list[i].invalidation_timer = 0;
+ default_router_list[i].flags = 0;
+ }
+ }
+ }
+
+ /* Process prefix entries. */
+ for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
+ if (prefix_list[i].invalidation_timer < ND6_TMR_INTERVAL / 1000) {
+ prefix_list[i].invalidation_timer = 0;
+ }
+ if ((prefix_list[i].invalidation_timer > 0) &&
+ (prefix_list[i].netif != NULL)) {
+ prefix_list[i].invalidation_timer -= ND6_TMR_INTERVAL / 1000;
+
+#if LWIP_IPV6_AUTOCONFIG
+ /* Initiate address autoconfiguration for this prefix, if conditions are met. */
+ if (prefix_list[i].netif->ip6_autoconfig_enabled &&
+ (prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_AUTONOMOUS) &&
+ !(prefix_list[i].flags & ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED)) {
+ /* Try to get an address on this netif that is invalid.
+ * Skip 0 index (link-local address) */
+ s8_t j;
+ for (j = 1; j < LWIP_IPV6_NUM_ADDRESSES; j++) {
+ if (ip6_addr_isinvalid(netif_ip6_addr_state(prefix_list[i].netif, j))) {
+ /* Generate an address using this prefix and interface ID from link-local address. */
+ prefix_list[i].netif->ip6_addr[j].addr[0] = prefix_list[i].prefix.addr[0];
+ prefix_list[i].netif->ip6_addr[j].addr[1] = prefix_list[i].prefix.addr[1];
+ prefix_list[i].netif->ip6_addr[j].addr[2] = prefix_list[i].netif->ip6_addr[0].addr[2];
+ prefix_list[i].netif->ip6_addr[j].addr[3] = prefix_list[i].netif->ip6_addr[0].addr[3];
+
+ /* Mark it as tentative (DAD will be performed if configured). */
+ netif_ip6_addr_set_state(prefix_list[i].netif, j, IP6_ADDR_TENTATIVE);
+
+ /* Mark this prefix with ADDRESS_GENERATED, so that we don't try again. */
+ prefix_list[i].flags |= ND6_PREFIX_AUTOCONFIG_ADDRESS_GENERATED;
+
+ /* Exit loop. */
+ break;
+ }
+ }
+ }
+#endif /* LWIP_IPV6_AUTOCONFIG */
+ }
+ }
+
+
+ /* Process our own addresses, if DAD configured. */
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; ++i) {
+ if (ip6_addr_istentative(netif->ip6_addr_state[i])) {
+ if ((netif->ip6_addr_state[i] & 0x07) >= LWIP_IPV6_DUP_DETECT_ATTEMPTS) {
+ /* No NA received in response. Mark address as valid. */
+ netif->ip6_addr_state[i] = IP6_ADDR_PREFERRED;
+ /* TODO implement preferred and valid lifetimes. */
+ }
+ else if (netif->flags & NETIF_FLAG_UP) {
+#if LWIP_IPV6_MLD
+ if ((netif->ip6_addr_state[i] & 0x07) == 0) {
+ /* Join solicited node multicast group. */
+ ip6_addr_set_solicitednode(&multicast_address, netif_ip6_addr(netif, i)->addr[3]);
+ mld6_joingroup(netif_ip6_addr(netif, i), &multicast_address);
+ }
+#endif /* LWIP_IPV6_MLD */
+ /* Send a NS for this address. */
+ nd6_send_ns(netif, netif_ip6_addr(netif, i), ND6_SEND_FLAG_MULTICAST_DEST);
+ (netif->ip6_addr_state[i])++;
+ /* TODO send max 1 NS per tmr call? enable return*/
+ /*return;*/
+ }
+ }
+ }
+ }
+
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+ /* Send router solicitation messages, if necessary. */
+ for (netif = netif_list; netif != NULL; netif = netif->next) {
+ if ((netif->rs_count > 0) && (netif->flags & NETIF_FLAG_UP)) {
+ nd6_send_rs(netif);
+ netif->rs_count--;
+ }
+ }
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+
+}
+
+/**
+ * Send a neighbor solicitation message
+ *
+ * @param netif the netif on which to send the message
+ * @param target_addr the IPv6 target address for the ND message
+ * @param flags one of ND6_SEND_FLAG_*
+ */
+static void
+nd6_send_ns(struct netif * netif, ip6_addr_t * target_addr, u8_t flags)
+{
+ struct ns_header * ns_hdr;
+ struct lladdr_option * lladdr_opt;
+ struct pbuf * p;
+ ip6_addr_t * src_addr;
+
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif,0))) {
+ /* Use link-local address as source address. */
+ src_addr = netif_ip6_addr(netif, 0);
+ } else {
+ src_addr = IP6_ADDR_ANY;
+ }
+
+ /* Allocate a packet. */
+ p = pbuf_alloc(PBUF_IP, sizeof(struct ns_header) + sizeof(struct lladdr_option), PBUF_RAM);
+ if ((p == NULL) || (p->len < (sizeof(struct ns_header) + sizeof(struct lladdr_option)))) {
+ /* We couldn't allocate a suitable pbuf for the ns. drop it. */
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+ ND6_STATS_INC(nd6.memerr);
+ return;
+ }
+
+ /* Set fields. */
+ ns_hdr = (struct ns_header *)p->payload;
+ lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct ns_header));
+
+ ns_hdr->type = ICMP6_TYPE_NS;
+ ns_hdr->code = 0;
+ ns_hdr->chksum = 0;
+ ns_hdr->reserved = 0;
+ ip6_addr_set(&(ns_hdr->target_address), target_addr);
+
+ lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR;
+ lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
+ SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len);
+
+ /* Generate the solicited node address for the target address. */
+ if (flags & ND6_SEND_FLAG_MULTICAST_DEST) {
+ ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]);
+ target_addr = &multicast_address;
+ }
+
+#if 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);
+ pbuf_free(p);
+}
+
+/**
+ * Send a neighbor advertisement message
+ *
+ * @param netif the netif on which to send the message
+ * @param target_addr the IPv6 target address for the ND message
+ * @param flags one of ND6_SEND_FLAG_*
+ */
+static void
+nd6_send_na(struct netif * netif, ip6_addr_t * target_addr, u8_t flags)
+{
+ struct na_header * na_hdr;
+ struct lladdr_option * lladdr_opt;
+ struct pbuf * p;
+ ip6_addr_t * src_addr;
+ ip6_addr_t * dest_addr;
+
+ /* Use link-local address as source address. */
+ /* src_addr = &(netif->ip6_addr[0]); */
+ /* Use target address as source address. */
+ src_addr = target_addr;
+
+ /* Allocate a packet. */
+ p = pbuf_alloc(PBUF_IP, sizeof(struct na_header) + sizeof(struct lladdr_option), PBUF_RAM);
+ if ((p == NULL) || (p->len < (sizeof(struct na_header) + sizeof(struct lladdr_option)))) {
+ /* We couldn't allocate a suitable pbuf for the ns. drop it. */
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+ ND6_STATS_INC(nd6.memerr);
+ return;
+ }
+
+ /* Set fields. */
+ na_hdr = (struct na_header *)p->payload;
+ lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct na_header));
+
+ na_hdr->type = ICMP6_TYPE_NA;
+ na_hdr->code = 0;
+ na_hdr->chksum = 0;
+ na_hdr->flags = flags & 0xf0;
+ na_hdr->reserved[0] = 0;
+ na_hdr->reserved[1] = 0;
+ na_hdr->reserved[2] = 0;
+ ip6_addr_set(&(na_hdr->target_address), target_addr);
+
+ lladdr_opt->type = ND6_OPTION_TYPE_TARGET_LLADDR;
+ lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
+ SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len);
+
+ /* Generate the solicited node address for the target address. */
+ if (flags & ND6_SEND_FLAG_MULTICAST_DEST) {
+ ip6_addr_set_solicitednode(&multicast_address, target_addr->addr[3]);
+ dest_addr = &multicast_address;
+ }
+ else if (flags & ND6_SEND_FLAG_ALLNODES_DEST) {
+ ip6_addr_set_allnodes_linklocal(&multicast_address);
+ dest_addr = &multicast_address;
+ }
+ else {
+ dest_addr = ip6_current_src_addr();
+ }
+
+#if CHECKSUM_GEN_ICMP6
+ na_hdr->chksum = ip6_chksum_pseudo(p, IP6_NEXTH_ICMP6, p->len, src_addr,
+ dest_addr);
+#endif /* CHECKSUM_GEN_ICMP6 */
+
+ /* Send the packet out. */
+ ND6_STATS_INC(nd6.xmit);
+ ip6_output_if(p, src_addr, dest_addr,
+ LWIP_ICMP6_HL, 0, IP6_NEXTH_ICMP6, netif);
+ pbuf_free(p);
+}
+
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+/**
+ * Send a router solicitation message
+ *
+ * @param netif the netif on which to send the message
+ */
+static void
+nd6_send_rs(struct netif * netif)
+{
+ struct rs_header * rs_hdr;
+ struct lladdr_option * lladdr_opt;
+ struct pbuf * p;
+ ip6_addr_t * src_addr;
+ u16_t packet_len;
+
+ /* Link-local source address, or unspecified address? */
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, 0))) {
+ src_addr = netif_ip6_addr(netif, 0);
+ }
+ else {
+ src_addr = IP6_ADDR_ANY;
+ }
+
+ /* Generate the all routers target address. */
+ ip6_addr_set_allrouters_linklocal(&multicast_address);
+
+ /* Allocate a packet. */
+ packet_len = sizeof(struct rs_header);
+ if (src_addr != IP6_ADDR_ANY) {
+ packet_len += sizeof(struct lladdr_option);
+ }
+ p = pbuf_alloc(PBUF_IP, packet_len, PBUF_RAM);
+ if ((p == NULL) || (p->len < packet_len)) {
+ /* We couldn't allocate a suitable pbuf for the ns. drop it. */
+ if (p != NULL) {
+ pbuf_free(p);
+ }
+ ND6_STATS_INC(nd6.memerr);
+ return;
+ }
+
+ /* Set fields. */
+ rs_hdr = (struct rs_header *)p->payload;
+
+ rs_hdr->type = ICMP6_TYPE_RS;
+ rs_hdr->code = 0;
+ rs_hdr->chksum = 0;
+ rs_hdr->reserved = 0;
+
+ if (src_addr != IP6_ADDR_ANY) {
+ /* Include our hw address. */
+ lladdr_opt = (struct lladdr_option *)((u8_t*)p->payload + sizeof(struct rs_header));
+ lladdr_opt->type = ND6_OPTION_TYPE_SOURCE_LLADDR;
+ lladdr_opt->length = ((netif->hwaddr_len + 2) >> 3) + (((netif->hwaddr_len + 2) & 0x07) ? 1 : 0);
+ SMEMCPY(lladdr_opt->addr, netif->hwaddr, netif->hwaddr_len);
+ }
+
+#if 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);
+ pbuf_free(p);
+}
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+
+/**
+ * Search for a neighbor cache entry
+ *
+ * @param ip6addr the IPv6 address of the neighbor
+ * @return The neighbor cache entry index that matched, -1 if no
+ * entry is found
+ */
+static s8_t
+nd6_find_neighbor_cache_entry(ip6_addr_t * ip6addr)
+{
+ s8_t i;
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ if (ip6_addr_cmp(ip6addr, &(neighbor_cache[i].next_hop_address))) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/**
+ * Create a new neighbor cache entry.
+ *
+ * If no unused entry is found, will try to recycle an old entry
+ * according to ad-hoc "age" heuristic.
+ *
+ * @return The neighbor cache entry index that was created, -1 if no
+ * entry could be created
+ */
+static s8_t
+nd6_new_neighbor_cache_entry(void)
+{
+ s8_t i;
+ s8_t j;
+ u32_t t;
+
+
+ /* First, try to find an empty entry. */
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ if (neighbor_cache[i].state == ND6_NO_ENTRY) {
+ return i;
+ }
+ }
+
+ /* We need to recycle an entry. in general, do not recycle if it is a router. */
+
+ /* Next, try to find a Stale entry. */
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ if ((neighbor_cache[i].state == ND6_STALE) &&
+ (!neighbor_cache[i].isrouter)) {
+ nd6_free_neighbor_cache_entry(i);
+ return i;
+ }
+ }
+
+ /* Next, try to find a Probe entry. */
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ if ((neighbor_cache[i].state == ND6_PROBE) &&
+ (!neighbor_cache[i].isrouter)) {
+ nd6_free_neighbor_cache_entry(i);
+ return i;
+ }
+ }
+
+ /* Next, try to find a Delayed entry. */
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ if ((neighbor_cache[i].state == ND6_DELAY) &&
+ (!neighbor_cache[i].isrouter)) {
+ nd6_free_neighbor_cache_entry(i);
+ return i;
+ }
+ }
+
+ /* Next, try to find the oldest reachable entry. */
+ t = 0xfffffffful;
+ j = -1;
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ if ((neighbor_cache[i].state == ND6_REACHABLE) &&
+ (!neighbor_cache[i].isrouter)) {
+ if (neighbor_cache[i].counter.reachable_time < t) {
+ j = i;
+ t = neighbor_cache[i].counter.reachable_time;
+ }
+ }
+ }
+ if (j >= 0) {
+ nd6_free_neighbor_cache_entry(j);
+ return j;
+ }
+
+ /* Next, find oldest incomplete entry without queued packets. */
+ t = 0;
+ j = -1;
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ if (
+ (neighbor_cache[i].q == NULL) &&
+ (neighbor_cache[i].state == ND6_INCOMPLETE) &&
+ (!neighbor_cache[i].isrouter)) {
+ if (neighbor_cache[i].counter.probes_sent >= t) {
+ j = i;
+ t = neighbor_cache[i].counter.probes_sent;
+ }
+ }
+ }
+ if (j >= 0) {
+ nd6_free_neighbor_cache_entry(j);
+ return j;
+ }
+
+ /* Next, find oldest incomplete entry with queued packets. */
+ t = 0;
+ j = -1;
+ for (i = 0; i < LWIP_ND6_NUM_NEIGHBORS; i++) {
+ if ((neighbor_cache[i].state == ND6_INCOMPLETE) &&
+ (!neighbor_cache[i].isrouter)) {
+ if (neighbor_cache[i].counter.probes_sent >= t) {
+ j = i;
+ t = neighbor_cache[i].counter.probes_sent;
+ }
+ }
+ }
+ if (j >= 0) {
+ nd6_free_neighbor_cache_entry(j);
+ return j;
+ }
+
+ /* No more entries to try. */
+ return -1;
+}
+
+/**
+ * Will free any resources associated with a neighbor cache
+ * entry, and will mark it as unused.
+ *
+ * @param i the neighbor cache entry index to free
+ */
+static void
+nd6_free_neighbor_cache_entry(s8_t i)
+{
+ if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) {
+ return;
+ }
+
+ /* Free any queued packets. */
+ if (neighbor_cache[i].q != NULL) {
+ nd6_free_q(neighbor_cache[i].q);
+ neighbor_cache[i].q = NULL;
+ }
+
+ neighbor_cache[i].state = ND6_NO_ENTRY;
+ neighbor_cache[i].isrouter = 0;
+ neighbor_cache[i].netif = NULL;
+ neighbor_cache[i].counter.reachable_time = 0;
+ ip6_addr_set_zero(&(neighbor_cache[i].next_hop_address));
+}
+
+/**
+ * Search for a destination cache entry
+ *
+ * @param ip6addr the IPv6 address of the destination
+ * @return The destination cache entry index that matched, -1 if no
+ * entry is found
+ */
+static s8_t
+nd6_find_destination_cache_entry(ip6_addr_t * ip6addr)
+{
+ s8_t i;
+ for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+ if (ip6_addr_cmp(ip6addr, &(destination_cache[i].destination_addr))) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+/**
+ * Create a new destination cache entry. If no unused entry is found,
+ * will recycle oldest entry.
+ *
+ * @return The destination cache entry index that was created, -1 if no
+ * entry was created
+ */
+static s8_t
+nd6_new_destination_cache_entry(void)
+{
+ s8_t i, j;
+ u32_t age;
+
+ /* Find an empty entry. */
+ for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+ if (ip6_addr_isany(&(destination_cache[i].destination_addr))) {
+ return i;
+ }
+ }
+
+ /* Find oldest entry. */
+ age = 0;
+ j = LWIP_ND6_NUM_DESTINATIONS - 1;
+ for (i = 0; i < LWIP_ND6_NUM_DESTINATIONS; i++) {
+ if (destination_cache[i].age > age) {
+ j = i;
+ }
+ }
+
+ return j;
+}
+
+/**
+ * Determine whether an address matches an on-link prefix.
+ *
+ * @param ip6addr the IPv6 address to match
+ * @return 1 if the address is on-link, 0 otherwise
+ */
+static s8_t
+nd6_is_prefix_in_netif(ip6_addr_t * ip6addr, struct netif * netif)
+{
+ s8_t i;
+ for (i = 0; i < LWIP_ND6_NUM_PREFIXES; i++) {
+ if ((prefix_list[i].netif == netif) &&
+ (prefix_list[i].invalidation_timer > 0) &&
+ ip6_addr_netcmp(ip6addr, &(prefix_list[i].prefix))) {
+ return 1;
+ }
+ }
+ /* Check to see if address prefix matches a (manually?) configured address. */
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (ip6_addr_isvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_netcmp(ip6addr, netif_ip6_addr(netif, i))) {
+ return 1;
+ }
+ }
+ return 0;
+}
+
+/**
+ * Select a default router for a destination.
+ *
+ * @param ip6addr the destination address
+ * @param netif the netif for the outgoing packet, if known
+ * @return the default router entry index, or -1 if no suitable
+ * router is found
+ */
+s8_t
+nd6_select_router(ip6_addr_t * ip6addr, struct netif * netif)
+{
+ s8_t i;
+ /* last_router is used for round-robin router selection (as recommended
+ * in RFC). This is more robust in case one router is not reachable,
+ * we are not stuck trying to resolve it. */
+ static s8_t last_router;
+ (void)ip6addr; /* TODO match preferred routes!! (must implement ND6_OPTION_TYPE_ROUTE_INFO) */
+
+ /* TODO: implement default router preference */
+
+ /* Look for reachable routers. */
+ for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+ if (++last_router >= LWIP_ND6_NUM_ROUTERS) {
+ last_router = 0;
+ }
+ if ((default_router_list[i].neighbor_entry != NULL) &&
+ (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) &&
+ (default_router_list[i].invalidation_timer > 0) &&
+ (default_router_list[i].neighbor_entry->state == ND6_REACHABLE)) {
+ return i;
+ }
+ }
+
+ /* Look for router in other reachability states, but still valid according to timer. */
+ for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+ if (++last_router >= LWIP_ND6_NUM_ROUTERS) {
+ last_router = 0;
+ }
+ if ((default_router_list[i].neighbor_entry != NULL) &&
+ (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1) &&
+ (default_router_list[i].invalidation_timer > 0)) {
+ return i;
+ }
+ }
+
+ /* Look for any router for which we have any information at all. */
+ for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+ if (++last_router >= LWIP_ND6_NUM_ROUTERS) {
+ last_router = 0;
+ }
+ if (default_router_list[i].neighbor_entry != NULL &&
+ (netif != NULL ? netif == default_router_list[i].neighbor_entry->netif : 1)) {
+ return i;
+ }
+ }
+
+ /* no suitable router found. */
+ return -1;
+}
+
+/**
+ * Find an entry for a default router.
+ *
+ * @param router_addr the IPv6 address of the router
+ * @param netif the netif on which the router is found, if known
+ * @return the index of the router entry, or -1 if not found
+ */
+static s8_t
+nd6_get_router(ip6_addr_t * router_addr, struct netif * netif)
+{
+ s8_t i;
+
+ /* Look for router. */
+ for (i = 0; i < LWIP_ND6_NUM_ROUTERS; i++) {
+ if ((default_router_list[i].neighbor_entry != NULL) &&
+ ((netif != NULL) ? netif == default_router_list[i].neighbor_entry->netif : 1) &&
+ ip6_addr_cmp(router_addr, &(default_router_list[i].neighbor_entry->next_hop_address))) {
+ return i;
+ }
+ }
+
+ /* router not found. */
+ return -1;
+}
+
+/**
+ * Create a new entry for a default router.
+ *
+ * @param router_addr the IPv6 address of the router
+ * @param netif the netif on which the router is connected, if known
+ * @return the index on the router table, or -1 if could not be created
+ */
+static s8_t
+nd6_new_router(ip6_addr_t * router_addr, struct netif * netif)
+{
+ s8_t router_index;
+ s8_t neighbor_index;
+
+ /* Do we have a neighbor entry for this router? */
+ neighbor_index = nd6_find_neighbor_cache_entry(router_addr);
+ if (neighbor_index < 0) {
+ /* Create a neighbor entry for this router. */
+ neighbor_index = nd6_new_neighbor_cache_entry();
+ if (neighbor_index < 0) {
+ /* Could not create neighbor entry for this router. */
+ return -1;
+ }
+ ip6_addr_set(&(neighbor_cache[neighbor_index].next_hop_address), router_addr);
+ neighbor_cache[neighbor_index].netif = netif;
+ neighbor_cache[neighbor_index].q = NULL;
+ neighbor_cache[neighbor_index].state = ND6_INCOMPLETE;
+ neighbor_cache[neighbor_index].counter.probes_sent = 0;
+ }
+
+ /* Mark neighbor as router. */
+ neighbor_cache[neighbor_index].isrouter = 1;
+
+ /* Look for empty entry. */
+ for (router_index = 0; router_index < LWIP_ND6_NUM_ROUTERS; router_index++) {
+ if (default_router_list[router_index].neighbor_entry == NULL) {
+ default_router_list[router_index].neighbor_entry = &(neighbor_cache[neighbor_index]);
+ return router_index;
+ }
+ }
+
+ /* Could not create a router entry. */
+
+ /* Mark neighbor entry as not-router. Entry might be useful as neighbor still. */
+ neighbor_cache[neighbor_index].isrouter = 0;
+
+ /* router not found. */
+ return -1;
+}
+
+/**
+ * Find the cached entry for an on-link prefix.
+ *
+ * @param prefix the IPv6 prefix that is on-link
+ * @param netif the netif on which the prefix is on-link
+ * @return the index on the prefix table, or -1 if not found
+ */
+static s8_t
+nd6_get_onlink_prefix(ip6_addr_t * prefix, struct netif * netif)
+{
+ s8_t i;
+
+ /* Look for prefix in list. */
+ for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) {
+ if ((ip6_addr_netcmp(&(prefix_list[i].prefix), prefix)) &&
+ (prefix_list[i].netif == netif)) {
+ return i;
+ }
+ }
+
+ /* Entry not available. */
+ return -1;
+}
+
+/**
+ * Creates a new entry for an on-link prefix.
+ *
+ * @param prefix the IPv6 prefix that is on-link
+ * @param netif the netif on which the prefix is on-link
+ * @return the index on the prefix table, or -1 if not created
+ */
+static s8_t
+nd6_new_onlink_prefix(ip6_addr_t * prefix, struct netif * netif)
+{
+ s8_t i;
+
+ /* Create new entry. */
+ for (i = 0; i < LWIP_ND6_NUM_PREFIXES; ++i) {
+ if ((prefix_list[i].netif == NULL) ||
+ (prefix_list[i].invalidation_timer == 0)) {
+ /* Found empty prefix entry. */
+ prefix_list[i].netif = netif;
+ ip6_addr_set(&(prefix_list[i].prefix), prefix);
+#if LWIP_IPV6_AUTOCONFIG
+ prefix_list[i].flags = 0;
+#endif
+ return i;
+ }
+ }
+
+ /* Entry not available. */
+ return -1;
+}
+
+/**
+ * Determine the next hop for a destination. Will determine if the
+ * destination is on-link, else a suitable on-link router is selected.
+ *
+ * The last entry index is cached for fast entry search.
+ *
+ * @param ip6addr the destination address
+ * @param netif the netif on which the packet will be sent
+ * @return the neighbor cache entry for the next hop, ERR_RTE if no
+ * suitable next hop was found, ERR_MEM if no cache entry
+ * could be created
+ */
+s8_t
+nd6_get_next_hop_entry(ip6_addr_t * ip6addr, struct netif * netif)
+{
+ s8_t i;
+
+#if LWIP_NETIF_HWADDRHINT
+ if (netif->addr_hint != NULL) {
+ /* per-pcb cached entry was given */
+ u8_t addr_hint = *(netif->addr_hint);
+ if (addr_hint < LWIP_ND6_NUM_DESTINATIONS) {
+ nd6_cached_destination_index = addr_hint;
+ }
+ }
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+ /* Look for ip6addr in destination cache. */
+ if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) {
+ /* the cached entry index is the right one! */
+ /* do nothing. */
+ ND6_STATS_INC(nd6.cachehit);
+ } else {
+ /* Search destination cache. */
+ i = nd6_find_destination_cache_entry(ip6addr);
+ if (i >= 0) {
+ /* found destination entry. make it our new cached index. */
+ nd6_cached_destination_index = i;
+ }
+ else {
+ /* Not found. Create a new destination entry. */
+ i = nd6_new_destination_cache_entry();
+ if (i >= 0) {
+ /* got new destination entry. make it our new cached index. */
+ nd6_cached_destination_index = i;
+ } else {
+ /* Could not create a destination cache entry. */
+ return ERR_MEM;
+ }
+
+ /* Copy dest address to destination cache. */
+ ip6_addr_set(&(destination_cache[nd6_cached_destination_index].destination_addr), ip6addr);
+
+ /* Now find the next hop. is it a neighbor? */
+ if (ip6_addr_islinklocal(ip6addr) ||
+ nd6_is_prefix_in_netif(ip6addr, netif)) {
+ /* Destination in local link. */
+ destination_cache[nd6_cached_destination_index].pmtu = netif->mtu;
+ ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, destination_cache[nd6_cached_destination_index].destination_addr);
+ }
+ else {
+ /* We need to select a router. */
+ i = nd6_select_router(ip6addr, netif);
+ if (i < 0) {
+ /* No router found. */
+ ip6_addr_set_any(&(destination_cache[nd6_cached_destination_index].destination_addr));
+ return ERR_RTE;
+ }
+ destination_cache[nd6_cached_destination_index].pmtu = netif->mtu; /* Start with netif mtu, correct through ICMPv6 if necessary */
+ ip6_addr_copy(destination_cache[nd6_cached_destination_index].next_hop_addr, default_router_list[i].neighbor_entry->next_hop_address);
+ }
+ }
+ }
+
+#if LWIP_NETIF_HWADDRHINT
+ if (netif->addr_hint != NULL) {
+ /* per-pcb cached entry was given */
+ *(netif->addr_hint) = nd6_cached_destination_index;
+ }
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+ /* Look in neighbor cache for the next-hop address. */
+ if (ip6_addr_cmp(&(destination_cache[nd6_cached_destination_index].next_hop_addr),
+ &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) {
+ /* Cache hit. */
+ /* Do nothing. */
+ ND6_STATS_INC(nd6.cachehit);
+ } else {
+ i = nd6_find_neighbor_cache_entry(&(destination_cache[nd6_cached_destination_index].next_hop_addr));
+ if (i >= 0) {
+ /* Found a matching record, make it new cached entry. */
+ nd6_cached_neighbor_index = i;
+ }
+ else {
+ /* Neighbor not in cache. Make a new entry. */
+ i = nd6_new_neighbor_cache_entry();
+ if (i >= 0) {
+ /* got new neighbor entry. make it our new cached index. */
+ nd6_cached_neighbor_index = i;
+ } else {
+ /* Could not create a neighbor cache entry. */
+ return ERR_MEM;
+ }
+
+ /* Initialize fields. */
+ ip6_addr_copy(neighbor_cache[i].next_hop_address,
+ destination_cache[nd6_cached_destination_index].next_hop_addr);
+ neighbor_cache[i].isrouter = 0;
+ neighbor_cache[i].netif = netif;
+ neighbor_cache[i].state = ND6_INCOMPLETE;
+ neighbor_cache[i].counter.probes_sent = 0;
+ }
+ }
+
+ /* Reset this destination's age. */
+ destination_cache[nd6_cached_destination_index].age = 0;
+
+ return nd6_cached_neighbor_index;
+}
+
+/**
+ * Queue a packet for a neighbor.
+ *
+ * @param neighbor_index the index in the neighbor cache table
+ * @param q packet to be queued
+ * @return ERR_OK if succeeded, ERR_MEM if out of memory
+ */
+err_t
+nd6_queue_packet(s8_t neighbor_index, struct pbuf * q)
+{
+ err_t result = ERR_MEM;
+ struct pbuf *p;
+ int copy_needed = 0;
+#if LWIP_ND6_QUEUEING
+ struct nd6_q_entry *new_entry, *r;
+#endif /* LWIP_ND6_QUEUEING */
+
+ if ((neighbor_index < 0) || (neighbor_index >= LWIP_ND6_NUM_NEIGHBORS)) {
+ return ERR_ARG;
+ }
+
+ /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but
+ * to copy the whole queue into a new PBUF_RAM (see bug #11400)
+ * PBUF_ROMs can be left as they are, since ROM must not get changed. */
+ p = q;
+ while (p) {
+ if(p->type != PBUF_ROM) {
+ copy_needed = 1;
+ break;
+ }
+ p = p->next;
+ }
+ if(copy_needed) {
+ /* copy the whole packet into new pbufs */
+ p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM);
+ while ((p == NULL) && (neighbor_cache[neighbor_index].q != NULL)) {
+ /* Free oldest packet (as per RFC recommendation) */
+#if LWIP_ND6_QUEUEING
+ r = neighbor_cache[neighbor_index].q;
+ neighbor_cache[neighbor_index].q = r->next;
+ r->next = NULL;
+ nd6_free_q(r);
+#else /* LWIP_ND6_QUEUEING */
+ pbuf_free(neighbor_cache[neighbor_index].q);
+ neighbor_cache[neighbor_index].q = NULL;
+#endif /* LWIP_ND6_QUEUEING */
+ p = pbuf_alloc(PBUF_LINK, q->tot_len, PBUF_RAM);
+ }
+ if(p != NULL) {
+ if (pbuf_copy(p, q) != ERR_OK) {
+ pbuf_free(p);
+ p = NULL;
+ }
+ }
+ } else {
+ /* referencing the old pbuf is enough */
+ p = q;
+ pbuf_ref(p);
+ }
+ /* packet was copied/ref'd? */
+ if (p != NULL) {
+ /* queue packet ... */
+#if LWIP_ND6_QUEUEING
+ /* allocate a new nd6 queue entry */
+ new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE);
+ if ((new_entry == NULL) && (neighbor_cache[neighbor_index].q != NULL)) {
+ /* Free oldest packet (as per RFC recommendation) */
+ r = neighbor_cache[neighbor_index].q;
+ neighbor_cache[neighbor_index].q = r->next;
+ r->next = NULL;
+ nd6_free_q(r);
+ new_entry = (struct nd6_q_entry *)memp_malloc(MEMP_ND6_QUEUE);
+ }
+ if (new_entry != NULL) {
+ new_entry->next = NULL;
+ new_entry->p = p;
+ if(neighbor_cache[neighbor_index].q != NULL) {
+ /* queue was already existent, append the new entry to the end */
+ r = neighbor_cache[neighbor_index].q;
+ while (r->next != NULL) {
+ r = r->next;
+ }
+ r->next = new_entry;
+ } else {
+ /* queue did not exist, first item in queue */
+ neighbor_cache[neighbor_index].q = new_entry;
+ }
+ LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index));
+ result = ERR_OK;
+ } else {
+ /* the pool MEMP_ND6_QUEUE is empty */
+ pbuf_free(p);
+ LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)p));
+ /* { result == ERR_MEM } through initialization */
+ }
+#else /* LWIP_ND6_QUEUEING */
+ /* Queue a single packet. If an older packet is already queued, free it as per RFC. */
+ if (neighbor_cache[neighbor_index].q != NULL) {
+ pbuf_free(neighbor_cache[neighbor_index].q);
+ }
+ neighbor_cache[neighbor_index].q = p;
+ LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: queued packet %p on neighbor entry %"S16_F"\n", (void *)p, (s16_t)neighbor_index));
+ result = ERR_OK;
+#endif /* LWIP_ND6_QUEUEING */
+ } else {
+ LWIP_DEBUGF(LWIP_DBG_TRACE, ("ipv6: could not queue a copy of packet %p (out of memory)\n", (void *)q));
+ /* { result == ERR_MEM } through initialization */
+ }
+
+ return result;
+}
+
+#if LWIP_ND6_QUEUEING
+/**
+ * Free a complete queue of nd6 q entries
+ *
+ * @param q a queue of nd6_q_entry to free
+ */
+static void
+nd6_free_q(struct nd6_q_entry *q)
+{
+ struct nd6_q_entry *r;
+ LWIP_ASSERT("q != NULL", q != NULL);
+ LWIP_ASSERT("q->p != NULL", q->p != NULL);
+ while (q) {
+ r = q;
+ q = q->next;
+ LWIP_ASSERT("r->p != NULL", (r->p != NULL));
+ pbuf_free(r->p);
+ memp_free(MEMP_ND6_QUEUE, r);
+ }
+}
+#endif /* LWIP_ND6_QUEUEING */
+
+/**
+ * Send queued packets for a neighbor
+ *
+ * @param i the neighbor to send packets to
+ */
+static void
+nd6_send_q(s8_t i)
+{
+ struct ip6_hdr *ip6hdr;
+#if LWIP_ND6_QUEUEING
+ struct nd6_q_entry *q;
+#endif /* LWIP_ND6_QUEUEING */
+
+ if ((i < 0) || (i >= LWIP_ND6_NUM_NEIGHBORS)) {
+ return;
+ }
+
+#if LWIP_ND6_QUEUEING
+ while (neighbor_cache[i].q != NULL) {
+ /* remember first in queue */
+ q = neighbor_cache[i].q;
+ /* pop first item off the queue */
+ neighbor_cache[i].q = q->next;
+ /* Get ipv6 header. */
+ ip6hdr = (struct ip6_hdr *)(q->p->payload);
+ /* Override ip6_current_dest_addr() so that we have an aligned copy. */
+ ip6_addr_set(ip6_current_dest_addr(), &(ip6hdr->dest));
+ /* send the queued IPv6 packet */
+ (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, q->p, ip6_current_dest_addr());
+ /* free the queued IP packet */
+ pbuf_free(q->p);
+ /* now queue entry can be freed */
+ memp_free(MEMP_ND6_QUEUE, q);
+ }
+#else /* LWIP_ND6_QUEUEING */
+ if (neighbor_cache[i].q != NULL) {
+ /* Get ipv6 header. */
+ ip6hdr = (struct ip6_hdr *)(neighbor_cache[i].q->payload);
+ /* Override ip6_current_dest_addr() so that we have an aligned copy. */
+ ip6_addr_set(ip6_current_dest_addr(), &(ip6hdr->dest));
+ /* send the queued IPv6 packet */
+ (neighbor_cache[i].netif)->output_ip6(neighbor_cache[i].netif, neighbor_cache[i].q, ip6_current_dest_addr());
+ /* free the queued IP packet */
+ pbuf_free(neighbor_cache[i].q);
+ neighbor_cache[i].q = NULL;
+ }
+#endif /* LWIP_ND6_QUEUEING */
+}
+
+
+/**
+ * Get the Path MTU for a destination.
+ *
+ * @param ip6addr the destination address
+ * @param netif the netif on which the packet will be sent
+ * @return the Path MTU, if known, or the netif default MTU
+ */
+u16_t
+nd6_get_destination_mtu(ip6_addr_t * ip6addr, struct netif * netif)
+{
+ s8_t i;
+
+ i = nd6_find_destination_cache_entry(ip6addr);
+ if (i >= 0) {
+ if (destination_cache[i].pmtu > 0) {
+ return destination_cache[i].pmtu;
+ }
+ }
+
+ if (netif != NULL) {
+ return netif->mtu;
+ }
+
+ return 1280; /* Minimum MTU */
+}
+
+
+#if LWIP_ND6_TCP_REACHABILITY_HINTS
+/**
+ * Provide the Neighbor discovery process with a hint that a
+ * destination is reachable. Called by tcp_receive when ACKs are
+ * received or sent (as per RFC). This is useful to avoid sending
+ * NS messages every 30 seconds.
+ *
+ * @param ip6addr the destination address which is know to be reachable
+ * by an upper layer protocol (TCP)
+ */
+void
+nd6_reachability_hint(ip6_addr_t * ip6addr)
+{
+ s8_t i;
+
+ /* Find destination in cache. */
+ if (ip6_addr_cmp(ip6addr, &(destination_cache[nd6_cached_destination_index].destination_addr))) {
+ i = nd6_cached_destination_index;
+ ND6_STATS_INC(nd6.cachehit);
+ }
+ else {
+ i = nd6_find_destination_cache_entry(ip6addr);
+ }
+ if (i < 0) {
+ return;
+ }
+
+ /* Find next hop neighbor in cache. */
+ if (ip6_addr_cmp(&(destination_cache[i].next_hop_addr), &(neighbor_cache[nd6_cached_neighbor_index].next_hop_address))) {
+ i = nd6_cached_neighbor_index;
+ ND6_STATS_INC(nd6.cachehit);
+ }
+ else {
+ i = nd6_find_neighbor_cache_entry(&(destination_cache[i].next_hop_addr));
+ }
+ if (i < 0) {
+ return;
+ }
+
+ /* Set reachability state. */
+ neighbor_cache[i].state = ND6_REACHABLE;
+ neighbor_cache[i].counter.reachable_time = reachable_time;
+}
+#endif /* LWIP_ND6_TCP_REACHABILITY_HINTS */
+
+#endif /* LWIP_IPV6 */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/mem.c b/src/VBox/Devices/Network/lwip-new/src/core/mem.c
new file mode 100644
index 00000000..1659a2c7
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/mem.c
@@ -0,0 +1,659 @@
+/**
+ * @file
+ * Dynamic memory manager
+ *
+ * This is a lightweight replacement for the standard C library malloc().
+ *
+ * If you want to use the standard C library malloc() instead, define
+ * MEM_LIBC_MALLOC to 1 in your lwipopts.h
+ *
+ * To let mem_malloc() use pools (prevents fragmentation and is much faster than
+ * a heap but might waste some memory), define MEM_USE_POOLS to 1, define
+ * MEM_USE_CUSTOM_POOLS to 1 and create a file "lwippools.h" that includes a list
+ * of pools like this (more pools can be added between _START and _END):
+ *
+ * Define three pools with sizes 256, 512, and 1512 bytes
+ * LWIP_MALLOC_MEMPOOL_START
+ * LWIP_MALLOC_MEMPOOL(20, 256)
+ * LWIP_MALLOC_MEMPOOL(10, 512)
+ * LWIP_MALLOC_MEMPOOL(5, 1512)
+ * LWIP_MALLOC_MEMPOOL_END
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ * Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if !MEM_LIBC_MALLOC /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/sys.h"
+#include "lwip/stats.h"
+#include "lwip/err.h"
+
+#include <string.h>
+
+#if MEM_USE_POOLS
+/* lwIP head implemented with different sized pools */
+
+/**
+ * Allocate memory: determine the smallest pool that is big enough
+ * to contain an element of 'size' and get an element from that pool.
+ *
+ * @param size the size in bytes of the memory needed
+ * @return a pointer to the allocated memory or NULL if the pool is empty
+ */
+void *
+mem_malloc(mem_size_t size)
+{
+ void *ret;
+ struct memp_malloc_helper *element;
+ memp_t poolnr;
+ mem_size_t required_size = size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper));
+
+ for (poolnr = MEMP_POOL_FIRST; poolnr <= MEMP_POOL_LAST; poolnr = (memp_t)(poolnr + 1)) {
+#if MEM_USE_POOLS_TRY_BIGGER_POOL
+again:
+#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
+ /* is this pool big enough to hold an element of the required size
+ plus a struct memp_malloc_helper that saves the pool this element came from? */
+ if (required_size <= memp_sizes[poolnr]) {
+ break;
+ }
+ }
+ if (poolnr > MEMP_POOL_LAST) {
+ LWIP_ASSERT("mem_malloc(): no pool is that big!", 0);
+ return NULL;
+ }
+ element = (struct memp_malloc_helper*)memp_malloc(poolnr);
+ if (element == NULL) {
+ /* No need to DEBUGF or ASSERT: This error is already
+ taken care of in memp.c */
+#if MEM_USE_POOLS_TRY_BIGGER_POOL
+ /** Try a bigger pool if this one is empty! */
+ if (poolnr < MEMP_POOL_LAST) {
+ poolnr++;
+ goto again;
+ }
+#endif /* MEM_USE_POOLS_TRY_BIGGER_POOL */
+ return NULL;
+ }
+
+ /* save the pool number this element came from */
+ element->poolnr = poolnr;
+ /* and return a pointer to the memory directly after the struct memp_malloc_helper */
+ ret = (u8_t*)element + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper));
+
+ return ret;
+}
+
+/**
+ * Free memory previously allocated by mem_malloc. Loads the pool number
+ * and calls memp_free with that pool number to put the element back into
+ * its pool
+ *
+ * @param rmem the memory element to free
+ */
+void
+mem_free(void *rmem)
+{
+ struct memp_malloc_helper *hmem;
+
+ LWIP_ASSERT("rmem != NULL", (rmem != NULL));
+ LWIP_ASSERT("rmem == MEM_ALIGN(rmem)", (rmem == LWIP_MEM_ALIGN(rmem)));
+
+ /* get the original struct memp_malloc_helper */
+ hmem = (struct memp_malloc_helper*)(void*)((u8_t*)rmem - LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper)));
+
+ LWIP_ASSERT("hmem != NULL", (hmem != NULL));
+ LWIP_ASSERT("hmem == MEM_ALIGN(hmem)", (hmem == LWIP_MEM_ALIGN(hmem)));
+ LWIP_ASSERT("hmem->poolnr < MEMP_MAX", (hmem->poolnr < MEMP_MAX));
+
+ /* and put it in the pool we saved earlier */
+ memp_free(hmem->poolnr, hmem);
+}
+
+#else /* MEM_USE_POOLS */
+/* lwIP replacement for your libc malloc() */
+
+/**
+ * The heap is made up as a list of structs of this type.
+ * This does not have to be aligned since for getting its size,
+ * we only use the macro SIZEOF_STRUCT_MEM, which automatically alignes.
+ */
+struct mem {
+ /** index (-> ram[next]) of the next struct */
+ mem_size_t next;
+ /** index (-> ram[prev]) of the previous struct */
+ mem_size_t prev;
+ /** 1: this area is used; 0: this area is unused */
+ u8_t used;
+};
+
+/** All allocated blocks will be MIN_SIZE bytes big, at least!
+ * MIN_SIZE can be overridden to suit your needs. Smaller values save space,
+ * larger values could prevent too small blocks to fragment the RAM too much. */
+#ifndef MIN_SIZE
+#define MIN_SIZE 12
+#endif /* MIN_SIZE */
+/* some alignment macros: we define them here for better source code layout */
+#define MIN_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MIN_SIZE)
+#define SIZEOF_STRUCT_MEM LWIP_MEM_ALIGN_SIZE(sizeof(struct mem))
+#define MEM_SIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEM_SIZE)
+
+/** If you want to relocate the heap to external memory, simply define
+ * LWIP_RAM_HEAP_POINTER as a void-pointer to that location.
+ * If so, make sure the memory at that location is big enough (see below on
+ * how that space is calculated). */
+#ifndef LWIP_RAM_HEAP_POINTER
+/** the heap. we need one struct mem at the end and some room for alignment */
+u8_t ram_heap[MEM_SIZE_ALIGNED + (2*SIZEOF_STRUCT_MEM) + MEM_ALIGNMENT];
+#define LWIP_RAM_HEAP_POINTER ram_heap
+#endif /* LWIP_RAM_HEAP_POINTER */
+
+/** pointer to the heap (ram_heap): for alignment, ram is now a pointer instead of an array */
+static u8_t *ram;
+/** the last entry, always unused! */
+static struct mem *ram_end;
+/** pointer to the lowest free block, this is used for faster search */
+static struct mem *lfree;
+
+/** concurrent access protection */
+#if !NO_SYS
+static sys_mutex_t mem_mutex;
+#endif
+
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+
+static volatile u8_t mem_free_count;
+
+/* Allow mem_free from other (e.g. interrupt) context */
+#define LWIP_MEM_FREE_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_free)
+#define LWIP_MEM_FREE_PROTECT() SYS_ARCH_PROTECT(lev_free)
+#define LWIP_MEM_FREE_UNPROTECT() SYS_ARCH_UNPROTECT(lev_free)
+#define LWIP_MEM_ALLOC_DECL_PROTECT() SYS_ARCH_DECL_PROTECT(lev_alloc)
+#define LWIP_MEM_ALLOC_PROTECT() SYS_ARCH_PROTECT(lev_alloc)
+#define LWIP_MEM_ALLOC_UNPROTECT() SYS_ARCH_UNPROTECT(lev_alloc)
+
+#else /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+/* Protect the heap only by using a semaphore */
+#define LWIP_MEM_FREE_DECL_PROTECT()
+#define LWIP_MEM_FREE_PROTECT() sys_mutex_lock(&mem_mutex)
+#define LWIP_MEM_FREE_UNPROTECT() sys_mutex_unlock(&mem_mutex)
+/* mem_malloc is protected using semaphore AND LWIP_MEM_ALLOC_PROTECT */
+#define LWIP_MEM_ALLOC_DECL_PROTECT()
+#define LWIP_MEM_ALLOC_PROTECT()
+#define LWIP_MEM_ALLOC_UNPROTECT()
+
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+
+/**
+ * "Plug holes" by combining adjacent empty struct mems.
+ * After this function is through, there should not exist
+ * one empty struct mem pointing to another empty struct mem.
+ *
+ * @param mem this points to a struct mem which just has been freed
+ * @internal this function is only called by mem_free() and mem_trim()
+ *
+ * This assumes access to the heap is protected by the calling function
+ * already.
+ */
+static void
+plug_holes(struct mem *mem)
+{
+ struct mem *nmem;
+ struct mem *pmem;
+
+ LWIP_ASSERT("plug_holes: mem >= ram", (u8_t *)mem >= ram);
+ LWIP_ASSERT("plug_holes: mem < ram_end", (u8_t *)mem < (u8_t *)ram_end);
+ LWIP_ASSERT("plug_holes: mem->used == 0", mem->used == 0);
+
+ /* plug hole forward */
+ LWIP_ASSERT("plug_holes: mem->next <= MEM_SIZE_ALIGNED", mem->next <= MEM_SIZE_ALIGNED);
+
+ nmem = (struct mem *)(void *)&ram[mem->next];
+ if (mem != nmem && nmem->used == 0 && (u8_t *)nmem != (u8_t *)ram_end) {
+ /* if mem->next is unused and not end of ram, combine mem and mem->next */
+ if (lfree == nmem) {
+ lfree = mem;
+ }
+ mem->next = nmem->next;
+ ((struct mem *)(void *)&ram[nmem->next])->prev = (mem_size_t)((u8_t *)mem - ram);
+ }
+
+ /* plug hole backward */
+ pmem = (struct mem *)(void *)&ram[mem->prev];
+ if (pmem != mem && pmem->used == 0) {
+ /* if mem->prev is unused, combine mem and mem->prev */
+ if (lfree == mem) {
+ lfree = pmem;
+ }
+ pmem->next = mem->next;
+ ((struct mem *)(void *)&ram[mem->next])->prev = (mem_size_t)((u8_t *)pmem - ram);
+ }
+}
+
+/**
+ * Zero the heap and initialize start, end and lowest-free
+ */
+void
+mem_init(void)
+{
+ struct mem *mem;
+
+ LWIP_ASSERT("Sanity check alignment",
+ (SIZEOF_STRUCT_MEM & (MEM_ALIGNMENT-1)) == 0);
+
+ /* align the heap */
+ ram = (u8_t *)LWIP_MEM_ALIGN(LWIP_RAM_HEAP_POINTER);
+ /* initialize the start of the heap */
+ mem = (struct mem *)(void *)ram;
+ mem->next = MEM_SIZE_ALIGNED;
+ mem->prev = 0;
+ mem->used = 0;
+ /* initialize the end of the heap */
+ ram_end = (struct mem *)(void *)&ram[MEM_SIZE_ALIGNED];
+ ram_end->used = 1;
+ ram_end->next = MEM_SIZE_ALIGNED;
+ ram_end->prev = MEM_SIZE_ALIGNED;
+
+ /* initialize the lowest-free pointer to the start of the heap */
+ lfree = (struct mem *)(void *)ram;
+
+ MEM_STATS_AVAIL(avail, MEM_SIZE_ALIGNED);
+
+ if(sys_mutex_new(&mem_mutex) != ERR_OK) {
+ LWIP_ASSERT("failed to create mem_mutex", 0);
+ }
+}
+
+/**
+ * Put a struct mem back on the heap
+ *
+ * @param rmem is the data portion of a struct mem as returned by a previous
+ * call to mem_malloc()
+ */
+void
+mem_free(void *rmem)
+{
+ struct mem *mem;
+ LWIP_MEM_FREE_DECL_PROTECT();
+
+ if (rmem == NULL) {
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("mem_free(p == NULL) was called.\n"));
+ return;
+ }
+ LWIP_ASSERT("mem_free: sanity check alignment", (((mem_ptr_t)rmem) & (MEM_ALIGNMENT-1)) == 0);
+
+ LWIP_ASSERT("mem_free: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
+ (u8_t *)rmem < (u8_t *)ram_end);
+
+ if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
+ SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_free: illegal memory\n"));
+ /* protect mem stats from concurrent access */
+ SYS_ARCH_PROTECT(lev);
+ MEM_STATS_INC(illegal);
+ SYS_ARCH_UNPROTECT(lev);
+ return;
+ }
+ /* protect the heap from concurrent access */
+ LWIP_MEM_FREE_PROTECT();
+ /* Get the corresponding struct mem ... */
+ mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
+ /* ... which has to be in a used state ... */
+ LWIP_ASSERT("mem_free: mem->used", mem->used);
+ /* ... and is now unused. */
+ mem->used = 0;
+
+ if (mem < lfree) {
+ /* the newly freed struct is now the lowest */
+ lfree = mem;
+ }
+
+ MEM_STATS_DEC_USED(used, mem->next - (mem_size_t)(((u8_t *)mem - ram)));
+
+ /* finally, see if prev or next are free also */
+ plug_holes(mem);
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ mem_free_count = 1;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_MEM_FREE_UNPROTECT();
+}
+
+/**
+ * Shrink memory returned by mem_malloc().
+ *
+ * @param rmem pointer to memory allocated by mem_malloc the is to be shrinked
+ * @param newsize required size after shrinking (needs to be smaller than or
+ * equal to the previous size)
+ * @return for compatibility reasons: is always == rmem, at the moment
+ * or NULL if newsize is > old size, in which case rmem is NOT touched
+ * or freed!
+ */
+void *
+mem_trim(void *rmem, mem_size_t newsize)
+{
+ mem_size_t size;
+ mem_size_t ptr, ptr2;
+ struct mem *mem, *mem2;
+ /* use the FREE_PROTECT here: it protects with sem OR SYS_ARCH_PROTECT */
+ LWIP_MEM_FREE_DECL_PROTECT();
+
+ /* Expand the size of the allocated memory region so that we can
+ adjust for alignment. */
+ newsize = LWIP_MEM_ALIGN_SIZE(newsize);
+
+ if(newsize < MIN_SIZE_ALIGNED) {
+ /* every data block must be at least MIN_SIZE_ALIGNED long */
+ newsize = MIN_SIZE_ALIGNED;
+ }
+
+ if (newsize > MEM_SIZE_ALIGNED) {
+ return NULL;
+ }
+
+ LWIP_ASSERT("mem_trim: legal memory", (u8_t *)rmem >= (u8_t *)ram &&
+ (u8_t *)rmem < (u8_t *)ram_end);
+
+ if ((u8_t *)rmem < (u8_t *)ram || (u8_t *)rmem >= (u8_t *)ram_end) {
+ SYS_ARCH_DECL_PROTECT(lev);
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SEVERE, ("mem_trim: illegal memory\n"));
+ /* protect mem stats from concurrent access */
+ SYS_ARCH_PROTECT(lev);
+ MEM_STATS_INC(illegal);
+ SYS_ARCH_UNPROTECT(lev);
+ return rmem;
+ }
+ /* Get the corresponding struct mem ... */
+ mem = (struct mem *)(void *)((u8_t *)rmem - SIZEOF_STRUCT_MEM);
+ /* ... and its offset pointer */
+ ptr = (mem_size_t)((u8_t *)mem - ram);
+
+ size = mem->next - ptr - SIZEOF_STRUCT_MEM;
+ LWIP_ASSERT("mem_trim can only shrink memory", newsize <= size);
+ if (newsize > size) {
+ /* not supported */
+ return NULL;
+ }
+ if (newsize == size) {
+ /* No change in size, simply return */
+ return rmem;
+ }
+
+ /* protect the heap from concurrent access */
+ LWIP_MEM_FREE_PROTECT();
+
+ mem2 = (struct mem *)(void *)&ram[mem->next];
+ if(mem2->used == 0) {
+ /* The next struct is unused, we can simply move it at little */
+ mem_size_t next;
+ /* remember the old next pointer */
+ next = mem2->next;
+ /* create new struct mem which is moved directly after the shrinked mem */
+ ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
+ if (lfree == mem2) {
+ lfree = (struct mem *)(void *)&ram[ptr2];
+ }
+ mem2 = (struct mem *)(void *)&ram[ptr2];
+ mem2->used = 0;
+ /* restore the next pointer */
+ mem2->next = next;
+ /* link it back to mem */
+ mem2->prev = ptr;
+ /* link mem to it */
+ mem->next = ptr2;
+ /* last thing to restore linked list: as we have moved mem2,
+ * let 'mem2->next->prev' point to mem2 again. but only if mem2->next is not
+ * the end of the heap */
+ if (mem2->next != MEM_SIZE_ALIGNED) {
+ ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+ }
+ MEM_STATS_DEC_USED(used, (size - newsize));
+ /* no need to plug holes, we've already done that */
+ } else if (newsize + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED <= size) {
+ /* Next struct is used but there's room for another struct mem with
+ * at least MIN_SIZE_ALIGNED of data.
+ * Old size ('size') must be big enough to contain at least 'newsize' plus a struct mem
+ * ('SIZEOF_STRUCT_MEM') with some data ('MIN_SIZE_ALIGNED').
+ * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
+ * region that couldn't hold data, but when mem->next gets freed,
+ * the 2 regions would be combined, resulting in more free memory */
+ ptr2 = ptr + SIZEOF_STRUCT_MEM + newsize;
+ mem2 = (struct mem *)(void *)&ram[ptr2];
+ if (mem2 < lfree) {
+ lfree = mem2;
+ }
+ mem2->used = 0;
+ mem2->next = mem->next;
+ mem2->prev = ptr;
+ mem->next = ptr2;
+ if (mem2->next != MEM_SIZE_ALIGNED) {
+ ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+ }
+ MEM_STATS_DEC_USED(used, (size - newsize));
+ /* the original mem->next is used, so no need to plug holes! */
+ }
+ /* else {
+ next struct mem is used but size between mem and mem2 is not big enough
+ to create another struct mem
+ -> don't do anyhting.
+ -> the remaining space stays unused since it is too small
+ } */
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ mem_free_count = 1;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_MEM_FREE_UNPROTECT();
+ return rmem;
+}
+
+/**
+ * Adam's mem_malloc() plus solution for bug #17922
+ * Allocate a block of memory with a minimum of 'size' bytes.
+ *
+ * @param size is the minimum size of the requested block in bytes.
+ * @return pointer to allocated memory or NULL if no free memory was found.
+ *
+ * Note that the returned value will always be aligned (as defined by MEM_ALIGNMENT).
+ */
+void *
+mem_malloc(mem_size_t size)
+{
+ mem_size_t ptr, ptr2;
+ struct mem *mem, *mem2;
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ u8_t local_mem_free_count = 0;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_MEM_ALLOC_DECL_PROTECT();
+
+ if (size == 0) {
+ return NULL;
+ }
+
+ /* Expand the size of the allocated memory region so that we can
+ adjust for alignment. */
+ size = LWIP_MEM_ALIGN_SIZE(size);
+
+ if(size < MIN_SIZE_ALIGNED) {
+ /* every data block must be at least MIN_SIZE_ALIGNED long */
+ size = MIN_SIZE_ALIGNED;
+ }
+
+ if (size > MEM_SIZE_ALIGNED) {
+ return NULL;
+ }
+
+ /* protect the heap from concurrent access */
+ sys_mutex_lock(&mem_mutex);
+ LWIP_MEM_ALLOC_PROTECT();
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ /* run as long as a mem_free disturbed mem_malloc or mem_trim */
+ do {
+ local_mem_free_count = 0;
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+ /* Scan through the heap searching for a free block that is big enough,
+ * beginning with the lowest free block.
+ */
+ for (ptr = (mem_size_t)((u8_t *)lfree - ram); ptr < MEM_SIZE_ALIGNED - size;
+ ptr = ((struct mem *)(void *)&ram[ptr])->next) {
+ mem = (struct mem *)(void *)&ram[ptr];
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ mem_free_count = 0;
+ LWIP_MEM_ALLOC_UNPROTECT();
+ /* allow mem_free or mem_trim to run */
+ LWIP_MEM_ALLOC_PROTECT();
+ if (mem_free_count != 0) {
+ /* If mem_free or mem_trim have run, we have to restart since they
+ could have altered our current struct mem. */
+ local_mem_free_count = 1;
+ break;
+ }
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+
+ if ((!mem->used) &&
+ (mem->next - (ptr + SIZEOF_STRUCT_MEM)) >= size) {
+ /* mem is not used and at least perfect fit is possible:
+ * mem->next - (ptr + SIZEOF_STRUCT_MEM) gives us the 'user data size' of mem */
+
+ if (mem->next - (ptr + SIZEOF_STRUCT_MEM) >= (size + SIZEOF_STRUCT_MEM + MIN_SIZE_ALIGNED)) {
+ /* (in addition to the above, we test if another struct mem (SIZEOF_STRUCT_MEM) containing
+ * at least MIN_SIZE_ALIGNED of data also fits in the 'user data space' of 'mem')
+ * -> split large block, create empty remainder,
+ * remainder must be large enough to contain MIN_SIZE_ALIGNED data: if
+ * mem->next - (ptr + (2*SIZEOF_STRUCT_MEM)) == size,
+ * struct mem would fit in but no data between mem2 and mem2->next
+ * @todo we could leave out MIN_SIZE_ALIGNED. We would create an empty
+ * region that couldn't hold data, but when mem->next gets freed,
+ * the 2 regions would be combined, resulting in more free memory
+ */
+ ptr2 = ptr + SIZEOF_STRUCT_MEM + size;
+ /* create mem2 struct */
+ mem2 = (struct mem *)(void *)&ram[ptr2];
+ mem2->used = 0;
+ mem2->next = mem->next;
+ mem2->prev = ptr;
+ /* and insert it between mem and mem->next */
+ mem->next = ptr2;
+ mem->used = 1;
+
+ if (mem2->next != MEM_SIZE_ALIGNED) {
+ ((struct mem *)(void *)&ram[mem2->next])->prev = ptr2;
+ }
+ MEM_STATS_INC_USED(used, (size + SIZEOF_STRUCT_MEM));
+ } else {
+ /* (a mem2 struct does no fit into the user data space of mem and mem->next will always
+ * be used at this point: if not we have 2 unused structs in a row, plug_holes should have
+ * take care of this).
+ * -> near fit or excact fit: do not split, no mem2 creation
+ * also can't move mem->next directly behind mem, since mem->next
+ * will always be used at this point!
+ */
+ mem->used = 1;
+ MEM_STATS_INC_USED(used, mem->next - (mem_size_t)((u8_t *)mem - ram));
+ }
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+mem_malloc_adjust_lfree:
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ if (mem == lfree) {
+ struct mem *cur = lfree;
+ /* Find next free block after mem and update lowest free pointer */
+ while (cur->used && cur != ram_end) {
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ mem_free_count = 0;
+ LWIP_MEM_ALLOC_UNPROTECT();
+ /* prevent high interrupt latency... */
+ LWIP_MEM_ALLOC_PROTECT();
+ if (mem_free_count != 0) {
+ /* If mem_free or mem_trim have run, we have to restart since they
+ could have altered our current struct mem or lfree. */
+ goto mem_malloc_adjust_lfree;
+ }
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ cur = (struct mem *)(void *)&ram[cur->next];
+ }
+ lfree = cur;
+ LWIP_ASSERT("mem_malloc: !lfree->used", ((lfree == ram_end) || (!lfree->used)));
+ }
+ LWIP_MEM_ALLOC_UNPROTECT();
+ sys_mutex_unlock(&mem_mutex);
+ LWIP_ASSERT("mem_malloc: allocated memory not above ram_end.",
+ (mem_ptr_t)mem + SIZEOF_STRUCT_MEM + size <= (mem_ptr_t)ram_end);
+ LWIP_ASSERT("mem_malloc: allocated memory properly aligned.",
+ ((mem_ptr_t)mem + SIZEOF_STRUCT_MEM) % MEM_ALIGNMENT == 0);
+ LWIP_ASSERT("mem_malloc: sanity check alignment",
+ (((mem_ptr_t)mem) & (MEM_ALIGNMENT-1)) == 0);
+
+ return (u8_t *)mem + SIZEOF_STRUCT_MEM;
+ }
+ }
+#if LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+ /* if we got interrupted by a mem_free, try again */
+ } while(local_mem_free_count != 0);
+#endif /* LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT */
+ LWIP_DEBUGF(MEM_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("mem_malloc: could not allocate %"S16_F" bytes\n", (s16_t)size));
+ MEM_STATS_INC(err);
+ LWIP_MEM_ALLOC_UNPROTECT();
+ sys_mutex_unlock(&mem_mutex);
+ return NULL;
+}
+
+#endif /* MEM_USE_POOLS */
+/**
+ * Contiguously allocates enough space for count objects that are size bytes
+ * of memory each and returns a pointer to the allocated memory.
+ *
+ * The allocated memory is filled with bytes of value zero.
+ *
+ * @param count number of objects to allocate
+ * @param size size of the objects to allocate
+ * @return pointer to allocated memory / NULL pointer if there is an error
+ */
+void *mem_calloc(mem_size_t count, mem_size_t size)
+{
+ void *p;
+
+ /* allocate 'count' objects of size 'size' */
+ p = mem_malloc(count * size);
+ if (p) {
+ /* zero the memory */
+ memset(p, 0, count * size);
+ }
+ return p;
+}
+
+#endif /* !MEM_LIBC_MALLOC */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/memp.c b/src/VBox/Devices/Network/lwip-new/src/core/memp.c
new file mode 100644
index 00000000..d2743736
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/memp.c
@@ -0,0 +1,543 @@
+/**
+ * @file
+ * Dynamic pool memory manager
+ *
+ * lwIP has dedicated pools for many structures (netconn, protocol control blocks,
+ * packet buffers, ...). All these pools are managed here.
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/udp.h"
+#include "lwip/raw.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/igmp.h"
+#include "lwip/api.h"
+#include "lwip/api_msg.h"
+#include "lwip/tcpip.h"
+#include "lwip/sys.h"
+#include "lwip/timers.h"
+#include "lwip/stats.h"
+#include "netif/etharp.h"
+#include "lwip/ip_frag.h"
+#include "lwip/snmp_structs.h"
+#include "lwip/snmp_msg.h"
+#include "lwip/dns.h"
+#include "netif/ppp_oe.h"
+#include "lwip/nd6.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 */
+
+#if !defined(WITH_VALGRIND)
+# ifdef VBOX /* don't make valgrind a compile-time dependency */
+# define WITH_VALGRIND 0
+# else
+# define WITH_VALGRIND 1
+# endif
+#endif
+
+/* Valgrind header is included but instrumentation may be disabled */
+#if WITH_VALGRIND
+# include "valgrind/memcheck.h"
+# ifdef NVALGRIND
+# define MEMP_USE_VALGRIND 0
+# else
+# define MEMP_USE_VALGRIND 1
+# endif
+#else /* !WITH_VALGRIND */
+# define MEMP_USE_VALGRIND 0
+#endif /* !WITH_VALGRIND */
+
+#if MEMP_USE_VALGRIND && MEMP_OVERFLOW_CHECK
+/* */
+#error Valgrind instrumentation is not compatible with MEMP_OVERFLOW_CHECK
+#endif
+
+
+struct memp {
+ struct memp *next;
+#if MEMP_OVERFLOW_CHECK
+ const char *file;
+ int line;
+#endif /* MEMP_OVERFLOW_CHECK */
+};
+
+#if MEMP_OVERFLOW_CHECK
+/* if MEMP_OVERFLOW_CHECK is turned on, we reserve some bytes at the beginning
+ * and at the end of each element, initialize them as 0xcd and check
+ * them later. */
+/* If MEMP_OVERFLOW_CHECK is >= 2, on every call to memp_malloc or memp_free,
+ * every single element in each pool is checked!
+ * This is VERY SLOW but also very helpful. */
+/* MEMP_SANITY_REGION_BEFORE and MEMP_SANITY_REGION_AFTER can be overridden in
+ * lwipopts.h to change the amount reserved for checking. */
+#ifndef MEMP_SANITY_REGION_BEFORE
+#define MEMP_SANITY_REGION_BEFORE 16
+#endif /* MEMP_SANITY_REGION_BEFORE*/
+#if MEMP_SANITY_REGION_BEFORE > 0
+#define MEMP_SANITY_REGION_BEFORE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_BEFORE)
+#else
+#define MEMP_SANITY_REGION_BEFORE_ALIGNED 0
+#endif /* MEMP_SANITY_REGION_BEFORE*/
+#ifndef MEMP_SANITY_REGION_AFTER
+#define MEMP_SANITY_REGION_AFTER 16
+#endif /* MEMP_SANITY_REGION_AFTER*/
+#if MEMP_SANITY_REGION_AFTER > 0
+#define MEMP_SANITY_REGION_AFTER_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_SANITY_REGION_AFTER)
+#else
+#define MEMP_SANITY_REGION_AFTER_ALIGNED 0
+#endif /* MEMP_SANITY_REGION_AFTER*/
+
+/* MEMP_SIZE: save space for struct memp and for sanity check */
+#define MEMP_SIZE (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEMP_SANITY_REGION_BEFORE_ALIGNED)
+#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x) + MEMP_SANITY_REGION_AFTER_ALIGNED)
+
+#else /* MEMP_OVERFLOW_CHECK */
+#if MEMP_USE_VALGRIND
+
+#ifndef MEMP_VALGRIND_RED_ZONE
+#define MEMP_VALGRIND_RED_ZONE 16
+#endif
+#if MEMP_VALGRIND_RED_ZONE > 0
+#define MEMP_VALGRIND_RED_ZONE_ALIGNED LWIP_MEM_ALIGN_SIZE(MEMP_VALGRIND_RED_ZONE)
+#else
+#define MEMP_VALGRIND_RED_ZONE_ALIGNED 0
+#endif
+
+/* MEMP_SIZE: save space for struct memp and for valgrind red zone */
+#define MEMP_SIZE (LWIP_MEM_ALIGN_SIZE(sizeof(struct memp)) + MEMP_VALGRIND_RED_ZONE_ALIGNED)
+#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x) + MEMP_VALGRIND_RED_ZONE_ALIGNED)
+
+#else /* no valgrind instrumentation */
+
+/* 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 /* no valgrind instrumentation */
+#endif /* MEMP_OVERFLOW_CHECK */
+
+/** This array holds the first free element of each pool.
+ * Elements form a linked list. */
+static struct memp *memp_tab[MEMP_MAX];
+
+#else /* MEMP_MEM_MALLOC */
+
+#define MEMP_ALIGN_SIZE(x) (LWIP_MEM_ALIGN_SIZE(x))
+
+#endif /* MEMP_MEM_MALLOC */
+
+/** This array holds the element sizes of each pool. */
+#if !MEM_USE_POOLS && !MEMP_MEM_MALLOC
+static
+#endif
+const u16_t memp_sizes[MEMP_MAX] = {
+#define LWIP_MEMPOOL(name,num,size,desc) LWIP_MEM_ALIGN_SIZE(size),
+#include "lwip/memp_std.h"
+};
+
+#if !MEMP_MEM_MALLOC /* don't build if not configured for use in lwipopts.h */
+
+/** This array holds the number of elements in each pool. */
+static const u16_t memp_num[MEMP_MAX] = {
+#define LWIP_MEMPOOL(name,num,size,desc) (num),
+#include "lwip/memp_std.h"
+};
+
+/** This array holds a textual description of each pool. */
+#ifdef LWIP_DEBUG
+static const char *memp_desc[MEMP_MAX] = {
+#define LWIP_MEMPOOL(name,num,size,desc) (desc),
+#include "lwip/memp_std.h"
+};
+#endif /* LWIP_DEBUG */
+
+#if MEMP_SEPARATE_POOLS
+
+/** This creates each memory pool. These are named memp_memory_XXX_base (where
+ * XXX is the name of the pool defined in memp_std.h).
+ * To relocate a pool, declare it as extern in cc.h. Example for GCC:
+ * extern u8_t __attribute__((section(".onchip_mem"))) memp_memory_UDP_PCB_base[];
+ */
+#define LWIP_MEMPOOL(name,num,size,desc) u8_t memp_memory_ ## name ## _base \
+ [LWIP_MEM_ALIGN_BUFFER((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 /* XXX: LWIP_MEM_ALIGN_BUFFER */
+#define LWIP_MEMPOOL(name,num,size,desc) + ( (num) * (MEMP_SIZE + MEMP_ALIGN_SIZE(size) ) )
+#include "lwip/memp_std.h"
+];
+
+#endif /* MEMP_SEPARATE_POOLS */
+
+#if MEMP_SANITY_CHECK
+/**
+ * Check that memp-lists don't form a circle, using "Floyd's cycle-finding algorithm".
+ */
+static int
+memp_sanity(void)
+{
+ s16_t i;
+ struct memp *t, *h;
+
+ for (i = 0; i < MEMP_MAX; i++) {
+ t = memp_tab[i];
+ if(t != NULL) {
+ for (h = t->next; (t != NULL) && (h != NULL); t = t->next,
+ h = (((h->next != NULL) && (h->next->next != NULL)) ? h->next->next : NULL)) {
+ if (t == h) {
+ return 0;
+ }
+ }
+ }
+ }
+ return 1;
+}
+#endif /* MEMP_SANITY_CHECK*/
+#if MEMP_OVERFLOW_CHECK
+#if defined(LWIP_DEBUG) && MEMP_STATS
+static const char * memp_overflow_names[] = {
+#define LWIP_MEMPOOL(name,num,size,desc) "/"desc,
+#include "lwip/memp_std.h"
+ };
+#endif
+
+/**
+ * Check if a memp element was victim of an overflow
+ * (e.g. the restricted area after it has been altered)
+ *
+ * @param p the memp element to check
+ * @param memp_type the pool p comes from
+ */
+static void
+memp_overflow_check_element_overflow(struct memp *p, u16_t memp_type)
+{
+ u16_t k;
+ u8_t *m;
+#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE + memp_sizes[memp_type];
+ for (k = 0; k < MEMP_SANITY_REGION_AFTER_ALIGNED; k++) {
+ if (m[k] != 0xcd) {
+ char errstr[128] = "detected memp overflow in pool ";
+ char digit[] = "0";
+ if(memp_type >= 10) {
+ digit[0] = '0' + (memp_type/10);
+ strcat(errstr, digit);
+ }
+ digit[0] = '0' + (memp_type%10);
+ strcat(errstr, digit);
+#if defined(LWIP_DEBUG) && MEMP_STATS
+ strcat(errstr, memp_overflow_names[memp_type]);
+#endif
+ LWIP_ASSERT(errstr, 0);
+ }
+ }
+#endif
+}
+
+/**
+ * Check if a memp element was victim of an underflow
+ * (e.g. the restricted area before it has been altered)
+ *
+ * @param p the memp element to check
+ * @param memp_type the pool p comes from
+ */
+static void
+memp_overflow_check_element_underflow(struct memp *p, u16_t memp_type)
+{
+ u16_t k;
+ u8_t *m;
+#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
+ for (k = 0; k < MEMP_SANITY_REGION_BEFORE_ALIGNED; k++) {
+ if (m[k] != 0xcd) {
+ char errstr[128] = "detected memp underflow in pool ";
+ char digit[] = "0";
+ if(memp_type >= 10) {
+ digit[0] = '0' + (memp_type/10);
+ strcat(errstr, digit);
+ }
+ digit[0] = '0' + (memp_type%10);
+ strcat(errstr, digit);
+#if defined(LWIP_DEBUG) && MEMP_STATS
+ strcat(errstr, memp_overflow_names[memp_type]);
+#endif
+ LWIP_ASSERT(errstr, 0);
+ }
+ }
+#endif
+}
+
+/**
+ * Do an overflow check for all elements in every pool.
+ *
+ * @see memp_overflow_check_element for a description of the check
+ */
+static void
+memp_overflow_check_all(void)
+{
+ u16_t i, j;
+ struct memp *p;
+
+#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 *)LWIP_MEM_ALIGN(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 *)LWIP_MEM_ALIGN(memp_bases[i]);
+#endif /* MEMP_SEPARATE_POOLS */
+ for (j = 0; j < memp_num[i]; ++j) {
+ memp_overflow_check_element_underflow(p, i);
+ p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED);
+ }
+ }
+}
+
+/**
+ * Initialize the restricted areas of all memp elements in every pool.
+ */
+static void
+memp_overflow_init(void)
+{
+ u16_t i, j;
+ struct memp *p;
+ u8_t *m;
+
+#if !MEMP_SEPARATE_POOLS
+ p = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
+#endif /* !MEMP_SEPARATE_POOLS */
+ for (i = 0; i < MEMP_MAX; ++i) {
+#if MEMP_SEPARATE_POOLS
+ p = (struct memp *)LWIP_MEM_ALIGN(memp_bases[i]);
+#endif /* MEMP_SEPARATE_POOLS */
+ for (j = 0; j < memp_num[i]; ++j) {
+#if MEMP_SANITY_REGION_BEFORE_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE - MEMP_SANITY_REGION_BEFORE_ALIGNED;
+ memset(m, 0xcd, MEMP_SANITY_REGION_BEFORE_ALIGNED);
+#endif
+#if MEMP_SANITY_REGION_AFTER_ALIGNED > 0
+ m = (u8_t*)p + MEMP_SIZE + memp_sizes[i];
+ memset(m, 0xcd, MEMP_SANITY_REGION_AFTER_ALIGNED);
+#endif
+ p = (struct memp*)((u8_t*)p + MEMP_SIZE + memp_sizes[i] + MEMP_SANITY_REGION_AFTER_ALIGNED);
+ }
+ }
+}
+#endif /* MEMP_OVERFLOW_CHECK */
+
+/**
+ * Initialize this module.
+ *
+ * Carves out memp_memory into linked lists for each pool-type.
+ */
+void
+memp_init(void)
+{
+ struct memp *memp;
+ u16_t i, j;
+
+ for (i = 0; i < MEMP_MAX; ++i) {
+ MEMP_STATS_AVAIL(used, i, 0);
+ MEMP_STATS_AVAIL(max, i, 0);
+ MEMP_STATS_AVAIL(err, i, 0);
+ MEMP_STATS_AVAIL(avail, i, memp_num[i]);
+ }
+
+#if !MEMP_SEPARATE_POOLS
+ memp = (struct memp *)LWIP_MEM_ALIGN(memp_memory);
+#endif /* !MEMP_SEPARATE_POOLS */
+ /* for every pool: */
+ for (i = 0; i < MEMP_MAX; ++i) {
+ memp_tab[i] = NULL;
+#if MEMP_USE_VALGRIND
+ VALGRIND_CREATE_MEMPOOL(&memp_tab[i], MEMP_VALGRIND_RED_ZONE, 0);
+#endif
+#if MEMP_SEPARATE_POOLS
+ memp = (struct memp*)LWIP_MEM_ALIGN(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;
+#if MEMP_USE_VALGRIND
+ (void) VALGRIND_MAKE_MEM_NOACCESS((u8_t *)memp + MEMP_SIZE, memp_sizes[i]);
+#endif
+ memp = (struct memp *)(void *)((u8_t *)memp + MEMP_SIZE + memp_sizes[i]
+#if MEMP_OVERFLOW_CHECK
+ + MEMP_SANITY_REGION_AFTER_ALIGNED
+#endif
+#if MEMP_USE_VALGRIND
+ + MEMP_VALGRIND_RED_ZONE_ALIGNED
+#endif
+ );
+ }
+ }
+#if MEMP_OVERFLOW_CHECK
+ memp_overflow_init();
+ /* check everything a first time to see if it worked */
+ memp_overflow_check_all();
+#endif /* MEMP_OVERFLOW_CHECK */
+}
+
+/**
+ * Get an element from a specific pool.
+ *
+ * @param type the pool to get an element from
+ *
+ * the debug version has two more parameters:
+ * @param file file name calling this function
+ * @param line number of line where this function is called
+ *
+ * @return a pointer to the allocated memory or a NULL pointer on error
+ */
+void *
+#if !MEMP_OVERFLOW_CHECK
+memp_malloc(memp_t type)
+#else
+memp_malloc_fn(memp_t type, const char* file, const int line)
+#endif
+{
+ struct memp *memp;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+ LWIP_ERROR("memp_malloc: type < MEMP_MAX", (type < MEMP_MAX), return NULL;);
+
+ SYS_ARCH_PROTECT(old_level);
+#if MEMP_OVERFLOW_CHECK >= 2
+ memp_overflow_check_all();
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+
+ memp = memp_tab[type];
+
+ if (memp != NULL) {
+ memp_tab[type] = memp->next;
+#if MEMP_OVERFLOW_CHECK
+ memp->next = NULL;
+ memp->file = file;
+ memp->line = line;
+#endif /* MEMP_OVERFLOW_CHECK */
+ MEMP_STATS_INC_USED(used, type);
+ LWIP_ASSERT("memp_malloc: memp properly aligned",
+ ((mem_ptr_t)memp % MEM_ALIGNMENT) == 0);
+ memp = (struct memp*)(void *)((u8_t*)memp + MEMP_SIZE);
+#if MEMP_USE_VALGRIND
+ VALGRIND_MEMPOOL_ALLOC(&memp_tab[type], memp, memp_sizes[type]);
+#endif
+ } else {
+ LWIP_DEBUGF(MEMP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("memp_malloc: out of memory in pool %s\n", memp_desc[type]));
+ MEMP_STATS_INC(err, type);
+ }
+
+ SYS_ARCH_UNPROTECT(old_level);
+
+ return memp;
+}
+
+/**
+ * Put an element back into its pool.
+ *
+ * @param type the pool where to put mem
+ * @param mem the memp element to free
+ */
+void
+memp_free(memp_t type, void *mem)
+{
+ struct memp *memp;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+ if (mem == NULL) {
+ return;
+ }
+ LWIP_ASSERT("memp_free: mem properly aligned",
+ ((mem_ptr_t)mem % MEM_ALIGNMENT) == 0);
+
+#if MEMP_USE_VALGRIND
+ VALGRIND_MEMPOOL_FREE(&memp_tab[type], mem);
+#endif
+ memp = (struct memp *)(void *)((u8_t*)mem - MEMP_SIZE);
+
+ SYS_ARCH_PROTECT(old_level);
+#if MEMP_OVERFLOW_CHECK
+#if MEMP_OVERFLOW_CHECK >= 2
+ memp_overflow_check_all();
+#else
+ memp_overflow_check_element_overflow(memp, type);
+ memp_overflow_check_element_underflow(memp, type);
+#endif /* MEMP_OVERFLOW_CHECK >= 2 */
+#endif /* MEMP_OVERFLOW_CHECK */
+
+ MEMP_STATS_DEC(used, type);
+
+ memp->next = memp_tab[type];
+ memp_tab[type] = memp;
+
+#if MEMP_SANITY_CHECK
+ LWIP_ASSERT("memp sanity", memp_sanity());
+#endif /* MEMP_SANITY_CHECK */
+
+ SYS_ARCH_UNPROTECT(old_level);
+}
+
+#endif /* MEMP_MEM_MALLOC */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/netif.c b/src/VBox/Devices/Network/lwip-new/src/core/netif.c
new file mode 100644
index 00000000..f88486e8
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/netif.c
@@ -0,0 +1,943 @@
+/**
+ * @file
+ * lwIP network interface abstraction
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/netif.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/snmp.h"
+#include "lwip/igmp.h"
+#include "netif/etharp.h"
+#include "lwip/stats.h"
+#if ENABLE_LOOPBACK
+#include "lwip/sys.h"
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+#include "lwip/tcpip.h"
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
+
+#if LWIP_AUTOIP
+#include "lwip/autoip.h"
+#endif /* LWIP_AUTOIP */
+#if LWIP_DHCP
+#include "lwip/dhcp.h"
+#endif /* LWIP_DHCP */
+#if LWIP_IPV6_DHCP6
+#include "lwip/dhcp6.h"
+#endif /* LWIP_IPV6_DHCP6 */
+#if LWIP_IPV6_MLD
+#include "lwip/mld6.h"
+#endif /* LWIP_IPV6_MLD */
+
+#if LWIP_NETIF_STATUS_CALLBACK
+#define NETIF_STATUS_CALLBACK(n) do{ if (n->status_callback) { (n->status_callback)(n); }}while(0)
+#else
+#define NETIF_STATUS_CALLBACK(n)
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+
+#if LWIP_NETIF_LINK_CALLBACK
+#define NETIF_LINK_CALLBACK(n) do{ if (n->link_callback) { (n->link_callback)(n); }}while(0)
+#else
+#define NETIF_LINK_CALLBACK(n)
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+struct netif *netif_list;
+struct netif *netif_default;
+
+static u8_t netif_num;
+
+#if LWIP_IPV6
+static err_t netif_null_output_ip6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr);
+#endif /* LWIP_IPV6 */
+
+#if LWIP_IPV6
+#define ipX_input(in, netif) (IP6H_V((const struct ip6_hdr *)in->payload) == 6) ? ip6_input(in, netif) : ip_input(in, netif)
+#else
+#define ipX_input(in, netif) ip_input(in, netif)
+#endif
+
+#if LWIP_HAVE_LOOPIF
+static err_t netif_loop_output_ipv4(struct netif *netif, struct pbuf *p, ip_addr_t* addr);
+#if LWIP_IPV6
+static err_t netif_loop_output_ipv6(struct netif *netif, struct pbuf *p, ip6_addr_t* addr);
+#endif
+
+
+static struct netif loop_netif;
+
+/**
+ * Initialize a lwip network interface structure for a loopback interface
+ *
+ * @param netif the lwip network interface structure for this loopif
+ * @return ERR_OK if the loopif is initialized
+ * ERR_MEM if private data couldn't be allocated
+ */
+static err_t
+netif_loopif_init(struct netif *netif)
+{
+ /* initialize the snmp variables and counters inside the struct netif
+ * ifSpeed: no assumption can be made!
+ */
+ NETIF_INIT_SNMP(netif, snmp_ifType_softwareLoopback, 0);
+
+ netif->name[0] = 'l';
+ netif->name[1] = 'o';
+ netif->output = netif_loop_output_ipv4;
+#if LWIP_IPV6
+ netif->output_ip6 = netif_loop_output_ipv6;
+#endif
+ return ERR_OK;
+}
+#endif /* LWIP_HAVE_LOOPIF */
+
+void
+netif_init(void)
+{
+#if LWIP_HAVE_LOOPIF
+ ip_addr_t loop_ipaddr, loop_netmask, loop_gw;
+ IP4_ADDR(&loop_gw, 127,0,0,1);
+ IP4_ADDR(&loop_ipaddr, 127,0,0,1);
+ IP4_ADDR(&loop_netmask, 255,0,0,0);
+
+#if NO_SYS
+ netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, ip_input);
+#else /* NO_SYS */
+ netif_add(&loop_netif, &loop_ipaddr, &loop_netmask, &loop_gw, NULL, netif_loopif_init, tcpip_input);
+#endif /* NO_SYS */
+
+#if LWIP_IPV6
+ loop_netif.ip6_addr[0].addr[0] = 0;
+ loop_netif.ip6_addr[0].addr[1] = 0;
+ loop_netif.ip6_addr[0].addr[2] = 0;
+ loop_netif.ip6_addr[0].addr[3] = PP_HTONL(0x00000001UL);
+ loop_netif.ip6_addr_state[0] = IP6_ADDR_VALID;
+#endif /* LWIP_IPV6 */
+
+ netif_set_up(&loop_netif);
+
+#endif /* LWIP_HAVE_LOOPIF */
+}
+
+/**
+ * Add a network interface to the list of lwIP netifs.
+ *
+ * @param netif a pre-allocated netif structure
+ * @param ipaddr IP address for the new netif
+ * @param netmask network mask for the new netif
+ * @param gw default gateway IP address for the new netif
+ * @param state opaque data passed to the new netif
+ * @param init callback function that initializes the interface
+ * @param input callback function that is called to pass
+ * ingress packets up in the protocol layer stack.
+ *
+ * @return netif, or NULL if failed.
+ */
+struct netif *
+netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
+ ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input)
+{
+#if LWIP_IPV6
+ u32_t i;
+#endif
+
+ LWIP_ASSERT("No init function given", init != NULL);
+
+ /* reset new interface configuration state */
+ ip_addr_set_zero(&netif->ip_addr);
+ ip_addr_set_zero(&netif->netmask);
+ ip_addr_set_zero(&netif->gw);
+#if LWIP_IPV6
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ ip6_addr_set_zero(&netif->ip6_addr[i]);
+ netif_ip6_addr_set_state(netif, i, IP6_ADDR_INVALID);
+ }
+ netif->output_ip6 = netif_null_output_ip6;
+#endif /* LWIP_IPV6 */
+ netif->flags = 0;
+#if LWIP_DHCP
+ /* netif not under DHCP control by default */
+ netif->dhcp = NULL;
+#endif /* LWIP_DHCP */
+#if LWIP_AUTOIP
+ /* netif not under AutoIP control by default */
+ netif->autoip = NULL;
+#endif /* LWIP_AUTOIP */
+#if LWIP_IPV6_AUTOCONFIG
+ /* IPv6 address autoconfiguration not enabled by default */
+ netif->ip6_autoconfig_enabled = 0;
+#endif /* LWIP_IPV6_AUTOCONFIG */
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+ netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT;
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+#if LWIP_IPV6_DHCP6
+ /* netif not under DHCPv6 control by default */
+ netif->dhcp6 = NULL;
+#endif /* LWIP_IPV6_DHCP6 */
+#if LWIP_NETIF_STATUS_CALLBACK
+ netif->status_callback = NULL;
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#if LWIP_NETIF_LINK_CALLBACK
+ netif->link_callback = NULL;
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+#if LWIP_IGMP
+ netif->igmp_mac_filter = NULL;
+#endif /* LWIP_IGMP */
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+ netif->mld_mac_filter = NULL;
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+#if ENABLE_LOOPBACK
+ netif->loop_first = NULL;
+ netif->loop_last = NULL;
+#endif /* ENABLE_LOOPBACK */
+
+ /* remember netif specific state information data */
+ netif->state = state;
+ netif->num = netif_num++;
+ netif->input = input;
+ NETIF_SET_HWADDRHINT(netif, NULL);
+#if ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS
+ netif->loop_cnt_current = 0;
+#endif /* ENABLE_LOOPBACK && LWIP_LOOPBACK_MAX_PBUFS */
+
+ netif_set_addr(netif, ipaddr, netmask, gw);
+
+ /* call user specified initialization function for netif */
+ if (init(netif) != ERR_OK) {
+ return NULL;
+ }
+
+ /* add this netif to the list */
+ netif->next = netif_list;
+ netif_list = netif;
+ snmp_inc_iflist();
+
+#if LWIP_IGMP
+ /* start IGMP processing */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_start(netif);
+ }
+#endif /* LWIP_IGMP */
+
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif: added interface %c%c IP addr ",
+ netif->name[0], netif->name[1]));
+ ip_addr_debug_print(NETIF_DEBUG, ipaddr);
+ LWIP_DEBUGF(NETIF_DEBUG, (" netmask "));
+ ip_addr_debug_print(NETIF_DEBUG, netmask);
+ LWIP_DEBUGF(NETIF_DEBUG, (" gw "));
+ ip_addr_debug_print(NETIF_DEBUG, gw);
+ LWIP_DEBUGF(NETIF_DEBUG, ("\n"));
+ return netif;
+}
+
+/**
+ * Change IP address configuration for a network interface (including netmask
+ * and default gateway).
+ *
+ * @param netif the network interface to change
+ * @param ipaddr the new IP address
+ * @param netmask the new netmask
+ * @param gw the new default gateway
+ */
+void
+netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
+ ip_addr_t *gw)
+{
+ netif_set_ipaddr(netif, ipaddr);
+ netif_set_netmask(netif, netmask);
+ netif_set_gw(netif, gw);
+}
+
+/**
+ * Remove a network interface from the list of lwIP netifs.
+ *
+ * @param netif the network interface to remove
+ */
+void
+netif_remove(struct netif *netif)
+{
+ if (netif == NULL) {
+ return;
+ }
+
+#if LWIP_IGMP
+ /* stop IGMP processing */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_stop(netif);
+ }
+#endif /* LWIP_IGMP */
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+ /* stop MLD processing */
+ mld6_stop(netif);
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+ if (netif_is_up(netif)) {
+ /* set netif down before removing (call callback function) */
+ netif_set_down(netif);
+ }
+
+ snmp_delete_ipaddridx_tree(netif);
+
+ /* is it the first netif? */
+ if (netif_list == netif) {
+ netif_list = netif->next;
+ } else {
+ /* look for netif further down the list */
+ struct netif * tmpNetif;
+ for (tmpNetif = netif_list; tmpNetif != NULL; tmpNetif = tmpNetif->next) {
+ if (tmpNetif->next == netif) {
+ tmpNetif->next = netif->next;
+ break;
+ }
+ }
+ if (tmpNetif == NULL)
+ return; /* we didn't find any netif today */
+ }
+ snmp_dec_iflist();
+ /* this netif is default? */
+ if (netif_default == netif) {
+ /* reset default netif */
+ netif_set_default(NULL);
+ }
+#if LWIP_NETIF_REMOVE_CALLBACK
+ if (netif->remove_callback) {
+ netif->remove_callback(netif);
+ }
+#endif /* LWIP_NETIF_REMOVE_CALLBACK */
+ LWIP_DEBUGF( NETIF_DEBUG, ("netif_remove: removed netif\n") );
+}
+
+/**
+ * Find a network interface by searching for its name
+ *
+ * @param name the name of the netif (like netif->name) plus concatenated number
+ * in ascii representation (e.g. 'en0')
+ */
+struct netif *
+netif_find(char *name)
+{
+ struct netif *netif;
+ u8_t num;
+
+ if (name == NULL) {
+ return NULL;
+ }
+
+ num = name[2] - '0';
+
+ for(netif = netif_list; netif != NULL; netif = netif->next) {
+ if (num == netif->num &&
+ name[0] == netif->name[0] &&
+ name[1] == netif->name[1]) {
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: found %c%c\n", name[0], name[1]));
+ return netif;
+ }
+ }
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif_find: didn't find %c%c\n", name[0], name[1]));
+ return NULL;
+}
+
+/**
+ * Change the IP address of a network interface
+ *
+ * @param netif the network interface to change
+ * @param ipaddr the new IP address
+ *
+ * @note call netif_set_addr() if you also want to change netmask and
+ * default gateway
+ */
+void
+netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr)
+{
+ /* TODO: Handling of obsolete pcbs */
+ /* See: http://mail.gnu.org/archive/html/lwip-users/2003-03/msg00118.html */
+#if LWIP_TCP
+ struct tcp_pcb *pcb;
+ struct tcp_pcb_listen *lpcb;
+
+ /* address is actually being changed? */
+ if (ipaddr && (ip_addr_cmp(ipaddr, &(netif->ip_addr))) == 0) {
+ /* extern struct tcp_pcb *tcp_active_pcbs; defined by tcp.h */
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: netif address being changed\n"));
+ pcb = tcp_active_pcbs;
+ while (pcb != NULL) {
+ /* PCB bound to current local interface address? */
+ if (ip_addr_cmp(ipX_2_ip(&pcb->local_ip), &(netif->ip_addr))
+#if LWIP_AUTOIP
+ /* connections to link-local addresses must persist (RFC3927 ch. 1.9) */
+ && !ip_addr_islinklocal(ipX_2_ip(&pcb->local_ip))
+#endif /* LWIP_AUTOIP */
+ ) {
+ /* this connection must be aborted */
+ struct tcp_pcb *next = pcb->next;
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_STATE, ("netif_set_ipaddr: aborting TCP pcb %p\n", (void *)pcb));
+ tcp_abort(pcb);
+ pcb = next;
+ } else {
+ pcb = pcb->next;
+ }
+ }
+ for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+ /* PCB bound to current local interface address? */
+ if ((!(ip_addr_isany(ipX_2_ip(&lpcb->local_ip)))) &&
+ (ip_addr_cmp(ipX_2_ip(&lpcb->local_ip), &(netif->ip_addr)))) {
+ /* The PCB is listening to the old ipaddr and
+ * is set to listen to the new one instead */
+ ip_addr_set(ipX_2_ip(&lpcb->local_ip), ipaddr);
+ }
+ }
+ }
+#endif
+ snmp_delete_ipaddridx_tree(netif);
+ snmp_delete_iprteidx_tree(0,netif);
+ /* set new IP address to netif */
+ ip_addr_set(&(netif->ip_addr), ipaddr);
+ snmp_insert_ipaddridx_tree(netif);
+ snmp_insert_iprteidx_tree(0,netif);
+
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: IP address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ netif->name[0], netif->name[1],
+ ip4_addr1_16(&netif->ip_addr),
+ ip4_addr2_16(&netif->ip_addr),
+ ip4_addr3_16(&netif->ip_addr),
+ ip4_addr4_16(&netif->ip_addr)));
+}
+
+/**
+ * Change the default gateway for a network interface
+ *
+ * @param netif the network interface to change
+ * @param gw the new default gateway
+ *
+ * @note call netif_set_addr() if you also want to change ip address and netmask
+ */
+void
+netif_set_gw(struct netif *netif, ip_addr_t *gw)
+{
+ ip_addr_set(&(netif->gw), gw);
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: GW address of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ netif->name[0], netif->name[1],
+ ip4_addr1_16(&netif->gw),
+ ip4_addr2_16(&netif->gw),
+ ip4_addr3_16(&netif->gw),
+ ip4_addr4_16(&netif->gw)));
+}
+
+/**
+ * Change the netmask of a network interface
+ *
+ * @param netif the network interface to change
+ * @param netmask the new netmask
+ *
+ * @note call netif_set_addr() if you also want to change ip address and
+ * default gateway
+ */
+void
+netif_set_netmask(struct netif *netif, ip_addr_t *netmask)
+{
+ snmp_delete_iprteidx_tree(0, netif);
+ /* set new netmask to netif */
+ ip_addr_set(&(netif->netmask), netmask);
+ snmp_insert_iprteidx_tree(0, netif);
+ LWIP_DEBUGF(NETIF_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("netif: netmask of interface %c%c set to %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ netif->name[0], netif->name[1],
+ ip4_addr1_16(&netif->netmask),
+ ip4_addr2_16(&netif->netmask),
+ ip4_addr3_16(&netif->netmask),
+ ip4_addr4_16(&netif->netmask)));
+}
+
+/**
+ * Set a network interface as the default network interface
+ * (used to output all packets for which no specific route is found)
+ *
+ * @param netif the default network interface
+ */
+void
+netif_set_default(struct netif *netif)
+{
+ if (netif == NULL) {
+ /* remove default route */
+ snmp_delete_iprteidx_tree(1, netif);
+ } else {
+ /* install default route */
+ snmp_insert_iprteidx_tree(1, netif);
+ }
+ netif_default = netif;
+ LWIP_DEBUGF(NETIF_DEBUG, ("netif: setting default interface %c%c\n",
+ netif ? netif->name[0] : '\'', netif ? netif->name[1] : '\''));
+}
+
+/**
+ * Bring an interface up, available for processing
+ * traffic.
+ *
+ * @note: Enabling DHCP on a down interface will make it come
+ * up once configured.
+ *
+ * @see dhcp_start()
+ */
+void netif_set_up(struct netif *netif)
+{
+ if (!(netif->flags & NETIF_FLAG_UP)) {
+ netif->flags |= NETIF_FLAG_UP;
+
+#if LWIP_SNMP
+ snmp_get_sysuptime(&netif->ts);
+#endif /* LWIP_SNMP */
+
+ NETIF_STATUS_CALLBACK(netif);
+
+ if (netif->flags & NETIF_FLAG_LINK_UP) {
+#if LWIP_ARP
+ /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */
+ if (netif->flags & (NETIF_FLAG_ETHARP)) {
+ etharp_gratuitous(netif);
+ }
+#endif /* LWIP_ARP */
+
+#if LWIP_IGMP
+ /* resend IGMP memberships */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_report_groups( netif);
+ }
+#endif /* LWIP_IGMP */
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+ /* send mld memberships */
+ mld6_report_groups( netif);
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+
+#if LWIP_IPV6_SEND_ROUTER_SOLICIT
+ /* Send Router Solicitation messages. */
+ netif->rs_count = LWIP_ND6_MAX_MULTICAST_SOLICIT;
+#endif /* LWIP_IPV6_SEND_ROUTER_SOLICIT */
+
+ }
+ }
+}
+
+/**
+ * Bring an interface down, disabling any traffic processing.
+ *
+ * @note: Enabling DHCP on a down interface will make it come
+ * up once configured.
+ *
+ * @see dhcp_start()
+ */
+void netif_set_down(struct netif *netif)
+{
+ if (netif->flags & NETIF_FLAG_UP) {
+ netif->flags &= ~NETIF_FLAG_UP;
+#if LWIP_SNMP
+ snmp_get_sysuptime(&netif->ts);
+#endif
+
+#if LWIP_ARP
+ if (netif->flags & NETIF_FLAG_ETHARP) {
+ etharp_cleanup_netif(netif);
+ }
+#endif /* LWIP_ARP */
+ NETIF_STATUS_CALLBACK(netif);
+ }
+}
+
+#if LWIP_NETIF_STATUS_CALLBACK
+/**
+ * Set callback to be called when interface is brought up/down
+ */
+void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback)
+{
+ if (netif) {
+ netif->status_callback = status_callback;
+ }
+}
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+
+#if LWIP_NETIF_REMOVE_CALLBACK
+/**
+ * Set callback to be called when the interface has been removed
+ */
+void
+netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback)
+{
+ if (netif) {
+ netif->remove_callback = remove_callback;
+ }
+}
+#endif /* LWIP_NETIF_REMOVE_CALLBACK */
+
+/**
+ * Called by a driver when its link goes up
+ */
+void netif_set_link_up(struct netif *netif )
+{
+ if (!(netif->flags & NETIF_FLAG_LINK_UP)) {
+ netif->flags |= NETIF_FLAG_LINK_UP;
+
+#if LWIP_DHCP
+ if (netif->dhcp) {
+ dhcp_network_changed(netif);
+ }
+#endif /* LWIP_DHCP */
+
+#if LWIP_AUTOIP
+ if (netif->autoip) {
+ autoip_network_changed(netif);
+ }
+#endif /* LWIP_AUTOIP */
+
+ if (netif->flags & NETIF_FLAG_UP) {
+#if LWIP_ARP
+ /* For Ethernet network interfaces, we would like to send a "gratuitous ARP" */
+ if (netif->flags & NETIF_FLAG_ETHARP) {
+ etharp_gratuitous(netif);
+ }
+#endif /* LWIP_ARP */
+
+#if LWIP_IGMP
+ /* resend IGMP memberships */
+ if (netif->flags & NETIF_FLAG_IGMP) {
+ igmp_report_groups( netif);
+ }
+#endif /* LWIP_IGMP */
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+ /* send mld memberships */
+ mld6_report_groups( netif);
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+ }
+ NETIF_LINK_CALLBACK(netif);
+ }
+}
+
+/**
+ * Called by a driver when its link goes down
+ */
+void netif_set_link_down(struct netif *netif )
+{
+ if (netif->flags & NETIF_FLAG_LINK_UP) {
+ netif->flags &= ~NETIF_FLAG_LINK_UP;
+ NETIF_LINK_CALLBACK(netif);
+ }
+}
+
+#if LWIP_NETIF_LINK_CALLBACK
+/**
+ * Set callback to be called when link is brought up/down
+ */
+void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback)
+{
+ if (netif) {
+ netif->link_callback = link_callback;
+ }
+}
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+#if ENABLE_LOOPBACK
+/**
+ * Send an IP packet to be received on the same netif (loopif-like).
+ * The pbuf is simply copied and handed back to netif->input.
+ * In multithreaded mode, this is done directly since netif->input must put
+ * the packet on a queue.
+ * In callback mode, the packet is put on an internal queue and is fed to
+ * netif->input by netif_poll().
+ *
+ * @param netif the lwip network interface structure
+ * @param p the (IP) packet to 'send'
+ * @return ERR_OK if the packet has been sent
+ * ERR_MEM if the pbuf used to copy the packet couldn't be allocated
+ */
+err_t
+netif_loop_output(struct netif *netif, struct pbuf *p)
+{
+ struct pbuf *r;
+ err_t err;
+ struct pbuf *last;
+#if LWIP_LOOPBACK_MAX_PBUFS
+ u8_t clen = 0;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+ /* If we have a loopif, SNMP counters are adjusted for it,
+ * if not they are adjusted for 'netif'. */
+#if LWIP_SNMP
+#if LWIP_HAVE_LOOPIF
+ struct netif *stats_if = &loop_netif;
+#else /* LWIP_HAVE_LOOPIF */
+ struct netif *stats_if = netif;
+#endif /* LWIP_HAVE_LOOPIF */
+#endif /* LWIP_SNMP */
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ /* Allocate a new pbuf */
+ r = pbuf_alloc(PBUF_LINK, p->tot_len, PBUF_RAM);
+ if (r == NULL) {
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(stats_if);
+ return ERR_MEM;
+ }
+#if LWIP_LOOPBACK_MAX_PBUFS
+ clen = pbuf_clen(r);
+ /* check for overflow or too many pbuf on queue */
+ if(((netif->loop_cnt_current + clen) < netif->loop_cnt_current) ||
+ ((netif->loop_cnt_current + clen) > LWIP_LOOPBACK_MAX_PBUFS)) {
+ pbuf_free(r);
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(stats_if);
+ return ERR_MEM;
+ }
+ netif->loop_cnt_current += clen;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+
+ /* Copy the whole pbuf queue p into the single pbuf r */
+ if ((err = pbuf_copy(r, p)) != ERR_OK) {
+ pbuf_free(r);
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(stats_if);
+ return err;
+ }
+
+ /* Put the packet on a linked list which gets emptied through calling
+ netif_poll(). */
+
+ /* let last point to the last pbuf in chain r */
+ for (last = r; last->next != NULL; last = last->next);
+
+ SYS_ARCH_PROTECT(lev);
+ if (netif->loop_first != NULL) {
+ LWIP_ASSERT("if first != NULL, last must also be != NULL", netif->loop_last != NULL);
+ netif->loop_last->next = r;
+ netif->loop_last = last;
+ } else {
+ netif->loop_first = r;
+ netif->loop_last = last;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+
+ LINK_STATS_INC(link.xmit);
+ snmp_add_ifoutoctets(stats_if, p->tot_len);
+ snmp_inc_ifoutucastpkts(stats_if);
+
+#if LWIP_NETIF_LOOPBACK_MULTITHREADING
+ /* For multithreading environment, schedule a call to netif_poll */
+ tcpip_callback_with_block((tcpip_callback_fn)netif_poll, netif, 0);
+#endif /* LWIP_NETIF_LOOPBACK_MULTITHREADING */
+
+ return ERR_OK;
+}
+
+static err_t
+netif_loop_output_ipv4(struct netif *netif, struct pbuf *p, ip_addr_t* addr)
+{
+ LWIP_UNUSED_ARG(addr);
+ return netif_loop_output(netif, p);
+}
+
+#if LWIP_IPV6
+static err_t
+netif_loop_output_ipv6(struct netif *netif, struct pbuf *p, ip6_addr_t* addr)
+{
+ LWIP_UNUSED_ARG(addr);
+ return netif_loop_output(netif, p);
+}
+#endif
+
+
+/**
+ * Call netif_poll() in the main loop of your application. This is to prevent
+ * reentering non-reentrant functions like tcp_input(). Packets passed to
+ * netif_loop_output() are put on a list that is passed to netif->input() by
+ * netif_poll().
+ */
+void
+netif_poll(struct netif *netif)
+{
+ struct pbuf *in;
+ /* If we have a loopif, SNMP counters are adjusted for it,
+ * if not they are adjusted for 'netif'. */
+#if LWIP_SNMP
+#if LWIP_HAVE_LOOPIF
+ struct netif *stats_if = &loop_netif;
+#else /* LWIP_HAVE_LOOPIF */
+ struct netif *stats_if = netif;
+#endif /* LWIP_HAVE_LOOPIF */
+#endif /* LWIP_SNMP */
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ do {
+ /* Get a packet from the list. With SYS_LIGHTWEIGHT_PROT=1, this is protected */
+ SYS_ARCH_PROTECT(lev);
+ in = netif->loop_first;
+ if (in != NULL) {
+ struct pbuf *in_end = in;
+#if LWIP_LOOPBACK_MAX_PBUFS
+ u8_t clen = 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;
+#if LWIP_LOOPBACK_MAX_PBUFS
+ clen++;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+ }
+#if LWIP_LOOPBACK_MAX_PBUFS
+ /* adjust the number of pbufs on queue */
+ LWIP_ASSERT("netif->loop_cnt_current underflow",
+ ((netif->loop_cnt_current - clen) < netif->loop_cnt_current));
+ netif->loop_cnt_current -= clen;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+
+ /* 'in_end' now points to the last pbuf from 'in' */
+ if (in_end == netif->loop_last) {
+ /* this was the last pbuf in the list */
+ netif->loop_first = netif->loop_last = NULL;
+ } else {
+ /* pop the pbuf off the list */
+ netif->loop_first = in_end->next;
+ LWIP_ASSERT("should not be null since first != last!", netif->loop_first != NULL);
+ }
+ /* De-queue the pbuf from its successors on the 'loop_' list. */
+ in_end->next = NULL;
+ }
+ SYS_ARCH_UNPROTECT(lev);
+
+ 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 (ipX_input(in, netif) != ERR_OK) {
+ pbuf_free(in);
+ }
+ /* Don't reference the packet any more! */
+ in = NULL;
+ }
+ /* go on while there is a packet on the list */
+ } while (netif->loop_first != NULL);
+}
+
+#if !LWIP_NETIF_LOOPBACK_MULTITHREADING
+/**
+ * Calls netif_poll() for every netif on the netif_list.
+ */
+void
+netif_poll_all(void)
+{
+ struct netif *netif = netif_list;
+ /* loop through netifs */
+ while (netif != NULL) {
+ netif_poll(netif);
+ /* proceed to next network interface */
+ netif = netif->next;
+ }
+}
+#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
+
+#if LWIP_IPV6
+s8_t
+netif_matches_ip6_addr(struct netif * netif, ip6_addr_t * ip6addr)
+{
+ s8_t i;
+ for (i = 0; i < LWIP_IPV6_NUM_ADDRESSES; i++) {
+ if (!ip6_addr_isinvalid(netif_ip6_addr_state(netif, i)) &&
+ ip6_addr_cmp(netif_ip6_addr(netif, i), ip6addr)) {
+ return i;
+ }
+ }
+ return -1;
+}
+
+void
+netif_create_ip6_linklocal_address(struct netif * netif, u8_t from_mac_48bit)
+{
+ u8_t i;
+
+ /* Link-local prefix. */
+ netif->ip6_addr[0].addr[0] = PP_HTONL(0xfe800000ul);
+ netif->ip6_addr[0].addr[1] = 0;
+
+ /* Generate interface ID. */
+ if (from_mac_48bit) {
+ /* Assume hwaddr is a 48-bit IEEE 802 MAC. Convert to EUI-64 address. Complement Group bit. */
+ netif->ip6_addr[0].addr[2] = htonl((((u32_t)(netif->hwaddr[0] ^ 0x02)) << 24) |
+ ((u32_t)(netif->hwaddr[1]) << 16) |
+ ((u32_t)(netif->hwaddr[2]) << 8) |
+ (0xff));
+ netif->ip6_addr[0].addr[3] = htonl((0xfeul << 24) |
+ ((u32_t)(netif->hwaddr[3]) << 16) |
+ ((u32_t)(netif->hwaddr[4]) << 8) |
+ (netif->hwaddr[5]));
+ }
+ else {
+ u8_t *id;
+
+ /* Use hwaddr directly as interface ID. */
+ netif->ip6_addr[0].addr[2] = 0;
+ netif->ip6_addr[0].addr[3] = 0;
+
+ LWIP_ASSERT("bad netif->hwaddr_len",
+ 0 < netif->hwaddr_len && netif->hwaddr_len <= 8);
+ id = (uint8_t *)&netif->ip6_addr[0].addr[2];
+ id += 8 - netif->hwaddr_len;
+ for (i = 0; i < netif->hwaddr_len; ++i) {
+ id[i] = netif->hwaddr[i];
+ }
+ }
+
+ /* Set address state. */
+#if LWIP_IPV6_DUP_DETECT_ATTEMPTS
+ /* Will perform duplicate address detection (DAD). */
+ netif->ip6_addr_state[0] = IP6_ADDR_TENTATIVE;
+#else
+ /* Consider address valid. */
+ netif->ip6_addr_state[0] = IP6_ADDR_PREFERRED;
+#endif /* LWIP_IPV6_AUTOCONFIG */
+}
+
+static err_t
+netif_null_output_ip6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr)
+{
+ (void)netif;
+ (void)p;
+ (void)ipaddr;
+
+ return ERR_IF;
+}
+#endif /* LWIP_IPV6 */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/pbuf.c b/src/VBox/Devices/Network/lwip-new/src/core/pbuf.c
new file mode 100644
index 00000000..a0fed8e5
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/pbuf.c
@@ -0,0 +1,1180 @@
+/**
+ * @file
+ * Packet buffer management
+ *
+ * Packets are built from the pbuf data structure. It supports dynamic
+ * memory allocation for packet contents or can reference externally
+ * managed packet contents both in RAM and ROM. Quick allocation for
+ * incoming packets is provided through pools with fixed sized pbufs.
+ *
+ * A packet may span over multiple pbufs, chained as a singly linked
+ * list. This is called a "pbuf chain".
+ *
+ * Multiple packets may be queued, also using this singly linked list.
+ * This is called a "packet queue".
+ *
+ * So, a packet queue consists of one or more pbuf chains, each of
+ * which consist of one or more pbufs. CURRENTLY, PACKET QUEUES ARE
+ * NOT SUPPORTED!!! Use helper structs to queue multiple packets.
+ *
+ * The differences between a pbuf chain and a packet queue are very
+ * precise but subtle.
+ *
+ * The last pbuf of a packet has a ->tot_len field that equals the
+ * ->len field. It can be found by traversing the list. If the last
+ * pbuf of a packet has a ->next field other than NULL, more packets
+ * are on the queue.
+ *
+ * Therefore, looping through a pbuf of a single packet, has an
+ * loop end condition (tot_len == p->len), NOT (next == NULL).
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/stats.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/pbuf.h"
+#include "lwip/sys.h"
+#include "arch/perf.h"
+#if LWIP_TCP && TCP_QUEUE_OOSEQ
+#include "lwip/tcp_impl.h"
+#endif
+#if LWIP_CHECKSUM_ON_COPY
+#include "lwip/inet_chksum.h"
+#endif
+
+#include <string.h>
+
+#define SIZEOF_STRUCT_PBUF LWIP_MEM_ALIGN_SIZE(sizeof(struct pbuf))
+/* Since the pool is created in memp, PBUF_POOL_BUFSIZE will be automatically
+ aligned there. Therefore, PBUF_POOL_BUFSIZE_ALIGNED can be used here. */
+#define PBUF_POOL_BUFSIZE_ALIGNED LWIP_MEM_ALIGN_SIZE(PBUF_POOL_BUFSIZE)
+
+#if !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ
+#define PBUF_POOL_IS_EMPTY()
+#else /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */
+
+#if !NO_SYS
+#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL
+#include "lwip/tcpip.h"
+#define PBUF_POOL_FREE_OOSEQ_QUEUE_CALL() do { \
+ if(tcpip_callback_with_block(pbuf_free_ooseq_callback, NULL, 0) != ERR_OK) { \
+ SYS_ARCH_PROTECT(old_level); \
+ pbuf_free_ooseq_pending = 0; \
+ SYS_ARCH_UNPROTECT(old_level); \
+ } } while(0)
+#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
+#endif /* !NO_SYS */
+
+volatile u8_t pbuf_free_ooseq_pending;
+#define PBUF_POOL_IS_EMPTY() pbuf_pool_is_empty()
+
+/**
+ * Attempt to reclaim some memory from queued out-of-sequence TCP segments
+ * if we run out of pool pbufs. It's better to give priority to new packets
+ * if we're running out.
+ *
+ * This must be done in the correct thread context therefore this function
+ * can only be used with NO_SYS=0 and through tcpip_callback.
+ */
+#if !NO_SYS
+static
+#endif /* !NO_SYS */
+void
+pbuf_free_ooseq(void)
+{
+ struct tcp_pcb* pcb;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+ SYS_ARCH_PROTECT(old_level);
+ pbuf_free_ooseq_pending = 0;
+ SYS_ARCH_UNPROTECT(old_level);
+
+ for (pcb = tcp_active_pcbs; NULL != pcb; pcb = pcb->next) {
+ if (NULL != pcb->ooseq) {
+ /** Free the ooseq pbufs of one PCB only */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free_ooseq: freeing out-of-sequence pbufs\n"));
+ tcp_segs_free(pcb->ooseq);
+ pcb->ooseq = NULL;
+ return;
+ }
+ }
+}
+
+#if !NO_SYS
+/**
+ * Just a callback function for tcpip_timeout() that calls pbuf_free_ooseq().
+ */
+static void
+pbuf_free_ooseq_callback(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ pbuf_free_ooseq();
+}
+#endif /* !NO_SYS */
+
+/** Queue a call to pbuf_free_ooseq if not already queued. */
+static void
+pbuf_pool_is_empty(void)
+{
+#ifndef PBUF_POOL_FREE_OOSEQ_QUEUE_CALL
+ SYS_ARCH_DECL_PROTECT(old_level);
+ SYS_ARCH_PROTECT(old_level);
+ pbuf_free_ooseq_pending = 1;
+ SYS_ARCH_UNPROTECT(old_level);
+#else /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
+ u8_t queued;
+ SYS_ARCH_DECL_PROTECT(old_level);
+ SYS_ARCH_PROTECT(old_level);
+ queued = pbuf_free_ooseq_pending;
+ pbuf_free_ooseq_pending = 1;
+ SYS_ARCH_UNPROTECT(old_level);
+
+ if(!queued) {
+ /* queue a call to pbuf_free_ooseq if not already queued */
+ PBUF_POOL_FREE_OOSEQ_QUEUE_CALL();
+ }
+#endif /* PBUF_POOL_FREE_OOSEQ_QUEUE_CALL */
+}
+#endif /* !LWIP_TCP || !TCP_QUEUE_OOSEQ || !PBUF_POOL_FREE_OOSEQ */
+
+/**
+ * Allocates a pbuf of the given type (possibly a chain for PBUF_POOL type).
+ *
+ * The actual memory allocated for the pbuf is determined by the
+ * layer at which the pbuf is allocated and the requested size
+ * (from the size parameter).
+ *
+ * @param layer flag to define header size
+ * @param length size of the pbuf's payload
+ * @param type this parameter decides how and where the pbuf
+ * should be allocated as follows:
+ *
+ * - PBUF_RAM: buffer memory for pbuf is allocated as one large
+ * chunk. This includes protocol headers as well.
+ * - PBUF_ROM: no buffer memory is allocated for the pbuf, even for
+ * protocol headers. Additional headers must be prepended
+ * by allocating another pbuf and chain in to the front of
+ * the ROM pbuf. It is assumed that the memory used is really
+ * similar to ROM in that it is immutable and will not be
+ * changed. Memory which is dynamic should generally not
+ * be attached to PBUF_ROM pbufs. Use PBUF_REF instead.
+ * - PBUF_REF: no buffer memory is allocated for the pbuf, even for
+ * protocol headers. It is assumed that the pbuf is only
+ * being used in a single thread. If the pbuf gets queued,
+ * then pbuf_take should be called to copy the buffer.
+ * - PBUF_POOL: the pbuf is allocated as a pbuf chain, with pbufs from
+ * the pbuf pool that is allocated during pbuf_init().
+ *
+ * @return the allocated pbuf. If multiple pbufs where allocated, this
+ * is the first pbuf of a pbuf chain.
+ */
+struct pbuf *
+pbuf_alloc(pbuf_layer layer, u16_t length, pbuf_type type)
+{
+ struct pbuf *p, *q, *r;
+ u16_t offset;
+ s32_t rem_len; /* remaining length */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F")\n", length));
+
+ /* determine header offset */
+ switch (layer) {
+ case PBUF_TRANSPORT:
+ /* add room for transport (often TCP) layer header */
+ offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
+ 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;
+ }
+ p->type = type;
+ p->next = NULL;
+
+ /* make the payload pointer point 'offset' bytes into pbuf data memory */
+ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + (SIZEOF_STRUCT_PBUF + offset)));
+ LWIP_ASSERT("pbuf_alloc: pbuf p->payload properly aligned",
+ ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
+ /* the total length of the pbuf chain is the requested size */
+ p->tot_len = length;
+ /* set the length of the first pbuf in the chain */
+ p->len = LWIP_MIN(length, PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset));
+ LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
+ ((u8_t*)p->payload + p->len <=
+ (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
+ LWIP_ASSERT("PBUF_POOL_BUFSIZE must be bigger than MEM_ALIGNMENT",
+ (PBUF_POOL_BUFSIZE_ALIGNED - LWIP_MEM_ALIGN_SIZE(offset)) > 0 );
+ /* set reference count (needed here in case we fail) */
+ p->ref = 1;
+
+ /* now allocate the tail of the pbuf chain */
+
+ /* remember first pbuf for linkage in next iteration */
+ r = p;
+ /* remaining length to be allocated */
+ rem_len = length - p->len;
+ /* any remaining pbufs to be allocated? */
+ while (rem_len > 0) {
+ q = (struct pbuf *)memp_malloc(MEMP_PBUF_POOL);
+ if (q == NULL) {
+ PBUF_POOL_IS_EMPTY();
+ /* free chain so far allocated */
+ pbuf_free(p);
+ /* bail out unsuccesfully */
+ return NULL;
+ }
+ q->type = type;
+ q->flags = 0;
+ q->next = NULL;
+ /* make previous pbuf point to this pbuf */
+ r->next = q;
+ /* set total length of this pbuf and next in chain */
+ LWIP_ASSERT("rem_len < max_u16_t", rem_len < 0xffff);
+ q->tot_len = (u16_t)rem_len;
+ /* this pbuf length is pool size, unless smaller sized tail */
+ q->len = LWIP_MIN((u16_t)rem_len, PBUF_POOL_BUFSIZE_ALIGNED);
+ q->payload = (void *)((u8_t *)q + SIZEOF_STRUCT_PBUF);
+ LWIP_ASSERT("pbuf_alloc: pbuf q->payload properly aligned",
+ ((mem_ptr_t)q->payload % MEM_ALIGNMENT) == 0);
+ LWIP_ASSERT("check p->payload + p->len does not overflow pbuf",
+ ((u8_t*)p->payload + p->len <=
+ (u8_t*)p + SIZEOF_STRUCT_PBUF + PBUF_POOL_BUFSIZE_ALIGNED));
+ q->ref = 1;
+ /* calculate remaining length to be allocated */
+ rem_len -= q->len;
+ /* remember this pbuf for linkage in next iteration */
+ r = q;
+ }
+ /* end of chain */
+ /*r->next = NULL;*/
+
+ break;
+ case PBUF_RAM:
+ /* If pbuf is to be allocated in RAM, allocate memory for it. */
+ p = (struct pbuf*)mem_malloc(LWIP_MEM_ALIGN_SIZE(SIZEOF_STRUCT_PBUF + offset) + LWIP_MEM_ALIGN_SIZE(length));
+ if (p == NULL) {
+ return NULL;
+ }
+ /* Set up internal structure of the pbuf. */
+ p->payload = LWIP_MEM_ALIGN((void *)((u8_t *)p + SIZEOF_STRUCT_PBUF + offset));
+ p->len = p->tot_len = length;
+ p->next = NULL;
+ p->type = type;
+
+ LWIP_ASSERT("pbuf_alloc: pbuf->payload properly aligned",
+ ((mem_ptr_t)p->payload % MEM_ALIGNMENT) == 0);
+ break;
+ /* pbuf references existing (non-volatile static constant) ROM payload? */
+ case PBUF_ROM:
+ /* pbuf references existing (externally allocated) RAM payload? */
+ case PBUF_REF:
+ /* only allocate memory for the pbuf structure */
+ p = (struct pbuf *)memp_malloc(MEMP_PBUF);
+ if (p == NULL) {
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("pbuf_alloc: Could not allocate MEMP_PBUF for PBUF_%s.\n",
+ (type == PBUF_ROM) ? "ROM" : "REF"));
+ return NULL;
+ }
+ /* caller must set this field properly, afterwards */
+ p->payload = NULL;
+ p->len = p->tot_len = length;
+ p->next = NULL;
+ p->type = type;
+ break;
+ default:
+ LWIP_ASSERT("pbuf_alloc: erroneous type", 0);
+ return NULL;
+ }
+ /* set reference count */
+ p->ref = 1;
+ /* set flags */
+ p->flags = 0;
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloc(length=%"U16_F") == %p\n", length, (void *)p));
+ return p;
+}
+
+#if LWIP_SUPPORT_CUSTOM_PBUF
+/** Initialize a custom pbuf (already allocated).
+ *
+ * @param layer flag to define header size
+ * @param length size of the pbuf's payload
+ * @param type type of the pbuf (only used to treat the pbuf accordingly, as
+ * this function allocates no memory)
+ * @param p pointer to the custom pbuf to initialize (already allocated)
+ * @param payload_mem pointer to the buffer that is used for payload and headers,
+ * must be at least big enough to hold 'length' plus the header size,
+ * may be NULL if set later.
+ * ATTENTION: The caller is responsible for correct alignment of this buffer!!
+ * @param payload_mem_len the size of the 'payload_mem' buffer, must be at least
+ * big enough to hold 'length' plus the header size
+ */
+struct pbuf*
+pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type, struct pbuf_custom *p,
+ void *payload_mem, u16_t payload_mem_len)
+{
+ u16_t offset;
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_alloced_custom(length=%"U16_F")\n", length));
+
+ /* determine header offset */
+ switch (l) {
+ case PBUF_TRANSPORT:
+ /* add room for transport (often TCP) layer header */
+ offset = PBUF_LINK_HLEN + PBUF_IP_HLEN + PBUF_TRANSPORT_HLEN;
+ break;
+ case PBUF_IP:
+ /* add room for IP layer header */
+ offset = PBUF_LINK_HLEN + PBUF_IP_HLEN;
+ break;
+ case PBUF_LINK:
+ /* add room for link layer header */
+ offset = PBUF_LINK_HLEN;
+ break;
+ case PBUF_RAW:
+ offset = 0;
+ break;
+ default:
+ LWIP_ASSERT("pbuf_alloced_custom: bad pbuf layer", 0);
+ return NULL;
+ }
+
+ if (LWIP_MEM_ALIGN_SIZE(offset) + length > payload_mem_len) {
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_WARNING, ("pbuf_alloced_custom(length=%"U16_F") buffer too short\n", length));
+ return NULL;
+ }
+
+ p->pbuf.next = NULL;
+ if (payload_mem != NULL) {
+ p->pbuf.payload = (u8_t *)payload_mem + LWIP_MEM_ALIGN_SIZE(offset);
+ } else {
+ p->pbuf.payload = NULL;
+ }
+ p->pbuf.flags = PBUF_FLAG_IS_CUSTOM;
+ p->pbuf.len = p->pbuf.tot_len = length;
+ p->pbuf.type = type;
+ p->pbuf.ref = 1;
+ return &p->pbuf;
+}
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+
+/**
+ * Shrink a pbuf chain to a desired length.
+ *
+ * @param p pbuf to shrink.
+ * @param new_len desired new length of pbuf chain
+ *
+ * Depending on the desired length, the first few pbufs in a chain might
+ * be skipped and left unchanged. The new last pbuf in the chain will be
+ * resized, and any remaining pbufs will be freed.
+ *
+ * @note If the pbuf is ROM/REF, only the ->tot_len and ->len fields are adjusted.
+ * @note May not be called on a packet queue.
+ *
+ * @note Despite its name, pbuf_realloc cannot grow the size of a pbuf (chain).
+ */
+void
+pbuf_realloc(struct pbuf *p, u16_t new_len)
+{
+ struct pbuf *q;
+ u16_t rem_len; /* remaining length */
+ s32_t grow;
+
+ LWIP_ASSERT("pbuf_realloc: p != NULL", p != NULL);
+ LWIP_ASSERT("pbuf_realloc: sane p->type", p->type == PBUF_POOL ||
+ p->type == PBUF_ROM ||
+ p->type == PBUF_RAM ||
+ p->type == PBUF_REF);
+
+ /* desired length larger than current length? */
+ if (new_len >= p->tot_len) {
+ /* enlarging not yet supported */
+ return;
+ }
+
+ /* the pbuf chain grows by (new_len - p->tot_len) bytes
+ * (which may be negative in case of shrinking) */
+ grow = new_len - p->tot_len;
+
+ /* first, step over any pbufs that should remain in the chain */
+ rem_len = new_len;
+ q = p;
+ /* should this pbuf be kept? */
+ while (rem_len > q->len) {
+ /* decrease remaining length by pbuf length */
+ rem_len -= q->len;
+ /* decrease total length indicator */
+ LWIP_ASSERT("grow < max_u16_t", grow < 0xffff);
+ q->tot_len += (u16_t)grow;
+ /* proceed to next pbuf in chain */
+ q = q->next;
+ LWIP_ASSERT("pbuf_realloc: q != NULL", q != NULL);
+ }
+ /* we have now reached the new last pbuf (in q) */
+ /* rem_len == desired length for pbuf q */
+
+ /* shrink allocated memory for PBUF_RAM */
+ /* (other types merely adjust their length fields */
+ if ((q->type == PBUF_RAM) && (rem_len != q->len)) {
+ /* reallocate and adjust the length of the pbuf that will be split */
+ q = (struct pbuf *)mem_trim(q, (u16_t)((u8_t *)q->payload - (u8_t *)q) + rem_len);
+ LWIP_ASSERT("mem_trim returned q == NULL", q != NULL);
+ }
+ /* adjust length fields for new last pbuf */
+ q->len = rem_len;
+ q->tot_len = q->len;
+
+ /* any remaining pbufs in chain? */
+ if (q->next != NULL) {
+ /* free remaining pbufs in chain */
+ pbuf_free(q->next);
+ }
+ /* q is last packet in chain */
+ q->next = NULL;
+
+}
+
+/**
+ * Adjusts the payload pointer to hide or reveal headers in the payload.
+ *
+ * Adjusts the ->payload pointer so that space for a header
+ * (dis)appears in the pbuf payload.
+ *
+ * The ->payload, ->tot_len and ->len fields are adjusted.
+ *
+ * @param p pbuf to change the header size.
+ * @param header_size_increment Number of bytes to increment header size which
+ * increases the size of the pbuf. New space is on the front.
+ * (Using a negative value decreases the header size.)
+ * If hdr_size_inc is 0, this function does nothing and returns succesful.
+ *
+ * PBUF_ROM and PBUF_REF type buffers cannot have their sizes increased, so
+ * the call will fail. A check is made that the increase in header size does
+ * not move the payload pointer in front of the start of the buffer.
+ * @return non-zero on failure, zero on success.
+ *
+ */
+u8_t
+pbuf_header(struct pbuf *p, s16_t header_size_increment)
+{
+ u16_t type;
+ void *payload;
+ u16_t increment_magnitude;
+
+ LWIP_ASSERT("p != NULL", p != NULL);
+ if ((header_size_increment == 0) || (p == NULL)) {
+ return 0;
+ }
+
+ if (header_size_increment < 0){
+ increment_magnitude = -header_size_increment;
+ /* Check that we aren't going to move off the end of the pbuf */
+ LWIP_ERROR("increment_magnitude <= p->len", (increment_magnitude <= p->len), return 1;);
+ } else {
+ increment_magnitude = header_size_increment;
+#if 0
+ /* Can't assert these as some callers speculatively call
+ pbuf_header() to see if it's OK. Will return 1 below instead. */
+ /* Check that we've got the correct type of pbuf to work with */
+ LWIP_ASSERT("p->type == PBUF_RAM || p->type == PBUF_POOL",
+ p->type == PBUF_RAM || p->type == PBUF_POOL);
+ /* Check that we aren't going to move off the beginning of the pbuf */
+ LWIP_ASSERT("p->payload - increment_magnitude >= p + SIZEOF_STRUCT_PBUF",
+ (u8_t *)p->payload - increment_magnitude >= (u8_t *)p + SIZEOF_STRUCT_PBUF);
+#endif
+ }
+
+ type = p->type;
+ /* remember current payload pointer */
+ payload = p->payload;
+
+ /* pbuf types containing payloads? */
+ if (type == PBUF_RAM || type == PBUF_POOL) {
+ /* set new payload pointer */
+ p->payload = (u8_t *)p->payload - header_size_increment;
+ /* boundary check fails? */
+ if ((u8_t *)p->payload < (u8_t *)p + SIZEOF_STRUCT_PBUF) {
+ LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("pbuf_header: failed as %p < %p (not enough space for new header size)\n",
+ (void *)p->payload, (void *)(p + 1)));
+ /* restore old payload pointer */
+ p->payload = payload;
+ /* bail out unsuccesfully */
+ return 1;
+ }
+ /* pbuf types refering to external payloads? */
+ } else if (type == PBUF_REF || type == PBUF_ROM) {
+ /* hide a header in the payload? */
+ if ((header_size_increment < 0) && (increment_magnitude <= p->len)) {
+ /* increase payload pointer */
+ p->payload = (u8_t *)p->payload - header_size_increment;
+ } else {
+ /* cannot expand payload to front (yet!)
+ * bail out unsuccesfully */
+ return 1;
+ }
+ } else {
+ /* Unknown type */
+ LWIP_ASSERT("bad pbuf type", 0);
+ return 1;
+ }
+ /* modify pbuf length fields */
+ p->len += header_size_increment;
+ p->tot_len += header_size_increment;
+
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_header: old %p new %p (%"S16_F")\n",
+ (void *)payload, (void *)p->payload, header_size_increment));
+
+ return 0;
+}
+
+/**
+ * Dereference a pbuf chain or queue and deallocate any no-longer-used
+ * pbufs at the head of this chain or queue.
+ *
+ * Decrements the pbuf reference count. If it reaches zero, the pbuf is
+ * deallocated.
+ *
+ * For a pbuf chain, this is repeated for each pbuf in the chain,
+ * up to the first pbuf which has a non-zero reference count after
+ * decrementing. So, when all reference counts are one, the whole
+ * chain is free'd.
+ *
+ * @param p The pbuf (chain) to be dereferenced.
+ *
+ * @return the number of pbufs that were de-allocated
+ * from the head of the chain.
+ *
+ * @note MUST NOT be called on a packet queue (Not verified to work yet).
+ * @note the reference counter of a pbuf equals the number of pointers
+ * that refer to the pbuf (or into the pbuf).
+ *
+ * @internal examples:
+ *
+ * Assuming existing chains a->b->c with the following reference
+ * counts, calling pbuf_free(a) results in:
+ *
+ * 1->2->3 becomes ...1->3
+ * 3->3->3 becomes 2->3->3
+ * 1->1->2 becomes ......1
+ * 2->1->1 becomes 1->1->1
+ * 1->1->1 becomes .......
+ *
+ */
+u8_t
+pbuf_free(struct pbuf *p)
+{
+ u16_t type;
+ struct pbuf *q;
+ u8_t count;
+
+ if (p == NULL) {
+ LWIP_ASSERT("p != NULL", p != NULL);
+ /* if assertions are disabled, proceed with debug output */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("pbuf_free(p == NULL) was called.\n"));
+ return 0;
+ }
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free(%p)\n", (void *)p));
+
+ PERF_START;
+
+ LWIP_ASSERT("pbuf_free: sane type",
+ p->type == PBUF_RAM || p->type == PBUF_ROM ||
+ p->type == PBUF_REF || p->type == PBUF_POOL);
+
+ count = 0;
+ /* de-allocate all consecutive pbufs from the head of the chain that
+ * obtain a zero reference count after decrementing*/
+ while (p != NULL) {
+ u16_t ref;
+ SYS_ARCH_DECL_PROTECT(old_level);
+ /* Since decrementing ref cannot be guaranteed to be a single machine operation
+ * we must protect it. We put the new ref into a local variable to prevent
+ * further protection. */
+ SYS_ARCH_PROTECT(old_level);
+ /* all pbufs in a chain are referenced at least once */
+ LWIP_ASSERT("pbuf_free: p->ref > 0", p->ref > 0);
+ /* decrease reference count (number of pointers to pbuf) */
+ ref = --(p->ref);
+ SYS_ARCH_UNPROTECT(old_level);
+ /* this pbuf is no longer referenced to? */
+ if (ref == 0) {
+ /* remember next pbuf in chain for next iteration */
+ q = p->next;
+ LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: deallocating %p\n", (void *)p));
+ type = p->type;
+#if LWIP_SUPPORT_CUSTOM_PBUF
+ /* is this a custom pbuf? */
+ if ((p->flags & PBUF_FLAG_IS_CUSTOM) != 0) {
+ struct pbuf_custom *pc = (struct pbuf_custom*)p;
+ LWIP_ASSERT("pc->custom_free_function != NULL", pc->custom_free_function != NULL);
+ pc->custom_free_function(p);
+ } else
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+ {
+ /* is this a pbuf from the pool? */
+ if (type == PBUF_POOL) {
+ memp_free(MEMP_PBUF_POOL, p);
+ /* is this a ROM or RAM referencing pbuf? */
+ } else if (type == PBUF_ROM || type == PBUF_REF) {
+ memp_free(MEMP_PBUF, p);
+ /* type == PBUF_RAM */
+ } else {
+ mem_free(p);
+ }
+ }
+ count++;
+ /* proceed to next pbuf */
+ p = q;
+ /* p->ref > 0, this pbuf is still referenced to */
+ /* (and so the remaining pbufs in chain as well) */
+ } else {
+ LWIP_DEBUGF( PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_free: %p has ref %"U16_F", ending here.\n", (void *)p, ref));
+ /* stop walking through the chain */
+ p = NULL;
+ }
+ }
+ PERF_STOP("pbuf_free");
+ /* return number of de-allocated pbufs */
+ return count;
+}
+
+/**
+ * Count number of pbufs in a chain
+ *
+ * @param p first pbuf of chain
+ * @return the number of pbufs in a chain
+ */
+
+u8_t
+pbuf_clen(struct pbuf *p)
+{
+ u8_t len;
+
+ len = 0;
+ while (p != NULL) {
+ ++len;
+ p = p->next;
+ }
+ return len;
+}
+
+/**
+ * Increment the reference count of the pbuf.
+ *
+ * @param p pbuf to increase reference counter of
+ *
+ */
+void
+pbuf_ref(struct pbuf *p)
+{
+ SYS_ARCH_DECL_PROTECT(old_level);
+ /* pbuf given? */
+ if (p != NULL) {
+ SYS_ARCH_PROTECT(old_level);
+ ++(p->ref);
+ SYS_ARCH_UNPROTECT(old_level);
+ }
+}
+
+/**
+ * Concatenate two pbufs (each may be a pbuf chain) and take over
+ * the caller's reference of the tail pbuf.
+ *
+ * @note The caller MAY NOT reference the tail pbuf afterwards.
+ * Use pbuf_chain() for that purpose.
+ *
+ * @see pbuf_chain()
+ */
+
+void
+pbuf_cat(struct pbuf *h, struct pbuf *t)
+{
+ struct pbuf *p;
+
+ LWIP_ERROR("(h != NULL) && (t != NULL) (programmer violates API)",
+ ((h != NULL) && (t != NULL)), return;);
+
+ /* proceed to last pbuf of chain */
+ for (p = h; p->next != NULL; p = p->next) {
+ /* add total length of second chain to all totals of first chain */
+ p->tot_len += t->tot_len;
+ }
+ /* { p is last pbuf of first h chain, p->next == NULL } */
+ LWIP_ASSERT("p->tot_len == p->len (of last pbuf in chain)", p->tot_len == p->len);
+ LWIP_ASSERT("p->next == NULL", p->next == NULL);
+ /* add total length of second chain to last pbuf total of first chain */
+ p->tot_len += t->tot_len;
+ /* chain last pbuf of head (p) with first of tail (t) */
+ p->next = t;
+ /* p->next now references t, but the caller will drop its reference to t,
+ * so netto there is no change to the reference count of t.
+ */
+}
+
+/**
+ * Chain two pbufs (or pbuf chains) together.
+ *
+ * The caller MUST call pbuf_free(t) once it has stopped
+ * using it. Use pbuf_cat() instead if you no longer use t.
+ *
+ * @param h head pbuf (chain)
+ * @param t tail pbuf (chain)
+ * @note The pbufs MUST belong to the same packet.
+ * @note MAY NOT be called on a packet queue.
+ *
+ * The ->tot_len fields of all pbufs of the head chain are adjusted.
+ * The ->next field of the last pbuf of the head chain is adjusted.
+ * The ->ref field of the first pbuf of the tail chain is adjusted.
+ *
+ */
+void
+pbuf_chain(struct pbuf *h, struct pbuf *t)
+{
+ pbuf_cat(h, t);
+ /* t is now referenced by h */
+ pbuf_ref(t);
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_chain: %p references %p\n", (void *)h, (void *)t));
+}
+
+/**
+ * Dechains the first pbuf from its succeeding pbufs in the chain.
+ *
+ * Makes p->tot_len field equal to p->len.
+ * @param p pbuf to dechain
+ * @return remainder of the pbuf chain, or NULL if it was de-allocated.
+ * @note May not be called on a packet queue.
+ */
+struct pbuf *
+pbuf_dechain(struct pbuf *p)
+{
+ struct pbuf *q;
+ u8_t tail_gone = 1;
+ /* tail */
+ q = p->next;
+ /* pbuf has successor in chain? */
+ if (q != NULL) {
+ /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+ LWIP_ASSERT("p->tot_len == p->len + q->tot_len", q->tot_len == p->tot_len - p->len);
+ /* enforce invariant if assertion is disabled */
+ q->tot_len = p->tot_len - p->len;
+ /* decouple pbuf from remainder */
+ p->next = NULL;
+ /* total length of pbuf p is its own length only */
+ p->tot_len = p->len;
+ /* q is no longer referenced by p, free it */
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_dechain: unreferencing %p\n", (void *)q));
+ tail_gone = pbuf_free(q);
+ if (tail_gone > 0) {
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE,
+ ("pbuf_dechain: deallocated %p (as it is no longer referenced)\n", (void *)q));
+ }
+ /* return remaining tail or NULL if deallocated */
+ }
+ /* assert tot_len invariant: (p->tot_len == p->len + (p->next? p->next->tot_len: 0) */
+ LWIP_ASSERT("p->tot_len == p->len", p->tot_len == p->len);
+ return ((tail_gone > 0) ? NULL : q);
+}
+
+/**
+ *
+ * Create PBUF_RAM copies of pbufs.
+ *
+ * Used to queue packets on behalf of the lwIP stack, such as
+ * ARP based queueing.
+ *
+ * @note You MUST explicitly use p = pbuf_take(p);
+ *
+ * @note Only one packet is copied, no packet queue!
+ *
+ * @param p_to pbuf destination of the copy
+ * @param p_from pbuf source of the copy
+ *
+ * @return ERR_OK if pbuf was copied
+ * ERR_ARG if one of the pbufs is NULL or p_to is not big
+ * enough to hold p_from
+ */
+err_t
+pbuf_copy(struct pbuf *p_to, struct pbuf *p_from)
+{
+ u16_t offset_to=0, offset_from=0, len;
+
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy(%p, %p)\n",
+ (void*)p_to, (void*)p_from));
+
+ /* is the target big enough to hold the source? */
+ LWIP_ERROR("pbuf_copy: target not big enough to hold source", ((p_to != NULL) &&
+ (p_from != NULL) && (p_to->tot_len >= p_from->tot_len)), return ERR_ARG;);
+
+ /* iterate through pbuf chain */
+ do
+ {
+ /* copy one part of the original chain */
+ if ((p_to->len - offset_to) >= (p_from->len - offset_from)) {
+ /* complete current p_from fits into current p_to */
+ len = p_from->len - offset_from;
+ } else {
+ /* current p_from does not fit into current p_to */
+ len = p_to->len - offset_to;
+ }
+ MEMCPY((u8_t*)p_to->payload + offset_to, (u8_t*)p_from->payload + offset_from, len);
+ offset_to += len;
+ offset_from += len;
+ LWIP_ASSERT("offset_to <= p_to->len", offset_to <= p_to->len);
+ LWIP_ASSERT("offset_from <= p_from->len", offset_from <= p_from->len);
+ if (offset_from >= p_from->len) {
+ /* on to next p_from (if any) */
+ offset_from = 0;
+ p_from = p_from->next;
+ }
+ if (offset_to == p_to->len) {
+ /* on to next p_to (if any) */
+ offset_to = 0;
+ p_to = p_to->next;
+ LWIP_ERROR("p_to != NULL", (p_to != NULL) || (p_from == NULL) , return ERR_ARG;);
+ }
+
+ if((p_from != NULL) && (p_from->len == p_from->tot_len)) {
+ /* don't copy more than one packet! */
+ LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
+ (p_from->next == NULL), return ERR_VAL;);
+ }
+ if((p_to != NULL) && (p_to->len == p_to->tot_len)) {
+ /* don't copy more than one packet! */
+ LWIP_ERROR("pbuf_copy() does not allow packet queues!\n",
+ (p_to->next == NULL), return ERR_VAL;);
+ }
+ } while (p_from);
+ LWIP_DEBUGF(PBUF_DEBUG | LWIP_DBG_TRACE, ("pbuf_copy: end of chain reached.\n"));
+ return ERR_OK;
+}
+
+/**
+ * Copy (part of) the contents of a packet buffer
+ * to an application supplied buffer.
+ *
+ * @param buf the pbuf from which to copy data
+ * @param dataptr the application supplied buffer
+ * @param len length of data to copy (dataptr must be big enough). No more
+ * than buf->tot_len will be copied, irrespective of len
+ * @param offset offset into the packet buffer from where to begin copying len bytes
+ * @return the number of bytes copied, or 0 on failure
+ */
+u16_t
+pbuf_copy_partial(struct pbuf *buf, void *dataptr, u16_t len, u16_t offset)
+{
+ struct pbuf *p;
+ u16_t left;
+ u16_t buf_copy_len;
+ u16_t copied_total = 0;
+
+ LWIP_ERROR("pbuf_copy_partial: invalid buf", (buf != NULL), return 0;);
+ LWIP_ERROR("pbuf_copy_partial: invalid dataptr", (dataptr != NULL), return 0;);
+
+ left = 0;
+
+ if((buf == NULL) || (dataptr == NULL)) {
+ return 0;
+ }
+
+ /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
+ for(p = buf; len != 0 && p != NULL; p = p->next) {
+ if ((offset != 0) && (offset >= p->len)) {
+ /* don't copy from this buffer -> on to the next */
+ offset -= p->len;
+ } else {
+ /* copy from this buffer. maybe only partially. */
+ buf_copy_len = p->len - offset;
+ if (buf_copy_len > len)
+ buf_copy_len = len;
+ /* copy the necessary parts of the buffer */
+ MEMCPY(&((char*)dataptr)[left], &((char*)p->payload)[offset], buf_copy_len);
+ copied_total += buf_copy_len;
+ left += buf_copy_len;
+ len -= buf_copy_len;
+ offset = 0;
+ }
+ }
+ return copied_total;
+}
+
+/**
+ * Copy application supplied data into a pbuf.
+ * This function can only be used to copy the equivalent of buf->tot_len data.
+ *
+ * @param buf pbuf to fill with data
+ * @param dataptr application supplied data buffer
+ * @param len length of the application supplied data buffer
+ *
+ * @return ERR_OK if successful, ERR_MEM if the pbuf is not big enough
+ */
+err_t
+pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len)
+{
+ struct pbuf *p;
+ u16_t buf_copy_len;
+ u16_t total_copy_len = len;
+ u16_t copied_total = 0;
+
+ LWIP_ERROR("pbuf_take: invalid buf", (buf != NULL), return 0;);
+ LWIP_ERROR("pbuf_take: invalid dataptr", (dataptr != NULL), return 0;);
+
+ if ((buf == NULL) || (dataptr == NULL) || (buf->tot_len < len)) {
+ return ERR_ARG;
+ }
+
+ /* Note some systems use byte copy if dataptr or one of the pbuf payload pointers are unaligned. */
+ for(p = buf; total_copy_len != 0; p = p->next) {
+ LWIP_ASSERT("pbuf_take: invalid pbuf", p != NULL);
+ buf_copy_len = total_copy_len;
+ if (buf_copy_len > p->len) {
+ /* this pbuf cannot hold all remaining data */
+ buf_copy_len = p->len;
+ }
+ /* copy the necessary parts of the buffer */
+ MEMCPY(p->payload, &((char*)dataptr)[copied_total], buf_copy_len);
+ total_copy_len -= buf_copy_len;
+ copied_total += buf_copy_len;
+ }
+ LWIP_ASSERT("did not copy all data", total_copy_len == 0 && copied_total == len);
+ return ERR_OK;
+}
+
+/**
+ * Creates a single pbuf out of a queue of pbufs.
+ *
+ * @remark: Either the source pbuf 'p' is freed by this function or the original
+ * pbuf 'p' is returned, therefore the caller has to check the result!
+ *
+ * @param p the source pbuf
+ * @param layer pbuf_layer of the new pbuf
+ *
+ * @return a new, single pbuf (p->next is NULL)
+ * or the old pbuf if allocation fails
+ */
+struct pbuf*
+pbuf_coalesce(struct pbuf *p, pbuf_layer layer)
+{
+ struct pbuf *q;
+ err_t err;
+ if (p->next == NULL) {
+ return p;
+ }
+ q = pbuf_alloc(layer, p->tot_len, PBUF_RAM);
+ if (q == NULL) {
+ /* @todo: what do we do now? */
+ return p;
+ }
+ err = pbuf_copy(q, p);
+ LWIP_ASSERT("pbuf_copy failed", err == ERR_OK);
+ LWIP_UNUSED_ARG(err);
+ pbuf_free(p);
+ return q;
+}
+
+#if LWIP_CHECKSUM_ON_COPY
+/**
+ * Copies data into a single pbuf (*not* into a pbuf queue!) and updates
+ * the checksum while copying
+ *
+ * @param p the pbuf to copy data into
+ * @param start_offset offset of p->payload where to copy the data to
+ * @param dataptr data to copy into the pbuf
+ * @param len length of data to copy into the pbuf
+ * @param chksum pointer to the checksum which is updated
+ * @return ERR_OK if successful, another error if the data does not fit
+ * within the (first) pbuf (no pbuf queues!)
+ */
+err_t
+pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
+ u16_t len, u16_t *chksum)
+{
+ u32_t acc;
+ u16_t copy_chksum;
+ char *dst_ptr;
+ LWIP_ASSERT("p != NULL", p != NULL);
+ LWIP_ASSERT("dataptr != NULL", dataptr != NULL);
+ LWIP_ASSERT("chksum != NULL", chksum != NULL);
+ LWIP_ASSERT("len != 0", len != 0);
+
+ if ((start_offset >= p->len) || (start_offset + len > p->len)) {
+ return ERR_ARG;
+ }
+
+ dst_ptr = ((char*)p->payload) + start_offset;
+ copy_chksum = LWIP_CHKSUM_COPY(dst_ptr, dataptr, len);
+ if ((start_offset & 1) != 0) {
+ copy_chksum = SWAP_BYTES_IN_WORD(copy_chksum);
+ }
+ acc = *chksum;
+ acc += copy_chksum;
+ *chksum = FOLD_U32T(acc);
+ return ERR_OK;
+}
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+ /** Get one byte from the specified position in a pbuf
+ * WARNING: returns zero for offset >= p->tot_len
+ *
+ * @param p pbuf to parse
+ * @param offset offset into p of the byte to return
+ * @return byte at an offset into p OR ZERO IF 'offset' >= p->tot_len
+ */
+u8_t
+pbuf_get_at(struct pbuf* p, u16_t offset)
+{
+ u16_t copy_from = offset;
+ struct pbuf* q = p;
+
+ /* get the correct pbuf */
+ while ((q != NULL) && (q->len <= copy_from)) {
+ copy_from -= q->len;
+ q = q->next;
+ }
+ /* return requested data if pbuf is OK */
+ if ((q != NULL) && (q->len > copy_from)) {
+ return ((u8_t*)q->payload)[copy_from];
+ }
+ return 0;
+}
+
+/** Compare pbuf contents at specified offset with memory s2, both of length n
+ *
+ * @param p pbuf to compare
+ * @param offset offset into p at wich to start comparing
+ * @param s2 buffer to compare
+ * @param n length of buffer to compare
+ * @return zero if equal, nonzero otherwise
+ * (0xffff if p is too short, diffoffset+1 otherwise)
+ */
+u16_t
+pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n)
+{
+ u16_t start = offset;
+ struct pbuf* q = p;
+
+ /* get the correct pbuf */
+ while ((q != NULL) && (q->len <= start)) {
+ start -= q->len;
+ q = q->next;
+ }
+ /* return requested data if pbuf is OK */
+ if ((q != NULL) && (q->len > start)) {
+ u16_t i;
+ for(i = 0; i < n; i++) {
+ u8_t a = pbuf_get_at(q, start + i);
+ u8_t b = ((u8_t*)s2)[i];
+ if (a != b) {
+ return i+1;
+ }
+ }
+ return 0;
+ }
+ return 0xffff;
+}
+
+/** Find occurrence of mem (with length mem_len) in pbuf p, starting at offset
+ * start_offset.
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ * return value 'not found'
+ * @param mem search for the contents of this buffer
+ * @param mem_len length of 'mem'
+ * @param start_offset offset into p at which to start searching
+ * @return 0xFFFF if substr was not found in p or the index where it was found
+ */
+u16_t
+pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset)
+{
+ u16_t i;
+ u16_t max = p->tot_len - mem_len;
+ if (p->tot_len >= mem_len + start_offset) {
+ for(i = start_offset; i <= max; ) {
+ u16_t plus = pbuf_memcmp(p, i, mem, mem_len);
+ if (plus == 0) {
+ return i;
+ } else {
+ i += plus;
+ }
+ }
+ }
+ return 0xFFFF;
+}
+
+/** Find occurrence of substr with length substr_len in pbuf p, start at offset
+ * start_offset
+ * WARNING: in contrast to strstr(), this one does not stop at the first \0 in
+ * the pbuf/source string!
+ *
+ * @param p pbuf to search, maximum length is 0xFFFE since 0xFFFF is used as
+ * return value 'not found'
+ * @param substr string to search for in p, maximum length is 0xFFFE
+ * @return 0xFFFF if substr was not found in p or the index where it was found
+ */
+u16_t
+pbuf_strstr(struct pbuf* p, const char* substr)
+{
+ size_t substr_len;
+ if ((substr == NULL) || (substr[0] == 0) || (p->tot_len == 0xFFFF)) {
+ return 0xFFFF;
+ }
+ substr_len = strlen(substr);
+ if (substr_len >= 0xFFFF) {
+ return 0xFFFF;
+ }
+ return pbuf_memfind(p, substr, (u16_t)substr_len, 0);
+}
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/raw.c b/src/VBox/Devices/Network/lwip-new/src/core/raw.c
new file mode 100644
index 00000000..68b23c61
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/raw.c
@@ -0,0 +1,422 @@
+/**
+ * @file
+ * Implementation of raw protocol PCBs for low-level handling of
+ * different types of protocols besides (or overriding) those
+ * already available in lwIP.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/raw.h"
+#include "lwip/stats.h"
+#include "arch/perf.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+
+#include <string.h>
+
+/** The list of RAW PCBs */
+static struct raw_pcb *raw_pcbs;
+
+/**
+ * Determine if in incoming IP packet is covered by a RAW PCB
+ * and if so, pass it to a user-provided receive callback function.
+ *
+ * Given an incoming IP datagram (as a chain of pbufs) this function
+ * finds a corresponding RAW PCB and calls the corresponding receive
+ * callback function.
+ *
+ * @param p pbuf to be demultiplexed to a RAW PCB.
+ * @param inp network interface on which the datagram was received.
+ * @return - 1 if the packet has been eaten by a RAW PCB receive
+ * callback function. The caller MAY NOT not reference the
+ * packet any longer, and MAY NOT call pbuf_free().
+ * @return - 0 if packet is not eaten (pbuf is still referenced by the
+ * caller).
+ *
+ */
+u8_t
+raw_input(struct pbuf *p, struct netif *inp)
+{
+ struct raw_pcb *pcb, *prev;
+ struct ip_hdr *iphdr;
+ s16_t proto;
+ u8_t eaten = 0;
+#if LWIP_IPV6
+ struct ip6_hdr *ip6hdr;
+#endif /* LWIP_IPV6 */
+
+
+ LWIP_UNUSED_ARG(inp);
+
+ iphdr = (struct ip_hdr *)p->payload;
+#if LWIP_IPV6
+ if (IPH_V(iphdr) == 6) {
+ ip6hdr = (struct ip6_hdr *)p->payload;
+ proto = IP6H_NEXTH(ip6hdr);
+ }
+ else
+#endif /* LWIP_IPV6 */
+ {
+ proto = IPH_PROTO(iphdr);
+ }
+
+ prev = NULL;
+ pcb = raw_pcbs;
+ /* loop through all raw pcbs until the packet is eaten by one */
+ /* this allows multiple pcbs to match against the packet by design */
+ while ((eaten == 0) && (pcb != NULL)) {
+ if ((pcb->protocol == proto) && IP_PCB_IPVER_INPUT_MATCH(pcb) &&
+ (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip) ||
+ ipX_addr_cmp(PCB_ISIPV6(pcb), &(pcb->local_ip), ipX_current_dest_addr()))) {
+#if IP_SOF_BROADCAST_RECV
+ /* broadcast filter? */
+ if ((ip_get_option(pcb, SOF_BROADCAST) || !ip_addr_isbroadcast(ip_current_dest_addr(), inp))
+#if LWIP_IPV6
+ && !PCB_ISIPV6(pcb)
+#endif /* LWIP_IPV6 */
+ )
+#endif /* IP_SOF_BROADCAST_RECV */
+ {
+ /* receive callback function available? */
+ if (pcb->recv.ip4 != NULL) {
+#ifndef LWIP_NOASSERT
+ void* old_payload = p->payload;
+#endif
+ /* the receive callback function did not eat the packet? */
+ eaten = pcb->recv.ip4(pcb->recv_arg, pcb, p, ip_current_src_addr());
+ if (eaten != 0) {
+ /* receive function ate the packet */
+ p = NULL;
+ eaten = 1;
+ if (prev != NULL) {
+ /* move the pcb to the front of raw_pcbs so that is
+ found faster next time */
+ prev->next = pcb->next;
+ pcb->next = raw_pcbs;
+ raw_pcbs = pcb;
+ }
+ } else {
+ /* sanity-check that the receive callback did not alter the pbuf */
+ LWIP_ASSERT("raw pcb recv callback altered pbuf payload pointer without eating packet",
+ p->payload == old_payload);
+ }
+ }
+ /* no receive callback function was set for this raw PCB */
+ }
+ /* drop the packet */
+ }
+ prev = pcb;
+ pcb = pcb->next;
+ }
+ return eaten;
+}
+
+/**
+ * Bind a RAW PCB.
+ *
+ * @param pcb RAW PCB to be bound with a local address ipaddr.
+ * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to
+ * bind to all local interfaces.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occured.
+ * - ERR_USE. The specified IP address is already bound to by
+ * another RAW PCB.
+ *
+ * @see raw_disconnect()
+ */
+err_t
+raw_bind(struct raw_pcb *pcb, ip_addr_t *ipaddr)
+{
+ ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->local_ip, ipaddr);
+ return ERR_OK;
+}
+
+/**
+ * Connect an RAW PCB. This function is required by upper layers
+ * of lwip. Using the raw api you could use raw_sendto() instead
+ *
+ * This will associate the RAW PCB with the remote address.
+ *
+ * @param pcb RAW PCB to be connected with remote address ipaddr and port.
+ * @param ipaddr remote IP address to connect with.
+ *
+ * @return lwIP error code
+ *
+ * @see raw_disconnect() and raw_sendto()
+ */
+err_t
+raw_connect(struct raw_pcb *pcb, ip_addr_t *ipaddr)
+{
+ ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->remote_ip, ipaddr);
+ return ERR_OK;
+}
+
+
+/**
+ * Set the callback function for received packets that match the
+ * raw PCB's protocol and binding.
+ *
+ * The callback function MUST either
+ * - eat the packet by calling pbuf_free() and returning non-zero. The
+ * packet will not be passed to other raw PCBs or other protocol layers.
+ * - not free the packet, and return zero. The packet will be matched
+ * against further PCBs and/or forwarded to another protocol layers.
+ *
+ * @return non-zero if the packet was free()d, zero if the packet remains
+ * available for others.
+ */
+void
+raw_recv(struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg)
+{
+ /* remember recv() callback and user data */
+ pcb->recv.ip4 = recv;
+ pcb->recv_arg = recv_arg;
+}
+
+/**
+ * Send the raw IP packet to the given address. Note that actually you cannot
+ * modify the IP headers (this is inconsistent with the receive callback where
+ * you actually get the IP headers), you can only specify the IP payload here.
+ * It requires some more changes in lwIP. (there will be a raw_send() function
+ * then.)
+ *
+ * @param pcb the raw pcb which to send
+ * @param p the IP payload to send
+ * @param ipaddr the destination address of the IP packet
+ *
+ */
+err_t
+raw_sendto(struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr)
+{
+ err_t err;
+ struct netif *netif;
+ ipX_addr_t *src_ip;
+ struct pbuf *q; /* q will be sent down the stack */
+ s16_t header_size;
+ ipX_addr_t *dst_ip = ip_2_ipX(ipaddr);
+
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_sendto\n"));
+
+ header_size = (
+#if LWIP_IPV6
+ PCB_ISIPV6(pcb) ? IP6_HLEN :
+#endif /* LWIP_IPV6 */
+ IP_HLEN);
+
+ /* not enough space to add an IP header to first pbuf in given p chain? */
+ if (pbuf_header(p, header_size)) {
+ /* allocate header in new pbuf */
+ q = pbuf_alloc(PBUF_IP, 0, PBUF_RAM);
+ /* new header pbuf could not be allocated? */
+ if (q == NULL) {
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("raw_sendto: could not allocate header\n"));
+ return ERR_MEM;
+ }
+ if (p->tot_len != 0) {
+ /* chain header q in front of given pbuf p */
+ pbuf_chain(q, p);
+ }
+ /* { first pbuf q points to header pbuf } */
+ LWIP_DEBUGF(RAW_DEBUG, ("raw_sendto: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
+ } else {
+ /* first pbuf q equals given pbuf */
+ q = p;
+ if(pbuf_header(q, -header_size)) {
+ LWIP_ASSERT("Can't restore header we just removed!", 0);
+ return ERR_MEM;
+ }
+ }
+
+ netif = ipX_route(PCB_ISIPV6(pcb), &pcb->local_ip, dst_ip);
+ if (netif == NULL) {
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: No route to "));
+ ipX_addr_debug_print(PCB_ISIPV6(pcb), RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, dst_ip);
+ /* free any temporary header pbuf allocated by pbuf_header() */
+ if (q != p) {
+ pbuf_free(q);
+ }
+ return ERR_RTE;
+ }
+
+#if IP_SOF_BROADCAST
+#if LWIP_IPV6
+ /* @todo: why does IPv6 not filter broadcast with SOF_BROADCAST enabled? */
+ if (!PCB_ISIPV6(pcb))
+#endif /* LWIP_IPV6 */
+ {
+ /* broadcast filter? */
+ if (!ip_get_option(pcb, SOF_BROADCAST) && ip_addr_isbroadcast(ipaddr, netif)) {
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_LEVEL_WARNING, ("raw_sendto: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
+ /* free any temporary header pbuf allocated by pbuf_header() */
+ if (q != p) {
+ pbuf_free(q);
+ }
+ return ERR_VAL;
+ }
+ }
+#endif /* IP_SOF_BROADCAST */
+
+ if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) {
+ /* use outgoing network interface IP address as source address */
+ src_ip = ipX_netif_get_local_ipX(PCB_ISIPV6(pcb), netif, dst_ip);
+#if LWIP_IPV6
+ if (src_ip == NULL) {
+ if (q != p) {
+ pbuf_free(q);
+ }
+ return ERR_RTE;
+ }
+#endif /* LWIP_IPV6 */
+ } else {
+ /* use RAW PCB local IP address as source address */
+ src_ip = &pcb->local_ip;
+ }
+
+ NETIF_SET_HWADDRHINT(netif, &pcb->addr_hint);
+ err = ipX_output_if(PCB_ISIPV6(pcb), q, ipX_2_ip(src_ip), ipX_2_ip(dst_ip), pcb->ttl, pcb->tos, pcb->protocol, netif);
+ NETIF_SET_HWADDRHINT(netif, NULL);
+
+ /* did we chain a header earlier? */
+ if (q != p) {
+ /* free the header */
+ pbuf_free(q);
+ }
+ return err;
+}
+
+/**
+ * Send the raw IP packet to the address given by raw_connect()
+ *
+ * @param pcb the raw pcb which to send
+ * @param p the IP payload to send
+ *
+ */
+err_t
+raw_send(struct raw_pcb *pcb, struct pbuf *p)
+{
+ return raw_sendto(pcb, p, ipX_2_ip(&pcb->remote_ip));
+}
+
+/**
+ * Remove an RAW PCB.
+ *
+ * @param pcb RAW PCB to be removed. The PCB is removed from the list of
+ * RAW PCB's and the data structure is freed from memory.
+ *
+ * @see raw_new()
+ */
+void
+raw_remove(struct raw_pcb *pcb)
+{
+ struct raw_pcb *pcb2;
+ /* pcb to be removed is first in list? */
+ if (raw_pcbs == pcb) {
+ /* make list start at 2nd pcb */
+ raw_pcbs = raw_pcbs->next;
+ /* pcb not 1st in list */
+ } else {
+ for(pcb2 = raw_pcbs; pcb2 != NULL; pcb2 = pcb2->next) {
+ /* find pcb in raw_pcbs list */
+ if (pcb2->next != NULL && pcb2->next == pcb) {
+ /* remove pcb from list */
+ pcb2->next = pcb->next;
+ }
+ }
+ }
+ memp_free(MEMP_RAW_PCB, pcb);
+}
+
+/**
+ * Create a RAW PCB.
+ *
+ * @return The RAW PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @param proto the protocol number of the IPs payload (e.g. IP_PROTO_ICMP)
+ *
+ * @see raw_remove()
+ */
+struct raw_pcb *
+raw_new(u8_t proto)
+{
+ struct raw_pcb *pcb;
+
+ LWIP_DEBUGF(RAW_DEBUG | LWIP_DBG_TRACE, ("raw_new\n"));
+
+ pcb = (struct raw_pcb *)memp_malloc(MEMP_RAW_PCB);
+ /* could allocate RAW PCB? */
+ if (pcb != NULL) {
+ /* initialize PCB to all zeroes */
+ memset(pcb, 0, sizeof(struct raw_pcb));
+ pcb->protocol = proto;
+ pcb->ttl = RAW_TTL;
+ pcb->next = raw_pcbs;
+ raw_pcbs = pcb;
+ }
+ return pcb;
+}
+
+#if LWIP_IPV6
+/**
+ * Create a RAW PCB for IPv6.
+ *
+ * @return The RAW PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @param proto the protocol number (next header) of the IPv6 packet payload
+ * (e.g. IP6_NEXTH_ICMP6)
+ *
+ * @see raw_remove()
+ */
+struct raw_pcb *
+raw_new_ip6(u8_t proto)
+{
+ struct raw_pcb *pcb;
+ pcb = raw_new(proto);
+ ip_set_v6(pcb, 1);
+ return pcb;
+}
+#endif /* LWIP_IPV6 */
+
+#endif /* LWIP_RAW */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/snmp/Makefile.kup b/src/VBox/Devices/Network/lwip-new/src/core/snmp/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/snmp/Makefile.kup
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/snmp/asn1_dec.c b/src/VBox/Devices/Network/lwip-new/src/core/snmp/asn1_dec.c
new file mode 100644
index 00000000..1d565820
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/snmp/asn1_dec.c
@@ -0,0 +1,657 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) decoding
+ *
+ * @todo not optimised (yet), favor correctness over speed, favor speed over size
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp_asn1.h"
+
+/**
+ * Retrieves type field from incoming pbuf chain.
+ *
+ * @param p points to a pbuf holding an ASN1 coded type field
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded type field
+ * @param type return ASN1 type
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ *type = *msg_ptr;
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes length field from incoming pbuf chain into host length.
+ *
+ * @param p points to a pbuf holding an ASN1 coded length
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded length
+ * @param octets_used returns number of octets used by the length code
+ * @param length return host order length, upto 64k
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ if (*msg_ptr < 0x80)
+ {
+ /* primitive definite length format */
+ *octets_used = 1;
+ *length = *msg_ptr;
+ return ERR_OK;
+ }
+ else if (*msg_ptr == 0x80)
+ {
+ /* constructed indefinite length format, termination with two zero octets */
+ u8_t zeros;
+ u8_t i;
+
+ *length = 0;
+ zeros = 0;
+ while (zeros != 2)
+ {
+ i = 2;
+ while (i > 0)
+ {
+ i--;
+ (*length) += 1;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ if (*msg_ptr == 0)
+ {
+ zeros++;
+ if (zeros == 2)
+ {
+ /* stop while (i > 0) */
+ i = 0;
+ }
+ }
+ else
+ {
+ zeros = 0;
+ }
+ }
+ }
+ *octets_used = 1;
+ return ERR_OK;
+ }
+ else if (*msg_ptr == 0x81)
+ {
+ /* constructed definite length format, one octet */
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ *length = *msg_ptr;
+ *octets_used = 2;
+ return ERR_OK;
+ }
+ else if (*msg_ptr == 0x82)
+ {
+ u8_t i;
+
+ /* constructed definite length format, two octets */
+ i = 2;
+ while (i > 0)
+ {
+ i--;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ if (i == 0)
+ {
+ /* least significant length octet */
+ *length |= *msg_ptr;
+ }
+ else
+ {
+ /* most significant length octet */
+ *length = (*msg_ptr) << 8;
+ }
+ }
+ *octets_used = 3;
+ return ERR_OK;
+ }
+ else
+ {
+ /* constructed definite length format 3..127 octets, this is too big (>64k) */
+ /** @todo: do we need to accept inefficient codings with many leading zero's? */
+ *octets_used = 1 + ((*msg_ptr) & 0x7f);
+ return ERR_ARG;
+ }
+ }
+ p = p->next;
+ }
+
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes positive integer (counter, gauge, timeticks) into u32_t.
+ *
+ * @param p points to a pbuf holding an ASN1 coded integer
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+err_t
+snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ if ((len > 0) && (len < 6))
+ {
+ /* start from zero */
+ *value = 0;
+ if (*msg_ptr & 0x80)
+ {
+ /* negative, expecting zero sign bit! */
+ return ERR_ARG;
+ }
+ else
+ {
+ /* positive */
+ if ((len > 1) && (*msg_ptr == 0))
+ {
+ /* skip leading "sign byte" octet 0x00 */
+ len--;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ }
+ /* OR octets with value */
+ while (len > 1)
+ {
+ len--;
+ *value |= *msg_ptr;
+ *value <<= 8;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ *value |= *msg_ptr;
+ return ERR_OK;
+ }
+ else
+ {
+ return ERR_ARG;
+ }
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes integer into s32_t.
+ *
+ * @param p points to a pbuf holding an ASN1 coded integer
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded integer
+ * @param len length of the coded integer field
+ * @param value return host order integer
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ *
+ * @note ASN coded integers are _always_ signed!
+ */
+err_t
+snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+#if BYTE_ORDER == LITTLE_ENDIAN
+ u8_t *lsb_ptr = (u8_t*)value;
+#endif
+#if BYTE_ORDER == BIG_ENDIAN
+ u8_t *lsb_ptr = (u8_t*)value + sizeof(s32_t) - 1;
+#endif
+ u8_t sign;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ if ((len > 0) && (len < 5))
+ {
+ if (*msg_ptr & 0x80)
+ {
+ /* negative, start from -1 */
+ *value = -1;
+ sign = 1;
+ }
+ else
+ {
+ /* positive, start from 0 */
+ *value = 0;
+ sign = 0;
+ }
+ /* OR/AND octets with value */
+ while (len > 1)
+ {
+ len--;
+ if (sign)
+ {
+ *lsb_ptr &= *msg_ptr;
+ *value <<= 8;
+ *lsb_ptr |= 255;
+ }
+ else
+ {
+ *lsb_ptr |= *msg_ptr;
+ *value <<= 8;
+ }
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ if (sign)
+ {
+ *lsb_ptr &= *msg_ptr;
+ }
+ else
+ {
+ *lsb_ptr |= *msg_ptr;
+ }
+ return ERR_OK;
+ }
+ else
+ {
+ return ERR_ARG;
+ }
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes object identifier from incoming message into array of s32_t.
+ *
+ * @param p points to a pbuf holding an ASN1 coded object identifier
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded object identifier
+ * @param len length of the coded object identifier
+ * @param oid return object identifier struct
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+ s32_t *oid_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ oid->len = 0;
+ oid_ptr = &oid->id[0];
+ if (len > 0)
+ {
+ /* first compressed octet */
+ if (*msg_ptr == 0x2B)
+ {
+ /* (most) common case 1.3 (iso.org) */
+ *oid_ptr = 1;
+ oid_ptr++;
+ *oid_ptr = 3;
+ oid_ptr++;
+ }
+ else if (*msg_ptr < 40)
+ {
+ *oid_ptr = 0;
+ oid_ptr++;
+ *oid_ptr = *msg_ptr;
+ oid_ptr++;
+ }
+ else if (*msg_ptr < 80)
+ {
+ *oid_ptr = 1;
+ oid_ptr++;
+ *oid_ptr = (*msg_ptr) - 40;
+ oid_ptr++;
+ }
+ else
+ {
+ *oid_ptr = 2;
+ oid_ptr++;
+ *oid_ptr = (*msg_ptr) - 80;
+ oid_ptr++;
+ }
+ oid->len = 2;
+ }
+ else
+ {
+ /* accepting zero length identifiers e.g. for
+ getnext operation. uncommon but valid */
+ return ERR_OK;
+ }
+ len--;
+ if (len > 0)
+ {
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ while ((len > 0) && (oid->len < LWIP_SNMP_OBJ_ID_LEN))
+ {
+ /* sub-identifier uses multiple octets */
+ if (*msg_ptr & 0x80)
+ {
+ s32_t sub_id = 0;
+
+ while ((*msg_ptr & 0x80) && (len > 1))
+ {
+ len--;
+ sub_id = (sub_id << 7) + (*msg_ptr & ~0x80);
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ if (!(*msg_ptr & 0x80) && (len > 0))
+ {
+ /* last octet sub-identifier */
+ len--;
+ sub_id = (sub_id << 7) + *msg_ptr;
+ *oid_ptr = sub_id;
+ }
+ }
+ else
+ {
+ /* !(*msg_ptr & 0x80) sub-identifier uses single octet */
+ len--;
+ *oid_ptr = *msg_ptr;
+ }
+ if (len > 0)
+ {
+ /* remaining oid bytes available ... */
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ oid_ptr++;
+ oid->len++;
+ }
+ if (len == 0)
+ {
+ /* len == 0, end of oid */
+ return ERR_OK;
+ }
+ else
+ {
+ /* len > 0, oid->len == LWIP_SNMP_OBJ_ID_LEN or malformed encoding */
+ return ERR_ARG;
+ }
+
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Decodes (copies) raw data (ip-addresses, octet strings, opaque encoding)
+ * from incoming message into array.
+ *
+ * @param p points to a pbuf holding an ASN1 coded raw data
+ * @param ofs points to the offset within the pbuf chain of the ASN1 coded raw data
+ * @param len length of the coded raw data (zero is valid, e.g. empty string!)
+ * @param raw_len length of the raw return value
+ * @param raw return raw bytes
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) decode
+ */
+err_t
+snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ if (len > 0)
+ {
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ if (raw_len >= len)
+ {
+ while (len > 1)
+ {
+ /* copy len - 1 octets */
+ len--;
+ *raw = *msg_ptr;
+ raw++;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ /* copy last octet */
+ *raw = *msg_ptr;
+ return ERR_OK;
+ }
+ else
+ {
+ /* raw_len < len, not enough dst space */
+ return ERR_ARG;
+ }
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+ }
+ else
+ {
+ /* len == 0, empty string */
+ return ERR_OK;
+ }
+}
+
+#endif /* LWIP_SNMP */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/snmp/asn1_enc.c b/src/VBox/Devices/Network/lwip-new/src/core/snmp/asn1_enc.c
new file mode 100644
index 00000000..64dfc5f6
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/snmp/asn1_enc.c
@@ -0,0 +1,611 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) encoding
+ *
+ * @todo not optimised (yet), favor correctness over speed, favor speed over size
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp_asn1.h"
+
+/**
+ * Returns octet count for length.
+ *
+ * @param length
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed)
+{
+ if (length < 0x80U)
+ {
+ *octets_needed = 1;
+ }
+ else if (length < 0x100U)
+ {
+ *octets_needed = 2;
+ }
+ else
+ {
+ *octets_needed = 3;
+ }
+}
+
+/**
+ * Returns octet count for an u32_t.
+ *
+ * @param value
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed. E.g. +0xFFFF is coded
+ * as 0x00,0xFF,0xFF. Note the leading sign octet. A positive value
+ * of 0xFFFFFFFF is preceded with 0x00 and the length is 5 octets!!
+ */
+void
+snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed)
+{
+ if (value < 0x80UL)
+ {
+ *octets_needed = 1;
+ }
+ else if (value < 0x8000UL)
+ {
+ *octets_needed = 2;
+ }
+ else if (value < 0x800000UL)
+ {
+ *octets_needed = 3;
+ }
+ else if (value < 0x80000000UL)
+ {
+ *octets_needed = 4;
+ }
+ else
+ {
+ *octets_needed = 5;
+ }
+}
+
+/**
+ * Returns octet count for an s32_t.
+ *
+ * @param value
+ * @param octets_needed points to the return value
+ *
+ * @note ASN coded integers are _always_ signed.
+ */
+void
+snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed)
+{
+ if (value < 0)
+ {
+ value = ~value;
+ }
+ if (value < 0x80L)
+ {
+ *octets_needed = 1;
+ }
+ else if (value < 0x8000L)
+ {
+ *octets_needed = 2;
+ }
+ else if (value < 0x800000L)
+ {
+ *octets_needed = 3;
+ }
+ else
+ {
+ *octets_needed = 4;
+ }
+}
+
+/**
+ * Returns octet count for an object identifier.
+ *
+ * @param ident_len object identifier array length
+ * @param ident points to object identifier array
+ * @param octets_needed points to the return value
+ */
+void
+snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed)
+{
+ s32_t sub_id;
+ u8_t cnt;
+
+ cnt = 0;
+ if (ident_len > 1)
+ {
+ /* compressed prefix in one octet */
+ cnt++;
+ ident_len -= 2;
+ ident += 2;
+ }
+ while(ident_len > 0)
+ {
+ ident_len--;
+ sub_id = *ident;
+
+ sub_id >>= 7;
+ cnt++;
+ while(sub_id > 0)
+ {
+ sub_id >>= 7;
+ cnt++;
+ }
+ ident++;
+ }
+ *octets_needed = cnt;
+}
+
+/**
+ * Encodes ASN type field into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode value into
+ * @param ofs points to the offset within the pbuf chain
+ * @param type input ASN1 type
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+ *msg_ptr = type;
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes host order length field into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode length into
+ * @param ofs points to the offset within the pbuf chain
+ * @param length is the host order length to be encoded
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ if (length < 0x80)
+ {
+ *msg_ptr = (u8_t)length;
+ return ERR_OK;
+ }
+ else if (length < 0x100)
+ {
+ *msg_ptr = 0x81;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ *msg_ptr = (u8_t)length;
+ return ERR_OK;
+ }
+ else
+ {
+ u8_t i;
+
+ /* length >= 0x100 && length <= 0xFFFF */
+ *msg_ptr = 0x82;
+ i = 2;
+ while (i > 0)
+ {
+ i--;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ if (i == 0)
+ {
+ /* least significant length octet */
+ *msg_ptr = (u8_t)length;
+ }
+ else
+ {
+ /* most significant length octet */
+ *msg_ptr = (u8_t)(length >> 8);
+ }
+ }
+ return ERR_OK;
+ }
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes u32_t (counter, gauge, timeticks) into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode value into
+ * @param ofs points to the offset within the pbuf chain
+ * @param octets_needed encoding length (from snmp_asn1_enc_u32t_cnt())
+ * @param value is the host order u32_t value to be encoded
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_u32t_cnt()
+ */
+err_t
+snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ if (octets_needed == 5)
+ {
+ /* not enough bits in 'value' add leading 0x00 */
+ octets_needed--;
+ *msg_ptr = 0x00;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ while (octets_needed > 1)
+ {
+ octets_needed--;
+ *msg_ptr = (u8_t)(value >> (octets_needed << 3));
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ /* (only) one least significant octet */
+ *msg_ptr = (u8_t)value;
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes s32_t integer into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode value into
+ * @param ofs points to the offset within the pbuf chain
+ * @param octets_needed encoding length (from snmp_asn1_enc_s32t_cnt())
+ * @param value is the host order s32_t value to be encoded
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ *
+ * @see snmp_asn1_enc_s32t_cnt()
+ */
+err_t
+snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ while (octets_needed > 1)
+ {
+ octets_needed--;
+ *msg_ptr = (u8_t)(value >> (octets_needed << 3));
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ /* (only) one least significant octet */
+ *msg_ptr = (u8_t)value;
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes object identifier into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode oid into
+ * @param ofs points to the offset within the pbuf chain
+ * @param ident_len object identifier array length
+ * @param ident points to object identifier array
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ if (ident_len > 1)
+ {
+ if ((ident[0] == 1) && (ident[1] == 3))
+ {
+ /* compressed (most common) prefix .iso.org */
+ *msg_ptr = 0x2b;
+ }
+ else
+ {
+ /* calculate prefix */
+ *msg_ptr = (u8_t)((ident[0] * 40) + ident[1]);
+ }
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ ident_len -= 2;
+ ident += 2;
+ }
+ else
+ {
+/* @bug: allow empty varbinds for symmetry (we must decode them for getnext), allow partial compression?? */
+ /* ident_len <= 1, at least we need zeroDotZero (0.0) (ident_len == 2) */
+ return ERR_ARG;
+ }
+ while (ident_len > 0)
+ {
+ s32_t sub_id;
+ u8_t shift, tail;
+
+ ident_len--;
+ sub_id = *ident;
+ tail = 0;
+ shift = 28;
+ while(shift > 0)
+ {
+ u8_t code;
+
+ code = (u8_t)(sub_id >> shift);
+ if ((code != 0) || (tail != 0))
+ {
+ tail = 1;
+ *msg_ptr = code | 0x80;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ shift -= 7;
+ }
+ *msg_ptr = (u8_t)sub_id & 0x7F;
+ if (ident_len > 0)
+ {
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ /* proceed to next sub-identifier */
+ ident++;
+ }
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+/**
+ * Encodes raw data (octet string, opaque) into a pbuf chained ASN1 msg.
+ *
+ * @param p points to output pbuf to encode raw data into
+ * @param ofs points to the offset within the pbuf chain
+ * @param raw_len raw data length
+ * @param raw points raw data
+ * @return ERR_OK if successfull, ERR_ARG if we can't (or won't) encode
+ */
+err_t
+snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw)
+{
+ u16_t plen, base;
+ u8_t *msg_ptr;
+
+ plen = 0;
+ while (p != NULL)
+ {
+ base = plen;
+ plen += p->len;
+ if (ofs < plen)
+ {
+ msg_ptr = (u8_t*)p->payload;
+ msg_ptr += ofs - base;
+
+ while (raw_len > 1)
+ {
+ /* copy raw_len - 1 octets */
+ raw_len--;
+ *msg_ptr = *raw;
+ raw++;
+ ofs += 1;
+ if (ofs >= plen)
+ {
+ /* next octet in next pbuf */
+ p = p->next;
+ if (p == NULL) { return ERR_ARG; }
+ msg_ptr = (u8_t*)p->payload;
+ plen += p->len;
+ }
+ else
+ {
+ /* next octet in same pbuf */
+ msg_ptr++;
+ }
+ }
+ if (raw_len > 0)
+ {
+ /* copy last or single octet */
+ *msg_ptr = *raw;
+ }
+ return ERR_OK;
+ }
+ p = p->next;
+ }
+ /* p == NULL, ofs >= plen */
+ return ERR_ARG;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/snmp/mib2.c b/src/VBox/Devices/Network/lwip-new/src/core/snmp/mib2.c
new file mode 100644
index 00000000..fe1bf6d7
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/snmp/mib2.c
@@ -0,0 +1,4146 @@
+/**
+ * @file
+ * Management Information Base II (RFC1213) objects and functions.
+ *
+ * @note the object identifiers for this MIB-2 and private MIB tree
+ * must be kept in sorted ascending order. This to ensure correct getnext operation.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp.h"
+#include "lwip/netif.h"
+#include "lwip/ip.h"
+#include "lwip/ip_frag.h"
+#include "lwip/mem.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/udp.h"
+#include "lwip/snmp_asn1.h"
+#include "lwip/snmp_structs.h"
+#include "lwip/sys.h"
+#include "netif/etharp.h"
+
+/**
+ * IANA assigned enterprise ID for lwIP is 26381
+ * @see http://www.iana.org/assignments/enterprise-numbers
+ *
+ * @note this enterprise ID is assigned to the lwIP project,
+ * all object identifiers living under this ID are assigned
+ * by the lwIP maintainers (contact Christiaan Simons)!
+ * @note don't change this define, use snmp_set_sysobjid()
+ *
+ * If you need to create your own private MIB you'll need
+ * to apply for your own enterprise ID with IANA:
+ * http://www.iana.org/numbers.html
+ */
+#define SNMP_ENTERPRISE_ID 26381
+#define SNMP_SYSOBJID_LEN 7
+#define SNMP_SYSOBJID {1, 3, 6, 1, 4, 1, SNMP_ENTERPRISE_ID}
+
+#ifndef SNMP_SYSSERVICES
+#define SNMP_SYSSERVICES ((1 << 6) | (1 << 3) | ((IP_FORWARD) << 2))
+#endif
+
+#ifndef SNMP_GET_SYSUPTIME
+#define SNMP_GET_SYSUPTIME(sysuptime) (sysuptime = (sys_now() / 10))
+#endif
+
+static void system_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void system_get_value(struct obj_def *od, u16_t len, void *value);
+static u8_t system_set_test(struct obj_def *od, u16_t len, void *value);
+static void system_set_value(struct obj_def *od, u16_t len, void *value);
+static void interfaces_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void interfaces_get_value(struct obj_def *od, u16_t len, void *value);
+static void ifentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void ifentry_get_value(struct obj_def *od, u16_t len, void *value);
+#if !SNMP_SAFE_REQUESTS
+static u8_t ifentry_set_test (struct obj_def *od, u16_t len, void *value);
+static void ifentry_set_value (struct obj_def *od, u16_t len, void *value);
+#endif /* SNMP_SAFE_REQUESTS */
+static void atentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void atentry_get_value(struct obj_def *od, u16_t len, void *value);
+static void ip_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void ip_get_value(struct obj_def *od, u16_t len, void *value);
+static u8_t ip_set_test(struct obj_def *od, u16_t len, void *value);
+static void ip_addrentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void ip_addrentry_get_value(struct obj_def *od, u16_t len, void *value);
+static void ip_rteentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void ip_rteentry_get_value(struct obj_def *od, u16_t len, void *value);
+static void ip_ntomentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void ip_ntomentry_get_value(struct obj_def *od, u16_t len, void *value);
+static void icmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void icmp_get_value(struct obj_def *od, u16_t len, void *value);
+#if LWIP_TCP
+static void tcp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void tcp_get_value(struct obj_def *od, u16_t len, void *value);
+#ifdef THIS_SEEMS_UNUSED
+static void tcpconnentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void tcpconnentry_get_value(struct obj_def *od, u16_t len, void *value);
+#endif
+#endif
+static void udp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void udp_get_value(struct obj_def *od, u16_t len, void *value);
+static void udpentry_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void udpentry_get_value(struct obj_def *od, u16_t len, void *value);
+static void snmp_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+static void snmp_get_value(struct obj_def *od, u16_t len, void *value);
+static u8_t snmp_set_test(struct obj_def *od, u16_t len, void *value);
+static void snmp_set_value(struct obj_def *od, u16_t len, void *value);
+
+
+/* snmp .1.3.6.1.2.1.11 */
+const mib_scalar_node snmp_scalar = {
+ &snmp_get_object_def,
+ &snmp_get_value,
+ &snmp_set_test,
+ &snmp_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t snmp_ids[28] = {
+ 1, 2, 3, 4, 5, 6, 8, 9, 10, 11, 12, 13, 14, 15, 16,
+ 17, 18, 19, 20, 21, 22, 24, 25, 26, 27, 28, 29, 30
+};
+struct mib_node* const snmp_nodes[28] = {
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar,
+ (struct mib_node*)&snmp_scalar, (struct mib_node*)&snmp_scalar
+};
+const struct mib_array_node snmp = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 28,
+ snmp_ids,
+ snmp_nodes
+};
+
+/* dot3 and EtherLike MIB not planned. (transmission .1.3.6.1.2.1.10) */
+/* historical (some say hysterical). (cmot .1.3.6.1.2.1.9) */
+/* lwIP has no EGP, thus may not implement it. (egp .1.3.6.1.2.1.8) */
+
+/* udp .1.3.6.1.2.1.7 */
+/** index root node for udpTable */
+struct mib_list_rootnode udp_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t udpentry_ids[2] = { 1, 2 };
+struct mib_node* const udpentry_nodes[2] = {
+ (struct mib_node*)&udp_root, (struct mib_node*)&udp_root,
+};
+const struct mib_array_node udpentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 2,
+ udpentry_ids,
+ udpentry_nodes
+};
+
+s32_t udptable_id = 1;
+struct mib_node* udptable_node = (struct mib_node*)&udpentry;
+struct mib_ram_array_node udptable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &udptable_id,
+ &udptable_node
+};
+
+const mib_scalar_node udp_scalar = {
+ &udp_get_object_def,
+ &udp_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t udp_ids[5] = { 1, 2, 3, 4, 5 };
+struct mib_node* const udp_nodes[5] = {
+ (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar,
+ (struct mib_node*)&udp_scalar, (struct mib_node*)&udp_scalar,
+ (struct mib_node*)&udptable
+};
+const struct mib_array_node udp = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 5,
+ udp_ids,
+ udp_nodes
+};
+
+/* tcp .1.3.6.1.2.1.6 */
+#if LWIP_TCP
+/* only if the TCP protocol is available may implement this group */
+/** index root node for tcpConnTable */
+struct mib_list_rootnode tcpconntree_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t tcpconnentry_ids[5] = { 1, 2, 3, 4, 5 };
+struct mib_node* const tcpconnentry_nodes[5] = {
+ (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root,
+ (struct mib_node*)&tcpconntree_root, (struct mib_node*)&tcpconntree_root,
+ (struct mib_node*)&tcpconntree_root
+};
+const struct mib_array_node tcpconnentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 5,
+ tcpconnentry_ids,
+ tcpconnentry_nodes
+};
+
+s32_t tcpconntable_id = 1;
+struct mib_node* tcpconntable_node = (struct mib_node*)&tcpconnentry;
+struct mib_ram_array_node tcpconntable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+/** @todo update maxlength when inserting / deleting from table
+ 0 when table is empty, 1 when more than one entry */
+ 0,
+ &tcpconntable_id,
+ &tcpconntable_node
+};
+
+const mib_scalar_node tcp_scalar = {
+ &tcp_get_object_def,
+ &tcp_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t tcp_ids[15] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 };
+struct mib_node* const tcp_nodes[15] = {
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcpconntable, (struct mib_node*)&tcp_scalar,
+ (struct mib_node*)&tcp_scalar
+};
+const struct mib_array_node tcp = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 15,
+ tcp_ids,
+ tcp_nodes
+};
+#endif
+
+/* icmp .1.3.6.1.2.1.5 */
+const mib_scalar_node icmp_scalar = {
+ &icmp_get_object_def,
+ &icmp_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t icmp_ids[26] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26 };
+struct mib_node* const icmp_nodes[26] = {
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar,
+ (struct mib_node*)&icmp_scalar, (struct mib_node*)&icmp_scalar
+};
+const struct mib_array_node icmp = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 26,
+ icmp_ids,
+ icmp_nodes
+};
+
+/** index root node for ipNetToMediaTable */
+struct mib_list_rootnode ipntomtree_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t ipntomentry_ids[4] = { 1, 2, 3, 4 };
+struct mib_node* const ipntomentry_nodes[4] = {
+ (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root,
+ (struct mib_node*)&ipntomtree_root, (struct mib_node*)&ipntomtree_root
+};
+const struct mib_array_node ipntomentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 4,
+ ipntomentry_ids,
+ ipntomentry_nodes
+};
+
+s32_t ipntomtable_id = 1;
+struct mib_node* ipntomtable_node = (struct mib_node*)&ipntomentry;
+struct mib_ram_array_node ipntomtable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &ipntomtable_id,
+ &ipntomtable_node
+};
+
+/** index root node for ipRouteTable */
+struct mib_list_rootnode iprtetree_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t iprteentry_ids[13] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13 };
+struct mib_node* const iprteentry_nodes[13] = {
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root, (struct mib_node*)&iprtetree_root,
+ (struct mib_node*)&iprtetree_root
+};
+const struct mib_array_node iprteentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 13,
+ iprteentry_ids,
+ iprteentry_nodes
+};
+
+s32_t iprtetable_id = 1;
+struct mib_node* iprtetable_node = (struct mib_node*)&iprteentry;
+struct mib_ram_array_node iprtetable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &iprtetable_id,
+ &iprtetable_node
+};
+
+/** index root node for ipAddrTable */
+struct mib_list_rootnode ipaddrtree_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t ipaddrentry_ids[5] = { 1, 2, 3, 4, 5 };
+struct mib_node* const ipaddrentry_nodes[5] = {
+ (struct mib_node*)&ipaddrtree_root,
+ (struct mib_node*)&ipaddrtree_root,
+ (struct mib_node*)&ipaddrtree_root,
+ (struct mib_node*)&ipaddrtree_root,
+ (struct mib_node*)&ipaddrtree_root
+};
+const struct mib_array_node ipaddrentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 5,
+ ipaddrentry_ids,
+ ipaddrentry_nodes
+};
+
+s32_t ipaddrtable_id = 1;
+struct mib_node* ipaddrtable_node = (struct mib_node*)&ipaddrentry;
+struct mib_ram_array_node ipaddrtable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &ipaddrtable_id,
+ &ipaddrtable_node
+};
+
+/* ip .1.3.6.1.2.1.4 */
+const mib_scalar_node ip_scalar = {
+ &ip_get_object_def,
+ &ip_get_value,
+ &ip_set_test,
+ &noleafs_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t ip_ids[23] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 };
+struct mib_node* const ip_nodes[23] = {
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ip_scalar,
+ (struct mib_node*)&ip_scalar, (struct mib_node*)&ipaddrtable,
+ (struct mib_node*)&iprtetable, (struct mib_node*)&ipntomtable,
+ (struct mib_node*)&ip_scalar
+};
+const struct mib_array_node mib2_ip = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 23,
+ ip_ids,
+ ip_nodes
+};
+
+/** index root node for atTable */
+struct mib_list_rootnode arptree_root = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t atentry_ids[3] = { 1, 2, 3 };
+struct mib_node* const atentry_nodes[3] = {
+ (struct mib_node*)&arptree_root,
+ (struct mib_node*)&arptree_root,
+ (struct mib_node*)&arptree_root
+};
+const struct mib_array_node atentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 3,
+ atentry_ids,
+ atentry_nodes
+};
+
+const s32_t attable_id = 1;
+struct mib_node* const attable_node = (struct mib_node*)&atentry;
+const struct mib_array_node attable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 1,
+ &attable_id,
+ &attable_node
+};
+
+/* at .1.3.6.1.2.1.3 */
+s32_t at_id = 1;
+struct mib_node* mib2_at_node = (struct mib_node*)&attable;
+struct mib_ram_array_node at = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &at_id,
+ &mib2_at_node
+};
+
+/** index root node for ifTable */
+struct mib_list_rootnode iflist_root = {
+ &ifentry_get_object_def,
+ &ifentry_get_value,
+#if SNMP_SAFE_REQUESTS
+ &noleafs_set_test,
+ &noleafs_set_value,
+#else /* SNMP_SAFE_REQUESTS */
+ &ifentry_set_test,
+ &ifentry_set_value,
+#endif /* SNMP_SAFE_REQUESTS */
+ MIB_NODE_LR,
+ 0,
+ NULL,
+ NULL,
+ 0
+};
+const s32_t ifentry_ids[22] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22 };
+struct mib_node* const ifentry_nodes[22] = {
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root,
+ (struct mib_node*)&iflist_root, (struct mib_node*)&iflist_root
+};
+const struct mib_array_node ifentry = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 22,
+ ifentry_ids,
+ ifentry_nodes
+};
+
+s32_t iftable_id = 1;
+struct mib_node* iftable_node = (struct mib_node*)&ifentry;
+struct mib_ram_array_node iftable = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_RA,
+ 0,
+ &iftable_id,
+ &iftable_node
+};
+
+/* interfaces .1.3.6.1.2.1.2 */
+const mib_scalar_node interfaces_scalar = {
+ &interfaces_get_object_def,
+ &interfaces_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t interfaces_ids[2] = { 1, 2 };
+struct mib_node* const interfaces_nodes[2] = {
+ (struct mib_node*)&interfaces_scalar, (struct mib_node*)&iftable
+};
+const struct mib_array_node interfaces = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 2,
+ interfaces_ids,
+ interfaces_nodes
+};
+
+
+/* 0 1 2 3 4 5 6 */
+/* system .1.3.6.1.2.1.1 */
+const mib_scalar_node sys_tem_scalar = {
+ &system_get_object_def,
+ &system_get_value,
+ &system_set_test,
+ &system_set_value,
+ MIB_NODE_SC,
+ 0
+};
+const s32_t sys_tem_ids[7] = { 1, 2, 3, 4, 5, 6, 7 };
+struct mib_node* const sys_tem_nodes[7] = {
+ (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar,
+ (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar,
+ (struct mib_node*)&sys_tem_scalar, (struct mib_node*)&sys_tem_scalar,
+ (struct mib_node*)&sys_tem_scalar
+};
+/* work around name issue with 'sys_tem', some compiler(s?) seem to reserve 'system' */
+const struct mib_array_node sys_tem = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 7,
+ sys_tem_ids,
+ sys_tem_nodes
+};
+
+/* mib-2 .1.3.6.1.2.1 */
+#if LWIP_TCP
+#define MIB2_GROUPS 8
+#else
+#define MIB2_GROUPS 7
+#endif
+const s32_t mib2_ids[MIB2_GROUPS] =
+{
+ 1,
+ 2,
+ 3,
+ 4,
+ 5,
+#if LWIP_TCP
+ 6,
+#endif
+ 7,
+ 11
+};
+struct mib_node* const mib2_nodes[MIB2_GROUPS] = {
+ (struct mib_node*)&sys_tem,
+ (struct mib_node*)&interfaces,
+ (struct mib_node*)&at,
+ (struct mib_node*)&mib2_ip,
+ (struct mib_node*)&icmp,
+#if LWIP_TCP
+ (struct mib_node*)&tcp,
+#endif
+ (struct mib_node*)&udp,
+ (struct mib_node*)&snmp
+};
+
+const struct mib_array_node mib2 = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ MIB2_GROUPS,
+ mib2_ids,
+ mib2_nodes
+};
+
+/* mgmt .1.3.6.1.2 */
+const s32_t mgmt_ids[1] = { 1 };
+struct mib_node* const mgmt_nodes[1] = { (struct mib_node*)&mib2 };
+const struct mib_array_node mgmt = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 1,
+ mgmt_ids,
+ mgmt_nodes
+};
+
+/* internet .1.3.6.1 */
+#if SNMP_PRIVATE_MIB
+/* When using a private MIB, you have to create a file 'private_mib.h' that contains
+ * a 'struct mib_array_node mib_private' which contains your MIB. */
+s32_t internet_ids[2] = { 2, 4 };
+struct mib_node* const internet_nodes[2] = { (struct mib_node*)&mgmt, (struct mib_node*)&mib_private };
+const struct mib_array_node internet = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 2,
+ internet_ids,
+ internet_nodes
+};
+#else
+const s32_t internet_ids[1] = { 2 };
+struct mib_node* const internet_nodes[1] = { (struct mib_node*)&mgmt };
+const struct mib_array_node internet = {
+ &noleafs_get_object_def,
+ &noleafs_get_value,
+ &noleafs_set_test,
+ &noleafs_set_value,
+ MIB_NODE_AR,
+ 1,
+ internet_ids,
+ internet_nodes
+};
+#endif
+
+/** mib-2.system.sysObjectID */
+static struct snmp_obj_id sysobjid = {SNMP_SYSOBJID_LEN, SNMP_SYSOBJID};
+/** enterprise ID for generic TRAPs, .iso.org.dod.internet.mgmt.mib-2.snmp */
+static struct snmp_obj_id snmpgrp_id = {7,{1,3,6,1,2,1,11}};
+/** mib-2.system.sysServices */
+static const s32_t sysservices = SNMP_SYSSERVICES;
+
+/** mib-2.system.sysDescr */
+static const u8_t sysdescr_len_default = 4;
+static const u8_t sysdescr_default[] = "lwIP";
+static const u8_t* sysdescr_len_ptr = &sysdescr_len_default;
+static const u8_t* sysdescr_ptr = &sysdescr_default[0];
+/** mib-2.system.sysContact */
+static const u8_t syscontact_len_default = 0;
+static const u8_t syscontact_default[] = "";
+static u8_t* syscontact_len_ptr = (u8_t*)&syscontact_len_default;
+static u8_t* syscontact_ptr = (u8_t*)&syscontact_default[0];
+/** mib-2.system.sysName */
+static const u8_t sysname_len_default = 8;
+static const u8_t sysname_default[] = "FQDN-unk";
+static u8_t* sysname_len_ptr = (u8_t*)&sysname_len_default;
+static u8_t* sysname_ptr = (u8_t*)&sysname_default[0];
+/** mib-2.system.sysLocation */
+static const u8_t syslocation_len_default = 0;
+static const u8_t syslocation_default[] = "";
+static u8_t* syslocation_len_ptr = (u8_t*)&syslocation_len_default;
+static u8_t* syslocation_ptr = (u8_t*)&syslocation_default[0];
+/** mib-2.snmp.snmpEnableAuthenTraps */
+static const u8_t snmpenableauthentraps_default = 2; /* disabled */
+static u8_t* snmpenableauthentraps_ptr = (u8_t*)&snmpenableauthentraps_default;
+
+/** mib-2.interfaces.ifTable.ifEntry.ifSpecific (zeroDotZero) */
+static const struct snmp_obj_id ifspecific = {2, {0, 0}};
+/** mib-2.ip.ipRouteTable.ipRouteEntry.ipRouteInfo (zeroDotZero) */
+static const struct snmp_obj_id iprouteinfo = {2, {0, 0}};
+
+
+
+/* mib-2.system counter(s) */
+static u32_t sysuptime = 0;
+
+/* mib-2.ip counter(s) */
+static u32_t ipinreceives = 0,
+ ipinhdrerrors = 0,
+ ipinaddrerrors = 0,
+ ipforwdatagrams = 0,
+ ipinunknownprotos = 0,
+ ipindiscards = 0,
+ ipindelivers = 0,
+ ipoutrequests = 0,
+ ipoutdiscards = 0,
+ ipoutnoroutes = 0,
+ ipreasmreqds = 0,
+ ipreasmoks = 0,
+ ipreasmfails = 0,
+ ipfragoks = 0,
+ ipfragfails = 0,
+ ipfragcreates = 0,
+ iproutingdiscards = 0;
+/* mib-2.icmp counter(s) */
+static u32_t icmpinmsgs = 0,
+ icmpinerrors = 0,
+ icmpindestunreachs = 0,
+ icmpintimeexcds = 0,
+ icmpinparmprobs = 0,
+ icmpinsrcquenchs = 0,
+ icmpinredirects = 0,
+ icmpinechos = 0,
+ icmpinechoreps = 0,
+ icmpintimestamps = 0,
+ icmpintimestampreps = 0,
+ icmpinaddrmasks = 0,
+ icmpinaddrmaskreps = 0,
+ icmpoutmsgs = 0,
+ icmpouterrors = 0,
+ icmpoutdestunreachs = 0,
+ icmpouttimeexcds = 0,
+ icmpoutparmprobs = 0,
+ icmpoutsrcquenchs = 0,
+ icmpoutredirects = 0,
+ icmpoutechos = 0,
+ icmpoutechoreps = 0,
+ icmpouttimestamps = 0,
+ icmpouttimestampreps = 0,
+ icmpoutaddrmasks = 0,
+ icmpoutaddrmaskreps = 0;
+/* mib-2.tcp counter(s) */
+static u32_t tcpactiveopens = 0,
+ tcppassiveopens = 0,
+ tcpattemptfails = 0,
+ tcpestabresets = 0,
+ tcpinsegs = 0,
+ tcpoutsegs = 0,
+ tcpretranssegs = 0,
+ tcpinerrs = 0,
+ tcpoutrsts = 0;
+/* mib-2.udp counter(s) */
+static u32_t udpindatagrams = 0,
+ udpnoports = 0,
+ udpinerrors = 0,
+ udpoutdatagrams = 0;
+/* mib-2.snmp counter(s) */
+static u32_t snmpinpkts = 0,
+ snmpoutpkts = 0,
+ snmpinbadversions = 0,
+ snmpinbadcommunitynames = 0,
+ snmpinbadcommunityuses = 0,
+ snmpinasnparseerrs = 0,
+ snmpintoobigs = 0,
+ snmpinnosuchnames = 0,
+ snmpinbadvalues = 0,
+ snmpinreadonlys = 0,
+ snmpingenerrs = 0,
+ snmpintotalreqvars = 0,
+ snmpintotalsetvars = 0,
+ snmpingetrequests = 0,
+ snmpingetnexts = 0,
+ snmpinsetrequests = 0,
+ snmpingetresponses = 0,
+ snmpintraps = 0,
+ snmpouttoobigs = 0,
+ snmpoutnosuchnames = 0,
+ snmpoutbadvalues = 0,
+ snmpoutgenerrs = 0,
+ snmpoutgetrequests = 0,
+ snmpoutgetnexts = 0,
+ snmpoutsetrequests = 0,
+ snmpoutgetresponses = 0,
+ snmpouttraps = 0;
+
+
+
+/* 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, const 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, const 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_sysdescr(const u8_t *str, const 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/src/VBox/Devices/Network/lwip-new/src/core/snmp/mib_structs.c b/src/VBox/Devices/Network/lwip-new/src/core/snmp/mib_structs.c
new file mode 100644
index 00000000..2f185cb4
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/snmp/mib_structs.c
@@ -0,0 +1,1174 @@
+/**
+ * @file
+ * MIB tree access/construction functions.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp_structs.h"
+#include "lwip/memp.h"
+#include "lwip/netif.h"
+
+/** .iso.org.dod.internet address prefix, @see snmp_iso_*() */
+const s32_t prefix[4] = {1, 3, 6, 1};
+
+#define NODE_STACK_SIZE (LWIP_SNMP_OBJ_ID_LEN)
+/** node stack entry (old news?) */
+struct nse
+{
+ /** right child */
+ struct mib_node* r_ptr;
+ /** right child identifier */
+ s32_t r_id;
+ /** right child next level */
+ u8_t r_nl;
+};
+static u8_t node_stack_cnt;
+static struct nse node_stack[NODE_STACK_SIZE];
+
+/**
+ * Pushes nse struct onto stack.
+ */
+static void
+push_node(struct nse* node)
+{
+ LWIP_ASSERT("node_stack_cnt < NODE_STACK_SIZE",node_stack_cnt < NODE_STACK_SIZE);
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("push_node() node=%p id=%"S32_F"\n",(void*)(node->r_ptr),node->r_id));
+ if (node_stack_cnt < NODE_STACK_SIZE)
+ {
+ node_stack[node_stack_cnt] = *node;
+ node_stack_cnt++;
+ }
+}
+
+/**
+ * Pops nse struct from stack.
+ */
+static void
+pop_node(struct nse* node)
+{
+ if (node_stack_cnt > 0)
+ {
+ node_stack_cnt--;
+ *node = node_stack[node_stack_cnt];
+ }
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("pop_node() node=%p id=%"S32_F"\n",(void *)(node->r_ptr),node->r_id));
+}
+
+/**
+ * Conversion from ifIndex to lwIP netif
+ * @param ifindex is a s32_t object sub-identifier
+ * @param netif points to returned netif struct pointer
+ */
+void
+snmp_ifindextonetif(s32_t ifindex, struct netif **netif)
+{
+ struct netif *nif = netif_list;
+ s32_t i, ifidx;
+
+ ifidx = ifindex - 1;
+ i = 0;
+ while ((nif != NULL) && (i < ifidx))
+ {
+ nif = nif->next;
+ i++;
+ }
+ *netif = nif;
+}
+
+/**
+ * Conversion from lwIP netif to ifIndex
+ * @param netif points to a netif struct
+ * @param ifidx points to s32_t object sub-identifier
+ */
+void
+snmp_netiftoifindex(struct netif *netif, s32_t *ifidx)
+{
+ struct netif *nif = netif_list;
+ u16_t i;
+
+ i = 0;
+ while ((nif != NULL) && (nif != netif))
+ {
+ nif = nif->next;
+ i++;
+ }
+ *ifidx = i+1;
+}
+
+/**
+ * Conversion from oid to lwIP ip_addr
+ * @param ident points to s32_t ident[4] input
+ * @param ip points to output struct
+ */
+void
+snmp_oidtoip(s32_t *ident, ip_addr_t *ip)
+{
+ IP4_ADDR(ip, ident[0], ident[1], ident[2], ident[3]);
+}
+
+/**
+ * Conversion from lwIP ip_addr to oid
+ * @param ip points to input struct
+ * @param ident points to s32_t ident[4] output
+ */
+void
+snmp_iptooid(ip_addr_t *ip, s32_t *ident)
+{
+ ident[0] = ip4_addr1(ip);
+ ident[1] = ip4_addr2(ip);
+ ident[2] = ip4_addr3(ip);
+ ident[3] = ip4_addr4(ip);
+}
+
+struct mib_list_node *
+snmp_mib_ln_alloc(s32_t id)
+{
+ struct mib_list_node *ln;
+
+ ln = (struct mib_list_node *)memp_malloc(MEMP_SNMP_NODE);
+ if (ln != NULL)
+ {
+ ln->prev = NULL;
+ ln->next = NULL;
+ ln->objid = id;
+ ln->nptr = NULL;
+ }
+ return ln;
+}
+
+void
+snmp_mib_ln_free(struct mib_list_node *ln)
+{
+ memp_free(MEMP_SNMP_NODE, ln);
+}
+
+struct mib_list_rootnode *
+snmp_mib_lrn_alloc(void)
+{
+ struct mib_list_rootnode *lrn;
+
+ lrn = (struct mib_list_rootnode*)memp_malloc(MEMP_SNMP_ROOTNODE);
+ if (lrn != NULL)
+ {
+ lrn->get_object_def = noleafs_get_object_def;
+ lrn->get_value = noleafs_get_value;
+ lrn->set_test = noleafs_set_test;
+ lrn->set_value = noleafs_set_value;
+ lrn->node_type = MIB_NODE_LR;
+ lrn->maxlength = 0;
+ lrn->head = NULL;
+ lrn->tail = NULL;
+ lrn->count = 0;
+ }
+ return lrn;
+}
+
+void
+snmp_mib_lrn_free(struct mib_list_rootnode *lrn)
+{
+ memp_free(MEMP_SNMP_ROOTNODE, lrn);
+}
+
+/**
+ * Inserts node in idx list in a sorted
+ * (ascending order) fashion and
+ * allocates the node if needed.
+ *
+ * @param rn points to the root node
+ * @param objid is the object sub identifier
+ * @param insn points to a pointer to the inserted node
+ * used for constructing the tree.
+ * @return -1 if failed, 1 if inserted, 2 if present.
+ */
+s8_t
+snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn)
+{
+ struct mib_list_node *nn;
+ s8_t insert;
+
+ LWIP_ASSERT("rn != NULL",rn != NULL);
+
+ /* -1 = malloc failure, 0 = not inserted, 1 = inserted, 2 = was present */
+ insert = 0;
+ if (rn->head == NULL)
+ {
+ /* empty list, add first node */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc empty list objid==%"S32_F"\n",objid));
+ nn = snmp_mib_ln_alloc(objid);
+ if (nn != NULL)
+ {
+ rn->head = nn;
+ rn->tail = nn;
+ *insn = nn;
+ insert = 1;
+ }
+ else
+ {
+ insert = -1;
+ }
+ }
+ else
+ {
+ struct mib_list_node *n;
+ /* at least one node is present */
+ n = rn->head;
+ while ((n != NULL) && (insert == 0))
+ {
+ if (n->objid == objid)
+ {
+ /* node is already there */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("node already there objid==%"S32_F"\n",objid));
+ *insn = n;
+ insert = 2;
+ }
+ else if (n->objid < objid)
+ {
+ if (n->next == NULL)
+ {
+ /* alloc and insert at the tail */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins tail objid==%"S32_F"\n",objid));
+ nn = snmp_mib_ln_alloc(objid);
+ if (nn != NULL)
+ {
+ nn->next = NULL;
+ nn->prev = n;
+ n->next = nn;
+ rn->tail = nn;
+ *insn = nn;
+ insert = 1;
+ }
+ else
+ {
+ /* insertion failure */
+ insert = -1;
+ }
+ }
+ else
+ {
+ /* there's more to explore: traverse list */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("traverse list\n"));
+ n = n->next;
+ }
+ }
+ else
+ {
+ /* n->objid > objid */
+ /* alloc and insert between n->prev and n */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("alloc ins n->prev, objid==%"S32_F", n\n",objid));
+ nn = snmp_mib_ln_alloc(objid);
+ if (nn != NULL)
+ {
+ if (n->prev == NULL)
+ {
+ /* insert at the head */
+ nn->next = n;
+ nn->prev = NULL;
+ rn->head = nn;
+ n->prev = nn;
+ }
+ else
+ {
+ /* insert in the middle */
+ nn->next = n;
+ nn->prev = n->prev;
+ n->prev->next = nn;
+ n->prev = nn;
+ }
+ *insn = nn;
+ insert = 1;
+ }
+ else
+ {
+ /* insertion failure */
+ insert = -1;
+ }
+ }
+ }
+ }
+ if (insert == 1)
+ {
+ rn->count += 1;
+ }
+ LWIP_ASSERT("insert != 0",insert != 0);
+ return insert;
+}
+
+/**
+ * Finds node in idx list and returns deletion mark.
+ *
+ * @param rn points to the root node
+ * @param objid is the object sub identifier
+ * @param fn returns pointer to found node
+ * @return 0 if not found, 1 if deletable,
+ * 2 can't delete (2 or more children), 3 not a list_node
+ */
+s8_t
+snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn)
+{
+ s8_t fc;
+ struct mib_list_node *n;
+
+ LWIP_ASSERT("rn != NULL",rn != NULL);
+ n = rn->head;
+ while ((n != NULL) && (n->objid != objid))
+ {
+ n = n->next;
+ }
+ if (n == NULL)
+ {
+ fc = 0;
+ }
+ else if (n->nptr == NULL)
+ {
+ /* leaf, can delete node */
+ fc = 1;
+ }
+ else
+ {
+ struct mib_list_rootnode *r;
+
+ if (n->nptr->node_type == MIB_NODE_LR)
+ {
+ r = (struct mib_list_rootnode *)n->nptr;
+ if (r->count > 1)
+ {
+ /* can't delete node */
+ fc = 2;
+ }
+ else
+ {
+ /* count <= 1, can delete node */
+ fc = 1;
+ }
+ }
+ else
+ {
+ /* other node type */
+ fc = 3;
+ }
+ }
+ *fn = n;
+ return fc;
+}
+
+/**
+ * Removes node from idx list
+ * if it has a single child left.
+ *
+ * @param rn points to the root node
+ * @param n points to the node to delete
+ * @return the nptr to be freed by caller
+ */
+struct mib_list_rootnode *
+snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n)
+{
+ struct mib_list_rootnode *next;
+
+ LWIP_ASSERT("rn != NULL",rn != NULL);
+ LWIP_ASSERT("n != NULL",n != NULL);
+
+ /* caller must remove this sub-tree */
+ next = (struct mib_list_rootnode*)(n->nptr);
+ rn->count -= 1;
+
+ if (n == rn->head)
+ {
+ rn->head = n->next;
+ if (n->next != NULL)
+ {
+ /* not last node, new list begin */
+ n->next->prev = NULL;
+ }
+ }
+ else if (n == rn->tail)
+ {
+ rn->tail = n->prev;
+ if (n->prev != NULL)
+ {
+ /* not last node, new list end */
+ n->prev->next = NULL;
+ }
+ }
+ else
+ {
+ /* node must be in the middle */
+ n->prev->next = n->next;
+ n->next->prev = n->prev;
+ }
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("free list objid==%"S32_F"\n",n->objid));
+ snmp_mib_ln_free(n);
+ if (rn->count == 0)
+ {
+ rn->head = NULL;
+ rn->tail = NULL;
+ }
+ return next;
+}
+
+
+
+/**
+ * Searches tree for the supplied (scalar?) object identifier.
+ *
+ * @param node points to the root of the tree ('.internet')
+ * @param ident_len the length of the supplied object identifier
+ * @param ident points to the array of sub identifiers
+ * @param np points to the found object instance (return)
+ * @return pointer to the requested parent (!) node if success, NULL otherwise
+ */
+struct mib_node *
+snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np)
+{
+ u8_t node_type, ext_level;
+
+ ext_level = 0;
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("node==%p *ident==%"S32_F"\n",(void*)node,*ident));
+ while (node != NULL)
+ {
+ node_type = node->node_type;
+ if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA))
+ {
+ struct mib_array_node *an;
+ u16_t i;
+
+ if (ident_len > 0)
+ {
+ /* array node (internal ROM or RAM, fixed length) */
+ an = (struct mib_array_node *)node;
+ i = 0;
+ while ((i < an->maxlength) && (an->objid[i] != *ident))
+ {
+ i++;
+ }
+ if (i < an->maxlength)
+ {
+ /* found it, if available proceed to child, otherwise inspect leaf */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident));
+ if (an->nptr[i] == NULL)
+ {
+ /* a scalar leaf OR table,
+ inspect remaining instance number / table index */
+ np->ident_len = ident_len;
+ np->ident = ident;
+ return (struct mib_node*)an;
+ }
+ else
+ {
+ /* follow next child pointer */
+ ident++;
+ ident_len--;
+ node = an->nptr[i];
+ }
+ }
+ else
+ {
+ /* search failed, identifier mismatch (nosuchname) */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed *ident==%"S32_F"\n",*ident));
+ return NULL;
+ }
+ }
+ else
+ {
+ /* search failed, short object identifier (nosuchname) */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("an search failed, short object identifier\n"));
+ return NULL;
+ }
+ }
+ else if(node_type == MIB_NODE_LR)
+ {
+ struct mib_list_rootnode *lrn;
+ struct mib_list_node *ln;
+
+ if (ident_len > 0)
+ {
+ /* list root node (internal 'RAM', variable length) */
+ lrn = (struct mib_list_rootnode *)node;
+ ln = lrn->head;
+ /* iterate over list, head to tail */
+ while ((ln != NULL) && (ln->objid != *ident))
+ {
+ ln = ln->next;
+ }
+ if (ln != NULL)
+ {
+ /* found it, proceed to child */;
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident));
+ if (ln->nptr == NULL)
+ {
+ np->ident_len = ident_len;
+ np->ident = ident;
+ return (struct mib_node*)lrn;
+ }
+ else
+ {
+ /* follow next child pointer */
+ ident_len--;
+ ident++;
+ node = ln->nptr;
+ }
+ }
+ else
+ {
+ /* search failed */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed *ident==%"S32_F"\n",*ident));
+ return NULL;
+ }
+ }
+ else
+ {
+ /* search failed, short object identifier (nosuchname) */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln search failed, short object identifier\n"));
+ return NULL;
+ }
+ }
+ else if(node_type == MIB_NODE_EX)
+ {
+ struct mib_external_node *en;
+ u16_t i, len;
+
+ if (ident_len > 0)
+ {
+ /* external node (addressing and access via functions) */
+ en = (struct mib_external_node *)node;
+
+ i = 0;
+ len = en->level_length(en->addr_inf,ext_level);
+ while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) != 0))
+ {
+ i++;
+ }
+ if (i < len)
+ {
+ s32_t debug_id;
+
+ en->get_objid(en->addr_inf,ext_level,i,&debug_id);
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid==%"S32_F" *ident==%"S32_F"\n",debug_id,*ident));
+ if ((ext_level + 1) == en->tree_levels)
+ {
+ np->ident_len = ident_len;
+ np->ident = ident;
+ return (struct mib_node*)en;
+ }
+ else
+ {
+ /* found it, proceed to child */
+ ident_len--;
+ ident++;
+ ext_level++;
+ }
+ }
+ else
+ {
+ /* search failed */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed *ident==%"S32_F"\n",*ident));
+ return NULL;
+ }
+ }
+ else
+ {
+ /* search failed, short object identifier (nosuchname) */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("en search failed, short object identifier\n"));
+ return NULL;
+ }
+ }
+ else if (node_type == MIB_NODE_SC)
+ {
+ mib_scalar_node *sn;
+
+ sn = (mib_scalar_node *)node;
+ if ((ident_len == 1) && (*ident == 0))
+ {
+ np->ident_len = ident_len;
+ np->ident = ident;
+ return (struct mib_node*)sn;
+ }
+ else
+ {
+ /* search failed, short object identifier (nosuchname) */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed, invalid object identifier length\n"));
+ return NULL;
+ }
+ }
+ else
+ {
+ /* unknown node_type */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node_type %"U16_F" unkown\n",(u16_t)node_type));
+ return NULL;
+ }
+ }
+ /* done, found nothing */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("search failed node==%p\n",(void*)node));
+ return NULL;
+}
+
+/**
+ * Test table for presence of at least one table entry.
+ */
+static u8_t
+empty_table(struct mib_node *node)
+{
+ u8_t node_type;
+ u8_t empty = 0;
+
+ if (node != NULL)
+ {
+ node_type = node->node_type;
+ if (node_type == MIB_NODE_LR)
+ {
+ struct mib_list_rootnode *lrn;
+ lrn = (struct mib_list_rootnode *)node;
+ if ((lrn->count == 0) || (lrn->head == NULL))
+ {
+ empty = 1;
+ }
+ }
+ else if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA))
+ {
+ struct mib_array_node *an;
+ an = (struct mib_array_node *)node;
+ if ((an->maxlength == 0) || (an->nptr == NULL))
+ {
+ empty = 1;
+ }
+ }
+ else if (node_type == MIB_NODE_EX)
+ {
+ struct mib_external_node *en;
+ en = (struct mib_external_node *)node;
+ if (en->tree_levels == 0)
+ {
+ empty = 1;
+ }
+ }
+ }
+ return empty;
+}
+
+/**
+ * Tree expansion.
+ */
+struct mib_node *
+snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret)
+{
+ u8_t node_type, ext_level, climb_tree;
+
+ ext_level = 0;
+ /* reset node stack */
+ node_stack_cnt = 0;
+ while (node != NULL)
+ {
+ climb_tree = 0;
+ node_type = node->node_type;
+ if ((node_type == MIB_NODE_AR) || (node_type == MIB_NODE_RA))
+ {
+ struct mib_array_node *an;
+ u16_t i;
+
+ /* array node (internal ROM or RAM, fixed length) */
+ an = (struct mib_array_node *)node;
+ if (ident_len > 0)
+ {
+ i = 0;
+ while ((i < an->maxlength) && (an->objid[i] < *ident))
+ {
+ i++;
+ }
+ if (i < an->maxlength)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("an->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,an->objid[i],*ident));
+ /* add identifier to oidret */
+ oidret->id[oidret->len] = an->objid[i];
+ (oidret->len)++;
+
+ if (an->nptr[i] == NULL)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n"));
+ /* leaf node (e.g. in a fixed size table) */
+ if (an->objid[i] > *ident)
+ {
+ return (struct mib_node*)an;
+ }
+ else if ((i + 1) < an->maxlength)
+ {
+ /* an->objid[i] == *ident */
+ (oidret->len)--;
+ oidret->id[oidret->len] = an->objid[i + 1];
+ (oidret->len)++;
+ return (struct mib_node*)an;
+ }
+ else
+ {
+ /* (i + 1) == an->maxlength */
+ (oidret->len)--;
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ u8_t j;
+ struct nse cur_node;
+
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n"));
+ /* non-leaf, store right child ptr and id */
+ LWIP_ASSERT("i < 0xff", i < 0xff);
+ j = (u8_t)i + 1;
+ while ((j < an->maxlength) && (empty_table(an->nptr[j])))
+ {
+ j++;
+ }
+ if (j < an->maxlength)
+ {
+ cur_node.r_ptr = an->nptr[j];
+ cur_node.r_id = an->objid[j];
+ cur_node.r_nl = 0;
+ }
+ else
+ {
+ cur_node.r_ptr = NULL;
+ }
+ push_node(&cur_node);
+ if (an->objid[i] == *ident)
+ {
+ ident_len--;
+ ident++;
+ }
+ else
+ {
+ /* an->objid[i] < *ident */
+ ident_len = 0;
+ }
+ /* follow next child pointer */
+ node = an->nptr[i];
+ }
+ }
+ else
+ {
+ /* i == an->maxlength */
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ u8_t j;
+ /* ident_len == 0, complete with leftmost '.thing' */
+ j = 0;
+ while ((j < an->maxlength) && empty_table(an->nptr[j]))
+ {
+ j++;
+ }
+ if (j < an->maxlength)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("left an->objid[j]==%"S32_F"\n",an->objid[j]));
+ oidret->id[oidret->len] = an->objid[j];
+ (oidret->len)++;
+ if (an->nptr[j] == NULL)
+ {
+ /* leaf node */
+ return (struct mib_node*)an;
+ }
+ else
+ {
+ /* no leaf, continue */
+ node = an->nptr[j];
+ }
+ }
+ else
+ {
+ /* j == an->maxlength */
+ climb_tree = 1;
+ }
+ }
+ }
+ else if(node_type == MIB_NODE_LR)
+ {
+ struct mib_list_rootnode *lrn;
+ struct mib_list_node *ln;
+
+ /* list root node (internal 'RAM', variable length) */
+ lrn = (struct mib_list_rootnode *)node;
+ if (ident_len > 0)
+ {
+ ln = lrn->head;
+ /* iterate over list, head to tail */
+ while ((ln != NULL) && (ln->objid < *ident))
+ {
+ ln = ln->next;
+ }
+ if (ln != NULL)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("ln->objid==%"S32_F" *ident==%"S32_F"\n",ln->objid,*ident));
+ oidret->id[oidret->len] = ln->objid;
+ (oidret->len)++;
+ if (ln->nptr == NULL)
+ {
+ /* leaf node */
+ if (ln->objid > *ident)
+ {
+ return (struct mib_node*)lrn;
+ }
+ else if (ln->next != NULL)
+ {
+ /* ln->objid == *ident */
+ (oidret->len)--;
+ oidret->id[oidret->len] = ln->next->objid;
+ (oidret->len)++;
+ return (struct mib_node*)lrn;
+ }
+ else
+ {
+ /* ln->next == NULL */
+ (oidret->len)--;
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ struct mib_list_node *jn;
+ struct nse cur_node;
+
+ /* non-leaf, store right child ptr and id */
+ jn = ln->next;
+ while ((jn != NULL) && empty_table(jn->nptr))
+ {
+ jn = jn->next;
+ }
+ if (jn != NULL)
+ {
+ cur_node.r_ptr = jn->nptr;
+ cur_node.r_id = jn->objid;
+ cur_node.r_nl = 0;
+ }
+ else
+ {
+ cur_node.r_ptr = NULL;
+ }
+ push_node(&cur_node);
+ if (ln->objid == *ident)
+ {
+ ident_len--;
+ ident++;
+ }
+ else
+ {
+ /* ln->objid < *ident */
+ ident_len = 0;
+ }
+ /* follow next child pointer */
+ node = ln->nptr;
+ }
+
+ }
+ else
+ {
+ /* ln == NULL */
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ struct mib_list_node *jn;
+ /* ident_len == 0, complete with leftmost '.thing' */
+ jn = lrn->head;
+ while ((jn != NULL) && empty_table(jn->nptr))
+ {
+ jn = jn->next;
+ }
+ if (jn != NULL)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("left jn->objid==%"S32_F"\n",jn->objid));
+ oidret->id[oidret->len] = jn->objid;
+ (oidret->len)++;
+ if (jn->nptr == NULL)
+ {
+ /* leaf node */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("jn->nptr == NULL\n"));
+ return (struct mib_node*)lrn;
+ }
+ else
+ {
+ /* no leaf, continue */
+ node = jn->nptr;
+ }
+ }
+ else
+ {
+ /* jn == NULL */
+ climb_tree = 1;
+ }
+ }
+ }
+ else if(node_type == MIB_NODE_EX)
+ {
+ struct mib_external_node *en;
+ s32_t ex_id;
+
+ /* external node (addressing and access via functions) */
+ en = (struct mib_external_node *)node;
+ if (ident_len > 0)
+ {
+ u16_t i, len;
+
+ i = 0;
+ len = en->level_length(en->addr_inf,ext_level);
+ while ((i < len) && (en->ident_cmp(en->addr_inf,ext_level,i,*ident) < 0))
+ {
+ i++;
+ }
+ if (i < len)
+ {
+ /* add identifier to oidret */
+ en->get_objid(en->addr_inf,ext_level,i,&ex_id);
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("en->objid[%"U16_F"]==%"S32_F" *ident==%"S32_F"\n",i,ex_id,*ident));
+ oidret->id[oidret->len] = ex_id;
+ (oidret->len)++;
+
+ if ((ext_level + 1) == en->tree_levels)
+ {
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("leaf node\n"));
+ /* leaf node */
+ if (ex_id > *ident)
+ {
+ return (struct mib_node*)en;
+ }
+ else if ((i + 1) < len)
+ {
+ /* ex_id == *ident */
+ en->get_objid(en->addr_inf,ext_level,i + 1,&ex_id);
+ (oidret->len)--;
+ oidret->id[oidret->len] = ex_id;
+ (oidret->len)++;
+ return (struct mib_node*)en;
+ }
+ else
+ {
+ /* (i + 1) == len */
+ (oidret->len)--;
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ u8_t j;
+ struct nse cur_node;
+
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("non-leaf node\n"));
+ /* non-leaf, store right child ptr and id */
+ LWIP_ASSERT("i < 0xff", i < 0xff);
+ j = (u8_t)i + 1;
+ if (j < len)
+ {
+ /* right node is the current external node */
+ cur_node.r_ptr = node;
+ en->get_objid(en->addr_inf,ext_level,j,&cur_node.r_id);
+ cur_node.r_nl = ext_level + 1;
+ }
+ else
+ {
+ cur_node.r_ptr = NULL;
+ }
+ push_node(&cur_node);
+ if (en->ident_cmp(en->addr_inf,ext_level,i,*ident) == 0)
+ {
+ ident_len--;
+ ident++;
+ }
+ else
+ {
+ /* external id < *ident */
+ ident_len = 0;
+ }
+ /* proceed to child */
+ ext_level++;
+ }
+ }
+ else
+ {
+ /* i == len (en->level_len()) */
+ climb_tree = 1;
+ }
+ }
+ else
+ {
+ /* ident_len == 0, complete with leftmost '.thing' */
+ en->get_objid(en->addr_inf,ext_level,0,&ex_id);
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("left en->objid==%"S32_F"\n",ex_id));
+ oidret->id[oidret->len] = ex_id;
+ (oidret->len)++;
+ if ((ext_level + 1) == en->tree_levels)
+ {
+ /* leaf node */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("(ext_level + 1) == en->tree_levels\n"));
+ return (struct mib_node*)en;
+ }
+ else
+ {
+ /* no leaf, proceed to child */
+ ext_level++;
+ }
+ }
+ }
+ else if(node_type == MIB_NODE_SC)
+ {
+ mib_scalar_node *sn;
+
+ /* scalar node */
+ sn = (mib_scalar_node *)node;
+ if (ident_len > 0)
+ {
+ /* at .0 */
+ climb_tree = 1;
+ }
+ else
+ {
+ /* ident_len == 0, complete object identifier */
+ oidret->id[oidret->len] = 0;
+ (oidret->len)++;
+ /* leaf node */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("completed scalar leaf\n"));
+ return (struct mib_node*)sn;
+ }
+ }
+ else
+ {
+ /* unknown/unhandled node_type */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node_type %"U16_F" unkown\n",(u16_t)node_type));
+ return NULL;
+ }
+
+ if (climb_tree)
+ {
+ struct nse child;
+
+ /* find right child ptr */
+ child.r_ptr = NULL;
+ child.r_id = 0;
+ child.r_nl = 0;
+ while ((node_stack_cnt > 0) && (child.r_ptr == NULL))
+ {
+ pop_node(&child);
+ /* trim returned oid */
+ (oidret->len)--;
+ }
+ if (child.r_ptr != NULL)
+ {
+ /* incoming ident is useless beyond this point */
+ ident_len = 0;
+ oidret->id[oidret->len] = child.r_id;
+ oidret->len++;
+ node = child.r_ptr;
+ ext_level = child.r_nl;
+ }
+ else
+ {
+ /* tree ends here ... */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed, tree ends here\n"));
+ return NULL;
+ }
+ }
+ }
+ /* done, found nothing */
+ LWIP_DEBUGF(SNMP_MIB_DEBUG,("expand failed node==%p\n",(void*)node));
+ return NULL;
+}
+
+/**
+ * Test object identifier for the iso.org.dod.internet prefix.
+ *
+ * @param ident_len the length of the supplied object identifier
+ * @param ident points to the array of sub identifiers
+ * @return 1 if it matches, 0 otherwise
+ */
+u8_t
+snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident)
+{
+ if ((ident_len > 3) &&
+ (ident[0] == 1) && (ident[1] == 3) &&
+ (ident[2] == 6) && (ident[3] == 1))
+ {
+ return 1;
+ }
+ else
+ {
+ return 0;
+ }
+}
+
+/**
+ * Expands object identifier to the iso.org.dod.internet
+ * prefix for use in getnext operation.
+ *
+ * @param ident_len the length of the supplied object identifier
+ * @param ident points to the array of sub identifiers
+ * @param oidret points to returned expanded object identifier
+ * @return 1 if it matches, 0 otherwise
+ *
+ * @note ident_len 0 is allowed, expanding to the first known object id!!
+ */
+u8_t
+snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret)
+{
+ const s32_t *prefix_ptr;
+ s32_t *ret_ptr;
+ u8_t i;
+
+ i = 0;
+ prefix_ptr = &prefix[0];
+ ret_ptr = &oidret->id[0];
+ ident_len = ((ident_len < 4)?ident_len:4);
+ while ((i < ident_len) && ((*ident) <= (*prefix_ptr)))
+ {
+ *ret_ptr++ = *prefix_ptr++;
+ ident++;
+ i++;
+ }
+ if (i == ident_len)
+ {
+ /* match, complete missing bits */
+ while (i < 4)
+ {
+ *ret_ptr++ = *prefix_ptr++;
+ i++;
+ }
+ oidret->len = i;
+ return 1;
+ }
+ else
+ {
+ /* i != ident_len */
+ return 0;
+ }
+}
+
+#endif /* LWIP_SNMP */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/snmp/msg_in.c b/src/VBox/Devices/Network/lwip-new/src/core/snmp/msg_in.c
new file mode 100644
index 00000000..be940c62
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/snmp/msg_in.c
@@ -0,0 +1,1453 @@
+/**
+ * @file
+ * SNMP input message processing (RFC1157).
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp.h"
+#include "lwip/snmp_asn1.h"
+#include "lwip/snmp_msg.h"
+#include "lwip/snmp_structs.h"
+#include "lwip/ip_addr.h"
+#include "lwip/memp.h"
+#include "lwip/udp.h"
+#include "lwip/stats.h"
+
+#include <string.h>
+
+/* public (non-static) constants */
+/** SNMP v1 == 0 */
+const s32_t snmp_version = 0;
+/** default SNMP community string */
+const char snmp_publiccommunity[7] = "public";
+
+/* statically allocated buffers for SNMP_CONCURRENT_REQUESTS */
+struct snmp_msg_pstat msg_input_list[SNMP_CONCURRENT_REQUESTS];
+/* UDP Protocol Control Block */
+struct udp_pcb *snmp1_pcb;
+
+static void snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port);
+static err_t snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
+static err_t snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat);
+
+
+/**
+ * Starts SNMP Agent.
+ * Allocates UDP pcb and binds it to IP_ADDR_ANY port 161.
+ */
+void
+snmp_init(void)
+{
+ struct snmp_msg_pstat *msg_ps;
+ u8_t i;
+
+ snmp1_pcb = udp_new();
+ if (snmp1_pcb != NULL)
+ {
+ udp_recv(snmp1_pcb, snmp_recv, (void *)SNMP_IN_PORT);
+ udp_bind(snmp1_pcb, IP_ADDR_ANY, SNMP_IN_PORT);
+ }
+ msg_ps = &msg_input_list[0];
+ for (i=0; i<SNMP_CONCURRENT_REQUESTS; i++)
+ {
+ msg_ps->state = SNMP_MSG_EMPTY;
+ msg_ps->error_index = 0;
+ msg_ps->error_status = SNMP_ES_NOERROR;
+ msg_ps++;
+ }
+ trap_msg.pcb = snmp1_pcb;
+
+#ifdef SNMP_PRIVATE_MIB_INIT
+ /* If defined, this must be a function-like define to initialize the
+ * private MIB after the stack has been initialized.
+ * The private MIB can also be initialized in tcpip_callback (or after
+ * the stack is initialized), this define is only for convenience. */
+ SNMP_PRIVATE_MIB_INIT();
+#endif /* SNMP_PRIVATE_MIB_INIT */
+
+ /* The coldstart trap will only be output
+ if our outgoing interface is up & configured */
+ snmp_coldstart_trap();
+}
+
+static void
+snmp_error_response(struct snmp_msg_pstat *msg_ps, u8_t error)
+{
+ /* move names back from outvb to invb */
+ int v;
+ struct snmp_varbind *vbi = msg_ps->invb.head;
+ struct snmp_varbind *vbo = msg_ps->outvb.head;
+ for (v=0; v<msg_ps->vb_idx; v++) {
+ vbi->ident_len = vbo->ident_len;
+ vbo->ident_len = 0;
+ vbi->ident = vbo->ident;
+ vbo->ident = NULL;
+ vbi = vbi->next;
+ vbo = vbo->next;
+ }
+ /* free outvb */
+ snmp_varbind_list_free(&msg_ps->outvb);
+ /* we send invb back */
+ msg_ps->outvb = msg_ps->invb;
+ msg_ps->invb.head = NULL;
+ msg_ps->invb.tail = NULL;
+ msg_ps->invb.count = 0;
+ msg_ps->error_status = error;
+ /* error index must be 0 for error too big */
+ msg_ps->error_index = (error != SNMP_ES_TOOBIG) ? (1 + msg_ps->vb_idx) : 0;
+ snmp_send_response(msg_ps);
+ snmp_varbind_list_free(&msg_ps->outvb);
+ msg_ps->state = SNMP_MSG_EMPTY;
+}
+
+static void
+snmp_ok_response(struct snmp_msg_pstat *msg_ps)
+{
+ err_t err_ret;
+
+ err_ret = snmp_send_response(msg_ps);
+ if (err_ret == ERR_MEM)
+ {
+ /* serious memory problem, can't return tooBig */
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event = %"S32_F"\n",msg_ps->error_status));
+ }
+ /* free varbinds (if available) */
+ snmp_varbind_list_free(&msg_ps->invb);
+ snmp_varbind_list_free(&msg_ps->outvb);
+ msg_ps->state = SNMP_MSG_EMPTY;
+}
+
+/**
+ * Service an internal or external event for SNMP GET.
+ *
+ * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
+ * @param msg_ps points to the assosicated message process state
+ */
+static void
+snmp_msg_get_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
+{
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_get_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
+
+ if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
+ {
+ struct mib_external_node *en;
+ struct snmp_name_ptr np;
+
+ /* get_object_def() answer*/
+ en = msg_ps->ext_mib_node;
+ np = msg_ps->ext_name_ptr;
+
+ /* translate answer into a known lifeform */
+ en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
+ if ((msg_ps->ext_object_def.instance != MIB_OBJECT_NONE) &&
+ (msg_ps->ext_object_def.access & MIB_ACCESS_READ))
+ {
+ msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
+ en->get_value_q(request_id, &msg_ps->ext_object_def);
+ }
+ else
+ {
+ en->get_object_def_pc(request_id, np.ident_len, np.ident);
+ /* search failed, object id points to unknown object (nosuchname) */
+ snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
+ }
+ }
+ else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
+ {
+ struct mib_external_node *en;
+ struct snmp_varbind *vb;
+
+ /* get_value() answer */
+ en = msg_ps->ext_mib_node;
+
+ /* allocate output varbind */
+ vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
+ if (vb != NULL)
+ {
+ vb->next = NULL;
+ vb->prev = NULL;
+
+ /* move name from invb to outvb */
+ vb->ident = msg_ps->vb_ptr->ident;
+ vb->ident_len = msg_ps->vb_ptr->ident_len;
+ /* ensure this memory is refereced once only */
+ msg_ps->vb_ptr->ident = NULL;
+ msg_ps->vb_ptr->ident_len = 0;
+
+ vb->value_type = msg_ps->ext_object_def.asn_type;
+ LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff);
+ vb->value_len = (u8_t)msg_ps->ext_object_def.v_len;
+ if (vb->value_len > 0)
+ {
+ LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE);
+ vb->value = memp_malloc(MEMP_SNMP_VALUE);
+ if (vb->value != NULL)
+ {
+ en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
+ snmp_varbind_tail_add(&msg_ps->outvb, vb);
+ /* search again (if vb_idx < msg_ps->invb.count) */
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ msg_ps->vb_idx += 1;
+ }
+ else
+ {
+ en->get_value_pc(request_id, &msg_ps->ext_object_def);
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no variable space\n"));
+ msg_ps->vb_ptr->ident = vb->ident;
+ msg_ps->vb_ptr->ident_len = vb->ident_len;
+ memp_free(MEMP_SNMP_VARBIND, vb);
+ snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
+ }
+ }
+ else
+ {
+ /* vb->value_len == 0, empty value (e.g. empty string) */
+ en->get_value_a(request_id, &msg_ps->ext_object_def, 0, NULL);
+ vb->value = NULL;
+ snmp_varbind_tail_add(&msg_ps->outvb, vb);
+ /* search again (if vb_idx < msg_ps->invb.count) */
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ msg_ps->vb_idx += 1;
+ }
+ }
+ else
+ {
+ en->get_value_pc(request_id, &msg_ps->ext_object_def);
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: no outvb space\n"));
+ snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
+ }
+ }
+
+ while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
+ (msg_ps->vb_idx < msg_ps->invb.count))
+ {
+ struct mib_node *mn;
+ struct snmp_name_ptr np;
+
+ if (msg_ps->vb_idx == 0)
+ {
+ msg_ps->vb_ptr = msg_ps->invb.head;
+ }
+ else
+ {
+ msg_ps->vb_ptr = msg_ps->vb_ptr->next;
+ }
+ /** test object identifier for .iso.org.dod.internet prefix */
+ if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident))
+ {
+ mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
+ msg_ps->vb_ptr->ident + 4, &np);
+ if (mn != NULL)
+ {
+ if (mn->node_type == MIB_NODE_EX)
+ {
+ /* external object */
+ struct mib_external_node *en = (struct mib_external_node*)mn;
+
+ msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
+ /* save en && args in msg_ps!! */
+ msg_ps->ext_mib_node = en;
+ msg_ps->ext_name_ptr = np;
+
+ en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
+ }
+ else
+ {
+ /* internal object */
+ struct obj_def object_def;
+
+ msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
+ mn->get_object_def(np.ident_len, np.ident, &object_def);
+ if ((object_def.instance != MIB_OBJECT_NONE) &&
+ (object_def.access & MIB_ACCESS_READ))
+ {
+ mn = mn;
+ }
+ else
+ {
+ /* search failed, object id points to unknown object (nosuchname) */
+ mn = NULL;
+ }
+ if (mn != NULL)
+ {
+ struct snmp_varbind *vb;
+
+ msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
+ /* allocate output varbind */
+ vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
+ if (vb != NULL)
+ {
+ vb->next = NULL;
+ vb->prev = NULL;
+
+ /* move name from invb to outvb */
+ vb->ident = msg_ps->vb_ptr->ident;
+ vb->ident_len = msg_ps->vb_ptr->ident_len;
+ /* ensure this memory is refereced once only */
+ msg_ps->vb_ptr->ident = NULL;
+ msg_ps->vb_ptr->ident_len = 0;
+
+ vb->value_type = object_def.asn_type;
+ LWIP_ASSERT("invalid length", object_def.v_len <= 0xff);
+ vb->value_len = (u8_t)object_def.v_len;
+ if (vb->value_len > 0)
+ {
+ LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low",
+ vb->value_len <= SNMP_MAX_VALUE_SIZE);
+ vb->value = memp_malloc(MEMP_SNMP_VALUE);
+ if (vb->value != NULL)
+ {
+ mn->get_value(&object_def, vb->value_len, vb->value);
+ snmp_varbind_tail_add(&msg_ps->outvb, vb);
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ msg_ps->vb_idx += 1;
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate variable space\n"));
+ msg_ps->vb_ptr->ident = vb->ident;
+ msg_ps->vb_ptr->ident_len = vb->ident_len;
+ vb->ident = NULL;
+ vb->ident_len = 0;
+ memp_free(MEMP_SNMP_VARBIND, vb);
+ snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
+ }
+ }
+ else
+ {
+ /* vb->value_len == 0, empty value (e.g. empty string) */
+ vb->value = NULL;
+ snmp_varbind_tail_add(&msg_ps->outvb, vb);
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ msg_ps->vb_idx += 1;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_event: couldn't allocate outvb space\n"));
+ snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ mn = NULL;
+ }
+ if (mn == NULL)
+ {
+ /* mn == NULL, noSuchName */
+ snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
+ }
+ }
+ if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
+ (msg_ps->vb_idx == msg_ps->invb.count))
+ {
+ snmp_ok_response(msg_ps);
+ }
+}
+
+/**
+ * Service an internal or external event for SNMP GETNEXT.
+ *
+ * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
+ * @param msg_ps points to the assosicated message process state
+ */
+static void
+snmp_msg_getnext_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
+{
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
+
+ if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
+ {
+ struct mib_external_node *en;
+
+ /* get_object_def() answer*/
+ en = msg_ps->ext_mib_node;
+
+ /* translate answer into a known lifeform */
+ en->get_object_def_a(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1], &msg_ps->ext_object_def);
+ if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
+ {
+ msg_ps->state = SNMP_MSG_EXTERNAL_GET_VALUE;
+ en->get_value_q(request_id, &msg_ps->ext_object_def);
+ }
+ else
+ {
+ en->get_object_def_pc(request_id, 1, &msg_ps->ext_oid.id[msg_ps->ext_oid.len - 1]);
+ /* search failed, object id points to unknown object (nosuchname) */
+ snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
+ }
+ }
+ else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_VALUE)
+ {
+ struct mib_external_node *en;
+ struct snmp_varbind *vb;
+
+ /* get_value() answer */
+ en = msg_ps->ext_mib_node;
+
+ LWIP_ASSERT("invalid length", msg_ps->ext_object_def.v_len <= 0xff);
+ vb = snmp_varbind_alloc(&msg_ps->ext_oid,
+ msg_ps->ext_object_def.asn_type,
+ (u8_t)msg_ps->ext_object_def.v_len);
+ if (vb != NULL)
+ {
+ en->get_value_a(request_id, &msg_ps->ext_object_def, vb->value_len, vb->value);
+ snmp_varbind_tail_add(&msg_ps->outvb, vb);
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ msg_ps->vb_idx += 1;
+ }
+ else
+ {
+ en->get_value_pc(request_id, &msg_ps->ext_object_def);
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_getnext_event: couldn't allocate outvb space\n"));
+ snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
+ }
+ }
+
+ while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
+ (msg_ps->vb_idx < msg_ps->invb.count))
+ {
+ struct mib_node *mn;
+ struct snmp_obj_id oid;
+
+ if (msg_ps->vb_idx == 0)
+ {
+ msg_ps->vb_ptr = msg_ps->invb.head;
+ }
+ else
+ {
+ msg_ps->vb_ptr = msg_ps->vb_ptr->next;
+ }
+ if (snmp_iso_prefix_expand(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident, &oid))
+ {
+ if (msg_ps->vb_ptr->ident_len > 3)
+ {
+ /* can offset ident_len and ident */
+ mn = snmp_expand_tree((struct mib_node*)&internet,
+ msg_ps->vb_ptr->ident_len - 4,
+ msg_ps->vb_ptr->ident + 4, &oid);
+ }
+ else
+ {
+ /* can't offset ident_len -4, ident + 4 */
+ mn = snmp_expand_tree((struct mib_node*)&internet, 0, NULL, &oid);
+ }
+ }
+ else
+ {
+ mn = NULL;
+ }
+ if (mn != NULL)
+ {
+ if (mn->node_type == MIB_NODE_EX)
+ {
+ /* external object */
+ struct mib_external_node *en = (struct mib_external_node*)mn;
+
+ msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
+ /* save en && args in msg_ps!! */
+ msg_ps->ext_mib_node = en;
+ msg_ps->ext_oid = oid;
+
+ en->get_object_def_q(en->addr_inf, request_id, 1, &oid.id[oid.len - 1]);
+ }
+ else
+ {
+ /* internal object */
+ struct obj_def object_def;
+ struct snmp_varbind *vb;
+
+ msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
+ mn->get_object_def(1, &oid.id[oid.len - 1], &object_def);
+
+ LWIP_ASSERT("invalid length", object_def.v_len <= 0xff);
+ vb = snmp_varbind_alloc(&oid, object_def.asn_type, (u8_t)object_def.v_len);
+ if (vb != NULL)
+ {
+ msg_ps->state = SNMP_MSG_INTERNAL_GET_VALUE;
+ mn->get_value(&object_def, object_def.v_len, vb->value);
+ snmp_varbind_tail_add(&msg_ps->outvb, vb);
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ msg_ps->vb_idx += 1;
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv couldn't allocate outvb space\n"));
+ snmp_error_response(msg_ps,SNMP_ES_TOOBIG);
+ }
+ }
+ }
+ if (mn == NULL)
+ {
+ /* mn == NULL, noSuchName */
+ snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
+ }
+ }
+ if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
+ (msg_ps->vb_idx == msg_ps->invb.count))
+ {
+ snmp_ok_response(msg_ps);
+ }
+}
+
+/**
+ * Service an internal or external event for SNMP SET.
+ *
+ * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
+ * @param msg_ps points to the assosicated message process state
+ */
+static void
+snmp_msg_set_event(u8_t request_id, struct snmp_msg_pstat *msg_ps)
+{
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_msg_set_event: msg_ps->state==%"U16_F"\n",(u16_t)msg_ps->state));
+
+ if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF)
+ {
+ struct mib_external_node *en;
+ struct snmp_name_ptr np;
+
+ /* get_object_def() answer*/
+ en = msg_ps->ext_mib_node;
+ np = msg_ps->ext_name_ptr;
+
+ /* translate answer into a known lifeform */
+ en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
+ if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
+ {
+ msg_ps->state = SNMP_MSG_EXTERNAL_SET_TEST;
+ en->set_test_q(request_id, &msg_ps->ext_object_def);
+ }
+ else
+ {
+ en->get_object_def_pc(request_id, np.ident_len, np.ident);
+ /* search failed, object id points to unknown object (nosuchname) */
+ snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
+ }
+ }
+ else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_TEST)
+ {
+ struct mib_external_node *en;
+
+ /* set_test() answer*/
+ en = msg_ps->ext_mib_node;
+
+ if (msg_ps->ext_object_def.access & MIB_ACCESS_WRITE)
+ {
+ if ((msg_ps->ext_object_def.asn_type == msg_ps->vb_ptr->value_type) &&
+ (en->set_test_a(request_id,&msg_ps->ext_object_def,
+ msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
+ {
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ msg_ps->vb_idx += 1;
+ }
+ else
+ {
+ en->set_test_pc(request_id,&msg_ps->ext_object_def);
+ /* bad value */
+ snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
+ }
+ }
+ else
+ {
+ en->set_test_pc(request_id,&msg_ps->ext_object_def);
+ /* object not available for set */
+ snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
+ }
+ }
+ else if (msg_ps->state == SNMP_MSG_EXTERNAL_GET_OBJDEF_S)
+ {
+ struct mib_external_node *en;
+ struct snmp_name_ptr np;
+
+ /* get_object_def() answer*/
+ en = msg_ps->ext_mib_node;
+ np = msg_ps->ext_name_ptr;
+
+ /* translate answer into a known lifeform */
+ en->get_object_def_a(request_id, np.ident_len, np.ident, &msg_ps->ext_object_def);
+ if (msg_ps->ext_object_def.instance != MIB_OBJECT_NONE)
+ {
+ msg_ps->state = SNMP_MSG_EXTERNAL_SET_VALUE;
+ en->set_value_q(request_id, &msg_ps->ext_object_def,
+ msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
+ }
+ else
+ {
+ en->get_object_def_pc(request_id, np.ident_len, np.ident);
+ /* set_value failed, object has disappeared for some odd reason?? */
+ snmp_error_response(msg_ps,SNMP_ES_GENERROR);
+ }
+ }
+ else if (msg_ps->state == SNMP_MSG_EXTERNAL_SET_VALUE)
+ {
+ struct mib_external_node *en;
+
+ /** set_value_a() */
+ en = msg_ps->ext_mib_node;
+ en->set_value_a(request_id, &msg_ps->ext_object_def,
+ msg_ps->vb_ptr->value_len, msg_ps->vb_ptr->value);
+
+ /** @todo use set_value_pc() if toobig */
+ msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
+ msg_ps->vb_idx += 1;
+ }
+
+ /* test all values before setting */
+ while ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
+ (msg_ps->vb_idx < msg_ps->invb.count))
+ {
+ struct mib_node *mn;
+ struct snmp_name_ptr np;
+
+ if (msg_ps->vb_idx == 0)
+ {
+ msg_ps->vb_ptr = msg_ps->invb.head;
+ }
+ else
+ {
+ msg_ps->vb_ptr = msg_ps->vb_ptr->next;
+ }
+ /** test object identifier for .iso.org.dod.internet prefix */
+ if (snmp_iso_prefix_tst(msg_ps->vb_ptr->ident_len, msg_ps->vb_ptr->ident))
+ {
+ mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
+ msg_ps->vb_ptr->ident + 4, &np);
+ if (mn != NULL)
+ {
+ if (mn->node_type == MIB_NODE_EX)
+ {
+ /* external object */
+ struct mib_external_node *en = (struct mib_external_node*)mn;
+
+ msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF;
+ /* save en && args in msg_ps!! */
+ msg_ps->ext_mib_node = en;
+ msg_ps->ext_name_ptr = np;
+
+ en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
+ }
+ else
+ {
+ /* internal object */
+ struct obj_def object_def;
+
+ msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF;
+ mn->get_object_def(np.ident_len, np.ident, &object_def);
+ if (object_def.instance != MIB_OBJECT_NONE)
+ {
+ mn = mn;
+ }
+ else
+ {
+ /* search failed, object id points to unknown object (nosuchname) */
+ mn = NULL;
+ }
+ if (mn != NULL)
+ {
+ msg_ps->state = SNMP_MSG_INTERNAL_SET_TEST;
+
+ if (object_def.access & MIB_ACCESS_WRITE)
+ {
+ if ((object_def.asn_type == msg_ps->vb_ptr->value_type) &&
+ (mn->set_test(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value) != 0))
+ {
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ msg_ps->vb_idx += 1;
+ }
+ else
+ {
+ /* bad value */
+ snmp_error_response(msg_ps,SNMP_ES_BADVALUE);
+ }
+ }
+ else
+ {
+ /* object not available for set */
+ snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
+ }
+ }
+ }
+ }
+ }
+ else
+ {
+ mn = NULL;
+ }
+ if (mn == NULL)
+ {
+ /* mn == NULL, noSuchName */
+ snmp_error_response(msg_ps,SNMP_ES_NOSUCHNAME);
+ }
+ }
+
+ if ((msg_ps->state == SNMP_MSG_SEARCH_OBJ) &&
+ (msg_ps->vb_idx == msg_ps->invb.count))
+ {
+ msg_ps->vb_idx = 0;
+ msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
+ }
+
+ /* set all values "atomically" (be as "atomic" as possible) */
+ while ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
+ (msg_ps->vb_idx < msg_ps->invb.count))
+ {
+ struct mib_node *mn;
+ struct snmp_name_ptr np;
+
+ if (msg_ps->vb_idx == 0)
+ {
+ msg_ps->vb_ptr = msg_ps->invb.head;
+ }
+ else
+ {
+ msg_ps->vb_ptr = msg_ps->vb_ptr->next;
+ }
+ /* skip iso prefix test, was done previously while settesting() */
+ mn = snmp_search_tree((struct mib_node*)&internet, msg_ps->vb_ptr->ident_len - 4,
+ msg_ps->vb_ptr->ident + 4, &np);
+ /* check if object is still available
+ (e.g. external hot-plug thingy present?) */
+ if (mn != NULL)
+ {
+ if (mn->node_type == MIB_NODE_EX)
+ {
+ /* external object */
+ struct mib_external_node *en = (struct mib_external_node*)mn;
+
+ msg_ps->state = SNMP_MSG_EXTERNAL_GET_OBJDEF_S;
+ /* save en && args in msg_ps!! */
+ msg_ps->ext_mib_node = en;
+ msg_ps->ext_name_ptr = np;
+
+ en->get_object_def_q(en->addr_inf, request_id, np.ident_len, np.ident);
+ }
+ else
+ {
+ /* internal object */
+ struct obj_def object_def;
+
+ msg_ps->state = SNMP_MSG_INTERNAL_GET_OBJDEF_S;
+ mn->get_object_def(np.ident_len, np.ident, &object_def);
+ msg_ps->state = SNMP_MSG_INTERNAL_SET_VALUE;
+ mn->set_value(&object_def,msg_ps->vb_ptr->value_len,msg_ps->vb_ptr->value);
+ msg_ps->vb_idx += 1;
+ }
+ }
+ }
+ if ((msg_ps->state == SNMP_MSG_INTERNAL_SET_VALUE) &&
+ (msg_ps->vb_idx == msg_ps->invb.count))
+ {
+ /* simply echo the input if we can set it
+ @todo do we need to return the actual value?
+ e.g. if value is silently modified or behaves sticky? */
+ msg_ps->outvb = msg_ps->invb;
+ msg_ps->invb.head = NULL;
+ msg_ps->invb.tail = NULL;
+ msg_ps->invb.count = 0;
+ snmp_ok_response(msg_ps);
+ }
+}
+
+
+/**
+ * Handle one internal or external event.
+ * Called for one async event. (recv external/private answer)
+ *
+ * @param request_id identifies requests from 0 to (SNMP_CONCURRENT_REQUESTS-1)
+ */
+void
+snmp_msg_event(u8_t request_id)
+{
+ struct snmp_msg_pstat *msg_ps;
+
+ if (request_id < SNMP_CONCURRENT_REQUESTS)
+ {
+ msg_ps = &msg_input_list[request_id];
+ if (msg_ps->rt == SNMP_ASN1_PDU_GET_NEXT_REQ)
+ {
+ snmp_msg_getnext_event(request_id, msg_ps);
+ }
+ else if (msg_ps->rt == SNMP_ASN1_PDU_GET_REQ)
+ {
+ snmp_msg_get_event(request_id, msg_ps);
+ }
+ else if(msg_ps->rt == SNMP_ASN1_PDU_SET_REQ)
+ {
+ snmp_msg_set_event(request_id, msg_ps);
+ }
+ }
+}
+
+
+/* lwIP UDP receive callback function */
+static void
+snmp_recv(void *arg, struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *addr, u16_t port)
+{
+ struct snmp_msg_pstat *msg_ps;
+ u8_t req_idx;
+ err_t err_ret;
+ u16_t payload_len = p->tot_len;
+ u16_t payload_ofs = 0;
+ u16_t varbind_ofs = 0;
+
+ /* suppress unused argument warning */
+ LWIP_UNUSED_ARG(arg);
+
+ /* traverse input message process list, look for SNMP_MSG_EMPTY */
+ msg_ps = &msg_input_list[0];
+ req_idx = 0;
+ while ((req_idx < SNMP_CONCURRENT_REQUESTS) && (msg_ps->state != SNMP_MSG_EMPTY))
+ {
+ req_idx++;
+ msg_ps++;
+ }
+ if (req_idx == SNMP_CONCURRENT_REQUESTS)
+ {
+ /* exceeding number of concurrent requests */
+ pbuf_free(p);
+ return;
+ }
+
+ /* accepting request */
+ snmp_inc_snmpinpkts();
+ /* record used 'protocol control block' */
+ msg_ps->pcb = pcb;
+ /* source address (network order) */
+ msg_ps->sip = *addr;
+ /* source port (host order (lwIP oddity)) */
+ msg_ps->sp = port;
+
+ /* check total length, version, community, pdu type */
+ err_ret = snmp_pdu_header_check(p, payload_ofs, payload_len, &varbind_ofs, msg_ps);
+ /* Only accept requests and requests without error (be robust) */
+ /* Reject response and trap headers or error requests as input! */
+ if ((err_ret != ERR_OK) ||
+ ((msg_ps->rt != SNMP_ASN1_PDU_GET_REQ) &&
+ (msg_ps->rt != SNMP_ASN1_PDU_GET_NEXT_REQ) &&
+ (msg_ps->rt != SNMP_ASN1_PDU_SET_REQ)) ||
+ ((msg_ps->error_status != SNMP_ES_NOERROR) ||
+ (msg_ps->error_index != 0)) )
+ {
+ /* header check failed drop request silently, do not return error! */
+ pbuf_free(p);
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_header_check() failed\n"));
+ return;
+ }
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv ok, community %s\n", msg_ps->community));
+
+ /* Builds a list of variable bindings. Copy the varbinds from the pbuf
+ chain to glue them when these are divided over two or more pbuf's. */
+ err_ret = snmp_pdu_dec_varbindlist(p, varbind_ofs, &varbind_ofs, msg_ps);
+ /* we've decoded the incoming message, release input msg now */
+ pbuf_free(p);
+ if ((err_ret != ERR_OK) || (msg_ps->invb.count == 0))
+ {
+ /* varbind-list decode failed, or varbind list empty.
+ drop request silently, do not return error!
+ (errors are only returned for a specific varbind failure) */
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_pdu_dec_varbindlist() failed\n"));
+ return;
+ }
+
+ msg_ps->error_status = SNMP_ES_NOERROR;
+ msg_ps->error_index = 0;
+ /* find object for each variable binding */
+ msg_ps->state = SNMP_MSG_SEARCH_OBJ;
+ /* first variable binding from list to inspect */
+ msg_ps->vb_idx = 0;
+
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_recv varbind cnt=%"U16_F"\n",(u16_t)msg_ps->invb.count));
+
+ /* handle input event and as much objects as possible in one go */
+ snmp_msg_event(req_idx);
+}
+
+/**
+ * Checks and decodes incoming SNMP message header, logs header errors.
+ *
+ * @param p points to pbuf chain of SNMP message (UDP payload)
+ * @param ofs points to first octet of SNMP message
+ * @param pdu_len the length of the UDP payload
+ * @param ofs_ret returns the ofset of the variable bindings
+ * @param m_stat points to the current message request state return
+ * @return
+ * - ERR_OK SNMP header is sane and accepted
+ * - ERR_ARG SNMP header is either malformed or rejected
+ */
+static err_t
+snmp_pdu_header_check(struct pbuf *p, u16_t ofs, u16_t pdu_len, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
+{
+ err_t derr;
+ u16_t len, ofs_base;
+ u8_t len_octets;
+ u8_t type;
+ s32_t version;
+
+ ofs_base = ofs;
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if ((derr != ERR_OK) ||
+ (pdu_len != (1 + len_octets + len)) ||
+ (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
+ {
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets);
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
+ {
+ /* can't decode or no integer (version) */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &version);
+ if (derr != ERR_OK)
+ {
+ /* can't decode */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ if (version != 0)
+ {
+ /* not version 1 */
+ snmp_inc_snmpinbadversions();
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets + len);
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR)))
+ {
+ /* can't decode or no octet string (community) */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, SNMP_COMMUNITY_STR_LEN, m_stat->community);
+ if (derr != ERR_OK)
+ {
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ /* add zero terminator */
+ len = ((len < (SNMP_COMMUNITY_STR_LEN))?(len):(SNMP_COMMUNITY_STR_LEN));
+ m_stat->community[len] = 0;
+ m_stat->com_strlen = (u8_t)len;
+ if (strncmp(snmp_publiccommunity, (const char*)m_stat->community, SNMP_COMMUNITY_STR_LEN) != 0)
+ {
+ /** @todo: move this if we need to check more names */
+ snmp_inc_snmpinbadcommunitynames();
+ snmp_authfail_trap();
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets + len);
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if (derr != ERR_OK)
+ {
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ switch(type)
+ {
+ case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_REQ):
+ /* GetRequest PDU */
+ snmp_inc_snmpingetrequests();
+ derr = ERR_OK;
+ break;
+ case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_NEXT_REQ):
+ /* GetNextRequest PDU */
+ snmp_inc_snmpingetnexts();
+ derr = ERR_OK;
+ break;
+ case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_GET_RESP):
+ /* GetResponse PDU */
+ snmp_inc_snmpingetresponses();
+ derr = ERR_ARG;
+ break;
+ case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_SET_REQ):
+ /* SetRequest PDU */
+ snmp_inc_snmpinsetrequests();
+ derr = ERR_OK;
+ break;
+ case (SNMP_ASN1_CONTXT | SNMP_ASN1_CONSTR | SNMP_ASN1_PDU_TRAP):
+ /* Trap PDU */
+ snmp_inc_snmpintraps();
+ derr = ERR_ARG;
+ break;
+ default:
+ snmp_inc_snmpinasnparseerrs();
+ derr = ERR_ARG;
+ break;
+ }
+ if (derr != ERR_OK)
+ {
+ /* unsupported input PDU for this agent (no parse error) */
+ return ERR_ARG;
+ }
+ m_stat->rt = type & 0x1F;
+ ofs += (1 + len_octets);
+ if (len != (pdu_len - (ofs - ofs_base)))
+ {
+ /* decoded PDU length does not equal actual payload length */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
+ {
+ /* can't decode or no integer (request ID) */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->rid);
+ if (derr != ERR_OK)
+ {
+ /* can't decode */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets + len);
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
+ {
+ /* can't decode or no integer (error-status) */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ /* must be noError (0) for incoming requests.
+ log errors for mib-2 completeness and for debug purposes */
+ derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_status);
+ if (derr != ERR_OK)
+ {
+ /* can't decode */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ switch (m_stat->error_status)
+ {
+ case SNMP_ES_TOOBIG:
+ snmp_inc_snmpintoobigs();
+ break;
+ case SNMP_ES_NOSUCHNAME:
+ snmp_inc_snmpinnosuchnames();
+ break;
+ case SNMP_ES_BADVALUE:
+ snmp_inc_snmpinbadvalues();
+ break;
+ case SNMP_ES_READONLY:
+ snmp_inc_snmpinreadonlys();
+ break;
+ case SNMP_ES_GENERROR:
+ snmp_inc_snmpingenerrs();
+ break;
+ }
+ ofs += (1 + len_octets + len);
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG)))
+ {
+ /* can't decode or no integer (error-index) */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ /* must be 0 for incoming requests.
+ decode anyway to catch bad integers (and dirty tricks) */
+ derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, &m_stat->error_index);
+ if (derr != ERR_OK)
+ {
+ /* can't decode */
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets + len);
+ *ofs_ret = ofs;
+ return ERR_OK;
+}
+
+static err_t
+snmp_pdu_dec_varbindlist(struct pbuf *p, u16_t ofs, u16_t *ofs_ret, struct snmp_msg_pstat *m_stat)
+{
+ err_t derr;
+ u16_t len, vb_len;
+ u8_t len_octets;
+ u8_t type;
+
+ /* variable binding list */
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &vb_len);
+ if ((derr != ERR_OK) ||
+ (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)))
+ {
+ snmp_inc_snmpinasnparseerrs();
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets);
+
+ /* start with empty list */
+ m_stat->invb.count = 0;
+ m_stat->invb.head = NULL;
+ m_stat->invb.tail = NULL;
+
+ while (vb_len > 0)
+ {
+ struct snmp_obj_id oid, oid_value;
+ struct snmp_varbind *vb;
+
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if ((derr != ERR_OK) ||
+ (type != (SNMP_ASN1_UNIV | SNMP_ASN1_CONSTR | SNMP_ASN1_SEQ)) ||
+ (len == 0) || (len > vb_len))
+ {
+ snmp_inc_snmpinasnparseerrs();
+ /* free varbinds (if available) */
+ snmp_varbind_list_free(&m_stat->invb);
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets);
+ vb_len -= (1 + len_octets);
+
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if ((derr != ERR_OK) || (type != (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID)))
+ {
+ /* can't decode object name length */
+ snmp_inc_snmpinasnparseerrs();
+ /* free varbinds (if available) */
+ snmp_varbind_list_free(&m_stat->invb);
+ return ERR_ARG;
+ }
+ derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid);
+ if (derr != ERR_OK)
+ {
+ /* can't decode object name */
+ snmp_inc_snmpinasnparseerrs();
+ /* free varbinds (if available) */
+ snmp_varbind_list_free(&m_stat->invb);
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets + len);
+ vb_len -= (1 + len_octets + len);
+
+ snmp_asn1_dec_type(p, ofs, &type);
+ derr = snmp_asn1_dec_length(p, ofs+1, &len_octets, &len);
+ if (derr != ERR_OK)
+ {
+ /* can't decode object value length */
+ snmp_inc_snmpinasnparseerrs();
+ /* free varbinds (if available) */
+ snmp_varbind_list_free(&m_stat->invb);
+ return ERR_ARG;
+ }
+
+ switch (type)
+ {
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_INTEG):
+ vb = snmp_varbind_alloc(&oid, type, sizeof(s32_t));
+ if (vb != NULL)
+ {
+ s32_t *vptr = (s32_t*)vb->value;
+
+ derr = snmp_asn1_dec_s32t(p, ofs + 1 + len_octets, len, vptr);
+ snmp_varbind_tail_add(&m_stat->invb, vb);
+ }
+ else
+ {
+ derr = ERR_ARG;
+ }
+ break;
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_COUNTER):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_GAUGE):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_TIMETICKS):
+ vb = snmp_varbind_alloc(&oid, type, sizeof(u32_t));
+ if (vb != NULL)
+ {
+ u32_t *vptr = (u32_t*)vb->value;
+
+ derr = snmp_asn1_dec_u32t(p, ofs + 1 + len_octets, len, vptr);
+ snmp_varbind_tail_add(&m_stat->invb, vb);
+ }
+ else
+ {
+ derr = ERR_ARG;
+ }
+ break;
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OC_STR):
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_OPAQUE):
+ LWIP_ASSERT("invalid length", len <= 0xff);
+ vb = snmp_varbind_alloc(&oid, type, (u8_t)len);
+ if (vb != NULL)
+ {
+ derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
+ snmp_varbind_tail_add(&m_stat->invb, vb);
+ }
+ else
+ {
+ derr = ERR_ARG;
+ }
+ break;
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_NUL):
+ vb = snmp_varbind_alloc(&oid, type, 0);
+ if (vb != NULL)
+ {
+ snmp_varbind_tail_add(&m_stat->invb, vb);
+ derr = ERR_OK;
+ }
+ else
+ {
+ derr = ERR_ARG;
+ }
+ break;
+ case (SNMP_ASN1_UNIV | SNMP_ASN1_PRIMIT | SNMP_ASN1_OBJ_ID):
+ derr = snmp_asn1_dec_oid(p, ofs + 1 + len_octets, len, &oid_value);
+ if (derr == ERR_OK)
+ {
+ vb = snmp_varbind_alloc(&oid, type, oid_value.len * sizeof(s32_t));
+ if (vb != NULL)
+ {
+ u8_t i = oid_value.len;
+ s32_t *vptr = (s32_t*)vb->value;
+
+ while(i > 0)
+ {
+ i--;
+ vptr[i] = oid_value.id[i];
+ }
+ snmp_varbind_tail_add(&m_stat->invb, vb);
+ derr = ERR_OK;
+ }
+ else
+ {
+ derr = ERR_ARG;
+ }
+ }
+ break;
+ case (SNMP_ASN1_APPLIC | SNMP_ASN1_PRIMIT | SNMP_ASN1_IPADDR):
+ if (len == 4)
+ {
+ /* must be exactly 4 octets! */
+ vb = snmp_varbind_alloc(&oid, type, 4);
+ if (vb != NULL)
+ {
+ derr = snmp_asn1_dec_raw(p, ofs + 1 + len_octets, len, vb->value_len, (u8_t*)vb->value);
+ snmp_varbind_tail_add(&m_stat->invb, vb);
+ }
+ else
+ {
+ derr = ERR_ARG;
+ }
+ }
+ else
+ {
+ derr = ERR_ARG;
+ }
+ break;
+ default:
+ derr = ERR_ARG;
+ break;
+ }
+ if (derr != ERR_OK)
+ {
+ snmp_inc_snmpinasnparseerrs();
+ /* free varbinds (if available) */
+ snmp_varbind_list_free(&m_stat->invb);
+ return ERR_ARG;
+ }
+ ofs += (1 + len_octets + len);
+ vb_len -= (1 + len_octets + len);
+ }
+
+ if (m_stat->rt == SNMP_ASN1_PDU_SET_REQ)
+ {
+ snmp_add_snmpintotalsetvars(m_stat->invb.count);
+ }
+ else
+ {
+ snmp_add_snmpintotalreqvars(m_stat->invb.count);
+ }
+
+ *ofs_ret = ofs;
+ return ERR_OK;
+}
+
+struct snmp_varbind*
+snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len)
+{
+ struct snmp_varbind *vb;
+
+ vb = (struct snmp_varbind *)memp_malloc(MEMP_SNMP_VARBIND);
+ if (vb != NULL)
+ {
+ u8_t i;
+
+ vb->next = NULL;
+ vb->prev = NULL;
+ i = oid->len;
+ vb->ident_len = i;
+ if (i > 0)
+ {
+ LWIP_ASSERT("SNMP_MAX_TREE_DEPTH is configured too low", i <= SNMP_MAX_TREE_DEPTH);
+ /* allocate array of s32_t for our object identifier */
+ vb->ident = (s32_t*)memp_malloc(MEMP_SNMP_VALUE);
+ if (vb->ident == NULL)
+ {
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate ident value space\n"));
+ memp_free(MEMP_SNMP_VARBIND, vb);
+ return NULL;
+ }
+ while(i > 0)
+ {
+ i--;
+ vb->ident[i] = oid->id[i];
+ }
+ }
+ else
+ {
+ /* i == 0, pass zero length object identifier */
+ vb->ident = NULL;
+ }
+ vb->value_type = type;
+ vb->value_len = len;
+ if (len > 0)
+ {
+ LWIP_ASSERT("SNMP_MAX_OCTET_STRING_LEN is configured too low", vb->value_len <= SNMP_MAX_VALUE_SIZE);
+ /* allocate raw bytes for our object value */
+ vb->value = memp_malloc(MEMP_SNMP_VALUE);
+ if (vb->value == NULL)
+ {
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate value space\n"));
+ if (vb->ident != NULL)
+ {
+ memp_free(MEMP_SNMP_VALUE, vb->ident);
+ }
+ memp_free(MEMP_SNMP_VARBIND, vb);
+ return NULL;
+ }
+ }
+ else
+ {
+ /* ASN1_NUL type, or zero length ASN1_OC_STR */
+ vb->value = NULL;
+ }
+ }
+ else
+ {
+ LWIP_DEBUGF(SNMP_MSG_DEBUG, ("snmp_varbind_alloc: couldn't allocate varbind space\n"));
+ }
+ return vb;
+}
+
+void
+snmp_varbind_free(struct snmp_varbind *vb)
+{
+ if (vb->value != NULL )
+ {
+ memp_free(MEMP_SNMP_VALUE, vb->value);
+ }
+ if (vb->ident != NULL )
+ {
+ memp_free(MEMP_SNMP_VALUE, vb->ident);
+ }
+ memp_free(MEMP_SNMP_VARBIND, vb);
+}
+
+void
+snmp_varbind_list_free(struct snmp_varbind_root *root)
+{
+ struct snmp_varbind *vb, *prev;
+
+ vb = root->tail;
+ while ( vb != NULL )
+ {
+ prev = vb->prev;
+ snmp_varbind_free(vb);
+ vb = prev;
+ }
+ root->count = 0;
+ root->head = NULL;
+ root->tail = NULL;
+}
+
+void
+snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb)
+{
+ if (root->count == 0)
+ {
+ /* add first varbind to list */
+ root->head = vb;
+ root->tail = vb;
+ }
+ else
+ {
+ /* add nth varbind to list tail */
+ root->tail->next = vb;
+ vb->prev = root->tail;
+ root->tail = vb;
+ }
+ root->count += 1;
+}
+
+struct snmp_varbind*
+snmp_varbind_tail_remove(struct snmp_varbind_root *root)
+{
+ struct snmp_varbind* vb;
+
+ if (root->count > 0)
+ {
+ /* remove tail varbind */
+ vb = root->tail;
+ root->tail = vb->prev;
+ vb->prev->next = NULL;
+ root->count -= 1;
+ }
+ else
+ {
+ /* nothing to remove */
+ vb = NULL;
+ }
+ return vb;
+}
+
+#endif /* LWIP_SNMP */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/snmp/msg_out.c b/src/VBox/Devices/Network/lwip-new/src/core/snmp/msg_out.c
new file mode 100644
index 00000000..fc0807c5
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/snmp/msg_out.c
@@ -0,0 +1,678 @@
+/**
+ * @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/src/VBox/Devices/Network/lwip-new/src/core/stats.c b/src/VBox/Devices/Network/lwip-new/src/core/stats.c
new file mode 100644
index 00000000..ff97853f
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/stats.c
@@ -0,0 +1,181 @@
+/**
+ * @file
+ * Statistics module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_STATS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+
+#include <string.h>
+
+struct stats_ lwip_stats;
+
+void stats_init(void)
+{
+#ifdef LWIP_DEBUG
+#if MEMP_STATS
+ const char * memp_names[] = {
+#define LWIP_MEMPOOL(name,num,size,desc) desc,
+#include "lwip/memp_std.h"
+ };
+ int i;
+ for (i = 0; i < MEMP_MAX; i++) {
+ lwip_stats.memp[i].name = memp_names[i];
+ }
+#endif /* MEMP_STATS */
+#if MEM_STATS
+ lwip_stats.mem.name = "MEM";
+#endif /* MEM_STATS */
+#endif /* LWIP_DEBUG */
+}
+
+#if LWIP_STATS_DISPLAY
+void
+stats_display_proto(struct stats_proto *proto, const char *name)
+{
+ LWIP_PLATFORM_DIAG(("\n%s\n\t", name));
+ LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", proto->xmit));
+ LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", proto->recv));
+ LWIP_PLATFORM_DIAG(("fw: %"STAT_COUNTER_F"\n\t", proto->fw));
+ LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", proto->drop));
+ LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", proto->chkerr));
+ LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", proto->lenerr));
+ LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", proto->memerr));
+ LWIP_PLATFORM_DIAG(("rterr: %"STAT_COUNTER_F"\n\t", proto->rterr));
+ LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", proto->proterr));
+ LWIP_PLATFORM_DIAG(("opterr: %"STAT_COUNTER_F"\n\t", proto->opterr));
+ LWIP_PLATFORM_DIAG(("err: %"STAT_COUNTER_F"\n\t", proto->err));
+ LWIP_PLATFORM_DIAG(("cachehit: %"STAT_COUNTER_F"\n", proto->cachehit));
+}
+
+#if IGMP_STATS || MLD6_STATS
+void
+stats_display_igmp(struct stats_igmp *igmp, const char *name)
+{
+ LWIP_PLATFORM_DIAG(("\n%s\n\t", name));
+ LWIP_PLATFORM_DIAG(("xmit: %"STAT_COUNTER_F"\n\t", igmp->xmit));
+ LWIP_PLATFORM_DIAG(("recv: %"STAT_COUNTER_F"\n\t", igmp->recv));
+ LWIP_PLATFORM_DIAG(("drop: %"STAT_COUNTER_F"\n\t", igmp->drop));
+ LWIP_PLATFORM_DIAG(("chkerr: %"STAT_COUNTER_F"\n\t", igmp->chkerr));
+ LWIP_PLATFORM_DIAG(("lenerr: %"STAT_COUNTER_F"\n\t", igmp->lenerr));
+ LWIP_PLATFORM_DIAG(("memerr: %"STAT_COUNTER_F"\n\t", igmp->memerr));
+ LWIP_PLATFORM_DIAG(("proterr: %"STAT_COUNTER_F"\n\t", igmp->proterr));
+ LWIP_PLATFORM_DIAG(("rx_v1: %"STAT_COUNTER_F"\n\t", igmp->rx_v1));
+ LWIP_PLATFORM_DIAG(("rx_group: %"STAT_COUNTER_F"\n\t", igmp->rx_group));
+ LWIP_PLATFORM_DIAG(("rx_general: %"STAT_COUNTER_F"\n\t", igmp->rx_general));
+ LWIP_PLATFORM_DIAG(("rx_report: %"STAT_COUNTER_F"\n\t", igmp->rx_report));
+ LWIP_PLATFORM_DIAG(("tx_join: %"STAT_COUNTER_F"\n\t", igmp->tx_join));
+ LWIP_PLATFORM_DIAG(("tx_leave: %"STAT_COUNTER_F"\n\t", igmp->tx_leave));
+ LWIP_PLATFORM_DIAG(("tx_report: %"STAT_COUNTER_F"\n\t", igmp->tx_report));
+}
+#endif /* IGMP_STATS || MLD6_STATS */
+
+#if MEM_STATS || MEMP_STATS
+void
+stats_display_mem(struct stats_mem *mem, const char *name)
+{
+ LWIP_PLATFORM_DIAG(("\nMEM %s\n\t", name));
+ LWIP_PLATFORM_DIAG(("avail: %"U32_F"\n\t", (u32_t)mem->avail));
+ LWIP_PLATFORM_DIAG(("used: %"U32_F"\n\t", (u32_t)mem->used));
+ LWIP_PLATFORM_DIAG(("max: %"U32_F"\n\t", (u32_t)mem->max));
+ LWIP_PLATFORM_DIAG(("err: %"U32_F"\n", (u32_t)mem->err));
+}
+
+#if MEMP_STATS
+void
+stats_display_memp(struct stats_mem *mem, int index)
+{
+ char * memp_names[] = {
+#define LWIP_MEMPOOL(name,num,size,desc) desc,
+#include "lwip/memp_std.h"
+ };
+ if(index < MEMP_MAX) {
+ stats_display_mem(mem, memp_names[index]);
+ }
+}
+#endif /* MEMP_STATS */
+#endif /* MEM_STATS || MEMP_STATS */
+
+#if SYS_STATS
+void
+stats_display_sys(struct stats_sys *sys)
+{
+ LWIP_PLATFORM_DIAG(("\nSYS\n\t"));
+ LWIP_PLATFORM_DIAG(("sem.used: %"U32_F"\n\t", (u32_t)sys->sem.used));
+ LWIP_PLATFORM_DIAG(("sem.max: %"U32_F"\n\t", (u32_t)sys->sem.max));
+ LWIP_PLATFORM_DIAG(("sem.err: %"U32_F"\n\t", (u32_t)sys->sem.err));
+ LWIP_PLATFORM_DIAG(("mutex.used: %"U32_F"\n\t", (u32_t)sys->mutex.used));
+ LWIP_PLATFORM_DIAG(("mutex.max: %"U32_F"\n\t", (u32_t)sys->mutex.max));
+ LWIP_PLATFORM_DIAG(("mutex.err: %"U32_F"\n\t", (u32_t)sys->mutex.err));
+ LWIP_PLATFORM_DIAG(("mbox.used: %"U32_F"\n\t", (u32_t)sys->mbox.used));
+ LWIP_PLATFORM_DIAG(("mbox.max: %"U32_F"\n\t", (u32_t)sys->mbox.max));
+ LWIP_PLATFORM_DIAG(("mbox.err: %"U32_F"\n\t", (u32_t)sys->mbox.err));
+}
+#endif /* SYS_STATS */
+
+void
+stats_display(void)
+{
+ s16_t i;
+
+ LINK_STATS_DISPLAY();
+ ETHARP_STATS_DISPLAY();
+ IPFRAG_STATS_DISPLAY();
+ IP6_FRAG_STATS_DISPLAY();
+ IP_STATS_DISPLAY();
+ ND6_STATS_DISPLAY();
+ IP6_STATS_DISPLAY();
+ IGMP_STATS_DISPLAY();
+ MLD6_STATS_DISPLAY();
+ ICMP_STATS_DISPLAY();
+ ICMP6_STATS_DISPLAY();
+ UDP_STATS_DISPLAY();
+ TCP_STATS_DISPLAY();
+ MEM_STATS_DISPLAY();
+ for (i = 0; i < MEMP_MAX; i++) {
+ MEMP_STATS_DISPLAY(i);
+ }
+ SYS_STATS_DISPLAY();
+}
+#endif /* LWIP_STATS_DISPLAY */
+
+#endif /* LWIP_STATS */
+
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/sys.c b/src/VBox/Devices/Network/lwip-new/src/core/sys.c
new file mode 100644
index 00000000..f1777372
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/sys.c
@@ -0,0 +1,68 @@
+/**
+ * @file
+ * lwIP Operating System abstraction
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/sys.h"
+
+/* Most of the functions defined in sys.h must be implemented in the
+ * architecture-dependent file sys_arch.c */
+
+#if !NO_SYS
+
+#ifndef sys_msleep
+/**
+ * Sleep for some ms. Timeouts are NOT processed while sleeping.
+ *
+ * @param ms number of milliseconds to sleep
+ */
+void
+sys_msleep(u32_t ms)
+{
+ if (ms > 0) {
+ sys_sem_t delaysem;
+ err_t err = sys_sem_new(&delaysem, 0);
+ if (err == ERR_OK) {
+ sys_arch_sem_wait(&delaysem, ms);
+ sys_sem_free(&delaysem);
+ }
+ }
+}
+#endif /* sys_msleep */
+
+#endif /* !NO_SYS */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/tcp.c b/src/VBox/Devices/Network/lwip-new/src/core/tcp.c
new file mode 100644
index 00000000..4c9096bc
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/tcp.c
@@ -0,0 +1,1926 @@
+/**
+ * @file
+ * Transmission Control Protocol for IP
+ *
+ * This file contains common functions for the TCP implementation, such as functinos
+ * for manipulating the data structures and the TCP timer functions. TCP functions
+ * related to input and output is found in tcp_in.c and tcp_out.c respectively.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/snmp.h"
+#include "lwip/tcp.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/debug.h"
+#include "lwip/stats.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/nd6.h"
+
+#include <string.h>
+
+#ifndef TCP_LOCAL_PORT_RANGE_START
+/* From http://www.iana.org/assignments/port-numbers:
+ "The Dynamic and/or Private Ports are those from 49152 through 65535" */
+#define TCP_LOCAL_PORT_RANGE_START 0xc000
+#define TCP_LOCAL_PORT_RANGE_END 0xffff
+#define TCP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~TCP_LOCAL_PORT_RANGE_START) + TCP_LOCAL_PORT_RANGE_START)
+#endif
+
+#if LWIP_TCP_KEEPALIVE
+#define TCP_KEEP_DUR(pcb) ((pcb)->keep_cnt * (pcb)->keep_intvl)
+#define TCP_KEEP_INTVL(pcb) ((pcb)->keep_intvl)
+#else /* LWIP_TCP_KEEPALIVE */
+#define TCP_KEEP_DUR(pcb) TCP_MAXIDLE
+#define TCP_KEEP_INTVL(pcb) TCP_KEEPINTVL_DEFAULT
+#endif /* LWIP_TCP_KEEPALIVE */
+
+const char * const tcp_state_str[] = {
+ "CLOSED",
+ "LISTEN",
+ "SYN_SENT",
+/* #if LWIP_CONNECTION_PROXY // - always provide the name */
+ "SYN_RCVD_0",
+/* #endif */
+ "SYN_RCVD",
+ "ESTABLISHED",
+ "FIN_WAIT_1",
+ "FIN_WAIT_2",
+ "CLOSE_WAIT",
+ "CLOSING",
+ "LAST_ACK",
+ "TIME_WAIT"
+};
+
+/* last local TCP port */
+static u16_t tcp_port = TCP_LOCAL_PORT_RANGE_START;
+
+/* Incremented every coarse grained timer shot (typically every 500 ms). */
+u32_t tcp_ticks;
+const u8_t tcp_backoff[13] =
+ { 1, 2, 3, 4, 5, 6, 7, 7, 7, 7, 7, 7, 7};
+ /* Times per slowtmr hits */
+const u8_t tcp_persist_backoff[7] = { 3, 6, 12, 24, 48, 96, 120 };
+
+/* The TCP PCB lists. */
+
+/** List of all TCP PCBs bound but not yet (connected || listening) */
+struct tcp_pcb *tcp_bound_pcbs;
+/** List of all TCP PCBs in LISTEN state */
+union tcp_listen_pcbs_t tcp_listen_pcbs;
+/** List of all TCP PCBs that are in a state in which
+ * they accept or send data. */
+struct tcp_pcb *tcp_active_pcbs;
+/** List of all TCP PCBs in TIME-WAIT state */
+struct tcp_pcb *tcp_tw_pcbs;
+
+#define NUM_TCP_PCB_LISTS 4
+#define NUM_TCP_PCB_LISTS_NO_TIME_WAIT 3
+/** An array with all (non-temporary) PCB lists, mainly used for smaller code size */
+struct tcp_pcb ** const tcp_pcb_lists[] = {&tcp_listen_pcbs.pcbs, &tcp_bound_pcbs,
+ &tcp_active_pcbs, &tcp_tw_pcbs};
+
+/** Only used for temporary storage. */
+struct tcp_pcb *tcp_tmp_pcb;
+
+u8_t tcp_active_pcbs_changed;
+
+/** Timer counter to handle calling slow-timer from tcp_tmr() */
+static u8_t tcp_timer;
+static u8_t tcp_timer_ctr;
+static u16_t tcp_new_port(void);
+
+/**
+ * Initialize this module.
+ */
+void
+tcp_init(void)
+{
+#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND)
+ tcp_port = TCP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
+#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */
+}
+
+/**
+ * Called periodically to dispatch TCP timers.
+ */
+void
+tcp_tmr(void)
+{
+ /* Call tcp_fasttmr() every 250 ms */
+ tcp_fasttmr();
+
+ if (++tcp_timer & 1) {
+ /* Call tcp_tmr() every 500 ms, i.e., every other timer
+ tcp_tmr() is called. */
+ tcp_slowtmr();
+ }
+}
+
+/**
+ * Closes the TX side of a connection held by the PCB.
+ * For tcp_close(), a RST is sent if the application didn't receive all data
+ * (tcp_recved() not called for all data passed to recv callback).
+ *
+ * Listening pcbs are freed and may not be referenced any more.
+ * Connection pcbs are freed if not yet connected and may not be referenced
+ * any more. If a connection is established (at least SYN received or in
+ * a closing state), the connection is closed, and put in a closing state.
+ * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
+ * unsafe to reference it.
+ *
+ * @param pcb the tcp_pcb to close
+ * @return ERR_OK if connection has been closed
+ * another err_t if closing failed and pcb is not freed
+ */
+static err_t
+tcp_close_shutdown(struct tcp_pcb *pcb, u8_t rst_on_unacked_data)
+{
+ err_t err;
+
+ if (rst_on_unacked_data && ((pcb->state == ESTABLISHED) || (pcb->state == CLOSE_WAIT))) {
+ if ((pcb->refused_data != NULL) || (pcb->rcv_wnd != TCP_WND)) {
+ /* Not all data received by application, send RST to tell the remote
+ side about this. */
+ LWIP_ASSERT("pcb->flags & TF_RXCLOSED", pcb->flags & TF_RXCLOSED);
+
+ /* don't call tcp_abort here: we must not deallocate the pcb since
+ that might not be expected when calling tcp_close */
+ tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+ pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb));
+
+ tcp_pcb_purge(pcb);
+ TCP_RMV_ACTIVE(pcb);
+ if (pcb->state == ESTABLISHED) {
+ /* move to TIME_WAIT since we close actively */
+ pcb->state = TIME_WAIT;
+ TCP_REG(&tcp_tw_pcbs, pcb);
+ } else {
+ /* CLOSE_WAIT: deallocate the pcb since we already sent a RST for it */
+ memp_free(MEMP_TCP_PCB, pcb);
+ }
+ return ERR_OK;
+ }
+ }
+
+ switch (pcb->state) {
+ case CLOSED:
+ /* Closing a pcb in the CLOSED state might seem erroneous,
+ * however, it is in this state once allocated and as yet unused
+ * and the user needs some way to free it should the need arise.
+ * Calling tcp_close() with a pcb that has already been closed, (i.e. twice)
+ * or for a pcb that has been used and then entered the CLOSED state
+ * is erroneous, but this should never happen as the pcb has in those cases
+ * been freed, and so any remaining handles are bogus. */
+ err = ERR_OK;
+ if (pcb->local_port != 0) {
+ TCP_RMV(&tcp_bound_pcbs, pcb);
+ }
+ memp_free(MEMP_TCP_PCB, pcb);
+ pcb = NULL;
+ break;
+ case LISTEN:
+ err = ERR_OK;
+ tcp_pcb_remove(&tcp_listen_pcbs.pcbs, pcb);
+ memp_free(MEMP_TCP_PCB_LISTEN, pcb);
+ pcb = NULL;
+ break;
+ case SYN_SENT:
+ err = ERR_OK;
+ TCP_PCB_REMOVE_ACTIVE(pcb);
+ memp_free(MEMP_TCP_PCB, pcb);
+ pcb = NULL;
+ snmp_inc_tcpattemptfails();
+ break;
+#if LWIP_CONNECTION_PROXY
+ case SYN_RCVD_0: /* FALLTHROUGH: treat like SYN_RCVD*/
+#endif
+ case SYN_RCVD:
+ err = tcp_send_fin(pcb);
+ if (err == ERR_OK) {
+ snmp_inc_tcpattemptfails();
+ pcb->state = FIN_WAIT_1;
+ }
+ break;
+ case ESTABLISHED:
+ err = tcp_send_fin(pcb);
+ if (err == ERR_OK) {
+ snmp_inc_tcpestabresets();
+ pcb->state = FIN_WAIT_1;
+ }
+ break;
+ case CLOSE_WAIT:
+ err = tcp_send_fin(pcb);
+ if (err == ERR_OK) {
+ snmp_inc_tcpestabresets();
+ pcb->state = LAST_ACK;
+ }
+ break;
+ default:
+ /* Has already been closed, do nothing. */
+ err = ERR_OK;
+ pcb = NULL;
+ break;
+ }
+
+ if (pcb != NULL && err == ERR_OK) {
+ /* To ensure all data has been sent when tcp_close returns, we have
+ to make sure tcp_output doesn't fail.
+ Since we don't really have to ensure all data has been sent when tcp_close
+ returns (unsent data is sent from tcp timer functions, also), we don't care
+ for the return value of tcp_output for now. */
+ /* @todo: When implementing SO_LINGER, this must be changed somehow:
+ If SOF_LINGER is set, the data should be sent and acked before close returns.
+ This can only be valid for sequential APIs, not for the raw API. */
+ tcp_output(pcb);
+ }
+ return err;
+}
+
+/**
+ * Closes the connection held by the PCB.
+ *
+ * Listening pcbs are freed and may not be referenced any more.
+ * Connection pcbs are freed if not yet connected and may not be referenced
+ * any more. If a connection is established (at least SYN received or in
+ * a closing state), the connection is closed, and put in a closing state.
+ * The pcb is then automatically freed in tcp_slowtmr(). It is therefore
+ * unsafe to reference it (unless an error is returned).
+ *
+ * @param pcb the tcp_pcb to close
+ * @return ERR_OK if connection has been closed
+ * another err_t if closing failed and pcb is not freed
+ */
+err_t
+tcp_close(struct tcp_pcb *pcb)
+{
+#if TCP_DEBUG
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_close: closing in "));
+ tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+
+ if (pcb->state != LISTEN) {
+ /* Set a flag not to receive any more data... */
+ pcb->flags |= TF_RXCLOSED;
+ }
+ /* ... and close */
+ return tcp_close_shutdown(pcb, 1);
+}
+
+/**
+ * Causes all or part of a full-duplex connection of this PCB to be shut down.
+ * This doesn't deallocate the PCB unless shutting down both sides!
+ * Shutting down both sides is the same as calling tcp_close, so if it succeds,
+ * the PCB should not be referenced any more.
+ *
+ * @param pcb PCB to shutdown
+ * @param shut_rx shut down receive side if this is != 0
+ * @param shut_tx shut down send side if this is != 0
+ * @return ERR_OK if shutdown succeeded (or the PCB has already been shut down)
+ * another err_t on error.
+ */
+err_t
+tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx)
+{
+ if (pcb->state == LISTEN) {
+ return ERR_CONN;
+ }
+ if (shut_rx) {
+ /* shut down the receive side: set a flag not to receive any more data... */
+ pcb->flags |= TF_RXCLOSED;
+ if (shut_tx) {
+ /* shutting down the tx AND rx side is the same as closing for the raw API */
+ return tcp_close_shutdown(pcb, 1);
+ }
+ /* ... and free buffered data */
+ if (pcb->refused_data != NULL) {
+ pbuf_free(pcb->refused_data);
+ pcb->refused_data = NULL;
+ }
+ }
+ if (shut_tx) {
+ /* This can't happen twice since if it succeeds, the pcb's state is changed.
+ Only close in these states as the others directly deallocate the PCB */
+ switch (pcb->state) {
+#if LWIP_CONNECTION_PROXY
+ case SYN_RCVD_0: /* FALLTHROUGH: treat like SYN_RCVD */
+#endif
+ case SYN_RCVD:
+ case ESTABLISHED:
+ case CLOSE_WAIT:
+ return tcp_close_shutdown(pcb, shut_rx);
+ default:
+ /* Not (yet?) connected, cannot shutdown the TX side as that would bring us
+ into CLOSED state, where the PCB is deallocated. */
+ return ERR_CONN;
+ }
+ }
+ return ERR_OK;
+}
+
+/**
+ * Abandons a connection and optionally sends a RST to the remote
+ * host. Deletes the local protocol control block. This is done when
+ * a connection is killed because of shortage of memory.
+ *
+ * @param pcb the tcp_pcb to abort
+ * @param reset boolean to indicate whether a reset should be sent
+ */
+void
+tcp_abandon(struct tcp_pcb *pcb, int reset)
+{
+ u32_t seqno, ackno;
+#if LWIP_CALLBACK_API
+ tcp_err_fn errf;
+#endif /* LWIP_CALLBACK_API */
+ void *errf_arg;
+
+ /* pcb->state LISTEN not allowed here */
+ LWIP_ASSERT("don't call tcp_abort/tcp_abandon for listen-pcbs",
+ pcb->state != LISTEN);
+ /* Figure out on which TCP PCB list we are, and remove us. If we
+ are in an active state, call the receive function associated with
+ the PCB with a NULL argument, and send an RST to the remote end. */
+ if (pcb->state == TIME_WAIT) {
+ tcp_pcb_remove(&tcp_tw_pcbs, pcb);
+ memp_free(MEMP_TCP_PCB, pcb);
+ } else {
+ int send_rst = reset && (pcb->state != CLOSED);
+ seqno = pcb->snd_nxt;
+ ackno = pcb->rcv_nxt;
+#if LWIP_CALLBACK_API
+ errf = pcb->errf;
+#endif /* LWIP_CALLBACK_API */
+ errf_arg = pcb->callback_arg;
+ TCP_PCB_REMOVE_ACTIVE(pcb);
+ if (pcb->unacked != NULL) {
+ tcp_segs_free(pcb->unacked);
+ }
+ if (pcb->unsent != NULL) {
+ tcp_segs_free(pcb->unsent);
+ }
+#if TCP_QUEUE_OOSEQ
+ if (pcb->ooseq != NULL) {
+ tcp_segs_free(pcb->ooseq);
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+ if (send_rst) {
+ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_abandon: sending RST\n"));
+ tcp_rst(seqno, ackno, &pcb->local_ip, &pcb->remote_ip, pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb));
+ }
+ memp_free(MEMP_TCP_PCB, pcb);
+ TCP_EVENT_ERR(errf, errf_arg, ERR_ABRT);
+ }
+}
+
+/**
+ * Aborts the connection by sending a RST (reset) segment to the remote
+ * host. The pcb is deallocated. This function never fails.
+ *
+ * ATTENTION: When calling this from one of the TCP callbacks, make
+ * sure you always return ERR_ABRT (and never return ERR_ABRT otherwise
+ * or you will risk accessing deallocated memory or memory leaks!
+ *
+ * @param pcb the tcp pcb to abort
+ */
+void
+tcp_abort(struct tcp_pcb *pcb)
+{
+ tcp_abandon(pcb, 1);
+}
+
+/**
+ * Binds the connection to a local portnumber and IP address. If the
+ * IP address is not given (i.e., ipaddr == NULL), the IP address of
+ * the outgoing network interface is used instead.
+ *
+ * @param pcb the tcp_pcb to bind (no check is done whether this pcb is
+ * already bound!)
+ * @param ipaddr the local ip address to bind to (use IP_ADDR_ANY to bind
+ * to any local address
+ * @param port the local port to bind to
+ * @return ERR_USE if the port is already in use
+ * ERR_VAL if bind failed because the PCB is not in a valid state
+ * ERR_OK if bound
+ */
+err_t
+tcp_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
+{
+ int i;
+ int max_pcb_list = NUM_TCP_PCB_LISTS;
+ struct tcp_pcb *cpcb;
+
+ LWIP_ERROR("tcp_bind: can only bind in state CLOSED", pcb->state == CLOSED, return ERR_VAL);
+
+#if SO_REUSE
+ /* Unless the REUSEADDR flag is set,
+ we have to check the pcbs in TIME-WAIT state, also.
+ We do not dump TIME_WAIT pcb's; they can still be matched by incoming
+ packets using both local and remote IP addresses and ports to distinguish.
+ */
+ if (ip_get_option(pcb, SOF_REUSEADDR)) {
+ max_pcb_list = NUM_TCP_PCB_LISTS_NO_TIME_WAIT;
+ }
+#endif /* SO_REUSE */
+
+ if (port == 0) {
+ port = tcp_new_port();
+ if (port == 0) {
+ return ERR_BUF;
+ }
+ }
+
+ /* Check if the address already is in use (on all lists) */
+ for (i = 0; i < max_pcb_list; i++) {
+ for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
+ if (cpcb->local_port == port) {
+#if SO_REUSE
+ /* Omit checking for the same port if both pcbs have REUSEADDR set.
+ For SO_REUSEADDR, the duplicate-check for a 5-tuple is done in
+ tcp_connect. */
+ if (!ip_get_option(pcb, SOF_REUSEADDR) ||
+ !ip_get_option(cpcb, SOF_REUSEADDR))
+#endif /* SO_REUSE */
+ {
+ /* @todo: check accept_any_ip_version */
+ if (IP_PCB_IPVER_EQ(pcb, cpcb) &&
+ (ipX_addr_isany(PCB_ISIPV6(pcb), &cpcb->local_ip) ||
+ ipX_addr_isany(PCB_ISIPV6(pcb), ip_2_ipX(ipaddr)) ||
+ ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->local_ip, ip_2_ipX(ipaddr)))) {
+ return ERR_USE;
+ }
+ }
+ }
+ }
+ }
+
+ if (!ipX_addr_isany(PCB_ISIPV6(pcb), ip_2_ipX(ipaddr))) {
+ ipX_addr_set(PCB_ISIPV6(pcb), &pcb->local_ip, ip_2_ipX(ipaddr));
+ }
+ pcb->local_port = port;
+ TCP_REG(&tcp_bound_pcbs, pcb);
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_bind: bind to port %"U16_F"\n", port));
+ return ERR_OK;
+}
+
+#if LWIP_CONNECTION_PROXY
+/**
+ * Like tcp_bind(), but the "local" address doesn't belong to any of
+ * local interfaces. The address and port must be specified.
+ */
+err_t
+tcp_proxy_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
+{
+ /* I think LWIP_ERROR in the original is too restrictive */
+ if (ipaddr == NULL || port == 0 || pcb->state != CLOSED
+ || ipX_addr_isany(PCB_ISIPV6(pcb), ip_2_ipX(ipaddr)))
+ {
+ return ERR_VAL;
+ }
+
+ /* Omit checking for the same port. The duplicate-check for a
+ 5-tuple is done in tcp_connect. */
+
+ ipX_addr_set(PCB_ISIPV6(pcb), &pcb->local_ip, ip_2_ipX(ipaddr));
+ pcb->local_port = port;
+ TCP_REG(&tcp_bound_pcbs, pcb);
+ return ERR_OK;
+}
+#endif /* LWIP_CONNECTION_PROXY */
+
+#if LWIP_CALLBACK_API
+/**
+ * Default accept callback if no accept callback is specified by the user.
+ */
+err_t
+tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(err);
+
+ tcp_abort(pcb);
+ return ERR_ABRT;
+}
+#endif /* LWIP_CALLBACK_API */
+
+#if LWIP_CONNECTION_PROXY
+/**
+ * Default proxy accept syn callback if no accept callback is specified by the user.
+ */
+err_t
+tcp_accept_syn_null(void *arg, struct tcp_pcb *newpcb, struct pbuf *syn)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_UNUSED_ARG(syn);
+
+ tcp_abort(newpcb);
+ return ERR_ABRT;
+}
+#endif /* LWIP_CONNECTION_PROXY */
+
+/**
+ * Set the state of the connection to be LISTEN, which means that it
+ * is able to accept incoming connections. The protocol control block
+ * is reallocated in order to consume less memory. Setting the
+ * connection to LISTEN is an irreversible process.
+ *
+ * @param pcb the original tcp_pcb
+ * @param backlog the incoming connections queue limit
+ * @return tcp_pcb used for listening, consumes less memory.
+ *
+ * @note The original tcp_pcb is freed. This function therefore has to be
+ * called like this:
+ * tpcb = tcp_listen(tpcb);
+ */
+struct tcp_pcb *
+tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
+{
+ struct tcp_pcb_listen *lpcb;
+
+ LWIP_UNUSED_ARG(backlog);
+ LWIP_ERROR("tcp_listen: pcb already connected", pcb->state == CLOSED, return NULL);
+
+ /* already listening? */
+ if (pcb->state == LISTEN) {
+ return pcb;
+ }
+#if SO_REUSE
+ if (ip_get_option(pcb, SOF_REUSEADDR)) {
+ /* Since SOF_REUSEADDR allows reusing a local address before the pcb's usage
+ is declared (listen-/connection-pcb), we have to make sure now that
+ this port is only used once for every local IP. */
+ for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+ if ((lpcb->local_port == pcb->local_port) &&
+ IP_PCB_IPVER_EQ(pcb, lpcb)) {
+ if (ipX_addr_cmp(PCB_ISIPV6(pcb), &lpcb->local_ip, &pcb->local_ip)) {
+ /* this address/port is already used */
+ return NULL;
+ }
+ }
+ }
+ }
+#endif /* SO_REUSE */
+ lpcb = (struct tcp_pcb_listen *)memp_malloc(MEMP_TCP_PCB_LISTEN);
+ if (lpcb == NULL) {
+ return NULL;
+ }
+ lpcb->callback_arg = pcb->callback_arg;
+ lpcb->local_port = pcb->local_port;
+ lpcb->state = LISTEN;
+ lpcb->prio = pcb->prio;
+ lpcb->so_options = pcb->so_options;
+ ip_set_option(lpcb, SOF_ACCEPTCONN);
+ lpcb->ttl = pcb->ttl;
+ lpcb->tos = pcb->tos;
+#if LWIP_IPV6
+ PCB_ISIPV6(lpcb) = PCB_ISIPV6(pcb);
+ lpcb->accept_any_ip_version = 0;
+#endif /* LWIP_IPV6 */
+#if LWIP_CONNECTION_PROXY
+ lpcb->accept_on_syn = 0;
+#endif
+ ipX_addr_copy(PCB_ISIPV6(pcb), lpcb->local_ip, pcb->local_ip);
+ if (pcb->local_port != 0) {
+ TCP_RMV(&tcp_bound_pcbs, pcb);
+ }
+ memp_free(MEMP_TCP_PCB, pcb);
+#if LWIP_CALLBACK_API
+ lpcb->accept = tcp_accept_null;
+#endif /* LWIP_CALLBACK_API */
+#if TCP_LISTEN_BACKLOG
+ lpcb->accepts_pending = 0;
+ lpcb->backlog = (backlog ? backlog : 1);
+#endif /* TCP_LISTEN_BACKLOG */
+ TCP_REG(&tcp_listen_pcbs.pcbs, (struct tcp_pcb *)lpcb);
+ return (struct tcp_pcb *)lpcb;
+}
+
+#if LWIP_IPV6
+/**
+ * Same as tcp_listen_with_backlog, but allows to accept IPv4 and IPv6
+ * connections, if the pcb's local address is set to ANY.
+ */
+struct tcp_pcb *
+tcp_listen_dual_with_backlog(struct tcp_pcb *pcb, u8_t backlog)
+{
+ struct tcp_pcb *lpcb;
+
+ lpcb = tcp_listen_with_backlog(pcb, backlog);
+ if ((lpcb != NULL) &&
+ ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) {
+ /* The default behavior is to accept connections on either
+ * IPv4 or IPv6, if not bound. */
+ /* @see NETCONN_FLAG_IPV6_V6ONLY for changing this behavior */
+ ((struct tcp_pcb_listen*)lpcb)->accept_any_ip_version = 1;
+ }
+ return lpcb;
+}
+#endif /* LWIP_IPV6 */
+
+/**
+ * Update the state that tracks the available window space to advertise.
+ *
+ * Returns how much extra window would be advertised if we sent an
+ * update now.
+ */
+u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb)
+{
+ u32_t new_right_edge = pcb->rcv_nxt + pcb->rcv_wnd;
+
+ if (TCP_SEQ_GEQ(new_right_edge, pcb->rcv_ann_right_edge + LWIP_MIN((TCP_WND / 2), pcb->mss))) {
+ /* we can advertise more window */
+ pcb->rcv_ann_wnd = pcb->rcv_wnd;
+ return new_right_edge - pcb->rcv_ann_right_edge;
+ } else {
+ if (TCP_SEQ_GT(pcb->rcv_nxt, pcb->rcv_ann_right_edge)) {
+ /* Can happen due to other end sending out of advertised window,
+ * but within actual available (but not yet advertised) window */
+ pcb->rcv_ann_wnd = 0;
+ } else {
+ /* keep the right edge of window constant */
+ u32_t new_rcv_ann_wnd = pcb->rcv_ann_right_edge - pcb->rcv_nxt;
+ LWIP_ASSERT("new_rcv_ann_wnd <= 0xffff", new_rcv_ann_wnd <= 0xffff);
+ pcb->rcv_ann_wnd = (u16_t)new_rcv_ann_wnd;
+ }
+ return 0;
+ }
+}
+
+/**
+ * This function should be called by the application when it has
+ * processed the data. The purpose is to advertise a larger window
+ * when the data has been processed.
+ *
+ * @param pcb the tcp_pcb for which data is read
+ * @param len the amount of bytes that have been read by the application
+ */
+void
+tcp_recved(struct tcp_pcb *pcb, u16_t len)
+{
+ int wnd_inflation;
+
+ /* pcb->state LISTEN not allowed here */
+ LWIP_ASSERT("don't call tcp_recved for listen-pcbs",
+ pcb->state != LISTEN);
+ LWIP_ASSERT("tcp_recved: len would wrap rcv_wnd\n",
+ len <= 0xffff - pcb->rcv_wnd );
+
+ pcb->rcv_wnd += len;
+ if (pcb->rcv_wnd > TCP_WND) {
+ pcb->rcv_wnd = TCP_WND;
+ }
+
+ wnd_inflation = tcp_update_rcv_ann_wnd(pcb);
+
+ /* If the change in the right edge of window is significant (default
+ * watermark is TCP_WND/4), then send an explicit update now.
+ * Otherwise wait for a packet to be sent in the normal course of
+ * events (or more window to be available later) */
+ if (wnd_inflation >= TCP_WND_UPDATE_THRESHOLD) {
+ tcp_ack_now(pcb);
+ tcp_output(pcb);
+ }
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_recved: received %"U16_F" bytes, wnd %"U16_F" (%"U16_F").\n",
+ len, pcb->rcv_wnd, TCP_WND - pcb->rcv_wnd));
+}
+
+/**
+ * Allocate a new local TCP port.
+ *
+ * @return a new (free) local TCP port number
+ */
+static u16_t
+tcp_new_port(void)
+{
+ u8_t i;
+ u16_t n = 0;
+ struct tcp_pcb *pcb;
+
+again:
+ if (tcp_port++ == TCP_LOCAL_PORT_RANGE_END) {
+ tcp_port = TCP_LOCAL_PORT_RANGE_START;
+ }
+ /* Check all PCB lists. */
+ for (i = 0; i < NUM_TCP_PCB_LISTS; i++) {
+ for(pcb = *tcp_pcb_lists[i]; pcb != NULL; pcb = pcb->next) {
+ if (pcb->local_port == tcp_port) {
+ if (++n > (TCP_LOCAL_PORT_RANGE_END - TCP_LOCAL_PORT_RANGE_START)) {
+ return 0;
+ }
+ goto again;
+ }
+ }
+ }
+ return tcp_port;
+}
+
+/**
+ * Connects to another host. The function given as the "connected"
+ * argument will be called when the connection has been established.
+ *
+ * @param pcb the tcp_pcb used to establish the connection
+ * @param ipaddr the remote ip address to connect to
+ * @param port the remote tcp port to connect to
+ * @param connected callback function to call when connected (or on error)
+ * @return ERR_VAL if invalid arguments are given
+ * ERR_OK if connect request has been sent
+ * other err_t values if connect request couldn't be sent
+ */
+err_t
+tcp_connect(struct tcp_pcb *pcb, ip_addr_t *ipaddr, u16_t port,
+ tcp_connected_fn connected)
+{
+ err_t ret;
+ u32_t iss;
+ u16_t old_local_port;
+
+ LWIP_ERROR("tcp_connect: can only connect from state CLOSED", pcb->state == CLOSED, return ERR_ISCONN);
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_connect to port %"U16_F"\n", port));
+ if (ipaddr != NULL) {
+ ipX_addr_set(PCB_ISIPV6(pcb), &pcb->remote_ip, ip_2_ipX(ipaddr));
+ } else {
+ return ERR_VAL;
+ }
+ pcb->remote_port = port;
+
+ /* check if we have a route to the remote host */
+ if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) {
+ /* no local IP address set, yet. */
+ struct netif *netif;
+ ipX_addr_t *local_ip;
+ ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip);
+ if ((netif == NULL) || (local_ip == NULL)) {
+ /* Don't even try to send a SYN packet if we have no route
+ since that will fail. */
+ return ERR_RTE;
+ }
+ /* Use the address as local address of the pcb. */
+ ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip);
+ }
+
+ old_local_port = pcb->local_port;
+ if (pcb->local_port == 0) {
+ pcb->local_port = tcp_new_port();
+ if (pcb->local_port == 0) {
+ return ERR_BUF;
+ }
+ }
+#if SO_REUSE
+ if (ip_get_option(pcb, SOF_REUSEADDR)) {
+ /* Since SOF_REUSEADDR allows reusing a local address, we have to make sure
+ now that the 5-tuple is unique. */
+ struct tcp_pcb *cpcb;
+ int i;
+ /* Don't check listen- and bound-PCBs, check active- and TIME-WAIT PCBs. */
+ for (i = 2; i < NUM_TCP_PCB_LISTS; i++) {
+ for(cpcb = *tcp_pcb_lists[i]; cpcb != NULL; cpcb = cpcb->next) {
+ if ((cpcb->local_port == pcb->local_port) &&
+ (cpcb->remote_port == port) &&
+ IP_PCB_IPVER_EQ(cpcb, pcb) &&
+ ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->local_ip, &pcb->local_ip) &&
+ ipX_addr_cmp(PCB_ISIPV6(pcb), &cpcb->remote_ip, ip_2_ipX(ipaddr))) {
+ /* linux returns EISCONN here, but ERR_USE should be OK for us */
+ return ERR_USE;
+ }
+ }
+ }
+ }
+#endif /* SO_REUSE */
+ iss = tcp_next_iss();
+ pcb->rcv_nxt = 0;
+ pcb->snd_nxt = iss;
+ pcb->lastack = iss - 1;
+ pcb->snd_lbb = iss - 1;
+ pcb->rcv_wnd = TCP_WND;
+ pcb->rcv_ann_wnd = TCP_WND;
+ pcb->rcv_ann_right_edge = pcb->rcv_nxt;
+ pcb->snd_wnd = TCP_WND;
+ /* As initial send MSS, we use TCP_MSS but limit it to 536.
+ The send MSS is updated when an MSS option is received. */
+ pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;
+#if TCP_CALCULATE_EFF_SEND_MSS
+ pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip, PCB_ISIPV6(pcb));
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+ pcb->cwnd = 1;
+ pcb->ssthresh = pcb->mss * 10;
+#if LWIP_CALLBACK_API
+ pcb->connected = connected;
+#else /* LWIP_CALLBACK_API */
+ LWIP_UNUSED_ARG(connected);
+#endif /* LWIP_CALLBACK_API */
+
+ /* Send a SYN together with the MSS option. */
+ ret = tcp_enqueue_flags(pcb, TCP_SYN);
+ if (ret == ERR_OK) {
+ /* SYN segment was enqueued, changed the pcbs state now */
+ pcb->state = SYN_SENT;
+ if (old_local_port != 0) {
+ TCP_RMV(&tcp_bound_pcbs, pcb);
+ }
+ TCP_REG_ACTIVE(pcb);
+ snmp_inc_tcpactiveopens();
+
+ tcp_output(pcb);
+ }
+ return ret;
+}
+
+/**
+ * Called every 500 ms and implements the retransmission timer and the timer that
+ * removes PCBs that have been in TIME-WAIT for enough time. It also increments
+ * various timers such as the inactivity timer in each PCB.
+ *
+ * Automatically called from tcp_tmr().
+ */
+void
+tcp_slowtmr(void)
+{
+ struct tcp_pcb *pcb, *prev;
+ u16_t eff_wnd;
+ u8_t pcb_remove; /* flag if a PCB should be removed */
+ u8_t pcb_reset; /* flag if a RST should be sent when removing */
+ err_t err;
+
+ err = ERR_OK;
+
+ ++tcp_ticks;
+ ++tcp_timer_ctr;
+
+tcp_slowtmr_start:
+ /* Steps through all of the active PCBs. */
+ prev = NULL;
+ pcb = tcp_active_pcbs;
+ if (pcb == NULL) {
+#if 0 /* XXX: uwe: disable for now to reduce noise */
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: no active pcbs\n"));
+#endif
+ }
+ while (pcb != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: processing active pcb\n"));
+ LWIP_ASSERT("tcp_slowtmr: active pcb->state != CLOSED\n", pcb->state != CLOSED);
+ LWIP_ASSERT("tcp_slowtmr: active pcb->state != LISTEN\n", pcb->state != LISTEN);
+ LWIP_ASSERT("tcp_slowtmr: active pcb->state != TIME-WAIT\n", pcb->state != TIME_WAIT);
+ if (pcb->last_timer == tcp_timer_ctr) {
+ /* skip this pcb, we have already processed it */
+ pcb = pcb->next;
+ continue;
+ }
+ pcb->last_timer = tcp_timer_ctr;
+
+ pcb_remove = 0;
+ pcb_reset = 0;
+
+ if (pcb->state == SYN_SENT && pcb->nrtx == TCP_SYNMAXRTX) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max SYN retries reached\n"));
+ }
+ else if (pcb->nrtx == TCP_MAXRTX) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: max DATA retries reached\n"));
+ } else {
+ if (pcb->persist_backoff > 0) {
+ /* If snd_wnd is zero, use persist timer to send 1 byte probes
+ * instead of using the standard retransmission mechanism. */
+ pcb->persist_cnt++;
+ if (pcb->persist_cnt >= tcp_persist_backoff[pcb->persist_backoff-1]) {
+ pcb->persist_cnt = 0;
+ if (pcb->persist_backoff < sizeof(tcp_persist_backoff)) {
+ pcb->persist_backoff++;
+ }
+ tcp_zero_window_probe(pcb);
+ }
+ } else {
+ /* Increase the retransmission timer if it is running */
+ if(pcb->rtime >= 0) {
+ ++pcb->rtime;
+ }
+
+ if (pcb->unacked != NULL && pcb->rtime >= pcb->rto) {
+ /* Time for a retransmission. */
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_slowtmr: rtime %"S16_F
+ " pcb->rto %"S16_F"\n",
+ pcb->rtime, pcb->rto));
+
+ /* Double retransmission time-out unless we are trying to
+ * connect to somebody (i.e., we are in SYN_SENT). */
+ if (pcb->state != SYN_SENT) {
+ pcb->rto = ((pcb->sa >> 3) + pcb->sv) << tcp_backoff[pcb->nrtx];
+ }
+
+ /* Reset the retransmission timer. */
+ pcb->rtime = 0;
+
+ /* Reduce congestion window and ssthresh. */
+ eff_wnd = LWIP_MIN(pcb->cwnd, pcb->snd_wnd);
+ pcb->ssthresh = eff_wnd >> 1;
+ if (pcb->ssthresh < (pcb->mss << 1)) {
+ pcb->ssthresh = (pcb->mss << 1);
+ }
+ pcb->cwnd = pcb->mss;
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: cwnd %"U16_F
+ " ssthresh %"U16_F"\n",
+ pcb->cwnd, pcb->ssthresh));
+
+ /* The following needs to be called AFTER cwnd is set to one
+ mss - STJ */
+ tcp_rexmit_rto(pcb);
+ }
+ }
+ }
+ /* Check if this PCB has stayed too long in FIN-WAIT-2 */
+ if (pcb->state == FIN_WAIT_2) {
+ /* If this PCB is in FIN_WAIT_2 because of SHUT_WR don't let it time out. */
+ if (pcb->flags & TF_RXCLOSED) {
+ /* PCB was fully closed (either through close() or SHUT_RDWR):
+ normal FIN-WAIT timeout handling. */
+ if ((u32_t)(tcp_ticks - pcb->tmr) >
+ TCP_FIN_WAIT_TIMEOUT / TCP_SLOW_INTERVAL) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in FIN-WAIT-2\n"));
+ }
+ }
+ }
+
+ /* Check if KEEPALIVE should be sent */
+ if(ip_get_option(pcb, SOF_KEEPALIVE) &&
+ ((pcb->state == ESTABLISHED) ||
+ (pcb->state == CLOSE_WAIT))) {
+ if((u32_t)(tcp_ticks - pcb->tmr) >
+ (pcb->keep_idle + TCP_KEEP_DUR(pcb)) / TCP_SLOW_INTERVAL)
+ {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: KEEPALIVE timeout. Aborting connection to "));
+ ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip);
+ LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+
+ ++pcb_remove;
+ ++pcb_reset;
+ }
+ else if((u32_t)(tcp_ticks - pcb->tmr) >
+ (pcb->keep_idle + pcb->keep_cnt_sent * TCP_KEEP_INTVL(pcb))
+ / TCP_SLOW_INTERVAL)
+ {
+ tcp_keepalive(pcb);
+ pcb->keep_cnt_sent++;
+ }
+ }
+
+ /* If this PCB has queued out of sequence data, but has been
+ inactive for too long, will drop the data (it will eventually
+ be retransmitted). */
+#if TCP_QUEUE_OOSEQ
+ if (pcb->ooseq != NULL &&
+ (u32_t)tcp_ticks - pcb->tmr >= pcb->rto * TCP_OOSEQ_TIMEOUT) {
+ tcp_segs_free(pcb->ooseq);
+ pcb->ooseq = NULL;
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_slowtmr: dropping OOSEQ queued data\n"));
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+
+ /*
+ * XXX: uwe: SYN_RCVD_0 should probabaly use separate timeout
+ * value, since, effectively, it's connection (SYN_SENT) timeout
+ * (to the real destination - as done by proxy hanlder).
+ */
+ /* Check if this PCB has stayed too long in SYN-RCVD */
+ if (pcb->state == SYN_RCVD
+#if LWIP_CONNECTION_PROXY
+ || pcb->state == SYN_RCVD_0
+#endif
+ )
+ {
+ if ((u32_t)(tcp_ticks - pcb->tmr) >
+ TCP_SYN_RCVD_TIMEOUT / TCP_SLOW_INTERVAL) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in SYN-RCVD\n"));
+ }
+ }
+
+ /* Check if this PCB has stayed too long in LAST-ACK */
+ if (pcb->state == LAST_ACK) {
+ if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
+ ++pcb_remove;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: removing pcb stuck in LAST-ACK\n"));
+ }
+ }
+
+ /* If the PCB should be removed, do it. */
+ if (pcb_remove) {
+ struct tcp_pcb *pcb2;
+ tcp_err_fn err_fn;
+ void *err_arg;
+ tcp_pcb_purge(pcb);
+ /* Remove PCB from tcp_active_pcbs list. */
+ if (prev != NULL) {
+ LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_active_pcbs", pcb != tcp_active_pcbs);
+ prev->next = pcb->next;
+ } else {
+ /* This PCB was the first. */
+ LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_active_pcbs", tcp_active_pcbs == pcb);
+ tcp_active_pcbs = pcb->next;
+ }
+
+ if (pcb_reset) {
+ tcp_rst(pcb->snd_nxt, pcb->rcv_nxt, &pcb->local_ip, &pcb->remote_ip,
+ pcb->local_port, pcb->remote_port, PCB_ISIPV6(pcb));
+ }
+
+ err_fn = pcb->errf;
+ err_arg = pcb->callback_arg;
+ pcb2 = pcb;
+ pcb = pcb->next;
+ memp_free(MEMP_TCP_PCB, pcb2);
+
+ tcp_active_pcbs_changed = 0;
+ TCP_EVENT_ERR(err_fn, err_arg, ERR_ABRT);
+ if (tcp_active_pcbs_changed) {
+ goto tcp_slowtmr_start;
+ }
+ } else {
+ /* get the 'next' element now and work with 'prev' below (in case of abort) */
+ prev = pcb;
+ pcb = pcb->next;
+
+ /* We check if we should poll the connection. */
+ ++prev->polltmr;
+ if (prev->polltmr >= prev->pollinterval) {
+ prev->polltmr = 0;
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_slowtmr: polling application\n"));
+ tcp_active_pcbs_changed = 0;
+ TCP_EVENT_POLL(prev, err);
+ if (tcp_active_pcbs_changed) {
+ goto tcp_slowtmr_start;
+ }
+ /* if err == ERR_ABRT, 'prev' is already deallocated */
+ if (err == ERR_OK) {
+ tcp_output(prev);
+ }
+ }
+ }
+ }
+
+
+ /* Steps through all of the TIME-WAIT PCBs. */
+ prev = NULL;
+ pcb = tcp_tw_pcbs;
+ while (pcb != NULL) {
+ LWIP_ASSERT("tcp_slowtmr: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+ pcb_remove = 0;
+
+ /* Check if this PCB has stayed long enough in TIME-WAIT */
+ if ((u32_t)(tcp_ticks - pcb->tmr) > 2 * TCP_MSL / TCP_SLOW_INTERVAL) {
+ ++pcb_remove;
+ }
+
+
+
+ /* If the PCB should be removed, do it. */
+ if (pcb_remove) {
+ struct tcp_pcb *pcb2;
+ tcp_pcb_purge(pcb);
+ /* Remove PCB from tcp_tw_pcbs list. */
+ if (prev != NULL) {
+ LWIP_ASSERT("tcp_slowtmr: middle tcp != tcp_tw_pcbs", pcb != tcp_tw_pcbs);
+ prev->next = pcb->next;
+ } else {
+ /* This PCB was the first. */
+ LWIP_ASSERT("tcp_slowtmr: first pcb == tcp_tw_pcbs", tcp_tw_pcbs == pcb);
+ tcp_tw_pcbs = pcb->next;
+ }
+ pcb2 = pcb;
+ pcb = pcb->next;
+ memp_free(MEMP_TCP_PCB, pcb2);
+ } else {
+ prev = pcb;
+ pcb = pcb->next;
+ }
+ }
+}
+
+/**
+ * Is called every TCP_FAST_INTERVAL (250 ms) and process data previously
+ * "refused" by upper layer (application) and sends delayed ACKs.
+ *
+ * Automatically called from tcp_tmr().
+ */
+void
+tcp_fasttmr(void)
+{
+ struct tcp_pcb *pcb;
+
+ ++tcp_timer_ctr;
+
+tcp_fasttmr_start:
+ pcb = tcp_active_pcbs;
+
+ while(pcb != NULL) {
+ if (pcb->last_timer != tcp_timer_ctr) {
+ struct tcp_pcb *next;
+ pcb->last_timer = tcp_timer_ctr;
+ /* send delayed ACKs */
+ if (pcb->flags & TF_ACK_DELAY) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_fasttmr: delayed ACK\n"));
+ tcp_ack_now(pcb);
+ tcp_output(pcb);
+ pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+ }
+
+ next = pcb->next;
+
+ /* If there is data which was previously "refused" by upper layer */
+ if (pcb->refused_data != NULL) {
+ tcp_active_pcbs_changed = 0;
+ tcp_process_refused_data(pcb);
+ if (tcp_active_pcbs_changed) {
+ /* application callback has changed the pcb list: restart the loop */
+ goto tcp_fasttmr_start;
+ }
+ }
+ pcb = next;
+ } else {
+ pcb = pcb->next;
+ }
+ }
+}
+
+/** Pass pcb->refused_data to the recv callback */
+err_t
+tcp_process_refused_data(struct tcp_pcb *pcb)
+{
+ err_t err;
+ u8_t refused_flags = pcb->refused_data->flags;
+ /* set pcb->refused_data to NULL in case the callback frees it and then
+ closes the pcb */
+ struct pbuf *refused_data = pcb->refused_data;
+ pcb->refused_data = NULL;
+ /* Notify again application with data previously received. */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: notify kept packet\n"));
+ TCP_EVENT_RECV(pcb, refused_data, ERR_OK, err);
+ if (err == ERR_OK) {
+ /* did refused_data include a FIN? */
+ if (refused_flags & PBUF_FLAG_TCP_FIN) {
+ /* correct rcv_wnd as the application won't call tcp_recved()
+ for the FIN's seqno */
+ if (pcb->rcv_wnd != TCP_WND) {
+ pcb->rcv_wnd++;
+ }
+ TCP_EVENT_CLOSED(pcb, err);
+ if (err == ERR_ABRT) {
+ return ERR_ABRT;
+ }
+ }
+ } else if (err == ERR_ABRT) {
+ /* if err == ERR_ABRT, 'pcb' is already deallocated */
+ /* Drop incoming packets because pcb is "full" (only if the incoming
+ segment contains data). */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: drop incoming packets, because pcb is \"full\"\n"));
+ return ERR_ABRT;
+ } else {
+ /* data is still refused, pbuf is still valid (go on for ACK-only packets) */
+ pcb->refused_data = refused_data;
+ }
+ return ERR_OK;
+}
+
+/**
+ * Deallocates a list of TCP segments (tcp_seg structures).
+ *
+ * @param seg tcp_seg list of TCP segments to free
+ */
+void
+tcp_segs_free(struct tcp_seg *seg)
+{
+ while (seg != NULL) {
+ struct tcp_seg *next = seg->next;
+ tcp_seg_free(seg);
+ seg = next;
+ }
+}
+
+/**
+ * Frees a TCP segment (tcp_seg structure).
+ *
+ * @param seg single tcp_seg to free
+ */
+void
+tcp_seg_free(struct tcp_seg *seg)
+{
+ if (seg != NULL) {
+ if (seg->p != NULL) {
+ pbuf_free(seg->p);
+#if TCP_DEBUG
+ seg->p = NULL;
+#endif /* TCP_DEBUG */
+ }
+ memp_free(MEMP_TCP_SEG, seg);
+ }
+}
+
+/**
+ * Sets the priority of a connection.
+ *
+ * @param pcb the tcp_pcb to manipulate
+ * @param prio new priority
+ */
+void
+tcp_setprio(struct tcp_pcb *pcb, u8_t prio)
+{
+ pcb->prio = prio;
+}
+
+#if TCP_QUEUE_OOSEQ
+/**
+ * Returns a copy of the given TCP segment.
+ * The pbuf and data are not copied, only the pointers
+ *
+ * @param seg the old tcp_seg
+ * @return a copy of seg
+ */
+struct tcp_seg *
+tcp_seg_copy(struct tcp_seg *seg)
+{
+ struct tcp_seg *cseg;
+
+ cseg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG);
+ if (cseg == NULL) {
+ return NULL;
+ }
+ SMEMCPY((u8_t *)cseg, (const u8_t *)seg, sizeof(struct tcp_seg));
+ pbuf_ref(cseg->p);
+ return cseg;
+}
+#endif /* TCP_QUEUE_OOSEQ */
+
+#if LWIP_CALLBACK_API
+/**
+ * Default receive callback that is called if the user didn't register
+ * a recv callback for the pcb.
+ */
+err_t
+tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err)
+{
+ LWIP_UNUSED_ARG(arg);
+ if (p != NULL) {
+ tcp_recved(pcb, p->tot_len);
+ pbuf_free(p);
+ } else if (err == ERR_OK) {
+ return tcp_close(pcb);
+ }
+ return ERR_OK;
+}
+#endif /* LWIP_CALLBACK_API */
+
+/**
+ * Kills the oldest active connection that has the same or lower priority than
+ * 'prio'.
+ *
+ * @param prio minimum priority
+ */
+static void
+tcp_kill_prio(u8_t prio)
+{
+ struct tcp_pcb *pcb, *inactive;
+ u32_t inactivity;
+ u8_t mprio;
+
+
+ mprio = TCP_PRIO_MAX;
+
+ /* We kill the oldest active connection that has lower priority than prio. */
+ inactivity = 0;
+ inactive = NULL;
+ for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ if (pcb->prio <= prio &&
+ pcb->prio <= mprio &&
+ (u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
+ inactivity = tcp_ticks - pcb->tmr;
+ inactive = pcb;
+ mprio = pcb->prio;
+ }
+ }
+ if (inactive != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_prio: killing oldest PCB %p (%"S32_F")\n",
+ (void *)inactive, inactivity));
+ tcp_abort(inactive);
+ }
+}
+
+/**
+ * Kills the oldest connection that is in TIME_WAIT state.
+ * Called from tcp_alloc() if no more connections are available.
+ */
+static void
+tcp_kill_timewait(void)
+{
+ struct tcp_pcb *pcb, *inactive;
+ u32_t inactivity;
+
+ inactivity = 0;
+ inactive = NULL;
+ /* Go through the list of TIME_WAIT pcbs and get the oldest pcb. */
+ for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+ if ((u32_t)(tcp_ticks - pcb->tmr) >= inactivity) {
+ inactivity = tcp_ticks - pcb->tmr;
+ inactive = pcb;
+ }
+ }
+ if (inactive != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_kill_timewait: killing oldest TIME-WAIT PCB %p (%"S32_F")\n",
+ (void *)inactive, inactivity));
+ tcp_abort(inactive);
+ }
+}
+
+/**
+ * Allocate a new tcp_pcb structure.
+ *
+ * @param prio priority for the new pcb
+ * @return a new tcp_pcb that initially is in state CLOSED
+ */
+struct tcp_pcb *
+tcp_alloc(u8_t prio)
+{
+ struct tcp_pcb *pcb;
+ u32_t iss;
+
+ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+ if (pcb == NULL) {
+ /* Try killing oldest connection in TIME-WAIT. */
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing off oldest TIME-WAIT connection\n"));
+ tcp_kill_timewait();
+ /* Try to allocate a tcp_pcb again. */
+ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+ if (pcb == NULL) {
+ /* Try killing active connections with lower priority than the new one. */
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_alloc: killing connection with prio lower than %d\n", prio));
+ tcp_kill_prio(prio);
+ /* Try to allocate a tcp_pcb again. */
+ pcb = (struct tcp_pcb *)memp_malloc(MEMP_TCP_PCB);
+ if (pcb != NULL) {
+ /* adjust err stats: memp_malloc failed twice before */
+ MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+ }
+ }
+ if (pcb != NULL) {
+ /* adjust err stats: timewait PCB was freed above */
+ MEMP_STATS_DEC(err, MEMP_TCP_PCB);
+ }
+ }
+ if (pcb != NULL) {
+ memset(pcb, 0, sizeof(struct tcp_pcb));
+ pcb->prio = prio;
+ pcb->snd_buf = TCP_SND_BUF;
+ pcb->snd_queuelen = 0;
+ pcb->rcv_wnd = TCP_WND;
+ pcb->rcv_ann_wnd = TCP_WND;
+ pcb->tos = 0;
+ pcb->ttl = TCP_TTL;
+ /* As initial send MSS, we use TCP_MSS but limit it to 536.
+ The send MSS is updated when an MSS option is received. */
+ pcb->mss = (TCP_MSS > 536) ? 536 : TCP_MSS;
+ pcb->rto = 3000 / TCP_SLOW_INTERVAL;
+ pcb->sa = 0;
+ pcb->sv = 3000 / TCP_SLOW_INTERVAL;
+ pcb->rtime = -1;
+ pcb->cwnd = 1;
+ iss = tcp_next_iss();
+ pcb->snd_wl2 = iss;
+ pcb->snd_nxt = iss;
+ pcb->lastack = iss;
+ pcb->snd_lbb = iss;
+ pcb->tmr = tcp_ticks;
+ pcb->last_timer = tcp_timer_ctr;
+
+ pcb->polltmr = 0;
+
+#if LWIP_CALLBACK_API
+ pcb->recv = tcp_recv_null;
+#endif /* LWIP_CALLBACK_API */
+
+ /* Init KEEPALIVE timer */
+ pcb->keep_idle = TCP_KEEPIDLE_DEFAULT;
+
+#if LWIP_TCP_KEEPALIVE
+ pcb->keep_intvl = TCP_KEEPINTVL_DEFAULT;
+ pcb->keep_cnt = TCP_KEEPCNT_DEFAULT;
+#endif /* LWIP_TCP_KEEPALIVE */
+
+ pcb->keep_cnt_sent = 0;
+ }
+ return pcb;
+}
+
+/**
+ * Creates a new TCP protocol control block but doesn't place it on
+ * any of the TCP PCB lists.
+ * The pcb is not put on any list until binding using tcp_bind().
+ *
+ * @internal: Maybe there should be a idle TCP PCB list where these
+ * PCBs are put on. Port reservation using tcp_bind() is implemented but
+ * allocated pcbs that are not bound can't be killed automatically if wanting
+ * to allocate a pcb with higher prio (@see tcp_kill_prio())
+ *
+ * @return a new tcp_pcb that initially is in state CLOSED
+ */
+struct tcp_pcb *
+tcp_new(void)
+{
+ return tcp_alloc(TCP_PRIO_NORMAL);
+}
+
+#if LWIP_IPV6
+/**
+ * Creates a new TCP-over-IPv6 protocol control block but doesn't
+ * place it on any of the TCP PCB lists.
+ * The pcb is not put on any list until binding using tcp_bind().
+ *
+ * @return a new tcp_pcb that initially is in state CLOSED
+ */
+struct tcp_pcb *
+tcp_new_ip6(void)
+{
+ struct tcp_pcb * pcb;
+ pcb = tcp_alloc(TCP_PRIO_NORMAL);
+ ip_set_v6(pcb, 1);
+ return pcb;
+}
+#endif /* LWIP_IPV6 */
+
+/**
+ * Used to specify the argument that should be passed callback
+ * functions.
+ *
+ * @param pcb tcp_pcb to set the callback argument
+ * @param arg void pointer argument to pass to callback functions
+ */
+void
+tcp_arg(struct tcp_pcb *pcb, void *arg)
+{
+ /* This function is allowed to be called for both listen pcbs and
+ connection pcbs. */
+ pcb->callback_arg = arg;
+}
+
+#if LWIP_CALLBACK_API
+
+/**
+ * Used to specify the function that should be called when a TCP
+ * connection receives data.
+ *
+ * @param pcb tcp_pcb to set the recv callback
+ * @param recv callback function to call for this pcb when data is received
+ */
+void
+tcp_recv(struct tcp_pcb *pcb, tcp_recv_fn recv)
+{
+ LWIP_ASSERT("invalid socket state for recv callback", pcb->state != LISTEN);
+ pcb->recv = recv;
+}
+
+/**
+ * Used to specify the function that should be called when TCP data
+ * has been successfully delivered to the remote host.
+ *
+ * @param pcb tcp_pcb to set the sent callback
+ * @param sent callback function to call for this pcb when data is successfully sent
+ */
+void
+tcp_sent(struct tcp_pcb *pcb, tcp_sent_fn sent)
+{
+ LWIP_ASSERT("invalid socket state for sent callback", pcb->state != LISTEN);
+ pcb->sent = sent;
+}
+
+/**
+ * Used to specify the function that should be called when a fatal error
+ * has occured on the connection.
+ *
+ * @param pcb tcp_pcb to set the err callback
+ * @param err callback function to call for this pcb when a fatal error
+ * has occured on the connection
+ */
+void
+tcp_err(struct tcp_pcb *pcb, tcp_err_fn err)
+{
+ LWIP_ASSERT("invalid socket state for err callback", pcb->state != LISTEN);
+ pcb->errf = err;
+}
+
+/**
+ * Used for specifying the function that should be called when a
+ * LISTENing connection has been connected to another host.
+ *
+ * @param pcb tcp_pcb to set the accept callback
+ * @param accept callback function to call for this pcb when LISTENing
+ * connection has been connected to another host
+ */
+void
+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 LWIP_CONNECTION_PROXY
+/**
+ * Used for specifying the function that should be called when a new
+ * proxy (pseudo-LISTENing) connection has been connected to another
+ * host. Like tcp_accept(), but there is no actual LISTENing pcb.
+ *
+ * @param accept callback function to call when new proxy
+ * (pseudo-LISTENing) connection has been connected to another
+ * host
+ */
+void
+tcp_proxy_accept(tcp_accept_syn_fn accept_syn)
+{
+ tcp_proxy_accept_callback = accept_syn;
+}
+
+
+/**
+ * A cross between normal accept and proxy early accept. Like with
+ * normal accept there's a normal LISTENing pcb. Like with proxy, the
+ * accept callback will be called on the first SYN.
+ */
+void
+tcp_accept_syn(struct tcp_pcb *pcb, tcp_accept_syn_fn accept_syn)
+{
+ struct tcp_pcb_listen *lpcb;
+
+ LWIP_ASSERT("invalid socket state for accept callback", pcb->state == LISTEN);
+ lpcb = (struct tcp_pcb_listen *)pcb;
+
+ lpcb->accept = (tcp_accept_fn)(uintptr_t)accept_syn;
+ lpcb->accept_on_syn = 1;
+}
+#endif /* LWIP_CONNECTION_PROXY */
+
+#endif /* LWIP_CALLBACK_API */
+
+
+/**
+ * Used to specify the function that should be called periodically
+ * from TCP. The interval is specified in terms of the TCP coarse
+ * timer interval, which is called twice a second.
+ *
+ */
+void
+tcp_poll(struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval)
+{
+ LWIP_ASSERT("invalid socket state for poll", pcb->state != LISTEN);
+#if LWIP_CALLBACK_API
+ pcb->poll = poll;
+#else /* LWIP_CALLBACK_API */
+ LWIP_UNUSED_ARG(poll);
+#endif /* LWIP_CALLBACK_API */
+ pcb->pollinterval = interval;
+}
+
+/**
+ * Purges a TCP PCB. Removes any buffered data and frees the buffer memory
+ * (pcb->ooseq, pcb->unsent and pcb->unacked are freed).
+ *
+ * @param pcb tcp_pcb to purge. The pcb itself is not deallocated!
+ */
+void
+tcp_pcb_purge(struct tcp_pcb *pcb)
+{
+ if (pcb->state != CLOSED &&
+ pcb->state != TIME_WAIT &&
+ pcb->state != LISTEN) {
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge\n"));
+
+#if TCP_LISTEN_BACKLOG
+ /* XXX: TODO: handle pcb->state == SYN_RCVD_0 vs. proxy listen backlog */
+ if (pcb->state == SYN_RCVD) {
+ /* Need to find the corresponding listen_pcb and decrease its accepts_pending */
+ struct tcp_pcb_listen *lpcb;
+ LWIP_ASSERT("tcp_pcb_purge: pcb->state == SYN_RCVD but tcp_listen_pcbs is NULL",
+ tcp_listen_pcbs.listen_pcbs != NULL);
+ for (lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+ if ((lpcb->local_port == pcb->local_port) &&
+ IP_PCB_IPVER_EQ(pcb, lpcb) &&
+ (ipX_addr_isany(PCB_ISIPV6(lpcb), &lpcb->local_ip) ||
+ ipX_addr_cmp(PCB_ISIPV6(lpcb), &pcb->local_ip, &lpcb->local_ip))) {
+ /* port and address of the listen pcb match the timed-out pcb */
+ LWIP_ASSERT("tcp_pcb_purge: listen pcb does not have accepts pending",
+ lpcb->accepts_pending > 0);
+ lpcb->accepts_pending--;
+ break;
+ }
+ }
+ }
+#endif /* TCP_LISTEN_BACKLOG */
+
+
+ if (pcb->refused_data != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->refused_data\n"));
+ pbuf_free(pcb->refused_data);
+ pcb->refused_data = NULL;
+ }
+ if (pcb->unsent != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: not all data sent\n"));
+ }
+ if (pcb->unacked != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->unacked\n"));
+ }
+#if TCP_QUEUE_OOSEQ
+ if (pcb->ooseq != NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_pcb_purge: data left on ->ooseq\n"));
+ }
+ tcp_segs_free(pcb->ooseq);
+ pcb->ooseq = NULL;
+#endif /* TCP_QUEUE_OOSEQ */
+
+ /* Stop the retransmission timer as it will expect data on unacked
+ queue if it fires */
+ pcb->rtime = -1;
+
+ tcp_segs_free(pcb->unsent);
+ tcp_segs_free(pcb->unacked);
+ pcb->unacked = pcb->unsent = NULL;
+#if TCP_OVERSIZE
+ pcb->unsent_oversize = 0;
+#endif /* TCP_OVERSIZE */
+ }
+}
+
+/**
+ * Purges the PCB and removes it from a PCB list. Any delayed ACKs are sent first.
+ *
+ * @param pcblist PCB list to purge.
+ * @param pcb tcp_pcb to purge. The pcb itself is NOT deallocated!
+ */
+void
+tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb)
+{
+ TCP_RMV(pcblist, pcb);
+
+ tcp_pcb_purge(pcb);
+
+ /* if there is an outstanding delayed ACKs, send it */
+ if (pcb->state != TIME_WAIT &&
+ pcb->state != LISTEN &&
+ pcb->flags & TF_ACK_DELAY) {
+ pcb->flags |= TF_ACK_NOW;
+ tcp_output(pcb);
+ }
+
+ if (pcb->state != LISTEN) {
+ LWIP_ASSERT("unsent segments leaking", pcb->unsent == NULL);
+ LWIP_ASSERT("unacked segments leaking", pcb->unacked == NULL);
+#if TCP_QUEUE_OOSEQ
+ LWIP_ASSERT("ooseq segments leaking", pcb->ooseq == NULL);
+#endif /* TCP_QUEUE_OOSEQ */
+ }
+
+ pcb->state = CLOSED;
+
+ LWIP_ASSERT("tcp_pcb_remove: tcp_pcbs_sane()", tcp_pcbs_sane());
+}
+
+/**
+ * Calculates a new initial sequence number for new connections.
+ *
+ * @return u32_t pseudo random sequence number
+ */
+u32_t
+tcp_next_iss(void)
+{
+ static u32_t iss = 6510;
+
+ iss += tcp_ticks; /* XXX */
+ return iss;
+}
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+/**
+ * Calcluates the effective send mss that can be used for a specific IP address
+ * by using ip_route to determin the netif used to send to the address and
+ * calculating the minimum of TCP_MSS and that netif's mtu (if set).
+ */
+u16_t
+tcp_eff_send_mss_impl(u16_t sendmss, ipX_addr_t *dest
+#if LWIP_IPV6
+ , ipX_addr_t *src, u8_t isipv6
+#endif /* LWIP_IPV6 */
+ )
+{
+ u16_t mss_s;
+ struct netif *outif;
+ s16_t mtu;
+
+ outif = ipX_route(isipv6, src, dest);
+#if LWIP_IPV6
+ if (isipv6) {
+ /* First look in destination cache, to see if there is a Path MTU. */
+ mtu = nd6_get_destination_mtu(ipX_2_ip6(dest), outif);
+ } else
+#endif /* LWIP_IPV6 */
+ {
+ if (outif == NULL) {
+ return sendmss;
+ }
+ mtu = outif->mtu;
+ }
+
+ if (mtu != 0) {
+ mss_s = mtu - IP_HLEN - TCP_HLEN;
+#if LWIP_IPV6
+ /* for IPv6, substract the difference in header size */
+ if (isipv6) {
+ mss_s -= (IP6_HLEN - IP_HLEN);
+ }
+#endif /* LWIP_IPV6 */
+ /* RFC 1122, chap 4.2.2.6:
+ * Eff.snd.MSS = min(SendMSS+20, MMS_S) - TCPhdrsize - IPoptionsize
+ * We correct for TCP options in tcp_write(), and don't support IP options.
+ */
+ sendmss = LWIP_MIN(sendmss, mss_s);
+ }
+ return sendmss;
+}
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+const char*
+tcp_debug_state_str(enum tcp_state s)
+{
+ return tcp_state_str[s];
+}
+
+#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
+/**
+ * Print a tcp header for debugging purposes.
+ *
+ * @param tcphdr pointer to a struct tcp_hdr
+ */
+void
+tcp_debug_print(struct tcp_hdr *tcphdr)
+{
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP header:\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n",
+ ntohs(tcphdr->src), ntohs(tcphdr->dest)));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (seq no)\n",
+ ntohl(tcphdr->seqno)));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| %010"U32_F" | (ack no)\n",
+ ntohl(tcphdr->ackno)));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| %2"U16_F" | |%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"%"U16_F"| %5"U16_F" | (hdrlen, flags (",
+ TCPH_HDRLEN(tcphdr),
+ TCPH_FLAGS(tcphdr) >> 5 & 1,
+ TCPH_FLAGS(tcphdr) >> 4 & 1,
+ TCPH_FLAGS(tcphdr) >> 3 & 1,
+ TCPH_FLAGS(tcphdr) >> 2 & 1,
+ TCPH_FLAGS(tcphdr) >> 1 & 1,
+ TCPH_FLAGS(tcphdr) & 1,
+ ntohs(tcphdr->wnd)));
+ tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
+ LWIP_DEBUGF(TCP_DEBUG, ("), win)\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(TCP_DEBUG, ("| 0x%04"X16_F" | %5"U16_F" | (chksum, urgp)\n",
+ ntohs(tcphdr->chksum), ntohs(tcphdr->urgp)));
+ LWIP_DEBUGF(TCP_DEBUG, ("+-------------------------------+\n"));
+}
+
+/**
+ * Print a tcp state for debugging purposes.
+ *
+ * @param s enum tcp_state to print
+ */
+void
+tcp_debug_print_state(enum tcp_state s)
+{
+ LWIP_UNUSED_ARG(s);
+ LWIP_DEBUGF(TCP_DEBUG, ("State: %s\n", tcp_state_str[s]));
+}
+
+/**
+ * Print tcp flags for debugging purposes.
+ *
+ * @param flags tcp flags, all active flags are printed
+ */
+void
+tcp_debug_print_flags(u8_t flags)
+{
+ if (flags & TCP_FIN) {
+ LWIP_DEBUGF(TCP_DEBUG, ("FIN "));
+ }
+ if (flags & TCP_SYN) {
+ LWIP_DEBUGF(TCP_DEBUG, ("SYN "));
+ }
+ if (flags & TCP_RST) {
+ LWIP_DEBUGF(TCP_DEBUG, ("RST "));
+ }
+ if (flags & TCP_PSH) {
+ LWIP_DEBUGF(TCP_DEBUG, ("PSH "));
+ }
+ if (flags & TCP_ACK) {
+ LWIP_DEBUGF(TCP_DEBUG, ("ACK "));
+ }
+ if (flags & TCP_URG) {
+ LWIP_DEBUGF(TCP_DEBUG, ("URG "));
+ }
+ if (flags & TCP_ECE) {
+ LWIP_DEBUGF(TCP_DEBUG, ("ECE "));
+ }
+ if (flags & TCP_CWR) {
+ LWIP_DEBUGF(TCP_DEBUG, ("CWR "));
+ }
+}
+
+/**
+ * Print all tcp_pcbs in every list for debugging purposes.
+ */
+void
+tcp_debug_print_pcbs(void)
+{
+ struct tcp_pcb *pcb;
+ LWIP_DEBUGF(TCP_DEBUG, ("Active PCB states:\n"));
+ for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+ pcb->local_port, pcb->remote_port,
+ pcb->snd_nxt, pcb->rcv_nxt));
+ tcp_debug_print_state(pcb->state);
+ }
+ LWIP_DEBUGF(TCP_DEBUG, ("Listen PCB states:\n"));
+ for(pcb = (struct tcp_pcb *)tcp_listen_pcbs.pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+ pcb->local_port, pcb->remote_port,
+ pcb->snd_nxt, pcb->rcv_nxt));
+ tcp_debug_print_state(pcb->state);
+ }
+ LWIP_DEBUGF(TCP_DEBUG, ("TIME-WAIT PCB states:\n"));
+ for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_DEBUGF(TCP_DEBUG, ("Local port %"U16_F", foreign port %"U16_F" snd_nxt %"U32_F" rcv_nxt %"U32_F" ",
+ pcb->local_port, pcb->remote_port,
+ pcb->snd_nxt, pcb->rcv_nxt));
+ tcp_debug_print_state(pcb->state);
+ }
+}
+
+/**
+ * Check state consistency of the tcp_pcb lists.
+ */
+s16_t
+tcp_pcbs_sane(void)
+{
+ struct tcp_pcb *pcb;
+ for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != CLOSED", pcb->state != CLOSED);
+ LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != LISTEN", pcb->state != LISTEN);
+ LWIP_ASSERT("tcp_pcbs_sane: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
+ }
+ for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_ASSERT("tcp_pcbs_sane: tw pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+ }
+ return 1;
+}
+#endif /* TCP_DEBUG */
+
+#endif /* LWIP_TCP */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/tcp_in.c b/src/VBox/Devices/Network/lwip-new/src/core/tcp_in.c
new file mode 100644
index 00000000..d0889e8d
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/tcp_in.c
@@ -0,0 +1,1938 @@
+/**
+ * @file
+ * Transmission Control Protocol, incoming traffic
+ *
+ * The input processing functions of the TCP layer.
+ *
+ * These functions are generally called in the order (ip_input() ->)
+ * tcp_input() -> * tcp_process() -> tcp_receive() (-> application).
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/tcp_impl.h"
+#include "lwip/def.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "arch/perf.h"
+#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 */
+
+#if LWIP_CONNECTION_PROXY
+tcp_accept_syn_fn tcp_proxy_accept_callback = tcp_accept_syn_null;
+#endif
+
+/* These variables are global to all functions involved in the input
+ processing of TCP segments. They are set by the tcp_input()
+ function. */
+#if LWIP_CONNECTION_PROXY
+static int tcp_proxy;
+#endif
+static struct tcp_seg inseg;
+static struct tcp_hdr *tcphdr;
+static u32_t seqno, ackno;
+static u8_t flags;
+static u16_t tcplen;
+
+static u8_t recv_flags;
+static struct pbuf *recv_data;
+
+struct tcp_pcb *tcp_input_pcb;
+
+/* Forward declarations. */
+static void tcp_input1(struct pbuf *p, struct netif *inp);
+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, struct pbuf *syn);
+static err_t tcp_timewait_input(struct tcp_pcb *pcb);
+
+#if LWIP_CONNECTION_PROXY
+static err_t tcp_proxy_listen_input(struct pbuf *syn);
+static void tcp_restore_pbuf(struct pbuf *p);
+
+void
+tcp_proxy_input(struct pbuf *p, struct netif *inp)
+{
+ tcp_proxy = 1;
+ tcp_input1(p, inp);
+}
+#endif /* LWIP_CONNECTION_PROXY */
+
+void
+tcp_input(struct pbuf *p, struct netif *inp)
+{
+#if LWIP_CONNECTION_PROXY
+ tcp_proxy = 0;
+#endif
+ tcp_input1(p, inp);
+}
+
+/**
+ * The initial input processing of TCP. It verifies the TCP header, demultiplexes
+ * the segment between the PCBs and passes it on to tcp_process(), which implements
+ * the TCP finite state machine. This function is called by the IP layer (in
+ * ip_input()).
+ *
+ * @param p received TCP segment to process (p->payload pointing to the TCP header)
+ * @param inp network interface on which this segment was received
+ */
+static void
+tcp_input1(struct pbuf *p, struct netif *inp)
+{
+ struct tcp_pcb *pcb, *prev;
+ struct tcp_pcb_listen *lpcb;
+#if SO_REUSE
+ struct tcp_pcb *lpcb_prev = NULL;
+ struct tcp_pcb_listen *lpcb_any = NULL;
+#endif /* SO_REUSE */
+ u8_t hdrlen;
+ err_t err;
+#if CHECKSUM_CHECK_TCP
+ u16_t chksum;
+#endif /* CHECKSUM_CHECK_TCP */
+
+ PERF_START;
+
+ TCP_STATS_INC(tcp.recv);
+ snmp_inc_tcpinsegs();
+
+ tcphdr = (struct tcp_hdr *)p->payload;
+
+#if TCP_INPUT_DEBUG
+ tcp_debug_print(tcphdr);
+#endif
+
+ /* Check that TCP header fits in payload */
+ if (p->len < sizeof(struct tcp_hdr)) {
+ /* drop short packets */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet (%"U16_F" bytes) discarded\n", p->tot_len));
+ TCP_STATS_INC(tcp.lenerr);
+ goto dropped;
+ }
+
+ /* Don't even process incoming broadcasts/multicasts. */
+ if ((!ip_current_is_v6() && ip_addr_isbroadcast(ip_current_dest_addr(), inp)) ||
+ ipX_addr_ismulticast(ip_current_is_v6(), ipX_current_dest_addr())) {
+ TCP_STATS_INC(tcp.proterr);
+ goto dropped;
+ }
+
+#if CHECKSUM_CHECK_TCP
+ /* Verify TCP checksum. */
+ chksum = ipX_chksum_pseudo(ip_current_is_v6(), p, IP_PROTO_TCP, p->tot_len,
+ ipX_current_src_addr(), ipX_current_dest_addr());
+ if (chksum != 0) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packet discarded due to failing checksum 0x%04"X16_F"\n",
+ chksum));
+ tcp_debug_print(tcphdr);
+ TCP_STATS_INC(tcp.chkerr);
+ goto dropped;
+ }
+#endif /* CHECKSUM_CHECK_TCP */
+
+ /* Move the payload pointer in the pbuf so that it points to the
+ TCP data instead of the TCP header. */
+ hdrlen = TCPH_HDRLEN(tcphdr);
+ if(pbuf_header(p, -(hdrlen * 4))){
+ /* drop short packets */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: short packet\n"));
+ TCP_STATS_INC(tcp.lenerr);
+ goto dropped;
+ }
+
+ /* Convert fields in TCP header to host byte order. */
+ tcphdr->src = ntohs(tcphdr->src);
+ tcphdr->dest = ntohs(tcphdr->dest);
+ seqno = tcphdr->seqno = ntohl(tcphdr->seqno);
+ ackno = tcphdr->ackno = ntohl(tcphdr->ackno);
+ tcphdr->wnd = ntohs(tcphdr->wnd);
+
+ flags = TCPH_FLAGS(tcphdr);
+ tcplen = p->tot_len + ((flags & (TCP_FIN | TCP_SYN)) ? 1 : 0);
+
+ /* Demultiplex an incoming segment. First, we check if it is destined
+ for an active connection. */
+ prev = NULL;
+
+
+ for(pcb = tcp_active_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_ASSERT("tcp_input: active pcb->state != CLOSED", pcb->state != CLOSED);
+ LWIP_ASSERT("tcp_input: active pcb->state != TIME-WAIT", pcb->state != TIME_WAIT);
+ LWIP_ASSERT("tcp_input: active pcb->state != LISTEN", pcb->state != LISTEN);
+ if (pcb->remote_port == tcphdr->src &&
+ pcb->local_port == tcphdr->dest &&
+ IP_PCB_IPVER_INPUT_MATCH(pcb) &&
+ ipX_addr_cmp(ip_current_is_v6(), &pcb->remote_ip, ipX_current_src_addr()) &&
+ ipX_addr_cmp(ip_current_is_v6(),&pcb->local_ip, ipX_current_dest_addr())) {
+ /* Move this PCB to the front of the list so that subsequent
+ lookups will be faster (we exploit locality in TCP segment
+ arrivals). */
+ LWIP_ASSERT("tcp_input: pcb->next != pcb (before cache)", pcb->next != pcb);
+ if (prev != NULL) {
+ prev->next = pcb->next;
+ pcb->next = tcp_active_pcbs;
+ tcp_active_pcbs = pcb;
+ }
+ LWIP_ASSERT("tcp_input: pcb->next != pcb (after cache)", pcb->next != pcb);
+ break;
+ }
+ prev = pcb;
+ }
+
+ if (pcb == NULL) {
+ /* If it did not go to an active connection, we check the connections
+ in the TIME-WAIT state. */
+ for(pcb = tcp_tw_pcbs; pcb != NULL; pcb = pcb->next) {
+ LWIP_ASSERT("tcp_input: TIME-WAIT pcb->state == TIME-WAIT", pcb->state == TIME_WAIT);
+ if (pcb->remote_port == tcphdr->src &&
+ pcb->local_port == tcphdr->dest &&
+ IP_PCB_IPVER_INPUT_MATCH(pcb) &&
+ ipX_addr_cmp(ip_current_is_v6(), &pcb->remote_ip, ipX_current_src_addr()) &&
+ ipX_addr_cmp(ip_current_is_v6(),&pcb->local_ip, ipX_current_dest_addr())) {
+ /* We don't really care enough to move this PCB to the front
+ of the list since we are not very likely to receive that
+ many segments for connections in TIME-WAIT. */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for TIME_WAITing connection.\n"));
+ tcp_timewait_input(pcb);
+ pbuf_free(p);
+ return;
+ }
+ }
+
+ /* Finally, if we still did not get a match, we check all PCBs that
+ are LISTENing for incoming connections. */
+#if LWIP_CONNECTION_PROXY
+ if (tcp_proxy) {
+ /*
+ * For proxied packets we pretend that we have a LISTENing PCB.
+ * We also pass the pbuf with payload pointer moved back to the
+ * IP header so that proxy can save it and use it later to
+ * create ICMP datagram.
+ */
+ tcp_proxy_listen_input(p);
+ pbuf_free(p);
+ return;
+ } else {
+ /* XXX: no indentation for the following block to avoid diff */
+#endif /* LWIP_CONNECTION_PROXY */
+ prev = NULL;
+ for(lpcb = tcp_listen_pcbs.listen_pcbs; lpcb != NULL; lpcb = lpcb->next) {
+ if (lpcb->local_port == tcphdr->dest) {
+#if LWIP_IPV6
+ if (lpcb->accept_any_ip_version) {
+ /* found an ANY-match */
+#if SO_REUSE
+ lpcb_any = lpcb;
+ lpcb_prev = prev;
+#else /* SO_REUSE */
+ break;
+#endif /* SO_REUSE */
+ } else
+#endif /* LWIP_IPV6 */
+ if (IP_PCB_IPVER_INPUT_MATCH(lpcb)) {
+ if (ipX_addr_cmp(ip_current_is_v6(), &lpcb->local_ip, ipX_current_dest_addr())) {
+ /* found an exact match */
+ break;
+ } else if (ipX_addr_isany(ip_current_is_v6(), &lpcb->local_ip)) {
+ /* found an ANY-match */
+#if SO_REUSE
+ lpcb_any = lpcb;
+ lpcb_prev = prev;
+#else /* SO_REUSE */
+ break;
+ #endif /* SO_REUSE */
+ }
+ }
+ }
+ prev = (struct tcp_pcb *)lpcb;
+ }
+#if SO_REUSE
+ /* first try specific local IP */
+ if (lpcb == NULL) {
+ /* only pass to ANY if no specific local IP has been found */
+ lpcb = lpcb_any;
+ prev = lpcb_prev;
+ }
+#endif /* SO_REUSE */
+ if (lpcb != NULL) {
+ /* Move this PCB to the front of the list so that subsequent
+ lookups will be faster (we exploit locality in TCP segment
+ arrivals). */
+ if (prev != NULL) {
+ ((struct tcp_pcb_listen *)prev)->next = lpcb->next;
+ /* our successor is the remainder of the listening list */
+ lpcb->next = tcp_listen_pcbs.listen_pcbs;
+ /* put this listening pcb at the head of the listening list */
+ tcp_listen_pcbs.listen_pcbs = lpcb;
+ }
+
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: packed for LISTENing connection.\n"));
+ tcp_listen_input(lpcb, p);
+ pbuf_free(p);
+ return;
+ }
+#if LWIP_CONNECTION_PROXY
+ } /* !tcp_proxy - intentionally not indented */
+#endif /* LWIP_CONNECTION_PROXY */
+ }
+
+#if TCP_INPUT_DEBUG
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("+-+-+-+-+-+-+-+-+-+-+-+-+-+- tcp_input: flags "));
+ tcp_debug_print_flags(TCPH_FLAGS(tcphdr));
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("-+-+-+-+-+-+-+-+-+-+-+-+-+-+\n"));
+#endif /* TCP_INPUT_DEBUG */
+
+
+ if (pcb != NULL) {
+ /* The incoming segment belongs to a connection. */
+#if TCP_INPUT_DEBUG
+#if TCP_DEBUG
+ tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+#endif /* TCP_INPUT_DEBUG */
+
+ /* Set up a tcp_seg structure. */
+ inseg.next = NULL;
+ inseg.len = p->tot_len;
+ inseg.p = p;
+ inseg.tcphdr = tcphdr;
+
+ recv_data = NULL;
+ recv_flags = 0;
+
+ if (flags & TCP_PSH) {
+ p->flags |= PBUF_FLAG_PUSH;
+ }
+
+ /* If there is data which was previously "refused" by upper layer */
+ if (pcb->refused_data != NULL) {
+ if ((tcp_process_refused_data(pcb) == ERR_ABRT) ||
+ ((pcb->refused_data != NULL) && (tcplen > 0))) {
+ /* pcb has been aborted or refused data is still refused and the new
+ segment contains data */
+ TCP_STATS_INC(tcp.drop);
+ snmp_inc_tcpinerrs();
+ goto aborted;
+ }
+ }
+ tcp_input_pcb = pcb;
+ err = tcp_process(pcb);
+ /* A return value of ERR_ABRT means that tcp_abort() was called
+ and that the pcb has been freed. If so, we don't do anything. */
+ if (err != ERR_ABRT) {
+ if (recv_flags & TF_RESET) {
+ /* TF_RESET means that the connection was reset by the other
+ end. We then call the error callback to inform the
+ application that the connection is dead before we
+ deallocate the PCB. */
+ TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_RST);
+ tcp_pcb_remove(&tcp_active_pcbs, pcb);
+ memp_free(MEMP_TCP_PCB, pcb);
+ } else if (recv_flags & TF_CLOSED) {
+ /* The connection has been closed and we will deallocate the
+ PCB. */
+ if (!(pcb->flags & TF_RXCLOSED)) {
+ /* Connection closed although the application has only shut down the
+ tx side: call the PCB's err callback and indicate the closure to
+ ensure the application doesn't continue using the PCB. */
+ TCP_EVENT_ERR(pcb->errf, pcb->callback_arg, ERR_CLSD);
+ }
+ tcp_pcb_remove(&tcp_active_pcbs, pcb);
+ memp_free(MEMP_TCP_PCB, pcb);
+ } else {
+ err = ERR_OK;
+ /* If the application has registered a "sent" function to be
+ called when new send buffer space is available, we call it
+ now. */
+ if (pcb->acked > 0) {
+ TCP_EVENT_SENT(pcb, pcb->acked, err);
+ if (err == ERR_ABRT) {
+ goto aborted;
+ }
+ }
+
+ if (recv_data != NULL) {
+ LWIP_ASSERT("pcb->refused_data == NULL", pcb->refused_data == NULL);
+ if (pcb->flags & TF_RXCLOSED) {
+ /* received data although already closed -> abort (send RST) to
+ notify the remote host that not all data has been processed */
+ pbuf_free(recv_data);
+ tcp_abort(pcb);
+ goto aborted;
+ }
+
+ /* Notify application that data has been received. */
+ TCP_EVENT_RECV(pcb, recv_data, ERR_OK, err);
+ if (err == ERR_ABRT) {
+ goto aborted;
+ }
+
+ /* If the upper layer can't receive this data, store it */
+ if (err != ERR_OK) {
+ pcb->refused_data = recv_data;
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_input: keep incoming packet, because pcb is \"full\"\n"));
+ }
+ }
+
+ /* If a FIN segment was received, we call the callback
+ function with a NULL buffer to indicate EOF. */
+ if (recv_flags & TF_GOT_FIN) {
+ if (pcb->refused_data != NULL) {
+ /* Delay this if we have refused data. */
+ pcb->refused_data->flags |= PBUF_FLAG_TCP_FIN;
+ } else {
+ /* correct rcv_wnd as the application won't call tcp_recved()
+ for the FIN's seqno */
+ if (pcb->rcv_wnd != TCP_WND) {
+ pcb->rcv_wnd++;
+ }
+ TCP_EVENT_CLOSED(pcb, err);
+ if (err == ERR_ABRT) {
+ goto aborted;
+ }
+ }
+ }
+
+ tcp_input_pcb = NULL;
+ /* Try to send something out. */
+ tcp_output(pcb);
+#if TCP_INPUT_DEBUG
+#if TCP_DEBUG
+ tcp_debug_print_state(pcb->state);
+#endif /* TCP_DEBUG */
+#endif /* TCP_INPUT_DEBUG */
+ }
+ }
+ /* Jump target if pcb has been aborted in a callback (by calling tcp_abort()).
+ Below this line, 'pcb' may not be dereferenced! */
+aborted:
+ tcp_input_pcb = NULL;
+ recv_data = NULL;
+
+ /* give up our reference to inseg.p */
+ if (inseg.p != NULL)
+ {
+ pbuf_free(inseg.p);
+ inseg.p = NULL;
+ }
+ } else {
+
+ /* If no matching PCB was found, send a TCP RST (reset) to the
+ sender. */
+ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_input: no PCB match found, resetting.\n"));
+ if (!(TCPH_FLAGS(tcphdr) & TCP_RST)) {
+ TCP_STATS_INC(tcp.proterr);
+ TCP_STATS_INC(tcp.drop);
+ tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(),
+ ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6());
+ }
+ pbuf_free(p);
+ }
+
+ LWIP_ASSERT("tcp_input: tcp_pcbs_sane()", tcp_pcbs_sane());
+ PERF_STOP("tcp_input");
+ return;
+dropped:
+ TCP_STATS_INC(tcp.drop);
+ snmp_inc_tcpinerrs();
+ pbuf_free(p);
+}
+
+/**
+ * Called by tcp_input() when a segment arrives for a listening
+ * connection (from tcp_input()).
+ *
+ * @param pcb the tcp_pcb_listen for which a segment arrived
+ * @return ERR_OK if the segment was processed
+ * another err_t on error
+ *
+ * @note the return value is not (yet?) used in tcp_input()
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ * involved is passed as a parameter to this function
+ */
+static err_t
+tcp_listen_input(struct tcp_pcb_listen *pcb, struct pbuf *syn)
+{
+ struct tcp_pcb *npcb;
+ err_t rc;
+
+ if (flags & TCP_RST) {
+ /* An incoming RST should be ignored. Return. */
+ return ERR_OK;
+ }
+
+ /* In the LISTEN state, we check for incoming SYN segments,
+ creates a new PCB, and responds with a SYN|ACK. */
+ if (flags & TCP_ACK) {
+ /* For incoming segments with the ACK flag set, respond with a
+ RST. */
+ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_listen_input: ACK in LISTEN, sending reset\n"));
+ tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(),
+ ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6());
+ } else if (flags & TCP_SYN) {
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest));
+#if TCP_LISTEN_BACKLOG
+ if (pcb->accepts_pending >= pcb->backlog) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest));
+ return ERR_ABRT;
+ }
+#endif /* TCP_LISTEN_BACKLOG */
+ npcb = tcp_alloc(pcb->prio);
+ /* If a new PCB could not be created (probably due to lack of memory),
+ we don't do anything, but rely on the sender will retransmit the
+ SYN at a time when we have more memory available. */
+ if (npcb == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_listen_input: could not allocate PCB\n"));
+ TCP_STATS_INC(tcp.memerr);
+ return ERR_MEM;
+ }
+#if TCP_LISTEN_BACKLOG
+ pcb->accepts_pending++;
+#endif /* TCP_LISTEN_BACKLOG */
+ /* Set up the new PCB. */
+#if LWIP_IPV6
+ PCB_ISIPV6(npcb) = ip_current_is_v6();
+#endif /* LWIP_IPV6 */
+ ipX_addr_copy(ip_current_is_v6(), npcb->local_ip, *ipX_current_dest_addr());
+ ipX_addr_copy(ip_current_is_v6(), npcb->remote_ip, *ipX_current_src_addr());
+ npcb->local_port = pcb->local_port;
+ npcb->remote_port = tcphdr->src;
+ npcb->state = SYN_RCVD;
+ npcb->rcv_nxt = seqno + 1;
+ npcb->rcv_ann_right_edge = npcb->rcv_nxt;
+ npcb->snd_wnd = tcphdr->wnd;
+ npcb->snd_wnd_max = tcphdr->wnd;
+ npcb->ssthresh = npcb->snd_wnd;
+ npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
+ npcb->callback_arg = pcb->callback_arg;
+#if LWIP_CALLBACK_API
+ npcb->accept = pcb->accept;
+#endif /* LWIP_CALLBACK_API */
+ /* inherit socket options */
+ npcb->so_options = pcb->so_options & SOF_INHERITED;
+ /* Register the new PCB so that we can begin receiving segments
+ for it. */
+ TCP_REG_ACTIVE(npcb);
+
+ /* Parse any options in the SYN. */
+ tcp_parseopt(npcb);
+#if TCP_CALCULATE_EFF_SEND_MSS
+ npcb->mss = tcp_eff_send_mss(npcb->mss, &npcb->local_ip,
+ &npcb->remote_ip, PCB_ISIPV6(npcb));
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+
+#if LWIP_CONNECTION_PROXY
+ /* Early accept on SYN, like we do in tcp_proxy_listen_input() */
+ if (pcb->accept_on_syn) {
+ tcp_accept_syn_fn accept_syn;
+ err_t err;
+
+ /* back off to "delayed" SYN_RCVD, see comments in proxy */
+ npcb->state = SYN_RCVD_0;
+
+ /*
+ * Call the accept syn function. Note, that it comes from the
+ * listening pcb and we reset the normal accept callback of the
+ * new pcb. The latter should be set by the client along with
+ * other callbacks if necessary.
+ */
+ accept_syn = (tcp_accept_syn_fn)(uintptr_t)npcb->accept;
+ npcb->accept = tcp_accept_null;
+
+ /* TCP_EVENT_ACCEPT_SYN */
+ if (accept_syn != NULL)
+ err = (*accept_syn)(npcb->callback_arg, npcb, syn);
+ else
+ err = ERR_ARG;
+
+ if (err != ERR_OK) {
+ /* If the accept function returns with an error, we abort
+ * the connection. */
+ /* Already aborted? */
+ if (err != ERR_ABRT) {
+ tcp_abort(npcb);
+ }
+ return ERR_ABRT;
+ }
+ /* Don't send SYN|ACK now, client will call
+ * tcp_proxy_accept_confirm(). */
+ return ERR_OK;
+ }
+#else
+ LWIP_UNUSED_ARG(syn);
+#endif
+
+ snmp_inc_tcppassiveopens();
+
+ /* Send a SYN|ACK together with the MSS option. */
+ rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK);
+ if (rc != ERR_OK) {
+ tcp_abandon(npcb, 0);
+ return rc;
+ }
+ return tcp_output(npcb);
+ }
+ return ERR_OK;
+}
+
+#if LWIP_CONNECTION_PROXY
+/*
+ * Proxy accept callback will be passed the pbuf with the SYN segment
+ * so that it can use it for ICMP errors if necessary. Undo changes
+ * we've done to the pbuf so that it's suitable to be passed to
+ * icmp_send_response().
+ */
+static void
+tcp_restore_pbuf(struct pbuf *p)
+{
+ u8_t hdrlen = TCPH_HDRLEN(tcphdr);
+
+ /* Reveal IP and TCP headers. */
+ pbuf_header(p, ip_current_header_tot_len() + hdrlen * 4);
+
+ /* Convert fields in the TCP header back to network byte order. */
+ tcphdr->src = htons(tcphdr->src);
+ tcphdr->dest = htons(tcphdr->dest);
+ tcphdr->seqno = htonl(tcphdr->seqno);
+ tcphdr->ackno = htonl(tcphdr->ackno);
+ tcphdr->wnd = htons(tcphdr->wnd);
+}
+
+
+/**
+ * We run proxied packets through tcp_input() since it mostly does
+ * what we need, the only exception is creation of new connections.
+ * For local connections we have LISTENing PCBs that we can check to
+ * determine if a new connection is to be created. For proxied
+ * packets we pretend that, in some sense, we have LISTENing PCB for
+ * all ports of all IP addresses.
+ */
+static err_t
+tcp_proxy_listen_input(struct pbuf *syn)
+{
+ struct tcp_pcb *npcb;
+ err_t err;
+
+ if (flags & TCP_RST) {
+ /* An incoming RST should be ignored. Return. */
+ return ERR_OK;
+ }
+
+ /* In the LISTEN state, we check for incoming SYN segments,
+ creates a new PCB, and responds with a SYN|ACK. */
+ if (flags & TCP_ACK) {
+ /* For incoming segments with the ACK flag set, respond with a
+ RST. */
+ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_proxy_listen_input: ACK in LISTEN, sending reset\n"));
+ tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(),
+ ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6());
+ } else if (flags & TCP_SYN) {
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection request %"U16_F" -> %"U16_F".\n", tcphdr->src, tcphdr->dest));
+#if TCP_LISTEN_BACKLOG /* XXX */ && 0
+ if (pcb->accepts_pending >= pcb->backlog) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_proxy_listen_input: listen backlog exceeded for port %"U16_F"\n", tcphdr->dest));
+ return ERR_ABRT;
+ }
+#endif /* TCP_LISTEN_BACKLOG */
+ npcb = tcp_alloc(/* pcb->prio */ TCP_PRIO_MIN);
+ /* If a new PCB could not be created (probably due to lack of memory),
+ we don't do anything, but rely on the sender will retransmit the
+ SYN at a time when we have more memory available. */
+ if (npcb == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_proxy_listen_input: could not allocate PCB\n"));
+ TCP_STATS_INC(tcp.memerr);
+ return ERR_MEM;
+ }
+#if TCP_LISTEN_BACKLOG /* XXX */ && 0
+ pcb->accepts_pending++;
+#endif /* TCP_LISTEN_BACKLOG */
+ /* Set up the new PCB. */
+#if LWIP_IPV6
+ PCB_ISIPV6(npcb) = ip_current_is_v6();
+#endif /* LWIP_IPV6 */
+ ipX_addr_copy(ip_current_is_v6(), npcb->local_ip, *ipX_current_dest_addr());
+ ipX_addr_copy(ip_current_is_v6(), npcb->remote_ip, *ipX_current_src_addr());
+ npcb->local_port = /* pcb->local_port */ tcphdr->dest;
+ npcb->remote_port = tcphdr->src;
+ npcb->state = SYN_RCVD_0; /* "delayed" SYN_RCVD, see below */
+ npcb->rcv_nxt = seqno + 1;
+ npcb->rcv_ann_right_edge = npcb->rcv_nxt;
+ npcb->snd_wnd = tcphdr->wnd;
+ npcb->snd_wnd_max = tcphdr->wnd;
+ npcb->ssthresh = npcb->snd_wnd;
+ npcb->snd_wl1 = seqno - 1;/* initialise to seqno-1 to force window update */
+ npcb->callback_arg = /* pcb->callback_arg */ NULL;
+#if LWIP_CALLBACK_API
+ npcb->accept = /* pcb->accept */ tcp_accept_null;
+#endif /* LWIP_CALLBACK_API */
+ /* inherit socket options */
+ npcb->so_options = /* pcb->so_options & SOF_INHERITED */ 0;
+ /* Register the new PCB so that we can begin receiving segments
+ for it. */
+ TCP_REG_ACTIVE(npcb);
+
+ /* Parse any options in the SYN. */
+ tcp_parseopt(npcb);
+#if TCP_CALCULATE_EFF_SEND_MSS
+ npcb->mss = tcp_eff_send_mss(npcb->mss, &npcb->local_ip,
+ &npcb->remote_ip, PCB_ISIPV6(npcb));
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+ /*
+ * For normal tcp_listen_input() we send SYN|ACK here (see above).
+ * But for proxy we delay it and move instead into an intermediate
+ * non-standard SYN_RCVD_0 state. It's a sort of superimposition
+ * of accepted (SYN_RCVD) and rejected connection.
+ *
+ * We let the proxy connect to the real destination and come back
+ * to us when it's done - only then we decide whether this accept
+ * was successful and send either SYN|ACK, or decide it was
+ * unsuccessful and RST or issue ICMP unreachable.
+ *
+ * See tcp_proxy_accept_confirm() below for successful accept.
+ *
+ * For the proxy to be able to create ICMP unreachable datagram we
+ * need to keep the beginning of the pbuf around. We pass is as
+ * callback arg here and let callback decide.
+ */
+
+ tcp_restore_pbuf(syn);
+
+ /* TCP_EVENT_ACCEPT_SYN */
+ if (tcp_proxy_accept_callback != NULL)
+ err = (*tcp_proxy_accept_callback)(NULL, npcb, syn);
+ else
+ err = ERR_ARG;
+
+ if (err != ERR_OK) {
+ /* If the accept function returns with an error, we abort
+ * the connection. */
+ /* Already aborted? */
+ if (err != ERR_ABRT) {
+ tcp_abort(npcb);
+ }
+ return ERR_ABRT;
+ }
+ }
+ return ERR_OK;
+}
+
+
+/**
+ * Move proxied connection to SYN_RCVD state.
+ *
+ * This corresponds to the final part of tcp_listen_input() omitted
+ * from tcp_proxy_listen_input().
+ *
+ * There is one important difference though. For normal case we can
+ * abandon new pcb if we failed to enqueue SYN|ACK because pcb has not
+ * been passed to user code yet.
+ *
+ * Here the user already holds onto the pcb, so if we abandon pcb we
+ * must let user know, otherwise it's left with a dangling reference.
+ */
+err_t
+tcp_proxy_accept_confirm(struct tcp_pcb *npcb)
+{
+ err_t rc;
+
+ if (npcb->state != SYN_RCVD_0) {
+ return ERR_ISCONN;
+ }
+ npcb->state = SYN_RCVD;
+
+ snmp_inc_tcppassiveopens();
+
+ /* Send a SYN|ACK together with the MSS option. */
+ rc = tcp_enqueue_flags(npcb, TCP_SYN | TCP_ACK);
+ if (rc != ERR_OK) {
+ tcp_abandon(npcb, 0);
+ return ERR_ABRT;
+ }
+ return tcp_output(npcb);
+}
+#endif /* LWIP_CONNECTION_PROXY */
+
+/**
+ * Called by tcp_input() when a segment arrives for a connection in
+ * TIME_WAIT.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ * involved is passed as a parameter to this function
+ */
+static err_t
+tcp_timewait_input(struct tcp_pcb *pcb)
+{
+ /* RFC 1337: in TIME_WAIT, ignore RST and ACK FINs + any 'acceptable' segments */
+ /* RFC 793 3.9 Event Processing - Segment Arrives:
+ * - first check sequence number - we skip that one in TIME_WAIT (always
+ * acceptable since we only send ACKs)
+ * - second check the RST bit (... return) */
+ if (flags & TCP_RST) {
+ return ERR_OK;
+ }
+ /* - fourth, check the SYN bit, */
+ if (flags & TCP_SYN) {
+ /* If an incoming segment is not acceptable, an acknowledgment
+ should be sent in reply */
+ if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt+pcb->rcv_wnd)) {
+ /* If the SYN is in the window it is an error, send a reset */
+ tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(),
+ ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6());
+ return ERR_OK;
+ }
+ } else if (flags & TCP_FIN) {
+ /* - eighth, check the FIN bit: Remain in the TIME-WAIT state.
+ Restart the 2 MSL time-wait timeout.*/
+ pcb->tmr = tcp_ticks;
+ }
+
+ if ((tcplen > 0)) {
+ /* Acknowledge data, FIN or out-of-window SYN */
+ pcb->flags |= TF_ACK_NOW;
+ return tcp_output(pcb);
+ }
+ return ERR_OK;
+}
+
+/**
+ * Implements the TCP state machine. Called by tcp_input. In some
+ * states tcp_receive() is called to receive data. The tcp_seg
+ * argument will be freed by the caller (tcp_input()) unless the
+ * recv_data pointer in the pcb is set.
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ *
+ * @note the segment which arrived is saved in global variables, therefore only the pcb
+ * involved is passed as a parameter to this function
+ */
+static err_t
+tcp_process(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *rseg;
+ u8_t acceptable = 0;
+ err_t err;
+
+ err = ERR_OK;
+
+ /* Process incoming RST segments. */
+ if (flags & TCP_RST) {
+ /* First, determine if the reset is acceptable. */
+ if (pcb->state == SYN_SENT) {
+ if (ackno == pcb->snd_nxt) {
+ acceptable = 1;
+ }
+ } else {
+ if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
+ pcb->rcv_nxt+pcb->rcv_wnd)) {
+ acceptable = 1;
+ }
+ }
+
+ if (acceptable) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: Connection RESET\n"));
+ LWIP_ASSERT("tcp_input: pcb->state != CLOSED", pcb->state != CLOSED);
+ recv_flags |= TF_RESET;
+ pcb->flags &= ~TF_ACK_DELAY;
+ return ERR_RST;
+ } else {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
+ seqno, pcb->rcv_nxt));
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_process: unacceptable reset seqno %"U32_F" rcv_nxt %"U32_F"\n",
+ seqno, pcb->rcv_nxt));
+ return ERR_OK;
+ }
+ }
+
+ if ((flags & TCP_SYN)
+ && ( pcb->state != SYN_SENT
+#if LWIP_CONNECTION_PROXY
+ && pcb->state != SYN_RCVD_0 /* treat like SYN_RCVD */
+#endif
+ && pcb->state != SYN_RCVD))
+ {
+ /* Cope with new connection attempt after remote end crashed */
+ tcp_ack_now(pcb);
+ return ERR_OK;
+ }
+
+ if ((pcb->flags & TF_RXCLOSED) == 0) {
+ /* Update the PCB (in)activity timer unless rx is closed (see tcp_shutdown) */
+ pcb->tmr = tcp_ticks;
+ }
+ pcb->keep_cnt_sent = 0;
+
+ tcp_parseopt(pcb);
+
+ /* Do different things depending on the TCP state. */
+ switch (pcb->state) {
+ case SYN_SENT:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("SYN-SENT: ackno %"U32_F" pcb->snd_nxt %"U32_F" unacked %"U32_F"\n", ackno,
+ pcb->snd_nxt, ntohl(pcb->unacked->tcphdr->seqno)));
+ /* received SYN ACK with expected sequence number? */
+ if ((flags & TCP_ACK) && (flags & TCP_SYN)
+ && ackno == ntohl(pcb->unacked->tcphdr->seqno) + 1) {
+ pcb->snd_buf++;
+ pcb->rcv_nxt = seqno + 1;
+ pcb->rcv_ann_right_edge = pcb->rcv_nxt;
+ pcb->lastack = ackno;
+ pcb->snd_wnd = tcphdr->wnd;
+ pcb->snd_wnd_max = tcphdr->wnd;
+ pcb->snd_wl1 = seqno - 1; /* initialise to seqno - 1 to force window update */
+ pcb->state = ESTABLISHED;
+
+#if TCP_CALCULATE_EFF_SEND_MSS
+ pcb->mss = tcp_eff_send_mss(pcb->mss, &pcb->local_ip, &pcb->remote_ip,
+ PCB_ISIPV6(pcb));
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+ /* Set ssthresh again after changing pcb->mss (already set in tcp_connect
+ * but for the default value of pcb->mss) */
+ pcb->ssthresh = pcb->mss * 10;
+
+ pcb->cwnd = ((pcb->cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
+ LWIP_ASSERT("pcb->snd_queuelen > 0", (pcb->snd_queuelen > 0));
+ --pcb->snd_queuelen;
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_process: SYN-SENT --queuelen %"U16_F"\n", (u16_t)pcb->snd_queuelen));
+ rseg = pcb->unacked;
+ pcb->unacked = rseg->next;
+ tcp_seg_free(rseg);
+
+ /* If there's nothing left to acknowledge, stop the retransmit
+ timer, otherwise reset it to start again */
+ if(pcb->unacked == NULL)
+ pcb->rtime = -1;
+ else {
+ pcb->rtime = 0;
+ pcb->nrtx = 0;
+ }
+
+ /* Call the user specified function to call when sucessfully
+ * connected. */
+ TCP_EVENT_CONNECTED(pcb, ERR_OK, err);
+ if (err == ERR_ABRT) {
+ return ERR_ABRT;
+ }
+ tcp_ack_now(pcb);
+ }
+ /* received ACK? possibly a half-open connection */
+ else if (flags & TCP_ACK) {
+ /* send a RST to bring the other side in a non-synchronized state. */
+ tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(),
+ ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6());
+ }
+ break;
+#if LWIP_CONNECTION_PROXY
+ case SYN_RCVD_0:
+ /*
+ * Pretend that any datagrams for this "tentatively accepted"
+ * proxy connection was lost in transit and we've never seen them.
+ * The only legal input in this state is a resent SYN, but we
+ * can't respond to it with SYN|ACK yet anyway. We may generate
+ * RST for anything else, but we might as well play dead and let
+ * the sender timeout eventually.
+ */
+ break;
+#endif
+ case SYN_RCVD:
+ if (flags & TCP_ACK) {
+ /* expected ACK number? */
+ if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)) {
+ u16_t old_cwnd;
+ pcb->state = ESTABLISHED;
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection established %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+#if LWIP_CALLBACK_API
+ LWIP_ASSERT("pcb->accept != NULL", pcb->accept != NULL);
+#endif
+ /* Call the accept function. */
+ TCP_EVENT_ACCEPT(pcb, ERR_OK, err);
+ if (err != ERR_OK) {
+ /* If the accept function returns with an error, we abort
+ * the connection. */
+ /* Already aborted? */
+ if (err != ERR_ABRT) {
+ tcp_abort(pcb);
+ }
+ return ERR_ABRT;
+ }
+ old_cwnd = pcb->cwnd;
+ /* If there was any data contained within this ACK,
+ * we'd better pass it on to the application as well. */
+ tcp_receive(pcb);
+
+ /* Prevent ACK for SYN to generate a sent event */
+ if (pcb->acked != 0) {
+ pcb->acked--;
+ }
+
+ pcb->cwnd = ((old_cwnd == 1) ? (pcb->mss * 2) : pcb->mss);
+
+ if (recv_flags & TF_GOT_FIN) {
+ tcp_ack_now(pcb);
+ pcb->state = CLOSE_WAIT;
+ }
+ } else {
+ /* incorrect ACK number, send RST */
+ tcp_rst(ackno, seqno + tcplen, ipX_current_dest_addr(),
+ ipX_current_src_addr(), tcphdr->dest, tcphdr->src, ip_current_is_v6());
+ }
+ } else if ((flags & TCP_SYN) && (seqno == pcb->rcv_nxt - 1)) {
+ /* Looks like another copy of the SYN - retransmit our SYN-ACK */
+ tcp_rexmit(pcb);
+ }
+ break;
+ case CLOSE_WAIT:
+ /* FALLTHROUGH */
+ case ESTABLISHED:
+ tcp_receive(pcb);
+ if (recv_flags & TF_GOT_FIN) { /* passive close */
+ tcp_ack_now(pcb);
+ pcb->state = CLOSE_WAIT;
+ }
+ break;
+ case FIN_WAIT_1:
+ tcp_receive(pcb);
+ if (recv_flags & TF_GOT_FIN) {
+ if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt) &&
+ pcb->unsent == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG,
+ ("TCP connection closed: FIN_WAIT_1 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ tcp_ack_now(pcb);
+ tcp_pcb_purge(pcb);
+ TCP_RMV_ACTIVE(pcb);
+ pcb->state = TIME_WAIT;
+ TCP_REG(&tcp_tw_pcbs, pcb);
+ } else {
+ tcp_ack_now(pcb);
+ pcb->state = CLOSING;
+ }
+ } else if ((flags & TCP_ACK) && (ackno == pcb->snd_nxt) &&
+ pcb->unsent == NULL) {
+ pcb->state = FIN_WAIT_2;
+ }
+ break;
+ case FIN_WAIT_2:
+ tcp_receive(pcb);
+ if (recv_flags & TF_GOT_FIN) {
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: FIN_WAIT_2 %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ tcp_ack_now(pcb);
+ tcp_pcb_purge(pcb);
+ TCP_RMV_ACTIVE(pcb);
+ pcb->state = TIME_WAIT;
+ TCP_REG(&tcp_tw_pcbs, pcb);
+ }
+ break;
+ case CLOSING:
+ tcp_receive(pcb);
+ if (flags & TCP_ACK && ackno == pcb->snd_nxt && pcb->unsent == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: CLOSING %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ tcp_pcb_purge(pcb);
+ TCP_RMV_ACTIVE(pcb);
+ pcb->state = TIME_WAIT;
+ TCP_REG(&tcp_tw_pcbs, pcb);
+ }
+ break;
+ case LAST_ACK:
+ tcp_receive(pcb);
+ if (flags & TCP_ACK && ackno == pcb->snd_nxt && pcb->unsent == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP connection closed: LAST_ACK %"U16_F" -> %"U16_F".\n", inseg.tcphdr->src, inseg.tcphdr->dest));
+ /* bugfix #21699: don't set pcb->state to CLOSED here or we risk leaking segments */
+ recv_flags |= TF_CLOSED;
+ }
+ break;
+ default:
+ break;
+ }
+ return ERR_OK;
+}
+
+#if TCP_QUEUE_OOSEQ
+/**
+ * Insert segment into the list (segments covered with new one will be deleted)
+ *
+ * Called from tcp_receive()
+ */
+static void
+tcp_oos_insert_segment(struct tcp_seg *cseg, struct tcp_seg *next)
+{
+ struct tcp_seg *old_seg;
+
+ if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
+ /* received segment overlaps all following segments */
+ tcp_segs_free(next);
+ next = NULL;
+ }
+ else {
+ /* delete some following segments
+ oos queue may have segments with FIN flag */
+ while (next &&
+ TCP_SEQ_GEQ((seqno + cseg->len),
+ (next->tcphdr->seqno + next->len))) {
+ /* cseg with FIN already processed */
+ if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
+ TCPH_SET_FLAG(cseg->tcphdr, TCP_FIN);
+ }
+ old_seg = next;
+ next = next->next;
+ tcp_seg_free(old_seg);
+ }
+ if (next &&
+ TCP_SEQ_GT(seqno + cseg->len, next->tcphdr->seqno)) {
+ /* We need to trim the incoming segment. */
+ cseg->len = (u16_t)(next->tcphdr->seqno - seqno);
+ pbuf_realloc(cseg->p, cseg->len);
+ }
+ }
+ cseg->next = next;
+}
+#endif /* TCP_QUEUE_OOSEQ */
+
+/**
+ * Called by tcp_process. Checks if the given segment is an ACK for outstanding
+ * data, and if so frees the memory of the buffered data. Next, is places the
+ * segment on any of the receive queues (pcb->recved or pcb->ooseq). If the segment
+ * is buffered, the pbuf is referenced by pbuf_ref so that it will not be freed until
+ * it has been removed from the buffer.
+ *
+ * If the incoming segment constitutes an ACK for a segment that was used for RTT
+ * estimation, the RTT is estimated here as well.
+ *
+ * Called from tcp_process().
+ */
+static void
+tcp_receive(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *next;
+#if TCP_QUEUE_OOSEQ
+ struct tcp_seg *prev, *cseg;
+#endif /* TCP_QUEUE_OOSEQ */
+ struct pbuf *p;
+ s32_t off;
+ s16_t m;
+ u32_t right_wnd_edge;
+ u16_t new_tot_len;
+ int found_dupack = 0;
+#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
+ u32_t ooseq_blen;
+ u16_t ooseq_qlen;
+#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */
+
+ LWIP_ASSERT("tcp_receive: wrong state", pcb->state >= ESTABLISHED);
+
+ if (flags & TCP_ACK) {
+ right_wnd_edge = pcb->snd_wnd + pcb->snd_wl2;
+
+ /* Update window. */
+ if (TCP_SEQ_LT(pcb->snd_wl1, seqno) ||
+ (pcb->snd_wl1 == seqno && TCP_SEQ_LT(pcb->snd_wl2, ackno)) ||
+ (pcb->snd_wl2 == ackno && tcphdr->wnd > pcb->snd_wnd)) {
+ pcb->snd_wnd = tcphdr->wnd;
+ /* keep track of the biggest window announced by the remote host to calculate
+ the maximum segment size */
+ if (pcb->snd_wnd_max < tcphdr->wnd) {
+ pcb->snd_wnd_max = tcphdr->wnd;
+ }
+ pcb->snd_wl1 = seqno;
+ pcb->snd_wl2 = ackno;
+ if (pcb->snd_wnd == 0) {
+ if (pcb->persist_backoff == 0) {
+ /* start persist timer */
+ pcb->persist_cnt = 0;
+ pcb->persist_backoff = 1;
+ }
+ } else if (pcb->persist_backoff > 0) {
+ /* stop persist timer */
+ pcb->persist_backoff = 0;
+ }
+ LWIP_DEBUGF(TCP_WND_DEBUG, ("tcp_receive: window update %"U16_F"\n", pcb->snd_wnd));
+#if TCP_WND_DEBUG
+ } else {
+ if (pcb->snd_wnd != tcphdr->wnd) {
+ LWIP_DEBUGF(TCP_WND_DEBUG,
+ ("tcp_receive: no window update lastack %"U32_F" ackno %"
+ U32_F" wl1 %"U32_F" seqno %"U32_F" wl2 %"U32_F"\n",
+ pcb->lastack, ackno, pcb->snd_wl1, seqno, pcb->snd_wl2));
+ }
+#endif /* TCP_WND_DEBUG */
+ }
+
+ /* (From Stevens TCP/IP Illustrated Vol II, p970.) Its only a
+ * duplicate ack if:
+ * 1) It doesn't ACK new data
+ * 2) length of received packet is zero (i.e. no payload)
+ * 3) the advertised window hasn't changed
+ * 4) There is outstanding unacknowledged data (retransmission timer running)
+ * 5) The ACK is == biggest ACK sequence number so far seen (snd_una)
+ *
+ * If it passes all five, should process as a dupack:
+ * a) dupacks < 3: do nothing
+ * b) dupacks == 3: fast retransmit
+ * c) dupacks > 3: increase cwnd
+ *
+ * If it only passes 1-3, should reset dupack counter (and add to
+ * stats, which we don't do in lwIP)
+ *
+ * If it only passes 1, should reset dupack counter
+ *
+ */
+
+ /* Clause 1 */
+ if (TCP_SEQ_LEQ(ackno, pcb->lastack)) {
+ pcb->acked = 0;
+ /* Clause 2 */
+ if (tcplen == 0) {
+ /* Clause 3 */
+ if (pcb->snd_wl2 + pcb->snd_wnd == right_wnd_edge){
+ /* Clause 4 */
+ if (pcb->rtime >= 0) {
+ /* Clause 5 */
+ if (pcb->lastack == ackno) {
+ found_dupack = 1;
+ if ((u8_t)(pcb->dupacks + 1) > pcb->dupacks) {
+ ++pcb->dupacks;
+ }
+ if (pcb->dupacks > 3) {
+ /* Inflate the congestion window, but not if it means that
+ the value overflows. */
+ if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
+ pcb->cwnd += pcb->mss;
+ }
+ } else if (pcb->dupacks == 3) {
+ /* Do fast retransmit */
+ tcp_rexmit_fast(pcb);
+ }
+ }
+ }
+ }
+ }
+ /* If Clause (1) or more is true, but not a duplicate ack, reset
+ * count of consecutive duplicate acks */
+ if (!found_dupack) {
+ pcb->dupacks = 0;
+ }
+ } else if (TCP_SEQ_BETWEEN(ackno, pcb->lastack+1, pcb->snd_nxt)){
+ /* We come here when the ACK acknowledges new data. */
+
+ /* Reset the "IN Fast Retransmit" flag, since we are no longer
+ in fast retransmit. Also reset the congestion window to the
+ slow start threshold. */
+ if (pcb->flags & TF_INFR) {
+ pcb->flags &= ~TF_INFR;
+ pcb->cwnd = pcb->ssthresh;
+ }
+
+ /* Reset the number of retransmissions. */
+ pcb->nrtx = 0;
+
+ /* Reset the retransmission time-out. */
+ pcb->rto = (pcb->sa >> 3) + pcb->sv;
+
+ /* Update the send buffer space. Diff between the two can never exceed 64K? */
+ pcb->acked = (u16_t)(ackno - pcb->lastack);
+
+ pcb->snd_buf += pcb->acked;
+
+ /* Reset the fast retransmit variables. */
+ pcb->dupacks = 0;
+ pcb->lastack = ackno;
+
+ /* Update the congestion control variables (cwnd and
+ ssthresh). */
+ if (pcb->state >= ESTABLISHED) {
+ if (pcb->cwnd < pcb->ssthresh) {
+ if ((u16_t)(pcb->cwnd + pcb->mss) > pcb->cwnd) {
+ pcb->cwnd += pcb->mss;
+ }
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: slow start cwnd %"U16_F"\n", pcb->cwnd));
+ } else {
+ u16_t new_cwnd = (pcb->cwnd + pcb->mss * pcb->mss / pcb->cwnd);
+ if (new_cwnd > pcb->cwnd) {
+ pcb->cwnd = new_cwnd;
+ }
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_receive: congestion avoidance cwnd %"U16_F"\n", pcb->cwnd));
+ }
+ }
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: ACK for %"U32_F", unacked->seqno %"U32_F":%"U32_F"\n",
+ ackno,
+ pcb->unacked != NULL?
+ ntohl(pcb->unacked->tcphdr->seqno): 0,
+ pcb->unacked != NULL?
+ ntohl(pcb->unacked->tcphdr->seqno) + TCP_TCPLEN(pcb->unacked): 0));
+
+ /* Remove segment from the unacknowledged list if the incoming
+ ACK acknowlegdes them. */
+ while (pcb->unacked != NULL &&
+ TCP_SEQ_LEQ(ntohl(pcb->unacked->tcphdr->seqno) +
+ TCP_TCPLEN(pcb->unacked), ackno)) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unacked\n",
+ ntohl(pcb->unacked->tcphdr->seqno),
+ ntohl(pcb->unacked->tcphdr->seqno) +
+ TCP_TCPLEN(pcb->unacked)));
+
+ next = pcb->unacked;
+ pcb->unacked = pcb->unacked->next;
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen));
+ LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
+ /* Prevent ACK for FIN to generate a sent event */
+ if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
+ pcb->acked--;
+ }
+
+ pcb->snd_queuelen -= pbuf_clen(next->p);
+ tcp_seg_free(next);
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unacked)\n", (u16_t)pcb->snd_queuelen));
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_receive: valid queue length", pcb->unacked != NULL ||
+ pcb->unsent != NULL);
+ }
+ }
+
+ /* If there's nothing left to acknowledge, stop the retransmit
+ timer, otherwise reset it to start again */
+ if(pcb->unacked == NULL)
+ pcb->rtime = -1;
+ else
+ pcb->rtime = 0;
+
+ pcb->polltmr = 0;
+
+#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS
+ if (PCB_ISIPV6(pcb)) {
+ /* Inform neighbor reachability of forward progress. */
+ nd6_reachability_hint(ip6_current_src_addr());
+ }
+#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/
+ } else {
+ /* Fix bug bug #21582: out of sequence ACK, didn't really ack anything */
+ pcb->acked = 0;
+ }
+
+ /* We go through the ->unsent list to see if any of the segments
+ on the list are acknowledged by the ACK. This may seem
+ strange since an "unsent" segment shouldn't be acked. The
+ rationale is that lwIP puts all outstanding segments on the
+ ->unsent list after a retransmission, so these segments may
+ in fact have been sent once. */
+ while (pcb->unsent != NULL &&
+ TCP_SEQ_BETWEEN(ackno, ntohl(pcb->unsent->tcphdr->seqno) +
+ TCP_TCPLEN(pcb->unsent), pcb->snd_nxt)) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: removing %"U32_F":%"U32_F" from pcb->unsent\n",
+ ntohl(pcb->unsent->tcphdr->seqno), ntohl(pcb->unsent->tcphdr->seqno) +
+ TCP_TCPLEN(pcb->unsent)));
+
+ next = pcb->unsent;
+ pcb->unsent = pcb->unsent->next;
+#if TCP_OVERSIZE
+ if (pcb->unsent == NULL) {
+ pcb->unsent_oversize = 0;
+ }
+#endif /* TCP_OVERSIZE */
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_receive: queuelen %"U16_F" ... ", (u16_t)pcb->snd_queuelen));
+ LWIP_ASSERT("pcb->snd_queuelen >= pbuf_clen(next->p)", (pcb->snd_queuelen >= pbuf_clen(next->p)));
+ /* Prevent ACK for FIN to generate a sent event */
+ if ((pcb->acked != 0) && ((TCPH_FLAGS(next->tcphdr) & TCP_FIN) != 0)) {
+ pcb->acked--;
+ }
+ pcb->snd_queuelen -= pbuf_clen(next->p);
+ tcp_seg_free(next);
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("%"U16_F" (after freeing unsent)\n", (u16_t)pcb->snd_queuelen));
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_receive: valid queue length",
+ pcb->unacked != NULL || pcb->unsent != NULL);
+ }
+ }
+ /* End of ACK for new data processing. */
+
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: pcb->rttest %"U32_F" rtseq %"U32_F" ackno %"U32_F"\n",
+ pcb->rttest, pcb->rtseq, ackno));
+
+ /* RTT estimation calculations. This is done by checking if the
+ incoming segment acknowledges the segment we use to take a
+ round-trip time measurement. */
+ if (pcb->rttest && TCP_SEQ_LT(pcb->rtseq, ackno)) {
+ /* diff between this shouldn't exceed 32K since this are tcp timer ticks
+ and a round-trip shouldn't be that long... */
+ m = (s16_t)(tcp_ticks - pcb->rttest);
+
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: experienced rtt %"U16_F" ticks (%"U16_F" msec).\n",
+ m, m * TCP_SLOW_INTERVAL));
+
+ /* This is taken directly from VJs original code in his paper */
+ m = m - (pcb->sa >> 3);
+ pcb->sa += m;
+ if (m < 0) {
+ m = -m;
+ }
+ m = m - (pcb->sv >> 2);
+ pcb->sv += m;
+ pcb->rto = (pcb->sa >> 3) + pcb->sv;
+
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_receive: RTO %"U16_F" (%"U16_F" milliseconds)\n",
+ pcb->rto, pcb->rto * TCP_SLOW_INTERVAL));
+
+ pcb->rttest = 0;
+ }
+ }
+
+ /* If the incoming segment contains data, we must process it
+ further unless the pcb already received a FIN.
+ (RFC 793, chapeter 3.9, "SEGMENT ARRIVES" in states CLOSE-WAIT, CLOSING,
+ LAST-ACK and TIME-WAIT: "Ignore the segment text.") */
+ if ((tcplen > 0) && (pcb->state < CLOSE_WAIT)) {
+ /* This code basically does three things:
+
+ +) If the incoming segment contains data that is the next
+ in-sequence data, this data is passed to the application. This
+ might involve trimming the first edge of the data. The rcv_nxt
+ variable and the advertised window are adjusted.
+
+ +) If the incoming segment has data that is above the next
+ sequence number expected (->rcv_nxt), the segment is placed on
+ the ->ooseq queue. This is done by finding the appropriate
+ place in the ->ooseq queue (which is ordered by sequence
+ number) and trim the segment in both ends if needed. An
+ immediate ACK is sent to indicate that we received an
+ out-of-sequence segment.
+
+ +) Finally, we check if the first segment on the ->ooseq queue
+ now is in sequence (i.e., if rcv_nxt >= ooseq->seqno). If
+ rcv_nxt > ooseq->seqno, we must trim the first edge of the
+ segment on ->ooseq before we adjust rcv_nxt. The data in the
+ segments that are now on sequence are chained onto the
+ incoming segment so that we only need to call the application
+ once.
+ */
+
+ /* First, we check if we must trim the first edge. We have to do
+ this if the sequence number of the incoming segment is less
+ than rcv_nxt, and the sequence number plus the length of the
+ segment is larger than rcv_nxt. */
+ /* if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){
+ if (TCP_SEQ_LT(pcb->rcv_nxt, seqno + tcplen)) {*/
+ if (TCP_SEQ_BETWEEN(pcb->rcv_nxt, seqno + 1, seqno + tcplen - 1)){
+ /* Trimming the first edge is done by pushing the payload
+ pointer in the pbuf downwards. This is somewhat tricky since
+ we do not want to discard the full contents of the pbuf up to
+ the new starting point of the data since we have to keep the
+ TCP header which is present in the first pbuf in the chain.
+
+ What is done is really quite a nasty hack: the first pbuf in
+ the pbuf chain is pointed to by inseg.p. Since we need to be
+ able to deallocate the whole pbuf, we cannot change this
+ inseg.p pointer to point to any of the later pbufs in the
+ chain. Instead, we point the ->payload pointer in the first
+ pbuf to data in one of the later pbufs. We also set the
+ inseg.data pointer to point to the right place. This way, the
+ ->p pointer will still point to the first pbuf, but the
+ ->p->payload pointer will point to data in another pbuf.
+
+ After we are done with adjusting the pbuf pointers we must
+ adjust the ->data pointer in the seg and the segment
+ length.*/
+
+ off = pcb->rcv_nxt - seqno;
+ p = inseg.p;
+ LWIP_ASSERT("inseg.p != NULL", inseg.p);
+ LWIP_ASSERT("insane offset!", (off < 0x7fff));
+ if (inseg.p->len < off) {
+ LWIP_ASSERT("pbuf too short!", (((s32_t)inseg.p->tot_len) >= off));
+ new_tot_len = (u16_t)(inseg.p->tot_len - off);
+ while (p->len < off) {
+ off -= p->len;
+ /* KJM following line changed (with addition of new_tot_len var)
+ to fix bug #9076
+ inseg.p->tot_len -= p->len; */
+ p->tot_len = new_tot_len;
+ p->len = 0;
+ p = p->next;
+ }
+ if(pbuf_header(p, (s16_t)-off)) {
+ /* Do we need to cope with this failing? Assert for now */
+ LWIP_ASSERT("pbuf_header failed", 0);
+ }
+ } else {
+ if(pbuf_header(inseg.p, (s16_t)-off)) {
+ /* Do we need to cope with this failing? Assert for now */
+ LWIP_ASSERT("pbuf_header failed", 0);
+ }
+ }
+ inseg.len -= (u16_t)(pcb->rcv_nxt - seqno);
+ inseg.tcphdr->seqno = seqno = pcb->rcv_nxt;
+ }
+ else {
+ if (TCP_SEQ_LT(seqno, pcb->rcv_nxt)){
+ /* the whole segment is < rcv_nxt */
+ /* must be a duplicate of a packet that has already been correctly handled */
+
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: duplicate seqno %"U32_F"\n", seqno));
+ tcp_ack_now(pcb);
+ }
+ }
+
+ /* The sequence number must be within the window (above rcv_nxt
+ and below rcv_nxt + rcv_wnd) in order to be further
+ processed. */
+ if (TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt,
+ pcb->rcv_nxt + pcb->rcv_wnd - 1)){
+ if (pcb->rcv_nxt == seqno) {
+ /* The incoming segment is the next in sequence. We check if
+ we have to trim the end of the segment and update rcv_nxt
+ and pass the data to the application. */
+ tcplen = TCP_TCPLEN(&inseg);
+
+ if (tcplen > pcb->rcv_wnd) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG,
+ ("tcp_receive: other end overran receive window"
+ "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
+ seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+ /* Must remove the FIN from the header as we're trimming
+ * that byte of sequence-space from the packet */
+ TCPH_FLAGS_SET(inseg.tcphdr, TCPH_FLAGS(inseg.tcphdr) &~ TCP_FIN);
+ }
+ /* Adjust length of segment to fit in the window. */
+ inseg.len = pcb->rcv_wnd;
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
+ inseg.len -= 1;
+ }
+ pbuf_realloc(inseg.p, inseg.len);
+ tcplen = TCP_TCPLEN(&inseg);
+ LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
+ (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
+ }
+#if TCP_QUEUE_OOSEQ
+ /* Received in-sequence data, adjust ooseq data if:
+ - FIN has been received or
+ - inseq overlaps with ooseq */
+ if (pcb->ooseq != NULL) {
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG,
+ ("tcp_receive: received in-order FIN, binning ooseq queue\n"));
+ /* Received in-order FIN means anything that was received
+ * out of order must now have been received in-order, so
+ * bin the ooseq queue */
+ while (pcb->ooseq != NULL) {
+ struct tcp_seg *old_ooseq = pcb->ooseq;
+ pcb->ooseq = pcb->ooseq->next;
+ tcp_seg_free(old_ooseq);
+ }
+ } else {
+ next = pcb->ooseq;
+ /* Remove all segments on ooseq that are covered by inseg already.
+ * FIN is copied from ooseq to inseg if present. */
+ while (next &&
+ TCP_SEQ_GEQ(seqno + tcplen,
+ next->tcphdr->seqno + next->len)) {
+ /* inseg cannot have FIN here (already processed above) */
+ if (TCPH_FLAGS(next->tcphdr) & TCP_FIN &&
+ (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) == 0) {
+ TCPH_SET_FLAG(inseg.tcphdr, TCP_FIN);
+ tcplen = TCP_TCPLEN(&inseg);
+ }
+ prev = next;
+ next = next->next;
+ tcp_seg_free(prev);
+ }
+ /* Now trim right side of inseg if it overlaps with the first
+ * segment on ooseq */
+ if (next &&
+ TCP_SEQ_GT(seqno + tcplen,
+ next->tcphdr->seqno)) {
+ /* inseg cannot have FIN here (already processed above) */
+ inseg.len = (u16_t)(next->tcphdr->seqno - seqno);
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_SYN) {
+ inseg.len -= 1;
+ }
+ pbuf_realloc(inseg.p, inseg.len);
+ tcplen = TCP_TCPLEN(&inseg);
+ LWIP_ASSERT("tcp_receive: segment not trimmed correctly to ooseq queue\n",
+ (seqno + tcplen) == next->tcphdr->seqno);
+ }
+ pcb->ooseq = next;
+ }
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+
+ pcb->rcv_nxt = seqno + tcplen;
+
+ /* Update the receiver's (our) window. */
+ LWIP_ASSERT("tcp_receive: tcplen > rcv_wnd\n", pcb->rcv_wnd >= tcplen);
+ pcb->rcv_wnd -= tcplen;
+
+ tcp_update_rcv_ann_wnd(pcb);
+
+ /* If there is data in the segment, we make preparations to
+ pass this up to the application. The ->recv_data variable
+ is used for holding the pbuf that goes to the
+ application. The code for reassembling out-of-sequence data
+ chains its data on this pbuf as well.
+
+ If the segment was a FIN, we set the TF_GOT_FIN flag that will
+ be used to indicate to the application that the remote side has
+ closed its end of the connection. */
+ if (inseg.p->tot_len > 0) {
+ recv_data = inseg.p;
+ /* Since this pbuf now is the responsibility of the
+ application, we delete our reference to it so that we won't
+ (mistakingly) deallocate it. */
+ inseg.p = NULL;
+ }
+ if (TCPH_FLAGS(inseg.tcphdr) & TCP_FIN) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: received FIN.\n"));
+ recv_flags |= TF_GOT_FIN;
+ }
+
+#if TCP_QUEUE_OOSEQ
+ /* We now check if we have segments on the ->ooseq queue that
+ are now in sequence. */
+ while (pcb->ooseq != NULL &&
+ pcb->ooseq->tcphdr->seqno == pcb->rcv_nxt) {
+
+ cseg = pcb->ooseq;
+ seqno = pcb->ooseq->tcphdr->seqno;
+
+ pcb->rcv_nxt += TCP_TCPLEN(cseg);
+ LWIP_ASSERT("tcp_receive: ooseq tcplen > rcv_wnd\n",
+ pcb->rcv_wnd >= TCP_TCPLEN(cseg));
+ pcb->rcv_wnd -= TCP_TCPLEN(cseg);
+
+ tcp_update_rcv_ann_wnd(pcb);
+
+ if (cseg->p->tot_len > 0) {
+ /* Chain this pbuf onto the pbuf that we will pass to
+ the application. */
+ if (recv_data) {
+ pbuf_cat(recv_data, cseg->p);
+ } else {
+ recv_data = cseg->p;
+ }
+ cseg->p = NULL;
+ }
+ if (TCPH_FLAGS(cseg->tcphdr) & TCP_FIN) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_receive: dequeued FIN.\n"));
+ recv_flags |= TF_GOT_FIN;
+ if (pcb->state == ESTABLISHED) { /* force passive close or we can move to active close */
+ pcb->state = CLOSE_WAIT;
+ }
+ }
+
+ pcb->ooseq = cseg->next;
+ tcp_seg_free(cseg);
+ }
+#endif /* TCP_QUEUE_OOSEQ */
+
+
+ /* Acknowledge the segment(s). */
+ tcp_ack(pcb);
+
+#if LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS
+ if (PCB_ISIPV6(pcb)) {
+ /* Inform neighbor reachability of forward progress. */
+ nd6_reachability_hint(ip6_current_src_addr());
+ }
+#endif /* LWIP_IPV6 && LWIP_ND6_TCP_REACHABILITY_HINTS*/
+
+ } else {
+ /* We get here if the incoming segment is out-of-sequence. */
+ tcp_send_empty_ack(pcb);
+#if TCP_QUEUE_OOSEQ
+ /* We queue the segment on the ->ooseq queue. */
+ if (pcb->ooseq == NULL) {
+ pcb->ooseq = tcp_seg_copy(&inseg);
+ } else {
+ /* If the queue is not empty, we walk through the queue and
+ try to find a place where the sequence number of the
+ incoming segment is between the sequence numbers of the
+ previous and the next segment on the ->ooseq queue. That is
+ the place where we put the incoming segment. If needed, we
+ trim the second edges of the previous and the incoming
+ segment so that it will fit into the sequence.
+
+ If the incoming segment has the same sequence number as a
+ segment on the ->ooseq queue, we discard the segment that
+ contains less data. */
+
+ prev = NULL;
+ for(next = pcb->ooseq; next != NULL; next = next->next) {
+ if (seqno == next->tcphdr->seqno) {
+ /* The sequence number of the incoming segment is the
+ same as the sequence number of the segment on
+ ->ooseq. We check the lengths to see which one to
+ discard. */
+ if (inseg.len > next->len) {
+ /* The incoming segment is larger than the old
+ segment. We replace some segments with the new
+ one. */
+ cseg = tcp_seg_copy(&inseg);
+ if (cseg != NULL) {
+ if (prev != NULL) {
+ prev->next = cseg;
+ } else {
+ pcb->ooseq = cseg;
+ }
+ tcp_oos_insert_segment(cseg, next);
+ }
+ break;
+ } else {
+ /* Either the lenghts are the same or the incoming
+ segment was smaller than the old one; in either
+ case, we ditch the incoming segment. */
+ break;
+ }
+ } else {
+ if (prev == NULL) {
+ if (TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {
+ /* The sequence number of the incoming segment is lower
+ than the sequence number of the first segment on the
+ queue. We put the incoming segment first on the
+ queue. */
+ cseg = tcp_seg_copy(&inseg);
+ if (cseg != NULL) {
+ pcb->ooseq = cseg;
+ tcp_oos_insert_segment(cseg, next);
+ }
+ break;
+ }
+ } else {
+ /*if (TCP_SEQ_LT(prev->tcphdr->seqno, seqno) &&
+ TCP_SEQ_LT(seqno, next->tcphdr->seqno)) {*/
+ if (TCP_SEQ_BETWEEN(seqno, prev->tcphdr->seqno+1, next->tcphdr->seqno-1)) {
+ /* The sequence number of the incoming segment is in
+ between the sequence numbers of the previous and
+ the next segment on ->ooseq. We trim trim the previous
+ segment, delete next segments that included in received segment
+ and trim received, if needed. */
+ cseg = tcp_seg_copy(&inseg);
+ if (cseg != NULL) {
+ if (TCP_SEQ_GT(prev->tcphdr->seqno + prev->len, seqno)) {
+ /* We need to trim the prev segment. */
+ prev->len = (u16_t)(seqno - prev->tcphdr->seqno);
+ pbuf_realloc(prev->p, prev->len);
+ }
+ prev->next = cseg;
+ tcp_oos_insert_segment(cseg, next);
+ }
+ break;
+ }
+ }
+ /* If the "next" segment is the last segment on the
+ ooseq queue, we add the incoming segment to the end
+ of the list. */
+ if (next->next == NULL &&
+ TCP_SEQ_GT(seqno, next->tcphdr->seqno)) {
+ if (TCPH_FLAGS(next->tcphdr) & TCP_FIN) {
+ /* segment "next" already contains all data */
+ break;
+ }
+ next->next = tcp_seg_copy(&inseg);
+ if (next->next != NULL) {
+ if (TCP_SEQ_GT(next->tcphdr->seqno + next->len, seqno)) {
+ /* We need to trim the last segment. */
+ next->len = (u16_t)(seqno - next->tcphdr->seqno);
+ pbuf_realloc(next->p, next->len);
+ }
+ /* check if the remote side overruns our receive window */
+ if ((u32_t)tcplen + seqno > pcb->rcv_nxt + (u32_t)pcb->rcv_wnd) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG,
+ ("tcp_receive: other end overran receive window"
+ "seqno %"U32_F" len %"U16_F" right edge %"U32_F"\n",
+ seqno, tcplen, pcb->rcv_nxt + pcb->rcv_wnd));
+ if (TCPH_FLAGS(next->next->tcphdr) & TCP_FIN) {
+ /* Must remove the FIN from the header as we're trimming
+ * that byte of sequence-space from the packet */
+ TCPH_FLAGS_SET(next->next->tcphdr, TCPH_FLAGS(next->next->tcphdr) &~ TCP_FIN);
+ }
+ /* Adjust length of segment to fit in the window. */
+ next->next->len = pcb->rcv_nxt + pcb->rcv_wnd - seqno;
+ pbuf_realloc(next->next->p, next->next->len);
+ tcplen = TCP_TCPLEN(next->next);
+ LWIP_ASSERT("tcp_receive: segment not trimmed correctly to rcv_wnd\n",
+ (seqno + tcplen) == (pcb->rcv_nxt + pcb->rcv_wnd));
+ }
+ }
+ break;
+ }
+ }
+ prev = next;
+ }
+ }
+#if TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS
+ /* Check that the data on ooseq doesn't exceed one of the limits
+ and throw away everything above that limit. */
+ ooseq_blen = 0;
+ ooseq_qlen = 0;
+ prev = NULL;
+ for(next = pcb->ooseq; next != NULL; prev = next, next = next->next) {
+ struct pbuf *p = next->p;
+ ooseq_blen += p->tot_len;
+ ooseq_qlen += pbuf_clen(p);
+ if ((ooseq_blen > TCP_OOSEQ_MAX_BYTES) ||
+ (ooseq_qlen > TCP_OOSEQ_MAX_PBUFS)) {
+ /* too much ooseq data, dump this and everything after it */
+ tcp_segs_free(next);
+ if (prev == NULL) {
+ /* first ooseq segment is too much, dump the whole queue */
+ pcb->ooseq = NULL;
+ } else {
+ /* just dump 'next' and everything after it */
+ prev->next = NULL;
+ }
+ break;
+ }
+ }
+#endif /* TCP_OOSEQ_MAX_BYTES || TCP_OOSEQ_MAX_PBUFS */
+#endif /* TCP_QUEUE_OOSEQ */
+ }
+ } else {
+ /* The incoming segment is not withing the window. */
+ tcp_send_empty_ack(pcb);
+ }
+ } else {
+ /* Segments with length 0 is taken care of here. Segments that
+ fall out of the window are ACKed. */
+ /*if (TCP_SEQ_GT(pcb->rcv_nxt, seqno) ||
+ TCP_SEQ_GEQ(seqno, pcb->rcv_nxt + pcb->rcv_wnd)) {*/
+ if(!TCP_SEQ_BETWEEN(seqno, pcb->rcv_nxt, pcb->rcv_nxt + pcb->rcv_wnd-1)){
+ tcp_ack_now(pcb);
+ }
+ }
+}
+
+/**
+ * Parses the options contained in the incoming segment.
+ *
+ * Called from tcp_listen_input() and tcp_process().
+ * Currently, only the MSS option is supported!
+ *
+ * @param pcb the tcp_pcb for which a segment arrived
+ */
+static void
+tcp_parseopt(struct tcp_pcb *pcb)
+{
+ u16_t c, max_c;
+ u16_t mss;
+ u8_t *opts, opt;
+#if LWIP_TCP_TIMESTAMPS
+ u32_t tsval;
+#endif
+
+ opts = (u8_t *)tcphdr + TCP_HLEN;
+
+ /* Parse the TCP MSS option, if present. */
+ if(TCPH_HDRLEN(tcphdr) > 0x5) {
+ max_c = (TCPH_HDRLEN(tcphdr) - 5) << 2;
+ for (c = 0; c < max_c; ) {
+ opt = opts[c];
+ switch (opt) {
+ case 0x00:
+ /* End of options. */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: EOL\n"));
+ return;
+ case 0x01:
+ /* NOP option. */
+ ++c;
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: NOP\n"));
+ break;
+ case 0x02:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: MSS\n"));
+ if (opts[c + 1] != 0x04 || c + 0x04 > max_c) {
+ /* Bad length */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+ return;
+ }
+ /* An MSS option with the right option length. */
+ mss = (opts[c + 2] << 8) | opts[c + 3];
+ /* Limit the mss to the configured TCP_MSS and prevent division by zero */
+ pcb->mss = ((mss > TCP_MSS) || (mss == 0)) ? TCP_MSS : mss;
+ /* Advance to next option */
+ c += 0x04;
+ break;
+#if LWIP_TCP_TIMESTAMPS
+ case 0x08:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: TS\n"));
+ if (opts[c + 1] != 0x0A || c + 0x0A > max_c) {
+ /* Bad length */
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+ return;
+ }
+ /* TCP timestamp option with valid length */
+ tsval = (opts[c+2]) | (opts[c+3] << 8) |
+ (opts[c+4] << 16) | (opts[c+5] << 24);
+ if (flags & TCP_SYN) {
+ pcb->ts_recent = ntohl(tsval);
+ pcb->flags |= TF_TIMESTAMP;
+ } else if (TCP_SEQ_BETWEEN(pcb->ts_lastacksent, seqno, seqno+tcplen)) {
+ pcb->ts_recent = ntohl(tsval);
+ }
+ /* Advance to next option */
+ c += 0x0A;
+ break;
+#endif
+ default:
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: other\n"));
+ if (opts[c + 1] == 0) {
+ LWIP_DEBUGF(TCP_INPUT_DEBUG, ("tcp_parseopt: bad length\n"));
+ /* If the length field is zero, the options are malformed
+ and we don't process them further. */
+ return;
+ }
+ /* All other options have a length field, so that we easily
+ can skip past them. */
+ c += opts[c + 1];
+ }
+ }
+ }
+}
+
+#endif /* LWIP_TCP */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/tcp_out.c b/src/VBox/Devices/Network/lwip-new/src/core/tcp_out.c
new file mode 100644
index 00000000..9884efa2
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/tcp_out.c
@@ -0,0 +1,1505 @@
+/**
+ * @file
+ * Transmission Control Protocol, outgoing traffic
+ *
+ * The output functions of TCP.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/tcp_impl.h"
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/inet_chksum.h"
+#if LWIP_TCP_TIMESTAMPS
+#include "lwip/sys.h"
+#endif
+
+#include <string.h>
+
+/* Define some copy-macros for checksum-on-copy so that the code looks
+ nicer by preventing too many ifdef's. */
+#if TCP_CHECKSUM_ON_COPY
+#define TCP_DATA_COPY(dst, src, len, seg) do { \
+ tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), \
+ len, &seg->chksum, &seg->chksum_swapped); \
+ seg->flags |= TF_SEG_DATA_CHECKSUMMED; } while(0)
+#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) \
+ tcp_seg_add_chksum(LWIP_CHKSUM_COPY(dst, src, len), len, chksum, chksum_swapped);
+#else /* TCP_CHECKSUM_ON_COPY*/
+#define TCP_DATA_COPY(dst, src, len, seg) MEMCPY(dst, src, len)
+#define TCP_DATA_COPY2(dst, src, len, chksum, chksum_swapped) MEMCPY(dst, src, len)
+#endif /* TCP_CHECKSUM_ON_COPY*/
+
+/** Define this to 1 for an extra check that the output checksum is valid
+ * (usefule when the checksum is generated by the application, not the stack) */
+#ifndef TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+#define TCP_CHECKSUM_ON_COPY_SANITY_CHECK 0
+#endif
+
+/* Forward declarations.*/
+static void tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb);
+
+/** Allocate a pbuf and create a tcphdr at p->payload, used for output
+ * functions other than the default tcp_output -> tcp_output_segment
+ * (e.g. tcp_send_empty_ack, etc.)
+ *
+ * @param pcb tcp pcb for which to send a packet (used to initialize tcp_hdr)
+ * @param optlen length of header-options
+ * @param datalen length of tcp data to reserve in pbuf
+ * @param seqno_be seqno in network byte order (big-endian)
+ * @return pbuf with p->payload being the tcp_hdr
+ */
+static struct pbuf *
+tcp_output_alloc_header(struct tcp_pcb *pcb, u16_t optlen, u16_t datalen,
+ u32_t seqno_be /* already in network byte order */)
+{
+ struct tcp_hdr *tcphdr;
+ struct pbuf *p = pbuf_alloc(PBUF_IP, TCP_HLEN + optlen + datalen, PBUF_RAM);
+ if (p != NULL) {
+ LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
+ (p->len >= TCP_HLEN + optlen));
+ tcphdr = (struct tcp_hdr *)p->payload;
+ tcphdr->src = htons(pcb->local_port);
+ tcphdr->dest = htons(pcb->remote_port);
+ tcphdr->seqno = seqno_be;
+ tcphdr->ackno = htonl(pcb->rcv_nxt);
+ TCPH_HDRLEN_FLAGS_SET(tcphdr, (5 + optlen / 4), TCP_ACK);
+ tcphdr->wnd = htons(pcb->rcv_ann_wnd);
+ tcphdr->chksum = 0;
+ tcphdr->urgp = 0;
+
+ /* If we're sending a packet, update the announced right window edge */
+ pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
+ }
+ return p;
+}
+
+/**
+ * Called by tcp_close() to send a segment including FIN flag but not data.
+ *
+ * @param pcb the tcp_pcb over which to send a segment
+ * @return ERR_OK if sent, another err_t otherwise
+ */
+err_t
+tcp_send_fin(struct tcp_pcb *pcb)
+{
+ /* first, try to add the fin to the last unsent segment */
+ if (pcb->unsent != NULL) {
+ struct tcp_seg *last_unsent;
+ for (last_unsent = pcb->unsent; last_unsent->next != NULL;
+ last_unsent = last_unsent->next);
+
+ if ((TCPH_FLAGS(last_unsent->tcphdr) & (TCP_SYN | TCP_FIN | TCP_RST)) == 0) {
+ /* no SYN/FIN/RST flag in the header, we can add the FIN flag */
+ TCPH_SET_FLAG(last_unsent->tcphdr, TCP_FIN);
+ pcb->flags |= TF_FIN;
+ return ERR_OK;
+ }
+ }
+ /* no data, no length, flags, copy=1, no optdata */
+ return tcp_enqueue_flags(pcb, TCP_FIN);
+}
+
+/**
+ * Create a TCP segment with prefilled header.
+ *
+ * Called by tcp_write and tcp_enqueue_flags.
+ *
+ * @param pcb Protocol control block for the TCP connection.
+ * @param p pbuf that is used to hold the TCP header.
+ * @param flags TCP flags for header.
+ * @param seqno TCP sequence number of this packet
+ * @param optflags options to include in TCP header
+ * @return a new tcp_seg pointing to p, or NULL.
+ * The TCP header is filled in except ackno and wnd.
+ * p is freed on failure.
+ */
+static struct tcp_seg *
+tcp_create_segment(struct tcp_pcb *pcb, struct pbuf *p, u8_t flags, u32_t seqno, u8_t optflags)
+{
+ struct tcp_seg *seg;
+ u8_t optlen = LWIP_TCP_OPT_LENGTH(optflags);
+
+ if ((seg = (struct tcp_seg *)memp_malloc(MEMP_TCP_SEG)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no memory.\n"));
+ pbuf_free(p);
+ return NULL;
+ }
+ seg->flags = optflags;
+ seg->next = NULL;
+ seg->p = p;
+ seg->len = p->tot_len - optlen;
+#if TCP_OVERSIZE_DBGCHECK
+ seg->oversize_left = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+ seg->chksum = 0;
+ seg->chksum_swapped = 0;
+ /* check optflags */
+ LWIP_ASSERT("invalid optflags passed: TF_SEG_DATA_CHECKSUMMED",
+ (optflags & TF_SEG_DATA_CHECKSUMMED) == 0);
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+ /* build TCP header */
+ if (pbuf_header(p, TCP_HLEN)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_create_segment: no room for TCP header in pbuf.\n"));
+ TCP_STATS_INC(tcp.err);
+ tcp_seg_free(seg);
+ return NULL;
+ }
+ seg->tcphdr = (struct tcp_hdr *)seg->p->payload;
+ seg->tcphdr->src = htons(pcb->local_port);
+ seg->tcphdr->dest = htons(pcb->remote_port);
+ seg->tcphdr->seqno = htonl(seqno);
+ /* ackno is set in tcp_output */
+ TCPH_HDRLEN_FLAGS_SET(seg->tcphdr, (5 + optlen / 4), flags);
+ /* wnd and chksum are set in tcp_output */
+ seg->tcphdr->urgp = 0;
+ return seg;
+}
+
+/**
+ * Allocate a PBUF_RAM pbuf, perhaps with extra space at the end.
+ *
+ * This function is like pbuf_alloc(layer, length, PBUF_RAM) except
+ * there may be extra bytes available at the end.
+ *
+ * @param layer flag to define header size.
+ * @param length size of the pbuf's payload.
+ * @param max_length maximum usable size of payload+oversize.
+ * @param oversize pointer to a u16_t that will receive the number of usable tail bytes.
+ * @param pcb The TCP connection that willo enqueue the pbuf.
+ * @param apiflags API flags given to tcp_write.
+ * @param first_seg true when this pbuf will be used in the first enqueued segment.
+ * @param
+ */
+#if TCP_OVERSIZE
+static struct pbuf *
+tcp_pbuf_prealloc(pbuf_layer layer, u16_t length, u16_t max_length,
+ u16_t *oversize, struct tcp_pcb *pcb, u8_t apiflags,
+ u8_t first_seg)
+{
+ struct pbuf *p;
+ u16_t alloc = length;
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ LWIP_UNUSED_ARG(max_length);
+ LWIP_UNUSED_ARG(pcb);
+ LWIP_UNUSED_ARG(apiflags);
+ LWIP_UNUSED_ARG(first_seg);
+ /* always create MSS-sized pbufs */
+ alloc = max_length;
+#else /* LWIP_NETIF_TX_SINGLE_PBUF */
+ if (length < max_length) {
+ /* Should we allocate an oversized pbuf, or just the minimum
+ * length required? If tcp_write is going to be called again
+ * before this segment is transmitted, we want the oversized
+ * buffer. If the segment will be transmitted immediately, we can
+ * save memory by allocating only length. We use a simple
+ * heuristic based on the following information:
+ *
+ * Did the user set TCP_WRITE_FLAG_MORE?
+ *
+ * Will the Nagle algorithm defer transmission of this segment?
+ */
+ if ((apiflags & TCP_WRITE_FLAG_MORE) ||
+ (!(pcb->flags & TF_NODELAY) &&
+ (!first_seg ||
+ pcb->unsent != NULL ||
+ pcb->unacked != NULL))) {
+ alloc = LWIP_MIN(max_length, LWIP_MEM_ALIGN_SIZE(length + TCP_OVERSIZE));
+ }
+ }
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+ p = pbuf_alloc(layer, alloc, PBUF_RAM);
+ if (p == NULL) {
+ return NULL;
+ }
+ LWIP_ASSERT("need unchained pbuf", p->next == NULL);
+ *oversize = p->len - length;
+ /* trim p->len to the currently used size */
+ p->len = p->tot_len = length;
+ return p;
+}
+#else /* TCP_OVERSIZE */
+#define tcp_pbuf_prealloc(layer, length, mx, os, pcb, api, fst) pbuf_alloc((layer), (length), PBUF_RAM)
+#endif /* TCP_OVERSIZE */
+
+#if TCP_CHECKSUM_ON_COPY
+/** Add a checksum of newly added data to the segment */
+static void
+tcp_seg_add_chksum(u16_t chksum, u16_t len, u16_t *seg_chksum,
+ u8_t *seg_chksum_swapped)
+{
+ u32_t helper;
+ /* add chksum to old chksum and fold to u16_t */
+ helper = chksum + *seg_chksum;
+ chksum = FOLD_U32T(helper);
+ if ((len & 1) != 0) {
+ *seg_chksum_swapped = 1 - *seg_chksum_swapped;
+ chksum = SWAP_BYTES_IN_WORD(chksum);
+ }
+ *seg_chksum = chksum;
+}
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+/** Checks if tcp_write is allowed or not (checks state, snd_buf and snd_queuelen).
+ *
+ * @param pcb the tcp pcb to check for
+ * @param len length of data to send (checked agains snd_buf)
+ * @return ERR_OK if tcp_write is allowed to proceed, another err_t otherwise
+ */
+static err_t
+tcp_write_checks(struct tcp_pcb *pcb, u16_t len)
+{
+ /* connection is in invalid state for data transmission? */
+ if ((pcb->state != ESTABLISHED) &&
+ (pcb->state != CLOSE_WAIT) &&
+ (pcb->state != SYN_SENT) &&
+ (pcb->state != SYN_RCVD)) { /* NB: but not SYN_RCVD_0 */
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_STATE | LWIP_DBG_LEVEL_SEVERE, ("tcp_write() called in invalid state\n"));
+ return ERR_CONN;
+ } else if (len == 0) {
+ return ERR_OK;
+ }
+
+ /* fail on too much data */
+ if (len > pcb->snd_buf) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too much data (len=%"U16_F" > snd_buf=%"U16_F")\n",
+ len, pcb->snd_buf));
+ pcb->flags |= TF_NAGLEMEMERR;
+ return ERR_MEM;
+ }
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));
+
+ /* If total number of pbufs on the unsent/unacked queues exceeds the
+ * configured maximum, return an error */
+ /* check for configured max queuelen and possible overflow */
+ if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_write: too long queue %"U16_F" (max %"U16_F")\n",
+ pcb->snd_queuelen, TCP_SND_QUEUELEN));
+ TCP_STATS_INC(tcp.memerr);
+ pcb->flags |= TF_NAGLEMEMERR;
+ return ERR_MEM;
+ }
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_write: pbufs on queue => at least one queue non-empty",
+ pcb->unacked != NULL || pcb->unsent != NULL);
+ } else {
+ LWIP_ASSERT("tcp_write: no pbufs on queue => both queues empty",
+ pcb->unacked == NULL && pcb->unsent == NULL);
+ }
+ return ERR_OK;
+}
+
+/**
+ * Write data for sending (but does not send it immediately).
+ *
+ * It waits in the expectation of more data being sent soon (as
+ * it can send them more efficiently by combining them together).
+ * To prompt the system to send data now, call tcp_output() after
+ * calling tcp_write().
+ *
+ * @param pcb Protocol control block for the TCP connection to enqueue data for.
+ * @param arg Pointer to the data to be enqueued for sending.
+ * @param len Data length in bytes
+ * @param apiflags combination of following flags :
+ * - TCP_WRITE_FLAG_COPY (0x01) data will be copied into memory belonging to the stack
+ * - TCP_WRITE_FLAG_MORE (0x02) for TCP connection, PSH flag will be set on last segment sent,
+ * @return ERR_OK if enqueued, another err_t on error
+ */
+err_t
+tcp_write(struct tcp_pcb *pcb, const void *arg, u16_t len, u8_t apiflags)
+{
+ struct pbuf *concat_p = NULL;
+ struct tcp_seg *last_unsent = NULL, *seg = NULL, *prev_seg = NULL, *queue = NULL;
+ u16_t pos = 0; /* position in 'arg' data */
+ u16_t queuelen;
+ u8_t optlen = 0;
+ u8_t optflags = 0;
+#if TCP_OVERSIZE
+ u16_t oversize = 0;
+ u16_t oversize_used = 0;
+#endif /* TCP_OVERSIZE */
+#if TCP_CHECKSUM_ON_COPY
+ u16_t concat_chksum = 0;
+ u8_t concat_chksum_swapped = 0;
+ u16_t concat_chksummed = 0;
+#endif /* TCP_CHECKSUM_ON_COPY */
+ err_t err;
+ /* don't allocate segments bigger than half the maximum window we ever received */
+ u16_t mss_local = LWIP_MIN(pcb->mss, pcb->snd_wnd_max/2);
+
+#if LWIP_NETIF_TX_SINGLE_PBUF
+ /* Always copy to try to create single pbufs for TX */
+ apiflags |= TCP_WRITE_FLAG_COPY;
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_write(pcb=%p, data=%p, len=%"U16_F", apiflags=%"U16_F")\n",
+ (void *)pcb, arg, len, (u16_t)apiflags));
+ LWIP_ERROR("tcp_write: arg == NULL (programmer violates API)",
+ arg != NULL, return ERR_ARG;);
+
+ err = tcp_write_checks(pcb, len);
+ if (err != ERR_OK) {
+ return err;
+ }
+ queuelen = pcb->snd_queuelen;
+
+#if LWIP_TCP_TIMESTAMPS
+ if ((pcb->flags & TF_TIMESTAMP)) {
+ optflags = TF_SEG_OPTS_TS;
+ optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
+ }
+#endif /* LWIP_TCP_TIMESTAMPS */
+
+
+ /*
+ * TCP segmentation is done in three phases with increasing complexity:
+ *
+ * 1. Copy data directly into an oversized pbuf.
+ * 2. Chain a new pbuf to the end of pcb->unsent.
+ * 3. Create new segments.
+ *
+ * We may run out of memory at any point. In that case we must
+ * return ERR_MEM and not change anything in pcb. Therefore, all
+ * changes are recorded in local variables and committed at the end
+ * of the function. Some pcb fields are maintained in local copies:
+ *
+ * queuelen = pcb->snd_queuelen
+ * oversize = pcb->unsent_oversize
+ *
+ * These variables are set consistently by the phases:
+ *
+ * seg points to the last segment tampered with.
+ *
+ * pos records progress as data is segmented.
+ */
+
+ /* Find the tail of the unsent queue. */
+ if (pcb->unsent != NULL) {
+ u16_t space;
+ u16_t unsent_optlen;
+
+ /* @todo: this could be sped up by keeping last_unsent in the pcb */
+ for (last_unsent = pcb->unsent; last_unsent->next != NULL;
+ last_unsent = last_unsent->next);
+
+ /* Usable space at the end of the last unsent segment */
+ unsent_optlen = LWIP_TCP_OPT_LENGTH(last_unsent->flags);
+ space = mss_local - (last_unsent->len + unsent_optlen);
+
+ /*
+ * Phase 1: Copy data directly into an oversized pbuf.
+ *
+ * The number of bytes copied is recorded in the oversize_used
+ * variable. The actual copying is done at the bottom of the
+ * function.
+ */
+#if TCP_OVERSIZE
+#if TCP_OVERSIZE_DBGCHECK
+ /* check that pcb->unsent_oversize matches last_unsent->unsent_oversize */
+ LWIP_ASSERT("unsent_oversize mismatch (pcb vs. last_unsent)",
+ pcb->unsent_oversize == last_unsent->oversize_left);
+#endif /* TCP_OVERSIZE_DBGCHECK */
+ oversize = pcb->unsent_oversize;
+ if (oversize > 0) {
+ LWIP_ASSERT("inconsistent oversize vs. space", oversize_used <= space);
+ seg = last_unsent;
+ oversize_used = oversize < len ? oversize : len;
+ pos += oversize_used;
+ oversize -= oversize_used;
+ space -= oversize_used;
+ }
+ /* now we are either finished or oversize is zero */
+ LWIP_ASSERT("inconsistend oversize vs. len", (oversize == 0) || (pos == len));
+#endif /* TCP_OVERSIZE */
+
+ /*
+ * Phase 2: Chain a new pbuf to the end of pcb->unsent.
+ *
+ * We don't extend segments containing SYN/FIN flags or options
+ * (len==0). The new pbuf is kept in concat_p and pbuf_cat'ed at
+ * the end.
+ */
+ if ((pos < len) && (space > 0) && (last_unsent->len > 0)) {
+ u16_t seglen = space < len - pos ? space : len - pos;
+ seg = last_unsent;
+
+ /* Create a pbuf with a copy or reference to seglen bytes. We
+ * can use PBUF_RAW here since the data appears in the middle of
+ * a segment. A header will never be prepended. */
+ if (apiflags & TCP_WRITE_FLAG_COPY) {
+ /* Data is copied */
+ if ((concat_p = tcp_pbuf_prealloc(PBUF_RAW, seglen, space, &oversize, pcb, apiflags, 1)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
+ ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n",
+ seglen));
+ goto memerr;
+ }
+#if TCP_OVERSIZE_DBGCHECK
+ last_unsent->oversize_left += oversize;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+ TCP_DATA_COPY2(concat_p->payload, (u8_t*)arg + pos, seglen, &concat_chksum, &concat_chksum_swapped);
+#if TCP_CHECKSUM_ON_COPY
+ concat_chksummed += seglen;
+#endif /* TCP_CHECKSUM_ON_COPY */
+ } else {
+ /* Data is not copied */
+ if ((concat_p = pbuf_alloc(PBUF_RAW, seglen, PBUF_ROM)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2,
+ ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+ goto memerr;
+ }
+#if TCP_CHECKSUM_ON_COPY
+ /* calculate the checksum of nocopy-data */
+ tcp_seg_add_chksum(~inet_chksum((u8_t*)arg + pos, seglen), seglen,
+ &concat_chksum, &concat_chksum_swapped);
+ concat_chksummed += seglen;
+#endif /* TCP_CHECKSUM_ON_COPY */
+ /* reference the non-volatile payload data */
+ concat_p->payload = (u8_t*)arg + pos;
+ }
+
+ pos += seglen;
+ queuelen += pbuf_clen(concat_p);
+ }
+ } else {
+#if TCP_OVERSIZE
+ LWIP_ASSERT("unsent_oversize mismatch (pcb->unsent is NULL)",
+ pcb->unsent_oversize == 0);
+#endif /* TCP_OVERSIZE */
+ }
+
+ /*
+ * Phase 3: Create new segments.
+ *
+ * The new segments are chained together in the local 'queue'
+ * variable, ready to be appended to pcb->unsent.
+ */
+ while (pos < len) {
+ struct pbuf *p;
+ u16_t left = len - pos;
+ u16_t max_len = mss_local - optlen;
+ u16_t seglen = left > max_len ? max_len : left;
+#if TCP_CHECKSUM_ON_COPY
+ u16_t chksum = 0;
+ u8_t chksum_swapped = 0;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+ if (apiflags & TCP_WRITE_FLAG_COPY) {
+ /* If copy is set, memory should be allocated and data copied
+ * into pbuf */
+ if ((p = tcp_pbuf_prealloc(PBUF_TRANSPORT, seglen + optlen, mss_local, &oversize, pcb, apiflags, queue == NULL)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write : could not allocate memory for pbuf copy size %"U16_F"\n", seglen));
+ goto memerr;
+ }
+ LWIP_ASSERT("tcp_write: check that first pbuf can hold the complete seglen",
+ (p->len >= seglen));
+ TCP_DATA_COPY2((char *)p->payload + optlen, (u8_t*)arg + pos, seglen, &chksum, &chksum_swapped);
+ } else {
+ /* Copy is not set: First allocate a pbuf for holding the data.
+ * Since the referenced data is available at least until it is
+ * sent out on the link (as it has to be ACKed by the remote
+ * party) we can safely use PBUF_ROM instead of PBUF_REF here.
+ */
+ struct pbuf *p2;
+#if TCP_OVERSIZE
+ LWIP_ASSERT("oversize == 0", oversize == 0);
+#endif /* TCP_OVERSIZE */
+ if ((p2 = pbuf_alloc(PBUF_TRANSPORT, seglen, PBUF_ROM)) == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for zero-copy pbuf\n"));
+ goto memerr;
+ }
+#if TCP_CHECKSUM_ON_COPY
+ /* calculate the checksum of nocopy-data */
+ chksum = ~inet_chksum((u8_t*)arg + pos, seglen);
+#endif /* TCP_CHECKSUM_ON_COPY */
+ /* reference the non-volatile payload data */
+ p2->payload = (u8_t*)arg + pos;
+
+ /* Second, allocate a pbuf for the headers. */
+ if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
+ /* If allocation fails, we have to deallocate the data pbuf as
+ * well. */
+ pbuf_free(p2);
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: could not allocate memory for header pbuf\n"));
+ goto memerr;
+ }
+ /* Concatenate the headers and data pbufs together. */
+ pbuf_cat(p/*header*/, p2/*data*/);
+ }
+
+ queuelen += pbuf_clen(p);
+
+ /* Now that there are more segments queued, we check again if the
+ * length of the queue exceeds the configured maximum or
+ * overflows. */
+ if ((queuelen > TCP_SND_QUEUELEN) || (queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 2, ("tcp_write: queue too long %"U16_F" (%"U16_F")\n", queuelen, TCP_SND_QUEUELEN));
+ pbuf_free(p);
+ goto memerr;
+ }
+
+ if ((seg = tcp_create_segment(pcb, p, 0, pcb->snd_lbb + pos, optflags)) == NULL) {
+ goto memerr;
+ }
+#if TCP_OVERSIZE_DBGCHECK
+ seg->oversize_left = oversize;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+ seg->chksum = chksum;
+ seg->chksum_swapped = chksum_swapped;
+ seg->flags |= TF_SEG_DATA_CHECKSUMMED;
+#endif /* TCP_CHECKSUM_ON_COPY */
+
+ /* first segment of to-be-queued data? */
+ if (queue == NULL) {
+ queue = seg;
+ } else {
+ /* Attach the segment to the end of the queued segments */
+ LWIP_ASSERT("prev_seg != NULL", prev_seg != NULL);
+ prev_seg->next = seg;
+ }
+ /* remember last segment of to-be-queued data for next iteration */
+ prev_seg = seg;
+
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | LWIP_DBG_TRACE, ("tcp_write: queueing %"U32_F":%"U32_F"\n",
+ ntohl(seg->tcphdr->seqno),
+ ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg)));
+
+ pos += seglen;
+ }
+
+ /*
+ * All three segmentation phases were successful. We can commit the
+ * transaction.
+ */
+
+ /*
+ * Phase 1: If data has been added to the preallocated tail of
+ * last_unsent, we update the length fields of the pbuf chain.
+ */
+#if TCP_OVERSIZE
+ if (oversize_used > 0) {
+ struct pbuf *p;
+ /* Bump tot_len of whole chain, len of tail */
+ for (p = last_unsent->p; p; p = p->next) {
+ p->tot_len += oversize_used;
+ if (p->next == NULL) {
+ TCP_DATA_COPY((char *)p->payload + p->len, arg, oversize_used, last_unsent);
+ p->len += oversize_used;
+ }
+ }
+ last_unsent->len += oversize_used;
+#if TCP_OVERSIZE_DBGCHECK
+ LWIP_ASSERT("last_unsent->oversize_left >= oversize_used",
+ last_unsent->oversize_left >= oversize_used);
+ last_unsent->oversize_left -= oversize_used;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+ }
+ pcb->unsent_oversize = oversize;
+#endif /* TCP_OVERSIZE */
+
+ /*
+ * Phase 2: concat_p can be concatenated onto last_unsent->p
+ */
+ if (concat_p != NULL) {
+ LWIP_ASSERT("tcp_write: cannot concatenate when pcb->unsent is empty",
+ (last_unsent != NULL));
+ pbuf_cat(last_unsent->p, concat_p);
+ last_unsent->len += concat_p->tot_len;
+#if TCP_CHECKSUM_ON_COPY
+ if (concat_chksummed) {
+ tcp_seg_add_chksum(concat_chksum, concat_chksummed, &last_unsent->chksum,
+ &last_unsent->chksum_swapped);
+ last_unsent->flags |= TF_SEG_DATA_CHECKSUMMED;
+ }
+#endif /* TCP_CHECKSUM_ON_COPY */
+ }
+
+ /*
+ * Phase 3: Append queue to pcb->unsent. Queue may be NULL, but that
+ * is harmless
+ */
+ if (last_unsent == NULL) {
+ pcb->unsent = queue;
+ } else {
+ last_unsent->next = queue;
+ }
+
+ /*
+ * Finally update the pcb state.
+ */
+ pcb->snd_lbb += len;
+ pcb->snd_buf -= len;
+ pcb->snd_queuelen = queuelen;
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_write: %"S16_F" (after enqueued)\n",
+ pcb->snd_queuelen));
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_write: valid queue length",
+ pcb->unacked != NULL || pcb->unsent != NULL);
+ }
+
+ /* Set the PSH flag in the last segment that we enqueued. */
+ if (seg != NULL && seg->tcphdr != NULL && ((apiflags & TCP_WRITE_FLAG_MORE)==0)) {
+ TCPH_SET_FLAG(seg->tcphdr, TCP_PSH);
+ }
+
+ return ERR_OK;
+memerr:
+ pcb->flags |= TF_NAGLEMEMERR;
+ TCP_STATS_INC(tcp.memerr);
+
+ if (concat_p != NULL) {
+ pbuf_free(concat_p);
+ }
+ if (queue != NULL) {
+ tcp_segs_free(queue);
+ }
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_write: valid queue length", pcb->unacked != NULL ||
+ pcb->unsent != NULL);
+ }
+ LWIP_DEBUGF(TCP_QLEN_DEBUG | LWIP_DBG_STATE, ("tcp_write: %"S16_F" (with mem err)\n", pcb->snd_queuelen));
+ return ERR_MEM;
+}
+
+/**
+ * Enqueue TCP options for transmission.
+ *
+ * Called by tcp_connect(), tcp_listen_input(), and tcp_send_ctrl().
+ *
+ * @param pcb Protocol control block for the TCP connection.
+ * @param flags TCP header flags to set in the outgoing segment.
+ * @param optdata pointer to TCP options, or NULL.
+ * @param optlen length of TCP options in bytes.
+ */
+err_t
+tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags)
+{
+ struct pbuf *p;
+ struct tcp_seg *seg;
+ u8_t optflags = 0;
+ u8_t optlen = 0;
+
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: queuelen: %"U16_F"\n", (u16_t)pcb->snd_queuelen));
+
+ LWIP_ASSERT("tcp_enqueue_flags: need either TCP_SYN or TCP_FIN in flags (programmer violates API)",
+ (flags & (TCP_SYN | TCP_FIN)) != 0);
+
+ /* check for configured max queuelen and possible overflow */
+ if ((pcb->snd_queuelen >= TCP_SND_QUEUELEN) || (pcb->snd_queuelen > TCP_SNDQUEUELEN_OVERFLOW)) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: too long queue %"U16_F" (max %"U16_F")\n",
+ pcb->snd_queuelen, TCP_SND_QUEUELEN));
+ TCP_STATS_INC(tcp.memerr);
+ pcb->flags |= TF_NAGLEMEMERR;
+ return ERR_MEM;
+ }
+
+ if (flags & TCP_SYN) {
+ optflags = TF_SEG_OPTS_MSS;
+ }
+#if LWIP_TCP_TIMESTAMPS
+ if ((pcb->flags & TF_TIMESTAMP)) {
+ optflags |= TF_SEG_OPTS_TS;
+ }
+#endif /* LWIP_TCP_TIMESTAMPS */
+ optlen = LWIP_TCP_OPT_LENGTH(optflags);
+
+ /* tcp_enqueue_flags is always called with either SYN or FIN in flags.
+ * We need one available snd_buf byte to do that.
+ * This means we can't send FIN while snd_buf==0. A better fix would be to
+ * not include SYN and FIN sequence numbers in the snd_buf count. */
+ if (pcb->snd_buf == 0) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG | 3, ("tcp_enqueue_flags: no send buffer available\n"));
+ TCP_STATS_INC(tcp.memerr);
+ return ERR_MEM;
+ }
+
+ /* Allocate pbuf with room for TCP header + options */
+ if ((p = pbuf_alloc(PBUF_TRANSPORT, optlen, PBUF_RAM)) == NULL) {
+ pcb->flags |= TF_NAGLEMEMERR;
+ TCP_STATS_INC(tcp.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("tcp_enqueue_flags: check that first pbuf can hold optlen",
+ (p->len >= optlen));
+
+ /* Allocate memory for tcp_seg, and fill in fields. */
+ if ((seg = tcp_create_segment(pcb, p, flags, pcb->snd_lbb, optflags)) == NULL) {
+ pcb->flags |= TF_NAGLEMEMERR;
+ TCP_STATS_INC(tcp.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("seg->tcphdr not aligned",
+ ((mem_ptr_t)seg->tcphdr % (MEM_ALIGNMENT < 4 ? 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),
+ (u16_t)flags));
+
+ /* Now append seg to pcb->unsent queue */
+ if (pcb->unsent == NULL) {
+ pcb->unsent = seg;
+ } else {
+ struct tcp_seg *useg;
+ for (useg = pcb->unsent; useg->next != NULL; useg = useg->next);
+ useg->next = seg;
+ }
+#if TCP_OVERSIZE
+ /* The new unsent tail has no space */
+ pcb->unsent_oversize = 0;
+#endif /* TCP_OVERSIZE */
+
+ /* SYN and FIN bump the sequence number */
+ if ((flags & TCP_SYN) || (flags & TCP_FIN)) {
+ pcb->snd_lbb++;
+ /* optlen does not influence snd_buf */
+ pcb->snd_buf--;
+ }
+ if (flags & TCP_FIN) {
+ pcb->flags |= TF_FIN;
+ }
+
+ /* update number of segments on the queues */
+ pcb->snd_queuelen += pbuf_clen(seg->p);
+ LWIP_DEBUGF(TCP_QLEN_DEBUG, ("tcp_enqueue_flags: %"S16_F" (after enqueued)\n", pcb->snd_queuelen));
+ if (pcb->snd_queuelen != 0) {
+ LWIP_ASSERT("tcp_enqueue_flags: invalid queue length",
+ pcb->unacked != NULL || pcb->unsent != NULL);
+ }
+
+ return ERR_OK;
+}
+
+#if LWIP_TCP_TIMESTAMPS
+/* Build a timestamp option (12 bytes long) at the specified options pointer)
+ *
+ * @param pcb tcp_pcb
+ * @param opts option pointer where to store the timestamp option
+ */
+static void
+tcp_build_timestamp_option(struct tcp_pcb *pcb, u32_t *opts)
+{
+ /* Pad with two NOP options to make everything nicely aligned */
+ opts[0] = PP_HTONL(0x0101080A);
+ opts[1] = htonl(sys_now());
+ opts[2] = htonl(pcb->ts_recent);
+}
+#endif
+
+/** Send an ACK without data.
+ *
+ * @param pcb Protocol control block for the TCP connection to send the ACK
+ */
+err_t
+tcp_send_empty_ack(struct tcp_pcb *pcb)
+{
+ struct pbuf *p;
+ u8_t optlen = 0;
+#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP
+ struct tcp_hdr *tcphdr;
+#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */
+
+#if LWIP_TCP_TIMESTAMPS
+ if (pcb->flags & TF_TIMESTAMP) {
+ optlen = LWIP_TCP_OPT_LENGTH(TF_SEG_OPTS_TS);
+ }
+#endif
+
+ p = tcp_output_alloc_header(pcb, optlen, 0, htonl(pcb->snd_nxt));
+ if (p == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: (ACK) could not allocate pbuf\n"));
+ return ERR_BUF;
+ }
+#if LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP
+ tcphdr = (struct tcp_hdr *)p->payload;
+#endif /* LWIP_TCP_TIMESTAMPS || CHECKSUM_GEN_TCP */
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG,
+ ("tcp_output: sending ACK for %"U32_F"\n", pcb->rcv_nxt));
+ /* remove ACK flags from the PCB, as we send an empty ACK now */
+ pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+
+ /* NB. MSS option is only sent on SYNs, so ignore it here */
+#if LWIP_TCP_TIMESTAMPS
+ pcb->ts_lastacksent = pcb->rcv_nxt;
+
+ if (pcb->flags & TF_TIMESTAMP) {
+ tcp_build_timestamp_option(pcb, (u32_t *)(tcphdr + 1));
+ }
+#endif
+
+#if CHECKSUM_GEN_TCP
+ tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len,
+ &pcb->local_ip, &pcb->remote_ip);
+#endif
+#if LWIP_NETIF_HWADDRHINT
+ ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos,
+ IP_PROTO_TCP, &pcb->addr_hint);
+#else /* LWIP_NETIF_HWADDRHINT*/
+ ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, pcb->tos,
+ IP_PROTO_TCP);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+ pbuf_free(p);
+
+ return ERR_OK;
+}
+
+/**
+ * Find out what we can send and send it
+ *
+ * @param pcb Protocol control block for the TCP connection to send data
+ * @return ERR_OK if data has been sent or nothing to send
+ * another err_t on error
+ */
+err_t
+tcp_output(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *seg, *useg;
+ u32_t wnd, snd_nxt;
+#if TCP_CWND_DEBUG
+ s16_t i = 0;
+#endif /* TCP_CWND_DEBUG */
+
+ /* pcb->state LISTEN not allowed here */
+ LWIP_ASSERT("don't call tcp_output for listen-pcbs",
+ pcb->state != LISTEN);
+
+ /* First, check if we are invoked by the TCP input processing
+ code. If so, we do not output anything. Instead, we rely on the
+ input processing code to call us when input processing is done
+ with. */
+ if (tcp_input_pcb == pcb) {
+ return ERR_OK;
+ }
+
+ wnd = LWIP_MIN(pcb->snd_wnd, pcb->cwnd);
+
+ seg = pcb->unsent;
+
+ /* If the TF_ACK_NOW flag is set and no data will be sent (either
+ * because the ->unsent queue is empty or because the window does
+ * not allow it), construct an empty ACK segment and send it.
+ *
+ * If data is to be sent, we will just piggyback the ACK (see below).
+ */
+ if (pcb->flags & TF_ACK_NOW &&
+ (seg == NULL ||
+ ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len > wnd)) {
+ return tcp_send_empty_ack(pcb);
+ }
+
+ /* useg should point to last segment on unacked queue */
+ useg = pcb->unacked;
+ if (useg != NULL) {
+ for (; useg->next != NULL; useg = useg->next);
+ }
+
+#if TCP_OUTPUT_DEBUG
+ if (seg == NULL) {
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output: nothing to send (%p)\n",
+ (void*)pcb->unsent));
+ }
+#endif /* TCP_OUTPUT_DEBUG */
+#if TCP_CWND_DEBUG
+ if (seg == NULL) {
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F
+ ", cwnd %"U16_F", wnd %"U32_F
+ ", seg == NULL, ack %"U32_F"\n",
+ pcb->snd_wnd, pcb->cwnd, wnd, pcb->lastack));
+ } else {
+ LWIP_DEBUGF(TCP_CWND_DEBUG,
+ ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F
+ ", effwnd %"U32_F", seq %"U32_F", ack %"U32_F"\n",
+ pcb->snd_wnd, pcb->cwnd, wnd,
+ ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len,
+ ntohl(seg->tcphdr->seqno), pcb->lastack));
+ }
+#endif /* TCP_CWND_DEBUG */
+ /* data available and window allows it to be sent? */
+ while (seg != NULL &&
+ ntohl(seg->tcphdr->seqno) - pcb->lastack + seg->len <= wnd) {
+ LWIP_ASSERT("RST not expected here!",
+ (TCPH_FLAGS(seg->tcphdr) & TCP_RST) == 0);
+ /* Stop sending if the nagle algorithm would prevent it
+ * Don't stop:
+ * - if tcp_write had a memory error before (prevent delayed ACK timeout) or
+ * - if FIN was already enqueued for this PCB (SYN is always alone in a segment -
+ * either seg->next != NULL or pcb->unacked == NULL;
+ * RST is no sent using tcp_write/tcp_output.
+ */
+ if((tcp_do_output_nagle(pcb) == 0) &&
+ ((pcb->flags & (TF_NAGLEMEMERR | TF_FIN)) == 0)){
+ break;
+ }
+#if TCP_CWND_DEBUG
+ LWIP_DEBUGF(TCP_CWND_DEBUG, ("tcp_output: snd_wnd %"U16_F", cwnd %"U16_F", wnd %"U32_F", effwnd %"U32_F", seq %"U32_F", ack %"U32_F", i %"S16_F"\n",
+ pcb->snd_wnd, pcb->cwnd, wnd,
+ ntohl(seg->tcphdr->seqno) + seg->len -
+ pcb->lastack,
+ ntohl(seg->tcphdr->seqno), pcb->lastack, i));
+ ++i;
+#endif /* TCP_CWND_DEBUG */
+
+ pcb->unsent = seg->next;
+
+ if (pcb->state != SYN_SENT) {
+ TCPH_SET_FLAG(seg->tcphdr, TCP_ACK);
+ pcb->flags &= ~(TF_ACK_DELAY | TF_ACK_NOW);
+ }
+
+#if TCP_OVERSIZE_DBGCHECK
+ seg->oversize_left = 0;
+#endif /* TCP_OVERSIZE_DBGCHECK */
+ tcp_output_segment(seg, pcb);
+ snd_nxt = ntohl(seg->tcphdr->seqno) + TCP_TCPLEN(seg);
+ if (TCP_SEQ_LT(pcb->snd_nxt, snd_nxt)) {
+ pcb->snd_nxt = snd_nxt;
+ }
+ /* put segment on unacknowledged list if length > 0 */
+ if (TCP_TCPLEN(seg) > 0) {
+ seg->next = NULL;
+ /* unacked list is empty? */
+ if (pcb->unacked == NULL) {
+ pcb->unacked = seg;
+ useg = seg;
+ /* unacked list is not empty? */
+ } else {
+ /* In the case of fast retransmit, the packet should not go to the tail
+ * of the unacked queue, but rather somewhere before it. We need to check for
+ * this case. -STJ Jul 27, 2004 */
+ if (TCP_SEQ_LT(ntohl(seg->tcphdr->seqno), ntohl(useg->tcphdr->seqno))) {
+ /* add segment to before tail of unacked list, keeping the list sorted */
+ struct tcp_seg **cur_seg = &(pcb->unacked);
+ while (*cur_seg &&
+ TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) {
+ cur_seg = &((*cur_seg)->next );
+ }
+ seg->next = (*cur_seg);
+ (*cur_seg) = seg;
+ } else {
+ /* add segment to tail of unacked list */
+ useg->next = seg;
+ useg = useg->next;
+ }
+ }
+ /* do not queue empty segments on the unacked list */
+ } else {
+ tcp_seg_free(seg);
+ }
+ seg = pcb->unsent;
+ }
+#if TCP_OVERSIZE
+ if (pcb->unsent == NULL) {
+ /* last unsent has been removed, reset unsent_oversize */
+ pcb->unsent_oversize = 0;
+ }
+#endif /* TCP_OVERSIZE */
+
+ pcb->flags &= ~TF_NAGLEMEMERR;
+ return ERR_OK;
+}
+
+/**
+ * Called by tcp_output() to actually send a TCP segment over IP.
+ *
+ * @param seg the tcp_seg to send
+ * @param pcb the tcp_pcb for the TCP connection used to send the segment
+ */
+static void
+tcp_output_segment(struct tcp_seg *seg, struct tcp_pcb *pcb)
+{
+ u16_t len;
+ u32_t *opts;
+
+ /** @bug Exclude retransmitted segments from this count. */
+ snmp_inc_tcpoutsegs();
+
+ /* The TCP header has already been constructed, but the ackno and
+ wnd fields remain. */
+ seg->tcphdr->ackno = htonl(pcb->rcv_nxt);
+
+ /* advertise our receive window size in this TCP segment */
+ seg->tcphdr->wnd = htons(pcb->rcv_ann_wnd);
+
+ pcb->rcv_ann_right_edge = pcb->rcv_nxt + pcb->rcv_ann_wnd;
+
+ /* Add any requested options. NB MSS option is only set on SYN
+ packets, so ignore it here */
+ opts = (u32_t *)(void *)(seg->tcphdr + 1);
+ if (seg->flags & TF_SEG_OPTS_MSS) {
+ u16_t mss;
+#if TCP_CALCULATE_EFF_SEND_MSS
+ mss = tcp_eff_send_mss(TCP_MSS, &pcb->local_ip, &pcb->remote_ip, PCB_ISIPV6(pcb));
+#else /* TCP_CALCULATE_EFF_SEND_MSS */
+ mss = TCP_MSS;
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+ *opts = TCP_BUILD_MSS_OPTION(mss);
+ opts += 1;
+ }
+#if LWIP_TCP_TIMESTAMPS
+ pcb->ts_lastacksent = pcb->rcv_nxt;
+
+ if (seg->flags & TF_SEG_OPTS_TS) {
+ tcp_build_timestamp_option(pcb, opts);
+ opts += 3;
+ }
+#endif
+
+ /* Set retransmission timer running if it is not currently enabled
+ This must be set before checking the route. */
+ if (pcb->rtime == -1) {
+ pcb->rtime = 0;
+ }
+
+ /* If we don't have a local IP address, we get one by
+ calling ip_route(). */
+ if (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->local_ip)) {
+ struct netif *netif;
+ ipX_addr_t *local_ip;
+ ipX_route_get_local_ipX(PCB_ISIPV6(pcb), &pcb->local_ip, &pcb->remote_ip, netif, local_ip);
+ if ((netif == NULL) || (local_ip == NULL)) {
+ return;
+ }
+ ipX_addr_copy(PCB_ISIPV6(pcb), pcb->local_ip, *local_ip);
+ }
+
+ if (pcb->rttest == 0) {
+ pcb->rttest = tcp_ticks;
+ pcb->rtseq = ntohl(seg->tcphdr->seqno);
+
+ LWIP_DEBUGF(TCP_RTO_DEBUG, ("tcp_output_segment: rtseq %"U32_F"\n", pcb->rtseq));
+ }
+ LWIP_DEBUGF(TCP_OUTPUT_DEBUG, ("tcp_output_segment: %"U32_F":%"U32_F"\n",
+ htonl(seg->tcphdr->seqno), htonl(seg->tcphdr->seqno) +
+ seg->len));
+
+ len = (u16_t)((u8_t *)seg->tcphdr - (u8_t *)seg->p->payload);
+
+ seg->p->len -= len;
+ seg->p->tot_len -= len;
+
+ seg->p->payload = seg->tcphdr;
+
+ seg->tcphdr->chksum = 0;
+#if TCP_CHECKSUM_ON_COPY
+ {
+ u32_t acc;
+#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+ u16_t chksum_slow = ipX_chksum_pseudo(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP,
+ seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip);
+#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
+ if ((seg->flags & TF_SEG_DATA_CHECKSUMMED) == 0) {
+ LWIP_ASSERT("data included but not checksummed",
+ seg->p->tot_len == (TCPH_HDRLEN(seg->tcphdr) * 4));
+ }
+
+ /* rebuild TCP header checksum (TCP header changes for retransmissions!) */
+ acc = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP,
+ seg->p->tot_len, TCPH_HDRLEN(seg->tcphdr) * 4, &pcb->local_ip, &pcb->remote_ip);
+ /* add payload checksum */
+ if (seg->chksum_swapped) {
+ seg->chksum = SWAP_BYTES_IN_WORD(seg->chksum);
+ seg->chksum_swapped = 0;
+ }
+ acc += (u16_t)~(seg->chksum);
+ seg->tcphdr->chksum = FOLD_U32T(acc);
+#if TCP_CHECKSUM_ON_COPY_SANITY_CHECK
+ if (chksum_slow != seg->tcphdr->chksum) {
+ LWIP_DEBUGF(TCP_DEBUG | LWIP_DBG_LEVEL_WARNING,
+ ("tcp_output_segment: calculated checksum is %"X16_F" instead of %"X16_F"\n",
+ seg->tcphdr->chksum, chksum_slow));
+ seg->tcphdr->chksum = chksum_slow;
+ }
+#endif /* TCP_CHECKSUM_ON_COPY_SANITY_CHECK */
+ }
+#else /* TCP_CHECKSUM_ON_COPY */
+#if CHECKSUM_GEN_TCP
+ seg->tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), seg->p, IP_PROTO_TCP,
+ seg->p->tot_len, &pcb->local_ip, &pcb->remote_ip);
+#endif /* CHECKSUM_GEN_TCP */
+#endif /* TCP_CHECKSUM_ON_COPY */
+ TCP_STATS_INC(tcp.xmit);
+
+#if LWIP_NETIF_HWADDRHINT
+ ipX_output_hinted(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip,
+ pcb->ttl, pcb->tos, IP_PROTO_TCP, &pcb->addr_hint);
+#else /* LWIP_NETIF_HWADDRHINT*/
+ ipX_output(PCB_ISIPV6(pcb), seg->p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
+ pcb->tos, IP_PROTO_TCP);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+}
+
+/**
+ * Send a TCP RESET packet (empty segment with RST flag set) either to
+ * abort a connection or to show that there is no matching local connection
+ * for a received segment.
+ *
+ * Called by tcp_abort() (to abort a local connection), tcp_input() (if no
+ * matching local pcb was found), tcp_listen_input() (if incoming segment
+ * has ACK flag set) and tcp_process() (received segment in the wrong state)
+ *
+ * Since a RST segment is in most cases not sent for an active connection,
+ * tcp_rst() has a number of arguments that are taken from a tcp_pcb for
+ * most other segment output functions.
+ *
+ * @param seqno the sequence number to use for the outgoing segment
+ * @param ackno the acknowledge number to use for the outgoing segment
+ * @param local_ip the local IP address to send the segment from
+ * @param remote_ip the remote IP address to send the segment to
+ * @param local_port the local TCP port to send the segment from
+ * @param remote_port the remote TCP port to send the segment to
+ */
+void
+tcp_rst_impl(u32_t seqno, u32_t ackno,
+ ipX_addr_t *local_ip, ipX_addr_t *remote_ip,
+ u16_t local_port, u16_t remote_port
+#if LWIP_IPV6
+ , u8_t isipv6
+#endif /* LWIP_IPV6 */
+ )
+{
+ struct pbuf *p;
+ struct tcp_hdr *tcphdr;
+ p = pbuf_alloc(PBUF_IP, TCP_HLEN, PBUF_RAM);
+ if (p == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_rst: could not allocate memory for pbuf\n"));
+ return;
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct tcp_hdr",
+ (p->len >= sizeof(struct tcp_hdr)));
+
+ tcphdr = (struct tcp_hdr *)p->payload;
+ tcphdr->src = htons(local_port);
+ tcphdr->dest = htons(remote_port);
+ tcphdr->seqno = htonl(seqno);
+ tcphdr->ackno = htonl(ackno);
+ TCPH_HDRLEN_FLAGS_SET(tcphdr, TCP_HLEN/4, TCP_RST | TCP_ACK);
+ tcphdr->wnd = PP_HTONS(TCP_WND);
+ tcphdr->chksum = 0;
+ tcphdr->urgp = 0;
+
+ TCP_STATS_INC(tcp.xmit);
+ snmp_inc_tcpoutrsts();
+
+#if CHECKSUM_GEN_TCP
+ tcphdr->chksum = ipX_chksum_pseudo(isipv6, p, IP_PROTO_TCP, p->tot_len,
+ local_ip, remote_ip);
+#endif
+ /* Send output with hardcoded TTL/HL since we have no access to the pcb */
+ ipX_output(isipv6, p, local_ip, remote_ip, TCP_TTL, 0, IP_PROTO_TCP);
+ pbuf_free(p);
+ LWIP_DEBUGF(TCP_RST_DEBUG, ("tcp_rst: seqno %"U32_F" ackno %"U32_F".\n", seqno, ackno));
+}
+
+/**
+ * Requeue all unacked segments for retransmission
+ *
+ * Called by tcp_slowtmr() for slow retransmission.
+ *
+ * @param pcb the tcp_pcb for which to re-enqueue all unacked segments
+ */
+void
+tcp_rexmit_rto(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *seg;
+
+ if (pcb->unacked == NULL) {
+ return;
+ }
+
+ /* Move all unacked segments to the head of the unsent queue */
+ for (seg = pcb->unacked; seg->next != NULL; seg = seg->next);
+ /* concatenate unsent queue after unacked queue */
+ seg->next = pcb->unsent;
+#if TCP_OVERSIZE && 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 && TCP_OVERSIZE_DBGCHECK*/
+ /* unsent queue is the concatenated queue (of unacked, unsent) */
+ pcb->unsent = pcb->unacked;
+ /* unacked queue is now empty */
+ pcb->unacked = NULL;
+
+ /* increment number of retransmissions */
+ ++pcb->nrtx;
+
+ /* Don't take any RTT measurements after retransmitting. */
+ pcb->rttest = 0;
+
+ /* Do the actual retransmission */
+ tcp_output(pcb);
+}
+
+/**
+ * Requeue the first unacked segment for retransmission
+ *
+ * Called by tcp_receive() for fast retramsmit.
+ *
+ * @param pcb the tcp_pcb for which to retransmit the first unacked segment
+ */
+void
+tcp_rexmit(struct tcp_pcb *pcb)
+{
+ struct tcp_seg *seg;
+ struct tcp_seg **cur_seg;
+
+ if (pcb->unacked == NULL) {
+ return;
+ }
+
+ /* Move the first unacked segment to the unsent queue */
+ /* Keep the unsent queue sorted. */
+ seg = pcb->unacked;
+ pcb->unacked = seg->next;
+
+ cur_seg = &(pcb->unsent);
+ while (*cur_seg &&
+ TCP_SEQ_LT(ntohl((*cur_seg)->tcphdr->seqno), ntohl(seg->tcphdr->seqno))) {
+ cur_seg = &((*cur_seg)->next );
+ }
+ seg->next = *cur_seg;
+ *cur_seg = seg;
+#if TCP_OVERSIZE
+ if (seg->next == NULL) {
+ /* the retransmitted segment is last in unsent, so reset unsent_oversize */
+ pcb->unsent_oversize = 0;
+ }
+#endif /* TCP_OVERSIZE */
+
+ ++pcb->nrtx;
+
+ /* Don't take any rtt measurements after retransmitting. */
+ pcb->rttest = 0;
+
+ /* Do the actual retransmission. */
+ snmp_inc_tcpretranssegs();
+ /* No need to call tcp_output: we are always called from tcp_input()
+ and thus tcp_output directly returns. */
+}
+
+
+/**
+ * Handle retransmission after three dupacks received
+ *
+ * @param pcb the tcp_pcb for which to retransmit the first unacked segment
+ */
+void
+tcp_rexmit_fast(struct tcp_pcb *pcb)
+{
+ if (pcb->unacked != NULL && !(pcb->flags & TF_INFR)) {
+ /* This is fast retransmit. Retransmit the first unacked segment. */
+ LWIP_DEBUGF(TCP_FR_DEBUG,
+ ("tcp_receive: dupacks %"U16_F" (%"U32_F
+ "), fast retransmit %"U32_F"\n",
+ (u16_t)pcb->dupacks, pcb->lastack,
+ ntohl(pcb->unacked->tcphdr->seqno)));
+ tcp_rexmit(pcb);
+
+ /* Set ssthresh to half of the minimum of the current
+ * cwnd and the advertised window */
+ if (pcb->cwnd > pcb->snd_wnd) {
+ pcb->ssthresh = pcb->snd_wnd / 2;
+ } else {
+ pcb->ssthresh = pcb->cwnd / 2;
+ }
+
+ /* The minimum value for ssthresh should be 2 MSS */
+ if (pcb->ssthresh < 2*pcb->mss) {
+ LWIP_DEBUGF(TCP_FR_DEBUG,
+ ("tcp_receive: The minimum value for ssthresh %"U16_F
+ " should be min 2 mss %"U16_F"...\n",
+ pcb->ssthresh, 2*pcb->mss));
+ pcb->ssthresh = 2*pcb->mss;
+ }
+
+ pcb->cwnd = pcb->ssthresh + 3 * pcb->mss;
+ pcb->flags |= TF_INFR;
+ }
+}
+
+
+/**
+ * Send keepalive packets to keep a connection active although
+ * no data is sent over it.
+ *
+ * Called by tcp_slowtmr()
+ *
+ * @param pcb the tcp_pcb for which to send a keepalive packet
+ */
+void
+tcp_keepalive(struct tcp_pcb *pcb)
+{
+ struct pbuf *p;
+#if CHECKSUM_GEN_TCP
+ struct tcp_hdr *tcphdr;
+#endif /* CHECKSUM_GEN_TCP */
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: sending KEEPALIVE probe to "));
+ ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip);
+ LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: tcp_ticks %"U32_F" pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
+ tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
+
+ p = tcp_output_alloc_header(pcb, 0, 0, htonl(pcb->snd_nxt - 1));
+ if(p == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG,
+ ("tcp_keepalive: could not allocate memory for pbuf\n"));
+ return;
+ }
+#if CHECKSUM_GEN_TCP
+ tcphdr = (struct tcp_hdr *)p->payload;
+
+ tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len,
+ &pcb->local_ip, &pcb->remote_ip);
+#endif /* CHECKSUM_GEN_TCP */
+ TCP_STATS_INC(tcp.xmit);
+
+ /* Send output to IP */
+#if LWIP_NETIF_HWADDRHINT
+ ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip,
+ pcb->ttl, 0, IP_PROTO_TCP, &pcb->addr_hint);
+#else /* LWIP_NETIF_HWADDRHINT*/
+ ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
+ 0, IP_PROTO_TCP);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+ pbuf_free(p);
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_keepalive: seqno %"U32_F" ackno %"U32_F".\n",
+ pcb->snd_nxt - 1, pcb->rcv_nxt));
+}
+
+
+/**
+ * Send persist timer zero-window probes to keep a connection active
+ * when a window update is lost.
+ *
+ * Called by tcp_slowtmr()
+ *
+ * @param pcb the tcp_pcb for which to send a zero-window probe packet
+ */
+void
+tcp_zero_window_probe(struct tcp_pcb *pcb)
+{
+ struct pbuf *p;
+ struct tcp_hdr *tcphdr;
+ struct tcp_seg *seg;
+ u16_t len;
+ u8_t is_fin;
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: sending ZERO WINDOW probe to "));
+ ipX_addr_debug_print(PCB_ISIPV6(pcb), TCP_DEBUG, &pcb->remote_ip);
+ LWIP_DEBUGF(TCP_DEBUG, ("\n"));
+
+ LWIP_DEBUGF(TCP_DEBUG,
+ ("tcp_zero_window_probe: tcp_ticks %"U32_F
+ " pcb->tmr %"U32_F" pcb->keep_cnt_sent %"U16_F"\n",
+ tcp_ticks, pcb->tmr, pcb->keep_cnt_sent));
+
+ seg = pcb->unacked;
+
+ if(seg == NULL) {
+ seg = pcb->unsent;
+ }
+ if(seg == NULL) {
+ return;
+ }
+
+ is_fin = ((TCPH_FLAGS(seg->tcphdr) & TCP_FIN) != 0) && (seg->len == 0);
+ /* we want to send one seqno: either FIN or data (no options) */
+ len = is_fin ? 0 : 1;
+
+ p = tcp_output_alloc_header(pcb, 0, len, seg->tcphdr->seqno);
+ if(p == NULL) {
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: no memory for pbuf\n"));
+ return;
+ }
+ tcphdr = (struct tcp_hdr *)p->payload;
+
+ if (is_fin) {
+ /* FIN segment, no data */
+ TCPH_FLAGS_SET(tcphdr, TCP_ACK | TCP_FIN);
+ } else {
+ /* Data segment, copy in one byte from the head of the unacked queue */
+ char *d = ((char *)p->payload + TCP_HLEN);
+ /* Depending on whether the segment has already been sent (unacked) or not
+ (unsent), seg->p->payload points to the IP header or TCP header.
+ Ensure we copy the first TCP data byte: */
+ pbuf_copy_partial(seg->p, d, 1, seg->p->tot_len - seg->len);
+ }
+
+#if CHECKSUM_GEN_TCP
+ tcphdr->chksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), p, IP_PROTO_TCP, p->tot_len,
+ &pcb->local_ip, &pcb->remote_ip);
+#endif
+ TCP_STATS_INC(tcp.xmit);
+
+ /* Send output to IP */
+#if LWIP_NETIF_HWADDRHINT
+ ipX_output_hinted(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl,
+ 0, IP_PROTO_TCP, &pcb->addr_hint);
+#else /* LWIP_NETIF_HWADDRHINT*/
+ ipX_output(PCB_ISIPV6(pcb), p, &pcb->local_ip, &pcb->remote_ip, pcb->ttl, 0, IP_PROTO_TCP);
+#endif /* LWIP_NETIF_HWADDRHINT*/
+
+ pbuf_free(p);
+
+ LWIP_DEBUGF(TCP_DEBUG, ("tcp_zero_window_probe: seqno %"U32_F
+ " ackno %"U32_F".\n",
+ pcb->snd_nxt - 1, pcb->rcv_nxt));
+}
+#endif /* LWIP_TCP */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/timers.c b/src/VBox/Devices/Network/lwip-new/src/core/timers.c
new file mode 100644
index 00000000..601a905f
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/timers.c
@@ -0,0 +1,606 @@
+/**
+ * @file
+ * Stack-internal timers implementation.
+ * This file includes timer callbacks for stack-internal timers as well as
+ * functions to set up or stop timers and check for expired timers.
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ * Simon Goldschmidt
+ *
+ */
+
+#include "lwip/opt.h"
+
+#include "lwip/timers.h"
+#include "lwip/tcp_impl.h"
+
+#if LWIP_TIMERS
+
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/tcpip.h"
+#if LWIP_CONNECTION_PROXY
+#include "lwip/udp.h"
+#endif
+
+#include "lwip/ip_frag.h"
+#include "netif/etharp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "lwip/igmp.h"
+#include "lwip/dns.h"
+#include "lwip/nd6.h"
+#include "lwip/ip6_frag.h"
+#include "lwip/mld6.h"
+#include "lwip/sys.h"
+#include "lwip/pbuf.h"
+
+/** The one and only timeout list */
+static struct sys_timeo *next_timeout;
+#if NO_SYS
+static u32_t timeouts_last_time;
+#endif /* NO_SYS */
+
+#if LWIP_TCP
+/** global variable that shows if the tcp timer is currently scheduled or not */
+static int tcpip_tcp_timer_active;
+#endif /* LWIP_TCP */
+
+#if LWIP_CONNECTION_PROXY
+/** global variable that shows if proxy timer is currently scheduled or not */
+static int proxy_udp_timer_active;
+#endif /* LWIP_CONNECTION_PROXY */
+
+
+#if LWIP_TCP
+/**
+ * Timer callback function that calls tcp_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+tcpip_tcp_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ /* call TCP timer handler */
+ tcp_tmr();
+ /* timer still needed? */
+ if (tcp_active_pcbs || tcp_tw_pcbs) {
+ /* restart timer */
+ sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
+ } else {
+ /* disable timer */
+ tcpip_tcp_timer_active = 0;
+ }
+}
+
+/**
+ * Called from TCP_REG when registering a new PCB:
+ * the reason is to have the TCP timer only running when
+ * there are active (or time-wait) PCBs.
+ */
+void
+tcp_timer_needed(void)
+{
+ /* timer is off but needed again? */
+ if (!tcpip_tcp_timer_active && (tcp_active_pcbs || tcp_tw_pcbs)) {
+ /* enable and start timer */
+ tcpip_tcp_timer_active = 1;
+ sys_timeout(TCP_TMR_INTERVAL, tcpip_tcp_timer, NULL);
+ }
+}
+#endif /* LWIP_TCP */
+
+#if LWIP_CONNECTION_PROXY
+static void
+proxy_udp_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ /* call proxy timer handler */
+ udp_proxy_tmr();
+
+ /* timer still needed? */
+ if (udp_proxy_pcbs) {
+ /* restart timer */
+ sys_timeout(UDP_PROXY_TMR_INTERVAL, proxy_udp_timer, NULL);
+ } else {
+ /* disable timer */
+ proxy_udp_timer_active = 0;
+ }
+}
+
+void
+udp_proxy_timer_needed(void)
+{
+ /* timer is off but needed again? */
+ if (!proxy_udp_timer_active && udp_proxy_pcbs) {
+ /* enable and start timer */
+ proxy_udp_timer_active = 1;
+ sys_timeout(UDP_PROXY_TMR_INTERVAL, proxy_udp_timer, NULL);
+ }
+}
+#endif /* LWIP_CONNECTION_PROXY */
+
+#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.
+ *
+ * @param arg unused argument
+ */
+static void
+dhcp_timer_coarse(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_coarse_tmr()\n"));
+ dhcp_coarse_tmr();
+ sys_timeout(DHCP_COARSE_TIMER_MSECS, dhcp_timer_coarse, NULL);
+}
+
+/**
+ * Timer callback function that calls dhcp_fine_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+dhcp_timer_fine(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dhcp_fine_tmr()\n"));
+ dhcp_fine_tmr();
+ sys_timeout(DHCP_FINE_TIMER_MSECS, dhcp_timer_fine, NULL);
+}
+#endif /* LWIP_DHCP */
+
+#if LWIP_AUTOIP
+/**
+ * Timer callback function that calls autoip_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+autoip_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: autoip_tmr()\n"));
+ autoip_tmr();
+ sys_timeout(AUTOIP_TMR_INTERVAL, autoip_timer, NULL);
+}
+#endif /* LWIP_AUTOIP */
+
+#if LWIP_IGMP
+/**
+ * Timer callback function that calls igmp_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+igmp_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: igmp_tmr()\n"));
+ igmp_tmr();
+ sys_timeout(IGMP_TMR_INTERVAL, igmp_timer, NULL);
+}
+#endif /* LWIP_IGMP */
+
+#if LWIP_DNS
+/**
+ * Timer callback function that calls dns_tmr() and reschedules itself.
+ *
+ * @param arg unused argument
+ */
+static void
+dns_timer(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+ LWIP_DEBUGF(TIMERS_DEBUG, ("tcpip: dns_tmr()\n"));
+ dns_tmr();
+ sys_timeout(DNS_TMR_INTERVAL, dns_timer, NULL);
+}
+#endif /* LWIP_DNS */
+
+#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);
+}
+#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 */
+
+#if NO_SYS
+ /* Initialise timestamp for sys_check_timeouts */
+ timeouts_last_time = sys_now();
+#endif
+}
+
+/**
+ * Create a one-shot timer (aka timeout). Timeouts are processed in the
+ * following cases:
+ * - while waiting for a message using sys_timeouts_mbox_fetch()
+ * - by calling sys_check_timeouts() (NO_SYS==1 only)
+ *
+ * @param msecs time in milliseconds after that the timer should expire
+ * @param handler callback function to call when msecs have elapsed
+ * @param arg argument to pass to the callback function
+ */
+#if LWIP_DEBUG_TIMERNAMES
+void
+sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name)
+#else /* LWIP_DEBUG_TIMERNAMES */
+void
+sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg)
+#endif /* LWIP_DEBUG_TIMERNAMES */
+{
+ struct sys_timeo *timeout, *t;
+#if NO_SYS
+ u32_t now, diff;
+#endif
+
+ 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;
+ }
+
+#if NO_SYS
+ now = sys_now();
+ if (next_timeout == NULL) {
+ diff = 0;
+ timeouts_last_time = now;
+ } else {
+ diff = now - timeouts_last_time;
+ }
+#endif
+
+ timeout->next = NULL;
+ timeout->h = handler;
+ timeout->arg = arg;
+#if NO_SYS
+ timeout->time = msecs + diff;
+#else
+ timeout->time = msecs;
+#endif
+#if LWIP_DEBUG_TIMERNAMES
+ timeout->handler_name = handler_name;
+ LWIP_DEBUGF(TIMERS_DEBUG, ("sys_timeout: %p msecs=%"U32_F" handler=%s arg=%p\n",
+ (void *)timeout, msecs, handler_name, (void *)arg));
+#endif /* LWIP_DEBUG_TIMERNAMES */
+
+ if (next_timeout == NULL) {
+ next_timeout = timeout;
+ return;
+ }
+
+ if (next_timeout->time > msecs) {
+ next_timeout->time -= msecs;
+ timeout->next = next_timeout;
+ next_timeout = timeout;
+ } else {
+ for(t = next_timeout; t != NULL; t = t->next) {
+ timeout->time -= t->time;
+ if (t->next == NULL || t->next->time > timeout->time) {
+ if (t->next != NULL) {
+ t->next->time -= timeout->time;
+ }
+ timeout->next = t->next;
+ t->next = timeout;
+ break;
+ }
+ }
+ }
+}
+
+/**
+ * Go through timeout list (for this task only) and remove the first matching
+ * entry, even though the timeout has not triggered yet.
+ *
+ * @note This function only works as expected if there is only one timeout
+ * calling 'handler' in the list of timeouts.
+ *
+ * @param handler callback function that would be called by the timeout
+ * @param arg callback argument that would be passed to handler
+*/
+void
+sys_untimeout(sys_timeout_handler handler, void *arg)
+{
+ struct sys_timeo *prev_t, *t;
+
+ if (next_timeout == NULL) {
+ return;
+ }
+
+ for (t = next_timeout, prev_t = NULL; t != NULL; prev_t = t, t = t->next) {
+ if ((t->h == handler) && (t->arg == arg)) {
+ /* We have a match */
+ /* Unlink from previous in list */
+ if (prev_t == NULL) {
+ next_timeout = t->next;
+ } else {
+ prev_t->next = t->next;
+ }
+ /* If not the last one, add time of this one back to next */
+ if (t->next != NULL) {
+ t->next->time += t->time;
+ }
+ memp_free(MEMP_SYS_TIMEOUT, t);
+ return;
+ }
+ }
+ return;
+}
+
+#if NO_SYS
+
+/** Handle timeouts for NO_SYS==1 (i.e. without using
+ * tcpip_thread/sys_timeouts_mbox_fetch(). Uses sys_now() to call timeout
+ * handler functions when timeouts expire.
+ *
+ * Must be called periodically from your main loop.
+ */
+void
+sys_check_timeouts(void)
+{
+ if (next_timeout) {
+ struct sys_timeo *tmptimeout;
+ u32_t diff;
+ sys_timeout_handler handler;
+ void *arg;
+ u8_t had_one;
+ u32_t now;
+
+ now = sys_now();
+ /* this cares for wraparounds */
+ diff = now - timeouts_last_time;
+ do
+ {
+#if PBUF_POOL_FREE_OOSEQ
+ PBUF_CHECK_FREE_OOSEQ();
+#endif /* PBUF_POOL_FREE_OOSEQ */
+ had_one = 0;
+ tmptimeout = next_timeout;
+ if (tmptimeout && (tmptimeout->time <= diff)) {
+ /* timeout has expired */
+ had_one = 1;
+ timeouts_last_time += tmptimeout->time;
+ diff -= tmptimeout->time;
+ next_timeout = tmptimeout->next;
+ handler = tmptimeout->h;
+ arg = tmptimeout->arg;
+#if LWIP_DEBUG_TIMERNAMES
+ if (handler != NULL) {
+ LWIP_DEBUGF(TIMERS_DEBUG, ("sct calling h=%s arg=%p\n",
+ tmptimeout->handler_name, arg));
+ }
+#endif /* LWIP_DEBUG_TIMERNAMES */
+ memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
+ if (handler != NULL) {
+ handler(arg);
+ }
+ }
+ /* repeat until all expired timers have been called */
+ }while(had_one);
+ }
+}
+
+/** Set back the timestamp of the last call to sys_check_timeouts()
+ * This is necessary if sys_check_timeouts() hasn't been called for a long
+ * time (e.g. while saving energy) to prevent all timer functions of that
+ * period being called.
+ */
+void
+sys_restart_timeouts(void)
+{
+ timeouts_last_time = sys_now();
+}
+
+#else /* NO_SYS */
+
+/**
+ * Wait (forever) for a message to arrive in an mbox.
+ * While waiting, timeouts are processed.
+ *
+ * @param mbox the mbox to fetch the message from
+ * @param msg the place to store the message
+ */
+void
+sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg)
+{
+ u32_t time_needed;
+ struct sys_timeo *tmptimeout;
+ sys_timeout_handler handler;
+ void *arg;
+
+ again:
+ if (!next_timeout) {
+ time_needed = sys_arch_mbox_fetch(mbox, msg, 0);
+ } else {
+ if (next_timeout->time > 0) {
+ time_needed = sys_arch_mbox_fetch(mbox, msg, next_timeout->time);
+ } else {
+ time_needed = SYS_ARCH_TIMEOUT;
+ }
+
+ if (time_needed == SYS_ARCH_TIMEOUT) {
+ /* If time == SYS_ARCH_TIMEOUT, a timeout occured before a message
+ could be fetched. We should now call the timeout handler and
+ deallocate the memory allocated for the timeout. */
+ tmptimeout = next_timeout;
+ next_timeout = tmptimeout->next;
+ handler = tmptimeout->h;
+ arg = tmptimeout->arg;
+#if LWIP_DEBUG_TIMERNAMES
+ if (handler != NULL) {
+ LWIP_DEBUGF(TIMERS_DEBUG, ("stmf calling h=%s arg=%p\n",
+ tmptimeout->handler_name, arg));
+ }
+#endif /* LWIP_DEBUG_TIMERNAMES */
+ memp_free(MEMP_SYS_TIMEOUT, tmptimeout);
+ if (handler != NULL) {
+ /* For LWIP_TCPIP_CORE_LOCKING, lock the core before calling the
+ timeout handler function. */
+ LOCK_TCPIP_CORE();
+ handler(arg);
+ UNLOCK_TCPIP_CORE();
+ }
+ LWIP_TCPIP_THREAD_ALIVE();
+
+ /* We try again to fetch a message from the mbox. */
+ goto again;
+ } else {
+ /* If time != SYS_ARCH_TIMEOUT, a message was received before the timeout
+ occured. The time variable is set to the number of
+ milliseconds we waited for the message. */
+ if (time_needed < next_timeout->time) {
+ next_timeout->time -= time_needed;
+ } else {
+ next_timeout->time = 0;
+ }
+ }
+ }
+}
+
+#endif /* NO_SYS */
+
+#else /* LWIP_TIMERS */
+/* Satisfy the TCP code which calls this function */
+void
+tcp_timer_needed(void)
+{
+}
+#endif /* LWIP_TIMERS */
diff --git a/src/VBox/Devices/Network/lwip-new/src/core/udp.c b/src/VBox/Devices/Network/lwip-new/src/core/udp.c
new file mode 100644
index 00000000..cea68a57
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/core/udp.c
@@ -0,0 +1,1410 @@
+/**
+ * @file
+ * User Datagram Protocol module
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+
+/* udp.c
+ *
+ * The code for the User Datagram Protocol UDP & UDPLite (RFC 3828).
+ *
+ */
+
+/* @todo Check the use of '(struct udp_pcb).chksum_len_rx'!
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/udp.h"
+#include "lwip/def.h"
+#include "lwip/memp.h"
+#include "lwip/inet_chksum.h"
+#include "lwip/ip_addr.h"
+#include "lwip/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>
+
+#ifndef UDP_LOCAL_PORT_RANGE_START
+/* From http://www.iana.org/assignments/port-numbers:
+ "The Dynamic and/or Private Ports are those from 49152 through 65535" */
+#define UDP_LOCAL_PORT_RANGE_START 0xc000
+#define UDP_LOCAL_PORT_RANGE_END 0xffff
+#define UDP_ENSURE_LOCAL_PORT_RANGE(port) (((port) & ~UDP_LOCAL_PORT_RANGE_START) + UDP_LOCAL_PORT_RANGE_START)
+#endif
+
+/* last local UDP port */
+static u16_t udp_port = UDP_LOCAL_PORT_RANGE_START;
+
+#if LWIP_CONNECTION_PROXY
+static udp_recv_fn udp_proxy_accept_callback;
+#endif
+
+/* The list of UDP PCBs */
+/* exported in udp.h (was static) */
+struct udp_pcb *udp_pcbs;
+
+/* The list of proxy UDP PCBs */
+struct udp_pcb *udp_proxy_pcbs;
+
+
+/**
+ * Initialize this module.
+ */
+void
+udp_init(void)
+{
+#if LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND)
+ udp_port = UDP_ENSURE_LOCAL_PORT_RANGE(LWIP_RAND());
+#endif /* LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS && defined(LWIP_RAND) */
+}
+
+/**
+ * Allocate a new local UDP port.
+ *
+ * @return a new (free) local UDP port number
+ */
+static u16_t
+udp_new_port(void)
+{
+ u16_t n = 0;
+ struct udp_pcb *pcb;
+
+again:
+ if (udp_port++ == UDP_LOCAL_PORT_RANGE_END) {
+ udp_port = UDP_LOCAL_PORT_RANGE_START;
+ }
+ /* Check all PCBs. */
+ for(pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
+ if (pcb->local_port == udp_port) {
+ if (++n > (UDP_LOCAL_PORT_RANGE_END - UDP_LOCAL_PORT_RANGE_START)) {
+ return 0;
+ }
+ goto again;
+ }
+ }
+ return udp_port;
+#if 0
+ struct udp_pcb *ipcb = udp_pcbs;
+ while ((ipcb != NULL) && (udp_port != UDP_LOCAL_PORT_RANGE_END)) {
+ if (ipcb->local_port == udp_port) {
+ /* port is already used by another udp_pcb */
+ udp_port++;
+ /* restart scanning all udp pcbs */
+ ipcb = udp_pcbs;
+ } else {
+ /* go on with next udp pcb */
+ ipcb = ipcb->next;
+ }
+ }
+ if (ipcb != NULL) {
+ return 0;
+ }
+ return udp_port;
+#endif
+}
+
+/**
+ * Process an incoming UDP datagram.
+ *
+ * Given an incoming UDP datagram (as a chain of pbufs) this function
+ * finds a corresponding UDP PCB and hands over the pbuf to the pcbs
+ * recv function. If no pcb is found or the datagram is incorrect, the
+ * pbuf is freed.
+ *
+ * @param p pbuf to be demultiplexed to a UDP PCB (p->payload pointing to the UDP header)
+ * @param inp network interface on which the datagram was received.
+ *
+ */
+void
+udp_input(struct pbuf *p, struct netif *inp)
+{
+ struct udp_hdr *udphdr;
+ struct udp_pcb *pcb, *prev;
+ struct udp_pcb *uncon_pcb;
+ u16_t src, dest;
+ u8_t local_match;
+ u8_t broadcast;
+ u8_t for_us;
+
+ PERF_START;
+
+ UDP_STATS_INC(udp.recv);
+
+ /* Check minimum length (UDP header) */
+ if (p->len < UDP_HLEN) {
+ /* drop short packets */
+ LWIP_DEBUGF(UDP_DEBUG,
+ ("udp_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len));
+ UDP_STATS_INC(udp.lenerr);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpinerrors();
+ pbuf_free(p);
+ goto end;
+ }
+
+ udphdr = (struct udp_hdr *)p->payload;
+
+ /* is broadcast packet ? */
+#if LWIP_IPV6
+ broadcast = !ip_current_is_v6() && ip_addr_isbroadcast(ip_current_dest_addr(), inp);
+#else /* LWIP_IPV6 */
+ broadcast = ip_addr_isbroadcast(ip_current_dest_addr(), inp);
+#endif /* LWIP_IPV6 */
+
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_input: received datagram of length %"U16_F"\n", p->tot_len));
+
+ /* convert src and dest ports to host byte order */
+ src = ntohs(udphdr->src);
+ dest = ntohs(udphdr->dest);
+
+ udp_debug_print(udphdr);
+
+ /* print the UDP source and destination */
+ LWIP_DEBUGF(UDP_DEBUG, ("udp ("));
+ ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_dest_addr());
+ LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", ntohs(udphdr->dest)));
+ ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_src_addr());
+ LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", ntohs(udphdr->src)));
+
+#if LWIP_DHCP
+ pcb = NULL;
+ /* when LWIP_DHCP is active, packets to DHCP_CLIENT_PORT may only be processed by
+ the dhcp module, no other UDP pcb may use the local UDP port DHCP_CLIENT_PORT */
+ if (dest == DHCP_CLIENT_PORT) {
+ /* all packets for DHCP_CLIENT_PORT not coming from DHCP_SERVER_PORT are dropped! */
+ if (src == DHCP_SERVER_PORT) {
+ if ((inp->dhcp != NULL) && (inp->dhcp->pcb != NULL)) {
+ /* accept the packe if
+ (- broadcast or directed to us) -> DHCP is link-layer-addressed, local ip is always ANY!
+ - inp->dhcp->pcb->remote == ANY or iphdr->src
+ (no need to check for IPv6 since the dhcp struct always uses IPv4) */
+ if (ipX_addr_isany(0, &inp->dhcp->pcb->remote_ip) ||
+ ip_addr_cmp(ipX_2_ip(&(inp->dhcp->pcb->remote_ip)), ip_current_src_addr())) {
+ pcb = inp->dhcp->pcb;
+ }
+ }
+ }
+ } else
+#endif /* LWIP_DHCP */
+ {
+ prev = NULL;
+ local_match = 0;
+ uncon_pcb = NULL;
+ /* Iterate through the UDP pcb list for a matching pcb.
+ * 'Perfect match' pcbs (connected to the remote port & ip address) are
+ * preferred. If no perfect match is found, the first unconnected pcb that
+ * matches the local port and ip address gets the datagram. */
+ for (pcb = udp_pcbs; pcb != NULL; pcb = pcb->next) {
+ local_match = 0;
+ /* print the PCB local and remote address */
+ LWIP_DEBUGF(UDP_DEBUG, ("pcb ("));
+ ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG, &pcb->local_ip);
+ LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", pcb->local_port));
+ ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG, &pcb->remote_ip);
+ LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", pcb->remote_port));
+
+ /* compare PCB local addr+port to UDP destination addr+port */
+ if (pcb->local_port == dest) {
+ if (
+#if LWIP_IPV6
+ ((PCB_ISIPV6(pcb) && (ip_current_is_v6()) &&
+ (ip6_addr_isany(ipX_2_ip6(&pcb->local_ip)) ||
+#if LWIP_IPV6_MLD
+ ip6_addr_ismulticast(ip6_current_dest_addr()) ||
+#endif /* LWIP_IPV6_MLD */
+ ip6_addr_cmp(ipX_2_ip6(&pcb->local_ip), ip6_current_dest_addr()))) ||
+ (!PCB_ISIPV6(pcb) &&
+ (ip_current_header() != NULL) &&
+#else /* LWIP_IPV6 */
+ ((
+#endif /* LWIP_IPV6 */
+ ((!broadcast && ipX_addr_isany(0, &pcb->local_ip)) ||
+ ip_addr_cmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr()) ||
+#if LWIP_IGMP
+ ip_addr_ismulticast(ip_current_dest_addr()) ||
+#endif /* LWIP_IGMP */
+#if IP_SOF_BROADCAST_RECV
+ (broadcast && ip_get_option(pcb, SOF_BROADCAST) &&
+ (ipX_addr_isany(0, &pcb->local_ip) ||
+ ip_addr_netcmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr(), &inp->netmask))))))) {
+#else /* IP_SOF_BROADCAST_RECV */
+ (broadcast &&
+ (ipX_addr_isany(0, &pcb->local_ip) ||
+ ip_addr_netcmp(ipX_2_ip(&pcb->local_ip), ip_current_dest_addr(), &inp->netmask))))))) {
+#endif /* IP_SOF_BROADCAST_RECV */
+ local_match = 1;
+ if ((uncon_pcb == NULL) &&
+ ((pcb->flags & UDP_FLAGS_CONNECTED) == 0)) {
+ /* the first unconnected matching PCB */
+ uncon_pcb = pcb;
+ }
+ }
+ }
+ /* compare PCB remote addr+port to UDP source addr+port */
+ if ((local_match != 0) &&
+ (pcb->remote_port == src) && IP_PCB_IPVER_INPUT_MATCH(pcb) &&
+ (ipX_addr_isany(PCB_ISIPV6(pcb), &pcb->remote_ip) ||
+ ipX_addr_cmp(PCB_ISIPV6(pcb), &pcb->remote_ip, ipX_current_src_addr()))) {
+ /* the first fully matching PCB */
+ if (prev != NULL) {
+ /* move the pcb to the front of udp_pcbs so that is
+ found faster next time */
+ prev->next = pcb->next;
+ pcb->next = udp_pcbs;
+ udp_pcbs = pcb;
+ } else {
+ UDP_STATS_INC(udp.cachehit);
+ }
+ break;
+ }
+ prev = pcb;
+ }
+ /* no fully matching pcb found? then look for an unconnected pcb */
+ if (pcb == NULL) {
+ pcb = uncon_pcb;
+ }
+ }
+
+ /* Check checksum if this is a match or if it was directed at us. */
+ if (pcb != NULL) {
+ for_us = 1;
+ } else {
+#if LWIP_IPV6
+ if (ip_current_is_v6()) {
+ for_us = (netif_matches_ip6_addr(inp, ip6_current_dest_addr()) >= 0);
+ } else
+#endif /* LWIP_IPV6 */
+ {
+ for_us = ip_addr_cmp(&inp->ip_addr, ip_current_dest_addr());
+ }
+ }
+ if (for_us) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: calculating checksum\n"));
+#if CHECKSUM_CHECK_UDP
+#if LWIP_UDPLITE
+ if (ip_current_header_proto() == IP_PROTO_UDPLITE) {
+ /* Do the UDP Lite checksum */
+ u16_t chklen = ntohs(udphdr->len);
+ if (chklen < sizeof(struct udp_hdr)) {
+ if (chklen == 0) {
+ /* For UDP-Lite, checksum length of 0 means checksum
+ over the complete packet (See RFC 3828 chap. 3.1) */
+ chklen = p->tot_len;
+ } else {
+ /* At least the UDP-Lite header must be covered by the
+ checksum! (Again, see RFC 3828 chap. 3.1) */
+ goto chkerr;
+ }
+ }
+ if (ipX_chksum_pseudo_partial(ip_current_is_v6(), p, IP_PROTO_UDPLITE,
+ p->tot_len, chklen,
+ ipX_current_src_addr(), ipX_current_dest_addr()) != 0) {
+ goto chkerr;
+ }
+ } else
+#endif /* LWIP_UDPLITE */
+ {
+ if (udphdr->chksum != 0) {
+ if (ipX_chksum_pseudo(ip_current_is_v6(), p, IP_PROTO_UDP, p->tot_len,
+ ipX_current_src_addr(),
+ ipX_current_dest_addr()) != 0) {
+ goto chkerr;
+ }
+ }
+ }
+#endif /* CHECKSUM_CHECK_UDP */
+ if(pbuf_header(p, -UDP_HLEN)) {
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpinerrors();
+ pbuf_free(p);
+ goto end;
+ }
+ if (pcb != NULL) {
+ snmp_inc_udpindatagrams();
+#if SO_REUSE && SO_REUSE_RXTOALL
+ if ((broadcast ||
+#if LWIP_IPV6
+ ip6_addr_ismulticast(ip6_current_dest_addr()) ||
+#endif /* LWIP_IPV6 */
+ ip_addr_ismulticast(ip_current_dest_addr())) &&
+ ip_get_option(pcb, SOF_REUSEADDR)) {
+ /* pass broadcast- or multicast packets to all multicast pcbs
+ if SOF_REUSEADDR is set on the first match */
+ struct udp_pcb *mpcb;
+ u8_t p_header_changed = 0;
+ s16_t hdrs_len = (s16_t)(ip_current_header_tot_len() + UDP_HLEN);
+ for (mpcb = udp_pcbs; mpcb != NULL; mpcb = mpcb->next) {
+ if (mpcb != pcb) {
+ /* compare PCB local addr+port to UDP destination addr+port */
+ if ((mpcb->local_port == dest) &&
+#if LWIP_IPV6
+ ((PCB_ISIPV6(mpcb) &&
+ (ip6_addr_ismulticast(ip6_current_dest_addr()) ||
+ ip6_addr_cmp(ipX_2_ip6(&mpcb->local_ip), ip6_current_dest_addr()))) ||
+ (!PCB_ISIPV6(mpcb) &&
+#else /* LWIP_IPV6 */
+ ((
+#endif /* LWIP_IPV6 */
+ ((!broadcast && ipX_addr_isany(0, &mpcb->local_ip)) ||
+ ip_addr_cmp(ipX_2_ip(&mpcb->local_ip), ip_current_dest_addr()) ||
+#if LWIP_IGMP
+ ip_addr_ismulticast(ip_current_dest_addr()) ||
+#endif /* LWIP_IGMP */
+#if IP_SOF_BROADCAST_RECV
+ (broadcast && ip_get_option(mpcb, SOF_BROADCAST)))))) {
+#else /* IP_SOF_BROADCAST_RECV */
+ (broadcast))))) {
+#endif /* IP_SOF_BROADCAST_RECV */
+ /* pass a copy of the packet to all local matches */
+ if (mpcb->recv.ip4 != NULL) {
+ struct pbuf *q;
+ /* for that, move payload to IP header again */
+ if (p_header_changed == 0) {
+ pbuf_header(p, hdrs_len);
+ p_header_changed = 1;
+ }
+ q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if (q != NULL) {
+ err_t err = pbuf_copy(q, p);
+ if (err == ERR_OK) {
+ /* move payload to UDP data */
+ pbuf_header(q, -hdrs_len);
+#if LWIP_IPV6
+ if (PCB_ISIPV6(mpcb)) {
+ mpcb->recv.ip6(mpcb->recv_arg, mpcb, q, ip6_current_src_addr(), src);
+ }
+ else
+#endif /* LWIP_IPV6 */
+ {
+ mpcb->recv.ip4(mpcb->recv_arg, mpcb, q, ip_current_src_addr(), src);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ if (p_header_changed) {
+ /* and move payload to UDP data again */
+ pbuf_header(p, -hdrs_len);
+ }
+ }
+#endif /* SO_REUSE && SO_REUSE_RXTOALL */
+ /* callback */
+ if (pcb->recv.ip4 != NULL) {
+ /* now the recv function is responsible for freeing p */
+#if LWIP_IPV6
+ if (PCB_ISIPV6(pcb)) {
+ pcb->recv.ip6(pcb->recv_arg, pcb, p, ip6_current_src_addr(), src);
+ }
+ else
+#endif /* LWIP_IPV6 */
+ {
+ pcb->recv.ip4(pcb->recv_arg, pcb, p, ip_current_src_addr(), src);
+ }
+ } else {
+ /* no recv function registered? then we have to free the pbuf! */
+ pbuf_free(p);
+ goto end;
+ }
+ } else {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_input: not for us.\n"));
+
+#if LWIP_ICMP || LWIP_ICMP6
+ /* No match was found, send ICMP destination port unreachable unless
+ destination address was broadcast/multicast. */
+ if (!broadcast &&
+#if LWIP_IPV6
+ !ip6_addr_ismulticast(ip6_current_dest_addr()) &&
+#endif /* LWIP_IPV6 */
+ !ip_addr_ismulticast(ip_current_dest_addr())) {
+ /* move payload pointer back to ip header */
+ pbuf_header(p, ip_current_header_tot_len() + UDP_HLEN);
+ icmp_port_unreach(ip_current_is_v6(), p);
+ }
+#endif /* LWIP_ICMP || LWIP_ICMP6 */
+ UDP_STATS_INC(udp.proterr);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpnoports();
+ pbuf_free(p);
+ }
+ } else {
+ pbuf_free(p);
+ }
+end:
+ PERF_STOP("udp_input");
+ return;
+#if CHECKSUM_CHECK_UDP
+chkerr:
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("udp_input: UDP (or UDP Lite) datagram discarded due to failing checksum\n"));
+ UDP_STATS_INC(udp.chkerr);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpinerrors();
+ pbuf_free(p);
+ PERF_STOP("udp_input");
+#endif /* CHECKSUM_CHECK_UDP */
+}
+
+#if LWIP_CONNECTION_PROXY
+/**
+ * Process an incoming/proxied UDP datagram.
+ *
+ * @param p pbuf to be demultiplexed to a UDP PCB (p->payload pointing to the UDP header)
+ * @param inp network interface on which the datagram was received.
+ */
+void
+udp_proxy_input(struct pbuf *p, struct netif *inp)
+{
+ struct udp_hdr *udphdr;
+ struct udp_pcb *pcb;
+ udp_recv_fn callback;
+ void *callback_arg;
+ u16_t src, dest;
+
+ LWIP_UNUSED_ARG(inp);
+
+ /* there's no point in continuing if proxy is not present */
+ if (udp_proxy_accept_callback == NULL) {
+ pbuf_free(p);
+ return;
+ }
+
+ PERF_START;
+
+ UDP_STATS_INC(udp.recv);
+
+ /* Check minimum length (UDP header) */
+ if (p->len < UDP_HLEN) {
+ /* drop short packets */
+ LWIP_DEBUGF(UDP_DEBUG,
+ ("udp_proxy_input: short UDP datagram (%"U16_F" bytes) discarded\n", p->tot_len));
+ UDP_STATS_INC(udp.lenerr);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpinerrors();
+ pbuf_free(p);
+ goto end;
+ }
+
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_proxy_input: received datagram of length %"U16_F"\n", p->tot_len));
+
+ udphdr = (struct udp_hdr *)p->payload;
+ if (udphdr->chksum != 0) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE,
+ ("udp_proxy_input: calculating checksum\n"));
+ if (ipX_chksum_pseudo(ip_current_is_v6(), p, IP_PROTO_UDP, p->tot_len,
+ ipX_current_src_addr(),
+ ipX_current_dest_addr()) != 0)
+ {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_WARNING,
+ ("udp_proxy_input: UDP datagram discarded due to failing checksum\n"));
+ UDP_STATS_INC(udp.chkerr);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpinerrors();
+ pbuf_free(p);
+ goto end;
+ }
+ }
+
+ /* convert src and dest ports to host byte order */
+ src = ntohs(udphdr->src);
+ dest = ntohs(udphdr->dest);
+
+ udp_debug_print(udphdr);
+
+ /* print the UDP source and destination */
+ LWIP_DEBUGF(UDP_DEBUG, ("udp ("));
+ ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_dest_addr());
+ LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F") <-- (", ntohs(udphdr->dest)));
+ ipX_addr_debug_print(ip_current_is_v6(), UDP_DEBUG, ipX_current_src_addr());
+ LWIP_DEBUGF(UDP_DEBUG, (", %"U16_F")\n", ntohs(udphdr->src)));
+
+
+ /* do NOT proxy IPv4 broadcast datagrams */
+ if (!ip_current_is_v6() && ip_addr_isbroadcast(ip_current_dest_addr(), inp)) {
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_proxy_input: broadcast datagram dropped\n"));
+ UDP_STATS_INC(udp.drop);
+ pbuf_free(p);
+ goto end;
+ }
+
+ /* do NOT proxy multicast datagrams */
+ if (ipX_addr_ismulticast(ip_current_is_v6(), ipX_current_dest_addr())) {
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_proxy_input: multicast datagram dropped\n"));
+ UDP_STATS_INC(udp.drop);
+ pbuf_free(p);
+ goto end;
+ }
+
+ pcb = NULL;
+ callback = NULL;
+ callback_arg = NULL;
+
+ /* search known UDP "conversations" */
+ for (pcb = udp_proxy_pcbs; pcb != NULL; pcb = pcb->next) {
+ /* 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));
+
+ if (PCB_ISIPV6(pcb) == ip_current_is_v6()
+ && pcb->local_port == dest
+ && ipX_addr_cmp(ip_current_is_v6(),
+ ipX_current_dest_addr(), &pcb->local_ip)
+ && pcb->remote_port == src
+ && ipX_addr_cmp(ip_current_is_v6(),
+ ipX_current_src_addr(), &pcb->remote_ip))
+ {
+ LWIP_DEBUGF(UDP_DEBUG, ("found\n"));
+ callback = pcb->recv.ip4;
+ callback_arg = pcb->recv_arg;
+ break;
+ }
+ }
+
+ if (pcb == NULL) {
+ struct udp_pcb *npcb = udp_new();
+ if (npcb == NULL) {
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_proxy_input: failed to allocate new udp pcb\n"));
+ pbuf_free(p);
+ goto end;
+ }
+#if LWIP_IPV6
+ ip_set_v6(npcb, ip_current_is_v6());
+#endif
+
+ /* equivalent of udp_bind */
+ ipX_addr_set(PCB_ISIPV6(npcb), &npcb->local_ip, ipX_current_dest_addr());
+ npcb->local_port = dest;
+
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("udp_proxy_input: bound to "));
+ ipX_addr_debug_print(PCB_ISIPV6(npcb),
+ UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ &npcb->local_ip);
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ (", port %"U16_F"\n", npcb->local_port));
+
+ /* equivalent of udp_connect */
+ ipX_addr_set(PCB_ISIPV6(npcb), &npcb->remote_ip, ipX_current_src_addr());
+ npcb->remote_port = src;
+ npcb->flags |= UDP_FLAGS_CONNECTED;
+
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ ("udp_proxy_input: connected to "));
+ ipX_addr_debug_print(PCB_ISIPV6(npcb),
+ UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ &npcb->remote_ip);
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ (", port %"U16_F"\n", npcb->remote_port));
+
+ npcb->next = udp_proxy_pcbs;
+ udp_proxy_pcbs = npcb;
+ udp_proxy_timer_needed();
+
+ pcb = npcb;
+ callback = udp_proxy_accept_callback;
+ callback_arg = NULL;
+ }
+
+ pcb->proxy_cnt = 0;
+
+ /* move payload pointer past UDP header */
+ if (pbuf_header(p, -UDP_HLEN)) {
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ UDP_STATS_INC(udp.drop);
+ snmp_inc_udpinerrors();
+ pbuf_free(p);
+ goto end;
+ }
+
+ LWIP_ASSERT("callback != NULL", (callback != NULL));
+ (*callback)(callback_arg, pcb, p, ip_current_src_addr(), src);
+
+end:
+ PERF_STOP("udp_proxy_input");
+ return;
+}
+
+void
+udp_proxy_tmr(void)
+{
+ struct udp_pcb *pcb;
+ struct udp_pcb **pprev;
+
+ const int limit = ((UDP_PROXY_EXPIRATION /* sec */ * 1000 /* msec/sec */
+ + UDP_PROXY_TMR_INTERVAL /* msec */ - 1)
+ / UDP_PROXY_TMR_INTERVAL /* msec */);
+
+ pprev = &udp_proxy_pcbs;
+ pcb = udp_proxy_pcbs;
+ while (pcb != NULL) {
+ struct udp_pcb *xpcb;
+
+ /*
+ * Although a strict inequality '<' seems appropriate here (20 < 3 * 7),
+ * the fact that UDP proxy timer is shared by all UDP proxies may cause
+ * the first timer interval to expire anywhere between 0 to 3 seconds,
+ * which results in UDP proxy timeout being in 18 to 21 second range. By
+ * using '<=' we shift it to 21-24 second range. See @ticketref{21560}.
+ */
+ if (++pcb->proxy_cnt <= limit) {
+ pprev = &pcb->next;
+ pcb = pcb->next;
+ continue;
+ }
+
+ LWIP_DEBUGF(UDP_DEBUG, ("udp ("));
+ 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") expired\n", pcb->remote_port));
+
+ xpcb = pcb;
+
+ *pprev = pcb->next;
+ pcb = pcb->next;
+
+ (*xpcb->recv.ip4)(xpcb->recv_arg, xpcb,
+ NULL, /* signals expiration timeout */
+ ipX_2_ip(&xpcb->remote_ip), xpcb->remote_port);
+ }
+
+ udp_proxy_timer_needed();
+}
+#endif /* LWIP_CONNECTION_PROXY */
+
+
+/**
+ * Send data using UDP.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ *
+ * The datagram will be sent to the current remote_ip & remote_port
+ * stored in pcb. If the pcb is not bound to a port, it will
+ * automatically be bound to a random port.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occured.
+ * - ERR_MEM. Out of memory.
+ * - ERR_RTE. Could not find route to destination address.
+ * - More errors could be returned by lower protocol layers.
+ *
+ * @see udp_disconnect() udp_sendto()
+ */
+err_t
+udp_send(struct udp_pcb *pcb, struct pbuf *p)
+{
+ /* send to the packet using remote ip and port stored in the pcb */
+ return udp_sendto(pcb, p, ipX_2_ip(&pcb->remote_ip), pcb->remote_port);
+}
+
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+/** Same as udp_send() but with checksum
+ */
+err_t
+udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p,
+ u8_t have_chksum, u16_t chksum)
+{
+ /* send to the packet using remote ip and port stored in the pcb */
+ return udp_sendto_chksum(pcb, p, ipX_2_ip(&pcb->remote_ip), pcb->remote_port,
+ have_chksum, chksum);
+}
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+
+/**
+ * Send data to a specified address using UDP.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ * @param dst_ip Destination IP address.
+ * @param dst_port Destination UDP port.
+ *
+ * dst_ip & dst_port are expected to be in the same byte order as in the pcb.
+ *
+ * If the PCB already has a remote address association, it will
+ * be restored after the data is sent.
+ *
+ * @return lwIP error code (@see udp_send for possible error codes)
+ *
+ * @see udp_disconnect() udp_send()
+ */
+err_t
+udp_sendto(struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *dst_ip, u16_t dst_port)
+{
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+ return udp_sendto_chksum(pcb, p, dst_ip, dst_port, 0, 0);
+}
+
+/** Same as udp_sendto(), but with checksum */
+err_t
+udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
+ u16_t dst_port, u8_t have_chksum, u16_t chksum)
+{
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+ struct netif *netif;
+ ipX_addr_t *dst_ip_route = ip_2_ipX(dst_ip);
+
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send\n"));
+
+#if LWIP_IPV6 || LWIP_IGMP
+ if (ipX_addr_ismulticast(PCB_ISIPV6(pcb), dst_ip_route)) {
+ /* For multicast, find a netif based on source address. */
+#if LWIP_IPV6
+ if (PCB_ISIPV6(pcb)) {
+ dst_ip_route = &pcb->local_ip;
+ } else
+#endif /* LWIP_IPV6 */
+ {
+#if LWIP_IGMP
+ dst_ip_route = ip_2_ipX(&pcb->multicast_ip);
+#endif /* LWIP_IGMP */
+ }
+ }
+#endif /* LWIP_IPV6 || LWIP_IGMP */
+
+ /* find the outgoing network interface for this packet */
+ netif = ipX_route(PCB_ISIPV6(pcb), &pcb->local_ip, dst_ip_route);
+
+ /* no outgoing network interface could be found? */
+ if (netif == NULL) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: No route to "));
+ ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS, ip_2_ipX(dst_ip));
+ LWIP_DEBUGF(UDP_DEBUG, ("\n"));
+ UDP_STATS_INC(udp.rterr);
+ return ERR_RTE;
+ }
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+ return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, have_chksum, chksum);
+#else /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+ return udp_sendto_if(pcb, p, dst_ip, dst_port, netif);
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+}
+
+/**
+ * Send data to a specified address using UDP.
+ * The netif used for sending can be specified.
+ *
+ * This function exists mainly for DHCP, to be able to send UDP packets
+ * on a netif that is still down.
+ *
+ * @param pcb UDP PCB used to send the data.
+ * @param p chain of pbuf's to be sent.
+ * @param dst_ip Destination IP address.
+ * @param dst_port Destination UDP port.
+ * @param netif the netif used for sending.
+ *
+ * dst_ip & dst_port are expected to be in the same byte order as in the pcb.
+ *
+ * @return lwIP error code (@see udp_send for possible error codes)
+ *
+ * @see udp_disconnect() udp_send()
+ */
+err_t
+udp_sendto_if(struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *dst_ip, u16_t dst_port, struct netif *netif)
+{
+#if LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP
+ return udp_sendto_if_chksum(pcb, p, dst_ip, dst_port, netif, 0, 0);
+}
+
+/** Same as udp_sendto_if(), but with checksum */
+err_t
+udp_sendto_if_chksum(struct udp_pcb *pcb, struct pbuf *p, ip_addr_t *dst_ip,
+ u16_t dst_port, struct netif *netif, u8_t have_chksum,
+ u16_t chksum)
+{
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+ struct udp_hdr *udphdr;
+ ip_addr_t *src_ip;
+ err_t err;
+ struct pbuf *q; /* q will be sent down the stack */
+ u8_t ip_proto;
+
+#if IP_SOF_BROADCAST
+ /* broadcast filter? */
+ if (!ip_get_option(pcb, SOF_BROADCAST) &&
+#if LWIP_IPV6
+ !PCB_ISIPV6(pcb) &&
+#endif /* LWIP_IPV6 */
+ ip_addr_isbroadcast(dst_ip, netif) ) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_LEVEL_SERIOUS,
+ ("udp_sendto_if: SOF_BROADCAST not enabled on pcb %p\n", (void *)pcb));
+ return ERR_VAL;
+ }
+#endif /* IP_SOF_BROADCAST */
+
+ /* if the PCB is not yet bound to a port, bind it here */
+ if (pcb->local_port == 0) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_send: not yet bound to a port, binding now\n"));
+ err = udp_bind(pcb, ipX_2_ip(&pcb->local_ip), pcb->local_port);
+ if (err != ERR_OK) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: forced port bind failed\n"));
+ return err;
+ }
+ }
+
+ /* not enough space to add an UDP header to first pbuf in given p chain? */
+ if (pbuf_header(p, UDP_HLEN)) {
+ /* allocate header in a separate new pbuf */
+ q = pbuf_alloc(PBUF_IP, UDP_HLEN, PBUF_RAM);
+ /* new header pbuf could not be allocated? */
+ if (q == NULL) {
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS, ("udp_send: could not allocate header\n"));
+ return ERR_MEM;
+ }
+ if (p->tot_len != 0) {
+ /* chain header q in front of given pbuf p (only if p contains data) */
+ pbuf_chain(q, p);
+ }
+ /* first pbuf q points to header pbuf */
+ LWIP_DEBUGF(UDP_DEBUG,
+ ("udp_send: added header pbuf %p before given pbuf %p\n", (void *)q, (void *)p));
+ } else {
+ /* adding space for header within p succeeded */
+ /* first pbuf q equals given pbuf */
+ q = p;
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: added header in given pbuf %p\n", (void *)p));
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct udp_hdr",
+ (q->len >= sizeof(struct udp_hdr)));
+ /* q now represents the packet to be sent */
+ udphdr = (struct udp_hdr *)q->payload;
+ udphdr->src = htons(pcb->local_port);
+ udphdr->dest = htons(dst_port);
+ /* in UDP, 0 checksum means 'no checksum' */
+ udphdr->chksum = 0x0000;
+
+ /* Multicast Loop? */
+#if LWIP_IGMP
+ if (((pcb->flags & UDP_FLAGS_MULTICAST_LOOP) != 0) &&
+#if LWIP_IPV6
+ (
+#if LWIP_IPV6_MLD
+ (PCB_ISIPV6(pcb) &&
+ ip6_addr_ismulticast(ip_2_ip6(dst_ip))) ||
+#endif /* LWIP_IPV6_MLD */
+ (!PCB_ISIPV6(pcb) &&
+#else /* LWIP_IPV6 */
+ ((
+#endif /* LWIP_IPV6 */
+ ip_addr_ismulticast(dst_ip)))) {
+ q->flags |= PBUF_FLAG_MCASTLOOP;
+ }
+#endif /* LWIP_IGMP */
+
+
+ /* PCB local address is IP_ANY_ADDR? */
+#if LWIP_IPV6
+ if (PCB_ISIPV6(pcb)) {
+ if (ip6_addr_isany(ipX_2_ip6(&pcb->local_ip))) {
+ src_ip = ip6_2_ip(ip6_select_source_address(netif, ip_2_ip6(dst_ip)));
+ if (src_ip == NULL) {
+ /* No suitable source address was found. */
+ if (q != p) {
+ /* free the header pbuf */
+ pbuf_free(q);
+ /* p is still referenced by the caller, and will live on */
+ }
+ return ERR_RTE;
+ }
+ } else {
+ /* XXX: uwe: for proxied packets pcb "local" address is the
+ * address of the external host */
+#if !LWIP_CONNECTION_PROXY
+ /* 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;
+ }
+#endif /* !LWIP_CONNECTION_PROXY */
+ 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 {
+ /* XXX: uwe: for proxied packets pcb "local" address is the
+ * address of the external host */
+#if !LWIP_CONNECTION_PROXY
+ /* 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;
+ }
+#endif /* !LWIP_CONNECTION_PROXY */
+ /* use UDP PCB local IP address as source address */
+ src_ip = ipX_2_ip(&(pcb->local_ip));
+ }
+
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: sending datagram of length %"U16_F"\n", q->tot_len));
+
+#if LWIP_UDPLITE
+ /* UDP Lite protocol? */
+ if (pcb->flags & UDP_FLAGS_UDPLITE) {
+ u16_t chklen, chklen_hdr;
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE packet length %"U16_F"\n", q->tot_len));
+ /* set UDP message length in UDP header */
+ chklen_hdr = chklen = pcb->chksum_len_tx;
+ if ((chklen < sizeof(struct udp_hdr)) || (chklen > q->tot_len)) {
+ if (chklen != 0) {
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP LITE pcb->chksum_len is illegal: %"U16_F"\n", chklen));
+ }
+ /* For UDP-Lite, checksum length of 0 means checksum
+ over the complete packet. (See RFC 3828 chap. 3.1)
+ At least the UDP-Lite header must be covered by the
+ checksum, therefore, if chksum_len has an illegal
+ value, we generate the checksum over the complete
+ packet to be safe. */
+ chklen_hdr = 0;
+ chklen = q->tot_len;
+ }
+ udphdr->len = htons(chklen_hdr);
+ /* calculate checksum */
+#if CHECKSUM_GEN_UDP
+#if LWIP_CHECKSUM_ON_COPY
+ if (have_chksum) {
+ chklen = UDP_HLEN;
+ }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ udphdr->chksum = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), q, IP_PROTO_UDPLITE,
+ q->tot_len, chklen, ip_2_ipX(src_ip), ip_2_ipX(dst_ip));
+#if LWIP_CHECKSUM_ON_COPY
+ if (have_chksum) {
+ u32_t acc;
+ acc = udphdr->chksum + (u16_t)~(chksum);
+ udphdr->chksum = FOLD_U32T(acc);
+ }
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+ /* chksum zero must become 0xffff, as zero means 'no checksum' */
+ if (udphdr->chksum == 0x0000) {
+ udphdr->chksum = 0xffff;
+ }
+#endif /* CHECKSUM_GEN_UDP */
+
+ ip_proto = IP_PROTO_UDPLITE;
+ } else
+#endif /* LWIP_UDPLITE */
+ { /* UDP */
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP packet length %"U16_F"\n", q->tot_len));
+ udphdr->len = htons(q->tot_len);
+ /* calculate checksum */
+#if CHECKSUM_GEN_UDP
+ /* Checksum is mandatory over IPv6. */
+ if (PCB_ISIPV6(pcb) || (pcb->flags & UDP_FLAGS_NOCHKSUM) == 0) {
+ u16_t udpchksum;
+#if LWIP_CHECKSUM_ON_COPY
+ if (have_chksum) {
+ u32_t acc;
+ udpchksum = ipX_chksum_pseudo_partial(PCB_ISIPV6(pcb), q, IP_PROTO_UDP,
+ q->tot_len, UDP_HLEN, ip_2_ipX(src_ip), ip_2_ipX(dst_ip));
+ acc = udpchksum + (u16_t)~(chksum);
+ udpchksum = FOLD_U32T(acc);
+ } else
+#endif /* LWIP_CHECKSUM_ON_COPY */
+ {
+ udpchksum = ipX_chksum_pseudo(PCB_ISIPV6(pcb), q, IP_PROTO_UDP, q->tot_len,
+ ip_2_ipX(src_ip), ip_2_ipX(dst_ip));
+ }
+
+ /* chksum zero must become 0xffff, as zero means 'no checksum' */
+ if (udpchksum == 0x0000) {
+ udpchksum = 0xffff;
+ }
+ udphdr->chksum = udpchksum;
+ }
+#endif /* CHECKSUM_GEN_UDP */
+ ip_proto = IP_PROTO_UDP;
+ }
+
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: UDP checksum 0x%04"X16_F"\n", udphdr->chksum));
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_send: ip_output_if (,,,,0x%02"X16_F",)\n", (u16_t)ip_proto));
+ /* output to IP */
+ NETIF_SET_HWADDRHINT(netif, &(pcb->addr_hint));
+ err = ipX_output_if(PCB_ISIPV6(pcb), q, src_ip, dst_ip, pcb->ttl, pcb->tos, ip_proto, netif);
+ NETIF_SET_HWADDRHINT(netif, NULL);
+
+ /* TODO: must this be increased even if error occured? */
+ snmp_inc_udpoutdatagrams();
+
+ /* did we chain a separate header pbuf earlier? */
+ if (q != p) {
+ /* free the header pbuf */
+ pbuf_free(q);
+ q = NULL;
+ /* p is still referenced by the caller, and will live on */
+ }
+
+ UDP_STATS_INC(udp.xmit);
+ return err;
+}
+
+/**
+ * Bind an UDP PCB.
+ *
+ * @param pcb UDP PCB to be bound with a local address ipaddr and port.
+ * @param ipaddr local IP address to bind with. Use IP_ADDR_ANY to
+ * bind to all local interfaces.
+ * @param port local UDP port to bind with. Use 0 to automatically bind
+ * to a random port between UDP_LOCAL_PORT_RANGE_START and
+ * UDP_LOCAL_PORT_RANGE_END.
+ *
+ * ipaddr & port are expected to be in the same byte order as in the pcb.
+ *
+ * @return lwIP error code.
+ * - ERR_OK. Successful. No error occured.
+ * - ERR_USE. The specified ipaddr and port are already bound to by
+ * another UDP PCB.
+ *
+ * @see udp_disconnect()
+ */
+err_t
+udp_bind(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
+{
+ struct udp_pcb *ipcb;
+ u8_t rebind;
+
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, ("udp_bind(ipaddr = "));
+ ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE, ip_2_ipX(ipaddr));
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE, (", port = %"U16_F")\n", port));
+
+ rebind = 0;
+ /* Check for double bind and rebind of the same pcb */
+ for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+ /* is this UDP PCB already on active list? */
+ if (pcb == ipcb) {
+ /* pcb may occur at most once in active list */
+ LWIP_ASSERT("rebind == 0", rebind == 0);
+ /* pcb already in list, just rebind */
+ rebind = 1;
+ }
+
+ /* By default, we don't allow to bind to a port that any other udp
+ PCB is alread bound to, unless *all* PCBs with that port have tha
+ REUSEADDR flag set. */
+#if SO_REUSE
+ else if (!ip_get_option(pcb, SOF_REUSEADDR) &&
+ !ip_get_option(ipcb, SOF_REUSEADDR)) {
+#else /* SO_REUSE */
+ /* port matches that of PCB in list and REUSEADDR not set -> reject */
+ else {
+#endif /* SO_REUSE */
+ if ((ipcb->local_port == port) && IP_PCB_IPVER_EQ(pcb, ipcb) &&
+ /* IP address matches, or one is IP_ADDR_ANY? */
+ (ipX_addr_isany(PCB_ISIPV6(ipcb), &(ipcb->local_ip)) ||
+ ipX_addr_isany(PCB_ISIPV6(ipcb), ip_2_ipX(ipaddr)) ||
+ ipX_addr_cmp(PCB_ISIPV6(ipcb), &(ipcb->local_ip), ip_2_ipX(ipaddr)))) {
+ /* other PCB already binds to this local IP and port */
+ LWIP_DEBUGF(UDP_DEBUG,
+ ("udp_bind: local port %"U16_F" already bound by another pcb\n", port));
+ return ERR_USE;
+ }
+ }
+ }
+
+ ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->local_ip, ipaddr);
+
+ /* no port specified? */
+ if (port == 0) {
+ port = udp_new_port();
+ if (port == 0) {
+ /* no more ports available in local range */
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_bind: out of free UDP ports\n"));
+ return ERR_USE;
+ }
+ }
+ pcb->local_port = port;
+ snmp_insert_udpidx_tree(pcb);
+ /* pcb not active yet? */
+ if (rebind == 0) {
+ /* place the PCB on the active list if not already there */
+ pcb->next = udp_pcbs;
+ udp_pcbs = pcb;
+ }
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_bind: bound to "));
+ ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, &pcb->local_ip);
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->local_port));
+ return ERR_OK;
+}
+
+/**
+ * Connect an UDP PCB.
+ *
+ * This will associate the UDP PCB with the remote address.
+ *
+ * @param pcb UDP PCB to be connected with remote address ipaddr and port.
+ * @param ipaddr remote IP address to connect with.
+ * @param port remote UDP port to connect with.
+ *
+ * @return lwIP error code
+ *
+ * ipaddr & port are expected to be in the same byte order as in the pcb.
+ *
+ * The udp pcb is bound to a random local port if not already bound.
+ *
+ * @see udp_disconnect()
+ */
+err_t
+udp_connect(struct udp_pcb *pcb, ip_addr_t *ipaddr, u16_t port)
+{
+ struct udp_pcb *ipcb;
+
+ if (pcb->local_port == 0) {
+ err_t err = udp_bind(pcb, ipX_2_ip(&pcb->local_ip), pcb->local_port);
+ if (err != ERR_OK) {
+ return err;
+ }
+ }
+
+ ipX_addr_set_ipaddr(PCB_ISIPV6(pcb), &pcb->remote_ip, ipaddr);
+ pcb->remote_port = port;
+ pcb->flags |= UDP_FLAGS_CONNECTED;
+/** TODO: this functionality belongs in upper layers */
+#ifdef LWIP_UDP_TODO
+#if LWIP_IPV6
+ if (!PCB_ISIPV6(pcb))
+#endif /* LWIP_IPV6 */
+ {
+ /* Nail down local IP for netconn_addr()/getsockname() */
+ if (ip_addr_isany(ipX_2_ip(&pcb->local_ip)) && !ip_addr_isany(ipX_2_ip(&pcb->remote_ip))) {
+ struct netif *netif;
+
+ if ((netif = ip_route(ipX_2_ip(&pcb->remote_ip))) == NULL) {
+ LWIP_DEBUGF(UDP_DEBUG, ("udp_connect: No route to 0x%lx\n",
+ ip4_addr_get_u32(ipX_2_ip(&pcb->remote_ip))));
+ UDP_STATS_INC(udp.rterr);
+ return ERR_RTE;
+ }
+ /** TODO: this will bind the udp pcb locally, to the interface which
+ is used to route output packets to the remote address. However, we
+ might want to accept incoming packets on any interface! */
+ ipX_addr_copy(0, pcb->local_ip, netif->ip_addr);
+ } else if (ip_addr_isany(ipX_2_ip(&pcb->remote_ip))) {
+ ipX_addr_set_any(0, &pcb->local_ip);
+ }
+ }
+#endif
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, ("udp_connect: connected to "));
+ ipX_addr_debug_print(PCB_ISIPV6(pcb), UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE,
+ &pcb->remote_ip);
+ LWIP_DEBUGF(UDP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_STATE, (", port %"U16_F")\n", pcb->remote_port));
+
+ /* Insert UDP PCB into the list of active UDP PCBs. */
+ for (ipcb = udp_pcbs; ipcb != NULL; ipcb = ipcb->next) {
+ if (pcb == ipcb) {
+ /* already on the list, just return */
+ return ERR_OK;
+ }
+ }
+ /* PCB not yet on the list, add PCB now */
+ pcb->next = udp_pcbs;
+ udp_pcbs = pcb;
+ return ERR_OK;
+}
+
+/**
+ * Disconnect a UDP PCB
+ *
+ * @param pcb the udp pcb to disconnect.
+ */
+void
+udp_disconnect(struct udp_pcb *pcb)
+{
+ /* reset remote address association */
+ ipX_addr_set_any(PCB_ISIPV6(pcb), &pcb->remote_ip);
+ pcb->remote_port = 0;
+ /* mark PCB as unconnected */
+ pcb->flags &= ~UDP_FLAGS_CONNECTED;
+}
+
+/**
+ * Set a receive callback for a UDP PCB
+ *
+ * This callback will be called when receiving a datagram for the pcb.
+ *
+ * @param pcb the pcb for wich to set the recv callback
+ * @param recv function pointer of the callback function
+ * @param recv_arg additional argument to pass to the callback function
+ */
+void
+udp_recv(struct udp_pcb *pcb, udp_recv_fn recv, void *recv_arg)
+{
+ /* remember recv() callback and user data */
+ pcb->recv.ip4 = recv;
+ pcb->recv_arg = recv_arg;
+}
+
+#if LWIP_CONNECTION_PROXY
+/**
+ * Set UDP proxy accept callback.
+ */
+void
+udp_proxy_accept(udp_recv_fn accept)
+{
+ udp_proxy_accept_callback = accept;
+}
+#endif /* LWIP_CONNECTION_PROXY */
+
+/**
+ * Remove an UDP PCB.
+ *
+ * @param pcb UDP PCB to be removed. The PCB is removed from the list of
+ * UDP PCB's and the data structure is freed from memory.
+ *
+ * @see udp_new()
+ */
+void
+udp_remove(struct udp_pcb *pcb)
+{
+ static struct udp_pcb **pcb_lists[] = { &udp_pcbs, &udp_proxy_pcbs };
+ unsigned int i;
+
+ snmp_delete_udpidx_tree(pcb);
+
+ for (i = 0; i < sizeof(pcb_lists)/sizeof(pcb_lists[0]); ++i) {
+ struct udp_pcb **p;
+ for (p = pcb_lists[i]; *p != NULL; p = &(*p)->next) {
+ if (pcb == *p) {
+ *p = pcb->next;
+ goto removed_from_list;
+ }
+ }
+ }
+removed_from_list:
+ memp_free(MEMP_UDP_PCB, pcb);
+}
+
+/**
+ * Create a UDP PCB.
+ *
+ * @return The UDP PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @see udp_remove()
+ */
+struct udp_pcb *
+udp_new(void)
+{
+ struct udp_pcb *pcb;
+ pcb = (struct udp_pcb *)memp_malloc(MEMP_UDP_PCB);
+ /* could allocate UDP PCB? */
+ if (pcb != NULL) {
+ /* UDP Lite: by initializing to all zeroes, chksum_len is set to 0
+ * which means checksum is generated over the whole datagram per default
+ * (recommended as default by RFC 3828). */
+ /* initialize PCB to all zeroes */
+ memset(pcb, 0, sizeof(struct udp_pcb));
+ pcb->ttl = UDP_TTL;
+ }
+ return pcb;
+}
+
+#if LWIP_IPV6
+/**
+ * Create a UDP PCB for IPv6.
+ *
+ * @return The UDP PCB which was created. NULL if the PCB data structure
+ * could not be allocated.
+ *
+ * @see udp_remove()
+ */
+struct udp_pcb *
+udp_new_ip6(void)
+{
+ struct udp_pcb *pcb;
+ pcb = udp_new();
+ ip_set_v6(pcb, 1);
+ return pcb;
+}
+#endif /* LWIP_IPV6 */
+
+#if UDP_DEBUG
+/**
+ * Print UDP header information for debug purposes.
+ *
+ * @param udphdr pointer to the udp header in memory.
+ */
+void
+udp_debug_print(struct udp_hdr *udphdr)
+{
+ LWIP_UNUSED_ARG(udphdr);
+
+ LWIP_DEBUGF(UDP_DEBUG, ("UDP header:\n"));
+ LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | %5"U16_F" | (src port, dest port)\n",
+ ntohs(udphdr->src), ntohs(udphdr->dest)));
+ LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+ LWIP_DEBUGF(UDP_DEBUG, ("| %5"U16_F" | 0x%04"X16_F" | (len, chksum)\n",
+ ntohs(udphdr->len), ntohs(udphdr->chksum)));
+ LWIP_DEBUGF(UDP_DEBUG, ("+-------------------------------+\n"));
+}
+#endif /* UDP_DEBUG */
+
+#endif /* LWIP_UDP */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/autoip.h b/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/autoip.h
new file mode 100644
index 00000000..b9f18733
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/autoip.h
@@ -0,0 +1,121 @@
+/**
+ * @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);
+
+/** Remove a struct autoip previously set to the netif using autoip_set_struct() */
+#define autoip_remove_struct(netif) do { (netif)->autoip = NULL; } while (0)
+
+/** 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/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/icmp.h b/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/icmp.h
new file mode 100644
index 00000000..6fb41331
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/icmp.h
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_ICMP_H__
+#define __LWIP_ICMP_H__
+
+#include "lwip/opt.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+
+#if LWIP_IPV6 && LWIP_ICMP6
+#include "lwip/icmp6.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define ICMP_HLEN 8
+
+#define ICMP_ER 0 /* echo reply */
+#define ICMP_DUR 3 /* destination unreachable */
+#define ICMP_SQ 4 /* source quench */
+#define ICMP_RD 5 /* redirect */
+#define ICMP_ECHO 8 /* echo */
+#define ICMP_TE 11 /* time exceeded */
+#define ICMP_PP 12 /* parameter problem */
+#define ICMP_TS 13 /* timestamp */
+#define ICMP_TSR 14 /* timestamp reply */
+#define ICMP_IRQ 15 /* information request */
+#define ICMP_IR 16 /* information reply */
+
+enum icmp_dur_type {
+ ICMP_DUR_NET = 0, /* net unreachable */
+ ICMP_DUR_HOST = 1, /* host unreachable */
+ ICMP_DUR_PROTO = 2, /* protocol unreachable */
+ ICMP_DUR_PORT = 3, /* port unreachable */
+ ICMP_DUR_FRAG = 4, /* fragmentation needed and DF set */
+ ICMP_DUR_SR = 5 /* source route failed */
+};
+
+enum icmp_te_type {
+ ICMP_TE_TTL = 0, /* time to live exceeded in transit */
+ ICMP_TE_FRAG = 1 /* fragment reassembly time exceeded */
+};
+
+/** This is the standard ICMP header only that the u32_t data
+ * is splitted to two u16_t like ICMP echo needs it.
+ * This header is also used for other ICMP types that do not
+ * use the data part.
+ */
+#ifdef VBOX /* Clang 11 takes offence when member addresses are taken (id). Packing not needed, so just dispense with it. */
+struct icmp_echo_hdr {
+ u8_t type;
+ u8_t code;
+ u16_t chksum;
+ u16_t id;
+ u16_t seqno;
+};
+AssertCompileSize(struct icmp_echo_hdr, sizeof(uint32_t)*2);
+#else
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct icmp_echo_hdr {
+ PACK_STRUCT_FIELD(u8_t type);
+ PACK_STRUCT_FIELD(u8_t code);
+ PACK_STRUCT_FIELD(u16_t chksum);
+ PACK_STRUCT_FIELD(u16_t id);
+ PACK_STRUCT_FIELD(u16_t seqno);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+#endif /* VBOX */
+
+#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);
+#if LWIP_CONNECTION_PROXY
+void icmp_proxy_input(struct pbuf *p, struct netif *inp);
+#endif
+void icmp_dest_unreach(struct pbuf *p, enum icmp_dur_type t);
+void icmp_time_exceeded(struct pbuf *p, enum icmp_te_type t);
+
+#if LWIP_CONNECTION_PROXY
+typedef void (*ping_proxy_fn)(void *arg, struct pbuf *p);
+void ping_proxy_accept(ping_proxy_fn callback, void *arg);
+#endif
+
+#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__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/igmp.h b/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/igmp.h
new file mode 100644
index 00000000..8aabac24
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/igmp.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (c) 2002 CITEL Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of CITEL Technologies Ltd nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY CITEL TECHNOLOGIES AND CONTRIBUTORS ``AS IS''
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL CITEL TECHNOLOGIES OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is a contribution to the lwIP TCP/IP stack.
+ * The Swedish Institute of Computer Science and Adam Dunkels
+ * are specifically granted permission to redistribute this
+ * source code.
+*/
+
+#ifndef __LWIP_IGMP_H__
+#define __LWIP_IGMP_H__
+
+#include "lwip/opt.h"
+#include "lwip/ip_addr.h"
+#include "lwip/netif.h"
+#include "lwip/pbuf.h"
+
+#if LWIP_IGMP /* don't build if not configured for use in lwipopts.h */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+/* IGMP timer */
+#define IGMP_TMR_INTERVAL 100 /* Milliseconds */
+#define IGMP_V1_DELAYING_MEMBER_TMR (1000/IGMP_TMR_INTERVAL)
+#define IGMP_JOIN_DELAYING_MEMBER_TMR (500 /IGMP_TMR_INTERVAL)
+
+/* MAC Filter Actions, these are passed to a netif's
+ * igmp_mac_filter callback function. */
+#define IGMP_DEL_MAC_FILTER 0
+#define IGMP_ADD_MAC_FILTER 1
+
+
+/**
+ * igmp group structure - there is
+ * a list of groups for each interface
+ * these should really be linked from the interface, but
+ * if we keep them separate we will not affect the lwip original code
+ * too much
+ *
+ * There will be a group for the all systems group address but this
+ * will not run the state machine as it is used to kick off reports
+ * from all the other groups
+ */
+struct igmp_group {
+ /** next link */
+ struct igmp_group *next;
+ /** interface on which the group is active */
+ struct netif *netif;
+ /** multicast address */
+ ip_addr_t group_address;
+ /** signifies we were the last person to report */
+ u8_t last_reporter_flag;
+ /** current state of the group */
+ u8_t group_state;
+ /** timer for reporting, negative is OFF */
+ u16_t timer;
+ /** counter of simultaneous uses */
+ u8_t use;
+};
+
+/* Prototypes */
+void igmp_init(void);
+err_t igmp_start(struct netif *netif);
+err_t igmp_stop(struct netif *netif);
+void igmp_report_groups(struct netif *netif);
+struct igmp_group *igmp_lookfor_group(struct netif *ifp, ip_addr_t *addr);
+void igmp_input(struct pbuf *p, struct netif *inp, ip_addr_t *dest);
+err_t igmp_joingroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr);
+err_t igmp_leavegroup(ip_addr_t *ifaddr, ip_addr_t *groupaddr);
+void igmp_tmr(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IGMP */
+
+#endif /* __LWIP_IGMP_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/inet.h b/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/inet.h
new file mode 100644
index 00000000..a6d2eda4
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/inet.h
@@ -0,0 +1,121 @@
+/*
+ * 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
+
+/* 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
+/** For compatibility with BSD code */
+struct in_addr {
+ in_addr_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
+
+#ifndef INET_ADDRSTRLEN
+#define INET_ADDRSTRLEN IP4ADDR_STRLEN_MAX
+#endif
+#if LWIP_IPV6
+#ifndef INET6_ADDRSTRLEN
+#define INET6_ADDRSTRLEN IP6ADDR_STRLEN_MAX
+#endif
+#endif
+
+#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/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/ip4.h b/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/ip4.h
new file mode 100644
index 00000000..c8f619ce
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/ip4.h
@@ -0,0 +1,151 @@
+/*
+ * 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_IP4_H__
+#define __LWIP_IP4_H__
+
+#include "lwip/opt.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"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Currently, the function ip_output_if_opt() is only used with IGMP */
+#define IP_OPTIONS_SEND LWIP_IGMP
+
+#define IP_HLEN 20
+
+#define IP_PROTO_ICMP 1
+#define IP_PROTO_IGMP 2
+#define IP_PROTO_UDP 17
+#define IP_PROTO_UDPLITE 136
+#define IP_PROTO_TCP 6
+
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip_hdr {
+ /* version / header length */
+ PACK_STRUCT_FIELD(u8_t _v_hl);
+ /* type of service */
+ PACK_STRUCT_FIELD(u8_t _tos);
+ /* total length */
+ PACK_STRUCT_FIELD(u16_t _len);
+ /* identification */
+ PACK_STRUCT_FIELD(u16_t _id);
+ /* fragment offset field */
+ PACK_STRUCT_FIELD(u16_t _offset);
+#define IP_RF 0x8000U /* reserved fragment flag */
+#define IP_DF 0x4000U /* dont fragment flag */
+#define IP_MF 0x2000U /* more fragments flag */
+#define IP_OFFMASK 0x1fffU /* mask for fragmenting bits */
+ /* time to live */
+ PACK_STRUCT_FIELD(u8_t _ttl);
+ /* protocol*/
+ PACK_STRUCT_FIELD(u8_t _proto);
+ /* checksum */
+ PACK_STRUCT_FIELD(u16_t _chksum);
+ /* source and destination IP addresses */
+ PACK_STRUCT_FIELD(ip_addr_p_t src);
+ PACK_STRUCT_FIELD(ip_addr_p_t dest);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define IPH_V(hdr) ((hdr)->_v_hl >> 4)
+#define IPH_HL(hdr) ((hdr)->_v_hl & 0x0f)
+#define IPH_TOS(hdr) ((hdr)->_tos)
+#define IPH_LEN(hdr) ((hdr)->_len)
+#define IPH_ID(hdr) ((hdr)->_id)
+#define IPH_OFFSET(hdr) ((hdr)->_offset)
+#define IPH_TTL(hdr) ((hdr)->_ttl)
+#define IPH_PROTO(hdr) ((hdr)->_proto)
+#define IPH_CHKSUM(hdr) ((hdr)->_chksum)
+
+#define IPH_VHL_SET(hdr, v, hl) (hdr)->_v_hl = (((v) << 4) | (hl))
+#define IPH_TOS_SET(hdr, tos) (hdr)->_tos = (tos)
+#define IPH_LEN_SET(hdr, len) (hdr)->_len = (len)
+#define IPH_ID_SET(hdr, id) (hdr)->_id = (id)
+#define IPH_OFFSET_SET(hdr, off) (hdr)->_offset = (off)
+#define IPH_TTL_SET(hdr, ttl) (hdr)->_ttl = (u8_t)(ttl)
+#define IPH_PROTO_SET(hdr, proto) (hdr)->_proto = (u8_t)(proto)
+#define IPH_CHKSUM_SET(hdr, chksum) (hdr)->_chksum = (chksum)
+
+
+#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 LWIP_CONNECTION_PROXY
+typedef int (*proxy_ip4_divert_hook_fn)(struct netif *netif, ip_addr_t *dest);
+extern proxy_ip4_divert_hook_fn proxy_ip4_divert_hook;
+#endif
+
+#if IP_DEBUG
+void ip_debug_print(struct pbuf *p);
+#else
+#define ip_debug_print(p)
+#endif /* IP_DEBUG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_IP_H__ */
+
+
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/ip4_addr.h b/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/ip4_addr.h
new file mode 100644
index 00000000..af76856c
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/ip4_addr.h
@@ -0,0 +1,247 @@
+/*
+ * 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_IP4_ADDR_H__
+#define __LWIP_IP4_ADDR_H__
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This is the aligned version of ip_addr_t,
+ used as local variable, on the stack, etc. */
+struct ip_addr {
+ u32_t addr;
+};
+
+/* This is the packed version of ip_addr_t,
+ used in network headers that are itself packed */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip_addr_packed {
+ PACK_STRUCT_FIELD(u32_t addr);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** ip_addr_t uses a struct for convenience only, so that the same defines can
+ * operate both on ip_addr_t as well as on ip_addr_p_t. */
+typedef struct ip_addr ip_addr_t;
+typedef struct ip_addr_packed ip_addr_p_t;
+
+/*
+ * struct ipaddr2 is used in the definition of the ARP packet format in
+ * order to support compilers that don't have structure packing.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip_addr2 {
+ PACK_STRUCT_FIELD(u16_t addrw[2]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/* Forward declaration to not include netif.h */
+struct netif;
+
+extern const ip_addr_t ip_addr_any;
+extern const ip_addr_t ip_addr_broadcast;
+
+/** IP_ADDR_ can be used as a fixed IP address
+ * for the wildcard and the broadcast address
+ */
+#define IP_ADDR_ANY ((ip_addr_t *)&ip_addr_any)
+#define IP_ADDR_BROADCAST ((ip_addr_t *)&ip_addr_broadcast)
+
+/** 255.255.255.255 */
+#define IPADDR_NONE ((u32_t)0xffffffffUL)
+/** 127.0.0.1 */
+#define IPADDR_LOOPBACK ((u32_t)0x7f000001UL)
+/** 0.0.0.0 */
+#define IPADDR_ANY ((u32_t)0x00000000UL)
+/** 255.255.255.255 */
+#define IPADDR_BROADCAST ((u32_t)0xffffffffUL)
+
+/* Definitions of the bits in an Internet address integer.
+
+ On subnets, host and network parts are found according to
+ the subnet mask, not these masks. */
+#define IP_CLASSA(a) ((((u32_t)(a)) & 0x80000000UL) == 0)
+#define IP_CLASSA_NET 0xff000000
+#define IP_CLASSA_NSHIFT 24
+#define IP_CLASSA_HOST (0xffffffff & ~IP_CLASSA_NET)
+#define IP_CLASSA_MAX 128
+
+#define IP_CLASSB(a) ((((u32_t)(a)) & 0xc0000000UL) == 0x80000000UL)
+#define IP_CLASSB_NET 0xffff0000
+#define IP_CLASSB_NSHIFT 16
+#define IP_CLASSB_HOST (0xffffffff & ~IP_CLASSB_NET)
+#define IP_CLASSB_MAX 65536
+
+#define IP_CLASSC(a) ((((u32_t)(a)) & 0xe0000000UL) == 0xc0000000UL)
+#define IP_CLASSC_NET 0xffffff00
+#define IP_CLASSC_NSHIFT 8
+#define IP_CLASSC_HOST (0xffffffff & ~IP_CLASSC_NET)
+
+#define IP_CLASSD(a) (((u32_t)(a) & 0xf0000000UL) == 0xe0000000UL)
+#define IP_CLASSD_NET 0xf0000000 /* These ones aren't really */
+#define IP_CLASSD_NSHIFT 28 /* net and host fields, but */
+#define IP_CLASSD_HOST 0x0fffffff /* routing needn't know. */
+#define IP_MULTICAST(a) IP_CLASSD(a)
+
+#define IP_EXPERIMENTAL(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL)
+#define IP_BADCLASS(a) (((u32_t)(a) & 0xf0000000UL) == 0xf0000000UL)
+
+#define IP_LOOPBACKNET 127 /* official! */
+
+
+#if BYTE_ORDER == BIG_ENDIAN
+/** Set an IP address given by the four byte-parts */
+#define IP4_ADDR(ipaddr, a,b,c,d) \
+ (ipaddr)->addr = ((u32_t)((a) & 0xff) << 24) | \
+ ((u32_t)((b) & 0xff) << 16) | \
+ ((u32_t)((c) & 0xff) << 8) | \
+ (u32_t)((d) & 0xff)
+#else
+/** Set an IP address given by the four byte-parts.
+ Little-endian version that prevents the use of htonl. */
+#define IP4_ADDR(ipaddr, a,b,c,d) \
+ (ipaddr)->addr = ((u32_t)((d) & 0xff) << 24) | \
+ ((u32_t)((c) & 0xff) << 16) | \
+ ((u32_t)((b) & 0xff) << 8) | \
+ (u32_t)((a) & 0xff)
+#endif
+
+/** MEMCPY-like copying of IP addresses where addresses are known to be
+ * 16-bit-aligned if the port is correctly configured (so a port could define
+ * this to copying 2 u16_t's) - no NULL-pointer-checking needed. */
+#ifndef IPADDR2_COPY
+#define IPADDR2_COPY(dest, src) SMEMCPY(dest, src, sizeof(ip_addr_t))
+#endif
+
+/** Copy IP address - faster than ip_addr_set: no NULL check */
+#define ip_addr_copy(dest, src) ((dest).addr = (src).addr)
+/** Safely copy one IP address to another (src may be NULL) */
+#define ip_addr_set(dest, src) ((dest)->addr = \
+ ((src) == NULL ? 0 : \
+ (src)->addr))
+/** Set complete address to zero */
+#define ip_addr_set_zero(ipaddr) ((ipaddr)->addr = 0)
+/** Set address to IPADDR_ANY (no need for htonl()) */
+#define ip_addr_set_any(ipaddr) ((ipaddr)->addr = IPADDR_ANY)
+/** Set address to loopback address */
+#define ip_addr_set_loopback(ipaddr) ((ipaddr)->addr = PP_HTONL(IPADDR_LOOPBACK))
+/** Safely copy one IP address to another and change byte order
+ * from host- to network-order. */
+#define ip_addr_set_hton(dest, src) ((dest)->addr = \
+ ((src) == NULL ? 0:\
+ htonl((src)->addr)))
+/** IPv4 only: set the IP address given as an u32_t */
+#define ip4_addr_set_u32(dest_ipaddr, src_u32) ((dest_ipaddr)->addr = (src_u32))
+/** IPv4 only: get the IP address as an u32_t */
+#define ip4_addr_get_u32(src_ipaddr) ((src_ipaddr)->addr)
+
+/** Get the network address by combining host address with netmask */
+#define ip_addr_get_network(target, host, netmask) ((target)->addr = ((host)->addr) & ((netmask)->addr))
+
+/**
+ * Determine if two address are on the same network.
+ *
+ * @arg addr1 IP address 1
+ * @arg addr2 IP address 2
+ * @arg mask network identifier mask
+ * @return !0 if the network identifiers of both address match
+ */
+#define ip_addr_netcmp(addr1, addr2, mask) (((addr1)->addr & \
+ (mask)->addr) == \
+ ((addr2)->addr & \
+ (mask)->addr))
+#define ip_addr_cmp(addr1, addr2) ((addr1)->addr == (addr2)->addr)
+
+#define ip_addr_isany(addr1) ((addr1) == NULL || (addr1)->addr == IPADDR_ANY)
+
+#define ip_addr_isbroadcast(ipaddr, netif) ip4_addr_isbroadcast((ipaddr)->addr, (netif))
+u8_t ip4_addr_isbroadcast(u32_t addr, const struct netif *netif);
+
+#define ip_addr_netmask_valid(netmask) ip4_addr_netmask_valid((netmask)->addr)
+u8_t ip4_addr_netmask_valid(u32_t netmask);
+
+#define ip_addr_ismulticast(addr1) (((addr1)->addr & PP_HTONL(0xf0000000UL)) == PP_HTONL(0xe0000000UL))
+
+#define ip_addr_islinklocal(addr1) (((addr1)->addr & PP_HTONL(0xffff0000UL)) == PP_HTONL(0xa9fe0000UL))
+
+#define ip_addr_debug_print(debug, ipaddr) \
+ LWIP_DEBUGF(debug, ("%" U16_F ".%" U16_F ".%" U16_F ".%" U16_F, \
+ ipaddr != NULL ? ip4_addr1_16(ipaddr) : 0, \
+ ipaddr != NULL ? ip4_addr2_16(ipaddr) : 0, \
+ ipaddr != NULL ? ip4_addr3_16(ipaddr) : 0, \
+ ipaddr != NULL ? ip4_addr4_16(ipaddr) : 0))
+
+/* Get one byte from the 4-byte address */
+#define ip4_addr1(ipaddr) (((u8_t*)(ipaddr))[0])
+#define ip4_addr2(ipaddr) (((u8_t*)(ipaddr))[1])
+#define ip4_addr3(ipaddr) (((u8_t*)(ipaddr))[2])
+#define ip4_addr4(ipaddr) (((u8_t*)(ipaddr))[3])
+/* These are cast to u16_t, with the intent that they are often arguments
+ * to printf using the U16_F format from cc.h. */
+#define ip4_addr1_16(ipaddr) ((u16_t)ip4_addr1(ipaddr))
+#define ip4_addr2_16(ipaddr) ((u16_t)ip4_addr2(ipaddr))
+#define ip4_addr3_16(ipaddr) ((u16_t)ip4_addr3(ipaddr))
+#define ip4_addr4_16(ipaddr) ((u16_t)ip4_addr4(ipaddr))
+
+#define IP4ADDR_STRLEN_MAX 16
+#define IPADDR_STRLEN_MAX IP4ADDR_STRLEN_MAX
+
+/** For backwards compatibility */
+#define ip_ntoa(ipaddr) ipaddr_ntoa(ipaddr)
+
+u32_t ipaddr_addr(const char *cp);
+int ipaddr_aton(const char *cp, ip_addr_t *addr);
+/** returns ptr to static buffer; not reentrant! */
+char *ipaddr_ntoa(const ip_addr_t *addr);
+char *ipaddr_ntoa_r(const ip_addr_t *addr, char *buf, int buflen);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_IP_ADDR_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/ip_frag.h b/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/ip_frag.h
new file mode 100644
index 00000000..47eca9f4
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/ipv4/lwip/ip_frag.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Jani Monoses <jani@iv.ro>
+ *
+ */
+
+#ifndef __LWIP_IP_FRAG_H__
+#define __LWIP_IP_FRAG_H__
+
+#include "lwip/opt.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if IP_REASSEMBLY
+/* The IP reassembly timer interval in milliseconds. */
+#define IP_TMR_INTERVAL 1000
+
+/* IP reassembly helper struct.
+ * This is exported because memp needs to know the size.
+ */
+struct ip_reassdata {
+ struct ip_reassdata *next;
+ struct pbuf *p;
+ struct ip_hdr iphdr;
+ u16_t datagram_len;
+ u8_t flags;
+ u8_t timer;
+};
+
+void ip_reass_init(void);
+void ip_reass_tmr(void);
+struct pbuf * ip_reass(struct pbuf *p);
+#endif /* IP_REASSEMBLY */
+
+#if IP_FRAG
+#if !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF
+/** A custom pbuf that holds a reference to another pbuf, which is freed
+ * when this custom pbuf is freed. This is used to create a custom PBUF_REF
+ * that points into the original pbuf. */
+#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 */
+
+err_t ip_frag(struct pbuf *p, struct netif *netif, ip_addr_t *dest);
+#endif /* IP_FRAG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_IP_FRAG_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/dhcp6.h b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/dhcp6.h
new file mode 100644
index 00000000..4b905c54
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/dhcp6.h
@@ -0,0 +1,58 @@
+/**
+ * @file
+ *
+ * IPv6 address autoconfiguration as per RFC 4862.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ * IPv6 address autoconfiguration as per RFC 4862.
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef __LWIP_IP6_DHCP6_H__
+#define __LWIP_IP6_DHCP6_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6_DHCP6 /* don't build if not configured for use in lwipopts.h */
+
+
+struct dhcp6
+{
+ /*TODO: implement DHCP6*/
+};
+
+#endif /* LWIP_IPV6_DHCP6 */
+
+#endif /* __LWIP_IP6_DHCP6_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/ethip6.h b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/ethip6.h
new file mode 100644
index 00000000..e7f412b4
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/ethip6.h
@@ -0,0 +1,68 @@
+/**
+ * @file
+ *
+ * Ethernet output for IPv6. Uses ND tables for link-layer addressing.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef __LWIP_ETHIP6_H__
+#define __LWIP_ETHIP6_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6 && LWIP_ETHERNET /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+#include "lwip/netif.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+
+err_t ethip6_output(struct netif *netif, struct pbuf *q, ip6_addr_t *ip6addr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 && LWIP_ETHERNET */
+
+#endif /* __LWIP_ETHIP6_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/icmp6.h b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/icmp6.h
new file mode 100644
index 00000000..04d6351a
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/icmp6.h
@@ -0,0 +1,171 @@
+/**
+ * @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 VBOX /* Clang 11 takes offence when member addresses are taken (id). Packing not needed, so just dispense with it. */
+struct icmp6_echo_hdr {
+ u8_t type;
+ u8_t code;
+ u16_t chksum;
+ u16_t id;
+ u16_t seqno;
+};
+AssertCompileSize(struct icmp6_echo_hdr, sizeof(uint32_t) * 2);
+#else
+#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
+#endif /* VBOX */
+
+
+#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);
+#if LWIP_CONNECTION_PROXY
+void icmp6_proxy_input(struct pbuf *p, struct netif *inp);
+#endif
+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);
+
+#if LWIP_CONNECTION_PROXY
+typedef void (*ping6_proxy_fn)(void *arg, struct pbuf *p);
+void ping6_proxy_accept(ping6_proxy_fn callback, void *arg);
+#endif
+
+#endif /* LWIP_ICMP6 && LWIP_IPV6 */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+
+#endif /* __LWIP_ICMP6_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/inet6.h b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/inet6.h
new file mode 100644
index 00000000..8359521b
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/inet6.h
@@ -0,0 +1,92 @@
+/**
+ * @file
+ *
+ * INET v6 addresses.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+#ifndef __LWIP_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.u8_addr
+};
+
+#define IN6ADDR_ANY_INIT {0,0,0,0}
+#define IN6ADDR_LOOPBACK_INIT {0,0,0,PP_HTONL(1)}
+
+
+#define inet6_addr_from_ip6addr(target_in6addr, source_ip6addr) {(target_in6addr)->un.u32_addr[0] = (source_ip6addr)->addr[0]; \
+ (target_in6addr)->un.u32_addr[1] = (source_ip6addr)->addr[1]; \
+ (target_in6addr)->un.u32_addr[2] = (source_ip6addr)->addr[2]; \
+ (target_in6addr)->un.u32_addr[3] = (source_ip6addr)->addr[3];}
+#define inet6_addr_to_ip6addr(target_ip6addr, source_in6addr) {(target_ip6addr)->addr[0] = (source_in6addr)->un.u32_addr[0]; \
+ (target_ip6addr)->addr[1] = (source_in6addr)->un.u32_addr[1]; \
+ (target_ip6addr)->addr[2] = (source_in6addr)->un.u32_addr[2]; \
+ (target_ip6addr)->addr[3] = (source_in6addr)->un.u32_addr[3];}
+/* ATTENTION: the next define only works because both in6_addr and ip6_addr_t are an u32_t[4] effectively! */
+#define inet6_addr_to_ip6addr_p(target_ip6addr_p, source_in6addr) ((target_ip6addr_p) = (ip6_addr_t*)(source_in6addr))
+
+/* directly map this to the lwip internal functions */
+#define inet6_aton(cp, addr) ip6addr_aton(cp, (ip6_addr_t*)addr)
+#define inet6_ntoa(addr) ip6addr_ntoa((ip6_addr_t*)&(addr))
+#define inet6_ntoa_r(addr, buf, buflen) ip6addr_ntoa_r((ip6_addr_t*)&(addr), buf, buflen)
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 */
+
+#endif /* __LWIP_INET6_H__ */
+
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/ip6.h b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/ip6.h
new file mode 100644
index 00000000..75eb61b1
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/ip6.h
@@ -0,0 +1,201 @@
+/**
+ * @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_IP6_H__
+#define __LWIP_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/def.h"
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define IP6_HLEN 40
+
+#define IP6_NEXTH_HOPBYHOP 0
+#define IP6_NEXTH_TCP 6
+#define IP6_NEXTH_UDP 17
+#define IP6_NEXTH_ENCAPS 41
+#define IP6_NEXTH_ROUTING 43
+#define IP6_NEXTH_FRAGMENT 44
+#define IP6_NEXTH_ICMP6 58
+#define IP6_NEXTH_NONE 59
+#define IP6_NEXTH_DESTOPTS 60
+#define IP6_NEXTH_UDPLITE 136
+
+
+/* The IPv6 header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_hdr {
+ /* version / traffic class / flow label */
+ PACK_STRUCT_FIELD(u32_t _v_tc_fl);
+ /* payload length */
+ PACK_STRUCT_FIELD(u16_t _plen);
+ /* next header */
+ PACK_STRUCT_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);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/* Hop-by-hop router alert option. */
+#define IP6_HBH_HLEN 8
+#define IP6_PAD1_OPTION 0
+#define IP6_PADN_ALERT_OPTION 1
+#define IP6_ROUTER_ALERT_OPTION 5
+#define IP6_ROUTER_ALERT_VALUE_MLD 0
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_hbh_hdr {
+ /* next header */
+ PACK_STRUCT_FIELD(u8_t _nexth);
+ /* header length */
+ PACK_STRUCT_FIELD(u8_t _hlen);
+ /* router alert option type */
+ PACK_STRUCT_FIELD(u8_t _ra_opt_type);
+ /* router alert option data len */
+ PACK_STRUCT_FIELD(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);
+ /* PadN option data len */
+ PACK_STRUCT_FIELD(u8_t _padn_opt_dlen);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/* Fragment header. */
+#define IP6_FRAG_HLEN 8
+#define IP6_FRAG_OFFSET_MASK 0xfff8
+#define IP6_FRAG_MORE_FLAG 0x0001
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ip6_frag_hdr {
+ /* next header */
+ PACK_STRUCT_FIELD(u8_t _nexth);
+ /* reserved */
+ PACK_STRUCT_FIELD(u8_t reserved);
+ /* fragment offset */
+ PACK_STRUCT_FIELD(u16_t _fragment_offset);
+ /* fragmented packet identification */
+ PACK_STRUCT_FIELD(u32_t _identification);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define IP6H_V(hdr) ((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_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_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 LWIP_CONNECTION_PROXY
+typedef int (*proxy_ip6_divert_hook_fn)(struct netif *netif, ip6_addr_t *dest);
+extern proxy_ip6_divert_hook_fn proxy_ip6_divert_hook;
+#endif
+
+#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__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/ip6_addr.h b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/ip6_addr.h
new file mode 100644
index 00000000..4e5f4a25
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/ip6_addr.h
@@ -0,0 +1,289 @@
+/**
+ * @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_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_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] = (PP_HTONL(0xff000000UL) | (if_id));}while(0)
+
+#define ip6_addr_cmp_solicitednode(ip6addr, sn_addr) (((ip6addr)->addr[0] == PP_HTONL(0xff020000UL)) && \
+ ((ip6addr)->addr[1] == 0) && \
+ ((ip6addr)->addr[2] == PP_HTONL(0x00000001UL)) && \
+ ((ip6addr)->addr[3] == (PP_HTONL(0xff000000UL) | (sn_addr)->addr[3])))
+
+/* IPv6 address states. */
+#define IP6_ADDR_INVALID 0x00
+#define IP6_ADDR_TENTATIVE 0x08
+#define IP6_ADDR_TENTATIVE_1 0x09 /* 1 probe sent */
+#define IP6_ADDR_TENTATIVE_2 0x0a /* 2 probes sent */
+#define IP6_ADDR_TENTATIVE_3 0x0b /* 3 probes sent */
+#define IP6_ADDR_TENTATIVE_4 0x0c /* 4 probes sent */
+#define IP6_ADDR_TENTATIVE_5 0x0d /* 5 probes sent */
+#define IP6_ADDR_TENTATIVE_6 0x0e /* 6 probes sent */
+#define IP6_ADDR_TENTATIVE_7 0x0f /* 7 probes sent */
+#define IP6_ADDR_VALID 0x10
+#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))
+
+#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_IP6_ADDR_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/ip6_frag.h b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/ip6_frag.h
new file mode 100644
index 00000000..95e917e6
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/ip6_frag.h
@@ -0,0 +1,105 @@
+/**
+ * @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_IP6_FRAG_H__
+#define __LWIP_IP6_FRAG_H__
+
+#include "lwip/opt.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.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
+
+struct ip6_reass_helper;
+
+/* IPv6 reassembly struct.
+ * This is exported because memp needs to know the size.
+ */
+struct ip6_reassdata {
+ struct ip6_reassdata *next;
+ struct ip6_reass_helper *iprh;
+ struct ip6_hdr *iphdr0;
+ struct ip6_hdr iphdr;
+ u32_t identification;
+ u16_t datagram_len;
+ 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 */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_IP6_FRAG_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/mld6.h b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/mld6.h
new file mode 100644
index 00000000..1953f29f
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/mld6.h
@@ -0,0 +1,120 @@
+/**
+ * @file
+ *
+ * Multicast listener discovery for IPv6. Aims to be compliant with RFC 2710.
+ * No support for MLDv2.
+ */
+
+/*
+ * Copyright (c) 2010 Inico Technologies Ltd.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Ivan Delamer <delamer@inicotech.com>
+ *
+ *
+ * Please coordinate changes and requests with Ivan Delamer
+ * <delamer@inicotech.com>
+ */
+
+#ifndef __LWIP_MLD6_H__
+#define __LWIP_MLD6_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_IPV6_MLD && LWIP_IPV6 /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+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 */
+ u8_t last_reporter_flag;
+ /** current state of the group */
+ u8_t group_state;
+ /** timer for reporting */
+ u16_t timer;
+ /** counter of simultaneous uses */
+ u8_t use;
+};
+
+/** 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);
+void mld6_input(struct pbuf *p, struct netif *inp);
+err_t mld6_joingroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr);
+err_t mld6_netif_joingroup(struct netif *netif, ip6_addr_t *groupaddr);
+err_t mld6_leavegroup(ip6_addr_t *srcaddr, ip6_addr_t *groupaddr);
+err_t mld6_netif_leavegroup(struct netif *netif, ip6_addr_t *groupaddr);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6_MLD && LWIP_IPV6 */
+
+#endif /* __LWIP_MLD6_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/nd6.h b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/nd6.h
new file mode 100644
index 00000000..c8e62f9a
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/ipv6/lwip/nd6.h
@@ -0,0 +1,367 @@
+/**
+ * @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_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/ip6_addr.h"
+#include "lwip/netif.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_FIELD(u16_t chksum);
+ PACK_STRUCT_FIELD(u32_t reserved);
+ PACK_STRUCT_FIELD(ip6_addr_p_t target_address);
+ /* Options follow. */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** Neighbor advertisement message header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct na_header {
+ PACK_STRUCT_FIELD(u8_t type);
+ PACK_STRUCT_FIELD(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);
+ /* Options follow. */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+#define ND6_FLAG_ROUTER (0x80)
+#define ND6_FLAG_SOLICITED (0x40)
+#define ND6_FLAG_OVERRIDE (0x20)
+
+/** Router solicitation message header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct rs_header {
+ PACK_STRUCT_FIELD(u8_t type);
+ PACK_STRUCT_FIELD(u8_t code);
+ PACK_STRUCT_FIELD(u16_t chksum);
+ PACK_STRUCT_FIELD(u32_t reserved);
+ /* Options follow. */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** Router advertisement message header. */
+#define ND6_RA_FLAG_MANAGED_ADDR_CONFIG (0x80)
+#define ND6_RA_FLAG_OTHER_STATEFUL_CONFIG (0x40)
+#define ND6_RA_FLAG_HOME_AGENT (0x20)
+#define ND6_RA_PREFERENCE_MASK (0x18)
+#define ND6_RA_PREFERENCE_HIGH (0x08)
+#define ND6_RA_PREFERENCE_MEDIUM (0x00)
+#define ND6_RA_PREFERENCE_LOW (0x18)
+#define ND6_RA_PREFERENCE_DISABLED (0x10)
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct ra_header {
+ PACK_STRUCT_FIELD(u8_t type);
+ PACK_STRUCT_FIELD(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_FIELD(u16_t router_lifetime);
+ PACK_STRUCT_FIELD(u32_t reachable_time);
+ PACK_STRUCT_FIELD(u32_t retrans_timer);
+ /* Options follow. */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** Redirect message header. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct redirect_header {
+ PACK_STRUCT_FIELD(u8_t type);
+ PACK_STRUCT_FIELD(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);
+ /* Options follow. */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** Link-layer address option. */
+#define ND6_OPTION_TYPE_SOURCE_LLADDR (0x01)
+#define ND6_OPTION_TYPE_TARGET_LLADDR (0x02)
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct lladdr_option {
+ PACK_STRUCT_FIELD(u8_t type);
+ PACK_STRUCT_FIELD(u8_t length);
+ PACK_STRUCT_FIELD(u8_t addr[NETIF_MAX_HWADDR_LEN]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** Prefix information option. */
+#define ND6_OPTION_TYPE_PREFIX_INFO (0x03)
+#define ND6_PREFIX_FLAG_ON_LINK (0x80)
+#define ND6_PREFIX_FLAG_AUTONOMOUS (0x40)
+#define ND6_PREFIX_FLAG_ROUTER_ADDRESS (0x20)
+#define ND6_PREFIX_FLAG_SITE_PREFIX (0x10)
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct prefix_option {
+ PACK_STRUCT_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_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_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** Redirected header option. */
+#define ND6_OPTION_TYPE_REDIR_HDR (0x04)
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct redirected_header_option {
+ PACK_STRUCT_FIELD(u8_t type);
+ PACK_STRUCT_FIELD(u8_t length);
+ PACK_STRUCT_FIELD(u8_t reserved[6]);
+ /* Portion of redirected packet follows. */
+ /* PACK_STRUCT_FIELD(u8_t redirected[8]); */
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** MTU option. */
+#define ND6_OPTION_TYPE_MTU (0x05)
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct mtu_option {
+ PACK_STRUCT_FIELD(u8_t type);
+ PACK_STRUCT_FIELD(u8_t length);
+ PACK_STRUCT_FIELD(u16_t reserved);
+ PACK_STRUCT_FIELD(u32_t mtu);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** Route information option. */
+#define ND6_OPTION_TYPE_ROUTE_INFO (24)
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct route_option {
+ PACK_STRUCT_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_FIELD(u32_t route_lifetime);
+ PACK_STRUCT_FIELD(ip6_addr_p_t prefix);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/** 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;
+
+#if LWIP_ND6_PROXY
+typedef int (*proxy_na_hook_fn)(struct netif *netif, ip6_addr_t *tgt);
+extern proxy_na_hook_fn proxy_na_hook;
+#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 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_IPV6 */
+
+#endif /* __LWIP_ND6_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/api.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/api.h
new file mode 100644
index 00000000..ac58eebb
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/api.h
@@ -0,0 +1,338 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_API_H__
+#define __LWIP_API_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include <stddef.h> /* for size_t */
+
+#include "lwip/netbuf.h"
+#include "lwip/sys.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Throughout this file, IP addresses and port numbers are expected to be in
+ * the same byte order as in the corresponding pcb.
+ */
+
+/* Flags for netconn_write (u8_t) */
+#define NETCONN_NOFLAG 0x00
+#define NETCONN_NOCOPY 0x00 /* Only for source code compatibility */
+#define NETCONN_COPY 0x01
+#define NETCONN_MORE 0x02
+#define NETCONN_DONTBLOCK 0x04
+
+/* Flags for struct netconn.flags (u8_t) */
+/** TCP: when data passed to netconn_write doesn't fit into the send buffer,
+ this temporarily stores whether to wake up the original application task
+ if data couldn't be sent in the first try. */
+#define NETCONN_FLAG_WRITE_DELAYED 0x01
+/** Should this netconn avoid blocking? */
+#define NETCONN_FLAG_NON_BLOCKING 0x02
+/** Was the last connect action a non-blocking one? */
+#define NETCONN_FLAG_IN_NONBLOCKING_CONNECT 0x04
+/** If this is set, a TCP netconn must call netconn_recved() to update
+ the TCP receive window (done automatically if not set). */
+#define NETCONN_FLAG_NO_AUTO_RECVED 0x08
+/** If a nonblocking write has been rejected before, poll_tcp needs to
+ check if the netconn is writable again */
+#define NETCONN_FLAG_CHECK_WRITESPACE 0x10
+#if LWIP_IPV6
+/** If this flag is set then only IPv6 communication is allowed on the
+ netconn. As per RFC#3493 this features defaults to OFF allowing
+ dual-stack usage by default. */
+#define NETCONN_FLAG_IPV6_V6ONLY 0x20
+#endif /* LWIP_IPV6 */
+
+
+/* Helpers to process several netconn_types by the same code */
+#define NETCONNTYPE_GROUP(t) ((t)&0xF0)
+#define NETCONNTYPE_DATAGRAM(t) ((t)&0xE0)
+#if LWIP_IPV6
+#define NETCONN_TYPE_IPV6 0x08
+#define NETCONNTYPE_ISIPV6(t) ((t)&0x08)
+#define NETCONNTYPE_ISUDPLITE(t) (((t)&0xF7) == NETCONN_UDPLITE)
+#define NETCONNTYPE_ISUDPNOCHKSUM(t) (((t)&0xF7) == NETCONN_UDPNOCHKSUM)
+#else /* LWIP_IPV6 */
+#define NETCONNTYPE_ISUDPLITE(t) ((t) == NETCONN_UDPLITE)
+#define NETCONNTYPE_ISUDPNOCHKSUM(t) ((t) == NETCONN_UDPNOCHKSUM)
+#endif /* LWIP_IPV6 */
+
+/** Protocol family and type of the netconn */
+enum netconn_type {
+ NETCONN_INVALID = 0,
+ /* NETCONN_TCP Group */
+ NETCONN_TCP = 0x10,
+#if LWIP_IPV6
+ NETCONN_TCP_IPV6 = NETCONN_TCP | NETCONN_TYPE_IPV6 /* 0x18 */,
+#endif /* LWIP_IPV6 */
+ /* NETCONN_UDP Group */
+ NETCONN_UDP = 0x20,
+ NETCONN_UDPLITE = 0x21,
+ NETCONN_UDPNOCHKSUM = 0x22,
+#if LWIP_IPV6
+ NETCONN_UDP_IPV6 = NETCONN_UDP | NETCONN_TYPE_IPV6 /* 0x28 */,
+ NETCONN_UDPLITE_IPV6 = NETCONN_UDPLITE | NETCONN_TYPE_IPV6 /* 0x29 */,
+ NETCONN_UDPNOCHKSUM_IPV6 = NETCONN_UDPNOCHKSUM | NETCONN_TYPE_IPV6 /* 0x2a */,
+#endif /* LWIP_IPV6 */
+ /* NETCONN_RAW Group */
+ NETCONN_RAW = 0x40
+#if LWIP_IPV6
+ ,
+ NETCONN_RAW_IPV6 = NETCONN_RAW | NETCONN_TYPE_IPV6 /* 0x48 */
+#endif /* LWIP_IPV6 */
+};
+
+/** Current state of the netconn. Non-TCP netconns are always
+ * in state NETCONN_NONE! */
+enum netconn_state {
+ NETCONN_NONE,
+ NETCONN_WRITE,
+ NETCONN_LISTEN,
+ NETCONN_CONNECT,
+ NETCONN_CLOSE
+};
+
+/** Use to inform the callback function about changes */
+enum netconn_evt {
+ NETCONN_EVT_RCVPLUS,
+ NETCONN_EVT_RCVMINUS,
+ NETCONN_EVT_SENDPLUS,
+ NETCONN_EVT_SENDMINUS,
+ NETCONN_EVT_ERROR
+};
+
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+/** Used for netconn_join_leave_group() */
+enum netconn_igmp {
+ NETCONN_JOIN,
+ NETCONN_LEAVE
+};
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+/* forward-declare some structs to avoid to include their headers */
+struct ip_pcb;
+struct tcp_pcb;
+struct udp_pcb;
+struct raw_pcb;
+struct netconn;
+struct api_msg_msg;
+
+/** A callback prototype to inform about events for a netconn */
+typedef void (* netconn_callback)(struct netconn *, enum netconn_evt, u16_t len);
+
+/** A netconn descriptor */
+struct netconn {
+ /** type of the netconn (TCP, UDP or RAW) */
+ enum netconn_type type;
+ /** current state of the netconn */
+ enum netconn_state state;
+ /** the lwIP internal protocol control block */
+ union {
+ struct ip_pcb *ip;
+ struct tcp_pcb *tcp;
+ struct udp_pcb *udp;
+ struct raw_pcb *raw;
+ } pcb;
+ /** the last error this netconn had */
+ err_t last_err;
+ /** sem that is used to synchroneously execute functions in the core context */
+ sys_sem_t op_completed;
+ /** mbox where received packets are stored until they are fetched
+ by the netconn application thread (can grow quite big) */
+ sys_mbox_t recvmbox;
+#if LWIP_TCP
+ /** mbox where new connections are stored until processed
+ by the application thread */
+ sys_mbox_t acceptmbox;
+#endif /* LWIP_TCP */
+ /** only used for socket layer */
+#if LWIP_SOCKET
+ int socket;
+#endif /* LWIP_SOCKET */
+#if LWIP_SO_SNDTIMEO
+ /** timeout to wait for sending data (which means enqueueing data for sending
+ in internal buffers) */
+ s32_t send_timeout;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVTIMEO
+ /** timeout to wait for new data to be received
+ (or connections to arrive for listening netconns) */
+ int recv_timeout;
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+ /** maximum amount of bytes queued in recvmbox
+ not used for TCP: adjust TCP_WND instead! */
+ int recv_bufsize;
+ /** number of bytes currently in recvmbox to be received,
+ tested against recv_bufsize to limit bytes on recvmbox
+ for UDP and RAW, used for FIONREAD */
+ s16_t recv_avail;
+#endif /* LWIP_SO_RCVBUF */
+ /** flags holding more netconn-internal state, see NETCONN_FLAG_* defines */
+ u8_t flags;
+#if LWIP_TCP
+ /** TCP: when data passed to netconn_write doesn't fit into the send buffer,
+ this temporarily stores how much is already sent. */
+ size_t write_offset;
+ /** TCP: when data passed to netconn_write doesn't fit into the send buffer,
+ this temporarily stores the message.
+ Also used during connect and close. */
+ struct api_msg_msg *current_msg;
+#endif /* LWIP_TCP */
+ /** A callback function that is informed about events for this netconn */
+ netconn_callback callback;
+};
+
+/** Register an Network connection event */
+#define API_EVENT(c,e,l) if (c->callback) { \
+ (*c->callback)(c, e, l); \
+ }
+
+/** Set conn->last_err to err but don't overwrite fatal errors */
+#define NETCONN_SET_SAFE_ERR(conn, err) do { \
+ SYS_ARCH_DECL_PROTECT(lev); \
+ SYS_ARCH_PROTECT(lev); \
+ if (!ERR_IS_FATAL((conn)->last_err)) { \
+ (conn)->last_err = err; \
+ } \
+ SYS_ARCH_UNPROTECT(lev); \
+} while(0);
+
+/* Network connection functions: */
+#define netconn_new(t) netconn_new_with_proto_and_callback(t, 0, NULL)
+#define netconn_new_with_callback(t, c) netconn_new_with_proto_and_callback(t, 0, c)
+struct
+netconn *netconn_new_with_proto_and_callback(enum netconn_type t, u8_t proto,
+ netconn_callback callback);
+err_t netconn_delete(struct netconn *conn);
+/** Get the type of a netconn (as enum netconn_type). */
+#define netconn_type(conn) (conn->type)
+
+err_t netconn_getaddr(struct netconn *conn, ip_addr_t *addr,
+ u16_t *port, u8_t local);
+#define netconn_peer(c,i,p) netconn_getaddr(c,i,p,0)
+#define netconn_addr(c,i,p) netconn_getaddr(c,i,p,1)
+
+err_t netconn_bind(struct netconn *conn, ip_addr_t *addr, u16_t port);
+err_t netconn_connect(struct netconn *conn, ip_addr_t *addr, u16_t port);
+err_t netconn_disconnect (struct netconn *conn);
+err_t netconn_listen_with_backlog(struct netconn *conn, u8_t backlog);
+#define netconn_listen(conn) netconn_listen_with_backlog(conn, TCP_DEFAULT_LISTEN_BACKLOG)
+err_t netconn_accept(struct netconn *conn, struct netconn **new_conn);
+err_t netconn_recv(struct netconn *conn, struct netbuf **new_buf);
+err_t netconn_recv_tcp_pbuf(struct netconn *conn, struct pbuf **new_buf);
+void netconn_recved(struct netconn *conn, u32_t length);
+err_t netconn_sendto(struct netconn *conn, struct netbuf *buf,
+ ip_addr_t *addr, u16_t port);
+err_t netconn_send(struct netconn *conn, struct netbuf *buf);
+err_t netconn_write_partly(struct netconn *conn, const void *dataptr, size_t size,
+ u8_t apiflags, size_t *bytes_written);
+#define netconn_write(conn, dataptr, size, apiflags) \
+ netconn_write_partly(conn, dataptr, size, apiflags, NULL)
+err_t netconn_close(struct netconn *conn);
+err_t netconn_shutdown(struct netconn *conn, u8_t shut_rx, u8_t shut_tx);
+
+#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);
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+#if LWIP_DNS
+err_t netconn_gethostbyname(const char *name, ip_addr_t *addr);
+#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)
+#define netconn_recv_bufsize(conn) ((conn)->recv_bufsize)
+
+/** Set the blocking status of netconn calls (@todo: write/send is missing) */
+#define netconn_set_nonblocking(conn, val) do { if(val) { \
+ (conn)->flags |= NETCONN_FLAG_NON_BLOCKING; \
+} else { \
+ (conn)->flags &= ~ NETCONN_FLAG_NON_BLOCKING; }} while(0)
+/** Get the blocking status of netconn calls (@todo: write/send is missing) */
+#define netconn_is_nonblocking(conn) (((conn)->flags & NETCONN_FLAG_NON_BLOCKING) != 0)
+
+/** TCP: Set the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */
+#define netconn_set_noautorecved(conn, val) do { if(val) { \
+ (conn)->flags |= NETCONN_FLAG_NO_AUTO_RECVED; \
+} else { \
+ (conn)->flags &= ~ NETCONN_FLAG_NO_AUTO_RECVED; }} while(0)
+/** TCP: Get the no-auto-recved status of netconn calls (see NETCONN_FLAG_NO_AUTO_RECVED) */
+#define netconn_get_noautorecved(conn) (((conn)->flags & NETCONN_FLAG_NO_AUTO_RECVED) != 0)
+
+#if LWIP_SO_SNDTIMEO
+/** Set the send timeout in milliseconds */
+#define netconn_set_sendtimeout(conn, timeout) ((conn)->send_timeout = (timeout))
+/** Get the send timeout in milliseconds */
+#define netconn_get_sendtimeout(conn) ((conn)->send_timeout)
+#endif /* LWIP_SO_SNDTIMEO */
+#if LWIP_SO_RCVTIMEO
+/** Set the receive timeout in milliseconds */
+#define netconn_set_recvtimeout(conn, timeout) ((conn)->recv_timeout = (timeout))
+/** Get the receive timeout in milliseconds */
+#define netconn_get_recvtimeout(conn) ((conn)->recv_timeout)
+#endif /* LWIP_SO_RCVTIMEO */
+#if LWIP_SO_RCVBUF
+/** Set the receive buffer in bytes */
+#define netconn_set_recvbufsize(conn, recvbufsize) ((conn)->recv_bufsize = (recvbufsize))
+/** Get the receive buffer in bytes */
+#define netconn_get_recvbufsize(conn) ((conn)->recv_bufsize)
+#endif /* LWIP_SO_RCVBUF*/
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_NETCONN */
+
+#endif /* __LWIP_API_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/api_msg.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/api_msg.h
new file mode 100644
index 00000000..8268036a
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/api_msg.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_API_MSG_H__
+#define __LWIP_API_MSG_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_NETCONN /* don't build if not configured for use in lwipopts.h */
+
+#include <stddef.h> /* for size_t */
+
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+#include "lwip/sys.h"
+#include "lwip/igmp.h"
+#include "lwip/api.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* For the netconn API, these values are use as a bitmask! */
+#define NETCONN_SHUT_RD 1
+#define NETCONN_SHUT_WR 2
+#define NETCONN_SHUT_RDWR (NETCONN_SHUT_RD | NETCONN_SHUT_WR)
+
+/* IP addresses and port numbers are expected to be in
+ * the same byte order as in the corresponding pcb.
+ */
+/** This struct includes everything that is necessary to execute a function
+ for a netconn in another thread context (mainly used to process netconns
+ in the tcpip_thread context to be thread safe). */
+struct api_msg_msg {
+ /** The netconn which to process - always needed: it includes the semaphore
+ which is used to block the application thread until the function finished. */
+ struct netconn *conn;
+ /** The return value of the function executed in tcpip_thread. */
+ err_t err;
+ /** Depending on the executed function, one of these union members is used */
+ union {
+ /** used for lwip_netconn_do_send */
+ struct netbuf *b;
+ /** used for lwip_netconn_do_newconn */
+ struct {
+ u8_t proto;
+ } n;
+ /** used for lwip_netconn_do_bind and lwip_netconn_do_connect */
+ struct {
+ ip_addr_t *ipaddr;
+ u16_t port;
+ } bc;
+ /** used for lwip_netconn_do_getaddr */
+ struct {
+ ipX_addr_t *ipaddr;
+ u16_t *port;
+ u8_t local;
+ } ad;
+ /** used for lwip_netconn_do_write */
+ struct {
+ const void *dataptr;
+ size_t len;
+ u8_t apiflags;
+#if LWIP_SO_SNDTIMEO
+ u32_t time_started;
+#endif /* LWIP_SO_SNDTIMEO */
+ } w;
+ /** used for lwip_netconn_do_recv */
+ struct {
+ u32_t len;
+ } r;
+ /** used for lwip_netconn_do_close (/shutdown) */
+ struct {
+ u8_t shut;
+ } sd;
+#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;
+ enum netconn_igmp join_or_leave;
+ } jl;
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+#if TCP_LISTEN_BACKLOG
+ struct {
+ u8_t backlog;
+ } lb;
+#endif /* TCP_LISTEN_BACKLOG */
+ } msg;
+};
+
+/** This struct contains a function to execute in another thread context and
+ a struct api_msg_msg that serves as an argument for this function.
+ This is passed to tcpip_apimsg to execute functions in tcpip_thread context. */
+struct api_msg {
+ /** function to execute in tcpip_thread context */
+ void (* function)(struct api_msg_msg *msg);
+ /** arguments for this function */
+ struct api_msg_msg msg;
+};
+
+#if LWIP_DNS
+/** As lwip_netconn_do_gethostbyname requires more arguments but doesn't require a netconn,
+ it has its own struct (to avoid struct api_msg getting bigger than necessary).
+ lwip_netconn_do_gethostbyname must be called using tcpip_callback instead of tcpip_apimsg
+ (see netconn_gethostbyname). */
+struct dns_api_msg {
+ /** Hostname to query or dotted IP address string */
+ const char *name;
+ /** Rhe resolved address is stored here */
+ ip_addr_t *addr;
+ /** This semaphore is posted when the name is resolved, the application thread
+ should wait on it. */
+ sys_sem_t *sem;
+ /** Errors are given back here */
+ err_t *err;
+};
+#endif /* LWIP_DNS */
+
+void 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);
+#if LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD)
+void lwip_netconn_do_join_leave_group( struct api_msg_msg *msg);
+#endif /* LWIP_IGMP || (LWIP_IPV6 && LWIP_IPV6_MLD) */
+
+#if LWIP_DNS
+void lwip_netconn_do_gethostbyname(void *arg);
+#endif /* LWIP_DNS */
+
+struct netconn* netconn_alloc(enum netconn_type t, netconn_callback callback);
+void netconn_free(struct netconn *conn);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_NETCONN */
+
+#endif /* __LWIP_API_MSG_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/arch.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/arch.h
new file mode 100644
index 00000000..4d6df773
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/arch.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_ARCH_H__
+#define __LWIP_ARCH_H__
+
+#ifndef LITTLE_ENDIAN
+#define LITTLE_ENDIAN 1234
+#endif
+
+#ifndef BIG_ENDIAN
+#define BIG_ENDIAN 4321
+#endif
+
+#include "arch/cc.h"
+
+/** Temporary: define format string for size_t if not defined in cc.h */
+#ifndef SZT_F
+#define SZT_F U32_F
+#endif /* SZT_F */
+/** Temporary upgrade helper: define format string for u8_t as hex if not
+ defined in cc.h */
+#ifndef X8_F
+#define X8_F "02x"
+#endif /* X8_F */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef PACK_STRUCT_BEGIN
+#define PACK_STRUCT_BEGIN
+#endif /* PACK_STRUCT_BEGIN */
+
+#ifndef PACK_STRUCT_END
+#define PACK_STRUCT_END
+#endif /* PACK_STRUCT_END */
+
+#ifndef PACK_STRUCT_FIELD
+#define PACK_STRUCT_FIELD(x) x
+#endif /* PACK_STRUCT_FIELD */
+
+
+#ifndef LWIP_UNUSED_ARG
+#define LWIP_UNUSED_ARG(x) (void)x
+#endif /* LWIP_UNUSED_ARG */
+
+
+#ifdef LWIP_PROVIDE_ERRNO
+
+#define EPERM 1 /* Operation not permitted */
+#define ENOENT 2 /* No such file or directory */
+#define ESRCH 3 /* No such process */
+#define EINTR 4 /* Interrupted system call */
+#define EIO 5 /* I/O error */
+#define ENXIO 6 /* No such device or address */
+#define E2BIG 7 /* Arg list too long */
+#define ENOEXEC 8 /* Exec format error */
+#define EBADF 9 /* Bad file number */
+#define ECHILD 10 /* No child processes */
+#define EAGAIN 11 /* Try again */
+#define ENOMEM 12 /* Out of memory */
+#define EACCES 13 /* Permission denied */
+#define EFAULT 14 /* Bad address */
+#define ENOTBLK 15 /* Block device required */
+#define EBUSY 16 /* Device or resource busy */
+#define EEXIST 17 /* File exists */
+#define EXDEV 18 /* Cross-device link */
+#define ENODEV 19 /* No such device */
+#define ENOTDIR 20 /* Not a directory */
+#define EISDIR 21 /* Is a directory */
+#define EINVAL 22 /* Invalid argument */
+#define ENFILE 23 /* File table overflow */
+#define EMFILE 24 /* Too many open files */
+#define ENOTTY 25 /* Not a typewriter */
+#define ETXTBSY 26 /* Text file busy */
+#define EFBIG 27 /* File too large */
+#define ENOSPC 28 /* No space left on device */
+#define ESPIPE 29 /* Illegal seek */
+#define EROFS 30 /* Read-only file system */
+#define EMLINK 31 /* Too many links */
+#define EPIPE 32 /* Broken pipe */
+#define EDOM 33 /* Math argument out of domain of func */
+#define ERANGE 34 /* Math result not representable */
+#define EDEADLK 35 /* Resource deadlock would occur */
+#define ENAMETOOLONG 36 /* File name too long */
+#define ENOLCK 37 /* No record locks available */
+#define ENOSYS 38 /* Function not implemented */
+#define ENOTEMPTY 39 /* Directory not empty */
+#define ELOOP 40 /* Too many symbolic links encountered */
+#define EWOULDBLOCK EAGAIN /* Operation would block */
+#define ENOMSG 42 /* No message of desired type */
+#define EIDRM 43 /* Identifier removed */
+#define ECHRNG 44 /* Channel number out of range */
+#define EL2NSYNC 45 /* Level 2 not synchronized */
+#define EL3HLT 46 /* Level 3 halted */
+#define EL3RST 47 /* Level 3 reset */
+#define ELNRNG 48 /* Link number out of range */
+#define EUNATCH 49 /* Protocol driver not attached */
+#define ENOCSI 50 /* No CSI structure available */
+#define EL2HLT 51 /* Level 2 halted */
+#define EBADE 52 /* Invalid exchange */
+#define EBADR 53 /* Invalid request descriptor */
+#define EXFULL 54 /* Exchange full */
+#define ENOANO 55 /* No anode */
+#define EBADRQC 56 /* Invalid request code */
+#define EBADSLT 57 /* Invalid slot */
+
+#define EDEADLOCK EDEADLK
+
+#define EBFONT 59 /* Bad font file format */
+#define ENOSTR 60 /* Device not a stream */
+#define ENODATA 61 /* No data available */
+#define ETIME 62 /* Timer expired */
+#define ENOSR 63 /* Out of streams resources */
+#define ENONET 64 /* Machine is not on the network */
+#define ENOPKG 65 /* Package not installed */
+#define EREMOTE 66 /* Object is remote */
+#define ENOLINK 67 /* Link has been severed */
+#define EADV 68 /* Advertise error */
+#define ESRMNT 69 /* Srmount error */
+#define ECOMM 70 /* Communication error on send */
+#define EPROTO 71 /* Protocol error */
+#define EMULTIHOP 72 /* Multihop attempted */
+#define EDOTDOT 73 /* RFS specific error */
+#define EBADMSG 74 /* Not a data message */
+#define EOVERFLOW 75 /* Value too large for defined data type */
+#define ENOTUNIQ 76 /* Name not unique on network */
+#define EBADFD 77 /* File descriptor in bad state */
+#define EREMCHG 78 /* Remote address changed */
+#define ELIBACC 79 /* Can not access a needed shared library */
+#define ELIBBAD 80 /* Accessing a corrupted shared library */
+#define ELIBSCN 81 /* .lib section in a.out corrupted */
+#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
+#define ELIBEXEC 83 /* Cannot exec a shared library directly */
+#define EILSEQ 84 /* Illegal byte sequence */
+#define ERESTART 85 /* Interrupted system call should be restarted */
+#define ESTRPIPE 86 /* Streams pipe error */
+#define EUSERS 87 /* Too many users */
+#define ENOTSOCK 88 /* Socket operation on non-socket */
+#define EDESTADDRREQ 89 /* Destination address required */
+#define EMSGSIZE 90 /* Message too long */
+#define EPROTOTYPE 91 /* Protocol wrong type for socket */
+#define ENOPROTOOPT 92 /* Protocol not available */
+#define EPROTONOSUPPORT 93 /* Protocol not supported */
+#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
+#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
+#define EPFNOSUPPORT 96 /* Protocol family not supported */
+#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
+#define EADDRINUSE 98 /* Address already in use */
+#define EADDRNOTAVAIL 99 /* Cannot assign requested address */
+#define ENETDOWN 100 /* Network is down */
+#define ENETUNREACH 101 /* Network is unreachable */
+#define ENETRESET 102 /* Network dropped connection because of reset */
+#define ECONNABORTED 103 /* Software caused connection abort */
+#define ECONNRESET 104 /* Connection reset by peer */
+#define ENOBUFS 105 /* No buffer space available */
+#define EISCONN 106 /* Transport endpoint is already connected */
+#define ENOTCONN 107 /* Transport endpoint is not connected */
+#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
+#define ETOOMANYREFS 109 /* Too many references: cannot splice */
+#define ETIMEDOUT 110 /* Connection timed out */
+#define ECONNREFUSED 111 /* Connection refused */
+#define EHOSTDOWN 112 /* Host is down */
+#define EHOSTUNREACH 113 /* No route to host */
+#define EALREADY 114 /* Operation already in progress */
+#define EINPROGRESS 115 /* Operation now in progress */
+#define ESTALE 116 /* Stale NFS file handle */
+#define EUCLEAN 117 /* Structure needs cleaning */
+#define ENOTNAM 118 /* Not a XENIX named type file */
+#define ENAVAIL 119 /* No XENIX semaphores available */
+#define EISNAM 120 /* Is a named type file */
+#define EREMOTEIO 121 /* Remote I/O error */
+#define EDQUOT 122 /* Quota exceeded */
+
+#define ENOMEDIUM 123 /* No medium found */
+#define EMEDIUMTYPE 124 /* Wrong medium type */
+
+#ifndef errno
+extern int errno;
+#endif
+
+#endif /* LWIP_PROVIDE_ERRNO */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_ARCH_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/debug.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/debug.h
new file mode 100644
index 00000000..fe66d4e4
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/debug.h
@@ -0,0 +1,105 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_DEBUG_H__
+#define __LWIP_DEBUG_H__
+
+#include "lwip/arch.h"
+#include "lwip/opt.h"
+
+/** lower two bits indicate debug level
+ * - 0 all
+ * - 1 warning
+ * - 2 serious
+ * - 3 severe
+ */
+#define LWIP_DBG_LEVEL_ALL 0x00
+#define LWIP_DBG_LEVEL_OFF LWIP_DBG_LEVEL_ALL /* compatibility define only */
+#define LWIP_DBG_LEVEL_WARNING 0x01 /* bad checksums, dropped packets, ... */
+#define LWIP_DBG_LEVEL_SERIOUS 0x02 /* memory allocation failures, ... */
+#define LWIP_DBG_LEVEL_SEVERE 0x03
+#define LWIP_DBG_MASK_LEVEL 0x03
+
+/** flag for LWIP_DEBUGF to enable that debug message */
+#define LWIP_DBG_ON 0x80U
+/** flag for LWIP_DEBUGF to disable that debug message */
+#define LWIP_DBG_OFF 0x00U
+
+/** flag for LWIP_DEBUGF indicating a tracing message (to follow program flow) */
+#define LWIP_DBG_TRACE 0x40U
+/** flag for LWIP_DEBUGF indicating a state debug message (to follow module states) */
+#define LWIP_DBG_STATE 0x20U
+/** flag for LWIP_DEBUGF indicating newly added code, not thoroughly tested yet */
+#define LWIP_DBG_FRESH 0x10U
+/** flag for LWIP_DEBUGF to halt after printing this debug message */
+#define LWIP_DBG_HALT 0x08U
+
+#ifndef LWIP_NOASSERT
+#define LWIP_ASSERT(message, assertion) do { if(!(assertion)) \
+ LWIP_PLATFORM_ASSERT(message); } while(0)
+#else /* LWIP_NOASSERT */
+#define LWIP_ASSERT(message, assertion)
+#endif /* LWIP_NOASSERT */
+
+/** if "expression" isn't true, then print "message" and execute "handler" expression */
+#ifndef LWIP_ERROR
+#define LWIP_ERROR(message, expression, handler) do { if (!(expression)) { \
+ LWIP_PLATFORM_ASSERT(message); handler;}} while(0)
+#endif /* LWIP_ERROR */
+
+#ifdef LWIP_DEBUG
+/* let VBox override it to use logging */
+#ifndef LWIP_DEBUGF
+/** print debug message only if debug message type is enabled...
+ * AND is of correct type AND is at least LWIP_DBG_LEVEL
+ */
+#define LWIP_DEBUGF(debug, message) do { \
+ if ( \
+ ((debug) & LWIP_DBG_ON) && \
+ ((debug) & LWIP_DBG_TYPES_ON) && \
+ ((s16_t)((debug) & LWIP_DBG_MASK_LEVEL) >= LWIP_DBG_MIN_LEVEL)) { \
+ LWIP_PLATFORM_DIAG(message); \
+ if ((debug) & LWIP_DBG_HALT) { \
+ while(1); \
+ } \
+ } \
+ } while(0)
+#endif /* !LWIP_DEBUGF */
+#else /* LWIP_DEBUG */
+#ifdef LWIP_DEBUG
+/* sanity check, this is going to fail with undefined variables anyway */
+#error LWIP_DEBUGF is defined without LWIP_DEBUG
+#endif
+#define LWIP_DEBUGF(debug, message)
+#endif /* LWIP_DEBUG */
+
+#endif /* __LWIP_DEBUG_H__ */
+
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/def.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/def.h
new file mode 100644
index 00000000..73a1b560
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/def.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_DEF_H__
+#define __LWIP_DEF_H__
+
+/* arch.h might define NULL already */
+#include "lwip/arch.h"
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define LWIP_MAX(x , y) (((x) > (y)) ? (x) : (y))
+#define LWIP_MIN(x , y) (((x) < (y)) ? (x) : (y))
+
+#ifndef NULL
+#define NULL ((void *)0)
+#endif
+
+/* Endianess-optimized shifting of two u8_t to create one u16_t */
+#if BYTE_ORDER == LITTLE_ENDIAN
+#define LWIP_MAKE_U16(a, b) ((a << 8) | b)
+#else
+#define LWIP_MAKE_U16(a, b) ((b << 8) | a)
+#endif
+
+#ifndef LWIP_PLATFORM_BYTESWAP
+#define LWIP_PLATFORM_BYTESWAP 0
+#endif
+
+#ifndef LWIP_PREFIX_BYTEORDER_FUNCS
+/* workaround for naming collisions on some platforms */
+
+#ifdef htons
+#undef htons
+#endif /* htons */
+#ifdef htonl
+#undef htonl
+#endif /* htonl */
+#ifdef ntohs
+#undef ntohs
+#endif /* ntohs */
+#ifdef ntohl
+#undef ntohl
+#endif /* ntohl */
+
+#define htons(x) lwip_htons(x)
+#define ntohs(x) lwip_ntohs(x)
+#define htonl(x) lwip_htonl(x)
+#define ntohl(x) lwip_ntohl(x)
+#endif /* LWIP_PREFIX_BYTEORDER_FUNCS */
+
+#if BYTE_ORDER == BIG_ENDIAN
+#define lwip_htons(x) (x)
+#define lwip_ntohs(x) (x)
+#define lwip_htonl(x) (x)
+#define lwip_ntohl(x) (x)
+#define PP_HTONS(x) (x)
+#define PP_NTOHS(x) (x)
+#define PP_HTONL(x) (x)
+#define PP_NTOHL(x) (x)
+#else /* BYTE_ORDER != BIG_ENDIAN */
+#if LWIP_PLATFORM_BYTESWAP
+#define lwip_htons(x) LWIP_PLATFORM_HTONS(x)
+#define lwip_ntohs(x) LWIP_PLATFORM_HTONS(x)
+#define lwip_htonl(x) LWIP_PLATFORM_HTONL(x)
+#define lwip_ntohl(x) LWIP_PLATFORM_HTONL(x)
+#else /* LWIP_PLATFORM_BYTESWAP */
+u16_t lwip_htons(u16_t x);
+u16_t lwip_ntohs(u16_t x);
+u32_t lwip_htonl(u32_t x);
+u32_t lwip_ntohl(u32_t x);
+#endif /* LWIP_PLATFORM_BYTESWAP */
+
+/* These macros should be calculated by the preprocessor and are used
+ with compile-time constants only (so that there is no little-endian
+ overhead at runtime). */
+#define PP_HTONS(x) ((((x) & 0xff) << 8) | (((x) & 0xff00) >> 8))
+#define PP_NTOHS(x) PP_HTONS(x)
+#define PP_HTONL(x) ((((x) & 0xff) << 24) | \
+ (((x) & 0xff00) << 8) | \
+ (((x) & 0xff0000UL) >> 8) | \
+ (((x) & 0xff000000UL) >> 24))
+#define PP_NTOHL(x) PP_HTONL(x)
+
+#endif /* BYTE_ORDER == BIG_ENDIAN */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_DEF_H__ */
+
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/dhcp.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/dhcp.h
new file mode 100644
index 00000000..32d93381
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/dhcp.h
@@ -0,0 +1,242 @@
+/** @file
+ */
+
+#ifndef __LWIP_DHCP_H__
+#define __LWIP_DHCP_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_DHCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/netif.h"
+#include "lwip/udp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** period (in seconds) of the application calling dhcp_coarse_tmr() */
+#define DHCP_COARSE_TIMER_SECS 60
+/** period (in milliseconds) of the application calling dhcp_coarse_tmr() */
+#define DHCP_COARSE_TIMER_MSECS (DHCP_COARSE_TIMER_SECS * 1000UL)
+/** period (in milliseconds) of the application calling dhcp_fine_tmr() */
+#define DHCP_FINE_TIMER_MSECS 500
+
+#define DHCP_CHADDR_LEN 16U
+#define DHCP_SNAME_LEN 64U
+#define DHCP_FILE_LEN 128U
+
+struct dhcp
+{
+ /** transaction identifier of last sent request */
+ u32_t xid;
+ /** our connection to the DHCP server */
+ struct udp_pcb *pcb;
+ /** incoming msg */
+ struct dhcp_msg *msg_in;
+ /** current DHCP state machine state */
+ u8_t state;
+ /** retries of current request */
+ u8_t tries;
+#if LWIP_DHCP_AUTOIP_COOP
+ u8_t autoip_coop_state;
+#endif
+ u8_t subnet_mask_given;
+
+ struct pbuf *p_out; /* pbuf of outcoming msg */
+ struct dhcp_msg *msg_out; /* outgoing msg */
+ u16_t options_out_len; /* outgoing msg options length */
+ u16_t request_timeout; /* #ticks with period DHCP_FINE_TIMER_SECS for request timeout */
+ u16_t t1_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for renewal time */
+ u16_t t2_timeout; /* #ticks with period DHCP_COARSE_TIMER_SECS for rebind time */
+ ip_addr_t server_ip_addr; /* dhcp server address that offered this lease */
+ ip_addr_t offered_ip_addr;
+ ip_addr_t offered_sn_mask;
+ ip_addr_t offered_gw_addr;
+
+ u32_t offered_t0_lease; /* lease period (in seconds) */
+ u32_t offered_t1_renew; /* recommended renew time (usually 50% of lease period) */
+ u32_t offered_t2_rebind; /* recommended rebind time (usually 66% of lease period) */
+ /* @todo: LWIP_DHCP_BOOTP_FILE configuration option?
+ integrate with possible TFTP-client for booting? */
+#if LWIP_DHCP_BOOTP_FILE
+ ip_addr_t offered_si_addr;
+ char boot_file_name[DHCP_FILE_LEN];
+#endif /* LWIP_DHCP_BOOTPFILE */
+};
+
+/* MUST be compiled with "pack structs" or equivalent! */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+/** minimum set of fields of any DHCP message */
+struct dhcp_msg
+{
+ PACK_STRUCT_FIELD(u8_t op);
+ PACK_STRUCT_FIELD(u8_t htype);
+ PACK_STRUCT_FIELD(u8_t hlen);
+ PACK_STRUCT_FIELD(u8_t hops);
+ PACK_STRUCT_FIELD(u32_t xid);
+ PACK_STRUCT_FIELD(u16_t secs);
+ PACK_STRUCT_FIELD(u16_t flags);
+ PACK_STRUCT_FIELD(ip_addr_p_t ciaddr);
+ PACK_STRUCT_FIELD(ip_addr_p_t yiaddr);
+ PACK_STRUCT_FIELD(ip_addr_p_t siaddr);
+ PACK_STRUCT_FIELD(ip_addr_p_t giaddr);
+ PACK_STRUCT_FIELD(u8_t chaddr[DHCP_CHADDR_LEN]);
+ PACK_STRUCT_FIELD(u8_t sname[DHCP_SNAME_LEN]);
+ PACK_STRUCT_FIELD(u8_t file[DHCP_FILE_LEN]);
+ PACK_STRUCT_FIELD(u32_t cookie);
+#define DHCP_MIN_OPTIONS_LEN 68U
+/** make sure user does not configure this too small */
+#if ((defined(DHCP_OPTIONS_LEN)) && (DHCP_OPTIONS_LEN < DHCP_MIN_OPTIONS_LEN))
+# undef DHCP_OPTIONS_LEN
+#endif
+/** allow this to be configured in lwipopts.h, but not too small */
+#if (!defined(DHCP_OPTIONS_LEN))
+/** set this to be sufficient for your options in outgoing DHCP msgs */
+# define DHCP_OPTIONS_LEN DHCP_MIN_OPTIONS_LEN
+#endif
+ PACK_STRUCT_FIELD(u8_t options[DHCP_OPTIONS_LEN]);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+void dhcp_set_struct(struct netif *netif, struct dhcp *dhcp);
+/** Remove a struct dhcp previously set to the netif using dhcp_set_struct() */
+#define dhcp_remove_struct(netif) do { (netif)->dhcp = NULL; } while(0)
+void dhcp_cleanup(struct netif *netif);
+/** start DHCP configuration */
+err_t dhcp_start(struct netif *netif);
+/** enforce early lease renewal (not needed normally)*/
+err_t dhcp_renew(struct netif *netif);
+/** release the DHCP lease, usually called before dhcp_stop()*/
+err_t dhcp_release(struct netif *netif);
+/** stop DHCP configuration */
+void dhcp_stop(struct netif *netif);
+/** inform server of our manual IP address */
+void dhcp_inform(struct netif *netif);
+/** Handle a possible change in the network configuration */
+void dhcp_network_changed(struct netif *netif);
+
+/** if enabled, check whether the offered IP address is not in use, using ARP */
+#if DHCP_DOES_ARP_CHECK
+void dhcp_arp_reply(struct netif *netif, ip_addr_t *addr);
+#endif
+
+/** to be called every minute */
+void dhcp_coarse_tmr(void);
+/** to be called every half second */
+void dhcp_fine_tmr(void);
+
+/** DHCP message item offsets and length */
+#define DHCP_OP_OFS 0
+#define DHCP_HTYPE_OFS 1
+#define DHCP_HLEN_OFS 2
+#define DHCP_HOPS_OFS 3
+#define DHCP_XID_OFS 4
+#define DHCP_SECS_OFS 8
+#define DHCP_FLAGS_OFS 10
+#define DHCP_CIADDR_OFS 12
+#define DHCP_YIADDR_OFS 16
+#define DHCP_SIADDR_OFS 20
+#define DHCP_GIADDR_OFS 24
+#define DHCP_CHADDR_OFS 28
+#define DHCP_SNAME_OFS 44
+#define DHCP_FILE_OFS 108
+#define DHCP_MSG_LEN 236
+
+#define DHCP_COOKIE_OFS DHCP_MSG_LEN
+#define DHCP_OPTIONS_OFS (DHCP_MSG_LEN + 4)
+
+#define DHCP_CLIENT_PORT 68
+#define DHCP_SERVER_PORT 67
+
+/** DHCP client states */
+#define DHCP_OFF 0
+#define DHCP_REQUESTING 1
+#define DHCP_INIT 2
+#define DHCP_REBOOTING 3
+#define DHCP_REBINDING 4
+#define DHCP_RENEWING 5
+#define DHCP_SELECTING 6
+#define DHCP_INFORMING 7
+#define DHCP_CHECKING 8
+#define DHCP_PERMANENT 9
+#define DHCP_BOUND 10
+/** not yet implemented #define DHCP_RELEASING 11 */
+#define DHCP_BACKING_OFF 12
+
+/** AUTOIP cooperatation flags */
+#define DHCP_AUTOIP_COOP_STATE_OFF 0
+#define DHCP_AUTOIP_COOP_STATE_ON 1
+
+#define DHCP_BOOTREQUEST 1
+#define DHCP_BOOTREPLY 2
+
+/** DHCP message types */
+#define DHCP_DISCOVER 1
+#define DHCP_OFFER 2
+#define DHCP_REQUEST 3
+#define DHCP_DECLINE 4
+#define DHCP_ACK 5
+#define DHCP_NAK 6
+#define DHCP_RELEASE 7
+#define DHCP_INFORM 8
+
+/** DHCP hardware type, currently only ethernet is supported */
+#define DHCP_HTYPE_ETH 1
+
+#define DHCP_MAGIC_COOKIE 0x63825363UL
+
+/* This is a list of options for BOOTP and DHCP, see RFC 2132 for descriptions */
+
+/** BootP options */
+#define DHCP_OPTION_PAD 0
+#define DHCP_OPTION_SUBNET_MASK 1 /* RFC 2132 3.3 */
+#define DHCP_OPTION_ROUTER 3
+#define DHCP_OPTION_DNS_SERVER 6
+#define DHCP_OPTION_HOSTNAME 12
+#define DHCP_OPTION_IP_TTL 23
+#define DHCP_OPTION_MTU 26
+#define DHCP_OPTION_BROADCAST 28
+#define DHCP_OPTION_TCP_TTL 37
+#define DHCP_OPTION_END 255
+
+/** DHCP options */
+#define DHCP_OPTION_REQUESTED_IP 50 /* RFC 2132 9.1, requested IP address */
+#define DHCP_OPTION_LEASE_TIME 51 /* RFC 2132 9.2, time in seconds, in 4 bytes */
+#define DHCP_OPTION_OVERLOAD 52 /* RFC2132 9.3, use file and/or sname field for options */
+
+#define DHCP_OPTION_MESSAGE_TYPE 53 /* RFC 2132 9.6, important for DHCP */
+#define DHCP_OPTION_MESSAGE_TYPE_LEN 1
+
+#define DHCP_OPTION_SERVER_ID 54 /* RFC 2132 9.7, server IP address */
+#define DHCP_OPTION_PARAMETER_REQUEST_LIST 55 /* RFC 2132 9.8, requested option types */
+
+#define DHCP_OPTION_MAX_MSG_SIZE 57 /* RFC 2132 9.10, message size accepted >= 576 */
+#define DHCP_OPTION_MAX_MSG_SIZE_LEN 2
+
+#define DHCP_OPTION_T1 58 /* T1 renewal time */
+#define DHCP_OPTION_T2 59 /* T2 rebinding time */
+#define DHCP_OPTION_US 60
+#define DHCP_OPTION_CLIENT_ID 61
+#define DHCP_OPTION_TFTP_SERVERNAME 66
+#define DHCP_OPTION_BOOTFILE 67
+
+/** possible combinations of overloading the file and sname fields with options */
+#define DHCP_OVERLOAD_NONE 0
+#define DHCP_OVERLOAD_FILE 1
+#define DHCP_OVERLOAD_SNAME 2
+#define DHCP_OVERLOAD_SNAME_FILE 3
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_DHCP */
+
+#endif /*__LWIP_DHCP_H__*/
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/dns.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/dns.h
new file mode 100644
index 00000000..6c7d9b07
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/dns.h
@@ -0,0 +1,124 @@
+/**
+ * lwip DNS resolver header file.
+
+ * Author: Jim Pettinato
+ * April 2007
+
+ * ported from uIP resolv.c Copyright (c) 2002-2003, Adam Dunkels.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __LWIP_DNS_H__
+#define __LWIP_DNS_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS /* don't build if not configured for use in lwipopts.h */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** DNS timer period */
+#define DNS_TMR_INTERVAL 1000
+
+/** DNS field TYPE used for "Resource Records" */
+#define DNS_RRTYPE_A 1 /* a host address */
+#define DNS_RRTYPE_NS 2 /* an authoritative name server */
+#define DNS_RRTYPE_MD 3 /* a mail destination (Obsolete - use MX) */
+#define DNS_RRTYPE_MF 4 /* a mail forwarder (Obsolete - use MX) */
+#define DNS_RRTYPE_CNAME 5 /* the canonical name for an alias */
+#define DNS_RRTYPE_SOA 6 /* marks the start of a zone of authority */
+#define DNS_RRTYPE_MB 7 /* a mailbox domain name (EXPERIMENTAL) */
+#define DNS_RRTYPE_MG 8 /* a mail group member (EXPERIMENTAL) */
+#define DNS_RRTYPE_MR 9 /* a mail rename domain name (EXPERIMENTAL) */
+#define DNS_RRTYPE_NULL 10 /* a null RR (EXPERIMENTAL) */
+#define DNS_RRTYPE_WKS 11 /* a well known service description */
+#define DNS_RRTYPE_PTR 12 /* a domain name pointer */
+#define DNS_RRTYPE_HINFO 13 /* host information */
+#define DNS_RRTYPE_MINFO 14 /* mailbox or mail list information */
+#define DNS_RRTYPE_MX 15 /* mail exchange */
+#define DNS_RRTYPE_TXT 16 /* text strings */
+
+/** DNS field CLASS used for "Resource Records" */
+#define DNS_RRCLASS_IN 1 /* the Internet */
+#define DNS_RRCLASS_CS 2 /* the CSNET class (Obsolete - used only for examples in some obsolete RFCs) */
+#define DNS_RRCLASS_CH 3 /* the CHAOS class */
+#define DNS_RRCLASS_HS 4 /* Hesiod [Dyer 87] */
+#define DNS_RRCLASS_FLUSH 0x800 /* Flush bit */
+
+/* The size used for the next line is rather a hack, but it prevents including socket.h in all files
+ that include memp.h, and that would possibly break portability (since socket.h defines some types
+ and constants possibly already define by the OS).
+ Calculation rule:
+ sizeof(struct addrinfo) + sizeof(struct sockaddr_in) + DNS_MAX_NAME_LENGTH + 1 byte zero-termination */
+#define NETDB_ELEM_SIZE (32 + 16 + DNS_MAX_NAME_LENGTH + 1)
+
+#if DNS_LOCAL_HOSTLIST
+/** struct used for local host-list */
+struct local_hostlist_entry {
+ /** static hostname */
+ const char *name;
+ /** static host address in network byteorder */
+ ip_addr_t addr;
+ struct local_hostlist_entry *next;
+};
+#if DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+#ifndef DNS_LOCAL_HOSTLIST_MAX_NAMELEN
+#define DNS_LOCAL_HOSTLIST_MAX_NAMELEN DNS_MAX_NAME_LENGTH
+#endif
+#define LOCALHOSTLIST_ELEM_SIZE ((sizeof(struct local_hostlist_entry) + DNS_LOCAL_HOSTLIST_MAX_NAMELEN + 1))
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+#endif /* DNS_LOCAL_HOSTLIST */
+
+/** Callback which is invoked when a hostname is found.
+ * A function of this type must be implemented by the application using the DNS resolver.
+ * @param name pointer to the name that was looked up.
+ * @param ipaddr pointer to an ip_addr_t containing the IP address of the hostname,
+ * or NULL if the name could not be found (or on any other error).
+ * @param callback_arg a user-specified callback argument passed to dns_gethostbyname
+*/
+typedef void (*dns_found_callback)(const char *name, ip_addr_t *ipaddr, void *callback_arg);
+
+void dns_init(void);
+void dns_tmr(void);
+void dns_setserver(u8_t numdns, ip_addr_t *dnsserver);
+ip_addr_t dns_getserver(u8_t numdns);
+err_t dns_gethostbyname(const char *hostname, ip_addr_t *addr,
+ dns_found_callback found, void *callback_arg);
+
+#if DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+int dns_local_removehost(const char *hostname, const ip_addr_t *addr);
+err_t dns_local_addhost(const char *hostname, const ip_addr_t *addr);
+#endif /* DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_DNS */
+
+#endif /* __LWIP_DNS_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/err.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/err.h
new file mode 100644
index 00000000..ac907729
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/err.h
@@ -0,0 +1,85 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_ERR_H__
+#define __LWIP_ERR_H__
+
+#include "lwip/opt.h"
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Define LWIP_ERR_T in cc.h if you want to use
+ * a different type for your platform (must be signed). */
+#ifdef LWIP_ERR_T
+typedef LWIP_ERR_T err_t;
+#else /* LWIP_ERR_T */
+typedef s8_t err_t;
+#endif /* LWIP_ERR_T*/
+
+/* Definitions for error constants. */
+
+#define ERR_OK 0 /* No error, everything OK. */
+#define ERR_MEM -1 /* Out of memory error. */
+#define ERR_BUF -2 /* Buffer error. */
+#define ERR_TIMEOUT -3 /* Timeout. */
+#define ERR_RTE -4 /* Routing problem. */
+#define ERR_INPROGRESS -5 /* Operation in progress */
+#define ERR_VAL -6 /* Illegal value. */
+#define ERR_WOULDBLOCK -7 /* Operation would block. */
+#define ERR_USE -8 /* Address in use. */
+#define ERR_ISCONN -9 /* Already connected. */
+
+#define ERR_IS_FATAL(e) ((e) < ERR_ISCONN)
+
+#define ERR_ABRT -10 /* Connection aborted. */
+#define ERR_RST -11 /* Connection reset. */
+#define ERR_CLSD -12 /* Connection closed. */
+#define ERR_CONN -13 /* Not connected. */
+
+#define ERR_ARG -14 /* Illegal argument. */
+
+#define ERR_IF -15 /* Low-level netif error */
+
+
+#ifdef LWIP_DEBUG
+extern const char *lwip_strerr(err_t err);
+#else
+#define lwip_strerr(x) ""
+#endif /* LWIP_DEBUG */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_ERR_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/inet_chksum.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/inet_chksum.h
new file mode 100644
index 00000000..e1687888
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/inet_chksum.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_INET_CHKSUM_H__
+#define __LWIP_INET_CHKSUM_H__
+
+#include "lwip/opt.h"
+
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+
+/** Swap the bytes in an u16_t: much like htons() for little-endian */
+#ifndef SWAP_BYTES_IN_WORD
+#if LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN)
+/* little endian and PLATFORM_BYTESWAP defined */
+#define SWAP_BYTES_IN_WORD(w) LWIP_PLATFORM_HTONS(w)
+#else /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN) */
+/* can't use htons on big endian (or PLATFORM_BYTESWAP not defined)... */
+#define SWAP_BYTES_IN_WORD(w) (((w) & 0xff) << 8) | (((w) & 0xff00) >> 8)
+#endif /* LWIP_PLATFORM_BYTESWAP && (BYTE_ORDER == LITTLE_ENDIAN)*/
+#endif /* SWAP_BYTES_IN_WORD */
+
+/** Split an u32_t in two u16_ts and add them up */
+#ifndef FOLD_U32T
+#define FOLD_U32T(u) (((u) >> 16) + ((u) & 0x0000ffffUL))
+#endif
+
+#if LWIP_CHECKSUM_ON_COPY
+/** Function-like macro: same as MEMCPY but returns the checksum of copied data
+ as u16_t */
+#ifndef LWIP_CHKSUM_COPY
+#define LWIP_CHKSUM_COPY(dst, src, len) lwip_chksum_copy(dst, src, len)
+#ifndef LWIP_CHKSUM_COPY_ALGORITHM
+#define LWIP_CHKSUM_COPY_ALGORITHM 1
+#endif /* LWIP_CHKSUM_COPY_ALGORITHM */
+#endif /* LWIP_CHKSUM_COPY */
+#else /* LWIP_CHECKSUM_ON_COPY */
+#define LWIP_CHKSUM_COPY_ALGORITHM 0
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+u16_t inet_chksum(void *dataptr, u16_t len);
+u16_t inet_chksum_pbuf(struct pbuf *p);
+u16_t inet_chksum_pseudo(struct pbuf *p, 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_IPV6
+u16_t ip6_chksum_pseudo(struct pbuf *p, u8_t proto, u16_t proto_len,
+ ip6_addr_t *src, 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 */
+
+#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 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_INET_H__ */
+
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/init.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/init.h
new file mode 100644
index 00000000..4e2e2856
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/init.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_INIT_H__
+#define __LWIP_INIT_H__
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** X.x.x: Major version of the stack */
+#define LWIP_VERSION_MAJOR 1U
+/** x.X.x: Minor version of the stack */
+#define LWIP_VERSION_MINOR 4U
+/** x.x.X: Revision of the stack */
+#define LWIP_VERSION_REVISION 1U
+/** For release candidates, this is set to 1..254
+ * For official releases, this is set to 255 (LWIP_RC_RELEASE)
+ * For development versions (CVS), this is set to 0 (LWIP_RC_DEVELOPMENT) */
+#define LWIP_VERSION_RC 0U
+
+/** LWIP_VERSION_RC is set to LWIP_RC_RELEASE for official releases */
+#define LWIP_RC_RELEASE 255U
+/** LWIP_VERSION_RC is set to LWIP_RC_DEVELOPMENT for CVS versions */
+#define LWIP_RC_DEVELOPMENT 0U
+
+#define LWIP_VERSION_IS_RELEASE (LWIP_VERSION_RC == LWIP_RC_RELEASE)
+#define LWIP_VERSION_IS_DEVELOPMENT (LWIP_VERSION_RC == LWIP_RC_DEVELOPMENT)
+#define LWIP_VERSION_IS_RC ((LWIP_VERSION_RC != LWIP_RC_RELEASE) && (LWIP_VERSION_RC != LWIP_RC_DEVELOPMENT))
+
+/** Provides the version of the stack */
+#define LWIP_VERSION (LWIP_VERSION_MAJOR << 24 | LWIP_VERSION_MINOR << 16 | \
+ LWIP_VERSION_REVISION << 8 | LWIP_VERSION_RC)
+
+/* Modules initialization */
+void lwip_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_INIT_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/ip.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/ip.h
new file mode 100644
index 00000000..b9139f26
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/ip.h
@@ -0,0 +1,262 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_IP_H__
+#define __LWIP_IP_H__
+
+#include "lwip/opt.h"
+
+#include "lwip/def.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+#include "lwip/netif.h"
+#include "lwip/ip4.h"
+#include "lwip/ip6.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* This is passed as the destination address to ip_output_if (not
+ to ip_output), meaning that an IP header already is constructed
+ in the pbuf. This is used when TCP retransmits. */
+#if 0
+/* XXX: "IP_HDRINCL" is a name of a socket option and the #undef below
+ is a good sign that reusing this name was rather unfortuate. Other
+ naming conflicts are mostly restricted to "lwip/sockets.h" but this
+ one is pretty nasty since it affects all code that uses lwip.
+ Rename it out of the way here and do undef/redef dance in the few
+ lwip files that use this name to minimize diffs to upstream. */
+#ifdef IP_HDRINCL
+#undef IP_HDRINCL
+#endif /* IP_HDRINCL */
+#endif /* XXX: 0 */
+#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 */
+
+#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 */
+
+/* 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 \
+ /* 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 \
+ /* link layer address resolution hint */ \
+ IP_PCB_ADDRHINT
+
+struct ip_pcb {
+/* Common members of all PCB types */
+ IP_PCB;
+};
+
+/*
+ * Option flags per-socket. These are the same like SO_XXX.
+ */
+/*#define SOF_DEBUG 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*/)
+
+/* 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. */
+ struct netif *current_netif;
+ /** Header of the input packet currently being processed. */
+ const struct ip_hdr *current_ip4_header;
+#if LWIP_IPV6
+ /** Header of the input IPv6 packet currently being processed. */
+ const 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;
+ /** Destination IP address of current_header */
+ ipX_addr_t current_iphdr_dest;
+};
+extern struct ip_globals ip_data;
+
+
+/** Get the interface that received the current packet.
+ * This function must only be called from a receive callback (udp_recv,
+ * raw_recv, tcp_accept). It will return NULL otherwise. */
+#define ip_current_netif() (ip_data.current_netif)
+/** Get the IP header of the current packet.
+ * This function must only be called from a receive callback (udp_recv,
+ * raw_recv, tcp_accept). It will return NULL otherwise. */
+#define ip_current_header() (ip_data.current_ip4_header)
+/** 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)
+/** Destination IP address of current_header */
+#define ipX_current_dest_addr() (&ip_data.current_iphdr_dest)
+
+#if 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() (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))
+/** Destination IPv6 address of current_header */
+#define ip6_current_dest_addr() (ipX_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()))
+/** 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)
+
+/** Source IP4 address of current_header */
+#define ip_current_src_addr() (ipX_2_ip(&ip_data.current_iphdr_src))
+/** Destination IP4 address of current_header */
+#define ip_current_dest_addr() (ipX_2_ip(&ip_data.current_iphdr_dest))
+
+#else /* LWIP_IPV6 */
+
+/** Always returns FALSE when only supporting IPv4 */
+#define ip_current_is_v6() 0
+/** Get the transport layer protocol */
+#define ip_current_header_proto() IPH_PROTO(ip_current_header())
+/** Get the transport layer header */
+#define ipX_next_header_ptr() ((void*)((u8_t*)ip_current_header() + ip_current_header_tot_len()))
+/** Source IP4 address of current_header */
+#define ip_current_src_addr() (&ip_data.current_iphdr_src)
+/** Destination IP4 address of current_header */
+#define ip_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)
+/** Union destination address of current_header */
+#define ipX_current_dest_addr() (&ip_data.current_iphdr_dest)
+
+/** Gets an IP pcb option (SOF_* flags) */
+#define ip_get_option(pcb, opt) ((pcb)->so_options & (opt))
+/** Sets an IP pcb option (SOF_* flags) */
+#define ip_set_option(pcb, opt) ((pcb)->so_options |= (opt))
+/** Resets an IP pcb option (SOF_* flags) */
+#define ip_reset_option(pcb, opt) ((pcb)->so_options &= ~(opt))
+
+#if LWIP_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) ? \
+ 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)
+#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); \
+}while(0)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_IP_H__ */
+
+
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/ip_addr.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/ip_addr.h
new file mode 100644
index 00000000..7bd03cbd
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/ip_addr.h
@@ -0,0 +1,130 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_IP_ADDR_H__
+#define __LWIP_IP_ADDR_H__
+
+#include "lwip/opt.h"
+#include "lwip/def.h"
+
+#include "lwip/ip4_addr.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#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 */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_IP_ADDR_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/mem.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/mem.h
new file mode 100644
index 00000000..5bb906b6
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/mem.h
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_MEM_H__
+#define __LWIP_MEM_H__
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if MEM_LIBC_MALLOC
+
+#include <stddef.h> /* for size_t */
+
+typedef size_t mem_size_t;
+#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 */
+
+/* MEM_SIZE would have to be aligned, but using 64000 here instead of
+ * 65535 leaves some room for alignment...
+ */
+#if MEM_SIZE > 64000L
+typedef u32_t mem_size_t;
+#define MEM_SIZE_F U32_F
+#else
+typedef u16_t mem_size_t;
+#define MEM_SIZE_F U16_F
+#endif /* MEM_SIZE > 64000 */
+
+#if MEM_USE_POOLS
+/** 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__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/memp.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/memp.h
new file mode 100644
index 00000000..f0d07399
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/memp.h
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#ifndef __LWIP_MEMP_H__
+#define __LWIP_MEMP_H__
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Create the list of all memory pools managed by memp. MEMP_MAX represents a NULL pool at the end */
+typedef enum {
+#define LWIP_MEMPOOL(name,num,size,desc) MEMP_##name,
+#include "lwip/memp_std.h"
+ MEMP_MAX
+} memp_t;
+
+#if MEM_USE_POOLS
+/* Use a helper type to get the start and end of the user "memory pools" for mem_malloc */
+typedef enum {
+ /* Get the first (via:
+ MEMP_POOL_HELPER_START = ((u8_t) 1*MEMP_POOL_A + 0*MEMP_POOL_B + 0*MEMP_POOL_C + 0)*/
+ MEMP_POOL_HELPER_FIRST = ((u8_t)
+#define LWIP_MEMPOOL(name,num,size,desc)
+#define LWIP_MALLOC_MEMPOOL_START 1
+#define LWIP_MALLOC_MEMPOOL(num, size) * MEMP_POOL_##size + 0
+#define LWIP_MALLOC_MEMPOOL_END
+#include "lwip/memp_std.h"
+ ) ,
+ /* Get the last (via:
+ MEMP_POOL_HELPER_END = ((u8_t) 0 + MEMP_POOL_A*0 + MEMP_POOL_B*0 + MEMP_POOL_C*1) */
+ MEMP_POOL_HELPER_LAST = ((u8_t)
+#define LWIP_MEMPOOL(name,num,size,desc)
+#define LWIP_MALLOC_MEMPOOL_START
+#define LWIP_MALLOC_MEMPOOL(num, size) 0 + MEMP_POOL_##size *
+#define LWIP_MALLOC_MEMPOOL_END 1
+#include "lwip/memp_std.h"
+ )
+} memp_pool_helper_t;
+
+/* The actual start and stop values are here (cast them over)
+ We use this helper type and these defines so we can avoid using const memp_t values */
+#define MEMP_POOL_FIRST ((memp_t) MEMP_POOL_HELPER_FIRST)
+#define MEMP_POOL_LAST ((memp_t) MEMP_POOL_HELPER_LAST)
+#endif /* MEM_USE_POOLS */
+
+#if MEMP_MEM_MALLOC || MEM_USE_POOLS
+extern const u16_t memp_sizes[MEMP_MAX];
+#endif /* MEMP_MEM_MALLOC || MEM_USE_POOLS */
+
+#if MEMP_MEM_MALLOC
+
+#include "mem.h"
+
+#define memp_init()
+#define memp_malloc(type) mem_malloc(memp_sizes[type])
+#define memp_free(type, mem) mem_free(mem)
+
+#else /* MEMP_MEM_MALLOC */
+
+#if MEM_USE_POOLS
+/** This structure is used to save the pool one element came from. */
+struct memp_malloc_helper
+{
+ memp_t poolnr;
+};
+#endif /* MEM_USE_POOLS */
+
+void memp_init(void);
+
+#if MEMP_OVERFLOW_CHECK
+void *memp_malloc_fn(memp_t type, const char* file, const int line);
+#define memp_malloc(t) memp_malloc_fn((t), __FILE__, __LINE__)
+#else
+void *memp_malloc(memp_t type);
+#endif
+void memp_free(memp_t type, void *mem);
+
+#endif /* MEMP_MEM_MALLOC */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_MEMP_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/memp_std.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/memp_std.h
new file mode 100644
index 00000000..8e1c99c4
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/memp_std.h
@@ -0,0 +1,135 @@
+/*
+ * SETUP: Make sure we define everything we will need.
+ *
+ * We have create three types of pools:
+ * 1) MEMPOOL - standard pools
+ * 2) MALLOC_MEMPOOL - to be used by mem_malloc in mem.c
+ * 3) PBUF_MEMPOOL - a mempool of pbuf's, so include space for the pbuf struct
+ *
+ * If the include'r doesn't require any special treatment of each of the types
+ * above, then will declare #2 & #3 to be just standard mempools.
+ */
+#ifndef LWIP_MALLOC_MEMPOOL
+/* This treats "malloc pools" just like any other pool.
+ The pools are a little bigger to provide 'size' as the amount of user data. */
+#define LWIP_MALLOC_MEMPOOL(num, size) LWIP_MEMPOOL(POOL_##size, num, (size + LWIP_MEM_ALIGN_SIZE(sizeof(struct memp_malloc_helper))), "MALLOC_"#size)
+#define LWIP_MALLOC_MEMPOOL_START
+#define LWIP_MALLOC_MEMPOOL_END
+#endif /* LWIP_MALLOC_MEMPOOL */
+
+#ifndef LWIP_PBUF_MEMPOOL
+/* This treats "pbuf pools" just like any other pool.
+ * Allocates buffers for a pbuf struct AND a payload size */
+#define LWIP_PBUF_MEMPOOL(name, num, payload, desc) LWIP_MEMPOOL(name, num, (MEMP_ALIGN_SIZE(sizeof(struct pbuf)) + MEMP_ALIGN_SIZE(payload)), desc)
+#endif /* LWIP_PBUF_MEMPOOL */
+
+
+/*
+ * A list of internal pools used by LWIP.
+ *
+ * LWIP_MEMPOOL(pool_name, number_elements, element_size, pool_description)
+ * creates a pool name MEMP_pool_name. description is used in stats.c
+ */
+#if LWIP_RAW
+LWIP_MEMPOOL(RAW_PCB, MEMP_NUM_RAW_PCB, sizeof(struct raw_pcb), "RAW_PCB")
+#endif /* LWIP_RAW */
+
+#if LWIP_UDP
+LWIP_MEMPOOL(UDP_PCB, MEMP_NUM_UDP_PCB, sizeof(struct udp_pcb), "UDP_PCB")
+#endif /* LWIP_UDP */
+
+#if LWIP_TCP
+LWIP_MEMPOOL(TCP_PCB, MEMP_NUM_TCP_PCB, sizeof(struct tcp_pcb), "TCP_PCB")
+LWIP_MEMPOOL(TCP_PCB_LISTEN, MEMP_NUM_TCP_PCB_LISTEN, sizeof(struct tcp_pcb_listen), "TCP_PCB_LISTEN")
+LWIP_MEMPOOL(TCP_SEG, MEMP_NUM_TCP_SEG, sizeof(struct tcp_seg), "TCP_SEG")
+#endif /* LWIP_TCP */
+
+#if IP_REASSEMBLY
+LWIP_MEMPOOL(REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip_reassdata), "REASSDATA")
+#endif /* IP_REASSEMBLY */
+#if (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF) || LWIP_IPV6_FRAG
+LWIP_MEMPOOL(FRAG_PBUF, MEMP_NUM_FRAG_PBUF, sizeof(struct pbuf_custom_ref),"FRAG_PBUF")
+#endif /* IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF */
+
+#if LWIP_NETCONN
+LWIP_MEMPOOL(NETBUF, MEMP_NUM_NETBUF, sizeof(struct netbuf), "NETBUF")
+LWIP_MEMPOOL(NETCONN, MEMP_NUM_NETCONN, sizeof(struct netconn), "NETCONN")
+#endif /* LWIP_NETCONN */
+
+#if NO_SYS==0
+LWIP_MEMPOOL(TCPIP_MSG_API, MEMP_NUM_TCPIP_MSG_API, sizeof(struct tcpip_msg), "TCPIP_MSG_API")
+#if !LWIP_TCPIP_CORE_LOCKING_INPUT
+LWIP_MEMPOOL(TCPIP_MSG_INPKT,MEMP_NUM_TCPIP_MSG_INPKT, sizeof(struct tcpip_msg), "TCPIP_MSG_INPKT")
+#endif /* !LWIP_TCPIP_CORE_LOCKING_INPUT */
+#endif /* NO_SYS==0 */
+
+#if LWIP_ARP && ARP_QUEUEING
+LWIP_MEMPOOL(ARP_QUEUE, MEMP_NUM_ARP_QUEUE, sizeof(struct etharp_q_entry), "ARP_QUEUE")
+#endif /* LWIP_ARP && ARP_QUEUEING */
+
+#if LWIP_IGMP
+LWIP_MEMPOOL(IGMP_GROUP, MEMP_NUM_IGMP_GROUP, sizeof(struct igmp_group), "IGMP_GROUP")
+#endif /* LWIP_IGMP */
+
+#if (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS)) /* LWIP_TIMERS */
+LWIP_MEMPOOL(SYS_TIMEOUT, MEMP_NUM_SYS_TIMEOUT, sizeof(struct sys_timeo), "SYS_TIMEOUT")
+#endif /* LWIP_TIMERS */
+
+#if LWIP_SNMP
+LWIP_MEMPOOL(SNMP_ROOTNODE, MEMP_NUM_SNMP_ROOTNODE, sizeof(struct mib_list_rootnode), "SNMP_ROOTNODE")
+LWIP_MEMPOOL(SNMP_NODE, MEMP_NUM_SNMP_NODE, sizeof(struct mib_list_node), "SNMP_NODE")
+LWIP_MEMPOOL(SNMP_VARBIND, MEMP_NUM_SNMP_VARBIND, sizeof(struct snmp_varbind), "SNMP_VARBIND")
+LWIP_MEMPOOL(SNMP_VALUE, MEMP_NUM_SNMP_VALUE, SNMP_MAX_VALUE_SIZE, "SNMP_VALUE")
+#endif /* LWIP_SNMP */
+#if LWIP_DNS && LWIP_SOCKET
+LWIP_MEMPOOL(NETDB, MEMP_NUM_NETDB, NETDB_ELEM_SIZE, "NETDB")
+#endif /* LWIP_DNS && LWIP_SOCKET */
+#if LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+LWIP_MEMPOOL(LOCALHOSTLIST, MEMP_NUM_LOCALHOSTLIST, LOCALHOSTLIST_ELEM_SIZE, "LOCALHOSTLIST")
+#endif /* LWIP_DNS && DNS_LOCAL_HOSTLIST && DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+#if PPP_SUPPORT && 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")
+#endif /* LWIP_IPV6 && LWIP_ND6_QUEUEING */
+
+#if LWIP_IPV6 && LWIP_IPV6_REASS
+LWIP_MEMPOOL(IP6_REASSDATA, MEMP_NUM_REASSDATA, sizeof(struct ip6_reassdata), "IP6_REASSDATA")
+#endif /* LWIP_IPV6 && LWIP_IPV6_REASS */
+
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+LWIP_MEMPOOL(MLD6_GROUP, MEMP_NUM_MLD6_GROUP, sizeof(struct mld_group), "MLD6_GROUP")
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+
+
+/*
+ * A list of pools of pbuf's used by LWIP.
+ *
+ * LWIP_PBUF_MEMPOOL(pool_name, number_elements, pbuf_payload_size, pool_description)
+ * creates a pool name MEMP_pool_name. description is used in stats.c
+ * This allocates enough space for the pbuf struct and a payload.
+ * (Example: pbuf_payload_size=0 allocates only size for the struct)
+ */
+LWIP_PBUF_MEMPOOL(PBUF, MEMP_NUM_PBUF, 0, "PBUF_REF/ROM")
+LWIP_PBUF_MEMPOOL(PBUF_POOL, PBUF_POOL_SIZE, PBUF_POOL_BUFSIZE, "PBUF_POOL")
+
+
+/*
+ * Allow for user-defined pools; this must be explicitly set in lwipopts.h
+ * since the default is to NOT look for lwippools.h
+ */
+#if MEMP_USE_CUSTOM_POOLS
+#include "lwippools.h"
+#endif /* MEMP_USE_CUSTOM_POOLS */
+
+/*
+ * REQUIRED CLEANUP: Clear up so we don't get "multiply defined" error later
+ * (#undef is ignored for something that is not defined)
+ */
+#undef LWIP_MEMPOOL
+#undef LWIP_MALLOC_MEMPOOL
+#undef LWIP_MALLOC_MEMPOOL_START
+#undef LWIP_MALLOC_MEMPOOL_END
+#undef LWIP_PBUF_MEMPOOL
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/netbuf.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/netbuf.h
new file mode 100644
index 00000000..d12fe270
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/netbuf.h
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_NETBUF_H__
+#define __LWIP_NETBUF_H__
+
+#include "lwip/opt.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** This netbuf has dest-addr/port set */
+#define NETBUF_FLAG_DESTADDR 0x01
+/** This netbuf includes a checksum */
+#define NETBUF_FLAG_CHKSUM 0x02
+
+struct netbuf {
+ struct pbuf *p, *ptr;
+ ipX_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;
+#endif /* LWIP_NETBUF_RECVINFO */
+#endif /* LWIP_NETBUF_RECVINFO || LWIP_CHECKSUM_ON_COPY */
+};
+
+/* Network buffer functions: */
+struct netbuf * netbuf_new (void);
+void netbuf_delete (struct netbuf *buf);
+void * netbuf_alloc (struct netbuf *buf, u16_t size);
+void netbuf_free (struct netbuf *buf);
+err_t netbuf_ref (struct netbuf *buf,
+ const void *dataptr, u16_t size);
+void netbuf_chain (struct netbuf *head,
+ struct netbuf *tail);
+
+err_t netbuf_data (struct netbuf *buf,
+ void **dataptr, u16_t *len);
+s8_t netbuf_next (struct netbuf *buf);
+void netbuf_first (struct netbuf *buf);
+
+
+#define netbuf_copy_partial(buf, dataptr, len, offset) \
+ pbuf_copy_partial((buf)->p, (dataptr), (len), (offset))
+#define netbuf_copy(buf,dataptr,len) netbuf_copy_partial(buf, dataptr, len, 0)
+#define netbuf_take(buf, dataptr, len) pbuf_take((buf)->p, dataptr, len)
+#define netbuf_len(buf) ((buf)->p->tot_len)
+#define netbuf_fromaddr(buf) (ipX_2_ip(&((buf)->addr)))
+#define netbuf_set_fromaddr(buf, fromaddr) ip_addr_set(ipX_2_ip(&((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_destport(buf) (((buf)->flags & NETBUF_FLAG_DESTADDR) ? (buf)->toport_chksum : 0)
+#endif /* LWIP_NETBUF_RECVINFO */
+#if LWIP_CHECKSUM_ON_COPY
+#define netbuf_set_chksum(buf, chksum) do { (buf)->flags = NETBUF_FLAG_CHKSUM; \
+ (buf)->toport_chksum = chksum; } while(0)
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+#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__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/netdb.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/netdb.h
new file mode 100644
index 00000000..7587e2f2
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/netdb.h
@@ -0,0 +1,124 @@
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Simon Goldschmidt
+ *
+ */
+#ifndef __LWIP_NETDB_H__
+#define __LWIP_NETDB_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_DNS && LWIP_SOCKET
+
+#include <stddef.h> /* for size_t */
+
+#include "lwip/inet.h"
+#include "lwip/sockets.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* some rarely used options */
+#ifndef LWIP_DNS_API_DECLARE_H_ERRNO
+#define LWIP_DNS_API_DECLARE_H_ERRNO 1
+#endif
+
+#ifndef LWIP_DNS_API_DEFINE_ERRORS
+#define LWIP_DNS_API_DEFINE_ERRORS 1
+#endif
+
+#ifndef LWIP_DNS_API_DECLARE_STRUCTS
+#define LWIP_DNS_API_DECLARE_STRUCTS 1
+#endif
+
+#if LWIP_DNS_API_DEFINE_ERRORS
+/** Errors used by the DNS API functions, h_errno can be one of them */
+#define EAI_NONAME 200
+#define EAI_SERVICE 201
+#define EAI_FAIL 202
+#define EAI_MEMORY 203
+
+#define HOST_NOT_FOUND 210
+#define NO_DATA 211
+#define NO_RECOVERY 212
+#define TRY_AGAIN 213
+#endif /* LWIP_DNS_API_DEFINE_ERRORS */
+
+#if LWIP_DNS_API_DECLARE_STRUCTS
+struct hostent {
+ char *h_name; /* Official name of the host. */
+ char **h_aliases; /* A pointer to an array of pointers to alternative host names,
+ terminated by a null pointer. */
+ int h_addrtype; /* Address type. */
+ int h_length; /* The length, in bytes, of the address. */
+ char **h_addr_list; /* A pointer to an array of pointers to network addresses (in
+ network byte order) for the host, terminated by a null pointer. */
+#define h_addr h_addr_list[0] /* for backward compatibility */
+};
+
+struct addrinfo {
+ int ai_flags; /* Input flags. */
+ int ai_family; /* Address family of socket. */
+ int ai_socktype; /* Socket type. */
+ int ai_protocol; /* Protocol of socket. */
+ socklen_t ai_addrlen; /* Length of socket address. */
+ struct sockaddr *ai_addr; /* Socket address of socket. */
+ char *ai_canonname; /* Canonical name of service location. */
+ struct addrinfo *ai_next; /* Pointer to next in list. */
+};
+#endif /* LWIP_DNS_API_DECLARE_STRUCTS */
+
+#if LWIP_DNS_API_DECLARE_H_ERRNO
+/* application accessable error code set by the DNS API functions */
+extern int h_errno;
+#endif /* LWIP_DNS_API_DECLARE_H_ERRNO*/
+
+struct hostent *lwip_gethostbyname(const char *name);
+int lwip_gethostbyname_r(const char *name, struct hostent *ret, char *buf,
+ size_t buflen, struct hostent **result, int *h_errnop);
+void lwip_freeaddrinfo(struct addrinfo *ai);
+int lwip_getaddrinfo(const char *nodename,
+ const char *servname,
+ const struct addrinfo *hints,
+ struct addrinfo **res);
+
+#if LWIP_COMPAT_SOCKETS
+#define gethostbyname(name) lwip_gethostbyname(name)
+#define gethostbyname_r(name, ret, buf, buflen, result, h_errnop) \
+ lwip_gethostbyname_r(name, ret, buf, buflen, result, h_errnop)
+#define freeaddrinfo(addrinfo) lwip_freeaddrinfo(addrinfo)
+#define getaddrinfo(nodname, servname, hints, res) \
+ lwip_getaddrinfo(nodname, servname, hints, res)
+#endif /* LWIP_COMPAT_SOCKETS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_DNS && LWIP_SOCKET */
+
+#endif /* __LWIP_NETDB_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/netif.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/netif.h
new file mode 100644
index 00000000..b2c8d87a
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/netif.h
@@ -0,0 +1,387 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_NETIF_H__
+#define __LWIP_NETIF_H__
+
+#include "lwip/opt.h"
+
+#define ENABLE_LOOPBACK (LWIP_NETIF_LOOPBACK || LWIP_HAVE_LOOPIF)
+
+#include "lwip/err.h"
+
+#include "lwip/ip_addr.h"
+#include "lwip/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 */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Throughout this file, IP addresses are expected to be in
+ * the same byte order as in IP_PCB. */
+
+/** must be the maximum of all used hardware address lengths
+ across all types of interfaces in use */
+#define NETIF_MAX_HWADDR_LEN 6U
+
+/** Whether the network interface is 'up'. This is
+ * a software flag used to control whether this network
+ * interface is enabled and processes traffic.
+ * It is set by the startup code (for static IP configuration) or
+ * by dhcp/autoip when an address has been assigned.
+ */
+#define NETIF_FLAG_UP 0x01U
+/** If set, the netif has broadcast capability.
+ * Set by the netif driver in its init function. */
+#define NETIF_FLAG_BROADCAST 0x02U
+/** If set, the netif is one end of a point-to-point connection.
+ * Set by the netif driver in its init function. */
+#define NETIF_FLAG_POINTTOPOINT 0x04U
+/** If set, the interface is configured using DHCP.
+ * Set by the DHCP code when starting or stopping DHCP. */
+#define NETIF_FLAG_DHCP 0x08U
+/** If set, the interface has an active link
+ * (set by the network interface driver).
+ * Either set by the netif driver in its init function (if the link
+ * is up at that time) or at a later point once the link comes up
+ * (if link detection is supported by the hardware). */
+#define NETIF_FLAG_LINK_UP 0x10U
+/** If set, the netif is an ethernet device using ARP.
+ * Set by the netif driver in its init function.
+ * Used to check input packet types and use of DHCP. */
+#define NETIF_FLAG_ETHARP 0x20U
+/** If set, the netif is an ethernet device. It might not use
+ * ARP or TCP/IP if it is used for PPPoE only.
+ */
+#define NETIF_FLAG_ETHERNET 0x40U
+/** If set, the netif has IGMP capability.
+ * Set by the netif driver in its init function. */
+#define NETIF_FLAG_IGMP 0x80U
+
+/** Function prototype for netif init functions. Set up flags and output/linkoutput
+ * callback functions in this function.
+ *
+ * @param netif The netif to initialize
+ */
+typedef err_t (*netif_init_fn)(struct netif *netif);
+/** Function prototype for netif->input functions. This function is saved as 'input'
+ * callback function in the netif struct. Call it when a packet has been received.
+ *
+ * @param p The received packet, copied into a pbuf
+ * @param inp The netif which received the packet
+ */
+typedef err_t (*netif_input_fn)(struct pbuf *p, struct netif *inp);
+/** Function prototype for netif->output functions. Called by lwIP when a packet
+ * shall be sent. For ethernet netif, set this to 'etharp_output' and set
+ * 'linkoutput'.
+ *
+ * @param netif The netif which shall send a packet
+ * @param p The packet to send (p->payload points to IP header)
+ * @param ipaddr The IP address to which the packet shall be sent
+ */
+typedef err_t (*netif_output_fn)(struct netif *netif, struct pbuf *p,
+ ip_addr_t *ipaddr);
+#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
+ * 'linkoutput'.
+ *
+ * @param netif The netif which shall send a packet
+ * @param p The packet to send (p->payload points to IP header)
+ * @param ipaddr The IPv6 address to which the packet shall be sent
+ */
+typedef err_t (*netif_output_ip6_fn)(struct netif *netif, struct pbuf *p,
+ ip6_addr_t *ipaddr);
+#endif /* LWIP_IPV6 */
+/** Function prototype for netif->linkoutput functions. Only used for ethernet
+ * netifs. This function is called by ARP when a packet shall be sent.
+ *
+ * @param netif The netif which shall send a packet
+ * @param p The packet to send (raw ethernet packet)
+ */
+typedef err_t (*netif_linkoutput_fn)(struct netif *netif, struct pbuf *p);
+/** Function prototype for netif status- or link-callback functions. */
+typedef void (*netif_status_callback_fn)(struct netif *netif);
+/** 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);
+#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);
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+
+/** Generic data structure used for all lwIP network interfaces.
+ * The following fields should be filled in by the initialization
+ * function for the device driver: hwaddr_len, hwaddr[], mtu, flags */
+struct netif {
+ /** pointer to next in linked list */
+ struct netif *next;
+
+ /** IP address configuration in network byte order */
+ ip_addr_t ip_addr;
+ ip_addr_t netmask;
+ ip_addr_t gw;
+
+#if LWIP_IPV6
+ /** Array of IPv6 addresses for this netif. */
+ ip6_addr_t ip6_addr[LWIP_IPV6_NUM_ADDRESSES];
+ /** The state of each IPv6 address (Tentative, Preferred, etc).
+ * @see ip6_addr.h */
+ u8_t ip6_addr_state[LWIP_IPV6_NUM_ADDRESSES];
+#endif /* LWIP_IPV6 */
+ /** This function is called by the network device driver
+ * to pass a packet up the TCP/IP stack. */
+ netif_input_fn input;
+ /** This function is called by the IP module when it wants
+ * to send a packet on the interface. This function typically
+ * first resolves the hardware address, then sends the packet. */
+ netif_output_fn output;
+ /** This function is called by the ARP module when it wants
+ * to send a packet on the interface. This function outputs
+ * the pbuf as-is on the link medium. */
+ netif_linkoutput_fn linkoutput;
+#if LWIP_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. */
+ netif_output_ip6_fn output_ip6;
+#endif /* LWIP_IPV6 */
+#if LWIP_NETIF_STATUS_CALLBACK
+ /** This function is called when the netif state is set to up or down
+ */
+ netif_status_callback_fn status_callback;
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#if LWIP_NETIF_LINK_CALLBACK
+ /** This function is called when the netif link is set to up or down
+ */
+ netif_status_callback_fn link_callback;
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+#if LWIP_NETIF_REMOVE_CALLBACK
+ /** This function is called when the netif has been removed */
+ netif_status_callback_fn remove_callback;
+#endif /* LWIP_NETIF_REMOVE_CALLBACK */
+ /** This field can be set by the device driver and could point
+ * to state information for the device. */
+ void *state;
+#if LWIP_DHCP
+ /** the DHCP client state information for this netif */
+ struct dhcp *dhcp;
+#endif /* LWIP_DHCP */
+#if LWIP_AUTOIP
+ /** the AutoIP client state information for this netif */
+ struct autoip *autoip;
+#endif
+#if LWIP_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;
+#endif /* LWIP_NETIF_HOSTNAME */
+ /** maximum transfer unit (in bytes) */
+ u16_t mtu;
+ /** number of bytes used in hwaddr */
+ u8_t hwaddr_len;
+ /** link level hardware address of this interface */
+ u8_t hwaddr[NETIF_MAX_HWADDR_LEN];
+ /** flags (see NETIF_FLAG_ above) */
+ u8_t flags;
+ /** descriptive abbreviation */
+ char name[2];
+ /** number of this interface */
+ u8_t num;
+#if LWIP_SNMP
+ /** link type (from "snmp_ifType" enum from snmp.h) */
+ u8_t link_type;
+ /** (estimate) link speed */
+ u32_t link_speed;
+ /** timestamp at last change made (up/down) */
+ u32_t ts;
+ /** counters */
+ u32_t ifinoctets;
+ u32_t ifinucastpkts;
+ u32_t ifinnucastpkts;
+ u32_t ifindiscards;
+ u32_t ifoutoctets;
+ u32_t ifoutucastpkts;
+ u32_t ifoutnucastpkts;
+ u32_t ifoutdiscards;
+#endif /* LWIP_SNMP */
+#if LWIP_IGMP
+ /** This function could be called to add or delete an entry in the multicast
+ filter table of the ethernet MAC.*/
+ netif_igmp_mac_filter_fn igmp_mac_filter;
+#endif /* LWIP_IGMP */
+#if LWIP_IPV6 && LWIP_IPV6_MLD
+ /** This function could be called to add or delete an entry in the IPv6 multicast
+ filter table of the ethernet MAC. */
+ netif_mld_mac_filter_fn mld_mac_filter;
+#endif /* LWIP_IPV6 && LWIP_IPV6_MLD */
+#if LWIP_NETIF_HWADDRHINT
+ u8_t *addr_hint;
+#endif /* LWIP_NETIF_HWADDRHINT */
+#if ENABLE_LOOPBACK
+ /* List of packets to be queued for ourselves. */
+ struct pbuf *loop_first;
+ struct pbuf *loop_last;
+#if LWIP_LOOPBACK_MAX_PBUFS
+ u16_t loop_cnt_current;
+#endif /* LWIP_LOOPBACK_MAX_PBUFS */
+#endif /* ENABLE_LOOPBACK */
+};
+
+#if LWIP_SNMP
+#define NETIF_INIT_SNMP(netif, type, speed) \
+ /* use "snmp_ifType" enum from snmp.h for "type", snmp_ifType_ethernet_csmacd by example */ \
+ (netif)->link_type = (type); \
+ /* your link speed here (units: bits per second) */ \
+ (netif)->link_speed = (speed); \
+ (netif)->ts = 0; \
+ (netif)->ifinoctets = 0; \
+ (netif)->ifinucastpkts = 0; \
+ (netif)->ifinnucastpkts = 0; \
+ (netif)->ifindiscards = 0; \
+ (netif)->ifoutoctets = 0; \
+ (netif)->ifoutucastpkts = 0; \
+ (netif)->ifoutnucastpkts = 0; \
+ (netif)->ifoutdiscards = 0
+#else /* LWIP_SNMP */
+#define NETIF_INIT_SNMP(netif, type, speed)
+#endif /* LWIP_SNMP */
+
+
+/** The list of network interfaces. */
+extern struct netif *netif_list;
+/** The default network interface. */
+extern struct netif *netif_default;
+
+void netif_init(void);
+
+struct netif *netif_add(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
+ ip_addr_t *gw, void *state, netif_init_fn init, netif_input_fn input);
+
+void
+netif_set_addr(struct netif *netif, ip_addr_t *ipaddr, ip_addr_t *netmask,
+ ip_addr_t *gw);
+void netif_remove(struct netif * netif);
+
+/* Returns a network interface given its name. The name is of the form
+ "et0", where the first two letters are the "name" field in the
+ netif structure, and the digit is in the num field in the same
+ structure. */
+struct netif *netif_find(char *name);
+
+void netif_set_default(struct netif *netif);
+
+void netif_set_ipaddr(struct netif *netif, ip_addr_t *ipaddr);
+void netif_set_netmask(struct netif *netif, ip_addr_t *netmask);
+void netif_set_gw(struct netif *netif, ip_addr_t *gw);
+
+void netif_set_up(struct netif *netif);
+void netif_set_down(struct netif *netif);
+/** Ask if an interface is up */
+#define netif_is_up(netif) (((netif)->flags & NETIF_FLAG_UP) ? (u8_t)1 : (u8_t)0)
+
+#if LWIP_NETIF_STATUS_CALLBACK
+void netif_set_status_callback(struct netif *netif, netif_status_callback_fn status_callback);
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#if LWIP_NETIF_REMOVE_CALLBACK
+void netif_set_remove_callback(struct netif *netif, netif_status_callback_fn remove_callback);
+#endif /* LWIP_NETIF_REMOVE_CALLBACK */
+
+void netif_set_link_up(struct netif *netif);
+void netif_set_link_down(struct netif *netif);
+/** Ask if a link is up */
+#define netif_is_link_up(netif) (((netif)->flags & NETIF_FLAG_LINK_UP) ? (u8_t)1 : (u8_t)0)
+
+#if LWIP_NETIF_LINK_CALLBACK
+void netif_set_link_callback(struct netif *netif, netif_status_callback_fn link_callback);
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+#if LWIP_NETIF_HOSTNAME
+#define netif_set_hostname(netif, name) do { if((netif) != NULL) { (netif)->hostname = name; }}while(0)
+#define netif_get_hostname(netif) (((netif) != NULL) ? ((netif)->hostname) : NULL)
+#endif /* LWIP_NETIF_HOSTNAME */
+
+#if LWIP_IGMP
+#define netif_set_igmp_mac_filter(netif, function) do { if((netif) != NULL) { (netif)->igmp_mac_filter = function; }}while(0)
+#define netif_get_igmp_mac_filter(netif) (((netif) != NULL) ? ((netif)->igmp_mac_filter) : NULL)
+#endif /* LWIP_IGMP */
+
+#if ENABLE_LOOPBACK
+err_t netif_loop_output(struct netif *netif, struct pbuf *p);
+void netif_poll(struct netif *netif);
+#if !LWIP_NETIF_LOOPBACK_MULTITHREADING
+void netif_poll_all(void);
+#endif /* !LWIP_NETIF_LOOPBACK_MULTITHREADING */
+#endif /* ENABLE_LOOPBACK */
+
+#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);
+#endif /* LWIP_IPV6 */
+
+#if LWIP_NETIF_HWADDRHINT
+#define NETIF_SET_HWADDRHINT(netif, hint) ((netif)->addr_hint = (hint))
+#else /* LWIP_NETIF_HWADDRHINT */
+#define NETIF_SET_HWADDRHINT(netif, hint)
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_NETIF_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/netifapi.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/netifapi.h
new file mode 100644
index 00000000..648151b6
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/netifapi.h
@@ -0,0 +1,111 @@
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#ifndef __LWIP_NETIFAPI_H__
+#define __LWIP_NETIFAPI_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_NETIF_API /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/sys.h"
+#include "lwip/netif.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+typedef void (*netifapi_void_fn)(struct netif *netif);
+typedef err_t (*netifapi_errt_fn)(struct netif *netif);
+
+struct netifapi_msg_msg {
+#if !LWIP_TCPIP_CORE_LOCKING
+ sys_sem_t sem;
+#endif /* !LWIP_TCPIP_CORE_LOCKING */
+ err_t err;
+ struct netif *netif;
+ union {
+ struct {
+ ip_addr_t *ipaddr;
+ ip_addr_t *netmask;
+ ip_addr_t *gw;
+ void *state;
+ netif_init_fn init;
+ netif_input_fn input;
+ } add;
+ struct {
+ netifapi_void_fn voidfunc;
+ netifapi_errt_fn errtfunc;
+ } common;
+ } msg;
+};
+
+struct netifapi_msg {
+ void (* function)(struct netifapi_msg_msg *msg);
+ struct netifapi_msg_msg msg;
+};
+
+
+/* API for application */
+err_t netifapi_netif_add ( struct netif *netif,
+ ip_addr_t *ipaddr,
+ ip_addr_t *netmask,
+ ip_addr_t *gw,
+ void *state,
+ netif_init_fn init,
+ netif_input_fn input);
+
+err_t netifapi_netif_set_addr ( struct netif *netif,
+ ip_addr_t *ipaddr,
+ ip_addr_t *netmask,
+ ip_addr_t *gw );
+
+err_t netifapi_netif_common ( struct netif *netif,
+ netifapi_void_fn voidfunc,
+ netifapi_errt_fn errtfunc);
+
+#define netifapi_netif_remove(n) netifapi_netif_common(n, netif_remove, NULL)
+#define netifapi_netif_set_up(n) netifapi_netif_common(n, netif_set_up, NULL)
+#define netifapi_netif_set_down(n) netifapi_netif_common(n, netif_set_down, NULL)
+#define netifapi_netif_set_default(n) netifapi_netif_common(n, netif_set_default, NULL)
+#define netifapi_dhcp_start(n) netifapi_netif_common(n, NULL, dhcp_start)
+#define netifapi_dhcp_stop(n) netifapi_netif_common(n, dhcp_stop, NULL)
+#define netifapi_dhcp_inform(n) netifapi_netif_common(n, dhcp_inform, NULL)
+#define netifapi_dhcp_renew(n) netifapi_netif_common(n, NULL, dhcp_renew)
+#define netifapi_dhcp_release(n) netifapi_netif_common(n, NULL, dhcp_release)
+#define netifapi_autoip_start(n) netifapi_netif_common(n, NULL, autoip_start)
+#define netifapi_autoip_stop(n) netifapi_netif_common(n, NULL, autoip_stop)
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_NETIF_API */
+
+#endif /* __LWIP_NETIFAPI_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/opt.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/opt.h
new file mode 100644
index 00000000..1cec8d46
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/opt.h
@@ -0,0 +1,2454 @@
+/**
+ * @file
+ *
+ * lwIP Options Configuration
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_OPT_H__
+#define __LWIP_OPT_H__
+
+/*
+ * Include user defined options first. Anything not defined in these files
+ * will be set to standard values. Override anything you dont like!
+ */
+#include "lwipopts.h"
+#include "lwip/debug.h"
+
+/*
+ -----------------------------------------------
+ ---------- Platform specific locking ----------
+ -----------------------------------------------
+*/
+
+/**
+ * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain
+ * critical regions during buffer allocation, deallocation and memory
+ * allocation and deallocation.
+ */
+#ifndef SYS_LIGHTWEIGHT_PROT
+#define SYS_LIGHTWEIGHT_PROT 0
+#endif
+
+/**
+ * NO_SYS==1: Provides VERY minimal functionality. Otherwise,
+ * use lwIP facilities.
+ */
+#ifndef NO_SYS
+#define NO_SYS 0
+#endif
+
+/**
+ * NO_SYS_NO_TIMERS==1: Drop support for sys_timeout when NO_SYS==1
+ * Mainly for compatibility to old versions.
+ */
+#ifndef NO_SYS_NO_TIMERS
+#define NO_SYS_NO_TIMERS 0
+#endif
+
+/**
+ * MEMCPY: override this if you have a faster implementation at hand than the
+ * one included in your C library
+ */
+#ifndef MEMCPY
+#define MEMCPY(dst,src,len) memcpy(dst,src,len)
+#endif
+
+/**
+ * SMEMCPY: override this with care! Some compilers (e.g. gcc) can inline a
+ * call to memcpy() if the length is known at compile time and is small.
+ */
+#ifndef SMEMCPY
+#define SMEMCPY(dst,src,len) memcpy(dst,src,len)
+#endif
+
+/*
+ ------------------------------------
+ ---------- Memory options ----------
+ ------------------------------------
+*/
+/**
+ * MEM_LIBC_MALLOC==1: Use malloc/free/realloc provided by your C-library
+ * instead of the lwip internal allocator. Can save code size if you
+ * already use it.
+ */
+#ifndef MEM_LIBC_MALLOC
+#define MEM_LIBC_MALLOC 0
+#endif
+
+/**
+* MEMP_MEM_MALLOC==1: Use mem_malloc/mem_free instead of the lwip pool allocator.
+* Especially useful with MEM_LIBC_MALLOC but handle with care regarding execution
+* speed and usage from interrupts!
+*/
+#ifndef MEMP_MEM_MALLOC
+#define MEMP_MEM_MALLOC 0
+#endif
+
+/**
+ * MEM_ALIGNMENT: should be set to the alignment of the CPU
+ * 4 byte alignment -> #define MEM_ALIGNMENT 4
+ * 2 byte alignment -> #define MEM_ALIGNMENT 2
+ */
+#ifndef MEM_ALIGNMENT
+#define MEM_ALIGNMENT 1
+#endif
+
+/**
+ * MEM_SIZE: the size of the heap memory. If the application will send
+ * a lot of data that needs to be copied, this should be set high.
+ */
+#ifndef MEM_SIZE
+#define MEM_SIZE 1600
+#endif
+
+/**
+ * MEMP_SEPARATE_POOLS: if defined to 1, each pool is placed in its own array.
+ * This can be used to individually change the location of each pool.
+ * Default is one big array for all pools
+ */
+#ifndef MEMP_SEPARATE_POOLS
+#define MEMP_SEPARATE_POOLS 0
+#endif
+
+/**
+ * MEMP_OVERFLOW_CHECK: memp overflow protection reserves a configurable
+ * amount of bytes before and after each memp element in every pool and fills
+ * it with a prominent default value.
+ * MEMP_OVERFLOW_CHECK == 0 no checking
+ * MEMP_OVERFLOW_CHECK == 1 checks each element when it is freed
+ * MEMP_OVERFLOW_CHECK >= 2 checks each element in every pool every time
+ * memp_malloc() or memp_free() is called (useful but slow!)
+ */
+#ifndef MEMP_OVERFLOW_CHECK
+#define MEMP_OVERFLOW_CHECK 0
+#endif
+
+/**
+ * MEMP_SANITY_CHECK==1: run a sanity check after each memp_free() to make
+ * sure that there are no cycles in the linked lists.
+ */
+#ifndef MEMP_SANITY_CHECK
+#define MEMP_SANITY_CHECK 0
+#endif
+
+/**
+ * MEM_USE_POOLS==1: Use an alternative to malloc() by allocating from a set
+ * of memory pools of various sizes. When mem_malloc is called, an element of
+ * the smallest pool that can provide the length needed is returned.
+ * To use this, MEMP_USE_CUSTOM_POOLS also has to be enabled.
+ */
+#ifndef MEM_USE_POOLS
+#define MEM_USE_POOLS 0
+#endif
+
+/**
+ * MEM_USE_POOLS_TRY_BIGGER_POOL==1: if one malloc-pool is empty, try the next
+ * bigger pool - WARNING: THIS MIGHT WASTE MEMORY but it can make a system more
+ * reliable. */
+#ifndef MEM_USE_POOLS_TRY_BIGGER_POOL
+#define MEM_USE_POOLS_TRY_BIGGER_POOL 0
+#endif
+
+/**
+ * MEMP_USE_CUSTOM_POOLS==1: whether to include a user file lwippools.h
+ * that defines additional pools beyond the "standard" ones required
+ * by lwIP. If you set this to 1, you must have lwippools.h in your
+ * inlude path somewhere.
+ */
+#ifndef MEMP_USE_CUSTOM_POOLS
+#define MEMP_USE_CUSTOM_POOLS 0
+#endif
+
+/**
+ * Set this to 1 if you want to free PBUF_RAM pbufs (or call mem_free()) from
+ * interrupt context (or another context that doesn't allow waiting for a
+ * semaphore).
+ * If set to 1, mem_malloc will be protected by a semaphore and SYS_ARCH_PROTECT,
+ * while mem_free will only use SYS_ARCH_PROTECT. mem_malloc SYS_ARCH_UNPROTECTs
+ * with each loop so that mem_free can run.
+ *
+ * ATTENTION: As you can see from the above description, this leads to dis-/
+ * enabling interrupts often, which can be slow! Also, on low memory, mem_malloc
+ * can need longer.
+ *
+ * If you don't want that, at least for NO_SYS=0, you can still use the following
+ * functions to enqueue a deallocation call which then runs in the tcpip_thread
+ * context:
+ * - pbuf_free_callback(p);
+ * - mem_free_callback(m);
+ */
+#ifndef LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT
+#define LWIP_ALLOW_MEM_FREE_FROM_OTHER_CONTEXT 0
+#endif
+
+/*
+ ------------------------------------------------
+ ---------- Internal Memory Pool Sizes ----------
+ ------------------------------------------------
+*/
+/**
+ * MEMP_NUM_PBUF: the number of memp struct pbufs (used for PBUF_ROM and PBUF_REF).
+ * If the application sends a lot of data out of ROM (or other static memory),
+ * this should be set high.
+ */
+#ifndef MEMP_NUM_PBUF
+#define MEMP_NUM_PBUF 16
+#endif
+
+/**
+ * MEMP_NUM_RAW_PCB: Number of raw connection PCBs
+ * (requires the LWIP_RAW option)
+ */
+#ifndef MEMP_NUM_RAW_PCB
+#define MEMP_NUM_RAW_PCB 4
+#endif
+
+/**
+ * MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
+ * per active UDP "connection".
+ * (requires the LWIP_UDP option)
+ */
+#ifndef MEMP_NUM_UDP_PCB
+#define MEMP_NUM_UDP_PCB 4
+#endif
+
+/**
+ * MEMP_NUM_TCP_PCB: the number of simulatenously active TCP connections.
+ * (requires the LWIP_TCP option)
+ */
+#ifndef MEMP_NUM_TCP_PCB
+#define MEMP_NUM_TCP_PCB 5
+#endif
+
+/**
+ * MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP connections.
+ * (requires the LWIP_TCP option)
+ */
+#ifndef MEMP_NUM_TCP_PCB_LISTEN
+#define MEMP_NUM_TCP_PCB_LISTEN 8
+#endif
+
+/**
+ * MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP segments.
+ * (requires the LWIP_TCP option)
+ */
+#ifndef MEMP_NUM_TCP_SEG
+#define MEMP_NUM_TCP_SEG 16
+#endif
+
+/**
+ * MEMP_NUM_REASSDATA: the number of IP packets simultaneously queued for
+ * reassembly (whole packets, not fragments!)
+ */
+#ifndef MEMP_NUM_REASSDATA
+#define MEMP_NUM_REASSDATA 5
+#endif
+
+/**
+ * MEMP_NUM_FRAG_PBUF: the number of IP fragments simultaneously sent
+ * (fragments, not whole packets!).
+ * This is only used with IP_FRAG_USES_STATIC_BUF==0 and
+ * LWIP_NETIF_TX_SINGLE_PBUF==0 and only has to be > 1 with DMA-enabled MACs
+ * where the packet is not yet sent when netif->output returns.
+ */
+#ifndef MEMP_NUM_FRAG_PBUF
+#define MEMP_NUM_FRAG_PBUF 15
+#endif
+
+/**
+ * MEMP_NUM_ARP_QUEUE: the number of simulateously queued outgoing
+ * packets (pbufs) that are waiting for an ARP request (to resolve
+ * their destination address) to finish.
+ * (requires the ARP_QUEUEING option)
+ */
+#ifndef MEMP_NUM_ARP_QUEUE
+#define MEMP_NUM_ARP_QUEUE 30
+#endif
+
+/**
+ * MEMP_NUM_IGMP_GROUP: The number of multicast groups whose network interfaces
+ * can be members et the same time (one per netif - allsystems group -, plus one
+ * per netif membership).
+ * (requires the LWIP_IGMP option)
+ */
+#ifndef MEMP_NUM_IGMP_GROUP
+#define MEMP_NUM_IGMP_GROUP 8
+#endif
+
+/**
+ * MEMP_NUM_SYS_TIMEOUT: the number of simulateously active timeouts.
+ * (requires NO_SYS==0)
+ * 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))
+#endif
+
+/**
+ * MEMP_NUM_NETBUF: the number of struct netbufs.
+ * (only needed if you use the sequential API, like api_lib.c)
+ */
+#ifndef MEMP_NUM_NETBUF
+#define MEMP_NUM_NETBUF 2
+#endif
+
+/**
+ * MEMP_NUM_NETCONN: the number of struct netconns.
+ * (only needed if you use the sequential API, like api_lib.c)
+ */
+#ifndef MEMP_NUM_NETCONN
+#define MEMP_NUM_NETCONN 4
+#endif
+
+/**
+ * MEMP_NUM_TCPIP_MSG_API: the number of struct tcpip_msg, which are used
+ * for callback/timeout API communication.
+ * (only needed if you use tcpip.c)
+ */
+#ifndef MEMP_NUM_TCPIP_MSG_API
+#define MEMP_NUM_TCPIP_MSG_API 8
+#endif
+
+/**
+ * MEMP_NUM_TCPIP_MSG_INPKT: the number of struct tcpip_msg, which are used
+ * for incoming packets.
+ * (only needed if you use tcpip.c)
+ */
+#ifndef MEMP_NUM_TCPIP_MSG_INPKT
+#define MEMP_NUM_TCPIP_MSG_INPKT 8
+#endif
+
+/**
+ * MEMP_NUM_SNMP_NODE: the number of leafs in the SNMP tree.
+ */
+#ifndef MEMP_NUM_SNMP_NODE
+#define MEMP_NUM_SNMP_NODE 50
+#endif
+
+/**
+ * MEMP_NUM_SNMP_ROOTNODE: the number of branches in the SNMP tree.
+ * Every branch has one leaf (MEMP_NUM_SNMP_NODE) at least!
+ */
+#ifndef MEMP_NUM_SNMP_ROOTNODE
+#define MEMP_NUM_SNMP_ROOTNODE 30
+#endif
+
+/**
+ * MEMP_NUM_SNMP_VARBIND: the number of concurrent requests (does not have to
+ * be changed normally) - 2 of these are used per request (1 for input,
+ * 1 for output)
+ */
+#ifndef MEMP_NUM_SNMP_VARBIND
+#define MEMP_NUM_SNMP_VARBIND 2
+#endif
+
+/**
+ * MEMP_NUM_SNMP_VALUE: the number of OID or values concurrently used
+ * (does not have to be changed normally) - 3 of these are used per request
+ * (1 for the value read and 2 for OIDs - input and output)
+ */
+#ifndef MEMP_NUM_SNMP_VALUE
+#define MEMP_NUM_SNMP_VALUE 3
+#endif
+
+/**
+ * MEMP_NUM_NETDB: the number of concurrently running lwip_addrinfo() calls
+ * (before freeing the corresponding memory using lwip_freeaddrinfo()).
+ */
+#ifndef MEMP_NUM_NETDB
+#define MEMP_NUM_NETDB 1
+#endif
+
+/**
+ * MEMP_NUM_LOCALHOSTLIST: the number of host entries in the local host list
+ * if DNS_LOCAL_HOSTLIST_IS_DYNAMIC==1.
+ */
+#ifndef MEMP_NUM_LOCALHOSTLIST
+#define MEMP_NUM_LOCALHOSTLIST 1
+#endif
+
+/**
+ * MEMP_NUM_PPPOE_INTERFACES: the number of concurrently active PPPoE
+ * interfaces (only used with PPPOE_SUPPORT==1)
+ */
+#ifndef MEMP_NUM_PPPOE_INTERFACES
+#define MEMP_NUM_PPPOE_INTERFACES 1
+#endif
+
+/**
+ * PBUF_POOL_SIZE: the number of buffers in the pbuf pool.
+ */
+#ifndef PBUF_POOL_SIZE
+#define PBUF_POOL_SIZE 16
+#endif
+
+/*
+ ---------------------------------
+ ---------- ARP options ----------
+ ---------------------------------
+*/
+/**
+ * LWIP_ARP==1: Enable ARP functionality.
+ */
+#ifndef LWIP_ARP
+#define LWIP_ARP 1
+#endif
+
+/**
+ * ARP_TABLE_SIZE: Number of active MAC-IP address pairs cached.
+ */
+#ifndef ARP_TABLE_SIZE
+#define ARP_TABLE_SIZE 10
+#endif
+
+/**
+ * ARP_QUEUEING==1: Multiple outgoing packets are queued during hardware address
+ * resolution. By default, only the most recent packet is queued per IP address.
+ * This is sufficient for most protocols and mainly reduces TCP connection
+ * startup time. Set this to 1 if you know your application sends more than one
+ * packet in a row to an IP address that is not in the ARP cache.
+ */
+#ifndef ARP_QUEUEING
+#define ARP_QUEUEING 0
+#endif
+
+/**
+ * ARP_PROXY==1: Proxy ARP support.
+ */
+#ifndef ARP_PROXY
+#define ARP_PROXY 0
+#endif
+
+/**
+ * ETHARP_TRUST_IP_MAC==1: Incoming IP packets cause the ARP table to be
+ * updated with the source MAC and IP addresses supplied in the packet.
+ * You may want to disable this if you do not trust LAN peers to have the
+ * correct addresses, or as a limited approach to attempt to handle
+ * spoofing. If disabled, lwIP will need to make a new ARP request if
+ * the peer is not already in the ARP table, adding a little latency.
+ * The peer *is* in the ARP table if it requested our address before.
+ * Also notice that this slows down input processing of every IP packet!
+ */
+#ifndef ETHARP_TRUST_IP_MAC
+#define ETHARP_TRUST_IP_MAC 0
+#endif
+
+/**
+ * ETHARP_SUPPORT_VLAN==1: support receiving ethernet packets with VLAN header.
+ * Additionally, you can define ETHARP_VLAN_CHECK to an u16_t VLAN ID to check.
+ * If ETHARP_VLAN_CHECK is defined, only VLAN-traffic for this VLAN is accepted.
+ * If ETHARP_VLAN_CHECK is not defined, all traffic is accepted.
+ * Alternatively, define a function/define ETHARP_VLAN_CHECK_FN(eth_hdr, vlan)
+ * that returns 1 to accept a packet or 0 to drop a packet.
+ */
+#ifndef ETHARP_SUPPORT_VLAN
+#define ETHARP_SUPPORT_VLAN 0
+#endif
+
+/** LWIP_ETHERNET==1: enable ethernet support for PPPoE even though ARP
+ * might be disabled
+ */
+#ifndef LWIP_ETHERNET
+#define LWIP_ETHERNET (LWIP_ARP || PPPOE_SUPPORT)
+#endif
+
+/** ETH_PAD_SIZE: number of bytes added before the ethernet header to ensure
+ * alignment of payload after that header. Since the header is 14 bytes long,
+ * without this padding e.g. addresses in the IP header will not be aligned
+ * on a 32-bit boundary, so setting this to 2 can speed up 32-bit-platforms.
+ */
+#ifndef ETH_PAD_SIZE
+#define ETH_PAD_SIZE 0
+#endif
+
+/** ETHARP_SUPPORT_STATIC_ENTRIES==1: enable code to support static ARP table
+ * entries (using etharp_add_static_entry/etharp_remove_static_entry).
+ */
+#ifndef ETHARP_SUPPORT_STATIC_ENTRIES
+#define ETHARP_SUPPORT_STATIC_ENTRIES 0
+#endif
+
+
+/*
+ --------------------------------
+ ---------- IP options ----------
+ --------------------------------
+*/
+/**
+ * LWIP_CONNECTION_PROXY==1: IP packets that are not for us and are
+ * not routable are processed by NAT/NAPT proxy similar to slirp.
+ */
+#ifndef LWIP_CONNECTION_PROXY
+#define LWIP_CONNECTION_PROXY 0
+#endif
+
+/**
+ * IP_FORWARD==1: Enables the ability to forward IP packets across network
+ * interfaces. If you are going to run lwIP on a device with only one network
+ * interface, define this to 0.
+ */
+#ifndef IP_FORWARD
+#define IP_FORWARD 0
+#endif
+
+/**
+ * IP_OPTIONS_ALLOWED: Defines the behavior for IP options.
+ * IP_OPTIONS_ALLOWED==0: All packets with IP options are dropped.
+ * IP_OPTIONS_ALLOWED==1: IP options are allowed (but not parsed).
+ */
+#ifndef IP_OPTIONS_ALLOWED
+#define IP_OPTIONS_ALLOWED 1
+#endif
+
+/**
+ * IP_REASSEMBLY==1: Reassemble incoming fragmented IP packets. Note that
+ * this option does not affect outgoing packet sizes, which can be controlled
+ * via IP_FRAG.
+ */
+#ifndef IP_REASSEMBLY
+#define IP_REASSEMBLY 1
+#endif
+
+/**
+ * IP_FRAG==1: Fragment outgoing IP packets if their size exceeds MTU. Note
+ * that this option does not affect incoming packet sizes, which can be
+ * controlled via IP_REASSEMBLY.
+ */
+#ifndef IP_FRAG
+#define IP_FRAG 1
+#endif
+
+/**
+ * IP_REASS_MAXAGE: Maximum time (in multiples of IP_TMR_INTERVAL - so seconds, normally)
+ * a fragmented IP packet waits for all fragments to arrive. If not all fragments arrived
+ * in this time, the whole packet is discarded.
+ */
+#ifndef IP_REASS_MAXAGE
+#define IP_REASS_MAXAGE 3
+#endif
+
+/**
+ * IP_REASS_MAX_PBUFS: Total maximum amount of pbufs waiting to be reassembled.
+ * Since the received pbufs are enqueued, be sure to configure
+ * PBUF_POOL_SIZE > IP_REASS_MAX_PBUFS so that the stack is still able to receive
+ * packets even if the maximum amount of fragments is enqueued for reassembly!
+ */
+#ifndef IP_REASS_MAX_PBUFS
+#define IP_REASS_MAX_PBUFS 10
+#endif
+
+/**
+ * IP_FRAG_USES_STATIC_BUF==1: Use a static MTU-sized buffer for IP
+ * fragmentation. Otherwise pbufs are allocated and reference the original
+ * packet data to be fragmented (or with LWIP_NETIF_TX_SINGLE_PBUF==1,
+ * new PBUF_RAM pbufs are used for fragments).
+ * ATTENTION: IP_FRAG_USES_STATIC_BUF==1 may not be used for DMA-enabled MACs!
+ */
+#ifndef IP_FRAG_USES_STATIC_BUF
+#define IP_FRAG_USES_STATIC_BUF 0
+#endif
+
+/**
+ * IP_FRAG_MAX_MTU: Assumed max MTU on any interface for IP frag buffer
+ * (requires IP_FRAG_USES_STATIC_BUF==1)
+ */
+#if IP_FRAG_USES_STATIC_BUF && !defined(IP_FRAG_MAX_MTU)
+#define IP_FRAG_MAX_MTU 1500
+#endif
+
+/**
+ * IP_DEFAULT_TTL: Default value for Time-To-Live used by transport layers.
+ */
+#ifndef IP_DEFAULT_TTL
+#define IP_DEFAULT_TTL 255
+#endif
+
+/**
+ * IP_SOF_BROADCAST=1: Use the SOF_BROADCAST field to enable broadcast
+ * filter per pcb on udp and raw send operations. To enable broadcast filter
+ * on recv operations, you also have to set IP_SOF_BROADCAST_RECV=1.
+ */
+#ifndef IP_SOF_BROADCAST
+#define IP_SOF_BROADCAST 0
+#endif
+
+/**
+ * IP_SOF_BROADCAST_RECV (requires IP_SOF_BROADCAST=1) enable the broadcast
+ * filter on recv operations.
+ */
+#ifndef IP_SOF_BROADCAST_RECV
+#define IP_SOF_BROADCAST_RECV 0
+#endif
+
+/**
+ * IP_FORWARD_ALLOW_TX_ON_RX_NETIF==1: allow ip_forward() to send packets back
+ * out on the netif where it was received. This should only be used for
+ * wireless networks.
+ * ATTENTION: When this is 1, make sure your netif driver correctly marks incoming
+ * link-layer-broadcast/multicast packets as such using the corresponding pbuf flags!
+ */
+#ifndef IP_FORWARD_ALLOW_TX_ON_RX_NETIF
+#define IP_FORWARD_ALLOW_TX_ON_RX_NETIF 0
+#endif
+
+/**
+ * LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS==1: randomize the local port for the first
+ * local TCP/UDP pcb (default==0). This can prevent creating predictable port
+ * numbers after booting a device.
+ */
+#ifndef LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS
+#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 0
+#endif
+
+/*
+ ----------------------------------
+ ---------- ICMP options ----------
+ ----------------------------------
+*/
+/**
+ * LWIP_ICMP==1: Enable ICMP module inside the IP stack.
+ * Be careful, disable that make your product non-compliant to RFC1122
+ */
+#ifndef LWIP_ICMP
+#define LWIP_ICMP 1
+#endif
+
+/**
+ * ICMP_TTL: Default value for Time-To-Live used by ICMP packets.
+ */
+#ifndef ICMP_TTL
+#define ICMP_TTL (IP_DEFAULT_TTL)
+#endif
+
+/**
+ * LWIP_BROADCAST_PING==1: respond to broadcast pings (default is unicast only)
+ */
+#ifndef LWIP_BROADCAST_PING
+#define LWIP_BROADCAST_PING 0
+#endif
+
+/**
+ * LWIP_MULTICAST_PING==1: respond to multicast pings (default is unicast only)
+ */
+#ifndef LWIP_MULTICAST_PING
+#define LWIP_MULTICAST_PING 0
+#endif
+
+/*
+ ---------------------------------
+ ---------- RAW options ----------
+ ---------------------------------
+*/
+/**
+ * LWIP_RAW==1: Enable application layer to hook into the IP layer itself.
+ */
+#ifndef LWIP_RAW
+#define LWIP_RAW 1
+#endif
+
+/**
+ * LWIP_RAW==1: Enable application layer to hook into the IP layer itself.
+ */
+#ifndef RAW_TTL
+#define RAW_TTL (IP_DEFAULT_TTL)
+#endif
+
+/*
+ ----------------------------------
+ ---------- DHCP options ----------
+ ----------------------------------
+*/
+/**
+ * LWIP_DHCP==1: Enable DHCP module.
+ */
+#ifndef LWIP_DHCP
+#define LWIP_DHCP 0
+#endif
+
+/**
+ * DHCP_DOES_ARP_CHECK==1: Do an ARP check on the offered address.
+ */
+#ifndef DHCP_DOES_ARP_CHECK
+#define DHCP_DOES_ARP_CHECK ((LWIP_DHCP) && (LWIP_ARP))
+#endif
+
+/*
+ ------------------------------------
+ ---------- AUTOIP options ----------
+ ------------------------------------
+*/
+/**
+ * LWIP_AUTOIP==1: Enable AUTOIP module.
+ */
+#ifndef LWIP_AUTOIP
+#define LWIP_AUTOIP 0
+#endif
+
+/**
+ * LWIP_DHCP_AUTOIP_COOP==1: Allow DHCP and AUTOIP to be both enabled on
+ * the same interface at the same time.
+ */
+#ifndef LWIP_DHCP_AUTOIP_COOP
+#define LWIP_DHCP_AUTOIP_COOP 0
+#endif
+
+/**
+ * LWIP_DHCP_AUTOIP_COOP_TRIES: Set to the number of DHCP DISCOVER probes
+ * that should be sent before falling back on AUTOIP. This can be set
+ * as low as 1 to get an AutoIP address very quickly, but you should
+ * be prepared to handle a changing IP address when DHCP overrides
+ * AutoIP.
+ */
+#ifndef LWIP_DHCP_AUTOIP_COOP_TRIES
+#define LWIP_DHCP_AUTOIP_COOP_TRIES 9
+#endif
+
+/*
+ ----------------------------------
+ ---------- SNMP options ----------
+ ----------------------------------
+*/
+/**
+ * LWIP_SNMP==1: Turn on SNMP module. UDP must be available for SNMP
+ * transport.
+ */
+#ifndef LWIP_SNMP
+#define LWIP_SNMP 0
+#endif
+
+/**
+ * SNMP_CONCURRENT_REQUESTS: Number of concurrent requests the module will
+ * allow. At least one request buffer is required.
+ * Does not have to be changed unless external MIBs answer request asynchronously
+ */
+#ifndef SNMP_CONCURRENT_REQUESTS
+#define SNMP_CONCURRENT_REQUESTS 1
+#endif
+
+/**
+ * SNMP_TRAP_DESTINATIONS: Number of trap destinations. At least one trap
+ * destination is required
+ */
+#ifndef SNMP_TRAP_DESTINATIONS
+#define SNMP_TRAP_DESTINATIONS 1
+#endif
+
+/**
+ * SNMP_PRIVATE_MIB:
+ * When using a private MIB, you have to create a file 'private_mib.h' that contains
+ * a 'struct mib_array_node mib_private' which contains your MIB.
+ */
+#ifndef SNMP_PRIVATE_MIB
+#define SNMP_PRIVATE_MIB 0
+#endif
+
+/**
+ * Only allow SNMP write actions that are 'safe' (e.g. disabeling netifs is not
+ * a safe action and disabled when SNMP_SAFE_REQUESTS = 1).
+ * Unsafe requests are disabled by default!
+ */
+#ifndef SNMP_SAFE_REQUESTS
+#define SNMP_SAFE_REQUESTS 1
+#endif
+
+/**
+ * The maximum length of strings used. This affects the size of
+ * MEMP_SNMP_VALUE elements.
+ */
+#ifndef SNMP_MAX_OCTET_STRING_LEN
+#define SNMP_MAX_OCTET_STRING_LEN 127
+#endif
+
+/**
+ * The maximum depth of the SNMP tree.
+ * With private MIBs enabled, this depends on your MIB!
+ * This affects the size of MEMP_SNMP_VALUE elements.
+ */
+#ifndef SNMP_MAX_TREE_DEPTH
+#define SNMP_MAX_TREE_DEPTH 15
+#endif
+
+/**
+ * The size of the MEMP_SNMP_VALUE elements, normally calculated from
+ * SNMP_MAX_OCTET_STRING_LEN and SNMP_MAX_TREE_DEPTH.
+ */
+#ifndef SNMP_MAX_VALUE_SIZE
+#define SNMP_MAX_VALUE_SIZE LWIP_MAX((SNMP_MAX_OCTET_STRING_LEN)+1, sizeof(s32_t)*(SNMP_MAX_TREE_DEPTH))
+#endif
+
+/*
+ ----------------------------------
+ ---------- IGMP options ----------
+ ----------------------------------
+*/
+/**
+ * LWIP_IGMP==1: Turn on IGMP module.
+ */
+#ifndef LWIP_IGMP
+#define LWIP_IGMP 0
+#endif
+
+/*
+ ----------------------------------
+ ---------- DNS options -----------
+ ----------------------------------
+*/
+/**
+ * LWIP_DNS==1: Turn on DNS module. UDP must be available for DNS
+ * transport.
+ */
+#ifndef LWIP_DNS
+#define LWIP_DNS 0
+#endif
+
+/** DNS maximum number of entries to maintain locally. */
+#ifndef DNS_TABLE_SIZE
+#define DNS_TABLE_SIZE 4
+#endif
+
+/** DNS maximum host name length supported in the name table. */
+#ifndef DNS_MAX_NAME_LENGTH
+#define DNS_MAX_NAME_LENGTH 256
+#endif
+
+/** The maximum of DNS servers */
+#ifndef DNS_MAX_SERVERS
+#define DNS_MAX_SERVERS 2
+#endif
+
+/** DNS do a name checking between the query and the response. */
+#ifndef DNS_DOES_NAME_CHECK
+#define DNS_DOES_NAME_CHECK 1
+#endif
+
+/** DNS message max. size. Default value is RFC compliant. */
+#ifndef DNS_MSG_SIZE
+#define DNS_MSG_SIZE 512
+#endif
+
+/** DNS_LOCAL_HOSTLIST: Implements a local host-to-address list. If enabled,
+ * you have to define
+ * #define DNS_LOCAL_HOSTLIST_INIT {{"host1", 0x123}, {"host2", 0x234}}
+ * (an array of structs name/address, where address is an u32_t in network
+ * byte order).
+ *
+ * Instead, you can also use an external function:
+ * #define DNS_LOOKUP_LOCAL_EXTERN(x) extern u32_t my_lookup_function(const char *name)
+ * that returns the IP address or INADDR_NONE if not found.
+ */
+#ifndef DNS_LOCAL_HOSTLIST
+#define DNS_LOCAL_HOSTLIST 0
+#endif /* DNS_LOCAL_HOSTLIST */
+
+/** If this is turned on, the local host-list can be dynamically changed
+ * at runtime. */
+#ifndef DNS_LOCAL_HOSTLIST_IS_DYNAMIC
+#define DNS_LOCAL_HOSTLIST_IS_DYNAMIC 0
+#endif /* DNS_LOCAL_HOSTLIST_IS_DYNAMIC */
+
+/*
+ ---------------------------------
+ ---------- UDP options ----------
+ ---------------------------------
+*/
+/**
+ * LWIP_UDP==1: Turn on UDP.
+ */
+#ifndef LWIP_UDP
+#define LWIP_UDP 1
+#endif
+
+/**
+ * LWIP_UDPLITE==1: Turn on UDP-Lite. (Requires LWIP_UDP)
+ */
+#ifndef LWIP_UDPLITE
+#define LWIP_UDPLITE 0
+#endif
+
+/**
+ * UDP_TTL: Default Time-To-Live value.
+ */
+#ifndef UDP_TTL
+#define UDP_TTL (IP_DEFAULT_TTL)
+#endif
+
+/**
+ * LWIP_NETBUF_RECVINFO==1: append destination addr and port to every netbuf.
+ */
+#ifndef LWIP_NETBUF_RECVINFO
+#define LWIP_NETBUF_RECVINFO 0
+#endif
+
+/*
+ ---------------------------------
+ ---------- TCP options ----------
+ ---------------------------------
+*/
+/**
+ * LWIP_TCP==1: Turn on TCP.
+ */
+#ifndef LWIP_TCP
+#define LWIP_TCP 1
+#endif
+
+/**
+ * TCP_TTL: Default Time-To-Live value.
+ */
+#ifndef TCP_TTL
+#define TCP_TTL (IP_DEFAULT_TTL)
+#endif
+
+/**
+ * TCP_WND: The size of a TCP window. This must be at least
+ * (2 * TCP_MSS) for things to work well
+ */
+#ifndef TCP_WND
+#define TCP_WND (4 * TCP_MSS)
+#endif
+
+/**
+ * TCP_MAXRTX: Maximum number of retransmissions of data segments.
+ */
+#ifndef TCP_MAXRTX
+#define TCP_MAXRTX 12
+#endif
+
+/**
+ * TCP_SYNMAXRTX: Maximum number of retransmissions of SYN segments.
+ */
+#ifndef TCP_SYNMAXRTX
+#define TCP_SYNMAXRTX 6
+#endif
+
+/**
+ * TCP_QUEUE_OOSEQ==1: TCP will queue segments that arrive out of order.
+ * Define to 0 if your device is low on memory.
+ */
+#ifndef TCP_QUEUE_OOSEQ
+#define TCP_QUEUE_OOSEQ (LWIP_TCP)
+#endif
+
+/**
+ * TCP_MSS: TCP Maximum segment size. (default is 536, a conservative default,
+ * you might want to increase this.)
+ * For the receive side, this MSS is advertised to the remote side
+ * when opening a connection. For the transmit size, this MSS sets
+ * an upper limit on the MSS advertised by the remote host.
+ */
+#ifndef TCP_MSS
+#define TCP_MSS 536
+#endif
+
+/**
+ * TCP_CALCULATE_EFF_SEND_MSS: "The maximum size of a segment that TCP really
+ * sends, the 'effective send MSS,' MUST be the smaller of the send MSS (which
+ * reflects the available reassembly buffer size at the remote host) and the
+ * largest size permitted by the IP layer" (RFC 1122)
+ * Setting this to 1 enables code that checks TCP_MSS against the MTU of the
+ * netif used for a connection and limits the MSS if it would be too big otherwise.
+ */
+#ifndef TCP_CALCULATE_EFF_SEND_MSS
+#define TCP_CALCULATE_EFF_SEND_MSS 1
+#endif
+
+
+/**
+ * TCP_SND_BUF: TCP sender buffer space (bytes).
+ * To achieve good performance, this should be at least 2 * TCP_MSS.
+ */
+#ifndef TCP_SND_BUF
+#define TCP_SND_BUF (2 * TCP_MSS)
+#endif
+
+/**
+ * TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least
+ * as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work.
+ */
+#ifndef TCP_SND_QUEUELEN
+#define TCP_SND_QUEUELEN ((4 * (TCP_SND_BUF) + (TCP_MSS - 1))/(TCP_MSS))
+#endif
+
+/**
+ * TCP_SNDLOWAT: TCP writable space (bytes). This must be less than
+ * TCP_SND_BUF. It is the amount of space which must be available in the
+ * TCP snd_buf for select to return writable (combined with TCP_SNDQUEUELOWAT).
+ */
+#ifndef TCP_SNDLOWAT
+#define TCP_SNDLOWAT LWIP_MIN(LWIP_MAX(((TCP_SND_BUF)/2), (2 * TCP_MSS) + 1), (TCP_SND_BUF) - 1)
+#endif
+
+/**
+ * TCP_SNDQUEUELOWAT: TCP writable bufs (pbuf count). This must be less
+ * than TCP_SND_QUEUELEN. If the number of pbufs queued on a pcb drops below
+ * this number, select returns writable (combined with TCP_SNDLOWAT).
+ */
+#ifndef TCP_SNDQUEUELOWAT
+#define TCP_SNDQUEUELOWAT LWIP_MAX(((TCP_SND_QUEUELEN)/2), 5)
+#endif
+
+/**
+ * TCP_OOSEQ_MAX_BYTES: The maximum number of bytes queued on ooseq per pcb.
+ * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==0.
+ */
+#ifndef TCP_OOSEQ_MAX_BYTES
+#define TCP_OOSEQ_MAX_BYTES 0
+#endif
+
+/**
+ * TCP_OOSEQ_MAX_PBUFS: The maximum number of pbufs queued on ooseq per pcb.
+ * Default is 0 (no limit). Only valid for TCP_QUEUE_OOSEQ==0.
+ */
+#ifndef TCP_OOSEQ_MAX_PBUFS
+#define TCP_OOSEQ_MAX_PBUFS 0
+#endif
+
+/**
+ * TCP_LISTEN_BACKLOG: Enable the backlog option for tcp listen pcb.
+ */
+#ifndef TCP_LISTEN_BACKLOG
+#define TCP_LISTEN_BACKLOG 0
+#endif
+
+/**
+ * The maximum allowed backlog for TCP listen netconns.
+ * This backlog is used unless another is explicitly specified.
+ * 0xff is the maximum (u8_t).
+ */
+#ifndef TCP_DEFAULT_LISTEN_BACKLOG
+#define TCP_DEFAULT_LISTEN_BACKLOG 0xff
+#endif
+
+/**
+ * TCP_OVERSIZE: The maximum number of bytes that tcp_write may
+ * allocate ahead of time in an attempt to create shorter pbuf chains
+ * for transmission. The meaningful range is 0 to TCP_MSS. Some
+ * suggested values are:
+ *
+ * 0: Disable oversized allocation. Each tcp_write() allocates a new
+ pbuf (old behaviour).
+ * 1: Allocate size-aligned pbufs with minimal excess. Use this if your
+ * scatter-gather DMA requires aligned fragments.
+ * 128: Limit the pbuf/memory overhead to 20%.
+ * TCP_MSS: Try to create unfragmented TCP packets.
+ * TCP_MSS/4: Try to create 4 fragments or less per TCP packet.
+ */
+#ifndef TCP_OVERSIZE
+#define TCP_OVERSIZE TCP_MSS
+#endif
+
+/**
+ * LWIP_TCP_TIMESTAMPS==1: support the TCP timestamp option.
+ */
+#ifndef LWIP_TCP_TIMESTAMPS
+#define LWIP_TCP_TIMESTAMPS 0
+#endif
+
+/**
+ * TCP_WND_UPDATE_THRESHOLD: difference in window to trigger an
+ * explicit window update
+ */
+#ifndef TCP_WND_UPDATE_THRESHOLD
+#define TCP_WND_UPDATE_THRESHOLD (TCP_WND / 4)
+#endif
+
+/**
+ * LWIP_EVENT_API and LWIP_CALLBACK_API: Only one of these should be set to 1.
+ * LWIP_EVENT_API==1: The user defines lwip_tcp_event() to receive all
+ * events (accept, sent, etc) that happen in the system.
+ * LWIP_CALLBACK_API==1: The PCB callback function is called directly
+ * for the event. This is the default.
+ */
+#if !defined(LWIP_EVENT_API) && !defined(LWIP_CALLBACK_API)
+#define LWIP_EVENT_API 0
+#define LWIP_CALLBACK_API 1
+#endif
+
+
+/*
+ ----------------------------------
+ ---------- Pbuf options ----------
+ ----------------------------------
+*/
+/**
+ * PBUF_LINK_HLEN: the number of bytes that should be allocated for a
+ * link level header. The default is 14, the standard value for
+ * Ethernet.
+ */
+#ifndef PBUF_LINK_HLEN
+#define PBUF_LINK_HLEN (14 + ETH_PAD_SIZE)
+#endif
+
+/**
+ * PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. The default is
+ * designed to accomodate single full size TCP frame in one pbuf, including
+ * TCP_MSS, IP header, and link header.
+ */
+#ifndef PBUF_POOL_BUFSIZE
+#define PBUF_POOL_BUFSIZE LWIP_MEM_ALIGN_SIZE(TCP_MSS+40+PBUF_LINK_HLEN)
+#endif
+
+/*
+ ------------------------------------------------
+ ---------- Network Interfaces options ----------
+ ------------------------------------------------
+*/
+/**
+ * LWIP_NETIF_HOSTNAME==1: use DHCP_OPTION_HOSTNAME with netif's hostname
+ * field.
+ */
+#ifndef LWIP_NETIF_HOSTNAME
+#define LWIP_NETIF_HOSTNAME 0
+#endif
+
+/**
+ * LWIP_NETIF_API==1: Support netif api (in netifapi.c)
+ */
+#ifndef LWIP_NETIF_API
+#define LWIP_NETIF_API 0
+#endif
+
+/**
+ * LWIP_NETIF_STATUS_CALLBACK==1: Support a callback function whenever an interface
+ * changes its up/down status (i.e., due to DHCP IP acquistion)
+ */
+#ifndef LWIP_NETIF_STATUS_CALLBACK
+#define LWIP_NETIF_STATUS_CALLBACK 0
+#endif
+
+/**
+ * LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface
+ * whenever the link changes (i.e., link down)
+ */
+#ifndef LWIP_NETIF_LINK_CALLBACK
+#define LWIP_NETIF_LINK_CALLBACK 0
+#endif
+
+/**
+ * LWIP_NETIF_REMOVE_CALLBACK==1: Support a callback function that is called
+ * when a netif has been removed
+ */
+#ifndef LWIP_NETIF_REMOVE_CALLBACK
+#define LWIP_NETIF_REMOVE_CALLBACK 0
+#endif
+
+/**
+ * LWIP_NETIF_HWADDRHINT==1: Cache link-layer-address hints (e.g. table
+ * indices) in struct netif. TCP and UDP can make use of this to prevent
+ * scanning the ARP table for every sent packet. While this is faster for big
+ * ARP tables or many concurrent connections, it might be counterproductive
+ * if you have a tiny ARP table or if there never are concurrent connections.
+ */
+#ifndef LWIP_NETIF_HWADDRHINT
+#define LWIP_NETIF_HWADDRHINT 0
+#endif
+
+/**
+ * LWIP_NETIF_LOOPBACK==1: Support sending packets with a destination IP
+ * address equal to the netif IP address, looping them back up the stack.
+ */
+#ifndef LWIP_NETIF_LOOPBACK
+#define LWIP_NETIF_LOOPBACK 0
+#endif
+
+/**
+ * LWIP_LOOPBACK_MAX_PBUFS: Maximum number of pbufs on queue for loopback
+ * sending for each netif (0 = disabled)
+ */
+#ifndef LWIP_LOOPBACK_MAX_PBUFS
+#define LWIP_LOOPBACK_MAX_PBUFS 0
+#endif
+
+/**
+ * LWIP_NETIF_LOOPBACK_MULTITHREADING: Indicates whether threading is enabled in
+ * the system, as netifs must change how they behave depending on this setting
+ * for the LWIP_NETIF_LOOPBACK option to work.
+ * Setting this is needed to avoid reentering non-reentrant functions like
+ * tcp_input().
+ * LWIP_NETIF_LOOPBACK_MULTITHREADING==1: Indicates that the user is using a
+ * multithreaded environment like tcpip.c. In this case, netif->input()
+ * is called directly.
+ * LWIP_NETIF_LOOPBACK_MULTITHREADING==0: Indicates a polling (or NO_SYS) setup.
+ * The packets are put on a list and netif_poll() must be called in
+ * the main application loop.
+ */
+#ifndef LWIP_NETIF_LOOPBACK_MULTITHREADING
+#define LWIP_NETIF_LOOPBACK_MULTITHREADING (!NO_SYS)
+#endif
+
+/**
+ * LWIP_NETIF_TX_SINGLE_PBUF: if this is set to 1, lwIP tries to put all data
+ * to be sent into one single pbuf. This is for compatibility with DMA-enabled
+ * MACs that do not support scatter-gather.
+ * Beware that this might involve CPU-memcpy before transmitting that would not
+ * be needed without this flag! Use this only if you need to!
+ *
+ * @todo: TCP and IP-frag do not work with this, yet:
+ */
+#ifndef LWIP_NETIF_TX_SINGLE_PBUF
+#define LWIP_NETIF_TX_SINGLE_PBUF 0
+#endif /* LWIP_NETIF_TX_SINGLE_PBUF */
+
+/*
+ ------------------------------------
+ ---------- LOOPIF options ----------
+ ------------------------------------
+*/
+/**
+ * LWIP_HAVE_LOOPIF==1: Support loop interface (127.0.0.1) and loopif.c
+ */
+#ifndef LWIP_HAVE_LOOPIF
+#define LWIP_HAVE_LOOPIF 0
+#endif
+
+/*
+ ------------------------------------
+ ---------- SLIPIF options ----------
+ ------------------------------------
+*/
+/**
+ * LWIP_HAVE_SLIPIF==1: Support slip interface and slipif.c
+ */
+#ifndef LWIP_HAVE_SLIPIF
+#define LWIP_HAVE_SLIPIF 0
+#endif
+
+/*
+ ------------------------------------
+ ---------- Thread options ----------
+ ------------------------------------
+*/
+/**
+ * TCPIP_THREAD_NAME: The name assigned to the main tcpip thread.
+ */
+#ifndef TCPIP_THREAD_NAME
+#define TCPIP_THREAD_NAME "tcpip_thread"
+#endif
+
+/**
+ * TCPIP_THREAD_STACKSIZE: The stack size used by the main tcpip thread.
+ * The stack size value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#ifndef TCPIP_THREAD_STACKSIZE
+#define TCPIP_THREAD_STACKSIZE 0
+#endif
+
+/**
+ * TCPIP_THREAD_PRIO: The priority assigned to the main tcpip thread.
+ * The priority value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#ifndef TCPIP_THREAD_PRIO
+#define TCPIP_THREAD_PRIO 1
+#endif
+
+/**
+ * TCPIP_MBOX_SIZE: The mailbox size for the tcpip thread messages
+ * The queue size value itself is platform-dependent, but is passed to
+ * sys_mbox_new() when tcpip_init is called.
+ */
+#ifndef TCPIP_MBOX_SIZE
+#define TCPIP_MBOX_SIZE 0
+#endif
+
+/**
+ * SLIPIF_THREAD_NAME: The name assigned to the slipif_loop thread.
+ */
+#ifndef SLIPIF_THREAD_NAME
+#define SLIPIF_THREAD_NAME "slipif_loop"
+#endif
+
+/**
+ * SLIP_THREAD_STACKSIZE: The stack size used by the slipif_loop thread.
+ * The stack size value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#ifndef SLIPIF_THREAD_STACKSIZE
+#define SLIPIF_THREAD_STACKSIZE 0
+#endif
+
+/**
+ * SLIPIF_THREAD_PRIO: The priority assigned to the slipif_loop thread.
+ * The priority value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#ifndef SLIPIF_THREAD_PRIO
+#define SLIPIF_THREAD_PRIO 1
+#endif
+
+/**
+ * PPP_THREAD_NAME: The name assigned to the pppInputThread.
+ */
+#ifndef PPP_THREAD_NAME
+#define PPP_THREAD_NAME "pppInputThread"
+#endif
+
+/**
+ * PPP_THREAD_STACKSIZE: The stack size used by the pppInputThread.
+ * The stack size value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#ifndef PPP_THREAD_STACKSIZE
+#define PPP_THREAD_STACKSIZE 0
+#endif
+
+/**
+ * PPP_THREAD_PRIO: The priority assigned to the pppInputThread.
+ * The priority value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#ifndef PPP_THREAD_PRIO
+#define PPP_THREAD_PRIO 1
+#endif
+
+/**
+ * DEFAULT_THREAD_NAME: The name assigned to any other lwIP thread.
+ */
+#ifndef DEFAULT_THREAD_NAME
+#define DEFAULT_THREAD_NAME "lwIP"
+#endif
+
+/**
+ * DEFAULT_THREAD_STACKSIZE: The stack size used by any other lwIP thread.
+ * The stack size value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#ifndef DEFAULT_THREAD_STACKSIZE
+#define DEFAULT_THREAD_STACKSIZE 0
+#endif
+
+/**
+ * DEFAULT_THREAD_PRIO: The priority assigned to any other lwIP thread.
+ * The priority value itself is platform-dependent, but is passed to
+ * sys_thread_new() when the thread is created.
+ */
+#ifndef DEFAULT_THREAD_PRIO
+#define DEFAULT_THREAD_PRIO 1
+#endif
+
+/**
+ * DEFAULT_RAW_RECVMBOX_SIZE: The mailbox size for the incoming packets on a
+ * NETCONN_RAW. The queue size value itself is platform-dependent, but is passed
+ * to sys_mbox_new() when the recvmbox is created.
+ */
+#ifndef DEFAULT_RAW_RECVMBOX_SIZE
+#define DEFAULT_RAW_RECVMBOX_SIZE 0
+#endif
+
+/**
+ * DEFAULT_UDP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a
+ * NETCONN_UDP. The queue size value itself is platform-dependent, but is passed
+ * to sys_mbox_new() when the recvmbox is created.
+ */
+#ifndef DEFAULT_UDP_RECVMBOX_SIZE
+#define DEFAULT_UDP_RECVMBOX_SIZE 0
+#endif
+
+/**
+ * DEFAULT_TCP_RECVMBOX_SIZE: The mailbox size for the incoming packets on a
+ * NETCONN_TCP. The queue size value itself is platform-dependent, but is passed
+ * to sys_mbox_new() when the recvmbox is created.
+ */
+#ifndef DEFAULT_TCP_RECVMBOX_SIZE
+#define DEFAULT_TCP_RECVMBOX_SIZE 0
+#endif
+
+/**
+ * DEFAULT_ACCEPTMBOX_SIZE: The mailbox size for the incoming connections.
+ * The queue size value itself is platform-dependent, but is passed to
+ * sys_mbox_new() when the acceptmbox is created.
+ */
+#ifndef DEFAULT_ACCEPTMBOX_SIZE
+#define DEFAULT_ACCEPTMBOX_SIZE 0
+#endif
+
+/*
+ ----------------------------------------------
+ ---------- Sequential layer options ----------
+ ----------------------------------------------
+*/
+/**
+ * LWIP_TCPIP_CORE_LOCKING: (EXPERIMENTAL!)
+ * Don't use it if you're not an active lwIP project member
+ */
+#ifndef LWIP_TCPIP_CORE_LOCKING
+#define LWIP_TCPIP_CORE_LOCKING 0
+#endif
+
+/**
+ * LWIP_TCPIP_CORE_LOCKING_INPUT: (EXPERIMENTAL!)
+ * Don't use it if you're not an active lwIP project member
+ */
+#ifndef LWIP_TCPIP_CORE_LOCKING_INPUT
+#define LWIP_TCPIP_CORE_LOCKING_INPUT 0
+#endif
+
+/**
+ * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c)
+ */
+#ifndef LWIP_NETCONN
+#define LWIP_NETCONN 1
+#endif
+
+/** LWIP_TCPIP_TIMEOUT==1: Enable tcpip_timeout/tcpip_untimeout tod create
+ * timers running in tcpip_thread from another thread.
+ */
+#ifndef LWIP_TCPIP_TIMEOUT
+#define LWIP_TCPIP_TIMEOUT 1
+#endif
+
+/*
+ ------------------------------------
+ ---------- Socket options ----------
+ ------------------------------------
+*/
+/**
+ * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c)
+ */
+#ifndef LWIP_SOCKET
+#define LWIP_SOCKET 1
+#endif
+
+/**
+ * LWIP_COMPAT_SOCKETS==1: Enable BSD-style sockets functions names.
+ * (only used if you use sockets.c)
+ */
+#ifndef LWIP_COMPAT_SOCKETS
+#define LWIP_COMPAT_SOCKETS 1
+#endif
+
+/**
+ * LWIP_POSIX_SOCKETS_IO_NAMES==1: Enable POSIX-style sockets functions names.
+ * Disable this option if you use a POSIX operating system that uses the same
+ * names (read, write & close). (only used if you use sockets.c)
+ */
+#ifndef LWIP_POSIX_SOCKETS_IO_NAMES
+#define LWIP_POSIX_SOCKETS_IO_NAMES 1
+#endif
+
+/**
+ * LWIP_TCP_KEEPALIVE==1: Enable TCP_KEEPIDLE, TCP_KEEPINTVL and TCP_KEEPCNT
+ * options processing. Note that TCP_KEEPIDLE and TCP_KEEPINTVL have to be set
+ * in seconds. (does not require sockets.c, and will affect tcp.c)
+ */
+#ifndef LWIP_TCP_KEEPALIVE
+#define LWIP_TCP_KEEPALIVE 0
+#endif
+
+/**
+ * LWIP_SO_SNDTIMEO==1: Enable send timeout for sockets/netconns and
+ * SO_SNDTIMEO processing.
+ */
+#ifndef LWIP_SO_SNDTIMEO
+#define LWIP_SO_SNDTIMEO 0
+#endif
+
+/**
+ * LWIP_SO_RCVTIMEO==1: Enable receive timeout for sockets/netconns and
+ * SO_RCVTIMEO processing.
+ */
+#ifndef LWIP_SO_RCVTIMEO
+#define LWIP_SO_RCVTIMEO 0
+#endif
+
+/**
+ * LWIP_SO_RCVBUF==1: Enable SO_RCVBUF processing.
+ */
+#ifndef LWIP_SO_RCVBUF
+#define LWIP_SO_RCVBUF 0
+#endif
+
+/**
+ * If LWIP_SO_RCVBUF is used, this is the default value for recv_bufsize.
+ */
+#ifndef RECV_BUFSIZE_DEFAULT
+#define RECV_BUFSIZE_DEFAULT INT_MAX
+#endif
+
+/**
+ * SO_REUSE==1: Enable SO_REUSEADDR option.
+ */
+#ifndef SO_REUSE
+#define SO_REUSE 0
+#endif
+
+/**
+ * SO_REUSE_RXTOALL==1: Pass a copy of incoming broadcast/multicast packets
+ * to all local matches if SO_REUSEADDR is turned on.
+ * WARNING: Adds a memcpy for every packet if passing to more than one pcb!
+ */
+#ifndef SO_REUSE_RXTOALL
+#define SO_REUSE_RXTOALL 0
+#endif
+
+/**
+ * LWIP_FIONREAD_LINUXMODE==0 (default): ioctl/FIONREAD returns the amount of
+ * pending data in the network buffer. This is the way windows does it. It's
+ * the default for lwIP since it is smaller.
+ * LWIP_FIONREAD_LINUXMODE==1: ioctl/FIONREAD returns the size of the next
+ * pending datagram in bytes. This is the way linux does it. This code is only
+ * here for compatibility.
+ */
+#ifndef LWIP_FIONREAD_LINUXMODE
+#define LWIP_FIONREAD_LINUXMODE 0
+#endif
+
+/*
+ ----------------------------------------
+ ---------- Statistics options ----------
+ ----------------------------------------
+*/
+/**
+ * LWIP_STATS==1: Enable statistics collection in lwip_stats.
+ */
+#ifndef LWIP_STATS
+#define LWIP_STATS 1
+#endif
+
+#if LWIP_STATS
+
+/**
+ * LWIP_STATS_DISPLAY==1: Compile in the statistics output functions.
+ */
+#ifndef LWIP_STATS_DISPLAY
+#define LWIP_STATS_DISPLAY 0
+#endif
+
+/**
+ * LINK_STATS==1: Enable link stats.
+ */
+#ifndef LINK_STATS
+#define LINK_STATS 1
+#endif
+
+/**
+ * ETHARP_STATS==1: Enable etharp stats.
+ */
+#ifndef ETHARP_STATS
+#define ETHARP_STATS (LWIP_ARP)
+#endif
+
+/**
+ * IP_STATS==1: Enable IP stats.
+ */
+#ifndef IP_STATS
+#define IP_STATS 1
+#endif
+
+/**
+ * IPFRAG_STATS==1: Enable IP fragmentation stats. Default is
+ * on if using either frag or reass.
+ */
+#ifndef IPFRAG_STATS
+#define IPFRAG_STATS (IP_REASSEMBLY || IP_FRAG)
+#endif
+
+/**
+ * ICMP_STATS==1: Enable ICMP stats.
+ */
+#ifndef ICMP_STATS
+#define ICMP_STATS 1
+#endif
+
+/**
+ * IGMP_STATS==1: Enable IGMP stats.
+ */
+#ifndef IGMP_STATS
+#define IGMP_STATS (LWIP_IGMP)
+#endif
+
+/**
+ * UDP_STATS==1: Enable UDP stats. Default is on if
+ * UDP enabled, otherwise off.
+ */
+#ifndef UDP_STATS
+#define UDP_STATS (LWIP_UDP)
+#endif
+
+/**
+ * TCP_STATS==1: Enable TCP stats. Default is on if TCP
+ * enabled, otherwise off.
+ */
+#ifndef TCP_STATS
+#define TCP_STATS (LWIP_TCP)
+#endif
+
+/**
+ * MEM_STATS==1: Enable mem.c stats.
+ */
+#ifndef MEM_STATS
+#define MEM_STATS ((MEM_LIBC_MALLOC == 0) && (MEM_USE_POOLS == 0))
+#endif
+
+/**
+ * MEMP_STATS==1: Enable memp.c pool stats.
+ */
+#ifndef MEMP_STATS
+#define MEMP_STATS (MEMP_MEM_MALLOC == 0)
+#endif
+
+/**
+ * SYS_STATS==1: Enable system stats (sem and mbox counts, etc).
+ */
+#ifndef SYS_STATS
+#define SYS_STATS (NO_SYS == 0)
+#endif
+
+/**
+ * IP6_STATS==1: Enable IPv6 stats.
+ */
+#ifndef IP6_STATS
+#define IP6_STATS (LWIP_IPV6)
+#endif
+
+/**
+ * ICMP6_STATS==1: Enable ICMP for IPv6 stats.
+ */
+#ifndef ICMP6_STATS
+#define ICMP6_STATS (LWIP_IPV6 && LWIP_ICMP6)
+#endif
+
+/**
+ * IP6_FRAG_STATS==1: Enable IPv6 fragmentation stats.
+ */
+#ifndef IP6_FRAG_STATS
+#define IP6_FRAG_STATS (LWIP_IPV6 && (LWIP_IPV6_FRAG || LWIP_IPV6_REASS))
+#endif
+
+/**
+ * MLD6_STATS==1: Enable MLD for IPv6 stats.
+ */
+#ifndef MLD6_STATS
+#define MLD6_STATS (LWIP_IPV6 && LWIP_IPV6_MLD)
+#endif
+
+/**
+ * ND6_STATS==1: Enable Neighbor discovery for IPv6 stats.
+ */
+#ifndef ND6_STATS
+#define ND6_STATS (LWIP_IPV6)
+#endif
+
+#else
+
+#define LINK_STATS 0
+#define ETHARP_STATS 0
+#define IP_STATS 0
+#define IPFRAG_STATS 0
+#define ICMP_STATS 0
+#define IGMP_STATS 0
+#define UDP_STATS 0
+#define TCP_STATS 0
+#define MEM_STATS 0
+#define MEMP_STATS 0
+#define SYS_STATS 0
+#define LWIP_STATS_DISPLAY 0
+#define IP6_STATS 0
+#define ICMP6_STATS 0
+#define IP6_FRAG_STATS 0
+#define MLD6_STATS 0
+#define ND6_STATS 0
+
+#endif /* LWIP_STATS */
+
+/*
+ ---------------------------------
+ ---------- PPP options ----------
+ ---------------------------------
+*/
+/**
+ * PPP_SUPPORT==1: Enable PPP.
+ */
+#ifndef PPP_SUPPORT
+#define PPP_SUPPORT 0
+#endif
+
+/**
+ * PPPOE_SUPPORT==1: Enable PPP Over Ethernet
+ */
+#ifndef PPPOE_SUPPORT
+#define PPPOE_SUPPORT 0
+#endif
+
+/**
+ * PPPOS_SUPPORT==1: Enable PPP Over Serial
+ */
+#ifndef PPPOS_SUPPORT
+#define PPPOS_SUPPORT PPP_SUPPORT
+#endif
+
+#if PPP_SUPPORT
+
+/**
+ * NUM_PPP: Max PPP sessions.
+ */
+#ifndef NUM_PPP
+#define NUM_PPP 1
+#endif
+
+/**
+ * PAP_SUPPORT==1: Support PAP.
+ */
+#ifndef PAP_SUPPORT
+#define PAP_SUPPORT 0
+#endif
+
+/**
+ * CHAP_SUPPORT==1: Support CHAP.
+ */
+#ifndef CHAP_SUPPORT
+#define CHAP_SUPPORT 0
+#endif
+
+/**
+ * MSCHAP_SUPPORT==1: Support MSCHAP. CURRENTLY NOT SUPPORTED! DO NOT SET!
+ */
+#ifndef MSCHAP_SUPPORT
+#define MSCHAP_SUPPORT 0
+#endif
+
+/**
+ * CBCP_SUPPORT==1: Support CBCP. CURRENTLY NOT SUPPORTED! DO NOT SET!
+ */
+#ifndef CBCP_SUPPORT
+#define CBCP_SUPPORT 0
+#endif
+
+/**
+ * CCP_SUPPORT==1: Support CCP. CURRENTLY NOT SUPPORTED! DO NOT SET!
+ */
+#ifndef CCP_SUPPORT
+#define CCP_SUPPORT 0
+#endif
+
+/**
+ * VJ_SUPPORT==1: Support VJ header compression.
+ */
+#ifndef VJ_SUPPORT
+#define VJ_SUPPORT 0
+#endif
+
+/**
+ * MD5_SUPPORT==1: Support MD5 (see also CHAP).
+ */
+#ifndef MD5_SUPPORT
+#define MD5_SUPPORT 0
+#endif
+
+/*
+ * Timeouts
+ */
+#ifndef FSM_DEFTIMEOUT
+#define FSM_DEFTIMEOUT 6 /* Timeout time in seconds */
+#endif
+
+#ifndef FSM_DEFMAXTERMREQS
+#define FSM_DEFMAXTERMREQS 2 /* Maximum Terminate-Request transmissions */
+#endif
+
+#ifndef FSM_DEFMAXCONFREQS
+#define FSM_DEFMAXCONFREQS 10 /* Maximum Configure-Request transmissions */
+#endif
+
+#ifndef FSM_DEFMAXNAKLOOPS
+#define FSM_DEFMAXNAKLOOPS 5 /* Maximum number of nak loops */
+#endif
+
+#ifndef UPAP_DEFTIMEOUT
+#define UPAP_DEFTIMEOUT 6 /* Timeout (seconds) for retransmitting req */
+#endif
+
+#ifndef UPAP_DEFREQTIME
+#define UPAP_DEFREQTIME 30 /* Time to wait for auth-req from peer */
+#endif
+
+#ifndef CHAP_DEFTIMEOUT
+#define CHAP_DEFTIMEOUT 6 /* Timeout time in seconds */
+#endif
+
+#ifndef CHAP_DEFTRANSMITS
+#define CHAP_DEFTRANSMITS 10 /* max # times to send challenge */
+#endif
+
+/* Interval in seconds between keepalive echo requests, 0 to disable. */
+#ifndef LCP_ECHOINTERVAL
+#define LCP_ECHOINTERVAL 0
+#endif
+
+/* Number of unanswered echo requests before failure. */
+#ifndef LCP_MAXECHOFAILS
+#define LCP_MAXECHOFAILS 3
+#endif
+
+/* Max Xmit idle time (in jiffies) before resend flag char. */
+#ifndef PPP_MAXIDLEFLAG
+#define PPP_MAXIDLEFLAG 100
+#endif
+
+/*
+ * Packet sizes
+ *
+ * Note - lcp shouldn't be allowed to negotiate stuff outside these
+ * limits. See lcp.h in the pppd directory.
+ * (XXX - these constants should simply be shared by lcp.c instead
+ * of living in lcp.h)
+ */
+#define PPP_MTU 1500 /* Default MTU (size of Info field) */
+#ifndef PPP_MAXMTU
+/* #define PPP_MAXMTU 65535 - (PPP_HDRLEN + PPP_FCSLEN) */
+#define PPP_MAXMTU 1500 /* Largest MTU we allow */
+#endif
+#define PPP_MINMTU 64
+#define PPP_MRU 1500 /* default MRU = max length of info field */
+#define PPP_MAXMRU 1500 /* Largest MRU we allow */
+#ifndef PPP_DEFMRU
+#define PPP_DEFMRU 296 /* Try for this */
+#endif
+#define PPP_MINMRU 128 /* No MRUs below this */
+
+#ifndef MAXNAMELEN
+#define MAXNAMELEN 256 /* max length of hostname or name for auth */
+#endif
+#ifndef MAXSECRETLEN
+#define MAXSECRETLEN 256 /* max length of password or secret */
+#endif
+
+#endif /* PPP_SUPPORT */
+
+/*
+ --------------------------------------
+ ---------- Checksum options ----------
+ --------------------------------------
+*/
+/**
+ * CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.
+ */
+#ifndef CHECKSUM_GEN_IP
+#define CHECKSUM_GEN_IP 1
+#endif
+
+/**
+ * CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.
+ */
+#ifndef CHECKSUM_GEN_UDP
+#define CHECKSUM_GEN_UDP 1
+#endif
+
+/**
+ * CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.
+ */
+#ifndef CHECKSUM_GEN_TCP
+#define CHECKSUM_GEN_TCP 1
+#endif
+
+/**
+ * CHECKSUM_GEN_ICMP==1: Generate checksums in software for outgoing ICMP packets.
+ */
+#ifndef CHECKSUM_GEN_ICMP
+#define CHECKSUM_GEN_ICMP 1
+#endif
+
+/**
+ * CHECKSUM_GEN_ICMP6==1: Generate checksums in software for outgoing ICMP6 packets.
+ */
+#ifndef CHECKSUM_GEN_ICMP6
+#define CHECKSUM_GEN_ICMP6 1
+#endif
+
+/**
+ * CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.
+ */
+#ifndef CHECKSUM_CHECK_IP
+#define CHECKSUM_CHECK_IP 1
+#endif
+
+/**
+ * CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.
+ */
+#ifndef CHECKSUM_CHECK_UDP
+#define CHECKSUM_CHECK_UDP 1
+#endif
+
+/**
+ * CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.
+ */
+#ifndef CHECKSUM_CHECK_TCP
+#define CHECKSUM_CHECK_TCP 1
+#endif
+
+/**
+ * CHECKSUM_CHECK_ICMP==1: Check checksums in software for incoming ICMP packets.
+ */
+#ifndef CHECKSUM_CHECK_ICMP
+#define CHECKSUM_CHECK_ICMP 1
+#endif
+
+/**
+ * CHECKSUM_CHECK_ICMP6==1: Check checksums in software for incoming ICMPv6 packets
+ */
+#ifndef CHECKSUM_CHECK_ICMP6
+#define CHECKSUM_CHECK_ICMP6 1
+#endif
+
+/**
+ * LWIP_CHECKSUM_ON_COPY==1: Calculate checksum when copying data from
+ * application buffers to pbufs.
+ */
+#ifndef LWIP_CHECKSUM_ON_COPY
+#define LWIP_CHECKSUM_ON_COPY 0
+#endif
+
+/*
+ ---------------------------------------
+ ---------- IPv6 options ---------------
+ ---------------------------------------
+*/
+/**
+ * LWIP_IPV6==1: Enable IPv6
+ */
+#ifndef LWIP_IPV6
+#define LWIP_IPV6 0
+#endif
+
+/**
+ * LWIP_IPV6_NUM_ADDRESSES: Number of IPv6 addresses per netif.
+ */
+#ifndef LWIP_IPV6_NUM_ADDRESSES
+#define LWIP_IPV6_NUM_ADDRESSES 3
+#endif
+
+/**
+ * LWIP_IPV6_FORWARD==1: Forward IPv6 packets across netifs
+ */
+#ifndef LWIP_IPV6_FORWARD
+#define LWIP_IPV6_FORWARD 0
+#endif
+
+/**
+ * LWIP_ICMP6==1: Enable ICMPv6 (mandatory per RFC)
+ */
+#ifndef LWIP_ICMP6
+#define LWIP_ICMP6 (LWIP_IPV6)
+#endif
+
+/**
+ * LWIP_ICMP6_DATASIZE: bytes from original packet to send back in
+ * ICMPv6 error messages.
+ */
+#ifndef LWIP_ICMP6_DATASIZE
+#define LWIP_ICMP6_DATASIZE 8
+#endif
+
+/**
+ * LWIP_ICMP6_HL: default hop limit for ICMPv6 messages
+ */
+#ifndef LWIP_ICMP6_HL
+#define LWIP_ICMP6_HL 255
+#endif
+
+/**
+ * LWIP_IPV6_MLD==1: Enable multicast listener discovery protocol.
+ */
+#ifndef LWIP_IPV6_MLD
+#define LWIP_IPV6_MLD (LWIP_IPV6)
+#endif
+
+/**
+ * MEMP_NUM_MLD6_GROUP: Max number of IPv6 multicast that can be joined.
+ */
+#ifndef MEMP_NUM_MLD6_GROUP
+#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
+ */
+#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
+#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
+#define MEMP_NUM_ND6_QUEUE 20
+#endif
+
+/**
+ * LWIP_ND6_PROXY: Do proxy neighbor advertisements.
+ */
+#ifndef LWIP_ND6_PROXY
+#define LWIP_ND6_PROXY 0
+#endif
+
+/**
+ * LWIP_ND6_NUM_NEIGHBORS: Number of entries in IPv6 neighbor cache
+ */
+#ifndef LWIP_ND6_NUM_NEIGHBORS
+#define LWIP_ND6_NUM_NEIGHBORS 10
+#endif
+
+/**
+ * LWIP_ND6_NUM_DESTINATIONS: number of entries in IPv6 destination cache
+ */
+#ifndef LWIP_ND6_NUM_DESTINATIONS
+#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
+#define LWIP_ND6_NUM_PREFIXES 5
+#endif
+
+/**
+ * LWIP_ND6_NUM_ROUTERS: number of entries in IPv6 default router cache
+ */
+#ifndef LWIP_ND6_NUM_ROUTERS
+#define LWIP_ND6_NUM_ROUTERS 3
+#endif
+
+/**
+ * LWIP_ND6_MAX_MULTICAST_SOLICIT: max number of multicast solicit messages to send
+ * (neighbor solicit and router solicit)
+ */
+#ifndef LWIP_ND6_MAX_MULTICAST_SOLICIT
+#define LWIP_ND6_MAX_MULTICAST_SOLICIT 3
+#endif
+
+/**
+ * LWIP_ND6_MAX_UNICAST_SOLICIT: max number of unicast neighbor solicitation messages
+ * to send during neighbor reachability detection.
+ */
+#ifndef LWIP_ND6_MAX_UNICAST_SOLICIT
+#define LWIP_ND6_MAX_UNICAST_SOLICIT 3
+#endif
+
+/**
+ * Unused: See ND RFC (time in milliseconds).
+ */
+#ifndef LWIP_ND6_MAX_ANYCAST_DELAY_TIME
+#define LWIP_ND6_MAX_ANYCAST_DELAY_TIME 1000
+#endif
+
+/**
+ * Unused: See ND RFC
+ */
+#ifndef LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT
+#define LWIP_ND6_MAX_NEIGHBOR_ADVERTISEMENT 3
+#endif
+
+/**
+ * LWIP_ND6_REACHABLE_TIME: default neighbor reachable time (in milliseconds).
+ * May be updated by router advertisement messages.
+ */
+#ifndef LWIP_ND6_REACHABLE_TIME
+#define LWIP_ND6_REACHABLE_TIME 30000
+#endif
+
+/**
+ * LWIP_ND6_RETRANS_TIMER: default retransmission timer for solicitation messages
+ */
+#ifndef LWIP_ND6_RETRANS_TIMER
+#define LWIP_ND6_RETRANS_TIMER 1000
+#endif
+
+/**
+ * LWIP_ND6_DELAY_FIRST_PROBE_TIME: Delay before first unicast neighbor solicitation
+ * message is sent, during neighbor reachability detection.
+ */
+#ifndef LWIP_ND6_DELAY_FIRST_PROBE_TIME
+#define LWIP_ND6_DELAY_FIRST_PROBE_TIME 5000
+#endif
+
+/**
+ * LWIP_ND6_ALLOW_RA_UPDATES==1: Allow Router Advertisement messages to update
+ * Reachable time and retransmission timers, and netif MTU.
+ */
+#ifndef LWIP_ND6_ALLOW_RA_UPDATES
+#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
+#define LWIP_ND6_TCP_REACHABILITY_HINTS 1
+#endif
+
+/**
+ * LWIP_IPV6_AUTOCONFIG==1: Enable stateless address autoconfiguration as per RFC 4862.
+ */
+#ifndef LWIP_IPV6_AUTOCONFIG
+#define LWIP_IPV6_AUTOCONFIG (LWIP_IPV6)
+#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
+#define LWIP_IPV6_DHCP6 0
+#endif
+
+/*
+ ---------------------------------------
+ ---------- Hook options ---------------
+ ---------------------------------------
+*/
+
+/* Hooks are undefined by default, define them to a function if you need them. */
+
+/**
+ * LWIP_HOOK_IP4_INPUT(pbuf, input_netif):
+ * - called from ip_input() (IPv4)
+ * - pbuf: received struct pbuf passed to ip_input()
+ * - input_netif: struct netif on which the packet has been received
+ * Return values:
+ * - 0: Hook has not consumed the packet, packet is processed as normal
+ * - != 0: Hook has consumed the packet.
+ * If the hook consumed the packet, 'pbuf' is in the responsibility of the hook
+ * (i.e. free it when done).
+ */
+
+/**
+ * LWIP_HOOK_IP4_ROUTE(dest):
+ * - called from ip_route() (IPv4)
+ * - dest: destination IPv4 address
+ * Returns the destination netif or NULL if no destination netif is found. In
+ * that case, ip_route() continues as normal.
+ */
+
+/*
+ ---------------------------------------
+ ---------- Debugging options ----------
+ ---------------------------------------
+*/
+/**
+ * LWIP_DBG_MIN_LEVEL: After masking, the value of the debug is
+ * compared against this value. If it is smaller, then debugging
+ * messages are written.
+ */
+#ifndef LWIP_DBG_MIN_LEVEL
+#define LWIP_DBG_MIN_LEVEL LWIP_DBG_LEVEL_ALL
+#endif
+
+/**
+ * LWIP_DBG_TYPES_ON: A mask that can be used to globally enable/disable
+ * debug messages of certain types.
+ */
+#ifndef LWIP_DBG_TYPES_ON
+#define LWIP_DBG_TYPES_ON LWIP_DBG_ON
+#endif
+
+/**
+ * ETHARP_DEBUG: Enable debugging in etharp.c.
+ */
+#ifndef ETHARP_DEBUG
+#define ETHARP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * NETIF_DEBUG: Enable debugging in netif.c.
+ */
+#ifndef NETIF_DEBUG
+#define NETIF_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * PBUF_DEBUG: Enable debugging in pbuf.c.
+ */
+#ifndef PBUF_DEBUG
+#define PBUF_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * API_LIB_DEBUG: Enable debugging in api_lib.c.
+ */
+#ifndef API_LIB_DEBUG
+#define API_LIB_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * API_MSG_DEBUG: Enable debugging in api_msg.c.
+ */
+#ifndef API_MSG_DEBUG
+#define API_MSG_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * SOCKETS_DEBUG: Enable debugging in sockets.c.
+ */
+#ifndef SOCKETS_DEBUG
+#define SOCKETS_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * ICMP_DEBUG: Enable debugging in icmp.c.
+ */
+#ifndef ICMP_DEBUG
+#define ICMP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * IGMP_DEBUG: Enable debugging in igmp.c.
+ */
+#ifndef IGMP_DEBUG
+#define IGMP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * INET_DEBUG: Enable debugging in inet.c.
+ */
+#ifndef INET_DEBUG
+#define INET_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * IP_DEBUG: Enable debugging for IP.
+ */
+#ifndef IP_DEBUG
+#define IP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * IP_REASS_DEBUG: Enable debugging in ip_frag.c for both frag & reass.
+ */
+#ifndef IP_REASS_DEBUG
+#define IP_REASS_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * RAW_DEBUG: Enable debugging in raw.c.
+ */
+#ifndef RAW_DEBUG
+#define RAW_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * MEM_DEBUG: Enable debugging in mem.c.
+ */
+#ifndef MEM_DEBUG
+#define MEM_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * MEMP_DEBUG: Enable debugging in memp.c.
+ */
+#ifndef MEMP_DEBUG
+#define MEMP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * SYS_DEBUG: Enable debugging in sys.c.
+ */
+#ifndef SYS_DEBUG
+#define SYS_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TIMERS_DEBUG: Enable debugging in timers.c.
+ */
+#ifndef TIMERS_DEBUG
+#define TIMERS_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_DEBUG: Enable debugging for TCP.
+ */
+#ifndef TCP_DEBUG
+#define TCP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_INPUT_DEBUG: Enable debugging in tcp_in.c for incoming debug.
+ */
+#ifndef TCP_INPUT_DEBUG
+#define TCP_INPUT_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_FR_DEBUG: Enable debugging in tcp_in.c for fast retransmit.
+ */
+#ifndef TCP_FR_DEBUG
+#define TCP_FR_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_RTO_DEBUG: Enable debugging in TCP for retransmit
+ * timeout.
+ */
+#ifndef TCP_RTO_DEBUG
+#define TCP_RTO_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_CWND_DEBUG: Enable debugging for TCP congestion window.
+ */
+#ifndef TCP_CWND_DEBUG
+#define TCP_CWND_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_WND_DEBUG: Enable debugging in tcp_in.c for window updating.
+ */
+#ifndef TCP_WND_DEBUG
+#define TCP_WND_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_OUTPUT_DEBUG: Enable debugging in tcp_out.c output functions.
+ */
+#ifndef TCP_OUTPUT_DEBUG
+#define TCP_OUTPUT_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_RST_DEBUG: Enable debugging for TCP with the RST message.
+ */
+#ifndef TCP_RST_DEBUG
+#define TCP_RST_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCP_QLEN_DEBUG: Enable debugging for TCP queue lengths.
+ */
+#ifndef TCP_QLEN_DEBUG
+#define TCP_QLEN_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * UDP_DEBUG: Enable debugging in UDP.
+ */
+#ifndef UDP_DEBUG
+#define UDP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * TCPIP_DEBUG: Enable debugging in tcpip.c.
+ */
+#ifndef TCPIP_DEBUG
+#define TCPIP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * PPP_DEBUG: Enable debugging for PPP.
+ */
+#ifndef PPP_DEBUG
+#define PPP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * SLIP_DEBUG: Enable debugging in slipif.c.
+ */
+#ifndef SLIP_DEBUG
+#define SLIP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * DHCP_DEBUG: Enable debugging in dhcp.c.
+ */
+#ifndef DHCP_DEBUG
+#define DHCP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * AUTOIP_DEBUG: Enable debugging in autoip.c.
+ */
+#ifndef AUTOIP_DEBUG
+#define AUTOIP_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * SNMP_MSG_DEBUG: Enable debugging for SNMP messages.
+ */
+#ifndef SNMP_MSG_DEBUG
+#define SNMP_MSG_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * SNMP_MIB_DEBUG: Enable debugging for SNMP MIBs.
+ */
+#ifndef SNMP_MIB_DEBUG
+#define SNMP_MIB_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * DNS_DEBUG: Enable debugging for DNS.
+ */
+#ifndef DNS_DEBUG
+#define DNS_DEBUG LWIP_DBG_OFF
+#endif
+
+/**
+ * IP6_DEBUG: Enable debugging for IPv6.
+ */
+#ifndef IP6_DEBUG
+#define IP6_DEBUG LWIP_DBG_OFF
+#endif
+
+#endif /* __LWIP_OPT_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/pbuf.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/pbuf.h
new file mode 100644
index 00000000..4f8dca8a
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/pbuf.h
@@ -0,0 +1,185 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+#ifndef __LWIP_PBUF_H__
+#define __LWIP_PBUF_H__
+
+#include "lwip/opt.h"
+#include "lwip/err.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Currently, the pbuf_custom code is only needed for one specific configuration
+ * of IP_FRAG */
+#define LWIP_SUPPORT_CUSTOM_PBUF (IP_FRAG && !IP_FRAG_USES_STATIC_BUF && !LWIP_NETIF_TX_SINGLE_PBUF)
+
+/* @todo: We need a mechanism to prevent wasting memory in every pbuf
+ (TCP vs. UDP, IPv4 vs. IPv6: UDP/IPv4 packets may waste up to 28 bytes) */
+
+#define PBUF_TRANSPORT_HLEN 20
+#if LWIP_IPV6
+#define PBUF_IP_HLEN 40
+#else
+#define PBUF_IP_HLEN 20
+#endif
+
+typedef enum {
+ PBUF_TRANSPORT,
+ PBUF_IP,
+ PBUF_LINK,
+ PBUF_RAW
+} pbuf_layer;
+
+typedef enum {
+ PBUF_RAM, /* pbuf data is stored in RAM */
+ PBUF_ROM, /* pbuf data is stored in ROM */
+ PBUF_REF, /* pbuf comes from the pbuf pool */
+ PBUF_POOL /* pbuf payload refers to RAM */
+} pbuf_type;
+
+
+/** indicates this packet's data should be immediately passed to the application */
+#define PBUF_FLAG_PUSH 0x01U
+/** indicates this is a custom pbuf: pbuf_free and pbuf_header handle such a
+ a pbuf differently */
+#define PBUF_FLAG_IS_CUSTOM 0x02U
+/** indicates this pbuf is UDP multicast to be looped back */
+#define PBUF_FLAG_MCASTLOOP 0x04U
+/** indicates this pbuf was received as link-level broadcast */
+#define PBUF_FLAG_LLBCAST 0x08U
+/** indicates this pbuf was received as link-level multicast */
+#define PBUF_FLAG_LLMCAST 0x10U
+/** indicates this pbuf includes a TCP FIN flag */
+#define PBUF_FLAG_TCP_FIN 0x20U
+
+struct pbuf {
+ /** next pbuf in singly linked pbuf chain */
+ struct pbuf *next;
+
+ /** pointer to the actual data in the buffer */
+ void *payload;
+
+ /**
+ * total length of this buffer and all next buffers in chain
+ * belonging to the same packet.
+ *
+ * For non-queue packet chains this is the invariant:
+ * p->tot_len == p->len + (p->next? p->next->tot_len: 0)
+ */
+ u16_t tot_len;
+
+ /** length of this buffer */
+ u16_t len;
+
+ /** pbuf_type as u8_t instead of enum to save space */
+ u8_t /*pbuf_type*/ type;
+
+ /** misc flags */
+ u8_t flags;
+
+ /**
+ * the reference count always equals the number of pointers
+ * that refer to this pbuf. This can be pointers from an application,
+ * the stack itself, or pbuf->next pointers from a chain.
+ */
+ u16_t ref;
+};
+
+#if LWIP_SUPPORT_CUSTOM_PBUF
+/** Prototype for a function to free a custom pbuf */
+typedef void (*pbuf_free_custom_fn)(struct pbuf *p);
+
+/** A custom pbuf: like a pbuf, but following a function pointer to free it. */
+struct pbuf_custom {
+ /** The actual pbuf */
+ struct pbuf pbuf;
+ /** This function is called when pbuf_free deallocates this pbuf(_custom) */
+ pbuf_free_custom_fn custom_free_function;
+};
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+
+#if LWIP_TCP && TCP_QUEUE_OOSEQ
+/** Define this to 0 to prevent freeing ooseq pbufs when the PBUF_POOL is empty */
+#ifndef PBUF_POOL_FREE_OOSEQ
+#define PBUF_POOL_FREE_OOSEQ 1
+#endif /* PBUF_POOL_FREE_OOSEQ */
+#if NO_SYS && PBUF_POOL_FREE_OOSEQ
+extern volatile u8_t pbuf_free_ooseq_pending;
+void pbuf_free_ooseq(void);
+/** When not using sys_check_timeouts(), call PBUF_CHECK_FREE_OOSEQ()
+ at regular intervals from main level to check if ooseq pbufs need to be
+ freed! */
+#define PBUF_CHECK_FREE_OOSEQ() do { if(pbuf_free_ooseq_pending) { \
+ /* pbuf_alloc() reported PBUF_POOL to be empty -> try to free some \
+ ooseq queued pbufs now */ \
+ pbuf_free_ooseq(); }}while(0)
+#endif /* NO_SYS && PBUF_POOL_FREE_OOSEQ*/
+#endif /* LWIP_TCP && TCP_QUEUE_OOSEQ */
+
+/* Initializes the pbuf module. This call is empty for now, but may not be in future. */
+#define pbuf_init()
+
+struct pbuf *pbuf_alloc(pbuf_layer l, u16_t length, pbuf_type type);
+#if LWIP_SUPPORT_CUSTOM_PBUF
+struct pbuf *pbuf_alloced_custom(pbuf_layer l, u16_t length, pbuf_type type,
+ struct pbuf_custom *p, void *payload_mem,
+ u16_t payload_mem_len);
+#endif /* LWIP_SUPPORT_CUSTOM_PBUF */
+void pbuf_realloc(struct pbuf *p, u16_t size);
+u8_t pbuf_header(struct pbuf *p, s16_t header_size);
+void pbuf_ref(struct pbuf *p);
+u8_t pbuf_free(struct pbuf *p);
+u8_t pbuf_clen(struct pbuf *p);
+void pbuf_cat(struct pbuf *head, struct pbuf *tail);
+void pbuf_chain(struct pbuf *head, struct pbuf *tail);
+struct pbuf *pbuf_dechain(struct pbuf *p);
+err_t pbuf_copy(struct pbuf *p_to, struct pbuf *p_from);
+u16_t pbuf_copy_partial(struct pbuf *p, void *dataptr, u16_t len, u16_t offset);
+err_t pbuf_take(struct pbuf *buf, const void *dataptr, u16_t len);
+struct pbuf *pbuf_coalesce(struct pbuf *p, pbuf_layer layer);
+#if LWIP_CHECKSUM_ON_COPY
+err_t pbuf_fill_chksum(struct pbuf *p, u16_t start_offset, const void *dataptr,
+ u16_t len, u16_t *chksum);
+#endif /* LWIP_CHECKSUM_ON_COPY */
+
+u8_t pbuf_get_at(struct pbuf* p, u16_t offset);
+u16_t pbuf_memcmp(struct pbuf* p, u16_t offset, const void* s2, u16_t n);
+u16_t pbuf_memfind(struct pbuf* p, const void* mem, u16_t mem_len, u16_t start_offset);
+u16_t pbuf_strstr(struct pbuf* p, const char* substr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_PBUF_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/raw.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/raw.h
new file mode 100644
index 00000000..f0c8ed47
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/raw.h
@@ -0,0 +1,131 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_RAW_H__
+#define __LWIP_RAW_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_RAW /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/def.h"
+#include "lwip/ip.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct raw_pcb;
+
+/** Function prototype for raw pcb receive callback functions.
+ * @param arg user supplied argument (raw_pcb.recv_arg)
+ * @param pcb the raw_pcb which received data
+ * @param p the packet buffer that was received
+ * @param addr the remote IP address from which the packet was received
+ * @return 1 if the packet was 'eaten' (aka. deleted),
+ * 0 if the packet lives on
+ * If returning 1, the callback is responsible for freeing the pbuf
+ * if it's not used any more.
+ */
+typedef u8_t (*raw_recv_fn)(void *arg, struct raw_pcb *pcb, struct pbuf *p,
+ 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 */
+
+struct raw_pcb {
+ /* Common members of all PCB types */
+ IP_PCB;
+
+ struct raw_pcb *next;
+
+ u8_t protocol;
+
+ /** receive callback function */
+ union {
+ raw_recv_fn ip4;
+ RAW_PCB_RECV_IP6
+ } recv;
+ /* user-supplied argument for the recv callback */
+ void *recv_arg;
+};
+
+/* The following functions is the application layer interface to the
+ RAW code. */
+struct raw_pcb * raw_new (u8_t proto);
+void raw_remove (struct raw_pcb *pcb);
+err_t raw_bind (struct raw_pcb *pcb, ip_addr_t *ipaddr);
+err_t raw_connect (struct raw_pcb *pcb, ip_addr_t *ipaddr);
+
+void raw_recv (struct raw_pcb *pcb, raw_recv_fn recv, void *recv_arg);
+err_t raw_sendto (struct raw_pcb *pcb, struct pbuf *p, ip_addr_t *ipaddr);
+err_t raw_send (struct raw_pcb *pcb, struct pbuf *p);
+
+#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 */
+
+/* The following functions are the lower layer interface to RAW. */
+u8_t raw_input (struct pbuf *p, struct netif *inp);
+#define raw_init() /* Compatibility define, not init needed. */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_RAW */
+
+#endif /* __LWIP_RAW_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/sio.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/sio.h
new file mode 100644
index 00000000..28ae2f22
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/sio.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ */
+
+/*
+ * This is the interface to the platform specific serial IO module
+ * It needs to be implemented by those platforms which need SLIP or PPP
+ */
+
+#ifndef __SIO_H__
+#define __SIO_H__
+
+#include "lwip/arch.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* If you want to define sio_fd_t elsewhere or differently,
+ define this in your cc.h file. */
+#ifndef __sio_fd_t_defined
+typedef void * sio_fd_t;
+#endif
+
+/* The following functions can be defined to something else in your cc.h file
+ or be implemented in your custom sio.c file. */
+
+#ifndef sio_open
+/**
+ * Opens a serial device for communication.
+ *
+ * @param devnum device number
+ * @return handle to serial device if successful, NULL otherwise
+ */
+sio_fd_t sio_open(u8_t devnum);
+#endif
+
+#ifndef sio_send
+/**
+ * Sends a single character to the serial device.
+ *
+ * @param c character to send
+ * @param fd serial device handle
+ *
+ * @note This function will block until the character can be sent.
+ */
+void sio_send(u8_t c, sio_fd_t fd);
+#endif
+
+#ifndef sio_recv
+/**
+ * Receives a single character from the serial device.
+ *
+ * @param fd serial device handle
+ *
+ * @note This function will block until a character is received.
+ */
+u8_t sio_recv(sio_fd_t fd);
+#endif
+
+#ifndef sio_read
+/**
+ * Reads from the serial device.
+ *
+ * @param fd serial device handle
+ * @param data pointer to data buffer for receiving
+ * @param len maximum length (in bytes) of data to receive
+ * @return number of bytes actually received - may be 0 if aborted by sio_read_abort
+ *
+ * @note This function will block until data can be received. The blocking
+ * can be cancelled by calling sio_read_abort().
+ */
+u32_t sio_read(sio_fd_t fd, u8_t *data, u32_t len);
+#endif
+
+#ifndef sio_tryread
+/**
+ * Tries to read from the serial device. Same as sio_read but returns
+ * immediately if no data is available and never blocks.
+ *
+ * @param fd serial device handle
+ * @param data pointer to data buffer for receiving
+ * @param len maximum length (in bytes) of data to receive
+ * @return number of bytes actually received
+ */
+u32_t sio_tryread(sio_fd_t fd, u8_t *data, u32_t len);
+#endif
+
+#ifndef sio_write
+/**
+ * Writes to the serial device.
+ *
+ * @param fd serial device handle
+ * @param data pointer to data to send
+ * @param len length (in bytes) of data to send
+ * @return number of bytes actually sent
+ *
+ * @note This function will block until all data can be sent.
+ */
+u32_t sio_write(sio_fd_t fd, u8_t *data, u32_t len);
+#endif
+
+#ifndef sio_read_abort
+/**
+ * Aborts a blocking sio_read() call.
+ *
+ * @param fd serial device handle
+ */
+void sio_read_abort(sio_fd_t fd);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __SIO_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/snmp.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/snmp.h
new file mode 100644
index 00000000..7844cc11
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/snmp.h
@@ -0,0 +1,367 @@
+/*
+ * Copyright (c) 2001, 2002 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2001, 2002 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Leon Woestenberg <leon.woestenberg@axon.tv>
+ *
+ */
+#ifndef __LWIP_SNMP_H__
+#define __LWIP_SNMP_H__
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "lwip/ip_addr.h"
+
+struct udp_pcb;
+struct netif;
+
+/**
+ * @see RFC1213, "MIB-II, 6. Definitions"
+ */
+enum snmp_ifType {
+ snmp_ifType_other=1, /* none of the following */
+ snmp_ifType_regular1822,
+ snmp_ifType_hdh1822,
+ snmp_ifType_ddn_x25,
+ snmp_ifType_rfc877_x25,
+ snmp_ifType_ethernet_csmacd,
+ snmp_ifType_iso88023_csmacd,
+ snmp_ifType_iso88024_tokenBus,
+ snmp_ifType_iso88025_tokenRing,
+ snmp_ifType_iso88026_man,
+ snmp_ifType_starLan,
+ snmp_ifType_proteon_10Mbit,
+ snmp_ifType_proteon_80Mbit,
+ snmp_ifType_hyperchannel,
+ snmp_ifType_fddi,
+ snmp_ifType_lapb,
+ snmp_ifType_sdlc,
+ snmp_ifType_ds1, /* T-1 */
+ snmp_ifType_e1, /* european equiv. of T-1 */
+ snmp_ifType_basicISDN,
+ snmp_ifType_primaryISDN, /* proprietary serial */
+ snmp_ifType_propPointToPointSerial,
+ snmp_ifType_ppp,
+ snmp_ifType_softwareLoopback,
+ snmp_ifType_eon, /* CLNP over IP [11] */
+ snmp_ifType_ethernet_3Mbit,
+ snmp_ifType_nsip, /* XNS over IP */
+ snmp_ifType_slip, /* generic SLIP */
+ snmp_ifType_ultra, /* ULTRA technologies */
+ snmp_ifType_ds3, /* T-3 */
+ snmp_ifType_sip, /* SMDS */
+ snmp_ifType_frame_relay
+};
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+/** SNMP "sysuptime" Interval */
+#define SNMP_SYSUPTIME_INTERVAL 10
+
+/** fixed maximum length for object identifier type */
+#define LWIP_SNMP_OBJ_ID_LEN 32
+
+/** internal object identifier representation */
+struct snmp_obj_id
+{
+ u8_t len;
+ s32_t id[LWIP_SNMP_OBJ_ID_LEN];
+};
+
+/* system */
+void snmp_set_sysdescr(const u8_t* str, const u8_t* len);
+void snmp_set_sysobjid(struct snmp_obj_id *oid);
+void snmp_get_sysobjid_ptr(struct snmp_obj_id **oid);
+void snmp_inc_sysuptime(void);
+void snmp_add_sysuptime(u32_t value);
+void snmp_get_sysuptime(u32_t *value);
+void snmp_set_syscontact(u8_t *ocstr, u8_t *ocstrlen);
+void snmp_set_sysname(u8_t *ocstr, u8_t *ocstrlen);
+void snmp_set_syslocation(u8_t *ocstr, u8_t *ocstrlen);
+
+/* network interface */
+void snmp_add_ifinoctets(struct netif *ni, u32_t value);
+void snmp_inc_ifinucastpkts(struct netif *ni);
+void snmp_inc_ifinnucastpkts(struct netif *ni);
+void snmp_inc_ifindiscards(struct netif *ni);
+void snmp_add_ifoutoctets(struct netif *ni, u32_t value);
+void snmp_inc_ifoutucastpkts(struct netif *ni);
+void snmp_inc_ifoutnucastpkts(struct netif *ni);
+void snmp_inc_ifoutdiscards(struct netif *ni);
+void snmp_inc_iflist(void);
+void snmp_dec_iflist(void);
+
+/* ARP (for atTable and ipNetToMediaTable) */
+void snmp_insert_arpidx_tree(struct netif *ni, ip_addr_t *ip);
+void snmp_delete_arpidx_tree(struct netif *ni, ip_addr_t *ip);
+
+/* IP */
+void snmp_inc_ipinreceives(void);
+void snmp_inc_ipinhdrerrors(void);
+void snmp_inc_ipinaddrerrors(void);
+void snmp_inc_ipforwdatagrams(void);
+void snmp_inc_ipinunknownprotos(void);
+void snmp_inc_ipindiscards(void);
+void snmp_inc_ipindelivers(void);
+void snmp_inc_ipoutrequests(void);
+void snmp_inc_ipoutdiscards(void);
+void snmp_inc_ipoutnoroutes(void);
+void snmp_inc_ipreasmreqds(void);
+void snmp_inc_ipreasmoks(void);
+void snmp_inc_ipreasmfails(void);
+void snmp_inc_ipfragoks(void);
+void snmp_inc_ipfragfails(void);
+void snmp_inc_ipfragcreates(void);
+void snmp_inc_iproutingdiscards(void);
+void snmp_insert_ipaddridx_tree(struct netif *ni);
+void snmp_delete_ipaddridx_tree(struct netif *ni);
+void snmp_insert_iprteidx_tree(u8_t dflt, struct netif *ni);
+void snmp_delete_iprteidx_tree(u8_t dflt, struct netif *ni);
+
+/* ICMP */
+void snmp_inc_icmpinmsgs(void);
+void snmp_inc_icmpinerrors(void);
+void snmp_inc_icmpindestunreachs(void);
+void snmp_inc_icmpintimeexcds(void);
+void snmp_inc_icmpinparmprobs(void);
+void snmp_inc_icmpinsrcquenchs(void);
+void snmp_inc_icmpinredirects(void);
+void snmp_inc_icmpinechos(void);
+void snmp_inc_icmpinechoreps(void);
+void snmp_inc_icmpintimestamps(void);
+void snmp_inc_icmpintimestampreps(void);
+void snmp_inc_icmpinaddrmasks(void);
+void snmp_inc_icmpinaddrmaskreps(void);
+void snmp_inc_icmpoutmsgs(void);
+void snmp_inc_icmpouterrors(void);
+void snmp_inc_icmpoutdestunreachs(void);
+void snmp_inc_icmpouttimeexcds(void);
+void snmp_inc_icmpoutparmprobs(void);
+void snmp_inc_icmpoutsrcquenchs(void);
+void snmp_inc_icmpoutredirects(void);
+void snmp_inc_icmpoutechos(void);
+void snmp_inc_icmpoutechoreps(void);
+void snmp_inc_icmpouttimestamps(void);
+void snmp_inc_icmpouttimestampreps(void);
+void snmp_inc_icmpoutaddrmasks(void);
+void snmp_inc_icmpoutaddrmaskreps(void);
+
+/* TCP */
+void snmp_inc_tcpactiveopens(void);
+void snmp_inc_tcppassiveopens(void);
+void snmp_inc_tcpattemptfails(void);
+void snmp_inc_tcpestabresets(void);
+void snmp_inc_tcpinsegs(void);
+void snmp_inc_tcpoutsegs(void);
+void snmp_inc_tcpretranssegs(void);
+void snmp_inc_tcpinerrs(void);
+void snmp_inc_tcpoutrsts(void);
+
+/* UDP */
+void snmp_inc_udpindatagrams(void);
+void snmp_inc_udpnoports(void);
+void snmp_inc_udpinerrors(void);
+void snmp_inc_udpoutdatagrams(void);
+void snmp_insert_udpidx_tree(struct udp_pcb *pcb);
+void snmp_delete_udpidx_tree(struct udp_pcb *pcb);
+
+/* SNMP */
+void snmp_inc_snmpinpkts(void);
+void snmp_inc_snmpoutpkts(void);
+void snmp_inc_snmpinbadversions(void);
+void snmp_inc_snmpinbadcommunitynames(void);
+void snmp_inc_snmpinbadcommunityuses(void);
+void snmp_inc_snmpinasnparseerrs(void);
+void snmp_inc_snmpintoobigs(void);
+void snmp_inc_snmpinnosuchnames(void);
+void snmp_inc_snmpinbadvalues(void);
+void snmp_inc_snmpinreadonlys(void);
+void snmp_inc_snmpingenerrs(void);
+void snmp_add_snmpintotalreqvars(u8_t value);
+void snmp_add_snmpintotalsetvars(u8_t value);
+void snmp_inc_snmpingetrequests(void);
+void snmp_inc_snmpingetnexts(void);
+void snmp_inc_snmpinsetrequests(void);
+void snmp_inc_snmpingetresponses(void);
+void snmp_inc_snmpintraps(void);
+void snmp_inc_snmpouttoobigs(void);
+void snmp_inc_snmpoutnosuchnames(void);
+void snmp_inc_snmpoutbadvalues(void);
+void snmp_inc_snmpoutgenerrs(void);
+void snmp_inc_snmpoutgetrequests(void);
+void snmp_inc_snmpoutgetnexts(void);
+void snmp_inc_snmpoutsetrequests(void);
+void snmp_inc_snmpoutgetresponses(void);
+void snmp_inc_snmpouttraps(void);
+void snmp_get_snmpgrpid_ptr(struct snmp_obj_id **oid);
+void snmp_set_snmpenableauthentraps(u8_t *value);
+void snmp_get_snmpenableauthentraps(u8_t *value);
+
+/* LWIP_SNMP support not available */
+/* define everything to be empty */
+#else
+
+/* system */
+#define snmp_set_sysdescr(str, len)
+#define snmp_set_sysobjid(oid);
+#define snmp_get_sysobjid_ptr(oid)
+#define snmp_inc_sysuptime()
+#define snmp_add_sysuptime(value)
+#define snmp_get_sysuptime(value)
+#define snmp_set_syscontact(ocstr, ocstrlen);
+#define snmp_set_sysname(ocstr, ocstrlen);
+#define snmp_set_syslocation(ocstr, ocstrlen);
+
+/* network interface */
+#define snmp_add_ifinoctets(ni,value)
+#define snmp_inc_ifinucastpkts(ni)
+#define snmp_inc_ifinnucastpkts(ni)
+#define snmp_inc_ifindiscards(ni)
+#define snmp_add_ifoutoctets(ni,value)
+#define snmp_inc_ifoutucastpkts(ni)
+#define snmp_inc_ifoutnucastpkts(ni)
+#define snmp_inc_ifoutdiscards(ni)
+#define snmp_inc_iflist()
+#define snmp_dec_iflist()
+
+/* ARP */
+#define snmp_insert_arpidx_tree(ni,ip)
+#define snmp_delete_arpidx_tree(ni,ip)
+
+/* IP */
+#define snmp_inc_ipinreceives()
+#define snmp_inc_ipinhdrerrors()
+#define snmp_inc_ipinaddrerrors()
+#define snmp_inc_ipforwdatagrams()
+#define snmp_inc_ipinunknownprotos()
+#define snmp_inc_ipindiscards()
+#define snmp_inc_ipindelivers()
+#define snmp_inc_ipoutrequests()
+#define snmp_inc_ipoutdiscards()
+#define snmp_inc_ipoutnoroutes()
+#define snmp_inc_ipreasmreqds()
+#define snmp_inc_ipreasmoks()
+#define snmp_inc_ipreasmfails()
+#define snmp_inc_ipfragoks()
+#define snmp_inc_ipfragfails()
+#define snmp_inc_ipfragcreates()
+#define snmp_inc_iproutingdiscards()
+#define snmp_insert_ipaddridx_tree(ni)
+#define snmp_delete_ipaddridx_tree(ni)
+#define snmp_insert_iprteidx_tree(dflt, ni)
+#define snmp_delete_iprteidx_tree(dflt, ni)
+
+/* ICMP */
+#define snmp_inc_icmpinmsgs()
+#define snmp_inc_icmpinerrors()
+#define snmp_inc_icmpindestunreachs()
+#define snmp_inc_icmpintimeexcds()
+#define snmp_inc_icmpinparmprobs()
+#define snmp_inc_icmpinsrcquenchs()
+#define snmp_inc_icmpinredirects()
+#define snmp_inc_icmpinechos()
+#define snmp_inc_icmpinechoreps()
+#define snmp_inc_icmpintimestamps()
+#define snmp_inc_icmpintimestampreps()
+#define snmp_inc_icmpinaddrmasks()
+#define snmp_inc_icmpinaddrmaskreps()
+#define snmp_inc_icmpoutmsgs()
+#define snmp_inc_icmpouterrors()
+#define snmp_inc_icmpoutdestunreachs()
+#define snmp_inc_icmpouttimeexcds()
+#define snmp_inc_icmpoutparmprobs()
+#define snmp_inc_icmpoutsrcquenchs()
+#define snmp_inc_icmpoutredirects()
+#define snmp_inc_icmpoutechos()
+#define snmp_inc_icmpoutechoreps()
+#define snmp_inc_icmpouttimestamps()
+#define snmp_inc_icmpouttimestampreps()
+#define snmp_inc_icmpoutaddrmasks()
+#define snmp_inc_icmpoutaddrmaskreps()
+/* TCP */
+#define snmp_inc_tcpactiveopens()
+#define snmp_inc_tcppassiveopens()
+#define snmp_inc_tcpattemptfails()
+#define snmp_inc_tcpestabresets()
+#define snmp_inc_tcpinsegs()
+#define snmp_inc_tcpoutsegs()
+#define snmp_inc_tcpretranssegs()
+#define snmp_inc_tcpinerrs()
+#define snmp_inc_tcpoutrsts()
+
+/* UDP */
+#define snmp_inc_udpindatagrams()
+#define snmp_inc_udpnoports()
+#define snmp_inc_udpinerrors()
+#define snmp_inc_udpoutdatagrams()
+#define snmp_insert_udpidx_tree(pcb)
+#define snmp_delete_udpidx_tree(pcb)
+
+/* SNMP */
+#define snmp_inc_snmpinpkts()
+#define snmp_inc_snmpoutpkts()
+#define snmp_inc_snmpinbadversions()
+#define snmp_inc_snmpinbadcommunitynames()
+#define snmp_inc_snmpinbadcommunityuses()
+#define snmp_inc_snmpinasnparseerrs()
+#define snmp_inc_snmpintoobigs()
+#define snmp_inc_snmpinnosuchnames()
+#define snmp_inc_snmpinbadvalues()
+#define snmp_inc_snmpinreadonlys()
+#define snmp_inc_snmpingenerrs()
+#define snmp_add_snmpintotalreqvars(value)
+#define snmp_add_snmpintotalsetvars(value)
+#define snmp_inc_snmpingetrequests()
+#define snmp_inc_snmpingetnexts()
+#define snmp_inc_snmpinsetrequests()
+#define snmp_inc_snmpingetresponses()
+#define snmp_inc_snmpintraps()
+#define snmp_inc_snmpouttoobigs()
+#define snmp_inc_snmpoutnosuchnames()
+#define snmp_inc_snmpoutbadvalues()
+#define snmp_inc_snmpoutgenerrs()
+#define snmp_inc_snmpoutgetrequests()
+#define snmp_inc_snmpoutgetnexts()
+#define snmp_inc_snmpoutsetrequests()
+#define snmp_inc_snmpoutgetresponses()
+#define snmp_inc_snmpouttraps()
+#define snmp_get_snmpgrpid_ptr(oid)
+#define snmp_set_snmpenableauthentraps(value)
+#define snmp_get_snmpenableauthentraps(value)
+
+#endif /* LWIP_SNMP */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_SNMP_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/snmp_asn1.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/snmp_asn1.h
new file mode 100644
index 00000000..605fa3f1
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/snmp_asn1.h
@@ -0,0 +1,101 @@
+/**
+ * @file
+ * Abstract Syntax Notation One (ISO 8824, 8825) codec.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#ifndef __LWIP_SNMP_ASN1_H__
+#define __LWIP_SNMP_ASN1_H__
+
+#include "lwip/opt.h"
+#include "lwip/err.h"
+#include "lwip/pbuf.h"
+#include "lwip/snmp.h"
+
+#if LWIP_SNMP
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define SNMP_ASN1_UNIV (0) /* (!0x80 | !0x40) */
+#define SNMP_ASN1_APPLIC (0x40) /* (!0x80 | 0x40) */
+#define SNMP_ASN1_CONTXT (0x80) /* ( 0x80 | !0x40) */
+
+#define SNMP_ASN1_CONSTR (0x20) /* ( 0x20) */
+#define SNMP_ASN1_PRIMIT (0) /* (!0x20) */
+
+/* universal tags */
+#define SNMP_ASN1_INTEG 2
+#define SNMP_ASN1_OC_STR 4
+#define SNMP_ASN1_NUL 5
+#define SNMP_ASN1_OBJ_ID 6
+#define SNMP_ASN1_SEQ 16
+
+/* application specific (SNMP) tags */
+#define SNMP_ASN1_IPADDR 0 /* octet string size(4) */
+#define SNMP_ASN1_COUNTER 1 /* u32_t */
+#define SNMP_ASN1_GAUGE 2 /* u32_t */
+#define SNMP_ASN1_TIMETICKS 3 /* u32_t */
+#define SNMP_ASN1_OPAQUE 4 /* octet string */
+
+/* context specific (SNMP) tags */
+#define SNMP_ASN1_PDU_GET_REQ 0
+#define SNMP_ASN1_PDU_GET_NEXT_REQ 1
+#define SNMP_ASN1_PDU_GET_RESP 2
+#define SNMP_ASN1_PDU_SET_REQ 3
+#define SNMP_ASN1_PDU_TRAP 4
+
+err_t snmp_asn1_dec_type(struct pbuf *p, u16_t ofs, u8_t *type);
+err_t snmp_asn1_dec_length(struct pbuf *p, u16_t ofs, u8_t *octets_used, u16_t *length);
+err_t snmp_asn1_dec_u32t(struct pbuf *p, u16_t ofs, u16_t len, u32_t *value);
+err_t snmp_asn1_dec_s32t(struct pbuf *p, u16_t ofs, u16_t len, s32_t *value);
+err_t snmp_asn1_dec_oid(struct pbuf *p, u16_t ofs, u16_t len, struct snmp_obj_id *oid);
+err_t snmp_asn1_dec_raw(struct pbuf *p, u16_t ofs, u16_t len, u16_t raw_len, u8_t *raw);
+
+void snmp_asn1_enc_length_cnt(u16_t length, u8_t *octets_needed);
+void snmp_asn1_enc_u32t_cnt(u32_t value, u16_t *octets_needed);
+void snmp_asn1_enc_s32t_cnt(s32_t value, u16_t *octets_needed);
+void snmp_asn1_enc_oid_cnt(u8_t ident_len, s32_t *ident, u16_t *octets_needed);
+err_t snmp_asn1_enc_type(struct pbuf *p, u16_t ofs, u8_t type);
+err_t snmp_asn1_enc_length(struct pbuf *p, u16_t ofs, u16_t length);
+err_t snmp_asn1_enc_u32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, u32_t value);
+err_t snmp_asn1_enc_s32t(struct pbuf *p, u16_t ofs, u16_t octets_needed, s32_t value);
+err_t snmp_asn1_enc_oid(struct pbuf *p, u16_t ofs, u8_t ident_len, s32_t *ident);
+err_t snmp_asn1_enc_raw(struct pbuf *p, u16_t ofs, u16_t raw_len, u8_t *raw);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* __LWIP_SNMP_ASN1_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/snmp_msg.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/snmp_msg.h
new file mode 100644
index 00000000..1183e3a9
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/snmp_msg.h
@@ -0,0 +1,315 @@
+/**
+ * @file
+ * SNMP Agent message handling structures.
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#ifndef __LWIP_SNMP_MSG_H__
+#define __LWIP_SNMP_MSG_H__
+
+#include "lwip/opt.h"
+#include "lwip/snmp.h"
+#include "lwip/snmp_structs.h"
+#include "lwip/ip_addr.h"
+#include "lwip/err.h"
+
+#if LWIP_SNMP
+
+#if SNMP_PRIVATE_MIB
+/* When using a private MIB, you have to create a file 'private_mib.h' that contains
+ * a 'struct mib_array_node mib_private' which contains your MIB. */
+#include "private_mib.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* The listen port of the SNMP agent. Clients have to make their requests to
+ this port. Most standard clients won't work if you change this! */
+#ifndef SNMP_IN_PORT
+#define SNMP_IN_PORT 161
+#endif
+/* The remote port the SNMP agent sends traps to. Most standard trap sinks won't
+ work if you change this! */
+#ifndef SNMP_TRAP_PORT
+#define SNMP_TRAP_PORT 162
+#endif
+
+#define SNMP_ES_NOERROR 0
+#define SNMP_ES_TOOBIG 1
+#define SNMP_ES_NOSUCHNAME 2
+#define SNMP_ES_BADVALUE 3
+#define SNMP_ES_READONLY 4
+#define SNMP_ES_GENERROR 5
+
+#define SNMP_GENTRAP_COLDSTART 0
+#define SNMP_GENTRAP_WARMSTART 1
+#define SNMP_GENTRAP_AUTHFAIL 4
+#define SNMP_GENTRAP_ENTERPRISESPC 6
+
+struct snmp_varbind
+{
+ /* next pointer, NULL for last in list */
+ struct snmp_varbind *next;
+ /* previous pointer, NULL for first in list */
+ struct snmp_varbind *prev;
+
+ /* object identifier length (in s32_t) */
+ u8_t ident_len;
+ /* object identifier array */
+ s32_t *ident;
+
+ /* object value ASN1 type */
+ u8_t value_type;
+ /* object value length (in u8_t) */
+ u8_t value_len;
+ /* object value */
+ void *value;
+
+ /* encoding varbind seq length length */
+ u8_t seqlenlen;
+ /* encoding object identifier length length */
+ u8_t olenlen;
+ /* encoding object value length length */
+ u8_t vlenlen;
+ /* encoding varbind seq length */
+ u16_t seqlen;
+ /* encoding object identifier length */
+ u16_t olen;
+ /* encoding object value length */
+ u16_t vlen;
+};
+
+struct snmp_varbind_root
+{
+ struct snmp_varbind *head;
+ struct snmp_varbind *tail;
+ /* number of variable bindings in list */
+ u8_t count;
+ /* encoding varbind-list seq length length */
+ u8_t seqlenlen;
+ /* encoding varbind-list seq length */
+ u16_t seqlen;
+};
+
+/** output response message header length fields */
+struct snmp_resp_header_lengths
+{
+ /* encoding error-index length length */
+ u8_t erridxlenlen;
+ /* encoding error-status length length */
+ u8_t errstatlenlen;
+ /* encoding request id length length */
+ u8_t ridlenlen;
+ /* encoding pdu length length */
+ u8_t pdulenlen;
+ /* encoding community length length */
+ u8_t comlenlen;
+ /* encoding version length length */
+ u8_t verlenlen;
+ /* encoding sequence length length */
+ u8_t seqlenlen;
+
+ /* encoding error-index length */
+ u16_t erridxlen;
+ /* encoding error-status length */
+ u16_t errstatlen;
+ /* encoding request id length */
+ u16_t ridlen;
+ /* encoding pdu length */
+ u16_t pdulen;
+ /* encoding community length */
+ u16_t comlen;
+ /* encoding version length */
+ u16_t verlen;
+ /* encoding sequence length */
+ u16_t seqlen;
+};
+
+/** output response message header length fields */
+struct snmp_trap_header_lengths
+{
+ /* encoding timestamp length length */
+ u8_t tslenlen;
+ /* encoding specific-trap length length */
+ u8_t strplenlen;
+ /* encoding generic-trap length length */
+ u8_t gtrplenlen;
+ /* encoding agent-addr length length */
+ u8_t aaddrlenlen;
+ /* encoding enterprise-id length length */
+ u8_t eidlenlen;
+ /* encoding pdu length length */
+ u8_t pdulenlen;
+ /* encoding community length length */
+ u8_t comlenlen;
+ /* encoding version length length */
+ u8_t verlenlen;
+ /* encoding sequence length length */
+ u8_t seqlenlen;
+
+ /* encoding timestamp length */
+ u16_t tslen;
+ /* encoding specific-trap length */
+ u16_t strplen;
+ /* encoding generic-trap length */
+ u16_t gtrplen;
+ /* encoding agent-addr length */
+ u16_t aaddrlen;
+ /* encoding enterprise-id length */
+ u16_t eidlen;
+ /* encoding pdu length */
+ u16_t pdulen;
+ /* encoding community length */
+ u16_t comlen;
+ /* encoding version length */
+ u16_t verlen;
+ /* encoding sequence length */
+ u16_t seqlen;
+};
+
+/* Accepting new SNMP messages. */
+#define SNMP_MSG_EMPTY 0
+/* Search for matching object for variable binding. */
+#define SNMP_MSG_SEARCH_OBJ 1
+/* Perform SNMP operation on in-memory object.
+ Pass-through states, for symmetry only. */
+#define SNMP_MSG_INTERNAL_GET_OBJDEF 2
+#define SNMP_MSG_INTERNAL_GET_VALUE 3
+#define SNMP_MSG_INTERNAL_SET_TEST 4
+#define SNMP_MSG_INTERNAL_GET_OBJDEF_S 5
+#define SNMP_MSG_INTERNAL_SET_VALUE 6
+/* Perform SNMP operation on object located externally.
+ In theory this could be used for building a proxy agent.
+ Practical use is for an enterprise spc. app. gateway. */
+#define SNMP_MSG_EXTERNAL_GET_OBJDEF 7
+#define SNMP_MSG_EXTERNAL_GET_VALUE 8
+#define SNMP_MSG_EXTERNAL_SET_TEST 9
+#define SNMP_MSG_EXTERNAL_GET_OBJDEF_S 10
+#define SNMP_MSG_EXTERNAL_SET_VALUE 11
+
+#define SNMP_COMMUNITY_STR_LEN 64
+struct snmp_msg_pstat
+{
+ /* lwIP local port (161) binding */
+ struct udp_pcb *pcb;
+ /* source IP address */
+ ip_addr_t sip;
+ /* source UDP port */
+ u16_t sp;
+ /* request type */
+ u8_t rt;
+ /* request ID */
+ s32_t rid;
+ /* error status */
+ s32_t error_status;
+ /* error index */
+ s32_t error_index;
+ /* community name (zero terminated) */
+ u8_t community[SNMP_COMMUNITY_STR_LEN + 1];
+ /* community string length (exclusive zero term) */
+ u8_t com_strlen;
+ /* one out of MSG_EMPTY, MSG_DEMUX, MSG_INTERNAL, MSG_EXTERNAL_x */
+ u8_t state;
+ /* saved arguments for MSG_EXTERNAL_x */
+ struct mib_external_node *ext_mib_node;
+ struct snmp_name_ptr ext_name_ptr;
+ struct obj_def ext_object_def;
+ struct snmp_obj_id ext_oid;
+ /* index into input variable binding list */
+ u8_t vb_idx;
+ /* ptr into input variable binding list */
+ struct snmp_varbind *vb_ptr;
+ /* list of variable bindings from input */
+ struct snmp_varbind_root invb;
+ /* list of variable bindings to output */
+ struct snmp_varbind_root outvb;
+ /* output response lengths used in ASN encoding */
+ struct snmp_resp_header_lengths rhl;
+};
+
+struct snmp_msg_trap
+{
+ /* lwIP local port (161) binding */
+ struct udp_pcb *pcb;
+ /* destination IP address in network order */
+ ip_addr_t dip;
+
+ /* source enterprise ID (sysObjectID) */
+ struct snmp_obj_id *enterprise;
+ /* source IP address, raw network order format */
+ u8_t sip_raw[4];
+ /* generic trap code */
+ u32_t gen_trap;
+ /* specific trap code */
+ u32_t spc_trap;
+ /* timestamp */
+ u32_t ts;
+ /* list of variable bindings to output */
+ struct snmp_varbind_root outvb;
+ /* output trap lengths used in ASN encoding */
+ struct snmp_trap_header_lengths thl;
+};
+
+/** Agent Version constant, 0 = v1 oddity */
+extern const s32_t snmp_version;
+/** Agent default "public" community string */
+extern const char snmp_publiccommunity[7];
+
+extern struct snmp_msg_trap trap_msg;
+
+/** Agent setup, start listening to port 161. */
+void snmp_init(void);
+void snmp_trap_dst_enable(u8_t dst_idx, u8_t enable);
+void snmp_trap_dst_ip_set(u8_t dst_idx, ip_addr_t *dst);
+
+/** Varbind-list functions. */
+struct snmp_varbind* snmp_varbind_alloc(struct snmp_obj_id *oid, u8_t type, u8_t len);
+void snmp_varbind_free(struct snmp_varbind *vb);
+void snmp_varbind_list_free(struct snmp_varbind_root *root);
+void snmp_varbind_tail_add(struct snmp_varbind_root *root, struct snmp_varbind *vb);
+struct snmp_varbind* snmp_varbind_tail_remove(struct snmp_varbind_root *root);
+
+/** Handle an internal (recv) or external (private response) event. */
+void snmp_msg_event(u8_t request_id);
+err_t snmp_send_response(struct snmp_msg_pstat *m_stat);
+err_t snmp_send_trap(s8_t generic_trap, struct snmp_obj_id *eoid, s32_t specific_trap);
+void snmp_coldstart_trap(void);
+void snmp_authfail_trap(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* __LWIP_SNMP_MSG_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/snmp_structs.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/snmp_structs.h
new file mode 100644
index 00000000..0d3b46a9
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/snmp_structs.h
@@ -0,0 +1,268 @@
+/**
+ * @file
+ * Generic MIB tree structures.
+ *
+ * @todo namespace prefixes
+ */
+
+/*
+ * Copyright (c) 2006 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Christiaan Simons <christiaan.simons@axon.tv>
+ */
+
+#ifndef __LWIP_SNMP_STRUCTS_H__
+#define __LWIP_SNMP_STRUCTS_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_SNMP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/snmp.h"
+
+#if SNMP_PRIVATE_MIB
+/* When using a private MIB, you have to create a file 'private_mib.h' that contains
+ * a 'struct mib_array_node mib_private' which contains your MIB. */
+#include "private_mib.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* MIB object instance */
+#define MIB_OBJECT_NONE 0
+#define MIB_OBJECT_SCALAR 1
+#define MIB_OBJECT_TAB 2
+
+/* MIB access types */
+#define MIB_ACCESS_READ 1
+#define MIB_ACCESS_WRITE 2
+
+/* MIB object access */
+#define MIB_OBJECT_READ_ONLY MIB_ACCESS_READ
+#define MIB_OBJECT_READ_WRITE (MIB_ACCESS_READ | MIB_ACCESS_WRITE)
+#define MIB_OBJECT_WRITE_ONLY MIB_ACCESS_WRITE
+#define MIB_OBJECT_NOT_ACCESSIBLE 0
+
+/** object definition returned by (get_object_def)() */
+struct obj_def
+{
+ /* MIB_OBJECT_NONE (0), MIB_OBJECT_SCALAR (1), MIB_OBJECT_TAB (2) */
+ u8_t instance;
+ /* 0 read-only, 1 read-write, 2 write-only, 3 not-accessible */
+ u8_t access;
+ /* ASN type for this object */
+ u8_t asn_type;
+ /* value length (host length) */
+ u16_t v_len;
+ /* length of instance part of supplied object identifier */
+ u8_t id_inst_len;
+ /* instance part of supplied object identifier */
+ s32_t *id_inst_ptr;
+};
+
+struct snmp_name_ptr
+{
+ u8_t ident_len;
+ s32_t *ident;
+};
+
+/** MIB const scalar (.0) node */
+#define MIB_NODE_SC 0x01
+/** MIB const array node */
+#define MIB_NODE_AR 0x02
+/** MIB array node (mem_malloced from RAM) */
+#define MIB_NODE_RA 0x03
+/** MIB list root node (mem_malloced from RAM) */
+#define MIB_NODE_LR 0x04
+/** MIB node for external objects */
+#define MIB_NODE_EX 0x05
+
+/** node "base class" layout, the mandatory fields for a node */
+struct mib_node
+{
+ /** returns struct obj_def for the given object identifier */
+ void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
+ /** returns object value for the given object identifier,
+ @note the caller must allocate at least len bytes for the value */
+ void (*get_value)(struct obj_def *od, u16_t len, void *value);
+ /** tests length and/or range BEFORE setting */
+ u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
+ /** sets object value, only to be called when set_test() */
+ void (*set_value)(struct obj_def *od, u16_t len, void *value);
+ /** One out of MIB_NODE_AR, MIB_NODE_LR or MIB_NODE_EX */
+ u8_t node_type;
+ /* array or max list length */
+ u16_t maxlength;
+};
+
+/** derived node for scalars .0 index */
+typedef struct mib_node mib_scalar_node;
+
+/** derived node, points to a fixed size const array
+ of sub-identifiers plus a 'child' pointer */
+struct mib_array_node
+{
+ /* inherited "base class" members */
+ void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
+ void (*get_value)(struct obj_def *od, u16_t len, void *value);
+ u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
+ void (*set_value)(struct obj_def *od, u16_t len, void *value);
+
+ u8_t node_type;
+ u16_t maxlength;
+
+ /* additional struct members */
+ const s32_t *objid;
+ struct mib_node* const *nptr;
+};
+
+/** derived node, points to a fixed size mem_malloced array
+ of sub-identifiers plus a 'child' pointer */
+struct mib_ram_array_node
+{
+ /* inherited "base class" members */
+ void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
+ void (*get_value)(struct obj_def *od, u16_t len, void *value);
+ u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
+ void (*set_value)(struct obj_def *od, u16_t len, void *value);
+
+ u8_t node_type;
+ u16_t maxlength;
+
+ /* aditional struct members */
+ s32_t *objid;
+ struct mib_node **nptr;
+};
+
+struct mib_list_node
+{
+ struct mib_list_node *prev;
+ struct mib_list_node *next;
+ s32_t objid;
+ struct mib_node *nptr;
+};
+
+/** derived node, points to a doubly linked list
+ of sub-identifiers plus a 'child' pointer */
+struct mib_list_rootnode
+{
+ /* inherited "base class" members */
+ void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
+ void (*get_value)(struct obj_def *od, u16_t len, void *value);
+ u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
+ void (*set_value)(struct obj_def *od, u16_t len, void *value);
+
+ u8_t node_type;
+ u16_t maxlength;
+
+ /* additional struct members */
+ struct mib_list_node *head;
+ struct mib_list_node *tail;
+ /* counts list nodes in list */
+ u16_t count;
+};
+
+/** derived node, has access functions for mib object in external memory or device
+ using 'tree_level' and 'idx', with a range 0 .. (level_length() - 1) */
+struct mib_external_node
+{
+ /* inherited "base class" members */
+ void (*get_object_def)(u8_t ident_len, s32_t *ident, struct obj_def *od);
+ void (*get_value)(struct obj_def *od, u16_t len, void *value);
+ u8_t (*set_test)(struct obj_def *od, u16_t len, void *value);
+ void (*set_value)(struct obj_def *od, u16_t len, void *value);
+
+ u8_t node_type;
+ u16_t maxlength;
+
+ /* additional struct members */
+ /** points to an external (in memory) record of some sort of addressing
+ information, passed to and interpreted by the funtions below */
+ void* addr_inf;
+ /** tree levels under this node */
+ u8_t tree_levels;
+ /** number of objects at this level */
+ u16_t (*level_length)(void* addr_inf, u8_t level);
+ /** compares object sub identifier with external id
+ return zero when equal, nonzero when unequal */
+ s32_t (*ident_cmp)(void* addr_inf, u8_t level, u16_t idx, s32_t sub_id);
+ void (*get_objid)(void* addr_inf, u8_t level, u16_t idx, s32_t *sub_id);
+
+ /** async Questions */
+ void (*get_object_def_q)(void* addr_inf, u8_t rid, u8_t ident_len, s32_t *ident);
+ void (*get_value_q)(u8_t rid, struct obj_def *od);
+ void (*set_test_q)(u8_t rid, struct obj_def *od);
+ void (*set_value_q)(u8_t rid, struct obj_def *od, u16_t len, void *value);
+ /** async Answers */
+ void (*get_object_def_a)(u8_t rid, u8_t ident_len, s32_t *ident, struct obj_def *od);
+ void (*get_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value);
+ u8_t (*set_test_a)(u8_t rid, struct obj_def *od, u16_t len, void *value);
+ void (*set_value_a)(u8_t rid, struct obj_def *od, u16_t len, void *value);
+ /** async Panic Close (agent returns error reply,
+ e.g. used for external transaction cleanup) */
+ void (*get_object_def_pc)(u8_t rid, u8_t ident_len, s32_t *ident);
+ void (*get_value_pc)(u8_t rid, struct obj_def *od);
+ void (*set_test_pc)(u8_t rid, struct obj_def *od);
+ void (*set_value_pc)(u8_t rid, struct obj_def *od);
+};
+
+/** export MIB tree from mib2.c */
+extern const struct mib_array_node internet;
+
+/** dummy function pointers for non-leaf MIB nodes from mib2.c */
+void noleafs_get_object_def(u8_t ident_len, s32_t *ident, struct obj_def *od);
+void noleafs_get_value(struct obj_def *od, u16_t len, void *value);
+u8_t noleafs_set_test(struct obj_def *od, u16_t len, void *value);
+void noleafs_set_value(struct obj_def *od, u16_t len, void *value);
+
+void snmp_oidtoip(s32_t *ident, ip_addr_t *ip);
+void snmp_iptooid(ip_addr_t *ip, s32_t *ident);
+void snmp_ifindextonetif(s32_t ifindex, struct netif **netif);
+void snmp_netiftoifindex(struct netif *netif, s32_t *ifidx);
+
+struct mib_list_node* snmp_mib_ln_alloc(s32_t id);
+void snmp_mib_ln_free(struct mib_list_node *ln);
+struct mib_list_rootnode* snmp_mib_lrn_alloc(void);
+void snmp_mib_lrn_free(struct mib_list_rootnode *lrn);
+
+s8_t snmp_mib_node_insert(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **insn);
+s8_t snmp_mib_node_find(struct mib_list_rootnode *rn, s32_t objid, struct mib_list_node **fn);
+struct mib_list_rootnode *snmp_mib_node_delete(struct mib_list_rootnode *rn, struct mib_list_node *n);
+
+struct mib_node* snmp_search_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_name_ptr *np);
+struct mib_node* snmp_expand_tree(struct mib_node *node, u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret);
+u8_t snmp_iso_prefix_tst(u8_t ident_len, s32_t *ident);
+u8_t snmp_iso_prefix_expand(u8_t ident_len, s32_t *ident, struct snmp_obj_id *oidret);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SNMP */
+
+#endif /* __LWIP_SNMP_STRUCTS_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/sockets.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/sockets.h
new file mode 100644
index 00000000..853beea8
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/sockets.h
@@ -0,0 +1,434 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+
+#ifndef __LWIP_SOCKETS_H__
+#define __LWIP_SOCKETS_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_SOCKET /* don't build if not configured for use in lwipopts.h */
+
+#include <stddef.h> /* for size_t */
+
+#include "lwip/ip_addr.h"
+#include "lwip/inet.h"
+#include "lwip/inet6.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
+
+/* members are in network byte order */
+struct sockaddr_in {
+ u8_t sin_len;
+ sa_family_t sin_family;
+ in_port_t sin_port;
+ struct in_addr sin_addr;
+#define SIN_ZERO_LEN 8
+ char sin_zero[SIN_ZERO_LEN];
+};
+
+#if LWIP_IPV6
+struct sockaddr_in6 {
+ u8_t sin6_len; /* length of this structure */
+ sa_family_t sin6_family; /* AF_INET6 */
+ in_port_t sin6_port; /* Transport layer port # */
+ u32_t sin6_flowinfo; /* IPv6 flow information */
+ struct in6_addr sin6_addr; /* IPv6 address */
+};
+#endif /* LWIP_IPV6 */
+
+struct sockaddr {
+ u8_t sa_len;
+ sa_family_t sa_family;
+#if LWIP_IPV6
+ char sa_data[22];
+#else /* LWIP_IPV6 */
+ char sa_data[14];
+#endif /* LWIP_IPV6 */
+};
+
+struct sockaddr_storage {
+ u8_t s2_len;
+ sa_family_t ss_family;
+ char s2_data1[2];
+ u32_t s2_data2[3];
+#if LWIP_IPV6
+ u32_t s2_data3[2];
+#endif /* LWIP_IPV6 */
+};
+
+/* If your port already typedef's socklen_t, define SOCKLEN_T_DEFINED
+ to prevent this code from redefining it. */
+#if !defined(socklen_t) && !defined(SOCKLEN_T_DEFINED)
+typedef u32_t socklen_t;
+#endif
+
+/* Socket protocol types (TCP/UDP/RAW) */
+#define SOCK_STREAM 1
+#define SOCK_DGRAM 2
+#define SOCK_RAW 3
+
+/*
+ * Option flags per-socket. These must match the SOF_ flags in ip.h (checked in init.c)
+ */
+#define SO_DEBUG 0x0001 /* Unimplemented: turn on debugging info recording */
+#define SO_ACCEPTCONN 0x0002 /* socket has had listen() */
+#define SO_REUSEADDR 0x0004 /* Allow local address reuse */
+#define SO_KEEPALIVE 0x0008 /* keep connections alive */
+#define SO_DONTROUTE 0x0010 /* Unimplemented: just use interface addresses */
+#define SO_BROADCAST 0x0020 /* permit to send and to receive broadcast messages (see IP_SOF_BROADCAST option) */
+#define SO_USELOOPBACK 0x0040 /* Unimplemented: bypass hardware when possible */
+#define SO_LINGER 0x0080 /* linger on close if data present */
+#define SO_OOBINLINE 0x0100 /* Unimplemented: leave received OOB data in line */
+#define SO_REUSEPORT 0x0200 /* Unimplemented: allow local address & port reuse */
+
+#define SO_DONTLINGER ((int)(~SO_LINGER))
+
+/*
+ * Additional options, not kept in so_options.
+ */
+#define SO_SNDBUF 0x1001 /* Unimplemented: send buffer size */
+#define SO_RCVBUF 0x1002 /* receive buffer size */
+#define SO_SNDLOWAT 0x1003 /* Unimplemented: send low-water mark */
+#define SO_RCVLOWAT 0x1004 /* Unimplemented: receive low-water mark */
+#define SO_SNDTIMEO 0x1005 /* Unimplemented: send timeout */
+#define SO_RCVTIMEO 0x1006 /* receive timeout */
+#define SO_ERROR 0x1007 /* get error status and clear */
+#define SO_TYPE 0x1008 /* get socket type */
+#define SO_CONTIMEO 0x1009 /* Unimplemented: connect timeout */
+#define SO_NO_CHECK 0x100a /* don't create UDP checksum */
+
+
+/*
+ * Structure used for manipulating linger option.
+ */
+struct linger {
+ int l_onoff; /* option on/off */
+ int l_linger; /* linger time */
+};
+
+/*
+ * Level number for (get/set)sockopt() to apply to socket itself.
+ */
+#define SOL_SOCKET 0xfff /* options for socket level */
+
+
+#define AF_UNSPEC 0
+#define AF_INET 2
+#if LWIP_IPV6
+#define AF_INET6 10
+#else /* LWIP_IPV6 */
+#define AF_INET6 AF_UNSPEC
+#endif /* LWIP_IPV6 */
+#define PF_INET AF_INET
+#define PF_INET6 AF_INET6
+#define PF_UNSPEC AF_UNSPEC
+
+#define IPPROTO_IP 0
+#define IPPROTO_ICMP 1
+#define IPPROTO_TCP 6
+#define IPPROTO_UDP 17
+#if LWIP_IPV6
+#define IPPROTO_IPV6 41
+#define IPPROTO_ICMPV6 58
+#endif /* LWIP_IPV6 */
+#define IPPROTO_UDPLITE 136
+
+/* Flags we can use with send and recv. */
+#define MSG_PEEK 0x01 /* Peeks at an incoming message */
+#define MSG_WAITALL 0x02 /* Unimplemented: Requests that the function block until the full amount of data requested can be returned */
+#define MSG_OOB 0x04 /* Unimplemented: Requests out-of-band data. The significance and semantics of out-of-band data are protocol-specific */
+#define MSG_DONTWAIT 0x08 /* Nonblocking i/o for this operation only */
+#define MSG_MORE 0x10 /* Sender will send more */
+
+
+/*
+ * Options for level IPPROTO_IP
+ */
+#define IP_TOS 1
+#define IP_TTL 2
+
+#if LWIP_TCP
+/*
+ * Options for level IPPROTO_TCP
+ */
+#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */
+#define TCP_KEEPALIVE 0x02 /* send KEEPALIVE probes when idle for pcb->keep_idle milliseconds */
+#define TCP_KEEPIDLE 0x03 /* set pcb->keep_idle - Same as TCP_KEEPALIVE, but use seconds for get/setsockopt */
+#define TCP_KEEPINTVL 0x04 /* set pcb->keep_intvl - Use seconds for get/setsockopt */
+#define TCP_KEEPCNT 0x05 /* set pcb->keep_cnt - Use number of probes sent for get/setsockopt */
+#endif /* LWIP_TCP */
+
+#if LWIP_IPV6
+/*
+ * Options for level IPPROTO_IPV6
+ */
+#define IPV6_V6ONLY 27 /* RFC3493: boolean control to restrict AF_INET6 sockets to IPv6 communications only. */
+#endif /* LWIP_IPV6 */
+
+#if LWIP_UDP && LWIP_UDPLITE
+/*
+ * Options for level IPPROTO_UDPLITE
+ */
+#define UDPLITE_SEND_CSCOV 0x01 /* sender checksum coverage */
+#define UDPLITE_RECV_CSCOV 0x02 /* minimal receiver checksum coverage */
+#endif /* LWIP_UDP && LWIP_UDPLITE*/
+
+
+#if LWIP_IGMP
+/*
+ * Options and types for UDP multicast traffic handling
+ */
+#define IP_ADD_MEMBERSHIP 3
+#define IP_DROP_MEMBERSHIP 4
+#define IP_MULTICAST_TTL 5
+#define IP_MULTICAST_IF 6
+#define IP_MULTICAST_LOOP 7
+
+typedef struct ip_mreq {
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_interface; /* local IP address of interface */
+} ip_mreq;
+#endif /* LWIP_IGMP */
+
+/*
+ * The Type of Service provides an indication of the abstract
+ * parameters of the quality of service desired. These parameters are
+ * to be used to guide the selection of the actual service parameters
+ * when transmitting a datagram through a particular network. Several
+ * networks offer service precedence, which somehow treats high
+ * precedence traffic as more important than other traffic (generally
+ * by accepting only traffic above a certain precedence at time of high
+ * load). The major choice is a three way tradeoff between low-delay,
+ * high-reliability, and high-throughput.
+ * The use of the Delay, Throughput, and Reliability indications may
+ * increase the cost (in some sense) of the service. In many networks
+ * better performance for one of these parameters is coupled with worse
+ * performance on another. Except for very unusual cases at most two
+ * of these three indications should be set.
+ */
+#define IPTOS_TOS_MASK 0x1E
+#define IPTOS_TOS(tos) ((tos) & IPTOS_TOS_MASK)
+#define IPTOS_LOWDELAY 0x10
+#define IPTOS_THROUGHPUT 0x08
+#define IPTOS_RELIABILITY 0x04
+#define IPTOS_LOWCOST 0x02
+#define IPTOS_MINCOST IPTOS_LOWCOST
+
+/*
+ * The Network Control precedence designation is intended to be used
+ * within a network only. The actual use and control of that
+ * designation is up to each network. The Internetwork Control
+ * designation is intended for use by gateway control originators only.
+ * If the actual use of these precedence designations is of concern to
+ * a particular network, it is the responsibility of that network to
+ * control the access to, and use of, those precedence designations.
+ */
+#define IPTOS_PREC_MASK 0xe0
+#define IPTOS_PREC(tos) ((tos) & IPTOS_PREC_MASK)
+#define IPTOS_PREC_NETCONTROL 0xe0
+#define IPTOS_PREC_INTERNETCONTROL 0xc0
+#define IPTOS_PREC_CRITIC_ECP 0xa0
+#define IPTOS_PREC_FLASHOVERRIDE 0x80
+#define IPTOS_PREC_FLASH 0x60
+#define IPTOS_PREC_IMMEDIATE 0x40
+#define IPTOS_PREC_PRIORITY 0x20
+#define IPTOS_PREC_ROUTINE 0x00
+
+
+/*
+ * Commands for ioctlsocket(), taken from the BSD file fcntl.h.
+ * lwip_ioctl only supports FIONREAD and FIONBIO, for now
+ *
+ * Ioctl's have the command encoded in the lower word,
+ * and the size of any in or out parameters in the upper
+ * word. The high 2 bits of the upper word are used
+ * to encode the in/out status of the parameter; for now
+ * we restrict parameters to at most 128 bytes.
+ */
+#if !defined(FIONREAD) || !defined(FIONBIO)
+#define IOCPARM_MASK 0x7fU /* parameters must be < 128 bytes */
+#define IOC_VOID 0x20000000UL /* no parameters */
+#define IOC_OUT 0x40000000UL /* copy out parameters */
+#define IOC_IN 0x80000000UL /* copy in parameters */
+#define IOC_INOUT (IOC_IN|IOC_OUT)
+ /* 0x20000000 distinguishes new &
+ old ioctl's */
+#define _IO(x,y) (IOC_VOID|((x)<<8)|(y))
+
+#define _IOR(x,y,t) (IOC_OUT|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y))
+
+#define _IOW(x,y,t) (IOC_IN|(((long)sizeof(t)&IOCPARM_MASK)<<16)|((x)<<8)|(y))
+#endif /* !defined(FIONREAD) || !defined(FIONBIO) */
+
+#ifndef FIONREAD
+#define FIONREAD _IOR('f', 127, unsigned long) /* get # bytes to read */
+#endif
+#ifndef FIONBIO
+#define FIONBIO _IOW('f', 126, unsigned long) /* set/clear non-blocking i/o */
+#endif
+
+/* Socket I/O Controls: unimplemented */
+#ifndef SIOCSHIWAT
+#define SIOCSHIWAT _IOW('s', 0, unsigned long) /* set high watermark */
+#define SIOCGHIWAT _IOR('s', 1, unsigned long) /* get high watermark */
+#define SIOCSLOWAT _IOW('s', 2, unsigned long) /* set low watermark */
+#define SIOCGLOWAT _IOR('s', 3, unsigned long) /* get low watermark */
+#define SIOCATMARK _IOR('s', 7, unsigned long) /* at oob mark? */
+#endif
+
+/* commands for fnctl */
+#ifndef F_GETFL
+#define F_GETFL 3
+#endif
+#ifndef F_SETFL
+#define F_SETFL 4
+#endif
+
+/* File status flags and file access modes for fnctl,
+ these are bits in an int. */
+#ifndef O_NONBLOCK
+#define O_NONBLOCK 1 /* nonblocking I/O */
+#endif
+#ifndef O_NDELAY
+#define O_NDELAY 1 /* same as O_NONBLOCK, for compatibility */
+#endif
+
+#ifndef SHUT_RD
+ #define SHUT_RD 0
+ #define SHUT_WR 1
+ #define SHUT_RDWR 2
+#endif
+
+/* FD_SET used for lwip_select */
+#ifndef FD_SET
+ #undef FD_SETSIZE
+ /* Make FD_SETSIZE match NUM_SOCKETS in socket.c */
+ #define FD_SETSIZE MEMP_NUM_NETCONN
+ #define FD_SET(n, p) ((p)->fd_bits[(n)/8] |= (1 << ((n) & 7)))
+ #define FD_CLR(n, p) ((p)->fd_bits[(n)/8] &= ~(1 << ((n) & 7)))
+ #define FD_ISSET(n,p) ((p)->fd_bits[(n)/8] & (1 << ((n) & 7)))
+ #define FD_ZERO(p) memset((void*)(p),0,sizeof(*(p)))
+
+ typedef struct fd_set {
+ unsigned char fd_bits [(FD_SETSIZE+7)/8];
+ } fd_set;
+
+#endif /* FD_SET */
+
+/** LWIP_TIMEVAL_PRIVATE: if you want to use the struct timeval provided
+ * by your system, set this to 0 and include <sys/time.h> in cc.h */
+#ifndef LWIP_TIMEVAL_PRIVATE
+#define LWIP_TIMEVAL_PRIVATE 1
+#endif
+
+#if LWIP_TIMEVAL_PRIVATE
+struct timeval {
+ long tv_sec; /* seconds */
+ long tv_usec; /* and microseconds */
+};
+#endif /* LWIP_TIMEVAL_PRIVATE */
+
+void lwip_socket_init(void);
+
+int lwip_accept(int s, struct sockaddr *addr, socklen_t *addrlen);
+int lwip_bind(int s, const struct sockaddr *name, socklen_t namelen);
+int lwip_shutdown(int s, int how);
+int lwip_getpeername (int s, struct sockaddr *name, socklen_t *namelen);
+int lwip_getsockname (int s, struct sockaddr *name, socklen_t *namelen);
+int lwip_getsockopt (int s, int level, int optname, void *optval, socklen_t *optlen);
+int lwip_setsockopt (int s, int level, int optname, const void *optval, socklen_t optlen);
+int lwip_close(int s);
+int lwip_connect(int s, const struct sockaddr *name, socklen_t namelen);
+int lwip_listen(int s, int backlog);
+int lwip_recv(int s, void *mem, size_t len, int flags);
+int lwip_read(int s, void *mem, size_t len);
+int lwip_recvfrom(int s, void *mem, size_t len, int flags,
+ struct sockaddr *from, socklen_t *fromlen);
+int lwip_send(int s, const void *dataptr, size_t size, int flags);
+int lwip_sendto(int s, const void *dataptr, size_t size, int flags,
+ const struct sockaddr *to, socklen_t tolen);
+int lwip_socket(int domain, int type, int protocol);
+int lwip_write(int s, const void *dataptr, size_t size);
+int lwip_select(int maxfdp1, fd_set *readset, fd_set *writeset, fd_set *exceptset,
+ struct timeval *timeout);
+int lwip_ioctl(int s, long cmd, void *argp);
+int lwip_fcntl(int s, int cmd, int val);
+
+#if LWIP_COMPAT_SOCKETS
+#define accept(a,b,c) lwip_accept(a,b,c)
+#define bind(a,b,c) lwip_bind(a,b,c)
+#define shutdown(a,b) lwip_shutdown(a,b)
+#define closesocket(s) lwip_close(s)
+#define connect(a,b,c) lwip_connect(a,b,c)
+#define getsockname(a,b,c) lwip_getsockname(a,b,c)
+#define getpeername(a,b,c) lwip_getpeername(a,b,c)
+#define setsockopt(a,b,c,d,e) lwip_setsockopt(a,b,c,d,e)
+#define getsockopt(a,b,c,d,e) lwip_getsockopt(a,b,c,d,e)
+#define listen(a,b) lwip_listen(a,b)
+#define recv(a,b,c,d) lwip_recv(a,b,c,d)
+#define recvfrom(a,b,c,d,e,f) lwip_recvfrom(a,b,c,d,e,f)
+#define send(a,b,c,d) lwip_send(a,b,c,d)
+#define sendto(a,b,c,d,e,f) lwip_sendto(a,b,c,d,e,f)
+#define socket(a,b,c) lwip_socket(a,b,c)
+#define select(a,b,c,d,e) lwip_select(a,b,c,d,e)
+#define ioctlsocket(a,b,c) lwip_ioctl(a,b,c)
+
+#if LWIP_POSIX_SOCKETS_IO_NAMES
+#define read(a,b,c) lwip_read(a,b,c)
+#define write(a,b,c) lwip_write(a,b,c)
+#define close(s) lwip_close(s)
+#define fcntl(a,b,c) lwip_fcntl(a,b,c)
+#endif /* LWIP_POSIX_SOCKETS_IO_NAMES */
+
+#endif /* LWIP_COMPAT_SOCKETS */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_SOCKET */
+
+#endif /* __LWIP_SOCKETS_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/stats.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/stats.h
new file mode 100644
index 00000000..d9112160
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/stats.h
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_STATS_H__
+#define __LWIP_STATS_H__
+
+#include "lwip/opt.h"
+
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if LWIP_STATS
+
+#ifndef LWIP_STATS_LARGE
+#define LWIP_STATS_LARGE 0
+#endif
+
+#if LWIP_STATS_LARGE
+#define STAT_COUNTER u32_t
+#define STAT_COUNTER_F U32_F
+#else
+#define STAT_COUNTER u16_t
+#define STAT_COUNTER_F U16_F
+#endif
+
+struct stats_proto {
+ STAT_COUNTER xmit; /* Transmitted packets. */
+ STAT_COUNTER recv; /* Received packets. */
+ STAT_COUNTER fw; /* Forwarded packets. */
+ STAT_COUNTER drop; /* Dropped packets. */
+ STAT_COUNTER chkerr; /* Checksum error. */
+ STAT_COUNTER lenerr; /* Invalid length error. */
+ STAT_COUNTER memerr; /* Out of memory error. */
+ STAT_COUNTER rterr; /* Routing error. */
+ STAT_COUNTER proterr; /* Protocol error. */
+ STAT_COUNTER opterr; /* Error in options. */
+ STAT_COUNTER err; /* Misc error. */
+ STAT_COUNTER cachehit;
+};
+
+struct stats_igmp {
+ STAT_COUNTER xmit; /* Transmitted packets. */
+ STAT_COUNTER recv; /* Received packets. */
+ STAT_COUNTER drop; /* Dropped packets. */
+ STAT_COUNTER chkerr; /* Checksum error. */
+ STAT_COUNTER lenerr; /* Invalid length error. */
+ STAT_COUNTER memerr; /* Out of memory error. */
+ STAT_COUNTER proterr; /* Protocol error. */
+ STAT_COUNTER rx_v1; /* Received v1 frames. */
+ STAT_COUNTER rx_group; /* Received group-specific queries. */
+ STAT_COUNTER rx_general; /* Received general queries. */
+ STAT_COUNTER rx_report; /* Received reports. */
+ STAT_COUNTER tx_join; /* Sent joins. */
+ STAT_COUNTER tx_leave; /* Sent leaves. */
+ STAT_COUNTER tx_report; /* Sent reports. */
+};
+
+struct stats_mem {
+#ifdef LWIP_DEBUG
+ const char *name;
+#endif /* LWIP_DEBUG */
+ mem_size_t avail;
+ mem_size_t used;
+ mem_size_t max;
+ STAT_COUNTER err;
+ STAT_COUNTER illegal;
+};
+
+struct stats_syselem {
+ STAT_COUNTER used;
+ STAT_COUNTER max;
+ STAT_COUNTER err;
+};
+
+struct stats_sys {
+ struct stats_syselem sem;
+ struct stats_syselem mutex;
+ struct stats_syselem mbox;
+};
+
+struct stats_ {
+#if LINK_STATS
+ struct stats_proto link;
+#endif
+#if ETHARP_STATS
+ struct stats_proto etharp;
+#endif
+#if IPFRAG_STATS
+ struct stats_proto ip_frag;
+#endif
+#if IP_STATS
+ struct stats_proto ip;
+#endif
+#if ICMP_STATS
+ struct stats_proto icmp;
+#endif
+#if IGMP_STATS
+ struct stats_igmp igmp;
+#endif
+#if UDP_STATS
+ struct stats_proto udp;
+#endif
+#if TCP_STATS
+ struct stats_proto tcp;
+#endif
+#if MEM_STATS
+ struct stats_mem mem;
+#endif
+#if MEMP_STATS
+ struct stats_mem memp[MEMP_MAX];
+#endif
+#if SYS_STATS
+ struct stats_sys sys;
+#endif
+#if IP6_STATS
+ struct stats_proto ip6;
+#endif
+#if ICMP6_STATS
+ struct stats_proto icmp6;
+#endif
+#if IP6_FRAG_STATS
+ struct stats_proto ip6_frag;
+#endif
+#if MLD6_STATS
+ struct stats_igmp mld6;
+#endif
+#if ND6_STATS
+ struct stats_proto nd6;
+#endif
+};
+
+extern struct stats_ lwip_stats;
+
+void stats_init(void);
+
+#define STATS_INC(x) ++lwip_stats.x
+#define STATS_DEC(x) --lwip_stats.x
+#define STATS_INC_USED(x, y) do { lwip_stats.x.used += y; \
+ if (lwip_stats.x.max < lwip_stats.x.used) { \
+ lwip_stats.x.max = lwip_stats.x.used; \
+ } \
+ } while(0)
+#else /* LWIP_STATS */
+#define stats_init()
+#define STATS_INC(x)
+#define STATS_DEC(x)
+#define STATS_INC_USED(x)
+#endif /* LWIP_STATS */
+
+#if TCP_STATS
+#define TCP_STATS_INC(x) STATS_INC(x)
+#define TCP_STATS_DISPLAY() stats_display_proto(&lwip_stats.tcp, "TCP")
+#else
+#define TCP_STATS_INC(x)
+#define TCP_STATS_DISPLAY()
+#endif
+
+#if UDP_STATS
+#define UDP_STATS_INC(x) STATS_INC(x)
+#define UDP_STATS_DISPLAY() stats_display_proto(&lwip_stats.udp, "UDP")
+#else
+#define UDP_STATS_INC(x)
+#define UDP_STATS_DISPLAY()
+#endif
+
+#if ICMP_STATS
+#define ICMP_STATS_INC(x) STATS_INC(x)
+#define ICMP_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp, "ICMP")
+#else
+#define ICMP_STATS_INC(x)
+#define ICMP_STATS_DISPLAY()
+#endif
+
+#if IGMP_STATS
+#define IGMP_STATS_INC(x) STATS_INC(x)
+#define IGMP_STATS_DISPLAY() stats_display_igmp(&lwip_stats.igmp, "IGMP")
+#else
+#define IGMP_STATS_INC(x)
+#define IGMP_STATS_DISPLAY()
+#endif
+
+#if IP_STATS
+#define IP_STATS_INC(x) STATS_INC(x)
+#define IP_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip, "IP")
+#else
+#define IP_STATS_INC(x)
+#define IP_STATS_DISPLAY()
+#endif
+
+#if IPFRAG_STATS
+#define IPFRAG_STATS_INC(x) STATS_INC(x)
+#define IPFRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip_frag, "IP_FRAG")
+#else
+#define IPFRAG_STATS_INC(x)
+#define IPFRAG_STATS_DISPLAY()
+#endif
+
+#if ETHARP_STATS
+#define ETHARP_STATS_INC(x) STATS_INC(x)
+#define ETHARP_STATS_DISPLAY() stats_display_proto(&lwip_stats.etharp, "ETHARP")
+#else
+#define ETHARP_STATS_INC(x)
+#define ETHARP_STATS_DISPLAY()
+#endif
+
+#if LINK_STATS
+#define LINK_STATS_INC(x) STATS_INC(x)
+#define LINK_STATS_DISPLAY() stats_display_proto(&lwip_stats.link, "LINK")
+#else
+#define LINK_STATS_INC(x)
+#define LINK_STATS_DISPLAY()
+#endif
+
+#if MEM_STATS
+#define MEM_STATS_AVAIL(x, y) lwip_stats.mem.x = y
+#define MEM_STATS_INC(x) STATS_INC(mem.x)
+#define MEM_STATS_INC_USED(x, y) STATS_INC_USED(mem, y)
+#define MEM_STATS_DEC_USED(x, y) lwip_stats.mem.x -= y
+#define MEM_STATS_DISPLAY() stats_display_mem(&lwip_stats.mem, "HEAP")
+#else
+#define MEM_STATS_AVAIL(x, y)
+#define MEM_STATS_INC(x)
+#define MEM_STATS_INC_USED(x, y)
+#define MEM_STATS_DEC_USED(x, y)
+#define MEM_STATS_DISPLAY()
+#endif
+
+#if MEMP_STATS
+#define MEMP_STATS_AVAIL(x, i, y) lwip_stats.memp[i].x = y
+#define MEMP_STATS_INC(x, i) STATS_INC(memp[i].x)
+#define MEMP_STATS_DEC(x, i) STATS_DEC(memp[i].x)
+#define MEMP_STATS_INC_USED(x, i) STATS_INC_USED(memp[i], 1)
+#define MEMP_STATS_DISPLAY(i) stats_display_memp(&lwip_stats.memp[i], i)
+#else
+#define MEMP_STATS_AVAIL(x, i, y)
+#define MEMP_STATS_INC(x, i)
+#define MEMP_STATS_DEC(x, i)
+#define MEMP_STATS_INC_USED(x, i)
+#define MEMP_STATS_DISPLAY(i)
+#endif
+
+#if SYS_STATS
+#define SYS_STATS_INC(x) STATS_INC(sys.x)
+#define SYS_STATS_DEC(x) STATS_DEC(sys.x)
+#define SYS_STATS_INC_USED(x) STATS_INC_USED(sys.x, 1)
+#define SYS_STATS_DISPLAY() stats_display_sys(&lwip_stats.sys)
+#else
+#define SYS_STATS_INC(x)
+#define SYS_STATS_DEC(x)
+#define SYS_STATS_INC_USED(x)
+#define SYS_STATS_DISPLAY()
+#endif
+
+#if IP6_STATS
+#define IP6_STATS_INC(x) STATS_INC(x)
+#define IP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6, "IPv6")
+#else
+#define IP6_STATS_INC(x)
+#define IP6_STATS_DISPLAY()
+#endif
+
+#if ICMP6_STATS
+#define ICMP6_STATS_INC(x) STATS_INC(x)
+#define ICMP6_STATS_DISPLAY() stats_display_proto(&lwip_stats.icmp6, "ICMPv6")
+#else
+#define ICMP6_STATS_INC(x)
+#define ICMP6_STATS_DISPLAY()
+#endif
+
+#if IP6_FRAG_STATS
+#define IP6_FRAG_STATS_INC(x) STATS_INC(x)
+#define IP6_FRAG_STATS_DISPLAY() stats_display_proto(&lwip_stats.ip6_frag, "IPv6 FRAG")
+#else
+#define IP6_FRAG_STATS_INC(x)
+#define IP6_FRAG_STATS_DISPLAY()
+#endif
+
+#if MLD6_STATS
+#define MLD6_STATS_INC(x) STATS_INC(x)
+#define MLD6_STATS_DISPLAY() stats_display_igmp(&lwip_stats.mld6, "MLDv1")
+#else
+#define MLD6_STATS_INC(x)
+#define MLD6_STATS_DISPLAY()
+#endif
+
+#if ND6_STATS
+#define ND6_STATS_INC(x) STATS_INC(x)
+#define ND6_STATS_DISPLAY() stats_display_proto(&lwip_stats.nd6, "ND")
+#else
+#define ND6_STATS_INC(x)
+#define ND6_STATS_DISPLAY()
+#endif
+
+/* Display of statistics */
+#if LWIP_STATS_DISPLAY
+void stats_display(void);
+void stats_display_proto(struct stats_proto *proto, const char *name);
+void stats_display_igmp(struct stats_igmp *igmp, const char *name);
+void stats_display_mem(struct stats_mem *mem, const char *name);
+void stats_display_memp(struct stats_mem *mem, int index);
+void stats_display_sys(struct stats_sys *sys);
+#else /* LWIP_STATS_DISPLAY */
+#define stats_display()
+#define stats_display_proto(proto, name)
+#define stats_display_igmp(igmp, name)
+#define stats_display_mem(mem, name)
+#define stats_display_memp(mem, index)
+#define stats_display_sys(sys)
+#endif /* LWIP_STATS_DISPLAY */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_STATS_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/sys.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/sys.h
new file mode 100644
index 00000000..fd45ee8a
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/sys.h
@@ -0,0 +1,336 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_SYS_H__
+#define __LWIP_SYS_H__
+
+#include "lwip/opt.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#if NO_SYS
+
+/* For a totally minimal and standalone system, we provide null
+ definitions of the sys_ functions. */
+typedef u8_t sys_sem_t;
+typedef u8_t sys_mutex_t;
+typedef u8_t sys_mbox_t;
+
+#define sys_sem_new(s, c) ERR_OK
+#define sys_sem_signal(s)
+#define sys_sem_wait(s)
+#define sys_arch_sem_wait(s,t)
+#define sys_sem_free(s)
+#define sys_sem_valid(s) 0
+#define sys_sem_set_invalid(s)
+#define sys_mutex_new(mu) ERR_OK
+#define sys_mutex_lock(mu)
+#define sys_mutex_unlock(mu)
+#define sys_mutex_free(mu)
+#define sys_mutex_valid(mu) 0
+#define sys_mutex_set_invalid(mu)
+#define sys_mbox_new(m, s) ERR_OK
+#define sys_mbox_fetch(m,d)
+#define sys_mbox_tryfetch(m,d)
+#define sys_mbox_post(m,d)
+#define sys_mbox_trypost(m,d)
+#define sys_mbox_free(m)
+#define sys_mbox_valid(m)
+#define sys_mbox_set_invalid(m)
+
+#define sys_thread_new(n,t,a,s,p)
+
+#define sys_msleep(t)
+
+#else /* NO_SYS */
+
+/** Return code for timeouts from sys_arch_mbox_fetch and sys_arch_sem_wait */
+#define SYS_ARCH_TIMEOUT 0xffffffffUL
+
+/** sys_mbox_tryfetch() returns SYS_MBOX_EMPTY if appropriate.
+ * For now we use the same magic value, but we allow this to change in future.
+ */
+#define SYS_MBOX_EMPTY SYS_ARCH_TIMEOUT
+
+#include "lwip/err.h"
+#include "arch/sys_arch.h"
+
+/** Function prototype for thread functions */
+typedef void (*lwip_thread_fn)(void *arg);
+
+/* Function prototypes for functions to be implemented by platform ports
+ (in sys_arch.c) */
+
+/* Mutex functions: */
+
+/** Define LWIP_COMPAT_MUTEX if the port has no mutexes and binary semaphores
+ should be used instead */
+#if LWIP_COMPAT_MUTEX
+/* for old ports that don't have mutexes: define them to binary semaphores */
+#define sys_mutex_t sys_sem_t
+#define sys_mutex_new(mutex) sys_sem_new(mutex, 1)
+#define sys_mutex_lock(mutex) sys_sem_wait(mutex)
+#define sys_mutex_unlock(mutex) sys_sem_signal(mutex)
+#define sys_mutex_free(mutex) sys_sem_free(mutex)
+#define sys_mutex_valid(mutex) sys_sem_valid(mutex)
+#define sys_mutex_set_invalid(mutex) sys_sem_set_invalid(mutex)
+
+#else /* LWIP_COMPAT_MUTEX */
+
+/** Create a new mutex
+ * @param mutex pointer to the mutex to create
+ * @return a new mutex */
+err_t sys_mutex_new(sys_mutex_t *mutex);
+/** Lock a mutex
+ * @param mutex the mutex to lock */
+void sys_mutex_lock(sys_mutex_t *mutex);
+/** Unlock a mutex
+ * @param mutex the mutex to unlock */
+void sys_mutex_unlock(sys_mutex_t *mutex);
+/** Delete a semaphore
+ * @param mutex the mutex to delete */
+void sys_mutex_free(sys_mutex_t *mutex);
+#ifndef sys_mutex_valid
+/** Check if a mutex is valid/allocated: return 1 for valid, 0 for invalid */
+int sys_mutex_valid(sys_mutex_t *mutex);
+#endif
+#ifndef sys_mutex_set_invalid
+/** Set a mutex invalid so that sys_mutex_valid returns 0 */
+void sys_mutex_set_invalid(sys_mutex_t *mutex);
+#endif
+#endif /* LWIP_COMPAT_MUTEX */
+
+/* Semaphore functions: */
+
+/** Create a new semaphore
+ * @param sem pointer to the semaphore to create
+ * @param count initial count of the semaphore
+ * @return ERR_OK if successful, another err_t otherwise */
+err_t sys_sem_new(sys_sem_t *sem, u8_t count);
+/** Signals a semaphore
+ * @param sem the semaphore to signal */
+void sys_sem_signal(sys_sem_t *sem);
+/** Wait for a semaphore for the specified timeout
+ * @param sem the semaphore to wait for
+ * @param timeout timeout in milliseconds to wait (0 = wait forever)
+ * @return time (in milliseconds) waited for the semaphore
+ * or SYS_ARCH_TIMEOUT on timeout */
+u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout);
+/** Delete a semaphore
+ * @param sem semaphore to delete */
+void sys_sem_free(sys_sem_t *sem);
+/** Wait for a semaphore - forever/no timeout */
+#define sys_sem_wait(sem) sys_arch_sem_wait(sem, 0)
+#ifndef sys_sem_valid
+/** Check if a sempahore is valid/allocated: return 1 for valid, 0 for invalid */
+int sys_sem_valid(sys_sem_t *sem);
+#endif
+#ifndef sys_sem_set_invalid
+/** Set a semaphore invalid so that sys_sem_valid returns 0 */
+void sys_sem_set_invalid(sys_sem_t *sem);
+#endif
+
+/* Time functions. */
+#ifndef sys_msleep
+void sys_msleep(u32_t ms); /* only has a (close to) 1 jiffy resolution. */
+#endif
+
+/* Mailbox functions. */
+
+/** Create a new mbox of specified size
+ * @param mbox pointer to the mbox to create
+ * @param size (miminum) number of messages in this mbox
+ * @return ERR_OK if successful, another err_t otherwise */
+err_t sys_mbox_new(sys_mbox_t *mbox, int size);
+/** Post a message to an mbox - may not fail
+ * -> blocks if full, only used from tasks not from ISR
+ * @param mbox mbox to posts the message
+ * @param msg message to post (ATTENTION: can be NULL) */
+void sys_mbox_post(sys_mbox_t *mbox, void *msg);
+/** Try to post a message to an mbox - may fail if full or ISR
+ * @param mbox mbox to posts the message
+ * @param msg message to post (ATTENTION: can be NULL) */
+err_t sys_mbox_trypost(sys_mbox_t *mbox, void *msg);
+/** Wait for a new message to arrive in the mbox
+ * @param mbox mbox to get a message from
+ * @param msg pointer where the message is stored
+ * @param timeout maximum time (in milliseconds) to wait for a message (0 = wait forever)
+ * @return time (in milliseconds) waited for a message, may be 0 if not waited
+ or SYS_ARCH_TIMEOUT on timeout
+ * The returned time has to be accurate to prevent timer jitter! */
+u32_t sys_arch_mbox_fetch(sys_mbox_t *mbox, void **msg, u32_t timeout);
+/* Allow port to override with a macro, e.g. special timout for sys_arch_mbox_fetch() */
+#ifndef sys_arch_mbox_tryfetch
+/** Wait for a new message to arrive in the mbox
+ * @param mbox mbox to get a message from
+ * @param msg pointer where the message is stored
+ * @return 0 (milliseconds) if a message has been received
+ * or SYS_MBOX_EMPTY if the mailbox is empty */
+u32_t sys_arch_mbox_tryfetch(sys_mbox_t *mbox, void **msg);
+#endif
+/** For now, we map straight to sys_arch implementation. */
+#define sys_mbox_tryfetch(mbox, msg) sys_arch_mbox_tryfetch(mbox, msg)
+/** Delete an mbox
+ * @param mbox mbox to delete */
+void sys_mbox_free(sys_mbox_t *mbox);
+#define sys_mbox_fetch(mbox, msg) sys_arch_mbox_fetch(mbox, msg, 0)
+#ifndef sys_mbox_valid
+/** Check if an mbox is valid/allocated: return 1 for valid, 0 for invalid */
+int sys_mbox_valid(sys_mbox_t *mbox);
+#endif
+#ifndef sys_mbox_set_invalid
+/** Set an mbox invalid so that sys_mbox_valid returns 0 */
+void sys_mbox_set_invalid(sys_mbox_t *mbox);
+#endif
+
+/** The only thread function:
+ * Creates a new thread
+ * @param name human-readable name for the thread (used for debugging purposes)
+ * @param thread thread-function
+ * @param arg parameter passed to 'thread'
+ * @param stacksize stack size in bytes for the new thread (may be ignored by ports)
+ * @param prio priority of the new thread (may be ignored by ports) */
+sys_thread_t sys_thread_new(const char *name, lwip_thread_fn thread, void *arg, int stacksize, int prio);
+
+#endif /* NO_SYS */
+
+/* sys_init() must be called before anthing else. */
+void sys_init(void);
+
+#ifndef sys_jiffies
+/** Ticks/jiffies since power up. */
+u32_t sys_jiffies(void);
+#endif
+
+/** Returns the current time in milliseconds,
+ * may be the same as sys_jiffies or at least based on it. */
+u32_t sys_now(void);
+
+/* Critical Region Protection */
+/* These functions must be implemented in the sys_arch.c file.
+ In some implementations they can provide a more light-weight protection
+ mechanism than using semaphores. Otherwise semaphores can be used for
+ implementation */
+#ifndef SYS_ARCH_PROTECT
+/** SYS_LIGHTWEIGHT_PROT
+ * define SYS_LIGHTWEIGHT_PROT in lwipopts.h if you want inter-task protection
+ * for certain critical regions during buffer allocation, deallocation and memory
+ * allocation and deallocation.
+ */
+#if SYS_LIGHTWEIGHT_PROT
+
+/** SYS_ARCH_DECL_PROTECT
+ * declare a protection variable. This macro will default to defining a variable of
+ * type sys_prot_t. If a particular port needs a different implementation, then
+ * this macro may be defined in sys_arch.h.
+ */
+#define SYS_ARCH_DECL_PROTECT(lev) sys_prot_t lev
+/** SYS_ARCH_PROTECT
+ * Perform a "fast" protect. This could be implemented by
+ * disabling interrupts for an embedded system or by using a semaphore or
+ * mutex. The implementation should allow calling SYS_ARCH_PROTECT when
+ * already protected. The old protection level is returned in the variable
+ * "lev". This macro will default to calling the sys_arch_protect() function
+ * which should be implemented in sys_arch.c. If a particular port needs a
+ * different implementation, then this macro may be defined in sys_arch.h
+ */
+#define SYS_ARCH_PROTECT(lev) lev = sys_arch_protect()
+/** SYS_ARCH_UNPROTECT
+ * Perform a "fast" set of the protection level to "lev". This could be
+ * implemented by setting the interrupt level to "lev" within the MACRO or by
+ * using a semaphore or mutex. This macro will default to calling the
+ * sys_arch_unprotect() function which should be implemented in
+ * sys_arch.c. If a particular port needs a different implementation, then
+ * this macro may be defined in sys_arch.h
+ */
+#define SYS_ARCH_UNPROTECT(lev) sys_arch_unprotect(lev)
+sys_prot_t sys_arch_protect(void);
+void sys_arch_unprotect(sys_prot_t pval);
+
+#else
+
+#define SYS_ARCH_DECL_PROTECT(lev)
+#define SYS_ARCH_PROTECT(lev)
+#define SYS_ARCH_UNPROTECT(lev)
+
+#endif /* SYS_LIGHTWEIGHT_PROT */
+
+#endif /* SYS_ARCH_PROTECT */
+
+/*
+ * Macros to set/get and increase/decrease variables in a thread-safe way.
+ * Use these for accessing variable that are used from more than one thread.
+ */
+
+#ifndef SYS_ARCH_INC
+#define SYS_ARCH_INC(var, val) do { \
+ SYS_ARCH_DECL_PROTECT(old_level); \
+ SYS_ARCH_PROTECT(old_level); \
+ var += val; \
+ SYS_ARCH_UNPROTECT(old_level); \
+ } while(0)
+#endif /* SYS_ARCH_INC */
+
+#ifndef SYS_ARCH_DEC
+#define SYS_ARCH_DEC(var, val) do { \
+ SYS_ARCH_DECL_PROTECT(old_level); \
+ SYS_ARCH_PROTECT(old_level); \
+ var -= val; \
+ SYS_ARCH_UNPROTECT(old_level); \
+ } while(0)
+#endif /* SYS_ARCH_DEC */
+
+#ifndef SYS_ARCH_GET
+#define SYS_ARCH_GET(var, ret) do { \
+ SYS_ARCH_DECL_PROTECT(old_level); \
+ SYS_ARCH_PROTECT(old_level); \
+ ret = var; \
+ SYS_ARCH_UNPROTECT(old_level); \
+ } while(0)
+#endif /* SYS_ARCH_GET */
+
+#ifndef SYS_ARCH_SET
+#define SYS_ARCH_SET(var, val) do { \
+ SYS_ARCH_DECL_PROTECT(old_level); \
+ SYS_ARCH_PROTECT(old_level); \
+ var = val; \
+ SYS_ARCH_UNPROTECT(old_level); \
+ } while(0)
+#endif /* SYS_ARCH_SET */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __LWIP_SYS_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/tcp.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/tcp.h
new file mode 100644
index 00000000..2da42918
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/tcp.h
@@ -0,0 +1,432 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_TCP_H__
+#define __LWIP_TCP_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip.h"
+#include "lwip/icmp.h"
+#include "lwip/err.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct tcp_pcb;
+
+/** Function prototype for tcp accept callback functions. Called when a new
+ * connection can be accepted on a listening pcb.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param newpcb The new connection pcb
+ * @param err An error code if there has been an error accepting.
+ * Only return ERR_ABRT if you have called tcp_abort from within the
+ * callback function!
+ */
+typedef err_t (*tcp_accept_fn)(void *arg, struct tcp_pcb *newpcb, err_t err);
+
+#if LWIP_CONNECTION_PROXY
+/** Function prototype for tcp accept callback functions. Called when
+ * a new connection is about to be accepted by the proxy or on a
+ * listening pcb that requested proxy-like accept on syn.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param newpcb The new connection pcb
+ * @param syn The pbuf with the SYN segment (may be used to reply with ICMP).
+ */
+typedef err_t (*tcp_accept_syn_fn)(void *arg, struct tcp_pcb *newpcb, struct pbuf *syn);
+#endif /* LWIP_CONNECTION_PROXY */
+
+/** Function prototype for tcp receive callback functions. Called when data has
+ * been received.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb which received data
+ * @param p The received data (or NULL when the connection has been closed!)
+ * @param err An error code if there has been an error receiving
+ * Only return ERR_ABRT if you have called tcp_abort from within the
+ * callback function!
+ */
+typedef err_t (*tcp_recv_fn)(void *arg, struct tcp_pcb *tpcb,
+ struct pbuf *p, err_t err);
+
+/** Function prototype for tcp sent callback functions. Called when sent data has
+ * been acknowledged by the remote side. Use it to free corresponding resources.
+ * This also means that the pcb has now space available to send new data.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb for which data has been acknowledged
+ * @param len The amount of bytes acknowledged
+ * @return ERR_OK: try to send some data by calling tcp_output
+ * Only return ERR_ABRT if you have called tcp_abort from within the
+ * callback function!
+ */
+typedef err_t (*tcp_sent_fn)(void *arg, struct tcp_pcb *tpcb,
+ u16_t len);
+
+/** Function prototype for tcp poll callback functions. Called periodically as
+ * specified by @see tcp_poll.
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb tcp pcb
+ * @return ERR_OK: try to send some data by calling tcp_output
+ * Only return ERR_ABRT if you have called tcp_abort from within the
+ * callback function!
+ */
+typedef err_t (*tcp_poll_fn)(void *arg, struct tcp_pcb *tpcb);
+
+/** Function prototype for tcp error callback functions. Called when the pcb
+ * receives a RST or is unexpectedly closed for any other reason.
+ *
+ * @note The corresponding pcb is already freed when this callback is called!
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param err Error code to indicate why the pcb has been closed
+ * ERR_ABRT: aborted through tcp_abort or by a TCP timer
+ * ERR_RST: the connection was reset by the remote host
+ */
+typedef void (*tcp_err_fn)(void *arg, err_t err);
+
+/** Function prototype for tcp connected callback functions. Called when a pcb
+ * is connected to the remote side after initiating a connection attempt by
+ * calling tcp_connect().
+ *
+ * @param arg Additional argument to pass to the callback function (@see tcp_arg())
+ * @param tpcb The connection pcb which is connected
+ * @param err An unused error code, always ERR_OK currently ;-) TODO!
+ * Only return ERR_ABRT if you have called tcp_abort from within the
+ * callback function!
+ *
+ * @note When a connection attempt fails, the error callback is currently called!
+ */
+typedef err_t (*tcp_connected_fn)(void *arg, struct tcp_pcb *tpcb, err_t err);
+
+enum tcp_state {
+ CLOSED = 0,
+ LISTEN = 1,
+ SYN_SENT = 2,
+#if LWIP_CONNECTION_PROXY
+ /* see tcp_proxy_listen_input */
+ SYN_RCVD_0 = 3,
+#endif
+ SYN_RCVD = 4,
+ ESTABLISHED = 5,
+ FIN_WAIT_1 = 6,
+ FIN_WAIT_2 = 7,
+ CLOSE_WAIT = 8,
+ CLOSING = 9,
+ LAST_ACK = 10,
+ TIME_WAIT = 11
+};
+
+#if LWIP_CALLBACK_API
+ /* Function to call when a listener has been connected.
+ * @param arg user-supplied argument (tcp_pcb.callback_arg)
+ * @param pcb a new tcp_pcb that now is connected
+ * @param err an error argument (TODO: that is current always ERR_OK?)
+ * @return ERR_OK: accept the new connection,
+ * any other err_t abortsthe new connection
+ */
+#define DEF_ACCEPT_CALLBACK tcp_accept_fn accept;
+#else /* LWIP_CALLBACK_API */
+#define DEF_ACCEPT_CALLBACK
+#endif /* LWIP_CALLBACK_API */
+
+/**
+ * members common to struct tcp_pcb and struct tcp_listen_pcb
+ */
+#define TCP_PCB_COMMON(type) \
+ type *next; /* for the linked list */ \
+ void *callback_arg; \
+ /* the accept callback for listen- and normal pcbs, if LWIP_CALLBACK_API */ \
+ DEF_ACCEPT_CALLBACK \
+ enum tcp_state state; /* TCP state */ \
+ u8_t prio; \
+ /* ports are in host byte order */ \
+ u16_t local_port
+
+
+/* the TCP protocol control block */
+struct tcp_pcb {
+/** common PCB members */
+ IP_PCB;
+/** protocol specific PCB members */
+ TCP_PCB_COMMON(struct tcp_pcb);
+
+ /* ports are in host byte order */
+ u16_t remote_port;
+
+ u8_t flags;
+#define TF_ACK_DELAY ((u8_t)0x01U) /* Delayed ACK. */
+#define TF_ACK_NOW ((u8_t)0x02U) /* Immediate ACK. */
+#define TF_INFR ((u8_t)0x04U) /* In fast recovery. */
+#define TF_TIMESTAMP ((u8_t)0x08U) /* Timestamp option enabled */
+#define TF_RXCLOSED ((u8_t)0x10U) /* rx closed by tcp_shutdown */
+#define TF_FIN ((u8_t)0x20U) /* Connection was closed locally (FIN segment enqueued). */
+#define TF_NODELAY ((u8_t)0x40U) /* Disable Nagle algorithm */
+#define TF_NAGLEMEMERR ((u8_t)0x80U) /* nagle enabled, memerr, try to output to prevent delayed ACK to happen */
+
+ /* the rest of the fields are in host byte order
+ as we have to do some math with them */
+
+ /* Timers */
+ u8_t polltmr, pollinterval;
+ u8_t last_timer;
+ u32_t tmr;
+
+ /* receiver variables */
+ u32_t rcv_nxt; /* next seqno expected */
+ u16_t rcv_wnd; /* receiver window available */
+ u16_t rcv_ann_wnd; /* receiver window to announce */
+ u32_t rcv_ann_right_edge; /* announced right edge of window */
+
+ /* Retransmission timer. */
+ s16_t rtime;
+
+ u16_t mss; /* maximum segment size */
+
+ /* RTT (round trip time) estimation variables */
+ u32_t rttest; /* RTT estimate in 500ms ticks */
+ u32_t rtseq; /* sequence number being timed */
+ s16_t sa, sv; /* @todo document this */
+
+ s16_t rto; /* retransmission time-out */
+ u8_t nrtx; /* number of retransmissions */
+
+ /* fast retransmit/recovery */
+ u8_t dupacks;
+ u32_t lastack; /* Highest acknowledged seqno. */
+
+ /* congestion avoidance/control variables */
+ u16_t cwnd;
+ u16_t ssthresh;
+
+ /* sender variables */
+ u32_t snd_nxt; /* next new seqno to be sent */
+ u32_t snd_wl1, snd_wl2; /* Sequence and acknowledgement numbers of last
+ window update. */
+ u32_t snd_lbb; /* Sequence number of next byte to be buffered. */
+ u16_t snd_wnd; /* sender window */
+ u16_t snd_wnd_max; /* the maximum sender window announced by the remote host */
+
+ u16_t acked;
+
+ u16_t snd_buf; /* Available buffer space for sending (in bytes). */
+#define TCP_SNDQUEUELEN_OVERFLOW (0xffffU-3)
+ u16_t snd_queuelen; /* Available buffer space for sending (in pbufs). */
+
+#if TCP_OVERSIZE
+ /* Extra bytes available at the end of the last pbuf in unsent. */
+ u16_t unsent_oversize;
+#endif /* TCP_OVERSIZE */
+
+ /* These are ordered by sequence number: */
+ struct tcp_seg *unsent; /* Unsent (queued) segments. */
+ struct tcp_seg *unacked; /* Sent but unacknowledged segments. */
+#if TCP_QUEUE_OOSEQ
+ struct tcp_seg *ooseq; /* Received out of sequence segments. */
+#endif /* TCP_QUEUE_OOSEQ */
+
+ struct pbuf *refused_data; /* Data previously received but not yet taken by upper layer */
+
+#if LWIP_CALLBACK_API
+ /* Function to be called when more send buffer space is available. */
+ tcp_sent_fn sent;
+ /* Function to be called when (in-sequence) data has arrived. */
+ tcp_recv_fn recv;
+ /* Function to be called when a connection has been set up. */
+ tcp_connected_fn connected;
+ /* Function which is called periodically. */
+ tcp_poll_fn poll;
+ /* Function to be called whenever a fatal error occurs. */
+ tcp_err_fn errf;
+#endif /* LWIP_CALLBACK_API */
+
+#if LWIP_TCP_TIMESTAMPS
+ u32_t ts_lastacksent;
+ u32_t ts_recent;
+#endif /* LWIP_TCP_TIMESTAMPS */
+
+ /* idle time before KEEPALIVE is sent */
+ u32_t keep_idle;
+#if LWIP_TCP_KEEPALIVE
+ u32_t keep_intvl;
+ u32_t keep_cnt;
+#endif /* LWIP_TCP_KEEPALIVE */
+
+ /* Persist timer counter */
+ u8_t persist_cnt;
+ /* Persist timer back-off */
+ u8_t persist_backoff;
+
+ /* KEEPALIVE counter */
+ u8_t keep_cnt_sent;
+};
+
+struct tcp_pcb_listen {
+/* Common members of all PCB types */
+ IP_PCB;
+/* Protocol specific PCB members */
+ TCP_PCB_COMMON(struct tcp_pcb_listen);
+
+#if TCP_LISTEN_BACKLOG
+ u8_t backlog;
+ u8_t accepts_pending;
+#endif /* TCP_LISTEN_BACKLOG */
+#if LWIP_IPV6
+ u8_t accept_any_ip_version;
+#endif /* LWIP_IPV6 */
+#if LWIP_CONNECTION_PROXY
+ u8_t accept_on_syn;
+#endif
+};
+
+#if LWIP_EVENT_API
+
+enum lwip_event {
+ LWIP_EVENT_ACCEPT,
+ LWIP_EVENT_SENT,
+ LWIP_EVENT_RECV,
+ LWIP_EVENT_CONNECTED,
+ LWIP_EVENT_POLL,
+ LWIP_EVENT_ERR
+};
+
+err_t lwip_tcp_event(void *arg, struct tcp_pcb *pcb,
+ enum lwip_event,
+ struct pbuf *p,
+ u16_t size,
+ err_t err);
+
+#endif /* LWIP_EVENT_API */
+
+/* Application program's interface: */
+struct tcp_pcb * tcp_new (void);
+
+void tcp_arg (struct tcp_pcb *pcb, void *arg);
+void tcp_accept (struct tcp_pcb *pcb, tcp_accept_fn accept);
+#if LWIP_CONNECTION_PROXY
+/* when proxied connection is accepted there's no listening pcb */
+void tcp_proxy_arg(void *arg);
+void tcp_proxy_accept(tcp_accept_syn_fn accept);
+/* but we also provide proxy-like early accept for listening pcbs */
+void tcp_accept_syn(struct tcp_pcb *lpcb, tcp_accept_syn_fn accept);
+#endif
+void tcp_recv (struct tcp_pcb *pcb, tcp_recv_fn recv);
+void tcp_sent (struct tcp_pcb *pcb, tcp_sent_fn sent);
+void tcp_poll (struct tcp_pcb *pcb, tcp_poll_fn poll, u8_t interval);
+void tcp_err (struct tcp_pcb *pcb, tcp_err_fn err);
+
+#define tcp_mss(pcb) (((pcb)->flags & TF_TIMESTAMP) ? ((pcb)->mss - 12) : (pcb)->mss)
+#define tcp_sndbuf(pcb) ((pcb)->snd_buf)
+#define tcp_sndqueuelen(pcb) ((pcb)->snd_queuelen)
+#define tcp_nagle_disable(pcb) ((pcb)->flags |= TF_NODELAY)
+#define tcp_nagle_enable(pcb) ((pcb)->flags &= ~TF_NODELAY)
+#define tcp_nagle_disabled(pcb) (((pcb)->flags & TF_NODELAY) != 0)
+
+#if TCP_LISTEN_BACKLOG
+#define tcp_accepted(pcb) do { \
+ LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", pcb->state == LISTEN); \
+ (((struct tcp_pcb_listen *)(pcb))->accepts_pending--); } while(0)
+#else /* TCP_LISTEN_BACKLOG */
+#define tcp_accepted(pcb) do { \
+ LWIP_ASSERT("pcb->state == LISTEN (called for wrong pcb?)", pcb->state == LISTEN); \
+ LWIP_UNUSED_ARG(pcb); } while(0)
+#endif /* TCP_LISTEN_BACKLOG */
+
+void tcp_recved (struct tcp_pcb *pcb, u16_t len);
+err_t tcp_bind (struct tcp_pcb *pcb, ip_addr_t *ipaddr,
+ u16_t port);
+#if LWIP_CONNECTION_PROXY
+err_t tcp_proxy_bind(struct tcp_pcb *pcb, ip_addr_t *ipaddr,
+ u16_t port);
+#endif /* LWIP_CONNECTION_PROXY */
+err_t tcp_connect (struct tcp_pcb *pcb, ip_addr_t *ipaddr,
+ u16_t port, tcp_connected_fn connected);
+
+struct tcp_pcb * tcp_listen_with_backlog(struct tcp_pcb *pcb, u8_t backlog);
+#define tcp_listen(pcb) tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG)
+
+#if LWIP_CONNECTION_PROXY
+err_t tcp_proxy_accept_confirm(struct tcp_pcb *npcb);
+#endif
+
+void tcp_abort (struct tcp_pcb *pcb);
+err_t tcp_close (struct tcp_pcb *pcb);
+err_t tcp_shutdown(struct tcp_pcb *pcb, int shut_rx, int shut_tx);
+
+/* Flags for "apiflags" parameter in tcp_write */
+#define TCP_WRITE_FLAG_COPY 0x01
+#define TCP_WRITE_FLAG_MORE 0x02
+
+err_t tcp_write (struct tcp_pcb *pcb, const void *dataptr, u16_t len,
+ u8_t apiflags);
+
+void tcp_setprio (struct tcp_pcb *pcb, u8_t prio);
+
+#define TCP_PRIO_MIN 1
+#define TCP_PRIO_NORMAL 64
+#define TCP_PRIO_MAX 127
+
+err_t tcp_output (struct tcp_pcb *pcb);
+
+
+const char* tcp_debug_state_str(enum tcp_state s);
+
+#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 */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_TCP */
+
+#endif /* __LWIP_TCP_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/tcp_impl.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/tcp_impl.h
new file mode 100644
index 00000000..dc57b53e
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/tcp_impl.h
@@ -0,0 +1,520 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_TCP_IMPL_H__
+#define __LWIP_TCP_IMPL_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_TCP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/tcp.h"
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/ip.h"
+#include "lwip/icmp.h"
+#include "lwip/err.h"
+#include "lwip/ip6.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Functions for interfacing with TCP: */
+
+/* Lower layer interface to TCP: */
+void tcp_init (void); /* Initialize this module. */
+void tcp_tmr (void); /* Must be called every
+ TCP_TMR_INTERVAL
+ ms. (Typically 250 ms). */
+/* It is also possible to call these two functions at the right
+ intervals (instead of calling tcp_tmr()). */
+void tcp_slowtmr (void);
+void tcp_fasttmr (void);
+
+
+/* Only used by IP to pass a TCP segment to TCP: */
+void tcp_input (struct pbuf *p, struct netif *inp);
+#if LWIP_CONNECTION_PROXY
+void tcp_proxy_input(struct pbuf *p, struct netif *inp);
+#endif
+/* Used within the TCP code only: */
+struct tcp_pcb * tcp_alloc (u8_t prio);
+void tcp_abandon (struct tcp_pcb *pcb, int reset);
+err_t tcp_send_empty_ack(struct tcp_pcb *pcb);
+void tcp_rexmit (struct tcp_pcb *pcb);
+void tcp_rexmit_rto (struct tcp_pcb *pcb);
+void tcp_rexmit_fast (struct tcp_pcb *pcb);
+u32_t tcp_update_rcv_ann_wnd(struct tcp_pcb *pcb);
+err_t tcp_process_refused_data(struct tcp_pcb *pcb);
+
+/**
+ * This is the Nagle algorithm: try to combine user data to send as few TCP
+ * segments as possible. Only send if
+ * - no previously transmitted data on the connection remains unacknowledged or
+ * - the TF_NODELAY flag is set (nagle algorithm turned off for this pcb) or
+ * - the only unsent segment is at least pcb->mss bytes long (or there is more
+ * than one unsent segment - with lwIP, this can happen although unsent->len < mss)
+ * - or if we are in fast-retransmit (TF_INFR)
+ */
+#define tcp_do_output_nagle(tpcb) ((((tpcb)->unacked == NULL) || \
+ ((tpcb)->flags & (TF_NODELAY | TF_INFR)) || \
+ (((tpcb)->unsent != NULL) && (((tpcb)->unsent->next != NULL) || \
+ ((tpcb)->unsent->len >= (tpcb)->mss))) || \
+ ((tcp_sndbuf(tpcb) == 0) || (tcp_sndqueuelen(tpcb) >= TCP_SND_QUEUELEN)) \
+ ) ? 1 : 0)
+#define tcp_output_nagle(tpcb) (tcp_do_output_nagle(tpcb) ? tcp_output(tpcb) : ERR_OK)
+
+
+#define TCP_SEQ_LT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) < 0)
+#define TCP_SEQ_LEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) <= 0)
+#define TCP_SEQ_GT(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) > 0)
+#define TCP_SEQ_GEQ(a,b) ((s32_t)((u32_t)(a) - (u32_t)(b)) >= 0)
+/* is b<=a<=c? */
+#if 0 /* see bug #10548 */
+#define TCP_SEQ_BETWEEN(a,b,c) ((c)-(b) >= (a)-(b))
+#endif
+#define TCP_SEQ_BETWEEN(a,b,c) (TCP_SEQ_GEQ(a,b) && TCP_SEQ_LEQ(a,c))
+#define TCP_FIN 0x01U
+#define TCP_SYN 0x02U
+#define TCP_RST 0x04U
+#define TCP_PSH 0x08U
+#define TCP_ACK 0x10U
+#define TCP_URG 0x20U
+#define TCP_ECE 0x40U
+#define TCP_CWR 0x80U
+
+#define TCP_FLAGS 0x3fU
+
+/* Length of the TCP header, excluding options. */
+#define TCP_HLEN 20
+
+#ifndef TCP_TMR_INTERVAL
+#define TCP_TMR_INTERVAL 250 /* The TCP timer interval in milliseconds. */
+#endif /* TCP_TMR_INTERVAL */
+
+#ifndef TCP_FAST_INTERVAL
+#define TCP_FAST_INTERVAL TCP_TMR_INTERVAL /* the fine grained timeout in milliseconds */
+#endif /* TCP_FAST_INTERVAL */
+
+#ifndef TCP_SLOW_INTERVAL
+#define TCP_SLOW_INTERVAL (2*TCP_TMR_INTERVAL) /* the coarse grained timeout in milliseconds */
+#endif /* TCP_SLOW_INTERVAL */
+
+#define TCP_FIN_WAIT_TIMEOUT 20000 /* milliseconds */
+#define TCP_SYN_RCVD_TIMEOUT 20000 /* milliseconds */
+
+#define TCP_OOSEQ_TIMEOUT 6U /* x RTO */
+
+#ifndef TCP_MSL
+#define TCP_MSL 60000UL /* The maximum segment lifetime in milliseconds */
+#endif
+
+/* Keepalive values, compliant with RFC 1122. Don't change this unless you know what you're doing */
+#ifndef TCP_KEEPIDLE_DEFAULT
+#define TCP_KEEPIDLE_DEFAULT 7200000UL /* Default KEEPALIVE timer in milliseconds */
+#endif
+
+#ifndef TCP_KEEPINTVL_DEFAULT
+#define TCP_KEEPINTVL_DEFAULT 75000UL /* Default Time between KEEPALIVE probes in milliseconds */
+#endif
+
+#ifndef TCP_KEEPCNT_DEFAULT
+#define TCP_KEEPCNT_DEFAULT 9U /* Default Counter for KEEPALIVE probes */
+#endif
+
+#define TCP_MAXIDLE TCP_KEEPCNT_DEFAULT * TCP_KEEPINTVL_DEFAULT /* Maximum KEEPALIVE probe time */
+
+/* Fields are (of course) in network byte order.
+ * Some fields are converted to host byte order in tcp_input().
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct tcp_hdr {
+ PACK_STRUCT_FIELD(u16_t src);
+ PACK_STRUCT_FIELD(u16_t dest);
+ PACK_STRUCT_FIELD(u32_t seqno);
+ PACK_STRUCT_FIELD(u32_t ackno);
+ PACK_STRUCT_FIELD(u16_t _hdrlen_rsvd_flags);
+ PACK_STRUCT_FIELD(u16_t wnd);
+ PACK_STRUCT_FIELD(u16_t chksum);
+ PACK_STRUCT_FIELD(u16_t urgp);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define TCPH_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))
+
+/** Flags used on input processing, not on pcb->flags
+*/
+#define TF_RESET (u8_t)0x08U /* Connection was reset. */
+#define TF_CLOSED (u8_t)0x10U /* Connection was sucessfully closed. */
+#define TF_GOT_FIN (u8_t)0x20U /* Connection was closed by the remote end. */
+
+
+#if LWIP_EVENT_API
+
+#define TCP_EVENT_ACCEPT(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+ LWIP_EVENT_ACCEPT, NULL, 0, err)
+#define TCP_EVENT_SENT(pcb,space,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+ LWIP_EVENT_SENT, NULL, space, ERR_OK)
+#define TCP_EVENT_RECV(pcb,p,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+ LWIP_EVENT_RECV, (p), 0, (err))
+#define TCP_EVENT_CLOSED(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+ LWIP_EVENT_RECV, NULL, 0, ERR_OK)
+#define TCP_EVENT_CONNECTED(pcb,err,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+ LWIP_EVENT_CONNECTED, NULL, 0, (err))
+#define TCP_EVENT_POLL(pcb,ret) ret = lwip_tcp_event((pcb)->callback_arg, (pcb),\
+ LWIP_EVENT_POLL, NULL, 0, ERR_OK)
+#define TCP_EVENT_ERR(errf,arg,err) lwip_tcp_event((arg), NULL, \
+ LWIP_EVENT_ERR, NULL, 0, (err))
+
+#else /* LWIP_EVENT_API */
+
+#define TCP_EVENT_ACCEPT(pcb,err,ret) \
+ do { \
+ if((pcb)->accept != NULL) \
+ (ret) = (pcb)->accept((pcb)->callback_arg,(pcb),(err)); \
+ else (ret) = ERR_ARG; \
+ } while (0)
+
+#define TCP_EVENT_SENT(pcb,space,ret) \
+ do { \
+ if((pcb)->sent != NULL) \
+ (ret) = (pcb)->sent((pcb)->callback_arg,(pcb),(space)); \
+ else (ret) = ERR_OK; \
+ } while (0)
+
+#define TCP_EVENT_RECV(pcb,p,err,ret) \
+ do { \
+ if((pcb)->recv != NULL) { \
+ (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),(p),(err));\
+ } else { \
+ (ret) = tcp_recv_null(NULL, (pcb), (p), (err)); \
+ } \
+ } while (0)
+
+#define TCP_EVENT_CLOSED(pcb,ret) \
+ do { \
+ if(((pcb)->recv != NULL)) { \
+ (ret) = (pcb)->recv((pcb)->callback_arg,(pcb),NULL,ERR_OK);\
+ } else { \
+ (ret) = ERR_OK; \
+ } \
+ } while (0)
+
+#define TCP_EVENT_CONNECTED(pcb,err,ret) \
+ do { \
+ if((pcb)->connected != NULL) \
+ (ret) = (pcb)->connected((pcb)->callback_arg,(pcb),(err)); \
+ else (ret) = ERR_OK; \
+ } while (0)
+
+#define TCP_EVENT_POLL(pcb,ret) \
+ do { \
+ if((pcb)->poll != NULL) \
+ (ret) = (pcb)->poll((pcb)->callback_arg,(pcb)); \
+ else (ret) = ERR_OK; \
+ } while (0)
+
+#define TCP_EVENT_ERR(errf,arg,err) \
+ do { \
+ if((errf) != NULL) \
+ (errf)((arg),(err)); \
+ } while (0)
+
+#endif /* LWIP_EVENT_API */
+
+/** Enabled extra-check for TCP_OVERSIZE if LWIP_DEBUG is enabled */
+#if TCP_OVERSIZE && defined(LWIP_DEBUG)
+#define TCP_OVERSIZE_DBGCHECK 1
+#else
+#define TCP_OVERSIZE_DBGCHECK 0
+#endif
+
+/** Don't generate checksum on copy if CHECKSUM_GEN_TCP is disabled */
+#define TCP_CHECKSUM_ON_COPY (LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_TCP)
+
+/* This structure represents a TCP segment on the unsent, unacked and ooseq queues */
+struct tcp_seg {
+ struct tcp_seg *next; /* used when putting segements on a queue */
+ struct pbuf *p; /* buffer containing data + TCP header */
+ u16_t len; /* the TCP length of this segment */
+#if TCP_OVERSIZE_DBGCHECK
+ u16_t oversize_left; /* Extra bytes available at the end of the last
+ pbuf in unsent (used for asserting vs.
+ tcp_pcb.unsent_oversized only) */
+#endif /* TCP_OVERSIZE_DBGCHECK */
+#if TCP_CHECKSUM_ON_COPY
+ u16_t chksum;
+ u8_t chksum_swapped;
+#endif /* TCP_CHECKSUM_ON_COPY */
+ u8_t flags;
+#define TF_SEG_OPTS_MSS (u8_t)0x01U /* Include MSS option. */
+#define TF_SEG_OPTS_TS (u8_t)0x02U /* Include timestamp option. */
+#define TF_SEG_DATA_CHECKSUMMED (u8_t)0x04U /* ALL data (not the header) is
+ checksummed into 'chksum' */
+ struct tcp_hdr *tcphdr; /* the TCP header */
+};
+
+#define LWIP_TCP_OPT_LENGTH(flags) \
+ (flags & TF_SEG_OPTS_MSS ? 4 : 0) + \
+ (flags & TF_SEG_OPTS_TS ? 12 : 0)
+
+/** This returns a TCP header option for MSS in an u32_t */
+#define TCP_BUILD_MSS_OPTION(mss) htonl(0x02040000 | ((mss) & 0xFFFF))
+
+/* Global variables: */
+extern struct tcp_pcb *tcp_input_pcb;
+extern u32_t tcp_ticks;
+extern u8_t tcp_active_pcbs_changed;
+
+#if LWIP_CONNECTION_PROXY
+extern tcp_accept_syn_fn tcp_proxy_accept_callback;
+#endif
+
+/* The TCP PCB lists. */
+union tcp_listen_pcbs_t { /* List of all TCP PCBs in LISTEN state. */
+ struct tcp_pcb_listen *listen_pcbs;
+ struct tcp_pcb *pcbs;
+};
+extern struct tcp_pcb *tcp_bound_pcbs;
+extern union tcp_listen_pcbs_t tcp_listen_pcbs;
+extern struct tcp_pcb *tcp_active_pcbs; /* List of all TCP PCBs that are in a
+ state in which they accept or send
+ data. */
+extern struct tcp_pcb *tcp_tw_pcbs; /* List of all TCP PCBs in TIME-WAIT. */
+
+extern struct tcp_pcb *tcp_tmp_pcb; /* Only used for temporary storage. */
+
+/* Axioms about the above lists:
+ 1) Every TCP PCB that is not CLOSED is in one of the lists.
+ 2) A PCB is only in one of the lists.
+ 3) All PCBs in the tcp_listen_pcbs list is in LISTEN state.
+ 4) All PCBs in the tcp_tw_pcbs list is in TIME-WAIT state.
+*/
+/* Define two macros, TCP_REG and TCP_RMV that registers a TCP PCB
+ with a PCB list or removes a PCB from a list, respectively. */
+#ifndef TCP_DEBUG_PCB_LISTS
+#define TCP_DEBUG_PCB_LISTS 0
+#endif
+#if TCP_DEBUG_PCB_LISTS
+#define TCP_REG(pcbs, npcb) do {\
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP_REG %p local port %d\n", (npcb), (npcb)->local_port)); \
+ for(tcp_tmp_pcb = *(pcbs); \
+ tcp_tmp_pcb != NULL; \
+ tcp_tmp_pcb = tcp_tmp_pcb->next) { \
+ LWIP_ASSERT("TCP_REG: already registered\n", tcp_tmp_pcb != (npcb)); \
+ } \
+ LWIP_ASSERT("TCP_REG: pcb->state != CLOSED", ((pcbs) == &tcp_bound_pcbs) || ((npcb)->state != CLOSED)); \
+ (npcb)->next = *(pcbs); \
+ LWIP_ASSERT("TCP_REG: npcb->next != npcb", (npcb)->next != (npcb)); \
+ *(pcbs) = (npcb); \
+ LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \
+ tcp_timer_needed(); \
+ } while(0)
+#define TCP_RMV(pcbs, npcb) do { \
+ LWIP_ASSERT("TCP_RMV: pcbs != NULL", *(pcbs) != NULL); \
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removing %p from %p\n", (npcb), *(pcbs))); \
+ if(*(pcbs) == (npcb)) { \
+ *(pcbs) = (*pcbs)->next; \
+ } else for(tcp_tmp_pcb = *(pcbs); tcp_tmp_pcb != NULL; tcp_tmp_pcb = tcp_tmp_pcb->next) { \
+ if(tcp_tmp_pcb->next == (npcb)) { \
+ tcp_tmp_pcb->next = (npcb)->next; \
+ break; \
+ } \
+ } \
+ (npcb)->next = NULL; \
+ LWIP_ASSERT("TCP_RMV: tcp_pcbs sane", tcp_pcbs_sane()); \
+ LWIP_DEBUGF(TCP_DEBUG, ("TCP_RMV: removed %p from %p\n", (npcb), *(pcbs))); \
+ } while(0)
+
+#else /* LWIP_DEBUG */
+
+#define TCP_REG(pcbs, npcb) \
+ do { \
+ (npcb)->next = *pcbs; \
+ *(pcbs) = (npcb); \
+ tcp_timer_needed(); \
+ } while (0)
+
+#define TCP_RMV(pcbs, npcb) \
+ do { \
+ if(*(pcbs) == (npcb)) { \
+ (*(pcbs)) = (*pcbs)->next; \
+ } \
+ else { \
+ for(tcp_tmp_pcb = *pcbs; \
+ tcp_tmp_pcb != NULL; \
+ tcp_tmp_pcb = tcp_tmp_pcb->next) { \
+ if(tcp_tmp_pcb->next == (npcb)) { \
+ tcp_tmp_pcb->next = (npcb)->next; \
+ break; \
+ } \
+ } \
+ } \
+ (npcb)->next = NULL; \
+ } while(0)
+
+#endif /* LWIP_DEBUG */
+
+#define TCP_REG_ACTIVE(npcb) \
+ do { \
+ TCP_REG(&tcp_active_pcbs, npcb); \
+ tcp_active_pcbs_changed = 1; \
+ } while (0)
+
+#define TCP_RMV_ACTIVE(npcb) \
+ do { \
+ TCP_RMV(&tcp_active_pcbs, npcb); \
+ tcp_active_pcbs_changed = 1; \
+ } while (0)
+
+#define TCP_PCB_REMOVE_ACTIVE(pcb) \
+ do { \
+ tcp_pcb_remove(&tcp_active_pcbs, pcb); \
+ tcp_active_pcbs_changed = 1; \
+ } while (0)
+
+
+/* Internal functions: */
+struct tcp_pcb *tcp_pcb_copy(struct tcp_pcb *pcb);
+void tcp_pcb_purge(struct tcp_pcb *pcb);
+void tcp_pcb_remove(struct tcp_pcb **pcblist, struct tcp_pcb *pcb);
+
+void tcp_segs_free(struct tcp_seg *seg);
+void tcp_seg_free(struct tcp_seg *seg);
+struct tcp_seg *tcp_seg_copy(struct tcp_seg *seg);
+
+#define tcp_ack(pcb) \
+ do { \
+ if((pcb)->flags & TF_ACK_DELAY) { \
+ (pcb)->flags &= ~TF_ACK_DELAY; \
+ (pcb)->flags |= TF_ACK_NOW; \
+ } \
+ else { \
+ (pcb)->flags |= TF_ACK_DELAY; \
+ } \
+ } while (0)
+
+#define tcp_ack_now(pcb) \
+ do { \
+ (pcb)->flags |= TF_ACK_NOW; \
+ } while (0)
+
+err_t tcp_send_fin(struct tcp_pcb *pcb);
+err_t tcp_enqueue_flags(struct tcp_pcb *pcb, u8_t flags);
+
+void tcp_rexmit_seg(struct tcp_pcb *pcb, struct tcp_seg *seg);
+
+void tcp_rst_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);
+
+#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 */
+#endif /* TCP_CALCULATE_EFF_SEND_MSS */
+
+#if LWIP_CALLBACK_API
+err_t tcp_accept_null(void *arg, struct tcp_pcb *pcb, err_t err);
+err_t tcp_recv_null(void *arg, struct tcp_pcb *pcb, struct pbuf *p, err_t err);
+#endif /* LWIP_CALLBACK_API */
+
+#if LWIP_CONNECTION_PROXY
+err_t tcp_accept_syn_null(void *arg, struct tcp_pcb *newpcb, struct pbuf *syn);
+#endif /* LWIP_CONNECTION_PROXY */
+
+#if TCP_DEBUG || TCP_INPUT_DEBUG || TCP_OUTPUT_DEBUG
+void tcp_debug_print(struct tcp_hdr *tcphdr);
+void tcp_debug_print_flags(u8_t flags);
+void tcp_debug_print_state(enum tcp_state s);
+void tcp_debug_print_pcbs(void);
+s16_t tcp_pcbs_sane(void);
+#else
+# define tcp_debug_print(tcphdr)
+# define tcp_debug_print_flags(flags)
+# define tcp_debug_print_state(s)
+# define tcp_debug_print_pcbs()
+# define tcp_pcbs_sane() 1
+#endif /* TCP_DEBUG */
+
+/** External function (implemented in timers.c), called when TCP detects
+ * that a timer is needed (i.e. active- or time-wait-pcb found). */
+void tcp_timer_needed(void);
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_TCP */
+
+#endif /* __LWIP_TCP_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/tcpip.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/tcpip.h
new file mode 100644
index 00000000..ed2c6fb7
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/tcpip.h
@@ -0,0 +1,186 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_TCPIP_H__
+#define __LWIP_TCPIP_H__
+
+#include "lwip/opt.h"
+
+#if !NO_SYS /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/api_msg.h"
+#include "lwip/netifapi.h"
+#include "lwip/pbuf.h"
+#include "lwip/api.h"
+#include "lwip/sys.h"
+#include "lwip/timers.h"
+#include "lwip/netif.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/** Define this to something that triggers a watchdog. This is called from
+ * tcpip_thread after processing a message. */
+#ifndef LWIP_TCPIP_THREAD_ALIVE
+#define LWIP_TCPIP_THREAD_ALIVE()
+#endif
+
+#if LWIP_TCPIP_CORE_LOCKING
+/** The global semaphore to lock the stack. */
+extern sys_mutex_t lock_tcpip_core;
+#define LOCK_TCPIP_CORE() sys_mutex_lock(&lock_tcpip_core)
+#define UNLOCK_TCPIP_CORE() sys_mutex_unlock(&lock_tcpip_core)
+#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 */
+
+/** Function prototype for the init_done function passed to tcpip_init */
+typedef void (*tcpip_init_done_fn)(void *arg);
+/** Function prototype for functions passed to tcpip_callback() */
+typedef void (*tcpip_callback_fn)(void *ctx);
+
+/* Forward declarations */
+struct tcpip_callback_msg;
+
+void tcpip_init(tcpip_init_done_fn tcpip_init_done, void *arg);
+
+#if LWIP_NETCONN
+err_t tcpip_apimsg(struct api_msg *apimsg);
+#endif /* LWIP_NETCONN */
+
+err_t tcpip_input(struct pbuf *p, struct netif *inp);
+
+#if LWIP_NETIF_API
+err_t tcpip_netifapi(struct netifapi_msg *netifapimsg);
+#if LWIP_TCPIP_CORE_LOCKING
+err_t tcpip_netifapi_lock(struct netifapi_msg *netifapimsg);
+#endif /* LWIP_TCPIP_CORE_LOCKING */
+#endif /* LWIP_NETIF_API */
+
+err_t tcpip_callback_with_block(tcpip_callback_fn function, void *ctx, u8_t block);
+#define tcpip_callback(f, ctx) tcpip_callback_with_block(f, ctx, 1)
+
+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);
+
+err_t tcpip_callbackmsg(struct tcpip_callback_msg* msg);
+#define tcpip_trycallbackmsg(msg) (tcpip_trycallback(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
+#ifdef VBOX
+ /* like CALLBACK_STATIC, but then makes tcpip_thread() return */
+ , TCPIP_MSG_CALLBACK_TERMINATE
+#endif
+};
+
+struct tcpip_msg {
+ enum tcpip_msg_type type;
+ sys_sem_t *sem;
+ union {
+#if LWIP_NETCONN
+ struct api_msg *apimsg;
+#endif /* LWIP_NETCONN */
+#if LWIP_NETIF_API
+ struct netifapi_msg *netifapimsg;
+#endif /* LWIP_NETIF_API */
+ struct {
+ struct pbuf *p;
+ struct netif *netif;
+ } inp;
+ struct {
+ tcpip_callback_fn function;
+ void *ctx;
+ } cb;
+#if LWIP_TCPIP_TIMEOUT
+ struct {
+ u32_t msecs;
+ sys_timeout_handler h;
+ void *arg;
+ } tmo;
+#endif /* LWIP_TCPIP_TIMEOUT */
+ } msg;
+};
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* !NO_SYS */
+
+#endif /* __LWIP_TCPIP_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/timers.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/timers.h
new file mode 100644
index 00000000..04e78e0f
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/timers.h
@@ -0,0 +1,100 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ * Simon Goldschmidt
+ *
+ */
+#ifndef __LWIP_TIMERS_H__
+#define __LWIP_TIMERS_H__
+
+#include "lwip/opt.h"
+
+/* Timers are not supported when NO_SYS==1 and NO_SYS_NO_TIMERS==1 */
+#define LWIP_TIMERS (!NO_SYS || (NO_SYS && !NO_SYS_NO_TIMERS))
+
+#if LWIP_TIMERS
+
+#include "lwip/err.h"
+#if !NO_SYS
+#include "lwip/sys.h"
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#ifndef LWIP_DEBUG_TIMERNAMES
+#ifdef LWIP_DEBUG
+#define LWIP_DEBUG_TIMERNAMES SYS_DEBUG
+#else /* LWIP_DEBUG */
+#define LWIP_DEBUG_TIMERNAMES 0
+#endif /* LWIP_DEBUG*/
+#endif
+
+/** Function prototype for a timeout callback function. Register such a function
+ * using sys_timeout().
+ *
+ * @param arg Additional argument to pass to the function - set up by sys_timeout()
+ */
+typedef void (* sys_timeout_handler)(void *arg);
+
+struct sys_timeo {
+ struct sys_timeo *next;
+ u32_t time;
+ sys_timeout_handler h;
+ void *arg;
+#if LWIP_DEBUG_TIMERNAMES
+ const char* handler_name;
+#endif /* LWIP_DEBUG_TIMERNAMES */
+};
+
+void sys_timeouts_init(void);
+
+#if LWIP_DEBUG_TIMERNAMES
+void sys_timeout_debug(u32_t msecs, sys_timeout_handler handler, void *arg, const char* handler_name);
+#define sys_timeout(msecs, handler, arg) sys_timeout_debug(msecs, handler, arg, #handler)
+#else /* LWIP_DEBUG_TIMERNAMES */
+void sys_timeout(u32_t msecs, sys_timeout_handler handler, void *arg);
+#endif /* LWIP_DEBUG_TIMERNAMES */
+
+void sys_untimeout(sys_timeout_handler handler, void *arg);
+#if NO_SYS
+void sys_check_timeouts(void);
+void sys_restart_timeouts(void);
+#else /* NO_SYS */
+void sys_timeouts_mbox_fetch(sys_mbox_t *mbox, void **msg);
+#endif /* NO_SYS */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_TIMERS */
+#endif /* __LWIP_TIMERS_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/lwip/udp.h b/src/VBox/Devices/Network/lwip-new/src/include/lwip/udp.h
new file mode 100644
index 00000000..69ac2496
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/lwip/udp.h
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __LWIP_UDP_H__
+#define __LWIP_UDP_H__
+
+#include "lwip/opt.h"
+
+#if LWIP_UDP /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/pbuf.h"
+#include "lwip/netif.h"
+#include "lwip/ip_addr.h"
+#include "lwip/ip.h"
+#include "lwip/ip6_addr.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define UDP_HLEN 8
+
+/* Fields are (of course) in network byte order. */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct udp_hdr {
+ PACK_STRUCT_FIELD(u16_t src);
+ PACK_STRUCT_FIELD(u16_t dest); /* src/dest UDP ports */
+ PACK_STRUCT_FIELD(u16_t len);
+ PACK_STRUCT_FIELD(u16_t chksum);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#define UDP_FLAGS_NOCHKSUM 0x01U
+#define UDP_FLAGS_UDPLITE 0x02U
+#define UDP_FLAGS_CONNECTED 0x04U
+#define UDP_FLAGS_MULTICAST_LOOP 0x08U
+
+struct udp_pcb;
+
+/** Function prototype for udp pcb receive callback functions
+ * addr and port are in same byte order as in the pcb
+ * The callback is responsible for freeing the pbuf
+ * if it's not used any more.
+ *
+ * ATTENTION: Be aware that 'addr' points into the pbuf 'p' so freeing this pbuf
+ * makes 'addr' invalid, too.
+ *
+ * @param arg user supplied argument (udp_pcb.recv_arg)
+ * @param pcb the udp_pcb which received data
+ * @param p the packet buffer that was received
+ * @param addr the remote IP address from which the packet was received
+ * @param port the remote port from which the packet was received
+ */
+typedef void (*udp_recv_fn)(void *arg, struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *addr, u16_t port);
+
+#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 */
+
+struct udp_pcb {
+/* Common members of all PCB types */
+ IP_PCB;
+
+/* Protocol specific PCB members */
+
+ struct udp_pcb *next;
+
+ u8_t flags;
+#if LWIP_CONNECTION_PROXY
+ u8_t proxy_cnt;
+#endif
+ /** ports are in host byte order */
+ u16_t local_port, remote_port;
+
+#if LWIP_IGMP
+ /** outgoing network interface for multicast packets */
+ ip_addr_t multicast_ip;
+#endif /* LWIP_IGMP */
+
+#if LWIP_UDPLITE
+ /** used for UDP_LITE only */
+ u16_t chksum_len_rx, chksum_len_tx;
+#endif /* LWIP_UDPLITE */
+
+ /** receive callback function */
+ union {
+ udp_recv_fn ip4;
+ UDP_PCB_RECV_IP6
+ }recv;
+ /** user-supplied argument for the recv callback */
+ void *recv_arg;
+};
+/* udp_pcbs export for exernal reference (e.g. SNMP agent) */
+extern struct udp_pcb *udp_pcbs;
+#if LWIP_CONNECTION_PROXY
+extern struct udp_pcb *udp_proxy_pcbs;
+#endif
+
+/* The following functions is the application layer interface to the
+ UDP code. */
+struct udp_pcb * udp_new (void);
+void udp_remove (struct udp_pcb *pcb);
+err_t udp_bind (struct udp_pcb *pcb, ip_addr_t *ipaddr,
+ u16_t port);
+err_t udp_connect (struct udp_pcb *pcb, ip_addr_t *ipaddr,
+ u16_t port);
+void udp_disconnect (struct udp_pcb *pcb);
+void udp_recv (struct udp_pcb *pcb, udp_recv_fn recv,
+ void *recv_arg);
+err_t udp_sendto_if (struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *dst_ip, u16_t dst_port,
+ struct netif *netif);
+err_t udp_sendto (struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *dst_ip, u16_t dst_port);
+err_t udp_send (struct udp_pcb *pcb, struct pbuf *p);
+
+#if LWIP_CHECKSUM_ON_COPY && 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,
+ struct netif *netif, u8_t have_chksum,
+ u16_t chksum);
+err_t udp_sendto_chksum(struct udp_pcb *pcb, struct pbuf *p,
+ ip_addr_t *dst_ip, u16_t dst_port,
+ u8_t have_chksum, u16_t chksum);
+err_t udp_send_chksum(struct udp_pcb *pcb, struct pbuf *p,
+ u8_t have_chksum, u16_t chksum);
+#endif /* LWIP_CHECKSUM_ON_COPY && CHECKSUM_GEN_UDP */
+
+#define udp_flags(pcb) ((pcb)->flags)
+#define udp_setflags(pcb, f) ((pcb)->flags = (f))
+
+#if LWIP_CONNECTION_PROXY
+void udp_proxy_accept(udp_recv_fn accept);
+#endif
+
+/* The following functions are the lower layer interface to UDP. */
+void udp_input (struct pbuf *p, struct netif *inp);
+
+#if LWIP_CONNECTION_PROXY
+void udp_proxy_input(struct pbuf *p, struct netif *inp);
+
+/**
+ * Time in seconds a proxy udp pcb is kept alive without outgoing
+ * traffic.
+ *
+ * E.g. RFC 3948 (read "VPN") uses default NAT keepalive of 20
+ * seconds.
+ */
+#define UDP_PROXY_EXPIRATION 20 /* seconds */
+
+#define UDP_PROXY_TMR_INTERVAL 3000 /* milliseconds */
+void udp_proxy_timer_needed(void);
+void udp_proxy_tmr(void);
+#endif
+
+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 */
+
+#if UDP_DEBUG
+void udp_debug_print(struct udp_hdr *udphdr);
+#else
+#define udp_debug_print(udphdr)
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* LWIP_UDP */
+
+#endif /* __LWIP_UDP_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/netif/etharp.h b/src/VBox/Devices/Network/lwip-new/src/include/netif/etharp.h
new file mode 100644
index 00000000..77f4bc51
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/netif/etharp.h
@@ -0,0 +1,228 @@
+/*
+ * 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 */
+
+#if ARP_PROXY
+typedef int (*proxy_arp_hook_fn)(struct netif *netif, ip_addr_t *dipaddr);
+extern proxy_arp_hook_fn proxy_arp_hook;
+#endif /* ARP_PROXY */
+
+#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;
+
+#endif /* LWIP_ARP || LWIP_ETHERNET */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __NETIF_ARP_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/netif/ppp_oe.h b/src/VBox/Devices/Network/lwip-new/src/include/netif/ppp_oe.h
new file mode 100644
index 00000000..e1cdfa51
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/netif/ppp_oe.h
@@ -0,0 +1,190 @@
+/*****************************************************************************
+* ppp_oe.h - PPP Over Ethernet implementation for lwIP.
+*
+* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 06-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+*****************************************************************************/
+
+
+
+/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Martin Husemann <martin@NetBSD.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef PPP_OE_H
+#define PPP_OE_H
+
+#include "lwip/opt.h"
+
+#if PPPOE_SUPPORT > 0
+
+#include "netif/etharp.h"
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct pppoehdr {
+ PACK_STRUCT_FIELD(u8_t vertype);
+ PACK_STRUCT_FIELD(u8_t code);
+ PACK_STRUCT_FIELD(u16_t session);
+ PACK_STRUCT_FIELD(u16_t plen);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct pppoetag {
+ PACK_STRUCT_FIELD(u16_t tag);
+ PACK_STRUCT_FIELD(u16_t len);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+
+#define PPPOE_STATE_INITIAL 0
+#define PPPOE_STATE_PADI_SENT 1
+#define PPPOE_STATE_PADR_SENT 2
+#define PPPOE_STATE_SESSION 3
+#define PPPOE_STATE_CLOSING 4
+/* passive */
+#define PPPOE_STATE_PADO_SENT 1
+
+#define PPPOE_HEADERLEN sizeof(struct pppoehdr)
+#define PPPOE_VERTYPE 0x11 /* VER=1, TYPE = 1 */
+
+#define PPPOE_TAG_EOL 0x0000 /* end of list */
+#define PPPOE_TAG_SNAME 0x0101 /* service name */
+#define PPPOE_TAG_ACNAME 0x0102 /* access concentrator name */
+#define PPPOE_TAG_HUNIQUE 0x0103 /* host unique */
+#define PPPOE_TAG_ACCOOKIE 0x0104 /* AC cookie */
+#define PPPOE_TAG_VENDOR 0x0105 /* vendor specific */
+#define PPPOE_TAG_RELAYSID 0x0110 /* relay session id */
+#define PPPOE_TAG_SNAME_ERR 0x0201 /* service name error */
+#define PPPOE_TAG_ACSYS_ERR 0x0202 /* AC system error */
+#define PPPOE_TAG_GENERIC_ERR 0x0203 /* gerneric error */
+
+#define PPPOE_CODE_PADI 0x09 /* Active Discovery Initiation */
+#define PPPOE_CODE_PADO 0x07 /* Active Discovery Offer */
+#define PPPOE_CODE_PADR 0x19 /* Active Discovery Request */
+#define PPPOE_CODE_PADS 0x65 /* Active Discovery Session confirmation */
+#define PPPOE_CODE_PADT 0xA7 /* Active Discovery Terminate */
+
+#ifndef ETHERMTU
+#define ETHERMTU 1500
+#endif
+
+/* two byte PPP protocol discriminator, then IP data */
+#define PPPOE_MAXMTU (ETHERMTU-PPPOE_HEADERLEN-2)
+
+#ifndef PPPOE_MAX_AC_COOKIE_LEN
+#define PPPOE_MAX_AC_COOKIE_LEN 64
+#endif
+
+struct pppoe_softc {
+ struct pppoe_softc *next;
+ struct netif *sc_ethif; /* ethernet interface we are using */
+ int sc_pd; /* ppp unit number */
+ void (*sc_linkStatusCB)(int pd, int up);
+
+ int sc_state; /* discovery phase or session connected */
+ struct eth_addr sc_dest; /* hardware address of concentrator */
+ u16_t sc_session; /* PPPoE session id */
+
+#ifdef PPPOE_TODO
+ char *sc_service_name; /* if != NULL: requested name of service */
+ char *sc_concentrator_name; /* if != NULL: requested concentrator id */
+#endif /* PPPOE_TODO */
+ u8_t sc_ac_cookie[PPPOE_MAX_AC_COOKIE_LEN]; /* content of AC cookie we must echo back */
+ size_t sc_ac_cookie_len; /* length of cookie data */
+#ifdef PPPOE_SERVER
+ u8_t *sc_hunique; /* content of host unique we must echo back */
+ size_t sc_hunique_len; /* length of host unique */
+#endif
+ int sc_padi_retried; /* number of PADI retries already done */
+ int sc_padr_retried; /* number of PADR retries already done */
+};
+
+
+#define pppoe_init() /* compatibility define, no initialization needed */
+
+err_t pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr);
+err_t pppoe_destroy(struct netif *ifp);
+
+int pppoe_connect(struct pppoe_softc *sc);
+void pppoe_disconnect(struct pppoe_softc *sc);
+
+void pppoe_disc_input(struct netif *netif, struct pbuf *p);
+void pppoe_data_input(struct netif *netif, struct pbuf *p);
+
+err_t pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb);
+
+/** used in ppp.c */
+#define PPPOE_HDRLEN (sizeof(struct eth_hdr) + PPPOE_HEADERLEN)
+
+#endif /* PPPOE_SUPPORT */
+
+#endif /* PPP_OE_H */
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/netif/slipif.h b/src/VBox/Devices/Network/lwip-new/src/include/netif/slipif.h
new file mode 100644
index 00000000..7b6ce5e2
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/netif/slipif.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright (c) 2001, Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+#ifndef __NETIF_SLIPIF_H__
+#define __NETIF_SLIPIF_H__
+
+#include "lwip/opt.h"
+#include "lwip/netif.h"
+
+/** Set this to 1 to start a thread that blocks reading on the serial line
+ * (using sio_read()).
+ */
+#ifndef SLIP_USE_RX_THREAD
+#define SLIP_USE_RX_THREAD !NO_SYS
+#endif
+
+/** Set this to 1 to enable functions to pass in RX bytes from ISR context.
+ * If enabled, slipif_received_byte[s]() process incoming bytes and put assembled
+ * packets on a queue, which is fed into lwIP from slipif_poll().
+ * If disabled, slipif_poll() polls the serila line (using sio_tryread()).
+ */
+#ifndef SLIP_RX_FROM_ISR
+#define SLIP_RX_FROM_ISR 0
+#endif
+
+/** Set this to 1 (default for SLIP_RX_FROM_ISR) to queue incoming packets
+ * received by slipif_received_byte[s]() as long as PBUF_POOL pbufs are available.
+ * If disabled, packets will be dropped if more than one packet is received.
+ */
+#ifndef SLIP_RX_QUEUE
+#define SLIP_RX_QUEUE SLIP_RX_FROM_ISR
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+err_t slipif_init(struct netif * netif);
+void slipif_poll(struct netif *netif);
+#if SLIP_RX_FROM_ISR
+void slipif_process_rxqueue(struct netif *netif);
+void slipif_received_byte(struct netif *netif, u8_t data);
+void slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len);
+#endif /* SLIP_RX_FROM_ISR */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/posix/netdb.h b/src/VBox/Devices/Network/lwip-new/src/include/posix/netdb.h
new file mode 100644
index 00000000..7134032d
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/posix/netdb.h
@@ -0,0 +1,33 @@
+/**
+ * @file
+ * This file is a posix wrapper for lwip/netdb.h.
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/netdb.h"
diff --git a/src/VBox/Devices/Network/lwip-new/src/include/posix/sys/socket.h b/src/VBox/Devices/Network/lwip-new/src/include/posix/sys/socket.h
new file mode 100644
index 00000000..f7c7066e
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/include/posix/sys/socket.h
@@ -0,0 +1,33 @@
+/**
+ * @file
+ * This file is a posix wrapper for lwip/sockets.h.
+ */
+
+/*
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/sockets.h"
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/FILES b/src/VBox/Devices/Network/lwip-new/src/netif/FILES
new file mode 100644
index 00000000..099dbf3e
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/FILES
@@ -0,0 +1,29 @@
+This directory contains generic network interface device drivers that
+do not contain any hardware or architecture specific code. The files
+are:
+
+etharp.c
+ Implements the ARP (Address Resolution Protocol) over
+ Ethernet. The code in this file should be used together with
+ Ethernet device drivers. Note that this module has been
+ largely made Ethernet independent so you should be able to
+ adapt this for other link layers (such as Firewire).
+
+ethernetif.c
+ An example of how an Ethernet device driver could look. This
+ file can be used as a "skeleton" for developing new Ethernet
+ network device drivers. It uses the etharp.c ARP code.
+
+loopif.c
+ A "loopback" network interface driver. It requires configuration
+ through the define LWIP_LOOPIF_MULTITHREADING (see opt.h).
+
+slipif.c
+ A generic implementation of the SLIP (Serial Line IP)
+ protocol. It requires a sio (serial I/O) module to work.
+
+ppp/ Point-to-Point Protocol stack
+ The PPP stack has been ported from ucip (http://ucip.sourceforge.net).
+ It matches quite well to pppd 2.3.1 (http://ppp.samba.org), although
+ compared to that, it has some modifications for embedded systems and
+ the source code has been reordered a bit. \ No newline at end of file
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/etharp.c b/src/VBox/Devices/Network/lwip-new/src/netif/etharp.c
new file mode 100644
index 00000000..e6d70b2f
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/etharp.c
@@ -0,0 +1,1440 @@
+/**
+ * @file
+ * Address Resolution Protocol module for IP over Ethernet
+ *
+ * Functionally, ARP is divided into two parts. The first maps an IP address
+ * to a physical address when sending a packet, and the second part answers
+ * requests from other machines for our physical address.
+ *
+ * This implementation complies with RFC 826 (Ethernet ARP). It supports
+ * Gratuitious ARP from RFC3220 (IP Mobility Support for IPv4) section 4.6
+ * if an interface calls etharp_gratuitous(our_netif) upon address change.
+ */
+
+/*
+ * Copyright (c) 2001-2003 Swedish Institute of Computer Science.
+ * Copyright (c) 2003-2004 Leon Woestenberg <leon.woestenberg@axon.tv>
+ * Copyright (c) 2003-2004 Axon Digital Design B.V., The Netherlands.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ */
+
+#include "lwip/opt.h"
+
+#if LWIP_ARP || LWIP_ETHERNET
+
+#include "lwip/ip_addr.h"
+#include "lwip/def.h"
+#include "lwip/ip.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/dhcp.h"
+#include "lwip/autoip.h"
+#include "netif/etharp.h"
+#include "lwip/ip6.h"
+
+#if PPPOE_SUPPORT
+#include "netif/ppp_oe.h"
+#endif /* PPPOE_SUPPORT */
+
+#include <string.h>
+
+const struct eth_addr ethbroadcast = {{0xff,0xff,0xff,0xff,0xff,0xff}};
+const struct eth_addr ethzero = {{0,0,0,0,0,0}};
+
+/** The 24-bit IANA multicast OUI is 01-00-5e: */
+#define LL_MULTICAST_ADDR_0 0x01
+#define LL_MULTICAST_ADDR_1 0x00
+#define LL_MULTICAST_ADDR_2 0x5e
+
+#if LWIP_ARP /* don't build if not configured for use in lwipopts.h */
+
+/** the time an ARP entry stays valid after its last update,
+ * for ARP_TMR_INTERVAL = 5000, this is
+ * (240 * 5) seconds = 20 minutes.
+ */
+#define ARP_MAXAGE 240
+/** Re-request a used ARP entry 1 minute before it would expire to prevent
+ * breaking a steadily used connection because the ARP entry timed out. */
+#define ARP_AGE_REREQUEST_USED (ARP_MAXAGE - 12)
+
+/** the time an ARP entry stays pending after first request,
+ * for ARP_TMR_INTERVAL = 5000, this is
+ * (2 * 5) seconds = 10 seconds.
+ *
+ * @internal Keep this number at least 2, otherwise it might
+ * run out instantly if the timeout occurs directly after a request.
+ */
+#define ARP_MAXPENDING 2
+
+#define HWTYPE_ETHERNET 1
+
+enum etharp_state {
+ ETHARP_STATE_EMPTY = 0,
+ ETHARP_STATE_PENDING,
+ ETHARP_STATE_STABLE,
+ ETHARP_STATE_STABLE_REREQUESTING
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ ,ETHARP_STATE_STATIC
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+};
+
+struct etharp_entry {
+#if ARP_QUEUEING
+ /** Pointer to queue of pending outgoing packets on this ARP entry. */
+ struct etharp_q_entry *q;
+#else /* ARP_QUEUEING */
+ /** Pointer to a single pending outgoing packet on this ARP entry. */
+ struct pbuf *q;
+#endif /* ARP_QUEUEING */
+ ip_addr_t ipaddr;
+ struct netif *netif;
+ struct eth_addr ethaddr;
+ u8_t state;
+ u8_t ctime;
+};
+
+static struct etharp_entry arp_table[ARP_TABLE_SIZE];
+
+#if !LWIP_NETIF_HWADDRHINT
+static u8_t etharp_cached_entry;
+#endif /* !LWIP_NETIF_HWADDRHINT */
+
+/** Try hard to create a new entry - we want the IP address to appear in
+ the cache (even if this means removing an active entry or so). */
+#define ETHARP_FLAG_TRY_HARD 1
+#define ETHARP_FLAG_FIND_ONLY 2
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+#define ETHARP_FLAG_STATIC_ENTRY 4
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+#if LWIP_NETIF_HWADDRHINT
+#define ETHARP_SET_HINT(netif, hint) if (((netif) != NULL) && ((netif)->addr_hint != NULL)) \
+ *((netif)->addr_hint) = (hint);
+#else /* LWIP_NETIF_HWADDRHINT */
+#define ETHARP_SET_HINT(netif, hint) (etharp_cached_entry = (hint))
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+#if ARP_PROXY
+proxy_arp_hook_fn proxy_arp_hook;
+#endif /* ARP_PROXY */
+
+/* Some checks, instead of etharp_init(): */
+#if (LWIP_ARP && (ARP_TABLE_SIZE > 0x7f))
+ #error "ARP_TABLE_SIZE must fit in an s8_t, you have to reduce it in your lwipopts.h"
+#endif
+
+
+#if ARP_QUEUEING
+/**
+ * Free a complete queue of etharp entries
+ *
+ * @param q a qeueue of etharp_q_entry's to free
+ */
+static void
+free_etharp_q(struct etharp_q_entry *q)
+{
+ struct etharp_q_entry *r;
+ LWIP_ASSERT("q != NULL", q != NULL);
+ LWIP_ASSERT("q->p != NULL", q->p != NULL);
+ while (q) {
+ r = q;
+ q = q->next;
+ LWIP_ASSERT("r->p != NULL", (r->p != NULL));
+ pbuf_free(r->p);
+ memp_free(MEMP_ARP_QUEUE, r);
+ }
+}
+#else /* ARP_QUEUEING */
+
+/** Compatibility define: free the queued pbuf */
+#define free_etharp_q(q) pbuf_free(q)
+
+#endif /* ARP_QUEUEING */
+
+/** Clean up ARP table entries */
+static void
+etharp_free_entry(int i)
+{
+ /* remove from SNMP ARP index tree */
+ snmp_delete_arpidx_tree(arp_table[i].netif, &arp_table[i].ipaddr);
+ /* and empty packet queue */
+ if (arp_table[i].q != NULL) {
+ /* remove all queued packets */
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_free_entry: freeing entry %"U16_F", packet queue %p.\n", (u16_t)i, (void *)(arp_table[i].q)));
+ free_etharp_q(arp_table[i].q);
+ arp_table[i].q = NULL;
+ }
+ /* recycle entry for re-use */
+ arp_table[i].state = ETHARP_STATE_EMPTY;
+#ifdef LWIP_DEBUG
+ /* for debugging, clean out the complete entry */
+ arp_table[i].ctime = 0;
+ arp_table[i].netif = NULL;
+ ip_addr_set_zero(&arp_table[i].ipaddr);
+ arp_table[i].ethaddr = ethzero;
+#endif /* LWIP_DEBUG */
+}
+
+/**
+ * Clears expired entries in the ARP table.
+ *
+ * This function should be called every ETHARP_TMR_INTERVAL milliseconds (5 seconds),
+ * in order to expire entries in the ARP table.
+ */
+void
+etharp_tmr(void)
+{
+ u8_t i;
+
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer\n"));
+ /* remove expired entries from the ARP table */
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ if (state != ETHARP_STATE_EMPTY
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ && (state != ETHARP_STATE_STATIC)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ ) {
+ arp_table[i].ctime++;
+ if ((arp_table[i].ctime >= ARP_MAXAGE) ||
+ ((arp_table[i].state == ETHARP_STATE_PENDING) &&
+ (arp_table[i].ctime >= ARP_MAXPENDING))) {
+ /* pending or stable entry has become old! */
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_timer: expired %s entry %"U16_F".\n",
+ arp_table[i].state >= ETHARP_STATE_STABLE ? "stable" : "pending", (u16_t)i));
+ /* clean up entries that have just been expired */
+ etharp_free_entry(i);
+ }
+ else if (arp_table[i].state == ETHARP_STATE_STABLE_REREQUESTING) {
+ /* Reset state to stable, so that the next transmitted packet will
+ re-send an ARP request. */
+ arp_table[i].state = ETHARP_STATE_STABLE;
+ }
+#if ARP_QUEUEING
+ /* still pending entry? (not expired) */
+ if (arp_table[i].state == ETHARP_STATE_PENDING) {
+ /* resend an ARP query here? */
+ }
+#endif /* ARP_QUEUEING */
+ }
+ }
+}
+
+/**
+ * Search the ARP table for a matching or new entry.
+ *
+ * If an IP address is given, return a pending or stable ARP entry that matches
+ * the address. If no match is found, create a new entry with this address set,
+ * but in state ETHARP_EMPTY. The caller must check and possibly change the
+ * state of the returned entry.
+ *
+ * If ipaddr is NULL, return a initialized new entry in state ETHARP_EMPTY.
+ *
+ * In all cases, attempt to create new entries from an empty entry. If no
+ * empty entries are available and ETHARP_FLAG_TRY_HARD flag is set, recycle
+ * old entries. Heuristic choose the least important entry for recycling.
+ *
+ * @param ipaddr IP address to find in ARP cache, or to add if not found.
+ * @param flags @see definition of ETHARP_FLAG_*
+ * @param netif netif related to this address (used for NETIF_HWADDRHINT)
+ *
+ * @return The ARP entry index that matched or is created, ERR_MEM if no
+ * entry is found or could be recycled.
+ */
+static s8_t
+etharp_find_entry(ip_addr_t *ipaddr, u8_t flags)
+{
+ s8_t old_pending = ARP_TABLE_SIZE, old_stable = ARP_TABLE_SIZE;
+ s8_t empty = ARP_TABLE_SIZE;
+ u8_t i = 0, age_pending = 0, age_stable = 0;
+ /* oldest entry with packets on queue */
+ s8_t old_queue = ARP_TABLE_SIZE;
+ /* its age */
+ u8_t age_queue = 0;
+
+ /**
+ * a) do a search through the cache, remember candidates
+ * b) select candidate entry
+ * c) create new entry
+ */
+
+ /* a) in a single search sweep, do all of this
+ * 1) remember the first empty entry (if any)
+ * 2) remember the oldest stable entry (if any)
+ * 3) remember the oldest pending entry without queued packets (if any)
+ * 4) remember the oldest pending entry with queued packets (if any)
+ * 5) search for a matching IP entry, either pending or stable
+ * until 5 matches, or all entries are searched for.
+ */
+
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ /* no empty entry found yet and now we do find one? */
+ if ((empty == ARP_TABLE_SIZE) && (state == ETHARP_STATE_EMPTY)) {
+ LWIP_DEBUGF(ETHARP_DEBUG, ("etharp_find_entry: found empty entry %"U16_F"\n", (u16_t)i));
+ /* remember first empty entry */
+ empty = i;
+ } else if (state != ETHARP_STATE_EMPTY) {
+ LWIP_ASSERT("state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE",
+ state == ETHARP_STATE_PENDING || state >= ETHARP_STATE_STABLE);
+ /* if given, does IP address match IP address in ARP entry? */
+ if (ipaddr && ip_addr_cmp(ipaddr, &arp_table[i].ipaddr)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: found matching entry %"U16_F"\n", (u16_t)i));
+ /* found exact IP address match, simply bail out */
+ return i;
+ }
+ /* pending entry? */
+ if (state == ETHARP_STATE_PENDING) {
+ /* pending with queued packets? */
+ if (arp_table[i].q != NULL) {
+ if (arp_table[i].ctime >= age_queue) {
+ old_queue = i;
+ age_queue = arp_table[i].ctime;
+ }
+ } else
+ /* pending without queued packets? */
+ {
+ if (arp_table[i].ctime >= age_pending) {
+ old_pending = i;
+ age_pending = arp_table[i].ctime;
+ }
+ }
+ /* stable entry? */
+ } else if (state >= ETHARP_STATE_STABLE) {
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ /* don't record old_stable for static entries since they never expire */
+ if (state < ETHARP_STATE_STATIC)
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ {
+ /* remember entry with oldest stable entry in oldest, its age in maxtime */
+ if (arp_table[i].ctime >= age_stable) {
+ old_stable = i;
+ age_stable = arp_table[i].ctime;
+ }
+ }
+ }
+ }
+ }
+ /* { we have no match } => try to create a new entry */
+
+ /* don't create new entry, only search? */
+ if (((flags & ETHARP_FLAG_FIND_ONLY) != 0) ||
+ /* or no empty entry found and not allowed to recycle? */
+ ((empty == ARP_TABLE_SIZE) && ((flags & ETHARP_FLAG_TRY_HARD) == 0))) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty entry found and not allowed to recycle\n"));
+ return (s8_t)ERR_MEM;
+ }
+
+ /* b) choose the least destructive entry to recycle:
+ * 1) empty entry
+ * 2) oldest stable entry
+ * 3) oldest pending entry without queued packets
+ * 4) oldest pending entry with queued packets
+ *
+ * { ETHARP_FLAG_TRY_HARD is set at this point }
+ */
+
+ /* 1) empty entry available? */
+ if (empty < ARP_TABLE_SIZE) {
+ i = empty;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting empty entry %"U16_F"\n", (u16_t)i));
+ } else {
+ /* 2) found recyclable stable entry? */
+ if (old_stable < ARP_TABLE_SIZE) {
+ /* recycle oldest stable*/
+ i = old_stable;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest stable entry %"U16_F"\n", (u16_t)i));
+ /* no queued packets should exist on stable entries */
+ LWIP_ASSERT("arp_table[i].q == NULL", arp_table[i].q == NULL);
+ /* 3) found recyclable pending entry without queued packets? */
+ } else if (old_pending < ARP_TABLE_SIZE) {
+ /* recycle oldest pending */
+ i = old_pending;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F" (without queue)\n", (u16_t)i));
+ /* 4) found recyclable pending entry with queued packets? */
+ } else if (old_queue < ARP_TABLE_SIZE) {
+ /* recycle oldest pending (queued packets are free in etharp_free_entry) */
+ i = old_queue;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: selecting oldest pending entry %"U16_F", freeing packet queue %p\n", (u16_t)i, (void *)(arp_table[i].q)));
+ /* no empty or recyclable entries found */
+ } else {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_find_entry: no empty or recyclable entries found\n"));
+ return (s8_t)ERR_MEM;
+ }
+
+ /* { empty or recyclable entry found } */
+ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+ etharp_free_entry(i);
+ }
+
+ LWIP_ASSERT("i < ARP_TABLE_SIZE", i < ARP_TABLE_SIZE);
+ LWIP_ASSERT("arp_table[i].state == ETHARP_STATE_EMPTY",
+ arp_table[i].state == ETHARP_STATE_EMPTY);
+
+ /* IP address given? */
+ if (ipaddr != NULL) {
+ /* set IP address */
+ ip_addr_copy(arp_table[i].ipaddr, *ipaddr);
+ }
+ arp_table[i].ctime = 0;
+ return (err_t)i;
+}
+
+/**
+ * Send an IP packet on the network using netif->linkoutput
+ * The ethernet header is filled in before sending.
+ *
+ * @params netif the lwIP network interface on which to send the packet
+ * @params p the packet to send, p->payload pointing to the (uninitialized) ethernet header
+ * @params src the source MAC address to be copied into the ethernet header
+ * @params dst the destination MAC address to be copied into the ethernet header
+ * @return ERR_OK if the packet was sent, any other err_t on failure
+ */
+static err_t
+etharp_send_ip(struct netif *netif, struct pbuf *p, struct eth_addr *src, struct eth_addr *dst)
+{
+ struct eth_hdr *ethhdr = (struct eth_hdr *)p->payload;
+
+ LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
+ (netif->hwaddr_len == ETHARP_HWADDR_LEN));
+ ETHADDR32_COPY(&ethhdr->dest, dst);
+ ETHADDR16_COPY(&ethhdr->src, src);
+ ethhdr->type = PP_HTONS(ETHTYPE_IP);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_send_ip: sending packet %p\n", (void *)p));
+ /* send the packet */
+ return netif->linkoutput(netif, p);
+}
+
+/**
+ * Update (or insert) a IP/MAC address pair in the ARP cache.
+ *
+ * If a pending entry is resolved, any queued packets will be sent
+ * at this point.
+ *
+ * @param netif netif related to this entry (used for NETIF_ADDRHINT)
+ * @param ipaddr IP address of the inserted ARP entry.
+ * @param ethaddr Ethernet address of the inserted ARP entry.
+ * @param flags @see definition of ETHARP_FLAG_*
+ *
+ * @return
+ * - ERR_OK Succesfully updated ARP cache.
+ * - ERR_MEM If we could not add a new ARP entry when ETHARP_FLAG_TRY_HARD was set.
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ * @see pbuf_free()
+ */
+static err_t
+etharp_update_arp_entry(struct netif *netif, ip_addr_t *ipaddr, struct eth_addr *ethaddr, u8_t flags)
+{
+ s8_t i;
+ LWIP_ASSERT("netif->hwaddr_len == ETHARP_HWADDR_LEN", netif->hwaddr_len == ETHARP_HWADDR_LEN);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+ ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
+ ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
+ /* non-unicast address? */
+ if (ip_addr_isany(ipaddr) ||
+ ip_addr_isbroadcast(ipaddr, netif) ||
+ ip_addr_ismulticast(ipaddr)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: will not add non-unicast IP address to ARP cache\n"));
+ return ERR_ARG;
+ }
+ /* find or create ARP entry */
+ i = etharp_find_entry(ipaddr, flags);
+ /* bail out if no entry could be found */
+ if (i < 0) {
+ return (err_t)i;
+ }
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+ if (flags & ETHARP_FLAG_STATIC_ENTRY) {
+ /* record static type */
+ arp_table[i].state = ETHARP_STATE_STATIC;
+ } else
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+ {
+ /* mark it stable */
+ arp_table[i].state = ETHARP_STATE_STABLE;
+ }
+
+ /* record network interface */
+ arp_table[i].netif = netif;
+ /* insert in SNMP ARP index tree */
+ snmp_insert_arpidx_tree(netif, &arp_table[i].ipaddr);
+
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_update_arp_entry: updating stable entry %"S16_F"\n", (s16_t)i));
+ /* update address */
+ ETHADDR32_COPY(&arp_table[i].ethaddr, ethaddr);
+ /* reset time stamp */
+ arp_table[i].ctime = 0;
+ /* this is where we will send out queued packets! */
+#if ARP_QUEUEING
+ while (arp_table[i].q != NULL) {
+ struct pbuf *p;
+ /* remember remainder of queue */
+ struct etharp_q_entry *q = arp_table[i].q;
+ /* pop first item off the queue */
+ arp_table[i].q = q->next;
+ /* get the packet pointer */
+ p = q->p;
+ /* now queue entry can be freed */
+ memp_free(MEMP_ARP_QUEUE, q);
+#else /* ARP_QUEUEING */
+ if (arp_table[i].q != NULL) {
+ struct pbuf *p = arp_table[i].q;
+ arp_table[i].q = NULL;
+#endif /* ARP_QUEUEING */
+ /* send the queued IP packet */
+ etharp_send_ip(netif, p, (struct eth_addr*)(netif->hwaddr), ethaddr);
+ /* free the queued IP packet */
+ pbuf_free(p);
+ }
+ return ERR_OK;
+}
+
+#if ETHARP_SUPPORT_STATIC_ENTRIES
+/** Add a new static entry to the ARP table. If an entry exists for the
+ * specified IP address, this entry is overwritten.
+ * If packets are queued for the specified IP address, they are sent out.
+ *
+ * @param ipaddr IP address for the new static entry
+ * @param ethaddr ethernet address for the new static entry
+ * @return @see return values of etharp_add_static_entry
+ */
+err_t
+etharp_add_static_entry(ip_addr_t *ipaddr, struct eth_addr *ethaddr)
+{
+ struct netif *netif;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_add_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F" - %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr),
+ ethaddr->addr[0], ethaddr->addr[1], ethaddr->addr[2],
+ ethaddr->addr[3], ethaddr->addr[4], ethaddr->addr[5]));
+
+ netif = ip_route(ipaddr);
+ if (netif == NULL) {
+ return ERR_RTE;
+ }
+
+ return etharp_update_arp_entry(netif, ipaddr, ethaddr, ETHARP_FLAG_TRY_HARD | ETHARP_FLAG_STATIC_ENTRY);
+}
+
+/** Remove a static entry from the ARP table previously added with a call to
+ * etharp_add_static_entry.
+ *
+ * @param ipaddr IP address of the static entry to remove
+ * @return ERR_OK: entry removed
+ * ERR_MEM: entry wasn't found
+ * ERR_ARG: entry wasn't a static entry but a dynamic one
+ */
+err_t
+etharp_remove_static_entry(ip_addr_t *ipaddr)
+{
+ s8_t i;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_remove_static_entry: %"U16_F".%"U16_F".%"U16_F".%"U16_F"\n",
+ ip4_addr1_16(ipaddr), ip4_addr2_16(ipaddr), ip4_addr3_16(ipaddr), ip4_addr4_16(ipaddr)));
+
+ /* find or create ARP entry */
+ i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY);
+ /* bail out if no entry could be found */
+ if (i < 0) {
+ return (err_t)i;
+ }
+
+ if (arp_table[i].state != ETHARP_STATE_STATIC) {
+ /* entry wasn't a static entry, cannot remove it */
+ return ERR_ARG;
+ }
+ /* entry found, free it */
+ etharp_free_entry(i);
+ return ERR_OK;
+}
+#endif /* ETHARP_SUPPORT_STATIC_ENTRIES */
+
+/**
+ * Remove all ARP table entries of the specified netif.
+ *
+ * @param netif points to a network interface
+ */
+void etharp_cleanup_netif(struct netif *netif)
+{
+ u8_t i;
+
+ for (i = 0; i < ARP_TABLE_SIZE; ++i) {
+ u8_t state = arp_table[i].state;
+ if ((state != ETHARP_STATE_EMPTY) && (arp_table[i].netif == netif)) {
+ etharp_free_entry(i);
+ }
+ }
+}
+
+/**
+ * Finds (stable) ethernet/IP address pair from ARP table
+ * using interface and IP address index.
+ * @note the addresses in the ARP table are in network order!
+ *
+ * @param netif points to interface index
+ * @param ipaddr points to the (network order) IP address index
+ * @param eth_ret points to return pointer
+ * @param ip_ret points to return pointer
+ * @return table index if found, -1 otherwise
+ */
+s8_t
+etharp_find_addr(struct netif *netif, ip_addr_t *ipaddr,
+ struct eth_addr **eth_ret, ip_addr_t **ip_ret)
+{
+ s8_t i;
+
+ LWIP_ASSERT("eth_ret != NULL && ip_ret != NULL",
+ eth_ret != NULL && ip_ret != NULL);
+
+ LWIP_UNUSED_ARG(netif);
+
+ i = etharp_find_entry(ipaddr, ETHARP_FLAG_FIND_ONLY);
+ if((i >= 0) && (arp_table[i].state >= ETHARP_STATE_STABLE)) {
+ *eth_ret = &arp_table[i].ethaddr;
+ *ip_ret = &arp_table[i].ipaddr;
+ return i;
+ }
+ return -1;
+}
+
+#if ETHARP_TRUST_IP_MAC
+/**
+ * Updates the ARP table using the given IP packet.
+ *
+ * Uses the incoming IP packet's source address to update the
+ * ARP cache for the local network. The function does not alter
+ * or free the packet. This function must be called before the
+ * packet p is passed to the IP layer.
+ *
+ * @param netif The lwIP network interface on which the IP packet pbuf arrived.
+ * @param p The IP packet that arrived on netif.
+ *
+ * @return NULL
+ *
+ * @see pbuf_free()
+ */
+static void
+etharp_ip_input(struct netif *netif, struct pbuf *p)
+{
+ struct eth_hdr *ethhdr;
+ struct ip_hdr *iphdr;
+ ip_addr_t iphdr_src;
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ /* Only insert an entry if the source IP address of the
+ incoming IP packet comes from a host on the local network. */
+ ethhdr = (struct eth_hdr *)p->payload;
+ iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
+#if ETHARP_SUPPORT_VLAN
+ if (ethhdr->type == PP_HTONS(ETHTYPE_VLAN)) {
+ iphdr = (struct ip_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR);
+ }
+#endif /* ETHARP_SUPPORT_VLAN */
+
+ ip_addr_copy(iphdr_src, iphdr->src);
+
+ /* source is not on the local network? */
+ if (!ip_addr_netcmp(&iphdr_src, &(netif->ip_addr), &(netif->netmask))) {
+ /* do nothing */
+ return;
+ }
+
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_ip_input: updating ETHARP table.\n"));
+ /* update the source IP address in the cache, if present */
+ /* @todo We could use ETHARP_FLAG_TRY_HARD if we think we are going to talk
+ * back soon (for example, if the destination IP address is ours. */
+ etharp_update_arp_entry(netif, &iphdr_src, &(ethhdr->src), ETHARP_FLAG_FIND_ONLY);
+}
+#endif /* ETHARP_TRUST_IP_MAC */
+
+/**
+ * Responds to ARP requests to us. Upon ARP replies to us, add entry to cache
+ * send out queued IP packets. Updates cache with snooped address pairs.
+ *
+ * Should be called for incoming ARP packets. The pbuf in the argument
+ * is freed by this function.
+ *
+ * @param netif The lwIP network interface on which the ARP packet pbuf arrived.
+ * @param ethaddr Ethernet address of netif.
+ * @param p The ARP packet that arrived on netif. Is freed by this function.
+ *
+ * @return NULL
+ *
+ * @see pbuf_free()
+ */
+static void
+etharp_arp_input(struct netif *netif, struct eth_addr *ethaddr, struct pbuf *p)
+{
+ struct etharp_hdr *hdr;
+ struct eth_hdr *ethhdr;
+ /* these are aligned properly, whereas the ARP header fields might not be */
+ ip_addr_t sipaddr, dipaddr;
+ u8_t for_us;
+#if ARP_PROXY
+ u8_t proxy;
+#endif
+#if LWIP_AUTOIP
+ const u8_t * ethdst_hwaddr;
+#endif /* LWIP_AUTOIP */
+
+ LWIP_ERROR("netif != NULL", (netif != NULL), return;);
+
+ /* drop short ARP packets: we have to check for p->len instead of p->tot_len here
+ since a struct etharp_hdr is pointed to p->payload, so it musn't be chained! */
+ if (p->len < SIZEOF_ETHARP_PACKET) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("etharp_arp_input: packet dropped, too short (%"S16_F"/%"S16_F")\n", p->tot_len,
+ (s16_t)SIZEOF_ETHARP_PACKET));
+ ETHARP_STATS_INC(etharp.lenerr);
+ ETHARP_STATS_INC(etharp.drop);
+ pbuf_free(p);
+ return;
+ }
+
+ ethhdr = (struct eth_hdr *)p->payload;
+ hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
+#if ETHARP_SUPPORT_VLAN
+ if (ethhdr->type == PP_HTONS(ETHTYPE_VLAN)) {
+ hdr = (struct etharp_hdr *)(((u8_t*)ethhdr) + SIZEOF_ETH_HDR + SIZEOF_VLAN_HDR);
+ }
+#endif /* ETHARP_SUPPORT_VLAN */
+
+ /* RFC 826 "Packet Reception": */
+ if ((hdr->hwtype != PP_HTONS(HWTYPE_ETHERNET)) ||
+ (hdr->hwlen != ETHARP_HWADDR_LEN) ||
+ (hdr->protolen != sizeof(ip_addr_t)) ||
+ (hdr->proto != PP_HTONS(ETHTYPE_IP))) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_WARNING,
+ ("etharp_arp_input: packet dropped, wrong hw type, hwlen, proto, protolen or ethernet type (%"U16_F"/%"U16_F"/%"U16_F"/%"U16_F")\n",
+ hdr->hwtype, hdr->hwlen, hdr->proto, hdr->protolen));
+ ETHARP_STATS_INC(etharp.proterr);
+ ETHARP_STATS_INC(etharp.drop);
+ pbuf_free(p);
+ return;
+ }
+ ETHARP_STATS_INC(etharp.recv);
+
+#if LWIP_AUTOIP
+ /* We have to check if a host already has configured our random
+ * created link local address and continously check if there is
+ * a host with this IP-address so we can detect collisions */
+ autoip_arp_reply(netif, hdr);
+#endif /* LWIP_AUTOIP */
+
+ /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
+ * structure packing (not using structure copy which breaks strict-aliasing rules). */
+ IPADDR2_COPY(&sipaddr, &hdr->sipaddr);
+ IPADDR2_COPY(&dipaddr, &hdr->dipaddr);
+
+ /* this interface is not configured? */
+ if (ip_addr_isany(&netif->ip_addr)) {
+ for_us = 0;
+#if defined(VBOX) && ARP_PROXY
+ proxy = 0; /* Shup up MSC. */
+#endif
+ } else {
+ /* ARP packet directed to us? */
+ for_us = (u8_t)ip_addr_cmp(&dipaddr, &(netif->ip_addr));
+#if ARP_PROXY
+ if (!for_us
+ && hdr->opcode == PP_HTONS(ARP_REQUEST)
+ && proxy_arp_hook != NULL
+ && (*proxy_arp_hook)(netif, &dipaddr))
+ {
+ for_us = proxy = 1;
+ }
+ else {
+ proxy = 0;
+ }
+#endif /* ARP_PROXY */
+ }
+
+ /* ARP message directed to us?
+ -> add IP address in ARP cache; assume requester wants to talk to us,
+ can result in directly sending the queued packets for this host.
+ ARP message not directed to us?
+ -> update the source IP address in the cache, if present */
+ etharp_update_arp_entry(netif, &sipaddr, &(hdr->shwaddr),
+ for_us ? ETHARP_FLAG_TRY_HARD : ETHARP_FLAG_FIND_ONLY);
+
+ /* now act on the message itself */
+ switch (hdr->opcode) {
+ /* ARP request? */
+ case PP_HTONS(ARP_REQUEST):
+ /* ARP request. If it asked for our address, we send out a
+ * reply. In any case, we time-stamp any existing ARP entry,
+ * and possiby send out an IP packet that was queued on it. */
+
+ LWIP_DEBUGF (ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP request\n"));
+ /* ARP request for our address? */
+ if (for_us) {
+
+#if !ARP_PROXY
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: replying to ARP request for our IP address\n"));
+#else
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE,
+ ("etharp_arp_input: replying to ARP request %s\n",
+ proxy ? "as proxy" : "for our IP address"));
+#endif
+ /* 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, &dipaddr);
+
+ 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(&dipaddr) ? (u8_t*)(ethbroadcast.addr) : hdr->shwaddr.addr;
+#endif /* LWIP_AUTOIP */
+
+ ETHADDR16_COPY(&hdr->dhwaddr, &hdr->shwaddr);
+#if LWIP_AUTOIP
+ ETHADDR16_COPY(&ethhdr->dest, ethdst_hwaddr);
+#else /* LWIP_AUTOIP */
+ ETHADDR16_COPY(&ethhdr->dest, &hdr->shwaddr);
+#endif /* LWIP_AUTOIP */
+ ETHADDR16_COPY(&hdr->shwaddr, ethaddr);
+ ETHADDR16_COPY(&ethhdr->src, ethaddr);
+
+ /* hwtype, hwaddr_len, proto, protolen and the type in the ethernet header
+ are already correct, we tested that before */
+
+ /* return ARP reply */
+ netif->linkoutput(netif, p);
+ /* we are not configured? */
+ } else if (ip_addr_isany(&netif->ip_addr)) {
+ /* { for_us == 0 and netif->ip_addr.addr == 0 } */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: we are unconfigured, ARP request ignored.\n"));
+ /* request was not directed to us */
+ } else {
+ /* { for_us == 0 and netif->ip_addr.addr != 0 } */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP request was not for us.\n"));
+ }
+ break;
+ case PP_HTONS(ARP_REPLY):
+ /* ARP reply. We already updated the ARP cache earlier. */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: incoming ARP reply\n"));
+#if (LWIP_DHCP && DHCP_DOES_ARP_CHECK)
+ /* DHCP wants to know about ARP replies from any host with an
+ * IP address also offered to us by the DHCP server. We do not
+ * want to take a duplicate IP address on a single network.
+ * @todo How should we handle redundant (fail-over) interfaces? */
+ dhcp_arp_reply(netif, &sipaddr);
+#endif /* (LWIP_DHCP && DHCP_DOES_ARP_CHECK) */
+ break;
+ default:
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_arp_input: ARP unknown opcode type %"S16_F"\n", htons(hdr->opcode)));
+ ETHARP_STATS_INC(etharp.err);
+ break;
+ }
+ /* free ARP packet */
+ pbuf_free(p);
+}
+
+/** Just a small helper function that sends a pbuf to an ethernet address
+ * in the arp_table specified by the index 'arp_idx'.
+ */
+static err_t
+etharp_output_to_arp_index(struct netif *netif, struct pbuf *q, u8_t arp_idx)
+{
+ LWIP_ASSERT("arp_table[arp_idx].state >= ETHARP_STATE_STABLE",
+ arp_table[arp_idx].state >= ETHARP_STATE_STABLE);
+ /* if arp table entry is about to expire: re-request it,
+ but only if its state is ETHARP_STATE_STABLE to prevent flooding the
+ network with ARP requests if this address is used frequently. */
+ if ((arp_table[arp_idx].state == ETHARP_STATE_STABLE) &&
+ (arp_table[arp_idx].ctime >= ARP_AGE_REREQUEST_USED)) {
+ if (etharp_request(netif, &arp_table[arp_idx].ipaddr) == ERR_OK) {
+ arp_table[arp_idx].state = ETHARP_STATE_STABLE_REREQUESTING;
+ }
+ }
+
+ return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr),
+ &arp_table[arp_idx].ethaddr);
+}
+
+/**
+ * Resolve and fill-in Ethernet address header for outgoing IP packet.
+ *
+ * For IP multicast and broadcast, corresponding Ethernet addresses
+ * are selected and the packet is transmitted on the link.
+ *
+ * For unicast addresses, the packet is submitted to etharp_query(). In
+ * case the IP address is outside the local network, the IP address of
+ * the gateway is used.
+ *
+ * @param netif The lwIP network interface which the IP packet will be sent on.
+ * @param q The pbuf(s) containing the IP packet to be sent.
+ * @param ipaddr The IP address of the packet destination.
+ *
+ * @return
+ * - ERR_RTE No route to destination (no gateway to external networks),
+ * or the return type of either etharp_query() or etharp_send_ip().
+ */
+err_t
+etharp_output(struct netif *netif, struct pbuf *q, ip_addr_t *ipaddr)
+{
+ struct eth_addr *dest;
+ struct eth_addr mcastaddr;
+ ip_addr_t *dst_addr = ipaddr;
+
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+ LWIP_ASSERT("q != NULL", q != NULL);
+ LWIP_ASSERT("ipaddr != NULL", ipaddr != NULL);
+
+ /* make room for Ethernet header - should not fail */
+ if (pbuf_header(q, sizeof(struct eth_hdr)) != 0) {
+ /* bail out */
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("etharp_output: could not allocate room for header.\n"));
+ LINK_STATS_INC(link.lenerr);
+ return ERR_BUF;
+ }
+
+ /* Determine on destination hardware address. Broadcasts and multicasts
+ * are special, other IP addresses are looked up in the ARP table. */
+
+ /* broadcast destination IP address? */
+ if (ip_addr_isbroadcast(ipaddr, netif)) {
+ /* broadcast on Ethernet also */
+ dest = (struct eth_addr *)&ethbroadcast;
+ /* multicast destination IP address? */
+ } else if (ip_addr_ismulticast(ipaddr)) {
+ /* Hash IP multicast address to MAC address.*/
+ mcastaddr.addr[0] = LL_MULTICAST_ADDR_0;
+ mcastaddr.addr[1] = LL_MULTICAST_ADDR_1;
+ mcastaddr.addr[2] = LL_MULTICAST_ADDR_2;
+ mcastaddr.addr[3] = ip4_addr2(ipaddr) & 0x7f;
+ mcastaddr.addr[4] = ip4_addr3(ipaddr);
+ mcastaddr.addr[5] = ip4_addr4(ipaddr);
+ /* destination Ethernet address is multicast */
+ dest = &mcastaddr;
+ /* unicast destination IP address? */
+ } else {
+ s8_t i;
+ /* outside local network? if so, this can neither be a global broadcast nor
+ a subnet broadcast. */
+ if (!ip_addr_netcmp(ipaddr, &(netif->ip_addr), &(netif->netmask)) &&
+ !ip_addr_islinklocal(ipaddr)) {
+#if LWIP_AUTOIP
+ struct ip_hdr *iphdr = (struct ip_hdr*)((u8_t*)q->payload +
+ sizeof(struct eth_hdr));
+ /* According to RFC 3297, chapter 2.6.2 (Forwarding Rules), a packet with
+ a link-local source address must always be "directly to its destination
+ on the same physical link. The host MUST NOT send the packet to any
+ router for forwarding". */
+ if (!ip_addr_islinklocal(&iphdr->src))
+#endif /* LWIP_AUTOIP */
+ {
+ /* interface has default gateway? */
+ if (!ip_addr_isany(&netif->gw)) {
+ /* send to hardware address of default gateway IP address */
+ dst_addr = &(netif->gw);
+ /* no default gateway available */
+ } else {
+ /* no route to destination error (default gateway missing) */
+ return ERR_RTE;
+ }
+ }
+ }
+#if LWIP_NETIF_HWADDRHINT
+ if (netif->addr_hint != NULL) {
+ /* per-pcb cached entry was given */
+ u8_t etharp_cached_entry = *(netif->addr_hint);
+ if (etharp_cached_entry < ARP_TABLE_SIZE) {
+#endif /* LWIP_NETIF_HWADDRHINT */
+ if ((arp_table[etharp_cached_entry].state >= ETHARP_STATE_STABLE) &&
+ (ip_addr_cmp(dst_addr, &arp_table[etharp_cached_entry].ipaddr))) {
+ /* the per-pcb-cached entry is stable and the right one! */
+ ETHARP_STATS_INC(etharp.cachehit);
+ return etharp_output_to_arp_index(netif, q, etharp_cached_entry);
+ }
+#if LWIP_NETIF_HWADDRHINT
+ }
+ }
+#endif /* LWIP_NETIF_HWADDRHINT */
+
+ /* find stable entry: do this here since this is a critical path for
+ throughput and etharp_find_entry() is kind of slow */
+ for (i = 0; i < ARP_TABLE_SIZE; i++) {
+ if ((arp_table[i].state >= ETHARP_STATE_STABLE) &&
+ (ip_addr_cmp(dst_addr, &arp_table[i].ipaddr))) {
+ /* found an existing, stable entry */
+ ETHARP_SET_HINT(netif, i);
+ return etharp_output_to_arp_index(netif, q, i);
+ }
+ }
+ /* no stable entry found, use the (slower) query function:
+ queue on destination Ethernet address belonging to ipaddr */
+ return etharp_query(netif, dst_addr, q);
+ }
+
+ /* continuation for multicast/broadcast destinations */
+ /* obtain source Ethernet address of the given interface */
+ /* send packet directly on the link */
+ return etharp_send_ip(netif, q, (struct eth_addr*)(netif->hwaddr), dest);
+}
+
+/**
+ * Send an ARP request for the given IP address and/or queue a packet.
+ *
+ * If the IP address was not yet in the cache, a pending ARP cache entry
+ * is added and an ARP request is sent for the given address. The packet
+ * is queued on this entry.
+ *
+ * If the IP address was already pending in the cache, a new ARP request
+ * is sent for the given address. The packet is queued on this entry.
+ *
+ * If the IP address was already stable in the cache, and a packet is
+ * given, it is directly sent and no ARP request is sent out.
+ *
+ * If the IP address was already stable in the cache, and no packet is
+ * given, an ARP request is sent out.
+ *
+ * @param netif The lwIP network interface on which ipaddr
+ * must be queried for.
+ * @param ipaddr The IP address to be resolved.
+ * @param q If non-NULL, a pbuf that must be delivered to the IP address.
+ * q is not freed by this function.
+ *
+ * @note q must only be ONE packet, not a packet queue!
+ *
+ * @return
+ * - ERR_BUF Could not make room for Ethernet header.
+ * - ERR_MEM Hardware address unknown, and no more ARP entries available
+ * to query for address or queue the packet.
+ * - ERR_MEM Could not queue packet due to memory shortage.
+ * - ERR_RTE No route to destination (no gateway to external networks).
+ * - ERR_ARG Non-unicast address given, those will not appear in ARP cache.
+ *
+ */
+err_t
+etharp_query(struct netif *netif, ip_addr_t *ipaddr, struct pbuf *q)
+{
+ struct eth_addr * srcaddr = (struct eth_addr *)netif->hwaddr;
+ err_t result = ERR_MEM;
+ s8_t i; /* ARP entry index */
+
+ /* non-unicast address? */
+ if (ip_addr_isbroadcast(ipaddr, netif) ||
+ ip_addr_ismulticast(ipaddr) ||
+ ip_addr_isany(ipaddr)) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: will not add non-unicast IP address to ARP cache\n"));
+ return ERR_ARG;
+ }
+
+ /* find entry in ARP cache, ask to create entry if queueing packet */
+ i = etharp_find_entry(ipaddr, ETHARP_FLAG_TRY_HARD);
+
+ /* could not find or create entry? */
+ if (i < 0) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not create ARP entry\n"));
+ if (q) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: packet dropped\n"));
+ ETHARP_STATS_INC(etharp.memerr);
+ }
+ return (err_t)i;
+ }
+
+ /* mark a fresh entry as pending (we just sent a request) */
+ if (arp_table[i].state == ETHARP_STATE_EMPTY) {
+ arp_table[i].state = ETHARP_STATE_PENDING;
+ }
+
+ /* { i is either a STABLE or (new or existing) PENDING entry } */
+ LWIP_ASSERT("arp_table[i].state == PENDING or STABLE",
+ ((arp_table[i].state == ETHARP_STATE_PENDING) ||
+ (arp_table[i].state >= ETHARP_STATE_STABLE)));
+
+ /* do we have a pending entry? or an implicit query request? */
+ if ((arp_table[i].state == ETHARP_STATE_PENDING) || (q == NULL)) {
+ /* try to resolve it; send out ARP request */
+ result = etharp_request(netif, ipaddr);
+ if (result != ERR_OK) {
+ /* ARP request couldn't be sent */
+ /* We don't re-send arp request in etharp_tmr, but we still queue packets,
+ since this failure could be temporary, and the next packet calling
+ etharp_query again could lead to sending the queued packets. */
+ }
+ if (q == NULL) {
+ return result;
+ }
+ }
+
+ /* packet given? */
+ LWIP_ASSERT("q != NULL", q != NULL);
+ /* stable entry? */
+ if (arp_table[i].state >= ETHARP_STATE_STABLE) {
+ /* we have a valid IP->Ethernet address mapping */
+ ETHARP_SET_HINT(netif, i);
+ /* send the packet */
+ result = etharp_send_ip(netif, q, srcaddr, &(arp_table[i].ethaddr));
+ /* pending entry? (either just created or already pending */
+ } else if (arp_table[i].state == ETHARP_STATE_PENDING) {
+ /* entry is still pending, queue the given packet 'q' */
+ struct pbuf *p;
+ int copy_needed = 0;
+ /* IF q includes a PBUF_REF, PBUF_POOL or PBUF_RAM, we have no choice but
+ * to copy the whole queue into a new PBUF_RAM (see bug #11400)
+ * PBUF_ROMs can be left as they are, since ROM must not get changed. */
+ p = q;
+ while (p) {
+ LWIP_ASSERT("no packet queues allowed!", (p->len != p->tot_len) || (p->next == 0));
+ if(p->type != PBUF_ROM) {
+ copy_needed = 1;
+ break;
+ }
+ p = p->next;
+ }
+ if(copy_needed) {
+ /* copy the whole packet into new pbufs */
+ p = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if(p != NULL) {
+ if (pbuf_copy(p, q) != ERR_OK) {
+ pbuf_free(p);
+ p = NULL;
+ }
+ }
+ } else {
+ /* referencing the old pbuf is enough */
+ p = q;
+ pbuf_ref(p);
+ }
+ /* packet could be taken over? */
+ if (p != NULL) {
+ /* queue packet ... */
+#if ARP_QUEUEING
+ struct etharp_q_entry *new_entry;
+ /* allocate a new arp queue entry */
+ new_entry = (struct etharp_q_entry *)memp_malloc(MEMP_ARP_QUEUE);
+ if (new_entry != NULL) {
+ new_entry->next = 0;
+ new_entry->p = p;
+ if(arp_table[i].q != NULL) {
+ /* queue was already existent, append the new entry to the end */
+ struct etharp_q_entry *r;
+ r = arp_table[i].q;
+ while (r->next != NULL) {
+ r = r->next;
+ }
+ r->next = new_entry;
+ } else {
+ /* queue did not exist, first item in queue */
+ arp_table[i].q = new_entry;
+ }
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+ result = ERR_OK;
+ } else {
+ /* the pool MEMP_ARP_QUEUE is empty */
+ pbuf_free(p);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+ result = ERR_MEM;
+ }
+#else /* ARP_QUEUEING */
+ /* always queue one packet per ARP request only, freeing a previously queued packet */
+ if (arp_table[i].q != NULL) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: dropped previously queued packet %p for ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+ pbuf_free(arp_table[i].q);
+ }
+ arp_table[i].q = p;
+ result = ERR_OK;
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: queued packet %p on ARP entry %"S16_F"\n", (void *)q, (s16_t)i));
+#endif /* ARP_QUEUEING */
+ } else {
+ ETHARP_STATS_INC(etharp.memerr);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_query: could not queue a copy of PBUF_REF packet %p (out of memory)\n", (void *)q));
+ result = ERR_MEM;
+ }
+ }
+ return result;
+}
+
+/**
+ * Send a raw ARP packet (opcode and all addresses can be modified)
+ *
+ * @param netif the lwip network interface on which to send the ARP packet
+ * @param ethsrc_addr the source MAC address for the ethernet header
+ * @param ethdst_addr the destination MAC address for the ethernet header
+ * @param hwsrc_addr the source MAC address for the ARP protocol header
+ * @param ipsrc_addr the source IP address for the ARP protocol header
+ * @param hwdst_addr the destination MAC address for the ARP protocol header
+ * @param ipdst_addr the destination IP address for the ARP protocol header
+ * @param opcode the type of the ARP packet
+ * @return ERR_OK if the ARP packet has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+#if !LWIP_AUTOIP
+static
+#endif /* LWIP_AUTOIP */
+err_t
+etharp_raw(struct netif *netif, const struct eth_addr *ethsrc_addr,
+ const struct eth_addr *ethdst_addr,
+ const struct eth_addr *hwsrc_addr, const ip_addr_t *ipsrc_addr,
+ const struct eth_addr *hwdst_addr, const ip_addr_t *ipdst_addr,
+ const u16_t opcode)
+{
+ struct pbuf *p;
+ err_t result = ERR_OK;
+ struct eth_hdr *ethhdr;
+ struct etharp_hdr *hdr;
+#if LWIP_AUTOIP
+ const u8_t * ethdst_hwaddr;
+#endif /* LWIP_AUTOIP */
+
+ LWIP_ASSERT("netif != NULL", netif != NULL);
+
+ /* allocate a pbuf for the outgoing ARP request packet */
+ p = pbuf_alloc(PBUF_RAW, SIZEOF_ETHARP_PACKET, PBUF_RAM);
+ /* could allocate a pbuf for an ARP request? */
+ if (p == NULL) {
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE | LWIP_DBG_LEVEL_SERIOUS,
+ ("etharp_raw: could not allocate pbuf for ARP request.\n"));
+ ETHARP_STATS_INC(etharp.memerr);
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("check that first pbuf can hold struct etharp_hdr",
+ (p->len >= SIZEOF_ETHARP_PACKET));
+
+ ethhdr = (struct eth_hdr *)p->payload;
+ hdr = (struct etharp_hdr *)((u8_t*)ethhdr + SIZEOF_ETH_HDR);
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_raw: sending raw ARP packet.\n"));
+ hdr->opcode = htons(opcode);
+
+ LWIP_ASSERT("netif->hwaddr_len must be the same as ETHARP_HWADDR_LEN for etharp!",
+ (netif->hwaddr_len == ETHARP_HWADDR_LEN));
+#if LWIP_AUTOIP
+ /* If we are using Link-Local, all ARP packets that contain a Link-Local
+ * 'sender IP address' MUST be sent using link-layer broadcast instead of
+ * link-layer unicast. (See RFC3927 Section 2.5, last paragraph) */
+ ethdst_hwaddr = ip_addr_islinklocal(ipsrc_addr) ? (u8_t*)(ethbroadcast.addr) : ethdst_addr->addr;
+#endif /* LWIP_AUTOIP */
+ /* Write the ARP MAC-Addresses */
+ ETHADDR16_COPY(&hdr->shwaddr, hwsrc_addr);
+ ETHADDR16_COPY(&hdr->dhwaddr, hwdst_addr);
+ /* Write the Ethernet MAC-Addresses */
+#if LWIP_AUTOIP
+ ETHADDR16_COPY(&ethhdr->dest, ethdst_hwaddr);
+#else /* LWIP_AUTOIP */
+ ETHADDR16_COPY(&ethhdr->dest, ethdst_addr);
+#endif /* LWIP_AUTOIP */
+ ETHADDR16_COPY(&ethhdr->src, ethsrc_addr);
+ /* Copy struct ip_addr2 to aligned ip_addr, to support compilers without
+ * structure packing. */
+ IPADDR2_COPY(&hdr->sipaddr, ipsrc_addr);
+ IPADDR2_COPY(&hdr->dipaddr, ipdst_addr);
+
+ hdr->hwtype = PP_HTONS(HWTYPE_ETHERNET);
+ hdr->proto = PP_HTONS(ETHTYPE_IP);
+ /* set hwlen and protolen */
+ hdr->hwlen = ETHARP_HWADDR_LEN;
+ hdr->protolen = sizeof(ip_addr_t);
+
+ ethhdr->type = PP_HTONS(ETHTYPE_ARP);
+ /* send ARP query */
+ result = netif->linkoutput(netif, p);
+ ETHARP_STATS_INC(etharp.xmit);
+ /* free ARP query packet */
+ pbuf_free(p);
+ p = NULL;
+ /* could not allocate pbuf for ARP request */
+
+ return result;
+}
+
+/**
+ * Send an ARP request packet asking for ipaddr.
+ *
+ * @param netif the lwip network interface on which to send the request
+ * @param ipaddr the IP address for which to ask
+ * @return ERR_OK if the request has been sent
+ * ERR_MEM if the ARP packet couldn't be allocated
+ * any other err_t on failure
+ */
+err_t
+etharp_request(struct netif *netif, ip_addr_t *ipaddr)
+{
+ LWIP_DEBUGF(ETHARP_DEBUG | LWIP_DBG_TRACE, ("etharp_request: sending ARP request.\n"));
+ return etharp_raw(netif, (struct eth_addr *)netif->hwaddr, &ethbroadcast,
+ (struct eth_addr *)netif->hwaddr, &netif->ip_addr, &ethzero,
+ ipaddr, ARP_REQUEST);
+}
+#endif /* LWIP_ARP */
+
+/**
+ * Process received ethernet frames. Using this function instead of directly
+ * calling ip_input and passing ARP frames through etharp in ethernetif_input,
+ * the ARP cache is protected from concurrent access.
+ *
+ * @param p the recevied packet, p->payload pointing to the ethernet header
+ * @param netif the network interface on which the packet was received
+ */
+err_t
+ethernet_input(struct pbuf *p, struct netif *netif)
+{
+ struct eth_hdr* ethhdr;
+ u16_t type;
+#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;
+}
+#endif /* LWIP_ARP || LWIP_ETHERNET */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ethernetif.c b/src/VBox/Devices/Network/lwip-new/src/netif/ethernetif.c
new file mode 100644
index 00000000..46900bdb
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ethernetif.c
@@ -0,0 +1,322 @@
+/**
+ * @file
+ * Ethernet Interface Skeleton
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright notice,
+ * this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
+ * this list of conditions and the following disclaimer in the documentation
+ * and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT
+ * SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
+ * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
+ * IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * This file is part of the lwIP TCP/IP stack.
+ *
+ * Author: Adam Dunkels <adam@sics.se>
+ *
+ */
+
+/*
+ * This file is a skeleton for developing Ethernet network interface
+ * drivers for lwIP. Add code to the low_level functions and do a
+ * search-and-replace for the word "ethernetif" to replace it with
+ * something that better describes your network interface.
+ */
+
+#include "lwip/opt.h"
+
+#if 0 /* don't build, this is only a skeleton, see previous comment */
+
+#include "lwip/def.h"
+#include "lwip/mem.h"
+#include "lwip/pbuf.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/ethip6.h"
+#include "netif/etharp.h"
+#include "netif/ppp_oe.h"
+
+/* Define those to better describe your network interface. */
+#define IFNAME0 'e'
+#define IFNAME1 'n'
+
+/**
+ * Helper struct to hold private data used to operate your ethernet interface.
+ * Keeping the ethernet address of the MAC in this struct is not necessary
+ * as it is already kept in the struct netif.
+ * But this is only an example, anyway...
+ */
+struct ethernetif {
+ struct eth_addr *ethaddr;
+ /* Add whatever per-interface state that is needed here. */
+};
+
+/* Forward declarations. */
+static void ethernetif_input(struct netif *netif);
+
+/**
+ * In this function, the hardware should be initialized.
+ * Called from ethernetif_init().
+ *
+ * @param netif the already initialized lwip network interface structure
+ * for this ethernetif
+ */
+static void
+low_level_init(struct netif *netif)
+{
+ struct ethernetif *ethernetif = netif->state;
+
+ /* set MAC hardware address length */
+ netif->hwaddr_len = ETHARP_HWADDR_LEN;
+
+ /* set MAC hardware address */
+ netif->hwaddr[0] = ;
+ ...
+ netif->hwaddr[5] = ;
+
+ /* maximum transfer unit */
+ netif->mtu = 1500;
+
+ /* device capabilities */
+ /* don't set NETIF_FLAG_ETHARP if this device is not an ethernet one */
+ netif->flags = NETIF_FLAG_BROADCAST | NETIF_FLAG_ETHARP | NETIF_FLAG_LINK_UP;
+
+ /* Do whatever else is needed to initialize interface. */
+}
+
+/**
+ * This function should do the actual transmission of the packet. The packet is
+ * contained in the pbuf that is passed to the function. This pbuf
+ * might be chained.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @param p the MAC packet to send (e.g. IP packet including MAC addresses and type)
+ * @return ERR_OK if the packet could be sent
+ * an err_t value if the packet couldn't be sent
+ *
+ * @note Returning ERR_MEM here if a DMA queue of your MAC is full can lead to
+ * strange results. You might consider waiting for space in the DMA queue
+ * to become availale since the stack doesn't retry to send a packet
+ * dropped because of memory failure (except for the TCP timers).
+ */
+
+static err_t
+low_level_output(struct netif *netif, struct pbuf *p)
+{
+ struct ethernetif *ethernetif = netif->state;
+ struct pbuf *q;
+
+ initiate transfer();
+
+#if ETH_PAD_SIZE
+ pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
+#endif
+
+ for(q = p; q != NULL; q = q->next) {
+ /* Send the data from the pbuf to the interface, one pbuf at a
+ time. The size of the data in each pbuf is kept in the ->len
+ variable. */
+ send data from(q->payload, q->len);
+ }
+
+ signal that packet should be sent();
+
+#if ETH_PAD_SIZE
+ pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
+#endif
+
+ LINK_STATS_INC(link.xmit);
+
+ return ERR_OK;
+}
+
+/**
+ * Should allocate a pbuf and transfer the bytes of the incoming
+ * packet from the interface into the pbuf.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @return a pbuf filled with the received packet (including MAC header)
+ * NULL on memory error
+ */
+static struct pbuf *
+low_level_input(struct netif *netif)
+{
+ struct ethernetif *ethernetif = netif->state;
+ struct pbuf *p, *q;
+ u16_t len;
+
+ /* Obtain the size of the packet and put it into the "len"
+ variable. */
+ len = ;
+
+#if ETH_PAD_SIZE
+ len += ETH_PAD_SIZE; /* allow room for Ethernet padding */
+#endif
+
+ /* We allocate a pbuf chain of pbufs from the pool. */
+ p = pbuf_alloc(PBUF_RAW, len, PBUF_POOL);
+
+ if (p != NULL) {
+
+#if ETH_PAD_SIZE
+ pbuf_header(p, -ETH_PAD_SIZE); /* drop the padding word */
+#endif
+
+ /* We iterate over the pbuf chain until we have read the entire
+ * packet into the pbuf. */
+ for(q = p; q != NULL; q = q->next) {
+ /* Read enough bytes to fill this pbuf in the chain. The
+ * available data in the pbuf is given by the q->len
+ * variable.
+ * This does not necessarily have to be a memcpy, you can also preallocate
+ * pbufs for a DMA-enabled MAC and after receiving truncate it to the
+ * actually received size. In this case, ensure the tot_len member of the
+ * pbuf is the sum of the chained pbuf len members.
+ */
+ read data into(q->payload, q->len);
+ }
+ acknowledge that packet has been read();
+
+#if ETH_PAD_SIZE
+ pbuf_header(p, ETH_PAD_SIZE); /* reclaim the padding word */
+#endif
+
+ LINK_STATS_INC(link.recv);
+ } else {
+ drop packet();
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ }
+
+ return p;
+}
+
+/**
+ * This function should be called when a packet is ready to be read
+ * from the interface. It uses the function low_level_input() that
+ * should handle the actual reception of bytes from the network
+ * interface. Then the type of the received packet is determined and
+ * the appropriate input function is called.
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ */
+static void
+ethernetif_input(struct netif *netif)
+{
+ struct ethernetif *ethernetif;
+ struct eth_hdr *ethhdr;
+ struct pbuf *p;
+
+ ethernetif = netif->state;
+
+ /* move received packet into a new pbuf */
+ p = low_level_input(netif);
+ /* no packet could be read, silently ignore this */
+ if (p == NULL) return;
+ /* points to packet payload, which starts with an Ethernet header */
+ ethhdr = p->payload;
+
+ switch (htons(ethhdr->type)) {
+ /* IP or ARP packet? */
+ case ETHTYPE_IP:
+ case ETHTYPE_IPV6:
+ case ETHTYPE_ARP:
+#if PPPOE_SUPPORT
+ /* PPPoE packet? */
+ case ETHTYPE_PPPOEDISC:
+ case ETHTYPE_PPPOE:
+#endif /* PPPOE_SUPPORT */
+ /* full packet send to tcpip_thread to process */
+ if (netif->input(p, netif)!=ERR_OK)
+ { LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_input: IP input error\n"));
+ pbuf_free(p);
+ p = NULL;
+ }
+ break;
+
+ default:
+ pbuf_free(p);
+ p = NULL;
+ break;
+ }
+}
+
+/**
+ * Should be called at the beginning of the program to set up the
+ * network interface. It calls the function low_level_init() to do the
+ * actual setup of the hardware.
+ *
+ * This function should be passed as a parameter to netif_add().
+ *
+ * @param netif the lwip network interface structure for this ethernetif
+ * @return ERR_OK if the loopif is initialized
+ * ERR_MEM if private data couldn't be allocated
+ * any other err_t on error
+ */
+err_t
+ethernetif_init(struct netif *netif)
+{
+ struct ethernetif *ethernetif;
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+
+ ethernetif = mem_malloc(sizeof(struct ethernetif));
+ if (ethernetif == NULL) {
+ LWIP_DEBUGF(NETIF_DEBUG, ("ethernetif_init: out of memory\n"));
+ return ERR_MEM;
+ }
+
+#if LWIP_NETIF_HOSTNAME
+ /* Initialize interface hostname */
+ netif->hostname = "lwip";
+#endif /* LWIP_NETIF_HOSTNAME */
+
+ /*
+ * Initialize the snmp variables and counters inside the struct netif.
+ * The last argument should be replaced with your link speed, in units
+ * of bits per second.
+ */
+ NETIF_INIT_SNMP(netif, snmp_ifType_ethernet_csmacd, LINK_SPEED_OF_YOUR_NETIF_IN_BPS);
+
+ netif->state = ethernetif;
+ netif->name[0] = IFNAME0;
+ netif->name[1] = IFNAME1;
+ /* We directly use etharp_output() here to save a function call.
+ * You can instead declare your own function an call etharp_output()
+ * from it if you have to do some checks before sending (e.g. if link
+ * is available...) */
+ netif->output = etharp_output;
+#if LWIP_IPV6
+ netif->output_ip6 = ethip6_output;
+#endif /* LWIP_IPV6 */
+ netif->linkoutput = low_level_output;
+
+ ethernetif->ethaddr = (struct eth_addr *)&(netif->hwaddr[0]);
+
+ /* initialize the hardware */
+ low_level_init(netif);
+
+ return ERR_OK;
+}
+
+#endif /* 0 */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/auth.c b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/auth.c
new file mode 100644
index 00000000..0fd87a37
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/auth.c
@@ -0,0 +1,1334 @@
+/*****************************************************************************
+* auth.c - Network Authentication and Phase Control program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-08 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Ported from public pppd code.
+*****************************************************************************/
+/*
+ * auth.c - PPP authentication and phase control.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "fsm.h"
+#include "lcp.h"
+#include "pap.h"
+#include "chap.h"
+#include "auth.h"
+#include "ipcp.h"
+
+#if CBCP_SUPPORT
+#include "cbcp.h"
+#endif /* CBCP_SUPPORT */
+
+#include "lwip/inet.h"
+
+#include <string.h>
+
+#if 0 /* UNUSED */
+/* Bits in scan_authfile return value */
+#define NONWILD_SERVER 1
+#define NONWILD_CLIENT 2
+
+#define ISWILD(word) (word[0] == '*' && word[1] == 0)
+#endif /* UNUSED */
+
+#if PAP_SUPPORT || CHAP_SUPPORT
+/* The name by which the peer authenticated itself to us. */
+static char peer_authname[MAXNAMELEN];
+#endif /* PAP_SUPPORT || CHAP_SUPPORT */
+
+/* Records which authentication operations haven't completed yet. */
+static int auth_pending[NUM_PPP];
+
+/* Set if we have successfully called plogin() */
+static int logged_in;
+
+/* Set if we have run the /etc/ppp/auth-up script. */
+static int did_authup; /* @todo, we don't need this in lwip*/
+
+/* List of addresses which the peer may use. */
+static struct wordlist *addresses[NUM_PPP];
+
+#if 0 /* UNUSED */
+/* Wordlist giving addresses which the peer may use
+ without authenticating itself. */
+static struct wordlist *noauth_addrs;
+
+/* Extra options to apply, from the secrets file entry for the peer. */
+static struct wordlist *extra_options;
+#endif /* UNUSED */
+
+/* Number of network protocols which we have opened. */
+static int num_np_open;
+
+/* Number of network protocols which have come up. */
+static int num_np_up;
+
+#if PAP_SUPPORT || CHAP_SUPPORT
+/* Set if we got the contents of passwd[] from the pap-secrets file. */
+static int passwd_from_file;
+#endif /* PAP_SUPPORT || CHAP_SUPPORT */
+
+#if 0 /* UNUSED */
+/* Set if we require authentication only because we have a default route. */
+static bool default_auth;
+
+/* Hook to enable a plugin to control the idle time limit */
+int (*idle_time_hook) __P((struct ppp_idle *)) = NULL;
+
+/* Hook for a plugin to say whether we can possibly authenticate any peer */
+int (*pap_check_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to check the PAP user and password */
+int (*pap_auth_hook) __P((char *user, char *passwd, char **msgp,
+ struct wordlist **paddrs,
+ struct wordlist **popts)) = NULL;
+
+/* Hook for a plugin to know about the PAP user logout */
+void (*pap_logout_hook) __P((void)) = NULL;
+
+/* Hook for a plugin to get the PAP password for authenticating us */
+int (*pap_passwd_hook) __P((char *user, char *passwd)) = NULL;
+
+/*
+ * This is used to ensure that we don't start an auth-up/down
+ * script while one is already running.
+ */
+enum script_state {
+ s_down,
+ s_up
+};
+
+static enum script_state auth_state = s_down;
+static enum script_state auth_script_state = s_down;
+static pid_t auth_script_pid = 0;
+
+/*
+ * Option variables.
+ * lwip: some of these are present in the ppp_settings structure
+ */
+bool uselogin = 0; /* Use /etc/passwd for checking PAP */
+bool cryptpap = 0; /* Passwords in pap-secrets are encrypted */
+bool refuse_pap = 0; /* Don't wanna auth. ourselves with PAP */
+bool refuse_chap = 0; /* Don't wanna auth. ourselves with CHAP */
+bool usehostname = 0; /* Use hostname for our_name */
+bool auth_required = 0; /* Always require authentication from peer */
+bool allow_any_ip = 0; /* Allow peer to use any IP address */
+bool explicit_remote = 0; /* User specified explicit remote name */
+char remote_name[MAXNAMELEN]; /* Peer's name for authentication */
+
+#endif /* UNUSED */
+
+/* Bits in auth_pending[] */
+#define PAP_WITHPEER 1
+#define PAP_PEER 2
+#define CHAP_WITHPEER 4
+#define CHAP_PEER 8
+
+/* @todo, move this somewhere */
+/* Used for storing a sequence of words. Usually malloced. */
+struct wordlist {
+ struct wordlist *next;
+ char word[1];
+};
+
+
+extern char *crypt (const char *, const char *);
+
+/* Prototypes for procedures local to this file. */
+
+static void network_phase (int);
+static void check_idle (void *);
+static void connect_time_expired (void *);
+#if 0
+static int plogin (char *, char *, char **, int *);
+#endif
+static void plogout (void);
+static int null_login (int);
+static int get_pap_passwd (int, char *, char *);
+static int have_pap_secret (void);
+static int have_chap_secret (char *, char *, u32_t);
+static int ip_addr_check (u32_t, struct wordlist *);
+
+#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */
+static int scan_authfile (FILE *, char *, char *, char *,
+ struct wordlist **, struct wordlist **,
+ char *);
+static void free_wordlist (struct wordlist *);
+static void auth_script (char *);
+static void auth_script_done (void *);
+static void set_allowed_addrs (int unit, struct wordlist *addrs);
+static int some_ip_ok (struct wordlist *);
+static int setupapfile (char **);
+static int privgroup (char **);
+static int set_noauth_addr (char **);
+static void check_access (FILE *, char *);
+#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */
+
+#if 0 /* UNUSED */
+/*
+ * Authentication-related options.
+ */
+option_t auth_options[] = {
+ { "require-pap", o_bool, &lcp_wantoptions[0].neg_upap,
+ "Require PAP authentication from peer", 1, &auth_required },
+ { "+pap", o_bool, &lcp_wantoptions[0].neg_upap,
+ "Require PAP authentication from peer", 1, &auth_required },
+ { "refuse-pap", o_bool, &refuse_pap,
+ "Don't agree to auth to peer with PAP", 1 },
+ { "-pap", o_bool, &refuse_pap,
+ "Don't allow PAP authentication with peer", 1 },
+ { "require-chap", o_bool, &lcp_wantoptions[0].neg_chap,
+ "Require CHAP authentication from peer", 1, &auth_required },
+ { "+chap", o_bool, &lcp_wantoptions[0].neg_chap,
+ "Require CHAP authentication from peer", 1, &auth_required },
+ { "refuse-chap", o_bool, &refuse_chap,
+ "Don't agree to auth to peer with CHAP", 1 },
+ { "-chap", o_bool, &refuse_chap,
+ "Don't allow CHAP authentication with peer", 1 },
+ { "name", o_string, our_name,
+ "Set local name for authentication",
+ OPT_PRIV|OPT_STATIC, NULL, MAXNAMELEN },
+ { "user", o_string, user,
+ "Set name for auth with peer", OPT_STATIC, NULL, MAXNAMELEN },
+ { "usehostname", o_bool, &usehostname,
+ "Must use hostname for authentication", 1 },
+ { "remotename", o_string, remote_name,
+ "Set remote name for authentication", OPT_STATIC,
+ &explicit_remote, MAXNAMELEN },
+ { "auth", o_bool, &auth_required,
+ "Require authentication from peer", 1 },
+ { "noauth", o_bool, &auth_required,
+ "Don't require peer to authenticate", OPT_PRIV, &allow_any_ip },
+ { "login", o_bool, &uselogin,
+ "Use system password database for PAP", 1 },
+ { "papcrypt", o_bool, &cryptpap,
+ "PAP passwords are encrypted", 1 },
+ { "+ua", o_special, (void *)setupapfile,
+ "Get PAP user and password from file" },
+ { "password", o_string, passwd,
+ "Password for authenticating us to the peer", OPT_STATIC,
+ NULL, MAXSECRETLEN },
+ { "privgroup", o_special, (void *)privgroup,
+ "Allow group members to use privileged options", OPT_PRIV },
+ { "allow-ip", o_special, (void *)set_noauth_addr,
+ "Set IP address(es) which can be used without authentication",
+ OPT_PRIV },
+ { NULL }
+};
+#endif /* UNUSED */
+#if 0 /* UNUSED */
+/*
+ * setupapfile - specifies UPAP info for authenticating with peer.
+ */
+static int
+setupapfile(char **argv)
+{
+ FILE * ufile;
+ int l;
+
+ lcp_allowoptions[0].neg_upap = 1;
+
+ /* open user info file */
+ seteuid(getuid());
+ ufile = fopen(*argv, "r");
+ seteuid(0);
+ if (ufile == NULL) {
+ option_error("unable to open user login data file %s", *argv);
+ return 0;
+ }
+ check_access(ufile, *argv);
+
+ /* get username */
+ if (fgets(user, MAXNAMELEN - 1, ufile) == NULL
+ || fgets(passwd, MAXSECRETLEN - 1, ufile) == NULL){
+ option_error("unable to read user login data file %s", *argv);
+ return 0;
+ }
+ fclose(ufile);
+
+ /* get rid of newlines */
+ l = strlen(user);
+ if (l > 0 && user[l-1] == '\n')
+ user[l-1] = 0;
+ l = strlen(passwd);
+ if (l > 0 && passwd[l-1] == '\n')
+ passwd[l-1] = 0;
+
+ return (1);
+}
+#endif /* UNUSED */
+
+#if 0 /* UNUSED */
+/*
+ * privgroup - allow members of the group to have privileged access.
+ */
+static int
+privgroup(char **argv)
+{
+ struct group *g;
+ int i;
+
+ g = getgrnam(*argv);
+ if (g == 0) {
+ option_error("group %s is unknown", *argv);
+ return 0;
+ }
+ for (i = 0; i < ngroups; ++i) {
+ if (groups[i] == g->gr_gid) {
+ privileged = 1;
+ break;
+ }
+ }
+ return 1;
+}
+#endif
+
+#if 0 /* UNUSED */
+/*
+ * set_noauth_addr - set address(es) that can be used without authentication.
+ * Equivalent to specifying an entry like `"" * "" addr' in pap-secrets.
+ */
+static int
+set_noauth_addr(char **argv)
+{
+ char *addr = *argv;
+ int l = strlen(addr);
+ struct wordlist *wp;
+
+ wp = (struct wordlist *) malloc(sizeof(struct wordlist) + l + 1);
+ if (wp == NULL)
+ novm("allow-ip argument");
+ wp->word = (char *) (wp + 1);
+ wp->next = noauth_addrs;
+ BCOPY(addr, wp->word, l);
+ noauth_addrs = wp;
+ return 1;
+}
+#endif /* UNUSED */
+
+/*
+ * An Open on LCP has requested a change from Dead to Establish phase.
+ * Do what's necessary to bring the physical layer up.
+ */
+void
+link_required(int unit)
+{
+ LWIP_UNUSED_ARG(unit);
+
+ AUTHDEBUG(LOG_INFO, ("link_required: %d\n", unit));
+}
+
+/*
+ * LCP has terminated the link; go to the Dead phase and take the
+ * physical layer down.
+ */
+void
+link_terminated(int unit)
+{
+ AUTHDEBUG(LOG_INFO, ("link_terminated: %d\n", unit));
+ if (lcp_phase[unit] == PHASE_DEAD) {
+ return;
+ }
+ if (logged_in) {
+ plogout();
+ }
+ lcp_phase[unit] = PHASE_DEAD;
+ AUTHDEBUG(LOG_NOTICE, ("Connection terminated.\n"));
+ pppLinkTerminated(unit);
+}
+
+/*
+ * LCP has gone down; it will either die or try to re-establish.
+ */
+void
+link_down(int unit)
+{
+ int i;
+ struct protent *protp;
+
+ AUTHDEBUG(LOG_INFO, ("link_down: %d\n", unit));
+
+ if (did_authup) {
+ /* XXX Do link down processing. */
+ did_authup = 0;
+ }
+ for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
+ if (!protp->enabled_flag) {
+ continue;
+ }
+ if (protp->protocol != PPP_LCP && protp->lowerdown != NULL) {
+ (*protp->lowerdown)(unit);
+ }
+ if (protp->protocol < 0xC000 && protp->close != NULL) {
+ (*protp->close)(unit, "LCP down");
+ }
+ }
+ num_np_open = 0; /* number of network protocols we have opened */
+ num_np_up = 0; /* Number of network protocols which have come up */
+
+ if (lcp_phase[unit] != PHASE_DEAD) {
+ lcp_phase[unit] = PHASE_TERMINATE;
+ }
+ pppLinkDown(unit);
+}
+
+/*
+ * The link is established.
+ * Proceed to the Dead, Authenticate or Network phase as appropriate.
+ */
+void
+link_established(int unit)
+{
+ int auth;
+ int i;
+ struct protent *protp;
+ lcp_options *wo = &lcp_wantoptions[unit];
+ lcp_options *go = &lcp_gotoptions[unit];
+#if PAP_SUPPORT || CHAP_SUPPORT
+ lcp_options *ho = &lcp_hisoptions[unit];
+#endif /* PAP_SUPPORT || CHAP_SUPPORT */
+
+ AUTHDEBUG(LOG_INFO, ("link_established: unit %d; Lowering up all protocols...\n", unit));
+ /*
+ * Tell higher-level protocols that LCP is up.
+ */
+ for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
+ if (protp->protocol != PPP_LCP && protp->enabled_flag && protp->lowerup != NULL) {
+ (*protp->lowerup)(unit);
+ }
+ }
+ if (ppp_settings.auth_required && !(go->neg_chap || go->neg_upap)) {
+ /*
+ * We wanted the peer to authenticate itself, and it refused:
+ * treat it as though it authenticated with PAP using a username
+ * of "" and a password of "". If that's not OK, boot it out.
+ */
+ if (!wo->neg_upap || !null_login(unit)) {
+ AUTHDEBUG(LOG_WARNING, ("peer refused to authenticate\n"));
+ lcp_close(unit, "peer refused to authenticate");
+ return;
+ }
+ }
+
+ lcp_phase[unit] = PHASE_AUTHENTICATE;
+ auth = 0;
+#if CHAP_SUPPORT
+ if (go->neg_chap) {
+ ChapAuthPeer(unit, ppp_settings.our_name, go->chap_mdtype);
+ auth |= CHAP_PEER;
+ }
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT && CHAP_SUPPORT
+ else
+#endif /* PAP_SUPPORT && CHAP_SUPPORT */
+#if PAP_SUPPORT
+ if (go->neg_upap) {
+ upap_authpeer(unit);
+ auth |= PAP_PEER;
+ }
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ if (ho->neg_chap) {
+ ChapAuthWithPeer(unit, ppp_settings.user, ho->chap_mdtype);
+ auth |= CHAP_WITHPEER;
+ }
+#endif /* CHAP_SUPPORT */
+#if PAP_SUPPORT && CHAP_SUPPORT
+ else
+#endif /* PAP_SUPPORT && CHAP_SUPPORT */
+#if PAP_SUPPORT
+ if (ho->neg_upap) {
+ if (ppp_settings.passwd[0] == 0) {
+ passwd_from_file = 1;
+ if (!get_pap_passwd(unit, ppp_settings.user, ppp_settings.passwd)) {
+ AUTHDEBUG(LOG_ERR, ("No secret found for PAP login\n"));
+ }
+ }
+ upap_authwithpeer(unit, ppp_settings.user, ppp_settings.passwd);
+ auth |= PAP_WITHPEER;
+ }
+#endif /* PAP_SUPPORT */
+ auth_pending[unit] = auth;
+
+ if (!auth) {
+ network_phase(unit);
+ }
+}
+
+/*
+ * Proceed to the network phase.
+ */
+static void
+network_phase(int unit)
+{
+ int i;
+ struct protent *protp;
+ lcp_options *go = &lcp_gotoptions[unit];
+
+ /*
+ * If the peer had to authenticate, run the auth-up script now.
+ */
+ if ((go->neg_chap || go->neg_upap) && !did_authup) {
+ /* XXX Do setup for peer authentication. */
+ did_authup = 1;
+ }
+
+#if CBCP_SUPPORT
+ /*
+ * If we negotiated callback, do it now.
+ */
+ if (go->neg_cbcp) {
+ lcp_phase[unit] = PHASE_CALLBACK;
+ (*cbcp_protent.open)(unit);
+ return;
+ }
+#endif /* CBCP_SUPPORT */
+
+ lcp_phase[unit] = PHASE_NETWORK;
+ for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
+ if (protp->protocol < 0xC000 && protp->enabled_flag && protp->open != NULL) {
+ (*protp->open)(unit);
+ if (protp->protocol != PPP_CCP) {
+ ++num_np_open;
+ }
+ }
+ }
+
+ if (num_np_open == 0) {
+ /* nothing to do */
+ lcp_close(0, "No network protocols running");
+ }
+}
+/* @todo: add void start_networks(void) here (pppd 2.3.11) */
+
+/*
+ * The peer has failed to authenticate himself using `protocol'.
+ */
+void
+auth_peer_fail(int unit, u16_t protocol)
+{
+ LWIP_UNUSED_ARG(protocol);
+
+ AUTHDEBUG(LOG_INFO, ("auth_peer_fail: %d proto=%X\n", unit, protocol));
+ /*
+ * Authentication failure: take the link down
+ */
+ lcp_close(unit, "Authentication failed");
+}
+
+
+#if PAP_SUPPORT || CHAP_SUPPORT
+/*
+ * The peer has been successfully authenticated using `protocol'.
+ */
+void
+auth_peer_success(int unit, u16_t protocol, char *name, int namelen)
+{
+ int pbit;
+
+ AUTHDEBUG(LOG_INFO, ("auth_peer_success: %d proto=%X\n", unit, protocol));
+ switch (protocol) {
+ case PPP_CHAP:
+ pbit = CHAP_PEER;
+ break;
+ case PPP_PAP:
+ pbit = PAP_PEER;
+ break;
+ default:
+ AUTHDEBUG(LOG_WARNING, ("auth_peer_success: unknown protocol %x\n", protocol));
+ return;
+ }
+
+ /*
+ * Save the authenticated name of the peer for later.
+ */
+ if (namelen > (int)sizeof(peer_authname) - 1) {
+ namelen = sizeof(peer_authname) - 1;
+ }
+ BCOPY(name, peer_authname, namelen);
+ peer_authname[namelen] = 0;
+
+ /*
+ * If there is no more authentication still to be done,
+ * proceed to the network (or callback) phase.
+ */
+ if ((auth_pending[unit] &= ~pbit) == 0) {
+ network_phase(unit);
+ }
+}
+
+/*
+ * We have failed to authenticate ourselves to the peer using `protocol'.
+ */
+void
+auth_withpeer_fail(int unit, u16_t protocol)
+{
+ int errCode = PPPERR_AUTHFAIL;
+
+ LWIP_UNUSED_ARG(protocol);
+
+ AUTHDEBUG(LOG_INFO, ("auth_withpeer_fail: %d proto=%X\n", unit, protocol));
+ if (passwd_from_file) {
+ BZERO(ppp_settings.passwd, MAXSECRETLEN);
+ }
+
+ /*
+ * We've failed to authenticate ourselves to our peer.
+ * He'll probably take the link down, and there's not much
+ * we can do except wait for that.
+ */
+ pppIOCtl(unit, PPPCTLS_ERRCODE, &errCode);
+ lcp_close(unit, "Failed to authenticate ourselves to peer");
+}
+
+/*
+ * We have successfully authenticated ourselves with the peer using `protocol'.
+ */
+void
+auth_withpeer_success(int unit, u16_t protocol)
+{
+ int pbit;
+
+ AUTHDEBUG(LOG_INFO, ("auth_withpeer_success: %d proto=%X\n", unit, protocol));
+ switch (protocol) {
+ case PPP_CHAP:
+ pbit = CHAP_WITHPEER;
+ break;
+ case PPP_PAP:
+ if (passwd_from_file) {
+ BZERO(ppp_settings.passwd, MAXSECRETLEN);
+ }
+ pbit = PAP_WITHPEER;
+ break;
+ default:
+ AUTHDEBUG(LOG_WARNING, ("auth_peer_success: unknown protocol %x\n", protocol));
+ pbit = 0;
+ }
+
+ /*
+ * If there is no more authentication still being done,
+ * proceed to the network (or callback) phase.
+ */
+ if ((auth_pending[unit] &= ~pbit) == 0) {
+ network_phase(unit);
+ }
+}
+#endif /* PAP_SUPPORT || CHAP_SUPPORT */
+
+
+/*
+ * np_up - a network protocol has come up.
+ */
+void
+np_up(int unit, u16_t proto)
+{
+ LWIP_UNUSED_ARG(unit);
+ LWIP_UNUSED_ARG(proto);
+
+ AUTHDEBUG(LOG_INFO, ("np_up: %d proto=%X\n", unit, proto));
+ if (num_np_up == 0) {
+ AUTHDEBUG(LOG_INFO, ("np_up: maxconnect=%d idle_time_limit=%d\n",ppp_settings.maxconnect,ppp_settings.idle_time_limit));
+ /*
+ * At this point we consider that the link has come up successfully.
+ */
+ if (ppp_settings.idle_time_limit > 0) {
+ TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit);
+ }
+
+ /*
+ * Set a timeout to close the connection once the maximum
+ * connect time has expired.
+ */
+ if (ppp_settings.maxconnect > 0) {
+ TIMEOUT(connect_time_expired, 0, ppp_settings.maxconnect);
+ }
+ }
+ ++num_np_up;
+}
+
+/*
+ * np_down - a network protocol has gone down.
+ */
+void
+np_down(int unit, u16_t proto)
+{
+ LWIP_UNUSED_ARG(unit);
+ LWIP_UNUSED_ARG(proto);
+
+ AUTHDEBUG(LOG_INFO, ("np_down: %d proto=%X\n", unit, proto));
+ if (--num_np_up == 0 && ppp_settings.idle_time_limit > 0) {
+ UNTIMEOUT(check_idle, NULL);
+ }
+}
+
+/*
+ * np_finished - a network protocol has finished using the link.
+ */
+void
+np_finished(int unit, u16_t proto)
+{
+ LWIP_UNUSED_ARG(unit);
+ LWIP_UNUSED_ARG(proto);
+
+ AUTHDEBUG(LOG_INFO, ("np_finished: %d proto=%X\n", unit, proto));
+ if (--num_np_open <= 0) {
+ /* no further use for the link: shut up shop. */
+ lcp_close(0, "No network protocols running");
+ }
+}
+
+/*
+ * check_idle - check whether the link has been idle for long
+ * enough that we can shut it down.
+ */
+static void
+check_idle(void *arg)
+{
+ struct ppp_idle idle;
+ u_short itime;
+
+ LWIP_UNUSED_ARG(arg);
+ if (!get_idle_time(0, &idle)) {
+ return;
+ }
+ itime = LWIP_MIN(idle.xmit_idle, idle.recv_idle);
+ if (itime >= ppp_settings.idle_time_limit) {
+ /* link is idle: shut it down. */
+ AUTHDEBUG(LOG_INFO, ("Terminating connection due to lack of activity.\n"));
+ lcp_close(0, "Link inactive");
+ } else {
+ TIMEOUT(check_idle, NULL, ppp_settings.idle_time_limit - itime);
+ }
+}
+
+/*
+ * connect_time_expired - log a message and close the connection.
+ */
+static void
+connect_time_expired(void *arg)
+{
+ LWIP_UNUSED_ARG(arg);
+
+ AUTHDEBUG(LOG_INFO, ("Connect time expired\n"));
+ lcp_close(0, "Connect time expired"); /* Close connection */
+}
+
+#if 0 /* UNUSED */
+/*
+ * auth_check_options - called to check authentication options.
+ */
+void
+auth_check_options(void)
+{
+ lcp_options *wo = &lcp_wantoptions[0];
+ int can_auth;
+ ipcp_options *ipwo = &ipcp_wantoptions[0];
+ u32_t remote;
+
+ /* Default our_name to hostname, and user to our_name */
+ if (ppp_settings.our_name[0] == 0 || ppp_settings.usehostname) {
+ strcpy(ppp_settings.our_name, ppp_settings.hostname);
+ }
+
+ if (ppp_settings.user[0] == 0) {
+ strcpy(ppp_settings.user, ppp_settings.our_name);
+ }
+
+ /* If authentication is required, ask peer for CHAP or PAP. */
+ if (ppp_settings.auth_required && !wo->neg_chap && !wo->neg_upap) {
+ wo->neg_chap = 1;
+ wo->neg_upap = 1;
+ }
+
+ /*
+ * Check whether we have appropriate secrets to use
+ * to authenticate the peer.
+ */
+ can_auth = wo->neg_upap && have_pap_secret();
+ if (!can_auth && wo->neg_chap) {
+ remote = ipwo->accept_remote? 0: ipwo->hisaddr;
+ can_auth = have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote);
+ }
+
+ if (ppp_settings.auth_required && !can_auth) {
+ ppp_panic("No auth secret");
+ }
+}
+#endif /* UNUSED */
+
+/*
+ * auth_reset - called when LCP is starting negotiations to recheck
+ * authentication options, i.e. whether we have appropriate secrets
+ * to use for authenticating ourselves and/or the peer.
+ */
+void
+auth_reset(int unit)
+{
+ lcp_options *go = &lcp_gotoptions[unit];
+ lcp_options *ao = &lcp_allowoptions[0];
+ ipcp_options *ipwo = &ipcp_wantoptions[0];
+ u32_t remote;
+
+ AUTHDEBUG(LOG_INFO, ("auth_reset: %d\n", unit));
+ ao->neg_upap = !ppp_settings.refuse_pap && (ppp_settings.passwd[0] != 0 || get_pap_passwd(unit, NULL, NULL));
+ ao->neg_chap = !ppp_settings.refuse_chap && ppp_settings.passwd[0] != 0 /*have_chap_secret(ppp_settings.user, ppp_settings.remote_name, (u32_t)0)*/;
+
+ if (go->neg_upap && !have_pap_secret()) {
+ go->neg_upap = 0;
+ }
+ if (go->neg_chap) {
+ remote = ipwo->accept_remote? 0: ipwo->hisaddr;
+ if (!have_chap_secret(ppp_settings.remote_name, ppp_settings.our_name, remote)) {
+ go->neg_chap = 0;
+ }
+ }
+}
+
+#if PAP_SUPPORT
+/*
+ * check_passwd - Check the user name and passwd against the PAP secrets
+ * file. If requested, also check against the system password database,
+ * and login the user if OK.
+ *
+ * returns:
+ * UPAP_AUTHNAK: Authentication failed.
+ * UPAP_AUTHACK: Authentication succeeded.
+ * In either case, msg points to an appropriate message.
+ */
+u_char
+check_passwd( int unit, char *auser, int userlen, char *apasswd, int passwdlen, char **msg, int *msglen)
+{
+#if 1 /* XXX Assume all entries OK. */
+ LWIP_UNUSED_ARG(unit);
+ LWIP_UNUSED_ARG(auser);
+ LWIP_UNUSED_ARG(userlen);
+ LWIP_UNUSED_ARG(apasswd);
+ LWIP_UNUSED_ARG(passwdlen);
+ LWIP_UNUSED_ARG(msglen);
+ *msg = (char *) 0;
+ return UPAP_AUTHACK; /* XXX Assume all entries OK. */
+#else
+ u_char ret = 0;
+ struct wordlist *addrs = NULL;
+ char passwd[256], user[256];
+ char secret[MAXWORDLEN];
+ static u_short attempts = 0;
+
+ /*
+ * Make copies of apasswd and auser, then null-terminate them.
+ */
+ BCOPY(apasswd, passwd, passwdlen);
+ passwd[passwdlen] = '\0';
+ BCOPY(auser, user, userlen);
+ user[userlen] = '\0';
+ *msg = (char *) 0;
+
+ /* XXX Validate user name and password. */
+ ret = UPAP_AUTHACK; /* XXX Assume all entries OK. */
+
+ if (ret == UPAP_AUTHNAK) {
+ if (*msg == (char *) 0) {
+ *msg = "Login incorrect";
+ }
+ *msglen = strlen(*msg);
+ /*
+ * Frustrate passwd stealer programs.
+ * Allow 10 tries, but start backing off after 3 (stolen from login).
+ * On 10'th, drop the connection.
+ */
+ if (attempts++ >= 10) {
+ AUTHDEBUG(LOG_WARNING, ("%d LOGIN FAILURES BY %s\n", attempts, user));
+ /*ppp_panic("Excess Bad Logins");*/
+ }
+ if (attempts > 3) {
+ /* @todo: this was sleep(), i.e. seconds, not milliseconds
+ * I don't think we really need this in lwIP - we would block tcpip_thread!
+ */
+ /*sys_msleep((attempts - 3) * 5);*/
+ }
+ if (addrs != NULL) {
+ free_wordlist(addrs);
+ }
+ } else {
+ attempts = 0; /* Reset count */
+ if (*msg == (char *) 0) {
+ *msg = "Login ok";
+ }
+ *msglen = strlen(*msg);
+ set_allowed_addrs(unit, addrs);
+ }
+
+ BZERO(passwd, sizeof(passwd));
+ BZERO(secret, sizeof(secret));
+
+ return ret;
+#endif
+}
+#endif /* PAP_SUPPORT */
+
+#if 0 /* UNUSED */
+/*
+ * This function is needed for PAM.
+ */
+
+#ifdef USE_PAM
+
+/* lwip does not support PAM*/
+
+#endif /* USE_PAM */
+
+#endif /* UNUSED */
+
+
+#if 0 /* UNUSED */
+/*
+ * plogin - Check the user name and password against the system
+ * password database, and login the user if OK.
+ *
+ * returns:
+ * UPAP_AUTHNAK: Login failed.
+ * UPAP_AUTHACK: Login succeeded.
+ * In either case, msg points to an appropriate message.
+ */
+static int
+plogin(char *user, char *passwd, char **msg, int *msglen)
+{
+
+ LWIP_UNUSED_ARG(user);
+ LWIP_UNUSED_ARG(passwd);
+ LWIP_UNUSED_ARG(msg);
+ LWIP_UNUSED_ARG(msglen);
+
+
+ /* The new lines are here align the file when
+ * compared against the pppd 2.3.11 code */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ /* XXX Fail until we decide that we want to support logins. */
+ return (UPAP_AUTHNAK);
+}
+#endif
+
+
+
+/*
+ * plogout - Logout the user.
+ */
+static void
+plogout(void)
+{
+ logged_in = 0;
+}
+
+/*
+ * null_login - Check if a username of "" and a password of "" are
+ * acceptable, and iff so, set the list of acceptable IP addresses
+ * and return 1.
+ */
+static int
+null_login(int unit)
+{
+ LWIP_UNUSED_ARG(unit);
+ /* XXX Fail until we decide that we want to support logins. */
+ return 0;
+}
+
+
+/*
+ * get_pap_passwd - get a password for authenticating ourselves with
+ * our peer using PAP. Returns 1 on success, 0 if no suitable password
+ * could be found.
+ */
+static int
+get_pap_passwd(int unit, char *user, char *passwd)
+{
+ LWIP_UNUSED_ARG(unit);
+/* normally we would reject PAP if no password is provided,
+ but this causes problems with some providers (like CHT in Taiwan)
+ who incorrectly request PAP and expect a bogus/empty password, so
+ always provide a default user/passwd of "none"/"none"
+
+ @todo: This should be configured by the user, instead of being hardcoded here!
+*/
+ if(user) {
+ strcpy(user, "none");
+ }
+ if(passwd) {
+ strcpy(passwd, "none");
+ }
+ return 1;
+}
+
+/*
+ * have_pap_secret - check whether we have a PAP file with any
+ * secrets that we could possibly use for authenticating the peer.
+ */
+static int
+have_pap_secret(void)
+{
+ /* XXX Fail until we set up our passwords. */
+ return 0;
+}
+
+/*
+ * have_chap_secret - check whether we have a CHAP file with a
+ * secret that we could possibly use for authenticating `client'
+ * on `server'. Either can be the null string, meaning we don't
+ * know the identity yet.
+ */
+static int
+have_chap_secret(char *client, char *server, u32_t remote)
+{
+ LWIP_UNUSED_ARG(client);
+ LWIP_UNUSED_ARG(server);
+ LWIP_UNUSED_ARG(remote);
+
+ /* XXX Fail until we set up our passwords. */
+ return 0;
+}
+#if CHAP_SUPPORT
+
+/*
+ * get_secret - open the CHAP secret file and return the secret
+ * for authenticating the given client on the given server.
+ * (We could be either client or server).
+ */
+int
+get_secret(int unit, char *client, char *server, char *secret, int *secret_len, int save_addrs)
+{
+#if 1
+ int len;
+ struct wordlist *addrs;
+
+ LWIP_UNUSED_ARG(unit);
+ LWIP_UNUSED_ARG(server);
+ LWIP_UNUSED_ARG(save_addrs);
+
+ addrs = NULL;
+
+ if(!client || !client[0] || strcmp(client, ppp_settings.user)) {
+ return 0;
+ }
+
+ len = (int)strlen(ppp_settings.passwd);
+ if (len > MAXSECRETLEN) {
+ AUTHDEBUG(LOG_ERR, ("Secret for %s on %s is too long\n", client, server));
+ len = MAXSECRETLEN;
+ }
+
+ BCOPY(ppp_settings.passwd, secret, len);
+ *secret_len = len;
+
+ return 1;
+#else
+ int ret = 0, len;
+ struct wordlist *addrs;
+ char secbuf[MAXWORDLEN];
+
+ addrs = NULL;
+ secbuf[0] = 0;
+
+ /* XXX Find secret. */
+ if (ret < 0) {
+ return 0;
+ }
+
+ if (save_addrs) {
+ set_allowed_addrs(unit, addrs);
+ }
+
+ len = strlen(secbuf);
+ if (len > MAXSECRETLEN) {
+ AUTHDEBUG(LOG_ERR, ("Secret for %s on %s is too long\n", client, server));
+ len = MAXSECRETLEN;
+ }
+
+ BCOPY(secbuf, secret, len);
+ BZERO(secbuf, sizeof(secbuf));
+ *secret_len = len;
+
+ return 1;
+#endif
+}
+#endif /* CHAP_SUPPORT */
+
+
+#if 0 /* PAP_SUPPORT || CHAP_SUPPORT */
+/*
+ * set_allowed_addrs() - set the list of allowed addresses.
+ */
+static void
+set_allowed_addrs(int unit, struct wordlist *addrs)
+{
+ if (addresses[unit] != NULL) {
+ free_wordlist(addresses[unit]);
+ }
+ addresses[unit] = addrs;
+
+#if 0
+ /*
+ * If there's only one authorized address we might as well
+ * ask our peer for that one right away
+ */
+ if (addrs != NULL && addrs->next == NULL) {
+ char *p = addrs->word;
+ struct ipcp_options *wo = &ipcp_wantoptions[unit];
+ u32_t a;
+ struct hostent *hp;
+
+ if (wo->hisaddr == 0 && *p != '!' && *p != '-' && strchr(p, '/') == NULL) {
+ hp = gethostbyname(p);
+ if (hp != NULL && hp->h_addrtype == AF_INET) {
+ a = *(u32_t *)hp->h_addr;
+ } else {
+ a = inet_addr(p);
+ }
+ if (a != (u32_t) -1) {
+ wo->hisaddr = a;
+ }
+ }
+ }
+#endif
+}
+#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */
+
+/*
+ * auth_ip_addr - check whether the peer is authorized to use
+ * a given IP address. Returns 1 if authorized, 0 otherwise.
+ */
+int
+auth_ip_addr(int unit, u32_t addr)
+{
+ return ip_addr_check(addr, addresses[unit]);
+}
+
+static int /* @todo: integrate this funtion into auth_ip_addr()*/
+ip_addr_check(u32_t addr, struct wordlist *addrs)
+{
+ /* don't allow loopback or multicast address */
+ if (bad_ip_adrs(addr)) {
+ return 0;
+ }
+
+ if (addrs == NULL) {
+ return !ppp_settings.auth_required; /* no addresses authorized */
+ }
+
+ /* XXX All other addresses allowed. */
+ return 1;
+}
+
+/*
+ * bad_ip_adrs - return 1 if the IP address is one we don't want
+ * to use, such as an address in the loopback net or a multicast address.
+ * addr is in network byte order.
+ */
+int
+bad_ip_adrs(u32_t addr)
+{
+ addr = ntohl(addr);
+ return (addr >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET
+ || IN_MULTICAST(addr) || IN_BADCLASS(addr);
+}
+
+#if 0 /* UNUSED */ /* PAP_SUPPORT || CHAP_SUPPORT */
+/*
+ * some_ip_ok - check a wordlist to see if it authorizes any
+ * IP address(es).
+ */
+static int
+some_ip_ok(struct wordlist *addrs)
+{
+ for (; addrs != 0; addrs = addrs->next) {
+ if (addrs->word[0] == '-')
+ break;
+ if (addrs->word[0] != '!')
+ return 1; /* some IP address is allowed */
+ }
+ return 0;
+}
+
+/*
+ * check_access - complain if a secret file has too-liberal permissions.
+ */
+static void
+check_access(FILE *f, char *filename)
+{
+ struct stat sbuf;
+
+ if (fstat(fileno(f), &sbuf) < 0) {
+ warn("cannot stat secret file %s: %m", filename);
+ } else if ((sbuf.st_mode & (S_IRWXG | S_IRWXO)) != 0) {
+ warn("Warning - secret file %s has world and/or group access",
+ filename);
+ }
+}
+
+
+/*
+ * scan_authfile - Scan an authorization file for a secret suitable
+ * for authenticating `client' on `server'. The return value is -1
+ * if no secret is found, otherwise >= 0. The return value has
+ * NONWILD_CLIENT set if the secret didn't have "*" for the client, and
+ * NONWILD_SERVER set if the secret didn't have "*" for the server.
+ * Any following words on the line up to a "--" (i.e. address authorization
+ * info) are placed in a wordlist and returned in *addrs. Any
+ * following words (extra options) are placed in a wordlist and
+ * returned in *opts.
+ * We assume secret is NULL or points to MAXWORDLEN bytes of space.
+ */
+static int
+scan_authfile(FILE *f, char *client, char *server, char *secret, struct wordlist **addrs, struct wordlist **opts, char *filename)
+{
+ /* We do not (currently) need this in lwip */
+ return 0; /* dummy */
+}
+/*
+ * free_wordlist - release memory allocated for a wordlist.
+ */
+static void
+free_wordlist(struct wordlist *wp)
+{
+ struct wordlist *next;
+
+ while (wp != NULL) {
+ next = wp->next;
+ free(wp);
+ wp = next;
+ }
+}
+
+/*
+ * auth_script_done - called when the auth-up or auth-down script
+ * has finished.
+ */
+static void
+auth_script_done(void *arg)
+{
+ auth_script_pid = 0;
+ switch (auth_script_state) {
+ case s_up:
+ if (auth_state == s_down) {
+ auth_script_state = s_down;
+ auth_script(_PATH_AUTHDOWN);
+ }
+ break;
+ case s_down:
+ if (auth_state == s_up) {
+ auth_script_state = s_up;
+ auth_script(_PATH_AUTHUP);
+ }
+ break;
+ }
+}
+
+/*
+ * auth_script - execute a script with arguments
+ * interface-name peer-name real-user tty speed
+ */
+static void
+auth_script(char *script)
+{
+ char strspeed[32];
+ struct passwd *pw;
+ char struid[32];
+ char *user_name;
+ char *argv[8];
+
+ if ((pw = getpwuid(getuid())) != NULL && pw->pw_name != NULL)
+ user_name = pw->pw_name;
+ else {
+ slprintf(struid, sizeof(struid), "%d", getuid());
+ user_name = struid;
+ }
+ slprintf(strspeed, sizeof(strspeed), "%d", baud_rate);
+
+ argv[0] = script;
+ argv[1] = ifname;
+ argv[2] = peer_authname;
+ argv[3] = user_name;
+ argv[4] = devnam;
+ argv[5] = strspeed;
+ argv[6] = NULL;
+
+ auth_script_pid = run_program(script, argv, 0, auth_script_done, NULL);
+}
+#endif /* 0 */ /* PAP_SUPPORT || CHAP_SUPPORT */
+#endif /* PPP_SUPPORT */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/auth.h b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/auth.h
new file mode 100644
index 00000000..a8069ec4
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/auth.h
@@ -0,0 +1,111 @@
+/*****************************************************************************
+* auth.h - PPP Authentication and phase control header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1998 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original derived from BSD pppd.h.
+*****************************************************************************/
+/*
+ * pppd.h - PPP daemon global declarations.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+
+#ifndef AUTH_H
+#define AUTH_H
+
+/***********************
+*** PUBLIC FUNCTIONS ***
+***********************/
+
+/* we are starting to use the link */
+void link_required (int);
+
+/* we are finished with the link */
+void link_terminated (int);
+
+/* the LCP layer has left the Opened state */
+void link_down (int);
+
+/* the link is up; authenticate now */
+void link_established (int);
+
+/* a network protocol has come up */
+void np_up (int, u16_t);
+
+/* a network protocol has gone down */
+void np_down (int, u16_t);
+
+/* a network protocol no longer needs link */
+void np_finished (int, u16_t);
+
+/* peer failed to authenticate itself */
+void auth_peer_fail (int, u16_t);
+
+/* peer successfully authenticated itself */
+void auth_peer_success (int, u16_t, char *, int);
+
+/* we failed to authenticate ourselves */
+void auth_withpeer_fail (int, u16_t);
+
+/* we successfully authenticated ourselves */
+void auth_withpeer_success (int, u16_t);
+
+/* check authentication options supplied */
+void auth_check_options (void);
+
+/* check what secrets we have */
+void auth_reset (int);
+
+/* Check peer-supplied username/password */
+u_char check_passwd (int, char *, int, char *, int, char **, int *);
+
+/* get "secret" for chap */
+int get_secret (int, char *, char *, char *, int *, int);
+
+/* check if IP address is authorized */
+int auth_ip_addr (int, u32_t);
+
+/* check if IP address is unreasonable */
+int bad_ip_adrs (u32_t);
+
+#endif /* AUTH_H */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/chap.c b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/chap.c
new file mode 100644
index 00000000..f10e27d2
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/chap.c
@@ -0,0 +1,908 @@
+/*** WARNING - THIS HAS NEVER BEEN FINISHED ***/
+/*****************************************************************************
+* chap.c - Network Challenge Handshake Authentication Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original based on BSD chap.c.
+*****************************************************************************/
+/*
+ * chap.c - Challenge Handshake Authentication Protocol.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1991 Gregory M. Christy.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Gregory M. Christy. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if CHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "magic.h"
+#include "randm.h"
+#include "auth.h"
+#include "md5.h"
+#include "chap.h"
+#include "chpms.h"
+
+#include <string.h>
+
+#if 0 /* UNUSED */
+/*
+ * Command-line options.
+ */
+static option_t chap_option_list[] = {
+ { "chap-restart", o_int, &chap[0].timeouttime,
+ "Set timeout for CHAP" },
+ { "chap-max-challenge", o_int, &chap[0].max_transmits,
+ "Set max #xmits for challenge" },
+ { "chap-interval", o_int, &chap[0].chal_interval,
+ "Set interval for rechallenge" },
+#ifdef MSLANMAN
+ { "ms-lanman", o_bool, &ms_lanman,
+ "Use LanMan passwd when using MS-CHAP", 1 },
+#endif
+ { NULL }
+};
+#endif /* UNUSED */
+
+/*
+ * Protocol entry points.
+ */
+static void ChapInit (int);
+static void ChapLowerUp (int);
+static void ChapLowerDown (int);
+static void ChapInput (int, u_char *, int);
+static void ChapProtocolReject (int);
+#if PPP_ADDITIONAL_CALLBACKS
+static int ChapPrintPkt (u_char *, int, void (*) (void *, char *, ...), void *);
+#endif
+
+struct protent chap_protent = {
+ PPP_CHAP,
+ ChapInit,
+ ChapInput,
+ ChapProtocolReject,
+ ChapLowerUp,
+ ChapLowerDown,
+ NULL,
+ NULL,
+#if PPP_ADDITIONAL_CALLBACKS
+ ChapPrintPkt,
+ NULL,
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+ 1,
+ "CHAP",
+#if PPP_ADDITIONAL_CALLBACKS
+ NULL,
+ NULL,
+ NULL
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+};
+
+chap_state chap[NUM_PPP]; /* CHAP state; one for each unit */
+
+static void ChapChallengeTimeout (void *);
+static void ChapResponseTimeout (void *);
+static void ChapReceiveChallenge (chap_state *, u_char *, u_char, int);
+static void ChapRechallenge (void *);
+static void ChapReceiveResponse (chap_state *, u_char *, int, int);
+static void ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len);
+static void ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len);
+static void ChapSendStatus (chap_state *, int);
+static void ChapSendChallenge (chap_state *);
+static void ChapSendResponse (chap_state *);
+static void ChapGenChallenge (chap_state *);
+
+/*
+ * ChapInit - Initialize a CHAP unit.
+ */
+static void
+ChapInit(int unit)
+{
+ chap_state *cstate = &chap[unit];
+
+ BZERO(cstate, sizeof(*cstate));
+ cstate->unit = unit;
+ cstate->clientstate = CHAPCS_INITIAL;
+ cstate->serverstate = CHAPSS_INITIAL;
+ cstate->timeouttime = CHAP_DEFTIMEOUT;
+ cstate->max_transmits = CHAP_DEFTRANSMITS;
+ /* random number generator is initialized in magic_init */
+}
+
+
+/*
+ * ChapAuthWithPeer - Authenticate us with our peer (start client).
+ *
+ */
+void
+ChapAuthWithPeer(int unit, char *our_name, u_char digest)
+{
+ chap_state *cstate = &chap[unit];
+
+ cstate->resp_name = our_name;
+ cstate->resp_type = digest;
+
+ if (cstate->clientstate == CHAPCS_INITIAL ||
+ cstate->clientstate == CHAPCS_PENDING) {
+ /* lower layer isn't up - wait until later */
+ cstate->clientstate = CHAPCS_PENDING;
+ return;
+ }
+
+ /*
+ * We get here as a result of LCP coming up.
+ * So even if CHAP was open before, we will
+ * have to re-authenticate ourselves.
+ */
+ cstate->clientstate = CHAPCS_LISTEN;
+}
+
+
+/*
+ * ChapAuthPeer - Authenticate our peer (start server).
+ */
+void
+ChapAuthPeer(int unit, char *our_name, u_char digest)
+{
+ chap_state *cstate = &chap[unit];
+
+ cstate->chal_name = our_name;
+ cstate->chal_type = digest;
+
+ if (cstate->serverstate == CHAPSS_INITIAL ||
+ cstate->serverstate == CHAPSS_PENDING) {
+ /* lower layer isn't up - wait until later */
+ cstate->serverstate = CHAPSS_PENDING;
+ return;
+ }
+
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate); /* crank it up dude! */
+ cstate->serverstate = CHAPSS_INITIAL_CHAL;
+}
+
+
+/*
+ * ChapChallengeTimeout - Timeout expired on sending challenge.
+ */
+static void
+ChapChallengeTimeout(void *arg)
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending challenges, don't worry. then again we */
+ /* probably shouldn't be here either */
+ if (cstate->serverstate != CHAPSS_INITIAL_CHAL &&
+ cstate->serverstate != CHAPSS_RECHALLENGE) {
+ return;
+ }
+
+ if (cstate->chal_transmits >= cstate->max_transmits) {
+ /* give up on peer */
+ CHAPDEBUG(LOG_ERR, ("Peer failed to respond to CHAP challenge\n"));
+ cstate->serverstate = CHAPSS_BADAUTH;
+ auth_peer_fail(cstate->unit, PPP_CHAP);
+ return;
+ }
+
+ ChapSendChallenge(cstate); /* Re-send challenge */
+}
+
+
+/*
+ * ChapResponseTimeout - Timeout expired on sending response.
+ */
+static void
+ChapResponseTimeout(void *arg)
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending a response, don't worry. */
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ return;
+ }
+
+ ChapSendResponse(cstate); /* re-send response */
+}
+
+
+/*
+ * ChapRechallenge - Time to challenge the peer again.
+ */
+static void
+ChapRechallenge(void *arg)
+{
+ chap_state *cstate = (chap_state *) arg;
+
+ /* if we aren't sending a response, don't worry. */
+ if (cstate->serverstate != CHAPSS_OPEN) {
+ return;
+ }
+
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate);
+ cstate->serverstate = CHAPSS_RECHALLENGE;
+}
+
+
+/*
+ * ChapLowerUp - The lower layer is up.
+ *
+ * Start up if we have pending requests.
+ */
+static void
+ChapLowerUp(int unit)
+{
+ chap_state *cstate = &chap[unit];
+
+ if (cstate->clientstate == CHAPCS_INITIAL) {
+ cstate->clientstate = CHAPCS_CLOSED;
+ } else if (cstate->clientstate == CHAPCS_PENDING) {
+ cstate->clientstate = CHAPCS_LISTEN;
+ }
+
+ if (cstate->serverstate == CHAPSS_INITIAL) {
+ cstate->serverstate = CHAPSS_CLOSED;
+ } else if (cstate->serverstate == CHAPSS_PENDING) {
+ ChapGenChallenge(cstate);
+ ChapSendChallenge(cstate);
+ cstate->serverstate = CHAPSS_INITIAL_CHAL;
+ }
+}
+
+
+/*
+ * ChapLowerDown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void
+ChapLowerDown(int unit)
+{
+ chap_state *cstate = &chap[unit];
+
+ /* Timeout(s) pending? Cancel if so. */
+ if (cstate->serverstate == CHAPSS_INITIAL_CHAL ||
+ cstate->serverstate == CHAPSS_RECHALLENGE) {
+ UNTIMEOUT(ChapChallengeTimeout, cstate);
+ } else if (cstate->serverstate == CHAPSS_OPEN
+ && cstate->chal_interval != 0) {
+ UNTIMEOUT(ChapRechallenge, cstate);
+ }
+ if (cstate->clientstate == CHAPCS_RESPONSE) {
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+ }
+ cstate->clientstate = CHAPCS_INITIAL;
+ cstate->serverstate = CHAPSS_INITIAL;
+}
+
+
+/*
+ * ChapProtocolReject - Peer doesn't grok CHAP.
+ */
+static void
+ChapProtocolReject(int unit)
+{
+ chap_state *cstate = &chap[unit];
+
+ if (cstate->serverstate != CHAPSS_INITIAL &&
+ cstate->serverstate != CHAPSS_CLOSED) {
+ auth_peer_fail(unit, PPP_CHAP);
+ }
+ if (cstate->clientstate != CHAPCS_INITIAL &&
+ cstate->clientstate != CHAPCS_CLOSED) {
+ auth_withpeer_fail(unit, PPP_CHAP); /* lwip: just sets the PPP error code on this unit to PPPERR_AUTHFAIL */
+ }
+ ChapLowerDown(unit); /* shutdown chap */
+}
+
+
+/*
+ * ChapInput - Input CHAP packet.
+ */
+static void
+ChapInput(int unit, u_char *inpacket, int packet_len)
+{
+ chap_state *cstate = &chap[unit];
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (packet_len < CHAP_HEADERLEN) {
+ CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd short header.\n"));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < CHAP_HEADERLEN) {
+ CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd illegal length.\n"));
+ return;
+ }
+ if (len > packet_len) {
+ CHAPDEBUG(LOG_INFO, ("ChapInput: rcvd short packet.\n"));
+ return;
+ }
+ len -= CHAP_HEADERLEN;
+
+ /*
+ * Action depends on code (as in fact it usually does :-).
+ */
+ switch (code) {
+ case CHAP_CHALLENGE:
+ ChapReceiveChallenge(cstate, inp, id, len);
+ break;
+
+ case CHAP_RESPONSE:
+ ChapReceiveResponse(cstate, inp, id, len);
+ break;
+
+ case CHAP_FAILURE:
+ ChapReceiveFailure(cstate, inp, id, len);
+ break;
+
+ case CHAP_SUCCESS:
+ ChapReceiveSuccess(cstate, inp, id, len);
+ break;
+
+ default: /* Need code reject? */
+ CHAPDEBUG(LOG_WARNING, ("Unknown CHAP code (%d) received.\n", code));
+ break;
+ }
+}
+
+
+/*
+ * ChapReceiveChallenge - Receive Challenge and send Response.
+ */
+static void
+ChapReceiveChallenge(chap_state *cstate, u_char *inp, u_char id, int len)
+{
+ int rchallenge_len;
+ u_char *rchallenge;
+ int secret_len;
+ char secret[MAXSECRETLEN];
+ char rhostname[256];
+ MD5_CTX mdContext;
+ u_char hash[MD5_SIGNATURE_SIZE];
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: Rcvd id %d.\n", id));
+ if (cstate->clientstate == CHAPCS_CLOSED ||
+ cstate->clientstate == CHAPCS_PENDING) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: in state %d\n",
+ cstate->clientstate));
+ return;
+ }
+
+ if (len < 2) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: rcvd short packet.\n"));
+ return;
+ }
+
+ GETCHAR(rchallenge_len, inp);
+ len -= sizeof (u_char) + rchallenge_len; /* now name field length */
+ if (len < 0) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: rcvd short packet.\n"));
+ return;
+ }
+ rchallenge = inp;
+ INCPTR(rchallenge_len, inp);
+
+ if (len >= (int)sizeof(rhostname)) {
+ len = sizeof(rhostname) - 1;
+ }
+ BCOPY(inp, rhostname, len);
+ rhostname[len] = '\000';
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: received name field '%s'\n",
+ rhostname));
+
+ /* Microsoft doesn't send their name back in the PPP packet */
+ if (ppp_settings.remote_name[0] != 0 && (ppp_settings.explicit_remote || rhostname[0] == 0)) {
+ strncpy(rhostname, ppp_settings.remote_name, sizeof(rhostname));
+ rhostname[sizeof(rhostname) - 1] = 0;
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveChallenge: using '%s' as remote name\n",
+ rhostname));
+ }
+
+ /* get secret for authenticating ourselves with the specified host */
+ if (!get_secret(cstate->unit, cstate->resp_name, rhostname,
+ secret, &secret_len, 0)) {
+ secret_len = 0; /* assume null secret if can't find one */
+ CHAPDEBUG(LOG_WARNING, ("No CHAP secret found for authenticating us to %s\n",
+ rhostname));
+ }
+
+ /* cancel response send timeout if necessary */
+ if (cstate->clientstate == CHAPCS_RESPONSE) {
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+ }
+
+ cstate->resp_id = id;
+ cstate->resp_transmits = 0;
+
+ /* generate MD based on negotiated type */
+ switch (cstate->resp_type) {
+
+ case CHAP_DIGEST_MD5:
+ MD5Init(&mdContext);
+ MD5Update(&mdContext, &cstate->resp_id, 1);
+ MD5Update(&mdContext, (u_char*)secret, secret_len);
+ MD5Update(&mdContext, rchallenge, rchallenge_len);
+ MD5Final(hash, &mdContext);
+ BCOPY(hash, cstate->response, MD5_SIGNATURE_SIZE);
+ cstate->resp_length = MD5_SIGNATURE_SIZE;
+ break;
+
+#if MSCHAP_SUPPORT
+ case CHAP_MICROSOFT:
+ ChapMS(cstate, rchallenge, rchallenge_len, secret, secret_len);
+ break;
+#endif
+
+ default:
+ CHAPDEBUG(LOG_INFO, ("unknown digest type %d\n", cstate->resp_type));
+ return;
+ }
+
+ BZERO(secret, sizeof(secret));
+ ChapSendResponse(cstate);
+}
+
+
+/*
+ * ChapReceiveResponse - Receive and process response.
+ */
+static void
+ChapReceiveResponse(chap_state *cstate, u_char *inp, int id, int len)
+{
+ u_char *remmd, remmd_len;
+ int secret_len, old_state;
+ int code;
+ char rhostname[256];
+ MD5_CTX mdContext;
+ char secret[MAXSECRETLEN];
+ u_char hash[MD5_SIGNATURE_SIZE];
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: Rcvd id %d.\n", id));
+
+ if (cstate->serverstate == CHAPSS_CLOSED ||
+ cstate->serverstate == CHAPSS_PENDING) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: in state %d\n",
+ cstate->serverstate));
+ return;
+ }
+
+ if (id != cstate->chal_id) {
+ return; /* doesn't match ID of last challenge */
+ }
+
+ /*
+ * If we have received a duplicate or bogus Response,
+ * we have to send the same answer (Success/Failure)
+ * as we did for the first Response we saw.
+ */
+ if (cstate->serverstate == CHAPSS_OPEN) {
+ ChapSendStatus(cstate, CHAP_SUCCESS);
+ return;
+ }
+ if (cstate->serverstate == CHAPSS_BADAUTH) {
+ ChapSendStatus(cstate, CHAP_FAILURE);
+ return;
+ }
+
+ if (len < 2) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: rcvd short packet.\n"));
+ return;
+ }
+ GETCHAR(remmd_len, inp); /* get length of MD */
+ remmd = inp; /* get pointer to MD */
+ INCPTR(remmd_len, inp);
+
+ len -= sizeof (u_char) + remmd_len;
+ if (len < 0) {
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: rcvd short packet.\n"));
+ return;
+ }
+
+ UNTIMEOUT(ChapChallengeTimeout, cstate);
+
+ if (len >= (int)sizeof(rhostname)) {
+ len = sizeof(rhostname) - 1;
+ }
+ BCOPY(inp, rhostname, len);
+ rhostname[len] = '\000';
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveResponse: received name field: %s\n",
+ rhostname));
+
+ /*
+ * Get secret for authenticating them with us,
+ * do the hash ourselves, and compare the result.
+ */
+ code = CHAP_FAILURE;
+ if (!get_secret(cstate->unit, rhostname, cstate->chal_name,
+ secret, &secret_len, 1)) {
+ CHAPDEBUG(LOG_WARNING, ("No CHAP secret found for authenticating %s\n",
+ rhostname));
+ } else {
+ /* generate MD based on negotiated type */
+ switch (cstate->chal_type) {
+
+ case CHAP_DIGEST_MD5: /* only MD5 is defined for now */
+ if (remmd_len != MD5_SIGNATURE_SIZE) {
+ break; /* it's not even the right length */
+ }
+ MD5Init(&mdContext);
+ MD5Update(&mdContext, &cstate->chal_id, 1);
+ MD5Update(&mdContext, (u_char*)secret, secret_len);
+ MD5Update(&mdContext, cstate->challenge, cstate->chal_len);
+ MD5Final(hash, &mdContext);
+
+ /* compare local and remote MDs and send the appropriate status */
+ if (memcmp (hash, remmd, MD5_SIGNATURE_SIZE) == 0) {
+ code = CHAP_SUCCESS; /* they are the same! */
+ }
+ break;
+
+ default:
+ CHAPDEBUG(LOG_INFO, ("unknown digest type %d\n", cstate->chal_type));
+ }
+ }
+
+ BZERO(secret, sizeof(secret));
+ ChapSendStatus(cstate, code);
+
+ if (code == CHAP_SUCCESS) {
+ old_state = cstate->serverstate;
+ cstate->serverstate = CHAPSS_OPEN;
+ if (old_state == CHAPSS_INITIAL_CHAL) {
+ auth_peer_success(cstate->unit, PPP_CHAP, rhostname, len);
+ }
+ if (cstate->chal_interval != 0) {
+ TIMEOUT(ChapRechallenge, cstate, cstate->chal_interval);
+ }
+ } else {
+ CHAPDEBUG(LOG_ERR, ("CHAP peer authentication failed\n"));
+ cstate->serverstate = CHAPSS_BADAUTH;
+ auth_peer_fail(cstate->unit, PPP_CHAP);
+ }
+}
+
+/*
+ * ChapReceiveSuccess - Receive Success
+ */
+static void
+ChapReceiveSuccess(chap_state *cstate, u_char *inp, u_char id, int len)
+{
+ LWIP_UNUSED_ARG(id);
+ LWIP_UNUSED_ARG(inp);
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveSuccess: Rcvd id %d.\n", id));
+
+ if (cstate->clientstate == CHAPCS_OPEN) {
+ /* presumably an answer to a duplicate response */
+ return;
+ }
+
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ /* don't know what this is */
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveSuccess: in state %d\n",
+ cstate->clientstate));
+ return;
+ }
+
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ /*
+ * Print message.
+ */
+ if (len > 0) {
+ PRINTMSG(inp, len);
+ }
+
+ cstate->clientstate = CHAPCS_OPEN;
+
+ auth_withpeer_success(cstate->unit, PPP_CHAP);
+}
+
+
+/*
+ * ChapReceiveFailure - Receive failure.
+ */
+static void
+ChapReceiveFailure(chap_state *cstate, u_char *inp, u_char id, int len)
+{
+ LWIP_UNUSED_ARG(id);
+ LWIP_UNUSED_ARG(inp);
+
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveFailure: Rcvd id %d.\n", id));
+
+ if (cstate->clientstate != CHAPCS_RESPONSE) {
+ /* don't know what this is */
+ CHAPDEBUG(LOG_INFO, ("ChapReceiveFailure: in state %d\n",
+ cstate->clientstate));
+ return;
+ }
+
+ UNTIMEOUT(ChapResponseTimeout, cstate);
+
+ /*
+ * Print message.
+ */
+ if (len > 0) {
+ PRINTMSG(inp, len);
+ }
+
+ CHAPDEBUG(LOG_ERR, ("CHAP authentication failed\n"));
+ auth_withpeer_fail(cstate->unit, PPP_CHAP); /* lwip: just sets the PPP error code on this unit to PPPERR_AUTHFAIL */
+}
+
+
+/*
+ * ChapSendChallenge - Send an Authenticate challenge.
+ */
+static void
+ChapSendChallenge(chap_state *cstate)
+{
+ u_char *outp;
+ int chal_len, name_len;
+ int outlen;
+
+ chal_len = cstate->chal_len;
+ name_len = (int)strlen(cstate->chal_name);
+ outlen = CHAP_HEADERLEN + sizeof (u_char) + chal_len + name_len;
+ outp = outpacket_buf[cstate->unit];
+
+ MAKEHEADER(outp, PPP_CHAP); /* paste in a CHAP header */
+
+ PUTCHAR(CHAP_CHALLENGE, outp);
+ PUTCHAR(cstate->chal_id, outp);
+ PUTSHORT(outlen, outp);
+
+ PUTCHAR(chal_len, outp); /* put length of challenge */
+ BCOPY(cstate->challenge, outp, chal_len);
+ INCPTR(chal_len, outp);
+
+ BCOPY(cstate->chal_name, outp, name_len); /* append hostname */
+
+ pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN);
+
+ CHAPDEBUG(LOG_INFO, ("ChapSendChallenge: Sent id %d.\n", cstate->chal_id));
+
+ TIMEOUT(ChapChallengeTimeout, cstate, cstate->timeouttime);
+ ++cstate->chal_transmits;
+}
+
+
+/*
+ * ChapSendStatus - Send a status response (ack or nak).
+ */
+static void
+ChapSendStatus(chap_state *cstate, int code)
+{
+ u_char *outp;
+ int outlen, msglen;
+ char msg[256]; /* @todo: this can be a char*, no strcpy needed */
+
+ if (code == CHAP_SUCCESS) {
+ strcpy(msg, "Welcome!");
+ } else {
+ strcpy(msg, "I don't like you. Go 'way.");
+ }
+ msglen = (int)strlen(msg);
+
+ outlen = CHAP_HEADERLEN + msglen;
+ outp = outpacket_buf[cstate->unit];
+
+ MAKEHEADER(outp, PPP_CHAP); /* paste in a header */
+
+ PUTCHAR(code, outp);
+ PUTCHAR(cstate->chal_id, outp);
+ PUTSHORT(outlen, outp);
+ BCOPY(msg, outp, msglen);
+ pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN);
+
+ CHAPDEBUG(LOG_INFO, ("ChapSendStatus: Sent code %d, id %d.\n", code,
+ cstate->chal_id));
+}
+
+/*
+ * ChapGenChallenge is used to generate a pseudo-random challenge string of
+ * a pseudo-random length between min_len and max_len. The challenge
+ * string and its length are stored in *cstate, and various other fields of
+ * *cstate are initialized.
+ */
+
+static void
+ChapGenChallenge(chap_state *cstate)
+{
+ int chal_len;
+ u_char *ptr = cstate->challenge;
+ int i;
+
+ /* pick a random challenge length between MIN_CHALLENGE_LENGTH and
+ MAX_CHALLENGE_LENGTH */
+ chal_len = (unsigned)
+ ((((magic() >> 16) *
+ (MAX_CHALLENGE_LENGTH - MIN_CHALLENGE_LENGTH)) >> 16)
+ + MIN_CHALLENGE_LENGTH);
+ LWIP_ASSERT("chal_len <= 0xff", chal_len <= 0xffff);
+ cstate->chal_len = (u_char)chal_len;
+ cstate->chal_id = ++cstate->id;
+ cstate->chal_transmits = 0;
+
+ /* generate a random string */
+ for (i = 0; i < chal_len; i++ ) {
+ *ptr++ = (char) (magic() & 0xff);
+ }
+}
+
+/*
+ * ChapSendResponse - send a response packet with values as specified
+ * in *cstate.
+ */
+/* ARGSUSED */
+static void
+ChapSendResponse(chap_state *cstate)
+{
+ u_char *outp;
+ int outlen, md_len, name_len;
+
+ md_len = cstate->resp_length;
+ name_len = (int)strlen(cstate->resp_name);
+ outlen = CHAP_HEADERLEN + sizeof (u_char) + md_len + name_len;
+ outp = outpacket_buf[cstate->unit];
+
+ MAKEHEADER(outp, PPP_CHAP);
+
+ PUTCHAR(CHAP_RESPONSE, outp); /* we are a response */
+ PUTCHAR(cstate->resp_id, outp); /* copy id from challenge packet */
+ PUTSHORT(outlen, outp); /* packet length */
+
+ PUTCHAR(md_len, outp); /* length of MD */
+ BCOPY(cstate->response, outp, md_len); /* copy MD to buffer */
+ INCPTR(md_len, outp);
+
+ BCOPY(cstate->resp_name, outp, name_len); /* append our name */
+
+ /* send the packet */
+ pppWrite(cstate->unit, outpacket_buf[cstate->unit], outlen + PPP_HDRLEN);
+
+ cstate->clientstate = CHAPCS_RESPONSE;
+ TIMEOUT(ChapResponseTimeout, cstate, cstate->timeouttime);
+ ++cstate->resp_transmits;
+}
+
+#if PPP_ADDITIONAL_CALLBACKS
+static char *ChapCodenames[] = {
+ "Challenge", "Response", "Success", "Failure"
+};
+/*
+ * ChapPrintPkt - print the contents of a CHAP packet.
+ */
+static int
+ChapPrintPkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg)
+{
+ int code, id, len;
+ int clen, nlen;
+ u_char x;
+
+ if (plen < CHAP_HEADERLEN) {
+ return 0;
+ }
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < CHAP_HEADERLEN || len > plen) {
+ return 0;
+ }
+
+ if (code >= 1 && code <= sizeof(ChapCodenames) / sizeof(char *)) {
+ printer(arg, " %s", ChapCodenames[code-1]);
+ } else {
+ printer(arg, " code=0x%x", code);
+ }
+ printer(arg, " id=0x%x", id);
+ len -= CHAP_HEADERLEN;
+ switch (code) {
+ case CHAP_CHALLENGE:
+ case CHAP_RESPONSE:
+ if (len < 1) {
+ break;
+ }
+ clen = p[0];
+ if (len < clen + 1) {
+ break;
+ }
+ ++p;
+ nlen = len - clen - 1;
+ printer(arg, " <");
+ for (; clen > 0; --clen) {
+ GETCHAR(x, p);
+ printer(arg, "%.2x", x);
+ }
+ printer(arg, ">, name = %.*Z", nlen, p);
+ break;
+ case CHAP_FAILURE:
+ case CHAP_SUCCESS:
+ printer(arg, " %.*Z", len, p);
+ break;
+ default:
+ for (clen = len; clen > 0; --clen) {
+ GETCHAR(x, p);
+ printer(arg, " %.2x", x);
+ }
+ }
+
+ return len + CHAP_HEADERLEN;
+}
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+
+#endif /* CHAP_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/chap.h b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/chap.h
new file mode 100644
index 00000000..884f6a39
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/chap.h
@@ -0,0 +1,150 @@
+/*****************************************************************************
+* chap.h - Network Challenge Handshake Authentication Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1998 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-03 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original built from BSD network code.
+******************************************************************************/
+/*
+ * chap.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1993 The Australian National University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the Australian National University. The name of the University
+ * may not be used to endorse or promote products derived from this
+ * software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Copyright (c) 1991 Gregory M. Christy
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the author.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: chap.h $
+ */
+
+#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/src/VBox/Devices/Network/lwip-new/src/netif/ppp/chpms.c b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/chpms.c
new file mode 100644
index 00000000..81a887b8
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/chpms.c
@@ -0,0 +1,396 @@
+/*** WARNING - THIS CODE HAS NOT BEEN FINISHED! ***/
+/*** The original PPPD code is written in a way to require either the UNIX DES
+ encryption functions encrypt(3) and setkey(3) or the DES library libdes.
+ Since both is not included in lwIP, MSCHAP currently does not work! */
+/*****************************************************************************
+* chpms.c - Network MicroSoft Challenge Handshake Authentication Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* Copyright (c) 1997 by Global Election Systems Inc. All rights reserved.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-08 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original based on BSD chap_ms.c.
+*****************************************************************************/
+/*
+ * chap_ms.c - Microsoft MS-CHAP compatible implementation.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Eric Rosenquist. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * Modifications by Lauri Pesonen / lpesonen@clinet.fi, april 1997
+ *
+ * Implemented LANManager type password response to MS-CHAP challenges.
+ * Now pppd provides both NT style and LANMan style blocks, and the
+ * prefered is set by option "ms-lanman". Default is to use NT.
+ * The hash text (StdText) was taken from Win95 RASAPI32.DLL.
+ *
+ * You should also use DOMAIN\\USERNAME as described in README.MSCHAP80
+ */
+
+#define USE_CRYPT
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if MSCHAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "md4.h"
+#ifndef USE_CRYPT
+#include "des.h"
+#endif
+#include "chap.h"
+#include "chpms.h"
+
+#include <string.h>
+
+
+/*************************/
+/*** LOCAL DEFINITIONS ***/
+/*************************/
+
+
+/************************/
+/*** LOCAL DATA TYPES ***/
+/************************/
+typedef struct {
+ u_char LANManResp[24];
+ u_char NTResp[24];
+ u_char UseNT; /* If 1, ignore the LANMan response field */
+} MS_ChapResponse;
+/* We use MS_CHAP_RESPONSE_LEN, rather than sizeof(MS_ChapResponse),
+ in case this struct gets padded. */
+
+
+
+/***********************************/
+/*** LOCAL FUNCTION DECLARATIONS ***/
+/***********************************/
+
+/* XXX Don't know what to do with these. */
+extern void setkey(const char *);
+extern void encrypt(char *, int);
+
+static void DesEncrypt (u_char *, u_char *, u_char *);
+static void MakeKey (u_char *, u_char *);
+
+#ifdef USE_CRYPT
+static void Expand (u_char *, u_char *);
+static void Collapse (u_char *, u_char *);
+#endif
+
+static void ChallengeResponse(
+ u_char *challenge, /* IN 8 octets */
+ u_char *pwHash, /* IN 16 octets */
+ u_char *response /* OUT 24 octets */
+);
+static void ChapMS_NT(
+ char *rchallenge,
+ int rchallenge_len,
+ char *secret,
+ int secret_len,
+ MS_ChapResponse *response
+);
+static u_char Get7Bits(
+ u_char *input,
+ int startBit
+);
+
+static void
+ChallengeResponse( u_char *challenge, /* IN 8 octets */
+ u_char *pwHash, /* IN 16 octets */
+ u_char *response /* OUT 24 octets */)
+{
+ u_char ZPasswordHash[21];
+
+ BZERO(ZPasswordHash, sizeof(ZPasswordHash));
+ BCOPY(pwHash, ZPasswordHash, 16);
+
+#if 0
+ log_packet(ZPasswordHash, sizeof(ZPasswordHash), "ChallengeResponse - ZPasswordHash", LOG_DEBUG);
+#endif
+
+ DesEncrypt(challenge, ZPasswordHash + 0, response + 0);
+ DesEncrypt(challenge, ZPasswordHash + 7, response + 8);
+ DesEncrypt(challenge, ZPasswordHash + 14, response + 16);
+
+#if 0
+ log_packet(response, 24, "ChallengeResponse - response", LOG_DEBUG);
+#endif
+}
+
+
+#ifdef USE_CRYPT
+static void
+DesEncrypt( u_char *clear, /* IN 8 octets */
+ u_char *key, /* IN 7 octets */
+ u_char *cipher /* OUT 8 octets */)
+{
+ u_char des_key[8];
+ u_char crypt_key[66];
+ u_char des_input[66];
+
+ MakeKey(key, des_key);
+
+ Expand(des_key, crypt_key);
+ setkey((char*)crypt_key);
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n",
+ clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7]));
+#endif
+
+ Expand(clear, des_input);
+ encrypt((char*)des_input, 0);
+ Collapse(des_input, cipher);
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n",
+ cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7]));
+#endif
+}
+
+#else /* USE_CRYPT */
+
+static void
+DesEncrypt( u_char *clear, /* IN 8 octets */
+ u_char *key, /* IN 7 octets */
+ u_char *cipher /* OUT 8 octets */)
+{
+ des_cblock des_key;
+ des_key_schedule key_schedule;
+
+ MakeKey(key, des_key);
+
+ des_set_key(&des_key, key_schedule);
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet input : %02X%02X%02X%02X%02X%02X%02X%02X\n",
+ clear[0], clear[1], clear[2], clear[3], clear[4], clear[5], clear[6], clear[7]));
+#endif
+
+ des_ecb_encrypt((des_cblock *)clear, (des_cblock *)cipher, key_schedule, 1);
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("DesEncrypt: 8 octet output: %02X%02X%02X%02X%02X%02X%02X%02X\n",
+ cipher[0], cipher[1], cipher[2], cipher[3], cipher[4], cipher[5], cipher[6], cipher[7]));
+#endif
+}
+
+#endif /* USE_CRYPT */
+
+
+static u_char
+Get7Bits( u_char *input, int startBit)
+{
+ register unsigned int word;
+
+ word = (unsigned)input[startBit / 8] << 8;
+ word |= (unsigned)input[startBit / 8 + 1];
+
+ word >>= 15 - (startBit % 8 + 7);
+
+ return word & 0xFE;
+}
+
+#ifdef USE_CRYPT
+
+/* in == 8-byte string (expanded version of the 56-bit key)
+ * out == 64-byte string where each byte is either 1 or 0
+ * Note that the low-order "bit" is always ignored by by setkey()
+ */
+static void
+Expand(u_char *in, u_char *out)
+{
+ int j, c;
+ int i;
+
+ for(i = 0; i < 64; in++){
+ c = *in;
+ for(j = 7; j >= 0; j--) {
+ *out++ = (c >> j) & 01;
+ }
+ i += 8;
+ }
+}
+
+/* The inverse of Expand
+ */
+static void
+Collapse(u_char *in, u_char *out)
+{
+ int j;
+ int i;
+ unsigned int c;
+
+ for (i = 0; i < 64; i += 8, out++) {
+ c = 0;
+ for (j = 7; j >= 0; j--, in++) {
+ c |= *in << j;
+ }
+ *out = c & 0xff;
+ }
+}
+#endif
+
+static void
+MakeKey( u_char *key, /* IN 56 bit DES key missing parity bits */
+ u_char *des_key /* OUT 64 bit DES key with parity bits added */)
+{
+ des_key[0] = Get7Bits(key, 0);
+ des_key[1] = Get7Bits(key, 7);
+ des_key[2] = Get7Bits(key, 14);
+ des_key[3] = Get7Bits(key, 21);
+ des_key[4] = Get7Bits(key, 28);
+ des_key[5] = Get7Bits(key, 35);
+ des_key[6] = Get7Bits(key, 42);
+ des_key[7] = Get7Bits(key, 49);
+
+#ifndef USE_CRYPT
+ des_set_odd_parity((des_cblock *)des_key);
+#endif
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("MakeKey: 56-bit input : %02X%02X%02X%02X%02X%02X%02X\n",
+ key[0], key[1], key[2], key[3], key[4], key[5], key[6]));
+ CHAPDEBUG(LOG_INFO, ("MakeKey: 64-bit output: %02X%02X%02X%02X%02X%02X%02X%02X\n",
+ des_key[0], des_key[1], des_key[2], des_key[3], des_key[4], des_key[5], des_key[6], des_key[7]));
+#endif
+}
+
+static void
+ChapMS_NT( char *rchallenge,
+ int rchallenge_len,
+ char *secret,
+ int secret_len,
+ MS_ChapResponse *response)
+{
+ int i;
+ MDstruct md4Context;
+ u_char unicodePassword[MAX_NT_PASSWORD * 2];
+ static int low_byte_first = -1;
+
+ LWIP_UNUSED_ARG(rchallenge_len);
+
+ /* Initialize the Unicode version of the secret (== password). */
+ /* This implicitly supports 8-bit ISO8859/1 characters. */
+ BZERO(unicodePassword, sizeof(unicodePassword));
+ for (i = 0; i < secret_len; i++) {
+ unicodePassword[i * 2] = (u_char)secret[i];
+ }
+ MDbegin(&md4Context);
+ MDupdate(&md4Context, unicodePassword, secret_len * 2 * 8); /* Unicode is 2 bytes/char, *8 for bit count */
+
+ if (low_byte_first == -1) {
+ low_byte_first = (PP_HTONS((unsigned short int)1) != 1);
+ }
+ if (low_byte_first == 0) {
+ /* @todo: arg type - u_long* or u_int* ? */
+ MDreverse((unsigned int*)&md4Context); /* sfb 961105 */
+ }
+
+ MDupdate(&md4Context, NULL, 0); /* Tell MD4 we're done */
+
+ ChallengeResponse((u_char*)rchallenge, (u_char*)md4Context.buffer, response->NTResp);
+}
+
+#ifdef MSLANMAN
+static u_char *StdText = (u_char *)"KGS!@#$%"; /* key from rasapi32.dll */
+
+static void
+ChapMS_LANMan( char *rchallenge,
+ int rchallenge_len,
+ char *secret,
+ int secret_len,
+ MS_ChapResponse *response)
+{
+ int i;
+ u_char UcasePassword[MAX_NT_PASSWORD]; /* max is actually 14 */
+ u_char PasswordHash[16];
+
+ /* LANMan password is case insensitive */
+ BZERO(UcasePassword, sizeof(UcasePassword));
+ for (i = 0; i < secret_len; i++) {
+ UcasePassword[i] = (u_char)toupper(secret[i]);
+ }
+ DesEncrypt( StdText, UcasePassword + 0, PasswordHash + 0 );
+ DesEncrypt( StdText, UcasePassword + 7, PasswordHash + 8 );
+ ChallengeResponse(rchallenge, PasswordHash, response->LANManResp);
+}
+#endif
+
+void
+ChapMS( chap_state *cstate, char *rchallenge, int rchallenge_len, char *secret, int secret_len)
+{
+ MS_ChapResponse response;
+#ifdef MSLANMAN
+ extern int ms_lanman;
+#endif
+
+#if 0
+ CHAPDEBUG(LOG_INFO, ("ChapMS: secret is '%.*s'\n", secret_len, secret));
+#endif
+ BZERO(&response, sizeof(response));
+
+ /* Calculate both always */
+ ChapMS_NT(rchallenge, rchallenge_len, secret, secret_len, &response);
+
+#ifdef MSLANMAN
+ ChapMS_LANMan(rchallenge, rchallenge_len, secret, secret_len, &response);
+
+ /* prefered method is set by option */
+ response.UseNT = !ms_lanman;
+#else
+ response.UseNT = 1;
+#endif
+
+ BCOPY(&response, cstate->response, MS_CHAP_RESPONSE_LEN);
+ cstate->resp_length = MS_CHAP_RESPONSE_LEN;
+}
+
+#endif /* MSCHAP_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/chpms.h b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/chpms.h
new file mode 100644
index 00000000..a85d05c7
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/chpms.h
@@ -0,0 +1,64 @@
+/*****************************************************************************
+* chpms.h - Network Microsoft Challenge Handshake Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1998 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 98-01-30 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original built from BSD network code.
+******************************************************************************/
+/*
+ * chap.h - Challenge Handshake Authentication Protocol definitions.
+ *
+ * Copyright (c) 1995 Eric Rosenquist, Strata Software Limited.
+ * http://www.strataware.com/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Eric Rosenquist. The name of the author may not be used to
+ * endorse or promote products derived from this software without
+ * specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: chpms.h $
+ */
+
+#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/src/VBox/Devices/Network/lwip-new/src/netif/ppp/fsm.c b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/fsm.c
new file mode 100644
index 00000000..e8a254ed
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/fsm.c
@@ -0,0 +1,890 @@
+/*****************************************************************************
+* fsm.c - Network Control Protocol Finite State Machine program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-01 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original based on BSD fsm.c.
+*****************************************************************************/
+/*
+ * fsm.c - {Link, IP} Control Protocol Finite State Machine.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+/*
+ * TODO:
+ * Randomize fsm id on link/init.
+ * Deal with variable outgoing MTU.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "fsm.h"
+
+#include <string.h>
+
+#if PPP_DEBUG
+static const char *ppperr_strerr[] = {
+ "LS_INITIAL", /* LS_INITIAL 0 */
+ "LS_STARTING", /* LS_STARTING 1 */
+ "LS_CLOSED", /* LS_CLOSED 2 */
+ "LS_STOPPED", /* LS_STOPPED 3 */
+ "LS_CLOSING", /* LS_CLOSING 4 */
+ "LS_STOPPING", /* LS_STOPPING 5 */
+ "LS_REQSENT", /* LS_REQSENT 6 */
+ "LS_ACKRCVD", /* LS_ACKRCVD 7 */
+ "LS_ACKSENT", /* LS_ACKSENT 8 */
+ "LS_OPENED" /* LS_OPENED 9 */
+};
+#endif /* PPP_DEBUG */
+
+static void fsm_timeout (void *);
+static void fsm_rconfreq (fsm *, u_char, u_char *, int);
+static void fsm_rconfack (fsm *, int, u_char *, int);
+static void fsm_rconfnakrej (fsm *, int, int, u_char *, int);
+static void fsm_rtermreq (fsm *, int, u_char *, int);
+static void fsm_rtermack (fsm *);
+static void fsm_rcoderej (fsm *, u_char *, int);
+static void fsm_sconfreq (fsm *, int);
+
+#define PROTO_NAME(f) ((f)->callbacks->proto_name)
+
+int peer_mru[NUM_PPP];
+
+
+/*
+ * fsm_init - Initialize fsm.
+ *
+ * Initialize fsm state.
+ */
+void
+fsm_init(fsm *f)
+{
+ f->state = LS_INITIAL;
+ f->flags = 0;
+ f->id = 0; /* XXX Start with random id? */
+ f->timeouttime = FSM_DEFTIMEOUT;
+ f->maxconfreqtransmits = FSM_DEFMAXCONFREQS;
+ f->maxtermtransmits = FSM_DEFMAXTERMREQS;
+ f->maxnakloops = FSM_DEFMAXNAKLOOPS;
+ f->term_reason_len = 0;
+}
+
+
+/*
+ * fsm_lowerup - The lower layer is up.
+ */
+void
+fsm_lowerup(fsm *f)
+{
+ int oldState = f->state;
+
+ LWIP_UNUSED_ARG(oldState);
+
+ switch( f->state ) {
+ case LS_INITIAL:
+ f->state = LS_CLOSED;
+ break;
+
+ case LS_STARTING:
+ if( f->flags & OPT_SILENT ) {
+ f->state = LS_STOPPED;
+ } else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = LS_REQSENT;
+ }
+ break;
+
+ default:
+ FSMDEBUG(LOG_INFO, ("%s: Up event in state %d (%s)!\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ }
+
+ FSMDEBUG(LOG_INFO, ("%s: lowerup state %d (%s) -> %d (%s)\n",
+ PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
+}
+
+
+/*
+ * fsm_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts and inform upper layers.
+ */
+void
+fsm_lowerdown(fsm *f)
+{
+ int oldState = f->state;
+
+ LWIP_UNUSED_ARG(oldState);
+
+ switch( f->state ) {
+ case LS_CLOSED:
+ f->state = LS_INITIAL;
+ break;
+
+ case LS_STOPPED:
+ f->state = LS_STARTING;
+ if( f->callbacks->starting ) {
+ (*f->callbacks->starting)(f);
+ }
+ break;
+
+ case LS_CLOSING:
+ f->state = LS_INITIAL;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case LS_STOPPING:
+ case LS_REQSENT:
+ case LS_ACKRCVD:
+ case LS_ACKSENT:
+ f->state = LS_STARTING;
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ break;
+
+ case LS_OPENED:
+ if( f->callbacks->down ) {
+ (*f->callbacks->down)(f);
+ }
+ f->state = LS_STARTING;
+ break;
+
+ default:
+ FSMDEBUG(LOG_INFO, ("%s: Down event in state %d (%s)!\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ }
+
+ FSMDEBUG(LOG_INFO, ("%s: lowerdown state %d (%s) -> %d (%s)\n",
+ PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
+}
+
+
+/*
+ * fsm_open - Link is allowed to come up.
+ */
+void
+fsm_open(fsm *f)
+{
+ int oldState = f->state;
+
+ LWIP_UNUSED_ARG(oldState);
+
+ switch( f->state ) {
+ case LS_INITIAL:
+ f->state = LS_STARTING;
+ if( f->callbacks->starting ) {
+ (*f->callbacks->starting)(f);
+ }
+ break;
+
+ case LS_CLOSED:
+ if( f->flags & OPT_SILENT ) {
+ f->state = LS_STOPPED;
+ } else {
+ /* Send an initial configure-request */
+ fsm_sconfreq(f, 0);
+ f->state = LS_REQSENT;
+ }
+ break;
+
+ case LS_CLOSING:
+ f->state = LS_STOPPING;
+ /* fall through */
+ case LS_STOPPED:
+ case LS_OPENED:
+ if( f->flags & OPT_RESTART ) {
+ fsm_lowerdown(f);
+ fsm_lowerup(f);
+ }
+ break;
+ }
+
+ FSMDEBUG(LOG_INFO, ("%s: open state %d (%s) -> %d (%s)\n",
+ PROTO_NAME(f), oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
+}
+
+#if 0 /* backport pppd 2.4.4b1; */
+/*
+ * terminate_layer - Start process of shutting down the FSM
+ *
+ * Cancel any timeout running, notify upper layers we're done, and
+ * send a terminate-request message as configured.
+ */
+static void
+terminate_layer(fsm *f, int nextstate)
+{
+ /* @todo */
+}
+#endif
+
+/*
+ * fsm_close - Start closing connection.
+ *
+ * Cancel timeouts and either initiate close or possibly go directly to
+ * the LS_CLOSED state.
+ */
+void
+fsm_close(fsm *f, char *reason)
+{
+ int oldState = f->state;
+
+ LWIP_UNUSED_ARG(oldState);
+
+ f->term_reason = reason;
+ f->term_reason_len = (reason == NULL ? 0 : (int)strlen(reason));
+ switch( f->state ) {
+ case LS_STARTING:
+ f->state = LS_INITIAL;
+ break;
+ case LS_STOPPED:
+ f->state = LS_CLOSED;
+ break;
+ case LS_STOPPING:
+ f->state = LS_CLOSING;
+ break;
+
+ case LS_REQSENT:
+ case LS_ACKRCVD:
+ case LS_ACKSENT:
+ case LS_OPENED:
+ if( f->state != LS_OPENED ) {
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ } else if( f->callbacks->down ) {
+ (*f->callbacks->down)(f); /* Inform upper layers we're down */
+ }
+ /* Init restart counter, send Terminate-Request */
+ f->retransmits = f->maxtermtransmits;
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+
+ f->state = LS_CLOSING;
+ break;
+ }
+
+ FSMDEBUG(LOG_INFO, ("%s: close reason=%s state %d (%s) -> %d (%s)\n",
+ PROTO_NAME(f), reason, oldState, ppperr_strerr[oldState], f->state, ppperr_strerr[f->state]));
+}
+
+
+/*
+ * fsm_timeout - Timeout expired.
+ */
+static void
+fsm_timeout(void *arg)
+{
+ fsm *f = (fsm *) arg;
+
+ switch (f->state) {
+ case LS_CLOSING:
+ case LS_STOPPING:
+ if( f->retransmits <= 0 ) {
+ FSMDEBUG(LOG_WARNING, ("%s: timeout sending Terminate-Request state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ /*
+ * We've waited for an ack long enough. Peer probably heard us.
+ */
+ f->state = (f->state == LS_CLOSING)? LS_CLOSED: LS_STOPPED;
+ if( f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ } else {
+ FSMDEBUG(LOG_WARNING, ("%s: timeout resending Terminate-Requests state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ /* Send Terminate-Request */
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+ }
+ break;
+
+ case LS_REQSENT:
+ case LS_ACKRCVD:
+ case LS_ACKSENT:
+ if (f->retransmits <= 0) {
+ FSMDEBUG(LOG_WARNING, ("%s: timeout sending Config-Requests state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ f->state = LS_STOPPED;
+ if( (f->flags & OPT_PASSIVE) == 0 && f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ } else {
+ FSMDEBUG(LOG_WARNING, ("%s: timeout resending Config-Request state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ /* Retransmit the configure-request */
+ if (f->callbacks->retransmit) {
+ (*f->callbacks->retransmit)(f);
+ }
+ fsm_sconfreq(f, 1); /* Re-send Configure-Request */
+ if( f->state == LS_ACKRCVD ) {
+ f->state = LS_REQSENT;
+ }
+ }
+ break;
+
+ default:
+ FSMDEBUG(LOG_INFO, ("%s: UNHANDLED timeout event in state %d (%s)!\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ }
+}
+
+
+/*
+ * fsm_input - Input packet.
+ */
+void
+fsm_input(fsm *f, u_char *inpacket, int l)
+{
+ u_char *inp = inpacket;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ if (l < HEADERLEN) {
+ FSMDEBUG(LOG_WARNING, ("fsm_input(%x): Rcvd short header.\n",
+ f->protocol));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < HEADERLEN) {
+ FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd illegal length.\n",
+ f->protocol));
+ return;
+ }
+ if (len > l) {
+ FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd short packet.\n",
+ f->protocol));
+ return;
+ }
+ len -= HEADERLEN; /* subtract header length */
+
+ if( f->state == LS_INITIAL || f->state == LS_STARTING ) {
+ FSMDEBUG(LOG_INFO, ("fsm_input(%x): Rcvd packet in state %d (%s).\n",
+ f->protocol, f->state, ppperr_strerr[f->state]));
+ return;
+ }
+ FSMDEBUG(LOG_INFO, ("fsm_input(%s):%d,%d,%d\n", PROTO_NAME(f), code, id, l));
+ /*
+ * Action depends on code.
+ */
+ switch (code) {
+ case CONFREQ:
+ fsm_rconfreq(f, id, inp, len);
+ break;
+
+ case CONFACK:
+ fsm_rconfack(f, id, inp, len);
+ break;
+
+ case CONFNAK:
+ case CONFREJ:
+ fsm_rconfnakrej(f, code, id, inp, len);
+ break;
+
+ case TERMREQ:
+ fsm_rtermreq(f, id, inp, len);
+ break;
+
+ case TERMACK:
+ fsm_rtermack(f);
+ break;
+
+ case CODEREJ:
+ fsm_rcoderej(f, inp, len);
+ break;
+
+ default:
+ FSMDEBUG(LOG_INFO, ("fsm_input(%s): default: \n", PROTO_NAME(f)));
+ if( !f->callbacks->extcode ||
+ !(*f->callbacks->extcode)(f, code, id, inp, len) ) {
+ fsm_sdata(f, CODEREJ, ++f->id, inpacket, len + HEADERLEN);
+ }
+ break;
+ }
+}
+
+
+/*
+ * fsm_rconfreq - Receive Configure-Request.
+ */
+static void
+fsm_rconfreq(fsm *f, u_char id, u_char *inp, int len)
+{
+ int code, reject_if_disagree;
+
+ FSMDEBUG(LOG_INFO, ("fsm_rconfreq(%s): Rcvd id %d state=%d (%s)\n",
+ PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
+ switch( f->state ) {
+ case LS_CLOSED:
+ /* Go away, we're closed */
+ fsm_sdata(f, TERMACK, id, NULL, 0);
+ return;
+ case LS_CLOSING:
+ case LS_STOPPING:
+ return;
+
+ case LS_OPENED:
+ /* Go down and restart negotiation */
+ if( f->callbacks->down ) {
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ }
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ break;
+
+ case LS_STOPPED:
+ /* Negotiation started by our peer */
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = LS_REQSENT;
+ break;
+ }
+
+ /*
+ * Pass the requested configuration options
+ * to protocol-specific code for checking.
+ */
+ if (f->callbacks->reqci) { /* Check CI */
+ reject_if_disagree = (f->nakloops >= f->maxnakloops);
+ code = (*f->callbacks->reqci)(f, inp, &len, reject_if_disagree);
+ } else if (len) {
+ code = CONFREJ; /* Reject all CI */
+ } else {
+ code = CONFACK;
+ }
+
+ /* send the Ack, Nak or Rej to the peer */
+ fsm_sdata(f, (u_char)code, id, inp, len);
+
+ if (code == CONFACK) {
+ if (f->state == LS_ACKRCVD) {
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ f->state = LS_OPENED;
+ if (f->callbacks->up) {
+ (*f->callbacks->up)(f); /* Inform upper layers */
+ }
+ } else {
+ f->state = LS_ACKSENT;
+ }
+ f->nakloops = 0;
+ } else {
+ /* we sent CONFACK or CONFREJ */
+ if (f->state != LS_ACKRCVD) {
+ f->state = LS_REQSENT;
+ }
+ if( code == CONFNAK ) {
+ ++f->nakloops;
+ }
+ }
+}
+
+
+/*
+ * fsm_rconfack - Receive Configure-Ack.
+ */
+static void
+fsm_rconfack(fsm *f, int id, u_char *inp, int len)
+{
+ FSMDEBUG(LOG_INFO, ("fsm_rconfack(%s): Rcvd id %d state=%d (%s)\n",
+ PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
+
+ if (id != f->reqid || f->seen_ack) { /* Expected id? */
+ return; /* Nope, toss... */
+ }
+ if( !(f->callbacks->ackci? (*f->callbacks->ackci)(f, inp, len): (len == 0)) ) {
+ /* Ack is bad - ignore it */
+ FSMDEBUG(LOG_INFO, ("%s: received bad Ack (length %d)\n",
+ PROTO_NAME(f), len));
+ return;
+ }
+ f->seen_ack = 1;
+
+ switch (f->state) {
+ case LS_CLOSED:
+ case LS_STOPPED:
+ fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
+ break;
+
+ case LS_REQSENT:
+ f->state = LS_ACKRCVD;
+ f->retransmits = f->maxconfreqtransmits;
+ break;
+
+ case LS_ACKRCVD:
+ /* Huh? an extra valid Ack? oh well... */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ fsm_sconfreq(f, 0);
+ f->state = LS_REQSENT;
+ break;
+
+ case LS_ACKSENT:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ f->state = LS_OPENED;
+ f->retransmits = f->maxconfreqtransmits;
+ if (f->callbacks->up) {
+ (*f->callbacks->up)(f); /* Inform upper layers */
+ }
+ break;
+
+ case LS_OPENED:
+ /* Go down and restart negotiation */
+ if (f->callbacks->down) {
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ }
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = LS_REQSENT;
+ break;
+ }
+}
+
+
+/*
+ * fsm_rconfnakrej - Receive Configure-Nak or Configure-Reject.
+ */
+static void
+fsm_rconfnakrej(fsm *f, int code, int id, u_char *inp, int len)
+{
+ int (*proc) (fsm *, u_char *, int);
+ int ret;
+
+ FSMDEBUG(LOG_INFO, ("fsm_rconfnakrej(%s): Rcvd id %d state=%d (%s)\n",
+ PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
+
+ if (id != f->reqid || f->seen_ack) { /* Expected id? */
+ return; /* Nope, toss... */
+ }
+ proc = (code == CONFNAK)? f->callbacks->nakci: f->callbacks->rejci;
+ if (!proc || !((ret = proc(f, inp, len)))) {
+ /* Nak/reject is bad - ignore it */
+ FSMDEBUG(LOG_INFO, ("%s: received bad %s (length %d)\n",
+ PROTO_NAME(f), (code==CONFNAK? "Nak": "reject"), len));
+ return;
+ }
+ f->seen_ack = 1;
+
+ switch (f->state) {
+ case LS_CLOSED:
+ case LS_STOPPED:
+ fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
+ break;
+
+ case LS_REQSENT:
+ case LS_ACKSENT:
+ /* They didn't agree to what we wanted - try another request */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ if (ret < 0) {
+ f->state = LS_STOPPED; /* kludge for stopping CCP */
+ } else {
+ fsm_sconfreq(f, 0); /* Send Configure-Request */
+ }
+ break;
+
+ case LS_ACKRCVD:
+ /* Got a Nak/reject when we had already had an Ack?? oh well... */
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ fsm_sconfreq(f, 0);
+ f->state = LS_REQSENT;
+ break;
+
+ case LS_OPENED:
+ /* Go down and restart negotiation */
+ if (f->callbacks->down) {
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ }
+ fsm_sconfreq(f, 0); /* Send initial Configure-Request */
+ f->state = LS_REQSENT;
+ break;
+ }
+}
+
+
+/*
+ * fsm_rtermreq - Receive Terminate-Req.
+ */
+static void
+fsm_rtermreq(fsm *f, int id, u_char *p, int len)
+{
+ LWIP_UNUSED_ARG(p);
+
+ FSMDEBUG(LOG_INFO, ("fsm_rtermreq(%s): Rcvd id %d state=%d (%s)\n",
+ PROTO_NAME(f), id, f->state, ppperr_strerr[f->state]));
+
+ switch (f->state) {
+ case LS_ACKRCVD:
+ case LS_ACKSENT:
+ f->state = LS_REQSENT; /* Start over but keep trying */
+ break;
+
+ case LS_OPENED:
+ if (len > 0) {
+ FSMDEBUG(LOG_INFO, ("%s terminated by peer (%p)\n", PROTO_NAME(f), p));
+ } else {
+ FSMDEBUG(LOG_INFO, ("%s terminated by peer\n", PROTO_NAME(f)));
+ }
+ if (f->callbacks->down) {
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ }
+ f->retransmits = 0;
+ f->state = LS_STOPPING;
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ break;
+ }
+
+ fsm_sdata(f, TERMACK, (u_char)id, NULL, 0);
+}
+
+
+/*
+ * fsm_rtermack - Receive Terminate-Ack.
+ */
+static void
+fsm_rtermack(fsm *f)
+{
+ FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+
+ switch (f->state) {
+ case LS_CLOSING:
+ UNTIMEOUT(fsm_timeout, f);
+ f->state = LS_CLOSED;
+ if( f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ break;
+
+ case LS_STOPPING:
+ UNTIMEOUT(fsm_timeout, f);
+ f->state = LS_STOPPED;
+ if( f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ break;
+
+ case LS_ACKRCVD:
+ f->state = LS_REQSENT;
+ break;
+
+ case LS_OPENED:
+ if (f->callbacks->down) {
+ (*f->callbacks->down)(f); /* Inform upper layers */
+ }
+ fsm_sconfreq(f, 0);
+ break;
+ default:
+ FSMDEBUG(LOG_INFO, ("fsm_rtermack(%s): UNHANDLED state=%d (%s)!!!\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ }
+}
+
+
+/*
+ * fsm_rcoderej - Receive an Code-Reject.
+ */
+static void
+fsm_rcoderej(fsm *f, u_char *inp, int len)
+{
+ u_char code, id;
+
+ FSMDEBUG(LOG_INFO, ("fsm_rcoderej(%s): state=%d (%s)\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+
+ if (len < HEADERLEN) {
+ FSMDEBUG(LOG_INFO, ("fsm_rcoderej: Rcvd short Code-Reject packet!\n"));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ FSMDEBUG(LOG_WARNING, ("%s: Rcvd Code-Reject for code %d, id %d\n",
+ PROTO_NAME(f), code, id));
+
+ if( f->state == LS_ACKRCVD ) {
+ f->state = LS_REQSENT;
+ }
+}
+
+
+/*
+ * fsm_protreject - Peer doesn't speak this protocol.
+ *
+ * Treat this as a catastrophic error (RXJ-).
+ */
+void
+fsm_protreject(fsm *f)
+{
+ switch( f->state ) {
+ case LS_CLOSING:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ /* fall through */
+ case LS_CLOSED:
+ f->state = LS_CLOSED;
+ if( f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ break;
+
+ case LS_STOPPING:
+ case LS_REQSENT:
+ case LS_ACKRCVD:
+ case LS_ACKSENT:
+ UNTIMEOUT(fsm_timeout, f); /* Cancel timeout */
+ /* fall through */
+ case LS_STOPPED:
+ f->state = LS_STOPPED;
+ if( f->callbacks->finished ) {
+ (*f->callbacks->finished)(f);
+ }
+ break;
+
+ case LS_OPENED:
+ if( f->callbacks->down ) {
+ (*f->callbacks->down)(f);
+ }
+ /* Init restart counter, send Terminate-Request */
+ f->retransmits = f->maxtermtransmits;
+ fsm_sdata(f, TERMREQ, f->reqid = ++f->id,
+ (u_char *) f->term_reason, f->term_reason_len);
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+ --f->retransmits;
+
+ f->state = LS_STOPPING;
+ break;
+
+ default:
+ FSMDEBUG(LOG_INFO, ("%s: Protocol-reject event in state %d (%s)!\n",
+ PROTO_NAME(f), f->state, ppperr_strerr[f->state]));
+ }
+}
+
+
+/*
+ * fsm_sconfreq - Send a Configure-Request.
+ */
+static void
+fsm_sconfreq(fsm *f, int retransmit)
+{
+ u_char *outp;
+ int cilen;
+
+ if( f->state != LS_REQSENT && f->state != LS_ACKRCVD && f->state != LS_ACKSENT ) {
+ /* Not currently negotiating - reset options */
+ if( f->callbacks->resetci ) {
+ (*f->callbacks->resetci)(f);
+ }
+ f->nakloops = 0;
+ }
+
+ if( !retransmit ) {
+ /* New request - reset retransmission counter, use new ID */
+ f->retransmits = f->maxconfreqtransmits;
+ f->reqid = ++f->id;
+ }
+
+ f->seen_ack = 0;
+
+ /*
+ * Make up the request packet
+ */
+ outp = outpacket_buf[f->unit] + PPP_HDRLEN + HEADERLEN;
+ if( f->callbacks->cilen && f->callbacks->addci ) {
+ cilen = (*f->callbacks->cilen)(f);
+ if( cilen > peer_mru[f->unit] - (int)HEADERLEN ) {
+ cilen = peer_mru[f->unit] - HEADERLEN;
+ }
+ if (f->callbacks->addci) {
+ (*f->callbacks->addci)(f, outp, &cilen);
+ }
+ } else {
+ cilen = 0;
+ }
+
+ /* send the request to our peer */
+ fsm_sdata(f, CONFREQ, f->reqid, outp, cilen);
+
+ /* start the retransmit timer */
+ --f->retransmits;
+ TIMEOUT(fsm_timeout, f, f->timeouttime);
+
+ FSMDEBUG(LOG_INFO, ("%s: sending Configure-Request, id %d\n",
+ PROTO_NAME(f), f->reqid));
+}
+
+
+/*
+ * fsm_sdata - Send some data.
+ *
+ * Used for all packets sent to our peer by this module.
+ */
+void
+fsm_sdata( fsm *f, u_char code, u_char id, u_char *data, int datalen)
+{
+ u_char *outp;
+ int outlen;
+
+ /* Adjust length to be smaller than MTU */
+ outp = outpacket_buf[f->unit];
+ if (datalen > peer_mru[f->unit] - (int)HEADERLEN) {
+ datalen = peer_mru[f->unit] - HEADERLEN;
+ }
+ if (datalen && data != outp + PPP_HDRLEN + HEADERLEN) {
+ BCOPY(data, outp + PPP_HDRLEN + HEADERLEN, datalen);
+ }
+ outlen = datalen + HEADERLEN;
+ MAKEHEADER(outp, f->protocol);
+ PUTCHAR(code, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(outlen, outp);
+ pppWrite(f->unit, outpacket_buf[f->unit], outlen + PPP_HDRLEN);
+ FSMDEBUG(LOG_INFO, ("fsm_sdata(%s): Sent code %d,%d,%d.\n",
+ PROTO_NAME(f), code, id, outlen));
+}
+
+#endif /* PPP_SUPPORT */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/fsm.h b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/fsm.h
new file mode 100644
index 00000000..0ebeb97f
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/fsm.h
@@ -0,0 +1,157 @@
+/*****************************************************************************
+* fsm.h - Network Control Protocol Finite State Machine header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-11-05 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original based on BSD code.
+*****************************************************************************/
+/*
+ * fsm.h - {Link, IP} Control Protocol Finite State Machine definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: fsm.h $
+ */
+
+#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/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ipcp.c b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ipcp.c
new file mode 100644
index 00000000..f0ab2e0e
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ipcp.c
@@ -0,0 +1,1411 @@
+/** In contrast to pppd 2.3.1, DNS support has been added, proxy-ARP and
+ dial-on-demand has been stripped. */
+/*****************************************************************************
+* ipcp.c - Network PPP IP Control Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-08 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original.
+*****************************************************************************/
+/*
+ * ipcp.c - PPP IP Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "auth.h"
+#include "fsm.h"
+#include "vj.h"
+#include "ipcp.h"
+
+#include "lwip/inet.h"
+
+#include <string.h>
+
+/* #define OLD_CI_ADDRS 1 */ /* Support deprecated address negotiation. */
+
+/* global vars */
+ipcp_options ipcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+ipcp_options ipcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+ipcp_options ipcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+ipcp_options ipcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+
+/* local vars */
+static int default_route_set[NUM_PPP]; /* Have set up a default route */
+static int cis_received[NUM_PPP]; /* # Conf-Reqs received */
+
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void ipcp_resetci (fsm *); /* Reset our CI */
+static int ipcp_cilen (fsm *); /* Return length of our CI */
+static void ipcp_addci (fsm *, u_char *, int *); /* Add our CI */
+static int ipcp_ackci (fsm *, u_char *, int); /* Peer ack'd our CI */
+static int ipcp_nakci (fsm *, u_char *, int); /* Peer nak'd our CI */
+static int ipcp_rejci (fsm *, u_char *, int); /* Peer rej'd our CI */
+static int ipcp_reqci (fsm *, u_char *, int *, int); /* Rcv CI */
+static void ipcp_up (fsm *); /* We're UP */
+static void ipcp_down (fsm *); /* We're DOWN */
+#if PPP_ADDITIONAL_CALLBACKS
+static void ipcp_script (fsm *, char *); /* Run an up/down script */
+#endif
+static void ipcp_finished (fsm *); /* Don't need lower layer */
+
+
+fsm ipcp_fsm[NUM_PPP]; /* IPCP fsm structure */
+
+
+static fsm_callbacks ipcp_callbacks = { /* IPCP callback routines */
+ ipcp_resetci, /* Reset our Configuration Information */
+ ipcp_cilen, /* Length of our Configuration Information */
+ ipcp_addci, /* Add our Configuration Information */
+ ipcp_ackci, /* ACK our Configuration Information */
+ ipcp_nakci, /* NAK our Configuration Information */
+ ipcp_rejci, /* Reject our Configuration Information */
+ ipcp_reqci, /* Request peer's Configuration Information */
+ ipcp_up, /* Called when fsm reaches LS_OPENED state */
+ ipcp_down, /* Called when fsm leaves LS_OPENED state */
+ NULL, /* Called when we want the lower layer up */
+ ipcp_finished, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ NULL, /* Called to handle protocol-specific codes */
+ "IPCP" /* String name of protocol */
+};
+
+/*
+ * Protocol entry points from main code.
+ */
+static void ipcp_init (int);
+static void ipcp_open (int);
+static void ipcp_close (int, char *);
+static void ipcp_lowerup (int);
+static void ipcp_lowerdown (int);
+static void ipcp_input (int, u_char *, int);
+static void ipcp_protrej (int);
+
+
+struct protent ipcp_protent = {
+ PPP_IPCP,
+ ipcp_init,
+ ipcp_input,
+ ipcp_protrej,
+ ipcp_lowerup,
+ ipcp_lowerdown,
+ ipcp_open,
+ ipcp_close,
+#if PPP_ADDITIONAL_CALLBACKS
+ ipcp_printpkt,
+ NULL,
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+ 1,
+ "IPCP",
+#if PPP_ADDITIONAL_CALLBACKS
+ ip_check_options,
+ NULL,
+ ip_active_pkt
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+};
+
+static void ipcp_clear_addrs (int);
+
+/*
+ * Lengths of configuration options.
+ */
+#define CILEN_VOID 2
+#define CILEN_COMPRESS 4 /* min length for compression protocol opt. */
+#define CILEN_VJ 6 /* length for RFC1332 Van-Jacobson opt. */
+#define CILEN_ADDR 6 /* new-style single address option */
+#define CILEN_ADDRS 10 /* old-style dual address option */
+
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : \
+ (x) == CONFNAK ? "NAK" : "REJ")
+
+
+/*
+ * ipcp_init - Initialize IPCP.
+ */
+static void
+ipcp_init(int unit)
+{
+ fsm *f = &ipcp_fsm[unit];
+ ipcp_options *wo = &ipcp_wantoptions[unit];
+ ipcp_options *ao = &ipcp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_IPCP;
+ f->callbacks = &ipcp_callbacks;
+ fsm_init(&ipcp_fsm[unit]);
+
+ memset(wo, 0, sizeof(*wo));
+ memset(ao, 0, sizeof(*ao));
+
+ wo->neg_addr = 1;
+ wo->ouraddr = 0;
+#if VJ_SUPPORT
+ wo->neg_vj = 1;
+#else /* VJ_SUPPORT */
+ wo->neg_vj = 0;
+#endif /* VJ_SUPPORT */
+ wo->vj_protocol = IPCP_VJ_COMP;
+ wo->maxslotindex = MAX_SLOTS - 1;
+ wo->cflag = 0;
+ wo->default_route = 1;
+
+ ao->neg_addr = 1;
+#if VJ_SUPPORT
+ ao->neg_vj = 1;
+#else /* VJ_SUPPORT */
+ ao->neg_vj = 0;
+#endif /* VJ_SUPPORT */
+ ao->maxslotindex = MAX_SLOTS - 1;
+ ao->cflag = 1;
+ ao->default_route = 1;
+}
+
+
+/*
+ * ipcp_open - IPCP is allowed to come up.
+ */
+static void
+ipcp_open(int unit)
+{
+ fsm_open(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_close - Take IPCP down.
+ */
+static void
+ipcp_close(int unit, char *reason)
+{
+ fsm_close(&ipcp_fsm[unit], reason);
+}
+
+
+/*
+ * ipcp_lowerup - The lower layer is up.
+ */
+static void
+ipcp_lowerup(int unit)
+{
+ fsm_lowerup(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_lowerdown - The lower layer is down.
+ */
+static void
+ipcp_lowerdown(int unit)
+{
+ fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_input - Input IPCP packet.
+ */
+static void
+ipcp_input(int unit, u_char *p, int len)
+{
+ fsm_input(&ipcp_fsm[unit], p, len);
+}
+
+
+/*
+ * ipcp_protrej - A Protocol-Reject was received for IPCP.
+ *
+ * Pretend the lower layer went down, so we shut up.
+ */
+static void
+ipcp_protrej(int unit)
+{
+ fsm_lowerdown(&ipcp_fsm[unit]);
+}
+
+
+/*
+ * ipcp_resetci - Reset our CI.
+ */
+static void
+ipcp_resetci(fsm *f)
+{
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+
+ wo->req_addr = wo->neg_addr && ipcp_allowoptions[f->unit].neg_addr;
+ if (wo->ouraddr == 0) {
+ wo->accept_local = 1;
+ }
+ if (wo->hisaddr == 0) {
+ wo->accept_remote = 1;
+ }
+ /* Request DNS addresses from the peer */
+ wo->req_dns1 = ppp_settings.usepeerdns;
+ wo->req_dns2 = ppp_settings.usepeerdns;
+ ipcp_gotoptions[f->unit] = *wo;
+ cis_received[f->unit] = 0;
+}
+
+
+/*
+ * ipcp_cilen - Return length of our CI.
+ */
+static int
+ipcp_cilen(fsm *f)
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+
+#define LENCIVJ(neg, old) (neg ? (old? CILEN_COMPRESS : CILEN_VJ) : 0)
+#define LENCIADDR(neg, old) (neg ? (old? CILEN_ADDRS : CILEN_ADDR) : 0)
+#define LENCIDNS(neg) (neg ? (CILEN_ADDR) : 0)
+
+ /*
+ * First see if we want to change our options to the old
+ * forms because we have received old forms from the peer.
+ */
+ if (wo->neg_addr && !go->neg_addr && !go->old_addrs) {
+ /* use the old style of address negotiation */
+ go->neg_addr = 1;
+ go->old_addrs = 1;
+ }
+ if (wo->neg_vj && !go->neg_vj && !go->old_vj) {
+ /* try an older style of VJ negotiation */
+ if (cis_received[f->unit] == 0) {
+ /* keep trying the new style until we see some CI from the peer */
+ go->neg_vj = 1;
+ } else {
+ /* use the old style only if the peer did */
+ if (ho->neg_vj && ho->old_vj) {
+ go->neg_vj = 1;
+ go->old_vj = 1;
+ go->vj_protocol = ho->vj_protocol;
+ }
+ }
+ }
+
+ return (LENCIADDR(go->neg_addr, go->old_addrs) +
+ LENCIVJ(go->neg_vj, go->old_vj) +
+ LENCIDNS(go->req_dns1) +
+ LENCIDNS(go->req_dns2));
+}
+
+
+/*
+ * ipcp_addci - Add our desired CIs to a packet.
+ */
+static void
+ipcp_addci(fsm *f, u_char *ucp, int *lenp)
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ int len = *lenp;
+
+#define ADDCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+ if (neg) { \
+ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+ if (len >= vjlen) { \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(vjlen, ucp); \
+ PUTSHORT(val, ucp); \
+ if (!old) { \
+ PUTCHAR(maxslotindex, ucp); \
+ PUTCHAR(cflag, ucp); \
+ } \
+ len -= vjlen; \
+ } else { \
+ neg = 0; \
+ } \
+ }
+
+#define ADDCIADDR(opt, neg, old, val1, val2) \
+ if (neg) { \
+ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+ if (len >= addrlen) { \
+ u32_t l; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(addrlen, ucp); \
+ l = ntohl(val1); \
+ PUTLONG(l, ucp); \
+ if (old) { \
+ l = ntohl(val2); \
+ PUTLONG(l, ucp); \
+ } \
+ len -= addrlen; \
+ } else { \
+ neg = 0; \
+ } \
+ }
+
+#define ADDCIDNS(opt, neg, addr) \
+ if (neg) { \
+ if (len >= CILEN_ADDR) { \
+ u32_t l; \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_ADDR, ucp); \
+ l = ntohl(addr); \
+ PUTLONG(l, ucp); \
+ len -= CILEN_ADDR; \
+ } else { \
+ neg = 0; \
+ } \
+ }
+
+ ADDCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ ADDCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ ADDCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
+
+ ADDCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
+
+ *lenp -= len;
+}
+
+
+/*
+ * ipcp_ackci - Ack our CIs.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+ipcp_ackci(fsm *f, u_char *p, int len)
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_short cilen, citype, cishort;
+ u32_t cilong;
+ u_char cimaxslotindex, cicflag;
+
+ /*
+ * CIs must be in exactly the same order that we sent...
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+
+#define ACKCIVJ(opt, neg, val, old, maxslotindex, cflag) \
+ if (neg) { \
+ int vjlen = old? CILEN_COMPRESS : CILEN_VJ; \
+ if ((len -= vjlen) < 0) { \
+ goto bad; \
+ } \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != vjlen || \
+ citype != opt) { \
+ goto bad; \
+ } \
+ GETSHORT(cishort, p); \
+ if (cishort != val) { \
+ goto bad; \
+ } \
+ if (!old) { \
+ GETCHAR(cimaxslotindex, p); \
+ if (cimaxslotindex != maxslotindex) { \
+ goto bad; \
+ } \
+ GETCHAR(cicflag, p); \
+ if (cicflag != cflag) { \
+ goto bad; \
+ } \
+ } \
+ }
+
+#define ACKCIADDR(opt, neg, old, val1, val2) \
+ if (neg) { \
+ int addrlen = (old? CILEN_ADDRS: CILEN_ADDR); \
+ u32_t l; \
+ if ((len -= addrlen) < 0) { \
+ goto bad; \
+ } \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != addrlen || \
+ citype != opt) { \
+ goto bad; \
+ } \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (val1 != cilong) { \
+ goto bad; \
+ } \
+ if (old) { \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (val2 != cilong) { \
+ goto bad; \
+ } \
+ } \
+ }
+
+#define ACKCIDNS(opt, neg, addr) \
+ if (neg) { \
+ u32_t l; \
+ if ((len -= CILEN_ADDR) < 0) { \
+ goto bad; \
+ } \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_ADDR || \
+ citype != opt) { \
+ goto bad; \
+ } \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ if (addr != cilong) { \
+ goto bad; \
+ } \
+ }
+
+ ACKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), go->neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ ACKCIVJ(CI_COMPRESSTYPE, go->neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ ACKCIDNS(CI_MS_DNS1, go->req_dns1, go->dnsaddr[0]);
+
+ ACKCIDNS(CI_MS_DNS2, go->req_dns2, go->dnsaddr[1]);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0) {
+ goto bad;
+ }
+ return (1);
+
+bad:
+ IPCPDEBUG(LOG_INFO, ("ipcp_ackci: received bad Ack!\n"));
+ return (0);
+}
+
+/*
+ * ipcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if IPCP is in the LS_OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+ipcp_nakci(fsm *f, u_char *p, int len)
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char cimaxslotindex, cicflag;
+ u_char citype, cilen, *next;
+ u_short cishort;
+ u32_t ciaddr1, ciaddr2, l, cidnsaddr;
+ ipcp_options no; /* options we've seen Naks for */
+ ipcp_options try; /* options to request next time */
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIADDR(opt, neg, old, code) \
+ if (go->neg && \
+ len >= (cilen = (old? CILEN_ADDRS: CILEN_ADDR)) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ ciaddr1 = htonl(l); \
+ if (old) { \
+ GETLONG(l, p); \
+ ciaddr2 = htonl(l); \
+ no.old_addrs = 1; \
+ } else { \
+ ciaddr2 = 0; \
+ } \
+ no.neg = 1; \
+ code \
+ }
+
+#define NAKCIVJ(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_COMPRESS || cilen == CILEN_VJ) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+
+#define NAKCIDNS(opt, neg, code) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_ADDR) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ cidnsaddr = htonl(l); \
+ no.neg = 1; \
+ code \
+ }
+
+ /*
+ * Accept the peer's idea of {our,his} address, if different
+ * from our idea, only if the accept_{local,remote} flag is set.
+ */
+ NAKCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr, go->old_addrs,
+ if (go->accept_local && ciaddr1) { /* Do we know our address? */
+ try.ouraddr = ciaddr1;
+ IPCPDEBUG(LOG_INFO, ("local IP address %s\n",
+ inet_ntoa(ciaddr1)));
+ }
+ if (go->accept_remote && ciaddr2) { /* Does he know his? */
+ try.hisaddr = ciaddr2;
+ IPCPDEBUG(LOG_INFO, ("remote IP address %s\n",
+ inet_ntoa(ciaddr2)));
+ }
+ );
+
+ /*
+ * Accept the peer's value of maxslotindex provided that it
+ * is less than what we asked for. Turn off slot-ID compression
+ * if the peer wants. Send old-style compress-type option if
+ * the peer wants.
+ */
+ NAKCIVJ(CI_COMPRESSTYPE, neg_vj,
+ if (cilen == CILEN_VJ) {
+ GETCHAR(cimaxslotindex, p);
+ GETCHAR(cicflag, p);
+ if (cishort == IPCP_VJ_COMP) {
+ try.old_vj = 0;
+ if (cimaxslotindex < go->maxslotindex) {
+ try.maxslotindex = cimaxslotindex;
+ }
+ if (!cicflag) {
+ try.cflag = 0;
+ }
+ } else {
+ try.neg_vj = 0;
+ }
+ } else {
+ if (cishort == IPCP_VJ_COMP || cishort == IPCP_VJ_COMP_OLD) {
+ try.old_vj = 1;
+ try.vj_protocol = cishort;
+ } else {
+ try.neg_vj = 0;
+ }
+ }
+ );
+
+ NAKCIDNS(CI_MS_DNS1, req_dns1,
+ try.dnsaddr[0] = cidnsaddr;
+ IPCPDEBUG(LOG_INFO, ("primary DNS address %s\n", inet_ntoa(cidnsaddr)));
+ );
+
+ NAKCIDNS(CI_MS_DNS2, req_dns2,
+ try.dnsaddr[1] = cidnsaddr;
+ IPCPDEBUG(LOG_INFO, ("secondary DNS address %s\n", inet_ntoa(cidnsaddr)));
+ );
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If they want to negotiate about IP addresses, we comply.
+ * If they want us to ask for compression, we refuse.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if( (len -= cilen) < 0 ) {
+ goto bad;
+ }
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_COMPRESSTYPE:
+ if (go->neg_vj || no.neg_vj ||
+ (cilen != CILEN_VJ && cilen != CILEN_COMPRESS)) {
+ goto bad;
+ }
+ no.neg_vj = 1;
+ break;
+ case CI_ADDRS:
+ if ((go->neg_addr && go->old_addrs) || no.old_addrs
+ || cilen != CILEN_ADDRS) {
+ goto bad;
+ }
+ try.neg_addr = 1;
+ try.old_addrs = 1;
+ GETLONG(l, p);
+ ciaddr1 = htonl(l);
+ if (ciaddr1 && go->accept_local) {
+ try.ouraddr = ciaddr1;
+ }
+ GETLONG(l, p);
+ ciaddr2 = htonl(l);
+ if (ciaddr2 && go->accept_remote) {
+ try.hisaddr = ciaddr2;
+ }
+ no.old_addrs = 1;
+ break;
+ case CI_ADDR:
+ if (go->neg_addr || no.neg_addr || cilen != CILEN_ADDR) {
+ goto bad;
+ }
+ try.old_addrs = 0;
+ GETLONG(l, p);
+ ciaddr1 = htonl(l);
+ if (ciaddr1 && go->accept_local) {
+ try.ouraddr = ciaddr1;
+ }
+ if (try.ouraddr != 0) {
+ try.neg_addr = 1;
+ }
+ no.neg_addr = 1;
+ break;
+ }
+ p = next;
+ }
+
+ /* If there is still anything left, this packet is bad. */
+ if (len != 0) {
+ goto bad;
+ }
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ */
+ if (f->state != LS_OPENED) {
+ *go = try;
+ }
+
+ return 1;
+
+bad:
+ IPCPDEBUG(LOG_INFO, ("ipcp_nakci: received bad Nak!\n"));
+ return 0;
+}
+
+
+/*
+ * ipcp_rejci - Reject some of our CIs.
+ */
+static int
+ipcp_rejci(fsm *f, u_char *p, int len)
+{
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ u_char cimaxslotindex, ciflag, cilen;
+ u_short cishort;
+ u32_t cilong;
+ ipcp_options try; /* options to request next time */
+
+ try = *go;
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIADDR(opt, neg, old, val1, val2) \
+ if (go->neg && \
+ len >= (cilen = old? CILEN_ADDRS: CILEN_ADDR) && \
+ p[1] == cilen && \
+ p[0] == opt) { \
+ u32_t l; \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != val1) { \
+ goto bad; \
+ } \
+ if (old) { \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != val2) { \
+ goto bad; \
+ } \
+ } \
+ try.neg = 0; \
+ }
+
+#define REJCIVJ(opt, neg, val, old, maxslot, cflag) \
+ if (go->neg && \
+ p[1] == (old? CILEN_COMPRESS : CILEN_VJ) && \
+ len >= p[1] && \
+ p[0] == opt) { \
+ len -= p[1]; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) { \
+ goto bad; \
+ } \
+ if (!old) { \
+ GETCHAR(cimaxslotindex, p); \
+ if (cimaxslotindex != maxslot) { \
+ goto bad; \
+ } \
+ GETCHAR(ciflag, p); \
+ if (ciflag != cflag) { \
+ goto bad; \
+ } \
+ } \
+ try.neg = 0; \
+ }
+
+#define REJCIDNS(opt, neg, dnsaddr) \
+ if (go->neg && \
+ ((cilen = p[1]) == CILEN_ADDR) && \
+ len >= cilen && \
+ p[0] == opt) { \
+ u32_t l; \
+ len -= cilen; \
+ INCPTR(2, p); \
+ GETLONG(l, p); \
+ cilong = htonl(l); \
+ /* Check rejected value. */ \
+ if (cilong != dnsaddr) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ }
+
+ REJCIADDR((go->old_addrs? CI_ADDRS: CI_ADDR), neg_addr,
+ go->old_addrs, go->ouraddr, go->hisaddr);
+
+ REJCIVJ(CI_COMPRESSTYPE, neg_vj, go->vj_protocol, go->old_vj,
+ go->maxslotindex, go->cflag);
+
+ REJCIDNS(CI_MS_DNS1, req_dns1, go->dnsaddr[0]);
+
+ REJCIDNS(CI_MS_DNS2, req_dns2, go->dnsaddr[1]);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0) {
+ goto bad;
+ }
+ /*
+ * Now we can update state.
+ */
+ if (f->state != LS_OPENED) {
+ *go = try;
+ }
+ return 1;
+
+bad:
+ IPCPDEBUG(LOG_INFO, ("ipcp_rejci: received bad Reject!\n"));
+ return 0;
+}
+
+
+/*
+ * ipcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+ipcp_reqci(fsm *f, u_char *inp/* Requested CIs */,int *len/* Length of requested CIs */,int reject_if_disagree)
+{
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+ ipcp_options *ao = &ipcp_allowoptions[f->unit];
+#ifdef OLD_CI_ADDRS
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+#endif
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ u_short cilen, citype; /* Parsed len, type */
+ u_short cishort; /* Parsed short value */
+ u32_t tl, ciaddr1; /* Parsed address values */
+#ifdef OLD_CI_ADDRS
+ u32_t ciaddr2; /* Parsed address values */
+#endif
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *ucp = inp; /* Pointer to current output char */
+ int l = *len; /* Length left */
+ u_char maxslotindex, cflag;
+ int d;
+
+ cis_received[f->unit] = 1;
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: bad CI length!\n"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = (u_short)l;/* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+#ifdef OLD_CI_ADDRS /* Need to save space... */
+ case CI_ADDRS:
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received ADDRS\n"));
+ if (!ao->neg_addr ||
+ cilen != CILEN_ADDRS) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no address, or if we both have his address but
+ * disagree about it, then NAK it with our idea.
+ * In particular, if we don't know his address, but he does,
+ * then accept it.
+ */
+ GETLONG(tl, p); /* Parse source address (his) */
+ ciaddr1 = htonl(tl);
+ IPCPDEBUG(LOG_INFO, ("his addr %s\n", inet_ntoa(ciaddr1)));
+ if (ciaddr1 != wo->hisaddr
+ && (ciaddr1 == 0 || !wo->accept_remote)) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u32_t), p);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, p);
+ }
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+ /*
+ * If neither we nor he knows his address, reject the option.
+ */
+ orc = CONFREJ;
+ wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
+ break;
+ }
+
+ /*
+ * If he doesn't know our address, or if we both have our address
+ * but disagree about it, then NAK it with our idea.
+ */
+ GETLONG(tl, p); /* Parse desination address (ours) */
+ ciaddr2 = htonl(tl);
+ IPCPDEBUG(LOG_INFO, ("our addr %s\n", inet_ntoa(ciaddr2)));
+ if (ciaddr2 != wo->ouraddr) {
+ if (ciaddr2 == 0 || !wo->accept_local) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u32_t), p);
+ tl = ntohl(wo->ouraddr);
+ PUTLONG(tl, p);
+ }
+ } else {
+ go->ouraddr = ciaddr2; /* accept peer's idea */
+ }
+ }
+
+ ho->neg_addr = 1;
+ ho->old_addrs = 1;
+ ho->hisaddr = ciaddr1;
+ ho->ouraddr = ciaddr2;
+ break;
+#endif
+
+ case CI_ADDR:
+ if (!ao->neg_addr) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR not allowed\n"));
+ orc = CONFREJ; /* Reject CI */
+ break;
+ } else if (cilen != CILEN_ADDR) { /* Check CI length */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR bad len\n"));
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+
+ /*
+ * If he has no address, or if we both have his address but
+ * disagree about it, then NAK it with our idea.
+ * In particular, if we don't know his address, but he does,
+ * then accept it.
+ */
+ GETLONG(tl, p); /* Parse source address (his) */
+ ciaddr1 = htonl(tl);
+ if (ciaddr1 != wo->hisaddr
+ && (ciaddr1 == 0 || !wo->accept_remote)) {
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(sizeof(u32_t), p);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, p);
+ }
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Nak ADDR %s\n", inet_ntoa(ciaddr1)));
+ } else if (ciaddr1 == 0 && wo->hisaddr == 0) {
+ /*
+ * Don't ACK an address of 0.0.0.0 - reject it instead.
+ */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Reject ADDR %s\n", inet_ntoa(ciaddr1)));
+ orc = CONFREJ;
+ wo->req_addr = 0; /* don't NAK with 0.0.0.0 later */
+ break;
+ }
+
+ ho->neg_addr = 1;
+ ho->hisaddr = ciaddr1;
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: ADDR %s\n", inet_ntoa(ciaddr1)));
+ break;
+
+ case CI_MS_DNS1:
+ case CI_MS_DNS2:
+ /* Microsoft primary or secondary DNS request */
+ d = citype == CI_MS_DNS2;
+
+ /* If we do not have a DNS address then we cannot send it */
+ if (ao->dnsaddr[d] == 0 ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting DNS%d Request\n", d+1));
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETLONG(tl, p);
+ if (htonl(tl) != ao->dnsaddr[d]) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking DNS%d Request %s\n",
+ d+1, inet_ntoa(tl)));
+ DECPTR(sizeof(u32_t), p);
+ tl = ntohl(ao->dnsaddr[d]);
+ PUTLONG(tl, p);
+ orc = CONFNAK;
+ }
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received DNS%d Request\n", d+1));
+ break;
+
+ case CI_MS_WINS1:
+ case CI_MS_WINS2:
+ /* Microsoft primary or secondary WINS request */
+ d = citype == CI_MS_WINS2;
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: received WINS%d Request\n", d+1));
+
+ /* If we do not have a DNS address then we cannot send it */
+ if (ao->winsaddr[d] == 0 ||
+ cilen != CILEN_ADDR) { /* Check CI length */
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETLONG(tl, p);
+ if (htonl(tl) != ao->winsaddr[d]) {
+ DECPTR(sizeof(u32_t), p);
+ tl = ntohl(ao->winsaddr[d]);
+ PUTLONG(tl, p);
+ orc = CONFNAK;
+ }
+ break;
+
+ case CI_COMPRESSTYPE:
+ if (!ao->neg_vj) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE not allowed\n"));
+ orc = CONFREJ;
+ break;
+ } else if (cilen != CILEN_VJ && cilen != CILEN_COMPRESS) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE len=%d\n", cilen));
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+
+ if (!(cishort == IPCP_VJ_COMP ||
+ (cishort == IPCP_VJ_COMP_OLD && cilen == CILEN_COMPRESS))) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting COMPRESSTYPE %d\n", cishort));
+ orc = CONFREJ;
+ break;
+ }
+
+ ho->neg_vj = 1;
+ ho->vj_protocol = cishort;
+ if (cilen == CILEN_VJ) {
+ GETCHAR(maxslotindex, p);
+ if (maxslotindex > ao->maxslotindex) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking VJ max slot %d\n", maxslotindex));
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(1, p);
+ PUTCHAR(ao->maxslotindex, p);
+ }
+ }
+ GETCHAR(cflag, p);
+ if (cflag && !ao->cflag) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Naking VJ cflag %d\n", cflag));
+ orc = CONFNAK;
+ if (!reject_if_disagree) {
+ DECPTR(1, p);
+ PUTCHAR(wo->cflag, p);
+ }
+ }
+ ho->maxslotindex = maxslotindex;
+ ho->cflag = cflag;
+ } else {
+ ho->old_vj = 1;
+ ho->maxslotindex = MAX_SLOTS - 1;
+ ho->cflag = 1;
+ }
+ IPCPDEBUG(LOG_INFO, (
+ "ipcp_reqci: received COMPRESSTYPE p=%d old=%d maxslot=%d cflag=%d\n",
+ ho->vj_protocol, ho->old_vj, ho->maxslotindex, ho->cflag));
+ break;
+
+ default:
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting unknown CI type %d\n", citype));
+ orc = CONFREJ;
+ break;
+ }
+
+endswitch:
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) { /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+ }
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree) { /* Getting fed up with sending NAKs? */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Rejecting too many naks\n"));
+ orc = CONFREJ; /* Get tough if so */
+ } else {
+ if (rc == CONFREJ) { /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ }
+ if (rc == CONFACK) { /* Ack'd all prior CIs? */
+ rc = CONFNAK; /* Not anymore... */
+ ucp = inp; /* Backup */
+ }
+ }
+ }
+
+ if (orc == CONFREJ && /* Reject this CI */
+ rc != CONFREJ) { /* but no prior ones? */
+ rc = CONFREJ;
+ ucp = inp; /* Backup */
+ }
+
+ /* Need to move CI? */
+ if (ucp != cip) {
+ BCOPY(cip, ucp, cilen); /* Move it */
+ }
+
+ /* Update output pointer */
+ INCPTR(cilen, ucp);
+ }
+
+ /*
+ * If we aren't rejecting this packet, and we want to negotiate
+ * their address, and they didn't send their address, then we
+ * send a NAK with a CI_ADDR option appended. We assume the
+ * input buffer is long enough that we can append the extra
+ * option safely.
+ */
+ if (rc != CONFREJ && !ho->neg_addr &&
+ wo->req_addr && !reject_if_disagree) {
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: Requesting peer address\n"));
+ if (rc == CONFACK) {
+ rc = CONFNAK;
+ ucp = inp; /* reset pointer */
+ wo->req_addr = 0; /* don't ask again */
+ }
+ PUTCHAR(CI_ADDR, ucp);
+ PUTCHAR(CILEN_ADDR, ucp);
+ tl = ntohl(wo->hisaddr);
+ PUTLONG(tl, ucp);
+ }
+
+ *len = (int)(ucp - inp); /* Compute output length */
+ IPCPDEBUG(LOG_INFO, ("ipcp_reqci: returning Configure-%s\n", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+
+#if 0
+/*
+ * ip_check_options - check that any IP-related options are OK,
+ * and assign appropriate defaults.
+ */
+static void
+ip_check_options(u_long localAddr)
+{
+ ipcp_options *wo = &ipcp_wantoptions[0];
+
+ /*
+ * Load our default IP address but allow the remote host to give us
+ * a new address.
+ */
+ if (wo->ouraddr == 0 && !ppp_settings.disable_defaultip) {
+ wo->accept_local = 1; /* don't insist on this default value */
+ wo->ouraddr = htonl(localAddr);
+ }
+}
+#endif
+
+
+/*
+ * ipcp_up - IPCP has come UP.
+ *
+ * Configure the IP network interface appropriately and bring it up.
+ */
+static void
+ipcp_up(fsm *f)
+{
+ u32_t mask;
+ ipcp_options *ho = &ipcp_hisoptions[f->unit];
+ ipcp_options *go = &ipcp_gotoptions[f->unit];
+ ipcp_options *wo = &ipcp_wantoptions[f->unit];
+
+ np_up(f->unit, PPP_IP);
+ IPCPDEBUG(LOG_INFO, ("ipcp: up\n"));
+
+ /*
+ * We must have a non-zero IP address for both ends of the link.
+ */
+ if (!ho->neg_addr) {
+ ho->hisaddr = wo->hisaddr;
+ }
+
+ if (ho->hisaddr == 0) {
+ IPCPDEBUG(LOG_ERR, ("Could not determine remote IP address\n"));
+ ipcp_close(f->unit, "Could not determine remote IP address");
+ return;
+ }
+ if (go->ouraddr == 0) {
+ IPCPDEBUG(LOG_ERR, ("Could not determine local IP address\n"));
+ ipcp_close(f->unit, "Could not determine local IP address");
+ return;
+ }
+
+ if (ppp_settings.usepeerdns && (go->dnsaddr[0] || go->dnsaddr[1])) {
+ /*pppGotDNSAddrs(go->dnsaddr[0], go->dnsaddr[1]);*/
+ }
+
+ /*
+ * Check that the peer is allowed to use the IP address it wants.
+ */
+ if (!auth_ip_addr(f->unit, ho->hisaddr)) {
+ IPCPDEBUG(LOG_ERR, ("Peer is not authorized to use remote address %s\n",
+ inet_ntoa(ho->hisaddr)));
+ ipcp_close(f->unit, "Unauthorized remote IP address");
+ return;
+ }
+
+ /* set tcp compression */
+ sifvjcomp(f->unit, ho->neg_vj, ho->cflag, ho->maxslotindex);
+
+ /*
+ * Set IP addresses and (if specified) netmask.
+ */
+ mask = GetMask(go->ouraddr);
+
+ if (!sifaddr(f->unit, go->ouraddr, ho->hisaddr, mask, go->dnsaddr[0], go->dnsaddr[1])) {
+ IPCPDEBUG(LOG_WARNING, ("sifaddr failed\n"));
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ /* bring the interface up for IP */
+ if (!sifup(f->unit)) {
+ IPCPDEBUG(LOG_WARNING, ("sifup failed\n"));
+ ipcp_close(f->unit, "Interface configuration failed");
+ return;
+ }
+
+ sifnpmode(f->unit, PPP_IP, NPMODE_PASS);
+
+ /* assign a default route through the interface if required */
+ if (ipcp_wantoptions[f->unit].default_route) {
+ if (sifdefaultroute(f->unit, go->ouraddr, ho->hisaddr)) {
+ default_route_set[f->unit] = 1;
+ }
+ }
+
+ IPCPDEBUG(LOG_NOTICE, ("local IP address %s\n", inet_ntoa(go->ouraddr)));
+ IPCPDEBUG(LOG_NOTICE, ("remote IP address %s\n", inet_ntoa(ho->hisaddr)));
+ if (go->dnsaddr[0]) {
+ IPCPDEBUG(LOG_NOTICE, ("primary DNS address %s\n", inet_ntoa(go->dnsaddr[0])));
+ }
+ if (go->dnsaddr[1]) {
+ IPCPDEBUG(LOG_NOTICE, ("secondary DNS address %s\n", inet_ntoa(go->dnsaddr[1])));
+ }
+}
+
+
+/*
+ * ipcp_down - IPCP has gone DOWN.
+ *
+ * Take the IP network interface down, clear its addresses
+ * and delete routes through it.
+ */
+static void
+ipcp_down(fsm *f)
+{
+ IPCPDEBUG(LOG_INFO, ("ipcp: down\n"));
+ np_down(f->unit, PPP_IP);
+ sifvjcomp(f->unit, 0, 0, 0);
+
+ sifdown(f->unit);
+ ipcp_clear_addrs(f->unit);
+}
+
+
+/*
+ * ipcp_clear_addrs() - clear the interface addresses, routes, etc.
+ */
+static void
+ipcp_clear_addrs(int unit)
+{
+ u32_t ouraddr, hisaddr;
+
+ ouraddr = ipcp_gotoptions[unit].ouraddr;
+ hisaddr = ipcp_hisoptions[unit].hisaddr;
+ if (default_route_set[unit]) {
+ cifdefaultroute(unit, ouraddr, hisaddr);
+ default_route_set[unit] = 0;
+ }
+ cifaddr(unit, ouraddr, hisaddr);
+}
+
+
+/*
+ * ipcp_finished - possibly shut down the lower layers.
+ */
+static void
+ipcp_finished(fsm *f)
+{
+ np_finished(f->unit, PPP_IP);
+}
+
+#if PPP_ADDITIONAL_CALLBACKS
+static int
+ipcp_printpkt(u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg)
+{
+ LWIP_UNUSED_ARG(p);
+ LWIP_UNUSED_ARG(plen);
+ LWIP_UNUSED_ARG(printer);
+ LWIP_UNUSED_ARG(arg);
+ return 0;
+}
+
+/*
+ * ip_active_pkt - see if this IP packet is worth bringing the link up for.
+ * We don't bring the link up for IP fragments or for TCP FIN packets
+ * with no data.
+ */
+#define IP_HDRLEN 20 /* bytes */
+#define IP_OFFMASK 0x1fff
+#define IPPROTO_TCP 6
+#define TCP_HDRLEN 20
+#define TH_FIN 0x01
+
+/*
+ * We use these macros because the IP header may be at an odd address,
+ * and some compilers might use word loads to get th_off or ip_hl.
+ */
+
+#define net_short(x) (((x)[0] << 8) + (x)[1])
+#define get_iphl(x) (((unsigned char *)(x))[0] & 0xF)
+#define get_ipoff(x) net_short((unsigned char *)(x) + 6)
+#define get_ipproto(x) (((unsigned char *)(x))[9])
+#define get_tcpoff(x) (((unsigned char *)(x))[12] >> 4)
+#define get_tcpflags(x) (((unsigned char *)(x))[13])
+
+static int
+ip_active_pkt(u_char *pkt, int len)
+{
+ u_char *tcp;
+ int hlen;
+
+ len -= PPP_HDRLEN;
+ pkt += PPP_HDRLEN;
+ if (len < IP_HDRLEN) {
+ return 0;
+ }
+ if ((get_ipoff(pkt) & IP_OFFMASK) != 0) {
+ return 0;
+ }
+ if (get_ipproto(pkt) != IPPROTO_TCP) {
+ return 1;
+ }
+ hlen = get_iphl(pkt) * 4;
+ if (len < hlen + TCP_HDRLEN) {
+ return 0;
+ }
+ tcp = pkt + hlen;
+ if ((get_tcpflags(tcp) & TH_FIN) != 0 && len == hlen + get_tcpoff(tcp) * 4) {
+ return 0;
+ }
+ return 1;
+}
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+
+#endif /* PPP_SUPPORT */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ipcp.h b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ipcp.h
new file mode 100644
index 00000000..4bfaf182
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ipcp.h
@@ -0,0 +1,106 @@
+/*****************************************************************************
+* ipcp.h - PPP IP NCP: Internet Protocol Network Control Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original derived from BSD codes.
+*****************************************************************************/
+/*
+ * ipcp.h - IP Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: ipcp.h $
+ */
+
+#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/src/VBox/Devices/Network/lwip-new/src/netif/ppp/lcp.c b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/lcp.c
new file mode 100644
index 00000000..54f758aa
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/lcp.c
@@ -0,0 +1,2066 @@
+/*****************************************************************************
+* lcp.c - Network Link Control Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-01 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original.
+*****************************************************************************/
+
+/*
+ * lcp.c - PPP Link Control Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "fsm.h"
+#include "chap.h"
+#include "magic.h"
+#include "auth.h"
+#include "lcp.h"
+
+#include <string.h>
+
+#if PPPOE_SUPPORT
+#include "netif/ppp_oe.h"
+#else
+#define PPPOE_MAXMTU PPP_MAXMRU
+#endif
+
+#if 0 /* UNUSED */
+/*
+ * LCP-related command-line options.
+ */
+int lcp_echo_interval = 0; /* Interval between LCP echo-requests */
+int lcp_echo_fails = 0; /* Tolerance to unanswered echo-requests */
+bool lax_recv = 0; /* accept control chars in asyncmap */
+
+static int setescape (char **);
+
+static option_t lcp_option_list[] = {
+ /* LCP options */
+ /* list stripped for simplicity */
+ {NULL}
+};
+#endif /* UNUSED */
+
+/* options */
+LinkPhase lcp_phase[NUM_PPP]; /* Phase of link session (RFC 1661) */
+static u_int lcp_echo_interval = LCP_ECHOINTERVAL; /* Interval between LCP echo-requests */
+static u_int lcp_echo_fails = LCP_MAXECHOFAILS; /* Tolerance to unanswered echo-requests */
+
+/* global vars */
+static fsm lcp_fsm[NUM_PPP]; /* LCP fsm structure (global)*/
+lcp_options lcp_wantoptions[NUM_PPP]; /* Options that we want to request */
+lcp_options lcp_gotoptions[NUM_PPP]; /* Options that peer ack'd */
+lcp_options lcp_allowoptions[NUM_PPP]; /* Options we allow peer to request */
+lcp_options lcp_hisoptions[NUM_PPP]; /* Options that we ack'd */
+ext_accm xmit_accm[NUM_PPP]; /* extended transmit ACCM */
+
+static u32_t lcp_echos_pending = 0; /* Number of outstanding echo msgs */
+static u32_t lcp_echo_number = 0; /* ID number of next echo frame */
+static u32_t lcp_echo_timer_running = 0; /* TRUE if a timer is running */
+
+/* @todo: do we really need such a large buffer? The typical 1500 bytes seem too much. */
+static u_char nak_buffer[PPP_MRU]; /* where we construct a nak packet */
+
+/*
+ * Callbacks for fsm code. (CI = Configuration Information)
+ */
+static void lcp_resetci (fsm*); /* Reset our CI */
+static int lcp_cilen (fsm*); /* Return length of our CI */
+static void lcp_addci (fsm*, u_char*, int*); /* Add our CI to pkt */
+static int lcp_ackci (fsm*, u_char*, int); /* Peer ack'd our CI */
+static int lcp_nakci (fsm*, u_char*, int); /* Peer nak'd our CI */
+static int lcp_rejci (fsm*, u_char*, int); /* Peer rej'd our CI */
+static int lcp_reqci (fsm*, u_char*, int*, int); /* Rcv peer CI */
+static void lcp_up (fsm*); /* We're UP */
+static void lcp_down (fsm*); /* We're DOWN */
+static void lcp_starting (fsm*); /* We need lower layer up */
+static void lcp_finished (fsm*); /* We need lower layer down */
+static int lcp_extcode (fsm*, int, u_char, u_char*, int);
+static void lcp_rprotrej (fsm*, u_char*, int);
+
+/*
+ * routines to send LCP echos to peer
+ */
+
+static void lcp_echo_lowerup (int);
+static void lcp_echo_lowerdown (int);
+static void LcpEchoTimeout (void*);
+static void lcp_received_echo_reply (fsm*, int, u_char*, int);
+static void LcpSendEchoRequest (fsm*);
+static void LcpLinkFailure (fsm*);
+static void LcpEchoCheck (fsm*);
+
+static fsm_callbacks lcp_callbacks = { /* LCP callback routines */
+ lcp_resetci, /* Reset our Configuration Information */
+ lcp_cilen, /* Length of our Configuration Information */
+ lcp_addci, /* Add our Configuration Information */
+ lcp_ackci, /* ACK our Configuration Information */
+ lcp_nakci, /* NAK our Configuration Information */
+ lcp_rejci, /* Reject our Configuration Information */
+ lcp_reqci, /* Request peer's Configuration Information */
+ lcp_up, /* Called when fsm reaches LS_OPENED state */
+ lcp_down, /* Called when fsm leaves LS_OPENED state */
+ lcp_starting, /* Called when we want the lower layer up */
+ lcp_finished, /* Called when we want the lower layer down */
+ NULL, /* Called when Protocol-Reject received */
+ NULL, /* Retransmission is necessary */
+ lcp_extcode, /* Called to handle LCP-specific codes */
+ "LCP" /* String name of protocol */
+};
+
+/*
+ * Protocol entry points.
+ * Some of these are called directly.
+ */
+
+static void lcp_input (int, u_char *, int);
+static void lcp_protrej (int);
+
+struct protent lcp_protent = {
+ PPP_LCP,
+ lcp_init,
+ lcp_input,
+ lcp_protrej,
+ lcp_lowerup,
+ lcp_lowerdown,
+ lcp_open,
+ lcp_close,
+#if PPP_ADDITIONAL_CALLBACKS
+ lcp_printpkt,
+ NULL,
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+ 1,
+ "LCP",
+#if PPP_ADDITIONAL_CALLBACKS
+ NULL,
+ NULL,
+ NULL
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+};
+
+int lcp_loopbackfail = DEFLOOPBACKFAIL;
+
+/*
+ * Length of each type of configuration option (in octets)
+ */
+#define CILEN_VOID 2
+#define CILEN_CHAR 3
+#define CILEN_SHORT 4 /* CILEN_VOID + sizeof(short) */
+#define CILEN_CHAP 5 /* CILEN_VOID + sizeof(short) + 1 */
+#define CILEN_LONG 6 /* CILEN_VOID + sizeof(long) */
+#define CILEN_LQR 8 /* CILEN_VOID + sizeof(short) + sizeof(long) */
+#define CILEN_CBCP 3
+
+#define CODENAME(x) ((x) == CONFACK ? "ACK" : (x) == CONFNAK ? "NAK" : "REJ")
+
+#if 0 /* UNUSED */
+/*
+ * setescape - add chars to the set we escape on transmission.
+ */
+static int
+setescape(argv)
+ char **argv;
+{
+ int n, ret;
+ char *p, *endp;
+
+ p = *argv;
+ ret = 1;
+ while (*p) {
+ n = strtol(p, &endp, 16);
+ if (p == endp) {
+ option_error("escape parameter contains invalid hex number '%s'", p);
+ return 0;
+ }
+ p = endp;
+ if (n < 0 || n == 0x5E || n > 0xFF) {
+ option_error("can't escape character 0x%x", n);
+ ret = 0;
+ } else
+ xmit_accm[0][n >> 5] |= 1 << (n & 0x1F);
+ while (*p == ',' || *p == ' ')
+ ++p;
+ }
+ return ret;
+}
+#endif /* UNUSED */
+
+/*
+ * lcp_init - Initialize LCP.
+ */
+void
+lcp_init(int unit)
+{
+ fsm *f = &lcp_fsm[unit];
+ lcp_options *wo = &lcp_wantoptions[unit];
+ lcp_options *ao = &lcp_allowoptions[unit];
+
+ f->unit = unit;
+ f->protocol = PPP_LCP;
+ f->callbacks = &lcp_callbacks;
+
+ fsm_init(f);
+
+ wo->passive = 0;
+ wo->silent = 0;
+ wo->restart = 0; /* Set to 1 in kernels or multi-line implementations */
+ wo->neg_mru = 1;
+ wo->mru = PPP_DEFMRU;
+ wo->neg_asyncmap = 1;
+ wo->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */
+ wo->neg_chap = 0; /* Set to 1 on server */
+ wo->neg_upap = 0; /* Set to 1 on server */
+ wo->chap_mdtype = CHAP_DIGEST_MD5;
+ wo->neg_magicnumber = 1;
+ wo->neg_pcompression = 1;
+ wo->neg_accompression = 1;
+ wo->neg_lqr = 0; /* no LQR implementation yet */
+ wo->neg_cbcp = 0;
+
+ ao->neg_mru = 1;
+ ao->mru = PPP_MAXMRU;
+ ao->neg_asyncmap = 1;
+ ao->asyncmap = 0x00000000l; /* Assume don't need to escape any ctl chars. */
+ ao->neg_chap = (CHAP_SUPPORT != 0);
+ ao->chap_mdtype = CHAP_DIGEST_MD5;
+ ao->neg_upap = (PAP_SUPPORT != 0);
+ ao->neg_magicnumber = 1;
+ ao->neg_pcompression = 1;
+ ao->neg_accompression = 1;
+ ao->neg_lqr = 0; /* no LQR implementation yet */
+ ao->neg_cbcp = (CBCP_SUPPORT != 0);
+
+ /*
+ * Set transmit escape for the flag and escape characters plus anything
+ * set for the allowable options.
+ */
+ memset(xmit_accm[unit], 0, sizeof(xmit_accm[0]));
+ xmit_accm[unit][15] = 0x60;
+ xmit_accm[unit][0] = (u_char)((ao->asyncmap & 0xFF));
+ xmit_accm[unit][1] = (u_char)((ao->asyncmap >> 8) & 0xFF);
+ xmit_accm[unit][2] = (u_char)((ao->asyncmap >> 16) & 0xFF);
+ xmit_accm[unit][3] = (u_char)((ao->asyncmap >> 24) & 0xFF);
+ LCPDEBUG(LOG_INFO, ("lcp_init: xmit_accm=%X %X %X %X\n",
+ xmit_accm[unit][0],
+ xmit_accm[unit][1],
+ xmit_accm[unit][2],
+ xmit_accm[unit][3]));
+
+ lcp_phase[unit] = PHASE_INITIALIZE;
+}
+
+
+/*
+ * lcp_open - LCP is allowed to come up.
+ */
+void
+lcp_open(int unit)
+{
+ fsm *f = &lcp_fsm[unit];
+ lcp_options *wo = &lcp_wantoptions[unit];
+
+ f->flags = 0;
+ if (wo->passive) {
+ f->flags |= OPT_PASSIVE;
+ }
+ if (wo->silent) {
+ f->flags |= OPT_SILENT;
+ }
+ fsm_open(f);
+
+ lcp_phase[unit] = PHASE_ESTABLISH;
+}
+
+
+/*
+ * lcp_close - Take LCP down.
+ */
+void
+lcp_close(int unit, char *reason)
+{
+ fsm *f = &lcp_fsm[unit];
+
+ if (lcp_phase[unit] != PHASE_DEAD) {
+ lcp_phase[unit] = PHASE_TERMINATE;
+ }
+ if (f->state == LS_STOPPED && f->flags & (OPT_PASSIVE|OPT_SILENT)) {
+ /*
+ * This action is not strictly according to the FSM in RFC1548,
+ * but it does mean that the program terminates if you do an
+ * lcp_close() in passive/silent mode when a connection hasn't
+ * been established.
+ */
+ f->state = LS_CLOSED;
+ lcp_finished(f);
+ } else {
+ fsm_close(f, reason);
+ }
+}
+
+
+/*
+ * lcp_lowerup - The lower layer is up.
+ */
+void
+lcp_lowerup(int unit)
+{
+ lcp_options *wo = &lcp_wantoptions[unit];
+
+ /*
+ * Don't use A/C or protocol compression on transmission,
+ * but accept A/C and protocol compressed packets
+ * if we are going to ask for A/C and protocol compression.
+ */
+ ppp_set_xaccm(unit, &xmit_accm[unit]);
+ ppp_send_config(unit, PPP_MRU, 0xffffffffl, 0, 0);
+ ppp_recv_config(unit, PPP_MRU, 0x00000000l,
+ wo->neg_pcompression, wo->neg_accompression);
+ peer_mru[unit] = PPP_MRU;
+ lcp_allowoptions[unit].asyncmap = (u_long)xmit_accm[unit][0]
+ | ((u_long)xmit_accm[unit][1] << 8)
+ | ((u_long)xmit_accm[unit][2] << 16)
+ | ((u_long)xmit_accm[unit][3] << 24);
+ LCPDEBUG(LOG_INFO, ("lcp_lowerup: asyncmap=%X %X %X %X\n",
+ xmit_accm[unit][3],
+ xmit_accm[unit][2],
+ xmit_accm[unit][1],
+ xmit_accm[unit][0]));
+
+ fsm_lowerup(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_lowerdown - The lower layer is down.
+ */
+void
+lcp_lowerdown(int unit)
+{
+ fsm_lowerdown(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_input - Input LCP packet.
+ */
+static void
+lcp_input(int unit, u_char *p, int len)
+{
+ fsm *f = &lcp_fsm[unit];
+
+ fsm_input(f, p, len);
+}
+
+
+/*
+ * lcp_extcode - Handle a LCP-specific code.
+ */
+static int
+lcp_extcode(fsm *f, int code, u_char id, u_char *inp, int len)
+{
+ u_char *magp;
+
+ switch( code ){
+ case PROTREJ:
+ lcp_rprotrej(f, inp, len);
+ break;
+
+ case ECHOREQ:
+ if (f->state != LS_OPENED) {
+ break;
+ }
+ LCPDEBUG(LOG_INFO, ("lcp: Echo-Request, Rcvd id %d\n", id));
+ magp = inp;
+ PUTLONG(lcp_gotoptions[f->unit].magicnumber, magp);
+ fsm_sdata(f, ECHOREP, id, inp, len);
+ break;
+
+ case ECHOREP:
+ lcp_received_echo_reply(f, id, inp, len);
+ break;
+
+ case DISCREQ:
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+/*
+ * lcp_rprotrej - Receive an Protocol-Reject.
+ *
+ * Figure out which protocol is rejected and inform it.
+ */
+static void
+lcp_rprotrej(fsm *f, u_char *inp, int len)
+{
+ int i;
+ struct protent *protp;
+ u_short prot;
+
+ if (len < (int)sizeof (u_short)) {
+ LCPDEBUG(LOG_INFO, ("lcp_rprotrej: Rcvd short Protocol-Reject packet!\n"));
+ return;
+ }
+
+ GETSHORT(prot, inp);
+
+ LCPDEBUG(LOG_INFO, ("lcp_rprotrej: Rcvd Protocol-Reject packet for %x!\n", prot));
+
+ /*
+ * Protocol-Reject packets received in any state other than the LCP
+ * LS_OPENED state SHOULD be silently discarded.
+ */
+ if( f->state != LS_OPENED ) {
+ LCPDEBUG(LOG_INFO, ("Protocol-Reject discarded: LCP in state %d\n", f->state));
+ return;
+ }
+
+ /*
+ * Upcall the proper Protocol-Reject routine.
+ */
+ for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
+ if (protp->protocol == prot && protp->enabled_flag) {
+ (*protp->protrej)(f->unit);
+ return;
+ }
+ }
+
+ LCPDEBUG(LOG_WARNING, ("Protocol-Reject for unsupported protocol 0x%x\n", prot));
+}
+
+
+/*
+ * lcp_protrej - A Protocol-Reject was received.
+ */
+static void
+lcp_protrej(int unit)
+{
+ LWIP_UNUSED_ARG(unit);
+ /*
+ * Can't reject LCP!
+ */
+ LCPDEBUG(LOG_WARNING, ("lcp_protrej: Received Protocol-Reject for LCP!\n"));
+ fsm_protreject(&lcp_fsm[unit]);
+}
+
+
+/*
+ * lcp_sprotrej - Send a Protocol-Reject for some protocol.
+ */
+void
+lcp_sprotrej(int unit, u_char *p, int len)
+{
+ /*
+ * Send back the protocol and the information field of the
+ * rejected packet. We only get here if LCP is in the LS_OPENED state.
+ */
+
+ fsm_sdata(&lcp_fsm[unit], PROTREJ, ++lcp_fsm[unit].id, p, len);
+}
+
+
+/*
+ * lcp_resetci - Reset our CI.
+ */
+static void
+lcp_resetci(fsm *f)
+{
+ lcp_wantoptions[f->unit].magicnumber = magic();
+ lcp_wantoptions[f->unit].numloops = 0;
+ lcp_gotoptions[f->unit] = lcp_wantoptions[f->unit];
+ peer_mru[f->unit] = PPP_MRU;
+ auth_reset(f->unit);
+}
+
+
+/*
+ * lcp_cilen - Return length of our CI.
+ */
+static int
+lcp_cilen(fsm *f)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+
+#define LENCIVOID(neg) ((neg) ? CILEN_VOID : 0)
+#define LENCICHAP(neg) ((neg) ? CILEN_CHAP : 0)
+#define LENCISHORT(neg) ((neg) ? CILEN_SHORT : 0)
+#define LENCILONG(neg) ((neg) ? CILEN_LONG : 0)
+#define LENCILQR(neg) ((neg) ? CILEN_LQR: 0)
+#define LENCICBCP(neg) ((neg) ? CILEN_CBCP: 0)
+ /*
+ * NB: we only ask for one of CHAP and UPAP, even if we will
+ * accept either.
+ */
+ return (LENCISHORT(go->neg_mru && go->mru != PPP_DEFMRU) +
+ LENCILONG(go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) +
+ LENCICHAP(go->neg_chap) +
+ LENCISHORT(!go->neg_chap && go->neg_upap) +
+ LENCILQR(go->neg_lqr) +
+ LENCICBCP(go->neg_cbcp) +
+ LENCILONG(go->neg_magicnumber) +
+ LENCIVOID(go->neg_pcompression) +
+ LENCIVOID(go->neg_accompression));
+}
+
+
+/*
+ * lcp_addci - Add our desired CIs to a packet.
+ */
+static void
+lcp_addci(fsm *f, u_char *ucp, int *lenp)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ u_char *start_ucp = ucp;
+
+#define ADDCIVOID(opt, neg) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: opt=%d\n", opt)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_VOID, ucp); \
+ }
+#define ADDCISHORT(opt, neg, val) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: INT opt=%d %X\n", opt, val)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_SHORT, ucp); \
+ PUTSHORT(val, ucp); \
+ }
+#define ADDCICHAP(opt, neg, val, digest) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: CHAP opt=%d %X\n", opt, val)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_CHAP, ucp); \
+ PUTSHORT(val, ucp); \
+ PUTCHAR(digest, ucp); \
+ }
+#define ADDCILONG(opt, neg, val) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: L opt=%d %lX\n", opt, val)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_LONG, ucp); \
+ PUTLONG(val, ucp); \
+ }
+#define ADDCILQR(opt, neg, val) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: LQR opt=%d %lX\n", opt, val)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_LQR, ucp); \
+ PUTSHORT(PPP_LQR, ucp); \
+ PUTLONG(val, ucp); \
+ }
+#define ADDCICHAR(opt, neg, val) \
+ if (neg) { \
+ LCPDEBUG(LOG_INFO, ("lcp_addci: CHAR opt=%d %X '%z'\n", opt, val, val)); \
+ PUTCHAR(opt, ucp); \
+ PUTCHAR(CILEN_CHAR, ucp); \
+ PUTCHAR(val, ucp); \
+ }
+
+ ADDCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru);
+ ADDCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap);
+ ADDCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype);
+ ADDCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+ ADDCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+ ADDCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
+ ADDCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+ ADDCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+ ADDCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+
+ if (ucp - start_ucp != *lenp) {
+ /* this should never happen, because peer_mtu should be 1500 */
+ LCPDEBUG(LOG_ERR, ("Bug in lcp_addci: wrong length\n"));
+ }
+}
+
+
+/*
+ * lcp_ackci - Ack our CIs.
+ * This should not modify any state if the Ack is bad.
+ *
+ * Returns:
+ * 0 - Ack was bad.
+ * 1 - Ack was good.
+ */
+static int
+lcp_ackci(fsm *f, u_char *p, int len)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ u_char cilen, citype, cichar;
+ u_short cishort;
+ u32_t cilong;
+
+ /*
+ * CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define ACKCIVOID(opt, neg) \
+ if (neg) { \
+ if ((len -= CILEN_VOID) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_VOID || citype != opt) \
+ goto bad; \
+ }
+#define ACKCISHORT(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_SHORT) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_SHORT || citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ }
+#define ACKCICHAR(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_CHAR) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_CHAR || citype != opt) \
+ goto bad; \
+ GETCHAR(cichar, p); \
+ if (cichar != val) \
+ goto bad; \
+ }
+#define ACKCICHAP(opt, neg, val, digest) \
+ if (neg) { \
+ if ((len -= CILEN_CHAP) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_CHAP || citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != val) \
+ goto bad; \
+ GETCHAR(cichar, p); \
+ if (cichar != digest) \
+ goto bad; \
+ }
+#define ACKCILONG(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_LONG) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_LONG || citype != opt) \
+ goto bad; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ goto bad; \
+ }
+#define ACKCILQR(opt, neg, val) \
+ if (neg) { \
+ if ((len -= CILEN_LQR) < 0) \
+ goto bad; \
+ GETCHAR(citype, p); \
+ GETCHAR(cilen, p); \
+ if (cilen != CILEN_LQR || citype != opt) \
+ goto bad; \
+ GETSHORT(cishort, p); \
+ if (cishort != PPP_LQR) \
+ goto bad; \
+ GETLONG(cilong, p); \
+ if (cilong != val) \
+ goto bad; \
+ }
+
+ ACKCISHORT(CI_MRU, go->neg_mru && go->mru != PPP_DEFMRU, go->mru);
+ ACKCILONG(CI_ASYNCMAP, go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl, go->asyncmap);
+ ACKCICHAP(CI_AUTHTYPE, go->neg_chap, PPP_CHAP, go->chap_mdtype);
+ ACKCISHORT(CI_AUTHTYPE, !go->neg_chap && go->neg_upap, PPP_PAP);
+ ACKCILQR(CI_QUALITY, go->neg_lqr, go->lqr_period);
+ ACKCICHAR(CI_CALLBACK, go->neg_cbcp, CBCP_OPT);
+ ACKCILONG(CI_MAGICNUMBER, go->neg_magicnumber, go->magicnumber);
+ ACKCIVOID(CI_PCOMPRESSION, go->neg_pcompression);
+ ACKCIVOID(CI_ACCOMPRESSION, go->neg_accompression);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0) {
+ goto bad;
+ }
+ LCPDEBUG(LOG_INFO, ("lcp_acki: Ack\n"));
+ return (1);
+bad:
+ LCPDEBUG(LOG_WARNING, ("lcp_acki: received bad Ack!\n"));
+ return (0);
+}
+
+
+/*
+ * lcp_nakci - Peer has sent a NAK for some of our CIs.
+ * This should not modify any state if the Nak is bad
+ * or if LCP is in the LS_OPENED state.
+ *
+ * Returns:
+ * 0 - Nak was bad.
+ * 1 - Nak was good.
+ */
+static int
+lcp_nakci(fsm *f, u_char *p, int len)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *wo = &lcp_wantoptions[f->unit];
+ u_char citype, cichar, *next;
+ u_short cishort;
+ u32_t cilong;
+ lcp_options no; /* options we've seen Naks for */
+ lcp_options try; /* options to request next time */
+ int looped_back = 0;
+ int cilen;
+
+ BZERO(&no, sizeof(no));
+ try = *go;
+
+ /*
+ * Any Nak'd CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define NAKCIVOID(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_VOID && \
+ p[1] == CILEN_VOID && \
+ p[0] == opt) { \
+ len -= CILEN_VOID; \
+ INCPTR(CILEN_VOID, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCICHAP(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_CHAP && \
+ p[1] == CILEN_CHAP && \
+ p[0] == opt) { \
+ len -= CILEN_CHAP; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETCHAR(cichar, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCICHAR(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_CHAR && \
+ p[1] == CILEN_CHAR && \
+ p[0] == opt) { \
+ len -= CILEN_CHAR; \
+ INCPTR(2, p); \
+ GETCHAR(cichar, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCISHORT(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_SHORT && \
+ p[1] == CILEN_SHORT && \
+ p[0] == opt) { \
+ len -= CILEN_SHORT; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCILONG(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_LONG && \
+ p[1] == CILEN_LONG && \
+ p[0] == opt) { \
+ len -= CILEN_LONG; \
+ INCPTR(2, p); \
+ GETLONG(cilong, p); \
+ no.neg = 1; \
+ code \
+ }
+#define NAKCILQR(opt, neg, code) \
+ if (go->neg && \
+ len >= CILEN_LQR && \
+ p[1] == CILEN_LQR && \
+ p[0] == opt) { \
+ len -= CILEN_LQR; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETLONG(cilong, p); \
+ no.neg = 1; \
+ code \
+ }
+
+ /*
+ * We don't care if they want to send us smaller packets than
+ * we want. Therefore, accept any MRU less than what we asked for,
+ * but then ignore the new value when setting the MRU in the kernel.
+ * If they send us a bigger MRU than what we asked, accept it, up to
+ * the limit of the default MRU we'd get if we didn't negotiate.
+ */
+ if (go->neg_mru && go->mru != PPP_DEFMRU) {
+ NAKCISHORT(CI_MRU, neg_mru,
+ if (cishort <= wo->mru || cishort < PPP_DEFMRU) {
+ try.mru = cishort;
+ }
+ );
+ }
+
+ /*
+ * Add any characters they want to our (receive-side) asyncmap.
+ */
+ if (go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl) {
+ NAKCILONG(CI_ASYNCMAP, neg_asyncmap,
+ try.asyncmap = go->asyncmap | cilong;
+ );
+ }
+
+ /*
+ * If they've nak'd our authentication-protocol, check whether
+ * they are proposing a different protocol, or a different
+ * hash algorithm for CHAP.
+ */
+ if ((go->neg_chap || go->neg_upap)
+ && len >= CILEN_SHORT
+ && p[0] == CI_AUTHTYPE && p[1] >= CILEN_SHORT && p[1] <= len) {
+ cilen = p[1];
+ len -= cilen;
+ no.neg_chap = go->neg_chap;
+ no.neg_upap = go->neg_upap;
+ INCPTR(2, p);
+ GETSHORT(cishort, p);
+ if (cishort == PPP_PAP && cilen == CILEN_SHORT) {
+ /*
+ * If we were asking for CHAP, they obviously don't want to do it.
+ * If we weren't asking for CHAP, then we were asking for PAP,
+ * in which case this Nak is bad.
+ */
+ if (!go->neg_chap) {
+ goto bad;
+ }
+ try.neg_chap = 0;
+
+ } else if (cishort == PPP_CHAP && cilen == CILEN_CHAP) {
+ GETCHAR(cichar, p);
+ if (go->neg_chap) {
+ /*
+ * We were asking for CHAP/MD5; they must want a different
+ * algorithm. If they can't do MD5, we'll have to stop
+ * asking for CHAP.
+ */
+ if (cichar != go->chap_mdtype) {
+ try.neg_chap = 0;
+ }
+ } else {
+ /*
+ * Stop asking for PAP if we were asking for it.
+ */
+ try.neg_upap = 0;
+ }
+
+ } else {
+ /*
+ * We don't recognize what they're suggesting.
+ * Stop asking for what we were asking for.
+ */
+ if (go->neg_chap) {
+ try.neg_chap = 0;
+ } else {
+ try.neg_upap = 0;
+ }
+ p += cilen - CILEN_SHORT;
+ }
+ }
+
+ /*
+ * If they can't cope with our link quality protocol, we'll have
+ * to stop asking for LQR. We haven't got any other protocol.
+ * If they Nak the reporting period, take their value XXX ?
+ */
+ NAKCILQR(CI_QUALITY, neg_lqr,
+ if (cishort != PPP_LQR) {
+ try.neg_lqr = 0;
+ } else {
+ try.lqr_period = cilong;
+ }
+ );
+
+ /*
+ * Only implementing CBCP...not the rest of the callback options
+ */
+ NAKCICHAR(CI_CALLBACK, neg_cbcp,
+ try.neg_cbcp = 0;
+ );
+
+ /*
+ * Check for a looped-back line.
+ */
+ NAKCILONG(CI_MAGICNUMBER, neg_magicnumber,
+ try.magicnumber = magic();
+ looped_back = 1;
+ );
+
+ /*
+ * Peer shouldn't send Nak for protocol compression or
+ * address/control compression requests; they should send
+ * a Reject instead. If they send a Nak, treat it as a Reject.
+ */
+ NAKCIVOID(CI_PCOMPRESSION, neg_pcompression,
+ try.neg_pcompression = 0;
+ );
+ NAKCIVOID(CI_ACCOMPRESSION, neg_accompression,
+ try.neg_accompression = 0;
+ );
+
+ /*
+ * There may be remaining CIs, if the peer is requesting negotiation
+ * on an option that we didn't include in our request packet.
+ * If we see an option that we requested, or one we've already seen
+ * in this packet, then this packet is bad.
+ * If we wanted to respond by starting to negotiate on the requested
+ * option(s), we could, but we don't, because except for the
+ * authentication type and quality protocol, if we are not negotiating
+ * an option, it is because we were told not to.
+ * For the authentication type, the Nak from the peer means
+ * `let me authenticate myself with you' which is a bit pointless.
+ * For the quality protocol, the Nak means `ask me to send you quality
+ * reports', but if we didn't ask for them, we don't want them.
+ * An option we don't recognize represents the peer asking to
+ * negotiate some option we don't support, so ignore it.
+ */
+ while (len > CILEN_VOID) {
+ GETCHAR(citype, p);
+ GETCHAR(cilen, p);
+ if (cilen < CILEN_VOID || (len -= cilen) < 0) {
+ goto bad;
+ }
+ next = p + cilen - 2;
+
+ switch (citype) {
+ case CI_MRU:
+ if ((go->neg_mru && go->mru != PPP_DEFMRU)
+ || no.neg_mru || cilen != CILEN_SHORT) {
+ goto bad;
+ }
+ GETSHORT(cishort, p);
+ if (cishort < PPP_DEFMRU) {
+ try.mru = cishort;
+ }
+ break;
+ case CI_ASYNCMAP:
+ if ((go->neg_asyncmap && go->asyncmap != 0xFFFFFFFFl)
+ || no.neg_asyncmap || cilen != CILEN_LONG) {
+ goto bad;
+ }
+ break;
+ case CI_AUTHTYPE:
+ if (go->neg_chap || no.neg_chap || go->neg_upap || no.neg_upap) {
+ goto bad;
+ }
+ break;
+ case CI_MAGICNUMBER:
+ if (go->neg_magicnumber || no.neg_magicnumber ||
+ cilen != CILEN_LONG) {
+ goto bad;
+ }
+ break;
+ case CI_PCOMPRESSION:
+ if (go->neg_pcompression || no.neg_pcompression
+ || cilen != CILEN_VOID) {
+ goto bad;
+ }
+ break;
+ case CI_ACCOMPRESSION:
+ if (go->neg_accompression || no.neg_accompression
+ || cilen != CILEN_VOID) {
+ goto bad;
+ }
+ break;
+ case CI_QUALITY:
+ if (go->neg_lqr || no.neg_lqr || cilen != CILEN_LQR) {
+ goto bad;
+ }
+ break;
+ }
+ p = next;
+ }
+
+ /* If there is still anything left, this packet is bad. */
+ if (len != 0) {
+ goto bad;
+ }
+
+ /*
+ * OK, the Nak is good. Now we can update state.
+ */
+ if (f->state != LS_OPENED) {
+ if (looped_back) {
+ if (++try.numloops >= lcp_loopbackfail) {
+ LCPDEBUG(LOG_NOTICE, ("Serial line is looped back.\n"));
+ lcp_close(f->unit, "Loopback detected");
+ }
+ } else {
+ try.numloops = 0;
+ }
+ *go = try;
+ }
+
+ return 1;
+
+bad:
+ LCPDEBUG(LOG_WARNING, ("lcp_nakci: received bad Nak!\n"));
+ return 0;
+}
+
+
+/*
+ * lcp_rejci - Peer has Rejected some of our CIs.
+ * This should not modify any state if the Reject is bad
+ * or if LCP is in the LS_OPENED state.
+ *
+ * Returns:
+ * 0 - Reject was bad.
+ * 1 - Reject was good.
+ */
+static int
+lcp_rejci(fsm *f, u_char *p, int len)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ u_char cichar;
+ u_short cishort;
+ u32_t cilong;
+ lcp_options try; /* options to request next time */
+
+ try = *go;
+
+ /*
+ * Any Rejected CIs must be in exactly the same order that we sent.
+ * Check packet length and CI length at each step.
+ * If we find any deviations, then this packet is bad.
+ */
+#define REJCIVOID(opt, neg) \
+ if (go->neg && \
+ len >= CILEN_VOID && \
+ p[1] == CILEN_VOID && \
+ p[0] == opt) { \
+ len -= CILEN_VOID; \
+ INCPTR(CILEN_VOID, p); \
+ try.neg = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: void opt %d rejected\n", opt)); \
+ }
+#define REJCISHORT(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_SHORT && \
+ p[1] == CILEN_SHORT && \
+ p[0] == opt) { \
+ len -= CILEN_SHORT; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ /* Check rejected value. */ \
+ if (cishort != val) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: short opt %d rejected\n", opt)); \
+ }
+#define REJCICHAP(opt, neg, val, digest) \
+ if (go->neg && \
+ len >= CILEN_CHAP && \
+ p[1] == CILEN_CHAP && \
+ p[0] == opt) { \
+ len -= CILEN_CHAP; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETCHAR(cichar, p); \
+ /* Check rejected value. */ \
+ if (cishort != val || cichar != digest) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ try.neg_upap = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: chap opt %d rejected\n", opt)); \
+ }
+#define REJCILONG(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_LONG && \
+ p[1] == CILEN_LONG && \
+ p[0] == opt) { \
+ len -= CILEN_LONG; \
+ INCPTR(2, p); \
+ GETLONG(cilong, p); \
+ /* Check rejected value. */ \
+ if (cilong != val) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: long opt %d rejected\n", opt)); \
+ }
+#define REJCILQR(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_LQR && \
+ p[1] == CILEN_LQR && \
+ p[0] == opt) { \
+ len -= CILEN_LQR; \
+ INCPTR(2, p); \
+ GETSHORT(cishort, p); \
+ GETLONG(cilong, p); \
+ /* Check rejected value. */ \
+ if (cishort != PPP_LQR || cilong != val) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: LQR opt %d rejected\n", opt)); \
+ }
+#define REJCICBCP(opt, neg, val) \
+ if (go->neg && \
+ len >= CILEN_CBCP && \
+ p[1] == CILEN_CBCP && \
+ p[0] == opt) { \
+ len -= CILEN_CBCP; \
+ INCPTR(2, p); \
+ GETCHAR(cichar, p); \
+ /* Check rejected value. */ \
+ if (cichar != val) { \
+ goto bad; \
+ } \
+ try.neg = 0; \
+ LCPDEBUG(LOG_INFO, ("lcp_rejci: Callback opt %d rejected\n", opt)); \
+ }
+
+ REJCISHORT(CI_MRU, neg_mru, go->mru);
+ REJCILONG(CI_ASYNCMAP, neg_asyncmap, go->asyncmap);
+ REJCICHAP(CI_AUTHTYPE, neg_chap, PPP_CHAP, go->chap_mdtype);
+ if (!go->neg_chap) {
+ REJCISHORT(CI_AUTHTYPE, neg_upap, PPP_PAP);
+ }
+ REJCILQR(CI_QUALITY, neg_lqr, go->lqr_period);
+ REJCICBCP(CI_CALLBACK, neg_cbcp, CBCP_OPT);
+ REJCILONG(CI_MAGICNUMBER, neg_magicnumber, go->magicnumber);
+ REJCIVOID(CI_PCOMPRESSION, neg_pcompression);
+ REJCIVOID(CI_ACCOMPRESSION, neg_accompression);
+
+ /*
+ * If there are any remaining CIs, then this packet is bad.
+ */
+ if (len != 0) {
+ goto bad;
+ }
+ /*
+ * Now we can update state.
+ */
+ if (f->state != LS_OPENED) {
+ *go = try;
+ }
+ return 1;
+
+bad:
+ LCPDEBUG(LOG_WARNING, ("lcp_rejci: received bad Reject!\n"));
+ return 0;
+}
+
+
+/*
+ * lcp_reqci - Check the peer's requested CIs and send appropriate response.
+ *
+ * Returns: CONFACK, CONFNAK or CONFREJ and input packet modified
+ * appropriately. If reject_if_disagree is non-zero, doesn't return
+ * CONFNAK; returns CONFREJ if it can't return CONFACK.
+ */
+static int
+lcp_reqci(fsm *f,
+ u_char *inp, /* Requested CIs */
+ int *lenp, /* Length of requested CIs */
+ int reject_if_disagree)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *ho = &lcp_hisoptions[f->unit];
+ lcp_options *ao = &lcp_allowoptions[f->unit];
+ u_char *cip, *next; /* Pointer to current and next CIs */
+ int cilen, citype; /* Parsed len, type */
+ u_char cichar; /* Parsed char value */
+ u_short cishort; /* Parsed short value */
+ u32_t cilong; /* Parse long value */
+ int rc = CONFACK; /* Final packet return code */
+ int orc; /* Individual option return code */
+ u_char *p; /* Pointer to next char to parse */
+ u_char *rejp; /* Pointer to next char in reject frame */
+ u_char *nakp; /* Pointer to next char in Nak frame */
+ int l = *lenp; /* Length left */
+#if TRACELCP > 0
+ char traceBuf[80];
+ size_t traceNdx = 0;
+#endif
+
+ /*
+ * Reset all his options.
+ */
+ BZERO(ho, sizeof(*ho));
+
+ /*
+ * Process all his options.
+ */
+ next = inp;
+ nakp = nak_buffer;
+ rejp = inp;
+ while (l) {
+ orc = CONFACK; /* Assume success */
+ cip = p = next; /* Remember begining of CI */
+ if (l < 2 || /* Not enough data for CI header or */
+ p[1] < 2 || /* CI length too small or */
+ p[1] > l) { /* CI length too big? */
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: bad CI length!\n"));
+ orc = CONFREJ; /* Reject bad CI */
+ cilen = l; /* Reject till end of packet */
+ l = 0; /* Don't loop again */
+ citype = 0;
+ goto endswitch;
+ }
+ GETCHAR(citype, p); /* Parse CI type */
+ GETCHAR(cilen, p); /* Parse CI length */
+ l -= cilen; /* Adjust remaining length */
+ next += cilen; /* Step to next CI */
+
+ switch (citype) { /* Check CI type */
+ case CI_MRU:
+ if (!ao->neg_mru) { /* Allow option? */
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject MRU - not allowed\n"));
+ orc = CONFREJ; /* Reject CI */
+ break;
+ } else if (cilen != CILEN_SHORT) { /* Check CI length */
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject MRU - bad length\n"));
+ orc = CONFREJ; /* Reject CI */
+ break;
+ }
+ GETSHORT(cishort, p); /* Parse MRU */
+
+ /*
+ * He must be able to receive at least our minimum.
+ * No need to check a maximum. If he sends a large number,
+ * we'll just ignore it.
+ */
+ if (cishort < PPP_MINMRU) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Nak - MRU too small\n"));
+ orc = CONFNAK; /* Nak CI */
+ PUTCHAR(CI_MRU, nakp);
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(PPP_MINMRU, nakp); /* Give him a hint */
+ break;
+ }
+ ho->neg_mru = 1; /* Remember he sent MRU */
+ ho->mru = cishort; /* And remember value */
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MRU %d", cishort);
+ traceNdx = strlen(traceBuf);
+#endif
+ break;
+
+ case CI_ASYNCMAP:
+ if (!ao->neg_asyncmap) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject ASYNCMAP not allowed\n"));
+ orc = CONFREJ;
+ break;
+ } else if (cilen != CILEN_LONG) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject ASYNCMAP bad length\n"));
+ orc = CONFREJ;
+ break;
+ }
+ GETLONG(cilong, p);
+
+ /*
+ * Asyncmap must have set at least the bits
+ * which are set in lcp_allowoptions[unit].asyncmap.
+ */
+ if ((ao->asyncmap & ~cilong) != 0) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Nak ASYNCMAP %lX missing %lX\n",
+ cilong, ao->asyncmap));
+ orc = CONFNAK;
+ PUTCHAR(CI_ASYNCMAP, nakp);
+ PUTCHAR(CILEN_LONG, nakp);
+ PUTLONG(ao->asyncmap | cilong, nakp);
+ break;
+ }
+ ho->neg_asyncmap = 1;
+ ho->asyncmap = cilong;
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ASYNCMAP=%lX", cilong);
+ traceNdx = strlen(traceBuf);
+#endif
+ break;
+
+ case CI_AUTHTYPE:
+ if (cilen < CILEN_SHORT) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject AUTHTYPE missing arg\n"));
+ orc = CONFREJ;
+ break;
+ } else if (!(ao->neg_upap || ao->neg_chap)) {
+ /*
+ * Reject the option if we're not willing to authenticate.
+ */
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: Reject AUTHTYPE not allowed\n"));
+ orc = CONFREJ;
+ break;
+ }
+ GETSHORT(cishort, p);
+
+ /*
+ * Authtype must be UPAP or CHAP.
+ *
+ * Note: if both ao->neg_upap and ao->neg_chap are set,
+ * and the peer sends a Configure-Request with two
+ * authenticate-protocol requests, one for CHAP and one
+ * for UPAP, then we will reject the second request.
+ * Whether we end up doing CHAP or UPAP depends then on
+ * the ordering of the CIs in the peer's Configure-Request.
+ */
+
+ if (cishort == PPP_PAP) {
+ if (ho->neg_chap) { /* we've already accepted CHAP */
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE PAP already accepted\n"));
+ orc = CONFREJ;
+ break;
+ } else if (cilen != CILEN_SHORT) {
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE PAP bad len\n"));
+ orc = CONFREJ;
+ break;
+ }
+ if (!ao->neg_upap) { /* we don't want to do PAP */
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE PAP not allowed\n"));
+ orc = CONFNAK; /* NAK it and suggest CHAP */
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ PUTCHAR(CILEN_CHAP, nakp);
+ PUTSHORT(PPP_CHAP, nakp);
+ PUTCHAR(ao->chap_mdtype, nakp);
+ break;
+ }
+ ho->neg_upap = 1;
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PAP (%X)", cishort);
+ traceNdx = strlen(traceBuf);
+#endif
+ break;
+ }
+ if (cishort == PPP_CHAP) {
+ if (ho->neg_upap) { /* we've already accepted PAP */
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE CHAP accepted PAP\n"));
+ orc = CONFREJ;
+ break;
+ } else if (cilen != CILEN_CHAP) {
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Reject AUTHTYPE CHAP bad len\n"));
+ orc = CONFREJ;
+ break;
+ }
+ if (!ao->neg_chap) { /* we don't want to do CHAP */
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE CHAP not allowed\n"));
+ orc = CONFNAK; /* NAK it and suggest PAP */
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(PPP_PAP, nakp);
+ break;
+ }
+ GETCHAR(cichar, p); /* get digest type*/
+ if (cichar != CHAP_DIGEST_MD5
+#if MSCHAP_SUPPORT
+ && cichar != CHAP_MICROSOFT
+#endif
+ ) {
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE CHAP digest=%d\n", (int)cichar));
+ orc = CONFNAK;
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ PUTCHAR(CILEN_CHAP, nakp);
+ PUTSHORT(PPP_CHAP, nakp);
+ PUTCHAR(ao->chap_mdtype, nakp);
+ break;
+ }
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CHAP %X,%d", cishort, (int)cichar);
+ traceNdx = strlen(traceBuf);
+#endif
+ ho->chap_mdtype = cichar; /* save md type */
+ ho->neg_chap = 1;
+ break;
+ }
+
+ /*
+ * We don't recognize the protocol they're asking for.
+ * Nak it with something we're willing to do.
+ * (At this point we know ao->neg_upap || ao->neg_chap.)
+ */
+ orc = CONFNAK;
+ PUTCHAR(CI_AUTHTYPE, nakp);
+ if (ao->neg_chap) {
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE %d req CHAP\n", cishort));
+ PUTCHAR(CILEN_CHAP, nakp);
+ PUTSHORT(PPP_CHAP, nakp);
+ PUTCHAR(ao->chap_mdtype, nakp);
+ } else {
+ LCPDEBUG(LOG_WARNING, ("lcp_reqci: Nak AUTHTYPE %d req PAP\n", cishort));
+ PUTCHAR(CILEN_SHORT, nakp);
+ PUTSHORT(PPP_PAP, nakp);
+ }
+ break;
+
+ case CI_QUALITY:
+ GETSHORT(cishort, p);
+ GETLONG(cilong, p);
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " QUALITY (%x %x)", cishort, (unsigned int) cilong);
+ traceNdx = strlen(traceBuf);
+#endif
+
+ if (!ao->neg_lqr ||
+ cilen != CILEN_LQR) {
+ orc = CONFREJ;
+ break;
+ }
+
+ /*
+ * Check the protocol and the reporting period.
+ * XXX When should we Nak this, and what with?
+ */
+ if (cishort != PPP_LQR) {
+ orc = CONFNAK;
+ PUTCHAR(CI_QUALITY, nakp);
+ PUTCHAR(CILEN_LQR, nakp);
+ PUTSHORT(PPP_LQR, nakp);
+ PUTLONG(ao->lqr_period, nakp);
+ break;
+ }
+ break;
+
+ case CI_MAGICNUMBER:
+ if (!(ao->neg_magicnumber || go->neg_magicnumber) ||
+ cilen != CILEN_LONG) {
+ orc = CONFREJ;
+ break;
+ }
+ GETLONG(cilong, p);
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " MAGICNUMBER (%lX)", cilong);
+ traceNdx = strlen(traceBuf);
+#endif
+
+ /*
+ * He must have a different magic number.
+ */
+ if (go->neg_magicnumber &&
+ cilong == go->magicnumber) {
+ cilong = magic(); /* Don't put magic() inside macro! */
+ orc = CONFNAK;
+ PUTCHAR(CI_MAGICNUMBER, nakp);
+ PUTCHAR(CILEN_LONG, nakp);
+ PUTLONG(cilong, nakp);
+ break;
+ }
+ ho->neg_magicnumber = 1;
+ ho->magicnumber = cilong;
+ break;
+
+
+ case CI_PCOMPRESSION:
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " PCOMPRESSION");
+ traceNdx = strlen(traceBuf);
+#endif
+ if (!ao->neg_pcompression ||
+ cilen != CILEN_VOID) {
+ orc = CONFREJ;
+ break;
+ }
+ ho->neg_pcompression = 1;
+ break;
+
+ case CI_ACCOMPRESSION:
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " ACCOMPRESSION");
+ traceNdx = strlen(traceBuf);
+#endif
+ if (!ao->neg_accompression ||
+ cilen != CILEN_VOID) {
+ orc = CONFREJ;
+ break;
+ }
+ ho->neg_accompression = 1;
+ break;
+
+ case CI_MRRU:
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_MRRU");
+ traceNdx = strlen(traceBuf);
+#endif
+ orc = CONFREJ;
+ break;
+
+ case CI_SSNHF:
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_SSNHF");
+ traceNdx = strlen(traceBuf);
+#endif
+ orc = CONFREJ;
+ break;
+
+ case CI_EPDISC:
+#if TRACELCP > 0
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " CI_EPDISC");
+ traceNdx = strlen(traceBuf);
+#endif
+ orc = CONFREJ;
+ break;
+
+ default:
+#if TRACELCP
+ snprintf(&traceBuf[traceNdx], sizeof(traceBuf), " unknown %d", citype);
+ traceNdx = strlen(traceBuf);
+#endif
+ orc = CONFREJ;
+ break;
+ }
+
+ endswitch:
+#if TRACELCP
+ if (traceNdx >= 80 - 32) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: rcvd%s\n", traceBuf));
+ traceNdx = 0;
+ }
+#endif
+ if (orc == CONFACK && /* Good CI */
+ rc != CONFACK) { /* but prior CI wasnt? */
+ continue; /* Don't send this one */
+ }
+
+ if (orc == CONFNAK) { /* Nak this CI? */
+ if (reject_if_disagree /* Getting fed up with sending NAKs? */
+ && citype != CI_MAGICNUMBER) {
+ orc = CONFREJ; /* Get tough if so */
+ } else {
+ if (rc == CONFREJ) { /* Rejecting prior CI? */
+ continue; /* Don't send this one */
+ }
+ rc = CONFNAK;
+ }
+ }
+ if (orc == CONFREJ) { /* Reject this CI */
+ rc = CONFREJ;
+ if (cip != rejp) { /* Need to move rejected CI? */
+ BCOPY(cip, rejp, cilen); /* Move it */
+ }
+ INCPTR(cilen, rejp); /* Update output pointer */
+ }
+ }
+
+ /*
+ * If we wanted to send additional NAKs (for unsent CIs), the
+ * code would go here. The extra NAKs would go at *nakp.
+ * At present there are no cases where we want to ask the
+ * peer to negotiate an option.
+ */
+
+ switch (rc) {
+ case CONFACK:
+ *lenp = (int)(next - inp);
+ break;
+ case CONFNAK:
+ /*
+ * Copy the Nak'd options from the nak_buffer to the caller's buffer.
+ */
+ *lenp = (int)(nakp - nak_buffer);
+ BCOPY(nak_buffer, inp, *lenp);
+ break;
+ case CONFREJ:
+ *lenp = (int)(rejp - inp);
+ break;
+ }
+
+#if TRACELCP > 0
+ if (traceNdx > 0) {
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: %s\n", traceBuf));
+ }
+#endif
+ LCPDEBUG(LOG_INFO, ("lcp_reqci: returning CONF%s.\n", CODENAME(rc)));
+ return (rc); /* Return final code */
+}
+
+
+/*
+ * lcp_up - LCP has come UP.
+ */
+static void
+lcp_up(fsm *f)
+{
+ lcp_options *wo = &lcp_wantoptions[f->unit];
+ lcp_options *ho = &lcp_hisoptions[f->unit];
+ lcp_options *go = &lcp_gotoptions[f->unit];
+ lcp_options *ao = &lcp_allowoptions[f->unit];
+
+ if (!go->neg_magicnumber) {
+ go->magicnumber = 0;
+ }
+ if (!ho->neg_magicnumber) {
+ ho->magicnumber = 0;
+ }
+
+ /*
+ * Set our MTU to the smaller of the MTU we wanted and
+ * the MRU our peer wanted. If we negotiated an MRU,
+ * set our MRU to the larger of value we wanted and
+ * the value we got in the negotiation.
+ */
+ ppp_send_config(f->unit, LWIP_MIN(ao->mru, (ho->neg_mru? ho->mru: PPP_MRU)),
+ (ho->neg_asyncmap? ho->asyncmap: 0xffffffffl),
+ ho->neg_pcompression, ho->neg_accompression);
+ /*
+ * If the asyncmap hasn't been negotiated, we really should
+ * set the receive asyncmap to ffffffff, but we set it to 0
+ * for backwards contemptibility.
+ */
+ ppp_recv_config(f->unit, (go->neg_mru? LWIP_MAX(wo->mru, go->mru): PPP_MRU),
+ (go->neg_asyncmap? go->asyncmap: 0x00000000),
+ go->neg_pcompression, go->neg_accompression);
+
+ if (ho->neg_mru) {
+ peer_mru[f->unit] = ho->mru;
+ }
+
+ lcp_echo_lowerup(f->unit); /* Enable echo messages */
+
+ link_established(f->unit); /* The link is up; authenticate now */
+}
+
+
+/*
+ * lcp_down - LCP has gone DOWN.
+ *
+ * Alert other protocols.
+ */
+static void
+lcp_down(fsm *f)
+{
+ lcp_options *go = &lcp_gotoptions[f->unit];
+
+ lcp_echo_lowerdown(f->unit);
+
+ link_down(f->unit);
+
+ ppp_send_config(f->unit, PPP_MRU, 0xffffffffl, 0, 0);
+ ppp_recv_config(f->unit, PPP_MRU,
+ (go->neg_asyncmap? go->asyncmap: 0x00000000),
+ go->neg_pcompression, go->neg_accompression);
+ peer_mru[f->unit] = PPP_MRU;
+}
+
+
+/*
+ * lcp_starting - LCP needs the lower layer up.
+ */
+static void
+lcp_starting(fsm *f)
+{
+ link_required(f->unit); /* lwip: currently does nothing */
+}
+
+
+/*
+ * lcp_finished - LCP has finished with the lower layer.
+ */
+static void
+lcp_finished(fsm *f)
+{
+ link_terminated(f->unit); /* we are finished with the link */
+}
+
+
+#if PPP_ADDITIONAL_CALLBACKS
+/*
+ * print_string - print a readable representation of a string using
+ * printer.
+ */
+static void
+print_string( char *p, int len, void (*printer) (void *, char *, ...), void *arg)
+{
+ int c;
+
+ printer(arg, "\"");
+ for (; len > 0; --len) {
+ c = *p++;
+ if (' ' <= c && c <= '~') {
+ if (c == '\\' || c == '"') {
+ printer(arg, "\\");
+ }
+ printer(arg, "%c", c);
+ } else {
+ switch (c) {
+ case '\n':
+ printer(arg, "\\n");
+ break;
+ case '\r':
+ printer(arg, "\\r");
+ break;
+ case '\t':
+ printer(arg, "\\t");
+ break;
+ default:
+ printer(arg, "\\%.3o", c);
+ }
+ }
+ }
+ printer(arg, "\"");
+}
+
+
+/*
+ * lcp_printpkt - print the contents of an LCP packet.
+ */
+static char *lcp_codenames[] = {
+ "ConfReq", "ConfAck", "ConfNak", "ConfRej",
+ "TermReq", "TermAck", "CodeRej", "ProtRej",
+ "EchoReq", "EchoRep", "DiscReq"
+};
+
+static int
+lcp_printpkt( u_char *p, int plen, void (*printer) (void *, char *, ...), void *arg)
+{
+ int code, id, len, olen;
+ u_char *pstart, *optend;
+ u_short cishort;
+ u32_t cilong;
+
+ if (plen < HEADERLEN) {
+ return 0;
+ }
+ pstart = p;
+ GETCHAR(code, p);
+ GETCHAR(id, p);
+ GETSHORT(len, p);
+ if (len < HEADERLEN || len > plen) {
+ return 0;
+ }
+
+ if (code >= 1 && code <= sizeof(lcp_codenames) / sizeof(char *)) {
+ printer(arg, " %s", lcp_codenames[code-1]);
+ } else {
+ printer(arg, " code=0x%x", code);
+ }
+ printer(arg, " id=0x%x", id);
+ len -= HEADERLEN;
+ switch (code) {
+ case CONFREQ:
+ case CONFACK:
+ case CONFNAK:
+ case CONFREJ:
+ /* print option list */
+ while (len >= 2) {
+ GETCHAR(code, p);
+ GETCHAR(olen, p);
+ p -= 2;
+ if (olen < 2 || olen > len) {
+ break;
+ }
+ printer(arg, " <");
+ len -= olen;
+ optend = p + olen;
+ switch (code) {
+ case CI_MRU:
+ if (olen == CILEN_SHORT) {
+ p += 2;
+ GETSHORT(cishort, p);
+ printer(arg, "mru %d", cishort);
+ }
+ break;
+ case CI_ASYNCMAP:
+ if (olen == CILEN_LONG) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "asyncmap 0x%lx", cilong);
+ }
+ break;
+ case CI_AUTHTYPE:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ printer(arg, "auth ");
+ GETSHORT(cishort, p);
+ switch (cishort) {
+ case PPP_PAP:
+ printer(arg, "pap");
+ break;
+ case PPP_CHAP:
+ printer(arg, "chap");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_QUALITY:
+ if (olen >= CILEN_SHORT) {
+ p += 2;
+ printer(arg, "quality ");
+ GETSHORT(cishort, p);
+ switch (cishort) {
+ case PPP_LQR:
+ printer(arg, "lqr");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_CALLBACK:
+ if (olen >= CILEN_CHAR) {
+ p += 2;
+ printer(arg, "callback ");
+ GETSHORT(cishort, p);
+ switch (cishort) {
+ case CBCP_OPT:
+ printer(arg, "CBCP");
+ break;
+ default:
+ printer(arg, "0x%x", cishort);
+ }
+ }
+ break;
+ case CI_MAGICNUMBER:
+ if (olen == CILEN_LONG) {
+ p += 2;
+ GETLONG(cilong, p);
+ printer(arg, "magic 0x%x", cilong);
+ }
+ break;
+ case CI_PCOMPRESSION:
+ if (olen == CILEN_VOID) {
+ p += 2;
+ printer(arg, "pcomp");
+ }
+ break;
+ case CI_ACCOMPRESSION:
+ if (olen == CILEN_VOID) {
+ p += 2;
+ printer(arg, "accomp");
+ }
+ break;
+ }
+ while (p < optend) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+ printer(arg, ">");
+ }
+ break;
+
+ case TERMACK:
+ case TERMREQ:
+ if (len > 0 && *p >= ' ' && *p < 0x7f) {
+ printer(arg, " ");
+ print_string((char*)p, len, printer, arg);
+ p += len;
+ len = 0;
+ }
+ break;
+
+ case ECHOREQ:
+ case ECHOREP:
+ case DISCREQ:
+ if (len >= 4) {
+ GETLONG(cilong, p);
+ printer(arg, " magic=0x%x", cilong);
+ p += 4;
+ len -= 4;
+ }
+ break;
+ }
+
+ /* print the rest of the bytes in the packet */
+ for (; len > 0; --len) {
+ GETCHAR(code, p);
+ printer(arg, " %.2x", code);
+ }
+
+ return (int)(p - pstart);
+}
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+
+/*
+ * Time to shut down the link because there is nothing out there.
+ */
+static void
+LcpLinkFailure (fsm *f)
+{
+ if (f->state == LS_OPENED) {
+ LCPDEBUG(LOG_INFO, ("No response to %d echo-requests\n", lcp_echos_pending));
+ LCPDEBUG(LOG_NOTICE, ("Serial link appears to be disconnected.\n"));
+ lcp_close(f->unit, "Peer not responding");
+ }
+}
+
+/*
+ * Timer expired for the LCP echo requests from this process.
+ */
+static void
+LcpEchoCheck (fsm *f)
+{
+ LcpSendEchoRequest (f);
+
+ /*
+ * Start the timer for the next interval.
+ */
+ LWIP_ASSERT("lcp_echo_timer_running == 0", lcp_echo_timer_running == 0);
+
+ TIMEOUT (LcpEchoTimeout, f, lcp_echo_interval);
+ lcp_echo_timer_running = 1;
+}
+
+/*
+ * LcpEchoTimeout - Timer expired on the LCP echo
+ */
+static void
+LcpEchoTimeout (void *arg)
+{
+ if (lcp_echo_timer_running != 0) {
+ lcp_echo_timer_running = 0;
+ LcpEchoCheck ((fsm *) arg);
+ }
+}
+
+/*
+ * LcpEchoReply - LCP has received a reply to the echo
+ */
+static void
+lcp_received_echo_reply (fsm *f, int id, u_char *inp, int len)
+{
+ u32_t magic;
+
+ LWIP_UNUSED_ARG(id);
+
+ /* Check the magic number - don't count replies from ourselves. */
+ if (len < 4) {
+ LCPDEBUG(LOG_WARNING, ("lcp: received short Echo-Reply, length %d\n", len));
+ return;
+ }
+ GETLONG(magic, inp);
+ if (lcp_gotoptions[f->unit].neg_magicnumber && magic == lcp_gotoptions[f->unit].magicnumber) {
+ LCPDEBUG(LOG_WARNING, ("appear to have received our own echo-reply!\n"));
+ return;
+ }
+
+ /* Reset the number of outstanding echo frames */
+ lcp_echos_pending = 0;
+}
+
+/*
+ * LcpSendEchoRequest - Send an echo request frame to the peer
+ */
+static void
+LcpSendEchoRequest (fsm *f)
+{
+ u32_t lcp_magic;
+ u_char pkt[4], *pktp;
+
+ /*
+ * Detect the failure of the peer at this point.
+ */
+ if (lcp_echo_fails != 0) {
+ if (lcp_echos_pending >= lcp_echo_fails) {
+ LcpLinkFailure(f);
+ lcp_echos_pending = 0;
+ }
+ }
+
+ /*
+ * Make and send the echo request frame.
+ */
+ if (f->state == LS_OPENED) {
+ lcp_magic = lcp_gotoptions[f->unit].magicnumber;
+ pktp = pkt;
+ PUTLONG(lcp_magic, pktp);
+ fsm_sdata(f, ECHOREQ, (u_char)(lcp_echo_number++ & 0xFF), pkt, (int)(pktp - pkt));
+ ++lcp_echos_pending;
+ }
+}
+
+/*
+ * lcp_echo_lowerup - Start the timer for the LCP frame
+ */
+
+static void
+lcp_echo_lowerup (int unit)
+{
+ fsm *f = &lcp_fsm[unit];
+
+ /* Clear the parameters for generating echo frames */
+ lcp_echos_pending = 0;
+ lcp_echo_number = 0;
+ lcp_echo_timer_running = 0;
+
+ /* If a timeout interval is specified then start the timer */
+ if (lcp_echo_interval != 0) {
+ LcpEchoCheck (f);
+ }
+}
+
+/*
+ * lcp_echo_lowerdown - Stop the timer for the LCP frame
+ */
+
+static void
+lcp_echo_lowerdown (int unit)
+{
+ fsm *f = &lcp_fsm[unit];
+
+ if (lcp_echo_timer_running != 0) {
+ UNTIMEOUT (LcpEchoTimeout, f);
+ lcp_echo_timer_running = 0;
+ }
+}
+
+#endif /* PPP_SUPPORT */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/lcp.h b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/lcp.h
new file mode 100644
index 00000000..6c1955c2
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/lcp.h
@@ -0,0 +1,151 @@
+/*****************************************************************************
+* lcp.h - Network Link Control Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-11-05 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original derived from BSD codes.
+*****************************************************************************/
+/*
+ * lcp.h - Link Control Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: lcp.h $
+ */
+
+#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/src/VBox/Devices/Network/lwip-new/src/netif/ppp/magic.c b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/magic.c
new file mode 100644
index 00000000..3732a424
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/magic.c
@@ -0,0 +1,80 @@
+/*****************************************************************************
+* magic.c - Network Random Number Generator program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original based on BSD magic.c.
+*****************************************************************************/
+/*
+ * magic.c - PPP Magic Number routines.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT
+
+#include "ppp_impl.h"
+#include "randm.h"
+#include "magic.h"
+
+
+/*
+ * magicInit - Initialize the magic number generator.
+ *
+ * Since we use another random number generator that has its own
+ * initialization, we do nothing here.
+ */
+void magicInit()
+{
+ return;
+}
+
+/*
+ * magic - Returns the next magic number.
+ */
+u32_t magic()
+{
+ return avRandom();
+}
+
+#endif /* PPP_SUPPORT */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/magic.h b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/magic.h
new file mode 100644
index 00000000..203f19f8
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/magic.h
@@ -0,0 +1,63 @@
+/*****************************************************************************
+* magic.h - Network Random Number Generator header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original derived from BSD codes.
+*****************************************************************************/
+/*
+ * magic.h - PPP Magic Number definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * $Id: magic.h $
+ */
+
+#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/src/VBox/Devices/Network/lwip-new/src/netif/ppp/md5.c b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/md5.c
new file mode 100644
index 00000000..dc3cc751
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/md5.c
@@ -0,0 +1,320 @@
+/*
+ ***********************************************************************
+ ** md5.c -- the source code for MD5 routines **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 1/91 SRD,AJ,BSK,JT Reference C ver., 7/10 constant corr. **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ ***********************************************************************
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if CHAP_SUPPORT || MD5_SUPPORT
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "md5.h"
+
+#include <string.h>
+
+/*
+ ***********************************************************************
+ ** Message-digest routines: **
+ ** To form the message digest for a message M **
+ ** (1) Initialize a context buffer mdContext using MD5Init **
+ ** (2) Call MD5Update on mdContext and M **
+ ** (3) Call MD5Final on mdContext **
+ ** The message digest is now in mdContext->digest[0...15] **
+ ***********************************************************************
+ */
+
+/* forward declaration */
+static void Transform (u32_t *buf, u32_t *in);
+
+static unsigned char PADDING[64] = {
+ 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
+};
+
+/* F, G, H and I are basic MD5 functions */
+#define F(x, y, z) (((x) & (y)) | ((~x) & (z)))
+#define G(x, y, z) (((x) & (z)) | ((y) & (~z)))
+#define H(x, y, z) ((x) ^ (y) ^ (z))
+#define I(x, y, z) ((y) ^ ((x) | (~z)))
+
+/* ROTATE_LEFT rotates x left n bits */
+#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n))))
+
+/* FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4 */
+/* Rotation is separate from addition to prevent recomputation */
+#define FF(a, b, c, d, x, s, ac) \
+ {(a) += F ((b), (c), (d)) + (x) + (u32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define GG(a, b, c, d, x, s, ac) \
+ {(a) += G ((b), (c), (d)) + (x) + (u32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define HH(a, b, c, d, x, s, ac) \
+ {(a) += H ((b), (c), (d)) + (x) + (u32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+#define II(a, b, c, d, x, s, ac) \
+ {(a) += I ((b), (c), (d)) + (x) + (u32_t)(ac); \
+ (a) = ROTATE_LEFT ((a), (s)); \
+ (a) += (b); \
+ }
+
+#ifdef __STDC__
+#define UL(x) x##UL
+#else
+#ifdef WIN32
+#define UL(x) x##UL
+#else
+#define UL(x) x
+#endif
+#endif
+
+/* The routine MD5Init initializes the message-digest context
+ mdContext. All fields are set to zero.
+ */
+void
+MD5Init (MD5_CTX *mdContext)
+{
+ mdContext->i[0] = mdContext->i[1] = (u32_t)0;
+
+ /* Load magic initialization constants. */
+ mdContext->buf[0] = (u32_t)0x67452301UL;
+ mdContext->buf[1] = (u32_t)0xefcdab89UL;
+ mdContext->buf[2] = (u32_t)0x98badcfeUL;
+ mdContext->buf[3] = (u32_t)0x10325476UL;
+}
+
+/* The routine MD5Update updates the message-digest context to
+ account for the presence of each of the characters inBuf[0..inLen-1]
+ in the message whose digest is being computed.
+ */
+void
+MD5Update(MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen)
+{
+ u32_t in[16];
+ int mdi;
+ unsigned int i, ii;
+
+#if 0
+ PPPDEBUG(LOG_INFO, ("MD5Update: %u:%.*H\n", inLen, LWIP_MIN(inLen, 20) * 2, inBuf));
+ PPPDEBUG(LOG_INFO, ("MD5Update: %u:%s\n", inLen, inBuf));
+#endif
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* update number of bits */
+ if ((mdContext->i[0] + ((u32_t)inLen << 3)) < mdContext->i[0]) {
+ mdContext->i[1]++;
+ }
+ mdContext->i[0] += ((u32_t)inLen << 3);
+ mdContext->i[1] += ((u32_t)inLen >> 29);
+
+ while (inLen--) {
+ /* add new character to buffer, increment mdi */
+ mdContext->in[mdi++] = *inBuf++;
+
+ /* transform if necessary */
+ if (mdi == 0x40) {
+ for (i = 0, ii = 0; i < 16; i++, ii += 4) {
+ in[i] = (((u32_t)mdContext->in[ii+3]) << 24) |
+ (((u32_t)mdContext->in[ii+2]) << 16) |
+ (((u32_t)mdContext->in[ii+1]) << 8) |
+ ((u32_t)mdContext->in[ii]);
+ }
+ Transform (mdContext->buf, in);
+ mdi = 0;
+ }
+ }
+}
+
+/* The routine MD5Final terminates the message-digest computation and
+ ends with the desired message digest in mdContext->digest[0...15].
+ */
+void
+MD5Final (unsigned char hash[], MD5_CTX *mdContext)
+{
+ u32_t in[16];
+ int mdi;
+ unsigned int i, ii;
+ unsigned int padLen;
+
+ /* save number of bits */
+ in[14] = mdContext->i[0];
+ in[15] = mdContext->i[1];
+
+ /* compute number of bytes mod 64 */
+ mdi = (int)((mdContext->i[0] >> 3) & 0x3F);
+
+ /* pad out to 56 mod 64 */
+ padLen = (mdi < 56) ? (56 - mdi) : (120 - mdi);
+ MD5Update (mdContext, PADDING, padLen);
+
+ /* append length in bits and transform */
+ for (i = 0, ii = 0; i < 14; i++, ii += 4) {
+ in[i] = (((u32_t)mdContext->in[ii+3]) << 24) |
+ (((u32_t)mdContext->in[ii+2]) << 16) |
+ (((u32_t)mdContext->in[ii+1]) << 8) |
+ ((u32_t)mdContext->in[ii]);
+ }
+ Transform (mdContext->buf, in);
+
+ /* store buffer in digest */
+ for (i = 0, ii = 0; i < 4; i++, ii += 4) {
+ mdContext->digest[ii] = (unsigned char)(mdContext->buf[i] & 0xFF);
+ mdContext->digest[ii+1] =
+ (unsigned char)((mdContext->buf[i] >> 8) & 0xFF);
+ mdContext->digest[ii+2] =
+ (unsigned char)((mdContext->buf[i] >> 16) & 0xFF);
+ mdContext->digest[ii+3] =
+ (unsigned char)((mdContext->buf[i] >> 24) & 0xFF);
+ }
+ SMEMCPY(hash, mdContext->digest, 16);
+}
+
+/* Basic MD5 step. Transforms buf based on in.
+ */
+static void
+Transform (u32_t *buf, u32_t *in)
+{
+ u32_t a = buf[0], b = buf[1], c = buf[2], d = buf[3];
+
+ /* Round 1 */
+#define S11 7
+#define S12 12
+#define S13 17
+#define S14 22
+ FF ( a, b, c, d, in[ 0], S11, UL(3614090360)); /* 1 */
+ FF ( d, a, b, c, in[ 1], S12, UL(3905402710)); /* 2 */
+ FF ( c, d, a, b, in[ 2], S13, UL( 606105819)); /* 3 */
+ FF ( b, c, d, a, in[ 3], S14, UL(3250441966)); /* 4 */
+ FF ( a, b, c, d, in[ 4], S11, UL(4118548399)); /* 5 */
+ FF ( d, a, b, c, in[ 5], S12, UL(1200080426)); /* 6 */
+ FF ( c, d, a, b, in[ 6], S13, UL(2821735955)); /* 7 */
+ FF ( b, c, d, a, in[ 7], S14, UL(4249261313)); /* 8 */
+ FF ( a, b, c, d, in[ 8], S11, UL(1770035416)); /* 9 */
+ FF ( d, a, b, c, in[ 9], S12, UL(2336552879)); /* 10 */
+ FF ( c, d, a, b, in[10], S13, UL(4294925233)); /* 11 */
+ FF ( b, c, d, a, in[11], S14, UL(2304563134)); /* 12 */
+ FF ( a, b, c, d, in[12], S11, UL(1804603682)); /* 13 */
+ FF ( d, a, b, c, in[13], S12, UL(4254626195)); /* 14 */
+ FF ( c, d, a, b, in[14], S13, UL(2792965006)); /* 15 */
+ FF ( b, c, d, a, in[15], S14, UL(1236535329)); /* 16 */
+
+ /* Round 2 */
+#define S21 5
+#define S22 9
+#define S23 14
+#define S24 20
+ GG ( a, b, c, d, in[ 1], S21, UL(4129170786)); /* 17 */
+ GG ( d, a, b, c, in[ 6], S22, UL(3225465664)); /* 18 */
+ GG ( c, d, a, b, in[11], S23, UL( 643717713)); /* 19 */
+ GG ( b, c, d, a, in[ 0], S24, UL(3921069994)); /* 20 */
+ GG ( a, b, c, d, in[ 5], S21, UL(3593408605)); /* 21 */
+ GG ( d, a, b, c, in[10], S22, UL( 38016083)); /* 22 */
+ GG ( c, d, a, b, in[15], S23, UL(3634488961)); /* 23 */
+ GG ( b, c, d, a, in[ 4], S24, UL(3889429448)); /* 24 */
+ GG ( a, b, c, d, in[ 9], S21, UL( 568446438)); /* 25 */
+ GG ( d, a, b, c, in[14], S22, UL(3275163606)); /* 26 */
+ GG ( c, d, a, b, in[ 3], S23, UL(4107603335)); /* 27 */
+ GG ( b, c, d, a, in[ 8], S24, UL(1163531501)); /* 28 */
+ GG ( a, b, c, d, in[13], S21, UL(2850285829)); /* 29 */
+ GG ( d, a, b, c, in[ 2], S22, UL(4243563512)); /* 30 */
+ GG ( c, d, a, b, in[ 7], S23, UL(1735328473)); /* 31 */
+ GG ( b, c, d, a, in[12], S24, UL(2368359562)); /* 32 */
+
+ /* Round 3 */
+#define S31 4
+#define S32 11
+#define S33 16
+#define S34 23
+ HH ( a, b, c, d, in[ 5], S31, UL(4294588738)); /* 33 */
+ HH ( d, a, b, c, in[ 8], S32, UL(2272392833)); /* 34 */
+ HH ( c, d, a, b, in[11], S33, UL(1839030562)); /* 35 */
+ HH ( b, c, d, a, in[14], S34, UL(4259657740)); /* 36 */
+ HH ( a, b, c, d, in[ 1], S31, UL(2763975236)); /* 37 */
+ HH ( d, a, b, c, in[ 4], S32, UL(1272893353)); /* 38 */
+ HH ( c, d, a, b, in[ 7], S33, UL(4139469664)); /* 39 */
+ HH ( b, c, d, a, in[10], S34, UL(3200236656)); /* 40 */
+ HH ( a, b, c, d, in[13], S31, UL( 681279174)); /* 41 */
+ HH ( d, a, b, c, in[ 0], S32, UL(3936430074)); /* 42 */
+ HH ( c, d, a, b, in[ 3], S33, UL(3572445317)); /* 43 */
+ HH ( b, c, d, a, in[ 6], S34, UL( 76029189)); /* 44 */
+ HH ( a, b, c, d, in[ 9], S31, UL(3654602809)); /* 45 */
+ HH ( d, a, b, c, in[12], S32, UL(3873151461)); /* 46 */
+ HH ( c, d, a, b, in[15], S33, UL( 530742520)); /* 47 */
+ HH ( b, c, d, a, in[ 2], S34, UL(3299628645)); /* 48 */
+
+ /* Round 4 */
+#define S41 6
+#define S42 10
+#define S43 15
+#define S44 21
+ II ( a, b, c, d, in[ 0], S41, UL(4096336452)); /* 49 */
+ II ( d, a, b, c, in[ 7], S42, UL(1126891415)); /* 50 */
+ II ( c, d, a, b, in[14], S43, UL(2878612391)); /* 51 */
+ II ( b, c, d, a, in[ 5], S44, UL(4237533241)); /* 52 */
+ II ( a, b, c, d, in[12], S41, UL(1700485571)); /* 53 */
+ II ( d, a, b, c, in[ 3], S42, UL(2399980690)); /* 54 */
+ II ( c, d, a, b, in[10], S43, UL(4293915773)); /* 55 */
+ II ( b, c, d, a, in[ 1], S44, UL(2240044497)); /* 56 */
+ II ( a, b, c, d, in[ 8], S41, UL(1873313359)); /* 57 */
+ II ( d, a, b, c, in[15], S42, UL(4264355552)); /* 58 */
+ II ( c, d, a, b, in[ 6], S43, UL(2734768916)); /* 59 */
+ II ( b, c, d, a, in[13], S44, UL(1309151649)); /* 60 */
+ II ( a, b, c, d, in[ 4], S41, UL(4149444226)); /* 61 */
+ II ( d, a, b, c, in[11], S42, UL(3174756917)); /* 62 */
+ II ( c, d, a, b, in[ 2], S43, UL( 718787259)); /* 63 */
+ II ( b, c, d, a, in[ 9], S44, UL(3951481745)); /* 64 */
+
+ buf[0] += a;
+ buf[1] += b;
+ buf[2] += c;
+ buf[3] += d;
+}
+
+#endif /* CHAP_SUPPORT || MD5_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/md5.h b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/md5.h
new file mode 100644
index 00000000..e129533f
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/md5.h
@@ -0,0 +1,55 @@
+/*
+ ***********************************************************************
+ ** md5.h -- header file for implementation of MD5 **
+ ** RSA Data Security, Inc. MD5 Message-Digest Algorithm **
+ ** Created: 2/17/90 RLR **
+ ** Revised: 12/27/90 SRD,AJ,BSK,JT Reference C version **
+ ** Revised (for MD5): RLR 4/27/91 **
+ ** -- G modified to have y&~z instead of y&z **
+ ** -- FF, GG, HH modified to add in last register done **
+ ** -- Access pattern: round 2 works mod 5, round 3 works mod 3 **
+ ** -- distinct additive constant for each step **
+ ** -- round 4 added, working mod 7 **
+ ***********************************************************************
+ */
+
+/*
+ ***********************************************************************
+ ** Copyright (C) 1990, RSA Data Security, Inc. All rights reserved. **
+ ** **
+ ** License to copy and use this software is granted provided that **
+ ** it is identified as the "RSA Data Security, Inc. MD5 Message- **
+ ** Digest Algorithm" in all material mentioning or referencing this **
+ ** software or this function. **
+ ** **
+ ** License is also granted to make and use derivative works **
+ ** provided that such works are identified as "derived from the RSA **
+ ** Data Security, Inc. MD5 Message-Digest Algorithm" in all **
+ ** material mentioning or referencing the derived work. **
+ ** **
+ ** RSA Data Security, Inc. makes no representations concerning **
+ ** either the merchantability of this software or the suitability **
+ ** of this software for any particular purpose. It is provided "as **
+ ** is" without express or implied warranty of any kind. **
+ ** **
+ ** These notices must be retained in any copies of any part of this **
+ ** documentation and/or software. **
+ ***********************************************************************
+ */
+
+#ifndef MD5_H
+#define MD5_H
+
+/* Data structure for MD5 (Message-Digest) computation */
+typedef struct {
+ u32_t i[2]; /* number of _bits_ handled mod 2^64 */
+ u32_t buf[4]; /* scratch buffer */
+ unsigned char in[64]; /* input buffer */
+ unsigned char digest[16]; /* actual digest after MD5Final call */
+} MD5_CTX;
+
+void MD5Init ( MD5_CTX *mdContext);
+void MD5Update( MD5_CTX *mdContext, unsigned char *inBuf, unsigned int inLen);
+void MD5Final ( unsigned char hash[], MD5_CTX *mdContext);
+
+#endif /* MD5_H */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/pap.c b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/pap.c
new file mode 100644
index 00000000..5fb9f886
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/pap.c
@@ -0,0 +1,628 @@
+/*****************************************************************************
+* pap.c - Network Password Authentication Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-12 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original.
+*****************************************************************************/
+/*
+ * upap.c - User/Password Authentication Protocol.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "auth.h"
+#include "pap.h"
+
+#include <string.h>
+
+#if 0 /* UNUSED */
+static bool hide_password = 1;
+
+/*
+ * Command-line options.
+ */
+static option_t pap_option_list[] = {
+ { "hide-password", o_bool, &hide_password,
+ "Don't output passwords to log", 1 },
+ { "show-password", o_bool, &hide_password,
+ "Show password string in debug log messages", 0 },
+ { "pap-restart", o_int, &upap[0].us_timeouttime,
+ "Set retransmit timeout for PAP" },
+ { "pap-max-authreq", o_int, &upap[0].us_maxtransmits,
+ "Set max number of transmissions for auth-reqs" },
+ { "pap-timeout", o_int, &upap[0].us_reqtimeout,
+ "Set time limit for peer PAP authentication" },
+ { NULL }
+};
+#endif
+
+/*
+ * Protocol entry points.
+ */
+static void upap_init (int);
+static void upap_lowerup (int);
+static void upap_lowerdown (int);
+static void upap_input (int, u_char *, int);
+static void upap_protrej (int);
+#if PPP_ADDITIONAL_CALLBACKS
+static int upap_printpkt (u_char *, int, void (*)(void *, char *, ...), void *);
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+
+struct protent pap_protent = {
+ PPP_PAP,
+ upap_init,
+ upap_input,
+ upap_protrej,
+ upap_lowerup,
+ upap_lowerdown,
+ NULL,
+ NULL,
+#if PPP_ADDITIONAL_CALLBACKS
+ upap_printpkt,
+ NULL,
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+ 1,
+ "PAP",
+#if PPP_ADDITIONAL_CALLBACKS
+ NULL,
+ NULL,
+ NULL
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+};
+
+upap_state upap[NUM_PPP]; /* UPAP state; one for each unit */
+
+static void upap_timeout (void *);
+static void upap_reqtimeout(void *);
+static void upap_rauthreq (upap_state *, u_char *, u_char, int);
+static void upap_rauthack (upap_state *, u_char *, int, int);
+static void upap_rauthnak (upap_state *, u_char *, int, int);
+static void upap_sauthreq (upap_state *);
+static void upap_sresp (upap_state *, u_char, u_char, char *, int);
+
+
+/*
+ * upap_init - Initialize a UPAP unit.
+ */
+static void
+upap_init(int unit)
+{
+ upap_state *u = &upap[unit];
+
+ UPAPDEBUG(LOG_INFO, ("upap_init: %d\n", unit));
+ u->us_unit = unit;
+ u->us_user = NULL;
+ u->us_userlen = 0;
+ u->us_passwd = NULL;
+ u->us_passwdlen = 0;
+ u->us_clientstate = UPAPCS_INITIAL;
+ u->us_serverstate = UPAPSS_INITIAL;
+ u->us_id = 0;
+ u->us_timeouttime = UPAP_DEFTIMEOUT;
+ u->us_maxtransmits = 10;
+ u->us_reqtimeout = UPAP_DEFREQTIME;
+}
+
+/*
+ * upap_authwithpeer - Authenticate us with our peer (start client).
+ *
+ * Set new state and send authenticate's.
+ */
+void
+upap_authwithpeer(int unit, char *user, char *password)
+{
+ upap_state *u = &upap[unit];
+
+ UPAPDEBUG(LOG_INFO, ("upap_authwithpeer: %d user=%s password=%s s=%d\n",
+ unit, user, password, u->us_clientstate));
+
+ /* Save the username and password we're given */
+ u->us_user = user;
+ u->us_userlen = (int)strlen(user);
+ u->us_passwd = password;
+ u->us_passwdlen = (int)strlen(password);
+
+ u->us_transmits = 0;
+
+ /* Lower layer up yet? */
+ if (u->us_clientstate == UPAPCS_INITIAL ||
+ u->us_clientstate == UPAPCS_PENDING) {
+ u->us_clientstate = UPAPCS_PENDING;
+ return;
+ }
+
+ upap_sauthreq(u); /* Start protocol */
+}
+
+
+/*
+ * upap_authpeer - Authenticate our peer (start server).
+ *
+ * Set new state.
+ */
+void
+upap_authpeer(int unit)
+{
+ upap_state *u = &upap[unit];
+
+ /* Lower layer up yet? */
+ if (u->us_serverstate == UPAPSS_INITIAL ||
+ u->us_serverstate == UPAPSS_PENDING) {
+ u->us_serverstate = UPAPSS_PENDING;
+ return;
+ }
+
+ u->us_serverstate = UPAPSS_LISTEN;
+ if (u->us_reqtimeout > 0) {
+ TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+ }
+}
+
+/*
+ * upap_timeout - Retransmission timer for sending auth-reqs expired.
+ */
+static void
+upap_timeout(void *arg)
+{
+ upap_state *u = (upap_state *) arg;
+
+ UPAPDEBUG(LOG_INFO, ("upap_timeout: %d timeout %d expired s=%d\n",
+ u->us_unit, u->us_timeouttime, u->us_clientstate));
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ) {
+ UPAPDEBUG(LOG_INFO, ("upap_timeout: not in AUTHREQ state!\n"));
+ return;
+ }
+
+ if (u->us_transmits >= u->us_maxtransmits) {
+ /* give up in disgust */
+ UPAPDEBUG(LOG_ERR, ("No response to PAP authenticate-requests\n"));
+ u->us_clientstate = UPAPCS_BADAUTH;
+ auth_withpeer_fail(u->us_unit, PPP_PAP);
+ return;
+ }
+
+ upap_sauthreq(u); /* Send Authenticate-Request and set upap timeout*/
+}
+
+
+/*
+ * upap_reqtimeout - Give up waiting for the peer to send an auth-req.
+ */
+static void
+upap_reqtimeout(void *arg)
+{
+ upap_state *u = (upap_state *) arg;
+
+ if (u->us_serverstate != UPAPSS_LISTEN) {
+ return; /* huh?? */
+ }
+
+ auth_peer_fail(u->us_unit, PPP_PAP);
+ u->us_serverstate = UPAPSS_BADAUTH;
+}
+
+
+/*
+ * upap_lowerup - The lower layer is up.
+ *
+ * Start authenticating if pending.
+ */
+static void
+upap_lowerup(int unit)
+{
+ upap_state *u = &upap[unit];
+
+ UPAPDEBUG(LOG_INFO, ("upap_lowerup: init %d clientstate s=%d\n", unit, u->us_clientstate));
+
+ if (u->us_clientstate == UPAPCS_INITIAL) {
+ u->us_clientstate = UPAPCS_CLOSED;
+ } else if (u->us_clientstate == UPAPCS_PENDING) {
+ upap_sauthreq(u); /* send an auth-request */
+ /* now client state is UPAPCS__AUTHREQ */
+ }
+
+ if (u->us_serverstate == UPAPSS_INITIAL) {
+ u->us_serverstate = UPAPSS_CLOSED;
+ } else if (u->us_serverstate == UPAPSS_PENDING) {
+ u->us_serverstate = UPAPSS_LISTEN;
+ if (u->us_reqtimeout > 0) {
+ TIMEOUT(upap_reqtimeout, u, u->us_reqtimeout);
+ }
+ }
+}
+
+
+/*
+ * upap_lowerdown - The lower layer is down.
+ *
+ * Cancel all timeouts.
+ */
+static void
+upap_lowerdown(int unit)
+{
+ upap_state *u = &upap[unit];
+
+ UPAPDEBUG(LOG_INFO, ("upap_lowerdown: %d s=%d\n", unit, u->us_clientstate));
+
+ if (u->us_clientstate == UPAPCS_AUTHREQ) { /* Timeout pending? */
+ UNTIMEOUT(upap_timeout, u); /* Cancel timeout */
+ }
+ if (u->us_serverstate == UPAPSS_LISTEN && u->us_reqtimeout > 0) {
+ UNTIMEOUT(upap_reqtimeout, u);
+ }
+
+ u->us_clientstate = UPAPCS_INITIAL;
+ u->us_serverstate = UPAPSS_INITIAL;
+}
+
+
+/*
+ * upap_protrej - Peer doesn't speak this protocol.
+ *
+ * This shouldn't happen. In any case, pretend lower layer went down.
+ */
+static void
+upap_protrej(int unit)
+{
+ upap_state *u = &upap[unit];
+
+ if (u->us_clientstate == UPAPCS_AUTHREQ) {
+ UPAPDEBUG(LOG_ERR, ("PAP authentication failed due to protocol-reject\n"));
+ auth_withpeer_fail(unit, PPP_PAP);
+ }
+ if (u->us_serverstate == UPAPSS_LISTEN) {
+ UPAPDEBUG(LOG_ERR, ("PAP authentication of peer failed (protocol-reject)\n"));
+ auth_peer_fail(unit, PPP_PAP);
+ }
+ upap_lowerdown(unit);
+}
+
+
+/*
+ * upap_input - Input UPAP packet.
+ */
+static void
+upap_input(int unit, u_char *inpacket, int l)
+{
+ upap_state *u = &upap[unit];
+ u_char *inp;
+ u_char code, id;
+ int len;
+
+ /*
+ * Parse header (code, id and length).
+ * If packet too short, drop it.
+ */
+ inp = inpacket;
+ if (l < (int)UPAP_HEADERLEN) {
+ UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short header.\n"));
+ return;
+ }
+ GETCHAR(code, inp);
+ GETCHAR(id, inp);
+ GETSHORT(len, inp);
+ if (len < (int)UPAP_HEADERLEN) {
+ UPAPDEBUG(LOG_INFO, ("pap_input: rcvd illegal length.\n"));
+ return;
+ }
+ if (len > l) {
+ UPAPDEBUG(LOG_INFO, ("pap_input: rcvd short packet.\n"));
+ return;
+ }
+ len -= UPAP_HEADERLEN;
+
+ /*
+ * Action depends on code.
+ */
+ switch (code) {
+ case UPAP_AUTHREQ:
+ upap_rauthreq(u, inp, id, len);
+ break;
+
+ case UPAP_AUTHACK:
+ upap_rauthack(u, inp, id, len);
+ break;
+
+ case UPAP_AUTHNAK:
+ upap_rauthnak(u, inp, id, len);
+ break;
+
+ default: /* XXX Need code reject */
+ UPAPDEBUG(LOG_INFO, ("pap_input: UNHANDLED default: code: %d, id: %d, len: %d.\n", code, id, len));
+ break;
+ }
+}
+
+
+/*
+ * upap_rauth - Receive Authenticate.
+ */
+static void
+upap_rauthreq(upap_state *u, u_char *inp, u_char id, int len)
+{
+ u_char ruserlen, rpasswdlen;
+ char *ruser, *rpasswd;
+ u_char retcode;
+ char *msg;
+ int msglen;
+
+ UPAPDEBUG(LOG_INFO, ("pap_rauth: Rcvd id %d.\n", id));
+
+ if (u->us_serverstate < UPAPSS_LISTEN) {
+ return;
+ }
+
+ /*
+ * If we receive a duplicate authenticate-request, we are
+ * supposed to return the same status as for the first request.
+ */
+ if (u->us_serverstate == UPAPSS_OPEN) {
+ upap_sresp(u, UPAP_AUTHACK, id, "", 0); /* return auth-ack */
+ return;
+ }
+ if (u->us_serverstate == UPAPSS_BADAUTH) {
+ upap_sresp(u, UPAP_AUTHNAK, id, "", 0); /* return auth-nak */
+ return;
+ }
+
+ /*
+ * Parse user/passwd.
+ */
+ if (len < (int)sizeof (u_char)) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n"));
+ return;
+ }
+ GETCHAR(ruserlen, inp);
+ len -= sizeof (u_char) + ruserlen + sizeof (u_char);
+ if (len < 0) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n"));
+ return;
+ }
+ ruser = (char *) inp;
+ INCPTR(ruserlen, inp);
+ GETCHAR(rpasswdlen, inp);
+ if (len < rpasswdlen) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauth: rcvd short packet.\n"));
+ return;
+ }
+ rpasswd = (char *) inp;
+
+ /*
+ * Check the username and password given.
+ */
+ retcode = check_passwd(u->us_unit, ruser, ruserlen, rpasswd, rpasswdlen, &msg, &msglen);
+ /* lwip: currently retcode is always UPAP_AUTHACK */
+ BZERO(rpasswd, rpasswdlen);
+
+ upap_sresp(u, retcode, id, msg, msglen);
+
+ if (retcode == UPAP_AUTHACK) {
+ u->us_serverstate = UPAPSS_OPEN;
+ auth_peer_success(u->us_unit, PPP_PAP, ruser, ruserlen);
+ } else {
+ u->us_serverstate = UPAPSS_BADAUTH;
+ auth_peer_fail(u->us_unit, PPP_PAP);
+ }
+
+ if (u->us_reqtimeout > 0) {
+ UNTIMEOUT(upap_reqtimeout, u);
+ }
+}
+
+
+/*
+ * upap_rauthack - Receive Authenticate-Ack.
+ */
+static void
+upap_rauthack(upap_state *u, u_char *inp, int id, int len)
+{
+ u_char msglen;
+ char *msg;
+
+ LWIP_UNUSED_ARG(id);
+
+ UPAPDEBUG(LOG_INFO, ("pap_rauthack: Rcvd id %d s=%d\n", id, u->us_clientstate));
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */
+ UPAPDEBUG(LOG_INFO, ("pap_rauthack: us_clientstate != UPAPCS_AUTHREQ\n"));
+ return;
+ }
+
+ /*
+ * Parse message.
+ */
+ if (len < (int)sizeof (u_char)) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauthack: ignoring missing msg-length.\n"));
+ } else {
+ GETCHAR(msglen, inp);
+ if (msglen > 0) {
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauthack: rcvd short packet.\n"));
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+ }
+ }
+ UNTIMEOUT(upap_timeout, u); /* Cancel timeout */
+ u->us_clientstate = UPAPCS_OPEN;
+
+ auth_withpeer_success(u->us_unit, PPP_PAP);
+}
+
+
+/*
+ * upap_rauthnak - Receive Authenticate-Nak.
+ */
+static void
+upap_rauthnak(upap_state *u, u_char *inp, int id, int len)
+{
+ u_char msglen;
+ char *msg;
+
+ LWIP_UNUSED_ARG(id);
+
+ UPAPDEBUG(LOG_INFO, ("pap_rauthnak: Rcvd id %d s=%d\n", id, u->us_clientstate));
+
+ if (u->us_clientstate != UPAPCS_AUTHREQ) { /* XXX */
+ return;
+ }
+
+ /*
+ * Parse message.
+ */
+ if (len < sizeof (u_char)) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauthnak: ignoring missing msg-length.\n"));
+ } else {
+ GETCHAR(msglen, inp);
+ if(msglen > 0) {
+ len -= sizeof (u_char);
+ if (len < msglen) {
+ UPAPDEBUG(LOG_INFO, ("pap_rauthnak: rcvd short packet.\n"));
+ return;
+ }
+ msg = (char *) inp;
+ PRINTMSG(msg, msglen);
+ }
+ }
+
+ u->us_clientstate = UPAPCS_BADAUTH;
+
+ UPAPDEBUG(LOG_ERR, ("PAP authentication failed\n"));
+ auth_withpeer_fail(u->us_unit, PPP_PAP);
+}
+
+
+/*
+ * upap_sauthreq - Send an Authenticate-Request.
+ */
+static void
+upap_sauthreq(upap_state *u)
+{
+ u_char *outp;
+ int outlen;
+
+ outlen = UPAP_HEADERLEN + 2 * sizeof (u_char)
+ + u->us_userlen + u->us_passwdlen;
+ outp = outpacket_buf[u->us_unit];
+
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(UPAP_AUTHREQ, outp);
+ PUTCHAR(++u->us_id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(u->us_userlen, outp);
+ BCOPY(u->us_user, outp, u->us_userlen);
+ INCPTR(u->us_userlen, outp);
+ PUTCHAR(u->us_passwdlen, outp);
+ BCOPY(u->us_passwd, outp, u->us_passwdlen);
+
+ pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN);
+
+ UPAPDEBUG(LOG_INFO, ("pap_sauth: Sent id %d\n", u->us_id));
+
+ TIMEOUT(upap_timeout, u, u->us_timeouttime);
+ ++u->us_transmits;
+ u->us_clientstate = UPAPCS_AUTHREQ;
+}
+
+
+/*
+ * upap_sresp - Send a response (ack or nak).
+ */
+static void
+upap_sresp(upap_state *u, u_char code, u_char id, char *msg, int msglen)
+{
+ u_char *outp;
+ int outlen;
+
+ outlen = UPAP_HEADERLEN + sizeof (u_char) + msglen;
+ outp = outpacket_buf[u->us_unit];
+ MAKEHEADER(outp, PPP_PAP);
+
+ PUTCHAR(code, outp);
+ PUTCHAR(id, outp);
+ PUTSHORT(outlen, outp);
+ PUTCHAR(msglen, outp);
+ BCOPY(msg, outp, msglen);
+ pppWrite(u->us_unit, outpacket_buf[u->us_unit], outlen + PPP_HDRLEN);
+
+ UPAPDEBUG(LOG_INFO, ("pap_sresp: Sent code %d, id %d s=%d\n", code, id, u->us_clientstate));
+}
+
+#if PPP_ADDITIONAL_CALLBACKS
+static char *upap_codenames[] = {
+ "AuthReq", "AuthAck", "AuthNak"
+};
+
+/*
+ * upap_printpkt - print the contents of a PAP packet.
+ */
+static int upap_printpkt(
+ u_char *p,
+ int plen,
+ void (*printer) (void *, char *, ...),
+ void *arg
+)
+{
+ LWIP_UNUSED_ARG(p);
+ LWIP_UNUSED_ARG(plen);
+ LWIP_UNUSED_ARG(printer);
+ LWIP_UNUSED_ARG(arg);
+ return 0;
+}
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+
+#endif /* PAP_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/pap.h b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/pap.h
new file mode 100644
index 00000000..c99a2040
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/pap.h
@@ -0,0 +1,118 @@
+/*****************************************************************************
+* pap.h - PPP Password Authentication Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-12-04 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original derived from BSD codes.
+*****************************************************************************/
+/*
+ * upap.h - User/Password Authentication Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#ifndef PAP_H
+#define PAP_H
+
+#if PAP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+/*
+ * Packet header = Code, id, length.
+ */
+#define UPAP_HEADERLEN (sizeof (u_char) + sizeof (u_char) + sizeof (u_short))
+
+
+/*
+ * UPAP codes.
+ */
+#define UPAP_AUTHREQ 1 /* Authenticate-Request */
+#define UPAP_AUTHACK 2 /* Authenticate-Ack */
+#define UPAP_AUTHNAK 3 /* Authenticate-Nak */
+
+/*
+ * Each interface is described by upap structure.
+ */
+typedef struct upap_state {
+ int us_unit; /* Interface unit number */
+ const char *us_user; /* User */
+ int us_userlen; /* User length */
+ const char *us_passwd; /* Password */
+ int us_passwdlen; /* Password length */
+ int us_clientstate; /* Client state */
+ int us_serverstate; /* Server state */
+ u_char us_id; /* Current id */
+ int us_timeouttime; /* Timeout (seconds) for auth-req retrans. */
+ int us_transmits; /* Number of auth-reqs sent */
+ int us_maxtransmits; /* Maximum number of auth-reqs to send */
+ int us_reqtimeout; /* Time to wait for auth-req from peer */
+} upap_state;
+
+/*
+ * Client states.
+ */
+#define UPAPCS_INITIAL 0 /* Connection down */
+#define UPAPCS_CLOSED 1 /* Connection up, haven't requested auth */
+#define UPAPCS_PENDING 2 /* Connection down, have requested auth */
+#define UPAPCS_AUTHREQ 3 /* We've sent an Authenticate-Request */
+#define UPAPCS_OPEN 4 /* We've received an Ack */
+#define UPAPCS_BADAUTH 5 /* We've received a Nak */
+
+/*
+ * Server states.
+ */
+#define UPAPSS_INITIAL 0 /* Connection down */
+#define UPAPSS_CLOSED 1 /* Connection up, haven't requested auth */
+#define UPAPSS_PENDING 2 /* Connection down, have requested auth */
+#define UPAPSS_LISTEN 3 /* Listening for an Authenticate */
+#define UPAPSS_OPEN 4 /* We've sent an Ack */
+#define UPAPSS_BADAUTH 5 /* We've sent a Nak */
+
+
+extern upap_state upap[];
+
+void upap_authwithpeer (int, char *, char *);
+void upap_authpeer (int);
+
+extern struct protent pap_protent;
+
+#endif /* PAP_SUPPORT */
+
+#endif /* PAP_H */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ppp.c b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ppp.c
new file mode 100644
index 00000000..2a346575
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ppp.c
@@ -0,0 +1,2052 @@
+/*****************************************************************************
+* ppp.c - Network Point to Point Protocol program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-11-05 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original.
+*****************************************************************************/
+
+/*
+ * ppp_defs.h - PPP definitions.
+ *
+ * if_pppvar.h - private structures and declarations for PPP.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+/*
+ * if_ppp.h - Point-to-Point Protocol definitions.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp_impl.h"
+#include "lwip/ip.h" /* for ip_input() */
+
+#include "pppdebug.h"
+
+#include "randm.h"
+#include "fsm.h"
+#if PAP_SUPPORT
+#include "pap.h"
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+#include "chap.h"
+#endif /* CHAP_SUPPORT */
+#include "ipcp.h"
+#include "lcp.h"
+#include "magic.h"
+#include "auth.h"
+#if VJ_SUPPORT
+#include "vj.h"
+#endif /* VJ_SUPPORT */
+#if PPPOE_SUPPORT
+#include "netif/ppp_oe.h"
+#endif /* PPPOE_SUPPORT */
+
+#include "lwip/tcpip.h"
+#include "lwip/api.h"
+#include "lwip/snmp.h"
+
+#include <string.h>
+
+/*************************/
+/*** LOCAL DEFINITIONS ***/
+/*************************/
+
+/** PPP_INPROC_MULTITHREADED==1 call pppInput using tcpip_callback().
+ * Set this to 0 if pppInProc is called inside tcpip_thread or with NO_SYS==1.
+ * Default is 1 for NO_SYS==0 (multithreaded) and 0 for NO_SYS==1 (single-threaded).
+ */
+#ifndef PPP_INPROC_MULTITHREADED
+#define PPP_INPROC_MULTITHREADED (NO_SYS==0)
+#endif
+
+/** PPP_INPROC_OWNTHREAD==1: start a dedicated RX thread per PPP session.
+ * Default is 0: call pppos_input() for received raw characters, charcater
+ * reception is up to the port */
+#ifndef PPP_INPROC_OWNTHREAD
+#define PPP_INPROC_OWNTHREAD PPP_INPROC_MULTITHREADED
+#endif
+
+#if PPP_INPROC_OWNTHREAD && !PPP_INPROC_MULTITHREADED
+ #error "PPP_INPROC_OWNTHREAD needs PPP_INPROC_MULTITHREADED==1"
+#endif
+
+/*
+ * The basic PPP frame.
+ */
+#define PPP_ADDRESS(p) (((u_char *)(p))[0])
+#define PPP_CONTROL(p) (((u_char *)(p))[1])
+#define PPP_PROTOCOL(p) ((((u_char *)(p))[2] << 8) + ((u_char *)(p))[3])
+
+/* PPP packet parser states. Current state indicates operation yet to be
+ * completed. */
+typedef enum {
+ PDIDLE = 0, /* Idle state - waiting. */
+ PDSTART, /* Process start flag. */
+ PDADDRESS, /* Process address field. */
+ PDCONTROL, /* Process control field. */
+ PDPROTOCOL1, /* Process protocol field 1. */
+ PDPROTOCOL2, /* Process protocol field 2. */
+ PDDATA /* Process data byte. */
+} PPPDevStates;
+
+#define ESCAPE_P(accm, c) ((accm)[(c) >> 3] & pppACCMMask[c & 0x07])
+
+/************************/
+/*** LOCAL DATA TYPES ***/
+/************************/
+
+/** RX buffer size: this may be configured smaller! */
+#ifndef PPPOS_RX_BUFSIZE
+#define PPPOS_RX_BUFSIZE (PPP_MRU + PPP_HDRLEN)
+#endif
+
+typedef struct PPPControlRx_s {
+ /** unit number / ppp descriptor */
+ int pd;
+ /** the rx file descriptor */
+ sio_fd_t fd;
+ /** receive buffer - encoded data is stored here */
+#if PPP_INPROC_OWNTHREAD
+ u_char rxbuf[PPPOS_RX_BUFSIZE];
+#endif /* PPP_INPROC_OWNTHREAD */
+
+ /* The input packet. */
+ struct pbuf *inHead, *inTail;
+
+#if PPPOS_SUPPORT
+ u16_t inProtocol; /* The input protocol code. */
+ u16_t inFCS; /* Input Frame Check Sequence value. */
+#endif /* PPPOS_SUPPORT */
+ PPPDevStates inState; /* The input process state. */
+ char inEscaped; /* Escape next character. */
+ ext_accm inACCM; /* Async-Ctl-Char-Map for input. */
+} PPPControlRx;
+
+/*
+ * PPP interface control block.
+ */
+typedef struct PPPControl_s {
+ PPPControlRx rx;
+ char openFlag; /* True when in use. */
+#if PPPOE_SUPPORT
+ struct netif *ethif;
+ struct pppoe_softc *pppoe_sc;
+#endif /* PPPOE_SUPPORT */
+ int if_up; /* True when the interface is up. */
+ int errCode; /* Code indicating why interface is down. */
+#if PPPOS_SUPPORT
+ sio_fd_t fd; /* File device ID of port. */
+#endif /* PPPOS_SUPPORT */
+ u16_t mtu; /* Peer's mru */
+ int pcomp; /* Does peer accept protocol compression? */
+ int accomp; /* Does peer accept addr/ctl compression? */
+ u_long lastXMit; /* Time of last transmission. */
+ ext_accm outACCM; /* Async-Ctl-Char-Map for output. */
+#if PPPOS_SUPPORT && VJ_SUPPORT
+ int vjEnabled; /* Flag indicating VJ compression enabled. */
+ struct vjcompress vjComp; /* Van Jacobson compression header. */
+#endif /* PPPOS_SUPPORT && VJ_SUPPORT */
+
+ struct netif netif;
+
+ struct ppp_addrs addrs;
+
+ void (*linkStatusCB)(void *ctx, int errCode, void *arg);
+ void *linkStatusCtx;
+
+} PPPControl;
+
+
+/*
+ * Ioctl definitions.
+ */
+
+struct npioctl {
+ int protocol; /* PPP procotol, e.g. PPP_IP */
+ enum NPmode mode;
+};
+
+
+
+/***********************************/
+/*** LOCAL FUNCTION DECLARATIONS ***/
+/***********************************/
+#if PPPOS_SUPPORT
+#if PPP_INPROC_OWNTHREAD
+static void pppInputThread(void *arg);
+#endif /* PPP_INPROC_OWNTHREAD */
+static void pppDrop(PPPControlRx *pcrx);
+static void pppInProc(PPPControlRx *pcrx, u_char *s, int l);
+static void pppFreeCurrentInputPacket(PPPControlRx *pcrx);
+#endif /* PPPOS_SUPPORT */
+
+
+/******************************/
+/*** PUBLIC DATA STRUCTURES ***/
+/******************************/
+static PPPControl pppControl[NUM_PPP]; /* The PPP interface control blocks. */
+
+/*
+ * PPP Data Link Layer "protocol" table.
+ * One entry per supported protocol.
+ * The last entry must be NULL.
+ */
+struct protent *ppp_protocols[] = {
+ &lcp_protent,
+#if PAP_SUPPORT
+ &pap_protent,
+#endif /* PAP_SUPPORT */
+#if CHAP_SUPPORT
+ &chap_protent,
+#endif /* CHAP_SUPPORT */
+#if CBCP_SUPPORT
+ &cbcp_protent,
+#endif /* CBCP_SUPPORT */
+ &ipcp_protent,
+#if CCP_SUPPORT
+ &ccp_protent,
+#endif /* CCP_SUPPORT */
+ NULL
+};
+
+
+/*
+ * Buffers for outgoing packets. This must be accessed only from the appropriate
+ * PPP task so that it doesn't need to be protected to avoid collisions.
+ */
+u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN];
+
+
+/*****************************/
+/*** LOCAL DATA STRUCTURES ***/
+/*****************************/
+
+#if PPPOS_SUPPORT
+/*
+ * FCS lookup table as calculated by genfcstab.
+ * @todo: smaller, slower implementation for lower memory footprint?
+ */
+static const u_short fcstab[256] = {
+ 0x0000, 0x1189, 0x2312, 0x329b, 0x4624, 0x57ad, 0x6536, 0x74bf,
+ 0x8c48, 0x9dc1, 0xaf5a, 0xbed3, 0xca6c, 0xdbe5, 0xe97e, 0xf8f7,
+ 0x1081, 0x0108, 0x3393, 0x221a, 0x56a5, 0x472c, 0x75b7, 0x643e,
+ 0x9cc9, 0x8d40, 0xbfdb, 0xae52, 0xdaed, 0xcb64, 0xf9ff, 0xe876,
+ 0x2102, 0x308b, 0x0210, 0x1399, 0x6726, 0x76af, 0x4434, 0x55bd,
+ 0xad4a, 0xbcc3, 0x8e58, 0x9fd1, 0xeb6e, 0xfae7, 0xc87c, 0xd9f5,
+ 0x3183, 0x200a, 0x1291, 0x0318, 0x77a7, 0x662e, 0x54b5, 0x453c,
+ 0xbdcb, 0xac42, 0x9ed9, 0x8f50, 0xfbef, 0xea66, 0xd8fd, 0xc974,
+ 0x4204, 0x538d, 0x6116, 0x709f, 0x0420, 0x15a9, 0x2732, 0x36bb,
+ 0xce4c, 0xdfc5, 0xed5e, 0xfcd7, 0x8868, 0x99e1, 0xab7a, 0xbaf3,
+ 0x5285, 0x430c, 0x7197, 0x601e, 0x14a1, 0x0528, 0x37b3, 0x263a,
+ 0xdecd, 0xcf44, 0xfddf, 0xec56, 0x98e9, 0x8960, 0xbbfb, 0xaa72,
+ 0x6306, 0x728f, 0x4014, 0x519d, 0x2522, 0x34ab, 0x0630, 0x17b9,
+ 0xef4e, 0xfec7, 0xcc5c, 0xddd5, 0xa96a, 0xb8e3, 0x8a78, 0x9bf1,
+ 0x7387, 0x620e, 0x5095, 0x411c, 0x35a3, 0x242a, 0x16b1, 0x0738,
+ 0xffcf, 0xee46, 0xdcdd, 0xcd54, 0xb9eb, 0xa862, 0x9af9, 0x8b70,
+ 0x8408, 0x9581, 0xa71a, 0xb693, 0xc22c, 0xd3a5, 0xe13e, 0xf0b7,
+ 0x0840, 0x19c9, 0x2b52, 0x3adb, 0x4e64, 0x5fed, 0x6d76, 0x7cff,
+ 0x9489, 0x8500, 0xb79b, 0xa612, 0xd2ad, 0xc324, 0xf1bf, 0xe036,
+ 0x18c1, 0x0948, 0x3bd3, 0x2a5a, 0x5ee5, 0x4f6c, 0x7df7, 0x6c7e,
+ 0xa50a, 0xb483, 0x8618, 0x9791, 0xe32e, 0xf2a7, 0xc03c, 0xd1b5,
+ 0x2942, 0x38cb, 0x0a50, 0x1bd9, 0x6f66, 0x7eef, 0x4c74, 0x5dfd,
+ 0xb58b, 0xa402, 0x9699, 0x8710, 0xf3af, 0xe226, 0xd0bd, 0xc134,
+ 0x39c3, 0x284a, 0x1ad1, 0x0b58, 0x7fe7, 0x6e6e, 0x5cf5, 0x4d7c,
+ 0xc60c, 0xd785, 0xe51e, 0xf497, 0x8028, 0x91a1, 0xa33a, 0xb2b3,
+ 0x4a44, 0x5bcd, 0x6956, 0x78df, 0x0c60, 0x1de9, 0x2f72, 0x3efb,
+ 0xd68d, 0xc704, 0xf59f, 0xe416, 0x90a9, 0x8120, 0xb3bb, 0xa232,
+ 0x5ac5, 0x4b4c, 0x79d7, 0x685e, 0x1ce1, 0x0d68, 0x3ff3, 0x2e7a,
+ 0xe70e, 0xf687, 0xc41c, 0xd595, 0xa12a, 0xb0a3, 0x8238, 0x93b1,
+ 0x6b46, 0x7acf, 0x4854, 0x59dd, 0x2d62, 0x3ceb, 0x0e70, 0x1ff9,
+ 0xf78f, 0xe606, 0xd49d, 0xc514, 0xb1ab, 0xa022, 0x92b9, 0x8330,
+ 0x7bc7, 0x6a4e, 0x58d5, 0x495c, 0x3de3, 0x2c6a, 0x1ef1, 0x0f78
+};
+
+/* PPP's Asynchronous-Control-Character-Map. The mask array is used
+ * to select the specific bit for a character. */
+static u_char pppACCMMask[] = {
+ 0x01,
+ 0x02,
+ 0x04,
+ 0x08,
+ 0x10,
+ 0x20,
+ 0x40,
+ 0x80
+};
+
+#if PPP_INPROC_OWNTHREAD
+/** Wake up the task blocked in reading from serial line (if any) */
+static void
+pppRecvWakeup(int pd)
+{
+ PPPDEBUG(LOG_DEBUG, ("pppRecvWakeup: unit %d\n", pd));
+ if (pppControl[pd].openFlag != 0) {
+ sio_read_abort(pppControl[pd].fd);
+ }
+}
+#endif /* PPP_INPROC_OWNTHREAD */
+#endif /* PPPOS_SUPPORT */
+
+void
+pppLinkTerminated(int pd)
+{
+ PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: unit %d\n", pd));
+
+#if PPPOE_SUPPORT
+ if (pppControl[pd].ethif) {
+ pppoe_disconnect(pppControl[pd].pppoe_sc);
+ } else
+#endif /* PPPOE_SUPPORT */
+ {
+#if PPPOS_SUPPORT
+ PPPControl* pc;
+#if PPP_INPROC_OWNTHREAD
+ pppRecvWakeup(pd);
+#endif /* PPP_INPROC_OWNTHREAD */
+ pc = &pppControl[pd];
+
+ PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode));
+ if (pc->linkStatusCB) {
+ pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL);
+ }
+
+ pc->openFlag = 0;/**/
+#endif /* PPPOS_SUPPORT */
+ }
+ PPPDEBUG(LOG_DEBUG, ("pppLinkTerminated: finished.\n"));
+}
+
+void
+pppLinkDown(int pd)
+{
+ PPPDEBUG(LOG_DEBUG, ("pppLinkDown: unit %d\n", pd));
+
+#if PPPOE_SUPPORT
+ if (pppControl[pd].ethif) {
+ pppoe_disconnect(pppControl[pd].pppoe_sc);
+ } else
+#endif /* PPPOE_SUPPORT */
+ {
+#if PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD
+ pppRecvWakeup(pd);
+#endif /* PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD*/
+ }
+}
+
+/** Initiate LCP open request */
+static void
+pppStart(int pd)
+{
+ PPPDEBUG(LOG_DEBUG, ("pppStart: unit %d\n", pd));
+ lcp_lowerup(pd);
+ lcp_open(pd); /* Start protocol */
+ PPPDEBUG(LOG_DEBUG, ("pppStart: finished\n"));
+}
+
+/** LCP close request */
+static void
+pppStop(int pd)
+{
+ PPPDEBUG(LOG_DEBUG, ("pppStop: unit %d\n", pd));
+ lcp_close(pd, "User request");
+}
+
+/** Called when carrier/link is lost */
+static void
+pppHup(int pd)
+{
+ PPPDEBUG(LOG_DEBUG, ("pppHupCB: unit %d\n", pd));
+ lcp_lowerdown(pd);
+ link_terminated(pd);
+}
+
+/***********************************/
+/*** PUBLIC FUNCTION DEFINITIONS ***/
+/***********************************/
+/* Initialize the PPP subsystem. */
+
+struct ppp_settings ppp_settings;
+
+void
+pppInit(void)
+{
+ struct protent *protp;
+ int i, j;
+
+ memset(&ppp_settings, 0, sizeof(ppp_settings));
+ ppp_settings.usepeerdns = 1;
+ pppSetAuth(PPPAUTHTYPE_NONE, NULL, NULL);
+
+ magicInit();
+
+ for (i = 0; i < NUM_PPP; i++) {
+ /* Initialize each protocol to the standard option set. */
+ for (j = 0; (protp = ppp_protocols[j]) != NULL; ++j) {
+ (*protp->init)(i);
+ }
+ }
+}
+
+void
+pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd)
+{
+ switch(authType) {
+ case PPPAUTHTYPE_NONE:
+ default:
+#ifdef LWIP_PPP_STRICT_PAP_REJECT
+ ppp_settings.refuse_pap = 1;
+#else /* LWIP_PPP_STRICT_PAP_REJECT */
+ /* some providers request pap and accept an empty login/pw */
+ ppp_settings.refuse_pap = 0;
+#endif /* LWIP_PPP_STRICT_PAP_REJECT */
+ ppp_settings.refuse_chap = 1;
+ break;
+
+ case PPPAUTHTYPE_ANY:
+ /* Warning: Using PPPAUTHTYPE_ANY might have security consequences.
+ * RFC 1994 says:
+ *
+ * In practice, within or associated with each PPP server, there is a
+ * database which associates "user" names with authentication
+ * information ("secrets"). It is not anticipated that a particular
+ * named user would be authenticated by multiple methods. This would
+ * make the user vulnerable to attacks which negotiate the least secure
+ * method from among a set (such as PAP rather than CHAP). If the same
+ * secret was used, PAP would reveal the secret to be used later with
+ * CHAP.
+ *
+ * Instead, for each user name there should be an indication of exactly
+ * one method used to authenticate that user name. If a user needs to
+ * make use of different authentication methods under different
+ * circumstances, then distinct user names SHOULD be employed, each of
+ * which identifies exactly one authentication method.
+ *
+ */
+ ppp_settings.refuse_pap = 0;
+ ppp_settings.refuse_chap = 0;
+ break;
+
+ case PPPAUTHTYPE_PAP:
+ ppp_settings.refuse_pap = 0;
+ ppp_settings.refuse_chap = 1;
+ break;
+
+ case PPPAUTHTYPE_CHAP:
+ ppp_settings.refuse_pap = 1;
+ ppp_settings.refuse_chap = 0;
+ break;
+ }
+
+ if(user) {
+ strncpy(ppp_settings.user, user, sizeof(ppp_settings.user)-1);
+ ppp_settings.user[sizeof(ppp_settings.user)-1] = '\0';
+ } else {
+ ppp_settings.user[0] = '\0';
+ }
+
+ if(passwd) {
+ strncpy(ppp_settings.passwd, passwd, sizeof(ppp_settings.passwd)-1);
+ ppp_settings.passwd[sizeof(ppp_settings.passwd)-1] = '\0';
+ } else {
+ ppp_settings.passwd[0] = '\0';
+ }
+}
+
+#if PPPOS_SUPPORT
+/** Open a new PPP connection using the given I/O device.
+ * This initializes the PPP control block but does not
+ * attempt to negotiate the LCP session. If this port
+ * connects to a modem, the modem connection must be
+ * established before calling this.
+ * Return a new PPP connection descriptor on success or
+ * an error code (negative) on failure.
+ *
+ * pppOpen() is directly defined to this function.
+ */
+int
+pppOverSerialOpen(sio_fd_t fd, pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx)
+{
+ PPPControl *pc;
+ int pd;
+
+ if (linkStatusCB == NULL) {
+ /* PPP is single-threaded: without a callback,
+ * there is no way to know when the link is up. */
+ return PPPERR_PARAM;
+ }
+
+ /* Find a free PPP session descriptor. */
+ for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++);
+
+ if (pd >= NUM_PPP) {
+ pd = PPPERR_OPEN;
+ } else {
+ pc = &pppControl[pd];
+ /* input pbuf left over from last session? */
+ pppFreeCurrentInputPacket(&pc->rx);
+ /* @todo: is this correct or do I overwrite something? */
+ memset(pc, 0, sizeof(PPPControl));
+ pc->rx.pd = pd;
+ pc->rx.fd = fd;
+
+ pc->openFlag = 1;
+ pc->fd = fd;
+
+#if VJ_SUPPORT
+ vj_compress_init(&pc->vjComp);
+#endif /* VJ_SUPPORT */
+
+ /*
+ * Default the in and out accm so that escape and flag characters
+ * are always escaped.
+ */
+ pc->rx.inACCM[15] = 0x60; /* no need to protect since RX is not running */
+ pc->outACCM[15] = 0x60;
+
+ pc->linkStatusCB = linkStatusCB;
+ pc->linkStatusCtx = linkStatusCtx;
+
+ /*
+ * Start the connection and handle incoming events (packet or timeout).
+ */
+ PPPDEBUG(LOG_INFO, ("pppOverSerialOpen: unit %d: Connecting\n", pd));
+ pppStart(pd);
+#if PPP_INPROC_OWNTHREAD
+ sys_thread_new(PPP_THREAD_NAME, pppInputThread, (void*)&pc->rx, PPP_THREAD_STACKSIZE, PPP_THREAD_PRIO);
+#endif /* PPP_INPROC_OWNTHREAD */
+ }
+
+ return pd;
+}
+#endif /* PPPOS_SUPPORT */
+
+#if PPPOE_SUPPORT
+static void pppOverEthernetLinkStatusCB(int pd, int up);
+
+void
+pppOverEthernetClose(int pd)
+{
+ PPPControl* pc = &pppControl[pd];
+
+ /* *TJL* There's no lcp_deinit */
+ lcp_close(pd, NULL);
+
+ pppoe_destroy(&pc->netif);
+}
+
+int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name,
+ pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx)
+{
+ PPPControl *pc;
+ int pd;
+
+ LWIP_UNUSED_ARG(service_name);
+ LWIP_UNUSED_ARG(concentrator_name);
+
+ if (linkStatusCB == NULL) {
+ /* PPP is single-threaded: without a callback,
+ * there is no way to know when the link is up. */
+ return PPPERR_PARAM;
+ }
+
+ /* Find a free PPP session descriptor. Critical region? */
+ for (pd = 0; pd < NUM_PPP && pppControl[pd].openFlag != 0; pd++);
+ if (pd >= NUM_PPP) {
+ pd = PPPERR_OPEN;
+ } else {
+ pc = &pppControl[pd];
+ memset(pc, 0, sizeof(PPPControl));
+ pc->openFlag = 1;
+ pc->ethif = ethif;
+
+ pc->linkStatusCB = linkStatusCB;
+ pc->linkStatusCtx = linkStatusCtx;
+
+ lcp_wantoptions[pd].mru = PPPOE_MAXMTU;
+ lcp_wantoptions[pd].neg_asyncmap = 0;
+ lcp_wantoptions[pd].neg_pcompression = 0;
+ lcp_wantoptions[pd].neg_accompression = 0;
+
+ lcp_allowoptions[pd].mru = PPPOE_MAXMTU;
+ lcp_allowoptions[pd].neg_asyncmap = 0;
+ lcp_allowoptions[pd].neg_pcompression = 0;
+ lcp_allowoptions[pd].neg_accompression = 0;
+
+ if(pppoe_create(ethif, pd, pppOverEthernetLinkStatusCB, &pc->pppoe_sc) != ERR_OK) {
+ pc->openFlag = 0;
+ return PPPERR_OPEN;
+ }
+
+ pppoe_connect(pc->pppoe_sc);
+ }
+
+ return pd;
+}
+#endif /* PPPOE_SUPPORT */
+
+
+/* Close a PPP connection and release the descriptor.
+ * Any outstanding packets in the queues are dropped.
+ * Return 0 on success, an error code on failure. */
+int
+pppClose(int pd)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 0;
+
+ PPPDEBUG(LOG_DEBUG, ("pppClose() called\n"));
+
+ /* Disconnect */
+#if PPPOE_SUPPORT
+ if(pc->ethif) {
+ PPPDEBUG(LOG_DEBUG, ("pppClose: unit %d kill_link -> pppStop\n", pd));
+ pc->errCode = PPPERR_USER;
+ /* This will leave us at PHASE_DEAD. */
+ pppStop(pd);
+ } else
+#endif /* PPPOE_SUPPORT */
+ {
+#if PPPOS_SUPPORT
+ PPPDEBUG(LOG_DEBUG, ("pppClose: unit %d kill_link -> pppStop\n", pd));
+ pc->errCode = PPPERR_USER;
+ /* This will leave us at PHASE_DEAD. */
+ pppStop(pd);
+#if PPP_INPROC_OWNTHREAD
+ pppRecvWakeup(pd);
+#endif /* PPP_INPROC_OWNTHREAD */
+#endif /* PPPOS_SUPPORT */
+ }
+
+ return st;
+}
+
+/* This function is called when carrier is lost on the PPP channel. */
+void
+pppSigHUP(int pd)
+{
+ PPPDEBUG(LOG_DEBUG, ("pppSigHUP: unit %d sig_hup -> pppHupCB\n", pd));
+ pppHup(pd);
+}
+
+#if PPPOS_SUPPORT
+static void
+nPut(PPPControl *pc, struct pbuf *nb)
+{
+ struct pbuf *b;
+ int c;
+
+ for(b = nb; b != NULL; b = b->next) {
+ if((c = sio_write(pc->fd, b->payload, b->len)) != b->len) {
+ PPPDEBUG(LOG_WARNING,
+ ("PPP nPut: incomplete sio_write(fd:%"SZT_F", len:%d, c: 0x%"X8_F") c = %d\n", (size_t)pc->fd, b->len, c, c));
+ LINK_STATS_INC(link.err);
+ pc->lastXMit = 0; /* prepend PPP_FLAG to next packet */
+ snmp_inc_ifoutdiscards(&pc->netif);
+ pbuf_free(nb);
+ return;
+ }
+ }
+
+ snmp_add_ifoutoctets(&pc->netif, nb->tot_len);
+ snmp_inc_ifoutucastpkts(&pc->netif);
+ pbuf_free(nb);
+ LINK_STATS_INC(link.xmit);
+}
+
+/*
+ * pppAppend - append given character to end of given pbuf. If outACCM
+ * is not NULL and the character needs to be escaped, do so.
+ * If pbuf is full, append another.
+ * Return the current pbuf.
+ */
+static struct pbuf *
+pppAppend(u_char c, struct pbuf *nb, ext_accm *outACCM)
+{
+ struct pbuf *tb = nb;
+
+ /* Make sure there is room for the character and an escape code.
+ * Sure we don't quite fill the buffer if the character doesn't
+ * get escaped but is one character worth complicating this? */
+ /* Note: We assume no packet header. */
+ if (nb && (PBUF_POOL_BUFSIZE - nb->len) < 2) {
+ tb = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
+ if (tb) {
+ nb->next = tb;
+ } else {
+ LINK_STATS_INC(link.memerr);
+ }
+ nb = tb;
+ }
+
+ if (nb) {
+ if (outACCM && ESCAPE_P(*outACCM, c)) {
+ *((u_char*)nb->payload + nb->len++) = PPP_ESCAPE;
+ *((u_char*)nb->payload + nb->len++) = c ^ PPP_TRANS;
+ } else {
+ *((u_char*)nb->payload + nb->len++) = c;
+ }
+ }
+
+ return tb;
+}
+#endif /* PPPOS_SUPPORT */
+
+#if PPPOE_SUPPORT
+static err_t
+pppifOutputOverEthernet(int pd, struct pbuf *p)
+{
+ PPPControl *pc = &pppControl[pd];
+ struct pbuf *pb;
+ u_short protocol = PPP_IP;
+ int i=0;
+ u16_t tot_len;
+
+ /* @todo: try to use pbuf_header() here! */
+ pb = pbuf_alloc(PBUF_LINK, PPPOE_HDRLEN + sizeof(protocol), PBUF_RAM);
+ if(!pb) {
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.proterr);
+ snmp_inc_ifoutdiscards(&pc->netif);
+ return ERR_MEM;
+ }
+
+ pbuf_header(pb, -(s16_t)PPPOE_HDRLEN);
+
+ pc->lastXMit = sys_jiffies();
+
+ if (!pc->pcomp || protocol > 0xFF) {
+ *((u_char*)pb->payload + i++) = (protocol >> 8) & 0xFF;
+ }
+ *((u_char*)pb->payload + i) = protocol & 0xFF;
+
+ pbuf_chain(pb, p);
+ tot_len = pb->tot_len;
+
+ if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) {
+ LINK_STATS_INC(link.err);
+ snmp_inc_ifoutdiscards(&pc->netif);
+ return PPPERR_DEVICE;
+ }
+
+ snmp_add_ifoutoctets(&pc->netif, tot_len);
+ snmp_inc_ifoutucastpkts(&pc->netif);
+ LINK_STATS_INC(link.xmit);
+ return ERR_OK;
+}
+#endif /* PPPOE_SUPPORT */
+
+/* Send a packet on the given connection. */
+static err_t
+pppifOutput(struct netif *netif, struct pbuf *pb, ip_addr_t *ipaddr)
+{
+ int pd = (int)(size_t)netif->state;
+ PPPControl *pc = &pppControl[pd];
+#if PPPOS_SUPPORT
+ u_short protocol = PPP_IP;
+ u_int fcsOut = PPP_INITFCS;
+ struct pbuf *headMB = NULL, *tailMB = NULL, *p;
+ u_char c;
+#endif /* PPPOS_SUPPORT */
+
+ LWIP_UNUSED_ARG(ipaddr);
+
+ /* Validate parameters. */
+ /* We let any protocol value go through - it can't hurt us
+ * and the peer will just drop it if it's not accepting it. */
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag || !pb) {
+ PPPDEBUG(LOG_WARNING, ("pppifOutput[%d]: bad parms prot=%d pb=%p\n",
+ pd, PPP_IP, pb));
+ LINK_STATS_INC(link.opterr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(netif);
+ return ERR_ARG;
+ }
+
+ /* Check that the link is up. */
+ if (lcp_phase[pd] == PHASE_DEAD) {
+ PPPDEBUG(LOG_ERR, ("pppifOutput[%d]: link not up\n", pd));
+ LINK_STATS_INC(link.rterr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(netif);
+ return ERR_RTE;
+ }
+
+#if PPPOE_SUPPORT
+ if(pc->ethif) {
+ return pppifOutputOverEthernet(pd, pb);
+ }
+#endif /* PPPOE_SUPPORT */
+
+#if PPPOS_SUPPORT
+ /* Grab an output buffer. */
+ headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
+ if (headMB == NULL) {
+ PPPDEBUG(LOG_WARNING, ("pppifOutput[%d]: first alloc fail\n", pd));
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(netif);
+ return ERR_MEM;
+ }
+
+#if VJ_SUPPORT
+ /*
+ * Attempt Van Jacobson header compression if VJ is configured and
+ * this is an IP packet.
+ */
+ if (protocol == PPP_IP && pc->vjEnabled) {
+ switch (vj_compress_tcp(&pc->vjComp, pb)) {
+ case TYPE_IP:
+ /* No change...
+ protocol = PPP_IP_PROTOCOL; */
+ break;
+ case TYPE_COMPRESSED_TCP:
+ protocol = PPP_VJC_COMP;
+ break;
+ case TYPE_UNCOMPRESSED_TCP:
+ protocol = PPP_VJC_UNCOMP;
+ break;
+ default:
+ PPPDEBUG(LOG_WARNING, ("pppifOutput[%d]: bad IP packet\n", pd));
+ LINK_STATS_INC(link.proterr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(netif);
+ pbuf_free(headMB);
+ return ERR_VAL;
+ }
+ }
+#endif /* VJ_SUPPORT */
+
+ tailMB = headMB;
+
+ /* Build the PPP header. */
+ if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) {
+ tailMB = pppAppend(PPP_FLAG, tailMB, NULL);
+ }
+
+ pc->lastXMit = sys_jiffies();
+ if (!pc->accomp) {
+ fcsOut = PPP_FCS(fcsOut, PPP_ALLSTATIONS);
+ tailMB = pppAppend(PPP_ALLSTATIONS, tailMB, &pc->outACCM);
+ fcsOut = PPP_FCS(fcsOut, PPP_UI);
+ tailMB = pppAppend(PPP_UI, tailMB, &pc->outACCM);
+ }
+ if (!pc->pcomp || protocol > 0xFF) {
+ c = (protocol >> 8) & 0xFF;
+ fcsOut = PPP_FCS(fcsOut, c);
+ tailMB = pppAppend(c, tailMB, &pc->outACCM);
+ }
+ c = protocol & 0xFF;
+ fcsOut = PPP_FCS(fcsOut, c);
+ tailMB = pppAppend(c, tailMB, &pc->outACCM);
+
+ /* Load packet. */
+ for(p = pb; p; p = p->next) {
+ int n;
+ u_char *sPtr;
+
+ sPtr = (u_char*)p->payload;
+ n = p->len;
+ while (n-- > 0) {
+ c = *sPtr++;
+
+ /* Update FCS before checking for special characters. */
+ fcsOut = PPP_FCS(fcsOut, c);
+
+ /* Copy to output buffer escaping special characters. */
+ tailMB = pppAppend(c, tailMB, &pc->outACCM);
+ }
+ }
+
+ /* Add FCS and trailing flag. */
+ c = ~fcsOut & 0xFF;
+ tailMB = pppAppend(c, tailMB, &pc->outACCM);
+ c = (~fcsOut >> 8) & 0xFF;
+ tailMB = pppAppend(c, tailMB, &pc->outACCM);
+ tailMB = pppAppend(PPP_FLAG, tailMB, NULL);
+
+ /* If we failed to complete the packet, throw it away. */
+ if (!tailMB) {
+ PPPDEBUG(LOG_WARNING,
+ ("pppifOutput[%d]: Alloc err - dropping proto=%d\n",
+ pd, protocol));
+ pbuf_free(headMB);
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifoutdiscards(netif);
+ return ERR_MEM;
+ }
+
+ /* Send it. */
+ PPPDEBUG(LOG_INFO, ("pppifOutput[%d]: proto=0x%"X16_F"\n", pd, protocol));
+
+ nPut(pc, headMB);
+#endif /* PPPOS_SUPPORT */
+
+ return ERR_OK;
+}
+
+/* Get and set parameters for the given connection.
+ * Return 0 on success, an error code on failure. */
+int
+pppIOCtl(int pd, int cmd, void *arg)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 0;
+
+ if (pd < 0 || pd >= NUM_PPP) {
+ st = PPPERR_PARAM;
+ } else {
+ switch(cmd) {
+ case PPPCTLG_UPSTATUS: /* Get the PPP up status. */
+ if (arg) {
+ *(int *)arg = (int)(pc->if_up);
+ } else {
+ st = PPPERR_PARAM;
+ }
+ break;
+ case PPPCTLS_ERRCODE: /* Set the PPP error code. */
+ if (arg) {
+ pc->errCode = *(int *)arg;
+ } else {
+ st = PPPERR_PARAM;
+ }
+ break;
+ case PPPCTLG_ERRCODE: /* Get the PPP error code. */
+ if (arg) {
+ *(int *)arg = (int)(pc->errCode);
+ } else {
+ st = PPPERR_PARAM;
+ }
+ break;
+#if PPPOS_SUPPORT
+ case PPPCTLG_FD: /* Get the fd associated with the ppp */
+ if (arg) {
+ *(sio_fd_t *)arg = pc->fd;
+ } else {
+ st = PPPERR_PARAM;
+ }
+ break;
+#endif /* PPPOS_SUPPORT */
+ default:
+ st = PPPERR_PARAM;
+ break;
+ }
+ }
+
+ return st;
+}
+
+/*
+ * Return the Maximum Transmission Unit for the given PPP connection.
+ */
+u_short
+pppMTU(int pd)
+{
+ PPPControl *pc = &pppControl[pd];
+ u_short st;
+
+ /* Validate parameters. */
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ } else {
+ st = pc->mtu;
+ }
+
+ return st;
+}
+
+#if PPPOE_SUPPORT
+int
+pppWriteOverEthernet(int pd, const u_char *s, int n)
+{
+ PPPControl *pc = &pppControl[pd];
+ struct pbuf *pb;
+
+ /* skip address & flags */
+ s += 2;
+ n -= 2;
+
+ LWIP_ASSERT("PPPOE_HDRLEN + n <= 0xffff", PPPOE_HDRLEN + n <= 0xffff);
+ pb = pbuf_alloc(PBUF_LINK, (u16_t)(PPPOE_HDRLEN + n), PBUF_RAM);
+ if(!pb) {
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.proterr);
+ snmp_inc_ifoutdiscards(&pc->netif);
+ return PPPERR_ALLOC;
+ }
+
+ pbuf_header(pb, -(s16_t)PPPOE_HDRLEN);
+
+ pc->lastXMit = sys_jiffies();
+
+ MEMCPY(pb->payload, s, n);
+
+ if(pppoe_xmit(pc->pppoe_sc, pb) != ERR_OK) {
+ LINK_STATS_INC(link.err);
+ snmp_inc_ifoutdiscards(&pc->netif);
+ return PPPERR_DEVICE;
+ }
+
+ snmp_add_ifoutoctets(&pc->netif, (u16_t)n);
+ snmp_inc_ifoutucastpkts(&pc->netif);
+ LINK_STATS_INC(link.xmit);
+ return PPPERR_NONE;
+}
+#endif /* PPPOE_SUPPORT */
+
+/*
+ * Write n characters to a ppp link.
+ * RETURN: >= 0 Number of characters written
+ * -1 Failed to write to device
+ */
+int
+pppWrite(int pd, const u_char *s, int n)
+{
+ PPPControl *pc = &pppControl[pd];
+#if PPPOS_SUPPORT
+ u_char c;
+ u_int fcsOut;
+ struct pbuf *headMB, *tailMB;
+#endif /* PPPOS_SUPPORT */
+
+#if PPPOE_SUPPORT
+ if(pc->ethif) {
+ return pppWriteOverEthernet(pd, s, n);
+ }
+#endif /* PPPOE_SUPPORT */
+
+#if PPPOS_SUPPORT
+ headMB = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
+ if (headMB == NULL) {
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.proterr);
+ snmp_inc_ifoutdiscards(&pc->netif);
+ return PPPERR_ALLOC;
+ }
+
+ tailMB = headMB;
+
+ /* If the link has been idle, we'll send a fresh flag character to
+ * flush any noise. */
+ if ((sys_jiffies() - pc->lastXMit) >= PPP_MAXIDLEFLAG) {
+ tailMB = pppAppend(PPP_FLAG, tailMB, NULL);
+ }
+ pc->lastXMit = sys_jiffies();
+
+ fcsOut = PPP_INITFCS;
+ /* Load output buffer. */
+ while (n-- > 0) {
+ c = *s++;
+
+ /* Update FCS before checking for special characters. */
+ fcsOut = PPP_FCS(fcsOut, c);
+
+ /* Copy to output buffer escaping special characters. */
+ tailMB = pppAppend(c, tailMB, &pc->outACCM);
+ }
+
+ /* Add FCS and trailing flag. */
+ c = ~fcsOut & 0xFF;
+ tailMB = pppAppend(c, tailMB, &pc->outACCM);
+ c = (~fcsOut >> 8) & 0xFF;
+ tailMB = pppAppend(c, tailMB, &pc->outACCM);
+ tailMB = pppAppend(PPP_FLAG, tailMB, NULL);
+
+ /* If we failed to complete the packet, throw it away.
+ * Otherwise send it. */
+ if (!tailMB) {
+ PPPDEBUG(LOG_WARNING,
+ ("pppWrite[%d]: Alloc err - dropping pbuf len=%d\n", pd, headMB->len));
+ /*"pppWrite[%d]: Alloc err - dropping %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */
+ pbuf_free(headMB);
+ LINK_STATS_INC(link.memerr);
+ LINK_STATS_INC(link.proterr);
+ snmp_inc_ifoutdiscards(&pc->netif);
+ return PPPERR_ALLOC;
+ }
+
+ PPPDEBUG(LOG_INFO, ("pppWrite[%d]: len=%d\n", pd, headMB->len));
+ /* "pppWrite[%d]: %d:%.*H", pd, headMB->len, LWIP_MIN(headMB->len * 2, 40), headMB->payload)); */
+ nPut(pc, headMB);
+#endif /* PPPOS_SUPPORT */
+
+ return PPPERR_NONE;
+}
+
+/*
+ * ppp_send_config - configure the transmit characteristics of
+ * the ppp interface.
+ */
+void
+ppp_send_config( int unit, u16_t mtu, u32_t asyncmap, int pcomp, int accomp)
+{
+ PPPControl *pc = &pppControl[unit];
+ int i;
+
+ pc->mtu = mtu;
+ pc->pcomp = pcomp;
+ pc->accomp = accomp;
+
+ /* Load the ACCM bits for the 32 control codes. */
+ for (i = 0; i < 32/8; i++) {
+ pc->outACCM[i] = (u_char)((asyncmap >> (8 * i)) & 0xFF);
+ }
+ PPPDEBUG(LOG_INFO, ("ppp_send_config[%d]: outACCM=%X %X %X %X\n",
+ unit,
+ pc->outACCM[0], pc->outACCM[1], pc->outACCM[2], pc->outACCM[3]));
+}
+
+
+/*
+ * ppp_set_xaccm - set the extended transmit ACCM for the interface.
+ */
+void
+ppp_set_xaccm(int unit, ext_accm *accm)
+{
+ SMEMCPY(pppControl[unit].outACCM, accm, sizeof(ext_accm));
+ PPPDEBUG(LOG_INFO, ("ppp_set_xaccm[%d]: outACCM=%X %X %X %X\n",
+ unit,
+ pppControl[unit].outACCM[0],
+ pppControl[unit].outACCM[1],
+ pppControl[unit].outACCM[2],
+ pppControl[unit].outACCM[3]));
+}
+
+
+/*
+ * ppp_recv_config - configure the receive-side characteristics of
+ * the ppp interface.
+ */
+void
+ppp_recv_config( int unit, int mru, u32_t asyncmap, int pcomp, int accomp)
+{
+ PPPControl *pc = &pppControl[unit];
+ int i;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ LWIP_UNUSED_ARG(accomp);
+ LWIP_UNUSED_ARG(pcomp);
+ LWIP_UNUSED_ARG(mru);
+
+ /* Load the ACCM bits for the 32 control codes. */
+ SYS_ARCH_PROTECT(lev);
+ for (i = 0; i < 32 / 8; i++) {
+ /* @todo: does this work? ext_accm has been modified from pppd! */
+ pc->rx.inACCM[i] = (u_char)(asyncmap >> (i * 8));
+ }
+ SYS_ARCH_UNPROTECT(lev);
+ PPPDEBUG(LOG_INFO, ("ppp_recv_config[%d]: inACCM=%X %X %X %X\n",
+ unit,
+ pc->rx.inACCM[0], pc->rx.inACCM[1], pc->rx.inACCM[2], pc->rx.inACCM[3]));
+}
+
+#if 0
+/*
+ * ccp_test - ask kernel whether a given compression method
+ * is acceptable for use. Returns 1 if the method and parameters
+ * are OK, 0 if the method is known but the parameters are not OK
+ * (e.g. code size should be reduced), or -1 if the method is unknown.
+ */
+int
+ccp_test( int unit, int opt_len, int for_transmit, u_char *opt_ptr)
+{
+ return 0; /* XXX Currently no compression. */
+}
+
+/*
+ * ccp_flags_set - inform kernel about the current state of CCP.
+ */
+void
+ccp_flags_set(int unit, int isopen, int isup)
+{
+ /* XXX */
+}
+
+/*
+ * ccp_fatal_error - returns 1 if decompression was disabled as a
+ * result of an error detected after decompression of a packet,
+ * 0 otherwise. This is necessary because of patent nonsense.
+ */
+int
+ccp_fatal_error(int unit)
+{
+ /* XXX */
+ return 0;
+}
+#endif
+
+/*
+ * get_idle_time - return how long the link has been idle.
+ */
+int
+get_idle_time(int u, struct ppp_idle *ip)
+{
+ /* XXX */
+ LWIP_UNUSED_ARG(u);
+ LWIP_UNUSED_ARG(ip);
+
+ return 0;
+}
+
+
+/*
+ * Return user specified netmask, modified by any mask we might determine
+ * for address `addr' (in network byte order).
+ * Here we scan through the system's list of interfaces, looking for
+ * any non-point-to-point interfaces which might appear to be on the same
+ * network as `addr'. If we find any, we OR in their netmask to the
+ * user-specified netmask.
+ */
+u32_t
+GetMask(u32_t addr)
+{
+ u32_t mask, nmask;
+
+ addr = htonl(addr);
+ if (IP_CLASSA(addr)) { /* determine network mask for address class */
+ nmask = IP_CLASSA_NET;
+ } else if (IP_CLASSB(addr)) {
+ nmask = IP_CLASSB_NET;
+ } else {
+ nmask = IP_CLASSC_NET;
+ }
+
+ /* class D nets are disallowed by bad_ip_adrs */
+ mask = PP_HTONL(0xffffff00UL) | htonl(nmask);
+
+ /* XXX
+ * Scan through the system's network interfaces.
+ * Get each netmask and OR them into our mask.
+ */
+
+ return mask;
+}
+
+/*
+ * sifvjcomp - config tcp header compression
+ */
+int
+sifvjcomp(int pd, int vjcomp, u8_t cidcomp, u8_t maxcid)
+{
+#if PPPOS_SUPPORT && VJ_SUPPORT
+ PPPControl *pc = &pppControl[pd];
+
+ pc->vjEnabled = vjcomp;
+ pc->vjComp.compressSlot = cidcomp;
+ pc->vjComp.maxSlotIndex = maxcid;
+ PPPDEBUG(LOG_INFO, ("sifvjcomp: VJ compress enable=%d slot=%d max slot=%d\n",
+ vjcomp, cidcomp, maxcid));
+#else /* PPPOS_SUPPORT && VJ_SUPPORT */
+ LWIP_UNUSED_ARG(pd);
+ LWIP_UNUSED_ARG(vjcomp);
+ LWIP_UNUSED_ARG(cidcomp);
+ LWIP_UNUSED_ARG(maxcid);
+#endif /* PPPOS_SUPPORT && VJ_SUPPORT */
+
+ return 0;
+}
+
+/*
+ * pppifNetifInit - netif init callback
+ */
+static err_t
+pppifNetifInit(struct netif *netif)
+{
+ netif->name[0] = 'p';
+ netif->name[1] = 'p';
+ netif->output = pppifOutput;
+ netif->mtu = pppMTU((int)(size_t)netif->state);
+ netif->flags = NETIF_FLAG_POINTTOPOINT | NETIF_FLAG_LINK_UP;
+#if LWIP_NETIF_HOSTNAME
+ /* @todo: Initialize interface hostname */
+ /* netif_set_hostname(netif, "lwip"); */
+#endif /* LWIP_NETIF_HOSTNAME */
+ return ERR_OK;
+}
+
+
+/*
+ * sifup - Config the interface up and enable IP packets to pass.
+ */
+int
+sifup(int pd)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
+ } else {
+ netif_remove(&pc->netif);
+ if (netif_add(&pc->netif, &pc->addrs.our_ipaddr, &pc->addrs.netmask,
+ &pc->addrs.his_ipaddr, (void *)(size_t)pd, pppifNetifInit, ip_input)) {
+ netif_set_up(&pc->netif);
+ pc->if_up = 1;
+ pc->errCode = PPPERR_NONE;
+
+ PPPDEBUG(LOG_DEBUG, ("sifup: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode));
+ if (pc->linkStatusCB) {
+ pc->linkStatusCB(pc->linkStatusCtx, pc->errCode, &pc->addrs);
+ }
+ } else {
+ st = 0;
+ PPPDEBUG(LOG_ERR, ("sifup[%d]: netif_add failed\n", pd));
+ }
+ }
+
+ return st;
+}
+
+/*
+ * sifnpmode - Set the mode for handling packets for a given NP.
+ */
+int
+sifnpmode(int u, int proto, enum NPmode mode)
+{
+ LWIP_UNUSED_ARG(u);
+ LWIP_UNUSED_ARG(proto);
+ LWIP_UNUSED_ARG(mode);
+ return 0;
+}
+
+/*
+ * sifdown - Config the interface down and disable IP.
+ */
+int
+sifdown(int pd)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifdown[%d]: bad parms\n", pd));
+ } else {
+ pc->if_up = 0;
+ /* make sure the netif status callback is called */
+ netif_set_down(&pc->netif);
+ netif_remove(&pc->netif);
+ PPPDEBUG(LOG_DEBUG, ("sifdown: unit %d: linkStatusCB=%p errCode=%d\n", pd, pc->linkStatusCB, pc->errCode));
+ if (pc->linkStatusCB) {
+ pc->linkStatusCB(pc->linkStatusCtx, PPPERR_CONNECT, NULL);
+ }
+ }
+ return st;
+}
+
+/**
+ * sifaddr - Config the interface IP addresses and netmask.
+ * @param pd Interface unit ???
+ * @param o Our IP address ???
+ * @param h His IP address ???
+ * @param m IP subnet mask ???
+ * @param ns1 Primary DNS
+ * @param ns2 Secondary DNS
+ */
+int
+sifaddr( int pd, u32_t o, u32_t h, u32_t m, u32_t ns1, u32_t ns2)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
+ } else {
+ SMEMCPY(&pc->addrs.our_ipaddr, &o, sizeof(o));
+ SMEMCPY(&pc->addrs.his_ipaddr, &h, sizeof(h));
+ SMEMCPY(&pc->addrs.netmask, &m, sizeof(m));
+ SMEMCPY(&pc->addrs.dns1, &ns1, sizeof(ns1));
+ SMEMCPY(&pc->addrs.dns2, &ns2, sizeof(ns2));
+ }
+ return st;
+}
+
+/**
+ * cifaddr - Clear the interface IP addresses, and delete routes
+ * through the interface if possible.
+ * @param pd Interface unit ???
+ * @param o Our IP address ???
+ * @param h IP broadcast address ???
+ */
+int
+cifaddr( int pd, u32_t o, u32_t h)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ LWIP_UNUSED_ARG(o);
+ LWIP_UNUSED_ARG(h);
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
+ } else {
+ IP4_ADDR(&pc->addrs.our_ipaddr, 0,0,0,0);
+ IP4_ADDR(&pc->addrs.his_ipaddr, 0,0,0,0);
+ IP4_ADDR(&pc->addrs.netmask, 255,255,255,0);
+ IP4_ADDR(&pc->addrs.dns1, 0,0,0,0);
+ IP4_ADDR(&pc->addrs.dns2, 0,0,0,0);
+ }
+ return st;
+}
+
+/*
+ * sifdefaultroute - assign a default route through the address given.
+ */
+int
+sifdefaultroute(int pd, u32_t l, u32_t g)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ LWIP_UNUSED_ARG(l);
+ LWIP_UNUSED_ARG(g);
+
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
+ } else {
+ netif_set_default(&pc->netif);
+ }
+
+ /* TODO: check how PPP handled the netMask, previously not set by ipSetDefault */
+
+ return st;
+}
+
+/*
+ * cifdefaultroute - delete a default route through the address given.
+ */
+int
+cifdefaultroute(int pd, u32_t l, u32_t g)
+{
+ PPPControl *pc = &pppControl[pd];
+ int st = 1;
+
+ LWIP_UNUSED_ARG(l);
+ LWIP_UNUSED_ARG(g);
+
+ if (pd < 0 || pd >= NUM_PPP || !pc->openFlag) {
+ st = 0;
+ PPPDEBUG(LOG_WARNING, ("sifup[%d]: bad parms\n", pd));
+ } else {
+ netif_set_default(NULL);
+ }
+
+ return st;
+}
+
+/**********************************/
+/*** LOCAL FUNCTION DEFINITIONS ***/
+/**********************************/
+
+#if PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD
+/* The main PPP process function. This implements the state machine according
+ * to section 4 of RFC 1661: The Point-To-Point Protocol. */
+static void
+pppInputThread(void *arg)
+{
+ int count;
+ PPPControlRx *pcrx = arg;
+
+ while (lcp_phase[pcrx->pd] != PHASE_DEAD) {
+ count = sio_read(pcrx->fd, pcrx->rxbuf, PPPOS_RX_BUFSIZE);
+ if(count > 0) {
+ pppInProc(pcrx, pcrx->rxbuf, count);
+ } else {
+ /* nothing received, give other tasks a chance to run */
+ sys_msleep(1);
+ }
+ }
+}
+#endif /* PPPOS_SUPPORT && PPP_INPROC_OWNTHREAD */
+
+#if PPPOE_SUPPORT
+
+void
+pppOverEthernetInitFailed(int pd)
+{
+ PPPControl* pc;
+
+ pppHup(pd);
+ pppStop(pd);
+
+ pc = &pppControl[pd];
+ pppoe_destroy(&pc->netif);
+ pc->openFlag = 0;
+
+ if(pc->linkStatusCB) {
+ pc->linkStatusCB(pc->linkStatusCtx, pc->errCode ? pc->errCode : PPPERR_PROTOCOL, NULL);
+ }
+}
+
+static void
+pppOverEthernetLinkStatusCB(int pd, int up)
+{
+ if(up) {
+ PPPDEBUG(LOG_INFO, ("pppOverEthernetLinkStatusCB: unit %d: Connecting\n", pd));
+ pppStart(pd);
+ } else {
+ pppOverEthernetInitFailed(pd);
+ }
+}
+#endif /* PPPOE_SUPPORT */
+
+struct pbuf *
+pppSingleBuf(struct pbuf *p)
+{
+ struct pbuf *q, *b;
+ u_char *pl;
+
+ if(p->tot_len == p->len) {
+ return p;
+ }
+
+ q = pbuf_alloc(PBUF_RAW, p->tot_len, PBUF_RAM);
+ if(!q) {
+ PPPDEBUG(LOG_ERR,
+ ("pppSingleBuf: unable to alloc new buf (%d)\n", p->tot_len));
+ return p; /* live dangerously */
+ }
+
+ for(b = p, pl = q->payload; b != NULL; b = b->next) {
+ MEMCPY(pl, b->payload, b->len);
+ pl += b->len;
+ }
+
+ pbuf_free(p);
+
+ return q;
+}
+
+/** Input helper struct, must be packed since it is stored to pbuf->payload,
+ * which might be unaligned.
+ */
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/bpstruct.h"
+#endif
+PACK_STRUCT_BEGIN
+struct pppInputHeader {
+ PACK_STRUCT_FIELD(int unit);
+ PACK_STRUCT_FIELD(u16_t proto);
+} PACK_STRUCT_STRUCT;
+PACK_STRUCT_END
+#ifdef PACK_STRUCT_USE_INCLUDES
+# include "arch/epstruct.h"
+#endif
+
+/*
+ * Pass the processed input packet to the appropriate handler.
+ * This function and all handlers run in the context of the tcpip_thread
+ */
+static void
+pppInput(void *arg)
+{
+ struct pbuf *nb = (struct pbuf *)arg;
+ u16_t protocol;
+ int pd;
+
+ pd = ((struct pppInputHeader *)nb->payload)->unit;
+ protocol = ((struct pppInputHeader *)nb->payload)->proto;
+
+ if(pbuf_header(nb, -(int)sizeof(struct pppInputHeader))) {
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ goto drop;
+ }
+
+ LINK_STATS_INC(link.recv);
+ snmp_inc_ifinucastpkts(&pppControl[pd].netif);
+ snmp_add_ifinoctets(&pppControl[pd].netif, nb->tot_len);
+
+ /*
+ * Toss all non-LCP packets unless LCP is OPEN.
+ * Until we get past the authentication phase, toss all packets
+ * except LCP, LQR and authentication packets.
+ */
+ if((lcp_phase[pd] <= PHASE_AUTHENTICATE) && (protocol != PPP_LCP)) {
+ if(!((protocol == PPP_LQR) || (protocol == PPP_PAP) || (protocol == PPP_CHAP)) ||
+ (lcp_phase[pd] != PHASE_AUTHENTICATE)) {
+ PPPDEBUG(LOG_INFO, ("pppInput: discarding proto 0x%"X16_F" in phase %d\n", protocol, lcp_phase[pd]));
+ goto drop;
+ }
+ }
+
+ switch(protocol) {
+ case PPP_VJC_COMP: /* VJ compressed TCP */
+#if PPPOS_SUPPORT && VJ_SUPPORT
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: vj_comp in pbuf len=%d\n", pd, nb->len));
+ /*
+ * Clip off the VJ header and prepend the rebuilt TCP/IP header and
+ * pass the result to IP.
+ */
+ if ((vj_uncompress_tcp(&nb, &pppControl[pd].vjComp) >= 0) && (pppControl[pd].netif.input)) {
+ pppControl[pd].netif.input(nb, &pppControl[pd].netif);
+ return;
+ }
+ /* Something's wrong so drop it. */
+ PPPDEBUG(LOG_WARNING, ("pppInput[%d]: Dropping VJ compressed\n", pd));
+#else /* PPPOS_SUPPORT && VJ_SUPPORT */
+ /* No handler for this protocol so drop the packet. */
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: drop VJ Comp in %d:%s\n", pd, nb->len, nb->payload));
+#endif /* PPPOS_SUPPORT && VJ_SUPPORT */
+ break;
+
+ case PPP_VJC_UNCOMP: /* VJ uncompressed TCP */
+#if PPPOS_SUPPORT && VJ_SUPPORT
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: vj_un in pbuf len=%d\n", pd, nb->len));
+ /*
+ * Process the TCP/IP header for VJ header compression and then pass
+ * the packet to IP.
+ */
+ if ((vj_uncompress_uncomp(nb, &pppControl[pd].vjComp) >= 0) && pppControl[pd].netif.input) {
+ pppControl[pd].netif.input(nb, &pppControl[pd].netif);
+ return;
+ }
+ /* Something's wrong so drop it. */
+ PPPDEBUG(LOG_WARNING, ("pppInput[%d]: Dropping VJ uncompressed\n", pd));
+#else /* PPPOS_SUPPORT && VJ_SUPPORT */
+ /* No handler for this protocol so drop the packet. */
+ PPPDEBUG(LOG_INFO,
+ ("pppInput[%d]: drop VJ UnComp in %d:.*H\n",
+ pd, nb->len, LWIP_MIN(nb->len * 2, 40), nb->payload));
+#endif /* PPPOS_SUPPORT && VJ_SUPPORT */
+ break;
+
+ case PPP_IP: /* Internet Protocol */
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: ip in pbuf len=%d\n", pd, nb->len));
+ if (pppControl[pd].netif.input) {
+ pppControl[pd].netif.input(nb, &pppControl[pd].netif);
+ return;
+ }
+ break;
+
+ default: {
+ struct protent *protp;
+ int i;
+
+ /*
+ * Upcall the proper protocol input routine.
+ */
+ for (i = 0; (protp = ppp_protocols[i]) != NULL; ++i) {
+ if (protp->protocol == protocol && protp->enabled_flag) {
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: %s len=%d\n", pd, protp->name, nb->len));
+ nb = pppSingleBuf(nb);
+ (*protp->input)(pd, nb->payload, nb->len);
+ PPPDEBUG(LOG_DETAIL, ("pppInput[%d]: packet processed\n", pd));
+ goto out;
+ }
+ }
+
+ /* No handler for this protocol so reject the packet. */
+ PPPDEBUG(LOG_INFO, ("pppInput[%d]: rejecting unsupported proto 0x%"X16_F" len=%d\n", pd, protocol, nb->len));
+ if (pbuf_header(nb, sizeof(protocol))) {
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ goto drop;
+ }
+#if BYTE_ORDER == LITTLE_ENDIAN
+ protocol = htons(protocol);
+#endif /* BYTE_ORDER == LITTLE_ENDIAN */
+ SMEMCPY(nb->payload, &protocol, sizeof(protocol));
+ lcp_sprotrej(pd, nb->payload, nb->len);
+ }
+ break;
+ }
+
+drop:
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifindiscards(&pppControl[pd].netif);
+
+out:
+ pbuf_free(nb);
+ return;
+}
+
+#if PPPOS_SUPPORT
+/*
+ * Drop the input packet.
+ */
+static void
+pppFreeCurrentInputPacket(PPPControlRx *pcrx)
+{
+ if (pcrx->inHead != NULL) {
+ if (pcrx->inTail && (pcrx->inTail != pcrx->inHead)) {
+ pbuf_free(pcrx->inTail);
+ }
+ pbuf_free(pcrx->inHead);
+ pcrx->inHead = NULL;
+ }
+ pcrx->inTail = NULL;
+}
+
+/*
+ * Drop the input packet and increase error counters.
+ */
+static void
+pppDrop(PPPControlRx *pcrx)
+{
+ if (pcrx->inHead != NULL) {
+#if 0
+ PPPDEBUG(LOG_INFO, ("pppDrop: %d:%.*H\n", pcrx->inHead->len, min(60, pcrx->inHead->len * 2), pcrx->inHead->payload));
+#endif
+ PPPDEBUG(LOG_INFO, ("pppDrop: pbuf len=%d, addr %p\n", pcrx->inHead->len, (void*)pcrx->inHead));
+ }
+ pppFreeCurrentInputPacket(pcrx);
+#if VJ_SUPPORT
+ vj_uncompress_err(&pppControl[pcrx->pd].vjComp);
+#endif /* VJ_SUPPORT */
+
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifindiscards(&pppControl[pcrx->pd].netif);
+}
+
+#if !PPP_INPROC_OWNTHREAD
+/** Pass received raw characters to PPPoS to be decoded. This function is
+ * thread-safe and can be called from a dedicated RX-thread or from a main-loop.
+ *
+ * @param pd PPP descriptor index, returned by pppOpen()
+ * @param data received data
+ * @param len length of received data
+ */
+void
+pppos_input(int pd, u_char* data, int len)
+{
+ pppInProc(&pppControl[pd].rx, data, len);
+}
+#endif
+
+/**
+ * Process a received octet string.
+ */
+static void
+pppInProc(PPPControlRx *pcrx, u_char *s, int l)
+{
+ struct pbuf *nextNBuf;
+ u_char curChar;
+ u_char escaped;
+ SYS_ARCH_DECL_PROTECT(lev);
+
+ PPPDEBUG(LOG_DEBUG, ("pppInProc[%d]: got %d bytes\n", pcrx->pd, l));
+ while (l-- > 0) {
+ curChar = *s++;
+
+ SYS_ARCH_PROTECT(lev);
+ escaped = ESCAPE_P(pcrx->inACCM, curChar);
+ SYS_ARCH_UNPROTECT(lev);
+ /* Handle special characters. */
+ if (escaped) {
+ /* Check for escape sequences. */
+ /* XXX Note that this does not handle an escaped 0x5d character which
+ * would appear as an escape character. Since this is an ASCII ']'
+ * and there is no reason that I know of to escape it, I won't complicate
+ * the code to handle this case. GLL */
+ if (curChar == PPP_ESCAPE) {
+ pcrx->inEscaped = 1;
+ /* Check for the flag character. */
+ } else if (curChar == PPP_FLAG) {
+ /* If this is just an extra flag character, ignore it. */
+ if (pcrx->inState <= PDADDRESS) {
+ /* ignore it */;
+ /* If we haven't received the packet header, drop what has come in. */
+ } else if (pcrx->inState < PDDATA) {
+ PPPDEBUG(LOG_WARNING,
+ ("pppInProc[%d]: Dropping incomplete packet %d\n",
+ pcrx->pd, pcrx->inState));
+ LINK_STATS_INC(link.lenerr);
+ pppDrop(pcrx);
+ /* If the fcs is invalid, drop the packet. */
+ } else if (pcrx->inFCS != PPP_GOODFCS) {
+ PPPDEBUG(LOG_INFO,
+ ("pppInProc[%d]: Dropping bad fcs 0x%"X16_F" proto=0x%"X16_F"\n",
+ pcrx->pd, pcrx->inFCS, pcrx->inProtocol));
+ /* Note: If you get lots of these, check for UART frame errors or try different baud rate */
+ LINK_STATS_INC(link.chkerr);
+ pppDrop(pcrx);
+ /* Otherwise it's a good packet so pass it on. */
+ } else {
+ struct pbuf *inp;
+ /* Trim off the checksum. */
+ if(pcrx->inTail->len > 2) {
+ pcrx->inTail->len -= 2;
+
+ pcrx->inTail->tot_len = pcrx->inTail->len;
+ if (pcrx->inTail != pcrx->inHead) {
+ pbuf_cat(pcrx->inHead, pcrx->inTail);
+ }
+ } else {
+ pcrx->inTail->tot_len = pcrx->inTail->len;
+ if (pcrx->inTail != pcrx->inHead) {
+ pbuf_cat(pcrx->inHead, pcrx->inTail);
+ }
+
+ pbuf_realloc(pcrx->inHead, pcrx->inHead->tot_len - 2);
+ }
+
+ /* Dispatch the packet thereby consuming it. */
+ inp = pcrx->inHead;
+ /* Packet consumed, release our references. */
+ pcrx->inHead = NULL;
+ pcrx->inTail = NULL;
+#if PPP_INPROC_MULTITHREADED
+ if(tcpip_callback_with_block(pppInput, inp, 0) != ERR_OK) {
+ PPPDEBUG(LOG_ERR, ("pppInProc[%d]: tcpip_callback() failed, dropping packet\n", pcrx->pd));
+ pbuf_free(inp);
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifindiscards(&pppControl[pcrx->pd].netif);
+ }
+#else /* PPP_INPROC_MULTITHREADED */
+ pppInput(inp);
+#endif /* PPP_INPROC_MULTITHREADED */
+ }
+
+ /* Prepare for a new packet. */
+ pcrx->inFCS = PPP_INITFCS;
+ pcrx->inState = PDADDRESS;
+ pcrx->inEscaped = 0;
+ /* Other characters are usually control characters that may have
+ * been inserted by the physical layer so here we just drop them. */
+ } else {
+ PPPDEBUG(LOG_WARNING,
+ ("pppInProc[%d]: Dropping ACCM char <%d>\n", pcrx->pd, curChar));
+ }
+ /* Process other characters. */
+ } else {
+ /* Unencode escaped characters. */
+ if (pcrx->inEscaped) {
+ pcrx->inEscaped = 0;
+ curChar ^= PPP_TRANS;
+ }
+
+ /* Process character relative to current state. */
+ switch(pcrx->inState) {
+ case PDIDLE: /* Idle state - waiting. */
+ /* Drop the character if it's not 0xff
+ * we would have processed a flag character above. */
+ if (curChar != PPP_ALLSTATIONS) {
+ break;
+ }
+
+ /* Fall through */
+ case PDSTART: /* Process start flag. */
+ /* Prepare for a new packet. */
+ pcrx->inFCS = PPP_INITFCS;
+
+ /* Fall through */
+ case PDADDRESS: /* Process address field. */
+ if (curChar == PPP_ALLSTATIONS) {
+ pcrx->inState = PDCONTROL;
+ break;
+ }
+ /* Else assume compressed address and control fields so
+ * fall through to get the protocol... */
+ case PDCONTROL: /* Process control field. */
+ /* If we don't get a valid control code, restart. */
+ if (curChar == PPP_UI) {
+ pcrx->inState = PDPROTOCOL1;
+ break;
+ }
+#if 0
+ else {
+ PPPDEBUG(LOG_WARNING,
+ ("pppInProc[%d]: Invalid control <%d>\n", pcrx->pd, curChar));
+ pcrx->inState = PDSTART;
+ }
+#endif
+ case PDPROTOCOL1: /* Process protocol field 1. */
+ /* If the lower bit is set, this is the end of the protocol
+ * field. */
+ if (curChar & 1) {
+ pcrx->inProtocol = curChar;
+ pcrx->inState = PDDATA;
+ } else {
+ pcrx->inProtocol = (u_int)curChar << 8;
+ pcrx->inState = PDPROTOCOL2;
+ }
+ break;
+ case PDPROTOCOL2: /* Process protocol field 2. */
+ pcrx->inProtocol |= curChar;
+ pcrx->inState = PDDATA;
+ break;
+ case PDDATA: /* Process data byte. */
+ /* Make space to receive processed data. */
+ if (pcrx->inTail == NULL || pcrx->inTail->len == PBUF_POOL_BUFSIZE) {
+ if (pcrx->inTail != NULL) {
+ pcrx->inTail->tot_len = pcrx->inTail->len;
+ if (pcrx->inTail != pcrx->inHead) {
+ pbuf_cat(pcrx->inHead, pcrx->inTail);
+ /* give up the inTail reference now */
+ pcrx->inTail = NULL;
+ }
+ }
+ /* If we haven't started a packet, we need a packet header. */
+ nextNBuf = pbuf_alloc(PBUF_RAW, 0, PBUF_POOL);
+ if (nextNBuf == NULL) {
+ /* No free buffers. Drop the input packet and let the
+ * higher layers deal with it. Continue processing
+ * the received pbuf chain in case a new packet starts. */
+ PPPDEBUG(LOG_ERR, ("pppInProc[%d]: NO FREE MBUFS!\n", pcrx->pd));
+ LINK_STATS_INC(link.memerr);
+ pppDrop(pcrx);
+ pcrx->inState = PDSTART; /* Wait for flag sequence. */
+ break;
+ }
+ if (pcrx->inHead == NULL) {
+ struct pppInputHeader *pih = nextNBuf->payload;
+
+ pih->unit = pcrx->pd;
+ pih->proto = pcrx->inProtocol;
+
+ nextNBuf->len += sizeof(*pih);
+
+ pcrx->inHead = nextNBuf;
+ }
+ pcrx->inTail = nextNBuf;
+ }
+ /* Load character into buffer. */
+ ((u_char*)pcrx->inTail->payload)[pcrx->inTail->len++] = curChar;
+ break;
+ }
+
+ /* update the frame check sequence number. */
+ pcrx->inFCS = PPP_FCS(pcrx->inFCS, curChar);
+ }
+ } /* while (l-- > 0), all bytes processed */
+
+ avRandomize();
+}
+#endif /* PPPOS_SUPPORT */
+
+#if PPPOE_SUPPORT
+void
+pppInProcOverEthernet(int pd, struct pbuf *pb)
+{
+ struct pppInputHeader *pih;
+ u16_t inProtocol;
+
+ if(pb->len < sizeof(inProtocol)) {
+ PPPDEBUG(LOG_ERR, ("pppInProcOverEthernet: too small for protocol field\n"));
+ goto drop;
+ }
+
+ inProtocol = (((u8_t *)pb->payload)[0] << 8) | ((u8_t*)pb->payload)[1];
+
+ /* make room for pppInputHeader - should not fail */
+ if (pbuf_header(pb, sizeof(*pih) - sizeof(inProtocol)) != 0) {
+ PPPDEBUG(LOG_ERR, ("pppInProcOverEthernet: could not allocate room for header\n"));
+ goto drop;
+ }
+
+ pih = pb->payload;
+
+ pih->unit = pd;
+ pih->proto = inProtocol;
+
+ /* Dispatch the packet thereby consuming it. */
+ pppInput(pb);
+ return;
+
+drop:
+ LINK_STATS_INC(link.drop);
+ snmp_inc_ifindiscards(&pppControl[pd].netif);
+ pbuf_free(pb);
+ return;
+}
+#endif /* PPPOE_SUPPORT */
+
+#if LWIP_NETIF_STATUS_CALLBACK
+/** Set the status callback of a PPP's netif
+ *
+ * @param pd The PPP descriptor returned by pppOpen()
+ * @param status_callback pointer to the status callback function
+ *
+ * @see netif_set_status_callback
+ */
+void
+ppp_set_netif_statuscallback(int pd, netif_status_callback_fn status_callback)
+{
+ netif_set_status_callback(&pppControl[pd].netif, status_callback);
+}
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+
+#if LWIP_NETIF_LINK_CALLBACK
+/** Set the link callback of a PPP's netif
+ *
+ * @param pd The PPP descriptor returned by pppOpen()
+ * @param link_callback pointer to the link callback function
+ *
+ * @see netif_set_link_callback
+ */
+void
+ppp_set_netif_linkcallback(int pd, netif_status_callback_fn link_callback)
+{
+ netif_set_link_callback(&pppControl[pd].netif, link_callback);
+}
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+#endif /* PPP_SUPPORT */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ppp.h b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ppp.h
new file mode 100644
index 00000000..08d6e62d
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ppp.h
@@ -0,0 +1,201 @@
+/*****************************************************************************
+* ppp.h - Network Point to Point Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-11-05 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original derived from BSD codes.
+*****************************************************************************/
+
+#ifndef PPP_H
+#define PPP_H
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "lwip/def.h"
+#include "lwip/sio.h"
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/netif.h"
+#include "lwip/sys.h"
+#include "lwip/timers.h"
+
+
+#ifndef __u_char_defined
+
+/* Type definitions for BSD code. */
+typedef unsigned long u_long;
+typedef unsigned int u_int;
+typedef unsigned short u_short;
+typedef unsigned char u_char;
+
+#endif
+
+
+/*************************
+*** PUBLIC DEFINITIONS ***
+*************************/
+
+/* Error codes. */
+#define PPPERR_NONE 0 /* No error. */
+#define PPPERR_PARAM -1 /* Invalid parameter. */
+#define PPPERR_OPEN -2 /* Unable to open PPP session. */
+#define PPPERR_DEVICE -3 /* Invalid I/O device for PPP. */
+#define PPPERR_ALLOC -4 /* Unable to allocate resources. */
+#define PPPERR_USER -5 /* User interrupt. */
+#define PPPERR_CONNECT -6 /* Connection lost. */
+#define PPPERR_AUTHFAIL -7 /* Failed authentication challenge. */
+#define PPPERR_PROTOCOL -8 /* Failed to meet protocol. */
+
+/*
+ * PPP IOCTL commands.
+ */
+/*
+ * Get the up status - 0 for down, non-zero for up. The argument must
+ * point to an int.
+ */
+#define PPPCTLG_UPSTATUS 100 /* Get the up status - 0 down else up */
+#define PPPCTLS_ERRCODE 101 /* Set the error code */
+#define PPPCTLG_ERRCODE 102 /* Get the error code */
+#define PPPCTLG_FD 103 /* Get the fd associated with the ppp */
+
+/************************
+*** PUBLIC DATA TYPES ***
+************************/
+
+struct ppp_addrs {
+ ip_addr_t our_ipaddr, his_ipaddr, netmask, dns1, dns2;
+};
+
+
+/***********************
+*** PUBLIC FUNCTIONS ***
+***********************/
+
+/* Initialize the PPP subsystem. */
+void pppInit(void);
+
+/* Warning: Using PPPAUTHTYPE_ANY might have security consequences.
+ * RFC 1994 says:
+ *
+ * In practice, within or associated with each PPP server, there is a
+ * database which associates "user" names with authentication
+ * information ("secrets"). It is not anticipated that a particular
+ * named user would be authenticated by multiple methods. This would
+ * make the user vulnerable to attacks which negotiate the least secure
+ * method from among a set (such as PAP rather than CHAP). If the same
+ * secret was used, PAP would reveal the secret to be used later with
+ * CHAP.
+ *
+ * Instead, for each user name there should be an indication of exactly
+ * one method used to authenticate that user name. If a user needs to
+ * make use of different authentication methods under different
+ * circumstances, then distinct user names SHOULD be employed, each of
+ * which identifies exactly one authentication method.
+ *
+ */
+enum pppAuthType {
+ PPPAUTHTYPE_NONE,
+ PPPAUTHTYPE_ANY,
+ PPPAUTHTYPE_PAP,
+ PPPAUTHTYPE_CHAP
+};
+
+void pppSetAuth(enum pppAuthType authType, const char *user, const char *passwd);
+
+/* Link status callback function prototype */
+typedef void (*pppLinkStatusCB_fn)(void *ctx, int errCode, void *arg);
+
+#if PPPOS_SUPPORT
+/*
+ * Open a new PPP connection using the given serial I/O device.
+ * This initializes the PPP control block but does not
+ * attempt to negotiate the LCP session.
+ * Return a new PPP connection descriptor on success or
+ * an error code (negative) on failure.
+ */
+int pppOverSerialOpen(sio_fd_t fd, pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx);
+#endif /* PPPOS_SUPPORT */
+
+#if PPPOE_SUPPORT
+/*
+ * Open a new PPP Over Ethernet (PPPOE) connection.
+ */
+int pppOverEthernetOpen(struct netif *ethif, const char *service_name, const char *concentrator_name,
+ pppLinkStatusCB_fn linkStatusCB, void *linkStatusCtx);
+#endif /* PPPOE_SUPPORT */
+
+/* for source code compatibility */
+#define pppOpen(fd,cb,ls) pppOverSerialOpen(fd,cb,ls)
+
+/*
+ * Close a PPP connection and release the descriptor.
+ * Any outstanding packets in the queues are dropped.
+ * Return 0 on success, an error code on failure.
+ */
+int pppClose(int pd);
+
+/*
+ * Indicate to the PPP process that the line has disconnected.
+ */
+void pppSigHUP(int pd);
+
+/*
+ * Get and set parameters for the given connection.
+ * Return 0 on success, an error code on failure.
+ */
+int pppIOCtl(int pd, int cmd, void *arg);
+
+/*
+ * Return the Maximum Transmission Unit for the given PPP connection.
+ */
+u_short pppMTU(int pd);
+
+#if PPPOS_SUPPORT && !PPP_INPROC_OWNTHREAD
+/*
+ * PPP over Serial: this is the input function to be called for received data.
+ * If PPP_INPROC_OWNTHREAD==1, a seperate input thread using the blocking
+ * sio_read() is used, so this is deactivated.
+ */
+void pppos_input(int pd, u_char* data, int len);
+#endif /* PPPOS_SUPPORT && !PPP_INPROC_OWNTHREAD */
+
+
+#if LWIP_NETIF_STATUS_CALLBACK
+/* Set an lwIP-style status-callback for the selected PPP device */
+void ppp_set_netif_statuscallback(int pd, netif_status_callback_fn status_callback);
+#endif /* LWIP_NETIF_STATUS_CALLBACK */
+#if LWIP_NETIF_LINK_CALLBACK
+/* Set an lwIP-style link-callback for the selected PPP device */
+void ppp_set_netif_linkcallback(int pd, netif_status_callback_fn link_callback);
+#endif /* LWIP_NETIF_LINK_CALLBACK */
+
+#endif /* PPP_SUPPORT */
+
+#endif /* PPP_H */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ppp_impl.h b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ppp_impl.h
new file mode 100644
index 00000000..89aea60b
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ppp_impl.h
@@ -0,0 +1,363 @@
+/*****************************************************************************
+* ppp.h - Network Point to Point Protocol header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1997 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 97-11-05 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Original derived from BSD codes.
+*****************************************************************************/
+
+#ifndef PPP_IMPL_H
+#define PPP_IMPL_H
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp.h"
+#include "lwip/def.h"
+#include "lwip/sio.h"
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/netif.h"
+#include "lwip/sys.h"
+#include "lwip/timers.h"
+
+/** Some defines for code we skip compared to the original pppd.
+ * These are just here to minimise the use of the ugly "#if 0". */
+#define PPP_ADDITIONAL_CALLBACKS 0
+
+/** Some error checks to test for unsupported code */
+#if CBCP_SUPPORT
+#error "CBCP is not supported in lwIP PPP"
+#endif
+#if CCP_SUPPORT
+#error "CCP is not supported in lwIP PPP"
+#endif
+
+/*
+ * pppd.h - PPP daemon global declarations.
+ *
+ * Copyright (c) 1989 Carnegie Mellon University.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by Carnegie Mellon University. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ */
+/*
+ * ppp_defs.h - PPP definitions.
+ *
+ * Copyright (c) 1994 The Australian National University.
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify, and distribute this software and its
+ * documentation is hereby granted, provided that the above copyright
+ * notice appears in all copies. This software is provided without any
+ * warranty, express or implied. The Australian National University
+ * makes no representations about the suitability of this software for
+ * any purpose.
+ *
+ * IN NO EVENT SHALL THE AUSTRALIAN NATIONAL UNIVERSITY BE LIABLE TO ANY
+ * PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
+ * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF
+ * THE AUSTRALIAN NATIONAL UNIVERSITY HAVE BEEN ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * THE AUSTRALIAN NATIONAL UNIVERSITY SPECIFICALLY DISCLAIMS ANY WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
+ * ON AN "AS IS" BASIS, AND THE AUSTRALIAN NATIONAL UNIVERSITY HAS NO
+ * OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS,
+ * OR MODIFICATIONS.
+ */
+
+#define TIMEOUT(f, a, t) do { sys_untimeout((f), (a)); sys_timeout((t)*1000, (f), (a)); } while(0)
+#define UNTIMEOUT(f, a) sys_untimeout((f), (a))
+
+
+/*
+ * Constants and structures defined by the internet system,
+ * Per RFC 790, September 1981, and numerous additions.
+ */
+
+/*
+ * The basic PPP frame.
+ */
+#define PPP_HDRLEN 4 /* octets for standard ppp header */
+#define PPP_FCSLEN 2 /* octets for FCS */
+
+
+/*
+ * Significant octet values.
+ */
+#define PPP_ALLSTATIONS 0xff /* All-Stations broadcast address */
+#define PPP_UI 0x03 /* Unnumbered Information */
+#define PPP_FLAG 0x7e /* Flag Sequence */
+#define PPP_ESCAPE 0x7d /* Asynchronous Control Escape */
+#define PPP_TRANS 0x20 /* Asynchronous transparency modifier */
+
+/*
+ * Protocol field values.
+ */
+#define PPP_IP 0x21 /* Internet Protocol */
+#define PPP_AT 0x29 /* AppleTalk Protocol */
+#define PPP_VJC_COMP 0x2d /* VJ compressed TCP */
+#define PPP_VJC_UNCOMP 0x2f /* VJ uncompressed TCP */
+#define PPP_COMP 0xfd /* compressed packet */
+#define PPP_IPCP 0x8021 /* IP Control Protocol */
+#define PPP_ATCP 0x8029 /* AppleTalk Control Protocol */
+#define PPP_CCP 0x80fd /* Compression Control Protocol */
+#define PPP_LCP 0xc021 /* Link Control Protocol */
+#define PPP_PAP 0xc023 /* Password Authentication Protocol */
+#define PPP_LQR 0xc025 /* Link Quality Report protocol */
+#define PPP_CHAP 0xc223 /* Cryptographic Handshake Auth. Protocol */
+#define PPP_CBCP 0xc029 /* Callback Control Protocol */
+
+/*
+ * Values for FCS calculations.
+ */
+#define PPP_INITFCS 0xffff /* Initial FCS value */
+#define PPP_GOODFCS 0xf0b8 /* Good final FCS value */
+#define PPP_FCS(fcs, c) (((fcs) >> 8) ^ fcstab[((fcs) ^ (c)) & 0xff])
+
+/*
+ * Extended asyncmap - allows any character to be escaped.
+ */
+typedef u_char ext_accm[32];
+
+/*
+ * What to do with network protocol (NP) packets.
+ */
+enum NPmode {
+ NPMODE_PASS, /* pass the packet through */
+ NPMODE_DROP, /* silently drop the packet */
+ NPMODE_ERROR, /* return an error */
+ NPMODE_QUEUE /* save it up for later. */
+};
+
+/*
+ * Inline versions of get/put char/short/long.
+ * Pointer is advanced; we assume that both arguments
+ * are lvalues and will already be in registers.
+ * cp MUST be u_char *.
+ */
+#define GETCHAR(c, cp) { \
+ (c) = *(cp)++; \
+}
+#define PUTCHAR(c, cp) { \
+ *(cp)++ = (u_char) (c); \
+}
+
+
+#define GETSHORT(s, cp) { \
+ (s) = *(cp); (cp)++; (s) <<= 8; \
+ (s) |= *(cp); (cp)++; \
+}
+#define PUTSHORT(s, cp) { \
+ *(cp)++ = (u_char) ((s) >> 8); \
+ *(cp)++ = (u_char) (s & 0xff); \
+}
+
+#define GETLONG(l, cp) { \
+ (l) = *(cp); (cp)++; (l) <<= 8; \
+ (l) |= *(cp); (cp)++; (l) <<= 8; \
+ (l) |= *(cp); (cp)++; (l) <<= 8; \
+ (l) |= *(cp); (cp)++; \
+}
+#define PUTLONG(l, cp) { \
+ *(cp)++ = (u_char) ((l) >> 24); \
+ *(cp)++ = (u_char) ((l) >> 16); \
+ *(cp)++ = (u_char) ((l) >> 8); \
+ *(cp)++ = (u_char) (l); \
+}
+
+
+#define INCPTR(n, cp) ((cp) += (n))
+#define DECPTR(n, cp) ((cp) -= (n))
+
+#define BCMP(s0, s1, l) memcmp((u_char *)(s0), (u_char *)(s1), (l))
+#define BCOPY(s, d, l) MEMCPY((d), (s), (l))
+#define BZERO(s, n) memset(s, 0, n)
+
+#if PPP_DEBUG
+#define PRINTMSG(m, l) { m[l] = '\0'; LWIP_DEBUGF(LOG_INFO, ("Remote message: %s\n", m)); }
+#else /* PPP_DEBUG */
+#define PRINTMSG(m, l)
+#endif /* PPP_DEBUG */
+
+/*
+ * MAKEHEADER - Add PPP Header fields to a packet.
+ */
+#define MAKEHEADER(p, t) { \
+ PUTCHAR(PPP_ALLSTATIONS, p); \
+ PUTCHAR(PPP_UI, p); \
+ PUTSHORT(t, p); }
+
+/************************
+*** PUBLIC DATA TYPES ***
+************************/
+
+/*
+ * The following struct gives the addresses of procedures to call
+ * for a particular protocol.
+ */
+struct protent {
+ u_short protocol; /* PPP protocol number */
+ /* Initialization procedure */
+ void (*init) (int unit);
+ /* Process a received packet */
+ void (*input) (int unit, u_char *pkt, int len);
+ /* Process a received protocol-reject */
+ void (*protrej) (int unit);
+ /* Lower layer has come up */
+ void (*lowerup) (int unit);
+ /* Lower layer has gone down */
+ void (*lowerdown) (int unit);
+ /* Open the protocol */
+ void (*open) (int unit);
+ /* Close the protocol */
+ void (*close) (int unit, char *reason);
+#if PPP_ADDITIONAL_CALLBACKS
+ /* Print a packet in readable form */
+ int (*printpkt) (u_char *pkt, int len,
+ void (*printer) (void *, char *, ...),
+ void *arg);
+ /* Process a received data packet */
+ void (*datainput) (int unit, u_char *pkt, int len);
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+ int enabled_flag; /* 0 if protocol is disabled */
+ char *name; /* Text name of protocol */
+#if PPP_ADDITIONAL_CALLBACKS
+ /* Check requested options, assign defaults */
+ void (*check_options) (u_long);
+ /* Configure interface for demand-dial */
+ int (*demand_conf) (int unit);
+ /* Say whether to bring up link for this pkt */
+ int (*active_pkt) (u_char *pkt, int len);
+#endif /* PPP_ADDITIONAL_CALLBACKS */
+};
+
+/*
+ * The following structure records the time in seconds since
+ * the last NP packet was sent or received.
+ */
+struct ppp_idle {
+ u_short xmit_idle; /* seconds since last NP packet sent */
+ u_short recv_idle; /* seconds since last NP packet received */
+};
+
+struct ppp_settings {
+
+ u_int disable_defaultip : 1; /* Don't use hostname for default IP addrs */
+ u_int auth_required : 1; /* Peer is required to authenticate */
+ u_int explicit_remote : 1; /* remote_name specified with remotename opt */
+ u_int refuse_pap : 1; /* Don't wanna auth. ourselves with PAP */
+ u_int refuse_chap : 1; /* Don't wanna auth. ourselves with CHAP */
+ u_int usehostname : 1; /* Use hostname for our_name */
+ u_int usepeerdns : 1; /* Ask peer for DNS adds */
+
+ u_short idle_time_limit; /* Shut down link if idle for this long */
+ int maxconnect; /* Maximum connect time (seconds) */
+
+ char user [MAXNAMELEN + 1]; /* Username for PAP */
+ char passwd [MAXSECRETLEN + 1]; /* Password for PAP, secret for CHAP */
+ char our_name [MAXNAMELEN + 1]; /* Our name for authentication purposes */
+ char remote_name[MAXNAMELEN + 1]; /* Peer's name for authentication */
+};
+
+/*****************************
+*** PUBLIC DATA STRUCTURES ***
+*****************************/
+
+/* Buffers for outgoing packets. */
+extern u_char outpacket_buf[NUM_PPP][PPP_MRU+PPP_HDRLEN];
+
+extern struct ppp_settings ppp_settings;
+
+extern struct protent *ppp_protocols[]; /* Table of pointers to supported protocols */
+
+
+/***********************
+*** PUBLIC FUNCTIONS ***
+***********************/
+
+/*
+ * Write n characters to a ppp link.
+ * RETURN: >= 0 Number of characters written, -1 Failed to write to device.
+ */
+int pppWrite(int pd, const u_char *s, int n);
+
+void pppInProcOverEthernet(int pd, struct pbuf *pb);
+
+struct pbuf *pppSingleBuf(struct pbuf *p);
+
+void pppLinkTerminated(int pd);
+
+void pppLinkDown(int pd);
+
+/* Configure i/f transmit parameters */
+void ppp_send_config (int, u16_t, u32_t, int, int);
+/* Set extended transmit ACCM */
+void ppp_set_xaccm (int, ext_accm *);
+/* Configure i/f receive parameters */
+void ppp_recv_config (int, int, u32_t, int, int);
+/* Find out how long link has been idle */
+int get_idle_time (int, struct ppp_idle *);
+
+/* Configure VJ TCP header compression */
+int sifvjcomp (int, int, u8_t, u8_t);
+/* Configure i/f down (for IP) */
+int sifup (int);
+/* Set mode for handling packets for proto */
+int sifnpmode (int u, int proto, enum NPmode mode);
+/* Configure i/f down (for IP) */
+int sifdown (int);
+/* Configure IP addresses for i/f */
+int sifaddr (int, u32_t, u32_t, u32_t, u32_t, u32_t);
+/* Reset i/f IP addresses */
+int cifaddr (int, u32_t, u32_t);
+/* Create default route through i/f */
+int sifdefaultroute (int, u32_t, u32_t);
+/* Delete default route through i/f */
+int cifdefaultroute (int, u32_t, u32_t);
+
+/* Get appropriate netmask for address */
+u32_t GetMask (u32_t);
+
+#endif /* PPP_SUPPORT */
+
+#endif /* PPP_IMPL_H */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ppp_oe.c b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ppp_oe.c
new file mode 100644
index 00000000..fdf52ae2
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/ppp_oe.c
@@ -0,0 +1,1132 @@
+/*****************************************************************************
+* ppp_oe.c - PPP Over Ethernet implementation for lwIP.
+*
+* Copyright (c) 2006 by Marc Boucher, Services Informatiques (MBSI) inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 06-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+*****************************************************************************/
+
+
+
+/* based on NetBSD: if_pppoe.c,v 1.64 2006/01/31 23:50:15 martin Exp */
+
+/*-
+ * Copyright (c) 2002 The NetBSD Foundation, Inc.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to The NetBSD Foundation
+ * by Martin Husemann <martin@NetBSD.org>.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the NetBSD
+ * Foundation, Inc. and its contributors.
+ * 4. Neither the name of The NetBSD Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
+ * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+ * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
+ * POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "lwip/opt.h"
+
+#if PPPOE_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "netif/ppp_oe.h"
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "lwip/timers.h"
+#include "lwip/memp.h"
+
+#include <string.h>
+#include <stdio.h>
+
+
+/* Add a 16 bit unsigned value to a buffer pointed to by PTR */
+#define PPPOE_ADD_16(PTR, VAL) \
+ *(PTR)++ = (u8_t)((VAL) / 256); \
+ *(PTR)++ = (u8_t)((VAL) % 256)
+
+/* Add a complete PPPoE header to the buffer pointed to by PTR */
+#define PPPOE_ADD_HEADER(PTR, CODE, SESS, LEN) \
+ *(PTR)++ = PPPOE_VERTYPE; \
+ *(PTR)++ = (CODE); \
+ PPPOE_ADD_16(PTR, SESS); \
+ PPPOE_ADD_16(PTR, LEN)
+
+#define PPPOE_DISC_TIMEOUT (5*1000) /* base for quick timeout calculation */
+#define PPPOE_SLOW_RETRY (60*1000) /* persistent retry interval */
+#define PPPOE_DISC_MAXPADI 4 /* retry PADI four times (quickly) */
+#define PPPOE_DISC_MAXPADR 2 /* retry PADR twice */
+
+#ifdef PPPOE_SERVER
+#error "PPPOE_SERVER is not yet supported under lwIP!"
+/* from if_spppsubr.c */
+#define IFF_PASSIVE IFF_LINK0 /* wait passively for connection */
+#endif
+
+#ifndef PPPOE_ERRORSTRING_LEN
+#define PPPOE_ERRORSTRING_LEN 64
+#endif
+static char pppoe_error_tmp[PPPOE_ERRORSTRING_LEN];
+
+
+/* input routines */
+static void pppoe_dispatch_disc_pkt(struct netif *, struct pbuf *);
+
+/* management routines */
+static int pppoe_do_disconnect(struct pppoe_softc *);
+static void pppoe_abort_connect(struct pppoe_softc *);
+static void pppoe_clear_softc(struct pppoe_softc *, const char *);
+
+/* internal timeout handling */
+static void pppoe_timeout(void *);
+
+/* sending actual protocol controll packets */
+static err_t pppoe_send_padi(struct pppoe_softc *);
+static err_t pppoe_send_padr(struct pppoe_softc *);
+#ifdef PPPOE_SERVER
+static err_t pppoe_send_pado(struct pppoe_softc *);
+static err_t pppoe_send_pads(struct pppoe_softc *);
+#endif
+static err_t pppoe_send_padt(struct netif *, u_int, const u8_t *);
+
+/* internal helper functions */
+static struct pppoe_softc * pppoe_find_softc_by_session(u_int, struct netif *);
+static struct pppoe_softc * pppoe_find_softc_by_hunique(u8_t *, size_t, struct netif *);
+
+/** linked list of created pppoe interfaces */
+static struct pppoe_softc *pppoe_softc_list;
+
+err_t
+pppoe_create(struct netif *ethif, int pd, void (*linkStatusCB)(int pd, int up), struct pppoe_softc **scptr)
+{
+ struct pppoe_softc *sc;
+
+ sc = (struct pppoe_softc *)memp_malloc(MEMP_PPPOE_IF);
+ if (sc == NULL) {
+ *scptr = NULL;
+ return ERR_MEM;
+ }
+ memset(sc, 0, sizeof(struct pppoe_softc));
+
+ /* changed to real address later */
+ MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+
+ sc->sc_pd = pd;
+ sc->sc_linkStatusCB = linkStatusCB;
+ sc->sc_ethif = ethif;
+
+ /* put the new interface at the head of the list */
+ sc->next = pppoe_softc_list;
+ pppoe_softc_list = sc;
+
+ *scptr = sc;
+
+ return ERR_OK;
+}
+
+err_t
+pppoe_destroy(struct netif *ifp)
+{
+ struct pppoe_softc *sc, *prev = NULL;
+
+ for (sc = pppoe_softc_list; sc != NULL; prev = sc, sc = sc->next) {
+ if (sc->sc_ethif == ifp) {
+ break;
+ }
+ }
+
+ if(!(sc && (sc->sc_ethif == ifp))) {
+ return ERR_IF;
+ }
+
+ sys_untimeout(pppoe_timeout, sc);
+ if (prev == NULL) {
+ /* remove sc from the head of the list */
+ pppoe_softc_list = sc->next;
+ } else {
+ /* remove sc from the list */
+ prev->next = sc->next;
+ }
+
+#ifdef PPPOE_TODO
+ if (sc->sc_concentrator_name) {
+ mem_free(sc->sc_concentrator_name);
+ }
+ if (sc->sc_service_name) {
+ mem_free(sc->sc_service_name);
+ }
+#endif /* PPPOE_TODO */
+ memp_free(MEMP_PPPOE_IF, sc);
+
+ return ERR_OK;
+}
+
+/*
+ * Find the interface handling the specified session.
+ * Note: O(number of sessions open), this is a client-side only, mean
+ * and lean implementation, so number of open sessions typically should
+ * be 1.
+ */
+static struct pppoe_softc *
+pppoe_find_softc_by_session(u_int session, struct netif *rcvif)
+{
+ struct pppoe_softc *sc;
+
+ if (session == 0) {
+ return NULL;
+ }
+
+ for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) {
+ if (sc->sc_state == PPPOE_STATE_SESSION
+ && sc->sc_session == session) {
+ if (sc->sc_ethif == rcvif) {
+ return sc;
+ } else {
+ return NULL;
+ }
+ }
+ }
+ return NULL;
+}
+
+/* Check host unique token passed and return appropriate softc pointer,
+ * or NULL if token is bogus. */
+static struct pppoe_softc *
+pppoe_find_softc_by_hunique(u8_t *token, size_t len, struct netif *rcvif)
+{
+ struct pppoe_softc *sc, *t;
+
+ if (pppoe_softc_list == NULL) {
+ return NULL;
+ }
+
+ if (len != sizeof sc) {
+ return NULL;
+ }
+ MEMCPY(&t, token, len);
+
+ for (sc = pppoe_softc_list; sc != NULL; sc = sc->next) {
+ if (sc == t) {
+ break;
+ }
+ }
+
+ if (sc == NULL) {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: alien host unique tag, no session found\n"));
+ return NULL;
+ }
+
+ /* should be safe to access *sc now */
+ if (sc->sc_state < PPPOE_STATE_PADI_SENT || sc->sc_state >= PPPOE_STATE_SESSION) {
+ printf("%c%c%"U16_F": host unique tag found, but it belongs to a connection in state %d\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_state);
+ return NULL;
+ }
+ if (sc->sc_ethif != rcvif) {
+ printf("%c%c%"U16_F": wrong interface, not accepting host unique\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ return NULL;
+ }
+ return sc;
+}
+
+static void
+pppoe_linkstatus_up(struct pppoe_softc *sc)
+{
+ sc->sc_linkStatusCB(sc->sc_pd, 1);
+}
+
+/* analyze and handle a single received packet while not in session state */
+static void
+pppoe_dispatch_disc_pkt(struct netif *netif, struct pbuf *pb)
+{
+ u16_t tag, len;
+ u16_t session, plen;
+ struct pppoe_softc *sc;
+ const char *err_msg;
+ char devname[6];
+ u8_t *ac_cookie;
+ u16_t ac_cookie_len;
+#ifdef PPPOE_SERVER
+ u8_t *hunique;
+ size_t hunique_len;
+#endif
+ struct pppoehdr *ph;
+ struct pppoetag pt;
+ int off, err, errortag;
+ struct eth_hdr *ethhdr;
+
+ pb = pppSingleBuf(pb);
+
+ strcpy(devname, "pppoe"); /* as long as we don't know which instance */
+ err_msg = NULL;
+ errortag = 0;
+ if (pb->len < sizeof(*ethhdr)) {
+ goto done;
+ }
+ ethhdr = (struct eth_hdr *)pb->payload;
+ off = sizeof(*ethhdr);
+
+ ac_cookie = NULL;
+ ac_cookie_len = 0;
+#ifdef PPPOE_SERVER
+ hunique = NULL;
+ hunique_len = 0;
+#endif
+ session = 0;
+ if (pb->len - off < PPPOE_HEADERLEN) {
+ printf("pppoe: packet too short: %d\n", pb->len);
+ goto done;
+ }
+
+ ph = (struct pppoehdr *) (ethhdr + 1);
+ if (ph->vertype != PPPOE_VERTYPE) {
+ printf("pppoe: unknown version/type packet: 0x%x\n", ph->vertype);
+ goto done;
+ }
+ session = ntohs(ph->session);
+ plen = ntohs(ph->plen);
+ off += sizeof(*ph);
+
+ if (plen + off > pb->len) {
+ printf("pppoe: packet content does not fit: data available = %d, packet size = %u\n",
+ pb->len - off, plen);
+ goto done;
+ }
+ if(pb->tot_len == pb->len) {
+ pb->tot_len = pb->len = (u16_t)off + plen; /* ignore trailing garbage */
+ }
+ tag = 0;
+ len = 0;
+ sc = NULL;
+ while (off + sizeof(pt) <= pb->len) {
+ MEMCPY(&pt, (u8_t*)pb->payload + off, sizeof(pt));
+ tag = ntohs(pt.tag);
+ len = ntohs(pt.len);
+ if (off + sizeof(pt) + len > pb->len) {
+ printf("pppoe: tag 0x%x len 0x%x is too long\n", tag, len);
+ goto done;
+ }
+ switch (tag) {
+ case PPPOE_TAG_EOL:
+ goto breakbreak;
+ case PPPOE_TAG_SNAME:
+ break; /* ignored */
+ case PPPOE_TAG_ACNAME:
+ break; /* ignored */
+ case PPPOE_TAG_HUNIQUE:
+ if (sc != NULL) {
+ break;
+ }
+#ifdef PPPOE_SERVER
+ hunique = (u8_t*)pb->payload + off + sizeof(pt);
+ hunique_len = len;
+#endif
+ sc = pppoe_find_softc_by_hunique((u8_t*)pb->payload + off + sizeof(pt), len, netif);
+ if (sc != NULL) {
+ snprintf(devname, sizeof(devname), "%c%c%"U16_F, sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ }
+ break;
+ case PPPOE_TAG_ACCOOKIE:
+ if (ac_cookie == NULL) {
+ ac_cookie = (u8_t*)pb->payload + off + sizeof(pt);
+ ac_cookie_len = len;
+ }
+ break;
+ case PPPOE_TAG_SNAME_ERR:
+ err_msg = "SERVICE NAME ERROR";
+ errortag = 1;
+ break;
+ case PPPOE_TAG_ACSYS_ERR:
+ err_msg = "AC SYSTEM ERROR";
+ errortag = 1;
+ break;
+ case PPPOE_TAG_GENERIC_ERR:
+ err_msg = "GENERIC ERROR";
+ errortag = 1;
+ break;
+ }
+ if (err_msg) {
+ if (errortag && len) {
+ u16_t error_len = LWIP_MIN(len, sizeof(pppoe_error_tmp)-1);
+ strncpy(pppoe_error_tmp, (char*)pb->payload + off + sizeof(pt), error_len);
+ pppoe_error_tmp[error_len-1] = '\0';
+ printf("%s: %s: %s\n", devname, err_msg, pppoe_error_tmp);
+ } else {
+ printf("%s: %s\n", devname, err_msg);
+ }
+ if (errortag) {
+ goto done;
+ }
+ }
+ off += sizeof(pt) + len;
+ }
+
+breakbreak:;
+ switch (ph->code) {
+ case PPPOE_CODE_PADI:
+#ifdef PPPOE_SERVER
+ /*
+ * got service name, concentrator name, and/or host unique.
+ * ignore if we have no interfaces with IFF_PASSIVE|IFF_UP.
+ */
+ if (LIST_EMPTY(&pppoe_softc_list)) {
+ goto done;
+ }
+ LIST_FOREACH(sc, &pppoe_softc_list, sc_list) {
+ if (!(sc->sc_sppp.pp_if.if_flags & IFF_UP)) {
+ continue;
+ }
+ if (!(sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) {
+ continue;
+ }
+ if (sc->sc_state == PPPOE_STATE_INITIAL) {
+ break;
+ }
+ }
+ if (sc == NULL) {
+ /* printf("pppoe: free passive interface is not found\n"); */
+ goto done;
+ }
+ if (hunique) {
+ if (sc->sc_hunique) {
+ mem_free(sc->sc_hunique);
+ }
+ sc->sc_hunique = mem_malloc(hunique_len);
+ if (sc->sc_hunique == NULL) {
+ goto done;
+ }
+ sc->sc_hunique_len = hunique_len;
+ MEMCPY(sc->sc_hunique, hunique, hunique_len);
+ }
+ MEMCPY(&sc->sc_dest, eh->ether_shost, sizeof sc->sc_dest);
+ sc->sc_state = PPPOE_STATE_PADO_SENT;
+ pppoe_send_pado(sc);
+ break;
+#endif /* PPPOE_SERVER */
+ case PPPOE_CODE_PADR:
+#ifdef PPPOE_SERVER
+ /*
+ * get sc from ac_cookie if IFF_PASSIVE
+ */
+ if (ac_cookie == NULL) {
+ /* be quiet if there is not a single pppoe instance */
+ printf("pppoe: received PADR but not includes ac_cookie\n");
+ goto done;
+ }
+ sc = pppoe_find_softc_by_hunique(ac_cookie, ac_cookie_len, netif);
+ if (sc == NULL) {
+ /* be quiet if there is not a single pppoe instance */
+ if (!LIST_EMPTY(&pppoe_softc_list)) {
+ printf("pppoe: received PADR but could not find request for it\n");
+ }
+ goto done;
+ }
+ if (sc->sc_state != PPPOE_STATE_PADO_SENT) {
+ printf("%c%c%"U16_F": received unexpected PADR\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ goto done;
+ }
+ if (hunique) {
+ if (sc->sc_hunique) {
+ mem_free(sc->sc_hunique);
+ }
+ sc->sc_hunique = mem_malloc(hunique_len);
+ if (sc->sc_hunique == NULL) {
+ goto done;
+ }
+ sc->sc_hunique_len = hunique_len;
+ MEMCPY(sc->sc_hunique, hunique, hunique_len);
+ }
+ pppoe_send_pads(sc);
+ sc->sc_state = PPPOE_STATE_SESSION;
+ pppoe_linkstatus_up(sc); /* notify upper layers */
+ break;
+#else
+ /* ignore, we are no access concentrator */
+ goto done;
+#endif /* PPPOE_SERVER */
+ case PPPOE_CODE_PADO:
+ if (sc == NULL) {
+ /* be quiet if there is not a single pppoe instance */
+ if (pppoe_softc_list != NULL) {
+ printf("pppoe: received PADO but could not find request for it\n");
+ }
+ goto done;
+ }
+ if (sc->sc_state != PPPOE_STATE_PADI_SENT) {
+ printf("%c%c%"U16_F": received unexpected PADO\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ goto done;
+ }
+ if (ac_cookie) {
+ sc->sc_ac_cookie_len = ac_cookie_len;
+ MEMCPY(sc->sc_ac_cookie, ac_cookie, ac_cookie_len);
+ }
+ MEMCPY(&sc->sc_dest, ethhdr->src.addr, sizeof(sc->sc_dest.addr));
+ sys_untimeout(pppoe_timeout, sc);
+ sc->sc_padr_retried = 0;
+ sc->sc_state = PPPOE_STATE_PADR_SENT;
+ if ((err = pppoe_send_padr(sc)) != 0) {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+ }
+ sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc);
+ break;
+ case PPPOE_CODE_PADS:
+ if (sc == NULL) {
+ goto done;
+ }
+ sc->sc_session = session;
+ sys_untimeout(pppoe_timeout, sc);
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x connected\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, session));
+ sc->sc_state = PPPOE_STATE_SESSION;
+ pppoe_linkstatus_up(sc); /* notify upper layers */
+ break;
+ case PPPOE_CODE_PADT:
+ if (sc == NULL) {
+ goto done;
+ }
+ pppoe_clear_softc(sc, "received PADT");
+ break;
+ default:
+ if(sc) {
+ printf("%c%c%"U16_F": unknown code (0x%"X16_F") session = 0x%"X16_F"\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num,
+ (u16_t)ph->code, session);
+ } else {
+ printf("pppoe: unknown code (0x%"X16_F") session = 0x%"X16_F"\n", (u16_t)ph->code, session);
+ }
+ break;
+ }
+
+done:
+ pbuf_free(pb);
+ return;
+}
+
+void
+pppoe_disc_input(struct netif *netif, struct pbuf *p)
+{
+ /* avoid error messages if there is not a single pppoe instance */
+ if (pppoe_softc_list != NULL) {
+ pppoe_dispatch_disc_pkt(netif, p);
+ } else {
+ pbuf_free(p);
+ }
+}
+
+void
+pppoe_data_input(struct netif *netif, struct pbuf *pb)
+{
+ u16_t session, plen;
+ struct pppoe_softc *sc;
+ struct pppoehdr *ph;
+#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
+ u8_t shost[ETHER_ADDR_LEN];
+#endif
+
+#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
+ MEMCPY(shost, ((struct eth_hdr *)pb->payload)->src.addr, sizeof(shost));
+#endif
+ if (pbuf_header(pb, -(int)sizeof(struct eth_hdr)) != 0) {
+ /* bail out */
+ PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header failed\n"));
+ LINK_STATS_INC(link.lenerr);
+ goto drop;
+ }
+
+ pb = pppSingleBuf (pb);
+
+ if (pb->len <= PPPOE_HEADERLEN) {
+ printf("pppoe (data): dropping too short packet: %d bytes\n", pb->len);
+ goto drop;
+ }
+
+ if (pb->len < sizeof(*ph)) {
+ printf("pppoe_data_input: could not get PPPoE header\n");
+ goto drop;
+ }
+ ph = (struct pppoehdr *)pb->payload;
+
+ if (ph->vertype != PPPOE_VERTYPE) {
+ printf("pppoe (data): unknown version/type packet: 0x%x\n", ph->vertype);
+ goto drop;
+ }
+ if (ph->code != 0) {
+ goto drop;
+ }
+
+ session = ntohs(ph->session);
+ sc = pppoe_find_softc_by_session(session, netif);
+ if (sc == NULL) {
+#ifdef PPPOE_TERM_UNKNOWN_SESSIONS
+ printf("pppoe: input for unknown session 0x%x, sending PADT\n", session);
+ pppoe_send_padt(netif, session, shost);
+#endif
+ goto drop;
+ }
+
+ plen = ntohs(ph->plen);
+
+ if (pbuf_header(pb, -(int)(PPPOE_HEADERLEN)) != 0) {
+ /* bail out */
+ PPPDEBUG(LOG_ERR, ("pppoe_data_input: pbuf_header PPPOE_HEADERLEN failed\n"));
+ LINK_STATS_INC(link.lenerr);
+ goto drop;
+ }
+
+ PPPDEBUG(LOG_DEBUG, ("pppoe_data_input: %c%c%"U16_F": pkthdr.len=%d, pppoe.len=%d\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num,
+ pb->len, plen));
+
+ if (pb->len < plen) {
+ goto drop;
+ }
+
+ pppInProcOverEthernet(sc->sc_pd, pb);
+
+ return;
+
+drop:
+ pbuf_free(pb);
+}
+
+static err_t
+pppoe_output(struct pppoe_softc *sc, struct pbuf *pb)
+{
+ struct eth_hdr *ethhdr;
+ u16_t etype;
+ err_t res;
+
+ if (!sc->sc_ethif) {
+ pbuf_free(pb);
+ return ERR_IF;
+ }
+
+ ethhdr = (struct eth_hdr *)pb->payload;
+ etype = sc->sc_state == PPPOE_STATE_SESSION ? ETHTYPE_PPPOE : ETHTYPE_PPPOEDISC;
+ ethhdr->type = htons(etype);
+ MEMCPY(ethhdr->dest.addr, sc->sc_dest.addr, sizeof(ethhdr->dest.addr));
+ MEMCPY(ethhdr->src.addr, ((struct eth_addr *)sc->sc_ethif->hwaddr)->addr, sizeof(ethhdr->src.addr));
+
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F" (%x) state=%d, session=0x%x output -> %02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F":%02"X16_F", len=%d\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, etype,
+ sc->sc_state, sc->sc_session,
+ sc->sc_dest.addr[0], sc->sc_dest.addr[1], sc->sc_dest.addr[2], sc->sc_dest.addr[3], sc->sc_dest.addr[4], sc->sc_dest.addr[5],
+ pb->tot_len));
+
+ res = sc->sc_ethif->linkoutput(sc->sc_ethif, pb);
+
+ pbuf_free(pb);
+
+ return res;
+}
+
+static err_t
+pppoe_send_padi(struct pppoe_softc *sc)
+{
+ struct pbuf *pb;
+ u8_t *p;
+ int len;
+#ifdef PPPOE_TODO
+ int l1 = 0, l2 = 0; /* XXX: gcc */
+#endif /* PPPOE_TODO */
+
+ if (sc->sc_state >PPPOE_STATE_PADI_SENT) {
+ PPPDEBUG(LOG_ERR, ("ERROR: pppoe_send_padi in state %d", sc->sc_state));
+ }
+
+ /* calculate length of frame (excluding ethernet header + pppoe header) */
+ len = 2 + 2 + 2 + 2 + sizeof sc; /* service name tag is required, host unique is send too */
+#ifdef PPPOE_TODO
+ if (sc->sc_service_name != NULL) {
+ l1 = (int)strlen(sc->sc_service_name);
+ len += l1;
+ }
+ if (sc->sc_concentrator_name != NULL) {
+ l2 = (int)strlen(sc->sc_concentrator_name);
+ len += 2 + 2 + l2;
+ }
+#endif /* PPPOE_TODO */
+ LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff",
+ sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff);
+
+ /* allocate a buffer */
+ pb = pbuf_alloc(PBUF_LINK, (u16_t)(sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len), PBUF_RAM);
+ if (!pb) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+ p = (u8_t*)pb->payload + sizeof (struct eth_hdr);
+ /* fill in pkt */
+ PPPOE_ADD_HEADER(p, PPPOE_CODE_PADI, 0, (u16_t)len);
+ PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
+#ifdef PPPOE_TODO
+ if (sc->sc_service_name != NULL) {
+ PPPOE_ADD_16(p, l1);
+ MEMCPY(p, sc->sc_service_name, l1);
+ p += l1;
+ } else
+#endif /* PPPOE_TODO */
+ {
+ PPPOE_ADD_16(p, 0);
+ }
+#ifdef PPPOE_TODO
+ if (sc->sc_concentrator_name != NULL) {
+ PPPOE_ADD_16(p, PPPOE_TAG_ACNAME);
+ PPPOE_ADD_16(p, l2);
+ MEMCPY(p, sc->sc_concentrator_name, l2);
+ p += l2;
+ }
+#endif /* PPPOE_TODO */
+ PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
+ PPPOE_ADD_16(p, sizeof(sc));
+ MEMCPY(p, &sc, sizeof sc);
+
+ /* send pkt */
+ return pppoe_output(sc, pb);
+}
+
+static void
+pppoe_timeout(void *arg)
+{
+ int retry_wait, err;
+ struct pppoe_softc *sc = (struct pppoe_softc*)arg;
+
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": timeout\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+
+ switch (sc->sc_state) {
+ case PPPOE_STATE_PADI_SENT:
+ /*
+ * We have two basic ways of retrying:
+ * - Quick retry mode: try a few times in short sequence
+ * - Slow retry mode: we already had a connection successfully
+ * established and will try infinitely (without user
+ * intervention)
+ * We only enter slow retry mode if IFF_LINK1 (aka autodial)
+ * is not set.
+ */
+
+ /* initialize for quick retry mode */
+ retry_wait = PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried);
+
+ sc->sc_padi_retried++;
+ if (sc->sc_padi_retried >= PPPOE_DISC_MAXPADI) {
+#if 0
+ if ((sc->sc_sppp.pp_if.if_flags & IFF_LINK1) == 0) {
+ /* slow retry mode */
+ retry_wait = PPPOE_SLOW_RETRY;
+ } else
+#endif
+ {
+ pppoe_abort_connect(sc);
+ return;
+ }
+ }
+ if ((err = pppoe_send_padi(sc)) != 0) {
+ sc->sc_padi_retried--;
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to transmit PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+ }
+ sys_timeout(retry_wait, pppoe_timeout, sc);
+ break;
+
+ case PPPOE_STATE_PADR_SENT:
+ sc->sc_padr_retried++;
+ if (sc->sc_padr_retried >= PPPOE_DISC_MAXPADR) {
+ MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+ sc->sc_state = PPPOE_STATE_PADI_SENT;
+ sc->sc_padr_retried = 0;
+ if ((err = pppoe_send_padi(sc)) != 0) {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+ }
+ sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padi_retried), pppoe_timeout, sc);
+ return;
+ }
+ if ((err = pppoe_send_padr(sc)) != 0) {
+ sc->sc_padr_retried--;
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADR, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+ }
+ sys_timeout(PPPOE_DISC_TIMEOUT * (1 + sc->sc_padr_retried), pppoe_timeout, sc);
+ break;
+ case PPPOE_STATE_CLOSING:
+ pppoe_do_disconnect(sc);
+ break;
+ default:
+ return; /* all done, work in peace */
+ }
+}
+
+/* Start a connection (i.e. initiate discovery phase) */
+int
+pppoe_connect(struct pppoe_softc *sc)
+{
+ int err;
+
+ if (sc->sc_state != PPPOE_STATE_INITIAL) {
+ return EBUSY;
+ }
+
+#ifdef PPPOE_SERVER
+ /* wait PADI if IFF_PASSIVE */
+ if ((sc->sc_sppp.pp_if.if_flags & IFF_PASSIVE)) {
+ return 0;
+ }
+#endif
+ /* save state, in case we fail to send PADI */
+ sc->sc_state = PPPOE_STATE_PADI_SENT;
+ sc->sc_padr_retried = 0;
+ err = pppoe_send_padi(sc);
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": failed to send PADI, error=%d\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, err));
+ sys_timeout(PPPOE_DISC_TIMEOUT, pppoe_timeout, sc);
+ return err;
+}
+
+/* disconnect */
+void
+pppoe_disconnect(struct pppoe_softc *sc)
+{
+ if (sc->sc_state < PPPOE_STATE_SESSION) {
+ return;
+ }
+ /*
+ * Do not call pppoe_disconnect here, the upper layer state
+ * machine gets confused by this. We must return from this
+ * function and defer disconnecting to the timeout handler.
+ */
+ sc->sc_state = PPPOE_STATE_CLOSING;
+ sys_timeout(20, pppoe_timeout, sc);
+}
+
+static int
+pppoe_do_disconnect(struct pppoe_softc *sc)
+{
+ int err;
+
+ if (sc->sc_state < PPPOE_STATE_SESSION) {
+ err = EBUSY;
+ } else {
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": disconnecting\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+ err = pppoe_send_padt(sc->sc_ethif, sc->sc_session, (const u8_t *)&sc->sc_dest);
+ }
+
+ /* cleanup softc */
+ sc->sc_state = PPPOE_STATE_INITIAL;
+ MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+ sc->sc_ac_cookie_len = 0;
+#ifdef PPPOE_SERVER
+ if (sc->sc_hunique) {
+ mem_free(sc->sc_hunique);
+ sc->sc_hunique = NULL;
+ }
+ sc->sc_hunique_len = 0;
+#endif
+ sc->sc_session = 0;
+
+ sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */
+
+ return err;
+}
+
+/* Connection attempt aborted */
+static void
+pppoe_abort_connect(struct pppoe_softc *sc)
+{
+ printf("%c%c%"U16_F": could not establish connection\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ sc->sc_state = PPPOE_STATE_CLOSING;
+
+ sc->sc_linkStatusCB(sc->sc_pd, 0); /* notify upper layers */
+
+ /* clear connection state */
+ MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+ sc->sc_state = PPPOE_STATE_INITIAL;
+}
+
+/* Send a PADR packet */
+static err_t
+pppoe_send_padr(struct pppoe_softc *sc)
+{
+ struct pbuf *pb;
+ u8_t *p;
+ size_t len;
+#ifdef PPPOE_TODO
+ size_t l1 = 0; /* XXX: gcc */
+#endif /* PPPOE_TODO */
+
+ if (sc->sc_state != PPPOE_STATE_PADR_SENT) {
+ return ERR_CONN;
+ }
+
+ len = 2 + 2 + 2 + 2 + sizeof(sc); /* service name, host unique */
+#ifdef PPPOE_TODO
+ if (sc->sc_service_name != NULL) { /* service name tag maybe empty */
+ l1 = strlen(sc->sc_service_name);
+ len += l1;
+ }
+#endif /* PPPOE_TODO */
+ if (sc->sc_ac_cookie_len > 0) {
+ len += 2 + 2 + sc->sc_ac_cookie_len; /* AC cookie */
+ }
+ LWIP_ASSERT("sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff",
+ sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len <= 0xffff);
+ pb = pbuf_alloc(PBUF_LINK, (u16_t)(sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len), PBUF_RAM);
+ if (!pb) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+ p = (u8_t*)pb->payload + sizeof (struct eth_hdr);
+ PPPOE_ADD_HEADER(p, PPPOE_CODE_PADR, 0, len);
+ PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
+#ifdef PPPOE_TODO
+ if (sc->sc_service_name != NULL) {
+ PPPOE_ADD_16(p, l1);
+ MEMCPY(p, sc->sc_service_name, l1);
+ p += l1;
+ } else
+#endif /* PPPOE_TODO */
+ {
+ PPPOE_ADD_16(p, 0);
+ }
+ if (sc->sc_ac_cookie_len > 0) {
+ PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE);
+ PPPOE_ADD_16(p, sc->sc_ac_cookie_len);
+ MEMCPY(p, sc->sc_ac_cookie, sc->sc_ac_cookie_len);
+ p += sc->sc_ac_cookie_len;
+ }
+ PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
+ PPPOE_ADD_16(p, sizeof(sc));
+ MEMCPY(p, &sc, sizeof sc);
+
+ return pppoe_output(sc, pb);
+}
+
+/* send a PADT packet */
+static err_t
+pppoe_send_padt(struct netif *outgoing_if, u_int session, const u8_t *dest)
+{
+ struct pbuf *pb;
+ struct eth_hdr *ethhdr;
+ err_t res;
+ u8_t *p;
+
+ pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN, PBUF_RAM);
+ if (!pb) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+
+ ethhdr = (struct eth_hdr *)pb->payload;
+ ethhdr->type = PP_HTONS(ETHTYPE_PPPOEDISC);
+ MEMCPY(ethhdr->dest.addr, dest, sizeof(ethhdr->dest.addr));
+ MEMCPY(ethhdr->src.addr, ((struct eth_addr *)outgoing_if->hwaddr)->addr, sizeof(ethhdr->src.addr));
+
+ p = (u8_t*)(ethhdr + 1);
+ PPPOE_ADD_HEADER(p, PPPOE_CODE_PADT, session, 0);
+
+ res = outgoing_if->linkoutput(outgoing_if, pb);
+
+ pbuf_free(pb);
+
+ return res;
+}
+
+#ifdef PPPOE_SERVER
+static err_t
+pppoe_send_pado(struct pppoe_softc *sc)
+{
+ struct pbuf *pb;
+ u8_t *p;
+ size_t len;
+
+ if (sc->sc_state != PPPOE_STATE_PADO_SENT) {
+ return ERR_CONN;
+ }
+
+ /* calc length */
+ len = 0;
+ /* include ac_cookie */
+ len += 2 + 2 + sizeof(sc);
+ /* include hunique */
+ len += 2 + 2 + sc->sc_hunique_len;
+ pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM);
+ if (!pb) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+ p = (u8_t*)pb->payload + sizeof (struct eth_hdr);
+ PPPOE_ADD_HEADER(p, PPPOE_CODE_PADO, 0, len);
+ PPPOE_ADD_16(p, PPPOE_TAG_ACCOOKIE);
+ PPPOE_ADD_16(p, sizeof(sc));
+ MEMCPY(p, &sc, sizeof(sc));
+ p += sizeof(sc);
+ PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
+ PPPOE_ADD_16(p, sc->sc_hunique_len);
+ MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len);
+ return pppoe_output(sc, pb);
+}
+
+static err_t
+pppoe_send_pads(struct pppoe_softc *sc)
+{
+ struct pbuf *pb;
+ u8_t *p;
+ size_t len, l1 = 0; /* XXX: gcc */
+
+ if (sc->sc_state != PPPOE_STATE_PADO_SENT) {
+ return ERR_CONN;
+ }
+
+ sc->sc_session = mono_time.tv_sec % 0xff + 1;
+ /* calc length */
+ len = 0;
+ /* include hunique */
+ len += 2 + 2 + 2 + 2 + sc->sc_hunique_len; /* service name, host unique*/
+ if (sc->sc_service_name != NULL) { /* service name tag maybe empty */
+ l1 = strlen(sc->sc_service_name);
+ len += l1;
+ }
+ pb = pbuf_alloc(PBUF_LINK, sizeof(struct eth_hdr) + PPPOE_HEADERLEN + len, PBUF_RAM);
+ if (!pb) {
+ return ERR_MEM;
+ }
+ LWIP_ASSERT("pb->tot_len == pb->len", pb->tot_len == pb->len);
+ p = (u8_t*)pb->payload + sizeof (struct eth_hdr);
+ PPPOE_ADD_HEADER(p, PPPOE_CODE_PADS, sc->sc_session, len);
+ PPPOE_ADD_16(p, PPPOE_TAG_SNAME);
+ if (sc->sc_service_name != NULL) {
+ PPPOE_ADD_16(p, l1);
+ MEMCPY(p, sc->sc_service_name, l1);
+ p += l1;
+ } else {
+ PPPOE_ADD_16(p, 0);
+ }
+ PPPOE_ADD_16(p, PPPOE_TAG_HUNIQUE);
+ PPPOE_ADD_16(p, sc->sc_hunique_len);
+ MEMCPY(p, sc->sc_hunique, sc->sc_hunique_len);
+ return pppoe_output(sc, pb);
+}
+#endif
+
+err_t
+pppoe_xmit(struct pppoe_softc *sc, struct pbuf *pb)
+{
+ u8_t *p;
+ size_t len;
+
+ /* are we ready to process data yet? */
+ if (sc->sc_state < PPPOE_STATE_SESSION) {
+ /*sppp_flush(&sc->sc_sppp.pp_if);*/
+ pbuf_free(pb);
+ return ERR_CONN;
+ }
+
+ len = pb->tot_len;
+
+ /* make room for Ethernet header - should not fail */
+ if (pbuf_header(pb, sizeof(struct eth_hdr) + PPPOE_HEADERLEN) != 0) {
+ /* bail out */
+ PPPDEBUG(LOG_ERR, ("pppoe: %c%c%"U16_F": pppoe_xmit: could not allocate room for header\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num));
+ LINK_STATS_INC(link.lenerr);
+ pbuf_free(pb);
+ return ERR_BUF;
+ }
+
+ p = (u8_t*)pb->payload + sizeof(struct eth_hdr);
+ PPPOE_ADD_HEADER(p, 0, sc->sc_session, len);
+
+ return pppoe_output(sc, pb);
+}
+
+#if 0 /*def PFIL_HOOKS*/
+static int
+pppoe_ifattach_hook(void *arg, struct pbuf **mp, struct netif *ifp, int dir)
+{
+ struct pppoe_softc *sc;
+ int s;
+
+ if (mp != (struct pbuf **)PFIL_IFNET_DETACH) {
+ return 0;
+ }
+
+ LIST_FOREACH(sc, &pppoe_softc_list, sc_list) {
+ if (sc->sc_ethif != ifp) {
+ continue;
+ }
+ if (sc->sc_sppp.pp_if.if_flags & IFF_UP) {
+ sc->sc_sppp.pp_if.if_flags &= ~(IFF_UP|IFF_RUNNING);
+ printf("%c%c%"U16_F": ethernet interface detached, going down\n",
+ sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num);
+ }
+ sc->sc_ethif = NULL;
+ pppoe_clear_softc(sc, "ethernet interface detached");
+ }
+
+ return 0;
+}
+#endif
+
+static void
+pppoe_clear_softc(struct pppoe_softc *sc, const char *message)
+{
+ LWIP_UNUSED_ARG(message);
+
+ /* stop timer */
+ sys_untimeout(pppoe_timeout, sc);
+ PPPDEBUG(LOG_DEBUG, ("pppoe: %c%c%"U16_F": session 0x%x terminated, %s\n", sc->sc_ethif->name[0], sc->sc_ethif->name[1], sc->sc_ethif->num, sc->sc_session, message));
+
+ /* fix our state */
+ sc->sc_state = PPPOE_STATE_INITIAL;
+
+ /* notify upper layers */
+ sc->sc_linkStatusCB(sc->sc_pd, 0);
+
+ /* clean up softc */
+ MEMCPY(&sc->sc_dest, ethbroadcast.addr, sizeof(sc->sc_dest));
+ sc->sc_ac_cookie_len = 0;
+ sc->sc_session = 0;
+}
+
+#endif /* PPPOE_SUPPORT */
+
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/pppdebug.h b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/pppdebug.h
new file mode 100644
index 00000000..81349971
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/pppdebug.h
@@ -0,0 +1,73 @@
+/*****************************************************************************
+* pppdebug.h - System debugging utilities.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* portions Copyright (c) 1998 Global Election Systems Inc.
+* portions Copyright (c) 2001 by Cognizant Pty Ltd.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY (please don't use tabs!)
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 98-07-29 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Original.
+*
+*****************************************************************************
+*/
+#ifndef PPPDEBUG_H
+#define PPPDEBUG_H
+
+/* Trace levels. */
+#define LOG_CRITICAL (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE)
+#define LOG_ERR (PPP_DEBUG | LWIP_DBG_LEVEL_SEVERE)
+#define LOG_NOTICE (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define LOG_WARNING (PPP_DEBUG | LWIP_DBG_LEVEL_WARNING)
+#define LOG_INFO (PPP_DEBUG)
+#define LOG_DETAIL (PPP_DEBUG)
+#define LOG_DEBUG (PPP_DEBUG)
+
+
+#define TRACELCP PPP_DEBUG
+
+#if PPP_DEBUG
+
+#define AUTHDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define IPCPDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define UPAPDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define LCPDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define FSMDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define CHAPDEBUG(a, b) LWIP_DEBUGF(a, b)
+#define PPPDEBUG(a, b) LWIP_DEBUGF(a, b)
+
+#else /* PPP_DEBUG */
+
+#define AUTHDEBUG(a, b)
+#define IPCPDEBUG(a, b)
+#define UPAPDEBUG(a, b)
+#define LCPDEBUG(a, b)
+#define FSMDEBUG(a, b)
+#define CHAPDEBUG(a, b)
+#define PPPDEBUG(a, b)
+
+#endif /* PPP_DEBUG */
+
+#endif /* PPPDEBUG_H */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/randm.c b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/randm.c
new file mode 100644
index 00000000..b736091f
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/randm.c
@@ -0,0 +1,249 @@
+/*****************************************************************************
+* randm.c - Random number generator program file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* Copyright (c) 1998 by Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 98-06-03 Guy Lancaster <lancasterg@acm.org>, Global Election Systems Inc.
+* Extracted from avos.
+*****************************************************************************/
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "md5.h"
+#include "randm.h"
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include <string.h>
+
+#if MD5_SUPPORT /* this module depends on MD5 */
+#define RANDPOOLSZ 16 /* Bytes stored in the pool of randomness. */
+
+/*****************************/
+/*** LOCAL DATA STRUCTURES ***/
+/*****************************/
+static char randPool[RANDPOOLSZ]; /* Pool of randomness. */
+static long randCount = 0; /* Pseudo-random incrementer */
+
+
+/***********************************/
+/*** PUBLIC FUNCTION DEFINITIONS ***/
+/***********************************/
+/*
+ * Initialize the random number generator.
+ *
+ * Since this is to be called on power up, we don't have much
+ * system randomess to work with. Here all we use is the
+ * real-time clock. We'll accumulate more randomness as soon
+ * as things start happening.
+ */
+void
+avRandomInit()
+{
+ avChurnRand(NULL, 0);
+}
+
+/*
+ * Churn the randomness pool on a random event. Call this early and often
+ * on random and semi-random system events to build randomness in time for
+ * usage. For randomly timed events, pass a null pointer and a zero length
+ * and this will use the system timer and other sources to add randomness.
+ * If new random data is available, pass a pointer to that and it will be
+ * included.
+ *
+ * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427
+ */
+void
+avChurnRand(char *randData, u32_t randLen)
+{
+ MD5_CTX md5;
+
+ /* LWIP_DEBUGF(LOG_INFO, ("churnRand: %u@%P\n", randLen, randData)); */
+ MD5Init(&md5);
+ MD5Update(&md5, (u_char *)randPool, sizeof(randPool));
+ if (randData) {
+ MD5Update(&md5, (u_char *)randData, randLen);
+ } else {
+ struct {
+ /* INCLUDE fields for any system sources of randomness */
+ char foobar;
+ } sysData;
+
+ /* Load sysData fields here. */
+ MD5Update(&md5, (u_char *)&sysData, sizeof(sysData));
+ }
+ MD5Final((u_char *)randPool, &md5);
+/* LWIP_DEBUGF(LOG_INFO, ("churnRand: -> 0\n")); */
+}
+
+/*
+ * Use the random pool to generate random data. This degrades to pseudo
+ * random when used faster than randomness is supplied using churnRand().
+ * Note: It's important that there be sufficient randomness in randPool
+ * before this is called for otherwise the range of the result may be
+ * narrow enough to make a search feasible.
+ *
+ * Ref: Applied Cryptography 2nd Ed. by Bruce Schneier p. 427
+ *
+ * XXX Why does he not just call churnRand() for each block? Probably
+ * so that you don't ever publish the seed which could possibly help
+ * predict future values.
+ * XXX Why don't we preserve md5 between blocks and just update it with
+ * randCount each time? Probably there is a weakness but I wish that
+ * it was documented.
+ */
+void
+avGenRand(char *buf, u32_t bufLen)
+{
+ MD5_CTX md5;
+ u_char tmp[16];
+ u32_t n;
+
+ while (bufLen > 0) {
+ n = LWIP_MIN(bufLen, RANDPOOLSZ);
+ MD5Init(&md5);
+ MD5Update(&md5, (u_char *)randPool, sizeof(randPool));
+ MD5Update(&md5, (u_char *)&randCount, sizeof(randCount));
+ MD5Final(tmp, &md5);
+ randCount++;
+ MEMCPY(buf, tmp, n);
+ buf += n;
+ bufLen -= n;
+ }
+}
+
+/*
+ * Return a new random number.
+ */
+u32_t
+avRandom()
+{
+ u32_t newRand;
+
+ avGenRand((char *)&newRand, sizeof(newRand));
+
+ return newRand;
+}
+
+#else /* MD5_SUPPORT */
+
+/*****************************/
+/*** LOCAL DATA STRUCTURES ***/
+/*****************************/
+static int avRandomized = 0; /* Set when truely randomized. */
+static u32_t avRandomSeed = 0; /* Seed used for random number generation. */
+
+
+/***********************************/
+/*** PUBLIC FUNCTION DEFINITIONS ***/
+/***********************************/
+/*
+ * Initialize the random number generator.
+ *
+ * Here we attempt to compute a random number seed but even if
+ * it isn't random, we'll randomize it later.
+ *
+ * The current method uses the fields from the real time clock,
+ * the idle process counter, the millisecond counter, and the
+ * hardware timer tick counter. When this is invoked
+ * in startup(), then the idle counter and timer values may
+ * repeat after each boot and the real time clock may not be
+ * operational. Thus we call it again on the first random
+ * event.
+ */
+void
+avRandomInit()
+{
+#if 0
+ /* Get a pointer into the last 4 bytes of clockBuf. */
+ u32_t *lptr1 = (u32_t *)((char *)&clockBuf[3]);
+
+ /*
+ * Initialize our seed using the real-time clock, the idle
+ * counter, the millisecond timer, and the hardware timer
+ * tick counter. The real-time clock and the hardware
+ * tick counter are the best sources of randomness but
+ * since the tick counter is only 16 bit (and truncated
+ * at that), the idle counter and millisecond timer
+ * (which may be small values) are added to help
+ * randomize the lower 16 bits of the seed.
+ */
+ readClk();
+ avRandomSeed += *(u32_t *)clockBuf + *lptr1 + OSIdleCtr
+ + ppp_mtime() + ((u32_t)TM1 << 16) + TM1;
+#else
+ avRandomSeed += sys_jiffies(); /* XXX */
+#endif
+
+ /* Initialize the Borland random number generator. */
+ srand((unsigned)avRandomSeed);
+}
+
+/*
+ * Randomize our random seed value. Here we use the fact that
+ * this function is called at *truely random* times by the polling
+ * and network functions. Here we only get 16 bits of new random
+ * value but we use the previous value to randomize the other 16
+ * bits.
+ */
+void
+avRandomize(void)
+{
+ static u32_t last_jiffies;
+
+ if (!avRandomized) {
+ avRandomized = !0;
+ avRandomInit();
+ /* The initialization function also updates the seed. */
+ } else {
+ /* avRandomSeed += (avRandomSeed << 16) + TM1; */
+ avRandomSeed += (sys_jiffies() - last_jiffies); /* XXX */
+ }
+ last_jiffies = sys_jiffies();
+}
+
+/*
+ * Return a new random number.
+ * Here we use the Borland rand() function to supply a pseudo random
+ * number which we make truely random by combining it with our own
+ * seed which is randomized by truely random events.
+ * Thus the numbers will be truely random unless there have been no
+ * operator or network events in which case it will be pseudo random
+ * seeded by the real time clock.
+ */
+u32_t
+avRandom()
+{
+ return ((((u32_t)rand() << 16) + rand()) + avRandomSeed);
+}
+
+#endif /* MD5_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/randm.h b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/randm.h
new file mode 100644
index 00000000..a0984b02
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/randm.h
@@ -0,0 +1,81 @@
+/*****************************************************************************
+* randm.h - Random number generator header file.
+*
+* Copyright (c) 2003 by Marc Boucher, Services Informatiques (MBSI) inc.
+* Copyright (c) 1998 Global Election Systems Inc.
+*
+* The authors hereby grant permission to use, copy, modify, distribute,
+* and license this software and its documentation for any purpose, provided
+* that existing copyright notices are retained in all copies and that this
+* notice and the following disclaimer are included verbatim in any
+* distributions. No written agreement, license, or royalty fee is required
+* for any of the authorized uses.
+*
+* THIS SOFTWARE IS PROVIDED BY THE CONTRIBUTORS *AS IS* AND ANY EXPRESS OR
+* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+* IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
+* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+*
+******************************************************************************
+* REVISION HISTORY
+*
+* 03-01-01 Marc Boucher <marc@mbsi.ca>
+* Ported to lwIP.
+* 98-05-29 Guy Lancaster <glanca@gesn.com>, Global Election Systems Inc.
+* Extracted from avos.
+*****************************************************************************/
+
+#ifndef RANDM_H
+#define RANDM_H
+
+/***********************
+*** PUBLIC FUNCTIONS ***
+***********************/
+/*
+ * Initialize the random number generator.
+ */
+void avRandomInit(void);
+
+/*
+ * Churn the randomness pool on a random event. Call this early and often
+ * on random and semi-random system events to build randomness in time for
+ * usage. For randomly timed events, pass a null pointer and a zero length
+ * and this will use the system timer and other sources to add randomness.
+ * If new random data is available, pass a pointer to that and it will be
+ * included.
+ */
+void avChurnRand(char *randData, u32_t randLen);
+
+/*
+ * Randomize our random seed value. To be called for truely random events
+ * such as user operations and network traffic.
+ */
+#if MD5_SUPPORT
+#define avRandomize() avChurnRand(NULL, 0)
+#else /* MD5_SUPPORT */
+void avRandomize(void);
+#endif /* MD5_SUPPORT */
+
+/*
+ * Use the random pool to generate random data. This degrades to pseudo
+ * random when used faster than randomness is supplied using churnRand().
+ * Thus it's important to make sure that the results of this are not
+ * published directly because one could predict the next result to at
+ * least some degree. Also, it's important to get a good seed before
+ * the first use.
+ */
+void avGenRand(char *buf, u32_t bufLen);
+
+/*
+ * Return a new random number.
+ */
+u32_t avRandom(void);
+
+
+#endif /* RANDM_H */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/readme.txt b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/readme.txt
new file mode 100644
index 00000000..5be41b90
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/readme.txt
@@ -0,0 +1,21 @@
+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/src/VBox/Devices/Network/lwip-new/src/netif/ppp/vj.c b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/vj.c
new file mode 100644
index 00000000..40fdad13
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/vj.c
@@ -0,0 +1,652 @@
+/*
+ * Routines to compress and uncompess tcp packets (for transmission
+ * over low speed serial lines.
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * Initial distribution.
+ *
+ * Modified June 1993 by Paul Mackerras, paulus@cs.anu.edu.au,
+ * so that the entire packet being decompressed doesn't have
+ * to be in contiguous memory (just the compressed header).
+ *
+ * Modified March 1998 by Guy Lancaster, glanca@gesn.com,
+ * for a 16 bit processor.
+ */
+
+#include "lwip/opt.h"
+
+#if PPP_SUPPORT /* don't build if not configured for use in lwipopts.h */
+
+#include "ppp_impl.h"
+#include "pppdebug.h"
+
+#include "vj.h"
+
+#include <string.h>
+
+#if VJ_SUPPORT
+
+#if LINK_STATS
+#define INCR(counter) ++comp->stats.counter
+#else
+#define INCR(counter)
+#endif
+
+void
+vj_compress_init(struct vjcompress *comp)
+{
+ register u_char i;
+ register struct cstate *tstate = comp->tstate;
+
+#if MAX_SLOTS == 0
+ memset((char *)comp, 0, sizeof(*comp));
+#endif
+ comp->maxSlotIndex = MAX_SLOTS - 1;
+ comp->compressSlot = 0; /* Disable slot ID compression by default. */
+ for (i = MAX_SLOTS - 1; i > 0; --i) {
+ tstate[i].cs_id = i;
+ tstate[i].cs_next = &tstate[i - 1];
+ }
+ tstate[0].cs_next = &tstate[MAX_SLOTS - 1];
+ tstate[0].cs_id = 0;
+ comp->last_cs = &tstate[0];
+ comp->last_recv = 255;
+ comp->last_xmit = 255;
+ comp->flags = VJF_TOSS;
+}
+
+
+/* ENCODE encodes a number that is known to be non-zero. ENCODEZ
+ * checks for zero (since zero has to be encoded in the long, 3 byte
+ * form).
+ */
+#define ENCODE(n) { \
+ if ((u_short)(n) >= 256) { \
+ *cp++ = 0; \
+ cp[1] = (u_char)(n); \
+ cp[0] = (u_char)((n) >> 8); \
+ cp += 2; \
+ } else { \
+ *cp++ = (u_char)(n); \
+ } \
+}
+#define ENCODEZ(n) { \
+ if ((u_short)(n) >= 256 || (u_short)(n) == 0) { \
+ *cp++ = 0; \
+ cp[1] = (u_char)(n); \
+ cp[0] = (u_char)((n) >> 8); \
+ cp += 2; \
+ } else { \
+ *cp++ = (u_char)(n); \
+ } \
+}
+
+#define DECODEL(f) { \
+ if (*cp == 0) {\
+ u32_t tmp = ntohl(f) + ((cp[1] << 8) | cp[2]); \
+ (f) = htonl(tmp); \
+ cp += 3; \
+ } else { \
+ u32_t tmp = ntohl(f) + (u32_t)*cp++; \
+ (f) = htonl(tmp); \
+ } \
+}
+
+#define DECODES(f) { \
+ if (*cp == 0) {\
+ u_short tmp = ntohs(f) + (((u_short)cp[1] << 8) | cp[2]); \
+ (f) = htons(tmp); \
+ cp += 3; \
+ } else { \
+ u_short tmp = ntohs(f) + (u_short)*cp++; \
+ (f) = htons(tmp); \
+ } \
+}
+
+#define DECODEU(f) { \
+ if (*cp == 0) {\
+ (f) = htons(((u_short)cp[1] << 8) | cp[2]); \
+ cp += 3; \
+ } else { \
+ (f) = htons((u_short)*cp++); \
+ } \
+}
+
+/*
+ * vj_compress_tcp - Attempt to do Van Jacobson header compression on a
+ * packet. This assumes that nb and comp are not null and that the first
+ * buffer of the chain contains a valid IP header.
+ * Return the VJ type code indicating whether or not the packet was
+ * compressed.
+ */
+u_int
+vj_compress_tcp(struct vjcompress *comp, struct pbuf *pb)
+{
+ register struct ip_hdr *ip = (struct ip_hdr *)pb->payload;
+ register struct cstate *cs = comp->last_cs->cs_next;
+ register u_short hlen = IPH_HL(ip);
+ register struct tcp_hdr *oth;
+ register struct tcp_hdr *th;
+ register u_short deltaS, deltaA;
+ register u_long deltaL;
+ register u_int changes = 0;
+ u_char new_seq[16];
+ register u_char *cp = new_seq;
+
+ /*
+ * Check that the packet is IP proto TCP.
+ */
+ if (IPH_PROTO(ip) != IP_PROTO_TCP) {
+ return (TYPE_IP);
+ }
+
+ /*
+ * Bail if this is an IP fragment or if the TCP packet isn't
+ * `compressible' (i.e., ACK isn't set or some other control bit is
+ * set).
+ */
+ if ((IPH_OFFSET(ip) & PP_HTONS(0x3fff)) || pb->tot_len < 40) {
+ return (TYPE_IP);
+ }
+ th = (struct tcp_hdr *)&((long *)ip)[hlen];
+ if ((TCPH_FLAGS(th) & (TCP_SYN|TCP_FIN|TCP_RST|TCP_ACK)) != TCP_ACK) {
+ return (TYPE_IP);
+ }
+ /*
+ * Packet is compressible -- we're going to send either a
+ * COMPRESSED_TCP or UNCOMPRESSED_TCP packet. Either way we need
+ * to locate (or create) the connection state. Special case the
+ * most recently used connection since it's most likely to be used
+ * again & we don't have to do any reordering if it's used.
+ */
+ INCR(vjs_packets);
+ if (!ip_addr_cmp(&ip->src, &cs->cs_ip.src)
+ || !ip_addr_cmp(&ip->dest, &cs->cs_ip.dest)
+ || *(long *)th != ((long *)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]) {
+ /*
+ * Wasn't the first -- search for it.
+ *
+ * States are kept in a circularly linked list with
+ * last_cs pointing to the end of the list. The
+ * list is kept in lru order by moving a state to the
+ * head of the list whenever it is referenced. Since
+ * the list is short and, empirically, the connection
+ * we want is almost always near the front, we locate
+ * states via linear search. If we don't find a state
+ * for the datagram, the oldest state is (re-)used.
+ */
+ register struct cstate *lcs;
+ register struct cstate *lastcs = comp->last_cs;
+
+ do {
+ lcs = cs; cs = cs->cs_next;
+ INCR(vjs_searches);
+ if (ip_addr_cmp(&ip->src, &cs->cs_ip.src)
+ && ip_addr_cmp(&ip->dest, &cs->cs_ip.dest)
+ && *(long *)th == ((long *)&cs->cs_ip)[IPH_HL(&cs->cs_ip)]) {
+ goto found;
+ }
+ } while (cs != lastcs);
+
+ /*
+ * Didn't find it -- re-use oldest cstate. Send an
+ * uncompressed packet that tells the other side what
+ * connection number we're using for this conversation.
+ * Note that since the state list is circular, the oldest
+ * state points to the newest and we only need to set
+ * last_cs to update the lru linkage.
+ */
+ INCR(vjs_misses);
+ comp->last_cs = lcs;
+ hlen += TCPH_HDRLEN(th);
+ hlen <<= 2;
+ /* Check that the IP/TCP headers are contained in the first buffer. */
+ if (hlen > pb->len) {
+ return (TYPE_IP);
+ }
+ goto uncompressed;
+
+ found:
+ /*
+ * Found it -- move to the front on the connection list.
+ */
+ if (cs == lastcs) {
+ comp->last_cs = lcs;
+ } else {
+ lcs->cs_next = cs->cs_next;
+ cs->cs_next = lastcs->cs_next;
+ lastcs->cs_next = cs;
+ }
+ }
+
+ oth = (struct tcp_hdr *)&((long *)&cs->cs_ip)[hlen];
+ deltaS = hlen;
+ hlen += TCPH_HDRLEN(th);
+ hlen <<= 2;
+ /* Check that the IP/TCP headers are contained in the first buffer. */
+ if (hlen > pb->len) {
+ PPPDEBUG(LOG_INFO, ("vj_compress_tcp: header len %d spans buffers\n", hlen));
+ return (TYPE_IP);
+ }
+
+ /*
+ * Make sure that only what we expect to change changed. The first
+ * line of the `if' checks the IP protocol version, header length &
+ * type of service. The 2nd line checks the "Don't fragment" bit.
+ * The 3rd line checks the time-to-live and protocol (the protocol
+ * check is unnecessary but costless). The 4th line checks the TCP
+ * header length. The 5th line checks IP options, if any. The 6th
+ * line checks TCP options, if any. If any of these things are
+ * different between the previous & current datagram, we send the
+ * current datagram `uncompressed'.
+ */
+ if (((u_short *)ip)[0] != ((u_short *)&cs->cs_ip)[0]
+ || ((u_short *)ip)[3] != ((u_short *)&cs->cs_ip)[3]
+ || ((u_short *)ip)[4] != ((u_short *)&cs->cs_ip)[4]
+ || TCPH_HDRLEN(th) != TCPH_HDRLEN(oth)
+ || (deltaS > 5 && BCMP(ip + 1, &cs->cs_ip + 1, (deltaS - 5) << 2))
+ || (TCPH_HDRLEN(th) > 5 && BCMP(th + 1, oth + 1, (TCPH_HDRLEN(th) - 5) << 2))) {
+ goto uncompressed;
+ }
+
+ /*
+ * Figure out which of the changing fields changed. The
+ * receiver expects changes in the order: urgent, window,
+ * ack, seq (the order minimizes the number of temporaries
+ * needed in this section of code).
+ */
+ if (TCPH_FLAGS(th) & TCP_URG) {
+ deltaS = ntohs(th->urgp);
+ ENCODEZ(deltaS);
+ changes |= NEW_U;
+ } else if (th->urgp != oth->urgp) {
+ /* argh! URG not set but urp changed -- a sensible
+ * implementation should never do this but RFC793
+ * doesn't prohibit the change so we have to deal
+ * with it. */
+ goto uncompressed;
+ }
+
+ if ((deltaS = (u_short)(ntohs(th->wnd) - ntohs(oth->wnd))) != 0) {
+ ENCODE(deltaS);
+ changes |= NEW_W;
+ }
+
+ if ((deltaL = ntohl(th->ackno) - ntohl(oth->ackno)) != 0) {
+ if (deltaL > 0xffff) {
+ goto uncompressed;
+ }
+ deltaA = (u_short)deltaL;
+ ENCODE(deltaA);
+ changes |= NEW_A;
+ }
+
+ if ((deltaL = ntohl(th->seqno) - ntohl(oth->seqno)) != 0) {
+ if (deltaL > 0xffff) {
+ goto uncompressed;
+ }
+ deltaS = (u_short)deltaL;
+ ENCODE(deltaS);
+ changes |= NEW_S;
+ }
+
+ switch(changes) {
+ case 0:
+ /*
+ * Nothing changed. If this packet contains data and the
+ * last one didn't, this is probably a data packet following
+ * an ack (normal on an interactive connection) and we send
+ * it compressed. Otherwise it's probably a retransmit,
+ * retransmitted ack or window probe. Send it uncompressed
+ * in case the other side missed the compressed version.
+ */
+ if (IPH_LEN(ip) != IPH_LEN(&cs->cs_ip) &&
+ ntohs(IPH_LEN(&cs->cs_ip)) == hlen) {
+ break;
+ }
+
+ /* (fall through) */
+
+ case SPECIAL_I:
+ case SPECIAL_D:
+ /*
+ * actual changes match one of our special case encodings --
+ * send packet uncompressed.
+ */
+ goto uncompressed;
+
+ case NEW_S|NEW_A:
+ if (deltaS == deltaA && deltaS == ntohs(IPH_LEN(&cs->cs_ip)) - hlen) {
+ /* special case for echoed terminal traffic */
+ changes = SPECIAL_I;
+ cp = new_seq;
+ }
+ break;
+
+ case NEW_S:
+ if (deltaS == ntohs(IPH_LEN(&cs->cs_ip)) - hlen) {
+ /* special case for data xfer */
+ changes = SPECIAL_D;
+ cp = new_seq;
+ }
+ break;
+ }
+
+ deltaS = (u_short)(ntohs(IPH_ID(ip)) - ntohs(IPH_ID(&cs->cs_ip)));
+ if (deltaS != 1) {
+ ENCODEZ(deltaS);
+ changes |= NEW_I;
+ }
+ if (TCPH_FLAGS(th) & TCP_PSH) {
+ changes |= TCP_PUSH_BIT;
+ }
+ /*
+ * Grab the cksum before we overwrite it below. Then update our
+ * state with this packet's header.
+ */
+ deltaA = ntohs(th->chksum);
+ BCOPY(ip, &cs->cs_ip, hlen);
+
+ /*
+ * We want to use the original packet as our compressed packet.
+ * (cp - new_seq) is the number of bytes we need for compressed
+ * sequence numbers. In addition we need one byte for the change
+ * mask, one for the connection id and two for the tcp checksum.
+ * So, (cp - new_seq) + 4 bytes of header are needed. hlen is how
+ * many bytes of the original packet to toss so subtract the two to
+ * get the new packet size.
+ */
+ deltaS = (u_short)(cp - new_seq);
+ if (!comp->compressSlot || comp->last_xmit != cs->cs_id) {
+ comp->last_xmit = cs->cs_id;
+ hlen -= deltaS + 4;
+ if(pbuf_header(pb, -hlen)){
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ }
+ cp = (u_char *)pb->payload;
+ *cp++ = (u_char)(changes | NEW_C);
+ *cp++ = cs->cs_id;
+ } else {
+ hlen -= deltaS + 3;
+ if(pbuf_header(pb, -hlen)) {
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ }
+ cp = (u_char *)pb->payload;
+ *cp++ = (u_char)changes;
+ }
+ *cp++ = (u_char)(deltaA >> 8);
+ *cp++ = (u_char)deltaA;
+ BCOPY(new_seq, cp, deltaS);
+ INCR(vjs_compressed);
+ return (TYPE_COMPRESSED_TCP);
+
+ /*
+ * Update connection state cs & send uncompressed packet (that is,
+ * a regular ip/tcp packet but with the 'conversation id' we hope
+ * to use on future compressed packets in the protocol field).
+ */
+uncompressed:
+ BCOPY(ip, &cs->cs_ip, hlen);
+ IPH_PROTO_SET(ip, cs->cs_id);
+ comp->last_xmit = cs->cs_id;
+ return (TYPE_UNCOMPRESSED_TCP);
+}
+
+/*
+ * Called when we may have missed a packet.
+ */
+void
+vj_uncompress_err(struct vjcompress *comp)
+{
+ comp->flags |= VJF_TOSS;
+ INCR(vjs_errorin);
+}
+
+/*
+ * "Uncompress" a packet of type TYPE_UNCOMPRESSED_TCP.
+ * Return 0 on success, -1 on failure.
+ */
+int
+vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp)
+{
+ register u_int hlen;
+ register struct cstate *cs;
+ register struct ip_hdr *ip;
+
+ ip = (struct ip_hdr *)nb->payload;
+ hlen = IPH_HL(ip) << 2;
+ if (IPH_PROTO(ip) >= MAX_SLOTS
+ || hlen + sizeof(struct tcp_hdr) > nb->len
+ || (hlen += TCPH_HDRLEN(((struct tcp_hdr *)&((char *)ip)[hlen])) << 2)
+ > nb->len
+ || hlen > MAX_HDR) {
+ PPPDEBUG(LOG_INFO, ("vj_uncompress_uncomp: bad cid=%d, hlen=%d buflen=%d\n",
+ IPH_PROTO(ip), hlen, nb->len));
+ comp->flags |= VJF_TOSS;
+ INCR(vjs_errorin);
+ return -1;
+ }
+ cs = &comp->rstate[comp->last_recv = IPH_PROTO(ip)];
+ comp->flags &=~ VJF_TOSS;
+ IPH_PROTO_SET(ip, IP_PROTO_TCP);
+ BCOPY(ip, &cs->cs_ip, hlen);
+ cs->cs_hlen = (u_short)hlen;
+ INCR(vjs_uncompressedin);
+ return 0;
+}
+
+/*
+ * Uncompress a packet of type TYPE_COMPRESSED_TCP.
+ * The packet is composed of a buffer chain and the first buffer
+ * must contain an accurate chain length.
+ * The first buffer must include the entire compressed TCP/IP header.
+ * This procedure replaces the compressed header with the uncompressed
+ * header and returns the length of the VJ header.
+ */
+int
+vj_uncompress_tcp(struct pbuf **nb, struct vjcompress *comp)
+{
+ u_char *cp;
+ struct tcp_hdr *th;
+ struct cstate *cs;
+ u_short *bp;
+ struct pbuf *n0 = *nb;
+ u32_t tmp;
+ u_int vjlen, hlen, changes;
+
+ INCR(vjs_compressedin);
+ cp = (u_char *)n0->payload;
+ changes = *cp++;
+ if (changes & NEW_C) {
+ /*
+ * Make sure the state index is in range, then grab the state.
+ * If we have a good state index, clear the 'discard' flag.
+ */
+ if (*cp >= MAX_SLOTS) {
+ PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: bad cid=%d\n", *cp));
+ goto bad;
+ }
+
+ comp->flags &=~ VJF_TOSS;
+ comp->last_recv = *cp++;
+ } else {
+ /*
+ * this packet has an implicit state index. If we've
+ * had a line error since the last time we got an
+ * explicit state index, we have to toss the packet.
+ */
+ if (comp->flags & VJF_TOSS) {
+ PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: tossing\n"));
+ INCR(vjs_tossed);
+ return (-1);
+ }
+ }
+ cs = &comp->rstate[comp->last_recv];
+ hlen = IPH_HL(&cs->cs_ip) << 2;
+ th = (struct tcp_hdr *)&((u_char *)&cs->cs_ip)[hlen];
+ th->chksum = htons((*cp << 8) | cp[1]);
+ cp += 2;
+ if (changes & TCP_PUSH_BIT) {
+ TCPH_SET_FLAG(th, TCP_PSH);
+ } else {
+ TCPH_UNSET_FLAG(th, TCP_PSH);
+ }
+
+ switch (changes & SPECIALS_MASK) {
+ case SPECIAL_I:
+ {
+ register u32_t i = ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen;
+ /* some compilers can't nest inline assembler.. */
+ tmp = ntohl(th->ackno) + i;
+ th->ackno = htonl(tmp);
+ tmp = ntohl(th->seqno) + i;
+ th->seqno = htonl(tmp);
+ }
+ break;
+
+ case SPECIAL_D:
+ /* some compilers can't nest inline assembler.. */
+ tmp = ntohl(th->seqno) + ntohs(IPH_LEN(&cs->cs_ip)) - cs->cs_hlen;
+ th->seqno = htonl(tmp);
+ break;
+
+ default:
+ if (changes & NEW_U) {
+ TCPH_SET_FLAG(th, TCP_URG);
+ DECODEU(th->urgp);
+ } else {
+ TCPH_UNSET_FLAG(th, TCP_URG);
+ }
+ if (changes & NEW_W) {
+ DECODES(th->wnd);
+ }
+ if (changes & NEW_A) {
+ DECODEL(th->ackno);
+ }
+ if (changes & NEW_S) {
+ DECODEL(th->seqno);
+ }
+ break;
+ }
+ if (changes & NEW_I) {
+ DECODES(cs->cs_ip._id);
+ } else {
+ IPH_ID_SET(&cs->cs_ip, ntohs(IPH_ID(&cs->cs_ip)) + 1);
+ IPH_ID_SET(&cs->cs_ip, htons(IPH_ID(&cs->cs_ip)));
+ }
+
+ /*
+ * At this point, cp points to the first byte of data in the
+ * packet. Fill in the IP total length and update the IP
+ * header checksum.
+ */
+ vjlen = (u_short)(cp - (u_char*)n0->payload);
+ if (n0->len < vjlen) {
+ /*
+ * We must have dropped some characters (crc should detect
+ * this but the old slip framing won't)
+ */
+ PPPDEBUG(LOG_INFO, ("vj_uncompress_tcp: head buffer %d too short %d\n",
+ n0->len, vjlen));
+ goto bad;
+ }
+
+#if BYTE_ORDER == LITTLE_ENDIAN
+ tmp = n0->tot_len - vjlen + cs->cs_hlen;
+ IPH_LEN_SET(&cs->cs_ip, htons((u_short)tmp));
+#else
+ IPH_LEN_SET(&cs->cs_ip, htons(n0->tot_len - vjlen + cs->cs_hlen));
+#endif
+
+ /* recompute the ip header checksum */
+ bp = (u_short *) &cs->cs_ip;
+ IPH_CHKSUM_SET(&cs->cs_ip, 0);
+ for (tmp = 0; hlen > 0; hlen -= 2) {
+ tmp += *bp++;
+ }
+ tmp = (tmp & 0xffff) + (tmp >> 16);
+ tmp = (tmp & 0xffff) + (tmp >> 16);
+ IPH_CHKSUM_SET(&cs->cs_ip, (u_short)(~tmp));
+
+ /* Remove the compressed header and prepend the uncompressed header. */
+ if(pbuf_header(n0, -((s16_t)(vjlen)))) {
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ goto bad;
+ }
+
+ if(LWIP_MEM_ALIGN(n0->payload) != n0->payload) {
+ struct pbuf *np, *q;
+ u8_t *bufptr;
+
+ np = pbuf_alloc(PBUF_RAW, n0->len + cs->cs_hlen, PBUF_POOL);
+ if(!np) {
+ PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: realign failed\n"));
+ goto bad;
+ }
+
+ if(pbuf_header(np, -cs->cs_hlen)) {
+ /* Can we cope with this failing? Just assert for now */
+ LWIP_ASSERT("pbuf_header failed\n", 0);
+ goto bad;
+ }
+
+ bufptr = n0->payload;
+ for(q = np; q != NULL; q = q->next) {
+ MEMCPY(q->payload, bufptr, q->len);
+ bufptr += q->len;
+ }
+
+ if(n0->next) {
+ pbuf_chain(np, n0->next);
+ pbuf_dechain(n0);
+ }
+ pbuf_free(n0);
+ n0 = np;
+ }
+
+ if(pbuf_header(n0, cs->cs_hlen)) {
+ struct pbuf *np;
+
+ LWIP_ASSERT("vj_uncompress_tcp: cs->cs_hlen <= PBUF_POOL_BUFSIZE", cs->cs_hlen <= PBUF_POOL_BUFSIZE);
+ np = pbuf_alloc(PBUF_RAW, cs->cs_hlen, PBUF_POOL);
+ if(!np) {
+ PPPDEBUG(LOG_WARNING, ("vj_uncompress_tcp: prepend failed\n"));
+ goto bad;
+ }
+ pbuf_cat(np, n0);
+ n0 = np;
+ }
+ LWIP_ASSERT("n0->len >= cs->cs_hlen", n0->len >= cs->cs_hlen);
+ MEMCPY(n0->payload, &cs->cs_ip, cs->cs_hlen);
+
+ *nb = n0;
+
+ return vjlen;
+
+bad:
+ comp->flags |= VJF_TOSS;
+ INCR(vjs_errorin);
+ return (-1);
+}
+
+#endif /* VJ_SUPPORT */
+
+#endif /* PPP_SUPPORT */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/ppp/vj.h b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/vj.h
new file mode 100644
index 00000000..feb165c4
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/ppp/vj.h
@@ -0,0 +1,156 @@
+/*
+ * Definitions for tcp compression routines.
+ *
+ * $Id: vj.h $
+ *
+ * Copyright (c) 1989 Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms are permitted
+ * provided that the above copyright notice and this paragraph are
+ * duplicated in all such forms and that any documentation,
+ * advertising materials, and other materials related to such
+ * distribution and use acknowledge that the software was developed
+ * by the University of California, Berkeley. The name of the
+ * University may not be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
+ * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
+ *
+ * Van Jacobson (van@helios.ee.lbl.gov), Dec 31, 1989:
+ * - Initial distribution.
+ */
+
+#ifndef VJ_H
+#define VJ_H
+
+#include "lwip/ip.h"
+#include "lwip/tcp_impl.h"
+
+#define MAX_SLOTS 16 /* must be > 2 and < 256 */
+#define MAX_HDR 128
+
+/*
+ * Compressed packet format:
+ *
+ * The first octet contains the packet type (top 3 bits), TCP
+ * 'push' bit, and flags that indicate which of the 4 TCP sequence
+ * numbers have changed (bottom 5 bits). The next octet is a
+ * conversation number that associates a saved IP/TCP header with
+ * the compressed packet. The next two octets are the TCP checksum
+ * from the original datagram. The next 0 to 15 octets are
+ * sequence number changes, one change per bit set in the header
+ * (there may be no changes and there are two special cases where
+ * the receiver implicitly knows what changed -- see below).
+ *
+ * There are 5 numbers which can change (they are always inserted
+ * in the following order): TCP urgent pointer, window,
+ * acknowlegement, sequence number and IP ID. (The urgent pointer
+ * is different from the others in that its value is sent, not the
+ * change in value.) Since typical use of SLIP links is biased
+ * toward small packets (see comments on MTU/MSS below), changes
+ * use a variable length coding with one octet for numbers in the
+ * range 1 - 255 and 3 octets (0, MSB, LSB) for numbers in the
+ * range 256 - 65535 or 0. (If the change in sequence number or
+ * ack is more than 65535, an uncompressed packet is sent.)
+ */
+
+/*
+ * Packet types (must not conflict with IP protocol version)
+ *
+ * The top nibble of the first octet is the packet type. There are
+ * three possible types: IP (not proto TCP or tcp with one of the
+ * control flags set); uncompressed TCP (a normal IP/TCP packet but
+ * with the 8-bit protocol field replaced by an 8-bit connection id --
+ * this type of packet syncs the sender & receiver); and compressed
+ * TCP (described above).
+ *
+ * LSB of 4-bit field is TCP "PUSH" bit (a worthless anachronism) and
+ * is logically part of the 4-bit "changes" field that follows. Top
+ * three bits are actual packet type. For backward compatibility
+ * and in the interest of conserving bits, numbers are chosen so the
+ * IP protocol version number (4) which normally appears in this nibble
+ * means "IP packet".
+ */
+
+/* packet types */
+#define TYPE_IP 0x40
+#define TYPE_UNCOMPRESSED_TCP 0x70
+#define TYPE_COMPRESSED_TCP 0x80
+#define TYPE_ERROR 0x00
+
+/* Bits in first octet of compressed packet */
+#define NEW_C 0x40 /* flag bits for what changed in a packet */
+#define NEW_I 0x20
+#define NEW_S 0x08
+#define NEW_A 0x04
+#define NEW_W 0x02
+#define NEW_U 0x01
+
+/* reserved, special-case values of above */
+#define SPECIAL_I (NEW_S|NEW_W|NEW_U) /* echoed interactive traffic */
+#define SPECIAL_D (NEW_S|NEW_A|NEW_W|NEW_U) /* unidirectional data */
+#define SPECIALS_MASK (NEW_S|NEW_A|NEW_W|NEW_U)
+
+#define TCP_PUSH_BIT 0x10
+
+
+/*
+ * "state" data for each active tcp conversation on the wire. This is
+ * basically a copy of the entire IP/TCP header from the last packet
+ * we saw from the conversation together with a small identifier
+ * the transmit & receive ends of the line use to locate saved header.
+ */
+struct cstate {
+ struct cstate *cs_next; /* next most recently used state (xmit only) */
+ u_short cs_hlen; /* size of hdr (receive only) */
+ u_char cs_id; /* connection # associated with this state */
+ u_char cs_filler;
+ union {
+ char csu_hdr[MAX_HDR];
+ struct ip_hdr csu_ip; /* ip/tcp hdr from most recent packet */
+ } vjcs_u;
+};
+#define cs_ip vjcs_u.csu_ip
+#define cs_hdr vjcs_u.csu_hdr
+
+
+struct vjstat {
+ unsigned long vjs_packets; /* outbound packets */
+ unsigned long vjs_compressed; /* outbound compressed packets */
+ unsigned long vjs_searches; /* searches for connection state */
+ unsigned long vjs_misses; /* times couldn't find conn. state */
+ unsigned long vjs_uncompressedin; /* inbound uncompressed packets */
+ unsigned long vjs_compressedin; /* inbound compressed packets */
+ unsigned long vjs_errorin; /* inbound unknown type packets */
+ unsigned long vjs_tossed; /* inbound packets tossed because of error */
+};
+
+/*
+ * all the state data for one serial line (we need one of these per line).
+ */
+struct vjcompress {
+ struct cstate *last_cs; /* most recently used tstate */
+ u_char last_recv; /* last rcvd conn. id */
+ u_char last_xmit; /* last sent conn. id */
+ u_short flags;
+ u_char maxSlotIndex;
+ u_char compressSlot; /* Flag indicating OK to compress slot ID. */
+#if LINK_STATS
+ struct vjstat stats;
+#endif
+ struct cstate tstate[MAX_SLOTS]; /* xmit connection states */
+ struct cstate rstate[MAX_SLOTS]; /* receive connection states */
+};
+
+/* flag values */
+#define VJF_TOSS 1U /* tossing rcvd frames because of input err */
+
+extern void vj_compress_init (struct vjcompress *comp);
+extern u_int vj_compress_tcp (struct vjcompress *comp, struct pbuf *pb);
+extern void vj_uncompress_err (struct vjcompress *comp);
+extern int vj_uncompress_uncomp(struct pbuf *nb, struct vjcompress *comp);
+extern int vj_uncompress_tcp (struct pbuf **nb, struct vjcompress *comp);
+
+#endif /* VJ_H */
diff --git a/src/VBox/Devices/Network/lwip-new/src/netif/slipif.c b/src/VBox/Devices/Network/lwip-new/src/netif/slipif.c
new file mode 100644
index 00000000..137ba89d
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/src/netif/slipif.c
@@ -0,0 +1,546 @@
+/**
+ * @file
+ * SLIP Interface
+ *
+ */
+
+/*
+ * Copyright (c) 2001-2004 Swedish Institute of Computer Science.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. Neither the name of the Institute nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file is built upon the file: src/arch/rtxc/netif/sioslip.c
+ *
+ * Author: Magnus Ivarsson <magnus.ivarsson(at)volvo.com>
+ * Simon Goldschmidt
+ *
+ * Usage: This netif can be used in three ways:
+ * 1) For NO_SYS==0, an RX thread can be used which blocks on sio_read()
+ * until data is received.
+ * 2) In your main loop, call slipif_poll() to check for new RX bytes,
+ * completed packets are fed into netif->input().
+ * 3) Call slipif_received_byte[s]() from your serial RX ISR and
+ * slipif_process_rxqueue() from your main loop. ISR level decodes
+ * packets and puts completed packets on a queue which is fed into
+ * the stack from the main loop (needs SYS_LIGHTWEIGHT_PROT for
+ * pbuf_alloc to work on ISR level!).
+ *
+ */
+
+/*
+ * This is an arch independent SLIP netif. The specific serial hooks must be
+ * provided by another file. They are sio_open, sio_read/sio_tryread and sio_send
+ */
+
+#include "netif/slipif.h"
+#include "lwip/opt.h"
+
+#if LWIP_HAVE_SLIPIF
+
+#include "lwip/def.h"
+#include "lwip/pbuf.h"
+#include "lwip/stats.h"
+#include "lwip/snmp.h"
+#include "lwip/sio.h"
+#include "lwip/sys.h"
+
+#define SLIP_END 0xC0 /* 0300: start and end of every packet */
+#define SLIP_ESC 0xDB /* 0333: escape start (one byte escaped data follows) */
+#define SLIP_ESC_END 0xDC /* 0334: following escape: original byte is 0xC0 (END) */
+#define SLIP_ESC_ESC 0xDD /* 0335: following escape: original byte is 0xDB (ESC) */
+
+/** Maximum packet size that is received by this netif */
+#ifndef SLIP_MAX_SIZE
+#define SLIP_MAX_SIZE 1500
+#endif
+
+/** Define this to the interface speed for SNMP
+ * (sio_fd is the sio_fd_t returned by sio_open).
+ * The default value of zero means 'unknown'.
+ */
+#ifndef SLIP_SIO_SPEED
+#define SLIP_SIO_SPEED(sio_fd) 0
+#endif
+
+enum slipif_recv_state {
+ SLIP_RECV_NORMAL,
+ SLIP_RECV_ESCAPE,
+};
+
+struct slipif_priv {
+ sio_fd_t sd;
+ /* q is the whole pbuf chain for a packet, p is the current pbuf in the chain */
+ struct pbuf *p, *q;
+ u8_t state;
+ u16_t i, recved;
+#if SLIP_RX_FROM_ISR
+ struct pbuf *rxpackets;
+#endif
+};
+
+/**
+ * Send a pbuf doing the necessary SLIP encapsulation
+ *
+ * Uses the serial layer's sio_send()
+ *
+ * @param netif the lwip network interface structure for this slipif
+ * @param p the pbuf chaing packet to send
+ * @return always returns ERR_OK since the serial layer does not provide return values
+ */
+static err_t
+slipif_output(struct netif *netif, struct pbuf *p)
+{
+ struct slipif_priv *priv;
+ struct pbuf *q;
+ u16_t i;
+ u8_t c;
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
+ LWIP_ASSERT("p != NULL", (p != NULL));
+
+ LWIP_DEBUGF(SLIP_DEBUG, ("slipif_output(%"U16_F"): sending %"U16_F" bytes\n", (u16_t)netif->num, p->tot_len));
+ priv = netif->state;
+
+ /* Send pbuf out on the serial I/O device. */
+ /* Start with packet delimiter. */
+ sio_send(SLIP_END, priv->sd);
+
+ for (q = p; q != NULL; q = q->next) {
+ for (i = 0; i < q->len; i++) {
+ c = ((u8_t *)q->payload)[i];
+ switch (c) {
+ case SLIP_END:
+ /* need to escape this byte (0xC0 -> 0xDB, 0xDC) */
+ sio_send(SLIP_ESC, priv->sd);
+ sio_send(SLIP_ESC_END, priv->sd);
+ break;
+ case SLIP_ESC:
+ /* need to escape this byte (0xDB -> 0xDB, 0xDD) */
+ sio_send(SLIP_ESC, priv->sd);
+ sio_send(SLIP_ESC_ESC, priv->sd);
+ break;
+ default:
+ /* normal byte - no need for escaping */
+ sio_send(c, priv->sd);
+ break;
+ }
+ }
+ }
+ /* End with packet delimiter. */
+ sio_send(SLIP_END, priv->sd);
+ return ERR_OK;
+}
+
+/**
+ * Send a pbuf doing the necessary SLIP encapsulation
+ *
+ * Uses the serial layer's sio_send()
+ *
+ * @param netif the lwip network interface structure for this slipif
+ * @param p the pbuf chaing packet to send
+ * @param ipaddr the ip address to send the packet to (not used for slipif)
+ * @return always returns ERR_OK since the serial layer does not provide return values
+ */
+static err_t
+slipif_output_v4(struct netif *netif, struct pbuf *p, ip_addr_t *ipaddr)
+{
+ LWIP_UNUSED_ARG(ipaddr);
+ return slipif_output(netif, p);
+}
+
+#if LWIP_IPV6
+/**
+ * Send a pbuf doing the necessary SLIP encapsulation
+ *
+ * Uses the serial layer's sio_send()
+ *
+ * @param netif the lwip network interface structure for this slipif
+ * @param p the pbuf chaing packet to send
+ * @param ipaddr the ip address to send the packet to (not used for slipif)
+ * @return always returns ERR_OK since the serial layer does not provide return values
+ */
+static err_t
+slipif_output_v6(struct netif *netif, struct pbuf *p, ip6_addr_t *ipaddr)
+{
+ LWIP_UNUSED_ARG(ipaddr);
+ return slipif_output(netif, p);
+}
+#endif /* LWIP_IPV6 */
+
+/**
+ * Handle the incoming SLIP stream character by character
+ *
+ * @param netif the lwip network interface structure for this slipif
+ * @param c received character (multiple calls to this function will
+ * return a complete packet, NULL is returned before - used for polling)
+ * @return The IP packet when SLIP_END is received
+ */
+static struct pbuf*
+slipif_rxbyte(struct netif *netif, u8_t c)
+{
+ struct slipif_priv *priv;
+ struct pbuf *t;
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
+
+ priv = netif->state;
+
+ switch (priv->state) {
+ case SLIP_RECV_NORMAL:
+ switch (c) {
+ case SLIP_END:
+ if (priv->recved > 0) {
+ /* Received whole packet. */
+ /* Trim the pbuf to the size of the received packet. */
+ pbuf_realloc(priv->q, priv->recved);
+
+ LINK_STATS_INC(link.recv);
+
+ LWIP_DEBUGF(SLIP_DEBUG, ("slipif: Got packet (%"U16_F" bytes)\n", priv->recved));
+ t = priv->q;
+ priv->p = priv->q = NULL;
+ priv->i = priv->recved = 0;
+ return t;
+ }
+ return NULL;
+ case SLIP_ESC:
+ priv->state = SLIP_RECV_ESCAPE;
+ return NULL;
+ } /* end switch (c) */
+ break;
+ case SLIP_RECV_ESCAPE:
+ /* un-escape END or ESC bytes, leave other bytes
+ (although that would be a protocol error) */
+ switch (c) {
+ case SLIP_ESC_END:
+ c = SLIP_END;
+ break;
+ case SLIP_ESC_ESC:
+ c = SLIP_ESC;
+ break;
+ }
+ priv->state = SLIP_RECV_NORMAL;
+ break;
+ } /* end switch (priv->state) */
+
+ /* byte received, packet not yet completely received */
+ if (priv->p == NULL) {
+ /* allocate a new pbuf */
+ LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: alloc\n"));
+ priv->p = pbuf_alloc(PBUF_LINK, (PBUF_POOL_BUFSIZE - PBUF_LINK_HLEN), PBUF_POOL);
+
+ if (priv->p == NULL) {
+ LINK_STATS_INC(link.drop);
+ LWIP_DEBUGF(SLIP_DEBUG, ("slipif_input: no new pbuf! (DROP)\n"));
+ /* don't process any further since we got no pbuf to receive to */
+ return NULL;
+ }
+
+ if (priv->q != NULL) {
+ /* 'chain' the pbuf to the existing chain */
+ pbuf_cat(priv->q, priv->p);
+ } else {
+ /* p is the first pbuf in the chain */
+ priv->q = priv->p;
+ }
+ }
+
+ /* this automatically drops bytes if > SLIP_MAX_SIZE */
+ if ((priv->p != NULL) && (priv->recved <= SLIP_MAX_SIZE)) {
+ ((u8_t *)priv->p->payload)[priv->i] = c;
+ priv->recved++;
+ priv->i++;
+ if (priv->i >= priv->p->len) {
+ /* on to the next pbuf */
+ priv->i = 0;
+ if (priv->p->next != NULL && priv->p->next->len > 0) {
+ /* p is a chain, on to the next in the chain */
+ priv->p = priv->p->next;
+ } else {
+ /* p is a single pbuf, set it to NULL so next time a new
+ * pbuf is allocated */
+ priv->p = NULL;
+ }
+ }
+ }
+ return NULL;
+}
+
+/** Like slipif_rxbyte, but passes completed packets to netif->input
+ *
+ * @param netif The lwip network interface structure for this slipif
+ * @param data received character
+ */
+static void
+slipif_rxbyte_input(struct netif *netif, u8_t c)
+{
+ struct pbuf *p;
+ p = slipif_rxbyte(netif, c);
+ if (p != NULL) {
+ if (netif->input(p, netif) != ERR_OK) {
+ pbuf_free(p);
+ }
+ }
+}
+
+#if SLIP_USE_RX_THREAD
+/**
+ * The SLIP input thread.
+ *
+ * Feed the IP layer with incoming packets
+ *
+ * @param nf the lwip network interface structure for this slipif
+ */
+static void
+slipif_loop_thread(void *nf)
+{
+ u8_t c;
+ struct netif *netif = (struct netif *)nf;
+ struct slipif_priv *priv = (struct slipif_priv *)netif->state;
+
+ while (1) {
+ if (sio_read(priv->sd, &c, 1) > 0) {
+ slipif_rxbyte_input(netif, c);
+ }
+ }
+}
+#endif /* SLIP_USE_RX_THREAD */
+
+/**
+ * SLIP netif initialization
+ *
+ * Call the arch specific sio_open and remember
+ * the opened device in the state field of the netif.
+ *
+ * @param netif the lwip network interface structure for this slipif
+ * @return ERR_OK if serial line could be opened,
+ * ERR_MEM if no memory could be allocated,
+ * ERR_IF is serial line couldn't be opened
+ *
+ * @note netif->num must contain the number of the serial port to open
+ * (0 by default). If netif->state is != NULL, it is interpreted as an
+ * u8_t pointer pointing to the serial port number instead of netif->num.
+ *
+ */
+err_t
+slipif_init(struct netif *netif)
+{
+ struct slipif_priv *priv;
+ u8_t sio_num;
+
+ LWIP_DEBUGF(SLIP_DEBUG, ("slipif_init: netif->num=%"U16_F"\n", (u16_t)netif->num));
+
+ /* Allocate private data */
+ priv = (struct slipif_priv *)mem_malloc(sizeof(struct slipif_priv));
+ if (!priv) {
+ return ERR_MEM;
+ }
+
+ netif->name[0] = 's';
+ netif->name[1] = 'l';
+ netif->output = slipif_output_v4;
+#if LWIP_IPV6
+ netif->output_ip6 = slipif_output_v6;
+#endif /* LWIP_IPV6 */
+ netif->mtu = SLIP_MAX_SIZE;
+ netif->flags |= NETIF_FLAG_POINTTOPOINT;
+
+ /* netif->state or netif->num contain the port number */
+ if (netif->state != NULL) {
+ sio_num = *(u8_t*)netif->state;
+ } else {
+ sio_num = netif->num;
+ }
+ /* Try to open the serial port. */
+ priv->sd = sio_open(sio_num);
+ if (!priv->sd) {
+ /* Opening the serial port failed. */
+ mem_free(priv);
+ return ERR_IF;
+ }
+
+ /* Initialize private data */
+ priv->p = NULL;
+ priv->q = NULL;
+ priv->state = SLIP_RECV_NORMAL;
+ priv->i = 0;
+ priv->recved = 0;
+#if SLIP_RX_FROM_ISR
+ priv->rxpackets = NULL;
+#endif
+
+ netif->state = priv;
+
+ /* initialize the snmp variables and counters inside the struct netif */
+ NETIF_INIT_SNMP(netif, snmp_ifType_slip, SLIP_SIO_SPEED(priv->sd));
+
+#if SLIP_USE_RX_THREAD
+ /* Create a thread to poll the serial line. */
+ sys_thread_new(SLIPIF_THREAD_NAME, slipif_loop_thread, netif,
+ SLIPIF_THREAD_STACKSIZE, SLIPIF_THREAD_PRIO);
+#endif /* SLIP_USE_RX_THREAD */
+ return ERR_OK;
+}
+
+/**
+ * Polls the serial device and feeds the IP layer with incoming packets.
+ *
+ * @param netif The lwip network interface structure for this slipif
+ */
+void
+slipif_poll(struct netif *netif)
+{
+ u8_t c;
+ struct slipif_priv *priv;
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
+
+ priv = (struct slipif_priv *)netif->state;
+
+ while (sio_tryread(priv->sd, &c, 1) > 0) {
+ slipif_rxbyte_input(netif, c);
+ }
+}
+
+#if SLIP_RX_FROM_ISR
+/**
+ * Feeds the IP layer with incoming packets that were receive
+ *
+ * @param netif The lwip network interface structure for this slipif
+ */
+void
+slipif_process_rxqueue(struct netif *netif)
+{
+ struct slipif_priv *priv;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
+
+ priv = (struct slipif_priv *)netif->state;
+
+ SYS_ARCH_PROTECT(old_level);
+ while (priv->rxpackets != NULL) {
+ struct pbuf *p = priv->rxpackets;
+#if SLIP_RX_QUEUE
+ /* dequeue packet */
+ struct pbuf *q = p;
+ while ((q->len != q->tot_len) && (q->next != NULL)) {
+ q = q->next;
+ }
+ priv->rxpackets = q->next;
+ q->next = NULL;
+#else /* SLIP_RX_QUEUE */
+ priv->rxpackets = NULL;
+#endif /* SLIP_RX_QUEUE */
+ SYS_ARCH_UNPROTECT(old_level);
+ if (netif->input(p, netif) != ERR_OK) {
+ pbuf_free(p);
+ }
+ SYS_ARCH_PROTECT(old_level);
+ }
+}
+
+/** Like slipif_rxbyte, but queues completed packets.
+ *
+ * @param netif The lwip network interface structure for this slipif
+ * @param data Received serial byte
+ */
+static void
+slipif_rxbyte_enqueue(struct netif *netif, u8_t data)
+{
+ struct pbuf *p;
+ struct slipif_priv *priv = (struct slipif_priv *)netif->state;
+ SYS_ARCH_DECL_PROTECT(old_level);
+
+ p = slipif_rxbyte(netif, data);
+ if (p != NULL) {
+ SYS_ARCH_PROTECT(old_level);
+ if (priv->rxpackets != NULL) {
+#if SLIP_RX_QUEUE
+ /* queue multiple pbufs */
+ struct pbuf *q = p;
+ while(q->next != NULL) {
+ q = q->next;
+ }
+ q->next = p;
+ } else {
+#else /* SLIP_RX_QUEUE */
+ pbuf_free(priv->rxpackets);
+ }
+ {
+#endif /* SLIP_RX_QUEUE */
+ priv->rxpackets = p;
+ }
+ SYS_ARCH_UNPROTECT(old_level);
+ }
+}
+
+/**
+ * Process a received byte, completed packets are put on a queue that is
+ * fed into IP through slipif_process_rxqueue().
+ *
+ * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled.
+ *
+ * @param netif The lwip network interface structure for this slipif
+ * @param data received character
+ */
+void
+slipif_received_byte(struct netif *netif, u8_t data)
+{
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
+ slipif_rxbyte_enqueue(netif, data);
+}
+
+/**
+ * Process multiple received byte, completed packets are put on a queue that is
+ * fed into IP through slipif_process_rxqueue().
+ *
+ * This function can be called from ISR if SYS_LIGHTWEIGHT_PROT is enabled.
+ *
+ * @param netif The lwip network interface structure for this slipif
+ * @param data received character
+ * @param len Number of received characters
+ */
+void
+slipif_received_bytes(struct netif *netif, u8_t *data, u8_t len)
+{
+ u8_t i;
+ u8_t *rxdata = data;
+ LWIP_ASSERT("netif != NULL", (netif != NULL));
+ LWIP_ASSERT("netif->state != NULL", (netif->state != NULL));
+
+ for (i = 0; i < len; i++, rxdata++) {
+ slipif_rxbyte_enqueue(netif, *rxdata);
+ }
+}
+#endif /* SLIP_RX_FROM_ISR */
+
+#endif /* LWIP_HAVE_SLIPIF */
diff --git a/src/VBox/Devices/Network/lwip-new/vbox/Makefile.kup b/src/VBox/Devices/Network/lwip-new/vbox/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/vbox/Makefile.kup
diff --git a/src/VBox/Devices/Network/lwip-new/vbox/VBoxLwipCore.cpp b/src/VBox/Devices/Network/lwip-new/vbox/VBoxLwipCore.cpp
new file mode 100644
index 00000000..7ba57892
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/vbox/VBoxLwipCore.cpp
@@ -0,0 +1,230 @@
+/* $Id: VBoxLwipCore.cpp $ */
+/** @file
+ * VBox Lwip Core Initiatetor/Finilizer.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/**
+ * @todo: this should be somehow shared with with DevINIP, because
+ * we want that every NAT and DevINIP instance uses a initialized LWIP
+ * initialization of LWIP should happen on iLWIPInitiatorCounter 0 -> 1.
+ * see pfnConstruct/Destruct.
+ *
+ * @note: see comment to DevINIP.cpp:DevINIPConfigured
+ * @note: perhaps initilization stuff would be better move out of NAT driver,
+ * because we have to deal with attaching detaching NAT driver at runtime.
+ */
+#include <iprt/types.h>
+#include "VBoxLwipCore.h"
+/** @todo lwip or nat ? */
+#define LOG_GROUP LOG_GROUP_DRV_NAT
+#include <iprt/cpp/lock.h>
+#include <iprt/timer.h>
+#include <iprt/errcore.h>
+#include <VBox/log.h>
+
+extern "C" {
+#include "lwip/opt.h"
+#include "lwip/sys.h"
+#include "netif/etharp.h"
+#include "lwip/stats.h"
+#include "lwip/mem.h"
+#include "lwip/memp.h"
+#include "lwip/tcp_impl.h"
+#include "lwip/tcpip.h"
+}
+
+typedef struct LWIPCOREUSERCALLBACK
+{
+ PFNRT1 pfn;
+ void *pvUser;
+} LWIPCOREUSERCALLBACK, *PLWIPCOREUSERCALLBACK;
+
+
+RTCLockMtx g_mtxLwip;
+
+typedef struct LWIPCORE
+{
+ int iLWIPInitiatorCounter;
+ sys_sem_t LwipTcpIpSem;
+} LWIPCORE;
+
+static LWIPCORE g_LwipCore;
+
+
+/**
+ * @note this function executes on TCPIP thread.
+ */
+static void lwipCoreUserCallback(void *pvArg) RT_NOTHROW_DEF
+{
+ LogFlowFunc(("ENTER: pvArg:%p\n", pvArg));
+
+ PLWIPCOREUSERCALLBACK pUserClbk = (PLWIPCOREUSERCALLBACK)pvArg;
+ if (pUserClbk != NULL && pUserClbk->pfn != NULL)
+ pUserClbk->pfn(pUserClbk->pvUser);
+
+ /* wake up caller on EMT/main */
+ sys_sem_signal(&g_LwipCore.LwipTcpIpSem);
+ LogFlowFuncLeave();
+}
+
+
+/**
+ * @note this function executes on TCPIP thread.
+ */
+static void lwipCoreInitDone(void *pvArg) RT_NOTHROW_DEF
+{
+ LogFlowFunc(("ENTER: pvArg:%p\n", pvArg));
+
+ /* ... init code goes here if need be ... */
+
+ lwipCoreUserCallback(pvArg);
+ LogFlowFuncLeave();
+}
+
+
+/**
+ * @note this function executes on TCPIP thread.
+ */
+static void lwipCoreFiniDone(void *pvArg) RT_NOTHROW_DEF
+{
+ LogFlowFunc(("ENTER: pvArg:%p\n", pvArg));
+
+ /* ... fini code goes here if need be ... */
+
+ lwipCoreUserCallback(pvArg);
+ LogFlowFuncLeave();
+}
+
+
+/**
+ * This function initializes lwip core once. Further NAT instancies
+ * should just add netifs configured according their needs.
+ *
+ * We're on EMT-n or on the main thread of a network service, and we
+ * want to execute something on the lwip tcpip thread.
+ */
+int vboxLwipCoreInitialize(PFNRT1 pfnCallback, void *pvCallbackArg)
+{
+ int rc = VINF_SUCCESS;
+ int lwipRc = ERR_OK;
+ LogFlowFuncEnter();
+
+ LWIPCOREUSERCALLBACK callback;
+ callback.pfn = pfnCallback;
+ callback.pvUser = pvCallbackArg;
+
+ {
+ RTCLock lock(g_mtxLwip);
+
+ if (g_LwipCore.iLWIPInitiatorCounter == 0)
+ {
+ lwipRc = sys_sem_new(&g_LwipCore.LwipTcpIpSem, 0);
+ if (lwipRc != ERR_OK)
+ {
+ LogFlowFunc(("sys_sem_new error %d\n", lwipRc));
+ goto done;
+ }
+
+ tcpip_init(lwipCoreInitDone, &callback);
+ }
+ else
+ {
+ lwipRc = tcpip_callback(lwipCoreUserCallback, &callback);
+ if (lwipRc != ERR_OK)
+ {
+ LogFlowFunc(("tcpip_callback error %d\n", lwipRc));
+ goto done;
+ }
+ }
+
+ sys_sem_wait(&g_LwipCore.LwipTcpIpSem);
+ ++g_LwipCore.iLWIPInitiatorCounter;
+ }
+ done:
+ if (lwipRc != ERR_OK)
+ {
+ /** @todo map lwip error code? */
+ rc = VERR_INTERNAL_ERROR;
+ }
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+
+/**
+ * This function decrement lwip reference counter
+ * and calls tcpip thread termination function.
+ */
+void vboxLwipCoreFinalize(PFNRT1 pfnCallback, void *pvCallbackArg)
+{
+ int lwipRc = ERR_OK;
+ LogFlowFuncEnter();
+
+ LWIPCOREUSERCALLBACK callback;
+ callback.pfn = pfnCallback;
+ callback.pvUser = pvCallbackArg;
+
+ {
+ RTCLock lock(g_mtxLwip);
+
+ if (g_LwipCore.iLWIPInitiatorCounter == 1)
+ {
+ /*
+ * TCPIP_MSG_CALLBACK_TERMINATE is like a static callback,
+ * but causes tcpip_thread() to return afterward.
+ *
+ * This should probably be hidden in a function inside
+ * lwip, but for it to be static callback the semaphore
+ * dance should also be done inside that function. There
+ * is tcpip_msg::sem, but it seems to be unused and may be
+ * gone in future versions of lwip.
+ */
+ struct tcpip_msg *msg = (struct tcpip_msg *)memp_malloc(MEMP_TCPIP_MSG_API);
+ if (msg)
+ {
+ msg->type = TCPIP_MSG_CALLBACK_TERMINATE;
+ msg->msg.cb.function = lwipCoreFiniDone;
+ msg->msg.cb.ctx = &callback;
+
+ lwipRc = tcpip_callbackmsg((struct tcpip_callback_msg *)msg);
+ if (lwipRc != ERR_OK)
+ LogFlowFunc(("tcpip_callback_msg error %d\n", lwipRc));
+ }
+ else
+ LogFlowFunc(("memp_malloc no memory\n"));
+ }
+ else
+ {
+ lwipRc = tcpip_callback(lwipCoreUserCallback, &callback);
+ if (lwipRc != ERR_OK)
+ LogFlowFunc(("tcpip_callback error %d\n", lwipRc));
+ }
+
+ if (lwipRc == ERR_OK)
+ sys_sem_wait(&g_LwipCore.LwipTcpIpSem);
+ }
+
+ LogFlowFuncLeave();
+}
diff --git a/src/VBox/Devices/Network/lwip-new/vbox/VBoxLwipCore.h b/src/VBox/Devices/Network/lwip-new/vbox/VBoxLwipCore.h
new file mode 100644
index 00000000..9c917d3b
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/vbox/VBoxLwipCore.h
@@ -0,0 +1,41 @@
+/* $Id: VBoxLwipCore.h $ */
+
+/** @file
+ * VBox Lwip Core Initiatetor/Finilizer.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Network_lwip_new_vbox_VBoxLwipCore_h
+#define VBOX_INCLUDED_SRC_Network_lwip_new_vbox_VBoxLwipCore_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+/**
+ * Initializes LWIP core, and do callback on tcp/ip thread.
+ */
+int vboxLwipCoreInitialize(PFNRT1 pfnCallback, void * pfnCallbackArg);
+void vboxLwipCoreFinalize(PFNRT1 pfnCallback, void * pfnCallbackArg);
+
+#endif /* !VBOX_INCLUDED_SRC_Network_lwip_new_vbox_VBoxLwipCore_h */
diff --git a/src/VBox/Devices/Network/lwip-new/vbox/include/arch/bpstruct.h b/src/VBox/Devices/Network/lwip-new/vbox/include/arch/bpstruct.h
new file mode 100644
index 00000000..1d81e3f7
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/vbox/include/arch/bpstruct.h
@@ -0,0 +1 @@
+#pragma pack(push,1)
diff --git a/src/VBox/Devices/Network/lwip-new/vbox/include/arch/cc.h b/src/VBox/Devices/Network/lwip-new/vbox/include/arch/cc.h
new file mode 100644
index 00000000..90ed1b53
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/vbox/include/arch/cc.h
@@ -0,0 +1,62 @@
+#ifndef VBOX_ARCH_CC_H_
+#define VBOX_ARCH_CC_H_
+
+#ifndef LOG_GROUP
+#define LOG_GROUP LOG_GROUP_DRV_LWIP
+#endif
+#include <VBox/log.h>
+#include <iprt/stdint.h>
+#include <iprt/cdefs.h>
+#include <iprt/assert.h>
+#include <iprt/time.h>
+
+#ifndef RT_OS_WINDOWS
+# define LWIP_TIMEVAL_PRIVATE 0
+#endif
+
+typedef uint8_t u8_t;
+#ifndef RT_OS_SOLARIS
+typedef int8_t s8_t;
+#else
+/* Solaris /usr/include/sys/int_types.h incorrectly defines int8_t as "char" */
+typedef signed char s8_t;
+#endif
+typedef uint16_t u16_t;
+typedef int16_t s16_t;
+typedef uint32_t u32_t;
+typedef int32_t s32_t;
+
+typedef uintptr_t mem_ptr_t;
+
+#ifdef _MSC_VER
+# define PACK_STRUCT_FIELD(x) x
+# define PACK_STRUCT_STRUCT
+# define PACK_STRUCT_USE_INCLUDES
+# if _MSC_VER < 1600
+# define LWIP_PROVIDE_ERRNO
+# else
+# include <errno.h>
+# endif
+# pragma warning (disable: 4103)
+#elif defined(__GNUC__)
+# define PACK_STRUCT_FIELD(x) x
+# define PACK_STRUCT_STRUCT __attribute__((__packed__))
+# define PACK_STRUCT_BEGIN
+# define PACK_STRUCT_END
+# include <errno.h>
+#else
+# error This header file has not been ported yet for this compiler.
+#endif
+
+/* Provide byte order hint. */
+#undef BYTE_ORDER
+#define BYTE_ORDER LITTLE_ENDIAN
+
+#ifdef DEBUG
+#define LWIP_PLATFORM_DIAG(x) Log(x)
+#else /* !DEBUG */
+#define LWIP_PLATFORM_DIAG(x) LogRel(x)
+#endif /* !DEBUG */
+#define LWIP_PLATFORM_ASSERT(x) AssertReleaseMsgFailed((x))
+
+#endif /* !VBOX_ARCH_CC_H_ */
diff --git a/src/VBox/Devices/Network/lwip-new/vbox/include/arch/epstruct.h b/src/VBox/Devices/Network/lwip-new/vbox/include/arch/epstruct.h
new file mode 100644
index 00000000..65898b54
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/vbox/include/arch/epstruct.h
@@ -0,0 +1 @@
+#pragma pack(pop)
diff --git a/src/VBox/Devices/Network/lwip-new/vbox/include/arch/perf.h b/src/VBox/Devices/Network/lwip-new/vbox/include/arch/perf.h
new file mode 100644
index 00000000..5b364db0
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/vbox/include/arch/perf.h
@@ -0,0 +1,7 @@
+#ifndef VBOX_ARCH_PERF_H_
+#define VBOX_ARCH_PERF_H_
+
+#define PERF_START
+#define PERF_STOP(x)
+
+#endif /* !VBOX_ARCH_PERF_H_ */
diff --git a/src/VBox/Devices/Network/lwip-new/vbox/include/arch/sys_arch.h b/src/VBox/Devices/Network/lwip-new/vbox/include/arch/sys_arch.h
new file mode 100644
index 00000000..6a83cea3
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/vbox/include/arch/sys_arch.h
@@ -0,0 +1,39 @@
+#ifndef VBOX_ARCH_SYS_ARCH_H_
+#define VBOX_ARCH_SYS_ARCH_H_
+
+#include <iprt/semaphore.h>
+#include <iprt/thread.h>
+#ifdef RT_OS_DARWIN
+#include <sys/time.h>
+#endif
+
+/** NULL value for a mbox. */
+#define SYS_MBOX_NULL NULL
+
+/** NULL value for a mutex semaphore. */
+#define SYS_SEM_NULL NIL_RTSEMEVENT
+
+/** The IPRT event semaphore ID just works fine for this type. */
+typedef RTSEMEVENT sys_sem_t;
+
+
+/** The opaque type of a mbox. */
+typedef void *sys_mbox_t;
+
+/** The IPRT thread ID just works fine for this type. */
+typedef RTTHREAD sys_thread_t;
+
+#if SYS_LIGHTWEIGHT_PROT
+/** This is just a dummy. The implementation doesn't need anything. */
+typedef void *sys_prot_t;
+#endif
+
+/** Check if an mbox is valid/allocated: return 1 for valid, 0 for invalid */
+int sys_mbox_valid(sys_mbox_t *mbox);
+/** Set an mbox invalid so that sys_mbox_valid returns 0 */
+void sys_mbox_set_invalid(sys_mbox_t *mbox);
+
+#define sys_sem_valid(sem) ((sem) && (*(sem)))
+#define sys_sem_set_invalid(sem) do {} while(0)
+
+#endif /* !VBOX_ARCH_SYS_ARCH_H_ */
diff --git a/src/VBox/Devices/Network/lwip-new/vbox/include/lwip-log.h b/src/VBox/Devices/Network/lwip-new/vbox/include/lwip-log.h
new file mode 100644
index 00000000..10c47f21
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/vbox/include/lwip-log.h
@@ -0,0 +1,104 @@
+/* -*- indent-tabs-mode: nil; -*- */
+#ifndef __VBOX_LWIP_LOG_H__
+#define __VBOX_LWIP_LOG_H__
+
+#include <VBox/log.h>
+
+#ifdef LWIP_DEBUG
+/*
+ * All LWIP_DBG_* constants fit into a byte, so we use upper bits to
+ * encode the VBox log group.
+ *
+ * Mapping between FOO_DEBUG and LOG_GROUP_LWIP_FOO is straightforward
+ * except for IP4 where extra '4' was added to the group names to make
+ * it possible to specify lwip_ip4* instead of lwip_ip*, where the
+ * latter would enable both IP4 and IP6 logging.
+ *
+ * We ignore LWIP_DBG_STATE &c since in our scheme they would traslate
+ * into additional log groups and require combinatorial explosion. We
+ * probably can use LWIP_DBG_TYPES_ON for finer selection if need be
+ * (for internal debugging only, as it requires recompilation).
+ *
+ * Debug levels are mapped to RT debug levels so lwip's default level
+ * ends up as RT's level4. Non-default levels are currently not used
+ * much in lwip sources, so enable l4 to get the logs.
+ *
+ * Caveat: Slight snag. The LOG_GROUP_LWIP_XXXX are enum values and
+ * the lwIP XXXX_DEBUG macros are used in \#if XXXX_DEBUG
+ * tests around the place. This make MSC raise complaint
+ * C4668, that e.g. 'LOG_GROUP_LWIP_IP4' is not defined as a
+ * preprocessor macro and therefore replaced with '0'.
+ * However, that works just fine because we or LWIP_DBG_ON so
+ * the test is true despite the warning. Thus the pragma
+ * below.
+ */
+# ifdef _MSC_VER
+# pragma warning(disable:4668)
+# endif
+
+# define LWIP_DEBUGF_LOG_GROUP_SHIFT 8
+# define LWIP_DEBUGF_LOG_GROUP(_g) \
+ (((_g) << LWIP_DEBUGF_LOG_GROUP_SHIFT) | LWIP_DBG_ON)
+
+# define API_LIB_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_API_LIB)
+# define API_MSG_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_API_MSG)
+# define ETHARP_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_ETHARP)
+# define ICMP_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_ICMP)
+# define IGMP_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_IGMP)
+# define INET_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_INET)
+# define IP_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_IP4)
+# define IP_REASS_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_IP4_REASS)
+# define IP6_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_IP6)
+# define MEM_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_MEM)
+# define MEMP_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_MEMP)
+# define NETIF_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_NETIF)
+# define PBUF_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_PBUF)
+# define RAW_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_RAW)
+# define SOCKETS_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_SOCKETS)
+# define SYS_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_SYS)
+# define TCP_CWND_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_TCP_CWND)
+# define TCP_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_TCP)
+# define TCP_FR_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_TCP_FR)
+# define TCP_INPUT_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_TCP_INPUT)
+# define TCP_OUTPUT_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_TCP_OUTPUT)
+# define TCP_QLEN_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_TCP_QLEN)
+# define TCP_RST_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_TCP_RST)
+# define TCP_RTO_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_TCP_RTO)
+# define TCP_WND_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_TCP_WND)
+# define TCPIP_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_TCPIP)
+# define TIMERS_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_TIMERS)
+# define UDP_DEBUG LWIP_DEBUGF_LOG_GROUP(LOG_GROUP_LWIP_UDP)
+
+/*
+ * The following symbols are for debugging of modules that are not
+ * compiled in. They are listed here for reference but there're no
+ * log groups defined for them currently.
+ */
+# undef AUTOIP_DEBUG
+# undef DHCP_DEBUG
+# undef DNS_DEBUG
+# undef PPP_DEBUG
+# undef SLIP_DEBUG
+# undef SNMP_MIB_DEBUG
+# undef SNMP_MSG_DEBUG
+
+# ifdef LOG_ENABLED
+
+# define LWIP_DEBUGF(_when, _args) \
+ do { \
+ const VBOXLOGGROUP _group = (_when) >> LWIP_DEBUGF_LOG_GROUP_SHIFT; \
+ if (_group >= LOG_GROUP_DEFAULT) { \
+ /* severe => l1; serious => l2; warning => l3; default => l4 */ \
+ const unsigned int _level = 1U << (LWIP_DBG_MASK_LEVEL + 1 - ((_when) & LWIP_DBG_MASK_LEVEL)); \
+ LogIt(_level, _group, _args); \
+ } \
+ } while (0)
+
+# else /* !LOG_ENABLED */
+
+# define LWIP_DEBUGF(_when, _args) do { } while (0)
+
+# endif /* !LOG_ENABLED */
+
+#endif /* LWIP_DEBUG */
+#endif /* !__VBOX_LWIP_LOG_H__ */
diff --git a/src/VBox/Devices/Network/lwip-new/vbox/include/lwip-namespace.h b/src/VBox/Devices/Network/lwip-new/vbox/include/lwip-namespace.h
new file mode 100644
index 00000000..3aa59bc3
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/vbox/include/lwip-namespace.h
@@ -0,0 +1,227 @@
+/*
+ * Hack to avoid function name collisions with slirp or any other code.
+ * Include at the end of your lwipopts.h
+ */
+#ifndef _VBOX_LWIP_NAMESPACE_H_
+#define _VBOX_LWIP_NAMESPACE_H_
+
+#define api_msg_input lwip_api_msg_input
+#define api_msg_post lwip_api_msg_post
+#define etharp_arp_input lwip_etharp_arp_input
+#define etharp_find_addr lwip_etharp_find_addr
+#define etharp_ip_input lwip_etharp_ip_input
+#define etharp_output lwip_etharp_output
+#define etharp_query lwip_etharp_query
+#define etharp_request lwip_etharp_request
+#define etharp_tmr lwip_etharp_tmr
+#define icmp_dest_unreach lwip_icmp_dest_unreach
+#define icmp_input lwip_icmp_input
+#define inet_chksum lwip_inet_chksum
+#define inet_chksum_pbuf lwip_inet_chksum_pbuf
+#define inet_chksum_pseudo lwip_inet_chksum_pseudo
+#define lwip_inet_aton(cp, addr) ipaddr_aton(cp, (ip_addr_t*)(addr))
+#define ip_addr_any lwip_ip_addr_any
+#define ip_addr_broadcast lwip_ip_addr_broadcast
+#ifdef ip_addr_isbroadcast
+# undef ip_addr_isbroadcast
+# if defined(ip4_addr_isbroadcast)
+# define lwip_ip_addr_isbroadcast(ipaddr. netif) lwip_ip4_addr_isbroadcast((ipaddr)->addr, (netif))
+# define ip4_addr_isbroadcast lwip_ip4_addr_isbroadcast
+# endif
+#endif
+#define ip_frag lwip_ip_frag
+#define ip_frag_init lwip_ip_frag_init
+#if 0
+#define ip_init lwip_ip_init
+#endif
+#define ip_input lwip_ip_input
+#define ip_output lwip_ip_output
+#define ip_output_if lwip_ip_output_if
+#define ip_reass lwip_ip_reass
+#define ip_reass_tmr lwip_ip_reass_tmr
+#define ip_route lwip_ip_route
+#define netbuf_alloc lwip_netbuf_alloc
+#define netbuf_chain lwip_netbuf_chain
+#define netbuf_data lwip_netbuf_data
+#define netbuf_delete lwip_netbuf_delete
+#define netbuf_first lwip_netbuf_first
+#define netbuf_free lwip_netbuf_free
+#define netbuf_new lwip_netbuf_new
+#define netbuf_next lwip_netbuf_next
+#define netbuf_ref lwip_netbuf_ref
+#define netconn_accept lwip_netconn_accept
+#if 0
+#define netconn_addr lwip_netconn_addr
+#endif
+#define netconn_bind lwip_netconn_bind
+#define netconn_close lwip_netconn_close
+#define netconn_connect lwip_netconn_connect
+#define netconn_delete lwip_netconn_delete
+#define netconn_disconnect lwip_netconn_disconnect
+#if 0
+#define netconn_err lwip_netconn_err
+#define netconn_listen lwip_netconn_listen
+#define netconn_new lwip_netconn_new
+#define netconn_new_with_callback lwip_netconn_new_with_callback
+#endif
+#define netconn_new_with_proto_and_callback lwip_netconn_new_with_proto_and_callback
+#if 0
+#define netconn_peer lwip_netconn_peer
+#endif
+#define netconn_recv lwip_netconn_recv
+#define netconn_send lwip_netconn_send
+#if 0
+#define netconn_type lwip_netconn_type
+#define netconn_write lwip_netconn_write
+#endif
+#define netif_add lwip_netif_add
+#define netif_default lwip_netif_default
+#define netif_find lwip_netif_find
+#define netif_init lwip_netif_init
+#define netif_list lwip_netif_list
+#define netif_remove lwip_netif_remove
+#define netif_set_addr lwip_netif_set_addr
+#define netif_set_default lwip_netif_set_default
+#define netif_set_down lwip_netif_set_down
+#define netif_set_gw lwip_netif_set_gw
+#define netif_set_ipaddr lwip_netif_set_ipaddr
+#define netif_set_netmask lwip_netif_set_netmask
+#define netif_set_up lwip_netif_set_up
+#if MEM_LIBC_MALLOC == 0
+#if MEM_USE_POOLS == 0
+#define mem_init lwip_mem_init
+#define mem_trim lwip_mem_trim
+#endif /* !MEM_USE_POOLS */
+#define mem_malloc lwip_mem_malloc
+#define mem_calloc lwip_mem_calloc
+#define mem_free lwip_mem_free
+#endif /* !MEM_LIBC_MALLOC */
+#define memp_free lwip_memp_free
+#define memp_init lwip_memp_init
+#define memp_malloc lwip_memp_malloc
+#define pbuf_alloc lwip_pbuf_alloc
+#define pbuf_cat lwip_pbuf_cat
+#define pbuf_chain lwip_pbuf_chain
+#define pbuf_clen lwip_pbuf_clen
+#define pbuf_dechain lwip_pbuf_dechain
+#define pbuf_dequeue lwip_pbuf_dequeue
+#define pbuf_free lwip_pbuf_free
+#define pbuf_header lwip_pbuf_header
+#if 0
+#define pbuf_init lwip_pbuf_init
+#endif
+#define pbuf_queue lwip_pbuf_queue
+#define pbuf_realloc lwip_pbuf_realloc
+#define pbuf_ref lwip_pbuf_ref
+#define pbuf_take lwip_pbuf_take
+#define raw_bind lwip_raw_bind
+#define raw_connect lwip_raw_connect
+#if 0
+#define raw_init lwip_raw_init
+#endif
+#define raw_input lwip_raw_input
+#define raw_new lwip_raw_new
+#define raw_recv lwip_raw_recv
+#define raw_remove lwip_raw_remove
+#define raw_send lwip_raw_send
+#define raw_sendto lwip_raw_sendto
+#define stats_init lwip_stats_init
+#define sys_arch_mbox_fetch lwip_sys_arch_mbox_fetch
+#if 0 /* XXX: cf. lwip/sys.h which misinterprets this */
+#define sys_arch_mbox_tryfetch lwip_sys_arch_mbox_tryfetch
+#endif
+#define sys_arch_protect lwip_sys_arch_protect
+#define sys_arch_sem_wait lwip_sys_arch_sem_wait
+#define sys_arch_timeouts lwip_sys_arch_timeouts
+#define sys_arch_unprotect lwip_sys_arch_unprotect
+#define sys_init lwip_sys_init
+#if 0
+#define sys_mbox_fetch lwip_sys_mbox_fetch
+#endif
+#define sys_mbox_free lwip_sys_mbox_free
+#define sys_mbox_new lwip_sys_mbox_new
+#define sys_mbox_post lwip_sys_mbox_post
+#define sys_thread_new lwip_sys_thread_new
+#define sys_msleep lwip_sys_msleep
+#define sys_mbox_set_invalid lwip_sys_mbox_set_invalid
+#define sys_mbox_valid lwip_sys_mbox_valid
+#if 1
+#define sys_sem_wait_timeout lwip_sys_sem_wait_timeout
+#define sys_sem_free lwip_sys_sem_free
+#define sys_sem_new lwip_sys_sem_new
+#define sys_sem_signal lwip_sys_sem_signal
+#define lwip_sys_sem_wait sys_sem_wait
+#define sys_arch_sem_wait lwip_sys_arch_sem_wait
+#endif
+#define sys_timeout_debug lwip_sys_timeout_debug
+#define sys_untimeout lwip_sys_untimeout
+#define tcp_abort lwip_tcp_abort
+#define tcp_accept lwip_tcp_accept
+#define tcp_active_pcbs lwip_tcp_active_pcbs
+#define tcp_alloc lwip_tcp_alloc
+#define tcp_arg lwip_tcp_arg
+#define tcp_backoff lwip_tcp_backoff
+#define tcp_bind lwip_tcp_bind
+#define tcp_close lwip_tcp_close
+#define tcp_connect lwip_tcp_connect
+#define tcp_enqueue lwip_tcp_enqueue
+#define tcp_err lwip_tcp_err
+#define tcp_fasttmr lwip_tcp_fasttmr
+#define tcp_init lwip_tcp_init
+#define tcp_input lwip_tcp_input
+#define tcp_input_pcb lwip_tcp_input_pcb
+#define tcp_keepalive lwip_tcp_keepalive
+#if defined(tcp_listen)
+# undef tcp_listen
+# define tcp_listen(pcb) lwip_tcp_listen_with_backlog(pcb, TCP_DEFAULT_LISTEN_BACKLOG)
+#endif
+#define tcp_listen_with_backlog lwip_tcp_listen_with_backlog
+#define tcp_listen_pcbs lwip_tcp_listen_pcbs
+#define tcp_new lwip_tcp_new
+#define tcp_next_iss lwip_tcp_next_iss
+#define tcp_output lwip_tcp_output
+#define tcp_pcb_purge lwip_tcp_pcb_purge
+#define tcp_pcb_remove lwip_tcp_pcb_remove
+#define tcp_poll lwip_tcp_poll
+#define tcp_recv lwip_tcp_recv
+#define tcp_recved lwip_tcp_recved
+#define tcp_rexmit lwip_tcp_rexmit
+#define tcp_rexmit_rto lwip_tcp_rexmit_rto
+/* tcp_rst is renaming to tcp_rst_impl,
+ * so for cleaner ABI, _impl was added. */
+#define tcp_rst_impl lwip_tcp_rst_impl
+#define tcp_seg_copy lwip_tcp_seg_copy
+#define tcp_seg_free lwip_tcp_seg_free
+#define tcp_segs_free lwip_tcp_segs_free
+#define tcp_send_ctrl lwip_tcp_send_ctrl
+#define tcp_sent lwip_tcp_sent
+#define tcp_setprio lwip_tcp_setprio
+#define tcp_slowtmr lwip_tcp_slowtmr
+#define tcp_ticks lwip_tcp_ticks
+#define tcp_timer_needed lwip_tcp_timer_needed
+#define tcp_tmp_pcb lwip_tcp_tmp_pcb
+#define tcp_tmr lwip_tcp_tmr
+#define tcp_tw_pcbs lwip_tcp_tw_pcbs
+#define tcp_write lwip_tcp_write
+#define tcpip_apimsg lwip_tcpip_apimsg
+#if 0
+#define tcpip_callback lwip_tcpip_callback
+#endif
+#define tcpip_init lwip_tcpip_init
+#define tcpip_input lwip_tcpip_input
+#define udp_bind lwip_udp_bind
+#define udp_connect lwip_udp_connect
+#define udp_disconnect lwip_udp_disconnect
+#define udp_init lwip_udp_init
+#define udp_input lwip_udp_input
+#define udp_new lwip_udp_new
+#define udp_pcbs lwip_udp_pcbs
+#define udp_recv lwip_udp_recv
+#define udp_remove lwip_udp_remove
+#define udp_send lwip_udp_send
+#define udp_sendto lwip_udp_sendto
+
+#define lwip_pbuf_init()
+#define lwip_etharp_init()
+
+#endif /* _VBOX_LWIP_NAMESPACE_H_ */
diff --git a/src/VBox/Devices/Network/lwip-new/vbox/sys_arch.c b/src/VBox/Devices/Network/lwip-new/vbox/sys_arch.c
new file mode 100644
index 00000000..d04c03ab
--- /dev/null
+++ b/src/VBox/Devices/Network/lwip-new/vbox/sys_arch.c
@@ -0,0 +1,583 @@
+/** $Id: sys_arch.c $ */
+/** @file
+ * System dependent parts of lwIP, implemented with IPRT.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+#include <lwip/sys.h>
+
+#include <iprt/assert.h>
+#include <iprt/errcore.h>
+#include <iprt/mem.h>
+#include <iprt/string.h>
+#include <iprt/critsect.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+
+/** @todo during my tests on Debian Lenny 64 bit I ran into trouble using
+ * mutex semaphores (crash deep down in the pthreads lib). Using the write
+ * case of rw semaphores also gives mutual exclusion, and didn't show those
+ * crashes. Should be investigated, because this "fix" might be just covering
+ * the symptoms of a bug elsewhere. */
+#if HC_ARCH_BITS == 64 && defined RT_ARCH_LINUX
+#define LWIPMUTEXTYPE RTSEMRW
+#define LWIPMutexCreate RTSemRWCreate
+#define LWIPMutexDestroy RTSemRWDestroy
+#define LWIPMutexRequest(m) RTSemRWRequestWrite((m), RT_INDEFINITE_WAIT)
+#define LWIPMutexRelease RTSemRWReleaseWrite
+#else
+#define LWIPMUTEXTYPE RTSEMMUTEX
+#define LWIPMutexCreate RTSemMutexCreate
+#define LWIPMutexDestroy RTSemMutexDestroy
+#define LWIPMutexRequest(m) RTSemMutexRequest((m), RT_INDEFINITE_WAIT)
+#define LWIPMutexRelease RTSemMutexRelease
+#endif
+
+/** Maximum number of threads lwIP is allowed to create. */
+#define THREADS_MAX 5
+
+/** Maximum number of mbox entries needed for reasonable performance. */
+#define MBOX_ENTRIES_MAX 128
+
+/** Data type for slots in TLS. */
+typedef struct
+{
+ RTTHREAD tid;
+ void (* thread)(void *arg);
+ void *arg;
+#if 0
+ struct sys_timeouts timeouts;
+#endif
+} THREADLOCALSTORAGE;
+
+/** Actual declaration of the mbox type. */
+/** @todo magic - ??? */
+struct sys_mbox
+{
+ LWIPMUTEXTYPE mutex;
+ RTSEMEVENTMULTI nonempty, nonfull;
+ void *apvEntries[MBOX_ENTRIES_MAX];
+ u32_t head, tail;
+ int valid;
+};
+
+#if SYS_LIGHTWEIGHT_PROT
+/** Critical section variable for short term synchronization. */
+static RTCRITSECT g_ProtCritSect;
+#else
+/** Synchronization for thread creation handling. */
+static RTSEMEVENT g_ThreadSem;
+#endif
+
+/** Number of threads currently created by lwIP. */
+static u32_t g_cThreads = 2;
+
+/** The simulated thread local storage for lwIP things. */
+static THREADLOCALSTORAGE g_aTLS[THREADS_MAX];
+
+/**
+ * Initialize the port to IPRT.
+ */
+void sys_init(void)
+{
+ int rc;
+ unsigned i;
+#if SYS_LIGHTWEIGHT_PROT
+ rc = RTCritSectInit(&g_ProtCritSect);
+ AssertRC(rc);
+#else
+ rc = RTSemEventCreate(&g_ThreadSem);
+ AssertRC(rc);
+ rc = RTSemEventSignal(g_ThreadSem);
+ AssertRC(rc);
+#endif
+ for (i = 0; i < THREADS_MAX; i++)
+ g_aTLS[i].tid = NIL_RTTHREAD;
+}
+
+/**
+ * Create a new (binary) semaphore.
+ */
+err_t sys_sem_new(sys_sem_t *pSem, u8_t count)
+{
+ int rc;
+ err_t rcLwip = ERR_ARG;
+
+ if (!pSem)
+ return ERR_ARG;
+ Assert(count <= 1);
+ rc = RTSemEventCreate(pSem);
+ AssertRCReturn(rc, ERR_ARG);
+ rcLwip = ERR_OK;
+ if (count == 1)
+ {
+ rc = RTSemEventSignal(*pSem);
+ AssertRCReturn(rc, ERR_VAL);
+ }
+ return rcLwip;
+}
+
+/**
+ * Destroy a (binary) semaphore.
+ */
+void sys_sem_free(sys_sem_t *sem)
+{
+ int rc;
+ rc = RTSemEventDestroy(*sem);
+ AssertRC(rc);
+}
+
+/**
+ * Signal a (binary) semaphore.
+ */
+void sys_sem_signal(sys_sem_t *sem)
+{
+ int rc;
+ rc = RTSemEventSignal(*sem);
+ AssertRC(rc);
+}
+
+/**
+ * Wait for a (binary) semaphore.
+ */
+u32_t sys_arch_sem_wait(sys_sem_t *sem, u32_t timeout)
+{
+ int rc;
+ RTMSINTERVAL cMillies;
+ uint64_t tsStart, tsEnd;
+
+ tsStart = RTTimeMilliTS();
+ if (timeout == 0)
+ cMillies = RT_INDEFINITE_WAIT;
+ else
+ cMillies = timeout;
+ rc = RTSemEventWait(*sem, cMillies);
+ if (rc == VERR_TIMEOUT)
+ return SYS_ARCH_TIMEOUT;
+ AssertRC(rc);
+ tsEnd = RTTimeMilliTS();
+ return tsEnd - tsStart;
+}
+
+/**
+ * Create new mbox.
+ */
+err_t sys_mbox_new(sys_mbox_t *pvMbox, int size)
+{
+ int rc;
+ struct sys_mbox *mbox = NULL;
+ RT_NOREF(size); /** @todo safe to ignore this? */
+
+ if (pvMbox == NULL)
+ return ERR_ARG;
+ mbox = RTMemAllocZ(sizeof(struct sys_mbox));
+ Assert(mbox != NULL);
+ if (!mbox)
+ return ERR_MEM;
+ rc = LWIPMutexCreate(&mbox->mutex);
+ AssertRC(rc);
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(mbox);
+ return ERR_VAL;
+ }
+ rc = RTSemEventMultiCreate(&mbox->nonempty);
+ AssertRC(rc);
+ if (RT_FAILURE(rc))
+ {
+ rc = LWIPMutexDestroy(mbox->mutex);
+ AssertRC(rc);
+ RTMemFree(mbox);
+ return ERR_VAL;
+ }
+ rc = RTSemEventMultiCreate(&mbox->nonfull);
+ AssertRC(rc);
+ if (RT_FAILURE(rc))
+ {
+ rc = RTSemEventMultiDestroy(mbox->nonempty);
+ AssertRC(rc);
+ rc = LWIPMutexDestroy(mbox->mutex);
+ AssertRC(rc);
+ RTMemFree(mbox);
+ return ERR_VAL;
+ }
+ mbox->valid = 1;
+ *pvMbox = mbox;
+ return ERR_OK;
+}
+
+/**
+ * Free an mbox.
+ */
+void sys_mbox_free(sys_mbox_t *pvMbox)
+{
+ struct sys_mbox *mbox = NULL;
+ Assert(pvMbox && *pvMbox);
+ mbox = (struct sys_mbox*)*pvMbox;
+ LWIPMutexDestroy((mbox)->mutex);
+ RTSemEventMultiDestroy((mbox)->nonempty);
+ RTSemEventMultiDestroy((mbox)->nonfull);
+ RTMemFree(mbox);
+ *pvMbox = NULL;
+}
+
+/**
+ * Place an entry in an mbox, waiting for a free slot if necessary.
+ */
+void sys_mbox_post(sys_mbox_t *pvMbox, void *msg)
+{
+ int rc;
+ struct sys_mbox *mbox = NULL;
+ Assert(pvMbox && *pvMbox);
+ mbox = (struct sys_mbox*)*pvMbox;
+
+ rc = LWIPMutexRequest((mbox)->mutex);
+ AssertRC(rc);
+
+ while (((mbox)->head + 1) % MBOX_ENTRIES_MAX == (mbox)->tail)
+ {
+ /* (mbox) is full, have to wait until a slot becomes available. */
+ rc = LWIPMutexRelease((mbox)->mutex);
+ AssertRC(rc);
+
+ rc = RTSemEventMultiWait((mbox)->nonfull, RT_INDEFINITE_WAIT);
+ AssertRC(rc);
+
+ rc = LWIPMutexRequest((mbox)->mutex);
+ AssertRC(rc);
+ }
+
+ if ((mbox)->head == (mbox)->tail)
+ {
+ rc = RTSemEventMultiSignal((mbox)->nonempty);
+ AssertRC(rc);
+ }
+ (mbox)->apvEntries[(mbox)->head] = msg;
+ (mbox)->head++;
+ (mbox)->head %= MBOX_ENTRIES_MAX;
+ if (((mbox)->head + 1) % MBOX_ENTRIES_MAX == (mbox)->tail)
+ {
+ rc = RTSemEventMultiReset((mbox)->nonfull);
+ AssertRC(rc);
+ }
+ rc = LWIPMutexRelease((mbox)->mutex);
+ AssertRC(rc);
+}
+
+
+/**
+ * Try to place an entry in an mbox if there is a free slot.
+ */
+err_t sys_mbox_trypost(sys_mbox_t *pvMbox, void *msg)
+{
+ int rc;
+ struct sys_mbox *mbox = NULL;
+ AssertReturn(pvMbox && *pvMbox, ERR_ARG);
+ mbox = (struct sys_mbox*)*pvMbox;
+
+ rc = LWIPMutexRequest((mbox)->mutex);
+ AssertRC(rc);
+
+ if (((mbox)->head + 1) % MBOX_ENTRIES_MAX == (mbox)->tail)
+ {
+ /* (mbox) is full */
+ rc = LWIPMutexRelease((mbox)->mutex);
+ AssertRC(rc);
+ return ERR_MEM;
+ }
+
+ if ((mbox)->head == (mbox)->tail)
+ {
+ rc = RTSemEventMultiSignal((mbox)->nonempty);
+ AssertRC(rc);
+ }
+ (mbox)->apvEntries[(mbox)->head] = msg;
+ (mbox)->head++;
+ (mbox)->head %= MBOX_ENTRIES_MAX;
+ if (((mbox)->head + 1) % MBOX_ENTRIES_MAX == (mbox)->tail)
+ {
+ rc = RTSemEventMultiReset((mbox)->nonfull);
+ AssertRC(rc);
+ }
+ rc = LWIPMutexRelease((mbox)->mutex);
+ AssertRC(rc);
+ return ERR_OK;
+}
+
+
+/**
+ * Get an entry from an mbox.
+ */
+u32_t sys_arch_mbox_fetch(sys_mbox_t *pvMbox, void **msg, u32_t timeout)
+{
+ int rc;
+ RTMSINTERVAL cMillies;
+ uint64_t tsStart, tsEnd;
+ struct sys_mbox *mbox = NULL;
+
+ if (!pvMbox || !*pvMbox) return 0;
+ mbox = (struct sys_mbox*)*pvMbox;
+
+ tsStart = RTTimeMilliTS();
+ if (timeout == 0)
+ cMillies = RT_INDEFINITE_WAIT;
+ else
+ cMillies = timeout;
+
+ rc = LWIPMutexRequest((mbox)->mutex);
+ AssertRC(rc);
+ while ((mbox)->head == (mbox)->tail)
+ {
+ /* (mbox) is empty, have to wait until a slot is filled. */
+ rc = LWIPMutexRelease((mbox)->mutex);
+ AssertRC(rc);
+ if (timeout != 0)
+ {
+ tsEnd = RTTimeMilliTS();
+ if (tsEnd - tsStart >= cMillies)
+ return SYS_ARCH_TIMEOUT;
+ cMillies -= tsEnd - tsStart;
+ }
+ rc = RTSemEventMultiWait((mbox)->nonempty, cMillies);
+ if (rc == VERR_TIMEOUT)
+ return SYS_ARCH_TIMEOUT;
+ AssertRC(rc);
+ if (timeout != 0)
+ {
+ tsEnd = RTTimeMilliTS();
+ if (tsEnd - tsStart >= cMillies)
+ return SYS_ARCH_TIMEOUT;
+ cMillies -= tsEnd - tsStart;
+ }
+ rc = LWIPMutexRequest((mbox)->mutex);
+ AssertRC(rc);
+ }
+ if (((mbox)->head + 1) % MBOX_ENTRIES_MAX == (mbox)->tail)
+ {
+ rc = RTSemEventMultiSignal((mbox)->nonfull);
+ AssertRC(rc);
+ }
+ if (msg != NULL)
+ *msg = (mbox)->apvEntries[(mbox)->tail];
+ (mbox)->tail++;
+ (mbox)->tail %= MBOX_ENTRIES_MAX;
+ rc = RTSemEventMultiSignal((mbox)->nonfull);
+ if ((mbox)->head == (mbox)->tail)
+ {
+ rc = RTSemEventMultiReset((mbox)->nonempty);
+ AssertRC(rc);
+ }
+ rc = LWIPMutexRelease((mbox)->mutex);
+ AssertRC(rc);
+ tsEnd = RTTimeMilliTS();
+ return tsEnd - tsStart;
+}
+
+/**
+ * Try to get an entry from an mbox.
+ */
+u32_t sys_arch_mbox_tryfetch(sys_mbox_t *pvMbox, void **msg)
+{
+ int rc;
+ struct sys_mbox *mbox = NULL;
+ if (!pvMbox || !*pvMbox) return SYS_MBOX_EMPTY;
+ mbox = (struct sys_mbox*)*pvMbox;
+
+ rc = LWIPMutexRequest((mbox)->mutex);
+ AssertRC(rc);
+ if ((mbox)->head == (mbox)->tail)
+ {
+ /* (mbox) is empty, don't wait */
+ rc = LWIPMutexRelease((mbox)->mutex);
+ AssertRC(rc);
+ return SYS_MBOX_EMPTY;
+ }
+ if (((mbox)->head + 1) % MBOX_ENTRIES_MAX == (mbox)->tail)
+ {
+ rc = RTSemEventMultiSignal((mbox)->nonfull);
+ AssertRC(rc);
+ }
+ if (msg != NULL)
+ *msg = (mbox)->apvEntries[(mbox)->tail];
+ (mbox)->tail++;
+ (mbox)->tail %= MBOX_ENTRIES_MAX;
+ rc = RTSemEventMultiSignal((mbox)->nonfull);
+ if ((mbox)->head == (mbox)->tail)
+ {
+ rc = RTSemEventMultiReset((mbox)->nonempty);
+ AssertRC(rc);
+ }
+ rc = LWIPMutexRelease((mbox)->mutex);
+ AssertRC(rc);
+ return 0;
+}
+
+/** Check if an mbox is valid/allocated: return 1 for valid, 0 for invalid */
+int sys_mbox_valid(sys_mbox_t *pvMbox)
+{
+ struct sys_mbox *mbox = NULL;
+ if (!pvMbox || !*pvMbox) return 0;
+ mbox = (struct sys_mbox*)*pvMbox;
+ return (mbox)->valid;
+}
+
+/** Set an mbox invalid so that sys_mbox_valid returns 0 */
+void sys_mbox_set_invalid(sys_mbox_t *pvMbox)
+{
+ struct sys_mbox *mbox = NULL;
+ if (!pvMbox || !*pvMbox)
+ return;
+ mbox = (struct sys_mbox *)*pvMbox;
+ mbox->valid = 0;
+}
+
+#if 0
+/**
+ * Grab the pointer to this thread's timeouts from TLS.
+ */
+struct sys_timeouts *sys_arch_timeouts(void)
+{
+ unsigned i;
+#if SYS_LIGHTWEIGHT_PROT
+ SYS_ARCH_DECL_PROTECT(old_level);
+#endif
+ RTTHREAD myself;
+ struct sys_timeouts *to = NULL;
+
+ myself = RTThreadSelf();
+#if SYS_LIGHTWEIGHT_PROT
+ SYS_ARCH_PROTECT(old_level);
+#else
+ RTSemEventWait(g_ThreadSem, RT_INDEFINITE_WAIT);
+#endif
+ for (i = 0; i < g_cThreads; i++)
+ {
+ if (g_aTLS[i].tid == myself)
+ {
+ to = &g_aTLS[i].timeouts;
+ break;
+ }
+ }
+ /* Auto-adopt new threads which use lwIP as they pop up. */
+ if (!to)
+ {
+ unsigned id;
+ id = g_cThreads;
+ g_cThreads++;
+ Assert(g_cThreads <= THREADS_MAX);
+ g_aTLS[id].tid = myself;
+ to = &g_aTLS[id].timeouts;
+ }
+#if SYS_LIGHTWEIGHT_PROT
+ SYS_ARCH_UNPROTECT(old_level);
+#else
+ RTSemEventSignal(g_ThreadSem);
+#endif
+ return to;
+}
+#endif
+
+/**
+ * Internal: thread main function adapter, dropping the first parameter. Needed
+ * to make lwip thread main function compatible with IPRT thread main function.
+ */
+static DECLCALLBACK(int) sys_thread_adapter(RTTHREAD hThreadSelf, void *pvUser)
+{
+ THREADLOCALSTORAGE *tls = (THREADLOCALSTORAGE *)pvUser;
+ RT_NOREF(hThreadSelf);
+
+ tls->thread(tls->arg);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Create new thread.
+ */
+sys_thread_t sys_thread_new(const char*name, lwip_thread_fn thread, void *arg, int stacksize, int prio)
+{
+ int rc;
+#if SYS_LIGHTWEIGHT_PROT
+ SYS_ARCH_DECL_PROTECT(old_level);
+#endif
+ unsigned id;
+ RTTHREAD tid;
+
+ RT_NOREF(prio, stacksize, name);
+
+#if SYS_LIGHTWEIGHT_PROT
+ SYS_ARCH_PROTECT(old_level);
+#else
+ RTSemEventWait(g_ThreadSem, RT_INDEFINITE_WAIT);
+#endif
+ id = g_cThreads;
+ g_cThreads++;
+ Assert(g_cThreads <= THREADS_MAX);
+ g_aTLS[id].thread = thread;
+ g_aTLS[id].arg = arg;
+ rc = RTThreadCreateF(&tid, sys_thread_adapter, &g_aTLS[id], 0,
+ RTTHREADTYPE_IO, 0, "lwIP%u", id);
+ if (RT_FAILURE(rc))
+ {
+ g_cThreads--;
+ tid = NIL_RTTHREAD;
+ }
+ else
+ g_aTLS[id].tid = tid;
+#if SYS_LIGHTWEIGHT_PROT
+ SYS_ARCH_UNPROTECT(old_level);
+#else
+ RTSemEventSignal(g_ThreadSem);
+#endif
+ AssertRC(rc);
+ return tid;
+}
+
+#if SYS_LIGHTWEIGHT_PROT
+/**
+ * Start a short critical section.
+ */
+sys_prot_t sys_arch_protect(void)
+{
+ int rc;
+ rc = RTCritSectEnter(&g_ProtCritSect);
+ AssertRC(rc);
+ return NULL;
+}
+#endif
+
+#if SYS_LIGHTWEIGHT_PROT
+/**
+ * End a short critical section.
+ */
+void sys_arch_unprotect(sys_prot_t pval)
+{
+ int rc;
+ (void)pval;
+ rc = RTCritSectLeave(&g_ProtCritSect);
+ AssertRC(rc);
+}
+#endif
+
diff --git a/src/VBox/Devices/Network/lwipopts.h b/src/VBox/Devices/Network/lwipopts.h
new file mode 100644
index 00000000..4a44359f
--- /dev/null
+++ b/src/VBox/Devices/Network/lwipopts.h
@@ -0,0 +1,186 @@
+
+#ifndef VBOX_INCLUDED_SRC_Network_lwipopts_h
+#define VBOX_INCLUDED_SRC_Network_lwipopts_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <VBox/cdefs.h> /* For VBOX_STRICT. */
+#include <iprt/mem.h>
+#include <iprt/alloca.h> /* This may include malloc.h (msc), which is something that has
+ * to be done before redefining any of the functions therein. */
+#include <iprt/rand.h> /* see LWIP_RAND() definition */
+
+/* lwip/sockets.h assumes that if FD_SET is defined (in case of Innotek GCC
+ * its definition is dragged through iprt/types.h) then struct timeval is
+ * defined as well, but it's not the case. So include it manually. */
+#ifdef RT_OS_OS2
+# include <sys/time.h>
+#endif
+
+/** Make lwIP use the libc malloc, or more precisely (see below) the IPRT
+ * memory allocation functions. */
+#define MEM_LIBC_MALLOC 1
+
+/** Set proper memory alignment. */
+#if HC_ARCH_BITS == 64
+# define MEM_ALIGNMENT 8
+#else
+#define MEM_ALIGNMENT 4
+#endif
+
+/* IP */
+#define IP_REASSEMBLY 1
+#define IP_REASS_MAX_PBUFS 128
+
+
+
+/** Increase maximum TCP window size. */
+#define TCP_WND 32768
+
+/** Increase TCP maximum segment size. */
+#define TCP_MSS 1460
+
+/** Enable queueing of out-of-order segments. */
+#define TCP_QUEUE_OOSEQ 1
+
+/** TCP sender buffer space (bytes). */
+#define TCP_SND_BUF (32 * TCP_MSS)
+
+/* TCP sender buffer space (pbufs). This must be at least = 2 *
+ TCP_SND_BUF/TCP_MSS for things to work. */
+#define TCP_SND_QUEUELEN 64
+
+/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application
+ sends a lot of data out of ROM (or other static memory), this
+ should be set high.
+
+ NB: This is for PBUF_ROM and PBUF_REF pbufs only!
+
+ Number of PBUF_POOL pbufs is controlled by PBUF_POOL_SIZE that,
+ somewhat confusingly, breaks MEMP_NUM_* pattern.
+
+ PBUF_RAM pbufs are allocated with mem_malloc (with MEM_LIBC_MALLOC
+ set to 1 this is just system malloc), not memp_malloc. */
+#define MEMP_NUM_PBUF (1024 * 4)
+
+
+/* MEMP_NUM_MLD6_GROUP: Maximum number of IPv6 multicast groups that
+ can be joined.
+
+ We need to be able to join solicited node multicast for each
+ address (potentially different) and two groups for DHCP6. All
+ routers multicast is hardcoded in ip6.c and does not require
+ explicit joining. Provide also for a few extra groups just in
+ case. */
+#define MEMP_NUM_MLD6_GROUP (LWIP_IPV6_NUM_ADDRESSES + /* dhcp6 */ 2 + /* extra */ 8)
+
+
+/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP
+ segments. */
+#define MEMP_NUM_TCP_SEG (MEMP_NUM_TCP_PCB * TCP_SND_QUEUELEN / 2)
+
+/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP
+ connections. */
+#define MEMP_NUM_TCP_PCB 128
+
+/* MEMP_NUM_TCPIP_MSG_*: the number of struct tcpip_msg, which is used
+ for sequential API communication and incoming packets. Used in
+ src/api/tcpip.c. */
+#define MEMP_NUM_TCPIP_MSG_API 128
+#define MEMP_NUM_TCPIP_MSG_INPKT 1024
+
+/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One
+ per active UDP "connection". */
+#define MEMP_NUM_UDP_PCB 32
+
+/* Pbuf options */
+/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool.
+ This is only for PBUF_POOL pbufs, primarily used by netif drivers.
+
+ This should have been named with the MEMP_NUM_ prefix (cf.
+ MEMP_NUM_PBUF for PBUF_ROM and PBUF_REF) as it controls the size of
+ yet another memp_malloc() pool. */
+#define PBUF_POOL_SIZE (1024 * 4)
+
+/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool.
+ Use default that is based on TCP_MSS and PBUF_LINK_HLEN. */
+#undef PBUF_POOL_BUFSIZE
+
+/** Turn on support for lightweight critical region protection. Leaving this
+ * off uses synchronization code in pbuf.c which is totally polluted with
+ * races. All the other lwip source files would fall back to semaphore-based
+ * synchronization, but pbuf.c is just broken, leading to incorrect allocation
+ * and as a result to assertions due to buffers being double freed. */
+#define SYS_LIGHTWEIGHT_PROT 1
+
+/** Attempt to get rid of htons etc. macro issues. */
+#undef LWIP_PREFIX_BYTEORDER_FUNCS
+
+#define LWIP_TCPIP_CORE_LOCKING_INPUT 0
+#define LWIP_TCPIP_CORE_LOCKING 0
+#define LWIP_TCP 1
+#define LWIP_SOCKET 1
+#define LWIP_ARP 1
+#define ARP_PROXY 0
+#define LWIP_ETHERNET 1
+#define LWIP_COMPAT_SOCKETS 0
+#define LWIP_COMPAT_MUTEX 1
+
+#define LWIP_IPV6 1
+#define LWIP_IPV6_FORWARD 0
+#define LWIP_ND6_PROXY 0
+
+#define LWIP_ND6_ALLOW_RA_UPDATES (!LWIP_IPV6_FORWARD)
+#define LWIP_IPV6_SEND_ROUTER_SOLICIT (!LWIP_IPV6_FORWARD)
+/* IPv6 autoconfig we don't need in proxy, but it required for very seldom cases
+ * iSCSI over intnet with IPv6
+ */
+#define LWIP_IPV6_AUTOCONFIG 1
+#if LWIP_IPV6_FORWARD /* otherwise use the default from lwip/opt.h */
+#define LWIP_IPV6_DUP_DETECT_ATTEMPTS 0
+#endif
+
+#define LWIP_IPV6_FRAG 1
+
+/**
+ * aka Slirp mode.
+ */
+#define LWIP_CONNECTION_PROXY 0
+#define IP_FORWARD 0
+
+/* MEMP_NUM_SYS_TIMEOUT: the number of simultaneously active
+ timeouts. */
+#define MEMP_NUM_SYS_TIMEOUT 16
+
+
+/* this is required for IPv6 and IGMP needs */
+#define LWIP_RAND() RTRandU32()
+
+/* Debugging stuff. */
+#ifdef DEBUG
+# define LWIP_DEBUG
+# include "lwip-log.h"
+#endif /* DEBUG */
+
+/* printf formatter definitions */
+#define U16_F "hu"
+#define S16_F "hd"
+#define X16_F "hx"
+#define U32_F "u"
+#define S32_F "d"
+#define X32_F "x"
+
+/* Redirect libc memory alloc functions to IPRT. */
+#define malloc(x) RTMemAlloc(x)
+#define realloc(x,y) RTMemRealloc((x), (y))
+#define free(x) RTMemFree(x)
+
+/* Align VBOX_STRICT and LWIP_NOASSERT. */
+#ifndef VBOX_STRICT
+# define LWIP_NOASSERT 1
+#endif
+
+#include "lwip-namespace.h"
+
+#endif /* !VBOX_INCLUDED_SRC_Network_lwipopts_h */
diff --git a/src/VBox/Devices/Network/scripts/VBoxConvertNATStats.sh b/src/VBox/Devices/Network/scripts/VBoxConvertNATStats.sh
new file mode 100755
index 00000000..6fd9a114
--- /dev/null
+++ b/src/VBox/Devices/Network/scripts/VBoxConvertNATStats.sh
@@ -0,0 +1,123 @@
+#!/bin/sh
+## @file
+# ???
+#
+
+#
+# Copyright (C) 2009-2023 Oracle and/or its affiliates.
+#
+# This file is part of VirtualBox base platform packages, as
+# available from https://www.virtualbox.org.
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation, in version 3 of the
+# License.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, see <https://www.gnu.org/licenses>.
+#
+# SPDX-License-Identifier: GPL-3.0-only
+#
+
+#change device to select NAT${NAT_STAT_DEVICE} counters
+
+NAT_STAT_DEBUG=1
+NAT_STAT_DEVICE=0
+NAT_TMP=/tmp
+NAT_STATS_FMT=wiki
+
+NAT_STATS_CONVERTER=$NAT_TMP/converter.awk
+NAT_STATS_COUNTERS_RAW=${NAT_TMP}/counters.out.raw
+NAT_STATS_COUNTERS=${NAT_TMP}/counters.out
+NAT_STATS_REPORT=NAT_STATS_NAME.${NAT_STATS_FMT}
+
+NAT_IN_FILE=$1
+
+[ x"$TMP" != x ] && NAT_TMP=TMP
+
+grep NAT${NAT_STAT_DEVICE} $NAT_IN_FILE > $NAT_STATS_COUNTERS_RAW
+[ $? -ne 0 ] && echo "error happens while grep'ing the NAT's counters" && exit 1
+
+#sed -ne "s/\ */\t/gp" $NAT_STATS_COUNTERS_RAW > $NAT_STATS_COUNTERS
+cp $NAT_STATS_COUNTERS_RAW $NAT_STATS_COUNTERS
+
+cat > $NAT_STATS_CONVERTER <<EOF
+BEGIN{
+ if (FMT == "tsv")
+ OFS="\t";
+ else if (FMT == "wiki")
+ OFS="</td><td>"
+
+ FS=" ";
+ if (FMT == "wiki")
+ print "<table>"
+ if (COUNTERS == "counting")
+ {
+ NF = 2;
+ \$1 = "name"
+ \$2 = "count"
+ if (FMT == "wiki")
+ print "<tr><td>" \$0 "</td></tr>"
+ else
+ print \$0
+ }
+ else if (COUNTERS == "profiling")
+ {
+ NF=6
+ \$1 = "name"
+ \$2 = "ticks_per_count"
+ \$3 = "total_ticks"
+ \$4 = "times"
+ \$5 = "max"
+ \$6 = "min"
+ if (FMT == "wiki")
+ print "<tr><td>" \$0 "</td></tr>"
+ else
+ print \$0
+ }
+}
+/*counting counters */
+NF == 3 && COUNTERS=="counting"{
+ name = \$1
+ count = \$2
+ NF=2
+ if (FMT == "wiki")
+ print "<tr><td>" \$0 "</td></tr>"
+ else
+ print \$0
+}
+/*profiling counters */
+NF == 12 && COUNTERS=="profiling"{
+ name = \$1
+ ticks_per_count = \$2
+ total_ticks = \$5
+ times = \$7
+ max = \$10
+ min = \$12
+ NF=6
+ \$1 = name
+ \$2 = ticks_per_count
+ \$3 = total_ticks
+ \$4 = times
+ \$5 = substr(max,0, index(max, ",") -1)
+ \$6 = substr(min,0, index(min, ")") - 1)
+
+ if (FMT == "wiki")
+ print "<tr><td>" \$0 "</td></tr>"
+ else
+ print \$0
+}
+END{
+ if (FMT == "wiki")
+ print "</table>"
+}
+EOF
+awk -v FMT=$NAT_STATS_FMT -v COUNTERS=profiling -f $NAT_STATS_CONVERTER $NAT_STATS_COUNTERS > $NAT_STATS_REPORT
+awk -v FMT=$NAT_STATS_FMT -v COUNTERS=counting -f $NAT_STATS_CONVERTER $NAT_STATS_COUNTERS >> $NAT_STATS_REPORT
+
diff --git a/src/VBox/Devices/Network/scripts/VBoxPortForwarding.py b/src/VBox/Devices/Network/scripts/VBoxPortForwarding.py
new file mode 100755
index 00000000..4968c947
--- /dev/null
+++ b/src/VBox/Devices/Network/scripts/VBoxPortForwarding.py
@@ -0,0 +1,149 @@
+#!/usr/bin/python
+
+"""
+Copyright (C) 2009-2023 Oracle and/or its affiliates.
+
+This file is part of VirtualBox base platform packages, as
+available from https://www.virtualbox.org.
+
+This program is free software; you can redistribute it and/or
+modify it under the terms of the GNU General Public License
+as published by the Free Software Foundation, in version 3 of the
+License.
+
+This program is distributed in the hope that it will be useful, but
+WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with this program; if not, see <https://www.gnu.org/licenses>.
+
+SPDX-License-Identifier: GPL-3.0-only
+"""
+
+#################################################################################
+# This program is a port-forwarding configurator supposed to simplify
+# port-forwarding for NAT users
+# > python VBoxPortForwarding.py --vm winXP -a 1 -p TCP -l 8080 -g 80 -P www
+# generates sequence of API calls, equivalent to:
+# > VBoxManage setextradata "winXP"
+# "VBoxInternal/Devices/pcnet/0/LUN#0/Config/www/Protocol" TCP
+# > VBoxManage setextradata "winXP"
+# "VBoxInternal/Devices/pcnet/0/LUN#0/Config/www/GuestPort" 80
+# > VBoxManage setextradata "winXP"
+# "VBoxInternal/Devices/pcnet/0/LUN#0/Config/www/HostPort" 8080
+################################################################################
+
+import os,sys
+from vboxapi import VirtualBoxManager
+import optparse
+
+class OptionParser (optparse.OptionParser):
+ def check_required(self, opt):
+ option = self.get_option(opt)
+ if option.type == "string" and getattr(self.values, option.dest) != None:
+ return True
+ if option.type == "int" and getattr(self.values, option.dest) != -1:
+ return True
+ return False
+
+def generate_profile_name(proto, host_port, guest_port):
+ return proto + '_' + str(host_port) + '_' + str(guest_port)
+
+def main(argv):
+
+ usage = "usage: %prog --vm winXP -a 1 -p TCP -l 8080 -g 80 -P www"
+ parser = OptionParser(usage=usage)
+ parser.add_option("-V", "--vm", action="store", dest="vmname", type="string",
+ help="Name or UID of VM to operate on", default=None)
+ parser.add_option("-P", "--profile", dest="profile", type="string",
+ default=None)
+ parser.add_option("-p", "--ip-proto", dest="proto", type="string",
+ default=None)
+ parser.add_option("-l", "--host-port", dest="host_port", type="int",
+ default = -1)
+ parser.add_option("-g", "--guest-port", dest="guest_port", type="int",
+ default = -1)
+ parser.add_option("-a", "--adapter", dest="adapter", type="int",
+ default=-1)
+ (options,args) = parser.parse_args(argv)
+
+ if (not (parser.check_required("-V") or parser.check_required("-G"))):
+ parser.error("please define --vm or --guid option")
+ if (not parser.check_required("-p")):
+ parser.error("please define -p or --ip-proto option")
+ if (not parser.check_required("-l")):
+ parser.error("please define -l or --host_port option")
+ if (not parser.check_required("-g")):
+ parser.error("please define -g or --guest_port option")
+ if (not parser.check_required("-a")):
+ parser.error("please define -a or --adapter option")
+
+ man = VirtualBoxManager(None, None)
+ vb = man.getVirtualBox()
+ print "VirtualBox version: %s" % vb.version,
+ print "r%s" % vb.revision
+
+ vm = None
+ try:
+ if options.vmname != None:
+ vm = vb.findMachine(options.vmname)
+ elif options.vmname != None:
+ vm = vb.getMachine(options.vmname)
+ except:
+ print "can't find VM by name or UID:",options.vmname
+ del man
+ return
+
+ print "vm found: %s [%s]" % (vm.name, vm.id)
+
+ session = man.openMachineSession(vm.id)
+ vm = session.machine
+
+ adapter = vm.getNetworkAdapter(options.adapter)
+
+ if adapter.enabled == False:
+ print "adapter(%d) is disabled" % adapter.slot
+ del man
+ return
+
+ name = None
+ if (adapter.adapterType == man.constants.NetworkAdapterType_Null):
+ print "none adapter type detected"
+ return -1
+ elif (adapter.adapterType == man.constants.NetworkAdapterType_Am79C970A):
+ name = "pcnet"
+ elif (adapter.adapterType == man.constants.NetworkAdapterType_Am79C973):
+ name = "pcnet"
+ elif (adapter.adapterType == man.constants.NetworkAdapterType_I82540EM):
+ name = "e1000"
+ elif (adapter.adapterType == man.constants.NetworkAdapterType_I82545EM):
+ name = "e1000"
+ elif (adapter.adapterType == man.constants.NetworkAdapterType_I82543GC):
+ name = "e1000"
+ print "adapter of '%s' type has been detected" % name
+
+ profile_name = options.profile
+ if profile_name == None:
+ profile_name = generate_profile_name(options.proto.upper(),
+ options.host_port,
+ options.guest_port)
+ config = "VBoxInternal/Devices/" + name + "/"
+ config = config + str(adapter.slot) +"/LUN#0/Config/" + profile_name
+ proto = config + "/Protocol"
+ host_port = config + "/HostPort"
+ guest_port = config + "/GuestPort"
+
+ vm.setExtraData(proto, options.proto.upper())
+ vm.setExtraData(host_port, str(options.host_port))
+ vm.setExtraData(guest_port, str(options.guest_port))
+
+
+ vm.saveSettings()
+ man.closeMachineSession(session)
+
+ del man
+
+if __name__ == "__main__":
+ main(sys.argv)
diff --git a/src/VBox/Devices/Network/slirp/COPYRIGHT b/src/VBox/Devices/Network/slirp/COPYRIGHT
new file mode 100644
index 00000000..ffbc2e31
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/COPYRIGHT
@@ -0,0 +1,64 @@
+Slirp was written by Danny Gasparovski.
+Copyright (c), 1995,1996 All Rights Reserved.
+
+Slirp is maintained by Kelly Price <tygris+slirp@erols.com>
+
+Slirp is free software; "free" as in you don't have to pay for it, and you
+are free to do whatever you want with it. I do not accept any donations,
+monetary or otherwise, for Slirp. Instead, I would ask you to pass this
+potential donation to your favorite charity. In fact, I encourage
+*everyone* who finds Slirp useful to make a small donation to their
+favorite charity (for example, GreenPeace). This is not a requirement, but
+a suggestion from someone who highly values the service they provide.
+
+The copyright terms and conditions:
+
+---BEGIN---
+
+ Copyright (c) 1995,1996 Danny Gasparovski. All rights reserved.
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+ 1. Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ 2. Redistributions in binary form must reproduce 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 copyright holder 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 ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
+ AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
+ DANNY GASPAROVSKI 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.
+
+---END---
+
+This basically means you can do anything you want with the software, except
+1) call it your own, and 2) claim warranty on it. There is no warranty for
+this software. None. Nada. If you lose a million dollars while using
+Slirp, that's your loss not mine. So, ***USE AT YOUR OWN RISK!***.
+
+If these conditions cannot be met due to legal restrictions (E.g. where it
+is against the law to give out Software without warranty), you must cease
+using the software and delete all copies you have.
+
+Slirp uses code that is copyrighted by the following people/organizations:
+
+Juha Pirkola.
+Gregory M. Christy.
+The Regents of the University of California.
+Carnegie Mellon University.
+The Australian National University.
+RSA Data Security, Inc.
+
+Please read the top of each source file for the details on the various
+copyrights.
diff --git a/src/VBox/Devices/Network/slirp/Makefile.kup b/src/VBox/Devices/Network/slirp/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/Makefile.kup
diff --git a/src/VBox/Devices/Network/slirp/bootp.c b/src/VBox/Devices/Network/slirp/bootp.c
new file mode 100644
index 00000000..0ad78891
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bootp.c
@@ -0,0 +1,969 @@
+/* $Id: bootp.c $ */
+/** @file
+ * NAT - BOOTP/DHCP server emulation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * QEMU BOOTP/DHCP server
+ *
+ * Copyright (c) 2004 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <slirp.h>
+#include <libslirp.h>
+#include <iprt/errcore.h>
+
+/** Entry in the table of known DHCP clients. */
+typedef struct
+{
+ uint32_t xid;
+ bool allocated;
+ uint8_t macaddr[ETH_ALEN];
+ struct in_addr addr;
+ int number;
+} BOOTPClient;
+/** Number of DHCP clients supported by NAT. */
+#define NB_ADDR 16
+
+#define bootp_clients ((BOOTPClient *)pData->pbootp_clients)
+
+/* XXX: only DHCP is supported */
+static const uint8_t rfc1533_cookie[4] = { RFC1533_COOKIE };
+
+static void bootp_reply(PNATState pData, struct mbuf *m0, int offReply, uint16_t flags);
+
+
+static uint8_t *dhcp_find_option(uint8_t *vendor, size_t vlen, uint8_t tag, ssize_t checklen)
+{
+ uint8_t *q = vendor;
+ size_t len = vlen;
+
+ q += sizeof(rfc1533_cookie);
+ len -= sizeof(rfc1533_cookie);
+
+ while (len > 0)
+ {
+ uint8_t *optptr = q;
+ uint8_t opt;
+ uint8_t optlen;
+
+ opt = *q++;
+ --len;
+
+ if (opt == RFC1533_END)
+ break;
+
+ if (opt == RFC1533_PAD)
+ continue;
+
+ if (len == 0)
+ break; /* no option length byte */
+
+ optlen = *q++;
+ --len;
+
+ if (len < optlen)
+ break; /* option value truncated */
+
+ if (opt == tag)
+ {
+ if (checklen > 0 && optlen != checklen)
+ break; /* wrong option size */
+
+ return optptr;
+ }
+
+ q += optlen;
+ len -= optlen;
+ }
+
+ return NULL;
+}
+
+static BOOTPClient *bc_alloc_client(PNATState pData)
+{
+ int i;
+ LogFlowFuncEnter();
+ for (i = 0; i < NB_ADDR; i++)
+ {
+ if (!bootp_clients[i].allocated)
+ {
+ BOOTPClient *bc;
+
+ bc = &bootp_clients[i];
+ memset(bc, 0, sizeof(BOOTPClient));
+ bc->allocated = 1;
+ bc->number = i;
+ LogFlowFunc(("LEAVE: bc:%d\n", bc->number));
+ return bc;
+ }
+ }
+ LogFlowFunc(("LEAVE: NULL\n"));
+ return NULL;
+}
+
+static BOOTPClient *get_new_addr(PNATState pData, struct in_addr *paddr)
+{
+ BOOTPClient *bc;
+ LogFlowFuncEnter();
+ bc = bc_alloc_client(pData);
+ if (!bc)
+ return NULL;
+
+ paddr->s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | (bc->number + START_ADDR));
+ bc->addr.s_addr = paddr->s_addr;
+ LogFlowFunc(("LEAVE: paddr:%RTnaipv4, bc:%d\n", paddr->s_addr, bc->number));
+ return bc;
+}
+
+static int release_addr(PNATState pData, struct in_addr *paddr)
+{
+ unsigned i;
+ for (i = 0; i < NB_ADDR; i++)
+ {
+ if (paddr->s_addr == bootp_clients[i].addr.s_addr)
+ {
+ memset(&bootp_clients[i], 0, sizeof(BOOTPClient));
+ return VINF_SUCCESS;
+ }
+ }
+ return VERR_NOT_FOUND;
+}
+
+/*
+ * from RFC 2131 4.3.1
+ * Field DHCPOFFER DHCPACK DHCPNAK
+ * ----- --------- ------- -------
+ * 'op' BOOTREPLY BOOTREPLY BOOTREPLY
+ * 'htype' (From "Assigned Numbers" RFC)
+ * 'hlen' (Hardware address length in octets)
+ * 'hops' 0 0 0
+ * 'xid' 'xid' from client 'xid' from client 'xid' from client
+ * DHCPDISCOVER DHCPREQUEST DHCPREQUEST
+ * message message message
+ * 'secs' 0 0 0
+ * 'ciaddr' 0 'ciaddr' from 0
+ * DHCPREQUEST or 0
+ * 'yiaddr' IP address offered IP address 0
+ * to client assigned to client
+ * 'siaddr' IP address of next IP address of next 0
+ * bootstrap server bootstrap server
+ * 'flags' 'flags' from 'flags' from 'flags' from
+ * client DHCPDISCOVER client DHCPREQUEST client DHCPREQUEST
+ * message message message
+ * 'giaddr' 'giaddr' from 'giaddr' from 'giaddr' from
+ * client DHCPDISCOVER client DHCPREQUEST client DHCPREQUEST
+ * message message message
+ * 'chaddr' 'chaddr' from 'chaddr' from 'chaddr' from
+ * client DHCPDISCOVER client DHCPREQUEST client DHCPREQUEST
+ * message message message
+ * 'sname' Server host name Server host name (unused)
+ * or options or options
+ * 'file' Client boot file Client boot file (unused)
+ * name or options name or options
+ * 'options' options options
+ *
+ * Option DHCPOFFER DHCPACK DHCPNAK
+ * ------ --------- ------- -------
+ * Requested IP address MUST NOT MUST NOT MUST NOT
+ * IP address lease time MUST MUST (DHCPREQUEST) MUST NOT
+ * MUST NOT (DHCPINFORM)
+ * Use 'file'/'sname' fields MAY MAY MUST NOT
+ * DHCP message type DHCPOFFER DHCPACK DHCPNAK
+ * Parameter request list MUST NOT MUST NOT MUST NOT
+ * Message SHOULD SHOULD SHOULD
+ * Client identifier MUST NOT MUST NOT MAY
+ * Vendor class identifier MAY MAY MAY
+ * Server identifier MUST MUST MUST
+ * Maximum message size MUST NOT MUST NOT MUST NOT
+ * All others MAY MAY MUST NOT
+ */
+static BOOTPClient *find_addr(PNATState pData, struct in_addr *paddr, const uint8_t *macaddr)
+{
+ int i;
+
+ LogFlowFunc(("macaddr:%RTmac\n", macaddr));
+ for (i = 0; i < NB_ADDR; i++)
+ {
+ if ( memcmp(macaddr, bootp_clients[i].macaddr, ETH_ALEN) == 0
+ && bootp_clients[i].allocated != 0)
+ {
+ BOOTPClient *bc;
+
+ bc = &bootp_clients[i];
+ bc->allocated = 1;
+ paddr->s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | (i + START_ADDR));
+ LogFlowFunc(("LEAVE: paddr:%RTnaipv4 bc:%d\n", paddr->s_addr, bc->number));
+ return bc;
+ }
+ }
+ LogFlowFunc(("LEAVE: NULL\n"));
+ return NULL;
+}
+
+static struct mbuf *dhcp_create_msg(PNATState pData, struct bootp_t *bp, struct mbuf *m, uint8_t type)
+{
+ struct bootp_t *rbp;
+ struct ethhdr *eh;
+ uint8_t *q;
+
+ eh = mtod(m, struct ethhdr *);
+ memcpy(eh->h_source, bp->bp_hwaddr, ETH_ALEN); /* XXX: if_encap just swap source with dest */
+
+ m->m_data += if_maxlinkhdr; /*reserve ether header */
+
+ rbp = mtod(m, struct bootp_t *);
+ memset(rbp, 0, sizeof(struct bootp_t));
+ rbp->bp_op = BOOTP_REPLY;
+ rbp->bp_xid = bp->bp_xid; /* see table 3 of rfc2131*/
+ rbp->bp_flags = bp->bp_flags; /* figure 2 of rfc2131 */
+ rbp->bp_giaddr.s_addr = bp->bp_giaddr.s_addr;
+#if 0 /*check flags*/
+ saddr.sin_port = RT_H2N_U16_C(BOOTP_SERVER);
+ daddr.sin_port = RT_H2N_U16_C(BOOTP_CLIENT);
+#endif
+ rbp->bp_htype = 1;
+ rbp->bp_hlen = 6;
+ memcpy(rbp->bp_hwaddr, bp->bp_hwaddr, 6);
+
+ memcpy(rbp->bp_vend, rfc1533_cookie, 4); /* cookie */
+ q = rbp->bp_vend;
+ q += 4;
+ *q++ = RFC2132_MSG_TYPE;
+ *q++ = 1;
+ *q++ = type;
+
+ return m;
+}
+
+static int dhcp_do_ack_offer(PNATState pData, struct mbuf *m, BOOTPClient *bc, int fDhcpRequest)
+{
+ struct bootp_t *rbp = NULL;
+ uint8_t *q;
+ struct in_addr saddr;
+ int val;
+
+ struct dns_entry *de = NULL;
+ struct dns_domain_entry *dd = NULL;
+ int added = 0;
+ uint8_t *q_dns_header = NULL;
+ uint32_t lease_time = RT_H2N_U32_C(LEASE_TIME);
+ uint32_t netmask = RT_H2N_U32(pData->netmask);
+
+ rbp = mtod(m, struct bootp_t *);
+ q = &rbp->bp_vend[0];
+ q += 7; /* !cookie rfc 2132 + TYPE*/
+
+ /*DHCP Offer specific*/
+ /*
+ * we're care in built-in tftp server about existence/validness of the boot file.
+ */
+ if (bootp_filename)
+ RTStrPrintf((char*)rbp->bp_file, sizeof(rbp->bp_file), "%s", bootp_filename);
+
+ Log(("NAT: DHCP: bp_file:%s\n", &rbp->bp_file));
+ /* Address/port of the DHCP server. */
+ rbp->bp_yiaddr = bc->addr; /* Client IP address */
+ Log(("NAT: DHCP: bp_yiaddr:%RTnaipv4\n", rbp->bp_yiaddr.s_addr));
+ rbp->bp_siaddr = pData->tftp_server; /* Next Server IP address, i.e. TFTP */
+ Log(("NAT: DHCP: bp_siaddr:%RTnaipv4\n", rbp->bp_siaddr.s_addr));
+ if (fDhcpRequest)
+ {
+ rbp->bp_ciaddr.s_addr = bc->addr.s_addr; /* Client IP address */
+ }
+ saddr.s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_ALIAS);
+ Log(("NAT: DHCP: s_addr:%RTnaipv4\n", saddr.s_addr));
+
+#define FILL_BOOTP_EXT(q, tag, len, pvalue) \
+ do { \
+ struct bootp_ext *be = (struct bootp_ext *)(q); \
+ be->bpe_tag = (tag); \
+ be->bpe_len = (len); \
+ memcpy(&be[1], (pvalue), (len)); \
+ (q) = (uint8_t *)(&be[1]) + (len); \
+ }while(0)
+/* appending another value to tag, calculates len of whole block*/
+#define FILL_BOOTP_APP(head, q, tag, len, pvalue) \
+ do { \
+ struct bootp_ext *be = (struct bootp_ext *)(head); \
+ memcpy(q, (pvalue), (len)); \
+ (q) += (len); \
+ Assert(be->bpe_tag == (tag)); \
+ be->bpe_len += (len); \
+ }while(0)
+
+
+ FILL_BOOTP_EXT(q, RFC1533_NETMASK, 4, &netmask);
+ FILL_BOOTP_EXT(q, RFC1533_GATEWAY, 4, &saddr);
+
+ if (pData->fUseDnsProxy || pData->fUseHostResolver)
+ {
+ uint32_t addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_DNS);
+ FILL_BOOTP_EXT(q, RFC1533_DNS, 4, &addr);
+ }
+ else if (!TAILQ_EMPTY(&pData->pDnsList))
+ {
+ de = TAILQ_LAST(&pData->pDnsList, dns_list_head);
+ q_dns_header = q;
+ FILL_BOOTP_EXT(q, RFC1533_DNS, 4, &de->de_addr.s_addr);
+
+ TAILQ_FOREACH_REVERSE(de, &pData->pDnsList, dns_list_head, de_list)
+ {
+ if (TAILQ_LAST(&pData->pDnsList, dns_list_head) == de)
+ continue; /* first value with head we've ingected before */
+ FILL_BOOTP_APP(q_dns_header, q, RFC1533_DNS, 4, &de->de_addr.s_addr);
+ }
+ }
+
+ if (pData->fPassDomain && !pData->fUseHostResolver)
+ {
+ LIST_FOREACH(dd, &pData->pDomainList, dd_list)
+ {
+
+ if (dd->dd_pszDomain == NULL)
+ continue;
+ /* never meet valid separator here in RFC1533*/
+ if (added != 0)
+ FILL_BOOTP_EXT(q, RFC1533_DOMAINNAME, 1, ",");
+ else
+ added = 1;
+ val = (int)strlen(dd->dd_pszDomain);
+ FILL_BOOTP_EXT(q, RFC1533_DOMAINNAME, val, dd->dd_pszDomain);
+ }
+ }
+
+ FILL_BOOTP_EXT(q, RFC2132_LEASE_TIME, 4, &lease_time);
+
+ if (*slirp_hostname)
+ {
+ val = (int)strlen(slirp_hostname);
+ FILL_BOOTP_EXT(q, RFC1533_HOSTNAME, val, slirp_hostname);
+ }
+ /* Temporary fix: do not pollute ARP cache from BOOTP because it may result
+ in network loss due to cache entry override w/ invalid MAC address. */
+ /*slirp_arp_cache_update_or_add(pData, rbp->bp_yiaddr.s_addr, bc->macaddr);*/
+ return q - rbp->bp_vend; /*return offset */
+}
+
+static int dhcp_send_nack(PNATState pData, struct bootp_t *bp, BOOTPClient *bc, struct mbuf *m)
+{
+ NOREF(bc);
+
+ dhcp_create_msg(pData, bp, m, DHCPNAK);
+ return 7;
+}
+
+static int dhcp_send_ack(PNATState pData, struct bootp_t *bp, BOOTPClient *bc, struct mbuf *m, int fDhcpRequest)
+{
+ int offReply = 0; /* boot_reply will fill general options and add END before sending response */
+
+ AssertReturn(bc != NULL, -1);
+
+ dhcp_create_msg(pData, bp, m, DHCPACK);
+ slirp_update_guest_addr_guess(pData, bc->addr.s_addr, "DHCP ACK");
+ offReply = dhcp_do_ack_offer(pData, m, bc, fDhcpRequest);
+ return offReply;
+}
+
+static int dhcp_send_offer(PNATState pData, struct bootp_t *bp, BOOTPClient *bc, struct mbuf *m)
+{
+ int offReply = 0; /* boot_reply will fill general options and add END before sending response */
+
+ dhcp_create_msg(pData, bp, m, DHCPOFFER);
+ offReply = dhcp_do_ack_offer(pData, m, bc, /* fDhcpRequest=*/ 0);
+ return offReply;
+}
+
+/**
+ * decoding client messages RFC2131 (4.3.6)
+ * ---------------------------------------------------------------------
+ * | |INIT-REBOOT |SELECTING |RENEWING |REBINDING |
+ * ---------------------------------------------------------------------
+ * |broad/unicast |broadcast |broadcast |unicast |broadcast |
+ * |server-ip |MUST NOT |MUST |MUST NOT |MUST NOT |
+ * |requested-ip |MUST |MUST |MUST NOT |MUST NOT |
+ * |ciaddr |zero |zero |IP address |IP address|
+ * ---------------------------------------------------------------------
+ *
+ */
+
+enum DHCP_REQUEST_STATES
+{
+ INIT_REBOOT,
+ SELECTING,
+ RENEWING,
+ REBINDING,
+ NONE
+};
+
+static int dhcp_decode_request(PNATState pData, struct bootp_t *bp, size_t vlen, struct mbuf *m)
+{
+ BOOTPClient *bc = NULL;
+ struct in_addr daddr;
+ int offReply;
+ uint8_t *req_ip = NULL;
+ uint8_t *server_ip = NULL;
+ uint32_t ui32;
+ enum DHCP_REQUEST_STATES dhcp_stat = NONE;
+
+ /* need to understand which type of request we get */
+ req_ip = dhcp_find_option(bp->bp_vend, vlen,
+ RFC2132_REQ_ADDR, sizeof(struct in_addr));
+ server_ip = dhcp_find_option(bp->bp_vend, vlen,
+ RFC2132_SRV_ID, sizeof(struct in_addr));
+
+ bc = find_addr(pData, &daddr, bp->bp_hwaddr);
+
+ if (server_ip != NULL)
+ {
+ /* selecting */
+ if (!bc)
+ {
+ LogRel(("NAT: DHCP no IP was allocated\n"));
+ return -1;
+ }
+
+ if ( !req_ip
+ || bp->bp_ciaddr.s_addr != INADDR_ANY)
+ {
+ LogRel(("NAT: Invalid SELECTING request\n"));
+ return -1; /* silently ignored */
+ }
+ dhcp_stat = SELECTING;
+ /* Assert((bp->bp_ciaddr.s_addr == INADDR_ANY)); */
+ }
+ else
+ {
+ if (req_ip != NULL)
+ {
+ /* init-reboot */
+ dhcp_stat = INIT_REBOOT;
+ }
+ else
+ {
+ /* table 4 of rfc2131 */
+ if (bp->bp_flags & RT_H2N_U16_C(DHCP_FLAGS_B))
+ dhcp_stat = REBINDING;
+ else
+ dhcp_stat = RENEWING;
+ }
+ }
+
+ /*?? renewing ??*/
+ switch (dhcp_stat)
+ {
+ case RENEWING:
+ /**
+ * decoding client messages RFC2131 (4.3.6)
+ * ------------------------------
+ * | |RENEWING |
+ * ------------------------------
+ * |broad/unicast |unicast |
+ * |server-ip |MUST NOT |
+ * |requested-ip |MUST NOT |
+ * |ciaddr |IP address |
+ * ------------------------------
+ */
+ if ( server_ip
+ || req_ip
+ || bp->bp_ciaddr.s_addr == INADDR_ANY)
+ {
+ LogRel(("NAT: Invalid RENEWING dhcp request\n"));
+ return -1; /* silent ignorance */
+ }
+ if (bc != NULL)
+ {
+ /* Assert((bc->addr.s_addr == bp->bp_ciaddr.s_addr)); */
+ /*if it already here well just do ack, we aren't aware of dhcp time expiration*/
+ }
+ else
+ {
+ if ((bp->bp_ciaddr.s_addr & RT_H2N_U32(pData->netmask)) != pData->special_addr.s_addr)
+ {
+ LogRel(("NAT: Client %RTnaipv4 requested IP -- sending NAK\n", bp->bp_ciaddr.s_addr));
+ offReply = dhcp_send_nack(pData, bp, bc, m);
+ return offReply;
+ }
+
+ bc = bc_alloc_client(pData);
+ if (!bc)
+ {
+ LogRel(("NAT: Can't allocate address. RENEW has been silently ignored\n"));
+ return -1;
+ }
+
+ memcpy(bc->macaddr, bp->bp_hwaddr, ETH_ALEN);
+ bc->addr.s_addr = bp->bp_ciaddr.s_addr;
+ }
+ break;
+
+ case INIT_REBOOT:
+ /**
+ * decoding client messages RFC2131 (4.3.6)
+ * ------------------------------
+ * | |INIT-REBOOT |
+ * ------------------------------
+ * |broad/unicast |broadcast |
+ * |server-ip |MUST NOT |
+ * |requested-ip |MUST |
+ * |ciaddr |zero |
+ * ------------------------------
+ *
+ */
+ if ( server_ip
+ || !req_ip
+ || bp->bp_ciaddr.s_addr != INADDR_ANY)
+ {
+ LogRel(("NAT: Invalid INIT-REBOOT dhcp request\n"));
+ return -1; /* silently ignored */
+ }
+ ui32 = *(uint32_t *)(req_ip + 2);
+ if ((ui32 & RT_H2N_U32(pData->netmask)) != pData->special_addr.s_addr)
+ {
+ LogRel(("NAT: Address %RTnaipv4 has been requested -- sending NAK\n", ui32));
+ offReply = dhcp_send_nack(pData, bp, bc, m);
+ return offReply;
+ }
+
+ /* find_addr() got some result? */
+ if (!bc)
+ {
+ bc = bc_alloc_client(pData);
+ if (!bc)
+ {
+ LogRel(("NAT: Can't allocate address. RENEW has been silently ignored\n"));
+ return -1;
+ }
+ }
+
+ memcpy(bc->macaddr, bp->bp_hwaddr, ETH_ALEN);
+ bc->addr.s_addr = ui32;
+ break;
+
+ case NONE:
+ return -1;
+
+ default:
+ break;
+ }
+
+ if (bc == NULL)
+ return -1;
+
+ LogRel(("NAT: DHCP offered IP address %RTnaipv4\n", bc->addr.s_addr));
+ offReply = dhcp_send_ack(pData, bp, bc, m, /* fDhcpRequest=*/ 1);
+ return offReply;
+}
+
+static int dhcp_decode_discover(PNATState pData, struct bootp_t *bp, int fDhcpDiscover, struct mbuf *m)
+{
+ BOOTPClient *bc;
+ struct in_addr daddr;
+ int offReply;
+
+ if (fDhcpDiscover)
+ {
+ bc = find_addr(pData, &daddr, bp->bp_hwaddr);
+ if (!bc)
+ {
+ bc = get_new_addr(pData, &daddr);
+ if (!bc)
+ {
+ LogRel(("NAT: DHCP no IP address left\n"));
+ Log(("no address left\n"));
+ return -1;
+ }
+ memcpy(bc->macaddr, bp->bp_hwaddr, ETH_ALEN);
+ }
+
+ bc->xid = bp->bp_xid;
+ LogRel(("NAT: DHCP offered IP address %RTnaipv4\n", bc->addr.s_addr));
+ offReply = dhcp_send_offer(pData, bp, bc, m);
+ return offReply;
+ }
+
+ bc = find_addr(pData, &daddr, bp->bp_hwaddr);
+ if (!bc)
+ {
+ LogRel(("NAT: DHCP Inform was ignored no boot client was found\n"));
+ return -1;
+ }
+
+ LogRel(("NAT: DHCP offered IP address %RTnaipv4\n", bc->addr.s_addr));
+ offReply = dhcp_send_ack(pData, bp, bc, m, /* fDhcpRequest=*/ 0);
+ return offReply;
+}
+
+static int dhcp_decode_release(PNATState pData, struct bootp_t *bp)
+{
+ int rc = release_addr(pData, &bp->bp_ciaddr);
+ LogRel(("NAT: %s %RTnaipv4\n",
+ RT_SUCCESS(rc) ? "DHCP released IP address" : "Ignored DHCP release for IP address",
+ bp->bp_ciaddr.s_addr));
+ return 0;
+}
+
+/**
+ * fields for discovering t
+ * Field DHCPDISCOVER DHCPREQUEST DHCPDECLINE,
+ * DHCPINFORM DHCPRELEASE
+ * ----- ------------ ----------- -----------
+ * 'op' BOOTREQUEST BOOTREQUEST BOOTREQUEST
+ * 'htype' (From "Assigned Numbers" RFC)
+ * 'hlen' (Hardware address length in octets)
+ * 'hops' 0 0 0
+ * 'xid' selected by client 'xid' from server selected by
+ * DHCPOFFER message client
+ * 'secs' 0 or seconds since 0 or seconds since 0
+ * DHCP process started DHCP process started
+ * 'flags' Set 'BROADCAST' Set 'BROADCAST' 0
+ * flag if client flag if client
+ * requires broadcast requires broadcast
+ * reply reply
+ * 'ciaddr' 0 (DHCPDISCOVER) 0 or client's 0 (DHCPDECLINE)
+ * client's network address client's network
+ * network address (BOUND/RENEW/REBIND) address
+ * (DHCPINFORM) (DHCPRELEASE)
+ * 'yiaddr' 0 0 0
+ * 'siaddr' 0 0 0
+ * 'giaddr' 0 0 0
+ * 'chaddr' client's hardware client's hardware client's hardware
+ * address address address
+ * 'sname' options, if options, if (unused)
+ * indicated in indicated in
+ * 'sname/file' 'sname/file'
+ * option; otherwise option; otherwise
+ * unused unused
+ * 'file' options, if options, if (unused)
+ * indicated in indicated in
+ * 'sname/file' 'sname/file'
+ * option; otherwise option; otherwise
+ * unused unused
+ * 'options' options options (unused)
+ * Requested IP address MAY MUST (in MUST
+ * (DISCOVER) SELECTING or (DHCPDECLINE),
+ * MUST NOT INIT-REBOOT) MUST NOT
+ * (INFORM) MUST NOT (in (DHCPRELEASE)
+ * BOUND or
+ * RENEWING)
+ * IP address lease time MAY MAY MUST NOT
+ * (DISCOVER)
+ * MUST NOT
+ * (INFORM)
+ * Use 'file'/'sname' fields MAY MAY MAY
+ * DHCP message type DHCPDISCOVER/ DHCPREQUEST DHCPDECLINE/
+ * DHCPINFORM DHCPRELEASE
+ * Client identifier MAY MAY MAY
+ * Vendor class identifier MAY MAY MUST NOT
+ * Server identifier MUST NOT MUST (after MUST
+ * SELECTING)
+ * MUST NOT (after
+ * INIT-REBOOT,
+ * BOUND, RENEWING
+ * or REBINDING)
+ * Parameter request list MAY MAY MUST NOT
+ * Maximum message size MAY MAY MUST NOT
+ * Message SHOULD NOT SHOULD NOT SHOULD
+ * Site-specific MAY MAY MUST NOT
+ * All others MAY MAY MUST NOT
+ *
+ */
+static void dhcp_decode(PNATState pData, struct bootp_t *bp, size_t vlen)
+{
+ const uint8_t *pu8RawDhcpObject;
+ int rc;
+ struct in_addr req_ip;
+ int fDhcpDiscover = 0;
+ uint8_t *parameter_list = NULL;
+ struct mbuf *m = NULL;
+
+ if (memcmp(bp->bp_vend, rfc1533_cookie, sizeof(rfc1533_cookie)) != 0)
+ return;
+
+ pu8RawDhcpObject = dhcp_find_option(bp->bp_vend, vlen, RFC2132_MSG_TYPE, 1);
+ if (pu8RawDhcpObject == NULL)
+ return;
+ if (pu8RawDhcpObject[1] != 1) /* option length */
+ return;
+
+ /**
+ * We're going update dns list at least once per DHCP transaction (!not on every operation
+ * within transaction), assuming that transaction can't be longer than 1 min.
+ *
+ * @note: if we have notification update (HAVE_NOTIFICATION_FOR_DNS_UPDATE)
+ * provided by host, we don't need implicitly re-initialize dns list.
+ *
+ * @note: NATState::fUseHostResolver became (r89055) the flag signalling that Slirp
+ * wasn't able to fetch fresh host DNS info and fall down to use host-resolver, on one
+ * of the previous attempts to proxy dns requests to Host's name-resolving API
+ *
+ * @note: Checking NATState::fUseHostResolver == true, we want to try restore behaviour initialy
+ * wanted by user ASAP (P here when host serialize its configuration in files parsed by Slirp).
+ */
+ if ( !HAVE_NOTIFICATION_FOR_DNS_UPDATE
+ && !pData->fUseHostResolverPermanent
+ && ( pData->dnsLastUpdate == 0
+ || curtime - pData->dnsLastUpdate > 60 * 1000 /* one minute */
+ || pData->fUseHostResolver))
+ {
+ uint8_t i;
+
+ parameter_list = dhcp_find_option(bp->bp_vend, vlen, RFC2132_PARAM_LIST, -1);
+ for (i = 0; parameter_list && i < parameter_list[1]; ++i)
+ {
+ if (parameter_list[2 + i] == RFC1533_DNS)
+ {
+ /* XXX: How differs it from host Suspend/Resume? */
+ slirpReleaseDnsSettings(pData);
+ slirpInitializeDnsSettings(pData);
+ pData->dnsLastUpdate = curtime;
+ break;
+ }
+ }
+ }
+
+ m = m_getcl(pData, M_DONTWAIT, MT_HEADER, M_PKTHDR);
+ if (!m)
+ {
+ LogRel(("NAT: Can't allocate memory for response!\n"));
+ return;
+ }
+
+ switch (*(pu8RawDhcpObject + 2))
+ {
+ case DHCPDISCOVER:
+ fDhcpDiscover = 1;
+ RT_FALL_THRU();
+ case DHCPINFORM:
+ rc = dhcp_decode_discover(pData, bp, fDhcpDiscover, m);
+ if (rc > 0)
+ goto reply;
+ break;
+
+ case DHCPREQUEST:
+ rc = dhcp_decode_request(pData, bp, vlen, m);
+ if (rc > 0)
+ goto reply;
+ break;
+
+ case DHCPRELEASE:
+ dhcp_decode_release(pData, bp);
+ /* no reply required */
+ break;
+
+ case DHCPDECLINE:
+ pu8RawDhcpObject = dhcp_find_option(bp->bp_vend, vlen,
+ RFC2132_REQ_ADDR, sizeof(struct in_addr));
+ if (pu8RawDhcpObject == NULL)
+ {
+ Log(("NAT: RFC2132_REQ_ADDR not found\n"));
+ break;
+ }
+
+ req_ip.s_addr = *(uint32_t *)(pu8RawDhcpObject + 2);
+ rc = bootp_cache_lookup_ether_by_ip(pData, req_ip.s_addr, NULL);
+ if (RT_FAILURE(rc))
+ {
+ /* Not registered */
+ BOOTPClient *bc;
+ bc = bc_alloc_client(pData);
+ Assert(bc);
+ if (!bc)
+ {
+ LogRel(("NAT: Can't allocate bootp client object\n"));
+ break;
+ }
+ bc->addr.s_addr = req_ip.s_addr;
+ slirp_arp_who_has(pData, bc->addr.s_addr);
+ LogRel(("NAT: %RTnaipv4 has been already registered\n", req_ip));
+ }
+ /* no response required */
+ break;
+
+ default:
+ /* unsupported DHCP message type */
+ break;
+ }
+ /* silently ignore */
+ m_freem(pData, m);
+ return;
+
+reply:
+ bootp_reply(pData, m, rc, bp->bp_flags);
+}
+
+static void bootp_reply(PNATState pData, struct mbuf *m, int offReply, uint16_t flags)
+{
+ struct sockaddr_in saddr, daddr;
+ struct bootp_t *rbp = NULL;
+ uint8_t *q = NULL;
+ int nack;
+ rbp = mtod(m, struct bootp_t *);
+ Assert((m));
+ Assert((rbp));
+ q = rbp->bp_vend;
+ nack = (q[6] == DHCPNAK);
+ q += offReply;
+
+ saddr.sin_addr.s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_ALIAS);
+
+ FILL_BOOTP_EXT(q, RFC2132_SRV_ID, 4, &saddr.sin_addr);
+
+ *q++ = RFC1533_END; /* end of message */
+
+ m->m_pkthdr.header = mtod(m, void *);
+ m->m_len = sizeof(struct bootp_t)
+ - sizeof(struct ip)
+ - sizeof(struct udphdr);
+ m->m_data += sizeof(struct udphdr)
+ + sizeof(struct ip);
+ if ( (flags & RT_H2N_U16_C(DHCP_FLAGS_B))
+ || nack != 0)
+ daddr.sin_addr.s_addr = INADDR_BROADCAST;
+ else
+ daddr.sin_addr.s_addr = rbp->bp_yiaddr.s_addr; /*unicast requested by client*/
+ saddr.sin_port = RT_H2N_U16_C(BOOTP_SERVER);
+ daddr.sin_port = RT_H2N_U16_C(BOOTP_CLIENT);
+ udp_output2(pData, NULL, m, &saddr, &daddr, IPTOS_LOWDELAY);
+}
+
+void bootp_input(PNATState pData, struct mbuf *m)
+{
+ struct bootp_t *bp = mtod(m, struct bootp_t *);
+ u_int mlen = m_length(m, NULL);
+ size_t vlen;
+
+ if (mlen < RT_UOFFSETOF(struct bootp_t, bp_vend) + sizeof(rfc1533_cookie))
+ {
+ LogRelMax(50, ("NAT: ignoring invalid BOOTP request (mlen %u too short)\n", mlen));
+ return;
+ }
+
+ if (bp->bp_op != BOOTP_REQUEST)
+ {
+ LogRelMax(50, ("NAT: ignoring invalid BOOTP request (wrong opcode %u)\n", bp->bp_op));
+ return;
+ }
+
+ if (bp->bp_htype != RTNET_ARP_ETHER)
+ {
+ LogRelMax(50, ("NAT: ignoring invalid BOOTP request (wrong HW type %u)\n", bp->bp_htype));
+ return;
+ }
+
+ if (bp->bp_hlen != ETH_ALEN)
+ {
+ LogRelMax(50, ("NAT: ignoring invalid BOOTP request (wrong HW address length %u)\n", bp->bp_hlen));
+ return;
+ }
+
+ if (bp->bp_hops != 0)
+ {
+ LogRelMax(50, ("NAT: ignoring invalid BOOTP request (wrong hop count %u)\n", bp->bp_hops));
+ return;
+ }
+
+ vlen = mlen - RT_UOFFSETOF(struct bootp_t, bp_vend);
+ dhcp_decode(pData, bp, vlen);
+}
+
+int bootp_cache_lookup_ip_by_ether(PNATState pData,const uint8_t* ether, uint32_t *pip)
+{
+ int i;
+
+ if (!ether || !pip)
+ return VERR_INVALID_PARAMETER;
+
+ for (i = 0; i < NB_ADDR; i++)
+ {
+ if ( bootp_clients[i].allocated
+ && memcmp(bootp_clients[i].macaddr, ether, ETH_ALEN) == 0)
+ {
+ *pip = bootp_clients[i].addr.s_addr;
+ return VINF_SUCCESS;
+ }
+ }
+
+ *pip = INADDR_ANY;
+ return VERR_NOT_FOUND;
+}
+
+int bootp_cache_lookup_ether_by_ip(PNATState pData, uint32_t ip, uint8_t *ether)
+{
+ int i;
+ for (i = 0; i < NB_ADDR; i++)
+ {
+ if ( bootp_clients[i].allocated
+ && ip == bootp_clients[i].addr.s_addr)
+ {
+ if (ether != NULL)
+ memcpy(ether, bootp_clients[i].macaddr, ETH_ALEN);
+ return VINF_SUCCESS;
+ }
+ }
+
+ return VERR_NOT_FOUND;
+}
+
+/*
+ * Initialize dhcp server
+ * @returns 0 - if initialization is ok, non-zero otherwise
+ */
+int bootp_dhcp_init(PNATState pData)
+{
+ pData->pbootp_clients = RTMemAllocZ(sizeof(BOOTPClient) * NB_ADDR);
+ if (!pData->pbootp_clients)
+ return VERR_NO_MEMORY;
+
+ return VINF_SUCCESS;
+}
+
+int bootp_dhcp_fini(PNATState pData)
+{
+ if (pData->pbootp_clients != NULL)
+ RTMemFree(pData->pbootp_clients);
+
+ return VINF_SUCCESS;
+}
diff --git a/src/VBox/Devices/Network/slirp/bootp.h b/src/VBox/Devices/Network/slirp/bootp.h
new file mode 100644
index 00000000..9c8ec8cc
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bootp.h
@@ -0,0 +1,158 @@
+/* $Id: bootp.h $ */
+/** @file
+ * NAT - BOOTP/DHCP server emulation (declarations/defines).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* bootp/dhcp defines */
+
+#define BOOTP_SERVER 67
+#define BOOTP_CLIENT 68
+
+#define BOOTP_REQUEST 1
+#define BOOTP_REPLY 2
+
+#define RFC1533_COOKIE 99, 130, 83, 99
+#define RFC1533_PAD 0
+#define RFC1533_NETMASK 1
+#define RFC1533_TIMEOFFSET 2
+#define RFC1533_GATEWAY 3
+#define RFC1533_TIMESERVER 4
+#define RFC1533_IEN116NS 5
+#define RFC1533_DNS 6
+#define RFC1533_LOGSERVER 7
+#define RFC1533_COOKIESERVER 8
+#define RFC1533_LPRSERVER 9
+#define RFC1533_IMPRESSSERVER 10
+#define RFC1533_RESOURCESERVER 11
+#define RFC1533_HOSTNAME 12
+#define RFC1533_BOOTFILESIZE 13
+#define RFC1533_MERITDUMPFILE 14
+#define RFC1533_DOMAINNAME 15
+#define RFC1533_SWAPSERVER 16
+#define RFC1533_ROOTPATH 17
+#define RFC1533_EXTENSIONPATH 18
+#define RFC1533_IPFORWARDING 19
+#define RFC1533_IPSOURCEROUTING 20
+#define RFC1533_IPPOLICYFILTER 21
+#define RFC1533_IPMAXREASSEMBLY 22
+#define RFC1533_IPTTL 23
+#define RFC1533_IPMTU 24
+#define RFC1533_IPMTUPLATEAU 25
+#define RFC1533_INTMTU 26
+#define RFC1533_INTLOCALSUBNETS 27
+#define RFC1533_INTBROADCAST 28
+#define RFC1533_INTICMPDISCOVER 29
+#define RFC1533_INTICMPRESPOND 30
+#define RFC1533_INTROUTEDISCOVER 31
+#define RFC1533_INTROUTESOLICIT 32
+#define RFC1533_INTSTATICROUTES 33
+#define RFC1533_LLTRAILERENCAP 34
+#define RFC1533_LLARPCACHETMO 35
+#define RFC1533_LLETHERNETENCAP 36
+#define RFC1533_TCPTTL 37
+#define RFC1533_TCPKEEPALIVETMO 38
+#define RFC1533_TCPKEEPALIVEGB 39
+#define RFC1533_NISDOMAIN 40
+#define RFC1533_NISSERVER 41
+#define RFC1533_NTPSERVER 42
+#define RFC1533_VENDOR 43
+#define RFC1533_NBNS 44
+#define RFC1533_NBDD 45
+#define RFC1533_NBNT 46
+#define RFC1533_NBSCOPE 47
+#define RFC1533_XFS 48
+#define RFC1533_XDM 49
+
+#define RFC2132_REQ_ADDR 50
+#define RFC2132_LEASE_TIME 51
+#define RFC2132_MSG_TYPE 53
+#define RFC2132_SRV_ID 54
+#define RFC2132_PARAM_LIST 55
+#define RFC2132_MAX_SIZE 57
+#define RFC2132_RENEWAL_TIME 58
+#define RFC2132_REBIND_TIME 59
+
+#define DHCPDISCOVER 1
+#define DHCPOFFER 2
+#define DHCPREQUEST 3
+#define DHCPDECLINE 4
+#define DHCPACK 5
+#define DHCPNAK 6
+#define DHCPRELEASE 7
+#define DHCPINFORM 8
+
+#define RFC1533_VENDOR_MAJOR 0
+#define RFC1533_VENDOR_MINOR 0
+
+#define RFC1533_VENDOR_MAGIC 128
+#define RFC1533_VENDOR_ADDPARM 129
+#define RFC1533_VENDOR_ETHDEV 130
+#define RFC1533_VENDOR_HOWTO 132
+#define RFC1533_VENDOR_MNUOPTS 160
+#define RFC1533_VENDOR_SELECTION 176
+#define RFC1533_VENDOR_MOTD 184
+#define RFC1533_VENDOR_NUMOFMOTD 8
+#define RFC1533_VENDOR_IMG 192
+#define RFC1533_VENDOR_NUMOFIMG 16
+
+#define RFC1533_END 255
+#define BOOTP_VENDOR_LEN 64
+#define DHCP_OPT_LEN 312
+
+/* RFC 2131 */
+struct bootp_t
+{
+ struct ip ip; /**< header: IP header */
+ struct udphdr udp; /**< header: UDP header */
+ uint8_t bp_op; /**< opcode (BOOTP_REQUEST, BOOTP_REPLY) */
+ uint8_t bp_htype; /**< hardware type */
+ uint8_t bp_hlen; /**< hardware address length */
+ uint8_t bp_hops; /**< hop count */
+ uint32_t bp_xid; /**< transaction ID */
+ uint16_t bp_secs; /**< numnber of seconds */
+ uint16_t bp_flags; /**< flags (DHCP_FLAGS_B) */
+ struct in_addr bp_ciaddr; /**< client IP address */
+ struct in_addr bp_yiaddr; /**< your IP address */
+ struct in_addr bp_siaddr; /**< server IP address */
+ struct in_addr bp_giaddr; /**< gateway IP address */
+ uint8_t bp_hwaddr[16]; /** client hardware address */
+ uint8_t bp_sname[64]; /** server host name */
+ uint8_t bp_file[128]; /** boot filename */
+ uint8_t bp_vend[DHCP_OPT_LEN]; /**< vendor specific info */
+};
+
+
+#define DHCP_FLAGS_B (1<<15) /**< B, broadcast */
+struct bootp_ext
+{
+ uint8_t bpe_tag;
+ uint8_t bpe_len;
+};
+
+void bootp_input(PNATState, struct mbuf *m);
+int bootp_cache_lookup_ip_by_ether(PNATState, const uint8_t *, uint32_t *);
+int bootp_cache_lookup_ether_by_ip(PNATState, uint32_t, uint8_t *);
+int bootp_dhcp_init(PNATState);
+int bootp_dhcp_fini(PNATState);
diff --git a/src/VBox/Devices/Network/slirp/bsd/Makefile.kup b/src/VBox/Devices/Network/slirp/bsd/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bsd/Makefile.kup
diff --git a/src/VBox/Devices/Network/slirp/bsd/amd64/Makefile.kup b/src/VBox/Devices/Network/slirp/bsd/amd64/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bsd/amd64/Makefile.kup
diff --git a/src/VBox/Devices/Network/slirp/bsd/amd64/in_cksum.c b/src/VBox/Devices/Network/slirp/bsd/amd64/in_cksum.c
new file mode 100644
index 00000000..351003e3
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bsd/amd64/in_cksum.c
@@ -0,0 +1,242 @@
+/* $NetBSD: in_cksum.c,v 1.7 1997/09/02 13:18:15 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 1988, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1996
+ * Matt Thomas <matt@3am-software.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef VBOX
+#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
+__FBSDID("$FreeBSD: src/sys/amd64/amd64/in_cksum.c,v 1.5.20.1 2009/04/15 03:14:26 kensmith Exp $");
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/systm.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <machine/in_cksum.h>
+#else
+# include "in_cksum.h"
+# include "slirp.h"
+#endif
+
+/*
+ * Checksum routine for Internet Protocol family headers
+ * (Portable Alpha version).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ */
+
+#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
+#define REDUCE32 \
+ { \
+ q_util.q = sum; \
+ sum = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
+ }
+#define REDUCE16 \
+ { \
+ q_util.q = sum; \
+ l_util.l = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
+ sum = l_util.s[0] + l_util.s[1]; \
+ ADDCARRY(sum); \
+ }
+
+static const u_int32_t in_masks[] = {
+ /*0 bytes*/ /*1 byte*/ /*2 bytes*/ /*3 bytes*/
+ 0x00000000, 0x000000FF, 0x0000FFFF, 0x00FFFFFF, /* offset 0 */
+ 0x00000000, 0x0000FF00, 0x00FFFF00, 0xFFFFFF00, /* offset 1 */
+ 0x00000000, 0x00FF0000, 0xFFFF0000, 0xFFFF0000, /* offset 2 */
+ 0x00000000, 0xFF000000, 0xFF000000, 0xFF000000, /* offset 3 */
+};
+
+union l_util {
+ u_int16_t s[2];
+ u_int32_t l;
+};
+union q_util {
+ u_int16_t s[4];
+ u_int32_t l[2];
+ u_int64_t q;
+};
+
+static u_int64_t
+in_cksumdata(const void *buf, int len)
+{
+ const u_int32_t *lw = (const u_int32_t *) buf;
+ u_int64_t sum = 0;
+ u_int64_t prefilled;
+ int offset;
+ union q_util q_util;
+
+ if ((3 & (intptr_t) lw) == 0 && len == 20) {
+ sum = (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3] + lw[4];
+ REDUCE32;
+ return sum;
+ }
+
+ if ((offset = 3 & (intptr_t) lw) != 0) {
+ const u_int32_t *masks = in_masks + (offset << 2);
+ lw = (u_int32_t *) (((RTHCUINTPTR) lw) - offset);
+ sum = *lw++ & masks[len >= 3 ? 3 : len];
+ len -= 4 - offset;
+ if (len <= 0) {
+ REDUCE32;
+ return sum;
+ }
+ }
+#if 0
+ /*
+ * Force to cache line boundary.
+ */
+ offset = 32 - (0x1f & (long) lw);
+ if (offset < 32 && len > offset) {
+ len -= offset;
+ if (4 & offset) {
+ sum += (u_int64_t) lw[0];
+ lw += 1;
+ }
+ if (8 & offset) {
+ sum += (u_int64_t) lw[0] + lw[1];
+ lw += 2;
+ }
+ if (16 & offset) {
+ sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
+ lw += 4;
+ }
+ }
+#endif
+ /*
+ * access prefilling to start load of next cache line.
+ * then add current cache line
+ * save result of prefilling for loop iteration.
+ */
+ prefilled = lw[0];
+ while ((len -= 32) >= 4) {
+ u_int64_t prefilling = lw[8];
+ sum += prefilled + lw[1] + lw[2] + lw[3]
+ + lw[4] + lw[5] + lw[6] + lw[7];
+ lw += 8;
+ prefilled = prefilling;
+ }
+ if (len >= 0) {
+ sum += prefilled + lw[1] + lw[2] + lw[3]
+ + lw[4] + lw[5] + lw[6] + lw[7];
+ lw += 8;
+ } else {
+ len += 32;
+ }
+ while ((len -= 16) >= 0) {
+ sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
+ lw += 4;
+ }
+ len += 16;
+ while ((len -= 4) >= 0) {
+ sum += (u_int64_t) *lw++;
+ }
+ len += 4;
+ if (len > 0)
+ sum += (u_int64_t) (in_masks[len] & *lw);
+ REDUCE32;
+ return sum;
+}
+
+u_short
+in_addword(u_short a, u_short b)
+{
+ u_int64_t sum = a + b;
+
+ ADDCARRY(sum);
+ return (sum);
+}
+
+u_short
+in_pseudo(u_int32_t a, u_int32_t b, u_int32_t c)
+{
+ u_int64_t sum;
+ union q_util q_util;
+ union l_util l_util;
+
+ sum = (u_int64_t) a + b + c;
+ REDUCE16;
+ return (sum);
+}
+
+u_short
+in_cksum_skip(struct mbuf *m, int len, int skip)
+{
+ u_int64_t sum = 0;
+ int mlen = 0;
+ int clen = 0;
+ caddr_t addr;
+ union q_util q_util;
+ union l_util l_util;
+
+ len -= skip;
+ for (; skip && m; m = m->m_next) {
+ if (m->m_len > skip) {
+ mlen = m->m_len - skip;
+ addr = mtod(m, caddr_t) + skip;
+ goto skip_start;
+ } else {
+ skip -= m->m_len;
+ }
+ }
+
+ for (; m && len; m = m->m_next) {
+ if (m->m_len == 0)
+ continue;
+ mlen = m->m_len;
+ addr = mtod(m, caddr_t);
+skip_start:
+ if (len < mlen)
+ mlen = len;
+ if ((clen ^ (intptr_t) addr) & 1)
+ sum += in_cksumdata(addr, mlen) << 8;
+ else
+ sum += in_cksumdata(addr, mlen);
+
+ clen += mlen;
+ len -= mlen;
+ }
+ REDUCE16;
+ return (~sum & 0xffff);
+}
+
+u_int in_cksum_hdr(const struct ip *ip)
+{
+ u_int64_t sum = in_cksumdata(ip, sizeof(struct ip));
+ union q_util q_util;
+ union l_util l_util;
+ REDUCE16;
+ return (~sum & 0xffff);
+}
diff --git a/src/VBox/Devices/Network/slirp/bsd/amd64/include/in_cksum.h b/src/VBox/Devices/Network/slirp/bsd/amd64/include/in_cksum.h
new file mode 100644
index 00000000..47a4565e
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bsd/amd64/include/in_cksum.h
@@ -0,0 +1,84 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ *
+ * from tahoe: in_cksum.c 1.2 86/01/05
+ * from: @(#)in_cksum.c 1.3 (Berkeley) 1/19/91
+ * from: Id: in_cksum.c,v 1.8 1995/12/03 18:35:19 bde Exp
+ * $FreeBSD: src/sys/amd64/include/in_cksum.h,v 1.5.20.1 2009/04/15 03:14:26 kensmith Exp $
+ */
+
+#ifndef _MACHINE_IN_CKSUM_H_
+#define _MACHINE_IN_CKSUM_H_ 1
+
+#ifndef VBOX
+#ifndef _SYS_CDEFS_H_
+#error this file needs sys/cdefs.h as a prerequisite
+#endif
+
+#include <sys/cdefs.h>
+#else
+# include "slirp.h"
+#endif
+
+#define in_cksum(m, len) in_cksum_skip(m, len, 0)
+
+/*
+ * It it useful to have an Internet checksum routine which is inlineable
+ * and optimized specifically for the task of computing IP header checksums
+ * in the normal case (where there are no options and the header length is
+ * therefore always exactly five 32-bit words.
+ */
+#ifdef __CC_SUPPORTS___INLINE
+
+static __inline void
+in_cksum_update(struct ip *ip)
+{
+ int __tmpsum;
+ __tmpsum = (int)ntohs(ip->ip_sum) + 256;
+ ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16));
+}
+
+#else
+
+#define in_cksum_update(ip) \
+ do { \
+ int __tmpsum; \
+ __tmpsum = (int)ntohs(ip->ip_sum) + 256; \
+ ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16)); \
+ } while(0)
+
+#endif
+
+#if defined(_KERNEL) || defined(VBOX)
+u_int in_cksum_hdr(const struct ip *ip);
+u_short in_addword(u_short sum, u_short b);
+u_short in_pseudo(u_int sum, u_int b, u_int c);
+u_short in_cksum_skip(struct mbuf *m, int len, int skip);
+#endif
+
+#endif /* _MACHINE_IN_CKSUM_H_ */
diff --git a/src/VBox/Devices/Network/slirp/bsd/arm64/Makefile.kup b/src/VBox/Devices/Network/slirp/bsd/arm64/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bsd/arm64/Makefile.kup
diff --git a/src/VBox/Devices/Network/slirp/bsd/arm64/in_cksum.c b/src/VBox/Devices/Network/slirp/bsd/arm64/in_cksum.c
new file mode 100644
index 00000000..351003e3
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bsd/arm64/in_cksum.c
@@ -0,0 +1,242 @@
+/* $NetBSD: in_cksum.c,v 1.7 1997/09/02 13:18:15 thorpej Exp $ */
+
+/*-
+ * Copyright (c) 1988, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ * Copyright (c) 1996
+ * Matt Thomas <matt@3am-software.com>
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
+ */
+
+#ifndef VBOX
+#include <sys/cdefs.h> /* RCS ID & Copyright macro defns */
+__FBSDID("$FreeBSD: src/sys/amd64/amd64/in_cksum.c,v 1.5.20.1 2009/04/15 03:14:26 kensmith Exp $");
+
+#include <sys/param.h>
+#include <sys/mbuf.h>
+#include <sys/systm.h>
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <machine/in_cksum.h>
+#else
+# include "in_cksum.h"
+# include "slirp.h"
+#endif
+
+/*
+ * Checksum routine for Internet Protocol family headers
+ * (Portable Alpha version).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ */
+
+#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
+#define REDUCE32 \
+ { \
+ q_util.q = sum; \
+ sum = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
+ }
+#define REDUCE16 \
+ { \
+ q_util.q = sum; \
+ l_util.l = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
+ sum = l_util.s[0] + l_util.s[1]; \
+ ADDCARRY(sum); \
+ }
+
+static const u_int32_t in_masks[] = {
+ /*0 bytes*/ /*1 byte*/ /*2 bytes*/ /*3 bytes*/
+ 0x00000000, 0x000000FF, 0x0000FFFF, 0x00FFFFFF, /* offset 0 */
+ 0x00000000, 0x0000FF00, 0x00FFFF00, 0xFFFFFF00, /* offset 1 */
+ 0x00000000, 0x00FF0000, 0xFFFF0000, 0xFFFF0000, /* offset 2 */
+ 0x00000000, 0xFF000000, 0xFF000000, 0xFF000000, /* offset 3 */
+};
+
+union l_util {
+ u_int16_t s[2];
+ u_int32_t l;
+};
+union q_util {
+ u_int16_t s[4];
+ u_int32_t l[2];
+ u_int64_t q;
+};
+
+static u_int64_t
+in_cksumdata(const void *buf, int len)
+{
+ const u_int32_t *lw = (const u_int32_t *) buf;
+ u_int64_t sum = 0;
+ u_int64_t prefilled;
+ int offset;
+ union q_util q_util;
+
+ if ((3 & (intptr_t) lw) == 0 && len == 20) {
+ sum = (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3] + lw[4];
+ REDUCE32;
+ return sum;
+ }
+
+ if ((offset = 3 & (intptr_t) lw) != 0) {
+ const u_int32_t *masks = in_masks + (offset << 2);
+ lw = (u_int32_t *) (((RTHCUINTPTR) lw) - offset);
+ sum = *lw++ & masks[len >= 3 ? 3 : len];
+ len -= 4 - offset;
+ if (len <= 0) {
+ REDUCE32;
+ return sum;
+ }
+ }
+#if 0
+ /*
+ * Force to cache line boundary.
+ */
+ offset = 32 - (0x1f & (long) lw);
+ if (offset < 32 && len > offset) {
+ len -= offset;
+ if (4 & offset) {
+ sum += (u_int64_t) lw[0];
+ lw += 1;
+ }
+ if (8 & offset) {
+ sum += (u_int64_t) lw[0] + lw[1];
+ lw += 2;
+ }
+ if (16 & offset) {
+ sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
+ lw += 4;
+ }
+ }
+#endif
+ /*
+ * access prefilling to start load of next cache line.
+ * then add current cache line
+ * save result of prefilling for loop iteration.
+ */
+ prefilled = lw[0];
+ while ((len -= 32) >= 4) {
+ u_int64_t prefilling = lw[8];
+ sum += prefilled + lw[1] + lw[2] + lw[3]
+ + lw[4] + lw[5] + lw[6] + lw[7];
+ lw += 8;
+ prefilled = prefilling;
+ }
+ if (len >= 0) {
+ sum += prefilled + lw[1] + lw[2] + lw[3]
+ + lw[4] + lw[5] + lw[6] + lw[7];
+ lw += 8;
+ } else {
+ len += 32;
+ }
+ while ((len -= 16) >= 0) {
+ sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
+ lw += 4;
+ }
+ len += 16;
+ while ((len -= 4) >= 0) {
+ sum += (u_int64_t) *lw++;
+ }
+ len += 4;
+ if (len > 0)
+ sum += (u_int64_t) (in_masks[len] & *lw);
+ REDUCE32;
+ return sum;
+}
+
+u_short
+in_addword(u_short a, u_short b)
+{
+ u_int64_t sum = a + b;
+
+ ADDCARRY(sum);
+ return (sum);
+}
+
+u_short
+in_pseudo(u_int32_t a, u_int32_t b, u_int32_t c)
+{
+ u_int64_t sum;
+ union q_util q_util;
+ union l_util l_util;
+
+ sum = (u_int64_t) a + b + c;
+ REDUCE16;
+ return (sum);
+}
+
+u_short
+in_cksum_skip(struct mbuf *m, int len, int skip)
+{
+ u_int64_t sum = 0;
+ int mlen = 0;
+ int clen = 0;
+ caddr_t addr;
+ union q_util q_util;
+ union l_util l_util;
+
+ len -= skip;
+ for (; skip && m; m = m->m_next) {
+ if (m->m_len > skip) {
+ mlen = m->m_len - skip;
+ addr = mtod(m, caddr_t) + skip;
+ goto skip_start;
+ } else {
+ skip -= m->m_len;
+ }
+ }
+
+ for (; m && len; m = m->m_next) {
+ if (m->m_len == 0)
+ continue;
+ mlen = m->m_len;
+ addr = mtod(m, caddr_t);
+skip_start:
+ if (len < mlen)
+ mlen = len;
+ if ((clen ^ (intptr_t) addr) & 1)
+ sum += in_cksumdata(addr, mlen) << 8;
+ else
+ sum += in_cksumdata(addr, mlen);
+
+ clen += mlen;
+ len -= mlen;
+ }
+ REDUCE16;
+ return (~sum & 0xffff);
+}
+
+u_int in_cksum_hdr(const struct ip *ip)
+{
+ u_int64_t sum = in_cksumdata(ip, sizeof(struct ip));
+ union q_util q_util;
+ union l_util l_util;
+ REDUCE16;
+ return (~sum & 0xffff);
+}
diff --git a/src/VBox/Devices/Network/slirp/bsd/arm64/include/in_cksum.h b/src/VBox/Devices/Network/slirp/bsd/arm64/include/in_cksum.h
new file mode 100644
index 00000000..47a4565e
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bsd/arm64/include/in_cksum.h
@@ -0,0 +1,84 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ *
+ * from tahoe: in_cksum.c 1.2 86/01/05
+ * from: @(#)in_cksum.c 1.3 (Berkeley) 1/19/91
+ * from: Id: in_cksum.c,v 1.8 1995/12/03 18:35:19 bde Exp
+ * $FreeBSD: src/sys/amd64/include/in_cksum.h,v 1.5.20.1 2009/04/15 03:14:26 kensmith Exp $
+ */
+
+#ifndef _MACHINE_IN_CKSUM_H_
+#define _MACHINE_IN_CKSUM_H_ 1
+
+#ifndef VBOX
+#ifndef _SYS_CDEFS_H_
+#error this file needs sys/cdefs.h as a prerequisite
+#endif
+
+#include <sys/cdefs.h>
+#else
+# include "slirp.h"
+#endif
+
+#define in_cksum(m, len) in_cksum_skip(m, len, 0)
+
+/*
+ * It it useful to have an Internet checksum routine which is inlineable
+ * and optimized specifically for the task of computing IP header checksums
+ * in the normal case (where there are no options and the header length is
+ * therefore always exactly five 32-bit words.
+ */
+#ifdef __CC_SUPPORTS___INLINE
+
+static __inline void
+in_cksum_update(struct ip *ip)
+{
+ int __tmpsum;
+ __tmpsum = (int)ntohs(ip->ip_sum) + 256;
+ ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16));
+}
+
+#else
+
+#define in_cksum_update(ip) \
+ do { \
+ int __tmpsum; \
+ __tmpsum = (int)ntohs(ip->ip_sum) + 256; \
+ ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16)); \
+ } while(0)
+
+#endif
+
+#if defined(_KERNEL) || defined(VBOX)
+u_int in_cksum_hdr(const struct ip *ip);
+u_short in_addword(u_short sum, u_short b);
+u_short in_pseudo(u_int sum, u_int b, u_int c);
+u_short in_cksum_skip(struct mbuf *m, int len, int skip);
+#endif
+
+#endif /* _MACHINE_IN_CKSUM_H_ */
diff --git a/src/VBox/Devices/Network/slirp/bsd/i386/Makefile.kup b/src/VBox/Devices/Network/slirp/bsd/i386/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bsd/i386/Makefile.kup
diff --git a/src/VBox/Devices/Network/slirp/bsd/i386/in_cksum.c b/src/VBox/Devices/Network/slirp/bsd/i386/in_cksum.c
new file mode 100644
index 00000000..2be70636
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bsd/i386/in_cksum.c
@@ -0,0 +1,499 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ *
+ * from tahoe: in_cksum.c 1.2 86/01/05
+ * from: @(#)in_cksum.c 1.3 (Berkeley) 1/19/91
+ */
+
+#ifndef VBOX
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/i386/i386/in_cksum.c,v 1.28.10.1.6.1 2009/04/15 03:14:26 kensmith Exp $");
+
+/*
+ * MPsafe: alfred
+ */
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/ip.h>
+
+#include <machine/in_cksum.h>
+#else
+# include "in_cksum.h"
+# include "slirp.h"
+#endif
+
+/*
+ * Checksum routine for Internet Protocol family headers.
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ *
+ * This implementation is 386 version.
+ */
+
+#undef ADDCARRY
+#define ADDCARRY(x) if ((x) > 0xffff) (x) -= 0xffff
+/*
+ * icc needs to be special cased here, as the asm code below results
+ * in broken code if compiled with icc.
+ */
+#if !defined(__GNUCLIKE_ASM) || defined(__INTEL_COMPILER)
+/* non gcc parts stolen from sys/alpha/alpha/in_cksum.c */
+#define REDUCE32 \
+ { \
+ q_util.q = sum; \
+ sum = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
+ }
+#define REDUCE16 \
+ { \
+ q_util.q = sum; \
+ l_util.l = q_util.s[0] + q_util.s[1] + q_util.s[2] + q_util.s[3]; \
+ sum = l_util.s[0] + l_util.s[1]; \
+ ADDCARRY(sum); \
+ }
+#endif
+#define REDUCE {sum = (sum & 0xffff) + (sum >> 16); ADDCARRY(sum);}
+
+#if !defined(__GNUCLIKE_ASM) || defined(__INTEL_COMPILER)
+static const u_int32_t in_masks[] = {
+ /*0 bytes*/ /*1 byte*/ /*2 bytes*/ /*3 bytes*/
+ 0x00000000, 0x000000FF, 0x0000FFFF, 0x00FFFFFF, /* offset 0 */
+ 0x00000000, 0x0000FF00, 0x00FFFF00, 0xFFFFFF00, /* offset 1 */
+ 0x00000000, 0x00FF0000, 0xFFFF0000, 0xFFFF0000, /* offset 2 */
+ 0x00000000, 0xFF000000, 0xFF000000, 0xFF000000, /* offset 3 */
+};
+
+union l_util {
+ u_int16_t s[2];
+ u_int32_t l;
+};
+union q_util {
+ u_int16_t s[4];
+ u_int32_t l[2];
+ u_int64_t q;
+};
+
+static u_int64_t
+in_cksumdata(const u_int32_t *lw, int len)
+{
+ u_int64_t sum = 0;
+ u_int64_t prefilled;
+ int offset;
+ union q_util q_util;
+
+ if ((3 & (long) lw) == 0 && len == 20) {
+ sum = (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3] + lw[4];
+ REDUCE32;
+ return sum;
+ }
+
+ if ((offset = 3 & (long) lw) != 0) {
+ const u_int32_t *masks = in_masks + (offset << 2);
+ lw = (u_int32_t *) (((RTHCUINTPTR) lw) - offset);
+ sum = *lw++ & masks[len >= 3 ? 3 : len];
+ len -= 4 - offset;
+ if (len <= 0) {
+ REDUCE32;
+ return sum;
+ }
+ }
+#if 0
+ /*
+ * Force to cache line boundary.
+ */
+ offset = 32 - (0x1f & (long) lw);
+ if (offset < 32 && len > offset) {
+ len -= offset;
+ if (4 & offset) {
+ sum += (u_int64_t) lw[0];
+ lw += 1;
+ }
+ if (8 & offset) {
+ sum += (u_int64_t) lw[0] + lw[1];
+ lw += 2;
+ }
+ if (16 & offset) {
+ sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
+ lw += 4;
+ }
+ }
+#endif
+ /*
+ * access prefilling to start load of next cache line.
+ * then add current cache line
+ * save result of prefilling for loop iteration.
+ */
+ prefilled = lw[0];
+ while ((len -= 32) >= 4) {
+ u_int64_t prefilling = lw[8];
+ sum += prefilled + lw[1] + lw[2] + lw[3]
+ + lw[4] + lw[5] + lw[6] + lw[7];
+ lw += 8;
+ prefilled = prefilling;
+ }
+ if (len >= 0) {
+ sum += prefilled + lw[1] + lw[2] + lw[3]
+ + lw[4] + lw[5] + lw[6] + lw[7];
+ lw += 8;
+ } else {
+ len += 32;
+ }
+ while ((len -= 16) >= 0) {
+ sum += (u_int64_t) lw[0] + lw[1] + lw[2] + lw[3];
+ lw += 4;
+ }
+ len += 16;
+ while ((len -= 4) >= 0) {
+ sum += (u_int64_t) *lw++;
+ }
+ len += 4;
+ if (len > 0)
+ sum += (u_int64_t) (in_masks[len] & *lw);
+ REDUCE32;
+ return sum;
+}
+
+u_short
+in_addword(u_short a, u_short b)
+{
+ u_int64_t sum = a + b;
+
+ ADDCARRY(sum);
+ return (sum);
+}
+
+u_short
+in_pseudo(u_int32_t a, u_int32_t b, u_int32_t c)
+{
+ u_int64_t sum;
+ union q_util q_util;
+ union l_util l_util;
+
+ sum = (u_int64_t) a + b + c;
+ REDUCE16;
+ return (sum);
+}
+
+u_short
+in_cksum_skip(struct mbuf *m, int len, int skip)
+{
+ u_int64_t sum = 0;
+ int mlen = 0;
+ int clen = 0;
+ caddr_t addr;
+ union q_util q_util;
+ union l_util l_util;
+
+ len -= skip;
+ for (; skip && m; m = m->m_next) {
+ if (m->m_len > skip) {
+ mlen = m->m_len - skip;
+ addr = mtod(m, caddr_t) + skip;
+ goto skip_start;
+ } else {
+ skip -= m->m_len;
+ }
+ }
+
+ for (; m && len; m = m->m_next) {
+ if (m->m_len == 0)
+ continue;
+ mlen = m->m_len;
+ addr = mtod(m, caddr_t);
+skip_start:
+ if (len < mlen)
+ mlen = len;
+ if ((clen ^ (long) addr) & 1)
+ sum += in_cksumdata((const u_int32_t *)addr, mlen) << 8;
+ else
+ sum += in_cksumdata((const u_int32_t *)addr, mlen);
+
+ clen += mlen;
+ len -= mlen;
+ }
+ REDUCE16;
+ return (~sum & 0xffff);
+}
+
+u_int in_cksum_hdr(const struct ip *ip)
+{
+ u_int64_t sum = in_cksumdata((const u_int32_t *)ip, sizeof(struct ip));
+ union q_util q_util;
+ union l_util l_util;
+
+ REDUCE16;
+ return (~sum & 0xffff);
+}
+#else
+
+/*
+ * These asm statements require __volatile because they pass information
+ * via the condition codes. GCC does not currently provide a way to specify
+ * the condition codes as an input or output operand.
+ *
+ * The LOAD macro below is effectively a prefetch into cache. GCC will
+ * load the value into a register but will not use it. Since modern CPUs
+ * reorder operations, this will generally take place in parallel with
+ * other calculations.
+ */
+u_short
+in_cksum_skip(m, len, skip)
+ struct mbuf *m;
+ int len;
+ int skip;
+{
+ register u_short *w;
+ register unsigned sum = 0;
+ register int mlen = 0;
+ int byte_swapped = 0;
+ union { char c[2]; u_short s; } su;
+
+ len -= skip;
+ for (; skip && m; m = m->m_next) {
+ if (m->m_len > skip) {
+ mlen = m->m_len - skip;
+ w = (u_short *)(mtod(m, u_char *) + skip);
+ goto skip_start;
+ } else {
+ skip -= m->m_len;
+ }
+ }
+
+ for (;m && len; m = m->m_next) {
+ if (m->m_len == 0)
+ continue;
+ w = mtod(m, u_short *);
+ if (mlen == -1) {
+ /*
+ * The first byte of this mbuf is the continuation
+ * of a word spanning between this mbuf and the
+ * last mbuf.
+ */
+
+ /* su.c[0] is already saved when scanning previous
+ * mbuf. sum was REDUCEd when we found mlen == -1
+ */
+ su.c[1] = *(u_char *)w;
+ sum += su.s;
+ w = (u_short *)((char *)w + 1);
+ mlen = m->m_len - 1;
+ len--;
+ } else
+ mlen = m->m_len;
+skip_start:
+ if (len < mlen)
+ mlen = len;
+ len -= mlen;
+ /*
+ * Force to long boundary so we do longword aligned
+ * memory operations
+ */
+ if (3 & (int) w) {
+ REDUCE;
+ if ((1 & (int) w) && (mlen > 0)) {
+ sum <<= 8;
+ su.c[0] = *(char *)w;
+ w = (u_short *)((char *)w + 1);
+ mlen--;
+ byte_swapped = 1;
+ }
+ if ((2 & (int) w) && (mlen >= 2)) {
+ sum += *w++;
+ mlen -= 2;
+ }
+ }
+ /*
+ * Advance to a 486 cache line boundary.
+ */
+ if (4 & (int) w && mlen >= 4) {
+ __asm __volatile (
+ "addl %1, %0\n"
+ "adcl $0, %0"
+ : "+r" (sum)
+ : "g" (((const u_int32_t *)w)[0])
+ );
+ w += 2;
+ mlen -= 4;
+ }
+ if (8 & (int) w && mlen >= 8) {
+ __asm __volatile (
+ "addl %1, %0\n"
+ "adcl %2, %0\n"
+ "adcl $0, %0"
+ : "+r" (sum)
+ : "g" (((const u_int32_t *)w)[0]),
+ "g" (((const u_int32_t *)w)[1])
+ );
+ w += 4;
+ mlen -= 8;
+ }
+ /*
+ * Do as much of the checksum as possible 32 bits at at time.
+ * In fact, this loop is unrolled to make overhead from
+ * branches &c small.
+ */
+ mlen -= 1;
+ while ((mlen -= 32) >= 0) {
+ /*
+ * Add with carry 16 words and fold in the last
+ * carry by adding a 0 with carry.
+ *
+ * The early ADD(16) and the LOAD(32) are to load
+ * the next 2 cache lines in advance on 486's. The
+ * 486 has a penalty of 2 clock cycles for loading
+ * a cache line, plus whatever time the external
+ * memory takes to load the first word(s) addressed.
+ * These penalties are unavoidable. Subsequent
+ * accesses to a cache line being loaded (and to
+ * other external memory?) are delayed until the
+ * whole load finishes. These penalties are mostly
+ * avoided by not accessing external memory for
+ * 8 cycles after the ADD(16) and 12 cycles after
+ * the LOAD(32). The loop terminates when mlen
+ * is initially 33 (not 32) to guaranteed that
+ * the LOAD(32) is within bounds.
+ */
+ __asm __volatile (
+ "addl %1, %0\n"
+ "adcl %2, %0\n"
+ "adcl %3, %0\n"
+ "adcl %4, %0\n"
+ "adcl %5, %0\n"
+ "mov %6, %%eax\n"
+ "adcl %7, %0\n"
+ "adcl %8, %0\n"
+ "adcl %9, %0\n"
+ "adcl $0, %0"
+ : "+r" (sum)
+ : "g" (((const u_int32_t *)w)[4]),
+ "g" (((const u_int32_t *)w)[0]),
+ "g" (((const u_int32_t *)w)[1]),
+ "g" (((const u_int32_t *)w)[2]),
+ "g" (((const u_int32_t *)w)[3]),
+ "g" (((const u_int32_t *)w)[8]),
+ "g" (((const u_int32_t *)w)[5]),
+ "g" (((const u_int32_t *)w)[6]),
+ "g" (((const u_int32_t *)w)[7])
+ : "eax"
+ );
+ w += 16;
+ }
+ mlen += 32 + 1;
+ if (mlen >= 32) {
+ __asm __volatile (
+ "addl %1, %0\n"
+ "adcl %2, %0\n"
+ "adcl %3, %0\n"
+ "adcl %4, %0\n"
+ "adcl %5, %0\n"
+ "adcl %6, %0\n"
+ "adcl %7, %0\n"
+ "adcl %8, %0\n"
+ "adcl $0, %0"
+ : "+r" (sum)
+ : "g" (((const u_int32_t *)w)[4]),
+ "g" (((const u_int32_t *)w)[0]),
+ "g" (((const u_int32_t *)w)[1]),
+ "g" (((const u_int32_t *)w)[2]),
+ "g" (((const u_int32_t *)w)[3]),
+ "g" (((const u_int32_t *)w)[5]),
+ "g" (((const u_int32_t *)w)[6]),
+ "g" (((const u_int32_t *)w)[7])
+ );
+ w += 16;
+ mlen -= 32;
+ }
+ if (mlen >= 16) {
+ __asm __volatile (
+ "addl %1, %0\n"
+ "adcl %2, %0\n"
+ "adcl %3, %0\n"
+ "adcl %4, %0\n"
+ "adcl $0, %0"
+ : "+r" (sum)
+ : "g" (((const u_int32_t *)w)[0]),
+ "g" (((const u_int32_t *)w)[1]),
+ "g" (((const u_int32_t *)w)[2]),
+ "g" (((const u_int32_t *)w)[3])
+ );
+ w += 8;
+ mlen -= 16;
+ }
+ if (mlen >= 8) {
+ __asm __volatile (
+ "addl %1, %0\n"
+ "adcl %2, %0\n"
+ "adcl $0, %0"
+ : "+r" (sum)
+ : "g" (((const u_int32_t *)w)[0]),
+ "g" (((const u_int32_t *)w)[1])
+ );
+ w += 4;
+ mlen -= 8;
+ }
+ if (mlen == 0 && byte_swapped == 0)
+ continue; /* worth 1% maybe ?? */
+ REDUCE;
+ while ((mlen -= 2) >= 0) {
+ sum += *w++;
+ }
+ if (byte_swapped) {
+ sum <<= 8;
+ byte_swapped = 0;
+ if (mlen == -1) {
+ su.c[1] = *(char *)w;
+ sum += su.s;
+ mlen = 0;
+ } else
+ mlen = -1;
+ } else if (mlen == -1)
+ /*
+ * This mbuf has odd number of bytes.
+ * There could be a word split betwen
+ * this mbuf and the next mbuf.
+ * Save the last byte (to prepend to next mbuf).
+ */
+ su.c[0] = *(char *)w;
+ }
+
+ if (len)
+ printf("%s: out of data by %d\n", __func__, len);
+ if (mlen == -1) {
+ /* The last mbuf has odd # of bytes. Follow the
+ standard (the odd byte is shifted left by 8 bits) */
+ su.c[1] = 0;
+ sum += su.s;
+ }
+ REDUCE;
+ return (~sum & 0xffff);
+}
+#endif
diff --git a/src/VBox/Devices/Network/slirp/bsd/i386/include/in_cksum.h b/src/VBox/Devices/Network/slirp/bsd/i386/include/in_cksum.h
new file mode 100644
index 00000000..0d20d193
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bsd/i386/include/in_cksum.h
@@ -0,0 +1,145 @@
+/*-
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ *
+ * from tahoe: in_cksum.c 1.2 86/01/05
+ * from: @(#)in_cksum.c 1.3 (Berkeley) 1/19/91
+ * from: Id: in_cksum.c,v 1.8 1995/12/03 18:35:19 bde Exp
+ * $FreeBSD: src/sys/i386/include/in_cksum.h,v 1.17.10.1.6.1 2009/04/15 03:14:26 kensmith Exp $
+ */
+
+#ifndef _MACHINE_IN_CKSUM_H_
+#define _MACHINE_IN_CKSUM_H_ 1
+
+#ifndef VBOX
+#ifndef _SYS_CDEFS_H_
+#error this file needs sys/cdefs.h as a prerequisite
+#endif
+
+/*
+ * MP safe (alfred)
+ */
+
+#include <sys/cdefs.h>
+#else
+# include "slirp.h"
+#endif
+
+#define in_cksum(m, len) in_cksum_skip(m, len, 0)
+
+/*
+ * It it useful to have an Internet checksum routine which is inlineable
+ * and optimized specifically for the task of computing IP header checksums
+ * in the normal case (where there are no options and the header length is
+ * therefore always exactly five 32-bit words.
+ */
+#if defined(__GNUCLIKE_ASM) && !defined(__INTEL_COMPILER)
+static __inline u_int
+in_cksum_hdr(const struct ip *ip)
+{
+ register u_int sum = 0;
+
+ __asm __volatile (
+ "addl %1, %0\n"
+ "adcl %2, %0\n"
+ "adcl %3, %0\n"
+ "adcl %4, %0\n"
+ "adcl %5, %0\n"
+ "adcl $0, %0"
+ : "+r" (sum)
+ : "g" (((const u_int32_t *)ip)[0]),
+ "g" (((const u_int32_t *)ip)[1]),
+ "g" (((const u_int32_t *)ip)[2]),
+ "g" (((const u_int32_t *)ip)[3]),
+ "g" (((const u_int32_t *)ip)[4])
+ );
+ sum = (sum & 0xffff) + (sum >> 16);
+ if (sum > 0xffff)
+ sum -= 0xffff;
+
+ return ~sum & 0xffff;
+}
+
+static __inline void
+in_cksum_update(struct ip *ip)
+{
+ int __tmpsum;
+ __tmpsum = (int)ntohs(ip->ip_sum) + 256;
+ ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16));
+}
+
+static __inline u_short
+in_addword(u_short sum, u_short b)
+{
+ /* __volatile is necessary because the condition codes are used. */
+ __asm __volatile (
+ "addw %1, %0\n"
+ "adcw $0, %0"
+ : "+r" (sum)
+ : "r" (b)
+ );
+ return (sum);
+}
+
+static __inline u_short
+in_pseudo(u_int sum, u_int b, u_int c)
+{
+ /* __volatile is necessary because the condition codes are used. */
+ __asm __volatile (
+ "addl %1, %0\n"
+ "adcl %2, %0\n"
+ "adcl $0, %0"
+ : "+r" (sum)
+ : "g" (b),
+ "g" (c)
+ );
+ sum = (sum & 0xffff) + (sum >> 16);
+ if (sum > 0xffff)
+ sum -= 0xffff;
+ return (sum);
+}
+
+#else
+#define in_cksum_update(ip) \
+ do { \
+ int __tmpsum; \
+ __tmpsum = (int)ntohs(ip->ip_sum) + 256; \
+ ip->ip_sum = htons(__tmpsum + (__tmpsum >> 16)); \
+ } while(0)
+
+#endif
+
+#if defined(_KERNEL) || defined(VBOX)
+#if !defined(__GNUCLIKE_ASM) || defined(__INTEL_COMPILER)
+u_int in_cksum_hdr(const struct ip *ip);
+u_short in_addword(u_short sum, u_short b);
+u_short in_pseudo(u_int sum, u_int b, u_int c);
+#endif
+u_short in_cksum_skip(struct mbuf *m, int len, int skip);
+#endif /* _KERNEL */
+
+#endif /* _MACHINE_IN_CKSUM_H_ */
diff --git a/src/VBox/Devices/Network/slirp/bsd/kern/Makefile.kup b/src/VBox/Devices/Network/slirp/bsd/kern/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bsd/kern/Makefile.kup
diff --git a/src/VBox/Devices/Network/slirp/bsd/kern/kern_mbuf.c b/src/VBox/Devices/Network/slirp/bsd/kern/kern_mbuf.c
new file mode 100644
index 00000000..c908e2fd
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bsd/kern/kern_mbuf.c
@@ -0,0 +1,824 @@
+/*-
+ * Copyright (c) 2004, 2005,
+ * Bosko Milekic <bmilekic@FreeBSD.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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice unmodified, this list of conditions and the following
+ * disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHOR 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 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 VBOX
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/kern/kern_mbuf.c,v 1.32.2.5.2.1 2009/04/15 03:14:26 kensmith Exp $");
+
+#include "opt_mac.h"
+#include "opt_param.h"
+
+#include <sys/param.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/domain.h>
+#include <sys/eventhandler.h>
+#include <sys/kernel.h>
+#include <sys/protosw.h>
+#include <sys/smp.h>
+#include <sys/sysctl.h>
+
+#include <security/mac/mac_framework.h>
+
+#include <vm/vm.h>
+#include <vm/vm_page.h>
+#include <vm/uma.h>
+#include <vm/uma_int.h>
+#include <vm/uma_dbg.h>
+#else
+# include <iprt/param.h>
+# include <slirp.h>
+# define IN_BSD
+# include "ext.h"
+#endif
+
+/*
+ * In FreeBSD, Mbufs and Mbuf Clusters are allocated from UMA
+ * Zones.
+ *
+ * Mbuf Clusters (2K, contiguous) are allocated from the Cluster
+ * Zone. The Zone can be capped at kern.ipc.nmbclusters, if the
+ * administrator so desires.
+ *
+ * Mbufs are allocated from a UMA Master Zone called the Mbuf
+ * Zone.
+ *
+ * Additionally, FreeBSD provides a Packet Zone, which it
+ * configures as a Secondary Zone to the Mbuf Master Zone,
+ * thus sharing backend Slab kegs with the Mbuf Master Zone.
+ *
+ * Thus common-case allocations and locking are simplified:
+ *
+ * m_clget() m_getcl()
+ * | |
+ * | .------------>[(Packet Cache)] m_get(), m_gethdr()
+ * | | [ Packet ] |
+ * [(Cluster Cache)] [ Secondary ] [ (Mbuf Cache) ]
+ * [ Cluster Zone ] [ Zone ] [ Mbuf Master Zone ]
+ * | \________ |
+ * [ Cluster Keg ] \ /
+ * | [ Mbuf Keg ]
+ * [ Cluster Slabs ] |
+ * | [ Mbuf Slabs ]
+ * \____________(VM)_________________/
+ *
+ *
+ * Whenever an object is allocated with uma_zalloc() out of
+ * one of the Zones its _ctor_ function is executed. The same
+ * for any deallocation through uma_zfree() the _dtor_ function
+ * is executed.
+ *
+ * Caches are per-CPU and are filled from the Master Zone.
+ *
+ * Whenever an object is allocated from the underlying global
+ * memory pool it gets pre-initialized with the _zinit_ functions.
+ * When the Keg's are overfull objects get decomissioned with
+ * _zfini_ functions and free'd back to the global memory pool.
+ *
+ */
+
+#ifndef VBOX
+int nmbclusters; /* limits number of mbuf clusters */
+int nmbjumbop; /* limits number of page size jumbo clusters */
+int nmbjumbo9; /* limits number of 9k jumbo clusters */
+int nmbjumbo16; /* limits number of 16k jumbo clusters */
+struct mbstat mbstat;
+#endif
+
+/*
+ * tunable_mbinit() has to be run before init_maxsockets() thus
+ * the SYSINIT order below is SI_ORDER_MIDDLE while init_maxsockets()
+ * runs at SI_ORDER_ANY.
+ */
+static void
+tunable_mbinit(void *dummy)
+{
+#ifdef VBOX
+ PNATState pData = (PNATState)dummy;
+#endif
+ /* This has to be done before VM init. */
+ nmbclusters = 1024 + maxusers * 64;
+ nmbjumbop = nmbclusters / 2;
+ nmbjumbo9 = nmbjumbop / 2;
+ nmbjumbo16 = nmbjumbo9 / 2;
+ TUNABLE_INT_FETCH("kern.ipc.nmbclusters", &nmbclusters);
+}
+SYSINIT(tunable_mbinit, SI_SUB_TUNABLES, SI_ORDER_MIDDLE, tunable_mbinit, NULL);
+
+#ifndef VBOX
+/* XXX: These should be tuneables. Can't change UMA limits on the fly. */
+static int
+sysctl_nmbclusters(SYSCTL_HANDLER_ARGS)
+{
+ int error, newnmbclusters;
+
+ newnmbclusters = nmbclusters;
+ error = sysctl_handle_int(oidp, &newnmbclusters, 0, req);
+ if (error == 0 && req->newptr) {
+ if (newnmbclusters > nmbclusters) {
+ nmbclusters = newnmbclusters;
+ uma_zone_set_max(zone_clust, nmbclusters);
+ EVENTHANDLER_INVOKE(nmbclusters_change);
+ } else
+ error = EINVAL;
+ }
+ return (error);
+}
+SYSCTL_PROC(_kern_ipc, OID_AUTO, nmbclusters, CTLTYPE_INT|CTLFLAG_RW,
+&nmbclusters, 0, sysctl_nmbclusters, "IU",
+ "Maximum number of mbuf clusters allowed");
+
+static int
+sysctl_nmbjumbop(SYSCTL_HANDLER_ARGS)
+{
+ int error, newnmbjumbop;
+
+ newnmbjumbop = nmbjumbop;
+ error = sysctl_handle_int(oidp, &newnmbjumbop, 0, req);
+ if (error == 0 && req->newptr) {
+ if (newnmbjumbop> nmbjumbop) {
+ nmbjumbop = newnmbjumbop;
+ uma_zone_set_max(zone_jumbop, nmbjumbop);
+ } else
+ error = EINVAL;
+ }
+ return (error);
+}
+SYSCTL_PROC(_kern_ipc, OID_AUTO, nmbjumbop, CTLTYPE_INT|CTLFLAG_RW,
+&nmbjumbop, 0, sysctl_nmbjumbop, "IU",
+ "Maximum number of mbuf page size jumbo clusters allowed");
+
+
+static int
+sysctl_nmbjumbo9(SYSCTL_HANDLER_ARGS)
+{
+ int error, newnmbjumbo9;
+
+ newnmbjumbo9 = nmbjumbo9;
+ error = sysctl_handle_int(oidp, &newnmbjumbo9, 0, req);
+ if (error == 0 && req->newptr) {
+ if (newnmbjumbo9> nmbjumbo9) {
+ nmbjumbo9 = newnmbjumbo9;
+ uma_zone_set_max(zone_jumbo9, nmbjumbo9);
+ } else
+ error = EINVAL;
+ }
+ return (error);
+}
+SYSCTL_PROC(_kern_ipc, OID_AUTO, nmbjumbo9, CTLTYPE_INT|CTLFLAG_RW,
+&nmbjumbo9, 0, sysctl_nmbjumbo9, "IU",
+ "Maximum number of mbuf 9k jumbo clusters allowed");
+
+static int
+sysctl_nmbjumbo16(SYSCTL_HANDLER_ARGS)
+{
+ int error, newnmbjumbo16;
+
+ newnmbjumbo16 = nmbjumbo16;
+ error = sysctl_handle_int(oidp, &newnmbjumbo16, 0, req);
+ if (error == 0 && req->newptr) {
+ if (newnmbjumbo16> nmbjumbo16) {
+ nmbjumbo16 = newnmbjumbo16;
+ uma_zone_set_max(zone_jumbo16, nmbjumbo16);
+ } else
+ error = EINVAL;
+ }
+ return (error);
+}
+SYSCTL_PROC(_kern_ipc, OID_AUTO, nmbjumbo16, CTLTYPE_INT|CTLFLAG_RW,
+&nmbjumbo16, 0, sysctl_nmbjumbo16, "IU",
+ "Maximum number of mbuf 16k jumbo clusters allowed");
+
+
+
+SYSCTL_STRUCT(_kern_ipc, OID_AUTO, mbstat, CTLFLAG_RD, &mbstat, mbstat,
+ "Mbuf general information and statistics");
+
+/*
+ * Zones from which we allocate.
+ */
+uma_zone_t zone_mbuf;
+uma_zone_t zone_clust;
+uma_zone_t zone_pack;
+uma_zone_t zone_jumbop;
+uma_zone_t zone_jumbo9;
+uma_zone_t zone_jumbo16;
+uma_zone_t zone_ext_refcnt;
+
+/*
+ * Local prototypes.
+ */
+static int mb_ctor_mbuf(void *, int, void *, int);
+static int mb_ctor_clust(void *, int, void *, int);
+static int mb_ctor_pack(void *, int, void *, int);
+static void mb_dtor_mbuf(void *, int, void *);
+static void mb_dtor_clust(void *, int, void *);
+static void mb_dtor_pack(void *, int, void *);
+static int mb_zinit_pack(void *, int, int);
+static void mb_zfini_pack(void *, int);
+#else
+/*
+ * Local prototypes.
+ */
+static int mb_ctor_mbuf(PNATState, void *, int, void *, int);
+static int mb_ctor_clust(PNATState, void *, int, void *, int);
+static int mb_ctor_pack(PNATState, void *, int, void *, int);
+static void mb_dtor_mbuf(PNATState, void *, int, void *);
+static void mb_dtor_clust(PNATState, void *, int, void *);
+static void mb_dtor_pack(PNATState, void *, int, void *);
+static int mb_zinit_pack(PNATState, void *, int, int);
+static void mb_zfini_pack(PNATState, void *, int);
+#endif
+
+/*static void mb_reclaim(void *); - unused */
+#ifndef VBOX
+static void mbuf_init(void *);
+static void *mbuf_jumbo_alloc(uma_zone_t, int, u_int8_t *, int);
+static void mbuf_jumbo_free(void *, int, u_int8_t);
+#endif
+
+#ifndef VBOX
+static MALLOC_DEFINE(M_JUMBOFRAME, "jumboframes", "mbuf jumbo frame buffers");
+
+/* Ensure that MSIZE doesn't break dtom() - it must be a power of 2 */
+CTASSERT((((MSIZE - 1) ^ MSIZE) + 1) >> 1 == MSIZE);
+#else
+#define uma_zcreate(a0, a1, a2, a3, a4, a5, a6, a7) \
+ uma_zcreate(pData, a0, a1, a2, a3, a4, a5, a6, a7)
+#endif
+
+/*
+ * Initialize FreeBSD Network buffer allocation.
+ */
+SYSINIT(mbuf, SI_SUB_MBUF, SI_ORDER_FIRST, mbuf_init, NULL);
+#ifndef VBOX
+static void
+#else
+void
+#endif
+mbuf_init(void *dummy)
+{
+
+ /*
+ * Configure UMA zones for Mbufs, Clusters, and Packets.
+ */
+#ifndef VBOX
+ zone_mbuf = uma_zcreate(MBUF_MEM_NAME, MSIZE,
+ mb_ctor_mbuf, mb_dtor_mbuf,
+#ifdef INVARIANTS
+ trash_init, trash_fini,
+#else
+ NULL, NULL,
+#endif
+ MSIZE - 1, UMA_ZONE_MAXBUCKET);
+
+ zone_clust = uma_zcreate(MBUF_CLUSTER_MEM_NAME, MCLBYTES,
+ mb_ctor_clust, mb_dtor_clust,
+#ifdef INVARIANTS
+ trash_init, trash_fini,
+#else
+ NULL, NULL,
+#endif
+ UMA_ALIGN_PTR, UMA_ZONE_REFCNT);
+#else /*!VBOX*/
+ PNATState pData = (PNATState)dummy;
+ tunable_mbinit(pData);
+ zone_mbuf = uma_zcreate(MBUF_MEM_NAME, MSIZE,
+ mb_ctor_mbuf, mb_dtor_mbuf,
+ NULL, NULL,
+ MSIZE - 1, UMA_ZONE_MAXBUCKET);
+ if (nmbclusters > 0)
+ uma_zone_set_max(zone_mbuf, nmbclusters);
+
+ zone_clust = uma_zcreate(MBUF_CLUSTER_MEM_NAME, MCLBYTES,
+ mb_ctor_clust, mb_dtor_clust,
+ NULL, NULL,
+ UMA_ALIGN_PTR, UMA_ZONE_REFCNT);
+#endif /*VBOX*/
+ if (nmbclusters > 0)
+ uma_zone_set_max(zone_clust, nmbclusters);
+
+ zone_pack = uma_zsecond_create(MBUF_PACKET_MEM_NAME, mb_ctor_pack,
+ mb_dtor_pack, mb_zinit_pack, mb_zfini_pack, zone_mbuf);
+
+ /* Make jumbo frame zone too. Page size, 9k and 16k. */
+#ifndef VBOX
+ zone_jumbop = uma_zcreate(MBUF_JUMBOP_MEM_NAME, MJUMPAGESIZE,
+ mb_ctor_clust, mb_dtor_clust,
+#ifdef INVARIANTS
+ trash_init, trash_fini,
+#else
+ NULL, NULL,
+#endif
+ UMA_ALIGN_PTR, UMA_ZONE_REFCNT);
+ if (nmbjumbop > 0)
+ uma_zone_set_max(zone_jumbop, nmbjumbop);
+
+ zone_jumbo9 = uma_zcreate(MBUF_JUMBO9_MEM_NAME, MJUM9BYTES,
+ mb_ctor_clust, mb_dtor_clust,
+#ifdef INVARIANTS
+ trash_init, trash_fini,
+#else
+ NULL, NULL,
+#endif
+ UMA_ALIGN_PTR, UMA_ZONE_REFCNT);
+ if (nmbjumbo9 > 0)
+ uma_zone_set_max(zone_jumbo9, nmbjumbo9);
+ uma_zone_set_allocf(zone_jumbo9, mbuf_jumbo_alloc);
+ uma_zone_set_freef(zone_jumbo9, mbuf_jumbo_free);
+
+ zone_jumbo16 = uma_zcreate(MBUF_JUMBO16_MEM_NAME, MJUM16BYTES,
+ mb_ctor_clust, mb_dtor_clust,
+#ifdef INVARIANTS
+ trash_init, trash_fini,
+#else
+ NULL, NULL,
+#endif
+ UMA_ALIGN_PTR, UMA_ZONE_REFCNT);
+ if (nmbjumbo16 > 0)
+ uma_zone_set_max(zone_jumbo16, nmbjumbo16);
+#else /*!VBOX*/
+ zone_jumbop = uma_zcreate(MBUF_JUMBOP_MEM_NAME, MJUMPAGESIZE,
+ mb_ctor_clust, mb_dtor_clust,
+ NULL, NULL,
+ UMA_ALIGN_PTR, UMA_ZONE_REFCNT);
+ if (nmbjumbop > 0)
+ uma_zone_set_max(zone_jumbop, nmbjumbop);
+
+ zone_jumbo9 = uma_zcreate(MBUF_JUMBO9_MEM_NAME, MJUM9BYTES,
+ mb_ctor_clust, mb_dtor_clust,
+ NULL, NULL,
+ UMA_ALIGN_PTR, UMA_ZONE_REFCNT);
+ if (nmbjumbo9 > 0)
+ uma_zone_set_max(zone_jumbo9, nmbjumbo9);
+
+ zone_jumbo16 = uma_zcreate(MBUF_JUMBO16_MEM_NAME, MJUM16BYTES,
+ mb_ctor_clust, mb_dtor_clust,
+ NULL, NULL,
+ UMA_ALIGN_PTR, UMA_ZONE_REFCNT);
+ if (nmbjumbo16 > 0)
+ uma_zone_set_max(zone_jumbo16, nmbjumbo16);
+#endif /*VBOX*/
+
+ zone_ext_refcnt = uma_zcreate(MBUF_EXTREFCNT_MEM_NAME, sizeof(u_int),
+ NULL, NULL,
+ NULL, NULL,
+ UMA_ALIGN_PTR, UMA_ZONE_ZINIT);
+
+ /* uma_prealloc() goes here... */
+
+ /*
+ * Hook event handler for low-memory situation, used to
+ * drain protocols and push data back to the caches (UMA
+ * later pushes it back to VM).
+ */
+ EVENTHANDLER_REGISTER(vm_lowmem, mb_reclaim, NULL,
+ EVENTHANDLER_PRI_FIRST);
+
+ /*
+ * [Re]set counters and local statistics knobs.
+ * XXX Some of these should go and be replaced, but UMA stat
+ * gathering needs to be revised.
+ */
+ mbstat.m_mbufs = 0;
+ mbstat.m_mclusts = 0;
+ mbstat.m_drain = 0;
+ mbstat.m_msize = MSIZE;
+ mbstat.m_mclbytes = MCLBYTES;
+ mbstat.m_minclsize = MINCLSIZE;
+ mbstat.m_mlen = MLEN;
+ mbstat.m_mhlen = MHLEN;
+ mbstat.m_numtypes = MT_NTYPES;
+
+ mbstat.m_mcfail = mbstat.m_mpfail = 0;
+ mbstat.sf_iocnt = 0;
+ mbstat.sf_allocwait = mbstat.sf_allocfail = 0;
+}
+
+#ifndef VBOX
+/*
+ * UMA backend page allocator for the jumbo frame zones.
+ *
+ * Allocates kernel virtual memory that is backed by contiguous physical
+ * pages.
+ */
+static void *
+mbuf_jumbo_alloc(uma_zone_t zone, int bytes, u_int8_t *flags, int fWait)
+{
+
+ /* Inform UMA that this allocator uses kernel_map/object. */
+ *flags = UMA_SLAB_KERNEL;
+ return (contigmalloc(bytes, M_JUMBOFRAME, fWait, (vm_paddr_t)0,
+ ~(vm_paddr_t)0, 1, 0));
+}
+
+/*
+ * UMA backend page deallocator for the jumbo frame zones.
+ */
+static void
+mbuf_jumbo_free(void *mem, int size, u_int8_t flags)
+{
+
+ contigfree(mem, size, M_JUMBOFRAME);
+}
+#endif
+
+/*
+ * Constructor for Mbuf master zone.
+ *
+ * The 'arg' pointer points to a mb_args structure which
+ * contains call-specific information required to support the
+ * mbuf allocation API. See mbuf.h.
+ */
+static int
+#ifndef VBOX
+mb_ctor_mbuf(void *mem, int size, void *arg, int how)
+#else
+mb_ctor_mbuf(PNATState pData, void *mem, int size, void *arg, int how)
+#endif
+{
+ struct mbuf *m;
+ struct mb_args *args;
+#ifdef MAC
+ int error;
+#endif
+ int flags;
+ short type;
+#ifdef VBOX
+ NOREF(pData);
+#endif
+
+#ifdef INVARIANTS
+ trash_ctor(mem, size, arg, how);
+#elif defined(VBOX)
+ NOREF(size);
+ NOREF(how);
+#endif
+ m = (struct mbuf *)mem;
+ args = (struct mb_args *)arg;
+ flags = args->flags;
+ type = args->type;
+
+ /*
+ * The mbuf is initialized later. The caller has the
+ * responsibility to set up any MAC labels too.
+ */
+ if (type == MT_NOINIT)
+ return (0);
+
+ m->m_next = NULL;
+ m->m_nextpkt = NULL;
+ m->m_len = 0;
+ m->m_flags = flags;
+ m->m_type = type;
+ if (flags & M_PKTHDR) {
+ m->m_data = m->m_pktdat;
+ m->m_pkthdr.rcvif = NULL;
+ m->m_pkthdr.len = 0;
+ m->m_pkthdr.header = NULL;
+ m->m_pkthdr.csum_flags = 0;
+ m->m_pkthdr.csum_data = 0;
+ m->m_pkthdr.tso_segsz = 0;
+ m->m_pkthdr.ether_vtag = 0;
+ SLIST_INIT(&m->m_pkthdr.tags);
+#ifdef MAC
+ /* If the label init fails, fail the alloc */
+ error = mac_init_mbuf(m, how);
+ if (error)
+ return (error);
+#endif
+ } else
+ m->m_data = m->m_dat;
+ return (0);
+}
+
+/*
+ * The Mbuf master zone destructor.
+ */
+static void
+#ifndef VBOX
+mb_dtor_mbuf(void *mem, int size, void *arg)
+#else
+mb_dtor_mbuf(PNATState pData, void *mem, int size, void *arg)
+#endif
+{
+ struct mbuf *m;
+ uintptr_t flags;
+#ifdef VBOX
+ NOREF(pData);
+#endif
+
+ m = (struct mbuf *)mem;
+ flags = (uintptr_t)arg;
+
+ if ((flags & MB_NOTAGS) == 0 && (m->m_flags & M_PKTHDR) != 0)
+ m_tag_delete_chain(m, NULL);
+ KASSERT((m->m_flags & M_EXT) == 0, ("%s: M_EXT set", __func__));
+ KASSERT((m->m_flags & M_NOFREE) == 0, ("%s: M_NOFREE set", __func__));
+#ifdef INVARIANTS
+ trash_dtor(mem, size, arg);
+#elif defined(VBOX)
+ NOREF(size);
+ NOREF(arg);
+#endif
+}
+
+/*
+ * The Mbuf Packet zone destructor.
+ */
+static void
+#ifndef VBOX
+mb_dtor_pack(void *mem, int size, void *arg)
+#else
+mb_dtor_pack(PNATState pData, void *mem, int size, void *arg)
+#endif
+{
+ struct mbuf *m;
+
+ m = (struct mbuf *)mem;
+ if ((m->m_flags & M_PKTHDR) != 0)
+ m_tag_delete_chain(m, NULL);
+
+ /* Make sure we've got a clean cluster back. */
+ KASSERT((m->m_flags & M_EXT) == M_EXT, ("%s: M_EXT not set", __func__));
+ KASSERT(m->m_ext.ext_buf != NULL, ("%s: ext_buf == NULL", __func__));
+ KASSERT(m->m_ext.ext_free == NULL, ("%s: ext_free != NULL", __func__));
+ KASSERT(m->m_ext.ext_args == NULL, ("%s: ext_args != NULL", __func__));
+ KASSERT(m->m_ext.ext_size == MCLBYTES, ("%s: ext_size != MCLBYTES", __func__));
+ KASSERT(m->m_ext.ext_type == EXT_PACKET, ("%s: ext_type != EXT_PACKET", __func__));
+ KASSERT(*m->m_ext.ref_cnt == 1, ("%s: ref_cnt != 1", __func__));
+#ifdef INVARIANTS
+ trash_dtor(m->m_ext.ext_buf, MCLBYTES, arg);
+#elif defined(VBOX)
+ NOREF(size);
+ NOREF(arg);
+#endif
+ /*
+ * If there are processes blocked on zone_clust, waiting for pages to be freed up,
+ * cause them to be woken up by draining the packet zone. We are exposed to a race here
+ * (in the check for the UMA_ZFLAG_FULL) where we might miss the flag set, but that is
+ * deliberate. We don't want to acquire the zone lock for every mbuf free.
+ */
+ if (uma_zone_exhausted_nolock(zone_clust))
+ zone_drain(zone_pack);
+}
+
+/*
+ * The Cluster and Jumbo[PAGESIZE|9|16] zone constructor.
+ *
+ * Here the 'arg' pointer points to the Mbuf which we
+ * are configuring cluster storage for. If 'arg' is
+ * empty we allocate just the cluster without setting
+ * the mbuf to it. See mbuf.h.
+ */
+static int
+#ifndef VBOX
+mb_ctor_clust(void *mem, int size, void *arg, int how)
+#else
+mb_ctor_clust(PNATState pData, void *mem, int size, void *arg, int how)
+#endif
+{
+ struct mbuf *m;
+ u_int *refcnt;
+ int type;
+ uma_zone_t zone;
+#ifdef VBOX
+ NOREF(how);
+#endif
+
+#ifdef INVARIANTS
+ trash_ctor(mem, size, arg, how);
+#elif defined(VBOX)
+ NOREF(how);
+#endif
+ switch (size) {
+ case MCLBYTES:
+ type = EXT_CLUSTER;
+ zone = zone_clust;
+ break;
+#if MJUMPAGESIZE != MCLBYTES
+ case MJUMPAGESIZE:
+ type = EXT_JUMBOP;
+ zone = zone_jumbop;
+ break;
+#endif
+ case MJUM9BYTES:
+ type = EXT_JUMBO9;
+ zone = zone_jumbo9;
+ break;
+ case MJUM16BYTES:
+ type = EXT_JUMBO16;
+ zone = zone_jumbo16;
+ break;
+ default:
+ panic("unknown cluster size");
+ break;
+ }
+
+ m = (struct mbuf *)arg;
+ refcnt = uma_find_refcnt(zone, mem);
+ *refcnt = 1;
+ if (m != NULL) {
+ m->m_ext.ext_buf = (caddr_t)mem;
+ m->m_data = m->m_ext.ext_buf;
+ m->m_flags |= M_EXT;
+ m->m_ext.ext_free = NULL;
+ m->m_ext.ext_args = NULL;
+ m->m_ext.ext_size = size;
+ m->m_ext.ext_type = type;
+ m->m_ext.ref_cnt = refcnt;
+ }
+
+ return (0);
+}
+
+/*
+ * The Mbuf Cluster zone destructor.
+ */
+static void
+#ifndef VBOX
+mb_dtor_clust(void *mem, int size, void *arg)
+#else
+mb_dtor_clust(PNATState pData, void *mem, int size, void *arg)
+#endif
+{
+#ifdef INVARIANTS
+ uma_zone_t zone;
+
+ zone = m_getzone(size);
+ KASSERT(*(uma_find_refcnt(zone, mem)) <= 1,
+ ("%s: refcnt incorrect %u", __func__,
+ *(uma_find_refcnt(zone, mem))) );
+
+ trash_dtor(mem, size, arg);
+#elif defined(VBOX)
+ NOREF(pData);
+ NOREF(mem);
+ NOREF(size);
+ NOREF(arg);
+#endif
+}
+
+/*
+ * The Packet secondary zone's init routine, executed on the
+ * object's transition from mbuf keg slab to zone cache.
+ */
+static int
+#ifndef VBOX
+mb_zinit_pack(void *mem, int size, int how)
+#else
+mb_zinit_pack(PNATState pData, void *mem, int size, int how)
+#endif
+{
+ struct mbuf *m;
+
+ m = (struct mbuf *)mem; /* m is virgin. */
+ if (uma_zalloc_arg(zone_clust, m, how) == NULL ||
+ m->m_ext.ext_buf == NULL)
+ return (ENOMEM);
+ m->m_ext.ext_type = EXT_PACKET; /* Override. */
+#ifdef INVARIANTS
+ trash_init(m->m_ext.ext_buf, MCLBYTES, how);
+#elif defined(VBOX)
+ NOREF(size);
+#endif
+ return (0);
+}
+
+/*
+ * The Packet secondary zone's fini routine, executed on the
+ * object's transition from zone cache to keg slab.
+ */
+static void
+#ifndef VBOX
+mb_zfini_pack(void *mem, int size)
+#else
+mb_zfini_pack(PNATState pData, void *mem, int size)
+#endif
+{
+ struct mbuf *m;
+
+ m = (struct mbuf *)mem;
+#ifdef INVARIANTS
+ trash_fini(m->m_ext.ext_buf, MCLBYTES);
+#endif
+ uma_zfree_arg(zone_clust, m->m_ext.ext_buf, NULL);
+#ifdef INVARIANTS
+ trash_dtor(mem, size, NULL);
+#elif defined(VBOX)
+ NOREF(size);
+#endif
+}
+
+/*
+ * The "packet" keg constructor.
+ */
+static int
+#ifndef VBOX
+mb_ctor_pack(void *mem, int size, void *arg, int how)
+#else
+mb_ctor_pack(PNATState pData, void *mem, int size, void *arg, int how)
+#endif
+{
+ struct mbuf *m;
+ struct mb_args *args;
+#ifdef MAC
+ int error;
+#endif
+ int flags;
+ short type;
+#ifdef VBOX
+ NOREF(pData);
+ NOREF(size);
+#endif
+
+ m = (struct mbuf *)mem;
+ args = (struct mb_args *)arg;
+ flags = args->flags;
+ type = args->type;
+
+#ifdef INVARIANTS
+ trash_ctor(m->m_ext.ext_buf, MCLBYTES, arg, how);
+#elif defined(VBOX)
+ NOREF(how);
+#endif
+ m->m_next = NULL;
+ m->m_nextpkt = NULL;
+ m->m_data = m->m_ext.ext_buf;
+ m->m_len = 0;
+ m->m_flags = (flags | M_EXT);
+ m->m_type = type;
+
+ if (flags & M_PKTHDR) {
+ m->m_pkthdr.rcvif = NULL;
+ m->m_pkthdr.len = 0;
+ m->m_pkthdr.header = NULL;
+ m->m_pkthdr.csum_flags = 0;
+ m->m_pkthdr.csum_data = 0;
+ m->m_pkthdr.tso_segsz = 0;
+ m->m_pkthdr.ether_vtag = 0;
+ SLIST_INIT(&m->m_pkthdr.tags);
+#ifdef MAC
+ /* If the label init fails, fail the alloc */
+ error = mac_init_mbuf(m, how);
+ if (error)
+ return (error);
+#endif
+ }
+ /* m_ext is already initialized. */
+
+ return (0);
+}
+
+#if 0 /* unused */
+/*
+ * This is the protocol drain routine.
+ *
+ * No locks should be held when this is called. The drain routines have to
+ * presently acquire some locks which raises the possibility of lock order
+ * reversal.
+ */
+static void
+mb_reclaim(void *junk)
+{
+#ifndef VBOX
+ struct domain *dp;
+ struct protosw *pr;
+
+ WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK | WARN_PANIC, NULL,
+ "mb_reclaim()");
+
+ for (dp = domains; dp != NULL; dp = dp->dom_next)
+ for (pr = dp->dom_protosw; pr < dp->dom_protoswNPROTOSW; pr++)
+ if (pr->pr_drain != NULL)
+ (*pr->pr_drain)();
+#else
+ NOREF(junk);
+#endif
+}
+#endif /* unused */
diff --git a/src/VBox/Devices/Network/slirp/bsd/kern/subr_sbuf.c b/src/VBox/Devices/Network/slirp/bsd/kern/subr_sbuf.c
new file mode 100644
index 00000000..c2021f5b
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bsd/kern/subr_sbuf.c
@@ -0,0 +1,594 @@
+/*-
+ * Copyright (c) 2000 Poul-Henning Kamp and Dag-Erling Coïdan Smørgrav
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (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 VBOX
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/kern/subr_sbuf.c,v 1.30.8.1 2009/04/15 03:14:26 kensmith Exp $");
+
+#include <sys/param.h>
+
+#ifdef _KERNEL
+#include <sys/ctype.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/systm.h>
+#include <sys/uio.h>
+#include <machine/stdarg.h>
+#else /* _KERNEL */
+#include <ctype.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#endif /* _KERNEL */
+
+#include <sys/sbuf.h>
+
+#ifdef _KERNEL
+static MALLOC_DEFINE(M_SBUF, "sbuf", "string buffers");
+#define SBMALLOC(size) malloc(size, M_SBUF, M_WAITOK)
+#define SBFREE(buf) free(buf, M_SBUF)
+#else /* _KERNEL */
+#define KASSERT(e, m)
+#define SBMALLOC(size) malloc(size)
+#define SBFREE(buf) free(buf)
+#define min(x,y) MIN(x,y)
+#endif /* _KERNEL */
+#else /* VBOX */
+# include <iprt/param.h>
+# include <iprt/ctype.h>
+# include <slirp.h>
+# define SBMALLOC(size) RTMemAlloc((size))
+# define SBFREE(buf) RTMemFree((buf))
+#endif
+
+/*
+ * Predicates
+ */
+#define SBUF_ISDYNAMIC(s) ((s)->s_flags & SBUF_DYNAMIC)
+#define SBUF_ISDYNSTRUCT(s) ((s)->s_flags & SBUF_DYNSTRUCT)
+#define SBUF_ISFINISHED(s) ((s)->s_flags & SBUF_FINISHED)
+#define SBUF_HASOVERFLOWED(s) ((s)->s_flags & SBUF_OVERFLOWED)
+#define SBUF_HASROOM(s) ((s)->s_len < (s)->s_size - 1)
+#define SBUF_FREESPACE(s) ((s)->s_size - (s)->s_len - 1)
+#define SBUF_CANEXTEND(s) ((s)->s_flags & SBUF_AUTOEXTEND)
+
+/*
+ * Set / clear flags
+ */
+#define SBUF_SETFLAG(s, f) do { (s)->s_flags |= (f); } while (0)
+#define SBUF_CLEARFLAG(s, f) do { (s)->s_flags &= ~(f); } while (0)
+
+#define SBUF_MINEXTENDSIZE 16 /* Should be power of 2. */
+#define SBUF_MAXEXTENDSIZE PAGE_SIZE
+#define SBUF_MAXEXTENDINCR PAGE_SIZE
+
+/*
+ * Debugging support
+ */
+#if defined(_KERNEL) && defined(INVARIANTS)
+static void
+_assert_sbuf_integrity(const char *fun, struct sbuf *s)
+{
+ KASSERT(s != NULL,
+ ("%s called with a NULL sbuf pointer", fun));
+ KASSERT(s->s_buf != NULL,
+ ("%s called with uninitialized or corrupt sbuf", fun));
+ KASSERT(s->s_len < s->s_size,
+ ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
+}
+
+static void
+_assert_sbuf_state(const char *fun, struct sbuf *s, int state)
+{
+ KASSERT((s->s_flags & SBUF_FINISHED) == state,
+ ("%s called with %sfinished or corrupt sbuf", fun,
+ (state ? "un" : "")));
+}
+#define assert_sbuf_integrity(s) _assert_sbuf_integrity(__func__, (s))
+#define assert_sbuf_state(s, i) _assert_sbuf_state(__func__, (s), (i))
+#else /* _KERNEL && INVARIANTS */
+#define assert_sbuf_integrity(s) do { } while (0)
+#define assert_sbuf_state(s, i) do { } while (0)
+#endif /* _KERNEL && INVARIANTS */
+
+static int
+sbuf_extendsize(int size)
+{
+ int newsize;
+
+ newsize = SBUF_MINEXTENDSIZE;
+ while (newsize < size) {
+ if (newsize < (int)SBUF_MAXEXTENDSIZE)
+ newsize *= 2;
+ else
+ newsize += SBUF_MAXEXTENDINCR;
+ }
+
+ return (newsize);
+}
+
+
+/*
+ * Extend an sbuf.
+ */
+static int
+sbuf_extend(struct sbuf *s, int addlen)
+{
+ char *newbuf;
+ int newsize;
+
+ if (!SBUF_CANEXTEND(s))
+ return (-1);
+
+ newsize = sbuf_extendsize(s->s_size + addlen);
+ newbuf = (char *)SBMALLOC(newsize);
+ if (newbuf == NULL)
+ return (-1);
+ bcopy(s->s_buf, newbuf, s->s_size);
+ if (SBUF_ISDYNAMIC(s))
+ SBFREE(s->s_buf);
+ else
+ SBUF_SETFLAG(s, SBUF_DYNAMIC);
+ s->s_buf = newbuf;
+ s->s_size = newsize;
+ return (0);
+}
+
+/*
+ * Initialize an sbuf.
+ * If buf is non-NULL, it points to a static or already-allocated string
+ * big enough to hold at least length characters.
+ */
+struct sbuf *
+sbuf_new(struct sbuf *s, char *buf, int length, int flags)
+{
+ KASSERT(length >= 0,
+ ("attempt to create an sbuf of negative length (%d)", length));
+ KASSERT((flags & ~SBUF_USRFLAGMSK) == 0,
+ ("%s called with invalid flags", __func__));
+
+ flags &= SBUF_USRFLAGMSK;
+ if (s == NULL) {
+ s = (struct sbuf *)SBMALLOC(sizeof *s);
+ if (s == NULL)
+ return (NULL);
+ bzero(s, sizeof *s);
+ s->s_flags = flags;
+ SBUF_SETFLAG(s, SBUF_DYNSTRUCT);
+ } else {
+ bzero(s, sizeof *s);
+ s->s_flags = flags;
+ }
+ s->s_size = length;
+ if (buf) {
+ s->s_buf = buf;
+ return (s);
+ }
+ if (flags & SBUF_AUTOEXTEND)
+ s->s_size = sbuf_extendsize(s->s_size);
+ s->s_buf = (char *)SBMALLOC(s->s_size);
+ if (s->s_buf == NULL) {
+ if (SBUF_ISDYNSTRUCT(s))
+ SBFREE(s);
+ return (NULL);
+ }
+ SBUF_SETFLAG(s, SBUF_DYNAMIC);
+ return (s);
+}
+
+#ifdef _KERNEL
+/*
+ * Create an sbuf with uio data
+ */
+struct sbuf *
+sbuf_uionew(struct sbuf *s, struct uio *uio, int *error)
+{
+ KASSERT(uio != NULL,
+ ("%s called with NULL uio pointer", __func__));
+ KASSERT(error != NULL,
+ ("%s called with NULL error pointer", __func__));
+
+ s = sbuf_new(s, NULL, uio->uio_resid + 1, 0);
+ if (s == NULL) {
+ *error = ENOMEM;
+ return (NULL);
+ }
+ *error = uiomove(s->s_buf, uio->uio_resid, uio);
+ if (*error != 0) {
+ sbuf_delete(s);
+ return (NULL);
+ }
+ s->s_len = s->s_size - 1;
+ *error = 0;
+ return (s);
+}
+#endif
+
+/*
+ * Clear an sbuf and reset its position.
+ */
+void
+sbuf_clear(struct sbuf *s)
+{
+ assert_sbuf_integrity(s);
+ /* don't care if it's finished or not */
+
+ SBUF_CLEARFLAG(s, SBUF_FINISHED);
+ SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
+ s->s_len = 0;
+}
+
+/*
+ * Set the sbuf's end position to an arbitrary value.
+ * Effectively truncates the sbuf at the new position.
+ */
+int
+sbuf_setpos(struct sbuf *s, int pos)
+{
+ assert_sbuf_integrity(s);
+ assert_sbuf_state(s, 0);
+
+ KASSERT(pos >= 0,
+ ("attempt to seek to a negative position (%d)", pos));
+ KASSERT(pos < s->s_size,
+ ("attempt to seek past end of sbuf (%d >= %d)", pos, s->s_size));
+
+ if (pos < 0 || pos > s->s_len)
+ return (-1);
+ s->s_len = pos;
+ return (0);
+}
+
+/*
+ * Append a byte string to an sbuf.
+ */
+int
+sbuf_bcat(struct sbuf *s, const void *buf, size_t len)
+{
+ const char *str = buf;
+
+ assert_sbuf_integrity(s);
+ assert_sbuf_state(s, 0);
+
+ if (SBUF_HASOVERFLOWED(s))
+ return (-1);
+
+ for (; len; len--) {
+ if (!SBUF_HASROOM(s) && sbuf_extend(s, len) < 0)
+ break;
+ s->s_buf[s->s_len++] = *str++;
+ }
+ if (len) {
+ SBUF_SETFLAG(s, SBUF_OVERFLOWED);
+ return (-1);
+ }
+ return (0);
+}
+
+#ifdef _KERNEL
+/*
+ * Copy a byte string from userland into an sbuf.
+ */
+int
+sbuf_bcopyin(struct sbuf *s, const void *uaddr, size_t len)
+{
+ assert_sbuf_integrity(s);
+ assert_sbuf_state(s, 0);
+
+ if (SBUF_HASOVERFLOWED(s))
+ return (-1);
+
+ if (len == 0)
+ return (0);
+ if (len > SBUF_FREESPACE(s)) {
+ sbuf_extend(s, len - SBUF_FREESPACE(s));
+ len = min(len, SBUF_FREESPACE(s));
+ }
+ if (copyin(uaddr, s->s_buf + s->s_len, len) != 0)
+ return (-1);
+ s->s_len += len;
+
+ return (0);
+}
+#endif
+
+/*
+ * Copy a byte string into an sbuf.
+ */
+int
+sbuf_bcpy(struct sbuf *s, const void *buf, size_t len)
+{
+ assert_sbuf_integrity(s);
+ assert_sbuf_state(s, 0);
+
+ sbuf_clear(s);
+ return (sbuf_bcat(s, buf, len));
+}
+
+/*
+ * Append a string to an sbuf.
+ */
+int
+sbuf_cat(struct sbuf *s, const char *str)
+{
+ assert_sbuf_integrity(s);
+ assert_sbuf_state(s, 0);
+
+ if (SBUF_HASOVERFLOWED(s))
+ return (-1);
+
+ while (*str) {
+ if (!SBUF_HASROOM(s) && sbuf_extend(s, strlen(str)) < 0)
+ break;
+ s->s_buf[s->s_len++] = *str++;
+ }
+ if (*str) {
+ SBUF_SETFLAG(s, SBUF_OVERFLOWED);
+ return (-1);
+ }
+ return (0);
+}
+
+#ifdef _KERNEL
+/*
+ * Append a string from userland to an sbuf.
+ */
+int
+sbuf_copyin(struct sbuf *s, const void *uaddr, size_t len)
+{
+ size_t done;
+
+ assert_sbuf_integrity(s);
+ assert_sbuf_state(s, 0);
+
+ if (SBUF_HASOVERFLOWED(s))
+ return (-1);
+
+ if (len == 0)
+ len = SBUF_FREESPACE(s); /* XXX return 0? */
+ if (len > SBUF_FREESPACE(s)) {
+ sbuf_extend(s, len);
+ len = min(len, SBUF_FREESPACE(s));
+ }
+ switch (copyinstr(uaddr, s->s_buf + s->s_len, len + 1, &done)) {
+ case ENAMETOOLONG:
+ SBUF_SETFLAG(s, SBUF_OVERFLOWED);
+ RT_FALL_THRU();
+ case 0:
+ s->s_len += done - 1;
+ break;
+ default:
+ return (-1); /* XXX */
+ }
+
+ return (done);
+}
+#endif
+
+/*
+ * Copy a string into an sbuf.
+ */
+int
+sbuf_cpy(struct sbuf *s, const char *str)
+{
+ assert_sbuf_integrity(s);
+ assert_sbuf_state(s, 0);
+
+ sbuf_clear(s);
+ return (sbuf_cat(s, str));
+}
+
+/*
+ * Format the given argument list and append the resulting string to an sbuf.
+ */
+int
+sbuf_vprintf(struct sbuf *s, const char *fmt, va_list ap)
+{
+ va_list ap_copy;
+ int len;
+
+ assert_sbuf_integrity(s);
+ assert_sbuf_state(s, 0);
+
+ KASSERT(fmt != NULL,
+ ("%s called with a NULL format string", __func__));
+
+ if (SBUF_HASOVERFLOWED(s))
+ return (-1);
+
+ do {
+ va_copy(ap_copy, ap);
+#ifndef VBOX
+ len = vsnprintf(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1,
+ fmt, ap_copy);
+#else
+ len = RTStrPrintfV(&s->s_buf[s->s_len], SBUF_FREESPACE(s) + 1,
+ fmt, ap_copy);
+#endif
+ va_end(ap_copy);
+ } while (len > SBUF_FREESPACE(s) &&
+ sbuf_extend(s, len - SBUF_FREESPACE(s)) == 0);
+
+ /*
+ * s->s_len is the length of the string, without the terminating nul.
+ * When updating s->s_len, we must subtract 1 from the length that
+ * we passed into vsnprintf() because that length includes the
+ * terminating nul.
+ *
+ * vsnprintf() returns the amount that would have been copied,
+ * given sufficient space, hence the min() calculation below.
+ */
+ s->s_len += min(len, SBUF_FREESPACE(s));
+ if (!SBUF_HASROOM(s) && !SBUF_CANEXTEND(s))
+ SBUF_SETFLAG(s, SBUF_OVERFLOWED);
+
+ KASSERT(s->s_len < s->s_size,
+ ("wrote past end of sbuf (%d >= %d)", s->s_len, s->s_size));
+
+ if (SBUF_HASOVERFLOWED(s))
+ return (-1);
+ return (0);
+}
+
+/*
+ * Format the given arguments and append the resulting string to an sbuf.
+ */
+int
+sbuf_printf(struct sbuf *s, const char *fmt, ...)
+{
+ va_list ap;
+ int result;
+
+ va_start(ap, fmt);
+ result = sbuf_vprintf(s, fmt, ap);
+ va_end(ap);
+ return(result);
+}
+
+/*
+ * Append a character to an sbuf.
+ */
+int
+sbuf_putc(struct sbuf *s, int c)
+{
+ assert_sbuf_integrity(s);
+ assert_sbuf_state(s, 0);
+
+ if (SBUF_HASOVERFLOWED(s))
+ return (-1);
+
+ if (!SBUF_HASROOM(s) && sbuf_extend(s, 1) < 0) {
+ SBUF_SETFLAG(s, SBUF_OVERFLOWED);
+ return (-1);
+ }
+ if (c != '\0')
+ s->s_buf[s->s_len++] = c;
+ return (0);
+}
+
+/*
+ * Trim whitespace characters from end of an sbuf.
+ */
+int
+sbuf_trim(struct sbuf *s)
+{
+ assert_sbuf_integrity(s);
+ assert_sbuf_state(s, 0);
+
+ if (SBUF_HASOVERFLOWED(s))
+ return (-1);
+
+#ifndef VBOX
+ while (s->s_len && isspace(s->s_buf[s->s_len-1]))
+ --s->s_len;
+#else
+ while (s->s_len && RT_C_IS_SPACE(s->s_buf[s->s_len-1]))
+ --s->s_len;
+#endif
+
+ return (0);
+}
+
+/*
+ * Check if an sbuf overflowed
+ */
+int
+sbuf_overflowed(struct sbuf *s)
+{
+ return SBUF_HASOVERFLOWED(s);
+}
+
+/*
+ * Finish off an sbuf.
+ */
+void
+sbuf_finish(struct sbuf *s)
+{
+ assert_sbuf_integrity(s);
+ assert_sbuf_state(s, 0);
+
+ s->s_buf[s->s_len] = '\0';
+ SBUF_CLEARFLAG(s, SBUF_OVERFLOWED);
+ SBUF_SETFLAG(s, SBUF_FINISHED);
+}
+
+/*
+ * Return a pointer to the sbuf data.
+ */
+char *
+sbuf_data(struct sbuf *s)
+{
+ assert_sbuf_integrity(s);
+ assert_sbuf_state(s, SBUF_FINISHED);
+
+ return s->s_buf;
+}
+
+/*
+ * Return the length of the sbuf data.
+ */
+int
+sbuf_len(struct sbuf *s)
+{
+ assert_sbuf_integrity(s);
+ /* don't care if it's finished or not */
+
+ if (SBUF_HASOVERFLOWED(s))
+ return (-1);
+ return s->s_len;
+}
+
+/*
+ * Clear an sbuf, free its buffer if necessary.
+ */
+void
+sbuf_delete(struct sbuf *s)
+{
+ int isdyn;
+
+ assert_sbuf_integrity(s);
+ /* don't care if it's finished or not */
+
+ if (SBUF_ISDYNAMIC(s))
+ SBFREE(s->s_buf);
+ isdyn = SBUF_ISDYNSTRUCT(s);
+ bzero(s, sizeof *s);
+ if (isdyn)
+ SBFREE(s);
+}
+
+/*
+ * Check if an sbuf has been finished.
+ */
+int
+sbuf_done(struct sbuf *s)
+{
+
+ return(SBUF_ISFINISHED(s));
+}
diff --git a/src/VBox/Devices/Network/slirp/bsd/kern/uipc_mbuf.c b/src/VBox/Devices/Network/slirp/bsd/kern/uipc_mbuf.c
new file mode 100644
index 00000000..0af49faa
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bsd/kern/uipc_mbuf.c
@@ -0,0 +1,2238 @@
+/*-
+ * Copyright (c) 1982, 1986, 1988, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)uipc_mbuf.c 8.2 (Berkeley) 1/4/94
+ */
+
+#ifndef VBOX
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/kern/uipc_mbuf.c,v 1.174.2.3.2.1 2009/04/15 03:14:26 kensmith Exp $");
+
+#include "opt_mac.h"
+#include "opt_param.h"
+#include "opt_mbuf_stress_test.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/sysctl.h>
+#include <sys/domain.h>
+#include <sys/protosw.h>
+#include <sys/uio.h>
+
+#include <security/mac/mac_framework.h>
+
+int max_linkhdr;
+#ifndef VBOX
+int max_protohdr;
+#endif
+int max_hdr;
+int max_datalen;
+#ifdef MBUF_STRESS_TEST
+int m_defragpackets;
+int m_defragbytes;
+int m_defraguseless;
+int m_defragfailure;
+int m_defragrandomfailures;
+#endif
+
+/*
+ * sysctl(8) exported objects
+ */
+SYSCTL_INT(_kern_ipc, KIPC_MAX_LINKHDR, max_linkhdr, CTLFLAG_RD,
+ &max_linkhdr, 0, "Size of largest link layer header");
+SYSCTL_INT(_kern_ipc, KIPC_MAX_PROTOHDR, max_protohdr, CTLFLAG_RD,
+ &max_protohdr, 0, "Size of largest protocol layer header");
+SYSCTL_INT(_kern_ipc, KIPC_MAX_HDR, max_hdr, CTLFLAG_RD,
+ &max_hdr, 0, "Size of largest link plus protocol header");
+SYSCTL_INT(_kern_ipc, KIPC_MAX_DATALEN, max_datalen, CTLFLAG_RD,
+ &max_datalen, 0, "Minimum space left in mbuf after max_hdr");
+#ifdef MBUF_STRESS_TEST
+SYSCTL_INT(_kern_ipc, OID_AUTO, m_defragpackets, CTLFLAG_RD,
+ &m_defragpackets, 0, "");
+SYSCTL_INT(_kern_ipc, OID_AUTO, m_defragbytes, CTLFLAG_RD,
+ &m_defragbytes, 0, "");
+SYSCTL_INT(_kern_ipc, OID_AUTO, m_defraguseless, CTLFLAG_RD,
+ &m_defraguseless, 0, "");
+SYSCTL_INT(_kern_ipc, OID_AUTO, m_defragfailure, CTLFLAG_RD,
+ &m_defragfailure, 0, "");
+SYSCTL_INT(_kern_ipc, OID_AUTO, m_defragrandomfailures, CTLFLAG_RW,
+ &m_defragrandomfailures, 0, "");
+#endif
+#else /* VBOX */
+# include <iprt/asm.h>
+# include "slirp.h"
+# define atomic_fetchadd_int(var, val) (ASMAtomicAddU32((var), (val)))
+# define atomic_add_int(var, val) (ASMAtomicAddU32((var), (val)))
+#endif /* VBOX */
+
+/*
+ * Allocate a given length worth of mbufs and/or clusters (whatever fits
+ * best) and return a pointer to the top of the allocated chain. If an
+ * existing mbuf chain is provided, then we will append the new chain
+ * to the existing one but still return the top of the newly allocated
+ * chain.
+ */
+struct mbuf *
+#ifndef VBOX
+m_getm2(struct mbuf *m, int len, int how, short type, int flags)
+#else
+m_getm2(PNATState pData, struct mbuf *m, int len, int how, short type, int flags)
+#endif
+{
+ struct mbuf *mb, *nm = NULL, *mtail = NULL;
+
+ KASSERT(len >= 0, ("%s: len is < 0", __func__));
+
+ /* Validate flags. */
+ flags &= (M_PKTHDR | M_EOR);
+
+ /* Packet header mbuf must be first in chain. */
+ if ((flags & M_PKTHDR) && m != NULL)
+ flags &= ~M_PKTHDR;
+
+ /* Loop and append maximum sized mbufs to the chain tail. */
+ while (len > 0) {
+#ifndef VBOX
+ if (len > MCLBYTES)
+ mb = m_getjcl(how, type, (flags & M_PKTHDR),
+ MJUMPAGESIZE);
+ else if (len >= MINCLSIZE)
+ mb = m_getcl(how, type, (flags & M_PKTHDR));
+ else if (flags & M_PKTHDR)
+ mb = m_gethdr(how, type);
+ else
+ mb = m_get(how, type);
+
+ /* Fail the whole operation if one mbuf can't be allocated. */
+ if (mb == NULL) {
+ if (nm != NULL)
+ m_freem(nm);
+ return (NULL);
+ }
+#else
+ if (len > MCLBYTES)
+ mb = m_getjcl(pData, how, type, (flags & M_PKTHDR),
+ MJUMPAGESIZE);
+ else if (len >= MINCLSIZE)
+ mb = m_getcl(pData, how, type, (flags & M_PKTHDR));
+ else if (flags & M_PKTHDR)
+ mb = m_gethdr(pData, how, type);
+ else
+ mb = m_get(pData, how, type);
+ /* Fail the whole operation if one mbuf can't be allocated. */
+ if (mb == NULL) {
+ if (nm != NULL)
+ m_freem(pData, nm);
+ return (NULL);
+ }
+#endif
+
+ /* Book keeping. */
+ len -= (mb->m_flags & M_EXT) ? mb->m_ext.ext_size :
+ ((mb->m_flags & M_PKTHDR) ? MHLEN : MLEN);
+ if (mtail != NULL)
+ mtail->m_next = mb;
+ else
+ nm = mb;
+ mtail = mb;
+ flags &= ~M_PKTHDR; /* Only valid on the first mbuf. */
+ }
+ if (flags & M_EOR)
+ mtail->m_flags |= M_EOR; /* Only valid on the last mbuf. */
+
+ /* If mbuf was supplied, append new chain to the end of it. */
+ if (m != NULL) {
+ for (mtail = m; mtail->m_next != NULL; mtail = mtail->m_next)
+ ;
+ mtail->m_next = nm;
+ mtail->m_flags &= ~M_EOR;
+ } else
+ m = nm;
+
+ return (m);
+}
+
+/*
+ * Free an entire chain of mbufs and associated external buffers, if
+ * applicable.
+ */
+void
+#ifndef VBOX
+m_freem(struct mbuf *mb)
+#else
+m_freem(PNATState pData, struct mbuf *mb)
+#endif
+{
+
+ while (mb != NULL)
+ mb = m_free(pData, mb);
+}
+
+/*-
+ * Configure a provided mbuf to refer to the provided external storage
+ * buffer and setup a reference count for said buffer. If the setting
+ * up of the reference count fails, the M_EXT bit will not be set. If
+ * successfull, the M_EXT bit is set in the mbuf's flags.
+ *
+ * Arguments:
+ * mb The existing mbuf to which to attach the provided buffer.
+ * buf The address of the provided external storage buffer.
+ * size The size of the provided buffer.
+ * freef A pointer to a routine that is responsible for freeing the
+ * provided external storage buffer.
+ * args A pointer to an argument structure (of any type) to be passed
+ * to the provided freef routine (may be NULL).
+ * flags Any other flags to be passed to the provided mbuf.
+ * type The type that the external storage buffer should be
+ * labeled with.
+ *
+ * Returns:
+ * Nothing.
+ */
+void
+#ifndef VBOX
+m_extadd(struct mbuf *mb, caddr_t buf, u_int size,
+ void (*freef)(void *, void *), void *args, int flags, int type)
+#else
+m_extadd(PNATState pData, struct mbuf *mb, caddr_t buf, u_int size,
+ void (*freef)(void *, void *), void *args, int flags, int type)
+#endif
+{
+ KASSERT(type != EXT_CLUSTER, ("%s: EXT_CLUSTER not allowed", __func__));
+
+ if (type != EXT_EXTREF)
+ mb->m_ext.ref_cnt = (u_int *)uma_zalloc(zone_ext_refcnt, M_NOWAIT);
+ if (mb->m_ext.ref_cnt != NULL) {
+ *(mb->m_ext.ref_cnt) = 1;
+ mb->m_flags |= (M_EXT | flags);
+ mb->m_ext.ext_buf = buf;
+ mb->m_data = mb->m_ext.ext_buf;
+ mb->m_ext.ext_size = size;
+ mb->m_ext.ext_free = freef;
+ mb->m_ext.ext_args = args;
+ mb->m_ext.ext_type = type;
+ }
+}
+
+/*
+ * Non-directly-exported function to clean up after mbufs with M_EXT
+ * storage attached to them if the reference count hits 1.
+ */
+void
+#ifndef VBOX
+mb_free_ext(struct mbuf *m)
+#else
+mb_free_ext(PNATState pData, struct mbuf *m)
+#endif
+{
+ int skipmbuf;
+
+ KASSERT((m->m_flags & M_EXT) == M_EXT, ("%s: M_EXT not set", __func__));
+ KASSERT(m->m_ext.ref_cnt != NULL, ("%s: ref_cnt not set", __func__));
+
+
+ /*
+ * check if the header is embedded in the cluster
+ */
+ skipmbuf = (m->m_flags & M_NOFREE);
+
+ /* Free attached storage if this mbuf is the only reference to it. */
+ if (*(m->m_ext.ref_cnt) == 1 ||
+ atomic_fetchadd_int(m->m_ext.ref_cnt, (uint32_t)-1) == 1) {
+ switch (m->m_ext.ext_type) {
+ case EXT_PACKET: /* The packet zone is special. */
+ if (*(m->m_ext.ref_cnt) == 0)
+ *(m->m_ext.ref_cnt) = 1;
+ uma_zfree(zone_pack, m);
+ return; /* Job done. */
+ case EXT_CLUSTER:
+ uma_zfree(zone_clust, m->m_ext.ext_buf);
+ break;
+ case EXT_JUMBOP:
+ uma_zfree(zone_jumbop, m->m_ext.ext_buf);
+ break;
+ case EXT_JUMBO9:
+ uma_zfree(zone_jumbo9, m->m_ext.ext_buf);
+ break;
+ case EXT_JUMBO16:
+ uma_zfree(zone_jumbo16, m->m_ext.ext_buf);
+ break;
+ case EXT_SFBUF:
+ case EXT_NET_DRV:
+ case EXT_MOD_TYPE:
+ case EXT_DISPOSABLE:
+#ifndef VBOX
+ /* This code is dead in VBOX port of BSD mbufs (probably will be used for EXT_SBUFS some day)
+ * @todo once bsd sbufs will be on trunk think about this code.
+ */
+ *(m->m_ext.ref_cnt) = 0;
+ uma_zfree(zone_ext_refcnt, __DEVOLATILE(u_int *,
+ m->m_ext.ref_cnt));
+#else
+ AssertMsgFailed(("unimplemented"));
+#endif
+ RT_FALL_THRU();
+ case EXT_EXTREF:
+ KASSERT(m->m_ext.ext_free != NULL,
+ ("%s: ext_free not set", __func__));
+ (*(m->m_ext.ext_free))(m->m_ext.ext_buf,
+ m->m_ext.ext_args);
+ break;
+ default:
+ KASSERT(m->m_ext.ext_type == 0,
+ ("%s: unknown ext_type", __func__));
+ }
+ }
+ if (skipmbuf)
+ return;
+
+ /*
+ * Free this mbuf back to the mbuf zone with all m_ext
+ * information purged.
+ */
+ m->m_ext.ext_buf = NULL;
+ m->m_ext.ext_free = NULL;
+ m->m_ext.ext_args = NULL;
+ m->m_ext.ref_cnt = NULL;
+ m->m_ext.ext_size = 0;
+ m->m_ext.ext_type = 0;
+ m->m_flags &= ~M_EXT;
+ uma_zfree(zone_mbuf, m);
+}
+
+/*
+ * Attach the cluster from *m to *n, set up m_ext in *n
+ * and bump the refcount of the cluster.
+ */
+static void
+mb_dupcl(struct mbuf *n, struct mbuf *m)
+{
+ KASSERT((m->m_flags & M_EXT) == M_EXT, ("%s: M_EXT not set", __func__));
+ KASSERT(m->m_ext.ref_cnt != NULL, ("%s: ref_cnt not set", __func__));
+ KASSERT((n->m_flags & M_EXT) == 0, ("%s: M_EXT set", __func__));
+
+ if (*(m->m_ext.ref_cnt) == 1)
+ *(m->m_ext.ref_cnt) += 1;
+ else
+ atomic_add_int(m->m_ext.ref_cnt, 1);
+ n->m_ext.ext_buf = m->m_ext.ext_buf;
+ n->m_ext.ext_free = m->m_ext.ext_free;
+ n->m_ext.ext_args = m->m_ext.ext_args;
+ n->m_ext.ext_size = m->m_ext.ext_size;
+ n->m_ext.ref_cnt = m->m_ext.ref_cnt;
+ n->m_ext.ext_type = m->m_ext.ext_type;
+ n->m_flags |= M_EXT;
+}
+
+/*
+ * Clean up mbuf (chain) from any tags and packet headers.
+ * If "all" is set then the first mbuf in the chain will be
+ * cleaned too.
+ */
+void
+m_demote(struct mbuf *m0, int all)
+{
+ struct mbuf *m;
+
+ for (m = all ? m0 : m0->m_next; m != NULL; m = m->m_next) {
+ if (m->m_flags & M_PKTHDR) {
+ m_tag_delete_chain(m, NULL);
+ m->m_flags &= ~M_PKTHDR;
+ bzero(&m->m_pkthdr, sizeof(struct pkthdr));
+ }
+ if (m->m_type == MT_HEADER)
+ m->m_type = MT_DATA;
+ if (m != m0 && m->m_nextpkt != NULL)
+ m->m_nextpkt = NULL;
+ m->m_flags = m->m_flags & (M_EXT|M_EOR|M_RDONLY|M_FREELIST);
+ }
+}
+
+/*
+ * Sanity checks on mbuf (chain) for use in KASSERT() and general
+ * debugging.
+ * Returns 0 or panics when bad and 1 on all tests passed.
+ * Sanitize, 0 to run M_SANITY_ACTION, 1 to garble things so they
+ * blow up later.
+ */
+int
+#ifndef VBOX
+m_sanity(struct mbuf *m0, int sanitize)
+#else
+m_sanity(PNATState pData, struct mbuf *m0, int sanitize)
+#endif
+{
+ struct mbuf *m;
+ caddr_t a, b;
+ int pktlen = 0;
+
+#ifdef INVARIANTS
+#define M_SANITY_ACTION(s) panic("mbuf %p: " s, m)
+#else
+#define M_SANITY_ACTION(s) printf("mbuf %p: " s, m)
+#endif
+
+ for (m = m0; m != NULL; m = m->m_next) {
+ /*
+ * Basic pointer checks. If any of these fails then some
+ * unrelated kernel memory before or after us is trashed.
+ * No way to recover from that.
+ */
+ a = ((m->m_flags & M_EXT) ? m->m_ext.ext_buf :
+ ((m->m_flags & M_PKTHDR) ? (caddr_t)(&m->m_pktdat) :
+ (caddr_t)(&m->m_dat)) );
+ b = (caddr_t)(a + (m->m_flags & M_EXT ? m->m_ext.ext_size :
+ ((m->m_flags & M_PKTHDR) ? MHLEN : MLEN)));
+ if ((caddr_t)m->m_data < a)
+ M_SANITY_ACTION("m_data outside mbuf data range left");
+ if ((caddr_t)m->m_data > b)
+ M_SANITY_ACTION("m_data outside mbuf data range right");
+ if ((caddr_t)m->m_data + m->m_len > b)
+ M_SANITY_ACTION("m_data + m_len exeeds mbuf space");
+ if ((m->m_flags & M_PKTHDR) && m->m_pkthdr.header) {
+ if ((caddr_t)m->m_pkthdr.header < a ||
+ (caddr_t)m->m_pkthdr.header > b)
+ M_SANITY_ACTION("m_pkthdr.header outside mbuf data range");
+ }
+
+ /* m->m_nextpkt may only be set on first mbuf in chain. */
+ if (m != m0 && m->m_nextpkt != NULL) {
+ if (sanitize) {
+#ifndef VBOX
+ m_freem(m->m_nextpkt);
+#else
+ m_freem(pData, m->m_nextpkt);
+#endif
+ m->m_nextpkt = (struct mbuf *)(uintptr_t)UINT32_C(0xDEADC0DE);
+ } else
+ M_SANITY_ACTION("m->m_nextpkt on in-chain mbuf");
+ }
+
+ /* packet length (not mbuf length!) calculation */
+ if (m0->m_flags & M_PKTHDR)
+ pktlen += m->m_len;
+
+ /* m_tags may only be attached to first mbuf in chain. */
+ if (m != m0 && m->m_flags & M_PKTHDR &&
+ !SLIST_EMPTY(&m->m_pkthdr.tags)) {
+ if (sanitize) {
+ m_tag_delete_chain(m, NULL);
+ /* put in 0xDEADC0DE perhaps? */
+ } else
+ M_SANITY_ACTION("m_tags on in-chain mbuf");
+ }
+
+ /* M_PKTHDR may only be set on first mbuf in chain */
+ if (m != m0 && m->m_flags & M_PKTHDR) {
+ if (sanitize) {
+ bzero(&m->m_pkthdr, sizeof(m->m_pkthdr));
+ m->m_flags &= ~M_PKTHDR;
+ /* put in 0xDEADCODE and leave hdr flag in */
+ } else
+ M_SANITY_ACTION("M_PKTHDR on in-chain mbuf");
+ }
+ }
+ m = m0;
+ if (pktlen && pktlen != m->m_pkthdr.len) {
+ if (sanitize)
+ m->m_pkthdr.len = 0;
+ else
+ M_SANITY_ACTION("m_pkthdr.len != mbuf chain length");
+ }
+ return 1;
+
+#undef M_SANITY_ACTION
+}
+
+
+/*
+ * "Move" mbuf pkthdr from "from" to "to".
+ * "from" must have M_PKTHDR set, and "to" must be empty.
+ */
+void
+m_move_pkthdr(struct mbuf *to, struct mbuf *from)
+{
+
+#if 0
+ /* see below for why these are not enabled */
+ M_ASSERTPKTHDR(to);
+ /* Note: with MAC, this may not be a good assertion. */
+ KASSERT(SLIST_EMPTY(&to->m_pkthdr.tags),
+ ("m_move_pkthdr: to has tags"));
+#endif
+#ifdef MAC
+ /*
+ * XXXMAC: It could be this should also occur for non-MAC?
+ */
+ if (to->m_flags & M_PKTHDR)
+ m_tag_delete_chain(to, NULL);
+#endif
+ to->m_flags = (from->m_flags & M_COPYFLAGS) | (to->m_flags & M_EXT);
+ if ((to->m_flags & M_EXT) == 0)
+ to->m_data = to->m_pktdat;
+ to->m_pkthdr = from->m_pkthdr; /* especially tags */
+ SLIST_INIT(&from->m_pkthdr.tags); /* purge tags from src */
+ from->m_flags &= ~M_PKTHDR;
+}
+
+/*
+ * Duplicate "from"'s mbuf pkthdr in "to".
+ * "from" must have M_PKTHDR set, and "to" must be empty.
+ * In particular, this does a deep copy of the packet tags.
+ */
+int
+m_dup_pkthdr(struct mbuf *to, struct mbuf *from, int how)
+{
+
+#if 0
+ /*
+ * The mbuf allocator only initializes the pkthdr
+ * when the mbuf is allocated with MGETHDR. Many users
+ * (e.g. m_copy*, m_prepend) use MGET and then
+ * smash the pkthdr as needed causing these
+ * assertions to trip. For now just disable them.
+ */
+ M_ASSERTPKTHDR(to);
+ /* Note: with MAC, this may not be a good assertion. */
+ KASSERT(SLIST_EMPTY(&to->m_pkthdr.tags), ("m_dup_pkthdr: to has tags"));
+#endif
+ MBUF_CHECKSLEEP(how);
+#ifdef MAC
+ if (to->m_flags & M_PKTHDR)
+ m_tag_delete_chain(to, NULL);
+#endif
+ to->m_flags = (from->m_flags & M_COPYFLAGS) | (to->m_flags & M_EXT);
+ if ((to->m_flags & M_EXT) == 0)
+ to->m_data = to->m_pktdat;
+ to->m_pkthdr = from->m_pkthdr;
+ SLIST_INIT(&to->m_pkthdr.tags);
+ return (m_tag_copy_chain(to, from, MBTOM(how)));
+}
+
+/*
+ * Lesser-used path for M_PREPEND:
+ * allocate new mbuf to prepend to chain,
+ * copy junk along.
+ */
+struct mbuf *
+#ifndef VBOX
+m_prepend(struct mbuf *m, int len, int how)
+#else
+m_prepend(PNATState pData, struct mbuf *m, int len, int how)
+#endif
+{
+ struct mbuf *mn;
+
+ if (m->m_flags & M_PKTHDR)
+ MGETHDR(mn, how, m->m_type);
+ else
+ MGET(mn, how, m->m_type);
+ if (mn == NULL) {
+#ifndef VBOX
+ m_freem(m);
+#else
+ m_freem(pData, m);
+#endif
+ return (NULL);
+ }
+ if (m->m_flags & M_PKTHDR)
+ M_MOVE_PKTHDR(mn, m);
+ mn->m_next = m;
+ m = mn;
+ if(m->m_flags & M_PKTHDR) {
+ if (len < MHLEN)
+ MH_ALIGN(m, len);
+ } else {
+ if (len < MLEN)
+ M_ALIGN(m, len);
+ }
+ m->m_len = len;
+ return (m);
+}
+
+/*
+ * Make a copy of an mbuf chain starting "off0" bytes from the beginning,
+ * continuing for "len" bytes. If len is M_COPYALL, copy to end of mbuf.
+ * The wait parameter is a choice of M_TRYWAIT/M_DONTWAIT from caller.
+ * Note that the copy is read-only, because clusters are not copied,
+ * only their reference counts are incremented.
+ */
+struct mbuf *
+#ifndef VBOX
+m_copym(struct mbuf *m, int off0, int len, int fWait)
+#else
+m_copym(PNATState pData, struct mbuf *m, int off0, int len, int fWait)
+#endif
+{
+ struct mbuf *n, **np;
+ int off = off0;
+ struct mbuf *top;
+ int copyhdr = 0;
+
+ KASSERT(off >= 0, ("m_copym, negative off %d", off));
+ KASSERT(len >= 0, ("m_copym, negative len %d", len));
+ MBUF_CHECKSLEEP(fWait);
+ if (off == 0 && m->m_flags & M_PKTHDR)
+ copyhdr = 1;
+ while (off > 0) {
+ KASSERT(m != NULL, ("m_copym, offset > size of mbuf chain"));
+ if (off < m->m_len)
+ break;
+ off -= m->m_len;
+ m = m->m_next;
+ }
+ np = &top;
+ top = 0;
+ while (len > 0) {
+ if (m == NULL) {
+ KASSERT(len == M_COPYALL,
+ ("m_copym, length > size of mbuf chain"));
+ break;
+ }
+ if (copyhdr)
+ MGETHDR(n, fWait, m->m_type);
+ else
+ MGET(n, fWait, m->m_type);
+ *np = n;
+ if (n == NULL)
+ goto nospace;
+ if (copyhdr) {
+ if (!m_dup_pkthdr(n, m, fWait))
+ goto nospace;
+ if (len == M_COPYALL)
+ n->m_pkthdr.len -= off0;
+ else
+ n->m_pkthdr.len = len;
+ copyhdr = 0;
+ }
+ n->m_len = min(len, m->m_len - off);
+ if (m->m_flags & M_EXT) {
+ n->m_data = m->m_data + off;
+ mb_dupcl(n, m);
+ } else
+ bcopy(mtod(m, caddr_t)+off, mtod(n, caddr_t),
+ (u_int)n->m_len);
+ if (len != M_COPYALL)
+ len -= n->m_len;
+ off = 0;
+ m = m->m_next;
+ np = &n->m_next;
+ }
+ if (top == NULL)
+ mbstat.m_mcfail++; /* XXX: No consistency. */
+
+ return (top);
+nospace:
+#ifndef VBOX
+ m_freem(top);
+#else
+ m_freem(pData, top);
+#endif
+ mbstat.m_mcfail++; /* XXX: No consistency. */
+ return (NULL);
+}
+
+/*
+ * Returns mbuf chain with new head for the prepending case.
+ * Copies from mbuf (chain) n from off for len to mbuf (chain) m
+ * either prepending or appending the data.
+ * The resulting mbuf (chain) m is fully writeable.
+ * m is destination (is made writeable)
+ * n is source, off is offset in source, len is len from offset
+ * dir, 0 append, 1 prepend
+ * how, wait or nowait
+ */
+
+static int
+m_bcopyxxx(void *s, void *t, u_int len)
+{
+ bcopy(s, t, (size_t)len);
+ return 0;
+}
+
+struct mbuf *
+#ifndef VBOX
+m_copymdata(struct mbuf *m, struct mbuf *n, int off, int len,
+ int prep, int how)
+#else
+m_copymdata(PNATState pData, struct mbuf *m, struct mbuf *n, int off, int len,
+ int prep, int how)
+#endif
+{
+ struct mbuf *mm, *x, *z, *prev = NULL;
+ caddr_t p;
+ int i, nlen = 0;
+ caddr_t buf[MLEN];
+
+ KASSERT(m != NULL && n != NULL, ("m_copymdata, no target or source"));
+ KASSERT(off >= 0, ("m_copymdata, negative off %d", off));
+ KASSERT(len >= 0, ("m_copymdata, negative len %d", len));
+ KASSERT(prep == 0 || prep == 1, ("m_copymdata, unknown direction %d", prep));
+
+ mm = m;
+ if (!prep) {
+ while(mm->m_next) {
+ prev = mm;
+ mm = mm->m_next;
+ }
+ }
+ for (z = n; z != NULL; z = z->m_next)
+ nlen += z->m_len;
+ if (len == M_COPYALL)
+ len = nlen - off;
+ if (off + len > nlen || len < 1)
+ return NULL;
+
+ if (!M_WRITABLE(mm)) {
+ /* XXX: Use proper m_xxx function instead. */
+#ifndef VBOX
+ x = m_getcl(how, MT_DATA, mm->m_flags);
+#else
+ x = m_getcl(pData, how, MT_DATA, mm->m_flags);
+#endif
+ if (x == NULL)
+ return NULL;
+ bcopy(mm->m_ext.ext_buf, x->m_ext.ext_buf, x->m_ext.ext_size);
+ p = x->m_ext.ext_buf + (mm->m_data - mm->m_ext.ext_buf);
+ x->m_data = p;
+ mm->m_next = NULL;
+ if (mm != m)
+ prev->m_next = x;
+#ifndef VBOX
+ m_free(mm);
+#else
+ m_free(pData, mm);
+#endif
+ mm = x;
+ }
+
+ /*
+ * Append/prepend the data. Allocating mbufs as necessary.
+ */
+ /* Shortcut if enough free space in first/last mbuf. */
+ if (!prep && M_TRAILINGSPACE(mm) >= len) {
+ m_apply(n, off, len, m_bcopyxxx, mtod(mm, caddr_t) +
+ mm->m_len);
+ mm->m_len += len;
+ mm->m_pkthdr.len += len;
+ return m;
+ }
+ if (prep && M_LEADINGSPACE(mm) >= len) {
+ mm->m_data = mtod(mm, caddr_t) - len;
+ m_apply(n, off, len, m_bcopyxxx, mtod(mm, caddr_t));
+ mm->m_len += len;
+ mm->m_pkthdr.len += len;
+ return mm;
+ }
+
+ /* Expand first/last mbuf to cluster if possible. */
+ if (!prep && !(mm->m_flags & M_EXT) && len > M_TRAILINGSPACE(mm)) {
+ bcopy(mm->m_data, &buf, mm->m_len);
+#ifndef VBOX
+ m_clget(mm, how);
+#else
+ m_clget(pData, mm, how);
+#endif
+ if (!(mm->m_flags & M_EXT))
+ return NULL;
+ bcopy(&buf, mm->m_ext.ext_buf, mm->m_len);
+ mm->m_data = mm->m_ext.ext_buf;
+ mm->m_pkthdr.header = NULL;
+ }
+ if (prep && !(mm->m_flags & M_EXT) && len > M_LEADINGSPACE(mm)) {
+ bcopy(mm->m_data, &buf, mm->m_len);
+#ifndef VBOX
+ m_clget(mm, how);
+#else
+ m_clget(pData, mm, how);
+#endif
+ if (!(mm->m_flags & M_EXT))
+ return NULL;
+ bcopy(&buf, (caddr_t *)mm->m_ext.ext_buf +
+ mm->m_ext.ext_size - mm->m_len, mm->m_len);
+ mm->m_data = (caddr_t)mm->m_ext.ext_buf +
+ mm->m_ext.ext_size - mm->m_len;
+ mm->m_pkthdr.header = NULL;
+ }
+
+ /* Append/prepend as many mbuf (clusters) as necessary to fit len. */
+ if (!prep && len > M_TRAILINGSPACE(mm)) {
+ if (!m_getm(mm, len - M_TRAILINGSPACE(mm), how, MT_DATA))
+ return NULL;
+ }
+ if (prep && len > M_LEADINGSPACE(mm)) {
+ if (!(z = m_getm(NULL, len - M_LEADINGSPACE(mm), how, MT_DATA)))
+ return NULL;
+ i = 0;
+ for (x = z; x != NULL; x = x->m_next) {
+ i += x->m_flags & M_EXT ? x->m_ext.ext_size :
+ (x->m_flags & M_PKTHDR ? MHLEN : MLEN);
+ if (!x->m_next)
+ break;
+ }
+ z->m_data += i - len;
+ m_move_pkthdr(mm, z);
+ x->m_next = mm;
+ mm = z;
+ }
+
+ /* Seek to start position in source mbuf. Optimization for long chains. */
+ while (off > 0) {
+ if (off < n->m_len)
+ break;
+ off -= n->m_len;
+ n = n->m_next;
+ }
+
+ /* Copy data into target mbuf. */
+ z = mm;
+ while (len > 0) {
+ KASSERT(z != NULL, ("m_copymdata, falling off target edge"));
+ i = M_TRAILINGSPACE(z);
+ m_apply(n, off, i, m_bcopyxxx, mtod(z, caddr_t) + z->m_len);
+ z->m_len += i;
+ /* fixup pkthdr.len if necessary */
+ if ((prep ? mm : m)->m_flags & M_PKTHDR)
+ (prep ? mm : m)->m_pkthdr.len += i;
+ off += i;
+ len -= i;
+ z = z->m_next;
+ }
+ return (prep ? mm : m);
+}
+
+/*
+ * Copy an entire packet, including header (which must be present).
+ * An optimization of the common case `m_copym(m, 0, M_COPYALL, how)'.
+ * Note that the copy is read-only, because clusters are not copied,
+ * only their reference counts are incremented.
+ * Preserve alignment of the first mbuf so if the creator has left
+ * some room at the beginning (e.g. for inserting protocol headers)
+ * the copies still have the room available.
+ */
+struct mbuf *
+#ifndef VBOX
+m_copypacket(struct mbuf *m, int how)
+#else
+m_copypacket(PNATState pData, struct mbuf *m, int how)
+#endif
+{
+ struct mbuf *top, *n, *o;
+
+ MBUF_CHECKSLEEP(how);
+ MGET(n, how, m->m_type);
+ top = n;
+ if (n == NULL)
+ goto nospace;
+
+ if (!m_dup_pkthdr(n, m, how))
+ goto nospace;
+ n->m_len = m->m_len;
+ if (m->m_flags & M_EXT) {
+ n->m_data = m->m_data;
+ mb_dupcl(n, m);
+ } else {
+ n->m_data = n->m_pktdat + (m->m_data - m->m_pktdat );
+ bcopy(mtod(m, char *), mtod(n, char *), n->m_len);
+ }
+
+ m = m->m_next;
+ while (m) {
+ MGET(o, how, m->m_type);
+ if (o == NULL)
+ goto nospace;
+
+ n->m_next = o;
+ n = n->m_next;
+
+ n->m_len = m->m_len;
+ if (m->m_flags & M_EXT) {
+ n->m_data = m->m_data;
+ mb_dupcl(n, m);
+ } else {
+ bcopy(mtod(m, char *), mtod(n, char *), n->m_len);
+ }
+
+ m = m->m_next;
+ }
+ return top;
+nospace:
+#ifndef VBOX
+ m_freem(top);
+#else
+ m_freem(pData, top);
+#endif
+ mbstat.m_mcfail++; /* XXX: No consistency. */
+ return (NULL);
+}
+
+/*
+ * Copy data from an mbuf chain starting "off" bytes from the beginning,
+ * continuing for "len" bytes, into the indicated buffer.
+ */
+void
+m_copydata(const struct mbuf *m, int off, int len, caddr_t cp)
+{
+ u_int count;
+
+ KASSERT(off >= 0, ("m_copydata, negative off %d", off));
+ KASSERT(len >= 0, ("m_copydata, negative len %d", len));
+ while (off > 0) {
+ KASSERT(m != NULL, ("m_copydata, offset > size of mbuf chain"));
+ if (off < m->m_len)
+ break;
+ off -= m->m_len;
+ m = m->m_next;
+ }
+ while (len > 0) {
+ KASSERT(m != NULL, ("m_copydata, length > size of mbuf chain"));
+ count = min(m->m_len - off, len);
+ bcopy(mtod(m, caddr_t) + off, cp, count);
+ len -= count;
+ cp += count;
+ off = 0;
+ m = m->m_next;
+ }
+}
+
+/*
+ * Copy a packet header mbuf chain into a completely new chain, including
+ * copying any mbuf clusters. Use this instead of m_copypacket() when
+ * you need a writable copy of an mbuf chain.
+ */
+struct mbuf *
+#ifndef VBOX
+m_dup(struct mbuf *m, int how)
+#else
+m_dup(PNATState pData, struct mbuf *m, int how)
+#endif
+{
+ struct mbuf **p, *top = NULL;
+ int remain, moff, nsize;
+
+ MBUF_CHECKSLEEP(how);
+ /* Sanity check */
+ if (m == NULL)
+ return (NULL);
+ M_ASSERTPKTHDR(m);
+
+ /* While there's more data, get a new mbuf, tack it on, and fill it */
+ remain = m->m_pkthdr.len;
+ moff = 0;
+ p = &top;
+ while (remain > 0 || top == NULL) { /* allow m->m_pkthdr.len == 0 */
+ struct mbuf *n;
+
+ /* Get the next new mbuf */
+ if (remain >= MINCLSIZE) {
+#ifndef VBOX
+ n = m_getcl(how, m->m_type, 0);
+#else
+ n = m_getcl(pData, how, m->m_type, 0);
+#endif
+ nsize = MCLBYTES;
+ } else {
+#ifndef VBOX
+ n = m_get(how, m->m_type);
+#else
+ n = m_get(pData, how, m->m_type);
+#endif
+ nsize = MLEN;
+ }
+ if (n == NULL)
+ goto nospace;
+
+ if (top == NULL) { /* First one, must be PKTHDR */
+ if (!m_dup_pkthdr(n, m, how)) {
+#ifndef VBOX
+ m_free(n);
+#else
+ m_free(pData, n);
+#endif
+ goto nospace;
+ }
+ if ((n->m_flags & M_EXT) == 0)
+ nsize = MHLEN;
+ }
+ n->m_len = 0;
+
+ /* Link it into the new chain */
+ *p = n;
+ p = &n->m_next;
+
+ /* Copy data from original mbuf(s) into new mbuf */
+ while (n->m_len < nsize && m != NULL) {
+ int chunk = min(nsize - n->m_len, m->m_len - moff);
+
+ bcopy(m->m_data + moff, n->m_data + n->m_len, chunk);
+ moff += chunk;
+ n->m_len += chunk;
+ remain -= chunk;
+ if (moff == m->m_len) {
+ m = m->m_next;
+ moff = 0;
+ }
+ }
+
+ /* Check correct total mbuf length */
+ KASSERT((remain > 0 && m != NULL) || (remain == 0 && m == NULL),
+ ("%s: bogus m_pkthdr.len", __func__));
+ }
+ return (top);
+
+nospace:
+#ifndef VBOX
+ m_freem(top);
+#else
+ m_freem(pData, top);
+#endif
+ mbstat.m_mcfail++; /* XXX: No consistency. */
+ return (NULL);
+}
+
+/*
+ * Concatenate mbuf chain n to m.
+ * Both chains must be of the same type (e.g. MT_DATA).
+ * Any m_pkthdr is not updated.
+ */
+void
+#ifndef VBOX
+m_cat(struct mbuf *m, struct mbuf *n)
+#else
+m_cat(PNATState pData, struct mbuf *m, struct mbuf *n)
+#endif
+{
+ while (m->m_next)
+ m = m->m_next;
+ while (n) {
+ if (m->m_flags & M_EXT ||
+ m->m_data + m->m_len + n->m_len >= &m->m_dat[MLEN]) {
+ /* just join the two chains */
+ m->m_next = n;
+ return;
+ }
+ /* splat the data from one into the other */
+ bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len,
+ (u_int)n->m_len);
+ m->m_len += n->m_len;
+#ifndef VBOX
+ n = m_free(n);
+#else
+ n = m_free(pData, n);
+#endif
+ }
+}
+
+void
+#ifndef VBOX
+m_adj(struct mbuf *mp, int req_len)
+#else
+m_adj(PNATState pData, struct mbuf *mp, int req_len)
+#endif
+{
+ int len = req_len;
+ struct mbuf *m;
+ int count;
+
+ if ((m = mp) == NULL)
+ return;
+ if (len >= 0) {
+ /*
+ * Trim from head.
+ */
+ while (m != NULL && len > 0) {
+ if (m->m_len <= len) {
+ len -= m->m_len;
+ m->m_len = 0;
+ m = m->m_next;
+ } else {
+ m->m_len -= len;
+ m->m_data += len;
+ len = 0;
+ }
+ }
+ m = mp;
+ if (mp->m_flags & M_PKTHDR)
+ m->m_pkthdr.len -= (req_len - len);
+ } else {
+ /*
+ * Trim from tail. Scan the mbuf chain,
+ * calculating its length and finding the last mbuf.
+ * If the adjustment only affects this mbuf, then just
+ * adjust and return. Otherwise, rescan and truncate
+ * after the remaining size.
+ */
+ len = -len;
+ count = 0;
+ for (;;) {
+ count += m->m_len;
+ if (m->m_next == (struct mbuf *)0)
+ break;
+ m = m->m_next;
+ }
+ if (m->m_len > len || (m->m_len == len && m == mp)) {
+ m->m_len -= len;
+ if (mp->m_flags & M_PKTHDR)
+ mp->m_pkthdr.len -= len;
+ return;
+ }
+ count -= len;
+ if (count < 0)
+ count = 0;
+ /*
+ * Correct length for chain is "count".
+ * Find the mbuf with last data, adjust its length,
+ * and toss data from remaining mbufs on chain.
+ */
+ m = mp;
+ if (m->m_flags & M_PKTHDR)
+ m->m_pkthdr.len = count;
+ for (; m; m = m->m_next) {
+ if (m->m_len >= count) {
+ m->m_len = count;
+ if (m->m_next != NULL) {
+#ifndef VBOX
+ m_freem(m->m_next);
+#else
+ m_freem(pData, m->m_next);
+#endif
+ m->m_next = NULL;
+ }
+ break;
+ }
+ count -= m->m_len;
+ }
+ }
+}
+
+/*
+ * Rearange an mbuf chain so that len bytes are contiguous
+ * and in the data area of an mbuf (so that mtod and dtom
+ * will work for a structure of size len). Returns the resulting
+ * mbuf chain on success, frees it and returns null on failure.
+ * If there is room, it will add up to max_protohdr-len extra bytes to the
+ * contiguous region in an attempt to avoid being called next time.
+ */
+struct mbuf *
+#ifndef VBOX
+m_pullup(struct mbuf *n, int len)
+#else
+m_pullup(PNATState pData, struct mbuf *n, int len)
+#endif
+{
+ struct mbuf *m;
+ int count;
+ int space;
+
+ /*
+ * If first mbuf has no cluster, and has room for len bytes
+ * without shifting current data, pullup into it,
+ * otherwise allocate a new mbuf to prepend to the chain.
+ */
+ if ((n->m_flags & M_EXT) == 0 &&
+ n->m_data + len < &n->m_dat[MLEN] && n->m_next) {
+ if (n->m_len >= len)
+ return (n);
+ m = n;
+ n = n->m_next;
+ len -= m->m_len;
+ } else {
+ if (len > MHLEN)
+ goto bad;
+ MGET(m, M_DONTWAIT, n->m_type);
+ if (m == NULL)
+ goto bad;
+ m->m_len = 0;
+ if (n->m_flags & M_PKTHDR)
+ M_MOVE_PKTHDR(m, n);
+ }
+ space = &m->m_dat[MLEN] - (m->m_data + m->m_len);
+ do {
+ count = min(min(max(len, max_protohdr), space), n->m_len);
+ bcopy(mtod(n, caddr_t), mtod(m, caddr_t) + m->m_len,
+ (u_int)count);
+ len -= count;
+ m->m_len += count;
+ n->m_len -= count;
+ space -= count;
+ if (n->m_len)
+ n->m_data += count;
+ else
+#ifndef VBOX
+ n = m_free(n);
+#else
+ n = m_free(pData, n);
+#endif
+ } while (len > 0 && n);
+ if (len > 0) {
+#ifndef VBOX
+ (void) m_free(m);
+#else
+ (void) m_free(pData, m);
+#endif
+ goto bad;
+ }
+ m->m_next = n;
+ return (m);
+bad:
+#ifndef VBOX
+ m_freem(n);
+#else
+ m_freem(pData, n);
+#endif
+ mbstat.m_mpfail++; /* XXX: No consistency. */
+ return (NULL);
+}
+
+/*
+ * Like m_pullup(), except a new mbuf is always allocated, and we allow
+ * the amount of empty space before the data in the new mbuf to be specified
+ * (in the event that the caller expects to prepend later).
+ */
+int MSFail;
+
+struct mbuf *
+#ifndef VBOX
+m_copyup(struct mbuf *n, int len, int dstoff)
+#else
+m_copyup(PNATState pData, struct mbuf *n, int len, int dstoff)
+#endif
+{
+ struct mbuf *m;
+ int count, space;
+
+ if (len > (int)(MHLEN - dstoff))
+ goto bad;
+ MGET(m, M_DONTWAIT, n->m_type);
+ if (m == NULL)
+ goto bad;
+ m->m_len = 0;
+ if (n->m_flags & M_PKTHDR)
+ M_MOVE_PKTHDR(m, n);
+ m->m_data += dstoff;
+ space = &m->m_dat[MLEN] - (m->m_data + m->m_len);
+ do {
+ count = min(min(max(len, max_protohdr), space), n->m_len);
+ memcpy(mtod(m, caddr_t) + m->m_len, mtod(n, caddr_t),
+ (unsigned)count);
+ len -= count;
+ m->m_len += count;
+ n->m_len -= count;
+ space -= count;
+ if (n->m_len)
+ n->m_data += count;
+ else
+#ifndef VBOX
+ n = m_free(n);
+#else
+ n = m_free(pData, n);
+#endif
+ } while (len > 0 && n);
+ if (len > 0) {
+#ifndef VBOX
+ (void) m_free(m);
+#else
+ (void) m_free(pData, m);
+#endif
+ goto bad;
+ }
+ m->m_next = n;
+ return (m);
+ bad:
+#ifndef VBOX
+ m_freem(n);
+#else
+ m_freem(pData, n);
+#endif
+ MSFail++;
+ return (NULL);
+}
+
+/*
+ * Partition an mbuf chain in two pieces, returning the tail --
+ * all but the first len0 bytes. In case of failure, it returns NULL and
+ * attempts to restore the chain to its original state.
+ *
+ * Note that the resulting mbufs might be read-only, because the new
+ * mbuf can end up sharing an mbuf cluster with the original mbuf if
+ * the "breaking point" happens to lie within a cluster mbuf. Use the
+ * M_WRITABLE() macro to check for this case.
+ */
+struct mbuf *
+#ifndef VBOX
+m_split(struct mbuf *m0, int len0, int fWait)
+#else
+m_split(PNATState pData, struct mbuf *m0, int len0, int fWait)
+#endif
+{
+ struct mbuf *m, *n;
+ u_int len = len0, remain;
+
+ MBUF_CHECKSLEEP(fWait);
+ for (m = m0; m && len > m->m_len; m = m->m_next)
+ len -= m->m_len;
+ if (m == NULL)
+ return (NULL);
+ remain = m->m_len - len;
+ if (m0->m_flags & M_PKTHDR) {
+ MGETHDR(n, fWait, m0->m_type);
+ if (n == NULL)
+ return (NULL);
+ n->m_pkthdr.rcvif = m0->m_pkthdr.rcvif;
+ n->m_pkthdr.len = m0->m_pkthdr.len - len0;
+ m0->m_pkthdr.len = len0;
+ if (m->m_flags & M_EXT)
+ goto extpacket;
+ if (remain > MHLEN) {
+ /* m can't be the lead packet */
+ MH_ALIGN(n, 0);
+#ifndef VBOX
+ n->m_next = m_split(m, len, fWait);
+#else
+ n->m_next = m_split(pData, m, len, fWait);
+#endif
+ if (n->m_next == NULL) {
+#ifndef VBOX
+ (void) m_free(n);
+#else
+ (void) m_free(pData, n);
+#endif
+ return (NULL);
+ } else {
+ n->m_len = 0;
+ return (n);
+ }
+ } else
+ MH_ALIGN(n, remain);
+ } else if (remain == 0) {
+ n = m->m_next;
+ m->m_next = NULL;
+ return (n);
+ } else {
+ MGET(n, fWait, m->m_type);
+ if (n == NULL)
+ return (NULL);
+ M_ALIGN(n, remain);
+ }
+extpacket:
+ if (m->m_flags & M_EXT) {
+ n->m_data = m->m_data + len;
+ mb_dupcl(n, m);
+ } else {
+ bcopy(mtod(m, caddr_t) + len, mtod(n, caddr_t), remain);
+ }
+ n->m_len = remain;
+ m->m_len = len;
+ n->m_next = m->m_next;
+ m->m_next = NULL;
+ return (n);
+}
+/*
+ * Routine to copy from device local memory into mbufs.
+ * Note that `off' argument is offset into first mbuf of target chain from
+ * which to begin copying the data to.
+ */
+#ifndef VBOX
+struct mbuf *
+m_devget(char *buf, int totlen, int off, struct ifnet *ifp,
+ void (*copy)(char *from, caddr_t to, u_int len))
+{
+ struct mbuf *m;
+ struct mbuf *top = NULL, **mp = &top;
+ int len;
+
+ if (off < 0 || off > MHLEN)
+ return (NULL);
+
+ while (totlen > 0) {
+ if (top == NULL) { /* First one, must be PKTHDR */
+ if (totlen + off >= MINCLSIZE) {
+ m = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+ len = MCLBYTES;
+ } else {
+ m = m_gethdr(M_DONTWAIT, MT_DATA);
+ len = MHLEN;
+
+ /* Place initial small packet/header at end of mbuf */
+ if (m && totlen + off + max_linkhdr <= MLEN) {
+ m->m_data += max_linkhdr;
+ len -= max_linkhdr;
+ }
+ }
+ if (m == NULL)
+ return NULL;
+ m->m_pkthdr.rcvif = ifp;
+ m->m_pkthdr.len = totlen;
+ } else {
+ if (totlen + off >= MINCLSIZE) {
+ m = m_getcl(M_DONTWAIT, MT_DATA, 0);
+ len = MCLBYTES;
+ } else {
+ m = m_get(M_DONTWAIT, MT_DATA);
+ len = MLEN;
+ }
+ if (m == NULL) {
+ m_freem(top);
+ return NULL;
+ }
+ }
+ if (off) {
+ m->m_data += off;
+ len -= off;
+ off = 0;
+ }
+ m->m_len = len = min(totlen, len);
+ if (copy)
+ copy(buf, mtod(m, caddr_t), (u_int)len);
+ else
+ bcopy(buf, mtod(m, caddr_t), (u_int)len);
+ buf += len;
+ *mp = m;
+ mp = &m->m_next;
+ totlen -= len;
+ }
+ return (top);
+}
+#endif
+
+/*
+ * Copy data from a buffer back into the indicated mbuf chain,
+ * starting "off" bytes from the beginning, extending the mbuf
+ * chain if necessary.
+ */
+void
+#ifndef VBOX
+m_copyback(struct mbuf *m0, int off, int len, c_caddr_t cp)
+#else
+m_copyback(PNATState pData, struct mbuf *m0, int off, int len, c_caddr_t cp)
+#endif
+{
+ int mlen;
+ struct mbuf *m = m0, *n;
+ int totlen = 0;
+
+ if (m0 == NULL)
+ return;
+ while (off > (mlen = m->m_len)) {
+ off -= mlen;
+ totlen += mlen;
+ if (m->m_next == NULL) {
+#ifndef VBOX
+ n = m_get(M_DONTWAIT, m->m_type);
+#else
+ n = m_get(pData, M_DONTWAIT, m->m_type);
+#endif
+ if (n == NULL)
+ goto out;
+ bzero(mtod(n, caddr_t), MLEN);
+ n->m_len = min(MLEN, len + off);
+ m->m_next = n;
+ }
+ m = m->m_next;
+ }
+ while (len > 0) {
+ if (m->m_next == NULL && (len > m->m_len - off)) {
+ m->m_len += min(len - (m->m_len - off),
+ M_TRAILINGSPACE(m));
+ }
+ mlen = min (m->m_len - off, len);
+ bcopy(cp, off + mtod(m, caddr_t), (u_int)mlen);
+ cp += mlen;
+ len -= mlen;
+ mlen += off;
+ off = 0;
+ totlen += mlen;
+ if (len == 0)
+ break;
+ if (m->m_next == NULL) {
+#ifndef VBOX
+ n = m_get(M_DONTWAIT, m->m_type);
+#else
+ n = m_get(pData, M_DONTWAIT, m->m_type);
+#endif
+ if (n == NULL)
+ break;
+ n->m_len = min(MLEN, len);
+ m->m_next = n;
+ }
+ m = m->m_next;
+ }
+out: if (((m = m0)->m_flags & M_PKTHDR) && (m->m_pkthdr.len < totlen))
+ m->m_pkthdr.len = totlen;
+}
+
+/*
+ * Append the specified data to the indicated mbuf chain,
+ * Extend the mbuf chain if the new data does not fit in
+ * existing space.
+ *
+ * Return 1 if able to complete the job; otherwise 0.
+ */
+int
+#ifndef VBOX
+m_append(struct mbuf *m0, int len, c_caddr_t cp)
+#else
+m_append(PNATState pData, struct mbuf *m0, int len, c_caddr_t cp)
+#endif
+{
+ struct mbuf *m, *n;
+ int remainder, space;
+
+ for (m = m0; m->m_next != NULL; m = m->m_next)
+ ;
+ remainder = len;
+ space = M_TRAILINGSPACE(m);
+ if (space > 0) {
+ /*
+ * Copy into available space.
+ */
+ if (space > remainder)
+ space = remainder;
+ bcopy(cp, mtod(m, caddr_t) + m->m_len, space);
+ m->m_len += space;
+ cp += space, remainder -= space;
+ }
+ while (remainder > 0) {
+ /*
+ * Allocate a new mbuf; could check space
+ * and allocate a cluster instead.
+ */
+#ifndef VBOX
+ n = m_get(M_DONTWAIT, m->m_type);
+#else
+ n = m_get(pData, M_DONTWAIT, m->m_type);
+#endif
+ if (n == NULL)
+ break;
+ n->m_len = min(MLEN, remainder);
+ bcopy(cp, mtod(n, caddr_t), n->m_len);
+ cp += n->m_len, remainder -= n->m_len;
+ m->m_next = n;
+ m = n;
+ }
+ if (m0->m_flags & M_PKTHDR)
+ m0->m_pkthdr.len += len - remainder;
+ return (remainder == 0);
+}
+
+/*
+ * Apply function f to the data in an mbuf chain starting "off" bytes from
+ * the beginning, continuing for "len" bytes.
+ */
+int
+m_apply(struct mbuf *m, int off, int len,
+ int (*f)(void *, void *, u_int), void *arg)
+{
+ u_int count;
+ int rval;
+
+ KASSERT(off >= 0, ("m_apply, negative off %d", off));
+ KASSERT(len >= 0, ("m_apply, negative len %d", len));
+ while (off > 0) {
+ KASSERT(m != NULL, ("m_apply, offset > size of mbuf chain"));
+ if (off < m->m_len)
+ break;
+ off -= m->m_len;
+ m = m->m_next;
+ }
+ while (len > 0) {
+ KASSERT(m != NULL, ("m_apply, offset > size of mbuf chain"));
+ count = min(m->m_len - off, len);
+ rval = (*f)(arg, mtod(m, caddr_t) + off, count);
+ if (rval)
+ return (rval);
+ len -= count;
+ off = 0;
+ m = m->m_next;
+ }
+ return (0);
+}
+
+/*
+ * Return a pointer to mbuf/offset of location in mbuf chain.
+ */
+struct mbuf *
+m_getptr(struct mbuf *m, int loc, int *off)
+{
+
+ while (loc >= 0) {
+ /* Normal end of search. */
+ if (m->m_len > loc) {
+ *off = loc;
+ return (m);
+ } else {
+ loc -= m->m_len;
+ if (m->m_next == NULL) {
+ if (loc == 0) {
+ /* Point at the end of valid data. */
+ *off = m->m_len;
+ return (m);
+ }
+ return (NULL);
+ }
+ m = m->m_next;
+ }
+ }
+ return (NULL);
+}
+
+void
+m_print(const struct mbuf *m, int maxlen)
+{
+ int len;
+ int pdata;
+ const struct mbuf *m2;
+
+ if (m->m_flags & M_PKTHDR)
+ len = m->m_pkthdr.len;
+ else
+ len = -1;
+ m2 = m;
+ while (m2 != NULL && (len == -1 || len)) {
+ pdata = m2->m_len;
+ if (maxlen != -1 && pdata > maxlen)
+ pdata = maxlen;
+ printf("mbuf: %p len: %d, next: %p, %b%s", m2, m2->m_len,
+ m2->m_next, m2->m_flags, "\20\20freelist\17skipfw"
+ "\11proto5\10proto4\7proto3\6proto2\5proto1\4rdonly"
+ "\3eor\2pkthdr\1ext", pdata ? "" : "\n");
+ if (pdata)
+ printf(", %*D\n", pdata, (u_char *)m2->m_data, "-");
+ if (len != -1)
+ len -= m2->m_len;
+ m2 = m2->m_next;
+ }
+ if (len > 0)
+ printf("%d bytes unaccounted for.\n", len);
+ return;
+}
+
+u_int
+m_fixhdr(struct mbuf *m0)
+{
+ u_int len;
+
+ len = m_length(m0, NULL);
+ m0->m_pkthdr.len = len;
+ return (len);
+}
+
+u_int
+m_length(struct mbuf *m0, struct mbuf **last)
+{
+ struct mbuf *m;
+ u_int len;
+
+ len = 0;
+ for (m = m0; m != NULL; m = m->m_next) {
+ len += m->m_len;
+ if (m->m_next == NULL)
+ break;
+ }
+ if (last != NULL)
+ *last = m;
+ return (len);
+}
+
+/*
+ * Defragment a mbuf chain, returning the shortest possible
+ * chain of mbufs and clusters. If allocation fails and
+ * this cannot be completed, NULL will be returned, but
+ * the passed in chain will be unchanged. Upon success,
+ * the original chain will be freed, and the new chain
+ * will be returned.
+ *
+ * If a non-packet header is passed in, the original
+ * mbuf (chain?) will be returned unharmed.
+ */
+struct mbuf *
+#ifndef VBOX
+m_defrag(struct mbuf *m0, int how)
+#else
+m_defrag(PNATState pData, struct mbuf *m0, int how)
+#endif
+{
+ struct mbuf *m_new = NULL, *m_final = NULL;
+ int progress = 0, length;
+
+ MBUF_CHECKSLEEP(how);
+ if (!(m0->m_flags & M_PKTHDR))
+ return (m0);
+
+ m_fixhdr(m0); /* Needed sanity check */
+
+#ifdef MBUF_STRESS_TEST
+ if (m_defragrandomfailures) {
+ int temp = arc4random() & 0xff;
+ if (temp == 0xba)
+ goto nospace;
+ }
+#endif
+
+ if (m0->m_pkthdr.len > MHLEN)
+#ifndef VBOX
+ m_final = m_getcl(how, MT_DATA, M_PKTHDR);
+#else
+ m_final = m_getcl(pData, how, MT_DATA, M_PKTHDR);
+#endif
+ else
+#ifndef VBOX
+ m_final = m_gethdr(how, MT_DATA);
+#else
+ m_final = m_gethdr(pData, how, MT_DATA);
+#endif
+
+ if (m_final == NULL)
+ goto nospace;
+
+ if (m_dup_pkthdr(m_final, m0, how) == 0)
+ goto nospace;
+
+ m_new = m_final;
+
+ while (progress < m0->m_pkthdr.len) {
+ length = m0->m_pkthdr.len - progress;
+ if (length > MCLBYTES)
+ length = MCLBYTES;
+
+ if (m_new == NULL) {
+ if (length > MLEN)
+#ifndef VBOX
+ m_new = m_getcl(how, MT_DATA, 0);
+#else
+ m_new = m_getcl(pData, how, MT_DATA, 0);
+#endif
+ else
+#ifndef VBOX
+ m_new = m_get(how, MT_DATA);
+#else
+ m_new = m_get(pData, how, MT_DATA);
+#endif
+ if (m_new == NULL)
+ goto nospace;
+ }
+
+ m_copydata(m0, progress, length, mtod(m_new, caddr_t));
+ progress += length;
+ m_new->m_len = length;
+ if (m_new != m_final)
+#ifndef VBOX
+ m_cat(m_final, m_new);
+#else
+ m_cat(pData, m_final, m_new);
+#endif
+ m_new = NULL;
+ }
+#ifdef MBUF_STRESS_TEST
+ if (m0->m_next == NULL)
+ m_defraguseless++;
+#endif
+#ifndef VBOX
+ m_freem(m0);
+#else
+ m_freem(pData, m0);
+#endif
+ m0 = m_final;
+#ifdef MBUF_STRESS_TEST
+ m_defragpackets++;
+ m_defragbytes += m0->m_pkthdr.len;
+#endif
+ return (m0);
+nospace:
+#ifdef MBUF_STRESS_TEST
+ m_defragfailure++;
+#endif
+ if (m_final)
+#ifndef VBOX
+ m_freem(m_final);
+#else
+ m_freem(pData, m_final);
+#endif
+ return (NULL);
+}
+
+/*
+ * Defragment an mbuf chain, returning at most maxfrags separate
+ * mbufs+clusters. If this is not possible NULL is returned and
+ * the original mbuf chain is left in it's present (potentially
+ * modified) state. We use two techniques: collapsing consecutive
+ * mbufs and replacing consecutive mbufs by a cluster.
+ *
+ * NB: this should really be named m_defrag but that name is taken
+ */
+struct mbuf *
+#ifndef VBOX
+m_collapse(struct mbuf *m0, int how, int maxfrags)
+#else
+m_collapse(PNATState pData, struct mbuf *m0, int how, int maxfrags)
+#endif
+{
+ struct mbuf *m, *n, *n2, **prev;
+ u_int curfrags;
+
+ /*
+ * Calculate the current number of frags.
+ */
+ curfrags = 0;
+ for (m = m0; m != NULL; m = m->m_next)
+ curfrags++;
+ /*
+ * First, try to collapse mbufs. Note that we always collapse
+ * towards the front so we don't need to deal with moving the
+ * pkthdr. This may be suboptimal if the first mbuf has much
+ * less data than the following.
+ */
+ m = m0;
+again:
+ for (;;) {
+ n = m->m_next;
+ if (n == NULL)
+ break;
+ if ((m->m_flags & M_RDONLY) == 0 &&
+ n->m_len < M_TRAILINGSPACE(m)) {
+ bcopy(mtod(n, void *), mtod(m, char *) + m->m_len,
+ n->m_len);
+ m->m_len += n->m_len;
+ m->m_next = n->m_next;
+#ifndef VBOX
+ m_free(n);
+#else
+ m_free(pData, n);
+#endif
+ if (--curfrags <= maxfrags)
+ return m0;
+ } else
+ m = n;
+ }
+ KASSERT(maxfrags > 1,
+ ("maxfrags %u, but normal collapse failed", maxfrags));
+ /*
+ * Collapse consecutive mbufs to a cluster.
+ */
+ prev = &m0->m_next; /* NB: not the first mbuf */
+ while ((n = *prev) != NULL) {
+ if ((n2 = n->m_next) != NULL &&
+ n->m_len + n2->m_len < MCLBYTES) {
+#ifndef VBOX
+ m = m_getcl(how, MT_DATA, 0);
+#else
+ m = m_getcl(pData, how, MT_DATA, 0);
+#endif
+ if (m == NULL)
+ goto bad;
+ bcopy(mtod(n, void *), mtod(m, void *), n->m_len);
+ bcopy(mtod(n2, void *), mtod(m, char *) + n->m_len,
+ n2->m_len);
+ m->m_len = n->m_len + n2->m_len;
+ m->m_next = n2->m_next;
+ *prev = m;
+#ifndef VBOX
+ m_free(n);
+ m_free(n2);
+#else
+ m_free(pData, n);
+ m_free(pData, n2);
+#endif
+ if (--curfrags <= maxfrags) /* +1 cl -2 mbufs */
+ return m0;
+ /*
+ * Still not there, try the normal collapse
+ * again before we allocate another cluster.
+ */
+ goto again;
+ }
+ prev = &n->m_next;
+ }
+ /*
+ * No place where we can collapse to a cluster; punt.
+ * This can occur if, for example, you request 2 frags
+ * but the packet requires that both be clusters (we
+ * never reallocate the first mbuf to avoid moving the
+ * packet header).
+ */
+bad:
+ return NULL;
+}
+
+#ifdef MBUF_STRESS_TEST
+
+/*
+ * Fragment an mbuf chain. There's no reason you'd ever want to do
+ * this in normal usage, but it's great for stress testing various
+ * mbuf consumers.
+ *
+ * If fragmentation is not possible, the original chain will be
+ * returned.
+ *
+ * Possible length values:
+ * 0 no fragmentation will occur
+ * > 0 each fragment will be of the specified length
+ * -1 each fragment will be the same random value in length
+ * -2 each fragment's length will be entirely random
+ * (Random values range from 1 to 256)
+ */
+struct mbuf *
+m_fragment(struct mbuf *m0, int how, int length)
+{
+ struct mbuf *m_new = NULL, *m_final = NULL;
+ int progress = 0;
+
+ if (!(m0->m_flags & M_PKTHDR))
+ return (m0);
+
+ if ((length == 0) || (length < -2))
+ return (m0);
+
+ m_fixhdr(m0); /* Needed sanity check */
+
+ m_final = m_getcl(how, MT_DATA, M_PKTHDR);
+
+ if (m_final == NULL)
+ goto nospace;
+
+ if (m_dup_pkthdr(m_final, m0, how) == 0)
+ goto nospace;
+
+ m_new = m_final;
+
+ if (length == -1)
+ length = 1 + (arc4random() & 255);
+
+ while (progress < m0->m_pkthdr.len) {
+ int fraglen;
+
+ if (length > 0)
+ fraglen = length;
+ else
+ fraglen = 1 + (arc4random() & 255);
+ if (fraglen > m0->m_pkthdr.len - progress)
+ fraglen = m0->m_pkthdr.len - progress;
+
+ if (fraglen > MCLBYTES)
+ fraglen = MCLBYTES;
+
+ if (m_new == NULL) {
+ m_new = m_getcl(how, MT_DATA, 0);
+ if (m_new == NULL)
+ goto nospace;
+ }
+
+ m_copydata(m0, progress, fraglen, mtod(m_new, caddr_t));
+ progress += fraglen;
+ m_new->m_len = fraglen;
+ if (m_new != m_final)
+ m_cat(m_final, m_new);
+ m_new = NULL;
+ }
+ m_freem(m0);
+ m0 = m_final;
+ return (m0);
+nospace:
+ if (m_final)
+ m_freem(m_final);
+ /* Return the original chain on failure */
+ return (m0);
+}
+
+#endif
+
+/*
+ * Copy the contents of uio into a properly sized mbuf chain.
+ */
+#ifndef VBOX
+struct mbuf *
+m_uiotombuf(struct uio *uio, int how, int len, int align, int flags)
+{
+ struct mbuf *m, *mb;
+ int error, length, total;
+ int progress = 0;
+
+ /*
+ * len can be zero or an arbitrary large value bound by
+ * the total data supplied by the uio.
+ */
+ if (len > 0)
+ total = min(uio->uio_resid, len);
+ else
+ total = uio->uio_resid;
+
+ /*
+ * The smallest unit returned by m_getm2() is a single mbuf
+ * with pkthdr. We can't align past it.
+ */
+ if (align >= MHLEN)
+ return (NULL);
+
+ /*
+ * Give us the full allocation or nothing.
+ * If len is zero return the smallest empty mbuf.
+ */
+ m = m_getm2(NULL, max(total + align, 1), how, MT_DATA, flags);
+ if (m == NULL)
+ return (NULL);
+ m->m_data += align;
+
+ /* Fill all mbufs with uio data and update header information. */
+ for (mb = m; mb != NULL; mb = mb->m_next) {
+ length = min(M_TRAILINGSPACE(mb), total - progress);
+
+ error = uiomove(mtod(mb, void *), length, uio);
+ if (error) {
+ m_freem(m);
+ return (NULL);
+ }
+
+ mb->m_len = length;
+ progress += length;
+ if (flags & M_PKTHDR)
+ m->m_pkthdr.len += length;
+ }
+ KASSERT(progress == total, ("%s: progress != total", __func__));
+
+ return (m);
+}
+#endif
+
+/*
+ * Set the m_data pointer of a newly-allocated mbuf
+ * to place an object of the specified size at the
+ * end of the mbuf, longword aligned.
+ */
+void
+m_align(struct mbuf *m, int len)
+{
+ int adjust;
+
+ if (m->m_flags & M_EXT)
+ adjust = m->m_ext.ext_size - len;
+ else if (m->m_flags & M_PKTHDR)
+ adjust = MHLEN - len;
+ else
+ adjust = MLEN - len;
+ m->m_data += adjust &~ (sizeof(long)-1);
+}
+
+/*
+ * Create a writable copy of the mbuf chain. While doing this
+ * we compact the chain with a goal of producing a chain with
+ * at most two mbufs. The second mbuf in this chain is likely
+ * to be a cluster. The primary purpose of this work is to create
+ * a writable packet for encryption, compression, etc. The
+ * secondary goal is to linearize the data so the data can be
+ * passed to crypto hardware in the most efficient manner possible.
+ */
+struct mbuf *
+#ifndef VBOX
+m_unshare(struct mbuf *m0, int how)
+#else
+m_unshare(PNATState pData, struct mbuf *m0, int how)
+#endif
+{
+ struct mbuf *m, *mprev;
+ struct mbuf *n, *mfirst, *mlast;
+ int len, off;
+
+ mprev = NULL;
+ for (m = m0; m != NULL; m = mprev->m_next) {
+ /*
+ * Regular mbufs are ignored unless there's a cluster
+ * in front of it that we can use to coalesce. We do
+ * the latter mainly so later clusters can be coalesced
+ * also w/o having to handle them specially (i.e. convert
+ * mbuf+cluster -> cluster). This optimization is heavily
+ * influenced by the assumption that we're running over
+ * Ethernet where MCLBYTES is large enough that the max
+ * packet size will permit lots of coalescing into a
+ * single cluster. This in turn permits efficient
+ * crypto operations, especially when using hardware.
+ */
+ if ((m->m_flags & M_EXT) == 0) {
+ if (mprev && (mprev->m_flags & M_EXT) &&
+ m->m_len <= M_TRAILINGSPACE(mprev)) {
+ /* XXX: this ignores mbuf types */
+ memcpy(mtod(mprev, caddr_t) + mprev->m_len,
+ mtod(m, caddr_t), m->m_len);
+ mprev->m_len += m->m_len;
+ mprev->m_next = m->m_next; /* unlink from chain */
+#ifndef VBOX
+ m_free(m); /* reclaim mbuf */
+#else
+ m_free(pData, m); /* reclaim mbuf */
+#endif
+#if 0
+ newipsecstat.ips_mbcoalesced++;
+#endif
+ } else {
+ mprev = m;
+ }
+ continue;
+ }
+ /*
+ * Writable mbufs are left alone (for now).
+ */
+ if (M_WRITABLE(m)) {
+ mprev = m;
+ continue;
+ }
+
+ /*
+ * Not writable, replace with a copy or coalesce with
+ * the previous mbuf if possible (since we have to copy
+ * it anyway, we try to reduce the number of mbufs and
+ * clusters so that future work is easier).
+ */
+ KASSERT(m->m_flags & M_EXT, ("m_flags 0x%x", m->m_flags));
+ /* NB: we only coalesce into a cluster or larger */
+ if (mprev != NULL && (mprev->m_flags & M_EXT) &&
+ m->m_len <= M_TRAILINGSPACE(mprev)) {
+ /* XXX: this ignores mbuf types */
+ memcpy(mtod(mprev, caddr_t) + mprev->m_len,
+ mtod(m, caddr_t), m->m_len);
+ mprev->m_len += m->m_len;
+ mprev->m_next = m->m_next; /* unlink from chain */
+#ifndef VBOX
+ m_free(m); /* reclaim mbuf */
+#else
+ m_free(pData, m); /* reclaim mbuf */
+#endif
+#if 0
+ newipsecstat.ips_clcoalesced++;
+#endif
+ continue;
+ }
+
+ /*
+ * Allocate new space to hold the copy...
+ */
+ /* XXX why can M_PKTHDR be set past the first mbuf? */
+ if (mprev == NULL && (m->m_flags & M_PKTHDR)) {
+ /*
+ * NB: if a packet header is present we must
+ * allocate the mbuf separately from any cluster
+ * because M_MOVE_PKTHDR will smash the data
+ * pointer and drop the M_EXT marker.
+ */
+ MGETHDR(n, how, m->m_type);
+ if (n == NULL) {
+#ifndef VBOX
+ m_freem(m0);
+#else
+ m_freem(pData, m0);
+#endif
+ return (NULL);
+ }
+ M_MOVE_PKTHDR(n, m);
+ MCLGET(n, how);
+ if ((n->m_flags & M_EXT) == 0) {
+#ifndef VBOX
+ m_free(n);
+ m_freem(m0);
+#else
+ m_free(pData, n);
+ m_freem(pData, m0);
+#endif
+ return (NULL);
+ }
+ } else {
+#ifndef VBOX
+ n = m_getcl(how, m->m_type, m->m_flags);
+#else
+ n = m_getcl(pData, how, m->m_type, m->m_flags);
+#endif
+ if (n == NULL) {
+#ifndef VBOX
+ m_freem(m0);
+#else
+ m_freem(pData, m0);
+#endif
+ return (NULL);
+ }
+ }
+ /*
+ * ... and copy the data. We deal with jumbo mbufs
+ * (i.e. m_len > MCLBYTES) by splitting them into
+ * clusters. We could just malloc a buffer and make
+ * it external but too many device drivers don't know
+ * how to break up the non-contiguous memory when
+ * doing DMA.
+ */
+ len = m->m_len;
+ off = 0;
+ mfirst = n;
+ mlast = NULL;
+ for (;;) {
+ int cc = min(len, MCLBYTES);
+ memcpy(mtod(n, caddr_t), mtod(m, caddr_t) + off, cc);
+ n->m_len = cc;
+ if (mlast != NULL)
+ mlast->m_next = n;
+ mlast = n;
+#if 0
+ newipsecstat.ips_clcopied++;
+#endif
+
+ len -= cc;
+ if (len <= 0)
+ break;
+ off += cc;
+
+#ifndef VBOX
+ n = m_getcl(how, m->m_type, m->m_flags);
+#else
+ n = m_getcl(pData, how, m->m_type, m->m_flags);
+#endif
+ if (n == NULL) {
+#ifndef VBOX
+ m_freem(mfirst);
+ m_freem(m0);
+#else
+ m_freem(pData, mfirst);
+ m_freem(pData, m0);
+#endif
+ return (NULL);
+ }
+ }
+ n->m_next = m->m_next;
+ if (mprev == NULL)
+ m0 = mfirst; /* new head of chain */
+ else
+ mprev->m_next = mfirst; /* replace old mbuf */
+#ifndef VBOX
+ m_free(m); /* release old mbuf */
+#else
+ m_free(pData, m); /* release old mbuf */
+#endif
+ mprev = mfirst;
+ }
+ return (m0);
+}
diff --git a/src/VBox/Devices/Network/slirp/bsd/kern/uipc_mbuf2.c b/src/VBox/Devices/Network/slirp/bsd/kern/uipc_mbuf2.c
new file mode 100644
index 00000000..5f3abde8
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bsd/kern/uipc_mbuf2.c
@@ -0,0 +1,539 @@
+/* $KAME: uipc_mbuf2.c,v 1.31 2001/11/28 11:08:53 itojun Exp $ */
+/* $NetBSD: uipc_mbuf.c,v 1.40 1999/04/01 00:23:25 thorpej Exp $ */
+
+/*-
+ * Copyright (C) 1999 WIDE 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. Neither the name of the project 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 PROJECT 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 PROJECT 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.
+ */
+/*-
+ * Copyright (c) 1982, 1986, 1988, 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)uipc_mbuf.c 8.4 (Berkeley) 2/14/95
+ */
+#ifndef VBOX
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/kern/uipc_mbuf2.c,v 1.33.8.1 2009/04/15 03:14:26 kensmith Exp $");
+
+/*#define PULLDOWN_DEBUG*/
+
+#include "opt_mac.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/lock.h>
+#include <sys/malloc.h>
+#include <sys/mbuf.h>
+#include <sys/mutex.h>
+
+#include <security/mac/mac_framework.h>
+
+static MALLOC_DEFINE(M_PACKET_TAGS, MBUF_TAG_MEM_NAME,
+ "packet-attached information");
+#else
+# include "slirp.h"
+#endif
+
+/* can't call it m_dup(), as freebsd[34] uses m_dup() with different arg */
+#ifndef VBOX
+static struct mbuf *m_dup1(struct mbuf *, int, int, int);
+#else
+static struct mbuf *m_dup1(PNATState, struct mbuf *, int, int, int);
+#endif
+
+/*
+ * ensure that [off, off + len) is contiguous on the mbuf chain "m".
+ * packet chain before "off" is kept untouched.
+ * if offp == NULL, the target will start at <retval, 0> on resulting chain.
+ * if offp != NULL, the target will start at <retval, *offp> on resulting chain.
+ *
+ * on error return (NULL return value), original "m" will be freed.
+ *
+ * XXX: M_TRAILINGSPACE/M_LEADINGSPACE only permitted on writable ext_buf.
+ */
+struct mbuf *
+#ifndef VBOX
+m_pulldown(struct mbuf *m, int off, int len, int *offp)
+#else
+m_pulldown(PNATState pData, struct mbuf *m, int off, int len, int *offp)
+#endif
+{
+ struct mbuf *n, *o;
+ int hlen, tlen, olen;
+ int writable;
+
+ /* check invalid arguments. */
+ if (m == NULL)
+ panic("m == NULL in m_pulldown()");
+ if (len > MCLBYTES) {
+#ifndef VBOX
+ m_freem(m);
+#else
+ m_freem(pData, m);
+#endif
+ return NULL; /* impossible */
+ }
+
+#ifdef PULLDOWN_DEBUG
+ {
+ struct mbuf *t;
+ printf("before:");
+ for (t = m; t; t = t->m_next)
+ printf(" %d", t->m_len);
+ printf("\n");
+ }
+#endif
+ n = m;
+ while (n != NULL && off > 0) {
+ if (n->m_len > off)
+ break;
+ off -= n->m_len;
+ n = n->m_next;
+ }
+ /* be sure to point non-empty mbuf */
+ while (n != NULL && n->m_len == 0)
+ n = n->m_next;
+ if (!n) {
+#ifndef VBOX
+ m_freem(m);
+#else
+ m_freem(pData, m);
+#endif
+ return NULL; /* mbuf chain too short */
+ }
+
+ /*
+ * XXX: This code is flawed because it considers a "writable" mbuf
+ * data region to require all of the following:
+ * (i) mbuf _has_ to have M_EXT set; if it is just a regular
+ * mbuf, it is still not considered "writable."
+ * (ii) since mbuf has M_EXT, the ext_type _has_ to be
+ * EXT_CLUSTER. Anything else makes it non-writable.
+ * (iii) M_WRITABLE() must evaluate true.
+ * Ideally, the requirement should only be (iii).
+ *
+ * If we're writable, we're sure we're writable, because the ref. count
+ * cannot increase from 1, as that would require posession of mbuf
+ * n by someone else (which is impossible). However, if we're _not_
+ * writable, we may eventually become writable )if the ref. count drops
+ * to 1), but we'll fail to notice it unless we re-evaluate
+ * M_WRITABLE(). For now, we only evaluate once at the beginning and
+ * live with this.
+ */
+ /*
+ * XXX: This is dumb. If we're just a regular mbuf with no M_EXT,
+ * then we're not "writable," according to this code.
+ */
+ writable = 0;
+ if ((n->m_flags & M_EXT) == 0 ||
+ (n->m_ext.ext_type == EXT_CLUSTER && M_WRITABLE(n)))
+ writable = 1;
+
+ /*
+ * the target data is on <n, off>.
+ * if we got enough data on the mbuf "n", we're done.
+ */
+ if ((off == 0 || offp) && len <= n->m_len - off && writable)
+ goto ok;
+
+ /*
+ * when len <= n->m_len - off and off != 0, it is a special case.
+ * len bytes from <n, off> sits in single mbuf, but the caller does
+ * not like the starting position (off).
+ * chop the current mbuf into two pieces, set off to 0.
+ */
+ if (len <= n->m_len - off) {
+#ifndef VBOX
+ o = m_dup1(n, off, n->m_len - off, M_DONTWAIT);
+#else
+ o = m_dup1(pData, n, off, n->m_len - off, M_DONTWAIT);
+#endif
+ if (o == NULL) {
+#ifndef VBOX
+ m_freem(m);
+#else
+ m_freem(pData, m);
+#endif
+ return NULL; /* ENOBUFS */
+ }
+ n->m_len = off;
+ o->m_next = n->m_next;
+ n->m_next = o;
+ n = n->m_next;
+ off = 0;
+ goto ok;
+ }
+
+ /*
+ * we need to take hlen from <n, off> and tlen from <n->m_next, 0>,
+ * and construct contiguous mbuf with m_len == len.
+ * note that hlen + tlen == len, and tlen > 0.
+ */
+ hlen = n->m_len - off;
+ tlen = len - hlen;
+
+ /*
+ * ensure that we have enough trailing data on mbuf chain.
+ * if not, we can do nothing about the chain.
+ */
+ olen = 0;
+ for (o = n->m_next; o != NULL; o = o->m_next)
+ olen += o->m_len;
+ if (hlen + olen < len) {
+#ifndef VBOX
+ m_freem(m);
+#else
+ m_freem(pData, m);
+#endif
+ return NULL; /* mbuf chain too short */
+ }
+
+ /*
+ * easy cases first.
+ * we need to use m_copydata() to get data from <n->m_next, 0>.
+ */
+ if ((off == 0 || offp) && M_TRAILINGSPACE(n) >= tlen
+ && writable) {
+ m_copydata(n->m_next, 0, tlen, mtod(n, caddr_t) + n->m_len);
+ n->m_len += tlen;
+#ifndef VBOX
+ m_adj(n->m_next, tlen);
+#else
+ m_adj(pData, n->m_next, tlen);
+#endif
+ goto ok;
+ }
+ if ((off == 0 || offp) && M_LEADINGSPACE(n->m_next) >= hlen
+ && writable) {
+ n->m_next->m_data -= hlen;
+ n->m_next->m_len += hlen;
+ bcopy(mtod(n, caddr_t) + off, mtod(n->m_next, caddr_t), hlen);
+ n->m_len -= hlen;
+ n = n->m_next;
+ off = 0;
+ goto ok;
+ }
+
+ /*
+ * now, we need to do the hard way. don't m_copy as there's no room
+ * on both end.
+ */
+ if (len > MLEN)
+#ifndef VBOX
+ o = m_getcl(M_DONTWAIT, m->m_type, 0);
+#else
+ o = m_getcl(pData, M_DONTWAIT, m->m_type, 0);
+#endif
+ else
+#ifndef VBOX
+ o = m_get(M_DONTWAIT, m->m_type);
+#else
+ o = m_get(pData, M_DONTWAIT, m->m_type);
+#endif
+ if (!o) {
+#ifndef VBOX
+ m_freem(m);
+#else
+ m_freem(pData, m);
+#endif
+ return NULL; /* ENOBUFS */
+ }
+ /* get hlen from <n, off> into <o, 0> */
+ o->m_len = hlen;
+ bcopy(mtod(n, caddr_t) + off, mtod(o, caddr_t), hlen);
+ n->m_len -= hlen;
+ /* get tlen from <n->m_next, 0> into <o, hlen> */
+ m_copydata(n->m_next, 0, tlen, mtod(o, caddr_t) + o->m_len);
+ o->m_len += tlen;
+#ifndef VBOX
+ m_adj(n->m_next, tlen);
+#else
+ m_adj(pData, n->m_next, tlen);
+#endif
+ o->m_next = n->m_next;
+ n->m_next = o;
+ n = o;
+ off = 0;
+
+ok:
+#ifdef PULLDOWN_DEBUG
+ {
+ struct mbuf *t;
+ printf("after:");
+ for (t = m; t; t = t->m_next)
+ printf("%c%d", t == n ? '*' : ' ', t->m_len);
+ printf(" (off=%d)\n", off);
+ }
+#endif
+ if (offp)
+ *offp = off;
+ return n;
+}
+
+static struct mbuf *
+#ifndef VBOX
+m_dup1(struct mbuf *m, int off, int len, int fWait)
+#else
+m_dup1(PNATState pData, struct mbuf *m, int off, int len, int fWait)
+#endif
+{
+ struct mbuf *n;
+ int copyhdr;
+
+ if (len > MCLBYTES)
+ return NULL;
+ if (off == 0 && (m->m_flags & M_PKTHDR) != 0)
+ copyhdr = 1;
+ else
+ copyhdr = 0;
+ if (len >= MINCLSIZE) {
+ if (copyhdr == 1)
+#ifndef VBOX
+ n = m_getcl(fWait, m->m_type, M_PKTHDR);
+#else
+ n = m_getcl(pData, fWait, m->m_type, M_PKTHDR);
+#endif
+ else
+#ifndef VBOX
+ n = m_getcl(fWait, m->m_type, 0);
+#else
+ n = m_getcl(pData, fWait, m->m_type, 0);
+#endif
+ } else {
+ if (copyhdr == 1)
+#ifndef VBOX
+ n = m_gethdr(fWait, m->m_type);
+#else
+ n = m_gethdr(pData, fWait, m->m_type);
+#endif
+ else
+#ifndef VBOX
+ n = m_get(fWait, m->m_type);
+#else
+ n = m_get(pData, fWait, m->m_type);
+#endif
+ }
+ if (!n)
+ return NULL; /* ENOBUFS */
+
+ if (copyhdr && !m_dup_pkthdr(n, m, fWait)) {
+#ifndef VBOX
+ m_free(n);
+#else
+ m_free(pData, n);
+#endif
+ return NULL;
+ }
+ m_copydata(m, off, len, mtod(n, caddr_t));
+ n->m_len = len;
+ return n;
+}
+
+/* Free a packet tag. */
+void
+m_tag_free_default(struct m_tag *t)
+{
+#ifdef MAC
+ if (t->m_tag_id == PACKET_TAG_MACLABEL)
+ mac_destroy_mbuf_tag(t);
+#endif
+#ifndef VBOX
+ free(t, M_PACKET_TAGS);
+#else
+ RTMemFree(t);
+#endif
+}
+
+/* Get a packet tag structure along with specified data following. */
+struct m_tag *
+m_tag_alloc(u_int32_t cookie, int type, int len, int fWait)
+{
+ struct m_tag *t;
+
+ MBUF_CHECKSLEEP(fWait);
+ if (len < 0)
+ return NULL;
+#ifndef VBOX
+ t = malloc(len + sizeof(struct m_tag), M_PACKET_TAGS, fWait);
+#else
+ NOREF(fWait);
+ t = RTMemAllocZ(len + sizeof(struct m_tag));
+#endif
+ if (t == NULL)
+ return NULL;
+ m_tag_setup(t, cookie, type, len);
+ t->m_tag_free = m_tag_free_default;
+ return t;
+}
+
+/* Unlink and free a packet tag. */
+void
+m_tag_delete(struct mbuf *m, struct m_tag *t)
+{
+
+ KASSERT(m && t, ("m_tag_delete: null argument, m %p t %p", m, t));
+ m_tag_unlink(m, t);
+ m_tag_free(t);
+}
+
+/* Unlink and free a packet tag chain, starting from given tag. */
+void
+m_tag_delete_chain(struct mbuf *m, struct m_tag *t)
+{
+ struct m_tag *p, *q;
+
+ KASSERT(m, ("m_tag_delete_chain: null mbuf"));
+ if (t != NULL)
+ p = t;
+ else
+ p = SLIST_FIRST(&m->m_pkthdr.tags);
+ if (p == NULL)
+ return;
+ while ((q = SLIST_NEXT(p, m_tag_link)) != NULL)
+ m_tag_delete(m, q);
+ m_tag_delete(m, p);
+}
+
+/*
+ * Strip off all tags that would normally vanish when
+ * passing through a network interface. Only persistent
+ * tags will exist after this; these are expected to remain
+ * so long as the mbuf chain exists, regardless of the
+ * path the mbufs take.
+ */
+void
+m_tag_delete_nonpersistent(struct mbuf *m)
+{
+ struct m_tag *p, *q;
+
+ SLIST_FOREACH_SAFE(p, &m->m_pkthdr.tags, m_tag_link, q)
+ if ((p->m_tag_id & MTAG_PERSISTENT) == 0)
+ m_tag_delete(m, p);
+}
+
+/* Find a tag, starting from a given position. */
+struct m_tag *
+m_tag_locate(struct mbuf *m, u_int32_t cookie, int type, struct m_tag *t)
+{
+ struct m_tag *p;
+
+ KASSERT(m, ("m_tag_locate: null mbuf"));
+ if (t == NULL)
+ p = SLIST_FIRST(&m->m_pkthdr.tags);
+ else
+ p = SLIST_NEXT(t, m_tag_link);
+ while (p != NULL) {
+ if (p->m_tag_cookie == cookie && p->m_tag_id == type)
+ return p;
+ p = SLIST_NEXT(p, m_tag_link);
+ }
+ return NULL;
+}
+
+/* Copy a single tag. */
+struct m_tag *
+m_tag_copy(struct m_tag *t, int how)
+{
+ struct m_tag *p;
+
+ MBUF_CHECKSLEEP(how);
+ KASSERT(t, ("m_tag_copy: null tag"));
+ p = m_tag_alloc(t->m_tag_cookie, t->m_tag_id, t->m_tag_len, how);
+ if (p == NULL)
+ return (NULL);
+#ifdef MAC
+ /*
+ * XXXMAC: we should probably pass off the initialization, and
+ * copying here? can we hide that PACKET_TAG_MACLABEL is
+ * special from the mbuf code?
+ */
+ if (t->m_tag_id == PACKET_TAG_MACLABEL) {
+ if (mac_init_mbuf_tag(p, how) != 0) {
+ m_tag_free(p);
+ return (NULL);
+ }
+ mac_copy_mbuf_tag(t, p);
+ } else
+#endif
+ bcopy(t + 1, p + 1, t->m_tag_len); /* Copy the data */
+ return p;
+}
+
+/*
+ * Copy two tag chains. The destination mbuf (to) loses any attached
+ * tags even if the operation fails. This should not be a problem, as
+ * m_tag_copy_chain() is typically called with a newly-allocated
+ * destination mbuf.
+ */
+int
+m_tag_copy_chain(struct mbuf *to, struct mbuf *from, int how)
+{
+ struct m_tag *p, *t, *tprev = NULL;
+
+ MBUF_CHECKSLEEP(how);
+ KASSERT(to && from,
+ ("m_tag_copy_chain: null argument, to %p from %p", to, from));
+ m_tag_delete_chain(to, NULL);
+ SLIST_FOREACH(p, &from->m_pkthdr.tags, m_tag_link) {
+ t = m_tag_copy(p, how);
+ if (t == NULL) {
+ m_tag_delete_chain(to, NULL);
+ return 0;
+ }
+ if (tprev == NULL)
+ SLIST_INSERT_HEAD(&to->m_pkthdr.tags, t, m_tag_link);
+ else
+ SLIST_INSERT_AFTER(tprev, t, m_tag_link);
+ tprev = t;
+ }
+ return 1;
+}
diff --git a/src/VBox/Devices/Network/slirp/bsd/sys/mbuf.h b/src/VBox/Devices/Network/slirp/bsd/sys/mbuf.h
new file mode 100644
index 00000000..909f300b
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bsd/sys/mbuf.h
@@ -0,0 +1,1177 @@
+/*-
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)mbuf.h 8.5 (Berkeley) 2/19/95
+ * $FreeBSD: src/sys/sys/mbuf.h,v 1.217.2.3.4.1 2009/04/15 03:14:26 kensmith Exp $
+ */
+
+#ifndef _SYS_MBUF_H_
+#define _SYS_MBUF_H_
+
+#ifndef VBOX
+/* XXX: These includes suck. Sorry! */
+#include <sys/queue.h>
+#ifdef _KERNEL
+#include <sys/systm.h>
+#include <vm/uma.h>
+#ifdef WITNESS
+#include <sys/lock.h>
+#endif
+#endif
+#else /* VBOX */
+# include <VBox/param.h>
+# include "misc.h"
+# include "ext.h"
+
+typedef const char *c_caddr_t;
+
+DECL_NO_RETURN(static void) panic (char *fmt, ...)
+{
+ va_list args;
+ va_start(args, fmt);
+ vbox_slirp_printV(fmt, args);
+ va_end(args);
+ AssertFatalFailed();
+}
+/* for non-gnu compilers */
+# define __func__ RT_GCC_EXTENSION __FUNCTION__
+# ifndef __inline
+# ifdef __GNUC__
+# define __inline __inline__
+# else
+# define __inline
+# endif
+# endif
+
+# undef bzero
+# define bzero(a1, len) memset((a1), 0, (len))
+
+/* (vvl) some definitions from sys/param.h */
+/*
+ * Constants related to network buffer management.
+ * MCLBYTES must be no larger than HOST_PAGE_SIZE.
+ */
+# ifndef MSIZE
+# define MSIZE 256 /* size of an mbuf */
+# endif /* MSIZE */
+
+# ifndef MCLSHIFT
+# define MCLSHIFT 11 /* convert bytes to mbuf clusters */
+# endif /* MCLSHIFT */
+
+# ifndef MCLBYTES
+# define MCLBYTES (1 << MCLSHIFT) /* size of an mbuf cluster */
+# endif /*MCLBYTES*/
+
+# if defined(RT_ARCH_AMD64) || defined(RT_ARCH_X86)
+# define MJUMPAGESIZE HOST_PAGE_SIZE /* jumbo cluster 4k */
+# else
+# define MJUMPAGESIZE (4 * 1024) /* jumbo cluster 4k */
+# endif
+# define MJUM9BYTES (9 * 1024) /* jumbo cluster 9k */
+# define MJUM16BYTES (16 * 1024) /* jumbo cluster 16k */
+#endif /* VBOX */
+
+/*
+ * Mbufs are of a single size, MSIZE (sys/param.h), which includes overhead.
+ * An mbuf may add a single "mbuf cluster" of size MCLBYTES (also in
+ * sys/param.h), which has no additional overhead and is used instead of the
+ * internal data area; this is done when at least MINCLSIZE of data must be
+ * stored. Additionally, it is possible to allocate a separate buffer
+ * externally and attach it to the mbuf in a way similar to that of mbuf
+ * clusters.
+ */
+#define MLEN (MSIZE - sizeof(struct m_hdr)) /* normal data len */
+#define MHLEN (MLEN - sizeof(struct pkthdr)) /* data len w/pkthdr */
+#define MINCLSIZE (MHLEN + 1) /* smallest amount to put in cluster */
+#define M_MAXCOMPRESS (MHLEN / 2) /* max amount to copy for compression */
+
+#if defined(_KERNEL) || defined(VBOX)
+/*-
+ * Macros for type conversion:
+ * mtod(m, t) -- Convert mbuf pointer to data pointer of correct type.
+ * dtom(x) -- Convert data pointer within mbuf to mbuf pointer (XXX).
+ */
+#define mtod(m, t) ((t)((m)->m_data))
+#define dtom(x) ((struct mbuf *)((intptr_t)(x) & ~(MSIZE-1)))
+
+/*
+ * Argument structure passed to UMA routines during mbuf and packet
+ * allocations.
+ */
+struct mb_args {
+ int flags; /* Flags for mbuf being allocated */
+ short type; /* Type of mbuf being allocated */
+};
+#endif /* _KERNEL */
+
+#if defined(__LP64__)
+#define M_HDR_PAD 6
+#else
+#define M_HDR_PAD 2
+#endif
+
+/*
+ * Header present at the beginning of every mbuf.
+ */
+struct m_hdr {
+ struct mbuf *mh_next; /* next buffer in chain */
+ struct mbuf *mh_nextpkt; /* next chain in queue/record */
+ caddr_t mh_data; /* location of data */
+ int mh_len; /* amount of data in this mbuf */
+ int mh_flags; /* flags; see below */
+ short mh_type; /* type of data in this mbuf */
+#ifdef VBOX
+ struct socket *mh_so; /*socket assotiated with mbuf*/
+ TAILQ_ENTRY(mbuf) mh_ifq;
+#endif
+ uint8_t pad[M_HDR_PAD];/* word align */
+};
+
+/*
+ * Packet tag structure (see below for details).
+ */
+struct m_tag {
+ SLIST_ENTRY(m_tag) m_tag_link; /* List of packet tags */
+ u_int16_t m_tag_id; /* Tag ID */
+ u_int16_t m_tag_len; /* Length of data */
+ u_int32_t m_tag_cookie; /* ABI/Module ID */
+ void (*m_tag_free)(struct m_tag *);
+};
+
+/*
+ * Record/packet header in first mbuf of chain; valid only if M_PKTHDR is set.
+ */
+struct pkthdr {
+ struct ifnet *rcvif; /* rcv interface */
+ /* variables for ip and tcp reassembly */
+ void *header; /* pointer to packet header */
+ int len; /* total packet length */
+ /* variables for hardware checksum */
+ int csum_flags; /* flags regarding checksum */
+ int csum_data; /* data field used by csum routines */
+ u_int16_t tso_segsz; /* TSO segment size */
+ u_int16_t ether_vtag; /* Ethernet 802.1p+q vlan tag */
+ SLIST_HEAD(packet_tags, m_tag) tags; /* list of packet tags */
+};
+
+/*
+ * Description of external storage mapped into mbuf; valid only if M_EXT is
+ * set.
+ */
+struct m_ext {
+ caddr_t ext_buf; /* start of buffer */
+ void (*ext_free) /* free routine if not the usual */
+ (void *, void *);
+ void *ext_args; /* optional argument pointer */
+ u_int ext_size; /* size of buffer, for ext_free */
+#ifdef VBOX
+ volatile uint32_t *ref_cnt; /* pointer to ref count info */
+#else
+ volatile u_int *ref_cnt; /* pointer to ref count info */
+#endif
+ int ext_type; /* type of external storage */
+};
+
+/*
+ * The core of the mbuf object along with some shortcut defines for practical
+ * purposes.
+ */
+struct mbuf {
+ struct m_hdr m_hdr;
+ union {
+ struct {
+ struct pkthdr MH_pkthdr; /* M_PKTHDR set */
+ union {
+ struct m_ext MH_ext; /* M_EXT set */
+ char MH_databuf[MHLEN];
+ } MH_dat;
+ } MH;
+ char M_databuf[MLEN]; /* !M_PKTHDR, !M_EXT */
+ } M_dat;
+};
+#define m_next m_hdr.mh_next
+#define m_len m_hdr.mh_len
+#define m_data m_hdr.mh_data
+#define m_type m_hdr.mh_type
+#define m_flags m_hdr.mh_flags
+#define m_nextpkt m_hdr.mh_nextpkt
+#define m_act m_nextpkt
+#define m_pkthdr M_dat.MH.MH_pkthdr
+#define m_ext M_dat.MH.MH_dat.MH_ext
+#define m_pktdat M_dat.MH.MH_dat.MH_databuf
+#define m_dat M_dat.M_databuf
+#ifdef VBOX
+# define m_so m_hdr.mh_so
+# define ifq_so m_hdr.mh_so
+# define m_ifq m_hdr.mh_ifq
+#endif
+
+/*
+ * mbuf flags.
+ */
+#define M_EXT 0x00000001 /* has associated external storage */
+#define M_PKTHDR 0x00000002 /* start of record */
+#define M_EOR 0x00000004 /* end of record */
+#define M_RDONLY 0x00000008 /* associated data is marked read-only */
+#define M_PROTO1 0x00000010 /* protocol-specific */
+#define M_PROTO2 0x00000020 /* protocol-specific */
+#define M_PROTO3 0x00000040 /* protocol-specific */
+#define M_PROTO4 0x00000080 /* protocol-specific */
+#define M_PROTO5 0x00000100 /* protocol-specific */
+#define M_BCAST 0x00000200 /* send/received as link-level broadcast */
+#define M_MCAST 0x00000400 /* send/received as link-level multicast */
+#define M_FRAG 0x00000800 /* packet is a fragment of a larger packet */
+#define M_FIRSTFRAG 0x00001000 /* packet is first fragment */
+#define M_LASTFRAG 0x00002000 /* packet is last fragment */
+#define M_SKIP_FIREWALL 0x00004000 /* skip firewall processing */
+#define M_FREELIST 0x00008000 /* mbuf is on the free list */
+#define M_VLANTAG 0x00010000 /* ether_vtag is valid */
+#define M_PROMISC 0x00020000 /* packet was not for us */
+#define M_NOFREE 0x00040000 /* do not free mbuf, embedded in cluster */
+#define M_PROTO6 0x00080000 /* protocol-specific */
+#define M_PROTO7 0x00100000 /* protocol-specific */
+#define M_PROTO8 0x00200000 /* protocol-specific */
+/*
+ * For RELENG_{6,7} steal these flags for limited multiple routing table
+ * support. In RELENG_8 and beyond, use just one flag and a tag.
+ */
+#define M_FIB 0xF0000000 /* steal some bits to store fib number. */
+
+#define M_NOTIFICATION M_PROTO5 /* SCTP notification */
+
+/*
+ * Flags to purge when crossing layers.
+ */
+#define M_PROTOFLAGS \
+ (M_PROTO1|M_PROTO2|M_PROTO3|M_PROTO4|M_PROTO5|M_PROTO6|M_PROTO7|M_PROTO8)
+
+/*
+ * Flags preserved when copying m_pkthdr.
+ */
+#define M_COPYFLAGS \
+ (M_PKTHDR|M_EOR|M_RDONLY|M_PROTOFLAGS|M_SKIP_FIREWALL|M_BCAST|M_MCAST|\
+ M_FRAG|M_FIRSTFRAG|M_LASTFRAG|M_VLANTAG|M_PROMISC|M_FIB)
+
+/*
+ * External buffer types: identify ext_buf type.
+ */
+#define EXT_CLUSTER 1 /* mbuf cluster */
+#define EXT_SFBUF 2 /* sendfile(2)'s sf_bufs */
+#define EXT_JUMBOP 3 /* jumbo cluster 4096 bytes */
+#define EXT_JUMBO9 4 /* jumbo cluster 9216 bytes */
+#define EXT_JUMBO16 5 /* jumbo cluster 16184 bytes */
+#define EXT_PACKET 6 /* mbuf+cluster from packet zone */
+#define EXT_MBUF 7 /* external mbuf reference (M_IOVEC) */
+#define EXT_NET_DRV 100 /* custom ext_buf provided by net driver(s) */
+#define EXT_MOD_TYPE 200 /* custom module's ext_buf type */
+#define EXT_DISPOSABLE 300 /* can throw this buffer away w/page flipping */
+#define EXT_EXTREF 400 /* has externally maintained ref_cnt ptr */
+
+/*
+ * Flags indicating hw checksum support and sw checksum requirements. This
+ * field can be directly tested against if_data.ifi_hwassist.
+ */
+#define CSUM_IP 0x0001 /* will csum IP */
+#define CSUM_TCP 0x0002 /* will csum TCP */
+#define CSUM_UDP 0x0004 /* will csum UDP */
+#define CSUM_IP_FRAGS 0x0008 /* will csum IP fragments */
+#define CSUM_FRAGMENT 0x0010 /* will do IP fragmentation */
+#define CSUM_TSO 0x0020 /* will do TSO */
+
+#define CSUM_IP_CHECKED 0x0100 /* did csum IP */
+#define CSUM_IP_VALID 0x0200 /* ... the csum is valid */
+#define CSUM_DATA_VALID 0x0400 /* csum_data field is valid */
+#define CSUM_PSEUDO_HDR 0x0800 /* csum_data has pseudo hdr */
+
+#define CSUM_DELAY_DATA (CSUM_TCP | CSUM_UDP)
+#define CSUM_DELAY_IP (CSUM_IP) /* XXX add ipv6 here too? */
+
+/*
+ * mbuf types.
+ */
+#define MT_NOTMBUF 0 /* USED INTERNALLY ONLY! Object is not mbuf */
+#define MT_DATA 1 /* dynamic (data) allocation */
+#define MT_HEADER MT_DATA /* packet header, use M_PKTHDR instead */
+#define MT_SONAME 8 /* socket name */
+#define MT_CONTROL 14 /* extra-data protocol message */
+#define MT_OOBDATA 15 /* expedited data */
+#define MT_NTYPES 16 /* number of mbuf types for mbtypes[] */
+
+#define MT_NOINIT 255 /* Not a type but a flag to allocate
+ a non-initialized mbuf */
+
+#define MB_NOTAGS 0x1UL /* no tags attached to mbuf */
+
+/*
+ * General mbuf allocator statistics structure.
+ *
+ * Many of these statistics are no longer used; we instead track many
+ * allocator statistics through UMA's built in statistics mechanism.
+ */
+struct mbstat {
+ u_long m_mbufs; /* XXX */
+ u_long m_mclusts; /* XXX */
+
+ u_long m_drain; /* times drained protocols for space */
+ u_long m_mcfail; /* XXX: times m_copym failed */
+ u_long m_mpfail; /* XXX: times m_pullup failed */
+ u_long m_msize; /* length of an mbuf */
+ u_long m_mclbytes; /* length of an mbuf cluster */
+ u_long m_minclsize; /* min length of data to allocate a cluster */
+ u_long m_mlen; /* length of data in an mbuf */
+ u_long m_mhlen; /* length of data in a header mbuf */
+
+ /* Number of mbtypes (gives # elems in mbtypes[] array) */
+ short m_numtypes;
+
+ /* XXX: Sendfile stats should eventually move to their own struct */
+ u_long sf_iocnt; /* times sendfile had to do disk I/O */
+ u_long sf_allocfail; /* times sfbuf allocation failed */
+ u_long sf_allocwait; /* times sfbuf allocation had to wait */
+};
+
+/*
+ * Flags specifying how an allocation should be made.
+ *
+ * The flag to use is as follows:
+ * - M_DONTWAIT or M_NOWAIT from an interrupt handler to not block allocation.
+ * - M_WAIT or M_WAITOK or M_TRYWAIT from wherever it is safe to block.
+ *
+ * M_DONTWAIT/M_NOWAIT means that we will not block the thread explicitly and
+ * if we cannot allocate immediately we may return NULL, whereas
+ * M_WAIT/M_WAITOK/M_TRYWAIT means that if we cannot allocate resources we
+ * will block until they are available, and thus never return NULL.
+ *
+ * XXX Eventually just phase this out to use M_WAITOK/M_NOWAIT.
+ */
+#define MBTOM(how) (how)
+#ifndef VBOX
+#define M_DONTWAIT M_NOWAIT
+#define M_TRYWAIT M_WAITOK
+#define M_WAIT M_WAITOK
+#else
+/* @todo (r=vvl) not sure we can do it in NAT */
+# define M_WAITOK 0
+# define M_NOWAIT 0
+# define M_DONTWAIT 0
+# define M_TRYWAI 0
+# define M_WAIT 0
+#endif
+
+/*
+ * String names of mbuf-related UMA(9) and malloc(9) types. Exposed to
+ * !_KERNEL so that monitoring tools can look up the zones with
+ * libmemstat(3).
+ */
+#define MBUF_MEM_NAME "mbuf"
+#define MBUF_CLUSTER_MEM_NAME "mbuf_cluster"
+#define MBUF_PACKET_MEM_NAME "mbuf_packet"
+#define MBUF_JUMBOP_MEM_NAME "mbuf_jumbo_pagesize"
+#define MBUF_JUMBO9_MEM_NAME "mbuf_jumbo_9k"
+#define MBUF_JUMBO16_MEM_NAME "mbuf_jumbo_16k"
+#define MBUF_TAG_MEM_NAME "mbuf_tag"
+#define MBUF_EXTREFCNT_MEM_NAME "mbuf_ext_refcnt"
+
+#if defined(_KERNEL) || defined(VBOX)
+
+#ifdef WITNESS
+#define MBUF_CHECKSLEEP(how) do { \
+ if (how == M_WAITOK) \
+ WITNESS_WARN(WARN_GIANTOK | WARN_SLEEPOK, NULL, \
+ "Sleeping in \"%s\"", __func__); \
+} while (0)
+#else
+#define MBUF_CHECKSLEEP(how)
+#endif
+
+/*
+ * Network buffer allocation API
+ *
+ * The rest of it is defined in kern/kern_mbuf.c
+ */
+
+#ifndef VBOX
+extern uma_zone_t zone_mbuf;
+extern uma_zone_t zone_clust;
+extern uma_zone_t zone_pack;
+extern uma_zone_t zone_jumbop;
+extern uma_zone_t zone_jumbo9;
+extern uma_zone_t zone_jumbo16;
+extern uma_zone_t zone_ext_refcnt;
+#endif
+
+#ifndef VBOX
+static __inline struct mbuf *m_getcl(int how, short type, int flags);
+static __inline struct mbuf *m_get(int how, short type);
+static __inline struct mbuf *m_gethdr(int how, short type);
+static __inline struct mbuf *m_getjcl(int how, short type, int flags,
+ int size);
+static __inline struct mbuf *m_getclr(int how, short type); /* XXX */
+static __inline struct mbuf *m_free(struct mbuf *m);
+static __inline void m_clget(struct mbuf *m, int how);
+static __inline void *m_cljget(struct mbuf *m, int how, int size);
+void mb_free_ext(struct mbuf *);
+#else
+static __inline struct mbuf *m_getcl(PNATState pData, int how, short type, int flags);
+static __inline struct mbuf *m_get(PNATState pData, int how, short type);
+static __inline struct mbuf *m_gethdr(PNATState pData, int how, short type);
+static __inline struct mbuf *m_getjcl(PNATState pData, int how,
+ short type, int flags, int size);
+static __inline struct mbuf *m_getclr(PNATState pData, int how, short type); /* XXX */
+static __inline struct mbuf *m_free(PNATState pData, struct mbuf *m);
+static __inline void m_clget(PNATState pData, struct mbuf *m, int how);
+static __inline void *m_cljget(PNATState pData, struct mbuf *m, int how, int size);
+void mb_free_ext(PNATState, struct mbuf *);
+#endif
+static __inline void m_chtype(struct mbuf *m, short new_type);
+static __inline struct mbuf *m_last(struct mbuf *m);
+
+static __inline int
+m_gettype(int size)
+{
+ int type;
+
+ switch (size) {
+ case MSIZE:
+ type = EXT_MBUF;
+ break;
+ case MCLBYTES:
+ type = EXT_CLUSTER;
+ break;
+#if MJUMPAGESIZE != MCLBYTES
+ case MJUMPAGESIZE:
+ type = EXT_JUMBOP;
+ break;
+#endif
+ case MJUM9BYTES:
+ type = EXT_JUMBO9;
+ break;
+ case MJUM16BYTES:
+ type = EXT_JUMBO16;
+ break;
+ default:
+ panic("%s: m_getjcl: invalid cluster size", __func__);
+ }
+
+ return (type);
+}
+
+static __inline uma_zone_t
+#ifndef VBOX
+m_getzone(int size)
+#else
+m_getzone(PNATState pData, int size)
+#endif
+{
+ uma_zone_t zone;
+
+ switch (size) {
+ case MSIZE:
+ zone = zone_mbuf;
+ break;
+ case MCLBYTES:
+ zone = zone_clust;
+ break;
+#if MJUMPAGESIZE != MCLBYTES
+ case MJUMPAGESIZE:
+ zone = zone_jumbop;
+ break;
+#endif
+ case MJUM9BYTES:
+ zone = zone_jumbo9;
+ break;
+ case MJUM16BYTES:
+ zone = zone_jumbo16;
+ break;
+ default:
+ panic("%s: m_getjcl: invalid cluster type", __func__);
+ }
+
+ return (zone);
+}
+
+static __inline struct mbuf *
+#ifndef VBOX
+m_get(int how, short type)
+#else
+m_get(PNATState pData, int how, short type)
+#endif
+{
+ struct mb_args args;
+
+ args.flags = 0;
+ args.type = type;
+ return ((struct mbuf *)(uma_zalloc_arg(zone_mbuf, &args, how)));
+}
+
+/*
+ * XXX This should be deprecated, very little use.
+ */
+static __inline struct mbuf *
+#ifndef VBOX
+m_getclr(int how, short type)
+#else
+m_getclr(PNATState pData, int how, short type)
+#endif
+{
+ struct mbuf *m;
+ struct mb_args args;
+
+ args.flags = 0;
+ args.type = type;
+ m = uma_zalloc_arg(zone_mbuf, &args, how);
+ if (m != NULL)
+ bzero(m->m_data, MLEN);
+ return (m);
+}
+
+static __inline struct mbuf *
+#ifndef VBOX
+m_gethdr(int how, short type)
+#else
+m_gethdr(PNATState pData, int how, short type)
+#endif
+{
+ struct mb_args args;
+
+ args.flags = M_PKTHDR;
+ args.type = type;
+ return ((struct mbuf *)(uma_zalloc_arg(zone_mbuf, &args, how)));
+}
+
+static __inline struct mbuf *
+#ifndef VBOX
+m_getcl(int how, short type, int flags)
+#else
+m_getcl(PNATState pData, int how, short type, int flags)
+#endif
+{
+ struct mb_args args;
+
+ args.flags = flags;
+ args.type = type;
+ return ((struct mbuf *)(uma_zalloc_arg(zone_pack, &args, how)));
+}
+
+/*
+ * m_getjcl() returns an mbuf with a cluster of the specified size attached.
+ * For size it takes MCLBYTES, MJUMPAGESIZE, MJUM9BYTES, MJUM16BYTES.
+ *
+ * XXX: This is rather large, should be real function maybe.
+ */
+static __inline struct mbuf *
+#ifndef VBOX
+m_getjcl(int how, short type, int flags, int size)
+#else
+m_getjcl(PNATState pData, int how, short type, int flags, int size)
+#endif
+{
+ struct mb_args args;
+ struct mbuf *m, *n;
+ uma_zone_t zone;
+
+ args.flags = flags;
+ args.type = type;
+
+ m = uma_zalloc_arg(zone_mbuf, &args, how);
+ if (m == NULL)
+ return (NULL);
+
+#ifndef VBOX
+ zone = m_getzone(size);
+#else
+ zone = m_getzone(pData, size);
+#endif
+ n = uma_zalloc_arg(zone, m, how);
+ if (n == NULL) {
+ uma_zfree(zone_mbuf, m);
+ return (NULL);
+ }
+ return (m);
+}
+
+#ifndef VBOX
+static __inline void
+m_free_fast(struct mbuf *m)
+{
+ KASSERT(SLIST_EMPTY(&m->m_pkthdr.tags), ("doing fast free of mbuf with tags"));
+
+ uma_zfree_arg(zone_mbuf, m, (void *)MB_NOTAGS);
+}
+#else
+static __inline void
+m_free_fast(PNATState pData, struct mbuf *m)
+{
+ AssertMsg(SLIST_EMPTY(&m->m_pkthdr.tags), ("doing fast free of mbuf with tags"));
+
+ uma_zfree_arg(zone_mbuf, m, (void *)(uintptr_t)MB_NOTAGS);
+}
+#endif
+
+static __inline struct mbuf *
+#ifndef VBOX
+m_free(struct mbuf *m)
+#else
+m_free(PNATState pData, struct mbuf *m)
+#endif
+{
+ struct mbuf *n = m->m_next;
+
+ if (m->m_flags & M_EXT)
+#ifndef VBOX
+ mb_free_ext(m);
+#else
+ mb_free_ext(pData, m);
+#endif
+ else if ((m->m_flags & M_NOFREE) == 0)
+ uma_zfree(zone_mbuf, m);
+ return (n);
+}
+
+static __inline void
+#ifndef VBOX
+m_clget(struct mbuf *m, int how)
+#else
+m_clget(PNATState pData, struct mbuf *m, int how)
+#endif
+{
+
+ if (m->m_flags & M_EXT)
+ printf("%s: %p mbuf already has cluster\n", __func__, m);
+ m->m_ext.ext_buf = (char *)NULL;
+ uma_zalloc_arg(zone_clust, m, how);
+ /*
+ * On a cluster allocation failure, drain the packet zone and retry,
+ * we might be able to loosen a few clusters up on the drain.
+ */
+ if ((how & M_NOWAIT) && (m->m_ext.ext_buf == NULL)) {
+ zone_drain(zone_pack);
+ uma_zalloc_arg(zone_clust, m, how);
+ }
+}
+
+/*
+ * m_cljget() is different from m_clget() as it can allocate clusters without
+ * attaching them to an mbuf. In that case the return value is the pointer
+ * to the cluster of the requested size. If an mbuf was specified, it gets
+ * the cluster attached to it and the return value can be safely ignored.
+ * For size it takes MCLBYTES, MJUMPAGESIZE, MJUM9BYTES, MJUM16BYTES.
+ */
+static __inline void *
+#ifndef VBOX
+m_cljget(struct mbuf *m, int how, int size)
+#else
+m_cljget(PNATState pData, struct mbuf *m, int how, int size)
+#endif
+{
+ uma_zone_t zone;
+
+ if (m && m->m_flags & M_EXT)
+ printf("%s: %p mbuf already has cluster\n", __func__, m);
+ if (m != NULL)
+ m->m_ext.ext_buf = NULL;
+
+#ifndef VBOX
+ zone = m_getzone(size);
+#else
+ zone = m_getzone(pData, size);
+#endif
+ return (uma_zalloc_arg(zone, m, how));
+}
+
+static __inline void
+#ifndef VBOX
+m_cljset(struct mbuf *m, void *cl, int type)
+#else
+m_cljset(PNATState pData, struct mbuf *m, void *cl, int type)
+#endif
+{
+ uma_zone_t zone;
+ int size;
+
+ switch (type) {
+ case EXT_CLUSTER:
+ size = MCLBYTES;
+ zone = zone_clust;
+ break;
+#if MJUMPAGESIZE != MCLBYTES
+ case EXT_JUMBOP:
+ size = MJUMPAGESIZE;
+ zone = zone_jumbop;
+ break;
+#endif
+ case EXT_JUMBO9:
+ size = MJUM9BYTES;
+ zone = zone_jumbo9;
+ break;
+ case EXT_JUMBO16:
+ size = MJUM16BYTES;
+ zone = zone_jumbo16;
+ break;
+ default:
+ panic("unknown cluster type");
+ break;
+ }
+
+ m->m_data = m->m_ext.ext_buf = cl;
+#ifdef VBOX
+ m->m_ext.ext_free = (void (*)(void *, void *))0;
+ m->m_ext.ext_args = NULL;
+#else
+ m->m_ext.ext_free = m->m_ext.ext_args = NULL;
+#endif
+ m->m_ext.ext_size = size;
+ m->m_ext.ext_type = type;
+ m->m_ext.ref_cnt = uma_find_refcnt(zone, cl);
+ m->m_flags |= M_EXT;
+
+}
+
+static __inline void
+m_chtype(struct mbuf *m, short new_type)
+{
+
+ m->m_type = new_type;
+}
+
+static __inline struct mbuf *
+m_last(struct mbuf *m)
+{
+
+ while (m->m_next)
+ m = m->m_next;
+ return (m);
+}
+
+/*
+ * mbuf, cluster, and external object allocation macros (for compatibility
+ * purposes).
+ */
+#define M_MOVE_PKTHDR(to, from) m_move_pkthdr((to), (from))
+#ifndef VBOX
+#define MGET(m, how, type) ((m) = m_get((how), (type)))
+#define MGETHDR(m, how, type) ((m) = m_gethdr((how), (type)))
+#define MCLGET(m, how) m_clget((m), (how))
+#define MEXTADD(m, buf, size, free, args, flags, type) \
+ m_extadd((m), (caddr_t)(buf), (size), (free), (args), (flags), (type))
+#define m_getm(m, len, how, type) \
+ m_getm2((m), (len), (how), (type), M_PKTHDR)
+#else /*!VBOX*/
+#define MGET(m, how, type) ((m) = m_get(pData, (how), (type)))
+#define MGETHDR(m, how, type) ((m) = m_gethdr(pData, (how), (type)))
+#define MCLGET(m, how) m_clget(pData, (m), (how))
+#define MEXTADD(m, buf, size, free, args, flags, type) \
+ m_extadd(pData, (m), (caddr_t)(buf), (size), (free), (args), (flags), (type))
+#define m_getm(m, len, how, type) \
+ m_getm2(pData, (m), (len), (how), (type), M_PKTHDR)
+#endif
+
+/*
+ * Evaluate TRUE if it's safe to write to the mbuf m's data region (this can
+ * be both the local data payload, or an external buffer area, depending on
+ * whether M_EXT is set).
+ */
+#define M_WRITABLE(m) (!((m)->m_flags & M_RDONLY) && \
+ (!(((m)->m_flags & M_EXT)) || \
+ (*((m)->m_ext.ref_cnt) == 1)) ) \
+
+/* Check if the supplied mbuf has a packet header, or else panic. */
+#define M_ASSERTPKTHDR(m) \
+ KASSERT(m != NULL && m->m_flags & M_PKTHDR, \
+ ("%s: no mbuf packet header!", __func__))
+
+/*
+ * Ensure that the supplied mbuf is a valid, non-free mbuf.
+ *
+ * XXX: Broken at the moment. Need some UMA magic to make it work again.
+ */
+#define M_ASSERTVALID(m) \
+ KASSERT((((struct mbuf *)m)->m_flags & 0) == 0, \
+ ("%s: attempted use of a free mbuf!", __func__))
+
+/*
+ * Set the m_data pointer of a newly-allocated mbuf (m_get/MGET) to place an
+ * object of the specified size at the end of the mbuf, longword aligned.
+ */
+#define M_ALIGN(m, len) do { \
+ KASSERT(!((m)->m_flags & (M_PKTHDR|M_EXT)), \
+ ("%s: M_ALIGN not normal mbuf", __func__)); \
+ KASSERT((m)->m_data == (m)->m_dat, \
+ ("%s: M_ALIGN not a virgin mbuf", __func__)); \
+ (m)->m_data += (MLEN - (len)) & ~(sizeof(long) - 1); \
+} while (0)
+
+/*
+ * As above, for mbufs allocated with m_gethdr/MGETHDR or initialized by
+ * M_DUP/MOVE_PKTHDR.
+ */
+#define MH_ALIGN(m, len) do { \
+ KASSERT((m)->m_flags & M_PKTHDR && !((m)->m_flags & M_EXT), \
+ ("%s: MH_ALIGN not PKTHDR mbuf", __func__)); \
+ KASSERT((m)->m_data == (m)->m_pktdat, \
+ ("%s: MH_ALIGN not a virgin mbuf", __func__)); \
+ (m)->m_data += (MHLEN - (len)) & ~(sizeof(long) - 1); \
+} while (0)
+
+/*
+ * Compute the amount of space available before the current start of data in
+ * an mbuf.
+ *
+ * The M_WRITABLE() is a temporary, conservative safety measure: the burden
+ * of checking writability of the mbuf data area rests solely with the caller.
+ */
+#define M_LEADINGSPACE(m) \
+ ((m)->m_flags & M_EXT ? \
+ (M_WRITABLE(m) ? (m)->m_data - (m)->m_ext.ext_buf : 0): \
+ (m)->m_flags & M_PKTHDR ? (m)->m_data - (m)->m_pktdat : \
+ (m)->m_data - (m)->m_dat)
+
+/*
+ * Compute the amount of space available after the end of data in an mbuf.
+ *
+ * The M_WRITABLE() is a temporary, conservative safety measure: the burden
+ * of checking writability of the mbuf data area rests solely with the caller.
+ */
+#define M_TRAILINGSPACE(m) \
+ ((m)->m_flags & M_EXT ? \
+ (M_WRITABLE(m) ? (m)->m_ext.ext_buf + (m)->m_ext.ext_size \
+ - ((m)->m_data + (m)->m_len) : 0) : \
+ &(m)->m_dat[MLEN] - ((m)->m_data + (m)->m_len))
+
+/*
+ * Arrange to prepend space of size plen to mbuf m. If a new mbuf must be
+ * allocated, how specifies whether to wait. If the allocation fails, the
+ * original mbuf chain is freed and m is set to NULL.
+ */
+#define M_PREPEND(m, plen, how) do { \
+ struct mbuf **_mmp = &(m); \
+ struct mbuf *_mm = *_mmp; \
+ int _mplen = (plen); \
+ int __mhow = (how); \
+ \
+ MBUF_CHECKSLEEP(how); \
+ if (M_LEADINGSPACE(_mm) >= _mplen) { \
+ _mm->m_data -= _mplen; \
+ _mm->m_len += _mplen; \
+ } else \
+ _mm = m_prepend(_mm, _mplen, __mhow); \
+ if (_mm != NULL && _mm->m_flags & M_PKTHDR) \
+ _mm->m_pkthdr.len += _mplen; \
+ *_mmp = _mm; \
+} while (0)
+
+/*
+ * Change mbuf to new type. This is a relatively expensive operation and
+ * should be avoided.
+ */
+#define MCHTYPE(m, t) m_chtype((m), (t))
+
+/* Length to m_copy to copy all. */
+#define M_COPYALL 1000000000
+
+/* Compatibility with 4.3. */
+#define m_copy(m, o, l) m_copym((m), (o), (l), M_DONTWAIT)
+
+extern int max_datalen; /* MHLEN - max_hdr */
+extern int max_hdr; /* Largest link + protocol header */
+extern int max_linkhdr; /* Largest link-level header */
+extern int max_protohdr; /* Largest protocol header */
+extern struct mbstat mbstat; /* General mbuf stats/infos */
+extern int nmbclusters; /* Maximum number of clusters */
+
+struct uio;
+
+void m_align(struct mbuf *, int);
+int m_apply(struct mbuf *, int, int,
+ int (*)(void *, void *, u_int), void *);
+#ifndef VBOX
+void m_adj(struct mbuf *, int);
+int m_append(struct mbuf *, int, c_caddr_t);
+struct mbuf *m_defrag(struct mbuf *, int);
+struct mbuf *m_dup(struct mbuf *, int);
+void m_cat(struct mbuf *, struct mbuf *);
+struct mbuf *m_collapse(struct mbuf *, int, int);
+void m_copyback(struct mbuf *, int, int, c_caddr_t);
+struct mbuf *m_copym(struct mbuf *, int, int, int);
+struct mbuf *m_copymdata(struct mbuf *, struct mbuf *,
+ int, int, int, int);
+struct mbuf *m_copypacket(struct mbuf *, int);
+struct mbuf *m_copyup(struct mbuf *n, int len, int dstoff);
+void m_extadd(struct mbuf *, caddr_t, u_int,
+ void (*)(void *, void *), void *, int, int);
+#else
+void m_adj(PNATState, struct mbuf *, int);
+int m_append(PNATState pData, struct mbuf *, int, c_caddr_t);
+struct mbuf *m_defrag(PNATState, struct mbuf *, int);
+struct mbuf *m_dup(PNATState, struct mbuf *, int);
+void m_cat(PNATState, struct mbuf *, struct mbuf *);
+struct mbuf *m_collapse(PNATState, struct mbuf *, int, int);
+void m_copyback(PNATState, struct mbuf *, int, int, c_caddr_t);
+struct mbuf *m_copym(PNATState, struct mbuf *, int, int, int);
+struct mbuf *m_copymdata(PNATState, struct mbuf *, struct mbuf *,
+ int, int, int, int);
+struct mbuf *m_copypacket(PNATState, struct mbuf *, int);
+struct mbuf *m_copyup(PNATState, struct mbuf *n, int len, int dstoff);
+void m_extadd(PNATState pData, struct mbuf *, caddr_t, u_int,
+ void (*)(void *, void *), void *, int, int);
+#endif
+void m_copydata(const struct mbuf *, int, int, caddr_t);
+void m_copy_pkthdr(struct mbuf *, struct mbuf *);
+void m_demote(struct mbuf *, int);
+struct mbuf *m_devget(char *, int, int, struct ifnet *,
+ void (*)(char *, caddr_t, u_int));
+int m_dup_pkthdr(struct mbuf *, struct mbuf *, int);
+u_int m_fixhdr(struct mbuf *);
+struct mbuf *m_fragment(struct mbuf *, int, int);
+#ifndef VBOX
+void m_freem(struct mbuf *);
+struct mbuf *m_getm2(struct mbuf *, int, int, short, int);
+struct mbuf *m_prepend(struct mbuf *, int, int);
+struct mbuf *m_pulldown(struct mbuf *, int, int, int *);
+struct mbuf *m_pullup(struct mbuf *, int);
+int m_sanity(struct mbuf *, int);
+struct mbuf *m_split(struct mbuf *, int, int);
+struct mbuf *m_unshare(struct mbuf *, int how);
+#else
+void m_freem(PNATState pData, struct mbuf *);
+struct mbuf *m_getm2(PNATState pData, struct mbuf *, int, int, short, int);
+struct mbuf *m_prepend(PNATState, struct mbuf *, int, int);
+struct mbuf *m_pulldown(PNATState, struct mbuf *, int, int, int *);
+struct mbuf *m_pullup(PNATState, struct mbuf *, int);
+int m_sanity(PNATState, struct mbuf *, int);
+struct mbuf *m_split(PNATState, struct mbuf *, int, int);
+struct mbuf *m_unshare(PNATState, struct mbuf *, int how);
+#endif
+struct mbuf *m_getptr(struct mbuf *, int, int *);
+u_int m_length(struct mbuf *, struct mbuf **);
+void m_move_pkthdr(struct mbuf *, struct mbuf *);
+void m_print(const struct mbuf *, int);
+struct mbuf *m_uiotombuf(struct uio *, int, int, int, int);
+
+/*-
+ * Network packets may have annotations attached by affixing a list of
+ * "packet tags" to the pkthdr structure. Packet tags are dynamically
+ * allocated semi-opaque data structures that have a fixed header
+ * (struct m_tag) that specifies the size of the memory block and a
+ * <cookie,type> pair that identifies it. The cookie is a 32-bit unique
+ * unsigned value used to identify a module or ABI. By convention this value
+ * is chosen as the date+time that the module is created, expressed as the
+ * number of seconds since the epoch (e.g., using date -u +'%s'). The type
+ * value is an ABI/module-specific value that identifies a particular
+ * annotation and is private to the module. For compatibility with systems
+ * like OpenBSD that define packet tags w/o an ABI/module cookie, the value
+ * PACKET_ABI_COMPAT is used to implement m_tag_get and m_tag_find
+ * compatibility shim functions and several tag types are defined below.
+ * Users that do not require compatibility should use a private cookie value
+ * so that packet tag-related definitions can be maintained privately.
+ *
+ * Note that the packet tag returned by m_tag_alloc has the default memory
+ * alignment implemented by malloc. To reference private data one can use a
+ * construct like:
+ *
+ * struct m_tag *mtag = m_tag_alloc(...);
+ * struct foo *p = (struct foo *)(mtag+1);
+ *
+ * if the alignment of struct m_tag is sufficient for referencing members of
+ * struct foo. Otherwise it is necessary to embed struct m_tag within the
+ * private data structure to insure proper alignment; e.g.,
+ *
+ * struct foo {
+ * struct m_tag tag;
+ * ...
+ * };
+ * struct foo *p = (struct foo *) m_tag_alloc(...);
+ * struct m_tag *mtag = &p->tag;
+ */
+
+/*
+ * Persistent tags stay with an mbuf until the mbuf is reclaimed. Otherwise
+ * tags are expected to ``vanish'' when they pass through a network
+ * interface. For most interfaces this happens normally as the tags are
+ * reclaimed when the mbuf is free'd. However in some special cases
+ * reclaiming must be done manually. An example is packets that pass through
+ * the loopback interface. Also, one must be careful to do this when
+ * ``turning around'' packets (e.g., icmp_reflect).
+ *
+ * To mark a tag persistent bit-or this flag in when defining the tag id.
+ * The tag will then be treated as described above.
+ */
+#define MTAG_PERSISTENT 0x800
+
+#define PACKET_TAG_NONE 0 /* Nadda */
+
+/* Packet tags for use with PACKET_ABI_COMPAT. */
+#define PACKET_TAG_IPSEC_IN_DONE 1 /* IPsec applied, in */
+#define PACKET_TAG_IPSEC_OUT_DONE 2 /* IPsec applied, out */
+#define PACKET_TAG_IPSEC_IN_CRYPTO_DONE 3 /* NIC IPsec crypto done */
+#define PACKET_TAG_IPSEC_OUT_CRYPTO_NEEDED 4 /* NIC IPsec crypto req'ed */
+#define PACKET_TAG_IPSEC_IN_COULD_DO_CRYPTO 5 /* NIC notifies IPsec */
+#define PACKET_TAG_IPSEC_PENDING_TDB 6 /* Reminder to do IPsec */
+#define PACKET_TAG_BRIDGE 7 /* Bridge processing done */
+#define PACKET_TAG_GIF 8 /* GIF processing done */
+#define PACKET_TAG_GRE 9 /* GRE processing done */
+#define PACKET_TAG_IN_PACKET_CHECKSUM 10 /* NIC checksumming done */
+#define PACKET_TAG_ENCAP 11 /* Encap. processing */
+#define PACKET_TAG_IPSEC_SOCKET 12 /* IPSEC socket ref */
+#define PACKET_TAG_IPSEC_HISTORY 13 /* IPSEC history */
+#define PACKET_TAG_IPV6_INPUT 14 /* IPV6 input processing */
+#define PACKET_TAG_DUMMYNET 15 /* dummynet info */
+#define PACKET_TAG_DIVERT 17 /* divert info */
+#define PACKET_TAG_IPFORWARD 18 /* ipforward info */
+#define PACKET_TAG_MACLABEL (19 | MTAG_PERSISTENT) /* MAC label */
+#define PACKET_TAG_PF 21 /* PF + ALTQ information */
+#define PACKET_TAG_RTSOCKFAM 25 /* rtsock sa family */
+#define PACKET_TAG_IPOPTIONS 27 /* Saved IP options */
+#define PACKET_TAG_CARP 28 /* CARP info */
+#ifdef VBOX
+# define PACKET_TAG_ALIAS 0xab01
+# define PACKET_TAG_ETHER 0xab02
+# define PACKET_SERVICE 0xab03
+#endif
+
+/* Specific cookies and tags. */
+
+/* Packet tag routines. */
+struct m_tag *m_tag_alloc(u_int32_t, int, int, int);
+void m_tag_delete(struct mbuf *, struct m_tag *);
+void m_tag_delete_chain(struct mbuf *, struct m_tag *);
+void m_tag_free_default(struct m_tag *);
+struct m_tag *m_tag_locate(struct mbuf *, u_int32_t, int, struct m_tag *);
+struct m_tag *m_tag_copy(struct m_tag *, int);
+int m_tag_copy_chain(struct mbuf *, struct mbuf *, int);
+void m_tag_delete_nonpersistent(struct mbuf *);
+
+/*
+ * Initialize the list of tags associated with an mbuf.
+ */
+static __inline void
+m_tag_init(struct mbuf *m)
+{
+
+ SLIST_INIT(&m->m_pkthdr.tags);
+}
+
+/*
+ * Set up the contents of a tag. Note that this does not fill in the free
+ * method; the caller is expected to do that.
+ *
+ * XXX probably should be called m_tag_init, but that was already taken.
+ */
+static __inline void
+m_tag_setup(struct m_tag *t, u_int32_t cookie, int type, int len)
+{
+
+ t->m_tag_id = type;
+ t->m_tag_len = len;
+ t->m_tag_cookie = cookie;
+}
+
+/*
+ * Reclaim resources associated with a tag.
+ */
+static __inline void
+m_tag_free(struct m_tag *t)
+{
+
+ (*t->m_tag_free)(t);
+}
+
+/*
+ * Return the first tag associated with an mbuf.
+ */
+static __inline struct m_tag *
+m_tag_first(struct mbuf *m)
+{
+
+ return (SLIST_FIRST(&m->m_pkthdr.tags));
+}
+
+/*
+ * Return the next tag in the list of tags associated with an mbuf.
+ */
+static __inline struct m_tag *
+m_tag_next(struct mbuf *m, struct m_tag *t)
+{
+ NOREF(m);
+ return (SLIST_NEXT(t, m_tag_link));
+}
+
+/*
+ * Prepend a tag to the list of tags associated with an mbuf.
+ */
+static __inline void
+m_tag_prepend(struct mbuf *m, struct m_tag *t)
+{
+
+ SLIST_INSERT_HEAD(&m->m_pkthdr.tags, t, m_tag_link);
+}
+
+/*
+ * Unlink a tag from the list of tags associated with an mbuf.
+ */
+static __inline void
+m_tag_unlink(struct mbuf *m, struct m_tag *t)
+{
+
+ SLIST_REMOVE(&m->m_pkthdr.tags, t, m_tag, m_tag_link);
+}
+
+/* These are for OpenBSD compatibility. */
+#define MTAG_ABI_COMPAT 0 /* compatibility ABI */
+
+static __inline struct m_tag *
+m_tag_get(int type, int length, int fWait)
+{
+ return (m_tag_alloc(MTAG_ABI_COMPAT, type, length, fWait));
+}
+
+static __inline struct m_tag *
+m_tag_find(struct mbuf *m, int type, struct m_tag *start)
+{
+ return (SLIST_EMPTY(&m->m_pkthdr.tags) ? (struct m_tag *)NULL :
+ m_tag_locate(m, MTAG_ABI_COMPAT, type, start));
+}
+
+/* XXX temporary FIB methods probably eventually use tags.*/
+#define M_FIBSHIFT 28
+#define M_FIBMASK 0x0F
+
+/* get the fib from an mbuf and if it is not set, return the default */
+#define M_GETFIB(_m) \
+ ((((_m)->m_flags & M_FIB) >> M_FIBSHIFT) & M_FIBMASK)
+
+#define M_SETFIB(_m, _fib) do { \
+ _m->m_flags &= ~M_FIB; \
+ _m->m_flags |= (((_fib) << M_FIBSHIFT) & M_FIB); \
+} while (0)
+
+#endif /* _KERNEL */
+
+#endif /* !_SYS_MBUF_H_ */
diff --git a/src/VBox/Devices/Network/slirp/bsd/sys/sbuf.h b/src/VBox/Devices/Network/slirp/bsd/sys/sbuf.h
new file mode 100644
index 00000000..3f59c46f
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/bsd/sys/sbuf.h
@@ -0,0 +1,95 @@
+/*-
+ * Copyright (c) 2000 Poul-Henning Kamp and Dag-Erling Coïdan Smørgrav
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer
+ * in this position and unchanged.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
+ * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
+ * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $FreeBSD: src/sys/sys/sbuf.h,v 1.14.18.1.2.1 2009/04/15 03:14:26 kensmith Exp $
+ */
+
+#ifndef _SYS_SBUF_H_
+#define _SYS_SBUF_H_
+
+#ifndef VBOX
+#include <sys/_types.h>
+#else
+# include <iprt/types.h>
+#endif
+
+/*
+ * Structure definition
+ */
+struct sbuf {
+ char *s_buf; /* storage buffer */
+ void *s_unused; /* binary compatibility. */
+ int s_size; /* size of storage buffer */
+ int s_len; /* current length of string */
+#define SBUF_FIXEDLEN 0x00000000 /* fixed length buffer (default) */
+#define SBUF_AUTOEXTEND 0x00000001 /* automatically extend buffer */
+#define SBUF_USRFLAGMSK 0x0000ffff /* mask of flags the user may specify */
+#define SBUF_DYNAMIC 0x00010000 /* s_buf must be freed */
+#define SBUF_FINISHED 0x00020000 /* set by sbuf_finish() */
+#define SBUF_OVERFLOWED 0x00040000 /* sbuf overflowed */
+#define SBUF_DYNSTRUCT 0x00080000 /* sbuf must be freed */
+ int s_flags; /* flags */
+};
+
+__BEGIN_DECLS
+/*
+ * API functions
+ */
+struct sbuf *sbuf_new(struct sbuf *, char *, int, int);
+#define sbuf_new_auto() \
+ sbuf_new(NULL, NULL, 0, SBUF_AUTOEXTEND)
+void sbuf_clear(struct sbuf *);
+int sbuf_setpos(struct sbuf *, int);
+int sbuf_bcat(struct sbuf *, const void *, size_t);
+int sbuf_bcpy(struct sbuf *, const void *, size_t);
+int sbuf_cat(struct sbuf *, const char *);
+int sbuf_cpy(struct sbuf *, const char *);
+#ifndef VBOX
+int sbuf_printf(struct sbuf *, const char *, ...) __printflike(2, 3);
+int sbuf_vprintf(struct sbuf *, const char *, __va_list) __printflike(2, 0);
+#else
+int sbuf_printf(struct sbuf *, const char *, ...);
+int sbuf_vprintf(struct sbuf *, const char *, va_list);
+#endif
+int sbuf_putc(struct sbuf *, int);
+int sbuf_trim(struct sbuf *);
+int sbuf_overflowed(struct sbuf *);
+void sbuf_finish(struct sbuf *);
+char *sbuf_data(struct sbuf *);
+int sbuf_len(struct sbuf *);
+int sbuf_done(struct sbuf *);
+void sbuf_delete(struct sbuf *);
+
+#ifdef _KERNEL
+struct uio;
+struct sbuf *sbuf_uionew(struct sbuf *, struct uio *, int *);
+int sbuf_bcopyin(struct sbuf *, const void *, size_t);
+int sbuf_copyin(struct sbuf *, const void *, size_t);
+#endif
+__END_DECLS
+
+#endif
diff --git a/src/VBox/Devices/Network/slirp/cksum.c b/src/VBox/Devices/Network/slirp/cksum.c
new file mode 100644
index 00000000..9e15fd89
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/cksum.c
@@ -0,0 +1,174 @@
+/* $Id: cksum.c $ */
+/** @file
+ * NAT - IP checksum generation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1988, 1992, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)in_cksum.c 8.1 (Berkeley) 6/10/93
+ * in_cksum.c,v 1.2 1994/08/02 07:48:16 davidg Exp
+ */
+
+#include <slirp.h>
+
+/*
+ * Checksum routine for Internet Protocol family headers (Portable Version).
+ *
+ * This routine is very heavily used in the network
+ * code and should be modified for each CPU to be as fast as possible.
+ *
+ * XXX Since we will never span more than 1 mbuf, we can optimise this
+ */
+
+#define ADDCARRY(x) (x > 65535 ? x -= 65535 : x)
+#define REDUCE { l_util.l = sum; sum = l_util.s[0] + l_util.s[1]; ADDCARRY(sum); }
+
+int cksum(struct mbuf *m, int len)
+{
+ register u_int16_t *w;
+ register int sum = 0;
+ register int mlen = 0;
+ int byte_swapped = 0;
+
+ union
+ {
+ u_int8_t c[2];
+ u_int16_t s;
+ } s_util;
+ union
+ {
+ u_int16_t s[2];
+ u_int32_t l;
+ } l_util;
+
+ if (m->m_len == 0)
+ goto cont;
+ w = mtod(m, u_int16_t *);
+
+ mlen = m->m_len;
+
+ if (len < mlen)
+ mlen = len;
+ len -= mlen;
+ /*
+ * Force to even boundary.
+ */
+ if ((1 & (long) w) && (mlen > 0))
+ {
+ REDUCE;
+ sum <<= 8;
+ s_util.c[0] = *(u_int8_t *)w;
+ w = (u_int16_t *)((int8_t *)w + 1);
+ mlen--;
+ byte_swapped = 1;
+ }
+ /*
+ * Unroll the loop to make overhead from
+ * branches &c small.
+ */
+ while ((mlen -= 32) >= 0)
+ {
+ sum += w[ 0]; sum += w[ 1]; sum += w[ 2]; sum += w[ 3];
+ sum += w[ 4]; sum += w[ 5]; sum += w[ 6]; sum += w[ 7];
+ sum += w[ 8]; sum += w[ 9]; sum += w[10]; sum += w[11];
+ sum += w[12]; sum += w[13]; sum += w[14]; sum += w[15];
+ w += 16;
+ }
+ mlen += 32;
+ while ((mlen -= 8) >= 0)
+ {
+ sum += w[0]; sum += w[1]; sum += w[2]; sum += w[3];
+ w += 4;
+ }
+ mlen += 8;
+ if (mlen == 0 && byte_swapped == 0)
+ goto cont;
+ REDUCE;
+ while ((mlen -= 2) >= 0)
+ {
+ sum += *w++;
+ }
+
+ if (byte_swapped)
+ {
+ REDUCE;
+ sum <<= 8;
+ byte_swapped = 0;
+ if (mlen == -1)
+ {
+ s_util.c[1] = *(u_int8_t *)w;
+ sum += s_util.s;
+ mlen = 0;
+ }
+ else
+ mlen = -1;
+ }
+ else if (mlen == -1)
+ s_util.c[0] = *(u_int8_t *)w;
+
+cont:
+#ifdef DEBUG
+ if (len)
+ Log(("cksum: out of data: len = %d\n", len));
+#endif
+ if (mlen == -1)
+ {
+ /* The last mbuf has odd # of bytes. Follow the
+ standard (the odd byte may be shifted left by 8 bits
+ or not as determined by endian-ness of the machine) */
+ s_util.c[1] = 0;
+ sum += s_util.s;
+ }
+ REDUCE;
+ return (~sum & 0xffff);
+}
diff --git a/src/VBox/Devices/Network/slirp/counters.h b/src/VBox/Devices/Network/slirp/counters.h
new file mode 100644
index 00000000..7caae948
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/counters.h
@@ -0,0 +1,149 @@
+/** $Id: counters.h $ */
+/** @file
+ * Counters macro invocation template.
+ *
+ * This is included with different PROFILE_COUNTER and COUNTING_COUNTER
+ * implementations to instantiate data members, create function prototypes and
+ * implement these prototypes.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * COUNTERS_INIT is used before using counters.h to declare helping macro
+ * definitions for (de-)registering counters
+ */
+#ifndef COUNTERS_H
+# define COUNTERS_H
+# if defined(VBOX_WITH_STATISTICS)
+# define REGISTER_COUNTER(name, storage, type, units, dsc) \
+ do { \
+ PDMDrvHlpSTAMRegisterF(pDrvIns, \
+ &(storage)->Stat ## name, \
+ type, \
+ STAMVISIBILITY_ALWAYS, \
+ units, \
+ dsc, \
+ "/Drivers/NAT%u/" #name, \
+ pDrvIns->iInstance); \
+ } while (0)
+# define DEREGISTER_COUNTER(name, storage) PDMDrvHlpSTAMDeregister(pDrvIns, &(storage)->Stat ## name)
+# else
+# define REGISTER_COUNTER(name, storage, type, units, dsc) do {} while (0)
+# define DEREGISTER_COUNTER(name, storage) do {} while (0)
+# endif
+#else
+# undef COUNTERS_INIT
+#endif
+
+#ifndef COUNTERS_INIT
+# if !defined(PROFILE_COUNTER) && !defined(DRV_PROFILE_COUNTER)
+# error (DRV_)PROFILE_COUNTER is not defied
+# endif
+# if !defined(COUNTING_COUNTER) && !defined(DRV_COUNTING_COUNTER)
+# error (DRV_)COUNTING_COUNTER is not defined
+# endif
+
+/*
+ * DRV_ prefixed are counters used in DrvNAT the rest are used in Slirp
+ */
+
+# if defined(PROFILE_COUNTER) || defined(COUNTING_COUNTER)
+PROFILE_COUNTER(Fill, "Profiling slirp fills");
+PROFILE_COUNTER(Poll, "Profiling slirp polls");
+PROFILE_COUNTER(FastTimer, "Profiling slirp fast timer");
+PROFILE_COUNTER(SlowTimer, "Profiling slirp slow timer");
+PROFILE_COUNTER(IOwrite, "Profiling IO sowrite");
+PROFILE_COUNTER(IOread, "Profiling IO soread");
+
+COUNTING_COUNTER(TCP, "TCP sockets");
+COUNTING_COUNTER(TCPHot, "TCP sockets active");
+COUNTING_COUNTER(UDP, "UDP sockets");
+COUNTING_COUNTER(UDPHot, "UDP sockets active");
+
+COUNTING_COUNTER(IORead_in_1, "SB IORead_in_1");
+COUNTING_COUNTER(IORead_in_1_bytes, "SB IORead_in_1_bytes");
+COUNTING_COUNTER(IORead_in_2, "SB IORead_in_2");
+COUNTING_COUNTER(IORead_in_2_1st_bytes, "SB IORead_in_2_1st_bytes");
+COUNTING_COUNTER(IORead_in_2_2nd_bytes, "SB IORead_in_2_2nd_bytes");
+COUNTING_COUNTER(IOWrite_in_1, "SB IOWrite_in_1");
+COUNTING_COUNTER(IOWrite_in_1_bytes, "SB IOWrite_in_1_bytes");
+COUNTING_COUNTER(IOWrite_in_2, "SB IOWrite_in_2");
+COUNTING_COUNTER(IOWrite_in_2_1st_bytes, "SB IOWrite_in_2_1st_bytes");
+COUNTING_COUNTER(IOWrite_in_2_2nd_bytes, "SB IOWrite_in_2_2nd_bytes");
+COUNTING_COUNTER(IOWrite_no_w, "SB IOWrite_no_w");
+COUNTING_COUNTER(IOWrite_rest, "SB IOWrite_rest");
+COUNTING_COUNTER(IOWrite_rest_bytes, "SB IOWrite_rest_bytes");
+
+PROFILE_COUNTER(IOSBAppend_pf, "Profiling sbuf::append common");
+PROFILE_COUNTER(IOSBAppend_pf_wa, "Profiling sbuf::append all writen in network");
+PROFILE_COUNTER(IOSBAppend_pf_wf, "Profiling sbuf::append writen fault");
+PROFILE_COUNTER(IOSBAppend_pf_wp, "Profiling sbuf::append writen partly");
+COUNTING_COUNTER(IOSBAppend, "SB: Append total");
+COUNTING_COUNTER(IOSBAppend_wa, "SB: Append all is written to network ");
+COUNTING_COUNTER(IOSBAppend_wf, "SB: Append nothing is written");
+COUNTING_COUNTER(IOSBAppend_wp, "SB: Append is written partly");
+COUNTING_COUNTER(IOSBAppend_zm, "SB: Append mbuf is zerro or less");
+
+COUNTING_COUNTER(IOSBAppendSB, "SB: AppendSB total");
+COUNTING_COUNTER(IOSBAppendSB_w_l_r, "SB: AppendSB (sb_wptr < sb_rptr)");
+COUNTING_COUNTER(IOSBAppendSB_w_ge_r, "SB: AppendSB (sb_wptr >= sb_rptr)");
+COUNTING_COUNTER(IOSBAppendSB_w_alter, "SB: AppendSB (altering of sb_wptr)");
+COUNTING_COUNTER(MBufAllocation,"MBUF::shows number of mbufs in used list");
+
+COUNTING_COUNTER(TCP_retransmit, "TCP::retransmit");
+
+PROFILE_COUNTER(TCP_reassamble, "TCP::reasamble");
+PROFILE_COUNTER(TCP_input, "TCP::input");
+PROFILE_COUNTER(IP_input, "IP::input");
+PROFILE_COUNTER(IP_output, "IP::output");
+PROFILE_COUNTER(IF_encap, "IF::encap");
+PROFILE_COUNTER(ALIAS_input, "ALIAS::input");
+PROFILE_COUNTER(ALIAS_output, "ALIAS::output");
+
+# else
+/*DrvNAT.cpp*/
+DRV_COUNTING_COUNTER(NATRecvWakeups, "counting wakeups of NAT RX thread");
+DRV_PROFILE_COUNTER(NATRecv,"Time spent in NATRecv worker");
+DRV_PROFILE_COUNTER(NATRecvWait,"Time spent in NATRecv worker in waiting of free RX buffers");
+DRV_COUNTING_COUNTER(QueuePktSent, "counting packet sent via PDM Queue");
+DRV_COUNTING_COUNTER(QueuePktDropped, "counting packet drops by PDM Queue");
+DRV_COUNTING_COUNTER(ConsumerFalse, "counting consumer's reject number to process the queue's item");
+# endif
+#endif /*!COUNTERS_INIT*/
+
+#ifdef DRV_COUNTING_COUNTER
+# undef DRV_COUNTING_COUNTER
+#endif
+
+#ifdef DRV_PROFILE_COUNTER
+# undef DRV_PROFILE_COUNTER
+#endif
+
+#ifdef COUNTING_COUNTER
+# undef COUNTING_COUNTER
+#endif
+
+#ifdef PROFILE_COUNTER
+# undef PROFILE_COUNTER
+#endif
diff --git a/src/VBox/Devices/Network/slirp/ctl.h b/src/VBox/Devices/Network/slirp/ctl.h
new file mode 100644
index 00000000..70fc4d3f
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/ctl.h
@@ -0,0 +1,52 @@
+/* $Id: ctl.h $ */
+/** @file
+ * NAT - IP subnet constants.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef _SLIRP_CTL_H_
+#define _SLIRP_CTL_H_
+
+#define CTL_CMD 0
+#define CTL_EXEC 1
+#define CTL_ALIAS 2
+#define CTL_DNS 3
+#define CTL_TFTP 4
+#define CTL_GUEST 15
+#define CTL_BROADCAST 255
+
+
+#define CTL_CHECK_NETWORK(x) (((x) & RT_H2N_U32(pData->netmask)) == pData->special_addr.s_addr)
+
+#define CTL_CHECK(x, ctl) ( ((RT_N2H_U32((x)) & ~pData->netmask) == (ctl)) \
+ && CTL_CHECK_NETWORK(x))
+
+#define CTL_CHECK_MINE(x) ( CTL_CHECK(x, CTL_ALIAS) \
+ || CTL_CHECK(x, CTL_DNS) \
+ || CTL_CHECK(x, CTL_TFTP))
+
+#define CTL_CHECK_BROADCAST(x) CTL_CHECK((x), ~pData->netmask)
+
+
+#endif /* _SLIRP_CTL_H_ */
diff --git a/src/VBox/Devices/Network/slirp/debug.c b/src/VBox/Devices/Network/slirp/debug.c
new file mode 100644
index 00000000..81426436
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/debug.c
@@ -0,0 +1,679 @@
+/* $Id: debug.c $ */
+/** @file
+ * NAT - debug helpers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1995 Danny Gasparovski.
+ * Portions copyright (c) 2000 Kelly Price.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/critsect.h>
+#include "zone.h"
+
+#ifdef DEBUG
+void dump_packet(void *, int);
+#endif
+
+#ifndef STRINGIFY
+# define STRINGIFY(x) #x
+#endif
+
+static char *g_apszTcpStates[TCP_NSTATES] =
+{
+ STRINGIFY(TCPS_CLOSED),
+ STRINGIFY(TCPS_LISTEN),
+ STRINGIFY(TCPS_SYN_SENT),
+ STRINGIFY(TCPS_SYN_RECEIVED),
+ STRINGIFY(TCPS_ESTABLISHED),
+ STRINGIFY(TCPS_CLOSE_WAIT),
+ STRINGIFY(TCPS_FIN_WAIT_1),
+ STRINGIFY(TCPS_CLOSING),
+ STRINGIFY(TCPS_LAST_ACK),
+ STRINGIFY(TCPS_FIN_WAIT_2),
+ STRINGIFY(TCPS_TIME_WAIT)
+};
+
+typedef struct DEBUGSTRSOCKETSTATE
+{
+ uint32_t u32SocketState;
+ const char *pcszSocketStateName;
+} DEBUGSTRSOCKETSTATE;
+
+#define DEBUGSTRSOCKETSTATE_HELPER(x) {(x), #x}
+
+static DEBUGSTRSOCKETSTATE g_apszSocketStates[8] =
+{
+ DEBUGSTRSOCKETSTATE_HELPER(SS_NOFDREF),
+ DEBUGSTRSOCKETSTATE_HELPER(SS_ISFCONNECTING),
+ DEBUGSTRSOCKETSTATE_HELPER(SS_ISFCONNECTED),
+ DEBUGSTRSOCKETSTATE_HELPER(SS_FCANTRCVMORE),
+ DEBUGSTRSOCKETSTATE_HELPER(SS_FCANTSENDMORE),
+ DEBUGSTRSOCKETSTATE_HELPER(SS_FWDRAIN),
+ DEBUGSTRSOCKETSTATE_HELPER(SS_FACCEPTCONN),
+ DEBUGSTRSOCKETSTATE_HELPER(SS_FACCEPTONCE),
+};
+
+static DEBUGSTRSOCKETSTATE g_aTcpFlags[] =
+{
+ DEBUGSTRSOCKETSTATE_HELPER(TH_FIN),
+ DEBUGSTRSOCKETSTATE_HELPER(TH_SYN),
+ DEBUGSTRSOCKETSTATE_HELPER(TH_RST),
+ DEBUGSTRSOCKETSTATE_HELPER(TH_PUSH),
+ DEBUGSTRSOCKETSTATE_HELPER(TH_ACK),
+ DEBUGSTRSOCKETSTATE_HELPER(TH_URG),
+};
+
+/*
+ * Dump a packet in the same format as tcpdump -x
+ */
+#ifdef DEBUG
+void
+dump_packet(void *dat, int n)
+{
+ Log(("nat: PACKET DUMPED:\n%.*Rhxd\n", n, dat));
+}
+#endif
+
+#ifdef LOG_ENABLED
+static void
+lprint(const char *pszFormat, ...)
+{
+ va_list args;
+ va_start(args, pszFormat);
+ RTLogPrintfV(pszFormat, args);
+ va_end(args);
+}
+
+void
+ipstats(PNATState pData)
+{
+ lprint("\n");
+
+ lprint("IP stats:\n");
+ lprint(" %6d total packets received (%d were unaligned)\n",
+ ipstat.ips_total, ipstat.ips_unaligned);
+ lprint(" %6d with incorrect version\n", ipstat.ips_badvers);
+ lprint(" %6d with bad header checksum\n", ipstat.ips_badsum);
+ lprint(" %6d with length too short (len < sizeof(iphdr))\n", ipstat.ips_tooshort);
+ lprint(" %6d with length too small (len < ip->len)\n", ipstat.ips_toosmall);
+ lprint(" %6d with bad header length\n", ipstat.ips_badhlen);
+ lprint(" %6d with bad packet length\n", ipstat.ips_badlen);
+ lprint(" %6d fragments received\n", ipstat.ips_fragments);
+ lprint(" %6d fragments dropped\n", ipstat.ips_fragdropped);
+ lprint(" %6d fragments timed out\n", ipstat.ips_fragtimeout);
+ lprint(" %6d packets reassembled ok\n", ipstat.ips_reassembled);
+ lprint(" %6d outgoing packets fragmented\n", ipstat.ips_fragmented);
+ lprint(" %6d total outgoing fragments\n", ipstat.ips_ofragments);
+ lprint(" %6d with bad protocol field\n", ipstat.ips_noproto);
+ lprint(" %6d total packets delivered\n", ipstat.ips_delivered);
+}
+
+void
+tcpstats(PNATState pData)
+{
+ lprint("\n");
+
+ lprint("TCP stats:\n");
+
+ lprint(" %6d packets sent\n", tcpstat.tcps_sndtotal);
+ lprint(" %6d data packets (%d bytes)\n",
+ tcpstat.tcps_sndpack, tcpstat.tcps_sndbyte);
+ lprint(" %6d data packets retransmitted (%d bytes)\n",
+ tcpstat.tcps_sndrexmitpack, tcpstat.tcps_sndrexmitbyte);
+ lprint(" %6d ack-only packets (%d delayed)\n",
+ tcpstat.tcps_sndacks, tcpstat.tcps_delack);
+ lprint(" %6d URG only packets\n", tcpstat.tcps_sndurg);
+ lprint(" %6d window probe packets\n", tcpstat.tcps_sndprobe);
+ lprint(" %6d window update packets\n", tcpstat.tcps_sndwinup);
+ lprint(" %6d control (SYN/FIN/RST) packets\n", tcpstat.tcps_sndctrl);
+ lprint(" %6d times tcp_output did nothing\n", tcpstat.tcps_didnuttin);
+
+ lprint(" %6d packets received\n", tcpstat.tcps_rcvtotal);
+ lprint(" %6d acks (for %d bytes)\n",
+ tcpstat.tcps_rcvackpack, tcpstat.tcps_rcvackbyte);
+ lprint(" %6d duplicate acks\n", tcpstat.tcps_rcvdupack);
+ lprint(" %6d acks for unsent data\n", tcpstat.tcps_rcvacktoomuch);
+ lprint(" %6d packets received in sequence (%d bytes)\n",
+ tcpstat.tcps_rcvpack, tcpstat.tcps_rcvbyte);
+ lprint(" %6d completely duplicate packets (%d bytes)\n",
+ tcpstat.tcps_rcvduppack, tcpstat.tcps_rcvdupbyte);
+
+ lprint(" %6d packets with some duplicate data (%d bytes duped)\n",
+ tcpstat.tcps_rcvpartduppack, tcpstat.tcps_rcvpartdupbyte);
+ lprint(" %6d out-of-order packets (%d bytes)\n",
+ tcpstat.tcps_rcvoopack, tcpstat.tcps_rcvoobyte);
+ lprint(" %6d packets of data after window (%d bytes)\n",
+ tcpstat.tcps_rcvpackafterwin, tcpstat.tcps_rcvbyteafterwin);
+ lprint(" %6d window probes\n", tcpstat.tcps_rcvwinprobe);
+ lprint(" %6d window update packets\n", tcpstat.tcps_rcvwinupd);
+ lprint(" %6d packets received after close\n", tcpstat.tcps_rcvafterclose);
+ lprint(" %6d discarded for bad checksums\n", tcpstat.tcps_rcvbadsum);
+ lprint(" %6d discarded for bad header offset fields\n",
+ tcpstat.tcps_rcvbadoff);
+
+ lprint(" %6d connection requests\n", tcpstat.tcps_connattempt);
+ lprint(" %6d connection accepts\n", tcpstat.tcps_accepts);
+ lprint(" %6d connections established (including accepts)\n", tcpstat.tcps_connects);
+ lprint(" %6d connections closed (including %d drop)\n",
+ tcpstat.tcps_closed, tcpstat.tcps_drops);
+ lprint(" %6d embryonic connections dropped\n", tcpstat.tcps_conndrops);
+ lprint(" %6d segments we tried to get rtt (%d succeeded)\n",
+ tcpstat.tcps_segstimed, tcpstat.tcps_rttupdated);
+ lprint(" %6d retransmit timeouts\n", tcpstat.tcps_rexmttimeo);
+ lprint(" %6d connections dropped by rxmt timeout\n",
+ tcpstat.tcps_timeoutdrop);
+ lprint(" %6d persist timeouts\n", tcpstat.tcps_persisttimeo);
+ lprint(" %6d keepalive timeouts\n", tcpstat.tcps_keeptimeo);
+ lprint(" %6d keepalive probes sent\n", tcpstat.tcps_keepprobe);
+ lprint(" %6d connections dropped by keepalive\n", tcpstat.tcps_keepdrops);
+ lprint(" %6d correct ACK header predictions\n", tcpstat.tcps_predack);
+ lprint(" %6d correct data packet header predictions\n", tcpstat.tcps_preddat);
+ lprint(" %6d TCP cache misses\n", tcpstat.tcps_socachemiss);
+
+/* lprint(" Packets received too short: %d\n", tcpstat.tcps_rcvshort); */
+/* lprint(" Segments dropped due to PAWS: %d\n", tcpstat.tcps_pawsdrop); */
+
+}
+
+void
+udpstats(PNATState pData)
+{
+ lprint("\n");
+
+ lprint("UDP stats:\n");
+ lprint(" %6d datagrams received\n", udpstat.udps_ipackets);
+ lprint(" %6d with packets shorter than header\n", udpstat.udps_hdrops);
+ lprint(" %6d with bad checksums\n", udpstat.udps_badsum);
+ lprint(" %6d with data length larger than packet\n", udpstat.udps_badlen);
+ lprint(" %6d UDP socket cache misses\n", udpstat.udpps_pcbcachemiss);
+ lprint(" %6d datagrams sent\n", udpstat.udps_opackets);
+}
+
+void
+icmpstats(PNATState pData)
+{
+ lprint("\n");
+ lprint("ICMP stats:\n");
+ lprint(" %6d ICMP packets received\n", icmpstat.icps_received);
+ lprint(" %6d were too short\n", icmpstat.icps_tooshort);
+ lprint(" %6d with bad checksums\n", icmpstat.icps_checksum);
+ lprint(" %6d with type not supported\n", icmpstat.icps_notsupp);
+ lprint(" %6d with bad type feilds\n", icmpstat.icps_badtype);
+ lprint(" %6d ICMP packets sent in reply\n", icmpstat.icps_reflect);
+}
+
+void
+mbufstats(PNATState pData)
+{
+ /*
+ * (vvl) this static code can't work with mbuf zone anymore
+ * @todo: make statistic correct
+ */
+ NOREF(pData);
+}
+
+void
+sockstats(PNATState pData)
+{
+ char buff[256];
+ size_t n;
+ struct socket *so, *so_next;
+
+ lprint("\n");
+
+ lprint(
+ "Proto[state] Sock Local Address, Port Remote Address, Port RecvQ SendQ\n");
+
+ QSOCKET_FOREACH(so, so_next, tcp)
+ /* { */
+ n = RTStrPrintf(buff, sizeof(buff), "tcp[%s]", so->so_tcpcb?tcpstates[so->so_tcpcb->t_state]:"NONE");
+ while (n < 17)
+ buff[n++] = ' ';
+ buff[17] = 0;
+ lprint("%s %3d %15s %5d ",
+ buff, so->s, inet_ntoa(so->so_laddr), RT_N2H_U16(so->so_lport));
+ lprint("%15s %5d %5d %5d\n",
+ inet_ntoa(so->so_faddr), RT_N2H_U16(so->so_fport),
+ SBUF_LEN(&so->so_rcv), SBUF_LEN(&so->so_snd));
+ LOOP_LABEL(tcp, so, so_next);
+ }
+
+ QSOCKET_FOREACH(so, so_next, udp)
+ /* { */
+ n = RTStrPrintf(buff, sizeof(buff), "udp[%d sec]", (so->so_expire - curtime) / 1000);
+ while (n < 17)
+ buff[n++] = ' ';
+ buff[17] = 0;
+ lprint("%s %3d %15s %5d ",
+ buff, so->s, inet_ntoa(so->so_laddr), RT_N2H_U16(so->so_lport));
+ lprint("%15s %5d %5d %5d\n",
+ inet_ntoa(so->so_faddr), RT_N2H_U16(so->so_fport),
+ SBUF_LEN(&so->so_rcv), SBUF_LEN(&so->so_snd));
+ LOOP_LABEL(udp, so, so_next);
+ }
+}
+#endif
+
+static DECLCALLBACK(size_t)
+printSocket(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+ const char *pszType, void const *pvValue,
+ int cchWidth, int cchPrecision, unsigned fFlags,
+ void *pvUser)
+{
+ struct socket *so = (struct socket*)pvValue;
+ PNATState pData = (PNATState)pvUser;
+ size_t cb = 0;
+
+ NOREF(cchWidth);
+ NOREF(cchPrecision);
+ NOREF(fFlags);
+ Assert(pData);
+
+ AssertReturn(strcmp(pszType, "natsock") == 0, 0);
+
+ if (so == NULL)
+ return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
+ "socket is null");
+ if (so->s == -1)
+ return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
+ "socket(%d)", so->s);
+
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
+ "socket %d", so->s);
+
+ if (so->so_type == IPPROTO_TCP)
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
+ " (tcp)");
+ else if (so->so_type == IPPROTO_UDP)
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
+ " (udp)");
+ else
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
+ " (proto %u)", so->so_type);
+
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
+ " exp. in %d"
+ " state=%R[natsockstate]"
+ "%s" /* fUnderPolling */
+ "%s" /* fShouldBeRemoved */
+ " f_(addr:port)=%RTnaipv4:%d"
+ " l_(addr:port)=%RTnaipv4:%d",
+ so->so_expire ? so->so_expire - curtime : 0,
+ so->so_state,
+ so->fUnderPolling ? " fUnderPolling" : "",
+ so->fShouldBeRemoved ? " fShouldBeRemoved" : "",
+ so->so_faddr.s_addr,
+ RT_N2H_U16(so->so_fport),
+ so->so_laddr.s_addr,
+ RT_N2H_U16(so->so_lport));
+
+ if (so->s != -1)
+ {
+ struct sockaddr addr;
+ socklen_t socklen;
+ int status;
+
+ socklen = sizeof(addr);
+ status = getsockname(so->s, &addr, &socklen);
+
+ if (status != 0)
+ {
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
+ " (getsockname failed)");
+ }
+ else if (addr.sa_family != AF_INET)
+ {
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
+ " (unexpected address family %d)",
+ addr.sa_family);
+ }
+ else
+ {
+ struct sockaddr_in *in_addr = (struct sockaddr_in *)&addr;
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0,
+ " name=%RTnaipv4:%d",
+ in_addr->sin_addr.s_addr,
+ RT_N2H_U16(in_addr->sin_port));
+ }
+ }
+ return cb;
+}
+
+static DECLCALLBACK(size_t)
+printNATSocketState(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+ const char *pszType, void const *pvValue,
+ int cchWidth, int cchPrecision, unsigned fFlags,
+ void *pvUser)
+{
+ uint32_t u32SocketState = (uint32_t)(uintptr_t)pvValue;
+ int idxNATState = 0;
+ bool fFirst = true;
+ size_t cbReturn = 0;
+ NOREF(cchWidth);
+ NOREF(cchPrecision);
+ NOREF(fFlags);
+ NOREF(pvUser);
+ AssertReturn(strcmp(pszType, "natsockstate") == 0, 0);
+
+ for (idxNATState = 0; idxNATState < RT_ELEMENTS(g_apszSocketStates); ++idxNATState)
+ {
+ if (u32SocketState & g_apszSocketStates[idxNATState].u32SocketState)
+ {
+ if (fFirst)
+ {
+ cbReturn += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, g_apszSocketStates[idxNATState].pcszSocketStateName);
+ fFirst = false;
+ }
+ else
+ cbReturn += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "|%s", g_apszSocketStates[idxNATState].pcszSocketStateName);
+ }
+ }
+
+ if (!cbReturn)
+ return RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "[unknown state %RX32]", u32SocketState);
+
+ return cbReturn;
+}
+
+/**
+ * Print callback dumping TCP Control Block in terms of RFC 793.
+ */
+static DECLCALLBACK(size_t)
+printTcpcbRfc793(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+ const char *pszType, void const *pvValue,
+ int cchWidth, int cchPrecision, unsigned fFlags,
+ void *pvUser)
+{
+ size_t cb = 0;
+ const struct tcpcb *tp = (const struct tcpcb *)pvValue;
+ NOREF(cchWidth);
+ NOREF(cchPrecision);
+ NOREF(fFlags);
+ NOREF(pvUser);
+ AssertReturn(RTStrCmp(pszType, "tcpcb793") == 0, 0);
+ if (tp)
+ {
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "TCB793[ state:%R[tcpstate] SND(UNA: %x, NXT: %x, UP: %x, WND: %x, WL1:%x, WL2:%x, ISS:%x), ",
+ tp->t_state, tp->snd_una, tp->snd_nxt, tp->snd_up, tp->snd_wnd, tp->snd_wl1, tp->snd_wl2, tp->iss);
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "RCV(WND: %x, NXT: %x, UP: %x, IRS:%x)]", tp->rcv_wnd, tp->rcv_nxt, tp->rcv_up, tp->irs);
+ }
+ else
+ {
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "TCB793[ NULL ]");
+ }
+ return cb;
+}
+/*
+ * Prints TCP segment in terms of RFC 793.
+ */
+static DECLCALLBACK(size_t)
+printTcpSegmentRfc793(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+ const char *pszType, void const *pvValue,
+ int cchWidth, int cchPrecision, unsigned fFlags,
+ void *pvUser)
+{
+ size_t cb = 0;
+ const struct tcpiphdr *ti = (const struct tcpiphdr *)pvValue;
+ NOREF(cchWidth);
+ NOREF(cchPrecision);
+ NOREF(fFlags);
+ NOREF(pvUser);
+ AssertReturn(RTStrCmp(pszType, "tcpseg793") == 0 && ti, 0);
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "SEG[ACK: %x, SEQ: %x, LEN: %x, WND: %x, UP: %x]",
+ ti->ti_ack, ti->ti_seq, ti->ti_len, ti->ti_win, ti->ti_urp);
+ return cb;
+}
+
+/*
+ * Prints TCP state
+ */
+static DECLCALLBACK(size_t)
+printTcpState(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+ const char *pszType, void const *pvValue,
+ int cchWidth, int cchPrecision, unsigned fFlags,
+ void *pvUser)
+{
+ size_t cb = 0;
+ const int idxTcpState = (int)(uintptr_t)pvValue;
+ char *pszTcpStateName = (idxTcpState >= 0 && idxTcpState < TCP_NSTATES) ? g_apszTcpStates[idxTcpState] : "TCPS_INVALIDE_STATE";
+ NOREF(cchWidth);
+ NOREF(cchPrecision);
+ NOREF(fFlags);
+ NOREF(pvUser);
+ AssertReturn(RTStrCmp(pszType, "tcpstate") == 0, 0);
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "%s", pszTcpStateName);
+ return cb;
+}
+
+/*
+ * Prints TCP flags
+ */
+static DECLCALLBACK(size_t)
+printTcpFlags(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+ const char *pszType, void const *pvValue,
+ int cchWidth, int cchPrecision, unsigned fFlags,
+ void *pvUser)
+{
+ size_t cbPrint = 0;
+ uint32_t u32TcpFlags = (uint32_t)(uintptr_t)pvValue;
+ bool fSingleValue = true;
+ int idxTcpFlags = 0;
+ NOREF(cchWidth);
+ NOREF(cchPrecision);
+ NOREF(fFlags);
+ NOREF(pvUser);
+ AssertReturn(RTStrCmp(pszType, "tcpflags") == 0, 0);
+ cbPrint += RTStrFormat(pfnOutput,
+ pvArgOutput,
+ NULL,
+ 0,
+ "tcpflags: %RX8 [", (uint8_t)u32TcpFlags);
+ for (idxTcpFlags = 0; idxTcpFlags < RT_ELEMENTS(g_aTcpFlags); ++idxTcpFlags)
+ {
+ if (u32TcpFlags & g_aTcpFlags[idxTcpFlags].u32SocketState)
+ {
+ cbPrint += RTStrFormat(pfnOutput,
+ pvArgOutput,
+ NULL,
+ 0,
+ fSingleValue ? "%s(%RX8)" : "|%s(%RX8)",
+ g_aTcpFlags[idxTcpFlags].pcszSocketStateName,
+ (uint8_t)g_aTcpFlags[idxTcpFlags].u32SocketState);
+ fSingleValue = false;
+ }
+ }
+ cbPrint += RTStrFormat(pfnOutput,
+ pvArgOutput,
+ NULL,
+ 0,
+ "]");
+ return cbPrint;
+}
+
+/*
+ * Prints sbuf state
+ */
+static DECLCALLBACK(size_t)
+printSbuf(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+ const char *pszType, void const *pvValue,
+ int cchWidth, int cchPrecision, unsigned fFlags,
+ void *pvUser)
+{
+ size_t cb = 0;
+ const struct sbuf *sb = (struct sbuf *)pvValue;
+ NOREF(cchWidth);
+ NOREF(cchPrecision);
+ NOREF(fFlags);
+ NOREF(pvUser);
+ AssertReturn(RTStrCmp(pszType, "sbuf") == 0, 0);
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "[sbuf:%p cc:%d, datalen:%d, wprt:%p, rptr:%p data:%p]",
+ sb, sb->sb_cc, sb->sb_datalen, sb->sb_wptr, sb->sb_rptr, sb->sb_data);
+ return cb;
+}
+
+/*
+ * Prints zone state
+ */
+static DECLCALLBACK(size_t)
+printMbufZone(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+ const char *pszType, void const *pvValue,
+ int cchWidth, int cchPrecision, unsigned fFlags,
+ void *pvUser)
+{
+ size_t cb = 0;
+ const uma_zone_t zone = (const uma_zone_t)pvValue;
+ NOREF(cchWidth);
+ NOREF(cchPrecision);
+ NOREF(fFlags);
+ NOREF(pvUser);
+ AssertReturn(RTStrCmp(pszType, "mzone") == 0, 0);
+ if (!zone)
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "[zone:NULL]");
+ else
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "[zone:%p name:%s, master_zone:%R[mzone]]",
+ zone, zone->name, zone->master_zone);
+ return cb;
+}
+
+/*
+ * Prints zone's item state
+ */
+static DECLCALLBACK(size_t)
+printMbufZoneItem(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+ const char *pszType, void const *pvValue,
+ int cchWidth, int cchPrecision, unsigned fFlags,
+ void *pvUser)
+{
+ size_t cb = 0;
+ const struct item *it = (const struct item *)pvValue;
+ NOREF(cchWidth);
+ NOREF(cchPrecision);
+ NOREF(fFlags);
+ NOREF(pvUser);
+ AssertReturn(RTStrCmp(pszType, "mzoneitem") == 0, 0);
+ if (!it)
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "[item:NULL]");
+ else
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "[iptem:%p ref_count:%d, zone:%R[mzone]]",
+ it, it->ref_count, it->zone);
+ return cb;
+}
+
+static DECLCALLBACK(size_t)
+print_networkevents(PFNRTSTROUTPUT pfnOutput, void *pvArgOutput,
+ const char *pszType, void const *pvValue,
+ int cchWidth, int cchPrecision, unsigned fFlags,
+ void *pvUser)
+{
+ size_t cb = 0;
+#ifdef RT_OS_WINDOWS
+ WSANETWORKEVENTS *pNetworkEvents = (WSANETWORKEVENTS*)pvValue;
+ bool fDelim = false;
+#endif
+
+ NOREF(cchWidth);
+ NOREF(cchPrecision);
+ NOREF(fFlags);
+ NOREF(pvUser);
+
+#ifdef RT_OS_WINDOWS
+ AssertReturn(strcmp(pszType, "natwinnetevents") == 0, 0);
+
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, "events=%02x (",
+ pNetworkEvents->lNetworkEvents);
+# define DO_BIT(bit) \
+ if (pNetworkEvents->lNetworkEvents & FD_ ## bit) \
+ { \
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, \
+ "%s" #bit "(%d)", fDelim ? "," : "", \
+ pNetworkEvents->iErrorCode[FD_ ## bit ## _BIT]); \
+ fDelim = true; \
+ }
+ DO_BIT(READ);
+ DO_BIT(WRITE);
+ DO_BIT(OOB);
+ DO_BIT(ACCEPT);
+ DO_BIT(CONNECT);
+ DO_BIT(CLOSE);
+ DO_BIT(QOS);
+# undef DO_BIT
+ cb += RTStrFormat(pfnOutput, pvArgOutput, NULL, 0, ")");
+#else
+ NOREF(pfnOutput);
+ NOREF(pvArgOutput);
+ NOREF(pszType);
+ NOREF(pvValue);
+#endif
+ return cb;
+}
+
+#if 0
+/*
+ * Debugging
+ */
+int errno_func(const char *file, int line)
+{
+ int err = WSAGetLastError();
+ LogRel(("errno=%d (%s:%d)\n", err, file, line));
+ return err;
+}
+#endif
+
+int
+debug_init(PNATState pData)
+{
+ int rc = VINF_SUCCESS;
+
+ static int g_fFormatRegistered;
+
+ if (!g_fFormatRegistered)
+ {
+
+ rc = RTStrFormatTypeRegister("natsock", printSocket, pData); AssertRC(rc);
+ rc = RTStrFormatTypeRegister("natsockstate", printNATSocketState, NULL); AssertRC(rc);
+ rc = RTStrFormatTypeRegister("natwinnetevents",
+ print_networkevents, NULL); AssertRC(rc);
+ rc = RTStrFormatTypeRegister("tcpcb793", printTcpcbRfc793, NULL); AssertRC(rc);
+ rc = RTStrFormatTypeRegister("tcpseg793", printTcpSegmentRfc793, NULL); AssertRC(rc);
+ rc = RTStrFormatTypeRegister("tcpstate", printTcpState, NULL); AssertRC(rc);
+ rc = RTStrFormatTypeRegister("tcpflags", printTcpFlags, NULL); AssertRC(rc);
+ rc = RTStrFormatTypeRegister("sbuf", printSbuf, NULL); AssertRC(rc);
+ rc = RTStrFormatTypeRegister("mzone", printMbufZone, NULL); AssertRC(rc);
+ rc = RTStrFormatTypeRegister("mzoneitem", printMbufZoneItem, NULL); AssertRC(rc);
+ g_fFormatRegistered = 1;
+ }
+
+ return rc;
+}
diff --git a/src/VBox/Devices/Network/slirp/debug.h b/src/VBox/Devices/Network/slirp/debug.h
new file mode 100644
index 00000000..b0b08b5d
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/debug.h
@@ -0,0 +1,77 @@
+/* $Id: debug.h $ */
+/** @file
+ * NAT - debug helpers (declarations/defines).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _DEBUG_H_
+#define _DEBUG_H_
+
+#include <VBox/log.h>
+/* we've excluded stdio.h */
+#define FILE void
+
+int debug_init (PNATState);
+void ipstats (PNATState);
+void tcpstats (PNATState);
+void udpstats (PNATState);
+void icmpstats (PNATState);
+void mbufstats (PNATState);
+void sockstats (PNATState);
+
+#ifdef LOG_ENABLED
+# define TCP_STATE_SWITCH_TO(tp, new_tcp_state) \
+ do { \
+ Log2(("%R[tcpcb793] switch to %R[tcpstate] -> %R[tcpstate]\n", (tp), (tp->t_state) ,(new_tcp_state))); \
+ if ((tp)->t_socket) \
+ Log2(("%R[tcpcb793] %R[natsock]\n", (tp), (tp)->t_socket)); \
+ (tp)->t_state = (new_tcp_state); \
+ } while (0)
+#else
+# define TCP_STATE_SWITCH_TO(tp, new_tcp_state) (tp)->t_state = (new_tcp_state)
+#endif
+
+/* TCP CB state validity macro definitions
+ * we need to be sure that TCP is in right state.
+ * TCP_ACCEPTABLE_STATEX(tp, (X-states here))
+ */
+#ifdef DEBUG_vvl
+# define TCP_ACCEPTABLE_STATE1(tp, tcp_state1) Assert((tp)->t_state == (tcp_state))
+# define TCP_ACCEPTABLE_STATE2(tp, tcp_state1, tcp_state2) \
+ Assert( (tp)->t_state == (tcp_state1) \
+ || (tp)->t_state == (tcp_state2) ); \
+#else
+# define TCP_ACCEPTABLE_STATE1(tp, tcp_state1) do { } while(0)
+# define TCP_ACCEPTABLE_STATE2(tp, tcp_state1, tcp_state2) do { } while(0)
+#endif
+#endif
diff --git a/src/VBox/Devices/Network/slirp/dnsproxy/dnsproxy.c b/src/VBox/Devices/Network/slirp/dnsproxy/dnsproxy.c
new file mode 100644
index 00000000..0e478b10
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/dnsproxy/dnsproxy.c
@@ -0,0 +1,764 @@
+/* $Id: dnsproxy.c $ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * Copyright (c) 2003,2004,2005 Armin Wolfermann
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef VBOX
+#include <config.h>
+#include <errno.h>
+#include <pwd.h>
+#include <signal.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#else
+#include "slirp.h"
+#endif
+
+#ifndef VBOX
+#define GLOBALS 1
+#include "dnsproxy.h"
+
+#define RD(x) (*(x + 2) & 0x01)
+#define MAX_BUFSPACE 512
+
+static unsigned short queryid = 0;
+#define QUERYID queryid++
+
+static struct sockaddr_in authoritative_addr;
+static struct sockaddr_in recursive_addr;
+static int sock_query;
+static int sock_answer;
+static int dnsproxy_sig;
+
+extern int event_gotsig;
+extern int (*event_sigcb)(void);
+
+#ifdef DEBUG
+char *malloc_options = "AGZ";
+#endif
+
+/* signal_handler -- Native signal handler. Set external flag for libevent
+ * and store type of signal. Real signal handling is done in signal_event.
+ */
+
+RETSIGTYPE
+signal_handler(int sig)
+{
+ event_gotsig = 1;
+ dnsproxy_sig = sig;
+}
+
+/* signal_event -- Called by libevent to deliver a signal.
+ */
+
+int
+signal_event(void)
+{
+ fatal("exiting on signal %d", dnsproxy_sig);
+ return 0;
+}
+
+#else /* VBOX */
+
+# define RD(x) (*(x + 2) & 0x01)
+
+# define QUERYID queryid++
+
+#endif
+/* timeout -- Called by the event loop when a query times out. Removes the
+ * query from the queue.
+ */
+/* ARGSUSED */
+#ifndef VBOX
+static void
+timeout(int fd, short event, void *arg)
+{
+ /* here we should check if we reached the end of the DNS server list */
+ hash_remove_request(pData, (struct request *)arg);
+ free((struct request *)arg);
+ ++removed_queries;
+}
+#else /* VBOX */
+static void
+timeout(PNATState pData, struct socket *so, void *arg)
+{
+ struct request *req = (struct request *)arg;
+ struct dns_entry *de;
+ /* be paranoid */
+ AssertPtrReturnVoid(arg);
+
+ if ( req->dnsgen != pData->dnsgen
+ || req->dns_server == NULL
+ || (de = TAILQ_PREV(req->dns_server, dns_list_head, de_list)) == NULL)
+ {
+ if (req->dnsgen != pData->dnsgen)
+ {
+ /* XXX: Log2 */
+ LogRel(("NAT: dnsproxy: timeout: req %p dnsgen %u != %u on %R[natsock]\n",
+ req, req->dnsgen, pData->dnsgen, so));
+ }
+ hash_remove_request(pData, req);
+ RTMemFree(req);
+ ++removed_queries;
+ /* the rest of clean up at the end of the method. */
+ }
+ else
+ {
+ struct ip *ip;
+ struct udphdr *udp;
+ int iphlen;
+ struct mbuf *m = NULL;
+ char *data;
+
+ m = slirpDnsMbufAlloc(pData);
+ if (m == NULL)
+ {
+ LogRel(("NAT: Can't allocate mbuf\n"));
+ goto socket_clean_up;
+ }
+
+ /* mbuf initialization */
+ m->m_data += if_maxlinkhdr;
+
+ ip = mtod(m, struct ip *);
+ udp = (struct udphdr *)&ip[1]; /* ip attributes */
+ data = (char *)&udp[1];
+ iphlen = sizeof(struct ip);
+
+ m->m_len += sizeof(struct ip);
+ m->m_len += sizeof(struct udphdr);
+ m->m_len += req->nbyte;
+
+ ip->ip_src.s_addr = so->so_laddr.s_addr;
+ ip->ip_dst.s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_DNS);
+ udp->uh_dport = ntohs(53);
+ udp->uh_sport = so->so_lport;
+
+ memcpy(data, req->byte, req->nbyte); /* coping initial req */
+
+ /* req points to so->so_timeout_arg */
+ req->dns_server = de;
+
+ /* expiration will be bumped in dnsproxy_query */
+
+ dnsproxy_query(pData, so, m, iphlen);
+ /* should we free so->so_m ? */
+ return;
+ }
+
+ socket_clean_up:
+ /* This socket (so) will be detached, so we need to remove timeout(&_arg) references
+ * before leave
+ */
+ so->so_timeout = NULL;
+ so->so_timeout_arg = NULL;
+ return;
+
+}
+#endif /* VBOX */
+
+/* do_query -- Called by the event loop when a packet arrives at our
+ * listening socket. Read the packet, create a new query, append it to the
+ * queue and send it to the correct server.
+ *
+ * Slirp: this routine should be called from udp_input
+ * socket is Slirp's construction (here we should set expiration time for socket)
+ * mbuf points on ip header to easy fetch information about source and destination.
+ * iphlen - len of ip header
+ */
+
+/* ARGSUSED */
+#ifndef VBOX
+static void
+do_query(int fd, short event, void *arg)
+#else
+void
+dnsproxy_query(PNATState pData, struct socket *so, struct mbuf *m, int iphlen)
+#endif
+{
+#ifndef VBOX
+ char buf[MAX_BUFSPACE];
+ unsigned int fromlen = sizeof(fromaddr);
+ struct timeval tv;
+#else
+ struct ip *ip;
+ char *buf;
+ int retransmit;
+ struct udphdr *udp;
+#endif
+ struct sockaddr_in addr;
+ struct request *req = NULL;
+#ifndef VBOX
+ struct sockaddr_in fromaddr;
+#else
+ struct sockaddr_in fromaddr = { 0, };
+#endif
+ int byte = 0;
+
+ ++all_queries;
+
+#ifndef VBOX
+ /* Reschedule event */
+ event_add((struct event *)arg, NULL);
+
+ /* read packet from socket */
+ if ((byte = recvfrom(fd, buf, sizeof(buf), 0,
+ (struct sockaddr *)&fromaddr, &fromlen)) == -1) {
+ LogRel(("recvfrom failed: %s\n", strerror(errno)));
+ ++dropped_queries;
+ return;
+ }
+
+ /* check for minimum dns packet length */
+ if (byte < 12) {
+ LogRel(("query too short from %s\n", inet_ntoa(fromaddr.sin_addr)));
+ ++dropped_queries;
+ return;
+ }
+
+ /* allocate new request */
+ if ((req = calloc(1, sizeof(struct request))) == NULL) {
+ LogRel(("calloc failed\n"));
+ ++dropped_queries;
+ return;
+ }
+
+ req->id = QUERYID;
+ memcpy(&req->client, &fromaddr, sizeof(struct sockaddr_in));
+ memcpy(&req->clientid, &buf[0], 2);
+
+ /* where is this query coming from? */
+ if (is_internal(pData, fromaddr.sin_addr)) {
+ req->recursion = RD(buf);
+ DPRINTF(("Internal query RD=%d\n", req->recursion));
+ } else {
+ /* no recursion for foreigners */
+ req->recursion = 0;
+ DPRINTF(("External query RD=%d\n", RD(buf)));
+ }
+
+ /* insert it into the hash table */
+ hash_add_request(pData, req);
+
+ /* overwrite the original query id */
+ memcpy(&buf[0], &req->id, 2);
+
+ if (req->recursion) {
+
+ /* recursive queries timeout in 90s */
+ event_set(&req->timeout, -1, 0, timeout, req);
+ tv.tv_sec=recursive_timeout; tv.tv_usec=0;
+ event_add(&req->timeout, &tv);
+
+ /* send it to our recursive server */
+ if ((byte = sendto(sock_answer, buf, (unsigned int)byte, 0,
+ (struct sockaddr *)&recursive_addr,
+ sizeof(struct sockaddr_in))) == -1) {
+ LogRel(("sendto failed: %s\n", strerror(errno)));
+ ++dropped_queries;
+ return;
+ }
+
+ ++recursive_queries;
+
+ } else {
+
+ /* authoritative queries timeout in 10s */
+ event_set(&req->timeout, -1, 0, timeout, req);
+ tv.tv_sec=authoritative_timeout; tv.tv_usec=0;
+ event_add(&req->timeout, &tv);
+
+ /* send it to our authoritative server */
+ if ((byte = sendto(sock_answer, buf, (unsigned int)byte, 0,
+ (struct sockaddr *)&authoritative_addr,
+ sizeof(struct sockaddr_in))) == -1) {
+ LogRel(("sendto failed: %s\n", strerror(errno)));
+ ++dropped_queries;
+ return;
+ }
+ ++authoritative_queries;
+ }
+
+#else /* VBOX */
+ AssertPtr(pData);
+
+ /* m->m_data points to IP header */
+#if 0
+ /* XXX: for some reason it make gdb ill,
+ * it good to have this assert here with assumption above.
+ */
+ M_ASSERTPKTHDR(m);
+#endif
+
+ ip = mtod(m, struct ip *);
+ udp = (struct udphdr *)(m->m_data + iphlen);
+
+ fromaddr.sin_addr.s_addr = ip->ip_src.s_addr;
+ fromaddr.sin_port = udp->uh_sport;
+ fromaddr.sin_family = AF_INET;
+
+ /* iphlen equals to lenght of ip header */
+ Assert(iphlen == sizeof(struct ip));
+ iphlen += sizeof (struct udphdr);
+
+ byte = m->m_len - iphlen;
+ buf = m->m_data + iphlen;
+
+ /* check for minimum dns packet length */
+ if (byte < 12) {
+ LogRel(("NAT: Query too short from %RTnaipv4\n", fromaddr.sin_addr));
+ ++dropped_queries;
+ return;
+ }
+
+ req = so->so_timeout_arg;
+
+ if (!req)
+ {
+
+ Assert(!so->so_timeout_arg);
+
+ if ((req = RTMemAllocZ(sizeof(struct request) + byte)) == NULL)
+ {
+ LogRel(("NAT: calloc failed\n"));
+ ++dropped_queries;
+ return;
+ }
+
+ req->id = QUERYID;
+ memcpy(&req->client, &fromaddr, sizeof(struct sockaddr_in));
+ memcpy(&req->clientid, &buf[0], 2);
+ req->dns_server = TAILQ_LAST(&pData->pDnsList, dns_list_head);
+ req->dnsgen = pData->dnsgen;
+ if (req->dns_server == NULL)
+ {
+ RTMemFree(req);
+ return;
+ }
+ retransmit = 0;
+ so->so_timeout = timeout;
+ so->so_timeout_arg = req;
+ req->nbyte = byte;
+ memcpy(req->byte, buf, byte); /* copying original request */
+ }
+ else
+ {
+ if (req->dnsgen != pData->dnsgen)
+ {
+ /* XXX: Log2 */
+ LogRel(("NAT: dnsproxy: query: req %p dnsgen %u != %u on %R[natsock]\n",
+ req, req->dnsgen, pData->dnsgen, so));
+ /*
+ * XXX: TODO: this probably requires more cleanup.
+ * Cf. XXX comment for sendto() failure below, but that
+ * error leg is probably untested since ~never taken.
+ */
+ ++dropped_queries;
+ return;
+ }
+ retransmit = 1;
+ }
+
+ req->recursion = 0;
+
+ DPRINTF(("External query RD=%d\n", RD(buf)));
+
+ if (retransmit == 0)
+ hash_add_request(pData, req);
+
+
+ /* overwrite the original query id */
+ memcpy(&buf[0], &req->id, 2);
+
+ /* let's slirp to care about expiration */
+ so->so_expire = curtime + recursive_timeout * 1000;
+
+ memset(&addr, 0, sizeof(struct sockaddr_in));
+ addr.sin_family = AF_INET;
+ if (req->dns_server->de_addr.s_addr == (pData->special_addr.s_addr | RT_H2N_U32_C(CTL_ALIAS))) {
+ /* undo loopback remapping done in get_dns_addr_domain() */
+ addr.sin_addr.s_addr = RT_N2H_U32_C(INADDR_LOOPBACK);
+ }
+ else {
+ addr.sin_addr.s_addr = req->dns_server->de_addr.s_addr;
+ }
+ addr.sin_port = htons(53);
+
+ /* send it to our authoritative server */
+ Log2(("NAT: request will be %ssent to %RTnaipv4 on %R[natsock]\n",
+ retransmit ? "re" : "", addr.sin_addr, so));
+
+ byte = sendto(so->s, buf, (unsigned int)byte, 0,
+ (struct sockaddr *)&addr,
+ sizeof(struct sockaddr_in));
+ if (byte == -1)
+ {
+ /* XXX: is it really enough? */
+ LogRel(("NAT: sendto failed: %s\n", strerror(errno)));
+ ++dropped_queries;
+ return;
+ }
+
+ so->so_state = SS_ISFCONNECTED; /* now it's selected */
+ Log2(("NAT: request was %ssent to %RTnaipv4 on %R[natsock]\n",
+ retransmit ? "re" : "", addr.sin_addr, so));
+
+ ++authoritative_queries;
+
+# if 0
+ /* XXX: this stuff for _debugging_ only,
+ * first enforce guest to send next request
+ * and second for faster getting timeout callback
+ * other option is adding couple entries in resolv.conf with
+ * invalid nameservers.
+ *
+ * For testing purposes could be used
+ * namebench -S -q 10000 -m random or -m chunk
+ */
+ /* RTThreadSleep(3000); */
+ /* curtime += 300; */
+# endif
+#endif /* VBOX */
+}
+
+/* do_answer -- Process a packet coming from our authoritative or recursive
+ * server. Find the corresponding query and send answer back to querying
+ * host.
+ *
+ * Slirp: we call this from the routine from socrecvfrom routine handling UDP responses.
+ * So at the moment of call response already has been readed and packed into the mbuf
+ */
+
+/* ARGSUSED */
+#ifndef VBOX
+static void
+do_answer(int fd, short event, void *arg)
+#else
+void
+dnsproxy_answer(PNATState pData, struct socket *so, struct mbuf *m)
+#endif
+{
+#ifndef VBOX
+ char buf[MAX_BUFSPACE];
+ int byte = 0;
+ struct request *query = NULL;
+
+ /* Reschedule event */
+ event_add((struct event *)arg, NULL);
+
+ /* read packet from socket */
+ if ((byte = recvfrom(fd, buf, sizeof(buf), 0, NULL, NULL)) == -1) {
+ LogRel(("recvfrom failed: %s\n", strerror(errno)));
+ ++dropped_answers;
+ return;
+ }
+
+ /* check for minimum dns packet length */
+ if (byte < 12) {
+ LogRel(("answer too short\n"));
+ ++dropped_answers;
+ return;
+ }
+
+ /* find corresponding query */
+ if ((query = hash_find_request(pData, *((unsigned short *)&buf))) == NULL) {
+ ++late_answers;
+ return;
+ }
+ event_del(&query->timeout);
+
+ hash_remove_request(pData, query);
+
+ /* restore original query id */
+ memcpy(&buf[0], &query->clientid, 2);
+
+ if (sendto(sock_query, buf, (unsigned int)byte, 0,
+ (struct sockaddr *)&query->client,
+ sizeof(struct sockaddr_in)) == -1) {
+ LogRel(("sendto failed: %s\n", strerror(errno)));
+ ++dropped_answers;
+ }
+ else
+ ++answered_queries;
+
+ free(query);
+#else /* VBOX */
+
+ char *buf = NULL;
+ int byte = 0;
+ struct request *query = NULL;
+
+ AssertPtr(pData);
+
+ /* XXX: mbuf->data points to ??? */
+ byte = m->m_len;
+ buf = mtod(m, char *);
+
+ /* check for minimum dns packet length */
+ if (byte < 12) {
+ LogRel(("NAT: Answer too short\n"));
+ ++dropped_answers;
+ return;
+ }
+
+ /* find corresponding query (XXX: but see below) */
+ query = hash_find_request(pData, *((unsigned short *)buf));
+
+ if (query == NULL)
+ {
+ /* XXX: if we haven't found anything for this request ...
+ * What we are expecting later?
+ */
+ ++late_answers;
+ so->so_expire = curtime + SO_EXPIREFAST;
+ Log2(("NAT: query wasn't found\n"));
+ return;
+ }
+
+ /*
+ * XXX: The whole hash thing is pretty meaningless right now since
+ * we use a separate socket for each request, so we already know
+ * the answer.
+ *
+ * If the answer is not what we expect it to be, then it's
+ * probably a stray or malicious reply and we'd better not free a
+ * query owned by some other socket - that would cause
+ * use-after-free later on.
+ */
+ if (query != so->so_timeout_arg)
+ return;
+
+ so->so_timeout = NULL;
+ so->so_timeout_arg = NULL;
+
+ hash_remove_request(pData, query);
+
+ /* restore original query id */
+ memcpy(&buf[0], &query->clientid, 2);
+
+ ++answered_queries;
+
+ RTMemFree(query);
+#endif /* VBOX */
+}
+
+
+#ifdef VBOX
+int
+dnsproxy_init(PNATState pData)
+{
+ /* globals initialization */
+ authoritative_port = 53;
+ authoritative_timeout = 10;
+ recursive_port = 53;
+ recursive_timeout = 2;
+ stats_timeout = 3600;
+ dns_port = 53;
+ return 0;
+}
+#else /* !VBOX */
+/* main -- dnsproxy main function
+ */
+int
+main(int argc, char *argv[])
+{
+ int ch;
+ struct passwd *pw = NULL;
+ struct sockaddr_in addr;
+ struct event evq, eva;
+ const char *config = "/etc/dnsproxy.conf";
+ int daemonize = 0;
+
+ /* Process commandline arguments */
+ while ((ch = getopt(argc, argv, "c:dhV")) != -1) {
+ switch (ch) {
+ case 'c':
+ config = optarg;
+ break;
+ case 'd':
+ daemonize = 1;
+ break;
+ case 'V':
+ fprintf(stderr, PACKAGE_STRING "\n");
+ exit(0);
+ RT_FALL_THRU();
+ case 'h':
+ default:
+ fprintf(stderr,
+ "usage: dnsproxy [-c file] [-dhV]\n" \
+ "\t-c file Read configuration from file\n" \
+ "\t-d Detach and run as a daemon\n" \
+ "\t-h This help text\n" \
+ "\t-V Show version information\n");
+ exit(1);
+ }
+ }
+
+ /* Parse configuration and check required parameters */
+ if (!parse(config))
+ fatal("unable to parse configuration");
+
+ if (!authoritative || !recursive)
+ fatal("No authoritative or recursive server defined");
+
+ if (!listenat)
+ listenat = strdup("0.0.0.0");
+
+ /* Create and bind query socket */
+ if ((sock_query = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ fatal("unable to create socket: %s", strerror(errno));
+
+ memset(&addr, 0, sizeof(struct sockaddr_in));
+ addr.sin_addr.s_addr = inet_addr(listenat);
+ addr.sin_port = htons(port);
+ addr.sin_family = AF_INET;
+
+ if (bind(sock_query, (struct sockaddr *)&addr, sizeof(addr)) != 0)
+ fatal("unable to bind socket: %s", strerror(errno));
+
+ /* Create and bind answer socket */
+ if ((sock_answer = socket(AF_INET, SOCK_DGRAM, 0)) == -1)
+ fatal("unable to create socket: %s", strerror(errno));
+
+ memset(&addr, 0, sizeof(struct sockaddr_in));
+ addr.sin_family = AF_INET;
+
+ if (bind(sock_answer, (struct sockaddr *)&addr, sizeof(addr)) != 0)
+ fatal("unable to bind socket: %s", strerror(errno));
+
+ /* Fill sockaddr_in structs for both servers */
+ memset(&authoritative_addr, 0, sizeof(struct sockaddr_in));
+ authoritative_addr.sin_addr.s_addr = inet_addr(authoritative);
+ authoritative_addr.sin_port = htons(authoritative_port);
+ authoritative_addr.sin_family = AF_INET;
+
+ memset(&recursive_addr, 0, sizeof(struct sockaddr_in));
+ recursive_addr.sin_addr.s_addr = inet_addr(recursive);
+ recursive_addr.sin_port = htons(recursive_port);
+ recursive_addr.sin_family = AF_INET;
+
+ /* Daemonize if requested and switch to syslog */
+ if (daemonize) {
+ if (daemon(0, 0) == -1)
+ fatal("unable to daemonize");
+ log_syslog("dnsproxy");
+ }
+
+ /* Find less privileged user */
+ if (user) {
+ pw = getpwnam(user);
+ if (!pw)
+ fatal("unable to find user %s", user);
+ }
+
+ /* Do a chroot if requested */
+ if (chrootdir) {
+ if (chdir(chrootdir) || chroot(chrootdir))
+ fatal("unable to chroot to %s", chrootdir);
+ chdir("/");
+ }
+
+ /* Drop privileges */
+ if (user) {
+ if (setgroups(1, &pw->pw_gid) < 0)
+ fatal("setgroups: %s", strerror(errno));
+#if defined(HAVE_SETRESGID)
+ if (setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) < 0)
+ fatal("setresgid: %s", strerror(errno));
+#elif defined(HAVE_SETREGID)
+ if (setregid(pw->pw_gid, pw->pw_gid) < 0)
+ fatal("setregid: %s", strerror(errno));
+#else
+ if (setegid(pw->pw_gid) < 0)
+ fatal("setegid: %s", strerror(errno));
+ if (setgid(pw->pw_gid) < 0)
+ fatal("setgid: %s", strerror(errno));
+#endif
+#if defined(HAVE_SETRESUID)
+ if (setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid) < 0)
+ fatal("setresuid: %s", strerror(errno));
+#elif defined(HAVE_SETREUID)
+ if (setreuid(pw->pw_uid, pw->pw_uid) < 0)
+ fatal("setreuid: %s", strerror(errno));
+#else
+ if (seteuid(pw->pw_uid) < 0)
+ fatal("seteuid: %s", strerror(errno));
+ if (setuid(pw->pw_uid) < 0)
+ fatal("setuid: %s", strerror(errno));
+#endif
+ }
+
+ /* Init event handling */
+ event_init();
+
+ event_set(&evq, sock_query, EV_READ, do_query, &evq);
+ event_add(&evq, NULL);
+
+ event_set(&eva, sock_answer, EV_READ, do_answer, &eva);
+ event_add(&eva, NULL);
+
+ /* Zero counters and start statistics timer */
+ statistics_start();
+
+ /* Take care of signals */
+ if (signal(SIGINT, signal_handler) == SIG_ERR)
+ fatal("unable to mask signal SIGINT: %s", strerror(errno));
+
+ if (signal(SIGTERM, signal_handler) == SIG_ERR)
+ fatal("unable to mask signal SIGTERM: %s", strerror(errno));
+
+ if (signal(SIGHUP, SIG_IGN) == SIG_ERR)
+ fatal("unable to mask signal SIGHUP: %s", strerror(errno));
+
+ event_sigcb = signal_event;
+
+ /* Start libevent main loop */
+ event_dispatch();
+
+ return 0;
+
+}
+#endif
diff --git a/src/VBox/Devices/Network/slirp/dnsproxy/dnsproxy.h b/src/VBox/Devices/Network/slirp/dnsproxy/dnsproxy.h
new file mode 100644
index 00000000..584f25dd
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/dnsproxy/dnsproxy.h
@@ -0,0 +1,157 @@
+/* $Id: dnsproxy.h $ */
+/*
+ * Copyright (c) 2003,2004,2005 Armin Wolfermann
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef _DNSPROXY_H_
+#define _DNSPROXY_H_
+
+/* LONGLONG */
+#include <sys/types.h>
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#ifndef VBOX
+#include <sys/socket.h>
+#include <netinet/in.h>
+#ifdef HAVE_ARPA_INET_H
+# include <arpa/inet.h>
+#endif
+#include <stdarg.h>
+
+#include <event.h>
+
+#ifdef DEBUG
+#define DPRINTF(x) do { printf x ; } while (0)
+#else
+#define DPRINTF(x)
+#endif
+
+#ifdef GLOBALS
+#define GLOBAL(a) a
+#define GLOBAL_INIT(a,b) a = b
+#else
+#define GLOBAL(a) extern a
+#define GLOBAL_INIT(a,b) extern a
+#endif
+#endif
+
+struct request {
+ unsigned short id;
+
+ struct sockaddr_in client;
+ unsigned short clientid;
+ unsigned char recursion;
+
+#ifndef VBOX
+ struct event timeout;
+#endif
+
+ struct request **prev;
+ struct request *next;
+#ifdef VBOX
+ /* this field used for saving last attempt
+ * to connect server, timeout function should change
+ * it's value on next server. And dnsproxy_query should
+ * initializate with first server in the list
+ *
+ * dnsgen is a generation number - a copy of pData->dnsgen at the
+ * time of request creation (poor man's weak reference).
+ * dns_server must not be used if pData->dnsgen changed.
+ */
+ struct dns_entry *dns_server;
+ uint32_t dnsgen;
+ int nbyte; /* length of dns request */
+ char byte[1]; /* copy of original request */
+#endif
+};
+
+#ifndef VBOX
+GLOBAL_INIT(unsigned int authoritative_port, 53);
+GLOBAL_INIT(unsigned int authoritative_timeout, 10);
+GLOBAL_INIT(unsigned int recursive_port, 53);
+GLOBAL_INIT(unsigned int recursive_timeout, 90);
+GLOBAL_INIT(unsigned int stats_timeout, 3600);
+GLOBAL_INIT(unsigned int port, 53);
+
+GLOBAL(char *authoritative);
+GLOBAL(char *chrootdir);
+GLOBAL(char *listenat);
+GLOBAL(char *recursive);
+GLOBAL(char *user);
+
+GLOBAL(unsigned long active_queries);
+GLOBAL(unsigned long all_queries);
+GLOBAL(unsigned long authoritative_queries);
+GLOBAL(unsigned long recursive_queries);
+GLOBAL(unsigned long removed_queries);
+GLOBAL(unsigned long dropped_queries);
+GLOBAL(unsigned long answered_queries);
+GLOBAL(unsigned long dropped_answers);
+GLOBAL(unsigned long late_answers);
+GLOBAL(unsigned long hash_collisions);
+
+/* dnsproxy.c */
+RETSIGTYPE signal_handler(int);
+int signal_event(void);
+
+/* daemon.c */
+int daemon(int, int);
+#endif
+
+/* hash.c */
+void hash_add_request(PNATState, struct request *);
+void hash_remove_request(PNATState, struct request *);
+struct request *hash_find_request(PNATState, unsigned short);
+
+/* internal.c */
+int add_internal(PNATState, char *);
+int is_internal(PNATState, struct in_addr);
+
+#ifndef VBOX
+/* log.c */
+void log_syslog(const char *);
+void info(const char *, ...);
+void error(const char *, ...);
+void fatal(const char *, ...);
+
+/* parse.c */
+int parse(const char *);
+
+/* statistics.c */
+void statistics_start(void);
+#else
+# define DPRINTF Log2
+int dnsproxy_init(PNATState pData);
+void dnsproxy_query(PNATState pData, struct socket *so, struct mbuf *m, int iphlen);
+void dnsproxy_answer(PNATState pData, struct socket *so, struct mbuf *m);
+#endif
+
+#endif /* _DNSPROXY_H_ */
diff --git a/src/VBox/Devices/Network/slirp/dnsproxy/hash.c b/src/VBox/Devices/Network/slirp/dnsproxy/hash.c
new file mode 100644
index 00000000..0de6cbea
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/dnsproxy/hash.c
@@ -0,0 +1,77 @@
+/* $Id: hash.c $ */
+/*
+ * Copyright (c) 2003,2004 Armin Wolfermann
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the "Software"),
+ * to deal in the Software without restriction, including without limitation
+ * the rights to use, copy, modify, merge, publish, distribute, sublicense,
+ * and/or sell copies of the Software, and to permit persons to whom the
+ * Software is furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+ * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
+ * DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef VBOX
+#include <config.h>
+#include "dnsproxy.h"
+
+#define HASHSIZE 10
+#define HASH(id) (id & ((1 << HASHSIZE) - 1))
+
+static struct request *request_hash[1 << HASHSIZE];
+#else /* VBOX */
+# include "slirp.h"
+#endif
+
+void
+hash_add_request(PNATState pData, struct request *req)
+{
+ struct request **p = &request_hash[HASH(req->id)];
+ Log2(("NAT: hash req id %d has been added \n", req->id));
+
+ if ((req->next = *p) != NULL) {
+ (*p)->prev = &req->next;
+ ++hash_collisions;
+ }
+ *p = req;
+ req->prev = p;
+
+ ++active_queries;
+}
+
+void
+hash_remove_request(PNATState pData, struct request *req)
+{
+ if (!req->prev) return;
+ if (req->next)
+ req->next->prev = req->prev;
+ *req->prev = req->next;
+ req->prev = NULL;
+
+ --active_queries;
+}
+
+struct request *
+hash_find_request(PNATState pData, unsigned short id)
+{
+ struct request *req = request_hash[HASH(id)];
+ Log2(("NAT: hash try to find req by id %d \n", id));
+
+ for (;;) {
+ if (!req) break;
+ if (req->id == id) break;
+ req = req->next;
+ }
+
+ return req;
+}
diff --git a/src/VBox/Devices/Network/slirp/ext.h b/src/VBox/Devices/Network/slirp/ext.h
new file mode 100644
index 00000000..e034496d
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/ext.h
@@ -0,0 +1,105 @@
+/** $Id: ext.h $ */
+/** @file
+ * NAT - some externals helpers
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef IN_BSD
+# define zone_mbuf slirp_zone_mbuf(pData)
+# define zone_clust slirp_zone_clust(pData)
+# define zone_pack slirp_zone_pack(pData)
+# define zone_jumbop slirp_zone_jumbop(pData)
+# define zone_jumbo9 slirp_zone_jumbo9(pData)
+# define zone_jumbo16 slirp_zone_jumbo16(pData)
+# define zone_ext_refcnt slirp_zone_ext_refcnt(pData)
+static inline uma_zone_t slirp_zone_mbuf(PNATState);
+static inline uma_zone_t slirp_zone_clust(PNATState);
+static inline uma_zone_t slirp_zone_pack(PNATState);
+static inline uma_zone_t slirp_zone_jumbop(PNATState);
+static inline uma_zone_t slirp_zone_jumbo9(PNATState);
+static inline uma_zone_t slirp_zone_jumbo16(PNATState);
+static inline uma_zone_t slirp_zone_ext_refcnt(PNATState);
+#else
+# undef zone_mbuf
+# undef zone_clust
+# undef zone_pack
+# undef zone_jumbop
+# undef zone_jumbo9
+# undef zone_jumbo16
+# undef zone_ext_refcnt
+
+# define zone_mbuf pData->zone_mbuf
+# define zone_clust pData->zone_clust
+# define zone_pack pData->zone_pack
+# define zone_jumbop pData->zone_jumbop
+# define zone_jumbo9 pData->zone_jumbo9
+# define zone_jumbo16 pData->zone_jumbo16
+# define zone_ext_refcnt pData->zone_ext_refcnt
+#endif
+
+#ifndef _EXT_H_
+#define _EXT_H_
+
+# define fprintf vbox_slirp_fprintf
+# define printf vbox_slirp_printf
+
+# ifndef vbox_slirp_printfV
+DECLINLINE(void) vbox_slirp_printV(char *format, va_list args)
+{
+ char buffer[1024];
+ memset(buffer, 0, 1024);
+ RTStrPrintfV(buffer, 1024, format, args);
+
+ LogRel(("NAT:EXT: %s\n", buffer));
+}
+# endif
+
+# ifndef vbox_slirp_printf
+DECLINLINE(void) vbox_slirp_printf(char *format, ...)
+{
+ va_list args;
+ va_start(args, format);
+ vbox_slirp_printV(format, args);
+ va_end(args);
+}
+# endif
+
+# ifndef vbox_slirp_fprintf
+DECLINLINE(void) vbox_slirp_fprintf(void *ignored, char *format, ...)
+{
+# ifdef LOG_ENABLED
+ va_list args;
+ NOREF(ignored);
+ va_start(args, format);
+ vbox_slirp_printV(format, args);
+ va_end(args);
+# else
+ NOREF(format);
+ NOREF(ignored);
+# endif
+}
+# endif
+
+#endif
+
diff --git a/src/VBox/Devices/Network/slirp/hostres.c b/src/VBox/Devices/Network/slirp/hostres.c
new file mode 100644
index 00000000..1ad55439
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/hostres.c
@@ -0,0 +1,1529 @@
+/* $Id: hostres.c $ */
+/** @file
+ * Host resolver
+ */
+
+/*
+ * Copyright (C) 2009-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef RT_OS_WINDOWS
+# include <netdb.h>
+#endif
+#include <iprt/assert.h>
+#include <iprt/ctype.h>
+#include <iprt/errcore.h>
+#include <slirp.h>
+
+#define isdigit(ch) RT_C_IS_DIGIT(ch)
+#define isalpha(ch) RT_C_IS_ALPHA(ch)
+
+#define DNS_CONTROL_PORT_NUMBER 53
+/* see RFC 1035(4.1.1) */
+struct dnsmsg_header
+{
+ uint16_t id;
+
+#ifdef RT_OS_WINDOWS
+ /* size of the type forces alignment */
+# define U16_BIT_FIELD_T uint16_t
+#else
+ /* gcc -pedantic complains about implementaion-defined types */
+# define U16_BIT_FIELD_T unsigned int
+#endif
+
+ /* XXX: endianness */
+ U16_BIT_FIELD_T rd:1;
+ U16_BIT_FIELD_T tc:1;
+ U16_BIT_FIELD_T aa:1;
+ U16_BIT_FIELD_T opcode:4;
+ U16_BIT_FIELD_T qr:1;
+ U16_BIT_FIELD_T rcode:4;
+ U16_BIT_FIELD_T Z:3;
+ U16_BIT_FIELD_T ra:1;
+
+ uint16_t qdcount;
+ uint16_t ancount;
+ uint16_t nscount;
+ uint16_t arcount;
+};
+AssertCompileSize(struct dnsmsg_header, 12);
+
+#define QR_Query 0
+#define QR_Response 1
+
+#define OpCode_Query 0
+
+#define RCode_NoError 0
+#define RCode_FormErr 1
+#define RCode_ServFail 2
+#define RCode_NXDomain 3
+#define RCode_NotImp 4
+#define RCode_Refused 5
+
+#define Type_A 1
+#define Type_CNAME 5
+#define Type_PTR 12
+#define Type_ANY 255
+
+#define Class_IN 1
+#define Class_ANY 255
+
+/* compressed label encoding */
+#define DNS_LABEL_PTR 0xc0
+
+#define DNS_MAX_UDP_LEN 512
+#define DNS_MAX_LABEL_LEN 63
+#define DNS_MAX_NAME_LEN 255
+
+
+/*
+ * A tree of labels.
+ *
+ * rfc1035#section-3.1
+ * rfc1035#section-4.1.4
+ */
+struct label
+{
+ const uint8_t *buf;
+ ssize_t off;
+ struct label *children;
+ struct label *sibling;
+};
+
+
+/*
+ * A structure to build DNS response.
+ */
+struct response
+{
+ PNATState pData;
+
+ uint32_t src;
+ uint16_t sport;
+
+ struct label *labels; /* already encoded in buf */
+ size_t qlen; /* original question */
+ size_t end; /* of data in buf */
+
+ /* continuous buffer to build the response */
+ uint8_t buf[DNS_MAX_UDP_LEN];
+};
+
+
+static int verify_header(PNATState pData, struct mbuf **pMBuf);
+static struct mbuf *refuse_mbuf(struct mbuf *m, unsigned int rcode);
+
+static int respond(struct response *res);
+static int resolve(struct response *res, uint16_t qtype, size_t qname);
+static int resolve_reverse(struct response *res, uint16_t qtype, size_t qname,
+ struct in_addr addr);
+
+static int refuse(struct response *res, unsigned int rcode);
+
+
+static ssize_t append_a(struct response *res, const char *name, struct in_addr addr);
+static ssize_t append_cname(struct response *res, const char *name, const char *cname);
+static ssize_t append_ptr(struct response *res, const char *inaddrname, const char *name);
+static ssize_t append_name_rr(struct response *res, const char *question, int type, const char *answer);
+static ssize_t append_rrhdr(struct response *res, const char *name, uint16_t type, uint32_t ttl);
+static ssize_t append_name(struct response *res, const char *name);
+static ssize_t append_u32(struct response *res, uint32_t value);
+static ssize_t append_u16(struct response *res, uint16_t value);
+static ssize_t append_u8(struct response *res, uint8_t value);
+static ssize_t append_bytes(struct response *res, uint8_t *p, size_t size);
+static ssize_t check_space(struct response *res, size_t size);
+
+static int get_in_addr_arpa(struct in_addr *paddr, struct label *root);
+static int labelstrcmp(struct label *l, const char *s);
+static void strnlabels(char *namebuf, size_t nbuflen, const uint8_t *msg, size_t off);
+
+/*static void LogLabelsTree(const char *before, struct label *l, const char *after); - unused */
+static void free_labels(struct label *root);
+
+#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
+static void alterHostentWithDataFromDNSMap(PNATState pData, struct hostent *h);
+static PDNSMAPPINGENTRY getDNSMapByName(PNATState pData, const char *name);
+static PDNSMAPPINGENTRY getDNSMapByAddr(PNATState pData, const uint32_t *pu32IpAddress);
+#endif
+
+#if 1 /* XXX */
+# define LogErr(args) Log2(args)
+# define LogDbg(args) Log3(args)
+#else
+# define LogErr(args) LogRel(args)
+# define LogDbg(args) LogRel(args)
+#endif
+
+
+static void hostres_async(struct response *res);
+static void hostres_slirp_reply(struct response *res);
+
+
+/*
+ * Host resolver is called on slirp thread from udp.c
+ */
+struct mbuf *
+hostresolver(PNATState pData, struct mbuf *m, uint32_t src, uint16_t sport)
+{
+ struct response *res;
+ u_int mlen;
+ int rc;
+
+ rc = verify_header(pData, &m);
+ if (RT_FAILURE(rc))
+ return m;
+
+ res = RTMemAllocZ(sizeof(*res));
+ if (res == NULL)
+ return refuse_mbuf(m, RCode_ServFail);
+
+ res->pData = pData;
+ res->src = src;
+ res->sport = sport;
+
+ mlen = m_length(m, NULL);
+ m_copydata(m, 0, mlen, (char *)res->buf);
+ res->end = res->qlen = mlen;
+
+ rc = slirp_call_hostres(pData->pvUser, NULL, 0,
+ RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
+ (PFNRT)hostres_async, 1, res);
+
+ if (RT_FAILURE(rc))
+ {
+ LogErr(("NAT: hostres: failed to post async request: %Rrc\n", rc));
+ RTMemFree(res);
+ return refuse_mbuf(m, RCode_ServFail);
+ }
+
+ m_freem(pData, m);
+ return NULL;
+}
+
+
+/*
+ * Do quick sanity-checks on the request before doing async
+ * resolution. If we don't like it, immediately drop or convert to
+ * response in place and bounce back the mbuf.
+ */
+static int
+verify_header(PNATState pData, struct mbuf **pMBuf)
+{
+ struct mbuf *m;
+ struct dnsmsg_header *pHdr;
+ size_t mlen;
+
+ m = *pMBuf;
+ mlen = m_length(m, NULL);
+
+ /*
+ * In theory we should have called
+ *
+ * m = m_pullup(m, sizeof(struct dnsmsg_header));
+ *
+ * here first (which should have been a nop), but the way mbufs
+ * are used in NAT will always cause a copy that will have no
+ * leading space. We can use m_copyup() instead, but if we are
+ * peeking under the hood anyway, we might as well just rely on
+ * the fact that this header will be contiguous.
+ */
+ pHdr = mtod(m, struct dnsmsg_header *);
+
+ if (RT_UNLIKELY(mlen < sizeof(*pHdr)))
+ {
+ LogErr(("NAT: hostres: packet too small: %zu bytes\n", mlen));
+ goto drop; /* can't even refuse it */
+ }
+
+ if (RT_UNLIKELY(mlen > DNS_MAX_UDP_LEN))
+ {
+ LogErr(("NAT: hostres: packet too large: %zu bytes\n", mlen));
+ goto drop; /* don't echo back huge packets */
+ }
+
+ if (RT_UNLIKELY(pHdr->qr != QR_Query))
+ {
+ LogErr(("NAT: hostres: unexpected response\n"));
+ goto drop; /* ignore */
+ }
+
+ if (RT_UNLIKELY(pHdr->opcode != OpCode_Query))
+ {
+ LogErr(("NAT: hostres: unsupported opcode %d\n", pHdr->opcode));
+ refuse_mbuf(m, RCode_NotImp);
+ return VERR_PARSE_ERROR;
+ }
+
+ if (RT_UNLIKELY(pHdr->qdcount != RT_H2N_U16_C(1)))
+ {
+ LogErr(("NAT: hostres: multiple questions\n"));
+ refuse_mbuf(m, RCode_FormErr);
+ return VERR_PARSE_ERROR;
+ }
+
+ if (RT_UNLIKELY(pHdr->ancount != 0))
+ {
+ LogErr(("NAT: hostres: answers in query\n"));
+ refuse_mbuf(m, RCode_FormErr);
+ return VERR_PARSE_ERROR;
+ }
+
+ /* XXX: let it fail when we parse it? */
+ if (RT_UNLIKELY(mlen < sizeof(*pHdr)
+ + /* qname */ 1
+ + /* qtype */ 2
+ + /* qclass */ 2))
+ {
+ LogErr(("NAT: hostres: packet too small: %zu bytes\n", mlen));
+ refuse_mbuf(m, RCode_FormErr);
+ return VERR_PARSE_ERROR;
+ }
+
+ return VINF_SUCCESS;
+
+ drop:
+ if (m != NULL)
+ m_freem(pData, m);
+ *pMBuf = NULL;
+ return VERR_PARSE_ERROR;
+}
+
+
+/*
+ * Turn the request in mbuf into an error response. This is used on
+ * slirp thread for pre-checks before we do async resolution.
+ */
+static struct mbuf *
+refuse_mbuf(struct mbuf *m, unsigned int rcode)
+{
+ struct dnsmsg_header *pHdr;
+
+ pHdr = mtod(m, struct dnsmsg_header *);
+ pHdr->qr = QR_Response;
+ pHdr->rcode = rcode;
+ pHdr->ra = 1;
+ pHdr->aa = 0;
+
+ return m;
+}
+
+
+/*
+ * Actuall resolution runs on the dedicated host resolver thread.
+ */
+static void
+hostres_async(struct response *res)
+{
+ int rc;
+
+ /* build reply in res->buf[] */
+ respond(res);
+
+ free_labels(res->labels);
+
+ rc = slirp_call(res->pData->pvUser, NULL, 0,
+ RTREQFLAGS_VOID | RTREQFLAGS_NO_WAIT,
+ (PFNRT)hostres_slirp_reply, 1, res);
+
+ if (RT_FAILURE(rc))
+ {
+ LogErr(("NAT: hostres: failed to post async reply: %Rrc\n", rc));
+ RTMemFree(res);
+ }
+}
+
+
+/*
+ * We are back to the slirp thread to send the reply.
+ */
+static void
+hostres_slirp_reply(struct response *res)
+{
+ PNATState pData = res->pData;
+ struct sockaddr_in src, dst;
+ struct mbuf *m = NULL;
+ size_t mlen;
+ int ok;
+
+ mlen = if_maxlinkhdr + sizeof(struct ip) + sizeof(struct udphdr);
+ mlen += res->end;
+
+ if (mlen <= MHLEN)
+ {
+ m = m_gethdr(pData, M_NOWAIT, MT_HEADER);
+ }
+ else
+ {
+ void *pvBuf; /* ignored */
+ size_t cbBuf;
+
+ m = slirp_ext_m_get(pData, mlen, &pvBuf, &cbBuf);
+ }
+
+ if (m == NULL)
+ goto out;
+
+ /* reserve leading space for ethernet header */
+ m->m_data += if_maxlinkhdr;
+
+ /* reserve leading space for protocol headers */
+ m->m_pkthdr.header = mtod(m, void *);
+ m->m_data += sizeof(struct ip) + sizeof(struct udphdr);
+
+ m->m_len = 0;
+ ok = m_append(pData, m, (int)res->end, (c_caddr_t)res->buf);
+ if (!ok)
+ {
+ m_freem(pData, m);
+ goto out;
+ }
+
+ src.sin_addr.s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_DNS);
+ src.sin_port = RT_H2N_U16_C(53);
+ dst.sin_addr.s_addr = res->src;
+ dst.sin_port = res->sport;
+
+ udp_output2(pData, NULL, m, &src, &dst, IPTOS_LOWDELAY);
+
+ out:
+ RTMemFree(res);
+}
+
+
+static int
+respond(struct response *res)
+{
+ struct dnsmsg_header *pHdr;
+ size_t off;
+ size_t qname;
+ uint16_t qtype, qclass;
+ struct in_addr in_addr_arpa;
+ struct label *l;
+
+ /* convert header to response */
+ pHdr = (struct dnsmsg_header *)res->buf;
+ pHdr->qr = QR_Response;
+ pHdr->rcode = RCode_NoError;
+ pHdr->ra = 1; /* the host provides recursion */
+ pHdr->aa = 0; /* we are not authoritative */
+ pHdr->Z = 0; /* clear rfc2535 dnssec bits */
+
+ off = sizeof(*pHdr);
+ qname = off;
+
+ /*
+ * Parse/verify QNAME and collect the suffixes to be used for
+ * compression in the answer.
+ */
+ while (off < res->qlen) {
+ size_t loff, llen;
+ uint8_t c;
+
+ c = res->buf[off];
+
+ /*
+ * There's just one question with just one name, so there are
+ * no other labels it can point to. Thus all well-formed
+ * names with a pointer can only be infinite loops.
+ */
+ if ((c & DNS_LABEL_PTR) == DNS_LABEL_PTR)
+ {
+ LogErr(("NAT: hostres: label pointer in the qname\n"));
+ return refuse(res, RCode_FormErr);
+ }
+
+ if ((c & DNS_LABEL_PTR) != 0)
+ {
+ LogErr(("NAT: hostres: unexpected high bits\n"));
+ return refuse(res, RCode_FormErr);
+ }
+
+ /*
+ * label of "llen" chars starts at offset "loff".
+ */
+ loff = off;
+ llen = c;
+ ++off;
+
+ if (loff + 1 + llen > res->qlen)
+ {
+ LogErr(("NAT: hostres: length byte points beyound packet boundary\n"));
+ return refuse(res, RCode_FormErr);
+ }
+
+ if (llen == 0) /* end of the label list */
+ {
+ break;
+ }
+
+ /* do only minimal verification of the label */
+ while (off < loff + 1 + llen)
+ {
+ c = res->buf[off];
+ ++off;
+
+ if (c == '.')
+ {
+ LogErr(("NAT: hostres: dot inside label\n"));
+ return refuse(res, RCode_FormErr);
+ }
+
+ if (c == '\0')
+ {
+ LogErr(("NAT: hostres: nul byte inside label\n"));
+ return refuse(res, RCode_FormErr);
+ }
+ }
+
+ l = RTMemAllocZ(sizeof(*l));
+ l->buf = res->buf;
+ l->off = loff;
+ l->children = res->labels;
+ res->labels = l;
+ }
+
+ /*
+ * QTYPE and QCLASS
+ */
+ if (RT_UNLIKELY(off + 4 > res->qlen))
+ {
+ LogErr(("NAT: hostres: question too short\n"));
+ return refuse(res, RCode_FormErr);
+ }
+
+ memcpy(&qtype, &res->buf[off], sizeof(qtype));
+ qtype = RT_N2H_U16(qtype);
+ off += sizeof(qtype);
+
+ memcpy(&qclass, &res->buf[off], sizeof(qclass));
+ qclass = RT_N2H_U16(qclass);
+ off += sizeof(qclass);
+
+ if ( qclass != Class_IN
+ && qclass != Class_ANY)
+ {
+ LogErr(("NAT: hostres: unsupported qclass %d\n", qclass));
+ return refuse(res, RCode_NoError);
+ }
+
+ if ( qtype != Type_A
+ && qtype != Type_CNAME
+ && qtype != Type_PTR
+ && qtype != Type_ANY)
+ {
+ LogErr(("NAT: hostres: unsupported qtype %d\n", qtype));
+ return refuse(res, RCode_NoError);
+ }
+
+
+ /**
+ * Check if there's anything after the question. If query says it
+ * has authority or additional records, ignore and drop them
+ * without parsing.
+ *
+ * We have already rejected queries with answer(s) before. We
+ * have ensured that qname in the question doesn't contain
+ * pointers, so truncating the buffer is safe.
+ */
+ if (off < res->qlen)
+ {
+ ssize_t trailer = res->qlen - off;
+
+ LogDbg(("NAT: hostres: question %zu < mlen %zu\n", off, res->qlen));
+
+ if (pHdr->nscount == 0 && pHdr->arcount == 0)
+ {
+ LogErr(("NAT: hostres: unexpected %d bytes after the question\n", trailer));
+ return refuse(res, RCode_FormErr);
+ }
+
+ LogDbg(("NAT: hostres: ignoring %d bytes of %s%s%s records\n",
+ trailer,
+ pHdr->nscount != 0 ? "authority" : "",
+ pHdr->nscount != 0 && pHdr->arcount != 0 ? " and " : "",
+ pHdr->arcount != 0 ? "additional" : ""));
+
+ res->qlen -= trailer;
+ res->end = res->qlen;
+
+ pHdr->nscount = 0;
+ pHdr->arcount = 0;
+ }
+
+
+ /*
+ * Check for IN-ADDR.ARPA. Use the fact that res->labels at this
+ * point contains only the qname, so we have easy top-down access
+ * to its components.
+ */
+ if (get_in_addr_arpa(&in_addr_arpa, res->labels))
+ return resolve_reverse(res, qtype, qname, in_addr_arpa);
+ else
+ return resolve(res, qtype, qname);
+}
+
+
+static int
+resolve(struct response *res, uint16_t qtype, size_t qname)
+{
+ struct dnsmsg_header *pHdr;
+ struct hostent *h;
+ struct hostent hostent;
+ char *h_aliases[1];
+ char *h_addr_list[2];
+ size_t oend;
+ size_t nanswers;
+ ssize_t nbytes;
+ int i;
+
+ char name[DNS_MAX_NAME_LEN+1];
+
+ pHdr = (struct dnsmsg_header *)res->buf;
+ nanswers = 0;
+ oend = res->end;
+
+ strnlabels(name, sizeof(name), res->buf, qname);
+ LogDbg(("NAT: hostres: qname=\"%s\"\n", name));
+
+ if (qtype != Type_A && qtype != Type_CNAME && qtype != Type_ANY)
+ {
+ goto out; /* NB: RCode_NoError without an answer, not RCode_NXDomain */
+ }
+
+ h = NULL;
+#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
+ {
+ PDNSMAPPINGENTRY pDNSMapingEntry = getDNSMapByName(res->pData, name);
+ if (pDNSMapingEntry != NULL)
+ {
+ LogDbg(("NAT: hostres: %s resolved from %s%s\n",
+ name,
+ pDNSMapingEntry->fPattern ? "pattern " : "mapping",
+ pDNSMapingEntry->fPattern ? pDNSMapingEntry->pszName : ""));
+
+ if (qtype == Type_CNAME)
+ {
+ goto out;
+ }
+
+ hostent.h_name = name;
+ hostent.h_aliases = h_aliases;
+ h_aliases[0] = NULL;
+ hostent.h_addrtype = AF_INET;
+ hostent.h_length = sizeof(RTNETADDRIPV4);
+ hostent.h_addr_list = h_addr_list;
+ h_addr_list[0] = (char *)&pDNSMapingEntry->u32IpAddress;
+ h_addr_list[1] = NULL;
+
+ h = &hostent;
+ }
+ }
+#endif
+
+ if (h == NULL)
+ {
+ h = gethostbyname(name);
+ }
+
+ if (h == NULL)
+ {
+ /* LogErr: h_errno */
+ return refuse(res, RCode_NXDomain);
+ }
+
+ if (h->h_length != sizeof(RTNETADDRIPV4))
+ {
+ /* Log: what kind of address did we get?! */
+ goto out;
+ }
+
+ if ( h->h_addr_list == NULL
+ || h->h_addr_list[0] == NULL)
+ {
+ /* Log: shouldn't happen */
+ goto out;
+ }
+
+#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
+ alterHostentWithDataFromDNSMap(res->pData, h);
+#endif
+
+ /*
+ * Emit CNAME record if canonical name differs from the qname.
+ */
+ if ( h->h_name != NULL
+ && RTStrICmp(h->h_name, name) != 0)
+ {
+ LogDbg(("NAT: hostres: %s CNAME %s\n", name, h->h_name));
+ nbytes = append_cname(res, name, h->h_name);
+ if (nbytes > 0)
+ {
+ ++nanswers;
+ }
+ else
+ {
+ LogErr(("NAT: hostres: failed to add %s CNAME %s\n",
+ name, h->h_name));
+ if (nbytes < 0)
+ return refuse(res, RCode_ServFail);
+ else
+ {
+ pHdr->tc = 1;
+ goto out;
+ }
+ }
+
+ /*
+ * rfc1034#section-3.6.2 - ... a type CNAME or * query should
+ * return just the CNAME.
+ */
+ if (qtype == Type_CNAME || qtype == Type_ANY)
+ goto out;
+ }
+ else if (qtype == Type_CNAME)
+ {
+ LogDbg(("NAT: hostres: %s is already canonical\n", name));
+ goto out; /* NB: RCode_NoError without an answer, not RCode_NXDomain */
+ }
+
+ /*
+ * Emit A records.
+ */
+ for (i = 0; h->h_addr_list[i] != NULL; ++i)
+ {
+ const char *cname = h->h_name ? h->h_name : name;
+ struct in_addr addr;
+
+ addr.s_addr = *(uint32_t *)h->h_addr_list[i];
+ nbytes = append_a(res, cname, addr);
+
+ if (nbytes > 0)
+ {
+ ++nanswers;
+ }
+ else
+ {
+ LogErr(("NAT: hostres: failed to add %s A %RTnaipv4\n",
+ cname, addr.s_addr));
+ if (nbytes < 0)
+ return refuse(res, RCode_ServFail);
+ else
+ {
+ pHdr->tc = 1;
+ goto out;
+ }
+ }
+ }
+
+#if 0
+ /*
+ * It's not clear what to do with h_aliases.
+ *
+ * For names from the DNS it seems to contain the chain of CNAMEs,
+ * starting with the original qname from the question. So for
+ * them we'd need to reply with a chain of:
+ *
+ * h_aliases[i] CNAME h_aliases[i+1]
+ *
+ * OTOH, for the names from the hosts file it seems to contain all
+ * the names except the first one (which is considered primary and
+ * is reported as h_name). In which case the reply should be:
+ *
+ * h_aliases[i] CNAME h_name
+ *
+ * Obviously, we have no idea how the name was resolved, so we
+ * generate at most one CNAME for h_host (if differs) and ignore
+ * aliases altogehter.
+ */
+ for (i = 0; h->h_aliases[i] != NULL; ++i)
+ {
+ LogDbg(("NAT: hostres: ... %s\n", h->h_aliases[i]));
+ }
+#endif
+
+ out:
+ pHdr->ancount = RT_H2N_U16((uint16_t)nanswers);
+ return VINF_SUCCESS;
+}
+
+
+static int
+resolve_reverse(struct response *res, uint16_t qtype, size_t qname,
+ struct in_addr in_addr_arpa)
+{
+ struct dnsmsg_header *pHdr;
+ struct hostent *h;
+ struct hostent hostent;
+ char *h_aliases[1];
+ char *h_addr_list[2];
+ size_t oend;
+ size_t nanswers;
+ ssize_t nbytes;
+
+ pHdr = (struct dnsmsg_header *)res->buf;
+ nanswers = 0;
+ oend = res->end;
+
+ LogDbg(("NAT: hostres: %RTnaipv4\n", in_addr_arpa.s_addr));
+
+ if (qtype != Type_PTR && qtype != Type_ANY)
+ {
+ /* can't answer CNAME to PTR queries using gethostby* */
+ goto out; /* NB: RCode_NoError without an answer, not RCode_NXDomain */
+ }
+
+ h = NULL;
+#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
+ /*
+ * If the address in the question is unknown to the real resolver
+ * but has a mapping, and if we do the real lookup first, then the
+ * guest will time out before our lookup times out and even though
+ * we reply with the answer from the map, the answer will be lost.
+ */
+ {
+ PDNSMAPPINGENTRY pReverseMapping = getDNSMapByAddr(res->pData, (const uint32_t *)&in_addr_arpa.s_addr);
+ if (pReverseMapping != NULL)
+ {
+ LogDbg(("NAT: hostres: %RTnaipv4 resolved from mapping\n",
+ in_addr_arpa.s_addr));
+
+ hostent.h_name = pReverseMapping->pszName;
+ hostent.h_aliases = h_aliases;
+ h_aliases[0] = NULL;
+ hostent.h_addrtype = AF_INET;
+ hostent.h_length = sizeof(RTNETADDRIPV4);
+ hostent.h_addr_list = h_addr_list;
+ h_addr_list[0] = (char *)&in_addr_arpa.s_addr;
+ h_addr_list[1] = NULL;
+
+ h = &hostent;
+ }
+ }
+#endif
+
+ if (h == NULL)
+ {
+#ifdef RT_OS_WINDOWS
+ h = gethostbyaddr((const char *)&in_addr_arpa, sizeof(struct in_addr), AF_INET);
+#else
+ h = gethostbyaddr(&in_addr_arpa, sizeof(struct in_addr), AF_INET);
+#endif
+ }
+
+ if (h == NULL)
+ {
+ /* LogErr: h_errno */
+ return refuse(res, RCode_NXDomain);
+ }
+
+ if (h->h_name != NULL)
+ {
+ char name[DNS_MAX_NAME_LEN+1];
+ strnlabels(name, sizeof(name), res->buf, qname);
+
+ LogDbg(("NAT: hostres: %s PTR %s\n", name, h->h_name));
+ nbytes = append_ptr(res, name, h->h_name);
+ if (nbytes > 0)
+ {
+ ++nanswers;
+ }
+ else
+ {
+ LogErr(("NAT: hostres: failed to add %s PTR %s\n",
+ name, h->h_name));
+ if (nbytes < 0)
+ return refuse(res, RCode_ServFail);
+ else
+ {
+ pHdr->tc = 1;
+ goto out;
+ }
+ }
+ }
+
+ out:
+ pHdr->ancount = RT_H2N_U16((uint16_t)nanswers);
+ return VINF_SUCCESS;
+}
+
+
+static int
+refuse(struct response *res, unsigned int rcode)
+{
+ struct dnsmsg_header *pHdr = (struct dnsmsg_header *)res->buf;
+ pHdr->rcode = rcode;
+
+ return VINF_SUCCESS;
+}
+
+
+#define APPEND_PROLOGUE() \
+ ssize_t size = -1; \
+ size_t oend = res->end; \
+ ssize_t nbytes; \
+ do {} while (0)
+
+#define CHECKED(_append) \
+ do { \
+ nbytes = (_append); \
+ if (RT_UNLIKELY(nbytes <= 0)) \
+ { \
+ if (nbytes == 0) \
+ size = 0; \
+ goto out; \
+ } \
+ } while (0)
+
+#define APPEND_EPILOGUE() \
+ do { \
+ size = res->end - oend; \
+ out: \
+ if (RT_UNLIKELY(size <= 0)) \
+ res->end = oend; \
+ return size; \
+ } while (0)
+
+
+/*
+ * A RR - rfc1035#section-3.4.1
+ */
+static ssize_t
+append_a(struct response *res, const char *name, struct in_addr addr)
+{
+ APPEND_PROLOGUE();
+
+ CHECKED( append_rrhdr(res, name, Type_A, 3600) );
+ CHECKED( append_u16(res, RT_H2N_U16_C(sizeof(addr))) );
+ CHECKED( append_u32(res, addr.s_addr) );
+
+ APPEND_EPILOGUE();
+}
+
+
+/*
+ * CNAME RR - rfc1035#section-3.3.1
+ */
+static ssize_t
+append_cname(struct response *res, const char *name, const char *cname)
+{
+ return append_name_rr(res, name, Type_CNAME, cname);
+}
+
+
+/*
+ * PTR RR - rfc1035#section-3.3.12
+ */
+static ssize_t
+append_ptr(struct response *res, const char *inaddrname, const char *name)
+{
+ return append_name_rr(res, inaddrname, Type_PTR, name);
+}
+
+
+static ssize_t
+append_name_rr(struct response *res, const char *question,
+ int type, const char *answer)
+{
+ size_t rdlpos;
+ uint16_t rdlength;
+
+ APPEND_PROLOGUE();
+
+ CHECKED( append_rrhdr(res, question, type, 3600) );
+
+ rdlpos = res->end;
+ CHECKED( append_u16(res, 0) ); /* RDLENGTH placeholder */
+
+ CHECKED( append_name(res, answer) );
+
+ rdlength = RT_H2N_U16(nbytes);
+ memcpy(&res->buf[rdlpos], &rdlength, sizeof(rdlength));
+
+ APPEND_EPILOGUE();
+}
+
+
+/*
+ * Append common RR header, up to but not including RDLENGTH and RDATA
+ * proper (rfc1035#section-3.2.1).
+ */
+static ssize_t
+append_rrhdr(struct response *res, const char *name, uint16_t type, uint32_t ttl)
+{
+ APPEND_PROLOGUE();
+
+ CHECKED( append_name(res, name) );
+ CHECKED( append_u16(res, RT_H2N_U16(type)) );
+ CHECKED( append_u16(res, RT_H2N_U16_C(Class_IN)) );
+ CHECKED( append_u32(res, RT_H2N_U32(ttl)) );
+
+ APPEND_EPILOGUE();
+}
+
+
+static ssize_t
+append_name(struct response *res, const char *name)
+{
+ ssize_t size, nbytes;
+ struct label *root;
+ struct label *haystack, *needle;
+ struct label *head, **neck;
+ struct label *tail, **graft;
+ uint8_t *buf;
+ size_t wr, oend;
+ const char *s;
+
+ size = -1;
+ oend = res->end;
+
+ /**
+ * Split new name into a list of labels encoding it into the
+ * temporary buffer.
+ */
+ root = NULL;
+
+ buf = RTMemAllocZ(strlen(name) + 1);
+ if (buf == NULL)
+ return -1;
+ wr = 0;
+
+ s = name;
+ while (*s != '\0') {
+ const char *part;
+ size_t poff, plen;
+ struct label *l;
+
+ part = s;
+ while (*s != '\0' && *s != '.')
+ ++s;
+
+ plen = s - part;
+
+ if (plen > DNS_MAX_LABEL_LEN)
+ {
+ LogErr(("NAT: hostres: name component too long\n"));
+ goto out;
+ }
+
+ if (*s == '.')
+ {
+ if (plen == 0)
+ {
+ LogErr(("NAT: hostres: empty name component\n"));
+ goto out;
+ }
+
+ ++s;
+ }
+
+ poff = wr;
+
+ buf[poff] = (uint8_t)plen; /* length byte */
+ ++wr;
+
+ memcpy(&buf[wr], part, plen); /* label text */
+ wr += plen;
+
+ l = RTMemAllocZ(sizeof(*l));
+ if (l == NULL)
+ goto out;
+
+ l->buf = buf;
+ l->off = poff;
+ l->children = root;
+ root = l;
+ }
+
+
+ /**
+ * Search for a tail that is already encoded in the message.
+ */
+ neck = &root; /* where needle head is connected */
+ needle = root;
+
+ tail = NULL; /* tail in the haystack */
+ graft = &res->labels;
+ haystack = res->labels;
+
+ while (needle != NULL && haystack != NULL)
+ {
+ size_t nlen, hlen;
+
+ nlen = needle->buf[needle->off];
+ Assert((nlen & DNS_LABEL_PTR) == 0);
+
+ hlen = haystack->buf[haystack->off];
+ Assert((hlen & DNS_LABEL_PTR) == 0);
+
+ if ( nlen == hlen
+ && RTStrNICmp((char *)&needle->buf[needle->off+1],
+ (char *)&haystack->buf[haystack->off+1],
+ nlen) == 0)
+ {
+ neck = &needle->children;
+ needle = needle->children;
+
+ tail = haystack;
+ graft = &haystack->children;
+ haystack = haystack->children;
+ }
+ else
+ {
+ haystack = haystack->sibling;
+ }
+ }
+
+
+ /**
+ * Head contains (in reverse) the prefix that needs to be encoded
+ * and added to the haystack. Tail points to existing suffix that
+ * can be compressed to a pointer into the haystack.
+ */
+ head = *neck;
+ if (head != NULL)
+ {
+ struct label *l;
+ size_t nlen, pfxlen, pfxdst;
+
+ nlen = needle->buf[head->off]; /* last component */
+ pfxlen = head->off + 1 + nlen; /* all prefix */
+ pfxdst = res->end; /* in response buffer */
+
+ /* copy new prefix into response buffer */
+ nbytes = append_bytes(res, buf, pfxlen);
+ if (nbytes <= 0)
+ {
+ if (nbytes == 0)
+ size = 0;
+ goto out;
+ }
+
+ /* adjust labels to point to the response */
+ for (l = head; l != NULL; l = l->children)
+ {
+ l->buf = res->buf;
+ l->off += pfxdst;
+ }
+
+ *neck = NULL; /* decapitate */
+
+ l = *graft; /* graft to the labels tree */
+ *graft = head;
+ head->sibling = l;
+ }
+
+ if (tail == NULL)
+ nbytes = append_u8(res, 0);
+ else
+ nbytes = append_u16(res, RT_H2N_U16((DNS_LABEL_PTR << 8) | tail->off));
+ if (nbytes <= 0)
+ {
+ if (nbytes == 0)
+ size = 0;
+ goto out;
+ }
+
+ size = res->end - oend;
+ out:
+ if (RT_UNLIKELY(size <= 0))
+ res->end = oend;
+ free_labels(root);
+ RTMemFree(buf);
+ return size;
+}
+
+
+static ssize_t
+append_u32(struct response *res, uint32_t value)
+{
+ return append_bytes(res, (uint8_t *)&value, sizeof(value));
+}
+
+
+static ssize_t
+append_u16(struct response *res, uint16_t value)
+{
+ return append_bytes(res, (uint8_t *)&value, sizeof(value));
+}
+
+
+static ssize_t
+append_u8(struct response *res, uint8_t value)
+{
+ return append_bytes(res, &value, sizeof(value));
+}
+
+
+static ssize_t
+append_bytes(struct response *res, uint8_t *p, size_t size)
+{
+ if (check_space(res, size) == 0)
+ return 0;
+
+ memcpy(&res->buf[res->end], p, size);
+ res->end += size;
+ return size;
+}
+
+
+static ssize_t
+check_space(struct response *res, size_t size)
+{
+ if ( size > sizeof(res->buf)
+ || res->end > sizeof(res->buf) - size)
+ return 0;
+
+ return size;
+}
+
+
+static int
+get_in_addr_arpa(struct in_addr *paddr, struct label *root)
+{
+ RTNETADDRIPV4 addr;
+ struct label *l;
+ int i;
+ RT_ZERO(addr); /* makes MSC happy*/
+
+ l = root;
+ if (l == NULL || labelstrcmp(l, "arpa") != 0)
+ return 0;
+
+ l = l->children;
+ if (l == NULL || labelstrcmp(l, "in-addr") != 0)
+ return 0;
+
+ for (i = 0; i < 4; ++i)
+ {
+ char buf[4];
+ size_t llen;
+ int rc;
+ uint8_t octet;
+
+ l = l->children;
+ if (l == NULL)
+ return 0;
+
+ llen = l->buf[l->off];
+ Assert((llen & DNS_LABEL_PTR) == 0);
+
+ /* valid octet values are at most 3 digits */
+ if (llen > 3)
+ return 0;
+
+ /* copy to avoid dealing with trailing bytes */
+ memcpy(buf, &l->buf[l->off + 1], llen);
+ buf[llen] = '\0';
+
+ rc = RTStrToUInt8Full(buf, 10, &octet);
+ if (rc != VINF_SUCCESS)
+ return 0;
+
+ addr.au8[i] = octet;
+ }
+
+ if (l->children != NULL)
+ return 0; /* too many components */
+
+ if (paddr != NULL)
+ paddr->s_addr = addr.u;
+
+ return 1;
+}
+
+
+/*
+ * Compare label with string.
+ */
+static int
+labelstrcmp(struct label *l, const char *s)
+{
+ size_t llen;
+
+ llen = l->buf[l->off];
+ Assert((llen & DNS_LABEL_PTR) == 0);
+
+ return RTStrNICmp((char *)&l->buf[l->off + 1], s, llen);
+}
+
+
+/*
+ * Convert a chain of labels to a C string.
+ *
+ * I'd rather use a custom formatter for e.g. %R[label] , but it needs
+ * two arguments and microsoft VC doesn't support compound literals.
+ */
+static void
+strnlabels(char *namebuf, size_t nbuflen, const uint8_t *msg, size_t off)
+{
+ size_t cb;
+ size_t llen;
+
+ namebuf[0] = '\0';
+ cb = 0;
+
+ llen = 0;
+
+ while (cb < nbuflen - 1) {
+ llen = msg[off];
+ if ((llen & DNS_LABEL_PTR) == DNS_LABEL_PTR)
+ {
+ off = ((llen & ~DNS_LABEL_PTR) << 8) | msg[off + 1];
+ llen = msg[off];
+ }
+
+ /* pointers to pointers should not happen */
+ if ((llen & DNS_LABEL_PTR) != 0)
+ {
+ cb += RTStrPrintf(namebuf + cb, nbuflen - cb, "[???]");
+ return;
+ }
+
+ if (llen == 0)
+ {
+ if (namebuf[0] == '\0')
+ cb += RTStrPrintf(namebuf + cb, nbuflen - cb, ".");
+ break;
+ }
+
+ if (namebuf[0] != '\0')
+ cb += RTStrPrintf(namebuf + cb, nbuflen - cb, ".");
+
+ cb += RTStrPrintf(namebuf + cb, nbuflen - cb,
+ "%.*s", llen, (char *)&msg[off+1]);
+ off = off + 1 + llen;
+ }
+}
+
+
+#if 0 /* unused */
+static void
+LogLabelsTree(const char *before, struct label *l, const char *after)
+{
+ size_t llen;
+
+ if (before != NULL)
+ LogDbg(("%s", before));
+
+ if (l == NULL)
+ {
+ LogDbg(("NULL%s", after ? after : ""));
+ return;
+ }
+
+ if (l->children)
+ LogDbg(("("));
+
+ if (l->buf != NULL)
+ {
+ llen = l->buf[l->off];
+ if ((llen & DNS_LABEL_PTR) == 0)
+ {
+ LogDbg(("\"%.*s\"@%zu", llen, &l->buf[l->off+1], l->off));
+ }
+ else
+ {
+ LogDbg(("<invalid byte 0t%zu/0x%zf at offset %zd>",
+ llen, llen, l->off));
+ }
+ }
+ else
+ {
+ LogDbg(("<*>"));
+ }
+
+ if (l->children)
+ LogLabelsTree(" ", l->children, ")");
+
+ if (l->sibling)
+ LogLabelsTree(" ", l->sibling, NULL);
+
+ if (after != NULL)
+ LogDbg(("%s", after));
+}
+#endif /* unused */
+
+
+static void
+free_labels(struct label *root)
+{
+ struct label TOP; /* traverse the tree with pointer reversal */
+ struct label *b, *f;
+
+ if (root == NULL)
+ return;
+
+ RT_ZERO(TOP);
+
+ b = &TOP;
+ f = root;
+
+ while (f != &TOP) {
+ if (f->children) { /* recurse left */
+ struct label *oldf = f;
+ struct label *newf = f->children;
+ oldf->children = b; /* reverse the pointer */
+ b = oldf;
+ f = newf;
+ }
+ else if (f->sibling) { /* turn right */
+ f->children = f->sibling;
+ f->sibling = NULL;
+ }
+ else { /* backtrack */
+ struct label *oldf = f; /* garbage */
+ struct label *oldb = b;
+ b = oldb->children;
+ oldb->children = NULL; /* oldf, but we are g/c'ing it */
+ f = oldb;
+
+ RTMemFree(oldf);
+ }
+ }
+}
+
+#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
+void
+slirp_add_host_resolver_mapping(PNATState pData,
+ const char *pszHostName, bool fPattern,
+ uint32_t u32HostIP)
+{
+ LogRel(("ENTER: pszHostName:%s%s, u32HostIP:%RTnaipv4\n",
+ pszHostName ? pszHostName : "(null)",
+ fPattern ? " (pattern)" : "",
+ u32HostIP));
+
+ if ( pszHostName != NULL
+ && u32HostIP != INADDR_ANY
+ && u32HostIP != INADDR_BROADCAST)
+ {
+ PDNSMAPPINGENTRY pDnsMapping = RTMemAllocZ(sizeof(DNSMAPPINGENTRY));
+ if (!pDnsMapping)
+ {
+ LogFunc(("Can't allocate DNSMAPPINGENTRY\n"));
+ LogFlowFuncLeave();
+ return;
+ }
+
+ pDnsMapping->u32IpAddress = u32HostIP;
+ pDnsMapping->fPattern = fPattern;
+ pDnsMapping->pszName = RTStrDup(pszHostName);
+
+ if (pDnsMapping->pszName == NULL)
+ {
+ LogFunc(("Can't allocate enough room for host name\n"));
+ RTMemFree(pDnsMapping);
+ LogFlowFuncLeave();
+ return;
+ }
+
+ if (fPattern) /* there's no case-insensitive pattern-match function */
+ RTStrToLower(pDnsMapping->pszName);
+
+ STAILQ_INSERT_TAIL(fPattern ? &pData->DNSMapPatterns : &pData->DNSMapNames,
+ pDnsMapping, MapList);
+
+ LogRel(("NAT: User-defined mapping %s%s = %RTnaipv4 is registered\n",
+ pDnsMapping->pszName,
+ pDnsMapping->fPattern ? " (pattern)" : "",
+ pDnsMapping->u32IpAddress));
+ }
+ LogFlowFuncLeave();
+}
+
+
+static PDNSMAPPINGENTRY
+getDNSMapByName(PNATState pData, const char *pszName)
+{
+ PDNSMAPPINGENTRY pDNSMapingEntry;
+ char *pszNameLower;
+
+ pszNameLower = RTStrDup(pszName);
+ if (RT_UNLIKELY(pszNameLower == NULL))
+ return NULL;
+ RTStrToLower(pszNameLower);
+
+ STAILQ_FOREACH(pDNSMapingEntry, &pData->DNSMapNames, MapList)
+ {
+ if (RTStrICmp(pDNSMapingEntry->pszName, pszNameLower) == 0)
+ goto done;
+ }
+
+ STAILQ_FOREACH(pDNSMapingEntry, &pData->DNSMapPatterns, MapList)
+ {
+ if (RTStrSimplePatternMultiMatch(pDNSMapingEntry->pszName, RTSTR_MAX,
+ pszNameLower, RTSTR_MAX, NULL))
+ goto done;
+ }
+
+ done:
+ RTStrFree(pszNameLower);
+ return pDNSMapingEntry;
+}
+
+
+static PDNSMAPPINGENTRY
+getDNSMapByAddr(PNATState pData, const uint32_t *pu32IpAddress)
+{
+ PDNSMAPPINGENTRY pDNSMapingEntry;
+
+ if (pu32IpAddress == NULL)
+ return NULL;
+
+ STAILQ_FOREACH(pDNSMapingEntry, &pData->DNSMapNames, MapList)
+ {
+ if (pDNSMapingEntry->u32IpAddress == *pu32IpAddress)
+ return pDNSMapingEntry;
+ }
+
+ return NULL;
+}
+
+
+static void
+alterHostentWithDataFromDNSMap(PNATState pData, struct hostent *h)
+{
+ PDNSMAPPINGENTRY pDNSMapingEntry = NULL;
+ char **ppszAlias;
+
+ if (h->h_name != NULL)
+ {
+ pDNSMapingEntry = getDNSMapByName(pData, h->h_name);
+ if (pDNSMapingEntry != NULL)
+ goto done;
+ }
+
+ for (ppszAlias = h->h_aliases; *ppszAlias != NULL; ++ppszAlias)
+ {
+ pDNSMapingEntry = getDNSMapByName(pData, *ppszAlias);
+ if (pDNSMapingEntry != NULL)
+ goto done;
+ }
+
+ done:
+ if (pDNSMapingEntry != NULL)
+ {
+ *(uint32_t *)h->h_addr_list[0] = pDNSMapingEntry->u32IpAddress;
+ h->h_addr_list[1] = NULL;
+ }
+}
+#endif /* VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER */
diff --git a/src/VBox/Devices/Network/slirp/icmp_var.h b/src/VBox/Devices/Network/slirp/icmp_var.h
new file mode 100644
index 00000000..e9a0cecb
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/icmp_var.h
@@ -0,0 +1,93 @@
+/* $Id: icmp_var.h $ */
+/** @file
+ * NAT - ICMP handling (declarations/defines).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)icmp_var.h 8.1 (Berkeley) 6/10/93
+ * icmp_var.h,v 1.4 1995/02/16 00:27:40 wollman Exp
+ */
+
+#ifndef _NETINET_ICMP_VAR_H_
+#define _NETINET_ICMP_VAR_H_
+
+/*
+ * Variables related to this implementation
+ * of the internet control message protocol.
+ */
+struct icmpstat_t
+{
+/* statistics related to input messages processed */
+ u_long icps_received; /* #ICMP packets received */
+ u_long icps_tooshort; /* packet < ICMP_MINLEN */
+ u_long icps_checksum; /* bad checksum */
+ u_long icps_notsupp; /* #ICMP packets not supported */
+ u_long icps_badtype; /* #with bad type feild */
+ u_long icps_reflect; /* number of responses */
+};
+
+/*
+ * Names for ICMP sysctl objects
+ */
+#define ICMPCTL_MASKREPL 1 /* allow replies to netmask requests */
+#define ICMPCTL_STATS 2 /* statistics (read-only) */
+#define ICMPCTL_MAXID 3
+
+#define ICMPCTL_NAMES { \
+ { 0, 0 }, \
+ { "maskrepl", CTLTYPE_INT }, \
+ { "stats", CTLTYPE_STRUCT }, \
+}
+
+#endif
diff --git a/src/VBox/Devices/Network/slirp/if.h b/src/VBox/Devices/Network/slirp/if.h
new file mode 100644
index 00000000..2eec22a7
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/if.h
@@ -0,0 +1,61 @@
+/* $Id: if.h $ */
+/** @file
+ * NAT - if_*.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _IF_H_
+#define _IF_H_
+
+#define IF_COMPRESS 0x01 /* We want compression */
+#define IF_NOCOMPRESS 0x02 /* Do not do compression */
+#define IF_AUTOCOMP 0x04 /* Autodetect (default) */
+#define IF_NOCIDCOMP 0x08 /* CID compression */
+
+
+#ifdef ETH_P_ARP
+# undef ETH_P_ARP
+#endif /* ETH_P_ARP*/
+#define ETH_P_ARP 0x0806 /* Address Resolution packet */
+
+#ifdef ETH_P_IP
+# undef ETH_P_IP
+#endif /* ETH_P_IP */
+#define ETH_P_IP 0x0800 /* Internet Protocol packet */
+
+#ifdef ETH_P_IPV6
+# undef ETH_P_IPV6
+#endif /* ETH_P_IPV6 */
+#define ETH_P_IPV6 0x86DD /* IPv6 */
+
+#endif
diff --git a/src/VBox/Devices/Network/slirp/ip.h b/src/VBox/Devices/Network/slirp/ip.h
new file mode 100644
index 00000000..ef1ea281
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/ip.h
@@ -0,0 +1,285 @@
+/* $Id: ip.h $ */
+/** @file
+ * NAT - IP handling (declarations/defines).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)ip.h 8.1 (Berkeley) 6/10/93
+ * ip.h,v 1.3 1994/08/21 05:27:30 paul Exp
+ */
+
+#ifndef _IP_H_
+#define _IP_H_
+
+#include "queue.h"
+
+#ifdef WORDS_BIGENDIAN
+# ifndef NTOHL
+# define NTOHL(d)
+# endif
+# ifndef NTOHS
+# define NTOHS(d)
+# endif
+# ifndef HTONL
+# define HTONL(d)
+# endif
+# ifndef HTONS
+# define HTONS(d)
+# endif
+#else
+# ifndef NTOHL
+# define NTOHL(d) ((d) = RT_N2H_U32((d)))
+# endif
+# ifndef NTOHS
+# define NTOHS(d) ((d) = RT_N2H_U16((u_int16_t)(d)))
+# endif
+# ifndef HTONL
+# define HTONL(d) ((d) = RT_H2N_U32((d)))
+# endif
+# ifndef HTONS
+# define HTONS(d) ((d) = RT_H2N_U16((u_int16_t)(d)))
+# endif
+#endif
+
+/*
+ * Definitions for internet protocol version 4.
+ * Per RFC 791, September 1981.
+ */
+#define IPVERSION 4
+
+/*
+ * Structure of an internet header, naked of options.
+ */
+struct ip
+{
+#ifdef WORDS_BIGENDIAN
+# ifdef _MSC_VER
+ uint8_t ip_v:4; /* version */
+ uint8_t ip_hl:4; /* header length */
+# else
+ unsigned ip_v:4; /* version */
+ unsigned ip_hl:4; /* header length */
+# endif
+#else
+# ifdef _MSC_VER
+ uint8_t ip_hl:4; /* header length */
+ uint8_t ip_v:4; /* version */
+# else
+ unsigned ip_hl:4; /* header length */
+ unsigned ip_v:4; /* version */
+# endif
+#endif
+ uint8_t ip_tos; /* type of service */
+ uint16_t ip_len; /* total length */
+ uint16_t ip_id; /* identification */
+ uint16_t ip_off; /* fragment offset field */
+#define IP_DF 0x4000 /* don't fragment flag */
+#define IP_MF 0x2000 /* more fragments flag */
+#define IP_OFFMASK 0x1fff /* mask for fragmenting bits */
+ uint8_t ip_ttl; /* time to live */
+ uint8_t ip_p; /* protocol */
+ uint16_t ip_sum; /* checksum */
+ struct in_addr ip_src; /* source address */
+ struct in_addr ip_dst; /* destination address */
+};
+AssertCompileSize(struct ip, 20);
+
+#define IP_MAXPACKET 65535 /* maximum packet size */
+
+/*
+ * Definitions for IP type of service (ip_tos)
+ */
+#define IPTOS_LOWDELAY 0x10
+#define IPTOS_THROUGHPUT 0x08
+#define IPTOS_RELIABILITY 0x04
+
+
+/*
+ * Time stamp option structure.
+ */
+struct ip_timestamp
+{
+ uint8_t ipt_code; /* IPOPT_TS */
+ uint8_t ipt_len; /* size of structure (variable) */
+ uint8_t ipt_ptr; /* index of current entry */
+#ifdef WORDS_BIGENDIAN
+# ifdef _MSC_VER
+ uint8_t ipt_oflw:4; /* overflow counter */
+ uint8_t ipt_flg:4; /* flags, see below */
+# else
+ unsigned ipt_oflw:4; /* overflow counter */
+ unsigned ipt_flg:4; /* flags, see below */
+# endif
+#else
+# ifdef _MSC_VER
+ uint8_t ipt_flg:4; /* flags, see below */
+ uint8_t ipt_oflw:4; /* overflow counter */
+# else
+ unsigned ipt_flg:4; /* flags, see below */
+ unsigned ipt_oflw:4; /* overflow counter */
+# endif
+#endif
+ union ipt_timestamp
+ {
+ uint32_t ipt_time[1];
+ struct ipt_ta
+ {
+ struct in_addr ipt_addr;
+ uint32_t ipt_time;
+ } ipt_ta[1];
+ } ipt_timestamp;
+};
+AssertCompileSize(struct ip_timestamp, 12);
+
+/*
+ * Internet implementation parameters.
+ */
+#define MAXTTL 255 /* maximum time to live (seconds) */
+#define IPDEFTTL 64 /* default ttl, from RFC 1340 */
+#define IPFRAGTTL 60 /* time to live for frags, slowhz */
+#define IPTTLDEC 1 /* subtracted when forwarding */
+
+#define IP_MSS 576 /* default maximum segment size */
+
+#ifdef HAVE_SYS_TYPES32_H /* Overcome some Solaris 2.x junk */
+# include <sys/types32.h>
+#else
+typedef caddr_t caddr32_t;
+#endif
+
+#if SIZEOF_CHAR_P == 4
+typedef struct ipq_t *ipqp_32;
+typedef struct ipasfrag *ipasfragp_32;
+#else
+typedef caddr32_t ipqp_32;
+typedef caddr32_t ipasfragp_32;
+#endif
+
+/*
+ * Overlay for ip header used by other protocols (tcp, udp).
+ */
+struct ipovly
+{
+ u_int8_t ih_x1[9]; /* (unused) */
+ u_int8_t ih_pr; /* protocol */
+ u_int16_t ih_len; /* protocol length */
+ struct in_addr ih_src; /* source internet address */
+ struct in_addr ih_dst; /* destination internet address */
+};
+AssertCompileSize(struct ipovly, 20);
+
+/*
+ * Ip reassembly queue structure. Each fragment being reassembled is
+ * attached to one of these structures. They are timed out after ipq_ttl
+ * drops to 0, and may also be reclaimed if memory becomes tight.
+ * size 28 bytes
+ */
+struct ipq_t
+{
+ TAILQ_ENTRY(ipq_t) ipq_list;
+ u_int8_t ipq_ttl; /* time for reass q to live */
+ u_int8_t ipq_p; /* protocol of this fragment */
+ u_int16_t ipq_id; /* sequence id for reassembly */
+ struct mbuf *ipq_frags; /* to ip headers of fragments */
+ uint8_t ipq_nfrags; /* # of fragments in this packet */
+ struct in_addr ipq_src;
+ struct in_addr ipq_dst;
+};
+
+
+/*
+* IP datagram reassembly.
+*/
+#define IPREASS_NHASH_LOG2 6
+#define IPREASS_NHASH (1 << IPREASS_NHASH_LOG2)
+#define IPREASS_HMASK (IPREASS_NHASH - 1)
+#define IPREASS_HASH(x,y) \
+(((((x) & 0xF) | ((((x) >> 8) & 0xF) << 4)) ^ (y)) & IPREASS_HMASK)
+TAILQ_HEAD(ipqhead, ipq_t);
+
+/*
+ * Structure attached to inpcb.ip_moptions and
+ * passed to ip_output when IP multicast options are in use.
+ */
+
+struct ipstat_t
+{
+ u_long ips_total; /* total packets received */
+ u_long ips_badsum; /* checksum bad */
+ u_long ips_tooshort; /* packet too short */
+ u_long ips_toosmall; /* not enough data */
+ u_long ips_badhlen; /* ip header length < data size */
+ u_long ips_badlen; /* ip length < ip header length */
+ u_long ips_fragments; /* fragments received */
+ u_long ips_fragdropped; /* frags dropped (dups, out of space) */
+ u_long ips_fragtimeout; /* fragments timed out */
+ u_long ips_forward; /* packets forwarded */
+ u_long ips_cantforward; /* packets rcvd for unreachable dest */
+ u_long ips_redirectsent; /* packets forwarded on same net */
+ u_long ips_noproto; /* unknown or unsupported protocol */
+ u_long ips_delivered; /* datagrams delivered to upper level*/
+ u_long ips_localout; /* total ip packets generated here */
+ u_long ips_odropped; /* lost packets due to nobufs, etc. */
+ u_long ips_reassembled; /* total packets reassembled ok */
+ u_long ips_fragmented; /* datagrams successfully fragmented */
+ u_long ips_ofragments; /* output fragments created */
+ u_long ips_cantfrag; /* don't fragment flag was set, etc. */
+ u_long ips_badoptions; /* error in option processing */
+ u_long ips_noroute; /* packets discarded due to no route */
+ u_long ips_badvers; /* ip version != 4 */
+ u_long ips_rawout; /* total raw ip packets generated */
+ u_long ips_unaligned; /* times the ip packet was not aligned */
+};
+
+#endif
diff --git a/src/VBox/Devices/Network/slirp/ip_icmp.c b/src/VBox/Devices/Network/slirp/ip_icmp.c
new file mode 100644
index 00000000..f61eaaa8
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/ip_icmp.c
@@ -0,0 +1,795 @@
+/* $Id: ip_icmp.c $ */
+/** @file
+ * NAT - IP/ICMP handling.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)ip_icmp.c 8.2 (Berkeley) 1/4/94
+ * ip_icmp.c,v 1.7 1995/05/30 08:09:42 rgrimes Exp
+ */
+
+#include "slirp.h"
+#include "ip_icmp.h"
+
+#ifdef VBOX_RAWSOCK_DEBUG_HELPER
+int getrawsock(int type);
+#endif
+
+
+/* The message sent when emulating PING */
+/* Be nice and tell them it's just a psuedo-ping packet */
+#if 0 /* unused */
+static const char icmp_ping_msg[] = "This is a psuedo-PING packet used by Slirp to emulate ICMP ECHO-REQUEST packets.\n";
+#endif
+
+/* list of actions for icmp_error() on RX of an icmp message */
+static const int icmp_flush[19] =
+{
+/* ECHO REPLY (0) */ 0,
+ 1,
+ 1,
+/* DEST UNREACH (3) */ 1,
+/* SOURCE QUENCH (4)*/ 1,
+/* REDIRECT (5) */ 1,
+ 1,
+ 1,
+/* ECHO (8) */ 0,
+/* ROUTERADVERT (9) */ 1,
+/* ROUTERSOLICIT (10) */ 1,
+/* TIME EXCEEDED (11) */ 1,
+/* PARAMETER PROBLEM (12) */ 1,
+/* TIMESTAMP (13) */ 0,
+/* TIMESTAMP REPLY (14) */ 0,
+/* INFO (15) */ 0,
+/* INFO REPLY (16) */ 0,
+/* ADDR MASK (17) */ 0,
+/* ADDR MASK REPLY (18) */ 0
+};
+
+
+int
+icmp_init(PNATState pData, int iIcmpCacheLimit)
+{
+ pData->icmp_socket.so_type = IPPROTO_ICMP;
+ pData->icmp_socket.so_state = SS_ISFCONNECTED;
+
+#ifndef RT_OS_WINDOWS
+ TAILQ_INIT(&pData->icmp_msg_head);
+
+ if (iIcmpCacheLimit < 0)
+ {
+ LogRel(("NAT: iIcmpCacheLimit is invalid %d, will be alter to default value 100\n", iIcmpCacheLimit));
+ iIcmpCacheLimit = 100;
+ }
+ pData->iIcmpCacheLimit = iIcmpCacheLimit;
+# ifndef RT_OS_DARWIN
+ pData->icmp_socket.s = socket(PF_INET, SOCK_RAW, IPPROTO_ICMP);
+# else /* !RT_OS_DARWIN */
+ pData->icmp_socket.s = socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP);
+# endif /* RT_OS_DARWIN */
+ if (pData->icmp_socket.s == -1)
+ {
+ int rc = RTErrConvertFromErrno(errno);
+# if defined(RT_OS_DARWIN) || !defined(VBOX_RAWSOCK_DEBUG_HELPER)
+ LogRel(("NAT: ICMP/ping not available (could not open ICMP socket, error %Rrc)\n", rc));
+ return 1;
+# else
+ /* try to get it from privileged helper */
+ LogRel(("NAT: ICMP/ping raw socket error %Rrc, asking helper...\n", rc));
+ pData->icmp_socket.s = getrawsock(AF_INET);
+ if (pData->icmp_socket.s == -1)
+ {
+ LogRel(("NAT: ICMP/ping not available\n"));
+ return 1;
+ }
+# endif /* !RT_OS_DARWIN && VBOX_RAWSOCK_DEBUG_HELPER */
+ }
+ fd_nonblock(pData->icmp_socket.s);
+ NSOCK_INC();
+
+#else /* RT_OS_WINDOWS */
+ RT_NOREF(iIcmpCacheLimit);
+
+ if (icmpwin_init(pData) != 0)
+ return 1;
+#endif /* RT_OS_WINDOWS */
+
+ return 0;
+}
+
+/**
+ * Cleans ICMP cache.
+ */
+void
+icmp_finit(PNATState pData)
+{
+#ifdef RT_OS_WINDOWS
+ icmpwin_finit(pData);
+#else
+ while (!TAILQ_EMPTY(&pData->icmp_msg_head))
+ {
+ struct icmp_msg *icm = TAILQ_FIRST(&pData->icmp_msg_head);
+ icmp_msg_delete(pData, icm);
+ }
+ closesocket(pData->icmp_socket.s);
+#endif
+}
+
+
+#if !defined(RT_OS_WINDOWS)
+static struct icmp_msg *
+icmp_msg_alloc(PNATState pData)
+{
+ struct icmp_msg *icm;
+
+#ifdef DEBUG
+ {
+ int iTally = 0;
+ TAILQ_FOREACH(icm, &pData->icmp_msg_head, im_queue)
+ ++iTally;
+ Assert(pData->cIcmpCacheSize == iTally);
+ }
+#endif
+
+ if (pData->cIcmpCacheSize >= pData->iIcmpCacheLimit)
+ {
+ int cTargetCacheSize = pData->iIcmpCacheLimit/2;
+
+ while (pData->cIcmpCacheSize > cTargetCacheSize)
+ {
+ icm = TAILQ_FIRST(&pData->icmp_msg_head);
+ icmp_msg_delete(pData, icm);
+ }
+ }
+
+ icm = RTMemAlloc(sizeof(struct icmp_msg));
+ if (RT_UNLIKELY(icm == NULL))
+ return NULL;
+
+ TAILQ_INSERT_TAIL(&pData->icmp_msg_head, icm, im_queue);
+ pData->cIcmpCacheSize++;
+
+ return icm;
+}
+
+
+static void
+icmp_attach(PNATState pData, struct mbuf *m)
+{
+ struct icmp_msg *icm;
+
+#ifdef DEBUG
+ {
+ /* only used for ping */
+ struct ip *ip = mtod(m, struct ip *);
+ Assert(ip->ip_p == IPPROTO_ICMP);
+ }
+#endif
+
+ icm = icmp_msg_alloc(pData);
+ if (RT_UNLIKELY(icm == NULL))
+ return;
+
+ icm->im_so = &pData->icmp_socket;
+ icm->im_m = m;
+}
+
+
+void
+icmp_msg_delete(PNATState pData, struct icmp_msg *icm)
+{
+ if (RT_UNLIKELY(icm == NULL))
+ return;
+
+#ifdef DEBUG
+ {
+ struct icmp_msg *existing;
+ int iTally = 0;
+
+ TAILQ_FOREACH(existing, &pData->icmp_msg_head, im_queue)
+ ++iTally;
+ Assert(pData->cIcmpCacheSize == iTally);
+
+ Assert(pData->cIcmpCacheSize > 0);
+ TAILQ_FOREACH(existing, &pData->icmp_msg_head, im_queue)
+ {
+ if (existing == icm)
+ break;
+ }
+ Assert(existing != NULL);
+ }
+#endif
+
+ TAILQ_REMOVE(&pData->icmp_msg_head, icm, im_queue);
+ pData->cIcmpCacheSize--;
+
+ icm->im_so->so_m = NULL;
+ if (icm->im_m != NULL)
+ m_freem(pData, icm->im_m);
+
+ RTMemFree(icm);
+}
+
+
+/*
+ * ip here is ip header + 64bytes readed from ICMP packet
+ */
+struct icmp_msg *
+icmp_find_original_mbuf(PNATState pData, struct ip *ip)
+{
+ struct mbuf *m0;
+ struct ip *ip0;
+ struct icmp *icp, *icp0;
+ struct icmp_msg *icm = NULL;
+ int found = 0;
+ struct udphdr *udp;
+ struct tcphdr *tcp;
+ struct socket *head_socket = NULL;
+ struct socket *last_socket = NULL;
+ struct socket *so = NULL;
+ struct in_addr faddr;
+ u_short lport, fport;
+
+ faddr.s_addr = ~0;
+
+ lport = ~0;
+ fport = ~0;
+
+
+ LogFlowFunc(("ENTER: ip->ip_p:%d\n", ip->ip_p));
+ switch (ip->ip_p)
+ {
+ case IPPROTO_ICMP:
+ icp = (struct icmp *)((char *)ip + (ip->ip_hl << 2));
+ TAILQ_FOREACH(icm, &pData->icmp_msg_head, im_queue)
+ {
+ m0 = icm->im_m;
+ ip0 = mtod(m0, struct ip *);
+ if (ip0->ip_p != IPPROTO_ICMP)
+ {
+ /* try next item */
+ continue;
+ }
+ icp0 = (struct icmp *)((char *)ip0 + (ip0->ip_hl << 2));
+ /*
+ * IP could pointer to ICMP_REPLY datagram (1)
+ * or pointer IP header in ICMP payload in case of
+ * ICMP_TIMXCEED or ICMP_UNREACH (2)
+ *
+ * if (1) and then ICMP (type should be ICMP_ECHOREPLY) and we need check that
+ * IP.IP_SRC == IP0.IP_DST received datagramm comes from destination.
+ *
+ * if (2) then check that payload ICMP has got type ICMP_ECHO and
+ * IP.IP_DST == IP0.IP_DST destination of returned datagram is the same as
+ * one was sent.
+ */
+ if ( ( (icp->icmp_type != ICMP_ECHO && ip->ip_src.s_addr == ip0->ip_dst.s_addr)
+ || (icp->icmp_type == ICMP_ECHO && ip->ip_dst.s_addr == ip0->ip_dst.s_addr))
+ && icp->icmp_id == icp0->icmp_id
+ && icp->icmp_seq == icp0->icmp_seq)
+ {
+ found = 1;
+ Log(("Have found %R[natsock]\n", icm->im_so));
+ break;
+ }
+ Log(("Have found nothing\n"));
+ }
+ break;
+
+ /*
+ * for TCP and UDP logic little bit reverted, we try to find the HOST socket
+ * from which the IP package has been sent.
+ */
+ case IPPROTO_UDP:
+ head_socket = &udb;
+ udp = (struct udphdr *)((char *)ip + (ip->ip_hl << 2));
+ faddr.s_addr = ip->ip_dst.s_addr;
+ fport = udp->uh_dport;
+ lport = udp->uh_sport;
+ last_socket = udp_last_so;
+ RT_FALL_THRU();
+
+ case IPPROTO_TCP:
+ if (head_socket == NULL)
+ {
+ tcp = (struct tcphdr *)((char *)ip + (ip->ip_hl << 2));
+ head_socket = &tcb; /* head_socket could be initialized with udb*/
+ faddr.s_addr = ip->ip_dst.s_addr;
+ fport = tcp->th_dport;
+ lport = tcp->th_sport;
+ last_socket = tcp_last_so;
+ }
+ /* check last socket first */
+ if ( last_socket->so_faddr.s_addr == faddr.s_addr
+ && last_socket->so_fport == fport
+ && last_socket->so_hlport == lport)
+ {
+ found = 1;
+ so = last_socket;
+ break;
+ }
+ for (so = head_socket->so_prev; so != head_socket; so = so->so_prev)
+ {
+ /* Should be replaced by hash here */
+ Log(("trying:%R[natsock] against %RTnaipv4:%d lport=%d hlport=%d\n",
+ so, faddr.s_addr, ntohs(fport), ntohs(lport), ntohs(so->so_hlport)));
+ if ( so->so_faddr.s_addr == faddr.s_addr
+ && so->so_fport == fport
+ && so->so_hlport == lport)
+ {
+ found = 1;
+ break;
+ }
+ }
+ break;
+
+ default:
+ Log(("NAT:ICMP: unsupported protocol(%d)\n", ip->ip_p));
+ }
+
+#ifdef DEBUG
+ if (found)
+ Assert((icm != NULL) ^ (so != NULL));
+#endif
+
+ if (found && icm == NULL)
+ {
+ /*
+ * XXX: Implies this is not a pong, found socket. This is, of
+ * course, wasteful since the caller will delete icmp_msg
+ * immediately after processing, so there's not much reason to
+ * clutter up the queue with it.
+ */
+ AssertReturn(so != NULL, NULL);
+
+ /*
+ * XXX: FIXME: If the very first send(2) fails, the socket is
+ * still in SS_NOFDREF and so we will not report this too.
+ */
+ if (so->so_state == SS_NOFDREF)
+ {
+ /* socket is shutting down we've already sent ICMP on it. */
+ Log(("NAT:ICMP: disconnected %R[natsock]\n", so));
+ LogFlowFunc(("LEAVE: icm:NULL\n"));
+ return NULL;
+ }
+
+ if (so->so_m == NULL)
+ {
+ Log(("NAT:ICMP: no saved mbuf for %R[natsock]\n", so));
+ LogFlowFunc(("LEAVE: icm:NULL\n"));
+ return NULL;
+ }
+
+ icm = icmp_msg_alloc(pData);
+ if (RT_UNLIKELY(icm == NULL))
+ {
+ LogFlowFunc(("LEAVE: icm:NULL\n"));
+ return NULL;
+ }
+
+ Log(("NAT:ICMP: for %R[natsock]\n", so));
+ icm->im_so = so;
+ icm->im_m = so->so_m;
+ }
+ LogFlowFunc(("LEAVE: icm:%p\n", icm));
+ return icm;
+}
+#endif /* !RT_OS_WINDOWS */
+
+
+/*
+ * Process a received ICMP message.
+ */
+void
+icmp_input(PNATState pData, struct mbuf *m, int hlen)
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ int icmplen = ip->ip_len;
+ uint8_t icmp_type;
+ void *icp_buf = NULL;
+ uint32_t dst;
+
+ /* int code; */
+
+ LogFlowFunc(("ENTER: m = %p, m_len = %d\n", m, m ? m->m_len : 0));
+
+ icmpstat.icps_received++;
+
+ /*
+ * Locate icmp structure in mbuf, and check
+ * that its not corrupted and of at least minimum length.
+ */
+ if (icmplen < ICMP_MINLEN)
+ {
+ /* min 8 bytes payload */
+ icmpstat.icps_tooshort++;
+ goto end_error_free_m;
+ }
+
+ m->m_len -= hlen;
+ m->m_data += hlen;
+
+ if (cksum(m, icmplen))
+ {
+ icmpstat.icps_checksum++;
+ goto end_error_free_m;
+ }
+
+ /* are we guaranteed to have ICMP header in first mbuf? be safe. */
+ m_copydata(m, 0, sizeof(icmp_type), (caddr_t)&icmp_type);
+
+ m->m_len += hlen;
+ m->m_data -= hlen;
+
+ /* icmpstat.icps_inhist[icp->icmp_type]++; */
+ /* code = icp->icmp_code; */
+
+ LogFlow(("icmp_type = %d\n", icmp_type));
+ switch (icmp_type)
+ {
+ case ICMP_ECHO:
+ ip->ip_len += hlen; /* since ip_input subtracts this */
+ dst = ip->ip_dst.s_addr;
+ if ( CTL_CHECK(dst, CTL_ALIAS)
+ || CTL_CHECK(dst, CTL_DNS)
+ || CTL_CHECK(dst, CTL_TFTP))
+ {
+ /* Don't reply to ping requests for the hosts loopback interface if it is disabled. */
+ if ( CTL_CHECK(dst, CTL_ALIAS)
+ && !pData->fLocalhostReachable)
+ goto done;
+
+ uint8_t echo_reply = ICMP_ECHOREPLY;
+ m_copyback(pData, m, hlen + RT_OFFSETOF(struct icmp, icmp_type),
+ sizeof(echo_reply), (caddr_t)&echo_reply);
+ ip->ip_dst.s_addr = ip->ip_src.s_addr;
+ ip->ip_src.s_addr = dst;
+ icmp_reflect(pData, m);
+ m = NULL; /* m was consumed and freed */
+ goto done;
+ }
+
+#ifdef RT_OS_WINDOWS
+ {
+ icmpwin_ping(pData, m, hlen);
+ break; /* free mbuf */
+ }
+#else
+ {
+ struct icmp *icp;
+ struct sockaddr_in addr;
+
+ /* XXX: FIXME: this is bogus, see CTL_CHECKs above */
+ addr.sin_family = AF_INET;
+ if ((ip->ip_dst.s_addr & RT_H2N_U32(pData->netmask)) == pData->special_addr.s_addr)
+ {
+ /* It's an alias */
+ switch (RT_N2H_U32(ip->ip_dst.s_addr) & ~pData->netmask)
+ {
+ case CTL_DNS:
+ case CTL_ALIAS:
+ default:
+ addr.sin_addr = loopback_addr;
+ break;
+ }
+ }
+ else
+ addr.sin_addr.s_addr = ip->ip_dst.s_addr;
+
+ if (m->m_next)
+ {
+ icp_buf = RTMemAlloc(icmplen);
+ if (!icp_buf)
+ {
+ Log(("NAT: not enought memory to allocate the buffer\n"));
+ goto end_error_free_m;
+ }
+ m_copydata(m, hlen, icmplen, icp_buf);
+ icp = (struct icmp *)icp_buf;
+ }
+ else
+ icp = (struct icmp *)(mtod(m, char *) + hlen);
+
+ if (pData->icmp_socket.s != -1)
+ {
+ static bool fIcmpSocketErrorReported;
+ int ttl;
+ int status;
+ ssize_t rc;
+
+ ttl = ip->ip_ttl;
+ Log(("NAT/ICMP: try to set TTL(%d)\n", ttl));
+ status = setsockopt(pData->icmp_socket.s, IPPROTO_IP, IP_TTL,
+ (void *)&ttl, sizeof(ttl));
+ if (status < 0)
+ Log(("NAT: Error (%s) occurred while setting TTL attribute of IP packet\n",
+ strerror(errno)));
+ rc = sendto(pData->icmp_socket.s, icp, icmplen, 0,
+ (struct sockaddr *)&addr, sizeof(addr));
+ if (rc >= 0)
+ {
+ icmp_attach(pData, m);
+ m = NULL; /* m was stashed away for safekeeping */
+ goto done;
+ }
+
+
+ if (!fIcmpSocketErrorReported)
+ {
+ LogRel(("NAT: icmp_input udp sendto tx errno = %d (%s)\n",
+ errno, strerror(errno)));
+ fIcmpSocketErrorReported = true;
+ }
+ icmp_error(pData, m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno));
+ m = NULL; /* m was consumed and freed */
+ goto done;
+ }
+ }
+#endif /* !RT_OS_WINDOWS */
+ break;
+ case ICMP_UNREACH:
+ case ICMP_TIMXCEED:
+ /* @todo(vvl): both up cases comes from guest,
+ * indeed right solution would be find the socket
+ * corresponding to ICMP data and close it.
+ */
+ case ICMP_PARAMPROB:
+ case ICMP_SOURCEQUENCH:
+ case ICMP_TSTAMP:
+ case ICMP_MASKREQ:
+ case ICMP_REDIRECT:
+ icmpstat.icps_notsupp++;
+ break;
+
+ default:
+ icmpstat.icps_badtype++;
+ } /* switch */
+
+end_error_free_m:
+ if (m != NULL)
+ m_freem(pData, m);
+
+done:
+ if (icp_buf)
+ RTMemFree(icp_buf);
+}
+
+
+/**
+ * Send an ICMP message in response to a situation
+ *
+ * RFC 1122: 3.2.2 MUST send at least the IP header and 8 bytes of header. MAY send more (we do).
+ * MUST NOT change this header information.
+ * MUST NOT reply to a multicast/broadcast IP address.
+ * MUST NOT reply to a multicast/broadcast MAC address.
+ * MUST reply to only the first fragment.
+ *
+ * Send ICMP_UNREACH back to the source regarding msrc.
+ * It is reported as the bad ip packet. The header should
+ * be fully correct and in host byte order.
+ * ICMP fragmentation is illegal.
+ *
+ * @note: implementation note: MSIZE is 256 bytes (minimal buffer).
+ * We always truncate original payload to 8 bytes required by the RFC,
+ * so the largest possible datagram is 14 (ethernet) + 20 (ip) +
+ * 8 (icmp) + 60 (max original ip with options) + 8 (original payload)
+ * = 110 bytes which fits into sinlge mbuf.
+ *
+ * @note This function will free msrc!
+ */
+
+void icmp_error(PNATState pData, struct mbuf *msrc, u_char type, u_char code, int minsize, const char *message)
+{
+ unsigned ohlen, olen;
+ struct mbuf *m;
+ struct ip *oip, *ip;
+ struct icmp *icp;
+ void *payload;
+ RT_NOREF(minsize);
+
+ LogFlow(("icmp_error: msrc = %p, msrc_len = %d\n",
+ (void *)msrc, msrc ? msrc->m_len : 0));
+
+ if (RT_UNLIKELY(msrc == NULL))
+ goto end_error;
+
+ M_ASSERTPKTHDR(msrc);
+
+ if ( type != ICMP_UNREACH
+ && type != ICMP_TIMXCEED
+ && type != ICMP_SOURCEQUENCH)
+ goto end_error;
+
+ oip = mtod(msrc, struct ip *);
+ LogFunc(("msrc: %RTnaipv4 -> %RTnaipv4\n", oip->ip_src, oip->ip_dst));
+
+ if (oip->ip_src.s_addr == INADDR_ANY)
+ goto end_error;
+
+ if (oip->ip_off & IP_OFFMASK)
+ goto end_error; /* Only reply to fragment 0 */
+
+ ohlen = oip->ip_hl * 4;
+ AssertStmt(ohlen >= sizeof(struct ip), goto end_error);
+
+ olen = oip->ip_len;
+ AssertStmt(olen >= ohlen, goto end_error);
+
+ if (oip->ip_p == IPPROTO_ICMP)
+ {
+ struct icmp *oicp = (struct icmp *)((char *)oip + ohlen);
+ /*
+ * Assume any unknown ICMP type is an error. This isn't
+ * specified by the RFC, but think about it..
+ */
+ if (oicp->icmp_type > ICMP_MAXTYPE || icmp_flush[oicp->icmp_type])
+ goto end_error;
+ }
+
+ /* undo byte order conversions done in ip_input() */
+ HTONS(oip->ip_len);
+ HTONS(oip->ip_id);
+ HTONS(oip->ip_off);
+
+ m = m_gethdr(pData, M_NOWAIT, MT_HEADER);
+ if (RT_UNLIKELY(m == NULL))
+ goto end_error;
+
+ m->m_flags |= M_SKIP_FIREWALL;
+ m->m_data += if_maxlinkhdr;
+
+ ip = mtod(m, struct ip *);
+ m->m_pkthdr.header = (void *)ip;
+
+ /* fill in ip (ip_output0() does the boilerplate for us) */
+ ip->ip_tos = ((oip->ip_tos & 0x1E) | 0xC0); /* high priority for errors */
+ /* ip->ip_len will be set later */
+ ip->ip_off = 0;
+ ip->ip_ttl = MAXTTL;
+ ip->ip_p = IPPROTO_ICMP;
+ ip->ip_src = alias_addr;
+ ip->ip_dst = oip->ip_src;
+
+ /* fill in icmp */
+ icp = (struct icmp *)((char *)ip + sizeof(*ip));
+ icp->icmp_type = type;
+ icp->icmp_code = code;
+ icp->icmp_id = 0;
+ icp->icmp_seq = 0;
+
+ /* fill in icmp payload: original ip header plus 8 bytes of its payload */
+ if (olen > ohlen + 8)
+ olen = ohlen + 8;
+ payload = (void *)((char *)icp + ICMP_MINLEN);
+ memcpy(payload, oip, olen);
+
+ /*
+ * Original code appended this message after the payload. This
+ * might have been a good idea for real slirp, as it provided a
+ * communication channel with the remote host. But 90s are over.
+ */
+ NOREF(message);
+
+ /* hide ip header for icmp checksum calculation */
+ m->m_data += sizeof(struct ip);
+ m->m_len = ICMP_MINLEN + /* truncated */ olen;
+
+ icp->icmp_cksum = 0;
+ icp->icmp_cksum = cksum(m, m->m_len);
+
+ /* reveal ip header */
+ m->m_data -= sizeof(struct ip);
+ m->m_len += sizeof(struct ip);
+ ip->ip_len = m->m_len;
+
+ (void) ip_output0(pData, (struct socket *)NULL, m, 1);
+
+ icmpstat.icps_reflect++;
+
+ /* clear source datagramm in positive branch */
+ m_freem(pData, msrc);
+ LogFlowFuncLeave();
+ return;
+
+end_error:
+
+ /*
+ * clear source datagramm in case if some of requirement haven't been met.
+ */
+ if (msrc)
+ m_freem(pData, msrc);
+
+ {
+ static bool fIcmpErrorReported;
+ if (!fIcmpErrorReported)
+ {
+ LogRel(("NAT: Error occurred while sending ICMP error message\n"));
+ fIcmpErrorReported = true;
+ }
+ }
+ LogFlowFuncLeave();
+}
+
+/*
+ * Reflect the ip packet back to the source
+ * Note: m isn't duplicated by this method and more delivered to ip_output then.
+ */
+void
+icmp_reflect(PNATState pData, struct mbuf *m)
+{
+ register struct ip *ip = mtod(m, struct ip *);
+ int hlen = ip->ip_hl << 2;
+ register struct icmp *icp;
+ LogFlowFunc(("ENTER: m:%p\n", m));
+
+ /*
+ * Send an icmp packet back to the ip level,
+ * after supplying a checksum.
+ */
+ m->m_data += hlen;
+ m->m_len -= hlen;
+ icp = mtod(m, struct icmp *);
+
+ icp->icmp_cksum = 0;
+ icp->icmp_cksum = cksum(m, ip->ip_len - hlen);
+
+ m->m_data -= hlen;
+ m->m_len += hlen;
+
+ (void) ip_output(pData, (struct socket *)NULL, m);
+
+ icmpstat.icps_reflect++;
+ LogFlowFuncLeave();
+}
diff --git a/src/VBox/Devices/Network/slirp/ip_icmp.h b/src/VBox/Devices/Network/slirp/ip_icmp.h
new file mode 100644
index 00000000..2dba66c1
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/ip_icmp.h
@@ -0,0 +1,220 @@
+/* $Id: ip_icmp.h $ */
+/** @file
+ * NAT - IP/ICMP handling (declarations/defines).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)ip_icmp.h 8.1 (Berkeley) 6/10/93
+ * ip_icmp.h,v 1.4 1995/05/30 08:09:43 rgrimes Exp
+ */
+
+#ifndef _NETINET_IP_ICMP_H_
+#define _NETINET_IP_ICMP_H_
+#include <queue.h>
+
+/*
+ * Interface Control Message Protocol Definitions.
+ * Per RFC 792, September 1981.
+ */
+
+typedef u_int32_t n_time;
+
+/*
+ * Structure of an icmp header.
+ */
+struct icmp
+{
+ uint8_t icmp_type; /* type of message, see below */
+ uint8_t icmp_code; /* type sub code */
+ uint16_t icmp_cksum; /* ones complement cksum of struct */
+ union
+ {
+ uint8_t ih_pptr; /* ICMP_PARAMPROB */
+ struct in_addr ih_gwaddr; /* ICMP_REDIRECT */
+ struct ih_idseq
+ {
+ uint16_t icd_id;
+ uint16_t icd_seq;
+ } ih_idseq;
+ int ih_void;
+
+ /* ICMP_UNREACH_NEEDFRAG -- Path MTU Discovery (RFC1191) */
+ struct ih_pmtu
+ {
+ uint16_t ipm_void;
+ uint16_t ipm_nextmtu;
+ } ih_pmtu;
+ } icmp_hun;
+#define icmp_pptr icmp_hun.ih_pptr
+#define icmp_gwaddr icmp_hun.ih_gwaddr
+#define icmp_id icmp_hun.ih_idseq.icd_id
+#define icmp_seq icmp_hun.ih_idseq.icd_seq
+#define icmp_void icmp_hun.ih_void
+#define icmp_pmvoid icmp_hun.ih_pmtu.ipm_void
+#define icmp_nextmtu icmp_hun.ih_pmtu.ipm_nextmtu
+ union
+ {
+ struct id_ts
+ {
+ n_time its_otime;
+ n_time its_rtime;
+ n_time its_ttime;
+ } id_ts;
+ struct id_ip
+ {
+ struct ip idi_ip;
+ /* options and then 64 bits of data */
+ } id_ip;
+ uint32_t id_mask;
+ char id_data[1];
+ } icmp_dun;
+#define icmp_otime icmp_dun.id_ts.its_otime
+#define icmp_rtime icmp_dun.id_ts.its_rtime
+#define icmp_ttime icmp_dun.id_ts.its_ttime
+#define icmp_ip icmp_dun.id_ip.idi_ip
+#define icmp_mask icmp_dun.id_mask
+#define icmp_data icmp_dun.id_data
+};
+AssertCompileSize(struct icmp, 28);
+
+/*
+ * Lower bounds on packet lengths for various types.
+ * For the error advice packets must first insure that the
+ * packet is large enought to contain the returned ip header.
+ * Only then can we do the check to see if 64 bits of packet
+ * data have been returned, since we need to check the returned
+ * ip header length.
+ */
+#define ICMP_MINLEN 8 /* abs minimum */
+#define ICMP_TSLEN (8 + 3 * sizeof (n_time)) /* timestamp */
+#define ICMP_MASKLEN 12 /* address mask */
+#define ICMP_ADVLENMIN (8 + sizeof (struct ip) + 8) /* min */
+#define ICMP_ADVLEN(p) (8 + ((p)->icmp_ip.ip_hl << 2) + 8)
+ /* N.B.: must separately check that ip_hl >= 5 */
+
+/*
+ * Definition of type and code field values.
+ */
+#define ICMP_ECHOREPLY 0 /* echo reply */
+#define ICMP_UNREACH 3 /* dest unreachable, codes: */
+#define ICMP_UNREACH_NET 0 /* bad net */
+#define ICMP_UNREACH_HOST 1 /* bad host */
+#define ICMP_UNREACH_PROTOCOL 2 /* bad protocol */
+#define ICMP_UNREACH_PORT 3 /* bad port */
+#define ICMP_UNREACH_NEEDFRAG 4 /* IP_DF caused drop */
+#define ICMP_UNREACH_SRCFAIL 5 /* src route failed */
+#define ICMP_UNREACH_NET_UNKNOWN 6 /* unknown net */
+#define ICMP_UNREACH_HOST_UNKNOWN 7 /* unknown host */
+#define ICMP_UNREACH_ISOLATED 8 /* src host isolated */
+#define ICMP_UNREACH_NET_PROHIB 9 /* prohibited access */
+#define ICMP_UNREACH_HOST_PROHIB 10 /* ditto */
+#define ICMP_UNREACH_TOSNET 11 /* bad tos for net */
+#define ICMP_UNREACH_TOSHOST 12 /* bad tos for host */
+#define ICMP_SOURCEQUENCH 4 /* packet lost, slow down */
+#define ICMP_REDIRECT 5 /* shorter route, codes: */
+#define ICMP_REDIRECT_NET 0 /* for network */
+#define ICMP_REDIRECT_HOST 1 /* for host */
+#define ICMP_REDIRECT_TOSNET 2 /* for tos and net */
+#define ICMP_REDIRECT_TOSHOST 3 /* for tos and host */
+#define ICMP_ECHO 8 /* echo service */
+#define ICMP_ROUTERADVERT 9 /* router advertisement */
+#define ICMP_ROUTERSOLICIT 10 /* router solicitation */
+#define ICMP_TIMXCEED 11 /* time exceeded, code: */
+#define ICMP_TIMXCEED_INTRANS 0 /* ttl==0 in transit */
+#define ICMP_TIMXCEED_REASS 1 /* ttl==0 in reass */
+#define ICMP_PARAMPROB 12 /* ip header bad */
+#define ICMP_PARAMPROB_OPTABSENT 1 /* req. opt. absent */
+#define ICMP_TSTAMP 13 /* timestamp request */
+#define ICMP_TSTAMPREPLY 14 /* timestamp reply */
+#define ICMP_IREQ 15 /* information request */
+#define ICMP_IREQREPLY 16 /* information reply */
+#define ICMP_MASKREQ 17 /* address mask request */
+#define ICMP_MASKREPLY 18 /* address mask reply */
+
+#define ICMP_MAXTYPE 18
+
+#define ICMP_INFOTYPE(type) \
+ ((type) == ICMP_ECHOREPLY || (type) == ICMP_ECHO || \
+ (type) == ICMP_ROUTERADVERT || (type) == ICMP_ROUTERSOLICIT || \
+ (type) == ICMP_TSTAMP || (type) == ICMP_TSTAMPREPLY || \
+ (type) == ICMP_IREQ || (type) == ICMP_IREQREPLY || \
+ (type) == ICMP_MASKREQ || (type) == ICMP_MASKREPLY)
+
+void icmp_input (PNATState, struct mbuf *, int);
+void icmp_error (PNATState, struct mbuf *, u_char, u_char, int, const char *);
+void icmp_reflect (PNATState, struct mbuf *);
+
+struct icmp_msg
+{
+ TAILQ_ENTRY(icmp_msg) im_queue;
+ struct mbuf *im_m;
+ struct socket *im_so;
+};
+
+TAILQ_HEAD(icmp_storage, icmp_msg);
+
+int icmp_init (PNATState , int);
+void icmp_finit (PNATState );
+struct icmp_msg * icmp_find_original_mbuf (PNATState , struct ip *);
+void icmp_msg_delete(PNATState, struct icmp_msg *);
+
+#ifdef RT_OS_WINDOWS
+/* Windows ICMP API code in ip_icmpwin.c */
+int icmpwin_init (PNATState);
+void icmpwin_finit (PNATState);
+void icmpwin_ping(PNATState, struct mbuf *, int);
+void icmpwin_process(PNATState);
+#endif
+
+#endif
diff --git a/src/VBox/Devices/Network/slirp/ip_icmpwin.c b/src/VBox/Devices/Network/slirp/ip_icmpwin.c
new file mode 100644
index 00000000..25467c8b
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/ip_icmpwin.c
@@ -0,0 +1,558 @@
+/* $Id: ip_icmpwin.c $ */
+/** @file
+ * NAT - Windows ICMP API based ping proxy.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "slirp.h"
+#include "ip_icmp.h"
+
+#include <winternl.h> /* for PIO_APC_ROUTINE &c */
+#ifndef PIO_APC_ROUTINE_DEFINED
+# define PIO_APC_ROUTINE_DEFINED 1
+#endif
+#include <iprt/win/iphlpapi.h>
+#include <icmpapi.h>
+
+/*
+ * A header of ICMP ECHO. Intended for storage, unlike struct icmp
+ * which is intended to be overlayed onto a buffer.
+ */
+struct icmp_echo {
+ uint8_t icmp_type;
+ uint8_t icmp_code;
+ uint16_t icmp_cksum;
+ uint16_t icmp_echo_id;
+ uint16_t icmp_echo_seq;
+};
+
+AssertCompileSize(struct icmp_echo, 8);
+
+
+struct pong {
+ PNATState pData;
+
+ TAILQ_ENTRY(pong) queue_entry;
+
+ union {
+ struct ip ip;
+ uint8_t au[60];
+ } reqiph;
+ struct icmp_echo reqicmph;
+
+ size_t bufsize;
+ uint8_t buf[1];
+};
+
+
+static VOID WINAPI icmpwin_callback_apc(void *ctx, PIO_STATUS_BLOCK iob, ULONG reserved);
+static VOID WINAPI icmpwin_callback_old(void *ctx);
+
+static void icmpwin_callback(struct pong *pong);
+static void icmpwin_pong(struct pong *pong);
+
+static struct mbuf *icmpwin_get_error(struct pong *pong, int type, int code);
+static struct mbuf *icmpwin_get_mbuf(PNATState pData, size_t reqsize);
+
+
+/*
+ * On Windows XP and Windows Server 2003 IcmpSendEcho2() callback
+ * is FARPROC, but starting from Vista it's PIO_APC_ROUTINE with
+ * two extra arguments. Callbacks use WINAPI (stdcall) calling
+ * convention with callee responsible for popping the arguments,
+ * so to avoid stack corruption we check windows version at run
+ * time and provide correct callback.
+ *
+ * XXX: this is system-wide, but what about multiple NAT threads?
+ */
+static PIO_APC_ROUTINE g_pfnIcmpCallback;
+
+
+int
+icmpwin_init(PNATState pData)
+{
+ if (g_pfnIcmpCallback == NULL)
+ {
+ OSVERSIONINFO osvi;
+ int status;
+
+ ZeroMemory(&osvi, sizeof(OSVERSIONINFO));
+ osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
+ status = GetVersionEx(&osvi);
+ if (status == 0)
+ return 1;
+
+ if (osvi.dwMajorVersion >= 6)
+ g_pfnIcmpCallback = icmpwin_callback_apc;
+ else
+ g_pfnIcmpCallback = (PIO_APC_ROUTINE)icmpwin_callback_old;
+ }
+
+ TAILQ_INIT(&pData->pongs_expected);
+ TAILQ_INIT(&pData->pongs_received);
+
+ pData->icmp_socket.sh = IcmpCreateFile();
+ pData->phEvents[VBOX_ICMP_EVENT_INDEX] = CreateEvent(NULL, FALSE, FALSE, NULL);
+
+ return 0;
+}
+
+
+void
+icmpwin_finit(PNATState pData)
+{
+ IcmpCloseHandle(pData->icmp_socket.sh);
+
+ while (!TAILQ_EMPTY(&pData->pongs_received)) {
+ struct pong *pong = TAILQ_FIRST(&pData->pongs_received);
+ TAILQ_REMOVE(&pData->pongs_received, pong, queue_entry);
+ RTMemFree(pong);
+ }
+
+ /* this should be empty */
+ while (!TAILQ_EMPTY(&pData->pongs_expected)) {
+ struct pong *pong = TAILQ_FIRST(&pData->pongs_expected);
+ TAILQ_REMOVE(&pData->pongs_expected, pong, queue_entry);
+ pong->pData = NULL;
+ }
+}
+
+
+/*
+ * Outgoing ping from guest.
+ */
+void
+icmpwin_ping(PNATState pData, struct mbuf *m, int hlen)
+{
+ struct ip *ip = mtod(m, struct ip *);
+ size_t reqsize, pongsize;
+ uint8_t ttl;
+ size_t bufsize;
+ struct pong *pong;
+ IPAddr dst;
+ IP_OPTION_INFORMATION opts;
+ void *reqdata;
+ int status;
+
+ ttl = ip->ip_ttl;
+ AssertReturnVoid(ttl > 0);
+
+ size_t hdrsize = hlen + sizeof(struct icmp_echo);
+ reqsize = ip->ip_len - hdrsize;
+
+ bufsize = sizeof(ICMP_ECHO_REPLY);
+ if (reqsize < sizeof(IO_STATUS_BLOCK) + sizeof(struct icmp_echo))
+ bufsize += sizeof(IO_STATUS_BLOCK) + sizeof(struct icmp_echo);
+ else
+ bufsize += reqsize;
+ bufsize += 16; /* whatever that is; empirically at least XP needs it */
+
+ pongsize = RT_UOFFSETOF(struct pong, buf) + bufsize;
+ if (pData->cbIcmpPending + pongsize > 1024 * 1024)
+ return;
+
+ pong = RTMemAlloc(pongsize);
+ if (RT_UNLIKELY(pong == NULL))
+ return;
+
+ pong->pData = pData;
+ pong->bufsize = bufsize;
+ m_copydata(m, 0, hlen, (caddr_t)&pong->reqiph);
+ m_copydata(m, hlen, sizeof(struct icmp_echo), (caddr_t)&pong->reqicmph);
+ AssertReturnVoid(pong->reqicmph.icmp_type == ICMP_ECHO);
+
+ if (m->m_next == NULL)
+ {
+ /* already in single contiguous buffer */
+ reqdata = mtod(m, char *) + hdrsize;
+ }
+ else
+ {
+ /* use reply buffer as temporary storage */
+ reqdata = pong->buf;
+ m_copydata(m, (int)hdrsize, (int)reqsize, reqdata);
+ }
+
+ dst = ip->ip_dst.s_addr;
+
+ opts.Ttl = ttl;
+ opts.Tos = ip->ip_tos; /* affected by DisableUserTOSSetting key */
+ opts.Flags = (ip->ip_off & IP_DF) != 0 ? IP_FLAG_DF : 0;
+ opts.OptionsSize = 0;
+ opts.OptionsData = 0;
+
+
+ status = IcmpSendEcho2(pData->icmp_socket.sh, NULL,
+ g_pfnIcmpCallback, pong,
+ dst, reqdata, (WORD)reqsize, &opts,
+ pong->buf, (DWORD)pong->bufsize,
+ 5 * 1000 /* ms */);
+
+ if (RT_UNLIKELY(status != 0))
+ {
+ Log2(("NAT: IcmpSendEcho2: unexpected status %d\n", status));
+ }
+ else if ((status = GetLastError()) != ERROR_IO_PENDING)
+ {
+ int code;
+
+ Log2(("NAT: IcmpSendEcho2: error %d\n", status));
+ switch (status) {
+ case ERROR_NETWORK_UNREACHABLE:
+ code = ICMP_UNREACH_NET;
+ break;
+ case ERROR_HOST_UNREACHABLE:
+ code = ICMP_UNREACH_HOST;
+ break;
+ default:
+ code = -1;
+ break;
+ }
+
+ if (code != -1) /* send icmp error */
+ {
+ struct mbuf *em = icmpwin_get_error(pong, ICMP_UNREACH, code);
+ if (em != NULL)
+ {
+ struct ip *eip = mtod(em, struct ip *);
+ eip->ip_src = alias_addr;
+ ip_output(pData, NULL, em);
+ }
+ }
+ }
+ else /* success */
+ {
+ Log2(("NAT: pong %p for ping %RTnaipv4 id 0x%04x seq %d len %zu (%zu)\n",
+ pong, dst,
+ RT_N2H_U16(pong->reqicmph.icmp_echo_id),
+ RT_N2H_U16(pong->reqicmph.icmp_echo_seq),
+ pongsize, reqsize));
+
+ pData->cbIcmpPending += pongsize;
+ TAILQ_INSERT_TAIL(&pData->pongs_expected, pong, queue_entry);
+ pong = NULL; /* callback owns it now */
+ }
+
+ if (pong != NULL)
+ RTMemFree(pong);
+}
+
+
+static VOID WINAPI
+icmpwin_callback_apc(void *ctx, PIO_STATUS_BLOCK iob, ULONG reserved)
+{
+ struct pong *pong = (struct pong *)ctx;
+ if (pong != NULL)
+ icmpwin_callback(pong);
+ RT_NOREF2(iob, reserved);
+}
+
+
+static VOID WINAPI
+icmpwin_callback_old(void *ctx)
+{
+ struct pong *pong = (struct pong *)ctx;
+ if (pong != NULL)
+ icmpwin_callback(pong);
+}
+
+
+/*
+ * Actual callback code for IcmpSendEcho2(). OS version specific
+ * trampoline will free "pong" argument for us.
+ *
+ * Since async callback can be called anytime the thread is alertable,
+ * it's not safe to do any processing here. Instead queue it and
+ * notify the main loop.
+ */
+static void
+icmpwin_callback(struct pong *pong)
+{
+ PNATState pData = pong->pData;
+
+ if (pData == NULL)
+ {
+ RTMemFree(pong);
+ return;
+ }
+
+#ifdef DEBUG
+ {
+ struct pong *expected, *already;
+
+ TAILQ_FOREACH(expected, &pData->pongs_expected, queue_entry)
+ {
+ if (expected == pong)
+ break;
+ }
+ Assert(expected);
+
+ TAILQ_FOREACH(already, &pData->pongs_received, queue_entry)
+ {
+ if (already == pong)
+ break;
+ }
+ Assert(!already);
+ }
+#endif
+
+ TAILQ_REMOVE(&pData->pongs_expected, pong, queue_entry);
+ TAILQ_INSERT_TAIL(&pData->pongs_received, pong, queue_entry);
+
+ WSASetEvent(pData->phEvents[VBOX_ICMP_EVENT_INDEX]);
+}
+
+
+void
+icmpwin_process(PNATState pData)
+{
+ struct pong_tailq pongs;
+
+ if (TAILQ_EMPTY(&pData->pongs_received))
+ return;
+
+ TAILQ_INIT(&pongs);
+ TAILQ_CONCAT(&pongs, &pData->pongs_received, queue_entry);
+
+ while (!TAILQ_EMPTY(&pongs)) {
+ struct pong *pong = TAILQ_FIRST(&pongs);
+ size_t sz;
+
+ sz = RT_UOFFSETOF(struct pong, buf) + pong->bufsize;
+ Assert(pData->cbIcmpPending >= sz);
+ pData->cbIcmpPending -= sz;
+
+ icmpwin_pong(pong);
+
+ TAILQ_REMOVE(&pongs, pong, queue_entry);
+ RTMemFree(pong);
+ }
+}
+
+
+void
+icmpwin_pong(struct pong *pong)
+{
+ PNATState pData;
+ DWORD nreplies;
+ ICMP_ECHO_REPLY *reply;
+ struct mbuf *m;
+ struct ip *ip;
+ struct icmp_echo *icmp;
+ size_t reqsize;
+
+ pData = pong->pData; /* to make slirp_state.h macro hackery work */
+
+ nreplies = IcmpParseReplies(pong->buf, (DWORD)pong->bufsize);
+ if (nreplies == 0)
+ {
+ DWORD error = GetLastError();
+ if (error == IP_REQ_TIMED_OUT)
+ Log2(("NAT: ping %p timed out\n", (void *)pong));
+ else
+ Log2(("NAT: ping %p: IcmpParseReplies: error %d\n",
+ (void *)pong, error));
+ return;
+ }
+
+ reply = (ICMP_ECHO_REPLY *)pong->buf;
+
+ if (reply->Status == IP_SUCCESS)
+ {
+ if (reply->Options.OptionsSize != 0) /* don't do options */
+ return;
+
+ /* need to remap &reply->Address ? */
+ if (/* not a mapped loopback */ 1)
+ {
+ if (reply->Options.Ttl <= 1)
+ return;
+ --reply->Options.Ttl;
+ }
+
+ reqsize = reply->DataSize;
+ if ( (reply->Options.Flags & IP_FLAG_DF) != 0
+ && sizeof(struct ip) + sizeof(struct icmp_echo) + reqsize > (size_t)if_mtu)
+ return;
+
+ m = icmpwin_get_mbuf(pData, reqsize);
+ if (m == NULL)
+ return;
+
+ ip = mtod(m, struct ip *);
+ icmp = (struct icmp_echo *)(mtod(m, char *) + sizeof(*ip));
+
+ /* fill in ip (ip_output0() does the boilerplate for us) */
+ ip->ip_tos = reply->Options.Tos;
+ ip->ip_len = sizeof(*ip) + sizeof(*icmp) + (int)reqsize;
+ ip->ip_off = 0;
+ ip->ip_ttl = reply->Options.Ttl;
+ ip->ip_p = IPPROTO_ICMP;
+ ip->ip_src.s_addr = reply->Address;
+ ip->ip_dst = pong->reqiph.ip.ip_src;
+
+ icmp->icmp_type = ICMP_ECHOREPLY;
+ icmp->icmp_code = 0;
+ icmp->icmp_cksum = 0;
+ icmp->icmp_echo_id = pong->reqicmph.icmp_echo_id;
+ icmp->icmp_echo_seq = pong->reqicmph.icmp_echo_seq;
+
+ m_append(pData, m, (int)reqsize, reply->Data);
+
+ icmp->icmp_cksum = in_cksum_skip(m, ip->ip_len, sizeof(*ip));
+ }
+ else {
+ uint8_t type, code;
+
+ switch (reply->Status) {
+ case IP_DEST_NET_UNREACHABLE:
+ type = ICMP_UNREACH; code = ICMP_UNREACH_NET;
+ break;
+ case IP_DEST_HOST_UNREACHABLE:
+ type = ICMP_UNREACH; code = ICMP_UNREACH_HOST;
+ break;
+ case IP_DEST_PROT_UNREACHABLE:
+ type = ICMP_UNREACH; code = ICMP_UNREACH_PROTOCOL;
+ break;
+ case IP_PACKET_TOO_BIG:
+ type = ICMP_UNREACH; code = ICMP_UNREACH_NEEDFRAG;
+ break;
+ case IP_SOURCE_QUENCH:
+ type = ICMP_SOURCEQUENCH; code = 0;
+ break;
+ case IP_TTL_EXPIRED_TRANSIT:
+ type = ICMP_TIMXCEED; code = ICMP_TIMXCEED_INTRANS;
+ break;
+ case IP_TTL_EXPIRED_REASSEM:
+ type = ICMP_TIMXCEED; code = ICMP_TIMXCEED_REASS;
+ break;
+ default:
+ Log2(("NAT: ping reply status %d, dropped\n", reply->Status));
+ return;
+ }
+
+ Log2(("NAT: ping status %d -> type %d/code %d\n",
+ reply->Status, type, code));
+
+ /*
+ * XXX: we don't know the TTL of the request at the time this
+ * ICMP error was generated (we can guess it was 1 for ttl
+ * exceeded, but don't bother faking it).
+ */
+ m = icmpwin_get_error(pong, type, code);
+ if (m == NULL)
+ return;
+
+ ip = mtod(m, struct ip *);
+
+ ip->ip_tos = reply->Options.Tos;
+ ip->ip_ttl = reply->Options.Ttl; /* XXX: decrement */
+ ip->ip_src.s_addr = reply->Address;
+ }
+
+ Assert(ip->ip_len == m_length(m, NULL));
+ ip_output(pData, NULL, m);
+}
+
+
+/*
+ * Prepare mbuf with ICMP error type/code.
+ * IP source must be filled by the caller.
+ */
+static struct mbuf *
+icmpwin_get_error(struct pong *pong, int type, int code)
+{
+ PNATState pData = pong->pData;
+ struct mbuf *m;
+ struct ip *ip;
+ struct icmp_echo *icmp;
+ size_t reqsize;
+
+ Log2(("NAT: ping error type %d/code %d\n", type, code));
+
+ size_t reqhlen = pong->reqiph.ip.ip_hl << 2;
+ reqsize = reqhlen + sizeof(pong->reqicmph);
+
+ m = icmpwin_get_mbuf(pData, reqsize);
+ if (m == NULL)
+ return NULL;
+
+ ip = mtod(m, struct ip *);
+ icmp = (struct icmp_echo *)(mtod(m, char *) + sizeof(*ip));
+
+ ip->ip_tos = 0;
+ ip->ip_len = sizeof(*ip) + sizeof(*icmp) + (int)reqsize;
+ ip->ip_off = 0;
+ ip->ip_ttl = IPDEFTTL;
+ ip->ip_p = IPPROTO_ICMP;
+ ip->ip_src.s_addr = 0; /* NB */
+ ip->ip_dst = pong->reqiph.ip.ip_src;
+
+ icmp->icmp_type = type;
+ icmp->icmp_code = code;
+ icmp->icmp_cksum = 0;
+ icmp->icmp_echo_id = 0;
+ icmp->icmp_echo_seq = 0;
+
+ /* payload: the IP and ICMP headers of the original request */
+ m_append(pData, m, (int)reqhlen, (caddr_t)&pong->reqiph);
+ m_append(pData, m, sizeof(pong->reqicmph), (caddr_t)&pong->reqicmph);
+
+ icmp->icmp_cksum = in_cksum_skip(m, ip->ip_len, sizeof(*ip));
+
+ return m;
+}
+
+
+/*
+ * Replacing original simple slirp mbufs with real mbufs from freebsd
+ * was a bit messy since assumption are different. This leads to
+ * rather ugly code at times. Hide the gore here.
+ */
+static struct mbuf *
+icmpwin_get_mbuf(PNATState pData, size_t reqsize)
+{
+ struct mbuf *m;
+
+ reqsize += if_maxlinkhdr;
+ reqsize += sizeof(struct ip) + sizeof(struct icmp_echo);
+
+ if (reqsize <= MHLEN)
+ /* good pings come in small packets */
+ m = m_gethdr(pData, M_NOWAIT, MT_HEADER);
+ else
+ m = m_getjcl(pData, M_NOWAIT, MT_HEADER, M_PKTHDR, (int)slirp_size(pData));
+
+ if (m == NULL)
+ return NULL;
+
+ m->m_flags |= M_SKIP_FIREWALL;
+ m->m_data += if_maxlinkhdr; /* reserve leading space for ethernet header */
+
+ m->m_pkthdr.header = mtod(m, void *);
+ m->m_len = sizeof(struct ip) + sizeof(struct icmp_echo);
+
+ return m;
+}
+
diff --git a/src/VBox/Devices/Network/slirp/ip_input.c b/src/VBox/Devices/Network/slirp/ip_input.c
new file mode 100644
index 00000000..b13f0cb0
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/ip_input.c
@@ -0,0 +1,693 @@
+/* $Id: ip_input.c $ */
+/** @file
+ * NAT - IP input.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1982, 1986, 1988, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)ip_input.c 8.2 (Berkeley) 1/4/94
+ * ip_input.c,v 1.11 1994/11/16 10:17:08 jkh Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP are
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include "ip_icmp.h"
+#include "alias.h"
+
+
+/*
+ * IP initialization: fill in IP protocol switch table.
+ * All protocols not implemented in kernel go to raw IP protocol handler.
+ */
+void
+ip_init(PNATState pData)
+{
+ int i = 0;
+ for (i = 0; i < IPREASS_NHASH; ++i)
+ TAILQ_INIT(&ipq[i]);
+ maxnipq = 100; /* ??? */
+ maxfragsperpacket = 16;
+ nipq = 0;
+ ip_currid = tt.tv_sec & 0xffff;
+ udp_init(pData);
+ tcp_init(pData);
+}
+
+/*
+ * Ip input routine. Checksum and byte swap header. If fragmented
+ * try to reassemble. Process options. Pass to next level.
+ */
+void
+ip_input(PNATState pData, struct mbuf *m)
+{
+ register struct ip *ip;
+ int hlen = 0;
+ int mlen = 0;
+ int iplen = 0;
+
+ STAM_PROFILE_START(&pData->StatIP_input, a);
+
+ LogFlowFunc(("ENTER: m = %p\n", m));
+ ip = mtod(m, struct ip *);
+ Log2(("ip_dst=%RTnaipv4(len:%d) m_len = %d\n", ip->ip_dst, RT_N2H_U16(ip->ip_len), m->m_len));
+
+ ipstat.ips_total++;
+
+ mlen = m->m_len;
+
+ if (mlen < sizeof(struct ip))
+ {
+ ipstat.ips_toosmall++;
+ goto bad_free_m;
+ }
+
+ ip = mtod(m, struct ip *);
+ if (ip->ip_v != IPVERSION)
+ {
+ ipstat.ips_badvers++;
+ goto bad_free_m;
+ }
+
+ hlen = ip->ip_hl << 2;
+ if ( hlen < sizeof(struct ip)
+ || hlen > mlen)
+ {
+ /* min header length */
+ ipstat.ips_badhlen++; /* or packet too short */
+ goto bad_free_m;
+ }
+
+ /* keep ip header intact for ICMP reply
+ * ip->ip_sum = cksum(m, hlen);
+ * if (ip->ip_sum) {
+ */
+ if (cksum(m, hlen))
+ {
+ ipstat.ips_badsum++;
+ goto bad_free_m;
+ }
+
+ iplen = RT_N2H_U16(ip->ip_len);
+ if (iplen < hlen)
+ {
+ ipstat.ips_badlen++;
+ goto bad_free_m;
+ }
+
+ /*
+ * Check that the amount of data in the buffers
+ * is as at least much as the IP header would have us expect.
+ * Trim mbufs if longer than we expect.
+ * Drop packet if shorter than we expect.
+ */
+ if (mlen < iplen)
+ {
+ ipstat.ips_tooshort++;
+ goto bad_free_m;
+ }
+
+ /* Should drop packet if mbuf too long? hmmm... */
+ if (mlen > iplen)
+ {
+ m_adj(m, iplen - mlen);
+ mlen = m->m_len;
+ }
+
+ /* source must be unicast */
+ if ((ip->ip_src.s_addr & RT_N2H_U32_C(0xe0000000)) == RT_N2H_U32_C(0xe0000000))
+ goto free_m;
+
+ /*
+ * Drop multicast (class d) and reserved (class e) here. The rest
+ * of the code is not yet prepared to deal with it. IGMP is not
+ * implemented either.
+ */
+ if ( (ip->ip_dst.s_addr & RT_N2H_U32_C(0xe0000000)) == RT_N2H_U32_C(0xe0000000)
+ && ip->ip_dst.s_addr != 0xffffffff)
+ {
+ goto free_m;
+ }
+
+
+ /* do we need to "forward" this packet? */
+ if (!CTL_CHECK_MINE(ip->ip_dst.s_addr))
+ {
+ if (ip->ip_ttl <= 1)
+ {
+ /* icmp_error expects these in host order */
+ NTOHS(ip->ip_len);
+ NTOHS(ip->ip_id);
+ NTOHS(ip->ip_off);
+
+ icmp_error(pData, m, ICMP_TIMXCEED, ICMP_TIMXCEED_INTRANS, 0, "ttl");
+ goto no_free_m;
+ }
+
+ /* ignore packets to other nodes from our private network */
+ if ( CTL_CHECK_NETWORK(ip->ip_dst.s_addr)
+ && !CTL_CHECK_BROADCAST(ip->ip_dst.s_addr))
+ {
+ /* XXX: send ICMP_REDIRECT_HOST to be pedantic? */
+ goto free_m;
+ }
+
+ ip->ip_ttl--;
+ if (ip->ip_sum > RT_H2N_U16_C(0xffffU - (1 << 8)))
+ ip->ip_sum += RT_H2N_U16_C(1 << 8) + 1;
+ else
+ ip->ip_sum += RT_H2N_U16_C(1 << 8);
+ }
+
+ /* run it through libalias */
+ {
+ int rc;
+ if (!(m->m_flags & M_SKIP_FIREWALL))
+ {
+ STAM_PROFILE_START(&pData->StatALIAS_input, b);
+ rc = LibAliasIn(pData->proxy_alias, mtod(m, char *), mlen);
+ STAM_PROFILE_STOP(&pData->StatALIAS_input, b);
+ Log2(("NAT: LibAlias return %d\n", rc));
+ }
+ else
+ m->m_flags &= ~M_SKIP_FIREWALL;
+
+#if 0 /* disabled: no module we use does it in this direction */
+ /*
+ * XXX: spooky action at a distance - libalias may modify the
+ * packet and will update ip_len to reflect the new length.
+ */
+ if (iplen != RT_N2H_U16(ip->ip_len))
+ {
+ iplen = RT_N2H_U16(ip->ip_len);
+ m->m_len = iplen;
+ mlen = m->m_len;
+ }
+#endif
+ }
+
+ /*
+ * Convert fields to host representation.
+ */
+ NTOHS(ip->ip_len);
+ NTOHS(ip->ip_id);
+ NTOHS(ip->ip_off);
+
+ /*
+ * If offset or IP_MF are set, must reassemble.
+ * Otherwise, nothing need be done.
+ * (We could look in the reassembly queue to see
+ * if the packet was previously fragmented,
+ * but it's not worth the time; just let them time out.)
+ *
+ */
+ if (ip->ip_off & (IP_MF | IP_OFFMASK))
+ {
+ m = ip_reass(pData, m);
+ if (m == NULL)
+ goto no_free_m;
+ ip = mtod(m, struct ip *);
+ hlen = ip->ip_hl << 2;
+ }
+ else
+ ip->ip_len -= hlen;
+
+ /*
+ * Switch out to protocol's input routine.
+ */
+ ipstat.ips_delivered++;
+ switch (ip->ip_p)
+ {
+ case IPPROTO_TCP:
+ tcp_input(pData, m, hlen, (struct socket *)NULL);
+ break;
+ case IPPROTO_UDP:
+ udp_input(pData, m, hlen);
+ break;
+ case IPPROTO_ICMP:
+ icmp_input(pData, m, hlen);
+ break;
+ default:
+ ipstat.ips_noproto++;
+ m_freem(pData, m);
+ }
+ goto no_free_m;
+
+bad_free_m:
+ Log2(("NAT: IP datagram to %RTnaipv4 with size(%d) claimed as bad\n",
+ ip->ip_dst, ip->ip_len));
+free_m:
+ m_freem(pData, m);
+no_free_m:
+ STAM_PROFILE_STOP(&pData->StatIP_input, a);
+ LogFlowFuncLeave();
+ return;
+}
+
+struct mbuf *
+ip_reass(PNATState pData, struct mbuf* m)
+{
+ struct ip *ip;
+ struct mbuf *p, *q, *nq;
+ struct ipq_t *fp = NULL;
+ struct ipqhead *head;
+ int i, hlen, next;
+ u_short hash;
+
+ /* If maxnipq or maxfragsperpacket are 0, never accept fragments. */
+ LogFlowFunc(("ENTER: m:%p\n", m));
+ if ( maxnipq == 0
+ || maxfragsperpacket == 0)
+ {
+ ipstat.ips_fragments++;
+ ipstat.ips_fragdropped++;
+ m_freem(pData, m);
+ LogFlowFunc(("LEAVE: NULL\n"));
+ return (NULL);
+ }
+
+ ip = mtod(m, struct ip *);
+ hlen = ip->ip_hl << 2;
+
+ hash = IPREASS_HASH(ip->ip_src.s_addr, ip->ip_id);
+ head = &ipq[hash];
+
+ /*
+ * Look for queue of fragments
+ * of this datagram.
+ */
+ TAILQ_FOREACH(fp, head, ipq_list)
+ if (ip->ip_id == fp->ipq_id &&
+ ip->ip_src.s_addr == fp->ipq_src.s_addr &&
+ ip->ip_dst.s_addr == fp->ipq_dst.s_addr &&
+ ip->ip_p == fp->ipq_p)
+ goto found;
+
+ fp = NULL;
+
+ /*
+ * Attempt to trim the number of allocated fragment queues if it
+ * exceeds the administrative limit.
+ */
+ if ((nipq > maxnipq) && (maxnipq > 0))
+ {
+ /*
+ * drop something from the tail of the current queue
+ * before proceeding further
+ */
+ struct ipq_t *pHead = TAILQ_LAST(head, ipqhead);
+ if (pHead == NULL)
+ {
+ /* gak */
+ for (i = 0; i < IPREASS_NHASH; i++)
+ {
+ struct ipq_t *pTail = TAILQ_LAST(&ipq[i], ipqhead);
+ if (pTail)
+ {
+ ipstat.ips_fragtimeout += pTail->ipq_nfrags;
+ ip_freef(pData, &ipq[i], pTail);
+ break;
+ }
+ }
+ }
+ else
+ {
+ ipstat.ips_fragtimeout += pHead->ipq_nfrags;
+ ip_freef(pData, head, pHead);
+ }
+ }
+
+found:
+ /*
+ * Adjust ip_len to not reflect header,
+ * convert offset of this to bytes.
+ */
+ ip->ip_len -= hlen;
+ if (ip->ip_off & IP_MF)
+ {
+ /*
+ * Make sure that fragments have a data length
+ * that's a non-zero multiple of 8 bytes.
+ */
+ if (ip->ip_len == 0 || (ip->ip_len & 0x7) != 0)
+ {
+ ipstat.ips_toosmall++; /* XXX */
+ goto dropfrag;
+ }
+ m->m_flags |= M_FRAG;
+ }
+ else
+ m->m_flags &= ~M_FRAG;
+ ip->ip_off <<= 3;
+
+
+ /*
+ * Attempt reassembly; if it succeeds, proceed.
+ * ip_reass() will return a different mbuf.
+ */
+ ipstat.ips_fragments++;
+
+ /* Previous ip_reass() started here. */
+ /*
+ * Presence of header sizes in mbufs
+ * would confuse code below.
+ */
+ m->m_data += hlen;
+ m->m_len -= hlen;
+
+ /*
+ * If first fragment to arrive, create a reassembly queue.
+ */
+ if (fp == NULL)
+ {
+ fp = RTMemAlloc(sizeof(struct ipq_t));
+ if (fp == NULL)
+ goto dropfrag;
+ TAILQ_INSERT_HEAD(head, fp, ipq_list);
+ nipq++;
+ fp->ipq_nfrags = 1;
+ fp->ipq_ttl = IPFRAGTTL;
+ fp->ipq_p = ip->ip_p;
+ fp->ipq_id = ip->ip_id;
+ fp->ipq_src = ip->ip_src;
+ fp->ipq_dst = ip->ip_dst;
+ fp->ipq_frags = m;
+ m->m_nextpkt = NULL;
+ goto done;
+ }
+ else
+ {
+ fp->ipq_nfrags++;
+ }
+
+#define GETIP(m) ((struct ip*)((m)->m_pkthdr.header))
+
+ /*
+ * Find a segment which begins after this one does.
+ */
+ for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt)
+ if (GETIP(q)->ip_off > ip->ip_off)
+ break;
+
+ /*
+ * If there is a preceding segment, it may provide some of
+ * our data already. If so, drop the data from the incoming
+ * segment. If it provides all of our data, drop us, otherwise
+ * stick new segment in the proper place.
+ *
+ * If some of the data is dropped from the preceding
+ * segment, then it's checksum is invalidated.
+ */
+ if (p)
+ {
+ i = GETIP(p)->ip_off + GETIP(p)->ip_len - ip->ip_off;
+ if (i > 0)
+ {
+ if (i >= ip->ip_len)
+ goto dropfrag;
+ m_adj(m, i);
+ ip->ip_off += i;
+ ip->ip_len -= i;
+ }
+ m->m_nextpkt = p->m_nextpkt;
+ p->m_nextpkt = m;
+ }
+ else
+ {
+ m->m_nextpkt = fp->ipq_frags;
+ fp->ipq_frags = m;
+ }
+
+ /*
+ * While we overlap succeeding segments trim them or,
+ * if they are completely covered, dequeue them.
+ */
+ for (; q != NULL && ip->ip_off + ip->ip_len > GETIP(q)->ip_off;
+ q = nq)
+ {
+ i = (ip->ip_off + ip->ip_len) - GETIP(q)->ip_off;
+ if (i < GETIP(q)->ip_len)
+ {
+ GETIP(q)->ip_len -= i;
+ GETIP(q)->ip_off += i;
+ m_adj(q, i);
+ break;
+ }
+ nq = q->m_nextpkt;
+ m->m_nextpkt = nq;
+ ipstat.ips_fragdropped++;
+ fp->ipq_nfrags--;
+ m_freem(pData, q);
+ }
+
+ /*
+ * Check for complete reassembly and perform frag per packet
+ * limiting.
+ *
+ * Frag limiting is performed here so that the nth frag has
+ * a chance to complete the packet before we drop the packet.
+ * As a result, n+1 frags are actually allowed per packet, but
+ * only n will ever be stored. (n = maxfragsperpacket.)
+ *
+ */
+ next = 0;
+ for (p = NULL, q = fp->ipq_frags; q; p = q, q = q->m_nextpkt)
+ {
+ if (GETIP(q)->ip_off != next)
+ {
+ if (fp->ipq_nfrags > maxfragsperpacket)
+ {
+ ipstat.ips_fragdropped += fp->ipq_nfrags;
+ ip_freef(pData, head, fp);
+ }
+ goto done;
+ }
+ next += GETIP(q)->ip_len;
+ }
+ /* Make sure the last packet didn't have the IP_MF flag */
+ if (p->m_flags & M_FRAG)
+ {
+ if (fp->ipq_nfrags > maxfragsperpacket)
+ {
+ ipstat.ips_fragdropped += fp->ipq_nfrags;
+ ip_freef(pData, head, fp);
+ }
+ goto done;
+ }
+
+ /*
+ * Reassembly is complete. Make sure the packet is a sane size.
+ */
+ q = fp->ipq_frags;
+ ip = GETIP(q);
+ hlen = ip->ip_hl << 2;
+ if (next + hlen > IP_MAXPACKET)
+ {
+ ipstat.ips_fragdropped += fp->ipq_nfrags;
+ ip_freef(pData, head, fp);
+ goto done;
+ }
+
+ /*
+ * Concatenate fragments.
+ */
+ m = q;
+ nq = q->m_nextpkt;
+ q->m_nextpkt = NULL;
+ for (q = nq; q != NULL; q = nq)
+ {
+ nq = q->m_nextpkt;
+ q->m_nextpkt = NULL;
+ m_cat(pData, m, q);
+
+ m->m_len += hlen;
+ m->m_data -= hlen;
+ ip = mtod(m, struct ip *); /*update ip pointer */
+ hlen = ip->ip_hl << 2;
+ m->m_len -= hlen;
+ m->m_data += hlen;
+ }
+ m->m_len += hlen;
+ m->m_data -= hlen;
+
+ /*
+ * Create header for new ip packet by modifying header of first
+ * packet; dequeue and discard fragment reassembly header.
+ * Make header visible.
+ */
+
+ ip->ip_len = next;
+ ip->ip_src = fp->ipq_src;
+ ip->ip_dst = fp->ipq_dst;
+ TAILQ_REMOVE(head, fp, ipq_list);
+ nipq--;
+ RTMemFree(fp);
+
+ Assert((ip->ip_len == next));
+ /* some debugging cruft by sklower, below, will go away soon */
+#if 0
+ if (m->m_flags & M_PKTHDR) /* XXX this should be done elsewhere */
+ m_fixhdr(m);
+#endif
+ ipstat.ips_reassembled++;
+ LogFlowFunc(("LEAVE: %p\n", m));
+ return (m);
+
+dropfrag:
+ ipstat.ips_fragdropped++;
+ if (fp != NULL)
+ fp->ipq_nfrags--;
+ m_freem(pData, m);
+
+done:
+ LogFlowFunc(("LEAVE: NULL\n"));
+ return NULL;
+
+#undef GETIP
+}
+
+void
+ip_freef(PNATState pData, struct ipqhead *fhp, struct ipq_t *fp)
+{
+ struct mbuf *q;
+
+ while (fp->ipq_frags)
+ {
+ q = fp->ipq_frags;
+ fp->ipq_frags = q->m_nextpkt;
+ m_freem(pData, q);
+ }
+ TAILQ_REMOVE(fhp, fp, ipq_list);
+ RTMemFree(fp);
+ nipq--;
+}
+
+/*
+ * IP timer processing;
+ * if a timer expires on a reassembly
+ * queue, discard it.
+ */
+void
+ip_slowtimo(PNATState pData)
+{
+ register struct ipq_t *fp;
+
+ /* XXX: the fragment expiration is the same but requier
+ * additional loop see (see ip_input.c in FreeBSD tree)
+ */
+ int i;
+ LogFlow(("ip_slowtimo:\n"));
+ for (i = 0; i < IPREASS_NHASH; i++)
+ {
+ for(fp = TAILQ_FIRST(&ipq[i]); fp;)
+ {
+ struct ipq_t *fpp;
+
+ fpp = fp;
+ fp = TAILQ_NEXT(fp, ipq_list);
+ if(--fpp->ipq_ttl == 0)
+ {
+ ipstat.ips_fragtimeout += fpp->ipq_nfrags;
+ ip_freef(pData, &ipq[i], fpp);
+ }
+ }
+ }
+ /*
+ * If we are over the maximum number of fragments
+ * (due to the limit being lowered), drain off
+ * enough to get down to the new limit.
+ */
+ if (maxnipq >= 0 && nipq > maxnipq)
+ {
+ for (i = 0; i < IPREASS_NHASH; i++)
+ {
+ while (nipq > maxnipq && !TAILQ_EMPTY(&ipq[i]))
+ {
+ ipstat.ips_fragdropped += TAILQ_FIRST(&ipq[i])->ipq_nfrags;
+ ip_freef(pData, &ipq[i], TAILQ_FIRST(&ipq[i]));
+ }
+ }
+ }
+}
+
+
+/*
+ * Strip out IP options, at higher
+ * level protocol in the kernel.
+ * Second argument is buffer to which options
+ * will be moved, and return value is their length.
+ * (XXX) should be deleted; last arg currently ignored.
+ */
+void
+ip_stripoptions(struct mbuf *m, struct mbuf *mopt)
+{
+ register int i;
+ struct ip *ip = mtod(m, struct ip *);
+ register caddr_t opts;
+ int olen;
+ NOREF(mopt); /** @todo do we really will need this options buffer? */
+
+ olen = (ip->ip_hl<<2) - sizeof(struct ip);
+ opts = (caddr_t)(ip + 1);
+ i = m->m_len - (sizeof(struct ip) + olen);
+ memcpy(opts, opts + olen, (unsigned)i);
+ m->m_len -= olen;
+
+ ip->ip_hl = sizeof(struct ip) >> 2;
+}
diff --git a/src/VBox/Devices/Network/slirp/ip_output.c b/src/VBox/Devices/Network/slirp/ip_output.c
new file mode 100644
index 00000000..3b37a47b
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/ip_output.c
@@ -0,0 +1,354 @@
+/* $Id: ip_output.c $ */
+/** @file
+ * NAT - IP output.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)ip_output.c 8.3 (Berkeley) 1/21/94
+ * ip_output.c,v 1.9 1994/11/16 10:17:10 jkh Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP are
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include <iprt/errcore.h>
+#include "alias.h"
+
+static const uint8_t broadcast_ethaddr[6] =
+{
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+static int rt_lookup_in_cache(PNATState pData, uint32_t dst, uint8_t *ether)
+{
+ int rc;
+ LogFlowFunc(("ENTER: dst:%RTnaipv4, ether:%RTmac\n", dst, ether));
+ if (dst == INADDR_BROADCAST)
+ {
+ memcpy(ether, broadcast_ethaddr, ETH_ALEN);
+ LogFlowFunc(("LEAVE: VINF_SUCCESS\n"));
+ return VINF_SUCCESS;
+ }
+
+ rc = slirp_arp_lookup_ether_by_ip(pData, dst, ether);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("LEAVE: %Rrc\n", rc));
+ return rc;
+ }
+
+ rc = bootp_cache_lookup_ether_by_ip(pData, dst, ether);
+ if (RT_SUCCESS(rc))
+ {
+ LogFlowFunc(("LEAVE: %Rrc\n", rc));
+ return rc;
+ }
+ /*
+ * no chance to send this packet, sorry, we will request ether address via ARP
+ */
+ slirp_arp_who_has(pData, dst);
+ LogFlowFunc(("LEAVE: VERR_NOT_FOUND\n"));
+ return VERR_NOT_FOUND;
+}
+
+/*
+ * IP output. The packet in mbuf chain m contains a skeletal IP
+ * header (with len, off, ttl, proto, tos, src, dst).
+ * The mbuf chain containing the packet will be freed.
+ * The mbuf opt, if present, will not be freed.
+ */
+int
+ip_output(PNATState pData, struct socket *so, struct mbuf *m0)
+{
+ return ip_output0(pData, so, m0, 0);
+}
+
+/* This function will free m0! */
+int
+ip_output0(PNATState pData, struct socket *so, struct mbuf *m0, int urg)
+{
+ register struct ip *ip;
+ register struct mbuf *m = m0;
+ register int hlen = sizeof(struct ip);
+ int len, off, error = 0;
+ struct ethhdr *eh = NULL;
+ uint8_t eth_dst[ETH_ALEN];
+ int rc = 1;
+
+ STAM_PROFILE_START(&pData->StatIP_output, a);
+
+#ifdef LOG_ENABLED
+ LogFlowFunc(("ip_output: so = %R[natsock], m0 = %p\n", so, m0));
+#else
+ NOREF(so);
+#endif
+
+ M_ASSERTPKTHDR(m);
+ Assert(m->m_pkthdr.header);
+
+#if 0 /* We do no options */
+ if (opt)
+ {
+ m = ip_insertoptions(m, opt, &len);
+ hlen = len;
+ }
+#endif
+ ip = mtod(m, struct ip *);
+ LogFunc(("ip(src:%RTnaipv4, dst:%RTnaipv4)\n", ip->ip_src, ip->ip_dst));
+ /*
+ * Fill in IP header.
+ */
+ ip->ip_v = IPVERSION;
+ ip->ip_off &= IP_DF;
+ ip->ip_id = RT_H2N_U16(ip_currid);
+ ip->ip_hl = hlen >> 2;
+ ip_currid++;
+ ipstat.ips_localout++;
+
+ /* Current TCP/IP stack hasn't routing information at
+ * all so we need to calculate destination ethernet address
+ */
+ rc = rt_lookup_in_cache(pData, ip->ip_dst.s_addr, eth_dst);
+ if (RT_FAILURE(rc))
+ goto exit_drop_package;
+
+ eh = (struct ethhdr *)(m->m_data - ETH_HLEN);
+ /*
+ * If small enough for interface, can just send directly.
+ */
+ if ((u_int16_t)ip->ip_len <= if_mtu)
+ {
+ ip->ip_len = RT_H2N_U16((u_int16_t)ip->ip_len);
+ ip->ip_off = RT_H2N_U16((u_int16_t)ip->ip_off);
+ ip->ip_sum = 0;
+ ip->ip_sum = cksum(m, hlen);
+
+ if (!(m->m_flags & M_SKIP_FIREWALL)){
+ STAM_PROFILE_START(&pData->StatALIAS_output, b);
+ rc = LibAliasOut(pData->proxy_alias, mtod(m, char *), m_length(m, NULL));
+ if (rc == PKT_ALIAS_IGNORED)
+ {
+ Log(("NAT: packet was droppped\n"));
+ goto exit_drop_package;
+ }
+ STAM_PROFILE_STOP(&pData->StatALIAS_output, b);
+ }
+ else
+ m->m_flags &= ~M_SKIP_FIREWALL;
+
+ memcpy(eh->h_source, eth_dst, ETH_ALEN);
+
+ LogFlowFunc(("ip(ip_src:%RTnaipv4, ip_dst:%RTnaipv4)\n",
+ ip->ip_src, ip->ip_dst));
+ if_encap(pData, ETH_P_IP, m, urg? ETH_ENCAP_URG : 0);
+ goto done;
+ }
+
+ /*
+ * Too large for interface; fragment if possible.
+ * Must be able to put at least 8 bytes per fragment.
+ */
+ if (ip->ip_off & IP_DF)
+ {
+ error = -1;
+ ipstat.ips_cantfrag++;
+ goto exit_drop_package;
+ }
+
+ len = (if_mtu - hlen) &~ 7; /* ip databytes per packet */
+ if (len < 8)
+ {
+ error = -1;
+ goto exit_drop_package;
+ }
+
+ {
+ int mhlen, firstlen = len;
+ struct mbuf **mnext = &m->m_nextpkt;
+ char *buf; /* intermediate buffer we'll use for a copy of the original packet */
+ /*
+ * Loop through length of segment after first fragment,
+ * make new header and copy data of each part and link onto chain.
+ */
+ m0 = m;
+ mhlen = ip->ip_hl << 2;
+ Log(("NAT:ip:frag: mhlen = %d\n", mhlen));
+ for (off = hlen + len; off < (u_int16_t)ip->ip_len; off += len)
+ {
+ register struct ip *mhip;
+ m = m_getjcl(pData, M_NOWAIT, MT_HEADER , M_PKTHDR, slirp_size(pData));
+ if (m == 0)
+ {
+ error = -1;
+ ipstat.ips_odropped++;
+ goto exit_drop_package;
+ }
+ m->m_data += if_maxlinkhdr;
+ mhip = mtod(m, struct ip *);
+ *mhip = *ip;
+ m->m_pkthdr.header = mtod(m, void *);
+ /* we've calculated eth_dst for first packet */
+#if 0 /* No options */
+ if (hlen > sizeof (struct ip))
+ {
+ mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip);
+ mhip->ip_hl = mhlen >> 2;
+ }
+#endif
+ m->m_len = mhlen;
+ mhip->ip_off = ((off - mhlen) >> 3) + (ip->ip_off & ~IP_MF);
+ if (ip->ip_off & IP_MF)
+ mhip->ip_off |= IP_MF;
+ if (off + len >= (u_int16_t)ip->ip_len)
+ len = (u_int16_t)ip->ip_len - off;
+ else
+ mhip->ip_off |= IP_MF;
+ mhip->ip_len = RT_H2N_U16((u_int16_t)(len + mhlen));
+
+ buf = RTMemAlloc(len);
+ Log(("NAT:ip:frag: alloc = %d\n", len));
+ m_copydata(m0, off, len, buf); /* copy to buffer */
+ Log(("NAT:ip:frag: m_copydata(m0 = %p,off = %d, len = %d,)\n", m0, off, len));
+
+ m->m_data += mhlen;
+ m->m_len -= mhlen;
+ m_copyback(pData, m, 0, len, buf); /* copy from buffer */
+ Log(("NAT:ip:frag: m_copyback(m = %p,, len = %d,)\n", m, len));
+ m->m_data -= mhlen;
+ m->m_len += mhlen;
+ RTMemFree(buf);
+ Assert((m->m_len == (mhlen + len)));
+
+ mhip->ip_off = RT_H2N_U16((u_int16_t)(mhip->ip_off));
+ mhip->ip_sum = 0;
+ mhip->ip_sum = cksum(m, mhlen);
+ *mnext = m;
+ mnext = &m->m_nextpkt;
+ ipstat.ips_ofragments++;
+ }
+ /*
+ * Update first fragment by trimming what's been copied out
+ * and updating header, then send each fragment (in order).
+ *
+ * note: m_adj do all required releases for chained mbufs.
+ */
+ m = m0;
+ m_adj(m, mhlen + firstlen - (u_int16_t)ip->ip_len);
+ Log(("NAT:ip:frag: m_adj(m(m_len:%d) = %p, len = %d)\n", m->m_len, m, mhlen + firstlen - (u_int16_t)ip->ip_len));
+ ip->ip_len = RT_H2N_U16((u_int16_t)mhlen + firstlen);
+ ip->ip_off = RT_H2N_U16((u_int16_t)(ip->ip_off | IP_MF));
+ ip->ip_sum = 0;
+ ip->ip_sum = cksum(m, mhlen);
+
+ if (!(m->m_flags & M_SKIP_FIREWALL)){
+ /** @todo We can't alias all fragments because the way libalias processing
+ * the fragments brake the sequence. libalias put alias_address to the source
+ * address of IP header of fragment, while IP header of the first packet is
+ * is unmodified. That confuses guest's TCP/IP stack and guest drop the sequence.
+ * Here we're letting libalias to process the first packet and send the rest as is,
+ * it's exactly the way in of packet are processing in proxyonly way.
+ * Here we need investigate what should be done to avoid such behavior and find right
+ * solution.
+ */
+ int rcLa;
+
+ rcLa = LibAliasOut(pData->proxy_alias, mtod(m, char *), m->m_len);
+ if (rcLa == PKT_ALIAS_IGNORED)
+ {
+ Log(("NAT: packet was droppped\n"));
+ goto exit_drop_package;
+ }
+ Log2(("NAT: LibAlias return %d\n", rcLa));
+ }
+ else
+ m->m_flags &= ~M_SKIP_FIREWALL;
+ for (m = m0; m; m = m0)
+ {
+ m0 = m->m_nextpkt;
+ m->m_nextpkt = 0;
+ if (error == 0)
+ {
+ m->m_data -= ETH_HLEN;
+ eh = mtod(m, struct ethhdr *);
+ m->m_data += ETH_HLEN;
+ memcpy(eh->h_source, eth_dst, ETH_ALEN);
+
+ Log(("NAT:ip:frag: if_encap(,,m(m_len = %d) = %p,0)\n", m->m_len, m));
+ if_encap(pData, ETH_P_IP, m, 0);
+ }
+ else
+ m_freem(pData, m);
+ }
+
+ if (error == 0)
+ ipstat.ips_fragmented++;
+ }
+
+done:
+ STAM_PROFILE_STOP(&pData->StatIP_output, a);
+ LogFlowFunc(("LEAVE: %d\n", error));
+ return error;
+
+exit_drop_package:
+ m_freem(pData, m0);
+ STAM_PROFILE_STOP(&pData->StatIP_output, a);
+ LogFlowFunc(("LEAVE: %d\n", error));
+ return error;
+}
diff --git a/src/VBox/Devices/Network/slirp/libalias/HISTORY b/src/VBox/Devices/Network/slirp/libalias/HISTORY
new file mode 100644
index 00000000..01744351
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/HISTORY
@@ -0,0 +1,145 @@
+$FreeBSD: src/sys/netinet/libalias/HISTORY,v 1.9.32.1 2009/04/15 03:14:26 kensmith Exp $
+
+Version 1.0: August 11, 1996 (cjm)
+
+Version 1.1: August 20, 1996 (cjm)
+ - Host accepts incoming connections for ports 0 to 1023.
+
+Version 1.2: September 7, 1996 (cjm)
+ - Fragment handling error in alias_db.c corrected.
+
+Version 1.3: September 15, 1996 (cjm)
+ - Generalized mechanism for handling incoming
+ connections (no more 0 to 1023 restriction).
+
+ - Increased ICMP support (will handle traceroute now).
+
+ - Improved TCP close connection logic.
+
+Version 1.4: September 16, 1996 (cjm)
+
+Version 1.5: September 17, 1996 (cjm)
+ - Corrected error in handling incoming UDP packets
+ with zero checksum.
+
+Version 1.6: September 18, 1996
+ - Simplified ICMP data storage. Will now handle
+ tracert from Win95 and NT as well as FreeBSD
+ traceroute, which uses UDP packets to non-existent
+ ports.
+
+Version 1.7: January 9, 1997 (cjm)
+ - Reduced malloc() activity for ICMP echo and
+ timestamp requests.
+
+ - Added handling for out-of-order IP fragments.
+
+ - Switched to differential checksum computation
+ for IP headers (TCP, UDP and ICMP checksums
+ were already differential).
+
+ - Accepts FTP data connections from other than
+ port 20. This allows one ftp connections
+ from two hosts which are both running packet
+ aliasing.
+
+ - Checksum error on FTP transfers. Problem
+ in code located by Martin Renters and
+ Brian Somers.
+
+Version 1.8: January 14, 1997 (cjm)
+ - Fixed data type error in function StartPoint()
+ in alias_db.c (this bug did not exist before v1.7)
+ Problem in code located by Ari Suutari.
+
+Version 1.9: February 1, 1997 (Eivind Eklund <perhaps@yes.no>)
+ - Added support for IRC DCC (ee)
+
+ - Changed the aliasing routines to use ANSI style
+ throughout (ee)
+
+ - Minor API changes for integration with other
+ programs than PPP (ee)
+
+ - Fixed minor security hole in alias_ftp.c for
+ other applications of the aliasing software.
+ Hole could _not_ manifest in ppp+pktAlias, but
+ could potentially manifest in other applications
+ of the aliasing. (ee)
+
+ - Connections initiated from packet aliasing
+ host machine will not have their port number
+ aliased unless it conflicts with an aliasing
+ port already being used. (There is an option
+ to disable this for debugging) (cjm)
+
+ - Sockets will be allocated in cases where
+ there might be port interference with the
+ host machine. This can be disabled in cases
+ where the ppp host will be acting purely as a
+ masquerading router and not generate any
+ traffic of its own.
+ (cjm)
+
+Version 2.0: March, 1997 (cjm)
+ - Aliasing links are cleared only when a host interface address
+ changes.
+
+ - PacketAliasPermanentLink() API added.
+
+ - Option for only aliasing private, unregistered
+ IP addresses added.
+
+ - Substantial rework to the aliasing lookup engine.
+
+Version 2.1: May, 1997 (cjm)
+ - Continuing rework to the aliasing lookup engine
+ to support multiple incoming addresses and static
+ NAT. PacketAliasRedirectPort() and
+ PacketAliasRedirectAddr() added to API.
+
+ - Now supports outgoing as well as incoming ICMP
+ error messages.
+
+Version 2.2: July, 1997 (cjm)
+ - Rationalized API function names to all begin with
+ "PacketAlias..." Old function names are retained
+ for backwards compatibility.
+
+ - Packet aliasing engine will now free memory of
+ fragments which are never resolved after a timeout
+ period. Once a fragment is resolved, it becomes
+ the users responsibility to free the memory.
+
+Version 2.3: August 11, 1997 (cjm)
+ - Problem associated with socket file descriptor
+ accumulation in alias_db.c corrected. The sockets
+ had to be closed when a binding failed. Problem
+ in code located by Gordon Burditt.
+
+Version 2.4: September 1, 1997 (cjm)
+ - PKT_ALIAS_UNREGISTERED_ONLY option repaired.
+ This part of the code was incorrectly re-implemented
+ in version 2.1.
+
+Version 2.5: December, 1997 (ee)
+ - Added PKT_ALIAS_PUNCH_FW mode for firewall
+ bypass of FTP/IRC DCC data connections. Also added
+ improved TCP connection monitoring.
+
+Version 2.6: May, 1998 (amurai)
+ - Added supporting routine for NetBios over TCP/IP.
+
+Version 3.0: January 1, 1999
+ - Transparent proxying support added.
+ - PPTP redirecting support added based on patches
+ contributed by Dru Nelson <dnelson@redwoodsoft.com>.
+
+Version 3.1: May, 2000 (Erik Salander, erik@whistle.com)
+ - Added support to alias 227 replies, allows aliasing for
+ FTP servers in passive mode.
+ - Added support for PPTP aliasing.
+
+Version 3.2: July, 2000 (Erik Salander, erik@whistle.com and
+ Junichi Satoh, junichi@junichi.org)
+ - Added support for streaming media (RTSP and PNA) aliasing.
diff --git a/src/VBox/Devices/Network/slirp/libalias/Makefile.kup b/src/VBox/Devices/Network/slirp/libalias/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/Makefile.kup
diff --git a/src/VBox/Devices/Network/slirp/libalias/alias.c b/src/VBox/Devices/Network/slirp/libalias/alias.c
new file mode 100644
index 00000000..4694503f
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/alias.c
@@ -0,0 +1,1758 @@
+/*-
+ * Copyright (c) 2001 Charles Mott <cm@linktel.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHOR 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 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 VBOX
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netinet/libalias/alias.c,v 1.58.2.1.4.1 2009/04/15 03:14:26 kensmith Exp $");
+#endif
+/*
+ Alias.c provides supervisory control for the functions of the
+ packet aliasing software. It consists of routines to monitor
+ TCP connection state, protocol-specific aliasing routines,
+ fragment handling and the following outside world functional
+ interfaces: SaveFragmentPtr, GetFragmentPtr, FragmentAliasIn,
+ PacketAliasIn and PacketAliasOut.
+
+ The other C program files are briefly described. The data
+ structure framework which holds information needed to translate
+ packets is encapsulated in alias_db.c. Data is accessed by
+ function calls, so other segments of the program need not know
+ about the underlying data structures. Alias_ftp.c contains
+ special code for modifying the ftp PORT command used to establish
+ data connections, while alias_irc.c does the same for IRC
+ DCC. Alias_util.c contains a few utility routines.
+
+ Version 1.0 August, 1996 (cjm)
+
+ Version 1.1 August 20, 1996 (cjm)
+ PPP host accepts incoming connections for ports 0 to 1023.
+ (Gary Roberts pointed out the need to handle incoming
+ connections.)
+
+ Version 1.2 September 7, 1996 (cjm)
+ Fragment handling error in alias_db.c corrected.
+ (Tom Torrance helped fix this problem.)
+
+ Version 1.4 September 16, 1996 (cjm)
+ - A more generalized method for handling incoming
+ connections, without the 0-1023 restriction, is
+ implemented in alias_db.c
+ - Improved ICMP support in alias.c. Traceroute
+ packet streams can now be correctly aliased.
+ - TCP connection closing logic simplified in
+ alias.c and now allows for additional 1 minute
+ "grace period" after FIN or RST is observed.
+
+ Version 1.5 September 17, 1996 (cjm)
+ Corrected error in handling incoming UDP packets with 0 checksum.
+ (Tom Torrance helped fix this problem.)
+
+ Version 1.6 September 18, 1996 (cjm)
+ Simplified ICMP aliasing scheme. Should now support
+ traceroute from Win95 as well as FreeBSD.
+
+ Version 1.7 January 9, 1997 (cjm)
+ - Out-of-order fragment handling.
+ - IP checksum error fixed for ftp transfers
+ from aliasing host.
+ - Integer return codes added to all
+ aliasing/de-aliasing functions.
+ - Some obsolete comments cleaned up.
+ - Differential checksum computations for
+ IP header (TCP, UDP and ICMP were already
+ differential).
+
+ Version 2.1 May 1997 (cjm)
+ - Added support for outgoing ICMP error
+ messages.
+ - Added two functions PacketAliasIn2()
+ and PacketAliasOut2() for dynamic address
+ control (e.g. round-robin allocation of
+ incoming packets).
+
+ Version 2.2 July 1997 (cjm)
+ - Rationalized API function names to begin
+ with "PacketAlias..."
+ - Eliminated PacketAliasIn2() and
+ PacketAliasOut2() as poorly conceived.
+
+ Version 2.3 Dec 1998 (dillon)
+ - Major bounds checking additions, see FreeBSD/CVS
+
+ Version 3.1 May, 2000 (salander)
+ - Added hooks to handle PPTP.
+
+ Version 3.2 July, 2000 (salander and satoh)
+ - Added PacketUnaliasOut routine.
+ - Added hooks to handle RTSP/RTP.
+
+ See HISTORY file for additional revisions.
+*/
+
+#ifndef VBOX
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#else
+#include <sys/types.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <dlfcn.h>
+#include <errno.h>
+#include <string.h>
+#endif
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/ip_icmp.h>
+#include <netinet/tcp.h>
+#include <netinet/udp.h>
+
+#ifdef _KERNEL
+#include <netinet/libalias/alias.h>
+#include <netinet/libalias/alias_local.h>
+#include <netinet/libalias/alias_mod.h>
+#else
+#include <err.h>
+#include "alias.h"
+#include "alias_local.h"
+#include "alias_mod.h"
+#endif
+#else /* VBOX */
+# include <slirp.h>
+# include "alias.h"
+# include "alias_local.h"
+# include "alias_mod.h"
+
+# if 0 /* Clang 11 does not approve of this */
+# define return(x) \
+do { \
+ Log2(("NAT:ALIAS: %s:%d return(%s:%d)\n", \
+ RT_GCC_EXTENSION __FUNCTION__, __LINE__, #x,(x))); \
+ return x; \
+} while(0)
+# endif
+#endif /* VBOX */
+static __inline int
+twowords(void *p)
+{
+ uint8_t *c = p;
+
+#ifdef RT_LITTLE_ENDIAN /*BYTE_ORDER == LITTLE_ENDIAN*/
+ uint16_t s1 = ((uint16_t)c[1] << 8) + (uint16_t)c[0];
+ uint16_t s2 = ((uint16_t)c[3] << 8) + (uint16_t)c[2];
+#else
+ uint16_t s1 = ((uint16_t)c[0] << 8) + (uint16_t)c[1];
+ uint16_t s2 = ((uint16_t)c[2] << 8) + (uint16_t)c[3];
+#endif
+ return (s1 + s2);
+}
+
+/* TCP Handling Routines
+
+ TcpMonitorIn() -- These routines monitor TCP connections, and
+ TcpMonitorOut() delete a link when a connection is closed.
+
+These routines look for SYN, FIN and RST flags to determine when TCP
+connections open and close. When a TCP connection closes, the data
+structure containing packet aliasing information is deleted after
+a timeout period.
+*/
+
+/* Local prototypes */
+static void TcpMonitorIn(struct ip *, struct alias_link *);
+
+static void TcpMonitorOut(struct ip *, struct alias_link *);
+
+
+static void
+TcpMonitorIn(struct ip *pip, struct alias_link *lnk)
+{
+ struct tcphdr *tc;
+
+ tc = (struct tcphdr *)ip_next(pip);
+
+ switch (GetStateIn(lnk)) {
+ case ALIAS_TCP_STATE_NOT_CONNECTED:
+ if (tc->th_flags & TH_RST)
+ SetStateIn(lnk, ALIAS_TCP_STATE_DISCONNECTED);
+ else if (tc->th_flags & TH_SYN)
+ SetStateIn(lnk, ALIAS_TCP_STATE_CONNECTED);
+ break;
+ case ALIAS_TCP_STATE_CONNECTED:
+ if (tc->th_flags & (TH_FIN | TH_RST))
+ SetStateIn(lnk, ALIAS_TCP_STATE_DISCONNECTED);
+ break;
+ }
+}
+
+static void
+TcpMonitorOut(struct ip *pip, struct alias_link *lnk)
+{
+ struct tcphdr *tc;
+
+ tc = (struct tcphdr *)ip_next(pip);
+
+ switch (GetStateOut(lnk)) {
+ case ALIAS_TCP_STATE_NOT_CONNECTED:
+ if (tc->th_flags & TH_RST)
+ SetStateOut(lnk, ALIAS_TCP_STATE_DISCONNECTED);
+ else if (tc->th_flags & TH_SYN)
+ SetStateOut(lnk, ALIAS_TCP_STATE_CONNECTED);
+ break;
+ case ALIAS_TCP_STATE_CONNECTED:
+ if (tc->th_flags & (TH_FIN | TH_RST))
+ SetStateOut(lnk, ALIAS_TCP_STATE_DISCONNECTED);
+ break;
+ }
+}
+
+
+
+
+
+/* Protocol Specific Packet Aliasing Routines
+
+ IcmpAliasIn(), IcmpAliasIn1(), IcmpAliasIn2()
+ IcmpAliasOut(), IcmpAliasOut1(), IcmpAliasOut2()
+ ProtoAliasIn(), ProtoAliasOut()
+ UdpAliasIn(), UdpAliasOut()
+ TcpAliasIn(), TcpAliasOut()
+
+These routines handle protocol specific details of packet aliasing.
+One may observe a certain amount of repetitive arithmetic in these
+functions, the purpose of which is to compute a revised checksum
+without actually summing over the entire data packet, which could be
+unnecessarily time consuming.
+
+The purpose of the packet aliasing routines is to replace the source
+address of the outgoing packet and then correctly put it back for
+any incoming packets. For TCP and UDP, ports are also re-mapped.
+
+For ICMP echo/timestamp requests and replies, the following scheme
+is used: the ID number is replaced by an alias for the outgoing
+packet.
+
+ICMP error messages are handled by looking at the IP fragment
+in the data section of the message.
+
+For TCP and UDP protocols, a port number is chosen for an outgoing
+packet, and then incoming packets are identified by IP address and
+port numbers. For TCP packets, there is additional logic in the event
+that sequence and ACK numbers have been altered (as in the case for
+FTP data port commands).
+
+The port numbers used by the packet aliasing module are not true
+ports in the Unix sense. No sockets are actually bound to ports.
+They are more correctly thought of as placeholders.
+
+All packets go through the aliasing mechanism, whether they come from
+the gateway machine or other machines on a local area network.
+*/
+
+
+/* Local prototypes */
+static int IcmpAliasIn1(struct libalias *, struct ip *);
+static int IcmpAliasIn2(struct libalias *, struct ip *);
+static int IcmpAliasIn(struct libalias *, struct ip *);
+
+static int IcmpAliasOut1(struct libalias *, struct ip *, int create);
+static int IcmpAliasOut2(struct libalias *, struct ip *);
+static int IcmpAliasOut(struct libalias *, struct ip *, int create);
+
+static int ProtoAliasIn(struct libalias *, struct ip *);
+static int ProtoAliasOut(struct libalias *, struct ip *, int create);
+
+static int UdpAliasIn(struct libalias *, struct ip *);
+static int UdpAliasOut(struct libalias *, struct ip *, int create);
+
+static int TcpAliasIn(struct libalias *, struct ip *);
+static int TcpAliasOut(struct libalias *, struct ip *, int, int create);
+
+
+static int
+IcmpAliasIn1(struct libalias *la, struct ip *pip)
+{
+
+/*
+ De-alias incoming echo and timestamp replies.
+ Alias incoming echo and timestamp requests.
+*/
+ struct alias_link *lnk;
+ struct icmp *ic;
+
+ LIBALIAS_LOCK_ASSERT(la);
+
+ ic = (struct icmp *)ip_next(pip);
+
+/* Get source address from ICMP data field and restore original data */
+ lnk = FindIcmpIn(la, pip->ip_src, pip->ip_dst, ic->icmp_id, 1);
+ if (lnk != NULL) {
+ u_short original_id;
+ int accumulate;
+
+ original_id = GetOriginalPort(lnk);
+
+/* Adjust ICMP checksum */
+ accumulate = ic->icmp_id;
+ accumulate -= original_id;
+ ADJUST_CHECKSUM(accumulate, ic->icmp_cksum);
+
+/* Put original sequence number back in */
+ ic->icmp_id = original_id;
+
+/* Put original address back into IP header */
+ {
+ struct in_addr original_address;
+
+ original_address = GetOriginalAddress(lnk);
+ DifferentialChecksum(&pip->ip_sum,
+ &original_address, &pip->ip_dst, 2);
+ pip->ip_dst = original_address;
+ }
+
+ return (PKT_ALIAS_OK);
+ }
+ return (PKT_ALIAS_IGNORED);
+}
+
+static int
+IcmpAliasIn2(struct libalias *la, struct ip *pip)
+{
+
+/*
+ Alias incoming ICMP error messages containing
+ IP header and first 64 bits of datagram.
+*/
+ struct ip *ip;
+ struct icmp *ic, *ic2;
+ struct udphdr *ud;
+ struct tcphdr *tc;
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+
+ ic = (struct icmp *)ip_next(pip);
+ ip = &ic->icmp_ip;
+
+ ud = (struct udphdr *)ip_next(ip);
+ tc = (struct tcphdr *)ip_next(ip);
+ ic2 = (struct icmp *)ip_next(ip);
+
+ if (ip->ip_p == IPPROTO_UDP)
+ lnk = FindUdpTcpIn(la, ip->ip_dst, ip->ip_src,
+ ud->uh_dport, ud->uh_sport,
+ IPPROTO_UDP, 0);
+ else if (ip->ip_p == IPPROTO_TCP)
+ lnk = FindUdpTcpIn(la, ip->ip_dst, ip->ip_src,
+ tc->th_dport, tc->th_sport,
+ IPPROTO_TCP, 0);
+ else if (ip->ip_p == IPPROTO_ICMP) {
+ if (ic2->icmp_type == ICMP_ECHO || ic2->icmp_type == ICMP_TSTAMP)
+ lnk = FindIcmpIn(la, ip->ip_dst, ip->ip_src, ic2->icmp_id, 0);
+ else
+ lnk = NULL;
+ } else
+ lnk = NULL;
+
+ if (lnk != NULL) {
+ if (ip->ip_p == IPPROTO_UDP || ip->ip_p == IPPROTO_TCP) {
+ int accumulate, accumulate2;
+ struct in_addr original_address;
+ u_short original_port;
+
+ original_address = GetOriginalAddress(lnk);
+ original_port = GetOriginalPort(lnk);
+
+/* Adjust ICMP checksum */
+ accumulate = twowords(&ip->ip_src);
+ accumulate -= twowords(&original_address);
+ accumulate += ud->uh_sport;
+ accumulate -= original_port;
+ accumulate2 = accumulate;
+ accumulate2 += ip->ip_sum;
+ ADJUST_CHECKSUM(accumulate, ip->ip_sum);
+ accumulate2 -= ip->ip_sum;
+ ADJUST_CHECKSUM(accumulate2, ic->icmp_cksum);
+
+/* Un-alias address in IP header */
+ DifferentialChecksum(&pip->ip_sum,
+ &original_address, &pip->ip_dst, 2);
+ pip->ip_dst = original_address;
+
+/* Un-alias address and port number of original IP packet
+fragment contained in ICMP data section */
+ ip->ip_src = original_address;
+ ud->uh_sport = original_port;
+ } else if (ip->ip_p == IPPROTO_ICMP) {
+ int accumulate, accumulate2;
+ struct in_addr original_address;
+ u_short original_id;
+
+ original_address = GetOriginalAddress(lnk);
+ original_id = GetOriginalPort(lnk);
+
+/* Adjust ICMP checksum */
+ accumulate = twowords(&ip->ip_src);
+ accumulate -= twowords(&original_address);
+ accumulate += ic2->icmp_id;
+ accumulate -= original_id;
+ accumulate2 = accumulate;
+ accumulate2 += ip->ip_sum;
+ ADJUST_CHECKSUM(accumulate, ip->ip_sum);
+ accumulate2 -= ip->ip_sum;
+ ADJUST_CHECKSUM(accumulate2, ic->icmp_cksum);
+
+/* Un-alias address in IP header */
+ DifferentialChecksum(&pip->ip_sum,
+ &original_address, &pip->ip_dst, 2);
+ pip->ip_dst = original_address;
+
+/* Un-alias address of original IP packet and sequence number of
+ embedded ICMP datagram */
+ ip->ip_src = original_address;
+ ic2->icmp_id = original_id;
+ }
+ return (PKT_ALIAS_OK);
+ }
+ return (PKT_ALIAS_IGNORED);
+}
+
+
+static int
+IcmpAliasIn(struct libalias *la, struct ip *pip)
+{
+ int iresult;
+ struct icmp *ic;
+
+ LIBALIAS_LOCK_ASSERT(la);
+/* Return if proxy-only mode is enabled */
+ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)
+ return (PKT_ALIAS_OK);
+
+ ic = (struct icmp *)ip_next(pip);
+
+ iresult = PKT_ALIAS_IGNORED;
+ switch (ic->icmp_type) {
+ case ICMP_ECHOREPLY:
+ case ICMP_TSTAMPREPLY:
+ if (ic->icmp_code == 0) {
+ iresult = IcmpAliasIn1(la, pip);
+ }
+ break;
+ case ICMP_UNREACH:
+ case ICMP_SOURCEQUENCH:
+ case ICMP_TIMXCEED:
+ case ICMP_PARAMPROB:
+ iresult = IcmpAliasIn2(la, pip);
+ break;
+ case ICMP_ECHO:
+ case ICMP_TSTAMP:
+ iresult = IcmpAliasIn1(la, pip);
+ break;
+ }
+ return (iresult);
+}
+
+
+static int
+IcmpAliasOut1(struct libalias *la, struct ip *pip, int create)
+{
+/*
+ Alias outgoing echo and timestamp requests.
+ De-alias outgoing echo and timestamp replies.
+*/
+ struct alias_link *lnk;
+ struct icmp *ic;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ ic = (struct icmp *)ip_next(pip);
+
+/* Save overwritten data for when echo packet returns */
+ lnk = FindIcmpOut(la, pip->ip_src, pip->ip_dst, ic->icmp_id, create);
+ if (lnk != NULL) {
+ u_short alias_id;
+ int accumulate;
+
+ alias_id = GetAliasPort(lnk);
+
+/* Since data field is being modified, adjust ICMP checksum */
+ accumulate = ic->icmp_id;
+ accumulate -= alias_id;
+ ADJUST_CHECKSUM(accumulate, ic->icmp_cksum);
+
+/* Alias sequence number */
+ ic->icmp_id = alias_id;
+
+/* Change source address */
+ {
+ struct in_addr alias_address;
+
+ alias_address = GetAliasAddress(lnk);
+ DifferentialChecksum(&pip->ip_sum,
+ &alias_address, &pip->ip_src, 2);
+ pip->ip_src = alias_address;
+ }
+
+ return (PKT_ALIAS_OK);
+ }
+ return (PKT_ALIAS_IGNORED);
+}
+
+
+static int
+IcmpAliasOut2(struct libalias *la, struct ip *pip)
+{
+/*
+ Alias outgoing ICMP error messages containing
+ IP header and first 64 bits of datagram.
+*/
+ struct ip *ip;
+ struct icmp *ic, *ic2;
+ struct udphdr *ud;
+ struct tcphdr *tc;
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ ic = (struct icmp *)ip_next(pip);
+ ip = &ic->icmp_ip;
+
+ ud = (struct udphdr *)ip_next(ip);
+ tc = (struct tcphdr *)ip_next(ip);
+ ic2 = (struct icmp *)ip_next(ip);
+
+ if (ip->ip_p == IPPROTO_UDP)
+ lnk = FindUdpTcpOut(la, ip->ip_dst, ip->ip_src,
+ ud->uh_dport, ud->uh_sport,
+ IPPROTO_UDP, 0);
+ else if (ip->ip_p == IPPROTO_TCP)
+ lnk = FindUdpTcpOut(la, ip->ip_dst, ip->ip_src,
+ tc->th_dport, tc->th_sport,
+ IPPROTO_TCP, 0);
+ else if (ip->ip_p == IPPROTO_ICMP) {
+ if (ic2->icmp_type == ICMP_ECHO || ic2->icmp_type == ICMP_TSTAMP)
+ lnk = FindIcmpOut(la, ip->ip_dst, ip->ip_src, ic2->icmp_id, 0);
+ else
+ lnk = NULL;
+ } else
+ lnk = NULL;
+
+ if (lnk != NULL) {
+ if (ip->ip_p == IPPROTO_UDP || ip->ip_p == IPPROTO_TCP) {
+ int accumulate;
+ struct in_addr alias_address;
+ u_short alias_port;
+
+ alias_address = GetAliasAddress(lnk);
+ alias_port = GetAliasPort(lnk);
+
+/* Adjust ICMP checksum */
+ accumulate = twowords(&ip->ip_dst);
+ accumulate -= twowords(&alias_address);
+ accumulate += ud->uh_dport;
+ accumulate -= alias_port;
+ ADJUST_CHECKSUM(accumulate, ic->icmp_cksum);
+
+/*
+ * Alias address in IP header if it comes from the host
+ * the original TCP/UDP packet was destined for.
+ */
+ if (pip->ip_src.s_addr == ip->ip_dst.s_addr) {
+ DifferentialChecksum(&pip->ip_sum,
+ &alias_address, &pip->ip_src, 2);
+ pip->ip_src = alias_address;
+ }
+/* Alias address and port number of original IP packet
+fragment contained in ICMP data section */
+ ip->ip_dst = alias_address;
+ ud->uh_dport = alias_port;
+ } else if (ip->ip_p == IPPROTO_ICMP) {
+ int accumulate;
+ struct in_addr alias_address;
+ u_short alias_id;
+
+ alias_address = GetAliasAddress(lnk);
+ alias_id = GetAliasPort(lnk);
+
+/* Adjust ICMP checksum */
+ accumulate = twowords(&ip->ip_dst);
+ accumulate -= twowords(&alias_address);
+ accumulate += ic2->icmp_id;
+ accumulate -= alias_id;
+ ADJUST_CHECKSUM(accumulate, ic->icmp_cksum);
+
+/*
+ * Alias address in IP header if it comes from the host
+ * the original ICMP message was destined for.
+ */
+ if (pip->ip_src.s_addr == ip->ip_dst.s_addr) {
+ DifferentialChecksum(&pip->ip_sum,
+ &alias_address, &pip->ip_src, 2);
+ pip->ip_src = alias_address;
+ }
+/* Alias address of original IP packet and sequence number of
+ embedded ICMP datagram */
+ ip->ip_dst = alias_address;
+ ic2->icmp_id = alias_id;
+ }
+ return (PKT_ALIAS_OK);
+ }
+ return (PKT_ALIAS_IGNORED);
+}
+
+
+static int
+IcmpAliasOut(struct libalias *la, struct ip *pip, int create)
+{
+ int iresult;
+ struct icmp *ic;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ (void)create;
+
+/* Return if proxy-only mode is enabled */
+ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)
+ return (PKT_ALIAS_OK);
+
+ ic = (struct icmp *)ip_next(pip);
+
+ iresult = PKT_ALIAS_IGNORED;
+ switch (ic->icmp_type) {
+ case ICMP_ECHO:
+ case ICMP_TSTAMP:
+ if (ic->icmp_code == 0) {
+ iresult = IcmpAliasOut1(la, pip, create);
+ }
+ break;
+ case ICMP_UNREACH:
+ case ICMP_SOURCEQUENCH:
+ case ICMP_TIMXCEED:
+ case ICMP_PARAMPROB:
+ iresult = IcmpAliasOut2(la, pip);
+ break;
+ case ICMP_ECHOREPLY:
+ case ICMP_TSTAMPREPLY:
+ iresult = IcmpAliasOut1(la, pip, create);
+ }
+ return (iresult);
+}
+
+
+
+static int
+ProtoAliasIn(struct libalias *la, struct ip *pip)
+{
+/*
+ Handle incoming IP packets. The
+ only thing which is done in this case is to alias
+ the dest IP address of the packet to our inside
+ machine.
+*/
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+/* Return if proxy-only mode is enabled */
+ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)
+ return (PKT_ALIAS_OK);
+
+ lnk = FindProtoIn(la, pip->ip_src, pip->ip_dst, pip->ip_p);
+ if (lnk != NULL) {
+ struct in_addr original_address;
+
+ original_address = GetOriginalAddress(lnk);
+
+/* Restore original IP address */
+ DifferentialChecksum(&pip->ip_sum,
+ &original_address, &pip->ip_dst, 2);
+ pip->ip_dst = original_address;
+
+ return (PKT_ALIAS_OK);
+ }
+ return (PKT_ALIAS_IGNORED);
+}
+
+
+static int
+ProtoAliasOut(struct libalias *la, struct ip *pip, int create)
+{
+/*
+ Handle outgoing IP packets. The
+ only thing which is done in this case is to alias
+ the source IP address of the packet.
+*/
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ (void)create;
+
+/* Return if proxy-only mode is enabled */
+ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)
+ return (PKT_ALIAS_OK);
+
+ lnk = FindProtoOut(la, pip->ip_src, pip->ip_dst, pip->ip_p);
+ if (lnk != NULL) {
+ struct in_addr alias_address;
+
+ alias_address = GetAliasAddress(lnk);
+
+/* Change source address */
+ DifferentialChecksum(&pip->ip_sum,
+ &alias_address, &pip->ip_src, 2);
+ pip->ip_src = alias_address;
+
+ return (PKT_ALIAS_OK);
+ }
+ return (PKT_ALIAS_IGNORED);
+}
+
+
+static int
+UdpAliasIn(struct libalias *la, struct ip *pip)
+{
+ struct udphdr *ud;
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+/* Return if proxy-only mode is enabled */
+ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)
+ return (PKT_ALIAS_OK);
+
+ ud = (struct udphdr *)ip_next(pip);
+
+ lnk = FindUdpTcpIn(la, pip->ip_src, pip->ip_dst,
+ ud->uh_sport, ud->uh_dport,
+ IPPROTO_UDP, 1);
+ if (lnk != NULL) {
+ struct in_addr alias_address;
+ struct in_addr original_address;
+ u_short alias_port;
+ int accumulate;
+ int error;
+ struct alias_data ad;
+ ad.lnk = lnk;
+ ad.oaddr = &original_address;
+ ad.aaddr = &alias_address;
+ ad.aport = &alias_port;
+ ad.sport = &ud->uh_sport;
+ ad.dport = &ud->uh_dport;
+ ad.maxpktsize = 0;
+
+
+ alias_address = GetAliasAddress(lnk);
+ original_address = GetOriginalAddress(lnk);
+ alias_port = ud->uh_dport;
+ ud->uh_dport = GetOriginalPort(lnk);
+
+ /* Walk out chain. */
+ error = find_handler(IN, UDP, la, pip, &ad);
+ /* If we cannot figure out the packet, ignore it. */
+ if (error < 0)
+ return (PKT_ALIAS_IGNORED);
+
+/* If UDP checksum is not zero, then adjust since destination port */
+/* is being unaliased and destination address is being altered. */
+ if (ud->uh_sum != 0) {
+ accumulate = alias_port;
+ accumulate -= ud->uh_dport;
+ accumulate += twowords(&alias_address);
+ accumulate -= twowords(&original_address);
+ ADJUST_CHECKSUM(accumulate, ud->uh_sum);
+ }
+/* Restore original IP address */
+ DifferentialChecksum(&pip->ip_sum,
+ &original_address, &pip->ip_dst, 2);
+ pip->ip_dst = original_address;
+
+ return (PKT_ALIAS_OK);
+ }
+ return (PKT_ALIAS_IGNORED);
+}
+
+static int
+UdpAliasOut(struct libalias *la, struct ip *pip, int create)
+{
+ struct udphdr *ud;
+ struct alias_link *lnk;
+ int error;
+
+ LIBALIAS_LOCK_ASSERT(la);
+/* Return if proxy-only mode is enabled */
+ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY)
+ return (PKT_ALIAS_OK);
+
+ ud = (struct udphdr *)ip_next(pip);
+
+ lnk = FindUdpTcpOut(la, pip->ip_src, pip->ip_dst,
+ ud->uh_sport, ud->uh_dport,
+ IPPROTO_UDP, create);
+ if (lnk != NULL) {
+ u_short alias_port;
+ struct in_addr alias_address;
+ struct alias_data ad;
+ ad.lnk = lnk;
+ ad.oaddr = NULL;
+ ad.aaddr = &alias_address;
+ ad.aport = &alias_port;
+ ad.sport = &ud->uh_sport;
+ ad.dport = &ud->uh_dport;
+ ad.maxpktsize = 0;
+
+ alias_address = GetAliasAddress(lnk);
+ alias_port = GetAliasPort(lnk);
+
+ /* Walk out chain. */
+ error = find_handler(OUT, UDP, la, pip, &ad);
+
+/* If UDP checksum is not zero, adjust since source port is */
+/* being aliased and source address is being altered */
+ if (ud->uh_sum != 0) {
+ int accumulate;
+
+ accumulate = ud->uh_sport;
+ accumulate -= alias_port;
+ accumulate += twowords(&pip->ip_src);
+ accumulate -= twowords(&alias_address);
+ ADJUST_CHECKSUM(accumulate, ud->uh_sum);
+ }
+/* Put alias port in UDP header */
+ ud->uh_sport = alias_port;
+
+/* Change source address */
+ DifferentialChecksum(&pip->ip_sum,
+ &alias_address, &pip->ip_src, 2);
+ pip->ip_src = alias_address;
+
+ return (PKT_ALIAS_OK);
+ }
+ return (PKT_ALIAS_IGNORED);
+}
+
+
+
+static int
+TcpAliasIn(struct libalias *la, struct ip *pip)
+{
+ struct tcphdr *tc;
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ tc = (struct tcphdr *)ip_next(pip);
+
+ lnk = FindUdpTcpIn(la, pip->ip_src, pip->ip_dst,
+ tc->th_sport, tc->th_dport,
+ IPPROTO_TCP,
+ !(la->packetAliasMode & PKT_ALIAS_PROXY_ONLY));
+ if (lnk != NULL) {
+ struct in_addr alias_address;
+ struct in_addr original_address;
+ struct in_addr proxy_address;
+ u_short alias_port;
+ u_short proxy_port;
+ int accumulate, error;
+
+ /*
+ * The init of MANY vars is a bit below, but aliashandlepptpin
+ * seems to need the destination port that came within the
+ * packet and not the original one looks below [*].
+ */
+
+ struct alias_data ad;
+ ad.lnk = lnk;
+ ad.oaddr = NULL;
+ ad.aaddr = NULL;
+ ad.aport = NULL;
+ ad.sport = &tc->th_sport;
+ ad.dport = &tc->th_dport;
+ ad.maxpktsize = 0;
+
+ /* Walk out chain. */
+ error = find_handler(IN, TCP, la, pip, &ad);
+
+ alias_address = GetAliasAddress(lnk);
+ original_address = GetOriginalAddress(lnk);
+ proxy_address = GetProxyAddress(lnk);
+ alias_port = tc->th_dport;
+ tc->th_dport = GetOriginalPort(lnk);
+ proxy_port = GetProxyPort(lnk);
+
+ /*
+ * Look above, if anyone is going to add find_handler AFTER
+ * this aliashandlepptpin/point, please redo alias_data too.
+ * Uncommenting the piece here below should be enough.
+ */
+#if 0
+ struct alias_data ad = {
+ .lnk = lnk,
+ .oaddr = &original_address,
+ .aaddr = &alias_address,
+ .aport = &alias_port,
+ .sport = &ud->uh_sport,
+ .dport = &ud->uh_dport,
+ .maxpktsize = 0
+ };
+
+ /* Walk out chain. */
+ error = find_handler(la, pip, &ad);
+ if (error == EHDNOF)
+ printf("Protocol handler not found\n");
+#endif
+
+/* Adjust TCP checksum since destination port is being unaliased */
+/* and destination port is being altered. */
+ accumulate = alias_port;
+ accumulate -= tc->th_dport;
+ accumulate += twowords(&alias_address);
+ accumulate -= twowords(&original_address);
+
+/* If this is a proxy, then modify the TCP source port and
+ checksum accumulation */
+ if (proxy_port != 0) {
+ accumulate += tc->th_sport;
+ tc->th_sport = proxy_port;
+ accumulate -= tc->th_sport;
+ accumulate += twowords(&pip->ip_src);
+ accumulate -= twowords(&proxy_address);
+ }
+/* See if ACK number needs to be modified */
+ if (GetAckModified(lnk) == 1) {
+ int delta;
+
+ delta = GetDeltaAckIn(pip, lnk);
+ if (delta != 0) {
+ accumulate += twowords(&tc->th_ack);
+ tc->th_ack = htonl(ntohl(tc->th_ack) - delta);
+ accumulate -= twowords(&tc->th_ack);
+ }
+ }
+ ADJUST_CHECKSUM(accumulate, tc->th_sum);
+
+/* Restore original IP address */
+ accumulate = twowords(&pip->ip_dst);
+ pip->ip_dst = original_address;
+ accumulate -= twowords(&pip->ip_dst);
+
+/* If this is a transparent proxy packet, then modify the source
+ address */
+ if (proxy_address.s_addr != 0) {
+ accumulate += twowords(&pip->ip_src);
+ pip->ip_src = proxy_address;
+ accumulate -= twowords(&pip->ip_src);
+ }
+ ADJUST_CHECKSUM(accumulate, pip->ip_sum);
+
+/* Monitor TCP connection state */
+ TcpMonitorIn(pip, lnk);
+
+ return (PKT_ALIAS_OK);
+ }
+ return (PKT_ALIAS_IGNORED);
+}
+
+static int
+TcpAliasOut(struct libalias *la, struct ip *pip, int maxpacketsize, int create)
+{
+ int proxy_type, error;
+ u_short dest_port;
+ u_short proxy_server_port = 0; /* Shut up MSC. */
+ struct in_addr dest_address;
+ struct in_addr proxy_server_address;
+ struct tcphdr *tc;
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ tc = (struct tcphdr *)ip_next(pip);
+
+ if (create)
+ proxy_type =
+ ProxyCheck(la, pip, &proxy_server_address, &proxy_server_port);
+ else
+ proxy_type = 0;
+
+ if (proxy_type == 0 && (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY))
+ return (PKT_ALIAS_OK);
+
+/* If this is a transparent proxy, save original destination,
+ then alter the destination and adjust checksums */
+ dest_port = tc->th_dport;
+ dest_address = pip->ip_dst;
+ if (proxy_type != 0) {
+ int accumulate;
+
+ accumulate = tc->th_dport;
+ tc->th_dport = proxy_server_port;
+ accumulate -= tc->th_dport;
+ accumulate += twowords(&pip->ip_dst);
+ accumulate -= twowords(&proxy_server_address);
+ ADJUST_CHECKSUM(accumulate, tc->th_sum);
+
+ accumulate = twowords(&pip->ip_dst);
+ pip->ip_dst = proxy_server_address;
+ accumulate -= twowords(&pip->ip_dst);
+ ADJUST_CHECKSUM(accumulate, pip->ip_sum);
+ }
+ lnk = FindUdpTcpOut(la, pip->ip_src, pip->ip_dst,
+ tc->th_sport, tc->th_dport,
+ IPPROTO_TCP, create);
+ if (lnk == NULL)
+ return (PKT_ALIAS_IGNORED);
+ if (lnk != NULL) {
+ u_short alias_port;
+ struct in_addr alias_address;
+ int accumulate;
+ struct alias_data ad;
+ ad.lnk = lnk;
+ ad.oaddr = NULL;
+ ad.aaddr = &alias_address;
+ ad.aport = &alias_port;
+ ad.sport = &tc->th_sport;
+ ad.dport = &tc->th_dport;
+ ad.maxpktsize = maxpacketsize;
+
+/* Save original destination address, if this is a proxy packet.
+ Also modify packet to include destination encoding. This may
+ change the size of IP header. */
+ if (proxy_type != 0) {
+ SetProxyPort(lnk, dest_port);
+ SetProxyAddress(lnk, dest_address);
+ ProxyModify(la, lnk, pip, maxpacketsize, proxy_type);
+ tc = (struct tcphdr *)ip_next(pip);
+ }
+/* Get alias address and port */
+ alias_port = GetAliasPort(lnk);
+ alias_address = GetAliasAddress(lnk);
+
+/* Monitor TCP connection state */
+ TcpMonitorOut(pip, lnk);
+
+ /* Walk out chain. */
+ error = find_handler(OUT, TCP, la, pip, &ad);
+
+/* Adjust TCP checksum since source port is being aliased */
+/* and source address is being altered */
+ accumulate = tc->th_sport;
+ tc->th_sport = alias_port;
+ accumulate -= tc->th_sport;
+ accumulate += twowords(&pip->ip_src);
+ accumulate -= twowords(&alias_address);
+
+/* Modify sequence number if necessary */
+ if (GetAckModified(lnk) == 1) {
+ int delta;
+
+ delta = GetDeltaSeqOut(pip, lnk);
+ if (delta != 0) {
+ accumulate += twowords(&tc->th_seq);
+ tc->th_seq = htonl(ntohl(tc->th_seq) + delta);
+ accumulate -= twowords(&tc->th_seq);
+ }
+ }
+ ADJUST_CHECKSUM(accumulate, tc->th_sum);
+
+/* Change source address */
+ accumulate = twowords(&pip->ip_src);
+ pip->ip_src = alias_address;
+ accumulate -= twowords(&pip->ip_src);
+ ADJUST_CHECKSUM(accumulate, pip->ip_sum);
+
+ return (PKT_ALIAS_OK);
+ }
+ return (PKT_ALIAS_IGNORED);
+}
+
+
+
+
+/* Fragment Handling
+
+ FragmentIn()
+ FragmentOut()
+
+The packet aliasing module has a limited ability for handling IP
+fragments. If the ICMP, TCP or UDP header is in the first fragment
+received, then the ID number of the IP packet is saved, and other
+fragments are identified according to their ID number and IP address
+they were sent from. Pointers to unresolved fragments can also be
+saved and recalled when a header fragment is seen.
+*/
+
+/* Local prototypes */
+static int FragmentIn(struct libalias *, struct ip *);
+static int FragmentOut(struct libalias *, struct ip *);
+
+
+static int
+FragmentIn(struct libalias *la, struct ip *pip)
+{
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ lnk = FindFragmentIn2(la, pip->ip_src, pip->ip_dst, pip->ip_id);
+ if (lnk != NULL) {
+ struct in_addr original_address;
+
+ GetFragmentAddr(lnk, &original_address);
+ DifferentialChecksum(&pip->ip_sum,
+ &original_address, &pip->ip_dst, 2);
+ pip->ip_dst = original_address;
+
+ return (PKT_ALIAS_OK);
+ }
+ return (PKT_ALIAS_UNRESOLVED_FRAGMENT);
+}
+
+
+static int
+FragmentOut(struct libalias *la, struct ip *pip)
+{
+ struct in_addr alias_address;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ alias_address = FindAliasAddress(la, pip->ip_src);
+ DifferentialChecksum(&pip->ip_sum,
+ &alias_address, &pip->ip_src, 2);
+ pip->ip_src = alias_address;
+
+ return (PKT_ALIAS_OK);
+}
+
+
+
+
+
+
+/* Outside World Access
+
+ PacketAliasSaveFragment()
+ PacketAliasGetFragment()
+ PacketAliasFragmentIn()
+ PacketAliasIn()
+ PacketAliasOut()
+ PacketUnaliasOut()
+
+(prototypes in alias.h)
+*/
+
+
+int
+LibAliasSaveFragment(struct libalias *la, char *ptr)
+{
+ int iresult;
+ struct alias_link *lnk;
+ struct ip *pip;
+
+ LIBALIAS_LOCK(la);
+ pip = (struct ip *)ptr;
+ lnk = AddFragmentPtrLink(la, pip->ip_src, pip->ip_id);
+ iresult = PKT_ALIAS_ERROR;
+ if (lnk != NULL) {
+ SetFragmentPtr(lnk, ptr);
+ iresult = PKT_ALIAS_OK;
+ }
+ LIBALIAS_UNLOCK(la);
+ return (iresult);
+}
+
+
+char *
+LibAliasGetFragment(struct libalias *la, char *ptr)
+{
+ struct alias_link *lnk;
+ char *fptr;
+ struct ip *pip;
+
+ LIBALIAS_LOCK(la);
+ pip = (struct ip *)ptr;
+ lnk = FindFragmentPtr(la, pip->ip_src, pip->ip_id);
+ if (lnk != NULL) {
+ GetFragmentPtr(lnk, &fptr);
+ SetFragmentPtr(lnk, NULL);
+ SetExpire(lnk, 0); /* Deletes link */
+ } else
+ fptr = NULL;
+
+ LIBALIAS_UNLOCK(la);
+ return (fptr);
+}
+
+
+void
+LibAliasFragmentIn(struct libalias *la, char *ptr, /* Points to correctly
+ * de-aliased header
+ * fragment */
+ char *ptr_fragment /* Points to fragment which must be
+ * de-aliased */
+)
+{
+ struct ip *pip;
+ struct ip *fpip;
+
+ LIBALIAS_LOCK(la);
+ (void)la;
+ pip = (struct ip *)ptr;
+ fpip = (struct ip *)ptr_fragment;
+
+ DifferentialChecksum(&fpip->ip_sum,
+ &pip->ip_dst, &fpip->ip_dst, 2);
+ fpip->ip_dst = pip->ip_dst;
+ LIBALIAS_UNLOCK(la);
+}
+
+/* Local prototypes */
+static int
+LibAliasOutLocked(struct libalias *la, char *ptr,
+ int maxpacketsize, int create);
+static int
+LibAliasInLocked(struct libalias *la, char *ptr,
+ int maxpacketsize);
+
+int
+LibAliasIn(struct libalias *la, char *ptr, int maxpacketsize)
+{
+ int res;
+
+ LIBALIAS_LOCK(la);
+ res = LibAliasInLocked(la, ptr, maxpacketsize);
+ LIBALIAS_UNLOCK(la);
+ return (res);
+}
+
+static int
+LibAliasInLocked(struct libalias *la, char *ptr, int maxpacketsize)
+{
+ struct in_addr alias_addr;
+ struct ip *pip;
+#ifndef VBOX
+ int iresult;
+#else
+ int iresult = PKT_ALIAS_IGNORED;
+#endif
+
+ if (la->packetAliasMode & PKT_ALIAS_REVERSE) {
+ la->packetAliasMode &= ~PKT_ALIAS_REVERSE;
+ iresult = LibAliasOutLocked(la, ptr, maxpacketsize, 1);
+ la->packetAliasMode |= PKT_ALIAS_REVERSE;
+ goto getout;
+ }
+ HouseKeeping(la);
+ ClearCheckNewLink(la);
+ pip = (struct ip *)ptr;
+ alias_addr = pip->ip_dst;
+
+ /* Defense against mangled packets */
+ if (ntohs(pip->ip_len) > maxpacketsize
+ || (pip->ip_hl << 2) > maxpacketsize) {
+ iresult = PKT_ALIAS_IGNORED;
+ goto getout;
+ }
+
+#ifndef VBOX
+ iresult = PKT_ALIAS_IGNORED;
+#endif
+ if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) {
+ switch (pip->ip_p) {
+ case IPPROTO_ICMP:
+ iresult = IcmpAliasIn(la, pip);
+ break;
+ case IPPROTO_UDP:
+ iresult = UdpAliasIn(la, pip);
+ break;
+ case IPPROTO_TCP:
+ iresult = TcpAliasIn(la, pip);
+ break;
+#ifndef VBOX
+ case IPPROTO_GRE: {
+ int error;
+ struct alias_data ad;
+ ad.lnk = NULL,
+ ad.oaddr = NULL,
+ ad.aaddr = NULL,
+ ad.aport = NULL,
+ ad.sport = NULL,
+ ad.dport = NULL,
+ ad.maxpktsize = 0
+
+ /* Walk out chain. */
+ error = find_handler(IN, IP, la, pip, &ad);
+ if (error == 0)
+ iresult = PKT_ALIAS_OK;
+ else
+ iresult = ProtoAliasIn(la, pip);
+ }
+ break;
+#endif
+ default:
+ iresult = ProtoAliasIn(la, pip);
+ break;
+ }
+
+ if (ntohs(pip->ip_off) & IP_MF) {
+ struct alias_link *lnk;
+
+ lnk = FindFragmentIn1(la, pip->ip_src, alias_addr, pip->ip_id);
+ if (lnk != NULL) {
+ iresult = PKT_ALIAS_FOUND_HEADER_FRAGMENT;
+ SetFragmentAddr(lnk, pip->ip_dst);
+ } else {
+ iresult = PKT_ALIAS_ERROR;
+ }
+ }
+ } else {
+ iresult = FragmentIn(la, pip);
+ }
+
+getout:
+ return (iresult);
+}
+
+
+
+/* Unregistered address ranges */
+
+/* 10.0.0.0 -> 10.255.255.255 */
+#define UNREG_ADDR_A_LOWER 0x0a000000
+#define UNREG_ADDR_A_UPPER 0x0affffff
+
+/* 172.16.0.0 -> 172.31.255.255 */
+#define UNREG_ADDR_B_LOWER 0xac100000
+#define UNREG_ADDR_B_UPPER 0xac1fffff
+
+/* 192.168.0.0 -> 192.168.255.255 */
+#define UNREG_ADDR_C_LOWER 0xc0a80000
+#define UNREG_ADDR_C_UPPER 0xc0a8ffff
+
+int
+LibAliasOut(struct libalias *la, char *ptr, int maxpacketsize)
+{
+ int res;
+
+ LIBALIAS_LOCK(la);
+ res = LibAliasOutLocked(la, ptr, maxpacketsize, 1);
+ LIBALIAS_UNLOCK(la);
+ return (res);
+}
+
+int
+LibAliasOutTry(struct libalias *la, char *ptr, int maxpacketsize, int create)
+{
+ int res;
+
+ LIBALIAS_LOCK(la);
+ res = LibAliasOutLocked(la, ptr, maxpacketsize, create);
+ LIBALIAS_UNLOCK(la);
+ return (res);
+}
+
+static int
+LibAliasOutLocked(struct libalias *la, char *ptr, /* valid IP packet */
+ int maxpacketsize, /* How much the packet data may grow (FTP
+ * and IRC inline changes) */
+ int create /* Create new entries ? */
+)
+{
+#ifndef VBOX
+ int iresult;
+#else
+ int iresult = PKT_ALIAS_IGNORED;
+#endif
+ struct in_addr addr_save;
+ struct ip *pip;
+
+ if (la->packetAliasMode & PKT_ALIAS_REVERSE) {
+ la->packetAliasMode &= ~PKT_ALIAS_REVERSE;
+ iresult = LibAliasInLocked(la, ptr, maxpacketsize);
+ la->packetAliasMode |= PKT_ALIAS_REVERSE;
+ goto getout;
+ }
+ HouseKeeping(la);
+ ClearCheckNewLink(la);
+ pip = (struct ip *)ptr;
+
+ /* Defense against mangled packets */
+ if (ntohs(pip->ip_len) > maxpacketsize
+ || (pip->ip_hl << 2) > maxpacketsize) {
+ iresult = PKT_ALIAS_IGNORED;
+ goto getout;
+ }
+
+ addr_save = GetDefaultAliasAddress(la);
+ if (la->packetAliasMode & PKT_ALIAS_UNREGISTERED_ONLY) {
+ u_long addr;
+ int iclass;
+
+ iclass = 0;
+ addr = ntohl(pip->ip_src.s_addr);
+ if (addr >= UNREG_ADDR_C_LOWER && addr <= UNREG_ADDR_C_UPPER)
+ iclass = 3;
+ else if (addr >= UNREG_ADDR_B_LOWER && addr <= UNREG_ADDR_B_UPPER)
+ iclass = 2;
+ else if (addr >= UNREG_ADDR_A_LOWER && addr <= UNREG_ADDR_A_UPPER)
+ iclass = 1;
+
+ if (iclass == 0) {
+ SetDefaultAliasAddress(la, pip->ip_src);
+ }
+ } else if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY) {
+ SetDefaultAliasAddress(la, pip->ip_src);
+ }
+#ifndef VBOX
+ iresult = PKT_ALIAS_IGNORED;
+#endif
+ if ((ntohs(pip->ip_off) & IP_OFFMASK) == 0) {
+ switch (pip->ip_p) {
+ case IPPROTO_ICMP:
+ iresult = IcmpAliasOut(la, pip, create);
+ break;
+ case IPPROTO_UDP:
+ iresult = UdpAliasOut(la, pip, create);
+ break;
+ case IPPROTO_TCP:
+ iresult = TcpAliasOut(la, pip, maxpacketsize, create);
+ break;
+#ifndef VBOX
+ case IPPROTO_GRE: {
+ int error;
+ struct alias_data ad = {
+ .lnk = NULL,
+ .oaddr = NULL,
+ .aaddr = NULL,
+ .aport = NULL,
+ .sport = NULL,
+ .dport = NULL,
+ .maxpktsize = 0
+ };
+ /* Walk out chain. */
+ error = find_handler(OUT, IP, la, pip, &ad);
+ if (error == 0)
+ iresult = PKT_ALIAS_OK;
+ else
+ iresult = ProtoAliasOut(la, pip, create);
+ }
+ break;
+#endif
+ default:
+ iresult = ProtoAliasOut(la, pip, create);
+ break;
+ }
+ } else {
+ iresult = FragmentOut(la, pip);
+ }
+
+ SetDefaultAliasAddress(la, addr_save);
+getout:
+ return (iresult);
+}
+
+int
+LibAliasUnaliasOut(struct libalias *la, char *ptr, /* valid IP packet */
+ int maxpacketsize /* for error checking */
+)
+{
+ struct ip *pip;
+ struct icmp *ic;
+ struct udphdr *ud;
+ struct tcphdr *tc;
+ struct alias_link *lnk;
+ int iresult = PKT_ALIAS_IGNORED;
+
+ LIBALIAS_LOCK(la);
+ pip = (struct ip *)ptr;
+
+ /* Defense against mangled packets */
+ if (ntohs(pip->ip_len) > maxpacketsize
+ || (pip->ip_hl << 2) > maxpacketsize)
+ goto getout;
+
+ ud = (struct udphdr *)ip_next(pip);
+ tc = (struct tcphdr *)ip_next(pip);
+ ic = (struct icmp *)ip_next(pip);
+
+ /* Find a link */
+ if (pip->ip_p == IPPROTO_UDP)
+ lnk = FindUdpTcpIn(la, pip->ip_dst, pip->ip_src,
+ ud->uh_dport, ud->uh_sport,
+ IPPROTO_UDP, 0);
+ else if (pip->ip_p == IPPROTO_TCP)
+ lnk = FindUdpTcpIn(la, pip->ip_dst, pip->ip_src,
+ tc->th_dport, tc->th_sport,
+ IPPROTO_TCP, 0);
+ else if (pip->ip_p == IPPROTO_ICMP)
+ lnk = FindIcmpIn(la, pip->ip_dst, pip->ip_src, ic->icmp_id, 0);
+ else
+ lnk = NULL;
+
+ /* Change it from an aliased packet to an unaliased packet */
+ if (lnk != NULL) {
+ if (pip->ip_p == IPPROTO_UDP || pip->ip_p == IPPROTO_TCP) {
+ int accumulate;
+ struct in_addr original_address;
+ u_short original_port;
+
+ original_address = GetOriginalAddress(lnk);
+ original_port = GetOriginalPort(lnk);
+
+ /* Adjust TCP/UDP checksum */
+ accumulate = twowords(&pip->ip_src);
+ accumulate -= twowords(&original_address);
+
+ if (pip->ip_p == IPPROTO_UDP) {
+ accumulate += ud->uh_sport;
+ accumulate -= original_port;
+ ADJUST_CHECKSUM(accumulate, ud->uh_sum);
+ } else {
+ accumulate += tc->th_sport;
+ accumulate -= original_port;
+ ADJUST_CHECKSUM(accumulate, tc->th_sum);
+ }
+
+ /* Adjust IP checksum */
+ DifferentialChecksum(&pip->ip_sum,
+ &original_address, &pip->ip_src, 2);
+
+ /* Un-alias source address and port number */
+ pip->ip_src = original_address;
+ if (pip->ip_p == IPPROTO_UDP)
+ ud->uh_sport = original_port;
+ else
+ tc->th_sport = original_port;
+
+ iresult = PKT_ALIAS_OK;
+
+ } else if (pip->ip_p == IPPROTO_ICMP) {
+
+ int accumulate;
+ struct in_addr original_address;
+ u_short original_id;
+
+ original_address = GetOriginalAddress(lnk);
+ original_id = GetOriginalPort(lnk);
+
+ /* Adjust ICMP checksum */
+ accumulate = twowords(&pip->ip_src);
+ accumulate -= twowords(&original_address);
+ accumulate += ic->icmp_id;
+ accumulate -= original_id;
+ ADJUST_CHECKSUM(accumulate, ic->icmp_cksum);
+
+ /* Adjust IP checksum */
+ DifferentialChecksum(&pip->ip_sum,
+ &original_address, &pip->ip_src, 2);
+
+ /* Un-alias source address and port number */
+ pip->ip_src = original_address;
+ ic->icmp_id = original_id;
+
+ iresult = PKT_ALIAS_OK;
+ }
+ }
+getout:
+ LIBALIAS_UNLOCK(la);
+ return (iresult);
+
+}
+
+#ifndef _KERNEL
+
+int
+LibAliasRefreshModules(void)
+{
+ /** @todo (r - vasily) here should be module loading */
+#ifndef VBOX
+ char buf[256], conf[] = "/etc/libalias.conf";
+ FILE *fd;
+ int i, len;
+
+ fd = fopen(conf, "r");
+ if (fd == NULL)
+ err(1, "fopen(%s)", conf);
+
+ LibAliasUnLoadAllModule();
+
+ for (;;) {
+ fgets(buf, 256, fd);
+ if feof(fd)
+ break;
+ len = strlen(buf);
+ if (len > 1) {
+ for (i = 0; i < len; i++)
+ if (!isspace(buf[i]))
+ break;
+ if (buf[i] == '#')
+ continue;
+ buf[len - 1] = '\0';
+ printf("Loading %s\n", buf);
+ LibAliasLoadModule(buf);
+ }
+ }
+#endif /* !VBOX */
+ return (0);
+}
+
+int
+LibAliasLoadModule(char *path)
+{
+#ifndef VBOX
+ struct dll *t;
+ void *handle;
+ struct proto_handler *m;
+ const char *error;
+ moduledata_t *p;
+
+ handle = dlopen (path, RTLD_LAZY);
+ if (!handle) {
+ fprintf(stderr, "%s\n", dlerror());
+ return (EINVAL);
+ }
+
+ p = dlsym(handle, "alias_mod");
+ if ((error = dlerror()) != NULL) {
+ fprintf(stderr, "%s\n", dlerror());
+ return (EINVAL);
+ }
+
+ t = malloc(sizeof(struct dll));
+ if (t == NULL)
+ return (ENOMEM);
+ strncpy(t->name, p->name, DLL_LEN);
+ t->handle = handle;
+ if (attach_dll(t) == EEXIST) {
+ free(t);
+ fprintf(stderr, "dll conflict\n");
+ return (EEXIST);
+ }
+
+ m = dlsym(t->handle, "handlers");
+ if ((error = dlerror()) != NULL) {
+ fprintf(stderr, "%s\n", error);
+ return (EINVAL);
+ }
+
+ LibAliasAttachHandlers(m);
+#else /* VBOX */
+ NOREF(path);
+#endif /* VBOX */
+ return (0);
+}
+
+int
+LibAliasUnLoadAllModule(void)
+{
+#ifndef VBOX
+ struct dll *t;
+ struct proto_handler *p;
+
+ /* Unload all modules then reload everything. */
+ while ((p = first_handler()) != NULL) {
+ detach_handler(p);
+ }
+ while ((t = walk_dll_chain()) != NULL) {
+ dlclose(t->handle);
+ free(t);
+ }
+#endif /* !VBOX */
+ return (1);
+}
+
+#endif
+
+#if defined(_KERNEL) || defined(VBOX)
+/*
+ * m_megapullup() - this function is a big hack.
+ * Thankfully, it's only used in ng_nat and ipfw+nat.
+ *
+ * It allocates an mbuf with cluster and copies the specified part of the chain
+ * into cluster, so that it is all contiguous and can be accessed via a plain
+ * (char *) pointer. This is required, because libalias doesn't know how to
+ * handle mbuf chains.
+ *
+ * On success, m_megapullup returns an mbuf (possibly with cluster) containing
+ * the input packet, on failure NULL. The input packet is always consumed.
+ */
+struct mbuf *
+#ifndef VBOX
+m_megapullup(struct mbuf *m, int len)
+#else
+m_megapullup(PNATState pData, struct mbuf *m, int len)
+#endif
+{
+ struct mbuf *mcl;
+
+ if (len > m->m_pkthdr.len)
+ goto bad;
+
+ /* Do not reallocate packet if it is sequentional,
+ * writable and has some extra space for expansion.
+ * XXX: Constant 100bytes is completely empirical. */
+#define RESERVE 100
+ if (m->m_next == NULL && M_WRITABLE(m) && M_TRAILINGSPACE(m) >= RESERVE)
+ return (m);
+
+ if (len <= MCLBYTES - RESERVE) {
+#ifndef VBOX
+ mcl = m_getcl(M_DONTWAIT, MT_DATA, M_PKTHDR);
+#else
+ mcl = m_getcl(pData, M_DONTWAIT, MT_DATA, M_PKTHDR);
+#endif
+ } else if (len < MJUM16BYTES) {
+ int size;
+ if (len <= MJUMPAGESIZE - RESERVE) {
+ size = MJUMPAGESIZE;
+ } else if (len <= MJUM9BYTES - RESERVE) {
+ size = MJUM9BYTES;
+ } else {
+ size = MJUM16BYTES;
+ };
+#ifndef VBOX
+ mcl = m_getjcl(M_DONTWAIT, MT_DATA, M_PKTHDR, size);
+#else
+ mcl = m_getjcl(pData, M_DONTWAIT, MT_DATA, M_PKTHDR, size);
+#endif
+ } else {
+ goto bad;
+ }
+ if (mcl == NULL)
+ goto bad;
+
+ m_move_pkthdr(mcl, m);
+ m_copydata(m, 0, len, mtod(mcl, caddr_t));
+ mcl->m_len = mcl->m_pkthdr.len = len;
+#ifndef VBOX
+ m_freem(m);
+#else
+ m_freem(pData, m);
+#endif
+
+ return (mcl);
+bad:
+#ifndef VBOX
+ m_freem(m);
+#else
+ m_freem(pData, m);
+#endif
+ return (NULL);
+}
+#endif
diff --git a/src/VBox/Devices/Network/slirp/libalias/alias.h b/src/VBox/Devices/Network/slirp/libalias/alias.h
new file mode 100644
index 00000000..6ecec9a2
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/alias.h
@@ -0,0 +1,304 @@
+/* lint -save -library Flexelint comment for external headers */
+
+/*-
+ * Copyright (c) 2001 Charles Mott <cm@linktel.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHOR 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 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.
+ *
+ * $FreeBSD: src/sys/netinet/libalias/alias.h,v 1.34.8.1 2009/04/15 03:14:26 kensmith Exp $
+ */
+
+/*
+ * Alias.h defines the outside world interfaces for the packet aliasing
+ * software.
+ *
+ * This software is placed into the public domain with no restrictions on its
+ * distribution.
+ */
+
+#ifndef _ALIAS_H_
+#define _ALIAS_H_
+
+#ifndef VBOX
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#else
+# include <slirp.h>
+#endif
+
+#define LIBALIAS_BUF_SIZE 128
+#ifdef _KERNEL
+/*
+ * The kernel version of libalias does not support these features.
+ */
+#define NO_FW_PUNCH
+#define NO_USE_SOCKETS
+#endif
+
+/*
+ * The external interface to libalias, the packet aliasing engine.
+ *
+ * There are two sets of functions:
+ *
+ * PacketAlias*() the old API which doesn't take an instance pointer
+ * and therefore can only have one packet engine at a time.
+ *
+ * LibAlias*() the new API which takes as first argument a pointer to
+ * the instance of the packet aliasing engine.
+ *
+ * The functions otherwise correspond to each other one for one, except
+ * for the LibAliasUnaliasOut()/PacketUnaliasOut() function which were
+ * were misnamed in the old API.
+ */
+
+/*
+ * The instance structure
+ */
+struct libalias;
+#if defined(VBOX) && !defined(VBOX_SLIRP_ALIAS)
+/* XXX: used only for browsing */
+struct libalias {
+ LIST_ENTRY(libalias) instancelist;
+};
+#endif
+
+/*
+ * An anonymous structure, a pointer to which is returned from
+ * PacketAliasRedirectAddr(), PacketAliasRedirectPort() or
+ * PacketAliasRedirectProto(), passed to PacketAliasAddServer(),
+ * and freed by PacketAliasRedirectDelete().
+ */
+struct alias_link;
+
+
+/* OLD API */
+
+/* Initialization and control functions. */
+void PacketAliasInit(void);
+void PacketAliasSetAddress(struct in_addr _addr);
+void PacketAliasSetFWBase(unsigned int _base, unsigned int _num);
+void PacketAliasSetSkinnyPort(unsigned int _port);
+unsigned int
+ PacketAliasSetMode(unsigned int _flags, unsigned int _mask);
+void PacketAliasUninit(void);
+
+/* Packet Handling functions. */
+int PacketAliasIn(char *_ptr, int _maxpacketsize);
+int PacketAliasOut(char *_ptr, int _maxpacketsize);
+int PacketUnaliasOut(char *_ptr, int _maxpacketsize);
+
+/* Port and address redirection functions. */
+
+
+int
+PacketAliasAddServer(struct alias_link *_lnk,
+ struct in_addr _addr, unsigned short _port);
+struct alias_link *
+PacketAliasRedirectAddr(struct in_addr _src_addr,
+ struct in_addr _alias_addr);
+int PacketAliasRedirectDynamic(struct alias_link *_lnk);
+void PacketAliasRedirectDelete(struct alias_link *_lnk);
+struct alias_link *
+PacketAliasRedirectPort(struct in_addr _src_addr,
+ unsigned short _src_port, struct in_addr _dst_addr,
+ unsigned short _dst_port, struct in_addr _alias_addr,
+ unsigned short _alias_port, unsigned char _proto);
+struct alias_link *
+PacketAliasRedirectProto(struct in_addr _src_addr,
+ struct in_addr _dst_addr, struct in_addr _alias_addr,
+ unsigned char _proto);
+
+/* Fragment Handling functions. */
+void PacketAliasFragmentIn(char *_ptr, char *_ptr_fragment);
+char *PacketAliasGetFragment(char *_ptr);
+int PacketAliasSaveFragment(char *_ptr);
+
+/* Miscellaneous functions. */
+int PacketAliasCheckNewLink(void);
+unsigned short
+ PacketAliasInternetChecksum(unsigned short *_ptr, int _nbytes);
+void PacketAliasSetTarget(struct in_addr _target_addr);
+
+/* Transparent proxying routines. */
+int PacketAliasProxyRule(const char *_cmd);
+
+/* NEW API */
+
+/* Initialization and control functions. */
+#ifndef VBOX
+struct libalias *LibAliasInit(struct libalias *);
+#else
+struct libalias *LibAliasInit(PNATState, struct libalias *);
+#endif
+void LibAliasSetAddress(struct libalias *, struct in_addr _addr);
+void LibAliasSetFWBase(struct libalias *, unsigned int _base, unsigned int _num);
+void LibAliasSetSkinnyPort(struct libalias *, unsigned int _port);
+unsigned int
+ LibAliasSetMode(struct libalias *, unsigned int _flags, unsigned int _mask);
+void LibAliasUninit(struct libalias *);
+
+/* Packet Handling functions. */
+int LibAliasIn (struct libalias *, char *_ptr, int _maxpacketsize);
+int LibAliasOut(struct libalias *, char *_ptr, int _maxpacketsize);
+int LibAliasOutTry(struct libalias *, char *_ptr, int _maxpacketsize, int _create);
+int LibAliasUnaliasOut(struct libalias *, char *_ptr, int _maxpacketsize);
+
+/* Port and address redirection functions. */
+
+int
+LibAliasAddServer(struct libalias *, struct alias_link *_lnk,
+ struct in_addr _addr, unsigned short _port);
+struct alias_link *
+LibAliasRedirectAddr(struct libalias *, struct in_addr _src_addr,
+ struct in_addr _alias_addr);
+int LibAliasRedirectDynamic(struct libalias *, struct alias_link *_lnk);
+void LibAliasRedirectDelete(struct libalias *, struct alias_link *_lnk);
+struct alias_link *
+LibAliasRedirectPort(struct libalias *, struct in_addr _src_addr,
+ unsigned short _src_port, struct in_addr _dst_addr,
+ unsigned short _dst_port, struct in_addr _alias_addr,
+ unsigned short _alias_port, unsigned char _proto);
+struct alias_link *
+LibAliasRedirectProto(struct libalias *, struct in_addr _src_addr,
+ struct in_addr _dst_addr, struct in_addr _alias_addr,
+ unsigned char _proto);
+
+/* Fragment Handling functions. */
+void LibAliasFragmentIn(struct libalias *, char *_ptr, char *_ptr_fragment);
+char *LibAliasGetFragment(struct libalias *, char *_ptr);
+int LibAliasSaveFragment(struct libalias *, char *_ptr);
+
+/* Miscellaneous functions. */
+int LibAliasCheckNewLink(struct libalias *);
+unsigned short
+ LibAliasInternetChecksum(struct libalias *, unsigned short *_ptr, int _nbytes);
+void LibAliasSetTarget(struct libalias *, struct in_addr _target_addr);
+
+/* Transparent proxying routines. */
+int LibAliasProxyRule(struct libalias *, const char *_cmd);
+
+/* Module handling API */
+int LibAliasLoadModule(char *);
+int LibAliasUnLoadAllModule(void);
+int LibAliasRefreshModules(void);
+
+/* Mbuf helper function. */
+#ifndef VBOX
+struct mbuf *m_megapullup(struct mbuf *, int);
+#else
+struct mbuf *m_megapullup(PNATState, struct mbuf *, int);
+#endif
+
+/*
+ * Mode flags and other constants.
+ */
+
+
+/* Mode flags, set using PacketAliasSetMode() */
+
+/*
+ * If PKT_ALIAS_LOG is set, a message will be printed to /var/log/alias.log
+ * every time a link is created or deleted. This is useful for debugging.
+ */
+#define PKT_ALIAS_LOG 0x01
+
+/*
+ * If PKT_ALIAS_DENY_INCOMING is set, then incoming connections (e.g. to ftp,
+ * telnet or web servers will be prevented by the aliasing mechanism.
+ */
+#define PKT_ALIAS_DENY_INCOMING 0x02
+
+/*
+ * If PKT_ALIAS_SAME_PORTS is set, packets will be attempted sent from the
+ * same port as they originated on. This allows e.g. rsh to work *99% of the
+ * time*, but _not_ 100% (it will be slightly flakey instead of not working
+ * at all). This mode bit is set by PacketAliasInit(), so it is a default
+ * mode of operation.
+ */
+#define PKT_ALIAS_SAME_PORTS 0x04
+
+/*
+ * If PKT_ALIAS_USE_SOCKETS is set, then when partially specified links (e.g.
+ * destination port and/or address is zero), the packet aliasing engine will
+ * attempt to allocate a socket for the aliasing port it chooses. This will
+ * avoid interference with the host machine. Fully specified links do not
+ * require this. This bit is set after a call to PacketAliasInit(), so it is
+ * a default mode of operation.
+ */
+#ifndef NO_USE_SOCKETS
+#define PKT_ALIAS_USE_SOCKETS 0x08
+#endif
+/*-
+ * If PKT_ALIAS_UNREGISTERED_ONLY is set, then only packets with
+ * unregistered source addresses will be aliased. Private
+ * addresses are those in the following ranges:
+ *
+ * 10.0.0.0 -> 10.255.255.255
+ * 172.16.0.0 -> 172.31.255.255
+ * 192.168.0.0 -> 192.168.255.255
+ */
+#define PKT_ALIAS_UNREGISTERED_ONLY 0x10
+
+/*
+ * If PKT_ALIAS_RESET_ON_ADDR_CHANGE is set, then the table of dynamic
+ * aliasing links will be reset whenever PacketAliasSetAddress() changes the
+ * default aliasing address. If the default aliasing address is left
+ * unchanged by this function call, then the table of dynamic aliasing links
+ * will be left intact. This bit is set after a call to PacketAliasInit().
+ */
+#define PKT_ALIAS_RESET_ON_ADDR_CHANGE 0x20
+
+#ifndef NO_FW_PUNCH
+/*
+ * If PKT_ALIAS_PUNCH_FW is set, active FTP and IRC DCC connections will
+ * create a 'hole' in the firewall to allow the transfers to work. The
+ * ipfw rule number that the hole is created with is controlled by
+ * PacketAliasSetFWBase(). The hole will be attached to that
+ * particular alias_link, so when the link goes away the hole is deleted.
+ */
+#define PKT_ALIAS_PUNCH_FW 0x100
+#endif
+
+/*
+ * If PKT_ALIAS_PROXY_ONLY is set, then NAT will be disabled and only
+ * transparent proxying is performed.
+ */
+#define PKT_ALIAS_PROXY_ONLY 0x40
+
+/*
+ * If PKT_ALIAS_REVERSE is set, the actions of PacketAliasIn() and
+ * PacketAliasOut() are reversed.
+ */
+#define PKT_ALIAS_REVERSE 0x80
+
+/* Function return codes. */
+#define PKT_ALIAS_ERROR -1
+#define PKT_ALIAS_OK 1
+#define PKT_ALIAS_IGNORED 2
+#define PKT_ALIAS_UNRESOLVED_FRAGMENT 3
+#define PKT_ALIAS_FOUND_HEADER_FRAGMENT 4
+
+#endif /* !_ALIAS_H_ */
+
+/* lint -restore */
diff --git a/src/VBox/Devices/Network/slirp/libalias/alias_cuseeme.c b/src/VBox/Devices/Network/slirp/libalias/alias_cuseeme.c
new file mode 100644
index 00000000..9b5c6a57
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/alias_cuseeme.c
@@ -0,0 +1,228 @@
+/*-
+ * Copyright (c) 1998 Brian Somers <brian@Awfulhak.org>
+ * with the aid of code written by
+ * Junichi SATOH <junichi@astec.co.jp> 1996, 1997.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHOR 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 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netinet/libalias/alias_cuseeme.c,v 1.13.8.1 2009/04/15 03:14:26 kensmith Exp $");
+
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#else
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#endif
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#ifdef _KERNEL
+#include <netinet/libalias/alias.h>
+#include <netinet/libalias/alias_local.h>
+#include <netinet/libalias/alias_mod.h>
+#else
+#include "alias_local.h"
+#include "alias_mod.h"
+#endif
+
+#define CUSEEME_PORT_NUMBER 7648
+
+static void
+AliasHandleCUSeeMeOut(struct libalias *la, struct ip *pip,
+ struct alias_link *lnk);
+
+static void
+AliasHandleCUSeeMeIn(struct libalias *la, struct ip *pip,
+ struct in_addr original_addr);
+
+static int
+fingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ if (ah->dport == NULL || ah->oaddr == NULL)
+ return (-1);
+ if (ntohs(*ah->dport) == CUSEEME_PORT_NUMBER)
+ return (0);
+ return (-1);
+}
+
+static int
+protohandlerin(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ AliasHandleCUSeeMeIn(la, pip, *ah->oaddr);
+ return (0);
+}
+
+static int
+protohandlerout(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ AliasHandleCUSeeMeOut(la, pip, ah->lnk);
+ return (0);
+}
+
+/* Kernel module definition. */
+struct proto_handler handlers[] = {
+ {
+ .pri = 120,
+ .dir = OUT,
+ .proto = UDP,
+ .fingerprint = &fingerprint,
+ .protohandler = &protohandlerout
+ },
+ {
+ .pri = 120,
+ .dir = IN,
+ .proto = UDP,
+ .fingerprint = &fingerprint,
+ .protohandler = &protohandlerin
+ },
+ { EOH }
+};
+
+static int
+mod_handler(module_t mod, int type, void *data)
+{
+ int error;
+
+ switch (type) {
+ case MOD_LOAD:
+ error = 0;
+ LibAliasAttachHandlers(handlers);
+ break;
+ case MOD_UNLOAD:
+ error = 0;
+ LibAliasDetachHandlers(handlers);
+ break;
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
+
+#ifdef _KERNEL
+static
+#endif
+moduledata_t
+alias_mod = {
+ "alias_cuseeme", mod_handler, NULL
+};
+
+#ifdef _KERNEL
+DECLARE_MODULE(alias_cuseeme, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
+MODULE_VERSION(alias_cuseeme, 1);
+MODULE_DEPEND(alias_cuseeme, libalias, 1, 1, 1);
+#endif
+
+/* CU-SeeMe Data Header */
+struct cu_header {
+ u_int16_t dest_family;
+ u_int16_t dest_port;
+ u_int32_t dest_addr;
+ int16_t family;
+ u_int16_t port;
+ u_int32_t addr;
+ u_int32_t seq;
+ u_int16_t msg;
+ u_int16_t data_type;
+ u_int16_t packet_len;
+};
+
+/* Open Continue Header */
+struct oc_header {
+ u_int16_t client_count; /* Number of client info structs */
+ u_int32_t seq_no;
+ char user_name [20];
+ char reserved [4]; /* flags, version stuff, etc */
+};
+
+/* client info structures */
+struct client_info {
+ u_int32_t address;/* Client address */
+ char reserved [8]; /* Flags, pruning bitfield, packet
+ * counts etc */
+};
+
+static void
+AliasHandleCUSeeMeOut(struct libalias *la, struct ip *pip, struct alias_link *lnk)
+{
+ struct udphdr *ud = ip_next(pip);
+
+ if (ntohs(ud->uh_ulen) - sizeof(struct udphdr) >= sizeof(struct cu_header)) {
+ struct cu_header *cu;
+ struct alias_link *cu_lnk;
+
+ cu = udp_next(ud);
+ if (cu->addr)
+ cu->addr = (u_int32_t) GetAliasAddress(lnk).s_addr;
+
+ cu_lnk = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk),
+ ud->uh_dport, 0, IPPROTO_UDP, 1);
+
+#ifndef NO_FW_PUNCH
+ if (cu_lnk)
+ PunchFWHole(cu_lnk);
+#endif
+ }
+}
+
+static void
+AliasHandleCUSeeMeIn(struct libalias *la, struct ip *pip, struct in_addr original_addr)
+{
+ struct in_addr alias_addr;
+ struct udphdr *ud;
+ struct cu_header *cu;
+ struct oc_header *oc;
+ struct client_info *ci;
+ char *end;
+ int i;
+
+ (void)la;
+ alias_addr.s_addr = pip->ip_dst.s_addr;
+ ud = ip_next(pip);
+ cu = udp_next(ud);
+ oc = (struct oc_header *)(cu + 1);
+ ci = (struct client_info *)(oc + 1);
+ end = (char *)ud + ntohs(ud->uh_ulen);
+
+ if ((char *)oc <= end) {
+ if (cu->dest_addr)
+ cu->dest_addr = (u_int32_t) original_addr.s_addr;
+ if (ntohs(cu->data_type) == 101)
+ /* Find and change our address */
+ for (i = 0; (char *)(ci + 1) <= end && i < oc->client_count; i++, ci++)
+ if (ci->address == (u_int32_t) alias_addr.s_addr) {
+ ci->address = (u_int32_t) original_addr.s_addr;
+ break;
+ }
+ }
+}
diff --git a/src/VBox/Devices/Network/slirp/libalias/alias_db.c b/src/VBox/Devices/Network/slirp/libalias/alias_db.c
new file mode 100644
index 00000000..a4f3c160
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/alias_db.c
@@ -0,0 +1,2966 @@
+/*-
+ * Copyright (c) 2001 Charles Mott <cm@linktel.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHOR 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 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 VBOX
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netinet/libalias/alias_db.c,v 1.71.2.2.4.1 2009/04/15 03:14:26 kensmith Exp $");
+#endif
+
+/*
+ Alias_db.c encapsulates all data structures used for storing
+ packet aliasing data. Other parts of the aliasing software
+ access data through functions provided in this file.
+
+ Data storage is based on the notion of a "link", which is
+ established for ICMP echo/reply packets, UDP datagrams and
+ TCP stream connections. A link stores the original source
+ and destination addresses. For UDP and TCP, it also stores
+ source and destination port numbers, as well as an alias
+ port number. Links are also used to store information about
+ fragments.
+
+ There is a facility for sweeping through and deleting old
+ links as new packets are sent through. A simple timeout is
+ used for ICMP and UDP links. TCP links are left alone unless
+ there is an incomplete connection, in which case the link
+ can be deleted after a certain amount of time.
+
+
+ Initial version: August, 1996 (cjm)
+
+ Version 1.4: September 16, 1996 (cjm)
+ Facility for handling incoming links added.
+
+ Version 1.6: September 18, 1996 (cjm)
+ ICMP data handling simplified.
+
+ Version 1.7: January 9, 1997 (cjm)
+ Fragment handling simplified.
+ Saves pointers for unresolved fragments.
+ Permits links for unspecified remote ports
+ or unspecified remote addresses.
+ Fixed bug which did not properly zero port
+ table entries after a link was deleted.
+ Cleaned up some obsolete comments.
+
+ Version 1.8: January 14, 1997 (cjm)
+ Fixed data type error in StartPoint().
+ (This error did not exist prior to v1.7
+ and was discovered and fixed by Ari Suutari)
+
+ Version 1.9: February 1, 1997
+ Optionally, connections initiated from packet aliasing host
+ machine will will not have their port number aliased unless it
+ conflicts with an aliasing port already being used. (cjm)
+
+ All options earlier being #ifdef'ed are now available through
+ a new interface, SetPacketAliasMode(). This allows run time
+ control (which is now available in PPP+pktAlias through the
+ 'alias' keyword). (ee)
+
+ Added ability to create an alias port without
+ either destination address or port specified.
+ port type = ALIAS_PORT_UNKNOWN_DEST_ALL (ee)
+
+ Removed K&R style function headers
+ and general cleanup. (ee)
+
+ Added packetAliasMode to replace compiler #defines's (ee)
+
+ Allocates sockets for partially specified
+ ports if ALIAS_USE_SOCKETS defined. (cjm)
+
+ Version 2.0: March, 1997
+ SetAliasAddress() will now clean up alias links
+ if the aliasing address is changed. (cjm)
+
+ PacketAliasPermanentLink() function added to support permanent
+ links. (J. Fortes suggested the need for this.)
+ Examples:
+
+ (192.168.0.1, port 23) <-> alias port 6002, unknown dest addr/port
+
+ (192.168.0.2, port 21) <-> alias port 3604, known dest addr
+ unknown dest port
+
+ These permanent links allow for incoming connections to
+ machines on the local network. They can be given with a
+ user-chosen amount of specificity, with increasing specificity
+ meaning more security. (cjm)
+
+ Quite a bit of rework to the basic engine. The portTable[]
+ array, which kept track of which ports were in use was replaced
+ by a table/linked list structure. (cjm)
+
+ SetExpire() function added. (cjm)
+
+ DeleteLink() no longer frees memory association with a pointer
+ to a fragment (this bug was first recognized by E. Eklund in
+ v1.9).
+
+ Version 2.1: May, 1997 (cjm)
+ Packet aliasing engine reworked so that it can handle
+ multiple external addresses rather than just a single
+ host address.
+
+ PacketAliasRedirectPort() and PacketAliasRedirectAddr()
+ added to the API. The first function is a more generalized
+ version of PacketAliasPermanentLink(). The second function
+ implements static network address translation.
+
+ Version 3.2: July, 2000 (salander and satoh)
+ Added FindNewPortGroup to get contiguous range of port values.
+
+ Added QueryUdpTcpIn and QueryUdpTcpOut to look for an aliasing
+ link but not actually add one.
+
+ Added FindRtspOut, which is closely derived from FindUdpTcpOut,
+ except that the alias port (from FindNewPortGroup) is provided
+ as input.
+
+ See HISTORY file for additional revisions.
+*/
+
+#ifndef VBOX
+#ifdef _KERNEL
+#include <machine/stdarg.h>
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#include <sys/syslog.h>
+#else
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <sys/errno.h>
+#include <sys/time.h>
+#include <unistd.h>
+#endif
+
+#include <sys/socket.h>
+#include <netinet/tcp.h>
+
+#ifdef _KERNEL
+#include <netinet/libalias/alias.h>
+#include <netinet/libalias/alias_local.h>
+#include <netinet/libalias/alias_mod.h>
+#include <net/if.h>
+#else
+#include "alias.h"
+#include "alias_local.h"
+#include "alias_mod.h"
+#endif
+#else /* VBOX */
+# include <iprt/assert.h>
+# include "alias.h"
+# include "alias_local.h"
+# include "alias_mod.h"
+# include <slirp.h>
+#endif /* VBOX */
+
+#ifndef VBOX
+static LIST_HEAD(, libalias) instancehead = LIST_HEAD_INITIALIZER(instancehead);
+#endif
+
+
+/*
+ Constants (note: constants are also defined
+ near relevant functions or structs)
+*/
+
+/* Parameters used for cleanup of expired links */
+/* NOTE: ALIAS_CLEANUP_INTERVAL_SECS must be less then LINK_TABLE_OUT_SIZE */
+#define ALIAS_CLEANUP_INTERVAL_SECS 64
+#define ALIAS_CLEANUP_MAX_SPOKES (LINK_TABLE_OUT_SIZE/5)
+
+/* Timeouts (in seconds) for different link types */
+#define ICMP_EXPIRE_TIME 60
+#define UDP_EXPIRE_TIME 60
+#define PROTO_EXPIRE_TIME 60
+#define FRAGMENT_ID_EXPIRE_TIME 10
+#define FRAGMENT_PTR_EXPIRE_TIME 30
+
+/* TCP link expire time for different cases */
+/* When the link has been used and closed - minimal grace time to
+ allow ACKs and potential re-connect in FTP (XXX - is this allowed?) */
+#ifndef TCP_EXPIRE_DEAD
+#define TCP_EXPIRE_DEAD 10
+#endif
+
+/* When the link has been used and closed on one side - the other side
+ is allowed to still send data */
+#ifndef TCP_EXPIRE_SINGLEDEAD
+#define TCP_EXPIRE_SINGLEDEAD 90
+#endif
+
+/* When the link isn't yet up */
+#ifndef TCP_EXPIRE_INITIAL
+#define TCP_EXPIRE_INITIAL 300
+#endif
+
+/* When the link is up */
+#ifndef TCP_EXPIRE_CONNECTED
+#define TCP_EXPIRE_CONNECTED 86400
+#endif
+
+
+/* Dummy port number codes used for FindLinkIn/Out() and AddLink().
+ These constants can be anything except zero, which indicates an
+ unknown port number. */
+
+#define NO_DEST_PORT 1
+#define NO_SRC_PORT 1
+
+
+
+/* Data Structures
+
+ The fundamental data structure used in this program is
+ "struct alias_link". Whenever a TCP connection is made,
+ a UDP datagram is sent out, or an ICMP echo request is made,
+ a link record is made (if it has not already been created).
+ The link record is identified by the source address/port
+ and the destination address/port. In the case of an ICMP
+ echo request, the source port is treated as being equivalent
+ with the 16-bit ID number of the ICMP packet.
+
+ The link record also can store some auxiliary data. For
+ TCP connections that have had sequence and acknowledgment
+ modifications, data space is available to track these changes.
+ A state field is used to keep track in changes to the TCP
+ connection state. ID numbers of fragments can also be
+ stored in the auxiliary space. Pointers to unresolved
+ fragments can also be stored.
+
+ The link records support two independent chainings. Lookup
+ tables for input and out tables hold the initial pointers
+ the link chains. On input, the lookup table indexes on alias
+ port and link type. On output, the lookup table indexes on
+ source address, destination address, source port, destination
+ port and link type.
+*/
+
+struct ack_data_record { /* used to save changes to ACK/sequence
+ * numbers */
+ u_long ack_old;
+ u_long ack_new;
+ int delta;
+ int active;
+};
+
+struct tcp_state { /* Information about TCP connection */
+ int in; /* State for outside -> inside */
+ int out; /* State for inside -> outside */
+ int index; /* Index to ACK data array */
+ int ack_modified; /* Indicates whether ACK and
+ * sequence numbers */
+ /* been modified */
+};
+
+#define N_LINK_TCP_DATA 3 /* Number of distinct ACK number changes
+ * saved for a modified TCP stream */
+struct tcp_dat {
+ struct tcp_state state;
+ struct ack_data_record ack[N_LINK_TCP_DATA];
+ int fwhole; /* Which firewall record is used for this
+ * hole? */
+};
+
+struct server { /* LSNAT server pool (circular list) */
+ struct in_addr addr;
+ u_short port;
+ struct server *next;
+};
+
+struct alias_link { /* Main data structure */
+ struct libalias *la;
+ struct in_addr src_addr; /* Address and port information */
+ struct in_addr dst_addr;
+ struct in_addr alias_addr;
+ struct in_addr proxy_addr;
+ u_short src_port;
+ u_short dst_port;
+ u_short alias_port;
+ u_short proxy_port;
+ struct server *server;
+
+ int link_type; /* Type of link: TCP, UDP, ICMP,
+ * proto, frag */
+
+/* values for link_type */
+#define LINK_ICMP IPPROTO_ICMP
+#define LINK_UDP IPPROTO_UDP
+#define LINK_TCP IPPROTO_TCP
+#define LINK_FRAGMENT_ID (IPPROTO_MAX + 1)
+#define LINK_FRAGMENT_PTR (IPPROTO_MAX + 2)
+#define LINK_ADDR (IPPROTO_MAX + 3)
+#define LINK_PPTP (IPPROTO_MAX + 4)
+
+ int flags; /* indicates special characteristics */
+ int pflags; /* protocol-specific flags */
+
+/* flag bits */
+#define LINK_UNKNOWN_DEST_PORT 0x01
+#define LINK_UNKNOWN_DEST_ADDR 0x02
+#define LINK_PERMANENT 0x04
+#define LINK_PARTIALLY_SPECIFIED 0x03 /* logical-or of first two bits */
+#ifndef VBOX
+# define LINK_UNFIREWALLED 0x08 /* This macro definition isn't used in this revision of libalias */
+
+ int timestamp; /* Time link was last accessed */
+ int expire_time; /* Expire time for link */
+#else /* VBOX */
+ unsigned int timestamp; /* Time link was last accessed */
+ unsigned int expire_time; /* Expire time for link */
+#endif
+
+#ifndef NO_USE_SOCKETS
+ int sockfd; /* socket descriptor */
+#endif
+ LIST_ENTRY (alias_link) list_out; /* Linked list of
+ * pointers for */
+ LIST_ENTRY (alias_link) list_in; /* input and output
+ * lookup tables */
+
+ union { /* Auxiliary data */
+ char *frag_ptr;
+ struct in_addr frag_addr;
+ struct tcp_dat *tcp;
+ } data;
+};
+
+/* Clean up procedure. */
+#ifndef VBOX
+static void finishoff(void);
+#endif
+
+/* Kernel module definition. */
+#ifdef _KERNEL
+MALLOC_DEFINE(M_ALIAS, "libalias", "packet aliasing");
+
+MODULE_VERSION(libalias, 1);
+
+static int
+alias_mod_handler(module_t mod, int type, void *data)
+{
+ int error;
+
+ switch (type) {
+ case MOD_LOAD:
+ error = 0;
+ handler_chain_init();
+ break;
+ case MOD_QUIESCE:
+ case MOD_UNLOAD:
+ handler_chain_destroy();
+ finishoff();
+ error = 0;
+ break;
+ default:
+ error = EINVAL;
+ }
+
+ return (error);
+}
+
+static moduledata_t alias_mod = {
+ "alias", alias_mod_handler, NULL
+};
+
+DECLARE_MODULE(alias, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
+#endif
+
+/* Internal utility routines (used only in alias_db.c)
+
+Lookup table starting points:
+ StartPointIn() -- link table initial search point for
+ incoming packets
+ StartPointOut() -- link table initial search point for
+ outgoing packets
+
+Miscellaneous:
+ SeqDiff() -- difference between two TCP sequences
+ ShowAliasStats() -- send alias statistics to a monitor file
+*/
+
+
+/* Local prototypes */
+static u_int StartPointIn(struct in_addr, u_short, int);
+
+static u_int
+StartPointOut(struct in_addr, struct in_addr,
+ u_short, u_short, int);
+
+static int SeqDiff(u_long, u_long);
+
+#ifndef NO_FW_PUNCH
+/* Firewall control */
+static void InitPunchFW(struct libalias *);
+static void UninitPunchFW(struct libalias *);
+static void ClearFWHole(struct alias_link *);
+
+#endif
+
+/* Log file control */
+static void ShowAliasStats(struct libalias *);
+static int InitPacketAliasLog(struct libalias *);
+static void UninitPacketAliasLog(struct libalias *);
+
+static u_int
+StartPointIn(struct in_addr alias_addr,
+ u_short alias_port,
+ int link_type)
+{
+ u_int n;
+
+ n = alias_addr.s_addr;
+ if (link_type != LINK_PPTP)
+ n += alias_port;
+ n += link_type;
+ return (n % LINK_TABLE_IN_SIZE);
+}
+
+
+static u_int
+StartPointOut(struct in_addr src_addr, struct in_addr dst_addr,
+ u_short src_port, u_short dst_port, int link_type)
+{
+ u_int n;
+
+ n = src_addr.s_addr;
+ n += dst_addr.s_addr;
+ if (link_type != LINK_PPTP) {
+ n += src_port;
+ n += dst_port;
+ }
+ n += link_type;
+
+ return (n % LINK_TABLE_OUT_SIZE);
+}
+
+
+static int
+SeqDiff(u_long x, u_long y)
+{
+/* Return the difference between two TCP sequence numbers */
+
+/*
+ This function is encapsulated in case there are any unusual
+ arithmetic conditions that need to be considered.
+*/
+
+ return (ntohl(y) - ntohl(x));
+}
+
+#ifdef _KERNEL
+
+static void
+AliasLog(char *str, const char *format, ...)
+{
+ va_list ap;
+
+ va_start(ap, format);
+ vsnprintf(str, LIBALIAS_BUF_SIZE, format, ap);
+ va_end(ap);
+}
+#else
+static void
+AliasLog(FILE *stream, const char *format, ...)
+{
+# ifndef VBOX
+ va_list ap;
+
+ va_start(ap, format);
+ vfprintf(stream, format, ap);
+ va_end(ap);
+ fflush(stream);
+# else
+
+ va_list args;
+ char buffer[1024];
+ NOREF(stream);
+ memset(buffer, 0, 1024);
+ va_start(args, format);
+ RTStrPrintfV(buffer, 1024, format, args);
+ va_end(args);
+ /*make it grepable */
+ Log2(("NAT:ALIAS: %s\n", buffer));
+# endif
+}
+#endif
+
+static void
+ShowAliasStats(struct libalias *la)
+{
+
+ LIBALIAS_LOCK_ASSERT(la);
+/* Used for debugging */
+ if (la->logDesc) {
+ int tot = la->icmpLinkCount + la->udpLinkCount +
+ la->tcpLinkCount + la->pptpLinkCount +
+ la->protoLinkCount + la->fragmentIdLinkCount +
+ la->fragmentPtrLinkCount;
+
+ AliasLog(la->logDesc,
+ "icmp=%u, udp=%u, tcp=%u, pptp=%u, proto=%u, frag_id=%u frag_ptr=%u / tot=%u",
+ la->icmpLinkCount,
+ la->udpLinkCount,
+ la->tcpLinkCount,
+ la->pptpLinkCount,
+ la->protoLinkCount,
+ la->fragmentIdLinkCount,
+ la->fragmentPtrLinkCount, tot);
+#ifndef _KERNEL
+ AliasLog(la->logDesc, " (sock=%u)\n", la->sockCount);
+#endif
+ }
+}
+
+/* Internal routines for finding, deleting and adding links
+
+Port Allocation:
+ GetNewPort() -- find and reserve new alias port number
+ GetSocket() -- try to allocate a socket for a given port
+
+Link creation and deletion:
+ CleanupAliasData() - remove all link chains from lookup table
+ IncrementalCleanup() - look for stale links in a single chain
+ DeleteLink() - remove link
+ AddLink() - add link
+ ReLink() - change link
+
+Link search:
+ FindLinkOut() - find link for outgoing packets
+ FindLinkIn() - find link for incoming packets
+
+Port search:
+ FindNewPortGroup() - find an available group of ports
+*/
+
+/* Local prototypes */
+static int GetNewPort(struct libalias *, struct alias_link *, int);
+#ifndef NO_USE_SOCKETS
+static u_short GetSocket(struct libalias *, u_short, int *, int);
+#endif
+static void CleanupAliasData(struct libalias *);
+
+static void IncrementalCleanup(struct libalias *);
+
+static void DeleteLink(struct alias_link *);
+
+static struct alias_link *
+AddLink(struct libalias *, struct in_addr, struct in_addr, struct in_addr,
+ u_short, u_short, int, int);
+
+static struct alias_link *
+ReLink(struct alias_link *,
+ struct in_addr, struct in_addr, struct in_addr,
+ u_short, u_short, int, int);
+
+static struct alias_link *
+ FindLinkOut (struct libalias *, struct in_addr, struct in_addr, u_short, u_short, int, int);
+
+static struct alias_link *
+ FindLinkIn (struct libalias *, struct in_addr, struct in_addr, u_short, u_short, int, int);
+
+
+#define ALIAS_PORT_BASE 0x08000
+#define ALIAS_PORT_MASK 0x07fff
+#define ALIAS_PORT_MASK_EVEN 0x07ffe
+#define GET_NEW_PORT_MAX_ATTEMPTS 20
+
+#define GET_ALIAS_PORT -1
+#define GET_ALIAS_ID GET_ALIAS_PORT
+
+#define FIND_EVEN_ALIAS_BASE 1
+
+/* GetNewPort() allocates port numbers. Note that if a port number
+ is already in use, that does not mean that it cannot be used by
+ another link concurrently. This is because GetNewPort() looks for
+ unused triplets: (dest addr, dest port, alias port). */
+
+static int
+GetNewPort(struct libalias *la, struct alias_link *lnk, int alias_port_param)
+{
+ int i;
+ int max_trials;
+ u_short port_sys;
+ u_short port_net;
+
+ LIBALIAS_LOCK_ASSERT(la);
+/*
+ Description of alias_port_param for GetNewPort(). When
+ this parameter is zero or positive, it precisely specifies
+ the port number. GetNewPort() will return this number
+ without check that it is in use.
+
+ When this parameter is GET_ALIAS_PORT, it indicates to get a randomly
+ selected port number.
+*/
+
+ if (alias_port_param == GET_ALIAS_PORT) {
+ /*
+ * The aliasing port is automatically selected by one of
+ * two methods below:
+ */
+ max_trials = GET_NEW_PORT_MAX_ATTEMPTS;
+
+ if (la->packetAliasMode & PKT_ALIAS_SAME_PORTS) {
+ /*
+ * When the PKT_ALIAS_SAME_PORTS option is chosen,
+ * the first try will be the actual source port. If
+ * this is already in use, the remainder of the
+ * trials will be random.
+ */
+ port_net = lnk->src_port;
+ port_sys = ntohs(port_net);
+ } else {
+ /* First trial and all subsequent are random. */
+ port_sys = arc4random() & ALIAS_PORT_MASK;
+ port_sys += ALIAS_PORT_BASE;
+ port_net = htons(port_sys);
+ }
+ } else if (alias_port_param >= 0 && alias_port_param < 0x10000) {
+ lnk->alias_port = (u_short) alias_port_param;
+ return (0);
+ } else {
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr, "PacketAlias/GetNewPort(): ");
+ fprintf(stderr, "input parameter error\n");
+#endif
+ return (-1);
+ }
+
+
+/* Port number search */
+ for (i = 0; i < max_trials; i++) {
+ int go_ahead;
+ struct alias_link *search_result;
+
+ search_result = FindLinkIn(la, lnk->dst_addr, lnk->alias_addr,
+ lnk->dst_port, port_net,
+ lnk->link_type, 0);
+
+ if (search_result == NULL)
+ go_ahead = 1;
+ else if (!(lnk->flags & LINK_PARTIALLY_SPECIFIED)
+ && (search_result->flags & LINK_PARTIALLY_SPECIFIED))
+ go_ahead = 1;
+ else
+ go_ahead = 0;
+
+ if (go_ahead) {
+#ifndef NO_USE_SOCKETS
+ if ((la->packetAliasMode & PKT_ALIAS_USE_SOCKETS)
+ && (lnk->flags & LINK_PARTIALLY_SPECIFIED)
+ && ((lnk->link_type == LINK_TCP) ||
+ (lnk->link_type == LINK_UDP))) {
+ if (GetSocket(la, port_net, &lnk->sockfd, lnk->link_type)) {
+ lnk->alias_port = port_net;
+ return (0);
+ }
+ } else {
+#endif
+ lnk->alias_port = port_net;
+ return (0);
+#ifndef NO_USE_SOCKETS
+ }
+#endif
+ }
+ port_sys = arc4random() & ALIAS_PORT_MASK;
+ port_sys += ALIAS_PORT_BASE;
+ port_net = htons(port_sys);
+ }
+
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr, "PacketAlias/GetnewPort(): ");
+ fprintf(stderr, "could not find free port\n");
+#endif
+
+ return (-1);
+}
+
+#ifndef NO_USE_SOCKETS
+static u_short
+GetSocket(struct libalias *la, u_short port_net, int *sockfd, int link_type)
+{
+ int err;
+ int sock;
+ struct sockaddr_in sock_addr;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ if (link_type == LINK_TCP)
+ sock = socket(AF_INET, SOCK_STREAM, 0);
+ else if (link_type == LINK_UDP)
+ sock = socket(AF_INET, SOCK_DGRAM, 0);
+ else {
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr, "PacketAlias/GetSocket(): ");
+ fprintf(stderr, "incorrect link type\n");
+#endif
+ return (0);
+ }
+
+ if (sock < 0) {
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr, "PacketAlias/GetSocket(): ");
+ fprintf(stderr, "socket() error %d\n", *sockfd);
+#endif
+ return (0);
+ }
+
+ memset(&sock_addr, 0, sizeof(struct sockaddr_in));
+ sock_addr.sin_family = AF_INET;
+ sock_addr.sin_addr.s_addr = htonl(INADDR_ANY);
+ sock_addr.sin_port = port_net;
+#ifdef RT_OS_DARWIN
+ sock_addr.sin_len = sizeof(struct sockaddr_in);
+#endif
+
+
+ err = bind(sock,
+ (struct sockaddr *)&sock_addr,
+ sizeof(sock_addr));
+ if (err == 0) {
+ la->sockCount++;
+ *sockfd = sock;
+ return (1);
+ } else {
+ closesocket(sock);
+ return (0);
+ }
+}
+#endif
+
+/* FindNewPortGroup() returns a base port number for an available
+ range of contiguous port numbers. Note that if a port number
+ is already in use, that does not mean that it cannot be used by
+ another link concurrently. This is because FindNewPortGroup()
+ looks for unused triplets: (dest addr, dest port, alias port). */
+
+int
+FindNewPortGroup(struct libalias *la,
+ struct in_addr dst_addr,
+ struct in_addr alias_addr,
+ u_short src_port,
+ u_short dst_port,
+ u_short port_count,
+ u_char proto,
+ u_char align)
+{
+ int i, j;
+ int max_trials;
+ u_short port_sys;
+ int link_type;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ /*
+ * Get link_type from protocol
+ */
+
+ switch (proto) {
+ case IPPROTO_UDP:
+ link_type = LINK_UDP;
+ break;
+ case IPPROTO_TCP:
+ link_type = LINK_TCP;
+ break;
+ default:
+ return (0);
+ break;
+ }
+
+ /*
+ * The aliasing port is automatically selected by one of two
+ * methods below:
+ */
+ max_trials = GET_NEW_PORT_MAX_ATTEMPTS;
+
+ if (la->packetAliasMode & PKT_ALIAS_SAME_PORTS) {
+ /*
+ * When the ALIAS_SAME_PORTS option is chosen, the first
+ * try will be the actual source port. If this is already
+ * in use, the remainder of the trials will be random.
+ */
+ port_sys = ntohs(src_port);
+
+ } else {
+
+ /* First trial and all subsequent are random. */
+ if (align == FIND_EVEN_ALIAS_BASE)
+ port_sys = arc4random() & ALIAS_PORT_MASK_EVEN;
+ else
+ port_sys = arc4random() & ALIAS_PORT_MASK;
+
+ port_sys += ALIAS_PORT_BASE;
+ }
+
+/* Port number search */
+ for (i = 0; i < max_trials; i++) {
+
+ struct alias_link *search_result;
+
+ for (j = 0; j < port_count; j++)
+ if (0 != (search_result = FindLinkIn(la, dst_addr, alias_addr,
+ dst_port, htons(port_sys + j),
+ link_type, 0)))
+ break;
+
+ /* Found a good range, return base */
+ if (j == port_count)
+ return (htons(port_sys));
+
+ /* Find a new base to try */
+ if (align == FIND_EVEN_ALIAS_BASE)
+ port_sys = arc4random() & ALIAS_PORT_MASK_EVEN;
+ else
+ port_sys = arc4random() & ALIAS_PORT_MASK;
+
+ port_sys += ALIAS_PORT_BASE;
+ }
+
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr, "PacketAlias/FindNewPortGroup(): ");
+ fprintf(stderr, "could not find free port(s)\n");
+#endif
+
+ return (0);
+}
+
+static void
+CleanupAliasData(struct libalias *la)
+{
+ struct alias_link *lnk;
+ int i;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ for (i = 0; i < LINK_TABLE_OUT_SIZE; i++) {
+ lnk = LIST_FIRST(&la->linkTableOut[i]);
+ while (lnk != NULL) {
+ struct alias_link *link_next = LIST_NEXT(lnk, list_out);
+ DeleteLink(lnk);
+ lnk = link_next;
+ }
+ }
+
+ la->cleanupIndex = 0;
+}
+
+
+static void
+IncrementalCleanup(struct libalias *la)
+{
+ struct alias_link *lnk, *lnk_tmp;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ LIST_FOREACH_SAFE(lnk, &la->linkTableOut[la->cleanupIndex++],
+ list_out, lnk_tmp) {
+ if (la->timeStamp - lnk->timestamp > lnk->expire_time)
+ DeleteLink(lnk);
+ }
+
+ if (la->cleanupIndex == LINK_TABLE_OUT_SIZE)
+ la->cleanupIndex = 0;
+}
+
+static void
+DeleteLink(struct alias_link *lnk)
+{
+ struct libalias *la = lnk->la;
+ LogFlowFuncEnter();
+
+ LIBALIAS_LOCK_ASSERT(la);
+/* Don't do anything if the link is marked permanent */
+ if (la->deleteAllLinks == 0 && lnk->flags & LINK_PERMANENT)
+ return;
+
+#ifndef NO_FW_PUNCH
+/* Delete associated firewall hole, if any */
+ ClearFWHole(lnk);
+#endif
+
+/* Free memory allocated for LSNAT server pool */
+ if (lnk->server != NULL) {
+ struct server *head, *curr, *next;
+
+ head = curr = lnk->server;
+ do {
+ next = curr->next;
+ free(curr);
+ } while ((curr = next) != head);
+ }
+/* Adjust output table pointers */
+ LIST_REMOVE(lnk, list_out);
+
+/* Adjust input table pointers */
+ LIST_REMOVE(lnk, list_in);
+#ifndef NO_USE_SOCKETS
+/* Close socket, if one has been allocated */
+ if (lnk->sockfd != -1) {
+ la->sockCount--;
+ closesocket(lnk->sockfd);
+ }
+#endif
+/* Link-type dependent cleanup */
+ switch (lnk->link_type) {
+ case LINK_ICMP:
+ la->icmpLinkCount--;
+ break;
+ case LINK_UDP:
+ la->udpLinkCount--;
+ break;
+ case LINK_TCP:
+ la->tcpLinkCount--;
+ free(lnk->data.tcp);
+ break;
+ case LINK_PPTP:
+ la->pptpLinkCount--;
+ break;
+ case LINK_FRAGMENT_ID:
+ la->fragmentIdLinkCount--;
+ break;
+ case LINK_FRAGMENT_PTR:
+ la->fragmentPtrLinkCount--;
+ if (lnk->data.frag_ptr != NULL)
+ free(lnk->data.frag_ptr);
+ break;
+ case LINK_ADDR:
+ break;
+ default:
+ la->protoLinkCount--;
+ break;
+ }
+
+/* Free memory */
+ free(lnk);
+
+/* Write statistics, if logging enabled */
+ if (la->packetAliasMode & PKT_ALIAS_LOG) {
+ ShowAliasStats(la);
+ }
+ LogFlowFuncLeave();
+}
+
+
+static struct alias_link *
+AddLink(struct libalias *la, struct in_addr src_addr,
+ struct in_addr dst_addr,
+ struct in_addr alias_addr,
+ u_short src_port,
+ u_short dst_port,
+ int alias_port_param, /* if less than zero, alias */
+ int link_type)
+{ /* port will be automatically *//* chosen.
+ * If greater than */
+ u_int start_point; /* zero, equal to alias port */
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ lnk = malloc(sizeof(struct alias_link));
+ if (lnk != NULL) {
+ /* Basic initialization */
+ lnk->la = la;
+ lnk->src_addr = src_addr;
+ lnk->dst_addr = dst_addr;
+ lnk->alias_addr = alias_addr;
+ lnk->proxy_addr.s_addr = INADDR_ANY;
+ lnk->src_port = src_port;
+ lnk->dst_port = dst_port;
+ lnk->proxy_port = 0;
+ lnk->server = NULL;
+ lnk->link_type = link_type;
+#ifndef NO_USE_SOCKETS
+ lnk->sockfd = -1;
+#endif
+ lnk->flags = 0;
+ lnk->pflags = 0;
+ lnk->timestamp = la->timeStamp;
+
+ /* Expiration time */
+ switch (link_type) {
+ case LINK_ICMP:
+ lnk->expire_time = ICMP_EXPIRE_TIME;
+ break;
+ case LINK_UDP:
+ lnk->expire_time = UDP_EXPIRE_TIME;
+ break;
+ case LINK_TCP:
+ lnk->expire_time = TCP_EXPIRE_INITIAL;
+ break;
+ case LINK_PPTP:
+ lnk->flags |= LINK_PERMANENT; /* no timeout. */
+ break;
+ case LINK_FRAGMENT_ID:
+ lnk->expire_time = FRAGMENT_ID_EXPIRE_TIME;
+ break;
+ case LINK_FRAGMENT_PTR:
+ lnk->expire_time = FRAGMENT_PTR_EXPIRE_TIME;
+ break;
+ case LINK_ADDR:
+ break;
+ default:
+ lnk->expire_time = PROTO_EXPIRE_TIME;
+ break;
+ }
+
+ /* Determine alias flags */
+ if (dst_addr.s_addr == INADDR_ANY)
+ lnk->flags |= LINK_UNKNOWN_DEST_ADDR;
+ if (dst_port == 0)
+ lnk->flags |= LINK_UNKNOWN_DEST_PORT;
+
+ /* Determine alias port */
+ if (GetNewPort(la, lnk, alias_port_param) != 0) {
+ free(lnk);
+ return (NULL);
+ }
+ /* Link-type dependent initialization */
+ switch (link_type) {
+ struct tcp_dat *aux_tcp;
+
+ case LINK_ICMP:
+ la->icmpLinkCount++;
+ break;
+ case LINK_UDP:
+ la->udpLinkCount++;
+ break;
+ case LINK_TCP:
+ aux_tcp = malloc(sizeof(struct tcp_dat));
+ if (aux_tcp != NULL) {
+ int i;
+
+ la->tcpLinkCount++;
+ aux_tcp->state.in = ALIAS_TCP_STATE_NOT_CONNECTED;
+ aux_tcp->state.out = ALIAS_TCP_STATE_NOT_CONNECTED;
+ aux_tcp->state.index = 0;
+ aux_tcp->state.ack_modified = 0;
+ for (i = 0; i < N_LINK_TCP_DATA; i++)
+ aux_tcp->ack[i].active = 0;
+ aux_tcp->fwhole = -1;
+ lnk->data.tcp = aux_tcp;
+ } else {
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr, "PacketAlias/AddLink: ");
+ fprintf(stderr, " cannot allocate auxiliary TCP data\n");
+#endif
+ free(lnk);
+ return (NULL);
+ }
+ break;
+ case LINK_PPTP:
+ la->pptpLinkCount++;
+ break;
+ case LINK_FRAGMENT_ID:
+ la->fragmentIdLinkCount++;
+ break;
+ case LINK_FRAGMENT_PTR:
+ la->fragmentPtrLinkCount++;
+ break;
+ case LINK_ADDR:
+ break;
+ default:
+ la->protoLinkCount++;
+ break;
+ }
+
+ /* Set up pointers for output lookup table */
+ start_point = StartPointOut(src_addr, dst_addr,
+ src_port, dst_port, link_type);
+ LIST_INSERT_HEAD(&la->linkTableOut[start_point], lnk, list_out);
+
+ /* Set up pointers for input lookup table */
+ start_point = StartPointIn(alias_addr, lnk->alias_port, link_type);
+ LIST_INSERT_HEAD(&la->linkTableIn[start_point], lnk, list_in);
+ } else {
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr, "PacketAlias/AddLink(): ");
+ fprintf(stderr, "malloc() call failed.\n");
+#endif
+ }
+ if (la->packetAliasMode & PKT_ALIAS_LOG) {
+ ShowAliasStats(la);
+ }
+ return (lnk);
+}
+
+static struct alias_link *
+ReLink(struct alias_link *old_lnk,
+ struct in_addr src_addr,
+ struct in_addr dst_addr,
+ struct in_addr alias_addr,
+ u_short src_port,
+ u_short dst_port,
+ int alias_port_param, /* if less than zero, alias */
+ int link_type)
+{ /* port will be automatically *//* chosen.
+ * If greater than */
+ struct alias_link *new_lnk; /* zero, equal to alias port */
+ struct libalias *la = old_lnk->la;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ new_lnk = AddLink(la, src_addr, dst_addr, alias_addr,
+ src_port, dst_port, alias_port_param,
+ link_type);
+#ifndef NO_FW_PUNCH
+ if (new_lnk != NULL &&
+ old_lnk->link_type == LINK_TCP &&
+ old_lnk->data.tcp->fwhole > 0) {
+ PunchFWHole(new_lnk);
+ }
+#endif
+ DeleteLink(old_lnk);
+ return (new_lnk);
+}
+
+static struct alias_link *
+_FindLinkOut(struct libalias *la, struct in_addr src_addr,
+ struct in_addr dst_addr,
+ u_short src_port,
+ u_short dst_port,
+ int link_type,
+ int replace_partial_links)
+{
+ u_int i;
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ i = StartPointOut(src_addr, dst_addr, src_port, dst_port, link_type);
+ LIST_FOREACH(lnk, &la->linkTableOut[i], list_out) {
+ if (lnk->dst_addr.s_addr == dst_addr.s_addr &&
+ lnk->src_addr.s_addr == src_addr.s_addr &&
+ lnk->src_port == src_port &&
+ lnk->dst_port == dst_port &&
+ lnk->link_type == link_type &&
+ lnk->server == NULL) {
+ lnk->timestamp = la->timeStamp;
+ break;
+ }
+ }
+
+/* Search for partially specified links. */
+ if (lnk == NULL && replace_partial_links) {
+ if (dst_port != 0 && dst_addr.s_addr != INADDR_ANY) {
+ lnk = _FindLinkOut(la, src_addr, dst_addr, src_port, 0,
+ link_type, 0);
+ if (lnk == NULL)
+ lnk = _FindLinkOut(la, src_addr, la->nullAddress, src_port,
+ dst_port, link_type, 0);
+ }
+ if (lnk == NULL &&
+ (dst_port != 0 || dst_addr.s_addr != INADDR_ANY)) {
+ lnk = _FindLinkOut(la, src_addr, la->nullAddress, src_port, 0,
+ link_type, 0);
+ }
+ if (lnk != NULL) {
+ lnk = ReLink(lnk,
+ src_addr, dst_addr, lnk->alias_addr,
+ src_port, dst_port, lnk->alias_port,
+ link_type);
+ }
+ }
+ return (lnk);
+}
+
+static struct alias_link *
+FindLinkOut(struct libalias *la, struct in_addr src_addr,
+ struct in_addr dst_addr,
+ u_short src_port,
+ u_short dst_port,
+ int link_type,
+ int replace_partial_links)
+{
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ lnk = _FindLinkOut(la, src_addr, dst_addr, src_port, dst_port,
+ link_type, replace_partial_links);
+
+ if (lnk == NULL) {
+ /*
+ * The following allows permanent links to be specified as
+ * using the default source address (i.e. device interface
+ * address) without knowing in advance what that address
+ * is.
+ */
+ if (la->aliasAddress.s_addr != INADDR_ANY &&
+ src_addr.s_addr == la->aliasAddress.s_addr) {
+ lnk = _FindLinkOut(la, la->nullAddress, dst_addr, src_port, dst_port,
+ link_type, replace_partial_links);
+ }
+ }
+ return (lnk);
+}
+
+
+static struct alias_link *
+_FindLinkIn(struct libalias *la, struct in_addr dst_addr,
+ struct in_addr alias_addr,
+ u_short dst_port,
+ u_short alias_port,
+ int link_type,
+ int replace_partial_links)
+{
+ int flags_in;
+ u_int start_point;
+ struct alias_link *lnk;
+ struct alias_link *lnk_fully_specified;
+ struct alias_link *lnk_unknown_all;
+ struct alias_link *lnk_unknown_dst_addr;
+ struct alias_link *lnk_unknown_dst_port;
+
+ LIBALIAS_LOCK_ASSERT(la);
+/* Initialize pointers */
+ lnk_fully_specified = NULL;
+ lnk_unknown_all = NULL;
+ lnk_unknown_dst_addr = NULL;
+ lnk_unknown_dst_port = NULL;
+
+/* If either the dest addr or port is unknown, the search
+ loop will have to know about this. */
+
+ flags_in = 0;
+ if (dst_addr.s_addr == INADDR_ANY)
+ flags_in |= LINK_UNKNOWN_DEST_ADDR;
+ if (dst_port == 0)
+ flags_in |= LINK_UNKNOWN_DEST_PORT;
+
+/* Search loop */
+ start_point = StartPointIn(alias_addr, alias_port, link_type);
+ LIST_FOREACH(lnk, &la->linkTableIn[start_point], list_in) {
+ int flags;
+
+ flags = flags_in | lnk->flags;
+ if (!(flags & LINK_PARTIALLY_SPECIFIED)) {
+ if (lnk->alias_addr.s_addr == alias_addr.s_addr
+ && lnk->alias_port == alias_port
+ && lnk->dst_addr.s_addr == dst_addr.s_addr
+ && lnk->dst_port == dst_port
+ && lnk->link_type == link_type) {
+ lnk_fully_specified = lnk;
+ break;
+ }
+ } else if ((flags & LINK_UNKNOWN_DEST_ADDR)
+ && (flags & LINK_UNKNOWN_DEST_PORT)) {
+ if (lnk->alias_addr.s_addr == alias_addr.s_addr
+ && lnk->alias_port == alias_port
+ && lnk->link_type == link_type) {
+ if (lnk_unknown_all == NULL)
+ lnk_unknown_all = lnk;
+ }
+ } else if (flags & LINK_UNKNOWN_DEST_ADDR) {
+ if (lnk->alias_addr.s_addr == alias_addr.s_addr
+ && lnk->alias_port == alias_port
+ && lnk->link_type == link_type
+ && lnk->dst_port == dst_port) {
+ if (lnk_unknown_dst_addr == NULL)
+ lnk_unknown_dst_addr = lnk;
+ }
+ } else if (flags & LINK_UNKNOWN_DEST_PORT) {
+ if (lnk->alias_addr.s_addr == alias_addr.s_addr
+ && lnk->alias_port == alias_port
+ && lnk->link_type == link_type
+ && lnk->dst_addr.s_addr == dst_addr.s_addr) {
+ if (lnk_unknown_dst_port == NULL)
+ lnk_unknown_dst_port = lnk;
+ }
+ }
+ }
+
+
+
+ if (lnk_fully_specified != NULL) {
+ lnk_fully_specified->timestamp = la->timeStamp;
+ lnk = lnk_fully_specified;
+ } else if (lnk_unknown_dst_port != NULL)
+ lnk = lnk_unknown_dst_port;
+ else if (lnk_unknown_dst_addr != NULL)
+ lnk = lnk_unknown_dst_addr;
+ else if (lnk_unknown_all != NULL)
+ lnk = lnk_unknown_all;
+ else
+ return (NULL);
+
+ if (replace_partial_links &&
+ (lnk->flags & LINK_PARTIALLY_SPECIFIED || lnk->server != NULL)) {
+ struct in_addr src_addr;
+ u_short src_port;
+
+ if (lnk->server != NULL) { /* LSNAT link */
+ src_addr = lnk->server->addr;
+ src_port = lnk->server->port;
+ lnk->server = lnk->server->next;
+ } else {
+ src_addr = lnk->src_addr;
+ src_port = lnk->src_port;
+ }
+
+ lnk = ReLink(lnk,
+ src_addr, dst_addr, alias_addr,
+ src_port, dst_port, alias_port,
+ link_type);
+ }
+ return (lnk);
+}
+
+static struct alias_link *
+FindLinkIn(struct libalias *la, struct in_addr dst_addr,
+ struct in_addr alias_addr,
+ u_short dst_port,
+ u_short alias_port,
+ int link_type,
+ int replace_partial_links)
+{
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ lnk = _FindLinkIn(la, dst_addr, alias_addr, dst_port, alias_port,
+ link_type, replace_partial_links);
+
+ if (lnk == NULL) {
+ /*
+ * The following allows permanent links to be specified as
+ * using the default aliasing address (i.e. device
+ * interface address) without knowing in advance what that
+ * address is.
+ */
+ if (la->aliasAddress.s_addr != INADDR_ANY &&
+ alias_addr.s_addr == la->aliasAddress.s_addr) {
+ lnk = _FindLinkIn(la, dst_addr, la->nullAddress, dst_port, alias_port,
+ link_type, replace_partial_links);
+ }
+ }
+ return (lnk);
+}
+
+
+
+
+/* External routines for finding/adding links
+
+-- "external" means outside alias_db.c, but within alias*.c --
+
+ FindIcmpIn(), FindIcmpOut()
+ FindFragmentIn1(), FindFragmentIn2()
+ AddFragmentPtrLink(), FindFragmentPtr()
+ FindProtoIn(), FindProtoOut()
+ FindUdpTcpIn(), FindUdpTcpOut()
+ AddPptp(), FindPptpOutByCallId(), FindPptpInByCallId(),
+ FindPptpOutByPeerCallId(), FindPptpInByPeerCallId()
+ FindOriginalAddress(), FindAliasAddress()
+
+(prototypes in alias_local.h)
+*/
+
+
+struct alias_link *
+FindIcmpIn(struct libalias *la, struct in_addr dst_addr,
+ struct in_addr alias_addr,
+ u_short id_alias,
+ int create)
+{
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ lnk = FindLinkIn(la, dst_addr, alias_addr,
+ NO_DEST_PORT, id_alias,
+ LINK_ICMP, 0);
+ if (lnk == NULL && create && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) {
+ struct in_addr target_addr;
+
+ target_addr = FindOriginalAddress(la, alias_addr);
+ lnk = AddLink(la, target_addr, dst_addr, alias_addr,
+ id_alias, NO_DEST_PORT, id_alias,
+ LINK_ICMP);
+ }
+ return (lnk);
+}
+
+
+struct alias_link *
+FindIcmpOut(struct libalias *la, struct in_addr src_addr,
+ struct in_addr dst_addr,
+ u_short id,
+ int create)
+{
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ lnk = FindLinkOut(la, src_addr, dst_addr,
+ id, NO_DEST_PORT,
+ LINK_ICMP, 0);
+ if (lnk == NULL && create) {
+ struct in_addr alias_addr;
+
+ alias_addr = FindAliasAddress(la, src_addr);
+ lnk = AddLink(la, src_addr, dst_addr, alias_addr,
+ id, NO_DEST_PORT, GET_ALIAS_ID,
+ LINK_ICMP);
+ }
+ return (lnk);
+}
+
+
+struct alias_link *
+FindFragmentIn1(struct libalias *la, struct in_addr dst_addr,
+ struct in_addr alias_addr,
+ u_short ip_id)
+{
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ lnk = FindLinkIn(la, dst_addr, alias_addr,
+ NO_DEST_PORT, ip_id,
+ LINK_FRAGMENT_ID, 0);
+
+ if (lnk == NULL) {
+ lnk = AddLink(la, la->nullAddress, dst_addr, alias_addr,
+ NO_SRC_PORT, NO_DEST_PORT, ip_id,
+ LINK_FRAGMENT_ID);
+ }
+ return (lnk);
+}
+
+
+struct alias_link *
+FindFragmentIn2(struct libalias *la, struct in_addr dst_addr, /* Doesn't add a link if
+ * one */
+ struct in_addr alias_addr, /* is not found. */
+ u_short ip_id)
+{
+
+ LIBALIAS_LOCK_ASSERT(la);
+ return FindLinkIn(la, dst_addr, alias_addr,
+ NO_DEST_PORT, ip_id,
+ LINK_FRAGMENT_ID, 0);
+}
+
+
+struct alias_link *
+AddFragmentPtrLink(struct libalias *la, struct in_addr dst_addr,
+ u_short ip_id)
+{
+
+ LIBALIAS_LOCK_ASSERT(la);
+ return AddLink(la, la->nullAddress, dst_addr, la->nullAddress,
+ NO_SRC_PORT, NO_DEST_PORT, ip_id,
+ LINK_FRAGMENT_PTR);
+}
+
+
+struct alias_link *
+FindFragmentPtr(struct libalias *la, struct in_addr dst_addr,
+ u_short ip_id)
+{
+
+ LIBALIAS_LOCK_ASSERT(la);
+ return FindLinkIn(la, dst_addr, la->nullAddress,
+ NO_DEST_PORT, ip_id,
+ LINK_FRAGMENT_PTR, 0);
+}
+
+
+struct alias_link *
+FindProtoIn(struct libalias *la, struct in_addr dst_addr,
+ struct in_addr alias_addr,
+ u_char proto)
+{
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ lnk = FindLinkIn(la, dst_addr, alias_addr,
+ NO_DEST_PORT, 0,
+ proto, 1);
+
+ if (lnk == NULL && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) {
+ struct in_addr target_addr;
+
+ target_addr = FindOriginalAddress(la, alias_addr);
+ lnk = AddLink(la, target_addr, dst_addr, alias_addr,
+ NO_SRC_PORT, NO_DEST_PORT, 0,
+ proto);
+ }
+ return (lnk);
+}
+
+
+struct alias_link *
+FindProtoOut(struct libalias *la, struct in_addr src_addr,
+ struct in_addr dst_addr,
+ u_char proto)
+{
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ lnk = FindLinkOut(la, src_addr, dst_addr,
+ NO_SRC_PORT, NO_DEST_PORT,
+ proto, 1);
+
+ if (lnk == NULL) {
+ struct in_addr alias_addr;
+
+ alias_addr = FindAliasAddress(la, src_addr);
+ lnk = AddLink(la, src_addr, dst_addr, alias_addr,
+ NO_SRC_PORT, NO_DEST_PORT, 0,
+ proto);
+ }
+ return (lnk);
+}
+
+
+struct alias_link *
+FindUdpTcpIn(struct libalias *la, struct in_addr dst_addr,
+ struct in_addr alias_addr,
+ u_short dst_port,
+ u_short alias_port,
+ u_char proto,
+ int create)
+{
+ int link_type;
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ switch (proto) {
+ case IPPROTO_UDP:
+ link_type = LINK_UDP;
+ break;
+ case IPPROTO_TCP:
+ link_type = LINK_TCP;
+ break;
+ default:
+ return (NULL);
+ break;
+ }
+
+ lnk = FindLinkIn(la, dst_addr, alias_addr,
+ dst_port, alias_port,
+ link_type, create);
+
+ if (lnk == NULL && create && !(la->packetAliasMode & PKT_ALIAS_DENY_INCOMING)) {
+ struct in_addr target_addr;
+
+ target_addr = FindOriginalAddress(la, alias_addr);
+ lnk = AddLink(la, target_addr, dst_addr, alias_addr,
+ alias_port, dst_port, alias_port,
+ link_type);
+ }
+ return (lnk);
+}
+
+
+struct alias_link *
+FindUdpTcpOut(struct libalias *la, struct in_addr src_addr,
+ struct in_addr dst_addr,
+ u_short src_port,
+ u_short dst_port,
+ u_char proto,
+ int create)
+{
+ int link_type;
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ switch (proto) {
+ case IPPROTO_UDP:
+ link_type = LINK_UDP;
+ break;
+ case IPPROTO_TCP:
+ link_type = LINK_TCP;
+ break;
+ default:
+ return (NULL);
+ break;
+ }
+
+ lnk = FindLinkOut(la, src_addr, dst_addr, src_port, dst_port, link_type, create);
+
+ if (lnk == NULL && create) {
+ struct in_addr alias_addr;
+
+ alias_addr = FindAliasAddress(la, src_addr);
+ lnk = AddLink(la, src_addr, dst_addr, alias_addr,
+ src_port, dst_port, GET_ALIAS_PORT,
+ link_type);
+ }
+ return (lnk);
+}
+
+
+struct alias_link *
+AddPptp(struct libalias *la, struct in_addr src_addr,
+ struct in_addr dst_addr,
+ struct in_addr alias_addr,
+ u_int16_t src_call_id)
+{
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ lnk = AddLink(la, src_addr, dst_addr, alias_addr,
+ src_call_id, 0, GET_ALIAS_PORT,
+ LINK_PPTP);
+
+ return (lnk);
+}
+
+
+struct alias_link *
+FindPptpOutByCallId(struct libalias *la, struct in_addr src_addr,
+ struct in_addr dst_addr,
+ u_int16_t src_call_id)
+{
+ u_int i;
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP);
+ LIST_FOREACH(lnk, &la->linkTableOut[i], list_out)
+ if (lnk->link_type == LINK_PPTP &&
+ lnk->src_addr.s_addr == src_addr.s_addr &&
+ lnk->dst_addr.s_addr == dst_addr.s_addr &&
+ lnk->src_port == src_call_id)
+ break;
+
+ return (lnk);
+}
+
+
+struct alias_link *
+FindPptpOutByPeerCallId(struct libalias *la, struct in_addr src_addr,
+ struct in_addr dst_addr,
+ u_int16_t dst_call_id)
+{
+ u_int i;
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ i = StartPointOut(src_addr, dst_addr, 0, 0, LINK_PPTP);
+ LIST_FOREACH(lnk, &la->linkTableOut[i], list_out)
+ if (lnk->link_type == LINK_PPTP &&
+ lnk->src_addr.s_addr == src_addr.s_addr &&
+ lnk->dst_addr.s_addr == dst_addr.s_addr &&
+ lnk->dst_port == dst_call_id)
+ break;
+
+ return (lnk);
+}
+
+
+struct alias_link *
+FindPptpInByCallId(struct libalias *la, struct in_addr dst_addr,
+ struct in_addr alias_addr,
+ u_int16_t dst_call_id)
+{
+ u_int i;
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ i = StartPointIn(alias_addr, 0, LINK_PPTP);
+ LIST_FOREACH(lnk, &la->linkTableIn[i], list_in)
+ if (lnk->link_type == LINK_PPTP &&
+ lnk->dst_addr.s_addr == dst_addr.s_addr &&
+ lnk->alias_addr.s_addr == alias_addr.s_addr &&
+ lnk->dst_port == dst_call_id)
+ break;
+
+ return (lnk);
+}
+
+
+struct alias_link *
+FindPptpInByPeerCallId(struct libalias *la, struct in_addr dst_addr,
+ struct in_addr alias_addr,
+ u_int16_t alias_call_id)
+{
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ lnk = FindLinkIn(la, dst_addr, alias_addr,
+ 0 /* any */ , alias_call_id,
+ LINK_PPTP, 0);
+
+
+ return (lnk);
+}
+
+
+struct alias_link *
+FindRtspOut(struct libalias *la, struct in_addr src_addr,
+ struct in_addr dst_addr,
+ u_short src_port,
+ u_short alias_port,
+ u_char proto)
+{
+ int link_type;
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ switch (proto) {
+ case IPPROTO_UDP:
+ link_type = LINK_UDP;
+ break;
+ case IPPROTO_TCP:
+ link_type = LINK_TCP;
+ break;
+ default:
+ return (NULL);
+ break;
+ }
+
+ lnk = FindLinkOut(la, src_addr, dst_addr, src_port, 0, link_type, 1);
+
+ if (lnk == NULL) {
+ struct in_addr alias_addr;
+
+ alias_addr = FindAliasAddress(la, src_addr);
+ lnk = AddLink(la, src_addr, dst_addr, alias_addr,
+ src_port, 0, alias_port,
+ link_type);
+ }
+ return (lnk);
+}
+
+
+struct in_addr
+FindOriginalAddress(struct libalias *la, struct in_addr alias_addr)
+{
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ lnk = FindLinkIn(la, la->nullAddress, alias_addr,
+ 0, 0, LINK_ADDR, 0);
+ if (lnk == NULL) {
+ la->newDefaultLink = 1;
+ if (la->targetAddress.s_addr == INADDR_ANY)
+ return (alias_addr);
+ else if (la->targetAddress.s_addr == INADDR_NONE)
+ return (la->aliasAddress.s_addr != INADDR_ANY) ?
+ la->aliasAddress : alias_addr;
+ else
+ return (la->targetAddress);
+ } else {
+ if (lnk->server != NULL) { /* LSNAT link */
+ struct in_addr src_addr;
+
+ src_addr = lnk->server->addr;
+ lnk->server = lnk->server->next;
+ return (src_addr);
+ } else if (lnk->src_addr.s_addr == INADDR_ANY)
+ return (la->aliasAddress.s_addr != INADDR_ANY) ?
+ la->aliasAddress : alias_addr;
+ else
+ return (lnk->src_addr);
+ }
+}
+
+
+struct in_addr
+FindAliasAddress(struct libalias *la, struct in_addr original_addr)
+{
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ lnk = FindLinkOut(la, original_addr, la->nullAddress,
+ 0, 0, LINK_ADDR, 0);
+ if (lnk == NULL) {
+ return (la->aliasAddress.s_addr != INADDR_ANY) ?
+ la->aliasAddress : original_addr;
+ } else {
+ if (lnk->alias_addr.s_addr == INADDR_ANY)
+ return (la->aliasAddress.s_addr != INADDR_ANY) ?
+ la->aliasAddress : original_addr;
+ else
+ return (lnk->alias_addr);
+ }
+}
+
+
+/* External routines for getting or changing link data
+ (external to alias_db.c, but internal to alias*.c)
+
+ SetFragmentData(), GetFragmentData()
+ SetFragmentPtr(), GetFragmentPtr()
+ SetStateIn(), SetStateOut(), GetStateIn(), GetStateOut()
+ GetOriginalAddress(), GetDestAddress(), GetAliasAddress()
+ GetOriginalPort(), GetAliasPort()
+ SetAckModified(), GetAckModified()
+ GetDeltaAckIn(), GetDeltaSeqOut(), AddSeq()
+ SetProtocolFlags(), GetProtocolFlags()
+ SetDestCallId()
+*/
+
+
+void
+SetFragmentAddr(struct alias_link *lnk, struct in_addr src_addr)
+{
+ lnk->data.frag_addr = src_addr;
+}
+
+
+void
+GetFragmentAddr(struct alias_link *lnk, struct in_addr *src_addr)
+{
+ *src_addr = lnk->data.frag_addr;
+}
+
+
+void
+SetFragmentPtr(struct alias_link *lnk, char *fptr)
+{
+ lnk->data.frag_ptr = fptr;
+}
+
+
+void
+GetFragmentPtr(struct alias_link *lnk, char **fptr)
+{
+ *fptr = lnk->data.frag_ptr;
+}
+
+
+void
+SetStateIn(struct alias_link *lnk, int state)
+{
+ /* TCP input state */
+ switch (state) {
+ case ALIAS_TCP_STATE_DISCONNECTED:
+ if (lnk->data.tcp->state.out != ALIAS_TCP_STATE_CONNECTED)
+ lnk->expire_time = TCP_EXPIRE_DEAD;
+ else
+ lnk->expire_time = TCP_EXPIRE_SINGLEDEAD;
+ break;
+ case ALIAS_TCP_STATE_CONNECTED:
+ if (lnk->data.tcp->state.out == ALIAS_TCP_STATE_CONNECTED)
+ lnk->expire_time = TCP_EXPIRE_CONNECTED;
+ break;
+ default:
+#ifdef _KERNEL
+ panic("libalias:SetStateIn() unknown state");
+#else
+ abort();
+#endif
+ }
+ lnk->data.tcp->state.in = state;
+}
+
+
+void
+SetStateOut(struct alias_link *lnk, int state)
+{
+ /* TCP output state */
+ switch (state) {
+ case ALIAS_TCP_STATE_DISCONNECTED:
+ if (lnk->data.tcp->state.in != ALIAS_TCP_STATE_CONNECTED)
+ lnk->expire_time = TCP_EXPIRE_DEAD;
+ else
+ lnk->expire_time = TCP_EXPIRE_SINGLEDEAD;
+ break;
+ case ALIAS_TCP_STATE_CONNECTED:
+ if (lnk->data.tcp->state.in == ALIAS_TCP_STATE_CONNECTED)
+ lnk->expire_time = TCP_EXPIRE_CONNECTED;
+ break;
+ default:
+#ifdef _KERNEL
+ panic("libalias:SetStateOut() unknown state");
+#else
+ abort();
+#endif
+ }
+ lnk->data.tcp->state.out = state;
+}
+
+
+int
+GetStateIn(struct alias_link *lnk)
+{
+ /* TCP input state */
+ return (lnk->data.tcp->state.in);
+}
+
+
+int
+GetStateOut(struct alias_link *lnk)
+{
+ /* TCP output state */
+ return (lnk->data.tcp->state.out);
+}
+
+
+struct in_addr
+GetOriginalAddress(struct alias_link *lnk)
+{
+ if (lnk->src_addr.s_addr == INADDR_ANY)
+ return (lnk->la->aliasAddress);
+ else
+ return (lnk->src_addr);
+}
+
+
+struct in_addr
+GetDestAddress(struct alias_link *lnk)
+{
+ return (lnk->dst_addr);
+}
+
+
+struct in_addr
+GetAliasAddress(struct alias_link *lnk)
+{
+ if (lnk->alias_addr.s_addr == INADDR_ANY)
+ return (lnk->la->aliasAddress);
+ else
+ return (lnk->alias_addr);
+}
+
+
+struct in_addr
+GetDefaultAliasAddress(struct libalias *la)
+{
+
+ LIBALIAS_LOCK_ASSERT(la);
+ return (la->aliasAddress);
+}
+
+
+void
+SetDefaultAliasAddress(struct libalias *la, struct in_addr alias_addr)
+{
+
+ LIBALIAS_LOCK_ASSERT(la);
+ la->aliasAddress = alias_addr;
+}
+
+
+u_short
+GetOriginalPort(struct alias_link *lnk)
+{
+ return (lnk->src_port);
+}
+
+
+u_short
+GetAliasPort(struct alias_link *lnk)
+{
+ return (lnk->alias_port);
+}
+
+#ifndef NO_FW_PUNCH
+static u_short
+GetDestPort(struct alias_link *lnk)
+{
+ return (lnk->dst_port);
+}
+
+#endif
+
+void
+SetAckModified(struct alias_link *lnk)
+{
+/* Indicate that ACK numbers have been modified in a TCP connection */
+ lnk->data.tcp->state.ack_modified = 1;
+}
+
+
+struct in_addr
+GetProxyAddress(struct alias_link *lnk)
+{
+ return (lnk->proxy_addr);
+}
+
+
+void
+SetProxyAddress(struct alias_link *lnk, struct in_addr addr)
+{
+ lnk->proxy_addr = addr;
+}
+
+
+u_short
+GetProxyPort(struct alias_link *lnk)
+{
+ return (lnk->proxy_port);
+}
+
+
+void
+SetProxyPort(struct alias_link *lnk, u_short port)
+{
+ lnk->proxy_port = port;
+}
+
+
+int
+GetAckModified(struct alias_link *lnk)
+{
+/* See if ACK numbers have been modified */
+ return (lnk->data.tcp->state.ack_modified);
+}
+
+
+int
+GetDeltaAckIn(struct ip *pip, struct alias_link *lnk)
+{
+/*
+Find out how much the ACK number has been altered for an incoming
+TCP packet. To do this, a circular list of ACK numbers where the TCP
+packet size was altered is searched.
+*/
+
+ int i;
+ struct tcphdr *tc;
+ int delta, ack_diff_min;
+ u_long ack;
+
+ tc = ip_next(pip);
+ ack = tc->th_ack;
+
+ delta = 0;
+ ack_diff_min = -1;
+ for (i = 0; i < N_LINK_TCP_DATA; i++) {
+ struct ack_data_record x;
+
+ x = lnk->data.tcp->ack[i];
+ if (x.active == 1) {
+ int ack_diff;
+
+ ack_diff = SeqDiff(x.ack_new, ack);
+ if (ack_diff >= 0) {
+ if (ack_diff_min >= 0) {
+ if (ack_diff < ack_diff_min) {
+ delta = x.delta;
+ ack_diff_min = ack_diff;
+ }
+ } else {
+ delta = x.delta;
+ ack_diff_min = ack_diff;
+ }
+ }
+ }
+ }
+ return (delta);
+}
+
+
+int
+GetDeltaSeqOut(struct ip *pip, struct alias_link *lnk)
+{
+/*
+Find out how much the sequence number has been altered for an outgoing
+TCP packet. To do this, a circular list of ACK numbers where the TCP
+packet size was altered is searched.
+*/
+
+ int i;
+ struct tcphdr *tc;
+ int delta, seq_diff_min;
+ u_long seq;
+
+ tc = ip_next(pip);
+ seq = tc->th_seq;
+
+ delta = 0;
+ seq_diff_min = -1;
+ for (i = 0; i < N_LINK_TCP_DATA; i++) {
+ struct ack_data_record x;
+
+ x = lnk->data.tcp->ack[i];
+ if (x.active == 1) {
+ int seq_diff;
+
+ seq_diff = SeqDiff(x.ack_old, seq);
+ if (seq_diff >= 0) {
+ if (seq_diff_min >= 0) {
+ if (seq_diff < seq_diff_min) {
+ delta = x.delta;
+ seq_diff_min = seq_diff;
+ }
+ } else {
+ delta = x.delta;
+ seq_diff_min = seq_diff;
+ }
+ }
+ }
+ }
+ return (delta);
+}
+
+
+void
+AddSeq(struct ip *pip, struct alias_link *lnk, int delta)
+{
+/*
+When a TCP packet has been altered in length, save this
+information in a circular list. If enough packets have
+been altered, then this list will begin to overwrite itself.
+*/
+
+ struct tcphdr *tc;
+ struct ack_data_record x;
+ int hlen, tlen, dlen;
+ int i;
+
+ tc = ip_next(pip);
+
+ hlen = (pip->ip_hl + tc->th_off) << 2;
+ tlen = ntohs(pip->ip_len);
+ dlen = tlen - hlen;
+
+ x.ack_old = htonl(ntohl(tc->th_seq) + dlen);
+ x.ack_new = htonl(ntohl(tc->th_seq) + dlen + delta);
+ x.delta = delta;
+ x.active = 1;
+
+ i = lnk->data.tcp->state.index;
+ lnk->data.tcp->ack[i] = x;
+
+ i++;
+ if (i == N_LINK_TCP_DATA)
+ lnk->data.tcp->state.index = 0;
+ else
+ lnk->data.tcp->state.index = i;
+}
+
+void
+SetExpire(struct alias_link *lnk, int expire)
+{
+ if (expire == 0) {
+ lnk->flags &= ~LINK_PERMANENT;
+ DeleteLink(lnk);
+ } else if (expire == -1) {
+ lnk->flags |= LINK_PERMANENT;
+ } else if (expire > 0) {
+ lnk->expire_time = expire;
+ } else {
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr, "PacketAlias/SetExpire(): ");
+ fprintf(stderr, "error in expire parameter\n");
+#endif
+ }
+}
+
+void
+ClearCheckNewLink(struct libalias *la)
+{
+
+ LIBALIAS_LOCK_ASSERT(la);
+ la->newDefaultLink = 0;
+}
+
+void
+SetProtocolFlags(struct alias_link *lnk, int pflags)
+{
+
+ lnk->pflags = pflags;;
+}
+
+int
+GetProtocolFlags(struct alias_link *lnk)
+{
+
+ return (lnk->pflags);
+}
+
+void
+SetDestCallId(struct alias_link *lnk, u_int16_t cid)
+{
+ struct libalias *la = lnk->la;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ la->deleteAllLinks = 1;
+ ReLink(lnk, lnk->src_addr, lnk->dst_addr, lnk->alias_addr,
+ lnk->src_port, cid, lnk->alias_port, lnk->link_type);
+ la->deleteAllLinks = 0;
+}
+
+
+/* Miscellaneous Functions
+
+ HouseKeeping()
+ InitPacketAliasLog()
+ UninitPacketAliasLog()
+*/
+
+/*
+ Whenever an outgoing or incoming packet is handled, HouseKeeping()
+ is called to find and remove timed-out aliasing links. Logic exists
+ to sweep through the entire table and linked list structure
+ every 60 seconds.
+
+ (prototype in alias_local.h)
+*/
+
+void
+HouseKeeping(struct libalias *la)
+{
+ int i, n;
+#ifndef VBOX
+#ifndef _KERNEL
+ struct timeval tv;
+ struct timezone tz;
+#endif
+#endif /* !VBOX */
+
+ LIBALIAS_LOCK_ASSERT(la);
+ /*
+ * Save system time (seconds) in global variable timeStamp for use
+ * by other functions. This is done so as not to unnecessarily
+ * waste timeline by making system calls.
+ */
+#ifndef VBOX
+#ifdef _KERNEL
+ la->timeStamp = time_uptime;
+#else
+ gettimeofday(&tv, &tz);
+ la->timeStamp = tv.tv_sec;
+#endif
+#else /* VBOX */
+ la->timeStamp = la->curtime / 1000; /* NB: la->pData->curtime (msec) */
+#endif
+
+ /* Compute number of spokes (output table link chains) to cover */
+ n = LINK_TABLE_OUT_SIZE * (la->timeStamp - la->lastCleanupTime);
+ n /= ALIAS_CLEANUP_INTERVAL_SECS;
+
+ /* Handle different cases */
+ if (n > 0) {
+ if (n > ALIAS_CLEANUP_MAX_SPOKES)
+ n = ALIAS_CLEANUP_MAX_SPOKES;
+ la->lastCleanupTime = la->timeStamp;
+ for (i = 0; i < n; i++)
+ IncrementalCleanup(la);
+ } else if (n < 0) {
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr, "PacketAlias/HouseKeeping(): ");
+ fprintf(stderr, "something unexpected in time values\n");
+#endif
+ la->lastCleanupTime = la->timeStamp;
+ }
+}
+
+/* Init the log file and enable logging */
+static int
+InitPacketAliasLog(struct libalias *la)
+{
+
+ LIBALIAS_LOCK_ASSERT(la);
+ if (~la->packetAliasMode & PKT_ALIAS_LOG) {
+#ifndef VBOX
+#ifdef _KERNEL
+ if ((la->logDesc = malloc(LIBALIAS_BUF_SIZE)))
+ ;
+#else
+ if ((la->logDesc = fopen("/var/log/alias.log", "w")))
+ fprintf(la->logDesc, "PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n");
+#endif
+ else
+ return (ENOMEM); /* log initialization failed */
+#else
+ Log2(("NAT: PacketAlias/InitPacketAliasLog: Packet alias logging enabled.\n"));
+ la->logDesc = (void *)(uintptr_t)1; /* XXX: in vbox we don't use this param */
+#endif
+ la->packetAliasMode |= PKT_ALIAS_LOG;
+ }
+
+ return (1);
+}
+
+/* Close the log-file and disable logging. */
+static void
+UninitPacketAliasLog(struct libalias *la)
+{
+
+ LIBALIAS_LOCK_ASSERT(la);
+ if (la->logDesc) {
+#ifndef VBOX
+#ifdef _KERNEL
+ free(la->logDesc);
+#else
+ fclose(la->logDesc);
+#endif
+#endif /* !VBOX */
+ la->logDesc = NULL;
+ }
+ la->packetAliasMode &= ~PKT_ALIAS_LOG;
+}
+
+/* Outside world interfaces
+
+-- "outside world" means other than alias*.c routines --
+
+ PacketAliasRedirectPort()
+ PacketAliasAddServer()
+ PacketAliasRedirectProto()
+ PacketAliasRedirectAddr()
+ PacketAliasRedirectDynamic()
+ PacketAliasRedirectDelete()
+ PacketAliasSetAddress()
+ PacketAliasInit()
+ PacketAliasUninit()
+ PacketAliasSetMode()
+
+(prototypes in alias.h)
+*/
+
+/* Redirection from a specific public addr:port to a
+ private addr:port */
+struct alias_link *
+LibAliasRedirectPort(struct libalias *la, struct in_addr src_addr, u_short src_port,
+ struct in_addr dst_addr, u_short dst_port,
+ struct in_addr alias_addr, u_short alias_port,
+ u_char proto)
+{
+ int link_type;
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK(la);
+ switch (proto) {
+ case IPPROTO_UDP:
+ link_type = LINK_UDP;
+ break;
+ case IPPROTO_TCP:
+ link_type = LINK_TCP;
+ break;
+ default:
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr, "PacketAliasRedirectPort(): ");
+ fprintf(stderr, "only TCP and UDP protocols allowed\n");
+#endif
+ lnk = NULL;
+ goto getout;
+ }
+
+ lnk = AddLink(la, src_addr, dst_addr, alias_addr,
+ src_port, dst_port, alias_port,
+ link_type);
+
+ if (lnk != NULL) {
+ lnk->flags |= LINK_PERMANENT;
+ }
+#ifdef LIBALIAS_DEBUG
+ else {
+ fprintf(stderr, "PacketAliasRedirectPort(): "
+ "call to AddLink() failed\n");
+ }
+#endif
+
+getout:
+ LIBALIAS_UNLOCK(la);
+ return (lnk);
+}
+
+/* Add server to the pool of servers */
+int
+LibAliasAddServer(struct libalias *la, struct alias_link *lnk, struct in_addr addr, u_short port)
+{
+ struct server *server;
+ int res;
+
+ LIBALIAS_LOCK(la);
+ (void)la;
+
+ server = malloc(sizeof(struct server));
+
+ if (server != NULL) {
+ struct server *head;
+
+ server->addr = addr;
+ server->port = port;
+
+ head = lnk->server;
+ if (head == NULL)
+ server->next = server;
+ else {
+ struct server *s;
+
+ for (s = head; s->next != head; s = s->next);
+ s->next = server;
+ server->next = head;
+ }
+ lnk->server = server;
+ res = 0;
+ } else
+ res = -1;
+
+ LIBALIAS_UNLOCK(la);
+ return (res);
+}
+
+/* Redirect packets of a given IP protocol from a specific
+ public address to a private address */
+struct alias_link *
+LibAliasRedirectProto(struct libalias *la, struct in_addr src_addr,
+ struct in_addr dst_addr,
+ struct in_addr alias_addr,
+ u_char proto)
+{
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK(la);
+ lnk = AddLink(la, src_addr, dst_addr, alias_addr,
+ NO_SRC_PORT, NO_DEST_PORT, 0,
+ proto);
+
+ if (lnk != NULL) {
+ lnk->flags |= LINK_PERMANENT;
+ }
+#ifdef LIBALIAS_DEBUG
+ else {
+ fprintf(stderr, "PacketAliasRedirectProto(): "
+ "call to AddLink() failed\n");
+ }
+#endif
+
+ LIBALIAS_UNLOCK(la);
+ return (lnk);
+}
+
+/* Static address translation */
+struct alias_link *
+LibAliasRedirectAddr(struct libalias *la, struct in_addr src_addr,
+ struct in_addr alias_addr)
+{
+ struct alias_link *lnk;
+
+ LIBALIAS_LOCK(la);
+ lnk = AddLink(la, src_addr, la->nullAddress, alias_addr,
+ 0, 0, 0,
+ LINK_ADDR);
+
+ if (lnk != NULL) {
+ lnk->flags |= LINK_PERMANENT;
+ }
+#ifdef LIBALIAS_DEBUG
+ else {
+ fprintf(stderr, "PacketAliasRedirectAddr(): "
+ "call to AddLink() failed\n");
+ }
+#endif
+
+ LIBALIAS_UNLOCK(la);
+ return (lnk);
+}
+
+
+/* Mark the aliasing link dynamic */
+int
+LibAliasRedirectDynamic(struct libalias *la, struct alias_link *lnk)
+{
+ int res;
+
+ LIBALIAS_LOCK(la);
+ (void)la;
+
+ if (lnk->flags & LINK_PARTIALLY_SPECIFIED)
+ res = -1;
+ else {
+ lnk->flags &= ~LINK_PERMANENT;
+ res = 0;
+ }
+ LIBALIAS_UNLOCK(la);
+ return (res);
+}
+
+
+void
+LibAliasRedirectDelete(struct libalias *la, struct alias_link *lnk)
+{
+/* This is a dangerous function to put in the API,
+ because an invalid pointer can crash the program. */
+
+ LIBALIAS_LOCK(la);
+ la->deleteAllLinks = 1;
+ DeleteLink(lnk);
+ la->deleteAllLinks = 0;
+ LIBALIAS_UNLOCK(la);
+}
+
+
+void
+LibAliasSetAddress(struct libalias *la, struct in_addr addr)
+{
+
+ LIBALIAS_LOCK(la);
+ if (la->packetAliasMode & PKT_ALIAS_RESET_ON_ADDR_CHANGE
+ && la->aliasAddress.s_addr != addr.s_addr)
+ CleanupAliasData(la);
+
+ la->aliasAddress = addr;
+ LIBALIAS_UNLOCK(la);
+}
+
+
+void
+LibAliasSetTarget(struct libalias *la, struct in_addr target_addr)
+{
+
+ LIBALIAS_LOCK(la);
+ la->targetAddress = target_addr;
+ LIBALIAS_UNLOCK(la);
+}
+
+#ifndef VBOX
+static void
+finishoff(void)
+{
+
+ while (!LIST_EMPTY(&instancehead))
+ LibAliasUninit(LIST_FIRST(&instancehead));
+}
+#endif
+
+struct libalias *
+#ifndef VBOX
+LibAliasInit(struct libalias *la)
+#else
+LibAliasInit(PNATState pData, struct libalias *la)
+#endif
+{
+ int i;
+#ifndef VBOX
+#ifndef _KERNEL
+ struct timeval tv;
+ struct timezone tz;
+#endif
+#endif /* !VBOX */
+
+ if (la == NULL) {
+ la = calloc(sizeof *la, 1);
+ if (la == NULL)
+ return (la);
+
+#ifndef VBOX
+#ifndef _KERNEL /* kernel cleans up on module unload */
+ if (LIST_EMPTY(&instancehead))
+ atexit(finishoff);
+#endif
+#endif /* !VBOX */
+ LIST_INSERT_HEAD(&instancehead, la, instancelist);
+
+#ifndef VBOX
+#ifdef _KERNEL
+ la->timeStamp = time_uptime;
+ la->lastCleanupTime = time_uptime;
+#else
+ gettimeofday(&tv, &tz);
+ la->timeStamp = tv.tv_sec;
+ la->lastCleanupTime = tv.tv_sec;
+#endif
+#else /* VBOX */
+ la->pData = pData;
+ la->timeStamp = la->curtime / 1000; /* NB: la->pData->curtime (msec) */
+ la->lastCleanupTime = la->timeStamp;
+#endif /* VBOX */
+
+ for (i = 0; i < LINK_TABLE_OUT_SIZE; i++)
+ LIST_INIT(&la->linkTableOut[i]);
+ for (i = 0; i < LINK_TABLE_IN_SIZE; i++)
+ LIST_INIT(&la->linkTableIn[i]);
+ LIBALIAS_LOCK_INIT(la);
+ LIBALIAS_LOCK(la);
+ } else {
+ LIBALIAS_LOCK(la);
+ la->deleteAllLinks = 1;
+ CleanupAliasData(la);
+ la->deleteAllLinks = 0;
+ }
+
+ la->aliasAddress.s_addr = INADDR_ANY;
+ la->targetAddress.s_addr = INADDR_ANY;
+
+ la->icmpLinkCount = 0;
+ la->udpLinkCount = 0;
+ la->tcpLinkCount = 0;
+ la->pptpLinkCount = 0;
+ la->protoLinkCount = 0;
+ la->fragmentIdLinkCount = 0;
+ la->fragmentPtrLinkCount = 0;
+ la->sockCount = 0;
+
+ la->cleanupIndex = 0;
+
+ la->packetAliasMode = PKT_ALIAS_SAME_PORTS
+#ifndef NO_USE_SOCKETS
+ | PKT_ALIAS_USE_SOCKETS
+#endif
+ | PKT_ALIAS_RESET_ON_ADDR_CHANGE;
+#ifndef NO_FW_PUNCH
+ la->fireWallFD = -1;
+#endif
+#ifndef _KERNEL
+ LibAliasRefreshModules();
+#endif
+ LIBALIAS_UNLOCK(la);
+ return (la);
+}
+
+void
+LibAliasUninit(struct libalias *la)
+{
+
+ LIBALIAS_LOCK(la);
+ la->deleteAllLinks = 1;
+ CleanupAliasData(la);
+ la->deleteAllLinks = 0;
+ UninitPacketAliasLog(la);
+#ifndef NO_FW_PUNCH
+ UninitPunchFW(la);
+#endif
+ LIST_REMOVE(la, instancelist);
+ LIBALIAS_UNLOCK(la);
+ LIBALIAS_LOCK_DESTROY(la);
+ free(la);
+}
+
+/* Change mode for some operations */
+unsigned int
+LibAliasSetMode(
+ struct libalias *la,
+ unsigned int flags, /* Which state to bring flags to */
+ unsigned int mask /* Mask of which flags to affect (use 0 to
+ * do a probe for flag values) */
+)
+{
+ int res = -1;
+
+ LIBALIAS_LOCK(la);
+/* Enable logging? */
+ if (flags & mask & PKT_ALIAS_LOG) {
+ /* Do the enable */
+ if (InitPacketAliasLog(la) == ENOMEM)
+ goto getout;
+ } else
+/* _Disable_ logging? */
+ if (~flags & mask & PKT_ALIAS_LOG) {
+ UninitPacketAliasLog(la);
+ }
+#ifndef NO_FW_PUNCH
+/* Start punching holes in the firewall? */
+ if (flags & mask & PKT_ALIAS_PUNCH_FW) {
+ InitPunchFW(la);
+ } else
+/* Stop punching holes in the firewall? */
+ if (~flags & mask & PKT_ALIAS_PUNCH_FW) {
+ UninitPunchFW(la);
+ }
+#endif
+
+/* Other flags can be set/cleared without special action */
+ la->packetAliasMode = (flags & mask) | (la->packetAliasMode & ~mask);
+ res = la->packetAliasMode;
+getout:
+ LIBALIAS_UNLOCK(la);
+ return (res);
+}
+
+
+int
+LibAliasCheckNewLink(struct libalias *la)
+{
+ int res;
+
+ LIBALIAS_LOCK(la);
+ res = la->newDefaultLink;
+ LIBALIAS_UNLOCK(la);
+ return (res);
+}
+
+
+#ifndef NO_FW_PUNCH
+
+/*****************
+ Code to support firewall punching. This shouldn't really be in this
+ file, but making variables global is evil too.
+ ****************/
+
+/* Firewall include files */
+#include <net/if.h>
+#include <netinet/ip_fw.h>
+#include <string.h>
+#include <err.h>
+
+/*
+ * helper function, updates the pointer to cmd with the length
+ * of the current command, and also cleans up the first word of
+ * the new command in case it has been clobbered before.
+ */
+static ipfw_insn *
+next_cmd(ipfw_insn * cmd)
+{
+ cmd += F_LEN(cmd);
+ bzero(cmd, sizeof(*cmd));
+ return (cmd);
+}
+
+/*
+ * A function to fill simple commands of size 1.
+ * Existing flags are preserved.
+ */
+static ipfw_insn *
+fill_cmd(ipfw_insn * cmd, enum ipfw_opcodes opcode, int size,
+ int flags, u_int16_t arg)
+{
+ cmd->opcode = opcode;
+ cmd->len = ((cmd->len | flags) & (F_NOT | F_OR)) | (size & F_LEN_MASK);
+ cmd->arg1 = arg;
+ return next_cmd(cmd);
+}
+
+static ipfw_insn *
+fill_ip(ipfw_insn * cmd1, enum ipfw_opcodes opcode, u_int32_t addr)
+{
+ ipfw_insn_ip *cmd = (ipfw_insn_ip *) cmd1;
+
+ cmd->addr.s_addr = addr;
+ return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u32), 0, 0);
+}
+
+static ipfw_insn *
+fill_one_port(ipfw_insn * cmd1, enum ipfw_opcodes opcode, u_int16_t port)
+{
+ ipfw_insn_u16 *cmd = (ipfw_insn_u16 *) cmd1;
+
+ cmd->ports[0] = cmd->ports[1] = port;
+ return fill_cmd(cmd1, opcode, F_INSN_SIZE(ipfw_insn_u16), 0, 0);
+}
+
+static int
+fill_rule(void *buf, int bufsize, int rulenum,
+ enum ipfw_opcodes action, int proto,
+ struct in_addr sa, u_int16_t sp, struct in_addr da, u_int16_t dp)
+{
+ struct ip_fw *rule = (struct ip_fw *)buf;
+ ipfw_insn *cmd = (ipfw_insn *) rule->cmd;
+
+ bzero(buf, bufsize);
+ rule->rulenum = rulenum;
+
+ cmd = fill_cmd(cmd, O_PROTO, F_INSN_SIZE(ipfw_insn), 0, proto);
+ cmd = fill_ip(cmd, O_IP_SRC, sa.s_addr);
+ cmd = fill_one_port(cmd, O_IP_SRCPORT, sp);
+ cmd = fill_ip(cmd, O_IP_DST, da.s_addr);
+ cmd = fill_one_port(cmd, O_IP_DSTPORT, dp);
+
+ rule->act_ofs = (u_int32_t *) cmd - (u_int32_t *) rule->cmd;
+ cmd = fill_cmd(cmd, action, F_INSN_SIZE(ipfw_insn), 0, 0);
+
+ rule->cmd_len = (u_int32_t *) cmd - (u_int32_t *) rule->cmd;
+
+ return ((char *)cmd - (char *)buf);
+}
+
+static void ClearAllFWHoles(struct libalias *la);
+
+
+#define fw_setfield(la, field, num) \
+do { \
+ (field)[(num) - la->fireWallBaseNum] = 1; \
+} /*lint -save -e717 */ while(0)/* lint -restore */
+
+#define fw_clrfield(la, field, num) \
+do { \
+ (field)[(num) - la->fireWallBaseNum] = 0; \
+} /*lint -save -e717 */ while(0)/* lint -restore */
+
+#define fw_tstfield(la, field, num) ((field)[(num) - la->fireWallBaseNum])
+
+static void
+InitPunchFW(struct libalias *la)
+{
+
+ LIBALIAS_LOCK_ASSERT(la);
+ la->fireWallField = malloc(la->fireWallNumNums);
+ if (la->fireWallField) {
+ memset(la->fireWallField, 0, la->fireWallNumNums);
+ if (la->fireWallFD < 0) {
+ la->fireWallFD = socket(AF_INET, SOCK_RAW, IPPROTO_RAW);
+ }
+ ClearAllFWHoles(la);
+ la->fireWallActiveNum = la->fireWallBaseNum;
+ }
+}
+
+static void
+UninitPunchFW(struct libalias *la)
+{
+
+ LIBALIAS_LOCK_ASSERT(la);
+ ClearAllFWHoles(la);
+ if (la->fireWallFD >= 0)
+ closesocket(la->fireWallFD);
+ la->fireWallFD = -1;
+ if (la->fireWallField)
+ free(la->fireWallField);
+ la->fireWallField = NULL;
+ la->packetAliasMode &= ~PKT_ALIAS_PUNCH_FW;
+}
+
+/* Make a certain link go through the firewall */
+void
+PunchFWHole(struct alias_link *lnk)
+{
+ struct libalias *la;
+ int r; /* Result code */
+ struct ip_fw rule; /* On-the-fly built rule */
+ int fwhole; /* Where to punch hole */
+
+ LIBALIAS_LOCK_ASSERT(la);
+ la = lnk->la;
+
+/* Don't do anything unless we are asked to */
+ if (!(la->packetAliasMode & PKT_ALIAS_PUNCH_FW) ||
+ la->fireWallFD < 0 ||
+ lnk->link_type != LINK_TCP)
+ return;
+
+ memset(&rule, 0, sizeof rule);
+
+/** Build rule **/
+
+ /* Find empty slot */
+ for (fwhole = la->fireWallActiveNum;
+ fwhole < la->fireWallBaseNum + la->fireWallNumNums &&
+ fw_tstfield(la, la->fireWallField, fwhole);
+ fwhole++);
+ if (fwhole == la->fireWallBaseNum + la->fireWallNumNums) {
+ for (fwhole = la->fireWallBaseNum;
+ fwhole < la->fireWallActiveNum &&
+ fw_tstfield(la, la->fireWallField, fwhole);
+ fwhole++);
+ if (fwhole == la->fireWallActiveNum) {
+ /* No rule point empty - we can't punch more holes. */
+ la->fireWallActiveNum = la->fireWallBaseNum;
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr, "libalias: Unable to create firewall hole!\n");
+#endif
+ return;
+ }
+ }
+ /* Start next search at next position */
+ la->fireWallActiveNum = fwhole + 1;
+
+ /*
+ * generate two rules of the form
+ *
+ * add fwhole accept tcp from OAddr OPort to DAddr DPort add fwhole
+ * accept tcp from DAddr DPort to OAddr OPort
+ */
+ if (GetOriginalPort(lnk) != 0 && GetDestPort(lnk) != 0) {
+ u_int32_t rulebuf[255];
+ int i;
+
+ i = fill_rule(rulebuf, sizeof(rulebuf), fwhole,
+ O_ACCEPT, IPPROTO_TCP,
+ GetOriginalAddress(lnk), ntohs(GetOriginalPort(lnk)),
+ GetDestAddress(lnk), ntohs(GetDestPort(lnk)));
+ r = setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i);
+ if (r)
+ err(1, "alias punch inbound(1) setsockopt(IP_FW_ADD)");
+
+ i = fill_rule(rulebuf, sizeof(rulebuf), fwhole,
+ O_ACCEPT, IPPROTO_TCP,
+ GetDestAddress(lnk), ntohs(GetDestPort(lnk)),
+ GetOriginalAddress(lnk), ntohs(GetOriginalPort(lnk)));
+ r = setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_ADD, rulebuf, i);
+ if (r)
+ err(1, "alias punch inbound(2) setsockopt(IP_FW_ADD)");
+ }
+
+/* Indicate hole applied */
+ lnk->data.tcp->fwhole = fwhole;
+ fw_setfield(la, la->fireWallField, fwhole);
+}
+
+/* Remove a hole in a firewall associated with a particular alias
+ lnk. Calling this too often is harmless. */
+static void
+ClearFWHole(struct alias_link *lnk)
+{
+ struct libalias *la;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ la = lnk->la;
+ if (lnk->link_type == LINK_TCP) {
+ int fwhole = lnk->data.tcp->fwhole; /* Where is the firewall
+ * hole? */
+ struct ip_fw rule;
+
+ if (fwhole < 0)
+ return;
+
+ memset(&rule, 0, sizeof rule); /* useless for ipfw2 */
+ while (!setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_DEL,
+ &fwhole, sizeof fwhole));
+ fw_clrfield(la, la->fireWallField, fwhole);
+ lnk->data.tcp->fwhole = -1;
+ }
+}
+
+/* Clear out the entire range dedicated to firewall holes. */
+static void
+ClearAllFWHoles(struct libalias *la)
+{
+ struct ip_fw rule; /* On-the-fly built rule */
+ int i;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ if (la->fireWallFD < 0)
+ return;
+
+ memset(&rule, 0, sizeof rule);
+ for (i = la->fireWallBaseNum; i < la->fireWallBaseNum + la->fireWallNumNums; i++) {
+ int r = i;
+
+ while (!setsockopt(la->fireWallFD, IPPROTO_IP, IP_FW_DEL, &r, sizeof r));
+ }
+ /* XXX: third arg correct here ? /phk */
+ memset(la->fireWallField, 0, la->fireWallNumNums);
+}
+
+#endif
+
+void
+LibAliasSetFWBase(struct libalias *la, unsigned int base, unsigned int num)
+{
+
+#ifdef VBOX
+ NOREF(la);
+ NOREF(base);
+ NOREF(num);
+#endif
+ LIBALIAS_LOCK(la);
+#ifndef NO_FW_PUNCH
+ la->fireWallBaseNum = base;
+ la->fireWallNumNums = num;
+#endif
+ LIBALIAS_UNLOCK(la);
+}
+
+void
+LibAliasSetSkinnyPort(struct libalias *la, unsigned int port)
+{
+
+ LIBALIAS_LOCK(la);
+ la->skinnyPort = port;
+ LIBALIAS_UNLOCK(la);
+}
diff --git a/src/VBox/Devices/Network/slirp/libalias/alias_dummy.c b/src/VBox/Devices/Network/slirp/libalias/alias_dummy.c
new file mode 100644
index 00000000..31220c54
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/alias_dummy.c
@@ -0,0 +1,153 @@
+/*-
+ * Copyright (c) 2005 Paolo Pisati <piso@FreeBSD.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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHOR 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 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netinet/libalias/alias_dummy.c,v 1.1.8.1 2009/04/15 03:14:26 kensmith Exp $");
+
+/*
+ * Alias_dummy is just an empty skeleton used to demostrate how to write
+ * a module for libalias, that will run unalterated in userland or in
+ * kernel land.
+ */
+
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#else
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#endif
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#ifdef _KERNEL
+#include <netinet/libalias/alias_local.h>
+#include <netinet/libalias/alias_mod.h>
+#else
+#include "alias_local.h"
+#include "alias_mod.h"
+#endif
+
+static void
+AliasHandleDummy(struct libalias *la, struct ip *ip, struct alias_data *ah);
+
+static int
+fingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ /*
+ * Check here all the data that will be used later, if any field
+ * is empy/NULL, return a -1 value.
+ */
+ if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL ||
+ ah->maxpktsize == 0)
+ return (-1);
+ /*
+ * Fingerprint the incoming packet, if it matches any conditions
+ * return an OK value.
+ */
+ if (ntohs(*ah->dport) == 123
+ || ntohs(*ah->sport) == 456)
+ return (0); /* I know how to handle it. */
+ return (-1); /* I don't recognize this packet. */
+}
+
+/*
+ * Wrap in this general purpose function, the real function used to alias the
+ * packets.
+ */
+
+static int
+protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ AliasHandleDummy(la, pip, ah);
+ return (0);
+}
+
+/*
+ * NOTA BENE: the next variable MUST NOT be renamed in any case if you want
+ * your module to work in userland, cause it's used to find and use all
+ * the protocol handlers present in every module.
+ * So WATCH OUT, your module needs this variables and it needs it with
+ * ITS EXACT NAME: handlers.
+ */
+
+struct proto_handler handlers [] = {
+ {
+ .pri = 666,
+ .dir = IN|OUT,
+ .proto = UDP|TCP,
+ .fingerprint = &fingerprint,
+ .protohandler = &protohandler
+ },
+ { EOH }
+};
+
+static int
+mod_handler(module_t mod, int type, void *data)
+{
+ int error;
+
+ switch (type) {
+ case MOD_LOAD:
+ error = 0;
+ LibAliasAttachHandlers(handlers);
+ break;
+ case MOD_UNLOAD:
+ error = 0;
+ LibAliasDetachHandlers(handlers);
+ break;
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
+
+#ifdef _KERNEL
+static
+#endif
+moduledata_t alias_mod = {
+ "alias_dummy", mod_handler, NULL
+};
+
+#ifdef _KERNEL
+DECLARE_MODULE(alias_dummy, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
+MODULE_VERSION(alias_dummy, 1);
+MODULE_DEPEND(alias_dummy, libalias, 1, 1, 1);
+#endif
+
+static void
+AliasHandleDummy(struct libalias *la, struct ip *ip, struct alias_data *ah)
+{
+ ; /* Dummy. */
+}
+
diff --git a/src/VBox/Devices/Network/slirp/libalias/alias_ftp.c b/src/VBox/Devices/Network/slirp/libalias/alias_ftp.c
new file mode 100644
index 00000000..afbd70d9
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/alias_ftp.c
@@ -0,0 +1,773 @@
+/*-
+ * Copyright (c) 2001 Charles Mott <cm@linktel.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHOR 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 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 VBOX
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netinet/libalias/alias_ftp.c,v 1.29.2.1.4.1 2009/04/15 03:14:26 kensmith Exp $");
+
+/*
+ Alias_ftp.c performs special processing for FTP sessions under
+ TCP. Specifically, when a PORT/EPRT command from the client
+ side or 227/229 reply from the server is sent, it is intercepted
+ and modified. The address is changed to the gateway machine
+ and an aliasing port is used.
+
+ For this routine to work, the message must fit entirely into a
+ single TCP packet. This is typically the case, but exceptions
+ can easily be envisioned under the actual specifications.
+
+ Probably the most troubling aspect of the approach taken here is
+ that the new message will typically be a different length, and
+ this causes a certain amount of bookkeeping to keep track of the
+ changes of sequence and acknowledgment numbers, since the client
+ machine is totally unaware of the modification to the TCP stream.
+
+
+ References: RFC 959, RFC 2428.
+
+ Initial version: August, 1996 (cjm)
+
+ Version 1.6
+ Brian Somers and Martin Renters identified an IP checksum
+ error for modified IP packets.
+
+ Version 1.7: January 9, 1996 (cjm)
+ Differential checksum computation for change
+ in IP packet length.
+
+ Version 2.1: May, 1997 (cjm)
+ Very minor changes to conform with
+ local/global/function naming conventions
+ within the packet aliasing module.
+
+ Version 3.1: May, 2000 (eds)
+ Add support for passive mode, alias the 227 replies.
+
+ See HISTORY file for record of revisions.
+*/
+
+/* Includes */
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/ctype.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#else
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#endif
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#ifdef _KERNEL
+#include <netinet/libalias/alias.h>
+#include <netinet/libalias/alias_local.h>
+#include <netinet/libalias/alias_mod.h>
+#else
+#include "alias_local.h"
+#include "alias_mod.h"
+#endif
+#else /* VBOX */
+# include <iprt/ctype.h>
+# include <slirp.h>
+# include "alias_local.h"
+# include "alias_mod.h"
+# define isspace(ch) RT_C_IS_SPACE(ch)
+# define isdigit(ch) RT_C_IS_DIGIT(ch)
+#endif /* VBOX */
+
+#define FTP_CONTROL_PORT_NUMBER 21
+
+static void
+AliasHandleFtpOut(struct libalias *, struct ip *, struct alias_link *,
+ int maxpacketsize);
+
+static int
+fingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+#ifdef VBOX
+ NOREF(la);
+ NOREF(pip);
+#endif
+ if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL ||
+ ah->maxpktsize == 0)
+ return (-1);
+ if (ntohs(*ah->dport) == FTP_CONTROL_PORT_NUMBER
+ || ntohs(*ah->sport) == FTP_CONTROL_PORT_NUMBER)
+ return (0);
+ return (-1);
+}
+
+static int
+protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ AliasHandleFtpOut(la, pip, ah->lnk, ah->maxpktsize);
+ return (0);
+}
+
+#ifndef VBOX
+struct proto_handler handlers[] = {
+ {
+ .pri = 80,
+ .dir = OUT,
+ .proto = TCP,
+ .fingerprint = &fingerprint,
+ .protohandler = &protohandler
+ },
+ { EOH }
+};
+#else /* VBOX */
+#define handlers pData->ftp_module
+#endif /* VBOX */
+
+#ifndef VBOX
+static int
+mod_handler(module_t mod, int type, void *data)
+#else
+static int ftp_alias_handler(PNATState pData, int type);
+
+int
+ftp_alias_load(PNATState pData)
+{
+ return ftp_alias_handler(pData, MOD_LOAD);
+}
+
+int
+ftp_alias_unload(PNATState pData)
+{
+ return ftp_alias_handler(pData, MOD_UNLOAD);
+}
+static int
+ftp_alias_handler(PNATState pData, int type)
+#endif
+{
+ int error;
+#ifdef VBOX
+ if (handlers == NULL)
+ handlers = RTMemAllocZ(2 * sizeof(struct proto_handler));
+ handlers[0].pri = 80;
+ handlers[0].dir = OUT;
+ handlers[0].proto = TCP;
+ handlers[0].fingerprint = &fingerprint;
+ handlers[0].protohandler = &protohandler;
+ handlers[1].pri = (u_int)EOH;
+#endif /* VBOX */
+
+ switch (type) {
+ case MOD_LOAD:
+ error = 0;
+#ifdef VBOX
+ LibAliasAttachHandlers(pData, handlers);
+#else
+ LibAliasAttachHandlers(handlers);
+#endif
+ break;
+ case MOD_UNLOAD:
+ error = 0;
+#ifdef VBOX
+ LibAliasDetachHandlers(pData, handlers);
+ RTMemFree(handlers);
+ handlers = NULL;
+#else
+ LibAliasDetachHandlers(handlers);
+#endif
+ break;
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
+
+#ifndef VBOX
+#ifdef _KERNEL
+static
+#endif
+moduledata_t alias_mod = {
+ "alias_ftp", mod_handler, NULL
+};
+#endif /* !VBOX */
+
+#ifdef _KERNEL
+DECLARE_MODULE(alias_ftp, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
+MODULE_VERSION(alias_ftp, 1);
+MODULE_DEPEND(alias_ftp, libalias, 1, 1, 1);
+#endif
+
+#define FTP_CONTROL_PORT_NUMBER 21
+#define MAX_MESSAGE_SIZE 128
+
+/* FTP protocol flags. */
+#define WAIT_CRLF 0x01
+
+enum ftp_message_type {
+ FTP_PORT_COMMAND,
+ FTP_EPRT_COMMAND,
+ FTP_227_REPLY,
+ FTP_229_REPLY,
+ FTP_UNKNOWN_MESSAGE
+};
+
+static int ParseFtpPortCommand(struct libalias *la, char *, int);
+static int ParseFtpEprtCommand(struct libalias *la, char *, int);
+static int ParseFtp227Reply(struct libalias *la, char *, int);
+static int ParseFtp229Reply(struct libalias *la, char *, int);
+static void NewFtpMessage(struct libalias *la, struct ip *, struct alias_link *, int, int);
+
+static void
+AliasHandleFtpOut(
+ struct libalias *la,
+ struct ip *pip, /* IP packet to examine/patch */
+ struct alias_link *lnk, /* The link to go through (aliased port) */
+ int maxpacketsize /* The maximum size this packet can grow to
+ (including headers) */ )
+{
+ int hlen, tlen, dlen, pflags;
+ char *sptr;
+ struct tcphdr *tc;
+ int ftp_message_type;
+
+/* Calculate data length of TCP packet */
+ tc = (struct tcphdr *)ip_next(pip);
+ hlen = (pip->ip_hl + tc->th_off) << 2;
+ tlen = ntohs(pip->ip_len);
+ dlen = tlen - hlen;
+
+/* Place string pointer and beginning of data */
+ sptr = (char *)pip;
+ sptr += hlen;
+
+/*
+ * Check that data length is not too long and previous message was
+ * properly terminated with CRLF.
+ */
+ pflags = GetProtocolFlags(lnk);
+ if (dlen <= MAX_MESSAGE_SIZE && !(pflags & WAIT_CRLF)) {
+ ftp_message_type = FTP_UNKNOWN_MESSAGE;
+
+ if (ntohs(tc->th_dport) == FTP_CONTROL_PORT_NUMBER) {
+/*
+ * When aliasing a client, check for the PORT/EPRT command.
+ */
+ if (ParseFtpPortCommand(la, sptr, dlen))
+ ftp_message_type = FTP_PORT_COMMAND;
+ else if (ParseFtpEprtCommand(la, sptr, dlen))
+ ftp_message_type = FTP_EPRT_COMMAND;
+ } else {
+/*
+ * When aliasing a server, check for the 227/229 reply.
+ */
+ if (ParseFtp227Reply(la, sptr, dlen))
+ ftp_message_type = FTP_227_REPLY;
+ else if (ParseFtp229Reply(la, sptr, dlen)) {
+ ftp_message_type = FTP_229_REPLY;
+ la->true_addr.s_addr = pip->ip_src.s_addr;
+ }
+ }
+
+ if (ftp_message_type != FTP_UNKNOWN_MESSAGE)
+ NewFtpMessage(la, pip, lnk, maxpacketsize, ftp_message_type);
+ }
+/* Track the msgs which are CRLF term'd for PORT/PASV FW breach */
+
+ if (dlen) { /* only if there's data */
+ sptr = (char *)pip; /* start over at beginning */
+ tlen = ntohs(pip->ip_len); /* recalc tlen, pkt may
+ * have grown */
+ if (sptr[tlen - 2] == '\r' && sptr[tlen - 1] == '\n')
+ pflags &= ~WAIT_CRLF;
+ else
+ pflags |= WAIT_CRLF;
+ SetProtocolFlags(lnk, pflags);
+ }
+}
+
+static int
+ParseFtpPortCommand(struct libalias *la, char *sptr, int dlen)
+{
+ char ch;
+ int i, state;
+ u_int32_t addr;
+ u_short port;
+ u_int8_t octet;
+
+ /* Format: "PORT A,D,D,R,PO,RT". */
+
+ /* Return if data length is too short. */
+ if (dlen < 18)
+ return (0);
+
+ if (strncasecmp("PORT ", sptr, 5))
+ return (0);
+
+ addr = port = octet = 0;
+ state = 0;
+ for (i = 5; i < dlen; i++) {
+ ch = sptr[i];
+ switch (state) {
+ case 0:
+ if (isspace(ch))
+ break;
+ else
+ state++;
+ RT_FALL_THRU();
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ case 9:
+ case 11:
+ if (isdigit(ch)) {
+ octet = ch - '0';
+ state++;
+ } else
+ return (0);
+ break;
+ case 2:
+ case 4:
+ case 6:
+ case 8:
+ if (isdigit(ch))
+ octet = 10 * octet + ch - '0';
+ else if (ch == ',') {
+ addr = (addr << 8) + octet;
+ state++;
+ } else
+ return (0);
+ break;
+ case 10:
+ case 12:
+ if (isdigit(ch))
+ octet = 10 * octet + ch - '0';
+ else if (ch == ',' || state == 12) {
+ port = (port << 8) + octet;
+ state++;
+ } else
+ return (0);
+ break;
+ }
+ }
+
+ if (state == 13) {
+ la->true_addr.s_addr = htonl(addr);
+ la->true_port = port;
+ return (1);
+ } else
+ return (0);
+}
+
+static int
+ParseFtpEprtCommand(struct libalias *la, char *sptr, int dlen)
+{
+ char ch, delim;
+ int i, state;
+ u_int32_t addr;
+ u_short port;
+ u_int8_t octet;
+
+ /* Format: "EPRT |1|A.D.D.R|PORT|". */
+
+ /* Return if data length is too short. */
+ if (dlen < 18)
+ return (0);
+
+ if (strncasecmp("EPRT ", sptr, 5))
+ return (0);
+
+ addr = port = octet = 0;
+ delim = '|'; /* XXX gcc -Wuninitialized */
+ state = 0;
+ for (i = 5; i < dlen; i++) {
+ ch = sptr[i];
+ switch (state) {
+ case 0:
+ if (!isspace(ch)) {
+ delim = ch;
+ state++;
+ }
+ break;
+ case 1:
+ if (ch == '1') /* IPv4 address */
+ state++;
+ else
+ return (0);
+ break;
+ case 2:
+ if (ch == delim)
+ state++;
+ else
+ return (0);
+ break;
+ case 3:
+ case 5:
+ case 7:
+ case 9:
+ if (isdigit(ch)) {
+ octet = ch - '0';
+ state++;
+ } else
+ return (0);
+ break;
+ case 4:
+ case 6:
+ case 8:
+ case 10:
+ if (isdigit(ch))
+ octet = 10 * octet + ch - '0';
+ else if (ch == '.' || state == 10) {
+ addr = (addr << 8) + octet;
+ state++;
+ } else
+ return (0);
+ break;
+ case 11:
+ if (isdigit(ch)) {
+ port = ch - '0';
+ state++;
+ } else
+ return (0);
+ break;
+ case 12:
+ if (isdigit(ch))
+ port = 10 * port + ch - '0';
+ else if (ch == delim)
+ state++;
+ else
+ return (0);
+ break;
+ }
+ }
+
+ if (state == 13) {
+ la->true_addr.s_addr = htonl(addr);
+ la->true_port = port;
+ return (1);
+ } else
+ return (0);
+}
+
+static int
+ParseFtp227Reply(struct libalias *la, char *sptr, int dlen)
+{
+ char ch;
+ int i, state;
+ u_int32_t addr;
+ u_short port;
+ u_int8_t octet;
+
+ /* Format: "227 Entering Passive Mode (A,D,D,R,PO,RT)" */
+
+ /* Return if data length is too short. */
+ if (dlen < 17)
+ return (0);
+
+ if (strncmp("227 ", sptr, 4))
+ return (0);
+
+ addr = port = octet = 0;
+
+ state = 0;
+ for (i = 4; i < dlen; i++) {
+ ch = sptr[i];
+ switch (state) {
+ case 0:
+ if (ch == '(')
+ state++;
+ break;
+ case 1:
+ case 3:
+ case 5:
+ case 7:
+ case 9:
+ case 11:
+ if (isdigit(ch)) {
+ octet = ch - '0';
+ state++;
+ } else
+ return (0);
+ break;
+ case 2:
+ case 4:
+ case 6:
+ case 8:
+ if (isdigit(ch))
+ octet = 10 * octet + ch - '0';
+ else if (ch == ',') {
+ addr = (addr << 8) + octet;
+ state++;
+ } else
+ return (0);
+ break;
+ case 10:
+ case 12:
+ if (isdigit(ch))
+ octet = 10 * octet + ch - '0';
+ else if (ch == ',' || (state == 12 && ch == ')')) {
+ port = (port << 8) + octet;
+ state++;
+ } else
+ return (0);
+ break;
+ }
+ }
+
+ if (state == 13) {
+ if (addr != INADDR_LOOPBACK)
+ la->true_addr.s_addr = htonl(addr);
+ else
+ la->true_addr.s_addr = la->pData->alias_addr.s_addr;
+ la->true_port = port;
+ return (1);
+ } else
+ return (0);
+}
+
+static int
+ParseFtp229Reply(struct libalias *la, char *sptr, int dlen)
+{
+ char ch, delim;
+ int i, state;
+ u_short port;
+
+ /* Format: "229 Entering Extended Passive Mode (|||PORT|)" */
+
+ /* Return if data length is too short. */
+ if (dlen < 11)
+ return (0);
+
+ if (strncmp("229 ", sptr, 4))
+ return (0);
+
+ port = 0;
+ delim = '|'; /* XXX gcc -Wuninitialized */
+
+ state = 0;
+ for (i = 4; i < dlen; i++) {
+ ch = sptr[i];
+ switch (state) {
+ case 0:
+ if (ch == '(')
+ state++;
+ break;
+ case 1:
+ delim = ch;
+ state++;
+ break;
+ case 2:
+ case 3:
+ if (ch == delim)
+ state++;
+ else
+ return (0);
+ break;
+ case 4:
+ if (isdigit(ch)) {
+ port = ch - '0';
+ state++;
+ } else
+ return (0);
+ break;
+ case 5:
+ if (isdigit(ch))
+ port = 10 * port + ch - '0';
+ else if (ch == delim)
+ state++;
+ else
+ return (0);
+ break;
+ case 6:
+ if (ch == ')')
+ state++;
+ else
+ return (0);
+ break;
+ }
+ }
+
+ if (state == 7) {
+ la->true_port = port;
+ return (1);
+ } else
+ return (0);
+}
+
+static void
+NewFtpMessage(struct libalias *la, struct ip *pip,
+ struct alias_link *lnk,
+ int maxpacketsize,
+ int ftp_message_type)
+{
+ struct alias_link *ftp_lnk;
+
+/* Security checks. */
+ if (pip->ip_src.s_addr != la->true_addr.s_addr)
+ return;
+
+ if (la->true_port < IPPORT_RESERVED)
+ return;
+
+/* Establish link to address and port found in FTP control message. */
+ ftp_lnk = FindUdpTcpOut(la, la->true_addr, GetDestAddress(lnk),
+ htons(la->true_port), 0, IPPROTO_TCP, 1);
+
+ if (ftp_lnk != NULL) {
+ int slen, hlen, tlen, dlen;
+ struct tcphdr *tc;
+
+#ifndef NO_FW_PUNCH
+ /* Punch hole in firewall */
+ PunchFWHole(ftp_lnk);
+#endif
+
+/* Calculate data length of TCP packet */
+ tc = (struct tcphdr *)ip_next(pip);
+ hlen = (pip->ip_hl + tc->th_off) << 2;
+ tlen = ntohs(pip->ip_len);
+ dlen = tlen - hlen;
+
+/* Create new FTP message. */
+ {
+ char stemp[MAX_MESSAGE_SIZE + 1];
+ char *sptr;
+ u_short alias_port;
+ u_char *ptr;
+ int a1, a2, a3, a4, p1, p2;
+ struct in_addr alias_address;
+
+/* Decompose alias address into quad format */
+ alias_address = GetAliasAddress(lnk);
+ ptr = (u_char *) & alias_address.s_addr;
+ a1 = *ptr++;
+ a2 = *ptr++;
+ a3 = *ptr++;
+ a4 = *ptr;
+
+ alias_port = GetAliasPort(ftp_lnk);
+
+/* Prepare new command */
+ switch (ftp_message_type) {
+ case FTP_PORT_COMMAND:
+ case FTP_227_REPLY:
+ /* Decompose alias port into pair format. */
+ ptr = (u_char *)&alias_port;
+ p1 = *ptr++;
+ p2 = *ptr;
+
+ if (ftp_message_type == FTP_PORT_COMMAND) {
+ /* Generate PORT command string. */
+#ifndef VBOX
+ sprintf(stemp, "PORT %d,%d,%d,%d,%d,%d\r\n",
+ a1, a2, a3, a4, p1, p2);
+#else
+ RTStrPrintf(stemp, sizeof(stemp), "PORT %d,%d,%d,%d,%d,%d\r\n",
+ a1, a2, a3, a4, p1, p2);
+#endif
+ } else {
+ /* Generate 227 reply string. */
+#ifndef VBOX
+ sprintf(stemp,
+ "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
+ a1, a2, a3, a4, p1, p2);
+#else
+ RTStrPrintf(stemp, sizeof(stemp),
+ "227 Entering Passive Mode (%d,%d,%d,%d,%d,%d)\r\n",
+ a1, a2, a3, a4, p1, p2);
+#endif
+ }
+ break;
+ case FTP_EPRT_COMMAND:
+ /* Generate EPRT command string. */
+#ifndef VBOX
+ sprintf(stemp, "EPRT |1|%d.%d.%d.%d|%d|\r\n",
+ a1, a2, a3, a4, ntohs(alias_port));
+#else
+ RTStrPrintf(stemp, sizeof(stemp), "EPRT |1|%d.%d.%d.%d|%d|\r\n",
+ a1, a2, a3, a4, ntohs(alias_port));
+#endif
+ break;
+ case FTP_229_REPLY:
+ /* Generate 229 reply string. */
+#ifndef VBOX
+ sprintf(stemp, "229 Entering Extended Passive Mode (|||%d|)\r\n",
+ ntohs(alias_port));
+#else
+ RTStrPrintf(stemp, sizeof(stemp), "229 Entering Extended Passive Mode (|||%d|)\r\n",
+ ntohs(alias_port));
+#endif
+ break;
+ }
+
+/* Save string length for IP header modification */
+ slen = (int)strlen(stemp);
+
+/* Copy modified buffer into IP packet. */
+ sptr = (char *)pip;
+ sptr += hlen;
+ strncpy(sptr, stemp, maxpacketsize - hlen);
+ }
+
+/* Save information regarding modified seq and ack numbers */
+ {
+ int delta;
+
+ SetAckModified(lnk);
+ delta = GetDeltaSeqOut(pip, lnk);
+ AddSeq(pip, lnk, delta + slen - dlen);
+ }
+
+/* Revise IP header */
+ {
+ u_short new_len;
+
+ new_len = htons(hlen + slen);
+ DifferentialChecksum(&pip->ip_sum,
+ &new_len,
+ &pip->ip_len,
+ 1);
+ pip->ip_len = new_len;
+ }
+
+/* Compute TCP checksum for revised packet */
+ tc->th_sum = 0;
+#ifdef _KERNEL
+ tc->th_x2 = 1;
+#else
+ tc->th_sum = TcpChecksum(pip);
+#endif
+ } else {
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr,
+ "PacketAlias/HandleFtpOut: Cannot allocate FTP data port\n");
+#endif
+ }
+}
diff --git a/src/VBox/Devices/Network/slirp/libalias/alias_irc.c b/src/VBox/Devices/Network/slirp/libalias/alias_irc.c
new file mode 100644
index 00000000..01962ddf
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/alias_irc.c
@@ -0,0 +1,485 @@
+/*-
+ * Copyright (c) 2001 Charles Mott <cm@linktel.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHOR 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 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netinet/libalias/alias_irc.c,v 1.23.2.2.4.1 2009/04/15 03:14:26 kensmith Exp $");
+
+/* Alias_irc.c intercepts packages contain IRC CTCP commands, and
+ changes DCC commands to export a port on the aliasing host instead
+ of an aliased host.
+
+ For this routine to work, the DCC command must fit entirely into a
+ single TCP packet. This will usually happen, but is not
+ guaranteed.
+
+ The interception is likely to change the length of the packet.
+ The handling of this is copied more-or-less verbatim from
+ ftp_alias.c
+
+ Initial version: Eivind Eklund <perhaps@yes.no> (ee) 97-01-29
+
+ Version 2.1: May, 1997 (cjm)
+ Very minor changes to conform with
+ local/global/function naming conventions
+ withing the packet alising module.
+*/
+
+/* Includes */
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/ctype.h>
+#include <sys/limits.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#else
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#endif
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#ifdef _KERNEL
+#include <netinet/libalias/alias.h>
+#include <netinet/libalias/alias_local.h>
+#include <netinet/libalias/alias_mod.h>
+#else
+#include "alias_local.h"
+#include "alias_mod.h"
+#endif
+
+#define IRC_CONTROL_PORT_NUMBER_1 6667
+#define IRC_CONTROL_PORT_NUMBER_2 6668
+
+#define PKTSIZE (IP_MAXPACKET + 1)
+char *newpacket;
+
+/* Local defines */
+#define DBprintf(a)
+
+static void
+AliasHandleIrcOut(struct libalias *, struct ip *, struct alias_link *,
+ int maxpacketsize);
+
+static int
+fingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ if (ah->dport == NULL || ah->lnk == NULL ||
+ ah->maxpktsize == 0)
+ return (-1);
+ if (ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_1
+ || ntohs(*ah->dport) == IRC_CONTROL_PORT_NUMBER_2)
+ return (0);
+ return (-1);
+}
+
+static int
+protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ newpacket = malloc(PKTSIZE);
+ if (newpacket) {
+ AliasHandleIrcOut(la, pip, ah->lnk, ah->maxpktsize);
+ free(newpacket);
+ }
+ return (0);
+}
+
+struct proto_handler handlers[] = {
+ {
+ .pri = 90,
+ .dir = OUT,
+ .proto = TCP,
+ .fingerprint = &fingerprint,
+ .protohandler = &protohandler
+ },
+ { EOH }
+};
+
+static int
+mod_handler(module_t mod, int type, void *data)
+{
+ int error;
+
+ switch (type) {
+ case MOD_LOAD:
+ error = 0;
+ LibAliasAttachHandlers(handlers);
+ break;
+ case MOD_UNLOAD:
+ error = 0;
+ LibAliasDetachHandlers(handlers);
+ break;
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
+
+#ifdef _KERNEL
+static
+#endif
+moduledata_t alias_mod = {
+ "alias_irc", mod_handler, NULL
+};
+
+/* Kernel module definition. */
+#ifdef _KERNEL
+DECLARE_MODULE(alias_irc, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
+MODULE_VERSION(alias_irc, 1);
+MODULE_DEPEND(alias_irc, libalias, 1, 1, 1);
+#endif
+
+static void
+AliasHandleIrcOut(struct libalias *la,
+ struct ip *pip, /* IP packet to examine */
+ struct alias_link *lnk, /* Which link are we on? */
+ int maxsize /* Maximum size of IP packet including
+ * headers */
+)
+{
+ int hlen, tlen, dlen;
+ struct in_addr true_addr;
+ u_short true_port;
+ char *sptr;
+ struct tcphdr *tc;
+ int i; /* Iterator through the source */
+
+/* Calculate data length of TCP packet */
+ tc = (struct tcphdr *)ip_next(pip);
+ hlen = (pip->ip_hl + tc->th_off) << 2;
+ tlen = ntohs(pip->ip_len);
+ dlen = tlen - hlen;
+
+ /*
+ * Return if data length is too short - assume an entire PRIVMSG in
+ * each packet.
+ */
+ if (dlen < (int)sizeof(":A!a@n.n PRIVMSG A :aDCC 1 1a") - 1)
+ return;
+
+/* Place string pointer at beginning of data */
+ sptr = (char *)pip;
+ sptr += hlen;
+ maxsize -= hlen; /* We're interested in maximum size of
+ * data, not packet */
+
+ /* Search for a CTCP command [Note 1] */
+ for (i = 0; i < dlen; i++) {
+ if (sptr[i] == '\001')
+ goto lFOUND_CTCP;
+ }
+ return; /* No CTCP commands in */
+ /* Handle CTCP commands - the buffer may have to be copied */
+lFOUND_CTCP:
+ {
+ unsigned int copyat = i;
+ unsigned int iCopy = 0; /* How much data have we written to
+ * copy-back string? */
+ unsigned long org_addr; /* Original IP address */
+ unsigned short org_port; /* Original source port
+ * address */
+
+lCTCP_START:
+ if (i >= dlen || iCopy >= PKTSIZE)
+ goto lPACKET_DONE;
+ newpacket[iCopy++] = sptr[i++]; /* Copy the CTCP start
+ * character */
+ /* Start of a CTCP */
+ if (i + 4 >= dlen) /* Too short for DCC */
+ goto lBAD_CTCP;
+ if (sptr[i + 0] != 'D')
+ goto lBAD_CTCP;
+ if (sptr[i + 1] != 'C')
+ goto lBAD_CTCP;
+ if (sptr[i + 2] != 'C')
+ goto lBAD_CTCP;
+ if (sptr[i + 3] != ' ')
+ goto lBAD_CTCP;
+ /* We have a DCC command - handle it! */
+ i += 4; /* Skip "DCC " */
+ if (iCopy + 4 > PKTSIZE)
+ goto lPACKET_DONE;
+ newpacket[iCopy++] = 'D';
+ newpacket[iCopy++] = 'C';
+ newpacket[iCopy++] = 'C';
+ newpacket[iCopy++] = ' ';
+
+ DBprintf(("Found DCC\n"));
+ /*
+ * Skip any extra spaces (should not occur according to
+ * protocol, but DCC breaks CTCP protocol anyway
+ */
+ while (sptr[i] == ' ') {
+ if (++i >= dlen) {
+ DBprintf(("DCC packet terminated in just spaces\n"));
+ goto lPACKET_DONE;
+ }
+ }
+
+ DBprintf(("Transferring command...\n"));
+ while (sptr[i] != ' ') {
+ newpacket[iCopy++] = sptr[i];
+ if (++i >= dlen || iCopy >= PKTSIZE) {
+ DBprintf(("DCC packet terminated during command\n"));
+ goto lPACKET_DONE;
+ }
+ }
+ /* Copy _one_ space */
+ if (i + 1 < dlen && iCopy < PKTSIZE)
+ newpacket[iCopy++] = sptr[i++];
+
+ DBprintf(("Done command - removing spaces\n"));
+ /*
+ * Skip any extra spaces (should not occur according to
+ * protocol, but DCC breaks CTCP protocol anyway
+ */
+ while (sptr[i] == ' ') {
+ if (++i >= dlen) {
+ DBprintf(("DCC packet terminated in just spaces (post-command)\n"));
+ goto lPACKET_DONE;
+ }
+ }
+
+ DBprintf(("Transferring filename...\n"));
+ while (sptr[i] != ' ') {
+ newpacket[iCopy++] = sptr[i];
+ if (++i >= dlen || iCopy >= PKTSIZE) {
+ DBprintf(("DCC packet terminated during filename\n"));
+ goto lPACKET_DONE;
+ }
+ }
+ /* Copy _one_ space */
+ if (i + 1 < dlen && iCopy < PKTSIZE)
+ newpacket[iCopy++] = sptr[i++];
+
+ DBprintf(("Done filename - removing spaces\n"));
+ /*
+ * Skip any extra spaces (should not occur according to
+ * protocol, but DCC breaks CTCP protocol anyway
+ */
+ while (sptr[i] == ' ') {
+ if (++i >= dlen) {
+ DBprintf(("DCC packet terminated in just spaces (post-filename)\n"));
+ goto lPACKET_DONE;
+ }
+ }
+
+ DBprintf(("Fetching IP address\n"));
+ /* Fetch IP address */
+ org_addr = 0;
+ while (i < dlen && isdigit(sptr[i])) {
+ if (org_addr > ULONG_MAX / 10UL) { /* Terminate on overflow */
+ DBprintf(("DCC Address overflow (org_addr == 0x%08lx, next char %c\n", org_addr, sptr[i]));
+ goto lBAD_CTCP;
+ }
+ org_addr *= 10;
+ org_addr += sptr[i++] - '0';
+ }
+ DBprintf(("Skipping space\n"));
+ if (i + 1 >= dlen || sptr[i] != ' ') {
+ DBprintf(("Overflow (%d >= %d) or bad character (%02x) terminating IP address\n", i + 1, dlen, sptr[i]));
+ goto lBAD_CTCP;
+ }
+ /*
+ * Skip any extra spaces (should not occur according to
+ * protocol, but DCC breaks CTCP protocol anyway, so we
+ * might as well play it safe
+ */
+ while (sptr[i] == ' ') {
+ if (++i >= dlen) {
+ DBprintf(("Packet failure - space overflow.\n"));
+ goto lPACKET_DONE;
+ }
+ }
+ DBprintf(("Fetching port number\n"));
+ /* Fetch source port */
+ org_port = 0;
+ while (i < dlen && isdigit(sptr[i])) {
+ if (org_port > 6554) { /* Terminate on overflow
+ * (65536/10 rounded up */
+ DBprintf(("DCC: port number overflow\n"));
+ goto lBAD_CTCP;
+ }
+ org_port *= 10;
+ org_port += sptr[i++] - '0';
+ }
+ /* Skip illegal addresses (or early termination) */
+ if (i >= dlen || (sptr[i] != '\001' && sptr[i] != ' ')) {
+ DBprintf(("Bad port termination\n"));
+ goto lBAD_CTCP;
+ }
+ DBprintf(("Got IP %lu and port %u\n", org_addr, (unsigned)org_port));
+
+ /* We've got the address and port - now alias it */
+ {
+ struct alias_link *dcc_lnk;
+ struct in_addr destaddr;
+
+
+ true_port = htons(org_port);
+ true_addr.s_addr = htonl(org_addr);
+ destaddr.s_addr = 0;
+
+ /* Sanity/Security checking */
+ if (!org_addr || !org_port ||
+ pip->ip_src.s_addr != true_addr.s_addr ||
+ org_port < IPPORT_RESERVED)
+ goto lBAD_CTCP;
+
+ /*
+ * Steal the FTP_DATA_PORT - it doesn't really
+ * matter, and this would probably allow it through
+ * at least _some_ firewalls.
+ */
+ dcc_lnk = FindUdpTcpOut(la, true_addr, destaddr,
+ true_port, 0,
+ IPPROTO_TCP, 1);
+ DBprintf(("Got a DCC link\n"));
+ if (dcc_lnk) {
+ struct in_addr alias_address; /* Address from aliasing */
+ u_short alias_port; /* Port given by
+ * aliasing */
+ int n;
+
+#ifndef NO_FW_PUNCH
+ /* Generate firewall hole as appropriate */
+ PunchFWHole(dcc_lnk);
+#endif
+
+ alias_address = GetAliasAddress(lnk);
+ n = snprintf(&newpacket[iCopy],
+ PKTSIZE - iCopy,
+ "%lu ", (u_long) htonl(alias_address.s_addr));
+ if (n < 0) {
+ DBprintf(("DCC packet construct failure.\n"));
+ goto lBAD_CTCP;
+ }
+ if ((iCopy += n) >= PKTSIZE) { /* Truncated/fit exactly
+ * - bad news */
+ DBprintf(("DCC constructed packet overflow.\n"));
+ goto lBAD_CTCP;
+ }
+ alias_port = GetAliasPort(dcc_lnk);
+ n = snprintf(&newpacket[iCopy],
+ PKTSIZE - iCopy,
+ "%u", htons(alias_port));
+ if (n < 0) {
+ DBprintf(("DCC packet construct failure.\n"));
+ goto lBAD_CTCP;
+ }
+ iCopy += n;
+ /*
+ * Done - truncated cases will be taken
+ * care of by lBAD_CTCP
+ */
+ DBprintf(("Aliased IP %lu and port %u\n", alias_address.s_addr, (unsigned)alias_port));
+ }
+ }
+ /*
+ * An uninteresting CTCP - state entered right after '\001'
+ * has been pushed. Also used to copy the rest of a DCC,
+ * after IP address and port has been handled
+ */
+lBAD_CTCP:
+ for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) {
+ newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */
+ if (sptr[i] == '\001') {
+ goto lNORMAL_TEXT;
+ }
+ }
+ goto lPACKET_DONE;
+ /* Normal text */
+lNORMAL_TEXT:
+ for (; i < dlen && iCopy < PKTSIZE; i++, iCopy++) {
+ newpacket[iCopy] = sptr[i]; /* Copy CTCP unchanged */
+ if (sptr[i] == '\001') {
+ goto lCTCP_START;
+ }
+ }
+ /* Handle the end of a packet */
+lPACKET_DONE:
+ iCopy = iCopy > maxsize - copyat ? maxsize - copyat : iCopy;
+ memcpy(sptr + copyat, newpacket, iCopy);
+
+/* Save information regarding modified seq and ack numbers */
+ {
+ int delta;
+
+ SetAckModified(lnk);
+ delta = GetDeltaSeqOut(pip, lnk);
+ AddSeq(pip, lnk, delta + copyat + iCopy - dlen);
+ }
+
+ /* Revise IP header */
+ {
+ u_short new_len;
+
+ new_len = htons(hlen + iCopy + copyat);
+ DifferentialChecksum(&pip->ip_sum,
+ &new_len,
+ &pip->ip_len,
+ 1);
+ pip->ip_len = new_len;
+ }
+
+ /* Compute TCP checksum for revised packet */
+ tc->th_sum = 0;
+#ifdef _KERNEL
+ tc->th_x2 = 1;
+#else
+ tc->th_sum = TcpChecksum(pip);
+#endif
+ return;
+ }
+}
+
+/* Notes:
+ [Note 1]
+ The initial search will most often fail; it could be replaced with a 32-bit specific search.
+ Such a search would be done for 32-bit unsigned value V:
+ V ^= 0x01010101; (Search is for null bytes)
+ if( ((V-0x01010101)^V) & 0x80808080 ) {
+ (found a null bytes which was a 01 byte)
+ }
+ To assert that the processor is 32-bits, do
+ extern int ircdccar[32]; (32 bits)
+ extern int ircdccar[CHAR_BIT*sizeof(unsigned int)];
+ which will generate a type-error on all but 32-bit machines.
+
+ [Note 2] This routine really ought to be replaced with one that
+ creates a transparent proxy on the aliasing host, to allow arbitary
+ changes in the TCP stream. This should not be too difficult given
+ this base; I (ee) will try to do this some time later.
+ */
diff --git a/src/VBox/Devices/Network/slirp/libalias/alias_local.h b/src/VBox/Devices/Network/slirp/libalias/alias_local.h
new file mode 100644
index 00000000..9ee4d03a
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/alias_local.h
@@ -0,0 +1,370 @@
+/*-
+ * Copyright (c) 2001 Charles Mott <cm@linktel.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHOR 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 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.
+ *
+ * $FreeBSD: src/sys/netinet/libalias/alias_local.h,v 1.34.2.1.4.1 2009/04/15 03:14:26 kensmith Exp $
+ */
+
+/*
+ * Alias_local.h contains the function prototypes for alias.c,
+ * alias_db.c, alias_util.c and alias_ftp.c, alias_irc.c (as well
+ * as any future add-ons). It also includes macros, globals and
+ * struct definitions shared by more than one alias*.c file.
+ *
+ * This include file is intended to be used only within the aliasing
+ * software. Outside world interfaces are defined in alias.h
+ *
+ * This software is placed into the public domain with no restrictions
+ * on its distribution.
+ *
+ * Initial version: August, 1996 (cjm)
+ *
+ * <updated several times by original author and Eivind Eklund>
+ */
+
+#ifndef _ALIAS_LOCAL_H_
+#define _ALIAS_LOCAL_H_
+
+#ifndef VBOX
+#include <sys/types.h>
+#include <sys/sysctl.h>
+
+#ifdef _KERNEL
+#include <sys/malloc.h>
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+
+/* XXX: LibAliasSetTarget() uses this constant. */
+#define INADDR_NONE 0xffffffff
+#endif
+#else /* VBOX */
+# include <slirp.h>
+#endif /* VBOX */
+
+/* Sizes of input and output link tables */
+#define LINK_TABLE_OUT_SIZE 4001
+#define LINK_TABLE_IN_SIZE 4001
+
+struct proxy_entry;
+
+struct libalias {
+ LIST_ENTRY(libalias) instancelist;
+
+ int packetAliasMode; /* Mode flags */
+ /* - documented in alias.h */
+
+ struct in_addr aliasAddress; /* Address written onto source */
+ /* field of IP packet. */
+
+ struct in_addr targetAddress; /* IP address incoming packets */
+ /* are sent to if no aliasing */
+ /* link already exists */
+
+ struct in_addr nullAddress; /* Used as a dummy parameter for */
+ /* some function calls */
+
+ LIST_HEAD (RT_NOTHING, alias_link) linkTableOut[LINK_TABLE_OUT_SIZE];
+ /* Lookup table of pointers to */
+ /* chains of link records. Each */
+
+ LIST_HEAD (RT_NOTHING, alias_link) linkTableIn[LINK_TABLE_IN_SIZE];
+ /* link record is doubly indexed */
+ /* into input and output lookup */
+ /* tables. */
+
+ /* Link statistics */
+ int icmpLinkCount;
+ int udpLinkCount;
+ int tcpLinkCount;
+ int pptpLinkCount;
+ int protoLinkCount;
+ int fragmentIdLinkCount;
+ int fragmentPtrLinkCount;
+ int sockCount;
+
+ int cleanupIndex; /* Index to chain of link table */
+ /* being inspected for old links */
+#ifndef VBOX
+ int timeStamp; /* System time in seconds for */
+ /* current packet */
+
+ int lastCleanupTime; /* Last time
+ * IncrementalCleanup() */
+#else
+ unsigned int timeStamp; /* System time in seconds for */
+ unsigned int lastCleanupTime; /* Last time */
+#endif
+ /* was called */
+
+ int deleteAllLinks; /* If equal to zero, DeleteLink() */
+ /* will not remove permanent links */
+
+ /* log descriptor */
+#ifdef _KERNEL
+ char *logDesc;
+#else
+ FILE *logDesc;
+#endif
+ /* statistics monitoring */
+
+ int newDefaultLink; /* Indicates if a new aliasing */
+ /* link has been created after a */
+ /* call to PacketAliasIn/Out(). */
+
+#ifndef NO_FW_PUNCH
+ int fireWallFD; /* File descriptor to be able to */
+ /* control firewall. Opened by */
+ /* PacketAliasSetMode on first */
+ /* setting the PKT_ALIAS_PUNCH_FW */
+ /* flag. */
+ int fireWallBaseNum; /* The first firewall entry
+ * free for our use */
+ int fireWallNumNums; /* How many entries can we
+ * use? */
+ int fireWallActiveNum; /* Which entry did we last
+ * use? */
+ char *fireWallField; /* bool array for entries */
+#endif
+
+ unsigned int skinnyPort; /* TCP port used by the Skinny */
+ /* protocol. */
+
+ struct proxy_entry *proxyList;
+
+ struct in_addr true_addr; /* in network byte order. */
+ u_short true_port; /* in host byte order. */
+#if defined(_KERNEL) && !defined(VBOX)
+ /*
+ * avoid races in libalias: every public function has to use it.
+ */
+ struct mtx mutex;
+#endif
+#ifdef VBOX
+ PNATState pData;
+#endif
+};
+
+/* Macros */
+
+#if defined(_KERNEL) && !defined(VBOX)
+#define LIBALIAS_LOCK_INIT(l) \
+ mtx_init(&l->mutex, "per-instance libalias mutex", NULL, MTX_DEF)
+#define LIBALIAS_LOCK_ASSERT(l) mtx_assert(&l->mutex, MA_OWNED)
+#define LIBALIAS_LOCK(l) mtx_lock(&l->mutex)
+#define LIBALIAS_UNLOCK(l) mtx_unlock(&l->mutex)
+#define LIBALIAS_LOCK_DESTROY(l) mtx_destroy(&l->mutex)
+#else
+#define LIBALIAS_LOCK_INIT(l)
+#define LIBALIAS_LOCK_ASSERT(l)
+#define LIBALIAS_LOCK(l) NOREF((l));
+#define LIBALIAS_UNLOCK(l) NOREF((l))
+#define LIBALIAS_LOCK_DESTROY(l)
+#endif
+
+/*
+ * The following macro is used to update an
+ * internet checksum. "delta" is a 32-bit
+ * accumulation of all the changes to the
+ * checksum (adding in new 16-bit words and
+ * subtracting out old words), and "cksum"
+ * is the checksum value to be updated.
+ */
+#define ADJUST_CHECKSUM(acc, cksum) \
+ do { \
+ acc += cksum; \
+ if (acc < 0) { \
+ acc = -acc; \
+ acc = (acc >> 16) + (acc & 0xffff); \
+ acc += acc >> 16; \
+ cksum = (u_short) ~acc; \
+ } else { \
+ acc = (acc >> 16) + (acc & 0xffff); \
+ acc += acc >> 16; \
+ cksum = (u_short) acc; \
+ } \
+ } while (0)
+
+
+/* Prototypes */
+
+/*
+ * We do not calculate TCP checksums when libalias is a kernel
+ * module, since it has no idea about checksum offloading.
+ * If TCP data has changed, then we just set checksum to zero,
+ * and caller must recalculate it himself.
+ * In case if libalias will edit UDP data, the same approach
+ * should be used.
+ */
+#ifndef _KERNEL
+u_short IpChecksum(struct ip *_pip);
+u_short TcpChecksum(struct ip *_pip);
+#endif
+void
+DifferentialChecksum(u_short * _cksum, void * _new, void * _old, int _n);
+
+/* Internal data access */
+struct alias_link *
+FindIcmpIn(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr,
+ u_short _id_alias, int _create);
+struct alias_link *
+FindIcmpOut(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr,
+ u_short _id, int _create);
+struct alias_link *
+FindFragmentIn1(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr,
+ u_short _ip_id);
+struct alias_link *
+FindFragmentIn2(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr,
+ u_short _ip_id);
+struct alias_link *
+ AddFragmentPtrLink(struct libalias *la, struct in_addr _dst_addr, u_short _ip_id);
+struct alias_link *
+ FindFragmentPtr(struct libalias *la, struct in_addr _dst_addr, u_short _ip_id);
+struct alias_link *
+FindProtoIn(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr,
+ u_char _proto);
+struct alias_link *
+FindProtoOut(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr,
+ u_char _proto);
+struct alias_link *
+FindUdpTcpIn(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr,
+ u_short _dst_port, u_short _alias_port, u_char _proto, int _create);
+struct alias_link *
+FindUdpTcpOut(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr,
+ u_short _src_port, u_short _dst_port, u_char _proto, int _create);
+struct alias_link *
+AddPptp(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr,
+ struct in_addr _alias_addr, u_int16_t _src_call_id);
+struct alias_link *
+FindPptpOutByCallId(struct libalias *la, struct in_addr _src_addr,
+ struct in_addr _dst_addr, u_int16_t _src_call_id);
+struct alias_link *
+FindPptpInByCallId(struct libalias *la, struct in_addr _dst_addr,
+ struct in_addr _alias_addr, u_int16_t _dst_call_id);
+struct alias_link *
+FindPptpOutByPeerCallId(struct libalias *la, struct in_addr _src_addr,
+ struct in_addr _dst_addr, u_int16_t _dst_call_id);
+struct alias_link *
+FindPptpInByPeerCallId(struct libalias *la, struct in_addr _dst_addr,
+ struct in_addr _alias_addr, u_int16_t _alias_call_id);
+struct alias_link *
+FindRtspOut(struct libalias *la, struct in_addr _src_addr, struct in_addr _dst_addr,
+ u_short _src_port, u_short _alias_port, u_char _proto);
+struct in_addr
+ FindOriginalAddress(struct libalias *la, struct in_addr _alias_addr);
+struct in_addr
+ FindAliasAddress(struct libalias *la, struct in_addr _original_addr);
+
+/* External data access/modification */
+int
+FindNewPortGroup(struct libalias *la, struct in_addr _dst_addr, struct in_addr _alias_addr,
+ u_short _src_port, u_short _dst_port, u_short _port_count,
+ u_char _proto, u_char _align);
+void GetFragmentAddr(struct alias_link *_lnk, struct in_addr *_src_addr);
+void SetFragmentAddr(struct alias_link *_lnk, struct in_addr _src_addr);
+void GetFragmentPtr(struct alias_link *_lnk, char **_fptr);
+void SetFragmentPtr(struct alias_link *_lnk, char *fptr);
+void SetStateIn(struct alias_link *_lnk, int _state);
+void SetStateOut(struct alias_link *_lnk, int _state);
+int GetStateIn (struct alias_link *_lnk);
+int GetStateOut(struct alias_link *_lnk);
+struct in_addr
+ GetOriginalAddress(struct alias_link *_lnk);
+struct in_addr
+ GetDestAddress(struct alias_link *_lnk);
+struct in_addr
+ GetAliasAddress(struct alias_link *_lnk);
+struct in_addr
+ GetDefaultAliasAddress(struct libalias *la);
+void SetDefaultAliasAddress(struct libalias *la, struct in_addr _alias_addr);
+u_short GetOriginalPort(struct alias_link *_lnk);
+u_short GetAliasPort(struct alias_link *_lnk);
+struct in_addr
+ GetProxyAddress(struct alias_link *_lnk);
+void SetProxyAddress(struct alias_link *_lnk, struct in_addr _addr);
+u_short GetProxyPort(struct alias_link *_lnk);
+void SetProxyPort(struct alias_link *_lnk, u_short _port);
+void SetAckModified(struct alias_link *_lnk);
+int GetAckModified(struct alias_link *_lnk);
+int GetDeltaAckIn(struct ip *_pip, struct alias_link *_lnk);
+int GetDeltaSeqOut(struct ip *_pip, struct alias_link *_lnk);
+void AddSeq (struct ip *_pip, struct alias_link *_lnk, int _delta);
+void SetExpire (struct alias_link *_lnk, int _expire);
+void ClearCheckNewLink(struct libalias *la);
+void SetProtocolFlags(struct alias_link *_lnk, int _pflags);
+int GetProtocolFlags(struct alias_link *_lnk);
+void SetDestCallId(struct alias_link *_lnk, u_int16_t _cid);
+
+#ifndef NO_FW_PUNCH
+void PunchFWHole(struct alias_link *_lnk);
+
+#endif
+
+/* Housekeeping function */
+void HouseKeeping(struct libalias *);
+
+/* Tcp specfic routines */
+/* lint -save -library Suppress flexelint warnings */
+
+/* Transparent proxy routines */
+int
+ProxyCheck(struct libalias *la, struct ip *_pip, struct in_addr *_proxy_server_addr,
+ u_short * _proxy_server_port);
+void
+ProxyModify(struct libalias *la, struct alias_link *_lnk, struct ip *_pip,
+ int _maxpacketsize, int _proxy_type);
+
+enum alias_tcp_state {
+ ALIAS_TCP_STATE_NOT_CONNECTED,
+ ALIAS_TCP_STATE_CONNECTED,
+ ALIAS_TCP_STATE_DISCONNECTED
+};
+
+#if defined(_NETINET_IP_H_)
+static __inline void *
+ip_next(struct ip *iphdr)
+{
+ char *p = (char *)iphdr;
+ return (&p[iphdr->ip_hl * 4]);
+}
+#endif
+
+#if defined(_NETINET_TCP_H_)
+static __inline void *
+tcp_next(struct tcphdr *tcphdr)
+{
+ char *p = (char *)tcphdr;
+ return (&p[tcphdr->th_off * 4]);
+}
+#endif
+
+#if defined(_NETINET_UDP_H_)
+static __inline void *
+udp_next(struct udphdr *udphdr)
+{
+ return ((void *)(udphdr + 1));
+}
+#endif
+
+#endif /* !_ALIAS_LOCAL_H_ */
diff --git a/src/VBox/Devices/Network/slirp/libalias/alias_mod.c b/src/VBox/Devices/Network/slirp/libalias/alias_mod.c
new file mode 100644
index 00000000..4423bc38
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/alias_mod.c
@@ -0,0 +1,347 @@
+/*-
+ * Copyright (c) 2005 Paolo Pisati <piso@FreeBSD.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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHOR 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 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 VBOX
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netinet/libalias/alias_mod.c,v 1.3.8.1 2009/04/15 03:14:26 kensmith Exp $");
+
+#ifdef _KERNEL
+#include <sys/libkern.h>
+#include <sys/param.h>
+#include <sys/lock.h>
+#include <sys/rwlock.h>
+#else
+#include <stdio.h>
+#include <string.h>
+#include <sys/types.h>
+#include <errno.h>
+#endif
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+
+#ifdef _KERNEL
+#include <netinet/libalias/alias_local.h>
+#include <netinet/libalias/alias_mod.h>
+#else
+#include "alias_local.h"
+#include "alias_mod.h"
+#endif
+
+/* Protocol and userland module handlers chains. */
+LIST_HEAD(handler_chain, proto_handler) handler_chain = LIST_HEAD_INITIALIZER(foo);
+#else /* VBOX */
+# include <slirp.h>
+# include "alias_local.h"
+# include "alias_mod.h"
+#endif /* VBOX */
+#ifdef _KERNEL
+struct rwlock handler_rw;
+#endif
+SLIST_HEAD(dll_chain, dll) dll_chain = SLIST_HEAD_INITIALIZER(foo);
+
+#ifdef _KERNEL
+
+#define LIBALIAS_RWLOCK_INIT() \
+ rw_init(&handler_rw, "Libalias_modules_rwlock")
+#define LIBALIAS_RWLOCK_DESTROY() rw_destroy(&handler_rw)
+#define LIBALIAS_WLOCK_ASSERT() \
+ rw_assert(&handler_rw, RA_WLOCKED)
+
+static __inline void
+LIBALIAS_RLOCK(void)
+{
+ rw_rlock(&handler_rw);
+}
+
+static __inline void
+LIBALIAS_RUNLOCK(void)
+{
+ rw_runlock(&handler_rw);
+}
+
+static __inline void
+LIBALIAS_WLOCK(void)
+{
+ rw_wlock(&handler_rw);
+}
+
+static __inline void
+LIBALIAS_WUNLOCK(void)
+{
+ rw_wunlock(&handler_rw);
+}
+
+static void
+_handler_chain_init(void)
+{
+
+ if (!rw_initialized(&handler_rw))
+ LIBALIAS_RWLOCK_INIT();
+}
+
+static void
+_handler_chain_destroy(void)
+{
+
+ if (rw_initialized(&handler_rw))
+ LIBALIAS_RWLOCK_DESTROY();
+}
+
+#else /* VBOX */
+# define LIBALIAS_WLOCK_ASSERT() ;
+# define LIBALIAS_RLOCK() \
+ do { \
+ int rc = RTCritSectRwEnterShared(&pData->CsRwHandlerChain); \
+ AssertRC(rc); \
+ } while (0)
+# define LIBALIAS_RUNLOCK() \
+ do { \
+ int rc = RTCritSectRwLeaveShared(&pData->CsRwHandlerChain); \
+ AssertRC(rc); \
+ } while (0)
+# define LIBALIAS_WLOCK() \
+ do { \
+ int rc = RTCritSectRwEnterExcl(&pData->CsRwHandlerChain); \
+ AssertRC(rc); \
+ } while (0)
+# define LIBALIAS_WUNLOCK() \
+ do { \
+ int rc = RTCritSectRwLeaveExcl(&pData->CsRwHandlerChain); \
+ AssertRC(rc); \
+ } while (0)
+# define _handler_chain_init() ;
+# define _handler_chain_destroy() ;
+#endif
+
+void
+handler_chain_init(void)
+{
+ _handler_chain_init();
+}
+
+void
+handler_chain_destroy(void)
+{
+ _handler_chain_destroy();
+}
+
+static int
+#ifdef VBOX
+_attach_handler(PNATState pData, struct proto_handler *p)
+#else
+_attach_handler(struct proto_handler *p)
+#endif
+{
+ struct proto_handler *b = NULL, *handler_chain_tail = NULL;
+
+ LIBALIAS_WLOCK_ASSERT();
+ LIST_FOREACH(b, &handler_chain, entries) {
+ if ((b->pri == p->pri) &&
+ (b->dir == p->dir) &&
+ (b->proto == p->proto))
+ return (EEXIST); /* Priority conflict. */
+ if (b->pri > p->pri) {
+ LIST_INSERT_BEFORE(b, p, entries);
+ return (0);
+ }
+
+ /* If the conditions above do not work, we should keep the last
+ * element of the list in order to insert *p right after it. */
+ handler_chain_tail = b;
+ }
+ /* End of list or found right position, inserts here. */
+ if (handler_chain_tail)
+ LIST_INSERT_AFTER(handler_chain_tail, p, entries);
+ else
+ LIST_INSERT_HEAD(&handler_chain, p, entries);
+ return (0);
+}
+
+static int
+#ifdef VBOX
+_detach_handler(PNATState pData, struct proto_handler *p)
+#else
+_detach_handler(struct proto_handler *p)
+#endif
+{
+ struct proto_handler *b, *b_tmp;;
+
+ LIBALIAS_WLOCK_ASSERT();
+ LIST_FOREACH_SAFE(b, &handler_chain, entries, b_tmp) {
+ if (b == p) {
+ LIST_REMOVE(b, entries);
+ return (0);
+ }
+ }
+ return (ENOENT); /* Handler not found. */
+}
+
+int
+#ifdef VBOX
+LibAliasAttachHandlers(PNATState pData, struct proto_handler *_p)
+#else
+LibAliasAttachHandlers(struct proto_handler *_p)
+#endif
+{
+ int i, error = -1;
+
+ LIBALIAS_WLOCK();
+ for (i=0; 1; i++) {
+ if (*((int *)&_p[i]) == EOH)
+ break;
+#ifdef VBOX
+ error = _attach_handler(pData, &_p[i]);
+#else
+ error = _attach_handler(&_p[i]);
+#endif
+ if (error != 0)
+ break;
+ }
+ LIBALIAS_WUNLOCK();
+ return (error);
+}
+
+int
+#ifdef VBOX
+LibAliasDetachHandlers(PNATState pData, struct proto_handler *_p)
+#else
+LibAliasDetachHandlers(struct proto_handler *_p)
+#endif
+{
+ int i, error = -1;
+
+ LIBALIAS_WLOCK();
+ for (i=0; 1; i++) {
+ if (*((int *)&_p[i]) == EOH)
+ break;
+#ifdef VBOX
+ error = _detach_handler(pData, &_p[i]);
+#else
+ error = _detach_handler(&_p[i]);
+#endif
+ if (error != 0)
+ break;
+ }
+ LIBALIAS_WUNLOCK();
+ return (error);
+}
+
+int
+#ifdef VBOX
+detach_handler(PNATState pData, struct proto_handler *_p)
+#else
+detach_handler(struct proto_handler *_p)
+#endif
+{
+ int error = -1;
+
+ LIBALIAS_WLOCK();
+#ifdef VBOX
+ error = _detach_handler(pData, _p);
+#else
+ error = _detach_handler(_p);
+#endif
+ LIBALIAS_WUNLOCK();
+ return (error);
+}
+
+int
+find_handler(int8_t dir, int8_t proto, struct libalias *la, struct ip *pip,
+ struct alias_data *ad)
+{
+#ifdef VBOX
+ PNATState pData = la->pData;
+#endif
+ struct proto_handler *p;
+ int error = ENOENT;
+
+ LIBALIAS_RLOCK();
+
+ LIST_FOREACH(p, &handler_chain, entries) {
+ if ((p->dir & dir) && (p->proto & proto))
+ if (p->fingerprint(la, pip, ad) == 0) {
+ error = p->protohandler(la, pip, ad);
+ break;
+ }
+ }
+ LIBALIAS_RUNLOCK();
+ return (error);
+}
+
+struct proto_handler *
+#ifdef VBOX
+first_handler(PNATState pData)
+#else
+first_handler(void)
+#endif
+{
+
+ return (LIST_FIRST(&handler_chain));
+}
+
+/* Dll manipulation code - this code is not thread safe... */
+
+int
+attach_dll(struct dll *p)
+{
+ struct dll *b;
+
+ SLIST_FOREACH(b, &dll_chain, next) {
+ if (!strncmp(b->name, p->name, DLL_LEN))
+ return (EEXIST); /* Dll name conflict. */
+ }
+ SLIST_INSERT_HEAD(&dll_chain, p, next);
+ return (0);
+}
+
+void *
+detach_dll(char *p)
+{
+ struct dll *b = NULL, *b_tmp;
+ void *error = NULL;
+
+ SLIST_FOREACH_SAFE(b, &dll_chain, next, b_tmp)
+ if (!strncmp(b->name, p, DLL_LEN)) {
+ SLIST_REMOVE(&dll_chain, b, dll, next);
+ error = b;
+ break;
+ }
+ return (error);
+}
+
+struct dll *
+walk_dll_chain(void)
+{
+ struct dll *t;
+
+ t = SLIST_FIRST(&dll_chain);
+ if (t == NULL)
+ return (NULL);
+ SLIST_REMOVE_HEAD(&dll_chain, next);
+ return (t);
+}
diff --git a/src/VBox/Devices/Network/slirp/libalias/alias_mod.h b/src/VBox/Devices/Network/slirp/libalias/alias_mod.h
new file mode 100644
index 00000000..74a52137
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/alias_mod.h
@@ -0,0 +1,169 @@
+/*-
+ * Copyright (c) 2005 Paolo Pisati <piso@FreeBSD.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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHOR 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 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.
+ *
+ * $FreeBSD: src/sys/netinet/libalias/alias_mod.h,v 1.1.8.1 2009/04/15 03:14:26 kensmith Exp $
+ */
+
+/*
+ * Alias_mod.h defines the outside world interfaces for the packet aliasing
+ * modular framework
+ */
+
+#ifndef _ALIAS_MOD_H_
+#define _ALIAS_MOD_H_
+
+#if defined(_KERNEL) && !defined(VBOX)
+MALLOC_DECLARE(M_ALIAS);
+
+/* Use kernel allocator. */
+#if defined(_SYS_MALLOC_H_)
+#define malloc(x) malloc(x, M_ALIAS, M_NOWAIT|M_ZERO)
+#define calloc(x, n) malloc(x*n)
+#define free(x) free(x, M_ALIAS)
+#endif
+#else /* VBOX */
+# ifdef RT_OS_WINDOWS
+# undef IN
+# undef OUT
+# endif /* RT_OS_WINDOWS */
+#endif /* VBOX */
+
+/* Protocol handlers struct & function. */
+
+/* Packet flow direction. */
+#define IN 1
+#define OUT 2
+
+/* Working protocol. */
+#define IP 1
+#define TCP 2
+#define UDP 4
+
+/*
+ * Data passed to protocol handler module, it must be filled
+ * right before calling find_handler() to determine which
+ * module is elegible to be called.
+ */
+
+struct alias_data {
+ struct alias_link *lnk;
+ struct in_addr *oaddr; /* Original address. */
+ struct in_addr *aaddr; /* Alias address. */
+ uint16_t *aport; /* Alias port. */
+ uint16_t *sport, *dport; /* Source & destination port */
+ uint16_t maxpktsize; /* Max packet size. */
+};
+
+/*
+ * This structure contains all the information necessary to make
+ * a protocol handler correctly work.
+ */
+
+struct proto_handler {
+ u_int pri; /* Handler priority. */
+ int16_t dir; /* Flow direction. */
+ uint8_t proto; /* Working protocol. */
+ int (*fingerprint)(struct libalias *la, /* Fingerprint * function. */
+ struct ip *pip, struct alias_data *ah);
+ int (*protohandler)(struct libalias *la, /* Aliasing * function. */
+ struct ip *pip, struct alias_data *ah);
+ LIST_ENTRY(proto_handler) entries;
+};
+
+
+/*
+ * Used only in userland when libalias needs to keep track of all
+ * module loaded. In kernel land (kld mode) we don't need to care
+ * care about libalias modules cause it's kld to do it for us.
+ */
+
+#define DLL_LEN 32
+struct dll {
+ char name[DLL_LEN]; /* Name of module. */
+ void *handle; /*
+ * Ptr to shared obj obtained through
+ * dlopen() - use this ptr to get access
+ * to any symbols from a loaded module
+ * via dlsym().
+ */
+ SLIST_ENTRY(dll) next;
+};
+
+/* Functions used with protocol handlers. */
+
+void handler_chain_init(void);
+void handler_chain_destroy(void);
+#ifdef VBOX
+int LibAliasAttachHandlers(PNATState pData, struct proto_handler *);
+int LibAliasDetachHandlers(PNATState pData, struct proto_handler *);
+int detach_handler(PNATState pData, struct proto_handler *);
+struct proto_handler *first_handler(PNATState pData);
+#else
+int LibAliasAttachHandlers(struct proto_handler *);
+int LibAliasDetachHandlers(struct proto_handler *);
+int detach_handler(struct proto_handler *);
+struct proto_handler *first_handler(void);
+#endif
+int find_handler(int8_t, int8_t, struct libalias *,
+ struct ip *, struct alias_data *);
+
+/* Functions used with dll module. */
+
+void dll_chain_init(void);
+void dll_chain_destroy(void);
+int attach_dll(struct dll *);
+void *detach_dll(char *);
+struct dll *walk_dll_chain(void);
+
+/* End of handlers. */
+#define EOH -1
+
+/*
+ * Some defines borrowed from sys/module.h used to compile a kld
+ * in userland as a shared lib.
+ */
+
+#ifndef _KERNEL
+typedef enum modeventtype {
+ MOD_LOAD,
+ MOD_UNLOAD,
+ MOD_SHUTDOWN,
+ MOD_QUIESCE
+} modeventtype_t;
+
+typedef struct module *module_t;
+typedef int (*modeventhand_t)(module_t, int /* modeventtype_t */, void *);
+
+/*
+ * Struct for registering modules statically via SYSINIT.
+ */
+typedef struct moduledata {
+ const char *name; /* module name */
+ modeventhand_t evhand; /* event handler */
+ void *priv; /* extra data */
+} moduledata_t;
+#endif
+
+#endif /* !_ALIAS_MOD_H_ */
diff --git a/src/VBox/Devices/Network/slirp/libalias/alias_nbt.c b/src/VBox/Devices/Network/slirp/libalias/alias_nbt.c
new file mode 100644
index 00000000..886433ae
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/alias_nbt.c
@@ -0,0 +1,955 @@
+/*-
+ * Written by Atsushi Murai <amurai@spec.co.jp>
+ * Copyright (c) 1998, System Planning and Engineering Co.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHOR 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 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.
+ * TODO:
+ * oClean up.
+ * oConsidering for word alignment for other platform.
+ */
+
+#ifndef VBOX
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netinet/libalias/alias_nbt.c,v 1.20.8.1 2009/04/15 03:14:26 kensmith Exp $");
+
+/*
+ alias_nbt.c performs special processing for NetBios over TCP/IP
+ sessions by UDP.
+
+ Initial version: May, 1998 (Atsushi Murai <amurai@spec.co.jp>)
+
+ See HISTORY file for record of revisions.
+*/
+
+/* Includes */
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#else
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#endif
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/udp.h>
+
+#ifdef _KERNEL
+#include <netinet/libalias/alias_local.h>
+#include <netinet/libalias/alias_mod.h>
+#else
+#include "alias_local.h"
+#include "alias_mod.h"
+#endif
+#else /* VBOX */
+# include <iprt/ctype.h>
+# include <slirp.h>
+# include "alias_local.h"
+# include "alias_mod.h"
+# define isprint RT_C_IS_PRINT
+#endif /* VBOX */
+
+#define NETBIOS_NS_PORT_NUMBER 137
+#define NETBIOS_DGM_PORT_NUMBER 138
+
+static int
+AliasHandleUdpNbt(struct libalias *, struct ip *, struct alias_link *,
+ struct in_addr *, u_short);
+
+static int
+AliasHandleUdpNbtNS(struct libalias *, struct ip *, struct alias_link *,
+ struct in_addr *, u_short *, struct in_addr *, u_short *);
+static int
+fingerprint1(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+#ifdef VBOX
+ NOREF(la);
+ NOREF(pip);
+#endif
+ if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL ||
+ ah->aaddr == NULL || ah->aport == NULL)
+ return (-1);
+ if (ntohs(*ah->dport) == NETBIOS_DGM_PORT_NUMBER
+ || ntohs(*ah->sport) == NETBIOS_DGM_PORT_NUMBER)
+ return (0);
+ return (-1);
+}
+
+static int
+protohandler1(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ AliasHandleUdpNbt(la, pip, ah->lnk, ah->aaddr, *ah->aport);
+ return (0);
+}
+
+static int
+fingerprint2(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+#ifdef VBOX
+ NOREF(la);
+ NOREF(pip);
+#endif
+ if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL ||
+ ah->aaddr == NULL || ah->aport == NULL)
+ return (-1);
+ if (ntohs(*ah->dport) == NETBIOS_NS_PORT_NUMBER
+ || ntohs(*ah->sport) == NETBIOS_NS_PORT_NUMBER)
+ return (0);
+ return (-1);
+}
+
+static int
+protohandler2in(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ AliasHandleUdpNbtNS(la, pip, ah->lnk, ah->aaddr, ah->aport,
+ ah->oaddr, ah->dport);
+ return (0);
+}
+
+static int
+protohandler2out(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ AliasHandleUdpNbtNS(la, pip, ah->lnk, &pip->ip_src, ah->sport,
+ ah->aaddr, ah->aport);
+ return (0);
+}
+
+/* Kernel module definition. */
+#ifndef VBOX
+struct proto_handler handlers[] = {
+ {
+ .pri = 130,
+ .dir = IN|OUT,
+ .proto = UDP,
+ .fingerprint = &fingerprint1,
+ .protohandler = &protohandler1
+ },
+ {
+ .pri = 140,
+ .dir = IN,
+ .proto = UDP,
+ .fingerprint = &fingerprint2,
+ .protohandler = &protohandler2in
+ },
+ {
+ .pri = 140,
+ .dir = OUT,
+ .proto = UDP,
+ .fingerprint = &fingerprint2,
+ .protohandler = &protohandler2out
+ },
+ { EOH }
+};
+#else /* VBOX */
+#define handlers pData->nbt_module
+#endif /* VBOX */
+
+#ifndef VBOX
+static int
+mod_handler(module_t mod, int type, void *data)
+#else /* VBOX */
+static int nbt_alias_handler(PNATState pData, int type);
+
+int
+nbt_alias_load(PNATState pData)
+{
+ return nbt_alias_handler(pData, MOD_LOAD);
+}
+
+int
+nbt_alias_unload(PNATState pData)
+{
+ return nbt_alias_handler(pData, MOD_UNLOAD);
+}
+static int
+nbt_alias_handler(PNATState pData, int type)
+#endif /* VBOX */
+{
+ int error;
+#ifdef VBOX
+ if (handlers == NULL)
+ handlers = RTMemAllocZ(4 * sizeof(struct proto_handler));
+ handlers[0].pri = 130;
+ handlers[0].dir = IN|OUT;
+ handlers[0].proto = UDP;
+ handlers[0].fingerprint = &fingerprint1;
+ handlers[0].protohandler = &protohandler1;
+
+
+ handlers[1].pri = 140;
+ handlers[1].dir = IN;
+ handlers[1].proto = UDP;
+ handlers[1].fingerprint = &fingerprint2;
+ handlers[1].protohandler = &protohandler2in;
+
+
+ handlers[2].pri = 140;
+ handlers[2].dir = OUT;
+ handlers[2].proto = UDP;
+ handlers[2].fingerprint = &fingerprint2;
+ handlers[2].protohandler = &protohandler2out;
+
+ handlers[3].pri = (u_int)EOH;
+#endif /* VBOX */
+
+ switch (type) {
+ case MOD_LOAD:
+ error = 0;
+#ifdef VBOX
+ LibAliasAttachHandlers(pData, handlers);
+#else
+ LibAliasAttachHandlers(handlers);
+#endif
+ break;
+ case MOD_UNLOAD:
+ error = 0;
+#ifdef VBOX
+ LibAliasDetachHandlers(pData, handlers);
+ RTMemFree(handlers);
+ handlers = NULL;
+#else
+ LibAliasDetachHandlers(handlers);
+#endif
+ break;
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
+
+#ifndef VBOX
+#ifdef _KERNEL
+static
+#endif
+moduledata_t alias_mod = {
+ "alias_nbt", mod_handler, NULL
+};
+#endif /* !VBOX */
+
+#ifdef _KERNEL
+DECLARE_MODULE(alias_nbt, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
+MODULE_VERSION(alias_nbt, 1);
+MODULE_DEPEND(alias_nbt, libalias, 1, 1, 1);
+#endif
+
+typedef struct {
+ struct in_addr oldaddr;
+ u_short oldport;
+ struct in_addr newaddr;
+ u_short newport;
+ u_short *uh_sum;
+} NBTArguments;
+
+typedef struct {
+ unsigned char type;
+ unsigned char flags;
+ u_short id;
+ struct in_addr source_ip;
+ u_short source_port;
+ u_short len;
+ u_short offset;
+} NbtDataHeader;
+
+#define OpQuery 0
+#define OpUnknown 4
+#define OpRegist 5
+#define OpRelease 6
+#define OpWACK 7
+#define OpRefresh 8
+typedef struct {
+#ifndef VBOX
+ u_short nametrid;
+ u_short dir: 1, opcode:4, nmflags:7, rcode:4;
+#else
+ unsigned nametrid:16;
+ unsigned dir: 1, opcode:4, nmflags:7, rcode:4;
+#endif
+ u_short qdcount;
+ u_short ancount;
+ u_short nscount;
+ u_short arcount;
+} NbtNSHeader;
+AssertCompileSize(NbtNSHeader, 12);
+
+#define FMT_ERR 0x1
+#define SRV_ERR 0x2
+#define IMP_ERR 0x4
+#define RFS_ERR 0x5
+#define ACT_ERR 0x6
+#define CFT_ERR 0x7
+
+
+#ifdef LIBALIAS_DEBUG
+static void
+PrintRcode(u_char rcode)
+{
+
+ switch (rcode) {
+ case FMT_ERR:
+ printf("\nFormat Error.");
+ case SRV_ERR:
+ printf("\nSever failure.");
+ case IMP_ERR:
+ printf("\nUnsupported request error.\n");
+ case RFS_ERR:
+ printf("\nRefused error.\n");
+ case ACT_ERR:
+ printf("\nActive error.\n");
+ case CFT_ERR:
+ printf("\nName in conflict error.\n");
+ default:
+ printf("\n?%c?=%0x\n", '?', rcode);
+
+ }
+}
+
+#endif
+
+
+/* Handling Name field */
+static u_char *
+AliasHandleName(u_char * p, char *pmax)
+{
+
+ u_char *s;
+#ifdef LIBALIAS_DEBUG
+ u_char c;
+#endif
+ int compress;
+
+ /* Following length field */
+
+ if (p == NULL || (char *)p >= pmax)
+ return (NULL);
+
+ if (*p & 0xc0) {
+ p = p + 2;
+ if ((char *)p > pmax)
+ return (NULL);
+ return ((u_char *) p);
+ }
+ while ((*p & 0x3f) != 0x00) {
+ s = p + 1;
+ if (*p == 0x20)
+ compress = 1;
+ else
+ compress = 0;
+
+ /* Get next length field */
+ p = (u_char *) (p + (*p & 0x3f) + 1);
+ if ((char *)p > pmax) {
+ p = NULL;
+ break;
+ }
+#ifdef LIBALIAS_DEBUG
+ printf(":");
+#endif
+ while (s < p) {
+ if (compress == 1) {
+#ifdef LIBALIAS_DEBUG
+ c = (u_char) (((((*s & 0x0f) << 4) | (*(s + 1) & 0x0f)) - 0x11));
+ if (isprint(c))
+ printf("%c", c);
+ else
+ printf("<0x%02x>", c);
+#endif
+ s += 2;
+ } else {
+#ifdef LIBALIAS_DEBUG
+ printf("%c", *s);
+#endif
+ s++;
+ }
+ }
+#ifdef LIBALIAS_DEBUG
+ printf(":");
+ fflush(stdout);
+#endif
+ }
+
+ /* Set up to out of Name field */
+ if (p == NULL || (char *)p >= pmax)
+ p = NULL;
+ else
+ p++;
+ return ((u_char *) p);
+}
+
+/*
+ * NetBios Datagram Handler (IP/UDP)
+ */
+#define DGM_DIRECT_UNIQ 0x10
+#define DGM_DIRECT_GROUP 0x11
+#define DGM_BROADCAST 0x12
+#define DGM_ERROR 0x13
+#define DGM_QUERY 0x14
+#define DGM_POSITIVE_RES 0x15
+#define DGM_NEGATIVE_RES 0x16
+
+static int
+AliasHandleUdpNbt(
+ struct libalias *la,
+ struct ip *pip, /* IP packet to examine/patch */
+ struct alias_link *lnk,
+ struct in_addr *alias_address,
+ u_short alias_port
+)
+{
+ struct udphdr *uh;
+ NbtDataHeader *ndh;
+ u_char *p = NULL;
+ char *pmax;
+
+ (void)la;
+ (void)lnk;
+
+ /* Calculate data length of UDP packet */
+ uh = (struct udphdr *)ip_next(pip);
+ pmax = (char *)uh + ntohs(uh->uh_ulen);
+
+ /* IP header has been verified, cross-check uh_ulen */
+ if (RT_UNLIKELY(pmax != (char *)pip + ntohs(pip->ip_len)))
+ return (-1);
+
+ ndh = (NbtDataHeader *)udp_next(uh);
+ if ((char *)(ndh + 1) > pmax)
+ return (-1);
+#ifdef LIBALIAS_DEBUG
+ printf("\nType=%02x,", ndh->type);
+#endif
+ switch (ndh->type) {
+ case DGM_DIRECT_UNIQ:
+ case DGM_DIRECT_GROUP:
+ case DGM_BROADCAST:
+ p = (u_char *) ndh + 14;
+ p = AliasHandleName(p, pmax); /* Source Name */
+ p = AliasHandleName(p, pmax); /* Destination Name */
+ break;
+ case DGM_ERROR:
+ p = (u_char *) ndh + 11;
+ break;
+ case DGM_QUERY:
+ case DGM_POSITIVE_RES:
+ case DGM_NEGATIVE_RES:
+ p = (u_char *) ndh + 10;
+ p = AliasHandleName(p, pmax); /* Destination Name */
+ break;
+ }
+ if (p == NULL || (char *)p > pmax)
+ p = NULL;
+#ifdef LIBALIAS_DEBUG
+ printf("%s:%d-->", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port));
+#endif
+ /* Doing an IP address and Port number Translation */
+ if (uh->uh_sum != 0) {
+ int acc;
+ u_short *sptr;
+
+ acc = ndh->source_port;
+ acc -= alias_port;
+ sptr = (u_short *) & (ndh->source_ip);
+ acc += *sptr++;
+ acc += *sptr;
+ sptr = (u_short *) alias_address;
+ acc -= *sptr++;
+ acc -= *sptr;
+ ADJUST_CHECKSUM(acc, uh->uh_sum);
+ }
+ ndh->source_ip = *alias_address;
+ ndh->source_port = alias_port;
+#ifdef LIBALIAS_DEBUG
+ printf("%s:%d\n", inet_ntoa(ndh->source_ip), ntohs(ndh->source_port));
+ fflush(stdout);
+#endif
+ return ((p == NULL) ? -1 : 0);
+}
+
+/* Question Section */
+#define QS_TYPE_NB 0x0020
+#define QS_TYPE_NBSTAT 0x0021
+#define QS_CLAS_IN 0x0001
+typedef struct {
+ u_short type; /* The type of Request */
+ u_short class; /* The class of Request */
+} NBTNsQuestion;
+
+static u_char *
+AliasHandleQuestion(
+ u_short count,
+ NBTNsQuestion * q,
+ char *pmax,
+ NBTArguments * nbtarg)
+{
+
+ (void)nbtarg;
+
+ while (count != 0) {
+ /* Name Filed */
+ q = (NBTNsQuestion *) AliasHandleName((u_char *) q, pmax);
+
+ if (q == NULL || (char *)(q + 1) > pmax) {
+ q = NULL;
+ break;
+ }
+ /* Type and Class filed */
+ switch (ntohs(q->type)) {
+ case QS_TYPE_NB:
+ case QS_TYPE_NBSTAT:
+ q = q + 1;
+ break;
+ default:
+#ifdef LIBALIAS_DEBUG
+ printf("\nUnknown Type on Question %0x\n", ntohs(q->type));
+#endif
+ break;
+ }
+ count--;
+ }
+
+ /* Set up to out of Question Section */
+ return ((u_char *) q);
+}
+
+/* Resource Record */
+#define RR_TYPE_A 0x0001
+#define RR_TYPE_NS 0x0002
+#define RR_TYPE_NULL 0x000a
+#define RR_TYPE_NB 0x0020
+#define RR_TYPE_NBSTAT 0x0021
+#define RR_CLAS_IN 0x0001
+#define SizeOfNsResource 8
+typedef struct {
+ u_short type;
+ u_short class;
+ unsigned int ttl;
+ u_short rdlen;
+} NBTNsResource;
+
+#define SizeOfNsRNB 6
+typedef struct {
+#ifndef VBOX
+ u_short g: 1 , ont:2, resv:13;
+#else
+ unsigned g: 1 , ont:2, resv:13;
+#endif
+ struct in_addr addr;
+} NBTNsRNB;
+AssertCompileSize(NBTNsRNB, 8);
+
+static u_char *
+AliasHandleResourceNB(
+ NBTNsResource * q,
+ char *pmax,
+ NBTArguments * nbtarg)
+{
+ NBTNsRNB *nb;
+ u_short bcount;
+
+ if (q == NULL || (char *)(q + 1) > pmax)
+ return (NULL);
+ /* Check out a length */
+ bcount = ntohs(q->rdlen);
+
+ /* Forward to Resource NB position */
+ nb = (NBTNsRNB *) ((u_char *) q + SizeOfNsResource);
+
+ /* Processing all in_addr array */
+#ifdef LIBALIAS_DEBUG
+ printf("NB rec[%s", inet_ntoa(nbtarg->oldaddr));
+ printf("->%s, %dbytes] ", inet_ntoa(nbtarg->newaddr), bcount);
+#endif
+ while (nb != NULL && bcount != 0) {
+ if ((char *)(nb + 1) > pmax) {
+ nb = NULL;
+ break;
+ }
+#ifdef LIBALIAS_DEBUG
+ printf("<%s>", inet_ntoa(nb->addr));
+#endif
+ if (!bcmp(&nbtarg->oldaddr, &nb->addr, sizeof(struct in_addr))) {
+ if (*nbtarg->uh_sum != 0) {
+ int acc;
+ u_short *sptr;
+
+ sptr = (u_short *) & (nb->addr);
+ acc = *sptr++;
+ acc += *sptr;
+ sptr = (u_short *) & (nbtarg->newaddr);
+ acc -= *sptr++;
+ acc -= *sptr;
+ ADJUST_CHECKSUM(acc, *nbtarg->uh_sum);
+ }
+ nb->addr = nbtarg->newaddr;
+#ifdef LIBALIAS_DEBUG
+ printf("O");
+#endif
+ }
+#ifdef LIBALIAS_DEBUG
+ else {
+ printf(".");
+ }
+#endif
+ nb = (NBTNsRNB *) ((u_char *) nb + SizeOfNsRNB);
+ bcount -= SizeOfNsRNB;
+ }
+ if (nb == NULL || (char *)(nb + 1) > pmax) {
+ nb = NULL;
+ }
+ return ((u_char *) nb);
+}
+
+#define SizeOfResourceA 6
+typedef struct {
+ struct in_addr addr;
+} NBTNsResourceA;
+
+static u_char *
+AliasHandleResourceA(
+ NBTNsResource * q,
+ char *pmax,
+ NBTArguments * nbtarg)
+{
+ NBTNsResourceA *a;
+ u_short bcount;
+
+ if (q == NULL || (char *)(q + 1) > pmax)
+ return (NULL);
+
+ /* Forward to Resource A position */
+ a = (NBTNsResourceA *) ((u_char *) q + sizeof(NBTNsResource));
+
+ /* Check out of length */
+ bcount = ntohs(q->rdlen);
+
+ /* Processing all in_addr array */
+#ifdef LIBALIAS_DEBUG
+ printf("Arec [%s", inet_ntoa(nbtarg->oldaddr));
+ printf("->%s]", inet_ntoa(nbtarg->newaddr));
+#endif
+ while (bcount != 0) {
+ if (a == NULL || (char *)(a + 1) > pmax)
+ return (NULL);
+#ifdef LIBALIAS_DEBUG
+ printf("..%s", inet_ntoa(a->addr));
+#endif
+ if (!bcmp(&nbtarg->oldaddr, &a->addr, sizeof(struct in_addr))) {
+ if (*nbtarg->uh_sum != 0) {
+ int acc;
+ u_short *sptr;
+
+ sptr = (u_short *) & (a->addr); /* Old */
+ acc = *sptr++;
+ acc += *sptr;
+ sptr = (u_short *) & nbtarg->newaddr; /* New */
+ acc -= *sptr++;
+ acc -= *sptr;
+ ADJUST_CHECKSUM(acc, *nbtarg->uh_sum);
+ }
+ a->addr = nbtarg->newaddr;
+ }
+ a++; /* XXXX */
+ bcount -= SizeOfResourceA;
+ }
+ if (a == NULL || (char *)(a + 1) > pmax)
+ a = NULL;
+ return ((u_char *) a);
+}
+
+typedef struct {
+#ifndef VBOX
+ u_short opcode:4, flags:8, resv:4;
+#else
+ u_short hidden; /* obviously not needed */
+#endif
+} NBTNsResourceNULL;
+AssertCompileSize(NBTNsResourceNULL, 2);
+
+static u_char *
+AliasHandleResourceNULL(
+ NBTNsResource * q,
+ char *pmax,
+ NBTArguments * nbtarg)
+{
+ NBTNsResourceNULL *n;
+ u_short bcount;
+
+ (void)nbtarg;
+
+ if (q == NULL || (char *)(q + 1) > pmax)
+ return (NULL);
+
+ /* Forward to Resource NULL position */
+ n = (NBTNsResourceNULL *) ((u_char *) q + sizeof(NBTNsResource));
+
+ /* Check out of length */
+ bcount = ntohs(q->rdlen);
+
+ /* Processing all in_addr array */
+ while (bcount != 0) {
+ if ((char *)(n + 1) > pmax) {
+ n = NULL;
+ break;
+ }
+ n++;
+ bcount -= sizeof(NBTNsResourceNULL);
+ }
+ if ((char *)(n + 1) > pmax)
+ n = NULL;
+
+ return ((u_char *) n);
+}
+
+static u_char *
+AliasHandleResourceNS(
+ NBTNsResource * q,
+ char *pmax,
+ NBTArguments * nbtarg)
+{
+ NBTNsResourceNULL *n;
+ u_short bcount;
+
+ (void)nbtarg;
+
+ if (q == NULL || (char *)(q + 1) > pmax)
+ return (NULL);
+
+ /* Forward to Resource NULL position */
+ n = (NBTNsResourceNULL *) ((u_char *) q + sizeof(NBTNsResource));
+
+ /* Check out of length */
+ bcount = ntohs(q->rdlen);
+
+ /* Resource Record Name Filed */
+ q = (NBTNsResource *) AliasHandleName((u_char *) n, pmax); /* XXX */
+
+ if (q == NULL || (char *)((u_char *) n + bcount) > pmax)
+ return (NULL);
+ else
+ return ((u_char *) n + bcount);
+}
+
+typedef struct {
+ u_short numnames;
+} NBTNsResourceNBSTAT;
+
+static u_char *
+AliasHandleResourceNBSTAT(
+ NBTNsResource * q,
+ char *pmax,
+ NBTArguments * nbtarg)
+{
+ NBTNsResourceNBSTAT *n;
+ u_short bcount;
+
+ (void)nbtarg;
+
+ if (q == NULL || (char *)(q + 1) > pmax)
+ return (NULL);
+
+ /* Forward to Resource NBSTAT position */
+ n = (NBTNsResourceNBSTAT *) ((u_char *) q + sizeof(NBTNsResource));
+
+ /* Check out of length */
+ bcount = ntohs(q->rdlen);
+
+ if ((char *)((u_char *) n + bcount) > pmax)
+ return (NULL);
+ else
+ return ((u_char *) n + bcount);
+}
+
+static u_char *
+AliasHandleResource(
+ u_short count,
+ NBTNsResource * q,
+ char *pmax,
+ NBTArguments
+ * nbtarg)
+{
+ while (count != 0) {
+ /* Resource Record Name Filed */
+ q = (NBTNsResource *) AliasHandleName((u_char *) q, pmax);
+
+ if (q == NULL || (char *)(q + 1) > pmax)
+ break;
+#ifdef LIBALIAS_DEBUG
+ printf("type=%02x, count=%d\n", ntohs(q->type), count);
+#endif
+
+ /* Type and Class filed */
+ switch (ntohs(q->type)) {
+ case RR_TYPE_NB:
+ q = (NBTNsResource *) AliasHandleResourceNB(
+ q,
+ pmax,
+ nbtarg
+ );
+ break;
+ case RR_TYPE_A:
+ q = (NBTNsResource *) AliasHandleResourceA(
+ q,
+ pmax,
+ nbtarg
+ );
+ break;
+ case RR_TYPE_NS:
+ q = (NBTNsResource *) AliasHandleResourceNS(
+ q,
+ pmax,
+ nbtarg
+ );
+ break;
+ case RR_TYPE_NULL:
+ q = (NBTNsResource *) AliasHandleResourceNULL(
+ q,
+ pmax,
+ nbtarg
+ );
+ break;
+ case RR_TYPE_NBSTAT:
+ q = (NBTNsResource *) AliasHandleResourceNBSTAT(
+ q,
+ pmax,
+ nbtarg
+ );
+ break;
+ default:
+#ifdef LIBALIAS_DEBUG
+ printf(
+ "\nUnknown Type of Resource %0x\n",
+ ntohs(q->type)
+ );
+ fflush(stdout);
+#endif
+ break;
+ }
+ count--;
+ }
+ return ((u_char *) q);
+}
+
+static int
+AliasHandleUdpNbtNS(
+ struct libalias *la,
+ struct ip *pip, /* IP packet to examine/patch */
+ struct alias_link *lnk,
+ struct in_addr *alias_address,
+ u_short * alias_port,
+ struct in_addr *original_address,
+ u_short * original_port)
+{
+ struct udphdr *uh;
+ NbtNSHeader *nsh;
+ u_char *p;
+ char *pmax;
+ NBTArguments nbtarg;
+
+ (void)la;
+ (void)lnk;
+
+ /* Set up Common Parameter */
+ nbtarg.oldaddr = *alias_address;
+ nbtarg.oldport = *alias_port;
+ nbtarg.newaddr = *original_address;
+ nbtarg.newport = *original_port;
+
+ /* Calculate data length of UDP packet */
+ uh = (struct udphdr *)ip_next(pip);
+ nbtarg.uh_sum = &(uh->uh_sum);
+ nsh = (NbtNSHeader *)udp_next(uh);
+ p = (u_char *) (nsh + 1);
+ pmax = (char *)uh + ntohs(uh->uh_ulen);
+
+ /* IP header has been verified, cross-check uh_ulen */
+ if (RT_UNLIKELY(pmax != (char *)pip + ntohs(pip->ip_len)))
+ return (-1);
+
+ if ((char *)(nsh + 1) > pmax)
+ return (-1);
+
+#ifdef LIBALIAS_DEBUG
+ printf(" [%s] ID=%02x, op=%01x, flag=%02x, rcode=%01x, qd=%04x"
+ ", an=%04x, ns=%04x, ar=%04x, [%d]-->",
+ nsh->dir ? "Response" : "Request",
+ nsh->nametrid,
+ nsh->opcode,
+ nsh->nmflags,
+ nsh->rcode,
+ ntohs(nsh->qdcount),
+ ntohs(nsh->ancount),
+ ntohs(nsh->nscount),
+ ntohs(nsh->arcount),
+ (u_char *) p - (u_char *) nsh
+ );
+#endif
+
+ /* Question Entries */
+ if (ntohs(nsh->qdcount) != 0) {
+ p = AliasHandleQuestion(
+ ntohs(nsh->qdcount),
+ (NBTNsQuestion *) p,
+ pmax,
+ &nbtarg
+ );
+ }
+ /* Answer Resource Records */
+ if (ntohs(nsh->ancount) != 0) {
+ p = AliasHandleResource(
+ ntohs(nsh->ancount),
+ (NBTNsResource *) p,
+ pmax,
+ &nbtarg
+ );
+ }
+ /* Authority Resource Recodrs */
+ if (ntohs(nsh->nscount) != 0) {
+ p = AliasHandleResource(
+ ntohs(nsh->nscount),
+ (NBTNsResource *) p,
+ pmax,
+ &nbtarg
+ );
+ }
+ /* Additional Resource Recodrs */
+ if (ntohs(nsh->arcount) != 0) {
+ p = AliasHandleResource(
+ ntohs(nsh->arcount),
+ (NBTNsResource *) p,
+ pmax,
+ &nbtarg
+ );
+ }
+#ifdef LIBALIAS_DEBUG
+ PrintRcode(nsh->rcode);
+#endif
+ return ((p == NULL) ? -1 : 0);
+}
diff --git a/src/VBox/Devices/Network/slirp/libalias/alias_old.c b/src/VBox/Devices/Network/slirp/libalias/alias_old.c
new file mode 100644
index 00000000..7a85be1b
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/alias_old.c
@@ -0,0 +1,216 @@
+/*-
+ * Copyright (c) 2004 Poul-Henning Kamp <phk@FreeBSD.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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHOR 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 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 <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netinet/libalias/alias_old.c,v 1.8.8.1 2009/04/15 03:14:26 kensmith Exp $");
+
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/proc.h>
+#else
+#include <sys/types.h>
+#include <stdlib.h>
+#endif
+
+#include <netinet/in.h>
+
+#ifdef _KERNEL
+#include <netinet/libalias/alias.h>
+#else
+#include "alias.h"
+#endif
+
+/*
+ * These functions are for backwards compatibility and because apps may
+ * be linked against shlib versions, they have to be actual functions,
+ * we cannot inline them.
+ */
+
+static struct libalias *la;
+
+void
+PacketAliasInit(void)
+{
+
+ la = LibAliasInit(la);
+}
+
+void
+PacketAliasSetAddress(struct in_addr _addr)
+{
+
+ LibAliasSetAddress(la, _addr);
+}
+
+void
+PacketAliasSetFWBase(unsigned int _base, unsigned int _num)
+{
+
+ LibAliasSetFWBase(la, _base, _num);
+}
+
+void
+PacketAliasSetSkinnyPort(unsigned int _port)
+{
+
+ LibAliasSetSkinnyPort(la, _port);
+}
+
+unsigned int
+PacketAliasSetMode(unsigned int _flags, unsigned int _mask)
+{
+
+ return LibAliasSetMode(la, _flags, _mask);
+}
+
+void
+PacketAliasUninit(void)
+{
+
+ LibAliasUninit(la);
+ la = NULL;
+}
+
+int
+PacketAliasIn(char *_ptr, int _maxpacketsize)
+{
+ return LibAliasIn(la, _ptr, _maxpacketsize);
+}
+
+int
+PacketAliasOut(char *_ptr, int _maxpacketsize)
+{
+
+ return LibAliasOut(la, _ptr, _maxpacketsize);
+}
+
+int
+PacketUnaliasOut(char *_ptr, int _maxpacketsize)
+{
+
+ return LibAliasUnaliasOut(la, _ptr, _maxpacketsize);
+}
+
+int
+PacketAliasAddServer(struct alias_link *_lnk,
+ struct in_addr _addr, unsigned short _port)
+{
+
+ return LibAliasAddServer(la, _lnk, _addr, _port);
+}
+
+struct alias_link *
+PacketAliasRedirectAddr(struct in_addr _src_addr,
+ struct in_addr _alias_addr)
+{
+
+ return LibAliasRedirectAddr(la, _src_addr, _alias_addr);
+}
+
+
+int
+PacketAliasRedirectDynamic(struct alias_link *_lnk)
+{
+
+ return LibAliasRedirectDynamic(la, _lnk);
+}
+
+void
+PacketAliasRedirectDelete(struct alias_link *_lnk)
+{
+
+ LibAliasRedirectDelete(la, _lnk);
+}
+
+struct alias_link *
+PacketAliasRedirectPort(struct in_addr _src_addr,
+ unsigned short _src_port, struct in_addr _dst_addr,
+ unsigned short _dst_port, struct in_addr _alias_addr,
+ unsigned short _alias_port, unsigned char _proto)
+{
+
+ return LibAliasRedirectPort(la, _src_addr, _src_port, _dst_addr,
+ _dst_port, _alias_addr, _alias_port, _proto);
+}
+
+struct alias_link *
+PacketAliasRedirectProto(struct in_addr _src_addr,
+ struct in_addr _dst_addr, struct in_addr _alias_addr,
+ unsigned char _proto)
+{
+
+ return LibAliasRedirectProto(la, _src_addr, _dst_addr, _alias_addr,
+ _proto);
+}
+
+void
+PacketAliasFragmentIn(char *_ptr, char *_ptr_fragment)
+{
+
+ LibAliasFragmentIn(la, _ptr, _ptr_fragment);
+}
+
+char *
+PacketAliasGetFragment(char *_ptr)
+{
+
+ return LibAliasGetFragment(la, _ptr);
+}
+
+int
+PacketAliasSaveFragment(char *_ptr)
+{
+ return LibAliasSaveFragment(la, _ptr);
+}
+
+int
+PacketAliasCheckNewLink(void)
+{
+
+ return LibAliasCheckNewLink(la);
+}
+
+unsigned short
+PacketAliasInternetChecksum(unsigned short *_ptr, int _nbytes)
+{
+
+ return LibAliasInternetChecksum(la, _ptr, _nbytes);
+}
+
+void
+PacketAliasSetTarget(struct in_addr _target_addr)
+{
+
+ LibAliasSetTarget(la, _target_addr);
+}
+
+/* Transparent proxying routines. */
+int
+PacketAliasProxyRule(const char *_cmd)
+{
+
+ return LibAliasProxyRule(la, _cmd);
+}
diff --git a/src/VBox/Devices/Network/slirp/libalias/alias_pptp.c b/src/VBox/Devices/Network/slirp/libalias/alias_pptp.c
new file mode 100644
index 00000000..601e87a2
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/alias_pptp.c
@@ -0,0 +1,523 @@
+/*
+ * alias_pptp.c
+ *
+ * Copyright (c) 2000 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Author: Erik Salander <erik@whistle.com>
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netinet/libalias/alias_pptp.c,v 1.15.8.1 2009/04/15 03:14:26 kensmith Exp $");
+
+/* Includes */
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/limits.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#else
+#include <errno.h>
+#include <limits.h>
+#include <sys/types.h>
+#include <stdio.h>
+#endif
+
+#include <netinet/tcp.h>
+
+#ifdef _KERNEL
+#include <netinet/libalias/alias.h>
+#include <netinet/libalias/alias_local.h>
+#include <netinet/libalias/alias_mod.h>
+#else
+#include "alias.h"
+#include "alias_local.h"
+#include "alias_mod.h"
+#endif
+
+#define PPTP_CONTROL_PORT_NUMBER 1723
+
+static void
+AliasHandlePptpOut(struct libalias *, struct ip *, struct alias_link *);
+
+static void
+AliasHandlePptpIn(struct libalias *, struct ip *, struct alias_link *);
+
+static int
+AliasHandlePptpGreOut(struct libalias *, struct ip *);
+
+static int
+AliasHandlePptpGreIn(struct libalias *, struct ip *);
+
+static int
+fingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL)
+ return (-1);
+ if (ntohs(*ah->dport) == PPTP_CONTROL_PORT_NUMBER
+ || ntohs(*ah->sport) == PPTP_CONTROL_PORT_NUMBER)
+ return (0);
+ return (-1);
+}
+
+static int
+fingerprintgre(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ return (0);
+}
+
+static int
+protohandlerin(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ AliasHandlePptpIn(la, pip, ah->lnk);
+ return (0);
+}
+
+static int
+protohandlerout(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ AliasHandlePptpOut(la, pip, ah->lnk);
+ return (0);
+}
+
+static int
+protohandlergrein(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ if (la->packetAliasMode & PKT_ALIAS_PROXY_ONLY ||
+ AliasHandlePptpGreIn(la, pip) == 0)
+ return (0);
+ return (-1);
+}
+
+static int
+protohandlergreout(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ if (AliasHandlePptpGreOut(la, pip) == 0)
+ return (0);
+ return (-1);
+}
+
+/* Kernel module definition. */
+struct proto_handler handlers[] = {
+ {
+ .pri = 200,
+ .dir = IN,
+ .proto = TCP,
+ .fingerprint = &fingerprint,
+ .protohandler = &protohandlerin
+ },
+ {
+ .pri = 210,
+ .dir = OUT,
+ .proto = TCP,
+ .fingerprint = &fingerprint,
+ .protohandler = &protohandlerout
+ },
+/*
+ * WATCH OUT!!! these 2 handlers NEED a priority of INT_MAX (highest possible)
+ * cause they will ALWAYS process packets, so they must be the last one
+ * in chain: look fingerprintgre() above.
+ */
+ {
+ .pri = INT_MAX,
+ .dir = IN,
+ .proto = IP,
+ .fingerprint = &fingerprintgre,
+ .protohandler = &protohandlergrein
+ },
+ {
+ .pri = INT_MAX,
+ .dir = OUT,
+ .proto = IP,
+ .fingerprint = &fingerprintgre,
+ .protohandler = &protohandlergreout
+ },
+ { EOH }
+};
+static int
+mod_handler(module_t mod, int type, void *data)
+{
+ int error;
+
+ switch (type) {
+ case MOD_LOAD:
+ error = 0;
+ LibAliasAttachHandlers(handlers);
+ break;
+ case MOD_UNLOAD:
+ error = 0;
+ LibAliasDetachHandlers(handlers);
+ break;
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
+
+#ifdef _KERNEL
+static
+#endif
+moduledata_t alias_mod = {
+ "alias_pptp", mod_handler, NULL
+};
+
+#ifdef _KERNEL
+DECLARE_MODULE(alias_pptp, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
+MODULE_VERSION(alias_pptp, 1);
+MODULE_DEPEND(alias_pptp, libalias, 1, 1, 1);
+#endif
+
+/*
+ Alias_pptp.c performs special processing for PPTP sessions under TCP.
+ Specifically, watch PPTP control messages and alias the Call ID or the
+ Peer's Call ID in the appropriate messages. Note, PPTP requires
+ "de-aliasing" of incoming packets, this is different than any other
+ TCP applications that are currently (ie. FTP, IRC and RTSP) aliased.
+
+ For Call IDs encountered for the first time, a PPTP alias link is created.
+ The PPTP alias link uses the Call ID in place of the original port number.
+ An alias Call ID is created.
+
+ For this routine to work, the PPTP control messages must fit entirely
+ into a single TCP packet. This is typically the case, but is not
+ required by the spec.
+
+ Unlike some of the other TCP applications that are aliased (ie. FTP,
+ IRC and RTSP), the PPTP control messages that need to be aliased are
+ guaranteed to remain the same length. The aliased Call ID is a fixed
+ length field.
+
+ Reference: RFC 2637
+
+ Initial version: May, 2000 (eds)
+
+*/
+
+/*
+ * PPTP definitions
+ */
+
+struct grehdr { /* Enhanced GRE header. */
+ u_int16_t gh_flags; /* Flags. */
+ u_int16_t gh_protocol; /* Protocol type. */
+ u_int16_t gh_length; /* Payload length. */
+ u_int16_t gh_call_id; /* Call ID. */
+ u_int32_t gh_seq_no; /* Sequence number (optional). */
+ u_int32_t gh_ack_no; /* Acknowledgment number
+ * (optional). */
+};
+typedef struct grehdr GreHdr;
+
+/* The PPTP protocol ID used in the GRE 'proto' field. */
+#define PPTP_GRE_PROTO 0x880b
+
+/* Bits that must be set a certain way in all PPTP/GRE packets. */
+#define PPTP_INIT_VALUE ((0x2001 << 16) | PPTP_GRE_PROTO)
+#define PPTP_INIT_MASK 0xef7fffff
+
+#define PPTP_MAGIC 0x1a2b3c4d
+#define PPTP_CTRL_MSG_TYPE 1
+
+enum {
+ PPTP_StartCtrlConnRequest = 1,
+ PPTP_StartCtrlConnReply = 2,
+ PPTP_StopCtrlConnRequest = 3,
+ PPTP_StopCtrlConnReply = 4,
+ PPTP_EchoRequest = 5,
+ PPTP_EchoReply = 6,
+ PPTP_OutCallRequest = 7,
+ PPTP_OutCallReply = 8,
+ PPTP_InCallRequest = 9,
+ PPTP_InCallReply = 10,
+ PPTP_InCallConn = 11,
+ PPTP_CallClearRequest = 12,
+ PPTP_CallDiscNotify = 13,
+ PPTP_WanErrorNotify = 14,
+ PPTP_SetLinkInfo = 15
+};
+
+ /* Message structures */
+struct pptpMsgHead {
+ u_int16_t length; /* total length */
+ u_int16_t msgType;/* PPTP message type */
+ u_int32_t magic; /* magic cookie */
+ u_int16_t type; /* control message type */
+ u_int16_t resv0; /* reserved */
+};
+typedef struct pptpMsgHead *PptpMsgHead;
+
+struct pptpCodes {
+ u_int8_t resCode;/* Result Code */
+ u_int8_t errCode;/* Error Code */
+};
+typedef struct pptpCodes *PptpCode;
+
+struct pptpCallIds {
+ u_int16_t cid1; /* Call ID field #1 */
+ u_int16_t cid2; /* Call ID field #2 */
+};
+typedef struct pptpCallIds *PptpCallId;
+
+static PptpCallId AliasVerifyPptp(struct ip *, u_int16_t *);
+
+
+static void
+AliasHandlePptpOut(struct libalias *la,
+ struct ip *pip, /* IP packet to examine/patch */
+ struct alias_link *lnk)
+{ /* The PPTP control link */
+ struct alias_link *pptp_lnk;
+ PptpCallId cptr;
+ PptpCode codes;
+ u_int16_t ctl_type; /* control message type */
+ struct tcphdr *tc;
+
+ /* Verify valid PPTP control message */
+ if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
+ return;
+
+ /* Modify certain PPTP messages */
+ switch (ctl_type) {
+ case PPTP_OutCallRequest:
+ case PPTP_OutCallReply:
+ case PPTP_InCallRequest:
+ case PPTP_InCallReply:
+ /*
+ * Establish PPTP link for address and Call ID found in
+ * control message.
+ */
+ pptp_lnk = AddPptp(la, GetOriginalAddress(lnk), GetDestAddress(lnk),
+ GetAliasAddress(lnk), cptr->cid1);
+ break;
+ case PPTP_CallClearRequest:
+ case PPTP_CallDiscNotify:
+ /*
+ * Find PPTP link for address and Call ID found in control
+ * message.
+ */
+ pptp_lnk = FindPptpOutByCallId(la, GetOriginalAddress(lnk),
+ GetDestAddress(lnk),
+ cptr->cid1);
+ break;
+ default:
+ return;
+ }
+
+ if (pptp_lnk != NULL) {
+ int accumulate = cptr->cid1;
+
+ /* alias the Call Id */
+ cptr->cid1 = GetAliasPort(pptp_lnk);
+
+ /* Compute TCP checksum for revised packet */
+ tc = (struct tcphdr *)ip_next(pip);
+ accumulate -= cptr->cid1;
+ ADJUST_CHECKSUM(accumulate, tc->th_sum);
+
+ switch (ctl_type) {
+ case PPTP_OutCallReply:
+ case PPTP_InCallReply:
+ codes = (PptpCode) (cptr + 1);
+ if (codes->resCode == 1) /* Connection
+ * established, */
+ SetDestCallId(pptp_lnk, /* note the Peer's Call
+ * ID. */
+ cptr->cid2);
+ else
+ SetExpire(pptp_lnk, 0); /* Connection refused. */
+ break;
+ case PPTP_CallDiscNotify: /* Connection closed. */
+ SetExpire(pptp_lnk, 0);
+ break;
+ }
+ }
+}
+
+static void
+AliasHandlePptpIn(struct libalias *la,
+ struct ip *pip, /* IP packet to examine/patch */
+ struct alias_link *lnk)
+{ /* The PPTP control link */
+ struct alias_link *pptp_lnk;
+ PptpCallId cptr;
+ u_int16_t *pcall_id;
+ u_int16_t ctl_type; /* control message type */
+ struct tcphdr *tc;
+
+ /* Verify valid PPTP control message */
+ if ((cptr = AliasVerifyPptp(pip, &ctl_type)) == NULL)
+ return;
+
+ /* Modify certain PPTP messages */
+ switch (ctl_type) {
+ case PPTP_InCallConn:
+ case PPTP_WanErrorNotify:
+ case PPTP_SetLinkInfo:
+ pcall_id = &cptr->cid1;
+ break;
+ case PPTP_OutCallReply:
+ case PPTP_InCallReply:
+ pcall_id = &cptr->cid2;
+ break;
+ case PPTP_CallDiscNotify: /* Connection closed. */
+ pptp_lnk = FindPptpInByCallId(la, GetDestAddress(lnk),
+ GetAliasAddress(lnk),
+ cptr->cid1);
+ if (pptp_lnk != NULL)
+ SetExpire(pptp_lnk, 0);
+ return;
+ default:
+ return;
+ }
+
+ /* Find PPTP link for address and Call ID found in PPTP Control Msg */
+ pptp_lnk = FindPptpInByPeerCallId(la, GetDestAddress(lnk),
+ GetAliasAddress(lnk),
+ *pcall_id);
+
+ if (pptp_lnk != NULL) {
+ int accumulate = *pcall_id;
+
+ /* De-alias the Peer's Call Id. */
+ *pcall_id = GetOriginalPort(pptp_lnk);
+
+ /* Compute TCP checksum for modified packet */
+ tc = (struct tcphdr *)ip_next(pip);
+ accumulate -= *pcall_id;
+ ADJUST_CHECKSUM(accumulate, tc->th_sum);
+
+ if (ctl_type == PPTP_OutCallReply || ctl_type == PPTP_InCallReply) {
+ PptpCode codes = (PptpCode) (cptr + 1);
+
+ if (codes->resCode == 1) /* Connection
+ * established, */
+ SetDestCallId(pptp_lnk, /* note the Call ID. */
+ cptr->cid1);
+ else
+ SetExpire(pptp_lnk, 0); /* Connection refused. */
+ }
+ }
+}
+
+static PptpCallId
+AliasVerifyPptp(struct ip *pip, u_int16_t * ptype)
+{ /* IP packet to examine/patch */
+ int hlen, tlen, dlen;
+ PptpMsgHead hptr;
+ struct tcphdr *tc;
+
+ /* Calculate some lengths */
+ tc = (struct tcphdr *)ip_next(pip);
+ hlen = (pip->ip_hl + tc->th_off) << 2;
+ tlen = ntohs(pip->ip_len);
+ dlen = tlen - hlen;
+
+ /* Verify data length */
+ if (dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds)))
+ return (NULL);
+
+ /* Move up to PPTP message header */
+ hptr = (PptpMsgHead) tcp_next(tc);
+
+ /* Return the control message type */
+ *ptype = ntohs(hptr->type);
+
+ /* Verify PPTP Control Message */
+ if ((ntohs(hptr->msgType) != PPTP_CTRL_MSG_TYPE) ||
+ (ntohl(hptr->magic) != PPTP_MAGIC))
+ return (NULL);
+
+ /* Verify data length. */
+ if ((*ptype == PPTP_OutCallReply || *ptype == PPTP_InCallReply) &&
+ (dlen < (int)(sizeof(struct pptpMsgHead) + sizeof(struct pptpCallIds) +
+ sizeof(struct pptpCodes))))
+ return (NULL);
+ else
+ return (PptpCallId) (hptr + 1);
+}
+
+static int
+AliasHandlePptpGreOut(struct libalias *la, struct ip *pip)
+{
+ GreHdr *gr;
+ struct alias_link *lnk;
+
+ gr = (GreHdr *) ip_next(pip);
+
+ /* Check GRE header bits. */
+ if ((ntohl(*((u_int32_t *) gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
+ return (-1);
+
+ lnk = FindPptpOutByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id);
+ if (lnk != NULL) {
+ struct in_addr alias_addr = GetAliasAddress(lnk);
+
+ /* Change source IP address. */
+ DifferentialChecksum(&pip->ip_sum,
+ &alias_addr, &pip->ip_src, 2);
+ pip->ip_src = alias_addr;
+ }
+ return (0);
+}
+
+static int
+AliasHandlePptpGreIn(struct libalias *la, struct ip *pip)
+{
+ GreHdr *gr;
+ struct alias_link *lnk;
+
+ gr = (GreHdr *) ip_next(pip);
+
+ /* Check GRE header bits. */
+ if ((ntohl(*((u_int32_t *) gr)) & PPTP_INIT_MASK) != PPTP_INIT_VALUE)
+ return (-1);
+
+ lnk = FindPptpInByPeerCallId(la, pip->ip_src, pip->ip_dst, gr->gh_call_id);
+ if (lnk != NULL) {
+ struct in_addr src_addr = GetOriginalAddress(lnk);
+
+ /* De-alias the Peer's Call Id. */
+ gr->gh_call_id = GetOriginalPort(lnk);
+
+ /* Restore original IP address. */
+ DifferentialChecksum(&pip->ip_sum,
+ &src_addr, &pip->ip_dst, 2);
+ pip->ip_dst = src_addr;
+ }
+ return (0);
+}
diff --git a/src/VBox/Devices/Network/slirp/libalias/alias_proxy.c b/src/VBox/Devices/Network/slirp/libalias/alias_proxy.c
new file mode 100644
index 00000000..b49b726c
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/alias_proxy.c
@@ -0,0 +1,999 @@
+/*-
+ * Copyright (c) 2001 Charles Mott <cm@linktel.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHOR 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 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 VBOX
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netinet/libalias/alias_proxy.c,v 1.31.8.1 2009/04/15 03:14:26 kensmith Exp $");
+
+/* file: alias_proxy.c
+
+ This file encapsulates special operations related to transparent
+ proxy redirection. This is where packets with a particular destination,
+ usually tcp port 80, are redirected to a proxy server.
+
+ When packets are proxied, the destination address and port are
+ modified. In certain cases, it is necessary to somehow encode
+ the original address/port info into the packet. Two methods are
+ presently supported: addition of a [DEST addr port] string at the
+ beginning of a tcp stream, or inclusion of an optional field
+ in the IP header.
+
+ There is one public API function:
+
+ PacketAliasProxyRule() -- Adds and deletes proxy
+ rules.
+
+ Rules are stored in a linear linked list, so lookup efficiency
+ won't be too good for large lists.
+
+
+ Initial development: April, 1998 (cjm)
+*/
+
+
+/* System includes */
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/ctype.h>
+#include <sys/libkern.h>
+#include <sys/limits.h>
+#else
+#include <sys/types.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <netdb.h>
+#include <string.h>
+#endif
+
+#include <netinet/tcp.h>
+
+#ifdef _KERNEL
+#include <netinet/libalias/alias.h>
+#include <netinet/libalias/alias_local.h>
+#include <netinet/libalias/alias_mod.h>
+#else
+#include <arpa/inet.h>
+#include "alias.h" /* Public API functions for libalias */
+#include "alias_local.h" /* Functions used by alias*.c */
+#endif
+#else /* VBOX */
+# include <iprt/ctype.h>
+# include <iprt/string.h>
+# include <slirp.h>
+# include "alias.h" /* Public API functions for libalias */
+# include "alias_local.h" /* Functions used by alias*.c */
+# define tolower(ch) RT_C_TO_LOWER(ch)
+#endif /* VBOX */
+
+/*
+ Data structures
+ */
+
+/*
+ * A linked list of arbitrary length, based on struct proxy_entry is
+ * used to store proxy rules.
+ */
+struct proxy_entry {
+ struct libalias *la;
+#define PROXY_TYPE_ENCODE_NONE 1
+#define PROXY_TYPE_ENCODE_TCPSTREAM 2
+#define PROXY_TYPE_ENCODE_IPHDR 3
+ int rule_index;
+ int proxy_type;
+ u_char proto;
+ u_short proxy_port;
+ u_short server_port;
+
+ struct in_addr server_addr;
+
+ struct in_addr src_addr;
+ struct in_addr src_mask;
+
+ struct in_addr dst_addr;
+ struct in_addr dst_mask;
+
+ struct proxy_entry *next;
+ struct proxy_entry *last;
+};
+
+
+
+/*
+ File scope variables
+*/
+
+
+
+/* Local (static) functions:
+
+ IpMask() -- Utility function for creating IP
+ masks from integer (1-32) specification.
+ IpAddr() -- Utility function for converting string
+ to IP address
+ IpPort() -- Utility function for converting string
+ to port number
+ RuleAdd() -- Adds an element to the rule list.
+ RuleDelete() -- Removes an element from the rule list.
+ RuleNumberDelete() -- Removes all elements from the rule list
+ having a certain rule number.
+ ProxyEncodeTcpStream() -- Adds [DEST x.x.x.x xxxx] to the beginning
+ of a TCP stream.
+ ProxyEncodeIpHeader() -- Adds an IP option indicating the true
+ destination of a proxied IP packet
+*/
+
+#ifdef _KERNEL /* XXX: can it be moved to libkern? */
+static int inet_aton(const char *cp, struct in_addr *addr);
+#endif
+static int IpMask(int, struct in_addr *);
+static int IpAddr(char *, struct in_addr *);
+static int IpPort(char *, int, int *);
+static void RuleAdd(struct libalias *la, struct proxy_entry *);
+static void RuleDelete(struct proxy_entry *);
+static int RuleNumberDelete(struct libalias *la, int);
+static void ProxyEncodeTcpStream(struct alias_link *, struct ip *, int);
+static void ProxyEncodeIpHeader(struct ip *, int);
+
+#ifdef _KERNEL
+static int
+inet_aton(cp, addr)
+ const char *cp;
+ struct in_addr *addr;
+{
+ u_long parts[4];
+ in_addr_t val;
+ const char *c;
+ char *endptr;
+ int gotend, n;
+
+ c = (const char *)cp;
+ n = 0;
+ /*
+ * Run through the string, grabbing numbers until
+ * the end of the string, or some error
+ */
+ gotend = 0;
+ while (!gotend) {
+ unsigned long l;
+
+ l = strtoul(c, &endptr, 0);
+
+ if (l == ULONG_MAX || (l == 0 && endptr == c))
+ return (0);
+
+ val = (in_addr_t)l;
+ /*
+ * If the whole string is invalid, endptr will equal
+ * c.. this way we can make sure someone hasn't
+ * gone '.12' or something which would get past
+ * the next check.
+ */
+ if (endptr == c)
+ return (0);
+ parts[n] = val;
+ c = endptr;
+
+ /* Check the next character past the previous number's end */
+ switch (*c) {
+ case '.' :
+ /* Make sure we only do 3 dots .. */
+ if (n == 3) /* Whoops. Quit. */
+ return (0);
+ n++;
+ c++;
+ break;
+
+ case '\0':
+ gotend = 1;
+ break;
+
+ default:
+ if (isspace((unsigned char)*c)) {
+ gotend = 1;
+ break;
+ } else
+ return (0); /* Invalid character, so fail */
+ }
+
+ }
+
+ /*
+ * Concoct the address according to
+ * the number of parts specified.
+ */
+
+ switch (n) {
+ case 0: /* a -- 32 bits */
+ /*
+ * Nothing is necessary here. Overflow checking was
+ * already done in strtoul().
+ */
+ break;
+ case 1: /* a.b -- 8.24 bits */
+ if (val > 0xffffff || parts[0] > 0xff)
+ return (0);
+ val |= parts[0] << 24;
+ break;
+
+ case 2: /* a.b.c -- 8.8.16 bits */
+ if (val > 0xffff || parts[0] > 0xff || parts[1] > 0xff)
+ return (0);
+ val |= (parts[0] << 24) | (parts[1] << 16);
+ break;
+
+ case 3: /* a.b.c.d -- 8.8.8.8 bits */
+ if (val > 0xff || parts[0] > 0xff || parts[1] > 0xff ||
+ parts[2] > 0xff)
+ return (0);
+ val |= (parts[0] << 24) | (parts[1] << 16) | (parts[2] << 8);
+ break;
+ }
+
+ if (addr != NULL)
+ addr->s_addr = htonl(val);
+ return (1);
+}
+#endif
+
+static int
+IpMask(int nbits, struct in_addr *mask)
+{
+ int i;
+ u_int imask;
+
+ if (nbits < 0 || nbits > 32)
+ return (-1);
+
+ imask = 0;
+ for (i = 0; i < nbits; i++)
+ imask = (imask >> 1) + 0x80000000;
+ mask->s_addr = htonl(imask);
+
+ return (0);
+}
+
+static int
+IpAddr(char *s, struct in_addr *addr)
+{
+ if (inet_aton(s, addr) == 0)
+ return (-1);
+ else
+ return (0);
+}
+
+static int
+IpPort(char *s, int proto, int *port)
+{
+ int n;
+
+ n = sscanf(s, "%d", port);
+ if (n != 1)
+#ifndef _KERNEL /* XXX: we accept only numeric ports in kernel */
+ {
+ struct servent *se;
+
+ if (proto == IPPROTO_TCP)
+ se = getservbyname(s, "tcp");
+ else if (proto == IPPROTO_UDP)
+ se = getservbyname(s, "udp");
+ else
+ return (-1);
+
+ if (se == NULL)
+ return (-1);
+
+ *port = (u_int) ntohs(se->s_port);
+ }
+#else
+ return (-1);
+#endif
+ return (0);
+}
+
+void
+RuleAdd(struct libalias *la, struct proxy_entry *entry)
+{
+ int rule_index;
+ struct proxy_entry *ptr;
+ struct proxy_entry *ptr_last;
+
+ LIBALIAS_LOCK_ASSERT(la);
+
+ if (la->proxyList == NULL) {
+ la->proxyList = entry;
+ entry->last = NULL;
+ entry->next = NULL;
+ return;
+ }
+ entry->la = la;
+
+ rule_index = entry->rule_index;
+ ptr = la->proxyList;
+ ptr_last = NULL;
+ while (ptr != NULL) {
+ if (ptr->rule_index >= rule_index) {
+ if (ptr_last == NULL) {
+ entry->next = la->proxyList;
+ entry->last = NULL;
+ la->proxyList->last = entry;
+ la->proxyList = entry;
+ return;
+ }
+ ptr_last->next = entry;
+ ptr->last = entry;
+ entry->last = ptr->last;
+ entry->next = ptr;
+ return;
+ }
+ ptr_last = ptr;
+ ptr = ptr->next;
+ }
+
+ ptr_last->next = entry;
+ entry->last = ptr_last;
+ entry->next = NULL;
+}
+
+static void
+RuleDelete(struct proxy_entry *entry)
+{
+ struct libalias *la;
+
+ la = entry->la;
+ LIBALIAS_LOCK_ASSERT(la);
+ if (entry->last != NULL)
+ entry->last->next = entry->next;
+ else
+ la->proxyList = entry->next;
+
+ if (entry->next != NULL)
+ entry->next->last = entry->last;
+
+ free(entry);
+}
+
+static int
+RuleNumberDelete(struct libalias *la, int rule_index)
+{
+ int err;
+ struct proxy_entry *ptr;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ err = -1;
+ ptr = la->proxyList;
+ while (ptr != NULL) {
+ struct proxy_entry *ptr_next;
+
+ ptr_next = ptr->next;
+ if (ptr->rule_index == rule_index) {
+ err = 0;
+ RuleDelete(ptr);
+ }
+ ptr = ptr_next;
+ }
+
+ return (err);
+}
+
+static void
+ProxyEncodeTcpStream(struct alias_link *lnk,
+ struct ip *pip,
+ int maxpacketsize)
+{
+ int slen;
+ char buffer[40];
+ struct tcphdr *tc;
+
+/* Compute pointer to tcp header */
+ tc = (struct tcphdr *)ip_next(pip);
+
+/* Don't modify if once already modified */
+
+ if (GetAckModified(lnk))
+ return;
+
+/* Translate destination address and port to string form */
+#ifndef VBOX
+ snprintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
+ inet_ntoa(GetProxyAddress(lnk)), (u_int) ntohs(GetProxyPort(lnk)));
+#else
+ RTStrPrintf(buffer, sizeof(buffer) - 2, "[DEST %s %d]",
+ inet_ntoa(GetProxyAddress(lnk)), (u_int) ntohs(GetProxyPort(lnk)));
+#endif
+
+/* Pad string out to a multiple of two in length */
+ slen = (int)strlen(buffer);
+ switch (slen % 2) {
+ case 0:
+ strcat(buffer, " \n");
+ slen += 2;
+ break;
+ case 1:
+ strcat(buffer, "\n");
+ slen += 1;
+ }
+
+/* Check for packet overflow */
+ if ((int)(ntohs(pip->ip_len) + strlen(buffer)) > maxpacketsize)
+ return;
+
+/* Shift existing TCP data and insert destination string */
+ {
+ int dlen;
+ int hlen;
+ char *p;
+
+ hlen = (pip->ip_hl + tc->th_off) << 2;
+ dlen = ntohs(pip->ip_len) - hlen;
+
+/* Modify first packet that has data in it */
+
+ if (dlen == 0)
+ return;
+
+ p = (char *)pip;
+ p += hlen;
+
+ bcopy(p, p + slen, dlen);
+ memcpy(p, buffer, slen);
+ }
+
+/* Save information about modfied sequence number */
+ {
+ int delta;
+
+ SetAckModified(lnk);
+ delta = GetDeltaSeqOut(pip, lnk);
+ AddSeq(pip, lnk, delta + slen);
+ }
+
+/* Update IP header packet length and checksum */
+ {
+ int accumulate;
+
+ accumulate = pip->ip_len;
+ pip->ip_len = htons(ntohs(pip->ip_len) + slen);
+ accumulate -= pip->ip_len;
+
+ ADJUST_CHECKSUM(accumulate, pip->ip_sum);
+ }
+
+/* Update TCP checksum, Use TcpChecksum since so many things have
+ already changed. */
+
+ tc->th_sum = 0;
+#ifdef _KERNEL
+ tc->th_x2 = 1;
+#else
+ tc->th_sum = TcpChecksum(pip);
+#endif
+}
+
+static void
+ProxyEncodeIpHeader(struct ip *pip,
+ int maxpacketsize)
+{
+#define OPTION_LEN_BYTES 8
+#define OPTION_LEN_INT16 4
+#define OPTION_LEN_INT32 2
+ u_char option[OPTION_LEN_BYTES];
+
+#ifdef LIBALIAS_DEBUG
+ fprintf(stdout, " ip cksum 1 = %x\n", (u_int) IpChecksum(pip));
+ fprintf(stdout, "tcp cksum 1 = %x\n", (u_int) TcpChecksum(pip));
+#endif
+
+ (void)maxpacketsize;
+
+/* Check to see that there is room to add an IP option */
+ if (pip->ip_hl > (0x0f - OPTION_LEN_INT32))
+ return;
+
+/* Build option and copy into packet */
+ {
+ u_char *ptr;
+ struct tcphdr *tc;
+
+ ptr = (u_char *) pip;
+ ptr += 20;
+ memcpy(ptr + OPTION_LEN_BYTES, ptr, ntohs(pip->ip_len) - 20);
+
+ option[0] = 0x64; /* class: 3 (reserved), option 4 */
+ option[1] = OPTION_LEN_BYTES;
+
+ memcpy(&option[2], (u_char *) & pip->ip_dst, 4);
+
+ tc = (struct tcphdr *)ip_next(pip);
+ memcpy(&option[6], (u_char *) & tc->th_sport, 2);
+
+ memcpy(ptr, option, 8);
+ }
+
+/* Update checksum, header length and packet length */
+ {
+ int i;
+ int accumulate;
+ u_short *sptr;
+
+ sptr = (u_short *) option;
+ accumulate = 0;
+ for (i = 0; i < OPTION_LEN_INT16; i++)
+ accumulate -= *(sptr++);
+
+ sptr = (u_short *) pip;
+ accumulate += *sptr;
+ pip->ip_hl += OPTION_LEN_INT32;
+ accumulate -= *sptr;
+
+ accumulate += pip->ip_len;
+ pip->ip_len = htons(ntohs(pip->ip_len) + OPTION_LEN_BYTES);
+ accumulate -= pip->ip_len;
+
+ ADJUST_CHECKSUM(accumulate, pip->ip_sum);
+ }
+#undef OPTION_LEN_BYTES
+#undef OPTION_LEN_INT16
+#undef OPTION_LEN_INT32
+#ifdef LIBALIAS_DEBUG
+ fprintf(stdout, " ip cksum 2 = %x\n", (u_int) IpChecksum(pip));
+ fprintf(stdout, "tcp cksum 2 = %x\n", (u_int) TcpChecksum(pip));
+#endif
+}
+
+
+/* Functions by other packet alias source files
+
+ ProxyCheck() -- Checks whether an outgoing packet should
+ be proxied.
+ ProxyModify() -- Encodes the original destination address/port
+ for a packet which is to be redirected to
+ a proxy server.
+*/
+
+int
+ProxyCheck(struct libalias *la, struct ip *pip,
+ struct in_addr *proxy_server_addr,
+ u_short * proxy_server_port)
+{
+ u_short dst_port;
+ struct in_addr src_addr;
+ struct in_addr dst_addr;
+ struct proxy_entry *ptr;
+
+ LIBALIAS_LOCK_ASSERT(la);
+ src_addr = pip->ip_src;
+ dst_addr = pip->ip_dst;
+ dst_port = ((struct tcphdr *)ip_next(pip))
+ ->th_dport;
+
+ ptr = la->proxyList;
+ while (ptr != NULL) {
+ u_short proxy_port;
+
+ proxy_port = ptr->proxy_port;
+ if ((dst_port == proxy_port || proxy_port == 0)
+ && pip->ip_p == ptr->proto
+ && src_addr.s_addr != ptr->server_addr.s_addr) {
+ struct in_addr src_addr_masked;
+ struct in_addr dst_addr_masked;
+
+ src_addr_masked.s_addr = src_addr.s_addr & ptr->src_mask.s_addr;
+ dst_addr_masked.s_addr = dst_addr.s_addr & ptr->dst_mask.s_addr;
+
+ if ((src_addr_masked.s_addr == ptr->src_addr.s_addr)
+ && (dst_addr_masked.s_addr == ptr->dst_addr.s_addr)) {
+ if ((*proxy_server_port = ptr->server_port) == 0)
+ *proxy_server_port = dst_port;
+ *proxy_server_addr = ptr->server_addr;
+ return (ptr->proxy_type);
+ }
+ }
+ ptr = ptr->next;
+ }
+
+ return (0);
+}
+
+void
+ProxyModify(struct libalias *la, struct alias_link *lnk,
+ struct ip *pip,
+ int maxpacketsize,
+ int proxy_type)
+{
+
+ LIBALIAS_LOCK_ASSERT(la);
+ (void)la;
+
+ switch (proxy_type) {
+ case PROXY_TYPE_ENCODE_IPHDR:
+ ProxyEncodeIpHeader(pip, maxpacketsize);
+ break;
+
+ case PROXY_TYPE_ENCODE_TCPSTREAM:
+ ProxyEncodeTcpStream(lnk, pip, maxpacketsize);
+ break;
+ }
+}
+
+
+/*
+ Public API functions
+*/
+
+int
+LibAliasProxyRule(struct libalias *la, const char *cmd)
+{
+/*
+ * This function takes command strings of the form:
+ *
+ * server <addr>[:<port>]
+ * [port <port>]
+ * [rule n]
+ * [proto tcp|udp]
+ * [src <addr>[/n]]
+ * [dst <addr>[/n]]
+ * [type encode_tcp_stream|encode_ip_hdr|no_encode]
+ *
+ * delete <rule number>
+ *
+ * Subfields can be in arbitrary order. Port numbers and addresses
+ * must be in either numeric or symbolic form. An optional rule number
+ * is used to control the order in which rules are searched. If two
+ * rules have the same number, then search order cannot be guaranteed,
+ * and the rules should be disjoint. If no rule number is specified,
+ * then 0 is used, and group 0 rules are always checked before any
+ * others.
+ */
+ int i, n, len, ret;
+ int cmd_len;
+ int token_count;
+ int state;
+ char *token;
+ char buffer[256];
+ char str_port[sizeof(buffer)];
+ char str_server_port[sizeof(buffer)];
+ char *res = buffer;
+
+ int rule_index;
+ int proto;
+ int proxy_type;
+ int proxy_port;
+ int server_port;
+ struct in_addr server_addr;
+ struct in_addr src_addr, src_mask;
+ struct in_addr dst_addr, dst_mask;
+ struct proxy_entry *proxy_entry;
+
+ LIBALIAS_LOCK(la);
+ ret = 0;
+/* Copy command line into a buffer */
+ cmd += strspn(cmd, " \t");
+ cmd_len = (int)strlen(cmd);
+ if (cmd_len > (int)(sizeof(buffer) - 1)) {
+ ret = -1;
+ goto getout;
+ }
+ strcpy(buffer, cmd);
+
+/* Convert to lower case */
+ len = (int)strlen(buffer);
+ for (i = 0; i < len; i++)
+ buffer[i] = tolower((unsigned char)buffer[i]);
+
+/* Set default proxy type */
+
+/* Set up default values */
+ rule_index = 0;
+ proxy_type = PROXY_TYPE_ENCODE_NONE;
+ proto = IPPROTO_TCP;
+ proxy_port = 0;
+ server_addr.s_addr = 0;
+ server_port = 0;
+ src_addr.s_addr = 0;
+ IpMask(0, &src_mask);
+ dst_addr.s_addr = 0;
+ IpMask(0, &dst_mask);
+
+ str_port[0] = 0;
+ str_server_port[0] = 0;
+
+/* Parse command string with state machine */
+#define STATE_READ_KEYWORD 0
+#define STATE_READ_TYPE 1
+#define STATE_READ_PORT 2
+#define STATE_READ_SERVER 3
+#define STATE_READ_RULE 4
+#define STATE_READ_DELETE 5
+#define STATE_READ_PROTO 6
+#define STATE_READ_SRC 7
+#define STATE_READ_DST 8
+ state = STATE_READ_KEYWORD;
+#ifndef VBOX
+ token = strsep(&res, " \t");
+#else
+ token = RTStrStr(res, " \t");
+#endif
+ token_count = 0;
+ while (token != NULL) {
+ token_count++;
+ switch (state) {
+ case STATE_READ_KEYWORD:
+ if (strcmp(token, "type") == 0)
+ state = STATE_READ_TYPE;
+ else if (strcmp(token, "port") == 0)
+ state = STATE_READ_PORT;
+ else if (strcmp(token, "server") == 0)
+ state = STATE_READ_SERVER;
+ else if (strcmp(token, "rule") == 0)
+ state = STATE_READ_RULE;
+ else if (strcmp(token, "delete") == 0)
+ state = STATE_READ_DELETE;
+ else if (strcmp(token, "proto") == 0)
+ state = STATE_READ_PROTO;
+ else if (strcmp(token, "src") == 0)
+ state = STATE_READ_SRC;
+ else if (strcmp(token, "dst") == 0)
+ state = STATE_READ_DST;
+ else {
+ ret = -1;
+ goto getout;
+ }
+ break;
+
+ case STATE_READ_TYPE:
+ if (strcmp(token, "encode_ip_hdr") == 0)
+ proxy_type = PROXY_TYPE_ENCODE_IPHDR;
+ else if (strcmp(token, "encode_tcp_stream") == 0)
+ proxy_type = PROXY_TYPE_ENCODE_TCPSTREAM;
+ else if (strcmp(token, "no_encode") == 0)
+ proxy_type = PROXY_TYPE_ENCODE_NONE;
+ else {
+ ret = -1;
+ goto getout;
+ }
+ state = STATE_READ_KEYWORD;
+ break;
+
+ case STATE_READ_PORT:
+ strcpy(str_port, token);
+ state = STATE_READ_KEYWORD;
+ break;
+
+ case STATE_READ_SERVER:
+ {
+ int err;
+ char *p;
+ char s[sizeof(buffer)];
+
+ p = token;
+ while (*p != ':' && *p != 0)
+ p++;
+
+ if (*p != ':') {
+ err = IpAddr(token, &server_addr);
+ if (err) {
+ ret = -1;
+ goto getout;
+ }
+ } else {
+ *p = ' ';
+
+ n = sscanf(token, "%255s %255s", s, str_server_port);
+ if (n != 2) {
+ ret = -1;
+ goto getout;
+ }
+
+ err = IpAddr(s, &server_addr);
+ if (err) {
+ ret = -1;
+ goto getout;
+ }
+ }
+ }
+ state = STATE_READ_KEYWORD;
+ break;
+
+ case STATE_READ_RULE:
+ n = sscanf(token, "%d", &rule_index);
+ if (n != 1 || rule_index < 0) {
+ ret = -1;
+ goto getout;
+ }
+ state = STATE_READ_KEYWORD;
+ break;
+
+ case STATE_READ_DELETE:
+ {
+ int err;
+ int rule_to_delete;
+
+ if (token_count != 2) {
+ ret = -1;
+ goto getout;
+ }
+
+ n = sscanf(token, "%d", &rule_to_delete);
+ if (n != 1) {
+ ret = -1;
+ goto getout;
+ }
+ err = RuleNumberDelete(la, rule_to_delete);
+ if (err)
+ ret = -1;
+ ret = 0;
+ goto getout;
+ }
+
+ case STATE_READ_PROTO:
+ if (strcmp(token, "tcp") == 0)
+ proto = IPPROTO_TCP;
+ else if (strcmp(token, "udp") == 0)
+ proto = IPPROTO_UDP;
+ else {
+ ret = -1;
+ goto getout;
+ }
+ state = STATE_READ_KEYWORD;
+ break;
+
+ case STATE_READ_SRC:
+ case STATE_READ_DST:
+ {
+ int err;
+ char *p;
+ struct in_addr mask;
+ struct in_addr addr;
+
+ p = token;
+ while (*p != '/' && *p != 0)
+ p++;
+
+ if (*p != '/') {
+ IpMask(32, &mask);
+ err = IpAddr(token, &addr);
+ if (err) {
+ ret = -1;
+ goto getout;
+ }
+ } else {
+ int nbits;
+ char s[sizeof(buffer)];
+
+ *p = ' ';
+ n = sscanf(token, "%255s %d", s, &nbits);
+ if (n != 2) {
+ ret = -1;
+ goto getout;
+ }
+
+ err = IpAddr(s, &addr);
+ if (err) {
+ ret = -1;
+ goto getout;
+ }
+
+ err = IpMask(nbits, &mask);
+ if (err) {
+ ret = -1;
+ goto getout;
+ }
+ }
+
+ if (state == STATE_READ_SRC) {
+ src_addr = addr;
+ src_mask = mask;
+ } else {
+ dst_addr = addr;
+ dst_mask = mask;
+ }
+ }
+ state = STATE_READ_KEYWORD;
+ break;
+
+ default:
+ ret = -1;
+ goto getout;
+ break;
+ }
+
+ do {
+#ifndef VBOX
+ token = strsep(&res, " \t");
+#else
+ token = RTStrStr(res, " \t");
+#endif
+ } while (token != NULL && !*token);
+ }
+#undef STATE_READ_KEYWORD
+#undef STATE_READ_TYPE
+#undef STATE_READ_PORT
+#undef STATE_READ_SERVER
+#undef STATE_READ_RULE
+#undef STATE_READ_DELETE
+#undef STATE_READ_PROTO
+#undef STATE_READ_SRC
+#undef STATE_READ_DST
+
+/* Convert port strings to numbers. This needs to be done after
+ the string is parsed, because the prototype might not be designated
+ before the ports (which might be symbolic entries in /etc/services) */
+
+ if (strlen(str_port) != 0) {
+ int err;
+
+ err = IpPort(str_port, proto, &proxy_port);
+ if (err) {
+ ret = -1;
+ goto getout;
+ }
+ } else {
+ proxy_port = 0;
+ }
+
+ if (strlen(str_server_port) != 0) {
+ int err;
+
+ err = IpPort(str_server_port, proto, &server_port);
+ if (err) {
+ ret = -1;
+ goto getout;
+ }
+ } else {
+ server_port = 0;
+ }
+
+/* Check that at least the server address has been defined */
+ if (server_addr.s_addr == 0) {
+ ret = -1;
+ goto getout;
+ }
+
+/* Add to linked list */
+ proxy_entry = malloc(sizeof(struct proxy_entry));
+ if (proxy_entry == NULL) {
+ ret = -1;
+ goto getout;
+ }
+
+ proxy_entry->proxy_type = proxy_type;
+ proxy_entry->rule_index = rule_index;
+ proxy_entry->proto = proto;
+ proxy_entry->proxy_port = htons(proxy_port);
+ proxy_entry->server_port = htons(server_port);
+ proxy_entry->server_addr = server_addr;
+ proxy_entry->src_addr.s_addr = src_addr.s_addr & src_mask.s_addr;
+ proxy_entry->dst_addr.s_addr = dst_addr.s_addr & dst_mask.s_addr;
+ proxy_entry->src_mask = src_mask;
+ proxy_entry->dst_mask = dst_mask;
+
+ RuleAdd(la, proxy_entry);
+
+getout:
+ LIBALIAS_UNLOCK(la);
+ return (ret);
+}
diff --git a/src/VBox/Devices/Network/slirp/libalias/alias_skinny.c b/src/VBox/Devices/Network/slirp/libalias/alias_skinny.c
new file mode 100644
index 00000000..4b9bab26
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/alias_skinny.c
@@ -0,0 +1,447 @@
+/*-
+ * alias_skinny.c
+ *
+ * Copyright (c) 2002, 2003 MarcusCom, 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.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR 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 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.
+ *
+ * Author: Joe Marcus Clarke <marcus@FreeBSD.org>
+ *
+ * $FreeBSD: src/sys/netinet/libalias/alias_skinny.c,v 1.14.8.1 2009/04/15 03:14:26 kensmith Exp $
+ */
+
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#else
+#include <errno.h>
+#include <stdio.h>
+#include <unistd.h>
+#endif
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#ifdef _KERNEL
+#include <netinet/libalias/alias_local.h>
+#include <netinet/libalias/alias_mod.h>
+#else
+#include "alias_local.h"
+#include "alias_mod.h"
+#endif
+
+static void
+AliasHandleSkinny(struct libalias *, struct ip *, struct alias_link *);
+
+static int
+fingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL)
+ return (-1);
+ if (la->skinnyPort != 0 && (ntohs(*ah->sport) == la->skinnyPort ||
+ ntohs(*ah->dport) == la->skinnyPort))
+ return (0);
+ return (-1);
+}
+
+static int
+protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ AliasHandleSkinny(la, pip, ah->lnk);
+ return (0);
+}
+
+struct proto_handler handlers[] = {
+ {
+ .pri = 110,
+ .dir = IN|OUT,
+ .proto = TCP,
+ .fingerprint = &fingerprint,
+ .protohandler = &protohandler
+ },
+ { EOH }
+};
+
+static int
+mod_handler(module_t mod, int type, void *data)
+{
+ int error;
+
+ switch (type) {
+ case MOD_LOAD:
+ error = 0;
+ LibAliasAttachHandlers(handlers);
+ break;
+ case MOD_UNLOAD:
+ error = 0;
+ LibAliasDetachHandlers(handlers);
+ break;
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
+
+#ifdef _KERNEL
+static
+#endif
+moduledata_t alias_mod = {
+ "alias_skinny", mod_handler, NULL
+};
+
+#ifdef _KERNEL
+DECLARE_MODULE(alias_skinny, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
+MODULE_VERSION(alias_skinny, 1);
+MODULE_DEPEND(alias_skinny, libalias, 1, 1, 1);
+#endif
+
+/*
+ * alias_skinny.c handles the translation for the Cisco Skinny Station
+ * protocol. Skinny typically uses TCP port 2000 to set up calls between
+ * a Cisco Call Manager and a Cisco IP phone. When a phone comes on line,
+ * it first needs to register with the Call Manager. To do this it sends
+ * a registration message. This message contains the IP address of the
+ * IP phone. This message must then be translated to reflect our global
+ * IP address. Along with the registration message (and usually in the
+ * same packet), the phone sends an IP port message. This message indicates
+ * the TCP port over which it will communicate.
+ *
+ * When a call is placed from the phone, the Call Manager will send an
+ * Open Receive Channel message to the phone to let the caller know someone
+ * has answered. The phone then sends back an Open Receive Channel
+ * Acknowledgement. In this packet, the phone sends its IP address again,
+ * and the UDP port over which the voice traffic should flow. These values
+ * need translation. Right after the Open Receive Channel Acknowledgement,
+ * the Call Manager sends a Start Media Transmission message indicating the
+ * call is connected. This message contains the IP address and UDP port
+ * number of the remote (called) party. Once this message is translated, the
+ * call can commence. The called part sends the first UDP packet to the
+ * calling phone at the pre-arranged UDP port in the Open Receive Channel
+ * Acknowledgement.
+ *
+ * Skinny is a Cisco-proprietary protocol and is a trademark of Cisco Systems,
+ * Inc. All rights reserved.
+*/
+
+/* #define LIBALIAS_DEBUG 1 */
+
+/* Message types that need translating */
+#define REG_MSG 0x00000001
+#define IP_PORT_MSG 0x00000002
+#define OPNRCVCH_ACK 0x00000022
+#define START_MEDIATX 0x0000008a
+
+struct skinny_header {
+ u_int32_t len;
+ u_int32_t reserved;
+ u_int32_t msgId;
+};
+
+struct RegisterMessage {
+ u_int32_t msgId;
+ char devName [16];
+ u_int32_t uid;
+ u_int32_t instance;
+ u_int32_t ipAddr;
+ u_char devType;
+ u_int32_t maxStreams;
+};
+
+struct IpPortMessage {
+ u_int32_t msgId;
+ u_int32_t stationIpPort; /* Note: Skinny uses 32-bit port
+ * numbers */
+};
+
+struct OpenReceiveChannelAck {
+ u_int32_t msgId;
+ u_int32_t status;
+ u_int32_t ipAddr;
+ u_int32_t port;
+ u_int32_t passThruPartyID;
+};
+
+struct StartMediaTransmission {
+ u_int32_t msgId;
+ u_int32_t conferenceID;
+ u_int32_t passThruPartyID;
+ u_int32_t remoteIpAddr;
+ u_int32_t remotePort;
+ u_int32_t MSPacket;
+ u_int32_t payloadCap;
+ u_int32_t precedence;
+ u_int32_t silenceSuppression;
+ u_short maxFramesPerPacket;
+ u_int32_t G723BitRate;
+};
+
+typedef enum {
+ ClientToServer = 0,
+ ServerToClient = 1
+} ConvDirection;
+
+
+static int
+alias_skinny_reg_msg(struct RegisterMessage *reg_msg, struct ip *pip,
+ struct tcphdr *tc, struct alias_link *lnk,
+ ConvDirection direction)
+{
+ (void)direction;
+
+ reg_msg->ipAddr = (u_int32_t) GetAliasAddress(lnk).s_addr;
+
+ tc->th_sum = 0;
+#ifdef _KERNEL
+ tc->th_x2 = 1;
+#else
+ tc->th_sum = TcpChecksum(pip);
+#endif
+
+ return (0);
+}
+
+static int
+alias_skinny_startmedia(struct StartMediaTransmission *start_media,
+ struct ip *pip, struct tcphdr *tc,
+ struct alias_link *lnk, u_int32_t localIpAddr,
+ ConvDirection direction)
+{
+ struct in_addr dst, src;
+
+ (void)pip;
+ (void)tc;
+ (void)lnk;
+ (void)direction;
+
+ dst.s_addr = start_media->remoteIpAddr;
+ src.s_addr = localIpAddr;
+
+ /*
+ * XXX I should probably handle in bound global translations as
+ * well.
+ */
+
+ return (0);
+}
+
+static int
+alias_skinny_port_msg(struct IpPortMessage *port_msg, struct ip *pip,
+ struct tcphdr *tc, struct alias_link *lnk,
+ ConvDirection direction)
+{
+ (void)direction;
+
+ port_msg->stationIpPort = (u_int32_t) ntohs(GetAliasPort(lnk));
+
+ tc->th_sum = 0;
+#ifdef _KERNEL
+ tc->th_x2 = 1;
+#else
+ tc->th_sum = TcpChecksum(pip);
+#endif
+ return (0);
+}
+
+static int
+alias_skinny_opnrcvch_ack(struct libalias *la, struct OpenReceiveChannelAck *opnrcvch_ack,
+ struct ip *pip, struct tcphdr *tc,
+ struct alias_link *lnk, u_int32_t * localIpAddr,
+ ConvDirection direction)
+{
+ struct in_addr null_addr;
+ struct alias_link *opnrcv_lnk;
+ u_int32_t localPort;
+
+ (void)lnk;
+ (void)direction;
+
+ *localIpAddr = (u_int32_t) opnrcvch_ack->ipAddr;
+ localPort = opnrcvch_ack->port;
+
+ null_addr.s_addr = INADDR_ANY;
+ opnrcv_lnk = FindUdpTcpOut(la, pip->ip_src, null_addr,
+ htons((u_short) opnrcvch_ack->port), 0,
+ IPPROTO_UDP, 1);
+ opnrcvch_ack->ipAddr = (u_int32_t) GetAliasAddress(opnrcv_lnk).s_addr;
+ opnrcvch_ack->port = (u_int32_t) ntohs(GetAliasPort(opnrcv_lnk));
+
+ tc->th_sum = 0;
+#ifdef _KERNEL
+ tc->th_x2 = 1;
+#else
+ tc->th_sum = TcpChecksum(pip);
+#endif
+ return (0);
+}
+
+static void
+AliasHandleSkinny(struct libalias *la, struct ip *pip, struct alias_link *lnk)
+{
+ size_t hlen, tlen, dlen;
+ struct tcphdr *tc;
+ u_int32_t msgId, t, len, lip;
+ struct skinny_header *sd;
+ size_t orig_len, skinny_hdr_len = sizeof(struct skinny_header);
+ ConvDirection direction;
+
+ lip = -1;
+ tc = (struct tcphdr *)ip_next(pip);
+ hlen = (pip->ip_hl + tc->th_off) << 2;
+ tlen = ntohs(pip->ip_len);
+ dlen = tlen - hlen;
+
+ sd = (struct skinny_header *)tcp_next(tc);
+
+ /*
+ * XXX This direction is reserved for future use. I still need to
+ * handle the scenario where the call manager is on the inside, and
+ * the calling phone is on the global outside.
+ */
+ if (ntohs(tc->th_dport) == la->skinnyPort) {
+ direction = ClientToServer;
+ } else if (ntohs(tc->th_sport) == la->skinnyPort) {
+ direction = ServerToClient;
+ } else {
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr,
+ "PacketAlias/Skinny: Invalid port number, not a Skinny packet\n");
+#endif
+ return;
+ }
+
+ orig_len = dlen;
+ /*
+ * Skinny packets can contain many messages. We need to loop
+ * through the packet using len to determine message boundaries.
+ * This comes into play big time with port messages being in the
+ * same packet as register messages. Also, open receive channel
+ * acks are usually buried in a pakcet some 400 bytes long.
+ */
+ while (dlen >= skinny_hdr_len) {
+ len = (sd->len);
+ msgId = (sd->msgId);
+ t = len;
+
+ if (t > orig_len || t > dlen) {
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr,
+ "PacketAlias/Skinny: Not a skinny packet, invalid length \n");
+#endif
+ return;
+ }
+ switch (msgId) {
+ case REG_MSG: {
+ struct RegisterMessage *reg_mesg;
+
+ if (len < (int)sizeof(struct RegisterMessage)) {
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr,
+ "PacketAlias/Skinny: Not a skinny packet, bad registration message\n");
+#endif
+ return;
+ }
+ reg_mesg = (struct RegisterMessage *)&sd->msgId;
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr,
+ "PacketAlias/Skinny: Received a register message");
+#endif
+ alias_skinny_reg_msg(reg_mesg, pip, tc, lnk, direction);
+ break;
+ }
+ case IP_PORT_MSG: {
+ struct IpPortMessage *port_mesg;
+
+ if (len < (int)sizeof(struct IpPortMessage)) {
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr,
+ "PacketAlias/Skinny: Not a skinny packet, port message\n");
+#endif
+ return;
+ }
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr,
+ "PacketAlias/Skinny: Received ipport message\n");
+#endif
+ port_mesg = (struct IpPortMessage *)&sd->msgId;
+ alias_skinny_port_msg(port_mesg, pip, tc, lnk, direction);
+ break;
+ }
+ case OPNRCVCH_ACK: {
+ struct OpenReceiveChannelAck *opnrcvchn_ack;
+
+ if (len < (int)sizeof(struct OpenReceiveChannelAck)) {
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr,
+ "PacketAlias/Skinny: Not a skinny packet, packet,OpnRcvChnAckMsg\n");
+#endif
+ return;
+ }
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr,
+ "PacketAlias/Skinny: Received open rcv channel msg\n");
+#endif
+ opnrcvchn_ack = (struct OpenReceiveChannelAck *)&sd->msgId;
+ alias_skinny_opnrcvch_ack(la, opnrcvchn_ack, pip, tc, lnk, &lip, direction);
+ break;
+ }
+ case START_MEDIATX: {
+ struct StartMediaTransmission *startmedia_tx;
+
+ if (len < (int)sizeof(struct StartMediaTransmission)) {
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr,
+ "PacketAlias/Skinny: Not a skinny packet,StartMediaTx Message\n");
+#endif
+ return;
+ }
+ if (lip == -1) {
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr,
+ "PacketAlias/Skinny: received a"
+ " packet,StartMediaTx Message before"
+ " packet,OpnRcvChnAckMsg\n"
+#endif
+ return;
+ }
+
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr,
+ "PacketAlias/Skinny: Received start media trans msg\n");
+#endif
+ startmedia_tx = (struct StartMediaTransmission *)&sd->msgId;
+ alias_skinny_startmedia(startmedia_tx, pip, tc, lnk, lip, direction);
+ break;
+ }
+ default:
+ break;
+ }
+ /* Place the pointer at the next message in the packet. */
+ dlen -= len + (skinny_hdr_len - sizeof(msgId));
+ sd = (struct skinny_header *)(((char *)&sd->msgId) + len);
+ }
+}
diff --git a/src/VBox/Devices/Network/slirp/libalias/alias_smedia.c b/src/VBox/Devices/Network/slirp/libalias/alias_smedia.c
new file mode 100644
index 00000000..75969567
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/alias_smedia.c
@@ -0,0 +1,547 @@
+/*
+ * alias_smedia.c
+ *
+ * Copyright (c) 2000 Whistle Communications, Inc.
+ * All rights reserved.
+ *
+ * Subject to the following obligations and disclaimer of warranty, use and
+ * redistribution of this software, in source or object code forms, with or
+ * without modifications are expressly permitted by Whistle Communications;
+ * provided, however, that:
+ * 1. Any and all reproductions of the source or object code must include the
+ * copyright notice above and the following disclaimer of warranties; and
+ * 2. No rights are granted, in any manner or form, to use Whistle
+ * Communications, Inc. trademarks, including the mark "WHISTLE
+ * COMMUNICATIONS" on advertising, endorsements, or otherwise except as
+ * such appears in the above copyright notice or in the software.
+ *
+ * THIS SOFTWARE IS BEING PROVIDED BY WHISTLE COMMUNICATIONS "AS IS", AND
+ * TO THE MAXIMUM EXTENT PERMITTED BY LAW, WHISTLE COMMUNICATIONS MAKES NO
+ * REPRESENTATIONS OR WARRANTIES, EXPRESS OR IMPLIED, REGARDING THIS SOFTWARE,
+ * INCLUDING WITHOUT LIMITATION, ANY AND ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, OR NON-INFRINGEMENT.
+ * WHISTLE COMMUNICATIONS DOES NOT WARRANT, GUARANTEE, OR MAKE ANY
+ * REPRESENTATIONS REGARDING THE USE OF, OR THE RESULTS OF THE USE OF THIS
+ * SOFTWARE IN TERMS OF ITS CORRECTNESS, ACCURACY, RELIABILITY OR OTHERWISE.
+ * IN NO EVENT SHALL WHISTLE COMMUNICATIONS BE LIABLE FOR ANY DAMAGES
+ * RESULTING FROM OR ARISING OUT OF ANY USE OF THIS SOFTWARE, INCLUDING
+ * WITHOUT LIMITATION, ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
+ * PUNITIVE, OR CONSEQUENTIAL DAMAGES, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES, LOSS OF USE, DATA OR PROFITS, HOWEVER CAUSED AND UNDER ANY
+ * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
+ * THIS SOFTWARE, EVEN IF WHISTLE COMMUNICATIONS IS ADVISED OF THE POSSIBILITY
+ * OF SUCH DAMAGE.
+ *
+ * Copyright (c) 2000 Junichi SATOH <junichi@astec.co.jp>
+ * <junichi@junichi.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:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHOR 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 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.
+ *
+ * Authors: Erik Salander <erik@whistle.com>
+ * Junichi SATOH <junichi@astec.co.jp>
+ * <junichi@junichi.org>
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netinet/libalias/alias_smedia.c,v 1.17.8.1 2009/04/15 03:14:26 kensmith Exp $");
+
+/*
+ Alias_smedia.c is meant to contain the aliasing code for streaming media
+ protocols. It performs special processing for RSTP sessions under TCP.
+ Specifically, when a SETUP request is sent by a client, or a 200 reply
+ is sent by a server, it is intercepted and modified. The address is
+ changed to the gateway machine and an aliasing port is used.
+
+ More specifically, the "client_port" configuration parameter is
+ parsed for SETUP requests. The "server_port" configuration parameter is
+ parsed for 200 replies eminating from a server. This is intended to handle
+ the unicast case.
+
+ RTSP also allows a redirection of a stream to another client by using the
+ "destination" configuration parameter. The destination config parm would
+ indicate a different IP address. This function is NOT supported by the
+ RTSP translation code below.
+
+ The RTSP multicast functions without any address translation intervention.
+
+ For this routine to work, the SETUP/200 must fit entirely
+ into a single TCP packet. This is typically the case, but exceptions
+ can easily be envisioned under the actual specifications.
+
+ Probably the most troubling aspect of the approach taken here is
+ that the new SETUP/200 will typically be a different length, and
+ this causes a certain amount of bookkeeping to keep track of the
+ changes of sequence and acknowledgment numbers, since the client
+ machine is totally unaware of the modification to the TCP stream.
+
+ Initial version: May, 2000 (eds)
+*/
+
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/module.h>
+#else
+#include <errno.h>
+#include <sys/types.h>
+#include <stdio.h>
+#include <string.h>
+#endif
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#ifdef _KERNEL
+#include <netinet/libalias/alias.h>
+#include <netinet/libalias/alias_local.h>
+#include <netinet/libalias/alias_mod.h>
+#else
+#include "alias_local.h"
+#include "alias_mod.h"
+#endif
+
+#define RTSP_CONTROL_PORT_NUMBER_1 554
+#define RTSP_CONTROL_PORT_NUMBER_2 7070
+#define TFTP_PORT_NUMBER 69
+
+static void
+AliasHandleRtspOut(struct libalias *, struct ip *, struct alias_link *,
+ int maxpacketsize);
+static int
+fingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ if (ah->dport != NULL && ah->aport != NULL && ah->sport != NULL &&
+ ntohs(*ah->dport) == TFTP_PORT_NUMBER)
+ return (0);
+ if (ah->dport == NULL || ah->sport == NULL || ah->lnk == NULL ||
+ ah->maxpktsize == 0)
+ return (-1);
+ if (ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_1
+ || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_1
+ || ntohs(*ah->dport) == RTSP_CONTROL_PORT_NUMBER_2
+ || ntohs(*ah->sport) == RTSP_CONTROL_PORT_NUMBER_2)
+ return (0);
+ return (-1);
+}
+
+static int
+protohandler(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+ if (ntohs(*ah->dport) == TFTP_PORT_NUMBER)
+ FindRtspOut(la, pip->ip_src, pip->ip_dst,
+ *ah->sport, *ah->aport, IPPROTO_UDP);
+ else AliasHandleRtspOut(la, pip, ah->lnk, ah->maxpktsize);
+ return (0);
+}
+
+struct proto_handler handlers[] = {
+ {
+ .pri = 100,
+ .dir = OUT,
+ .proto = TCP|UDP,
+ .fingerprint = &fingerprint,
+ .protohandler = &protohandler
+ },
+ { EOH }
+};
+
+static int
+mod_handler(module_t mod, int type, void *data)
+{
+ int error;
+
+ switch (type) {
+ case MOD_LOAD:
+ error = 0;
+ LibAliasAttachHandlers(handlers);
+ break;
+ case MOD_UNLOAD:
+ error = 0;
+ LibAliasDetachHandlers(handlers);
+ break;
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
+
+#ifdef _KERNEL
+static
+#endif
+moduledata_t alias_mod = {
+ "alias_smedia", mod_handler, NULL
+};
+
+#ifdef _KERNEL
+DECLARE_MODULE(alias_smedia, alias_mod, SI_SUB_DRIVERS, SI_ORDER_SECOND);
+MODULE_VERSION(alias_smedia, 1);
+MODULE_DEPEND(alias_smedia, libalias, 1, 1, 1);
+#endif
+
+#define RTSP_CONTROL_PORT_NUMBER_1 554
+#define RTSP_CONTROL_PORT_NUMBER_2 7070
+#define RTSP_PORT_GROUP 2
+
+#define ISDIGIT(a) (((a) >= '0') && ((a) <= '9'))
+
+static int
+search_string(char *data, int dlen, const char *search_str)
+{
+ int i, j, k;
+ int search_str_len;
+
+ search_str_len = strlen(search_str);
+ for (i = 0; i < dlen - search_str_len; i++) {
+ for (j = i, k = 0; j < dlen - search_str_len; j++, k++) {
+ if (data[j] != search_str[k] &&
+ data[j] != search_str[k] - ('a' - 'A')) {
+ break;
+ }
+ if (k == search_str_len - 1) {
+ return (j + 1);
+ }
+ }
+ }
+ return (-1);
+}
+
+static int
+alias_rtsp_out(struct libalias *la, struct ip *pip,
+ struct alias_link *lnk,
+ char *data,
+ const char *port_str)
+{
+ int hlen, tlen, dlen;
+ struct tcphdr *tc;
+ int i, j, pos, state, port_dlen, new_dlen, delta;
+ u_short p[2], new_len;
+ u_short sport, eport, base_port;
+ u_short salias = 0, ealias = 0, base_alias = 0;
+ const char *transport_str = "transport:";
+ char newdata[2048], *port_data, *port_newdata, stemp[80];
+ int links_created = 0, pkt_updated = 0;
+ struct alias_link *rtsp_lnk = NULL;
+ struct in_addr null_addr;
+
+ /* Calculate data length of TCP packet */
+ tc = (struct tcphdr *)ip_next(pip);
+ hlen = (pip->ip_hl + tc->th_off) << 2;
+ tlen = ntohs(pip->ip_len);
+ dlen = tlen - hlen;
+
+ /* Find keyword, "Transport: " */
+ pos = search_string(data, dlen, transport_str);
+ if (pos < 0) {
+ return (-1);
+ }
+ port_data = data + pos;
+ port_dlen = dlen - pos;
+
+ memcpy(newdata, data, pos);
+ port_newdata = newdata + pos;
+
+ while (port_dlen > (int)strlen(port_str)) {
+ /* Find keyword, appropriate port string */
+ pos = search_string(port_data, port_dlen, port_str);
+ if (pos < 0) {
+ break;
+ }
+ memcpy(port_newdata, port_data, pos + 1);
+ port_newdata += (pos + 1);
+
+ p[0] = p[1] = 0;
+ sport = eport = 0;
+ state = 0;
+ for (i = pos; i < port_dlen; i++) {
+ switch (state) {
+ case 0:
+ if (port_data[i] == '=') {
+ state++;
+ }
+ break;
+ case 1:
+ if (ISDIGIT(port_data[i])) {
+ p[0] = p[0] * 10 + port_data[i] - '0';
+ } else {
+ if (port_data[i] == ';') {
+ state = 3;
+ }
+ if (port_data[i] == '-') {
+ state++;
+ }
+ }
+ break;
+ case 2:
+ if (ISDIGIT(port_data[i])) {
+ p[1] = p[1] * 10 + port_data[i] - '0';
+ } else {
+ state++;
+ }
+ break;
+ case 3:
+ base_port = p[0];
+ sport = htons(p[0]);
+ eport = htons(p[1]);
+
+ if (!links_created) {
+
+ links_created = 1;
+ /*
+ * Find an even numbered port
+ * number base that satisfies the
+ * contiguous number of ports we
+ * need
+ */
+ null_addr.s_addr = 0;
+ if (0 == (salias = FindNewPortGroup(la, null_addr,
+ FindAliasAddress(la, pip->ip_src),
+ sport, 0,
+ RTSP_PORT_GROUP,
+ IPPROTO_UDP, 1))) {
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr,
+ "PacketAlias/RTSP: Cannot find contiguous RTSP data ports\n");
+#endif
+ } else {
+
+ base_alias = ntohs(salias);
+ for (j = 0; j < RTSP_PORT_GROUP; j++) {
+ /*
+ * Establish link
+ * to port found in
+ * RTSP packet
+ */
+ rtsp_lnk = FindRtspOut(la, GetOriginalAddress(lnk), null_addr,
+ htons(base_port + j), htons(base_alias + j),
+ IPPROTO_UDP);
+ if (rtsp_lnk != NULL) {
+#ifndef NO_FW_PUNCH
+ /*
+ * Punch
+ * hole in
+ * firewall
+ */
+ PunchFWHole(rtsp_lnk);
+#endif
+ } else {
+#ifdef LIBALIAS_DEBUG
+ fprintf(stderr,
+ "PacketAlias/RTSP: Cannot allocate RTSP data ports\n");
+#endif
+ break;
+ }
+ }
+ }
+ ealias = htons(base_alias + (RTSP_PORT_GROUP - 1));
+ }
+ if (salias && rtsp_lnk) {
+
+ pkt_updated = 1;
+
+ /* Copy into IP packet */
+ sprintf(stemp, "%d", ntohs(salias));
+ memcpy(port_newdata, stemp, strlen(stemp));
+ port_newdata += strlen(stemp);
+
+ if (eport != 0) {
+ *port_newdata = '-';
+ port_newdata++;
+
+ /* Copy into IP packet */
+ sprintf(stemp, "%d", ntohs(ealias));
+ memcpy(port_newdata, stemp, strlen(stemp));
+ port_newdata += strlen(stemp);
+ }
+ *port_newdata = ';';
+ port_newdata++;
+ }
+ state++;
+ break;
+ }
+ if (state > 3) {
+ break;
+ }
+ }
+ port_data += i;
+ port_dlen -= i;
+ }
+
+ if (!pkt_updated)
+ return (-1);
+
+ memcpy(port_newdata, port_data, port_dlen);
+ port_newdata += port_dlen;
+ *port_newdata = '\0';
+
+ /* Create new packet */
+ new_dlen = port_newdata - newdata;
+ memcpy(data, newdata, new_dlen);
+
+ SetAckModified(lnk);
+ delta = GetDeltaSeqOut(pip, lnk);
+ AddSeq(pip, lnk, delta + new_dlen - dlen);
+
+ new_len = htons(hlen + new_dlen);
+ DifferentialChecksum(&pip->ip_sum,
+ &new_len,
+ &pip->ip_len,
+ 1);
+ pip->ip_len = new_len;
+
+ tc->th_sum = 0;
+#ifdef _KERNEL
+ tc->th_x2 = 1;
+#else
+ tc->th_sum = TcpChecksum(pip);
+#endif
+ return (0);
+}
+
+/* Support the protocol used by early versions of RealPlayer */
+
+static int
+alias_pna_out(struct libalias *la, struct ip *pip,
+ struct alias_link *lnk,
+ char *data,
+ int dlen)
+{
+ struct alias_link *pna_links;
+ u_short msg_id, msg_len;
+ char *work;
+ u_short alias_port, port;
+ struct tcphdr *tc;
+
+ work = data;
+ work += 5;
+ while (work + 4 < data + dlen) {
+ memcpy(&msg_id, work, 2);
+ work += 2;
+ memcpy(&msg_len, work, 2);
+ work += 2;
+ if (ntohs(msg_id) == 0) {
+ /* end of options */
+ return (0);
+ }
+ if ((ntohs(msg_id) == 1) || (ntohs(msg_id) == 7)) {
+ memcpy(&port, work, 2);
+ pna_links = FindUdpTcpOut(la, pip->ip_src, GetDestAddress(lnk),
+ port, 0, IPPROTO_UDP, 1);
+ if (pna_links != NULL) {
+#ifndef NO_FW_PUNCH
+ /* Punch hole in firewall */
+ PunchFWHole(pna_links);
+#endif
+ tc = (struct tcphdr *)ip_next(pip);
+ alias_port = GetAliasPort(pna_links);
+ memcpy(work, &alias_port, 2);
+
+ /* Compute TCP checksum for revised packet */
+ tc->th_sum = 0;
+#ifdef _KERNEL
+ tc->th_x2 = 1;
+#else
+ tc->th_sum = TcpChecksum(pip);
+#endif
+ }
+ }
+ work += ntohs(msg_len);
+ }
+
+ return (0);
+}
+
+static void
+AliasHandleRtspOut(struct libalias *la, struct ip *pip, struct alias_link *lnk, int maxpacketsize)
+{
+ int hlen, tlen, dlen;
+ struct tcphdr *tc;
+ char *data;
+ const char *setup = "SETUP", *pna = "PNA", *str200 = "200";
+ const char *okstr = "OK", *client_port_str = "client_port";
+ const char *server_port_str = "server_port";
+ int i, parseOk;
+
+ (void)maxpacketsize;
+
+ tc = (struct tcphdr *)ip_next(pip);
+ hlen = (pip->ip_hl + tc->th_off) << 2;
+ tlen = ntohs(pip->ip_len);
+ dlen = tlen - hlen;
+
+ data = (char *)pip;
+ data += hlen;
+
+ /* When aliasing a client, check for the SETUP request */
+ if ((ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_1) ||
+ (ntohs(tc->th_dport) == RTSP_CONTROL_PORT_NUMBER_2)) {
+
+ if (dlen >= (int)strlen(setup)) {
+ if (memcmp(data, setup, strlen(setup)) == 0) {
+ alias_rtsp_out(la, pip, lnk, data, client_port_str);
+ return;
+ }
+ }
+ if (dlen >= (int)strlen(pna)) {
+ if (memcmp(data, pna, strlen(pna)) == 0) {
+ alias_pna_out(la, pip, lnk, data, dlen);
+ }
+ }
+ } else {
+
+ /*
+ * When aliasing a server, check for the 200 reply
+ * Accomodate varying number of blanks between 200 & OK
+ */
+
+ if (dlen >= (int)strlen(str200)) {
+
+ for (parseOk = 0, i = 0;
+ i <= dlen - (int)strlen(str200);
+ i++) {
+ if (memcmp(&data[i], str200, strlen(str200)) == 0) {
+ parseOk = 1;
+ break;
+ }
+ }
+ if (parseOk) {
+
+ i += strlen(str200); /* skip string found */
+ while (data[i] == ' ') /* skip blank(s) */
+ i++;
+
+ if ((dlen - i) >= (int)strlen(okstr)) {
+
+ if (memcmp(&data[i], okstr, strlen(okstr)) == 0)
+ alias_rtsp_out(la, pip, lnk, data, server_port_str);
+
+ }
+ }
+ }
+ }
+}
diff --git a/src/VBox/Devices/Network/slirp/libalias/alias_util.c b/src/VBox/Devices/Network/slirp/libalias/alias_util.c
new file mode 100644
index 00000000..9a0310d3
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/alias_util.c
@@ -0,0 +1,182 @@
+/*-
+ * Copyright (c) 2001 Charles Mott <cm@linktel.net>
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must 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 AUTHOR 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 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 VBOX
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD: src/sys/netinet/libalias/alias_util.c,v 1.20.8.1 2009/04/15 03:14:26 kensmith Exp $");
+
+
+/*
+ Alias_util.c contains general utilities used by other functions
+ in the packet aliasing module. At the moment, there are functions
+ for computing IP header and TCP packet checksums.
+
+ The checksum routines are based upon example code in a Unix networking
+ text written by Stevens (sorry, I can't remember the title -- but
+ at least this is a good author).
+
+ Initial Version: August, 1996 (cjm)
+
+ Version 1.7: January 9, 1997
+ Added differential checksum update function.
+*/
+
+#ifdef _KERNEL
+#include <sys/param.h>
+#include <sys/proc.h>
+#else
+#include <sys/types.h>
+#include <stdio.h>
+#endif
+
+#include <netinet/in_systm.h>
+#include <netinet/in.h>
+#include <netinet/ip.h>
+#include <netinet/tcp.h>
+
+#ifdef _KERNEL
+#include <netinet/libalias/alias.h>
+#include <netinet/libalias/alias_local.h>
+#else
+#include "alias.h"
+#include "alias_local.h"
+#endif
+#else /* VBOX */
+# include <slirp.h>
+# include "alias.h"
+# include "alias_local.h"
+#endif /* VBOX */
+
+/*
+ * Note: the checksum routines assume that the actual checksum word has
+ * been zeroed out. If the checksum word is filled with the proper value,
+ * then these routines will give a result of zero (useful for testing
+ * purposes);
+ */
+u_short
+LibAliasInternetChecksum(struct libalias *la __unused, u_short * ptr,
+ int nbytes)
+{
+ int sum, oddbyte;
+
+ LIBALIAS_LOCK(la);
+ sum = 0;
+ while (nbytes > 1) {
+ sum += *ptr++;
+ nbytes -= 2;
+ }
+ if (nbytes == 1) {
+ oddbyte = 0;
+ ((u_char *) & oddbyte)[0] = *(u_char *) ptr;
+ ((u_char *) & oddbyte)[1] = 0;
+ sum += oddbyte;
+ }
+ sum = (sum >> 16) + (sum & 0xffff);
+ sum += (sum >> 16);
+ LIBALIAS_UNLOCK(la);
+ return (~sum);
+}
+
+#ifndef _KERNEL
+u_short
+IpChecksum(struct ip *pip)
+{
+ return (LibAliasInternetChecksum(NULL, (u_short *) pip,
+ (pip->ip_hl << 2)));
+
+}
+
+u_short
+TcpChecksum(struct ip *pip)
+{
+ u_short *ptr;
+ struct tcphdr *tc;
+ int nhdr, ntcp, nbytes;
+ int sum, oddbyte;
+
+ nhdr = pip->ip_hl << 2;
+ ntcp = ntohs(pip->ip_len) - nhdr;
+
+ tc = (struct tcphdr *)ip_next(pip);
+ ptr = (u_short *) tc;
+
+/* Add up TCP header and data */
+ nbytes = ntcp;
+ sum = 0;
+ while (nbytes > 1) {
+ sum += *ptr++;
+ nbytes -= 2;
+ }
+ if (nbytes == 1) {
+ oddbyte = 0;
+ ((u_char *) & oddbyte)[0] = *(u_char *) ptr;
+ ((u_char *) & oddbyte)[1] = 0;
+ sum += oddbyte;
+ }
+/* "Pseudo-header" data */
+ ptr = (u_short *) & (pip->ip_dst);
+ sum += *ptr++;
+ sum += *ptr;
+ ptr = (u_short *) & (pip->ip_src);
+ sum += *ptr++;
+ sum += *ptr;
+ sum += htons((u_short) ntcp);
+ sum += htons((u_short) pip->ip_p);
+
+/* Roll over carry bits */
+ sum = (sum >> 16) + (sum & 0xffff);
+ sum += (sum >> 16);
+
+/* Return checksum */
+ return ((u_short) ~ sum);
+}
+#endif /* not _KERNEL */
+
+void
+DifferentialChecksum(u_short * cksum, void *newp, void *oldp, int n)
+{
+ int i;
+ int accumulate;
+ u_short *new = newp;
+ u_short *old = oldp;
+
+ accumulate = *cksum;
+ for (i = 0; i < n; i++) {
+ accumulate -= *new++;
+ accumulate += *old++;
+ }
+
+ if (accumulate < 0) {
+ accumulate = -accumulate;
+ accumulate = (accumulate >> 16) + (accumulate & 0xffff);
+ accumulate += accumulate >> 16;
+ *cksum = (u_short) ~ accumulate;
+ } else {
+ accumulate = (accumulate >> 16) + (accumulate & 0xffff);
+ accumulate += accumulate >> 16;
+ *cksum = (u_short) accumulate;
+ }
+}
diff --git a/src/VBox/Devices/Network/slirp/libalias/libalias.3 b/src/VBox/Devices/Network/slirp/libalias/libalias.3
new file mode 100644
index 00000000..367e37df
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libalias/libalias.3
@@ -0,0 +1,1458 @@
+.\"-
+.\" Copyright (c) 2001 Charles Mott <cm@linktel.net>
+.\" All rights reserved.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\" notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must 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 AUTHOR 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 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.
+.\"
+.\" $FreeBSD: src/sys/netinet/libalias/libalias.3,v 1.58.8.1 2009/04/15 03:14:26 kensmith Exp $
+.\"
+.Dd October 1, 2006
+.Dt LIBALIAS 3
+.Os
+.Sh NAME
+.Nm libalias
+.Nd packet aliasing library for masquerading and network address translation
+.Sh SYNOPSIS
+.In sys/types.h
+.In netinet/in.h
+.In alias.h
+.Pp
+Function prototypes are given in the main body of the text.
+.Sh DESCRIPTION
+The
+.Nm
+library is a collection of functions for aliasing and de-aliasing of IP
+packets, intended for masquerading and network address translation (NAT).
+.Sh INTRODUCTION
+This library is a moderately portable set of functions designed to assist
+in the process of IP masquerading and network address translation.
+Outgoing packets from a local network with unregistered IP addresses can
+be aliased to appear as if they came from an accessible IP address.
+Incoming packets are then de-aliased so that they are sent to the correct
+machine on the local network.
+.Pp
+A certain amount of flexibility is built into the packet aliasing engine.
+In the simplest mode of operation, a many-to-one address mapping takes
+place between local network and the packet aliasing host.
+This is known as IP masquerading.
+In addition, one-to-one mappings between local and public addresses can
+also be implemented, which is known as static NAT.
+In between these extremes, different groups of private addresses can be
+linked to different public addresses, comprising several distinct
+many-to-one mappings.
+Also, a given public address and port can be statically redirected to a
+private address/port.
+.Pp
+The packet aliasing engine was designed to operate in user space outside
+of the kernel, without any access to private kernel data structure, but
+the source code can also be ported to a kernel environment.
+.Sh INITIALIZATION AND CONTROL
+One special function,
+.Fn LibAliasInit ,
+must always be called before any packet handling may be performed and
+the returned instance pointer passed to all the other functions.
+Normally, the
+.Fn LibAliasSetAddress
+function is called afterwards, to set the default aliasing address.
+In addition, the operating mode of the packet aliasing engine can be
+customized by calling
+.Fn LibAliasSetMode .
+.Pp
+.Ft "struct libalias *"
+.Fn LibAliasInit "struct libalias *"
+.Bd -ragged -offset indent
+This function is used to initialize
+internal data structures.
+When called the first time, a
+.Dv NULL
+pointer should be passed as an argument.
+The following mode bits are always set after calling
+.Fn LibAliasInit .
+See the description of
+.Fn LibAliasSetMode
+below for the meaning of these mode bits.
+.Pp
+.Bl -item -offset indent -compact
+.It
+.Dv PKT_ALIAS_SAME_PORTS
+.It
+.Dv PKT_ALIAS_USE_SOCKETS
+.It
+.Dv PKT_ALIAS_RESET_ON_ADDR_CHANGE
+.El
+.Pp
+This function will always return the packet aliasing engine to the same
+initial state.
+The
+.Fn LibAliasSetAddress
+function is normally called afterwards, and any desired changes from the
+default mode bits listed above require a call to
+.Fn LibAliasSetMode .
+.Pp
+It is mandatory that this function be called at the beginning of a program
+prior to any packet handling.
+.Ed
+.Pp
+.Ft void
+.Fn LibAliasUninit "struct libalias *"
+.Bd -ragged -offset indent
+This function has no return value and is used to clear any
+resources attached to internal data structures.
+.Pp
+This functions should be called when a program stops using the aliasing
+engine; it does, amongst other things, clear out any firewall holes.
+To provide backwards compatibility and extra security, it is added to
+the
+.Xr atexit 3
+chain by
+.Fn LibAliasInit .
+.Ed
+.Pp
+.Ft void
+.Fn LibAliasSetAddress "struct libalias *" "struct in_addr addr"
+.Bd -ragged -offset indent
+This function sets the source address to which outgoing packets from the
+local area network are aliased.
+All outgoing packets are re-mapped to this address unless overridden by a
+static address mapping established by
+.Fn LibAliasRedirectAddr .
+If this function is not called, and no static rules match, an outgoing
+packet retains its source address.
+.Pp
+If the
+.Dv PKT_ALIAS_RESET_ON_ADDR_CHANGE
+mode bit is set (the default mode of operation), then the internal aliasing
+link tables will be reset any time the aliasing address changes.
+This is useful for interfaces such as
+.Xr ppp 8 ,
+where the IP
+address may or may not change on successive dial-up attempts.
+.Pp
+If the
+.Dv PKT_ALIAS_RESET_ON_ADDR_CHANGE
+mode bit is set to zero, this function can also be used to dynamically change
+the aliasing address on a packet to packet basis (it is a low overhead call).
+.Pp
+It is mandatory that this function be called prior to any packet handling.
+.Ed
+.Pp
+.Ft unsigned int
+.Fn LibAliasSetMode "struct libalias *" "unsigned int flags" "unsigned int mask"
+.Bd -ragged -offset indent
+This function sets or clears mode bits
+according to the value of
+.Fa flags .
+Only bits marked in
+.Fa mask
+are affected.
+The following mode bits are defined in
+.In alias.h :
+.Bl -tag -width indent
+.It Dv PKT_ALIAS_LOG
+Enables logging into
+.Pa /var/log/alias.log .
+Each time an aliasing link is created or deleted, the log file is appended
+with the current number of ICMP, TCP and UDP links.
+Mainly useful for debugging when the log file is viewed continuously with
+.Xr tail 1 .
+.It Dv PKT_ALIAS_DENY_INCOMING
+If this mode bit is set, all incoming packets associated with new TCP
+connections or new UDP transactions will be marked for being ignored
+.Fn ( LibAliasIn
+returns
+.Dv PKT_ALIAS_IGNORED
+code)
+by the calling program.
+Response packets to connections or transactions initiated from the packet
+aliasing host or local network will be unaffected.
+This mode bit is useful for implementing a one-way firewall.
+.It Dv PKT_ALIAS_SAME_PORTS
+If this mode bit is set, the packet aliasing engine will attempt to leave
+the alias port numbers unchanged from the actual local port numbers.
+This can be done as long as the quintuple (proto, alias addr, alias port,
+remote addr, remote port) is unique.
+If a conflict exists, a new aliasing port number is chosen even if this
+mode bit is set.
+.It Dv PKT_ALIAS_USE_SOCKETS
+This bit should be set when the packet aliasing host originates network
+traffic as well as forwards it.
+When the packet aliasing host is waiting for a connection from an unknown
+host address or unknown port number (e.g.\& an FTP data connection), this
+mode bit specifies that a socket be allocated as a place holder to prevent
+port conflicts.
+Once a connection is established, usually within a minute or so, the socket
+is closed.
+.It Dv PKT_ALIAS_UNREGISTERED_ONLY
+If this mode bit is set, traffic on the local network which does not
+originate from unregistered address spaces will be ignored.
+Standard Class A, B and C unregistered addresses are:
+.Bd -literal -offset indent
+10.0.0.0 -> 10.255.255.255 (Class A subnet)
+172.16.0.0 -> 172.31.255.255 (Class B subnets)
+192.168.0.0 -> 192.168.255.255 (Class C subnets)
+.Ed
+.Pp
+This option is useful in the case that packet aliasing host has both
+registered and unregistered subnets on different interfaces.
+The registered subnet is fully accessible to the outside world, so traffic
+from it does not need to be passed through the packet aliasing engine.
+.It Dv PKT_ALIAS_RESET_ON_ADDR_CHANGE
+When this mode bit is set and
+.Fn LibAliasSetAddress
+is called to change the aliasing address, the internal link table of the
+packet aliasing engine will be cleared.
+This operating mode is useful for
+.Xr ppp 8
+links where the interface address can sometimes change or remain the same
+between dial-up attempts.
+If this mode bit is not set, the link table will never be reset in the event
+of an address change.
+.It Dv PKT_ALIAS_PUNCH_FW
+This option makes
+.Nm
+`punch holes' in an
+.Xr ipfirewall 4
+based firewall for FTP/IRC DCC connections.
+The holes punched are bound by from/to IP address and port; it will not be
+possible to use a hole for another connection.
+A hole is removed when the connection that uses it dies.
+To cater to unexpected death of a program using
+.Nm
+(e.g.\& kill -9),
+changing the state of the flag will clear the entire firewall range
+allocated for holes.
+This will also happen on the initial call to
+.Fn LibAliasSetFWBase .
+This call must happen prior to setting this flag.
+.It Dv PKT_ALIAS_REVERSE
+This option makes
+.Nm
+reverse the way it handles incoming and outgoing packets, allowing it
+to be fed with data that passes through the internal interface rather
+than the external one.
+.It Dv PKT_ALIAS_PROXY_ONLY
+This option tells
+.Nm
+to obey transparent proxy rules only.
+Normal packet aliasing is not performed.
+See
+.Fn LibAliasProxyRule
+below for details.
+.El
+.Ed
+.Pp
+.Ft void
+.Fn LibAliasSetFWBase "struct libalias *" "unsigned int base" "unsigned int num"
+.Bd -ragged -offset indent
+Set firewall range allocated for punching firewall holes (with the
+.Dv PKT_ALIAS_PUNCH_FW
+flag).
+The range will be cleared for all rules on initialization.
+.Ed
+.Pp
+.Ft void
+.Fn LibAliasSkinnyPort "struct libalias *" "unsigned int port"
+.Bd -ragged -offset indent
+Set the TCP port used by the Skinny Station protocol.
+Skinny is used by Cisco IP phones to communicate with
+Cisco Call Managers to set up voice over IP calls.
+If this is not set, Skinny aliasing will not be done.
+The typical port used by Skinny is 2000.
+.Ed
+.Sh PACKET HANDLING
+The packet handling functions are used to modify incoming (remote to local)
+and outgoing (local to remote) packets.
+The calling program is responsible for receiving and sending packets via
+network interfaces.
+.Pp
+Along with
+.Fn LibAliasInit
+and
+.Fn LibAliasSetAddress ,
+the two packet handling functions,
+.Fn LibAliasIn
+and
+.Fn LibAliasOut ,
+comprise minimal set of functions needed for a basic IP masquerading
+implementation.
+.Pp
+.Ft int
+.Fn LibAliasIn "struct libalias *" "char *buffer" "int maxpacketsize"
+.Bd -ragged -offset indent
+An incoming packet coming from a remote machine to the local network is
+de-aliased by this function.
+The IP packet is pointed to by
+.Fa buffer ,
+and
+.Fa maxpacketsize
+indicates the size of the data structure containing the packet and should
+be at least as large as the actual packet size.
+.Pp
+Return codes:
+.Bl -tag -width indent
+.It Dv PKT_ALIAS_OK
+The packet aliasing process was successful.
+.It Dv PKT_ALIAS_IGNORED
+The packet was ignored and not de-aliased.
+This can happen if the protocol is unrecognized, possibly an ICMP message
+type is not handled or if incoming packets for new connections are being
+ignored (if
+.Dv PKT_ALIAS_DENY_INCOMING
+mode bit was set by
+.Fn LibAliasSetMode ) .
+.It Dv PKT_ALIAS_UNRESOLVED_FRAGMENT
+This is returned when a fragment cannot be resolved because the header
+fragment has not been sent yet.
+In this situation, fragments must be saved with
+.Fn LibAliasSaveFragment
+until a header fragment is found.
+.It Dv PKT_ALIAS_FOUND_HEADER_FRAGMENT
+The packet aliasing process was successful, and a header fragment was found.
+This is a signal to retrieve any unresolved fragments with
+.Fn LibAliasGetFragment
+and de-alias them with
+.Fn LibAliasFragmentIn .
+.It Dv PKT_ALIAS_ERROR
+An internal error within the packet aliasing engine occurred.
+.El
+.Ed
+.Pp
+.Ft int
+.Fn LibAliasOut "struct libalias *" "char *buffer" "int maxpacketsize"
+.Bd -ragged -offset indent
+An outgoing packet coming from the local network to a remote machine is
+aliased by this function.
+The IP packet is pointed to by
+.Fa buffer ,
+and
+.Fa maxpacketsize
+indicates the maximum packet size permissible should the packet length be
+changed.
+IP encoding protocols place address and port information in the encapsulated
+data stream which has to be modified and can account for changes in packet
+length.
+Well known examples of such protocols are FTP and IRC DCC.
+.Pp
+Return codes:
+.Bl -tag -width indent
+.It Dv PKT_ALIAS_OK
+The packet aliasing process was successful.
+.It Dv PKT_ALIAS_IGNORED
+The packet was ignored and not aliased.
+This can happen if the protocol is unrecognized, or possibly an ICMP message
+type is not handled.
+.It Dv PKT_ALIAS_ERROR
+An internal error within the packet aliasing engine occurred.
+.El
+.Ed
+.Sh PORT AND ADDRESS REDIRECTION
+The functions described in this section allow machines on the local network
+to be accessible in some degree to new incoming connections from the external
+network.
+Individual ports can be re-mapped or static network address translations can
+be designated.
+.Pp
+.Ft struct alias_link *
+.Fo LibAliasRedirectPort
+.Fa "struct libalias *"
+.Fa "struct in_addr local_addr"
+.Fa "u_short local_port"
+.Fa "struct in_addr remote_addr"
+.Fa "u_short remote_port"
+.Fa "struct in_addr alias_addr"
+.Fa "u_short alias_port"
+.Fa "u_char proto"
+.Fc
+.Bd -ragged -offset indent
+This function specifies that traffic from a given remote address/port to
+an alias address/port be redirected to a specified local address/port.
+The parameter
+.Fa proto
+can be either
+.Dv IPPROTO_TCP
+or
+.Dv IPPROTO_UDP ,
+as defined in
+.In netinet/in.h .
+.Pp
+If
+.Fa local_addr
+or
+.Fa alias_addr
+is zero, this indicates that the packet aliasing address as established
+by
+.Fn LibAliasSetAddress
+is to be used.
+Even if
+.Fn LibAliasSetAddress
+is called to change the address after
+.Fn LibAliasRedirectPort
+is called, a zero reference will track this change.
+.Pp
+If the link is further set up to operate for a load sharing, then
+.Fa local_addr
+and
+.Fa local_port
+are ignored, and are selected dynamically from the server pool, as described in
+.Fn LibAliasAddServer
+below.
+.Pp
+If
+.Fa remote_addr
+is zero, this indicates to redirect packets from any remote address.
+Likewise, if
+.Fa remote_port
+is zero, this indicates to redirect packets originating from any remote
+port number.
+Almost always, the remote port specification will be zero, but non-zero
+remote addresses can sometimes be useful for firewalling.
+If two calls to
+.Fn LibAliasRedirectPort
+overlap in their address/port specifications, then the most recent call
+will have precedence.
+.Pp
+This function returns a pointer which can subsequently be used by
+.Fn LibAliasRedirectDelete .
+If
+.Dv NULL
+is returned, then the function call did not complete successfully.
+.Pp
+All port numbers should be in network address byte order, so it is necessary
+to use
+.Xr htons 3
+to convert these parameters from internally readable numbers to network byte
+order.
+Addresses are also in network byte order, which is implicit in the use of the
+.Fa struct in_addr
+data type.
+.Ed
+.Pp
+.Ft struct alias_link *
+.Fo LibAliasRedirectAddr
+.Fa "struct libalias *"
+.Fa "struct in_addr local_addr"
+.Fa "struct in_addr alias_addr"
+.Fc
+.Bd -ragged -offset indent
+This function designates that all incoming traffic to
+.Fa alias_addr
+be redirected to
+.Fa local_addr .
+Similarly, all outgoing traffic from
+.Fa local_addr
+is aliased to
+.Fa alias_addr .
+.Pp
+If
+.Fa local_addr
+or
+.Fa alias_addr
+is zero, this indicates that the packet aliasing address as established by
+.Fn LibAliasSetAddress
+is to be used.
+Even if
+.Fn LibAliasSetAddress
+is called to change the address after
+.Fn LibAliasRedirectAddr
+is called, a zero reference will track this change.
+.Pp
+If the link is further set up to operate for a load sharing, then
+.Fa local_addr
+is ignored, and is selected dynamically from the server pool, as described in
+.Fn LibAliasAddServer
+below.
+.Pp
+If subsequent calls to
+.Fn LibAliasRedirectAddr
+use the same aliasing address, all new incoming traffic to this aliasing
+address will be redirected to the local address made in the last function
+call.
+New traffic generated by any of the local machines, designated in the
+several function calls, will be aliased to the same address.
+Consider the following example:
+.Bd -literal -offset indent
+LibAliasRedirectAddr(la, inet_aton("192.168.0.2"),
+ inet_aton("141.221.254.101"));
+LibAliasRedirectAddr(la, inet_aton("192.168.0.3"),
+ inet_aton("141.221.254.101"));
+LibAliasRedirectAddr(la, inet_aton("192.168.0.4"),
+ inet_aton("141.221.254.101"));
+.Ed
+.Pp
+Any outgoing connections such as
+.Xr telnet 1
+or
+.Xr ftp 1
+from 192.168.0.2, 192.168.0.3 and 192.168.0.4 will appear to come from
+141.221.254.101.
+Any incoming connections to 141.221.254.101 will be directed to 192.168.0.4.
+.Pp
+Any calls to
+.Fn LibAliasRedirectPort
+will have precedence over address mappings designated by
+.Fn LibAliasRedirectAddr .
+.Pp
+This function returns a pointer which can subsequently be used by
+.Fn LibAliasRedirectDelete .
+If
+.Dv NULL
+is returned, then the function call did not complete successfully.
+.Ed
+.Pp
+.Ft int
+.Fo LibAliasAddServer
+.Fa "struct libalias *"
+.Fa "struct alias_link *link"
+.Fa "struct in_addr addr"
+.Fa "u_short port"
+.Fc
+.Bd -ragged -offset indent
+This function sets the
+.Fa link
+up for Load Sharing using IP Network Address Translation (RFC 2391, LSNAT).
+LSNAT operates as follows.
+A client attempts to access a server by using the server virtual address.
+The LSNAT router transparently redirects the request to one of the hosts
+in server pool, selected using a real-time load sharing algorithm.
+Multiple sessions may be initiated from the same client, and each session
+could be directed to a different host based on load balance across server
+pool hosts at the time.
+If load share is desired for just a few specific services, the configuration
+on LSNAT could be defined to restrict load share for just the services
+desired.
+.Pp
+Currently, only the simplest selection algorithm is implemented, where a
+host is selected on a round-robin basis only, without regard to load on
+the host.
+.Pp
+First, the
+.Fa link
+is created by either
+.Fn LibAliasRedirectPort
+or
+.Fn LibAliasRedirectAddr .
+Then,
+.Fn LibAliasAddServer
+is called multiple times to add entries to the
+.Fa link Ns 's
+server pool.
+.Pp
+For links created with
+.Fn LibAliasRedirectAddr ,
+the
+.Fa port
+argument is ignored and could have any value, e.g.\& htons(~0).
+.Pp
+This function returns 0 on success, \-1 otherwise.
+.Ed
+.Pp
+.Ft int
+.Fn LibAliasRedirectDynamic "struct libalias *" "struct alias_link *link"
+.Bd -ragged -offset indent
+This function marks the specified static redirect rule entered by
+.Fn LibAliasRedirectPort
+as dynamic.
+This can be used to e.g.\& dynamically redirect a single TCP connection,
+after which the rule is removed.
+Only fully specified links can be made dynamic.
+(See the
+.Sx STATIC AND DYNAMIC LINKS
+and
+.Sx PARTIALLY SPECIFIED ALIASING LINKS
+sections below for a definition of static vs.\& dynamic,
+and partially vs.\& fully specified links.)
+.Pp
+This function returns 0 on success, \-1 otherwise.
+.Ed
+.Pp
+.Ft void
+.Fn LibAliasRedirectDelete "struct libalias *" "struct alias_link *link"
+.Bd -ragged -offset indent
+This function will delete a specific static redirect rule entered by
+.Fn LibAliasRedirectPort
+or
+.Fn LibAliasRedirectAddr .
+The parameter
+.Fa link
+is the pointer returned by either of the redirection functions.
+If an invalid pointer is passed to
+.Fn LibAliasRedirectDelete ,
+then a program crash or unpredictable operation could result, so it is
+necessary to be careful using this function.
+.Ed
+.Pp
+.Ft int
+.Fn LibAliasProxyRule "struct libalias *" "const char *cmd"
+.Bd -ragged -offset indent
+The passed
+.Fa cmd
+string consists of one or more pairs of words.
+The first word in each pair is a token and the second is the value that
+should be applied for that token.
+Tokens and their argument types are as follows:
+.Bl -tag -width indent
+.It Cm type encode_ip_hdr | encode_tcp_stream | no_encode
+In order to support transparent proxying, it is necessary to somehow
+pass the original address and port information into the new destination
+server.
+If
+.Cm encode_ip_hdr
+is specified, the original destination address and port are passed
+as an extra IP option.
+If
+.Cm encode_tcp_stream
+is specified, the original destination address and port are passed
+as the first piece of data in the TCP stream in the format
+.Dq Li DEST Ar IP port .
+.It Cm port Ar portnum
+Only packets with the destination port
+.Ar portnum
+are proxied.
+.It Cm server Ar host Ns Op : Ns Ar portnum
+This specifies the
+.Ar host
+and
+.Ar portnum
+that the data is to be redirected to.
+.Ar host
+must be an IP address rather than a DNS host name.
+If
+.Ar portnum
+is not specified, the destination port number is not changed.
+.Pp
+The
+.Ar server
+specification is mandatory unless the
+.Cm delete
+command is being used.
+.It Cm rule Ar index
+Normally, each call to
+.Fn LibAliasProxyRule
+inserts the next rule at the start of a linear list of rules.
+If an
+.Ar index
+is specified, the new rule will be checked after all rules with lower
+indices.
+Calls to
+.Fn LibAliasProxyRule
+that do not specify a rule are assigned rule 0.
+.It Cm delete Ar index
+This token and its argument MUST NOT be used with any other tokens.
+When used, all existing rules with the given
+.Ar index
+are deleted.
+.It Cm proto tcp | udp
+If specified, only packets of the given protocol type are matched.
+.It Cm src Ar IP Ns Op / Ns Ar bits
+If specified, only packets with a source address matching the given
+.Ar IP
+are matched.
+If
+.Ar bits
+is also specified, then the first
+.Ar bits
+bits of
+.Ar IP
+are taken as a network specification, and all IP addresses from that
+network will be matched.
+.It Cm dst Ar IP Ns Op / Ns Ar bits
+If specified, only packets with a destination address matching the given
+.Ar IP
+are matched.
+If
+.Ar bits
+is also specified, then the first
+.Ar bits
+bits of
+.Ar IP
+are taken as a network specification, and all IP addresses from that
+network will be matched.
+.El
+.Pp
+This function is usually used to redirect outgoing connections for
+internal machines that are not permitted certain types of internet
+access, or to restrict access to certain external machines.
+.Ed
+.Pp
+.Ft struct alias_link *
+.Fo LibAliasRedirectProto
+.Fa "struct libalias *"
+.Fa "struct in_addr local_addr"
+.Fa "struct in_addr remote_addr"
+.Fa "struct in_addr alias_addr"
+.Fa "u_char proto"
+.Fc
+.Bd -ragged -offset indent
+This function specifies that any IP packet with protocol number of
+.Fa proto
+from a given remote address to an alias address be
+redirected to a specified local address.
+.Pp
+If
+.Fa local_addr
+or
+.Fa alias_addr
+is zero, this indicates that the packet aliasing address as established
+by
+.Fn LibAliasSetAddress
+is to be used.
+Even if
+.Fn LibAliasSetAddress
+is called to change the address after
+.Fn LibAliasRedirectProto
+is called, a zero reference will track this change.
+.Pp
+If
+.Fa remote_addr
+is zero, this indicates to redirect packets from any remote address.
+Non-zero remote addresses can sometimes be useful for firewalling.
+.Pp
+If two calls to
+.Fn LibAliasRedirectProto
+overlap in their address specifications, then the most recent call
+will have precedence.
+.Pp
+This function returns a pointer which can subsequently be used by
+.Fn LibAliasRedirectDelete .
+If
+.Dv NULL
+is returned, then the function call did not complete successfully.
+.Ed
+.Sh FRAGMENT HANDLING
+The functions in this section are used to deal with incoming fragments.
+.Pp
+Outgoing fragments are handled within
+.Fn LibAliasOut
+by changing the address according to any applicable mapping set by
+.Fn LibAliasRedirectAddr ,
+or the default aliasing address set by
+.Fn LibAliasSetAddress .
+.Pp
+Incoming fragments are handled in one of two ways.
+If the header of a fragmented IP packet has already been seen, then all
+subsequent fragments will be re-mapped in the same manner the header
+fragment was.
+Fragments which arrive before the header are saved and then retrieved
+once the header fragment has been resolved.
+.Pp
+.Ft int
+.Fn LibAliasSaveFragment "struct libalias *" "char *ptr"
+.Bd -ragged -offset indent
+When
+.Fn LibAliasIn
+returns
+.Dv PKT_ALIAS_UNRESOLVED_FRAGMENT ,
+this function can be used to save the pointer to the unresolved fragment.
+.Pp
+It is implicitly assumed that
+.Fa ptr
+points to a block of memory allocated by
+.Xr malloc 3 .
+If the fragment is never resolved, the packet aliasing engine will
+automatically free the memory after a timeout period.
+[Eventually this function should be modified so that a callback function
+for freeing memory is passed as an argument.]
+.Pp
+This function returns
+.Dv PKT_ALIAS_OK
+if it was successful and
+.Dv PKT_ALIAS_ERROR
+if there was an error.
+.Ed
+.Pp
+.Ft char *
+.Fn LibAliasGetFragment "struct libalias *" "char *buffer"
+.Bd -ragged -offset indent
+This function can be used to retrieve fragment pointers saved by
+.Fn LibAliasSaveFragment .
+The IP header fragment pointed to by
+.Fa buffer
+is the header fragment indicated when
+.Fn LibAliasIn
+returns
+.Dv PKT_ALIAS_FOUND_HEADER_FRAGMENT .
+Once a fragment pointer is retrieved, it becomes the calling program's
+responsibility to free the dynamically allocated memory for the fragment.
+.Pp
+The
+.Fn LibAliasGetFragment
+function can be called sequentially until there are no more fragments
+available, at which time it returns
+.Dv NULL .
+.Ed
+.Pp
+.Ft void
+.Fn LibAliasFragmentIn "struct libalias *" "char *header" "char *fragment"
+.Bd -ragged -offset indent
+When a fragment is retrieved with
+.Fn LibAliasGetFragment ,
+it can then be de-aliased with a call to
+.Fn LibAliasFragmentIn .
+The
+.Fa header
+argument is the pointer to a header fragment used as a template, and
+.Fa fragment
+is the pointer to the packet to be de-aliased.
+.Ed
+.Sh MISCELLANEOUS FUNCTIONS
+.Ft void
+.Fn LibAliasSetTarget "struct libalias *" "struct in_addr addr"
+.Bd -ragged -offset indent
+When an incoming packet not associated with any pre-existing aliasing link
+arrives at the host machine, it will be sent to the address indicated by a
+call to
+.Fn LibAliasSetTarget .
+.Pp
+If this function is called with an
+.Dv INADDR_NONE
+address argument, then all new incoming packets go to the address set by
+.Fn LibAliasSetAddress .
+.Pp
+If this function is not called, or is called with an
+.Dv INADDR_ANY
+address argument, then all new incoming packets go to the address specified
+in the packet.
+This allows external machines to talk directly to internal machines if they
+can route packets to the machine in question.
+.Ed
+.Pp
+.Ft int
+.Fn LibAliasCheckNewLink "struct libalias *"
+.Bd -ragged -offset indent
+This function returns a non-zero value when a new aliasing link is created.
+In circumstances where incoming traffic is being sequentially sent to
+different local servers, this function can be used to trigger when
+.Fn LibAliasSetTarget
+is called to change the default target address.
+.Ed
+.Pp
+.Ft u_short
+.Fn LibAliasInternetChecksum "struct libalias *" "u_short *buffer" "int nbytes"
+.Bd -ragged -offset indent
+This is a utility function that does not seem to be available elsewhere and
+is included as a convenience.
+It computes the internet checksum, which is used in both IP and
+protocol-specific headers (TCP, UDP, ICMP).
+.Pp
+The
+.Fa buffer
+argument points to the data block to be checksummed, and
+.Fa nbytes
+is the number of bytes.
+The 16-bit checksum field should be zeroed before computing the checksum.
+.Pp
+Checksums can also be verified by operating on a block of data including
+its checksum.
+If the checksum is valid,
+.Fn LibAliasInternetChecksum
+will return zero.
+.Ed
+.Pp
+.Ft int
+.Fn LibAliasUnaliasOut "struct libalias *" "char *buffer" "int maxpacketsize"
+.Bd -ragged -offset indent
+An outgoing packet, which has already been aliased,
+has its private address/port information restored by this function.
+The IP packet is pointed to by
+.Fa buffer ,
+and
+.Fa maxpacketsize
+is provided for error checking purposes.
+This function can be used if an already-aliased packet needs to have its
+original IP header restored for further processing (e.g.\& logging).
+.Ed
+.Sh AUTHORS
+.An Charles Mott Aq cm@linktel.net ,
+versions 1.0 - 1.8, 2.0 - 2.4.
+.An Eivind Eklund Aq eivind@FreeBSD.org ,
+versions 1.8b, 1.9 and 2.5.
+Added IRC DCC support as well as contributing a number of architectural
+improvements; added the firewall bypass for FTP/IRC DCC.
+.An Erik Salander Aq erik@whistle.com
+added support for PPTP and RTSP.
+.An Junichi Satoh Aq junichi@junichi.org
+added support for RTSP/PNA.
+.An Ruslan Ermilov Aq ru@FreeBSD.org
+added support for PPTP and LSNAT as well as general hacking.
+.An Paolo Pisati Aq piso@FreeBSD.org
+made the library modular, moving support for all
+protocols (except for IP, TCP and UDP) to external modules.
+.Sh ACKNOWLEDGMENTS
+Listed below, in approximate chronological order, are individuals who
+have provided valuable comments and/or debugging assistance.
+.Pp
+.Bd -ragged -offset indent
+.An -split
+.An Gary Roberts
+.An Tom Torrance
+.An Reto Burkhalter
+.An Martin Renters
+.An Brian Somers
+.An Paul Traina
+.An Ari Suutari
+.An Dave Remien
+.An J. Fortes
+.An Andrzej Bialecki
+.An Gordon Burditt
+.Ed
+.Sh CONCEPTUAL BACKGROUND
+This section is intended for those who are planning to modify the source
+code or want to create somewhat esoteric applications using the packet
+aliasing functions.
+.Pp
+The conceptual framework under which the packet aliasing engine operates
+is described here.
+Central to the discussion is the idea of an
+.Em aliasing link
+which describes the relationship for a given packet transaction between
+the local machine, aliased identity and remote machine.
+It is discussed how such links come into existence and are destroyed.
+.Ss ALIASING LINKS
+There is a notion of an
+.Em aliasing link ,
+which is a 7-tuple describing a specific translation:
+.Bd -literal -offset indent
+(local addr, local port, alias addr, alias port,
+ remote addr, remote port, protocol)
+.Ed
+.Pp
+Outgoing packets have the local address and port number replaced with the
+alias address and port number.
+Incoming packets undergo the reverse process.
+The packet aliasing engine attempts to match packets against an internal
+table of aliasing links to determine how to modify a given IP packet.
+Both the IP header and protocol dependent headers are modified as necessary.
+Aliasing links are created and deleted as necessary according to network
+traffic.
+.Pp
+Protocols can be TCP, UDP or even ICMP in certain circumstances.
+(Some types of ICMP packets can be aliased according to sequence or ID
+number which acts as an equivalent port number for identifying how
+individual packets should be handled.)
+.Pp
+Each aliasing link must have a unique combination of the following five
+quantities: alias address/port, remote address/port and protocol.
+This ensures that several machines on a local network can share the
+same aliasing IP address.
+In cases where conflicts might arise, the aliasing port is chosen so that
+uniqueness is maintained.
+.Ss STATIC AND DYNAMIC LINKS
+Aliasing links can either be static or dynamic.
+Static links persist indefinitely and represent fixed rules for translating
+IP packets.
+Dynamic links come into existence for a specific TCP connection or UDP
+transaction or ICMP ECHO sequence.
+For the case of TCP, the connection can be monitored to see when the
+associated aliasing link should be deleted.
+Aliasing links for UDP transactions (and ICMP ECHO and TIMESTAMP requests)
+work on a simple timeout rule.
+When no activity is observed on a dynamic link for a certain amount of time
+it is automatically deleted.
+Timeout rules also apply to TCP connections which do not open or close
+properly.
+.Ss PARTIALLY SPECIFIED ALIASING LINKS
+Aliasing links can be partially specified, meaning that the remote address
+and/or remote port are unknown.
+In this case, when a packet matching the incomplete specification is found,
+a fully specified dynamic link is created.
+If the original partially specified link is dynamic, it will be deleted
+after the fully specified link is created, otherwise it will persist.
+.Pp
+For instance, a partially specified link might be
+.Bd -literal -offset indent
+(192.168.0.4, 23, 204.228.203.215, 8066, 0, 0, tcp)
+.Ed
+.Pp
+The zeros denote unspecified components for the remote address and port.
+If this link were static it would have the effect of redirecting all
+incoming traffic from port 8066 of 204.228.203.215 to port 23 (telnet)
+of machine 192.168.0.4 on the local network.
+Each individual telnet connection would initiate the creation of a distinct
+dynamic link.
+.Ss DYNAMIC LINK CREATION
+In addition to aliasing links, there are also address mappings that can be
+stored within the internal data table of the packet aliasing mechanism.
+.Bd -literal -offset indent
+(local addr, alias addr)
+.Ed
+.Pp
+Address mappings are searched when creating new dynamic links.
+.Pp
+All outgoing packets from the local network automatically create a dynamic
+link if they do not match an already existing fully specified link.
+If an address mapping exists for the outgoing packet, this determines
+the alias address to be used.
+If no mapping exists, then a default address, usually the address of the
+packet aliasing host, is used.
+If necessary, this default address can be changed as often as each individual
+packet arrives.
+.Pp
+The aliasing port number is determined such that the new dynamic link does
+not conflict with any existing links.
+In the default operating mode, the packet aliasing engine attempts to set
+the aliasing port equal to the local port number.
+If this results in a conflict, then port numbers are randomly chosen until
+a unique aliasing link can be established.
+In an alternate operating mode, the first choice of an aliasing port is also
+random and unrelated to the local port number.
+.Sh MODULAR ARCHITECTURE (AND Xr ipfw 4 Sh SUPPORT)
+One of the latest improvements to
+.Nm
+was to make its support
+for new protocols independent from the rest of the library, giving it
+the ability to load/unload support for new protocols at run-time.
+To achieve this feature, all the code for protocol handling was moved
+to a series of modules outside of the main library.
+These modules are compiled from the same sources but work in
+different ways, depending on whether they are compiled to work inside a kernel
+or as part of the userland library.
+.Ss LIBALIAS MODULES IN KERNEL LAND
+When compiled for the kernel,
+.Nm
+modules are plain KLDs recognizable with the
+.Pa alias_
+prefix.
+.Pp
+To add support for a new protocol, load the corresponding module.
+For example:
+.Pp
+.Dl "kldload alias_ftp"
+.Pp
+When support for a protocol is no longer needed, its module can be unloaded:
+.Pp
+.Dl "kldunload alias_ftp"
+.Ss LIBALIAS MODULES IN USERLAND
+Due to the differences between kernel and userland (no KLD mechanism,
+many different address spaces, etc.), we had to change a bit how to
+handle module loading/tracking/unloading in userland.
+.Pp
+While compiled for a userland
+.Nm ,
+all the modules are plain libraries, residing in
+.Pa /usr/lib ,
+and recognizable with the
+.Pa libalias_
+prefix.
+.Pp
+There is a configuration file,
+.Pa /etc/libalias.conf ,
+with the following contents (by default):
+.Bd -literal -offset indent
+/usr/lib/libalias_cuseeme.so
+/usr/lib/libalias_ftp.so
+/usr/lib/libalias_irc.so
+/usr/lib/libalias_nbt.so
+/usr/lib/libalias_pptp.so
+/usr/lib/libalias_skinny.so
+/usr/lib/libalias_smedia.so
+.Ed
+.Pp
+This file contains the paths to the modules that
+.Nm
+will load.
+To load/unload a new module, just add its path to
+.Pa libalias.conf
+and call
+.Fn LibAliasRefreshModules
+from the program.
+In case the application provides a
+.Dv SIGHUP
+signal handler, add a call to
+.Fn LibAliasRefreshModules
+inside the handler, and everytime you want to refresh the loaded modules,
+send it the
+.Dv SIGHUP
+signal:
+.Pp
+.Dl "kill -HUP <process_pid>"
+.Ss MODULAR ARCHITECURE: HOW IT WORKS
+The modular architecture of
+.Nm
+works similar whether it is running inside the
+kernel or in userland.
+From
+.Pa alias_mod.c :
+.Bd -literal
+/* Protocol and userland module handlers chains. */
+LIST_HEAD(handler_chain, proto_handler) handler_chain ...
+\&...
+SLIST_HEAD(dll_chain, dll) dll_chain ...
+.Ed
+.Pp
+.Va handler_chain
+keep tracks of all the protocol handlers loaded, while
+.Va ddl_chain
+takes care of userland modules loaded.
+.Pp
+.Va handler_chain
+is composed of
+.Vt "struct proto_handler"
+entries:
+.Bd -literal
+struct proto_handler {
+ u_int pri;
+ int16_t dir;
+ uint8_t proto;
+ int (*fingerprint)(struct libalias *la,
+ struct ip *pip, struct alias_data *ah);
+ int (*protohandler)(struct libalias *la,
+ struct ip *pip, struct alias_data *ah);
+ LIST_ENTRY(proto_handler) entries;
+};
+.Ed
+.Pp
+where:
+.Bl -inset
+.It Va pri
+is the priority assigned to a protocol handler, lower
+is better.
+.It Va dir
+is the direction of packets: ingoing or outgoing.
+.It Va proto
+says at which protocol this packet belongs: IP, TCP or UDP.
+.It Va fingerprint
+points to the fingerprint function while protohandler points
+to the protocol handler function.
+.El
+.Pp
+The
+.Va fingerprint
+function has the double of scope of checking if the
+incoming packet is found and if it belongs to any categories that this
+module can handle.
+.Pp
+The
+.Va protohandler
+function actually manipulates
+the packet to make
+.Nm
+correctly NAT it.
+.Pp
+When a packet enters
+.Nm ,
+if it meets a module hook,
+.Va handler_chain
+is searched to see if there is an handler that matches
+this type of a packet (it checks protocol and direction of packet), then if
+more than one handler is found, it starts with the module with
+the lowest priority number: it calls the
+.Va fingerprint
+function and interprets the result.
+.Pp
+If the result value is equal to 0 then it calls the protocol handler
+of this handler and returns.
+Otherwise, it proceeds to the next eligible module until the
+.Va handler_chain
+is exhausted.
+.Pp
+Inside
+.Nm ,
+the module hook looks like this:
+.Bd -literal -offset indent
+struct alias_data ad = {
+ lnk,
+ &original_address,
+ &alias_address,
+ &alias_port,
+ &ud->uh_sport, /* original source port */
+ &ud->uh_dport, /* original dest port */
+ 256 /* maxpacketsize */
+};
+
+\&...
+
+/* walk out chain */
+err = find_handler(IN, UDP, la, pip, &ad);
+.Ed
+.Pp
+All data useful to a module are gathered together in an
+.Vt alias_data
+structure, then
+.Fn find_handler
+is called.
+The
+.Fn find_handler
+function is responsible for walking out the handler
+chain, it receives as input parameters:
+.Bl -tag -width indent
+.It Fa IN
+direction
+.It Fa UDP
+working protocol
+.It Fa la
+pointer to this instance of libalias
+.It Fa pip
+pointer to a
+.Vt "struct ip"
+.It Fa ad
+pointer to
+.Vt "struct alias_data"
+(see above)
+.El
+.Pp
+In this case,
+.Fn find_handler
+will search only for modules registered for
+supporting INcoming UDP packets.
+.Pp
+As was mentioned earlier,
+.Nm
+in userland is a bit different, cause
+care has to be taken of module handling too (avoiding duplicate load of
+module, avoiding module with same name, etc.) so
+.Va dll_chain
+was introduced.
+.Pp
+.Va dll_chain
+contains a list of all userland
+.Nm
+modules loaded.
+.Pp
+When an application calls
+.Fn LibAliasRefreshModules ,
+.Nm
+first unloads all the loaded modules, then reloads all the modules listed in
+.Pa /etc/libalias.conf :
+for every module loaded, a new entry to
+.Va dll_chain
+is added.
+.Pp
+.Va dll_chain
+is composed of
+.Vt "struct dll"
+entries:
+.Bd -literal
+struct dll {
+ /* name of module */
+ char name[DLL_LEN];
+ /*
+ * ptr to shared obj obtained through
+ * dlopen() - use this ptr to get access
+ * to any symbols from a loaded module
+ * via dlsym()
+ */
+ void *handle;
+ struct dll *next;
+};
+.Ed
+.Bl -inset
+.It Va name
+is the name of the module
+.It Va handle
+is a pointer to the module obtained through
+.Xr dlopen 3
+.El
+Whenever a module is loaded in userland, an entry is added to
+.Va dll_chain ,
+then every protocol handler present in that module
+is resolved and registered in
+.Va handler_chain .
+.Ss HOW TO WRITE A MODULE FOR LIBALIAS
+There is a module (called
+.Pa alias_dummy.[ch] )
+in
+.Nm
+that can be used as a skeleton for future work, here we analyse some parts of that
+module.
+From
+.Pa alias_dummy.c :
+.Bd -literal
+struct proto_handler handlers [] = {{666, IN|OUT, UDP|TCP,
+ &fingerprint, &protohandler}};
+.Ed
+.Pp
+The variable
+.Va handlers
+is the
+.Dq "most important thing"
+in a module
+cause it describes the handlers present and lets the outside world use
+it in an opaque way.
+.Pp
+It must ALWAYS be present in every module, and it MUST retain
+the name
+.Va handlers ,
+otherwise attempting to load a module in userland will fail and
+complain about missing symbols: for more information about module
+load/unload, please refer to
+.Fn LibAliasRefreshModules ,
+.Fn LibAliasLoadModule
+and
+.Fn LibAliasUnloadModule
+in
+.Pa alias.c .
+.Pp
+.Va handlers
+contains all the
+.Vt proto_handler
+structures present in a module.
+.Bd -literal
+static int
+mod_handler(module_t mod, int type, void *data)
+{
+ int error;
+
+ switch (type) {
+ case MOD_LOAD:
+ error = 0;
+ attach_handlers(handlers);
+ break;
+ case MOD_UNLOAD:
+ error = 0;
+ detach_handlers(handlers;
+ break;
+ default:
+ error = EINVAL;
+ }
+ return (error);
+}
+.Ed
+When running as KLD,
+.Fn mod_handler
+register/deregister the module using
+.Fn attach_handlers
+and
+.Fn detach_handlers ,
+respectively.
+.Pp
+Every module must contain at least 2 functions: one fingerprint
+function and a protocol handler function.
+.Bd -literal
+#ifdef _KERNEL
+static
+#endif
+int
+fingerprint(struct libalias *la, struct ip *pip, struct alias_data *ah)
+{
+
+\&...
+}
+
+#ifdef _KERNEL
+static
+#endif
+int
+protohandler(struct libalias *la, struct ip *pip,
+ struct alias_data *ah)
+{
+
+\&...
+}
+.Ed
+and they must accept exactly these input parameters.
+.Ss PATCHING AN APPLICATION FOR USERLAND LIBALIAS MODULES
+To add module support into an application that uses
+.Nm ,
+the following simple steps can be followed.
+.Bl -enum
+.It
+Find the main file of an application
+(let us call it
+.Pa main.c ) .
+.It
+Add this to the header section of
+.Pa main.c ,
+if not already present:
+.Pp
+.Dl "#include <signal.h>"
+.Pp
+and this just after the header section:
+.Pp
+.Dl "static void signal_handler(int);"
+.It
+Add the following line to the init function of an application or,
+if it does not have any init function, put it in
+.Fn main :
+.Pp
+.Dl "signal(SIGHUP, signal_handler);"
+.Pp
+and place the
+.Fn signal_handler
+function somewhere in
+.Pa main.c :
+.Bd -literal -offset indent
+static void
+signal_handler(int sig)
+{
+
+ LibAliasRefreshModules();
+}
+.Ed
+.Pp
+Otherwise, if an application already traps the
+.Dv SIGHUP
+signal, just add a call to
+.Fn LibAliasRefreshModules
+in the signal handler function.
+.El
+For example, to patch
+.Xr natd 8
+to use
+.Nm
+modules, just add the following line to
+.Fn RefreshAddr "int sig __unused" :
+.Pp
+.Dl "LibAliasRefreshModules()"
+.Pp
+recompile and you are done.
+.Ss LOGGING SUPPORT IN KERNEL LAND
+When working as KLD,
+.Nm
+now has log support that
+happens on a buffer allocated inside
+.Vt "struct libalias"
+(from
+.Pa alias_local.h ) :
+.Bd -literal
+struct libalias {
+ ...
+
+ /* log descriptor */
+#ifdef KERNEL_LOG
+ char *logDesc; /*
+ * ptr to an auto-malloced
+ * memory buffer when libalias
+ * works as kld
+ */
+#else
+ FILE *logDesc; /*
+ * ptr to /var/log/alias.log
+ * when libalias runs as a
+ * userland lib
+ */
+#endif
+
+ ...
+}
+.Ed
+so all applications using
+.Nm
+will be able to handle their
+own logs, if they want, accessing
+.Va logDesc .
+Moreover, every change to a log buffer is automatically added to
+.Xr syslog 3
+with the
+.Dv LOG_SECURITY
+facility and the
+.Dv LOG_INFO
+level.
diff --git a/src/VBox/Devices/Network/slirp/libslirp.h b/src/VBox/Devices/Network/slirp/libslirp.h
new file mode 100644
index 00000000..8e70d002
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/libslirp.h
@@ -0,0 +1,200 @@
+/* $Id: libslirp.h $ */
+/** @file
+ * NAT - slirp interface.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef _LIBSLIRP_H
+#define _LIBSLIRP_H
+
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/winsock2.h>
+# ifdef __cplusplus
+extern "C" {
+# endif
+int inet_aton(const char *cp, struct in_addr *ia);
+# ifdef __cplusplus
+}
+# endif
+#else
+# ifdef RT_OS_OS2 /* temporary workaround, see ticket #127 */
+# include <sys/time.h>
+# endif
+# include <sys/select.h>
+# include <poll.h>
+# include <arpa/inet.h>
+#endif
+
+#include <VBox/types.h>
+#include <iprt/req.h>
+
+typedef struct NATState *PNATState;
+struct mbuf;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int slirp_init(PNATState *, uint32_t, uint32_t, bool, bool, int, int, bool, void *);
+void slirp_register_statistics(PNATState pData, PPDMDRVINS pDrvIns);
+void slirp_deregister_statistics(PNATState pData, PPDMDRVINS pDrvIns);
+void slirp_term(PNATState);
+void slirp_link_up(PNATState);
+void slirp_link_down(PNATState);
+
+#if defined(RT_OS_WINDOWS)
+void slirp_select_fill(PNATState pData, int *pndfs);
+
+void slirp_select_poll(PNATState pData, int fTimeout);
+#else /* RT_OS_WINDOWS */
+void slirp_select_fill(PNATState pData, int *pnfds, struct pollfd *polls);
+void slirp_select_poll(PNATState pData, struct pollfd *polls, int ndfs);
+#endif /* !RT_OS_WINDOWS */
+
+void slirp_input(PNATState pData, struct mbuf *m, size_t cbBuf);
+
+/* you must provide the following functions: */
+void slirp_arm_fast_timer(void *pvUser);
+int slirp_can_output(void * pvUser);
+void slirp_output(void * pvUser, struct mbuf *m, const uint8_t *pkt, int pkt_len);
+void slirp_output_pending(void * pvUser);
+void slirp_urg_output(void *pvUser, struct mbuf *, const uint8_t *pu8Buf, int cb);
+void slirp_post_sent(PNATState pData, void *pvArg);
+
+int slirp_call(void *pvUser, PRTREQ *ppReq, RTMSINTERVAL cMillies,
+ unsigned fFlags, PFNRT pfnFunction, unsigned cArgs, ...);
+
+int slirp_call_hostres(void *pvUser, PRTREQ *ppReq, RTMSINTERVAL cMillies,
+ unsigned fFlags, PFNRT pfnFunction, unsigned cArgs, ...);
+
+
+void slirp_update_guest_addr_guess(PNATState pData, uint32_t guess, const char *msg);
+
+int slirp_add_redirect(PNATState pData, int is_udp, struct in_addr host_addr,
+ int host_port, struct in_addr guest_addr,
+ int guest_port);
+int slirp_remove_redirect(PNATState pData, int is_udp, struct in_addr host_addr,
+ int host_port, struct in_addr guest_addr,
+ int guest_port);
+int slirp_add_exec(PNATState pData, int do_pty, const char *args, int addr_low_byte,
+ int guest_port);
+
+void slirp_set_dhcp_TFTP_prefix(PNATState pData, const char *tftpPrefix);
+void slirp_set_dhcp_TFTP_bootfile(PNATState pData, const char *bootFile);
+void slirp_set_dhcp_next_server(PNATState pData, const char *nextServer);
+void slirp_set_dhcp_dns_proxy(PNATState pData, bool fDNSProxy);
+void slirp_set_rcvbuf(PNATState pData, int kilobytes);
+void slirp_set_sndbuf(PNATState pData, int kilobytes);
+void slirp_set_tcp_rcvspace(PNATState pData, int kilobytes);
+void slirp_set_tcp_sndspace(PNATState pData, int kilobytes);
+
+int slirp_set_binding_address(PNATState, char *addr);
+void slirp_set_mtu(PNATState, int);
+void slirp_info(PNATState pData, const void *pvArg, const char *pszArgs);
+void slirp_set_somaxconn(PNATState pData, int iSoMaxConn);
+
+/**
+ * This macrodefinition is shortcut for check of hosts where Slirp,
+ * receives notifications from host. For now it's Darwin only. But
+ * Main API has primitives for listening DNS change event since 4.3.
+ */
+#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
+# define HAVE_NOTIFICATION_FOR_DNS_UPDATE 1
+#else
+# define HAVE_NOTIFICATION_FOR_DNS_UPDATE 0
+#endif
+
+
+/**
+ * This method help DrvNAT to select strategy: about VMRESUMEREASON_HOST_RESUME:
+ * - proceed with link termination (we let guest track host DNS settings)
+ * VBOX_NAT_DNS_EXTERNAL
+ * - enforce internal DNS update (we are using dnsproxy and track but don't export DNS host settings)
+ * VBOX_NAT_DNS_DNSPROXY
+ * - ignore (NAT configured to use hostresolver - we aren't track any host DNS changes)
+ * VBOX_NAT_DNS_HOSTRESOLVER
+ * @note: It's safe to call this method from any thread, because settings we're checking
+ * are immutable at runtime.
+ */
+#define VBOX_NAT_DNS_EXTERNAL 0
+#define VBOX_NAT_DNS_DNSPROXY 1
+#define VBOX_NAT_DNS_HOSTRESOLVER 2
+int slirp_host_network_configuration_change_strategy_selector(const PNATState);
+#if defined(RT_OS_WINDOWS)
+
+
+/*
+ * ICMP handle state change
+ */
+# define VBOX_ICMP_EVENT_INDEX 0
+
+/**
+ * This event is for
+ * - slirp_input
+ * - slirp_link_up
+ * - slirp_link_down
+ * - wakeup
+ */
+# define VBOX_WAKEUP_EVENT_INDEX 1
+
+/*
+ * UDP/TCP socket state change (socket ready to receive, to send, ...)
+ */
+# define VBOX_SOCKET_EVENT_INDEX 2
+
+/*
+ * The number of events for WSAWaitForMultipleEvents().
+ */
+# define VBOX_EVENT_COUNT 3
+
+HANDLE *slirp_get_events(PNATState pData);
+void slirp_register_external_event(PNATState pData, HANDLE hEvent, int index);
+#endif /* RT_OS_WINDOWS */
+
+struct mbuf *slirp_ext_m_get(PNATState pData, size_t cbMin, void **ppvBuf, size_t *pcbBuf);
+void slirp_ext_m_free(PNATState pData, struct mbuf *, uint8_t *pu8Buf);
+
+/*
+ * Returns the timeout.
+ */
+unsigned int slirp_get_timeout_ms(PNATState pData);
+
+# ifndef RT_OS_WINDOWS
+/*
+ * Returns the number of sockets.
+ */
+int slirp_get_nsock(PNATState pData);
+# endif
+
+#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
+void slirp_add_host_resolver_mapping(PNATState pData,
+ const char *pszHostName, bool fPattern,
+ uint32_t u32HostIP);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/src/VBox/Devices/Network/slirp/main.h b/src/VBox/Devices/Network/slirp/main.h
new file mode 100644
index 00000000..be9014e2
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/main.h
@@ -0,0 +1,41 @@
+/* $Id: main.h $ */
+/** @file
+ * NAT - main.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifdef HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#define ETH_ENCAP_URG 1
+void if_encap(PNATState pData, uint16_t eth_proto, struct mbuf *m, int flags);
diff --git a/src/VBox/Devices/Network/slirp/misc.c b/src/VBox/Devices/Network/slirp/misc.c
new file mode 100644
index 00000000..cfb82eea
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/misc.c
@@ -0,0 +1,560 @@
+/* $Id: misc.c $ */
+/** @file
+ * NAT - helpers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef VBOX_NAT_TST_QUEUE
+#include <slirp.h>
+#include "zone.h"
+
+# ifndef HAVE_INET_ATON
+int
+inet_aton(const char *cp, struct in_addr *ia)
+{
+ u_int32_t addr = inet_addr(cp);
+ if (addr == 0xffffffff)
+ return 0;
+ ia->s_addr = addr;
+ return 1;
+}
+# endif
+
+/*
+ * Get our IP address and put it in our_addr
+ */
+void
+getouraddr(PNATState pData)
+{
+ our_addr.s_addr = loopback_addr.s_addr;
+}
+#else /* VBOX_NAT_TST_QUEUE */
+# include <iprt/cdefs.h>
+# include <iprt/types.h>
+# include "misc.h"
+#endif
+struct quehead
+{
+ struct quehead *qh_link;
+ struct quehead *qh_rlink;
+};
+
+void
+insque(PNATState pData, void *a, void *b)
+{
+ register struct quehead *element = (struct quehead *) a;
+ register struct quehead *head = (struct quehead *) b;
+ NOREF(pData);
+ element->qh_link = head->qh_link;
+ head->qh_link = (struct quehead *)element;
+ element->qh_rlink = (struct quehead *)head;
+ ((struct quehead *)(element->qh_link))->qh_rlink = (struct quehead *)element;
+}
+
+void
+remque(PNATState pData, void *a)
+{
+ register struct quehead *element = (struct quehead *) a;
+ NOREF(pData);
+ ((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink;
+ ((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link;
+ element->qh_rlink = NULL;
+ /* element->qh_link = NULL; TCP FIN1 crashes if you do this. Why ? */
+}
+
+#ifndef VBOX_NAT_TST_QUEUE
+
+/*
+ * Set fd blocking and non-blocking
+ */
+void
+fd_nonblock(int fd)
+{
+# ifdef FIONBIO
+# ifdef RT_OS_WINDOWS
+ u_long opt = 1;
+# else
+ int opt = 1;
+# endif
+ ioctlsocket(fd, FIONBIO, &opt);
+# else /* !FIONBIO */
+ int opt;
+
+ opt = fcntl(fd, F_GETFL, 0);
+ opt |= O_NONBLOCK;
+ fcntl(fd, F_SETFL, opt);
+# endif
+}
+
+
+# if defined(VBOX_NAT_MEM_DEBUG)
+# define NATMEM_LOG_FLOW_FUNC(a) LogFlowFunc(a)
+# define NATMEM_LOG_FLOW_FUNC_ENTER() LogFlowFuncEnter()
+# define NATMEM_LOG_FLOW_FUNC_LEAVE() LogFlowFuncLeave()
+# define NATMEM_LOG_2(a) Log2(a)
+# else
+# define NATMEM_LOG_FLOW_FUNC(a) do { } while (0)
+# define NATMEM_LOG_FLOW_FUNC_ENTER() do { } while (0)
+# define NATMEM_LOG_FLOW_FUNC_LEAVE() do { } while (0)
+# define NATMEM_LOG_2(a) do { } while (0)
+# endif
+
+
+/**
+ * Called when memory becomes available, works pfnXmitPending.
+ *
+ * @note This will LEAVE the critical section of the zone and RE-ENTER it
+ * again. Changes to the zone data should be expected across calls to
+ * this function!
+ *
+ * @param zone The zone.
+ */
+DECLINLINE(void) slirp_zone_check_and_send_pending(uma_zone_t zone)
+{
+ NATMEM_LOG_FLOW_FUNC(("ENTER: zone:%R[mzone]\n", zone));
+ if ( zone->fDoXmitPending
+ && zone->master_zone == NULL)
+ {
+ int rc2;
+ zone->fDoXmitPending = false;
+ rc2 = RTCritSectLeave(&zone->csZone); AssertRC(rc2);
+
+ slirp_output_pending(zone->pData->pvUser);
+
+ rc2 = RTCritSectEnter(&zone->csZone); AssertRC(rc2);
+ }
+ NATMEM_LOG_FLOW_FUNC_LEAVE();
+}
+
+static void *slirp_uma_alloc(uma_zone_t zone,
+ int size, uint8_t *pflags, int fWait)
+{
+ struct item *it;
+ uint8_t *sub_area;
+ void *ret = NULL;
+ int rc;
+
+ NATMEM_LOG_FLOW_FUNC(("ENTER: %R[mzone], size:%d, pflags:%p, %RTbool\n", zone, size, pflags, fWait)); RT_NOREF(size, pflags, fWait);
+ RTCritSectEnter(&zone->csZone);
+ for (;;)
+ {
+ if (!LIST_EMPTY(&zone->free_items))
+ {
+ it = LIST_FIRST(&zone->free_items);
+ Assert(it->magic == ITEM_MAGIC);
+ rc = 0;
+ if (zone->pfInit)
+ rc = zone->pfInit(zone->pData, (void *)&it[1], (int /*sigh*/)zone->size, M_DONTWAIT);
+ if (rc == 0)
+ {
+ zone->cur_items++;
+ LIST_REMOVE(it, list);
+ LIST_INSERT_HEAD(&zone->used_items, it, list);
+ slirp_zone_check_and_send_pending(zone); /* may exit+enter the cs! */
+ ret = (void *)&it[1];
+ }
+ else
+ {
+ AssertMsgFailed(("NAT: item initialization failed for zone %s\n", zone->name));
+ ret = NULL;
+ }
+ break;
+ }
+
+ if (!zone->master_zone)
+ {
+ /* We're on the master zone and we can't allocate more. */
+ NATMEM_LOG_2(("NAT: no room on %s zone\n", zone->name));
+ /* AssertMsgFailed(("NAT: OOM!")); */
+ zone->fDoXmitPending = true;
+ break;
+ }
+
+ /* we're on a sub-zone, we need get a chunk from the master zone and split
+ * it into sub-zone conforming chunks.
+ */
+ sub_area = slirp_uma_alloc(zone->master_zone, (int /*sigh*/)zone->master_zone->size, NULL, 0);
+ if (!sub_area)
+ {
+ /* No room on master */
+ NATMEM_LOG_2(("NAT: no room on %s zone for %s zone\n", zone->master_zone->name, zone->name));
+ break;
+ }
+ zone->max_items++;
+ it = &((struct item *)sub_area)[-1];
+ /* It's the chunk descriptor of the master zone, we should remove it
+ * from the master list first.
+ */
+ Assert((it->zone && it->zone->magic == ZONE_MAGIC));
+ RTCritSectEnter(&it->zone->csZone);
+ /** @todo should we alter count of master counters? */
+ LIST_REMOVE(it, list);
+ RTCritSectLeave(&it->zone->csZone);
+
+ /** @todo '+ zone->size' should be depend on flag */
+ memset(it, 0, sizeof(struct item));
+ it->zone = zone;
+ it->magic = ITEM_MAGIC;
+ LIST_INSERT_HEAD(&zone->free_items, it, list);
+ if (zone->cur_items >= zone->max_items)
+ LogRel(("NAT: Zone(%s) has reached it maximum\n", zone->name));
+ }
+ RTCritSectLeave(&zone->csZone);
+ NATMEM_LOG_FLOW_FUNC(("LEAVE: %p\n", ret));
+ return ret;
+}
+
+static void slirp_uma_free(void *item, int size, uint8_t flags)
+{
+ struct item *it;
+ uma_zone_t zone;
+
+ Assert(item);
+ it = &((struct item *)item)[-1];
+ NATMEM_LOG_FLOW_FUNC(("ENTER: item:%p(%R[mzoneitem]), size:%d, flags:%RX8\n", item, it, size, flags)); RT_NOREF(size, flags);
+ Assert(it->magic == ITEM_MAGIC);
+ zone = it->zone;
+ /* check border magic */
+ Assert((*(uint32_t *)(((uint8_t *)&it[1]) + zone->size) == 0xabadbabe));
+
+ RTCritSectEnter(&zone->csZone);
+ Assert(zone->magic == ZONE_MAGIC);
+ LIST_REMOVE(it, list);
+ if (zone->pfFini)
+ {
+ zone->pfFini(zone->pData, item, (int /*sigh*/)zone->size);
+ }
+ if (zone->pfDtor)
+ {
+ zone->pfDtor(zone->pData, item, (int /*sigh*/)zone->size, NULL);
+ }
+ LIST_INSERT_HEAD(&zone->free_items, it, list);
+ zone->cur_items--;
+ slirp_zone_check_and_send_pending(zone); /* may exit+enter the cs! */
+ RTCritSectLeave(&zone->csZone);
+ NATMEM_LOG_FLOW_FUNC_LEAVE();
+}
+
+uma_zone_t uma_zcreate(PNATState pData, char *name, size_t size,
+ ctor_t ctor, dtor_t dtor, zinit_t init, zfini_t fini, int flags1, int flags2)
+{
+ uma_zone_t zone = NULL;
+ NATMEM_LOG_FLOW_FUNC(("ENTER: name:%s size:%d, ctor:%p, dtor:%p, init:%p, fini:%p, flags1:%RX32, flags2:%RX32\n",
+ name, ctor, dtor, init, fini, flags1, flags2)); RT_NOREF(flags1, flags2);
+ zone = RTMemAllocZ(sizeof(struct uma_zone));
+ Assert((pData));
+ zone->magic = ZONE_MAGIC;
+ zone->pData = pData;
+ zone->name = name;
+ zone->size = size;
+ zone->pfCtor = ctor;
+ zone->pfDtor = dtor;
+ zone->pfInit = init;
+ zone->pfFini = fini;
+ zone->pfAlloc = slirp_uma_alloc;
+ zone->pfFree = slirp_uma_free;
+ RTCritSectInit(&zone->csZone);
+ NATMEM_LOG_FLOW_FUNC(("LEAVE: %R[mzone]\n", zone));
+ return zone;
+
+}
+uma_zone_t uma_zsecond_create(char *name, ctor_t ctor,
+ dtor_t dtor, zinit_t init, zfini_t fini, uma_zone_t master)
+{
+ uma_zone_t zone;
+ Assert(master);
+ NATMEM_LOG_FLOW_FUNC(("ENTER: name:%s ctor:%p, dtor:%p, init:%p, fini:%p, master:%R[mzone]\n",
+ name, ctor, dtor, init, fini, master));
+ zone = RTMemAllocZ(sizeof(struct uma_zone));
+ if (zone == NULL)
+ {
+ NATMEM_LOG_FLOW_FUNC(("LEAVE: %R[mzone]\n", NULL));
+ return NULL;
+ }
+
+ Assert((master && master->pData));
+ zone->magic = ZONE_MAGIC;
+ zone->pData = master->pData;
+ zone->name = name;
+ zone->pfCtor = ctor;
+ zone->pfDtor = dtor;
+ zone->pfInit = init;
+ zone->pfFini = fini;
+ zone->pfAlloc = slirp_uma_alloc;
+ zone->pfFree = slirp_uma_free;
+ zone->size = master->size;
+ zone->master_zone = master;
+ RTCritSectInit(&zone->csZone);
+ NATMEM_LOG_FLOW_FUNC(("LEAVE: %R[mzone]\n", zone));
+ return zone;
+}
+
+void uma_zone_set_max(uma_zone_t zone, int max)
+{
+ int i = 0;
+ struct item *it;
+ NATMEM_LOG_FLOW_FUNC(("ENTER: zone:%R[mzone], max:%d\n", zone, max));
+ zone->max_items = max;
+ zone->area = RTMemAllocZ(max * (sizeof(struct item) + zone->size + sizeof(uint32_t)));
+ for (; i < max; ++i)
+ {
+ it = (struct item *)(((uint8_t *)zone->area) + i*(sizeof(struct item) + zone->size + sizeof(uint32_t)));
+ it->magic = ITEM_MAGIC;
+ it->zone = zone;
+ *(uint32_t *)(((uint8_t *)&it[1]) + zone->size) = 0xabadbabe;
+ LIST_INSERT_HEAD(&zone->free_items, it, list);
+ }
+ NATMEM_LOG_FLOW_FUNC_LEAVE();
+}
+
+void uma_zone_set_allocf(uma_zone_t zone, uma_alloc_t pfAlloc)
+{
+ NATMEM_LOG_FLOW_FUNC(("ENTER: zone:%R[mzone], pfAlloc:%Rfn\n", zone, pfAlloc));
+ zone->pfAlloc = pfAlloc;
+ NATMEM_LOG_FLOW_FUNC_LEAVE();
+}
+
+void uma_zone_set_freef(uma_zone_t zone, uma_free_t pfFree)
+{
+ NATMEM_LOG_FLOW_FUNC(("ENTER: zone:%R[mzone], pfAlloc:%Rfn\n", zone, pfFree));
+ zone->pfFree = pfFree;
+ NATMEM_LOG_FLOW_FUNC_LEAVE();
+}
+
+uint32_t *uma_find_refcnt(uma_zone_t zone, void *mem)
+{
+ /** @todo (vvl) this function supposed to work with special zone storing
+ reference counters */
+ struct item *it = NULL;
+ NATMEM_LOG_FLOW_FUNC(("ENTER: zone:%R[mzone], mem:%p\n", zone, mem)); RT_NOREF(zone);
+ it = (struct item *)mem; /* 1st element */
+ Assert(mem != NULL);
+ Assert(zone->magic == ZONE_MAGIC);
+ /* for returning pointer to counter we need get 0 elemnt */
+ Assert(it[-1].magic == ITEM_MAGIC);
+ NATMEM_LOG_FLOW_FUNC(("LEAVE: %p\n", &it[-1].ref_count));
+ return &it[-1].ref_count;
+}
+
+void *uma_zalloc_arg(uma_zone_t zone, void *args, int how)
+{
+ void *mem;
+ Assert(zone->magic == ZONE_MAGIC);
+ NATMEM_LOG_FLOW_FUNC(("ENTER: zone:%R[mzone], args:%p, how:%RX32\n", zone, args, how)); RT_NOREF(how);
+ if (zone->pfAlloc == NULL)
+ {
+ NATMEM_LOG_FLOW_FUNC(("LEAVE: NULL\n"));
+ return NULL;
+ }
+ RTCritSectEnter(&zone->csZone);
+ mem = zone->pfAlloc(zone, (int /*sigh*/)zone->size, NULL, 0);
+ if (mem != NULL)
+ {
+ if (zone->pfCtor)
+ zone->pfCtor(zone->pData, mem, (int /*sigh*/)zone->size, args, M_DONTWAIT);
+ }
+ RTCritSectLeave(&zone->csZone);
+ NATMEM_LOG_FLOW_FUNC(("LEAVE: %p\n", mem));
+ return mem;
+}
+
+void uma_zfree(uma_zone_t zone, void *item)
+{
+ NATMEM_LOG_FLOW_FUNC(("ENTER: zone:%R[mzone], item:%p\n", zone, item));
+ uma_zfree_arg(zone, item, NULL);
+ NATMEM_LOG_FLOW_FUNC_LEAVE();
+}
+
+void uma_zfree_arg(uma_zone_t zone, void *mem, void *flags)
+{
+ struct item *it;
+ Assert(zone->magic == ZONE_MAGIC);
+ Assert((zone->pfFree));
+ Assert((mem));
+ NATMEM_LOG_FLOW_FUNC(("ENTER: zone:%R[mzone], mem:%p, flags:%p\n", zone, mem, flags)); RT_NOREF(flags);
+
+ RTCritSectEnter(&zone->csZone);
+ it = &((struct item *)mem)[-1];
+ Assert((it->magic == ITEM_MAGIC));
+ Assert((zone->magic == ZONE_MAGIC && zone == it->zone));
+
+ zone->pfFree(mem, 0, 0);
+ RTCritSectLeave(&zone->csZone);
+
+ NATMEM_LOG_FLOW_FUNC_LEAVE();
+}
+
+int uma_zone_exhausted_nolock(uma_zone_t zone)
+{
+ int fExhausted;
+ NATMEM_LOG_FLOW_FUNC(("ENTER: zone:%R[mzone]\n", zone));
+ RTCritSectEnter(&zone->csZone);
+ fExhausted = (zone->cur_items == zone->max_items);
+ RTCritSectLeave(&zone->csZone);
+ NATMEM_LOG_FLOW_FUNC(("LEAVE: %RTbool\n", fExhausted));
+ return fExhausted;
+}
+
+void zone_drain(uma_zone_t zone)
+{
+ struct item *it;
+ uma_zone_t master_zone;
+
+ /* vvl: Huh? What to do with zone which hasn't got backstore ? */
+ Assert((zone->master_zone));
+ NATMEM_LOG_FLOW_FUNC(("ENTER: zone:%R[mzone]\n", zone));
+ master_zone = zone->master_zone;
+ while (!LIST_EMPTY(&zone->free_items))
+ {
+ it = LIST_FIRST(&zone->free_items);
+ Assert((it->magic == ITEM_MAGIC));
+
+ RTCritSectEnter(&zone->csZone);
+ LIST_REMOVE(it, list);
+ zone->max_items--;
+ RTCritSectLeave(&zone->csZone);
+
+ it->zone = master_zone;
+
+ RTCritSectEnter(&master_zone->csZone);
+ LIST_INSERT_HEAD(&master_zone->free_items, it, list);
+ master_zone->cur_items--;
+ slirp_zone_check_and_send_pending(master_zone); /* may exit+enter the cs! */
+ RTCritSectLeave(&master_zone->csZone);
+ }
+ NATMEM_LOG_FLOW_FUNC_LEAVE();
+}
+
+void slirp_null_arg_free(void *mem, void *arg)
+{
+ /** @todo (vvl) make it wiser */
+ NATMEM_LOG_FLOW_FUNC(("ENTER: mem:%p, arg:%p\n", mem, arg));
+ RT_NOREF(arg);
+ Assert(mem);
+ RTMemFree(mem);
+ NATMEM_LOG_FLOW_FUNC_LEAVE();
+}
+
+void *uma_zalloc(uma_zone_t zone, int len)
+{
+ NATMEM_LOG_FLOW_FUNC(("ENTER: zone:%R[mzone], len:%d\n", zone, len));
+ RT_NOREF(zone, len);
+ NATMEM_LOG_FLOW_FUNC(("LEAVE: NULL"));
+ return NULL;
+}
+
+struct mbuf *slirp_ext_m_get(PNATState pData, size_t cbMin, void **ppvBuf, size_t *pcbBuf)
+{
+ struct mbuf *m;
+ int size = MCLBYTES;
+ NATMEM_LOG_FLOW_FUNC(("ENTER: cbMin:%d, ppvBuf:%p, pcbBuf:%p\n", cbMin, ppvBuf, pcbBuf));
+
+ *ppvBuf = NULL;
+ *pcbBuf = 0;
+
+ if (cbMin < MCLBYTES)
+ size = MCLBYTES;
+ else if (cbMin < MJUM9BYTES)
+ size = MJUM9BYTES;
+ else if (cbMin < MJUM16BYTES)
+ size = MJUM16BYTES;
+ else
+ {
+ AssertMsgFailed(("Unsupported size %zu", cbMin));
+ NATMEM_LOG_FLOW_FUNC(("LEAVE: NULL (bad size %zu)\n", cbMin));
+ return NULL;
+ }
+
+ m = m_getjcl(pData, M_NOWAIT, MT_HEADER, M_PKTHDR, size);
+ if (m == NULL)
+ {
+ NATMEM_LOG_FLOW_FUNC(("LEAVE: NULL\n"));
+ return NULL;
+ }
+ m->m_len = size;
+ *ppvBuf = mtod(m, void *);
+ *pcbBuf = size;
+ NATMEM_LOG_FLOW_FUNC(("LEAVE: %p\n", m));
+ return m;
+}
+
+void slirp_ext_m_free(PNATState pData, struct mbuf *m, uint8_t *pu8Buf)
+{
+
+ NATMEM_LOG_FLOW_FUNC(("ENTER: m:%p, pu8Buf:%p\n", m, pu8Buf));
+ if ( !pu8Buf
+ && pu8Buf != mtod(m, uint8_t *))
+ RTMemFree(pu8Buf); /* This buffer was allocated on heap */
+ m_freem(pData, m);
+ NATMEM_LOG_FLOW_FUNC_LEAVE();
+}
+
+static void zone_destroy(uma_zone_t zone)
+{
+ RTCritSectEnter(&zone->csZone);
+ NATMEM_LOG_FLOW_FUNC(("ENTER: zone:%R[mzone]\n", zone));
+ LogRel(("NAT: Zone(nm:%s, used:%d)\n", zone->name, zone->cur_items));
+ RTMemFree(zone->area);
+ RTCritSectLeave(&zone->csZone);
+ RTCritSectDelete(&zone->csZone);
+ RTMemFree(zone);
+ NATMEM_LOG_FLOW_FUNC_LEAVE();
+}
+
+void m_fini(PNATState pData)
+{
+ NATMEM_LOG_FLOW_FUNC_ENTER();
+# define ZONE_DESTROY(zone) do { zone_destroy((zone)); (zone) = NULL;} while (0)
+ ZONE_DESTROY(pData->zone_clust);
+ ZONE_DESTROY(pData->zone_pack);
+ ZONE_DESTROY(pData->zone_mbuf);
+ ZONE_DESTROY(pData->zone_jumbop);
+ ZONE_DESTROY(pData->zone_jumbo9);
+ ZONE_DESTROY(pData->zone_jumbo16);
+ ZONE_DESTROY(pData->zone_ext_refcnt);
+# undef ZONE_DESTROY
+ /** @todo do finalize here.*/
+ NATMEM_LOG_FLOW_FUNC_LEAVE();
+}
+
+void
+if_init(PNATState pData)
+{
+ /* 14 for ethernet */
+ if_maxlinkhdr = 14;
+ if_comp = IF_AUTOCOMP;
+ if_mtu = 1500;
+ if_mru = 1500;
+}
+
+#endif /* VBOX_NAT_TST_QUEUE */
diff --git a/src/VBox/Devices/Network/slirp/misc.h b/src/VBox/Devices/Network/slirp/misc.h
new file mode 100644
index 00000000..ac25be25
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/misc.h
@@ -0,0 +1,88 @@
+/* $Id: misc.h $ */
+/** @file
+ * NAT - helpers (declarations/defines).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _MISC_H_
+#define _MISC_H_
+
+#ifdef VBOX_NAT_TST_QUEUE
+typedef void *PNATState;
+#endif
+
+void slirp_insque (PNATState, void *, void *);
+void slirp_remque (PNATState, void *);
+# ifndef VBOX_NAT_TST_QUEUE
+void getouraddr (PNATState);
+void fd_nonblock (int);
+
+/* UVM interface */
+#define UMA_ALIGN_PTR (1 << 0)
+#define UMA_ZONE_REFCNT (1 << 1)
+#define UMA_ZONE_MAXBUCKET (1 << 2)
+#define UMA_ZONE_ZINIT (1 << 3)
+#define UMA_SLAB_KERNEL (1 << 4)
+#define UMA_ZFLAG_FULL (1 << 5)
+
+struct uma_zone;
+typedef struct uma_zone *uma_zone_t;
+
+typedef void *(*uma_alloc_t)(uma_zone_t, int, u_int8_t *, int);
+typedef void (*uma_free_t)(void *, int, u_int8_t);
+
+typedef int (*ctor_t)(PNATState, void *, int, void *, int);
+typedef void (*dtor_t)(PNATState, void *, int, void *);
+typedef int (*zinit_t)(PNATState, void *, int, int);
+typedef void (*zfini_t)(PNATState, void *, int);
+uma_zone_t uma_zcreate(PNATState, char *, size_t, ctor_t, dtor_t, zinit_t, zfini_t, int, int);
+uma_zone_t uma_zsecond_create(char *, ctor_t, dtor_t, zinit_t, zfini_t, uma_zone_t);
+void uma_zone_set_max(uma_zone_t, int);
+void uma_zone_set_allocf(uma_zone_t, uma_alloc_t);
+void uma_zone_set_freef(uma_zone_t, uma_free_t);
+
+uint32_t *uma_find_refcnt(uma_zone_t, void *);
+void *uma_zalloc(uma_zone_t, int);
+void *uma_zalloc_arg(uma_zone_t, void *, int);
+void uma_zfree(uma_zone_t, void *);
+void uma_zfree_arg(uma_zone_t, void *, void *);
+int uma_zone_exhausted_nolock(uma_zone_t);
+void zone_drain(uma_zone_t);
+
+void slirp_null_arg_free(void *, void *);
+void m_fini(PNATState pData);
+# else /* VBOX_NAT_TST_QUEUE */
+# define insque slirp_insque
+# define remque slirp_remque
+# endif
+#endif
diff --git a/src/VBox/Devices/Network/slirp/queue.h b/src/VBox/Devices/Network/slirp/queue.h
new file mode 100644
index 00000000..c686c0a2
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/queue.h
@@ -0,0 +1,653 @@
+/* $Id: queue.h $ */
+/** @file
+ * NAT - Queue handling.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1991, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 4. Neither the name of the University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)queue.h 8.5 (Berkeley) 8/20/94
+ * $FreeBSD: src/sys/sys/queue.h,v 1.68 2006/10/24 11:20:29 ru Exp $
+ */
+
+#ifndef _SYS_QUEUE_H_
+#define _SYS_QUEUE_H_
+
+#include <iprt/cdefs.h>
+#ifdef RT_OS_WINDOWS
+#ifdef SLIST_ENTRY
+/*Here is a conflict with winnt.h*/
+#undef SLIST_ENTRY
+#endif
+#endif /* RT_OS_WINDOWS */
+
+/*
+ * This file defines four types of data structures: singly-linked lists,
+ * singly-linked tail queues, lists and tail queues.
+ *
+ * A singly-linked list is headed by a single forward pointer. The elements
+ * are singly linked for minimum space and pointer manipulation overhead at
+ * the expense of O(n) removal for arbitrary elements. New elements can be
+ * added to the list after an existing element or at the head of the list.
+ * Elements being removed from the head of the list should use the explicit
+ * macro for this purpose for optimum efficiency. A singly-linked list may
+ * only be traversed in the forward direction. Singly-linked lists are ideal
+ * for applications with large datasets and few or no removals or for
+ * implementing a LIFO queue.
+ *
+ * A singly-linked tail queue is headed by a pair of pointers, one to the
+ * head of the list and the other to the tail of the list. The elements are
+ * singly linked for minimum space and pointer manipulation overhead at the
+ * expense of O(n) removal for arbitrary elements. New elements can be added
+ * to the list after an existing element, at the head of the list, or at the
+ * end of the list. Elements being removed from the head of the tail queue
+ * should use the explicit macro for this purpose for optimum efficiency.
+ * A singly-linked tail queue may only be traversed in the forward direction.
+ * Singly-linked tail queues are ideal for applications with large datasets
+ * and few or no removals or for implementing a FIFO queue.
+ *
+ * A list is headed by a single forward pointer (or an array of forward
+ * pointers for a hash table header). The elements are doubly linked
+ * so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before
+ * or after an existing element or at the head of the list. A list
+ * may only be traversed in the forward direction.
+ *
+ * A tail queue is headed by a pair of pointers, one to the head of the
+ * list and the other to the tail of the list. The elements are doubly
+ * linked so that an arbitrary element can be removed without a need to
+ * traverse the list. New elements can be added to the list before or
+ * after an existing element, at the head of the list, or at the end of
+ * the list. A tail queue may be traversed in either direction.
+ *
+ * For details on the use of these macros, see the queue(3) manual page.
+ *
+ *
+ * SLIST LIST STAILQ TAILQ
+ * _HEAD + + + +
+ * _HEAD_INITIALIZER + + + +
+ * _ENTRY + + + +
+ * _INIT + + + +
+ * _EMPTY + + + +
+ * _FIRST + + + +
+ * _NEXT + + + +
+ * _PREV - - - +
+ * _LAST - - + +
+ * _FOREACH + + + +
+ * _FOREACH_SAFE + + + +
+ * _FOREACH_REVERSE - - - +
+ * _FOREACH_REVERSE_SAFE - - - +
+ * _INSERT_HEAD + + + +
+ * _INSERT_BEFORE - + - +
+ * _INSERT_AFTER + + + +
+ * _INSERT_TAIL - - + +
+ * _CONCAT - - + +
+ * _REMOVE_HEAD + - + -
+ * _REMOVE + + + +
+ *
+ */
+#ifdef QUEUE_MACRO_DEBUG
+/* Store the last 2 places the queue element or head was altered */
+struct qm_trace {
+ char * lastfile;
+ int lastline;
+ char * prevfile;
+ int prevline;
+};
+
+#define TRACEBUF struct qm_trace trace;
+#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
+
+#define QMD_TRACE_HEAD(head) do { \
+ (head)->trace.prevline = (head)->trace.lastline; \
+ (head)->trace.prevfile = (head)->trace.lastfile; \
+ (head)->trace.lastline = __LINE__; \
+ (head)->trace.lastfile = __FILE__; \
+} while (0)
+
+#define QMD_TRACE_ELEM(elem) do { \
+ (elem)->trace.prevline = (elem)->trace.lastline; \
+ (elem)->trace.prevfile = (elem)->trace.lastfile; \
+ (elem)->trace.lastline = __LINE__; \
+ (elem)->trace.lastfile = __FILE__; \
+} while (0)
+
+#else
+#define QMD_TRACE_ELEM(elem)
+#define QMD_TRACE_HEAD(head)
+#define TRACEBUF
+#define TRASHIT(x)
+#endif /* QUEUE_MACRO_DEBUG */
+
+/*
+ * Singly-linked List declarations.
+ */
+#define SLIST_HEAD(name, type) \
+struct name { \
+ struct type *slh_first; /* first element */ \
+}
+
+#define SLIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define SLIST_ENTRY(type) \
+struct { \
+ struct type *sle_next; /* next element */ \
+}
+
+/*
+ * Singly-linked List functions.
+ */
+#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
+
+#define SLIST_FIRST(head) ((head)->slh_first)
+
+#define SLIST_FOREACH(var, head, field) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var); \
+ (var) = SLIST_NEXT((var), field))
+
+#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = SLIST_FIRST((head)); \
+ (var) && ((tvar) = SLIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
+ for ((varp) = &SLIST_FIRST((head)); \
+ ((var) = *(varp)) != NULL; \
+ (varp) = &SLIST_NEXT((var), field))
+
+#define SLIST_INIT(head) do { \
+ SLIST_FIRST((head)) = NULL; \
+} while (0)
+
+#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
+ SLIST_NEXT((slistelm), field) = (elm); \
+} while (0)
+
+#define SLIST_INSERT_HEAD(head, elm, field) do { \
+ SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
+ SLIST_FIRST((head)) = (elm); \
+} while (0)
+
+#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
+
+#define SLIST_REMOVE(head, elm, type, field) do { \
+ if (SLIST_FIRST((head)) == (elm)) { \
+ SLIST_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = SLIST_FIRST((head)); \
+ while (SLIST_NEXT(curelm, field) != (elm)) \
+ curelm = SLIST_NEXT(curelm, field); \
+ SLIST_NEXT(curelm, field) = \
+ SLIST_NEXT(SLIST_NEXT(curelm, field), field); \
+ } \
+ TRASHIT((elm)->field.sle_next); \
+} while (0)
+
+#define SLIST_REMOVE_HEAD(head, field) do { \
+ SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
+} while (0)
+
+/*
+ * Singly-linked Tail queue declarations.
+ */
+#define STAILQ_HEAD(name, type) \
+struct name { \
+ struct type *stqh_first;/* first element */ \
+ struct type **stqh_last;/* addr of last next element */ \
+}
+
+#define STAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).stqh_first }
+
+#define STAILQ_ENTRY(type) \
+struct { \
+ struct type *stqe_next; /* next element */ \
+}
+
+/*
+ * Singly-linked Tail queue functions.
+ */
+#define STAILQ_CONCAT(head1, head2) do { \
+ if (!STAILQ_EMPTY((head2))) { \
+ *(head1)->stqh_last = (head2)->stqh_first; \
+ (head1)->stqh_last = (head2)->stqh_last; \
+ STAILQ_INIT((head2)); \
+ } \
+} while (0)
+
+#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
+
+#define STAILQ_FIRST(head) ((head)->stqh_first)
+
+#define STAILQ_FOREACH(var, head, field) \
+ for((var) = STAILQ_FIRST((head)); \
+ (var); \
+ (var) = STAILQ_NEXT((var), field))
+
+
+#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = STAILQ_FIRST((head)); \
+ (var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define STAILQ_INIT(head) do { \
+ STAILQ_FIRST((head)) = NULL; \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_NEXT((tqelm), field) = (elm); \
+} while (0)
+
+#define STAILQ_INSERT_HEAD(head, elm, field) do { \
+ if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+ STAILQ_FIRST((head)) = (elm); \
+} while (0)
+
+#define STAILQ_INSERT_TAIL(head, elm, field) do { \
+ STAILQ_NEXT((elm), field) = NULL; \
+ *(head)->stqh_last = (elm); \
+ (head)->stqh_last = &STAILQ_NEXT((elm), field); \
+} while (0)
+
+#define STAILQ_LAST(head, type, field) \
+ (STAILQ_EMPTY((head)) ? \
+ NULL : \
+ ((struct type *)(void *) \
+ ((char *)((head)->stqh_last) - __offsetof(struct type, field))))
+
+#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
+
+#define STAILQ_REMOVE(head, elm, type, field) do { \
+ if (STAILQ_FIRST((head)) == (elm)) { \
+ STAILQ_REMOVE_HEAD((head), field); \
+ } \
+ else { \
+ struct type *curelm = STAILQ_FIRST((head)); \
+ while (STAILQ_NEXT(curelm, field) != (elm)) \
+ curelm = STAILQ_NEXT(curelm, field); \
+ if ((STAILQ_NEXT(curelm, field) = \
+ STAILQ_NEXT(STAILQ_NEXT(curelm, field), field)) == NULL)\
+ (head)->stqh_last = &STAILQ_NEXT((curelm), field);\
+ } \
+ TRASHIT((elm)->field.stqe_next); \
+} while (0)
+
+#define STAILQ_REMOVE_HEAD(head, field) do { \
+ if ((STAILQ_FIRST((head)) = \
+ STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
+ (head)->stqh_last = &STAILQ_FIRST((head)); \
+} while (0)
+
+/*
+ * List declarations.
+ */
+#define LIST_HEAD(name, type) \
+struct name { \
+ struct type *lh_first; /* first element */ \
+}
+
+#define LIST_HEAD_INITIALIZER(head) \
+ { NULL }
+
+#define LIST_ENTRY(type) \
+struct { \
+ struct type *le_next; /* next element */ \
+ struct type **le_prev; /* address of previous next element */ \
+}
+
+/*
+ * List functions.
+ */
+
+#if (defined(_KERNEL) && defined(INVARIANTS))
+#define QMD_LIST_CHECK_HEAD(head, field) do { \
+ if (LIST_FIRST((head)) != NULL && \
+ LIST_FIRST((head))->field.le_prev != \
+ &LIST_FIRST((head))) \
+ panic("Bad list head %p first->prev != head", (head)); \
+} while (0)
+
+#define QMD_LIST_CHECK_NEXT(elm, field) do { \
+ if (LIST_NEXT((elm), field) != NULL && \
+ LIST_NEXT((elm), field)->field.le_prev != \
+ &((elm)->field.le_next)) \
+ panic("Bad link elm %p next->prev != elm", (elm)); \
+} while (0)
+
+#define QMD_LIST_CHECK_PREV(elm, field) do { \
+ if (*(elm)->field.le_prev != (elm)) \
+ panic("Bad link elm %p prev->next != elm", (elm)); \
+} while (0)
+#else
+#define QMD_LIST_CHECK_HEAD(head, field)
+#define QMD_LIST_CHECK_NEXT(elm, field)
+#define QMD_LIST_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define LIST_EMPTY(head) ((head)->lh_first == NULL)
+
+#define LIST_FIRST(head) ((head)->lh_first)
+
+#define LIST_FOREACH(var, head, field) \
+ for ((var) = LIST_FIRST((head)); \
+ (var); \
+ (var) = LIST_NEXT((var), field))
+
+#define LIST_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = LIST_FIRST((head)); \
+ (var) && ((tvar) = LIST_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define LIST_INIT(head) do { \
+ LIST_FIRST((head)) = NULL; \
+} while (0)
+
+#define LIST_INSERT_AFTER(listelm, elm, field) do { \
+ QMD_LIST_CHECK_NEXT(listelm, field); \
+ if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
+ LIST_NEXT((listelm), field)->field.le_prev = \
+ &LIST_NEXT((elm), field); \
+ LIST_NEXT((listelm), field) = (elm); \
+ (elm)->field.le_prev = &LIST_NEXT((listelm), field); \
+} while (0)
+
+#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
+ QMD_LIST_CHECK_PREV(listelm, field); \
+ (elm)->field.le_prev = (listelm)->field.le_prev; \
+ LIST_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.le_prev = (elm); \
+ (listelm)->field.le_prev = &LIST_NEXT((elm), field); \
+} while (0)
+
+#define LIST_INSERT_HEAD(head, elm, field) do { \
+ QMD_LIST_CHECK_HEAD((head), field); \
+ if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
+ LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
+ LIST_FIRST((head)) = (elm); \
+ (elm)->field.le_prev = &LIST_FIRST((head)); \
+} while (0)
+
+#define LIST_NEXT(elm, field) ((elm)->field.le_next)
+
+#define LIST_REMOVE(elm, field) do { \
+ QMD_LIST_CHECK_NEXT(elm, field); \
+ QMD_LIST_CHECK_PREV(elm, field); \
+ if (LIST_NEXT((elm), field) != NULL) \
+ LIST_NEXT((elm), field)->field.le_prev = \
+ (elm)->field.le_prev; \
+ *(elm)->field.le_prev = LIST_NEXT((elm), field); \
+ TRASHIT((elm)->field.le_next); \
+ TRASHIT((elm)->field.le_prev); \
+} while (0)
+
+/*
+ * Tail queue declarations.
+ */
+#define TAILQ_HEAD(name, type) \
+struct name { \
+ struct type *tqh_first; /* first element */ \
+ struct type **tqh_last; /* addr of last next element */ \
+ TRACEBUF \
+}
+
+#define TAILQ_HEAD_INITIALIZER(head) \
+ { NULL, &(head).tqh_first }
+
+#define TAILQ_ENTRY(type) \
+struct { \
+ struct type *tqe_next; /* next element */ \
+ struct type **tqe_prev; /* address of previous next element */ \
+ TRACEBUF \
+}
+
+/*
+ * Tail queue functions.
+ */
+#if (defined(_KERNEL) && defined(INVARIANTS))
+#define QMD_TAILQ_CHECK_HEAD(head, field) do { \
+ if (!TAILQ_EMPTY(head) && \
+ TAILQ_FIRST((head))->field.tqe_prev != \
+ &TAILQ_FIRST((head))) \
+ panic("Bad tailq head %p first->prev != head", (head)); \
+} while (0)
+
+#define QMD_TAILQ_CHECK_TAIL(head, field) do { \
+ if (*(head)->tqh_last != NULL) \
+ panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
+} while (0)
+
+#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \
+ if (TAILQ_NEXT((elm), field) != NULL && \
+ TAILQ_NEXT((elm), field)->field.tqe_prev != \
+ &((elm)->field.tqe_next)) \
+ panic("Bad link elm %p next->prev != elm", (elm)); \
+} while (0)
+
+#define QMD_TAILQ_CHECK_PREV(elm, field) do { \
+ if (*(elm)->field.tqe_prev != (elm)) \
+ panic("Bad link elm %p prev->next != elm", (elm)); \
+} while (0)
+#else
+#define QMD_TAILQ_CHECK_HEAD(head, field)
+#define QMD_TAILQ_CHECK_TAIL(head, headname)
+#define QMD_TAILQ_CHECK_NEXT(elm, field)
+#define QMD_TAILQ_CHECK_PREV(elm, field)
+#endif /* (_KERNEL && INVARIANTS) */
+
+#define TAILQ_CONCAT(head1, head2, field) do { \
+ if (!TAILQ_EMPTY(head2)) { \
+ *(head1)->tqh_last = (head2)->tqh_first; \
+ (head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
+ (head1)->tqh_last = (head2)->tqh_last; \
+ TAILQ_INIT((head2)); \
+ QMD_TRACE_HEAD(head1); \
+ QMD_TRACE_HEAD(head2); \
+ } \
+} while (0)
+
+#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
+
+#define TAILQ_FIRST(head) ((head)->tqh_first)
+
+#define TAILQ_FOREACH(var, head, field) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var); \
+ (var) = TAILQ_NEXT((var), field))
+
+#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
+ for ((var) = TAILQ_FIRST((head)); \
+ (var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var); \
+ (var) = TAILQ_PREV((var), headname, field))
+
+#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
+ for ((var) = TAILQ_LAST((head), headname); \
+ (var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
+ (var) = (tvar))
+
+#define TAILQ_INIT(head) do { \
+ TAILQ_FIRST((head)) = NULL; \
+ (head)->tqh_last = &TAILQ_FIRST((head)); \
+ QMD_TRACE_HEAD(head); \
+} while (0)
+
+#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
+ QMD_TAILQ_CHECK_NEXT(listelm, field); \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else { \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_HEAD(head); \
+ } \
+ TAILQ_NEXT((listelm), field) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ QMD_TRACE_ELEM(&listelm->field); \
+} while (0)
+
+#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
+ QMD_TAILQ_CHECK_PREV(listelm, field); \
+ (elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
+ TAILQ_NEXT((elm), field) = (listelm); \
+ *(listelm)->field.tqe_prev = (elm); \
+ (listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+ QMD_TRACE_ELEM(&listelm->field); \
+} while (0)
+
+#define TAILQ_INSERT_HEAD(head, elm, field) do { \
+ QMD_TAILQ_CHECK_HEAD(head, field); \
+ if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
+ TAILQ_FIRST((head))->field.tqe_prev = \
+ &TAILQ_NEXT((elm), field); \
+ else \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ TAILQ_FIRST((head)) = (elm); \
+ (elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (0)
+
+#define TAILQ_INSERT_TAIL(head, elm, field) do { \
+ QMD_TAILQ_CHECK_TAIL(head, field); \
+ TAILQ_NEXT((elm), field) = NULL; \
+ (elm)->field.tqe_prev = (head)->tqh_last; \
+ *(head)->tqh_last = (elm); \
+ (head)->tqh_last = &TAILQ_NEXT((elm), field); \
+ QMD_TRACE_HEAD(head); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (0)
+
+#define TAILQ_LAST(head, headname) \
+ (*(((struct headname *)((head)->tqh_last))->tqh_last))
+
+#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
+
+#define TAILQ_PREV(elm, headname, field) \
+ (*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
+
+#define TAILQ_REMOVE(head, elm, field) do { \
+ QMD_TAILQ_CHECK_NEXT(elm, field); \
+ QMD_TAILQ_CHECK_PREV(elm, field); \
+ if ((TAILQ_NEXT((elm), field)) != NULL) \
+ TAILQ_NEXT((elm), field)->field.tqe_prev = \
+ (elm)->field.tqe_prev; \
+ else { \
+ (head)->tqh_last = (elm)->field.tqe_prev; \
+ QMD_TRACE_HEAD(head); \
+ } \
+ *(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
+ TRASHIT((elm)->field.tqe_next); \
+ TRASHIT((elm)->field.tqe_prev); \
+ QMD_TRACE_ELEM(&(elm)->field); \
+} while (0)
+
+
+#ifdef _KERNEL
+
+/*
+ * XXX insque() and remque() are an old way of handling certain queues.
+ * They bogusly assumes that all queue heads look alike.
+ */
+
+struct quehead {
+ struct quehead *qh_link;
+ struct quehead *qh_rlink;
+};
+
+#ifdef __CC_SUPPORTS___INLINE
+
+static __inline void
+insque(void *a, void *b)
+{
+ struct quehead *element = (struct quehead *)a,
+ *head = (struct quehead *)b;
+
+ element->qh_link = head->qh_link;
+ element->qh_rlink = head;
+ head->qh_link = element;
+ element->qh_link->qh_rlink = element;
+}
+
+static __inline void
+remque(void *a)
+{
+ struct quehead *element = (struct quehead *)a;
+
+ element->qh_link->qh_rlink = element->qh_rlink;
+ element->qh_rlink->qh_link = element->qh_link;
+ element->qh_rlink = 0;
+}
+
+#else /* !__CC_SUPPORTS___INLINE */
+
+void insque(void *a, void *b);
+void remque(void *a);
+
+#endif /* __CC_SUPPORTS___INLINE */
+
+#endif /* _KERNEL */
+
+#endif /* !_SYS_QUEUE_H_ */
diff --git a/src/VBox/Devices/Network/slirp/resolv_conf_parser.c b/src/VBox/Devices/Network/slirp/resolv_conf_parser.c
new file mode 100644
index 00000000..7e89ac0e
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/resolv_conf_parser.c
@@ -0,0 +1,521 @@
+/* $Id: resolv_conf_parser.c $ */
+/** @file
+ * resolv_conf_parser.c - parser of resolv.conf resolver(5)
+ */
+
+/*
+ * Copyright (C) 2016-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifdef RCP_STANDALONE
+#define IN_RING3
+#endif
+
+#ifndef LOG_GROUP
+# define LOG_GROUP LOG_GROUP_DRV_NAT
+#endif
+
+#include <iprt/assert.h>
+#include <iprt/err.h>
+#include <iprt/net.h>
+#include <iprt/string.h>
+#include <iprt/stream.h>
+#include <iprt/thread.h>
+
+#include <VBox/log.h>
+
+#ifdef RT_OS_FREEBSD
+# include <sys/socket.h>
+#endif
+
+#include <arpa/inet.h>
+
+#include "resolv_conf_parser.h"
+
+#if !defined(RCP_ACCEPT_PORT)
+# if defined(RT_OS_DARWIN)
+# define RCP_ACCEPT_PORT
+# endif
+#endif
+
+static int rcp_address_trailer(char **ppszNext, PRTNETADDR pNetAddr, RTNETADDRTYPE enmType);
+static char *getToken(char *psz, char **ppszSavePtr);
+
+#if 0
+#undef Log2
+#define Log2 LogRel
+#endif
+
+#ifdef RCP_STANDALONE
+#undef LogRel
+#define LogRel(a) RTPrintf a
+#endif
+
+
+#ifdef RCP_STANDALONE
+int main(int argc, char **argv)
+{
+ struct rcp_state state;
+ int i;
+ int rc;
+
+ rc = rcp_parse(&state, NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf(">>> Failed: %Rrc\n", rc);
+ return 1;
+ }
+
+ RTPrintf(">>> Success:\n");
+
+ RTPrintf("rcps_num_nameserver = %u\n", state.rcps_num_nameserver);
+ for (i = 0; i < state.rcps_num_nameserver; ++i)
+ {
+ if (state.rcps_str_nameserver[i] == NULL)
+ LogRel((" nameserver %RTnaddr\n",
+ &state.rcps_nameserver[i]));
+ else
+ LogRel((" nameserver %RTnaddr (from \"%s\")\n",
+ &state.rcps_nameserver[i], state.rcps_str_nameserver[i]));
+ }
+
+ if (state.rcps_domain != NULL)
+ RTPrintf("domain %s\n", state.rcps_domain);
+
+ RTPrintf("rcps_num_searchlist = %u\n", state.rcps_num_searchlist);
+ for (i = 0; i < state.rcps_num_searchlist; ++i)
+ {
+ RTPrintf("... %s\n", state.rcps_searchlist[i] ? state.rcps_searchlist[i] : "(null)");
+ }
+
+ return 0;
+}
+#endif
+
+
+int rcp_parse(struct rcp_state *state, const char *filename)
+{
+ PRTSTREAM stream;
+# define RCP_BUFFER_SIZE 256
+ char buf[RCP_BUFFER_SIZE];
+ char *pszAddrBuf;
+ size_t cbAddrBuf;
+ char *pszSearchBuf;
+ size_t cbSearchBuf;
+ uint32_t flags;
+#ifdef RCP_ACCEPT_PORT /* OS X extention */
+ uint32_t default_port = RTNETADDR_PORT_NA;
+#endif
+ unsigned i;
+ int rc;
+
+ AssertPtrReturn(state, VERR_INVALID_PARAMETER);
+ flags = state->rcps_flags;
+
+ RT_ZERO(*state);
+ state->rcps_flags = flags;
+
+ if (RT_UNLIKELY(filename == NULL))
+ {
+#ifdef RCP_STANDALONE
+ stream = g_pStdIn; /* for testing/debugging */
+#else
+ return VERR_INVALID_PARAMETER;
+#endif
+ }
+ else
+ {
+ rc = RTStrmOpen(filename, "r", &stream);
+ if (RT_FAILURE(rc))
+ return rc;
+ }
+
+
+ pszAddrBuf = state->rcps_nameserver_str_buffer;
+ cbAddrBuf = sizeof(state->rcps_nameserver_str_buffer);
+
+ pszSearchBuf = state->rcps_searchlist_buffer;
+ cbSearchBuf = sizeof(state->rcps_searchlist_buffer);
+
+ for (;;)
+ {
+ char *s, *tok;
+
+ rc = RTStrmGetLine(stream, buf, sizeof(buf));
+ if (RT_FAILURE(rc))
+ {
+ if (rc == VERR_EOF)
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ /*
+ * Strip comment if present.
+ *
+ * This is not how ad-hoc parser in bind's res_init.c does it,
+ * btw, so this code will accept more input as valid compared
+ * to res_init. (e.g. "nameserver 1.1.1.1; comment" is
+ * misparsed by res_init).
+ */
+ for (s = buf; *s != '\0'; ++s)
+ {
+ if (*s == '#' || *s == ';')
+ {
+ *s = '\0';
+ break;
+ }
+ }
+
+ tok = getToken(buf, &s);
+ if (tok == NULL)
+ continue;
+
+
+ /*
+ * NAMESERVER
+ */
+ if (RTStrCmp(tok, "nameserver") == 0)
+ {
+ RTNETADDR NetAddr;
+ const char *pszAddr;
+ char *pszNext;
+
+ if (RT_UNLIKELY(state->rcps_num_nameserver >= RCPS_MAX_NAMESERVERS))
+ {
+ LogRel(("NAT: resolv.conf: too many nameserver lines, ignoring %s\n", s));
+ continue;
+ }
+
+ /* XXX: TODO: don't save strings unless asked to */
+ if (RT_UNLIKELY(cbAddrBuf == 0))
+ {
+ LogRel(("NAT: resolv.conf: no buffer space, ignoring %s\n", s));
+ continue;
+ }
+
+
+ /*
+ * parse next token as an IP address
+ */
+ tok = getToken(NULL, &s);
+ if (tok == NULL)
+ {
+ LogRel(("NAT: resolv.conf: nameserver line without value\n"));
+ continue;
+ }
+
+ pszAddr = tok;
+ RT_ZERO(NetAddr);
+ NetAddr.uPort = RTNETADDR_PORT_NA;
+
+ /* if (NetAddr.enmType == RTNETADDRTYPE_INVALID) */
+ {
+ rc = RTNetStrToIPv4AddrEx(tok, &NetAddr.uAddr.IPv4, &pszNext);
+ if (RT_SUCCESS(rc))
+ {
+ rc = rcp_address_trailer(&pszNext, &NetAddr, RTNETADDRTYPE_IPV4);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("NAT: resolv.conf: garbage at the end of IPv4 address %s\n", tok));
+ continue;
+ }
+
+ LogRel(("NAT: resolv.conf: nameserver %RTnaddr\n", &NetAddr));
+ }
+ } /* IPv4 */
+
+ if (NetAddr.enmType == RTNETADDRTYPE_INVALID)
+ {
+ rc = RTNetStrToIPv6AddrEx(tok, &NetAddr.uAddr.IPv6, &pszNext);
+ if (RT_SUCCESS(rc))
+ {
+ if (*pszNext == '%') /* XXX: TODO: IPv6 zones */
+ {
+ size_t zlen = RTStrOffCharOrTerm(pszNext, '.');
+ LogRel(("NAT: resolv.conf: FIXME: ignoring IPv6 zone %*.*s\n",
+ zlen, zlen, pszNext));
+ pszNext += zlen;
+ }
+
+ rc = rcp_address_trailer(&pszNext, &NetAddr, RTNETADDRTYPE_IPV6);
+ if (RT_FAILURE(rc))
+ {
+ LogRel(("NAT: resolv.conf: garbage at the end of IPv6 address %s\n", tok));
+ continue;
+ }
+
+ LogRel(("NAT: resolv.conf: nameserver %RTnaddr\n", &NetAddr));
+ }
+ } /* IPv6 */
+
+ if (NetAddr.enmType == RTNETADDRTYPE_INVALID)
+ {
+ LogRel(("NAT: resolv.conf: bad nameserver address %s\n", tok));
+ continue;
+ }
+
+
+ tok = getToken(NULL, &s);
+ if (tok != NULL)
+ LogRel(("NAT: resolv.conf: ignoring unexpected trailer on the nameserver line\n"));
+
+ if ((flags & RCPSF_IGNORE_IPV6) && NetAddr.enmType == RTNETADDRTYPE_IPV6)
+ {
+ Log2(("NAT: resolv.conf: IPv6 address ignored\n"));
+ continue;
+ }
+
+ /* seems ok, save it */
+ {
+ i = state->rcps_num_nameserver;
+
+ state->rcps_nameserver[i] = NetAddr;
+
+ /* XXX: TODO: don't save strings unless asked to */
+ Log2(("NAT: resolv.conf: saving address @%td,+%zu\n",
+ pszAddrBuf - state->rcps_nameserver_str_buffer, cbAddrBuf));
+ state->rcps_str_nameserver[i] = pszAddrBuf;
+ rc = RTStrCopyP(&pszAddrBuf, &cbAddrBuf, pszAddr);
+ if (RT_SUCCESS(rc))
+ {
+ ++pszAddrBuf; /* skip '\0' */
+ if (cbAddrBuf > 0) /* on overflow we get 1 (for the '\0'), but be defensive */
+ --cbAddrBuf;
+ ++state->rcps_num_nameserver;
+ }
+ else
+ {
+ Log2(("NAT: resolv.conf: ... truncated\n"));
+ }
+ }
+
+ continue;
+ }
+
+
+#ifdef RCP_ACCEPT_PORT /* OS X extention */
+ /*
+ * PORT
+ */
+ if (RTStrCmp(tok, "port") == 0)
+ {
+ uint16_t port;
+
+ if (default_port != RTNETADDR_PORT_NA)
+ {
+ LogRel(("NAT: resolv.conf: ignoring multiple port lines\n"));
+ continue;
+ }
+
+ tok = getToken(NULL, &s);
+ if (tok == NULL)
+ {
+ LogRel(("NAT: resolv.conf: port line without value\n"));
+ continue;
+ }
+
+ rc = RTStrToUInt16Full(tok, 10, &port);
+ if (RT_SUCCESS(rc))
+ {
+ if (port != 0)
+ default_port = port;
+ else
+ LogRel(("NAT: resolv.conf: port 0 is invalid\n"));
+ }
+
+ continue;
+ }
+#endif
+
+
+ /*
+ * DOMAIN
+ */
+ if (RTStrCmp(tok, "domain") == 0)
+ {
+ if (state->rcps_domain != NULL)
+ {
+ LogRel(("NAT: resolv.conf: ignoring multiple domain lines\n"));
+ continue;
+ }
+
+ tok = getToken(NULL, &s);
+ if (tok == NULL)
+ {
+ LogRel(("NAT: resolv.conf: domain line without value\n"));
+ continue;
+ }
+
+ rc = RTStrCopy(state->rcps_domain_buffer, sizeof(state->rcps_domain_buffer), tok);
+ if (RT_SUCCESS(rc))
+ {
+ state->rcps_domain = state->rcps_domain_buffer;
+ }
+ else
+ {
+ LogRel(("NAT: resolv.conf: domain name too long\n"));
+ RT_ZERO(state->rcps_domain_buffer);
+ }
+
+ continue;
+ }
+
+
+ /*
+ * SEARCH
+ */
+ if (RTStrCmp(tok, "search") == 0)
+ {
+ while ((tok = getToken(NULL, &s)) && tok != NULL)
+ {
+ i = state->rcps_num_searchlist;
+ if (RT_UNLIKELY(i >= RCPS_MAX_SEARCHLIST))
+ {
+ LogRel(("NAT: resolv.conf: too many search domains, ignoring %s\n", tok));
+ continue;
+ }
+
+ Log2(("NAT: resolv.conf: saving search %s @%td,+%zu\n",
+ tok, pszSearchBuf - state->rcps_searchlist_buffer, cbSearchBuf));
+ state->rcps_searchlist[i] = pszSearchBuf;
+ rc = RTStrCopyP(&pszSearchBuf, &cbSearchBuf, tok);
+ if (RT_SUCCESS(rc))
+ {
+ ++pszSearchBuf; /* skip '\0' */
+ if (cbSearchBuf > 0) /* on overflow we get 1 (for the '\0'), but be defensive */
+ --cbSearchBuf;
+ ++state->rcps_num_searchlist;
+ }
+ else
+ {
+ LogRel(("NAT: resolv.conf: no buffer space, ignoring search domain %s\n", tok));
+ pszSearchBuf = state->rcps_searchlist[i];
+ cbSearchBuf = sizeof(state->rcps_searchlist_buffer)
+ - (pszSearchBuf - state->rcps_searchlist_buffer);
+ Log2(("NAT: resolv.conf: backtracking to @%td,+%zu\n",
+ pszSearchBuf - state->rcps_searchlist_buffer, cbSearchBuf));
+ }
+ }
+
+ continue;
+ }
+
+
+ LogRel(("NAT: resolv.conf: ignoring \"%s %s\"\n", tok, s));
+ }
+
+ if (filename != NULL)
+ RTStrmClose(stream);
+
+ if (RT_FAILURE(rc))
+ return rc;
+
+
+ /* XXX: I don't like that OS X would return a different result here */
+#ifdef RCP_ACCEPT_PORT /* OS X extention */
+ if (default_port == RTNETADDR_PORT_NA)
+ default_port = 53;
+
+ for (i = 0; i < state->rcps_num_nameserver; ++i)
+ {
+ RTNETADDR *addr = &state->rcps_nameserver[i];
+ if (addr->uPort == RTNETADDR_PORT_NA || addr->uPort == 0)
+ addr->uPort = (uint16_t)default_port;
+ }
+#endif
+
+ if ( state->rcps_domain == NULL
+ && state->rcps_num_searchlist > 0)
+ {
+ state->rcps_domain = state->rcps_searchlist[0];
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+static int
+rcp_address_trailer(char **ppszNext, PRTNETADDR pNetAddr, RTNETADDRTYPE enmType)
+{
+ char *pszNext = *ppszNext;
+ int rc = VINF_SUCCESS;
+
+ if (*pszNext == '\0')
+ {
+ pNetAddr->enmType = enmType;
+ rc = VINF_SUCCESS;
+ }
+#ifdef RCP_ACCEPT_PORT /* OS X extention */
+ else if (*pszNext == '.')
+ {
+ uint16_t port;
+
+ rc = RTStrToUInt16Ex(++pszNext, NULL, 10, &port);
+ if (RT_SUCCESS(rc))
+ {
+ pNetAddr->enmType = enmType;
+ pNetAddr->uPort = port;
+ }
+ }
+#endif
+ else
+ {
+ rc = VERR_TRAILING_CHARS;
+ }
+
+ return rc;
+}
+
+
+static char *getToken(char *psz, char **ppszSavePtr)
+{
+ char *pszToken;
+
+ AssertPtrReturn(ppszSavePtr, NULL);
+
+ if (psz == NULL)
+ {
+ psz = *ppszSavePtr;
+ if (psz == NULL)
+ return NULL;
+ }
+
+ while (*psz == ' ' || *psz == '\t')
+ ++psz;
+
+ if (*psz == '\0')
+ {
+ *ppszSavePtr = NULL;
+ return NULL;
+ }
+
+ pszToken = psz;
+ while (*psz && *psz != ' ' && *psz != '\t')
+ ++psz;
+
+ if (*psz == '\0')
+ psz = NULL;
+ else
+ *psz++ = '\0';
+
+ *ppszSavePtr = psz;
+ return pszToken;
+}
diff --git a/src/VBox/Devices/Network/slirp/resolv_conf_parser.h b/src/VBox/Devices/Network/slirp/resolv_conf_parser.h
new file mode 100644
index 00000000..d486be02
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/resolv_conf_parser.h
@@ -0,0 +1,135 @@
+/* $Id: resolv_conf_parser.h $ */
+/** @file
+ * resolv_conf_parser.h - interface to parser of resolv.conf resolver(5)
+ */
+
+/*
+ * Copyright (C) 2014-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef __RESOLV_CONF_PARSER_H__
+#define __RESOLV_CONF_PARSER_H__
+
+#include <iprt/cdefs.h>
+#include <iprt/net.h>
+
+RT_C_DECLS_BEGIN
+
+#define RCPS_MAX_NAMESERVERS 3
+#define RCPS_MAX_SEARCHLIST 10
+#define RCPS_BUFFER_SIZE 256
+#define RCPS_IPVX_SIZE 47
+
+/**
+ * RESOLV_CONF_FILE can be defined in external tests for verification of Slirp behaviour.
+ */
+#ifndef RESOLV_CONF_FILE
+# ifndef RT_OS_OS2
+# define RESOLV_CONF_FILE "/etc/resolv.conf"
+# else
+# define RESOLV_CONF_FILE "\\MPTN\\ETC\\RESOLV2"
+# endif
+#endif
+
+/**
+ * In Slirp we don't need IPv6 for general case (only for dnsproxy mode
+ * it's potentially acceptable)
+ */
+#define RCPSF_IGNORE_IPV6 RT_BIT(0)
+
+/**
+ * This flag used to request just the strings in rcps_str_nameserver,
+ * but no addresses in rcps_nameserver. This is not very useful,
+ * since we need to validate addresses anyway. This flag is ignored
+ * now.
+ */
+#define RCPSF_NO_STR2IPCONV RT_BIT(1)
+
+
+struct rcp_state
+{
+ uint16_t rcps_port;
+ /**
+ * Filling of this array ommited iff RCPSF_NO_STR2IPCONF in rcp_state::rcps_flags set.
+ */
+ RTNETADDR rcps_nameserver[RCPS_MAX_NAMESERVERS];
+ /**
+ * this array contains non-NULL (pointing to rcp_state::rcps_nameserver_str_buffer) iff
+ * RCPSF_NO_STR2IPCONF in rcp_state::rcps_flags set.
+ */
+ char *rcps_str_nameserver[RCPS_MAX_NAMESERVERS];
+ unsigned rcps_num_nameserver;
+ /**
+ * Shortcuts to storage, note that domain is optional
+ * and if it's missed in resolv.conf rcps_domain should be equal
+ * to rcps_search_list[0]
+ */
+ char *rcps_domain;
+ char *rcps_searchlist[RCPS_MAX_SEARCHLIST];
+ unsigned rcps_num_searchlist;
+
+ uint32_t rcps_flags;
+
+ char rcps_domain_buffer[RCPS_BUFFER_SIZE];
+ char rcps_searchlist_buffer[RCPS_BUFFER_SIZE];
+ char rcps_nameserver_str_buffer[RCPS_MAX_NAMESERVERS * RCPS_IPVX_SIZE];
+};
+
+
+/**
+ * This function parses specified file (expected to conform resolver (5) Mac OSX or resolv.conf (3) Linux)
+ * and fills the structure.
+ * @return 0 - on success
+ * -1 - on fail.
+ * <code>
+ * struct rcp_state state;
+ * int rc;
+ *
+ * rc = rcp_parse(&state, "/etc/resolv.conf");
+ * for(i = 0; rc == 0 && i != state.rcps_num_nameserver; ++i)
+ * {
+ * if ((state.rcps_flags & RCPSF_NO_STR2IPCONV) == 0)
+ * {
+ * const RTNETADDR *addr = &state.rcps_nameserver[i];
+ *
+ * switch (state.rcps_nameserver[i].enmType)
+ * {
+ * case RTNETADDRTYPE_IPV4:
+ * RTPrintf("nameserver[%d]: [%RTnaipv4]:%d\n", i, addr->uAddr.IPv4, addr->uPort);
+ * break;
+ * case RTNETADDRTYPE_IPV6:
+ * RTPrintf("nameserver[%d]: [%RTnaipv6]:%d\n", i, &addr->uAddr.IPv6, addr->uPort);
+ * break;
+ * default:
+ * break;
+ * }
+ * }
+ * else
+ * RTPrintf("nameserver[%d]: %s\n", i, state.rcps_str_nameserver[i]);
+ * }
+ * </code>
+ *
+ */
+int rcp_parse(struct rcp_state *, const char *);
+
+RT_C_DECLS_END
+
+#endif
diff --git a/src/VBox/Devices/Network/slirp/sbuf.c b/src/VBox/Devices/Network/slirp/sbuf.c
new file mode 100644
index 00000000..9b29a32c
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/sbuf.c
@@ -0,0 +1,292 @@
+/* $Id: sbuf.c $ */
+/** @file
+ * NAT - sbuf implemenation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+/* Done as a macro in socket.h */
+/* int
+ * sbspace(struct sockbuff *sb)
+ * {
+ * return SB_DATALEN - sb->sb_cc;
+ * }
+ */
+
+void
+sbfree(struct sbuf *sb)
+{
+ /*
+ * Catch double frees. Actually tcp_close() already filters out listening sockets
+ * passing NULL.
+ */
+ Assert((sb->sb_data));
+
+ /*
+ * Don't call RTMemFree() for an already freed buffer, the EFence could complain
+ */
+ if (sb->sb_data)
+ {
+ RTMemFree(sb->sb_data);
+ sb->sb_data = NULL;
+ }
+}
+
+void
+sbdrop(struct sbuf *sb, int num)
+{
+ /*
+ * We can only drop how much we have
+ * This should never succeed
+ */
+ if (num > sb->sb_cc)
+ num = sb->sb_cc;
+ sb->sb_cc -= num;
+ sb->sb_rptr += num;
+ if (sb->sb_rptr >= sb->sb_data + sb->sb_datalen)
+ sb->sb_rptr -= sb->sb_datalen;
+
+}
+
+void
+sbreserve(PNATState pData, struct sbuf *sb, int size)
+{
+ NOREF(pData);
+ if (sb->sb_data)
+ {
+ /* Already alloced, realloc if necessary */
+ if (sb->sb_datalen != (u_int)size)
+ {
+ sb->sb_wptr =
+ sb->sb_rptr =
+ sb->sb_data = (char *)RTMemReallocZ(sb->sb_data, sb->sb_datalen, size);
+ sb->sb_cc = 0;
+ if (sb->sb_wptr)
+ sb->sb_datalen = size;
+ else
+ sb->sb_datalen = 0;
+ }
+ }
+ else
+ {
+ sb->sb_wptr = sb->sb_rptr = sb->sb_data = (char *)RTMemAllocZ(size);
+ sb->sb_cc = 0;
+ if (sb->sb_wptr)
+ sb->sb_datalen = size;
+ else
+ sb->sb_datalen = 0;
+ }
+}
+
+/*
+ * Try and write() to the socket, whatever doesn't get written
+ * append to the buffer... for a host with a fast net connection,
+ * this prevents an unnecessary copy of the data
+ * (the socket is non-blocking, so we won't hang)
+ */
+void
+sbappend(PNATState pData, struct socket *so, struct mbuf *m)
+{
+ int ret = 0;
+ int mlen = 0;
+
+ STAM_PROFILE_START(&pData->StatIOSBAppend_pf, a);
+ LogFlow(("sbappend: so = %p, m = %p, m->m_len = %d\n", so, m, m ? m->m_len : 0));
+
+ STAM_COUNTER_INC(&pData->StatIOSBAppend);
+ /* Shouldn't happen, but... e.g. foreign host closes connection */
+ mlen = m_length(m, NULL);
+ if (mlen <= 0)
+ {
+ STAM_COUNTER_INC(&pData->StatIOSBAppend_zm);
+ goto done;
+ }
+
+ /*
+ * If there is urgent data, call sosendoob
+ * if not all was sent, sowrite will take care of the rest
+ * (The rest of this function is just an optimisation)
+ */
+ if (so->so_urgc)
+ {
+ sbappendsb(pData, &so->so_rcv, m);
+ m_freem(pData, m);
+ sosendoob(so);
+ return;
+ }
+
+ /*
+ * We only write if there's nothing in the buffer,
+ * otherwise it'll arrive out of order, and hence corrupt
+ */
+ if (so->so_rcv.sb_cc == 0)
+ {
+ caddr_t buf = NULL;
+
+ if (m->m_next)
+ {
+ buf = RTMemAllocZ(mlen);
+ if (buf == NULL)
+ {
+ ret = 0;
+ goto no_sent;
+ }
+ m_copydata(m, 0, mlen, buf);
+ }
+ else
+ buf = mtod(m, char *);
+
+ ret = send(so->s, buf, mlen, 0);
+
+ if (m->m_next)
+ RTMemFree(buf);
+ }
+no_sent:
+
+ if (ret <= 0)
+ {
+ STAM_COUNTER_INC(&pData->StatIOSBAppend_wf);
+ /*
+ * Nothing was written
+ * It's possible that the socket has closed, but
+ * we don't need to check because if it has closed,
+ * it will be detected in the normal way by soread()
+ */
+ sbappendsb(pData, &so->so_rcv, m);
+ STAM_PROFILE_STOP(&pData->StatIOSBAppend_pf_wf, a);
+ goto done;
+ }
+ else if (ret != mlen)
+ {
+ STAM_COUNTER_INC(&pData->StatIOSBAppend_wp);
+ /*
+ * Something was written, but not everything..
+ * sbappendsb the rest
+ */
+ m_adj(m, ret);
+ sbappendsb(pData, &so->so_rcv, m);
+ STAM_PROFILE_STOP(&pData->StatIOSBAppend_pf_wp, a);
+ goto done;
+ } /* else */
+ /* Whatever happened, we free the mbuf */
+ STAM_COUNTER_INC(&pData->StatIOSBAppend_wa);
+ STAM_PROFILE_STOP(&pData->StatIOSBAppend_pf_wa, a);
+done:
+ m_freem(pData, m);
+}
+
+/*
+ * Copy the data from m into sb
+ * The caller is responsible to make sure there's enough room
+ */
+void
+sbappendsb(PNATState pData, struct sbuf *sb, struct mbuf *m)
+{
+ int len, n, nn;
+#ifndef VBOX_WITH_STATISTICS
+ NOREF(pData);
+#endif
+
+ len = m_length(m, NULL);
+
+ STAM_COUNTER_INC(&pData->StatIOSBAppendSB);
+ if (sb->sb_wptr < sb->sb_rptr)
+ {
+ STAM_COUNTER_INC(&pData->StatIOSBAppendSB_w_l_r);
+ n = sb->sb_rptr - sb->sb_wptr;
+ if (n > len)
+ n = len;
+ m_copydata(m, 0, n, sb->sb_wptr);
+ }
+ else
+ {
+ STAM_COUNTER_INC(&pData->StatIOSBAppendSB_w_ge_r);
+ /* Do the right edge first */
+ n = sb->sb_data + sb->sb_datalen - sb->sb_wptr;
+ if (n > len)
+ n = len;
+ m_copydata(m, 0, n, sb->sb_wptr);
+ len -= n;
+ if (len)
+ {
+ /* Now the left edge */
+ nn = sb->sb_rptr - sb->sb_data;
+ if (nn > len)
+ nn = len;
+ m_copydata(m, n, nn, sb->sb_data);
+ n += nn;
+ }
+ }
+
+ sb->sb_cc += n;
+ sb->sb_wptr += n;
+ if (sb->sb_wptr >= sb->sb_data + sb->sb_datalen)
+ {
+ STAM_COUNTER_INC(&pData->StatIOSBAppendSB_w_alter);
+ sb->sb_wptr -= sb->sb_datalen;
+ }
+}
+
+/*
+ * Copy data from sbuf to a normal, straight buffer
+ * Don't update the sbuf rptr, this will be
+ * done in sbdrop when the data is acked
+ */
+void
+sbcopy(struct sbuf *sb, int off, int len, char *to)
+{
+ char *from;
+
+ from = sb->sb_rptr + off;
+ if (from >= sb->sb_data + sb->sb_datalen)
+ from -= sb->sb_datalen;
+
+ if (from < sb->sb_wptr)
+ {
+ if (len > sb->sb_cc)
+ len = sb->sb_cc;
+ memcpy(to, from, len);
+ }
+ else
+ {
+ /* re-use off */
+ off = (sb->sb_data + sb->sb_datalen) - from;
+ if (off > len)
+ off = len;
+ memcpy(to, from, off);
+ len -= off;
+ if (len)
+ memcpy(to+off, sb->sb_data, len);
+ }
+}
diff --git a/src/VBox/Devices/Network/slirp/sbuf.h b/src/VBox/Devices/Network/slirp/sbuf.h
new file mode 100644
index 00000000..35983c0d
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/sbuf.h
@@ -0,0 +1,63 @@
+/* $Id: sbuf.h $ */
+/** @file
+ * NAT - sbuf declarations/defines.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#ifndef _SBUF_H_
+#define _SBUF_H_
+
+# define sbflush(sb) sbdrop((sb),(sb)->sb_cc)
+# define sbspace(sb) ((sb)->sb_datalen - (sb)->sb_cc)
+# define SBUF_LEN(sb) ((sb)->sb_cc)
+# define SBUF_SIZE(sb) ((sb)->sb_datalen)
+
+
+struct sbuf
+{
+ u_int sb_cc; /* actual chars in buffer */
+ u_int sb_datalen; /* Length of data */
+ char *sb_wptr; /* write pointer. points to where the next
+ * bytes should be written in the sbuf */
+ char *sb_rptr; /* read pointer. points to where the next
+ * byte should be read from the sbuf */
+ char *sb_data; /* Actual data */
+};
+
+void sbfree (struct sbuf *);
+void sbdrop (struct sbuf *, int);
+void sbreserve (PNATState, struct sbuf *, int);
+void sbappend (PNATState, struct socket *, struct mbuf *);
+void sbappendsb (PNATState, struct sbuf *, struct mbuf *);
+void sbcopy (struct sbuf *, int, int, char *);
+#endif
diff --git a/src/VBox/Devices/Network/slirp/slirp.c b/src/VBox/Devices/Network/slirp/slirp.c
new file mode 100644
index 00000000..37c53292
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/slirp.c
@@ -0,0 +1,2086 @@
+/* $Id: slirp.c $ */
+/** @file
+ * NAT - slirp glue.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * libslirp glue
+ *
+ * Copyright (c) 2004-2008 Fabrice Bellard
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include "slirp.h"
+#ifdef RT_OS_OS2
+# include <paths.h>
+#endif
+
+#include <iprt/errcore.h>
+#include <VBox/vmm/dbgf.h>
+#include <VBox/vmm/pdmdrv.h>
+#include <iprt/assert.h>
+#include <iprt/file.h>
+#include <iprt/path.h>
+#ifndef RT_OS_WINDOWS
+# include <sys/ioctl.h>
+# include <poll.h>
+# include <netinet/in.h>
+#else
+# include <Winnls.h>
+# define _WINSOCK2API_
+# include <iprt/win/iphlpapi.h>
+#endif
+#include <alias.h>
+
+#ifndef RT_OS_WINDOWS
+/**
+ * XXX: It shouldn't be non-Windows specific.
+ * resolv_conf_parser.h client's structure isn't OS specific, it's just need to be generalized a
+ * a bit to replace slirp_state.h DNS server (domain) lists with rcp_state like structure.
+ */
+# include "resolv_conf_parser.h"
+#endif
+
+#ifndef RT_OS_WINDOWS
+# define DO_ENGAGE_EVENT1(so, fdset, label) \
+ do { \
+ if ( so->so_poll_index != -1 \
+ && so->s == polls[so->so_poll_index].fd) \
+ { \
+ polls[so->so_poll_index].events |= N_(fdset ## _poll); \
+ break; \
+ } \
+ AssertRelease(poll_index < (nfds)); \
+ AssertRelease(poll_index >= 0 && poll_index < (nfds)); \
+ polls[poll_index].fd = (so)->s; \
+ (so)->so_poll_index = poll_index; \
+ polls[poll_index].events = N_(fdset ## _poll); \
+ polls[poll_index].revents = 0; \
+ poll_index++; \
+ } while (0)
+
+# define DO_ENGAGE_EVENT2(so, fdset1, fdset2, label) \
+ do { \
+ if ( so->so_poll_index != -1 \
+ && so->s == polls[so->so_poll_index].fd) \
+ { \
+ polls[so->so_poll_index].events |= \
+ N_(fdset1 ## _poll) | N_(fdset2 ## _poll); \
+ break; \
+ } \
+ AssertRelease(poll_index < (nfds)); \
+ polls[poll_index].fd = (so)->s; \
+ (so)->so_poll_index = poll_index; \
+ polls[poll_index].events = \
+ N_(fdset1 ## _poll) | N_(fdset2 ## _poll); \
+ poll_index++; \
+ } while (0)
+
+# define DO_POLL_EVENTS(rc, error, so, events, label) do {} while (0)
+
+/*
+ * DO_CHECK_FD_SET is used in dumping events on socket, including POLLNVAL.
+ * gcc warns about attempts to log POLLNVAL so construction in a last to lines
+ * used to catch POLLNVAL while logging and return false in case of error while
+ * normal usage.
+ */
+# define DO_CHECK_FD_SET(so, events, fdset) \
+ ( ((so)->so_poll_index != -1) \
+ && ((so)->so_poll_index <= ndfs) \
+ && ((so)->s == polls[so->so_poll_index].fd) \
+ && (polls[(so)->so_poll_index].revents & N_(fdset ## _poll)) \
+ && ( N_(fdset ## _poll) == POLLNVAL \
+ || !(polls[(so)->so_poll_index].revents & POLLNVAL)))
+
+ /* specific for Windows Winsock API */
+# define DO_WIN_CHECK_FD_SET(so, events, fdset) 0
+
+# ifndef RT_OS_LINUX
+# define readfds_poll (POLLRDNORM)
+# define writefds_poll (POLLWRNORM)
+# else
+# define readfds_poll (POLLIN)
+# define writefds_poll (POLLOUT)
+# endif
+# define xfds_poll (POLLPRI)
+# define closefds_poll (POLLHUP)
+# define rderr_poll (POLLERR)
+# if 0 /* unused yet */
+# define rdhup_poll (POLLHUP)
+# define nval_poll (POLLNVAL)
+# endif
+
+# define ICMP_ENGAGE_EVENT(so, fdset) \
+ do { \
+ if (pData->icmp_socket.s != -1) \
+ DO_ENGAGE_EVENT1((so), fdset, ICMP); \
+ } while (0)
+
+#else /* RT_OS_WINDOWS */
+
+/*
+ * On Windows, we will be notified by IcmpSendEcho2() when the response arrives.
+ * So no call to WSAEventSelect necessary.
+ */
+# define ICMP_ENGAGE_EVENT(so, fdset) do {} while (0)
+
+/*
+ * On Windows we use FD_ALL_EVENTS to ensure that we don't miss any event.
+ */
+# define DO_ENGAGE_EVENT1(so, fdset1, label) \
+ do { \
+ rc = WSAEventSelect((so)->s, VBOX_SOCKET_EVENT, FD_ALL_EVENTS); \
+ if (rc == SOCKET_ERROR) \
+ { \
+ /* This should not happen */ \
+ error = WSAGetLastError(); \
+ LogRel(("WSAEventSelect (" #label ") error %d (so=%x, socket=%s, event=%x)\n", \
+ error, (so), (so)->s, VBOX_SOCKET_EVENT)); \
+ } \
+ } while (0); \
+ CONTINUE(label)
+
+# define DO_ENGAGE_EVENT2(so, fdset1, fdset2, label) \
+ DO_ENGAGE_EVENT1((so), (fdset1), label)
+
+# define DO_POLL_EVENTS(rc, error, so, events, label) \
+ (rc) = WSAEnumNetworkEvents((so)->s, VBOX_SOCKET_EVENT, (events)); \
+ if ((rc) == SOCKET_ERROR) \
+ { \
+ (error) = WSAGetLastError(); \
+ LogRel(("WSAEnumNetworkEvents %R[natsock] " #label " error %d\n", (so), (error))); \
+ LogFunc(("WSAEnumNetworkEvents %R[natsock] " #label " error %d\n", (so), (error))); \
+ CONTINUE(label); \
+ }
+
+# define acceptds_win FD_ACCEPT
+# define acceptds_win_bit FD_ACCEPT_BIT
+# define readfds_win FD_READ
+# define readfds_win_bit FD_READ_BIT
+# define writefds_win FD_WRITE
+# define writefds_win_bit FD_WRITE_BIT
+# define xfds_win FD_OOB
+# define xfds_win_bit FD_OOB_BIT
+# define closefds_win FD_CLOSE
+# define closefds_win_bit FD_CLOSE_BIT
+# define connectfds_win FD_CONNECT
+# define connectfds_win_bit FD_CONNECT_BIT
+
+# define closefds_win FD_CLOSE
+# define closefds_win_bit FD_CLOSE_BIT
+
+# define DO_CHECK_FD_SET(so, events, fdset) \
+ ((events).lNetworkEvents & fdset ## _win)
+
+# define DO_WIN_CHECK_FD_SET(so, events, fdset) DO_CHECK_FD_SET((so), (events), fdset)
+# define DO_UNIX_CHECK_FD_SET(so, events, fdset) 1 /*specific for Unix API */
+
+#endif /* RT_OS_WINDOWS */
+
+#define TCP_ENGAGE_EVENT1(so, fdset) \
+ DO_ENGAGE_EVENT1((so), fdset, tcp)
+
+#define TCP_ENGAGE_EVENT2(so, fdset1, fdset2) \
+ DO_ENGAGE_EVENT2((so), fdset1, fdset2, tcp)
+
+#ifdef RT_OS_WINDOWS
+# define WIN_TCP_ENGAGE_EVENT2(so, fdset, fdset2) TCP_ENGAGE_EVENT2(so, fdset1, fdset2)
+#endif
+
+#define UDP_ENGAGE_EVENT(so, fdset) \
+ DO_ENGAGE_EVENT1((so), fdset, udp)
+
+#define POLL_TCP_EVENTS(rc, error, so, events) \
+ DO_POLL_EVENTS((rc), (error), (so), (events), tcp)
+
+#define POLL_UDP_EVENTS(rc, error, so, events) \
+ DO_POLL_EVENTS((rc), (error), (so), (events), udp)
+
+#define CHECK_FD_SET(so, events, set) \
+ (DO_CHECK_FD_SET((so), (events), set))
+
+#define WIN_CHECK_FD_SET(so, events, set) \
+ (DO_WIN_CHECK_FD_SET((so), (events), set))
+
+/*
+ * Loging macros
+ */
+#ifdef VBOX_WITH_DEBUG_NAT_SOCKETS
+# if defined(RT_OS_WINDOWS)
+# define DO_LOG_NAT_SOCK(so, proto, winevent, r_fdset, w_fdset, x_fdset) \
+ do { \
+ LogRel((" " #proto " %R[natsock] %R[natwinnetevents]\n", (so), (winevent))); \
+ } while (0)
+# else /* !RT_OS_WINDOWS */
+# define DO_LOG_NAT_SOCK(so, proto, winevent, r_fdset, w_fdset, x_fdset) \
+ do { \
+ LogRel((" " #proto " %R[natsock] %s %s %s er: %s, %s, %s\n", (so), \
+ CHECK_FD_SET(so, ign ,r_fdset) ? "READ":"", \
+ CHECK_FD_SET(so, ign, w_fdset) ? "WRITE":"", \
+ CHECK_FD_SET(so, ign, x_fdset) ? "OOB":"", \
+ CHECK_FD_SET(so, ign, rderr) ? "RDERR":"", \
+ CHECK_FD_SET(so, ign, rdhup) ? "RDHUP":"", \
+ CHECK_FD_SET(so, ign, nval) ? "RDNVAL":"")); \
+ } while (0)
+# endif /* !RT_OS_WINDOWS */
+#else /* !VBOX_WITH_DEBUG_NAT_SOCKETS */
+# define DO_LOG_NAT_SOCK(so, proto, winevent, r_fdset, w_fdset, x_fdset) do {} while (0)
+#endif /* !VBOX_WITH_DEBUG_NAT_SOCKETS */
+
+#define LOG_NAT_SOCK(so, proto, winevent, r_fdset, w_fdset, x_fdset) \
+ DO_LOG_NAT_SOCK((so), proto, (winevent), r_fdset, w_fdset, x_fdset)
+
+static const uint8_t special_ethaddr[6] =
+{
+ 0x52, 0x54, 0x00, 0x12, 0x35, 0x00
+};
+
+static const uint8_t broadcast_ethaddr[6] =
+{
+ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
+};
+
+const uint8_t zerro_ethaddr[6] =
+{
+ 0x0, 0x0, 0x0, 0x0, 0x0, 0x0
+};
+
+/**
+ * This helper routine do the checks in descriptions to
+ * ''fUnderPolling'' and ''fShouldBeRemoved'' flags
+ * @returns 1 if socket removed and 0 if no changes was made.
+ */
+static int slirpVerifyAndFreeSocket(PNATState pData, struct socket *pSocket)
+{
+ AssertPtrReturn(pData, 0);
+ AssertPtrReturn(pSocket, 0);
+ AssertReturn(pSocket->fUnderPolling, 0);
+ if (pSocket->fShouldBeRemoved)
+ {
+ pSocket->fUnderPolling = 0;
+ sofree(pData, pSocket);
+ /* pSocket is PHANTOM, now */
+ return 1;
+ }
+ return 0;
+}
+
+int slirp_init(PNATState *ppData, uint32_t u32NetAddr, uint32_t u32Netmask,
+ bool fPassDomain, bool fUseHostResolver, int i32AliasMode,
+ int iIcmpCacheLimit, bool fLocalhostReachable, void *pvUser)
+{
+ int rc;
+ PNATState pData;
+ if (u32Netmask & 0x1f)
+ {
+ /* CTL is x.x.x.15, bootp passes up to 16 IPs (15..31) */
+ LogRel(("NAT: The last 5 bits of the netmask (%RTnaipv4) need to be unset\n", RT_BE2H_U32(u32Netmask)));
+ return VERR_INVALID_PARAMETER;
+ }
+ pData = RTMemAllocZ(RT_ALIGN_Z(sizeof(NATState), sizeof(uint64_t)));
+ *ppData = pData;
+ if (!pData)
+ return VERR_NO_MEMORY;
+ pData->fPassDomain = !fUseHostResolver ? fPassDomain : false;
+ pData->fUseHostResolver = fUseHostResolver;
+ pData->fUseHostResolverPermanent = fUseHostResolver;
+ pData->fLocalhostReachable = fLocalhostReachable;
+ pData->pvUser = pvUser;
+ pData->netmask = u32Netmask;
+
+ rc = RTCritSectRwInit(&pData->CsRwHandlerChain);
+ if (RT_FAILURE(rc))
+ return rc;
+
+ /* sockets & TCP defaults */
+ pData->socket_rcv = 64 * _1K;
+ pData->socket_snd = 64 * _1K;
+ tcp_sndspace = 64 * _1K;
+ tcp_rcvspace = 64 * _1K;
+
+ /*
+ * Use the same default here as in DevNAT.cpp (SoMaxConnection CFGM value)
+ * to avoid release log noise.
+ */
+ pData->soMaxConn = 10;
+
+#ifdef RT_OS_WINDOWS
+ {
+ WSADATA Data;
+ RTLDRMOD hLdrMod;
+
+ WSAStartup(MAKEWORD(2, 0), &Data);
+
+ rc = RTLdrLoadSystem("Iphlpapi.dll", true /*fNoUnload*/, &hLdrMod);
+ if (RT_SUCCESS(rc))
+ {
+ rc = RTLdrGetSymbol(hLdrMod, "GetAdaptersAddresses", (void **)&pData->pfnGetAdaptersAddresses);
+ if (RT_FAILURE(rc))
+ LogRel(("NAT: Can't find GetAdapterAddresses in Iphlpapi.dll\n"));
+
+ RTLdrClose(hLdrMod);
+ }
+ }
+ pData->phEvents[VBOX_SOCKET_EVENT_INDEX] = CreateEvent(NULL, FALSE, FALSE, NULL);
+#endif
+
+ rc = bootp_dhcp_init(pData);
+ if (RT_FAILURE(rc))
+ {
+ Log(("NAT: DHCP server initialization failed\n"));
+ RTMemFree(pData);
+ *ppData = NULL;
+ return rc;
+ }
+ debug_init(pData);
+ if_init(pData);
+ ip_init(pData);
+ icmp_init(pData, iIcmpCacheLimit);
+
+ /* Initialise mbufs *after* setting the MTU */
+ mbuf_init(pData);
+
+ pData->special_addr.s_addr = u32NetAddr;
+ pData->slirp_ethaddr = &special_ethaddr[0];
+ alias_addr.s_addr = pData->special_addr.s_addr | RT_H2N_U32_C(CTL_ALIAS);
+ /** @todo add ability to configure this staff */
+
+ /*
+ * Some guests won't reacquire DHCP lease on link flap when VM is
+ * restored. Instead of forcing users to explicitly set CTL_GUEST
+ * in port-forwarding rules, provide it as initial guess here.
+ */
+ slirp_update_guest_addr_guess(pData,
+ pData->special_addr.s_addr | RT_H2N_U32_C(CTL_GUEST),
+ "initialization");
+
+ /* set default addresses */
+ inet_aton("127.0.0.1", &loopback_addr);
+
+ rc = slirpTftpInit(pData);
+ AssertRCReturn(rc, rc);
+
+ if (i32AliasMode & ~(PKT_ALIAS_LOG|PKT_ALIAS_SAME_PORTS|PKT_ALIAS_PROXY_ONLY))
+ {
+ LogRel(("NAT: bad alias mode 0x%x ignored\n", i32AliasMode));
+ i32AliasMode = 0;
+ }
+ else if (i32AliasMode != 0)
+ {
+ LogRel(("NAT: alias mode 0x%x\n", i32AliasMode));
+ }
+
+ pData->i32AliasMode = i32AliasMode;
+ getouraddr(pData);
+ {
+ int flags = 0;
+ struct in_addr proxy_addr;
+ pData->proxy_alias = LibAliasInit(pData, NULL);
+ if (pData->proxy_alias == NULL)
+ {
+ Log(("NAT: LibAlias default rule wasn't initialized\n"));
+ AssertMsgFailed(("NAT: LibAlias default rule wasn't initialized\n"));
+ }
+ flags = LibAliasSetMode(pData->proxy_alias, 0, 0);
+#ifndef NO_FW_PUNCH
+ flags |= PKT_ALIAS_PUNCH_FW;
+#endif
+ flags |= pData->i32AliasMode; /* do transparent proxying */
+ flags = LibAliasSetMode(pData->proxy_alias, flags, ~0U);
+ proxy_addr.s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_ALIAS);
+ LibAliasSetAddress(pData->proxy_alias, proxy_addr);
+ ftp_alias_load(pData);
+ nbt_alias_load(pData);
+ }
+#ifdef VBOX_WITH_NAT_SEND2HOME
+ /** @todo we should know all interfaces available on host. */
+ pData->pInSockAddrHomeAddress = RTMemAllocZ(sizeof(struct sockaddr));
+ pData->cInHomeAddressSize = 1;
+ inet_aton("192.168.1.25", &pData->pInSockAddrHomeAddress[0].sin_addr);
+ pData->pInSockAddrHomeAddress[0].sin_family = AF_INET;
+# ifdef RT_OS_DARWIN
+ pData->pInSockAddrHomeAddress[0].sin_len = sizeof(struct sockaddr_in);
+# endif
+#endif
+
+#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
+ STAILQ_INIT(&pData->DNSMapNames);
+ STAILQ_INIT(&pData->DNSMapPatterns);
+#endif
+
+ slirp_link_up(pData);
+ return VINF_SUCCESS;
+}
+
+/**
+ * Register statistics.
+ */
+void slirp_register_statistics(PNATState pData, PPDMDRVINS pDrvIns)
+{
+#ifdef VBOX_WITH_STATISTICS
+# define PROFILE_COUNTER(name, dsc) REGISTER_COUNTER(name, pData, STAMTYPE_PROFILE, STAMUNIT_TICKS_PER_CALL, dsc)
+# define COUNTING_COUNTER(name, dsc) REGISTER_COUNTER(name, pData, STAMTYPE_COUNTER, STAMUNIT_COUNT, dsc)
+# include "counters.h"
+# undef COUNTER
+/** @todo register statistics for the variables dumped by:
+ * ipstats(pData); tcpstats(pData); udpstats(pData); icmpstats(pData);
+ * mbufstats(pData); sockstats(pData); */
+#else /* VBOX_WITH_STATISTICS */
+ NOREF(pData);
+ NOREF(pDrvIns);
+#endif /* !VBOX_WITH_STATISTICS */
+}
+
+/**
+ * Deregister statistics.
+ */
+void slirp_deregister_statistics(PNATState pData, PPDMDRVINS pDrvIns)
+{
+ if (pData == NULL)
+ return;
+#ifdef VBOX_WITH_STATISTICS
+# define PROFILE_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pData)
+# define COUNTING_COUNTER(name, dsc) DEREGISTER_COUNTER(name, pData)
+# include "counters.h"
+#else /* VBOX_WITH_STATISTICS */
+ NOREF(pData);
+ NOREF(pDrvIns);
+#endif /* !VBOX_WITH_STATISTICS */
+}
+
+/**
+ * Marks the link as up, making it possible to establish new connections.
+ */
+void slirp_link_up(PNATState pData)
+{
+ if (link_up == 1)
+ return;
+
+ link_up = 1;
+
+ if (!pData->fUseHostResolverPermanent)
+ slirpInitializeDnsSettings(pData);
+}
+
+/**
+ * Marks the link as down and cleans up the current connections.
+ */
+void slirp_link_down(PNATState pData)
+{
+ if (link_up == 0)
+ return;
+
+ slirpReleaseDnsSettings(pData);
+
+ link_up = 0;
+}
+
+/**
+ * Terminates the slirp component.
+ */
+void slirp_term(PNATState pData)
+{
+ struct socket *so;
+
+ if (pData == NULL)
+ return;
+
+ icmp_finit(pData);
+
+ while ((so = tcb.so_next) != &tcb)
+ {
+ /* Don't miss TCB releasing */
+ if ( !sototcpcb(so)
+ && ( so->so_state & SS_NOFDREF
+ || so->s == -1))
+ sofree(pData, so);
+ else
+ tcp_close(pData, sototcpcb(so));
+ }
+
+ while ((so = udb.so_next) != &udb)
+ udp_detach(pData, so);
+
+ slirp_link_down(pData);
+ ftp_alias_unload(pData);
+ nbt_alias_unload(pData);
+
+#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
+ {
+ DNSMAPPINGHEAD *heads[2];
+ int i;
+
+ heads[0] = &pData->DNSMapNames;
+ heads[1] = &pData->DNSMapPatterns;
+ for (i = 0; i < RT_ELEMENTS(heads); ++i)
+ {
+ while (!STAILQ_EMPTY(heads[i]))
+ {
+ PDNSMAPPINGENTRY pDnsEntry = STAILQ_FIRST(heads[i]);
+ STAILQ_REMOVE_HEAD(heads[i], MapList);
+ RTStrFree(pDnsEntry->pszName);
+ RTMemFree(pDnsEntry);
+ }
+ }
+ }
+#endif
+
+ while (!LIST_EMPTY(&instancehead))
+ {
+ struct libalias *la = LIST_FIRST(&instancehead);
+ /* libalias do all clean up */
+ LibAliasUninit(la);
+ }
+ while (!LIST_EMPTY(&pData->arp_cache))
+ {
+ struct arp_cache_entry *ac = LIST_FIRST(&pData->arp_cache);
+ LIST_REMOVE(ac, list);
+ RTMemFree(ac);
+ }
+ while (!LIST_EMPTY(&pData->port_forward_rule_head))
+ {
+ struct port_forward_rule *rule = LIST_FIRST(&pData->port_forward_rule_head);
+ LIST_REMOVE(rule, list);
+ RTMemFree(rule);
+ }
+ slirpTftpTerm(pData);
+ bootp_dhcp_fini(pData);
+ m_fini(pData);
+#ifdef RT_OS_WINDOWS
+ WSACleanup();
+#endif
+ if (tftp_prefix)
+ RTStrFree((char *)tftp_prefix);
+#ifdef LOG_ENABLED
+ Log(("\n"
+ "NAT statistics\n"
+ "--------------\n"
+ "\n"));
+ ipstats(pData);
+ tcpstats(pData);
+ udpstats(pData);
+ icmpstats(pData);
+ mbufstats(pData);
+ sockstats(pData);
+ Log(("\n"
+ "\n"
+ "\n"));
+#endif
+ RTCritSectRwDelete(&pData->CsRwHandlerChain);
+ RTMemFree(pData);
+}
+
+
+#define CONN_CANFSEND(so) (((so)->so_state & (SS_FCANTSENDMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
+#define CONN_CANFRCV(so) (((so)->so_state & (SS_FCANTRCVMORE|SS_ISFCONNECTED)) == SS_ISFCONNECTED)
+
+/*
+ * curtime kept to an accuracy of 1ms
+ */
+static void updtime(PNATState pData)
+{
+#ifdef RT_OS_WINDOWS
+ struct _timeb tb;
+
+ _ftime(&tb);
+ curtime = (u_int)tb.time * (u_int)1000;
+ curtime += (u_int)tb.millitm;
+#else
+ gettimeofday(&tt, 0);
+
+ curtime = (u_int)tt.tv_sec * (u_int)1000;
+ curtime += (u_int)tt.tv_usec / (u_int)1000;
+
+ if ((tt.tv_usec % 1000) >= 500)
+ curtime++;
+#endif
+}
+
+#ifdef RT_OS_WINDOWS
+void slirp_select_fill(PNATState pData, int *pnfds)
+#else /* RT_OS_WINDOWS */
+void slirp_select_fill(PNATState pData, int *pnfds, struct pollfd *polls)
+#endif /* !RT_OS_WINDOWS */
+{
+ struct socket *so, *so_next;
+ int nfds;
+#if defined(RT_OS_WINDOWS)
+ int rc;
+ int error;
+#else
+ int poll_index = 0;
+#endif
+ int i;
+
+ STAM_PROFILE_START(&pData->StatFill, a);
+
+ nfds = *pnfds;
+
+ /*
+ * First, TCP sockets
+ */
+ do_slowtimo = 0;
+ if (!link_up)
+ goto done;
+
+ /*
+ * *_slowtimo needs calling if there are IP fragments
+ * in the fragment queue, or there are TCP connections active
+ */
+ /* XXX:
+ * triggering of fragment expiration should be the same but use new macroses
+ */
+ do_slowtimo = (tcb.so_next != &tcb);
+ if (!do_slowtimo)
+ {
+ for (i = 0; i < IPREASS_NHASH; i++)
+ {
+ if (!TAILQ_EMPTY(&ipq[i]))
+ {
+ do_slowtimo = 1;
+ break;
+ }
+ }
+ }
+ /* always add the ICMP socket */
+#ifndef RT_OS_WINDOWS
+ pData->icmp_socket.so_poll_index = -1;
+#endif
+ ICMP_ENGAGE_EVENT(&pData->icmp_socket, readfds);
+
+ STAM_COUNTER_RESET(&pData->StatTCP);
+ STAM_COUNTER_RESET(&pData->StatTCPHot);
+
+ QSOCKET_FOREACH(so, so_next, tcp)
+ /* { */
+ Assert(so->so_type == IPPROTO_TCP);
+#if !defined(RT_OS_WINDOWS)
+ so->so_poll_index = -1;
+#endif
+ STAM_COUNTER_INC(&pData->StatTCP);
+
+ /*
+ * See if we need a tcp_fasttimo
+ */
+ if ( time_fasttimo == 0
+ && so->so_tcpcb != NULL
+ && so->so_tcpcb->t_flags & TF_DELACK)
+ {
+ time_fasttimo = curtime; /* Flag when we want a fasttimo */
+ }
+
+ /*
+ * NOFDREF can include still connecting to local-host,
+ * newly socreated() sockets etc. Don't want to select these.
+ */
+ if (so->so_state & SS_NOFDREF || so->s == -1)
+ CONTINUE(tcp);
+
+ /*
+ * Set for reading sockets which are accepting
+ */
+ if (so->so_state & SS_FACCEPTCONN)
+ {
+ STAM_COUNTER_INC(&pData->StatTCPHot);
+ TCP_ENGAGE_EVENT1(so, readfds);
+ CONTINUE(tcp);
+ }
+
+ /*
+ * Set for writing sockets which are connecting
+ */
+ if (so->so_state & SS_ISFCONNECTING)
+ {
+ Log2(("connecting %R[natsock] engaged\n",so));
+ STAM_COUNTER_INC(&pData->StatTCPHot);
+#ifdef RT_OS_WINDOWS
+ WIN_TCP_ENGAGE_EVENT2(so, writefds, connectfds);
+#else
+ TCP_ENGAGE_EVENT1(so, writefds);
+#endif
+ }
+
+ /*
+ * Set for writing if we are connected, can send more, and
+ * we have something to send
+ */
+ if (CONN_CANFSEND(so) && SBUF_LEN(&so->so_rcv))
+ {
+ STAM_COUNTER_INC(&pData->StatTCPHot);
+ TCP_ENGAGE_EVENT1(so, writefds);
+ }
+
+ /*
+ * Set for reading (and urgent data) if we are connected, can
+ * receive more, and we have room for it XXX /2 ?
+ */
+ /** @todo vvl - check which predicat here will be more useful here in rerm of new sbufs. */
+ if ( CONN_CANFRCV(so)
+ && (SBUF_LEN(&so->so_snd) < (SBUF_SIZE(&so->so_snd)/2))
+#ifdef RT_OS_WINDOWS
+ && !(so->so_state & SS_ISFCONNECTING)
+#endif
+ )
+ {
+ STAM_COUNTER_INC(&pData->StatTCPHot);
+ TCP_ENGAGE_EVENT2(so, readfds, xfds);
+ }
+ LOOP_LABEL(tcp, so, so_next);
+ }
+
+ /*
+ * UDP sockets
+ */
+ STAM_COUNTER_RESET(&pData->StatUDP);
+ STAM_COUNTER_RESET(&pData->StatUDPHot);
+
+ QSOCKET_FOREACH(so, so_next, udp)
+ /* { */
+
+ Assert(so->so_type == IPPROTO_UDP);
+ STAM_COUNTER_INC(&pData->StatUDP);
+#if !defined(RT_OS_WINDOWS)
+ so->so_poll_index = -1;
+#endif
+
+ /*
+ * See if it's timed out
+ */
+ if (so->so_expire)
+ {
+ if (so->so_expire <= curtime)
+ {
+ Log2(("NAT: %R[natsock] expired\n", so));
+ if (so->so_timeout != NULL)
+ {
+ /* so_timeout - might change the so_expire value or
+ * drop so_timeout* from so.
+ */
+ so->so_timeout(pData, so, so->so_timeout_arg);
+ /* on 4.2 so->
+ */
+ if ( so_next->so_prev != so /* so_timeout freed the socket */
+ || so->so_timeout) /* so_timeout just freed so_timeout */
+ CONTINUE_NO_UNLOCK(udp);
+ }
+ UDP_DETACH(pData, so, so_next);
+ CONTINUE_NO_UNLOCK(udp);
+ }
+ }
+
+ /*
+ * When UDP packets are received from over the link, they're
+ * sendto()'d straight away, so no need for setting for writing
+ * Limit the number of packets queued by this session to 4.
+ * Note that even though we try and limit this to 4 packets,
+ * the session could have more queued if the packets needed
+ * to be fragmented.
+ *
+ * (XXX <= 4 ?)
+ */
+ if ((so->so_state & SS_ISFCONNECTED) && so->so_queued <= 4)
+ {
+ STAM_COUNTER_INC(&pData->StatUDPHot);
+ UDP_ENGAGE_EVENT(so, readfds);
+ }
+ LOOP_LABEL(udp, so, so_next);
+ }
+done:
+
+#if defined(RT_OS_WINDOWS)
+ *pnfds = VBOX_EVENT_COUNT;
+#else /* RT_OS_WINDOWS */
+ AssertRelease(poll_index <= *pnfds);
+ *pnfds = poll_index;
+#endif /* !RT_OS_WINDOWS */
+
+ STAM_PROFILE_STOP(&pData->StatFill, a);
+}
+
+
+/**
+ * This function do Connection or sending tcp sequence to.
+ * @returns if true operation completed
+ * @note: functions call tcp_input that potentially could lead to tcp_drop
+ */
+static bool slirpConnectOrWrite(PNATState pData, struct socket *so, bool fConnectOnly)
+{
+ int ret;
+ LogFlowFunc(("ENTER: so:%R[natsock], fConnectOnly:%RTbool\n", so, fConnectOnly));
+ /*
+ * Check for non-blocking, still-connecting sockets
+ */
+ if (so->so_state & SS_ISFCONNECTING)
+ {
+ Log2(("connecting %R[natsock] catched\n", so));
+ /* Connected */
+ so->so_state &= ~SS_ISFCONNECTING;
+
+ /*
+ * This should be probably guarded by PROBE_CONN too. Anyway,
+ * we disable it on OS/2 because the below send call returns
+ * EFAULT which causes the opened TCP socket to close right
+ * after it has been opened and connected.
+ */
+#ifndef RT_OS_OS2
+ ret = send(so->s, (const char *)&ret, 0, 0);
+ if (ret < 0)
+ {
+ /* XXXXX Must fix, zero bytes is a NOP */
+ if ( soIgnorableErrorCode(errno)
+ || errno == ENOTCONN)
+ {
+ LogFlowFunc(("LEAVE: false\n"));
+ return false;
+ }
+
+ /* else failed */
+ so->so_state = SS_NOFDREF;
+ }
+ /* else so->so_state &= ~SS_ISFCONNECTING; */
+#endif
+
+ /*
+ * Continue tcp_input
+ */
+ TCP_INPUT(pData, (struct mbuf *)NULL, sizeof(struct ip), so);
+ /* continue; */
+ }
+ else if (!fConnectOnly)
+ {
+ SOWRITE(ret, pData, so);
+ if (RT_LIKELY(ret > 0))
+ {
+ /*
+ * Make sure we will send window update to peer. This is
+ * a moral equivalent of calling tcp_output() for PRU_RCVD
+ * in tcp_usrreq() of the real stack.
+ */
+ struct tcpcb *tp = sototcpcb(so);
+ if (RT_LIKELY(tp != NULL))
+ tp->t_flags |= TF_DELACK;
+ }
+ }
+
+ LogFlowFunc(("LEAVE: true\n"));
+ return true;
+}
+
+#if defined(RT_OS_WINDOWS)
+void slirp_select_poll(PNATState pData, int fTimeout)
+#else /* RT_OS_WINDOWS */
+void slirp_select_poll(PNATState pData, struct pollfd *polls, int ndfs)
+#endif /* !RT_OS_WINDOWS */
+{
+ struct socket *so, *so_next;
+ int ret;
+#if defined(RT_OS_WINDOWS)
+ WSANETWORKEVENTS NetworkEvents;
+ int rc;
+ int error;
+#endif
+
+ STAM_PROFILE_START(&pData->StatPoll, a);
+
+ /* Update time */
+ updtime(pData);
+
+ /*
+ * See if anything has timed out
+ */
+ if (link_up)
+ {
+ if (time_fasttimo && ((curtime - time_fasttimo) >= 2))
+ {
+ STAM_PROFILE_START(&pData->StatFastTimer, b);
+ tcp_fasttimo(pData);
+ time_fasttimo = 0;
+ STAM_PROFILE_STOP(&pData->StatFastTimer, b);
+ }
+ if (do_slowtimo && ((curtime - last_slowtimo) >= 499))
+ {
+ STAM_PROFILE_START(&pData->StatSlowTimer, c);
+ ip_slowtimo(pData);
+ tcp_slowtimo(pData);
+ last_slowtimo = curtime;
+ STAM_PROFILE_STOP(&pData->StatSlowTimer, c);
+ }
+ }
+#if defined(RT_OS_WINDOWS)
+ if (fTimeout)
+ return; /* only timer update */
+#endif
+
+ /*
+ * Check sockets
+ */
+ if (!link_up)
+ goto done;
+#if defined(RT_OS_WINDOWS)
+ icmpwin_process(pData);
+#else
+ if ( (pData->icmp_socket.s != -1)
+ && CHECK_FD_SET(&pData->icmp_socket, ignored, readfds))
+ sorecvfrom(pData, &pData->icmp_socket);
+#endif
+ /*
+ * Check TCP sockets
+ */
+ QSOCKET_FOREACH(so, so_next, tcp)
+ /* { */
+ Assert(!so->fUnderPolling);
+ so->fUnderPolling = 1;
+ if (slirpVerifyAndFreeSocket(pData, so))
+ CONTINUE(tcp);
+ /*
+ * FD_ISSET is meaningless on these sockets
+ * (and they can crash the program)
+ */
+ if (so->so_state & SS_NOFDREF || so->s == -1)
+ {
+ so->fUnderPolling = 0;
+ CONTINUE(tcp);
+ }
+
+ POLL_TCP_EVENTS(rc, error, so, &NetworkEvents);
+
+ LOG_NAT_SOCK(so, TCP, &NetworkEvents, readfds, writefds, xfds);
+
+ if (so->so_state & SS_ISFCONNECTING)
+ {
+ int sockerr = 0;
+#if !defined(RT_OS_WINDOWS)
+ {
+ int revents = 0;
+
+ /*
+ * Failed connect(2) is reported by poll(2) on
+ * different OSes with different combinations of
+ * POLLERR, POLLHUP, and POLLOUT.
+ */
+ if ( CHECK_FD_SET(so, NetworkEvents, closefds) /* POLLHUP */
+ || CHECK_FD_SET(so, NetworkEvents, rderr)) /* POLLERR */
+ {
+ revents = POLLHUP; /* squash to single "failed" flag */
+ }
+#if defined(RT_OS_SOLARIS) || defined(RT_OS_NETBSD)
+ /* Solaris and NetBSD report plain POLLOUT even on error */
+ else if (CHECK_FD_SET(so, NetworkEvents, writefds)) /* POLLOUT */
+ {
+ revents = POLLOUT;
+ }
+#endif
+
+ if (revents != 0)
+ {
+ socklen_t optlen = (socklen_t)sizeof(sockerr);
+ ret = getsockopt(so->s, SOL_SOCKET, SO_ERROR, &sockerr, &optlen);
+
+ if ( RT_UNLIKELY(ret < 0)
+ || ( (revents & POLLHUP)
+ && RT_UNLIKELY(sockerr == 0)))
+ sockerr = ETIMEDOUT;
+ }
+ }
+#else /* RT_OS_WINDOWS */
+ {
+ if (NetworkEvents.lNetworkEvents & FD_CONNECT)
+ sockerr = NetworkEvents.iErrorCode[FD_CONNECT_BIT];
+ }
+#endif
+ if (sockerr != 0)
+ {
+ tcp_fconnect_failed(pData, so, sockerr);
+ ret = slirpVerifyAndFreeSocket(pData, so);
+ Assert(ret == 1); /* freed */
+ CONTINUE(tcp);
+ }
+
+ /*
+ * XXX: For now just fall through to the old code to
+ * handle successful connect(2).
+ */
+ }
+
+ /*
+ * Check for URG data
+ * This will soread as well, so no need to
+ * test for readfds below if this succeeds
+ */
+
+ /* out-of-band data */
+ if ( CHECK_FD_SET(so, NetworkEvents, xfds)
+#ifdef RT_OS_DARWIN
+ /* Darwin and probably BSD hosts generates POLLPRI|POLLHUP event on receiving TCP.flags.{ACK|URG|FIN} this
+ * combination on other Unixs hosts doesn't enter to this branch
+ */
+ && !CHECK_FD_SET(so, NetworkEvents, closefds)
+#endif
+#ifdef RT_OS_WINDOWS
+ /**
+ * In some cases FD_CLOSE comes with FD_OOB, that confuse tcp processing.
+ */
+ && !WIN_CHECK_FD_SET(so, NetworkEvents, closefds)
+#endif
+ )
+ {
+ sorecvoob(pData, so);
+ if (slirpVerifyAndFreeSocket(pData, so))
+ CONTINUE(tcp);
+ }
+
+ /*
+ * Check sockets for reading
+ */
+ else if ( CHECK_FD_SET(so, NetworkEvents, readfds)
+ || WIN_CHECK_FD_SET(so, NetworkEvents, acceptds))
+ {
+
+#ifdef RT_OS_WINDOWS
+ if (WIN_CHECK_FD_SET(so, NetworkEvents, connectfds))
+ {
+ /* Finish connection first */
+ /* should we ignore return value? */
+ bool fRet = slirpConnectOrWrite(pData, so, true);
+ LogFunc(("fRet:%RTbool\n", fRet)); NOREF(fRet);
+ if (slirpVerifyAndFreeSocket(pData, so))
+ CONTINUE(tcp);
+ }
+#endif
+ /*
+ * Check for incoming connections
+ */
+ if (so->so_state & SS_FACCEPTCONN)
+ {
+ TCP_CONNECT(pData, so);
+ if (slirpVerifyAndFreeSocket(pData, so))
+ CONTINUE(tcp);
+ if (!CHECK_FD_SET(so, NetworkEvents, closefds))
+ {
+ so->fUnderPolling = 0;
+ CONTINUE(tcp);
+ }
+ }
+
+ ret = soread(pData, so);
+ if (slirpVerifyAndFreeSocket(pData, so))
+ CONTINUE(tcp);
+ /* Output it if we read something */
+ if (RT_LIKELY(ret > 0))
+ TCP_OUTPUT(pData, sototcpcb(so));
+
+ if (slirpVerifyAndFreeSocket(pData, so))
+ CONTINUE(tcp);
+ }
+
+ /*
+ * Check for FD_CLOSE events.
+ * in some cases once FD_CLOSE engaged on socket it could be flashed latter (for some reasons)
+ */
+ if ( CHECK_FD_SET(so, NetworkEvents, closefds)
+ || (so->so_close == 1))
+ {
+ /*
+ * drain the socket
+ */
+ for (; so_next->so_prev == so
+ && !slirpVerifyAndFreeSocket(pData, so);)
+ {
+ ret = soread(pData, so);
+ if (slirpVerifyAndFreeSocket(pData, so))
+ break;
+
+ if (ret > 0)
+ TCP_OUTPUT(pData, sototcpcb(so));
+ else if (so_next->so_prev == so)
+ {
+ Log2(("%R[natsock] errno %d (%s)\n", so, errno, strerror(errno)));
+ break;
+ }
+ }
+
+ /* if socket freed ''so'' is PHANTOM and next socket isn't points on it */
+ if (so_next->so_prev != so)
+ {
+ CONTINUE(tcp);
+ }
+ else
+ {
+ /* mark the socket for termination _after_ it was drained */
+ so->so_close = 1;
+ /* No idea about Windows but on Posix, POLLHUP means that we can't send more.
+ * Actually in the specific error scenario, POLLERR is set as well. */
+#ifndef RT_OS_WINDOWS
+ if (CHECK_FD_SET(so, NetworkEvents, rderr))
+ sofcantsendmore(so);
+#endif
+ }
+ }
+
+ /*
+ * Check sockets for writing
+ */
+ if ( CHECK_FD_SET(so, NetworkEvents, writefds)
+#ifdef RT_OS_WINDOWS
+ || WIN_CHECK_FD_SET(so, NetworkEvents, connectfds)
+#endif
+ )
+ {
+ int fConnectOrWriteSuccess = slirpConnectOrWrite(pData, so, false);
+ /* slirpConnectOrWrite could return true even if tcp_input called tcp_drop,
+ * so we should be ready to such situations.
+ */
+ if (slirpVerifyAndFreeSocket(pData, so))
+ CONTINUE(tcp);
+ else if (!fConnectOrWriteSuccess)
+ {
+ so->fUnderPolling = 0;
+ CONTINUE(tcp);
+ }
+ /* slirpConnectionOrWrite succeeded and socket wasn't dropped */
+ }
+
+ /*
+ * Probe a still-connecting, non-blocking socket
+ * to check if it's still alive
+ */
+#ifdef PROBE_CONN
+ if (so->so_state & SS_ISFCONNECTING)
+ {
+ ret = recv(so->s, (char *)&ret, 0, 0);
+
+ if (ret < 0)
+ {
+ /* XXX */
+ if ( soIgnorableErrorCode(errno)
+ || errno == ENOTCONN)
+ {
+ CONTINUE(tcp); /* Still connecting, continue */
+ }
+
+ /* else failed */
+ so->so_state = SS_NOFDREF;
+
+ /* tcp_input will take care of it */
+ }
+ else
+ {
+ ret = send(so->s, &ret, 0, 0);
+ if (ret < 0)
+ {
+ /* XXX */
+ if ( soIgnorableErrorCode(errno)
+ || errno == ENOTCONN)
+ {
+ CONTINUE(tcp);
+ }
+ /* else failed */
+ so->so_state = SS_NOFDREF;
+ }
+ else
+ so->so_state &= ~SS_ISFCONNECTING;
+
+ }
+ TCP_INPUT((struct mbuf *)NULL, sizeof(struct ip),so);
+ } /* SS_ISFCONNECTING */
+#endif
+ if (!slirpVerifyAndFreeSocket(pData, so))
+ so->fUnderPolling = 0;
+ LOOP_LABEL(tcp, so, so_next);
+ }
+
+ /*
+ * Now UDP sockets.
+ * Incoming packets are sent straight away, they're not buffered.
+ * Incoming UDP data isn't buffered either.
+ */
+ QSOCKET_FOREACH(so, so_next, udp)
+ /* { */
+#if 0
+ so->fUnderPolling = 1;
+ if(slirpVerifyAndFreeSocket(pData, so));
+ CONTINUE(udp);
+ so->fUnderPolling = 0;
+#endif
+
+ POLL_UDP_EVENTS(rc, error, so, &NetworkEvents);
+
+ LOG_NAT_SOCK(so, UDP, &NetworkEvents, readfds, writefds, xfds);
+
+ if (so->s != -1 && CHECK_FD_SET(so, NetworkEvents, readfds))
+ {
+ SORECVFROM(pData, so);
+ }
+ LOOP_LABEL(udp, so, so_next);
+ }
+
+done:
+
+ STAM_PROFILE_STOP(&pData->StatPoll, a);
+}
+
+
+struct arphdr
+{
+ unsigned short ar_hrd; /* format of hardware address */
+#define ARPHRD_ETHER 1 /* ethernet hardware format */
+ unsigned short ar_pro; /* format of protocol address */
+ unsigned char ar_hln; /* length of hardware address */
+ unsigned char ar_pln; /* length of protocol address */
+ unsigned short ar_op; /* ARP opcode (command) */
+#define ARPOP_REQUEST 1 /* ARP request */
+#define ARPOP_REPLY 2 /* ARP reply */
+
+ /*
+ * Ethernet looks like this : This bit is variable sized however...
+ */
+ unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
+ unsigned char ar_sip[4]; /* sender IP address */
+ unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
+ unsigned char ar_tip[4]; /* target IP address */
+};
+AssertCompileSize(struct arphdr, 28);
+
+static void arp_output(PNATState pData, const uint8_t *pcu8EtherSource, const struct arphdr *pcARPHeaderSource, uint32_t ip4TargetAddress)
+{
+ struct ethhdr *pEtherHeaderResponse;
+ struct arphdr *pARPHeaderResponse;
+ uint32_t ip4TargetAddressInHostFormat;
+ struct mbuf *pMbufResponse;
+
+ Assert((pcu8EtherSource));
+ if (!pcu8EtherSource)
+ return;
+ ip4TargetAddressInHostFormat = RT_N2H_U32(ip4TargetAddress);
+
+ pMbufResponse = m_getcl(pData, M_NOWAIT, MT_HEADER, M_PKTHDR);
+ if (!pMbufResponse)
+ return;
+ pEtherHeaderResponse = mtod(pMbufResponse, struct ethhdr *);
+ /* @note: if_encap will swap src and dst*/
+ memcpy(pEtherHeaderResponse->h_source, pcu8EtherSource, ETH_ALEN);
+ pMbufResponse->m_data += ETH_HLEN;
+ pARPHeaderResponse = mtod(pMbufResponse, struct arphdr *);
+ pMbufResponse->m_len = sizeof(struct arphdr);
+
+ pARPHeaderResponse->ar_hrd = RT_H2N_U16_C(1);
+ pARPHeaderResponse->ar_pro = RT_H2N_U16_C(ETH_P_IP);
+ pARPHeaderResponse->ar_hln = ETH_ALEN;
+ pARPHeaderResponse->ar_pln = 4;
+ pARPHeaderResponse->ar_op = RT_H2N_U16_C(ARPOP_REPLY);
+ memcpy(pARPHeaderResponse->ar_sha, special_ethaddr, ETH_ALEN);
+
+ if (!slirpMbufTagService(pData, pMbufResponse, (uint8_t)(ip4TargetAddressInHostFormat & ~pData->netmask)))
+ {
+ static bool fTagErrorReported;
+ if (!fTagErrorReported)
+ {
+ LogRel(("NAT: Couldn't add the tag(PACKET_SERVICE:%d)\n",
+ (uint8_t)(ip4TargetAddressInHostFormat & ~pData->netmask)));
+ fTagErrorReported = true;
+ }
+ }
+ pARPHeaderResponse->ar_sha[5] = (uint8_t)(ip4TargetAddressInHostFormat & ~pData->netmask);
+
+ memcpy(pARPHeaderResponse->ar_sip, pcARPHeaderSource->ar_tip, 4);
+ memcpy(pARPHeaderResponse->ar_tha, pcARPHeaderSource->ar_sha, ETH_ALEN);
+ memcpy(pARPHeaderResponse->ar_tip, pcARPHeaderSource->ar_sip, 4);
+ if_encap(pData, ETH_P_ARP, pMbufResponse, ETH_ENCAP_URG);
+}
+
+/**
+ * @note This function will free m!
+ */
+static void arp_input(PNATState pData, struct mbuf *m)
+{
+ struct ethhdr *pEtherHeader;
+ struct arphdr *pARPHeader;
+ int ar_op;
+ uint32_t ip4TargetAddress;
+
+ /* drivers never return runt packets, so this should never happen */
+ if (RT_UNLIKELY((size_t)m->m_len
+ < sizeof(struct ethhdr) + sizeof(struct arphdr)))
+ goto done;
+
+ pEtherHeader = mtod(m, struct ethhdr *);
+ pARPHeader = (struct arphdr *)&pEtherHeader[1];
+
+ if (RT_UNLIKELY( pARPHeader->ar_hrd != RT_H2N_U16_C(ARPHRD_ETHER)
+ || pARPHeader->ar_pro != RT_H2N_U16_C(ETH_P_IP)
+ || pARPHeader->ar_hln != ETH_ALEN
+ || pARPHeader->ar_pln != sizeof(RTNETADDRIPV4)))
+ goto done;
+
+ ar_op = RT_N2H_U16(pARPHeader->ar_op);
+ ip4TargetAddress = *(uint32_t*)pARPHeader->ar_tip;
+
+ switch (ar_op)
+ {
+ case ARPOP_REQUEST:
+ if ( CTL_CHECK(ip4TargetAddress, CTL_DNS)
+ || CTL_CHECK(ip4TargetAddress, CTL_ALIAS)
+ || CTL_CHECK(ip4TargetAddress, CTL_TFTP))
+ {
+#if 0 /* Dropping ARP requests destined for CTL_ALIAS breaks all outgoing traffic completely, so don't do that... */
+ /* Don't reply to ARP requests for the hosts loopback interface if it is disabled. */
+ if ( CTL_CHECK(ip4TargetAddress, CTL_ALIAS)
+ && !pData->fLocalhostReachable)
+ break;
+#endif
+ slirp_update_guest_addr_guess(pData, *(uint32_t *)pARPHeader->ar_sip, "arp request");
+ arp_output(pData, pEtherHeader->h_source, pARPHeader, ip4TargetAddress);
+ break;
+ }
+
+ /* Gratuitous ARP */
+ if ( *(uint32_t *)pARPHeader->ar_sip == *(uint32_t *)pARPHeader->ar_tip
+ && ( memcmp(pARPHeader->ar_tha, zerro_ethaddr, ETH_ALEN) == 0
+ || memcmp(pARPHeader->ar_tha, broadcast_ethaddr, ETH_ALEN) == 0)
+ && memcmp(pEtherHeader->h_dest, broadcast_ethaddr, ETH_ALEN) == 0)
+ {
+ LogRel2(("NAT: Gratuitous ARP from %RTnaipv4 at %RTmac\n",
+ *(uint32_t *)pARPHeader->ar_sip, pARPHeader->ar_sha));
+ slirp_update_guest_addr_guess(pData, *(uint32_t *)pARPHeader->ar_sip, "gratuitous arp");
+ slirp_arp_cache_update_or_add(pData, *(uint32_t *)pARPHeader->ar_sip, &pARPHeader->ar_sha[0]);
+ }
+ break;
+
+ case ARPOP_REPLY:
+ slirp_arp_cache_update_or_add(pData, *(uint32_t *)pARPHeader->ar_sip, &pARPHeader->ar_sha[0]);
+ break;
+
+ default:
+ break;
+ }
+
+ done:
+ m_freem(pData, m);
+}
+
+/**
+ * Feed a packet into the slirp engine.
+ *
+ * @param m Data buffer, m_len is not valid.
+ * @param cbBuf The length of the data in m.
+ */
+void slirp_input(PNATState pData, struct mbuf *m, size_t cbBuf)
+{
+ int proto;
+ static bool fWarnedIpv6;
+ struct ethhdr *eh;
+
+ m->m_len = (int)cbBuf; Assert((size_t)m->m_len == cbBuf);
+ if (cbBuf < ETH_HLEN)
+ {
+ Log(("NAT: packet having size %d has been ignored\n", m->m_len));
+ m_freem(pData, m);
+ return;
+ }
+
+ eh = mtod(m, struct ethhdr *);
+ proto = RT_N2H_U16(eh->h_proto);
+ switch(proto)
+ {
+ case ETH_P_ARP:
+ arp_input(pData, m);
+ break;
+
+ case ETH_P_IP:
+ /* Update time. Important if the network is very quiet, as otherwise
+ * the first outgoing connection gets an incorrect timestamp. */
+ updtime(pData);
+ m_adj(m, ETH_HLEN);
+ M_ASSERTPKTHDR(m);
+ m->m_pkthdr.header = mtod(m, void *);
+ ip_input(pData, m);
+ break;
+
+ case ETH_P_IPV6:
+ m_freem(pData, m);
+ if (!fWarnedIpv6)
+ {
+ LogRel(("NAT: IPv6 not supported\n"));
+ fWarnedIpv6 = true;
+ }
+ break;
+
+ default:
+ Log(("NAT: Unsupported protocol %x\n", proto));
+ m_freem(pData, m);
+ break;
+ }
+}
+
+/**
+ * Output the IP packet to the ethernet device.
+ *
+ * @note This function will free m!
+ */
+void if_encap(PNATState pData, uint16_t eth_proto, struct mbuf *m, int flags)
+{
+ struct ethhdr *eh;
+ uint8_t *mbuf = NULL;
+ int mlen;
+ STAM_PROFILE_START(&pData->StatIF_encap, a);
+ LogFlowFunc(("ENTER: pData:%p, eth_proto:%RX16, m:%p, flags:%d\n",
+ pData, eth_proto, m, flags));
+
+ M_ASSERTPKTHDR(m);
+
+ Assert(M_LEADINGSPACE(m) >= ETH_HLEN);
+ m->m_data -= ETH_HLEN;
+ m->m_len += ETH_HLEN;
+ eh = mtod(m, struct ethhdr *);
+ mlen = m->m_len;
+
+ if (memcmp(eh->h_source, special_ethaddr, ETH_ALEN) != 0)
+ {
+ struct m_tag *t = m_tag_first(m);
+ uint8_t u8ServiceId = CTL_ALIAS;
+ memcpy(eh->h_dest, eh->h_source, ETH_ALEN);
+ memcpy(eh->h_source, special_ethaddr, ETH_ALEN);
+ Assert(memcmp(eh->h_dest, special_ethaddr, ETH_ALEN) != 0);
+ if (memcmp(eh->h_dest, zerro_ethaddr, ETH_ALEN) == 0)
+ {
+ /* don't do anything */
+ m_freem(pData, m);
+ goto done;
+ }
+ if ( t
+ && (t = m_tag_find(m, PACKET_SERVICE, NULL)))
+ {
+ Assert(t);
+ u8ServiceId = *(uint8_t *)&t[1];
+ }
+ eh->h_source[5] = u8ServiceId;
+ }
+ /*
+ * we're processing the chain, that isn't not expected.
+ */
+ Assert((!m->m_next));
+ if (m->m_next)
+ {
+ Log(("NAT: if_encap's recived the chain, dropping...\n"));
+ m_freem(pData, m);
+ goto done;
+ }
+ mbuf = mtod(m, uint8_t *);
+ eh->h_proto = RT_H2N_U16(eth_proto);
+ LogFunc(("eh(dst:%RTmac, src:%RTmac)\n", eh->h_dest, eh->h_source));
+ if (flags & ETH_ENCAP_URG)
+ slirp_urg_output(pData->pvUser, m, mbuf, mlen);
+ else
+ slirp_output(pData->pvUser, m, mbuf, mlen);
+done:
+ STAM_PROFILE_STOP(&pData->StatIF_encap, a);
+ LogFlowFuncLeave();
+}
+
+
+void
+slirp_update_guest_addr_guess(PNATState pData, uint32_t guess, const char *msg)
+{
+ Assert(msg != NULL);
+
+ if (pData->guest_addr_guess.s_addr == guess)
+ {
+ LogRel2(("NAT: Guest address guess %RTnaipv4 re-confirmed by %s\n",
+ pData->guest_addr_guess.s_addr, msg));
+ return;
+ }
+
+ if (pData->guest_addr_guess.s_addr == INADDR_ANY)
+ {
+ pData->guest_addr_guess.s_addr = guess;
+ LogRel(("NAT: Guest address guess set to %RTnaipv4 by %s\n",
+ pData->guest_addr_guess.s_addr, msg));
+ return;
+ }
+ else
+ {
+ LogRel(("NAT: Guest address guess changed from %RTnaipv4 to %RTnaipv4 by %s\n",
+ pData->guest_addr_guess.s_addr, guess, msg));
+ pData->guest_addr_guess.s_addr = guess;
+ return;
+ }
+}
+
+
+static struct port_forward_rule *
+slirp_find_redirect(PNATState pData,
+ int is_udp,
+ struct in_addr host_addr, int host_port,
+ struct in_addr guest_addr, int guest_port)
+{
+ struct port_forward_rule *rule;
+ uint16_t proto = (is_udp ? IPPROTO_UDP : IPPROTO_TCP);
+
+ LIST_FOREACH(rule, &pData->port_forward_rule_head, list)
+ {
+ if ( rule->proto == proto
+ && rule->host_port == host_port
+ && rule->bind_ip.s_addr == host_addr.s_addr
+ && rule->guest_port == guest_port
+ && rule->guest_addr.s_addr == guest_addr.s_addr)
+ {
+ return rule;
+ }
+ }
+
+ return NULL;
+}
+
+
+int slirp_add_redirect(PNATState pData, int is_udp, struct in_addr host_addr, int host_port,
+ struct in_addr guest_addr, int guest_port)
+{
+ struct port_forward_rule *rule;
+
+ rule = slirp_find_redirect(pData, is_udp, host_addr, host_port, guest_addr, guest_port);
+ if (rule != NULL) /* rule has been already registered */
+ {
+ /* XXX: this shouldn't happen */
+ return 0;
+ }
+
+ rule = RTMemAllocZ(sizeof(struct port_forward_rule));
+ if (rule == NULL)
+ return 1;
+
+ rule->proto = (is_udp ? IPPROTO_UDP : IPPROTO_TCP);
+ rule->bind_ip.s_addr = host_addr.s_addr;
+ rule->host_port = host_port;
+ rule->guest_addr.s_addr = guest_addr.s_addr;
+ rule->guest_port = guest_port;
+
+ if (rule->proto == IPPROTO_UDP)
+ rule->so = udp_listen(pData, rule->bind_ip.s_addr, RT_H2N_U16(rule->host_port),
+ rule->guest_addr.s_addr, RT_H2N_U16(rule->guest_port), 0);
+ else
+ rule->so = solisten(pData, rule->bind_ip.s_addr, RT_H2N_U16(rule->host_port),
+ rule->guest_addr.s_addr, RT_H2N_U16(rule->guest_port), 0);
+
+ if (rule->so == NULL)
+ {
+ LogRel(("NAT: Failed to redirect %s %RTnaipv4:%d -> %RTnaipv4:%d (%s)\n",
+ rule->proto == IPPROTO_UDP ? "UDP" : "TCP",
+ rule->bind_ip.s_addr, rule->host_port,
+ guest_addr, rule->guest_port, strerror(errno)));
+ RTMemFree(rule);
+ return 1;
+ }
+
+ LogRel(("NAT: Set redirect %s %RTnaipv4:%d -> %RTnaipv4:%d\n",
+ rule->proto == IPPROTO_UDP ? "UDP" : "TCP",
+ rule->bind_ip.s_addr, rule->host_port,
+ guest_addr, rule->guest_port));
+
+ LIST_INSERT_HEAD(&pData->port_forward_rule_head, rule, list);
+ return 0;
+}
+
+
+int slirp_remove_redirect(PNATState pData, int is_udp, struct in_addr host_addr, int host_port,
+ struct in_addr guest_addr, int guest_port)
+{
+ struct port_forward_rule *rule;
+
+ rule = slirp_find_redirect(pData, is_udp, host_addr, host_port, guest_addr, guest_port);
+ if (rule == NULL)
+ {
+ LogRel(("NAT: Unable to find redirect %s %RTnaipv4:%d -> %RTnaipv4:%d\n",
+ is_udp ? "UDP" : "TCP",
+ host_addr.s_addr, host_port,
+ guest_addr.s_addr, guest_port));
+ return 0;
+ }
+
+ LogRel(("NAT: Remove redirect %s %RTnaipv4:%d -> %RTnaipv4:%d\n",
+ rule->proto == IPPROTO_UDP ? "UDP" : "TCP",
+ rule->bind_ip.s_addr, rule->host_port,
+ guest_addr.s_addr, rule->guest_port));
+
+ if (rule->so != NULL)
+ {
+ if (is_udp)
+ udp_detach(pData, rule->so);
+ else
+ tcp_close(pData, sototcpcb(rule->so));
+ }
+
+ LIST_REMOVE(rule, list);
+ RTMemFree(rule);
+ return 0;
+}
+
+
+#if defined(RT_OS_WINDOWS)
+HANDLE *slirp_get_events(PNATState pData)
+{
+ return pData->phEvents;
+}
+void slirp_register_external_event(PNATState pData, HANDLE hEvent, int index)
+{
+ pData->phEvents[index] = hEvent;
+}
+#endif
+
+unsigned int slirp_get_timeout_ms(PNATState pData)
+{
+ if (link_up)
+ {
+ if (time_fasttimo)
+ return 2;
+ if (do_slowtimo)
+ return 500; /* see PR_SLOWHZ */
+ }
+ return 3600*1000; /* one hour */
+}
+
+#ifndef RT_OS_WINDOWS
+int slirp_get_nsock(PNATState pData)
+{
+ return pData->nsock;
+}
+#endif
+
+/*
+ * this function called from NAT thread
+ */
+void slirp_post_sent(PNATState pData, void *pvArg)
+{
+ struct mbuf *m = (struct mbuf *)pvArg;
+ m_freem(pData, m);
+}
+
+void slirp_set_dhcp_TFTP_prefix(PNATState pData, const char *tftpPrefix)
+{
+ Log2(("tftp_prefix: %s\n", tftpPrefix));
+ if (tftp_prefix)
+ RTStrFree((char *)tftp_prefix);
+ tftp_prefix = RTPathAbsDup(tftpPrefix);
+}
+
+void slirp_set_dhcp_TFTP_bootfile(PNATState pData, const char *bootFile)
+{
+ Log2(("bootFile: %s\n", bootFile));
+ bootp_filename = bootFile;
+}
+
+void slirp_set_dhcp_next_server(PNATState pData, const char *next_server)
+{
+ Log2(("next_server: %s\n", next_server));
+ if (next_server == NULL)
+ pData->tftp_server.s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_TFTP);
+ else
+ inet_aton(next_server, &pData->tftp_server);
+}
+
+int slirp_set_binding_address(PNATState pData, char *addr)
+{
+ int ok;
+
+ pData->bindIP.s_addr = INADDR_ANY;
+
+ if (addr == NULL || *addr == '\0')
+ return VINF_SUCCESS;
+
+ ok = inet_aton(addr, &pData->bindIP);
+ if (!ok)
+ {
+ LogRel(("NAT: Unable to parse binding address: %s\n", addr));
+ return VERR_INVALID_PARAMETER;
+ }
+
+ if (pData->bindIP.s_addr == INADDR_ANY)
+ return VINF_SUCCESS;
+
+ if ((pData->bindIP.s_addr & RT_N2H_U32_C(0xe0000000)) == RT_N2H_U32_C(0xe0000000))
+ {
+ LogRel(("NAT: Ignoring multicast binding address %RTnaipv4\n", pData->bindIP.s_addr));
+ pData->bindIP.s_addr = INADDR_ANY;
+ return VERR_INVALID_PARAMETER;
+ }
+
+ LogRel(("NAT: Binding address %RTnaipv4\n", pData->bindIP.s_addr));
+ return VINF_SUCCESS;
+}
+
+void slirp_set_dhcp_dns_proxy(PNATState pData, bool fDNSProxy)
+{
+ if (!pData->fUseHostResolver)
+ {
+ Log2(("NAT: DNS proxy switched %s\n", (fDNSProxy ? "on" : "off")));
+ pData->fUseDnsProxy = fDNSProxy;
+ }
+ else if (fDNSProxy)
+ LogRel(("NAT: Host Resolver conflicts with DNS proxy, the last one was forcely ignored\n"));
+}
+
+#define CHECK_ARG(name, val, lim_min, lim_max) \
+ do { \
+ if ((val) < (lim_min) || (val) > (lim_max)) \
+ { \
+ LogRel(("NAT: (" #name ":%d) has been ignored, " \
+ "because out of range (%d, %d)\n", (val), (lim_min), (lim_max))); \
+ return; \
+ } \
+ else \
+ LogRel(("NAT: (" #name ":%d)\n", (val))); \
+ } while (0)
+
+void slirp_set_somaxconn(PNATState pData, int iSoMaxConn)
+{
+ LogFlowFunc(("iSoMaxConn:%d\n", iSoMaxConn));
+ /* Conditions */
+ if (iSoMaxConn > SOMAXCONN)
+ {
+ LogRel(("NAT: value of somaxconn(%d) bigger than SOMAXCONN(%d)\n", iSoMaxConn, SOMAXCONN));
+ iSoMaxConn = SOMAXCONN;
+ }
+
+ if (iSoMaxConn < 1)
+ {
+ LogRel(("NAT: proposed value(%d) of somaxconn is invalid, default value is used (%d)\n", iSoMaxConn, pData->soMaxConn));
+ LogFlowFuncLeave();
+ return;
+ }
+
+ /* Asignment */
+ if (pData->soMaxConn != iSoMaxConn)
+ {
+ LogRel(("NAT: value of somaxconn has been changed from %d to %d\n",
+ pData->soMaxConn, iSoMaxConn));
+ pData->soMaxConn = iSoMaxConn;
+ }
+ LogFlowFuncLeave();
+}
+/* don't allow user set less 8kB and more than 1M values */
+#define _8K_1M_CHECK_ARG(name, val) CHECK_ARG(name, (val), 8, 1024)
+void slirp_set_rcvbuf(PNATState pData, int kilobytes)
+{
+ _8K_1M_CHECK_ARG("SOCKET_RCVBUF", kilobytes);
+ pData->socket_rcv = kilobytes;
+}
+void slirp_set_sndbuf(PNATState pData, int kilobytes)
+{
+ _8K_1M_CHECK_ARG("SOCKET_SNDBUF", kilobytes);
+ pData->socket_snd = kilobytes * _1K;
+}
+void slirp_set_tcp_rcvspace(PNATState pData, int kilobytes)
+{
+ _8K_1M_CHECK_ARG("TCP_RCVSPACE", kilobytes);
+ tcp_rcvspace = kilobytes * _1K;
+}
+void slirp_set_tcp_sndspace(PNATState pData, int kilobytes)
+{
+ _8K_1M_CHECK_ARG("TCP_SNDSPACE", kilobytes);
+ tcp_sndspace = kilobytes * _1K;
+}
+
+/*
+ * Looking for Ether by ip in ARP-cache
+ * Note: it´s responsible of caller to allocate buffer for result
+ * @returns iprt status code
+ */
+int slirp_arp_lookup_ether_by_ip(PNATState pData, uint32_t ip, uint8_t *ether)
+{
+ struct arp_cache_entry *ac;
+
+ if (ether == NULL)
+ return VERR_INVALID_PARAMETER;
+
+ if (LIST_EMPTY(&pData->arp_cache))
+ return VERR_NOT_FOUND;
+
+ LIST_FOREACH(ac, &pData->arp_cache, list)
+ {
+ if ( ac->ip == ip
+ && memcmp(ac->ether, broadcast_ethaddr, ETH_ALEN) != 0)
+ {
+ memcpy(ether, ac->ether, ETH_ALEN);
+ return VINF_SUCCESS;
+ }
+ }
+ return VERR_NOT_FOUND;
+}
+
+/*
+ * Looking for IP by Ether in ARP-cache
+ * Note: it´s responsible of caller to allocate buffer for result
+ * @returns 0 - if found, 1 - otherwise
+ */
+int slirp_arp_lookup_ip_by_ether(PNATState pData, const uint8_t *ether, uint32_t *ip)
+{
+ struct arp_cache_entry *ac;
+ *ip = INADDR_ANY;
+
+ if (LIST_EMPTY(&pData->arp_cache))
+ return VERR_NOT_FOUND;
+
+ LIST_FOREACH(ac, &pData->arp_cache, list)
+ {
+ if (memcmp(ether, ac->ether, ETH_ALEN) == 0)
+ {
+ *ip = ac->ip;
+ return VINF_SUCCESS;
+ }
+ }
+ return VERR_NOT_FOUND;
+}
+
+void slirp_arp_who_has(PNATState pData, uint32_t dst)
+{
+ struct mbuf *m;
+ struct ethhdr *ehdr;
+ struct arphdr *ahdr;
+ static bool fWarned = false;
+ LogFlowFunc(("ENTER: %RTnaipv4\n", dst));
+
+ /* ARP request WHO HAS 0.0.0.0 is one of the signals
+ * that something has been broken at Slirp. Investigating
+ * pcap dumps it's easy to miss warning ARP requests being
+ * focused on investigation of other protocols flow.
+ */
+#ifdef DEBUG_vvl
+ Assert((dst != INADDR_ANY));
+ NOREF(fWarned);
+#else
+ if ( dst == INADDR_ANY
+ && !fWarned)
+ {
+ LogRel(("NAT: ARP: \"WHO HAS INADDR_ANY\" request has been detected\n"));
+ fWarned = true;
+ }
+#endif /* !DEBUG_vvl */
+
+ m = m_getcl(pData, M_NOWAIT, MT_HEADER, M_PKTHDR);
+ if (m == NULL)
+ {
+ Log(("NAT: Can't alloc mbuf for ARP request\n"));
+ LogFlowFuncLeave();
+ return;
+ }
+ ehdr = mtod(m, struct ethhdr *);
+ memset(ehdr->h_source, 0xff, ETH_ALEN);
+ ahdr = (struct arphdr *)&ehdr[1];
+ ahdr->ar_hrd = RT_H2N_U16_C(1);
+ ahdr->ar_pro = RT_H2N_U16_C(ETH_P_IP);
+ ahdr->ar_hln = ETH_ALEN;
+ ahdr->ar_pln = 4;
+ ahdr->ar_op = RT_H2N_U16_C(ARPOP_REQUEST);
+ memcpy(ahdr->ar_sha, special_ethaddr, ETH_ALEN);
+ /* we assume that this request come from gw, but not from DNS or TFTP */
+ ahdr->ar_sha[5] = CTL_ALIAS;
+ *(uint32_t *)ahdr->ar_sip = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_ALIAS);
+ memset(ahdr->ar_tha, 0xff, ETH_ALEN); /*broadcast*/
+ *(uint32_t *)ahdr->ar_tip = dst;
+ /* warn!!! should falls in mbuf minimal size */
+ m->m_len = sizeof(struct arphdr) + ETH_HLEN;
+ m->m_data += ETH_HLEN;
+ m->m_len -= ETH_HLEN;
+ if_encap(pData, ETH_P_ARP, m, ETH_ENCAP_URG);
+ LogFlowFuncLeave();
+}
+
+
+/* updates the arp cache
+ * @note: this is helper function, slirp_arp_cache_update_or_add should be used.
+ * @returns 0 - if has found and updated
+ * 1 - if hasn't found.
+ */
+static inline int slirp_arp_cache_update(PNATState pData, uint32_t dst, const uint8_t *mac)
+{
+ struct arp_cache_entry *ac;
+ Assert(( memcmp(mac, broadcast_ethaddr, ETH_ALEN)
+ && memcmp(mac, zerro_ethaddr, ETH_ALEN)));
+ LIST_FOREACH(ac, &pData->arp_cache, list)
+ {
+ if (ac->ip == dst)
+ {
+ memcpy(ac->ether, mac, ETH_ALEN);
+ return 0;
+ }
+ }
+ return 1;
+}
+
+/**
+ * add entry to the arp cache
+ * @note: this is helper function, slirp_arp_cache_update_or_add should be used.
+ */
+static inline void slirp_arp_cache_add(PNATState pData, uint32_t ip, const uint8_t *ether)
+{
+ struct arp_cache_entry *ac = NULL;
+ Assert(( memcmp(ether, broadcast_ethaddr, ETH_ALEN)
+ && memcmp(ether, zerro_ethaddr, ETH_ALEN)));
+ ac = RTMemAllocZ(sizeof(struct arp_cache_entry));
+ if (ac == NULL)
+ {
+ Log(("NAT: Can't allocate arp cache entry\n"));
+ return;
+ }
+ ac->ip = ip;
+ memcpy(ac->ether, ether, ETH_ALEN);
+ LIST_INSERT_HEAD(&pData->arp_cache, ac, list);
+}
+
+/* updates or adds entry to the arp cache
+ * @returns 0 - if has found and updated
+ * 1 - if hasn't found.
+ */
+int slirp_arp_cache_update_or_add(PNATState pData, uint32_t dst, const uint8_t *mac)
+{
+ if ( !memcmp(mac, broadcast_ethaddr, ETH_ALEN)
+ || !memcmp(mac, zerro_ethaddr, ETH_ALEN))
+ {
+ static bool fBroadcastEtherAddReported;
+ if (!fBroadcastEtherAddReported)
+ {
+ LogRel(("NAT: Attempt to add pair [%RTmac:%RTnaipv4] in ARP cache was ignored\n",
+ mac, dst));
+ fBroadcastEtherAddReported = true;
+ }
+ return 1;
+ }
+ if (slirp_arp_cache_update(pData, dst, mac))
+ slirp_arp_cache_add(pData, dst, mac);
+
+ return 0;
+}
+
+
+void slirp_set_mtu(PNATState pData, int mtu)
+{
+ if (mtu < 20 || mtu >= 16000)
+ {
+ LogRel(("NAT: MTU(%d) is out of range (20;16000] mtu forcely assigned to 1500\n", mtu));
+ mtu = 1500;
+ }
+ /* MTU is maximum transition unit on */
+ if_mtu =
+ if_mru = mtu;
+}
+
+/**
+ * Info handler.
+ */
+void slirp_info(PNATState pData, const void *pvArg, const char *pszArgs)
+{
+ struct socket *so, *so_next;
+ struct arp_cache_entry *ac;
+ struct port_forward_rule *rule;
+ PCDBGFINFOHLP pHlp = (PCDBGFINFOHLP)pvArg;
+ NOREF(pszArgs);
+
+ pHlp->pfnPrintf(pHlp, "NAT parameters: MTU=%d\n", if_mtu);
+ pHlp->pfnPrintf(pHlp, "NAT TCP ports:\n");
+ QSOCKET_FOREACH(so, so_next, tcp)
+ /* { */
+ pHlp->pfnPrintf(pHlp, " %R[natsock]\n", so);
+ }
+
+ pHlp->pfnPrintf(pHlp, "NAT UDP ports:\n");
+ QSOCKET_FOREACH(so, so_next, udp)
+ /* { */
+ pHlp->pfnPrintf(pHlp, " %R[natsock]\n", so);
+ }
+
+ pHlp->pfnPrintf(pHlp, "NAT ARP cache:\n");
+ LIST_FOREACH(ac, &pData->arp_cache, list)
+ {
+ pHlp->pfnPrintf(pHlp, " %RTnaipv4 %RTmac\n", ac->ip, &ac->ether);
+ }
+
+ pHlp->pfnPrintf(pHlp, "NAT rules:\n");
+ LIST_FOREACH(rule, &pData->port_forward_rule_head, list)
+ {
+ pHlp->pfnPrintf(pHlp, " %s %d => %RTnaipv4:%d %c\n",
+ rule->proto == IPPROTO_UDP ? "UDP" : "TCP",
+ rule->host_port, rule->guest_addr.s_addr, rule->guest_port,
+ rule->activated ? ' ' : '*');
+ }
+}
+
+/**
+ * @note: NATState::fUseHostResolver could be changed in bootp.c::dhcp_decode
+ * @note: this function is executed on GUI/VirtualBox or main/VBoxHeadless thread.
+ * @note: this function can potentially race with bootp.c::dhcp_decode (except Darwin)
+ */
+int slirp_host_network_configuration_change_strategy_selector(const PNATState pData)
+{
+ if (pData->fUseHostResolverPermanent)
+ return VBOX_NAT_DNS_HOSTRESOLVER;
+
+ if (pData->fUseDnsProxy) {
+#if HAVE_NOTIFICATION_FOR_DNS_UPDATE /* XXX */ && !defined(RT_OS_WINDOWS)
+ /* We dont conflict with bootp.c::dhcp_decode */
+ struct rcp_state rcp_state;
+ int rc;
+
+ rcp_state.rcps_flags = RCPSF_IGNORE_IPV6;
+ rc = rcp_parse(&rcp_state, RESOLV_CONF_FILE);
+ LogRelFunc(("NAT: rcp_parse:%Rrc old domain:%s new domain:%s\n",
+ rc, LIST_EMPTY(&pData->pDomainList)
+ ? "(null)"
+ : LIST_FIRST(&pData->pDomainList)->dd_pszDomain,
+ rcp_state.rcps_domain));
+ if ( RT_FAILURE(rc)
+ || LIST_EMPTY(&pData->pDomainList))
+ return VBOX_NAT_DNS_DNSPROXY;
+
+ if ( rcp_state.rcps_domain
+ && strcmp(rcp_state.rcps_domain, LIST_FIRST(&pData->pDomainList)->dd_pszDomain) == 0)
+ return VBOX_NAT_DNS_DNSPROXY;
+ else
+ return VBOX_NAT_DNS_EXTERNAL;
+#else
+ /* copy domain name */
+ /* domain only compare with coy version */
+ return VBOX_NAT_DNS_DNSPROXY;
+#endif
+ }
+ return VBOX_NAT_DNS_EXTERNAL;
+}
diff --git a/src/VBox/Devices/Network/slirp/slirp.h b/src/VBox/Devices/Network/slirp/slirp.h
new file mode 100644
index 00000000..96ff468c
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/slirp.h
@@ -0,0 +1,555 @@
+/* $Id: slirp.h $ */
+/** @file
+ * NAT - slirp (declarations/defines).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef __COMMON_H__
+#define __COMMON_H__
+
+#include <VBox/vmm/stam.h>
+
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/winsock2.h>
+# include <iprt/win/ws2tcpip.h>
+typedef int socklen_t;
+#endif
+#ifdef RT_OS_OS2 /* temporary workaround, see ticket #127 */
+# define mbstat mbstat_os2
+# include <sys/socket.h>
+# undef mbstat
+typedef int socklen_t;
+#endif
+
+#define CONFIG_QEMU
+
+#ifdef DEBUG
+# undef DEBUG
+# define DEBUG 1
+#endif
+
+#ifndef CONFIG_QEMU
+# include "version.h"
+#endif
+#define LOG_GROUP LOG_GROUP_DRV_NAT
+#include <VBox/log.h>
+#include <iprt/mem.h>
+#ifdef RT_OS_WINDOWS
+# include <iprt/win/windows.h>
+# include <io.h>
+#endif
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+#include <iprt/string.h>
+#include <iprt/dir.h>
+#include <iprt/rand.h>
+#include <iprt/net.h>
+#include <VBox/types.h>
+
+#undef malloc
+#define malloc dont_use_malloc
+#undef free
+#define free dont_use_free
+#undef realloc
+#define realloc dont_use_realloc
+#undef strdup
+#define strdup dont_use_strdup
+
+#include "slirp_config.h"
+
+#ifdef RT_OS_WINDOWS
+
+# ifndef _MSC_VER
+# include <inttypes.h>
+# endif
+
+
+# include <sys/timeb.h>
+# include <iprt/win/iphlpapi.h>
+
+/* We don't want the errno.h versions of these error defines. */
+# if defined(_MSC_VER) && _MSC_VER >= 1600
+# include <errno.h>
+# undef ECONNREFUSED
+# undef ECONNRESET
+# undef EHOSTDOWN
+# undef EHOSTUNREACH
+# undef EINPROGRESS
+# undef ENETDOWN
+# undef ENETUNREACH
+# undef ENOTCONN
+# undef ESHUTDOWN
+# undef EWOULDBLOCK
+# endif
+# define ECONNREFUSED WSAECONNREFUSED
+# define ECONNRESET WSAECONNRESET
+# define EHOSTDOWN WSAEHOSTDOWN
+# define EHOSTUNREACH WSAEHOSTUNREACH
+# define EINPROGRESS WSAEINPROGRESS
+# define ENETDOWN WSAENETDOWN
+# define ENETUNREACH WSAENETUNREACH
+# define ENOTCONN WSAENOTCONN
+# define ESHUTDOWN WSAESHUTDOWN
+# define EWOULDBLOCK WSAEWOULDBLOCK
+
+/* standard names for the shutdown() "how" argument */
+#define SHUT_RD SD_RECEIVE
+#define SHUT_WR SD_SEND
+#define SHUT_RDWR SD_BOTH
+
+typedef uint8_t u_int8_t;
+typedef uint16_t u_int16_t;
+typedef uint32_t u_int32_t;
+
+#else /* !RT_OS_WINDOWS */
+
+# define ioctlsocket ioctl
+# define closesocket(s) close(s)
+# define O_BINARY 0
+
+#endif /* !RT_OS_WINDOWS */
+
+#if defined(RT_OS_WINDOWS) || defined (RT_OS_SOLARIS)
+typedef uint64_t u_int64_t;
+typedef char *caddr_t;
+#endif
+
+#include <sys/types.h>
+#ifdef HAVE_SYS_BITYPES_H
+# include <sys/bitypes.h>
+#endif
+
+#ifdef _MSC_VER
+# include <time.h>
+#else /* !_MSC_VER */
+# include <sys/time.h>
+#endif /* !_MSC_VER */
+
+#ifdef NEED_TYPEDEFS
+typedef char int8_t;
+typedef unsigned char u_int8_t;
+
+# if SIZEOF_SHORT == 2
+ typedef short int16_t;
+ typedef unsigned short u_int16_t;
+# else
+# if SIZEOF_INT == 2
+ typedef int int16_t;
+ typedef unsigned int u_int16_t;
+# else
+ #error Cannot find a type with sizeof() == 2
+# endif
+# endif
+
+# if SIZEOF_SHORT == 4
+ typedef short int32_t;
+ typedef unsigned short u_int32_t;
+# else
+# if SIZEOF_INT == 4
+ typedef int int32_t;
+ typedef unsigned int u_int32_t;
+# else
+ #error Cannot find a type with sizeof() == 4
+# endif
+# endif
+#endif /* NEED_TYPEDEFS */
+
+#ifdef HAVE_UNISTD_H
+# include <unistd.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+# include <stdlib.h>
+#endif
+
+#include <errno.h>
+
+
+#ifndef HAVE_MEMMOVE
+# define memmove(x, y, z) bcopy(y, x, z)
+#endif
+
+#if TIME_WITH_SYS_TIME
+# include <sys/time.h>
+# include <time.h>
+#else
+# ifndef HAVE_SYS_TIME_H
+# define HAVE_SYS_TIME_H 0
+# endif
+# if HAVE_SYS_TIME_H
+# include <sys/time.h>
+# else
+# include <time.h>
+# endif
+#endif
+
+#ifdef HAVE_STRING_H
+# include <string.h>
+#else
+# include <strings.h>
+#endif
+
+#ifndef RT_OS_WINDOWS
+# include <sys/uio.h>
+#endif
+
+#ifndef RT_OS_WINDOWS
+# include <netinet/in.h>
+# include <arpa/inet.h>
+#endif
+
+#ifdef GETTIMEOFDAY_ONE_ARG
+# define gettimeofday(x, y) gettimeofday(x)
+#endif
+
+#ifndef HAVE_INET_ATON
+int inet_aton (const char *cp, struct in_addr *ia);
+#endif
+
+#include <fcntl.h>
+#ifndef NO_UNIX_SOCKETS
+# include <sys/un.h>
+#endif
+#include <signal.h>
+#ifdef HAVE_SYS_SIGNAL_H
+# include <sys/signal.h>
+#endif
+#ifndef RT_OS_WINDOWS
+# include <sys/socket.h>
+#endif
+
+#if defined(HAVE_SYS_IOCTL_H)
+# include <sys/ioctl.h>
+#endif
+
+#ifdef HAVE_SYS_SELECT_H
+# include <sys/select.h>
+#endif
+
+#ifdef HAVE_SYS_WAIT_H
+# include <sys/wait.h>
+#endif
+
+#ifdef HAVE_SYS_FILIO_H
+# include <sys/filio.h>
+#endif
+
+#if defined(__STDC__) || defined(_MSC_VER)
+# include <stdarg.h>
+#else
+# include <varargs.h>
+#endif
+
+#include <sys/stat.h>
+
+/* Avoid conflicting with the libc insque() and remque(), which
+ * have different prototypes. */
+#define insque slirp_insque
+#define remque slirp_remque
+
+#ifdef HAVE_SYS_STROPTS_H
+# include <sys/stropts.h>
+#endif
+
+#include "libslirp.h"
+
+#include "debug.h"
+
+#include "ip.h"
+#include "tcp.h"
+#include "tcp_timer.h"
+#include "tcp_var.h"
+#include "tcpip.h"
+#include "udp.h"
+#include "icmp_var.h"
+#include "mbuf.h"
+#include "if.h"
+#include "sbuf.h"
+#include "socket.h"
+#include "main.h"
+#include "misc.h"
+#include "ctl.h"
+#include "bootp.h"
+#include "tftp.h"
+
+#include "slirp_state.h"
+#include "slirp_dns.h"
+
+#undef PVM /* XXX Mac OS X hack */
+
+#ifndef NULL
+# define NULL (void *)0
+#endif
+
+void if_start (PNATState);
+
+#ifndef HAVE_INDEX
+ char *index (const char *, int);
+#endif
+
+#ifndef HAVE_GETHOSTID
+ long gethostid (void);
+#endif
+
+#ifndef RT_OS_WINDOWS
+#include <netdb.h>
+#endif
+
+#include "dnsproxy/dnsproxy.h"
+
+#define DEFAULT_BAUD 115200
+
+int get_dns_addr(PNATState pData);
+
+/* cksum.c */
+typedef uint16_t u_short;
+typedef unsigned int u_int;
+#include "in_cksum.h"
+
+/* if.c */
+void if_init (PNATState);
+void if_output (PNATState, struct socket *, struct mbuf *);
+
+/* ip_input.c */
+void ip_init (PNATState);
+void ip_input (PNATState, struct mbuf *);
+struct mbuf * ip_reass (PNATState, register struct mbuf *);
+void ip_freef (PNATState, struct ipqhead *, struct ipq_t *);
+void ip_slowtimo (PNATState);
+void ip_stripoptions (register struct mbuf *, struct mbuf *);
+
+/* ip_output.c */
+int ip_output (PNATState, struct socket *, struct mbuf *);
+int ip_output0 (PNATState, struct socket *, struct mbuf *, int urg);
+
+/* tcp_input.c */
+int tcp_reass (PNATState, struct tcpcb *, struct tcphdr *, int *, struct mbuf *);
+void tcp_input (PNATState, register struct mbuf *, int, struct socket *);
+void tcp_fconnect_failed(PNATState, struct socket *, int);
+void tcp_dooptions (PNATState, struct tcpcb *, u_char *, int, struct tcpiphdr *);
+void tcp_xmit_timer (PNATState, register struct tcpcb *, int);
+int tcp_mss (PNATState, register struct tcpcb *, u_int);
+
+/* tcp_output.c */
+int tcp_output (PNATState, register struct tcpcb *);
+void tcp_setpersist (register struct tcpcb *);
+
+/* tcp_subr.c */
+void tcp_init (PNATState);
+void tcp_template (struct tcpcb *);
+void tcp_respond (PNATState, struct tcpcb *, register struct tcpiphdr *, register struct mbuf *, tcp_seq, tcp_seq, int);
+struct tcpcb * tcp_newtcpcb (PNATState, struct socket *);
+struct tcpcb * tcp_close (PNATState, register struct tcpcb *);
+void tcp_drain (void);
+void tcp_sockclosed (PNATState, struct tcpcb *);
+int tcp_fconnect (PNATState, struct socket *);
+void tcp_connect (PNATState, struct socket *);
+int tcp_attach (PNATState, struct socket *);
+u_int8_t tcp_tos (struct socket *);
+int tcp_ctl (PNATState, struct socket *);
+struct tcpcb *tcp_drop(PNATState, struct tcpcb *tp, int err);
+
+/* hostres.c */
+struct mbuf *hostresolver(PNATState, struct mbuf *, uint32_t src, uint16_t sport);
+
+/*slirp.c*/
+void slirp_arp_who_has(PNATState pData, uint32_t dst);
+int slirp_arp_cache_update_or_add(PNATState pData, uint32_t dst, const uint8_t *mac);
+int slirp_init_dns_list(PNATState pData);
+void slirp_release_dns_list(PNATState pData);
+#define MIN_MRU 128
+#define MAX_MRU 16384
+
+#ifndef RT_OS_WINDOWS
+# define min(x, y) ((x) < (y) ? (x) : (y))
+# define max(x, y) ((x) > (y) ? (x) : (y))
+#endif
+
+#ifdef RT_OS_WINDOWS
+# undef errno
+# if 0 /* debugging */
+int errno_func(const char *file, int line);
+# define errno (errno_func(__FILE__, __LINE__))
+# else
+# define errno (WSAGetLastError())
+# endif
+#endif
+
+# define ETH_ALEN 6
+# define ETH_HLEN 14
+
+struct ethhdr
+{
+ unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
+ unsigned char h_source[ETH_ALEN]; /* source ether addr */
+ unsigned short h_proto; /* packet type ID field */
+};
+AssertCompileSize(struct ethhdr, 14);
+
+/*
+ * (vvl) externing of sscanf.
+ */
+int sscanf(const char *s, const char *format, ...);
+
+#if defined(VBOX_SLIRP_ALIAS) || defined(VBOX_SLIRP_BSD)
+
+# define ip_next(ip) (void *)((uint8_t *)(ip) + ((ip)->ip_hl << 2))
+# define udp_next(udp) (void *)((uint8_t *)&((struct udphdr *)(udp))[1])
+# undef bcopy
+# define bcopy(src, dst, len) memcpy((dst), (src), (len))
+# undef bcmp
+# define bcmp(a1, a2, len) memcmp((a1), (a2), (len))
+# define NO_FW_PUNCH
+/* Two wrongs don't make a right, but this at least averts harm. */
+# define NO_USE_SOCKETS
+
+# ifdef alias_addr
+# ifndef VBOX_SLIRP_BSD
+# error alias_addr has already defined!!!
+# else
+# undef alias_addr
+# endif
+# endif
+
+# define arc4random() RTRandU32()
+# undef malloc
+# undef calloc
+# undef free
+# define malloc(x) RTMemAlloc((x))
+# define calloc(x, n) RTMemAllocZ((x)*(n))
+# define free(x) RTMemFree((x))
+# ifndef __unused
+# define __unused
+# endif
+
+# define strncasecmp RTStrNICmp
+# define stderr NULL
+# define stdout NULL
+
+# ifdef VBOX_WITH_DEBUG_LIBALIAS
+# define LIBALIAS_DEBUG
+# endif
+
+# define fflush(x) do{} while(0)
+# include "ext.h"
+#endif /*VBOX_SLIRP_ALIAS*/
+
+/**
+ * @todo might be useful to make it configurable, especially in terms of Intnet behind NAT
+ */
+# define maxusers 32
+# define max_protohdr 0
+/**
+ * @todo (vvl) for now ignore these values, later perhaps initialize tuning parameters
+ */
+# define TUNABLE_INT_FETCH(name, pval) do { } while (0)
+# define SYSCTL_PROC(a0, a1, a2, a3, a4, a5, a6, a7, a8) const int dummy_ ## a6 = 0
+# define SYSCTL_STRUCT(a0, a1, a2, a3, a4, a5, a6) const int dummy_ ## a5 = 0
+# define SYSINIT(a0, a1, a2, a3, a4) const int dummy_ ## a3 = 0
+# define sysctl_handle_int(a0, a1, a2, a3) 0
+# define EVENTHANDLER_INVOKE(a) do{}while(0)
+# define EVENTHANDLER_REGISTER(a0, a1, a2, a3) do{}while(0)
+# define KASSERT AssertMsg
+
+struct dummy_req
+{
+ void *newptr;
+};
+
+#define SYSCTL_HANDLER_ARGS PNATState pData, void *oidp, struct dummy_req *req
+
+void mbuf_init(void *);
+# define cksum(m, len) in_cksum_skip((m), (len), 0)
+
+int ftp_alias_load(PNATState);
+int ftp_alias_unload(PNATState);
+int nbt_alias_load(PNATState);
+int nbt_alias_unload(PNATState);
+int slirp_arp_lookup_ip_by_ether(PNATState, const uint8_t *, uint32_t *);
+int slirp_arp_lookup_ether_by_ip(PNATState, uint32_t, uint8_t *);
+
+DECLINLINE(unsigned) slirp_size(PNATState pData)
+{
+ if (if_mtu < MSIZE)
+ return MCLBYTES;
+ else if (if_mtu < MCLBYTES)
+ return MCLBYTES;
+ else if (if_mtu < MJUM9BYTES)
+ return MJUM9BYTES;
+ else if (if_mtu < MJUM16BYTES)
+ return MJUM16BYTES;
+ else
+ AssertMsgFailed(("Unsupported size"));
+ return 0;
+}
+
+static inline bool slirpMbufTagService(PNATState pData, struct mbuf *m, uint8_t u8ServiceId)
+{
+ struct m_tag * t = NULL;
+ NOREF(pData);
+ /* if_encap assumes that all packets goes through aliased address(gw) */
+ if (u8ServiceId == CTL_ALIAS)
+ return true;
+ t = m_tag_get(PACKET_SERVICE, sizeof(uint8_t), 0);
+ if (!t)
+ return false;
+ *(uint8_t *)&t[1] = u8ServiceId;
+ m_tag_prepend(m, t);
+ return true;
+}
+
+/**
+ * This function tags mbuf allocated for special services.
+ * @todo: add service id verification.
+ */
+static inline struct mbuf *slirpServiceMbufAlloc(PNATState pData, uint8_t u8ServiceId)
+{
+ struct mbuf *m = NULL;
+ m = m_getcl(pData, M_DONTWAIT, MT_HEADER, M_PKTHDR);
+ if (!m)
+ return m;
+ if(!slirpMbufTagService(pData, m, u8ServiceId))
+ {
+ m_freem(pData, m);
+ return NULL;
+ }
+ return m;
+}
+
+static inline struct mbuf *slirpDnsMbufAlloc(PNATState pData)
+{
+ return slirpServiceMbufAlloc(pData, CTL_DNS);
+}
+
+DECLINLINE(bool) slirpIsWideCasting(PNATState pData, uint32_t u32Addr)
+{
+ bool fWideCasting;
+ LogFlowFunc(("Enter: u32Addr:%RTnaipv4\n", u32Addr));
+ fWideCasting = ( u32Addr == INADDR_BROADCAST
+ || (u32Addr & RT_H2N_U32_C(~pData->netmask)) == RT_H2N_U32_C(~pData->netmask));
+ LogFlowFunc(("Leave: %RTbool\n", fWideCasting));
+ return fWideCasting;
+}
+#endif
+
diff --git a/src/VBox/Devices/Network/slirp/slirp_config.h b/src/VBox/Devices/Network/slirp/slirp_config.h
new file mode 100644
index 00000000..7ea0c1a8
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/slirp_config.h
@@ -0,0 +1,225 @@
+/* $Id: slirp_config.h $ */
+/** @file
+ * NAT - compile-time configuration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * User definable configuration options
+ */
+
+/* Undefine if you don't want talk emulation */
+#undef EMULATE_TALK
+
+/* Define if you want the connection to be probed */
+/* XXX Not working yet, so ignore this for now */
+#undef PROBE_CONN
+
+/* Define to 1 if you want KEEPALIVE timers */
+#define DO_KEEPALIVE 0
+
+/* Define to MAX interfaces you expect to use at once */
+/* MAX_INTERFACES determines the max. TOTAL number of interfaces (SLIP and PPP) */
+/* MAX_PPP_INTERFACES determines max. number of PPP interfaces */
+#define MAX_INTERFACES 1
+#define MAX_PPP_INTERFACES 1
+
+/* Define if you want slirp's socket in /tmp */
+/* XXXXXX Do this in ./configure */
+#undef USE_TMPSOCKET
+
+/* Define if you want slirp to use cfsetXspeed() on the terminal */
+#undef DO_CFSETSPEED
+
+/* Define this if you want slirp to write to the tty as fast as it can */
+/* This should only be set if you are using load-balancing, slirp does a */
+/* pretty good job on single modems already, and seting this will make */
+/* interactive sessions less responsive */
+/* XXXXX Talk about having fast modem as unit 0 */
+#undef FULL_BOLT
+
+/*
+ * Define if you want slirp to use less CPU
+ * You will notice a small lag in interactive sessions, but it's not that bad
+ * Things like Netscape/ftp/etc. are completely unaffected
+ * This is mainly for sysadmins who have many slirp users
+ */
+#undef USE_LOWCPU
+
+/*********************************************************/
+/*
+ * Autoconf defined configuration options
+ * You shouldn't need to touch any of these
+ */
+
+#ifdef _MSC_VER
+#undef HAVE_UNISTD_H
+#else
+/* Define if you have unistd.h */
+#define HAVE_UNISTD_H
+#endif
+
+/* Define if you have stdlib.h */
+#define HAVE_STDLIB_H
+
+/* Define if you have sys/ioctl.h */
+#undef HAVE_SYS_IOCTL_H
+#ifndef RT_OS_WINDOWS
+# define HAVE_SYS_IOCTL_H
+#endif
+
+/* Define if you have sys/filio.h */
+#undef HAVE_SYS_FILIO_H
+#ifdef __APPLE__
+#define HAVE_SYS_FILIO_H
+#endif
+
+/* Define according to how time.h should be included */
+#define TIME_WITH_SYS_TIME 0
+#undef HAVE_SYS_TIME_H
+
+/* Define if you have sys/bitypes.h */
+#undef HAVE_SYS_BITYPES_H
+
+/* Define if the machine is big endian */
+/*#undef WORDS_BIGENDIAN */
+
+/* Define if you have readv */
+#undef HAVE_READV
+
+/* Define if iovec needs to be declared */
+#undef DECLARE_IOVEC
+#ifdef RT_OS_WINDOWS
+# define DECLARE_IOVEC
+#endif
+
+/* Define if you have a POSIX.1 sys/wait.h */
+#undef HAVE_SYS_WAIT_H
+
+/* Define if you have sys/select.h */
+#undef HAVE_SYS_SELECT_H
+#ifndef RT_OS_WINDOWS
+# define HAVE_SYS_SELECT_H
+#endif
+
+/* Define if you have strings.h */
+#define HAVE_STRING_H
+
+/* Define if you have arpa/inet.h */
+#undef HAVE_ARPA_INET_H
+#ifndef RT_OS_WINDOWS
+# define HAVE_ARPA_INET_H
+#endif
+
+/* Define if you have sys/signal.h */
+#undef HAVE_SYS_SIGNAL_H
+
+/* Define if you have sys/stropts.h */
+#undef HAVE_SYS_STROPTS_H
+
+/* Define to whatever your compiler thinks inline should be */
+#if defined(_MSC_VER) && !defined(__cplusplus)
+# define inline _inline
+#else
+# define inline inline
+#endif
+
+/* Define to whatever your compiler thinks const should be */
+#define const const
+
+/* Define if you don't have u_int32_t etc. typedef'd */
+#undef NEED_TYPEDEFS
+#ifdef __sun__
+#define NEED_TYPEDEFS
+#endif
+
+/* Define to sizeof(char) */
+#define SIZEOF_CHAR 1
+
+/* Define to sizeof(short) */
+#define SIZEOF_SHORT 2
+
+/* Define to sizeof(int) */
+#define SIZEOF_INT 4
+
+/* Define to sizeof(char *) */
+#define HOST_LONG_BITS ARCH_BITS
+#define SIZEOF_CHAR_P (HOST_LONG_BITS / 8)
+
+/* Define if you have random() */
+#undef HAVE_RANDOM
+
+/* Define if you have srandom() */
+#undef HAVE_SRANDOM
+
+/* Define if you have inet_aton */
+#undef HAVE_INET_ATON
+#ifndef RT_OS_WINDOWS
+# define HAVE_INET_ATON
+#endif
+
+/* Define if you have setenv */
+#undef HAVE_SETENV
+
+/* Define if you have index() */
+#undef HAVE_INDEX
+
+/* Define if you have bcmp() */
+#undef HAVE_BCMP
+
+/* Define if you have drand48 */
+#undef HAVE_DRAND48
+
+/* Define if you have memmove */
+#define HAVE_MEMMOVE
+
+/* Define if you have gethostid */
+#undef HAVE_GETHOSTID
+#ifdef RT_OS_OS2
+# define HAVE_GETHOSTID
+#endif
+
+/* Define if you DON'T have unix-domain sockets */
+#undef NO_UNIX_SOCKETS
+#ifdef RT_OS_WINDOWS
+# define NO_UNIX_SOCKETS
+#endif
+
+/* Define if gettimeofday only takes one argument */
+#undef GETTIMEOFDAY_ONE_ARG
+
+/* Define if you have revoke() */
+#undef HAVE_REVOKE
+
+/* Define if you have the sysv method of opening pty's (/dev/ptmx, etc.) */
+#undef HAVE_GRANTPT
+
+/* Define if you have fchmod */
+#undef HAVE_FCHMOD
+
+/* Define if you have <sys/type32.h> */
+#undef HAVE_SYS_TYPES32_H
+#ifdef RT_OS_SOLARIS
+# define HAVE_SYS_TYPES32_H
+#endif
diff --git a/src/VBox/Devices/Network/slirp/slirp_dns.c b/src/VBox/Devices/Network/slirp/slirp_dns.c
new file mode 100644
index 00000000..e3b105fe
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/slirp_dns.c
@@ -0,0 +1,323 @@
+/* $Id: slirp_dns.c $ */
+/** @file
+ * NAT - dns initialization.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#include "slirp.h"
+#ifdef RT_OS_OS2
+# include <paths.h>
+#endif
+
+#include <iprt/errcore.h>
+#include <VBox/vmm/pdmdrv.h>
+#include <iprt/assert.h>
+#include <iprt/file.h>
+
+#ifdef RT_OS_WINDOWS
+# include <iprt/utf16.h>
+# include <Winnls.h>
+# define _WINSOCK2API_
+# include <iprt/win/iphlpapi.h>
+
+static int get_dns_addr_domain(PNATState pData)
+{
+ /*ULONG flags = GAA_FLAG_INCLUDE_PREFIX;*/ /*GAA_FLAG_INCLUDE_ALL_INTERFACES;*/ /* all interfaces registered in NDIS */
+ PIP_ADAPTER_ADDRESSES pAdapterAddr = NULL;
+ PIP_ADAPTER_ADDRESSES pAddr = NULL;
+ PIP_ADAPTER_DNS_SERVER_ADDRESS pDnsAddr = NULL;
+ ULONG size;
+ char *pszSuffix;
+ struct dns_domain_entry *pDomain = NULL;
+ ULONG ret = ERROR_SUCCESS;
+
+ /** @todo add SKIPing flags to get only required information */
+
+ /* determine size of buffer */
+ size = 0;
+ ret = pData->pfnGetAdaptersAddresses(AF_INET, 0, NULL /* reserved */, pAdapterAddr, &size);
+ if (ret != ERROR_BUFFER_OVERFLOW)
+ {
+ Log(("NAT: error %lu occurred on capacity detection operation\n", ret));
+ return -1;
+ }
+ if (size == 0)
+ {
+ Log(("NAT: Win socket API returns non capacity\n"));
+ return -1;
+ }
+
+ pAdapterAddr = RTMemAllocZ(size);
+ if (!pAdapterAddr)
+ {
+ Log(("NAT: No memory available\n"));
+ return -1;
+ }
+ ret = pData->pfnGetAdaptersAddresses(AF_INET, 0, NULL /* reserved */, pAdapterAddr, &size);
+ if (ret != ERROR_SUCCESS)
+ {
+ Log(("NAT: error %lu occurred on fetching adapters info\n", ret));
+ RTMemFree(pAdapterAddr);
+ return -1;
+ }
+
+ for (pAddr = pAdapterAddr; pAddr != NULL; pAddr = pAddr->Next)
+ {
+ int found;
+ if (pAddr->OperStatus != IfOperStatusUp)
+ continue;
+
+ for (pDnsAddr = pAddr->FirstDnsServerAddress; pDnsAddr != NULL; pDnsAddr = pDnsAddr->Next)
+ {
+ struct sockaddr *SockAddr = pDnsAddr->Address.lpSockaddr;
+ struct in_addr InAddr;
+ struct dns_entry *pDns;
+
+ if (SockAddr->sa_family != AF_INET)
+ continue;
+
+ InAddr = ((struct sockaddr_in *)SockAddr)->sin_addr;
+
+ /* add dns server to list */
+ pDns = RTMemAllocZ(sizeof(struct dns_entry));
+ if (!pDns)
+ {
+ Log(("NAT: Can't allocate buffer for DNS entry\n"));
+ RTMemFree(pAdapterAddr);
+ return VERR_NO_MEMORY;
+ }
+
+ Log(("NAT: adding %RTnaipv4 to DNS server list\n", InAddr));
+ if ((InAddr.s_addr & RT_H2N_U32_C(IN_CLASSA_NET)) == RT_N2H_U32_C(INADDR_LOOPBACK & IN_CLASSA_NET))
+ pDns->de_addr.s_addr = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_ALIAS);
+ else
+ pDns->de_addr.s_addr = InAddr.s_addr;
+
+ TAILQ_INSERT_HEAD(&pData->pDnsList, pDns, de_list);
+
+ if (pAddr->DnsSuffix == NULL)
+ continue;
+
+ /* uniq */
+ RTUtf16ToUtf8(pAddr->DnsSuffix, &pszSuffix);
+ if (!pszSuffix || strlen(pszSuffix) == 0)
+ {
+ RTStrFree(pszSuffix);
+ continue;
+ }
+
+ found = 0;
+ LIST_FOREACH(pDomain, &pData->pDomainList, dd_list)
+ {
+ if ( pDomain->dd_pszDomain != NULL
+ && strcmp(pDomain->dd_pszDomain, pszSuffix) == 0)
+ {
+ found = 1;
+ RTStrFree(pszSuffix);
+ break;
+ }
+ }
+ if (!found)
+ {
+ pDomain = RTMemAllocZ(sizeof(struct dns_domain_entry));
+ if (!pDomain)
+ {
+ Log(("NAT: not enough memory\n"));
+ RTStrFree(pszSuffix);
+ RTMemFree(pAdapterAddr);
+ return VERR_NO_MEMORY;
+ }
+ pDomain->dd_pszDomain = pszSuffix;
+ Log(("NAT: adding domain name %s to search list\n", pDomain->dd_pszDomain));
+ LIST_INSERT_HEAD(&pData->pDomainList, pDomain, dd_list);
+ }
+ }
+ }
+ RTMemFree(pAdapterAddr);
+ return 0;
+}
+
+#else /* !RT_OS_WINDOWS */
+
+#include "resolv_conf_parser.h"
+
+static int get_dns_addr_domain(PNATState pData)
+{
+ struct rcp_state st;
+ int rc;
+ unsigned i;
+
+ /* XXX: perhaps IPv6 shouldn't be ignored if we're using DNS proxy */
+ st.rcps_flags = RCPSF_IGNORE_IPV6;
+ rc = rcp_parse(&st, RESOLV_CONF_FILE);
+
+ if (rc < 0)
+ return -1;
+
+ /* for historical reasons: Slirp returns -1 if no nameservers were found */
+ if (st.rcps_num_nameserver == 0)
+ return -1;
+
+
+ /* XXX: We're composing the list, but we already knows
+ * its size so we can allocate array instead (Linux guests
+ * dont like >3 servers in the list anyway)
+ * or use pre-allocated array in NATState.
+ */
+ for (i = 0; i != st.rcps_num_nameserver; ++i)
+ {
+ struct dns_entry *pDns;
+ RTNETADDRU *address = &st.rcps_nameserver[i].uAddr;
+
+ if (address->IPv4.u == INADDR_ANY)
+ {
+ /*
+ * This doesn't seem to be very well documented except for
+ * RTFS of res_init.c, but INADDR_ANY is a valid value for
+ * for "nameserver".
+ */
+ address->IPv4.u = RT_H2N_U32_C(INADDR_LOOPBACK);
+ }
+
+ if ( (address->IPv4.u & RT_H2N_U32_C(IN_CLASSA_NET))
+ == RT_N2H_U32_C(INADDR_LOOPBACK & IN_CLASSA_NET))
+ {
+ /**
+ * XXX: Note shouldn't patch the address in case of using DNS proxy,
+ * because DNS proxy we do revert it back actually.
+ */
+ if ( address->IPv4.u == RT_N2H_U32_C(INADDR_LOOPBACK)
+ && pData->fLocalhostReachable)
+ address->IPv4.u = RT_H2N_U32(RT_N2H_U32(pData->special_addr.s_addr) | CTL_ALIAS);
+ else if (pData->fUseDnsProxy == 0) {
+ /*
+ * Either the resolver lives somewhere else on the 127/8 network or the loopback interface
+ * is blocked for access from the guest, either way switch to the DNS proxy.
+ */
+ if (pData->fLocalhostReachable)
+ LogRel(("NAT: DNS server %RTnaipv4 registration detected, switching to the DNS proxy\n", address->IPv4));
+ else
+ LogRel(("NAT: Switching to DNS proxying due to access to the loopback interface being blocked\n"));
+ pData->fUseDnsProxy = 1;
+ }
+ }
+
+ pDns = RTMemAllocZ(sizeof(struct dns_entry));
+ if (pDns == NULL)
+ {
+ slirpReleaseDnsSettings(pData);
+ return VERR_NO_MEMORY;
+ }
+
+ pDns->de_addr.s_addr = address->IPv4.u;
+ TAILQ_INSERT_HEAD(&pData->pDnsList, pDns, de_list);
+ }
+
+ if (st.rcps_domain != 0)
+ {
+ struct dns_domain_entry *pDomain = RTMemAllocZ(sizeof(struct dns_domain_entry));
+ if (pDomain == NULL)
+ {
+ slirpReleaseDnsSettings(pData);
+ return -1;
+ }
+
+ pDomain->dd_pszDomain = RTStrDup(st.rcps_domain);
+ LogRel(("NAT: Adding domain name %s\n", pDomain->dd_pszDomain));
+ LIST_INSERT_HEAD(&pData->pDomainList, pDomain, dd_list);
+ }
+
+ return 0;
+}
+
+#endif /* !RT_OS_WINDOWS */
+
+int slirpInitializeDnsSettings(PNATState pData)
+{
+ int rc = VINF_SUCCESS;
+ AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
+ LogFlowFuncEnter();
+ if (!pData->fUseHostResolverPermanent)
+ {
+ TAILQ_INIT(&pData->pDnsList);
+ LIST_INIT(&pData->pDomainList);
+
+ /*
+ * Some distributions haven't got /etc/resolv.conf
+ * so we should other way to configure DNS settings.
+ */
+ if (get_dns_addr_domain(pData) < 0)
+ pData->fUseHostResolver = true;
+ else
+ {
+ pData->fUseHostResolver = false;
+ dnsproxy_init(pData);
+ }
+
+ if (!pData->fUseHostResolver)
+ {
+ struct dns_entry *pDNSEntry = NULL;
+ int cDNSListEntry = 0;
+ TAILQ_FOREACH_REVERSE(pDNSEntry, &pData->pDnsList, dns_list_head, de_list)
+ {
+ LogRel(("NAT: DNS#%i: %RTnaipv4\n", cDNSListEntry, pDNSEntry->de_addr.s_addr));
+ cDNSListEntry++;
+ }
+ }
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+int slirpReleaseDnsSettings(PNATState pData)
+{
+ struct dns_entry *pDns = NULL;
+ struct dns_domain_entry *pDomain = NULL;
+ int rc = VINF_SUCCESS;
+ AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
+ LogFlowFuncEnter();
+
+ while (!TAILQ_EMPTY(&pData->pDnsList))
+ {
+ pDns = TAILQ_FIRST(&pData->pDnsList);
+ TAILQ_REMOVE(&pData->pDnsList, pDns, de_list);
+ RTMemFree(pDns);
+ }
+
+ while (!LIST_EMPTY(&pData->pDomainList))
+ {
+ pDomain = LIST_FIRST(&pData->pDomainList);
+ LIST_REMOVE(pDomain, dd_list);
+ if (pDomain->dd_pszDomain != NULL)
+ RTStrFree(pDomain->dd_pszDomain);
+ RTMemFree(pDomain);
+ }
+
+ /* tell any pending dnsproxy requests their copy is expired */
+ ++pData->dnsgen;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
diff --git a/src/VBox/Devices/Network/slirp/slirp_dns.h b/src/VBox/Devices/Network/slirp/slirp_dns.h
new file mode 100644
index 00000000..c2ee1807
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/slirp_dns.h
@@ -0,0 +1,32 @@
+/* $Id: slirp_dns.h $ */
+/** @file
+ * NAT - Slirp's dns header.
+ */
+
+/*
+ * Copyright (C) 2012-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+#ifndef _SLIRP_DNS_H_
+#define _SLIRP_DNS_H_
+int slirpInitializeDnsSettings(PNATState pData);
+int slirpReleaseDnsSettings(PNATState pData);
+#endif
+
diff --git a/src/VBox/Devices/Network/slirp/slirp_state.h b/src/VBox/Devices/Network/slirp/slirp_state.h
new file mode 100644
index 00000000..5bf59825
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/slirp_state.h
@@ -0,0 +1,548 @@
+/** @file
+ * NAT - slirp state/configuration.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef ___slirp_state_h
+#define ___slirp_state_h
+
+#include <iprt/req.h>
+#include <iprt/critsect.h>
+
+#define COUNTERS_INIT
+#include "counters.h"
+
+#include "ip_icmp.h"
+#include "dnsproxy/dnsproxy.h"
+
+
+/** Where to start DHCP IP number allocation. */
+#define START_ADDR 15
+
+/** DHCP Lease time. */
+#define LEASE_TIME (24 * 3600)
+
+/*
+ * ARP cache this is naive implementaion of ARP
+ * cache of mapping 4 byte IPv4 address to 6 byte
+ * ethernet one.
+ */
+struct arp_cache_entry
+{
+ uint32_t ip;
+ uint8_t ether[6];
+ LIST_ENTRY(arp_cache_entry) list;
+};
+LIST_HEAD(arp_cache_head, arp_cache_entry);
+
+/** TFTP session entry. */
+struct dns_domain_entry
+{
+ char *dd_pszDomain;
+ LIST_ENTRY(dns_domain_entry) dd_list;
+};
+LIST_HEAD(dns_domain_list_head, dns_domain_entry);
+
+#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
+typedef struct DNSMAPPINGENTRY
+{
+ /** Literal or pattern. */
+ bool fPattern;
+ /** Host name or pattern to map. */
+ char *pszName;
+ /** The IP Address. */
+ uint32_t u32IpAddress;
+ /** List entry. */
+ STAILQ_ENTRY(DNSMAPPINGENTRY) MapList;
+} DNSMAPPINGENTRY, *PDNSMAPPINGENTRY;
+typedef STAILQ_HEAD(DNSMAPPINGHEAD, DNSMAPPINGENTRY) DNSMAPPINGHEAD;
+#endif
+
+struct dns_entry
+{
+ struct in_addr de_addr;
+ TAILQ_ENTRY(dns_entry) de_list;
+};
+TAILQ_HEAD(dns_list_head, dns_entry);
+TAILQ_HEAD(if_queue, mbuf);
+
+struct port_forward_rule
+{
+ uint16_t proto;
+ uint16_t host_port;
+ uint16_t guest_port;
+ struct in_addr guest_addr;
+ struct in_addr bind_ip;
+ int activated;
+ struct socket *so;
+ LIST_ENTRY(port_forward_rule) list;
+};
+LIST_HEAD(port_forward_rule_list, port_forward_rule);
+
+
+#ifdef RT_OS_WINDOWS
+struct pong;
+TAILQ_HEAD(pong_tailq, pong);
+#endif
+
+/* forward declaration */
+struct proto_handler;
+
+/** Main state/configuration structure for slirp NAT. */
+typedef struct NATState
+{
+#define PROFILE_COUNTER(name, dsc) STAMPROFILE Stat ## name
+#define COUNTING_COUNTER(name, dsc) STAMCOUNTER Stat ## name
+#include "counters.h"
+ /* Stuff from boot.c */
+ void *pbootp_clients;
+ const char *bootp_filename;
+ /* Stuff from if.c */
+ int if_mtu, if_mru;
+ int if_comp;
+ int if_maxlinkhdr;
+ int if_queued;
+ int if_thresh;
+ /* Stuff from icmp.c */
+ struct icmpstat_t icmpstat;
+ /* Stuff from ip_input.c */
+ struct ipstat_t ipstat;
+ struct ipqhead ipq[IPREASS_NHASH];
+ int maxnipq; /* Administrative limit on # of reass queues*/
+ int maxfragsperpacket; /* Maximum number of IPv4 fragments allowed per packet */
+ int nipq; /* total number of reass queues */
+ uint16_t ip_currid;
+ /* Stuff from mbuf.c */
+ /* Stuff from slirp.c */
+ void *pvUser;
+ uint32_t curtime;
+ uint32_t time_fasttimo;
+ uint32_t last_slowtimo;
+ bool do_slowtimo;
+ bool link_up;
+ struct timeval tt;
+ struct in_addr our_addr;
+ struct in_addr alias_addr;
+ struct in_addr special_addr;
+ struct in_addr guest_addr_guess;
+
+ int tcp_rcvspace;
+ int tcp_sndspace;
+ int socket_rcv;
+ int socket_snd;
+ int soMaxConn;
+#ifdef RT_OS_WINDOWS
+ ULONG (WINAPI * pfnGetAdaptersAddresses)(ULONG, ULONG, PVOID, PIP_ADAPTER_ADDRESSES, PULONG);
+#endif
+ struct dns_list_head pDnsList;
+ struct dns_domain_list_head pDomainList;
+ uint32_t dnsgen; /* XXX: merge with dnsLastUpdate? */
+ struct in_addr tftp_server;
+ struct in_addr loopback_addr;
+ uint32_t dnsLastUpdate;
+ uint32_t netmask;
+ const uint8_t *slirp_ethaddr;
+ char slirp_hostname[33];
+ bool fPassDomain;
+ struct in_addr bindIP;
+ /* Stuff from tcp_input.c */
+ struct socket tcb;
+
+ struct socket *tcp_last_so;
+ tcp_seq tcp_iss;
+ /* Stuff from tcp_timer.c */
+ struct tcpstat_t tcpstat;
+ uint32_t tcp_now;
+ int tcp_reass_qsize;
+ int tcp_reass_maxqlen;
+ int tcp_reass_maxseg;
+ int tcp_reass_overflows;
+ /* Stuff from tftp.c */
+ void *pvTftpSessions;
+ int cTftpSession;
+ const char *tftp_prefix;
+ /* Stuff from udp.c */
+ struct udpstat_t udpstat;
+ struct socket udb;
+ struct socket *udp_last_so;
+
+# ifndef RT_OS_WINDOWS
+ /* counter of sockets needed for allocation enough room to
+ * process sockets with poll/epoll
+ *
+ * NSOCK_INC/DEC should be injected before every
+ * operation on socket queue (tcb, udb)
+ */
+ int nsock;
+# define NSOCK_INC() do {pData->nsock++;} while (0)
+# define NSOCK_DEC() do {pData->nsock--;} while (0)
+# define NSOCK_INC_EX(ex) do {ex->pData->nsock++;} while (0)
+# define NSOCK_DEC_EX(ex) do {ex->pData->nsock--;} while (0)
+# else
+# define NSOCK_INC() do {} while (0)
+# define NSOCK_DEC() do {} while (0)
+# define NSOCK_INC_EX(ex) do {} while (0)
+# define NSOCK_DEC_EX(ex) do {} while (0)
+# endif
+
+ struct socket icmp_socket;
+# if !defined(RT_OS_WINDOWS)
+ struct icmp_storage icmp_msg_head;
+ int cIcmpCacheSize;
+ int iIcmpCacheLimit;
+# else
+ struct pong_tailq pongs_expected;
+ struct pong_tailq pongs_received;
+ size_t cbIcmpPending;
+# endif
+
+#if defined(RT_OS_WINDOWS)
+# define VBOX_SOCKET_EVENT (pData->phEvents[VBOX_SOCKET_EVENT_INDEX])
+ HANDLE phEvents[VBOX_EVENT_COUNT];
+#endif
+#ifdef zone_mbuf
+# undef zone_mbuf
+#endif
+ uma_zone_t zone_mbuf;
+#ifdef zone_clust
+# undef zone_clust
+#endif
+ uma_zone_t zone_clust;
+#ifdef zone_pack
+# undef zone_pack
+#endif
+ uma_zone_t zone_pack;
+#ifdef zone_jumbop
+# undef zone_jumbop
+#endif
+ uma_zone_t zone_jumbop;
+#ifdef zone_jumbo9
+# undef zone_jumbo9
+#endif
+ uma_zone_t zone_jumbo9;
+#ifdef zone_jumbo16
+# undef zone_jumbo16
+#endif
+ uma_zone_t zone_jumbo16;
+#ifdef zone_ext_refcnt
+# undef zone_ext_refcnt
+ int nmbclusters; /* limits number of mbuf clusters */
+ int nmbjumbop; /* limits number of page size jumbo clusters */
+ int nmbjumbo9; /* limits number of 9k jumbo clusters */
+ int nmbjumbo16; /* limits number of 16k jumbo clusters */
+ struct mbstat mbstat;
+#endif
+ uma_zone_t zone_ext_refcnt;
+ /**
+ * in (r89055) using of this behaviour has been changed and mean that Slirp
+ * can't parse hosts strucutures/files to provide to guest host name-resolving
+ * configuration, instead Slirp provides .{interface-number + 1}.3 as a nameserver
+ * and proxies DNS queiries to Host's Name Resolver API.
+ */
+ bool fUseHostResolver;
+ /**
+ * Flag whether using the host resolver mode is permanent
+ * because the user configured it that way.
+ */
+ bool fUseHostResolverPermanent;
+ /* from dnsproxy/dnsproxy.h*/
+ unsigned int authoritative_port;
+ unsigned int authoritative_timeout;
+ unsigned int recursive_port;
+ unsigned int recursive_timeout;
+ unsigned int stats_timeout;
+ unsigned int port;
+
+ unsigned long active_queries;
+ unsigned long all_queries;
+ unsigned long authoritative_queries;
+ unsigned long recursive_queries;
+ unsigned long removed_queries;
+ unsigned long dropped_queries;
+ unsigned long answered_queries;
+ unsigned long dropped_answers;
+ unsigned long late_answers;
+ unsigned long hash_collisions;
+ /*dnsproxy/dnsproxy.c*/
+ unsigned short queryid;
+ struct sockaddr_in authoritative_addr;
+ struct sockaddr_in recursive_addr;
+ int sock_query;
+ int sock_answer;
+ /* dnsproxy/hash.c */
+#define HASHSIZE 10
+#define HASH(id) (id & ((1 << HASHSIZE) - 1))
+ struct request *request_hash[1 << HASHSIZE];
+ /* this field control behaviour of DHCP server */
+ bool fUseDnsProxy;
+ /** Flag whether the guest can contact services on the host's
+ * loopback interface (127.0.0.1/localhost). */
+ bool fLocalhostReachable;
+
+ LIST_HEAD(RT_NOTHING, libalias) instancehead;
+ int i32AliasMode;
+ struct libalias *proxy_alias;
+ LIST_HEAD(handler_chain, proto_handler) handler_chain;
+ /** Critical R/W section to protect the handler chain list. */
+ RTCRITSECTRW CsRwHandlerChain;
+ struct port_forward_rule_list port_forward_rule_head;
+ struct arp_cache_head arp_cache;
+ /* libalis modules' handlers*/
+ struct proto_handler *ftp_module;
+ struct proto_handler *nbt_module;
+#ifdef VBOX_WITH_NAT_SEND2HOME
+ /* array of home addresses */
+ struct sockaddr_in *pInSockAddrHomeAddress;
+ /* size of pInSockAddrHomeAddress in elements */
+ int cInHomeAddressSize;
+#endif
+#ifdef VBOX_WITH_DNSMAPPING_IN_HOSTRESOLVER
+ DNSMAPPINGHEAD DNSMapNames;
+ DNSMAPPINGHEAD DNSMapPatterns;
+#endif
+} NATState;
+
+
+/** Default IP time to live. */
+#define ip_defttl IPDEFTTL
+
+/** Number of permanent buffers in mbuf. */
+#define mbuf_thresh 30
+
+/** Use a fixed time before sending keepalive. */
+#define tcp_keepidle TCPTV_KEEP_IDLE
+
+/** Use a fixed interval between keepalive. */
+#define tcp_keepintvl TCPTV_KEEPINTVL
+
+/** Maximum idle time before timing out a connection. */
+#define tcp_maxidle (TCPTV_KEEPCNT * tcp_keepintvl)
+
+/** Default TCP socket options. */
+#define so_options DO_KEEPALIVE
+
+/** Default TCP MSS value. */
+#define tcp_mssdflt TCP_MSS
+
+/** Default TCP round trip time. */
+#define tcp_rttdflt (TCPTV_SRTTDFLT / PR_SLOWHZ)
+
+/** Enable RFC1323 performance enhancements.
+ * @todo check if it really works, it was turned off before. */
+#define tcp_do_rfc1323 1
+
+/** TCP receive buffer size. */
+#define tcp_rcvspace pData->tcp_rcvspace
+
+/** TCP receive buffer size. */
+#define tcp_sndspace pData->tcp_sndspace
+
+/* TCP duplicate ACK retransmit threshold. */
+#define tcprexmtthresh 3
+
+
+#define bootp_filename pData->bootp_filename
+
+#define if_mtu pData->if_mtu
+#define if_mru pData->if_mru
+#define if_comp pData->if_comp
+#define if_maxlinkhdr pData->if_maxlinkhdr
+#define if_queued pData->if_queued
+#define if_thresh pData->if_thresh
+
+#define icmpstat pData->icmpstat
+
+#define ipstat pData->ipstat
+#define ipq pData->ipq
+#define ip_currid pData->ip_currid
+
+#define mbuf_alloced pData->mbuf_alloced
+#define mbuf_max pData->mbuf_max
+#define msize pData->msize
+#define m_freelist pData->m_freelist
+#define m_usedlist pData->m_usedlist
+
+#define curtime pData->curtime
+#define time_fasttimo pData->time_fasttimo
+#define last_slowtimo pData->last_slowtimo
+#define do_slowtimo pData->do_slowtimo
+#define link_up pData->link_up
+#define cUsers pData->cUsers
+#define tt pData->tt
+#define our_addr pData->our_addr
+#ifndef VBOX_SLIRP_ALIAS
+# define alias_addr pData->alias_addr
+#else
+# define handler_chain pData->handler_chain
+#endif
+#define dns_addr pData->dns_addr
+#define loopback_addr pData->loopback_addr
+#define slirp_hostname pData->slirp_hostname
+
+#define tcb pData->tcb
+#define tcp_last_so pData->tcp_last_so
+#define tcp_iss pData->tcp_iss
+
+#define tcpstat pData->tcpstat
+#define tcp_now pData->tcp_now
+
+#define tftp_prefix pData->tftp_prefix
+
+#define udpstat pData->udpstat
+#define udb pData->udb
+#define udp_last_so pData->udp_last_so
+
+#define maxfragsperpacket pData->maxfragsperpacket
+#define maxnipq pData->maxnipq
+#define nipq pData->nipq
+
+#define tcp_reass_qsize pData->tcp_reass_qsize
+#define tcp_reass_maxqlen pData->tcp_reass_maxqlen
+#define tcp_reass_maxseg pData->tcp_reass_maxseg
+#define tcp_reass_overflows pData->tcp_reass_overflows
+
+#define queue_tcp_label tcb
+#define queue_udp_label udb
+#define VBOX_X2(x) x
+#define VBOX_X(x) VBOX_X2(x)
+
+#if 1
+
+# define QSOCKET_LOCK(queue) do {} while (0)
+# define QSOCKET_UNLOCK(queue) do {} while (0)
+# define QSOCKET_LOCK_CREATE(queue) do {} while (0)
+# define QSOCKET_LOCK_DESTROY(queue) do {} while (0)
+# define QSOCKET_FOREACH(so, sonext, label) \
+ for ((so) = VBOX_X2(queue_ ## label ## _label).so_next; \
+ (so) != &(VBOX_X2(queue_ ## label ## _label)); \
+ (so) = (sonext)) \
+ { \
+ (sonext) = (so)->so_next; \
+ Log5(("%s:%d Processing so:%R[natsock]\n", RT_GCC_EXTENSION __FUNCTION__, __LINE__, (so)));
+# define CONTINUE(label) continue
+# define CONTINUE_NO_UNLOCK(label) continue
+# define LOOP_LABEL(label, so, sonext) /* empty*/
+# define DO_TCP_OUTPUT(data, sotcb) tcp_output((data), (sotcb))
+# define DO_TCP_INPUT(data, mbuf, size, so) tcp_input((data), (mbuf), (size), (so))
+# define DO_TCP_CONNECT(data, so) tcp_connect((data), (so))
+# define DO_SOREAD(ret, data, so, ifclose) \
+ do { \
+ (ret) = soread((data), (so), (ifclose)); \
+ } while(0)
+# define DO_SOWRITE(ret, data, so) \
+ do { \
+ (ret) = sowrite((data), (so)); \
+ } while(0)
+# define DO_SORECFROM(data, so) sorecvfrom((data), (so))
+# define SOLOOKUP(so, label, src, sport, dst, dport) \
+ do { \
+ (so) = solookup(&VBOX_X2(queue_ ## label ## _label), (src), (sport), (dst), (dport)); \
+ } while (0)
+# define DO_UDP_DETACH(data, so, ignored) udp_detach((data), (so))
+
+#endif
+
+#define TCP_OUTPUT(data, sotcb) DO_TCP_OUTPUT((data), (sotcb))
+#define TCP_INPUT(data, mbuf, size, so) DO_TCP_INPUT((data), (mbuf), (size), (so))
+#define TCP_CONNECT(data, so) DO_TCP_CONNECT((data), (so))
+#define SOREAD(ret, data, so, ifclose) DO_SOREAD((ret), (data), (so), (ifclose))
+#define SOWRITE(ret, data, so) DO_SOWRITE((ret), (data), (so))
+#define SORECVFROM(data, so) DO_SORECFROM((data), (so))
+#define UDP_DETACH(data, so, so_next) DO_UDP_DETACH((data), (so), (so_next))
+
+/* dnsproxy/dnsproxy.c */
+#define authoritative_port pData->authoritative_port
+#define authoritative_timeout pData->authoritative_timeout
+#define recursive_port pData->recursive_port
+#define recursive_timeout pData->recursive_timeout
+#define stats_timeout pData->stats_timeout
+/* dnsproxy/hash.c */
+#define dns_port pData->port
+#define request_hash pData->request_hash
+#define hash_collisions pData->hash_collisions
+#define active_queries pData->active_queries
+#define all_queries pData->all_queries
+#define authoritative_queries pData->authoritative_queries
+#define recursive_queries pData->recursive_queries
+#define removed_queries pData->removed_queries
+#define dropped_queries pData->dropped_queries
+#define answered_queries pData->answered_queries
+#define dropped_answers pData->dropped_answers
+#define late_answers pData->late_answers
+
+/* dnsproxy/dnsproxy.c */
+#define queryid pData->queryid
+#define authoritative_addr pData->authoritative_addr
+#define recursive_addr pData->recursive_addr
+#define sock_query pData->sock_query
+#define sock_answer pData->sock_answer
+
+#define instancehead pData->instancehead
+
+#define nmbclusters pData->nmbclusters
+#define nmbjumbop pData->nmbjumbop
+#define nmbjumbo9 pData->nmbjumbo9
+#define nmbjumbo16 pData->nmbjumbo16
+#define mbstat pData->mbstat
+#include "ext.h"
+#undef zone_mbuf
+#undef zone_clust
+#undef zone_pack
+#undef zone_jumbop
+#undef zone_jumbo9
+#undef zone_jumbo16
+#undef zone_ext_refcnt
+static inline uma_zone_t slirp_zone_pack(PNATState pData)
+{
+ return pData->zone_pack;
+}
+static inline uma_zone_t slirp_zone_jumbop(PNATState pData)
+{
+ return pData->zone_jumbop;
+}
+static inline uma_zone_t slirp_zone_jumbo9(PNATState pData)
+{
+ return pData->zone_jumbo9;
+}
+static inline uma_zone_t slirp_zone_jumbo16(PNATState pData)
+{
+ return pData->zone_jumbo16;
+}
+static inline uma_zone_t slirp_zone_ext_refcnt(PNATState pData)
+{
+ return pData->zone_ext_refcnt;
+}
+static inline uma_zone_t slirp_zone_mbuf(PNATState pData)
+{
+ return pData->zone_mbuf;
+}
+static inline uma_zone_t slirp_zone_clust(PNATState pData)
+{
+ return pData->zone_clust;
+}
+#ifndef VBOX_SLIRP_BSD
+# define m_adj(m, len) m_adj(pData, (m), (len))
+#endif
+
+#endif /* !___slirp_state_h */
diff --git a/src/VBox/Devices/Network/slirp/socket.c b/src/VBox/Devices/Network/slirp/socket.c
new file mode 100644
index 00000000..c12a2ed9
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/socket.c
@@ -0,0 +1,1497 @@
+/* $Id: socket.c $ */
+/** @file
+ * NAT - socket handling.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include "ip_icmp.h"
+#include "main.h"
+#ifdef __sun__
+#include <sys/filio.h>
+#endif
+#include <VBox/vmm/pdmdrv.h>
+#if defined (RT_OS_WINDOWS)
+#include <iprt/win/iphlpapi.h>
+#include <icmpapi.h>
+#endif
+#include <alias.h>
+
+#if defined(DECLARE_IOVEC) && defined(RT_OS_WINDOWS)
+AssertCompileMembersSameSizeAndOffset(struct iovec, iov_base, WSABUF, buf);
+AssertCompileMembersSameSizeAndOffset(struct iovec, iov_len, WSABUF, len);
+#endif
+
+#ifdef VBOX_WITH_NAT_SEND2HOME
+DECLINLINE(bool) slirpSend2Home(PNATState pData, struct socket *pSo, const void *pvBuf, uint32_t cbBuf, int iFlags)
+{
+ int idxAddr;
+ int ret = 0;
+ bool fSendDone = false;
+ LogFlowFunc(("Enter pSo:%R[natsock] pvBuf: %p, cbBuf: %d, iFlags: %d\n", pSo, pvBuf, cbBuf, iFlags));
+ for (idxAddr = 0; idxAddr < pData->cInHomeAddressSize; ++idxAddr)
+ {
+
+ struct socket *pNewSocket = soCloneUDPSocketWithForegnAddr(pData, pSo, pData->pInSockAddrHomeAddress[idxAddr].sin_addr);
+ AssertReturn((pNewSocket, false));
+ pData->pInSockAddrHomeAddress[idxAddr].sin_port = pSo->so_fport;
+ /** @todo more verbose on errors,
+ * @note: we shouldn't care if this send fail or not (we're in broadcast).
+ */
+ LogFunc(("send %d bytes to %RTnaipv4 from %R[natsock]\n", cbBuf, pData->pInSockAddrHomeAddress[idxAddr].sin_addr.s_addr, pNewSocket));
+ ret = sendto(pNewSocket->s, pvBuf, cbBuf, iFlags, (struct sockaddr *)&pData->pInSockAddrHomeAddress[idxAddr], sizeof(struct sockaddr_in));
+ if (ret < 0)
+ LogFunc(("Failed to send %d bytes to %RTnaipv4\n", cbBuf, pData->pInSockAddrHomeAddress[idxAddr].sin_addr.s_addr));
+ fSendDone |= ret > 0;
+ }
+ LogFlowFunc(("Leave %RTbool\n", fSendDone));
+ return fSendDone;
+}
+#endif /* !VBOX_WITH_NAT_SEND2HOME */
+
+#if !defined(RT_OS_WINDOWS)
+static void send_icmp_to_guest(PNATState, char *, size_t, const struct sockaddr_in *);
+static void sorecvfrom_icmp_unix(PNATState, struct socket *);
+#endif /* !RT_OS_WINDOWS */
+
+void
+so_init(void)
+{
+}
+
+struct socket *
+solookup(struct socket *head, struct in_addr laddr,
+ u_int lport, struct in_addr faddr, u_int fport)
+{
+ struct socket *so;
+
+ for (so = head->so_next; so != head; so = so->so_next)
+ {
+ if ( so->so_lport == lport
+ && so->so_laddr.s_addr == laddr.s_addr
+ && so->so_faddr.s_addr == faddr.s_addr
+ && so->so_fport == fport)
+ return so;
+ }
+
+ return (struct socket *)NULL;
+}
+
+/*
+ * Create a new socket, initialise the fields
+ * It is the responsibility of the caller to
+ * insque() it into the correct linked-list
+ */
+struct socket *
+socreate(void)
+{
+ struct socket *so;
+
+ so = (struct socket *)RTMemAllocZ(sizeof(struct socket));
+ if (so)
+ {
+ so->so_state = SS_NOFDREF;
+ so->s = -1;
+#if !defined(RT_OS_WINDOWS)
+ so->so_poll_index = -1;
+#endif
+ }
+ return so;
+}
+
+/*
+ * remque and free a socket, clobber cache
+ */
+void
+sofree(PNATState pData, struct socket *so)
+{
+ LogFlowFunc(("ENTER:%R[natsock]\n", so));
+ /*
+ * We should not remove socket when polling routine do the polling
+ * instead we mark it for deletion.
+ */
+ if (so->fUnderPolling)
+ {
+ so->fShouldBeRemoved = 1;
+ LogFlowFunc(("LEAVE:%R[natsock] postponed deletion\n", so));
+ return;
+ }
+ /**
+ * Check that we don't freeng socket with tcbcb
+ */
+ Assert(!sototcpcb(so));
+ /* udp checks */
+ Assert(!so->so_timeout);
+ Assert(!so->so_timeout_arg);
+ if (so == tcp_last_so)
+ tcp_last_so = &tcb;
+ else if (so == udp_last_so)
+ udp_last_so = &udb;
+
+ /* check if mbuf haven't been already freed */
+ if (so->so_m != NULL)
+ {
+ m_freem(pData, so->so_m);
+ so->so_m = NULL;
+ }
+
+ if (so->so_ohdr != NULL)
+ {
+ RTMemFree(so->so_ohdr);
+ so->so_ohdr = NULL;
+ }
+
+ if (so->so_next && so->so_prev)
+ {
+ remque(pData, so); /* crashes if so is not in a queue */
+ NSOCK_DEC();
+ }
+
+ RTMemFree(so);
+ LogFlowFuncLeave();
+}
+
+
+/*
+ * Worker for sobind() below.
+ */
+static int
+sobindto(struct socket *so, uint32_t addr, uint16_t port)
+{
+ struct sockaddr_in self;
+ int status;
+
+ if (addr == INADDR_ANY && port == 0 && so->so_type != IPPROTO_UDP)
+ {
+ /* TCP sockets without constraints don't need to be bound */
+ Log2(("NAT: sobind: %s guest %RTnaipv4:%d - nothing to do\n",
+ so->so_type == IPPROTO_UDP ? "udp" : "tcp",
+ so->so_laddr.s_addr, ntohs(so->so_lport)));
+ return 0;
+ }
+
+ RT_ZERO(self);
+#ifdef RT_OS_DARWIN
+ self.sin_len = sizeof(self);
+#endif
+ self.sin_family = AF_INET;
+ self.sin_addr.s_addr = addr;
+ self.sin_port = port;
+
+ status = bind(so->s, (struct sockaddr *)&self, sizeof(self));
+ if (status == 0)
+ {
+ Log2(("NAT: sobind: %s guest %RTnaipv4:%d to host %RTnaipv4:%d\n",
+ so->so_type == IPPROTO_UDP ? "udp" : "tcp",
+ so->so_laddr.s_addr, ntohs(so->so_lport), addr, ntohs(port)));
+ return 0;
+ }
+
+ Log2(("NAT: sobind: %s guest %RTnaipv4:%d to host %RTnaipv4:%d error %d%s\n",
+ so->so_type == IPPROTO_UDP ? "udp" : "tcp",
+ so->so_laddr.s_addr, ntohs(so->so_lport),
+ addr, ntohs(port),
+ errno, port ? " (will retry with random port)" : ""));
+
+ if (port) /* retry without */
+ status = sobindto(so, addr, 0);
+
+ if (addr)
+ return status;
+ else
+ return 0;
+}
+
+
+/*
+ * Bind the socket to specific host address and/or port if necessary.
+ * We also always bind udp sockets to force the local port to be
+ * allocated and known in advance.
+ */
+int
+sobind(PNATState pData, struct socket *so)
+{
+ uint32_t addr = pData->bindIP.s_addr; /* may be INADDR_ANY */
+ bool fSamePorts = !!(pData->i32AliasMode & PKT_ALIAS_SAME_PORTS);
+ uint16_t port;
+ int status;
+
+ if (fSamePorts)
+ {
+ int opt = 1;
+ setsockopt(so->s, SOL_SOCKET, SO_REUSEADDR, (char *)&opt, sizeof(opt));
+ port = so->so_lport;
+ }
+ else
+ {
+ port = 0;
+ }
+
+ status = sobindto(so, addr, port);
+ return status;
+}
+
+
+/*
+ * Read from so's socket into sb_snd, updating all relevant sbuf fields
+ * NOTE: This will only be called if it is select()ed for reading, so
+ * a read() of 0 (or less) means it's disconnected
+ */
+int
+soread(PNATState pData, struct socket *so)
+{
+ int n, nn, lss, total;
+ struct sbuf *sb = &so->so_snd;
+ u_int len = sb->sb_datalen - sb->sb_cc;
+ struct iovec iov[2];
+ int mss = so->so_tcpcb->t_maxseg;
+ int sockerr;
+
+ STAM_PROFILE_START(&pData->StatIOread, a);
+ STAM_COUNTER_RESET(&pData->StatIORead_in_1);
+ STAM_COUNTER_RESET(&pData->StatIORead_in_2);
+
+ QSOCKET_LOCK(tcb);
+ SOCKET_LOCK(so);
+ QSOCKET_UNLOCK(tcb);
+
+ LogFlow(("soread: so = %R[natsock]\n", so));
+ Log2(("%s: so = %R[natsock] so->so_snd = %R[sbuf]\n", RT_GCC_EXTENSION __PRETTY_FUNCTION__, so, sb));
+
+ /*
+ * No need to check if there's enough room to read.
+ * soread wouldn't have been called if there weren't
+ */
+
+ len = sb->sb_datalen - sb->sb_cc;
+
+ iov[0].iov_base = sb->sb_wptr;
+ iov[1].iov_base = 0;
+ iov[1].iov_len = 0;
+ if (sb->sb_wptr < sb->sb_rptr)
+ {
+ iov[0].iov_len = sb->sb_rptr - sb->sb_wptr;
+ /* Should never succeed, but... */
+ if (iov[0].iov_len > len)
+ iov[0].iov_len = len;
+ if (iov[0].iov_len > mss)
+ iov[0].iov_len -= iov[0].iov_len%mss;
+ n = 1;
+ }
+ else
+ {
+ iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_wptr;
+ /* Should never succeed, but... */
+ if (iov[0].iov_len > len)
+ iov[0].iov_len = len;
+ len -= iov[0].iov_len;
+ if (len)
+ {
+ iov[1].iov_base = sb->sb_data;
+ iov[1].iov_len = sb->sb_rptr - sb->sb_data;
+ if (iov[1].iov_len > len)
+ iov[1].iov_len = len;
+ total = iov[0].iov_len + iov[1].iov_len;
+ if (total > mss)
+ {
+ lss = total % mss;
+ if (iov[1].iov_len > lss)
+ {
+ iov[1].iov_len -= lss;
+ n = 2;
+ }
+ else
+ {
+ lss -= iov[1].iov_len;
+ iov[0].iov_len -= lss;
+ n = 1;
+ }
+ }
+ else
+ n = 2;
+ }
+ else
+ {
+ if (iov[0].iov_len > mss)
+ iov[0].iov_len -= iov[0].iov_len%mss;
+ n = 1;
+ }
+ }
+
+#ifdef HAVE_READV
+ nn = readv(so->s, (struct iovec *)iov, n);
+#else
+ nn = recv(so->s, iov[0].iov_base, iov[0].iov_len, (so->so_tcpcb->t_force? MSG_OOB:0));
+#endif
+ if (nn < 0)
+ sockerr = errno; /* save it, as it may be clobbered by logging */
+ else
+ sockerr = 0;
+
+ Log2(("%s: read(1) nn = %d bytes\n", RT_GCC_EXTENSION __PRETTY_FUNCTION__, nn));
+ Log2(("%s: so = %R[natsock] so->so_snd = %R[sbuf]\n", RT_GCC_EXTENSION __PRETTY_FUNCTION__, so, sb));
+ if (nn <= 0)
+ {
+#ifdef RT_OS_WINDOWS
+ /*
+ * Windows reports ESHUTDOWN after SHUT_RD (SD_RECEIVE)
+ * instead of just returning EOF indication.
+ */
+ if (nn < 0 && sockerr == ESHUTDOWN)
+ {
+ nn = 0;
+ sockerr = 0;
+ }
+#endif
+
+ if (nn == 0) /* XXX: should this be inside #if defined(RT_OS_WINDOWS)? */
+ {
+ /*
+ * Special case for WSAEnumNetworkEvents: If we receive 0 bytes that
+ * _could_ mean that the connection is closed. But we will receive an
+ * FD_CLOSE event later if the connection was _really_ closed. With
+ * www.youtube.com I see this very often. Closing the socket too early
+ * would be dangerous.
+ */
+ int status;
+ unsigned long pending = 0;
+ status = ioctlsocket(so->s, FIONREAD, &pending);
+ if (status < 0)
+ Log(("NAT:%s: error in WSAIoctl: %d\n", RT_GCC_EXTENSION __PRETTY_FUNCTION__, errno));
+ if (pending != 0)
+ {
+ SOCKET_UNLOCK(so);
+ STAM_PROFILE_STOP(&pData->StatIOread, a);
+ return 0;
+ }
+ }
+
+ if ( nn < 0
+ && soIgnorableErrorCode(sockerr))
+ {
+ SOCKET_UNLOCK(so);
+ STAM_PROFILE_STOP(&pData->StatIOread, a);
+ return 0;
+ }
+ else
+ {
+ int fUninitializedTemplate = 0;
+ int shuterr;
+
+ fUninitializedTemplate = RT_BOOL(( sototcpcb(so)
+ && ( sototcpcb(so)->t_template.ti_src.s_addr == INADDR_ANY
+ || sototcpcb(so)->t_template.ti_dst.s_addr == INADDR_ANY)));
+ /* nn == 0 means peer has performed an orderly shutdown */
+ Log2(("%s: disconnected, nn = %d, errno = %d (%s)\n",
+ RT_GCC_EXTENSION __PRETTY_FUNCTION__, nn, sockerr, strerror(sockerr)));
+
+ shuterr = sofcantrcvmore(so);
+ if (!sockerr && !shuterr && !fUninitializedTemplate)
+ tcp_sockclosed(pData, sototcpcb(so));
+ else
+ {
+ LogRel2(("NAT: sockerr %d, shuterr %d - %R[natsock]\n", sockerr, shuterr, so));
+ tcp_drop(pData, sototcpcb(so), sockerr);
+ }
+ SOCKET_UNLOCK(so);
+ STAM_PROFILE_STOP(&pData->StatIOread, a);
+ return -1;
+ }
+ }
+ STAM_STATS(
+ if (n == 1)
+ {
+ STAM_COUNTER_INC(&pData->StatIORead_in_1);
+ STAM_COUNTER_ADD(&pData->StatIORead_in_1_bytes, nn);
+ }
+ else
+ {
+ STAM_COUNTER_INC(&pData->StatIORead_in_2);
+ STAM_COUNTER_ADD(&pData->StatIORead_in_2_1st_bytes, nn);
+ }
+ );
+
+#ifndef HAVE_READV
+ /*
+ * If there was no error, try and read the second time round
+ * We read again if n = 2 (ie, there's another part of the buffer)
+ * and we read as much as we could in the first read
+ * We don't test for <= 0 this time, because there legitimately
+ * might not be any more data (since the socket is non-blocking),
+ * a close will be detected on next iteration.
+ * A return of -1 wont (shouldn't) happen, since it didn't happen above
+ */
+ if (n == 2 && (unsigned)nn == iov[0].iov_len)
+ {
+ int ret;
+ ret = recv(so->s, iov[1].iov_base, iov[1].iov_len, 0);
+ if (ret > 0)
+ nn += ret;
+ STAM_STATS(
+ if (ret > 0)
+ {
+ STAM_COUNTER_INC(&pData->StatIORead_in_2);
+ STAM_COUNTER_ADD(&pData->StatIORead_in_2_2nd_bytes, ret);
+ }
+ );
+ }
+
+ Log2(("%s: read(2) nn = %d bytes\n", RT_GCC_EXTENSION __PRETTY_FUNCTION__, nn));
+#endif
+
+ /* Update fields */
+ sb->sb_cc += nn;
+ sb->sb_wptr += nn;
+ Log2(("%s: update so_snd (readed nn = %d) %R[sbuf]\n", RT_GCC_EXTENSION __PRETTY_FUNCTION__, nn, sb));
+ if (sb->sb_wptr >= (sb->sb_data + sb->sb_datalen))
+ {
+ sb->sb_wptr -= sb->sb_datalen;
+ Log2(("%s: alter sb_wptr so_snd = %R[sbuf]\n", RT_GCC_EXTENSION __PRETTY_FUNCTION__, sb));
+ }
+ STAM_PROFILE_STOP(&pData->StatIOread, a);
+ SOCKET_UNLOCK(so);
+ return nn;
+}
+
+/*
+ * Get urgent data
+ *
+ * When the socket is created, we set it SO_OOBINLINE,
+ * so when OOB data arrives, we soread() it and everything
+ * in the send buffer is sent as urgent data
+ */
+void
+sorecvoob(PNATState pData, struct socket *so)
+{
+ struct tcpcb *tp = sototcpcb(so);
+ ssize_t ret;
+
+ LogFlowFunc(("sorecvoob: so = %R[natsock]\n", so));
+
+ /*
+ * We take a guess at how much urgent data has arrived.
+ * In most situations, when urgent data arrives, the next
+ * read() should get all the urgent data. This guess will
+ * be wrong however if more data arrives just after the
+ * urgent data, or the read() doesn't return all the
+ * urgent data.
+ */
+ ret = soread(pData, so);
+ if (RT_LIKELY(ret > 0))
+ {
+ /*
+ * @todo for now just scrub the URG pointer. To faithfully
+ * proxy URG we need to read the srteam until SIOCATMARK, and
+ * then mark the first byte of the next read ar urgent.
+ */
+#if 0
+ tp->snd_up = tp->snd_una + SBUF_LEN(&so->so_snd);
+#endif
+ tp->t_force = 1;
+ tcp_output(pData, tp);
+ tp->t_force = 0;
+ }
+}
+
+/*
+ * Send urgent data
+ * There's a lot duplicated code here, but...
+ */
+int
+sosendoob(struct socket *so)
+{
+ struct sbuf *sb = &so->so_rcv;
+ char buff[2048]; /* XXX Shouldn't be sending more oob data than this */
+
+ int n, len;
+
+ LogFlowFunc(("sosendoob so = %R[natsock]\n", so));
+
+ if (so->so_urgc > sizeof(buff))
+ so->so_urgc = sizeof(buff); /* XXX */
+
+ if (sb->sb_rptr < sb->sb_wptr)
+ {
+ /* We can send it directly */
+ n = send(so->s, sb->sb_rptr, so->so_urgc, (MSG_OOB)); /* |MSG_DONTWAIT)); */
+ so->so_urgc -= n;
+
+ Log2((" --- sent %d bytes urgent data, %d urgent bytes left\n",
+ n, so->so_urgc));
+ }
+ else
+ {
+ /*
+ * Since there's no sendv or sendtov like writev,
+ * we must copy all data to a linear buffer then
+ * send it all
+ */
+ len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr;
+ if (len > so->so_urgc)
+ len = so->so_urgc;
+ memcpy(buff, sb->sb_rptr, len);
+ so->so_urgc -= len;
+ if (so->so_urgc)
+ {
+ n = sb->sb_wptr - sb->sb_data;
+ if (n > so->so_urgc)
+ n = so->so_urgc;
+ memcpy(buff + len, sb->sb_data, n);
+ so->so_urgc -= n;
+ len += n;
+ }
+ n = send(so->s, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */
+#ifdef DEBUG
+ if (n != len)
+ Log(("Didn't send all data urgently XXXXX\n"));
+#endif
+ Log2((" ---2 sent %d bytes urgent data, %d urgent bytes left\n",
+ n, so->so_urgc));
+ }
+
+ sb->sb_cc -= n;
+ sb->sb_rptr += n;
+ if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen))
+ sb->sb_rptr -= sb->sb_datalen;
+
+ return n;
+}
+
+/*
+ * Write data from so_rcv to so's socket,
+ * updating all sbuf field as necessary
+ */
+int
+sowrite(PNATState pData, struct socket *so)
+{
+ int n, nn;
+ struct sbuf *sb = &so->so_rcv;
+ u_int len = sb->sb_cc;
+ struct iovec iov[2];
+
+ STAM_PROFILE_START(&pData->StatIOwrite, a);
+ STAM_COUNTER_RESET(&pData->StatIOWrite_in_1);
+ STAM_COUNTER_RESET(&pData->StatIOWrite_in_1_bytes);
+ STAM_COUNTER_RESET(&pData->StatIOWrite_in_2);
+ STAM_COUNTER_RESET(&pData->StatIOWrite_in_2_1st_bytes);
+ STAM_COUNTER_RESET(&pData->StatIOWrite_in_2_2nd_bytes);
+ STAM_COUNTER_RESET(&pData->StatIOWrite_no_w);
+ STAM_COUNTER_RESET(&pData->StatIOWrite_rest);
+ STAM_COUNTER_RESET(&pData->StatIOWrite_rest_bytes);
+ LogFlowFunc(("so = %R[natsock]\n", so));
+ Log2(("%s: so = %R[natsock] so->so_rcv = %R[sbuf]\n", RT_GCC_EXTENSION __PRETTY_FUNCTION__, so, sb));
+ QSOCKET_LOCK(tcb);
+ SOCKET_LOCK(so);
+ QSOCKET_UNLOCK(tcb);
+ if (so->so_urgc)
+ {
+ sosendoob(so);
+ if (sb->sb_cc == 0)
+ {
+ SOCKET_UNLOCK(so);
+ STAM_PROFILE_STOP(&pData->StatIOwrite, a);
+ return 0;
+ }
+ }
+
+ /*
+ * No need to check if there's something to write,
+ * sowrite wouldn't have been called otherwise
+ */
+
+ len = sb->sb_cc;
+
+ iov[0].iov_base = sb->sb_rptr;
+ iov[1].iov_base = 0;
+ iov[1].iov_len = 0;
+ if (sb->sb_rptr < sb->sb_wptr)
+ {
+ iov[0].iov_len = sb->sb_wptr - sb->sb_rptr;
+ /* Should never succeed, but... */
+ if (iov[0].iov_len > len)
+ iov[0].iov_len = len;
+ n = 1;
+ }
+ else
+ {
+ iov[0].iov_len = (sb->sb_data + sb->sb_datalen) - sb->sb_rptr;
+ if (iov[0].iov_len > len)
+ iov[0].iov_len = len;
+ len -= iov[0].iov_len;
+ if (len)
+ {
+ iov[1].iov_base = sb->sb_data;
+ iov[1].iov_len = sb->sb_wptr - sb->sb_data;
+ if (iov[1].iov_len > len)
+ iov[1].iov_len = len;
+ n = 2;
+ }
+ else
+ n = 1;
+ }
+ STAM_STATS({
+ if (n == 1)
+ {
+ STAM_COUNTER_INC(&pData->StatIOWrite_in_1);
+ STAM_COUNTER_ADD(&pData->StatIOWrite_in_1_bytes, iov[0].iov_len);
+ }
+ else
+ {
+ STAM_COUNTER_INC(&pData->StatIOWrite_in_2);
+ STAM_COUNTER_ADD(&pData->StatIOWrite_in_2_1st_bytes, iov[0].iov_len);
+ STAM_COUNTER_ADD(&pData->StatIOWrite_in_2_2nd_bytes, iov[1].iov_len);
+ }
+ });
+ /* Check if there's urgent data to send, and if so, send it */
+#ifdef HAVE_READV
+ nn = writev(so->s, (const struct iovec *)iov, n);
+#else
+ nn = send(so->s, iov[0].iov_base, iov[0].iov_len, 0);
+#endif
+ Log2(("%s: wrote(1) nn = %d bytes\n", RT_GCC_EXTENSION __PRETTY_FUNCTION__, nn));
+ /* This should never happen, but people tell me it does *shrug* */
+ if ( nn < 0
+ && soIgnorableErrorCode(errno))
+ {
+ SOCKET_UNLOCK(so);
+ STAM_PROFILE_STOP(&pData->StatIOwrite, a);
+ return 0;
+ }
+
+ if (nn < 0 || (nn == 0 && iov[0].iov_len > 0))
+ {
+ Log2(("%s: disconnected, so->so_state = %x, errno = %d\n",
+ RT_GCC_EXTENSION __PRETTY_FUNCTION__, so->so_state, errno));
+ sofcantsendmore(so);
+ tcp_sockclosed(pData, sototcpcb(so));
+ SOCKET_UNLOCK(so);
+ STAM_PROFILE_STOP(&pData->StatIOwrite, a);
+ return -1;
+ }
+
+#ifndef HAVE_READV
+ if (n == 2 && (unsigned)nn == iov[0].iov_len)
+ {
+ int ret;
+ ret = send(so->s, iov[1].iov_base, iov[1].iov_len, 0);
+ if (ret > 0)
+ nn += ret;
+# ifdef VBOX_WITH_STATISTICS
+ if (ret > 0 && ret != (ssize_t)iov[1].iov_len)
+ {
+ STAM_COUNTER_INC(&pData->StatIOWrite_rest);
+ STAM_COUNTER_ADD(&pData->StatIOWrite_rest_bytes, (iov[1].iov_len - ret));
+ }
+#endif
+ }
+ Log2(("%s: wrote(2) nn = %d bytes\n", RT_GCC_EXTENSION __PRETTY_FUNCTION__, nn));
+#endif
+
+ /* Update sbuf */
+ sb->sb_cc -= nn;
+ sb->sb_rptr += nn;
+ Log2(("%s: update so_rcv (written nn = %d) %R[sbuf]\n", RT_GCC_EXTENSION __PRETTY_FUNCTION__, nn, sb));
+ if (sb->sb_rptr >= (sb->sb_data + sb->sb_datalen))
+ {
+ sb->sb_rptr -= sb->sb_datalen;
+ Log2(("%s: alter sb_rptr of so_rcv %R[sbuf]\n", RT_GCC_EXTENSION __PRETTY_FUNCTION__, sb));
+ }
+
+ /*
+ * If in DRAIN mode, and there's no more data, set
+ * it CANTSENDMORE
+ */
+ if ((so->so_state & SS_FWDRAIN) && sb->sb_cc == 0)
+ sofcantsendmore(so);
+
+ SOCKET_UNLOCK(so);
+ STAM_PROFILE_STOP(&pData->StatIOwrite, a);
+ return nn;
+}
+
+/*
+ * recvfrom() a UDP socket
+ */
+void
+sorecvfrom(PNATState pData, struct socket *so)
+{
+ LogFlowFunc(("sorecvfrom: so = %p\n", so));
+
+#ifdef RT_OS_WINDOWS
+ /* ping is handled with ICMP API in ip_icmpwin.c */
+ Assert(so->so_type == IPPROTO_UDP);
+#else
+ if (so->so_type == IPPROTO_ICMP)
+ {
+ /* This is a "ping" reply */
+ sorecvfrom_icmp_unix(pData, so);
+ udp_detach(pData, so);
+ }
+ else
+#endif /* !RT_OS_WINDOWS */
+ {
+ static char achBuf[64 * 1024];
+
+ /* A "normal" UDP packet */
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(struct sockaddr_in);
+ struct iovec iov[2];
+ ssize_t nread;
+ struct mbuf *m;
+
+ QSOCKET_LOCK(udb);
+ SOCKET_LOCK(so);
+ QSOCKET_UNLOCK(udb);
+
+ m = m_getjcl(pData, M_NOWAIT, MT_HEADER, M_PKTHDR, slirp_size(pData));
+ if (m == NULL)
+ {
+ SOCKET_UNLOCK(so);
+ return;
+ }
+
+ m->m_data += ETH_HLEN;
+ m->m_pkthdr.header = mtod(m, void *);
+
+ m->m_data += sizeof(struct udpiphdr);
+
+ /* small packets will fit without copying */
+ iov[0].iov_base = mtod(m, char *);
+ iov[0].iov_len = M_TRAILINGSPACE(m);
+
+ /* large packets will spill into a temp buffer */
+ iov[1].iov_base = achBuf;
+ iov[1].iov_len = sizeof(achBuf);
+
+#if !defined(RT_OS_WINDOWS)
+ {
+ struct msghdr mh;
+ memset(&mh, 0, sizeof(mh));
+
+ mh.msg_iov = iov;
+ mh.msg_iovlen = 2;
+ mh.msg_name = &addr;
+ mh.msg_namelen = addrlen;
+
+ nread = recvmsg(so->s, &mh, 0);
+ }
+#else /* RT_OS_WINDOWS */
+ {
+ DWORD nbytes; /* NB: can't use nread b/c of different size */
+ DWORD flags = 0;
+ int status;
+ AssertCompile(sizeof(WSABUF) == sizeof(struct iovec));
+ AssertCompileMembersSameSizeAndOffset(WSABUF, len, struct iovec, iov_len);
+ AssertCompileMembersSameSizeAndOffset(WSABUF, buf, struct iovec, iov_base);
+ status = WSARecvFrom(so->s, (WSABUF *)&iov[0], 2, &nbytes, &flags,
+ (struct sockaddr *)&addr, &addrlen,
+ NULL, NULL);
+ if (status != SOCKET_ERROR)
+ nread = nbytes;
+ else
+ nread = -1;
+ }
+#endif
+ if (nread >= 0)
+ {
+ if (nread <= iov[0].iov_len)
+ m->m_len = nread;
+ else
+ {
+ m->m_len = iov[0].iov_len;
+ m_append(pData, m, nread - iov[0].iov_len, iov[1].iov_base);
+ }
+ Assert(m_length(m, NULL) == (size_t)nread);
+
+ /*
+ * Hack: domain name lookup will be used the most for UDP,
+ * and since they'll only be used once there's no need
+ * for the 4 minute (or whatever) timeout... So we time them
+ * out much quicker (10 seconds for now...)
+ */
+ if (so->so_expire)
+ {
+ if (so->so_fport != RT_H2N_U16_C(53))
+ so->so_expire = curtime + SO_EXPIRE;
+ }
+
+ /*
+ * DNS proxy requests are forwarded to the real resolver,
+ * but its socket's so_faddr is that of the DNS proxy
+ * itself.
+ *
+ * last argument should be changed if Slirp will inject IP attributes
+ */
+ if ( pData->fUseDnsProxy
+ && so->so_fport == RT_H2N_U16_C(53)
+ && CTL_CHECK(so->so_faddr.s_addr, CTL_DNS))
+ dnsproxy_answer(pData, so, m);
+
+ /* packets definetly will be fragmented, could confuse receiver peer. */
+ if (nread > if_mtu)
+ m->m_flags |= M_SKIP_FIREWALL;
+
+ /*
+ * If this packet was destined for CTL_ADDR,
+ * make it look like that's where it came from, done by udp_output
+ */
+ udp_output(pData, so, m, &addr);
+ }
+ else
+ {
+ m_freem(pData, m);
+
+ if (!soIgnorableErrorCode(errno))
+ {
+ u_char code;
+ if (errno == EHOSTUNREACH)
+ code = ICMP_UNREACH_HOST;
+ else if (errno == ENETUNREACH)
+ code = ICMP_UNREACH_NET;
+ else
+ code = ICMP_UNREACH_PORT;
+
+ Log2((" rx error, tx icmp ICMP_UNREACH:%i\n", code));
+ icmp_error(pData, so->so_m, ICMP_UNREACH, code, 0, strerror(errno));
+ so->so_m = NULL;
+ }
+ }
+
+ SOCKET_UNLOCK(so);
+ }
+}
+
+/*
+ * sendto() a socket
+ */
+int
+sosendto(PNATState pData, struct socket *so, struct mbuf *m)
+{
+ int ret;
+ struct sockaddr_in *paddr;
+ struct sockaddr addr;
+#if 0
+ struct sockaddr_in host_addr;
+#endif
+ caddr_t buf = 0;
+ int mlen;
+
+ LogFlowFunc(("sosendto: so = %R[natsock], m = %p\n", so, m));
+
+ memset(&addr, 0, sizeof(struct sockaddr));
+#ifdef RT_OS_DARWIN
+ addr.sa_len = sizeof(struct sockaddr_in);
+#endif
+ paddr = (struct sockaddr_in *)&addr;
+ paddr->sin_family = AF_INET;
+ if ((so->so_faddr.s_addr & RT_H2N_U32(pData->netmask)) == pData->special_addr.s_addr)
+ {
+ /* It's an alias */
+ uint32_t last_byte = RT_N2H_U32(so->so_faddr.s_addr) & ~pData->netmask;
+ switch(last_byte)
+ {
+#if 0
+ /* handle this case at 'default:' */
+ case CTL_BROADCAST:
+ addr.sin_addr.s_addr = INADDR_BROADCAST;
+ /* Send the packet to host to fully emulate broadcast */
+ /** @todo r=klaus: on Linux host this causes the host to receive
+ * the packet twice for some reason. And I cannot find any place
+ * in the man pages which states that sending a broadcast does not
+ * reach the host itself. */
+ host_addr.sin_family = AF_INET;
+ host_addr.sin_port = so->so_fport;
+ host_addr.sin_addr = our_addr;
+ sendto(so->s, m->m_data, m->m_len, 0,
+ (struct sockaddr *)&host_addr, sizeof (struct sockaddr));
+ break;
+#endif
+ case CTL_DNS:
+ case CTL_ALIAS:
+ default:
+ if (last_byte == ~pData->netmask)
+ paddr->sin_addr.s_addr = INADDR_BROADCAST;
+ else
+ paddr->sin_addr = loopback_addr;
+ break;
+ }
+ }
+ else
+ paddr->sin_addr = so->so_faddr;
+ paddr->sin_port = so->so_fport;
+
+ Log2((" sendto()ing, addr.sin_port=%d, addr.sin_addr.s_addr=%.16s\n",
+ RT_N2H_U16(paddr->sin_port), inet_ntoa(paddr->sin_addr)));
+
+ /* Don't care what port we get */
+ /*
+ * > nmap -sV -T4 -O -A -v -PU3483 255.255.255.255
+ * generates bodyless messages, annoying memmory management system.
+ */
+ mlen = m_length(m, NULL);
+ if (mlen > 0)
+ {
+ buf = RTMemAlloc(mlen);
+ if (buf == NULL)
+ {
+ return -1;
+ }
+ m_copydata(m, 0, mlen, buf);
+ }
+ ret = sendto(so->s, buf, mlen, 0,
+ (struct sockaddr *)&addr, sizeof (struct sockaddr));
+#ifdef VBOX_WITH_NAT_SEND2HOME
+ if (slirpIsWideCasting(pData, so->so_faddr.s_addr))
+ {
+ slirpSend2Home(pData, so, buf, mlen, 0);
+ }
+#endif
+ if (buf)
+ RTMemFree(buf);
+ if (ret < 0)
+ {
+ Log2(("UDP: sendto fails (%s)\n", strerror(errno)));
+ return -1;
+ }
+
+ /*
+ * Kill the socket if there's no reply in 4 minutes,
+ * but only if it's an expirable socket
+ */
+ if (so->so_expire)
+ so->so_expire = curtime + SO_EXPIRE;
+ so->so_state = SS_ISFCONNECTED; /* So that it gets select()ed */
+ return 0;
+}
+
+/*
+ * XXX This should really be tcp_listen
+ */
+struct socket *
+solisten(PNATState pData, u_int32_t bind_addr, u_int port, u_int32_t laddr, u_int lport, int flags)
+{
+ struct sockaddr_in addr;
+ struct socket *so;
+ socklen_t addrlen = sizeof(addr);
+ int s, opt = 1;
+ int status;
+
+ LogFlowFunc(("solisten: port = %d, laddr = %x, lport = %d, flags = %x\n", port, laddr, lport, flags));
+
+ if ((so = socreate()) == NULL)
+ {
+ /* RTMemFree(so); Not sofree() ??? free(NULL) == NOP */
+ return NULL;
+ }
+
+ /* Don't tcp_attach... we don't need so_snd nor so_rcv */
+ if ((so->so_tcpcb = tcp_newtcpcb(pData, so)) == NULL)
+ {
+ RTMemFree(so);
+ return NULL;
+ }
+
+ SOCKET_LOCK_CREATE(so);
+ SOCKET_LOCK(so);
+ QSOCKET_LOCK(tcb);
+ insque(pData, so,&tcb);
+ NSOCK_INC();
+ QSOCKET_UNLOCK(tcb);
+
+ /*
+ * SS_FACCEPTONCE sockets must time out.
+ */
+ if (flags & SS_FACCEPTONCE)
+ so->so_tcpcb->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT*2;
+
+ so->so_state = (SS_FACCEPTCONN|flags);
+ so->so_lport = lport; /* Kept in network format */
+ so->so_laddr.s_addr = laddr; /* Ditto */
+
+ memset(&addr, 0, sizeof(addr));
+#ifdef RT_OS_DARWIN
+ addr.sin_len = sizeof(addr);
+#endif
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = bind_addr;
+ addr.sin_port = port;
+
+ /**
+ * changing listen(,1->SOMAXCONN) shouldn't be harmful for NAT's TCP/IP stack,
+ * kernel will choose the optimal value for requests queue length.
+ * @note: MSDN recommends low (2-4) values for bluetooth networking devices.
+ */
+ if ( ((s = socket(AF_INET, SOCK_STREAM, 0)) < 0)
+ || (setsockopt(s, SOL_SOCKET, SO_REUSEADDR,(char *)&opt, sizeof(int)) < 0)
+ || (bind(s,(struct sockaddr *)&addr, sizeof(addr)) < 0)
+ || (listen(s, pData->soMaxConn) < 0))
+ {
+#ifdef RT_OS_WINDOWS
+ int tmperrno = WSAGetLastError(); /* Don't clobber the real reason we failed */
+ closesocket(s);
+ QSOCKET_LOCK(tcb);
+ sofree(pData, so);
+ QSOCKET_UNLOCK(tcb);
+ /* Restore the real errno */
+ WSASetLastError(tmperrno);
+#else
+ int tmperrno = errno; /* Don't clobber the real reason we failed */
+ close(s);
+ if (sototcpcb(so))
+ tcp_close(pData, sototcpcb(so));
+ else
+ sofree(pData, so);
+ /* Restore the real errno */
+ errno = tmperrno;
+#endif
+ return NULL;
+ }
+ fd_nonblock(s);
+ setsockopt(s, SOL_SOCKET, SO_OOBINLINE,(char *)&opt, sizeof(int));
+
+ getsockname(s,(struct sockaddr *)&addr,&addrlen);
+ so->so_fport = addr.sin_port;
+ /* set socket buffers */
+ opt = pData->socket_rcv;
+ status = setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&opt, sizeof(int));
+ if (status < 0)
+ {
+ LogRel(("NAT: Error(%d) while setting RCV capacity to (%d)\n", errno, opt));
+ goto no_sockopt;
+ }
+ opt = pData->socket_snd;
+ status = setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&opt, sizeof(int));
+ if (status < 0)
+ {
+ LogRel(("NAT: Error(%d) while setting SND capacity to (%d)\n", errno, opt));
+ goto no_sockopt;
+ }
+no_sockopt:
+ if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr)
+ so->so_faddr = alias_addr;
+ else
+ so->so_faddr = addr.sin_addr;
+
+ so->s = s;
+ SOCKET_UNLOCK(so);
+ return so;
+}
+
+/*
+ * Data is available in so_rcv
+ * Just write() the data to the socket
+ * XXX not yet...
+ * @todo do we really need this function, what it's intended to do?
+ */
+void
+sorwakeup(struct socket *so)
+{
+ NOREF(so);
+#if 0
+ sowrite(so);
+ FD_CLR(so->s,&writefds);
+#endif
+}
+
+/*
+ * Data has been freed in so_snd
+ * We have room for a read() if we want to
+ * For now, don't read, it'll be done in the main loop
+ */
+void
+sowwakeup(struct socket *so)
+{
+ NOREF(so);
+}
+
+/*
+ * Various session state calls
+ * XXX Should be #define's
+ * The socket state stuff needs work, these often get call 2 or 3
+ * times each when only 1 was needed
+ */
+void
+soisfconnecting(struct socket *so)
+{
+ so->so_state &= ~(SS_NOFDREF|SS_ISFCONNECTED|SS_FCANTRCVMORE|
+ SS_FCANTSENDMORE|SS_FWDRAIN);
+ so->so_state |= SS_ISFCONNECTING; /* Clobber other states */
+}
+
+void
+soisfconnected(struct socket *so)
+{
+ LogFlowFunc(("ENTER: so:%R[natsock]\n", so));
+ so->so_state &= ~(SS_ISFCONNECTING|SS_FWDRAIN|SS_NOFDREF);
+ so->so_state |= SS_ISFCONNECTED; /* Clobber other states */
+ LogFlowFunc(("LEAVE: so:%R[natsock]\n", so));
+}
+
+int
+sofcantrcvmore(struct socket *so)
+{
+ int err = 0;
+
+ LogFlowFunc(("ENTER: so:%R[natsock]\n", so));
+ if ((so->so_state & SS_NOFDREF) == 0)
+ {
+ /*
+ * If remote closes first and then sends an RST, the recv() in
+ * soread() will keep reporting EOF without any error
+ * indication. As far as I can tell the only way to detect
+ * this on Linux is to check if shutdown() succeeds here (but
+ * see below).
+ *
+ * OTOH on OS X shutdown() "helpfully" checks if remote has
+ * already closed and then always returns ENOTCONN
+ * immediately.
+ */
+ int status = shutdown(so->s, SHUT_RD);
+#if defined(RT_OS_LINUX)
+ if (status < 0)
+ err = errno;
+#else
+ RT_NOREF(status);
+#endif
+ }
+ so->so_state &= ~(SS_ISFCONNECTING);
+ if (so->so_state & SS_FCANTSENDMORE)
+ {
+#if defined(RT_OS_LINUX)
+ /*
+ * If we have closed first, and remote closes, shutdown will
+ * return ENOTCONN, but this is expected. Don't tell the
+ * caller there was an error.
+ */
+ if (err == ENOTCONN)
+ err = 0;
+#endif
+ so->so_state = SS_NOFDREF; /* Don't select it */
+ /* XXX close() here as well? */
+ }
+ else
+ so->so_state |= SS_FCANTRCVMORE;
+
+ LogFlowFunc(("LEAVE: %d\n", err));
+ return err;
+}
+
+void
+sofcantsendmore(struct socket *so)
+{
+ LogFlowFunc(("ENTER: so:%R[natsock]\n", so));
+ if ((so->so_state & SS_NOFDREF) == 0)
+ shutdown(so->s, 1); /* send FIN to fhost */
+
+ so->so_state &= ~(SS_ISFCONNECTING);
+ if (so->so_state & SS_FCANTRCVMORE)
+ so->so_state = SS_NOFDREF; /* as above */
+ else
+ so->so_state |= SS_FCANTSENDMORE;
+ LogFlowFuncLeave();
+}
+
+void
+soisfdisconnected(struct socket *so)
+{
+ NOREF(so);
+#if 0
+ so->so_state &= ~(SS_ISFCONNECTING|SS_ISFCONNECTED);
+ close(so->s);
+ so->so_state = SS_ISFDISCONNECTED;
+ /*
+ * XXX Do nothing ... ?
+ */
+#endif
+}
+
+/*
+ * Set write drain mode
+ * Set CANTSENDMORE once all data has been write()n
+ */
+void
+sofwdrain(struct socket *so)
+{
+ if (SBUF_LEN(&so->so_rcv))
+ so->so_state |= SS_FWDRAIN;
+ else
+ sofcantsendmore(so);
+}
+
+#if !defined(RT_OS_WINDOWS)
+static void
+send_icmp_to_guest(PNATState pData, char *buff, size_t len, const struct sockaddr_in *addr)
+{
+ struct ip *ip;
+ uint32_t dst, src;
+ char ip_copy[256];
+ struct icmp *icp;
+ int old_ip_len = 0;
+ int hlen, original_hlen = 0;
+ struct mbuf *m;
+ struct icmp_msg *icm;
+ uint8_t proto;
+ int type = 0;
+
+ ip = (struct ip *)buff;
+ /* Fix ip->ip_len to contain the total packet length including the header
+ * in _host_ byte order for all OSes. On Darwin, that value already is in
+ * host byte order. Solaris and Darwin report only the payload. */
+#ifndef RT_OS_DARWIN
+ ip->ip_len = RT_N2H_U16(ip->ip_len);
+#endif
+ hlen = (ip->ip_hl << 2);
+#if defined(RT_OS_SOLARIS) || defined(RT_OS_DARWIN)
+ ip->ip_len += hlen;
+#endif
+ if (ip->ip_len < hlen + ICMP_MINLEN)
+ {
+ Log(("send_icmp_to_guest: ICMP header is too small to understand which type/subtype of the datagram\n"));
+ return;
+ }
+ icp = (struct icmp *)((char *)ip + hlen);
+
+ Log(("ICMP:received msg(t:%d, c:%d)\n", icp->icmp_type, icp->icmp_code));
+ if ( icp->icmp_type != ICMP_ECHOREPLY
+ && icp->icmp_type != ICMP_TIMXCEED
+ && icp->icmp_type != ICMP_UNREACH)
+ {
+ return;
+ }
+
+ /*
+ * ICMP_ECHOREPLY, ICMP_TIMXCEED, ICMP_UNREACH minimal header size is
+ * ICMP_ECHOREPLY assuming data 0
+ * icmp_{type(8), code(8), cksum(16),identifier(16),seqnum(16)}
+ */
+ if (ip->ip_len < hlen + 8)
+ {
+ Log(("send_icmp_to_guest: NAT accept ICMP_{ECHOREPLY, TIMXCEED, UNREACH} the minimum size is 64 (see rfc792)\n"));
+ return;
+ }
+
+ type = icp->icmp_type;
+ if ( type == ICMP_TIMXCEED
+ || type == ICMP_UNREACH)
+ {
+ /*
+ * ICMP_TIMXCEED, ICMP_UNREACH minimal header size is
+ * icmp_{type(8), code(8), cksum(16),unused(32)} + IP header + 64 bit of original datagram
+ */
+ if (ip->ip_len < hlen + 2*8 + sizeof(struct ip))
+ {
+ Log(("send_icmp_to_guest: NAT accept ICMP_{TIMXCEED, UNREACH} the minimum size of ipheader + 64 bit of data (see rfc792)\n"));
+ return;
+ }
+ ip = &icp->icmp_ip;
+ }
+
+ icm = icmp_find_original_mbuf(pData, ip);
+ if (icm == NULL)
+ {
+ Log(("NAT: Can't find the corresponding packet for the received ICMP\n"));
+ return;
+ }
+
+ m = icm->im_m;
+ if (!m)
+ {
+ LogFunc(("%R[natsock] hasn't stored it's mbuf on sent\n", icm->im_so));
+ goto done;
+ }
+
+ src = addr->sin_addr.s_addr;
+ if (type == ICMP_ECHOREPLY)
+ {
+ struct ip *ip0 = mtod(m, struct ip *);
+ struct icmp *icp0 = (struct icmp *)((char *)ip0 + (ip0->ip_hl << 2));
+ if (icp0->icmp_type != ICMP_ECHO)
+ {
+ Log(("NAT: we haven't found echo for this reply\n"));
+ goto done;
+ }
+ /*
+ * while combining buffer to send (see ip_icmp.c) we control ICMP header only,
+ * IP header combined by OS network stack, our local copy of IP header contians values
+ * in host byte order so no byte order conversion is required. IP headers fields are converting
+ * in ip_output0 routine only.
+ */
+ if ( (ip->ip_len - hlen)
+ != (ip0->ip_len - (ip0->ip_hl << 2)))
+ {
+ Log(("NAT: ECHO(%d) lenght doesn't match ECHOREPLY(%d)\n",
+ (ip->ip_len - hlen), (ip0->ip_len - (ip0->ip_hl << 2))));
+ goto done;
+ }
+ }
+
+ /* ip points on origianal ip header */
+ ip = mtod(m, struct ip *);
+ proto = ip->ip_p;
+ /* Now ip is pointing on header we've sent from guest */
+ if ( icp->icmp_type == ICMP_TIMXCEED
+ || icp->icmp_type == ICMP_UNREACH)
+ {
+ old_ip_len = (ip->ip_hl << 2) + 64;
+ if (old_ip_len > sizeof(ip_copy))
+ old_ip_len = sizeof(ip_copy);
+ memcpy(ip_copy, ip, old_ip_len);
+ }
+
+ /* source address from original IP packet*/
+ dst = ip->ip_src.s_addr;
+
+ /* overide ther tail of old packet */
+ ip = mtod(m, struct ip *); /* ip is from mbuf we've overrided */
+ original_hlen = ip->ip_hl << 2;
+ /* saves original ip header and options */
+ m_copyback(pData, m, original_hlen, len - hlen, buff + hlen);
+ ip->ip_len = m_length(m, NULL);
+ ip->ip_p = IPPROTO_ICMP; /* the original package could be whatever, but we're response via ICMP*/
+
+ icp = (struct icmp *)((char *)ip + (ip->ip_hl << 2));
+ type = icp->icmp_type;
+ if ( type == ICMP_TIMXCEED
+ || type == ICMP_UNREACH)
+ {
+ /* according RFC 793 error messages required copy of initial IP header + 64 bit */
+ memcpy(&icp->icmp_ip, ip_copy, old_ip_len);
+
+ /* undo byte order conversions done in ip_input() */
+ HTONS(icp->icmp_ip.ip_len);
+ HTONS(icp->icmp_ip.ip_id);
+ HTONS(icp->icmp_ip.ip_off);
+
+ ip->ip_tos = ((ip->ip_tos & 0x1E) | 0xC0); /* high priority for errors */
+ }
+
+ ip->ip_src.s_addr = src;
+ ip->ip_dst.s_addr = dst;
+ icmp_reflect(pData, m);
+ /* m was freed */
+ icm->im_m = NULL;
+
+ done:
+ icmp_msg_delete(pData, icm);
+}
+
+static void sorecvfrom_icmp_unix(PNATState pData, struct socket *so)
+{
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(struct sockaddr_in);
+ struct ip ip;
+ char *buff;
+ int len = 0;
+
+ /* 1- step: read the ip header */
+ len = recvfrom(so->s, &ip, sizeof(struct ip), MSG_PEEK,
+ (struct sockaddr *)&addr, &addrlen);
+ if ( len < 0
+ && ( soIgnorableErrorCode(errno)
+ || errno == ENOTCONN))
+ {
+ Log(("sorecvfrom_icmp_unix: 1 - step can't read IP datagramm (would block)\n"));
+ return;
+ }
+
+ if ( len < sizeof(struct ip)
+ || len < 0
+ || len == 0)
+ {
+ u_char code;
+ code = ICMP_UNREACH_PORT;
+
+ if (errno == EHOSTUNREACH)
+ code = ICMP_UNREACH_HOST;
+ else if (errno == ENETUNREACH)
+ code = ICMP_UNREACH_NET;
+
+ LogRel(("NAT: UDP ICMP rx errno=%d (%s)\n", errno, strerror(errno)));
+ icmp_error(pData, so->so_m, ICMP_UNREACH, code, 0, strerror(errno));
+ so->so_m = NULL;
+ Log(("sorecvfrom_icmp_unix: 1 - step can't read IP datagramm\n"));
+ return;
+ }
+ /* basic check of IP header */
+ if ( ip.ip_v != IPVERSION
+# ifndef RT_OS_DARWIN
+ || ip.ip_p != IPPROTO_ICMP
+# endif
+ )
+ {
+ Log(("sorecvfrom_icmp_unix: 1 - step IP isn't IPv4\n"));
+ return;
+ }
+# ifndef RT_OS_DARWIN
+ /* Darwin reports the IP length already in host byte order. */
+ ip.ip_len = RT_N2H_U16(ip.ip_len);
+# endif
+# if defined(RT_OS_SOLARIS) || defined(RT_OS_DARWIN)
+ /* Solaris and Darwin report the payload only */
+ ip.ip_len += (ip.ip_hl << 2);
+# endif
+ /* Note: ip->ip_len in host byte order (all OS) */
+ len = ip.ip_len;
+ buff = RTMemAlloc(len);
+ if (buff == NULL)
+ {
+ Log(("sorecvfrom_icmp_unix: 1 - step can't allocate enought room for datagram\n"));
+ return;
+ }
+ /* 2 - step: we're reading rest of the datagramm to the buffer */
+ addrlen = sizeof(struct sockaddr_in);
+ memset(&addr, 0, addrlen);
+ len = recvfrom(so->s, buff, len, 0,
+ (struct sockaddr *)&addr, &addrlen);
+ if ( len < 0
+ && ( soIgnorableErrorCode(errno)
+ || errno == ENOTCONN))
+ {
+ Log(("sorecvfrom_icmp_unix: 2 - step can't read IP body (would block expected:%d)\n",
+ ip.ip_len));
+ RTMemFree(buff);
+ return;
+ }
+ if ( len < 0
+ || len == 0)
+ {
+ Log(("sorecvfrom_icmp_unix: 2 - step read of the rest of datagramm is fallen (errno:%d, len:%d expected: %d)\n",
+ errno, len, (ip.ip_len - sizeof(struct ip))));
+ RTMemFree(buff);
+ return;
+ }
+ /* len is modified in 2nd read, when the rest of the datagramm was read */
+ send_icmp_to_guest(pData, buff, len, &addr);
+ RTMemFree(buff);
+}
+#endif /* !RT_OS_WINDOWS */
diff --git a/src/VBox/Devices/Network/slirp/socket.h b/src/VBox/Devices/Network/slirp/socket.h
new file mode 100644
index 00000000..a518412f
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/socket.h
@@ -0,0 +1,205 @@
+/* $Id: socket.h $ */
+/** @file
+ * NAT - socket handling (declarations/defines).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+/* MINE */
+
+#ifndef _SLIRP_SOCKET_H_
+#define _SLIRP_SOCKET_H_
+
+#define SO_EXPIRE 240000
+#define SO_EXPIREFAST 10000
+
+/*
+ * Our socket structure
+ */
+
+struct socket
+{
+ struct socket *so_next;
+ struct socket *so_prev; /* For a linked list of sockets */
+
+#if !defined(RT_OS_WINDOWS)
+ int s; /* The actual socket */
+#else
+ union {
+ int s;
+ HANDLE sh;
+ };
+ uint64_t so_icmp_id; /* XXX: hack */
+ uint64_t so_icmp_seq; /* XXX: hack */
+#endif
+
+ /* XXX union these with not-yet-used sbuf params */
+ struct mbuf *so_m; /* Pointer to the original SYN packet,
+ * for non-blocking connect()'s, and
+ * PING reply's */
+ struct tcpiphdr *so_ti; /* Pointer to the original ti within
+ * so_mconn, for non-blocking connections */
+ uint8_t *so_ohdr; /* unmolested IP header of the datagram in so_m */
+ caddr_t so_optp; /* tcp options in so_m */
+ int so_optlen; /* length of options in so_m */
+ int so_urgc;
+ struct in_addr so_faddr; /* foreign host table entry */
+ struct in_addr so_laddr; /* local host table entry */
+ u_int16_t so_fport; /* foreign port */
+ u_int16_t so_lport; /* local port */
+ u_int16_t so_hlport; /* host local port */
+ struct in_addr so_hladdr; /* local host addr */
+
+ u_int8_t so_iptos; /* Type of service */
+
+ uint8_t so_sottl; /* cached socket's IP_TTL option */
+ uint8_t so_sotos; /* cached socket's IP_TOS option */
+ int8_t so_sodf; /* cached socket's DF option */
+
+ u_char so_type; /* Type of socket, UDP or TCP */
+ int so_state; /* internal state flags SS_*, below */
+
+ struct tcpcb *so_tcpcb; /* pointer to TCP protocol control block */
+ u_int so_expire; /* When the socket will expire */
+
+ int so_queued; /* Number of packets queued from this socket */
+ int so_nqueued; /* Number of packets queued in a row
+ * Used to determine when to "downgrade" a session
+ * from fastq to batchq */
+
+ struct sbuf so_rcv; /* Receive buffer */
+ struct sbuf so_snd; /* Send buffer */
+#ifndef RT_OS_WINDOWS
+ int so_poll_index;
+#endif /* !RT_OS_WINDOWS */
+ /*
+ * FD_CLOSE/POLLHUP event has been occurred on socket
+ */
+ int so_close;
+
+ void (* so_timeout)(PNATState pData, struct socket *so, void *arg);
+ void *so_timeout_arg;
+
+ /** These flags (''fUnderPolling'' and ''fShouldBeRemoved'') introduced to
+ * to let polling routine gain control over freeing socket whatever level of
+ * TCP/IP initiated socket releasing.
+ * So polling routine when start processing socket alter it's state to
+ * ''fUnderPolling'' to 1, and clean (set to 0) when it finish.
+ * When polling routine calls functions it should be ensure on return,
+ * whether ''fShouldBeRemoved'' set or not, and depending on state call
+ * ''sofree'' or continue socket processing.
+ * On ''fShouldBeRemoved'' equal to 1, polling routine should call ''sofree'',
+ * clearing ''fUnderPolling'' to do real freeng of the socket and removing from
+ * the queue.
+ * @todo: perhaps, to simplefy the things we need some helper function.
+ * @note: it's used like a bool, I use 'int' to avoid compiler warnings
+ * appearing if [-Wc++-compat] used.
+ */
+ int fUnderPolling;
+ /** This flag used by ''sofree'' function in following manner
+ *
+ * fUnderPolling = 1, then we don't remove socket from the queue, just
+ * alter value ''fShouldBeRemoved'' to 1, else we do removal.
+ */
+ int fShouldBeRemoved;
+};
+
+# define SOCKET_LOCK(so) do {} while (0)
+# define SOCKET_UNLOCK(so) do {} while (0)
+# define SOCKET_LOCK_CREATE(so) do {} while (0)
+# define SOCKET_LOCK_DESTROY(so) do {} while (0)
+
+/*
+ * Socket state bits. (peer means the host on the Internet,
+ * local host means the host on the other end of the modem)
+ */
+#define SS_NOFDREF 0x001 /* No fd reference */
+
+#define SS_ISFCONNECTING 0x002 /* Socket is connecting to peer (non-blocking connect()'s) */
+#define SS_ISFCONNECTED 0x004 /* Socket is connected to peer */
+#define SS_FCANTRCVMORE 0x008 /* Socket can't receive more from peer (for half-closes) */
+#define SS_FCANTSENDMORE 0x010 /* Socket can't send more to peer (for half-closes) */
+/* #define SS_ISFDISCONNECTED 0x020*/ /* Socket has disconnected from peer, in 2MSL state */
+#define SS_FWDRAIN 0x040 /* We received a FIN, drain data and set SS_FCANTSENDMORE */
+
+/* #define SS_CTL 0x080 */
+#define SS_FACCEPTCONN 0x100 /* Socket is accepting connections from a host on the internet */
+#define SS_FACCEPTONCE 0x200 /* If set, the SS_FACCEPTCONN socket will die after one accept */
+
+extern struct socket tcb;
+
+#if defined(DECLARE_IOVEC) && !defined(HAVE_READV)
+# if !defined(RT_OS_WINDOWS)
+struct iovec
+{
+ char *iov_base;
+ size_t iov_len;
+};
+# else
+/* make it congruent with WSABUF */
+struct iovec
+{
+ ULONG iov_len;
+ char *iov_base;
+};
+# endif
+#endif
+
+void so_init (void);
+struct socket * solookup (struct socket *, struct in_addr, u_int, struct in_addr, u_int);
+struct socket * socreate (void);
+void sofree (PNATState, struct socket *);
+int sobind(PNATState, struct socket *);
+int soread (PNATState, struct socket *);
+void sorecvoob (PNATState, struct socket *);
+int sosendoob (struct socket *);
+int sowrite (PNATState, struct socket *);
+void sorecvfrom (PNATState, struct socket *);
+int sosendto (PNATState, struct socket *, struct mbuf *);
+struct socket * solisten (PNATState, u_int32_t, u_int, u_int32_t, u_int, int);
+void sorwakeup (struct socket *);
+void sowwakeup (struct socket *);
+void soisfconnecting (register struct socket *);
+void soisfconnected (register struct socket *);
+int sofcantrcvmore (struct socket *);
+void sofcantsendmore (struct socket *);
+void soisfdisconnected (struct socket *);
+void sofwdrain (struct socket *);
+
+static inline int soIgnorableErrorCode(int iErrorCode)
+{
+ return ( iErrorCode == EINPROGRESS
+ || iErrorCode == EAGAIN
+ || iErrorCode == EWOULDBLOCK);
+}
+
+#endif /* _SOCKET_H_ */
diff --git a/src/VBox/Devices/Network/slirp/tcp.h b/src/VBox/Devices/Network/slirp/tcp.h
new file mode 100644
index 00000000..8ca6a17c
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/tcp.h
@@ -0,0 +1,212 @@
+/* $Id: tcp.h $ */
+/** @file
+ * NAT - TCP.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)tcp.h 8.1 (Berkeley) 6/10/93
+ * tcp.h,v 1.3 1994/08/21 05:27:34 paul Exp
+ */
+
+#ifndef _TCP_H_
+#define _TCP_H_
+
+typedef uint32_t tcp_seq;
+
+#define PR_SLOWHZ 2 /* 2 slow timeouts per second (approx) */
+#define PR_FASTHZ 5 /* 5 fast timeouts per second (not important) */
+
+extern int tcp_rcvspace;
+extern int tcp_sndspace;
+extern struct socket *tcp_last_so;
+
+#define TCP_SNDSPACE 8192
+#define TCP_RCVSPACE 8192
+
+/*
+ * TCP header.
+ * Per RFC 793, September, 1981.
+ */
+struct tcphdr
+{
+ uint16_t th_sport; /* source port */
+ uint16_t th_dport; /* destination port */
+ tcp_seq th_seq; /* sequence number */
+ tcp_seq th_ack; /* acknowledgement number */
+#ifdef WORDS_BIGENDIAN
+# ifdef _MSC_VER
+ uint8_t th_off:4; /* data offset */
+ uint8_t th_x2:4; /* (unused) */
+# else
+ unsigned th_off:4; /* data offset */
+ unsigned th_x2:4; /* (unused) */
+# endif
+#else
+# ifdef _MSC_VER
+ uint8_t th_x2:4; /* (unused) */
+ uint8_t th_off:4; /* data offset */
+# else
+ unsigned th_x2:4; /* (unused) */
+ unsigned th_off:4; /* data offset */
+# endif
+#endif
+ uint8_t th_flags;
+#define TH_FIN 0x01
+#define TH_SYN 0x02
+#define TH_RST 0x04
+#define TH_PUSH 0x08
+#define TH_ACK 0x10
+#define TH_URG 0x20
+ uint16_t th_win; /* window */
+ uint16_t th_sum; /* checksum */
+ uint16_t th_urp; /* urgent pointer */
+};
+AssertCompileSize(struct tcphdr, 20);
+
+#include "tcp_var.h"
+
+#define TCPOPT_EOL 0
+#define TCPOPT_NOP 1
+#define TCPOPT_MAXSEG 2
+#define TCPOLEN_MAXSEG 4
+#define TCPOPT_WINDOW 3
+#define TCPOLEN_WINDOW 3
+#define TCPOPT_SACK_PERMITTED 4 /* Experimental */
+#define TCPOLEN_SACK_PERMITTED 2
+#define TCPOPT_SACK 5 /* Experimental */
+#define TCPOPT_TIMESTAMP 8
+#define TCPOLEN_TIMESTAMP 10
+#define TCPOLEN_TSTAMP_APPA (TCPOLEN_TIMESTAMP+2) /* appendix A */
+
+#define TCPOPT_TSTAMP_HDR \
+ (TCPOPT_NOP<<24|TCPOPT_NOP<<16|TCPOPT_TIMESTAMP<<8|TCPOLEN_TIMESTAMP)
+
+/*
+ * Default maximum segment size for TCP.
+ * With an IP MSS of 576, this is 536,
+ * but 512 is probably more convenient.
+ * This should be defined as MIN(512, IP_MSS - sizeof (struct tcpiphdr)).
+ *
+ * We make this 1460 because we only care about Ethernet in the qemu context.
+ */
+#define TCP_MSS (if_mtu - 80)
+
+#define TCP_MAXWIN 65535 /* largest value for (unscaled) window */
+
+#define TCP_MAX_WINSHIFT 14 /* maximum window shift */
+
+/*
+ * User-settable options (used with setsockopt).
+ *
+ * We don't use the system headers on unix because we have conflicting
+ * local structures. We can't avoid the system definitions on Windows,
+ * so we undefine them.
+ */
+#undef TCP_NODELAY
+#define TCP_NODELAY 0x01 /* don't delay send to coalesce packets */
+#undef TCP_MAXSEG
+/* #define TCP_MAXSEG 0x02 */ /* set maximum segment size */
+
+/*
+ * TCP FSM state definitions.
+ * Per RFC793, September, 1981.
+ */
+
+#define TCP_NSTATES 11
+
+#define TCPS_CLOSED 0 /* closed */
+#define TCPS_LISTEN 1 /* listening for connection */
+#define TCPS_SYN_SENT 2 /* active, have sent syn */
+#define TCPS_SYN_RECEIVED 3 /* have send and received syn */
+/* states < TCPS_ESTABLISHED are those where connections not established */
+#define TCPS_ESTABLISHED 4 /* established */
+#define TCPS_CLOSE_WAIT 5 /* rcvd fin, waiting for close */
+/* states > TCPS_CLOSE_WAIT are those where user has closed */
+#define TCPS_FIN_WAIT_1 6 /* have closed, sent fin */
+#define TCPS_CLOSING 7 /* closed xchd FIN; await FIN ACK */
+#define TCPS_LAST_ACK 8 /* had fin and close; await FIN ACK */
+/* states > TCPS_CLOSE_WAIT && < TCPS_FIN_WAIT_2 await ACK of FIN */
+#define TCPS_FIN_WAIT_2 9 /* have closed, fin is acked */
+#define TCPS_TIME_WAIT 10 /* in 2*msl quiet wait after close */
+
+#define TCPS_HAVERCVDSYN(s) ((s) >= TCPS_SYN_RECEIVED)
+#define TCPS_HAVEESTABLISHED(s) ((s) >= TCPS_ESTABLISHED)
+#define TCPS_HAVERCVDFIN(s) ((s) >= TCPS_TIME_WAIT)
+
+/*
+ * TCP sequence numbers are 32 bit integers operated on with modular arithmetic.
+ * These macros can be used to compare such integers.
+ */
+#define SEQ_LT(a,b) ((int)((a)-(b)) < 0)
+#define SEQ_LEQ(a,b) ((int)((a)-(b)) <= 0)
+#define SEQ_GT(a,b) ((int)((a)-(b)) > 0)
+#define SEQ_GEQ(a,b) ((int)((a)-(b)) >= 0)
+
+/*
+ * Macros to initialize tcp sequence numbers for
+ * send and receive from initial send and receive
+ * sequence numbers.
+ */
+#define tcp_rcvseqinit(tp) \
+ (tp)->rcv_adv = (tp)->rcv_nxt = (tp)->irs + 1
+
+#define tcp_sendseqinit(tp) \
+ (tp)->snd_una = (tp)->snd_nxt = (tp)->snd_max = (tp)->snd_up = (tp)->iss
+
+#define TCP_ISSINCR (125*1024) /* increment for tcp_iss each second */
+
+
+extern const char * const tcpstates[];
+
+#endif
diff --git a/src/VBox/Devices/Network/slirp/tcp_input.c b/src/VBox/Devices/Network/slirp/tcp_input.c
new file mode 100644
index 00000000..30abb131
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/tcp_input.c
@@ -0,0 +1,2060 @@
+/* $Id: tcp_input.c $ */
+/** @file
+ * NAT - TCP input.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)tcp_input.c 8.5 (Berkeley) 4/10/94
+ * tcp_input.c,v 1.10 1994/10/13 18:36:32 wollman Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include "ip_icmp.h"
+
+
+#if 0 /* code using this macroses is commented out */
+# define TCP_PAWS_IDLE (24 * 24 * 60 * 60 * PR_SLOWHZ)
+
+/* for modulo comparisons of timestamps */
+# define TSTMP_LT(a, b) ((int)((a)-(b)) < 0)
+# define TSTMP_GEQ(a, b) ((int)((a)-(b)) >= 0)
+#endif
+
+#ifndef TCP_ACK_HACK
+#define DELAY_ACK(tp, ti) \
+ if (ti->ti_flags & TH_PUSH) \
+ tp->t_flags |= TF_ACKNOW; \
+ else \
+ tp->t_flags |= TF_DELACK;
+#else /* !TCP_ACK_HACK */
+#define DELAY_ACK(tp, ign) \
+ tp->t_flags |= TF_DELACK;
+#endif /* TCP_ACK_HACK */
+
+
+/*
+ * deps: netinet/tcp_reass.c
+ * tcp_reass_maxqlen = 48 (deafault)
+ * tcp_reass_maxseg = nmbclusters/16 (nmbclusters = 1024 + maxusers * 64 from kern/kern_mbuf.c let's say 256)
+ */
+int
+tcp_reass(PNATState pData, struct tcpcb *tp, struct tcphdr *th, int *tlenp, struct mbuf *m)
+{
+ struct tseg_qent *q;
+ struct tseg_qent *p = NULL;
+ struct tseg_qent *nq;
+ struct tseg_qent *te = NULL;
+ struct socket *so = tp->t_socket;
+ int flags;
+ STAM_PROFILE_START(&pData->StatTCP_reassamble, tcp_reassamble);
+ LogFlowFunc(("ENTER: pData:%p, tp:%R[tcpcb793], th:%p, tlenp:%p, m:%p\n", pData, tp, th, tlenp, m));
+
+ /*
+ * XXX: tcp_reass() is rather inefficient with its data structures
+ * and should be rewritten (see NetBSD for optimizations). While
+ * doing that it should move to its own file tcp_reass.c.
+ */
+
+ /*
+ * Call with th==NULL after become established to
+ * force pre-ESTABLISHED data up to user socket.
+ */
+ if (th == NULL)
+ {
+ LogFlowFunc(("%d -> present\n", __LINE__));
+ goto present;
+ }
+
+ /*
+ * Limit the number of segments in the reassembly queue to prevent
+ * holding on to too many segments (and thus running out of mbufs).
+ * Make sure to let the missing segment through which caused this
+ * queue. Always keep one global queue entry spare to be able to
+ * process the missing segment.
+ */
+ if ( th->th_seq != tp->rcv_nxt
+ && ( tcp_reass_qsize + 1 >= tcp_reass_maxseg
+ || tp->t_segqlen >= tcp_reass_maxqlen))
+ {
+ tcp_reass_overflows++;
+ tcpstat.tcps_rcvmemdrop++;
+ m_freem(pData, m);
+ *tlenp = 0;
+ STAM_PROFILE_STOP(&pData->StatTCP_reassamble, tcp_reassamble);
+ LogFlowFuncLeave();
+ return (0);
+ }
+
+ /*
+ * Allocate a new queue entry. If we can't, or hit the zone limit
+ * just drop the pkt.
+ */
+ te = RTMemAlloc(sizeof(struct tseg_qent));
+ if (te == NULL)
+ {
+ tcpstat.tcps_rcvmemdrop++;
+ m_freem(pData, m);
+ *tlenp = 0;
+ STAM_PROFILE_STOP(&pData->StatTCP_reassamble, tcp_reassamble);
+ LogFlowFuncLeave();
+ return (0);
+ }
+ tp->t_segqlen++;
+ tcp_reass_qsize++;
+
+ /*
+ * Find a segment which begins after this one does.
+ */
+ LIST_FOREACH(q, &tp->t_segq, tqe_q)
+ {
+ if (SEQ_GT(q->tqe_th->th_seq, th->th_seq))
+ break;
+ p = q;
+ }
+
+ /*
+ * If there is a preceding segment, it may provide some of
+ * our data already. If so, drop the data from the incoming
+ * segment. If it provides all of our data, drop us.
+ */
+ if (p != NULL)
+ {
+ int i;
+ /* conversion to int (in i) handles seq wraparound */
+ i = p->tqe_th->th_seq + p->tqe_len - th->th_seq;
+ if (i > 0)
+ {
+ if (i >= *tlenp)
+ {
+ tcpstat.tcps_rcvduppack++;
+ tcpstat.tcps_rcvdupbyte += *tlenp;
+ m_freem(pData, m);
+ RTMemFree(te);
+ tp->t_segqlen--;
+ tcp_reass_qsize--;
+ /*
+ * Try to present any queued data
+ * at the left window edge to the user.
+ * This is needed after the 3-WHS
+ * completes.
+ */
+ LogFlowFunc(("%d -> present\n", __LINE__));
+ goto present; /* ??? */
+ }
+ m_adj(m, i);
+ *tlenp -= i;
+ th->th_seq += i;
+ }
+ }
+ tcpstat.tcps_rcvoopack++;
+ tcpstat.tcps_rcvoobyte += *tlenp;
+
+ /*
+ * While we overlap succeeding segments trim them or,
+ * if they are completely covered, dequeue them.
+ */
+ while (q)
+ {
+ int i = (th->th_seq + *tlenp) - q->tqe_th->th_seq;
+ if (i <= 0)
+ break;
+ if (i < q->tqe_len)
+ {
+ q->tqe_th->th_seq += i;
+ q->tqe_len -= i;
+ m_adj(q->tqe_m, i);
+ break;
+ }
+
+ nq = LIST_NEXT(q, tqe_q);
+ LIST_REMOVE(q, tqe_q);
+ m_freem(pData, q->tqe_m);
+ RTMemFree(q);
+ tp->t_segqlen--;
+ tcp_reass_qsize--;
+ q = nq;
+ }
+
+ /* Insert the new segment queue entry into place. */
+ te->tqe_m = m;
+ te->tqe_th = th;
+ te->tqe_len = *tlenp;
+
+ if (p == NULL)
+ {
+ LIST_INSERT_HEAD(&tp->t_segq, te, tqe_q);
+ }
+ else
+ {
+ LIST_INSERT_AFTER(p, te, tqe_q);
+ }
+
+present:
+ /*
+ * Present data to user, advancing rcv_nxt through
+ * completed sequence space.
+ */
+ if (!TCPS_HAVEESTABLISHED(tp->t_state))
+ {
+ STAM_PROFILE_STOP(&pData->StatTCP_reassamble, tcp_reassamble);
+ return (0);
+ }
+ q = LIST_FIRST(&tp->t_segq);
+ if (!q || q->tqe_th->th_seq != tp->rcv_nxt)
+ {
+ STAM_PROFILE_STOP(&pData->StatTCP_reassamble, tcp_reassamble);
+ return (0);
+ }
+ do
+ {
+ tp->rcv_nxt += q->tqe_len;
+ flags = q->tqe_th->th_flags & TH_FIN;
+ nq = LIST_NEXT(q, tqe_q);
+ LIST_REMOVE(q, tqe_q);
+ /* XXX: This place should be checked for the same code in
+ * original BSD code for Slirp and current BSD used SS_FCANTRCVMORE
+ */
+ if (so->so_state & SS_FCANTSENDMORE)
+ m_freem(pData, q->tqe_m);
+ else
+ sbappend(pData, so, q->tqe_m);
+ RTMemFree(q);
+ tp->t_segqlen--;
+ tcp_reass_qsize--;
+ q = nq;
+ }
+ while (q && q->tqe_th->th_seq == tp->rcv_nxt);
+
+ STAM_PROFILE_STOP(&pData->StatTCP_reassamble, tcp_reassamble);
+ return flags;
+}
+
+/*
+ * TCP input routine, follows pages 65-76 of the
+ * protocol specification dated September, 1981 very closely.
+ */
+void
+tcp_input(PNATState pData, register struct mbuf *m, int iphlen, struct socket *inso)
+{
+ struct ip *ip, *save_ip;
+ register struct tcpiphdr *ti;
+ caddr_t optp = NULL;
+ int optlen = 0;
+ int len, off;
+ int tlen = 0; /* Shut up MSC (didn't check whether MSC was right). */
+ register struct tcpcb *tp = 0;
+ register int tiflags;
+ struct socket *so = 0;
+ int todrop, acked, ourfinisacked, needoutput = 0;
+/* int dropsocket = 0; */
+ int iss = 0;
+ u_long tiwin;
+/* int ts_present = 0; */
+ unsigned ohdrlen;
+ uint8_t ohdr[60 + 8]; /* max IP header plus 8 bytes of payload for icmp */
+
+ STAM_PROFILE_START(&pData->StatTCP_input, counter_input);
+
+ LogFlow(("tcp_input: m = %p, iphlen = %2d, inso = %R[natsock]\n", m, iphlen, inso));
+
+ if (inso != NULL)
+ {
+ QSOCKET_LOCK(tcb);
+ SOCKET_LOCK(inso);
+ QSOCKET_UNLOCK(tcb);
+ }
+ /*
+ * If called with m == 0, then we're continuing the connect
+ */
+ if (m == NULL)
+ {
+ so = inso;
+ Log4(("NAT: tcp_input: %R[natsock]\n", so));
+
+ /* Re-set a few variables */
+ tp = sototcpcb(so);
+
+ m = so->so_m;
+ optp = so->so_optp; /* points into m if set */
+ optlen = so->so_optlen;
+ so->so_m = NULL;
+ so->so_optp = 0;
+ so->so_optlen = 0;
+
+ if (RT_LIKELY(so->so_ohdr != NULL))
+ {
+ RTMemFree(so->so_ohdr);
+ so->so_ohdr = NULL;
+ }
+
+ ti = so->so_ti;
+
+ /** @todo (vvl) clarify why it might happens */
+ if (ti == NULL)
+ {
+ LogRel(("NAT: ti is null. can't do any reseting connection actions\n"));
+ /* mbuf should be cleared in sofree called from tcp_close */
+ tcp_close(pData, tp);
+ STAM_PROFILE_STOP(&pData->StatTCP_input, counter_input);
+ LogFlowFuncLeave();
+ return;
+ }
+
+ tiwin = ti->ti_win;
+ tiflags = ti->ti_flags;
+
+ LogFlowFunc(("%d -> cont_conn\n", __LINE__));
+ goto cont_conn;
+ }
+
+ tcpstat.tcps_rcvtotal++;
+
+ ip = mtod(m, struct ip *);
+
+ /* ip_input() subtracts iphlen from ip::ip_len */
+ AssertStmt(ip->ip_len + iphlen == (ssize_t)m_length(m, NULL), goto drop);
+ if (RT_UNLIKELY(ip->ip_len < sizeof(struct tcphdr)))
+ {
+ /* tcps_rcvshort++; */
+ goto drop;
+ }
+
+ /*
+ * Save a copy of the IP header in case we want to restore it for
+ * sending an ICMP error message in response.
+ *
+ * XXX: This function should really be fixed to not strip IP
+ * options, to not overwrite IP header and to use "tlen" local
+ * variable (instead of ti->ti_len), then "m" could be passed to
+ * icmp_error() directly.
+ */
+ ohdrlen = iphlen + 8;
+ m_copydata(m, 0, ohdrlen, (caddr_t)ohdr);
+ save_ip = (struct ip *)ohdr;
+ save_ip->ip_len += iphlen; /* undo change by ip_input() */
+
+
+ /*
+ * Get IP and TCP header together in first mbuf.
+ * Note: IP leaves IP header in first mbuf.
+ */
+ ti = mtod(m, struct tcpiphdr *);
+ if (iphlen > sizeof(struct ip))
+ {
+ ip_stripoptions(m, (struct mbuf *)0);
+ iphlen = sizeof(struct ip);
+ }
+
+ /*
+ * Checksum extended TCP header and data.
+ */
+ tlen = ((struct ip *)ti)->ip_len;
+ memset(ti->ti_x1, 0, 9);
+ ti->ti_len = RT_H2N_U16((u_int16_t)tlen);
+ len = sizeof(struct ip) + tlen;
+ /* keep checksum for ICMP reply
+ * ti->ti_sum = cksum(m, len);
+ * if (ti->ti_sum) { */
+ if (cksum(m, len))
+ {
+ tcpstat.tcps_rcvbadsum++;
+ LogFlowFunc(("%d -> drop\n", __LINE__));
+ goto drop;
+ }
+
+ /*
+ * Check that TCP offset makes sense,
+ * pull out TCP options and adjust length. XXX
+ */
+ off = ti->ti_off << 2;
+ if ( off < sizeof (struct tcphdr)
+ || off > tlen)
+ {
+ tcpstat.tcps_rcvbadoff++;
+ LogFlowFunc(("%d -> drop\n", __LINE__));
+ goto drop;
+ }
+ tlen -= off;
+ ti->ti_len = tlen;
+ if (off > sizeof (struct tcphdr))
+ {
+ optlen = off - sizeof (struct tcphdr);
+ optp = mtod(m, caddr_t) + sizeof (struct tcpiphdr);
+
+ /*
+ * Do quick retrieval of timestamp options ("options
+ * prediction?"). If timestamp is the only option and it's
+ * formatted as recommended in RFC 1323 appendix A, we
+ * quickly get the values now and not bother calling
+ * tcp_dooptions(), etc.
+ */
+#if 0
+ if (( optlen == TCPOLEN_TSTAMP_APPA
+ || ( optlen > TCPOLEN_TSTAMP_APPA
+ && optp[TCPOLEN_TSTAMP_APPA] == TCPOPT_EOL)) &&
+ *(u_int32_t *)optp == RT_H2N_U32_C(TCPOPT_TSTAMP_HDR) &&
+ (ti->ti_flags & TH_SYN) == 0)
+ {
+ ts_present = 1;
+ ts_val = RT_N2H_U32(*(u_int32_t *)(optp + 4));
+ ts_ecr = RT_N2H_U32(*(u_int32_t *)(optp + 8));
+ optp = NULL; / * we have parsed the options * /
+ }
+#endif
+ }
+ tiflags = ti->ti_flags;
+
+ /*
+ * Convert TCP protocol specific fields to host format.
+ */
+ NTOHL(ti->ti_seq);
+ NTOHL(ti->ti_ack);
+ NTOHS(ti->ti_win);
+ NTOHS(ti->ti_urp);
+
+ /*
+ * Drop TCP, IP headers and TCP options.
+ */
+ m->m_data += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ m->m_len -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+
+ /*
+ * Locate pcb for segment.
+ */
+findso:
+ LogFlowFunc(("(enter) findso: %R[natsock]\n", so));
+ if (so != NULL && so != &tcb)
+ SOCKET_UNLOCK(so);
+ QSOCKET_LOCK(tcb);
+ so = tcp_last_so;
+ if ( so->so_fport != ti->ti_dport
+ || so->so_lport != ti->ti_sport
+ || so->so_laddr.s_addr != ti->ti_src.s_addr
+ || so->so_faddr.s_addr != ti->ti_dst.s_addr)
+ {
+ QSOCKET_UNLOCK(tcb);
+ /** @todo fix SOLOOKUP macrodefinition to be usable here */
+ so = solookup(&tcb, ti->ti_src, ti->ti_sport,
+ ti->ti_dst, ti->ti_dport);
+ if (so)
+ {
+ tcp_last_so = so;
+ }
+ ++tcpstat.tcps_socachemiss;
+ }
+ else
+ {
+ SOCKET_LOCK(so);
+ QSOCKET_UNLOCK(tcb);
+ }
+ LogFlowFunc(("(leave) findso: %R[natsock]\n", so));
+
+ /*
+ * Check whether the packet is targeting CTL_ALIAS and drop it if the connection wasn't
+ * initiated by localhost (so == NULL), see @bugref{9896}.
+ */
+ if ( (CTL_CHECK(ti->ti_dst.s_addr, CTL_ALIAS))
+ && !pData->fLocalhostReachable
+ && !so)
+ {
+ LogFlowFunc(("Packet for CTL_ALIAS and fLocalhostReachable=false so=NULL -> drop\n"));
+ goto drop;
+ }
+
+ /*
+ * If the state is CLOSED (i.e., TCB does not exist) then
+ * all data in the incoming segment is discarded.
+ * If the TCB exists but is in CLOSED state, it is embryonic,
+ * but should either do a listen or a connect soon.
+ *
+ * state == CLOSED means we've done socreate() but haven't
+ * attached it to a protocol yet...
+ *
+ * XXX If a TCB does not exist, and the TH_SYN flag is
+ * the only flag set, then create a session, mark it
+ * as if it was LISTENING, and continue...
+ */
+ if (so == 0)
+ {
+ if ((tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) != TH_SYN)
+ {
+ LogFlowFunc(("%d -> dropwithreset\n", __LINE__));
+ goto dropwithreset;
+ }
+
+ if ((so = socreate()) == NULL)
+ {
+ LogFlowFunc(("%d -> dropwithreset\n", __LINE__));
+ goto dropwithreset;
+ }
+ if (tcp_attach(pData, so) < 0)
+ {
+ RTMemFree(so); /* Not sofree (if it failed, it's not insqued) */
+ LogFlowFunc(("%d -> dropwithreset\n", __LINE__));
+ goto dropwithreset;
+ }
+ SOCKET_LOCK(so);
+ sbreserve(pData, &so->so_snd, tcp_sndspace);
+ sbreserve(pData, &so->so_rcv, tcp_rcvspace);
+
+/* tcp_last_so = so; */ /* XXX ? */
+/* tp = sototcpcb(so); */
+
+ so->so_laddr = ti->ti_src;
+ so->so_lport = ti->ti_sport;
+ so->so_faddr = ti->ti_dst;
+ so->so_fport = ti->ti_dport;
+
+ so->so_iptos = ((struct ip *)ti)->ip_tos;
+
+ tp = sototcpcb(so);
+ TCP_STATE_SWITCH_TO(tp, TCPS_LISTEN);
+ }
+
+ /*
+ * If this is a still-connecting socket, this probably
+ * a retransmit of the SYN. Whether it's a retransmit SYN
+ * or something else, we nuke it.
+ */
+ if (so->so_state & SS_ISFCONNECTING)
+ {
+ LogFlowFunc(("%d -> drop\n", __LINE__));
+ goto drop;
+ }
+
+ tp = sototcpcb(so);
+
+ /* XXX Should never fail */
+ if (tp == 0)
+ {
+ LogFlowFunc(("%d -> dropwithreset\n", __LINE__));
+ goto dropwithreset;
+ }
+ if (tp->t_state == TCPS_CLOSED)
+ {
+ LogFlowFunc(("%d -> drop\n", __LINE__));
+ goto drop;
+ }
+
+ /* Unscale the window into a 32-bit value. */
+/* if ((tiflags & TH_SYN) == 0)
+ * tiwin = ti->ti_win << tp->snd_scale;
+ * else
+ */
+ tiwin = ti->ti_win;
+
+ /*
+ * Segment received on connection.
+ * Reset idle time and keep-alive timer.
+ */
+ tp->t_idle = 0;
+ if (so_options)
+ tp->t_timer[TCPT_KEEP] = tcp_keepintvl;
+ else
+ tp->t_timer[TCPT_KEEP] = tcp_keepidle;
+
+ /*
+ * Process options if not in LISTEN state,
+ * else do it below (after getting remote address).
+ */
+ if (optp && tp->t_state != TCPS_LISTEN)
+ tcp_dooptions(pData, tp, (u_char *)optp, optlen, ti);
+/* , */
+/* &ts_present, &ts_val, &ts_ecr); */
+
+ /*
+ * Header prediction: check for the two common cases
+ * of a uni-directional data xfer. If the packet has
+ * no control flags, is in-sequence, the window didn't
+ * change and we're not retransmitting, it's a
+ * candidate. If the length is zero and the ack moved
+ * forward, we're the sender side of the xfer. Just
+ * free the data acked & wake any higher level process
+ * that was blocked waiting for space. If the length
+ * is non-zero and the ack didn't move, we're the
+ * receiver side. If we're getting packets in-order
+ * (the reassembly queue is empty), add the data to
+ * the socket buffer and note that we need a delayed ack.
+ *
+ * XXX Some of these tests are not needed
+ * eg: the tiwin == tp->snd_wnd prevents many more
+ * predictions.. with no *real* advantage..
+ */
+ if ( tp->t_state == TCPS_ESTABLISHED
+ && (tiflags & (TH_SYN|TH_FIN|TH_RST|TH_URG|TH_ACK)) == TH_ACK
+/* && (!ts_present || TSTMP_GEQ(ts_val, tp->ts_recent)) */
+ && ti->ti_seq == tp->rcv_nxt
+ && tiwin && tiwin == tp->snd_wnd
+ && tp->snd_nxt == tp->snd_max)
+ {
+ /*
+ * If last ACK falls within this segment's sequence numbers,
+ * record the timestamp.
+ */
+#if 0
+ if (ts_present && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent) &&
+ SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len))
+ {
+ tp->ts_recent_age = tcp_now;
+ tp->ts_recent = ts_val;
+ }
+#endif
+
+ if (ti->ti_len == 0)
+ {
+ if ( SEQ_GT(ti->ti_ack, tp->snd_una)
+ && SEQ_LEQ(ti->ti_ack, tp->snd_max)
+ && tp->snd_cwnd >= tp->snd_wnd)
+ {
+ /*
+ * this is a pure ack for outstanding data.
+ */
+ ++tcpstat.tcps_predack;
+#if 0
+ if (ts_present)
+ tcp_xmit_timer(tp, tcp_now-ts_ecr+1);
+ else
+#endif
+ if ( tp->t_rtt
+ && SEQ_GT(ti->ti_ack, tp->t_rtseq))
+ tcp_xmit_timer(pData, tp, tp->t_rtt);
+ acked = ti->ti_ack - tp->snd_una;
+ tcpstat.tcps_rcvackpack++;
+ tcpstat.tcps_rcvackbyte += acked;
+ sbdrop(&so->so_snd, acked);
+ tp->snd_una = ti->ti_ack;
+ m_freem(pData, m);
+
+ /*
+ * If all outstanding data are acked, stop
+ * retransmit timer, otherwise restart timer
+ * using current (possibly backed-off) value.
+ * If process is waiting for space,
+ * wakeup/selwakeup/signal. If data
+ * are ready to send, let tcp_output
+ * decide between more output or persist.
+ */
+ if (tp->snd_una == tp->snd_max)
+ tp->t_timer[TCPT_REXMT] = 0;
+ else if (tp->t_timer[TCPT_PERSIST] == 0)
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+
+ /*
+ * There's room in so_snd, sowwakup will read()
+ * from the socket if we can
+ */
+#if 0
+ if (so->so_snd.sb_flags & SB_NOTIFY)
+ sowwakeup(so);
+#endif
+ /*
+ * This is called because sowwakeup might have
+ * put data into so_snd. Since we don't so sowwakeup,
+ * we don't need this.. XXX???
+ */
+ if (SBUF_LEN(&so->so_snd))
+ (void) tcp_output(pData, tp);
+
+ SOCKET_UNLOCK(so);
+ STAM_PROFILE_STOP(&pData->StatTCP_input, counter_input);
+ return;
+ }
+ }
+ else if ( ti->ti_ack == tp->snd_una
+ && LIST_EMPTY(&tp->t_segq)
+ && ti->ti_len <= sbspace(&so->so_rcv))
+ {
+ /*
+ * this is a pure, in-sequence data packet
+ * with nothing on the reassembly queue and
+ * we have enough buffer space to take it.
+ */
+ ++tcpstat.tcps_preddat;
+ tp->rcv_nxt += ti->ti_len;
+ tcpstat.tcps_rcvpack++;
+ tcpstat.tcps_rcvbyte += ti->ti_len;
+ /*
+ * Add data to socket buffer.
+ */
+ sbappend(pData, so, m);
+
+ /*
+ * XXX This is called when data arrives. Later, check
+ * if we can actually write() to the socket
+ * XXX Need to check? It's be NON_BLOCKING
+ */
+/* sorwakeup(so); */
+
+ /*
+ * If this is a short packet, then ACK now - with Nagle
+ * congestion avoidance sender won't send more until
+ * he gets an ACK.
+ *
+ * It is better to not delay acks at all to maximize
+ * TCP throughput. See RFC 2581.
+ */
+ tp->t_flags |= TF_ACKNOW;
+ tcp_output(pData, tp);
+ SOCKET_UNLOCK(so);
+ STAM_PROFILE_STOP(&pData->StatTCP_input, counter_input);
+ return;
+ }
+ } /* header prediction */
+ /*
+ * Calculate amount of space in receive window,
+ * and then do TCP input processing.
+ * Receive window is amount of space in rcv queue,
+ * but not less than advertised window.
+ */
+ {
+ int win;
+ win = sbspace(&so->so_rcv);
+ if (win < 0)
+ win = 0;
+ tp->rcv_wnd = max(win, (int)(tp->rcv_adv - tp->rcv_nxt));
+ }
+
+ switch (tp->t_state)
+ {
+ /*
+ * If the state is LISTEN then ignore segment if it contains an RST.
+ * If the segment contains an ACK then it is bad and send a RST.
+ * If it does not contain a SYN then it is not interesting; drop it.
+ * Don't bother responding if the destination was a broadcast.
+ * Otherwise initialize tp->rcv_nxt, and tp->irs, select an initial
+ * tp->iss, and send a segment:
+ * <SEQ=ISS><ACK=RCV_NXT><CTL=SYN,ACK>
+ * Also initialize tp->snd_nxt to tp->iss+1 and tp->snd_una to tp->iss.
+ * Fill in remote peer address fields if not previously specified.
+ * Enter SYN_RECEIVED state, and process any other fields of this
+ * segment in this state.
+ */
+ case TCPS_LISTEN:
+ {
+ if (tiflags & TH_RST)
+ {
+ LogFlowFunc(("%d -> drop\n", __LINE__));
+ goto drop;
+ }
+ if (tiflags & TH_ACK)
+ {
+ LogFlowFunc(("%d -> dropwithreset\n", __LINE__));
+ goto dropwithreset;
+ }
+ if ((tiflags & TH_SYN) == 0)
+ {
+ LogFlowFunc(("%d -> drop\n", __LINE__));
+ goto drop;
+ }
+
+ /*
+ * This has way too many gotos...
+ * But a bit of spaghetti code never hurt anybody :)
+ */
+ if ( (tcp_fconnect(pData, so) == -1)
+ && errno != EINPROGRESS
+ && errno != EWOULDBLOCK)
+ {
+ u_char code = ICMP_UNREACH_NET;
+ Log2((" tcp fconnect errno = %d (%s)\n", errno, strerror(errno)));
+ if (errno == ECONNREFUSED)
+ {
+ /* ACK the SYN, send RST to refuse the connection */
+ tcp_respond(pData, tp, ti, m, ti->ti_seq+1, (tcp_seq)0,
+ TH_RST|TH_ACK);
+ }
+ else
+ {
+ if (errno == EHOSTUNREACH)
+ code = ICMP_UNREACH_HOST;
+ HTONL(ti->ti_seq); /* restore tcp header */
+ HTONL(ti->ti_ack);
+ HTONS(ti->ti_win);
+ HTONS(ti->ti_urp);
+ m->m_data -= sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ m->m_len += sizeof(struct tcpiphdr)+off-sizeof(struct tcphdr);
+ *ip = *save_ip;
+ icmp_error(pData, m, ICMP_UNREACH, code, 0, strerror(errno));
+ tp->t_socket->so_m = NULL;
+ }
+ tp = tcp_close(pData, tp);
+ }
+ else
+ {
+ /*
+ * Haven't connected yet, save the current mbuf
+ * and ti, and return
+ * XXX Some OS's don't tell us whether the connect()
+ * succeeded or not. So we must time it out.
+ */
+ so->so_m = m;
+ so->so_ti = ti;
+ so->so_ohdr = RTMemDup(ohdr, ohdrlen);
+ so->so_optp = optp;
+ so->so_optlen = optlen;
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ TCP_STATE_SWITCH_TO(tp, TCPS_SYN_RECEIVED);
+ }
+ SOCKET_UNLOCK(so);
+ STAM_PROFILE_STOP(&pData->StatTCP_input, counter_input);
+ LogFlowFuncLeave();
+ return;
+
+cont_conn:
+ /* m==NULL
+ * Check if the connect succeeded
+ */
+ LogFlowFunc(("cont_conn:\n"));
+ if (so->so_state & SS_NOFDREF)
+ {
+ tp = tcp_close(pData, tp);
+ LogFlowFunc(("%d -> dropwithreset\n", __LINE__));
+ goto dropwithreset;
+ }
+
+ tcp_template(tp);
+
+ if (optp)
+ tcp_dooptions(pData, tp, (u_char *)optp, optlen, ti);
+
+ if (iss)
+ tp->iss = iss;
+ else
+ tp->iss = tcp_iss;
+ tcp_iss += TCP_ISSINCR/2;
+ tp->irs = ti->ti_seq;
+ tcp_sendseqinit(tp);
+ tcp_rcvseqinit(tp);
+ tp->t_flags |= TF_ACKNOW;
+ TCP_STATE_SWITCH_TO(tp, TCPS_SYN_RECEIVED);
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ tcpstat.tcps_accepts++;
+ LogFlowFunc(("%d -> trimthenstep6\n", __LINE__));
+ goto trimthenstep6;
+ } /* case TCPS_LISTEN */
+
+ /*
+ * If the state is SYN_SENT:
+ * if seg contains an ACK, but not for our SYN, drop the input.
+ * if seg contains a RST, then drop the connection.
+ * if seg does not contain SYN, then drop it.
+ * Otherwise this is an acceptable SYN segment
+ * initialize tp->rcv_nxt and tp->irs
+ * if seg contains ack then advance tp->snd_una
+ * if SYN has been acked change to ESTABLISHED else SYN_RCVD state
+ * arrange for segment to be acked (eventually)
+ * continue processing rest of data/controls, beginning with URG
+ */
+ case TCPS_SYN_SENT:
+ if ( (tiflags & TH_ACK)
+ && ( SEQ_LEQ(ti->ti_ack, tp->iss)
+ || SEQ_GT(ti->ti_ack, tp->snd_max)))
+ {
+ LogFlowFunc(("%d -> dropwithreset\n", __LINE__));
+ goto dropwithreset;
+ }
+
+ if (tiflags & TH_RST)
+ {
+ if (tiflags & TH_ACK)
+ tp = tcp_drop(pData, tp, 0); /* XXX Check t_softerror! */
+ LogFlowFunc(("%d -> drop\n", __LINE__));
+ goto drop;
+ }
+
+ if ((tiflags & TH_SYN) == 0)
+ {
+ LogFlowFunc(("%d -> drop\n", __LINE__));
+ goto drop;
+ }
+ if (tiflags & TH_ACK)
+ {
+ tp->snd_una = ti->ti_ack;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_una))
+ tp->snd_nxt = tp->snd_una;
+ }
+
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->irs = ti->ti_seq;
+ tcp_rcvseqinit(tp);
+ tp->t_flags |= TF_ACKNOW;
+ if (tiflags & TH_ACK && SEQ_GT(tp->snd_una, tp->iss))
+ {
+ tcpstat.tcps_connects++;
+ soisfconnected(so);
+ TCP_STATE_SWITCH_TO(tp, TCPS_ESTABLISHED);
+
+ /* Do window scaling on this connection? */
+#if 0
+ if (( tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE))
+ == (TF_RCVD_SCALE|TF_REQ_SCALE))
+ {
+ tp->snd_scale = tp->requested_s_scale;
+ tp->rcv_scale = tp->request_r_scale;
+ }
+#endif
+ (void) tcp_reass(pData, tp, (struct tcphdr *)0, NULL, (struct mbuf *)0);
+ /*
+ * if we didn't have to retransmit the SYN,
+ * use its rtt as our initial srtt & rtt var.
+ */
+ if (tp->t_rtt)
+ tcp_xmit_timer(pData, tp, tp->t_rtt);
+ }
+ else
+ TCP_STATE_SWITCH_TO(tp, TCPS_SYN_RECEIVED);
+
+trimthenstep6:
+ LogFlowFunc(("trimthenstep6:\n"));
+ /*
+ * Advance ti->ti_seq to correspond to first data byte.
+ * If data, trim to stay within window,
+ * dropping FIN if necessary.
+ */
+ ti->ti_seq++;
+ if (ti->ti_len > tp->rcv_wnd)
+ {
+ todrop = ti->ti_len - tp->rcv_wnd;
+ m_adj(m, -todrop);
+ ti->ti_len = tp->rcv_wnd;
+ tiflags &= ~TH_FIN;
+ tcpstat.tcps_rcvpackafterwin++;
+ tcpstat.tcps_rcvbyteafterwin += todrop;
+ }
+ tp->snd_wl1 = ti->ti_seq - 1;
+ tp->rcv_up = ti->ti_seq;
+ LogFlowFunc(("%d -> step6\n", __LINE__));
+ goto step6;
+ } /* switch tp->t_state */
+ /*
+ * States other than LISTEN or SYN_SENT.
+ * First check timestamp, if present.
+ * Then check that at least some bytes of segment are within
+ * receive window. If segment begins before rcv_nxt,
+ * drop leading data (and SYN); if nothing left, just ack.
+ *
+ * RFC 1323 PAWS: If we have a timestamp reply on this segment
+ * and it's less than ts_recent, drop it.
+ */
+#if 0
+ if ( ts_present
+ && (tiflags & TH_RST) == 0
+ && tp->ts_recent
+ && TSTMP_LT(ts_val, tp->ts_recent))
+ {
+ /* Check to see if ts_recent is over 24 days old. */
+ if ((int)(tcp_now - tp->ts_recent_age) > TCP_PAWS_IDLE)
+ {
+ /*
+ * Invalidate ts_recent. If this segment updates
+ * ts_recent, the age will be reset later and ts_recent
+ * will get a valid value. If it does not, setting
+ * ts_recent to zero will at least satisfy the
+ * requirement that zero be placed in the timestamp
+ * echo reply when ts_recent isn't valid. The
+ * age isn't reset until we get a valid ts_recent
+ * because we don't want out-of-order segments to be
+ * dropped when ts_recent is old.
+ */
+ tp->ts_recent = 0;
+ }
+ else
+ {
+ tcpstat.tcps_rcvduppack++;
+ tcpstat.tcps_rcvdupbyte += ti->ti_len;
+ tcpstat.tcps_pawsdrop++;
+ goto dropafterack;
+ }
+ }
+#endif
+
+ todrop = tp->rcv_nxt - ti->ti_seq;
+ if (todrop > 0)
+ {
+ if (tiflags & TH_SYN)
+ {
+ tiflags &= ~TH_SYN;
+ ti->ti_seq++;
+ if (ti->ti_urp > 1)
+ ti->ti_urp--;
+ else
+ tiflags &= ~TH_URG;
+ todrop--;
+ }
+ /*
+ * Following if statement from Stevens, vol. 2, p. 960.
+ */
+ if ( todrop > ti->ti_len
+ || ( todrop == ti->ti_len
+ && (tiflags & TH_FIN) == 0))
+ {
+ /*
+ * Any valid FIN must be to the left of the window.
+ * At this point the FIN must be a duplicate or out
+ * of sequence; drop it.
+ */
+ tiflags &= ~TH_FIN;
+
+ /*
+ * Send an ACK to resynchronize and drop any data.
+ * But keep on processing for RST or ACK.
+ */
+ tp->t_flags |= TF_ACKNOW;
+ todrop = ti->ti_len;
+ tcpstat.tcps_rcvduppack++;
+ tcpstat.tcps_rcvdupbyte += todrop;
+ }
+ else
+ {
+ tcpstat.tcps_rcvpartduppack++;
+ tcpstat.tcps_rcvpartdupbyte += todrop;
+ }
+ m_adj(m, todrop);
+ ti->ti_seq += todrop;
+ ti->ti_len -= todrop;
+ if (ti->ti_urp > todrop)
+ ti->ti_urp -= todrop;
+ else
+ {
+ tiflags &= ~TH_URG;
+ ti->ti_urp = 0;
+ }
+ }
+ /*
+ * If new data are received on a connection after the
+ * user processes are gone, then RST the other end.
+ */
+ if ( (so->so_state & SS_NOFDREF)
+ && tp->t_state > TCPS_CLOSE_WAIT && ti->ti_len)
+ {
+ tp = tcp_close(pData, tp);
+ tcpstat.tcps_rcvafterclose++;
+ LogFlowFunc(("%d -> dropwithreset\n", __LINE__));
+ goto dropwithreset;
+ }
+
+ /*
+ * If segment ends after window, drop trailing data
+ * (and PUSH and FIN); if nothing left, just ACK.
+ */
+ todrop = (ti->ti_seq+ti->ti_len) - (tp->rcv_nxt+tp->rcv_wnd);
+ if (todrop > 0)
+ {
+ tcpstat.tcps_rcvpackafterwin++;
+ if (todrop >= ti->ti_len)
+ {
+ tcpstat.tcps_rcvbyteafterwin += ti->ti_len;
+ /*
+ * If a new connection request is received
+ * while in TIME_WAIT, drop the old connection
+ * and start over if the sequence numbers
+ * are above the previous ones.
+ */
+ if ( tiflags & TH_SYN
+ && tp->t_state == TCPS_TIME_WAIT
+ && SEQ_GT(ti->ti_seq, tp->rcv_nxt))
+ {
+ iss = tp->rcv_nxt + TCP_ISSINCR;
+ tp = tcp_close(pData, tp);
+ SOCKET_UNLOCK(tp->t_socket);
+ LogFlowFunc(("%d -> findso\n", __LINE__));
+ goto findso;
+ }
+ /*
+ * If window is closed can only take segments at
+ * window edge, and have to drop data and PUSH from
+ * incoming segments. Continue processing, but
+ * remember to ack. Otherwise, drop segment
+ * and ack.
+ */
+ if (tp->rcv_wnd == 0 && ti->ti_seq == tp->rcv_nxt)
+ {
+ tp->t_flags |= TF_ACKNOW;
+ tcpstat.tcps_rcvwinprobe++;
+ }
+ else
+ {
+ LogFlowFunc(("%d -> dropafterack\n", __LINE__));
+ goto dropafterack;
+ }
+ }
+ else
+ tcpstat.tcps_rcvbyteafterwin += todrop;
+ m_adj(m, -todrop);
+ ti->ti_len -= todrop;
+ tiflags &= ~(TH_PUSH|TH_FIN);
+ }
+
+ /*
+ * If last ACK falls within this segment's sequence numbers,
+ * record its timestamp.
+ */
+#if 0
+ if ( ts_present
+ && SEQ_LEQ(ti->ti_seq, tp->last_ack_sent)
+ && SEQ_LT(tp->last_ack_sent, ti->ti_seq + ti->ti_len + ((tiflags & (TH_SYN|TH_FIN)) != 0)))
+ {
+ tp->ts_recent_age = tcp_now;
+ tp->ts_recent = ts_val;
+ }
+#endif
+
+ /*
+ * If the RST bit is set examine the state:
+ * SYN_RECEIVED STATE:
+ * If passive open, return to LISTEN state.
+ * If active open, inform user that connection was refused.
+ * ESTABLISHED, FIN_WAIT_1, FIN_WAIT2, CLOSE_WAIT STATES:
+ * Inform user that connection was reset, and close tcb.
+ * CLOSING, LAST_ACK, TIME_WAIT STATES
+ * Close the tcb.
+ */
+ if (tiflags&TH_RST)
+ switch (tp->t_state)
+ {
+ case TCPS_SYN_RECEIVED:
+/* so->so_error = ECONNREFUSED; */
+ LogFlowFunc(("%d -> close\n", __LINE__));
+ goto close;
+
+ case TCPS_ESTABLISHED:
+ case TCPS_FIN_WAIT_1:
+ case TCPS_FIN_WAIT_2:
+ case TCPS_CLOSE_WAIT:
+/* so->so_error = ECONNRESET; */
+close:
+ LogFlowFunc(("close:\n"));
+ TCP_STATE_SWITCH_TO(tp, TCPS_CLOSED);
+ tcpstat.tcps_drops++;
+ tp = tcp_close(pData, tp);
+ LogFlowFunc(("%d -> drop\n", __LINE__));
+ goto drop;
+
+ case TCPS_CLOSING:
+ case TCPS_LAST_ACK:
+ case TCPS_TIME_WAIT:
+ tp = tcp_close(pData, tp);
+ LogFlowFunc(("%d -> drop\n", __LINE__));
+ goto drop;
+ }
+
+ /*
+ * If a SYN is in the window, then this is an
+ * error and we send an RST and drop the connection.
+ */
+ if (tiflags & TH_SYN)
+ {
+ tp = tcp_drop(pData, tp, 0);
+ LogFlowFunc(("%d -> dropwithreset\n", __LINE__));
+ goto dropwithreset;
+ }
+
+ /*
+ * If the ACK bit is off we drop the segment and return.
+ */
+ if ((tiflags & TH_ACK) == 0)
+ {
+ LogFlowFunc(("%d -> drop\n", __LINE__));
+ goto drop;
+ }
+
+ /*
+ * Ack processing.
+ */
+ switch (tp->t_state)
+ {
+ /*
+ * In SYN_RECEIVED state if the ack ACKs our SYN then enter
+ * ESTABLISHED state and continue processing, otherwise
+ * send an RST. una<=ack<=max
+ */
+ case TCPS_SYN_RECEIVED:
+ LogFlowFunc(("%d -> TCPS_SYN_RECEIVED\n", __LINE__));
+ if ( SEQ_GT(tp->snd_una, ti->ti_ack)
+ || SEQ_GT(ti->ti_ack, tp->snd_max))
+ goto dropwithreset;
+ tcpstat.tcps_connects++;
+ TCP_STATE_SWITCH_TO(tp, TCPS_ESTABLISHED);
+ /*
+ * The sent SYN is ack'ed with our sequence number +1
+ * The first data byte already in the buffer will get
+ * lost if no correction is made. This is only needed for
+ * SS_CTL since the buffer is empty otherwise.
+ * tp->snd_una++; or:
+ */
+ tp->snd_una = ti->ti_ack;
+ soisfconnected(so);
+
+ /* Do window scaling? */
+#if 0
+ if ( (tp->t_flags & (TF_RCVD_SCALE|TF_REQ_SCALE))
+ == (TF_RCVD_SCALE|TF_REQ_SCALE))
+ {
+ tp->snd_scale = tp->requested_s_scale;
+ tp->rcv_scale = tp->request_r_scale;
+ }
+#endif
+ (void) tcp_reass(pData, tp, (struct tcphdr *)0, (int *)0, (struct mbuf *)0);
+ tp->snd_wl1 = ti->ti_seq - 1;
+ /* Avoid ack processing; snd_una==ti_ack => dup ack */
+ LogFlowFunc(("%d -> synrx_to_est\n", __LINE__));
+ goto synrx_to_est;
+ /* fall into ... */
+
+ /*
+ * In ESTABLISHED state: drop duplicate ACKs; ACK out of range
+ * ACKs. If the ack is in the range
+ * tp->snd_una < ti->ti_ack <= tp->snd_max
+ * then advance tp->snd_una to ti->ti_ack and drop
+ * data from the retransmission queue. If this ACK reflects
+ * more up to date window information we update our window information.
+ */
+ case TCPS_ESTABLISHED:
+ case TCPS_FIN_WAIT_1:
+ case TCPS_FIN_WAIT_2:
+ case TCPS_CLOSE_WAIT:
+ case TCPS_CLOSING:
+ case TCPS_LAST_ACK:
+ case TCPS_TIME_WAIT:
+ LogFlowFunc(("%d -> TCPS_ESTABLISHED|TCPS_FIN_WAIT_1|TCPS_FIN_WAIT_2|TCPS_CLOSE_WAIT|"
+ "TCPS_CLOSING|TCPS_LAST_ACK|TCPS_TIME_WAIT\n", __LINE__));
+ if (SEQ_LEQ(ti->ti_ack, tp->snd_una))
+ {
+ if (ti->ti_len == 0 && tiwin == tp->snd_wnd)
+ {
+ tcpstat.tcps_rcvdupack++;
+ Log2((" dup ack m = %p, so = %p\n", m, so));
+ /*
+ * If we have outstanding data (other than
+ * a window probe), this is a completely
+ * duplicate ack (ie, window info didn't
+ * change), the ack is the biggest we've
+ * seen and we've seen exactly our rexmt
+ * threshold of them, assume a packet
+ * has been dropped and retransmit it.
+ * Kludge snd_nxt & the congestion
+ * window so we send only this one
+ * packet.
+ *
+ * We know we're losing at the current
+ * window size so do congestion avoidance
+ * (set ssthresh to half the current window
+ * and pull our congestion window back to
+ * the new ssthresh).
+ *
+ * Dup acks mean that packets have left the
+ * network (they're now cached at the receiver)
+ * so bump cwnd by the amount in the receiver
+ * to keep a constant cwnd packets in the
+ * network.
+ */
+ if ( tp->t_timer[TCPT_REXMT] == 0
+ || ti->ti_ack != tp->snd_una)
+ tp->t_dupacks = 0;
+ else if (++tp->t_dupacks == tcprexmtthresh)
+ {
+ tcp_seq onxt = tp->snd_nxt;
+ u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg;
+ if (win < 2)
+ win = 2;
+ tp->snd_ssthresh = win * tp->t_maxseg;
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->t_rtt = 0;
+ tp->snd_nxt = ti->ti_ack;
+ tp->snd_cwnd = tp->t_maxseg;
+ (void) tcp_output(pData, tp);
+ tp->snd_cwnd = tp->snd_ssthresh +
+ tp->t_maxseg * tp->t_dupacks;
+ if (SEQ_GT(onxt, tp->snd_nxt))
+ tp->snd_nxt = onxt;
+ LogFlowFunc(("%d -> drop\n", __LINE__));
+ goto drop;
+ }
+ else if (tp->t_dupacks > tcprexmtthresh)
+ {
+ tp->snd_cwnd += tp->t_maxseg;
+ (void) tcp_output(pData, tp);
+ LogFlowFunc(("%d -> drop\n", __LINE__));
+ goto drop;
+ }
+ }
+ else
+ tp->t_dupacks = 0;
+ break;
+ }
+synrx_to_est:
+ LogFlowFunc(("synrx_to_est:\n"));
+ /*
+ * If the congestion window was inflated to account
+ * for the other side's cached packets, retract it.
+ */
+ if ( tp->t_dupacks > tcprexmtthresh
+ && tp->snd_cwnd > tp->snd_ssthresh)
+ tp->snd_cwnd = tp->snd_ssthresh;
+ tp->t_dupacks = 0;
+ if (SEQ_GT(ti->ti_ack, tp->snd_max))
+ {
+ tcpstat.tcps_rcvacktoomuch++;
+ LogFlowFunc(("%d -> dropafterack\n", __LINE__));
+ goto dropafterack;
+ }
+ acked = ti->ti_ack - tp->snd_una;
+ tcpstat.tcps_rcvackpack++;
+ tcpstat.tcps_rcvackbyte += acked;
+
+ /*
+ * If we have a timestamp reply, update smoothed
+ * round trip time. If no timestamp is present but
+ * transmit timer is running and timed sequence
+ * number was acked, update smoothed round trip time.
+ * Since we now have an rtt measurement, cancel the
+ * timer backoff (cf., Phil Karn's retransmit alg.).
+ * Recompute the initial retransmit timer.
+ */
+#if 0
+ if (ts_present)
+ tcp_xmit_timer(tp, tcp_now-ts_ecr+1);
+ else
+#endif
+ if (tp->t_rtt && SEQ_GT(ti->ti_ack, tp->t_rtseq))
+ tcp_xmit_timer(pData, tp, tp->t_rtt);
+
+ /*
+ * If all outstanding data is acked, stop retransmit
+ * timer and remember to restart (more output or persist).
+ * If there is more data to be acked, restart retransmit
+ * timer, using current (possibly backed-off) value.
+ */
+ if (ti->ti_ack == tp->snd_max)
+ {
+ tp->t_timer[TCPT_REXMT] = 0;
+ needoutput = 1;
+ }
+ else if (tp->t_timer[TCPT_PERSIST] == 0)
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ /*
+ * When new data is acked, open the congestion window.
+ * If the window gives us less than ssthresh packets
+ * in flight, open exponentially (maxseg per packet).
+ * Otherwise open linearly: maxseg per window
+ * (maxseg^2 / cwnd per packet).
+ */
+ {
+ register u_int cw = tp->snd_cwnd;
+ register u_int incr = tp->t_maxseg;
+
+ if (cw > tp->snd_ssthresh)
+ incr = incr * incr / cw;
+ tp->snd_cwnd = min(cw + incr, TCP_MAXWIN<<tp->snd_scale);
+ }
+ if (acked > SBUF_LEN(&so->so_snd))
+ {
+ tp->snd_wnd -= SBUF_LEN(&so->so_snd);
+ sbdrop(&so->so_snd, (int)so->so_snd.sb_cc);
+ ourfinisacked = 1;
+ }
+ else
+ {
+ sbdrop(&so->so_snd, acked);
+ tp->snd_wnd -= acked;
+ ourfinisacked = 0;
+ }
+ /*
+ * XXX sowwakup is called when data is acked and there's room for
+ * for more data... it should read() the socket
+ */
+#if 0
+ if (so->so_snd.sb_flags & SB_NOTIFY)
+ sowwakeup(so);
+#endif
+ tp->snd_una = ti->ti_ack;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_una))
+ tp->snd_nxt = tp->snd_una;
+
+ switch (tp->t_state)
+ {
+ /*
+ * In FIN_WAIT_1 STATE in addition to the processing
+ * for the ESTABLISHED state if our FIN is now acknowledged
+ * then enter FIN_WAIT_2.
+ */
+ case TCPS_FIN_WAIT_1:
+ if (ourfinisacked)
+ {
+ /*
+ * If we can't receive any more
+ * data, then closing user can proceed.
+ * Starting the timer is contrary to the
+ * specification, but if we don't get a FIN
+ * we'll hang forever.
+ */
+ if (so->so_state & SS_FCANTRCVMORE)
+ {
+ soisfdisconnected(so);
+ tp->t_timer[TCPT_2MSL] = tcp_maxidle;
+ }
+ TCP_STATE_SWITCH_TO(tp, TCPS_FIN_WAIT_2);
+ }
+ break;
+
+ /*
+ * In CLOSING STATE in addition to the processing for
+ * the ESTABLISHED state if the ACK acknowledges our FIN
+ * then enter the TIME-WAIT state, otherwise ignore
+ * the segment.
+ */
+ case TCPS_CLOSING:
+ if (ourfinisacked)
+ {
+ TCP_STATE_SWITCH_TO(tp, TCPS_TIME_WAIT);
+ tcp_canceltimers(tp);
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ soisfdisconnected(so);
+ }
+ break;
+
+ /*
+ * In LAST_ACK, we may still be waiting for data to drain
+ * and/or to be acked, as well as for the ack of our FIN.
+ * If our FIN is now acknowledged, delete the TCB,
+ * enter the closed state and return.
+ */
+ case TCPS_LAST_ACK:
+ if (ourfinisacked)
+ {
+ tp = tcp_close(pData, tp);
+ LogFlowFunc(("%d -> drop\n", __LINE__));
+ goto drop;
+ }
+ break;
+
+ /*
+ * In TIME_WAIT state the only thing that should arrive
+ * is a retransmission of the remote FIN. Acknowledge
+ * it and restart the finack timer.
+ */
+ case TCPS_TIME_WAIT:
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ LogFlowFunc(("%d -> dropafterack\n", __LINE__));
+ goto dropafterack;
+ }
+ } /* switch(tp->t_state) */
+
+step6:
+ LogFlowFunc(("step6:\n"));
+ /*
+ * Update window information.
+ * Don't look at window if no ACK: TAC's send garbage on first SYN.
+ */
+ if ( (tiflags & TH_ACK)
+ && ( SEQ_LT(tp->snd_wl1, ti->ti_seq)
+ || ( tp->snd_wl1 == ti->ti_seq
+ && ( SEQ_LT(tp->snd_wl2, ti->ti_ack)
+ || ( tp->snd_wl2 == ti->ti_ack
+ && tiwin > tp->snd_wnd)))))
+ {
+ /* keep track of pure window updates */
+ if ( ti->ti_len == 0
+ && tp->snd_wl2 == ti->ti_ack
+ && tiwin > tp->snd_wnd)
+ tcpstat.tcps_rcvwinupd++;
+ tp->snd_wnd = tiwin;
+ tp->snd_wl1 = ti->ti_seq;
+ tp->snd_wl2 = ti->ti_ack;
+ if (tp->snd_wnd > tp->max_sndwnd)
+ tp->max_sndwnd = tp->snd_wnd;
+ needoutput = 1;
+ }
+
+ /*
+ * Process segments with URG.
+ */
+ if ((tiflags & TH_URG) && ti->ti_urp &&
+ TCPS_HAVERCVDFIN(tp->t_state) == 0)
+ {
+ /*
+ * This is a kludge, but if we receive and accept
+ * random urgent pointers, we'll crash in
+ * soreceive. It's hard to imagine someone
+ * actually wanting to send this much urgent data.
+ */
+ if (ti->ti_urp + so->so_rcv.sb_cc > so->so_rcv.sb_datalen)
+ {
+ ti->ti_urp = 0;
+ tiflags &= ~TH_URG;
+ LogFlowFunc(("%d -> dodata\n", __LINE__));
+ goto dodata;
+ }
+
+ /*
+ * If this segment advances the known urgent pointer,
+ * then mark the data stream. This should not happen
+ * in CLOSE_WAIT, CLOSING, LAST_ACK or TIME_WAIT STATES since
+ * a FIN has been received from the remote side.
+ * In these states we ignore the URG.
+ *
+ * According to RFC961 (Assigned Protocols),
+ * the urgent pointer points to the last octet
+ * of urgent data. We continue, however,
+ * to consider it to indicate the first octet
+ * of data past the urgent section as the original
+ * spec states (in one of two places).
+ */
+ if (SEQ_GT(ti->ti_seq+ti->ti_urp, tp->rcv_up))
+ {
+ tp->rcv_up = ti->ti_seq + ti->ti_urp;
+ so->so_urgc = SBUF_LEN(&so->so_rcv) +
+ (tp->rcv_up - tp->rcv_nxt); /* -1; */
+ tp->rcv_up = ti->ti_seq + ti->ti_urp;
+ }
+ }
+ else
+ /*
+ * If no out of band data is expected,
+ * pull receive urgent pointer along
+ * with the receive window.
+ */
+ if (SEQ_GT(tp->rcv_nxt, tp->rcv_up))
+ tp->rcv_up = tp->rcv_nxt;
+dodata:
+ LogFlowFunc(("dodata:\n"));
+
+ /*
+ * If this is a small packet, then ACK now - with Nagel
+ * congestion avoidance sender won't send more until
+ * he gets an ACK.
+ *
+ * XXX: In case you wonder... The magic "27" below is ESC that
+ * presumably starts a terminal escape-sequence and that we want
+ * to ACK ASAP. [Original slirp code had three different
+ * heuristics to chose from here and in the header prediction case
+ * above, but the commented out alternatives were lost and the
+ * header prediction case that had an expanded comment about this
+ * has been modified to always send an ACK].
+ */
+ if ( ti->ti_len
+ && (unsigned)ti->ti_len <= 5
+ && ((struct tcpiphdr_2 *)ti)->first_char == (char)27)
+ {
+ tp->t_flags |= TF_ACKNOW;
+ }
+
+ /*
+ * Process the segment text, merging it into the TCP sequencing queue,
+ * and arranging for acknowledgment of receipt if necessary.
+ * This process logically involves adjusting tp->rcv_wnd as data
+ * is presented to the user (this happens in tcp_usrreq.c,
+ * case PRU_RCVD). If a FIN has already been received on this
+ * connection then we just ignore the text.
+ */
+ if ( (ti->ti_len || (tiflags&TH_FIN))
+ && TCPS_HAVERCVDFIN(tp->t_state) == 0)
+ {
+ if ( ti->ti_seq == tp->rcv_nxt
+ && LIST_EMPTY(&tp->t_segq)
+ && tp->t_state == TCPS_ESTABLISHED)
+ {
+ DELAY_ACK(tp, ti); /* little bit different from BSD declaration see netinet/tcp_input.c */
+ tp->rcv_nxt += tlen;
+ tiflags = ti->ti_t.th_flags & TH_FIN;
+ tcpstat.tcps_rcvpack++;
+ tcpstat.tcps_rcvbyte += tlen;
+ if (so->so_state & SS_FCANTRCVMORE)
+ m_freem(pData, m);
+ else
+ sbappend(pData, so, m);
+ }
+ else
+ {
+ tiflags = tcp_reass(pData, tp, &ti->ti_t, &tlen, m);
+ tp->t_flags |= TF_ACKNOW;
+ }
+ /*
+ * Note the amount of data that peer has sent into
+ * our window, in order to estimate the sender's
+ * buffer size.
+ */
+ len = SBUF_SIZE(&so->so_rcv) - (tp->rcv_adv - tp->rcv_nxt);
+ }
+ else
+ {
+ m_freem(pData, m);
+ tiflags &= ~TH_FIN;
+ }
+
+ /*
+ * If FIN is received ACK the FIN and let the user know
+ * that the connection is closing.
+ */
+ if (tiflags & TH_FIN)
+ {
+ if (TCPS_HAVERCVDFIN(tp->t_state) == 0)
+ {
+ /*
+ * If we receive a FIN we can't send more data,
+ * set it SS_FDRAIN
+ * Shutdown the socket if there is no rx data in the
+ * buffer.
+ * soread() is called on completion of shutdown() and
+ * will got to TCPS_LAST_ACK, and use tcp_output()
+ * to send the FIN.
+ */
+/* sofcantrcvmore(so); */
+ sofwdrain(so);
+
+ tp->t_flags |= TF_ACKNOW;
+ tp->rcv_nxt++;
+ }
+ switch (tp->t_state)
+ {
+ /*
+ * In SYN_RECEIVED and ESTABLISHED STATES
+ * enter the CLOSE_WAIT state.
+ */
+ case TCPS_SYN_RECEIVED:
+ case TCPS_ESTABLISHED:
+ TCP_STATE_SWITCH_TO(tp, TCPS_CLOSE_WAIT);
+ break;
+
+ /*
+ * If still in FIN_WAIT_1 STATE FIN has not been acked so
+ * enter the CLOSING state.
+ */
+ case TCPS_FIN_WAIT_1:
+ TCP_STATE_SWITCH_TO(tp, TCPS_CLOSING);
+ break;
+
+ /*
+ * In FIN_WAIT_2 state enter the TIME_WAIT state,
+ * starting the time-wait timer, turning off the other
+ * standard timers.
+ */
+ case TCPS_FIN_WAIT_2:
+ TCP_STATE_SWITCH_TO(tp, TCPS_TIME_WAIT);
+ tcp_canceltimers(tp);
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ soisfdisconnected(so);
+ break;
+
+ /*
+ * In TIME_WAIT state restart the 2 MSL time_wait timer.
+ */
+ case TCPS_TIME_WAIT:
+ tp->t_timer[TCPT_2MSL] = 2 * TCPTV_MSL;
+ break;
+ }
+ }
+
+ /*
+ * Return any desired output.
+ */
+ if (needoutput || (tp->t_flags & TF_ACKNOW))
+ tcp_output(pData, tp);
+
+ SOCKET_UNLOCK(so);
+ STAM_PROFILE_STOP(&pData->StatTCP_input, counter_input);
+ LogFlowFuncLeave();
+ return;
+
+dropafterack:
+ LogFlowFunc(("dropafterack:\n"));
+ /*
+ * Generate an ACK dropping incoming segment if it occupies
+ * sequence space, where the ACK reflects our state.
+ */
+ if (tiflags & TH_RST)
+ {
+ LogFlowFunc(("%d -> drop\n", __LINE__));
+ goto drop;
+ }
+ m_freem(pData, m);
+ tp->t_flags |= TF_ACKNOW;
+ (void) tcp_output(pData, tp);
+ SOCKET_UNLOCK(so);
+ STAM_PROFILE_STOP(&pData->StatTCP_input, counter_input);
+ LogFlowFuncLeave();
+ return;
+
+dropwithreset:
+ LogFlowFunc(("dropwithreset:\n"));
+ /* reuses m if m!=NULL, m_free() unnecessary */
+ if (tiflags & TH_ACK)
+ tcp_respond(pData, tp, ti, m, (tcp_seq)0, ti->ti_ack, TH_RST);
+ else
+ {
+ if (tiflags & TH_SYN)
+ ti->ti_len++;
+ tcp_respond(pData, tp, ti, m, ti->ti_seq+ti->ti_len, (tcp_seq)0,
+ TH_RST|TH_ACK);
+ }
+
+ if (so != &tcb)
+ SOCKET_UNLOCK(so);
+ STAM_PROFILE_STOP(&pData->StatTCP_input, counter_input);
+ LogFlowFuncLeave();
+ return;
+
+drop:
+ LogFlowFunc(("drop:\n"));
+ /*
+ * Drop space held by incoming segment and return.
+ */
+ m_freem(pData, m);
+
+#ifdef VBOX_WITH_SLIRP_MT
+ if (RTCritSectIsOwned(&so->so_mutex))
+ {
+ SOCKET_UNLOCK(so);
+ }
+#endif
+
+ STAM_PROFILE_STOP(&pData->StatTCP_input, counter_input);
+ LogFlowFuncLeave();
+ return;
+}
+
+
+void
+tcp_fconnect_failed(PNATState pData, struct socket *so, int sockerr)
+{
+ struct tcpcb *tp;
+ int code;
+
+ Log2(("NAT: connect error %d %R[natsock]\n", sockerr, so));
+
+ Assert(so->so_state & SS_ISFCONNECTING);
+ so->so_state = SS_NOFDREF;
+
+ if (sockerr == ECONNREFUSED || sockerr == ECONNRESET)
+ {
+ /* hand off to tcp_input():cont_conn to send RST */
+ TCP_INPUT(pData, NULL, 0, so);
+ return;
+ }
+
+ tp = sototcpcb(so);
+ if (RT_UNLIKELY(tp == NULL)) /* should never happen */
+ {
+ LogRel(("NAT: tp == NULL %R[natsock]\n", so));
+ sofree(pData, so);
+ return;
+ }
+
+ if (sockerr == ENETUNREACH || sockerr == ENETDOWN)
+ code = ICMP_UNREACH_NET;
+ else if (sockerr == EHOSTUNREACH || sockerr == EHOSTDOWN)
+ code = ICMP_UNREACH_HOST;
+ else
+ code = -1;
+
+ if (code >= 0)
+ {
+ struct ip *oip;
+ unsigned ohdrlen;
+ struct mbuf *m;
+
+ if (RT_UNLIKELY(so->so_ohdr == NULL))
+ goto out;
+
+ oip = (struct ip *)so->so_ohdr;
+ ohdrlen = oip->ip_hl * 4 + 8;
+
+ m = m_gethdr(pData, M_NOWAIT, MT_HEADER);
+ if (RT_UNLIKELY(m == NULL))
+ goto out;
+
+ m_copyback(pData, m, 0, ohdrlen, (caddr_t)so->so_ohdr);
+ m->m_pkthdr.header = mtod(m, void *);
+
+ icmp_error(pData, m, ICMP_UNREACH, code, 0, NULL);
+ }
+
+ out:
+ tcp_close(pData, tp);
+}
+
+
+void
+tcp_dooptions(PNATState pData, struct tcpcb *tp, u_char *cp, int cnt, struct tcpiphdr *ti)
+{
+ u_int16_t mss;
+ int opt, optlen;
+
+ LogFlowFunc(("tcp_dooptions: tp = %R[tcpcb793], cnt=%i\n", tp, cnt));
+
+ for (; cnt > 0; cnt -= optlen, cp += optlen)
+ {
+ opt = cp[0];
+ if (opt == TCPOPT_EOL)
+ break;
+ if (opt == TCPOPT_NOP)
+ optlen = 1;
+ else
+ {
+ optlen = cp[1];
+ if (optlen <= 0)
+ break;
+ }
+ switch (opt)
+ {
+ default:
+ continue;
+
+ case TCPOPT_MAXSEG:
+ if (optlen != TCPOLEN_MAXSEG)
+ continue;
+ if (!(ti->ti_flags & TH_SYN))
+ continue;
+ memcpy((char *) &mss, (char *) cp + 2, sizeof(mss));
+ NTOHS(mss);
+ (void) tcp_mss(pData, tp, mss); /* sets t_maxseg */
+ break;
+
+#if 0
+ case TCPOPT_WINDOW:
+ if (optlen != TCPOLEN_WINDOW)
+ continue;
+ if (!(ti->ti_flags & TH_SYN))
+ continue;
+ tp->t_flags |= TF_RCVD_SCALE;
+ tp->requested_s_scale = min(cp[2], TCP_MAX_WINSHIFT);
+ break;
+
+ case TCPOPT_TIMESTAMP:
+ if (optlen != TCPOLEN_TIMESTAMP)
+ continue;
+ *ts_present = 1;
+ memcpy((char *) ts_val, (char *)cp + 2, sizeof(*ts_val));
+ NTOHL(*ts_val);
+ memcpy((char *) ts_ecr, (char *)cp + 6, sizeof(*ts_ecr));
+ NTOHL(*ts_ecr);
+
+ /*
+ * A timestamp received in a SYN makes
+ * it ok to send timestamp requests and replies.
+ */
+ if (ti->ti_flags & TH_SYN)
+ {
+ tp->t_flags |= TF_RCVD_TSTMP;
+ tp->ts_recent = *ts_val;
+ tp->ts_recent_age = tcp_now;
+ }
+ break;
+#endif
+ }
+ }
+}
+
+
+/*
+ * Pull out of band byte out of a segment so
+ * it doesn't appear in the user's data queue.
+ * It is still reflected in the segment length for
+ * sequencing purposes.
+ */
+
+#if 0
+void
+tcp_pulloutofband(struct socket *so, struct tcpiphdr *ti, struct mbuf *m)
+{
+ int cnt = ti->ti_urp - 1;
+
+ while (cnt >= 0)
+ {
+ if (m->m_len > cnt)
+ {
+ char *cp = mtod(m, caddr_t) + cnt;
+ struct tcpcb *tp = sototcpcb(so);
+
+ tp->t_iobc = *cp;
+ tp->t_oobflags |= TCPOOB_HAVEDATA;
+ memcpy(sp, cp+1, (unsigned)(m->m_len - cnt - 1));
+ m->m_len--;
+ return;
+ }
+ cnt -= m->m_len;
+ m = m->m_next; /* XXX WRONG! Fix it! */
+ if (m == 0)
+ break;
+ }
+ panic("tcp_pulloutofband");
+}
+#endif
+
+/*
+ * Collect new round-trip time estimate
+ * and update averages and current timeout.
+ */
+
+void
+tcp_xmit_timer(PNATState pData, register struct tcpcb *tp, int rtt)
+{
+ register short delta;
+
+ LogFlowFunc(("ENTER: tcp_xmit_timer: tp = %R[tcpcb793] rtt = %d\n", tp, rtt));
+
+ tcpstat.tcps_rttupdated++;
+ if (tp->t_srtt != 0)
+ {
+ /*
+ * srtt is stored as fixed point with 3 bits after the
+ * binary point (i.e., scaled by 8). The following magic
+ * is equivalent to the smoothing algorithm in rfc793 with
+ * an alpha of .875 (srtt = rtt/8 + srtt*7/8 in fixed
+ * point). Adjust rtt to origin 0.
+ */
+ delta = rtt - 1 - (tp->t_srtt >> TCP_RTT_SHIFT);
+ if ((tp->t_srtt += delta) <= 0)
+ tp->t_srtt = 1;
+ /*
+ * We accumulate a smoothed rtt variance (actually, a
+ * smoothed mean difference), then set the retransmit
+ * timer to smoothed rtt + 4 times the smoothed variance.
+ * rttvar is stored as fixed point with 2 bits after the
+ * binary point (scaled by 4). The following is
+ * equivalent to rfc793 smoothing with an alpha of .75
+ * (rttvar = rttvar*3/4 + |delta| / 4). This replaces
+ * rfc793's wired-in beta.
+ */
+ if (delta < 0)
+ delta = -delta;
+ delta -= (tp->t_rttvar >> TCP_RTTVAR_SHIFT);
+ if ((tp->t_rttvar += delta) <= 0)
+ tp->t_rttvar = 1;
+ }
+ else
+ {
+ /*
+ * No rtt measurement yet - use the unsmoothed rtt.
+ * Set the variance to half the rtt (so our first
+ * retransmit happens at 3*rtt).
+ */
+ tp->t_srtt = rtt << TCP_RTT_SHIFT;
+ tp->t_rttvar = rtt << (TCP_RTTVAR_SHIFT - 1);
+ }
+ tp->t_rtt = 0;
+ tp->t_rxtshift = 0;
+
+ /*
+ * the retransmit should happen at rtt + 4 * rttvar.
+ * Because of the way we do the smoothing, srtt and rttvar
+ * will each average +1/2 tick of bias. When we compute
+ * the retransmit timer, we want 1/2 tick of rounding and
+ * 1 extra tick because of +-1/2 tick uncertainty in the
+ * firing of the timer. The bias will give us exactly the
+ * 1.5 tick we need. But, because the bias is
+ * statistical, we have to test that we don't drop below
+ * the minimum feasible timer (which is 2 ticks).
+ */
+ TCPT_RANGESET(tp->t_rxtcur, TCP_REXMTVAL(tp),
+ (short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */
+
+ /*
+ * We received an ack for a packet that wasn't retransmitted;
+ * it is probably safe to discard any error indications we've
+ * received recently. This isn't quite right, but close enough
+ * for now (a route might have failed after we sent a segment,
+ * and the return path might not be symmetrical).
+ */
+ tp->t_softerror = 0;
+}
+
+/*
+ * Determine a reasonable value for maxseg size.
+ * If the route is known, check route for mtu.
+ * If none, use an mss that can be handled on the outgoing
+ * interface without forcing IP to fragment; if bigger than
+ * an mbuf cluster (MCLBYTES), round down to nearest multiple of MCLBYTES
+ * to utilize large mbufs. If no route is found, route has no mtu,
+ * or the destination isn't local, use a default, hopefully conservative
+ * size (usually 512 or the default IP max size, but no more than the mtu
+ * of the interface), as we can't discover anything about intervening
+ * gateways or networks. We also initialize the congestion/slow start
+ * window to be a single segment if the destination isn't local.
+ * While looking at the routing entry, we also initialize other path-dependent
+ * parameters from pre-set or cached values in the routing entry.
+ */
+
+int
+tcp_mss(PNATState pData, register struct tcpcb *tp, u_int offer)
+{
+ struct socket *so = tp->t_socket;
+ int mss;
+
+ LogFlowFunc(("ENTER: tcp_mss: offer=%u, t_maxseg=%u; tp=%R[natsock]\n",
+ offer, (unsigned int)tp->t_maxseg, so));
+
+ mss = min(if_mtu, if_mru) - sizeof(struct tcpiphdr);
+ if (offer)
+ mss = min(mss, offer);
+ mss = max(mss, 32);
+ if (mss < tp->t_maxseg || offer != 0)
+ tp->t_maxseg = mss;
+
+ tp->snd_cwnd = mss;
+
+ sbreserve(pData, &so->so_snd, tcp_sndspace+((tcp_sndspace%mss)?(mss-(tcp_sndspace%mss)):0));
+ sbreserve(pData, &so->so_rcv, tcp_rcvspace+((tcp_rcvspace%mss)?(mss-(tcp_rcvspace%mss)):0));
+
+ LogFlowFunc(("LEAVE: mss=%d\n", mss));
+ return mss;
+}
diff --git a/src/VBox/Devices/Network/slirp/tcp_output.c b/src/VBox/Devices/Network/slirp/tcp_output.c
new file mode 100644
index 00000000..9e61e77b
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/tcp_output.c
@@ -0,0 +1,739 @@
+/* $Id: tcp_output.c $ */
+/** @file
+ * NAT - TCP output.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)tcp_output.c 8.3 (Berkeley) 12/30/93
+ * tcp_output.c,v 1.3 1994/09/15 10:36:55 davidg Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+/*
+ * Since this is only used in "stats socket", we give meaning
+ * names instead of the REAL names
+ */
+const char * const tcpstates[] =
+{
+/* "CLOSED", "LISTEN", "SYN_SENT", "SYN_RCVD", */
+ "REDIRECT", "LISTEN", "SYN_SENT", "SYN_RCVD",
+ "ESTABLISHED", "CLOSE_WAIT", "FIN_WAIT_1", "CLOSING",
+ "LAST_ACK", "FIN_WAIT_2", "TIME_WAIT",
+};
+
+static const u_char tcp_outflags[TCP_NSTATES] =
+{
+ TH_RST|TH_ACK, 0, TH_SYN, TH_SYN|TH_ACK,
+ TH_ACK, TH_ACK, TH_FIN|TH_ACK, TH_FIN|TH_ACK,
+ TH_FIN|TH_ACK, TH_ACK, TH_ACK,
+};
+
+
+#define MAX_TCPOPTLEN 32 /* max # bytes that go in options */
+
+/*
+ * Tcp output routine: figure out what should be sent and send it.
+ */
+int
+tcp_output(PNATState pData, register struct tcpcb *tp)
+{
+ register struct socket *so = tp->t_socket;
+ register long len, win;
+ int off, flags, error;
+ register struct mbuf *m = NULL;
+ register struct tcpiphdr *ti;
+ u_char opt[MAX_TCPOPTLEN];
+ unsigned optlen, hdrlen;
+ int idle, sendalot;
+ int size = 0;
+
+ LogFlowFunc(("ENTER: tcp_output: tp = %R[tcpcb793]\n", tp));
+
+ /*
+ * Determine length of data that should be transmitted,
+ * and flags that will be used.
+ * If there is some data or critical controls (SYN, RST)
+ * to send, then transmit; otherwise, investigate further.
+ */
+ idle = (tp->snd_max == tp->snd_una);
+ if (idle && tp->t_idle >= tp->t_rxtcur)
+ /*
+ * We have been idle for "a while" and no acks are
+ * expected to clock out any data we send --
+ * slow start to get ack "clock" running again.
+ */
+ tp->snd_cwnd = tp->t_maxseg;
+
+again:
+ sendalot = 0;
+ off = tp->snd_nxt - tp->snd_una;
+ win = min(tp->snd_wnd, tp->snd_cwnd);
+
+ flags = tcp_outflags[tp->t_state];
+
+ Log2((" --- tcp_output flags = 0x%x\n", flags));
+
+ /*
+ * If in persist timeout with window of 0, send 1 byte.
+ * Otherwise, if window is small but nonzero
+ * and timer expired, we will send what we can
+ * and go to transmit state.
+ */
+ if (tp->t_force)
+ {
+ if (win == 0)
+ {
+ /*
+ * If we still have some data to send, then
+ * clear the FIN bit. Usually this would
+ * happen below when it realizes that we
+ * aren't sending all the data. However,
+ * if we have exactly 1 byte of unset data,
+ * then it won't clear the FIN bit below,
+ * and if we are in persist state, we wind
+ * up sending the packet without recording
+ * that we sent the FIN bit.
+ *
+ * We can't just blindly clear the FIN bit,
+ * because if we don't have any more data
+ * to send then the probe will be the FIN
+ * itself.
+ */
+ if (off < SBUF_LEN(&so->so_snd))
+ flags &= ~TH_FIN;
+ win = 1;
+ }
+ else
+ {
+ tp->t_timer[TCPT_PERSIST] = 0;
+ tp->t_rxtshift = 0;
+ }
+ }
+
+ len = min(SBUF_LEN(&so->so_snd), win) - off;
+ if (len < 0)
+ {
+ /*
+ * If FIN has been sent but not acked,
+ * but we haven't been called to retransmit,
+ * len will be -1. Otherwise, window shrank
+ * after we sent into it. If window shrank to 0,
+ * cancel pending retransmit and pull snd_nxt
+ * back to (closed) window. We will enter persist
+ * state below. If the window didn't close completely,
+ * just wait for an ACK.
+ */
+ len = 0;
+ if (win == 0)
+ {
+ tp->t_timer[TCPT_REXMT] = 0;
+ tp->snd_nxt = tp->snd_una;
+ }
+ }
+ if (len > tp->t_maxseg)
+ {
+ len = tp->t_maxseg;
+ sendalot = 1;
+ }
+ if (SEQ_LT(tp->snd_nxt + len, tp->snd_una + SBUF_LEN(&so->so_snd)))
+ flags &= ~TH_FIN;
+
+ win = sbspace(&so->so_rcv);
+
+ /*
+ * Sender silly window avoidance. If connection is idle
+ * and can send all data, a maximum segment,
+ * at least a maximum default-size segment do it,
+ * or are forced, do it; otherwise don't bother.
+ * If peer's buffer is tiny, then send
+ * when window is at least half open.
+ * If retransmitting (possibly after persist timer forced us
+ * to send into a small window), then must resend.
+ */
+ if (len)
+ {
+ if (len == tp->t_maxseg)
+ goto send;
+ if ((1 || idle || tp->t_flags & TF_NODELAY) &&
+ len + off >= SBUF_LEN(&so->so_snd))
+ goto send;
+ if (tp->t_force)
+ goto send;
+ if (len >= tp->max_sndwnd / 2 && tp->max_sndwnd > 0)
+ goto send;
+ if (SEQ_LT(tp->snd_nxt, tp->snd_max))
+ goto send;
+ }
+
+ /*
+ * Compare available window to amount of window
+ * known to peer (as advertised window less
+ * next expected input). If the difference is at least two
+ * max size segments, or at least 50% of the maximum possible
+ * window, then want to send a window update to peer.
+ */
+ if (win > 0)
+ {
+ /*
+ * "adv" is the amount we can increase the window,
+ * taking into account that we are limited by
+ * TCP_MAXWIN << tp->rcv_scale.
+ */
+ long adv = min(win, (long)TCP_MAXWIN << tp->rcv_scale);
+ if (SEQ_GT(tp->rcv_adv, tp->rcv_nxt))
+ adv -= tp->rcv_adv - tp->rcv_nxt;
+
+ if (adv >= (long) (2 * tp->t_maxseg))
+ goto send;
+ if (2 * adv >= (long) SBUF_SIZE(&so->so_rcv))
+ goto send;
+ }
+
+ /*
+ * Send if we owe peer an ACK.
+ */
+ if (tp->t_flags & TF_ACKNOW)
+ goto send;
+ if (flags & (TH_SYN|TH_RST))
+ goto send;
+ if (SEQ_GT(tp->snd_up, tp->snd_una))
+ goto send;
+ /*
+ * If our state indicates that FIN should be sent
+ * and we have not yet done so, or we're retransmitting the FIN,
+ * then we need to send.
+ */
+ if ( flags & TH_FIN
+ && ((tp->t_flags & TF_SENTFIN) == 0 || tp->snd_nxt == tp->snd_una))
+ goto send;
+
+ /*
+ * TCP window updates are not reliable, rather a polling protocol
+ * using ``persist'' packets is used to insure receipt of window
+ * updates. The three ``states'' for the output side are:
+ * idle not doing retransmits or persists
+ * persisting to move a small or zero window
+ * (re)transmitting and thereby not persisting
+ *
+ * tp->t_timer[TCPT_PERSIST]
+ * is set when we are in persist state.
+ * tp->t_force
+ * is set when we are called to send a persist packet.
+ * tp->t_timer[TCPT_REXMT]
+ * is set when we are retransmitting
+ * The output side is idle when both timers are zero.
+ *
+ * If send window is too small, there is data to transmit, and no
+ * retransmit or persist is pending, then go to persist state.
+ * If nothing happens soon, send when timer expires:
+ * if window is nonzero, transmit what we can,
+ * otherwise force out a byte.
+ */
+ if ( SBUF_LEN(&so->so_snd)
+ && tp->t_timer[TCPT_REXMT] == 0
+ && tp->t_timer[TCPT_PERSIST] == 0)
+ {
+ tp->t_rxtshift = 0;
+ tcp_setpersist(tp);
+ }
+
+ /*
+ * No reason to send a segment, just return.
+ */
+ tcpstat.tcps_didnuttin++;
+
+ LogFlowFuncLeave();
+ return (0);
+
+send:
+ LogFlowFunc(("send\n"));
+ /*
+ * Before ESTABLISHED, force sending of initial options
+ * unless TCP set not to do any options.
+ * NOTE: we assume that the IP/TCP header plus TCP options
+ * always fit in a single mbuf, leaving room for a maximum
+ * link header, i.e.
+ * max_linkhdr + sizeof (struct tcpiphdr) + optlen <= MHLEN
+ */
+ optlen = 0;
+ hdrlen = sizeof (struct tcpiphdr);
+ if (flags & TH_SYN)
+ {
+ tp->snd_nxt = tp->iss;
+ if ((tp->t_flags & TF_NOOPT) == 0)
+ {
+ u_int16_t mss;
+
+ opt[0] = TCPOPT_MAXSEG;
+ opt[1] = 4;
+ mss = RT_H2N_U16((u_int16_t) tcp_mss(pData, tp, 0));
+ memcpy((caddr_t)(opt + 2), (caddr_t)&mss, sizeof(mss));
+ optlen = 4;
+
+#if 0
+ if ( (tp->t_flags & TF_REQ_SCALE)
+ && ( (flags & TH_ACK) == 0
+ || (tp->t_flags & TF_RCVD_SCALE)))
+ {
+ *((u_int32_t *) (opt + optlen)) = RT_H2N_U32( TCPOPT_NOP << 24
+ | TCPOPT_WINDOW << 16
+ | TCPOLEN_WINDOW << 8
+ | tp->request_r_scale);
+ optlen += 4;
+ }
+#endif
+ }
+ }
+
+ /*
+ * Send a timestamp and echo-reply if this is a SYN and our side
+ * wants to use timestamps (TF_REQ_TSTMP is set) or both our side
+ * and our peer have sent timestamps in our SYN's.
+ */
+#if 0
+ if ( (tp->t_flags & (TF_REQ_TSTMP|TF_NOOPT)) == TF_REQ_TSTMP
+ && (flags & TH_RST) == 0
+ && ( (flags & (TH_SYN|TH_ACK)) == TH_SYN
+ || (tp->t_flags & TF_RCVD_TSTMP)))
+ {
+ u_int32_t *lp = (u_int32_t *)(opt + optlen);
+
+ /* Form timestamp option as shown in appendix A of RFC 1323. */
+ *lp++ = RT_H2N_U32_C(TCPOPT_TSTAMP_HDR);
+ *lp++ = RT_H2N_U32(tcp_now);
+ *lp = RT_H2N_U32(tp->ts_recent);
+ optlen += TCPOLEN_TSTAMP_APPA;
+ }
+#endif
+ hdrlen += optlen;
+
+ /*
+ * Adjust data length if insertion of options will
+ * bump the packet length beyond the t_maxseg length.
+ */
+ if (len > tp->t_maxseg - optlen)
+ {
+ len = tp->t_maxseg - optlen;
+ sendalot = 1;
+ }
+
+ /*
+ * Grab a header mbuf, attaching a copy of data to
+ * be transmitted, and initialize the header from
+ * the template for sends on this connection.
+ */
+ if (len)
+ {
+ if (tp->t_force && len == 1)
+ tcpstat.tcps_sndprobe++;
+ else if (SEQ_LT(tp->snd_nxt, tp->snd_max))
+ {
+ tcpstat.tcps_sndrexmitpack++;
+ tcpstat.tcps_sndrexmitbyte += len;
+ }
+ else
+ {
+ tcpstat.tcps_sndpack++;
+ tcpstat.tcps_sndbyte += len;
+ }
+
+ size = MCLBYTES;
+ if ((len + hdrlen + ETH_HLEN) < MSIZE)
+ size = MCLBYTES;
+ else if ((len + hdrlen + ETH_HLEN) < MCLBYTES)
+ size = MCLBYTES;
+ else if((len + hdrlen + ETH_HLEN) < MJUM9BYTES)
+ size = MJUM9BYTES;
+ else if ((len + hdrlen + ETH_HLEN) < MJUM16BYTES)
+ size = MJUM16BYTES;
+ else
+ AssertMsgFailed(("Unsupported size"));
+ m = m_getjcl(pData, M_NOWAIT, MT_HEADER, M_PKTHDR, size);
+ if (m == NULL)
+ {
+/* error = ENOBUFS; */
+ error = 1;
+ goto out;
+ }
+ m->m_data += if_maxlinkhdr;
+ m->m_pkthdr.header = mtod(m, void *);
+ m->m_len = hdrlen;
+
+ /*
+ * This will always succeed, since we make sure our mbufs
+ * are big enough to hold one MSS packet + header + ... etc.
+ */
+#if 0
+ if (len <= MHLEN - hdrlen - max_linkhdr)
+ {
+#endif
+ sbcopy(&so->so_snd, off, (int) len, mtod(m, caddr_t) + hdrlen);
+ m->m_len += len;
+#if 0
+ }
+ else
+ {
+ m->m_next = m_copy(so->so_snd.sb_mb, off, (int) len);
+ if (m->m_next == 0)
+ len = 0;
+ }
+#endif
+ /*
+ * If we're sending everything we've got, set PUSH.
+ * (This will keep happy those implementations which only
+ * give data to the user when a buffer fills or
+ * a PUSH comes in.)
+ */
+ if (off + len == (ssize_t)SBUF_LEN(&so->so_snd))
+ flags |= TH_PUSH;
+ }
+ else
+ {
+ bool fUninitializedTemplate = false;
+ if (tp->t_flags & TF_ACKNOW)
+ tcpstat.tcps_sndacks++;
+ else if (flags & (TH_SYN|TH_FIN|TH_RST))
+ tcpstat.tcps_sndctrl++;
+ else if (SEQ_GT(tp->snd_up, tp->snd_una))
+ tcpstat.tcps_sndurg++;
+ else
+ tcpstat.tcps_sndwinup++;
+
+ if ((hdrlen + ETH_HLEN) < MSIZE)
+ {
+ size = MCLBYTES;
+ }
+ else if ((hdrlen + ETH_HLEN) < MCLBYTES)
+ {
+ size = MCLBYTES;
+ }
+ else if((hdrlen + ETH_HLEN) < MJUM9BYTES)
+ {
+ size = MJUM9BYTES;
+ }
+ else if ((hdrlen + ETH_HLEN) < MJUM16BYTES)
+ {
+ size = MJUM16BYTES;
+ }
+ else
+ {
+ AssertMsgFailed(("Unsupported size"));
+ }
+ m = m_getjcl(pData, M_NOWAIT, MT_HEADER, M_PKTHDR, size);
+ if (m == NULL)
+ {
+/* error = ENOBUFS; */
+ error = 1;
+ goto out;
+ }
+ m->m_data += if_maxlinkhdr;
+ m->m_pkthdr.header = mtod(m, void *);
+ m->m_len = hdrlen;
+ /*
+ * Uninitialized TCP template looks very suspicious at this processing state, thus why we have
+ * to workaround the problem till right fix. Warning appears once at release log.
+ */
+ fUninitializedTemplate = RT_BOOL(( tp->t_template.ti_src.s_addr == INADDR_ANY
+ || tp->t_template.ti_dst.s_addr == INADDR_ANY));
+#ifndef DEBUG_vvl
+ if (fUninitializedTemplate)
+ {
+ static bool fWarn;
+ tcp_template(tp);
+ if(!fWarn)
+ {
+ LogRel(("NAT: TCP: TCP template was created forcely from socket information\n"));
+ fWarn = true;
+ }
+ }
+#else
+ Assert((!fUninitializedTemplate));
+#endif
+ }
+
+ ti = mtod(m, struct tcpiphdr *);
+
+ memcpy((caddr_t)ti, &tp->t_template, sizeof (struct tcpiphdr));
+
+ /*
+ * Fill in fields, remembering maximum advertised
+ * window for use in delaying messages about window sizes.
+ * If resending a FIN, be sure not to use a new sequence number.
+ */
+ if ( flags & TH_FIN
+ && tp->t_flags & TF_SENTFIN
+ && tp->snd_nxt == tp->snd_max)
+ tp->snd_nxt--;
+ /*
+ * If we are doing retransmissions, then snd_nxt will
+ * not reflect the first unsent octet. For ACK only
+ * packets, we do not want the sequence number of the
+ * retransmitted packet, we want the sequence number
+ * of the next unsent octet. So, if there is no data
+ * (and no SYN or FIN), use snd_max instead of snd_nxt
+ * when filling in ti_seq. But if we are in persist
+ * state, snd_max might reflect one byte beyond the
+ * right edge of the window, so use snd_nxt in that
+ * case, since we know we aren't doing a retransmission.
+ * (retransmit and persist are mutually exclusive...)
+ */
+ if (len || (flags & (TH_SYN|TH_FIN)) || tp->t_timer[TCPT_PERSIST])
+ ti->ti_seq = RT_H2N_U32(tp->snd_nxt);
+ else
+ ti->ti_seq = RT_H2N_U32(tp->snd_max);
+ ti->ti_ack = RT_H2N_U32(tp->rcv_nxt);
+ if (optlen)
+ {
+ memcpy((caddr_t)(ti + 1), (caddr_t)opt, optlen);
+ ti->ti_off = (uint8_t)((sizeof (struct tcphdr) + optlen) >> 2);
+ }
+ ti->ti_flags = flags;
+ /*
+ * Calculate receive window. Don't shrink window,
+ * but avoid silly window syndrome.
+ */
+ if (win < (long)(SBUF_SIZE(&so->so_rcv) / 4) && win < (long)tp->t_maxseg)
+ win = 0;
+ if (win > (long)TCP_MAXWIN << tp->rcv_scale)
+ win = (long)TCP_MAXWIN << tp->rcv_scale;
+ if (win < (long)(int32_t)(tp->rcv_adv - tp->rcv_nxt))
+ win = (long)(int32_t)(tp->rcv_adv - tp->rcv_nxt);
+ ti->ti_win = RT_H2N_U16((u_int16_t) (win>>tp->rcv_scale));
+
+#if 0
+ if (SEQ_GT(tp->snd_up, tp->snd_nxt))
+ {
+ ti->ti_urp = RT_H2N_U16((u_int16_t)(tp->snd_up - tp->snd_nxt));
+#else
+ if (SEQ_GT(tp->snd_up, tp->snd_una))
+ {
+ ti->ti_urp = RT_H2N_U16((u_int16_t)(tp->snd_up - RT_N2H_U32(ti->ti_seq)));
+#endif
+ ti->ti_flags |= TH_URG;
+ }
+ else
+ /*
+ * If no urgent pointer to send, then we pull
+ * the urgent pointer to the left edge of the send window
+ * so that it doesn't drift into the send window on sequence
+ * number wraparound.
+ */
+ tp->snd_up = tp->snd_una; /* drag it along */
+
+ /*
+ * Put TCP length in extended header, and then
+ * checksum extended header and data.
+ */
+ if (len + optlen)
+ ti->ti_len = RT_H2N_U16((u_int16_t)(sizeof (struct tcphdr)
+ + optlen + len));
+ ti->ti_sum = cksum(m, (int)(hdrlen + len));
+
+ /*
+ * In transmit state, time the transmission and arrange for
+ * the retransmit. In persist state, just set snd_max.
+ */
+ if (tp->t_force == 0 || tp->t_timer[TCPT_PERSIST] == 0)
+ {
+ tcp_seq startseq = tp->snd_nxt;
+
+ /*
+ * Advance snd_nxt over sequence space of this segment.
+ */
+ if (flags & (TH_SYN|TH_FIN))
+ {
+ if (flags & TH_SYN)
+ tp->snd_nxt++;
+ if (flags & TH_FIN)
+ {
+ tp->snd_nxt++;
+ tp->t_flags |= TF_SENTFIN;
+ }
+ }
+ tp->snd_nxt += len;
+ if (SEQ_GT(tp->snd_nxt, tp->snd_max))
+ {
+ tp->snd_max = tp->snd_nxt;
+ /*
+ * Time this transmission if not a retransmission and
+ * not currently timing anything.
+ */
+ if (tp->t_rtt == 0)
+ {
+ tp->t_rtt = 1;
+ tp->t_rtseq = startseq;
+ tcpstat.tcps_segstimed++;
+ }
+ }
+
+ /*
+ * Set retransmit timer if not currently set,
+ * and not doing an ack or a keep-alive probe.
+ * Initial value for retransmit timer is smoothed
+ * round-trip time + 2 * round-trip time variance.
+ * Initialize shift counter which is used for backoff
+ * of retransmit time.
+ */
+ if ( tp->t_timer[TCPT_REXMT] == 0
+ && tp->snd_nxt != tp->snd_una)
+ {
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ if (tp->t_timer[TCPT_PERSIST])
+ {
+ tp->t_timer[TCPT_PERSIST] = 0;
+ tp->t_rxtshift = 0;
+ }
+ }
+ }
+ else
+ if (SEQ_GT(tp->snd_nxt + len, tp->snd_max))
+ tp->snd_max = tp->snd_nxt + len;
+
+ /*
+ * Fill in IP length and desired time to live and
+ * send to IP level. There should be a better way
+ * to handle ttl and tos; we could keep them in
+ * the template, but need a way to checksum without them.
+ */
+ M_ASSERTPKTHDR(m);
+ m->m_pkthdr.header = mtod(m, void *);
+ m->m_len = hdrlen + len; /* XXX Needed? m_len should be correct */
+
+ {
+ ((struct ip *)ti)->ip_len = m->m_len;
+ ((struct ip *)ti)->ip_ttl = ip_defttl;
+ ((struct ip *)ti)->ip_tos = so->so_iptos;
+
+ /* #if BSD >= 43 */
+ /* Don't do IP options... */
+#if 0
+ error = ip_output(m, tp->t_inpcb->inp_options, &tp->t_inpcb->inp_route,
+ so->so_options & SO_DONTROUTE, 0);
+#endif
+ error = ip_output(pData, so, m);
+
+#if 0
+/* #else */
+ error = ip_output(m, (struct mbuf *)0, &tp->t_inpcb->inp_route,
+ so->so_options & SO_DONTROUTE);
+/* #endif */
+#endif
+ }
+ if (error)
+ {
+out:
+#if 0
+ if (error == ENOBUFS)
+ {
+ tcp_quench(tp->t_inpcb, 0);
+ return (0);
+ }
+
+ if ( ( error == EHOSTUNREACH
+ || error == ENETDOWN)
+ && TCPS_HAVERCVDSYN(tp->t_state))
+ {
+ tp->t_softerror = error;
+ return (0);
+ }
+#endif
+ if (m != NULL)
+ m_freem(pData, m);
+ return (error);
+ }
+ tcpstat.tcps_sndtotal++;
+
+ /*
+ * Data sent (as far as we can tell).
+ * If this advertises a larger window than any other segment,
+ * then remember the size of the advertised window.
+ * Any pending ACK has now been sent.
+ */
+ if (win > 0 && SEQ_GT(tp->rcv_nxt+win, tp->rcv_adv))
+ tp->rcv_adv = tp->rcv_nxt + win;
+ tp->last_ack_sent = tp->rcv_nxt;
+ tp->t_flags &= ~(TF_ACKNOW|TF_DELACK);
+ if (sendalot)
+ goto again;
+
+ return (0);
+}
+
+void
+tcp_setpersist(struct tcpcb *tp)
+{
+ int t = ((tp->t_srtt >> 2) + tp->t_rttvar) >> 1;
+
+#if 0
+ if (tp->t_timer[TCPT_REXMT])
+ panic("tcp_output REXMT");
+#endif
+ /*
+ * Start/restart persistence timer.
+ */
+ TCPT_RANGESET(tp->t_timer[TCPT_PERSIST],
+ t * tcp_backoff[tp->t_rxtshift],
+ TCPTV_PERSMIN, TCPTV_PERSMAX);
+ if (tp->t_rxtshift < TCP_MAXRXTSHIFT)
+ tp->t_rxtshift++;
+}
diff --git a/src/VBox/Devices/Network/slirp/tcp_subr.c b/src/VBox/Devices/Network/slirp/tcp_subr.c
new file mode 100644
index 00000000..c7820488
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/tcp_subr.c
@@ -0,0 +1,654 @@
+/* $Id: tcp_subr.c $ */
+/** @file
+ * NAT - TCP support.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)tcp_subr.c 8.1 (Berkeley) 6/10/93
+ * tcp_subr.c,v 1.5 1994/10/08 22:39:58 phk Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+
+
+/*
+ * Tcp initialization
+ */
+void
+tcp_init(PNATState pData)
+{
+ tcp_iss = 1; /* wrong */
+ tcb.so_next = tcb.so_prev = &tcb;
+ tcp_last_so = &tcb;
+ tcp_reass_maxqlen = 48;
+ tcp_reass_maxseg = 256;
+}
+
+/*
+ * Create template to be used to send tcp packets on a connection.
+ * Call after host entry created, fills
+ * in a skeletal tcp/ip header, minimizing the amount of work
+ * necessary when the connection is used.
+ */
+/* struct tcpiphdr * */
+void
+tcp_template(struct tcpcb *tp)
+{
+ struct socket *so = tp->t_socket;
+ register struct tcpiphdr *n = &tp->t_template;
+
+ memset(n->ti_x1, 0, 9);
+ n->ti_pr = IPPROTO_TCP;
+ n->ti_len = RT_H2N_U16(sizeof (struct tcpiphdr) - sizeof (struct ip));
+ n->ti_src = so->so_faddr;
+ n->ti_dst = so->so_laddr;
+ n->ti_sport = so->so_fport;
+ n->ti_dport = so->so_lport;
+
+ n->ti_seq = 0;
+ n->ti_ack = 0;
+ n->ti_x2 = 0;
+ n->ti_off = 5;
+ n->ti_flags = 0;
+ n->ti_win = 0;
+ n->ti_sum = 0;
+ n->ti_urp = 0;
+}
+
+/*
+ * Send a single message to the TCP at address specified by
+ * the given TCP/IP header. If m == 0, then we make a copy
+ * of the tcpiphdr at ti and send directly to the addressed host.
+ * This is used to force keep alive messages out using the TCP
+ * template for a connection tp->t_template. If flags are given
+ * then we send a message back to the TCP which originated the
+ * segment ti, and discard the mbuf containing it and any other
+ * attached mbufs.
+ *
+ * In any case the ack and sequence number of the transmitted
+ * segment are as specified by the parameters.
+ */
+void
+tcp_respond(PNATState pData, struct tcpcb *tp, struct tcpiphdr *ti, struct mbuf *m, tcp_seq ack, tcp_seq seq, int flags)
+{
+ register int tlen;
+
+ LogFlowFunc(("ENTER: tp = %R[tcpcb793], ti = %p, m = %p, ack = %u, seq = %u, flags = %x\n", tp, ti, m, ack, seq, flags));
+
+ if (m == 0)
+ {
+ if ((m = m_gethdr(pData, M_DONTWAIT, MT_HEADER)) == NULL)
+ return;
+#ifdef TCP_COMPAT_42
+ tlen = 1;
+#else
+ tlen = 0;
+#endif
+ m->m_data += if_maxlinkhdr;
+ m->m_pkthdr.header = mtod(m, void *);
+ *mtod(m, struct tcpiphdr *) = *ti;
+ ti = mtod(m, struct tcpiphdr *);
+ flags = TH_ACK;
+ }
+ else
+ {
+ /*
+ * ti points into m so the next line is just making
+ * the mbuf point to ti
+ */
+ m->m_data = (caddr_t)ti;
+
+ m->m_len = sizeof (struct tcpiphdr);
+ tlen = 0;
+#define xchg(a,b,type) { type t; t = a; a = b; b = t; }
+ xchg(ti->ti_dst.s_addr, ti->ti_src.s_addr, u_int32_t);
+ xchg(ti->ti_dport, ti->ti_sport, u_int16_t);
+#undef xchg
+ }
+ ti->ti_len = RT_H2N_U16((u_short)(sizeof (struct tcphdr) + tlen));
+ tlen += sizeof (struct tcpiphdr);
+ m->m_len = tlen;
+
+ memset(ti->ti_x1, 0, 9);
+ ti->ti_seq = RT_H2N_U32(seq);
+ ti->ti_ack = RT_H2N_U32(ack);
+ ti->ti_x2 = 0;
+ ti->ti_off = sizeof (struct tcphdr) >> 2;
+ ti->ti_flags = flags;
+ if (tp)
+ {
+ int win = sbspace(&tp->t_socket->so_rcv);
+ ti->ti_win = RT_H2N_U16((u_int16_t) (win >> tp->rcv_scale));
+ }
+ else
+ ti->ti_win = 0;
+ ti->ti_urp = 0;
+ ti->ti_sum = 0;
+ ti->ti_sum = cksum(m, tlen);
+ ((struct ip *)ti)->ip_len = tlen;
+
+ if(flags & TH_RST)
+ ((struct ip *)ti)->ip_ttl = MAXTTL;
+ else
+ ((struct ip *)ti)->ip_ttl = ip_defttl;
+
+ (void) ip_output(pData, (struct socket *)0, m);
+}
+
+/*
+ * Create a new TCP control block, making an
+ * empty reassembly queue and hooking it to the argument
+ * protocol control block.
+ */
+struct tcpcb *
+tcp_newtcpcb(PNATState pData, struct socket *so)
+{
+ register struct tcpcb *tp;
+
+ tp = (struct tcpcb *)RTMemAllocZ(sizeof(*tp));
+ if (tp == NULL)
+ return ((struct tcpcb *)0);
+
+ tp->t_maxseg = tcp_mssdflt;
+
+ tp->t_flags = tcp_do_rfc1323 ? (TF_REQ_SCALE|TF_REQ_TSTMP) : 0;
+ tp->t_socket = so;
+
+ /*
+ * Init srtt to TCPTV_SRTTBASE (0), so we can tell that we have no
+ * rtt estimate. Set rttvar so that srtt + 2 * rttvar gives
+ * reasonable initial retransmit time.
+ */
+ tp->t_srtt = TCPTV_SRTTBASE;
+ tp->t_rttvar = tcp_rttdflt * PR_SLOWHZ << 2;
+ tp->t_rttmin = TCPTV_MIN;
+
+ TCPT_RANGESET(tp->t_rxtcur,
+ ((TCPTV_SRTTBASE >> 2) + (TCPTV_SRTTDFLT << 2)) >> 1,
+ TCPTV_MIN, TCPTV_REXMTMAX);
+
+ tp->snd_cwnd = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+ tp->snd_ssthresh = TCP_MAXWIN << TCP_MAX_WINSHIFT;
+ TCP_STATE_SWITCH_TO(tp, TCPS_CLOSED);
+
+ so->so_tcpcb = tp;
+ so->so_type = IPPROTO_TCP;
+
+ return (tp);
+}
+
+/*
+ * Drop a TCP connection, reporting
+ * the specified error. If connection is synchronized,
+ * then send a RST to peer.
+ */
+struct tcpcb *tcp_drop(PNATState pData, struct tcpcb *tp, int err)
+{
+/* tcp_drop(tp, errno)
+ register struct tcpcb *tp;
+ int errno;
+{
+*/
+ int fUninitializedTemplate = 0;
+#ifndef LOG_ENABLED
+ NOREF(err);
+#endif
+ LogFlowFunc(("ENTER: tp = %R[tcpcb793], errno = %d\n", tp, err));
+ fUninitializedTemplate = RT_BOOL(( tp
+ && ( tp->t_template.ti_src.s_addr == INADDR_ANY
+ || tp->t_template.ti_dst.s_addr == INADDR_ANY)));
+
+ if ( TCPS_HAVERCVDSYN(tp->t_state)
+ && !fUninitializedTemplate)
+ {
+ TCP_STATE_SWITCH_TO(tp, TCPS_CLOSED);
+ (void) tcp_output(pData, tp);
+ tcpstat.tcps_drops++;
+ }
+ else
+ tcpstat.tcps_conndrops++;
+#if 0
+ if (errno == ETIMEDOUT && tp->t_softerror)
+ errno = tp->t_softerror;
+
+ so->so_error = errno;
+#endif
+ return (tcp_close(pData, tp));
+}
+
+/*
+ * Close a TCP control block:
+ * discard all space held by the tcp
+ * discard internet protocol block
+ * wake up any sleepers
+ */
+struct tcpcb *
+tcp_close(PNATState pData, register struct tcpcb *tp)
+{
+ struct socket *so = tp->t_socket;
+
+ struct tseg_qent *te = NULL;
+ LogFlowFunc(("ENTER: tp = %R[tcpcb793]\n", tp));
+ /*XXX: freeing the reassembly queue */
+ while (!LIST_EMPTY(&tp->t_segq))
+ {
+ te = LIST_FIRST(&tp->t_segq);
+ LIST_REMOVE(te, tqe_q);
+ m_freem(pData, te->tqe_m);
+ RTMemFree(te);
+ tcp_reass_qsize--;
+ }
+ RTMemFree(tp);
+ so->so_tcpcb = 0;
+ soisfdisconnected(so);
+ /* clobber input socket cache if we're closing the cached connection */
+ if (so == tcp_last_so)
+ tcp_last_so = &tcb;
+ if (so->s != -1)
+ closesocket(so->s);
+ /* Avoid double free if the socket is listening and therefore doesn't have
+ * any sbufs reserved. */
+ if (!(so->so_state & SS_FACCEPTCONN))
+ {
+ sbfree(&so->so_rcv);
+ sbfree(&so->so_snd);
+ }
+ sofree(pData, so);
+ SOCKET_UNLOCK(so);
+ tcpstat.tcps_closed++;
+ return ((struct tcpcb *)0);
+}
+
+void
+tcp_drain(void)
+{
+ /* XXX */
+}
+
+/*
+ * When a source quench is received, close congestion window
+ * to one segment. We will gradually open it again as we proceed.
+ */
+
+#if 0
+
+void
+tcp_quench(i, int errno)
+{
+ struct tcpcb *tp = intotcpcb(inp);
+
+ if (tp)
+ tp->snd_cwnd = tp->t_maxseg;
+}
+
+#endif
+
+/*
+ * TCP protocol interface to socket abstraction.
+ */
+
+/*
+ * User issued close, and wish to trail through shutdown states:
+ * if never received SYN, just forget it. If got a SYN from peer,
+ * but haven't sent FIN, then go to FIN_WAIT_1 state to send peer a FIN.
+ * If already got a FIN from peer, then almost done; go to LAST_ACK
+ * state. In all other cases, have already sent FIN to peer (e.g.
+ * after PRU_SHUTDOWN), and just have to play tedious game waiting
+ * for peer to send FIN or not respond to keep-alives, etc.
+ * We can let the user exit from the close as soon as the FIN is acked.
+ */
+void
+tcp_sockclosed(PNATState pData, struct tcpcb *tp)
+{
+ LogFlowFunc(("ENTER: tp = %R[tcpcb793]\n", tp));
+ LogFunc(("tp->t_socket:%R[natsock]\n",tp->t_socket));
+
+ switch (tp->t_state)
+ {
+ case TCPS_CLOSED:
+ case TCPS_LISTEN:
+ case TCPS_SYN_SENT:
+ TCP_STATE_SWITCH_TO(tp, TCPS_CLOSED);
+ tp = tcp_close(pData, tp);
+ break;
+
+ case TCPS_SYN_RECEIVED:
+ case TCPS_ESTABLISHED:
+ TCP_STATE_SWITCH_TO(tp, TCPS_FIN_WAIT_1);
+ break;
+
+ case TCPS_CLOSE_WAIT:
+ TCP_STATE_SWITCH_TO(tp, TCPS_LAST_ACK);
+ break;
+ }
+/* soisfdisconnecting(tp->t_socket); */
+ if ( tp
+ && tp->t_state >= TCPS_FIN_WAIT_2)
+ soisfdisconnected(tp->t_socket);
+ /*
+ * (vasily) there're situations when the FIN or FIN,ACK are lost (Windows host)
+ * and retransmitting keeps VBox busy on sending closing sequences *very* frequent,
+ * easting a lot of CPU. To avoid this we don't sent on sockets marked as closed
+ * (see slirp.c for details about setting so_close member).
+ */
+ if ( tp
+ && tp->t_socket
+ && !tp->t_socket->so_close)
+ tcp_output(pData, tp);
+}
+
+/*
+ * Connect to a host on the Internet
+ * Called by tcp_input
+ * Only do a connect, the tcp fields will be set in tcp_input
+ * return 0 if there's a result of the connect,
+ * else return -1 means we're still connecting
+ * The return value is almost always -1 since the socket is
+ * nonblocking. Connect returns after the SYN is sent, and does
+ * not wait for ACK+SYN.
+ */
+int tcp_fconnect(PNATState pData, struct socket *so)
+{
+ int ret = 0;
+
+ LogFlowFunc(("ENTER: so = %R[natsock]\n", so));
+
+ if ((ret = so->s = socket(AF_INET, SOCK_STREAM, 0)) >= 0)
+ {
+ int opt, s = so->s;
+ struct sockaddr_in addr;
+
+ fd_nonblock(s);
+
+ opt = 1;
+ setsockopt(s, SOL_SOCKET, SO_OOBINLINE, (char *)&opt, sizeof(opt));
+ opt = 1;
+ setsockopt(s, IPPROTO_TCP, TCP_NODELAY, (char *)&opt, sizeof(opt));
+
+ ret = sobind(pData, so);
+ if (ret != 0)
+ return ret;
+
+ addr.sin_family = AF_INET;
+ if ((so->so_faddr.s_addr & RT_H2N_U32(pData->netmask)) == pData->special_addr.s_addr)
+ {
+ /* It's an alias */
+ switch(RT_N2H_U32(so->so_faddr.s_addr) & ~pData->netmask)
+ {
+ case CTL_DNS:
+ /*
+ * TCP DNS proxy. We only support "forwarding" to
+ * single server. We don't have infrastructure in
+ * place to re-try connections to other servers.
+ */
+ if ( pData->fUseDnsProxy
+ && so->so_fport == RT_H2N_U16_C(53))
+ {
+ struct dns_entry *ns = TAILQ_LAST(&pData->pDnsList, dns_list_head);
+ if (ns != NULL)
+ {
+ addr.sin_addr = ns->de_addr;
+ break;
+ }
+ }
+ RT_FALL_THRU();
+ case CTL_ALIAS:
+ default:
+ addr.sin_addr = loopback_addr;
+ break;
+ }
+ }
+ else
+ addr.sin_addr = so->so_faddr;
+ addr.sin_port = so->so_fport;
+
+ Log2(("NAT: tcp connect to %RTnaipv4:%d\n",
+ addr.sin_addr.s_addr, RT_N2H_U16(addr.sin_port)));
+
+ ret = connect(s,(struct sockaddr *)&addr,sizeof (addr));
+
+ /*
+ * If it's not in progress, it failed, so we just return 0,
+ * without clearing SS_NOFDREF
+ */
+ soisfconnecting(so);
+ }
+
+ return(ret);
+}
+
+/*
+ * Accept the socket and connect to the local-host
+ *
+ * We have a problem. The correct thing to do would be
+ * to first connect to the local-host, and only if the
+ * connection is accepted, then do an accept() here.
+ * But, a) we need to know who's trying to connect
+ * to the socket to be able to SYN the local-host, and
+ * b) we are already connected to the foreign host by
+ * the time it gets to accept(), so... We simply accept
+ * here and SYN the local-host.
+ */
+void
+tcp_connect(PNATState pData, struct socket *inso)
+{
+ struct socket *so;
+ struct sockaddr_in addr;
+ socklen_t addrlen = sizeof(struct sockaddr_in);
+ struct tcpcb *tp;
+ int s, opt;
+ int status;
+ socklen_t optlen;
+ static int cVerbose = 1;
+
+ LogFlowFunc(("ENTER: inso = %R[natsock]\n", inso));
+
+ if ( inso->so_laddr.s_addr == INADDR_ANY /* delayed port-forwarding? */
+ && pData->guest_addr_guess.s_addr == INADDR_ANY)
+ {
+ LogRel2(("NAT: Port-forward: guest address unknown for %R[natsock]\n", inso));
+ closesocket(accept(inso->s, NULL, NULL));
+ if (inso->so_state & SS_FACCEPTONCE)
+ tcp_close(pData, sototcpcb(inso));
+ return;
+ }
+
+ /*
+ * If it's an SS_ACCEPTONCE socket, no need to socreate()
+ * another socket, just use the accept() socket.
+ */
+ if (inso->so_state & SS_FACCEPTONCE)
+ {
+ /* FACCEPTONCE already have a tcpcb */
+ so = inso;
+ }
+ else
+ {
+ if ((so = socreate()) == NULL)
+ {
+ /* If it failed, get rid of the pending connection */
+ closesocket(accept(inso->s,(struct sockaddr *)&addr,&addrlen));
+ return;
+ }
+ if (tcp_attach(pData, so) < 0)
+ {
+ RTMemFree(so); /* NOT sofree */
+ return;
+ }
+ so->so_laddr = inso->so_laddr;
+ so->so_lport = inso->so_lport;
+ }
+
+ if (so->so_laddr.s_addr == INADDR_ANY)
+ {
+ LogRel2(("NAT: Port-forward: using %RTnaipv4 for %R[natsock]\n",
+ pData->guest_addr_guess.s_addr, inso));
+ so->so_laddr = pData->guest_addr_guess;
+ }
+
+ (void) tcp_mss(pData, sototcpcb(so), 0);
+
+ fd_nonblock(inso->s);
+ if ((s = accept(inso->s,(struct sockaddr *)&addr,&addrlen)) < 0)
+ {
+ tcp_close(pData, sototcpcb(so)); /* This will sofree() as well */
+ return;
+ }
+ fd_nonblock(s);
+ opt = 1;
+ setsockopt(s, SOL_SOCKET, SO_REUSEADDR,(char *)&opt, sizeof(int));
+ opt = 1;
+ setsockopt(s, SOL_SOCKET, SO_OOBINLINE,(char *)&opt, sizeof(int));
+ opt = 1;
+ setsockopt(s, IPPROTO_TCP, TCP_NODELAY,(char *)&opt, sizeof(int));
+
+ optlen = sizeof(int);
+ status = getsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&opt, &optlen);
+ if (status < 0)
+ {
+ LogRel(("NAT: Error(%d) while getting RCV capacity\n", errno));
+ goto no_sockopt;
+ }
+ if (cVerbose > 0)
+ LogRel(("NAT: Old socket recv size: %dKB\n", opt / 1024));
+ /** @todo (r-vvl) make it configurable (via extra data) */
+ opt = pData->socket_rcv;
+ status = setsockopt(s, SOL_SOCKET, SO_RCVBUF, (char *)&opt, sizeof(int));
+ if (status < 0)
+ {
+ LogRel(("NAT: Error(%d) while setting RCV capacity to (%d)\n", errno, opt));
+ goto no_sockopt;
+ }
+ optlen = sizeof(int);
+ status = getsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&opt, &optlen);
+ if (status < 0)
+ {
+ LogRel(("NAT: Error(%d) while getting SND capacity\n", errno));
+ goto no_sockopt;
+ }
+ if (cVerbose > 0)
+ LogRel(("NAT: Old socket send size: %dKB\n", opt / 1024));
+ opt = pData->socket_rcv;
+ status = setsockopt(s, SOL_SOCKET, SO_SNDBUF, (char *)&opt, sizeof(int));
+ if (status < 0)
+ {
+ LogRel(("NAT: Error(%d) while setting SND capacity to (%d)\n", errno, opt));
+ goto no_sockopt;
+ }
+ if (cVerbose > 0)
+ cVerbose--;
+
+ no_sockopt:
+ so->so_fport = addr.sin_port;
+ so->so_faddr = addr.sin_addr;
+ /* Translate connections from localhost to the real hostname */
+ if (so->so_faddr.s_addr == 0 || so->so_faddr.s_addr == loopback_addr.s_addr)
+ so->so_faddr = alias_addr;
+
+ /* Close the accept() socket, set right state */
+ if (inso->so_state & SS_FACCEPTONCE)
+ {
+ closesocket(so->s); /* If we only accept once, close the accept() socket */
+ so->so_state = SS_NOFDREF; /* Don't select it yet, even though we have an FD */
+ /* if it's not FACCEPTONCE, it's already NOFDREF */
+ }
+ so->s = s;
+
+ tp = sototcpcb(so);
+
+ tcp_template(tp);
+
+ /* Compute window scaling to request. */
+/* while (tp->request_r_scale < TCP_MAX_WINSHIFT
+ * && (TCP_MAXWIN << tp->request_r_scale) < so->so_rcv.sb_hiwat)
+ * tp->request_r_scale++;
+ */
+
+/* soisconnecting(so); */ /* NOFDREF used instead */
+ tcpstat.tcps_connattempt++;
+
+ TCP_STATE_SWITCH_TO(tp, TCPS_SYN_SENT);
+ tp->t_timer[TCPT_KEEP] = TCPTV_KEEP_INIT;
+ tp->iss = tcp_iss;
+ tcp_iss += TCP_ISSINCR/2;
+ tcp_sendseqinit(tp);
+ tcp_output(pData, tp);
+}
+
+/*
+ * Attach a TCPCB to a socket.
+ */
+int
+tcp_attach(PNATState pData, struct socket *so)
+{
+ /* We're attaching already attached socket??? */
+ Assert(so->so_type == 0);
+ if ((so->so_tcpcb = tcp_newtcpcb(pData, so)) == NULL)
+ return -1;
+
+ SOCKET_LOCK_CREATE(so);
+ QSOCKET_LOCK(tcb);
+ insque(pData, so, &tcb);
+ NSOCK_INC();
+ QSOCKET_UNLOCK(tcb);
+ return 0;
+}
diff --git a/src/VBox/Devices/Network/slirp/tcp_timer.c b/src/VBox/Devices/Network/slirp/tcp_timer.c
new file mode 100644
index 00000000..5ee67c9e
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/tcp_timer.c
@@ -0,0 +1,361 @@
+/* $Id: tcp_timer.c $ */
+/** @file
+ * NAT - TCP timers.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)tcp_timer.c 8.1 (Berkeley) 6/10/93
+ * tcp_timer.c,v 1.2 1994/08/02 07:49:10 davidg Exp
+ */
+
+#include <slirp.h>
+
+
+static struct tcpcb *tcp_timers(PNATState pData, register struct tcpcb *tp, int timer);
+/*
+ * Fast timeout routine for processing delayed acks
+ */
+void
+tcp_fasttimo(PNATState pData)
+{
+ register struct socket *so, *so_next;
+ register struct tcpcb *tp;
+
+ LogFlowFuncEnter();
+
+ so = tcb.so_next;
+ if (so)
+ QSOCKET_FOREACH (so, so_next, tcp)
+ /* { */
+ if ( (tp = (struct tcpcb *)so->so_tcpcb)
+ && (tp->t_flags & TF_DELACK))
+ {
+ tp->t_flags &= ~TF_DELACK;
+ tp->t_flags |= TF_ACKNOW;
+ tcpstat.tcps_delack++;
+ TCP_OUTPUT(pData, tp);
+ }
+ LOOP_LABEL(tcp, so, so_next);
+ }
+}
+
+/*
+ * Tcp protocol timeout routine called every 500 ms.
+ * Updates the timers in all active tcb's and
+ * causes finite state machine actions if timers expire.
+ */
+void
+tcp_slowtimo(PNATState pData)
+{
+ register struct socket *ip, *ipnxt;
+ register struct tcpcb *tp;
+ register int i;
+
+ LogFlowFuncEnter();
+
+ /*
+ * Search through tcb's and update active timers.
+ */
+ ip = tcb.so_next;
+ if (ip == 0)
+ return;
+ QSOCKET_FOREACH(ip, ipnxt, tcp)
+ /* { */
+ ipnxt = ip->so_next;
+ tp = sototcpcb(ip);
+ if (tp == 0)
+ CONTINUE(tcp);
+ for (i = 0; i < TCPT_NTIMERS; i++)
+ {
+ if (tp->t_timer[i] && --tp->t_timer[i] == 0)
+ {
+ tcp_timers(pData, tp, i);
+ if (ipnxt->so_prev != ip)
+ goto tpgone;
+ }
+ }
+ tp->t_idle++;
+ if (tp->t_rtt)
+ tp->t_rtt++;
+tpgone:
+ ;
+ LOOP_LABEL(tcp, ip, ipnxt);
+ }
+ tcp_iss += TCP_ISSINCR / PR_SLOWHZ; /* increment iss */
+#ifdef TCP_COMPAT_42
+ if ((int)tcp_iss < 0)
+ tcp_iss = 0; /* XXX */
+#endif
+ tcp_now++; /* for timestamps */
+}
+
+/*
+ * Cancel all timers for TCP tp.
+ */
+void
+tcp_canceltimers(struct tcpcb *tp)
+{
+ register int i;
+
+ for (i = 0; i < TCPT_NTIMERS; i++)
+ tp->t_timer[i] = 0;
+}
+
+const int tcp_backoff[TCP_MAXRXTSHIFT + 1] =
+{
+ 1, 2, 4, 8, 16, 32, 64, 64, 64, 64, 64, 64, 64
+};
+
+/*
+ * TCP timer processing.
+ */
+static struct tcpcb *
+tcp_timers(PNATState pData, register struct tcpcb *tp, int timer)
+{
+ register int rexmt;
+ int fUninitializedTemplate = 0;
+
+ LogFlowFunc(("ENTER: tp:%R[tcpcb793], timer:%d\n", tp, timer));
+ fUninitializedTemplate = RT_BOOL(( tp->t_template.ti_src.s_addr == INADDR_ANY
+ || tp->t_template.ti_dst.s_addr == INADDR_ANY));
+ if (fUninitializedTemplate)
+ {
+ tp = tcp_drop(pData, tp, 0);
+ return tp;
+ }
+
+ switch (timer)
+ {
+ /*
+ * 2 MSL timeout in shutdown went off. If we're closed but
+ * still waiting for peer to close and connection has been idle
+ * too long, or if 2MSL time is up from TIME_WAIT, delete connection
+ * control block. Otherwise, check again in a bit.
+ */
+ case TCPT_2MSL:
+ if (tp->t_state != TCPS_TIME_WAIT &&
+ tp->t_idle <= tcp_maxidle)
+ tp->t_timer[TCPT_2MSL] = tcp_keepintvl;
+ else
+ tp = tcp_close(pData, tp);
+ break;
+
+ /*
+ * Retransmission timer went off. Message has not
+ * been acked within retransmit interval. Back off
+ * to a longer retransmit interval and retransmit one segment.
+ */
+ case TCPT_REXMT:
+ STAM_COUNTER_INC(&pData->StatTCP_retransmit);
+ /*
+ * XXX If a packet has timed out, then remove all the queued
+ * packets for that session.
+ */
+ if (++tp->t_rxtshift > TCP_MAXRXTSHIFT)
+ {
+ /*
+ * This is a hack to suit our terminal server here at the uni of canberra
+ * since they have trouble with zeroes... It usually lets them through
+ * unharmed, but under some conditions, it'll eat the zeros. If we
+ * keep retransmitting it, it'll keep eating the zeroes, so we keep
+ * retransmitting, and eventually the connection dies...
+ * (this only happens on incoming data)
+ *
+ * So, if we were gonna drop the connection from too many retransmits,
+ * don't... instead halve the t_maxseg, which might break up the NULLs and
+ * let them through
+ *
+ * *sigh*
+ */
+ tp->t_maxseg >>= 1;
+ if (tp->t_maxseg < 32)
+ {
+ /*
+ * We tried our best, now the connection must die!
+ */
+ tp->t_rxtshift = TCP_MAXRXTSHIFT;
+ tcpstat.tcps_timeoutdrop++;
+ tp = tcp_drop(pData, tp, tp->t_softerror);
+ /* tp->t_softerror : ETIMEDOUT); */ /* XXX */
+ return (tp); /* XXX */
+ }
+
+ /*
+ * Set rxtshift to 6, which is still at the maximum
+ * backoff time
+ */
+ tp->t_rxtshift = 6;
+ }
+ tcpstat.tcps_rexmttimeo++;
+ rexmt = TCP_REXMTVAL(tp) * tcp_backoff[tp->t_rxtshift];
+ TCPT_RANGESET(tp->t_rxtcur, rexmt,
+ (short)tp->t_rttmin, TCPTV_REXMTMAX); /* XXX */
+ tp->t_timer[TCPT_REXMT] = tp->t_rxtcur;
+ /*
+ * If losing, let the lower level know and try for
+ * a better route. Also, if we backed off this far,
+ * our srtt estimate is probably bogus. Clobber it
+ * so we'll take the next rtt measurement as our srtt;
+ * move the current srtt into rttvar to keep the current
+ * retransmit times until then.
+ */
+ if (tp->t_rxtshift > TCP_MAXRXTSHIFT / 4)
+ {
+/* in_losing(tp->t_inpcb); */
+ tp->t_rttvar += (tp->t_srtt >> TCP_RTT_SHIFT);
+ tp->t_srtt = 0;
+ }
+ tp->snd_nxt = tp->snd_una;
+ /*
+ * If timing a segment in this window, stop the timer.
+ */
+ tp->t_rtt = 0;
+ /*
+ * Close the congestion window down to one segment
+ * (we'll open it by one segment for each ack we get).
+ * Since we probably have a window's worth of unacked
+ * data accumulated, this "slow start" keeps us from
+ * dumping all that data as back-to-back packets (which
+ * might overwhelm an intermediate gateway).
+ *
+ * There are two phases to the opening: Initially we
+ * open by one mss on each ack. This makes the window
+ * size increase exponentially with time. If the
+ * window is larger than the path can handle, this
+ * exponential growth results in dropped packet(s)
+ * almost immediately. To get more time between
+ * drops but still "push" the network to take advantage
+ * of improving conditions, we switch from exponential
+ * to linear window opening at some threshold size.
+ * For a threshold, we use half the current window
+ * size, truncated to a multiple of the mss.
+ *
+ * (the minimum cwnd that will give us exponential
+ * growth is 2 mss. We don't allow the threshold
+ * to go below this.)
+ */
+ {
+ u_int win = min(tp->snd_wnd, tp->snd_cwnd) / 2 / tp->t_maxseg;
+ if (win < 2)
+ win = 2;
+ tp->snd_cwnd = tp->t_maxseg;
+ tp->snd_ssthresh = win * tp->t_maxseg;
+ tp->t_dupacks = 0;
+ }
+ (void) tcp_output(pData, tp);
+ break;
+
+ /*
+ * Persistence timer into zero window.
+ * Force a byte to be output, if possible.
+ */
+ case TCPT_PERSIST:
+ tcpstat.tcps_persisttimeo++;
+ tcp_setpersist(tp);
+ tp->t_force = 1;
+ (void) tcp_output(pData, tp);
+ tp->t_force = 0;
+ break;
+
+ /*
+ * Keep-alive timer went off; send something
+ * or drop connection if idle for too long.
+ */
+ case TCPT_KEEP:
+ tcpstat.tcps_keeptimeo++;
+ if (tp->t_state < TCPS_ESTABLISHED)
+ goto dropit;
+/* if (tp->t_socket->so_options & SO_KEEPALIVE && */
+ if ((so_options) && tp->t_state <= TCPS_CLOSE_WAIT)
+ {
+ if (tp->t_idle >= tcp_keepidle + tcp_maxidle)
+ goto dropit;
+ /*
+ * Send a packet designed to force a response
+ * if the peer is up and reachable:
+ * either an ACK if the connection is still alive,
+ * or an RST if the peer has closed the connection
+ * due to timeout or reboot.
+ * Using sequence number tp->snd_una-1
+ * causes the transmitted zero-length segment
+ * to lie outside the receive window;
+ * by the protocol spec, this requires the
+ * correspondent TCP to respond.
+ */
+ tcpstat.tcps_keepprobe++;
+#ifdef TCP_COMPAT_42
+ /*
+ * The keepalive packet must have nonzero length
+ * to get a 4.2 host to respond.
+ */
+ tcp_respond(tp, &tp->t_template, (struct mbuf *)NULL,
+ tp->rcv_nxt - 1, tp->snd_una - 1, 0);
+#else
+ tcp_respond(pData, tp, &tp->t_template, (struct mbuf *)NULL,
+ tp->rcv_nxt, tp->snd_una - 1, 0);
+#endif
+ tp->t_timer[TCPT_KEEP] = tcp_keepintvl;
+ }
+ else
+ tp->t_timer[TCPT_KEEP] = tcp_keepidle;
+ break;
+
+ dropit:
+ tcpstat.tcps_keepdrops++;
+ tp = tcp_drop(pData, tp, 0); /* ETIMEDOUT); */
+ break;
+ }
+
+ return tp;
+}
diff --git a/src/VBox/Devices/Network/slirp/tcp_timer.h b/src/VBox/Devices/Network/slirp/tcp_timer.h
new file mode 100644
index 00000000..c2c03b77
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/tcp_timer.h
@@ -0,0 +1,160 @@
+/* $Id: tcp_timer.h $ */
+/** @file
+ * NAT - TCP timer (declarations/defines).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)tcp_timer.h 8.1 (Berkeley) 6/10/93
+ * tcp_timer.h,v 1.4 1994/08/21 05:27:38 paul Exp
+ */
+
+#ifndef _TCP_TIMER_H_
+#define _TCP_TIMER_H_
+
+/*
+ * Definitions of the TCP timers. These timers are counted
+ * down PR_SLOWHZ times a second.
+ */
+#define TCPT_NTIMERS 4
+
+#define TCPT_REXMT 0 /* retransmit */
+#define TCPT_PERSIST 1 /* retransmit persistence */
+#define TCPT_KEEP 2 /* keep alive */
+#define TCPT_2MSL 3 /* 2*msl quiet time timer */
+
+/*
+ * The TCPT_REXMT timer is used to force retransmissions.
+ * The TCP has the TCPT_REXMT timer set whenever segments
+ * have been sent for which ACKs are expected but not yet
+ * received. If an ACK is received which advances tp->snd_una,
+ * then the retransmit timer is cleared (if there are no more
+ * outstanding segments) or reset to the base value (if there
+ * are more ACKs expected). Whenever the retransmit timer goes off,
+ * we retransmit one unacknowledged segment, and do a backoff
+ * on the retransmit timer.
+ *
+ * The TCPT_PERSIST timer is used to keep window size information
+ * flowing even if the window goes shut. If all previous transmissions
+ * have been acknowledged (so that there are no retransmissions in progress),
+ * and the window is too small to bother sending anything, then we start
+ * the TCPT_PERSIST timer. When it expires, if the window is nonzero,
+ * we go to transmit state. Otherwise, at intervals send a single byte
+ * into the peer's window to force him to update our window information.
+ * We do this at most as often as TCPT_PERSMIN time intervals,
+ * but no more frequently than the current estimate of round-trip
+ * packet time. The TCPT_PERSIST timer is cleared whenever we receive
+ * a window update from the peer.
+ *
+ * The TCPT_KEEP timer is used to keep connections alive. If an
+ * connection is idle (no segments received) for TCPTV_KEEP_INIT amount of time,
+ * but not yet established, then we drop the connection. Once the connection
+ * is established, if the connection is idle for TCPTV_KEEP_IDLE time
+ * (and keepalives have been enabled on the socket), we begin to probe
+ * the connection. We force the peer to send us a segment by sending:
+ * <SEQ=SND.UNA-1><ACK=RCV.NXT><CTL=ACK>
+ * This segment is (deliberately) outside the window, and should elicit
+ * an ack segment in response from the peer. If, despite the TCPT_KEEP
+ * initiated segments we cannot elicit a response from a peer in TCPT_MAXIDLE
+ * amount of time probing, then we drop the connection.
+ */
+
+/*
+ * Time constants.
+ */
+#define TCPTV_MSL ( 5*PR_SLOWHZ) /* max seg lifetime (hah!) */
+
+#define TCPTV_SRTTBASE 0 /* base roundtrip time;
+ if 0, no idea yet */
+#define TCPTV_SRTTDFLT ( 3*PR_SLOWHZ) /* assumed RTT if no info */
+
+#define TCPTV_PERSMIN ( 5*PR_SLOWHZ) /* retransmit persistence */
+#define TCPTV_PERSMAX ( 60*PR_SLOWHZ) /* maximum persist interval */
+
+#define TCPTV_KEEP_INIT ( 75*PR_SLOWHZ) /* initial connect keep alive */
+#define TCPTV_KEEP_IDLE (120*60*PR_SLOWHZ) /* dflt time before probing */
+#define TCPTV_KEEPINTVL ( 75*PR_SLOWHZ) /* default probe interval */
+#define TCPTV_KEEPCNT 8 /* max probes before drop */
+
+#define TCPTV_MIN ( 1*PR_SLOWHZ) /* minimum allowable value */
+#define TCPTV_REXMTMAX ( 12*PR_SLOWHZ) /* max allowable REXMT value */
+
+#define TCP_LINGERTIME 120 /* linger at most 2 minutes */
+
+#define TCP_MAXRXTSHIFT 12 /* maximum retransmits */
+
+
+#ifdef TCPTIMERS
+char *tcptimers[] =
+ { "REXMT", "PERSIST", "KEEP", "2MSL" };
+#endif
+
+/*
+ * Force a time value to be in a certain range.
+ */
+#define TCPT_RANGESET(tv, value, tvmin, tvmax) { \
+ (tv) = (value); \
+ if ((tv) < (tvmin)) \
+ (tv) = (tvmin); \
+ else if ((tv) > (tvmax)) \
+ (tv) = (tvmax); \
+}
+
+extern const int tcp_backoff[];
+
+struct tcpcb;
+
+void tcp_fasttimo (PNATState);
+void tcp_slowtimo (PNATState);
+void tcp_canceltimers (struct tcpcb *);
+#endif
diff --git a/src/VBox/Devices/Network/slirp/tcp_var.h b/src/VBox/Devices/Network/slirp/tcp_var.h
new file mode 100644
index 00000000..ae183f6c
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/tcp_var.h
@@ -0,0 +1,269 @@
+/* $Id: tcp_var.h $ */
+/** @file
+ * NAT - TCP (declarations).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1982, 1986, 1993, 1994
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)tcp_var.h 8.3 (Berkeley) 4/10/94
+ * tcp_var.h,v 1.3 1994/08/21 05:27:39 paul Exp
+ */
+
+#ifndef _TCP_VAR_H_
+#define _TCP_VAR_H_
+
+#include "queue.h"
+#include "tcpip.h"
+#include "tcp_timer.h"
+
+/* TCP segment queue entry */
+struct tseg_qent
+{
+ LIST_ENTRY(tseg_qent) tqe_q;
+ int tqe_len; /* TCP segment data length */
+ struct tcphdr *tqe_th; /* a pointer to tcp header */
+ struct mbuf *tqe_m; /* mbuf contains packet */
+};
+LIST_HEAD(tsegqe_head, tseg_qent);
+
+/*
+ * Tcp control block, one per tcp; fields:
+ */
+struct tcpcb
+{
+ LIST_ENTRY(tcpcb) t_list;
+ struct tsegqe_head t_segq; /* segment reassembly queue */
+ int t_segqlen; /* segment reassembly queue length */
+ int16_t t_state; /* state of this connection */
+ int16_t t_timer[TCPT_NTIMERS]; /* tcp timers */
+ int16_t t_rxtshift; /* log(2) of rexmt exp. backoff */
+ int16_t t_rxtcur; /* current retransmit value */
+ int16_t t_dupacks; /* consecutive dup acks recd */
+ uint16_t t_maxseg; /* maximum segment size */
+ char t_force; /* 1 if forcing out a byte */
+ uint16_t t_flags;
+#define TF_ACKNOW 0x0001 /* ack peer immediately */
+#define TF_DELACK 0x0002 /* ack, but try to delay it */
+#define TF_NODELAY 0x0004 /* don't delay packets to coalesce */
+#define TF_NOOPT 0x0008 /* don't use tcp options */
+#define TF_SENTFIN 0x0010 /* have sent FIN */
+#define TF_REQ_SCALE 0x0020 /* have/will request window scaling */
+#define TF_RCVD_SCALE 0x0040 /* other side has requested scaling */
+#define TF_REQ_TSTMP 0x0080 /* have/will request timestamps */
+#define TF_RCVD_TSTMP 0x0100 /* a timestamp was received in SYN */
+#define TF_SACK_PERMIT 0x0200 /* other side said I could SACK */
+
+ /* Make it static for now */
+/* struct tcpiphdr *t_template; / * skeletal packet for transmit */
+ struct tcpiphdr t_template;
+
+ struct socket *t_socket; /* back pointer to socket */
+/*
+ * The following fields are used as in the protocol specification.
+ * See RFC783, Dec. 1981, page 21.
+ */
+ /* send sequence variables */
+ tcp_seq snd_una; /* send unacknowledged */
+ tcp_seq snd_nxt; /* send next */
+ tcp_seq snd_up; /* send urgent pointer */
+ tcp_seq snd_wl1; /* window update seg seq number */
+ tcp_seq snd_wl2; /* window update seg ack number */
+ tcp_seq iss; /* initial send sequence number */
+ uint32_t snd_wnd; /* send window */
+ /* receive sequence variables */
+ uint32_t rcv_wnd; /* receive window */
+ tcp_seq rcv_nxt; /* receive next */
+ tcp_seq rcv_up; /* receive urgent pointer */
+ tcp_seq irs; /* initial receive sequence number */
+/*
+ * Additional variables for this implementation.
+ */
+ /* receive variables */
+ tcp_seq rcv_adv; /* advertised window */
+ /* retransmit variables */
+ tcp_seq snd_max; /* highest sequence number sent;
+ * used to recognize retransmits
+ */
+ /* congestion control (for slow start, source quench, retransmit after loss) */
+ uint32_t snd_cwnd; /* congestion-controlled window */
+ uint32_t snd_ssthresh; /* snd_cwnd size threshold for
+ * for slow start exponential to
+ * linear switch
+ */
+/*
+ * transmit timing stuff. See below for scale of srtt and rttvar.
+ * "Variance" is actually smoothed difference.
+ */
+ int16_t t_idle; /* inactivity time */
+ int16_t t_rtt; /* round trip time */
+ tcp_seq t_rtseq; /* sequence number being timed */
+ int16_t t_srtt; /* smoothed round-trip time */
+ int16_t t_rttvar; /* variance in round-trip time */
+ uint16_t t_rttmin; /* minimum rtt allowed */
+ uint32_t max_sndwnd; /* largest window peer has offered */
+
+/* out-of-band data */
+ char t_oobflags; /* have some */
+ char t_iobc; /* input character */
+#define TCPOOB_HAVEDATA 0x01
+#define TCPOOB_HADDATA 0x02
+ short t_softerror; /* possible error not yet reported */
+
+/* RFC 1323 variables */
+ uint8_t snd_scale; /* window scaling for send window */
+ uint8_t rcv_scale; /* window scaling for recv window */
+ uint8_t request_r_scale; /* pending window scaling */
+ uint8_t requested_s_scale;
+ uint32_t ts_recent; /* timestamp echo data */
+ uint32_t ts_recent_age; /* when last updated */
+ tcp_seq last_ack_sent;
+};
+
+LIST_HEAD(tcpcbhead, tcpcb);
+
+#define sototcpcb(so) ((so)->so_tcpcb)
+
+/*
+ * The smoothed round-trip time and estimated variance
+ * are stored as fixed point numbers scaled by the values below.
+ * For convenience, these scales are also used in smoothing the average
+ * (smoothed = (1/scale)sample + ((scale-1)/scale)smoothed).
+ * With these scales, srtt has 3 bits to the right of the binary point,
+ * and thus an "ALPHA" of 0.875. rttvar has 2 bits to the right of the
+ * binary point, and is smoothed with an ALPHA of 0.75.
+ */
+#define TCP_RTT_SCALE 8 /* multiplier for srtt; 3 bits frac. */
+#define TCP_RTT_SHIFT 3 /* shift for srtt; 3 bits frac. */
+#define TCP_RTTVAR_SCALE 4 /* multiplier for rttvar; 2 bits */
+#define TCP_RTTVAR_SHIFT 2 /* multiplier for rttvar; 2 bits */
+
+/*
+ * The initial retransmission should happen at rtt + 4 * rttvar.
+ * Because of the way we do the smoothing, srtt and rttvar
+ * will each average +1/2 tick of bias. When we compute
+ * the retransmit timer, we want 1/2 tick of rounding and
+ * 1 extra tick because of +-1/2 tick uncertainty in the
+ * firing of the timer. The bias will give us exactly the
+ * 1.5 tick we need. But, because the bias is
+ * statistical, we have to test that we don't drop below
+ * the minimum feasible timer (which is 2 ticks).
+ * This macro assumes that the value of TCP_RTTVAR_SCALE
+ * is the same as the multiplier for rttvar.
+ */
+#define TCP_REXMTVAL(tp) \
+ (((tp)->t_srtt >> TCP_RTT_SHIFT) + (tp)->t_rttvar)
+
+/*
+ * TCP statistics.
+ * Many of these should be kept per connection,
+ * but that's inconvenient at the moment.
+ */
+struct tcpstat_t
+{
+ u_long tcps_connattempt; /* connections initiated */
+ u_long tcps_accepts; /* connections accepted */
+ u_long tcps_connects; /* connections established */
+ u_long tcps_drops; /* connections dropped */
+ u_long tcps_conndrops; /* embryonic connections dropped */
+ u_long tcps_closed; /* conn. closed (includes drops) */
+ u_long tcps_segstimed; /* segs where we tried to get rtt */
+ u_long tcps_rttupdated; /* times we succeeded */
+ u_long tcps_delack; /* delayed acks sent */
+ u_long tcps_timeoutdrop; /* conn. dropped in rxmt timeout */
+ u_long tcps_rexmttimeo; /* retransmit timeouts */
+ u_long tcps_persisttimeo; /* persist timeouts */
+ u_long tcps_keeptimeo; /* keepalive timeouts */
+ u_long tcps_keepprobe; /* keepalive probes sent */
+ u_long tcps_keepdrops; /* connections dropped in keepalive */
+
+ u_long tcps_sndtotal; /* total packets sent */
+ u_long tcps_sndpack; /* data packets sent */
+ u_long tcps_sndbyte; /* data bytes sent */
+ u_long tcps_sndrexmitpack; /* data packets retransmitted */
+ u_long tcps_sndrexmitbyte; /* data bytes retransmitted */
+ u_long tcps_sndacks; /* ack-only packets sent */
+ u_long tcps_sndprobe; /* window probes sent */
+ u_long tcps_sndurg; /* packets sent with URG only */
+ u_long tcps_sndwinup; /* window update-only packets sent */
+ u_long tcps_sndctrl; /* control (SYN|FIN|RST) packets sent */
+
+ u_long tcps_rcvtotal; /* total packets received */
+ u_long tcps_rcvpack; /* packets received in sequence */
+ u_long tcps_rcvbyte; /* bytes received in sequence */
+ u_long tcps_rcvbadsum; /* packets received with ccksum errs */
+ u_long tcps_rcvbadoff; /* packets received with bad offset */
+/* u_long tcps_rcvshort; */ /* packets received too short */
+ u_long tcps_rcvduppack; /* duplicate-only packets received */
+ u_long tcps_rcvdupbyte; /* duplicate-only bytes received */
+ u_long tcps_rcvpartduppack; /* packets with some duplicate data */
+ u_long tcps_rcvpartdupbyte; /* dup. bytes in part-dup. packets */
+ u_long tcps_rcvoopack; /* out-of-order packets received */
+ u_long tcps_rcvoobyte; /* out-of-order bytes received */
+ u_long tcps_rcvpackafterwin; /* packets with data after window */
+ u_long tcps_rcvbyteafterwin; /* bytes rcvd after window */
+ u_long tcps_rcvafterclose; /* packets rcvd after "close" */
+ u_long tcps_rcvwinprobe; /* rcvd window probe packets */
+ u_long tcps_rcvdupack; /* rcvd duplicate acks */
+ u_long tcps_rcvacktoomuch; /* rcvd acks for unsent data */
+ u_long tcps_rcvackpack; /* rcvd ack packets */
+ u_long tcps_rcvackbyte; /* bytes acked by rcvd acks */
+ u_long tcps_rcvwinupd; /* rcvd window update packets */
+/* u_long tcps_pawsdrop; */ /* segments dropped due to PAWS */
+ u_long tcps_predack; /* times hdr predict ok for acks */
+ u_long tcps_preddat; /* times hdr predict ok for data pkts */
+ u_long tcps_socachemiss; /* tcp_last_so misses */
+ u_long tcps_didnuttin; /* Times tcp_output didn't do anything XXX */
+ u_long tcps_rcvmemdrop;
+};
+
+#endif
diff --git a/src/VBox/Devices/Network/slirp/tcpip.h b/src/VBox/Devices/Network/slirp/tcpip.h
new file mode 100644
index 00000000..fc6c3b08
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/tcpip.h
@@ -0,0 +1,102 @@
+/* $Id: tcpip.h $ */
+/** @file
+ * NAT - TCP/IP (declarations/defines).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)tcpip.h 8.1 (Berkeley) 6/10/93
+ * tcpip.h,v 1.3 1994/08/21 05:27:40 paul Exp
+ */
+
+#ifndef _TCPIP_H_
+#define _TCPIP_H_
+
+/*
+ * Tcp+ip header, after ip options removed.
+ */
+struct tcpiphdr
+{
+ struct ipovly ti_i; /* overlaid ip structure */
+ struct tcphdr ti_t; /* tcp header */
+};
+AssertCompileSize(struct tcpiphdr, 40);
+#define ti_next ti_i.ih_next
+#define ti_prev ti_i.ih_prev
+#define ti_x1 ti_i.ih_x1
+#define ti_pr ti_i.ih_pr
+#define ti_len ti_i.ih_len
+#define ti_src ti_i.ih_src
+#define ti_dst ti_i.ih_dst
+#define ti_sport ti_t.th_sport
+#define ti_dport ti_t.th_dport
+#define ti_seq ti_t.th_seq
+#define ti_ack ti_t.th_ack
+#define ti_x2 ti_t.th_x2
+#define ti_off ti_t.th_off
+#define ti_flags ti_t.th_flags
+#define ti_win ti_t.th_win
+#define ti_sum ti_t.th_sum
+#define ti_urp ti_t.th_urp
+
+/*
+ * Just a clean way to get to the first byte
+ * of the packet
+ */
+struct tcpiphdr_2
+{
+ struct tcpiphdr dummy;
+ char first_char;
+};
+
+#endif
diff --git a/src/VBox/Devices/Network/slirp/tftp.c b/src/VBox/Devices/Network/slirp/tftp.c
new file mode 100644
index 00000000..6bf4c5b7
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/tftp.c
@@ -0,0 +1,931 @@
+/* $Id: tftp.c $ */
+/** @file
+ * NAT - TFTP server.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * tftp.c - a simple, read-only tftp server for qemu
+ *
+ * Copyright (c) 2004 Magnus Damm <damm@opensource.se>
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
+ * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+
+#include <slirp.h>
+#include <iprt/file.h>
+#include <iprt/err.h>
+#include <iprt/path.h>
+
+typedef enum ENMTFTPSESSIONFMT
+{
+ TFTPFMT_NONE = 0,
+ TFTPFMT_OCTET,
+ TFTPFMT_NETASCII,
+ TFTPFMT_MAIL,
+ TFTPFMT_NOT_FMT = 0xffff
+} ENMTFTPSESSIONFMT;
+
+typedef struct TFPTPSESSIONOPTDESC
+{
+ int fRequested;
+ uint64_t u64Value;
+} TFPTPSESSIONOPTDESC, *PTFPTPSESSIONOPTDESC;
+
+typedef struct TFTPSESSION
+{
+ int fInUse;
+ struct in_addr IpClientAddress;
+ uint16_t u16ClientPort;
+ int iTimestamp;
+ uint64_t cbTransfered;
+ uint16_t cTftpAck;
+ ENMTFTPSESSIONFMT enmTftpFmt;
+ TFPTPSESSIONOPTDESC OptionBlkSize;
+ TFPTPSESSIONOPTDESC OptionTSize;
+ TFPTPSESSIONOPTDESC OptionTimeout;
+
+ const char *pcszFilenameHost;
+ char szFilename[TFTP_FILENAME_MAX];
+} TFTPSESSION, *PTFTPSESSION, **PPTFTPSESSION;
+
+#pragma pack(1)
+typedef struct TFTPCOREHDR
+{
+ uint16_t u16TftpOpCode;
+ /* Data lays here (might be raw uint8_t* or header of payload ) */
+} TFTPCOREHDR, *PTFTPCOREHDR;
+
+typedef struct TFTPIPHDR
+{
+ struct ip IPv4Hdr;
+ struct udphdr UdpHdr;
+ uint16_t u16TftpOpType;
+ TFTPCOREHDR Core;
+ /* Data lays here */
+} TFTPIPHDR, *PTFTPIPHDR;
+#pragma pack()
+
+typedef const PTFTPIPHDR PCTFTPIPHDR;
+
+typedef const PTFTPSESSION PCTFTPSESSION;
+
+
+typedef struct TFTPOPTIONDESC
+{
+ const char *pszName;
+ ENMTFTPSESSIONFMT enmType;
+ int cbName;
+ bool fHasValue;
+} TFTPOPTIONDESC, *PTFTPOPTIONDESC;
+
+typedef const PTFTPOPTIONDESC PCTFTPOPTIONDESC;
+static TFTPOPTIONDESC g_TftpTransferFmtDesc[] =
+{
+ {"octet", TFTPFMT_OCTET, 5, false}, /* RFC1350 */
+ {"netascii", TFTPFMT_NETASCII, 8, false}, /* RFC1350 */
+ {"mail", TFTPFMT_MAIL, 4, false}, /* RFC1350 */
+};
+
+static TFTPOPTIONDESC g_TftpDesc[] =
+{
+ {"blksize", TFTPFMT_NOT_FMT, 7, true}, /* RFC2348 */
+ {"timeout", TFTPFMT_NOT_FMT, 7, true}, /* RFC2349 */
+ {"tsize", TFTPFMT_NOT_FMT, 5, true}, /* RFC2349 */
+ {"size", TFTPFMT_NOT_FMT, 4, true}, /* RFC2349 */
+};
+
+
+DECLINLINE(struct mbuf *) slirpTftpMbufAlloc(PNATState pData)
+{
+ struct mbuf *m = slirpServiceMbufAlloc(pData, CTL_TFTP);
+ if (RT_UNLIKELY(m == NULL))
+ LogFlowFunc(("LEAVE: Can't allocate mbuf\n"));
+ return m;
+}
+
+
+/**
+ * This function resolves file name relative to tftp prefix.
+ * @param pData
+ * @param pTftpSession
+ */
+DECLINLINE(int) tftpSecurityFilenameCheck(PNATState pData, PTFTPSESSION pTftpSession)
+{
+ int rc = VERR_FILE_NOT_FOUND; /* guilty until proved innocent */
+
+ AssertPtrReturn(pTftpSession, VERR_INVALID_PARAMETER);
+ AssertReturn(pTftpSession->pcszFilenameHost == NULL, VERR_INVALID_PARAMETER);
+
+ /* prefix must be set to an absolute pathname. assert? */
+ if (tftp_prefix == NULL || RTPathSkipRootSpec(tftp_prefix) == tftp_prefix)
+ goto done;
+
+ /* replace backslashes with forward slashes */
+ char *s = pTftpSession->szFilename;
+ while ((s = strchr(s, '\\')) != NULL)
+ *s++ = '/';
+
+ /* deny dot-dot by itself or at the beginning */
+ if ( pTftpSession->szFilename[0] == '.'
+ && pTftpSession->szFilename[1] == '.'
+ && ( pTftpSession->szFilename[2] == '\0'
+ || pTftpSession->szFilename[2] == '/'))
+ goto done;
+
+ /* deny dot-dot in the middle */
+ if (RTStrStr(pTftpSession->szFilename, "/../") != NULL)
+ goto done;
+
+ /* deny dot-dot at the end (there's no RTStrEndsWith) */
+ const char *dotdot = RTStrStr(pTftpSession->szFilename, "/..");
+ if (dotdot != NULL && dotdot[3] == '\0')
+ goto done;
+
+ char *pszPathHostAbs;
+ int cbLen = RTStrAPrintf(&pszPathHostAbs, "%s/%s",
+ tftp_prefix, pTftpSession->szFilename);
+ if (cbLen == -1)
+ goto done;
+
+ LogRel2(("NAT: TFTP: %s\n", pszPathHostAbs));
+ pTftpSession->pcszFilenameHost = pszPathHostAbs;
+ rc = VINF_SUCCESS;
+
+ done:
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+/*
+ * This function returns index of option descriptor in passed descriptor array
+ * @param piIdxOpt returned index value
+ * @param paTftpDesc array of known Tftp descriptors
+ * @param caTftpDesc size of array of tftp descriptors
+ * @param pszOpt name of option
+ */
+DECLINLINE(int) tftpFindDesciptorIndexByName(int *piIdxOpt, PCTFTPOPTIONDESC paTftpDesc, int caTftpDesc, const char *pszOptName)
+{
+ int rc = VINF_SUCCESS;
+ int idxOption = 0;
+ AssertReturn(piIdxOpt, VERR_INVALID_PARAMETER);
+ AssertReturn(paTftpDesc, VERR_INVALID_PARAMETER);
+ AssertReturn(pszOptName, VERR_INVALID_PARAMETER);
+ for (idxOption = 0; idxOption < caTftpDesc; ++idxOption)
+ {
+ if (!RTStrNICmp(pszOptName, paTftpDesc[idxOption].pszName, 10))
+ {
+ *piIdxOpt = idxOption;
+ return rc;
+ }
+ }
+ rc = VERR_NOT_FOUND;
+ return rc;
+}
+
+/**
+ * Helper function to look for index of descriptor in transfer format descriptors
+ * @param piIdxOpt returned value of index
+ * @param pszOpt name of option
+ */
+DECLINLINE(int) tftpFindTransferFormatIdxbyName(int *piIdxOpt, const char *pszOpt)
+{
+ return tftpFindDesciptorIndexByName(piIdxOpt, &g_TftpTransferFmtDesc[0], RT_ELEMENTS(g_TftpTransferFmtDesc), pszOpt);
+}
+
+/**
+ * Helper function to look for index of descriptor in options descriptors
+ * @param piIdxOpt returned value of index
+ * @param pszOpt name of option
+ */
+DECLINLINE(int) tftpFindOptionIdxbyName(int *piIdxOpt, const char *pszOpt)
+{
+ return tftpFindDesciptorIndexByName(piIdxOpt, &g_TftpDesc[0], RT_ELEMENTS(g_TftpDesc), pszOpt);
+}
+
+
+#if 0 /* unused */
+DECLINLINE(bool) tftpIsAcceptableOption(const char *pszOptionName)
+{
+ int idxOptDesc = 0;
+ AssertPtrReturn(pszOptionName, false);
+ AssertReturn(RTStrNLen(pszOptionName,10) >= 4, false);
+ AssertReturn(RTStrNLen(pszOptionName,10) < 8, false);
+ for(idxOptDesc = 0; idxOptDesc < RT_ELEMENTS(g_TftpTransferFmtDesc); ++idxOptDesc)
+ {
+ if (!RTStrNICmp(pszOptionName, g_TftpTransferFmtDesc[idxOptDesc].pszName, 10))
+ return true;
+ }
+ for(idxOptDesc = 0; idxOptDesc < RT_ELEMENTS(g_TftpDesc); ++idxOptDesc)
+ {
+ if (!RTStrNICmp(pszOptionName, g_TftpDesc[idxOptDesc].pszName, 10))
+ return true;
+ }
+ return false;
+}
+#endif /* unused */
+
+
+/**
+ * This helper function that validate if client want to operate in supported by server mode.
+ * @param pcTftpHeader comulative header (IP, UDP, TFTP)
+ * @param pcu8Options pointer to the options supposing that pointer points at the mode option
+ * @param cbOptions size of the options buffer
+ */
+DECLINLINE(int) tftpIsSupportedTransferMode(PCTFTPSESSION pcTftpSession)
+{
+ AssertPtrReturn(pcTftpSession, 0);
+ return (pcTftpSession->enmTftpFmt == TFTPFMT_OCTET);
+}
+
+
+DECLINLINE(void) tftpSessionUpdate(PNATState pData, PTFTPSESSION pTftpSession)
+{
+ pTftpSession->iTimestamp = curtime;
+ pTftpSession->fInUse = 1;
+}
+
+DECLINLINE(void) tftpSessionTerminate(PTFTPSESSION pTftpSession)
+{
+ if (pTftpSession->pcszFilenameHost != NULL)
+ {
+ RTStrFree((char *)pTftpSession->pcszFilenameHost);
+ pTftpSession->pcszFilenameHost = NULL;
+ }
+
+ pTftpSession->fInUse = 0;
+}
+
+DECLINLINE(int) tftpSessionParseAndMarkOption(const char *pcszRawOption, PTFPTPSESSIONOPTDESC pTftpSessionOption)
+{
+ int rc = VINF_SUCCESS;
+ rc = RTStrToInt64Full(pcszRawOption, 0, (int64_t *)&pTftpSessionOption->u64Value);
+ AssertRCReturn(rc, rc);
+ pTftpSessionOption->fRequested = 1;
+ return rc;
+}
+
+DECLINLINE(int) tftpSessionOptionParse(PTFTPSESSION pTftpSession, PCTFTPIPHDR pcTftpIpHeader)
+{
+ int rc = VINF_SUCCESS;
+ char *pszTftpRRQRaw;
+ size_t idxTftpRRQRaw = 0;
+ ssize_t cbTftpRRQRaw = 0;
+ int fWithArg = 0;
+ int idxOptionArg = 0;
+
+ AssertPtrReturn(pTftpSession, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcTftpIpHeader, VERR_INVALID_PARAMETER);
+ AssertReturn(RT_N2H_U16(pcTftpIpHeader->u16TftpOpType) == TFTP_RRQ, VERR_INVALID_PARAMETER);
+ LogFlowFunc(("pTftpSession:%p, pcTftpIpHeader:%p\n", pTftpSession, pcTftpIpHeader));
+
+ pszTftpRRQRaw = (char *)&pcTftpIpHeader->Core;
+ cbTftpRRQRaw = RT_H2N_U16(pcTftpIpHeader->UdpHdr.uh_ulen) + sizeof(struct ip) - RT_UOFFSETOF(TFTPIPHDR, Core);
+ while (cbTftpRRQRaw)
+ {
+ rc = RTStrNLenEx(pszTftpRRQRaw, cbTftpRRQRaw, &idxTftpRRQRaw);
+ if (RT_SUCCESS(rc))
+ ++idxTftpRRQRaw; /* count the NUL too */
+ else
+ break;
+
+ if (RTStrNLen(pTftpSession->szFilename, TFTP_FILENAME_MAX) == 0)
+ {
+ rc = RTStrCopy(pTftpSession->szFilename, TFTP_FILENAME_MAX, pszTftpRRQRaw);
+ if (RT_FAILURE(rc))
+ {
+ LogFlowFuncLeaveRC(rc);
+ AssertRCReturn(rc,rc);
+ }
+ }
+ else if (pTftpSession->enmTftpFmt == TFTPFMT_NONE)
+ {
+ int idxFmt = 0;
+ rc = tftpFindTransferFormatIdxbyName(&idxFmt, pszTftpRRQRaw);
+ if (RT_FAILURE(rc))
+ {
+ LogFlowFuncLeaveRC(VERR_INTERNAL_ERROR);
+ return VERR_INTERNAL_ERROR;
+ }
+ AssertReturn( g_TftpTransferFmtDesc[idxFmt].enmType != TFTPFMT_NONE
+ && g_TftpTransferFmtDesc[idxFmt].enmType != TFTPFMT_NOT_FMT, VERR_INTERNAL_ERROR);
+ pTftpSession->enmTftpFmt = g_TftpTransferFmtDesc[idxFmt].enmType;
+ }
+ else if (fWithArg)
+ {
+ if (!RTStrICmp("blksize", g_TftpDesc[idxOptionArg].pszName))
+ rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionBlkSize);
+
+ if ( RT_SUCCESS(rc)
+ && !RTStrICmp("tsize", g_TftpDesc[idxOptionArg].pszName))
+ rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionTSize);
+
+ /** @todo we don't use timeout, but its value in the range 0-255 */
+ if ( RT_SUCCESS(rc)
+ && !RTStrICmp("timeout", g_TftpDesc[idxOptionArg].pszName))
+ rc = tftpSessionParseAndMarkOption(pszTftpRRQRaw, &pTftpSession->OptionTimeout);
+
+ /** @todo unknown option detection */
+ if (RT_FAILURE(rc))
+ {
+ LogFlowFuncLeaveRC(rc);
+ AssertRCReturn(rc,rc);
+ }
+ fWithArg = 0;
+ idxOptionArg = 0;
+ }
+ else
+ {
+ rc = tftpFindOptionIdxbyName(&idxOptionArg, pszTftpRRQRaw);
+ if (RT_SUCCESS(rc))
+ fWithArg = 1;
+ else
+ {
+ LogFlowFuncLeaveRC(rc);
+ AssertRCReturn(rc,rc);
+ }
+ }
+ pszTftpRRQRaw += idxTftpRRQRaw;
+ cbTftpRRQRaw -= idxTftpRRQRaw;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+static int tftpAllocateSession(PNATState pData, PCTFTPIPHDR pcTftpIpHeader, PPTFTPSESSION ppTftpSession)
+{
+ PTFTPSESSION pTftpSession = NULL;
+ int rc = VINF_SUCCESS;
+ int idxSession;
+ AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcTftpIpHeader, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(ppTftpSession, VERR_INVALID_PARAMETER);
+
+ for (idxSession = 0; idxSession < TFTP_SESSIONS_MAX; idxSession++)
+ {
+ pTftpSession = &((PTFTPSESSION)pData->pvTftpSessions)[idxSession];
+
+ if (!pTftpSession->fInUse)
+ goto found;
+
+ /* sessions time out after 5 inactive seconds */
+ if ((int)(curtime - pTftpSession->iTimestamp) > 5000)
+ goto found;
+ }
+
+ return VERR_NOT_FOUND;
+
+ found:
+ if (pTftpSession->pcszFilenameHost != NULL)
+ {
+ RTStrFree((char *)pTftpSession->pcszFilenameHost);
+ // pTftpSession->pcszFilenameHost = NULL; /* will be zeroed out below */
+ }
+ RT_ZERO(*pTftpSession);
+
+ memcpy(&pTftpSession->IpClientAddress, &pcTftpIpHeader->IPv4Hdr.ip_src, sizeof(pTftpSession->IpClientAddress));
+ pTftpSession->u16ClientPort = pcTftpIpHeader->UdpHdr.uh_sport;
+ rc = tftpSessionOptionParse(pTftpSession, pcTftpIpHeader);
+ AssertRCReturn(rc, VERR_INTERNAL_ERROR);
+ *ppTftpSession = pTftpSession;
+
+ LogRel(("NAT: TFTP RRQ %s", pTftpSession->szFilename));
+ const char *pszPrefix = " ";
+ if (pTftpSession->OptionBlkSize.fRequested)
+ {
+ LogRel(("%s" "blksize=%RU64", pszPrefix, pTftpSession->OptionBlkSize.u64Value));
+ pszPrefix = ", ";
+ }
+ if (pTftpSession->OptionTSize.fRequested)
+ {
+ LogRel(("%s" "tsize=%RU64", pszPrefix, pTftpSession->OptionTSize.u64Value));
+ pszPrefix = ", ";
+ }
+ if (pTftpSession->OptionTimeout.fRequested)
+ {
+ LogRel(("%s" "timeout=%RU64", pszPrefix, pTftpSession->OptionTimeout.u64Value));
+ pszPrefix = ", ";
+ }
+ LogRel(("\n"));
+
+ tftpSessionUpdate(pData, pTftpSession);
+
+ return VINF_SUCCESS;
+}
+
+static int tftpSessionFind(PNATState pData, PCTFTPIPHDR pcTftpIpHeader, PPTFTPSESSION ppTftpSessions)
+{
+ PTFTPSESSION pTftpSession;
+ int idxTftpSession;
+ AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcTftpIpHeader, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(ppTftpSessions, VERR_INVALID_PARAMETER);
+
+ for (idxTftpSession = 0; idxTftpSession < TFTP_SESSIONS_MAX; idxTftpSession++)
+ {
+ pTftpSession = &((PTFTPSESSION)pData->pvTftpSessions)[idxTftpSession];
+
+ if (pTftpSession->fInUse)
+ {
+ if (!memcmp(&pTftpSession->IpClientAddress, &pcTftpIpHeader->IPv4Hdr.ip_src, sizeof(pTftpSession->IpClientAddress)))
+ {
+ if (pTftpSession->u16ClientPort == pcTftpIpHeader->UdpHdr.uh_sport)
+ {
+ *ppTftpSessions = pTftpSession;
+ return VINF_SUCCESS;
+ }
+ }
+ }
+ }
+
+ return VERR_NOT_FOUND;
+}
+
+DECLINLINE(int) pftpSessionOpenFile(PTFTPSESSION pTftpSession, PRTFILE pSessionFile)
+{
+ int rc;
+ LogFlowFuncEnter();
+
+ if (pTftpSession->pcszFilenameHost == NULL)
+ {
+ rc = VERR_FILE_NOT_FOUND;
+ }
+ else
+ {
+ rc = RTFileOpen(pSessionFile, pTftpSession->pcszFilenameHost,
+ RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_WRITE);
+ if (RT_FAILURE(rc))
+ rc = VERR_FILE_NOT_FOUND;
+ }
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+DECLINLINE(int) tftpSessionEvaluateOptions(PTFTPSESSION pTftpSession)
+{
+ int rc;
+ RTFILE hSessionFile;
+ uint64_t cbSessionFile = 0;
+ int cOptions;
+ LogFlowFunc(("pTftpSession:%p\n", pTftpSession));
+
+ rc = pftpSessionOpenFile(pTftpSession, &hSessionFile);
+ if (RT_FAILURE(rc))
+ {
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+ }
+
+ rc = RTFileQuerySize(hSessionFile, &cbSessionFile);
+ RTFileClose(hSessionFile);
+ if (RT_FAILURE(rc))
+ {
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+ }
+
+ cOptions = 0;
+
+ if (pTftpSession->OptionTSize.fRequested)
+ {
+ pTftpSession->OptionTSize.u64Value = cbSessionFile;
+ ++cOptions;
+ }
+
+ if (pTftpSession->OptionBlkSize.fRequested)
+ {
+ if (pTftpSession->OptionBlkSize.u64Value < 8)
+ {
+ /*
+ * we cannot make a counter-offer larger than the client's
+ * value, so just pretend we didn't recognize it and use
+ * default block size
+ */
+ pTftpSession->OptionBlkSize.fRequested = 0;
+ pTftpSession->OptionBlkSize.u64Value = 512;
+ }
+ else if (pTftpSession->OptionBlkSize.u64Value > 1428)
+ {
+ pTftpSession->OptionBlkSize.u64Value = 1428;
+ ++cOptions;
+ }
+ }
+ else
+ {
+ pTftpSession->OptionBlkSize.u64Value = 512;
+ }
+
+ rc = cOptions > 0 ? VINF_SUCCESS : VWRN_NOT_FOUND;
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+DECLINLINE(int) tftpSend(PNATState pData,
+ PTFTPSESSION pTftpSession,
+ struct mbuf *pMBuf,
+ PCTFTPIPHDR pcTftpIpHeaderRecv)
+{
+ struct sockaddr_in saddr, daddr;
+ int error, rc;
+
+ LogFlowFunc(("pMBuf:%p, pcTftpIpHeaderRecv:%p\n", pMBuf, pcTftpIpHeaderRecv));
+ saddr.sin_addr = pcTftpIpHeaderRecv->IPv4Hdr.ip_dst;
+ saddr.sin_port = pcTftpIpHeaderRecv->UdpHdr.uh_dport;
+
+ daddr.sin_addr = pTftpSession->IpClientAddress;
+ daddr.sin_port = pTftpSession->u16ClientPort;
+
+
+ pMBuf->m_data += sizeof(struct udpiphdr);
+ pMBuf->m_len -= sizeof(struct udpiphdr);
+
+ error = udp_output2(pData, NULL, pMBuf, &saddr, &daddr, IPTOS_LOWDELAY);
+ rc = error ? VERR_GENERAL_FAILURE : VINF_SUCCESS;
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+
+DECLINLINE(int) tftpSendError(PNATState pData, PTFTPSESSION pTftpSession, uint16_t errorcode,
+ const char *msg, PCTFTPIPHDR pcTftpIpHeaderRecv); /* gee wiz */
+
+DECLINLINE(int) tftpReadDataBlock(PNATState pData,
+ PTFTPSESSION pcTftpSession,
+ uint8_t *pu8Data,
+ int *pcbReadData)
+{
+ RTFILE hSessionFile;
+ int rc = VINF_SUCCESS;
+ uint16_t u16BlkSize = 0;
+ AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcTftpSession, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pu8Data, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pcbReadData, VERR_INVALID_PARAMETER);
+ LogFlowFunc(("pcTftpSession:%p, pu8Data:%p, pcbReadData:%p\n",
+ pcTftpSession,
+ pu8Data,
+ pcbReadData));
+
+ u16BlkSize = (uint16_t)pcTftpSession->OptionBlkSize.u64Value;
+ rc = pftpSessionOpenFile(pcTftpSession, &hSessionFile);
+ if (RT_FAILURE(rc))
+ {
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+ }
+
+ if (pcbReadData)
+ {
+ size_t cbRead;
+
+ rc = RTFileSeek(hSessionFile,
+ pcTftpSession->cbTransfered,
+ RTFILE_SEEK_BEGIN,
+ NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTFileClose(hSessionFile);
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+ }
+ rc = RTFileRead(hSessionFile, pu8Data, u16BlkSize, &cbRead);
+ if (RT_FAILURE(rc))
+ {
+ RTFileClose(hSessionFile);
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+ }
+ *pcbReadData = (int)cbRead;
+ }
+
+ rc = RTFileClose(hSessionFile);
+
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+DECLINLINE(int) tftpAddOptionToOACK(PNATState pData, struct mbuf *pMBuf, const char *pszOptName, uint64_t u64OptValue)
+{
+ char szOptionBuffer[256];
+ size_t iOptLength;
+ int rc = VINF_SUCCESS;
+ int cbMBufCurrent = pMBuf->m_len;
+ LogFlowFunc(("pMBuf:%p, pszOptName:%s, u16OptValue:%ld\n", pMBuf, pszOptName, u64OptValue));
+ AssertPtrReturn(pMBuf, VERR_INVALID_PARAMETER);
+ AssertPtrReturn(pszOptName, VERR_INVALID_PARAMETER);
+
+ RT_ZERO(szOptionBuffer);
+ iOptLength = RTStrPrintf(szOptionBuffer, 256 , "%s", pszOptName) + 1;
+ iOptLength += RTStrPrintf(szOptionBuffer + iOptLength, 256 - iOptLength , "%llu", u64OptValue) + 1;
+ if (iOptLength > M_TRAILINGSPACE(pMBuf))
+ rc = VERR_BUFFER_OVERFLOW; /* buffer too small */
+ else
+ {
+ pMBuf->m_len += (int)iOptLength;
+ m_copyback(pData, pMBuf, cbMBufCurrent, (int)iOptLength, szOptionBuffer);
+ }
+ LogFlowFuncLeaveRC(rc);
+ return rc;
+}
+
+
+DECLINLINE(int) tftpSendOACK(PNATState pData,
+ PTFTPSESSION pTftpSession,
+ PCTFTPIPHDR pcTftpIpHeaderRecv)
+{
+ struct mbuf *m;
+ PTFTPIPHDR pTftpIpHeader;
+ int rc;
+
+ rc = tftpSessionEvaluateOptions(pTftpSession);
+ if (RT_FAILURE(rc))
+ {
+ tftpSendError(pData, pTftpSession, TFTP_EACCESS, "Option negotiation failure (file not found or inaccessible?)", pcTftpIpHeaderRecv);
+ LogFlowFuncLeave();
+ return rc;
+ }
+
+ if (rc == VWRN_NOT_FOUND)
+ return rc;
+
+ m = slirpTftpMbufAlloc(pData);
+ if (m == NULL)
+ {
+ tftpSessionTerminate(pTftpSession);
+ return VERR_NO_MEMORY;
+ }
+
+ m->m_data += if_maxlinkhdr;
+ m->m_pkthdr.header = mtod(m, void *);
+ pTftpIpHeader = mtod(m, PTFTPIPHDR);
+ m->m_len = sizeof(TFTPIPHDR) - sizeof(uint16_t); /* no u16TftpOpCode */
+
+ pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_OACK);
+
+ if (pTftpSession->OptionBlkSize.fRequested)
+ rc = tftpAddOptionToOACK(pData, m, "blksize", pTftpSession->OptionBlkSize.u64Value);
+
+ if ( RT_SUCCESS(rc)
+ && pTftpSession->OptionTSize.fRequested)
+ rc = tftpAddOptionToOACK(pData, m, "tsize", pTftpSession->OptionTSize.u64Value);
+
+ rc = tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv);
+ if (RT_FAILURE(rc))
+ tftpSessionTerminate(pTftpSession);
+
+ return rc;
+}
+
+
+DECLINLINE(int) tftpSendError(PNATState pData,
+ PTFTPSESSION pTftpSession,
+ uint16_t errorcode,
+ const char *msg,
+ PCTFTPIPHDR pcTftpIpHeaderRecv)
+{
+ struct mbuf *m = NULL;
+
+ LogFlowFunc(("ENTER: errorcode: %RX16, msg: %s\n", errorcode, msg));
+ m = slirpTftpMbufAlloc(pData);
+ if (m != NULL)
+ {
+ u_int cbMsg = (u_int)strlen(msg) + 1; /* ending zero */
+ PTFTPIPHDR pTftpIpHeader;
+
+ m->m_data += if_maxlinkhdr;
+ m->m_len = sizeof(TFTPIPHDR) + cbMsg;
+ m->m_pkthdr.header = mtod(m, void *);
+ pTftpIpHeader = mtod(m, PTFTPIPHDR);
+
+ pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_ERROR);
+ pTftpIpHeader->Core.u16TftpOpCode = RT_H2N_U16(errorcode);
+
+ m_copyback(pData, m, sizeof(TFTPIPHDR), cbMsg, (c_caddr_t)msg);
+
+ tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv);
+ }
+
+ tftpSessionTerminate(pTftpSession);
+
+ LogFlowFuncLeave();
+ return 0;
+}
+
+
+static int tftpSendData(PNATState pData,
+ PTFTPSESSION pTftpSession,
+ uint16_t u16Block,
+ PCTFTPIPHDR pcTftpIpHeaderRecv)
+{
+ struct mbuf *m;
+ PTFTPIPHDR pTftpIpHeader;
+ int cbRead = 0;
+ int rc = VINF_SUCCESS;
+
+ if (u16Block == pTftpSession->cTftpAck)
+ pTftpSession->cTftpAck++;
+ else
+ {
+ tftpSendError(pData, pTftpSession, TFTP_EEXIST, "ACK is wrong", pcTftpIpHeaderRecv);
+ return -1;
+ }
+
+ m = slirpTftpMbufAlloc(pData);
+ if (!m)
+ return -1;
+
+ m->m_data += if_maxlinkhdr;
+ m->m_pkthdr.header = mtod(m, void *);
+ pTftpIpHeader = mtod(m, PTFTPIPHDR);
+ m->m_len = sizeof(TFTPIPHDR);
+
+ pTftpIpHeader->u16TftpOpType = RT_H2N_U16_C(TFTP_DATA);
+ pTftpIpHeader->Core.u16TftpOpCode = RT_H2N_U16(pTftpSession->cTftpAck);
+
+ if (RT_LIKELY(M_TRAILINGSPACE(m) >= pTftpSession->OptionBlkSize.u64Value))
+ {
+ uint8_t *pu8Data = (uint8_t *)&pTftpIpHeader->Core.u16TftpOpCode + sizeof(uint16_t);
+ rc = tftpReadDataBlock(pData, pTftpSession, pu8Data, &cbRead);
+ }
+ else
+ rc = VERR_BUFFER_OVERFLOW;
+
+ if (RT_SUCCESS(rc))
+ {
+ pTftpSession->cbTransfered += cbRead;
+ m->m_len += cbRead;
+ tftpSend(pData, pTftpSession, m, pcTftpIpHeaderRecv);
+ if (cbRead > 0)
+ tftpSessionUpdate(pData, pTftpSession);
+ else
+ tftpSessionTerminate(pTftpSession);
+ }
+ else
+ {
+ m_freem(pData, m);
+ tftpSendError(pData, pTftpSession, TFTP_ENOENT, "File not found", pcTftpIpHeaderRecv);
+ /* send "file not found" error back */
+ return -1;
+ }
+
+ return 0;
+}
+
+DECLINLINE(void) tftpProcessRRQ(PNATState pData, PCTFTPIPHDR pTftpIpHeader, int pktlen)
+{
+ PTFTPSESSION pTftpSession = NULL;
+ uint8_t *pu8Payload = NULL;
+ int cbPayload = 0;
+ size_t cbFileName = 0;
+ int rc = VINF_SUCCESS;
+
+ AssertPtrReturnVoid(pTftpIpHeader);
+ AssertPtrReturnVoid(pData);
+ AssertReturnVoid(pktlen > sizeof(TFTPIPHDR));
+ LogFlowFunc(("ENTER: pTftpIpHeader:%p, pktlen:%d\n", pTftpIpHeader, pktlen));
+
+ rc = tftpAllocateSession(pData, pTftpIpHeader, &pTftpSession);
+ if ( RT_FAILURE(rc)
+ || pTftpSession == NULL)
+ {
+ LogFlowFuncLeave();
+ return;
+ }
+
+ pu8Payload = (uint8_t *)&pTftpIpHeader->Core;
+ cbPayload = pktlen - sizeof(TFTPIPHDR);
+
+ cbFileName = RTStrNLen((char *)pu8Payload, cbPayload);
+ /* We assume that file name should finish with '\0' and shouldn't bigger
+ * than buffer for name storage.
+ */
+ AssertReturnVoid( (ssize_t)cbFileName < cbPayload
+ && cbFileName < TFTP_FILENAME_MAX /* current limit in tftp session handle */
+ && cbFileName);
+
+ /* Dont't bother with rest processing in case of invalid access */
+ if (RT_FAILURE(tftpSecurityFilenameCheck(pData, pTftpSession)))
+ {
+ tftpSendError(pData, pTftpSession, TFTP_EACCESS, "Access violation", pTftpIpHeader);
+ LogFlowFuncLeave();
+ return;
+ }
+
+
+
+ if (RT_UNLIKELY(!tftpIsSupportedTransferMode(pTftpSession)))
+ {
+ tftpSendError(pData, pTftpSession, TFTP_ENOSYS, "Unsupported transfer mode", pTftpIpHeader);
+ LogFlowFuncLeave();
+ return;
+ }
+
+
+ rc = tftpSendOACK(pData, pTftpSession, pTftpIpHeader);
+ if (rc == VWRN_NOT_FOUND)
+ rc = tftpSendData(pData, pTftpSession, 0, pTftpIpHeader);
+
+ LogFlowFuncLeave();
+ return;
+}
+
+static void tftpProcessACK(PNATState pData, PTFTPIPHDR pTftpIpHeader)
+{
+ int rc;
+ PTFTPSESSION pTftpSession = NULL;
+
+ rc = tftpSessionFind(pData, pTftpIpHeader, &pTftpSession);
+ if (RT_FAILURE(rc))
+ return;
+
+ if (tftpSendData(pData, pTftpSession,
+ RT_N2H_U16(pTftpIpHeader->Core.u16TftpOpCode),
+ pTftpIpHeader))
+ LogRel(("NAT: TFTP send failed\n"));
+}
+
+int slirpTftpInit(PNATState pData)
+{
+ AssertPtrReturn(pData, VERR_INVALID_PARAMETER);
+ pData->pvTftpSessions = RTMemAllocZ(sizeof(TFTPSESSION) * TFTP_SESSIONS_MAX);
+ AssertPtrReturn(pData->pvTftpSessions, VERR_NO_MEMORY);
+ return VINF_SUCCESS;
+}
+
+void slirpTftpTerm(PNATState pData)
+{
+ RTMemFree(pData->pvTftpSessions);
+}
+
+int slirpTftpInput(PNATState pData, struct mbuf *pMbuf)
+{
+ PTFTPIPHDR pTftpIpHeader = NULL;
+ AssertPtr(pData);
+ AssertPtr(pMbuf);
+ pTftpIpHeader = mtod(pMbuf, PTFTPIPHDR);
+
+ switch(RT_N2H_U16(pTftpIpHeader->u16TftpOpType))
+ {
+ case TFTP_RRQ:
+ tftpProcessRRQ(pData, pTftpIpHeader, m_length(pMbuf, NULL));
+ break;
+
+ case TFTP_ACK:
+ tftpProcessACK(pData, pTftpIpHeader);
+ break;
+
+ case TFTP_ERROR:
+ {
+ PTFTPSESSION pTftpSession;
+ int rc = tftpSessionFind(pData, pTftpIpHeader, &pTftpSession);
+ if (RT_SUCCESS(rc))
+ tftpSessionTerminate(pTftpSession);
+ }
+
+ default:;
+ }
+ LogFlowFuncLeaveRC(VINF_SUCCESS);
+ return VINF_SUCCESS;
+}
diff --git a/src/VBox/Devices/Network/slirp/tftp.h b/src/VBox/Devices/Network/slirp/tftp.h
new file mode 100644
index 00000000..5c84bd58
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/tftp.h
@@ -0,0 +1,63 @@
+/* $Id: tftp.h $ */
+/** @file
+ * NAT - TFTP server (declarations/defines).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/* tftp defines */
+
+#ifndef _SLIRP_TFTP_H_
+#define _SLIRP_TFTP_H_
+
+#define TFTP_SESSIONS_MAX 3
+
+#define TFTP_SERVER 69
+
+#define TFTP_RRQ 1
+#define TFTP_WRQ 2
+#define TFTP_DATA 3
+#define TFTP_ACK 4
+#define TFTP_ERROR 5
+#define TFTP_OACK 6
+
+/* error codes */
+#define TFTP_EUNDEF 0 /* Not defined, see error message (if any). */
+#define TFTP_ENOENT 1 /* File not found. */
+#define TFTP_EACCESS 2 /* Access violation. */
+#define TFTP_EFBIG 3 /* Disk full or allocation exceeded. */
+#define TFTP_ENOSYS 4 /* Illegal TFTP operation. */
+#define TFTP_ESRCH 5 /* Unknown transfer ID. */
+#define TFTP_EEXIST 6 /* File already exists. */
+#define TFTP_EUSER 7 /* No such user. */
+/* RFC 2347 */
+#define TFTP_EONAK 8 /* Option refused. */
+
+
+#define TFTP_FILENAME_MAX 512
+
+
+int slirpTftpInput(PNATState pData, struct mbuf *m);
+int slirpTftpInit(PNATState pData);
+void slirpTftpTerm(PNATState pData);
+#endif
diff --git a/src/VBox/Devices/Network/slirp/udp.c b/src/VBox/Devices/Network/slirp/udp.c
new file mode 100644
index 00000000..ebd9c20e
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/udp.c
@@ -0,0 +1,685 @@
+/* $Id: udp.c $ */
+/** @file
+ * NAT - UDP protocol.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1982, 1986, 1988, 1990, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)udp_usrreq.c 8.4 (Berkeley) 1/21/94
+ * udp_usrreq.c,v 1.4 1994/10/02 17:48:45 phk Exp
+ */
+
+/*
+ * Changes and additions relating to SLiRP
+ * Copyright (c) 1995 Danny Gasparovski.
+ *
+ * Please read the file COPYRIGHT for the
+ * terms and conditions of the copyright.
+ */
+
+#include <slirp.h>
+#include "ip_icmp.h"
+
+
+/*
+ * UDP protocol implementation.
+ * Per RFC 768, August, 1980.
+ */
+#define udpcksum 1
+
+void
+udp_init(PNATState pData)
+{
+ udp_last_so = &udb;
+ udb.so_next = udb.so_prev = &udb;
+}
+
+/* m->m_data points at ip packet header
+ * m->m_len length ip packet
+ * ip->ip_len length data (IPDU)
+ */
+void
+udp_input(PNATState pData, register struct mbuf *m, int iphlen)
+{
+ register struct ip *ip;
+ register struct udphdr *uh;
+ int len;
+ struct ip save_ip;
+ struct socket *so;
+ int ret;
+ int ttl, tos;
+
+ LogFlowFunc(("ENTER: m = %p, iphlen = %d\n", m, iphlen));
+ ip = mtod(m, struct ip *);
+ Log2(("%RTnaipv4 iphlen = %d\n", ip->ip_dst, iphlen));
+
+ udpstat.udps_ipackets++;
+
+ /*
+ * Strip IP options, if any; should skip this,
+ * make available to user, and use on returned packets,
+ * but we don't yet have a way to check the checksum
+ * with options still present.
+ */
+ if (iphlen > sizeof(struct ip))
+ {
+ ip_stripoptions(m, (struct mbuf *)0);
+ iphlen = sizeof(struct ip);
+ }
+
+ /*
+ * Get IP and UDP header together in first mbuf.
+ */
+ ip = mtod(m, struct ip *);
+ uh = (struct udphdr *)((caddr_t)ip + iphlen);
+
+ /*
+ * Make mbuf data length reflect UDP length.
+ * If not enough data to reflect UDP length, drop.
+ */
+ len = RT_N2H_U16((u_int16_t)uh->uh_ulen);
+ Assert(ip->ip_len + iphlen == (ssize_t)m_length(m, NULL));
+
+ if (ip->ip_len != len)
+ {
+ if (len > ip->ip_len)
+ {
+ udpstat.udps_badlen++;
+ Log3(("NAT: IP(id: %hd) has bad size\n", ip->ip_id));
+ goto bad_free_mbuf;
+ }
+ m_adj(m, len - ip->ip_len);
+ ip->ip_len = len;
+ }
+
+ /*
+ * Save a copy of the IP header in case we want restore it
+ * for sending an ICMP error message in response.
+ */
+ save_ip = *ip;
+ save_ip.ip_len+= iphlen; /* tcp_input subtracts this */
+
+ /*
+ * Checksum extended UDP header and data.
+ */
+ if (udpcksum && uh->uh_sum)
+ {
+ memset(((struct ipovly *)ip)->ih_x1, 0, 9);
+ ((struct ipovly *)ip)->ih_len = uh->uh_ulen;
+#if 0
+ /* keep uh_sum for ICMP reply */
+ uh->uh_sum = cksum(m, len + sizeof (struct ip));
+ if (uh->uh_sum)
+ {
+
+#endif
+ if (cksum(m, len + iphlen))
+ {
+ udpstat.udps_badsum++;
+ Log3(("NAT: IP(id: %hd) has bad (udp) cksum\n", ip->ip_id));
+ goto bad_free_mbuf;
+ }
+ }
+#if 0
+ }
+#endif
+
+ /*
+ * handle DHCP/BOOTP
+ */
+ if (uh->uh_dport == RT_H2N_U16_C(BOOTP_SERVER))
+ {
+ bootp_input(pData, m);
+ goto done_free_mbuf;
+ }
+
+ LogFunc(("uh src: %RTnaipv4:%d, dst: %RTnaipv4:%d\n",
+ ip->ip_src.s_addr, RT_N2H_U16(uh->uh_sport),
+ ip->ip_dst.s_addr, RT_N2H_U16(uh->uh_dport)));
+
+ /*
+ * handle DNS host resolver without creating a socket
+ */
+ if ( pData->fUseHostResolver
+ && uh->uh_dport == RT_H2N_U16_C(53)
+ && CTL_CHECK(ip->ip_dst.s_addr, CTL_DNS))
+ {
+ struct sockaddr_in dst, src;
+
+ src.sin_addr.s_addr = ip->ip_dst.s_addr;
+ src.sin_port = uh->uh_dport;
+ dst.sin_addr.s_addr = ip->ip_src.s_addr;
+ dst.sin_port = uh->uh_sport;
+
+ m_adj(m, sizeof(struct udpiphdr));
+
+ m = hostresolver(pData, m, ip->ip_src.s_addr, uh->uh_sport);
+ if (m == NULL)
+ goto done_free_mbuf;
+
+ slirpMbufTagService(pData, m, CTL_DNS);
+
+ udp_output2(pData, NULL, m, &src, &dst, IPTOS_LOWDELAY);
+ LogFlowFuncLeave();
+ return;
+ }
+
+ /*
+ * handle TFTP
+ */
+ if ( uh->uh_dport == RT_H2N_U16_C(TFTP_SERVER)
+ && CTL_CHECK(ip->ip_dst.s_addr, CTL_TFTP))
+ {
+ if (pData->pvTftpSessions)
+ slirpTftpInput(pData, m);
+ goto done_free_mbuf;
+ }
+
+ /*
+ * XXX: DNS proxy currently relies on the fact that each socket
+ * only serves one request.
+ */
+ if ( pData->fUseDnsProxy
+ && CTL_CHECK(ip->ip_dst.s_addr, CTL_DNS)
+ && (uh->uh_dport == RT_H2N_U16_C(53)))
+ {
+ so = NULL;
+ goto new_socket;
+ }
+
+ /*
+ * Drop UDP packets destind for CTL_ALIAS (i.e. the hosts loopback interface)
+ * if it is disabled.
+ */
+ if ( CTL_CHECK(ip->ip_dst.s_addr, CTL_ALIAS)
+ && !pData->fLocalhostReachable)
+ goto done_free_mbuf;
+
+ /*
+ * Locate pcb for datagram.
+ */
+ so = udp_last_so;
+ if ( so->so_lport != uh->uh_sport
+ || so->so_laddr.s_addr != ip->ip_src.s_addr)
+ {
+ struct socket *tmp;
+
+ for (tmp = udb.so_next; tmp != &udb; tmp = tmp->so_next)
+ {
+ if ( tmp->so_lport == uh->uh_sport
+ && tmp->so_laddr.s_addr == ip->ip_src.s_addr)
+ {
+ so = tmp;
+ break;
+ }
+ }
+ if (tmp == &udb)
+ so = NULL;
+ else
+ {
+ udpstat.udpps_pcbcachemiss++;
+ udp_last_so = so;
+ }
+ }
+
+ new_socket:
+ if (so == NULL)
+ {
+ /*
+ * If there's no socket for this packet,
+ * create one
+ */
+ if ((so = socreate()) == NULL)
+ {
+ Log2(("NAT: IP(id: %hd) failed to create socket\n", ip->ip_id));
+ goto bad_free_mbuf;
+ }
+
+ /*
+ * Setup fields
+ */
+ so->so_laddr = ip->ip_src;
+ so->so_lport = uh->uh_sport;
+ so->so_iptos = ip->ip_tos;
+
+ if (udp_attach(pData, so) <= 0)
+ {
+ Log2(("NAT: IP(id: %hd) udp_attach errno = %d (%s)\n",
+ ip->ip_id, errno, strerror(errno)));
+ sofree(pData, so);
+ goto bad_free_mbuf;
+ }
+
+ /* udp_last_so = so; */
+ /*
+ * XXXXX Here, check if it's in udpexec_list,
+ * and if it is, do the fork_exec() etc.
+ */
+ }
+
+ so->so_faddr = ip->ip_dst; /* XXX */
+ so->so_fport = uh->uh_dport; /* XXX */
+ Assert(so->so_type == IPPROTO_UDP);
+
+ /*
+ * DNS proxy
+ */
+ if ( pData->fUseDnsProxy
+ && CTL_CHECK(ip->ip_dst.s_addr, CTL_DNS)
+ && (uh->uh_dport == RT_H2N_U16_C(53)))
+ {
+ dnsproxy_query(pData, so, m, iphlen);
+ goto done_free_mbuf;
+ }
+
+ iphlen += sizeof(struct udphdr);
+ m->m_len -= iphlen;
+ m->m_data += iphlen;
+
+ ttl = ip->ip_ttl = save_ip.ip_ttl;
+ if (ttl != so->so_sottl) {
+ ret = setsockopt(so->s, IPPROTO_IP, IP_TTL,
+ (char *)&ttl, sizeof(ttl));
+ if (RT_LIKELY(ret == 0))
+ so->so_sottl = ttl;
+ }
+
+ tos = save_ip.ip_tos;
+ if (tos != so->so_sotos) {
+ ret = setsockopt(so->s, IPPROTO_IP, IP_TOS,
+ (char *)&tos, sizeof(tos));
+ if (RT_LIKELY(ret == 0))
+ so->so_sotos = tos;
+ }
+
+ {
+ /*
+ * Different OSes have different socket options for DF. We
+ * can't use IP_HDRINCL here as it's only valid for SOCK_RAW.
+ */
+# define USE_DF_OPTION(_Optname) \
+ const int dfopt = _Optname
+#if defined(IP_MTU_DISCOVER)
+ USE_DF_OPTION(IP_MTU_DISCOVER);
+#elif defined(IP_DONTFRAG) /* Solaris 11+, FreeBSD */
+ USE_DF_OPTION(IP_DONTFRAG);
+#elif defined(IP_DONTFRAGMENT) /* Windows */
+ USE_DF_OPTION(IP_DONTFRAGMENT);
+#else
+ USE_DF_OPTION(0);
+#endif
+ if (dfopt) {
+ int df = (save_ip.ip_off & IP_DF) != 0;
+#if defined(IP_MTU_DISCOVER)
+ df = df ? IP_PMTUDISC_DO : IP_PMTUDISC_DONT;
+#endif
+ if (df != so->so_sodf) {
+ ret = setsockopt(so->s, IPPROTO_IP, dfopt,
+ (char *)&df, sizeof(df));
+ if (RT_LIKELY(ret == 0))
+ so->so_sodf = df;
+ }
+ }
+ }
+
+ if ( sosendto(pData, so, m) == -1
+ && ( !soIgnorableErrorCode(errno)
+ && errno != ENOTCONN))
+ {
+ m->m_len += iphlen;
+ m->m_data -= iphlen;
+ *ip = save_ip;
+ Log2(("NAT: UDP tx errno = %d (%s) on sent to %RTnaipv4\n",
+ errno, strerror(errno), ip->ip_dst));
+ icmp_error(pData, m, ICMP_UNREACH, ICMP_UNREACH_NET, 0, strerror(errno));
+ so->so_m = NULL;
+ LogFlowFuncLeave();
+ return;
+ }
+
+ if (so->so_m)
+ m_freem(pData, so->so_m); /* used for ICMP if error on sorecvfrom */
+
+ /* restore the orig mbuf packet */
+ m->m_len += iphlen;
+ m->m_data -= iphlen;
+ *ip = save_ip;
+ so->so_m = m; /* ICMP backup */
+ LogFlowFuncLeave();
+ return;
+
+bad_free_mbuf:
+ Log2(("NAT: UDP(id: %hd) datagram to %RTnaipv4 with size(%d) claimed as bad\n",
+ ip->ip_id, &ip->ip_dst, ip->ip_len));
+
+done_free_mbuf:
+ /* some services like bootp(built-in), dns(buildt-in) and dhcp don't need sockets
+ * and create new m'buffers to send them to guest, so we'll free their incomming
+ * buffers here.
+ */
+ if (m != NULL)
+ m_freem(pData, m);
+ LogFlowFuncLeave();
+ return;
+}
+
+/**
+ * Output a UDP packet.
+ *
+ * @note This function will finally free m!
+ */
+int udp_output2(PNATState pData, struct socket *so, struct mbuf *m,
+ struct sockaddr_in *saddr, struct sockaddr_in *daddr,
+ int iptos)
+{
+ register struct udpiphdr *ui;
+ int error;
+ int mlen = 0;
+
+ LogFlowFunc(("ENTER: so = %R[natsock], m = %p, saddr = %RTnaipv4, daddr = %RTnaipv4\n",
+ so, m, saddr->sin_addr.s_addr, daddr->sin_addr.s_addr));
+
+ /* in case of built-in service so might be NULL */
+ if (so) Assert(so->so_type == IPPROTO_UDP);
+
+ /*
+ * Adjust for header
+ */
+ m->m_data -= sizeof(struct udpiphdr);
+ m->m_len += sizeof(struct udpiphdr);
+ mlen = m_length(m, NULL);
+
+ /*
+ * Fill in mbuf with extended UDP header
+ * and addresses and length put into network format.
+ */
+ ui = mtod(m, struct udpiphdr *);
+ memset(ui->ui_x1, 0, 9);
+ ui->ui_pr = IPPROTO_UDP;
+ ui->ui_len = RT_H2N_U16((uint16_t)(mlen - sizeof(struct ip)));
+ /* XXXXX Check for from-one-location sockets, or from-any-location sockets */
+ ui->ui_src = saddr->sin_addr;
+ ui->ui_dst = daddr->sin_addr;
+ ui->ui_sport = saddr->sin_port;
+ ui->ui_dport = daddr->sin_port;
+ ui->ui_ulen = ui->ui_len;
+
+ /*
+ * Stuff checksum and output datagram.
+ */
+ ui->ui_sum = 0;
+ if (udpcksum)
+ {
+ if ((ui->ui_sum = cksum(m, /* sizeof (struct udpiphdr) + */ mlen)) == 0)
+ ui->ui_sum = 0xffff;
+ }
+ ((struct ip *)ui)->ip_len = mlen;
+ ((struct ip *)ui)->ip_ttl = ip_defttl;
+ ((struct ip *)ui)->ip_tos = iptos;
+
+ udpstat.udps_opackets++;
+
+ error = ip_output(pData, so, m);
+
+ return error;
+}
+
+/**
+ * @note This function will free m!
+ */
+int udp_output(PNATState pData, struct socket *so, struct mbuf *m,
+ struct sockaddr_in *addr)
+{
+ struct sockaddr_in saddr, daddr;
+
+ Assert(so->so_type == IPPROTO_UDP);
+ LogFlowFunc(("ENTER: so = %R[natsock], m = %p, saddr = %RTnaipv4\n", so, m, addr->sin_addr.s_addr));
+
+ if (so->so_laddr.s_addr == INADDR_ANY)
+ {
+ if (pData->guest_addr_guess.s_addr != INADDR_ANY)
+ {
+ LogRel2(("NAT: port-forward: using %RTnaipv4 for %R[natsock]\n",
+ pData->guest_addr_guess.s_addr, so));
+ so->so_laddr = pData->guest_addr_guess;
+ }
+ else
+ {
+ LogRel2(("NAT: port-forward: guest address unknown for %R[natsock]\n", so));
+ m_freem(pData, m);
+ return 0;
+ }
+ }
+
+ saddr = *addr;
+ if ((so->so_faddr.s_addr & RT_H2N_U32(pData->netmask)) == pData->special_addr.s_addr)
+ {
+ saddr.sin_addr.s_addr = so->so_faddr.s_addr;
+ if (slirpIsWideCasting(pData, so->so_faddr.s_addr))
+ {
+ /**
+ * We haven't got real firewall but have got its submodule libalias.
+ */
+ m->m_flags |= M_SKIP_FIREWALL;
+ /**
+ * udp/137 port is Name Service in NetBIOS protocol. for some reasons Windows guest rejects
+ * accept data from non-aliased server.
+ */
+ if ( (so->so_fport == so->so_lport)
+ && (so->so_fport == RT_H2N_U16(137)))
+ saddr.sin_addr.s_addr = alias_addr.s_addr;
+ else
+ saddr.sin_addr.s_addr = addr->sin_addr.s_addr;
+ so->so_faddr.s_addr = addr->sin_addr.s_addr;
+ }
+ }
+
+ /* Any UDP packet to the loopback address must be translated to be from
+ * the forwarding address, i.e. 10.0.2.2. */
+ if ( (saddr.sin_addr.s_addr & RT_H2N_U32_C(IN_CLASSA_NET))
+ == RT_H2N_U32_C(INADDR_LOOPBACK & IN_CLASSA_NET))
+ saddr.sin_addr.s_addr = alias_addr.s_addr;
+
+ daddr.sin_addr = so->so_laddr;
+ daddr.sin_port = so->so_lport;
+
+ return udp_output2(pData, so, m, &saddr, &daddr, so->so_iptos);
+}
+
+int
+udp_attach(PNATState pData, struct socket *so)
+{
+ struct sockaddr sa_addr;
+ socklen_t socklen = sizeof(struct sockaddr);
+ int status;
+ int opt = 1;
+
+ AssertReturn(so->so_type == 0, -1);
+ so->so_type = IPPROTO_UDP;
+
+ so->s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (so->s == -1)
+ goto error;
+ fd_nonblock(so->s);
+
+ so->so_sottl = 0;
+ so->so_sotos = 0;
+ so->so_sodf = -1;
+
+ status = sobind(pData, so);
+ if (status != 0)
+ return status;
+
+ /* success, insert in queue */
+ so->so_expire = curtime + SO_EXPIRE;
+
+ /* enable broadcast for later use */
+ setsockopt(so->s, SOL_SOCKET, SO_BROADCAST, (const char *)&opt, sizeof(opt));
+
+ status = getsockname(so->s, &sa_addr, &socklen);
+ if (status == 0)
+ {
+ Assert(sa_addr.sa_family == AF_INET);
+ so->so_hlport = ((struct sockaddr_in *)&sa_addr)->sin_port;
+ so->so_hladdr.s_addr = ((struct sockaddr_in *)&sa_addr)->sin_addr.s_addr;
+ }
+
+ SOCKET_LOCK_CREATE(so);
+ QSOCKET_LOCK(udb);
+ insque(pData, so, &udb);
+ NSOCK_INC();
+ QSOCKET_UNLOCK(udb);
+ return so->s;
+error:
+ Log2(("NAT: can't create datagram socket\n"));
+ return -1;
+}
+
+void
+udp_detach(PNATState pData, struct socket *so)
+{
+ if (so != &pData->icmp_socket)
+ {
+ Assert(so->so_type == IPPROTO_UDP);
+ QSOCKET_LOCK(udb);
+ SOCKET_LOCK(so);
+ QSOCKET_UNLOCK(udb);
+ closesocket(so->s);
+ sofree(pData, so);
+ SOCKET_UNLOCK(so);
+ }
+}
+
+struct socket *
+udp_listen(PNATState pData, u_int32_t bind_addr, u_int port, u_int32_t laddr, u_int lport, int flags)
+{
+ struct sockaddr_in addr;
+ struct socket *so;
+ socklen_t addrlen = sizeof(struct sockaddr_in);
+ int opt = 1;
+ LogFlowFunc(("ENTER: bind_addr:%RTnaipv4, port:%d, laddr:%RTnaipv4, lport:%d, flags:%x\n",
+ bind_addr, RT_N2H_U16(port), laddr, RT_N2H_U16(lport), flags));
+
+ if ((so = socreate()) == NULL)
+ {
+ LogFlowFunc(("LEAVE: NULL\n"));
+ return NULL;
+ }
+
+ so->s = socket(AF_INET, SOCK_DGRAM, 0);
+ if (so->s == -1)
+ {
+ LogRel(("NAT: can't create datagram socket\n"));
+ RTMemFree(so);
+ LogFlowFunc(("LEAVE: NULL\n"));
+ return NULL;
+ }
+ so->so_expire = curtime + SO_EXPIRE;
+ so->so_type = IPPROTO_UDP;
+ fd_nonblock(so->s);
+ so->so_sottl = 0;
+ so->so_sotos = 0;
+ so->so_sodf = -1;
+ SOCKET_LOCK_CREATE(so);
+ QSOCKET_LOCK(udb);
+ insque(pData, so, &udb);
+ NSOCK_INC();
+ QSOCKET_UNLOCK(udb);
+
+ memset(&addr, 0, sizeof(addr));
+#ifdef RT_OS_DARWIN
+ addr.sin_len = sizeof(addr);
+#endif
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = bind_addr;
+ addr.sin_port = port;
+
+ if (bind(so->s,(struct sockaddr *)&addr, addrlen) < 0)
+ {
+ LogRel(("NAT: udp bind to %RTnaipv4:%d failed, error %d\n",
+ addr.sin_addr, RT_N2H_U16(port), errno));
+ udp_detach(pData, so);
+ LogFlowFunc(("LEAVE: NULL\n"));
+ return NULL;
+ }
+ setsockopt(so->s, SOL_SOCKET, SO_REUSEADDR,(char *)&opt, sizeof(int));
+/* setsockopt(so->s, SOL_SOCKET, SO_OOBINLINE,(char *)&opt, sizeof(int)); */
+
+ getsockname(so->s,(struct sockaddr *)&addr,&addrlen);
+ so->so_hladdr = addr.sin_addr;
+ so->so_hlport = addr.sin_port;
+
+ /* XXX: wtf are we setting so_faddr/so_fport here? */
+ so->so_fport = addr.sin_port;
+#if 0
+ /* The original check was completely broken, as the commented out
+ * if statement was always true (INADDR_ANY=0). */
+ /** @todo vvl - alias_addr should be set (if required)
+ * later by liabalias module.
+ */
+ if (addr.sin_addr.s_addr == 0 || addr.sin_addr.s_addr == loopback_addr.s_addr)
+ so->so_faddr = alias_addr;
+ else
+#endif
+ so->so_faddr = addr.sin_addr;
+
+ so->so_lport = lport;
+ so->so_laddr.s_addr = laddr;
+ if (flags != SS_FACCEPTONCE)
+ so->so_expire = 0;
+
+ so->so_state = SS_ISFCONNECTED;
+
+ LogFlowFunc(("LEAVE: %R[natsock]\n", so));
+ return so;
+}
diff --git a/src/VBox/Devices/Network/slirp/udp.h b/src/VBox/Devices/Network/slirp/udp.h
new file mode 100644
index 00000000..6e11f0c5
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/udp.h
@@ -0,0 +1,142 @@
+/* $Id: udp.h $ */
+/** @file
+ * NAT - UDP protocol (declarations/defines).
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*
+ * This code is based on:
+ *
+ * Copyright (c) 1982, 1986, 1993
+ * The Regents of the University of California. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce 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 University 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 REGENTS 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 REGENTS 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.
+ *
+ * @(#)udp.h 8.1 (Berkeley) 6/10/93
+ * udp.h,v 1.3 1994/08/21 05:27:41 paul Exp
+ */
+
+#ifndef _UDP_H_
+#define _UDP_H_
+
+#define UDP_TTL 0x60
+#define UDP_UDPDATALEN 16192
+
+extern struct socket *udp_last_so;
+
+/*
+ * Udp protocol header.
+ * Per RFC 768, September, 1981.
+ */
+struct udphdr
+{
+ uint16_t uh_sport; /* source port */
+ uint16_t uh_dport; /* destination port */
+ int16_t uh_ulen; /* udp length */
+ uint16_t uh_sum; /* udp checksum */
+};
+AssertCompileSize(struct udphdr, 8);
+
+/*
+ * UDP kernel structures and variables.
+ */
+struct udpiphdr
+{
+ struct ipovly ui_i; /* overlaid ip structure */
+ struct udphdr ui_u; /* udp header */
+};
+AssertCompileSize(struct udpiphdr, 28);
+#define ui_next ui_i.ih_next
+#define ui_prev ui_i.ih_prev
+#define ui_x1 ui_i.ih_x1
+#define ui_pr ui_i.ih_pr
+#define ui_len ui_i.ih_len
+#define ui_src ui_i.ih_src
+#define ui_dst ui_i.ih_dst
+#define ui_sport ui_u.uh_sport
+#define ui_dport ui_u.uh_dport
+#define ui_ulen ui_u.uh_ulen
+#define ui_sum ui_u.uh_sum
+
+struct udpstat_t
+{
+ /* input statistics: */
+ u_long udps_ipackets; /* total input packets */
+ u_long udps_hdrops; /* packet shorter than header */
+ u_long udps_badsum; /* checksum error */
+ u_long udps_badlen; /* data length larger than packet */
+ u_long udps_noport; /* no socket on port */
+ u_long udps_noportbcast; /* of above, arrived as broadcast */
+ u_long udps_fullsock; /* not delivered, input socket full */
+ u_long udpps_pcbcachemiss; /* input packets missing pcb cache */
+ /* output statistics: */
+ u_long udps_opackets; /* total output packets */
+};
+
+/*
+ * Names for UDP sysctl objects
+ */
+#define UDPCTL_CHECKSUM 1 /* checksum UDP packets */
+#define UDPCTL_MAXID 2
+
+extern struct udpstat udpstat;
+extern struct socket udb;
+struct mbuf;
+
+void udp_init (PNATState);
+void udp_input (PNATState, register struct mbuf *, int);
+int udp_output (PNATState, struct socket *, struct mbuf *, struct sockaddr_in *);
+int udp_attach (PNATState, struct socket *);
+void udp_detach (PNATState, struct socket *);
+u_int8_t udp_tos (struct socket *);
+void udp_emu (PNATState, struct socket *, struct mbuf *);
+struct socket * udp_listen (PNATState, u_int32_t, u_int, u_int32_t, u_int, int);
+int udp_output2(PNATState pData, struct socket *so, struct mbuf *m,
+ struct sockaddr_in *saddr, struct sockaddr_in *daddr,
+ int iptos);
+
+#endif
diff --git a/src/VBox/Devices/Network/slirp/zone.h b/src/VBox/Devices/Network/slirp/zone.h
new file mode 100644
index 00000000..4fabc082
--- /dev/null
+++ b/src/VBox/Devices/Network/slirp/zone.h
@@ -0,0 +1,64 @@
+/* $Id: zone.h $ */
+/** @file
+ * NAT - this file is for sharing zone declaration with emu emulation and logging routines.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef __ZONE_H__
+# define __ZONE_H__
+
+# define ITEM_MAGIC 0xdead0001
+struct item
+{
+ uint32_t magic;
+ uma_zone_t zone;
+ uint32_t ref_count;
+ LIST_ENTRY(item) list;
+};
+
+# define ZONE_MAGIC 0xdead0002
+struct uma_zone
+{
+ uint32_t magic;
+ PNATState pData; /* to minimize changes in the rest of UMA emulation code */
+ RTCRITSECT csZone;
+ const char *name;
+ size_t size; /* item size */
+ ctor_t pfCtor;
+ dtor_t pfDtor;
+ zinit_t pfInit;
+ zfini_t pfFini;
+ uma_alloc_t pfAlloc;
+ uma_free_t pfFree;
+ int max_items;
+ int cur_items;
+ LIST_HEAD(RT_NOTHING, item) used_items;
+ LIST_HEAD(RT_NOTHING, item) free_items;
+ uma_zone_t master_zone;
+ void *area;
+ /** Needs call pfnXmitPending when memory becomes available if @c true.
+ * @remarks Only applies to the master zone (master_zone == NULL) */
+ bool fDoXmitPending;
+};
+#endif
diff --git a/src/VBox/Devices/Network/testcase/CppUnitEmulation.h b/src/VBox/Devices/Network/testcase/CppUnitEmulation.h
new file mode 100644
index 00000000..18131c7f
--- /dev/null
+++ b/src/VBox/Devices/Network/testcase/CppUnitEmulation.h
@@ -0,0 +1,71 @@
+/* $Id: CppUnitEmulation.h $ */
+/** @file
+ * Simple cppunit emulation
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+#ifndef VBOX_INCLUDED_SRC_Network_testcase_CppUnitEmulation_h
+#define VBOX_INCLUDED_SRC_Network_testcase_CppUnitEmulation_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/test.h>
+
+#define CPPUNIT_TEST_SUITE(a_Name) \
+public: \
+ RTEXITCODE run(void) \
+ { \
+ RTTEST hTest = NIL_RTTEST; \
+ RTEXITCODE rcExit = RTTestInitAndCreate(#a_Name, &hTest); \
+ if (rcExit == RTEXITCODE_SUCCESS) \
+ { \
+ RTTestBanner(hTest)
+
+#define CPPUNIT_TEST(a_MethodName) \
+ RTTestISub(#a_MethodName); \
+ setUp(); \
+ a_MethodName(); \
+ tearDown()
+
+#define CPPUNIT_TEST_SUITE_END() \
+ rcExit = RTTestSummaryAndDestroy(hTest); \
+ } \
+ return rcExit; \
+ } \
+ typedef int dummy_end
+
+#define CPPUNIT_FAIL(a_Msg) RTTestIFailed(a_Msg)
+#if __cplusplus+0 < 201100
+# define CPPUNIT_ASSERT_EQUAL(a_Value1, a_Value2) RTTESTI_CHECK((a_Value1) == (a_Value2))
+#else
+# define CPPUNIT_ASSERT_EQUAL(a_Value1, a_Value2) do { \
+ auto const Val1 = (a_Value1); \
+ auto const Val2 = (a_Value2); \
+ if (Val1 != Val2) \
+ RTTestIFailed("%s (%#llx) != %s (%#llx)", #a_Value1, (uint64_t)Val1, #a_Value2, (uint64_t)Val2); \
+ } while (0)
+#endif
+
+#endif /* !VBOX_INCLUDED_SRC_Network_testcase_CppUnitEmulation_h */
diff --git a/src/VBox/Devices/Network/testcase/Makefile.kup b/src/VBox/Devices/Network/testcase/Makefile.kup
new file mode 100644
index 00000000..e69de29b
--- /dev/null
+++ b/src/VBox/Devices/Network/testcase/Makefile.kup
diff --git a/src/VBox/Devices/Network/testcase/tstDevEEPROM.cpp b/src/VBox/Devices/Network/testcase/tstDevEEPROM.cpp
new file mode 100644
index 00000000..148bc7aa
--- /dev/null
+++ b/src/VBox/Devices/Network/testcase/tstDevEEPROM.cpp
@@ -0,0 +1,550 @@
+/* $Id: tstDevEEPROM.cpp $ */
+/** @file
+ * EEPROM 93C46 unit tests.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef USE_CPPUNIT
+# include <cppunit/ui/text/TestRunner.h>
+# include <cppunit/extensions/HelperMacros.h>
+#else
+# include "CppUnitEmulation.h"
+#endif
+#include <VBox/vmm/pdmdev.h>
+#include "../DevEEPROM.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static const uint16_t g_abInitialContent[] =
+{
+ 0x0000, 0x0001, 0x0002, 0x0003, 0x0004, 0x0005, 0x0006, 0x0007,
+ 0x0008, 0x0009, 0x000a, 0x000b, 0x000c, 0x000d, 0x000e, 0x000f,
+ 0x0010, 0x0011, 0x0012, 0x0013, 0x0014, 0x0015, 0x0016, 0x0017,
+ 0x0018, 0x0019, 0x001a, 0x001b, 0x001c, 0x001d, 0x001e, 0x001f,
+ 0x0020, 0x0021, 0x0022, 0x0023, 0x0024, 0x0025, 0x0026, 0x0027,
+ 0x0028, 0x0029, 0x002a, 0x002b, 0x002c, 0x002d, 0x002e, 0x002f,
+ 0x0030, 0x0031, 0x0032, 0x0033, 0x0034, 0x0035, 0x0036, 0x0037,
+ 0x0038, 0x0039, 0x003a, 0x003b, 0x003c, 0x003d, 0x003e, 0x003f
+};
+
+
+/**
+ * Test fixture for 93C46-compatible EEPROM device emulation.
+ */
+class EEPROMTest
+#ifdef USE_CPPUNIT
+ : public CppUnit::TestFixture
+#endif
+{
+ CPPUNIT_TEST_SUITE( tstDevEEPROM );
+
+ CPPUNIT_TEST( testRead );
+ CPPUNIT_TEST( testSequentialRead );
+ CPPUNIT_TEST( testWrite );
+ CPPUNIT_TEST( testWriteAll );
+ CPPUNIT_TEST( testWriteDisabled );
+ CPPUNIT_TEST( testErase );
+ CPPUNIT_TEST( testEraseAll );
+
+ CPPUNIT_TEST_SUITE_END();
+
+private:
+ enum Wires { DO=8, DI=4, CS=2, SK=0x01 };
+ enum OpCodes {
+ READ_OPCODE = 0x6,
+ WRITE_OPCODE = 0x5,
+ ERASE_OPCODE = 0x7,
+ EWDS_OPCODE = 0x10, // erase/write disable
+ WRAL_OPCODE = 0x11, // write all
+ ERAL_OPCODE = 0x12, // erase all
+ EWEN_OPCODE = 0x13 // erase/write enable
+ };
+ enum BitWidths {
+ READ_OPCODE_BITS = 3,
+ WRITE_OPCODE_BITS = 3,
+ ERASE_OPCODE_BITS = 3,
+ EWDS_OPCODE_BITS = 5,
+ WRAL_OPCODE_BITS = 5,
+ ERAL_OPCODE_BITS = 5,
+ EWEN_OPCODE_BITS = 5,
+ READ_ADDR_BITS = 6,
+ WRITE_ADDR_BITS = 6,
+ ERASE_ADDR_BITS = 6,
+ EWDS_ADDR_BITS = 4,
+ WRAL_ADDR_BITS = 4,
+ ERAL_ADDR_BITS = 4,
+ EWEN_ADDR_BITS = 4,
+ DATA_BITS = 16
+ };
+
+ EEPROM93C46 *eeprom;
+
+ // Helper methods
+ void shiftOutBits(uint16_t data, uint16_t count);
+ uint16_t shiftInBits(uint16_t count);
+ void getReady();
+ void standby();
+ void stop();
+ uint16_t readAt(uint16_t addr);
+ bool writeTo(uint16_t addr, uint16_t value);
+ void writeOpAddr(int opCode, int opCodeBits, uint16_t addr, int addrBits);
+ void writeData(uint16_t value) { shiftOutBits(value, DATA_BITS); }
+ bool waitForCompletion();
+
+public:
+ void setUp()
+ {
+ eeprom = new EEPROM93C46;
+ eeprom->init(g_abInitialContent);
+ }
+
+ void tearDown()
+ {
+ delete eeprom;
+ }
+
+ void testSize()
+ {
+ CPPUNIT_ASSERT_EQUAL( sizeof(g_abInitialContent), (size_t)EEPROM93C46::SIZE );
+ }
+
+ void testRead()
+ {
+ getReady();
+ for ( uint32_t wordAddr=0; wordAddr < EEPROM93C46::SIZE; wordAddr++ ) {
+ shiftOutBits(READ_OPCODE, READ_OPCODE_BITS);
+ shiftOutBits(wordAddr, READ_ADDR_BITS);
+
+ CPPUNIT_ASSERT_EQUAL( g_abInitialContent[wordAddr], (uint16_t)wordAddr );
+ CPPUNIT_ASSERT_EQUAL( g_abInitialContent[wordAddr], shiftInBits(DATA_BITS) );
+ standby();
+ }
+ stop();
+ }
+
+ void testSequentialRead()
+ {
+ getReady();
+ shiftOutBits(READ_OPCODE, READ_OPCODE_BITS);
+ shiftOutBits(0, READ_ADDR_BITS);
+ for ( int wordAddr=0; wordAddr < EEPROM93C46::SIZE; wordAddr++ ) {
+ CPPUNIT_ASSERT_EQUAL( g_abInitialContent[wordAddr], shiftInBits(DATA_BITS) );
+ }
+ stop();
+ }
+
+ void testWrite()
+ {
+ //unused: int i;
+ uint16_t wordAddr;
+
+ getReady();
+ // Enable write
+ writeOpAddr(EWEN_OPCODE, EWEN_OPCODE_BITS, 0, EWEN_ADDR_BITS);
+ standby();
+
+ for ( wordAddr=0; wordAddr < EEPROM93C46::SIZE; wordAddr++ ) {
+ //writeOpAddr(WRITE_OPCODE, WRITE_OPCODE_BITS, (uint16_t)wordAddr, WRITE_ADDR_BITS);
+ writeTo(wordAddr, 0x3F00 - (wordAddr<<8));
+ standby();
+
+ if (!waitForCompletion()) {
+ CPPUNIT_FAIL("EEPROM write was not completed");
+ stop();
+ return;
+ }
+ standby();
+ }
+
+ // Disable write
+ writeOpAddr(EWDS_OPCODE, EWDS_OPCODE_BITS, 0, EWDS_ADDR_BITS);
+
+ stop();
+
+ // Now check the result
+ getReady();
+ writeOpAddr(READ_OPCODE, READ_OPCODE_BITS, 0, READ_ADDR_BITS);
+ for ( wordAddr=0; wordAddr < EEPROM93C46::SIZE; wordAddr++ ) {
+ CPPUNIT_ASSERT_EQUAL((uint16_t)(0x3F00 - (wordAddr<<8)), shiftInBits(DATA_BITS) );
+ }
+ stop();
+ }
+
+ void testWriteDisabled()
+ {
+ getReady();
+
+ uint16_t addr = 0;
+ uint16_t oldValue = readAt(addr);
+ stop();
+ getReady();
+ if (writeTo(addr, ~oldValue)) {
+ // Write appears to be successful -- continue
+ CPPUNIT_ASSERT_EQUAL(oldValue, readAt(addr));
+ }
+ else {
+ CPPUNIT_FAIL("EEPROM write was not completed");
+ }
+ stop();
+ }
+
+ void testErase()
+ {
+ int i;
+ uint16_t addr = 0x1F;
+
+ getReady();
+ // Enable write
+ shiftOutBits(EWEN_OPCODE, EWEN_OPCODE_BITS);
+ shiftOutBits(0, EWEN_ADDR_BITS);
+ standby();
+
+ if (writeTo(addr, addr)) {
+ stop();
+ getReady();
+ // Write successful -- continue
+ CPPUNIT_ASSERT_EQUAL(addr, readAt(addr));
+ stop();
+ getReady();
+
+ shiftOutBits(ERASE_OPCODE, ERASE_OPCODE_BITS);
+ shiftOutBits(addr, ERASE_ADDR_BITS);
+
+ standby();
+
+ for (i = 0; i < 200; i++) {
+ if (eeprom->read() & DO)
+ break;
+ //usec_delay(50);
+ }
+
+ if (i == 200) {
+ CPPUNIT_FAIL("EEPROM erase was not completed");
+ stop();
+ return;
+ }
+
+ standby();
+
+ shiftOutBits(EWDS_OPCODE, EWDS_OPCODE_BITS);
+ shiftOutBits(0, EWDS_ADDR_BITS);
+
+ stop();
+ getReady();
+ CPPUNIT_ASSERT_EQUAL((uint16_t)0xFFFF, readAt(addr));
+ }
+ else {
+ CPPUNIT_FAIL("EEPROM write was not completed");
+ }
+ stop();
+ }
+
+ void testWriteAll()
+ {
+ uint16_t addr;
+
+ getReady();
+ // Enable write
+ writeOpAddr(EWEN_OPCODE, EWEN_OPCODE_BITS, 0, EWEN_ADDR_BITS);
+ standby();
+ // Fill all memory
+ writeOpAddr(WRAL_OPCODE, WRAL_OPCODE_BITS, 0, WRAL_ADDR_BITS);
+ writeData(0xABBA);
+ standby();
+
+ if (waitForCompletion()) {
+ stop();
+ getReady();
+ // Write successful -- verify all memory
+ for ( addr=0; addr < EEPROM93C46::SIZE; addr++ ) {
+ CPPUNIT_ASSERT_EQUAL((uint16_t)0xABBA, readAt(addr));
+ }
+ }
+ else {
+ CPPUNIT_FAIL("EEPROM write was not completed");
+ }
+ stop();
+ }
+
+ void testEraseAll()
+ {
+ //unused: int i;
+ uint16_t addr = 0x1F;
+
+ getReady();
+ // Enable write
+ writeOpAddr(EWEN_OPCODE, EWEN_OPCODE_BITS, 0, EWEN_ADDR_BITS);
+ standby();
+ // Fill all memory
+ writeOpAddr(WRITE_OPCODE, WRITE_OPCODE_BITS, addr, WRITE_ADDR_BITS);
+ writeData(0);
+ standby();
+
+ if (waitForCompletion()) {
+ stop();
+ getReady();
+ // Write successful -- verify random location
+ CPPUNIT_ASSERT_EQUAL((uint16_t)0, readAt(addr));
+ stop();
+ getReady();
+
+ writeOpAddr(ERAL_OPCODE, ERAL_OPCODE_BITS, addr, ERAL_ADDR_BITS);
+ standby();
+
+ if (!waitForCompletion()) {
+ CPPUNIT_FAIL("EEPROM erase was not completed");
+ stop();
+ return;
+ }
+
+ standby();
+
+ writeOpAddr(EWDS_OPCODE, EWDS_OPCODE_BITS, 0, EWDS_ADDR_BITS);
+ stop();
+
+ getReady();
+ for ( addr=0; addr < EEPROM93C46::SIZE; addr++ ) {
+ CPPUNIT_ASSERT_EQUAL((uint16_t)0xFFFF, readAt(addr));
+ }
+ }
+ else {
+ CPPUNIT_FAIL("EEPROM write was not completed");
+ }
+ stop();
+ }
+};
+
+/**
+ * shiftOutBits - Shift data bits our to the EEPROM
+ * @hw: pointer to the EEPROM object
+ * @data: data to send to the EEPROM
+ * @count: number of bits to shift out
+ *
+ * We need to shift 'count' bits out to the EEPROM. So, the value in the
+ * "data" parameter will be shifted out to the EEPROM one bit at a time.
+ * In order to do this, "data" must be broken down into bits.
+ **/
+void EEPROMTest::shiftOutBits(uint16_t data, uint16_t count) {
+ uint32_t wires = eeprom->read();
+ uint32_t mask;
+
+ mask = 0x01 << (count - 1);
+ wires &= ~DO;
+
+ do {
+ wires &= ~DI;
+
+ if (data & mask)
+ wires |= DI;
+
+ eeprom->write(wires);
+
+ // Raise clock
+ eeprom->write(wires |= SK);
+ // Lower clock
+ eeprom->write(wires &= ~SK);
+
+ mask >>= 1;
+ } while (mask);
+
+ wires &= ~DI;
+ eeprom->write(wires);
+}
+
+/**
+ * shiftInBits - Shift data bits in from the EEPROM
+ * @count: number of bits to shift in
+ *
+ * In order to read a register from the EEPROM, we need to shift 'count' bits
+ * in from the EEPROM. Bits are "shifted in" by raising the clock input to
+ * the EEPROM (setting the SK bit), and then reading the value of the data out
+ * "DO" bit. During this "shifting in" process the data in "DI" bit should
+ * always be clear.
+ **/
+uint16_t EEPROMTest::shiftInBits(uint16_t count)
+{
+ uint32_t wires;
+ uint32_t i;
+ uint16_t data;
+
+ wires = eeprom->read();
+
+ wires &= ~(DO | DI);
+ data = 0;
+
+ for (i = 0; i < count; i++) {
+ data <<= 1;
+ // Raise clock
+ eeprom->write(wires |= SK);
+
+ wires = eeprom->read();
+
+ wires &= ~DI;
+ if (wires & DO)
+ data |= 1;
+
+ // Lower clock
+ eeprom->write(wires &= ~SK);
+ }
+
+ return data;
+}
+
+/**
+ * getReady - Prepares EEPROM for read/write
+ *
+ * Setups the EEPROM for reading and writing.
+ **/
+void EEPROMTest::getReady()
+{
+ unsigned wires = eeprom->read();
+ /* Clear SK and DI */
+ eeprom->write(wires &= ~(DI | SK));
+ /* Set CS */
+ eeprom->write(wires | CS);
+}
+
+/**
+ * standby - Return EEPROM to standby state
+ *
+ * Return the EEPROM to a standby state.
+ **/
+void EEPROMTest::standby()
+{
+ unsigned wires = eeprom->read();
+
+ eeprom->write(wires &= ~(CS | SK));
+
+ // Raise clock
+ eeprom->write(wires |= SK);
+
+ // Select EEPROM
+ eeprom->write(wires |= CS);
+
+ // Lower clock
+ eeprom->write(wires &= ~SK);
+}
+
+/**
+ * stop - Terminate EEPROM command
+ *
+ * Terminates the current command by inverting the EEPROM's chip select pin.
+ **/
+void EEPROMTest::stop()
+{
+ unsigned wires = eeprom->read();
+
+ eeprom->write(wires &= ~(CS | DI));
+ // Raise clock
+ eeprom->write(wires |= SK);
+ // Lower clock
+ eeprom->write(wires &= ~SK);
+}
+
+/**
+ * readAt - Read a word at specified address
+ * @addr: address to read
+ *
+ * Returns the value of the word specified in 'addr' parameter.
+ **/
+uint16_t EEPROMTest::readAt(uint16_t addr)
+{
+ getReady();
+ shiftOutBits(READ_OPCODE, READ_OPCODE_BITS);
+ shiftOutBits(addr, READ_ADDR_BITS);
+
+ uint16_t value = shiftInBits(DATA_BITS);
+ stop();
+
+ return value;
+}
+
+/**
+ * writeTo - Write a word to specified address
+ * @addr: address to write to
+ * @value: value to store
+ *
+ * Returns false if write did not complete.
+ *
+ * Note: Make sure EEPROM is selected and writable before attempting
+ * to write. Use getReady() and stop() to select/deselect
+ * EEPROM.
+ **/
+bool EEPROMTest::writeTo(uint16_t addr, uint16_t value)
+{
+ writeOpAddr(WRITE_OPCODE, WRITE_OPCODE_BITS, addr, WRITE_ADDR_BITS);
+ writeData(value);
+ standby();
+ return waitForCompletion();
+}
+
+
+/**
+ * waitForCompletion - Wait until EEPROM clears the busy bit
+ *
+ * Returns false if the EEPROM is still busy.
+ */
+bool EEPROMTest::waitForCompletion() {
+ for (int i = 0; i < 200; i++) {
+ if (eeprom->read() & DO) {
+ standby();
+ return true;
+ }
+ // Wait 50 usec;
+ }
+
+ return false;
+}
+
+/**
+ * writeOpAddr - Write an opcode and address
+ * @opCode: operation code
+ * @opCodeBits: number of bits in opCode
+ * @addr: address to write to
+ * @addrBits: number of bits in address
+ **/
+void EEPROMTest::writeOpAddr(int opCode, int opCodeBits, uint16_t addr, int addrBits)
+{
+ shiftOutBits(opCode, opCodeBits);
+ shiftOutBits(addr, addrBits);
+}
+
+int main()
+{
+#ifdef USE_CPPUNIT
+ CppUnit::TextUi::TestRunner runner;
+ runner.addTest( EEPROMTest::suite() );
+ return runner.run() ? 0 : 1;
+#else
+ EEPROMTest Test;
+ return Test.run();
+#endif
+}
+
diff --git a/src/VBox/Devices/Network/testcase/tstDevPhy.cpp b/src/VBox/Devices/Network/testcase/tstDevPhy.cpp
new file mode 100644
index 00000000..93186bfe
--- /dev/null
+++ b/src/VBox/Devices/Network/testcase/tstDevPhy.cpp
@@ -0,0 +1,192 @@
+/* $Id: tstDevPhy.cpp $ */
+/** @file
+ * PHY MDIO unit tests.
+ */
+
+/*
+ * Copyright (C) 2007-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#ifdef USE_CPPUNIT
+# include <cppunit/ui/text/TestRunner.h>
+# include <cppunit/extensions/HelperMacros.h>
+#else
+# include "CppUnitEmulation.h"
+#endif
+
+#include "../DevE1000Phy.h"
+
+
+/**
+ * Test fixture for PHY MDIO/MDC interface emulation.
+ */
+class PhyTest
+#ifdef USE_CPPUNIT
+ : public CppUnit::TestFixture
+#endif
+{
+ CPPUNIT_TEST_SUITE( tstDevPhy );
+
+ CPPUNIT_TEST(testSize);
+ CPPUNIT_TEST(testReadPID);
+ CPPUNIT_TEST(testReadEPID);
+ CPPUNIT_TEST(testWriteANA);
+
+ CPPUNIT_TEST_SUITE_END();
+
+private:
+ enum Op
+ {
+ WRITE_OP = 0x1,
+ READ_OP = 0x2
+ };
+
+#define PHYADR_VAL (uint16_t)0
+#define ST_VAL (uint16_t)1
+#define TA_VAL (uint16_t)2
+#define PREAMBLE_VAL 0xFFFFFFFF
+
+ enum BitWidths {
+ ST_BITS = 2,
+ OP_BITS = 2,
+ PHYADR_BITS = 5,
+ REGADR_BITS = 5,
+ TA_BITS = 2,
+ DATA_BITS = 16,
+ PREAMBLE_BITS = 32
+ };
+
+ PPHY phy;
+
+ // Helper methods
+ void shiftOutBits(uint32_t data, uint16_t count);
+ uint16_t shiftInBits(uint16_t count);
+ int readAt(uint16_t addr);
+ void writeTo(uint16_t addr, uint32_t value);
+
+public:
+ void setUp()
+ {
+ phy = new PHY;
+ Phy::init(phy, 0, PHY_EPID_M881000);
+ }
+
+ void tearDown()
+ {
+ delete phy;
+ }
+
+ void testSize()
+ {
+ CPPUNIT_ASSERT_EQUAL(32, ST_BITS+OP_BITS+PHYADR_BITS+REGADR_BITS+TA_BITS+DATA_BITS);
+ }
+
+ void testReadPID()
+ {
+ CPPUNIT_ASSERT_EQUAL(0x0141, readAt(2));
+ }
+
+ void testReadEPID()
+ {
+ CPPUNIT_ASSERT_EQUAL(0x0141, readAt(2));
+ CPPUNIT_ASSERT_EQUAL(PHY_EPID_M881000, readAt(3));
+ }
+
+ void testWriteANA()
+ {
+ writeTo(4, 0xBEEF);
+ CPPUNIT_ASSERT_EQUAL(0xBEEF, readAt(4));
+ }
+
+};
+
+/**
+ * shiftOutBits - Shift data bits our to MDIO
+ **/
+void PhyTest::shiftOutBits(uint32_t data, uint16_t count) {
+ uint32_t mask = 0x01 << (count - 1);
+
+ do {
+ Phy::writeMDIO(phy, data & mask, NULL /*pDevIns*/);
+ mask >>= 1;
+ } while (mask);
+}
+
+/**
+ * shiftInBits - Shift data bits in from MDIO
+ **/
+uint16_t PhyTest::shiftInBits(uint16_t count)
+{
+ uint16_t data = 0;
+
+ while (count--)
+ {
+ data <<= 1;
+ data |= Phy::readMDIO(phy) ? 1 : 0;
+ }
+
+ return data;
+}
+
+int PhyTest::readAt(uint16_t addr)
+{
+ shiftOutBits(PREAMBLE_VAL, PREAMBLE_BITS);
+
+ shiftOutBits(ST_VAL, ST_BITS);
+ shiftOutBits(READ_OP, OP_BITS);
+ shiftOutBits(PHYADR_VAL, PHYADR_BITS);
+ shiftOutBits(addr, REGADR_BITS);
+
+ CPPUNIT_ASSERT_EQUAL((uint16_t)0, shiftInBits(1));
+ uint16_t u16Data = shiftInBits(DATA_BITS);
+ shiftInBits(1);
+ return u16Data;
+}
+
+void PhyTest::writeTo(uint16_t addr, uint32_t value)
+{
+ shiftOutBits(PREAMBLE_VAL, PREAMBLE_BITS);
+
+ shiftOutBits(ST_VAL, ST_BITS);
+ shiftOutBits(WRITE_OP, OP_BITS);
+ shiftOutBits(PHYADR_VAL, PHYADR_BITS);
+ shiftOutBits(addr, REGADR_BITS);
+ shiftOutBits(TA_VAL, TA_BITS);
+ shiftOutBits(value, DATA_BITS);
+}
+
+// Create text test runner and run all tests.
+int main()
+{
+#ifdef USE_CPPUNIT
+ CppUnit::TextUi::TestRunner runner;
+ runner.addTest( PhyTest::suite() );
+ return runner.run() ? 0 : 1;
+#else
+ PhyTest Test;
+ return Test.run();
+#endif
+}
+
diff --git a/src/VBox/Devices/Network/testcase/tstIntNet-1.cpp b/src/VBox/Devices/Network/testcase/tstIntNet-1.cpp
new file mode 100644
index 00000000..fbbff794
--- /dev/null
+++ b/src/VBox/Devices/Network/testcase/tstIntNet-1.cpp
@@ -0,0 +1,1030 @@
+/* $Id: tstIntNet-1.cpp $ */
+/** @file
+ * VBox - Testcase for internal networking, simple NetFlt trunk creation.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#include <VBox/intnet.h>
+#include <VBox/intnetinline.h>
+#include <VBox/vmm/pdmnetinline.h>
+#include <VBox/sup.h>
+#include <VBox/vmm/vmm.h>
+#include <iprt/errcore.h>
+#include <iprt/initterm.h>
+#include <iprt/alloc.h>
+#include <iprt/path.h>
+#include <iprt/stream.h>
+#include <iprt/string.h>
+#include <iprt/thread.h>
+#include <iprt/param.h>
+#include <iprt/getopt.h>
+#include <iprt/rand.h>
+#include <iprt/log.h>
+#include <iprt/crc.h>
+#include <iprt/net.h>
+
+#include "../Pcap.h"
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+static int g_cErrors = 0;
+static uint64_t g_StartTS = 0;
+static uint32_t g_DhcpXID = 0;
+static bool g_fDhcpReply = false;
+static bool g_fPingReply = false;
+static uint32_t g_cOtherPkts = 0;
+static uint32_t g_cArpPkts = 0;
+static uint32_t g_cIpv4Pkts = 0;
+static uint32_t g_cUdpPkts = 0;
+static uint32_t g_cDhcpPkts = 0;
+static uint32_t g_cTcpPkts = 0;
+
+
+/**
+ * Error reporting wrapper.
+ *
+ * @param pErrStrm The stream to write the error message to. Can be NULL.
+ * @param pszFormat The message format string.
+ * @param ... Format arguments.
+ */
+static void tstIntNetError(PRTSTREAM pErrStrm, const char *pszFormat, ...)
+{
+ if (!pErrStrm)
+ pErrStrm = g_pStdOut;
+
+ va_list va;
+ va_start(va, pszFormat);
+ RTStrmPrintf(pErrStrm, "tstIntNet-1: ERROR - ");
+ RTStrmPrintfV(pErrStrm, pszFormat, va);
+ va_end(va);
+
+ g_cErrors++;
+}
+
+
+/**
+ * Parses a frame an runs in thru the RTNet validation code so it gets
+ * some exercise.
+ *
+ * @param pvFrame Pointer to the ethernet frame.
+ * @param cbFrame The size of the ethernet frame.
+ * @param pErrStrm The error stream.
+ */
+static void tstIntNetTestFrame(void const *pvFrame, size_t cbFrame, PRTSTREAM pErrStrm, bool fGso)
+{
+ /*
+ * Ethernet header.
+ */
+ PCRTNETETHERHDR pEtherHdr = (PCRTNETETHERHDR)pvFrame;
+ if (cbFrame <= sizeof(*pEtherHdr))
+ return tstIntNetError(pErrStrm, "cbFrame=%#x <= %#x (ether)\n", cbFrame, sizeof(*pEtherHdr));
+ ssize_t cbLeft = cbFrame - sizeof(*pEtherHdr);
+ uint8_t const *pbCur = (uint8_t const *)(pEtherHdr + 1);
+
+ switch (RT_BE2H_U16(pEtherHdr->EtherType))
+ {
+ case RTNET_ETHERTYPE_ARP:
+ {
+ g_cArpPkts++;
+ break;
+ }
+
+ case RTNET_ETHERTYPE_IPV4:
+ {
+ g_cIpv4Pkts++;
+
+ PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)pbCur;
+ if (!RTNetIPv4IsHdrValid(pIpHdr, cbLeft, cbLeft, !fGso /*fChecksum*/))
+ return tstIntNetError(pErrStrm, "RTNetIPv4IsHdrValid failed\n");
+ pbCur += pIpHdr->ip_hl * 4;
+ cbLeft -= pIpHdr->ip_hl * 4;
+ AssertFatal(cbLeft >= 0);
+
+ switch (pIpHdr->ip_p)
+ {
+ case RTNETIPV4_PROT_ICMP:
+ {
+ /** @todo ICMP? */
+ break;
+ }
+
+ case RTNETIPV4_PROT_UDP:
+ {
+ g_cUdpPkts++;
+ PCRTNETUDP pUdpHdr = (PCRTNETUDP)pbCur;
+ if (!RTNetIPv4IsUDPValid(pIpHdr, pUdpHdr, pUdpHdr + 1, cbLeft, !fGso /*fChecksum*/))
+ return tstIntNetError(pErrStrm, "RTNetIPv4IsUDPValid failed\n");
+ pbCur += sizeof(*pUdpHdr);
+ cbLeft -= sizeof(*pUdpHdr);
+
+ if (RT_BE2H_U16(pUdpHdr->uh_dport) == RTNETIPV4_PORT_BOOTPS)
+ {
+ g_cDhcpPkts++;
+ PCRTNETBOOTP pDhcp = (PCRTNETBOOTP)pbCur;
+ if (!RTNetIPv4IsDHCPValid(pUdpHdr, pDhcp, cbLeft, NULL))
+ return tstIntNetError(pErrStrm, "RTNetIPv4IsDHCPValid failed\n");
+ }
+ break;
+ }
+
+ case RTNETIPV4_PROT_TCP:
+ {
+ g_cTcpPkts++;
+ PCRTNETTCP pTcpHdr = (PCRTNETTCP)pbCur;
+ if (!RTNetIPv4IsTCPValid(pIpHdr, pTcpHdr, cbLeft, NULL, cbLeft, !fGso /*fChecksum*/))
+ return tstIntNetError(pErrStrm, "RTNetIPv4IsTCPValid failed\n");
+ break;
+ }
+ }
+ break;
+ }
+
+ //case RTNET_ETHERTYPE_IPV6:
+ default:
+ g_cOtherPkts++;
+ break;
+ }
+}
+
+
+/**
+ * Transmits one frame after appending the CRC.
+ *
+ * @param hIf The interface handle.
+ * @param pSession The session.
+ * @param pBuf The shared interface buffer.
+ * @param pvFrame The frame without a crc.
+ * @param cbFrame The size of it.
+ * @param pFileRaw The file to write the raw data to (optional).
+ * @param pFileText The file to write a textual packet summary to (optional).
+ */
+static void doXmitFrame(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF pBuf, void *pvFrame, size_t cbFrame, PRTSTREAM pFileRaw, PRTSTREAM pFileText)
+{
+ /*
+ * Log it.
+ */
+ if (pFileText)
+ {
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame;
+ uint64_t NanoTS = RTTimeNanoTS() - g_StartTS;
+ RTStrmPrintf(pFileText, "%3RU64.%09u: cb=%04x dst=%.6Rhxs src=%.6Rhxs type=%04x Send!\n",
+ NanoTS / 1000000000, (uint32_t)(NanoTS % 1000000000),
+ cbFrame, &pEthHdr->SrcMac, &pEthHdr->DstMac, RT_BE2H_U16(pEthHdr->EtherType));
+ }
+
+ /*
+ * Run in thru the frame validator to test the RTNet code.
+ */
+ tstIntNetTestFrame(pvFrame, cbFrame, pFileText, false /*fGso*/);
+
+ /*
+ * Write the frame and push the queue.
+ *
+ * Don't bother with dealing with overflows like DrvIntNet does, because
+ * it's not supposed to happen here in this testcase.
+ */
+ int rc = IntNetRingWriteFrame(&pBuf->Send, pvFrame, cbFrame);
+ if (RT_SUCCESS(rc))
+ {
+ if (pFileRaw)
+ PcapStreamFrame(pFileRaw, g_StartTS, pvFrame, cbFrame, 0xffff);
+ }
+ else
+ {
+ RTPrintf("tstIntNet-1: IntNetRingWriteFrame failed, %Rrc; cbFrame=%d pBuf->cbSend=%d\n", rc, cbFrame, pBuf->cbSend);
+ g_cErrors++;
+ }
+
+ INTNETIFSENDREQ SendReq;
+ SendReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ SendReq.Hdr.cbReq = sizeof(SendReq);
+ SendReq.pSession = pSession;
+ SendReq.hIf = hIf;
+ rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_SEND, 0, &SendReq.Hdr);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstIntNet-1: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_IF_SEND,) failed, rc=%Rrc\n", rc);
+ g_cErrors++;
+ }
+
+}
+
+
+/**
+ * Does the transmit test.
+ *
+ * @param hIf The interface handle.
+ * @param pSession The session.
+ * @param pBuf The shared interface buffer.
+ * @param pSrcMac The mac address to use as source.
+ * @param pFileRaw The file to write the raw data to (optional).
+ * @param pFileText The file to write a textual packet summary to (optional).
+ */
+static void doXmitTest(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF pBuf, PCRTMAC pSrcMac, PRTSTREAM pFileRaw, PRTSTREAM pFileText)
+{
+ uint8_t abFrame[4096];
+ PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)&abFrame[0];
+ PRTNETIPV4 pIpHdr = (PRTNETIPV4) (pEthHdr + 1);
+ PRTNETUDP pUdpHdr = (PRTNETUDP) (pIpHdr + 1);
+ PRTNETDHCP pDhcpMsg = (PRTNETDHCP) (pUdpHdr + 1);
+
+ /*
+ * Create a simple DHCP broadcast request.
+ */
+ memset(&abFrame, 0, sizeof(abFrame));
+
+ pDhcpMsg->Op = 1; /* request */
+ pDhcpMsg->HType = 1; /* ethernet */
+ pDhcpMsg->HLen = sizeof(RTMAC);
+ pDhcpMsg->Hops = 0;
+ pDhcpMsg->XID = g_DhcpXID = RTRandU32();
+ pDhcpMsg->Secs = 0;
+ pDhcpMsg->Flags = 0; /* unicast */ //RT_H2BE_U16(0x8000); /* broadcast */
+ pDhcpMsg->CIAddr.u = 0;
+ pDhcpMsg->YIAddr.u = 0;
+ pDhcpMsg->SIAddr.u = 0;
+ pDhcpMsg->GIAddr.u = 0;
+ memset(&pDhcpMsg->CHAddr[0], '\0', sizeof(pDhcpMsg->CHAddr));
+ memcpy(&pDhcpMsg->CHAddr[0], pSrcMac, sizeof(*pSrcMac));
+ memset(&pDhcpMsg->SName[0], '\0', sizeof(pDhcpMsg->SName));
+ memset(&pDhcpMsg->File[0], '\0', sizeof(pDhcpMsg->File));
+ pDhcpMsg->abMagic[0] = 99;
+ pDhcpMsg->abMagic[1] = 130;
+ pDhcpMsg->abMagic[2] = 83;
+ pDhcpMsg->abMagic[3] = 99;
+
+ pDhcpMsg->DhcpOpt = 53; /* DHCP Msssage Type option */
+ pDhcpMsg->DhcpLen = 1;
+ pDhcpMsg->DhcpReq = 1; /* DHCPDISCOVER */
+
+ memset(&pDhcpMsg->abOptions[0], '\0', sizeof(pDhcpMsg->abOptions));
+ uint8_t *pbOpt = &pDhcpMsg->abOptions[0];
+
+ *pbOpt++ = 116; /* DHCP Auto-Configure */
+ *pbOpt++ = 1;
+ *pbOpt++ = 1;
+
+ *pbOpt++ = 61; /* Client identifier */
+ *pbOpt++ = 1 + sizeof(*pSrcMac);
+ *pbOpt++ = 1; /* hw type: ethernet */
+ memcpy(pbOpt, pSrcMac, sizeof(*pSrcMac));
+ pbOpt += sizeof(*pSrcMac);
+
+ *pbOpt++ = 12; /* Host name */
+ *pbOpt++ = sizeof("tstIntNet-1") - 1;
+ memcpy(pbOpt, "tstIntNet-1", sizeof("tstIntNet-1") - 1);
+ pbOpt += sizeof("tstIntNet-1") - 1;
+
+ *pbOpt = 0xff; /* the end */
+
+ /* UDP */
+ pUdpHdr->uh_sport = RT_H2BE_U16(68); /* bootp */
+ pUdpHdr->uh_dport = RT_H2BE_U16(67); /* bootps */
+ pUdpHdr->uh_ulen = RT_H2BE_U16(sizeof(*pDhcpMsg) + sizeof(*pUdpHdr));
+ pUdpHdr->uh_sum = 0; /* pretend checksumming is disabled */
+
+ /* IP */
+ pIpHdr->ip_v = 4;
+ pIpHdr->ip_hl = sizeof(*pIpHdr) / sizeof(uint32_t);
+ pIpHdr->ip_tos = 0;
+ pIpHdr->ip_len = RT_H2BE_U16(sizeof(*pDhcpMsg) + sizeof(*pUdpHdr) + sizeof(*pIpHdr));
+ pIpHdr->ip_id = (uint16_t)RTRandU32();
+ pIpHdr->ip_off = 0;
+ pIpHdr->ip_ttl = 255;
+ pIpHdr->ip_p = 0x11; /* UDP */
+ pIpHdr->ip_sum = 0;
+ pIpHdr->ip_src.u = 0;
+ pIpHdr->ip_dst.u = UINT32_C(0xffffffff); /* broadcast */
+ pIpHdr->ip_sum = RTNetIPv4HdrChecksum(pIpHdr);
+
+ /* calc the UDP checksum. */
+ pUdpHdr->uh_sum = RTNetIPv4UDPChecksum(pIpHdr, pUdpHdr, pUdpHdr + 1);
+
+ /* Ethernet */
+ memset(&pEthHdr->DstMac, 0xff, sizeof(pEthHdr->DstMac)); /* broadcast */
+ pEthHdr->SrcMac = *pSrcMac;
+ pEthHdr->EtherType = RT_H2BE_U16(RTNET_ETHERTYPE_IPV4); /* IP */
+
+ doXmitFrame(hIf, pSession, pBuf, &abFrame[0], (uint8_t *)(pDhcpMsg + 1) - (uint8_t *)&abFrame[0], pFileRaw, pFileText);
+}
+
+
+static uint16_t icmpChecksum(PRTNETICMPV4HDR pHdr, size_t cbHdr)
+{
+ size_t cbLeft = cbHdr;
+ uint16_t *pbSrc = (uint16_t *)pHdr;
+ uint16_t oddByte = 0;
+ int cSum = 0;
+
+ while (cbLeft > 1)
+ {
+ cSum += *pbSrc++;
+ cbLeft -= 2;
+ }
+
+ if (cbLeft == 1)
+ {
+ *(uint16_t *)(&oddByte) = *(uint16_t *)pbSrc;
+ cSum += oddByte;
+ }
+
+ cSum = (cSum >> 16) + (cSum & 0xffff);
+ cSum += (cSum >> 16);
+ uint16_t Result = ~cSum;
+ return Result;
+}
+
+
+/**
+ * Does the rudimentary ping test with fixed destination and source IPs.
+ *
+ * @param hIf The interface handle.
+ * @param pSession The session.
+ * @param pBuf The shared interface buffer.
+ * @param pSrcMac The mac address to use as source.
+ * @param pFileRaw The file to write the raw data to (optional).
+ * @param pFileText The file to write a textual packet summary to (optional).
+ */
+static void doPingTest(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF pBuf, PCRTMAC pSrcMac, PRTSTREAM pFileRaw, PRTSTREAM pFileText)
+{
+ uint8_t abFrame[4096];
+ PRTNETETHERHDR pEthHdr = (PRTNETETHERHDR)&abFrame[0];
+ PRTNETIPV4 pIpHdr = (PRTNETIPV4) (pEthHdr + 1);
+ PRTNETICMPV4ECHO pIcmpEcho = (PRTNETICMPV4ECHO) (pIpHdr + 1);
+
+ /*
+ * Create a simple ping request.
+ */
+ memset(&abFrame, 0, sizeof(abFrame));
+
+ pIcmpEcho->Hdr.icmp_type = RTNETICMPV4_TYPE_ECHO_REQUEST;
+ pIcmpEcho->Hdr.icmp_code = 0;
+ pIcmpEcho->icmp_id = 0x06;
+ pIcmpEcho->icmp_seq = 0x05;
+ size_t cbPad = 56;
+ memset(&pIcmpEcho->icmp_data, '\0', cbPad);
+ pIcmpEcho->Hdr.icmp_cksum = icmpChecksum(&pIcmpEcho->Hdr, cbPad + 8);
+
+ /* IP */
+ pIpHdr->ip_v = 4;
+ pIpHdr->ip_hl = sizeof(*pIpHdr) / sizeof(uint32_t);
+ pIpHdr->ip_tos = 0;
+ pIpHdr->ip_len = RT_H2BE_U16((uint16_t)(sizeof(*pIcmpEcho) + cbPad + sizeof(*pIpHdr)));
+ pIpHdr->ip_id = (uint16_t)RTRandU32();
+ pIpHdr->ip_off = 0;
+ pIpHdr->ip_ttl = 255;
+ pIpHdr->ip_p = 0x01; /*ICMP */
+ pIpHdr->ip_sum = 0;
+ pIpHdr->ip_src.u = UINT32_C(0x9701A8C0); /* 192.168.1.151 */
+ pIpHdr->ip_dst.u = UINT32_C(0xF9A344D0); /* 208.68.163.249 */
+ pIpHdr->ip_sum = RTNetIPv4HdrChecksum(pIpHdr);
+
+ /* Ethernet */
+ memset(&pEthHdr->DstMac, 0xff, sizeof(pEthHdr->DstMac)); /* broadcast */
+
+ pEthHdr->SrcMac = *pSrcMac;
+#if 0 /* Enable with host's real Mac address for testing of the testcase. */
+ pEthHdr->SrcMac.au8[0] = 0x00;
+ pEthHdr->SrcMac.au8[1] = 0x1b;
+ pEthHdr->SrcMac.au8[2] = 0x24;
+ pEthHdr->SrcMac.au8[3] = 0xa0;
+ pEthHdr->SrcMac.au8[4] = 0x2f;
+ pEthHdr->SrcMac.au8[5] = 0xce;
+#endif
+
+ pEthHdr->EtherType = RT_H2BE_U16(RTNET_ETHERTYPE_IPV4); /* IP */
+
+ doXmitFrame(hIf, pSession, pBuf, &abFrame[0], (uint8_t *)(pIcmpEcho + 1) + cbPad - (uint8_t *)&abFrame[0], pFileRaw, pFileText);
+}
+
+
+/**
+ * Does packet sniffing for a given period of time.
+ *
+ * @param hIf The interface handle.
+ * @param pSession The session.
+ * @param pBuf The shared interface buffer.
+ * @param cMillies The time period, ms.
+ * @param pFileRaw The file to write the raw data to (optional).
+ * @param pFileText The file to write a textual packet summary to (optional).
+ * @param pSrcMac Out MAC address.
+ */
+static void doPacketSniffing(INTNETIFHANDLE hIf, PSUPDRVSESSION pSession, PINTNETBUF pBuf, uint32_t cMillies,
+ PRTSTREAM pFileRaw, PRTSTREAM pFileText, PCRTMAC pSrcMac)
+{
+ /*
+ * The loop.
+ */
+ PINTNETRINGBUF pRingBuf = &pBuf->Recv;
+ for (;;)
+ {
+ /*
+ * Wait for a packet to become available.
+ */
+ uint64_t cElapsedMillies = (RTTimeNanoTS() - g_StartTS) / 1000000;
+ if (cElapsedMillies >= cMillies)
+ break;
+ INTNETIFWAITREQ WaitReq;
+ WaitReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ WaitReq.Hdr.cbReq = sizeof(WaitReq);
+ WaitReq.pSession = pSession;
+ WaitReq.hIf = hIf;
+ WaitReq.cMillies = cMillies - (uint32_t)cElapsedMillies;
+ int rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_WAIT, 0, &WaitReq.Hdr);
+ if (rc == VERR_TIMEOUT || rc == VERR_INTERRUPTED)
+ break;
+ if (RT_FAILURE(rc))
+ {
+ g_cErrors++;
+ RTPrintf("tstIntNet-1: VMMR0_DO_INTNET_IF_WAIT returned %Rrc\n", rc);
+ break;
+ }
+
+ /*
+ * Process the receive buffer.
+ */
+ PINTNETHDR pHdr;
+ while ((pHdr = IntNetRingGetNextFrameToRead(pRingBuf)))
+ {
+ if (pHdr->u8Type == INTNETHDR_TYPE_FRAME)
+ {
+ size_t cbFrame = pHdr->cbFrame;
+ const void *pvFrame = IntNetHdrGetFramePtr(pHdr, pBuf);
+ uint64_t NanoTS = RTTimeNanoTS() - g_StartTS;
+
+ if (pFileRaw)
+ PcapStreamFrame(pFileRaw, g_StartTS, pvFrame, cbFrame, 0xffff);
+
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame;
+ if (pFileText)
+ RTStrmPrintf(pFileText, "%3RU64.%09u: cb=%04x dst=%.6Rhxs src=%.6Rhxs type=%04x%s\n",
+ NanoTS / 1000000000, (uint32_t)(NanoTS % 1000000000),
+ cbFrame, &pEthHdr->DstMac, &pEthHdr->SrcMac, RT_BE2H_U16(pEthHdr->EtherType),
+ !memcmp(&pEthHdr->DstMac, pSrcMac, sizeof(*pSrcMac)) ? " Mine!" : "");
+ tstIntNetTestFrame(pvFrame, cbFrame, pFileText, false /*fGso*/);
+
+ /* Loop for the DHCP reply. */
+ if ( cbFrame > 64
+ && RT_BE2H_U16(pEthHdr->EtherType) == 0x0800 /* EtherType == IP */)
+ {
+ PCRTNETIPV4 pIpHdr = (PCRTNETIPV4)(pEthHdr + 1);
+ PCRTNETUDP pUdpHdr = (PCRTNETUDP)((uint32_t *)pIpHdr + pIpHdr->ip_hl);
+ if ( pIpHdr->ip_p == 0x11 /*UDP*/
+ && RT_BE2H_U16(pUdpHdr->uh_dport) == 68 /* bootp */
+ && RT_BE2H_U16(pUdpHdr->uh_sport) == 67 /* bootps */)
+ {
+ PCRTNETDHCP pDhcpMsg = (PCRTNETDHCP)(pUdpHdr + 1);
+ if ( pDhcpMsg->Op == 2 /* boot reply */
+ && pDhcpMsg->HType == 1 /* ethernet */
+ && pDhcpMsg->HLen == sizeof(RTMAC)
+ && (pDhcpMsg->XID == g_DhcpXID || !g_DhcpXID)
+ && !memcmp(&pDhcpMsg->CHAddr[0], pSrcMac, sizeof(*pSrcMac)))
+ {
+ g_fDhcpReply = true;
+ RTPrintf("tstIntNet-1: DHCP server reply! My IP: %d.%d.%d.%d\n",
+ pDhcpMsg->YIAddr.au8[0],
+ pDhcpMsg->YIAddr.au8[1],
+ pDhcpMsg->YIAddr.au8[2],
+ pDhcpMsg->YIAddr.au8[3]);
+ }
+ }
+ else if (pIpHdr->ip_p == 0x01) /* ICMP */
+ {
+ PRTNETICMPV4HDR pIcmpHdr = (PRTNETICMPV4HDR)(pIpHdr + 1);
+ PRTNETICMPV4ECHO pIcmpEcho = (PRTNETICMPV4ECHO)(pIpHdr + 1);
+ if ( pIcmpHdr->icmp_type == RTNETICMPV4_TYPE_ECHO_REPLY
+ && pIcmpEcho->icmp_seq == 0x05
+ && pIpHdr->ip_dst.u == UINT32_C(0x9701A8C0)
+#if 0
+ /** Enable with the host's real Mac address for testing of the testcase.*/
+ && pEthHdr->DstMac.au8[0] == 0x00
+ && pEthHdr->DstMac.au8[1] == 0x1b
+ && pEthHdr->DstMac.au8[2] == 0x24
+ && pEthHdr->DstMac.au8[3] == 0xa0
+ && pEthHdr->DstMac.au8[4] == 0x2f
+ && pEthHdr->DstMac.au8[5] == 0xce
+#else
+ && pEthHdr->DstMac.au16[0] == pSrcMac->au16[0]
+ && pEthHdr->DstMac.au16[1] == pSrcMac->au16[1]
+ && pEthHdr->DstMac.au16[2] == pSrcMac->au16[2]
+#endif
+ )
+ {
+ g_fPingReply = true;
+ RTPrintf("tstIntNet-1: Ping reply! From %d.%d.%d.%d\n",
+ pIpHdr->ip_src.au8[0],
+ pIpHdr->ip_src.au8[1],
+ pIpHdr->ip_src.au8[2],
+ pIpHdr->ip_src.au8[3]);
+ }
+ else
+ RTPrintf("type=%d seq=%d dstmac=%.6Rhxs ip=%d.%d.%d.%d\n", pIcmpHdr->icmp_type, pIcmpEcho->icmp_seq,
+ &pEthHdr->DstMac, pIpHdr->ip_dst.au8[0], pIpHdr->ip_dst.au8[1], pIpHdr->ip_dst.au8[2], pIpHdr->ip_dst.au8[3]);
+ }
+ }
+ }
+ else if (pHdr->u8Type == INTNETHDR_TYPE_GSO)
+ {
+ PCPDMNETWORKGSO pGso = IntNetHdrGetGsoContext(pHdr, pBuf);
+ size_t cbFrame = pHdr->cbFrame;
+ if (PDMNetGsoIsValid(pGso, cbFrame, cbFrame - sizeof(*pGso)))
+ {
+ const void *pvFrame = pGso + 1;
+ uint64_t NanoTS = RTTimeNanoTS() - g_StartTS;
+ cbFrame -= sizeof(pGso);
+
+ if (pFileRaw)
+ PcapStreamGsoFrame(pFileRaw, g_StartTS, pGso, pvFrame, cbFrame, 0xffff);
+
+ PCRTNETETHERHDR pEthHdr = (PCRTNETETHERHDR)pvFrame;
+ if (pFileText)
+ RTStrmPrintf(pFileText, "%3RU64.%09u: cb=%04x dst=%.6Rhxs src=%.6Rhxs type=%04x%s [GSO]\n",
+ NanoTS / 1000000000, (uint32_t)(NanoTS % 1000000000),
+ cbFrame, &pEthHdr->DstMac, &pEthHdr->SrcMac, RT_BE2H_U16(pEthHdr->EtherType),
+ !memcmp(&pEthHdr->DstMac, pSrcMac, sizeof(*pSrcMac)) ? " Mine!" : "");
+ tstIntNetTestFrame(pvFrame, cbFrame, pFileText, true /*fGso*/);
+ }
+ else
+ {
+ RTPrintf("tstIntNet-1: Bad GSO frame: %.*Rhxs\n", sizeof(*pGso), pGso);
+ STAM_REL_COUNTER_INC(&pBuf->cStatBadFrames);
+ g_cErrors++;
+ }
+ }
+ else if (pHdr->u8Type != INTNETHDR_TYPE_PADDING)
+ {
+ RTPrintf("tstIntNet-1: Unknown frame type %d\n", pHdr->u8Type);
+ STAM_REL_COUNTER_INC(&pBuf->cStatBadFrames);
+ g_cErrors++;
+ }
+
+ /* Advance to the next frame. */
+ IntNetRingSkipFrame(pRingBuf);
+ }
+ }
+
+ uint64_t NanoTS = RTTimeNanoTS() - g_StartTS;
+ RTStrmPrintf(pFileText ? pFileText : g_pStdOut,
+ "%3RU64.%09u: stopped. cRecvs=%RU64 cbRecv=%RU64 cLost=%RU64 cOYs=%RU64 cNYs=%RU64\n",
+ NanoTS / 1000000000, (uint32_t)(NanoTS % 1000000000),
+ pBuf->Recv.cStatFrames.c,
+ pBuf->Recv.cbStatWritten.c,
+ pBuf->cStatLost.c,
+ pBuf->cStatYieldsOk.c,
+ pBuf->cStatYieldsNok.c
+ );
+ RTStrmPrintf(pFileText ? pFileText : g_pStdOut,
+ "%3RU64.%09u: cOtherPkts=%RU32 cArpPkts=%RU32 cIpv4Pkts=%RU32 cTcpPkts=%RU32 cUdpPkts=%RU32 cDhcpPkts=%RU32\n",
+ NanoTS / 1000000000, (uint32_t)(NanoTS % 1000000000),
+ g_cOtherPkts, g_cArpPkts, g_cIpv4Pkts, g_cTcpPkts, g_cUdpPkts, g_cDhcpPkts);
+}
+
+#ifdef RT_OS_LINUX
+#include <stdio.h>
+#include <net/if.h>
+#include <net/route.h>
+/**
+ * Obtain the name of the interface used for default routing.
+ *
+ * NOTE: Copied from Main/src-server/linux/NetIf-linux.cpp
+ *
+ * @returns VBox status code.
+ *
+ * @param pszName The buffer where to put the name.
+ * @param cbName The buffer length.
+ */
+static int getDefaultIfaceName(char *pszName, size_t cbName)
+{
+ FILE *fp = fopen("/proc/net/route", "r");
+ char szBuf[1024];
+ char szIfName[17];
+ uint32_t uAddr;
+ uint32_t uGateway;
+ uint32_t uMask;
+ int iTmp;
+ unsigned uFlags;
+
+ if (fp)
+ {
+ while (fgets(szBuf, sizeof(szBuf)-1, fp))
+ {
+ int n = sscanf(szBuf, "%16s %x %x %x %d %d %d %x %d %d %d\n",
+ szIfName, &uAddr, &uGateway, &uFlags, &iTmp, &iTmp, &iTmp,
+ &uMask, &iTmp, &iTmp, &iTmp);
+ if (n < 10 || !(uFlags & RTF_UP))
+ continue;
+
+ if (uAddr == 0 && uMask == 0)
+ {
+ fclose(fp);
+ szIfName[sizeof(szIfName) - 1] = '\0';
+ return RTStrCopy(pszName, cbName, szIfName);
+ }
+ }
+ fclose(fp);
+ }
+ return VERR_INTERNAL_ERROR;
+}
+#endif /* RT_OS_LINUX */
+
+
+/**
+ * Entry point.
+ */
+extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
+{
+ RT_NOREF(envp);
+
+ /*
+ * Init the runtime and parse the arguments.
+ */
+ RTR3InitExe(argc, &argv, 0);
+
+ static RTGETOPTDEF const s_aOptions[] =
+ {
+ { "--duration", 'd', RTGETOPT_REQ_UINT32 },
+ { "--file", 'f', RTGETOPT_REQ_STRING },
+ { "--interface", 'i', RTGETOPT_REQ_STRING },
+ { "--mac-sharing", 'm', RTGETOPT_REQ_NOTHING },
+ { "--network", 'n', RTGETOPT_REQ_STRING },
+ { "--promiscuous", 'p', RTGETOPT_REQ_NOTHING },
+ { "--recv-buffer", 'r', RTGETOPT_REQ_UINT32 },
+ { "--send-buffer", 's', RTGETOPT_REQ_UINT32 },
+ { "--sniffer", 'S', RTGETOPT_REQ_NOTHING },
+ { "--text-file", 't', RTGETOPT_REQ_STRING },
+ { "--xmit-test", 'x', RTGETOPT_REQ_NOTHING },
+ { "--ping-test", 'P', RTGETOPT_REQ_NOTHING },
+ };
+
+ uint32_t cMillies = 1000;
+ PRTSTREAM pFileRaw = NULL;
+#ifdef RT_OS_DARWIN
+ const char *pszIf = "en0";
+#elif defined(RT_OS_LINUX)
+ char szIf[IFNAMSIZ+1] = "eth0"; /* Reasonable default */
+ /*
+ * Try to update the default interface by consulting the routing table.
+ * If we fail we still have our reasonable default.
+ */
+ getDefaultIfaceName(szIf, sizeof(szIf));
+ const char *pszIf = szIf;
+#elif defined(RT_OS_SOLARIS)
+ const char* pszIf = "rge0";
+#else
+ const char *pszIf = "em0";
+#endif
+ bool fMacSharing = false;
+ const char *pszNetwork = "tstIntNet-1";
+ bool fPromiscuous = false;
+ uint32_t cbRecv = 0;
+ uint32_t cbSend = 0;
+ bool fSniffer = false;
+ PRTSTREAM pFileText = g_pStdOut;
+ bool fXmitTest = false;
+ bool fPingTest = false;
+ RTMAC SrcMac;
+ SrcMac.au8[0] = 0x08;
+ SrcMac.au8[1] = 0x03;
+ SrcMac.au8[2] = 0x86;
+ RTRandBytes(&SrcMac.au8[3], sizeof(SrcMac) - 3);
+
+ int rc;
+ int ch;
+ RTGETOPTUNION Value;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /* fFlags */);
+ while ((ch = RTGetOpt(&GetState, &Value)))
+ switch (ch)
+ {
+ case 'd':
+ cMillies = Value.u32 * 1000;
+ if (cMillies / 1000 != Value.u32)
+ {
+ RTPrintf("tstIntNet-1: warning duration overflowed\n");
+ cMillies = UINT32_MAX - 1;
+ }
+ break;
+
+ case 'f':
+ rc = RTStrmOpen(Value.psz, "w+b", &pFileRaw);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstIntNet-1: Failed to creating \"%s\" for writing: %Rrc\n", Value.psz, rc);
+ return 1;
+ }
+ break;
+
+ case 'i':
+ pszIf = Value.psz;
+ if (strlen(pszIf) >= INTNET_MAX_TRUNK_NAME)
+ {
+ RTPrintf("tstIntNet-1: Interface name is too long (max %d chars): %s\n", INTNET_MAX_TRUNK_NAME - 1, pszIf);
+ return 1;
+ }
+ break;
+
+ case 'm':
+ fMacSharing = true;
+ break;
+
+ case 'n':
+ pszNetwork = Value.psz;
+ if (strlen(pszNetwork) >= INTNET_MAX_NETWORK_NAME)
+ {
+ RTPrintf("tstIntNet-1: Network name is too long (max %d chars): %s\n", INTNET_MAX_NETWORK_NAME - 1, pszNetwork);
+ return 1;
+ }
+ break;
+
+ case 'p':
+ fPromiscuous = true;
+ break;
+
+ case 'r':
+ cbRecv = Value.u32;
+ break;
+
+ case 's':
+ cbSend = Value.u32;
+ break;
+
+ case 'S':
+ fSniffer = true;
+ break;
+
+ case 't':
+ if (!*Value.psz)
+ pFileText = NULL;
+ else if (!strcmp(Value.psz, "-"))
+ pFileText = g_pStdOut;
+ else if (!strcmp(Value.psz, "!"))
+ pFileText = g_pStdErr;
+ else
+ {
+ rc = RTStrmOpen(Value.psz, "w", &pFileText);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstIntNet-1: Failed to creating \"%s\" for writing: %Rrc\n", Value.psz, rc);
+ return 1;
+ }
+ }
+ break;
+
+ case 'x':
+ fXmitTest = true;
+ break;
+
+ case 'P':
+ fPingTest = true;
+ break;
+
+ case 'h':
+ RTPrintf("syntax: tstIntNet-1 <options>\n"
+ "\n"
+ "Options:\n");
+ for (size_t i = 0; i < RT_ELEMENTS(s_aOptions); i++)
+ RTPrintf(" -%c,%s\n", s_aOptions[i].iShort, s_aOptions[i].pszLong);
+ RTPrintf("\n"
+ "Examples:\n"
+ " tstIntNet-1 -r 8192 -s 4096 -xS\n"
+ " tstIntNet-1 -n VBoxNetDhcp -r 4096 -s 4096 -i \"\" -xS\n");
+ return 1;
+
+ case 'V':
+ RTPrintf("$Revision: 155244 $\n");
+ return 0;
+
+ default:
+ return RTGetOptPrintError(ch, &Value);
+ }
+
+ RTPrintf("tstIntNet-1: TESTING...\n");
+
+ /*
+ * Open the session, load ring-0 and issue the request.
+ */
+ PSUPDRVSESSION pSession;
+ rc = SUPR3Init(&pSession);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstIntNet-1: SUPR3Init -> %Rrc\n", rc);
+ return 1;
+ }
+
+ char szPath[RTPATH_MAX];
+ rc = RTPathExecDir(szPath, sizeof(szPath) - sizeof("/../VMMR0.r0"));
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstIntNet-1: RTPathExecDir -> %Rrc\n", rc);
+ return 1;
+ }
+
+ strcat(szPath, "/../VMMR0.r0");
+
+ char szAbsPath[RTPATH_MAX];
+ rc = RTPathAbs(szPath, szAbsPath, sizeof(szAbsPath));
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstIntNet-1: RTPathAbs -> %Rrc\n", rc);
+ return 1;
+ }
+
+ rc = SUPR3LoadVMM(szAbsPath, NULL);
+ if (RT_FAILURE(rc))
+ {
+ RTPrintf("tstIntNet-1: SUPR3LoadVMM(\"%s\") -> %Rrc\n", szAbsPath, rc);
+ return 1;
+ }
+
+ /*
+ * Create the request, picking the network and trunk names from argv[2]
+ * and argv[1] if present.
+ */
+ INTNETOPENREQ OpenReq;
+ OpenReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ OpenReq.Hdr.cbReq = sizeof(OpenReq);
+ OpenReq.pSession = pSession;
+ RTStrCopy(OpenReq.szNetwork, sizeof(OpenReq.szNetwork), pszNetwork);
+ RTStrCopy(OpenReq.szTrunk, sizeof(OpenReq.szTrunk), pszIf);
+ OpenReq.enmTrunkType = *pszIf ? kIntNetTrunkType_NetFlt : kIntNetTrunkType_WhateverNone;
+ OpenReq.fFlags = fMacSharing ? INTNET_OPEN_FLAGS_SHARED_MAC_ON_WIRE : 0;
+ OpenReq.cbSend = cbSend;
+ OpenReq.cbRecv = cbRecv;
+ OpenReq.hIf = INTNET_HANDLE_INVALID;
+
+ /*
+ * Issue the request.
+ */
+ RTPrintf("tstIntNet-1: attempting to open/create network \"%s\" with NetFlt trunk \"%s\"...\n",
+ OpenReq.szNetwork, OpenReq.szTrunk);
+ RTStrmFlush(g_pStdOut);
+ rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_OPEN, 0, &OpenReq.Hdr);
+ if (RT_SUCCESS(rc))
+ {
+ RTPrintf("tstIntNet-1: successfully opened/created \"%s\" with NetFlt trunk \"%s\" - hIf=%#x\n",
+ OpenReq.szNetwork, OpenReq.szTrunk, OpenReq.hIf);
+ RTStrmFlush(g_pStdOut);
+
+ /*
+ * Get the ring-3 address of the shared interface buffer.
+ */
+ INTNETIFGETBUFFERPTRSREQ GetBufferPtrsReq;
+ GetBufferPtrsReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ GetBufferPtrsReq.Hdr.cbReq = sizeof(GetBufferPtrsReq);
+ GetBufferPtrsReq.pSession = pSession;
+ GetBufferPtrsReq.hIf = OpenReq.hIf;
+ GetBufferPtrsReq.pRing3Buf = NULL;
+ GetBufferPtrsReq.pRing0Buf = NIL_RTR0PTR;
+ rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS, 0, &GetBufferPtrsReq.Hdr);
+ if (RT_SUCCESS(rc))
+ {
+ PINTNETBUF pBuf = GetBufferPtrsReq.pRing3Buf;
+ RTPrintf("tstIntNet-1: pBuf=%p cbBuf=%d cbSend=%d cbRecv=%d\n",
+ pBuf, pBuf->cbBuf, pBuf->cbSend, pBuf->cbRecv);
+ RTStrmFlush(g_pStdOut);
+ if (fPromiscuous)
+ {
+ INTNETIFSETPROMISCUOUSMODEREQ PromiscReq;
+ PromiscReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ PromiscReq.Hdr.cbReq = sizeof(PromiscReq);
+ PromiscReq.pSession = pSession;
+ PromiscReq.hIf = OpenReq.hIf;
+ PromiscReq.fPromiscuous = true;
+ rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE, 0, &PromiscReq.Hdr);
+ if (RT_SUCCESS(rc))
+ RTPrintf("tstIntNet-1: interface in promiscuous mode\n");
+ }
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Activate the interface.
+ */
+ INTNETIFSETACTIVEREQ ActiveReq;
+ ActiveReq.Hdr.u32Magic = SUPVMMR0REQHDR_MAGIC;
+ ActiveReq.Hdr.cbReq = sizeof(ActiveReq);
+ ActiveReq.pSession = pSession;
+ ActiveReq.hIf = OpenReq.hIf;
+ ActiveReq.fActive = true;
+ rc = SUPR3CallVMMR0Ex(NIL_RTR0PTR, NIL_VMCPUID, VMMR0_DO_INTNET_IF_SET_ACTIVE, 0, &ActiveReq.Hdr);
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Start the stop watch, init the pcap file.
+ */
+ g_StartTS = RTTimeNanoTS();
+ if (pFileRaw)
+ PcapStreamHdr(pFileRaw, g_StartTS);
+
+ /*
+ * Do the transmit test first and so we can sniff for the response.
+ */
+ if (fXmitTest)
+ doXmitTest(OpenReq.hIf, pSession, pBuf, &SrcMac, pFileRaw, pFileText);
+
+ if (fPingTest)
+ doPingTest(OpenReq.hIf, pSession, pBuf, &SrcMac, pFileRaw, pFileText);
+
+ /*
+ * Either enter sniffing mode or do a timeout thing.
+ */
+ if (fSniffer)
+ {
+ doPacketSniffing(OpenReq.hIf, pSession, pBuf, cMillies, pFileRaw, pFileText, &SrcMac);
+ if ( fXmitTest
+ && !g_fDhcpReply)
+ {
+ RTPrintf("tstIntNet-1: Error! The DHCP server didn't reply... (Perhaps you don't have one?)\n");
+ g_cErrors++;
+ }
+
+ if ( fPingTest
+ && !g_fPingReply)
+ {
+ RTPrintf("tstIntNet-1: Error! No reply for ping request...\n");
+ g_cErrors++;
+ }
+ }
+ else
+ RTThreadSleep(cMillies);
+ }
+ else
+ {
+ RTPrintf("tstIntNet-1: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE,) failed, rc=%Rrc\n", rc);
+ g_cErrors++;
+ }
+ }
+ else
+ {
+ RTPrintf("tstIntNet-1: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_IF_SET_PROMISCUOUS_MODE,) failed, rc=%Rrc\n", rc);
+ g_cErrors++;
+ }
+ }
+ else
+ {
+ RTPrintf("tstIntNet-1: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_IF_GET_BUFFER_PTRS,) failed, rc=%Rrc\n", rc);
+ g_cErrors++;
+ }
+ }
+ else
+ {
+ RTPrintf("tstIntNet-1: SUPR3CallVMMR0Ex(,VMMR0_DO_INTNET_OPEN,) failed, rc=%Rrc\n", rc);
+ g_cErrors++;
+ }
+
+ SUPR3Term(false /*fForced*/);
+
+ /* close open files */
+ if (pFileRaw)
+ RTStrmClose(pFileRaw);
+ if (pFileText && pFileText != g_pStdErr && pFileText != g_pStdOut)
+ RTStrmClose(pFileText);
+
+ /*
+ * Summary.
+ */
+ if (!g_cErrors)
+ RTPrintf("tstIntNet-1: SUCCESS\n");
+ else
+ RTPrintf("tstIntNet-1: FAILURE - %d errors\n", g_cErrors);
+
+ return !!g_cErrors;
+}
+
+
+#if !defined(VBOX_WITH_HARDENING) || !defined(RT_OS_WINDOWS)
+/**
+ * Main entry point.
+ */
+int main(int argc, char **argv, char **envp)
+{
+ return TrustedMain(argc, argv, envp);
+}
+#endif
+
diff --git a/src/VBox/Devices/Network/testcase/tstIntNetR0.cpp b/src/VBox/Devices/Network/testcase/tstIntNetR0.cpp
new file mode 100644
index 00000000..5cb94894
--- /dev/null
+++ b/src/VBox/Devices/Network/testcase/tstIntNetR0.cpp
@@ -0,0 +1,867 @@
+/* $Id: tstIntNetR0.cpp $ */
+/** @file
+ * Internal networking - Usermode testcase for the kernel mode bits.
+ *
+ * This is a bit hackish as we're mixing context here, however it is
+ * very useful when making changes to the internal networking service.
+ */
+
+/*
+ * Copyright (C) 2006-2023 Oracle and/or its affiliates.
+ *
+ * This file is part of VirtualBox base platform packages, as
+ * available from https://www.virtualbox.org.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation, in version 3 of the
+ * License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <https://www.gnu.org/licenses>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-only
+ */
+
+
+/*********************************************************************************************************************************
+* Header Files *
+*********************************************************************************************************************************/
+#define IN_INTNET_TESTCASE
+#define IN_INTNET_R3
+#include <VBox/cdefs.h>
+#undef INTNETR0DECL
+#define INTNETR0DECL INTNETR3DECL
+#undef DECLR0CALLBACKMEMBER
+#define DECLR0CALLBACKMEMBER(type, name, args) DECLR3CALLBACKMEMBER(type, name, args)
+#include <VBox/types.h>
+typedef void *MYPSUPDRVSESSION;
+#define PSUPDRVSESSION MYPSUPDRVSESSION
+
+#include <VBox/intnet.h>
+#include <VBox/sup.h>
+#include <VBox/err.h>
+#include <iprt/asm.h>
+#include <iprt/getopt.h>
+#include <iprt/initterm.h>
+#include <iprt/mem.h>
+#include <iprt/mp.h>
+#include <iprt/stream.h>
+#include <iprt/thread.h>
+#include <iprt/time.h>
+#include <iprt/test.h>
+
+
+/*********************************************************************************************************************************
+* Structures and Typedefs *
+*********************************************************************************************************************************/
+/**
+ * Security objectype.
+ */
+typedef enum SUPDRVOBJTYPE
+{
+ /** The usual invalid object. */
+ SUPDRVOBJTYPE_INVALID = 0,
+ /** Internal network. */
+ SUPDRVOBJTYPE_INTERNAL_NETWORK,
+ /** Internal network interface. */
+ SUPDRVOBJTYPE_INTERNAL_NETWORK_INTERFACE,
+ /** The first invalid object type in this end. */
+ SUPDRVOBJTYPE_END,
+ /** The usual 32-bit type size hack. */
+ SUPDRVOBJTYPE_32_BIT_HACK = 0x7ffffff
+} SUPDRVOBJTYPE;
+
+/**
+ * Object destructor callback.
+ * This is called for reference counted objectes when the count reaches 0.
+ *
+ * @param pvObj The object pointer.
+ * @param pvUser1 The first user argument.
+ * @param pvUser2 The second user argument.
+ */
+typedef DECLCALLBACKTYPE(void, FNSUPDRVDESTRUCTOR,(void *pvObj, void *pvUser1, void *pvUser2));
+/** Pointer to a FNSUPDRVDESTRUCTOR(). */
+typedef FNSUPDRVDESTRUCTOR *PFNSUPDRVDESTRUCTOR;
+
+
+/**
+ * Dummy
+ */
+typedef struct OBJREF
+{
+ PFNSUPDRVDESTRUCTOR pfnDestructor;
+ void *pvUser1;
+ void *pvUser2;
+ uint32_t volatile cRefs;
+} OBJREF, *POBJREF;
+
+
+/*********************************************************************************************************************************
+* Global Variables *
+*********************************************************************************************************************************/
+/** The test handle.*/
+static RTTEST g_hTest = NIL_RTTEST;
+/** The size (in bytes) of the large transfer tests. */
+static uint32_t g_cbTransfer = _1M * 384;
+/** Fake session handle. */
+const PSUPDRVSESSION g_pSession = (PSUPDRVSESSION)(uintptr_t)0xdeadface;
+
+
+INTNETR3DECL(void *) SUPR0ObjRegister(PSUPDRVSESSION pSession, SUPDRVOBJTYPE enmType,
+ PFNSUPDRVDESTRUCTOR pfnDestructor, void *pvUser1, void *pvUser2)
+{
+ RTTEST_CHECK_RET(g_hTest, pSession == g_pSession, NULL);
+ POBJREF pRef = (POBJREF)RTTestGuardedAllocTail(g_hTest, sizeof(OBJREF));
+ if (!pRef)
+ return NULL;
+ pRef->cRefs = 1;
+ pRef->pfnDestructor = pfnDestructor;
+ pRef->pvUser1 = pvUser1;
+ pRef->pvUser2 = pvUser2;
+ NOREF(enmType);
+ return pRef;
+}
+
+INTNETR3DECL(int) SUPR0ObjAddRefEx(void *pvObj, PSUPDRVSESSION pSession, bool fNoBlocking)
+{
+ RTTEST_CHECK_RET(g_hTest, pSession == g_pSession, VERR_INVALID_PARAMETER);
+ POBJREF pRef = (POBJREF)pvObj;
+ ASMAtomicIncU32(&pRef->cRefs);
+ NOREF(fNoBlocking);
+ return VINF_SUCCESS;
+}
+
+INTNETR3DECL(int) SUPR0ObjAddRef(void *pvObj, PSUPDRVSESSION pSession)
+{
+ return SUPR0ObjAddRefEx(pvObj, pSession, false);
+}
+
+INTNETR3DECL(int) SUPR0ObjRelease(void *pvObj, PSUPDRVSESSION pSession)
+{
+ RTTEST_CHECK_RET(g_hTest, pSession == g_pSession, VERR_INVALID_PARAMETER);
+ POBJREF pRef = (POBJREF)pvObj;
+ if (!ASMAtomicDecU32(&pRef->cRefs))
+ {
+ pRef->pfnDestructor(pRef, pRef->pvUser1, pRef->pvUser2);
+ RTTestGuardedFree(g_hTest, pRef);
+ return VINF_OBJECT_DESTROYED;
+ }
+ return VINF_SUCCESS;
+}
+
+INTNETR3DECL(int) SUPR0ObjVerifyAccess(void *pvObj, PSUPDRVSESSION pSession, const char *pszObjName)
+{
+ RTTEST_CHECK_RET(g_hTest, pSession == g_pSession, VERR_INVALID_PARAMETER);
+ NOREF(pvObj); NOREF(pszObjName);
+ return VINF_SUCCESS;
+}
+
+INTNETR3DECL(int) SUPR0MemAlloc(PSUPDRVSESSION pSession, uint32_t cb, PRTR0PTR ppvR0, PRTR3PTR ppvR3)
+{
+ RTTEST_CHECK_RET(g_hTest, pSession == g_pSession, VERR_INVALID_PARAMETER);
+ void *pv = RTTestGuardedAllocTail(g_hTest, cb);
+ if (!pv)
+ return VERR_NO_MEMORY;
+ *ppvR0 = (RTR0PTR)pv;
+ if (ppvR3)
+ *ppvR3 = pv;
+ return VINF_SUCCESS;
+}
+
+INTNETR3DECL(int) SUPR0MemFree(PSUPDRVSESSION pSession, RTHCUINTPTR uPtr)
+{
+ RTTEST_CHECK_RET(g_hTest, pSession == g_pSession, VERR_INVALID_PARAMETER);
+ RTTestGuardedFree(g_hTest, (void *)uPtr);
+ return VINF_SUCCESS;
+}
+
+/* Fake non-existing ring-0 APIs. */
+#define RTThreadIsInInterrupt(hThread) false
+#define RTThreadPreemptIsEnabled(hThread) true
+#define RTMpCpuId() 0
+
+/* No CLI/POPF, please. */
+#include <iprt/spinlock.h>
+#undef RTSPINLOCK_FLAGS_INTERRUPT_SAFE
+#define RTSPINLOCK_FLAGS_INTERRUPT_SAFE RTSPINLOCK_FLAGS_INTERRUPT_UNSAFE
+
+
+/* ugly but necessary for making R0 code compilable for R3. */
+#undef LOG_GROUP
+#include "../SrvIntNetR0.cpp"
+
+
+/**
+ * Sends the data @a pvBuf points to.
+ */
+static int tstIntNetSendBuf(PINTNETRINGBUF pRingBuf, INTNETIFHANDLE hIf,
+ PSUPDRVSESSION pSession, void const *pvBuf, size_t cbBuf)
+{
+ INTNETSG Sg;
+ IntNetSgInitTemp(&Sg, (void *)pvBuf, (uint32_t)cbBuf);
+ int rc = intnetR0RingWriteFrame(pRingBuf, &Sg, NULL);
+ if (RT_SUCCESS(rc))
+ rc = IntNetR0IfSend(hIf, pSession);
+ return rc;
+}
+
+
+typedef struct MYARGS
+{
+ PINTNETBUF pBuf;
+ INTNETIFHANDLE hIf;
+ RTMAC Mac;
+ uint32_t cbFrame;
+ uint64_t u64Start;
+ uint64_t u64End;
+ uint32_t cbSent;
+ uint32_t cFramesSent;
+} MYARGS, *PMYARGS;
+
+
+/**
+ * Frame header used when testing.
+ */
+#pragma pack(1)
+typedef struct MYFRAMEHDR
+{
+ RTMAC SrcMac;
+ RTMAC DstMac;
+ uint32_t iFrame;
+ uint32_t auEos[3];
+} MYFRAMEHDR;
+#pragma pack()
+
+/**
+ * Send thread.
+ * This is constantly sending frames to the other interface.
+ */
+static DECLCALLBACK(int) SendThread(RTTHREAD hThreadSelf, void *pvArg)
+{
+ PMYARGS pArgs = (PMYARGS)pvArg;
+ int rc;
+ NOREF(hThreadSelf);
+
+ /*
+ * Send g_cbTransfer of data.
+ */
+ uint8_t abBuf[16384] = {0};
+ MYFRAMEHDR *pHdr = (MYFRAMEHDR *)&abBuf[0];
+ uint32_t iFrame = 0;
+ uint32_t cbSent = 0;
+ uint32_t cErrors = 0;
+
+ pHdr->SrcMac = pArgs->Mac;
+ pHdr->DstMac = pArgs->Mac;
+ pHdr->DstMac.au16[2] = (pArgs->Mac.au16[2] + 1) % 2;
+
+ pArgs->u64Start = RTTimeNanoTS();
+ for (; cbSent < g_cbTransfer; iFrame++)
+ {
+ const unsigned cb = pArgs->cbFrame
+ ? pArgs->cbFrame
+ : iFrame % 1519 + sizeof(RTMAC) * 2 + sizeof(unsigned);
+ pHdr->iFrame = iFrame;
+
+ INTNETSG Sg;
+ IntNetSgInitTemp(&Sg, abBuf, cb);
+ RTTEST_CHECK_RC_OK(g_hTest, rc = intnetR0RingWriteFrame(&pArgs->pBuf->Send, &Sg, NULL));
+ if (RT_SUCCESS(rc))
+ RTTEST_CHECK_RC_OK(g_hTest, rc = IntNetR0IfSend(pArgs->hIf, g_pSession));
+ if (RT_FAILURE(rc) && ++cErrors > 64)
+ {
+ RTTestFailed(g_hTest, "Aborting xmit after >64 errors");
+ break;
+ }
+
+ cbSent += cb;
+ }
+ pArgs->cbSent = cbSent;
+ pArgs->cFramesSent = iFrame;
+
+ /*
+ * Termination frames.
+ */
+ pHdr->iFrame = 0xffffdead;
+ pHdr->auEos[0] = 0xffffdead;
+ pHdr->auEos[1] = 0xffffdead;
+ pHdr->auEos[2] = 0xffffdead;
+ for (unsigned c = 0; c < 20; c++)
+ {
+ RTTEST_CHECK_RC_OK(g_hTest, rc = tstIntNetSendBuf(&pArgs->pBuf->Send, pArgs->hIf, g_pSession,
+ abBuf, sizeof(RTMAC) * 2 + sizeof(unsigned) * 4));
+ RTThreadSleep(1);
+ }
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "sender thread %.6Rhxs terminating.\n"
+ "iFrame=%u cb=%'u\n",
+ &pArgs->Mac, iFrame, cbSent);
+ return 0;
+}
+
+
+/** Ignore lost frames. It only makes things worse to bitch about it. */
+#define IGNORE_LOST_FRAMES
+
+/**
+ * Receive thread.
+ * This is reading stuff from the network.
+ */
+static DECLCALLBACK(int) ReceiveThread(RTTHREAD hThreadSelf, void *pvArg)
+{
+ uint32_t cbReceived = 0;
+ uint32_t cLostFrames = 0;
+ uint32_t iFrame = UINT32_MAX;
+ PMYARGS pArgs = (PMYARGS)pvArg;
+ NOREF(hThreadSelf);
+
+ for (;;)
+ {
+ /*
+ * Read data.
+ */
+ while (IntNetRingHasMoreToRead(&pArgs->pBuf->Recv))
+ {
+ uint8_t abBuf[16384 + 1024];
+ MYFRAMEHDR *pHdr = (MYFRAMEHDR *)&abBuf[0];
+ uint32_t cb = IntNetRingReadAndSkipFrame(&pArgs->pBuf->Recv, abBuf);
+
+ /* check for termination frame. */
+ if ( pHdr->iFrame == 0xffffdead
+ && pHdr->auEos[0] == 0xffffdead
+ && pHdr->auEos[1] == 0xffffdead
+ && pHdr->auEos[2] == 0xffffdead)
+ {
+ pArgs->u64End = RTTimeNanoTS();
+ RTThreadSleep(10);
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "receiver thread %.6Rhxs terminating.\n"
+ " iFrame=%u cb=%'u c=%'u %'uKB/s %'ufps cLost=%'u \n",
+ &pArgs->Mac, iFrame, cbReceived, iFrame - cLostFrames,
+ (unsigned)((double)cbReceived * 1000000000.0 / 1024 / (double)(pArgs->u64End - pArgs->u64Start)),
+ (unsigned)((double)(iFrame - cLostFrames) * 1000000000.0 / (double)(pArgs->u64End - pArgs->u64Start)),
+ cLostFrames);
+ return VINF_SUCCESS;
+ }
+
+ /* validate frame header */
+ if ( pHdr->DstMac.au16[0] != pArgs->Mac.au16[0]
+ || pHdr->DstMac.au16[1] != pArgs->Mac.au16[1]
+ || pHdr->DstMac.au16[2] != pArgs->Mac.au16[2]
+ || pHdr->SrcMac.au16[0] != pArgs->Mac.au16[0]
+ || pHdr->SrcMac.au16[1] != pArgs->Mac.au16[1]
+ || pHdr->SrcMac.au16[2] != (pArgs->Mac.au16[2] + 1) % 2)
+ {
+ RTTestFailed(g_hTest, "receiver thread %.6Rhxs received frame header: %.16Rhxs\n", &pArgs->Mac, abBuf);
+ }
+
+ /* frame stuff and stats. */
+ int32_t off = pHdr->iFrame - (iFrame + 1);
+ if (off)
+ {
+ if (off > 0)
+ {
+#ifndef IGNORE_LOST_FRAMES
+ RTTestFailed(g_hTest, "receiver thread %.6Rhxs: iFrame=%#x *puFrame=%#x off=%d\n",
+ &pArgs->Mac, iFrame, pHdr->iFrame, off);
+#endif
+ cLostFrames += off;
+ }
+ else
+ {
+ cLostFrames++;
+ RTTestFailed(g_hTest, "receiver thread %.6Rhxs: iFrame=%#x *puFrame=%#x off=%d\n",
+ &pArgs->Mac, iFrame, pHdr->iFrame, off);
+ }
+ }
+ iFrame = pHdr->iFrame;
+ cbReceived += cb;
+ }
+
+ /*
+ * Wait for data.
+ */
+ int rc = IntNetR0IfWait(pArgs->hIf, g_pSession, RT_INDEFINITE_WAIT);
+ switch (rc)
+ {
+ case VERR_INTERRUPTED:
+ case VINF_SUCCESS:
+ break;
+ case VERR_SEM_DESTROYED:
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "receiver thread %.6Rhxs terminating. iFrame=%u cb=%'u c=%'u cLost=%'u\n",
+ &pArgs->Mac, iFrame, cbReceived, iFrame - cLostFrames, cLostFrames);
+ return VINF_SUCCESS;
+
+ default:
+ RTTestFailed(g_hTest, "receiver thread %.6Rhxs got odd return value %Rrc! iFrame=%u cb=%'u c=%'u cLost=%'u\n",
+ &pArgs->Mac, rc, iFrame, cbReceived, iFrame - cLostFrames, cLostFrames);
+ return rc;
+ }
+
+ }
+}
+
+
+/**
+ * Drains the interface buffer before starting a new bi-directional run.
+ *
+ * We may have termination frames from previous runs pending in the buffer.
+ */
+static void tstDrainInterfaceBuffer(PMYARGS pArgs)
+{
+ uint8_t abBuf[16384 + 1024];
+ while (IntNetRingHasMoreToRead(&pArgs->pBuf->Recv))
+ IntNetRingReadAndSkipFrame(&pArgs->pBuf->Recv, abBuf);
+}
+
+
+/**
+ * Test state.
+ */
+typedef struct TSTSTATE
+{
+ PINTNETBUF pBuf0;
+ INTNETIFHANDLE hIf0;
+
+ PINTNETBUF pBuf1;
+ INTNETIFHANDLE hIf1;
+} TSTSTATE;
+typedef TSTSTATE *PTSTSTATE;
+
+
+/**
+ * Open two internal network interfaces.
+ *
+ * @returns IPRT status of the first failure.
+ * @param pThis The test instance.
+ */
+static int tstOpenInterfaces(PTSTSTATE pThis, const char *pszNetwork, uint32_t cbSend, uint32_t cbRecv)
+{
+ pThis->hIf0 = INTNET_HANDLE_INVALID;
+ RTTESTI_CHECK_RC_OK_RET(IntNetR0Open(g_pSession, pszNetwork, kIntNetTrunkType_None, "", 0/*fFlags*/, cbSend, cbRecv,
+ NULL /*pfnRecvAvail*/, NULL /*pvUser*/, &pThis->hIf0), rcCheck);
+ RTTESTI_CHECK_RET(pThis->hIf0 != INTNET_HANDLE_INVALID, VERR_INTERNAL_ERROR);
+ RTTESTI_CHECK_RC_RET(IntNetR0IfGetBufferPtrs(pThis->hIf0, g_pSession, &pThis->pBuf0, NULL), VINF_SUCCESS, rcCheck);
+ RTTESTI_CHECK_RET(pThis->pBuf0, VERR_INTERNAL_ERROR);
+
+
+ pThis->hIf1 = INTNET_HANDLE_INVALID;
+ RTTESTI_CHECK_RC_OK_RET(IntNetR0Open(g_pSession, pszNetwork, kIntNetTrunkType_None, "", 0/*fFlags*/, cbSend, cbRecv,
+ NULL /*pfnRecvAvail*/, NULL /*pvUser*/, &pThis->hIf1), rcCheck);
+ RTTESTI_CHECK_RET(pThis->hIf1 != INTNET_HANDLE_INVALID, VERR_INTERNAL_ERROR);
+ RTTESTI_CHECK_RC_RET(IntNetR0IfGetBufferPtrs(pThis->hIf1, g_pSession, &pThis->pBuf1, NULL), VINF_SUCCESS, rcCheck);
+ RTTESTI_CHECK_RET(pThis->pBuf1, VERR_INTERNAL_ERROR);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * Close the interfaces.
+ *
+ * @param pThis The test instance.
+ */
+static void tstCloseInterfaces(PTSTSTATE pThis)
+{
+ int rc;
+ RTTESTI_CHECK_RC_OK(rc = IntNetR0IfClose(pThis->hIf0, g_pSession));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->hIf0 = INTNET_HANDLE_INVALID;
+ pThis->pBuf0 = NULL;
+ }
+
+ RTTESTI_CHECK_RC_OK(rc = IntNetR0IfClose(pThis->hIf1, g_pSession));
+ if (RT_SUCCESS(rc))
+ {
+ pThis->hIf1 = INTNET_HANDLE_INVALID;
+ pThis->pBuf1 = NULL;
+ }
+
+ /* The network should be dead now. */
+ RTTESTI_CHECK(IntNetR0GetNetworkCount() == 0);
+}
+
+/**
+ * Do the bi-directional transfer test.
+ */
+static void tstBidirectionalTransfer(PTSTSTATE pThis, uint32_t cbFrame)
+{
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS, "-------------------------------------------------------------\n");
+
+ /*
+ * Reset statistics.
+ */
+ pThis->pBuf0->cStatYieldsOk.c = 0;
+ pThis->pBuf0->cStatYieldsNok.c = 0;
+ pThis->pBuf0->cStatLost.c = 0;
+ pThis->pBuf0->cStatBadFrames.c = 0;
+ pThis->pBuf0->Recv.cStatFrames.c = 0;
+ pThis->pBuf0->Recv.cbStatWritten.c = 0;
+ pThis->pBuf0->Recv.cOverflows.c = 0;
+ pThis->pBuf0->Send.cStatFrames.c = 0;
+ pThis->pBuf0->Send.cbStatWritten.c = 0;
+ pThis->pBuf0->Send.cOverflows.c = 0;
+ pThis->pBuf1->cStatYieldsOk.c = 0;
+ pThis->pBuf1->cStatYieldsNok.c = 0;
+ pThis->pBuf1->cStatLost.c = 0;
+ pThis->pBuf1->cStatBadFrames.c = 0;
+ pThis->pBuf1->Recv.cStatFrames.c = 0;
+ pThis->pBuf1->Recv.cbStatWritten.c = 0;
+ pThis->pBuf1->Recv.cOverflows.c = 0;
+ pThis->pBuf1->Send.cStatFrames.c = 0;
+ pThis->pBuf1->Send.cbStatWritten.c = 0;
+ pThis->pBuf1->Send.cOverflows.c = 0;
+
+ /*
+ * Do the benchmarking.
+ */
+ MYARGS Args0;
+ RT_ZERO(Args0);
+ Args0.hIf = pThis->hIf0;
+ Args0.pBuf = pThis->pBuf0;
+ Args0.Mac.au16[0] = 0x8086;
+ Args0.Mac.au16[1] = 0;
+ Args0.Mac.au16[2] = 0;
+ Args0.cbFrame = cbFrame;
+ tstDrainInterfaceBuffer(&Args0);
+ //RTTESTI_CHECK_RC_OK(IntNetR0IfSetMacAddress(pThis->hIf0, g_pSession, &Args0.Mac));
+
+ MYARGS Args1;
+ RT_ZERO(Args1);
+ Args1.hIf = pThis->hIf1;
+ Args1.pBuf = pThis->pBuf1;
+ Args1.Mac.au16[0] = 0x8086;
+ Args1.Mac.au16[1] = 0;
+ Args1.Mac.au16[2] = 1;
+ Args1.cbFrame = cbFrame;
+ tstDrainInterfaceBuffer(&Args1);
+ //RTTESTI_CHECK_RC_OK(IntNetR0IfSetMacAddress(pThis->hIf1, g_pSession, &Args1.Mac));
+
+ RTTHREAD ThreadRecv0 = NIL_RTTHREAD;
+ RTTHREAD ThreadRecv1 = NIL_RTTHREAD;
+ RTTHREAD ThreadSend0 = NIL_RTTHREAD;
+ RTTHREAD ThreadSend1 = NIL_RTTHREAD;
+ RTTESTI_CHECK_RC_OK_RETV(RTThreadCreate(&ThreadRecv0, ReceiveThread, &Args0, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "RECV0"));
+ RTTESTI_CHECK_RC_OK_RETV(RTThreadCreate(&ThreadRecv1, ReceiveThread, &Args1, 0, RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "RECV1"));
+ RTTESTI_CHECK_RC_OK_RETV(RTThreadCreate(&ThreadSend0, SendThread, &Args0, 0, RTTHREADTYPE_EMULATION, RTTHREADFLAGS_WAITABLE, "SEND0"));
+ RTTESTI_CHECK_RC_OK_RETV(RTThreadCreate(&ThreadSend1, SendThread, &Args1, 0, RTTHREADTYPE_EMULATION, RTTHREADFLAGS_WAITABLE, "SEND1"));
+
+ int rc2 = VINF_SUCCESS;
+ int rc;
+ RTTESTI_CHECK_RC_OK(rc = RTThreadWait(ThreadSend0, RT_MS_5MIN, &rc2));
+ if (RT_SUCCESS(rc))
+ {
+ RTTESTI_CHECK_RC_OK(rc2);
+ ThreadSend0 = NIL_RTTHREAD;
+ RTTESTI_CHECK_RC_OK(rc = RTThreadWait(ThreadSend1, RT_MS_5MIN, RT_SUCCESS(rc2) ? &rc2 : NULL));
+ if (RT_SUCCESS(rc))
+ {
+ ThreadSend1 = NIL_RTTHREAD;
+ RTTESTI_CHECK_RC_OK(rc2);
+ }
+ }
+ if (RTTestErrorCount(g_hTest) == 0)
+ {
+ /*
+ * Wait a bit for the receivers to finish up.
+ */
+ unsigned cYields = 100000;
+ while ( ( IntNetRingHasMoreToRead(&pThis->pBuf0->Recv)
+ || IntNetRingHasMoreToRead(&pThis->pBuf1->Recv))
+ && cYields-- > 0)
+ RTThreadYield();
+
+ /*
+ * Wait for the threads to finish up...
+ */
+ RTTESTI_CHECK_RC_OK(rc = RTThreadWait(ThreadRecv0, RT_MS_5SEC, &rc2));
+ if (RT_SUCCESS(rc))
+ {
+ RTTESTI_CHECK_RC_OK(rc2);
+ ThreadRecv0 = NIL_RTTHREAD;
+ }
+
+ RTTESTI_CHECK_RC_OK(rc = RTThreadWait(ThreadRecv1, RT_MS_5MIN, &rc2));
+ if (RT_SUCCESS(rc))
+ {
+ RTTESTI_CHECK_RC_OK(rc2);
+ ThreadRecv1 = NIL_RTTHREAD;
+ }
+
+ /*
+ * Report the results.
+ */
+ uint64_t cNsElapsed = RT_MAX(Args0.u64End, Args1.u64End) - RT_MIN(Args0.u64Start, Args1.u64Start);
+ uint64_t cbSent = (uint64_t)Args0.cbSent + Args1.cbSent;
+ uint64_t cKbps = (uint64_t)((double)(cbSent / 1024) / ((double)cNsElapsed / 1000000000.0));
+ uint64_t cFrames = (uint64_t)Args0.cFramesSent + Args1.cFramesSent;
+ uint64_t cFps = (uint64_t)((double)cFrames / ((double)cNsElapsed / 1000000000.0));
+ RTTestValue(g_hTest, "frame size", cbFrame, RTTESTUNIT_BYTES);
+ RTTestValue(g_hTest, "xmit time", cNsElapsed, RTTESTUNIT_NS);
+ RTTestValue(g_hTest, "bytes sent", cbSent, RTTESTUNIT_BYTES);
+ RTTestValue(g_hTest, "speed", cKbps, RTTESTUNIT_KILOBYTES_PER_SEC);
+ RTTestValue(g_hTest, "frames sent", cFrames, RTTESTUNIT_FRAMES);
+ RTTestValue(g_hTest, "fps", cFps, RTTESTUNIT_FRAMES_PER_SEC);
+ RTTestValue(g_hTest, "overflows",
+ pThis->pBuf0->Send.cOverflows.c + pThis->pBuf1->Send.cOverflows.c, RTTESTUNIT_OCCURRENCES);
+
+ }
+
+ /*
+ * Give them a chance to complete...
+ */
+ RTThreadWait(ThreadRecv0, RT_MS_5MIN, NULL);
+ RTThreadWait(ThreadRecv1, RT_MS_5MIN, NULL);
+ RTThreadWait(ThreadSend0, RT_MS_5MIN, NULL);
+ RTThreadWait(ThreadSend1, RT_MS_5MIN, NULL);
+
+
+ /*
+ * Display statistics.
+ */
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Buf0: Yields-OK=%llu Yields-NOK=%llu Lost=%llu Bad=%llu\n",
+ pThis->pBuf0->cStatYieldsOk.c,
+ pThis->pBuf0->cStatYieldsNok.c,
+ pThis->pBuf0->cStatLost.c,
+ pThis->pBuf0->cStatBadFrames.c);
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Buf0.Recv: Frames=%llu Bytes=%llu Overflows=%llu\n",
+ pThis->pBuf0->Recv.cStatFrames.c,
+ pThis->pBuf0->Recv.cbStatWritten.c,
+ pThis->pBuf0->Recv.cOverflows.c);
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Buf0.Send: Frames=%llu Bytes=%llu Overflows=%llu\n",
+ pThis->pBuf0->Send.cStatFrames.c,
+ pThis->pBuf0->Send.cbStatWritten.c,
+ pThis->pBuf0->Send.cOverflows.c);
+
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Buf1: Yields-OK=%llu Yields-NOK=%llu Lost=%llu Bad=%llu\n",
+ pThis->pBuf1->cStatYieldsOk.c,
+ pThis->pBuf1->cStatYieldsNok.c,
+ pThis->pBuf1->cStatLost.c,
+ pThis->pBuf1->cStatBadFrames.c);
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Buf1.Recv: Frames=%llu Bytes=%llu Overflows=%llu\n",
+ pThis->pBuf1->Recv.cStatFrames.c,
+ pThis->pBuf1->Recv.cbStatWritten.c,
+ pThis->pBuf1->Recv.cOverflows.c);
+ RTTestPrintf(g_hTest, RTTESTLVL_ALWAYS,
+ "Buf1.Send: Frames=%llu Bytes=%llu Overflows=%llu\n",
+ pThis->pBuf1->Send.cStatFrames.c,
+ pThis->pBuf1->Send.cbStatWritten.c,
+ pThis->pBuf1->Send.cOverflows.c);
+
+}
+
+/**
+ * Performs a simple broadcast test.
+ *
+ * @param pThis The test instance.
+ * @param fHeadGuard Whether to use a head or tail guard.
+ */
+static void doBroadcastTest(PTSTSTATE pThis, bool fHeadGuard)
+{
+ static uint16_t const s_au16Frame[7] = { /* dst:*/ 0xffff, 0xffff, 0xffff, /*src:*/0x8086, 0, 0, 0x0800 };
+
+ RTTESTI_CHECK_RC_RETV(tstIntNetSendBuf(&pThis->pBuf0->Send, pThis->hIf0,
+ g_pSession, &s_au16Frame, sizeof(s_au16Frame)),
+ VINF_SUCCESS);
+
+ /* No echo, please */
+ RTTESTI_CHECK_RC_RETV(IntNetR0IfWait(pThis->hIf0, g_pSession, 1), VERR_TIMEOUT);
+
+ /* The other interface should see it though. But Wait should only return once, thank you. */
+ RTTESTI_CHECK_RC_RETV(IntNetR0IfWait(pThis->hIf1, g_pSession, 1), VINF_SUCCESS);
+ RTTESTI_CHECK_RC_RETV(IntNetR0IfWait(pThis->hIf1, g_pSession, 0), VERR_TIMEOUT);
+
+ /* Receive the data. */
+ const unsigned cbExpect = RT_ALIGN(sizeof(s_au16Frame) + sizeof(INTNETHDR), sizeof(INTNETHDR));
+ RTTESTI_CHECK_MSG(IntNetRingGetReadable(&pThis->pBuf1->Recv) == cbExpect,
+ ("%#x vs. %#x\n", IntNetRingGetReadable(&pThis->pBuf1->Recv), cbExpect));
+
+ void *pvBuf;
+ RTTESTI_CHECK_RC_OK_RETV(RTTestGuardedAlloc(g_hTest, sizeof(s_au16Frame), 1, fHeadGuard, &pvBuf));
+ uint32_t cb;
+ RTTESTI_CHECK_MSG_RETV((cb = IntNetRingReadAndSkipFrame(&pThis->pBuf1->Recv, pvBuf)) == sizeof(s_au16Frame),
+ ("%#x vs. %#x\n", cb, sizeof(s_au16Frame)));
+
+ if (memcmp(pvBuf, &s_au16Frame, sizeof(s_au16Frame)))
+ RTTestIFailed("Got invalid data!\n"
+ "received: %.*Rhxs\n"
+ "expected: %.*Rhxs\n",
+ cb, pvBuf, sizeof(s_au16Frame), &s_au16Frame);
+}
+
+/**
+ * Performs a simple unicast test.
+ *
+ * @param pThis The test instance.
+ * @param fHeadGuard Whether to use a head or tail guard.
+ */
+static void doUnicastTest(PTSTSTATE pThis, bool fHeadGuard)
+{
+ static uint16_t const s_au16Frame[7] = { /* dst:*/ 0x8086, 0, 0, /*src:*/0x8086, 0, 1, 0x0800 };
+
+ RTTESTI_CHECK_RC_RETV(tstIntNetSendBuf(&pThis->pBuf1->Send, pThis->hIf1,
+ g_pSession, s_au16Frame, sizeof(s_au16Frame)),
+ VINF_SUCCESS);
+
+ /* No echo, please */
+ RTTESTI_CHECK_RC_RETV(IntNetR0IfWait(pThis->hIf1, g_pSession, 1), VERR_TIMEOUT);
+
+ /* The other interface should see it though. But Wait should only return once, thank you. */
+ RTTESTI_CHECK_RC_RETV(IntNetR0IfWait(pThis->hIf0, g_pSession, 1), VINF_SUCCESS);
+ RTTESTI_CHECK_RC_RETV(IntNetR0IfWait(pThis->hIf0, g_pSession, 0), VERR_TIMEOUT);
+
+ /* Receive the data. */
+ const unsigned cbExpect = RT_ALIGN(sizeof(s_au16Frame) + sizeof(INTNETHDR), sizeof(INTNETHDR));
+ RTTESTI_CHECK_MSG(IntNetRingGetReadable(&pThis->pBuf0->Recv) == cbExpect,
+ ("%#x vs. %#x\n", IntNetRingGetReadable(&pThis->pBuf0->Recv), cbExpect));
+
+ void *pvBuf;
+ RTTESTI_CHECK_RC_OK_RETV(RTTestGuardedAlloc(g_hTest, sizeof(s_au16Frame), 1, fHeadGuard, &pvBuf));
+ uint32_t cb;
+ RTTESTI_CHECK_MSG_RETV((cb = IntNetRingReadAndSkipFrame(&pThis->pBuf0->Recv, pvBuf)) == sizeof(s_au16Frame),
+ ("%#x vs. %#x\n", cb, sizeof(s_au16Frame)));
+
+ if (memcmp(pvBuf, &s_au16Frame, sizeof(s_au16Frame)))
+ RTTestIFailed("Got invalid data!\n"
+ "received: %.*Rhxs\n"
+ "expected: %.*Rhxs\n",
+ cb, pvBuf, sizeof(s_au16Frame), s_au16Frame);
+}
+
+static void doTest(PTSTSTATE pThis, uint32_t cbRecv, uint32_t cbSend)
+{
+
+ /*
+ * Create an INTNET instance.
+ */
+ RTTestISub("IntNetR0Init");
+ RTTESTI_CHECK_RC_RETV(IntNetR0Init(), VINF_SUCCESS);
+
+ /*
+ * Create two interfaces and activate them.
+ */
+ RTTestISub("Network creation");
+ int rc = tstOpenInterfaces(pThis, "test", cbSend, cbRecv);
+ if (RT_FAILURE(rc))
+ return;
+ RTTESTI_CHECK_RC(IntNetR0IfSetActive(pThis->hIf0, g_pSession, true), VINF_SUCCESS);
+ RTTESTI_CHECK_RC(IntNetR0IfSetActive(pThis->hIf1, g_pSession, true), VINF_SUCCESS);
+
+ /*
+ * Test basic waiting.
+ */
+ RTTestISub("IntNetR0IfWait");
+ RTTESTI_CHECK_RC(IntNetR0IfWait(pThis->hIf0, g_pSession, 1), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(IntNetR0IfWait(pThis->hIf0, g_pSession, 0), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(IntNetR0IfWait(pThis->hIf1, g_pSession, 1), VERR_TIMEOUT);
+ RTTESTI_CHECK_RC(IntNetR0IfWait(pThis->hIf1, g_pSession, 0), VERR_TIMEOUT);
+
+ /*
+ * Broadcast send and receive.
+ * (This establishes the MAC address of the 1st interface.)
+ */
+ RTTestISub("Broadcast");
+ doBroadcastTest(pThis, false /*fHeadGuard*/);
+ doBroadcastTest(pThis, true /*fHeadGuard*/);
+
+ /*
+ * Unicast send and receive.
+ * (This establishes the MAC address of the 2nd interface.)
+ */
+ RTTestISub("Unicast");
+ doUnicastTest(pThis, false /*fHeadGuard*/);
+ doUnicastTest(pThis, true /*fHeadGuard*/);
+
+ /*
+ * Do the big bi-directional transfer test if the basics worked out.
+ */
+ if (!RTTestIErrorCount())
+ {
+ RTTestISubF("bi-dir benchmark, xbuf=%u rbuf=%u xfer=%u",
+ pThis->pBuf0->cbSend, pThis->pBuf0->cbRecv, g_cbTransfer);
+ tstBidirectionalTransfer(pThis, 256);
+
+ /* Only doing up to half the xmit buffer size as it is easy to get into a
+ bad frame position from a previous run and run into overflow issues. */
+ /** @todo fix the code so it skips to a more optimal buffer position? */
+ for (uint32_t cbFrame = 64; cbFrame < cbSend / 2 - 64; cbFrame += 16)
+ {
+ RTTestISubF("bi-dir benchmark, xbuf=%u rbuf=%u xmit=%u frm=%u",
+ pThis->pBuf0->cbSend, pThis->pBuf0->cbRecv, g_cbTransfer, cbFrame);
+ tstBidirectionalTransfer(pThis, cbFrame);
+ }
+ }
+
+ /*
+ * Destroy the service.
+ */
+ tstCloseInterfaces(pThis);
+ IntNetR0Term();
+}
+
+
+int main(int argc, char **argv)
+{
+ int rc = RTTestInitAndCreate("tstIntNetR0", &g_hTest);
+ if (rc)
+ return rc;
+
+ /*
+ * Parse the arguments.
+ */
+ static RTGETOPTDEF const s_aOptions[] =
+ {
+ { "--recv-buffer", 'r', RTGETOPT_REQ_UINT32 },
+ { "--send-buffer", 's', RTGETOPT_REQ_UINT32 },
+ { "--transfer-size", 'l', RTGETOPT_REQ_UINT32 },
+ };
+
+ uint32_t cbSend = 1536*2 + 4;
+ uint32_t cbRecv = 0x8000;
+
+ int ch;
+ RTGETOPTUNION Value;
+ RTGETOPTSTATE GetState;
+ RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
+ while ((ch = RTGetOpt(&GetState, &Value)))
+ switch (ch)
+ {
+ case 'l':
+ g_cbTransfer = Value.u32;
+ break;
+
+ case 'r':
+ cbRecv = Value.u32;
+ break;
+
+ case 's':
+ cbSend = Value.u32;
+ break;
+
+ default:
+ return RTGetOptPrintError(ch, &Value);
+ }
+
+ /*
+ * Do the testing and report summary.
+ */
+ TSTSTATE This;
+ RT_ZERO(This);
+ doTest(&This, cbRecv, cbSend);
+
+ return RTTestSummaryAndDestroy(g_hTest);
+}
+