📄 route.c
字号:
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))) { dev = &loopback_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 (!(grt->rt6i_flags&RTF_GATEWAY)) err = 0; dev = grt->rt6i_dev; 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 = ndisc_get_neigh(dev, &rt->rt6i_gateway); err = -ENOMEM; if (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.rtt = TCP_TIMEOUT_INIT; rt->rt6i_dev = dev; return rt6_ins(rt);out: dst_free((struct dst_entry *) rt); return err;}int ip6_del_rt(struct rt6_info *rt){ int err; start_bh_atomic(); rt6_dflt_pointer = NULL; err = fib6_del(rt); end_bh_atomic(); return err;}int ip6_route_del(struct in6_rtmsg *rtmsg){ struct fib6_node *fn; struct rt6_info *rt; int err = -ESRCH; start_bh_atomic(); 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; err = ip6_del_rt(rt); break; } } end_bh_atomic(); return err;}#ifdef CONFIG_IPV6_NETLINK/* * NETLINK interface * routing socket moral equivalent */static int rt6_msgrcv(int unit, struct sk_buff *skb){ int count = 0; struct in6_rtmsg *rtmsg; int err; rtnl_lock(); while (skb->len) { if (skb->len < sizeof(struct in6_rtmsg)) { count = -EINVAL; goto out; } rtmsg = (struct in6_rtmsg *) skb->data; skb_pull(skb, sizeof(struct in6_rtmsg)); count += sizeof(struct in6_rtmsg); switch (rtmsg->rtmsg_type) { case RTMSG_NEWROUTE: err = ip6_route_add(rtmsg); break; case RTMSG_DELROUTE: err = ip6_route_del(rtmsg); break; default: count = -EINVAL; goto out; }; }out: rtnl_unlock(); kfree_skb(skb); return count;}static void rt6_sndrtmsg(struct in6_rtmsg *rtmsg){ struct sk_buff *skb; skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC); if (skb == NULL) return; memcpy(skb_put(skb, sizeof(struct in6_rtmsg)), &rtmsg, sizeof(struct in6_rtmsg)); if (netlink_post(NETLINK_ROUTE6, skb)) kfree_skb(skb);}void rt6_sndmsg(int type, struct in6_addr *dst, struct in6_addr *src, struct in6_addr *gw, struct device *dev, int dstlen, int srclen, int metric, __u32 flags){ struct sk_buff *skb; struct in6_rtmsg *msg; skb = alloc_skb(sizeof(struct in6_rtmsg), GFP_ATOMIC); if (skb == NULL) return; msg = (struct in6_rtmsg *) skb_put(skb, sizeof(struct in6_rtmsg)); memset(msg, 0, sizeof(struct in6_rtmsg)); msg->rtmsg_type = type; if (dst) ipv6_addr_copy(&msg->rtmsg_dst, dst); if (src) { ipv6_addr_copy(&msg->rtmsg_src, src); msg->rtmsg_src_len = srclen; } if (gw) ipv6_addr_copy(&msg->rtmsg_gateway, gw); msg->rtmsg_dst_len = dstlen; msg->rtmsg_metric = metric; if (dev) msg->rtmsg_ifindex = dev->ifindex; msg->rtmsg_flags = flags; if (netlink_post(NETLINK_ROUTE6, skb)) kfree_skb(skb);}#endif /* CONFIG_IPV6_NETLINK *//* * 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;#if !defined(CONFIG_IPV6_EUI64) || defined(CONFIG_IPV6_NO_PB) /* * During transition gateways have more than * one link local address. Certainly, it is violation * of basic principles, but it is temparary. */ /* * 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; 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); rt = rt1; goto source_ok; } } } if (net_ratelimit()) printk(KERN_DEBUG "rt6_redirect: source isn't a valid nexthop " "for redirect target\n"); goto out; }source_ok:#endif /* * 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; 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->rt6i_hoplimit = ipv6_get_hoplimit(neigh->dev); if (rt6_ins(nrt)) goto out; /* Sic! rt6_redirect is called by bh, so that it is allowed */ dst_release(&rt->u.dst); 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 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->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(sizeof(struct rt6_info), &ip6_dst_ops); if (rt) { rt->u.dst.input = ort->u.dst.input; rt->u.dst.output = ort->u.dst.output; rt->u.dst.pmtu = ort->u.dst.pmtu; rt->u.dst.rtt = ort->u.dst.rtt; rt->u.dst.window = ort->u.dst.window; rt->u.dst.mxlock = ort->u.dst.mxlock; rt->u.dst.dev = ort->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 device *dev){ struct rt6_info *rt; struct fib6_node *fn; fn = &ip6_routing_table; start_bh_atomic(); 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); end_bh_atomic(); return rt;}struct rt6_info *rt6_add_dflt_router(struct in6_addr *gwaddr, struct 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: rt6_dflt_pointer = NULL; for (rt = ip6_routing_table.leaf; rt; rt = rt->u.next) { if (rt->rt6i_flags & flags) { ip6_del_rt(rt); goto restart; } }}int ipv6_route_ioctl(unsigned int cmd, void *arg){ struct in6_rtmsg rtmsg; int err; RDBG(("ipv6_route_ioctl(%d,%p)\n", cmd, arg)); 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();#ifdef CONFIG_IPV6_NETLINK if (err == 0) rt6_sndrtmsg(&rtmsg);#endif return err; }; return -EINVAL;}/* * Drop the packet on the floor */int ip6_pkt_discard(struct sk_buff *skb){ ipv6_statistics.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 device *dev){ struct rt6_info *rt; rt = dst_alloc(sizeof(struct rt6_info), &ip6_dst_ops); if (rt == NULL) return -ENOMEM; rt->u.dst.input = ip6_input; rt->u.dst.output = ip6_output; rt->rt6i_dev = dev_get("lo"); rt->u.dst.rtt = TCP_TIMEOUT_INIT; rt->u.dst.pmtu = ipv6_get_mtu(rt->rt6i_dev); 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 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); 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,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -