diff options
-rw-r--r-- | net/ipv6/route.c | 140 |
1 files changed, 103 insertions, 37 deletions
diff --git a/net/ipv6/route.c b/net/ipv6/route.c index aa4411c81e7e..fe3966a9c999 100644 --- a/net/ipv6/route.c +++ b/net/ipv6/route.c | |||
@@ -2440,7 +2440,8 @@ static int ip6_convert_metrics(struct mx6_config *mxc, | |||
2440 | 2440 | ||
2441 | static struct rt6_info *ip6_nh_lookup_table(struct net *net, | 2441 | static struct rt6_info *ip6_nh_lookup_table(struct net *net, |
2442 | struct fib6_config *cfg, | 2442 | struct fib6_config *cfg, |
2443 | const struct in6_addr *gw_addr) | 2443 | const struct in6_addr *gw_addr, |
2444 | u32 tbid, int flags) | ||
2444 | { | 2445 | { |
2445 | struct flowi6 fl6 = { | 2446 | struct flowi6 fl6 = { |
2446 | .flowi6_oif = cfg->fc_ifindex, | 2447 | .flowi6_oif = cfg->fc_ifindex, |
@@ -2449,15 +2450,15 @@ static struct rt6_info *ip6_nh_lookup_table(struct net *net, | |||
2449 | }; | 2450 | }; |
2450 | struct fib6_table *table; | 2451 | struct fib6_table *table; |
2451 | struct rt6_info *rt; | 2452 | struct rt6_info *rt; |
2452 | int flags = RT6_LOOKUP_F_IFACE | RT6_LOOKUP_F_IGNORE_LINKSTATE; | ||
2453 | 2453 | ||
2454 | table = fib6_get_table(net, cfg->fc_table); | 2454 | table = fib6_get_table(net, tbid); |
2455 | if (!table) | 2455 | if (!table) |
2456 | return NULL; | 2456 | return NULL; |
2457 | 2457 | ||
2458 | if (!ipv6_addr_any(&cfg->fc_prefsrc)) | 2458 | if (!ipv6_addr_any(&cfg->fc_prefsrc)) |
2459 | flags |= RT6_LOOKUP_F_HAS_SADDR; | 2459 | flags |= RT6_LOOKUP_F_HAS_SADDR; |
2460 | 2460 | ||
2461 | flags |= RT6_LOOKUP_F_IGNORE_LINKSTATE; | ||
2461 | rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, flags); | 2462 | rt = ip6_pol_route(net, table, cfg->fc_ifindex, &fl6, flags); |
2462 | 2463 | ||
2463 | /* if table lookup failed, fall back to full lookup */ | 2464 | /* if table lookup failed, fall back to full lookup */ |
@@ -2469,6 +2470,82 @@ static struct rt6_info *ip6_nh_lookup_table(struct net *net, | |||
2469 | return rt; | 2470 | return rt; |
2470 | } | 2471 | } |
2471 | 2472 | ||
2473 | static int ip6_route_check_nh_onlink(struct net *net, | ||
2474 | struct fib6_config *cfg, | ||
2475 | struct net_device *dev, | ||
2476 | struct netlink_ext_ack *extack) | ||
2477 | { | ||
2478 | u32 tbid = l3mdev_fib_table(dev) ? : RT_TABLE_LOCAL; | ||
2479 | const struct in6_addr *gw_addr = &cfg->fc_gateway; | ||
2480 | u32 flags = RTF_LOCAL | RTF_ANYCAST | RTF_REJECT; | ||
2481 | struct rt6_info *grt; | ||
2482 | int err; | ||
2483 | |||
2484 | err = 0; | ||
2485 | grt = ip6_nh_lookup_table(net, cfg, gw_addr, tbid, 0); | ||
2486 | if (grt) { | ||
2487 | if (grt->rt6i_flags & flags || dev != grt->dst.dev) { | ||
2488 | NL_SET_ERR_MSG(extack, "Nexthop has invalid gateway"); | ||
2489 | err = -EINVAL; | ||
2490 | } | ||
2491 | |||
2492 | ip6_rt_put(grt); | ||
2493 | } | ||
2494 | |||
2495 | return err; | ||
2496 | } | ||
2497 | |||
2498 | static int ip6_route_check_nh(struct net *net, | ||
2499 | struct fib6_config *cfg, | ||
2500 | struct net_device **_dev, | ||
2501 | struct inet6_dev **idev) | ||
2502 | { | ||
2503 | const struct in6_addr *gw_addr = &cfg->fc_gateway; | ||
2504 | struct net_device *dev = _dev ? *_dev : NULL; | ||
2505 | struct rt6_info *grt = NULL; | ||
2506 | int err = -EHOSTUNREACH; | ||
2507 | |||
2508 | if (cfg->fc_table) { | ||
2509 | int flags = RT6_LOOKUP_F_IFACE; | ||
2510 | |||
2511 | grt = ip6_nh_lookup_table(net, cfg, gw_addr, | ||
2512 | cfg->fc_table, flags); | ||
2513 | if (grt) { | ||
2514 | if (grt->rt6i_flags & RTF_GATEWAY || | ||
2515 | (dev && dev != grt->dst.dev)) { | ||
2516 | ip6_rt_put(grt); | ||
2517 | grt = NULL; | ||
2518 | } | ||
2519 | } | ||
2520 | } | ||
2521 | |||
2522 | if (!grt) | ||
2523 | grt = rt6_lookup(net, gw_addr, NULL, cfg->fc_ifindex, 1); | ||
2524 | |||
2525 | if (!grt) | ||
2526 | goto out; | ||
2527 | |||
2528 | if (dev) { | ||
2529 | if (dev != grt->dst.dev) { | ||
2530 | ip6_rt_put(grt); | ||
2531 | goto out; | ||
2532 | } | ||
2533 | } else { | ||
2534 | *_dev = dev = grt->dst.dev; | ||
2535 | *idev = grt->rt6i_idev; | ||
2536 | dev_hold(dev); | ||
2537 | in6_dev_hold(grt->rt6i_idev); | ||
2538 | } | ||
2539 | |||
2540 | if (!(grt->rt6i_flags & RTF_GATEWAY)) | ||
2541 | err = 0; | ||
2542 | |||
2543 | ip6_rt_put(grt); | ||
2544 | |||
2545 | out: | ||
2546 | return err; | ||
2547 | } | ||
2548 | |||
2472 | static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, | 2549 | static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, |
2473 | struct netlink_ext_ack *extack) | 2550 | struct netlink_ext_ack *extack) |
2474 | { | 2551 | { |
@@ -2520,6 +2597,21 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, | |||
2520 | if (cfg->fc_metric == 0) | 2597 | if (cfg->fc_metric == 0) |
2521 | cfg->fc_metric = IP6_RT_PRIO_USER; | 2598 | cfg->fc_metric = IP6_RT_PRIO_USER; |
2522 | 2599 | ||
2600 | if (cfg->fc_flags & RTNH_F_ONLINK) { | ||
2601 | if (!dev) { | ||
2602 | NL_SET_ERR_MSG(extack, | ||
2603 | "Nexthop device required for onlink"); | ||
2604 | err = -ENODEV; | ||
2605 | goto out; | ||
2606 | } | ||
2607 | |||
2608 | if (!(dev->flags & IFF_UP)) { | ||
2609 | NL_SET_ERR_MSG(extack, "Nexthop device is not up"); | ||
2610 | err = -ENETDOWN; | ||
2611 | goto out; | ||
2612 | } | ||
2613 | } | ||
2614 | |||
2523 | err = -ENOBUFS; | 2615 | err = -ENOBUFS; |
2524 | if (cfg->fc_nlinfo.nlh && | 2616 | if (cfg->fc_nlinfo.nlh && |
2525 | !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { | 2617 | !(cfg->fc_nlinfo.nlh->nlmsg_flags & NLM_F_CREATE)) { |
@@ -2664,8 +2756,6 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, | |||
2664 | rt->rt6i_gateway = *gw_addr; | 2756 | rt->rt6i_gateway = *gw_addr; |
2665 | 2757 | ||
2666 | if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { | 2758 | if (gwa_type != (IPV6_ADDR_LINKLOCAL|IPV6_ADDR_UNICAST)) { |
2667 | struct rt6_info *grt = NULL; | ||
2668 | |||
2669 | /* IPv6 strictly inhibits using not link-local | 2759 | /* IPv6 strictly inhibits using not link-local |
2670 | addresses as nexthop address. | 2760 | addresses as nexthop address. |
2671 | Otherwise, router will not able to send redirects. | 2761 | Otherwise, router will not able to send redirects. |
@@ -2682,40 +2772,12 @@ static struct rt6_info *ip6_route_info_create(struct fib6_config *cfg, | |||
2682 | goto out; | 2772 | goto out; |
2683 | } | 2773 | } |
2684 | 2774 | ||
2685 | if (cfg->fc_table) { | 2775 | if (cfg->fc_flags & RTNH_F_ONLINK) { |
2686 | grt = ip6_nh_lookup_table(net, cfg, gw_addr); | 2776 | err = ip6_route_check_nh_onlink(net, cfg, dev, |
2687 | 2777 | extack); | |
2688 | if (grt) { | ||
2689 | if (grt->rt6i_flags & RTF_GATEWAY || | ||
2690 | (dev && dev != grt->dst.dev)) { | ||
2691 | ip6_rt_put(grt); | ||
2692 | grt = NULL; | ||
2693 | } | ||
2694 | } | ||
2695 | } | ||
2696 | |||
2697 | if (!grt) | ||
2698 | grt = rt6_lookup(net, gw_addr, NULL, | ||
2699 | cfg->fc_ifindex, 1); | ||
2700 | |||
2701 | err = -EHOSTUNREACH; | ||
2702 | if (!grt) | ||
2703 | goto out; | ||
2704 | if (dev) { | ||
2705 | if (dev != grt->dst.dev) { | ||
2706 | ip6_rt_put(grt); | ||
2707 | goto out; | ||
2708 | } | ||
2709 | } else { | 2778 | } else { |
2710 | dev = grt->dst.dev; | 2779 | err = ip6_route_check_nh(net, cfg, &dev, &idev); |
2711 | idev = grt->rt6i_idev; | ||
2712 | dev_hold(dev); | ||
2713 | in6_dev_hold(grt->rt6i_idev); | ||
2714 | } | 2780 | } |
2715 | if (!(grt->rt6i_flags & RTF_GATEWAY)) | ||
2716 | err = 0; | ||
2717 | ip6_rt_put(grt); | ||
2718 | |||
2719 | if (err) | 2781 | if (err) |
2720 | goto out; | 2782 | goto out; |
2721 | } | 2783 | } |
@@ -2757,6 +2819,7 @@ install_route: | |||
2757 | if (!(rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST)) && | 2819 | if (!(rt->rt6i_flags & (RTF_LOCAL | RTF_ANYCAST)) && |
2758 | !netif_carrier_ok(dev)) | 2820 | !netif_carrier_ok(dev)) |
2759 | rt->rt6i_nh_flags |= RTNH_F_LINKDOWN; | 2821 | rt->rt6i_nh_flags |= RTNH_F_LINKDOWN; |
2822 | rt->rt6i_nh_flags |= (cfg->fc_flags & RTNH_F_ONLINK); | ||
2760 | rt->dst.dev = dev; | 2823 | rt->dst.dev = dev; |
2761 | rt->rt6i_idev = idev; | 2824 | rt->rt6i_idev = idev; |
2762 | rt->rt6i_table = table; | 2825 | rt->rt6i_table = table; |
@@ -3826,6 +3889,8 @@ static int rtm_to_fib6_config(struct sk_buff *skb, struct nlmsghdr *nlh, | |||
3826 | if (rtm->rtm_flags & RTM_F_CLONED) | 3889 | if (rtm->rtm_flags & RTM_F_CLONED) |
3827 | cfg->fc_flags |= RTF_CACHE; | 3890 | cfg->fc_flags |= RTF_CACHE; |
3828 | 3891 | ||
3892 | cfg->fc_flags |= (rtm->rtm_flags & RTNH_F_ONLINK); | ||
3893 | |||
3829 | cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; | 3894 | cfg->fc_nlinfo.portid = NETLINK_CB(skb).portid; |
3830 | cfg->fc_nlinfo.nlh = nlh; | 3895 | cfg->fc_nlinfo.nlh = nlh; |
3831 | cfg->fc_nlinfo.nl_net = sock_net(skb->sk); | 3896 | cfg->fc_nlinfo.nl_net = sock_net(skb->sk); |
@@ -4231,6 +4296,7 @@ static int rt6_nexthop_info(struct sk_buff *skb, struct rt6_info *rt, | |||
4231 | goto nla_put_failure; | 4296 | goto nla_put_failure; |
4232 | } | 4297 | } |
4233 | 4298 | ||
4299 | *flags |= (rt->rt6i_nh_flags & RTNH_F_ONLINK); | ||
4234 | if (rt->rt6i_nh_flags & RTNH_F_OFFLOAD) | 4300 | if (rt->rt6i_nh_flags & RTNH_F_OFFLOAD) |
4235 | *flags |= RTNH_F_OFFLOAD; | 4301 | *flags |= RTNH_F_OFFLOAD; |
4236 | 4302 | ||