It is possible to add routes for IPv4 destinations using an IPv6 address for next hop.
In such configurations the next hop information is provided in the RTA_VIA attribute instead of the RTA_GATEWAY attribute. Signed-off-by: Frode Nordahl <[email protected]> --- lib/netlink.c | 12 +++++++++++ lib/netlink.h | 1 + lib/route-table.c | 47 +++++++++++++++++++++++++++++++++++++++++++ tests/system-route.at | 20 ++++++++++++++++++ 4 files changed, 80 insertions(+) diff --git a/lib/netlink.c b/lib/netlink.c index 1e8d5a8ec..446a0679e 100644 --- a/lib/netlink.c +++ b/lib/netlink.c @@ -29,6 +29,16 @@ #include "openvswitch/vlog.h" #include "util.h" +#ifdef HAVE_NETLINK +#include <linux/rtnetlink.h> +#else +/* RTA_VIA */ +struct rtvia { + sa_family_t rtvia_family; + uint8_t rtvia_addr[]; +}; +#endif + VLOG_DEFINE_THIS_MODULE(netlink); /* A single (bad) Netlink message can in theory dump out many, many log @@ -819,6 +829,7 @@ min_attr_len(enum nl_attr_type type) case NL_A_IPV6: return 16; case NL_A_NESTED: return 0; case NL_A_LL_ADDR: return 6; /* ETH_ALEN */ + case NL_A_RTA_VIA: return sizeof(struct rtvia) + sizeof(struct in_addr); case N_NL_ATTR_TYPES: default: OVS_NOT_REACHED(); } } @@ -840,6 +851,7 @@ max_attr_len(enum nl_attr_type type) case NL_A_IPV6: return 16; case NL_A_NESTED: return SIZE_MAX; case NL_A_LL_ADDR: return 20; /* INFINIBAND_ALEN */ + case NL_A_RTA_VIA: return sizeof(struct rtvia) + sizeof(struct in6_addr); case N_NL_ATTR_TYPES: default: OVS_NOT_REACHED(); } } diff --git a/lib/netlink.h b/lib/netlink.h index 008604aa6..d98ef3a98 100644 --- a/lib/netlink.h +++ b/lib/netlink.h @@ -152,6 +152,7 @@ enum nl_attr_type NL_A_IPV6, NL_A_NESTED, NL_A_LL_ADDR, + NL_A_RTA_VIA, N_NL_ATTR_TYPES }; diff --git a/lib/route-table.c b/lib/route-table.c index 183d300c6..1703b69b4 100644 --- a/lib/route-table.c +++ b/lib/route-table.c @@ -51,6 +51,7 @@ COVERAGE_DEFINE(route_table_dump); struct route_data_nexthop { struct ovs_list nexthop_node; + sa_family_t family; struct in6_addr addr; char ifname[IFNAMSIZ]; /* Interface name. */ }; @@ -276,6 +277,7 @@ route_table_parse__(struct ofpbuf *buf, size_t ofs, [RTA_PREFSRC] = { .type = NL_A_U32, .optional = true }, [RTA_TABLE] = { .type = NL_A_U32, .optional = true }, [RTA_PRIORITY] = { .type = NL_A_U32, .optional = true }, + [RTA_VIA] = { .type = NL_A_RTA_VIA, .optional = true }, }; static const struct nl_policy policy6[] = { @@ -286,6 +288,7 @@ route_table_parse__(struct ofpbuf *buf, size_t ofs, [RTA_PREFSRC] = { .type = NL_A_IPV6, .optional = true }, [RTA_TABLE] = { .type = NL_A_U32, .optional = true }, [RTA_PRIORITY] = { .type = NL_A_U32, .optional = true }, + [RTA_VIA] = { .type = NL_A_RTA_VIA, .optional = true }, }; struct nlattr *attrs[ARRAY_SIZE(policy)]; @@ -310,6 +313,7 @@ route_table_parse__(struct ofpbuf *buf, size_t ofs, ovs_list_init(&change->rd.nexthops); rdnh = &change->rd._primary_next_hop; + rdnh->family = rtm->rtm_family; ovs_list_insert(&change->rd.nexthops, &rdnh->nexthop_node); change->relevant = true; @@ -384,6 +388,49 @@ route_table_parse__(struct ofpbuf *buf, size_t ofs, if (attrs[RTA_PRIORITY]) { change->rd.rta_priority = nl_attr_get_u32(attrs[RTA_PRIORITY]); } + if (attrs[RTA_VIA]) { + const struct rtvia *rtvia = nl_attr_get(attrs[RTA_VIA]); + ovs_be32 addr; + + if (attrs[RTA_GATEWAY]) { + VLOG_DBG_RL(&rl, "route message can not contain both " + "RTA_GATEWAY and RTA_VIA"); + goto error_out; + } + + rdnh->family = rtvia->rtvia_family; + + switch (rdnh->family) { + case AF_INET: + if (nl_attr_get_size(attrs[RTA_VIA]) + - sizeof *rtvia < sizeof addr) { + VLOG_DBG_RL(&rl, "got short message while parsing RTA_VIA " + "attribute for family AF_INET"); + goto error_out; + } + memcpy(&addr, rtvia->rtvia_addr, sizeof addr); + in6_addr_set_mapped_ipv4(&rdnh->addr, addr); + break; + case AF_INET6: + if (nl_attr_get_size(attrs[RTA_VIA]) + - sizeof *rtvia < sizeof rdnh->addr) { + VLOG_DBG_RL(&rl, "got short message while parsing RTA_VIA " + "attribute for family AF_INET6"); + goto error_out; + } + memcpy(&rdnh->addr, rtvia->rtvia_addr, sizeof rdnh->addr); + break; + default: + VLOG_DBG_RL(&rl, "unsupported address family, %d, " + "in via attribute", rdnh->family); + goto error_out; + } + } + if (!attrs[RTA_OIF] && !attrs[RTA_GATEWAY] && !attrs[RTA_VIA]) { + VLOG_DBG_RL(&rl, "route message needs an RTA_OIF, RTA_GATEWAY or " + "RTA_VIA attribute"); + goto error_out; + } } else { VLOG_DBG_RL(&rl, "received unparseable rtnetlink route message"); goto error_out; diff --git a/tests/system-route.at b/tests/system-route.at index c0ecad6cf..010a3412a 100644 --- a/tests/system-route.at +++ b/tests/system-route.at @@ -65,6 +65,26 @@ Cached: fc00:db8:beef::13/128 dev br0 GW fc00:db8:cafe::1 SRC fc00:db8:cafe::2]) OVS_TRAFFIC_VSWITCHD_STOP AT_CLEANUP +AT_SETUP([ovs-route - add system route - ipv4 via ipv6 nexthop]) +AT_KEYWORDS([route]) +OVS_TRAFFIC_VSWITCHD_START() +AT_CHECK([ovs-vsctl set bridge br0 other-config:hwaddr=00:53:00:00:00:42]) +AT_CHECK([ip link set br0 up]) + +AT_CHECK([ip addr add 192.168.9.2/24 dev br0], [0], [stdout]) + +AT_CHECK([ip route add 192.168.10.12/32 \ + via inet6 fe80::253:ff:fe00:51 dev br0], [0], [stdout]) + +AT_CHECK([ovs-appctl revalidator/wait]) + +OVS_WAIT_UNTIL_EQUAL([ovs-appctl ovs/route/show | \ + grep -E '192.168.10.12/32' | sort], [dnl +Cached: 192.168.10.12/32 dev br0 GW fe80::253:ff:fe00:51 SRC fe80::253:ff:fe00:42]) + +OVS_TRAFFIC_VSWITCHD_STOP +AT_CLEANUP + dnl Checks that OVS doesn't use routes from non-standard tables. AT_SETUP([ovs-route - route tables]) AT_KEYWORDS([route]) -- 2.47.1 _______________________________________________ dev mailing list [email protected] https://mail.openvswitch.org/mailman/listinfo/ovs-dev
