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

📄 ndisc.c

📁 linux 内核源代码
💻 C
📖 第 1 页 / 共 3 页
字号:
	__ndisc_send(dev, neigh, daddr, saddr,		     &icmp6h, solicit,		     !ipv6_addr_any(saddr) ? ND_OPT_SOURCE_LL_ADDR : 0);}void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr,		   struct in6_addr *daddr){	struct icmp6hdr icmp6h = {		.icmp6_type = NDISC_ROUTER_SOLICITATION,	};	int send_sllao = dev->addr_len;#ifdef CONFIG_IPV6_OPTIMISTIC_DAD	/*	 * According to section 2.2 of RFC 4429, we must not	 * send router solicitations with a sllao from	 * optimistic addresses, but we may send the solicitation	 * if we don't include the sllao.  So here we check	 * if our address is optimistic, and if so, we	 * suppress the inclusion of the sllao.	 */	if (send_sllao) {		struct inet6_ifaddr *ifp = ipv6_get_ifaddr(saddr, dev, 1);		if (ifp) {			if (ifp->flags & IFA_F_OPTIMISTIC)  {				send_sllao = 0;			}			in6_ifa_put(ifp);		} else {			send_sllao = 0;		}	}#endif	__ndisc_send(dev, NULL, daddr, saddr,		     &icmp6h, NULL,		     send_sllao ? ND_OPT_SOURCE_LL_ADDR : 0);}static void ndisc_error_report(struct neighbour *neigh, struct sk_buff *skb){	/*	 *	"The sender MUST return an ICMP	 *	 destination unreachable"	 */	dst_link_failure(skb);	kfree_skb(skb);}/* Called with locked neigh: either read or both */static void ndisc_solicit(struct neighbour *neigh, struct sk_buff *skb){	struct in6_addr *saddr = NULL;	struct in6_addr mcaddr;	struct net_device *dev = neigh->dev;	struct in6_addr *target = (struct in6_addr *)&neigh->primary_key;	int probes = atomic_read(&neigh->probes);	if (skb && ipv6_chk_addr(&ipv6_hdr(skb)->saddr, dev, 1))		saddr = &ipv6_hdr(skb)->saddr;	if ((probes -= neigh->parms->ucast_probes) < 0) {		if (!(neigh->nud_state & NUD_VALID)) {			ND_PRINTK1(KERN_DEBUG				   "%s(): trying to ucast probe in NUD_INVALID: "				   NIP6_FMT "\n",				   __FUNCTION__,				   NIP6(*target));		}		ndisc_send_ns(dev, neigh, target, target, saddr);	} else if ((probes -= neigh->parms->app_probes) < 0) {#ifdef CONFIG_ARPD		neigh_app_ns(neigh);#endif	} else {		addrconf_addr_solict_mult(target, &mcaddr);		ndisc_send_ns(dev, NULL, target, &mcaddr, saddr);	}}static void ndisc_recv_ns(struct sk_buff *skb){	struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);	struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;	struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;	u8 *lladdr = NULL;	u32 ndoptlen = skb->tail - (skb->transport_header +				    offsetof(struct nd_msg, opt));	struct ndisc_options ndopts;	struct net_device *dev = skb->dev;	struct inet6_ifaddr *ifp;	struct inet6_dev *idev = NULL;	struct neighbour *neigh;	struct pneigh_entry *pneigh = NULL;	int dad = ipv6_addr_any(saddr);	int inc;	int is_router;	if (ipv6_addr_is_multicast(&msg->target)) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 NS: multicast target address");		return;	}	/*	 * RFC2461 7.1.1:	 * DAD has to be destined for solicited node multicast address.	 */	if (dad &&	    !(daddr->s6_addr32[0] == htonl(0xff020000) &&	      daddr->s6_addr32[1] == htonl(0x00000000) &&	      daddr->s6_addr32[2] == htonl(0x00000001) &&	      daddr->s6_addr [12] == 0xff )) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 NS: bad DAD packet (wrong destination)\n");		return;	}	if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 NS: invalid ND options\n");		return;	}	if (ndopts.nd_opts_src_lladdr) {		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr, dev);		if (!lladdr) {			ND_PRINTK2(KERN_WARNING				   "ICMPv6 NS: invalid link-layer address length\n");			return;		}		/* RFC2461 7.1.1:		 *	If the IP source address is the unspecified address,		 *	there MUST NOT be source link-layer address option		 *	in the message.		 */		if (dad) {			ND_PRINTK2(KERN_WARNING				   "ICMPv6 NS: bad DAD packet (link-layer address option)\n");			return;		}	}	inc = ipv6_addr_is_multicast(daddr);	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1)) != NULL) {		if (ifp->flags & (IFA_F_TENTATIVE|IFA_F_OPTIMISTIC)) {			if (dad) {				if (dev->type == ARPHRD_IEEE802_TR) {					const unsigned char *sadr;					sadr = skb_mac_header(skb);					if (((sadr[8] ^ dev->dev_addr[0]) & 0x7f) == 0 &&					    sadr[9] == dev->dev_addr[1] &&					    sadr[10] == dev->dev_addr[2] &&					    sadr[11] == dev->dev_addr[3] &&					    sadr[12] == dev->dev_addr[4] &&					    sadr[13] == dev->dev_addr[5]) {						/* looped-back to us */						goto out;					}				}				/*				 * We are colliding with another node				 * who is doing DAD				 * so fail our DAD process				 */				addrconf_dad_failure(ifp);				return;			} else {				/*				 * This is not a dad solicitation.				 * If we are an optimistic node,				 * we should respond.				 * Otherwise, we should ignore it.				 */				if (!(ifp->flags & IFA_F_OPTIMISTIC))					goto out;			}		}		idev = ifp->idev;	} else {		idev = in6_dev_get(dev);		if (!idev) {			/* XXX: count this drop? */			return;		}		if (ipv6_chk_acast_addr(dev, &msg->target) ||		    (idev->cnf.forwarding &&		     (ipv6_devconf.proxy_ndp || idev->cnf.proxy_ndp) &&		     (pneigh = pneigh_lookup(&nd_tbl,					     &msg->target, dev, 0)) != NULL)) {			if (!(NEIGH_CB(skb)->flags & LOCALLY_ENQUEUED) &&			    skb->pkt_type != PACKET_HOST &&			    inc != 0 &&			    idev->nd_parms->proxy_delay != 0) {				/*				 * for anycast or proxy,				 * sender should delay its response				 * by a random time between 0 and				 * MAX_ANYCAST_DELAY_TIME seconds.				 * (RFC2461) -- yoshfuji				 */				struct sk_buff *n = skb_clone(skb, GFP_ATOMIC);				if (n)					pneigh_enqueue(&nd_tbl, idev->nd_parms, n);				goto out;			}		} else			goto out;	}	is_router = !!(pneigh ? pneigh->flags & NTF_ROUTER : idev->cnf.forwarding);	if (dad) {		struct in6_addr maddr;		ipv6_addr_all_nodes(&maddr);		ndisc_send_na(dev, NULL, &maddr, &msg->target,			      is_router, 0, (ifp != NULL), 1);		goto out;	}	if (inc)		NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_mcast);	else		NEIGH_CACHE_STAT_INC(&nd_tbl, rcv_probes_ucast);	/*	 *	update / create cache entry	 *	for the source address	 */	neigh = __neigh_lookup(&nd_tbl, saddr, dev,			       !inc || lladdr || !dev->addr_len);	if (neigh)		neigh_update(neigh, lladdr, NUD_STALE,			     NEIGH_UPDATE_F_WEAK_OVERRIDE|			     NEIGH_UPDATE_F_OVERRIDE);	if (neigh || !dev->header_ops) {		ndisc_send_na(dev, neigh, saddr, &msg->target,			      is_router,			      1, (ifp != NULL && inc), inc);		if (neigh)			neigh_release(neigh);	}out:	if (ifp)		in6_ifa_put(ifp);	else		in6_dev_put(idev);	return;}static void ndisc_recv_na(struct sk_buff *skb){	struct nd_msg *msg = (struct nd_msg *)skb_transport_header(skb);	struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;	struct in6_addr *daddr = &ipv6_hdr(skb)->daddr;	u8 *lladdr = NULL;	u32 ndoptlen = skb->tail - (skb->transport_header +				    offsetof(struct nd_msg, opt));	struct ndisc_options ndopts;	struct net_device *dev = skb->dev;	struct inet6_ifaddr *ifp;	struct neighbour *neigh;	if (skb->len < sizeof(struct nd_msg)) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 NA: packet too short\n");		return;	}	if (ipv6_addr_is_multicast(&msg->target)) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 NA: target address is multicast.\n");		return;	}	if (ipv6_addr_is_multicast(daddr) &&	    msg->icmph.icmp6_solicited) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 NA: solicited NA is multicasted.\n");		return;	}	if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts)) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 NS: invalid ND option\n");		return;	}	if (ndopts.nd_opts_tgt_lladdr) {		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_tgt_lladdr, dev);		if (!lladdr) {			ND_PRINTK2(KERN_WARNING				   "ICMPv6 NA: invalid link-layer address length\n");			return;		}	}	if ((ifp = ipv6_get_ifaddr(&msg->target, dev, 1))) {		if (ifp->flags & IFA_F_TENTATIVE) {			addrconf_dad_failure(ifp);			return;		}		/* What should we make now? The advertisement		   is invalid, but ndisc specs say nothing		   about it. It could be misconfiguration, or		   an smart proxy agent tries to help us :-)		 */		ND_PRINTK1(KERN_WARNING			   "ICMPv6 NA: someone advertises our address on %s!\n",			   ifp->idev->dev->name);		in6_ifa_put(ifp);		return;	}	neigh = neigh_lookup(&nd_tbl, &msg->target, dev);	if (neigh) {		u8 old_flags = neigh->flags;		if (neigh->nud_state & NUD_FAILED)			goto out;		/*		 * Don't update the neighbor cache entry on a proxy NA from		 * ourselves because either the proxied node is off link or it		 * has already sent a NA to us.		 */		if (lladdr && !memcmp(lladdr, dev->dev_addr, dev->addr_len) &&		    ipv6_devconf.forwarding && ipv6_devconf.proxy_ndp &&		    pneigh_lookup(&nd_tbl, &msg->target, dev, 0)) {			/* XXX: idev->cnf.prixy_ndp */			goto out;		}		neigh_update(neigh, lladdr,			     msg->icmph.icmp6_solicited ? NUD_REACHABLE : NUD_STALE,			     NEIGH_UPDATE_F_WEAK_OVERRIDE|			     (msg->icmph.icmp6_override ? NEIGH_UPDATE_F_OVERRIDE : 0)|			     NEIGH_UPDATE_F_OVERRIDE_ISROUTER|			     (msg->icmph.icmp6_router ? NEIGH_UPDATE_F_ISROUTER : 0));		if ((old_flags & ~neigh->flags) & NTF_ROUTER) {			/*			 * Change: router to host			 */			struct rt6_info *rt;			rt = rt6_get_dflt_router(saddr, dev);			if (rt)				ip6_del_rt(rt);		}out:		neigh_release(neigh);	}}static void ndisc_recv_rs(struct sk_buff *skb){	struct rs_msg *rs_msg = (struct rs_msg *)skb_transport_header(skb);	unsigned long ndoptlen = skb->len - sizeof(*rs_msg);	struct neighbour *neigh;	struct inet6_dev *idev;	struct in6_addr *saddr = &ipv6_hdr(skb)->saddr;	struct ndisc_options ndopts;	u8 *lladdr = NULL;	if (skb->len < sizeof(*rs_msg))		return;	idev = in6_dev_get(skb->dev);	if (!idev) {		if (net_ratelimit())			ND_PRINTK1("ICMP6 RS: can't find in6 device\n");		return;	}	/* Don't accept RS if we're not in router mode */	if (!idev->cnf.forwarding)		goto out;	/*	 * Don't update NCE if src = ::;	 * this implies that the source node has no ip address assigned yet.	 */	if (ipv6_addr_any(saddr))		goto out;	/* Parse ND options */	if (!ndisc_parse_options(rs_msg->opt, ndoptlen, &ndopts)) {		if (net_ratelimit())			ND_PRINTK2("ICMP6 NS: invalid ND option, ignored\n");		goto out;	}	if (ndopts.nd_opts_src_lladdr) {		lladdr = ndisc_opt_addr_data(ndopts.nd_opts_src_lladdr,					     skb->dev);		if (!lladdr)			goto out;	}	neigh = __neigh_lookup(&nd_tbl, saddr, skb->dev, 1);	if (neigh) {		neigh_update(neigh, lladdr, NUD_STALE,			     NEIGH_UPDATE_F_WEAK_OVERRIDE|			     NEIGH_UPDATE_F_OVERRIDE|			     NEIGH_UPDATE_F_OVERRIDE_ISROUTER);		neigh_release(neigh);	}out:	in6_dev_put(idev);}static void ndisc_ra_useropt(struct sk_buff *ra, struct nd_opt_hdr *opt){	struct icmp6hdr *icmp6h = (struct icmp6hdr *)skb_transport_header(ra);	struct sk_buff *skb;	struct nlmsghdr *nlh;	struct nduseroptmsg *ndmsg;	int err;	int base_size = NLMSG_ALIGN(sizeof(struct nduseroptmsg)				    + (opt->nd_opt_len << 3));	size_t msg_size = base_size + nla_total_size(sizeof(struct in6_addr));	skb = nlmsg_new(msg_size, GFP_ATOMIC);	if (skb == NULL) {		err = -ENOBUFS;		goto errout;	}	nlh = nlmsg_put(skb, 0, 0, RTM_NEWNDUSEROPT, base_size, 0);	if (nlh == NULL) {		goto nla_put_failure;	}	ndmsg = nlmsg_data(nlh);	ndmsg->nduseropt_family = AF_INET6;	ndmsg->nduseropt_ifindex = ra->dev->ifindex;	ndmsg->nduseropt_icmp_type = icmp6h->icmp6_type;	ndmsg->nduseropt_icmp_code = icmp6h->icmp6_code;	ndmsg->nduseropt_opts_len = opt->nd_opt_len << 3;	memcpy(ndmsg + 1, opt, opt->nd_opt_len << 3);	NLA_PUT(skb, NDUSEROPT_SRCADDR, sizeof(struct in6_addr),		&ipv6_hdr(ra)->saddr);	nlmsg_end(skb, nlh);	err = rtnl_notify(skb, 0, RTNLGRP_ND_USEROPT, NULL, GFP_ATOMIC);	if (err < 0)		goto errout;	return;nla_put_failure:	nlmsg_free(skb);	err = -EMSGSIZE;errout:	rtnl_set_sk_err(RTNLGRP_ND_USEROPT, err);}static void ndisc_router_discovery(struct sk_buff *skb){	struct ra_msg *ra_msg = (struct ra_msg *)skb_transport_header(skb);	struct neighbour *neigh = NULL;	struct inet6_dev *in6_dev;	struct rt6_info *rt = NULL;	int lifetime;	struct ndisc_options ndopts;	int optlen;	unsigned int pref = 0;	__u8 * opt = (__u8 *)(ra_msg + 1);	optlen = (skb->tail - skb->transport_header) - sizeof(struct ra_msg);	if (!(ipv6_addr_type(&ipv6_hdr(skb)->saddr) & IPV6_ADDR_LINKLOCAL)) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 RA: source address is not link-local.\n");		return;	}	if (optlen < 0) {		ND_PRINTK2(KERN_WARNING			   "ICMPv6 RA: packet too short\n");		return;	}	/*	 *	set the RA_RECV flag in the interface	 */	in6_dev = in6_dev_get(skb->dev);	if (in6_dev == NULL) {		ND_PRINTK0(KERN_ERR			   "ICMPv6 RA: can't find inet6 device for %s.\n",			   skb->dev->name);		return;	}	if (in6_dev->cnf.forwarding || !in6_dev->cnf.accept_ra) {		in6_dev_put(in6_dev);		return;	}	if (!ndisc_parse_options(opt, optlen, &ndopts)) {		in6_dev_put(in6_dev);		ND_PRINTK2(KERN_WARNING			   "ICMP6 RA: invalid ND options\n");		return;	}	if (in6_dev->if_flags & IF_RS_SENT) {		/*		 *	flag that an RA was received after an RS was sent		 *	out on this interface.		 */		in6_dev->if_flags |= IF_RA_RCVD;	}	/*	 * Remember the managed/otherconf flags from most recently	 * received RA message (RFC 2462) -- yoshfuji	 */	in6_dev->if_flags = (in6_dev->if_flags & ~(IF_RA_MANAGED |				IF_RA_OTHERCONF)) |				(ra_msg->icmph.icmp6_addrconf_managed ?					IF_RA_MANAGED : 0) |				(ra_msg->icmph.icmp6_addrconf_other ?					IF_RA_OTHERCONF : 0);	if (!in6_dev->cnf.accept_ra_defrtr)		goto skip_defrtr;	lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime);#ifdef CONFIG_IPV6_ROUTER_PREF	pref = ra_msg->icmph.icmp6_router_pref;	/* 10b is handled as if it were 00b (medium) */	if (pref == ICMPV6_ROUTER_PREF_INVALID ||	    !in6_dev->cnf.accept_ra_rtr_pref)		pref = ICMPV6_ROUTER_PREF_MEDIUM;#endif	rt = rt6_get_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev);	if (rt)		neigh = rt->rt6i_nexthop;	if (rt && lifetime == 0) {		neigh_clone(neigh);		ip6_del_rt(rt);		rt = NULL;	}	if (rt == NULL && lifetime) {		ND_PRINTK3(KERN_DEBUG			   "ICMPv6 RA: adding default router.\n");		rt = rt6_add_dflt_router(&ipv6_hdr(skb)->saddr, skb->dev, pref);		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;	} else if (rt) {		rt->rt6i_flags |= (rt->rt6i_flags & ~RTF_PREF_MASK) | RTF_PREF(pref);	}	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;		if (rt)

⌨️ 快捷键说明

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