VYPR
High severity7.5NVD Advisory· Published May 24, 2012· Updated Apr 29, 2026

CVE-2011-2699

CVE-2011-2699

Description

The IPv6 implementation in the Linux kernel before 3.1 does not generate Fragment Identification values separately for each destination, which makes it easier for remote attackers to cause a denial of service (disrupted networking) by predicting these values and sending crafted packets.

Affected products

3

Patches

1
87c48fa3b463

ipv6: make fragment identifications less predictable

https://github.com/torvalds/linuxEric DumazetJul 22, 2011via nvd-ref
7 files changed · +64 22
  • drivers/char/random.c+15 0 modified
    @@ -1523,6 +1523,21 @@ __u32 secure_ip_id(__be32 daddr)
     	return half_md4_transform(hash, keyptr->secret);
     }
     
    +__u32 secure_ipv6_id(const __be32 daddr[4])
    +{
    +	const struct keydata *keyptr;
    +	__u32 hash[4];
    +
    +	keyptr = get_keyptr();
    +
    +	hash[0] = (__force __u32)daddr[0];
    +	hash[1] = (__force __u32)daddr[1];
    +	hash[2] = (__force __u32)daddr[2];
    +	hash[3] = (__force __u32)daddr[3];
    +
    +	return half_md4_transform(hash, keyptr->secret);
    +}
    +
     #ifdef CONFIG_INET
     
     __u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr,
    
  • include/linux/random.h+1 0 modified
    @@ -58,6 +58,7 @@ extern void get_random_bytes(void *buf, int nbytes);
     void generate_random_uuid(unsigned char uuid_out[16]);
     
     extern __u32 secure_ip_id(__be32 daddr);
    +extern __u32 secure_ipv6_id(const __be32 daddr[4]);
     extern u32 secure_ipv4_port_ephemeral(__be32 saddr, __be32 daddr, __be16 dport);
     extern u32 secure_ipv6_port_ephemeral(const __be32 *saddr, const __be32 *daddr,
     				      __be16 dport);
    
  • include/net/inetpeer.h+10 3 modified
    @@ -71,7 +71,7 @@ static inline bool inet_metrics_new(const struct inet_peer *p)
     }
     
     /* can be called with or without local BH being disabled */
    -struct inet_peer	*inet_getpeer(struct inetpeer_addr *daddr, int create);
    +struct inet_peer	*inet_getpeer(const struct inetpeer_addr *daddr, int create);
     
     static inline struct inet_peer *inet_getpeer_v4(__be32 v4daddr, int create)
     {
    @@ -106,11 +106,18 @@ static inline void inet_peer_refcheck(const struct inet_peer *p)
     
     
     /* can be called with or without local BH being disabled */
    -static inline __u16	inet_getid(struct inet_peer *p, int more)
    +static inline int inet_getid(struct inet_peer *p, int more)
     {
    +	int old, new;
     	more++;
     	inet_peer_refcheck(p);
    -	return atomic_add_return(more, &p->ip_id_count) - more;
    +	do {
    +		old = atomic_read(&p->ip_id_count);
    +		new = old + more;
    +		if (!new)
    +			new = 1;
    +	} while (atomic_cmpxchg(&p->ip_id_count, old, new) != old);
    +	return new;
     }
     
     #endif /* _NET_INETPEER_H */
    
  • include/net/ipv6.h+1 11 modified
    @@ -463,17 +463,7 @@ static inline int ipv6_addr_diff(const struct in6_addr *a1, const struct in6_add
     	return __ipv6_addr_diff(a1, a2, sizeof(struct in6_addr));
     }
     
    -static __inline__ void ipv6_select_ident(struct frag_hdr *fhdr)
    -{
    -	static u32 ipv6_fragmentation_id = 1;
    -	static DEFINE_SPINLOCK(ip6_id_lock);
    -
    -	spin_lock_bh(&ip6_id_lock);
    -	fhdr->identification = htonl(ipv6_fragmentation_id);
    -	if (++ipv6_fragmentation_id == 0)
    -		ipv6_fragmentation_id = 1;
    -	spin_unlock_bh(&ip6_id_lock);
    -}
    +extern void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt);
     
     /*
      *	Prototypes exported by ipv6
    
  • net/ipv4/inetpeer.c+5 2 modified
    @@ -391,7 +391,7 @@ static int inet_peer_gc(struct inet_peer_base *base,
     	return cnt;
     }
     
    -struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create)
    +struct inet_peer *inet_getpeer(const struct inetpeer_addr *daddr, int create)
     {
     	struct inet_peer __rcu **stack[PEER_MAXDEPTH], ***stackptr;
     	struct inet_peer_base *base = family_to_base(daddr->family);
    @@ -436,7 +436,10 @@ struct inet_peer *inet_getpeer(struct inetpeer_addr *daddr, int create)
     		p->daddr = *daddr;
     		atomic_set(&p->refcnt, 1);
     		atomic_set(&p->rid, 0);
    -		atomic_set(&p->ip_id_count, secure_ip_id(daddr->addr.a4));
    +		atomic_set(&p->ip_id_count,
    +				(daddr->family == AF_INET) ?
    +					secure_ip_id(daddr->addr.a4) :
    +					secure_ipv6_id(daddr->addr.a6));
     		p->tcp_ts_stamp = 0;
     		p->metrics[RTAX_LOCK-1] = INETPEER_METRICS_NEW;
     		p->rate_tokens = 0;
    
  • net/ipv6/ip6_output.c+31 5 modified
    @@ -596,6 +596,31 @@ int ip6_find_1stfragopt(struct sk_buff *skb, u8 **nexthdr)
     	return offset;
     }
     
    +void ipv6_select_ident(struct frag_hdr *fhdr, struct rt6_info *rt)
    +{
    +	static atomic_t ipv6_fragmentation_id;
    +	int old, new;
    +
    +	if (rt) {
    +		struct inet_peer *peer;
    +
    +		if (!rt->rt6i_peer)
    +			rt6_bind_peer(rt, 1);
    +		peer = rt->rt6i_peer;
    +		if (peer) {
    +			fhdr->identification = htonl(inet_getid(peer, 0));
    +			return;
    +		}
    +	}
    +	do {
    +		old = atomic_read(&ipv6_fragmentation_id);
    +		new = old + 1;
    +		if (!new)
    +			new = 1;
    +	} while (atomic_cmpxchg(&ipv6_fragmentation_id, old, new) != old);
    +	fhdr->identification = htonl(new);
    +}
    +
     int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
     {
     	struct sk_buff *frag;
    @@ -680,7 +705,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
     		skb_reset_network_header(skb);
     		memcpy(skb_network_header(skb), tmp_hdr, hlen);
     
    -		ipv6_select_ident(fh);
    +		ipv6_select_ident(fh, rt);
     		fh->nexthdr = nexthdr;
     		fh->reserved = 0;
     		fh->frag_off = htons(IP6_MF);
    @@ -826,7 +851,7 @@ int ip6_fragment(struct sk_buff *skb, int (*output)(struct sk_buff *))
     		fh->nexthdr = nexthdr;
     		fh->reserved = 0;
     		if (!frag_id) {
    -			ipv6_select_ident(fh);
    +			ipv6_select_ident(fh, rt);
     			frag_id = fh->identification;
     		} else
     			fh->identification = frag_id;
    @@ -1076,7 +1101,8 @@ static inline int ip6_ufo_append_data(struct sock *sk,
     			int getfrag(void *from, char *to, int offset, int len,
     			int odd, struct sk_buff *skb),
     			void *from, int length, int hh_len, int fragheaderlen,
    -			int transhdrlen, int mtu,unsigned int flags)
    +			int transhdrlen, int mtu,unsigned int flags,
    +			struct rt6_info *rt)
     
     {
     	struct sk_buff *skb;
    @@ -1120,7 +1146,7 @@ static inline int ip6_ufo_append_data(struct sock *sk,
     		skb_shinfo(skb)->gso_size = (mtu - fragheaderlen -
     					     sizeof(struct frag_hdr)) & ~7;
     		skb_shinfo(skb)->gso_type = SKB_GSO_UDP;
    -		ipv6_select_ident(&fhdr);
    +		ipv6_select_ident(&fhdr, rt);
     		skb_shinfo(skb)->ip6_frag_id = fhdr.identification;
     		__skb_queue_tail(&sk->sk_write_queue, skb);
     
    @@ -1286,7 +1312,7 @@ int ip6_append_data(struct sock *sk, int getfrag(void *from, char *to,
     
     			err = ip6_ufo_append_data(sk, getfrag, from, length,
     						  hh_len, fragheaderlen,
    -						  transhdrlen, mtu, flags);
    +						  transhdrlen, mtu, flags, rt);
     			if (err)
     				goto error;
     			return 0;
    
  • net/ipv6/udp.c+1 1 modified
    @@ -1359,7 +1359,7 @@ static struct sk_buff *udp6_ufo_fragment(struct sk_buff *skb, u32 features)
     	fptr = (struct frag_hdr *)(skb_network_header(skb) + unfrag_ip6hlen);
     	fptr->nexthdr = nexthdr;
     	fptr->reserved = 0;
    -	ipv6_select_ident(fptr);
    +	ipv6_select_ident(fptr, (struct rt6_info *)skb_dst(skb));
     
     	/* Fragment the skb. ipv6 header and the remaining fields of the
     	 * fragment header are updated in ipv6_gso_segment()
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

6

News mentions

0

No linked articles in our index yet.