#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 */
/**
}
}
+ 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);
}
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,
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)));
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)));
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;
+}
+
/** @} */
/**
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)
{
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)
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;
/** @} */
+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 },
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);
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)))
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) {
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);
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;
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);
.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 */