Refactor ip_fragment() and ip6_fragment(). Use a mbuf list to
authorbluhm <bluhm@openbsd.org>
Mon, 1 Mar 2021 11:05:42 +0000 (11:05 +0000)
committerbluhm <bluhm@openbsd.org>
Mon, 1 Mar 2021 11:05:42 +0000 (11:05 +0000)
simplify the handling of the fragment list.  Now the functions
ip_fragment() and ip6_fragment() always consume the mbuf.  They
free the mbuf and mbuf list in case of an error and take care about
the counter.  Adjust the code a bit to make v4 and v6 look similar.
Fixes a potential mbuf leak when pf_route6() called pf_refragment6()
and it failed.  Now the mbuf is always freed by ip6_fragment().
OK dlg@ mvs@

sys/net/if_bridge.c
sys/net/pf.c
sys/net/pf_norm.c
sys/netinet/ip_output.c
sys/netinet/ip_var.h
sys/netinet6/ip6_id.c
sys/netinet6/ip6_output.c
sys/netinet6/ip6_var.h

index bb91143..3ec31ff 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: if_bridge.c,v 1.352 2021/02/25 02:48:21 dlg Exp $     */
+/*     $OpenBSD: if_bridge.c,v 1.353 2021/03/01 11:05:42 bluhm Exp $   */
 
 /*
  * Copyright (c) 1999, 2000 Jason L. Wright (jason@thought.net)
@@ -1853,7 +1853,7 @@ bridge_fragment(struct ifnet *brifp, struct ifnet *ifp, struct ether_header *eh,
     struct mbuf *m)
 {
        struct llc llc;
-       struct mbuf *m0;
+       struct mbuf_list fml;
        int error = 0;
        int hassnap = 0;
        u_int16_t etype;
@@ -1911,40 +1911,32 @@ bridge_fragment(struct ifnet *brifp, struct ifnet *ifp, struct ether_header *eh,
                return;
        }
 
-       error = ip_fragment(m, ifp, ifp->if_mtu);
-       if (error) {
-               m = NULL;
-               goto dropit;
-       }
+       error = ip_fragment(m, &fml, ifp, ifp->if_mtu);
+       if (error)
+               return;
 
-       for (; m; m = m0) {
-               m0 = m->m_nextpkt;
-               m->m_nextpkt = NULL;
-               if (error == 0) {
-                       if (hassnap) {
-                               M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT);
-                               if (m == NULL) {
-                                       error = ENOBUFS;
-                                       continue;
-                               }
-                               bcopy(&llc, mtod(m, caddr_t),
-                                   LLC_SNAPFRAMELEN);
-                       }
-                       M_PREPEND(m, sizeof(*eh), M_DONTWAIT);
+       while ((m = ml_dequeue(&fml)) != NULL) {
+               if (hassnap) {
+                       M_PREPEND(m, LLC_SNAPFRAMELEN, M_DONTWAIT);
                        if (m == NULL) {
                                error = ENOBUFS;
-                               continue;
-                       }
-                       bcopy(eh, mtod(m, caddr_t), sizeof(*eh));
-                       error = bridge_ifenqueue(brifp, ifp, m);
-                       if (error) {
-                               continue;
+                               break;
                        }
-               } else
-                       m_freem(m);
+                       bcopy(&llc, mtod(m, caddr_t), LLC_SNAPFRAMELEN);
+               }
+               M_PREPEND(m, sizeof(*eh), M_DONTWAIT);
+               if (m == NULL) {
+                       error = ENOBUFS;
+                       break;
+               }
+               bcopy(eh, mtod(m, caddr_t), sizeof(*eh));
+               error = bridge_ifenqueue(brifp, ifp, m);
+               if (error)
+                       break;
        }
-
-       if (error == 0)
+       if (error)
+               ml_purge(&fml);
+       else
                ipstat_inc(ips_fragmented);
 
        return;
index fc76da3..8eee8df 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: pf.c,v 1.1112 2021/02/23 11:43:40 mvs Exp $ */
+/*     $OpenBSD: pf.c,v 1.1113 2021/03/01 11:05:42 bluhm Exp $ */
 
 /*
  * Copyright (c) 2001 Daniel Hartmeier
@@ -5969,7 +5969,8 @@ pf_rtlabel_match(struct pf_addr *addr, sa_family_t af, struct pf_addr_wrap *aw,
 void
 pf_route(struct pf_pdesc *pd, struct pf_state *s)
 {
-       struct mbuf             *m0, *m1;
+       struct mbuf             *m0;
+       struct mbuf_list         fml;
        struct sockaddr_in      *dst, sin;
        struct rtentry          *rt = NULL;
        struct ip               *ip;
@@ -6078,23 +6079,18 @@ pf_route(struct pf_pdesc *pd, struct pf_state *s)
                goto bad;
        }
 
-       m1 = m0;
-       error = ip_fragment(m0, ifp, ifp->if_mtu);
-       if (error) {
-               m0 = NULL;
-               goto bad;
-       }
+       error = ip_fragment(m0, &fml, ifp, ifp->if_mtu);
+       if (error)
+               goto done;
 
-       for (m0 = m1; m0; m0 = m1) {
-               m1 = m0->m_nextpkt;
-               m0->m_nextpkt = NULL;
-               if (error == 0)
-                       error = ifp->if_output(ifp, m0, sintosa(dst), rt);
-               else
-                       m_freem(m0);
+       while ((m0 = ml_dequeue(&fml)) != NULL) {
+               error = ifp->if_output(ifp, m0, sintosa(dst), rt);
+               if (error)
+                       break;
        }
-
-       if (error == 0)
+       if (error)
+               ml_purge(&fml);
+       else
                ipstat_inc(ips_fragmented);
 
 done:
index 9bd1514..0e892c5 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: pf_norm.c,v 1.221 2021/02/22 13:04:56 bluhm Exp $ */
+/*     $OpenBSD: pf_norm.c,v 1.222 2021/03/01 11:05:42 bluhm Exp $ */
 
 /*
  * Copyright 2001 Niels Provos <provos@citi.umich.edu>
@@ -966,12 +966,13 @@ int
 pf_refragment6(struct mbuf **m0, struct m_tag *mtag, struct sockaddr_in6 *dst,
     struct ifnet *ifp, struct rtentry *rt)
 {
-       struct mbuf             *m = *m0, *t;
+       struct mbuf             *m = *m0;
+       struct mbuf_list         fml;
        struct pf_fragment_tag  *ftag = (struct pf_fragment_tag *)(mtag + 1);
        u_int32_t                mtu;
        u_int16_t                hdrlen, extoff, maxlen;
        u_int8_t                 proto;
-       int                      error, action;
+       int                      error;
 
        hdrlen = ftag->ft_hdrlen;
        extoff = ftag->ft_extoff;
@@ -1009,39 +1010,25 @@ pf_refragment6(struct mbuf **m0, struct m_tag *mtag, struct sockaddr_in6 *dst,
         * we drop the packet.
         */
        mtu = hdrlen + sizeof(struct ip6_frag) + maxlen;
-       error = ip6_fragment(m, hdrlen, proto, mtu);
-
-       m = (*m0)->m_nextpkt;
-       (*m0)->m_nextpkt = NULL;
-       if (error == 0) {
-               /* The first mbuf contains the unfragmented packet */
-               m_freemp(m0);
-               action = PF_PASS;
-       } else {
-               /* Drop expects an mbuf to free */
+       error = ip6_fragment(m, &fml, hdrlen, proto, mtu);
+       *m0 = NULL;     /* ip6_fragment() has consumed original packet. */
+       if (error) {
                DPFPRINTF(LOG_NOTICE, "refragment error %d", error);
-               action = PF_DROP;
+               return (PF_DROP);
        }
 
-       for (t = m; m; m = t) {
-               t = m->m_nextpkt;
-               m->m_nextpkt = NULL;
+       while ((m = ml_dequeue(&fml)) != NULL) {
                m->m_pkthdr.pf.flags |= PF_TAG_REFRAGMENTED;
-               if (error == 0) {
-                       if (ifp == NULL) {
-                               ip6_forward(m, NULL, 0);
-                       } else if ((u_long)m->m_pkthdr.len <= ifp->if_mtu) {
-                               ifp->if_output(ifp, m, sin6tosa(dst), rt);
-                       } else {
-                               icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0,
-                                   ifp->if_mtu);
-                       }
+               if (ifp == NULL) {
+                       ip6_forward(m, NULL, 0);
+               } else if ((u_long)m->m_pkthdr.len <= ifp->if_mtu) {
+                       ifp->if_output(ifp, m, sin6tosa(dst), rt);
                } else {
-                       m_freem(m);
+                       icmp6_error(m, ICMP6_PACKET_TOO_BIG, 0, ifp->if_mtu);
                }
        }
 
-       return (action);
+       return (PF_PASS);
 }
 #endif /* INET6 */
 
index 7402905..cd28e53 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip_output.c,v 1.367 2021/02/23 12:14:10 bluhm Exp $   */
+/*     $OpenBSD: ip_output.c,v 1.368 2021/03/01 11:05:42 bluhm Exp $   */
 /*     $NetBSD: ip_output.c,v 1.28 1996/02/13 23:43:07 christos Exp $  */
 
 /*
@@ -96,12 +96,12 @@ ip_output_ipsec_send(struct tdb *, struct mbuf *, struct route *, int);
  * The mbuf opt, if present, will not be freed.
  */
 int
-ip_output(struct mbuf *m0, struct mbuf *opt, struct route *ro, int flags,
+ip_output(struct mbuf *m, struct mbuf *opt, struct route *ro, int flags,
     struct ip_moptions *imo, struct inpcb *inp, u_int32_t ipsecflowinfo)
 {
        struct ip *ip;
        struct ifnet *ifp = NULL;
-       struct mbuf *m = m0;
+       struct mbuf_list fml;
        int hlen = sizeof (struct ip);
        int error = 0;
        struct route iproute;
@@ -502,22 +502,18 @@ sendit:
                goto bad;
        }
 
-       error = ip_fragment(m, ifp, mtu);
-       if (error) {
-               m = m0 = NULL;
-               goto bad;
-       }
+       error = ip_fragment(m, &fml, ifp, mtu);
+       if (error)
+               goto done;
 
-       for (; m; m = m0) {
-               m0 = m->m_nextpkt;
-               m->m_nextpkt = NULL;
-               if (error == 0)
-                       error = ifp->if_output(ifp, m, sintosa(dst), ro->ro_rt);
-               else
-                       m_freem(m);
+       while ((m = ml_dequeue(&fml)) != NULL) {
+               error = ifp->if_output(ifp, m, sintosa(dst), ro->ro_rt);
+               if (error)
+                       break;
        }
-
-       if (error == 0)
+       if (error)
+               ml_purge(&fml);
+       else
                ipstat_inc(ips_fragmented);
 
 done:
@@ -525,6 +521,7 @@ done:
                rtfree(ro->ro_rt);
        if_put(ifp);
        return (error);
+
 bad:
        m_freem(m);
        goto done;
@@ -651,23 +648,24 @@ ip_output_ipsec_send(struct tdb *tdb, struct mbuf *m, struct route *ro, int fwd)
 #endif /* IPSEC */
 
 int
-ip_fragment(struct mbuf *m, struct ifnet *ifp, u_long mtu)
+ip_fragment(struct mbuf *m, struct mbuf_list *fml, struct ifnet *ifp,
+    u_long mtu)
 {
        struct ip *ip, *mhip;
        struct mbuf *m0;
        int len, hlen, off;
        int mhlen, firstlen;
-       struct mbuf **mnext;
-       int fragments = 0;
-       int error = 0;
+       int error;
+
+       ml_init(fml);
+       ml_enqueue(fml, m);
 
        ip = mtod(m, struct ip *);
        hlen = ip->ip_hl << 2;
-
        len = (mtu - hlen) &~ 7;
        if (len < 8) {
-               m_freem(m);
-               return (EMSGSIZE);
+               error = EMSGSIZE;
+               goto bad;
        }
 
        /*
@@ -676,7 +674,6 @@ ip_fragment(struct mbuf *m, struct ifnet *ifp, u_long mtu)
         */
        in_proto_cksum_out(m, NULL);
        firstlen = len;
-       mnext = &m->m_nextpkt;
 
        /*
         * Loop through length of segment after first fragment,
@@ -687,12 +684,10 @@ ip_fragment(struct mbuf *m, struct ifnet *ifp, u_long mtu)
        for (off = hlen + len; off < ntohs(ip->ip_len); off += len) {
                MGETHDR(m, M_DONTWAIT, MT_HEADER);
                if (m == NULL) {
-                       ipstat_inc(ips_odropped);
                        error = ENOBUFS;
-                       goto sendorfree;
+                       goto bad;
                }
-               *mnext = m;
-               mnext = &m->m_nextpkt;
+               ml_enqueue(fml, m);
                m->m_data += max_linkhdr;
                mhip = mtod(m, struct ip *);
                *mhip = *ip;
@@ -715,10 +710,9 @@ ip_fragment(struct mbuf *m, struct ifnet *ifp, u_long mtu)
                        mhip->ip_off |= IP_MF;
                mhip->ip_len = htons((u_int16_t)(len + mhlen));
                m->m_next = m_copym(m0, off, len, M_NOWAIT);
-               if (m->m_next == 0) {
-                       ipstat_inc(ips_odropped);
+               if (m->m_next == NULL) {
                        error = ENOBUFS;
-                       goto sendorfree;
+                       goto bad;
                }
                m->m_pkthdr.len = mhlen + len;
                m->m_pkthdr.ph_ifidx = 0;
@@ -730,8 +724,6 @@ ip_fragment(struct mbuf *m, struct ifnet *ifp, u_long mtu)
                        ipstat_inc(ips_outswcsum);
                        mhip->ip_sum = in_cksum(m, mhlen);
                }
-               ipstat_inc(ips_ofragments);
-               fragments++;
        }
        /*
         * Update first fragment by trimming what's been copied out
@@ -749,15 +741,13 @@ ip_fragment(struct mbuf *m, struct ifnet *ifp, u_long mtu)
                ipstat_inc(ips_outswcsum);
                ip->ip_sum = in_cksum(m, hlen);
        }
-sendorfree:
-       if (error) {
-               for (m = m0; m; m = m0) {
-                       m0 = m->m_nextpkt;
-                       m->m_nextpkt = NULL;
-                       m_freem(m);
-               }
-       }
 
+       ipstat_add(ips_ofragments, ml_len(fml));
+       return (0);
+
+bad:
+       ipstat_inc(ips_odropped);
+       ml_purge(fml);
        return (error);
 }
 
index b54e687..7ede24c 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip_var.h,v 1.86 2019/12/08 11:08:22 sashan Exp $      */
+/*     $OpenBSD: ip_var.h,v 1.87 2021/03/01 11:05:42 bluhm Exp $       */
 /*     $NetBSD: ip_var.h,v 1.16 1996/02/13 23:43:20 christos Exp $     */
 
 /*
@@ -145,6 +145,12 @@ ipstat_inc(enum ipstat_counters c)
        counters_inc(ipcounters, c);
 }
 
+static inline void
+ipstat_add(enum ipstat_counters c, uint64_t v)
+{
+       counters_add(ipcounters, c, v);
+}
+
 /*
  * Structure attached to inpcb.ip_moptions and
  * passed to ip_output when IP multicast options are in use.
@@ -218,7 +224,7 @@ struct inpcb;
 
 int     ip_ctloutput(int, struct socket *, int, int, struct mbuf *);
 void    ip_flush(void);
-int     ip_fragment(struct mbuf *, struct ifnet *, u_long);
+int     ip_fragment(struct mbuf *, struct mbuf_list *, struct ifnet *, u_long);
 void    ip_freef(struct ipq *);
 void    ip_freemoptions(struct ip_moptions *);
 int     ip_getmoptions(int, struct ip_moptions *, struct mbuf *);
index 40584a1..10c786d 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip6_id.c,v 1.14 2020/06/24 22:03:44 cheloha Exp $     */
+/*     $OpenBSD: ip6_id.c,v 1.15 2021/03/01 11:05:43 bluhm Exp $       */
 /*     $NetBSD: ip6_id.c,v 1.7 2003/09/13 21:32:59 itojun Exp $        */
 /*     $KAME: ip6_id.c,v 1.8 2003/09/06 13:41:06 itojun Exp $  */
 
@@ -83,6 +83,7 @@
 
 #include <sys/param.h>
 #include <sys/kernel.h>
+#include <sys/mbuf.h>
 #include <sys/socket.h>
 #include <sys/systm.h>
 
index 79add8a..3591f17 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip6_output.c,v 1.254 2021/02/23 11:43:41 mvs Exp $    */
+/*     $OpenBSD: ip6_output.c,v 1.255 2021/03/01 11:05:43 bluhm Exp $  */
 /*     $KAME: ip6_output.c,v 1.172 2001/03/25 09:55:56 itojun Exp $    */
 
 /*
@@ -154,12 +154,12 @@ struct idgen32_ctx ip6_id_ctx;
  * which is rt_mtu.
  */
 int
-ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, struct route_in6 *ro,
+ip6_output(struct mbuf *m, struct ip6_pktopts *opt, struct route_in6 *ro,
     int flags, struct ip6_moptions *im6o, struct inpcb *inp)
 {
        struct ip6_hdr *ip6;
        struct ifnet *ifp = NULL;
-       struct mbuf *m = m0;
+       struct mbuf_list fml;
        int hlen, tlen;
        struct route_in6 ip6route;
        struct rtentry *rt = NULL;
@@ -174,6 +174,7 @@ ip6_output(struct mbuf *m0, struct ip6_pktopts *opt, struct route_in6 *ro,
        struct route_in6 *ro_pmtu = NULL;
        int hdrsplit = 0;
        u_int8_t sproto = 0;
+       u_char nextproto;
 #ifdef IPSEC
        struct tdb *tdb = NULL;
 #endif /* IPSEC */
@@ -709,64 +710,47 @@ reroute:
                /* jumbo payload cannot be fragmented */
                error = EMSGSIZE;
                goto bad;
-       } else {
-               u_char nextproto;
-#if 0
-               struct ip6ctlparam ip6cp;
-               u_int32_t mtu32;
-#endif
-
-               /*
-                * Too large for the destination or interface;
-                * fragment if possible.
-                * Must be able to put at least 8 bytes per fragment.
-                */
-               hlen = unfragpartlen;
-               if (mtu > IPV6_MAXPACKET)
-                       mtu = IPV6_MAXPACKET;
-
-               /*
-                * Change the next header field of the last header in the
-                * unfragmentable part.
-                */
-               if (exthdrs.ip6e_rthdr) {
-                       nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *);
-                       *mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT;
-               } else if (exthdrs.ip6e_dest1) {
-                       nextproto = *mtod(exthdrs.ip6e_dest1, u_char *);
-                       *mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT;
-               } else if (exthdrs.ip6e_hbh) {
-                       nextproto = *mtod(exthdrs.ip6e_hbh, u_char *);
-                       *mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT;
-               } else {
-                       nextproto = ip6->ip6_nxt;
-                       ip6->ip6_nxt = IPPROTO_FRAGMENT;
-               }
-
-               m0 = m;
-               error = ip6_fragment(m0, hlen, nextproto, mtu);
-               if (error)
-                       ip6stat_inc(ip6s_odropped);
        }
 
        /*
-        * Remove leading garbages.
+        * Too large for the destination or interface;
+        * fragment if possible.
+        * Must be able to put at least 8 bytes per fragment.
         */
-       m = m0->m_nextpkt;
-       m0->m_nextpkt = NULL;
-       m_freem(m0);
-       for (m0 = m; m; m = m0) {
-               m0 = m->m_nextpkt;
-               m->m_nextpkt = NULL;
-               if (error == 0) {
-                       ip6stat_inc(ip6s_ofragments);
-                       error = ifp->if_output(ifp, m, sin6tosa(dst),
-                           ro->ro_rt);
-               } else
-                       m_freem(m);
+       hlen = unfragpartlen;
+       if (mtu > IPV6_MAXPACKET)
+               mtu = IPV6_MAXPACKET;
+
+       /*
+        * Change the next header field of the last header in the
+        * unfragmentable part.
+        */
+       if (exthdrs.ip6e_rthdr) {
+               nextproto = *mtod(exthdrs.ip6e_rthdr, u_char *);
+               *mtod(exthdrs.ip6e_rthdr, u_char *) = IPPROTO_FRAGMENT;
+       } else if (exthdrs.ip6e_dest1) {
+               nextproto = *mtod(exthdrs.ip6e_dest1, u_char *);
+               *mtod(exthdrs.ip6e_dest1, u_char *) = IPPROTO_FRAGMENT;
+       } else if (exthdrs.ip6e_hbh) {
+               nextproto = *mtod(exthdrs.ip6e_hbh, u_char *);
+               *mtod(exthdrs.ip6e_hbh, u_char *) = IPPROTO_FRAGMENT;
+       } else {
+               nextproto = ip6->ip6_nxt;
+               ip6->ip6_nxt = IPPROTO_FRAGMENT;
        }
 
-       if (error == 0)
+       error = ip6_fragment(m, &fml, hlen, nextproto, mtu);
+       if (error)
+               goto done;
+
+       while ((m = ml_dequeue(&fml)) != NULL) {
+               error = ifp->if_output(ifp, m, sin6tosa(dst), ro->ro_rt);
+               if (error)
+                       break;
+       }
+       if (error)
+               ml_purge(&fml);
+       else
                ip6stat_inc(ip6s_fragmented);
 
 done:
@@ -776,7 +760,6 @@ done:
        } else if (ro_pmtu == &ip6route && ro_pmtu->ro_rt) {
                rtfree(ro_pmtu->ro_rt);
        }
-
        return (error);
 
 freehdrs:
@@ -791,45 +774,48 @@ bad:
 }
 
 int
-ip6_fragment(struct mbuf *m0, int hlen, u_char nextproto, u_long mtu)
+ip6_fragment(struct mbuf *m0, struct mbuf_list *fml, int hlen,
+    u_char nextproto, u_long mtu)
 {
-       struct mbuf     *m, **mnext, *m_frgpart;
+       struct mbuf     *m, *m_frgpart;
        struct ip6_hdr  *mhip6;
        struct ip6_frag *ip6f;
        u_int32_t        id;
        int              tlen, len, off;
        int              error;
 
-       id = htonl(ip6_randomid());
-
-       mnext = &m0->m_nextpkt;
-       *mnext = NULL;
+       ml_init(fml);
 
        tlen = m0->m_pkthdr.len;
        len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7;
-       if (len < 8)
-               return (EMSGSIZE);
+       if (len < 8) {
+               error = EMSGSIZE;
+               goto bad;
+       }
+
+       id = htonl(ip6_randomid());
 
        /*
         * Loop through length of segment after first fragment,
-        * make new header and copy data of each part and link onto
-        * chain.
+        * make new header and copy data of each part and link onto chain.
         */
        for (off = hlen; off < tlen; off += len) {
                struct mbuf *mlast;
 
-               if ((m = m_gethdr(M_DONTWAIT, MT_HEADER)) == NULL)
-                       return (ENOBUFS);
-               *mnext = m;
-               mnext = &m->m_nextpkt;
+               MGETHDR(m, M_DONTWAIT, MT_HEADER);
+               if (m == NULL) {
+                       error = ENOBUFS;
+                       goto bad;
+               }
+               ml_enqueue(fml, m);
                if ((error = m_dup_pkthdr(m, m0, M_DONTWAIT)) != 0)
-                       return (error);
+                       goto bad;
                m->m_data += max_linkhdr;
                mhip6 = mtod(m, struct ip6_hdr *);
                *mhip6 = *mtod(m0, struct ip6_hdr *);
                m->m_len = sizeof(*mhip6);
                if ((error = ip6_insertfraghdr(m0, m, hlen, &ip6f)) != 0)
-                       return (error);
+                       goto bad;
                ip6f->ip6f_offlg = htons((u_int16_t)((off - hlen) & ~7));
                if (off + len >= tlen)
                        len = tlen - off;
@@ -837,8 +823,10 @@ ip6_fragment(struct mbuf *m0, int hlen, u_char nextproto, u_long mtu)
                        ip6f->ip6f_offlg |= IP6F_MORE_FRAG;
                mhip6->ip6_plen = htons((u_int16_t)(len + hlen +
                    sizeof(*ip6f) - sizeof(struct ip6_hdr)));
-               if ((m_frgpart = m_copym(m0, off, len, M_DONTWAIT)) == NULL)
-                       return (ENOBUFS);
+               if ((m_frgpart = m_copym(m0, off, len, M_DONTWAIT)) == NULL) {
+                       error = ENOBUFS;
+                       goto bad;
+               }
                for (mlast = m; mlast->m_next; mlast = mlast->m_next)
                        ;
                mlast->m_next = m_frgpart;
@@ -848,7 +836,15 @@ ip6_fragment(struct mbuf *m0, int hlen, u_char nextproto, u_long mtu)
                ip6f->ip6f_nxt = nextproto;
        }
 
+       ip6stat_add(ip6s_ofragments, ml_len(fml));
+       m_freem(m0);
        return (0);
+
+bad:
+       ip6stat_inc(ip6s_odropped);
+       ml_purge(fml);
+       m_freem(m0);
+       return (error);
 }
 
 int
index 640d89e..cbb81ef 100644 (file)
@@ -1,4 +1,4 @@
-/*     $OpenBSD: ip6_var.h,v 1.87 2021/01/11 13:28:54 bluhm Exp $      */
+/*     $OpenBSD: ip6_var.h,v 1.88 2021/03/01 11:05:43 bluhm Exp $      */
 /*     $KAME: ip6_var.h,v 1.33 2000/06/11 14:59:20 jinmei Exp $        */
 
 /*
@@ -322,7 +322,7 @@ void        ip6_forward(struct mbuf *, struct rtentry *, int);
 void   ip6_mloopback(struct ifnet *, struct mbuf *, struct sockaddr_in6 *);
 int    ip6_output(struct mbuf *, struct ip6_pktopts *, struct route_in6 *, int,
            struct ip6_moptions *, struct inpcb *);
-int    ip6_fragment(struct mbuf *, int, u_char, u_long);
+int    ip6_fragment(struct mbuf *, struct mbuf_list *, int, u_char, u_long);
 int    ip6_ctloutput(int, struct socket *, int, int, struct mbuf *);
 int    ip6_raw_ctloutput(int, struct socket *, int, int, struct mbuf *);
 void   ip6_initpktopts(struct ip6_pktopts *);