📄 route.c
字号:
rt->rt6i_flags |= RTF_ANYCAST; ipv6_addr_copy(&rt->rt6i_gateway, daddr); } ipv6_addr_copy(&rt->rt6i_dst.addr, daddr); rt->rt6i_dst.plen = 128; rt->rt6i_flags |= RTF_CACHE; rt->u.dst.flags |= DST_HOST;#ifdef CONFIG_IPV6_SUBTREES if (rt->rt6i_src.plen && saddr) { ipv6_addr_copy(&rt->rt6i_src.addr, saddr); rt->rt6i_src.plen = 128; }#endif rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); } return rt;}static struct rt6_info *rt6_alloc_clone(struct rt6_info *ort, struct in6_addr *daddr){ struct rt6_info *rt = ip6_rt_copy(ort); if (rt) { ipv6_addr_copy(&rt->rt6i_dst.addr, daddr); rt->rt6i_dst.plen = 128; rt->rt6i_flags |= RTF_CACHE; rt->u.dst.flags |= DST_HOST; rt->rt6i_nexthop = neigh_clone(ort->rt6i_nexthop); } return rt;}static struct rt6_info *ip6_pol_route(struct fib6_table *table, int oif, struct flowi *fl, int flags){ struct fib6_node *fn; struct rt6_info *rt, *nrt; int strict = 0; int attempts = 3; int err; int reachable = ipv6_devconf.forwarding ? 0 : RT6_LOOKUP_F_REACHABLE; strict |= flags & RT6_LOOKUP_F_IFACE;relookup: read_lock_bh(&table->tb6_lock);restart_2: fn = fib6_lookup(&table->tb6_root, &fl->fl6_dst, &fl->fl6_src);restart: rt = rt6_select(fn, oif, strict | reachable); BACKTRACK(&fl->fl6_src); if (rt == &ip6_null_entry || rt->rt6i_flags & RTF_CACHE) goto out; dst_hold(&rt->u.dst); read_unlock_bh(&table->tb6_lock); if (!rt->rt6i_nexthop && !(rt->rt6i_flags & RTF_NONEXTHOP)) nrt = rt6_alloc_cow(rt, &fl->fl6_dst, &fl->fl6_src); else {#if CLONE_OFFLINK_ROUTE nrt = rt6_alloc_clone(rt, &fl->fl6_dst);#else goto out2;#endif } dst_release(&rt->u.dst); rt = nrt ? : &ip6_null_entry; dst_hold(&rt->u.dst); if (nrt) { err = ip6_ins_rt(nrt); if (!err) goto out2; } if (--attempts <= 0) goto out2; /* * Race condition! In the gap, when table->tb6_lock was * released someone could insert this route. Relookup. */ dst_release(&rt->u.dst); goto relookup;out: if (reachable) { reachable = 0; goto restart_2; } dst_hold(&rt->u.dst); read_unlock_bh(&table->tb6_lock);out2: rt->u.dst.lastuse = jiffies; rt->u.dst.__use++; return rt;}static struct rt6_info *ip6_pol_route_input(struct fib6_table *table, struct flowi *fl, int flags){ return ip6_pol_route(table, fl->iif, fl, flags);}void ip6_route_input(struct sk_buff *skb){ struct ipv6hdr *iph = ipv6_hdr(skb); int flags = RT6_LOOKUP_F_HAS_SADDR; struct flowi fl = { .iif = skb->dev->ifindex, .nl_u = { .ip6_u = { .daddr = iph->daddr, .saddr = iph->saddr, .flowlabel = (* (__be32 *) iph)&IPV6_FLOWINFO_MASK, }, }, .mark = skb->mark, .proto = iph->nexthdr, }; if (rt6_need_strict(&iph->daddr)) flags |= RT6_LOOKUP_F_IFACE; skb->dst = fib6_rule_lookup(&fl, flags, ip6_pol_route_input);}static struct rt6_info *ip6_pol_route_output(struct fib6_table *table, struct flowi *fl, int flags){ return ip6_pol_route(table, fl->oif, fl, flags);}struct dst_entry * ip6_route_output(struct sock *sk, struct flowi *fl){ int flags = 0; if (rt6_need_strict(&fl->fl6_dst)) flags |= RT6_LOOKUP_F_IFACE; if (!ipv6_addr_any(&fl->fl6_src)) flags |= RT6_LOOKUP_F_HAS_SADDR; return fib6_rule_lookup(fl, flags, ip6_pol_route_output);}EXPORT_SYMBOL(ip6_route_output);static int ip6_blackhole_output(struct sk_buff *skb){ kfree_skb(skb); return 0;}int ip6_dst_blackhole(struct sock *sk, struct dst_entry **dstp, struct flowi *fl){ struct rt6_info *ort = (struct rt6_info *) *dstp; struct rt6_info *rt = (struct rt6_info *) dst_alloc(&ip6_dst_blackhole_ops); struct dst_entry *new = NULL; if (rt) { new = &rt->u.dst; atomic_set(&new->__refcnt, 1); new->__use = 1; new->input = ip6_blackhole_output; new->output = ip6_blackhole_output; memcpy(new->metrics, ort->u.dst.metrics, RTAX_MAX*sizeof(u32)); new->dev = ort->u.dst.dev; if (new->dev) dev_hold(new->dev); rt->rt6i_idev = ort->rt6i_idev; if (rt->rt6i_idev) in6_dev_hold(rt->rt6i_idev); 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 dst_free(new); } dst_release(*dstp); *dstp = new; return (new ? 0 : -ENOMEM);}EXPORT_SYMBOL_GPL(ip6_dst_blackhole);/* * Destination cache support functions */static struct dst_entry *ip6_dst_check(struct dst_entry *dst, u32 cookie){ struct rt6_info *rt; rt = (struct rt6_info *) dst; if (rt && rt->rt6i_node && (rt->rt6i_node->fn_sernum == cookie)) return dst; return NULL;}static struct dst_entry *ip6_negative_advice(struct dst_entry *dst){ struct rt6_info *rt = (struct rt6_info *) dst; if (rt) { if (rt->rt6i_flags & RTF_CACHE) ip6_del_rt(rt); else dst_release(dst); } return NULL;}static void ip6_link_failure(struct sk_buff *skb){ struct rt6_info *rt; icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev); rt = (struct rt6_info *) skb->dst; if (rt) { if (rt->rt6i_flags&RTF_CACHE) { dst_set_expires(&rt->u.dst, 0); rt->rt6i_flags |= RTF_EXPIRES; } else if (rt->rt6i_node && (rt->rt6i_flags & RTF_DEFAULT)) rt->rt6i_node->fn_sernum = -1; }}static void ip6_rt_update_pmtu(struct dst_entry *dst, u32 mtu){ struct rt6_info *rt6 = (struct rt6_info*)dst; if (mtu < dst_mtu(dst) && rt6->rt6i_dst.plen == 128) { rt6->rt6i_flags |= RTF_MODIFIED; if (mtu < IPV6_MIN_MTU) { mtu = IPV6_MIN_MTU; dst->metrics[RTAX_FEATURES-1] |= RTAX_FEATURE_ALLFRAG; } dst->metrics[RTAX_MTU-1] = mtu; call_netevent_notifiers(NETEVENT_PMTU_UPDATE, dst); }}static int ipv6_get_mtu(struct net_device *dev);static inline unsigned int ipv6_advmss(unsigned int mtu){ mtu -= sizeof(struct ipv6hdr) + sizeof(struct tcphdr); if (mtu < ip6_rt_min_advmss) mtu = ip6_rt_min_advmss; /* * Maximal non-jumbo IPv6 payload is IPV6_MAXPLEN and * corresponding MSS is IPV6_MAXPLEN - tcp_header_size. * IPV6_MAXPLEN is also valid and means: "any MSS, * rely only on pmtu discovery" */ if (mtu > IPV6_MAXPLEN - sizeof(struct tcphdr)) mtu = IPV6_MAXPLEN; return mtu;}static struct dst_entry *ndisc_dst_gc_list;static DEFINE_SPINLOCK(ndisc_lock);struct dst_entry *ndisc_dst_alloc(struct net_device *dev, struct neighbour *neigh, struct in6_addr *addr, int (*output)(struct sk_buff *)){ struct rt6_info *rt; struct inet6_dev *idev = in6_dev_get(dev); if (unlikely(idev == NULL)) return NULL; rt = ip6_dst_alloc(); if (unlikely(rt == NULL)) { in6_dev_put(idev); goto out; } dev_hold(dev); if (neigh) neigh_hold(neigh); else neigh = ndisc_get_neigh(dev, addr); rt->rt6i_dev = dev; rt->rt6i_idev = idev; rt->rt6i_nexthop = neigh; atomic_set(&rt->u.dst.__refcnt, 1); rt->u.dst.metrics[RTAX_HOPLIMIT-1] = 255; 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.output = output;#if 0 /* there's no chance to use these for ndisc */ rt->u.dst.flags = ipv6_addr_type(addr) & IPV6_ADDR_UNICAST ? DST_HOST : 0; ipv6_addr_copy(&rt->rt6i_dst.addr, addr); rt->rt6i_dst.plen = 128;#endif spin_lock_bh(&ndisc_lock); rt->u.dst.next = ndisc_dst_gc_list; ndisc_dst_gc_list = &rt->u.dst; spin_unlock_bh(&ndisc_lock); fib6_force_start_gc();out: return &rt->u.dst;}int ndisc_dst_gc(int *more){ struct dst_entry *dst, *next, **pprev; int freed; next = NULL; freed = 0; spin_lock_bh(&ndisc_lock); pprev = &ndisc_dst_gc_list; while ((dst = *pprev) != NULL) { if (!atomic_read(&dst->__refcnt)) { *pprev = dst->next; dst_free(dst); freed++; } else { pprev = &dst->next; (*more)++; } } spin_unlock_bh(&ndisc_lock); return freed;}static int ip6_dst_gc(void){ static unsigned expire = 30*HZ; static unsigned long last_gc; unsigned long now = jiffies; if (time_after(last_gc + ip6_rt_gc_min_interval, now) && atomic_read(&ip6_dst_ops.entries) <= ip6_rt_max_size) goto out; expire++; fib6_run_gc(expire); last_gc = now; if (atomic_read(&ip6_dst_ops.entries) < ip6_dst_ops.gc_thresh) expire = ip6_rt_gc_timeout>>1;out: expire -= expire>>ip6_rt_gc_elasticity; return (atomic_read(&ip6_dst_ops.entries) > ip6_rt_max_size);}/* Clean host part of a prefix. Not necessary in radix tree, but results in cleaner routing tables. Remove it only when all the things will work! */static int ipv6_get_mtu(struct net_device *dev){ int mtu = IPV6_MIN_MTU; struct inet6_dev *idev; idev = in6_dev_get(dev); if (idev) { mtu = idev->cnf.mtu6; in6_dev_put(idev); } return mtu;}int ipv6_get_hoplimit(struct net_device *dev){ int hoplimit = ipv6_devconf.hop_limit; struct inet6_dev *idev; idev = in6_dev_get(dev); if (idev) { hoplimit = idev->cnf.hop_limit; in6_dev_put(idev); } return hoplimit;}/* * */int ip6_route_add(struct fib6_config *cfg){ int err; struct rt6_info *rt = NULL; struct net_device *dev = NULL; struct inet6_dev *idev = NULL; struct fib6_table *table; int addr_type; if (cfg->fc_dst_len > 128 || cfg->fc_src_len > 128) return -EINVAL;#ifndef CONFIG_IPV6_SUBTREES if (cfg->fc_src_len) return -EINVAL;#endif if (cfg->fc_ifindex) { err = -ENODEV; dev = dev_get_by_index(&init_net, cfg->fc_ifindex); if (!dev) goto out; idev = in6_dev_get(dev); if (!idev) goto out; } if (cfg->fc_metric == 0) cfg->fc_metric = IP6_RT_PRIO_USER; table = fib6_new_table(cfg->fc_table); if (table == NULL) { err = -ENOBUFS; goto out; } rt = ip6_dst_alloc(); if (rt == NULL) { err = -ENOMEM; goto out; } rt->u.dst.obsolete = -1; rt->rt6i_expires = jiffies + clock_t_to_jiffies(cfg->fc_expires); if (cfg->fc_protocol == RTPROT_UNSPEC) cfg->fc_protocol = RTPROT_BOOT; rt->rt6i_protocol = cfg->fc_protocol; addr_type = ipv6_addr_type(&cfg->fc_dst); if (addr_type & IPV6_ADDR_MULTICAST) rt->u.dst.input = ip6_mc_input; else rt->u.dst.input = ip6_forward; rt->u.dst.output = ip6_output; ipv6_addr_prefix(&rt->rt6i_dst.addr, &cfg->fc_dst, cfg->fc_dst_len); rt->rt6i_dst.plen = cfg->fc_dst_len; if (rt->rt6i_dst.plen == 128) rt->u.dst.flags = DST_HOST;#ifdef CONFIG_IPV6_SUBTREES ipv6_addr_prefix(&rt->rt6i_src.addr, &cfg->fc_src, cfg->fc_src_len); rt->rt6i_src.plen = cfg->fc_src_len;#endif rt->rt6i_metric = cfg->fc_metric; /* We cannot add true routes via loopback here, they would result in kernel looping; promote them to reject routes */ if ((cfg->fc_flags & RTF_REJECT) || (dev && (dev->flags&IFF_LOOPBACK) && !(addr_type&IPV6_ADDR_LOOPBACK))) { /* hold loopback dev/idev if we haven't done so. */ if (dev != init_net.loopback_dev) { if (dev) { dev_put(dev); in6_dev_put(idev); } dev = init_net.loopback_dev; dev_hold(dev); idev = in6_dev_get(dev); if (!idev) { err = -ENODEV; goto out; } } rt->u.dst.output = ip6_pkt_discard_out; rt->u.dst.input = ip6_pkt_discard; rt->u.dst.error = -ENETUNREACH; rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; goto install_route; } if (cfg->fc_flags & RTF_GATEWAY) { struct in6_addr *gw_addr; int gwa_type; gw_addr = &cfg->fc_gateway; ipv6_addr_copy(&rt->rt6i_gateway, gw_addr); gwa_type = ipv6_addr_type(gw_addr); if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { struct rt6_info *grt; /* IPv6 strictly inhibits using not link-local addresses as nexthop address. Otherwise, router will not able to send redirects. It is very good, but in some (rare!) circumstances (SIT, PtP, NBMA NOARP links) it is handy to allow some exceptions. --ANK */ err = -EINVAL; if (!(gwa_type&IPV6_ADDR_UNICAST)) goto out; grt = rt6_lookup(gw_addr, NULL, cfg->fc_ifindex, 1); err = -EHOSTUNREACH; if (grt == NULL) goto out; if (dev) { if (dev != grt->rt6i_dev) { dst_release(&grt->u.dst); goto out; } } else { dev = grt->rt6i_dev; idev = grt->rt6i_idev; dev_hold(dev); in6_dev_hold(grt->rt6i_idev); } if (!(grt->rt6i_flags&RTF_GATEWAY)) err = 0; dst_release(&grt->u.dst); if (err) goto out; } err = -EINVAL; if (dev == NULL || (dev->flags&IFF_LOOPBACK)) goto out; } err = -ENODEV; if (dev == NULL) goto out; if (cfg->fc_flags & (RTF_GATEWAY | RTF_NONEXTHOP)) { rt->rt6i_nexthop = __neigh_lookup_errno(&nd_tbl, &rt->rt6i_gateway, dev); if (IS_ERR(rt->rt6i_nexthop)) { err = PTR_ERR(rt->rt6i_nexthop); rt->rt6i_nexthop = NULL; goto out; } } rt->rt6i_flags = cfg->fc_flags;install_route: if (cfg->fc_mx) { struct nlattr *nla; int remaining; nla_for_each_attr(nla, cfg->fc_mx, cfg->fc_mx_len, remaining) { int type = nla_type(nla); if (type) { if (type > RTAX_MAX) { err = -EINVAL; goto out; } rt->u.dst.metrics[type - 1] = nla_get_u32(nla); } } } if (rt->u.dst.metrics[RTAX_HOPLIMIT-1] == 0) rt->u.dst.metrics[RTAX_HOPLIMIT-1] = -1; if (!rt->u.dst.metrics[RTAX_MTU-1]) rt->u.dst.metrics[RTAX_MTU-1] = ipv6_get_mtu(dev); if (!rt->u.dst.metrics[RTAX_ADVMSS-1]) rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(dst_mtu(&rt->u.dst)); rt->u.dst.dev = dev; rt->rt6i_idev = idev; rt->rt6i_table = table; return __ip6_ins_rt(rt, &cfg->fc_nlinfo);out: if (dev) dev_put(dev); if (idev) in6_dev_put(idev); if (rt) dst_free(&rt->u.dst); return err;}static int __ip6_del_rt(struct rt6_info *rt, struct nl_info *info){
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -