All of lore.kernel.org
 help / color / mirror / Atom feed
From: Matt Mackall <mpm@selenic.com>
To: Eric Dumazet <eric.dumazet@gmail.com>
Cc: Fernando Gont <fernando@gont.com.ar>,
	David Miller <davem@davemloft.net>,
	security@kernel.org, Eugene Teo <eugeneteo@kernel.sg>,
	netdev <netdev@vger.kernel.org>
Subject: Re: [PATCH net-next-2.6] ipv6: make fragment identifications less predictable
Date: Tue, 19 Jul 2011 15:56:59 -0500	[thread overview]
Message-ID: <1311109019.14555.11.camel@calx> (raw)
In-Reply-To: <1311108423.3113.24.camel@edumazet-laptop>

On Tue, 2011-07-19 at 22:47 +0200, Eric Dumazet wrote:
> IPv6 fragment identification generation is way beyond what we use for
> IPv4 : It uses a single generator. Its not scalable and allows DOS
> attacks.
> 
> Now inetpeer is IPv6 aware, we can use it to provide a more secure and
> scalable frag ident generator (per destination, instead of system wide)

This code really needs to get moved out of random.c and into net/. Other
than that, looks fine to me.

> This patch :
> 1) defines a new secure_ipv6_id() helper
> 2) extends inet_getid() to provide 32bit results
> 3) extends ipv6_select_ident() with a new dest parameter
> 
> Reported-by: Fernando Gont <fernando@gont.com.ar>
> CC: Matt Mackall <mpm@selenic.com>
> CC: Eugene Teo <eugeneteo@kernel.sg>
> Signed-off-by: Eric Dumazet <eric.dumazet@gmail.com>
> ---
>  drivers/char/random.c  |   15 +++++++++++++++
>  include/linux/random.h |    1 +
>  include/net/inetpeer.h |   13 ++++++++++---
>  include/net/ipv6.h     |   12 +-----------
>  net/ipv4/inetpeer.c    |    7 +++++--
>  net/ipv6/ip6_output.c  |   36 +++++++++++++++++++++++++++++++-----
>  net/ipv6/udp.c         |    2 +-
>  7 files changed, 64 insertions(+), 22 deletions(-)
> 
> diff --git a/drivers/char/random.c b/drivers/char/random.c
> index d4ddeba..7292819 100644
> --- a/drivers/char/random.c
> +++ b/drivers/char/random.c
> @@ -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,
> diff --git a/include/linux/random.h b/include/linux/random.h
> index fb7ab9d..ce29a04 100644
> --- a/include/linux/random.h
> +++ b/include/linux/random.h
> @@ -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);
> diff --git a/include/net/inetpeer.h b/include/net/inetpeer.h
> index 39d1230..4233e6f 100644
> --- a/include/net/inetpeer.h
> +++ b/include/net/inetpeer.h
> @@ -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 */
> diff --git a/include/net/ipv6.h b/include/net/ipv6.h
> index c033ed0..3b5ac1f 100644
> --- a/include/net/ipv6.h
> +++ b/include/net/ipv6.h
> @@ -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
> diff --git a/net/ipv4/inetpeer.c b/net/ipv4/inetpeer.c
> index 90c5f0d..e382138 100644
> --- a/net/ipv4/inetpeer.c
> +++ b/net/ipv4/inetpeer.c
> @@ -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 @@ relookup:
>  		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;
> diff --git a/net/ipv6/ip6_output.c b/net/ipv6/ip6_output.c
> index 8db0e48..32e5339 100644
> --- a/net/ipv6/ip6_output.c
> +++ b/net/ipv6/ip6_output.c
> @@ -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 @@ slow_path:
>  		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;
> diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c
> index 328985c..29213b5 100644
> --- a/net/ipv6/udp.c
> +++ b/net/ipv6/udp.c
> @@ -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()
> 


-- 
Mathematics is the supreme nostalgia of our time.



  reply	other threads:[~2011-07-19 20:57 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <4E24BE94.7010301@gont.com.ar>
     [not found] ` <1311082696.2375.26.camel@edumazet-HP-Compaq-6005-Pro-SFF-PC>
     [not found]   ` <1311089463.2375.42.camel@edumazet-HP-Compaq-6005-Pro-SFF-PC>
2011-07-19 20:47     ` [PATCH net-next-2.6] ipv6: make fragment identifications less predictable Eric Dumazet
2011-07-19 20:56       ` Matt Mackall [this message]
2011-07-20  6:50         ` Eric Dumazet
2011-07-20  8:25       ` Eric Dumazet
2011-07-20 10:27         ` Eric Dumazet
2011-07-21  1:32           ` Fernando Gont
2011-07-21 22:17             ` David Miller
2011-07-21 22:46               ` Rick Jones
2011-07-21 23:13                 ` David Miller
2011-07-21 23:37               ` Fernando Gont
2011-07-22  0:07                 ` David Miller
2011-07-22  0:34                 ` Rick Jones
2011-07-22  1:18                   ` Fernando Gont
2011-07-22  4:26       ` David Miller

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1311109019.14555.11.camel@calx \
    --to=mpm@selenic.com \
    --cc=davem@davemloft.net \
    --cc=eric.dumazet@gmail.com \
    --cc=eugeneteo@kernel.sg \
    --cc=fernando@gont.com.ar \
    --cc=netdev@vger.kernel.org \
    --cc=security@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.