📄 route.c
字号:
int err; struct fib6_table *table; if (rt == &ip6_null_entry) return -ENOENT; table = rt->rt6i_table; write_lock_bh(&table->tb6_lock); err = fib6_del(rt, info); dst_release(&rt->u.dst); write_unlock_bh(&table->tb6_lock); return err;}int ip6_del_rt(struct rt6_info *rt){ return __ip6_del_rt(rt, NULL);}static int ip6_route_del(struct fib6_config *cfg){ struct fib6_table *table; struct fib6_node *fn; struct rt6_info *rt; int err = -ESRCH; table = fib6_get_table(cfg->fc_table); if (table == NULL) return err; read_lock_bh(&table->tb6_lock); fn = fib6_locate(&table->tb6_root, &cfg->fc_dst, cfg->fc_dst_len, &cfg->fc_src, cfg->fc_src_len); if (fn) { for (rt = fn->leaf; rt; rt = rt->u.dst.rt6_next) { if (cfg->fc_ifindex && (rt->rt6i_dev == NULL || rt->rt6i_dev->ifindex != cfg->fc_ifindex)) continue; if (cfg->fc_flags & RTF_GATEWAY && !ipv6_addr_equal(&cfg->fc_gateway, &rt->rt6i_gateway)) continue; if (cfg->fc_metric && cfg->fc_metric != rt->rt6i_metric) continue; dst_hold(&rt->u.dst); read_unlock_bh(&table->tb6_lock); return __ip6_del_rt(rt, &cfg->fc_nlinfo); } } read_unlock_bh(&table->tb6_lock); return err;}/* * Handle redirects */struct ip6rd_flowi { struct flowi fl; struct in6_addr gateway;};static struct rt6_info *__ip6_route_redirect(struct fib6_table *table, struct flowi *fl, int flags){ struct ip6rd_flowi *rdfl = (struct ip6rd_flowi *)fl; struct rt6_info *rt; struct fib6_node *fn; /* * Get the "current" route for this destination and * check if the redirect has come from approriate router. * * RFC 2461 specifies that redirects should only be * accepted if they come from the nexthop to the target. * Due to the way the routes are chosen, this notion * is a bit fuzzy and one might need to check all possible * routes. */ read_lock_bh(&table->tb6_lock); fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);restart: for (rt = fn->leaf; rt; rt = rt->u.dst.rt6_next) { /* * Current route is on-link; redirect is always invalid. * * Seems, previous statement is not true. It could * be node, which looks for us as on-link (f.e. proxy ndisc) * But then router serving it might decide, that we should * know truth 8)8) --ANK (980726). */ if (rt6_check_expired(rt)) continue; if (!(rt->rt6i_flags & RTF_GATEWAY)) continue; if (fl->oif != rt->rt6i_dev->ifindex) continue; if (!ipv6_addr_equal(&rdfl->gateway, &rt->rt6i_gateway)) continue; break; } if (!rt) rt = &ip6_null_entry; BACKTRACK(&fl->fl6_src);out: dst_hold(&rt->u.dst); read_unlock_bh(&table->tb6_lock); return rt;};static struct rt6_info *ip6_route_redirect(struct in6_addr *dest, struct in6_addr *src, struct in6_addr *gateway, struct net_device *dev){ int flags = RT6_LOOKUP_F_HAS_SADDR; struct ip6rd_flowi rdfl = { .fl = { .oif = dev->ifindex, .nl_u = { .ip6_u = { .daddr = *dest, .saddr = *src, }, }, }, .gateway = *gateway, }; if (rt6_need_strict(dest)) flags |= RT6_LOOKUP_F_IFACE; return (struct rt6_info *)fib6_rule_lookup((struct flowi *)&rdfl, flags, __ip6_route_redirect);}void rt6_redirect(struct in6_addr *dest, struct in6_addr *src, struct in6_addr *saddr, struct neighbour *neigh, u8 *lladdr, int on_link){ struct rt6_info *rt, *nrt = NULL; struct netevent_redirect netevent; rt = ip6_route_redirect(dest, src, saddr, neigh->dev); if (rt == &ip6_null_entry) { if (net_ratelimit()) printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop " "for redirect target\n"); goto out; } /* * We have finally decided to accept it. */ neigh_update(neigh, lladdr, NUD_STALE, NEIGH_UPDATE_F_WEAK_OVERRIDE| NEIGH_UPDATE_F_OVERRIDE| (on_link ? 0 : (NEIGH_UPDATE_F_OVERRIDE_ISROUTER| NEIGH_UPDATE_F_ISROUTER)) ); /* * Redirect received -> path was valid. * Look, redirects are sent only in response to data packets, * so that this nexthop apparently is reachable. --ANK */ dst_confirm(&rt->u.dst); /* Duplicate redirect: silently ignore. */ if (neigh == rt->u.dst.neighbour) goto out; nrt = ip6_rt_copy(rt); if (nrt == NULL) goto out; nrt->rt6i_flags = RTF_GATEWAY|RTF_UP|RTF_DYNAMIC|RTF_CACHE; if (on_link) nrt->rt6i_flags &= ~RTF_GATEWAY; ipv6_addr_copy(&nrt->rt6i_dst.addr, dest); nrt->rt6i_dst.plen = 128; nrt->u.dst.flags |= DST_HOST; ipv6_addr_copy(&nrt->rt6i_gateway, (struct in6_addr*)neigh->primary_key); nrt->rt6i_nexthop = neigh_clone(neigh); /* Reset pmtu, it may be better */ nrt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(neigh->dev); nrt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(dst_mtu(&nrt->u.dst)); if (ip6_ins_rt(nrt)) goto out; netevent.old = &rt->u.dst; netevent.new = &nrt->u.dst; call_netevent_notifiers(NETEVENT_REDIRECT, &netevent); if (rt->rt6i_flags&RTF_CACHE) { ip6_del_rt(rt); return; }out: dst_release(&rt->u.dst); return;}/* * Handle ICMP "packet too big" messages * i.e. Path MTU discovery */void rt6_pmtu_discovery(struct in6_addr *daddr, struct in6_addr *saddr, struct net_device *dev, u32 pmtu){ struct rt6_info *rt, *nrt; int allfrag = 0; rt = rt6_lookup(daddr, saddr, dev->ifindex, 0); if (rt == NULL) return; if (pmtu >= dst_mtu(&rt->u.dst)) goto out; if (pmtu < IPV6_MIN_MTU) { /* * According to RFC2460, PMTU is set to the IPv6 Minimum Link * MTU (1280) and a fragment header should always be included * after a node receiving Too Big message reporting PMTU is * less than the IPv6 Minimum Link MTU. */ pmtu = IPV6_MIN_MTU; allfrag = 1; } /* New mtu received -> path was valid. They are sent only in response to data packets, so that this nexthop apparently is reachable. --ANK */ dst_confirm(&rt->u.dst); /* Host route. If it is static, it would be better not to override it, but add new one, so that when cache entry will expire old pmtu would return automatically. */ if (rt->rt6i_flags & RTF_CACHE) { rt->u.dst.metrics[RTAX_MTU-1] = pmtu; if (allfrag) rt->u.dst.metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG; dst_set_expires(&rt->u.dst, ip6_rt_mtu_expires); rt->rt6i_flags |= RTF_MODIFIED|RTF_EXPIRES; goto out; } /* Network route. Two cases are possible: 1. It is connected route. Action: COW 2. It is gatewayed route or NONEXTHOP route. Action: clone it. */ if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) nrt = rt6_alloc_cow(rt, daddr, saddr); else nrt = rt6_alloc_clone(rt, daddr); if (nrt) { nrt->u.dst.metrics[RTAX_MTU-1] = pmtu; if (allfrag) nrt->u.dst.metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG; /* According to RFC 1981, detecting PMTU increase shouldn't be * happened within 5 mins, the recommended timer is 10 mins. * Here this route expiration time is set to ip6_rt_mtu_expires * which is 10 mins. After 10 mins the decreased pmtu is expired * and detecting PMTU increase will be automatically happened. */ dst_set_expires(&nrt->u.dst, ip6_rt_mtu_expires); nrt->rt6i_flags |= RTF_DYNAMIC|RTF_EXPIRES; ip6_ins_rt(nrt); }out: dst_release(&rt->u.dst);}/* * Misc support functions */static struct rt6_info * ip6_rt_copy(struct rt6_info *ort){ struct rt6_info *rt = ip6_dst_alloc(); if (rt) { rt->u.dst.input = ort->u.dst.input; rt->u.dst.output = ort->u.dst.output; memcpy(rt->u.dst.metrics, ort->u.dst.metrics, RTAX_MAX*sizeof(u32)); rt->u.dst.error = ort->u.dst.error; rt->u.dst.dev = ort->u.dst.dev; if (rt->u.dst.dev) dev_hold(rt->u.dst.dev); rt->rt6i_idev = ort->rt6i_idev; if (rt->rt6i_idev) in6_dev_hold(rt->rt6i_idev); rt->u.dst.lastuse = jiffies; rt->rt6i_expires = 0; ipv6_addr_copy(&rt->rt6i_gateway, &ort->rt6i_gateway); rt->rt6i_flags = ort->rt6i_flags & ~RTF_EXPIRES; rt->rt6i_metric = 0; memcpy(&rt->rt6i_dst, &ort->rt6i_dst, sizeof(struct rt6key));#ifdef CONFIG_IPV6_SUBTREES memcpy(&rt->rt6i_src, &ort->rt6i_src, sizeof(struct rt6key));#endif rt->rt6i_table = ort->rt6i_table; } return rt;}#ifdef CONFIG_IPV6_ROUTE_INFOstatic struct rt6_info *rt6_get_route_info(struct in6_addr *prefix, int prefixlen, struct in6_addr *gwaddr, int ifindex){ struct fib6_node *fn; struct rt6_info *rt = NULL; struct fib6_table *table; table = fib6_get_table(RT6_TABLE_INFO); if (table == NULL) return NULL; write_lock_bh(&table->tb6_lock); fn = fib6_locate(&table->tb6_root, prefix ,prefixlen, NULL, 0); if (!fn) goto out; for (rt = fn->leaf; rt; rt = rt->u.dst.rt6_next) { if (rt->rt6i_dev->ifindex != ifindex) continue; if ((rt->rt6i_flags & (RTF_ROUTEINFO|RTF_GATEWAY)) != (RTF_ROUTEINFO|RTF_GATEWAY)) continue; if (!ipv6_addr_equal(&rt->rt6i_gateway, gwaddr)) continue; dst_hold(&rt->u.dst); break; }out: write_unlock_bh(&table->tb6_lock); return rt;}static struct rt6_info *rt6_add_route_info(struct in6_addr *prefix, int prefixlen, struct in6_addr *gwaddr, int ifindex, unsigned pref){ struct fib6_config cfg = { .fc_table = RT6_TABLE_INFO, .fc_metric = 1024, .fc_ifindex = ifindex, .fc_dst_len = prefixlen, .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_ROUTEINFO | RTF_UP | RTF_PREF(pref), }; ipv6_addr_copy(&cfg.fc_dst, prefix); ipv6_addr_copy(&cfg.fc_gateway, gwaddr); /* We should treat it as a default route if prefix length is 0. */ if (!prefixlen) cfg.fc_flags |= RTF_DEFAULT; ip6_route_add(&cfg); return rt6_get_route_info(prefix, prefixlen, gwaddr, ifindex);}#endifstruct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *dev){ struct rt6_info *rt; struct fib6_table *table; table = fib6_get_table(RT6_TABLE_DFLT); if (table == NULL) return NULL; write_lock_bh(&table->tb6_lock); for (rt = table->tb6_root.leaf; rt; rt=rt->u.dst.rt6_next) { if (dev == rt->rt6i_dev && ((rt->rt6i_flags & (RTF_ADDRCONF | RTF_DEFAULT)) == (RTF_ADDRCONF | RTF_DEFAULT)) && ipv6_addr_equal(&rt->rt6i_gateway, addr)) break; } if (rt) dst_hold(&rt->u.dst); write_unlock_bh(&table->tb6_lock); return rt;}struct rt6_info *rt6_add_dflt_router(struct in6_addr *gwaddr, struct net_device *dev, unsigned int pref){ struct fib6_config cfg = { .fc_table = RT6_TABLE_DFLT, .fc_metric = 1024, .fc_ifindex = dev->ifindex, .fc_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | RTF_UP | RTF_EXPIRES | RTF_PREF(pref), }; ipv6_addr_copy(&cfg.fc_gateway, gwaddr); ip6_route_add(&cfg); return rt6_get_dflt_router(gwaddr, dev);}void rt6_purge_dflt_routers(void){ struct rt6_info *rt; struct fib6_table *table; /* NOTE: Keep consistent with rt6_get_dflt_router */ table = fib6_get_table(RT6_TABLE_DFLT); if (table == NULL) return;restart: read_lock_bh(&table->tb6_lock); for (rt = table->tb6_root.leaf; rt; rt = rt->u.dst.rt6_next) { if (rt->rt6i_flags & (RTF_DEFAULT | RTF_ADDRCONF)) { dst_hold(&rt->u.dst); read_unlock_bh(&table->tb6_lock); ip6_del_rt(rt); goto restart; } } read_unlock_bh(&table->tb6_lock);}static void rtmsg_to_fib6_config(struct in6_rtmsg *rtmsg, struct fib6_config *cfg){ memset(cfg, 0, sizeof(*cfg)); cfg->fc_table = RT6_TABLE_MAIN; cfg->fc_ifindex = rtmsg->rtmsg_ifindex; cfg->fc_metric = rtmsg->rtmsg_metric; cfg->fc_expires = rtmsg->rtmsg_info; cfg->fc_dst_len = rtmsg->rtmsg_dst_len; cfg->fc_src_len = rtmsg->rtmsg_src_len; cfg->fc_flags = rtmsg->rtmsg_flags; ipv6_addr_copy(&cfg->fc_dst, &rtmsg->rtmsg_dst); ipv6_addr_copy(&cfg->fc_src, &rtmsg->rtmsg_src); ipv6_addr_copy(&cfg->fc_gateway, &rtmsg->rtmsg_gateway);}int ipv6_route_ioctl(unsigned int cmd, void __user *arg){ struct fib6_config cfg; struct in6_rtmsg rtmsg; int err; switch(cmd) { case SIOCADDRT: /* Add a route */ case SIOCDELRT: /* Delete a route */ if (!capable(CAP_NET_ADMIN)) return -EPERM; err = copy_from_user(&rtmsg, arg, sizeof(struct in6_rtmsg)); if (err) return -EFAULT; rtmsg_to_fib6_config(&rtmsg, &cfg); rtnl_lock(); switch (cmd) { case SIOCADDRT: err = ip6_route_add(&cfg); break; case SIOCDELRT: err = ip6_route_del(&cfg); break; default: err = -EINVAL; } rtnl_unlock(); return err; } return -EINVAL;}/* * Drop the packet on the floor */static inline int ip6_pkt_drop(struct sk_buff *skb, int code, int ipstats_mib_noroutes){ int type; switch (ipstats_mib_noroutes) { case IPSTATS_MIB_INNOROUTES: type = ipv6_addr_type(&ipv6_hdr(skb)->daddr); if (type == IPV6_ADDR_ANY || type == IPV6_ADDR_RESERVED) { IP6_INC_STATS(ip6_dst_idev(skb->dst), IPSTATS_MIB_INADDRERRORS); break; } /* FALLTHROUGH */ case IPSTATS_MIB_OUTNOROUTES: IP6_INC_STATS(ip6_dst_idev(skb->dst), ipstats_mib_noroutes); break; } icmpv6_send(skb, ICMPV6_DEST_UNREACH, code, 0, skb->dev); kfree_skb(skb); return 0;}static int ip6_pkt_discard(struct sk_buff *skb){ return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_INNOROUTES);}static int ip6_pkt_discard_out(struct sk_buff *skb){ skb->dev = skb->dst->dev; return ip6_pkt_drop(skb, ICMPV6_NOROUTE, IPSTATS_MIB_OUTNOROUTES);}#ifdef CONFIG_IPV6_MULTIPLE_TABLESstatic int ip6_pkt_prohibit(struct sk_buff *skb){ return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_INNOROUTES);}static int ip6_pkt_prohibit_out(struct sk_buff *skb){ skb->dev = skb->dst->dev; return ip6_pkt_drop(skb, ICMPV6_ADM_PROHIBITED, IPSTATS_MIB_OUTNOROUTES);}static int ip6_pkt_blk_hole(struct sk_buff *skb){ kfree_skb(skb); return 0;}#endif/* * Allocate a dst for local (unicast / anycast) address. */struct rt6_info *addrconf_dst_alloc(struct inet6_dev *idev, const struct in6_addr *addr, int anycast){ struct rt6_info *rt = ip6_dst_alloc(); if (rt == NULL) return ERR_PTR(-ENOMEM); dev_hold(init_net.loopback_dev); in6_dev_hold(idev); rt->u.dst.flags = DST_HOST; rt->u.dst.input = ip6_input; rt->u.dst.output = ip6_output; rt->rt6i_dev = init_net.loopback_dev; rt->rt6i_idev = idev; rt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(rt->rt6i_dev); rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(dst_mtu(&rt->u.dst)); rt->u.dst.metrics[RTAX_HOPLIMIT-1] = -1; rt->u.dst.obsolete = -1; rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; if (anycast) rt->rt6i_flags |= RTF_ANYCAST; else rt->rt6i_flags |= RTF_LOCAL; rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); if (rt->rt6i_nexthop == NULL) { dst_free(&rt->u.dst); return ERR_PTR(-ENOMEM); } ipv6_addr_copy(&rt->rt6i_dst.addr, addr); rt->rt6i_dst.plen = 128; rt->rt6i_table = fib6_get_table(RT6_TABLE_LOCAL); atomic_set(&rt->u.dst.__refcnt, 1); return rt;}static int fib6_ifdown(struct rt6_info *rt, void *arg){ if (((void*)rt->rt6i_dev == arg || arg == NULL) && rt != &ip6_null_entry) { RT6_TRACE("deleted by ifdown %p\n", rt); return -1; } return 0;}void rt6_ifdown(struct net_device *dev)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -