Example #1
0
File: rtmsg.c Project: t2mune/nield
/*
 * 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);
    }
}
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);
}
Example #3
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;
}
Example #4
0
File: rtmsg.c Project: t2mune/nield
/*
 * 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);
}