📄 ndisc.c
字号:
err = xfrm_lookup(&dst, &fl, NULL, 0); if (err < 0) { dst_release(dst); return; } len = sizeof(struct icmp6hdr) + sizeof(struct in6_addr); send_llinfo = dev->addr_len && !ipv6_addr_any(saddr); if (send_llinfo) len += NDISC_OPT_SPACE(dev->addr_len); skb = sock_alloc_send_skb(sk, MAX_HEADER + len + LL_RESERVED_SPACE(dev), 1, &err); if (skb == NULL) { ND_PRINTK0(KERN_ERR "ICMPv6 NA: %s() failed to allocate an skb.\n", __FUNCTION__); dst_release(dst); return; } skb_reserve(skb, LL_RESERVED_SPACE(dev)); ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len); msg = (struct nd_msg *)skb_put(skb, len); skb->h.raw = (unsigned char*)msg; msg->icmph.icmp6_type = NDISC_NEIGHBOUR_SOLICITATION; msg->icmph.icmp6_code = 0; msg->icmph.icmp6_cksum = 0; msg->icmph.icmp6_unused = 0; /* Set the target address. */ ipv6_addr_copy(&msg->target, solicit); if (send_llinfo) ndisc_fill_option(msg->opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr, dev->addr_len); /* checksum */ msg->icmph.icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr, daddr, len, IPPROTO_ICMPV6, csum_partial((__u8 *) msg, len, 0)); /* send it! */ skb->dst = dst; idev = in6_dev_get(dst->dev); IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS); err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output); if (!err) { ICMP6_INC_STATS(idev, ICMP6_MIB_OUTNEIGHBORSOLICITS); ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS); } if (likely(idev != NULL)) in6_dev_put(idev);}void ndisc_send_rs(struct net_device *dev, struct in6_addr *saddr, struct in6_addr *daddr){ struct flowi fl; struct dst_entry* dst; struct inet6_dev *idev; struct sock *sk = ndisc_socket->sk; struct sk_buff *skb; struct icmp6hdr *hdr; __u8 * opt; int len; int err; ndisc_flow_init(&fl, NDISC_ROUTER_SOLICITATION, saddr, daddr); dst = ndisc_dst_alloc(dev, NULL, daddr, ip6_output); if (!dst) return; err = xfrm_lookup(&dst, &fl, NULL, 0); if (err < 0) { dst_release(dst); return; } len = sizeof(struct icmp6hdr); if (dev->addr_len) len += NDISC_OPT_SPACE(dev->addr_len); skb = sock_alloc_send_skb(sk, MAX_HEADER + len + LL_RESERVED_SPACE(dev), 1, &err); if (skb == NULL) { ND_PRINTK0(KERN_ERR "ICMPv6 RS: %s() failed to allocate an skb.\n", __FUNCTION__); dst_release(dst); return; } skb_reserve(skb, LL_RESERVED_SPACE(dev)); ip6_nd_hdr(sk, skb, dev, saddr, daddr, IPPROTO_ICMPV6, len); hdr = (struct icmp6hdr *)skb_put(skb, len); skb->h.raw = (unsigned char*)hdr; hdr->icmp6_type = NDISC_ROUTER_SOLICITATION; hdr->icmp6_code = 0; hdr->icmp6_cksum = 0; hdr->icmp6_unused = 0; opt = (u8*) (hdr + 1); if (dev->addr_len) ndisc_fill_option(opt, ND_OPT_SOURCE_LL_ADDR, dev->dev_addr, dev->addr_len); /* checksum */ hdr->icmp6_cksum = csum_ipv6_magic(&skb->nh.ipv6h->saddr, daddr, len, IPPROTO_ICMPV6, csum_partial((__u8 *) hdr, len, 0)); /* send it! */ skb->dst = dst; idev = in6_dev_get(dst->dev); IP6_INC_STATS(IPSTATS_MIB_OUTREQUESTS); err = NF_HOOK(PF_INET6, NF_IP6_LOCAL_OUT, skb, NULL, dst->dev, dst_output); if (!err) { ICMP6_INC_STATS(idev, ICMP6_MIB_OUTROUTERSOLICITS); ICMP6_INC_STATS(idev, ICMP6_MIB_OUTMSGS); } if (likely(idev != NULL)) in6_dev_put(idev);} 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(&skb->nh.ipv6h->saddr, dev, 1)) saddr = &skb->nh.ipv6h->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: " "%04x:%04x:%04x:%04x:%04x:%04x:%04x:%04x\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->h.raw; struct in6_addr *saddr = &skb->nh.ipv6h->saddr; struct in6_addr *daddr = &skb->nh.ipv6h->daddr; u8 *lladdr = NULL; int lladdrlen = 0; u32 ndoptlen = skb->tail - msg->opt; struct ndisc_options ndopts; struct net_device *dev = skb->dev; struct inet6_ifaddr *ifp; struct inet6_dev *idev = NULL; struct neighbour *neigh; int dad = ipv6_addr_any(saddr); int inc; 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 = (u8*)(ndopts.nd_opts_src_lladdr + 1); lladdrlen = ndopts.nd_opts_src_lladdr->nd_opt_len << 3; if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len)) { 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) { /* Address is tentative. If the source is unspecified address, it is someone does DAD, otherwise we ignore solicitations until DAD timer expires. */ if (!dad) goto out; if (dev->type == ARPHRD_IEEE802_TR) { unsigned char *sadr = skb->mac.raw; 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; } } addrconf_dad_failure(ifp); return; } 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 && pneigh_lookup(&nd_tbl, &msg->target, dev, 0))) { if (skb->stamp.tv_sec != 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; } if (dad) { struct in6_addr maddr; ipv6_addr_all_nodes(&maddr); ndisc_send_na(dev, NULL, &maddr, &msg->target, idev->cnf.forwarding, 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->hard_header) { ndisc_send_na(dev, neigh, saddr, &msg->target, idev->cnf.forwarding, 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->h.raw; struct in6_addr *saddr = &skb->nh.ipv6h->saddr; struct in6_addr *daddr = &skb->nh.ipv6h->daddr; u8 *lladdr = NULL; int lladdrlen = 0; u32 ndoptlen = skb->tail - 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 = (u8*)(ndopts.nd_opts_tgt_lladdr + 1); lladdrlen = ndopts.nd_opts_tgt_lladdr->nd_opt_len << 3; if (lladdrlen != NDISC_OPT_SPACE(dev->addr_len)) { 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; 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, NULL, NULL); } neigh_release(neigh); }}static void ndisc_router_discovery(struct sk_buff *skb){ struct ra_msg *ra_msg = (struct ra_msg *) skb->h.raw; struct neighbour *neigh; struct inet6_dev *in6_dev; struct rt6_info *rt; int lifetime; struct ndisc_options ndopts; int optlen; __u8 * opt = (__u8 *)(ra_msg + 1); optlen = (skb->tail - skb->h.raw) - sizeof(struct ra_msg); if (!(ipv6_addr_type(&skb->nh.ipv6h->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); lifetime = ntohs(ra_msg->icmph.icmp6_rt_lifetime); rt = rt6_get_dflt_router(&skb->nh.ipv6h->saddr, skb->dev); if (rt && lifetime == 0) { ip6_del_rt(rt, NULL, NULL); rt = NULL; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -