diff -Nru libslirp-4.6.1/CHANGELOG.md libslirp-4.7.0/CHANGELOG.md --- libslirp-4.6.1/CHANGELOG.md 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/CHANGELOG.md 2022-04-26 09:50:57.000000000 +0000 @@ -5,6 +5,27 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.7.0] - 2022-04-26 + +### Added + + - Allow disabling the internal DHCP server !22 + - icmp: Support falling back on trying a SOCK_RAW socket !92 + - Support Unix sockets in hostfwd !103 + - IPv6 DNS proxying support !110 + - bootp: add support for UEFI HTTP boot !111 + - New callback that supports CFI better !117 + +### Fixed + + - dhcp: Always send DHCP_OPT_LEN bytes in options !97 + - Fix Haiku build !98 !99 + - Fix memory leak when using libresolv !100 + - Ensure sin6_scope_id is zero for global addresses !102 + - resolv: fix IPv6 resolution on Darwin !104 + - socket: Initialize so_type in socreate !109 + - Handle ECONNABORTED from recv !116 + ## [4.6.1] - 2021-06-18 ### Fixed @@ -172,7 +193,8 @@ - Standalone project, removing any QEMU dependency. - License clarifications. -[Unreleased]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.6.1...master +[Unreleased]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.7.0...master +[4.7.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.6.1...v4.7.0 [4.6.1]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.6.0...v4.6.1 [4.6.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.5.0...v4.6.0 [4.5.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.4.0...v4.5.0 diff -Nru libslirp-4.6.1/debian/changelog libslirp-4.7.0/debian/changelog --- libslirp-4.6.1/debian/changelog 2022-03-24 16:13:49.000000000 +0000 +++ libslirp-4.7.0/debian/changelog 2022-04-30 21:13:57.000000000 +0000 @@ -1,8 +1,11 @@ -libslirp (4.6.1-1build1) jammy; urgency=high +libslirp (4.7.0-1) unstable; urgency=medium - * No change rebuild for ppc64el baseline bump. + * new upstream release + * libslirp0.symbols: add slirp_handle_timer + * add gbp.conf (list just upstream-vcs-tag=); + switched to default gbp naming scheme in tags and branches - -- Julian Andres Klode Thu, 24 Mar 2022 17:13:49 +0100 + -- Michael Tokarev Sun, 01 May 2022 00:13:57 +0300 libslirp (4.6.1-1) unstable; urgency=medium diff -Nru libslirp-4.6.1/debian/control libslirp-4.7.0/debian/control --- libslirp-4.6.1/debian/control 2022-03-24 16:13:49.000000000 +0000 +++ libslirp-4.7.0/debian/control 2022-04-30 21:01:53.000000000 +0000 @@ -1,8 +1,7 @@ Source: libslirp Section: net Priority: optional -Maintainer: Ubuntu Developers -XSBC-Original-Maintainer: Debian QEMU Team +Maintainer: Debian QEMU Team Uploaders: Michael Tokarev Build-Depends: debhelper-compat (= 13), libglib2.0-dev, meson, ninja-build Standards-Version: 4.6.0 diff -Nru libslirp-4.6.1/debian/gbp.conf libslirp-4.7.0/debian/gbp.conf --- libslirp-4.6.1/debian/gbp.conf 1970-01-01 00:00:00.000000000 +0000 +++ libslirp-4.7.0/debian/gbp.conf 2022-04-30 21:12:25.000000000 +0000 @@ -0,0 +1,2 @@ +[DEFAULT] +upstream-vcs-tag = v%(version)s diff -Nru libslirp-4.6.1/debian/libslirp0.symbols libslirp-4.7.0/debian/libslirp0.symbols --- libslirp-4.6.1/debian/libslirp0.symbols 2021-08-19 12:40:39.000000000 +0000 +++ libslirp-4.7.0/debian/libslirp0.symbols 2022-04-30 21:03:40.000000000 +0000 @@ -4,6 +4,7 @@ SLIRP_4.1@SLIRP_4.1 4.1.0 SLIRP_4.2@SLIRP_4.2 4.2.0 SLIRP_4.5@SLIRP_4.5 4.5.0 + SLIRP_4.7@SLIRP_4.7 4.7.0 slirp_add_exec@SLIRP_4.0 4.0.0 slirp_add_guestfwd@SLIRP_4.0 4.0.0 slirp_add_hostfwd@SLIRP_4.0 4.0.0 @@ -11,6 +12,7 @@ slirp_add_unix@SLIRP_4.2 4.2.0 slirp_cleanup@SLIRP_4.0 4.0.0 slirp_connection_info@SLIRP_4.0 4.0.0 + slirp_handle_timer@SLIRP_4.7 4.7.0 slirp_init@SLIRP_4.0 4.0.0 slirp_input@SLIRP_4.0 4.0.0 slirp_neighbor_info@SLIRP_4.5 4.5.0 diff -Nru libslirp-4.6.1/.gitlab-ci.yml libslirp-4.7.0/.gitlab-ci.yml --- libslirp-4.6.1/.gitlab-ci.yml 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/.gitlab-ci.yml 2022-04-26 09:50:57.000000000 +0000 @@ -2,7 +2,7 @@ variables: DEPS: meson ninja-build - gcc pkg-config glib2-devel + gcc libasan liblsan libubsan pkg-config glib2-devel mingw64-gcc mingw64-pkg-config mingw64-glib2 clang-analyzer git-core @@ -18,6 +18,24 @@ - (cd build && meson test) || (cat build/meson-logs/testlog.txt && exit 1) - ninja -C build scan-build +build-asan: + script: + - CFLAGS=-fsanitize=address meson --werror build || (cat build/meson-logs/meson-log.txt && exit 1) + - ninja -C build + - (cd build && ASAN_OPTIONS=detect_leaks=0 meson test) || (cat build/meson-logs/testlog.txt && exit 1) + +build-lsan: + script: + - CFLAGS=-fsanitize=leak meson --werror build || (cat build/meson-logs/meson-log.txt && exit 1) + - ninja -C build + - (cd build && meson test) || (cat build/meson-logs/testlog.txt && exit 1) + +build-usan: + script: + - CFLAGS=-fsanitize=undefined meson --werror build || (cat build/meson-logs/meson-log.txt && exit 1) + - ninja -C build + - (cd build && meson test) || (cat build/meson-logs/testlog.txt && exit 1) + build-mingw64: script: - (mkdir buildw && cd buildw && mingw64-meson --werror) || (cat buildw/meson-logs/meson-log.txt && exit 1) @@ -41,3 +59,36 @@ --form token=$COVERITY_SCAN_TOKEN --form email=$GITLAB_USER_EMAIL --form file=@cov-int.tar.gz --form version="`git describe --tags`" --form description="`git describe --tags` / $CI_COMMIT_TITLE / $CI_COMMIT_REF_NAME:$CI_PIPELINE_ID " + +integration-slirp4netns: + variables: + SLIRP4NETNS_VERSION: "v1.1.12" + # Consumed by `make benchmark` + BENCHMARK_IPERF3_DURATION: "10" + script: + # Install libslirp + - meson build + - ninja -C build install + # Register the path of libslirp.so.0 + - echo /usr/local/lib64 >/etc/ld.so.conf.d/libslirp.conf + - ldconfig + # Install the dependencies of slirp4netns and its test suite + # TODO: install udhcpc for `slirp4netns/tests/test-slirp4netns-dhcp.sh` (currently skipped, due to lack of udhcpc) + - dnf install -y autoconf automake findutils iperf3 iproute iputils jq libcap-devel libseccomp-devel nmap-ncat util-linux + # Check whether the runner environment is configured correctly + - unshare -rn true || (echo Make sure you have relaxed seccomp and appamor && exit 1) + - unshare -rn ip tap add tap0 mode tap || (echo Make sure you have /dev/net/tun && exit 1) + # Install slirp4netns + - git clone https://github.com/rootless-containers/slirp4netns -b "${SLIRP4NETNS_VERSION}" + - cd slirp4netns + - ./autogen.sh + - ./configure + - make + - make install + - slirp4netns --version + # Run slirp4netns integration test + - make distcheck || (cat $(find . -name 'test-suite.log' ) && exit 1) + # Run benchmark test to ensure that libslirp can actually handle packets, with several MTU configurations + - make benchmark MTU=1500 + - make benchmark MTU=512 + - make benchmark MTU=65520 diff -Nru libslirp-4.6.1/meson.build libslirp-4.7.0/meson.build --- libslirp-4.6.1/meson.build 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/meson.build 2022-04-26 09:50:57.000000000 +0000 @@ -1,5 +1,5 @@ project('libslirp', 'c', - version : '4.6.1', + version : '4.7.0', license : 'BSD-3-Clause', default_options : ['warning_level=1', 'c_std=gnu99'], meson_version : '>= 0.50', @@ -44,9 +44,9 @@ # - If the interface is the same as the previous version, but bugs are # fixed, change: # REVISION += 1 -lt_current = 3 -lt_revision = 1 -lt_age = 3 +lt_current = 4 +lt_revision = 0 +lt_age = 4 lt_version = '@0@.@1@.@2@'.format(lt_current - lt_age, lt_age, lt_revision) host_system = host_machine.system() @@ -136,6 +136,14 @@ install : install_devel or get_option('default_library') == 'shared', ) +pingtest = executable('pingtest', 'test/pingtest.c', + link_with: [ lib ], + include_directories: [ 'src' ], + dependencies : [ platform_deps ] +) + +test('ping', pingtest) + if install_devel install_headers(['src/libslirp.h'], subdir : 'slirp') diff -Nru libslirp-4.6.1/src/arp_table.c libslirp-4.7.0/src/arp_table.c --- libslirp-4.6.1/src/arp_table.c 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/arp_table.c 2022-04-26 09:50:57.000000000 +0000 @@ -35,9 +35,11 @@ ArpTable *arptbl = &slirp->arp_table; int i; char ethaddr_str[ETH_ADDRSTRLEN]; + char addr[INET_ADDRSTRLEN]; DEBUG_CALL("arp_table_add"); - DEBUG_ARG("ip = %s", inet_ntoa((struct in_addr){ .s_addr = ip_addr })); + DEBUG_ARG("ip = %s", inet_ntop(AF_INET, &(struct in_addr){ .s_addr = ip_addr }, + addr, sizeof(addr))); DEBUG_ARG("hw addr = %s", slirp_ether_ntoa(ethaddr, ethaddr_str, sizeof(ethaddr_str))); @@ -69,9 +71,11 @@ ArpTable *arptbl = &slirp->arp_table; int i; char ethaddr_str[ETH_ADDRSTRLEN]; + char addr[INET_ADDRSTRLEN]; DEBUG_CALL("arp_table_search"); - DEBUG_ARG("ip = %s", inet_ntoa((struct in_addr){ .s_addr = ip_addr })); + DEBUG_ARG("ip = %s", inet_ntop(AF_INET, &(struct in_addr){ .s_addr = ip_addr }, + addr, sizeof(addr))); /* If broadcast address */ if (ip_addr == 0 || ip_addr == 0xffffffff || ip_addr == broadcast_addr) { diff -Nru libslirp-4.6.1/src/bootp.c libslirp-4.7.0/src/bootp.c --- libslirp-4.6.1/src/bootp.c 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/bootp.c 2022-04-26 09:50:57.000000000 +0000 @@ -34,6 +34,8 @@ #define LEASE_TIME (24 * 3600) +#define UEFI_HTTP_VENDOR_CLASS_ID "HTTPClient" + static const uint8_t rfc1533_cookie[] = { RFC1533_COOKIE }; #define DPRINTF(fmt, ...) DEBUG_CALL(fmt, ##__VA_ARGS__) @@ -340,6 +342,27 @@ q += val; } } + + /* this allows to support UEFI HTTP boot: according to the UEFI + specification, DHCP server must send vendor class identifier option + set to "HTTPClient" string, when responding to DHCP requests as part + of the UEFI HTTP boot + + we assume that, if the bootfile parameter was configured as an http + URL, the user intends to perform UEFI HTTP boot, so send this option + automatically */ + if (slirp->bootp_filename && g_str_has_prefix(slirp->bootp_filename, "http://")) { + val = strlen(UEFI_HTTP_VENDOR_CLASS_ID); + if (q + val + 2 >= end) { + g_warning("DHCP packet size exceeded, " + "omitting vendor class id option."); + } else { + *q++ = RFC2132_VENDOR_CLASS_ID; + *q++ = val; + memcpy(q, UEFI_HTTP_VENDOR_CLASS_ID, val); + q += val; + } + } } else { static const char nak_msg[] = "requested address not available"; @@ -355,13 +378,13 @@ q += sizeof(nak_msg) - 1; } assert(q < end); - *q = RFC1533_END; + *q++ = RFC1533_END; daddr.sin_addr.s_addr = 0xffffffffu; - assert ((q - rbp->bp_vend + 1) <= DHCP_OPT_LEN); + assert(q <= end); - m->m_len = sizeof(struct bootp_t) + (q - rbp->bp_vend + 1) - sizeof(struct ip) - sizeof(struct udphdr); + m->m_len = sizeof(struct bootp_t) + (end - rbp->bp_vend) - sizeof(struct ip) - sizeof(struct udphdr); udp_output(NULL, m, &saddr, &daddr, IPTOS_LOWDELAY); } @@ -369,7 +392,7 @@ { struct bootp_t *bp = mtod_check(m, sizeof(struct bootp_t)); - if (bp && bp->bp_op == BOOTP_REQUEST) { + if (!m->slirp->disable_dhcp && bp && bp->bp_op == BOOTP_REQUEST) { bootp_reply(m->slirp, bp, m_end(m)); } } diff -Nru libslirp-4.6.1/src/bootp.h libslirp-4.7.0/src/bootp.h --- libslirp-4.6.1/src/bootp.h 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/bootp.h 2022-04-26 09:50:57.000000000 +0000 @@ -71,6 +71,7 @@ #define RFC2132_MAX_SIZE 57 #define RFC2132_RENEWAL_TIME 58 #define RFC2132_REBIND_TIME 59 +#define RFC2132_VENDOR_CLASS_ID 60 #define RFC2132_TFTP_SERVER_NAME 66 #define DHCPDISCOVER 1 diff -Nru libslirp-4.6.1/src/if.c libslirp-4.7.0/src/if.c --- libslirp-4.6.1/src/if.c 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/if.c 2022-04-26 09:50:57.000000000 +0000 @@ -56,7 +56,7 @@ * XXX Shouldn't need this, gotta change dtom() etc. */ if (ifm->m_flags & M_USEDLIST) { - remque(ifm); + slirp_remque(ifm); ifm->m_flags &= ~M_USEDLIST; } @@ -70,7 +70,8 @@ */ if (so) { for (ifq = (struct mbuf *)slirp->if_batchq.qh_rlink; - (struct quehead *)ifq != &slirp->if_batchq; ifq = ifq->ifq_prev) { + (struct slirp_quehead *)ifq != &slirp->if_batchq; + ifq = ifq->ifq_prev) { if (so == ifq->ifq_so) { /* A match! */ ifm->ifq_so = so; @@ -100,7 +101,7 @@ /* Create a new doubly linked list for this session */ ifm->ifq_so = so; ifs_init(ifm); - insque(ifm, ifq); + slirp_insque(ifm, ifq); diddit: if (so) { @@ -117,10 +118,10 @@ if (on_fastq && ((so->so_nqueued >= 6) && (so->so_nqueued - so->so_queued) >= 3)) { /* Remove from current queue... */ - remque(ifm->ifs_next); + slirp_remque(ifm->ifs_next); /* ...And insert in the new. That'll teach ya! */ - insque(ifm->ifs_next, &slirp->if_batchq); + slirp_insque(ifm->ifs_next, &slirp->if_batchq); } } @@ -171,12 +172,12 @@ ifm = ifm_next; ifm_next = ifm->ifq_next; - if ((struct quehead *)ifm_next == &slirp->if_fastq) { + if ((struct slirp_quehead *)ifm_next == &slirp->if_fastq) { /* No more packets in fastq, switch to batchq */ ifm_next = batch_head; from_batchq = true; } - if ((struct quehead *)ifm_next == &slirp->if_batchq) { + if ((struct slirp_quehead *)ifm_next == &slirp->if_batchq) { /* end of batchq */ ifm_next = NULL; } @@ -189,13 +190,13 @@ /* Remove it from the queue */ ifqt = ifm->ifq_prev; - remque(ifm); + slirp_remque(ifm); /* If there are more packets for this session, re-queue them */ if (ifm->ifs_next != ifm) { struct mbuf *next = ifm->ifs_next; - insque(next, ifqt); + slirp_insque(next, ifqt); ifs_remque(ifm); if (!from_batchq) { ifm_next = next; diff -Nru libslirp-4.6.1/src/ip6_icmp.c libslirp-4.7.0/src/ip6_icmp.c --- libslirp-4.6.1/src/ip6_icmp.c 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/ip6_icmp.c 2022-04-26 09:50:57.000000000 +0000 @@ -10,25 +10,14 @@ #define NDP_Interval \ g_rand_int_range(slirp->grand, NDP_MinRtrAdvInterval, NDP_MaxRtrAdvInterval) -static void ra_timer_handler(void *opaque) -{ - Slirp *slirp = opaque; - - slirp->cb->timer_mod(slirp->ra_timer, - slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS + - NDP_Interval, - slirp->opaque); - ndp_send_ra(slirp); -} - -void icmp6_init(Slirp *slirp) +void icmp6_post_init(Slirp *slirp) { if (!slirp->in6_enabled) { return; } slirp->ra_timer = - slirp->cb->timer_new(ra_timer_handler, slirp, slirp->opaque); + slirp_timer_new(slirp, SLIRP_TIMER_RA, NULL); slirp->cb->timer_mod(slirp->ra_timer, slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS + NDP_Interval, @@ -140,7 +129,7 @@ /* * Send NDP Router Advertisement */ -void ndp_send_ra(Slirp *slirp) +static void ndp_send_ra(Slirp *slirp) { DEBUG_CALL("ndp_send_ra"); @@ -219,6 +208,15 @@ ip6_output(NULL, t, 0); } +void ra_timer_handler(Slirp *slirp, void *unused) +{ + slirp->cb->timer_mod(slirp->ra_timer, + slirp->cb->clock_get_ns(slirp->opaque) / SCALE_MS + + NDP_Interval, + slirp->opaque); + ndp_send_ra(slirp); +} + /* * Send NDP Neighbor Solitication */ diff -Nru libslirp-4.6.1/src/ip6_icmp.h libslirp-4.7.0/src/ip6_icmp.h --- libslirp-4.6.1/src/ip6_icmp.h 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/ip6_icmp.h 2022-04-26 09:50:57.000000000 +0000 @@ -209,12 +209,12 @@ #define NDP_AdvPrefLifetime 14400 #define NDP_AdvAutonomousFlag 1 -void icmp6_init(Slirp *slirp); +void icmp6_post_init(Slirp *slirp); void icmp6_cleanup(Slirp *slirp); void icmp6_input(struct mbuf *); void icmp6_forward_error(struct mbuf *m, uint8_t type, uint8_t code, struct in6_addr *src); void icmp6_send_error(struct mbuf *m, uint8_t type, uint8_t code); -void ndp_send_ra(Slirp *slirp); void ndp_send_ns(Slirp *slirp, struct in6_addr addr); +void ra_timer_handler(Slirp *slirp, void *unused); #endif diff -Nru libslirp-4.6.1/src/ip6_input.c libslirp-4.7.0/src/ip6_input.c --- libslirp-4.6.1/src/ip6_input.c 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/ip6_input.c 2022-04-26 09:50:57.000000000 +0000 @@ -11,9 +11,9 @@ * IP initialization: fill in IP protocol switch table. * All protocols not implemented in kernel go to raw IP protocol handler. */ -void ip6_init(Slirp *slirp) +void ip6_post_init(Slirp *slirp) { - icmp6_init(slirp); + icmp6_post_init(slirp); } void ip6_cleanup(Slirp *slirp) diff -Nru libslirp-4.6.1/src/ip_icmp.c libslirp-4.7.0/src/ip_icmp.c --- libslirp-4.6.1/src/ip_icmp.c 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/ip_icmp.c 2022-04-26 09:50:57.000000000 +0000 @@ -91,8 +91,32 @@ struct ip *ip = mtod(m, struct ip *); struct sockaddr_in addr; + /* + * The behavior of reading SOCK_DGRAM+IPPROTO_ICMP sockets is inconsistent + * between host OSes. On Linux, only the ICMP header and payload is + * included. On macOS/Darwin, the socket acts like a raw socket and + * includes the IP header as well. On other BSDs, SOCK_DGRAM+IPPROTO_ICMP + * sockets aren't supported at all, so we treat them like raw sockets. It + * isn't possible to detect this difference at runtime, so we must use an + * #ifdef to determine if we need to remove the IP header. + */ +#ifdef CONFIG_BSD + so->so_type = IPPROTO_IP; +#else + so->so_type = IPPROTO_ICMP; +#endif + so->s = slirp_socket(AF_INET, SOCK_DGRAM, IPPROTO_ICMP); if (so->s == -1) { + if (errno == EAFNOSUPPORT + || errno == EPROTONOSUPPORT + || errno == EACCES) { + /* Kernel doesn't support or allow ping sockets. */ + so->so_type = IPPROTO_IP; + so->s = slirp_socket(AF_INET, SOCK_RAW, IPPROTO_ICMP); + } + } + if (so->s == -1) { return -1; } so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque); @@ -108,14 +132,13 @@ so->so_faddr = ip->ip_dst; so->so_laddr = ip->ip_src; so->so_iptos = ip->ip_tos; - so->so_type = IPPROTO_ICMP; so->so_state = SS_ISFCONNECTED; so->so_expire = curtime + SO_EXPIRE; addr.sin_family = AF_INET; addr.sin_addr = so->so_faddr; - insque(so, &so->slirp->icmp); + slirp_insque(so, &so->slirp->icmp); if (sendto(so->s, m->m_data + hlen, m->m_len - hlen, 0, (struct sockaddr *)&addr, sizeof(addr)) == -1) { @@ -184,7 +207,7 @@ struct sockaddr_storage addr; int ttl; - so = socreate(slirp); + so = socreate(slirp, IPPROTO_ICMP); if (icmp_send(so, m, hlen) == 0) { /* We could send this as ICMP, good! */ return; @@ -208,7 +231,6 @@ so->so_laddr = ip->ip_src; so->so_lport = htons(9); so->so_iptos = ip->ip_tos; - so->so_type = IPPROTO_ICMP; so->so_state = SS_ISFCONNECTED; /* Send the packet */ @@ -302,10 +324,12 @@ goto end_error; ip = mtod(msrc, struct ip *); if (slirp_debug & DBG_MISC) { - char bufa[20], bufb[20]; - slirp_pstrcpy(bufa, sizeof(bufa), inet_ntoa(ip->ip_src)); - slirp_pstrcpy(bufb, sizeof(bufb), inet_ntoa(ip->ip_dst)); - DEBUG_MISC(" %.16s to %.16s", bufa, bufb); + char addr_src[INET_ADDRSTRLEN]; + char addr_dst[INET_ADDRSTRLEN]; + + inet_ntop(AF_INET, &ip->ip_src, addr_src, sizeof(addr_src)); + inet_ntop(AF_INET, &ip->ip_dst, addr_dst, sizeof(addr_dst)); + DEBUG_MISC(" %.16s to %.16s", addr_src, addr_dst); } if (ip->ip_off & IP_OFFMASK) goto end_error; /* Only reply to fragment 0 */ @@ -478,31 +502,24 @@ id = icp->icmp_id; len = recv(so->s, icp, M_ROOM(m), 0); - /* - * The behavior of reading SOCK_DGRAM+IPPROTO_ICMP sockets is inconsistent - * between host OSes. On Linux, only the ICMP header and payload is - * included. On macOS/Darwin, the socket acts like a raw socket and - * includes the IP header as well. On other BSDs, SOCK_DGRAM+IPPROTO_ICMP - * sockets aren't supported at all, so we treat them like raw sockets. It - * isn't possible to detect this difference at runtime, so we must use an - * #ifdef to determine if we need to remove the IP header. - */ -#ifdef CONFIG_BSD - if (len >= sizeof(struct ip)) { - struct ip *inner_ip = mtod(m, struct ip *); - int inner_hlen = inner_ip->ip_hl << 2; - if (inner_hlen > len) { + + if (so->so_type == IPPROTO_IP) { + if (len >= sizeof(struct ip)) { + struct ip *inner_ip = mtod(m, struct ip *); + int inner_hlen = inner_ip->ip_hl << 2; + if (inner_hlen > len) { + len = -1; + errno = -EINVAL; + } else { + len -= inner_hlen; + memmove(icp, (unsigned char *)icp + inner_hlen, len); + } + } else { len = -1; errno = -EINVAL; - } else { - len -= inner_hlen; - memmove(icp, (unsigned char *)icp + inner_hlen, len); } - } else { - len = -1; - errno = -EINVAL; } -#endif + icp->icmp_id = id; m->m_data -= hlen; diff -Nru libslirp-4.6.1/src/ip_input.c libslirp-4.7.0/src/ip_input.c --- libslirp-4.6.1/src/ip_input.c 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/ip_input.c 2022-04-26 09:50:57.000000000 +0000 @@ -252,7 +252,7 @@ goto dropfrag; } fp = mtod(t, struct ipq *); - insque(&fp->ip_link, &slirp->ipq.ip_link); + slirp_insque(&fp->ip_link, &slirp->ipq.ip_link); fp->ipq_ttl = IPFRAGTTL; fp->ipq_p = ip->ip_p; fp->ipq_id = ip->ip_id; @@ -361,7 +361,7 @@ ip->ip_tos &= ~1; ip->ip_src = fp->ipq_src; ip->ip_dst = fp->ipq_dst; - remque(&fp->ip_link); + slirp_remque(&fp->ip_link); m_free(dtom(slirp, fp)); m->m_len += (ip->ip_hl << 2); m->m_data -= (ip->ip_hl << 2); @@ -387,13 +387,13 @@ ip_deq(q); m_free(dtom(slirp, q)); } - remque(&fp->ip_link); + slirp_remque(&fp->ip_link); m_free(dtom(slirp, fp)); } /* * Put an ip fragment on a reassembly chain. - * Like insque, but pointers in middle of structure. + * Like slirp_insque, but pointers in middle of structure. */ static void ip_enq(register struct ipasfrag *p, register struct ipasfrag *prev) { @@ -406,7 +406,7 @@ } /* - * To ip_enq as remque is to insque. + * To ip_enq as slirp_remque is to slirp_insque. */ static void ip_deq(register struct ipasfrag *p) { diff -Nru libslirp-4.6.1/src/libslirp.h libslirp-4.7.0/src/libslirp.h --- libslirp-4.6.1/src/libslirp.h 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/libslirp.h 2022-04-26 09:50:57.000000000 +0000 @@ -39,6 +39,11 @@ typedef int (*SlirpAddPollCb)(int fd, int events, void *opaque); typedef int (*SlirpGetREventsCb)(int idx, void *opaque); +typedef enum SlirpTimerId { + SLIRP_TIMER_RA, + SLIRP_TIMER_NUM, +} SlirpTimerId; + /* * Callbacks from slirp, to be set by the application. * @@ -58,7 +63,8 @@ void (*guest_error)(const char *msg, void *opaque); /* Return the virtual clock value in nanoseconds */ int64_t (*clock_get_ns)(void *opaque); - /* Create a new timer with the given callback and opaque data */ + /* Create a new timer with the given callback and opaque data. Not + * needed if timer_new_opaque is provided. */ void *(*timer_new)(SlirpTimerCb cb, void *cb_opaque, void *opaque); /* Remove and free a timer */ void (*timer_free)(void *timer, void *opaque); @@ -68,12 +74,23 @@ void (*register_poll_fd)(int fd, void *opaque); /* Unregister a fd */ void (*unregister_poll_fd)(int fd, void *opaque); - /* Kick the io-thread, to signal that new events may be processed */ + /* Kick the io-thread, to signal that new events may be processed because some TCP buffer + * can now receive more data, i.e. slirp_socket_can_recv will return 1. */ void (*notify)(void *opaque); + + /* + * Fields introduced in SlirpConfig version 4 begin + */ + + /* Initialization has completed and a Slirp* has been created. */ + void (*init_completed)(Slirp *slirp, void *opaque); + /* Create a new timer. When the timer fires, the application passes + * the SlirpTimerId and cb_opaque to slirp_handle_timer. */ + void *(*timer_new_opaque)(SlirpTimerId id, void *cb_opaque, void *opaque); } SlirpCb; #define SLIRP_CONFIG_VERSION_MIN 1 -#define SLIRP_CONFIG_VERSION_MAX 3 +#define SLIRP_CONFIG_VERSION_MAX 4 typedef struct SlirpConfig { /* Version must be provided */ @@ -119,6 +136,10 @@ * Fields introduced in SlirpConfig version 3 begin */ bool disable_dns; /* slirp will not redirect/serve any DNS packet */ + /* + * Fields introduced in SlirpConfig version 4 begin + */ + bool disable_dhcp; /* slirp will not reply to any DHCP requests */ } SlirpConfig; /* Create a new instance of a slirp stack */ @@ -161,6 +182,11 @@ * guest network, to be interpreted by slirp. */ void slirp_input(Slirp *slirp, const uint8_t *pkt, int pkt_len); +/* This is called by the application when a timer expires, if it provides + * the timer_new_opaque callback. It is not needed if the application only + * uses timer_new. */ +void slirp_handle_timer(Slirp *slirp, SlirpTimerId id, void *cb_opaque); + /* These set up / remove port forwarding between a host port in the real world * and the guest network. */ int slirp_add_hostfwd(Slirp *slirp, int is_udp, struct in_addr host_addr, diff -Nru libslirp-4.6.1/src/libslirp.map libslirp-4.7.0/src/libslirp.map --- libslirp-4.6.1/src/libslirp.map 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/libslirp.map 2022-04-26 09:50:57.000000000 +0000 @@ -34,3 +34,7 @@ slirp_remove_hostxfwd; slirp_neighbor_info; } SLIRP_4.2; + +SLIRP_4.7 { + slirp_handle_timer; +} SLIRP_4.5; diff -Nru libslirp-4.6.1/src/mbuf.c libslirp-4.7.0/src/mbuf.c --- libslirp-4.6.1/src/mbuf.c 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/mbuf.c 2022-04-26 09:50:57.000000000 +0000 @@ -29,12 +29,12 @@ slirp->m_usedlist.qh_link = slirp->m_usedlist.qh_rlink = &slirp->m_usedlist; } -static void m_cleanup_list(struct quehead *list_head) +static void m_cleanup_list(struct slirp_quehead *list_head) { struct mbuf *m, *next; m = (struct mbuf *)list_head->qh_link; - while ((struct quehead *)m != list_head) { + while ((struct slirp_quehead *)m != list_head) { next = m->m_next; if (m->m_flags & M_EXT) { g_free(m->m_ext); @@ -77,11 +77,11 @@ m->slirp = slirp; } else { m = (struct mbuf *)slirp->m_freelist.qh_link; - remque(m); + slirp_remque(m); } /* Insert it in the used list */ - insque(m, &slirp->m_usedlist); + slirp_insque(m, &slirp->m_usedlist); m->m_flags = (flags | M_USEDLIST); /* Initialise it */ @@ -104,7 +104,7 @@ if (m) { /* Remove from m_usedlist */ if (m->m_flags & M_USEDLIST) - remque(m); + slirp_remque(m); /* If it's M_EXT, free() it */ if (m->m_flags & M_EXT) { @@ -118,7 +118,7 @@ m->slirp->mbuf_alloced--; g_free(m); } else if ((m->m_flags & M_FREELIST) == 0) { - insque(m, &m->slirp->m_freelist); + slirp_insque(m, &m->slirp->m_freelist); m->m_flags = M_FREELIST; /* Clobber other flags */ } } /* if(m) */ @@ -213,7 +213,7 @@ /* bug corrected for M_EXT buffers */ for (m = (struct mbuf *)slirp->m_usedlist.qh_link; - (struct quehead *)m != &slirp->m_usedlist; m = m->m_next) { + (struct slirp_quehead *)m != &slirp->m_usedlist; m = m->m_next) { if (m->m_flags & M_EXT) { if ((char *)dat >= m->m_ext && (char *)dat < (m->m_ext + m->m_size)) return m; diff -Nru libslirp-4.6.1/src/mbuf.h libslirp-4.7.0/src/mbuf.h --- libslirp-4.6.1/src/mbuf.h 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/mbuf.h 2022-04-26 09:50:57.000000000 +0000 @@ -134,7 +134,7 @@ ifm->ifs_next = ifm->ifs_prev = ifm; } -#ifdef DEBUG +#ifdef SLIRP_DEBUG # define MBUF_DEBUG 1 #else # ifdef HAVE_VALGRIND diff -Nru libslirp-4.6.1/src/misc.c libslirp-4.7.0/src/misc.c --- libslirp-4.6.1/src/misc.c 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/misc.c 2022-04-26 09:50:57.000000000 +0000 @@ -8,22 +8,22 @@ #include #endif -inline void insque(void *a, void *b) +inline void slirp_insque(void *a, void *b) { - register struct quehead *element = (struct quehead *)a; - register struct quehead *head = (struct quehead *)b; + register struct slirp_quehead *element = (struct slirp_quehead *)a; + register struct slirp_quehead *head = (struct slirp_quehead *)b; 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; + head->qh_link = (struct slirp_quehead *)element; + element->qh_rlink = (struct slirp_quehead *)head; + ((struct slirp_quehead *)(element->qh_link))->qh_rlink = + (struct slirp_quehead *)element; } -inline void remque(void *a) +inline void slirp_remque(void *a) { - register struct quehead *element = (struct quehead *)a; - ((struct quehead *)(element->qh_link))->qh_rlink = element->qh_rlink; - ((struct quehead *)(element->qh_rlink))->qh_link = element->qh_link; + register struct slirp_quehead *element = (struct slirp_quehead *)a; + ((struct slirp_quehead *)(element->qh_link))->qh_rlink = element->qh_rlink; + ((struct slirp_quehead *)(element->qh_rlink))->qh_link = element->qh_link; element->qh_rlink = NULL; } @@ -82,7 +82,7 @@ struct sockaddr_in addr = { .sin_family = AF_INET, .sin_port = 0, - .sin_addr.s_addr = INADDR_ANY, + .sin_addr.s_addr = htonl(INADDR_LOOPBACK), }; socklen_t addrlen = sizeof(addr); int ret, s; @@ -301,6 +301,7 @@ uint16_t dst_port; struct socket *so; const char *state; + char addr[INET_ADDRSTRLEN]; char buf[20]; g_string_append_printf(str, @@ -330,10 +331,11 @@ } slirp_fmt0(buf, sizeof(buf), " TCP[%s]", state); g_string_append_printf(str, "%-19s %3d %15s %5d ", buf, so->s, - src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : - "*", + src.sin_addr.s_addr ? + inet_ntop(AF_INET, &src.sin_addr, addr, sizeof(addr)) : "*", ntohs(src.sin_port)); - g_string_append_printf(str, "%15s %5d %5d %5d\n", inet_ntoa(dst_addr), + g_string_append_printf(str, "%15s %5d %5d %5d\n", + inet_ntop(AF_INET, &dst_addr, addr, sizeof(addr)), ntohs(dst_port), so->so_rcv.sb_cc, so->so_snd.sb_cc); } @@ -354,10 +356,11 @@ dst_port = so->so_fport; } g_string_append_printf(str, "%-19s %3d %15s %5d ", buf, so->s, - src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : - "*", + src.sin_addr.s_addr ? + inet_ntop(AF_INET, &src.sin_addr, addr, sizeof(addr)) : "*", ntohs(src.sin_port)); - g_string_append_printf(str, "%15s %5d %5d %5d\n", inet_ntoa(dst_addr), + g_string_append_printf(str, "%15s %5d %5d %5d\n", + inet_ntop(AF_INET, &dst_addr, addr, sizeof(addr)), ntohs(dst_port), so->so_rcv.sb_cc, so->so_snd.sb_cc); } @@ -368,9 +371,10 @@ src.sin_addr = so->so_laddr; dst_addr = so->so_faddr; g_string_append_printf(str, "%-19s %3d %15s - ", buf, so->s, - src.sin_addr.s_addr ? inet_ntoa(src.sin_addr) : - "*"); - g_string_append_printf(str, "%15s - %5d %5d\n", inet_ntoa(dst_addr), + src.sin_addr.s_addr ? + inet_ntop(AF_INET, &src.sin_addr, addr, sizeof(addr)) : "*"); + g_string_append_printf(str, "%15s - %5d %5d\n", + inet_ntop(AF_INET, &dst_addr, addr, sizeof(addr)), so->so_rcv.sb_cc, so->so_snd.sb_cc); } diff -Nru libslirp-4.6.1/src/slirp.c libslirp-4.7.0/src/slirp.c --- libslirp-4.6.1/src/slirp.c 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/slirp.c 2022-04-26 09:50:57.000000000 +0000 @@ -34,6 +34,37 @@ #undef if_mtu #endif +#if defined(_WIN32) + +#define INITIAL_DNS_ADDR_BUF_SIZE 32 * 1024 +#define REALLOC_RETRIES 5 + +// Broadcast site local DNS resolvers. We do not use these because they are +// highly unlikely to be valid. +// https://www.ietf.org/proceedings/52/I-D/draft-ietf-ipngwg-dns-discovery-03.txt +static const struct in6_addr SITE_LOCAL_DNS_BROADCAST_ADDRS[] = { + { + {{ + 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01 + }} + }, + { + {{ + 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02 + }} + }, + { + {{ + 0xfe, 0xc0, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, + }} + }, +}; + +#endif + int slirp_debug; /* Define to 1 if you want KEEPALIVE timers */ @@ -51,13 +82,10 @@ unsigned curtime; static struct in_addr dns_addr; -#ifndef _WIN32 static struct in6_addr dns6_addr; -#endif +static uint32_t dns6_scope_id; static unsigned dns_addr_time; -#ifndef _WIN32 static unsigned dns6_addr_time; -#endif #define TIMEOUT_FAST 2 /* milliseconds */ #define TIMEOUT_SLOW 499 /* milliseconds */ @@ -111,8 +139,108 @@ return 0; } +int is_site_local_dns_broadcast(struct in6_addr *address) +{ + int i; + for (i = 0; i < G_N_ELEMENTS(SITE_LOCAL_DNS_BROADCAST_ADDRS); i++) { + if (in6_equal(address, &SITE_LOCAL_DNS_BROADCAST_ADDRS[i])) { + return 1; + } + } + return 0; +} + +void print_dns_v6_address(struct in6_addr address) +{ + char address_str[INET6_ADDRSTRLEN] = ""; + if (inet_ntop(AF_INET6, &address, address_str, INET6_ADDRSTRLEN) + == NULL) { + DEBUG_ERROR("Failed to stringify IPv6 address for logging."); + return; + } + DEBUG_CALL("IPv6 DNS server found: %s", address_str); +} + +// Gets the first valid DNS resolver with an IPv6 address. +// Ignores any site local broadcast DNS servers, as these +// are on deprecated addresses and not generally expected +// to work. Further details at: +// https://www.ietf.org/proceedings/52/I-D/draft-ietf-ipngwg-dns-discovery-03.txt +int get_ipv6_dns_server(struct in6_addr *dns_server_address, uint32_t *scope_id) +{ + PIP_ADAPTER_ADDRESSES addresses = NULL; + PIP_ADAPTER_ADDRESSES address = NULL; + IP_ADAPTER_DNS_SERVER_ADDRESS *dns_server = NULL; + struct sockaddr_in6 *dns_v6_addr = NULL; + + ULONG buf_size = INITIAL_DNS_ADDR_BUF_SIZE; + DWORD res = ERROR_BUFFER_OVERFLOW; + int i; + + for (i = 0; i < REALLOC_RETRIES; i++) { + // If non null, we hit buffer overflow, free it so we can try again. + if (addresses != NULL) { + g_free(addresses); + } + + addresses = g_malloc(buf_size); + res = GetAdaptersAddresses(AF_UNSPEC, GAA_FLAG_INCLUDE_PREFIX, NULL, + addresses, &buf_size); + + if (res != ERROR_BUFFER_OVERFLOW) { + break; + } + } + + if (res != NO_ERROR) { + DEBUG_ERROR("Failed to get IPv6 DNS addresses due to error %lX", res); + goto failure; + } + + address = addresses; + for (address = addresses; address != NULL; address = address->Next) { + for (dns_server = address->FirstDnsServerAddress; + dns_server != NULL; + dns_server = dns_server->Next) { + + if (dns_server->Address.lpSockaddr->sa_family != AF_INET6) { + continue; + } + + dns_v6_addr = (struct sockaddr_in6 *)dns_server->Address.lpSockaddr; + if (is_site_local_dns_broadcast(&dns_v6_addr->sin6_addr) == 0) { + print_dns_v6_address(dns_v6_addr->sin6_addr); + *dns_server_address = dns_v6_addr->sin6_addr; + *scope_id = dns_v6_addr->sin6_scope_id; + + g_free(addresses); + return 0; + } + } + } + + DEBUG_ERROR("No IPv6 DNS servers found.\n"); + +failure: + g_free(addresses); + return -1; +} + int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id) { + if (!in6_zero(&dns6_addr) && (curtime - dns6_addr_time) < TIMEOUT_DEFAULT) { + *pdns6_addr = dns6_addr; + *scope_id = dns6_scope_id; + return 0; + } + + if (get_ipv6_dns_server(pdns6_addr, scope_id) == 0) { + dns6_addr = *pdns6_addr; + dns6_addr_time = curtime; + dns6_scope_id = *scope_id; + return 0; + } + return -1; } @@ -136,13 +264,18 @@ } static int get_dns_addr_libresolv(int af, void *pdns_addr, void *cached_addr, - socklen_t addrlen, uint32_t *scope_id, + socklen_t addrlen, + uint32_t *scope_id, uint32_t *cached_scope_id, unsigned *cached_time) { struct __res_state state; union res_sockaddr_union servers[NI_MAXSERV]; int count; int found; + void *addr; + + // we only support IPv4 and IPv4, we assume it's one or the other + assert(af == AF_INET || af == AF_INET6); if (res_ninit(&state) != 0) { return -1; @@ -155,14 +288,22 @@ if (af == servers[i].sin.sin_family) { found++; } + if (af == AF_INET) { + addr = &servers[i].sin.sin_addr; + } else { // af == AF_INET6 + addr = &servers[i].sin6.sin6_addr; + } // we use the first found entry if (found == 1) { - memcpy(pdns_addr, &servers[i].sin.sin_addr, addrlen); - memcpy(cached_addr, &servers[i].sin.sin_addr, addrlen); + memcpy(pdns_addr, addr, addrlen); + memcpy(cached_addr, addr, addrlen); if (scope_id) { *scope_id = 0; } + if (cached_scope_id) { + *cached_scope_id = 0; + } *cached_time = curtime; } @@ -171,10 +312,7 @@ break; } else if (slirp_debug & DBG_MISC) { char s[INET6_ADDRSTRLEN]; - const char *res = inet_ntop(servers[i].sin.sin_family, - &servers[i].sin.sin_addr, - s, - sizeof(s)); + const char *res = inet_ntop(af, addr, s, sizeof(s)); if (!res) { res = " (string conversion error)"; } @@ -182,7 +320,7 @@ } } - res_nclose(&state); + res_ndestroy(&state); if (!found) return -1; return 0; @@ -199,7 +337,7 @@ } } return get_dns_addr_libresolv(AF_INET, pdns_addr, &dns_addr, - sizeof(dns_addr), NULL, &dns_addr_time); + sizeof(dns_addr), NULL, NULL, &dns_addr_time); } int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id) @@ -208,16 +346,26 @@ int ret; ret = get_dns_addr_cached(pdns6_addr, &dns6_addr, sizeof(dns6_addr), &dns6_addr_time); + if (ret == 0) { + *scope_id = dns6_scope_id; + } if (ret <= 0) { return ret; } } return get_dns_addr_libresolv(AF_INET6, pdns6_addr, &dns6_addr, - sizeof(dns6_addr), scope_id, &dns6_addr_time); + sizeof(dns6_addr), + scope_id, &dns6_scope_id, &dns6_addr_time); } #else // !defined(_WIN32) && !defined(__APPLE__) +#if defined(__HAIKU__) +#define RESOLV_CONF_PATH "/boot/system/settings/network/resolv.conf" +#else +#define RESOLV_CONF_PATH "/etc/resolv.conf" +#endif + static int get_dns_addr_cached(void *pdns_addr, void *cached_addr, socklen_t addrlen, struct stat *cached_stat, unsigned *cached_time) @@ -228,7 +376,7 @@ return 0; } old_stat = *cached_stat; - if (stat("/etc/resolv.conf", cached_stat) != 0) { + if (stat(RESOLV_CONF_PATH, cached_stat) != 0) { return -1; } if (cached_stat->st_dev == old_stat.st_dev && @@ -242,7 +390,8 @@ } static int get_dns_addr_resolv_conf(int af, void *pdns_addr, void *cached_addr, - socklen_t addrlen, uint32_t *scope_id, + socklen_t addrlen, + uint32_t *scope_id, uint32_t *cached_scope_id, unsigned *cached_time) { char buff[512]; @@ -256,7 +405,7 @@ unsigned if_index; assert(sizeof(tmp_addr) >= addrlen); - f = fopen("/etc/resolv.conf", "r"); + f = fopen(RESOLV_CONF_PATH, "r"); if (!f) return -1; @@ -281,6 +430,9 @@ if (scope_id) { *scope_id = if_index; } + if (cached_scope_id) { + *cached_scope_id = if_index; + } *cached_time = curtime; } @@ -316,7 +468,8 @@ } } return get_dns_addr_resolv_conf(AF_INET, pdns_addr, &dns_addr, - sizeof(dns_addr), NULL, &dns_addr_time); + sizeof(dns_addr), + NULL, NULL, &dns_addr_time); } int get_dns6_addr(struct in6_addr *pdns6_addr, uint32_t *scope_id) @@ -327,13 +480,16 @@ int ret; ret = get_dns_addr_cached(pdns6_addr, &dns6_addr, sizeof(dns6_addr), &dns6_addr_stat, &dns6_addr_time); + if (ret == 0) { + *scope_id = dns6_scope_id; + } if (ret <= 0) { return ret; } } return get_dns_addr_resolv_conf(AF_INET6, pdns6_addr, &dns6_addr, - sizeof(dns6_addr), scope_id, - &dns6_addr_time); + sizeof(dns6_addr), + scope_id, &dns6_scope_id, &dns6_addr_time); } #endif @@ -372,6 +528,43 @@ } } +static void ra_timer_handler_cb(void *opaque) +{ + Slirp *slirp = opaque; + + return ra_timer_handler(slirp, NULL); +} + +void slirp_handle_timer(Slirp *slirp, SlirpTimerId id, void *cb_opaque) +{ + g_return_if_fail(id >= 0 && id < SLIRP_TIMER_NUM); + + switch (id) { + case SLIRP_TIMER_RA: + return ra_timer_handler(slirp, cb_opaque); + default: + abort(); + } +} + +void *slirp_timer_new(Slirp *slirp, SlirpTimerId id, void *cb_opaque) +{ + g_return_val_if_fail(id >= 0 && id < SLIRP_TIMER_NUM, NULL); + + if (slirp->cfg_version >= 4 && slirp->cb->timer_new_opaque) { + return slirp->cb->timer_new_opaque(id, cb_opaque, slirp->opaque); + } + + switch (id) { + case SLIRP_TIMER_RA: + g_return_val_if_fail(cb_opaque == NULL, NULL); + return slirp->cb->timer_new(ra_timer_handler_cb, slirp, slirp->opaque); + + default: + abort(); + } +} + Slirp *slirp_new(const SlirpConfig *cfg, const SlirpCb *callbacks, void *opaque) { Slirp *slirp; @@ -391,6 +584,7 @@ slirp_init_once(); + slirp->cfg_version = cfg->version; slirp->opaque = opaque; slirp->cb = callbacks; slirp->grand = g_rand_new(); @@ -401,7 +595,6 @@ if_init(slirp); ip_init(slirp); - ip6_init(slirp); m_init(slirp); @@ -445,6 +638,17 @@ slirp->disable_dns = false; } + if (cfg->version >= 4) { + slirp->disable_dhcp = cfg->disable_dhcp; + } else { + slirp->disable_dhcp = false; + } + + if (slirp->cfg_version >= 4 && slirp->cb->init_completed) { + slirp->cb->init_completed(slirp, slirp->opaque); + } + + ip6_post_init(slirp); return slirp; } @@ -601,7 +805,10 @@ /* * Set for reading (and urgent data) if we are connected, can - * receive more, and we have room for it XXX /2 ? + * receive more, and we have room for it. + * + * If sb is already half full, we will wait for the guest to consume it, + * and notify again in sbdrop() when the sb becomes less than half full. */ if (CONN_CANFRCV(so) && (so->so_snd.sb_cc < (so->so_snd.sb_datalen / 2))) { @@ -1347,6 +1554,8 @@ } if (!CONN_CANFRCV(so) || so->so_snd.sb_cc >= (so->so_snd.sb_datalen / 2)) { + /* If the sb is already half full, we will wait for the guest to consume it, + * and notify again in sbdrop() when the sb becomes less than half full. */ return 0; } diff -Nru libslirp-4.6.1/src/slirp.h libslirp-4.7.0/src/slirp.h --- libslirp-4.6.1/src/slirp.h 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/slirp.h 2022-04-26 09:50:57.000000000 +0000 @@ -20,10 +20,8 @@ #include #else -#if !defined(__HAIKU__) #define O_BINARY 0 #endif -#endif #ifndef _WIN32 #include @@ -37,12 +35,6 @@ #include #endif -/* Avoid conflicting with the libc insque() and remque(), which - have different prototypes. */ -#define insque slirp_insque -#define remque slirp_remque -#define quehead slirp_quehead - #include "debug.h" #include "util.h" @@ -128,6 +120,8 @@ uint8_t out_ethaddr[ETH_ALEN]); struct Slirp { + int cfg_version; + unsigned time_fasttimo; unsigned last_slowtimo; bool do_slowtimo; @@ -141,6 +135,7 @@ struct in6_addr vprefix_addr6; uint8_t vprefix_len; struct in6_addr vhost_addr6; + bool disable_dhcp; /* slirp will not reply to any DHCP requests */ struct in_addr vdhcp_startaddr; struct in_addr vnameserver_addr; struct in6_addr vnameserver_addr6; @@ -157,13 +152,13 @@ bool disable_host_loopback; /* mbuf states */ - struct quehead m_freelist; - struct quehead m_usedlist; + struct slirp_quehead m_freelist; + struct slirp_quehead m_usedlist; int mbuf_alloced; /* if states */ - struct quehead if_fastq; /* fast queue (for interactive data) */ - struct quehead if_batchq; /* queue for non-interactive data */ + struct slirp_quehead if_fastq; /* fast queue (for interactive data) */ + struct slirp_quehead if_batchq; /* queue for non-interactive data */ bool if_start_busy; /* avoid if_start recursion */ /* ip states */ @@ -251,7 +246,7 @@ int ip_output(struct socket *, struct mbuf *); /* ip6_input.c */ -void ip6_init(Slirp *); +void ip6_post_init(Slirp *); void ip6_cleanup(Slirp *); void ip6_input(struct mbuf *); @@ -287,5 +282,6 @@ int guest_port); void slirp_send_packet_all(Slirp *slirp, const void *buf, size_t len); +void *slirp_timer_new(Slirp *slirp, SlirpTimerId id, void *cb_opaque); #endif diff -Nru libslirp-4.6.1/src/socket.c libslirp-4.7.0/src/socket.c --- libslirp-4.6.1/src/socket.c 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/socket.c 2022-04-26 09:50:57.000000000 +0000 @@ -41,15 +41,17 @@ /* * Create a new socket, initialise the fields * It is the responsibility of the caller to - * insque() it into the correct linked-list + * slirp_insque() it into the correct linked-list */ -struct socket *socreate(Slirp *slirp) +struct socket *socreate(Slirp *slirp, int type) { struct socket *so = g_new(struct socket, 1); memset(so, 0, sizeof(struct socket)); + so->so_type = type; so->so_state = SS_NOFDREF; so->s = -1; + so->s_aux = -1; so->slirp = slirp; so->pollfds_idx = -1; @@ -59,11 +61,11 @@ /* * Remove references to so from the given message queue. */ -static void soqfree(struct socket *so, struct quehead *qh) +static void soqfree(struct socket *so, struct slirp_quehead *qh) { struct mbuf *ifq; - for (ifq = (struct mbuf *)qh->qh_link; (struct quehead *)ifq != qh; + for (ifq = (struct mbuf *)qh->qh_link; (struct slirp_quehead *)ifq != qh; ifq = ifq->ifq_next) { if (ifq->ifq_so == so) { struct mbuf *ifm; @@ -76,12 +78,16 @@ } /* - * remque and free a socket, clobber cache + * slirp_remque and free a socket, clobber cache */ void sofree(struct socket *so) { Slirp *slirp = so->slirp; + if (so->s_aux != -1) { + closesocket(so->s_aux); + } + soqfree(so, &slirp->if_fastq); soqfree(so, &slirp->if_batchq); @@ -95,7 +101,7 @@ m_free(so->so_m); if (so->so_next && so->so_prev) - remque(so); /* crashes if so is not in a queue */ + slirp_remque(so); /* crashes if so is not in a queue */ if (so->so_tcpcb) { g_free(so->so_tcpcb); @@ -211,8 +217,8 @@ errno, strerror(errno)); sofcantrcvmore(so); - if (err == ECONNRESET || err == ECONNREFUSED || err == ENOTCONN || - err == EPIPE) { + if (err == ECONNABORTED || err == ECONNRESET || err == ECONNREFUSED || + err == ENOTCONN || err == EPIPE) { tcp_drop(sototcpcb(so), err); } else { tcp_sockclosed(sototcpcb(so)); @@ -369,7 +375,7 @@ len += n; } n = slirp_send(so, buff, len, (MSG_OOB)); /* |MSG_DONTWAIT)); */ -#ifdef DEBUG +#ifdef SLIRP_DEBUG if (n != len) { DEBUG_ERROR("Didn't send all data urgently XXXXX"); } @@ -773,14 +779,35 @@ char addrstr[INET6_ADDRSTRLEN]; char portstr[6]; int ret; - ret = getnameinfo(haddr, haddrlen, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV); - g_assert(ret == 0); - DEBUG_ARG("haddr = %s", addrstr); - DEBUG_ARG("hport = %s", portstr); - ret = getnameinfo(laddr, laddrlen, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV); - g_assert(ret == 0); - DEBUG_ARG("laddr = %s", addrstr); - DEBUG_ARG("lport = %s", portstr); + switch (haddr->sa_family) { + case AF_INET: + case AF_INET6: + ret = getnameinfo(haddr, haddrlen, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV); + g_assert(ret == 0); + DEBUG_ARG("hfamily = INET"); + DEBUG_ARG("haddr = %s", addrstr); + DEBUG_ARG("hport = %s", portstr); + break; +#ifndef _WIN32 + case AF_UNIX: + DEBUG_ARG("hfamily = UNIX"); + DEBUG_ARG("hpath = %s", ((struct sockaddr_un *) haddr)->sun_path); + break; +#endif + default: + g_assert_not_reached(); + } + switch (laddr->sa_family) { + case AF_INET: + case AF_INET6: + ret = getnameinfo(laddr, laddrlen, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV); + g_assert(ret == 0); + DEBUG_ARG("laddr = %s", addrstr); + DEBUG_ARG("lport = %s", portstr); + break; + default: + g_assert_not_reached(); + } DEBUG_ARG("flags = %x", flags); /* @@ -792,11 +819,11 @@ */ g_assert(!((flags & SS_HOSTFWD) && (flags & SS_FACCEPTONCE))); - so = socreate(slirp); + so = socreate(slirp, IPPROTO_TCP); /* Don't tcp_attach... we don't need so_snd nor so_rcv */ so->so_tcpcb = tcp_newtcpcb(so); - insque(so, &slirp->tcb); + slirp_insque(so, &slirp->tcb); /* * SS_FACCEPTONCE sockets must time out. @@ -1041,6 +1068,108 @@ } break; + case AF_UNIX: { + /* Translate Unix socket to random ephemeral source port. We obtain + * this source port by binding to port 0 so that the OS allocates a + * port for us. If this fails, we fall back to choosing a random port + * with a random number generator. */ + int s; + struct sockaddr_in in_addr; + struct sockaddr_in6 in6_addr; + socklen_t in_addr_len; + + if (so->slirp->in_enabled) { + so->so_ffamily = AF_INET; + so->so_faddr = slirp->vhost_addr; + so->so_fport = 0; + + switch (so->so_type) { + case IPPROTO_TCP: + s = slirp_socket(PF_INET, SOCK_STREAM, 0); + break; + case IPPROTO_UDP: + s = slirp_socket(PF_INET, SOCK_DGRAM, 0); + break; + default: + g_assert_not_reached(); + break; + } + if (s < 0) { + g_error("Ephemeral slirp_socket() allocation failed"); + goto unix2inet_cont; + } + memset(&in_addr, 0, sizeof(in_addr)); + in_addr.sin_family = AF_INET; + in_addr.sin_addr.s_addr = htonl(INADDR_LOOPBACK); + in_addr.sin_port = htons(0); + if (bind(s, (struct sockaddr *) &in_addr, sizeof(in_addr))) { + g_error("Ephemeral bind() failed"); + closesocket(s); + goto unix2inet_cont; + } + in_addr_len = sizeof(in_addr); + if (getsockname(s, (struct sockaddr *) &in_addr, &in_addr_len)) { + g_error("Ephemeral getsockname() failed"); + closesocket(s); + goto unix2inet_cont; + } + so->s_aux = s; + so->so_fport = in_addr.sin_port; + +unix2inet_cont: + if (!so->so_fport) { + g_warning("Falling back to random port allocation"); + so->so_fport = htons(g_rand_int_range(slirp->grand, 49152, 65536)); + } + } else if (so->slirp->in6_enabled) { + so->so_ffamily = AF_INET6; + so->so_faddr6 = slirp->vhost_addr6; + so->so_fport6 = 0; + + switch (so->so_type) { + case IPPROTO_TCP: + s = slirp_socket(PF_INET6, SOCK_STREAM, 0); + break; + case IPPROTO_UDP: + s = slirp_socket(PF_INET6, SOCK_DGRAM, 0); + break; + default: + g_assert_not_reached(); + break; + } + if (s < 0) { + g_error("Ephemeral slirp_socket() allocation failed"); + goto unix2inet6_cont; + } + memset(&in6_addr, 0, sizeof(in6_addr)); + in6_addr.sin6_family = AF_INET6; + in6_addr.sin6_addr = in6addr_loopback; + in6_addr.sin6_port = htons(0); + if (bind(s, (struct sockaddr *) &in6_addr, sizeof(in6_addr))) { + g_error("Ephemeral bind() failed"); + closesocket(s); + goto unix2inet6_cont; + } + in_addr_len = sizeof(in6_addr); + if (getsockname(s, (struct sockaddr *) &in6_addr, &in_addr_len)) { + g_error("Ephemeral getsockname() failed"); + closesocket(s); + goto unix2inet6_cont; + } + so->s_aux = s; + so->so_fport6 = in6_addr.sin6_port; + +unix2inet6_cont: + if (!so->so_fport6) { + g_warning("Falling back to random port allocation"); + so->so_fport6 = htons(g_rand_int_range(slirp->grand, 49152, 65536)); + } + } else { + g_assert_not_reached(); + } + break; + } /* case AF_UNIX */ + default: break; } diff -Nru libslirp-4.6.1/src/socket.h libslirp-4.7.0/src/socket.h --- libslirp-4.6.1/src/socket.h 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/socket.h 2022-04-26 09:50:57.000000000 +0000 @@ -6,6 +6,12 @@ #ifndef SLIRP_SOCKET_H #define SLIRP_SOCKET_H +#include + +#ifndef _WIN32 +#include +#endif + #include "misc.h" #include "sbuf.h" @@ -34,6 +40,8 @@ struct socket *so_next, *so_prev; /* For a linked list of sockets */ int s; /* The actual socket */ + int s_aux; /* An auxiliary socket for miscellaneous use. Currently used to + * reserve OS ports in UNIX-to-inet translation. */ struct gfwd_list *guestfwd; int pollfds_idx; /* GPollFD GArray index */ @@ -64,7 +72,8 @@ uint8_t so_iptos; /* Type of service */ uint8_t so_emu; /* Is the socket emulated? */ - uint8_t so_type; /* Type of socket, UDP or TCP */ + uint8_t so_type; /* Protocol of the socket. May be 0 if loading old + * states. */ int32_t so_state; /* internal state flags SS_*, below */ struct tcpcb *so_tcpcb; /* pointer to TCP protocol control block */ @@ -128,6 +137,13 @@ return (in6_equal(&a6->sin6_addr, &b6->sin6_addr) && a6->sin6_port == b6->sin6_port); } +#ifndef _WIN32 + case AF_UNIX: { + const struct sockaddr_un *aun = (const struct sockaddr_un *)a; + const struct sockaddr_un *bun = (const struct sockaddr_un *)b; + return strncmp(aun->sun_path, bun->sun_path, sizeof(aun->sun_path)) == 0; + } +#endif default: g_assert_not_reached(); } @@ -142,6 +158,10 @@ return sizeof(struct sockaddr_in); case AF_INET6: return sizeof(struct sockaddr_in6); +#ifndef _WIN32 + case AF_UNIX: + return sizeof(struct sockaddr_un); +#endif default: g_assert_not_reached(); } @@ -157,7 +177,7 @@ struct socket *solookup(struct socket **, struct socket *, struct sockaddr_storage *, struct sockaddr_storage *); -struct socket *socreate(Slirp *); +struct socket *socreate(Slirp *, int); void sofree(struct socket *); int soread(struct socket *); int sorecvoob(struct socket *); diff -Nru libslirp-4.6.1/src/state.c libslirp-4.7.0/src/state.c --- libslirp-4.6.1/src/state.c 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/state.c 2022-04-26 09:50:57.000000000 +0000 @@ -344,7 +344,7 @@ while (slirp_istream_read_u8(&f)) { int ret; - struct socket *so = socreate(slirp); + struct socket *so = socreate(slirp, -1); ret = slirp_vmstate_load_state(&f, &vmstate_slirp_socket, so, version_id); diff -Nru libslirp-4.6.1/src/tcp_input.c libslirp-4.7.0/src/tcp_input.c --- libslirp-4.6.1/src/tcp_input.c 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/tcp_input.c 2022-04-26 09:50:57.000000000 +0000 @@ -82,9 +82,6 @@ static int tcp_reass(register struct tcpcb *tp, register struct tcpiphdr *ti, struct mbuf *m) { - if (m) - M_DUP_DEBUG(m->slirp, m, 0, 0); - register struct tcpiphdr *q; struct socket *so = tp->t_socket; int flags; @@ -149,14 +146,14 @@ } q = tcpiphdr_next(q); m = tcpiphdr_prev(q)->ti_mbuf; - remque(tcpiphdr2qlink(tcpiphdr_prev(q))); + slirp_remque(tcpiphdr2qlink(tcpiphdr_prev(q))); m_free(m); } /* * Stick new segment in its place. */ - insque(tcpiphdr2qlink(ti), tcpiphdr2qlink(tcpiphdr_prev(q))); + slirp_insque(tcpiphdr2qlink(ti), tcpiphdr2qlink(tcpiphdr_prev(q))); present: /* @@ -173,7 +170,7 @@ do { tp->rcv_nxt += ti->ti_len; flags = ti->ti_flags & TH_FIN; - remque(tcpiphdr2qlink(ti)); + slirp_remque(tcpiphdr2qlink(ti)); m = ti->ti_mbuf; ti = tcpiphdr_next(ti); if (so->so_state & SS_FCANTSENDMORE) @@ -218,6 +215,9 @@ DEBUG_CALL("tcp_input"); DEBUG_ARG("m = %p iphlen = %2d inso = %p", m, iphlen, inso); + memset(&lhost, 0, sizeof(struct sockaddr_storage)); + memset(&fhost, 0, sizeof(struct sockaddr_storage)); + /* * If called with m == 0, then we're continuing the connect */ @@ -420,7 +420,7 @@ if ((tiflags & (TH_SYN | TH_FIN | TH_RST | TH_URG | TH_ACK)) != TH_SYN) goto dropwithreset; - so = socreate(slirp); + so = socreate(slirp, IPPROTO_TCP); tcp_attach(so); sbreserve(&so->so_snd, TCP_SNDSPACE); diff -Nru libslirp-4.6.1/src/tcp_subr.c libslirp-4.7.0/src/tcp_subr.c --- libslirp-4.6.1/src/tcp_subr.c 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/tcp_subr.c 2022-04-26 09:50:57.000000000 +0000 @@ -329,7 +329,7 @@ while (!tcpfrag_list_end(t, tp)) { t = tcpiphdr_next(t); m = tcpiphdr_prev(t)->ti_mbuf; - remque(tcpiphdr2qlink(tcpiphdr_prev(t))); + slirp_remque(tcpiphdr2qlink(tcpiphdr_prev(t))); m_free(m); } g_free(tp); @@ -464,7 +464,7 @@ Slirp *slirp = inso->slirp; struct socket *so; struct sockaddr_storage addr; - socklen_t addrlen = sizeof(struct sockaddr_storage); + socklen_t addrlen; struct tcpcb *tp; int s, opt, ret; /* AF_INET6 addresses are bigger than AF_INET, so this is big enough. */ @@ -473,7 +473,17 @@ DEBUG_CALL("tcp_connect"); DEBUG_ARG("inso = %p", inso); - ret = getnameinfo((const struct sockaddr *) &inso->lhost.ss, sizeof(inso->lhost.ss), addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV); + switch (inso->lhost.ss.ss_family) { + case AF_INET: + addrlen = sizeof(struct sockaddr_in); + break; + case AF_INET6: + addrlen = sizeof(struct sockaddr_in6); + break; + default: + g_assert_not_reached(); + } + ret = getnameinfo((const struct sockaddr *) &inso->lhost.ss, addrlen, addrstr, sizeof(addrstr), portstr, sizeof(portstr), NI_NUMERICHOST|NI_NUMERICSERV); g_assert(ret == 0); DEBUG_ARG("ip = [%s]:%s", addrstr, portstr); DEBUG_ARG("so_state = 0x%x", inso->so_state); @@ -494,6 +504,7 @@ * us again until the guest address is available. */ DEBUG_MISC(" guest address not available yet"); + addrlen = sizeof(addr); s = accept(inso->s, (struct sockaddr *)&addr, &addrlen); if (s >= 0) { close(s); @@ -510,7 +521,7 @@ /* FACCEPTONCE already have a tcpcb */ so = inso; } else { - so = socreate(slirp); + so = socreate(slirp, IPPROTO_TCP); tcp_attach(so); so->lhost = inso->lhost; so->so_ffamily = inso->so_ffamily; @@ -518,6 +529,7 @@ tcp_mss(sototcpcb(so), 0); + addrlen = sizeof(addr); s = accept(inso->s, (struct sockaddr *)&addr, &addrlen); if (s < 0) { tcp_close(sototcpcb(so)); /* This will sofree() as well */ @@ -565,7 +577,7 @@ void tcp_attach(struct socket *so) { so->so_tcpcb = tcp_newtcpcb(so); - insque(so, &so->slirp->tcb); + slirp_insque(so, &so->slirp->tcb); } /* diff -Nru libslirp-4.6.1/src/udp6.c libslirp-4.7.0/src/udp6.c --- libslirp-4.6.1/src/udp6.c 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/udp6.c 2022-04-26 09:50:57.000000000 +0000 @@ -95,7 +95,7 @@ if (so == NULL) { /* If there's no socket for this packet, create one. */ - so = socreate(slirp); + so = socreate(slirp, IPPROTO_UDP); if (udp_attach(so, AF_INET6) == -1) { DEBUG_MISC(" udp6_attach errno = %d-%s", errno, strerror(errno)); sofree(so); diff -Nru libslirp-4.6.1/src/udp.c libslirp-4.7.0/src/udp.c --- libslirp-4.6.1/src/udp.c 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/udp.c 2022-04-26 09:50:57.000000000 +0000 @@ -177,7 +177,7 @@ * If there's no socket for this packet, * create one */ - so = socreate(slirp); + so = socreate(slirp, IPPROTO_UDP); if (udp_attach(so, AF_INET) == -1) { DEBUG_MISC(" udp_attach errno = %d-%s", errno, strerror(errno)); sofree(so); @@ -251,6 +251,8 @@ struct sockaddr_in *daddr, int iptos) { Slirp *slirp = m->slirp; + char addr[INET_ADDRSTRLEN]; + M_DUP_DEBUG(slirp, m, 0, sizeof(struct udpiphdr)); register struct udpiphdr *ui; @@ -259,8 +261,8 @@ DEBUG_CALL("udp_output"); DEBUG_ARG("so = %p", so); DEBUG_ARG("m = %p", m); - DEBUG_ARG("saddr = %s", inet_ntoa(saddr->sin_addr)); - DEBUG_ARG("daddr = %s", inet_ntoa(daddr->sin_addr)); + DEBUG_ARG("saddr = %s", inet_ntop(AF_INET, &saddr->sin_addr, addr, sizeof(addr))); + DEBUG_ARG("daddr = %s", inet_ntop(AF_INET, &daddr->sin_addr, addr, sizeof(addr))); /* * Adjust for header @@ -329,7 +331,7 @@ #endif so->so_expire = curtime + SO_EXPIRE; - insque(so, &so->slirp->udb); + slirp_insque(so, &so->slirp->udb); } so->slirp->cb->register_poll_fd(so->s, so->slirp->opaque); return (so->s); @@ -371,7 +373,7 @@ socklen_t addrlen; int save_errno; - so = socreate(slirp); + so = socreate(slirp, IPPROTO_UDP); so->s = slirp_socket(haddr->sa_family, SOCK_DGRAM, 0); if (so->s < 0) { save_errno = errno; @@ -382,7 +384,7 @@ if (haddr->sa_family == AF_INET6) slirp_socket_set_v6only(so->s, (flags & SS_HOSTFWD_V6ONLY) != 0); so->so_expire = curtime + SO_EXPIRE; - insque(so, &slirp->udb); + slirp_insque(so, &slirp->udb); if (bind(so->s, haddr, haddrlen) < 0) { save_errno = errno; diff -Nru libslirp-4.6.1/src/vmstate.h libslirp-4.7.0/src/vmstate.h --- libslirp-4.6.1/src/vmstate.h 2021-06-18 07:49:56.000000000 +0000 +++ libslirp-4.7.0/src/vmstate.h 2022-04-26 09:50:57.000000000 +0000 @@ -173,6 +173,9 @@ * VMStateField.struct_version_id to tell which version of the * structure we are referencing to use. */ VMS_VSTRUCT = 0x8000, + + /* Marker for end of list */ + VMS_END = 0x10000 }; struct VMStateField { @@ -386,6 +389,7 @@ #define VMSTATE_END_OF_LIST() \ { \ + .flags = VMS_END, \ } #endif diff -Nru libslirp-4.6.1/test/pingtest.c libslirp-4.7.0/test/pingtest.c --- libslirp-4.6.1/test/pingtest.c 1970-01-01 00:00:00.000000000 +0000 +++ libslirp-4.7.0/test/pingtest.c 2022-04-26 09:50:57.000000000 +0000 @@ -0,0 +1,488 @@ +/* SPDX-License-Identifier: BSD-3-Clause */ +/* + * Copyright (c) 2021 Samuel Thibault + */ + +/* + * This simple test configures slirp and tries to ping it + * + * Note: to make this example actually be able to use the outside world, you + * need to either + * - run as root + * - set /proc/sys/net/ipv4/ping_group_range to allow sending ICMP echo requests + * - run a UDP echo server on the target + */ + +#include +#include +#include +#include + +#include "libslirp.h" + +//#define _WIN32 +#ifdef _WIN32 +//#include +#include +int slirp_inet_aton(const char *cp, struct in_addr *ia) +{ + uint32_t addr = inet_addr(cp); + if (addr == 0xffffffff) { + return 0; + } + ia->s_addr = addr; + return 1; +} +#define inet_aton slirp_inet_aton +#else +#include +#endif + +/* Dumb simulation tick: 100ms */ +#define TICK 100 + +static Slirp *slirp; +static bool done; +static int64_t mytime; + +/* Print a frame for debugging */ +static void print_frame(const uint8_t *data, size_t len) { + int i; + + printf("\ngot packet size %zd:\n", len); + for (i = 0; i < len; i++) { + if (i && i % 16 == 0) + printf("\n"); + printf("%s%02x", i % 16 ? " " : "", data[i]); + } + if (len % 16 != 0) + printf("\n"); + printf("\n"); +} + +/* Classical 16bit checksum */ +static void checksum(uint8_t *data, size_t size, uint8_t *cksum) { + uint32_t sum = 0; + int i; + + cksum[0] = 0; + cksum[1] = 0; + + for (i = 0; i+1 < size; i += 2) + sum += (((uint16_t) data[i]) << 8) + data[i+1]; + if (i < size) /* Odd number of bytes */ + sum += ((uint16_t) data[i]) << 8; + + sum = (sum & 0xffff) + (sum >> 16); + sum = (sum & 0xffff) + (sum >> 16); + sum = ~sum; + + cksum[0] = sum >> 8; + cksum[1] = sum; +} + +/* This is called when receiving a packet from the virtual network, for the + * guest */ +static ssize_t send_packet(const void *buf, size_t len, void *opaque) { + const uint8_t *data = buf; + + assert(len >= 14); + + if (data[12] == 0x86 && + data[13] == 0xdd) { + /* Ignore IPv6 */ + return len; + } + + print_frame(data, len); + + if (data[12] == 0x08 && + data[13] == 0x06) { + /* ARP */ + /* We expect receiving an ARP request for our address */ + + /* Ethernet address type */ + assert(data[14] == 0x00); + assert(data[15] == 0x01); + + /* IPv4 address type */ + assert(data[16] == 0x08); + assert(data[17] == 0x00); + + /* Ethernet addresses are 6 bytes long */ + assert(data[18] == 0x06); + + /* IPv4 addresses are 4 bytes long */ + assert(data[19] == 0x04); + + /* Opcode: ARP request */ + assert(data[20] == 0x00); + assert(data[21] == 0x01); + + /* Ok, reply! */ + uint8_t myframe[] = { + /*** Ethernet ***/ + /* dst */ + 0x52, 0x55, 0x0a, 0x00, 0x02, 0x02, + /* src */ + 0x52, 0x55, 0x0a, 0x00, 0x02, 0x0e, + /* Type: ARP */ + 0x08, 0x06, + + /* ether, IPv4, */ + 0x00, 0x01, 0x08, 0x00, + /* elen, IPlen */ + 0x06, 0x04, + /* ARP reply */ + 0x00, 0x02, + + /* Our ethernet address */ + 0x52, 0x55, 0x0a, 0x00, 0x02, 0x0e, + /* Our IP address */ + 0x0a, 0x00, 0x02, 0x0e, + + /* Host ethernet address */ + 0x52, 0x55, 0x0a, 0x00, 0x02, 0x02, + /* Host IP address */ + 0x0a, 0x00, 0x02, 0x02, + }; + + slirp_input(slirp, myframe, sizeof(myframe)); + } + + if (data[12] == 0x08 && + data[13] == 0x00) { + /* IPv4 */ + assert(len >= 14 + 20); + + /* We expect receiving the ICMP echo reply for our echo request */ + + /* IPv + hlen */ + assert(data[14] == 0x45); + + /* proto: ICMP */ + assert(data[23] == 0x01); + + /* ICMP */ + assert(len >= 14 + 20 + 8 + 4); + + /* ICMP type: reply */ + assert(data[34] == 0x00); + + /* Check the data */ + assert(data[42] == 0xde); + assert(data[43] == 0xad); + assert(data[44] == 0xbe); + assert(data[45] == 0xef); + + /* Got the answer! */ + printf("got it!\n"); + done = 1; + } + + return len; +} + +static void guest_error(const char *msg, void *opaque) { + printf("guest error %s\n", msg); +} + + +/* + * Dumb timer implementation + */ +static int64_t clock_get_ns(void *opaque) { + return mytime; +} + +struct timer { + SlirpTimerId id; + void *cb_opaque; + int64_t expire; + struct timer *next; +}; + +static struct timer *timer_queue; + +static void *timer_new_opaque(SlirpTimerId id, void *cb_opaque, void *opaque) { + struct timer *new_timer = malloc(sizeof(*new_timer)); + new_timer->id = id; + new_timer->cb_opaque = cb_opaque; + new_timer->next = NULL; + return new_timer; +} + +static void timer_free(void *_timer, void *opaque) { + struct timer *timer = _timer; + struct timer **t; + + for (t = &timer_queue; *t != NULL; *t = (*t)->next) { + if (*t == timer) { + /* Not expired yet, drop it */ + *t = timer->next; + break; + } + } + + free(timer); +} + +static void timer_mod(void *_timer, int64_t expire_time, void *opaque) { + struct timer *timer = _timer; + struct timer **t; + + timer->expire = expire_time * 1000 * 1000; + + for (t = &timer_queue; *t != NULL; *t = (*t)->next) { + if (expire_time < (*t)->expire) + break; + } + + timer->next = *t; + *t = timer; +} + +static void timer_check(Slirp *slirp) { + while (timer_queue && timer_queue->expire <= mytime) + { + struct timer *t = timer_queue; + printf("handling %p at time %lu\n", + t, (unsigned long) timer_queue->expire); + timer_queue = t->next; + slirp_handle_timer(slirp, t->id, t->cb_opaque); + } +} + +static uint32_t timer_timeout(void) { + if (timer_queue) + { + uint32_t timeout = (timer_queue->expire - mytime) / (1000 * 1000); + if (timeout < TICK) + return timeout; + } + + return TICK; +} + + +/* + * Dumb polling implementation + */ +static int npoll; +static void register_poll_fd(int fd, void *opaque) { + /* We might want to prepare for polling on fd */ + npoll++; +} + +static void unregister_poll_fd(int fd, void *opaque) { + /* We might want to clear polling on fd */ + npoll--; +} + +static void notify(void *opaque) { + /* No need for this in single-thread case */ +} + +#ifdef _WIN32 +/* select() variant */ +static fd_set readfds, writefds, exceptfds; +static int maxfd; +static int add_poll_cb(int fd, int events, void *opaque) +{ + if (events & SLIRP_POLL_IN) + FD_SET(fd, &readfds); + if (events & SLIRP_POLL_OUT) + FD_SET(fd, &writefds); + if (events & SLIRP_POLL_PRI) + FD_SET(fd, &exceptfds); + if (maxfd < fd) + maxfd = fd; + return fd; +} + +static int get_revents_cb(int idx, void *opaque) +{ + int event = 0; + if (FD_ISSET(idx, &readfds)) + event |= SLIRP_POLL_IN; + if (FD_ISSET(idx, &writefds)) + event |= SLIRP_POLL_OUT; + if (FD_ISSET(idx, &exceptfds)) + event |= SLIRP_POLL_PRI; + return event; +} + +static void dopoll(uint32_t timeout) { + int err; + FD_ZERO(&readfds); + FD_ZERO(&writefds); + FD_ZERO(&exceptfds); + maxfd = 0; + + slirp_pollfds_fill(slirp, &timeout, add_poll_cb, NULL); + printf("we will use timeout %u\n", (unsigned) timeout); + + struct timeval tv = { + .tv_sec = timeout / 1000, + .tv_usec = (timeout % 1000) * 1000, + }; + err = select(maxfd+1, &readfds, &writefds, &exceptfds, &tv); + + slirp_pollfds_poll(slirp, err < 0, get_revents_cb, NULL); +} +#else +/* poll() variant */ +static struct pollfd *fds; +static int cur_poll; +static int add_poll_cb(int fd, int events, void *opaque) +{ + short poll_events = 0; + + assert(cur_poll < npoll); + fds[cur_poll].fd = fd; + + if (events & SLIRP_POLL_IN) + poll_events |= POLLIN; + if (events & SLIRP_POLL_OUT) + poll_events |= POLLOUT; + if (events & SLIRP_POLL_PRI) + poll_events |= POLLPRI; + fds[cur_poll].events = poll_events; + + return cur_poll++; +} + +static int get_revents_cb(int idx, void *opaque) +{ + return fds[idx].revents; +} + +static void dopoll(uint32_t timeout) { + int err; + fds = malloc(sizeof(*fds) * npoll); + cur_poll = 0; + + slirp_pollfds_fill(slirp, &timeout, add_poll_cb, NULL); + printf("we will use timeout %u\n", (unsigned) timeout); + + err = poll(fds, cur_poll, timeout); + + slirp_pollfds_poll(slirp, err < 0, get_revents_cb, NULL); + + free(fds); +} +#endif + + +static struct SlirpCb callbacks = { + .send_packet = send_packet, + .guest_error = guest_error, + .clock_get_ns = clock_get_ns, + .timer_new_opaque = timer_new_opaque, + .timer_free = timer_free, + .timer_mod = timer_mod, + .register_poll_fd = register_poll_fd, + .unregister_poll_fd = unregister_poll_fd, + .notify = notify, +}; + + +int main(int argc, char *argv[]) { + SlirpConfig config = { + .version = 4, + .restricted = false, + .in_enabled = true, + .vnetwork.s_addr = htonl(0x0a000200), + .vnetmask.s_addr = htonl(0xffffff00), + .vhost.s_addr = htonl(0x0a000202), + .vdhcp_start.s_addr = htonl(0x0a00020f), + .vnameserver.s_addr = htonl(0x0a000203), + .disable_host_loopback = false, + .enable_emu = false, + .disable_dns = false, + }; + uint32_t timeout = 0; + + printf("Slirp version %s\n", slirp_version_string()); + +#if !defined(_WIN32) + inet_pton(AF_INET6, "fec0::", &config.vprefix_addr6); + config.vprefix_len = 64; + config.vhost6 = config.vprefix_addr6; + config.vhost6.s6_addr[15] = 2; + config.vnameserver6 = config.vprefix_addr6; + config.vnameserver6.s6_addr[15] = 2; + config.in6_enabled = true, +#endif + + slirp = slirp_new(&config, &callbacks, NULL); + + /* Send echo request */ + uint8_t myframe[] = { + /*** Ethernet ***/ + /* dst */ + 0x52, 0x55, 0x0a, 0x00, 0x02, 0x02, + /* src */ + 0x52, 0x55, 0x0a, 0x00, 0x02, 0x0e, + /* Type: IPv4 */ + 0x08, 0x00, + + /*** IPv4 ***/ + /* vhl,tos, len */ + 0x45, 0x00, 0x00, 0x20, + /* id, off (DF) */ + 0x68, 0xd7, 0x40, 0x00, + /* ttl,pro, cksum */ + 0x40, 0x01, 0x00, 0x00, + /* src */ + 0x0a, 0x00, 0x02, 0x0e, + /* dst */ + 0x00, 0x00, 0x00, 0x00, + + /*** ICMPv4 ***/ + /* type, code, cksum */ + 0x08, 0x00, 0x00, 0x00, + /* id, seq */ + 0x01, 0xec, 0x00, 0x01, + /* data */ + 0xde, 0xad, 0xbe, 0xef, + }; + + struct in_addr in_addr = { .s_addr = htonl(0x0a000202) }; + if (argc > 1) { + if (inet_aton(argv[1], &in_addr) == 0) { + printf("usage: %s [destination IPv4 address]\n", argv[0]); + exit(EXIT_FAILURE); + } + } + uint32_t addr = ntohl(in_addr.s_addr); + myframe[30] = addr >> 24; + myframe[31] = addr >> 16; + myframe[32] = addr >> 8; + myframe[33] = addr >> 0; + + /* IPv4 header checksum */ + checksum(&myframe[14], 20, &myframe[24]); + /* ICMP header checksum */ + checksum(&myframe[34], 12, &myframe[36]); + + slirp_input(slirp, myframe, sizeof(myframe)); + + /* Wait for echo reply */ + while (!done) { + printf("time %lu\n", (unsigned long) mytime); + + timer_check(slirp); + /* Here we make the virtual time wait like the real time, but we could + * make it wait differently */ + timeout = timer_timeout(); + printf("we wish timeout %u\n", (unsigned) timeout); + + dopoll(timeout); + + /* Fake that the tick elapsed */ + mytime += TICK * 1000 * 1000; + } + + slirp_cleanup(slirp); +}