diff options
Diffstat (limited to 'net/ipv6/tcp_ipv6.c')
| -rw-r--r-- | net/ipv6/tcp_ipv6.c | 124 | 
1 files changed, 96 insertions, 28 deletions
diff --git a/net/ipv6/tcp_ipv6.c b/net/ipv6/tcp_ipv6.c index df5fd9109696..33a578a3eb3a 100644 --- a/net/ipv6/tcp_ipv6.c +++ b/net/ipv6/tcp_ipv6.c @@ -75,13 +75,14 @@ static void	tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,  static int	tcp_v6_do_rcv(struct sock *sk, struct sk_buff *skb);  static const struct inet_connection_sock_af_ops ipv6_mapped; -static const struct inet_connection_sock_af_ops ipv6_specific; +const struct inet_connection_sock_af_ops ipv6_specific;  #ifdef CONFIG_TCP_MD5SIG  static const struct tcp_sock_af_ops tcp_sock_ipv6_specific;  static const struct tcp_sock_af_ops tcp_sock_ipv6_mapped_specific;  #else  static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(const struct sock *sk, -						   const struct in6_addr *addr) +						   const struct in6_addr *addr, +						   int l3index)  {  	return NULL;  } @@ -237,6 +238,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,  		sin.sin_addr.s_addr = usin->sin6_addr.s6_addr32[3];  		icsk->icsk_af_ops = &ipv6_mapped; +		if (sk_is_mptcp(sk)) +			mptcp_handle_ipv6_mapped(sk, true);  		sk->sk_backlog_rcv = tcp_v4_do_rcv;  #ifdef CONFIG_TCP_MD5SIG  		tp->af_specific = &tcp_sock_ipv6_mapped_specific; @@ -247,6 +250,8 @@ static int tcp_v6_connect(struct sock *sk, struct sockaddr *uaddr,  		if (err) {  			icsk->icsk_ext_hdr_len = exthdrlen;  			icsk->icsk_af_ops = &ipv6_specific; +			if (sk_is_mptcp(sk)) +				mptcp_handle_ipv6_mapped(sk, false);  			sk->sk_backlog_rcv = tcp_v6_do_rcv;  #ifdef CONFIG_TCP_MD5SIG  			tp->af_specific = &tcp_sock_ipv6_specific; @@ -532,15 +537,22 @@ static void tcp_v6_reqsk_destructor(struct request_sock *req)  #ifdef CONFIG_TCP_MD5SIG  static struct tcp_md5sig_key *tcp_v6_md5_do_lookup(const struct sock *sk, -						   const struct in6_addr *addr) +						   const struct in6_addr *addr, +						   int l3index)  { -	return tcp_md5_do_lookup(sk, (union tcp_md5_addr *)addr, AF_INET6); +	return tcp_md5_do_lookup(sk, l3index, +				 (union tcp_md5_addr *)addr, AF_INET6);  }  static struct tcp_md5sig_key *tcp_v6_md5_lookup(const struct sock *sk,  						const struct sock *addr_sk)  { -	return tcp_v6_md5_do_lookup(sk, &addr_sk->sk_v6_daddr); +	int l3index; + +	l3index = l3mdev_master_ifindex_by_index(sock_net(sk), +						 addr_sk->sk_bound_dev_if); +	return tcp_v6_md5_do_lookup(sk, &addr_sk->sk_v6_daddr, +				    l3index);  }  static int tcp_v6_parse_md5_keys(struct sock *sk, int optname, @@ -548,6 +560,7 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname,  {  	struct tcp_md5sig cmd;  	struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&cmd.tcpm_addr; +	int l3index = 0;  	u8 prefixlen;  	if (optlen < sizeof(cmd)) @@ -569,12 +582,30 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname,  		prefixlen = ipv6_addr_v4mapped(&sin6->sin6_addr) ? 32 : 128;  	} +	if (optname == TCP_MD5SIG_EXT && +	    cmd.tcpm_flags & TCP_MD5SIG_FLAG_IFINDEX) { +		struct net_device *dev; + +		rcu_read_lock(); +		dev = dev_get_by_index_rcu(sock_net(sk), cmd.tcpm_ifindex); +		if (dev && netif_is_l3_master(dev)) +			l3index = dev->ifindex; +		rcu_read_unlock(); + +		/* ok to reference set/not set outside of rcu; +		 * right now device MUST be an L3 master +		 */ +		if (!dev || !l3index) +			return -EINVAL; +	} +  	if (!cmd.tcpm_keylen) {  		if (ipv6_addr_v4mapped(&sin6->sin6_addr))  			return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3], -					      AF_INET, prefixlen); +					      AF_INET, prefixlen, +					      l3index);  		return tcp_md5_do_del(sk, (union tcp_md5_addr *)&sin6->sin6_addr, -				      AF_INET6, prefixlen); +				      AF_INET6, prefixlen, l3index);  	}  	if (cmd.tcpm_keylen > TCP_MD5SIG_MAXKEYLEN) @@ -582,12 +613,13 @@ static int tcp_v6_parse_md5_keys(struct sock *sk, int optname,  	if (ipv6_addr_v4mapped(&sin6->sin6_addr))  		return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr.s6_addr32[3], -				      AF_INET, prefixlen, cmd.tcpm_key, -				      cmd.tcpm_keylen, GFP_KERNEL); +				      AF_INET, prefixlen, l3index, +				      cmd.tcpm_key, cmd.tcpm_keylen, +				      GFP_KERNEL);  	return tcp_md5_do_add(sk, (union tcp_md5_addr *)&sin6->sin6_addr, -			      AF_INET6, prefixlen, cmd.tcpm_key, -			      cmd.tcpm_keylen, GFP_KERNEL); +			      AF_INET6, prefixlen, l3index, +			      cmd.tcpm_key, cmd.tcpm_keylen, GFP_KERNEL);  }  static int tcp_v6_md5_hash_headers(struct tcp_md5sig_pool *hp, @@ -698,17 +730,23 @@ clear_hash_noput:  #endif  static bool tcp_v6_inbound_md5_hash(const struct sock *sk, -				    const struct sk_buff *skb) +				    const struct sk_buff *skb, +				    int dif, int sdif)  {  #ifdef CONFIG_TCP_MD5SIG  	const __u8 *hash_location = NULL;  	struct tcp_md5sig_key *hash_expected;  	const struct ipv6hdr *ip6h = ipv6_hdr(skb);  	const struct tcphdr *th = tcp_hdr(skb); -	int genhash; +	int genhash, l3index;  	u8 newhash[16]; -	hash_expected = tcp_v6_md5_do_lookup(sk, &ip6h->saddr); +	/* sdif set, means packet ingressed via a device +	 * in an L3 domain and dif is set to the l3mdev +	 */ +	l3index = sdif ? dif : 0; + +	hash_expected = tcp_v6_md5_do_lookup(sk, &ip6h->saddr, l3index);  	hash_location = tcp_parse_md5sig_option(th);  	/* We've parsed the options - do we have a hash? */ @@ -732,10 +770,10 @@ static bool tcp_v6_inbound_md5_hash(const struct sock *sk,  	if (genhash || memcmp(hash_location, newhash, 16) != 0) {  		NET_INC_STATS(sock_net(sk), LINUX_MIB_TCPMD5FAILURE); -		net_info_ratelimited("MD5 Hash %s for [%pI6c]:%u->[%pI6c]:%u\n", +		net_info_ratelimited("MD5 Hash %s for [%pI6c]:%u->[%pI6c]:%u L3 index %d\n",  				     genhash ? "failed" : "mismatch",  				     &ip6h->saddr, ntohs(th->source), -				     &ip6h->daddr, ntohs(th->dest)); +				     &ip6h->daddr, ntohs(th->dest), l3index);  		return true;  	}  #endif @@ -785,7 +823,7 @@ struct request_sock_ops tcp6_request_sock_ops __read_mostly = {  	.syn_ack_timeout =	tcp_syn_ack_timeout,  }; -static const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = { +const struct tcp_request_sock_ops tcp_request_sock_ipv6_ops = {  	.mss_clamp	=	IPV6_MIN_MTU - sizeof(struct tcphdr) -  				sizeof(struct ipv6hdr),  #ifdef CONFIG_TCP_MD5SIG @@ -951,8 +989,18 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)  	rcu_read_lock();  	hash_location = tcp_parse_md5sig_option(th);  	if (sk && sk_fullsock(sk)) { -		key = tcp_v6_md5_do_lookup(sk, &ipv6h->saddr); +		int l3index; + +		/* sdif set, means packet ingressed via a device +		 * in an L3 domain and inet_iif is set to it. +		 */ +		l3index = tcp_v6_sdif(skb) ? tcp_v6_iif_l3_slave(skb) : 0; +		key = tcp_v6_md5_do_lookup(sk, &ipv6h->saddr, l3index);  	} else if (hash_location) { +		int dif = tcp_v6_iif_l3_slave(skb); +		int sdif = tcp_v6_sdif(skb); +		int l3index; +  		/*  		 * active side is lost. Try to find listening socket through  		 * source port, and then find md5 key through listening socket. @@ -964,13 +1012,16 @@ static void tcp_v6_send_reset(const struct sock *sk, struct sk_buff *skb)  					   &tcp_hashinfo, NULL, 0,  					   &ipv6h->saddr,  					   th->source, &ipv6h->daddr, -					   ntohs(th->source), -					   tcp_v6_iif_l3_slave(skb), -					   tcp_v6_sdif(skb)); +					   ntohs(th->source), dif, sdif);  		if (!sk1)  			goto out; -		key = tcp_v6_md5_do_lookup(sk1, &ipv6h->saddr); +		/* sdif set, means packet ingressed via a device +		 * in an L3 domain and dif is set to it. +		 */ +		l3index = tcp_v6_sdif(skb) ? dif : 0; + +		key = tcp_v6_md5_do_lookup(sk1, &ipv6h->saddr, l3index);  		if (!key)  			goto out; @@ -1040,6 +1091,10 @@ static void tcp_v6_timewait_ack(struct sock *sk, struct sk_buff *skb)  static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,  				  struct request_sock *req)  { +	int l3index; + +	l3index = tcp_v6_sdif(skb) ? tcp_v6_iif_l3_slave(skb) : 0; +  	/* sk->sk_state == TCP_LISTEN -> for regular TCP_SYN_RECV  	 * sk->sk_state == TCP_SYN_RECV -> for Fast Open.  	 */ @@ -1054,7 +1109,7 @@ static void tcp_v6_reqsk_send_ack(const struct sock *sk, struct sk_buff *skb,  			req->rsk_rcv_wnd >> inet_rsk(req)->rcv_wscale,  			tcp_time_stamp_raw() + tcp_rsk(req)->ts_off,  			req->ts_recent, sk->sk_bound_dev_if, -			tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr), +			tcp_v6_md5_do_lookup(sk, &ipv6_hdr(skb)->saddr, l3index),  			0, 0, sk->sk_priority);  } @@ -1126,6 +1181,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *  	struct sock *newsk;  #ifdef CONFIG_TCP_MD5SIG  	struct tcp_md5sig_key *key; +	int l3index;  #endif  	struct flowi6 fl6; @@ -1151,6 +1207,8 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *  		newnp->saddr = newsk->sk_v6_rcv_saddr;  		inet_csk(newsk)->icsk_af_ops = &ipv6_mapped; +		if (sk_is_mptcp(newsk)) +			mptcp_handle_ipv6_mapped(newsk, true);  		newsk->sk_backlog_rcv = tcp_v4_do_rcv;  #ifdef CONFIG_TCP_MD5SIG  		newtp->af_specific = &tcp_sock_ipv6_mapped_specific; @@ -1269,8 +1327,10 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *  	newinet->inet_rcv_saddr = LOOPBACK4_IPV6;  #ifdef CONFIG_TCP_MD5SIG +	l3index = l3mdev_master_ifindex_by_index(sock_net(sk), ireq->ir_iif); +  	/* Copy over the MD5 key from the original socket */ -	key = tcp_v6_md5_do_lookup(sk, &newsk->sk_v6_daddr); +	key = tcp_v6_md5_do_lookup(sk, &newsk->sk_v6_daddr, l3index);  	if (key) {  		/* We're using one, so create a matching key  		 * on the newsk structure. If we fail to get @@ -1278,7 +1338,7 @@ static struct sock *tcp_v6_syn_recv_sock(const struct sock *sk, struct sk_buff *  		 * across. Shucks.  		 */  		tcp_md5_do_add(newsk, (union tcp_md5_addr *)&newsk->sk_v6_daddr, -			       AF_INET6, 128, key->key, key->keylen, +			       AF_INET6, 128, l3index, key->key, key->keylen,  			       sk_gfp_mask(sk, GFP_ATOMIC));  	}  #endif @@ -1480,6 +1540,7 @@ INDIRECT_CALLABLE_SCOPE int tcp_v6_rcv(struct sk_buff *skb)  {  	struct sk_buff *skb_to_free;  	int sdif = inet6_sdif(skb); +	int dif = inet6_iif(skb);  	const struct tcphdr *th;  	const struct ipv6hdr *hdr;  	bool refcounted; @@ -1528,7 +1589,7 @@ process:  		struct sock *nsk;  		sk = req->rsk_listener; -		if (tcp_v6_inbound_md5_hash(sk, skb)) { +		if (tcp_v6_inbound_md5_hash(sk, skb, dif, sdif)) {  			sk_drops_add(sk, skb);  			reqsk_put(req);  			goto discard_it; @@ -1583,7 +1644,7 @@ process:  	if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))  		goto discard_and_relse; -	if (tcp_v6_inbound_md5_hash(sk, skb)) +	if (tcp_v6_inbound_md5_hash(sk, skb, dif, sdif))  		goto discard_and_relse;  	if (tcp_filter(sk, skb)) @@ -1739,7 +1800,7 @@ static struct timewait_sock_ops tcp6_timewait_sock_ops = {  	.twsk_destructor = tcp_twsk_destructor,  }; -static const struct inet_connection_sock_af_ops ipv6_specific = { +const struct inet_connection_sock_af_ops ipv6_specific = {  	.queue_xmit	   = inet6_csk_xmit,  	.send_check	   = tcp_v6_send_check,  	.rebuild_header	   = inet6_sk_rebuild_header, @@ -2108,9 +2169,16 @@ int __init tcpv6_init(void)  	ret = register_pernet_subsys(&tcpv6_net_ops);  	if (ret)  		goto out_tcpv6_protosw; + +	ret = mptcpv6_init(); +	if (ret) +		goto out_tcpv6_pernet_subsys; +  out:  	return ret; +out_tcpv6_pernet_subsys: +	unregister_pernet_subsys(&tcpv6_net_ops);  out_tcpv6_protosw:  	inet6_unregister_protosw(&tcpv6_protosw);  out_tcpv6_protocol:  | 
