/*
@@ -800,20 +803,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;
@@ -871,6 +915,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;
@@ -883,12 +928,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;
@@ -1121,13 +1166,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) {
@@ -1138,7 +1185,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));
@@ -1158,7 +1205,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
@@ -1166,9 +1224,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))
@@ -1180,6 +1235,8 @@ ip6_getpmtu(ro_pmtu, ro, ifp, dst, mtup)
error = EHOSTUNREACH; /* XXX */