📄 addrconf.c
字号:
rtmsg.rtmsg_ifindex = ifp->idev->dev->ifindex; ip6_route_add(&rtmsg); }}/* * Duplicate Address Detection */static void addrconf_dad_start(struct inet6_ifaddr *ifp){ struct device *dev; unsigned long rand_num; dev = ifp->idev->dev; addrconf_join_solict(dev, &ifp->addr); if (ifp->prefix_len != 128 && (ifp->flags&ADDR_PERMANENT)) addrconf_prefix_route(&ifp->addr, ifp->prefix_len, dev, 0, RTF_ADDRCONF); if (dev->flags&(IFF_NOARP|IFF_LOOPBACK)) { start_bh_atomic(); ifp->flags &= ~DAD_INCOMPLETE; addrconf_dad_completed(ifp); end_bh_atomic(); return; } net_srandom(ifp->addr.s6_addr32[3]); ifp->probes = ifp->idev->cnf.dad_transmits; ifp->flags |= DAD_INCOMPLETE; rand_num = net_random() % ifp->idev->cnf.rtr_solicit_delay; ifp->timer.function = addrconf_dad_timer; ifp->timer.expires = jiffies + rand_num; add_timer(&ifp->timer);}static void addrconf_dad_timer(unsigned long data){ struct inet6_ifaddr *ifp; struct in6_addr unspec; struct in6_addr mcaddr; ifp = (struct inet6_ifaddr *) data; if (ifp->probes == 0) { /* * DAD was successful */ ifp->flags &= ~DAD_INCOMPLETE; addrconf_dad_completed(ifp); return; } ifp->probes--; /* send a neighbour solicitation for our addr */ memset(&unspec, 0, sizeof(unspec));#ifdef CONFIG_IPV6_EUI64 addrconf_addr_solict_mult_new(&ifp->addr, &mcaddr); ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);#endif#ifndef CONFIG_IPV6_NO_PB addrconf_addr_solict_mult_old(&ifp->addr, &mcaddr); ndisc_send_ns(ifp->idev->dev, NULL, &ifp->addr, &mcaddr, &unspec);#endif ifp->timer.expires = jiffies + ifp->idev->cnf.rtr_solicit_interval; add_timer(&ifp->timer);}static void addrconf_dad_completed(struct inet6_ifaddr *ifp){ struct device * dev = ifp->idev->dev; /* * Configure the address for reception. Now it is valid. */ ipv6_ifa_notify(RTM_NEWADDR, ifp); /* If added prefix is link local and forwarding is off, start sending router solicitations. */ if (ifp->idev->cnf.forwarding == 0 && (dev->flags&IFF_LOOPBACK) == 0 && (ipv6_addr_type(&ifp->addr) & IPV6_ADDR_LINKLOCAL)) { struct in6_addr all_routers; ipv6_addr_all_routers(&all_routers); /* * If a host as already performed a random delay * [...] as part of DAD [...] there is no need * to delay again before sending the first RS */ ndisc_send_rs(ifp->idev->dev, &ifp->addr, &all_routers); ifp->probes = 1; ifp->timer.function = addrconf_rs_timer; ifp->timer.expires = (jiffies + ifp->idev->cnf.rtr_solicit_interval); ifp->idev->if_flags |= IF_RS_SENT; add_timer(&ifp->timer); }}#ifdef CONFIG_PROC_FSstatic int iface_proc_info(char *buffer, char **start, off_t offset, int length, int dummy){ struct inet6_ifaddr *ifp; int i; int len = 0; off_t pos=0; off_t begin=0; addrconf_lock(); for (i=0; i < IN6_ADDR_HSIZE; i++) { for (ifp=inet6_addr_lst[i]; ifp; ifp=ifp->lst_next) { int j; for (j=0; j<16; j++) { sprintf(buffer + len, "%02x", ifp->addr.s6_addr[j]); len += 2; } len += sprintf(buffer + len, " %02x %02x %02x %02x %8s\n", ifp->idev->dev->ifindex, ifp->prefix_len, ifp->scope, ifp->flags, ifp->idev->dev->name); pos=begin+len; if(pos<offset) { len=0; begin=pos; } if(pos>offset+length) goto done; } }done: addrconf_unlock(); *start=buffer+(offset-begin); len-=(offset-begin); if(len>length) len=length; if(len<0) len=0; return len;}struct proc_dir_entry iface_proc_entry ={ 0, 8, "if_inet6", S_IFREG | S_IRUGO, 1, 0, 0, 0, NULL, &iface_proc_info};#endif /* CONFIG_PROC_FS *//* * Periodic address status verification */void addrconf_verify(unsigned long foo){ struct inet6_ifaddr *ifp; unsigned long now = jiffies; int i; if (atomic_read(&addr_list_lock)) { addr_chk_timer.expires = jiffies + 1*HZ; add_timer(&addr_chk_timer); return; } for (i=0; i < IN6_ADDR_HSIZE; i++) { for (ifp=inet6_addr_lst[i]; ifp;) { if (ifp->flags & ADDR_INVALID) { struct inet6_ifaddr *bp = ifp; ifp= ifp->lst_next; ipv6_del_addr(bp); continue; } if (!(ifp->flags & ADDR_PERMANENT)) { struct inet6_ifaddr *bp; unsigned long age; age = (now - ifp->tstamp) / HZ; bp = ifp; ifp= ifp->lst_next; if (age > bp->valid_lft) ipv6_del_addr(bp); else if (age > bp->prefered_lft) { bp->flags |= ADDR_DEPRECATED; ipv6_ifa_notify(0, bp); } continue; } ifp = ifp->lst_next; } } addr_chk_timer.expires = jiffies + ADDR_CHECK_FREQUENCY; add_timer(&addr_chk_timer);}#ifdef CONFIG_RTNETLINKstatic intinet6_rtm_deladdr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg){ struct rtattr **rta = arg; struct ifaddrmsg *ifm = NLMSG_DATA(nlh); struct in6_addr *pfx; pfx = NULL; if (rta[IFA_ADDRESS-1]) { if (RTA_PAYLOAD(rta[IFA_ADDRESS-1]) < sizeof(*pfx)) return -EINVAL; pfx = RTA_DATA(rta[IFA_ADDRESS-1]); } if (rta[IFA_LOCAL-1]) { if (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx))) return -EINVAL; pfx = RTA_DATA(rta[IFA_LOCAL-1]); } if (pfx == NULL) return -EINVAL; return inet6_addr_del(ifm->ifa_index, pfx, ifm->ifa_prefixlen);}static intinet6_rtm_newaddr(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg){ struct rtattr **rta = arg; struct ifaddrmsg *ifm = NLMSG_DATA(nlh); struct in6_addr *pfx; pfx = NULL; if (rta[IFA_ADDRESS-1]) { if (RTA_PAYLOAD(rta[IFA_ADDRESS-1]) < sizeof(*pfx)) return -EINVAL; pfx = RTA_DATA(rta[IFA_ADDRESS-1]); } if (rta[IFA_LOCAL-1]) { if (pfx && memcmp(pfx, RTA_DATA(rta[IFA_LOCAL-1]), sizeof(*pfx))) return -EINVAL; pfx = RTA_DATA(rta[IFA_LOCAL-1]); } if (pfx == NULL) return -EINVAL; return inet6_addr_add(ifm->ifa_index, pfx, ifm->ifa_prefixlen);}static int inet6_fill_ifaddr(struct sk_buff *skb, struct inet6_ifaddr *ifa, u32 pid, u32 seq, int event){ struct ifaddrmsg *ifm; struct nlmsghdr *nlh; struct ifa_cacheinfo ci; unsigned char *b = skb->tail; nlh = NLMSG_PUT(skb, pid, seq, event, sizeof(*ifm)); ifm = NLMSG_DATA(nlh); ifm->ifa_family = AF_INET6; ifm->ifa_prefixlen = ifa->prefix_len; ifm->ifa_flags = ifa->flags & ~ADDR_INVALID; ifm->ifa_scope = RT_SCOPE_UNIVERSE; if (ifa->scope&IFA_HOST) ifm->ifa_scope = RT_SCOPE_HOST; else if (ifa->scope&IFA_LINK) ifm->ifa_scope = RT_SCOPE_LINK; else if (ifa->scope&IFA_SITE) ifm->ifa_scope = RT_SCOPE_SITE; ifm->ifa_index = ifa->idev->dev->ifindex; RTA_PUT(skb, IFA_ADDRESS, 16, &ifa->addr); if (!(ifa->flags&IFA_F_PERMANENT)) { ci.ifa_prefered = ifa->prefered_lft; ci.ifa_valid = ifa->valid_lft; if (ci.ifa_prefered != 0xFFFFFFFF) { long tval = (jiffies - ifa->tstamp)/HZ; ci.ifa_prefered -= tval; if (ci.ifa_valid != 0xFFFFFFFF) ci.ifa_valid -= tval; } RTA_PUT(skb, IFA_CACHEINFO, sizeof(ci), &ci); } nlh->nlmsg_len = skb->tail - b; return skb->len;nlmsg_failure:rtattr_failure: skb_trim(skb, b - skb->data); return -1;}static int inet6_dump_ifaddr(struct sk_buff *skb, struct netlink_callback *cb){ int idx, ip_idx; int s_idx, s_ip_idx; struct inet6_ifaddr *ifa; s_idx = cb->args[0]; s_ip_idx = ip_idx = cb->args[1]; for (idx=0; idx < IN6_ADDR_HSIZE; idx++) { if (idx < s_idx) continue; if (idx > s_idx) s_ip_idx = 0; start_bh_atomic(); for (ifa=inet6_addr_lst[idx], ip_idx = 0; ifa; ifa = ifa->lst_next, ip_idx++) { if (ip_idx < s_ip_idx) continue; if (inet6_fill_ifaddr(skb, ifa, NETLINK_CB(cb->skb).pid, cb->nlh->nlmsg_seq, RTM_NEWADDR) <= 0) { end_bh_atomic(); goto done; } } end_bh_atomic(); }done: cb->args[0] = idx; cb->args[1] = ip_idx; return skb->len;}static void inet6_ifa_notify(int event, struct inet6_ifaddr *ifa){ struct sk_buff *skb; int size = NLMSG_SPACE(sizeof(struct ifaddrmsg)+128); skb = alloc_skb(size, GFP_ATOMIC); if (!skb) { netlink_set_err(rtnl, 0, RTMGRP_IPV6_IFADDR, ENOBUFS); return; } if (inet6_fill_ifaddr(skb, ifa, 0, 0, event) < 0) { kfree_skb(skb); netlink_set_err(rtnl, 0, RTMGRP_IPV6_IFADDR, EINVAL); return; } NETLINK_CB(skb).dst_groups = RTMGRP_IPV6_IFADDR; netlink_broadcast(rtnl, skb, 0, RTMGRP_IPV6_IFADDR, GFP_ATOMIC);}static struct rtnetlink_link inet6_rtnetlink_table[RTM_MAX-RTM_BASE+1] ={ { NULL, NULL, }, { NULL, NULL, }, { NULL, NULL, }, { NULL, NULL, }, { inet6_rtm_newaddr, NULL, }, { inet6_rtm_deladdr, NULL, }, { NULL, inet6_dump_ifaddr, }, { NULL, NULL, }, { inet6_rtm_newroute, NULL, }, { inet6_rtm_delroute, NULL, }, { inet6_rtm_getroute, inet6_dump_fib, }, { NULL, NULL, },};#endifstatic void ipv6_ifa_notify(int event, struct inet6_ifaddr *ifp){#ifdef CONFIG_RTNETLINK inet6_ifa_notify(event ? : RTM_NEWADDR, ifp);#endif switch (event) { case RTM_NEWADDR: ip6_rt_addr_add(&ifp->addr, ifp->idev->dev); break; case RTM_DELADDR: start_bh_atomic(); addrconf_leave_solict(ifp->idev->dev, &ifp->addr); if (ipv6_chk_addr(&ifp->addr, ifp->idev->dev, 0) == NULL) ip6_rt_addr_del(&ifp->addr, ifp->idev->dev); end_bh_atomic(); break; }}#ifdef CONFIG_SYSCTLstaticint addrconf_sysctl_forward(ctl_table *ctl, int write, struct file * filp, void *buffer, size_t *lenp){ int *valp = ctl->data; int val = *valp; int ret; ret = proc_dointvec(ctl, write, filp, buffer, lenp); if (write && *valp != val && valp != &ipv6_devconf_dflt.forwarding) { struct inet6_dev *idev = NULL; if (valp != &ipv6_devconf.forwarding) { struct device *dev = dev_get_by_index(ctl->ctl_name); if (dev) idev = ipv6_get_idev(dev); if (idev == NULL) return ret; } else ipv6_devconf_dflt.forwarding = ipv6_devconf.forwarding; addrconf_forward_change(idev); if (*valp) { start_bh_atomic(); rt6_purge_dflt_routers(0); end_bh_atomic(); } } return ret;}static struct addrconf_sysctl_table{ struct ctl_table_header *sysctl_header; ctl_table addrconf_vars[11]; ctl_table addrconf_dev[2]; ctl_table addrconf_conf_dir[2]; ctl_table addrconf_proto_dir[2]; ctl_table addrconf_root_dir[2];} addrconf_sysctl = { NULL, {{NET_IPV6_FORWARDING, "forwarding", &ipv6_devconf.forwarding, sizeof(int), 0644, NULL, &addrconf_sysctl_forward}, {NET_IPV6_HOP_LIMIT, "hop_limit", &ipv6_devconf.hop_limit, sizeof(int), 0644, NULL, &proc_dointvec}, {NET_IPV6_MTU, "mtu", &ipv6_devconf.mtu6, sizeof(int), 0644, NULL, &proc_dointvec}, {NET_IPV6_ACCEPT_RA, "accept_ra", &ipv6_devconf.accept_ra, sizeof(int), 0644, NULL, &proc_dointvec}, {NET_IPV6_ACCEPT_REDIRECTS, "accept_redirects", &ipv6_devconf.accept_redirects, sizeof(int), 0644, NULL, &proc_dointvec}, {NET_IPV6_AUTOCONF, "autoconf", &ipv6_devconf.autoconf, sizeof(int), 0644, NULL, &proc_dointvec}, {NET_IPV6_DAD_TRANSMITS, "dad_transmits", &ipv6_devconf.dad_transmits, sizeof(int), 0644, NULL, &proc_dointvec}, {NET_IPV6_RTR_SOLICITS, "router_solicitations", &ipv6_devconf.rtr_solicits, sizeof(int), 0644, NULL, &proc_dointvec}, {NET_IPV6_RTR_SOLICIT_INTERVAL, "router_solicitation_interval", &ipv6_devconf.rtr_solicit_interval, sizeof(int), 0644, NULL, &proc_dointvec_jiffies}, {NET_IPV6_RTR_SOLICIT_DELAY, "router_solicitation_delay", &ipv6_devconf.rtr_solicit_delay, sizeof(int), 0644, NULL, &proc_dointvec_jiffies}, {0}}, {{NET_PROTO_CONF_ALL, "all", NULL, 0, 0555, addrconf_sysctl.addrconf_vars},{0}}, {{NET_IPV6_CONF, "conf", NULL, 0, 0555, addrconf_sysctl.addrconf_dev},{0}}, {{NET_IPV6, "ipv6", NULL, 0, 0555, addrconf_sysctl.addrconf_conf_dir},{0}}, {{CTL_NET, "net", NULL, 0, 0555, addrconf_sysctl.addrconf_proto_dir},{0}}};static void addrconf_sysctl_register(struct inet6_dev *idev, struct ipv6_devconf *p){ int i; struct device *dev = idev ? idev->dev : NULL; struct addrconf_sysctl_table *t; t = kmalloc(sizeof(*t), GFP_KERNEL); if (t == NULL) return; memcpy(t, &addrconf_sysctl, sizeof(*t)); for (i=0; i<sizeof(t->addrconf_vars)/sizeof(t->addrconf_vars[0])-1; i++) { t->addrconf_vars[i].data += (char*)p - (char*)&ipv6_devconf; t->addrconf_vars[i].de = NULL; } if (dev) { t->addrconf_dev[0].procname = dev->name; t->addrconf_dev[0].ctl_name = dev->ifindex; } else { t->addrconf_dev[0].procname = "default"; t->addrconf_dev[0].ctl_name = NET_PROTO_CONF_DEFAULT; } t->addrconf_dev[0].child = t->addrconf_vars; t->addrconf_dev[0].de = NULL; t->addrconf_conf_dir[0].child = t->addrconf_dev; t->addrconf_conf_dir[0].de = NULL; t->addrconf_proto_dir[0].child = t->addrconf_conf_dir; t->addrconf_proto_dir[0].de = NULL; t->addrconf_root_dir[0].child = t->addrconf_proto_dir; t->addrconf_root_dir[0].de = NULL; t->sysctl_header = register_sysctl_table(t->addrconf_root_dir, 0); if (t->sysctl_header == NULL) kfree(t); else p->sysctl = t;}static void addrconf_sysctl_unregister(struct ipv6_devconf *p){ if (p->sysctl) { struct addrconf_sysctl_table *t = p->sysctl; p->sysctl = NULL; unregister_sysctl_table(t->sysctl_header); kfree(t); }}#endif/* * Init / cleanup code */__initfunc(void addrconf_init(void)){#ifdef MODULE struct device *dev; /* This takes sense only during module load. */ for (dev = dev_base; dev; dev = dev->next) { if (!(dev->flags&IFF_UP)) continue; switch (dev->type) { case ARPHRD_LOOPBACK: init_loopback(dev); break; case ARPHRD_ETHER: addrconf_dev_config(dev); break; default: /* Ignore all other */ } }#endif #ifdef CONFIG_PROC_FS proc_net_register(&iface_proc_entry);#endif addr_chk_timer.expires = jiffies + ADDR_CHECK_FREQUENCY; add_timer(&addr_chk_timer);#ifdef CONFIG_RTNETLINK rtnetlink_links[PF_INET6] = inet6_rtnetlink_table;#endif#ifdef CONFIG_SYSCTL addrconf_sysctl.sysctl_header = register_sysctl_table(addrconf_sysctl.addrconf_root_dir, 0); addrconf_sysctl_register(NULL, &ipv6_devconf_dflt);#endif}#ifdef MODULEvoid addrconf_cleanup(void){ struct inet6_dev *idev; struct inet6_ifaddr *ifa; int i;#ifdef CONFIG_RTNETLINK rtnetlink_links[PF_INET6] = NULL;#endif#ifdef CONFIG_SYSCTL addrconf_sysctl_unregister(&ipv6_devconf_dflt); addrconf_sysctl_unregister(&ipv6_devconf);#endif del_timer(&addr_chk_timer); /* * clean dev list. */ for (i=0; i < IN6_ADDR_HSIZE; i++) { struct inet6_dev *next; for (idev = inet6_dev_lst[i]; idev; idev = next) { next = idev->next; addrconf_ifdown(idev->dev, 1); } } start_bh_atomic(); /* * clean addr_list */ for (i=0; i < IN6_ADDR_HSIZE; i++) { for (ifa=inet6_addr_lst[i]; ifa; ) { struct inet6_ifaddr *bifa; bifa = ifa; ifa = ifa->lst_next; printk(KERN_DEBUG "bug: IPv6 address leakage detected: ifa=%p\n", bifa); /* Do not free it; something is wrong. Now we can investigate it with debugger. */ } } end_bh_atomic();#ifdef CONFIG_PROC_FS proc_net_unregister(iface_proc_entry.low_ino);#endif}#endif /* MODULE */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -