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

📄 ndisc.c

📁 Linux Kernel 2.6.9 for OMAP1710
💻 C
📖 第 1 页 / 共 3 页
字号:
	if (rt == NULL && lifetime) {		ND_PRINTK3(KERN_DEBUG			   "ICMPv6 RA: adding default router.\n");		rt = rt6_add_dflt_router(&skb->nh.ipv6h->saddr, skb->dev);		if (rt == NULL) {			ND_PRINTK0(KERN_ERR				   "ICMPv6 RA: %s() failed to add default route.\n",				   __FUNCTION__);			in6_dev_put(in6_dev);			return;		}		neigh = rt->rt6i_nexthop;		if (neigh == NULL) {			ND_PRINTK0(KERN_ERR				   "ICMPv6 RA: %s() got default router without neighbour.\n",				   __FUNCTION__);			dst_release(&rt->u.dst);			in6_dev_put(in6_dev);			return;		}		neigh->flags |= NTF_ROUTER;		/*		 *	If we where using an "all destinations on link" route		 *	delete it		 */		rt6_purge_dflt_routers(RTF_ALLONLINK);	}	if (rt)		rt->rt6i_expires = jiffies + (HZ * lifetime);	if (ra_msg->icmph.icmp6_hop_limit)		in6_dev->cnf.hop_limit = ra_msg->icmph.icmp6_hop_limit;	/*	 *	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 (rt && (neigh = rt->rt6i_nexthop) != NULL) {		u8 *lladdr = NULL;		int lladdrlen;		if (ndopts.nd_opts_src_lladdr) {			lladdr = (u8*)((ndopts.nd_opts_src_lladdr)+1);			lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3;			if (lladdrlen != NDISC_OPT_SPACE(skb->dev->addr_len)) {				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);	}	if (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) {		u32 mtu;		memcpy(&mtu, ((u8*)(ndopts.nd_opts_mtu+1))+2, sizeof(mtu));		mtu = ntohl(mtu);		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_opts_tgt_lladdr || ndopts.nd_opts_rh) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 RA: invalid RA options");	}out:	if (rt)		dst_release(&rt->u.dst);	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;	int lladdrlen;	if (!(ipv6_addr_type(&skb->nh.ipv6h->saddr) & IPV6_ADDR_LINKLOCAL)) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 Redirect: source address is not link-local.\n");		return;	}	optlen = skb->tail - skb->h.raw;	optlen -= sizeof(struct icmp6hdr) + 2 * sizeof(struct in6_addr);	if (optlen < 0) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 Redirect: packet too short\n");		return;	}	icmph = (struct icmp6hdr *) skb->h.raw;	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_cmp(dest, target) == 0) {		on_link = 1;	} else if (!(ipv6_addr_type(target) & IPV6_ADDR_LINKLOCAL)) {		ND_PRINTK2(KERN_WARNING 			   "ICMPv6 Redirect: target address is not link-local.\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 = (u8*)(ndopts.nd_opts_tgt_lladdr + 1);		lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3;		if (lladdrlen != NDISC_OPT_SPACE(skb->dev->addr_len)) {			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, &skb->nh.ipv6h->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;	dev = skb->dev;	if (ipv6_get_lladdr(dev, &saddr_buf)) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 Redirect: no link-local address on %s\n",			   dev->name); 		return; 	}	ndisc_flow_init(&fl, NDISC_REDIRECT, &saddr_buf, &skb->nh.ipv6h->saddr);	rt = rt6_lookup(&skb->nh.ipv6h->saddr, NULL, dev->ifindex, 1);	if (rt == NULL)		return;	dst = &rt->u.dst;	err = xfrm_lookup(&dst, &fl, NULL, 0);	if (err) {		dst_release(dst);		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) {		if (neigh->nud_state&NUD_VALID) {			len  += NDISC_OPT_SPACE(dev->addr_len);		} else {			/* If nexthop is not valid, do not redirect!			   We will make it later, when will be sure,			   that it is alive.			 */			dst_release(dst);			return;		}	}	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 + 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, &skb->nh.ipv6h->saddr,		   IPPROTO_ICMPV6, len);	icmph = (struct icmp6hdr *)skb_put(buff, len);	buff->h.raw = (unsigned char*)icmph;	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, &skb->nh.ipv6h->daddr);	opt = (u8*) (addrp + 1);	/*	 *	include target_address option	 */	if (dev->addr_len)		opt = ndisc_fill_option(opt, ND_OPT_TARGET_LL_ADDR, neigh->ha, dev->addr_len);	/*	 *	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, skb->nh.ipv6h, rd_len - 8);	icmph->icmp6_cksum = csum_ipv6_magic(&saddr_buf, &skb->nh.ipv6h->saddr,					     len, IPPROTO_ICMPV6,					     csum_partial((u8 *) icmph, len, 0));	buff->dst = dst;	idev = in6_dev_get(dst->dev);	IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS);	err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, buff, NULL, dst->dev, dst_output);	if (!err) {		ICMP6_INC_STATS(idev, ICMP6_MIB_OUTREDIRECTS);		ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS);	}	if (likely(idev != NULL))		in6_dev_put(idev);}static void pndisc_redo(struct sk_buff *skb){	ndisc_rcv(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->h.raw;	__skb_push(skb, skb->data-skb->h.raw);	if (skb->nh.ipv6h->hop_limit != 255) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 NDISC: invalid hop-limit: %d\n",			   skb->nh.ipv6h->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;	}	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_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;	switch (event) {	case NETDEV_CHANGEADDR:		neigh_changeaddr(&nd_tbl, dev);		fib6_run_gc(0);		break;	case NETDEV_DOWN:		neigh_ifdown(&nd_tbl, dev);		fib6_run_gc(0);		break;	default:		break;	}	return NOTIFY_DONE;}static struct notifier_block ndisc_netdev_notifier = {	.notifier_call = ndisc_netdev_event,};#ifdef CONFIG_SYSCTLint 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;	if (write && dev && (idev = in6_dev_get(dev)) != NULL) {		idev->tstamp = jiffies;		inet6_ifinfo_notify(RTM_NEWLINK, idev);		in6_dev_put(idev);	}	return proc_dointvec(ctl, write, filp, buffer, lenp, ppos);}#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);#endif	register_netdevice_notifier(&ndisc_netdev_notifier);	return 0;}void ndisc_cleanup(void){#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 + -