ipv4: Allow ipv6 gateway with ipv4 routes
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>
This commit is contained in:
parent
19a9d136f1
commit
d15662682d
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue