Hi, ip_fragment() and ip6_fragment() do nearly the same thing, but they are implemented differently.
The idea of this diff is to move things around. Then only differences from the IP standards but not in coding style remain. This allows to compare both functions easily. In IPv4 assume that m_pkthdr is correct and take the length from the, like in IPv6. ok? bluhm Index: netinet/ip_output.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet/ip_output.c,v retrieving revision 1.381 diff -u -p -r1.381 ip_output.c --- netinet/ip_output.c 25 May 2022 19:48:46 -0000 1.381 +++ netinet/ip_output.c 11 Aug 2022 07:33:20 -0000 @@ -677,73 +677,78 @@ ip_output_ipsec_send(struct tdb *tdb, st #endif /* IPSEC */ int -ip_fragment(struct mbuf *m, struct mbuf_list *fml, struct ifnet *ifp, +ip_fragment(struct mbuf *m0, 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 *m; + struct ip *ip; + int firstlen, hlen, tlen, len, off; int error; ml_init(fml); - ml_enqueue(fml, m); + ml_enqueue(fml, m0); - ip = mtod(m, struct ip *); + ip = mtod(m0, struct ip *); hlen = ip->ip_hl << 2; + tlen = m0->m_pkthdr.len; len = (mtu - hlen) &~ 7; if (len < 8) { error = EMSGSIZE; goto bad; } + firstlen = len; /* * If we are doing fragmentation, we can't defer TCP/UDP * checksumming; compute the checksum and clear the flag. */ - in_proto_cksum_out(m, NULL); - firstlen = len; + in_proto_cksum_out(m0, NULL); /* * Loop through length of segment after first fragment, * make new header and copy data of each part and link onto chain. */ - m0 = m; - mhlen = sizeof (struct ip); - for (off = hlen + len; off < ntohs(ip->ip_len); off += len) { + for (off = hlen + firstlen; off < tlen; off += len) { + struct ip *mhip; + int mhlen; + 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) goto bad; m->m_data += max_linkhdr; mhip = mtod(m, struct ip *); *mhip = *ip; - if (hlen > sizeof (struct ip)) { - mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); + if (hlen > sizeof(struct ip)) { + mhlen = ip_optcopy(ip, mhip) + sizeof(struct ip); mhip->ip_hl = mhlen >> 2; - } + } else + mhlen = sizeof(struct ip); m->m_len = mhlen; + mhip->ip_off = ((off - hlen) >> 3) + (ntohs(ip->ip_off) & ~IP_MF); if (ip->ip_off & htons(IP_MF)) mhip->ip_off |= IP_MF; - if (off + len >= ntohs(ip->ip_len)) - len = ntohs(ip->ip_len) - off; + if (off + len >= tlen) + len = tlen - off; else mhip->ip_off |= IP_MF; - mhip->ip_len = htons((u_int16_t)(len + mhlen)); + mhip->ip_off = htons(mhip->ip_off); + + m->m_pkthdr.len = mhlen + len; + mhip->ip_len = htons(m->m_pkthdr.len); m->m_next = m_copym(m0, off, len, M_NOWAIT); if (m->m_next == NULL) { error = ENOBUFS; goto bad; } - m->m_pkthdr.len = mhlen + len; - m->m_pkthdr.ph_ifidx = 0; - mhip->ip_off = htons((u_int16_t)mhip->ip_off); + mhip->ip_sum = 0; if (in_ifcap_cksum(m, ifp, IFCAP_CSUM_IPv4)) m->m_pkthdr.csum_flags |= M_IPV4_CSUM_OUT; @@ -752,15 +757,16 @@ ip_fragment(struct mbuf *m, struct mbuf_ mhip->ip_sum = in_cksum(m, mhlen); } } + /* * Update first fragment by trimming what's been copied out * and updating header, then send each fragment (in order). */ m = m0; - m_adj(m, hlen + firstlen - ntohs(ip->ip_len)); - m->m_pkthdr.len = hlen + firstlen; - ip->ip_len = htons((u_int16_t)m->m_pkthdr.len); + m_adj(m, hlen + firstlen - tlen); ip->ip_off |= htons(IP_MF); + ip->ip_len = htons(m->m_pkthdr.len); + ip->ip_sum = 0; if (in_ifcap_cksum(m, ifp, IFCAP_CSUM_IPv4)) m->m_pkthdr.csum_flags |= M_IPV4_CSUM_OUT; Index: netinet6/ip6_output.c =================================================================== RCS file: /data/mirror/openbsd/cvs/src/sys/netinet6/ip6_output.c,v retrieving revision 1.270 diff -u -p -r1.270 ip6_output.c --- netinet6/ip6_output.c 8 Aug 2022 23:00:51 -0000 1.270 +++ netinet6/ip6_output.c 11 Aug 2022 07:33:20 -0000 @@ -793,30 +793,31 @@ int ip6_fragment(struct mbuf *m0, struct mbuf_list *fml, int hlen, u_char nextproto, u_long mtu) { - struct mbuf *m, *m_frgpart; - struct ip6_hdr *mhip6; - struct ip6_frag *ip6f; - u_int32_t id; - int tlen, len, off; - int error; + struct mbuf *m; + struct ip6_hdr *ip6; + u_int32_t id; + int tlen, len, off; + int error; ml_init(fml); + ip6 = mtod(m0, struct ip6_hdr *); tlen = m0->m_pkthdr.len; len = (mtu - hlen - sizeof(struct ip6_frag)) & ~7; if (len < 8) { error = EMSGSIZE; goto bad; } - id = htonl(ip6_randomid()); /* - * Loop through length of segment after first fragment, + * Loop through length of segment, * make new header and copy data of each part and link onto chain. */ for (off = hlen; off < tlen; off += len) { struct mbuf *mlast; + struct ip6_hdr *mhip6; + struct ip6_frag *ip6f; MGETHDR(m, M_DONTWAIT, MT_HEADER); if (m == NULL) { @@ -824,29 +825,33 @@ ip6_fragment(struct mbuf *m0, struct mbu goto bad; } ml_enqueue(fml, m); + if ((error = m_dup_pkthdr(m, m0, M_DONTWAIT)) != 0) goto bad; m->m_data += max_linkhdr; mhip6 = mtod(m, struct ip6_hdr *); - *mhip6 = *mtod(m0, struct ip6_hdr *); - m->m_len = sizeof(*mhip6); + *mhip6 = *ip6; + m->m_len = sizeof(struct ip6_hdr); + if ((error = ip6_insertfraghdr(m0, m, hlen, &ip6f)) != 0) goto bad; - ip6f->ip6f_offlg = htons((u_int16_t)((off - hlen) & ~7)); + ip6f->ip6f_offlg = htons((off - hlen) & ~7); if (off + len >= tlen) len = tlen - off; else 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) { + + m->m_pkthdr.len = hlen + sizeof(struct ip6_frag) + len; + mhip6->ip6_plen = htons(m->m_pkthdr.len - + sizeof(struct ip6_hdr)); + for (mlast = m; mlast->m_next; mlast = mlast->m_next) + ; + mlast->m_next = m_copym(m0, off, len, M_DONTWAIT); + if (mlast->m_next == NULL) { error = ENOBUFS; goto bad; } - for (mlast = m; mlast->m_next; mlast = mlast->m_next) - ; - mlast->m_next = m_frgpart; - m->m_pkthdr.len = len + hlen + sizeof(*ip6f); + ip6f->ip6f_reserved = 0; ip6f->ip6f_ident = id; ip6f->ip6f_nxt = nextproto;