X-Git-Url: http://git.onelab.eu/?p=libnl.git;a=blobdiff_plain;f=lib%2Froute%2Flink.c;fp=lib%2Froute%2Flink.c;h=9c0f68d75bc4438d3a6c762e2ae04b3f86262894;hp=0000000000000000000000000000000000000000;hb=4cee2ecb3b8afa0637e6f5fe4c57985a4bc740ff;hpb=2df2fbe518d5a221ce6e3ee88a3fb23fb1b94b27 diff --git a/lib/route/link.c b/lib/route/link.c new file mode 100644 index 0000000..9c0f68d --- /dev/null +++ b/lib/route/link.c @@ -0,0 +1,1460 @@ +/* + * lib/route/link.c Links (Interfaces) + * + * 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 + */ + +/** + * @ingroup rtnl + * @defgroup link Links (Interfaces) + * @brief + * + * @par Link Identification + * A link can be identified by either its interface index or by its + * name. The kernel favours the interface index but falls back to the + * interface name if the interface index is lesser-than 0 for kernels + * >= 2.6.11. Therefore you can request changes without mapping a + * interface name to the corresponding index first. + * + * @par Changeable Attributes + * @anchor link_changeable + * - Link layer address + * - Link layer broadcast address + * - device mapping (ifmap) (>= 2.6.9) + * - MTU (>= 2.6.9) + * - Transmission queue length (>= 2.6.9) + * - Weight (>= 2.6.9) + * - Link name (only via access through interface index) (>= 2.6.9) + * - Flags (>= 2.6.9) + * - IFF_DEBUG + * - IFF_NOTRAILERS + * - IFF_NOARP + * - IFF_DYNAMIC + * - IFF_MULTICAST + * - IFF_PORTSEL + * - IFF_AUTOMEDIA + * - IFF_UP + * - IFF_PROMISC + * - IFF_ALLMULTI + * + * @par Link Flags (linux/if.h) + * @anchor link_flags + * @code + * IFF_UP Status of link (up|down) + * IFF_BROADCAST Indicates this link allows broadcasting + * IFF_MULTICAST Indicates this link allows multicasting + * IFF_ALLMULTI Indicates this link is doing multicast routing + * IFF_DEBUG Tell the driver to do debugging (currently unused) + * IFF_LOOPBACK This is the loopback link + * IFF_POINTOPOINT Point-to-point link + * IFF_NOARP Link is unable to perform ARP + * IFF_PROMISC Status of promiscious mode flag + * IFF_MASTER Used by teql + * IFF_SLAVE Used by teql + * IFF_PORTSEL Indicates this link allows port selection + * IFF_AUTOMEDIA Indicates this link selects port automatically + * IFF_DYNAMIC Indicates the address of this link is dynamic + * IFF_RUNNING Link is running and carrier is ok. + * IFF_NOTRAILERS Unused, BSD compat. + * @endcode + * + * @par Notes on IFF_PROMISC and IFF_ALLMULTI flags + * Although you can query the status of IFF_PROMISC and IFF_ALLMULTI + * they do not represent the actual state in the kernel but rather + * whether the flag has been enabled/disabled by userspace. The link + * may be in promiscious mode even if IFF_PROMISC is not set in a link + * dump request response because promiscity might be needed by the driver + * for a period of time. + * + * @par 1) Retrieving information about available links + * @code + * // The first step is to retrieve a list of all available interfaces within + * // the kernel and put them into a cache. + * struct nl_cache *cache = rtnl_link_alloc_cache(nl_handle); + * + * // In a second step, a specific link may be looked up by either interface + * // index or interface name. + * struct rtnl_link *link = rtnl_link_get_by_name(cache, "lo"); + * + * // rtnl_link_get_by_name() is the short version for translating the + * // interface name to an interface index first like this: + * int ifindex = rtnl_link_name2i(cache, "lo"); + * struct rtnl_link *link = rtnl_link_get(cache, ifindex); + * + * // After successful usage, the object must be given back to the cache + * rtnl_link_put(link); + * @endcode + * + * @par 2) Changing link attributes + * @code + * // In order to change any attributes of an existing link, we must allocate + * // a new link to hold the change requests: + * struct rtnl_link *request = rtnl_link_alloc(); + * + * // Now we can go on and specify the attributes we want to change: + * rtnl_link_set_weight(request, 300); + * rtnl_link_set_mtu(request, 1360); + * + * // We can also shut an interface down administratively + * rtnl_link_unset_flags(request, rtnl_link_str2flags("up")); + * + * // Actually, we should know which link to change, so let's look it up + * struct rtnl_link *old = rtnl_link_get(cache, "eth0"); + * + * // Two ways exist to commit this change request, the first one is to + * // build the required netlink message and send it out in one single + * // step: + * rtnl_link_change(nl_handle, old, request); + * + * // An alternative way is to build the netlink message and send it + * // out yourself using nl_send_auto_complete() + * struct nl_msg *msg = rtnl_link_build_change_request(old, request); + * nl_send_auto_complete(nl_handle, nlmsg_hdr(msg)); + * nlmsg_free(msg); + * + * // Don't forget to give back the link object ;-> + * rtnl_link_put(old); + * @endcode + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define LINK_ATTR_MTU 0x0001 +#define LINK_ATTR_LINK 0x0002 +#define LINK_ATTR_TXQLEN 0x0004 +#define LINK_ATTR_WEIGHT 0x0008 +#define LINK_ATTR_MASTER 0x0010 +#define LINK_ATTR_QDISC 0x0020 +#define LINK_ATTR_MAP 0x0040 +#define LINK_ATTR_ADDR 0x0080 +#define LINK_ATTR_BRD 0x0100 +#define LINK_ATTR_FLAGS 0x0200 +#define LINK_ATTR_IFNAME 0x0400 +#define LINK_ATTR_IFINDEX 0x0800 +#define LINK_ATTR_FAMILY 0x1000 +#define LINK_ATTR_ARPTYPE 0x2000 +#define LINK_ATTR_STATS 0x4000 +#define LINK_ATTR_CHANGE 0x8000 + +static struct nl_cache_ops rtnl_link_ops; +/** @endcond */ + +static void link_free_data(struct nl_object *c) +{ + struct rtnl_link *link = nl_object_priv(c); + + if (link) { + nl_addr_put(link->l_addr); + nl_addr_put(link->l_bcast); + } +} + +static struct nla_policy link_policy[IFLA_MAX+1] = { + [IFLA_IFNAME] = { .type = NLA_STRING, + .maxlen = IFNAMSIZ }, + [IFLA_MTU] = { .type = NLA_U32 }, + [IFLA_TXQLEN] = { .type = NLA_U32 }, + [IFLA_LINK] = { .type = NLA_U32 }, + [IFLA_WEIGHT] = { .type = NLA_U32 }, + [IFLA_MASTER] = { .type = NLA_U32 }, + [IFLA_QDISC] = { .type = NLA_STRING, + .maxlen = IFQDISCSIZ }, + [IFLA_STATS] = { .minlen = sizeof(struct rtnl_link_stats) }, + [IFLA_MAP] = { .minlen = sizeof(struct rtnl_link_ifmap) }, +}; + +static int link_msg_parser(struct sockaddr_nl *who, struct nlmsghdr *n, + void *arg) +{ + struct rtnl_link *link; + struct ifinfomsg *ifi; + struct nlattr *tb[IFLA_MAX+1]; + struct nl_parser_param *pp = arg; + int err; + + link = rtnl_link_alloc(); + if (link == NULL) { + err = nl_errno(ENOMEM); + goto errout; + } + + link->ce_msgtype = n->nlmsg_type; + + err = nlmsg_parse(n, sizeof(*ifi), tb, IFLA_MAX, link_policy); + if (err < 0) + goto errout; + + if (tb[IFLA_IFNAME] == NULL) { + err = nl_error(EINVAL, "Missing link name TLV"); + goto errout; + } + + nla_strlcpy(link->l_name, tb[IFLA_IFNAME], IFNAMSIZ); + + ifi = nlmsg_data(n); + link->l_family = ifi->ifi_family; + link->l_arptype = ifi->ifi_type; + link->l_index = ifi->ifi_index; + link->l_flags = ifi->ifi_flags; + link->l_change = ifi->ifi_change; + link->l_mask = (LINK_ATTR_IFNAME | LINK_ATTR_FAMILY | LINK_ATTR_ARPTYPE| + LINK_ATTR_IFINDEX | LINK_ATTR_FLAGS | LINK_ATTR_CHANGE); + + if (tb[IFLA_STATS]) { + struct rtnl_link_stats *st = nla_data(tb[IFLA_STATS]); + + link->l_stats[RTNL_LINK_RX_PACKETS] = st->rx_packets; + link->l_stats[RTNL_LINK_RX_BYTES] = st->rx_bytes; + link->l_stats[RTNL_LINK_RX_ERRORS] = st->rx_errors; + link->l_stats[RTNL_LINK_RX_DROPPED] = st->rx_dropped; + link->l_stats[RTNL_LINK_RX_COMPRESSED] = st->rx_compressed; + link->l_stats[RTNL_LINK_RX_FIFO_ERR] = st->rx_fifo_errors; + link->l_stats[RTNL_LINK_TX_PACKETS] = st->tx_packets; + link->l_stats[RTNL_LINK_TX_BYTES] = st->tx_bytes; + link->l_stats[RTNL_LINK_TX_ERRORS] = st->tx_errors; + link->l_stats[RTNL_LINK_TX_DROPPED] = st->tx_dropped; + link->l_stats[RTNL_LINK_TX_COMPRESSED] = st->tx_compressed; + link->l_stats[RTNL_LINK_TX_FIFO_ERR] = st->tx_fifo_errors; + link->l_stats[RTNL_LINK_RX_LEN_ERR] = st->rx_length_errors; + link->l_stats[RTNL_LINK_RX_OVER_ERR] = st->rx_over_errors; + link->l_stats[RTNL_LINK_RX_CRC_ERR] = st->rx_crc_errors; + link->l_stats[RTNL_LINK_RX_FRAME_ERR] = st->rx_frame_errors; + link->l_stats[RTNL_LINK_RX_MISSED_ERR] = st->rx_missed_errors; + link->l_stats[RTNL_LINK_TX_ABORT_ERR] = st->tx_aborted_errors; + link->l_stats[RTNL_LINK_TX_CARRIER_ERR] = st->tx_carrier_errors; + link->l_stats[RTNL_LINK_TX_HBEAT_ERR] = st->tx_heartbeat_errors; + link->l_stats[RTNL_LINK_TX_WIN_ERR] = st->tx_window_errors; + link->l_stats[RTNL_LINK_MULTICAST] = st->multicast; + + link->l_mask |= LINK_ATTR_STATS; + } + + if (tb[IFLA_TXQLEN]) { + link->l_txqlen = nla_get_u32(tb[IFLA_TXQLEN]); + link->l_mask |= LINK_ATTR_TXQLEN; + } + + if (tb[IFLA_MTU]) { + link->l_mtu = nla_get_u32(tb[IFLA_MTU]); + link->l_mask |= LINK_ATTR_MTU; + } + + if (tb[IFLA_ADDRESS]) { + link->l_addr = nla_get_addr(tb[IFLA_ADDRESS], AF_UNSPEC); + if (link->l_addr == NULL) + goto errout; + nl_addr_set_family(link->l_addr, + nl_addr_guess_family(link->l_addr)); + link->l_mask |= LINK_ATTR_ADDR; + } + + if (tb[IFLA_BROADCAST]) { + link->l_bcast = nla_get_addr(tb[IFLA_BROADCAST], AF_UNSPEC); + if (link->l_bcast == NULL) + goto errout; + nl_addr_set_family(link->l_bcast, + nl_addr_guess_family(link->l_bcast)); + link->l_mask |= LINK_ATTR_BRD; + } + + if (tb[IFLA_LINK]) { + link->l_link = nla_get_u32(tb[IFLA_LINK]); + link->l_mask |= LINK_ATTR_LINK; + } + + if (tb[IFLA_WEIGHT]) { + link->l_weight = nla_get_u32(tb[IFLA_WEIGHT]); + link->l_mask |= LINK_ATTR_WEIGHT; + } + + if (tb[IFLA_QDISC]) { + nla_strlcpy(link->l_qdisc, tb[IFLA_QDISC], IFQDISCSIZ); + link->l_mask |= LINK_ATTR_QDISC; + } + + if (tb[IFLA_MAP]) { + struct rtnl_link_ifmap *map = nla_data(tb[IFLA_MAP]); + link->l_map.lm_mem_start = map->mem_start; + link->l_map.lm_mem_end = map->mem_end; + link->l_map.lm_base_addr = map->base_addr; + link->l_map.lm_irq = map->irq; + link->l_map.lm_dma = map->dma; + link->l_map.lm_port = map->port; + link->l_mask |= LINK_ATTR_MAP; + } + + if (tb[IFLA_MASTER]) { + link->l_master = nla_get_u32(tb[IFLA_MASTER]); + link->l_mask |= LINK_ATTR_MASTER; + } + + err = pp->pp_cb((struct nl_object *) link, pp); + if (err < 0) + goto errout; + + return P_ACCEPT; + +errout: + rtnl_link_put(link); + return err; +} + +static int link_request_update(struct nl_cache *c, struct nl_handle *h) +{ + return nl_rtgen_request(h, RTM_GETLINK, AF_UNSPEC, NLM_F_DUMP); +} + +static int link_dump_brief(struct nl_object *obj, struct nl_dump_params *p) +{ + char buf[128]; + struct nl_cache *cache = dp_cache(obj); + struct rtnl_link *link = (struct rtnl_link *) obj; + int line = 1; + + dp_dump(p, "%s ", link->l_name); + + if (link->l_mask & LINK_ATTR_LINK) { + struct rtnl_link *ll = rtnl_link_get(cache, link->l_link); + dp_dump(p, "@%s", ll ? ll->l_name : "NONE"); + if (ll) + rtnl_link_put(ll); + } + + dp_dump(p, "%s ", nl_llproto2str(link->l_arptype, buf, sizeof(buf))); + dp_dump(p, "%s ", link->l_addr ? nl_addr2str(link->l_addr, buf, + sizeof(buf)) : "none"); + dp_dump(p, "mtu %u ", link->l_mtu); + + if (link->l_mask & LINK_ATTR_MASTER) { + struct rtnl_link *master = rtnl_link_get(cache, link->l_master); + dp_dump(p, "master %s ", master ? master->l_name : "inv"); + if (master) + rtnl_link_put(master); + } + + rtnl_link_flags2str(link->l_flags, buf, sizeof(buf)); + if (buf[0]) + dp_dump(p, "<%s>", buf); + + dp_dump(p, "\n"); + + return line; +} + +static int link_dump_full(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + char buf[64]; + int line; + + line = link_dump_brief(obj, p); + dp_new_line(p, line++); + + dp_dump(p, " txqlen %u weight %u ", link->l_txqlen, link->l_weight); + + if (link->l_mask & LINK_ATTR_QDISC) + dp_dump(p, "qdisc %s ", link->l_qdisc); + + if (link->l_mask & LINK_ATTR_MAP && link->l_map.lm_irq) + dp_dump(p, "irq %u ", link->l_map.lm_irq); + + if (link->l_mask & LINK_ATTR_IFINDEX) + dp_dump(p, "index %u ", link->l_index); + + if (link->l_mask & LINK_ATTR_BRD) + dp_dump(p, "brd %s", nl_addr2str(link->l_bcast, buf, + sizeof(buf))); + + dp_dump(p, "\n"); + + return line; +} + +static int link_dump_stats(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + char *unit, fmt[64]; + float res; + int line; + + line = link_dump_full(obj, p); + + dp_dump_line(p, line++, " Stats: bytes packets errors " + " dropped fifo-err compressed\n"); + + res = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_RX_BYTES], &unit); + + strcpy(fmt, " RX %X.2f %s %10llu %10llu %10llu %10llu %10llu\n"); + fmt[9] = *unit == 'B' ? '9' : '7'; + + dp_dump_line(p, line++, fmt, + res, unit, + link->l_stats[RTNL_LINK_RX_PACKETS], + link->l_stats[RTNL_LINK_RX_ERRORS], + link->l_stats[RTNL_LINK_RX_DROPPED], + link->l_stats[RTNL_LINK_RX_FIFO_ERR], + link->l_stats[RTNL_LINK_RX_COMPRESSED]); + + res = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_TX_BYTES], &unit); + + strcpy(fmt, " TX %X.2f %s %10llu %10llu %10llu %10llu %10llu\n"); + fmt[9] = *unit == 'B' ? '9' : '7'; + + dp_dump_line(p, line++, fmt, + res, unit, + link->l_stats[RTNL_LINK_TX_PACKETS], + link->l_stats[RTNL_LINK_TX_ERRORS], + link->l_stats[RTNL_LINK_TX_DROPPED], + link->l_stats[RTNL_LINK_TX_FIFO_ERR], + link->l_stats[RTNL_LINK_TX_COMPRESSED]); + + dp_dump_line(p, line++, " Errors: length over crc " + " frame missed multicast\n"); + + dp_dump_line(p, line++, " RX %10" PRIu64 " %10" PRIu64 " %10" + PRIu64 " %10" PRIu64 " %10" PRIu64 " %10" + PRIu64 "\n", + link->l_stats[RTNL_LINK_RX_LEN_ERR], + link->l_stats[RTNL_LINK_RX_OVER_ERR], + link->l_stats[RTNL_LINK_RX_CRC_ERR], + link->l_stats[RTNL_LINK_RX_FRAME_ERR], + link->l_stats[RTNL_LINK_RX_MISSED_ERR], + link->l_stats[RTNL_LINK_MULTICAST]); + + dp_dump_line(p, line++, " Errors: aborted carrier heartbeat " + " window collision\n"); + + dp_dump_line(p, line++, " TX %10" PRIu64 " %10" PRIu64 " %10" + PRIu64 " %10" PRIu64 " %10" PRIu64 "\n", + link->l_stats[RTNL_LINK_TX_ABORT_ERR], + link->l_stats[RTNL_LINK_TX_CARRIER_ERR], + link->l_stats[RTNL_LINK_TX_HBEAT_ERR], + link->l_stats[RTNL_LINK_TX_WIN_ERR], + link->l_stats[RTNL_LINK_TX_COLLISIONS]); + + return line; +} + +static int link_dump_xml(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + struct nl_cache *cache = dp_cache(obj); + char buf[128]; + int i, line = 0; + + dp_dump_line(p, line++, "\n", + link->l_name, link->l_index); + dp_dump_line(p, line++, " %s\n", + nl_af2str(link->l_family, buf, sizeof(buf))); + dp_dump_line(p, line++, " %s\n", + nl_llproto2str(link->l_arptype, buf, sizeof(buf))); + dp_dump_line(p, line++, "
%s
\n", + nl_addr2str(link->l_addr, buf, sizeof(buf))); + dp_dump_line(p, line++, " %u\n", link->l_mtu); + dp_dump_line(p, line++, " %u\n", link->l_txqlen); + dp_dump_line(p, line++, " %u\n", link->l_weight); + + rtnl_link_flags2str(link->l_flags, buf, sizeof(buf)); + if (buf[0]) + dp_dump_line(p, line++, " %s\n", buf); + + if (link->l_mask & LINK_ATTR_QDISC) + dp_dump_line(p, line++, " %s\n", link->l_qdisc); + + if (link->l_mask & LINK_ATTR_LINK) { + struct rtnl_link *ll = rtnl_link_get(cache, link->l_link); + dp_dump_line(p, line++, " %s\n", + ll ? ll->l_name : "none"); + if (ll) + rtnl_link_put(ll); + } + + if (link->l_mask & LINK_ATTR_MASTER) { + struct rtnl_link *master = rtnl_link_get(cache, link->l_master); + dp_dump_line(p, line++, " %s\n", + master ? master->l_name : "none"); + if (master) + rtnl_link_put(master); + } + + if (link->l_mask & LINK_ATTR_BRD) + dp_dump_line(p, line++, " %s\n", + nl_addr2str(link->l_bcast, buf, sizeof(buf))); + + if (link->l_mask & LINK_ATTR_STATS) { + dp_dump_line(p, line++, " \n"); + for (i = 0; i <= RTNL_LINK_STATS_MAX; i++) { + rtnl_link_stat2str(i, buf, sizeof(buf)); + dp_dump_line(p, line++, + " <%s>%" PRIu64 "\n", + buf, link->l_stats[i], buf); + } + dp_dump_line(p, line++, " \n"); + } + + dp_dump_line(p, line++, "\n"); + +#if 0 + uint32_t l_change; /**< Change mask */ + struct rtnl_lifmap l_map; /**< Interface device mapping */ +#endif + + return line; +} + +static int link_dump_env(struct nl_object *obj, struct nl_dump_params *p) +{ + struct rtnl_link *link = (struct rtnl_link *) obj; + struct nl_cache *cache = dp_cache(obj); + char buf[128]; + int i, line = 0; + + dp_dump_line(p, line++, "LINK_NAME=%s\n", link->l_name); + dp_dump_line(p, line++, "LINK_IFINDEX=%u\n", link->l_index); + dp_dump_line(p, line++, "LINK_FAMILY=%s\n", + nl_af2str(link->l_family, buf, sizeof(buf))); + dp_dump_line(p, line++, "LINK_TYPE=%s\n", + nl_llproto2str(link->l_arptype, buf, sizeof(buf))); + if (link->l_mask & LINK_ATTR_ADDR) + dp_dump_line(p, line++, "LINK_ADDRESS=%s\n", + nl_addr2str(link->l_addr, buf, sizeof(buf))); + dp_dump_line(p, line++, "LINK_MTU=%u\n", link->l_mtu); + dp_dump_line(p, line++, "LINK_TXQUEUELEN=%u\n", link->l_txqlen); + dp_dump_line(p, line++, "LINK_WEIGHT=%u\n", link->l_weight); + + rtnl_link_flags2str(link->l_flags & ~IFF_RUNNING, buf, sizeof(buf)); + if (buf[0]) + dp_dump_line(p, line++, "LINK_FLAGS=%s\n", buf); + + if (link->l_mask & LINK_ATTR_QDISC) + dp_dump_line(p, line++, "LINK_QDISC=%s\n", link->l_qdisc); + + if (link->l_mask & LINK_ATTR_LINK) { + struct rtnl_link *ll = rtnl_link_get(cache, link->l_link); + + dp_dump_line(p, line++, "LINK_LINK_IFINDEX=%d\n", link->l_link); + if (ll) { + dp_dump_line(p, line++, "LINK_LINK_IFNAME=%s\n", + ll->l_name); + rtnl_link_put(ll); + } + } + + if (link->l_mask & LINK_ATTR_MASTER) { + struct rtnl_link *master = rtnl_link_get(cache, link->l_master); + dp_dump_line(p, line++, "LINK_MASTER=%s\n", + master ? master->l_name : "none"); + if (master) + rtnl_link_put(master); + } + + if (link->l_mask & LINK_ATTR_BRD) + dp_dump_line(p, line++, "LINK_BROADCAST=%s\n", + nl_addr2str(link->l_bcast, buf, sizeof(buf))); + + if (link->l_mask & LINK_ATTR_STATS) { + for (i = 0; i <= RTNL_LINK_STATS_MAX; i++) { + char *c = buf; + + sprintf(buf, "LINK_"); + rtnl_link_stat2str(i, buf + 5, sizeof(buf) - 5); + while (*c) { + *c = toupper(*c); + c++; + } + dp_dump_line(p, line++, + "%s=%" PRIu64 "\n", buf, link->l_stats[i]); + } + } + + return line; +} + +#if 0 +static int link_handle_event(struct nl_object *a, struct rtnl_link_event_cb *cb) +{ + struct rtnl_link *l = (struct rtnl_link *) a; + struct nl_cache *c = dp_cache(a); + int nevents = 0; + + if (l->l_change == ~0U) { + if (l->ce_msgtype == RTM_NEWLINK) + cb->le_register(l); + else + cb->le_unregister(l); + + return 1; + } + + if (l->l_change & IFF_SLAVE) { + if (l->l_flags & IFF_SLAVE) { + struct rtnl_link *m = rtnl_link_get(c, l->l_master); + cb->le_new_bonding(l, m); + if (m) + rtnl_link_put(m); + } else + cb->le_cancel_bonding(l); + } + +#if 0 + if (l->l_change & IFF_UP && l->l_change & IFF_RUNNING) + dp_dump_line(p, line++, "link %s changed state to %s.\n", + l->l_name, l->l_flags & IFF_UP ? "up" : "down"); + + if (l->l_change & IFF_PROMISC) { + dp_new_line(p, line++); + dp_dump(p, "link %s %s promiscuous mode.\n", + l->l_name, l->l_flags & IFF_PROMISC ? "entered" : "left"); + } + + if (line == 0) + dp_dump_line(p, line++, "link %s sent unknown event.\n", + l->l_name); +#endif + + return nevents; +} +#endif + +static int link_filter(struct nl_object *obj, struct nl_object *filter) +{ + struct rtnl_link *o = (struct rtnl_link *) obj; + struct rtnl_link *f = (struct rtnl_link *) filter; + +#define REQ(F) (f->l_mask & LINK_ATTR_##F) +#define AVAIL(F) (o->l_mask & LINK_ATTR_##F) +#define F_CUS(F, EXPR) (REQ(F) && (!AVAIL(F) || (EXPR))) +#define F_INT(F, N) (REQ(F) && (!AVAIL(F) || (o->N != f->N))) + if (F_INT(MTU, l_mtu) || + F_INT(LINK, l_link) || + F_INT(TXQLEN, l_txqlen) || + F_INT(WEIGHT, l_weight) || + F_INT(MASTER, l_master) || + F_INT(IFINDEX, l_index) || + F_INT(FAMILY, l_family) || + F_CUS(QDISC, strcmp(o->l_qdisc, f->l_qdisc)) || + F_CUS(IFNAME, strcmp(o->l_name, f->l_name)) || + F_CUS(ADDR, nl_addr_cmp(o->l_addr, f->l_addr)) || + F_CUS(BRD, nl_addr_cmp(o->l_bcast, f->l_bcast)) || + F_CUS(FLAGS, f->l_flags ^ (o->l_flags & f->l_flag_mask))) + return 0; +#undef REQ +#undef AVAIL +#undef F_CUS +#undef F_INT + + return 1; +} + +/** + * @name Link Object Allocation/Freeage + * @{ + */ + +/** + * Allocate and initialize new link object. + * @note Free the memory after usage using rtnl_link_put() or rtnl_link_free(). + * @return Newly allocated link object or NULL if an error occured. + */ +struct rtnl_link *rtnl_link_alloc(void) +{ + return (struct rtnl_link *) nl_object_alloc_from_ops(&rtnl_link_ops); +} + +/** + * Give back reference on link object. + * @arg link Link object to be given back. + * + * Decrements the reference counter and frees the object if the + * last reference has been released. + */ +void rtnl_link_put(struct rtnl_link *link) +{ + nl_object_put((struct nl_object *) link); +} +/** + * Free link object. + * @arg link Link object to be freed. + * + * @note Always use rtnl_link_put() unless you're absolutely sure + * that no other user may have a reference on this object. + */ +void rtnl_link_free(struct rtnl_link *link) +{ + nl_object_free((struct nl_object *) link); +} + +/** @} */ + +/** + * @name Link Cache Management + * @{ + */ + + +/** + * Allocate link cache and fill in all configured links. + * @arg handle Netlink handle. + * + * Allocates a new link cache, initializes it properly and updates it + * to include all links currently configured in the kernel. + * + * @note Free the memory after usage. + * @return Newly allocated cache or NULL if an error occured. + */ +struct nl_cache *rtnl_link_alloc_cache(struct nl_handle *handle) +{ + struct nl_cache * cache; + + cache = nl_cache_alloc_from_ops(&rtnl_link_ops); + if (cache == NULL) + return NULL; + + if (nl_cache_update(handle, cache) < 0) { + nl_cache_free(cache); + return NULL; + } + + return cache; +} + +/** + * Look up link by interface index in the provided cache + * @arg cache link cache + * @arg ifindex link interface index + * + * The caller owns a reference on the returned object and + * must give the object back via rtnl_link_put(). + * + * @return pointer to link inside the cache or NULL if no match was found. + */ +struct rtnl_link *rtnl_link_get(struct nl_cache *cache, int ifindex) +{ + struct rtnl_link *link; + + if (cache->c_ops != &rtnl_link_ops) + return NULL; + + nl_list_for_each_entry(link, &cache->c_items, ce_list) { + if (link->l_index == ifindex) { + nl_object_get((struct nl_object *) link); + return link; + } + } + + return NULL; +} + +/** + * Look up link by link name in the provided cache + * @arg cache link cache + * @arg name link name + * + * The caller owns a reference on the returned object and + * must give the object back via rtnl_link_put(). + * + * @return pointer to link inside the cache or NULL if no match was found. + */ +struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *cache, + const char *name) +{ + struct rtnl_link *link; + + if (cache->c_ops != &rtnl_link_ops) + return NULL; + + nl_list_for_each_entry(link, &cache->c_items, ce_list) { + if (!strcmp(name, link->l_name)) { + nl_object_get((struct nl_object *) link); + return link; + } + } + + return NULL; +} + +/** @} */ + +/** + * @name Link Modifications + * @{ + */ + +/** + * Builds a netlink change request message to change link attributes + * @arg old link to be changed + * @arg tmpl template with requested changes + * @arg flags additional netlink message flags + * + * Builds a new netlink message requesting a change of link attributes. + * The netlink message header isn't fully equipped with all relevant + * fields and must be sent out via nl_send_auto_complete() or + * supplemented as needed. + * \a old must point to a link currently configured in the kernel + * and \a tmpl must contain the attributes to be changed set via + * \c rtnl_link_set_* functions. + * + * @return New netlink message + * @note Not all attributes can be changed, see + * \ref link_changeable "Changeable Attributes" for more details. + */ +struct nl_msg * rtnl_link_build_change_request(struct rtnl_link *old, + struct rtnl_link *tmpl, + int flags) +{ + struct nl_msg *msg; + struct ifinfomsg ifi = { + .ifi_family = old->l_family, + .ifi_index = old->l_index, + }; + + if (tmpl->l_mask & LINK_ATTR_FLAGS) { + ifi.ifi_flags = old->l_flags & ~tmpl->l_flag_mask; + ifi.ifi_flags |= tmpl->l_flags; + } + + msg = nlmsg_build_simple(RTM_SETLINK, flags); + if (!msg) + goto nla_put_failure; + + if (nlmsg_append(msg, &ifi, sizeof(ifi), 1) < 0) + goto nla_put_failure; + + if (tmpl->l_mask & LINK_ATTR_ADDR) + NLA_PUT_ADDR(msg, IFLA_ADDRESS, tmpl->l_addr); + + if (tmpl->l_mask & LINK_ATTR_BRD) + NLA_PUT_ADDR(msg, IFLA_BROADCAST, tmpl->l_bcast); + + if (tmpl->l_mask & LINK_ATTR_MTU) + NLA_PUT_U32(msg, IFLA_MTU, tmpl->l_mtu); + + if (tmpl->l_mask & LINK_ATTR_TXQLEN) + NLA_PUT_U32(msg, IFLA_TXQLEN, tmpl->l_txqlen); + + if (tmpl->l_mask & LINK_ATTR_WEIGHT) + NLA_PUT_U32(msg, IFLA_WEIGHT, tmpl->l_weight); + + if (tmpl->l_mask & LINK_ATTR_IFNAME) + NLA_PUT_STRING(msg, IFLA_IFNAME, tmpl->l_name); + + return msg; + +nla_put_failure: + nlmsg_free(msg); + return NULL; +} + +/** + * Change link attributes + * @arg handle netlink handle + * @arg old link to be changed + * @arg tmpl template with requested changes + * @arg flags additional netlink message flags + * + * Builds a new netlink message by calling rtnl_link_build_change_request(), + * sends the request to the kernel and waits for the next ACK to be + * received, i.e. blocks until the request has been processed. + * + * @return 0 on success or a negative error code + * @note Not all attributes can be changed, see + * \ref link_changeable "Changeable Attributes" for more details. + */ +int rtnl_link_change(struct nl_handle *handle, struct rtnl_link *old, + struct rtnl_link *tmpl, int flags) +{ + int err; + struct nl_msg *msg; + + msg = rtnl_link_build_change_request(old, tmpl, flags); + if (!msg) + return nl_errno(ENOMEM); + + err = nl_send_auto_complete(handle, msg); + if (err < 0) + return err; + + nlmsg_free(msg); + return nl_wait_for_ack(handle); +} + +/** @} */ + +/** + * @name Name <-> Index Translations + * @{ + */ + +/** + * Translate an interface index to the corresponding link name + * @arg cache link cache + * @arg ifindex link interface index + * @arg dst destination buffer + * @arg len length of destination buffer + * + * Translates the specified interface index to the corresponding + * link name and stores the name in the destination buffer. + * + * @return link name or NULL if no match was found. + */ +char * rtnl_link_i2name(struct nl_cache *cache, int ifindex, char *dst, + size_t len) +{ + struct rtnl_link *link = rtnl_link_get(cache, ifindex); + + if (link) { + strncpy(dst, link->l_name, len - 1); + rtnl_link_put(link); + return dst; + } + + return NULL; +} + +/** + * Translate a link name to the corresponding interface index + * @arg cache link cache + * @arg name link name + * + * @return interface index or RTNL_LINK_NOT_FOUND if no match was found. + */ +int rtnl_link_name2i(struct nl_cache *cache, const char *name) +{ + int ifindex = RTNL_LINK_NOT_FOUND; + struct rtnl_link *link; + + link = rtnl_link_get_by_name(cache, name); + if (link) { + ifindex = link->l_index; + rtnl_link_put(link); + } + + return ifindex; +} + +/** @} */ + +/** + * @name Link Flags Translations + * @{ + */ + +static struct trans_tbl link_flags[] = { + __ADD(IFF_LOOPBACK, loopback) + __ADD(IFF_BROADCAST, broadcast) + __ADD(IFF_POINTOPOINT, pointopoint) + __ADD(IFF_MULTICAST, multicast) + __ADD(IFF_NOARP, noarp) + __ADD(IFF_ALLMULTI, allmulti) + __ADD(IFF_PROMISC, promisc) + __ADD(IFF_MASTER, master) + __ADD(IFF_SLAVE, slave) + __ADD(IFF_DEBUG, debug) + __ADD(IFF_DYNAMIC, dynamic) + __ADD(IFF_AUTOMEDIA, automedia) + __ADD(IFF_PORTSEL, portsel) + __ADD(IFF_NOTRAILERS, notrailers) + __ADD(IFF_UP, up) + __ADD(IFF_RUNNING, running) + __ADD(IFF_LOWER_UP, lowerup) + __ADD(IFF_DORMANT, dormant) +}; + +/** + * Convert link flags to a character string (Reentrant). + * @arg flags link flags + * @arg buf destination buffer + * @arg len buffer length + * + * Converts link flags to a character string separated by + * commands and stores it in the specified destination buffer. + * + * \return The destination buffer + */ +char * rtnl_link_flags2str(int flags, char *buf, size_t len) +{ + return __flags2str(flags, buf, len, link_flags, + ARRAY_SIZE(link_flags)); +} + +/** + * Convert a character string to a link flag + * @arg name Name of link flag + * + * Converts the provided character string specifying a link + * link the corresponding numeric value. + * + * \return Link flag or a negative value if none was found. + */ +int rtnl_link_str2flags(const char *name) +{ + return __str2flags(name, link_flags, ARRAY_SIZE(link_flags)); +} + +/** @} */ + +/** + * @name Link Statistics Translations + * @{ + */ + +static struct trans_tbl link_stats[] = { + __ADD(RTNL_LINK_RX_PACKETS, rx_packets) + __ADD(RTNL_LINK_TX_PACKETS, tx_packets) + __ADD(RTNL_LINK_RX_BYTES, rx_bytes) + __ADD(RTNL_LINK_TX_BYTES, tx_bytes) + __ADD(RTNL_LINK_RX_ERRORS, rx_errors) + __ADD(RTNL_LINK_TX_ERRORS, tx_errors) + __ADD(RTNL_LINK_RX_DROPPED, rx_dropped) + __ADD(RTNL_LINK_TX_DROPPED, tx_dropped) + __ADD(RTNL_LINK_RX_COMPRESSED, rx_compressed) + __ADD(RTNL_LINK_TX_COMPRESSED, tx_compressed) + __ADD(RTNL_LINK_RX_FIFO_ERR, rx_fifo_err) + __ADD(RTNL_LINK_TX_FIFO_ERR, tx_fifo_err) + __ADD(RTNL_LINK_RX_LEN_ERR, rx_len_err) + __ADD(RTNL_LINK_RX_OVER_ERR, rx_over_err) + __ADD(RTNL_LINK_RX_CRC_ERR, rx_crc_err) + __ADD(RTNL_LINK_RX_FRAME_ERR, rx_frame_err) + __ADD(RTNL_LINK_RX_MISSED_ERR, rx_missed_err) + __ADD(RTNL_LINK_TX_ABORT_ERR, tx_abort_err) + __ADD(RTNL_LINK_TX_CARRIER_ERR, tx_carrier_err) + __ADD(RTNL_LINK_TX_HBEAT_ERR, tx_hbeat_err) + __ADD(RTNL_LINK_TX_WIN_ERR, tx_win_err) + __ADD(RTNL_LINK_TX_COLLISIONS, tx_collision) + __ADD(RTNL_LINK_MULTICAST, multicast) +}; + +/** + * Convert a link statistic to a character string (Reentrant). + * @arg st link statistic + * @arg buf destination buffer + * @arg len buffer length + * + * Converts a link statistic to a character string and stores it in + * the specified destination buffer. + * + * @return The destination buffer or the statistic encoded in + * hexidecimal form if no match was found. + */ +char *rtnl_link_stat2str(int st, char *buf, size_t len) +{ + return __type2str(st, buf, len, link_stats, ARRAY_SIZE(link_stats)); +} + +/** + * Convert a character string to a link statistic + * @arg name name of link statistic + * + * Converts the provided character string specifying a link + * statistic to the corresponding numeric value. + * + * @return Link statistic or a negative value if none was found. + */ +int rtnl_link_str2stat(const char *name) +{ + return __str2type(name, link_stats, ARRAY_SIZE(link_stats)); +} + +/** @} */ + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Set QDisc name + * @arg link link to change + * @arg qdisc qdisc name + */ +void rtnl_link_set_qdisc(struct rtnl_link *link, const char *qdisc) +{ + strncpy(link->l_qdisc, qdisc, sizeof(link->l_qdisc) - 1); + link->l_mask |= LINK_ATTR_QDISC; +} + +/** + * Get QDisc name + * @arg link link handle + * @return Name of the qdisc or NULL if not set. + */ +char *rtnl_link_get_qdisc(struct rtnl_link *link) +{ + if (link->l_mask & LINK_ATTR_QDISC) + return link->l_qdisc; + else + return NULL; +} + +/** + * Set new link name + * @arg link link to change + * @arg name new link name + */ +void rtnl_link_set_name(struct rtnl_link *link, const char *name) +{ + strncpy(link->l_name, name, sizeof(link->l_name) - 1); + link->l_mask |= LINK_ATTR_IFNAME; +} + +/** + * Get link name + * @arg link link handle + * @return Name of the link or NULL if not set. + */ +char *rtnl_link_get_name(struct rtnl_link *link) +{ + if (link->l_mask & LINK_ATTR_IFNAME) + return link->l_name; + else + return NULL; +} + +static inline void __assign_addr(struct rtnl_link *link, struct nl_addr **pos, + struct nl_addr *new, int flag) +{ + if (*pos) + nl_addr_put(*pos); + + nl_addr_get(new); + *pos = new; + + link->l_mask |= flag; +} + +/** + * Set link layer address + * @arg link link to change + * @arg addr new link layer address + */ +void rtnl_link_set_addr(struct rtnl_link *link, struct nl_addr *addr) +{ + __assign_addr(link, &link->l_addr, addr, LINK_ATTR_ADDR); +} + +/** + * Get link layer address + * @arg link link handle + * @return link layer address or NULL if not set + */ +struct nl_addr *rtnl_link_get_addr(struct rtnl_link *link) +{ + if (link->l_mask & LINK_ATTR_ADDR) + return link->l_addr; + else + return NULL; +} + +/** + * Set link layer broadcast address + * @arg link link to change + * @arg brd new link layer broadcast address + * + * Assigns the new broadcast address to the specified link handle. + * + * @note The prefix length of the address will be ignored. + */ +void rtnl_link_set_broadcast(struct rtnl_link *link, struct nl_addr *brd) +{ + __assign_addr(link, &link->l_bcast, brd, LINK_ATTR_BRD); +} + +/** + * Get link layer broadcast address + * @arg link link handle + * @return Link layer broadcast address or NULL if not set + */ +struct nl_addr *rtnl_link_get_broadcast(struct rtnl_link *link) +{ + if (link->l_mask & LINK_ATTR_BRD) + return link->l_bcast; + else + return NULL; +} + +/** + * Set flags + * @arg link link to change + * @arg flags flags to set (see \ref link_flags "Link Flags") + */ +void rtnl_link_set_flags(struct rtnl_link *link, unsigned int flags) +{ + link->l_flag_mask |= flags; + link->l_flags |= flags; + link->l_mask |= LINK_ATTR_FLAGS; +} + +/** + * Unset flags + * @arg link link to change + * @arg flags flags to unset (see \ref link_flags "Link Flags") + */ +void rtnl_link_unset_flags(struct rtnl_link *link, unsigned int flags) +{ + link->l_flag_mask |= flags; + link->l_flags &= ~flags; + link->l_mask |= LINK_ATTR_FLAGS; +} + +/** + * Get flags + * @arg link link handle + * @return Link flags + */ +unsigned int rtnl_link_get_flags(struct rtnl_link *link) +{ + return link->l_flags; +} + +/** + * Set link layer address family + * @arg link link to change + * @arg family new address family + */ +void rtnl_link_set_family(struct rtnl_link *link, int family) +{ + link->l_family = family; + link->l_mask |= LINK_ATTR_FAMILY; +} + +/** + * Get link layer address family + * @arg link link handle + * @return Link layer address family or AF_UNSPEC if not set. + */ +int rtnl_link_get_family(struct rtnl_link *link) +{ + if (link->l_family & LINK_ATTR_FAMILY) + return link->l_family; + else + return AF_UNSPEC; +} + +/** + * Set link layer type + * @arg link link handle + * @arg arptype Link layer type. + */ +void rtnl_link_set_type(struct rtnl_link *link, unsigned int arptype) +{ + link->l_arptype = arptype; +} + +/** + * Get link layer type + * @arg link link handle + * @return Link layer type. + */ +unsigned int rtnl_link_get_arptype(struct rtnl_link *link) +{ + return link->l_arptype; +} + +/** + * Set interface index + * @arg link link to change + * @arg ifindex new interface index + */ +void rtnl_link_set_ifindex(struct rtnl_link *link, int ifindex) +{ + link->l_index = ifindex; + link->l_mask |= LINK_ATTR_IFINDEX; +} + +/** + * Get interface index + * @arg link link handle + * @return interface index or RTNL_LINK_NOT_FOUND if not set. + */ +int rtnl_link_get_ifindex(struct rtnl_link *link) +{ + if (link->l_mask & LINK_ATTR_IFINDEX) + return link->l_index; + else + return RTNL_LINK_NOT_FOUND; +} + +/** + * Set Maximum Transmission Unit + * @arg link link to change + * @arg mtu new MTU + */ +void rtnl_link_set_mtu(struct rtnl_link *link, unsigned int mtu) +{ + link->l_mtu = mtu; + link->l_mask |= LINK_ATTR_MTU; +} + +/** + * Get Maximum Transmission Unit + * @arg link link handle + * @return Link MTU or 0 if MTU is not set. + */ +unsigned int rtnl_link_get_mtu(struct rtnl_link *link) +{ + if (link->l_mask & LINK_ATTR_MTU) + return link->l_mtu; + else + return 0; +} + +/** + * Set Transmission Queue Length + * @arg link link to change + * @arg txqlen new TX queue length + * @note The unit of the transmission queue length depends on the + * link type, a common unit is \a packets. + */ +void rtnl_link_set_txqlen(struct rtnl_link *link, unsigned int txqlen) +{ + link->l_txqlen = txqlen; + link->l_mask |= LINK_ATTR_TXQLEN; +} + +/** + * Get Transmission Queue Length + * @arg link link handle + * @return Transmission Queue Length or UINT_MAX if not set. + */ +unsigned int rtnl_link_get_txqlen(struct rtnl_link *link) +{ + if (link->l_mask & LINK_ATTR_TXQLEN) + return link->l_txqlen; + else + return UINT_MAX; +} + +/** + * Set Weight + * @arg link link to change + * @arg weight new weight + */ +void rtnl_link_set_weight(struct rtnl_link *link, unsigned int weight) +{ + link->l_weight = weight; + link->l_mask |= LINK_ATTR_WEIGHT; +} + +/** + * Get Weight + * @arg link link handle + * @return Link weight or UINT_MAX if not set. + */ +unsigned int rtnl_link_get_weight(struct rtnl_link *link) +{ + if (link->l_mask & LINK_ATTR_WEIGHT) + return link->l_weight; + else + return UINT_MAX; +} + +/** + * Set parent interface index + * @arg link link to change + * @arg ifindex new parent's interface index + */ +void rtnl_link_set_link(struct rtnl_link *link, int ifindex) +{ + link->l_link = ifindex; + link->l_mask |= LINK_ATTR_LINK; +} + +/** + * Get parent interface index + * @arg link link handle + * @return Parent interface index or RTNL_LINK_NOT_FOUND if not set + */ +int rtnl_link_get_link(struct rtnl_link *link) +{ + if (link->l_mask & LINK_ATTR_LINK) + return link->l_link; + else + return RTNL_LINK_NOT_FOUND; +} + +/** + * Set master interface index + * @arg link link to change + * @arg ifindex new master's interface index + */ +void rtnl_link_set_master(struct rtnl_link *link, int ifindex) +{ + link->l_master = ifindex; + link->l_mask |= LINK_ATTR_MASTER; +} + +/** + * Get master interface index + * @arg link link handle + * @return Interface index of master or RTNL_LINK_NOT_FOUND if not set + */ +int rtnl_link_get_master(struct rtnl_link *link) +{ + if (link->l_mask & LINK_ATTR_MASTER) + return link->l_master; + else + return RTNL_LINK_NOT_FOUND; +} + +/** + * Get the statistic specified by the id + * @arg link link handle + * @arg id statistic id + * @return The current counter of the specified statistic + */ +uint64_t rtnl_link_get_stat(struct rtnl_link *link, int id) +{ + if (id < 0 || id > RTNL_LINK_STATS_MAX) + return 0; + + return link->l_stats[id]; +} + +/** @} */ + +static struct nl_cache_ops rtnl_link_ops = { + .co_name = "route/link", + .co_size = sizeof(struct rtnl_link), + .co_hdrsize = sizeof(struct ifinfomsg), + .co_msgtypes = { + { RTM_NEWLINK, "new" }, + { RTM_DELLINK, "delete" }, + { RTM_GETLINK, "get" }, + { -1, NULL }, + }, + .co_protocol = NETLINK_ROUTE, + .co_request_update = link_request_update, + .co_msg_parser = link_msg_parser, + .co_free_data = link_free_data, + .co_dump[NL_DUMP_BRIEF] = link_dump_brief, + .co_dump[NL_DUMP_FULL] = link_dump_full, + .co_dump[NL_DUMP_STATS] = link_dump_stats, + .co_dump[NL_DUMP_XML] = link_dump_xml, + .co_dump[NL_DUMP_ENV] = link_dump_env, + .co_filter = link_filter, +}; + +static void __init link_init(void) +{ + nl_cache_mngt_register(&rtnl_link_ops); +} + +static void __exit link_exit(void) +{ + nl_cache_mngt_unregister(&rtnl_link_ops); +} + +/** @} */