📄 route.c
字号:
int ip6_route_add(struct in6_rtmsg *rtmsg){ int err; struct rt6_info *rt; struct net_device *dev = NULL; int addr_type; if (rtmsg->rtmsg_dst_len > 128 || rtmsg->rtmsg_src_len > 128) return -EINVAL;#ifndef CONFIG_IPV6_SUBTREES if (rtmsg->rtmsg_src_len) return -EINVAL;#endif if (rtmsg->rtmsg_metric == 0) rtmsg->rtmsg_metric = IP6_RT_PRIO_USER; rt = dst_alloc(&ip6_dst_ops); if (rt == NULL) return -ENOMEM; rt->u.dst.obsolete = -1; rt->rt6i_expires = rtmsg->rtmsg_info; addr_type = ipv6_addr_type(&rtmsg->rtmsg_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; if (rtmsg->rtmsg_ifindex) { dev = dev_get_by_index(rtmsg->rtmsg_ifindex); err = -ENODEV; if (dev == NULL) goto out; } ipv6_addr_copy(&rt->rt6i_dst.addr, &rtmsg->rtmsg_dst); rt->rt6i_dst.plen = rtmsg->rtmsg_dst_len; if (rt->rt6i_dst.plen == 128) rt->u.dst.flags = DST_HOST; ipv6_wash_prefix(&rt->rt6i_dst.addr, rt->rt6i_dst.plen);#ifdef CONFIG_IPV6_SUBTREES ipv6_addr_copy(&rt->rt6i_src.addr, &rtmsg->rtmsg_src); rt->rt6i_src.plen = rtmsg->rtmsg_src_len; ipv6_wash_prefix(&rt->rt6i_src.addr, rt->rt6i_src.plen);#endif rt->rt6i_metric = rtmsg->rtmsg_metric; /* We cannot add true routes via loopback here, they would result in kernel looping; promote them to reject routes */ if ((rtmsg->rtmsg_flags&RTF_REJECT) || (dev && (dev->flags&IFF_LOOPBACK) && !(addr_type&IPV6_ADDR_LOOPBACK))) { if (dev) dev_put(dev); dev = &loopback_dev; dev_hold(dev); rt->u.dst.output = ip6_pkt_discard; rt->u.dst.input = ip6_pkt_discard; rt->u.dst.error = -ENETUNREACH; rt->rt6i_flags = RTF_REJECT|RTF_NONEXTHOP; goto install_route; } if (rtmsg->rtmsg_flags & RTF_GATEWAY) { struct in6_addr *gw_addr; int gwa_type; gw_addr = &rtmsg->rtmsg_gateway; ipv6_addr_copy(&rt->rt6i_gateway, &rtmsg->rtmsg_gateway); 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!) curcumstances (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, rtmsg->rtmsg_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; dev_hold(dev); } 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 (rtmsg->rtmsg_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; } } if (ipv6_addr_is_multicast(&rt->rt6i_dst.addr)) rt->rt6i_hoplimit = IPV6_DEFAULT_MCASTHOPS; else rt->rt6i_hoplimit = ipv6_get_hoplimit(dev); rt->rt6i_flags = rtmsg->rtmsg_flags;install_route: rt->u.dst.pmtu = ipv6_get_mtu(dev); rt->u.dst.advmss = max_t(unsigned int, rt->u.dst.pmtu - 60, ip6_rt_min_advmss); /* Maximal non-jumbo IPv6 payload is 65535 and corresponding MSS is 65535 - tcp_header_size. 65535 is also valid and means: "any MSS, rely only on pmtu discovery" */ if (rt->u.dst.advmss > 65535-20) rt->u.dst.advmss = 65535; rt->u.dst.dev = dev; return rt6_ins(rt);out: if (dev) dev_put(dev); dst_free((struct dst_entry *) rt); return err;}int ip6_del_rt(struct rt6_info *rt){ int err; write_lock_bh(&rt6_lock); spin_lock_bh(&rt6_dflt_lock); rt6_dflt_pointer = NULL; spin_unlock_bh(&rt6_dflt_lock); dst_release(&rt->u.dst); err = fib6_del(rt); write_unlock_bh(&rt6_lock); return err;}int ip6_route_del(struct in6_rtmsg *rtmsg){ struct fib6_node *fn; struct rt6_info *rt; int err = -ESRCH; read_lock_bh(&rt6_lock); fn = fib6_locate(&ip6_routing_table, &rtmsg->rtmsg_dst, rtmsg->rtmsg_dst_len, &rtmsg->rtmsg_src, rtmsg->rtmsg_src_len); if (fn) { for (rt = fn->leaf; rt; rt = rt->u.next) { if (rtmsg->rtmsg_ifindex && (rt->rt6i_dev == NULL || rt->rt6i_dev->ifindex != rtmsg->rtmsg_ifindex)) continue; if (rtmsg->rtmsg_flags&RTF_GATEWAY && ipv6_addr_cmp(&rtmsg->rtmsg_gateway, &rt->rt6i_gateway)) continue; if (rtmsg->rtmsg_metric && rtmsg->rtmsg_metric != rt->rt6i_metric) continue; dst_clone(&rt->u.dst); read_unlock_bh(&rt6_lock); return ip6_del_rt(rt); } } read_unlock_bh(&rt6_lock); return err;}/* * Handle redirects */void rt6_redirect(struct in6_addr *dest, struct in6_addr *saddr, struct neighbour *neigh, int on_link){ struct rt6_info *rt, *nrt; /* Locate old route to this destination. */ rt = rt6_lookup(dest, NULL, neigh->dev->ifindex, 1); if (rt == NULL) return; if (neigh->dev != rt->rt6i_dev) goto out; /* 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; /* 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 (!(rt->rt6i_flags&RTF_GATEWAY)) goto out; /* * RFC 1970 specifies that redirects should only be * accepted if they come from the nexthop to the target. * Due to the way default routers are chosen, this notion * is a bit fuzzy and one might need to check all default * routers. */ if (ipv6_addr_cmp(saddr, &rt->rt6i_gateway)) { if (rt->rt6i_flags & RTF_DEFAULT) { struct rt6_info *rt1; read_lock(&rt6_lock); for (rt1 = ip6_routing_table.leaf; rt1; rt1 = rt1->u.next) { if (!ipv6_addr_cmp(saddr, &rt1->rt6i_gateway)) { dst_clone(&rt1->u.dst); dst_release(&rt->u.dst); read_unlock(&rt6_lock); rt = rt1; goto source_ok; } } read_unlock(&rt6_lock); } if (net_ratelimit()) printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop " "for redirect target\n"); goto out; }source_ok: /* * We have finally decided to accept it. */ 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.pmtu = ipv6_get_mtu(neigh->dev); nrt->u.dst.advmss = max_t(unsigned int, nrt->u.dst.pmtu - 60, ip6_rt_min_advmss); if (rt->u.dst.advmss > 65535-20) rt->u.dst.advmss = 65535; nrt->rt6i_hoplimit = ipv6_get_hoplimit(neigh->dev); if (rt6_ins(nrt)) goto out; 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; if (pmtu < IPV6_MIN_MTU) { if (net_ratelimit()) printk(KERN_DEBUG "rt6_pmtu_discovery: invalid MTU value %d\n", pmtu); return; } rt = rt6_lookup(daddr, saddr, dev->ifindex, 0); if (rt == NULL) return; if (pmtu >= rt->u.dst.pmtu) goto out; /* 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.pmtu = pmtu; 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_cow(rt, daddr, saddr); if (!nrt->u.dst.error) { nrt->u.dst.pmtu = pmtu; dst_set_expires(&rt->u.dst, ip6_rt_mtu_expires); nrt->rt6i_flags |= RTF_DYNAMIC|RTF_EXPIRES; dst_release(&nrt->u.dst); } } else { nrt = ip6_rt_copy(rt); if (nrt == NULL) goto out; ipv6_addr_copy(&nrt->rt6i_dst.addr, daddr); nrt->rt6i_dst.plen = 128; nrt->u.dst.flags |= DST_HOST; nrt->rt6i_nexthop = neigh_clone(rt->rt6i_nexthop); dst_set_expires(&rt->u.dst, ip6_rt_mtu_expires); nrt->rt6i_flags |= RTF_DYNAMIC|RTF_CACHE|RTF_EXPIRES; nrt->u.dst.pmtu = pmtu; rt6_ins(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; rt = dst_alloc(&ip6_dst_ops); if (rt) { rt->u.dst.input = ort->u.dst.input; rt->u.dst.output = ort->u.dst.output; memcpy(&rt->u.dst.mxlock, &ort->u.dst.mxlock, RTAX_MAX*sizeof(unsigned)); rt->u.dst.dev = ort->u.dst.dev; if (rt->u.dst.dev) dev_hold(rt->u.dst.dev); rt->u.dst.lastuse = jiffies; rt->rt6i_hoplimit = ort->rt6i_hoplimit; 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 } return rt;}struct rt6_info *rt6_get_dflt_router(struct in6_addr *addr, struct net_device *dev){ struct rt6_info *rt; struct fib6_node *fn; fn = &ip6_routing_table; write_lock_bh(&rt6_lock); for (rt = fn->leaf; rt; rt=rt->u.next) { if (dev == rt->rt6i_dev && ipv6_addr_cmp(&rt->rt6i_gateway, addr) == 0) break; } if (rt) dst_clone(&rt->u.dst); write_unlock_bh(&rt6_lock); return rt;}struct rt6_info *rt6_add_dflt_router(struct in6_addr *gwaddr, struct net_device *dev){ struct in6_rtmsg rtmsg; memset(&rtmsg, 0, sizeof(struct in6_rtmsg)); rtmsg.rtmsg_type = RTMSG_NEWROUTE; ipv6_addr_copy(&rtmsg.rtmsg_gateway, gwaddr); rtmsg.rtmsg_metric = 1024; rtmsg.rtmsg_flags = RTF_GATEWAY | RTF_ADDRCONF | RTF_DEFAULT | RTF_UP; rtmsg.rtmsg_ifindex = dev->ifindex; ip6_route_add(&rtmsg); return rt6_get_dflt_router(gwaddr, dev);}void rt6_purge_dflt_routers(int last_resort){ struct rt6_info *rt; u32 flags; if (last_resort) flags = RTF_ALLONLINK; else flags = RTF_DEFAULT | RTF_ADDRCONF; restart: read_lock_bh(&rt6_lock); for (rt = ip6_routing_table.leaf; rt; rt = rt->u.next) { if (rt->rt6i_flags & flags) { dst_hold(&rt->u.dst); spin_lock_bh(&rt6_dflt_lock); rt6_dflt_pointer = NULL; spin_unlock_bh(&rt6_dflt_lock); read_unlock_bh(&rt6_lock); ip6_del_rt(rt); goto restart; } } read_unlock_bh(&rt6_lock);}int ipv6_route_ioctl(unsigned int cmd, void *arg){ 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; rtnl_lock(); switch (cmd) { case SIOCADDRT: err = ip6_route_add(&rtmsg); break; case SIOCDELRT: err = ip6_route_del(&rtmsg); break; default: err = -EINVAL; } rtnl_unlock(); return err; }; return -EINVAL;}/* * Drop the packet on the floor */int ip6_pkt_discard(struct sk_buff *skb){ IP6_INC_STATS(Ip6OutNoRoutes); icmpv6_send(skb, ICMPV6_DEST_UNREACH, ICMPV6_ADDR_UNREACH, 0, skb->dev); kfree_skb(skb); return 0;}/* * Add address */int ip6_rt_addr_add(struct in6_addr *addr, struct net_device *dev){ struct rt6_info *rt; rt = dst_alloc(&ip6_dst_ops); if (rt == NULL) return -ENOMEM; rt->u.dst.flags = DST_HOST; rt->u.dst.input = ip6_input; rt->u.dst.output = ip6_output; rt->rt6i_dev = dev_get_by_name("lo"); rt->u.dst.pmtu = ipv6_get_mtu(rt->rt6i_dev); rt->u.dst.advmss = max_t(unsigned int, rt->u.dst.pmtu - 60, ip6_rt_min_advmss); if (rt->u.dst.advmss > 65535-20) rt->u.dst.advmss = 65535; rt->rt6i_hoplimit = ipv6_get_hoplimit(rt->rt6i_dev); rt->u.dst.obsolete = -1; rt->rt6i_flags = RTF_UP | RTF_NONEXTHOP; rt->rt6i_nexthop = ndisc_get_neigh(rt->rt6i_dev, &rt->rt6i_gateway); if (rt->rt6i_nexthop == NULL) { dst_free((struct dst_entry *) rt); return -ENOMEM; } ipv6_addr_copy(&rt->rt6i_dst.addr, addr); rt->rt6i_dst.plen = 128; rt6_ins(rt); return 0;}/* Delete address. Warning: you should check that this address disappeared before calling this function. */int ip6_rt_addr_del(struct in6_addr *addr, struct net_device *dev){ struct rt6_info *rt; int err = -ENOENT; rt = rt6_lookup(addr, NULL, loopback_dev.ifindex, 1); if (rt) { if (rt->rt6i_dst.plen == 128) err = ip6_del_rt(rt); else dst_release(&rt->u.dst); } return err;}#ifdef CONFIG_RT6_POLICYstatic int rt6_flow_match_in(struct rt6_info *rt, struct sk_buff *skb){ struct flow_filter *frule; struct pkt_filter *filter; int res = 1; if ((frule = rt->rt6i_filter) == NULL) goto out; if (frule->type != FLR_INPUT) { res = 0; goto out; } for (filter = frule->u.filter; filter; filter = filter->next) { __u32 *word; word = (__u32 *) skb->h.raw; word += filter->offset; if ((*word ^ filter->value) & filter->mask) { res = 0; break; } }out: return res;}static int rt6_flow_match_out(struct rt6_info *rt, struct sock *sk){ struct flow_filter *frule; int res = 1; if ((frule = rt->rt6i_filter) == NULL) goto out; if (frule->type != FLR_INPUT) { res = 0; goto out; } if (frule->u.sk != sk) res = 0;out: return res;}static struct rt6_info *rt6_flow_lookup(struct rt6_info *rt, struct in6_addr *daddr, struct in6_addr *saddr, struct fl_acc_args *args)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -