Logo Search packages:      
Sourcecode: libnl version File versions  Download package

route.c

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

/** @} */

Generated by  Doxygen 1.6.0   Back to index