ip: generate unique IP identificator if local fragmentation is allowed
authorAnsis Atteka <aatteka@nicira.com>
Wed, 18 Sep 2013 22:29:53 +0000 (15:29 -0700)
committerDavid S. Miller <davem@davemloft.net>
Thu, 19 Sep 2013 18:11:15 +0000 (14:11 -0400)
If local fragmentation is allowed, then ip_select_ident() and
ip_select_ident_more() need to generate unique IDs to ensure
correct defragmentation on the peer.

For example, if IPsec (tunnel mode) has to encrypt large skbs
that have local_df bit set, then all IP fragments that belonged
to different ESP datagrams would have used the same identificator.
If one of these IP fragments would get lost or reordered, then
peer could possibly stitch together wrong IP fragments that did
not belong to the same datagram. This would lead to a packet loss
or data corruption.

Signed-off-by: Ansis Atteka <aatteka@nicira.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
drivers/net/ppp/pptp.c
include/net/ip.h
net/ipv4/igmp.c
net/ipv4/inetpeer.c
net/ipv4/ip_output.c
net/ipv4/ipmr.c
net/ipv4/raw.c
net/ipv4/xfrm4_mode_tunnel.c
net/netfilter/ipvs/ip_vs_xmit.c

index 6fa5ae00039fd65b2c634d0d22241d9c7a9d13e2..01805319e1e0335a70f86109e058cac44d669ee0 100644 (file)
@@ -281,7 +281,7 @@ static int pptp_xmit(struct ppp_channel *chan, struct sk_buff *skb)
        nf_reset(skb);
 
        skb->ip_summed = CHECKSUM_NONE;
-       ip_select_ident(iph, &rt->dst, NULL);
+       ip_select_ident(skb, &rt->dst, NULL);
        ip_send_check(iph);
 
        ip_local_out(skb);
index 48f55979d842c3e9d46eedb5c6d893374c0ef499..5e5268807a1ceb521f673ca05ec3233389a97f61 100644 (file)
@@ -264,9 +264,11 @@ int ip_dont_fragment(struct sock *sk, struct dst_entry *dst)
 
 extern void __ip_select_ident(struct iphdr *iph, struct dst_entry *dst, int more);
 
