Skip to content

Commit

Permalink
ip: generate unique IP identificator if local fragmentation is allowed
Browse files Browse the repository at this point in the history
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>
  • Loading branch information
Ansis Atteka authored and David S. Miller committed Sep 19, 2013
1 parent 749154a commit 703133d
Show file tree
Hide file tree
Showing 9 changed files with 20 additions and 16 deletions.
2 changes: 1 addition & 1 deletion drivers/net/ppp/pptp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
12 changes: 8 additions & 4 deletions include/net/ip.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions net/ipv4/igmp.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
4 changes: 2 additions & 2 deletions net/ipv4/inetpeer.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions net/ipv4/ip_output.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -386,7 +386,7 @@ int ip_queue_xmit(struct sk_buff *skb, struct flowi *fl)
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;
Expand Down Expand Up @@ -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;
Expand Down
2 changes: 1 addition & 1 deletion net/ipv4/ipmr.c
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
2 changes: 1 addition & 1 deletion net/ipv4/raw.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand Down
2 changes: 1 addition & 1 deletion net/ipv4/xfrm4_mode_tunnel.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);

Expand Down
2 changes: 1 addition & 1 deletion net/netfilter/ipvs/ip_vs_xmit.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down

0 comments on commit 703133d

Please sign in to comment.