Apply by doing: cd /usr/src patch -p0 < 011_ip6.patch And then rebuild your kernel. Index: sys/netinet6/ip6_output.c =================================================================== RCS file: /cvs/src/sys/netinet6/ip6_output.c,v retrieving revision 1.76 retrieving revision 1.76.2.1 diff -u -p -r1.76 -r1.76.2.1 --- sys/netinet6/ip6_output.c 15 Aug 2003 20:32:20 -0000 1.76 +++ sys/netinet6/ip6_output.c 7 Feb 2004 22:08:00 -0000 1.76.2.1 @@ -86,6 +86,7 @@ #include #include #include +#include #if NPF > 0 #include @@ -123,7 +124,7 @@ static int ip6_insertfraghdr(struct mbuf static int ip6_insert_jumboopt(struct ip6_exthdrs *, u_int32_t); static int ip6_splithdr(struct mbuf *, struct ip6_exthdrs *); static int ip6_getpmtu(struct route_in6 *, struct route_in6 *, - struct ifnet *, struct in6_addr *, u_long *); + struct ifnet *, struct in6_addr *, u_long *, int *); /* * IP6 output. The packet in mbuf chain m contains a skeletal IP6 @@ -154,6 +155,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifp int error = 0; struct in6_ifaddr *ia; u_long mtu; + int alwaysfrag, dontfrag; u_int32_t optlen = 0, plen = 0, unfragpartlen = 0; struct ip6_exthdrs exthdrs; struct in6_addr finaldst; @@ -699,7 +701,8 @@ ip6_output(m0, opt, ro, flags, im6o, ifp *ifpp = ifp; /* Determine path MTU. */ - if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu)) != 0) + if ((error = ip6_getpmtu(ro_pmtu, ro, ifp, &finaldst, &mtu, + &alwaysfrag)) != 0) goto bad; /* @@ -797,20 +800,61 @@ ip6_output(m0, opt, ro, flags, im6o, ifp /* * Send the packet to the outgoing interface. * If necessary, do IPv6 fragmentation before sending. + * + * the logic here is rather complex: + * 1: normal case (dontfrag == 0, alwaysfrag == 0) + * 1-a: send as is if tlen <= path mtu + * 1-b: fragment if tlen > path mtu + * + * 2: if user asks us not to fragment (dontfrag == 1) + * 2-a: send as is if tlen <= interface mtu + * 2-b: error if tlen > interface mtu + * + * 3: if we always need to attach fragment header (alwaysfrag == 1) + * always fragment + * + * 4: if dontfrag == 1 && alwaysfrag == 1 + * error, as we cannot handle this conflicting request */ tlen = m->m_pkthdr.len; - if (tlen <= mtu) { - error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); - goto done; - } else if (mtu < IPV6_MMTU) { + + dontfrag = 0; + if (dontfrag && alwaysfrag) { /* case 4 */ + /* conflicting request - can't transmit */ + error = EMSGSIZE; + goto bad; + } + if (dontfrag && tlen > IN6_LINKMTU(ifp)) { /* case 2-b */ /* - * note that path MTU is never less than IPV6_MMTU - * (see icmp6_input). + * Even if the DONTFRAG option is specified, we cannot send the + * packet when the data length is larger than the MTU of the + * outgoing interface. + * Notify the error by sending IPV6_PATHMTU ancillary data as + * well as returning an error code (the latter is not described + * in the API spec.) */ error = EMSGSIZE; + goto bad; + } + + /* + * transmit packet without fragmentation + */ + if (dontfrag || (!alwaysfrag && tlen <= mtu)) { /* case 1-a and 2-a */ + error = nd6_output(ifp, origifp, m, dst, ro->ro_rt); + goto done; + } + + /* + * try to fragment the packet. case 1-b and 3 + */ + if (mtu < IPV6_MMTU) { + /* path MTU cannot be less than IPV6_MMTU */ + error = EMSGSIZE; in6_ifstat_inc(ifp, ifs6_out_fragfail); goto bad; - } else if (ip6->ip6_plen == 0) { /* jumbo payload cannot be fragmented */ + } else if (ip6->ip6_plen == 0) { + /* jumbo payload cannot be fragmented */ error = EMSGSIZE; in6_ifstat_inc(ifp, ifs6_out_fragfail); goto bad; @@ -868,6 +912,7 @@ ip6_output(m0, opt, ro, flags, im6o, ifp ip6stat.ip6s_odropped++; goto sendorfree; } + m->m_pkthdr.rcvif = NULL; m->m_flags = m0->m_flags & M_COPYFLAGS; *mnext = m; mnext = &m->m_nextpkt; @@ -880,12 +925,12 @@ ip6_output(m0, opt, ro, flags, im6o, ifp ip6stat.ip6s_odropped++; goto sendorfree; } - ip6f->ip6f_offlg = htons((u_short)((off - hlen) & ~7)); + ip6f->ip6f_offlg = htons((u_int16_t)((off - hlen) & ~7)); if (off + len >= tlen) len = tlen - off; else ip6f->ip6f_offlg |= IP6F_MORE_FRAG; - mhip6->ip6_plen = htons((u_short)(len + hlen + + mhip6->ip6_plen = htons((u_int16_t)(len + hlen + sizeof(*ip6f) - sizeof(struct ip6_hdr))); if ((m_frgpart = m_copy(m0, off, len)) == 0) { error = ENOBUFS; @@ -1118,13 +1163,15 @@ ip6_insertfraghdr(m0, m, hlen, frghdrp) } static int -ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup) +ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup, alwaysfragp) struct route_in6 *ro_pmtu, *ro; struct ifnet *ifp; struct in6_addr *dst; u_long *mtup; + int *alwaysfragp; { u_int32_t mtu = 0; + int alwaysfrag = 0; int error = 0; if (ro_pmtu != ro) { @@ -1135,7 +1182,7 @@ ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup) ((ro_pmtu->ro_rt->rt_flags & RTF_UP) == 0 || !IN6_ARE_ADDR_EQUAL(&sa6_dst->sin6_addr, dst))) { RTFREE(ro_pmtu->ro_rt); - ro_pmtu->ro_rt = (struct rtentry *)0; + ro_pmtu->ro_rt = (struct rtentry *)NULL; } if (ro_pmtu->ro_rt == 0) { bzero(sa6_dst, sizeof(*sa6_dst)); @@ -1155,7 +1202,18 @@ ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup) mtu = ro_pmtu->ro_rt->rt_rmx.rmx_mtu; if (mtu == 0) mtu = ifmtu; - else if (mtu > ifmtu) { + else if (mtu < IPV6_MMTU) { + /* + * RFC2460 section 5, last paragraph: + * if we record ICMPv6 too big message with + * mtu < IPV6_MMTU, transmit packets sized IPV6_MMTU + * or smaller, with fragment header attached. + * (fragment header is needed regardless from the + * packet size, for translators to identify packets) + */ + alwaysfrag = 1; + mtu = IPV6_MMTU; + } else if (mtu > ifmtu) { /* * The MTU on the route is larger than the MTU on * the interface! This shouldn't happen, unless the @@ -1163,9 +1221,6 @@ ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup) * interface was brought up. Change the MTU in the * route to match the interface MTU (as long as the * field isn't locked). - * - * if MTU on the route is 0, we need to fix the MTU. - * this case happens with path MTU discovery timeouts. */ mtu = ifmtu; if (!(ro_pmtu->ro_rt->rt_rmx.rmx_locks & RTV_MTU)) @@ -1177,6 +1232,8 @@ ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup) error = EHOSTUNREACH; /* XXX */ *mtup = mtu; + if (alwaysfragp) + *alwaysfragp = alwaysfrag; return (error); }