Skip to content

Commit d156626

Browse files
dsaherndavem330
authored andcommitted
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 <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent 19a9d13 commit d156626

File tree

3 files changed

+123
-8
lines changed

3 files changed

+123
-8
lines changed

include/net/ip_fib.h

+2
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,8 @@ static inline bool fib4_rules_early_flow_dissect(struct net *net,
401401
/* Exported by fib_frontend.c */
402402
extern const struct nla_policy rtm_ipv4_policy[];
403403
void ip_fib_init(void);
404+
int fib_gw_from_via(struct fib_config *cfg, struct nlattr *nla,
405+
struct netlink_ext_ack *extack);
404406
__be32 fib_compute_spec_dst(struct sk_buff *skb);
405407
bool fib_info_nh_uses_dev(struct fib_info *fi, const struct net_device *dev);
406408
int fib_validate_source(struct sk_buff *skb, __be32 src, __be32 dst,

net/ipv4/fib_frontend.c

+57-3
Original file line numberDiff line numberDiff line change
@@ -665,10 +665,55 @@ const struct nla_policy rtm_ipv4_policy[RTA_MAX + 1] = {
665665
[RTA_DPORT] = { .type = NLA_U16 },
666666
};
667667

668+
int fib_gw_from_via(struct fib_config *cfg, struct nlattr *nla,
669+
struct netlink_ext_ack *extack)
670+
{
671+
struct rtvia *via;
672+
int alen;
673+
674+
if (nla_len(nla) < offsetof(struct rtvia, rtvia_addr)) {
675+
NL_SET_ERR_MSG(extack, "Invalid attribute length for RTA_VIA");
676+
return -EINVAL;
677+
}
678+
679+
via = nla_data(nla);
680+
alen = nla_len(nla) - offsetof(struct rtvia, rtvia_addr);
681+
682+
switch (via->rtvia_family) {
683+
case AF_INET:
684+
if (alen != sizeof(__be32)) {
685+
NL_SET_ERR_MSG(extack, "Invalid IPv4 address in RTA_VIA");
686+
return -EINVAL;
687+
}
688+
cfg->fc_gw_family = AF_INET;
689+
cfg->fc_gw4 = *((__be32 *)via->rtvia_addr);
690+
break;
691+
case AF_INET6:
692+
#ifdef CONFIG_IPV6
693+
if (alen != sizeof(struct in6_addr)) {
694+
NL_SET_ERR_MSG(extack, "Invalid IPv6 address in RTA_VIA");
695+
return -EINVAL;
696+
}
697+
cfg->fc_gw_family = AF_INET6;
698+
cfg->fc_gw6 = *((struct in6_addr *)via->rtvia_addr);
699+
#else
700+
NL_SET_ERR_MSG(extack, "IPv6 support not enabled in kernel");
701+
return -EINVAL;
702+
#endif
703+
break;
704+
default:
705+
NL_SET_ERR_MSG(extack, "Unsupported address family in RTA_VIA");
706+
return -EINVAL;
707+
}
708+
709+
return 0;
710+
}
711+
668712
static int rtm_to_fib_config(struct net *net, struct sk_buff *skb,
669713
struct nlmsghdr *nlh, struct fib_config *cfg,
670714
struct netlink_ext_ack *extack)
671715
{
716+
bool has_gw = false, has_via = false;
672717
struct nlattr *attr;
673718
int err, remaining;
674719
struct rtmsg *rtm;
@@ -709,13 +754,16 @@ static int rtm_to_fib_config(struct net *net, struct sk_buff *skb,
709754
cfg->fc_oif = nla_get_u32(attr);
710755
break;
711756
case RTA_GATEWAY:
757+
has_gw = true;
712758
cfg->fc_gw_family = AF_INET;
713759
cfg->fc_gw4 = nla_get_be32(attr);
714760
break;
715761
case RTA_VIA:
716-
NL_SET_ERR_MSG(extack, "IPv4 does not support RTA_VIA attribute");
717-
err = -EINVAL;
718-
goto errout;
762+
has_via = true;
763+
err = fib_gw_from_via(cfg, attr, extack);
764+
if (err)
765+
goto errout;
766+
break;
719767
case RTA_PRIORITY:
720768
cfg->fc_priority = nla_get_u32(attr);
721769
break;
@@ -754,6 +802,12 @@ static int rtm_to_fib_config(struct net *net, struct sk_buff *skb,
754802
}
755803
}
756804

805+
if (has_gw && has_via) {
806+
NL_SET_ERR_MSG(extack,
807+
"Nexthop configuration can not contain both GATEWAY and VIA");
808+
goto errout;
809+
}
810+
757811
return 0;
758812
errout:
759813
return err;

net/ipv4/fib_semantics.c

+64-5
Original file line numberDiff line numberDiff line change
@@ -606,12 +606,22 @@ static int fib_get_nhs(struct fib_info *fi, struct rtnexthop *rtnh,
606606

607607
attrlen = rtnh_attrlen(rtnh);
608608
if (attrlen > 0) {
609-
struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
609+
struct nlattr *nla, *nlav, *attrs = rtnh_attrs(rtnh);
610610

611611
nla = nla_find(attrs, attrlen, RTA_GATEWAY);
612+
nlav = nla_find(attrs, attrlen, RTA_VIA);
613+
if (nla && nlav) {
614+
NL_SET_ERR_MSG(extack,
615+
"Nexthop configuration can not contain both GATEWAY and VIA");
616+
return -EINVAL;
617+
}
612618
if (nla) {
613619
fib_cfg.fc_gw_family = AF_INET;
614620
fib_cfg.fc_gw4 = nla_get_in_addr(nla);
621+
} else if (nlav) {
622+
ret = fib_gw_from_via(&fib_cfg, nlav, extack);
623+
if (ret)
624+
goto errout;
615625
}
616626

617627
nla = nla_find(attrs, attrlen, RTA_FLOW);
@@ -792,11 +802,43 @@ int fib_nh_match(struct fib_config *cfg, struct fib_info *fi,
792802

793803
attrlen = rtnh_attrlen(rtnh);
794804
if (attrlen > 0) {
795-
struct nlattr *nla, *attrs = rtnh_attrs(rtnh);
805+
struct nlattr *nla, *nlav, *attrs = rtnh_attrs(rtnh);
796806

797807
nla = nla_find(attrs, attrlen, RTA_GATEWAY);
798-
if (nla && nla_get_in_addr(nla) != nh->fib_nh_gw4)
799-
return 1;
808+
nlav = nla_find(attrs, attrlen, RTA_VIA);
809+
if (nla && nlav) {
810+
NL_SET_ERR_MSG(extack,
811+
"Nexthop configuration can not contain both GATEWAY and VIA");
812+
return -EINVAL;
813+
}
814+
815+
if (nla) {
816+
if (nh->fib_nh_gw_family != AF_INET ||
817+
nla_get_in_addr(nla) != nh->fib_nh_gw4)
818+
return 1;
819+
} else if (nlav) {
820+
struct fib_config cfg2;
821+
int err;
822+
823+
err = fib_gw_from_via(&cfg2, nlav, extack);
824+
if (err)
825+
return err;
826+
827+
switch (nh->fib_nh_gw_family) {
828+
case AF_INET:
829+
if (cfg2.fc_gw_family != AF_INET ||
830+
cfg2.fc_gw4 != nh->fib_nh_gw4)
831+
return 1;
832+
break;
833+
case AF_INET6:
834+
if (cfg2.fc_gw_family != AF_INET6 ||
835+
ipv6_addr_cmp(&cfg2.fc_gw6,
836+
&nh->fib_nh_gw6))
837+
return 1;
838+
break;
839+
}
840+
}
841+
800842
#ifdef CONFIG_IP_ROUTE_CLASSID
801843
nla = nla_find(attrs, attrlen, RTA_FLOW);
802844
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,
14291471
goto nla_put_failure;
14301472
break;
14311473
case AF_INET6:
1432-
if (nla_put_in6_addr(skb, RTA_GATEWAY, &nhc->nhc_gw.ipv6) < 0)
1474+
/* if gateway family does not match nexthop family
1475+
* gateway is encoded as RTA_VIA
1476+
*/
1477+
if (nhc->nhc_gw_family != nhc->nhc_family) {
1478+
int alen = sizeof(struct in6_addr);
1479+
struct nlattr *nla;
1480+
struct rtvia *via;
1481+
1482+
nla = nla_reserve(skb, RTA_VIA, alen + 2);
1483+
if (!nla)
1484+
goto nla_put_failure;
1485+
1486+
via = nla_data(nla);
1487+
via->rtvia_family = AF_INET6;
1488+
memcpy(via->rtvia_addr, &nhc->nhc_gw.ipv6, alen);
1489+
} else if (nla_put_in6_addr(skb, RTA_GATEWAY,
1490+
&nhc->nhc_gw.ipv6) < 0) {
14331491
goto nla_put_failure;
1492+
}
14341493
break;
14351494
}
14361495

0 commit comments

Comments
 (0)