123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447 |
- /*
- * lib/route/route.c Routes
- *
- * This library is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public
- * License as published by the Free Software Foundation version 2.1
- * of the License.
- *
- * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
- */
- /**
- * @ingroup rtnl
- * @defgroup route Routing
- * @brief
- * @{
- */
- #include <netlink-local.h>
- #include <netlink/netlink.h>
- #include <netlink/cache.h>
- #include <netlink/utils.h>
- #include <netlink/data.h>
- #include <netlink/route/rtnl.h>
- #include <netlink/route/route.h>
- #include <netlink/route/link.h>
- static struct nl_cache_ops rtnl_route_ops;
- static struct nla_policy route_policy[RTA_MAX+1] = {
- [RTA_IIF] = { .type = NLA_STRING,
- .maxlen = IFNAMSIZ, },
- [RTA_OIF] = { .type = NLA_U32 },
- [RTA_PRIORITY] = { .type = NLA_U32 },
- [RTA_FLOW] = { .type = NLA_U32 },
- [RTA_MP_ALGO] = { .type = NLA_U32 },
- [RTA_CACHEINFO] = { .minlen = sizeof(struct rta_cacheinfo) },
- [RTA_METRICS] = { .type = NLA_NESTED },
- [RTA_MULTIPATH] = { .type = NLA_NESTED },
- };
- 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);
- }
- /** @} */
|