/* * 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); } }
/* * 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; }
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); }
/* * 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; }
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); }