/* SPDX-License-Identifier: LGPL-2.1-only */ /* * lib/route/nexthop.c Routing Nexthop * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation version 2.1 * of the License. * * Copyright (c) 2003-2008 Thomas Graf */ /** * @ingroup route_obj * @defgroup nexthop Nexthop * @{ */ #include #include #include #include #include #include /** @cond SKIP */ #define NH_ATTR_FLAGS 0x000001 #define NH_ATTR_WEIGHT 0x000002 #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 #define NH_ATTR_ENCAP 0x000080 /** @endcond */ /** * @name Allocation/Freeing * @{ */ struct rtnl_nexthop *rtnl_route_nh_alloc(void) { struct rtnl_nexthop *nh; nh = calloc(1, sizeof(*nh)); if (!nh) return NULL; nl_init_list_head(&nh->rtnh_list); return nh; } struct rtnl_nexthop *rtnl_route_nh_clone(struct rtnl_nexthop *src) { struct rtnl_nexthop *nh; nh = rtnl_route_nh_alloc(); if (!nh) return NULL; nh->rtnh_flags = src->rtnh_flags; nh->rtnh_flag_mask = src->rtnh_flag_mask; nh->rtnh_weight = src->rtnh_weight; nh->rtnh_ifindex = src->rtnh_ifindex; nh->ce_mask = src->ce_mask; if (src->rtnh_gateway) { nh->rtnh_gateway = nl_addr_clone(src->rtnh_gateway); if (!nh->rtnh_gateway) { free(nh); return NULL; } } 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); if (nh->rtnh_encap) { if (nh->rtnh_encap->ops && nh->rtnh_encap->ops->destructor) nh->rtnh_encap->ops->destructor(nh->rtnh_encap->priv); free(nh->rtnh_encap->priv); free(nh->rtnh_encap); } free(nh); } /** @} */ int rtnl_route_nh_compare(struct rtnl_nexthop *a, struct rtnl_nexthop *b, uint32_t attrs, int loose) { int diff = 0; #define NH_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, NH_ATTR_##ATTR, a, b, EXPR) diff |= NH_DIFF(IFINDEX, a->rtnh_ifindex != b->rtnh_ifindex); diff |= NH_DIFF(WEIGHT, a->rtnh_weight != b->rtnh_weight); 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)); diff |= NH_DIFF(ENCAP, nh_encap_compare(a->rtnh_encap, b->rtnh_encap)); if (loose) diff |= NH_DIFF(FLAGS, (a->rtnh_flags ^ b->rtnh_flags) & b->rtnh_flag_mask); else diff |= NH_DIFF(FLAGS, a->rtnh_flags != b->rtnh_flags); #undef NH_DIFF return diff; } static void nh_dump_line(struct rtnl_nexthop *nh, struct nl_dump_params *dp) { struct nl_cache *link_cache; char buf[128]; link_cache = nl_cache_mngt_require_safe("route/link"); if (nh->ce_mask & NH_ATTR_ENCAP) nh_encap_dump(nh->rtnh_encap, dp); 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))); if(nh->ce_mask & NH_ATTR_IFINDEX) { if (link_cache) { nl_dump(dp, " dev %s", rtnl_link_i2name(link_cache, nh->rtnh_ifindex, buf, sizeof(buf))); } else nl_dump(dp, " dev %d", nh->rtnh_ifindex); } nl_dump(dp, " "); if (link_cache) nl_cache_put(link_cache); } static void nh_dump_details(struct rtnl_nexthop *nh, struct nl_dump_params *dp) { struct nl_cache *link_cache; char buf[128]; link_cache = nl_cache_mngt_require_safe("route/link"); nl_dump(dp, "nexthop"); if (nh->ce_mask & NH_ATTR_ENCAP) nh_encap_dump(nh->rtnh_encap, dp); 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))); if(nh->ce_mask & NH_ATTR_IFINDEX) { if (link_cache) { nl_dump(dp, " dev %s", rtnl_link_i2name(link_cache, nh->rtnh_ifindex, buf, sizeof(buf))); } else nl_dump(dp, " dev %d", nh->rtnh_ifindex); } if (nh->ce_mask & NH_ATTR_WEIGHT) nl_dump(dp, " weight %u", nh->rtnh_weight); if (nh->ce_mask & NH_ATTR_REALMS) nl_dump(dp, " realm %04x:%04x", RTNL_REALM_FROM(nh->rtnh_realms), RTNL_REALM_TO(nh->rtnh_realms)); if (nh->ce_mask & NH_ATTR_FLAGS) nl_dump(dp, " <%s>", rtnl_route_nh_flags2str(nh->rtnh_flags, buf, sizeof(buf))); if (link_cache) nl_cache_put(link_cache); } void rtnl_route_nh_dump(struct rtnl_nexthop *nh, struct nl_dump_params *dp) { switch (dp->dp_type) { case NL_DUMP_LINE: nh_dump_line(nh, dp); break; case NL_DUMP_DETAILS: case NL_DUMP_STATS: if (dp->dp_ivar == NH_DUMP_FROM_DETAILS) nh_dump_details(nh, dp); break; default: break; } } void nh_set_encap(struct rtnl_nexthop *nh, struct rtnl_nh_encap *rtnh_encap) { if (nh->rtnh_encap) { if (nh->rtnh_encap->ops && nh->rtnh_encap->ops->destructor) nh->rtnh_encap->ops->destructor(nh->rtnh_encap->priv); free(nh->rtnh_encap->priv); free(nh->rtnh_encap); } if (rtnh_encap) { nh->rtnh_encap = rtnh_encap; nh->ce_mask |= NH_ATTR_ENCAP; } else { nh->rtnh_encap = NULL; nh->ce_mask &= ~NH_ATTR_ENCAP; } } /** * @name Attributes * @{ */ void rtnl_route_nh_set_weight(struct rtnl_nexthop *nh, uint8_t weight) { nh->rtnh_weight = weight; nh->ce_mask |= NH_ATTR_WEIGHT; } uint8_t rtnl_route_nh_get_weight(struct rtnl_nexthop *nh) { return nh->rtnh_weight; } void rtnl_route_nh_set_ifindex(struct rtnl_nexthop *nh, int ifindex) { nh->rtnh_ifindex = ifindex; nh->ce_mask |= NH_ATTR_IFINDEX; } int rtnl_route_nh_get_ifindex(struct rtnl_nexthop *nh) { return nh->rtnh_ifindex; } /* FIXME: Convert to return an int */ void rtnl_route_nh_set_gateway(struct rtnl_nexthop *nh, struct nl_addr *addr) { struct nl_addr *old = nh->rtnh_gateway; if (addr) { nh->rtnh_gateway = nl_addr_get(addr); nh->ce_mask |= NH_ATTR_GATEWAY; } else { nh->ce_mask &= ~NH_ATTR_GATEWAY; nh->rtnh_gateway = NULL; } if (old) nl_addr_put(old); } struct nl_addr *rtnl_route_nh_get_gateway(struct rtnl_nexthop *nh) { return nh->rtnh_gateway; } void rtnl_route_nh_set_flags(struct rtnl_nexthop *nh, unsigned int flags) { nh->rtnh_flag_mask |= flags; nh->rtnh_flags |= flags; nh->ce_mask |= NH_ATTR_FLAGS; } void rtnl_route_nh_unset_flags(struct rtnl_nexthop *nh, unsigned int flags) { nh->rtnh_flag_mask |= flags; nh->rtnh_flags &= ~flags; nh->ce_mask |= NH_ATTR_FLAGS; } unsigned int rtnl_route_nh_get_flags(struct rtnl_nexthop *nh) { return nh->rtnh_flags; } void rtnl_route_nh_set_realms(struct rtnl_nexthop *nh, uint32_t realms) { nh->rtnh_realms = realms; nh->ce_mask |= NH_ATTR_REALMS; } 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; } /** @} */ /** * @name Nexthop Flags Translations * @{ */ static const struct trans_tbl nh_flags[] = { __ADD(RTNH_F_DEAD, dead), __ADD(RTNH_F_PERVASIVE, pervasive), __ADD(RTNH_F_ONLINK, onlink), }; char *rtnl_route_nh_flags2str(int flags, char *buf, size_t len) { return __flags2str(flags, buf, len, nh_flags, ARRAY_SIZE(nh_flags)); } int rtnl_route_nh_str2flags(const char *name) { return __str2flags(name, nh_flags, ARRAY_SIZE(nh_flags)); } /** @} */ /** @} */