diff options
author | David Ahern <dsahern@gmail.com> | 2017-08-17 22:59:32 (GMT) |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2017-08-18 13:01:04 (GMT) |
commit | 0a6d27ce90a1a97272cd0516a52d21bcf4428127 (patch) | |
tree | 67cb456bc1e2b8fa8638c7140bbf30178bf44bfc | |
parent | 3cf287ca17ab4e095b9d400c21f4f21e00e4ae8d (diff) | |
download | libnl-0a6d27ce90a1a97272cd0516a52d21bcf4428127.zip libnl-0a6d27ce90a1a97272cd0516a52d21bcf4428127.tar.gz libnl-0a6d27ce90a1a97272cd0516a52d21bcf4428127.tar.bz2 |
route: Add support for MPLS address family
Add support for route in MPLS family. New attributes:
- RTA_NEWDST - label stack for a nexthop
- RTA_VIA - nexthop address (e.g., IPv4 or IPv6)
Other changes required:
- scope has to be universe for MPLS routes so fixup rtnl_route_guess_scope
- priority attribute can not be set for MPLS. Change rtnl_route_parse to
not set the attribute by default for AF_MPLS.
- table attribute should not be set unless something other than the default
table. For MPLS this attribute can not be set.
'/' is the separator in label stacks for consistency with iproute2.
Signed-off-by: David Ahern <dsahern@gmail.com>
-rw-r--r-- | include/netlink-private/types.h | 2 | ||||
-rw-r--r-- | include/netlink/route/nexthop.h | 6 | ||||
-rw-r--r-- | lib/route/nexthop.c | 97 | ||||
-rw-r--r-- | lib/route/route.c | 1 | ||||
-rw-r--r-- | lib/route/route_obj.c | 142 | ||||
-rw-r--r-- | libnl-route-3.sym | 4 |
6 files changed, 242 insertions, 10 deletions
diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h index b6c5cea..ad4b125 100644 --- a/include/netlink-private/types.h +++ b/include/netlink-private/types.h @@ -311,6 +311,8 @@ struct rtnl_nexthop uint32_t ce_mask; /* HACK to support attr macros */ struct nl_list_head rtnh_list; uint32_t rtnh_realms; + struct nl_addr * rtnh_newdst; + struct nl_addr * rtnh_via; }; struct rtnl_route diff --git a/include/netlink/route/nexthop.h b/include/netlink/route/nexthop.h index 2aa44dc..654b84d 100644 --- a/include/netlink/route/nexthop.h +++ b/include/netlink/route/nexthop.h @@ -55,6 +55,12 @@ extern void rtnl_route_nh_set_realms(struct rtnl_nexthop *, uint32_t); extern uint32_t rtnl_route_nh_get_realms(struct rtnl_nexthop *); +extern int rtnl_route_nh_set_newdst(struct rtnl_nexthop *, + struct nl_addr *); +extern struct nl_addr * rtnl_route_nh_get_newdst(struct rtnl_nexthop *); +extern int rtnl_route_nh_set_via(struct rtnl_nexthop *, + struct nl_addr *); +extern struct nl_addr * rtnl_route_nh_get_via(struct rtnl_nexthop *); extern char * rtnl_route_nh_flags2str(int, char *, size_t); extern int rtnl_route_nh_str2flags(const char *); diff --git a/lib/route/nexthop.c b/lib/route/nexthop.c index a7cda90..c69b23e 100644 --- a/lib/route/nexthop.c +++ b/lib/route/nexthop.c @@ -27,6 +27,8 @@ #define NH_ATTR_IFINDEX 0x000004 #define NH_ATTR_GATEWAY 0x000008 #define NH_ATTR_REALMS 0x000010 +#define NH_ATTR_NEWDST 0x000020 +#define NH_ATTR_VIA 0x000040 /** @endcond */ /** @@ -69,12 +71,33 @@ struct rtnl_nexthop *rtnl_route_nh_clone(struct rtnl_nexthop *src) } } + if (src->rtnh_newdst) { + nh->rtnh_newdst = nl_addr_clone(src->rtnh_newdst); + if (!nh->rtnh_newdst) { + nl_addr_put(nh->rtnh_gateway); + free(nh); + return NULL; + } + } + + if (src->rtnh_via) { + nh->rtnh_via = nl_addr_clone(src->rtnh_via); + if (!nh->rtnh_via) { + nl_addr_put(nh->rtnh_gateway); + nl_addr_put(nh->rtnh_newdst); + free(nh); + return NULL; + } + } + return nh; } void rtnl_route_nh_free(struct rtnl_nexthop *nh) { nl_addr_put(nh->rtnh_gateway); + nl_addr_put(nh->rtnh_newdst); + nl_addr_put(nh->rtnh_via); free(nh); } @@ -92,6 +115,10 @@ int rtnl_route_nh_compare(struct rtnl_nexthop *a, struct rtnl_nexthop *b, diff |= NH_DIFF(REALMS, a->rtnh_realms != b->rtnh_realms); diff |= NH_DIFF(GATEWAY, nl_addr_cmp(a->rtnh_gateway, b->rtnh_gateway)); + diff |= NH_DIFF(NEWDST, nl_addr_cmp(a->rtnh_newdst, + b->rtnh_newdst)); + diff |= NH_DIFF(VIA, nl_addr_cmp(a->rtnh_via, + b->rtnh_via)); if (loose) diff |= NH_DIFF(FLAGS, @@ -111,8 +138,16 @@ static void nh_dump_line(struct rtnl_nexthop *nh, struct nl_dump_params *dp) link_cache = nl_cache_mngt_require_safe("route/link"); + if (nh->ce_mask & NH_ATTR_NEWDST) + nl_dump(dp, "as to %s ", + nl_addr2str(nh->rtnh_newdst, buf, sizeof(buf))); + nl_dump(dp, "via"); + if (nh->ce_mask & NH_ATTR_VIA) + nl_dump(dp, " %s", + nl_addr2str(nh->rtnh_via, buf, sizeof(buf))); + if (nh->ce_mask & NH_ATTR_GATEWAY) nl_dump(dp, " %s", nl_addr2str(nh->rtnh_gateway, buf, sizeof(buf))); @@ -142,6 +177,14 @@ static void nh_dump_details(struct rtnl_nexthop *nh, struct nl_dump_params *dp) nl_dump(dp, "nexthop"); + if (nh->ce_mask & NH_ATTR_NEWDST) + nl_dump(dp, " as to %s", + nl_addr2str(nh->rtnh_newdst, buf, sizeof(buf))); + + if (nh->ce_mask & NH_ATTR_VIA) + nl_dump(dp, " via %s", + nl_addr2str(nh->rtnh_via, buf, sizeof(buf))); + if (nh->ce_mask & NH_ATTR_GATEWAY) nl_dump(dp, " via %s", nl_addr2str(nh->rtnh_gateway, buf, sizeof(buf))); @@ -269,6 +312,60 @@ uint32_t rtnl_route_nh_get_realms(struct rtnl_nexthop *nh) return nh->rtnh_realms; } +int rtnl_route_nh_set_newdst(struct rtnl_nexthop *nh, struct nl_addr *addr) +{ + struct nl_addr *old = nh->rtnh_newdst; + + if (!nl_addr_valid(nl_addr_get_binary_addr(addr), + nl_addr_get_len(addr))) + return -NLE_INVAL; + + if (addr) { + nh->rtnh_newdst = nl_addr_get(addr); + nh->ce_mask |= NH_ATTR_NEWDST; + } else { + nh->ce_mask &= ~NH_ATTR_NEWDST; + nh->rtnh_newdst = NULL; + } + + if (old) + nl_addr_put(old); + + return 0; +} + +struct nl_addr *rtnl_route_nh_get_newdst(struct rtnl_nexthop *nh) +{ + return nh->rtnh_newdst; +} + +int rtnl_route_nh_set_via(struct rtnl_nexthop *nh, struct nl_addr *addr) +{ + struct nl_addr *old = nh->rtnh_via; + + if (!nl_addr_valid(nl_addr_get_binary_addr(addr), + nl_addr_get_len(addr))) + return -NLE_INVAL; + + if (addr) { + nh->rtnh_via = nl_addr_get(addr); + nh->ce_mask |= NH_ATTR_VIA; + } else { + nh->ce_mask &= ~NH_ATTR_VIA; + nh->rtnh_via= NULL; + } + + if (old) + nl_addr_put(old); + + return 0; +} + +struct nl_addr *rtnl_route_nh_get_via(struct rtnl_nexthop *nh) +{ + return nh->rtnh_via; +} + /** @} */ /** diff --git a/lib/route/route.c b/lib/route/route.c index 2985187..6688749 100644 --- a/lib/route/route.c +++ b/lib/route/route.c @@ -173,6 +173,7 @@ int rtnl_route_delete(struct nl_sock *sk, struct rtnl_route *route, int flags) static struct nl_af_group route_groups[] = { { AF_INET, RTNLGRP_IPV4_ROUTE }, { AF_INET6, RTNLGRP_IPV6_ROUTE }, + { AF_MPLS, RTNLGRP_MPLS_ROUTE }, { AF_DECnet, RTNLGRP_DECnet_ROUTE }, { END_OF_GROUP_LIST }, }; diff --git a/lib/route/route_obj.c b/lib/route/route_obj.c index b8e9f77..0f03f54 100644 --- a/lib/route/route_obj.c +++ b/lib/route/route_obj.c @@ -345,6 +345,19 @@ static void route_keygen(struct nl_object *obj, uint32_t *hashkey, return; } +static uint32_t route_id_attrs_get(struct nl_object *obj) +{ + struct rtnl_route *route = (struct rtnl_route *)obj; + struct nl_object_ops *ops = obj->ce_ops; + uint32_t rv = ops->oo_id_attrs; + + /* MPLS address family does not allow RTA_PRIORITY to be set */ + if (route->rt_family == AF_MPLS) + rv &= ~ROUTE_ATTR_PRIO; + + return rv; +} + static uint64_t route_compare(struct nl_object *_a, struct nl_object *_b, uint64_t attrs, int flags) { @@ -657,13 +670,17 @@ uint32_t rtnl_route_get_priority(struct rtnl_route *route) int rtnl_route_set_family(struct rtnl_route *route, uint8_t family) { - if (family != AF_INET && family != AF_INET6 && family != AF_DECnet) - return -NLE_AF_NOSUPPORT; - - route->rt_family = family; - route->ce_mask |= ROUTE_ATTR_FAMILY; + switch(family) { + case AF_INET: + case AF_INET6: + case AF_DECnet: + case AF_MPLS: + route->rt_family = family; + route->ce_mask |= ROUTE_ATTR_FAMILY; + return 0; + } - return 0; + return -NLE_AF_NOSUPPORT; } uint8_t rtnl_route_get_family(struct rtnl_route *route) @@ -918,6 +935,9 @@ int rtnl_route_guess_scope(struct rtnl_route *route) if (route->rt_type == RTN_LOCAL) return RT_SCOPE_HOST; + if (route->rt_family == AF_MPLS) + return RT_SCOPE_UNIVERSE; + if (!nl_list_empty(&route->rt_nexthops)) { struct rtnl_nexthop *nh; @@ -936,6 +956,31 @@ int rtnl_route_guess_scope(struct rtnl_route *route) /** @} */ +static struct nl_addr *rtnl_route_parse_via(struct nlattr *nla) +{ + int alen = nla_len(nla) - offsetof(struct rtvia, rtvia_addr); + struct rtvia *via = nla_data(nla); + + return nl_addr_build(via->rtvia_family, via->rtvia_addr, alen); +} + +static int rtnl_route_put_via(struct nl_msg *msg, struct nl_addr *addr) +{ + unsigned int alen = nl_addr_get_len(addr); + struct nlattr *nla; + struct rtvia *via; + + nla = nla_reserve(msg, RTA_VIA, alen + sizeof(*via)); + if (!nla) + return -EMSGSIZE; + + via = nla_data(nla); + via->rtvia_family = nl_addr_get_family(addr); + memcpy(via->rtvia_addr, nl_addr_get_binary_addr(addr), alen); + + return 0; +} + static struct nla_policy route_policy[RTA_MAX+1] = { [RTA_IIF] = { .type = NLA_U32 }, [RTA_OIF] = { .type = NLA_U32 }, @@ -992,6 +1037,33 @@ static int parse_multipath(struct rtnl_route *route, struct nlattr *attr) realms = nla_get_u32(ntb[RTA_FLOW]); rtnl_route_nh_set_realms(nh, realms); } + + if (ntb[RTA_NEWDST]) { + struct nl_addr *addr; + + addr = nl_addr_alloc_attr(ntb[RTA_NEWDST], + route->rt_family); + if (!addr) + goto errout; + + err = rtnl_route_nh_set_newdst(nh, addr); + nl_addr_put(addr); + if (err) + goto errout; + } + + if (ntb[RTA_VIA]) { + struct nl_addr *addr; + + addr = rtnl_route_parse_via(ntb[RTA_VIA]); + if (!addr) + goto errout; + + err = rtnl_route_nh_set_via(nh, addr); + nl_addr_put(addr); + if (err) + goto errout; + } } rtnl_route_add_nexthop(route, nh); @@ -1041,7 +1113,13 @@ int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result) route->ce_mask |= ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS | ROUTE_ATTR_TABLE | ROUTE_ATTR_TYPE | ROUTE_ATTR_SCOPE | ROUTE_ATTR_PROTOCOL | - ROUTE_ATTR_FLAGS | ROUTE_ATTR_PRIO; + ROUTE_ATTR_FLAGS; + + /* right now MPLS does not allow rt_prio to be set, so don't + * assume it is unless it comes from an attribute + */ + if (family != AF_MPLS) + route->ce_mask |= ROUTE_ATTR_PRIO; if (tb[RTA_DST]) { if (!(dst = nl_addr_alloc_attr(tb[RTA_DST], family))) @@ -1140,6 +1218,33 @@ int rtnl_route_parse(struct nlmsghdr *nlh, struct rtnl_route **result) rtnl_route_nh_set_realms(old_nh, nla_get_u32(tb[RTA_FLOW])); } + if (tb[RTA_NEWDST]) { + struct nl_addr *addr; + + addr = nl_addr_alloc_attr(tb[RTA_NEWDST], route->rt_family); + if (!addr) + goto errout_nomem; + + err = rtnl_route_nh_set_newdst(old_nh, addr); + nl_addr_put(addr); + if (err) + goto errout; + } + + if (tb[RTA_VIA]) { + int alen = nla_len(tb[RTA_VIA]) - offsetof(struct rtvia, rtvia_addr); + struct rtvia *via = nla_data(tb[RTA_VIA]); + + addr = nl_addr_build(via->rtvia_family, via->rtvia_addr, alen); + if (!addr) + goto errout_nomem; + + err = rtnl_route_nh_set_via(old_nh, addr); + nl_addr_put(addr); + if (err) + goto errout; + } + if (old_nh) { rtnl_route_nh_set_flags(old_nh, rtm->rtm_flags & 0xff); if (route->rt_nr_nh == 0) { @@ -1214,12 +1319,17 @@ int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route) goto nla_put_failure; /* Additional table attribute replacing the 8bit in the header, was - * required to allow more than 256 tables. */ - NLA_PUT_U32(msg, RTA_TABLE, route->rt_table); + * required to allow more than 256 tables. MPLS does not allow the + * table attribute to be set + */ + if (route->rt_family != AF_MPLS) + NLA_PUT_U32(msg, RTA_TABLE, route->rt_table); if (nl_addr_get_len(route->rt_dst)) NLA_PUT_ADDR(msg, RTA_DST, route->rt_dst); - NLA_PUT_U32(msg, RTA_PRIORITY, route->rt_prio); + + if (route->ce_mask & ROUTE_ATTR_PRIO) + NLA_PUT_U32(msg, RTA_PRIORITY, route->rt_prio); if (route->ce_mask & ROUTE_ATTR_SRC) NLA_PUT_ADDR(msg, RTA_SRC, route->rt_src); @@ -1255,6 +1365,10 @@ int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route) NLA_PUT_U32(msg, RTA_OIF, nh->rtnh_ifindex); if (nh->rtnh_realms) NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms); + if (nh->rtnh_newdst) + NLA_PUT_ADDR(msg, RTA_NEWDST, nh->rtnh_newdst); + if (nh->rtnh_via && rtnl_route_put_via(msg, nh->rtnh_via) < 0) + goto nla_put_failure; } else if (rtnl_route_get_nnexthops(route) > 1) { struct nlattr *multipath; struct rtnl_nexthop *nh; @@ -1277,6 +1391,13 @@ int rtnl_route_build_msg(struct nl_msg *msg, struct rtnl_route *route) NLA_PUT_ADDR(msg, RTA_GATEWAY, nh->rtnh_gateway); + if (nh->rtnh_newdst) + NLA_PUT_ADDR(msg, RTA_NEWDST, nh->rtnh_newdst); + + if (nh->rtnh_via && + rtnl_route_put_via(msg, nh->rtnh_via) < 0) + goto nla_put_failure; + if (nh->rtnh_realms) NLA_PUT_U32(msg, RTA_FLOW, nh->rtnh_realms); @@ -1312,6 +1433,7 @@ struct nl_object_ops route_obj_ops = { .oo_id_attrs = (ROUTE_ATTR_FAMILY | ROUTE_ATTR_TOS | ROUTE_ATTR_TABLE | ROUTE_ATTR_DST | ROUTE_ATTR_PRIO), + .oo_id_attrs_get = route_id_attrs_get, }; /** @endcond */ diff --git a/libnl-route-3.sym b/libnl-route-3.sym index 2ba920b..bb2d658 100644 --- a/libnl-route-3.sym +++ b/libnl-route-3.sym @@ -1053,4 +1053,8 @@ global: rtnl_rule_get_l3mdev; rtnl_rule_set_l3mdev; rtnl_u32_get_action; + rtnl_route_nh_set_newdst; + rtnl_route_nh_get_newdst; + rtnl_route_nh_set_via; + rtnl_route_nh_get_via; } libnl_3_2_29; |