From: bluhm Date: Mon, 1 Mar 2021 11:05:42 +0000 (+0000) Subject: Refactor ip_fragment() and ip6_fragment(). Use a mbuf list to X-Git-Url: http://artulab.com/gitweb/?a=commitdiff_plain;h=2818ef99e36cb3e1aa903761b5b1dde22c59b89f;p=openbsd Refactor ip_fragment() and ip6_fragment(). Use a mbuf list to 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@ --- diff --git a/sys/net/if_bridge.c b/sys/net/if_bridge.c index bb91143b35d..3ec31ff993a 100644 --- a/sys/net/if_bridge.c +++ b/sys/net/if_bridge.c @@ -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; diff --git a/sys/net/pf.c b/sys/net/pf.c index fc76da389cf..8eee8df8c23 100644 --- a/sys/net/pf.c +++ b/sys/net/pf.c @@ -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: diff --git a/sys/net/pf_norm.c b/sys/net/pf_norm.c index 9bd1514167c..0e892c5d28d 100644 --- a/sys/net/pf_norm.c +++ b/sys/net/pf_norm.c @@ -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 @@ -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 */ diff --git a/sys/netinet/ip_output.c b/sys/netinet/ip_output.c index 74029056b70..cd28e534d79 100644 --- a/sys/netinet/ip_output.c +++ b/sys/netinet/ip_output.c @@ -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); } diff --git a/sys/netinet/ip_var.h b/sys/netinet/ip_var.h index b54e687110b..7ede24ce922 100644 --- a/sys/netinet/ip_var.h +++ b/sys/netinet/ip_var.h @@ -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 *); diff --git a/sys/netinet6/ip6_id.c b/sys/netinet6/ip6_id.c index 40584a18cdf..10c786d6aba 100644 --- a/sys/netinet6/ip6_id.c +++ b/sys/netinet6/ip6_id.c @@ -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 #include +#include #include #include diff --git a/sys/netinet6/ip6_output.c b/sys/netinet6/ip6_output.c index 79add8aa7f5..3591f17b922 100644 --- a/sys/netinet6/ip6_output.c +++ b/sys/netinet6/ip6_output.c @@ -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 diff --git a/sys/netinet6/ip6_var.h b/sys/netinet6/ip6_var.h index 640d89e5f6a..cbb81efb872 100644 --- a/sys/netinet6/ip6_var.h +++ b/sys/netinet6/ip6_var.h @@ -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 *);