📄 route.c
字号:
{ fib6_clean_all(fib6_ifdown, 0, dev);}struct rt6_mtu_change_arg{ struct net_device *dev; unsigned mtu;};static int rt6_mtu_change_route(struct rt6_info *rt, void *p_arg){ struct rt6_mtu_change_arg *arg = (struct rt6_mtu_change_arg *) p_arg; struct inet6_dev *idev; /* In IPv6 pmtu discovery is not optional, so that RTAX_MTU lock cannot disable it. We still use this lock to block changes caused by addrconf/ndisc. */ idev = __in6_dev_get(arg->dev); if (idev == NULL) return 0; /* For administrative MTU increase, there is no way to discover IPv6 PMTU increase, so PMTU increase should be updated here. Since RFC 1981 doesn't include administrative MTU increase update PMTU increase is a MUST. (i.e. jumbo frame) */ /* If new MTU is less than route PMTU, this new MTU will be the lowest MTU in the path, update the route PMTU to reflect PMTU decreases; if new MTU is greater than route PMTU, and the old MTU is the lowest MTU in the path, update the route PMTU to reflect the increase. In this case if the other nodes' MTU also have the lowest MTU, TOO BIG MESSAGE will be lead to PMTU discouvery. */ if (rt->rt6i_dev == arg->dev && !dst_metric_locked(&rt->u.dst, RTAX_MTU) && (dst_mtu(&rt->u.dst) > arg->mtu || (dst_mtu(&rt->u.dst) < arg->mtu && dst_mtu(&rt->u.dst) == idev->cnf.mtu6))) { rt->u.dst.metrics[RTAX_MTU-1] = arg->mtu; rt->u.dst.metrics[RTAX_ADVMSS-1] = ipv6_advmss(arg->mtu); } return 0;}void rt6_mtu_change(struct net_device *dev, unsigned mtu){ struct rt6_mtu_change_arg arg = { .dev = dev, .mtu = mtu, }; fib6_clean_all(rt6_mtu_change_route, 0, &arg);}static const struct nla_policy rtm_ipv6_policy[RTA_MAX+1] = { [RTA_GATEWAY] = { .len = sizeof(struct in6_addr) }, [RTA_OIF] = { .type = NLA_U32 }, [RTA_IIF] = { .type = NLA_U32 }, [RTA_PRIORITY] = { .type = NLA_U32 }, [RTA_METRICS] = { .type = NLA_NESTED },};static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, struct fib6_config *cfg){ struct rtmsg *rtm; struct nlattr *tb[RTA_MAX+1]; int err; err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); if (err < 0) goto errout; err = -EINVAL; rtm = nlmsg_data(nlh); memset(cfg, 0, sizeof(*cfg)); cfg->fc_table = rtm->rtm_table; cfg->fc_dst_len = rtm->rtm_dst_len; cfg->fc_src_len = rtm->rtm_src_len; cfg->fc_flags = RTF_UP; cfg->fc_protocol = rtm->rtm_protocol; if (rtm->rtm_type == RTN_UNREACHABLE) cfg->fc_flags |= RTF_REJECT; cfg->fc_nlinfo.pid = NETLINK_CB(skb).pid; cfg->fc_nlinfo.nlh = nlh; if (tb[RTA_GATEWAY]) { nla_memcpy(&cfg->fc_gateway, tb[RTA_GATEWAY], 16); cfg->fc_flags |= RTF_GATEWAY; } if (tb[RTA_DST]) { int plen = (rtm->rtm_dst_len + 7) >> 3; if (nla_len(tb[RTA_DST]) < plen) goto errout; nla_memcpy(&cfg->fc_dst, tb[RTA_DST], plen); } if (tb[RTA_SRC]) { int plen = (rtm->rtm_src_len + 7) >> 3; if (nla_len(tb[RTA_SRC]) < plen) goto errout; nla_memcpy(&cfg->fc_src, tb[RTA_SRC], plen); } if (tb[RTA_OIF]) cfg->fc_ifindex = nla_get_u32(tb[RTA_OIF]); if (tb[RTA_PRIORITY]) cfg->fc_metric = nla_get_u32(tb[RTA_PRIORITY]); if (tb[RTA_METRICS]) { cfg->fc_mx = nla_data(tb[RTA_METRICS]); cfg->fc_mx_len = nla_len(tb[RTA_METRICS]); } if (tb[RTA_TABLE]) cfg->fc_table = nla_get_u32(tb[RTA_TABLE]); err = 0;errout: return err;}static int inet6_rtm_delroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg){ struct fib6_config cfg; int err; err = rtm_to_fib6_config(skb, nlh, &cfg); if (err < 0) return err; return ip6_route_del(&cfg);}static int inet6_rtm_newroute(struct sk_buff *skb, struct nlmsghdr* nlh, void *arg){ struct fib6_config cfg; int err; err = rtm_to_fib6_config(skb, nlh, &cfg); if (err < 0) return err; return ip6_route_add(&cfg);}static inline size_t rt6_nlmsg_size(void){ return NLMSG_ALIGN(sizeof(struct rtmsg)) + nla_total_size(16) /* RTA_SRC */ + nla_total_size(16) /* RTA_DST */ + nla_total_size(16) /* RTA_GATEWAY */ + nla_total_size(16) /* RTA_PREFSRC */ + nla_total_size(4) /* RTA_TABLE */ + nla_total_size(4) /* RTA_IIF */ + nla_total_size(4) /* RTA_OIF */ + nla_total_size(4) /* RTA_PRIORITY */ + RTAX_MAX * nla_total_size(4) /* RTA_METRICS */ + nla_total_size(sizeof(struct rta_cacheinfo));}static int rt6_fill_node(struct sk_buff *skb, struct rt6_info *rt, struct in6_addr *dst, struct in6_addr *src, int iif, int type, u32 pid, u32 seq, int prefix, unsigned int flags){ struct rtmsg *rtm; struct nlmsghdr *nlh; long expires; u32 table; if (prefix) { /* user wants prefix routes only */ if (!(rt->rt6i_flags & RTF_PREFIX_RT)) { /* success since this is not a prefix route */ return 1; } } nlh = nlmsg_put(skb, pid, seq, type, sizeof(*rtm), flags); if (nlh == NULL) return -EMSGSIZE; rtm = nlmsg_data(nlh); rtm->rtm_family = AF_INET6; rtm->rtm_dst_len = rt->rt6i_dst.plen; rtm->rtm_src_len = rt->rt6i_src.plen; rtm->rtm_tos = 0; if (rt->rt6i_table) table = rt->rt6i_table->tb6_id; else table = RT6_TABLE_UNSPEC; rtm->rtm_table = table; NLA_PUT_U32(skb, RTA_TABLE, table); if (rt->rt6i_flags&RTF_REJECT) rtm->rtm_type = RTN_UNREACHABLE; else if (rt->rt6i_dev && (rt->rt6i_dev->flags&IFF_LOOPBACK)) rtm->rtm_type = RTN_LOCAL; else rtm->rtm_type = RTN_UNICAST; rtm->rtm_flags = 0; rtm->rtm_scope = RT_SCOPE_UNIVERSE; rtm->rtm_protocol = rt->rt6i_protocol; if (rt->rt6i_flags&RTF_DYNAMIC) rtm->rtm_protocol = RTPROT_REDIRECT; else if (rt->rt6i_flags & RTF_ADDRCONF) rtm->rtm_protocol = RTPROT_KERNEL; else if (rt->rt6i_flags&RTF_DEFAULT) rtm->rtm_protocol = RTPROT_RA; if (rt->rt6i_flags&RTF_CACHE) rtm->rtm_flags |= RTM_F_CLONED; if (dst) { NLA_PUT(skb, RTA_DST, 16, dst); rtm->rtm_dst_len = 128; } else if (rtm->rtm_dst_len) NLA_PUT(skb, RTA_DST, 16, &rt->rt6i_dst.addr);#ifdef CONFIG_IPV6_SUBTREES if (src) { NLA_PUT(skb, RTA_SRC, 16, src); rtm->rtm_src_len = 128; } else if (rtm->rtm_src_len) NLA_PUT(skb, RTA_SRC, 16, &rt->rt6i_src.addr);#endif if (iif) NLA_PUT_U32(skb, RTA_IIF, iif); else if (dst) { struct in6_addr saddr_buf; if (ipv6_get_saddr(&rt->u.dst, dst, &saddr_buf) == 0) NLA_PUT(skb, RTA_PREFSRC, 16, &saddr_buf); } if (rtnetlink_put_metrics(skb, rt->u.dst.metrics) < 0) goto nla_put_failure; if (rt->u.dst.neighbour) NLA_PUT(skb, RTA_GATEWAY, 16, &rt->u.dst.neighbour->primary_key); if (rt->u.dst.dev) NLA_PUT_U32(skb, RTA_OIF, rt->rt6i_dev->ifindex); NLA_PUT_U32(skb, RTA_PRIORITY, rt->rt6i_metric); expires = rt->rt6i_expires ? rt->rt6i_expires - jiffies : 0; if (rtnl_put_cacheinfo(skb, &rt->u.dst, 0, 0, 0, expires, rt->u.dst.error) < 0) goto nla_put_failure; return nlmsg_end(skb, nlh);nla_put_failure: nlmsg_cancel(skb, nlh); return -EMSGSIZE;}int rt6_dump_route(struct rt6_info *rt, void *p_arg){ struct rt6_rtnl_dump_arg *arg = (struct rt6_rtnl_dump_arg *) p_arg; int prefix; if (nlmsg_len(arg->cb->nlh) >= sizeof(struct rtmsg)) { struct rtmsg *rtm = nlmsg_data(arg->cb->nlh); prefix = (rtm->rtm_flags & RTM_F_PREFIX) != 0; } else prefix = 0; return rt6_fill_node(arg->skb, rt, NULL, NULL, 0, RTM_NEWROUTE, NETLINK_CB(arg->cb->skb).pid, arg->cb->nlh->nlmsg_seq, prefix, NLM_F_MULTI);}static int inet6_rtm_getroute(struct sk_buff *in_skb, struct nlmsghdr* nlh, void *arg){ struct nlattr *tb[RTA_MAX+1]; struct rt6_info *rt; struct sk_buff *skb; struct rtmsg *rtm; struct flowi fl; int err, iif = 0; err = nlmsg_parse(nlh, sizeof(*rtm), tb, RTA_MAX, rtm_ipv6_policy); if (err < 0) goto errout; err = -EINVAL; memset(&fl, 0, sizeof(fl)); if (tb[RTA_SRC]) { if (nla_len(tb[RTA_SRC]) < sizeof(struct in6_addr)) goto errout; ipv6_addr_copy(&fl.fl6_src, nla_data(tb[RTA_SRC])); } if (tb[RTA_DST]) { if (nla_len(tb[RTA_DST]) < sizeof(struct in6_addr)) goto errout; ipv6_addr_copy(&fl.fl6_dst, nla_data(tb[RTA_DST])); } if (tb[RTA_IIF]) iif = nla_get_u32(tb[RTA_IIF]); if (tb[RTA_OIF]) fl.oif = nla_get_u32(tb[RTA_OIF]); if (iif) { struct net_device *dev; dev = __dev_get_by_index(&init_net, iif); if (!dev) { err = -ENODEV; goto errout; } } skb = alloc_skb(NLMSG_GOODSIZE, GFP_KERNEL); if (skb == NULL) { err = -ENOBUFS; goto errout; } /* Reserve room for dummy headers, this skb can pass through good chunk of routing engine. */ skb_reset_mac_header(skb); skb_reserve(skb, MAX_HEADER + sizeof(struct ipv6hdr)); rt = (struct rt6_info*) ip6_route_output(NULL, &fl); skb->dst = &rt->u.dst; err = rt6_fill_node(skb, rt, &fl.fl6_dst, &fl.fl6_src, iif, RTM_NEWROUTE, NETLINK_CB(in_skb).pid, nlh->nlmsg_seq, 0, 0); if (err < 0) { kfree_skb(skb); goto errout; } err = rtnl_unicast(skb, NETLINK_CB(in_skb).pid);errout: return err;}void inet6_rt_notify(int event, struct rt6_info *rt, struct nl_info *info){ struct sk_buff *skb; u32 pid = 0, seq = 0; struct nlmsghdr *nlh = NULL; int err = -ENOBUFS; if (info) { pid = info->pid; nlh = info->nlh; if (nlh) seq = nlh->nlmsg_seq; } skb = nlmsg_new(rt6_nlmsg_size(), gfp_any()); if (skb == NULL) goto errout; err = rt6_fill_node(skb, rt, NULL, NULL, 0, event, pid, seq, 0, 0); if (err < 0) { /* -EMSGSIZE implies BUG in rt6_nlmsg_size() */ WARN_ON(err == -EMSGSIZE); kfree_skb(skb); goto errout; } err = rtnl_notify(skb, pid, RTNLGRP_IPV6_ROUTE, nlh, gfp_any());errout: if (err < 0) rtnl_set_sk_err(RTNLGRP_IPV6_ROUTE, err);}/* * /proc */#ifdef CONFIG_PROC_FS#define RT6_INFO_LEN (32 + 4 + 32 + 4 + 32 + 40 + 5 + 1)struct rt6_proc_arg{ char *buffer; int offset; int length; int skip; int len;};static int rt6_info_route(struct rt6_info *rt, void *p_arg){ struct seq_file *m = p_arg; seq_printf(m, NIP6_SEQFMT " %02x ", NIP6(rt->rt6i_dst.addr), rt->rt6i_dst.plen);#ifdef CONFIG_IPV6_SUBTREES seq_printf(m, NIP6_SEQFMT " %02x ", NIP6(rt->rt6i_src.addr), rt->rt6i_src.plen);#else seq_puts(m, "00000000000000000000000000000000 00 ");#endif if (rt->rt6i_nexthop) { seq_printf(m, NIP6_SEQFMT, NIP6(*((struct in6_addr *)rt->rt6i_nexthop->primary_key))); } else { seq_puts(m, "00000000000000000000000000000000"); } seq_printf(m, " %08x %08x %08x %08x %8s\n", rt->rt6i_metric, atomic_read(&rt->u.dst.__refcnt), rt->u.dst.__use, rt->rt6i_flags, rt->rt6i_dev ? rt->rt6i_dev->name : ""); return 0;}static int ipv6_route_show(struct seq_file *m, void *v){ fib6_clean_all(rt6_info_route, 0, m); return 0;}static int ipv6_route_open(struct inode *inode, struct file *file){ return single_open(file, ipv6_route_show, NULL);}static const struct file_operations ipv6_route_proc_fops = { .owner = THIS_MODULE, .open = ipv6_route_open, .read = seq_read, .llseek = seq_lseek, .release = single_release,};static int rt6_stats_seq_show(struct seq_file *seq, void *v){ seq_printf(seq, "%04x %04x %04x %04x %04x %04x %04x\n", rt6_stats.fib_nodes, rt6_stats.fib_route_nodes, rt6_stats.fib_rt_alloc, rt6_stats.fib_rt_entries, rt6_stats.fib_rt_cache, atomic_read(&ip6_dst_ops.entries), rt6_stats.fib_discarded_routes); return 0;}static int rt6_stats_seq_open(struct inode *inode, struct file *file){ return single_open(file, rt6_stats_seq_show, NULL);}static const struct file_operations rt6_stats_seq_fops = { .owner = THIS_MODULE, .open = rt6_stats_seq_open, .read = seq_read, .llseek = seq_lseek, .release = single_release,};#endif /* CONFIG_PROC_FS */#ifdef CONFIG_SYSCTLstatic int flush_delay;staticint ipv6_sysctl_rtcache_flush(ctl_table *ctl, int write, struct file * filp, void __user *buffer, size_t *lenp, loff_t *ppos){ if (write) { proc_dointvec(ctl, write, filp, buffer, lenp, ppos); fib6_run_gc(flush_delay <= 0 ? ~0UL : (unsigned long)flush_delay); return 0; } else return -EINVAL;}ctl_table ipv6_route_table[] = { { .procname = "flush", .data = &flush_delay, .maxlen = sizeof(int), .mode = 0200, .proc_handler = &ipv6_sysctl_rtcache_flush }, { .ctl_name = NET_IPV6_ROUTE_GC_THRESH, .procname = "gc_thresh", .data = &ip6_dst_ops.gc_thresh, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec, }, { .ctl_name = NET_IPV6_ROUTE_MAX_SIZE, .procname = "max_size", .data = &ip6_rt_max_size, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec, }, { .ctl_name = NET_IPV6_ROUTE_GC_MIN_INTERVAL, .procname = "gc_min_interval", .data = &ip6_rt_gc_min_interval, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, .strategy = &sysctl_jiffies, }, { .ctl_name = NET_IPV6_ROUTE_GC_TIMEOUT, .procname = "gc_timeout", .data = &ip6_rt_gc_timeout, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, .strategy = &sysctl_jiffies, }, { .ctl_name = NET_IPV6_ROUTE_GC_INTERVAL, .procname = "gc_interval", .data = &ip6_rt_gc_interval, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, .strategy = &sysctl_jiffies, }, { .ctl_name = NET_IPV6_ROUTE_GC_ELASTICITY, .procname = "gc_elasticity", .data = &ip6_rt_gc_elasticity, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, .strategy = &sysctl_jiffies, }, { .ctl_name = NET_IPV6_ROUTE_MTU_EXPIRES, .procname = "mtu_expires", .data = &ip6_rt_mtu_expires, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, .strategy = &sysctl_jiffies, }, { .ctl_name = NET_IPV6_ROUTE_MIN_ADVMSS, .procname = "min_adv_mss", .data = &ip6_rt_min_advmss, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_jiffies, .strategy = &sysctl_jiffies, }, { .ctl_name = NET_IPV6_ROUTE_GC_MIN_INTERVAL_MS, .procname = "gc_min_interval_ms", .data = &ip6_rt_gc_min_interval, .maxlen = sizeof(int), .mode = 0644, .proc_handler = &proc_dointvec_ms_jiffies, .strategy = &sysctl_ms_jiffies, }, { .ctl_name = 0 }};#endifvoid __init ip6_route_init(void){ ip6_dst_ops.kmem_cachep = kmem_cache_create("ip6_dst_cache", sizeof(struct rt6_info), 0, SLAB_HWCACHE_ALIGN|SLAB_PANIC, NULL); ip6_dst_blackhole_ops.kmem_cachep = ip6_dst_ops.kmem_cachep; fib6_init(); proc_net_fops_create(&init_net, "ipv6_route", 0, &ipv6_route_proc_fops); proc_net_fops_create(&init_net, "rt6_stats", S_IRUGO, &rt6_stats_seq_fops);#ifdef CONFIG_XFRM xfrm6_init();#endif#ifdef CONFIG_IPV6_MULTIPLE_TABLES fib6_rules_init();#endif __rtnl_register(PF_INET6, RTM_NEWROUTE, inet6_rtm_newroute, NULL); __rtnl_register(PF_INET6, RTM_DELROUTE, inet6_rtm_delroute, NULL); __rtnl_register(PF_INET6, RTM_GETROUTE, inet6_rtm_getroute, NULL);}void ip6_route_cleanup(void){#ifdef CONFIG_IPV6_MULTIPLE_TABLES fib6_rules_cleanup();#endif#ifdef CONFIG_PROC_FS proc_net_remove(&init_net, "ipv6_route"); proc_net_remove(&init_net, "rt6_stats");#endif#ifdef CONFIG_XFRM xfrm6_fini();#endif rt6_ifdown(NULL); fib6_gc_cleanup(); kmem_cache_destroy(ip6_dst_ops.kmem_cachep);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -