Note: this is the second edition of this patch, which includes bugfixes found inside the added code, which were fixed in OpenBSD 3.2-current. This patch applies to 3.1-stable as of October 11, 2002 (-rOPENBSD_3_1). Index: sys/netinet/ip_var.h =================================================================== RCS file: /cvs/src/sys/netinet/ip_var.h,v retrieving revision 1.22 diff -u -r1.22 ip_var.h --- sys/netinet/ip_var.h 14 Mar 2002 01:27:11 -0000 1.22 +++ sys/netinet/ip_var.h 11 Oct 2002 19:23:23 -0000 @@ -172,6 +172,7 @@ void ip_drain(void); void ip_flush(void); void ip_forward(struct mbuf *, int); +int ip_fragment(struct mbuf *, struct ifnet *); void ip_freef(struct ipq *); void ip_freemoptions(struct ip_moptions *); int ip_getmoptions(int, struct ip_moptions *, struct mbuf **); Index: sys/netinet/ip_output.c =================================================================== RCS file: /cvs/src/sys/netinet/ip_output.c,v retrieving revision 1.143 diff -u -r1.143 ip_output.c --- sys/netinet/ip_output.c 15 Mar 2002 18:19:52 -0000 1.143 +++ sys/netinet/ip_output.c 11 Oct 2002 19:23:26 -0000 @@ -99,11 +99,11 @@ int ip_output(struct mbuf *m0, ...) { - register struct ip *ip, *mhip; - register struct ifnet *ifp; + struct ip *ip; + struct ifnet *ifp; struct mbuf *m = m0; - register int hlen = sizeof (struct ip); - int len, off, error = 0; + int hlen = sizeof (struct ip); + int len, error = 0; struct route iproute; struct sockaddr_in *dst; struct in_ifaddr *ia; @@ -427,7 +427,7 @@ * of outgoing interface. */ if (ip->ip_src.s_addr == INADDR_ANY) { - register struct in_ifaddr *ia; + struct in_ifaddr *ia; for (ia = in_ifaddr.tqh_first; ia; @@ -705,12 +705,53 @@ ipstat.ips_cantfrag++; goto bad; } - len = (ifp->if_mtu - hlen) &~ 7; - if (len < 8) { - error = EMSGSIZE; + + error = ip_fragment(m, ifp); + if (error == EMSGSIZE) goto bad; + + for (; m; m = m0) { + m0 = m->m_nextpkt; + m->m_nextpkt = 0; + if (error == 0) + error = (*ifp->if_output)(ifp, m, sintosa(dst), + ro->ro_rt); + else + m_freem(m); } + if (error == 0) + ipstat.ips_fragmented++; + +done: + if (ro == &iproute && (flags & IP_ROUTETOIF) == 0 && ro->ro_rt) + RTFREE(ro->ro_rt); + return (error); +bad: +#ifdef IPSEC + if (error == EMSGSIZE && ip_mtudisc && icmp_mtu != 0) + ipsec_adjust_mtu(m, icmp_mtu); +#endif + m_freem(m0); + goto done; +} + +int +ip_fragment(struct mbuf *m, struct ifnet *ifp) +{ + struct ip *ip, *mhip; + struct mbuf *m0; + int len, hlen, off; + int mhlen, firstlen; + struct mbuf **mnext; + + ip = mtod(m, struct ip *); + hlen = ip->ip_hl << 2; + + len = (ifp->if_mtu - hlen) &~ 7; + if (len < 8) + return (EMSGSIZE); + /* * If we are doing fragmentation, we can't defer TCP/UDP * checksumming; compute the checksum and clear the flag. @@ -720,9 +761,8 @@ m->m_pkthdr.csum &= ~(M_UDPV4_CSUM_OUT | M_TCPV4_CSUM_OUT); } - { - int mhlen, firstlen = len; - struct mbuf **mnext = &m->m_nextpkt; + firstlen = len; + mnext = &m->m_nextpkt; /* * Loop through length of segment after first fragment, @@ -733,9 +773,8 @@ for (off = hlen + len; off < (u_int16_t)ip->ip_len; off += len) { MGETHDR(m, M_DONTWAIT, MT_HEADER); if (m == 0) { - error = ENOBUFS; ipstat.ips_odropped++; - goto sendorfree; + return (ENOBUFS); } *mnext = m; mnext = &m->m_nextpkt; @@ -759,9 +798,8 @@ mhip->ip_len = htons((u_int16_t)(len + mhlen)); m->m_next = m_copy(m0, off, len); if (m->m_next == 0) { - error = ENOBUFS; /* ??? */ ipstat.ips_odropped++; - goto sendorfree; + return (ENOBUFS); /* ??? */ } m->m_pkthdr.len = mhlen + len; m->m_pkthdr.rcvif = (struct ifnet *)0; @@ -793,31 +831,8 @@ ip->ip_sum = 0; ip->ip_sum = in_cksum(m, hlen); } -sendorfree: - for (m = m0; m; m = m0) { - m0 = m->m_nextpkt; - m->m_nextpkt = 0; - if (error == 0) - error = (*ifp->if_output)(ifp, m, sintosa(dst), - ro->ro_rt); - else - m_freem(m); - } - if (error == 0) - ipstat.ips_fragmented++; - } -done: - if (ro == &iproute && (flags & IP_ROUTETOIF) == 0 && ro->ro_rt) - RTFREE(ro->ro_rt); - return (error); -bad: -#ifdef IPSEC - if (error == EMSGSIZE && ip_mtudisc && icmp_mtu != 0) - ipsec_adjust_mtu(m, icmp_mtu); -#endif - m_freem(m0); - goto done; + return (0); } /* @@ -827,13 +842,13 @@ */ static struct mbuf * ip_insertoptions(m, opt, phlen) - register struct mbuf *m; + struct mbuf *m; struct mbuf *opt; int *phlen; { - register struct ipoption *p = mtod(opt, struct ipoption *); + struct ipoption *p = mtod(opt, struct ipoption *); struct mbuf *n; - register struct ip *ip = mtod(m, struct ip *); + struct ip *ip = mtod(m, struct ip *); unsigned optlen; optlen = opt->m_len - sizeof(p->ipopt_dst); @@ -875,7 +890,7 @@ ip_optcopy(ip, jp) struct ip *ip, *jp; { - register u_char *cp, *dp; + u_char *cp, *dp; int opt, optlen, cnt; cp = (u_char *)(ip + 1); @@ -923,9 +938,9 @@ int level, optname; struct mbuf **mp; { - register struct inpcb *inp = sotoinpcb(so); - register struct mbuf *m = *mp; - register int optval = 0; + struct inpcb *inp = sotoinpcb(so); + struct mbuf *m = *mp; + int optval = 0; #ifdef IPSEC struct proc *p = curproc; /* XXX */ struct ipsec_ref *ipr; @@ -1411,10 +1426,10 @@ ip_pcbopts(pcbopt, m) #endif struct mbuf **pcbopt; - register struct mbuf *m; + struct mbuf *m; { - register int cnt, optlen; - register u_char *cp; + int cnt, optlen; + u_char *cp; u_char opt; /* turn off any old options */ @@ -1516,15 +1531,15 @@ struct ip_moptions **imop; struct mbuf *m; { - register int error = 0; + int error = 0; u_char loop; - register int i; + int i; struct in_addr addr; - register struct ip_mreq *mreq; - register struct ifnet *ifp; - register struct ip_moptions *imo = *imop; + struct ip_mreq *mreq; + struct ifnet *ifp; + struct ip_moptions *imo = *imop; struct route ro; - register struct sockaddr_in *dst; + struct sockaddr_in *dst; if (imo == NULL) { /* @@ -1749,8 +1764,8 @@ int ip_getmoptions(optname, imo, mp) int optname; - register struct ip_moptions *imo; - register struct mbuf **mp; + struct ip_moptions *imo; + struct mbuf **mp; { u_char *ttl; u_char *loop; @@ -1797,9 +1812,9 @@ */ void ip_freemoptions(imo) - register struct ip_moptions *imo; + struct ip_moptions *imo; { - register int i; + int i; if (imo != NULL) { for (i = 0; i < imo->imo_num_memberships; ++i) @@ -1817,10 +1832,10 @@ static void ip_mloopback(ifp, m, dst) struct ifnet *ifp; - register struct mbuf *m; - register struct sockaddr_in *dst; + struct mbuf *m; + struct sockaddr_in *dst; { - register struct ip *ip; + struct ip *ip; struct mbuf *copym; copym = m_copym2(m, 0, M_COPYALL, M_DONTWAIT); Index: sys/net/if_bridge.c =================================================================== RCS file: /cvs/src/sys/net/if_bridge.c,v retrieving revision 1.88 diff -u -r1.88 if_bridge.c --- sys/net/if_bridge.c 8 Apr 2002 17:49:42 -0000 1.88 +++ sys/net/if_bridge.c 11 Oct 2002 19:23:30 -0000 @@ -156,6 +156,8 @@ struct mbuf *bridge_filter(struct bridge_softc *, int, struct ifnet *, struct ether_header *, struct mbuf *m); #endif +void bridge_fragment(struct bridge_softc *, struct ifnet *, + struct altq_pktattr *, struct ether_header *, struct mbuf *); #define ETHERADDR_IS_IP_MCAST(a) \ /* struct etheraddr *a; */ \ @@ -1220,23 +1222,28 @@ if (ALTQ_IS_ENABLED(&dst_if->if_snd)) altq_etherclassify(&dst_if->if_snd, m, &pktattr); #endif + len = m->m_pkthdr.len; - mflags = m->m_flags; - s = splimp(); - IFQ_ENQUEUE(&dst_if->if_snd, m, &pktattr, error); - if (error) { - sc->sc_if.if_oerrors++; + if ((len - sizeof(struct ether_header)) > dst_if->if_mtu) + bridge_fragment(sc, dst_if, &pktattr, &eh, m); + else { + mflags = m->m_flags; + s = splimp(); + IFQ_ENQUEUE(&dst_if->if_snd, m, &pktattr, error); + if (error) { + sc->sc_if.if_oerrors++; + splx(s); + return; + } + sc->sc_if.if_opackets++; + sc->sc_if.if_obytes += len; + dst_if->if_obytes += len; + if (mflags & M_MCAST) + dst_if->if_omcasts++; + if ((dst_if->if_flags & IFF_OACTIVE) == 0) + (*dst_if->if_start)(dst_if); splx(s); - return; } - sc->sc_if.if_opackets++; - sc->sc_if.if_obytes += len; - dst_if->if_obytes += len; - if (mflags & M_MCAST) - dst_if->if_omcasts++; - if ((dst_if->if_flags & IFF_OACTIVE) == 0) - (*dst_if->if_start)(dst_if); - splx(s); } /* @@ -1487,18 +1494,22 @@ if (ALTQ_IS_ENABLED(&dst_if->if_snd)) altq_etherclassify(&dst_if->if_snd, mc, &pktattr); #endif - IFQ_ENQUEUE(&dst_if->if_snd, mc, &pktattr, error); - if (error) { - sc->sc_if.if_oerrors++; - continue; + if ((len - sizeof(struct ether_header)) > dst_if->if_mtu) + bridge_fragment(sc, dst_if, &pktattr, eh, mc); + else { + IFQ_ENQUEUE(&dst_if->if_snd, mc, &pktattr, error); + if (error) { + sc->sc_if.if_oerrors++; + continue; + } + sc->sc_if.if_opackets++; + sc->sc_if.if_obytes += len; + dst_if->if_obytes += len; + if (mflags & M_MCAST) + dst_if->if_omcasts++; + if ((dst_if->if_flags & IFF_OACTIVE) == 0) + (*dst_if->if_start)(dst_if); } - sc->sc_if.if_opackets++; - sc->sc_if.if_obytes += len; - dst_if->if_obytes += len; - if (mflags & M_MCAST) - dst_if->if_omcasts++; - if ((dst_if->if_flags & IFF_OACTIVE) == 0) - (*dst_if->if_start)(dst_if); } if (!used) @@ -2307,3 +2318,109 @@ return (NULL); } #endif /* NPF > 0 */ + +void +bridge_fragment(struct bridge_softc *sc, struct ifnet *ifp, + struct altq_pktattr *pktattr, struct ether_header *eh, struct mbuf *m) +{ + struct llc llc; + struct mbuf *m0; + int s, len, error = 0; + int hassnap = 0; + short mflags = m->m_flags; +#ifdef INET + struct ip *ip; +#endif + +#ifndef INET + goto dropit; +#else + if (eh->ether_type != htons(ETHERTYPE_IP)) { + if (eh->ether_type > ETHERMTU ||+ m->m_pkthdr.len < (LLC_SNAPFRAMELEN + + sizeof(struct ether_header))) + goto dropit; + + m_copydata(m, sizeof(struct ether_header), + LLC_SNAPFRAMELEN, (caddr_t)&llc); + + if (llc.llc_dsap != LLC_SNAP_LSAP ||+ llc.llc_ssap != LLC_SNAP_LSAP ||+ llc.llc_control != LLC_UI ||+ llc.llc_snap.org_code[0] ||+ llc.llc_snap.org_code[1] ||+ llc.llc_snap.org_code[2] ||+ llc.llc_snap.ether_type != htons(ETHERTYPE_IP)) + goto dropit; + + hassnap = 1; + } + + m_adj(m, sizeof(struct ether_header)); + if (hassnap) + m_adj(m, LLC_SNAPFRAMELEN); + + if (m->m_len < sizeof(struct ip) && + (m = m_pullup(m, sizeof(struct ip))) == NULL) + goto dropit; + ip = mtod(m, struct ip *); + NTOHS(ip->ip_len); + NTOHS(ip->ip_off); + + /* Respect IP_DF */ + if (ip->ip_off & IP_DF) + goto dropit; + + error = ip_fragment(m, ifp); + if (error == EMSGSIZE) + goto dropit; + + for (; m; m = m0) { + m0 = m->m_nextpkt; + m->m_nextpkt = 0; + 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); + if (m == NULL) { + error = ENOBUFS; + continue; + } + len = m->m_pkthdr.len; + bcopy(eh, mtod(m, caddr_t), sizeof(*eh)); + s = splimp(); + IFQ_ENQUEUE(&ifp->if_snd, m, pktattr, error); + if (error) { + sc->sc_if.if_oerrors++; + splx(s); + continue; + } + sc->sc_if.if_opackets++; + sc->sc_if.if_obytes += len; + ifp->if_obytes += len; + if (mflags & M_MCAST) + ifp->if_omcasts++; + if ((ifp->if_flags & IFF_OACTIVE) == 0) + (*ifp->if_start)(ifp); + splx(s); + + } else + m_freem(m); + } + + if (error == 0) + ipstat.ips_fragmented++; + + return; +#endif /* INET */ + dropit: + if (m != NULL) + m_freem(m); +} Index: sys/net/pf.c =================================================================== RCS file: /cvs/src/sys/net/pf.c,v retrieving revision 1.201.2.1 diff -u -r1.201.2.1 pf.c --- sys/net/pf.c 30 Sep 2002 18:30:51 -0000 1.201.2.1 +++ sys/net/pf.c 11 Oct 2002 19:23:40 -0000 @@ -4773,10 +4773,10 @@ struct route iproute; struct route *ro; struct sockaddr_in *dst; - struct ip *ip, *mhip; + struct ip *ip; struct ifnet *ifp = r->rt_ifp; int hlen; - int len, off, error = 0; + int error = 0; if (r->rt == PF_DUPTO) { m0 = m_copym2(*m, 0, M_COPYALL, M_NOWAIT); @@ -4848,107 +4848,24 @@ ipstat.ips_cantfrag++; goto bad; } - len = (ifp->if_mtu - hlen) &~ 7; - if (len < 8) { - error = EMSGSIZE; - goto bad; - } - /* - * If we are doing fragmentation, we can't defer TCP/UDP - * checksumming; compute the checksum and clear the flag. - */ - if (m0->m_pkthdr.csum & (M_TCPV4_CSUM_OUT | M_UDPV4_CSUM_OUT)) { - in_delayed_cksum(m0); - m0->m_pkthdr.csum &= ~(M_UDPV4_CSUM_OUT | M_TCPV4_CSUM_OUT); - } - { - int mhlen, firstlen = len; - struct mbuf **mnext = &m0->m_nextpkt; - - /* - * Loop through length of segment after first fragment, - * make new header and copy data of each part and link onto chain. - */ - m1 = m0; - mhlen = sizeof (struct ip); - for (off = hlen + len; off < (u_int16_t)ip->ip_len; off += len) { - MGETHDR(m0, M_DONTWAIT, MT_HEADER); - if (m0 == 0) { - error = ENOBUFS; - ipstat.ips_odropped++; - goto sendorfree; - } - *mnext = m0; - mnext = &m0->m_nextpkt; - m0->m_data += max_linkhdr; - mhip = mtod(m0, struct ip *); - *mhip = *ip; - /* we must inherit MCAST and BCAST flags */ - m0->m_flags |= m1->m_flags & (M_MCAST|M_BCAST); - if (hlen > sizeof (struct ip)) { - mhlen = ip_optcopy(ip, mhip) + sizeof (struct ip); - mhip->ip_hl = mhlen >> 2; - } - m0->m_len = mhlen; - mhip->ip_off = ((off - hlen) >> 3) + (ip->ip_off & ~IP_MF); - if (ip->ip_off & IP_MF) - mhip->ip_off |= IP_MF; - if (off + len >= (u_int16_t)ip->ip_len) - len = (u_int16_t)ip->ip_len - off; - else - mhip->ip_off |= IP_MF; - mhip->ip_len = htons((u_int16_t)(len + mhlen)); - m0->m_next = m_copy(m1, off, len); - if (m0->m_next == 0) { - error = ENOBUFS;/* ??? */ - ipstat.ips_odropped++; - goto sendorfree; - } - m0->m_pkthdr.len = mhlen + len; - m0->m_pkthdr.rcvif = (struct ifnet *)0; - mhip->ip_off = htons((u_int16_t)mhip->ip_off); - if ((ifp->if_capabilities & IFCAP_CSUM_IPv4) && - ifp->if_bridge == NULL) { - m0->m_pkthdr.csum |= M_IPV4_CSUM_OUT; - ipstat.ips_outhwcsum++; - } else { - mhip->ip_sum = 0; - mhip->ip_sum = in_cksum(m0, mhlen); - } - ipstat.ips_ofragments++; - } - /* - * Update first fragment by trimming what's been copied out - * and updating header, then send each fragment (in order). - */ - m0 = m1; - m_adj(m0, hlen + firstlen - (u_int16_t)ip->ip_len); - m0->m_pkthdr.len = hlen + firstlen; - ip->ip_len = htons((u_int16_t)m0->m_pkthdr.len); - ip->ip_off = htons((u_int16_t)(ip->ip_off | IP_MF)); - if ((ifp->if_capabilities & IFCAP_CSUM_IPv4) && - ifp->if_bridge == NULL) { - m0->m_pkthdr.csum |= M_IPV4_CSUM_OUT; - ipstat.ips_outhwcsum++; - } else { - ip->ip_sum = 0; - ip->ip_sum = in_cksum(m0, hlen); - } -sendorfree: - for (m0 = m1; m0; m0 = m1) { - m1 = m0->m_nextpkt; - m0->m_nextpkt = 0; - if (error == 0) - error = (*ifp->if_output)(ifp, m0, sintosa(dst), - NULL); - else - m_freem(m0); - } + m1 = m0; + error = ip_fragment(m0, ifp); + if (error == EMSGSIZE) + goto bad; - if (error == 0) - ipstat.ips_fragmented++; - } + for (m0 = m1; m0; m0 = m1) { + m1 = m0->m_nextpkt; + m0->m_nextpkt = 0; + if (error == 0) + error = (*ifp->if_output)(ifp, m0, sintosa(dst), + NULL); + else + m_freem(m0); + } + + if (error == 0) + ipstat.ips_fragmented++; done: if (r->rt != PF_DUPTO)