1327 lines · 29.8 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* *	RAW sockets for IPv6 *	Linux INET6 implementation * *	Authors: *	Pedro Roque		<roque@di.fc.ul.pt> * *	Adapted from linux/net/ipv4/raw.c * *	Fixes: *	Hideaki YOSHIFUJI	:	sin6_scope_id support *	YOSHIFUJI,H.@USAGI	:	raw checksum (RFC2292(bis) compliance) *	Kazunori MIYAZAWA @USAGI:	change process style to use ip6_append_data */ #include <linux/errno.h>#include <linux/types.h>#include <linux/socket.h>#include <linux/slab.h>#include <linux/sockios.h>#include <linux/net.h>#include <linux/in6.h>#include <linux/netdevice.h>#include <linux/if_arp.h>#include <linux/icmpv6.h>#include <linux/netfilter.h>#include <linux/netfilter_ipv6.h>#include <linux/skbuff.h>#include <linux/compat.h>#include <linux/uaccess.h>#include <asm/ioctls.h> #include <net/net_namespace.h>#include <net/ip.h>#include <net/sock.h>#include <net/snmp.h> #include <net/ipv6.h>#include <net/ndisc.h>#include <net/protocol.h>#include <net/ip6_route.h>#include <net/ip6_checksum.h>#include <net/addrconf.h>#include <net/transp_v6.h>#include <net/udp.h>#include <net/inet_common.h>#include <net/tcp_states.h>#if IS_ENABLED(CONFIG_IPV6_MIP6)#include <net/mip6.h>#endif#include <linux/mroute6.h> #include <net/raw.h>#include <net/rawv6.h>#include <net/xfrm.h> #include <linux/proc_fs.h>#include <linux/seq_file.h>#include <linux/export.h> #define	ICMPV6_HDRLEN	4	/* ICMPv6 header, RFC 4443 Section 2.1 */ struct raw_hashinfo raw_v6_hashinfo;EXPORT_SYMBOL_GPL(raw_v6_hashinfo); bool raw_v6_match(struct net *net, const struct sock *sk, unsigned short num,		  const struct in6_addr *loc_addr,		  const struct in6_addr *rmt_addr, int dif, int sdif){	if (inet_sk(sk)->inet_num != num ||	    !net_eq(sock_net(sk), net) ||	    (!ipv6_addr_any(&sk->sk_v6_daddr) &&	     !ipv6_addr_equal(&sk->sk_v6_daddr, rmt_addr)) ||	    !raw_sk_bound_dev_eq(net, sk->sk_bound_dev_if,				 dif, sdif))		return false; 	if (ipv6_addr_any(&sk->sk_v6_rcv_saddr) ||	    ipv6_addr_equal(&sk->sk_v6_rcv_saddr, loc_addr) ||	    (ipv6_addr_is_multicast(loc_addr) &&	     inet6_mc_check(sk, loc_addr, rmt_addr)))		return true; 	return false;}EXPORT_SYMBOL_GPL(raw_v6_match); /* *	0 - deliver *	1 - block */static int icmpv6_filter(const struct sock *sk, struct sk_buff *skb){	const struct icmp6hdr *hdr;	const __u32 *data;	unsigned int type; 	/* We require only the four bytes of the ICMPv6 header, not any	 * additional bytes of message body in "struct icmp6hdr".	 */	if (!pskb_may_pull(skb, ICMPV6_HDRLEN))		return 1; 	hdr = (struct icmp6hdr *)skb->data;	type = hdr->icmp6_type; 	data = &raw6_sk(sk)->filter.data[0]; 	return (data[type >> 5] & (1U << (type & 31))) != 0;} #if IS_ENABLED(CONFIG_IPV6_MIP6)typedef int mh_filter_t(struct sock *sock, struct sk_buff *skb); static mh_filter_t __rcu *mh_filter __read_mostly; int rawv6_mh_filter_register(mh_filter_t filter){	rcu_assign_pointer(mh_filter, filter);	return 0;}EXPORT_SYMBOL(rawv6_mh_filter_register); int rawv6_mh_filter_unregister(mh_filter_t filter){	RCU_INIT_POINTER(mh_filter, NULL);	synchronize_rcu();	return 0;}EXPORT_SYMBOL(rawv6_mh_filter_unregister); #endif /* *	demultiplex raw sockets. *	(should consider queueing the skb in the sock receive_queue *	without calling rawv6.c) * *	Caller owns SKB so we must make clones. */static bool ipv6_raw_deliver(struct sk_buff *skb, int nexthdr){	struct net *net = dev_net(skb->dev);	const struct ipv6hdr *ip6h;	struct hlist_head *hlist;	bool delivered = false;	struct sock *sk;	__u8 hash; 	ip6h = ipv6_hdr(skb); 	hash = raw_hashfunc(net, nexthdr);	hlist = &raw_v6_hashinfo.ht[hash];	rcu_read_lock();	sk_for_each_rcu(sk, hlist) {		int filtered; 		if (!raw_v6_match(net, sk, nexthdr, &ip6h->daddr, &ip6h->saddr,				  inet6_iif(skb), inet6_sdif(skb)))			continue; 		if (atomic_read(&sk->sk_rmem_alloc) >=		    READ_ONCE(sk->sk_rcvbuf)) {			sk_drops_inc(sk);			continue;		} 		delivered = true;		switch (nexthdr) {		case IPPROTO_ICMPV6:			filtered = icmpv6_filter(sk, skb);			ip6h = ipv6_hdr(skb);			break; #if IS_ENABLED(CONFIG_IPV6_MIP6)		case IPPROTO_MH:		{			/* XXX: To validate MH only once for each packet,			 * this is placed here. It should be after checking			 * xfrm policy, however it doesn't. The checking xfrm			 * policy is placed in rawv6_rcv() because it is			 * required for each socket.			 */			mh_filter_t *filter; 			filter = rcu_dereference(mh_filter);			filtered = filter ? (*filter)(sk, skb) : 0;			break;		}#endif		default:			filtered = 0;			break;		} 		if (filtered < 0)			break;		if (filtered == 0) {			struct sk_buff *clone = skb_clone(skb, GFP_ATOMIC); 			/* Not releasing hash table! */			if (clone)				rawv6_rcv(sk, clone);		}	}	rcu_read_unlock();	return delivered;} bool raw6_local_deliver(struct sk_buff *skb, int nexthdr){	return ipv6_raw_deliver(skb, nexthdr);} /* This cleans up af_inet6 a bit. -DaveM */static int rawv6_bind(struct sock *sk, struct sockaddr_unsized *uaddr,		      int addr_len){	struct inet_sock *inet = inet_sk(sk);	struct ipv6_pinfo *np = inet6_sk(sk);	struct sockaddr_in6 *addr = (struct sockaddr_in6 *) uaddr;	__be32 v4addr = 0;	int addr_type;	int err; 	if (addr_len < SIN6_LEN_RFC2133)		return -EINVAL; 	if (addr->sin6_family != AF_INET6)		return -EINVAL; 	addr_type = ipv6_addr_type(&addr->sin6_addr); 	/* Raw sockets are IPv6 only */	if (addr_type == IPV6_ADDR_MAPPED)		return -EADDRNOTAVAIL; 	lock_sock(sk); 	err = -EINVAL;	if (sk->sk_state != TCP_CLOSE)		goto out; 	rcu_read_lock();	/* Check if the address belongs to the host. */	if (addr_type != IPV6_ADDR_ANY) {		struct net_device *dev = NULL; 		if (__ipv6_addr_needs_scope_id(addr_type)) {			if (addr_len >= sizeof(struct sockaddr_in6) &&			    addr->sin6_scope_id) {				/* Override any existing binding, if another				 * one is supplied by user.				 */				sk->sk_bound_dev_if = addr->sin6_scope_id;			} 			/* Binding to link-local address requires an interface */			if (!sk->sk_bound_dev_if)				goto out_unlock;		} 		if (sk->sk_bound_dev_if) {			err = -ENODEV;			dev = dev_get_by_index_rcu(sock_net(sk),						   sk->sk_bound_dev_if);			if (!dev)				goto out_unlock;		} 		/* ipv4 addr of the socket is invalid.  Only the		 * unspecified and mapped address have a v4 equivalent.		 */		v4addr = LOOPBACK4_IPV6;		if (!(addr_type & IPV6_ADDR_MULTICAST) &&		    !ipv6_can_nonlocal_bind(sock_net(sk), inet)) {			err = -EADDRNOTAVAIL;			if (!ipv6_chk_addr(sock_net(sk), &addr->sin6_addr,					   dev, 0)) {				goto out_unlock;			}		}	} 	inet->inet_rcv_saddr = inet->inet_saddr = v4addr;	sk->sk_v6_rcv_saddr = addr->sin6_addr;	if (!(addr_type & IPV6_ADDR_MULTICAST))		np->saddr = addr->sin6_addr;	err = 0;out_unlock:	rcu_read_unlock();out:	release_sock(sk);	return err;} static void rawv6_err(struct sock *sk, struct sk_buff *skb,		      u8 type, u8 code, int offset, __be32 info){	bool recverr = inet6_test_bit(RECVERR6, sk);	struct ipv6_pinfo *np = inet6_sk(sk);	int err;	int harderr; 	/* Report error on raw socket, if:	   1. User requested recverr.	   2. Socket is connected (otherwise the error indication	      is useless without recverr and error is hard.	 */	if (!recverr && sk->sk_state != TCP_ESTABLISHED)		return; 	harderr = icmpv6_err_convert(type, code, &err);	if (type == ICMPV6_PKT_TOOBIG) {		ip6_sk_update_pmtu(skb, sk, info);		harderr = (READ_ONCE(np->pmtudisc) == IPV6_PMTUDISC_DO);	}	if (type == NDISC_REDIRECT) {		ip6_sk_redirect(skb, sk);		return;	}	if (recverr) {		u8 *payload = skb->data;		if (!inet_test_bit(HDRINCL, sk))			payload += offset;		ipv6_icmp_error(sk, skb, err, 0, ntohl(info), payload);	} 	if (recverr || harderr) {		sk->sk_err = err;		sk_error_report(sk);	}} void raw6_icmp_error(struct sk_buff *skb, int nexthdr,		u8 type, u8 code, int inner_offset, __be32 info){	struct net *net = dev_net(skb->dev);	struct hlist_head *hlist;	struct sock *sk;	int hash; 	hash = raw_hashfunc(net, nexthdr);	hlist = &raw_v6_hashinfo.ht[hash];	rcu_read_lock();	sk_for_each_rcu(sk, hlist) {		/* Note: ipv6_hdr(skb) != skb->data */		const struct ipv6hdr *ip6h = (const struct ipv6hdr *)skb->data; 		if (!raw_v6_match(net, sk, nexthdr, &ip6h->saddr, &ip6h->daddr,				  inet6_iif(skb), inet6_sdif(skb)))			continue;		rawv6_err(sk, skb, type, code, inner_offset, info);	}	rcu_read_unlock();} static inline int rawv6_rcv_skb(struct sock *sk, struct sk_buff *skb){	enum skb_drop_reason reason; 	if ((raw6_sk(sk)->checksum || rcu_access_pointer(sk->sk_filter)) &&	    skb_checksum_complete(skb)) {		sk_drops_inc(sk);		sk_skb_reason_drop(sk, skb, SKB_DROP_REASON_SKB_CSUM);		return NET_RX_DROP;	} 	/* Charge it to the socket. */	skb_dst_drop(skb);	reason = sock_queue_rcv_skb_reason(sk, skb);	if (reason) {		sk_skb_reason_drop(sk, skb, reason);		return NET_RX_DROP;	} 	return 0;} /* *	This is next to useless... *	if we demultiplex in network layer we don't need the extra call *	just to queue the skb... *	maybe we could have the network decide upon a hint if it *	should call raw_rcv for demultiplexing */int rawv6_rcv(struct sock *sk, struct sk_buff *skb){	struct inet_sock *inet = inet_sk(sk);	struct raw6_sock *rp = raw6_sk(sk); 	if (!xfrm6_policy_check(sk, XFRM_POLICY_IN, skb)) {		sk_drops_inc(sk);		sk_skb_reason_drop(sk, skb, SKB_DROP_REASON_XFRM_POLICY);		return NET_RX_DROP;	}	nf_reset_ct(skb); 	if (!rp->checksum)		skb->ip_summed = CHECKSUM_UNNECESSARY; 	if (skb->ip_summed == CHECKSUM_COMPLETE) {		skb_postpull_rcsum(skb, skb_network_header(skb),				   skb_network_header_len(skb));		if (!csum_ipv6_magic(&ipv6_hdr(skb)->saddr,				     &ipv6_hdr(skb)->daddr,				     skb->len, inet->inet_num, skb->csum))			skb->ip_summed = CHECKSUM_UNNECESSARY;	}	if (!skb_csum_unnecessary(skb))		skb->csum = ~csum_unfold(csum_ipv6_magic(&ipv6_hdr(skb)->saddr,							 &ipv6_hdr(skb)->daddr,							 skb->len,							 inet->inet_num, 0)); 	if (inet_test_bit(HDRINCL, sk)) {		if (skb_checksum_complete(skb)) {			sk_drops_inc(sk);			sk_skb_reason_drop(sk, skb, SKB_DROP_REASON_SKB_CSUM);			return NET_RX_DROP;		}	} 	rawv6_rcv_skb(sk, skb);	return 0;}  /* *	This should be easy, if there is something there *	we return it, otherwise we block. */ static int rawv6_recvmsg(struct sock *sk, struct msghdr *msg, size_t len,			 int flags){	struct ipv6_pinfo *np = inet6_sk(sk);	DECLARE_SOCKADDR(struct sockaddr_in6 *, sin6, msg->msg_name);	struct sk_buff *skb;	size_t copied;	int err; 	if (flags & MSG_OOB)		return -EOPNOTSUPP; 	if (flags & MSG_ERRQUEUE)		return ipv6_recv_error(sk, msg, len); 	if (np->rxopt.bits.rxpmtu && READ_ONCE(np->rxpmtu))		return ipv6_recv_rxpmtu(sk, msg, len); 	skb = skb_recv_datagram(sk, flags, &err);	if (!skb)		goto out; 	copied = skb->len;	if (copied > len) {		copied = len;		msg->msg_flags |= MSG_TRUNC;	} 	if (skb_csum_unnecessary(skb)) {		err = skb_copy_datagram_msg(skb, 0, msg, copied);	} else if (msg->msg_flags&MSG_TRUNC) {		if (__skb_checksum_complete(skb))			goto csum_copy_err;		err = skb_copy_datagram_msg(skb, 0, msg, copied);	} else {		err = skb_copy_and_csum_datagram_msg(skb, 0, msg);		if (err == -EINVAL)			goto csum_copy_err;	}	if (err)		goto out_free; 	/* Copy the address. */	if (sin6) {		sin6->sin6_family = AF_INET6;		sin6->sin6_port = 0;		sin6->sin6_addr = ipv6_hdr(skb)->saddr;		sin6->sin6_flowinfo = 0;		sin6->sin6_scope_id = ipv6_iface_scope_id(&sin6->sin6_addr,							  inet6_iif(skb));		msg->msg_namelen = sizeof(*sin6);	} 	sock_recv_cmsgs(msg, sk, skb); 	if (np->rxopt.all)		ip6_datagram_recv_ctl(sk, msg, skb); 	err = copied;	if (flags & MSG_TRUNC)		err = skb->len; out_free:	skb_free_datagram(sk, skb);out:	return err;

Lines 1–500 of 1327.