-static inline void ip_select_ident(struct iphdr *iph, struct dst_entry *dst, struct sock *sk)
+static inline void ip_select_ident(struct sk_buff *skb, struct dst_entry *dst, struct sock *sk)
 {
-       if (iph->frag_off & htons(IP_DF)) {
+       struct iphdr *iph = ip_hdr(skb);
+
+       if ((iph->frag_off & htons(IP_DF)) && !skb->local_df) {
                /* This is only to work around buggy Windows95/2000
                 * VJ compression implementations.  If the ID field
                 * does not change, they drop every other packet in
@@ -278,9 +280,11 @@ static inline void ip_select_ident(struct iphdr *iph, struct dst_entry *dst, str
                __ip_select_ident(iph, dst, 0);
 }
 
-static inline void ip_select_ident_more(struct iphdr *iph, struct dst_entry *dst, struct sock *sk, int more)
+static inline void ip_select_ident_more(struct sk_buff *skb, struct dst_entry *dst, struct sock *sk, int more)
 {
-       if (iph->frag_off & htons(IP_DF)) {
+       struct iphdr *iph = ip_hdr(skb);
+
+       if ((iph->frag_off & htons(IP_DF)) && !skb->local_df) {
                if (sk && inet_sk(sk)->inet_daddr) {
                        iph->id = htons(inet_sk(sk)->inet_id);
                        inet_sk(sk)->inet_id += 1 + more;
index d6c0e64ec97f2147124e84d4b634f263bac9eb95..dace87f06e5f9bf22ed99d078d058ea8f1339f4a 100644 (file)
@@ -369,7 +369,7 @@ static struct sk_buff *igmpv3_newpack(struct net_device *dev, int size)
        pip->saddr    = fl4.saddr;
        pip->protocol = IPPROTO_IGMP;
        pip->tot_len  = 0;      /* filled in later */
-       ip_select_ident(pip, &rt->dst, NULL);
+       ip_select_ident(skb, &rt->dst, NULL);
        ((u8 *)&pip[1])[0] = IPOPT_RA;
        ((u8 *)&pip[1])[1] = 4;
        ((u8 *)&pip[1])[2] = 0;
@@ -714,7 +714,7 @@ static int igmp_send_report(struct in_device *in_dev, struct ip_mc_list *pmc,
        iph->daddr    = dst;
        iph->saddr    = fl4.saddr;
        iph->protocol = IPPROTO_IGMP;
-       ip_select_ident(iph, &rt->dst, NULL);
+       ip_select_ident(skb, &rt->dst, NULL);
        ((u8 *)&iph[1])[0] = IPOPT_RA;
        ((u8 *)&iph[1])[1] = 4;
        ((u8 *)&iph[1])[2] = 0;
index 000e3d239d6481ed230e71c9c9033dba301694e9..33d5537881ed7b39e33199dfe978bbe5b912d706 100644 (file)
@@ -32,8 +32,8 @@
  *  At the moment of writing this notes identifier of IP packets is generated
  *  to be unpredictable using this code only for packets subjected
  *  (actually or potentially) to defragmentation.  I.e. DF packets less than
- *  PMTU in size uses a constant ID and do not use this code (see
- *  ip_select_ident() in include/net/ip.h).
+ *  PMTU in size when local fragmentation is disabled use a constant ID and do
+ *  not use this code (see ip_select_ident() in include/net/ip.h).
  *
  *  Route cache entries hold references to our nodes.
  *  New cache entries get references via lookup by destination IP address in
index eae2e262fbe5c68e582fa6eeab92fdfab3ccddf1..a04d872c54f919c7133e7830773301cdf070f3ed 100644 (file)
@@ -148,7 +148,7 @@ int ip_build_and_send_pkt(struct sk_buff *skb, struct sock *sk,
        iph->daddr    = (opt && opt->opt.srr ? opt->opt.faddr : daddr);
        iph->saddr    = saddr;
        iph->protocol = sk->sk_protocol;
-       ip_select_ident(iph, &rt->dst, sk);
+       ip_select_ident(skb, &rt->dst, sk);
 
        if (opt && opt->opt.optlen) {
                iph->ihl += opt->opt.optlen>>2;
@@ -386,7 +386,7 @@ packet_routed:
                ip_options_build(skb, &inet_opt->opt, inet->inet_daddr, rt, 0);
        }
 
-       ip_select_ident_more(iph, &rt->dst, sk,
+       ip_select_ident_more(skb, &rt->dst, sk,
                             (skb_shinfo(skb)->gso_segs ?: 1) - 1);
 
        skb->priority = sk->sk_priority;
@@ -1324,7 +1324,7 @@ struct sk_buff *__ip_make_skb(struct sock *sk,
        iph->ttl = ttl;
        iph->protocol = sk->sk_protocol;
        ip_copy_addrs(iph, fl4);
-       ip_select_ident(iph, &rt->dst, sk);
+       ip_select_ident(skb, &rt->dst, sk);
 
        if (opt) {
                iph->ihl += opt->optlen>>2;
index 9ae54b09254f158d82d6b86adafe04776c830775..62212c772a4b95961dafe1efce3fd06f146ccbb6 100644 (file)
@@ -1658,7 +1658,7 @@ static void ip_encap(struct sk_buff *skb, __be32 saddr, __be32 daddr)
        iph->protocol   =       IPPROTO_IPIP;
        iph->ihl        =       5;
        iph->tot_len    =       htons(skb->len);
-       ip_select_ident(iph, skb_dst(skb), NULL);
+       ip_select_ident(skb, skb_dst(skb), NULL);
        ip_send_check(iph);
 
        memset(&(IPCB(skb)->opt), 0, sizeof(IPCB(skb)->opt));
index a86c7ae71881b1e65e998888c92749fe63fcf3c1..bfec521c717fd2320242c24e7a7f74a64c1c1a44 100644 (file)
@@ -387,7 +387,7 @@ static int raw_send_hdrinc(struct sock *sk, struct flowi4 *fl4,
                iph->check   = 0;
                iph->tot_len = htons(length);
                if (!iph->id)
-                       ip_select_ident(iph, &rt->dst, NULL);
+                       ip_select_ident(skb, &rt->dst, NULL);
 
                iph->check = ip_fast_csum((unsigned char *)iph, iph->ihl);
        }
index eb1dd4d643f2f92d5a2385d0839d06a3424c545c..b5663c37f089ed0afe33115bcbfad2555b8d0f48 100644 (file)
@@ -117,7 +117,7 @@ static int xfrm4_mode_tunnel_output(struct xfrm_state *x, struct sk_buff *skb)
 
        top_iph->frag_off = (flags & XFRM_STATE_NOPMTUDISC) ?
                0 : (XFRM_MODE_SKB_CB(skb)->frag_off & htons(IP_DF));
-       ip_select_ident(top_iph, dst->child, NULL);
+       ip_select_ident(skb, dst->child, NULL);
 
        top_iph->ttl = ip4_dst_hoplimit(dst->child);
 
index b75ff6429a04e25d4ba8e6368173ab84a342751c..c47444e4cf8ccc9977fa0622689b4fb55799ff4b 100644 (file)
@@ -883,7 +883,7 @@ ip_vs_tunnel_xmit(struct sk_buff *skb, struct ip_vs_conn *cp,
        iph->daddr              =       cp->daddr.ip;
        iph->saddr              =       saddr;
        iph->ttl                =       old_iph->ttl;
-       ip_select_ident(iph, &rt->dst, NULL);
+       ip_select_ident(skb, &rt->dst, NULL);
 
        /* Another hack: avoid icmp_send in ip_fragment */
        skb->local_df = 1;