diff options
| author | Hideaki Yoshifuji <yoshfuji@linux-ipv6.org> | 2005-03-04 08:38:31 +0900 |
|---|---|---|
| committer | Hideaki Yoshifuji <yoshfuji@linux-ipv6.org> | 2005-03-04 08:38:31 +0900 |
| commit | 8b123d00b713465bb5d262cdfa9a05d3e34f612d (patch) | |
| tree | 8c3336e93d87aa3df92e26e4e4f4a8a883fa46a2 | |
| parent | 3b4804268a4aa955614ad03770f6a68cc808403e (diff) | |
[IPV6] Always add a fragment header after receiving TOO BIG w/ pmtu < 1280.
According to RFC2460, PMTU is set to the IPv6 Minimum Link
MTU (1280) and a fragment header should always be included
after a node receiving Too Big message reporting PMTU is
less than the IPv6 Minimum Link MTU (1280).
Signed-off-by: Hideaki YOSHIFUJI <yoshfuji@linux-ipv6.org>
| -rw-r--r-- | include/linux/ip.h | 1 | ||||
| -rw-r--r-- | include/linux/rtnetlink.h | 1 | ||||
| -rw-r--r-- | include/net/dst.h | 9 | ||||
| -rw-r--r-- | net/ipv6/ip6_output.c | 10 | ||||
| -rw-r--r-- | net/ipv6/route.c | 34 |
5 files changed, 39 insertions, 16 deletions
diff --git a/include/linux/ip.h b/include/linux/ip.h index 487152a404f8..7d7633632d28 100644 --- a/include/linux/ip.h +++ b/include/linux/ip.h @@ -152,6 +152,7 @@ struct inet_sock { }; #define IPCORK_OPT 1 /* ip-options has been held in ipcork.opt */ +#define IPCORK_ALLFRAG 2 /* always fragment (for ipv6 for now) */ static inline struct inet_sock *inet_sk(const struct sock *sk) { diff --git a/include/linux/rtnetlink.h b/include/linux/rtnetlink.h index c3d5d043a5ca..5f6310f0d880 100644 --- a/include/linux/rtnetlink.h +++ b/include/linux/rtnetlink.h @@ -346,6 +346,7 @@ enum #define RTAX_FEATURE_ECN 0x00000001 #define RTAX_FEATURE_SACK 0x00000002 #define RTAX_FEATURE_TIMESTAMP 0x00000004 +#define RTAX_FEATURE_ALLFRAG 0x00000008 struct rta_session { diff --git a/include/net/dst.h b/include/net/dst.h index b7e47a7ad105..785c4a185ff5 100644 --- a/include/net/dst.h +++ b/include/net/dst.h @@ -124,6 +124,15 @@ dst_pmtu(const struct dst_entry *dst) return mtu; } +static inline u32 +dst_allfrag(const struct dst_entry *dst) +{ + int ret = dst_path_metric(dst, RTAX_FEATURES) & RTAX_FEATURE_ALLFRAG; + /* Yes, _exactly_. This is paranoia. */ + barrier(); + return ret; +} + static inline int dst_metric_locked(struct dst_entry *dst, int metric) { diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c index fce2a87e9e4a..d19cafe5d1e2 100644 --- a/net/ipv6/ip6_output.c +++ b/net/ipv6/ip6_output.c @@ -147,7 +147,7 @@ static int ip6_output2(struct sk_buff *skb) int ip6_output(struct sk_buff *skb) { - if (skb->len > dst_pmtu(skb->dst)) + if (skb->len > dst_pmtu(skb->dst) || dst_allfrag(skb->dst)) return ip6_fragment(skb, ip6_output2); else return ip6_output2(skb); @@ -848,6 +848,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offse inet->cork.fl = *fl; np->cork.hop_limit = hlimit; inet->cork.fragsize = mtu = dst_pmtu(&rt->u.dst); + if (dst_allfrag(&rt->u.dst)) + inet->cork.flags |= IPCORK_ALLFRAG; inet->cork.length = 0; sk->sk_sndmsg_page = NULL; sk->sk_sndmsg_off = 0; @@ -899,7 +901,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, int offse while (length > 0) { /* Check if the remaining data fits into current packet. */ - copy = mtu - skb->len; + copy = (inet->cork.length <= mtu && !(inet->cork.flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - skb->len; if (copy < length) copy = maxfraglen - skb->len; @@ -924,7 +926,7 @@ alloc_new_skb: * we know we need more fragment(s). */ datalen = length + fraggap; - if (datalen > mtu - fragheaderlen) + if (datalen > (inet->cork.length <= mtu && !(inet->cork.flags & IPCORK_ALLFRAG) ? mtu : maxfraglen) - fragheaderlen) datalen = maxfraglen - fragheaderlen; fraglen = datalen + fragheaderlen; @@ -1158,6 +1160,7 @@ out: if (np->cork.rt) { dst_release(&np->cork.rt->u.dst); np->cork.rt = NULL; + inet->cork.flags &= ~IPCORK_ALLFRAG; } memset(&inet->cork.fl, 0, sizeof(inet->cork.fl)); return err; @@ -1185,6 +1188,7 @@ void ip6_flush_pending_frames(struct sock *sk) if (np->cork.rt) { dst_release(&np->cork.rt->u.dst); np->cork.rt = NULL; + inet->cork.flags &= ~IPCORK_ALLFRAG; } memset(&inet->cork.fl, 0, sizeof(inet->cork.fl)); } diff --git a/net/ipv6/route.c b/net/ipv6/route.c index c696043a8e47..e5bc02ccc0ea 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c @@ -628,8 +628,10 @@ static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu) if (mtu < dst_pmtu(dst) && rt6->rt6i_dst.plen == 128) { rt6->rt6i_flags |= RTF_MODIFIED; - if (mtu < IPV6_MIN_MTU) + if (mtu < IPV6_MIN_MTU) { mtu = IPV6_MIN_MTU; + dst->metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG; + } dst->metrics[RTAX_MTU-1] = mtu; } } @@ -1164,26 +1166,26 @@ void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr, struct net_device *dev, u32 pmtu) { struct rt6_info *rt, *nrt; - - if (pmtu < IPV6_MIN_MTU) { - if (net_ratelimit()) - printk(KERN_DEBUG "rt6_pmtu_discovery: invalid MTU value %d\n", - pmtu); - /* According to RFC1981, the PMTU is set to the IPv6 minimum - link MTU if the node receives a Packet Too Big message - reporting next-hop MTU that is less than the IPv6 minimum MTU. - */ - pmtu = IPV6_MIN_MTU; - } + int allfrag = 0; rt = rt6_lookup(daddr, saddr, dev->ifindex, 0); - if (rt == NULL) return; if (pmtu >= dst_pmtu(&rt->u.dst)) goto out; + if (pmtu < IPV6_MIN_MTU) { + /* + * According to RFC2460, PMTU is set to the IPv6 Minimum Link + * MTU (1280) and a fragment header should always be included + * after a node receiving Too Big message reporting PMTU is + * less than the IPv6 Minimum Link MTU. + */ + pmtu = IPV6_MIN_MTU; + allfrag = 1; + } + /* New mtu received -> path was valid. They are sent only in response to data packets, so that this nexthop apparently is reachable. --ANK @@ -1197,6 +1199,8 @@ void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr, */ if (rt->rt6i_flags & RTF_CACHE) { rt->u.dst.metrics[RTAX_MTU-1] = pmtu; + if (allfrag) + rt->u.dst.metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG; dst_set_expires(&rt->u.dst, ip6_rt_mtu_expires); rt->rt6i_flags |= RTF_MODIFIED|RTF_EXPIRES; goto out; @@ -1211,6 +1215,8 @@ void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr, nrt = rt6_cow(rt, daddr, saddr); if (!nrt->u.dst.error) { nrt->u.dst.metrics[RTAX_MTU-1] = pmtu; + if (allfrag) + nrt->u.dst.metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG; /* According to RFC 1981, detecting PMTU increase shouldn't be happened within 5 mins, the recommended timer is 10 mins. Here this route expiration time is set to ip6_rt_mtu_expires @@ -1232,6 +1238,8 @@ void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr, dst_set_expires(&nrt->u.dst, ip6_rt_mtu_expires); nrt->rt6i_flags |= RTF_DYNAMIC|RTF_CACHE|RTF_EXPIRES; nrt->u.dst.metrics[RTAX_MTU-1] = pmtu; + if (allfrag) + nrt->u.dst.metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG; ip6_ins_rt(nrt, NULL, NULL); } |
