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

📄 ndisc.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
			rt->u.dst.metrics[RTAX_HOPLIMIT-1] = ra_msg->icmph.icmp6_hop_limit;	}skip_defrtr:	/*	 *	Update Reachable Time and Retrans Timer	 */	if (in6_dev->nd_parms) {		unsigned long rtime = ntohl(ra_msg->retrans_timer);		if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/HZ) {			rtime = (rtime*HZ)/1000;			if (rtime < HZ/10)				rtime = HZ/10;			in6_dev->nd_parms->retrans_time = rtime;			in6_dev->tstamp = jiffies;			inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);		}		rtime = ntohl(ra_msg->reachable_time);		if (rtime && rtime/1000 < MAX_SCHEDULE_TIMEOUT/(3*HZ)) {			rtime = (rtime*HZ)/1000;			if (rtime < HZ/10)				rtime = HZ/10;			if (rtime != in6_dev->nd_parms->base_reachable_time) {				in6_dev->nd_parms->base_reachable_time = rtime;				in6_dev->nd_parms->gc_staletime = 3 * rtime;				in6_dev->nd_parms->reachable_time = neigh_rand_reach_time(rtime);				in6_dev->tstamp = jiffies;				inet6_ifinfo_notify(RTM_NEWLINK, in6_dev);			}		}	}	/*	 *	Process options.	 */	if (!neigh)		neigh = __neigh_lookup(&nd_tbl, &ipv6_hdr(skb)->saddr,				       skb->dev, 1);	if (neigh) {		u8 *lladdr = NULL;		if (ndopts.nd_opts_src_lladdr) {			lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,						     skb->dev);			if (!lladdr) {				ND_PRINTK2(KERN_WARNING					   "ICMPv6 RA: invalid link-layer address length\n");				goto out;			}		}		neigh_update(neigh, lladdr, NUD_STALE,			     NEIGH_UPDATE_F_WEAK_OVERRIDE|			     NEIGH_UPDATE_F_OVERRIDE|			     NEIGH_UPDATE_F_OVERRIDE_ISROUTER|			     NEIGH_UPDATE_F_ISROUTER);	}#ifdef CONFIG_IPV6_ROUTE_INFO	if (in6_dev->cnf.accept_ra_rtr_pref && ndopts.nd_opts_ri) {		struct nd_opt_hdr *p;		for (p = ndopts.nd_opts_ri;		     p;		     p = ndisc_next_option(p, ndopts.nd_opts_ri_end)) {			if (((struct route_info *)p)->prefix_len > in6_dev->cnf.accept_ra_rt_info_max_plen)				continue;			rt6_route_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3,				      &ipv6_hdr(skb)->saddr);		}	}#endif	if (in6_dev->cnf.accept_ra_pinfo && ndopts.nd_opts_pi) {		struct nd_opt_hdr *p;		for (p = ndopts.nd_opts_pi;		     p;		     p = ndisc_next_option(p, ndopts.nd_opts_pi_end)) {			addrconf_prefix_rcv(skb->dev, (u8*)p, (p->nd_opt_len) << 3);		}	}	if (ndopts.nd_opts_mtu) {		__be32 n;		u32 mtu;		memcpy(&n, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));		mtu = ntohl(n);		if (mtu < IPV6_MIN_MTU || mtu > skb->dev->mtu) {			ND_PRINTK2(KERN_WARNING				   "ICMPv6 RA: invalid mtu: %d\n",				   mtu);		} else if (in6_dev->cnf.mtu6 != mtu) {			in6_dev->cnf.mtu6 = mtu;			if (rt)				rt->u.dst.metrics[RTAX_MTU-1] = mtu;			rt6_mtu_change(skb->dev, mtu);		}	}	if (ndopts.nd_useropts) {		struct nd_opt_hdr *opt;		for (opt = ndopts.nd_useropts;		     opt;		     opt = ndisc_next_useropt(opt, ndopts.nd_useropts_end)) {				ndisc_ra_useropt(skb, opt);		}	}	if (ndopts.nd_opts_tgt_lladdr || ndopts.nd_opts_rh) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 RA: invalid RA options");	}out:	if (rt)		dst_release(&rt->u.dst);	else if (neigh)		neigh_release(neigh);	in6_dev_put(in6_dev);}static void ndisc_redirect_rcv(struct sk_buff *skb){	struct inet6_dev *in6_dev;	struct icmp6hdr *icmph;	struct in6_addr *dest;	struct in6_addr *target;	/* new first hop to destination */	struct neighbour *neigh;	int on_link = 0;	struct ndisc_options ndopts;	int optlen;	u8 *lladdr = NULL;	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 Redirect: source address is not link-local.\n");		return;	}	optlen = skb->tail - skb->transport_header;	optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);	if (optlen < 0) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 Redirect: packet too short\n");		return;	}	icmph = icmp6_hdr(skb);	target = (struct in6_addr *) (icmph + 1);	dest = target + 1;	if (ipv6_addr_is_multicast(dest)) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 Redirect: destination address is multicast.\n");		return;	}	if (ipv6_addr_equal(dest, target)) {		on_link = 1;	} else if (ipv6_addr_type(target) !=		   (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 Redirect: target address is not link-local unicast.\n");		return;	}	in6_dev = in6_dev_get(skb->dev);	if (!in6_dev)		return;	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_redirects) {		in6_dev_put(in6_dev);		return;	}	/* RFC2461 8.1:	 *	The IP source address of the Redirect MUST be the same as the current	 *	first-hop router for the specified ICMP Destination Address.	 */	if (!ndisc_parse_options((u8*)(dest + 1), optlen, &ndopts)) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 Redirect: invalid ND options\n");		in6_dev_put(in6_dev);		return;	}	if (ndopts.nd_opts_tgt_lladdr) {		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr,					     skb->dev);		if (!lladdr) {			ND_PRINTK2(KERN_WARNING				   "ICMPv6 Redirect: invalid link-layer address length\n");			in6_dev_put(in6_dev);			return;		}	}	neigh = __neigh_lookup(&nd_tbl, target, skb->dev, 1);	if (neigh) {		rt6_redirect(dest, &ipv6_hdr(skb)->daddr,			     &ipv6_hdr(skb)->saddr, neigh, lladdr,			     on_link);		neigh_release(neigh);	}	in6_dev_put(in6_dev);}void ndisc_send_redirect(struct sk_buff *skb, struct neighbour *neigh,			 struct in6_addr *target){	struct sock *sk = ndisc_socket->sk;	int len = sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);	struct sk_buff *buff;	struct icmp6hdr *icmph;	struct in6_addr saddr_buf;	struct in6_addr *addrp;	struct net_device *dev;	struct rt6_info *rt;	struct dst_entry *dst;	struct inet6_dev *idev;	struct flowi fl;	u8 *opt;	int rd_len;	int err;	int hlen;	u8 ha_buf[MAX_ADDR_LEN], *ha = NULL;	dev = skb->dev;	if (ipv6_get_lladdr(dev, &saddr_buf, IFA_F_TENTATIVE)) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 Redirect: no link-local address on %s\n",			   dev->name);		return;	}	if (!ipv6_addr_equal(&ipv6_hdr(skb)->daddr, target) &&	    ipv6_addr_type(target) != (IPV6_ADDR_UNICAST|IPV6_ADDR_LINKLOCAL)) {		ND_PRINTK2(KERN_WARNING			"ICMPv6 Redirect: target address is not link-local unicast.\n");		return;	}	ndisc_flow_init(&fl, NDISC_REDIRECT, &saddr_buf, &ipv6_hdr(skb)->saddr,			dev->ifindex);	dst = ip6_route_output(NULL, &fl);	if (dst == NULL)		return;	err = xfrm_lookup(&dst, &fl, NULL, 0);	if (err)		return;	rt = (struct rt6_info *) dst;	if (rt->rt6i_flags & RTF_GATEWAY) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 Redirect: destination is not a neighbour.\n");		dst_release(dst);		return;	}	if (!xrlim_allow(dst, 1*HZ)) {		dst_release(dst);		return;	}	if (dev->addr_len) {		read_lock_bh(&neigh->lock);		if (neigh->nud_state & NUD_VALID) {			memcpy(ha_buf, neigh->ha, dev->addr_len);			read_unlock_bh(&neigh->lock);			ha = ha_buf;			len += ndisc_opt_addr_space(dev);		} else			read_unlock_bh(&neigh->lock);	}	rd_len = min_t(unsigned int,		     IPV6_MIN_MTU-sizeof(struct ipv6hdr)-len, skb->len + 8);	rd_len &= ~0x7;	len += rd_len;	buff = sock_alloc_send_skb(sk,				   (MAX_HEADER + sizeof(struct ipv6hdr) +				    len + LL_RESERVED_SPACE(dev)),				   1, &err);	if (buff == NULL) {		ND_PRINTK0(KERN_ERR			   "ICMPv6 Redirect: %s() failed to allocate an skb.\n",			   __FUNCTION__);		dst_release(dst);		return;	}	hlen = 0;	skb_reserve(buff, LL_RESERVED_SPACE(dev));	ip6_nd_hdr(sk, buff, dev, &saddr_buf, &ipv6_hdr(skb)->saddr,		   IPPROTO_ICMPV6, len);	skb_set_transport_header(buff, skb_tail_pointer(buff) - buff->data);	skb_put(buff, len);	icmph = icmp6_hdr(buff);	memset(icmph, 0, sizeof(struct icmp6hdr));	icmph->icmp6_type = NDISC_REDIRECT;	/*	 *	copy target and destination addresses	 */	addrp = (struct in6_addr *)(icmph + 1);	ipv6_addr_copy(addrp, target);	addrp++;	ipv6_addr_copy(addrp, &ipv6_hdr(skb)->daddr);	opt = (u8*) (addrp + 1);	/*	 *	include target_address option	 */	if (ha)		opt = ndisc_fill_addr_option(opt, ND_OPT_TARGET_LL_ADDR, ha,					     dev->addr_len, dev->type);	/*	 *	build redirect option and copy skb over to the new packet.	 */	memset(opt, 0, 8);	*(opt++) = ND_OPT_REDIRECT_HDR;	*(opt++) = (rd_len >> 3);	opt += 6;	memcpy(opt, ipv6_hdr(skb), rd_len - 8);	icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &ipv6_hdr(skb)->saddr,					     len, IPPROTO_ICMPV6,					     csum_partial((u8 *) icmph, len, 0));	buff->dst = dst;	idev = in6_dev_get(dst->dev);	IP6_INC_STATS(idev, IPSTATS_MIB_OUTREQUESTS);	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, buff, NULL, dst->dev, dst_output);	if (!err) {		ICMP6MSGOUT_INC_STATS(idev, NDISC_REDIRECT);		ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);	}	if (likely(idev != NULL))		in6_dev_put(idev);}static void pndisc_redo(struct sk_buff *skb){	ndisc_recv_ns(skb);	kfree_skb(skb);}int ndisc_rcv(struct sk_buff *skb){	struct nd_msg *msg;	if (!pskb_may_pull(skb, skb->len))		return 0;	msg = (struct nd_msg *)skb_transport_header(skb);	__skb_push(skb, skb->data - skb_transport_header(skb));	if (ipv6_hdr(skb)->hop_limit != 255) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 NDISC: invalid hop-limit: %d\n",			   ipv6_hdr(skb)->hop_limit);		return 0;	}	if (msg->icmph.icmp6_code != 0) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 NDISC: invalid ICMPv6 code: %d\n",			   msg->icmph.icmp6_code);		return 0;	}	memset(NEIGH_CB(skb), 0, sizeof(struct neighbour_cb));	switch (msg->icmph.icmp6_type) {	case NDISC_NEIGHBOUR_SOLICITATION:		ndisc_recv_ns(skb);		break;	case NDISC_NEIGHBOUR_ADVERTISEMENT:		ndisc_recv_na(skb);		break;	case NDISC_ROUTER_SOLICITATION:		ndisc_recv_rs(skb);		break;	case NDISC_ROUTER_ADVERTISEMENT:		ndisc_router_discovery(skb);		break;	case NDISC_REDIRECT:		ndisc_redirect_rcv(skb);		break;	}	return 0;}static int ndisc_netdev_event(struct notifier_block *this, unsigned long event, void *ptr){	struct net_device *dev = ptr;	if (dev->nd_net != &init_net)		return NOTIFY_DONE;	switch (event) {	case NETDEV_CHANGEADDR:		neigh_changeaddr(&nd_tbl, dev);		fib6_run_gc(~0UL);		break;	case NETDEV_DOWN:		neigh_ifdown(&nd_tbl, dev);		fib6_run_gc(~0UL);		break;	default:		break;	}	return NOTIFY_DONE;}static struct notifier_block ndisc_netdev_notifier = {	.notifier_call = ndisc_netdev_event,};#ifdef CONFIG_SYSCTLstatic void ndisc_warn_deprecated_sysctl(struct ctl_table *ctl,					 const char *func, const char *dev_name){	static char warncomm[TASK_COMM_LEN];	static int warned;	if (strcmp(warncomm, current->comm) && warned < 5) {		strcpy(warncomm, current->comm);		printk(KERN_WARNING			"process `%s' is using deprecated sysctl (%s) "			"net.ipv6.neigh.%s.%s; "			"Use net.ipv6.neigh.%s.%s_ms "			"instead.\n",			warncomm, func,			dev_name, ctl->procname,			dev_name, ctl->procname);		warned++;	}}int ndisc_ifinfo_sysctl_change(struct ctl_table *ctl, int write, struct file * filp, void __user *buffer, size_t *lenp, loff_t *ppos){	struct net_device *dev = ctl->extra1;	struct inet6_dev *idev;	int ret;	if ((strcmp(ctl->procname, "retrans_time") == 0) ||	    (strcmp(ctl->procname, "base_reachable_time") == 0))		ndisc_warn_deprecated_sysctl(ctl, "syscall", dev ? dev->name : "default");	if (strcmp(ctl->procname, "retrans_time") == 0)		ret = proc_dointvec(ctl, write, filp, buffer, lenp, ppos);	else if (strcmp(ctl->procname, "base_reachable_time") == 0)		ret = proc_dointvec_jiffies(ctl, write,					    filp, buffer, lenp, ppos);	else if ((strcmp(ctl->procname, "retrans_time_ms") == 0) ||		 (strcmp(ctl->procname, "base_reachable_time_ms") == 0))		ret = proc_dointvec_ms_jiffies(ctl, write,					       filp, buffer, lenp, ppos);	else		ret = -1;	if (write && ret == 0 && dev && (idev = in6_dev_get(dev)) != NULL) {		if (ctl->data == &idev->nd_parms->base_reachable_time)			idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);		idev->tstamp = jiffies;		inet6_ifinfo_notify(RTM_NEWLINK, idev);		in6_dev_put(idev);	}	return ret;}static int ndisc_ifinfo_sysctl_strategy(ctl_table *ctl, int __user *name,					int nlen, void __user *oldval,					size_t __user *oldlenp,					void __user *newval, size_t newlen){	struct net_device *dev = ctl->extra1;	struct inet6_dev *idev;	int ret;	if (ctl->ctl_name == NET_NEIGH_RETRANS_TIME ||	    ctl->ctl_name == NET_NEIGH_REACHABLE_TIME)		ndisc_warn_deprecated_sysctl(ctl, "procfs", dev ? dev->name : "default");	switch (ctl->ctl_name) {	case NET_NEIGH_REACHABLE_TIME:		ret = sysctl_jiffies(ctl, name, nlen,				     oldval, oldlenp, newval, newlen);		break;	case NET_NEIGH_RETRANS_TIME_MS:	case NET_NEIGH_REACHABLE_TIME_MS:		 ret = sysctl_ms_jiffies(ctl, name, nlen,					 oldval, oldlenp, newval, newlen);		 break;	default:		ret = 0;	}	if (newval && newlen && ret > 0 &&	    dev && (idev = in6_dev_get(dev)) != NULL) {		if (ctl->ctl_name == NET_NEIGH_REACHABLE_TIME ||		    ctl->ctl_name == NET_NEIGH_REACHABLE_TIME_MS)			idev->nd_parms->reachable_time = neigh_rand_reach_time(idev->nd_parms->base_reachable_time);		idev->tstamp = jiffies;		inet6_ifinfo_notify(RTM_NEWLINK, idev);		in6_dev_put(idev);	}	return ret;}#endifint __init ndisc_init(struct net_proto_family *ops){	struct ipv6_pinfo *np;	struct sock *sk;	int err;	err = sock_create_kern(PF_INET6, SOCK_RAW, IPPROTO_ICMPV6, &ndisc_socket);	if (err < 0) {		ND_PRINTK0(KERN_ERR			   "ICMPv6 NDISC: Failed to initialize the control socket (err %d).\n",			   err);		ndisc_socket = NULL; /* For safety. */		return err;	}	sk = ndisc_socket->sk;	np = inet6_sk(sk);	sk->sk_allocation = GFP_ATOMIC;	np->hop_limit = 255;	/* Do not loopback ndisc messages */	np->mc_loop = 0;	sk->sk_prot->unhash(sk);	/*	 * Initialize the neighbour table	 */	neigh_table_init(&nd_tbl);#ifdef CONFIG_SYSCTL	neigh_sysctl_register(NULL, &nd_tbl.parms, NET_IPV6, NET_IPV6_NEIGH,			      "ipv6",			      &ndisc_ifinfo_sysctl_change,			      &ndisc_ifinfo_sysctl_strategy);#endif	register_netdevice_notifier(&ndisc_netdev_notifier);	return 0;}void ndisc_cleanup(void){	unregister_netdevice_notifier(&ndisc_netdev_notifier);#ifdef CONFIG_SYSCTL	neigh_sysctl_unregister(&nd_tbl.parms);#endif	neigh_table_clear(&nd_tbl);	sock_release(ndisc_socket);	ndisc_socket = NULL; /* For safety. */}

⌨️ 快捷键说明

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