static switchlink_handle_t process_oif_list(struct nlattr *attr, switchlink_handle_t vrf_h) { switchlink_db_status_t status; switchlink_db_oifl_info_t oifl_info; memset(&oifl_info, 0, sizeof(switchlink_db_oifl_info_t)); struct rtnexthop *rnh = (struct rtnexthop *)nla_data(attr); int attrlen = nla_len(attr); while (RTNH_OK(rnh, attrlen)) { switchlink_db_interface_info_t ifinfo; memset(&ifinfo, 0, sizeof(switchlink_db_interface_info_t)); status = switchlink_db_interface_get_info(rnh->rtnh_ifindex, &ifinfo); if (status == SWITCHLINK_DB_STATUS_SUCCESS) { oifl_info.intfs[oifl_info.num_intfs++] = ifinfo.intf_h; } rnh = RTNH_NEXT(rnh); } if (!oifl_info.num_intfs) { return 0; } status = switchlink_db_oifl_get_info(&oifl_info); if (status == SWITCHLINK_DB_STATUS_ITEM_NOT_FOUND) { switchlink_db_oifl_add(&oifl_info); } return oifl_info.oifl_h; }
static void add_nexthops(ip_route_t *route, struct nlmsghdr *nlh, struct rtmsg *rtm) { char buf[ENCAP_RTA_SIZE]; struct rtattr *rta = (void *)buf; struct rtnexthop *rtnh; nexthop_t *nh; element e; rta->rta_type = RTA_MULTIPATH; rta->rta_len = RTA_LENGTH(0); rtnh = RTA_DATA(rta); for (e = LIST_HEAD(route->nhs); e; ELEMENT_NEXT(e)) { nh = ELEMENT_DATA(e); memset(rtnh, 0, sizeof(*rtnh)); rtnh->rtnh_len = sizeof(*rtnh); rta->rta_len += rtnh->rtnh_len; add_nexthop(nh, nlh, rtm, rta, sizeof(buf), rtnh); rtnh = RTNH_NEXT(rtnh); } if (rta->rta_len > RTA_LENGTH(0)) addattr_l(nlh, sizeof(buf), RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta)); }
/* * debug attribute RTA_MULTIPATH */ void debug_rta_multipath(int lev, struct rtmsg *rtm, struct rtattr *rta, const char *name) { struct rtnexthop *rtnh; int rtnh_len = RTA_PAYLOAD(rta); struct rtattr *rtnha[__RTA_MAX]; char ifname[IFNAMSIZ] = ""; char flags_list[MAX_STR_SIZE] = ""; if(debug_rta_len_chk(lev, rta, name, sizeof(*rtnh))) return; rec_dbg(lev, "%s(%hu):", name, RTA_ALIGN(rta->rta_len)); for(rtnh = RTA_DATA(rta); RTNH_OK(rtnh, rtnh_len); rtnh = RTNH_NEXT(rtnh), rtnh_len -= RTNH_ALIGN(rtnh->rtnh_len)) { conv_rtnh_flags(rtnh->rtnh_flags, flags_list, sizeof(flags_list)); if_indextoname_from_lists(rtnh->rtnh_ifindex, ifname); rec_dbg(lev, " [ rtnexthop(%d) ]", sizeof(*rtnh)); rec_dbg(lev, " rtnh_len(%d): %hu", sizeof(rtnh->rtnh_len), rtnh->rtnh_len); rec_dbg(lev, " rtnh_flags(%d): %d(%s)", sizeof(rtnh->rtnh_flags), rtnh->rtnh_flags, flags_list); rec_dbg(lev, " rtnh_hops(%d): %d", sizeof(rtnh->rtnh_hops), rtnh->rtnh_hops); rec_dbg(lev, " rtnh_ifindex(%d): %d(%s)", sizeof(rtnh->rtnh_ifindex), rtnh->rtnh_ifindex, ifname); parse_rtattr(rtnha, RTA_MAX, RTNH_DATA(rtnh), rtnh->rtnh_len - sizeof(*rtnh)); if(rtnha[RTA_GATEWAY]) debug_rta_af(lev+3, rtnha[RTA_GATEWAY], "RTA_GATEWAY", rtm->rtm_family); } }
int parse_nexthops(struct nlmsghdr *n, struct rtmsg *r, int argc, char *argv) { char buf[1024]; struct rtattr *rta = (void*)buf; struct rtnexthop *rtnh; rta->rta_type = RTA_MULTIPATH; rta->rta_len = RTA_LENGTH(0); rtnh = RTA_DATA(rta); while (argc > 0) { if(strcmp(argv, "nexthop") != 0) { fprintf(stderr, "Error: \"nexthop\" or end of line is expected instead of \"%s\"\n", argv); exit(-1); } if(argc <= 1) { fprintf(stderr, "Error: unexpected end of line after \"nexthop\"\n"); exit(-1); } memset(rtnh, 0, sizeof(*rtnh)); rtnh->rtnh_len = sizeof(*rtnh); rta->rta_len += rtnh->rtnh_len; parse_one_nh(rta, rtnh, &argc, argv); rtnh = RTNH_NEXT(rtnh); } if(rta->rta_len > RTA_LENGTH(0)) addattr_l(n, 1024, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta)); return 0; }
/* * parse_multipath_attr */ int parse_multipath_attr (netlink_msg_ctx_t *ctx, struct rtattr *mpath_rtattr) { size_t len, attr_len; struct rtnexthop *rtnh; struct rtattr *rtattrs[RTA_MAX + 1]; struct rtattr *rtattr, *gateway; int if_index; const char *err_msg; rtnh = RTA_DATA(mpath_rtattr); len = RTA_PAYLOAD(mpath_rtattr); for (; len > 0; len -= NLMSG_ALIGN(rtnh->rtnh_len), rtnh = RTNH_NEXT(rtnh)) { if (!RTNH_OK(rtnh, len)) { netlink_msg_ctx_set_err(ctx, "Malformed nh"); return 0; } if (rtnh->rtnh_len <= sizeof(*rtnh)) { netlink_msg_ctx_set_err(ctx, "NH len too small"); return 0; } /* * Parse attributes included in the nexthop. */ err_msg = NULL; if (!parse_rtattrs_(RTNH_DATA(rtnh), rtnh->rtnh_len - sizeof(*rtnh), rtattrs, NUM_OF(rtattrs), &err_msg)) { netlink_msg_ctx_set_err(ctx, err_msg); return 0; } gateway = rtattrs[RTA_GATEWAY]; netlink_msg_ctx_add_nh(ctx, rtnh->rtnh_ifindex, gateway); } return 1; }
/* Routing table change via netlink interface. */ int netlink_route_multipath (int cmd, struct prefix *p, struct rib *rib, int family) { int bytelen; struct sockaddr_nl snl; struct nexthop *nexthop = NULL; int nexthop_num = 0; struct nlsock *nl; struct { struct nlmsghdr n; struct rtmsg r; char buf[1024]; } req; memset (&req, 0, sizeof req); bytelen = (family == AF_INET ? 4 : 16); req.n.nlmsg_len = NLMSG_LENGTH (sizeof (struct rtmsg)); req.n.nlmsg_flags = NLM_F_CREATE | NLM_F_REQUEST; req.n.nlmsg_type = cmd; req.r.rtm_family = family; req.r.rtm_table = rib->table; req.r.rtm_dst_len = p->prefixlen; if (cmd == RTM_NEWROUTE) { req.r.rtm_protocol = RTPROT_ZEBRA; req.r.rtm_scope = RT_SCOPE_UNIVERSE; if (CHECK_FLAG (rib->flags, ZEBRA_FLAG_BLACKHOLE)) req.r.rtm_type = RTN_BLACKHOLE; else req.r.rtm_type = RTN_UNICAST; } addattr_l (&req.n, sizeof req, RTA_DST, &p->u.prefix, bytelen); /* Metric. */ addattr32 (&req.n, sizeof req, RTA_PRIORITY, rib->metric); /* Multipath case. */ if (rib->nexthop_active_num == 1 || MULTIPATH_NUM == 1) { for (nexthop = rib->nexthop; nexthop; nexthop = nexthop->next) { if ((cmd == RTM_NEWROUTE && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) || (cmd == RTM_DELROUTE && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) { if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) { if (nexthop->rtype == NEXTHOP_TYPE_IPV4 || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) addattr_l (&req.n, sizeof req, RTA_GATEWAY, &nexthop->rgate.ipv4, bytelen); #ifdef HAVE_IPV6 if (nexthop->rtype == NEXTHOP_TYPE_IPV6 || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME) addattr_l (&req.n, sizeof req, RTA_GATEWAY, &nexthop->rgate.ipv6, bytelen); #endif /* HAVE_IPV6 */ if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX || nexthop->rtype == NEXTHOP_TYPE_IFNAME || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME) addattr32 (&req.n, sizeof req, RTA_OIF, nexthop->rifindex); } else { if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) addattr_l (&req.n, sizeof req, RTA_GATEWAY, &nexthop->gate.ipv4, bytelen); #ifdef HAVE_IPV6 if (nexthop->type == NEXTHOP_TYPE_IPV6 || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) addattr_l (&req.n, sizeof req, RTA_GATEWAY, &nexthop->gate.ipv6, bytelen); #endif /* HAVE_IPV6 */ if (nexthop->type == NEXTHOP_TYPE_IFINDEX || nexthop->type == NEXTHOP_TYPE_IFNAME || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME) addattr32 (&req.n, sizeof req, RTA_OIF, nexthop->ifindex); } if (cmd == RTM_NEWROUTE) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); nexthop_num++; break; } } } else { char buf[1024]; struct rtattr *rta = (void *) buf; struct rtnexthop *rtnh; rta->rta_type = RTA_MULTIPATH; rta->rta_len = RTA_LENGTH(0); rtnh = RTA_DATA(rta); nexthop_num = 0; for (nexthop = rib->nexthop; nexthop && (MULTIPATH_NUM == 0 || nexthop_num < MULTIPATH_NUM); nexthop = nexthop->next) { if ((cmd == RTM_NEWROUTE && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_ACTIVE)) || (cmd == RTM_DELROUTE && CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB))) { nexthop_num++; rtnh->rtnh_len = sizeof (*rtnh); rtnh->rtnh_flags = 0; rtnh->rtnh_hops = 0; rta->rta_len += rtnh->rtnh_len; if (CHECK_FLAG (nexthop->flags, NEXTHOP_FLAG_RECURSIVE)) { if (nexthop->rtype == NEXTHOP_TYPE_IPV4 || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX) { rta_addattr_l (rta, 4096, RTA_GATEWAY, &nexthop->rgate.ipv4, bytelen); rtnh->rtnh_len += sizeof (struct rtattr) + 4; } #ifdef HAVE_IPV6 if (nexthop->rtype == NEXTHOP_TYPE_IPV6 || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX) rta_addattr_l (rta, 4096, RTA_GATEWAY, &nexthop->rgate.ipv6, bytelen); #endif /* HAVE_IPV6 */ /* ifindex */ if (nexthop->rtype == NEXTHOP_TYPE_IFINDEX || nexthop->rtype == NEXTHOP_TYPE_IFNAME || nexthop->rtype == NEXTHOP_TYPE_IPV4_IFINDEX || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFINDEX || nexthop->rtype == NEXTHOP_TYPE_IPV6_IFNAME) rtnh->rtnh_ifindex = nexthop->rifindex; else rtnh->rtnh_ifindex = 0; } else { if (nexthop->type == NEXTHOP_TYPE_IPV4 || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX) { rta_addattr_l (rta, 4096, RTA_GATEWAY, &nexthop->gate.ipv4, bytelen); rtnh->rtnh_len += sizeof (struct rtattr) + 4; } #ifdef HAVE_IPV6 if (nexthop->type == NEXTHOP_TYPE_IPV6 || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) rta_addattr_l (rta, 4096, RTA_GATEWAY, &nexthop->gate.ipv6, bytelen); #endif /* HAVE_IPV6 */ /* ifindex */ if (nexthop->type == NEXTHOP_TYPE_IFINDEX || nexthop->type == NEXTHOP_TYPE_IFNAME || nexthop->type == NEXTHOP_TYPE_IPV4_IFINDEX || nexthop->type == NEXTHOP_TYPE_IPV6_IFNAME || nexthop->type == NEXTHOP_TYPE_IPV6_IFINDEX) rtnh->rtnh_ifindex = nexthop->ifindex; else rtnh->rtnh_ifindex = 0; } rtnh = RTNH_NEXT(rtnh); if (cmd == RTM_NEWROUTE) SET_FLAG (nexthop->flags, NEXTHOP_FLAG_FIB); } } if (rta->rta_len > RTA_LENGTH (0)) addattr_l (&req.n, 1024, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta)); } /* If there is no useful nexthop then return. */ if (nexthop_num == 0) { if (IS_ZEBRA_DEBUG_KERNEL) zlog_info ("netlink_route_multipath(): No useful nexthop."); return 0; } /* Destination netlink address. */ memset (&snl, 0, sizeof snl); snl.nl_family = AF_NETLINK; if (family == AF_INET) nl = &netlink_cmd; else nl = &netlink; /* Talk to netlink socket. */ return netlink_talk (&req.n, nl); }
static void copy_cacheinfo_into_route(struct rta_cacheinfo *ci, struct rtnl_route *route) { struct rtnl_rtcacheinfo nci = { .rtci_clntref = ci->rta_clntref, .rtci_last_use = ci->rta_lastuse, .rtci_expires = ci->rta_expires, .rtci_error = ci->rta_error, .rtci_used = ci->rta_used, .rtci_id = ci->rta_id, .rtci_ts = ci->rta_ts, .rtci_tsage = ci->rta_tsage, }; rtnl_route_set_cacheinfo(route, &nci); } static int route_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who, struct nlmsghdr *nlh, struct nl_parser_param *pp) { struct rtmsg *rtm; struct rtnl_route *route; struct nlattr *tb[RTA_MAX + 1]; struct nl_addr *src = NULL, *dst = NULL, *addr; int err; route = rtnl_route_alloc(); if (!route) { err = nl_errno(ENOMEM); goto errout; } route->ce_msgtype = nlh->nlmsg_type; err = nlmsg_parse(nlh, sizeof(struct rtmsg), tb, RTA_MAX, route_policy); if (err < 0) goto errout; rtm = nlmsg_data(nlh); rtnl_route_set_family(route, rtm->rtm_family); rtnl_route_set_tos(route, rtm->rtm_tos); rtnl_route_set_table(route, rtm->rtm_table); rtnl_route_set_type(route, rtm->rtm_type); rtnl_route_set_scope(route, rtm->rtm_scope); rtnl_route_set_protocol(route, rtm->rtm_protocol); rtnl_route_set_flags(route, rtm->rtm_flags); if (tb[RTA_DST]) { dst = nla_get_addr(tb[RTA_DST], rtm->rtm_family); if (dst == NULL) goto errout_errno; } else { dst = nl_addr_alloc(0); nl_addr_set_family(dst, rtm->rtm_family); } nl_addr_set_prefixlen(dst, rtm->rtm_dst_len); err = rtnl_route_set_dst(route, dst); if (err < 0) goto errout; nl_addr_put(dst); if (tb[RTA_SRC]) { src = nla_get_addr(tb[RTA_SRC], rtm->rtm_family); if (src == NULL) goto errout_errno; } else if (rtm->rtm_src_len) src = nl_addr_alloc(0); if (src) { nl_addr_set_prefixlen(src, rtm->rtm_src_len); rtnl_route_set_src(route, src); nl_addr_put(src); } if (tb[RTA_IIF]) rtnl_route_set_iif(route, nla_get_string(tb[RTA_IIF])); if (tb[RTA_OIF]) rtnl_route_set_oif(route, nla_get_u32(tb[RTA_OIF])); if (tb[RTA_GATEWAY]) { addr = nla_get_addr(tb[RTA_GATEWAY], route->rt_family); if (addr == NULL) goto errout_errno; rtnl_route_set_gateway(route, addr); nl_addr_put(addr); } if (tb[RTA_PRIORITY]) rtnl_route_set_prio(route, nla_get_u32(tb[RTA_PRIORITY])); if (tb[RTA_PREFSRC]) { addr = nla_get_addr(tb[RTA_PREFSRC], route->rt_family); if (addr == NULL) goto errout_errno; rtnl_route_set_pref_src(route, addr); nl_addr_put(addr); } if (tb[RTA_METRICS]) { struct nlattr *mtb[RTAX_MAX + 1]; int i; err = nla_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS], NULL); if (err < 0) goto errout; for (i = 1; i <= RTAX_MAX; i++) { if (mtb[i] && nla_len(mtb[i]) >= sizeof(uint32_t)) { uint32_t m = nla_get_u32(mtb[i]); if (rtnl_route_set_metric(route, i, m) < 0) goto errout_errno; } } } if (tb[RTA_MULTIPATH]) { struct rtnl_nexthop *nh; struct rtnexthop *rtnh = nla_data(tb[RTA_MULTIPATH]); size_t tlen = nla_len(tb[RTA_MULTIPATH]); while (tlen >= sizeof(*rtnh) && tlen >= rtnh->rtnh_len) { nh = rtnl_route_nh_alloc(); if (!nh) goto errout; rtnl_route_nh_set_weight(nh, rtnh->rtnh_hops); rtnl_route_nh_set_ifindex(nh, rtnh->rtnh_ifindex); rtnl_route_nh_set_flags(nh, rtnh->rtnh_flags); if (rtnh->rtnh_len > sizeof(*rtnh)) { struct nlattr *ntb[RTA_MAX + 1]; nla_parse(ntb, RTA_MAX, (struct nlattr *) RTNH_DATA(rtnh), rtnh->rtnh_len - sizeof(*rtnh), route_policy); if (ntb[RTA_GATEWAY]) { nh->rtnh_gateway = nla_get_addr( ntb[RTA_GATEWAY], route->rt_family); nh->rtnh_mask = NEXTHOP_HAS_GATEWAY; } } rtnl_route_add_nexthop(route, nh); tlen -= RTNH_ALIGN(rtnh->rtnh_len); rtnh = RTNH_NEXT(rtnh); } } if (tb[RTA_FLOW]) rtnl_route_set_realms(route, nla_get_u32(tb[RTA_FLOW])); if (tb[RTA_CACHEINFO]) copy_cacheinfo_into_route(nla_data(tb[RTA_CACHEINFO]), route); if (tb[RTA_MP_ALGO]) rtnl_route_set_mp_algo(route, nla_get_u32(tb[RTA_MP_ALGO])); err = pp->pp_cb((struct nl_object *) route, pp); if (err < 0) goto errout; err = P_ACCEPT; errout: rtnl_route_put(route); return err; errout_errno: err = nl_get_errno(); goto errout; } static int route_request_update(struct nl_cache *c, struct nl_handle *h) { return nl_rtgen_request(h, RTM_GETROUTE, AF_UNSPEC, NLM_F_DUMP); } /** * @name Cache Management * @{ */ /** * Build a route cache holding all routes currently configured in the kernel * @arg handle netlink handle * * Allocates a new cache, initializes it properly and updates it to * contain all routes currently configured in the kernel. * * @note The caller is responsible for destroying and freeing the * cache after using it. * @return The cache or NULL if an error has occured. */ struct nl_cache *rtnl_route_alloc_cache(struct nl_handle *handle) { struct nl_cache *cache; cache = nl_cache_alloc(&rtnl_route_ops); if (!cache) return NULL; if (handle && nl_cache_refill(handle, cache) < 0) { free(cache); return NULL; } return cache; } /** @} */ /** * @name Route Addition * @{ */ static struct nl_msg *build_route_msg(struct rtnl_route *tmpl, int cmd, int flags) { struct nl_msg *msg; struct nl_addr *addr; int scope, i, oif, nmetrics = 0; struct nlattr *metrics; struct rtmsg rtmsg = { .rtm_family = rtnl_route_get_family(tmpl), .rtm_dst_len = rtnl_route_get_dst_len(tmpl), .rtm_src_len = rtnl_route_get_src_len(tmpl), .rtm_tos = rtnl_route_get_tos(tmpl), .rtm_table = rtnl_route_get_table(tmpl), .rtm_type = rtnl_route_get_type(tmpl), .rtm_protocol = rtnl_route_get_protocol(tmpl), .rtm_flags = rtnl_route_get_flags(tmpl), }; if (rtmsg.rtm_family == AF_UNSPEC) { nl_error(EINVAL, "Cannot build route message, address " \ "family is unknown."); return NULL; } scope = rtnl_route_get_scope(tmpl); if (scope == RT_SCOPE_NOWHERE) { if (rtmsg.rtm_type == RTN_LOCAL) scope = RT_SCOPE_HOST; else { /* XXX Change to UNIVERSE if gw || nexthops */ scope = RT_SCOPE_LINK; } } rtmsg.rtm_scope = scope; msg = nlmsg_alloc_simple(cmd, flags); if (msg == NULL) return NULL; if (nlmsg_append(msg, &rtmsg, sizeof(rtmsg), NLMSG_ALIGNTO) < 0) goto nla_put_failure; addr = rtnl_route_get_dst(tmpl); if (addr) NLA_PUT_ADDR(msg, RTA_DST, addr); addr = rtnl_route_get_src(tmpl); if (addr) NLA_PUT_ADDR(msg, RTA_SRC, addr); addr = rtnl_route_get_gateway(tmpl); if (addr) NLA_PUT_ADDR(msg, RTA_GATEWAY, addr); addr = rtnl_route_get_pref_src(tmpl); if (addr) NLA_PUT_ADDR(msg, RTA_PREFSRC, addr); NLA_PUT_U32(msg, RTA_PRIORITY, rtnl_route_get_prio(tmpl)); oif = rtnl_route_get_oif(tmpl); if (oif != RTNL_LINK_NOT_FOUND) NLA_PUT_U32(msg, RTA_OIF, oif); for (i = 1; i <= RTAX_MAX; i++) if (rtnl_route_get_metric(tmpl, i) != UINT_MAX) nmetrics++; if (nmetrics > 0) { unsigned int val; metrics = nla_nest_start(msg, RTA_METRICS); if (metrics == NULL) goto nla_put_failure; for (i = 1; i <= RTAX_MAX; i++) { val = rtnl_route_get_metric(tmpl, i); if (val != UINT_MAX) NLA_PUT_U32(msg, i, val); } nla_nest_end(msg, metrics); } #if 0 RTA_IIF, RTA_MULTIPATH, RTA_PROTOINFO, RTA_FLOW, RTA_CACHEINFO, RTA_SESSION, RTA_MP_ALGO, #endif return msg; nla_put_failure: nlmsg_free(msg); return NULL; } struct nl_msg *rtnl_route_build_add_request(struct rtnl_route *tmpl, int flags) { return build_route_msg(tmpl, RTM_NEWROUTE, NLM_F_CREATE | flags); } int rtnl_route_add(struct nl_handle *handle, struct rtnl_route *route, int flags) { struct nl_msg *msg; int err; msg = rtnl_route_build_add_request(route, flags); if (!msg) return nl_get_errno(); err = nl_send_auto_complete(handle, msg); nlmsg_free(msg); if (err < 0) return err; return nl_wait_for_ack(handle); } struct nl_msg *rtnl_route_build_del_request(struct rtnl_route *tmpl, int flags) { return build_route_msg(tmpl, RTM_DELROUTE, flags); } int rtnl_route_del(struct nl_handle *handle, struct rtnl_route *route, int flags) { struct nl_msg *msg; int err; msg = rtnl_route_build_del_request(route, flags); if (!msg) return nl_get_errno(); err = nl_send_auto_complete(handle, msg); nlmsg_free(msg); if (err < 0) return err; return nl_wait_for_ack(handle); } /** @} */ static struct nl_af_group route_groups[] = { { AF_INET, RTNLGRP_IPV4_ROUTE }, { AF_INET6, RTNLGRP_IPV6_ROUTE }, { AF_DECnet, RTNLGRP_DECnet_ROUTE }, { END_OF_GROUP_LIST }, }; static struct nl_cache_ops rtnl_route_ops = { .co_name = "route/route", .co_hdrsize = sizeof(struct rtmsg), .co_msgtypes = { { RTM_NEWROUTE, NL_ACT_NEW, "new" }, { RTM_DELROUTE, NL_ACT_DEL, "del" }, { RTM_GETROUTE, NL_ACT_GET, "get" }, END_OF_MSGTYPES_LIST, }, .co_protocol = NETLINK_ROUTE, .co_groups = route_groups, .co_request_update = route_request_update, .co_msg_parser = route_msg_parser, .co_obj_ops = &route_obj_ops, }; static void __init route_init(void) { nl_cache_mngt_register(&rtnl_route_ops); } static void __exit route_exit(void) { nl_cache_mngt_unregister(&rtnl_route_ops); }
int print_mroute(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { FILE *fp = (FILE*)arg; struct rtmsg *r = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr * tb[RTA_MAX+1]; char abuf[256]; char obuf[256]; SPRINT_BUF(b1); __u32 table; int iif = 0; int family; if ((n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) || !(n->nlmsg_flags & NLM_F_MULTI)) { fprintf(stderr, "Not a multicast route: %08x %08x %08x\n", n->nlmsg_len, n->nlmsg_type, n->nlmsg_flags); return 0; } len -= NLMSG_LENGTH(sizeof(*r)); if (len < 0) { fprintf(stderr, "BUG: wrong nlmsg len %d\n", len); return -1; } if (r->rtm_type != RTN_MULTICAST) { fprintf(stderr, "Not a multicast route (type: %s)\n", rtnl_rtntype_n2a(r->rtm_type, b1, sizeof(b1))); return 0; } parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); table = rtm_get_table(r, tb); if (filter.tb > 0 && filter.tb != table) return 0; if (tb[RTA_IIF]) iif = *(int*)RTA_DATA(tb[RTA_IIF]); if (filter.iif && filter.iif != iif) return 0; if (filter.af && filter.af != r->rtm_family) return 0; if (tb[RTA_DST] && filter.mdst.bitlen > 0 && inet_addr_match(RTA_DATA(tb[RTA_DST]), &filter.mdst, filter.mdst.bitlen)) return 0; if (tb[RTA_SRC] && filter.msrc.bitlen > 0 && inet_addr_match(RTA_DATA(tb[RTA_SRC]), &filter.msrc, filter.msrc.bitlen)) return 0; family = r->rtm_family == RTNL_FAMILY_IPMR ? AF_INET : AF_INET6; if (n->nlmsg_type == RTM_DELROUTE) fprintf(fp, "Deleted "); if (tb[RTA_SRC]) len = snprintf(obuf, sizeof(obuf), "(%s, ", rt_addr_n2a(family, RTA_PAYLOAD(tb[RTA_SRC]), RTA_DATA(tb[RTA_SRC]), abuf, sizeof(abuf))); else len = sprintf(obuf, "(unknown, "); if (tb[RTA_DST]) snprintf(obuf + len, sizeof(obuf) - len, "%s)", rt_addr_n2a(family, RTA_PAYLOAD(tb[RTA_DST]), RTA_DATA(tb[RTA_DST]), abuf, sizeof(abuf))); else snprintf(obuf + len, sizeof(obuf) - len, "unknown) "); fprintf(fp, "%-32s Iif: ", obuf); if (iif) fprintf(fp, "%-10s ", ll_index_to_name(iif)); else fprintf(fp, "unresolved "); if (tb[RTA_MULTIPATH]) { struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]); int first = 1; len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); for (;;) { if (len < sizeof(*nh)) break; if (nh->rtnh_len > len) break; if (first) { fprintf(fp, "Oifs: "); first = 0; } fprintf(fp, "%s", ll_index_to_name(nh->rtnh_ifindex)); if (nh->rtnh_hops > 1) fprintf(fp, "(ttl %d) ", nh->rtnh_hops); else fprintf(fp, " "); len -= NLMSG_ALIGN(nh->rtnh_len); nh = RTNH_NEXT(nh); } } if (show_stats && tb[RTA_MFC_STATS]) { struct rta_mfc_stats *mfcs = RTA_DATA(tb[RTA_MFC_STATS]); fprintf(fp, "%s %"PRIu64" packets, %"PRIu64" bytes", _SL_, (uint64_t)mfcs->mfcs_packets, (uint64_t)mfcs->mfcs_bytes); if (mfcs->mfcs_wrong_if) fprintf(fp, ", %"PRIu64" arrived on wrong iif.", (uint64_t)mfcs->mfcs_wrong_if); } fprintf(fp, "\n"); fflush(fp); return 0; }
/* * route_get_gw: if the route stored in `who' and `n' is matched by the * `filter', it stores the gateway address of that route in `arg', which * is a pointer to an inet_prefix struct. The address is stored in host order. * The dev name of the route is appended at `arg'+sizeof(inet_prefix). * Only the non-deleted routes are considered. */ int route_get_gw(const struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { struct rtmsg *r = NLMSG_DATA(n); int len = n->nlmsg_len; struct rtattr *tb[RTA_MAX + 1]; inet_prefix dst; inet_prefix via; int host_len = -1; if (n->nlmsg_type != RTM_NEWROUTE && n->nlmsg_type != RTM_DELROUTE) return 0; if (filter.flushb && n->nlmsg_type != RTM_NEWROUTE) return 0; len -= NLMSG_LENGTH(sizeof(*r)); if (len < 0) return -1; if (r->rtm_family == AF_INET6) host_len = 128; else if (r->rtm_family == AF_INET) host_len = 32; else if (r->rtm_family == AF_DECnet) host_len = 16; else if (r->rtm_family == AF_IPX) host_len = 80; if (r->rtm_family == AF_INET6) { if (filter.tb) { if (filter.tb < 0) { if (!(r->rtm_flags & RTM_F_CLONED)) return 0; } else { if (r->rtm_flags & RTM_F_CLONED) return 0; if (filter.tb == RT_TABLE_LOCAL) { if (r->rtm_type != RTN_LOCAL) return 0; } else if (filter.tb == RT_TABLE_MAIN) { if (r->rtm_type == RTN_LOCAL) return 0; } else { return 0; } } } } else { if (filter.tb > 0 && filter.tb != r->rtm_table) return 0; } if ((filter.protocol ^ r->rtm_protocol) & filter.protocolmask) return 0; if ((filter.scope ^ r->rtm_scope) & filter.scopemask) return 0; if ((filter.type ^ r->rtm_type) & filter.typemask) return 0; if ((filter.tos ^ r->rtm_tos) & filter.tosmask) return 0; if (filter.rdst.family && (r->rtm_family != filter.rdst.family || filter.rdst.bits > r->rtm_dst_len)) return 0; if (filter.mdst.family && (r->rtm_family != filter.mdst.family || (filter.mdst.bits < r->rtm_dst_len))) return 0; if (filter.rsrc.family && (r->rtm_family != filter.rsrc.family || filter.rsrc.bits > r->rtm_src_len)) return 0; if (filter.msrc.family && (r->rtm_family != filter.msrc.family || (filter.msrc.bits < r->rtm_src_len))) return 0; if (filter.rvia.family && r->rtm_family != filter.rvia.family) return 0; if (filter.rprefsrc.family && r->rtm_family != filter.rprefsrc.family) return 0; parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); setzero(&dst, sizeof(dst)); dst.family = r->rtm_family; if (tb[RTA_DST]) { memcpy(&dst.data, RTA_DATA(tb[RTA_DST]), (r->rtm_dst_len + 7) / 8); } if (filter.rdst.family && inet_addr_match(&dst, &filter.rdst, filter.rdst.bits)) return 0; if (filter.mdst.family && inet_addr_match(&dst, &filter.mdst, r->rtm_dst_len)) return 0; if (n->nlmsg_type == RTM_DELROUTE) return 0; /* * ... and finally if all the tests passed, copy the gateway address */ if (tb[RTA_GATEWAY]) { memcpy(&via.data, RTA_DATA(tb[RTA_GATEWAY]), host_len / 8); via.family = r->rtm_family; inet_setip(arg, (u_int *) & via.data, via.family); } else if (tb[RTA_MULTIPATH]) { struct rtnexthop *nh = RTA_DATA(tb[RTA_MULTIPATH]); len = RTA_PAYLOAD(tb[RTA_MULTIPATH]); for (;;) { if (len < sizeof(*nh)) break; if (nh->rtnh_len > len) break; if (r->rtm_flags & RTM_F_CLONED && r->rtm_type == RTN_MULTICAST) goto skip_nexthop; if (nh->rtnh_len > sizeof(*nh)) { parse_rtattr(tb, RTA_MAX, RTNH_DATA(nh), nh->rtnh_len - sizeof(*nh)); if (tb[RTA_GATEWAY]) { memcpy(&via.data, RTA_DATA(tb[RTA_GATEWAY]), host_len / 8); via.family = r->rtm_family; inet_setip(arg, (u_int *) & via.data, via.family); /* Copy the interface name */ strncpy((char *) arg + sizeof(inet_prefix), ll_index_to_name(nh->rtnh_ifindex), IFNAMSIZ); break; } } skip_nexthop: len -= NLMSG_ALIGN(nh->rtnh_len); nh = RTNH_NEXT(nh); } } /* Copy the interface name */ if (tb[RTA_OIF] && filter.oifmask != -1) strncpy((char *) arg + sizeof(inet_prefix), ll_index_to_name(*(int *) RTA_DATA(tb[RTA_OIF])), IFNAMSIZ); return 0; }
int add_nexthops(struct nlmsghdr *n, struct rtmsg *r, struct nexthop *nhop) { char buf[1024]; struct rtattr *rta = (void *) buf; struct rtnexthop *rtnh; int i = 0, idx; rta->rta_type = RTA_MULTIPATH; rta->rta_len = RTA_LENGTH(0); rtnh = RTA_DATA(rta); if (!nhop[i + 1].dev) { /* Just one gateway */ r->rtm_family = nhop[i].gw.family; addattr_l(n, sizeof(struct rt_request), RTA_GATEWAY, &nhop[i].gw.data, nhop[i].gw.len); if (nhop[0].dev) { if ((idx = ll_name_to_index(nhop[0].dev)) == 0) { error(ERROR_MSG "Device \"%s\" doesn't really exist\n", ERROR_POS, nhop[0].dev); return -1; } addattr32(n, sizeof(struct rt_request), RTA_OIF, idx); } return 0; } #if 0 /* We have more than one nexthop, equalize them */ req.rt.rtm_flags |= RTM_F_EQUALIZE; #endif while (nhop[i].dev != 0) { setzero(rtnh, sizeof(*rtnh)); rtnh->rtnh_len = sizeof(*rtnh); rta->rta_len += rtnh->rtnh_len; if (nhop[i].gw.len) { if (nhop[i].gw.family == AF_INET) rta_addattr32(rta, 4096, RTA_GATEWAY, nhop[i].gw.data[0]); else if (nhop[i].gw.family == AF_INET6) rta_addattr_l(rta, 4096, RTA_GATEWAY, nhop[i].gw.data, nhop[i].gw.len); rtnh->rtnh_len += sizeof(struct rtattr) + nhop[i].gw.len; } if (nhop[i].dev) if ((rtnh->rtnh_ifindex = ll_name_to_index(nhop[i].dev)) == 0) fatal("%s:%d, Cannot find device \"%s\"\n", ERROR_POS, nhop[i].dev); if (nhop[i].hops == 0) { debug(DBG_NORMAL, "hops=%d is invalid. Using hops=255\n", nhop[i].hops); rtnh->rtnh_hops = 255; } else rtnh->rtnh_hops = nhop[i].hops - 1; rtnh = RTNH_NEXT(rtnh); i++; } if (rta->rta_len > RTA_LENGTH(0)) addattr_l(n, 1024, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta)); return 0; }
int multipath_route_add(struct sockaddr_storage *destination, vector *gateways,int prefix, unsigned int metric){ struct rtnl_handle rth; // structure of the netlink packet. struct{ struct nlmsghdr n; struct rtmsg r; char buf[1024]; } req; memset(&req, 0, sizeof(req)); // Initialisation of a few parameters req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct rtmsg)); req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE; req.n.nlmsg_type = RTM_NEWROUTE; req.r.rtm_family = destination->ss_family; req.r.rtm_table = get_eigrp_routing_table_number(); req.r.rtm_dst_len = prefix; req.r.rtm_protocol = get_eigrp_routing_protocol_number(); req.r.rtm_scope = RT_SCOPE_UNIVERSE; req.r.rtm_type = RTN_UNICAST; // RTA_DST and RTA_GW are the two esential parameters for adding a route // for ipv4, the length of the address is 4 bytes. if(destination->ss_family == AF_INET){ struct sockaddr_in *dest = (struct sockaddr_in *)destination; addattr_l(&req.n, sizeof(req), RTA_DST, &dest->sin_addr.s_addr, 4); addattr_l(&req.n, sizeof(req), RTA_PRIORITY, &metric, 4); char buf[NL_PKT_BUF_SIZE]; struct rtattr *rta = (void *)buf; struct rtnexthop *rtnh; rta->rta_type = RTA_MULTIPATH; rta->rta_len = RTA_LENGTH(0); rtnh = RTA_DATA(rta); int i; for(i=0;i<gateways->size;i++){ struct sockaddr_in *gate = (struct sockaddr_in *)vector_get(gateways,i); rtnh->rtnh_len = sizeof(*rtnh); rtnh->rtnh_flags = 0; rtnh->rtnh_hops = gate->sin_port; //sockaddr_in.sin_port is used as the weight of the route, -1 is because rta adds plus one by it self rta->rta_len += rtnh->rtnh_len; char address[INET6_ADDRSTRLEN]; ip_tochar(&address,vector_get(gateways,i)); printf("Route through %s weight %d\n",address,rtnh->rtnh_hops); rta_addattr_l(rta, NL_PKT_BUF_SIZE, RTA_GATEWAY, &gate->sin_addr.s_addr, 4); rtnh->rtnh_len += (sizeof(struct rtattr) + 4); rtnh = RTNH_NEXT(rtnh); } addattr_l(&req.n, NL_PKT_BUF_SIZE, RTA_MULTIPATH, RTA_DATA(rta), RTA_PAYLOAD(rta)); } //addattr_l(&req.n, sizeof(req), RTA_PRIORITY, metric, 4); int status = 0; // opening the netlink socket to communicate with the kernel if (rtnl_open(&rth, 0) < 0){ printf("cannot open rtnetlink\n"); return -1; } // sending the packet to the kernel. status = rtnl_talk(&rth, &req.n, 0, 0, NULL); if (status < 0) return status; rtnl_close(&rth); return 0; }
static int route_msg_parser(struct sockaddr_nl *who, struct nlmsghdr *n, void *arg) { struct nl_parser_param *pp = arg; struct rtnl_route route = RTNL_INIT_ROUTE(); struct rtattr *tb[RTA_MAX + 1]; struct rtmsg *r = NLMSG_DATA(n); size_t len; int err; if (n->nlmsg_type != RTM_NEWROUTE) return P_IGNORE; len = n->nlmsg_len - NLMSG_LENGTH(sizeof(*r)); if (len < 0) return nl_error(EINVAL, "netlink message too short to be a " \ "routing message"); err = nl_parse_rtattr(tb, RTA_MAX, RTM_RTA(r), len); if (err < 0) return err; route.rt_family = r->rtm_family; route.rt_dst_len = r->rtm_dst_len; route.rt_src_len = r->rtm_src_len; route.rt_tos = r->rtm_tos; route.rt_table = r->rtm_table; route.rt_protocol = r->rtm_protocol; route.rt_scope = r->rtm_scope; route.rt_type = r->rtm_type; route.rt_flags = r->rtm_flags; route.rt_mask = (ROUTE_HAS_FAMILY|ROUTE_HAS_DST_LEN|ROUTE_HAS_SRC_LEN| ROUTE_HAS_TOS|ROUTE_HAS_TABLE|ROUTE_HAS_PROTOCOL|ROUTE_HAS_SCOPE| ROUTE_HAS_TYPE|ROUTE_HAS_FLAGS); if (tb[RTA_DST]) { nl_copy_addr(&route.rt_dst, tb[RTA_DST]); route.rt_dst.a_prefix = route.rt_dst_len; route.rt_dst.a_family = route.rt_family; route.rt_mask |= ROUTE_HAS_DST; } if (tb[RTA_SRC]) { nl_copy_addr(&route.rt_src, tb[RTA_SRC]); route.rt_src.a_prefix = route.rt_src_len; route.rt_src.a_family = route.rt_family; route.rt_mask |= ROUTE_HAS_SRC; } if (tb[RTA_IIF]) { err = NL_COPY_DATA(route.rt_iif, tb[RTA_IIF]); if (err < 0) return err; route.rt_mask |= ROUTE_HAS_IIF; } if (tb[RTA_OIF]) { err = NL_COPY_DATA(route.rt_oif, tb[RTA_OIF]); if (err < 0) return err; route.rt_mask |= ROUTE_HAS_OIF; } if (tb[RTA_GATEWAY]) { nl_copy_addr(&route.rt_gateway, tb[RTA_GATEWAY]); route.rt_gateway.a_family = route.rt_family; route.rt_mask |= ROUTE_HAS_GATEWAY; } if (tb[RTA_PRIORITY]) { err = NL_COPY_DATA(route.rt_prio, tb[RTA_PRIORITY]); if (err < 0) return err; route.rt_mask |= ROUTE_HAS_PRIO; } if (tb[RTA_PREFSRC]) { nl_copy_addr(&route.rt_pref_src, tb[RTA_PREFSRC]); route.rt_pref_src.a_family = route.rt_family; route.rt_mask |= ROUTE_HAS_PREF_SRC; } if (tb[RTA_METRICS]) { int i; struct rtattr *mtb[RTAX_MAX + 1]; err = nl_parse_nested(mtb, RTAX_MAX, tb[RTA_METRICS]); if (err < 0) return err; for (i = 1; i <= RTAX_MAX; i++) { if (mtb[i]) { uint32_t m = *(uint32_t *) RTA_DATA(mtb[i]); route.rt_metrics[i] = m; route.rt_metrics_mask |= (1<<(i-1)); } } route.rt_mask |= ROUTE_HAS_METRICS; } if (tb[RTA_MULTIPATH]) { struct rtnl_nexthop *nh; struct rtnexthop *rtnh = RTA_DATA(tb[RTA_MULTIPATH]); size_t tlen = RTA_PAYLOAD(tb[RTA_MULTIPATH]); for (;;) { if (tlen < sizeof(*rtnh) || tlen < rtnh->rtnh_len) break; nh = calloc(1, sizeof(*nh)); if (nh == NULL) { err = -ENOMEM; goto err_out; } nh->rtnh_flags = rtnh->rtnh_flags; nh->rtnh_hops = rtnh->rtnh_hops; nh->rtnh_ifindex = rtnh->rtnh_ifindex; nh->rtnh_mask = (NEXTHOP_HAS_FLAGS | NEXTHOP_HAS_HOPS | NEXTHOP_HAS_IFINDEX); if (rtnh->rtnh_len > sizeof(*rtnh)) { struct rtattr *ntb[RTA_MAX + 1]; nl_parse_rtattr(ntb, RTA_MAX, RTNH_DATA(rtnh), rtnh->rtnh_len - sizeof(*rtnh)); if (ntb[RTA_GATEWAY]) { nl_copy_addr(&nh->rtnh_gateway, ntb[RTA_GATEWAY]); nh->rtnh_gateway.a_family = route.rt_family; nh->rtnh_mask = NEXTHOP_HAS_GATEWAY; } } nh->rtnh_next = route.rt_nexthops; route.rt_nexthops = nh; len -= RTNH_ALIGN(rtnh->rtnh_len); rtnh = RTNH_NEXT(rtnh); } route.rt_mask |= ROUTE_HAS_MULTIPATH; } /* Not sure if there are any users not using this for fwmark, * allocating for now */ if (tb[RTA_PROTOINFO]) { err = nl_alloc_data_from_rtattr(&route.rt_protoinfo, tb[RTA_PROTOINFO]); if (err < 0) goto err_out; route.rt_mask |= ROUTE_HAS_PROTOINFO; } if (tb[RTA_FLOW]) { err = NL_COPY_DATA(route.rt_realm, tb[RTA_FLOW]); if (err < 0) goto err_out; route.rt_mask |= ROUTE_HAS_REALM; } if (tb[RTA_CACHEINFO]) { struct rta_cacheinfo *ci; if (RTA_PAYLOAD(tb[RTA_CACHEINFO]) < sizeof(struct rta_cacheinfo)) return nl_error(EINVAL, "routing cacheinfo TLV is too short"); ci = (struct rta_cacheinfo *) RTA_DATA(tb[RTA_CACHEINFO]); route.rt_cacheinfo.rtci_clntref = ci->rta_clntref; route.rt_cacheinfo.rtci_last_use = ci->rta_lastuse; route.rt_cacheinfo.rtci_expires = ci->rta_expires; route.rt_cacheinfo.rtci_error = ci->rta_error; route.rt_cacheinfo.rtci_used = ci->rta_used; route.rt_cacheinfo.rtci_id = ci->rta_id; route.rt_cacheinfo.rtci_ts = ci->rta_ts; route.rt_cacheinfo.rtci_tsage = ci->rta_tsage; route.rt_mask |= ROUTE_HAS_CACHEINFO; } if (tb[RTA_SESSION]) { err = nl_alloc_data_from_rtattr(&route.rt_session, tb[RTA_SESSION]); if (err < 0) goto err_out; route.rt_mask |= ROUTE_HAS_SESSION; } err = pp->pp_cb((struct nl_common *) &route, pp); if (err < 0) goto err_out; return P_ACCEPT; err_out: route_free_data((struct nl_common *) &route); return err; }
static switchlink_handle_t process_ecmp(uint8_t family, struct nlattr *attr, switchlink_handle_t vrf_h) { switchlink_db_status_t status; if ((family != AF_INET) && (family != AF_INET6)) { return 0; } switchlink_db_ecmp_info_t ecmp_info; memset(&ecmp_info, 0, sizeof(switchlink_db_ecmp_info_t)); struct rtnexthop *rnh = (struct rtnexthop *)nla_data(attr); int attrlen = nla_len(attr); while (RTNH_OK(rnh, attrlen)) { struct rtattr *rta = RTNH_DATA(rnh); if (rta->rta_type == RTA_GATEWAY) { switchlink_ip_addr_t gateway; memset(&gateway, 0, sizeof(switchlink_ip_addr_t)); gateway.family = family; if (family == AF_INET) { gateway.ip.v4addr.s_addr = ntohl(*((uint32_t *)RTA_DATA(rta))); gateway.prefix_len = 32; } else { gateway.prefix_len = 128; } switchlink_db_interface_info_t ifinfo; memset(&ifinfo, 0, sizeof(switchlink_db_interface_info_t)); status = switchlink_db_interface_get_info(rnh->rtnh_ifindex, &ifinfo); if (status == SWITCHLINK_DB_STATUS_SUCCESS) { switchlink_db_neigh_info_t neigh_info; memset(&neigh_info, 0, sizeof(switchlink_db_neigh_info_t)); memcpy(&(neigh_info.ip_addr), &gateway, sizeof(switchlink_ip_addr_t)); neigh_info.intf_h = ifinfo.intf_h; neigh_info.vrf_h = vrf_h; status = switchlink_db_neighbor_get_info(&neigh_info); if (status == SWITCHLINK_DB_STATUS_SUCCESS) { ecmp_info.nhops[ecmp_info.num_nhops] = neigh_info.nhop_h; } else { ecmp_info.nhops[ecmp_info.num_nhops] = g_cpu_rx_nhop_h; } ecmp_info.num_nhops++; assert(ecmp_info.num_nhops < SWITCHLINK_ECMP_NUM_MEMBERS_MAX); } } rnh = RTNH_NEXT(rnh); } if (!ecmp_info.num_nhops) { return 0; } status = switchlink_db_ecmp_get_info(&ecmp_info); if (status == SWITCHLINK_DB_STATUS_ITEM_NOT_FOUND) { switchlink_ecmp_create(&ecmp_info); switchlink_db_ecmp_add(&ecmp_info); } return ecmp_info.ecmp_h; }
static boolean_t addRoute(route_entry_t *entry) { static int skfd = -1; #if !defined(freebsd8) struct rtentry rt; #else struct ortentry rt; #endif if (entry) { memset((char *) &rt, 0, sizeof (rt)); rt.rt_flags |= RTF_UP; if (entry->mask == 0xffffffff) { rt.rt_flags |= RTF_HOST; } #if !defined(freebsd8) rt.rt_metric = 0; ((struct sockaddr_in *) &rt.rt_genmask)->sin_addr.s_addr = entry->mask; ((struct sockaddr_in *) &rt.rt_genmask)->sin_family = AF_INET; if (entry->iface[0] != '\0') { rt.rt_dev = entry->iface; } #else #endif ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr = entry->dst; ((struct sockaddr_in *) &rt.rt_dst)->sin_family = AF_INET; ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = entry->gateway; ((struct sockaddr_in *) &rt.rt_gateway)->sin_family = AF_INET; rt.rt_flags |= RTF_GATEWAY; #if !defined(freebsd8) if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { #else if ((skfd = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { #endif DEBUGP(DERROR, "addRoute", "socket: %s", strerror(errno)); return FALSE; } #if !defined(freebsd8) if (ioctl(skfd, SIOCADDRT, &rt) < 0) { DEBUGP(DERROR, "addRoute", "ioctl: %s", strerror(errno)); close(skfd); return FALSE; } #else struct { struct rt_msghdr rtm; struct sockaddr addrs[RTAX_MAX]; } r; memset(&r, 0, sizeof (r)); r.rtm.rtm_version = RTM_VERSION; r.rtm.rtm_type = RTM_ADD; r.rtm.rtm_pid = getpid(); r.rtm.rtm_seq = 0; struct sockaddr_in dst = {.sin_addr.s_addr = entry->dst, .sin_family = AF_INET, .sin_len = sizeof (struct sockaddr_in)}; struct sockaddr_in gw = {.sin_addr.s_addr = entry->gateway, .sin_family = AF_INET, .sin_len = sizeof (struct sockaddr_in)}; struct sockaddr_in mask = {.sin_addr.s_addr = entry->mask, .sin_family = AF_INET, .sin_len = sizeof (struct sockaddr_in)}; memcpy(&r.addrs[RTAX_DST], &dst, dst.sin_len); memcpy(&r.addrs[RTAX_GATEWAY], &gw, gw.sin_len); memcpy(&r.addrs[RTAX_NETMASK], &mask, mask.sin_len); r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY; r.rtm.rtm_flags = RTF_STATIC | RTF_GATEWAY; r.rtm.rtm_msglen = sizeof (r); if (entry->mask != 0xffffffff) { r.rtm.rtm_addrs |= RTA_NETMASK; } else { r.rtm.rtm_flags |= (RTF_HOST); } if (write(skfd, &r, r.rtm.rtm_msglen) != r.rtm.rtm_msglen) { DEBUGP(DERROR, "addRoute", "write: %s", strerror(errno)); close(skfd); return FALSE; } #endif close(skfd); return TRUE; } return FALSE; } static boolean_t delRoute(route_entry_t * entry) { static int skfd = -1; #if !defined(freebsd8) struct rtentry rt; #else struct ortentry rt; #endif if (entry) { memset((char *) &rt, 0, sizeof (rt)); rt.rt_flags |= RTF_UP; if (entry->mask == 0xffffffff) { rt.rt_flags |= RTF_HOST; } #if !defined(freebsd8) rt.rt_metric = 0; ((struct sockaddr_in *) &rt.rt_genmask)->sin_addr.s_addr = entry->mask; ((struct sockaddr_in *) &rt.rt_genmask)->sin_family = AF_INET; if (entry->iface[0] != '\0') { rt.rt_dev = entry->iface; } #else #endif ((struct sockaddr_in *) &rt.rt_dst)->sin_addr.s_addr = entry->dst; ((struct sockaddr_in *) &rt.rt_dst)->sin_family = AF_INET; ((struct sockaddr_in *) &rt.rt_gateway)->sin_addr.s_addr = entry->gateway; ((struct sockaddr_in *) &rt.rt_gateway)->sin_family = AF_INET; rt.rt_flags |= RTF_GATEWAY; #if !defined(freebsd8) if ((skfd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { #else if ((skfd = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { #endif DEBUGP(DERROR, "delRoute", "socket: %s", strerror(errno)); return FALSE; } #if !defined(freebsd8) if (ioctl(skfd, SIOCDELRT, &rt) < 0) { DEBUGP(DERROR, "delRoute", "ioctl: %s", strerror(errno)); close(skfd); return FALSE; } #else struct { struct rt_msghdr rtm; struct sockaddr addrs[RTAX_MAX]; } r; memset(&r, 0, sizeof (r)); r.rtm.rtm_version = RTM_VERSION; r.rtm.rtm_type = RTM_DELETE; r.rtm.rtm_pid = getpid(); r.rtm.rtm_seq = 0; struct sockaddr_in dst = {.sin_addr.s_addr = entry->dst, .sin_family = AF_INET, .sin_len = sizeof (struct sockaddr_in)}; struct sockaddr_in gw = {.sin_addr.s_addr = entry->gateway, .sin_family = AF_INET, .sin_len = sizeof (struct sockaddr_in)}; struct sockaddr_in mask = {.sin_addr.s_addr = entry->mask, .sin_family = AF_INET, .sin_len = sizeof (struct sockaddr_in)}; memcpy(&r.addrs[RTAX_DST], &dst, dst.sin_len); memcpy(&r.addrs[RTAX_GATEWAY], &gw, gw.sin_len); memcpy(&r.addrs[RTAX_NETMASK], &mask, mask.sin_len); r.rtm.rtm_addrs = RTA_DST | RTA_GATEWAY; r.rtm.rtm_flags = RTF_DONE; r.rtm.rtm_msglen = sizeof (r); if (entry->mask != 0xffffffff) { r.rtm.rtm_addrs |= RTA_NETMASK; } else { r.rtm.rtm_flags |= (RTF_HOST); } if (write(skfd, &r, r.rtm.rtm_msglen) != r.rtm.rtm_msglen) { DEBUGP(DERROR, "delRoute", "write: %s", strerror(errno)); close(skfd); return FALSE; } #endif close(skfd); return TRUE; } return FALSE; } static list_t *routeLookup(route_entry_t *key) { list_t *result = NULL; int skfd = -1; if (key) { #ifdef freebsd8 struct ortentry rt; memset((char *) &rt, 0, sizeof (rt)); rt.rt_flags |= RTF_UP; if (key->mask == 0xffffffff) { rt.rt_flags |= RTF_HOST; } rt.rt_flags |= RTF_GATEWAY; if ((skfd = socket(PF_ROUTE, SOCK_RAW, 0)) < 0) { DEBUGP(DERROR, "routeLookup", "socket: %s", strerror(errno)); return result; } struct { struct rt_msghdr rtm; struct sockaddr addrs[RTAX_MAX]; } r; memset(&r, 0, sizeof (r)); r.rtm.rtm_version = RTM_VERSION; r.rtm.rtm_type = RTM_GET; r.rtm.rtm_pid = getpid(); r.rtm.rtm_seq = 0; struct sockaddr_in dst = {.sin_addr.s_addr = key->dst, .sin_family = AF_INET, .sin_len = sizeof (struct sockaddr_in)}; struct sockaddr_in mask = {.sin_addr.s_addr = key->mask, .sin_family = AF_INET, .sin_len = sizeof (struct sockaddr_in)}; memcpy(&r.addrs[RTAX_DST], &dst, dst.sin_len); memcpy(&r.addrs[RTAX_NETMASK], &mask, mask.sin_len); r.rtm.rtm_addrs = RTA_DST; r.rtm.rtm_flags = RTF_DONE; r.rtm.rtm_msglen = sizeof (r); if (key->mask != 0xffffffff) { r.rtm.rtm_addrs |= RTA_NETMASK; } else { r.rtm.rtm_flags |= (RTF_HOST); } if (write(skfd, &r, r.rtm.rtm_msglen) != r.rtm.rtm_msglen) { DEBUGP(DERROR, "routeLookup", "write: %s", strerror(errno)); close(skfd); return result; } result = I(List)->new(); while (1) { if (read(skfd, (struct rt_msghdr *) &r, sizeof (r)) < 0) { DEBUGP(DERROR, "routeLookup", "read: %s", strerror(errno)); close(skfd); break; } route_entry_t *e = calloc(1, sizeof (route_entry_t)); e->gateway = ((struct sockaddr_in*) &r.addrs[RTAX_GATEWAY])->sin_addr.s_addr; I(List)->insert(result, I(ListItem)->new(e)); if (r.rtm.rtm_flags & RTF_DONE) { break; } } #endif #ifdef linux struct { struct nlmsghdr n; struct rtmsg r; char buf[1024]; } req; struct nhop { in_addr_t gw; char dev[IFNAMSIZ]; }; list_t *nhops = NULL; struct nlmsghdr *h; struct rtmsg *rtmp; struct rtattr *rtatp; int rtattrlen; int rval = -1; char buf[4096]; char dev[IFNAMSIZ]; in_addr_t src = 0; in_addr_t dst = 0; in_addr_t mask = 0; in_addr_t gw = 0; if ((skfd = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_ROUTE)) < 0) { DEBUGP(DERROR, "routeLookup", "socket: %s", strerror(errno)); return FALSE; } memset(&req, 0, sizeof (req)); req.n.nlmsg_len = NLMSG_LENGTH(sizeof (struct rtmsg)); req.n.nlmsg_flags = NLM_F_REQUEST | NLM_F_DUMP; req.n.nlmsg_type = RTM_GETROUTE; req.n.nlmsg_len = NLMSG_ALIGN(req.n.nlmsg_len); if (send(skfd, &req, req.n.nlmsg_len, 0) < 0) { DEBUGP(DERROR, "routeLookup", "send: %s", strerror(errno)); close(skfd); return result; } if ((rval = recv(skfd, buf, sizeof (buf), 0)) < 0) { DEBUGP(DERROR, "routeLookup", "recv: %s", strerror(errno)); close(skfd); return result; } for (h = (struct nlmsghdr *) buf; NLMSG_OK(h, rval); h = NLMSG_NEXT(h, rval)) { rtmp = (struct rtmsg *) NLMSG_DATA(h); rtatp = (struct rtattr *) RTM_RTA(rtmp); rtattrlen = RTM_PAYLOAD(h); src = 0; dst = 0; mask = 0; gw = 0; if (result == NULL) { result = I(List)->new(); } for (; RTA_OK(rtatp, rtattrlen); rtatp = RTA_NEXT(rtatp, rtattrlen)) { switch (rtatp->rta_type) { case RTA_OIF: { int oif_index = *(int *) RTA_DATA(rtatp); if_indextoname(oif_index, dev); break; } case RTA_PREFSRC: src = *((in_addr_t *) RTA_DATA(rtatp)); break; case RTA_DST: dst = *((in_addr_t *) RTA_DATA(rtatp)); mask = rtmp->rtm_dst_len != 0 ? htonl(~0 << (32 - rtmp->rtm_dst_len)) : 0; break; case RTA_GATEWAY: gw = *((in_addr_t *) RTA_DATA(rtatp)); break; case RTA_MULTIPATH: { nhops = I(List)->new(); struct rtnexthop *nh = NULL; struct rtattr *nhrtattr = NULL; int nh_len = RTA_PAYLOAD(rtatp); for (nh = RTA_DATA(rtatp); RTNH_OK(nh, nh_len); nh = RTNH_NEXT(nh)) { struct nhop *hop = calloc(1, sizeof (struct nhop)); int attr_len = nh->rtnh_len - sizeof (*nh); if (nh_len < sizeof (*nh)) break; if (nh->rtnh_len > nh_len) break; if (nh->rtnh_len > sizeof (*nh)) { if_indextoname(nh->rtnh_ifindex, hop->dev); for (nhrtattr = RTNH_DATA(nh); RTA_OK(nhrtattr, attr_len); nhrtattr = RTA_NEXT(nhrtattr, attr_len)) { switch (nhrtattr->rta_type) { case RTA_GATEWAY: hop->gw = *((in_addr_t *) RTA_DATA(nhrtattr)); break; } } I(List)->insert(nhops, I(ListItem)->new(hop)); } nh_len -= NLMSG_ALIGN(nh->rtnh_len); } break; } } } if (nhops == NULL) { if (key && ((key->dst != dst) || (key->iface[0] != '\0' && strcmp(key->iface, dev) != 0))) { continue; } route_entry_t *r = calloc(1, sizeof (route_entry_t)); r->gateway = gw; r->mask = mask; r->dst = dst; r->src = src; strcpy(r->iface, dev); I(List)->insert(result, I(ListItem)->new(r)); } else { struct nhop *h = NULL; list_item_t *item = NULL; while (item = I(List)->pop(nhops)) { h = item->data; if (key && ((key->dst != dst) || (key->iface[0] != '\0' && strcmp(key->iface, h->dev) != 0))) { I(ListItem)->destroy(&item); free(h); continue; } route_entry_t *r = calloc(1, sizeof (route_entry_t)); r->gateway = h->gw; r->mask = mask; r->dst = dst; r->src = src; strcpy(r->iface, h->dev); I(List)->insert(result, I(ListItem)->new(r)); I(ListItem)->destroy(&item); free(h); } I(List)->destroy(&nhops); } } #endif close(skfd); return result; } return result; } static list_t * cacheLookup(route_entry_t * dest) { list_t *result = NULL; #if defined(linux) FILE *f = fopen("/proc/net/rt_cache", "r"); if (f) { char buf[512]; result = I(List)->new(); fgets(buf, sizeof (buf), f); // skip header while (!feof(f)) { if (fgets(buf, sizeof (buf), f)) { list_t *fields = I(String)->tokenize(buf, "\t"); int i = 0; list_item_t *item; route_entry_t *entry = calloc(1, sizeof (route_entry_t)); while ((item = I(List)->pop(fields))) { switch (i) { case 0: strcpy(entry->iface, (char*) item->data); break; case 1: sscanf((char*) item->data, "%X", &entry->dst); break; case 2: sscanf((char*) item->data, "%X", &entry->gateway); break; case 7: sscanf((char*) item->data, "%X", &entry->src); break; } free(item->data); I(ListItem)->destroy(&item); i++; } I(List)->destroy(&fields); if (dest) { if (dest->dst && dest->dst != entry->dst) { free(entry); continue; } } I(List)->insert(result, I(ListItem)->new(entry)); } } fclose(f); } #endif return result; } static boolean_t addHostRoute(in_addr_t dst, in_addr_t gw, char *iface) { route_entry_t route = {.dst = dst, .src = 0, .gateway = gw, .mask = 0xffffffff}; boolean_t result = FALSE; if (iface) strcpy(route.iface, iface); else route.iface[0] = '\0'; result = addRoute(&route); return result; } static boolean_t delHostRoute(in_addr_t dst, in_addr_t gw, char *iface) { route_entry_t route = {.dst = dst, .src = 0, .gateway = gw, .mask = 0xffffffff}; boolean_t result = FALSE; if (iface) strcpy(route.iface, iface); else route.iface[0] = '\0'; result = delRoute(&route); return result; } static in_addr_t getIfIP(char *iface) { in_addr_t result = 0; if (iface) { struct ifreq req; int sock = socket(AF_INET, SOCK_DGRAM, 0); memset(&req, 0, sizeof (struct ifreq)); strcpy(req.ifr_name, iface); if (ioctl(sock, SIOCGIFADDR, &req) >= 0) { result = ((struct sockaddr_in *) &req.ifr_addr)->sin_addr.s_addr; } else { DEBUGP(DERROR, "getIfIP", "ioctl: %s", strerror(errno)); } close(sock); } return result; } static in_addr_t getIfGW(char *iface) { route_entry_t route = {.dst = 0, .src = 0, .gateway = 0, .mask = 0}; list_t *routes = NULL; in_addr_t result = 0; if (iface) strcpy(route.iface, iface); else route.iface[0] = '\0'; routes = routeLookup(&route); if (routes) { if (I(List)->count(routes) > 0) { list_item_t *e = I(List)->pop(routes); route_entry_t *entry = (route_entry_t *) e->data; result = entry->gateway; free(entry); I(ListItem)->destroy(&e); } I(List)->clear(routes, TRUE); I(List)->destroy(&routes); } return result; } IMPLEMENT_INTERFACE(Route) = { .addRoute = addRoute, .delRoute = delRoute, .cacheLookup = cacheLookup, .addHostRoute = addHostRoute, .delHostRoute = delHostRoute, .getIfGW = getIfGW, .getIfIP = getIfIP };
/* * parse route message */ int parse_rtmsg(struct nlmsghdr *nlh) { struct rtmsg *rtm; int rtm_len; struct rtattr *rta[__RTA_MAX]; char ipv[MAX_STR_SIZE] = ""; char msg[MAX_MSG_SIZE] = ""; char *mp = msg; int log_opts = get_log_opts(); int res; /* debug nlmsghdr */ if(log_opts & L_DEBUG) debug_nlmsg(0, nlh); /* get rtmsg */ rtm_len = NLMSG_PAYLOAD(nlh, 0); if(rtm_len < sizeof(*rtm)) { rec_log("error: %s: rtmsg: length too short", __func__); return(1); } rtm = (struct rtmsg *)NLMSG_DATA(nlh); /* parse route attributes */ parse_rtattr(rta, RTA_MAX, RTM_RTA(rtm), RTM_PAYLOAD(nlh)); /* debug rtmsg */ if(log_opts & L_DEBUG) debug_rtmsg(0, rtm, rta, rtm_len); /* check address family */ char dst[INET6_ADDRSTRLEN] = ""; if(rtm->rtm_family == AF_INET) { strcpy(ipv, "ipv4"); strcpy(dst, "0.0.0.0"); } else if(rtm->rtm_family == AF_INET6) { strcpy(ipv, "ipv6"); strcpy(dst, "::"); } else { rec_log("error: %s: unknown address family: %d", __func__, rtm->rtm_family); return(1); } /* convert from table id to table name */ char table[MAX_STR_SIZE] = ""; snprintf(table, sizeof(table), "%s", conv_rt_table(rtm->rtm_table, 0)); if(!strncmp(table, "unknown", sizeof(table))) snprintf(table, sizeof(table), "%d", rtm->rtm_table); /* check route table id(other than RT_TABLE_LOCAL) */ if(rtm->rtm_table == RT_TABLE_LOCAL) return(1); /* check route protocol(other than RTPROT_UNSPEC) */ if(rtm->rtm_protocol == RTPROT_UNSPEC) return(1); /* check route flags(other then RTM_F_CLONED) */ if(rtm->rtm_flags & RTM_F_CLONED) return(1); /* get destination prefix */ if(rta[RTA_DST]) { res = inet_ntop_ifa(rtm->rtm_family, rta[RTA_DST], dst, sizeof(dst)); if(res) { rec_log("error: %s: RTA_DST: %s", __func__, (res == 1) ? strerror(errno) : "payload too short"); return(1); } } /* no RTA_DST attribute if destination is a default gateway */ mp = add_log(msg, mp, "destination=%s/%d ", dst, rtm->rtm_dst_len); /* get source prefix */ if(rta[RTA_SRC]) { char src[INET6_ADDRSTRLEN] = ""; res = inet_ntop_ifa(rtm->rtm_family, rta[RTA_SRC], src, sizeof(src)); if(res == 1) { rec_log("error: %s: RTA_SRC: %s", __func__, (res == 1) ? strerror(errno) : "payload too short"); return(1); } mp = add_log(msg, mp, "source=%s/%d ", src, rtm->rtm_src_len); } /* get preferred source address */ if(rta[RTA_PREFSRC]) { char prefsrc[INET6_ADDRSTRLEN] = ""; res = inet_ntop_ifa(rtm->rtm_family, rta[RTA_PREFSRC], prefsrc, sizeof(prefsrc)); if(res) { rec_log("error: %s: RTA_PREFSRC: %s", __func__, (res == 1) ? strerror(errno) : "payload too short"); return(1); } mp = add_log(msg, mp, "preferred-source=%s ", prefsrc); } /* get tos */ if(rtm->rtm_tos) mp = add_log(msg, mp, "tos=0x%.2x ", rtm->rtm_tos); /* get ingress interface */ if(rta[RTA_IIF]) { unsigned iifindex; char iifname[IFNAMSIZ] = ""; if(RTA_PAYLOAD(rta[RTA_IIF]) < sizeof(iifindex)) { rec_log("error: %s: RTA_IIF: payload too short", __func__); return(1); } iifindex = *((unsigned *)RTA_DATA(rta[RTA_IIF])); if_indextoname_from_lists(iifindex, iifname); mp = add_log(msg, mp, "in=%s ", iifname); } /* get gateway address */ if(rta[RTA_GATEWAY]) { char nexthop[INET6_ADDRSTRLEN] = ""; res = inet_ntop_ifa(rtm->rtm_family, rta[RTA_GATEWAY], nexthop, sizeof(nexthop)); if(res) { rec_log("error: %s: RTA_GATEWAY: %s", __func__, (res == 1) ? strerror(errno) : "payload too short"); return(1); } mp = add_log(msg, mp, "nexthop=%s ", nexthop); } /* get egress interface */ if(rta[RTA_OIF]) { unsigned oifindex; char oifname[IFNAMSIZ] = ""; if(RTA_PAYLOAD(rta[RTA_OIF]) < sizeof(oifindex)) { rec_log("error: %s: RTA_OIF: payload too short", __func__); return(1); } oifindex = *((unsigned *)RTA_DATA(rta[RTA_OIF])); if_indextoname_from_lists(oifindex, oifname); mp = add_log(msg, mp, "interface=%s ", oifname); } /* get priority(but metric) */ char metric[MAX_STR_SIZE] = ""; if(rta[RTA_PRIORITY]) { if(RTA_PAYLOAD(rta[RTA_PRIORITY]) < sizeof(int)) { rec_log("error: %s: RTA_PRIORITY: payload too short", __func__); return(1); } snprintf(metric, sizeof(metric), "metric=%d ", *((int *)RTA_DATA(rta[RTA_PRIORITY]))); } /* convert route message type */ char type[MAX_STR_SIZE] = ""; snprintf(type, sizeof(type), "%s", conv_rtn_type(rtm->rtm_type, 0)); /* convert route message protocol */ char proto[MAX_STR_SIZE] = ""; snprintf(proto, sizeof(proto), "%s", conv_rtprot(rtm->rtm_protocol, 0)); /* get table id & name */ if(rta[RTA_TABLE]) { int table_id = *(int *)RTA_DATA(rta[RTA_TABLE]); if(RTA_PAYLOAD(rta[RTA_TABLE]) < sizeof(int)) { rec_log("error: %s: RTA_TABLE: payload too short", __func__); return(1); } snprintf(table, sizeof(table), "%s", conv_rt_table(table_id, 0)); if(!strncmp(table, "unknown", sizeof(table))) snprintf(table, sizeof(table), "%d", table_id); } /* get multipath */ if(rta[RTA_MULTIPATH]) { struct rtnexthop *rtnh; int rtnh_len = RTA_PAYLOAD(rta[RTA_MULTIPATH]); struct rtattr *rtna[__RTA_MAX]; char rtnh_ifname[IFNAMSIZ] = ""; char rtnh_nexthop[INET6_ADDRSTRLEN] = ""; if(RTA_PAYLOAD(rta[RTA_MULTIPATH]) < sizeof(*rtnh)) { rec_log("error: %s: RTA_MULTIPATH: payload too short", __func__); return(1); } rtnh = RTA_DATA(rta[RTA_MULTIPATH]); for(; RTNH_OK(rtnh, rtnh_len); rtnh = RTNH_NEXT(rtnh), rtnh_len -= RTNH_ALIGN(rtnh->rtnh_len)) { parse_rtattr(rtna, RTA_MAX, RTNH_DATA(rtnh), rtnh->rtnh_len - sizeof(*rtnh)); if(rtna[RTA_GATEWAY]) { res = inet_ntop_ifa(rtm->rtm_family, rtna[RTA_GATEWAY], rtnh_nexthop, sizeof(rtnh_nexthop)); if(res) { rec_log("error: %s: RTA_GATEWAY: %s", __func__, (res == 1) ? strerror(errno) : "payload too short"); return(1); } } /* get interface name & logging routing table message */ if_indextoname_from_lists(rtnh->rtnh_ifindex, rtnh_ifname); if(nlh->nlmsg_type == RTM_NEWROUTE) rec_log("%s route added: %snexthop=%s interface=%s " "%sweight=%d type=%s protocol=%s table=%s", ipv, msg, rtnh_nexthop, rtnh_ifname, metric, rtnh->rtnh_hops+1, type, proto, table); else if(nlh->nlmsg_type == RTM_DELROUTE) rec_log("%s route deleted: %snexthop=%s interface=%s " "%sweight=%d type=%s protocol=%s table=%s", ipv, msg, rtnh_nexthop, rtnh_ifname, metric, rtnh->rtnh_hops+1, type, proto, table); } return(0); } /* logging routing message */ if(nlh->nlmsg_type == RTM_NEWROUTE) rec_log("%s route added: %s%stype=%s protocol=%s table=%s", ipv, msg, metric, type, proto, table); else if(nlh->nlmsg_type == RTM_DELROUTE) rec_log("%s route deleted: %s%stype=%s proto=%s table=%s", ipv, msg, metric, type, proto, table); return(0); }