]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
ipv4: Allow ipv6 gateway with ipv4 routes
authorDavid Ahern <dsahern@gmail.com>
Fri, 5 Apr 2019 23:30:40 +0000 (16:30 -0700)
committerDavid S. Miller <davem@davemloft.net>
Mon, 8 Apr 2019 22:22:41 +0000 (15:22 -0700)
Add support for RTA_VIA and allow an IPv6 nexthop for v4 routes:
   $ ip ro add 172.16.1.0/24 via inet6 2001:db8::1 dev eth0
   $ ip ro ls
   ...
   172.16.1.0/24 via inet6 2001:db8::1 dev eth0

For convenience and simplicity, userspace can use RTA_VIA to specify
AF_INET or AF_INET6 gateway.

The common fib_nexthop_info dump function compares the gateway address
family to the nh_common family to know if the gateway should be encoded
as RTA_VIA or RTA_GATEWAY.

Signed-off-by: David Ahern <dsahern@gmail.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
include/net/ip_fib.h
net/ipv4/fib_frontend.c
net/ipv4/fib_semantics.c

index 337106469ec5de2664bbe017247ae0ac9b52423c..d8195c77e2479daea8aab8161cb0ef57138ec47c 100644 (file)
@@ -401,6 +401,8 @@ static inline bool fib4_rules_early_flow_dissect(struct net *net,
 /* Exported by fib_frontend.c */
 extern const struct nla_policy rtm_ipv4_policy[];
 void ip_fib_init(void);
+int fib_gw_from_via(struct fib_config *cfg, struct nlattr *nla,
+                   struct netlink_ext_ack *extack);
 __be32 fib_compute_spec_dst(struct sk_buff *skb);
 bool fib_info_nh_uses_dev(struct fib_info *fi, const struct net_device *dev);
 int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,
index f99a2ec3250578d05e1cf68d2fcaab8a66ebd78e..310060e67790cf6578308249f2f9f66317f24081 100644 (file)
@@ -665,10 +665,55 @@ const struct nla_policy rtm_ipv4_policy[RTA_MAX + 1] = {
        [RTA_DPORT]             = { .type = NLA_U16 },
 };
 
+int fib_gw_from_via(struct fib_config *cfg, struct nlattr *nla,
+                   struct netlink_ext_ack *extack)
+{
+       struct rtvia *via;
+       int alen;
+
+       if (nla_len(nla) < offsetof(struct rtvia, rtvia_addr)) {
+               NL_SET_ERR_MSG(extack, "Invalid attribute length for RTA_VIA");
+               return -EINVAL;
+       }
+
+       via = nla_data(nla);
+       alen = nla_len(nla) - offsetof(struct rtvia, rtvia_addr);
+
+       switch (via->rtvia_family) {
+       case AF_INET:
+               if (alen != sizeof(__be32)) {
+                       NL_SET_ERR_MSG(extack, "Invalid IPv4 address in RTA_VIA");
+                       return -EINVAL;
+               }
+               cfg->fc_gw_family = AF_INET;
+               cfg->fc_gw4 = *((__be32 *)via->rtvia_addr);
+               break;
+       case AF_INET6:
+#ifdef CONFIG_IPV6
+               if (alen != sizeof(struct in6_addr)) {
+                       NL_SET_ERR_MSG(extack, "Invalid IPv6 address in RTA_VIA");
+                       return -EINVAL;
+               }
+               cfg->fc_gw_family = AF_INET6;
+               cfg->fc_gw6 = *((struct in6_addr *)via->rtvia_addr);
+#else
+               NL_SET_ERR_MSG(extack, "IPv6 support not enabled in kernel");
+               return -EINVAL;
+#endif
+               break;
+       default:
+               NL_SET_ERR_MSG(extack, "Unsupported address family in RTA_VIA");
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
 static int rtm_to_fib_config(struct net *net, struct sk_buff *skb,
                             struct nlmsghdr *nlh, struct fib_config *cfg,
                             struct netlink_ext_ack *extack)
 {
+       bool has_gw = false, has_via = false;
        struct nlattr *attr;
        int err, remaining;
        struct rtmsg *rtm;
@@ -709,13 +754,16 @@ static int rtm_to_fib_config(struct net *net, struct sk_buff *skb,
                        cfg->fc_oif = nla_get_u32(attr);
                        break;
                case RTA_GATEWAY:
+                       has_gw = true;
                        cfg->fc_gw_family = AF_INET;
                        cfg->fc_gw4 = nla_get_be32(attr);
                        break;
                case RTA_VIA:
-                       NL_SET_ERR_MSG(extack, "IPv4 does not support RTA_VIA attribute");
-                       err = -EINVAL;
-                       goto errout;
+                       has_via = true;
+                       err = fib_gw_from_via(cfg, attr, extack);
+                       if (err)
+                               goto errout;
+                       break;
                case RTA_PRIORITY:
                        cfg->fc_priority = nla_get_u32(attr);
                        break;
@@ -754,6 +802,12 @@ static int rtm_to_fib_config(struct net *net, struct sk_buff *skb,
                }
        }
 
+       if (has_gw && has_via) {
+               NL_SET_ERR_MSG(extack,
+                              "Nexthop configuration can not contain both GATEWAY and VIA");
+               goto errout;
+       }
+
        return 0;
 errout:
        return err;
index 4a968e24507bd3fbdc8af55a60b6e2dd386ba604..017273885eeeb9fc50e47f8daf015673cb1ce536 100644 (file)
@@ -606,12 +606,22 @@ static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh,
 
                attrlen = rtnh_attrlen(rtnh);
                if (attrlen > 0) {
-                       struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
+                       struct nlattr *nla, *nlav, *attrs = rtnh_attrs(rtnh);
 
                        nla = nla_find(attrs, attrlen, RTA_GATEWAY);
+                       nlav = nla_find(attrs, attrlen, RTA_VIA);
+                       if (nla && nlav) {
+                               NL_SET_ERR_MSG(extack,
+                                              "Nexthop configuration can not contain both GATEWAY and VIA");
+                               return -EINVAL;
+                       }
                        if (nla) {
                                fib_cfg.fc_gw_family = AF_INET;
                                fib_cfg.fc_gw4 = nla_get_in_addr(nla);
+                       } else if (nlav) {
+                               ret = fib_gw_from_via(&fib_cfg, nlav, extack);
+                               if (ret)
+                                       goto errout;
                        }
 
                        nla = nla_find(attrs, attrlen, RTA_FLOW);
@@ -792,11 +802,43 @@ int fib_nh_match(struct fib_config *cfg, struct fib_info *fi,
 
                attrlen = rtnh_attrlen(rtnh);
                if (attrlen > 0) {
-                       struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
+                       struct nlattr *nla, *nlav, *attrs = rtnh_attrs(rtnh);
 
                        nla = nla_find(attrs, attrlen, RTA_GATEWAY);
-                       if (nla && nla_get_in_addr(nla) != nh->fib_nh_gw4)
-                               return 1;
+                       nlav = nla_find(attrs, attrlen, RTA_VIA);
+                       if (nla && nlav) {
+                               NL_SET_ERR_MSG(extack,
+                                              "Nexthop configuration can not contain both GATEWAY and VIA");
+                               return -EINVAL;
+                       }
+
+                       if (nla) {
+                               if (nh->fib_nh_gw_family != AF_INET ||
+                                   nla_get_in_addr(nla) != nh->fib_nh_gw4)
+                                       return 1;
+                       } else if (nlav) {
+                               struct fib_config cfg2;
+                               int err;
+
+                               err = fib_gw_from_via(&cfg2, nlav, extack);
+                               if (err)
+                                       return err;
+
+                               switch (nh->fib_nh_gw_family) {
+                               case AF_INET:
+                                       if (cfg2.fc_gw_family != AF_INET ||
+                                           cfg2.fc_gw4 != nh->fib_nh_gw4)
+                                               return 1;
+                                       break;
+                               case AF_INET6:
+                                       if (cfg2.fc_gw_family != AF_INET6 ||
+                                           ipv6_addr_cmp(&cfg2.fc_gw6,
+                                                         &nh->fib_nh_gw6))
+                                               return 1;
+                                       break;
+                               }
+                       }
+
 #ifdef CONFIG_IP_ROUTE_CLASSID
                        nla = nla_find(attrs, attrlen, RTA_FLOW);
                        if (nla && nla_get_u32(nla) != nh->nh_tclassid)
@@ -1429,8 +1471,25 @@ int fib_nexthop_info(struct sk_buff *skb, const struct fib_nh_common *nhc,
                        goto nla_put_failure;
                break;
        case AF_INET6:
-               if (nla_put_in6_addr(skb, RTA_GATEWAY, &nhc->nhc_gw.ipv6) < 0)
+               /* if gateway family does not match nexthop family
+                * gateway is encoded as RTA_VIA
+                */
+               if (nhc->nhc_gw_family != nhc->nhc_family) {
+                       int alen = sizeof(struct in6_addr);
+                       struct nlattr *nla;
+                       struct rtvia *via;
+
+                       nla = nla_reserve(skb, RTA_VIA, alen + 2);
+                       if (!nla)
+                               goto nla_put_failure;
+
+                       via = nla_data(nla);
+                       via->rtvia_family = AF_INET6;
+                       memcpy(via->rtvia_addr, &nhc->nhc_gw.ipv6, alen);
+               } else if (nla_put_in6_addr(skb, RTA_GATEWAY,
+                                           &nhc->nhc_gw.ipv6) < 0) {
                        goto nla_put_failure;
+               }
                break;
        }