⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 ipv6_sockglue.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
/* *	IPv6 BSD socket options interface *	Linux INET6 implementation * *	Authors: *	Pedro Roque		<roque@di.fc.ul.pt> * *	Based on linux/net/ipv4/ip_sockglue.c * *	$Id: ipv6_sockglue.c,v 1.41 2002/02/01 22:01:04 davem Exp $ * *	This program is free software; you can redistribute it and/or *      modify it under the terms of the GNU General Public License *      as published by the Free Software Foundation; either version *      2 of the License, or (at your option) any later version. * *	FIXME: Make the setsockopt code POSIX compliant: That is * *	o	Return -EINVAL for setsockopt of short lengths *	o	Truncate getsockopt returns *	o	Return an optlen of the truncated length if need be * *	Changes: *	David L Stevens <dlstevens@us.ibm.com>: *		- added multicast source filtering API for MLDv2 */#include <linux/module.h>#include <linux/capability.h>#include <linux/errno.h>#include <linux/types.h>#include <linux/socket.h>#include <linux/sockios.h>#include <linux/net.h>#include <linux/in6.h>#include <linux/netdevice.h>#include <linux/if_arp.h>#include <linux/init.h>#include <linux/sysctl.h>#include <linux/netfilter.h>#include <net/sock.h>#include <net/snmp.h>#include <net/ipv6.h>#include <net/ndisc.h>#include <net/protocol.h>#include <net/transp_v6.h>#include <net/ip6_route.h>#include <net/addrconf.h>#include <net/inet_common.h>#include <net/tcp.h>#include <net/udp.h>#include <net/udplite.h>#include <net/xfrm.h>#include <asm/uaccess.h>DEFINE_SNMP_STAT(struct ipstats_mib, ipv6_statistics) __read_mostly;static struct inet6_protocol *ipv6_gso_pull_exthdrs(struct sk_buff *skb,						    int proto){	struct inet6_protocol *ops = NULL;	for (;;) {		struct ipv6_opt_hdr *opth;		int len;		if (proto != NEXTHDR_HOP) {			ops = rcu_dereference(inet6_protos[proto]);			if (unlikely(!ops))				break;			if (!(ops->flags & INET6_PROTO_GSO_EXTHDR))				break;		}		if (unlikely(!pskb_may_pull(skb, 8)))			break;		opth = (void *)skb->data;		len = opth->hdrlen * 8 + 8;		if (unlikely(!pskb_may_pull(skb, len)))			break;		proto = opth->nexthdr;		__skb_pull(skb, len);	}	return ops;}static int ipv6_gso_send_check(struct sk_buff *skb){	struct ipv6hdr *ipv6h;	struct inet6_protocol *ops;	int err = -EINVAL;	if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))		goto out;	ipv6h = ipv6_hdr(skb);	__skb_pull(skb, sizeof(*ipv6h));	err = -EPROTONOSUPPORT;	rcu_read_lock();	ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);	if (likely(ops && ops->gso_send_check)) {		skb_reset_transport_header(skb);		err = ops->gso_send_check(skb);	}	rcu_read_unlock();out:	return err;}static struct sk_buff *ipv6_gso_segment(struct sk_buff *skb, int features){	struct sk_buff *segs = ERR_PTR(-EINVAL);	struct ipv6hdr *ipv6h;	struct inet6_protocol *ops;	if (!(features & NETIF_F_V6_CSUM))		features &= ~NETIF_F_SG;	if (unlikely(skb_shinfo(skb)->gso_type &		     ~(SKB_GSO_UDP |		       SKB_GSO_DODGY |		       SKB_GSO_TCP_ECN |		       SKB_GSO_TCPV6 |		       0)))		goto out;	if (unlikely(!pskb_may_pull(skb, sizeof(*ipv6h))))		goto out;	ipv6h = ipv6_hdr(skb);	__skb_pull(skb, sizeof(*ipv6h));	segs = ERR_PTR(-EPROTONOSUPPORT);	rcu_read_lock();	ops = ipv6_gso_pull_exthdrs(skb, ipv6h->nexthdr);	if (likely(ops && ops->gso_segment)) {		skb_reset_transport_header(skb);		segs = ops->gso_segment(skb, features);	}	rcu_read_unlock();	if (unlikely(IS_ERR(segs)))		goto out;	for (skb = segs; skb; skb = skb->next) {		ipv6h = ipv6_hdr(skb);		ipv6h->payload_len = htons(skb->len - skb->mac_len -					   sizeof(*ipv6h));	}out:	return segs;}static struct packet_type ipv6_packet_type = {	.type = __constant_htons(ETH_P_IPV6),	.func = ipv6_rcv,	.gso_send_check = ipv6_gso_send_check,	.gso_segment = ipv6_gso_segment,};struct ip6_ra_chain *ip6_ra_chain;DEFINE_RWLOCK(ip6_ra_lock);int ip6_ra_control(struct sock *sk, int sel, void (*destructor)(struct sock *)){	struct ip6_ra_chain *ra, *new_ra, **rap;	/* RA packet may be delivered ONLY to IPPROTO_RAW socket */	if (sk->sk_type != SOCK_RAW || inet_sk(sk)->num != IPPROTO_RAW)		return -EINVAL;	new_ra = (sel>=0) ? kmalloc(sizeof(*new_ra), GFP_KERNEL) : NULL;	write_lock_bh(&ip6_ra_lock);	for (rap = &ip6_ra_chain; (ra=*rap) != NULL; rap = &ra->next) {		if (ra->sk == sk) {			if (sel>=0) {				write_unlock_bh(&ip6_ra_lock);				kfree(new_ra);				return -EADDRINUSE;			}			*rap = ra->next;			write_unlock_bh(&ip6_ra_lock);			if (ra->destructor)				ra->destructor(sk);			sock_put(sk);			kfree(ra);			return 0;		}	}	if (new_ra == NULL) {		write_unlock_bh(&ip6_ra_lock);		return -ENOBUFS;	}	new_ra->sk = sk;	new_ra->sel = sel;	new_ra->destructor = destructor;	new_ra->next = ra;	*rap = new_ra;	sock_hold(sk);	write_unlock_bh(&ip6_ra_lock);	return 0;}static int do_ipv6_setsockopt(struct sock *sk, int level, int optname,		    char __user *optval, int optlen){	struct ipv6_pinfo *np = inet6_sk(sk);	int val, valbool;	int retv = -ENOPROTOOPT;	if (optval == NULL)		val=0;	else if (get_user(val, (int __user *) optval))		return -EFAULT;	valbool = (val!=0);	lock_sock(sk);	switch (optname) {	case IPV6_ADDRFORM:		if (val == PF_INET) {			struct ipv6_txoptions *opt;			struct sk_buff *pktopt;			if (sk->sk_protocol != IPPROTO_UDP &&			    sk->sk_protocol != IPPROTO_UDPLITE &&			    sk->sk_protocol != IPPROTO_TCP)				break;			if (sk->sk_state != TCP_ESTABLISHED) {				retv = -ENOTCONN;				break;			}			if (ipv6_only_sock(sk) ||			    !ipv6_addr_v4mapped(&np->daddr)) {				retv = -EADDRNOTAVAIL;				break;			}			fl6_free_socklist(sk);			ipv6_sock_mc_close(sk);			/*			 * Sock is moving from IPv6 to IPv4 (sk_prot), so			 * remove it from the refcnt debug socks count in the			 * original family...			 */			sk_refcnt_debug_dec(sk);			if (sk->sk_protocol == IPPROTO_TCP) {				struct inet_connection_sock *icsk = inet_csk(sk);				local_bh_disable();				sock_prot_dec_use(sk->sk_prot);				sock_prot_inc_use(&tcp_prot);				local_bh_enable();				sk->sk_prot = &tcp_prot;				icsk->icsk_af_ops = &ipv4_specific;				sk->sk_socket->ops = &inet_stream_ops;				sk->sk_family = PF_INET;				tcp_sync_mss(sk, icsk->icsk_pmtu_cookie);			} else {				struct proto *prot = &udp_prot;				if (sk->sk_protocol == IPPROTO_UDPLITE)					prot = &udplite_prot;				local_bh_disable();				sock_prot_dec_use(sk->sk_prot);				sock_prot_inc_use(prot);				local_bh_enable();				sk->sk_prot = prot;				sk->sk_socket->ops = &inet_dgram_ops;				sk->sk_family = PF_INET;			}			opt = xchg(&np->opt, NULL);			if (opt)				sock_kfree_s(sk, opt, opt->tot_len);			pktopt = xchg(&np->pktoptions, NULL);			if (pktopt)				kfree_skb(pktopt);			sk->sk_destruct = inet_sock_destruct;			/*			 * ... and add it to the refcnt debug socks count			 * in the new family. -acme			 */			sk_refcnt_debug_inc(sk);			module_put(THIS_MODULE);			retv = 0;			break;		}		goto e_inval;	case IPV6_V6ONLY:		if (inet_sk(sk)->num)			goto e_inval;		np->ipv6only = valbool;		retv = 0;		break;	case IPV6_RECVPKTINFO:		np->rxopt.bits.rxinfo = valbool;		retv = 0;		break;	case IPV6_2292PKTINFO:		np->rxopt.bits.rxoinfo = valbool;		retv = 0;		break;	case IPV6_RECVHOPLIMIT:		np->rxopt.bits.rxhlim = valbool;		retv = 0;		break;	case IPV6_2292HOPLIMIT:		np->rxopt.bits.rxohlim = valbool;		retv = 0;		break;	case IPV6_RECVRTHDR:		np->rxopt.bits.srcrt = valbool;		retv = 0;		break;	case IPV6_2292RTHDR:		np->rxopt.bits.osrcrt = valbool;		retv = 0;		break;	case IPV6_RECVHOPOPTS:		np->rxopt.bits.hopopts = valbool;		retv = 0;		break;	case IPV6_2292HOPOPTS:		np->rxopt.bits.ohopopts = valbool;		retv = 0;		break;	case IPV6_RECVDSTOPTS:		np->rxopt.bits.dstopts = valbool;		retv = 0;		break;	case IPV6_2292DSTOPTS:		np->rxopt.bits.odstopts = valbool;		retv = 0;		break;	case IPV6_TCLASS:		if (val < -1 || val > 0xff)			goto e_inval;		np->tclass = val;		retv = 0;		break;	case IPV6_RECVTCLASS:		np->rxopt.bits.rxtclass = valbool;		retv = 0;		break;	case IPV6_FLOWINFO:		np->rxopt.bits.rxflow = valbool;		retv = 0;		break;	case IPV6_HOPOPTS:	case IPV6_RTHDRDSTOPTS:	case IPV6_RTHDR:	case IPV6_DSTOPTS:	{		struct ipv6_txoptions *opt;		if (optlen == 0)			optval = NULL;		/* hop-by-hop / destination options are privileged option */		retv = -EPERM;		if (optname != IPV6_RTHDR && !capable(CAP_NET_RAW))			break;		retv = -EINVAL;		if (optlen & 0x7 || optlen > 8 * 255)			break;		opt = ipv6_renew_options(sk, np->opt, optname,					 (struct ipv6_opt_hdr __user *)optval,					 optlen);		if (IS_ERR(opt)) {			retv = PTR_ERR(opt);			break;		}		/* routing header option needs extra check */		if (optname == IPV6_RTHDR && opt && opt->srcrt) {			struct ipv6_rt_hdr *rthdr = opt->srcrt;			switch (rthdr->type) {#if defined(CONFIG_IPV6_MIP6) || defined(CONFIG_IPV6_MIP6_MODULE)			case IPV6_SRCRT_TYPE_2:				break;#endif			default:				goto sticky_done;			}			if ((rthdr->hdrlen & 1) ||			    (rthdr->hdrlen >> 1) != rthdr->segments_left)				goto sticky_done;		}		retv = 0;		if (inet_sk(sk)->is_icsk) {			if (opt) {				struct inet_connection_sock *icsk = inet_csk(sk);				if (!((1 << sk->sk_state) &				      (TCPF_LISTEN | TCPF_CLOSE))				    && inet_sk(sk)->daddr != LOOPBACK4_IPV6) {					icsk->icsk_ext_hdr_len =						opt->opt_flen + opt->opt_nflen;					icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);				}			}			opt = xchg(&np->opt, opt);			sk_dst_reset(sk);		} else {			write_lock(&sk->sk_dst_lock);			opt = xchg(&np->opt, opt);			write_unlock(&sk->sk_dst_lock);			sk_dst_reset(sk);		}sticky_done:		if (opt)			sock_kfree_s(sk, opt, opt->tot_len);		break;	}	case IPV6_2292PKTOPTIONS:	{		struct ipv6_txoptions *opt = NULL;		struct msghdr msg;		struct flowi fl;		int junk;		fl.fl6_flowlabel = 0;		fl.oif = sk->sk_bound_dev_if;		if (optlen == 0)			goto update;		/* 1K is probably excessive		 * 1K is surely not enough, 2K per standard header is 16K.		 */		retv = -EINVAL;		if (optlen > 64*1024)			break;		opt = sock_kmalloc(sk, sizeof(*opt) + optlen, GFP_KERNEL);		retv = -ENOBUFS;		if (opt == NULL)			break;		memset(opt, 0, sizeof(*opt));		opt->tot_len = sizeof(*opt) + optlen;		retv = -EFAULT;		if (copy_from_user(opt+1, optval, optlen))			goto done;		msg.msg_controllen = optlen;		msg.msg_control = (void*)(opt+1);		retv = datagram_send_ctl(&msg, &fl, opt, &junk, &junk);		if (retv)			goto done;update:		retv = 0;		if (inet_sk(sk)->is_icsk) {			if (opt) {				struct inet_connection_sock *icsk = inet_csk(sk);				if (!((1 << sk->sk_state) &				      (TCPF_LISTEN | TCPF_CLOSE))				    && inet_sk(sk)->daddr != LOOPBACK4_IPV6) {					icsk->icsk_ext_hdr_len =						opt->opt_flen + opt->opt_nflen;					icsk->icsk_sync_mss(sk, icsk->icsk_pmtu_cookie);				}			}			opt = xchg(&np->opt, opt);			sk_dst_reset(sk);		} else {			write_lock(&sk->sk_dst_lock);			opt = xchg(&np->opt, opt);			write_unlock(&sk->sk_dst_lock);			sk_dst_reset(sk);		}done:		if (opt)			sock_kfree_s(sk, opt, opt->tot_len);		break;	}	case IPV6_UNICAST_HOPS:		if (val > 255 || val < -1)			goto e_inval;		np->hop_limit = val;		retv = 0;		break;	case IPV6_MULTICAST_HOPS:		if (sk->sk_type == SOCK_STREAM)			goto e_inval;		if (val > 255 || val < -1)			goto e_inval;		np->mcast_hops = val;		retv = 0;		break;	case IPV6_MULTICAST_LOOP:		np->mc_loop = valbool;		retv = 0;		break;	case IPV6_MULTICAST_IF:		if (sk->sk_type == SOCK_STREAM)			goto e_inval;		if (val) {			if (sk->sk_bound_dev_if && sk->sk_bound_dev_if != val)				goto e_inval;			if (__dev_get_by_index(&init_net, val) == NULL) {				retv = -ENODEV;				break;			}		}		np->mcast_oif = val;		retv = 0;		break;	case IPV6_ADD_MEMBERSHIP:	case IPV6_DROP_MEMBERSHIP:	{		struct ipv6_mreq mreq;		retv = -EPROTO;		if (inet_sk(sk)->is_icsk)			break;		retv = -EFAULT;		if (copy_from_user(&mreq, optval, sizeof(struct ipv6_mreq)))			break;		if (optname == IPV6_ADD_MEMBERSHIP)			retv = ipv6_sock_mc_join(sk, mreq.ipv6mr_ifindex, &mreq.ipv6mr_multiaddr);		else

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -