Merge branch 'pingtest' into 'master'

pingtest: Add a trivial ping test

See merge request slirp/libslirp!91
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 46e58a9..2512ef1 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -41,3 +41,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 --git a/CHANGELOG.md b/CHANGELOG.md
index 7f33557..bd4845c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -5,16 +5,30 @@
 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).
 
-## [Unreleased]
+## [4.6.1] - 2021-06-18
+
+### Fixed
+
+ - Fix DHCP regression introduced in 4.6.0. !95
+
+## [4.6.0] - 2021-06-14
 
 ### Added
 
+ - mbuf: Add debugging helpers for allocation. !90
+
 ### Changed
 
-### Deprecated
+ -  Revert "Set macOS deployment target to macOS 10.4". !93
 
 ### Fixed
 
+ - mtod()-related buffer overflows (CVE-2021-3592 #44, CVE-2021-3593 #45,
+   CVE-2021-3594 #47, CVE-2021-3595 #46).
+ - poll_fd: add missing fd registration for UDP and ICMP
+ - ncsi: make ncsi_calculate_checksum work with unaligned data. !89
+ - Various typos and doc fixes. !88
+
 ## [4.5.0] - 2021-05-18
 
 ### Added
@@ -158,7 +172,9 @@
  - Standalone project, removing any QEMU dependency.
  - License clarifications.
 
-[Unreleased]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.5.0...master
+[Unreleased]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.6.1...master
+[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
 [4.4.0]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.3.1...v4.4.0
 [4.3.1]: https://gitlab.freedesktop.org/slirp/libslirp/compare/v4.3.0...v4.3.1
diff --git a/meson.build b/meson.build
index 831d865..37d3bf4 100644
--- a/meson.build
+++ b/meson.build
@@ -1,5 +1,5 @@
 project('libslirp', 'c',
-  version : '4.5.0',
+  version : '4.6.1',
   license : 'BSD-3-Clause',
   default_options : ['warning_level=1', 'c_std=gnu99'],
   meson_version : '>= 0.50',
@@ -45,7 +45,7 @@
 #   fixed, change:
 #      REVISION += 1
 lt_current = 3
-lt_revision = 0
+lt_revision = 1
 lt_age = 3
 lt_version = '@0@.@1@.@2@'.format(lt_current - lt_age, lt_age, lt_revision)
 
@@ -117,11 +117,6 @@
   vflag += vflag_test
 endif
 
-if host_system == 'darwin'
-  cargs += '-mmacosx-version-min=10.4'
-  vflag += '-mmacosx-version-min=10.4'
-endif
-
 install_devel = not meson.is_subproject()
 
 configure_file(
diff --git a/src/bootp.c b/src/bootp.c
index 46e9681..d78d61b 100644
--- a/src/bootp.c
+++ b/src/bootp.c
@@ -92,21 +92,22 @@
     return bc;
 }
 
-static void dhcp_decode(const struct bootp_t *bp, int *pmsg_type,
+static void dhcp_decode(const struct bootp_t *bp,
+                        const uint8_t *bp_end,
+                        int *pmsg_type,
                         struct in_addr *preq_addr)
 {
-    const uint8_t *p, *p_end;
+    const uint8_t *p;
     int len, tag;
 
     *pmsg_type = 0;
     preq_addr->s_addr = htonl(0L);
 
     p = bp->bp_vend;
-    p_end = p + DHCP_OPT_LEN;
     if (memcmp(p, rfc1533_cookie, 4) != 0)
         return;
     p += 4;
-    while (p < p_end) {
+    while (p < bp_end) {
         tag = p[0];
         if (tag == RFC1533_PAD) {
             p++;
@@ -114,10 +115,10 @@
             break;
         } else {
             p++;
-            if (p >= p_end)
+            if (p >= bp_end)
                 break;
             len = *p++;
-            if (p + len > p_end) {
+            if (p + len > bp_end) {
                 break;
             }
             DPRINTF("dhcp: tag=%d len=%d\n", tag, len);
@@ -144,7 +145,9 @@
     }
 }
 
-static void bootp_reply(Slirp *slirp, const struct bootp_t *bp)
+static void bootp_reply(Slirp *slirp,
+                        const struct bootp_t *bp,
+                        const uint8_t *bp_end)
 {
     BOOTPClient *bc = NULL;
     struct mbuf *m;
@@ -157,7 +160,7 @@
     uint8_t client_ethaddr[ETH_ALEN];
 
     /* extract exact DHCP msg type */
-    dhcp_decode(bp, &dhcp_msg_type, &preq_addr);
+    dhcp_decode(bp, bp_end, &dhcp_msg_type, &preq_addr);
     DPRINTF("bootp packet op=%d msgtype=%d", bp->bp_op, dhcp_msg_type);
     if (preq_addr.s_addr != htonl(0L))
         DPRINTF(" req_addr=%08" PRIx32 "\n", ntohl(preq_addr.s_addr));
@@ -179,9 +182,10 @@
         return;
     }
     m->m_data += IF_MAXLINKHDR;
+    m_inc(m, sizeof(struct bootp_t) + DHCP_OPT_LEN);
     rbp = (struct bootp_t *)m->m_data;
     m->m_data += sizeof(struct udpiphdr);
-    memset(rbp, 0, sizeof(struct bootp_t));
+    memset(rbp, 0, sizeof(struct bootp_t) + DHCP_OPT_LEN);
 
     if (dhcp_msg_type == DHCPDISCOVER) {
         if (preq_addr.s_addr != htonl(0L)) {
@@ -235,7 +239,7 @@
     rbp->bp_siaddr = saddr.sin_addr; /* Server IP address */
 
     q = rbp->bp_vend;
-    end = (uint8_t *)&rbp[1];
+    end = rbp->bp_vend + DHCP_OPT_LEN;
     memcpy(q, rfc1533_cookie, 4);
     q += 4;
 
@@ -351,19 +355,21 @@
         q += sizeof(nak_msg) - 1;
     }
     assert(q < end);
-    *q = RFC1533_END;
+    *q++ = RFC1533_END;
 
     daddr.sin_addr.s_addr = 0xffffffffu;
 
-    m->m_len = sizeof(struct bootp_t) - sizeof(struct ip) - sizeof(struct udphdr);
+    assert(q <= end);
+
+    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);
 }
 
 void bootp_input(struct mbuf *m)
 {
-    struct bootp_t *bp = mtod(m, struct bootp_t *);
+    struct bootp_t *bp = mtod_check(m, sizeof(struct bootp_t));
 
-    if (bp->bp_op == BOOTP_REQUEST) {
-        bootp_reply(m->slirp, bp);
+    if (bp && bp->bp_op == BOOTP_REQUEST) {
+        bootp_reply(m->slirp, bp, m_end(m));
     }
 }
diff --git a/src/bootp.h b/src/bootp.h
index a57fa51..31ce5fd 100644
--- a/src/bootp.h
+++ b/src/bootp.h
@@ -114,7 +114,7 @@
     uint8_t bp_hwaddr[16];
     uint8_t bp_sname[64];
     char bp_file[128];
-    uint8_t bp_vend[DHCP_OPT_LEN];
+    uint8_t bp_vend[];
 };
 
 typedef struct {
diff --git a/src/libslirp.h b/src/libslirp.h
index 5760d53..8e4ea33 100644
--- a/src/libslirp.h
+++ b/src/libslirp.h
@@ -68,7 +68,8 @@
     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);
 } SlirpCb;
 
diff --git a/src/mbuf.c b/src/mbuf.c
index b47f64e..36864a4 100644
--- a/src/mbuf.c
+++ b/src/mbuf.c
@@ -263,3 +263,19 @@
 
     return n;
 }
+
+void *mtod_check(struct mbuf *m, size_t len)
+{
+    if (m->m_len >= len) {
+        return m->m_data;
+    }
+
+    DEBUG_ERROR("mtod failed");
+
+    return NULL;
+}
+
+void *m_end(struct mbuf *m)
+{
+    return m->m_data + m->m_len;
+}
diff --git a/src/mbuf.h b/src/mbuf.h
index 47b3b14..34e697a 100644
--- a/src/mbuf.h
+++ b/src/mbuf.h
@@ -126,6 +126,8 @@
 int m_copy(struct mbuf *, struct mbuf *, int, int);
 struct mbuf *m_dup(Slirp *slirp, struct mbuf *m, bool copy_header, size_t header_size);
 struct mbuf *dtom(Slirp *, void *);
+void *mtod_check(struct mbuf *, size_t len);
+void *m_end(struct mbuf *);
 
 static inline void ifs_init(struct mbuf *ifm)
 {
diff --git a/src/misc.c b/src/misc.c
index 48f180b..ba5fb51 100644
--- a/src/misc.c
+++ b/src/misc.c
@@ -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;
diff --git a/src/slirp.c b/src/slirp.c
index 5d60cb5..378a70b 100644
--- a/src/slirp.c
+++ b/src/slirp.c
@@ -143,6 +143,10 @@
     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,11 +159,16 @@
         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;
             }
@@ -171,10 +180,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 +188,7 @@
         }
     }
 
-    res_nclose(&state);
+    res_ndestroy(&state);
     if (!found)
         return -1;
     return 0;
@@ -218,6 +224,12 @@
 
 #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 +240,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 &&
@@ -256,7 +268,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;
 
@@ -601,7 +613,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 +1362,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 --git a/src/slirp.h b/src/slirp.h
index e669383..89d79f3 100644
--- a/src/slirp.h
+++ b/src/slirp.h
@@ -20,10 +20,8 @@
 #include <iphlpapi.h>
 
 #else
-#if !defined(__HAIKU__)
 #define O_BINARY 0
 #endif
-#endif
 
 #ifndef _WIN32
 #include <sys/uio.h>
diff --git a/src/tcp_input.c b/src/tcp_input.c
index 36a4844..45706fe 100644
--- a/src/tcp_input.c
+++ b/src/tcp_input.c
@@ -218,6 +218,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
      */
diff --git a/src/tcp_subr.c b/src/tcp_subr.c
index 600cfa1..7c63f49 100644
--- a/src/tcp_subr.c
+++ b/src/tcp_subr.c
@@ -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);
@@ -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 */
diff --git a/src/tftp.c b/src/tftp.c
index c6950ee..a19c889 100644
--- a/src/tftp.c
+++ b/src/tftp.c
@@ -50,7 +50,7 @@
 }
 
 static int tftp_session_allocate(Slirp *slirp, struct sockaddr_storage *srcsas,
-                                 struct tftp_t *tp)
+                                 struct tftphdr *hdr)
 {
     struct tftp_session *spt;
     int k;
@@ -75,7 +75,7 @@
     memcpy(&spt->client_addr, srcsas, sockaddr_size(srcsas));
     spt->fd = -1;
     spt->block_size = 512;
-    spt->client_port = tp->udp.uh_sport;
+    spt->client_port = hdr->udp.uh_sport;
     spt->slirp = slirp;
 
     tftp_session_update(spt);
@@ -84,7 +84,7 @@
 }
 
 static int tftp_session_find(Slirp *slirp, struct sockaddr_storage *srcsas,
-                             struct tftp_t *tp)
+                             struct tftphdr *hdr)
 {
     struct tftp_session *spt;
     int k;
@@ -94,7 +94,7 @@
 
         if (tftp_session_in_use(spt)) {
             if (sockaddr_equal(&spt->client_addr, srcsas)) {
-                if (spt->client_port == tp->udp.uh_sport) {
+                if (spt->client_port == hdr->udp.uh_sport) {
                     return k;
                 }
             }
@@ -148,13 +148,13 @@
 }
 
 static void tftp_udp_output(struct tftp_session *spt, struct mbuf *m,
-                            struct tftp_t *recv_tp)
+                            struct tftphdr *hdr)
 {
     if (spt->client_addr.ss_family == AF_INET6) {
         struct sockaddr_in6 sa6, da6;
 
         sa6.sin6_addr = spt->slirp->vhost_addr6;
-        sa6.sin6_port = recv_tp->udp.uh_dport;
+        sa6.sin6_port = hdr->udp.uh_dport;
         da6.sin6_addr = ((struct sockaddr_in6 *)&spt->client_addr)->sin6_addr;
         da6.sin6_port = spt->client_port;
 
@@ -163,7 +163,7 @@
         struct sockaddr_in sa4, da4;
 
         sa4.sin_addr = spt->slirp->vhost_addr;
-        sa4.sin_port = recv_tp->udp.uh_dport;
+        sa4.sin_port = hdr->udp.uh_dport;
         da4.sin_addr = ((struct sockaddr_in *)&spt->client_addr)->sin_addr;
         da4.sin_port = spt->client_port;
 
@@ -185,14 +185,14 @@
 
     tp = tftp_prep_mbuf_data(spt, m);
 
-    tp->tp_op = htons(TFTP_OACK);
+    tp->hdr.tp_op = htons(TFTP_OACK);
     for (i = 0; i < nb; i++) {
         n += slirp_fmt0(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%s", keys[i]);
         n += slirp_fmt0(tp->x.tp_buf + n, sizeof(tp->x.tp_buf) - n, "%u", values[i]);
     }
 
-    m->m_len = G_SIZEOF_MEMBER(struct tftp_t, tp_op) + n;
-    tftp_udp_output(spt, m, recv_tp);
+    m->m_len = G_SIZEOF_MEMBER(struct tftp_t, hdr.tp_op) + n;
+    tftp_udp_output(spt, m, &recv_tp->hdr);
 
     return 0;
 }
@@ -213,21 +213,21 @@
 
     tp = tftp_prep_mbuf_data(spt, m);
 
-    tp->tp_op = htons(TFTP_ERROR);
+    tp->hdr.tp_op = htons(TFTP_ERROR);
     tp->x.tp_error.tp_error_code = htons(errorcode);
     slirp_pstrcpy((char *)tp->x.tp_error.tp_msg, sizeof(tp->x.tp_error.tp_msg),
                   msg);
 
     m->m_len = sizeof(struct tftp_t) - (TFTP_BLOCKSIZE_MAX + 2) + 3 +
                strlen(msg) - sizeof(struct udphdr);
-    tftp_udp_output(spt, m, recv_tp);
+    tftp_udp_output(spt, m, &recv_tp->hdr);
 
 out:
     tftp_session_terminate(spt);
 }
 
 static void tftp_send_next_block(struct tftp_session *spt,
-                                 struct tftp_t *recv_tp)
+                                 struct tftphdr *hdr)
 {
     struct mbuf *m;
     struct tftp_t *tp;
@@ -241,7 +241,7 @@
 
     tp = tftp_prep_mbuf_data(spt, m);
 
-    tp->tp_op = htons(TFTP_DATA);
+    tp->hdr.tp_op = htons(TFTP_DATA);
     tp->x.tp_data.tp_block_nr = htons((spt->block_nr + 1) & 0xffff);
 
     nobytes = tftp_read_data(spt, spt->block_nr, tp->x.tp_data.tp_buf,
@@ -259,7 +259,7 @@
 
     m->m_len = sizeof(struct tftp_t) - (TFTP_BLOCKSIZE_MAX - nobytes) -
                sizeof(struct udphdr);
-    tftp_udp_output(spt, m, recv_tp);
+    tftp_udp_output(spt, m, hdr);
 
     if (nobytes == spt->block_size) {
         tftp_session_update(spt);
@@ -282,12 +282,12 @@
     int nb_options = 0;
 
     /* check if a session already exists and if so terminate it */
-    s = tftp_session_find(slirp, srcsas, tp);
+    s = tftp_session_find(slirp, srcsas, &tp->hdr);
     if (s >= 0) {
         tftp_session_terminate(&slirp->tftp_sessions[s]);
     }
 
-    s = tftp_session_allocate(slirp, srcsas, tp);
+    s = tftp_session_allocate(slirp, srcsas, &tp->hdr);
 
     if (s < 0) {
         return;
@@ -413,29 +413,29 @@
     }
 
     spt->block_nr = 0;
-    tftp_send_next_block(spt, tp);
+    tftp_send_next_block(spt, &tp->hdr);
 }
 
 static void tftp_handle_ack(Slirp *slirp, struct sockaddr_storage *srcsas,
-                            struct tftp_t *tp, int pktlen)
+                            struct tftphdr *hdr)
 {
     int s;
 
-    s = tftp_session_find(slirp, srcsas, tp);
+    s = tftp_session_find(slirp, srcsas, hdr);
 
     if (s < 0) {
         return;
     }
 
-    tftp_send_next_block(&slirp->tftp_sessions[s], tp);
+    tftp_send_next_block(&slirp->tftp_sessions[s], hdr);
 }
 
 static void tftp_handle_error(Slirp *slirp, struct sockaddr_storage *srcsas,
-                              struct tftp_t *tp, int pktlen)
+                              struct tftphdr *hdr)
 {
     int s;
 
-    s = tftp_session_find(slirp, srcsas, tp);
+    s = tftp_session_find(slirp, srcsas, hdr);
 
     if (s < 0) {
         return;
@@ -446,19 +446,25 @@
 
 void tftp_input(struct sockaddr_storage *srcsas, struct mbuf *m)
 {
-    struct tftp_t *tp = (struct tftp_t *)m->m_data;
+    struct tftphdr *hdr = mtod_check(m, sizeof(struct tftphdr));
 
-    switch (ntohs(tp->tp_op)) {
+    if (hdr == NULL) {
+        return;
+    }
+
+    switch (ntohs(hdr->tp_op)) {
     case TFTP_RRQ:
-        tftp_handle_rrq(m->slirp, srcsas, tp, m->m_len);
+        tftp_handle_rrq(m->slirp, srcsas,
+                        mtod(m, struct tftp_t *),
+                        m->m_len);
         break;
 
     case TFTP_ACK:
-        tftp_handle_ack(m->slirp, srcsas, tp, m->m_len);
+        tftp_handle_ack(m->slirp, srcsas, hdr);
         break;
 
     case TFTP_ERROR:
-        tftp_handle_error(m->slirp, srcsas, tp, m->m_len);
+        tftp_handle_error(m->slirp, srcsas, hdr);
         break;
     }
 }
diff --git a/src/tftp.h b/src/tftp.h
index 6d75478..cafab03 100644
--- a/src/tftp.h
+++ b/src/tftp.h
@@ -20,9 +20,13 @@
 #define TFTP_FILENAME_MAX 512
 #define TFTP_BLOCKSIZE_MAX 1428
 
-struct tftp_t {
+struct tftphdr {
     struct udphdr udp;
     uint16_t tp_op;
+} SLIRP_PACKED;
+
+struct tftp_t {
+    struct tftphdr hdr;
     union {
         struct {
             uint16_t tp_block_nr;
diff --git a/src/udp.c b/src/udp.c
index 767ca85..06b7b7d 100644
--- a/src/udp.c
+++ b/src/udp.c
@@ -96,7 +96,10 @@
     /*
      * Get IP and UDP header together in first mbuf.
      */
-    ip = mtod(m, struct ip *);
+    ip = mtod_check(m, iphlen + sizeof(struct udphdr));
+    if (ip == NULL) {
+        goto bad;
+    }
     uh = (struct udphdr *)((char *)ip + iphlen);
 
     /*
diff --git a/src/udp6.c b/src/udp6.c
index 18ce998..efeac5c 100644
--- a/src/udp6.c
+++ b/src/udp6.c
@@ -31,7 +31,10 @@
     ip = mtod(m, struct ip6 *);
     m->m_len -= iphlen;
     m->m_data += iphlen;
-    uh = mtod(m, struct udphdr *);
+    uh = mtod_check(m, sizeof(struct udphdr));
+    if (uh == NULL) {
+        goto bad;
+    }
     m->m_len += iphlen;
     m->m_data -= iphlen;