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

📄 icmp.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 2 页
字号:
	len = min_t(unsigned int, len, IPV6_MIN_MTU - sizeof(struct ipv6hdr) -sizeof(struct icmp6hdr));	if (len < 0) {		LIMIT_NETDEBUG(KERN_DEBUG "icmp: len problem\n");		goto out_dst_release;	}	idev = in6_dev_get(skb->dev);	err = ip6_append_data(sk, icmpv6_getfrag, &msg,			      len + sizeof(struct icmp6hdr),			      sizeof(struct icmp6hdr),			      hlimit, tclass, NULL, &fl, (struct rt6_info*)dst,			      MSG_DONTWAIT);	if (err) {		ip6_flush_pending_frames(sk);		goto out_put;	}	err = icmpv6_push_pending_frames(sk, &fl, &tmp_hdr, len + sizeof(struct icmp6hdr));out_put:	if (likely(idev != NULL))		in6_dev_put(idev);out_dst_release:	dst_release(dst);out:	icmpv6_xmit_unlock();}EXPORT_SYMBOL(icmpv6_send);static void icmpv6_echo_reply(struct sk_buff *skb){	struct sock *sk;	struct inet6_dev *idev;	struct ipv6_pinfo *np;	struct in6_addr *saddr = NULL;	struct icmp6hdr *icmph = icmp6_hdr(skb);	struct icmp6hdr tmp_hdr;	struct flowi fl;	struct icmpv6_msg msg;	struct dst_entry *dst;	int err = 0;	int hlimit;	int tclass;	saddr = &ipv6_hdr(skb)->daddr;	if (!ipv6_unicast_destination(skb))		saddr = NULL;	memcpy(&tmp_hdr, icmph, sizeof(tmp_hdr));	tmp_hdr.icmp6_type = ICMPV6_ECHO_REPLY;	memset(&fl, 0, sizeof(fl));	fl.proto = IPPROTO_ICMPV6;	ipv6_addr_copy(&fl.fl6_dst, &ipv6_hdr(skb)->saddr);	if (saddr)		ipv6_addr_copy(&fl.fl6_src, saddr);	fl.oif = skb->dev->ifindex;	fl.fl_icmp_type = ICMPV6_ECHO_REPLY;	security_skb_classify_flow(skb, &fl);	if (icmpv6_xmit_lock())		return;	sk = icmpv6_socket->sk;	np = inet6_sk(sk);	if (!fl.oif && ipv6_addr_is_multicast(&fl.fl6_dst))		fl.oif = np->mcast_oif;	err = ip6_dst_lookup(sk, &dst, &fl);	if (err)		goto out;	if ((err = xfrm_lookup(&dst, &fl, sk, 0)) < 0)		goto out;	if (ipv6_addr_is_multicast(&fl.fl6_dst))		hlimit = np->mcast_hops;	else		hlimit = np->hop_limit;	if (hlimit < 0)		hlimit = dst_metric(dst, RTAX_HOPLIMIT);	if (hlimit < 0)		hlimit = ipv6_get_hoplimit(dst->dev);	tclass = np->tclass;	if (tclass < 0)		tclass = 0;	idev = in6_dev_get(skb->dev);	msg.skb = skb;	msg.offset = 0;	msg.type = ICMPV6_ECHO_REPLY;	err = ip6_append_data(sk, icmpv6_getfrag, &msg, skb->len + sizeof(struct icmp6hdr),				sizeof(struct icmp6hdr), hlimit, tclass, NULL, &fl,				(struct rt6_info*)dst, MSG_DONTWAIT);	if (err) {		ip6_flush_pending_frames(sk);		goto out_put;	}	err = icmpv6_push_pending_frames(sk, &fl, &tmp_hdr, skb->len + sizeof(struct icmp6hdr));out_put:	if (likely(idev != NULL))		in6_dev_put(idev);	dst_release(dst);out:	icmpv6_xmit_unlock();}static void icmpv6_notify(struct sk_buff *skb, int type, int code, __be32 info){	struct in6_addr *saddr, *daddr;	struct inet6_protocol *ipprot;	struct sock *sk;	int inner_offset;	int hash;	u8 nexthdr;	if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))		return;	nexthdr = ((struct ipv6hdr *)skb->data)->nexthdr;	if (ipv6_ext_hdr(nexthdr)) {		/* now skip over extension headers */		inner_offset = ipv6_skip_exthdr(skb, sizeof(struct ipv6hdr), &nexthdr);		if (inner_offset<0)			return;	} else {		inner_offset = sizeof(struct ipv6hdr);	}	/* Checkin header including 8 bytes of inner protocol header. */	if (!pskb_may_pull(skb, inner_offset+8))		return;	saddr = &ipv6_hdr(skb)->saddr;	daddr = &ipv6_hdr(skb)->daddr;	/* BUGGG_FUTURE: we should try to parse exthdrs in this packet.	   Without this we will not able f.e. to make source routed	   pmtu discovery.	   Corresponding argument (opt) to notifiers is already added.	   --ANK (980726)	 */	hash = nexthdr & (MAX_INET_PROTOS - 1);	rcu_read_lock();	ipprot = rcu_dereference(inet6_protos[hash]);	if (ipprot && ipprot->err_handler)		ipprot->err_handler(skb, NULL, type, code, inner_offset, info);	rcu_read_unlock();	read_lock(&raw_v6_lock);	if ((sk = sk_head(&raw_v6_htable[hash])) != NULL) {		while ((sk = __raw_v6_lookup(sk, nexthdr, saddr, daddr,					    IP6CB(skb)->iif))) {			rawv6_err(sk, skb, NULL, type, code, inner_offset, info);			sk = sk_next(sk);		}	}	read_unlock(&raw_v6_lock);}/* *	Handle icmp messages */static int icmpv6_rcv(struct sk_buff *skb){	struct net_device *dev = skb->dev;	struct inet6_dev *idev = __in6_dev_get(dev);	struct in6_addr *saddr, *daddr;	struct ipv6hdr *orig_hdr;	struct icmp6hdr *hdr;	int type;	ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INMSGS);	saddr = &ipv6_hdr(skb)->saddr;	daddr = &ipv6_hdr(skb)->daddr;	/* Perform checksum. */	switch (skb->ip_summed) {	case CHECKSUM_COMPLETE:		if (!csum_ipv6_magic(saddr, daddr, skb->len, IPPROTO_ICMPV6,				     skb->csum))			break;		/* fall through */	case CHECKSUM_NONE:		skb->csum = ~csum_unfold(csum_ipv6_magic(saddr, daddr, skb->len,					     IPPROTO_ICMPV6, 0));		if (__skb_checksum_complete(skb)) {			LIMIT_NETDEBUG(KERN_DEBUG "ICMPv6 checksum failed [" NIP6_FMT " > " NIP6_FMT "]\n",				       NIP6(*saddr), NIP6(*daddr));			goto discard_it;		}	}	if (!pskb_pull(skb, sizeof(struct icmp6hdr)))		goto discard_it;	hdr = icmp6_hdr(skb);	type = hdr->icmp6_type;	ICMP6MSGIN_INC_STATS_BH(idev, type);	switch (type) {	case ICMPV6_ECHO_REQUEST:		icmpv6_echo_reply(skb);		break;	case ICMPV6_ECHO_REPLY:		/* we couldn't care less */		break;	case ICMPV6_PKT_TOOBIG:		/* BUGGG_FUTURE: if packet contains rthdr, we cannot update		   standard destination cache. Seems, only "advanced"		   destination cache will allow to solve this problem		   --ANK (980726)		 */		if (!pskb_may_pull(skb, sizeof(struct ipv6hdr)))			goto discard_it;		hdr = icmp6_hdr(skb);		orig_hdr = (struct ipv6hdr *) (hdr + 1);		rt6_pmtu_discovery(&orig_hdr->daddr, &orig_hdr->saddr, dev,				   ntohl(hdr->icmp6_mtu));		/*		 *	Drop through to notify		 */	case ICMPV6_DEST_UNREACH:	case ICMPV6_TIME_EXCEED:	case ICMPV6_PARAMPROB:		icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu);		break;	case NDISC_ROUTER_SOLICITATION:	case NDISC_ROUTER_ADVERTISEMENT:	case NDISC_NEIGHBOUR_SOLICITATION:	case NDISC_NEIGHBOUR_ADVERTISEMENT:	case NDISC_REDIRECT:		ndisc_rcv(skb);		break;	case ICMPV6_MGM_QUERY:		igmp6_event_query(skb);		break;	case ICMPV6_MGM_REPORT:		igmp6_event_report(skb);		break;	case ICMPV6_MGM_REDUCTION:	case ICMPV6_NI_QUERY:	case ICMPV6_NI_REPLY:	case ICMPV6_MLD2_REPORT:	case ICMPV6_DHAAD_REQUEST:	case ICMPV6_DHAAD_REPLY:	case ICMPV6_MOBILE_PREFIX_SOL:	case ICMPV6_MOBILE_PREFIX_ADV:		break;	default:		LIMIT_NETDEBUG(KERN_DEBUG "icmpv6: msg of unknown type\n");		/* informational */		if (type & ICMPV6_INFOMSG_MASK)			break;		/*		 * error of unknown type.		 * must pass to upper level		 */		icmpv6_notify(skb, type, hdr->icmp6_code, hdr->icmp6_mtu);	}	kfree_skb(skb);	return 0;discard_it:	ICMP6_INC_STATS_BH(idev, ICMP6_MIB_INERRORS);	kfree_skb(skb);	return 0;}/* * Special lock-class for __icmpv6_socket: */static struct lock_class_key icmpv6_socket_sk_dst_lock_key;int __init icmpv6_init(struct net_proto_family *ops){	struct sock *sk;	int err, i, j;	for_each_possible_cpu(i) {		err = sock_create_kern(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6,				       &per_cpu(__icmpv6_socket, i));		if (err < 0) {			printk(KERN_ERR			       "Failed to initialize the ICMP6 control socket "			       "(err %d).\n",			       err);			goto fail;		}		sk = per_cpu(__icmpv6_socket, i)->sk;		sk->sk_allocation = GFP_ATOMIC;		/*		 * Split off their lock-class, because sk->sk_dst_lock		 * gets used from softirqs, which is safe for		 * __icmpv6_socket (because those never get directly used		 * via userspace syscalls), but unsafe for normal sockets.		 */		lockdep_set_class(&sk->sk_dst_lock,				  &icmpv6_socket_sk_dst_lock_key);		/* Enough space for 2 64K ICMP packets, including		 * sk_buff struct overhead.		 */		sk->sk_sndbuf =			(2 * ((64 * 1024) + sizeof(struct sk_buff)));		sk->sk_prot->unhash(sk);	}	if (inet6_add_protocol(&icmpv6_protocol, IPPROTO_ICMPV6) < 0) {		printk(KERN_ERR "Failed to register ICMP6 protocol\n");		err = -EAGAIN;		goto fail;	}	return 0; fail:	for (j = 0; j < i; j++) {		if (!cpu_possible(j))			continue;		sock_release(per_cpu(__icmpv6_socket, j));	}	return err;}void icmpv6_cleanup(void){	int i;	for_each_possible_cpu(i) {		sock_release(per_cpu(__icmpv6_socket, i));	}	inet6_del_protocol(&icmpv6_protocol, IPPROTO_ICMPV6);}static const struct icmp6_err {	int err;	int fatal;} tab_unreach[] = {	{	/* NOROUTE */		.err	= ENETUNREACH,		.fatal	= 0,	},	{	/* ADM_PROHIBITED */		.err	= EACCES,		.fatal	= 1,	},	{	/* Was NOT_NEIGHBOUR, now reserved */		.err	= EHOSTUNREACH,		.fatal	= 0,	},	{	/* ADDR_UNREACH	*/		.err	= EHOSTUNREACH,		.fatal	= 0,	},	{	/* PORT_UNREACH	*/		.err	= ECONNREFUSED,		.fatal	= 1,	},};int icmpv6_err_convert(int type, int code, int *err){	int fatal = 0;	*err = EPROTO;	switch (type) {	case ICMPV6_DEST_UNREACH:		fatal = 1;		if (code <= ICMPV6_PORT_UNREACH) {			*err  = tab_unreach[code].err;			fatal = tab_unreach[code].fatal;		}		break;	case ICMPV6_PKT_TOOBIG:		*err = EMSGSIZE;		break;	case ICMPV6_PARAMPROB:		*err = EPROTO;		fatal = 1;		break;	case ICMPV6_TIME_EXCEED:		*err = EHOSTUNREACH;		break;	}	return fatal;}EXPORT_SYMBOL(icmpv6_err_convert);#ifdef CONFIG_SYSCTLctl_table ipv6_icmp_table[] = {	{		.ctl_name	= NET_IPV6_ICMP_RATELIMIT,		.procname	= "ratelimit",		.data		= &sysctl_icmpv6_time,		.maxlen		= sizeof(int),		.mode		= 0644,		.proc_handler	= &proc_dointvec	},	{ .ctl_name = 0 },};#endif

⌨️ 快捷键说明

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