diff options
Diffstat (limited to 'net/l2tp/l2tp_core.c')
| -rw-r--r-- | net/l2tp/l2tp_core.c | 190 | 
1 files changed, 48 insertions, 142 deletions
diff --git a/net/l2tp/l2tp_core.c b/net/l2tp/l2tp_core.c index feae495a0a3..bea25904320 100644 --- a/net/l2tp/l2tp_core.c +++ b/net/l2tp/l2tp_core.c @@ -112,9 +112,13 @@ struct l2tp_net {  	spinlock_t l2tp_session_hlist_lock;  }; -static void l2tp_session_set_header_len(struct l2tp_session *session, int version);  static void l2tp_tunnel_free(struct l2tp_tunnel *tunnel); +static inline struct l2tp_tunnel *l2tp_tunnel(struct sock *sk) +{ +	return sk->sk_user_data; +} +  static inline struct l2tp_net *l2tp_pernet(struct net *net)  {  	BUG_ON(!net); @@ -171,7 +175,7 @@ l2tp_session_id_hash_2(struct l2tp_net *pn, u32 session_id)   * owned by userspace.  A struct sock returned from this function must be   * released using l2tp_tunnel_sock_put once you're done with it.   */ -struct sock *l2tp_tunnel_sock_lookup(struct l2tp_tunnel *tunnel) +static struct sock *l2tp_tunnel_sock_lookup(struct l2tp_tunnel *tunnel)  {  	int err = 0;  	struct socket *sock = NULL; @@ -197,10 +201,9 @@ struct sock *l2tp_tunnel_sock_lookup(struct l2tp_tunnel *tunnel)  out:  	return sk;  } -EXPORT_SYMBOL_GPL(l2tp_tunnel_sock_lookup);  /* Drop a reference to a tunnel socket obtained via. l2tp_tunnel_sock_put */ -void l2tp_tunnel_sock_put(struct sock *sk) +static void l2tp_tunnel_sock_put(struct sock *sk)  {  	struct l2tp_tunnel *tunnel = l2tp_sock_to_tunnel(sk);  	if (tunnel) { @@ -212,7 +215,6 @@ void l2tp_tunnel_sock_put(struct sock *sk)  	}  	sock_put(sk);  } -EXPORT_SYMBOL_GPL(l2tp_tunnel_sock_put);  /* Lookup a session by id in the global session list   */ @@ -493,52 +495,6 @@ out:  	spin_unlock_bh(&session->reorder_q.lock);  } -static inline int l2tp_verify_udp_checksum(struct sock *sk, -					   struct sk_buff *skb) -{ -	struct udphdr *uh = udp_hdr(skb); -	u16 ulen = ntohs(uh->len); -	__wsum psum; - -	if (sk->sk_no_check || skb_csum_unnecessary(skb)) -		return 0; - -#if IS_ENABLED(CONFIG_IPV6) -	if (sk->sk_family == PF_INET6) { -		if (!uh->check) { -			LIMIT_NETDEBUG(KERN_INFO "L2TP: IPv6: checksum is 0\n"); -			return 1; -		} -		if ((skb->ip_summed == CHECKSUM_COMPLETE) && -		    !csum_ipv6_magic(&ipv6_hdr(skb)->saddr, -				     &ipv6_hdr(skb)->daddr, ulen, -				     IPPROTO_UDP, skb->csum)) { -			skb->ip_summed = CHECKSUM_UNNECESSARY; -			return 0; -		} -		skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr, -							 &ipv6_hdr(skb)->daddr, -							 skb->len, IPPROTO_UDP, -							 0)); -	} else -#endif -	{ -		struct inet_sock *inet; -		if (!uh->check) -			return 0; -		inet = inet_sk(sk); -		psum = csum_tcpudp_nofold(inet->inet_saddr, inet->inet_daddr, -					  ulen, IPPROTO_UDP, 0); - -		if ((skb->ip_summed == CHECKSUM_COMPLETE) && -		    !csum_fold(csum_add(psum, skb->csum))) -			return 0; -		skb->csum = psum; -	} - -	return __skb_checksum_complete(skb); -} -  static int l2tp_seq_check_rx_window(struct l2tp_session *session, u32 nr)  {  	u32 nws; @@ -893,8 +849,7 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,  	u16 version;  	int length; -	if (tunnel->sock && l2tp_verify_udp_checksum(tunnel->sock, skb)) -		goto discard_bad_csum; +	/* UDP has verifed checksum */  	/* UDP always verifies the packet length. */  	__skb_pull(skb, sizeof(struct udphdr)); @@ -977,14 +932,6 @@ static int l2tp_udp_recv_core(struct l2tp_tunnel *tunnel, struct sk_buff *skb,  	return 0; -discard_bad_csum: -	LIMIT_NETDEBUG("%s: UDP: bad checksum\n", tunnel->name); -	UDP_INC_STATS_USER(tunnel->l2tp_net, UDP_MIB_INERRORS, 0); -	atomic_long_inc(&tunnel->stats.rx_errors); -	kfree_skb(skb); - -	return 0; -  error:  	/* Put UDP header back */  	__skb_push(skb, sizeof(struct udphdr)); @@ -1126,13 +1073,13 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,  	}  	/* Queue the packet to IP for output */ -	skb->local_df = 1; +	skb->ignore_df = 1;  #if IS_ENABLED(CONFIG_IPV6) -	if (skb->sk->sk_family == PF_INET6) -		error = inet6_csk_xmit(skb, NULL); +	if (tunnel->sock->sk_family == PF_INET6 && !tunnel->v4mapped) +		error = inet6_csk_xmit(tunnel->sock, skb, NULL);  	else  #endif -		error = ip_queue_xmit(skb, fl); +		error = ip_queue_xmit(tunnel->sock, skb, fl);  	/* Update stats */  	if (error >= 0) { @@ -1148,48 +1095,6 @@ static int l2tp_xmit_core(struct l2tp_session *session, struct sk_buff *skb,  	return 0;  } -/* Automatically called when the skb is freed. - */ -static void l2tp_sock_wfree(struct sk_buff *skb) -{ -	sock_put(skb->sk); -} - -/* For data skbs that we transmit, we associate with the tunnel socket - * but don't do accounting. - */ -static inline void l2tp_skb_set_owner_w(struct sk_buff *skb, struct sock *sk) -{ -	sock_hold(sk); -	skb->sk = sk; -	skb->destructor = l2tp_sock_wfree; -} - -#if IS_ENABLED(CONFIG_IPV6) -static void l2tp_xmit_ipv6_csum(struct sock *sk, struct sk_buff *skb, -				int udp_len) -{ -	struct ipv6_pinfo *np = inet6_sk(sk); -	struct udphdr *uh = udp_hdr(skb); - -	if (!skb_dst(skb) || !skb_dst(skb)->dev || -	    !(skb_dst(skb)->dev->features & NETIF_F_IPV6_CSUM)) { -		__wsum csum = skb_checksum(skb, 0, udp_len, 0); -		skb->ip_summed = CHECKSUM_UNNECESSARY; -		uh->check = csum_ipv6_magic(&np->saddr, &np->daddr, udp_len, -					    IPPROTO_UDP, csum); -		if (uh->check == 0) -			uh->check = CSUM_MANGLED_0; -	} else { -		skb->ip_summed = CHECKSUM_PARTIAL; -		skb->csum_start = skb_transport_header(skb) - skb->head; -		skb->csum_offset = offsetof(struct udphdr, check); -		uh->check = ~csum_ipv6_magic(&np->saddr, &np->daddr, -					     udp_len, IPPROTO_UDP, 0); -	} -} -#endif -  /* If caller requires the skb to have a ppp header, the header must be   * inserted in the skb data before calling this function.   */ @@ -1201,7 +1106,6 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len  	struct flowi *fl;  	struct udphdr *uh;  	struct inet_sock *inet; -	__wsum csum;  	int headroom;  	int uhlen = (tunnel->encap == L2TP_ENCAPTYPE_UDP) ? sizeof(struct udphdr) : 0;  	int udp_len; @@ -1218,7 +1122,6 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len  		return NET_XMIT_DROP;  	} -	skb_orphan(skb);  	/* Setup L2TP header */  	session->build_header(session, __skb_push(skb, hdr_len)); @@ -1251,41 +1154,23 @@ int l2tp_xmit_skb(struct l2tp_session *session, struct sk_buff *skb, int hdr_len  		uh->dest = inet->inet_dport;  		udp_len = uhlen + hdr_len + data_len;  		uh->len = htons(udp_len); -		uh->check = 0;  		/* Calculate UDP checksum if configured to do so */  #if IS_ENABLED(CONFIG_IPV6) -		if (sk->sk_family == PF_INET6) -			l2tp_xmit_ipv6_csum(sk, skb, udp_len); +		if (sk->sk_family == PF_INET6 && !tunnel->v4mapped) +			udp6_set_csum(udp_get_no_check6_tx(sk), +				      skb, &inet6_sk(sk)->saddr, +				      &sk->sk_v6_daddr, udp_len);  		else  #endif -		if (sk->sk_no_check == UDP_CSUM_NOXMIT) -			skb->ip_summed = CHECKSUM_NONE; -		else if ((skb_dst(skb) && skb_dst(skb)->dev) && -			 (!(skb_dst(skb)->dev->features & NETIF_F_V4_CSUM))) { -			skb->ip_summed = CHECKSUM_COMPLETE; -			csum = skb_checksum(skb, 0, udp_len, 0); -			uh->check = csum_tcpudp_magic(inet->inet_saddr, -						      inet->inet_daddr, -						      udp_len, IPPROTO_UDP, csum); -			if (uh->check == 0) -				uh->check = CSUM_MANGLED_0; -		} else { -			skb->ip_summed = CHECKSUM_PARTIAL; -			skb->csum_start = skb_transport_header(skb) - skb->head; -			skb->csum_offset = offsetof(struct udphdr, check); -			uh->check = ~csum_tcpudp_magic(inet->inet_saddr, -						       inet->inet_daddr, -						       udp_len, IPPROTO_UDP, 0); -		} +		udp_set_csum(sk->sk_no_check_tx, skb, inet->inet_saddr, +			     inet->inet_daddr, udp_len);  		break;  	case L2TP_ENCAPTYPE_IP:  		break;  	} -	l2tp_skb_set_owner_w(skb, sk); -  	l2tp_xmit_core(session, skb, fl, data_len);  out_unlock:  	bh_unlock_sock(sk); @@ -1304,10 +1189,9 @@ EXPORT_SYMBOL_GPL(l2tp_xmit_skb);   */  static void l2tp_tunnel_destruct(struct sock *sk)  { -	struct l2tp_tunnel *tunnel; +	struct l2tp_tunnel *tunnel = l2tp_tunnel(sk);  	struct l2tp_net *pn; -	tunnel = sk->sk_user_data;  	if (tunnel == NULL)  		goto end; @@ -1509,6 +1393,11 @@ static int l2tp_tunnel_sock_create(struct net *net,  					     sizeof(udp6_addr), 0);  			if (err < 0)  				goto out; + +			if (cfg->udp6_zero_tx_checksums) +				udp_set_no_check6_tx(sock->sk, true); +			if (cfg->udp6_zero_rx_checksums) +				udp_set_no_check6_rx(sock->sk, true);  		} else  #endif  		{ @@ -1537,7 +1426,7 @@ static int l2tp_tunnel_sock_create(struct net *net,  		}  		if (!cfg->use_udp_checksums) -			sock->sk->sk_no_check = UDP_CSUM_NOXMIT; +			sock->sk->sk_no_check_tx = 1;  		break; @@ -1675,7 +1564,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32  	}  	/* Check if this socket has already been prepped */ -	tunnel = (struct l2tp_tunnel *)sk->sk_user_data; +	tunnel = l2tp_tunnel(sk);  	if (tunnel != NULL) {  		/* This socket has already been prepped */  		err = -EBUSY; @@ -1704,6 +1593,24 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32  	if (cfg != NULL)  		tunnel->debug = cfg->debug; +#if IS_ENABLED(CONFIG_IPV6) +	if (sk->sk_family == PF_INET6) { +		struct ipv6_pinfo *np = inet6_sk(sk); + +		if (ipv6_addr_v4mapped(&np->saddr) && +		    ipv6_addr_v4mapped(&sk->sk_v6_daddr)) { +			struct inet_sock *inet = inet_sk(sk); + +			tunnel->v4mapped = true; +			inet->inet_saddr = np->saddr.s6_addr32[3]; +			inet->inet_rcv_saddr = sk->sk_v6_rcv_saddr.s6_addr32[3]; +			inet->inet_daddr = sk->sk_v6_daddr.s6_addr32[3]; +		} else { +			tunnel->v4mapped = false; +		} +	} +#endif +  	/* Mark socket as an encapsulation socket. See net/ipv4/udp.c */  	tunnel->encap = encap;  	if (encap == L2TP_ENCAPTYPE_UDP) { @@ -1712,7 +1619,7 @@ int l2tp_tunnel_create(struct net *net, int fd, int version, u32 tunnel_id, u32  		udp_sk(sk)->encap_rcv = l2tp_udp_encap_recv;  		udp_sk(sk)->encap_destroy = l2tp_udp_encap_destroy;  #if IS_ENABLED(CONFIG_IPV6) -		if (sk->sk_family == PF_INET6) +		if (sk->sk_family == PF_INET6 && !tunnel->v4mapped)  			udpv6_encap_enable();  		else  #endif @@ -1789,8 +1696,6 @@ void l2tp_session_free(struct l2tp_session *session)  	}  	kfree(session); - -	return;  }  EXPORT_SYMBOL_GPL(l2tp_session_free); @@ -1843,7 +1748,7 @@ EXPORT_SYMBOL_GPL(l2tp_session_delete);  /* We come here whenever a session's send_seq, cookie_len or   * l2specific_len parameters are set.   */ -static void l2tp_session_set_header_len(struct l2tp_session *session, int version) +void l2tp_session_set_header_len(struct l2tp_session *session, int version)  {  	if (version == L2TP_HDR_VER_2) {  		session->hdr_len = 6; @@ -1856,6 +1761,7 @@ static void l2tp_session_set_header_len(struct l2tp_session *session, int versio  	}  } +EXPORT_SYMBOL_GPL(l2tp_session_set_header_len);  struct l2tp_session *l2tp_session_create(int priv_size, struct l2tp_tunnel *tunnel, u32 session_id, u32 peer_session_id, struct l2tp_session_cfg *cfg)  { @@ -1996,7 +1902,7 @@ static int __init l2tp_init(void)  	if (rc)  		goto out; -	l2tp_wq = alloc_workqueue("l2tp", WQ_NON_REENTRANT | WQ_UNBOUND, 0); +	l2tp_wq = alloc_workqueue("l2tp", WQ_UNBOUND, 0);  	if (!l2tp_wq) {  		pr_err("alloc_workqueue failed\n");  		rc = -ENOMEM;  | 
