Do pmtu check in transport layer
Subsystems (3)
PATCH 1/3 Do pmtu check in transport layer
Check the pmtu check at the transport layer (for UDP, ICMP and raw), and send a local error if socket is PMTUDISC_DO and packet is too big. This is actually a pure bugfix for ipv6. For ipv4, it allows us to do pmtu checks in the same way as for ipv6.
--- net/ipv4/ip_output.c | 4 +++- net/ipv4/raw.c | 8 +++++--- net/ipv6/ip6_output.c | 11 ++++++----- net/ipv6/raw.c | 7 +++++-- 4 files changed, 19 insertions(+), 11 deletions(-)
net/ipv4/ip_output.c +3 -1
@@ -822,7 +822,9 @@ int ip_append_data(struct sock *sk, fragheaderlen =sizeof (struct iphdr ) + (opt ? opt->optlen :0 ); maxfraglen = ((mtu - fragheaderlen) & ~7 ) + fragheaderlen; -if (inet->cork.length + length >0xFFFF - fragheaderlen) { +if (inet->cork.length + length >0xFFFF - fragheaderlen || + (inet->pmtudisc >=IP_PMTUDISC_DO && + inet->cork.length + length > mtu)) {ip_local_error (sk,EMSGSIZE , rt->rt_dst, inet->dport, mtu-exthdrlen);return -EMSGSIZE ; }
net/ipv4/raw.c +5 -3
@@ -271,10 +271,12 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,struct iphdr *iph;struct sk_buff *skb;int err; +int mtu; -if (length > rt->u.dst.dev->mtu) { -ip_local_error (sk,EMSGSIZE , rt->rt_dst, inet->dport, - rt->u.dst.dev->mtu); + mtu = inet->pmtudisc ==IP_PMTUDISC_DO ?dst_mtu (&rt->u.dst) : + rt->u.dst.dev->mtu; +if (length > mtu) { +ip_local_error (sk,EMSGSIZE , rt->rt_dst, inet->dport, mtu);return -EMSGSIZE ; }if (flags&MSG_PROBE )
net/ipv6/ip6_output.c +6 -5
@@ -1044,11 +1044,12 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, fragheaderlen =sizeof (struct ipv6hdr ) + rt->u.dst.nfheader_len + (opt ? opt->opt_nflen :0 ); maxfraglen = ((mtu - fragheaderlen) & ~7 ) + fragheaderlen -sizeof (struct frag_hdr ); -if (mtu <=sizeof (struct ipv6hdr ) +IPV6_MAXPLEN ) { -if (inet->cork.length + length >sizeof (struct ipv6hdr ) +IPV6_MAXPLEN - fragheaderlen) { -ipv6_local_error (sk,EMSGSIZE , fl, mtu-exthdrlen); -return -EMSGSIZE ; - } +if ((mtu <=sizeof (struct ipv6hdr ) +IPV6_MAXPLEN && + inet->cork.length + length >sizeof (struct ipv6hdr ) +IPV6_MAXPLEN - fragheaderlen) || + (np->pmtudisc >=IPV6_PMTUDISC_DO && + inet->cork.length + length > mtu)) { +ipv6_local_error (sk,EMSGSIZE , fl, mtu-exthdrlen); +return -EMSGSIZE ; }/*
net/ipv6/raw.c +5 -2
@@ -556,9 +556,12 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,struct sk_buff *skb;unsigned int hh_len;int err; +int mtu; -if (length > rt->u.dst.dev->mtu) { -ipv6_local_error (sk,EMSGSIZE , fl, rt->u.dst.dev->mtu); + mtu = np->pmtudisc ==IPV6_PMTUDISC_DO ?dst_mtu (&rt->u.dst) : + rt->u.dst.dev->mtu; +if (length > mtu) { +ipv6_local_error (sk,EMSGSIZE , fl, mtu);return -EMSGSIZE ; }if (flags&MSG_PROBE )
Signature · 1 line
1.5.0.2.gc260-dirty
PATCH 2/3 Move DF check to ip_forward
Do fragmentation check in ip_forward, similar to ipv6 forwarding. Also add a debug printk in the DF check in ip_fragment since we should now never reach it.
--- net/ipv4/ip_forward.c | 8 ++++++++ net/ipv4/ip_output.c | 2 ++ 2 files changed, 10 insertions(+), 0 deletions(-)
net/ipv4/ip_forward.c +8 -0
@@ -85,6 +85,14 @@ int ip_forward(struct sk_buff *skb)if (opt->is_strictroute && rt->rt_dst != rt->rt_gateway) goto sr_failed; +if (unlikely (skb->len >dst_mtu (&rt->u.dst) && + (skb->nh.iph->frag_off &htons (IP_DF ))) && !skb->local_df) { +IP_INC_STATS (IPSTATS_MIB_FRAGFAILS ); +icmp_send (skb,ICMP_DEST_UNREACH ,ICMP_FRAG_NEEDED , +htonl (dst_mtu (&rt->u.dst))); + goto drop; + } +/* We are about to mangle packet. Copy it! */ if (skb_cow (skb,LL_RESERVED_SPACE (rt->u.dst.dev)+rt->u.dst.header_len)) goto drop;
net/ipv4/ip_output.c +2 -0
@@ -433,6 +433,8 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*)) iph = skb->nh.iph;if (unlikely ((iph->frag_off &htons (IP_DF )) && !skb->local_df)) { +if (net_ratelimit ()) +printk (KERN_DEBUG "ip_fragment: requested fragment of packet with DF set\n" );IP_INC_STATS (IPSTATS_MIB_FRAGFAILS );icmp_send (skb,ICMP_DEST_UNREACH ,ICMP_FRAG_NEEDED ,htonl (dst_mtu (&rt->u.dst)));
Signature · 1 line
1.5.0.2.gc260-dirty
PATCH 3/3 Add IP(V6)_PMTUDISC_RPOBE
Add IP(V6)_PMTUDISC_PROBE value for IP(V6)_MTU_DISCOVER. This option forces us not to fragment, but does not make use of the kernel path MTU discovery. That is, it allows for user-mode MTU probing (or, packetization-layer path MTU discovery). This is particularly useful for diagnostic utilities, like traceroute/tracepath.
--- include/linux/in.h | 1 + include/linux/in6.h | 1 + include/linux/skbuff.h | 3 ++- include/net/ip.h | 2 +- net/core/skbuff.c | 2 ++ net/ipv4/ip_output.c | 14 ++++++++++---- net/ipv4/ip_sockglue.c | 2 +- net/ipv4/raw.c | 3 +++ net/ipv6/ip6_output.c | 12 ++++++++---- net/ipv6/ipv6_sockglue.c | 2 +- net/ipv6/raw.c | 3 +++ 11 files changed, 33 insertions(+), 12 deletions(-)
include/linux/in.h +1 -0
@@ -83,6 +83,7 @@ struct in_addr {#define IP_PMTUDISC_DONT 0/* Never send DF frames */ #define IP_PMTUDISC_WANT 1/* Use per route hints */ #define IP_PMTUDISC_DO 2/* Always DF */ +#define IP_PMTUDISC_PROBE 3/* Ignore dst pmtu */ #define IP_MULTICAST_IF 32#define IP_MULTICAST_TTL 33
include/linux/in6.h +1 -0
@@ -179,6 +179,7 @@ struct in6_flowlabel_req#define IPV6_PMTUDISC_DONT 0#define IPV6_PMTUDISC_WANT 1#define IPV6_PMTUDISC_DO 2 +#define IPV6_PMTUDISC_PROBE 3/* Flowlabel */ #define IPV6_FLOWLABEL_MGR 32
include/linux/skbuff.h +2 -1
@@ -284,7 +284,8 @@ struct sk_buff { nfctinfo:3 ; __u8 pkt_type:3, fclone:2, - ipvs_property:1; + ipvs_property:1, + ign_dst_mtu;__be16 protocol;void (*destructor)(struct sk_buff *skb);
include/net/ip.h +1 -1
@@ -201,7 +201,7 @@ int ip_decrease_ttl(struct iphdr *iph)static inline int ip_dont_fragment (struct sock *sk,struct dst_entry *dst) { -return (inet_sk (sk)->pmtudisc ==IP_PMTUDISC_DO || +return (inet_sk (sk)->pmtudisc >=IP_PMTUDISC_DO || (inet_sk (sk)->pmtudisc ==IP_PMTUDISC_WANT && !(dst_metric (dst,RTAX_LOCK )&(1 <<RTAX_MTU )))); }
net/core/skbuff.c +2 -0
@@ -474,6 +474,7 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)#if defined(CONFIG_IP_VS ) || defined(CONFIG_IP_VS_MODULE )C (ipvs_property);#endif +C (ign_dst_mtu);C (protocol); n->destructor =NULL ;C (mark);
@@ -549,6 +550,7 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)#if defined(CONFIG_IP_VS ) || defined(CONFIG_IP_VS_MODULE ) new->ipvs_property = old->ipvs_property;#endif + new->ign_dst_mtu = old->ign_dst_mtu;#ifdef CONFIG_BRIDGE_NETFILTER new->nf_bridge = old->nf_bridge;nf_bridge_get (old->nf_bridge);
net/ipv4/ip_output.c +10 -4
@@ -201,7 +201,8 @@ static inline int ip_finish_output(struct sk_buff *skb) return dst_output(skb); }#endif -if (skb->len >dst_mtu (skb->dst) && !skb_is_gso (skb)) +if (skb->len >dst_mtu (skb->dst) && + !skb->ign_dst_mtu && !skb_is_gso (skb))return ip_fragment (skb, ip_finish_output2);else return ip_finish_output2 (skb);
@@ -801,7 +802,9 @@ int ip_append_data(struct sock *sk, inet->cork.addr = ipc->addr; }dst_hold (&rt->u.dst); - inet->cork.fragsize = mtu =dst_mtu (rt->u.dst.path); + inet->cork.fragsize = mtu = inet->pmtudisc ==IP_PMTUDISC_PROBE ? + rt->u.dst.dev->mtu : +dst_mtu (rt->u.dst.path); inet->cork.rt = rt; inet->cork.length =0 ; sk->sk_sndmsg_page =NULL ;
@@ -1220,13 +1223,16 @@ int ip_push_pending_frames(struct sock *sk)* to fragment the frame generated here. No matter, what transforms * how transforms change size of the packet, it will come out. */ -if (inet->pmtudisc !=IP_PMTUDISC_DO ) +if (inet->pmtudisc <IP_PMTUDISC_DO ) skb->local_df =1 ; +if (inet->pmtudisc ==IP_PMTUDISC_PROBE ) + skb->ign_dst_mtu =1 ; +/* DF bit is set when we want to see DF on outgoing frames. * If local_df is set too, we still allow to fragment this frame * locally. */ -if (inet->pmtudisc ==IP_PMTUDISC_DO || +if (inet->pmtudisc >=IP_PMTUDISC_DO || (skb->len <=dst_mtu (&rt->u.dst) &&ip_dont_fragment (sk, &rt->u.dst))) df =htons (IP_DF );
net/ipv4/ip_sockglue.c +1 -1
@@ -536,7 +536,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, inet->hdrincl = val ?1 :0 ;break ;case IP_MTU_DISCOVER : -if (val<0 || val>2 ) +if (val<0 || val>3 ) goto e_inval; inet->pmtudisc = val;break ;
net/ipv4/raw.c +3 -0
@@ -302,6 +302,9 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,if (err) goto error_fault; +if (inet->pmtudisc ==IP_PMTUDISC_PROBE ) + skb->ign_dst_mtu =1 ; +/* We don't modify invalid header */ if (length >=sizeof (*iph) && iph->ihl *4U <= length) {if (!iph->saddr)
net/ipv6/ip6_output.c +8 -4
@@ -139,8 +139,8 @@ static int ip6_output2(struct sk_buff *skb)int ip6_output (struct sk_buff *skb) { -if ((skb->len >dst_mtu (skb->dst) && !skb_is_gso (skb)) || -dst_allfrag (skb->dst)) +if ((skb->len >dst_mtu (skb->dst) && !skb->ign_dst_mtu && + !skb_is_gso (skb)) ||dst_allfrag (skb->dst))return ip6_fragment (skb, ip6_output2);else return ip6_output2 (skb);
@@ -574,7 +574,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) hlen =ip6_find_1stfragopt (skb, &prevhdr); nexthdr = *prevhdr; - mtu =dst_mtu (&rt->u.dst); + mtu = skb->ign_dst_mtu ? skb->len :dst_mtu (&rt->u.dst);if (np && np->frag_size < mtu) {if (np->frag_size) mtu = np->frag_size;
@@ -1015,7 +1015,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, inet->cork.fl = *fl; np->cork.hop_limit = hlimit; np->cork.tclass = tclass; - mtu =dst_mtu (rt->u.dst.path); + mtu = np->pmtudisc ==IPV6_PMTUDISC_PROBE ? + rt->u.dst.dev->mtu :dst_mtu (rt->u.dst.path);if (np->frag_size < mtu) {if (np->frag_size) mtu = np->frag_size;
@@ -1303,6 +1304,9 @@ int ip6_push_pending_frames(struct sock *sk) tmp_skb->sk =NULL ; } +if (np->pmtudisc ==IPV6_PMTUDISC_PROBE ) + skb->ign_dst_mtu =1 ; +ipv6_addr_copy (final_dst, &fl->fl6_dst);__skb_pull (skb, skb->h.raw - skb->nh.raw);if (opt && opt->opt_flen)
net/ipv6/ipv6_sockglue.c +1 -1
@@ -694,7 +694,7 @@ done: retv =ip6_ra_control (sk, val,NULL );break ;case IPV6_MTU_DISCOVER : -if (val<0 || val>2 ) +if (val<0 || val>3 ) goto e_inval; np->pmtudisc = val; retv =0 ;
net/ipv6/raw.c +3 -0
@@ -587,6 +587,9 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,if (err) goto error_fault; +if (np->pmtudisc ==IPV6_PMTUDISC_PROBE ) + skb->ign_dst_mtu =1 ; +IP6_INC_STATS (rt->rt6i_idev,IPSTATS_MIB_OUTREQUESTS ); err =NF_HOOK (PF_INET6 ,NF_IP6_LOCAL_OUT , skb,NULL , rt->u.dst.dev, dst_output);
Signature · 1 line
1.5.0.2.gc260-dirty
David Miller
maintainerRe: [PATCH 3/3] [NET] Add IP(V6)_PMTUDISC_RPOBE
From: John Heffner <[email protected]>
Date: Fri, 23 Mar 2007 20:06:46 -0400
Quoted 7 lines
Add IP(V6)_PMTUDISC_PROBE value for IP(V6)_MTU_DISCOVER. This option forces us not to fragment, but does not make use of the kernel path MTU discovery. That is, it allows for user-mode MTU probing (or, packetization-layer path MTU discovery). This is particularly useful for diagnostic utilities, like traceroute/tracepath.
Signed-off-by John Heffner <[email protected]>
Also applied to net-2.6.22, thanks John.
I made a slight change:
Quoted 5 lines
include/linux/skbuff.h +0
-0
include/linux/skbuff.h struct sk_buff { · line 291
Quoted 9 lines
include/linux/skbuff.h +2
-1@@ -284,7 +284,8 @@ struct sk_buff { nfctinfo:3 ; __u8 pkt_type:3, fclone:2, - ipvs_property:1; + ipvs_property:1, + ign_dst_mtu;__be16 protocol;void (*destructor)(struct sk_buff *skb);
I marked "ign_dst_mtu" with a bit field size of one since this appears to be a boolean I take it this is what you mean to do here. Otherwise it adds another __u8 to struct sk_buff. :-)
Re: [PATCH 3/3] [NET] Add IP(V6)_PMTUDISC_RPOBE
In reply to John HeffnerQuoted 5 lines · John Heffner
Add IP(V6)_PMTUDISC_PROBE value for IP(V6)_MTU_DISCOVER. This option forces us not to fragment, but does not make use of the kernel path MTU discovery. That is, it allows for user-mode MTU probing (or, packetization-layer path MTU discovery). This is particularly useful for diagnostic utilities, like traceroute/tracepath.
You should probably send a manpages update to the manpages maintainer too (cc'ed with fullquote)
-Andi
Quoted 236 lines
Signed-off-by John Heffner <[email protected]>--- include/linux/in.h | 1 + include/linux/in6.h | 1 + include/linux/skbuff.h | 3 ++- include/net/ip.h | 2 +- net/core/skbuff.c | 2 ++ net/ipv4/ip_output.c | 14 ++++++++++---- net/ipv4/ip_sockglue.c | 2 +- net/ipv4/raw.c | 3 +++ net/ipv6/ip6_output.c | 12 ++++++++---- net/ipv6/ipv6_sockglue.c | 2 +- net/ipv6/raw.c | 3 +++ 11 files changed, 33 insertions(+), 12 deletions(-)include/linux/in.h +1
-0@@ -83,6 +83,7 @@ struct in_addr {#define IP_PMTUDISC_DONT 0/* Never send DF frames */ #define IP_PMTUDISC_WANT 1/* Use per route hints */ #define IP_PMTUDISC_DO 2/* Always DF */ +#define IP_PMTUDISC_PROBE 3/* Ignore dst pmtu */ #define IP_MULTICAST_IF 32#define IP_MULTICAST_TTL 33include/linux/in6.h +1
-0@@ -179,6 +179,7 @@ struct in6_flowlabel_req#define IPV6_PMTUDISC_DONT 0#define IPV6_PMTUDISC_WANT 1#define IPV6_PMTUDISC_DO 2 +#define IPV6_PMTUDISC_PROBE 3/* Flowlabel */ #define IPV6_FLOWLABEL_MGR 32include/linux/skbuff.h +2
-1@@ -284,7 +284,8 @@ struct sk_buff { nfctinfo:3 ; __u8 pkt_type:3, fclone:2, - ipvs_property:1; + ipvs_property:1, + ign_dst_mtu;__be16 protocol;void (*destructor)(struct sk_buff *skb);include/net/ip.h +1
-1@@ -201,7 +201,7 @@ int ip_decrease_ttl(struct iphdr *iph)static inline int ip_dont_fragment (struct sock *sk,struct dst_entry *dst) { -return (inet_sk (sk)->pmtudisc ==IP_PMTUDISC_DO || +return (inet_sk (sk)->pmtudisc >=IP_PMTUDISC_DO || (inet_sk (sk)->pmtudisc ==IP_PMTUDISC_WANT && !(dst_metric (dst,RTAX_LOCK )&(1 <<RTAX_MTU )))); }net/core/skbuff.c +2
-0@@ -474,6 +474,7 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)#if defined(CONFIG_IP_VS ) || defined(CONFIG_IP_VS_MODULE )C (ipvs_property);#endif +C (ign_dst_mtu);C (protocol); n->destructor =NULL ;C (mark);@@ -549,6 +550,7 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)#if defined(CONFIG_IP_VS ) || defined(CONFIG_IP_VS_MODULE ) new->ipvs_property = old->ipvs_property;#endif + new->ign_dst_mtu = old->ign_dst_mtu;#ifdef CONFIG_BRIDGE_NETFILTER new->nf_bridge = old->nf_bridge;nf_bridge_get (old->nf_bridge);net/ipv4/ip_output.c +10
-4@@ -201,7 +201,8 @@ static inline int ip_finish_output(struct sk_buff *skb) return dst_output(skb); }#endif -if (skb->len >dst_mtu (skb->dst) && !skb_is_gso (skb)) +if (skb->len >dst_mtu (skb->dst) && + !skb->ign_dst_mtu && !skb_is_gso (skb))return ip_fragment (skb, ip_finish_output2);else return ip_finish_output2 (skb);@@ -801,7 +802,9 @@ int ip_append_data(struct sock *sk, inet->cork.addr = ipc->addr; }dst_hold (&rt->u.dst); - inet->cork.fragsize = mtu =dst_mtu (rt->u.dst.path); + inet->cork.fragsize = mtu = inet->pmtudisc ==IP_PMTUDISC_PROBE ? + rt->u.dst.dev->mtu : +dst_mtu (rt->u.dst.path); inet->cork.rt = rt; inet->cork.length =0 ; sk->sk_sndmsg_page =NULL ;@@ -1220,13 +1223,16 @@ int ip_push_pending_frames(struct sock *sk)* to fragment the frame generated here. No matter, what transforms * how transforms change size of the packet, it will come out. */ -if (inet->pmtudisc !=IP_PMTUDISC_DO ) +if (inet->pmtudisc <IP_PMTUDISC_DO ) skb->local_df =1 ; +if (inet->pmtudisc ==IP_PMTUDISC_PROBE ) + skb->ign_dst_mtu =1 ; +/* DF bit is set when we want to see DF on outgoing frames. * If local_df is set too, we still allow to fragment this frame * locally. */ -if (inet->pmtudisc ==IP_PMTUDISC_DO || +if (inet->pmtudisc >=IP_PMTUDISC_DO || (skb->len <=dst_mtu (&rt->u.dst) &&ip_dont_fragment (sk, &rt->u.dst))) df =htons (IP_DF );net/ipv4/ip_sockglue.c +1
-1@@ -536,7 +536,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, inet->hdrincl = val ?1 :0 ;break ;case IP_MTU_DISCOVER : -if (val<0 || val>2 ) +if (val<0 || val>3 ) goto e_inval; inet->pmtudisc = val;break ;net/ipv4/raw.c +3
-0@@ -302,6 +302,9 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,if (err) goto error_fault; +if (inet->pmtudisc ==IP_PMTUDISC_PROBE ) + skb->ign_dst_mtu =1 ; +/* We don't modify invalid header */ if (length >=sizeof (*iph) && iph->ihl *4U <= length) {if (!iph->saddr)net/ipv6/ip6_output.c +8
-4@@ -139,8 +139,8 @@ static int ip6_output2(struct sk_buff *skb)int ip6_output (struct sk_buff *skb) { -if ((skb->len >dst_mtu (skb->dst) && !skb_is_gso (skb)) || -dst_allfrag (skb->dst)) +if ((skb->len >dst_mtu (skb->dst) && !skb->ign_dst_mtu && + !skb_is_gso (skb)) ||dst_allfrag (skb->dst))return ip6_fragment (skb, ip6_output2);else return ip6_output2 (skb);@@ -574,7 +574,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) hlen =ip6_find_1stfragopt (skb, &prevhdr); nexthdr = *prevhdr; - mtu =dst_mtu (&rt->u.dst); + mtu = skb->ign_dst_mtu ? skb->len :dst_mtu (&rt->u.dst);if (np && np->frag_size < mtu) {if (np->frag_size) mtu = np->frag_size;@@ -1015,7 +1015,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, inet->cork.fl = *fl; np->cork.hop_limit = hlimit; np->cork.tclass = tclass; - mtu =dst_mtu (rt->u.dst.path); + mtu = np->pmtudisc ==IPV6_PMTUDISC_PROBE ? + rt->u.dst.dev->mtu :dst_mtu (rt->u.dst.path);if (np->frag_size < mtu) {if (np->frag_size) mtu = np->frag_size;@@ -1303,6 +1304,9 @@ int ip6_push_pending_frames(struct sock *sk) tmp_skb->sk =NULL ; } +if (np->pmtudisc ==IPV6_PMTUDISC_PROBE ) + skb->ign_dst_mtu =1 ; +ipv6_addr_copy (final_dst, &fl->fl6_dst);__skb_pull (skb, skb->h.raw - skb->nh.raw);if (opt && opt->opt_flen)net/ipv6/ipv6_sockglue.c +1
-1@@ -694,7 +694,7 @@ done: retv =ip6_ra_control (sk, val,NULL );break ;case IPV6_MTU_DISCOVER : -if (val<0 || val>2 ) +if (val<0 || val>3 ) goto e_inval; np->pmtudisc = val; retv =0 ;net/ipv6/raw.c +3
-0@@ -587,6 +587,9 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,if (err) goto error_fault; +if (np->pmtudisc ==IPV6_PMTUDISC_PROBE ) + skb->ign_dst_mtu =1 ; +IP6_INC_STATS (rt->rt6i_idev,IPSTATS_MIB_OUTREQUESTS ); err =NF_HOOK (PF_INET6 ,NF_IP6_LOCAL_OUT , skb,NULL , rt->u.dst.dev, dst_output);Signature · 6 lines
1.5.0.2.gc260-dirty - To unsubscribe from this list: send the line "unsubscribe netdev" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
A message that is not in the archive (sent off-list or not yet received): [email protected]
A message that is not in the archive (sent off-list or not yet received): [email protected]
PATCH 1/1 ip(7) IP_PMTUDISC_PROBE
Document new IP_PMTUDISC_PROBE value for IP_MTU_DISCOVERY. (Going into 2.6.22).
Thanks,
-John
man7/ip.7 +10 -0
@@ -515,6 +515,7 @@ IP_PMTUDISC_WANT:Use per-route settings. IP_PMTUDISC_DONT:Never do Path MTU Discovery. IP_PMTUDISC_DO:Always do Path MTU Discovery. +IP_PMTUDISC_PROBE:Set DF but ignore Path MTU. .TE When PMTU discovery is enabled the kernel automatically keeps track of
@@ -550,6 +551,15 @@ with the .B IP_MTU option. + +It is possible to implement RFC 4821 MTU probing with +.B SOCK_DGRAM +of +.B SOCK_RAW +sockets by setting a value of IP_PMTUDISC_PROBE. This is also particularly +useful for diagnostic tools such as +.BR tracepath (8) +that wish to deliberately send probe packets larger than the observed Path MTU. .TP .B IP_MTU Retrieve the current known path MTU of the current socket.
Re: [PATCH] ip(7) IP_PMTUDISC_PROBE
Quoted 2 lines
Quoted 2 lines
Document new
IP_PMTUDISC_PROBEvalue forIP_MTU_DISCOVERY. (Going into 2.6.22).
Hi John,
Thanks -- accepted -- fix will appear in man-pages-2.47.
Andi: thanks for pointing John in the right direction.
Cheers,
Michael
Quoted 5 lines
Quoted 5 lines
------------------------------------------------------------------------
diff -rU3 man-pages-2.43-a/man7/ip.7 man-pages-2.43-b/man7/ip.7
man7/ip.7 +0
-0
Signature · 5 lines
Michael Kerrisk maintainer of Linux man pages Sections 2, 3, 4, 5, and 7 Want to help with man page maintenance? Grab the latest tarball at http://www.kernel.org/pub/linux/docs/manpages/ read the HOWTOHELP file and grep the source files for 'FIXME'.
David Miller
maintainerRe: [PATCH 2/3] [NET] Move DF check to ip_forward
In reply to John HeffnerFrom: John Heffner <[email protected]>
Date: Fri, 23 Mar 2007 20:06:45 -0400
Quoted 5 lines
Do fragmentation check in
ip_forward, similar to ipv6 forwarding. Also add a debug printk in the DF check inip_fragmentsince we should now never reach it.Signed-off-by John Heffner <[email protected]>
I don't think this debugging log message makes sense considering netfilter can potentially do interesting things to the packet.
Therefore I applied your patch without the log message, we can discuss it's merits seperately as I like the rest of this patch.
Thanks.
[NET]: Fix breakage, use ip_hdr() for DF check in ip_forward
In reply to John HeffnerThe patch [NET] Move DF check to ip_forward broke net-2.6.22 as skb->nh has been renamed to skb->network_header. Use ip_hdr().
net/ipv4/ip_forward.c +1 -1
@@ -86,7 +86,7 @@ int ip_forward(struct sk_buff *skb) goto sr_failed;if (unlikely (skb->len >dst_mtu (&rt->u.dst) && - (skb->nh.iph->frag_off &htons (IP_DF ))) && !skb->local_df) { + (ip_hdr (skb)->frag_off &htons (IP_DF ))) && !skb->local_df) {IP_INC_STATS (IPSTATS_MIB_FRAGFAILS );icmp_send (skb,ICMP_DEST_UNREACH ,ICMP_FRAG_NEEDED ,htonl (dst_mtu (&rt->u.dst)));
David Miller
maintainerRe: [NET]: Fix breakage, use ip_hdr() for DF check in ip_forward
From: Thomas Graf <[email protected]>
Date: Sun, 25 Mar 2007 15:37:31 +0200
Quoted 4 lines
The patch [NET] Move DF check to
ip_forwardbroke net-2.6.22 asskb->nhhas been renamed toskb->network_header. Useip_hdr().Signed-off-by Thomas Graf <[email protected]>
Thanks Thomas.
John I'm extremely irritated. You send me which is obviouly a feature patch, which I'm obviously NOT going to apply to the current upstream for 2.6.21, which is only accepting bug fixes, yet you do not test compile your work and build your patches against my net-2.6.22 tree which is where such feature patches are going to get applied?
That's just irresponsible, please don't do that again or you'll be on my shit list for a good long while.
From: John Heffner <[email protected]>
Date: Fri, 23 Mar 2007 20:06:44 -0400
Quoted 6 lines
Check the pmtu check at the transport layer (for UDP, ICMP and raw), and send a local error if socket is
PMTUDISC_DOand packet is too big. This is actually a pure bugfix for ipv6. For ipv4, it allows us to do pmtu checks in the same way as for ipv6.Signed-off-by John Heffner <[email protected]>
Applied, thanks John.
Quoted 10 lines · John Heffner
Check the pmtu check at the transport layer (for UDP, ICMP and raw), and send a local error if socket is
PMTUDISC_DOand packet is too big. This is actually a pure bugfix for ipv6. For ipv4, it allows us to do pmtu checks in the same way as for ipv6.net/ipv4/ip_output.c +0
-0
net/ipv4/ip_output.c int ip_append_data(struct sock *sk, · line 830
Quoted 10 lines
net/ipv4/ip_output.c +3
-1@@ -822,7 +822,9 @@ int ip_append_data(struct sock *sk, fragheaderlen =sizeof (struct iphdr ) + (opt ? opt->optlen :0 ); maxfraglen = ((mtu - fragheaderlen) & ~7 ) + fragheaderlen; -if (inet->cork.length + length >0xFFFF - fragheaderlen) { +if (inet->cork.length + length >0xFFFF - fragheaderlen || + (inet->pmtudisc >=IP_PMTUDISC_DO && + inet->cork.length + length > mtu)) {ip_local_error (sk,EMSGSIZE , rt->rt_dst, inet->dport, mtu-exthdrlen);return -EMSGSIZE ; }
This makes ping report an incorrect MTU when IPsec is used since we're only accounting for the additional header_len, not the trailer_len (which is not easily changeable). Additionally it will report different MTUs for the first and following fragments when the socket is corked because only the first fragment includes the header_len. It also can't deal with things like NAT and routing by fwmark that change the route. The old behaviour was that we get an ICMP frag. required with the MTU of the final route, while this will always report the MTU of the initially chosen route.
For all these reasons I think it should be reverted to the old behaviour.
Quoted 33 lines · Patrick McHardy
Quoted 10 lines · John Heffner
Check the pmtu check at the transport layer (for UDP, ICMP and raw), and send a local error if socket is
PMTUDISC_DOand packet is too big. This is actually a pure bugfix for ipv6. For ipv4, it allows us to do pmtu checks in the same way as for ipv6.net/ipv4/ip_output.c +0
-0net/ipv4/ip_output.c int ip_append_data(struct sock *sk, · line 830
Quoted 10 lines
net/ipv4/ip_output.c +3
-1@@ -822,7 +822,9 @@ int ip_append_data(struct sock *sk, fragheaderlen =sizeof (struct iphdr ) + (opt ? opt->optlen :0 ); maxfraglen = ((mtu - fragheaderlen) & ~7 ) + fragheaderlen; -if (inet->cork.length + length >0xFFFF - fragheaderlen) { +if (inet->cork.length + length >0xFFFF - fragheaderlen || + (inet->pmtudisc >=IP_PMTUDISC_DO && + inet->cork.length + length > mtu)) {ip_local_error (sk,EMSGSIZE , rt->rt_dst, inet->dport, mtu-exthdrlen);return -EMSGSIZE ; }This makes ping report an incorrect MTU when IPsec is used since we're only accounting for the additional
header_len, not thetrailer_len(which is not easily changeable). Additionally it will report different MTUs for the first and following fragments when the socket is corked because only the first fragment includes theheader_len. It also can't deal with things like NAT and routing by fwmark that change the route. The old behaviour was that we get an ICMP frag. required with the MTU of the final route, while this will always report the MTU of the initially chosen route.
Quoted 2 lines
For all these reasons I think it should be reverted to the old behaviour.
You're right, this is no good. I think the other problems are fixable, but NAT really screws this.
Unfortunately, there is still a real problem with ipv6, in that the output side does not generate a packet too big ICMP like ipv4. Also, it feels kind of undesirable be rely on local ICMP instead of direct error message delivery. I'll try to generate a new patch.
Thanks,
-John
Quoted 12 lines · John Heffner
Quoted 10 lines · Patrick McHardy
This makes ping report an incorrect MTU when IPsec is used since we're only accounting for the additional
header_len, not thetrailer_len(which is not easily changeable). Additionally it will report different MTUs for the first and following fragments when the socket is corked because only the first fragment includes theheader_len. It also can't deal with things like NAT and routing by fwmark that change the route. The old behaviour was that we get an ICMP frag. required with the MTU of the final route, while this will always report the MTU of the initially chosen route.
Quoted 6 lines
Quoted 2 lines
For all these reasons I think it should be reverted to the old behaviour.
You're right, this is no good. I think the other problems are fixable, but NAT really screws this.
Routing by fwmark is also unfixable and IPsec is quite hard.
Quoted 4 lines
Unfortunately, there is still a real problem with ipv6, in that the output side does not generate a packet too big ICMP like ipv4. Also, it feels kind of undesirable be rely on local ICMP instead of direct error message delivery. I'll try to generate a new patch.
I think its necessary since at the transport layer we simply don't have all the information about whats going to happen to a packet. IPv6 now also supports routing by fwmark, so it has the same problem if it doesn't generate packet too big messages.
cover Re-try changes for PMTUDISC_PROBE
In reply to Patrick McHardyThis backs out the the transport layer MTU checks that don't work. As a consequence, I had to back out the PMTUDISC_PROBE patch as well. These patches should fix the problem with ipv6 that the transport layer change tried to address, and re-implement PMTUDISC_PROBE. I think this approach is nicer than the last one, since it doesn't require a bit in struct sk_buff.
Thanks,
-John
David Miller
maintainerRe: [PATCH 0/0] Re-try changes for PMTUDISC_PROBE
From: John Heffner <[email protected]>
Date: Wed, 18 Apr 2007 21:07:10 -0400
Quoted 6 lines
This backs out the the transport layer MTU checks that don't work. As a consequence, I had to back out the
PMTUDISC_PROBEpatch as well. These patches should fix the problem with ipv6 that the transport layer change tried to address, and re-implementPMTUDISC_PROBE. I think this approach is nicer than the last one, since it doesn't require a bit in structsk_buff.
Since I was rebasing net-2.6.22, here is what I did. I simply elided the two patches you backed out, then I'll add patch 3 and 4, the new one's, on top.
This will show up when I publish the rebased net-2.6.22 tree which I hope will happen later this evening.
Thanks!
PATCH 1/1 Revert "[NET] Add IP(V6)_PMTUDISC_RPOBE"
In reply to John HeffnerThis reverts commit d21d2a90b879c0cf159df5944847e6d9833816eb.
Must be backed out because commit 87e927a0583bd4a8ba9e97cd75b58d8aa1c76e37 does not work.
--- include/linux/in.h | 1 - include/linux/in6.h | 1 - include/linux/skbuff.h | 3 +-- include/net/ip.h | 2 +- net/core/skbuff.c | 2 -- net/ipv4/ip_output.c | 14 ++++---------- net/ipv4/ip_sockglue.c | 2 +- net/ipv4/raw.c | 3 --- net/ipv6/ip6_output.c | 12 ++++-------- net/ipv6/ipv6_sockglue.c | 2 +- net/ipv6/raw.c | 3 --- 11 files changed, 12 insertions(+), 33 deletions(-)
include/linux/in.h +0 -1
@@ -83,7 +83,6 @@ struct in_addr {#define IP_PMTUDISC_DONT 0/* Never send DF frames */ #define IP_PMTUDISC_WANT 1/* Use per route hints */ #define IP_PMTUDISC_DO 2/* Always DF */ -#define IP_PMTUDISC_PROBE 3/* Ignore dst pmtu */ #define IP_MULTICAST_IF 32#define IP_MULTICAST_TTL 33
include/linux/in6.h +0 -1
@@ -179,7 +179,6 @@ struct in6_flowlabel_req#define IPV6_PMTUDISC_DONT 0#define IPV6_PMTUDISC_WANT 1#define IPV6_PMTUDISC_DO 2 -#define IPV6_PMTUDISC_PROBE 3/* Flowlabel */ #define IPV6_FLOWLABEL_MGR 32
include/linux/skbuff.h +1 -2
@@ -277,8 +277,7 @@ struct sk_buff { nfctinfo:3 ; __u8 pkt_type:3, fclone:2, - ipvs_property:1, - ign_dst_mtu:1; + ipvs_property:1;__be16 protocol;void (*destructor)(struct sk_buff *skb);
include/net/ip.h +1 -1
@@ -206,7 +206,7 @@ int ip_decrease_ttl(struct iphdr *iph)static inline int ip_dont_fragment (struct sock *sk,struct dst_entry *dst) { -return (inet_sk (sk)->pmtudisc >=IP_PMTUDISC_DO || +return (inet_sk (sk)->pmtudisc ==IP_PMTUDISC_DO || (inet_sk (sk)->pmtudisc ==IP_PMTUDISC_WANT && !(dst_metric (dst,RTAX_LOCK )&(1 <<RTAX_MTU )))); }
net/core/skbuff.c +0 -2
@@ -479,7 +479,6 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)#if defined(CONFIG_IP_VS ) || defined(CONFIG_IP_VS_MODULE )C (ipvs_property);#endif -C (ign_dst_mtu);C (protocol); n->destructor =NULL ;C (mark);
@@ -543,7 +542,6 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)#if defined(CONFIG_IP_VS ) || defined(CONFIG_IP_VS_MODULE ) new->ipvs_property = old->ipvs_property;#endif - new->ign_dst_mtu = old->ign_dst_mtu;#ifdef CONFIG_NET_SCHED #ifdef CONFIG_NET_CLS_ACT new->tc_verd = old->tc_verd;
net/ipv4/ip_output.c +4 -10
@@ -198,8 +198,7 @@ static inline int ip_finish_output(struct sk_buff *skb) return dst_output(skb); }#endif -if (skb->len >dst_mtu (skb->dst) && - !skb->ign_dst_mtu && !skb_is_gso (skb)) +if (skb->len >dst_mtu (skb->dst) && !skb_is_gso (skb))return ip_fragment (skb, ip_finish_output2);else return ip_finish_output2 (skb);
@@ -788,9 +787,7 @@ int ip_append_data(struct sock *sk, inet->cork.addr = ipc->addr; }dst_hold (&rt->u.dst); - inet->cork.fragsize = mtu = inet->pmtudisc ==IP_PMTUDISC_PROBE ? - rt->u.dst.dev->mtu : -dst_mtu (rt->u.dst.path); + inet->cork.fragsize = mtu =dst_mtu (rt->u.dst.path); inet->cork.rt = rt; inet->cork.length =0 ; sk->sk_sndmsg_page =NULL ;
@@ -1208,16 +1205,13 @@ int ip_push_pending_frames(struct sock *sk)* to fragment the frame generated here. No matter, what transforms * how transforms change size of the packet, it will come out. */ -if (inet->pmtudisc <IP_PMTUDISC_DO ) +if (inet->pmtudisc !=IP_PMTUDISC_DO ) skb->local_df =1 ; -if (inet->pmtudisc ==IP_PMTUDISC_PROBE ) - skb->ign_dst_mtu =1 ; -/* DF bit is set when we want to see DF on outgoing frames. * If local_df is set too, we still allow to fragment this frame * locally. */ -if (inet->pmtudisc >=IP_PMTUDISC_DO || +if (inet->pmtudisc ==IP_PMTUDISC_DO || (skb->len <=dst_mtu (&rt->u.dst) &&ip_dont_fragment (sk, &rt->u.dst))) df =htons (IP_DF );
net/ipv4/ip_sockglue.c +1 -1
@@ -542,7 +542,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, inet->hdrincl = val ?1 :0 ;break ;case IP_MTU_DISCOVER : -if (val<0 || val>3 ) +if (val<0 || val>2 ) goto e_inval; inet->pmtudisc = val;break ;
net/ipv4/raw.c +0 -3
@@ -304,9 +304,6 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,if (err) goto error_fault; -if (inet->pmtudisc ==IP_PMTUDISC_PROBE ) - skb->ign_dst_mtu =1 ; -/* We don't modify invalid header */ if (length >=sizeof (*iph) && iph->ihl *4U <= length) {if (!iph->saddr)
net/ipv6/ip6_output.c +4 -8
@@ -139,8 +139,8 @@ static int ip6_output2(struct sk_buff *skb)int ip6_output (struct sk_buff *skb) { -if ((skb->len >dst_mtu (skb->dst) && !skb->ign_dst_mtu && - !skb_is_gso (skb)) ||dst_allfrag (skb->dst)) +if ((skb->len >dst_mtu (skb->dst) && !skb_is_gso (skb)) || +dst_allfrag (skb->dst))return ip6_fragment (skb, ip6_output2);else return ip6_output2 (skb);
@@ -566,7 +566,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) hlen =ip6_find_1stfragopt (skb, &prevhdr); nexthdr = *prevhdr; - mtu = skb->ign_dst_mtu ? skb->len :dst_mtu (&rt->u.dst); + mtu =dst_mtu (&rt->u.dst);if (np && np->frag_size < mtu) {if (np->frag_size) mtu = np->frag_size;
@@ -1050,8 +1050,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, inet->cork.fl = *fl; np->cork.hop_limit = hlimit; np->cork.tclass = tclass; - mtu = np->pmtudisc ==IPV6_PMTUDISC_PROBE ? - rt->u.dst.dev->mtu :dst_mtu (rt->u.dst.path); + mtu =dst_mtu (rt->u.dst.path);if (np->frag_size < mtu) {if (np->frag_size) mtu = np->frag_size;
@@ -1339,9 +1338,6 @@ int ip6_push_pending_frames(struct sock *sk) tmp_skb->sk =NULL ; } -if (np->pmtudisc ==IPV6_PMTUDISC_PROBE ) - skb->ign_dst_mtu =1 ; -ipv6_addr_copy (final_dst, &fl->fl6_dst);__skb_pull (skb,skb_network_header_len (skb));if (opt && opt->opt_flen)
net/ipv6/ipv6_sockglue.c +1 -1
@@ -694,7 +694,7 @@ done: retv =ip6_ra_control (sk, val,NULL );break ;case IPV6_MTU_DISCOVER : -if (val<0 || val>3 ) +if (val<0 || val>2 ) goto e_inval; np->pmtudisc = val; retv =0 ;
net/ipv6/raw.c +0 -3
@@ -591,9 +591,6 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,if (err) goto error_fault; -if (np->pmtudisc ==IPV6_PMTUDISC_PROBE ) - skb->ign_dst_mtu =1 ; -IP6_INC_STATS (rt->rt6i_idev,IPSTATS_MIB_OUTREQUESTS ); err =NF_HOOK (PF_INET6 ,NF_IP6_LOCAL_OUT , skb,NULL , rt->u.dst.dev, dst_output);
Signature · 1 line
1.5.1.rc3.30.ga8f4-dirty
PATCH 1/1 Revert "[NET] Do pmtu check in transport layer"
This reverts commit 87e927a0583bd4a8ba9e97cd75b58d8aa1c76e37.
This idea does not work, as pointed at by Patrick McHardy.
--- net/ipv4/ip_output.c | 4 +--- net/ipv4/raw.c | 8 +++----- net/ipv6/ip6_output.c | 11 +++++------ net/ipv6/raw.c | 7 ++----- 4 files changed, 11 insertions(+), 19 deletions(-)
net/ipv4/ip_output.c +1 -3
@@ -810,9 +810,7 @@ int ip_append_data(struct sock *sk, fragheaderlen =sizeof (struct iphdr ) + (opt ? opt->optlen :0 ); maxfraglen = ((mtu - fragheaderlen) & ~7 ) + fragheaderlen; -if (inet->cork.length + length >0xFFFF - fragheaderlen || - (inet->pmtudisc >=IP_PMTUDISC_DO && - inet->cork.length + length > mtu)) { +if (inet->cork.length + length >0xFFFF - fragheaderlen) {ip_local_error (sk,EMSGSIZE , rt->rt_dst, inet->dport, mtu-exthdrlen);return -EMSGSIZE ; }
net/ipv4/raw.c +3 -5
@@ -271,12 +271,10 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,struct iphdr *iph;struct sk_buff *skb;int err; -int mtu; - mtu = inet->pmtudisc ==IP_PMTUDISC_DO ?dst_mtu (&rt->u.dst) : - rt->u.dst.dev->mtu; -if (length > mtu) { -ip_local_error (sk,EMSGSIZE , rt->rt_dst, inet->dport, mtu); +if (length > rt->u.dst.dev->mtu) { +ip_local_error (sk,EMSGSIZE , rt->rt_dst, inet->dport, + rt->u.dst.dev->mtu);return -EMSGSIZE ; }if (flags&MSG_PROBE )
net/ipv6/ip6_output.c +5 -6
@@ -1079,12 +1079,11 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, fragheaderlen =sizeof (struct ipv6hdr ) + rt->u.dst.nfheader_len + (opt ? opt->opt_nflen :0 ); maxfraglen = ((mtu - fragheaderlen) & ~7 ) + fragheaderlen -sizeof (struct frag_hdr ); -if ((mtu <=sizeof (struct ipv6hdr ) +IPV6_MAXPLEN && - inet->cork.length + length >sizeof (struct ipv6hdr ) +IPV6_MAXPLEN - fragheaderlen) || - (np->pmtudisc >=IPV6_PMTUDISC_DO && - inet->cork.length + length > mtu)) { -ipv6_local_error (sk,EMSGSIZE , fl, mtu-exthdrlen); -return -EMSGSIZE ; +if (mtu <=sizeof (struct ipv6hdr ) +IPV6_MAXPLEN ) { +if (inet->cork.length + length >sizeof (struct ipv6hdr ) +IPV6_MAXPLEN - fragheaderlen) { +ipv6_local_error (sk,EMSGSIZE , fl, mtu-exthdrlen); +return -EMSGSIZE ; + } }/*
net/ipv6/raw.c +2 -5
@@ -558,12 +558,9 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,struct sk_buff *skb;unsigned int hh_len;int err; -int mtu; - mtu = np->pmtudisc ==IPV6_PMTUDISC_DO ?dst_mtu (&rt->u.dst) : - rt->u.dst.dev->mtu; -if (length > mtu) { -ipv6_local_error (sk,EMSGSIZE , fl, mtu); +if (length > rt->u.dst.dev->mtu) { +ipv6_local_error (sk,EMSGSIZE , fl, rt->u.dst.dev->mtu);return -EMSGSIZE ; }if (flags&MSG_PROBE )
Signature · 1 line
1.5.1.rc3.30.ga8f4-dirty
PATCH 1/1 MTU discovery check in ip6_fragment()
Adds a check in ip6_fragment() mirroring ip_fragment() for packets that we can't fragment, and sends an ICMP Packet Too Big message in response.
--- net/ipv6/ip6_output.c | 13 +++++++++++++ 1 files changed, 13 insertions(+), 0 deletions(-)
net/ipv6/ip6_output.c +13 -0
@@ -567,6 +567,19 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) nexthdr = *prevhdr; mtu =dst_mtu (&rt->u.dst); + +/* We must not fragment if the socket is set to force MTU discovery +* or if the skb it not generated by a local socket. (This last +* check should be redundant, but it's free.) +*/ +if (!np || np->pmtudisc >=IPV6_PMTUDISC_DO ) { + skb->dev = skb->dst->dev; +icmpv6_send (skb,ICMPV6_PKT_TOOBIG ,0 , mtu, skb->dev); +IP6_INC_STATS (ip6_dst_idev (skb->dst),IPSTATS_MIB_FRAGFAILS ); +kfree_skb (skb); +return -EMSGSIZE ; + } +if (np && np->frag_size < mtu) {if (np->frag_size) mtu = np->frag_size;
Signature · 1 line
1.5.1.rc3.30.ga8f4-dirty
PATCH 1/1 Add IP(V6)_PMTUDISC_RPOBE
Add IP(V6)_PMTUDISC_PROBE value for IP(V6)_MTU_DISCOVER. This option forces us not to fragment, but does not make use of the kernel path MTU discovery. That is, it allows for user-mode MTU probing (or, packetization-layer path MTU discovery). This is particularly useful for diagnostic utilities, like traceroute/tracepath.
--- include/linux/in.h | 1 + include/linux/in6.h | 1 + net/ipv4/ip_output.c | 20 +++++++++++++++----- net/ipv4/ip_sockglue.c | 2 +- net/ipv6/ip6_output.c | 15 ++++++++++++--- net/ipv6/ipv6_sockglue.c | 2 +- 6 files changed, 31 insertions(+), 10 deletions(-)
include/linux/in.h +1 -0
@@ -83,6 +83,7 @@ struct in_addr {#define IP_PMTUDISC_DONT 0/* Never send DF frames */ #define IP_PMTUDISC_WANT 1/* Use per route hints */ #define IP_PMTUDISC_DO 2/* Always DF */ +#define IP_PMTUDISC_PROBE 3/* Ignore dst pmtu */ #define IP_MULTICAST_IF 32#define IP_MULTICAST_TTL 33
include/linux/in6.h +1 -0
@@ -179,6 +179,7 @@ struct in6_flowlabel_req#define IPV6_PMTUDISC_DONT 0#define IPV6_PMTUDISC_WANT 1#define IPV6_PMTUDISC_DO 2 +#define IPV6_PMTUDISC_PROBE 3/* Flowlabel */ #define IPV6_FLOWLABEL_MGR 32
net/ipv4/ip_output.c +15 -5
@@ -189,6 +189,14 @@ static inline int ip_finish_output2(struct sk_buff *skb)return -EINVAL ; } +static inline int ip_skb_dst_mtu (struct sk_buff *skb) +{ +struct inet_sock *inet = skb->sk ?inet_sk (skb->sk) :NULL ; + +return (inet && inet->pmtudisc ==IP_PMTUDISC_PROBE ) ? + skb->dst->dev->mtu :dst_mtu (skb->dst); +} +static inline int ip_finish_output (struct sk_buff *skb) {#if defined(CONFIG_NETFILTER ) && defined(CONFIG_XFRM )
@@ -198,7 +206,7 @@ static inline int ip_finish_output(struct sk_buff *skb) return dst_output(skb); }#endif -if (skb->len >dst_mtu (skb->dst) && !skb_is_gso (skb)) +if (skb->len >ip_skb_dst_mtu (skb) && !skb_is_gso (skb))return ip_fragment (skb, ip_finish_output2);else return ip_finish_output2 (skb);
@@ -422,7 +430,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))if (unlikely ((iph->frag_off &htons (IP_DF )) && !skb->local_df)) {IP_INC_STATS (IPSTATS_MIB_FRAGFAILS );icmp_send (skb,ICMP_DEST_UNREACH ,ICMP_FRAG_NEEDED , -htonl (dst_mtu (&rt->u.dst))); +htonl (ip_skb_dst_mtu (skb)));kfree_skb (skb);return -EMSGSIZE ; }
@@ -787,7 +795,9 @@ int ip_append_data(struct sock *sk, inet->cork.addr = ipc->addr; }dst_hold (&rt->u.dst); - inet->cork.fragsize = mtu =dst_mtu (rt->u.dst.path); + inet->cork.fragsize = mtu = inet->pmtudisc ==IP_PMTUDISC_PROBE ? + rt->u.dst.dev->mtu : +dst_mtu (rt->u.dst.path); inet->cork.rt = rt; inet->cork.length =0 ; sk->sk_sndmsg_page =NULL ;
@@ -1203,13 +1213,13 @@ int ip_push_pending_frames(struct sock *sk)* to fragment the frame generated here. No matter, what transforms * how transforms change size of the packet, it will come out. */ -if (inet->pmtudisc !=IP_PMTUDISC_DO ) +if (inet->pmtudisc <IP_PMTUDISC_DO ) skb->local_df =1 ;/* DF bit is set when we want to see DF on outgoing frames. * If local_df is set too, we still allow to fragment this frame * locally. */ -if (inet->pmtudisc ==IP_PMTUDISC_DO || +if (inet->pmtudisc >=IP_PMTUDISC_DO || (skb->len <=dst_mtu (&rt->u.dst) &&ip_dont_fragment (sk, &rt->u.dst))) df =htons (IP_DF );
net/ipv4/ip_sockglue.c +1 -1
@@ -542,7 +542,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, inet->hdrincl = val ?1 :0 ;break ;case IP_MTU_DISCOVER : -if (val<0 || val>2 ) +if (val<0 || val>3 ) goto e_inval; inet->pmtudisc = val;break ;
net/ipv6/ip6_output.c +12 -3
@@ -137,9 +137,17 @@ static int ip6_output2(struct sk_buff *skb)return NF_HOOK (PF_INET6 ,NF_IP6_POST_ROUTING , skb,NULL , skb->dev,ip6_output_finish); } +static inline int ip6_skb_dst_mtu (struct sk_buff *skb) +{ +struct ipv6_pinfo *np = skb->sk ?inet6_sk (skb->sk) :NULL ; + +return (np && np->pmtudisc ==IPV6_PMTUDISC_PROBE ) ? + skb->dst->dev->mtu :dst_mtu (skb->dst); +} +int ip6_output (struct sk_buff *skb) { -if ((skb->len >dst_mtu (skb->dst) && !skb_is_gso (skb)) || +if ((skb->len >ip6_skb_dst_mtu (skb) && !skb_is_gso (skb)) ||dst_allfrag (skb->dst))return ip6_fragment (skb, ip6_output2);else
@@ -566,7 +574,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) hlen =ip6_find_1stfragopt (skb, &prevhdr); nexthdr = *prevhdr; - mtu =dst_mtu (&rt->u.dst); + mtu =ip6_skb_dst_mtu (skb);/* We must not fragment if the socket is set to force MTU discovery * or if the skb it not generated by a local socket. (This last
@@ -1063,7 +1071,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, inet->cork.fl = *fl; np->cork.hop_limit = hlimit; np->cork.tclass = tclass; - mtu =dst_mtu (rt->u.dst.path); + mtu = np->pmtudisc ==IPV6_PMTUDISC_PROBE ? + rt->u.dst.dev->mtu :dst_mtu (rt->u.dst.path);if (np->frag_size < mtu) {if (np->frag_size) mtu = np->frag_size;
net/ipv6/ipv6_sockglue.c +1 -1
@@ -694,7 +694,7 @@ done: retv =ip6_ra_control (sk, val,NULL );break ;case IPV6_MTU_DISCOVER : -if (val<0 || val>2 ) +if (val<0 || val>3 ) goto e_inval; np->pmtudisc = val; retv =0 ;
Signature · 1 line
1.5.1.rc3.30.ga8f4-dirty
Re: [PATCH] [NET] Add IP(V6)_PMTUDISC_RPOBE
Sorry, forgot the -n flag on git-format-patch. Patches resent with correct sequence numbers.
Thanks,
-John
David Miller
maintainerRe: [PATCH] [NET] Add IP(V6)_PMTUDISC_RPOBE
From: John Heffner <[email protected]>
Date: Wed, 18 Apr 2007 21:11:26 -0400
Quoted 2 lines
Sorry, forgot the -n flag on git-format-patch. Patches resent with correct sequence numbers.
Thanks for fixing that.
PATCH 1/4 Revert "[NET] Add IP(V6)_PMTUDISC_RPOBE"
In reply to John HeffnerThis reverts commit d21d2a90b879c0cf159df5944847e6d9833816eb.
Must be backed out because commit 87e927a0583bd4a8ba9e97cd75b58d8aa1c76e37 does not work.
--- include/linux/in.h | 1 - include/linux/in6.h | 1 - include/linux/skbuff.h | 3 +-- include/net/ip.h | 2 +- net/core/skbuff.c | 2 -- net/ipv4/ip_output.c | 14 ++++---------- net/ipv4/ip_sockglue.c | 2 +- net/ipv4/raw.c | 3 --- net/ipv6/ip6_output.c | 12 ++++-------- net/ipv6/ipv6_sockglue.c | 2 +- net/ipv6/raw.c | 3 --- 11 files changed, 12 insertions(+), 33 deletions(-)
include/linux/in.h +0 -1
@@ -83,7 +83,6 @@ struct in_addr {#define IP_PMTUDISC_DONT 0/* Never send DF frames */ #define IP_PMTUDISC_WANT 1/* Use per route hints */ #define IP_PMTUDISC_DO 2/* Always DF */ -#define IP_PMTUDISC_PROBE 3/* Ignore dst pmtu */ #define IP_MULTICAST_IF 32#define IP_MULTICAST_TTL 33
include/linux/in6.h +0 -1
@@ -179,7 +179,6 @@ struct in6_flowlabel_req#define IPV6_PMTUDISC_DONT 0#define IPV6_PMTUDISC_WANT 1#define IPV6_PMTUDISC_DO 2 -#define IPV6_PMTUDISC_PROBE 3/* Flowlabel */ #define IPV6_FLOWLABEL_MGR 32
include/linux/skbuff.h +1 -2
@@ -277,8 +277,7 @@ struct sk_buff { nfctinfo:3 ; __u8 pkt_type:3, fclone:2, - ipvs_property:1, - ign_dst_mtu:1; + ipvs_property:1;__be16 protocol;void (*destructor)(struct sk_buff *skb);
include/net/ip.h +1 -1
@@ -206,7 +206,7 @@ int ip_decrease_ttl(struct iphdr *iph)static inline int ip_dont_fragment (struct sock *sk,struct dst_entry *dst) { -return (inet_sk (sk)->pmtudisc >=IP_PMTUDISC_DO || +return (inet_sk (sk)->pmtudisc ==IP_PMTUDISC_DO || (inet_sk (sk)->pmtudisc ==IP_PMTUDISC_WANT && !(dst_metric (dst,RTAX_LOCK )&(1 <<RTAX_MTU )))); }
net/core/skbuff.c +0 -2
@@ -479,7 +479,6 @@ struct sk_buff *skb_clone(struct sk_buff *skb, gfp_t gfp_mask)#if defined(CONFIG_IP_VS ) || defined(CONFIG_IP_VS_MODULE )C (ipvs_property);#endif -C (ign_dst_mtu);C (protocol); n->destructor =NULL ;C (mark);
@@ -543,7 +542,6 @@ static void copy_skb_header(struct sk_buff *new, const struct sk_buff *old)#if defined(CONFIG_IP_VS ) || defined(CONFIG_IP_VS_MODULE ) new->ipvs_property = old->ipvs_property;#endif - new->ign_dst_mtu = old->ign_dst_mtu;#ifdef CONFIG_NET_SCHED #ifdef CONFIG_NET_CLS_ACT new->tc_verd = old->tc_verd;
net/ipv4/ip_output.c +4 -10
@@ -198,8 +198,7 @@ static inline int ip_finish_output(struct sk_buff *skb) return dst_output(skb); }#endif -if (skb->len >dst_mtu (skb->dst) && - !skb->ign_dst_mtu && !skb_is_gso (skb)) +if (skb->len >dst_mtu (skb->dst) && !skb_is_gso (skb))return ip_fragment (skb, ip_finish_output2);else return ip_finish_output2 (skb);
@@ -788,9 +787,7 @@ int ip_append_data(struct sock *sk, inet->cork.addr = ipc->addr; }dst_hold (&rt->u.dst); - inet->cork.fragsize = mtu = inet->pmtudisc ==IP_PMTUDISC_PROBE ? - rt->u.dst.dev->mtu : -dst_mtu (rt->u.dst.path); + inet->cork.fragsize = mtu =dst_mtu (rt->u.dst.path); inet->cork.rt = rt; inet->cork.length =0 ; sk->sk_sndmsg_page =NULL ;
@@ -1208,16 +1205,13 @@ int ip_push_pending_frames(struct sock *sk)* to fragment the frame generated here. No matter, what transforms * how transforms change size of the packet, it will come out. */ -if (inet->pmtudisc <IP_PMTUDISC_DO ) +if (inet->pmtudisc !=IP_PMTUDISC_DO ) skb->local_df =1 ; -if (inet->pmtudisc ==IP_PMTUDISC_PROBE ) - skb->ign_dst_mtu =1 ; -/* DF bit is set when we want to see DF on outgoing frames. * If local_df is set too, we still allow to fragment this frame * locally. */ -if (inet->pmtudisc >=IP_PMTUDISC_DO || +if (inet->pmtudisc ==IP_PMTUDISC_DO || (skb->len <=dst_mtu (&rt->u.dst) &&ip_dont_fragment (sk, &rt->u.dst))) df =htons (IP_DF );
net/ipv4/ip_sockglue.c +1 -1
@@ -542,7 +542,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, inet->hdrincl = val ?1 :0 ;break ;case IP_MTU_DISCOVER : -if (val<0 || val>3 ) +if (val<0 || val>2 ) goto e_inval; inet->pmtudisc = val;break ;
net/ipv4/raw.c +0 -3
@@ -304,9 +304,6 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,if (err) goto error_fault; -if (inet->pmtudisc ==IP_PMTUDISC_PROBE ) - skb->ign_dst_mtu =1 ; -/* We don't modify invalid header */ if (length >=sizeof (*iph) && iph->ihl *4U <= length) {if (!iph->saddr)
net/ipv6/ip6_output.c +4 -8
@@ -139,8 +139,8 @@ static int ip6_output2(struct sk_buff *skb)int ip6_output (struct sk_buff *skb) { -if ((skb->len >dst_mtu (skb->dst) && !skb->ign_dst_mtu && - !skb_is_gso (skb)) ||dst_allfrag (skb->dst)) +if ((skb->len >dst_mtu (skb->dst) && !skb_is_gso (skb)) || +dst_allfrag (skb->dst))return ip6_fragment (skb, ip6_output2);else return ip6_output2 (skb);
@@ -566,7 +566,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) hlen =ip6_find_1stfragopt (skb, &prevhdr); nexthdr = *prevhdr; - mtu = skb->ign_dst_mtu ? skb->len :dst_mtu (&rt->u.dst); + mtu =dst_mtu (&rt->u.dst);if (np && np->frag_size < mtu) {if (np->frag_size) mtu = np->frag_size;
@@ -1050,8 +1050,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, inet->cork.fl = *fl; np->cork.hop_limit = hlimit; np->cork.tclass = tclass; - mtu = np->pmtudisc ==IPV6_PMTUDISC_PROBE ? - rt->u.dst.dev->mtu :dst_mtu (rt->u.dst.path); + mtu =dst_mtu (rt->u.dst.path);if (np->frag_size < mtu) {if (np->frag_size) mtu = np->frag_size;
@@ -1339,9 +1338,6 @@ int ip6_push_pending_frames(struct sock *sk) tmp_skb->sk =NULL ; } -if (np->pmtudisc ==IPV6_PMTUDISC_PROBE ) - skb->ign_dst_mtu =1 ; -ipv6_addr_copy (final_dst, &fl->fl6_dst);__skb_pull (skb,skb_network_header_len (skb));if (opt && opt->opt_flen)
net/ipv6/ipv6_sockglue.c +1 -1
@@ -694,7 +694,7 @@ done: retv =ip6_ra_control (sk, val,NULL );break ;case IPV6_MTU_DISCOVER : -if (val<0 || val>3 ) +if (val<0 || val>2 ) goto e_inval; np->pmtudisc = val; retv =0 ;
net/ipv6/raw.c +0 -3
@@ -591,9 +591,6 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,if (err) goto error_fault; -if (np->pmtudisc ==IPV6_PMTUDISC_PROBE ) - skb->ign_dst_mtu =1 ; -IP6_INC_STATS (rt->rt6i_idev,IPSTATS_MIB_OUTREQUESTS ); err =NF_HOOK (PF_INET6 ,NF_IP6_LOCAL_OUT , skb,NULL , rt->u.dst.dev, dst_output);
Signature · 1 line
1.5.1.rc3.30.ga8f4-dirty
PATCH 2/4 Revert "[NET] Do pmtu check in transport layer"
This reverts commit 87e927a0583bd4a8ba9e97cd75b58d8aa1c76e37.
This idea does not work, as pointed at by Patrick McHardy.
--- net/ipv4/ip_output.c | 4 +--- net/ipv4/raw.c | 8 +++----- net/ipv6/ip6_output.c | 11 +++++------ net/ipv6/raw.c | 7 ++----- 4 files changed, 11 insertions(+), 19 deletions(-)
net/ipv4/ip_output.c +1 -3
@@ -810,9 +810,7 @@ int ip_append_data(struct sock *sk, fragheaderlen =sizeof (struct iphdr ) + (opt ? opt->optlen :0 ); maxfraglen = ((mtu - fragheaderlen) & ~7 ) + fragheaderlen; -if (inet->cork.length + length >0xFFFF - fragheaderlen || - (inet->pmtudisc >=IP_PMTUDISC_DO && - inet->cork.length + length > mtu)) { +if (inet->cork.length + length >0xFFFF - fragheaderlen) {ip_local_error (sk,EMSGSIZE , rt->rt_dst, inet->dport, mtu-exthdrlen);return -EMSGSIZE ; }
net/ipv4/raw.c +3 -5
@@ -271,12 +271,10 @@ static int raw_send_hdrinc(struct sock *sk, void *from, size_t length,struct iphdr *iph;struct sk_buff *skb;int err; -int mtu; - mtu = inet->pmtudisc ==IP_PMTUDISC_DO ?dst_mtu (&rt->u.dst) : - rt->u.dst.dev->mtu; -if (length > mtu) { -ip_local_error (sk,EMSGSIZE , rt->rt_dst, inet->dport, mtu); +if (length > rt->u.dst.dev->mtu) { +ip_local_error (sk,EMSGSIZE , rt->rt_dst, inet->dport, + rt->u.dst.dev->mtu);return -EMSGSIZE ; }if (flags&MSG_PROBE )
net/ipv6/ip6_output.c +5 -6
@@ -1079,12 +1079,11 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, fragheaderlen =sizeof (struct ipv6hdr ) + rt->u.dst.nfheader_len + (opt ? opt->opt_nflen :0 ); maxfraglen = ((mtu - fragheaderlen) & ~7 ) + fragheaderlen -sizeof (struct frag_hdr ); -if ((mtu <=sizeof (struct ipv6hdr ) +IPV6_MAXPLEN && - inet->cork.length + length >sizeof (struct ipv6hdr ) +IPV6_MAXPLEN - fragheaderlen) || - (np->pmtudisc >=IPV6_PMTUDISC_DO && - inet->cork.length + length > mtu)) { -ipv6_local_error (sk,EMSGSIZE , fl, mtu-exthdrlen); -return -EMSGSIZE ; +if (mtu <=sizeof (struct ipv6hdr ) +IPV6_MAXPLEN ) { +if (inet->cork.length + length >sizeof (struct ipv6hdr ) +IPV6_MAXPLEN - fragheaderlen) { +ipv6_local_error (sk,EMSGSIZE , fl, mtu-exthdrlen); +return -EMSGSIZE ; + } }/*
net/ipv6/raw.c +2 -5
@@ -558,12 +558,9 @@ static int rawv6_send_hdrinc(struct sock *sk, void *from, int length,struct sk_buff *skb;unsigned int hh_len;int err; -int mtu; - mtu = np->pmtudisc ==IPV6_PMTUDISC_DO ?dst_mtu (&rt->u.dst) : - rt->u.dst.dev->mtu; -if (length > mtu) { -ipv6_local_error (sk,EMSGSIZE , fl, mtu); +if (length > rt->u.dst.dev->mtu) { +ipv6_local_error (sk,EMSGSIZE , fl, rt->u.dst.dev->mtu);return -EMSGSIZE ; }if (flags&MSG_PROBE )
Signature · 1 line
1.5.1.rc3.30.ga8f4-dirty
PATCH 3/4 MTU discovery check in ip6_fragment()
Adds a check in ip6_fragment() mirroring ip_fragment() for packets that we can't fragment, and sends an ICMP Packet Too Big message in response.
--- net/ipv6/ip6_output.c | 13 +++++++++++++ 1 files changed, 13 insertions(+), 0 deletions(-)
net/ipv6/ip6_output.c +13 -0
@@ -567,6 +567,19 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) nexthdr = *prevhdr; mtu =dst_mtu (&rt->u.dst); + +/* We must not fragment if the socket is set to force MTU discovery +* or if the skb it not generated by a local socket. (This last +* check should be redundant, but it's free.) +*/ +if (!np || np->pmtudisc >=IPV6_PMTUDISC_DO ) { + skb->dev = skb->dst->dev; +icmpv6_send (skb,ICMPV6_PKT_TOOBIG ,0 , mtu, skb->dev); +IP6_INC_STATS (ip6_dst_idev (skb->dst),IPSTATS_MIB_FRAGFAILS ); +kfree_skb (skb); +return -EMSGSIZE ; + } +if (np && np->frag_size < mtu) {if (np->frag_size) mtu = np->frag_size;
Signature · 1 line
1.5.1.rc3.30.ga8f4-dirty
PATCH 4/4 Add IP(V6)_PMTUDISC_RPOBE
Add IP(V6)_PMTUDISC_PROBE value for IP(V6)_MTU_DISCOVER. This option forces us not to fragment, but does not make use of the kernel path MTU discovery. That is, it allows for user-mode MTU probing (or, packetization-layer path MTU discovery). This is particularly useful for diagnostic utilities, like traceroute/tracepath.
--- include/linux/in.h | 1 + include/linux/in6.h | 1 + net/ipv4/ip_output.c | 20 +++++++++++++++----- net/ipv4/ip_sockglue.c | 2 +- net/ipv6/ip6_output.c | 15 ++++++++++++--- net/ipv6/ipv6_sockglue.c | 2 +- 6 files changed, 31 insertions(+), 10 deletions(-)
include/linux/in.h +1 -0
@@ -83,6 +83,7 @@ struct in_addr {#define IP_PMTUDISC_DONT 0/* Never send DF frames */ #define IP_PMTUDISC_WANT 1/* Use per route hints */ #define IP_PMTUDISC_DO 2/* Always DF */ +#define IP_PMTUDISC_PROBE 3/* Ignore dst pmtu */ #define IP_MULTICAST_IF 32#define IP_MULTICAST_TTL 33
include/linux/in6.h +1 -0
@@ -179,6 +179,7 @@ struct in6_flowlabel_req#define IPV6_PMTUDISC_DONT 0#define IPV6_PMTUDISC_WANT 1#define IPV6_PMTUDISC_DO 2 +#define IPV6_PMTUDISC_PROBE 3/* Flowlabel */ #define IPV6_FLOWLABEL_MGR 32
net/ipv4/ip_output.c +15 -5
@@ -189,6 +189,14 @@ static inline int ip_finish_output2(struct sk_buff *skb)return -EINVAL ; } +static inline int ip_skb_dst_mtu (struct sk_buff *skb) +{ +struct inet_sock *inet = skb->sk ?inet_sk (skb->sk) :NULL ; + +return (inet && inet->pmtudisc ==IP_PMTUDISC_PROBE ) ? + skb->dst->dev->mtu :dst_mtu (skb->dst); +} +static inline int ip_finish_output (struct sk_buff *skb) {#if defined(CONFIG_NETFILTER ) && defined(CONFIG_XFRM )
@@ -198,7 +206,7 @@ static inline int ip_finish_output(struct sk_buff *skb) return dst_output(skb); }#endif -if (skb->len >dst_mtu (skb->dst) && !skb_is_gso (skb)) +if (skb->len >ip_skb_dst_mtu (skb) && !skb_is_gso (skb))return ip_fragment (skb, ip_finish_output2);else return ip_finish_output2 (skb);
@@ -422,7 +430,7 @@ int ip_fragment(struct sk_buff *skb, int (*output)(struct sk_buff*))if (unlikely ((iph->frag_off &htons (IP_DF )) && !skb->local_df)) {IP_INC_STATS (IPSTATS_MIB_FRAGFAILS );icmp_send (skb,ICMP_DEST_UNREACH ,ICMP_FRAG_NEEDED , -htonl (dst_mtu (&rt->u.dst))); +htonl (ip_skb_dst_mtu (skb)));kfree_skb (skb);return -EMSGSIZE ; }
@@ -787,7 +795,9 @@ int ip_append_data(struct sock *sk, inet->cork.addr = ipc->addr; }dst_hold (&rt->u.dst); - inet->cork.fragsize = mtu =dst_mtu (rt->u.dst.path); + inet->cork.fragsize = mtu = inet->pmtudisc ==IP_PMTUDISC_PROBE ? + rt->u.dst.dev->mtu : +dst_mtu (rt->u.dst.path); inet->cork.rt = rt; inet->cork.length =0 ; sk->sk_sndmsg_page =NULL ;
@@ -1203,13 +1213,13 @@ int ip_push_pending_frames(struct sock *sk)* to fragment the frame generated here. No matter, what transforms * how transforms change size of the packet, it will come out. */ -if (inet->pmtudisc !=IP_PMTUDISC_DO ) +if (inet->pmtudisc <IP_PMTUDISC_DO ) skb->local_df =1 ;/* DF bit is set when we want to see DF on outgoing frames. * If local_df is set too, we still allow to fragment this frame * locally. */ -if (inet->pmtudisc ==IP_PMTUDISC_DO || +if (inet->pmtudisc >=IP_PMTUDISC_DO || (skb->len <=dst_mtu (&rt->u.dst) &&ip_dont_fragment (sk, &rt->u.dst))) df =htons (IP_DF );
net/ipv4/ip_sockglue.c +1 -1
@@ -542,7 +542,7 @@ static int do_ip_setsockopt(struct sock *sk, int level, inet->hdrincl = val ?1 :0 ;break ;case IP_MTU_DISCOVER : -if (val<0 || val>2 ) +if (val<0 || val>3 ) goto e_inval; inet->pmtudisc = val;break ;
net/ipv6/ip6_output.c +12 -3
@@ -137,9 +137,17 @@ static int ip6_output2(struct sk_buff *skb)return NF_HOOK (PF_INET6 ,NF_IP6_POST_ROUTING , skb,NULL , skb->dev,ip6_output_finish); } +static inline int ip6_skb_dst_mtu (struct sk_buff *skb) +{ +struct ipv6_pinfo *np = skb->sk ?inet6_sk (skb->sk) :NULL ; + +return (np && np->pmtudisc ==IPV6_PMTUDISC_PROBE ) ? + skb->dst->dev->mtu :dst_mtu (skb->dst); +} +int ip6_output (struct sk_buff *skb) { -if ((skb->len >dst_mtu (skb->dst) && !skb_is_gso (skb)) || +if ((skb->len >ip6_skb_dst_mtu (skb) && !skb_is_gso (skb)) ||dst_allfrag (skb->dst))return ip6_fragment (skb, ip6_output2);else
@@ -566,7 +574,7 @@ static int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *)) hlen =ip6_find_1stfragopt (skb, &prevhdr); nexthdr = *prevhdr; - mtu =dst_mtu (&rt->u.dst); + mtu =ip6_skb_dst_mtu (skb);/* We must not fragment if the socket is set to force MTU discovery * or if the skb it not generated by a local socket. (This last
@@ -1063,7 +1071,8 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to, inet->cork.fl = *fl; np->cork.hop_limit = hlimit; np->cork.tclass = tclass; - mtu =dst_mtu (rt->u.dst.path); + mtu = np->pmtudisc ==IPV6_PMTUDISC_PROBE ? + rt->u.dst.dev->mtu :dst_mtu (rt->u.dst.path);if (np->frag_size < mtu) {if (np->frag_size) mtu = np->frag_size;
net/ipv6/ipv6_sockglue.c +1 -1
@@ -694,7 +694,7 @@ done: retv =ip6_ra_control (sk, val,NULL );break ;case IPV6_MTU_DISCOVER : -if (val<0 || val>2 ) +if (val<0 || val>3 ) goto e_inval; np->pmtudisc = val; retv =0 ;
Signature · 1 line
1.5.1.rc3.30.ga8f4-dirty