diff options
Diffstat (limited to 'net/ipv6/udp.c')
| -rw-r--r-- | net/ipv6/udp.c | 98 | 
1 files changed, 73 insertions, 25 deletions
diff --git a/net/ipv6/udp.c b/net/ipv6/udp.c index ea0730028e5d..e6645cae403e 100644 --- a/net/ipv6/udp.c +++ b/net/ipv6/udp.c @@ -285,9 +285,7 @@ EXPORT_SYMBOL_GPL(udp6_lib_lookup_skb);  /* Must be called under rcu_read_lock().   * Does increment socket refcount.   */ -#if IS_ENABLED(CONFIG_NETFILTER_XT_MATCH_SOCKET) || \ -    IS_ENABLED(CONFIG_NETFILTER_XT_TARGET_TPROXY) || \ -    IS_ENABLED(CONFIG_NF_SOCKET_IPV6) +#if IS_ENABLED(CONFIG_NF_TPROXY_IPV6) || IS_ENABLED(CONFIG_NF_SOCKET_IPV6)  struct sock *udp6_lib_lookup(struct net *net, const struct in6_addr *saddr, __be16 sport,  			     const struct in6_addr *daddr, __be16 dport, int dif)  { @@ -546,10 +544,10 @@ static __inline__ void udpv6_err(struct sk_buff *skb,  	__udp6_lib_err(skb, opt, type, code, offset, info, &udp_table);  } -static struct static_key udpv6_encap_needed __read_mostly; +static DEFINE_STATIC_KEY_FALSE(udpv6_encap_needed_key);  void udpv6_encap_enable(void)  { -	static_key_enable(&udpv6_encap_needed); +	static_branch_enable(&udpv6_encap_needed_key);  }  EXPORT_SYMBOL(udpv6_encap_enable); @@ -561,7 +559,7 @@ static int udpv6_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)  	if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb))  		goto drop; -	if (static_key_false(&udpv6_encap_needed) && up->encap_type) { +	if (static_branch_unlikely(&udpv6_encap_needed_key) && up->encap_type) {  		int (*encap_rcv)(struct sock *sk, struct sk_buff *skb);  		/* @@ -1023,7 +1021,8 @@ static void udp6_hwcsum_outgoing(struct sock *sk, struct sk_buff *skb,   *	Sending   */ -static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6) +static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6, +			   struct inet_cork *cork)  {  	struct sock *sk = skb->sk;  	struct udphdr *uh; @@ -1042,12 +1041,32 @@ static int udp_v6_send_skb(struct sk_buff *skb, struct flowi6 *fl6)  	uh->len = htons(len);  	uh->check = 0; +	if (cork->gso_size) { +		const int hlen = skb_network_header_len(skb) + +				 sizeof(struct udphdr); + +		if (hlen + cork->gso_size > cork->fragsize) +			return -EINVAL; +		if (skb->len > cork->gso_size * UDP_MAX_SEGMENTS) +			return -EINVAL; +		if (udp_sk(sk)->no_check6_tx) +			return -EINVAL; +		if (skb->ip_summed != CHECKSUM_PARTIAL || is_udplite || +		    dst_xfrm(skb_dst(skb))) +			return -EIO; + +		skb_shinfo(skb)->gso_size = cork->gso_size; +		skb_shinfo(skb)->gso_type = SKB_GSO_UDP_L4; +		goto csum_partial; +	} +  	if (is_udplite)  		csum = udplite_csum(skb);  	else if (udp_sk(sk)->no_check6_tx) {   /* UDP csum disabled */  		skb->ip_summed = CHECKSUM_NONE;  		goto send;  	} else if (skb->ip_summed == CHECKSUM_PARTIAL) { /* UDP hardware csum */ +csum_partial:  		udp6_hwcsum_outgoing(sk, skb, &fl6->saddr, &fl6->daddr, len);  		goto send;  	} else @@ -1093,7 +1112,7 @@ static int udp_v6_push_pending_frames(struct sock *sk)  	if (!skb)  		goto out; -	err = udp_v6_send_skb(skb, &fl6); +	err = udp_v6_send_skb(skb, &fl6, &inet_sk(sk)->cork.base);  out:  	up->len = 0; @@ -1127,6 +1146,7 @@ int udpv6_sendmsg(struct sock *sk, struct msghdr *msg, size_t len)  	ipc6.hlimit = -1;  	ipc6.tclass = -1;  	ipc6.dontfrag = -1; +	ipc6.gso_size = up->gso_size;  	sockc.tsflags = sk->sk_tsflags;  	/* destination address check */ @@ -1259,7 +1279,10 @@ do_udp_sendmsg:  		opt->tot_len = sizeof(*opt);  		ipc6.opt = opt; -		err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, &ipc6, &sockc); +		err = udp_cmsg_send(sk, msg, &ipc6.gso_size); +		if (err > 0) +			err = ip6_datagram_send_ctl(sock_net(sk), sk, msg, &fl6, +						    &ipc6, &sockc);  		if (err < 0) {  			fl6_sock_release(flowlabel);  			return err; @@ -1291,6 +1314,29 @@ do_udp_sendmsg:  		fl6.saddr = np->saddr;  	fl6.fl6_sport = inet->inet_sport; +	if (cgroup_bpf_enabled && !connected) { +		err = BPF_CGROUP_RUN_PROG_UDP6_SENDMSG_LOCK(sk, +					   (struct sockaddr *)sin6, &fl6.saddr); +		if (err) +			goto out_no_dst; +		if (sin6) { +			if (ipv6_addr_v4mapped(&sin6->sin6_addr)) { +				/* BPF program rewrote IPv6-only by IPv4-mapped +				 * IPv6. It's currently unsupported. +				 */ +				err = -ENOTSUPP; +				goto out_no_dst; +			} +			if (sin6->sin6_port == 0) { +				/* BPF program set invalid port. Reject it. */ +				err = -EINVAL; +				goto out_no_dst; +			} +			fl6.fl6_dport = sin6->sin6_port; +			fl6.daddr = sin6->sin6_addr; +		} +	} +  	final_p = fl6_update_dst(&fl6, opt, &final);  	if (final_p)  		connected = false; @@ -1324,15 +1370,16 @@ back_from_confirm:  	/* Lockless fast path for the non-corking case */  	if (!corkreq) { +		struct inet_cork_full cork;  		struct sk_buff *skb;  		skb = ip6_make_skb(sk, getfrag, msg, ulen,  				   sizeof(struct udphdr), &ipc6,  				   &fl6, (struct rt6_info *)dst, -				   msg->msg_flags, &sockc); +				   msg->msg_flags, &cork, &sockc);  		err = PTR_ERR(skb);  		if (!IS_ERR_OR_NULL(skb)) -			err = udp_v6_send_skb(skb, &fl6); +			err = udp_v6_send_skb(skb, &fl6, &cork.base);  		goto out;  	} @@ -1369,6 +1416,7 @@ do_append_data:  out:  	dst_release(dst); +out_no_dst:  	fl6_sock_release(flowlabel);  	txopt_put(opt_to_free);  	if (!err) @@ -1402,7 +1450,7 @@ void udpv6_destroy_sock(struct sock *sk)  	udp_v6_flush_pending_frames(sk);  	release_sock(sk); -	if (static_key_false(&udpv6_encap_needed) && up->encap_type) { +	if (static_branch_unlikely(&udpv6_encap_needed_key) && up->encap_type) {  		void (*encap_destroy)(struct sock *sk);  		encap_destroy = READ_ONCE(up->encap_destroy);  		if (encap_destroy) @@ -1475,36 +1523,36 @@ int udp6_seq_show(struct seq_file *seq, void *v)  		struct inet_sock *inet = inet_sk(v);  		__u16 srcp = ntohs(inet->inet_sport);  		__u16 destp = ntohs(inet->inet_dport); -		ip6_dgram_sock_seq_show(seq, v, srcp, destp, bucket); +		__ip6_dgram_sock_seq_show(seq, v, srcp, destp, +					  udp_rqueue_get(v), bucket);  	}  	return 0;  } -static const struct file_operations udp6_afinfo_seq_fops = { -	.open     = udp_seq_open, -	.read     = seq_read, -	.llseek   = seq_lseek, -	.release  = seq_release_net +const struct seq_operations udp6_seq_ops = { +	.start		= udp_seq_start, +	.next		= udp_seq_next, +	.stop		= udp_seq_stop, +	.show		= udp6_seq_show,  }; +EXPORT_SYMBOL(udp6_seq_ops);  static struct udp_seq_afinfo udp6_seq_afinfo = { -	.name		= "udp6",  	.family		= AF_INET6,  	.udp_table	= &udp_table, -	.seq_fops	= &udp6_afinfo_seq_fops, -	.seq_ops	= { -		.show		= udp6_seq_show, -	},  };  int __net_init udp6_proc_init(struct net *net)  { -	return udp_proc_register(net, &udp6_seq_afinfo); +	if (!proc_create_net_data("udp6", 0444, net->proc_net, &udp6_seq_ops, +			sizeof(struct udp_iter_state), &udp6_seq_afinfo)) +		return -ENOMEM; +	return 0;  }  void udp6_proc_exit(struct net *net)  { -	udp_proc_unregister(net, &udp6_seq_afinfo); +	remove_proc_entry("udp6", net->proc_net);  }  #endif /* CONFIG_PROC_FS */  | 
