link.c revision 44d362409d5469aed47d19e7908d19bd194493a4
1/*
2 * lib/route/link.c	Links (Interfaces)
3 *
4 *	This library is free software; you can redistribute it and/or
5 *	modify it under the terms of the GNU Lesser General Public
6 *	License as published by the Free Software Foundation version 2.1
7 *	of the License.
8 *
9 * Copyright (c) 2003-2006 Thomas Graf <tgraf@suug.ch>
10 */
11
12/**
13 * @ingroup rtnl
14 * @defgroup link Links (Interfaces)
15 * @brief
16 *
17 * @par Link Identification
18 * A link can be identified by either its interface index or by its
19 * name. The kernel favours the interface index but falls back to the
20 * interface name if the interface index is lesser-than 0 for kernels
21 * >= 2.6.11. Therefore you can request changes without mapping a
22 * interface name to the corresponding index first.
23 *
24 * @par Changeable Attributes
25 * @anchor link_changeable
26 *  - Link layer address
27 *  - Link layer broadcast address
28 *  - device mapping (ifmap) (>= 2.6.9)
29 *  - MTU (>= 2.6.9)
30 *  - Transmission queue length (>= 2.6.9)
31 *  - Weight (>= 2.6.9)
32 *  - Link name (only via access through interface index) (>= 2.6.9)
33 *  - Flags (>= 2.6.9)
34 *    - IFF_DEBUG
35 *    - IFF_NOTRAILERS
36 *    - IFF_NOARP
37 *    - IFF_DYNAMIC
38 *    - IFF_MULTICAST
39 *    - IFF_PORTSEL
40 *    - IFF_AUTOMEDIA
41 *    - IFF_UP
42 *    - IFF_PROMISC
43 *    - IFF_ALLMULTI
44 *
45 * @par Link Flags (linux/if.h)
46 * @anchor link_flags
47 * @code
48 *   IFF_UP            Status of link (up|down)
49 *   IFF_BROADCAST     Indicates this link allows broadcasting
50 *   IFF_MULTICAST     Indicates this link allows multicasting
51 *   IFF_ALLMULTI      Indicates this link is doing multicast routing
52 *   IFF_DEBUG         Tell the driver to do debugging (currently unused)
53 *   IFF_LOOPBACK      This is the loopback link
54 *   IFF_POINTOPOINT   Point-to-point link
55 *   IFF_NOARP         Link is unable to perform ARP
56 *   IFF_PROMISC       Status of promiscious mode flag
57 *   IFF_MASTER        Used by teql
58 *   IFF_SLAVE         Used by teql
59 *   IFF_PORTSEL       Indicates this link allows port selection
60 *   IFF_AUTOMEDIA     Indicates this link selects port automatically
61 *   IFF_DYNAMIC       Indicates the address of this link is dynamic
62 *   IFF_RUNNING       Link is running and carrier is ok.
63 *   IFF_NOTRAILERS    Unused, BSD compat.
64 * @endcode
65 *
66 * @par Notes on IFF_PROMISC and IFF_ALLMULTI flags
67 * Although you can query the status of IFF_PROMISC and IFF_ALLMULTI
68 * they do not represent the actual state in the kernel but rather
69 * whether the flag has been enabled/disabled by userspace. The link
70 * may be in promiscious mode even if IFF_PROMISC is not set in a link
71 * dump request response because promiscity might be needed by the driver
72 * for a period of time.
73 *
74 * @note The unit of the transmission queue length depends on the
75 *       link type, a common unit is \a packets.
76 *
77 * @par 1) Retrieving information about available links
78 * @code
79 * // The first step is to retrieve a list of all available interfaces within
80 * // the kernel and put them into a cache.
81 * struct nl_cache *cache = rtnl_link_alloc_cache(nl_handle);
82 *
83 * // In a second step, a specific link may be looked up by either interface
84 * // index or interface name.
85 * struct rtnl_link *link = rtnl_link_get_by_name(cache, "lo");
86 *
87 * // rtnl_link_get_by_name() is the short version for translating the
88 * // interface name to an interface index first like this:
89 * int ifindex = rtnl_link_name2i(cache, "lo");
90 * struct rtnl_link *link = rtnl_link_get(cache, ifindex);
91 *
92 * // After successful usage, the object must be given back to the cache
93 * rtnl_link_put(link);
94 * @endcode
95 *
96 * @par 2) Changing link attributes
97 * @code
98 * // In order to change any attributes of an existing link, we must allocate
99 * // a new link to hold the change requests:
100 * struct rtnl_link *request = rtnl_link_alloc();
101 *
102 * // Now we can go on and specify the attributes we want to change:
103 * rtnl_link_set_weight(request, 300);
104 * rtnl_link_set_mtu(request, 1360);
105 *
106 * // We can also shut an interface down administratively
107 * rtnl_link_unset_flags(request, rtnl_link_str2flags("up"));
108 *
109 * // Actually, we should know which link to change, so let's look it up
110 * struct rtnl_link *old = rtnl_link_get(cache, "eth0");
111 *
112 * // Two ways exist to commit this change request, the first one is to
113 * // build the required netlink message and send it out in one single
114 * // step:
115 * rtnl_link_change(nl_handle, old, request);
116 *
117 * // An alternative way is to build the netlink message and send it
118 * // out yourself using nl_send_auto_complete()
119 * struct nl_msg *msg = rtnl_link_build_change_request(old, request);
120 * nl_send_auto_complete(nl_handle, nlmsg_hdr(msg));
121 * nlmsg_free(msg);
122 *
123 * // Don't forget to give back the link object ;->
124 * rtnl_link_put(old);
125 * @endcode
126 * @{
127 */
128
129#include <netlink-local.h>
130#include <netlink/netlink.h>
131#include <netlink/attr.h>
132#include <netlink/utils.h>
133#include <netlink/object.h>
134#include <netlink/route/rtnl.h>
135#include <netlink/route/link.h>
136
137/** @cond SKIP */
138#define LINK_ATTR_MTU     0x0001
139#define LINK_ATTR_LINK    0x0002
140#define LINK_ATTR_TXQLEN  0x0004
141#define LINK_ATTR_WEIGHT  0x0008
142#define LINK_ATTR_MASTER  0x0010
143#define LINK_ATTR_QDISC   0x0020
144#define LINK_ATTR_MAP     0x0040
145#define LINK_ATTR_ADDR    0x0080
146#define LINK_ATTR_BRD     0x0100
147#define LINK_ATTR_FLAGS   0x0200
148#define LINK_ATTR_IFNAME  0x0400
149#define LINK_ATTR_IFINDEX 0x0800
150#define LINK_ATTR_FAMILY  0x1000
151#define LINK_ATTR_ARPTYPE 0x2000
152#define LINK_ATTR_STATS   0x4000
153#define LINK_ATTR_CHANGE  0x8000
154
155static struct nl_cache_ops rtnl_link_ops;
156static struct nl_object_ops link_obj_ops;
157/** @endcond */
158
159static void link_free_data(struct nl_object *c)
160{
161	struct rtnl_link *link = nl_object_priv(c);
162
163	if (link) {
164		nl_addr_put(link->l_addr);
165		nl_addr_put(link->l_bcast);
166	}
167}
168
169static int link_clone(struct nl_object *_dst, struct nl_object *_src)
170{
171	struct rtnl_link *dst = nl_object_priv(_dst);
172	struct rtnl_link *src = nl_object_priv(_src);
173
174	if (src->l_addr)
175		if (!(dst->l_addr = nl_addr_clone(src->l_addr)))
176			goto errout;
177
178	if (src->l_bcast)
179		if (!(dst->l_bcast = nl_addr_clone(src->l_bcast)))
180			goto errout;
181
182	return 0;
183errout:
184	return nl_get_errno();
185}
186
187static struct nla_policy link_policy[IFLA_MAX+1] = {
188	[IFLA_IFNAME]	= { .type = NLA_STRING,
189			    .maxlen = IFNAMSIZ },
190	[IFLA_MTU]	= { .type = NLA_U32 },
191	[IFLA_TXQLEN]	= { .type = NLA_U32 },
192	[IFLA_LINK]	= { .type = NLA_U32 },
193	[IFLA_WEIGHT]	= { .type = NLA_U32 },
194	[IFLA_MASTER]	= { .type = NLA_U32 },
195	[IFLA_QDISC]	= { .type = NLA_STRING,
196			    .maxlen = IFQDISCSIZ },
197	[IFLA_STATS]	= { .minlen = sizeof(struct rtnl_link_stats) },
198	[IFLA_MAP]	= { .minlen = sizeof(struct rtnl_link_ifmap) },
199};
200
201static int link_msg_parser(struct nl_cache_ops *ops, struct sockaddr_nl *who,
202			   struct nlmsghdr *n, void *arg)
203{
204	struct rtnl_link *link;
205	struct ifinfomsg *ifi;
206	struct nlattr *tb[IFLA_MAX+1];
207	struct nl_parser_param *pp = arg;
208	int err;
209
210	link = rtnl_link_alloc();
211	if (link == NULL) {
212		err = nl_errno(ENOMEM);
213		goto errout;
214	}
215
216	link->ce_msgtype = n->nlmsg_type;
217
218	err = nlmsg_parse(n, sizeof(*ifi), tb, IFLA_MAX, link_policy);
219	if (err < 0)
220		goto errout;
221
222	if (tb[IFLA_IFNAME] == NULL) {
223		err = nl_error(EINVAL, "Missing link name TLV");
224		goto errout;
225	}
226
227	nla_strlcpy(link->l_name, tb[IFLA_IFNAME], IFNAMSIZ);
228
229	ifi = nlmsg_data(n);
230	link->l_family = ifi->ifi_family;
231	link->l_arptype = ifi->ifi_type;
232	link->l_index = ifi->ifi_index;
233	link->l_flags = ifi->ifi_flags;
234	link->l_change = ifi->ifi_change;
235	link->ce_mask = (LINK_ATTR_IFNAME | LINK_ATTR_FAMILY |
236			  LINK_ATTR_ARPTYPE| LINK_ATTR_IFINDEX |
237			  LINK_ATTR_FLAGS | LINK_ATTR_CHANGE);
238
239	if (tb[IFLA_STATS]) {
240		struct rtnl_link_stats *st = nla_data(tb[IFLA_STATS]);
241
242		link->l_stats[RTNL_LINK_RX_PACKETS]	= st->rx_packets;
243		link->l_stats[RTNL_LINK_RX_BYTES]	= st->rx_bytes;
244		link->l_stats[RTNL_LINK_RX_ERRORS]	= st->rx_errors;
245		link->l_stats[RTNL_LINK_RX_DROPPED]	= st->rx_dropped;
246		link->l_stats[RTNL_LINK_RX_COMPRESSED]	= st->rx_compressed;
247		link->l_stats[RTNL_LINK_RX_FIFO_ERR]	= st->rx_fifo_errors;
248		link->l_stats[RTNL_LINK_TX_PACKETS]	= st->tx_packets;
249		link->l_stats[RTNL_LINK_TX_BYTES]	= st->tx_bytes;
250		link->l_stats[RTNL_LINK_TX_ERRORS]	= st->tx_errors;
251		link->l_stats[RTNL_LINK_TX_DROPPED]	= st->tx_dropped;
252		link->l_stats[RTNL_LINK_TX_COMPRESSED]	= st->tx_compressed;
253		link->l_stats[RTNL_LINK_TX_FIFO_ERR]	= st->tx_fifo_errors;
254		link->l_stats[RTNL_LINK_RX_LEN_ERR]	= st->rx_length_errors;
255		link->l_stats[RTNL_LINK_RX_OVER_ERR]	= st->rx_over_errors;
256		link->l_stats[RTNL_LINK_RX_CRC_ERR]	= st->rx_crc_errors;
257		link->l_stats[RTNL_LINK_RX_FRAME_ERR]	= st->rx_frame_errors;
258		link->l_stats[RTNL_LINK_RX_MISSED_ERR]	= st->rx_missed_errors;
259		link->l_stats[RTNL_LINK_TX_ABORT_ERR]	= st->tx_aborted_errors;
260		link->l_stats[RTNL_LINK_TX_CARRIER_ERR]	= st->tx_carrier_errors;
261		link->l_stats[RTNL_LINK_TX_HBEAT_ERR]	= st->tx_heartbeat_errors;
262		link->l_stats[RTNL_LINK_TX_WIN_ERR]	= st->tx_window_errors;
263		link->l_stats[RTNL_LINK_MULTICAST]	= st->multicast;
264
265		link->ce_mask |= LINK_ATTR_STATS;
266	}
267
268	if (tb[IFLA_TXQLEN]) {
269		link->l_txqlen = nla_get_u32(tb[IFLA_TXQLEN]);
270		link->ce_mask |= LINK_ATTR_TXQLEN;
271	}
272
273	if (tb[IFLA_MTU]) {
274		link->l_mtu = nla_get_u32(tb[IFLA_MTU]);
275		link->ce_mask |= LINK_ATTR_MTU;
276	}
277
278	if (tb[IFLA_ADDRESS]) {
279		link->l_addr = nla_get_addr(tb[IFLA_ADDRESS], AF_UNSPEC);
280		if (link->l_addr == NULL)
281			goto errout;
282		nl_addr_set_family(link->l_addr,
283				   nl_addr_guess_family(link->l_addr));
284		link->ce_mask |= LINK_ATTR_ADDR;
285	}
286
287	if (tb[IFLA_BROADCAST]) {
288		link->l_bcast = nla_get_addr(tb[IFLA_BROADCAST], AF_UNSPEC);
289		if (link->l_bcast == NULL)
290			goto errout;
291		nl_addr_set_family(link->l_bcast,
292				   nl_addr_guess_family(link->l_bcast));
293		link->ce_mask |= LINK_ATTR_BRD;
294	}
295
296	if (tb[IFLA_LINK]) {
297		link->l_link = nla_get_u32(tb[IFLA_LINK]);
298		link->ce_mask |= LINK_ATTR_LINK;
299	}
300
301	if (tb[IFLA_WEIGHT]) {
302		link->l_weight = nla_get_u32(tb[IFLA_WEIGHT]);
303		link->ce_mask |= LINK_ATTR_WEIGHT;
304	}
305
306	if (tb[IFLA_QDISC]) {
307		nla_strlcpy(link->l_qdisc, tb[IFLA_QDISC], IFQDISCSIZ);
308		link->ce_mask |= LINK_ATTR_QDISC;
309	}
310
311	if (tb[IFLA_MAP]) {
312		struct rtnl_link_ifmap *map =  nla_data(tb[IFLA_MAP]);
313		link->l_map.lm_mem_start = map->mem_start;
314		link->l_map.lm_mem_end   = map->mem_end;
315		link->l_map.lm_base_addr = map->base_addr;
316		link->l_map.lm_irq       = map->irq;
317		link->l_map.lm_dma       = map->dma;
318		link->l_map.lm_port      = map->port;
319		link->ce_mask |= LINK_ATTR_MAP;
320	}
321
322	if (tb[IFLA_MASTER]) {
323		link->l_master = nla_get_u32(tb[IFLA_MASTER]);
324		link->ce_mask |= LINK_ATTR_MASTER;
325	}
326
327	err = pp->pp_cb((struct nl_object *) link, pp);
328	if (err < 0)
329		goto errout;
330
331	return P_ACCEPT;
332
333errout:
334	rtnl_link_put(link);
335	return err;
336}
337
338static int link_request_update(struct nl_cache *c, struct nl_handle *h)
339{
340	return nl_rtgen_request(h, RTM_GETLINK, AF_UNSPEC, NLM_F_DUMP);
341}
342
343static int link_dump_brief(struct nl_object *obj, struct nl_dump_params *p)
344{
345	char buf[128];
346	struct nl_cache *cache = dp_cache(obj);
347	struct rtnl_link *link = (struct rtnl_link *) obj;
348	int line = 1;
349
350	dp_dump(p, "%s ", link->l_name);
351
352	if (link->ce_mask & LINK_ATTR_LINK) {
353		struct rtnl_link *ll = rtnl_link_get(cache, link->l_link);
354		dp_dump(p, "@%s", ll ? ll->l_name : "NONE");
355		if (ll)
356			rtnl_link_put(ll);
357	}
358
359	dp_dump(p, "%s ", nl_llproto2str(link->l_arptype, buf, sizeof(buf)));
360	dp_dump(p, "%s ", link->l_addr ?  nl_addr2str(link->l_addr, buf,
361						     sizeof(buf)) : "none");
362	dp_dump(p, "mtu %u ", link->l_mtu);
363
364	if (link->ce_mask & LINK_ATTR_MASTER) {
365		struct rtnl_link *master = rtnl_link_get(cache, link->l_master);
366		dp_dump(p, "master %s ", master ? master->l_name : "inv");
367		if (master)
368			rtnl_link_put(master);
369	}
370
371	rtnl_link_flags2str(link->l_flags, buf, sizeof(buf));
372	if (buf[0])
373		dp_dump(p, "<%s>", buf);
374
375	dp_dump(p, "\n");
376
377	return line;
378}
379
380static int link_dump_full(struct nl_object *obj, struct nl_dump_params *p)
381{
382	struct rtnl_link *link = (struct rtnl_link *) obj;
383	char buf[64];
384	int line;
385
386	line = link_dump_brief(obj, p);
387	dp_new_line(p, line++);
388
389	dp_dump(p, "    txqlen %u weight %u ", link->l_txqlen, link->l_weight);
390
391	if (link->ce_mask & LINK_ATTR_QDISC)
392		dp_dump(p, "qdisc %s ", link->l_qdisc);
393
394	if (link->ce_mask & LINK_ATTR_MAP && link->l_map.lm_irq)
395		dp_dump(p, "irq %u ", link->l_map.lm_irq);
396
397	if (link->ce_mask & LINK_ATTR_IFINDEX)
398		dp_dump(p, "index %u ", link->l_index);
399
400	if (link->ce_mask & LINK_ATTR_BRD)
401		dp_dump(p, "brd %s", nl_addr2str(link->l_bcast, buf,
402						   sizeof(buf)));
403
404	dp_dump(p, "\n");
405
406	return line;
407}
408
409static int link_dump_stats(struct nl_object *obj, struct nl_dump_params *p)
410{
411	struct rtnl_link *link = (struct rtnl_link *) obj;
412	char *unit, fmt[64];
413	float res;
414	int line;
415
416	line = link_dump_full(obj, p);
417
418	dp_dump_line(p, line++, "    Stats:    bytes    packets     errors "
419				"   dropped   fifo-err compressed\n");
420
421	res = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_RX_BYTES], &unit);
422
423	strcpy(fmt, "    RX  %X.2f %s %10llu %10llu %10llu %10llu %10llu\n");
424	fmt[9] = *unit == 'B' ? '9' : '7';
425
426	dp_dump_line(p, line++, fmt,
427		res, unit,
428		link->l_stats[RTNL_LINK_RX_PACKETS],
429		link->l_stats[RTNL_LINK_RX_ERRORS],
430		link->l_stats[RTNL_LINK_RX_DROPPED],
431		link->l_stats[RTNL_LINK_RX_FIFO_ERR],
432		link->l_stats[RTNL_LINK_RX_COMPRESSED]);
433
434	res = nl_cancel_down_bytes(link->l_stats[RTNL_LINK_TX_BYTES], &unit);
435
436	strcpy(fmt, "    TX  %X.2f %s %10llu %10llu %10llu %10llu %10llu\n");
437	fmt[9] = *unit == 'B' ? '9' : '7';
438
439	dp_dump_line(p, line++, fmt,
440		res, unit,
441		link->l_stats[RTNL_LINK_TX_PACKETS],
442		link->l_stats[RTNL_LINK_TX_ERRORS],
443		link->l_stats[RTNL_LINK_TX_DROPPED],
444		link->l_stats[RTNL_LINK_TX_FIFO_ERR],
445		link->l_stats[RTNL_LINK_TX_COMPRESSED]);
446
447	dp_dump_line(p, line++, "    Errors:  length       over        crc "
448				"     frame     missed  multicast\n");
449
450	dp_dump_line(p, line++, "    RX   %10" PRIu64 " %10" PRIu64 " %10"
451				PRIu64 " %10" PRIu64 " %10" PRIu64 " %10"
452				PRIu64 "\n",
453		link->l_stats[RTNL_LINK_RX_LEN_ERR],
454		link->l_stats[RTNL_LINK_RX_OVER_ERR],
455		link->l_stats[RTNL_LINK_RX_CRC_ERR],
456		link->l_stats[RTNL_LINK_RX_FRAME_ERR],
457		link->l_stats[RTNL_LINK_RX_MISSED_ERR],
458		link->l_stats[RTNL_LINK_MULTICAST]);
459
460	dp_dump_line(p, line++, "    Errors: aborted    carrier  heartbeat "
461				"    window  collision\n");
462
463	dp_dump_line(p, line++, "    TX   %10" PRIu64 " %10" PRIu64 " %10"
464				PRIu64 " %10" PRIu64 " %10" PRIu64 "\n",
465		link->l_stats[RTNL_LINK_TX_ABORT_ERR],
466		link->l_stats[RTNL_LINK_TX_CARRIER_ERR],
467		link->l_stats[RTNL_LINK_TX_HBEAT_ERR],
468		link->l_stats[RTNL_LINK_TX_WIN_ERR],
469		link->l_stats[RTNL_LINK_TX_COLLISIONS]);
470
471	return line;
472}
473
474static int link_dump_xml(struct nl_object *obj, struct nl_dump_params *p)
475{
476	struct rtnl_link *link = (struct rtnl_link *) obj;
477	struct nl_cache *cache = dp_cache(obj);
478	char buf[128];
479	int i, line = 0;
480
481	dp_dump_line(p, line++, "<link name=\"%s\" index=\"%u\">\n",
482		     link->l_name, link->l_index);
483	dp_dump_line(p, line++, "  <family>%s</family>\n",
484		     nl_af2str(link->l_family, buf, sizeof(buf)));
485	dp_dump_line(p, line++, "  <arptype>%s</arptype>\n",
486		     nl_llproto2str(link->l_arptype, buf, sizeof(buf)));
487	dp_dump_line(p, line++, "  <address>%s</address>\n",
488		     nl_addr2str(link->l_addr, buf, sizeof(buf)));
489	dp_dump_line(p, line++, "  <mtu>%u</mtu>\n", link->l_mtu);
490	dp_dump_line(p, line++, "  <txqlen>%u</txqlen>\n", link->l_txqlen);
491	dp_dump_line(p, line++, "  <weight>%u</weight>\n", link->l_weight);
492
493	rtnl_link_flags2str(link->l_flags, buf, sizeof(buf));
494	if (buf[0])
495		dp_dump_line(p, line++, "  <flags>%s</flags>\n", buf);
496
497	if (link->ce_mask & LINK_ATTR_QDISC)
498		dp_dump_line(p, line++, "  <qdisc>%s</qdisc>\n", link->l_qdisc);
499
500	if (link->ce_mask & LINK_ATTR_LINK) {
501		struct rtnl_link *ll = rtnl_link_get(cache, link->l_link);
502		dp_dump_line(p, line++, "  <link>%s</link>\n",
503			     ll ? ll->l_name : "none");
504		if (ll)
505			rtnl_link_put(ll);
506	}
507
508	if (link->ce_mask & LINK_ATTR_MASTER) {
509		struct rtnl_link *master = rtnl_link_get(cache, link->l_master);
510		dp_dump_line(p, line++, "  <master>%s</master>\n",
511			     master ? master->l_name : "none");
512		if (master)
513			rtnl_link_put(master);
514	}
515
516	if (link->ce_mask & LINK_ATTR_BRD)
517		dp_dump_line(p, line++, "  <broadcast>%s</broadcast>\n",
518			     nl_addr2str(link->l_bcast, buf, sizeof(buf)));
519
520	if (link->ce_mask & LINK_ATTR_STATS) {
521		dp_dump_line(p, line++, "  <stats>\n");
522		for (i = 0; i <= RTNL_LINK_STATS_MAX; i++) {
523			rtnl_link_stat2str(i, buf, sizeof(buf));
524			dp_dump_line(p, line++,
525				     "    <%s>%" PRIu64 "</%s>\n",
526				     buf, link->l_stats[i], buf);
527		}
528		dp_dump_line(p, line++, "  </stats>\n");
529	}
530
531	dp_dump_line(p, line++, "</link>\n");
532
533#if 0
534	uint32_t	l_change;	/**< Change mask */
535	struct rtnl_lifmap l_map;	/**< Interface device mapping */
536#endif
537
538	return line;
539}
540
541static int link_dump_env(struct nl_object *obj, struct nl_dump_params *p)
542{
543	struct rtnl_link *link = (struct rtnl_link *) obj;
544	struct nl_cache *cache = dp_cache(obj);
545	char buf[128];
546	int i, line = 0;
547
548	dp_dump_line(p, line++, "LINK_NAME=%s\n", link->l_name);
549	dp_dump_line(p, line++, "LINK_IFINDEX=%u\n", link->l_index);
550	dp_dump_line(p, line++, "LINK_FAMILY=%s\n",
551		     nl_af2str(link->l_family, buf, sizeof(buf)));
552	dp_dump_line(p, line++, "LINK_TYPE=%s\n",
553		     nl_llproto2str(link->l_arptype, buf, sizeof(buf)));
554	if (link->ce_mask & LINK_ATTR_ADDR)
555		dp_dump_line(p, line++, "LINK_ADDRESS=%s\n",
556			     nl_addr2str(link->l_addr, buf, sizeof(buf)));
557	dp_dump_line(p, line++, "LINK_MTU=%u\n", link->l_mtu);
558	dp_dump_line(p, line++, "LINK_TXQUEUELEN=%u\n", link->l_txqlen);
559	dp_dump_line(p, line++, "LINK_WEIGHT=%u\n", link->l_weight);
560
561	rtnl_link_flags2str(link->l_flags & ~IFF_RUNNING, buf, sizeof(buf));
562	if (buf[0])
563		dp_dump_line(p, line++, "LINK_FLAGS=%s\n", buf);
564
565	if (link->ce_mask & LINK_ATTR_QDISC)
566		dp_dump_line(p, line++, "LINK_QDISC=%s\n", link->l_qdisc);
567
568	if (link->ce_mask & LINK_ATTR_LINK) {
569		struct rtnl_link *ll = rtnl_link_get(cache, link->l_link);
570
571		dp_dump_line(p, line++, "LINK_LINK_IFINDEX=%d\n", link->l_link);
572		if (ll) {
573			dp_dump_line(p, line++, "LINK_LINK_IFNAME=%s\n",
574				     ll->l_name);
575			rtnl_link_put(ll);
576		}
577	}
578
579	if (link->ce_mask & LINK_ATTR_MASTER) {
580		struct rtnl_link *master = rtnl_link_get(cache, link->l_master);
581		dp_dump_line(p, line++, "LINK_MASTER=%s\n",
582			     master ? master->l_name : "none");
583		if (master)
584			rtnl_link_put(master);
585	}
586
587	if (link->ce_mask & LINK_ATTR_BRD)
588		dp_dump_line(p, line++, "LINK_BROADCAST=%s\n",
589			     nl_addr2str(link->l_bcast, buf, sizeof(buf)));
590
591	if (link->ce_mask & LINK_ATTR_STATS) {
592		for (i = 0; i <= RTNL_LINK_STATS_MAX; i++) {
593			char *c = buf;
594
595			sprintf(buf, "LINK_");
596			rtnl_link_stat2str(i, buf + 5, sizeof(buf) - 5);
597			while (*c) {
598				*c = toupper(*c);
599				c++;
600			}
601			dp_dump_line(p, line++,
602				     "%s=%" PRIu64 "\n", buf, link->l_stats[i]);
603		}
604	}
605
606	return line;
607}
608
609#if 0
610static int link_handle_event(struct nl_object *a, struct rtnl_link_event_cb *cb)
611{
612	struct rtnl_link *l = (struct rtnl_link *) a;
613	struct nl_cache *c = dp_cache(a);
614	int nevents = 0;
615
616	if (l->l_change == ~0U) {
617		if (l->ce_msgtype == RTM_NEWLINK)
618			cb->le_register(l);
619		else
620			cb->le_unregister(l);
621
622		return 1;
623	}
624
625	if (l->l_change & IFF_SLAVE) {
626		if (l->l_flags & IFF_SLAVE) {
627			struct rtnl_link *m = rtnl_link_get(c, l->l_master);
628			cb->le_new_bonding(l, m);
629			if (m)
630				rtnl_link_put(m);
631		} else
632			cb->le_cancel_bonding(l);
633	}
634
635#if 0
636	if (l->l_change & IFF_UP && l->l_change & IFF_RUNNING)
637		dp_dump_line(p, line++, "link %s changed state to %s.\n",
638			l->l_name, l->l_flags & IFF_UP ? "up" : "down");
639
640	if (l->l_change & IFF_PROMISC) {
641		dp_new_line(p, line++);
642		dp_dump(p, "link %s %s promiscuous mode.\n",
643		    l->l_name, l->l_flags & IFF_PROMISC ? "entered" : "left");
644	}
645
646	if (line == 0)
647		dp_dump_line(p, line++, "link %s sent unknown event.\n",
648			     l->l_name);
649#endif
650
651	return nevents;
652}
653#endif
654
655static int link_compare(struct nl_object *_a, struct nl_object *_b,
656			uint32_t attrs, int flags)
657{
658	struct rtnl_link *a = (struct rtnl_link *) _a;
659	struct rtnl_link *b = (struct rtnl_link *) _b;
660	int diff = 0;
661
662#define LINK_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, LINK_ATTR_##ATTR, a, b, EXPR)
663
664	diff |= LINK_DIFF(IFINDEX,	a->l_index != b->l_index);
665	diff |= LINK_DIFF(MTU,		a->l_mtu != b->l_mtu);
666	diff |= LINK_DIFF(LINK,		a->l_link != b->l_link);
667	diff |= LINK_DIFF(TXQLEN,	a->l_txqlen != b->l_txqlen);
668	diff |= LINK_DIFF(WEIGHT,	a->l_weight != b->l_weight);
669	diff |= LINK_DIFF(MASTER,	a->l_master != b->l_master);
670	diff |= LINK_DIFF(FAMILY,	a->l_family != b->l_family);
671	diff |= LINK_DIFF(QDISC,	strcmp(a->l_qdisc, b->l_qdisc));
672	diff |= LINK_DIFF(IFNAME,	strcmp(a->l_name, b->l_name));
673	diff |= LINK_DIFF(ADDR,		nl_addr_cmp(a->l_addr, b->l_addr));
674	diff |= LINK_DIFF(BRD,		nl_addr_cmp(a->l_bcast, b->l_bcast));
675
676	if (flags & LOOSE_FLAG_COMPARISON)
677		diff |= LINK_DIFF(FLAGS,
678				  (a->l_flags ^ b->l_flags) & b->l_flag_mask);
679	else
680		diff |= LINK_DIFF(FLAGS, a->l_flags != b->l_flags);
681
682#undef LINK_DIFF
683
684	return diff;
685}
686
687static struct trans_tbl link_attrs[] = {
688	__ADD(LINK_ATTR_MTU, mtu)
689	__ADD(LINK_ATTR_LINK, link)
690	__ADD(LINK_ATTR_TXQLEN, txqlen)
691	__ADD(LINK_ATTR_WEIGHT, weight)
692	__ADD(LINK_ATTR_MASTER, master)
693	__ADD(LINK_ATTR_QDISC, qdisc)
694	__ADD(LINK_ATTR_MAP, map)
695	__ADD(LINK_ATTR_ADDR, address)
696	__ADD(LINK_ATTR_BRD, broadcast)
697	__ADD(LINK_ATTR_FLAGS, flags)
698	__ADD(LINK_ATTR_IFNAME, name)
699	__ADD(LINK_ATTR_IFINDEX, ifindex)
700	__ADD(LINK_ATTR_FAMILY, family)
701	__ADD(LINK_ATTR_ARPTYPE, arptype)
702	__ADD(LINK_ATTR_STATS, stats)
703	__ADD(LINK_ATTR_CHANGE, change)
704};
705
706static char *link_attrs2str(int attrs, char *buf, size_t len)
707{
708	return __flags2str(attrs, buf, len, link_attrs,
709			   ARRAY_SIZE(link_attrs));
710}
711
712/**
713 * @name Allocation/Freeing
714 * @{
715 */
716
717struct rtnl_link *rtnl_link_alloc(void)
718{
719	return (struct rtnl_link *) nl_object_alloc(&link_obj_ops);
720}
721
722void rtnl_link_put(struct rtnl_link *link)
723{
724	nl_object_put((struct nl_object *) link);
725}
726
727/** @} */
728
729/**
730 * @name Cache Management
731 * @{
732 */
733
734
735/**
736 * Allocate link cache and fill in all configured links.
737 * @arg handle		Netlink handle.
738 *
739 * Allocates a new link cache, initializes it properly and updates it
740 * to include all links currently configured in the kernel.
741 *
742 * @note Free the memory after usage.
743 * @return Newly allocated cache or NULL if an error occured.
744 */
745struct nl_cache *rtnl_link_alloc_cache(struct nl_handle *handle)
746{
747	struct nl_cache * cache;
748
749	cache = nl_cache_alloc(&rtnl_link_ops);
750	if (cache == NULL)
751		return NULL;
752
753	if (handle && nl_cache_refill(handle, cache) < 0) {
754		nl_cache_free(cache);
755		return NULL;
756	}
757
758	return cache;
759}
760
761/**
762 * Look up link by interface index in the provided cache
763 * @arg cache		link cache
764 * @arg ifindex		link interface index
765 *
766 * The caller owns a reference on the returned object and
767 * must give the object back via rtnl_link_put().
768 *
769 * @return pointer to link inside the cache or NULL if no match was found.
770 */
771struct rtnl_link *rtnl_link_get(struct nl_cache *cache, int ifindex)
772{
773	struct rtnl_link *link;
774
775	if (cache->c_ops != &rtnl_link_ops)
776		return NULL;
777
778	nl_list_for_each_entry(link, &cache->c_items, ce_list) {
779		if (link->l_index == ifindex) {
780			nl_object_get((struct nl_object *) link);
781			return link;
782		}
783	}
784
785	return NULL;
786}
787
788/**
789 * Look up link by link name in the provided cache
790 * @arg cache		link cache
791 * @arg name		link name
792 *
793 * The caller owns a reference on the returned object and
794 * must give the object back via rtnl_link_put().
795 *
796 * @return pointer to link inside the cache or NULL if no match was found.
797 */
798struct rtnl_link *rtnl_link_get_by_name(struct nl_cache *cache,
799					 const char *name)
800{
801	struct rtnl_link *link;
802
803	if (cache->c_ops != &rtnl_link_ops)
804		return NULL;
805
806	nl_list_for_each_entry(link, &cache->c_items, ce_list) {
807		if (!strcmp(name, link->l_name)) {
808			nl_object_get((struct nl_object *) link);
809			return link;
810		}
811	}
812
813	return NULL;
814}
815
816/** @} */
817
818/**
819 * @name Link Modifications
820 * @{
821 */
822
823/**
824 * Builds a netlink change request message to change link attributes
825 * @arg old		link to be changed
826 * @arg tmpl		template with requested changes
827 * @arg flags		additional netlink message flags
828 *
829 * Builds a new netlink message requesting a change of link attributes.
830 * The netlink message header isn't fully equipped with all relevant
831 * fields and must be sent out via nl_send_auto_complete() or
832 * supplemented as needed.
833 * \a old must point to a link currently configured in the kernel
834 * and \a tmpl must contain the attributes to be changed set via
835 * \c rtnl_link_set_* functions.
836 *
837 * @return New netlink message
838 * @note Not all attributes can be changed, see
839 *       \ref link_changeable "Changeable Attributes" for more details.
840 */
841struct nl_msg * rtnl_link_build_change_request(struct rtnl_link *old,
842					       struct rtnl_link *tmpl,
843					       int flags)
844{
845	struct nl_msg *msg;
846	struct ifinfomsg ifi = {
847		.ifi_family = old->l_family,
848		.ifi_index = old->l_index,
849	};
850
851	if (tmpl->ce_mask & LINK_ATTR_FLAGS) {
852		ifi.ifi_flags = old->l_flags & ~tmpl->l_flag_mask;
853		ifi.ifi_flags |= tmpl->l_flags;
854	}
855
856	msg = nlmsg_alloc_simple(RTM_SETLINK, flags);
857	if (!msg)
858		goto nla_put_failure;
859
860	if (nlmsg_append(msg, &ifi, sizeof(ifi), NLMSG_ALIGNTO) < 0)
861		goto nla_put_failure;
862
863	if (tmpl->ce_mask & LINK_ATTR_ADDR)
864		NLA_PUT_ADDR(msg, IFLA_ADDRESS, tmpl->l_addr);
865
866	if (tmpl->ce_mask & LINK_ATTR_BRD)
867		NLA_PUT_ADDR(msg, IFLA_BROADCAST, tmpl->l_bcast);
868
869	if (tmpl->ce_mask & LINK_ATTR_MTU)
870		NLA_PUT_U32(msg, IFLA_MTU, tmpl->l_mtu);
871
872	if (tmpl->ce_mask & LINK_ATTR_TXQLEN)
873		NLA_PUT_U32(msg, IFLA_TXQLEN, tmpl->l_txqlen);
874
875	if (tmpl->ce_mask & LINK_ATTR_WEIGHT)
876		NLA_PUT_U32(msg, IFLA_WEIGHT, tmpl->l_weight);
877
878	if (tmpl->ce_mask & LINK_ATTR_IFNAME)
879		NLA_PUT_STRING(msg, IFLA_IFNAME, tmpl->l_name);
880
881	return msg;
882
883nla_put_failure:
884	nlmsg_free(msg);
885	return NULL;
886}
887
888/**
889 * Change link attributes
890 * @arg handle		netlink handle
891 * @arg old		link to be changed
892 * @arg tmpl		template with requested changes
893 * @arg flags		additional netlink message flags
894 *
895 * Builds a new netlink message by calling rtnl_link_build_change_request(),
896 * sends the request to the kernel and waits for the next ACK to be
897 * received, i.e. blocks until the request has been processed.
898 *
899 * @return 0 on success or a negative error code
900 * @note Not all attributes can be changed, see
901 *       \ref link_changeable "Changeable Attributes" for more details.
902 */
903int rtnl_link_change(struct nl_handle *handle, struct rtnl_link *old,
904		     struct rtnl_link *tmpl, int flags)
905{
906	int err;
907	struct nl_msg *msg;
908
909	msg = rtnl_link_build_change_request(old, tmpl, flags);
910	if (!msg)
911		return nl_errno(ENOMEM);
912
913	err = nl_send_auto_complete(handle, msg);
914	if (err < 0)
915		return err;
916
917	nlmsg_free(msg);
918	return nl_wait_for_ack(handle);
919}
920
921/** @} */
922
923/**
924 * @name Name <-> Index Translations
925 * @{
926 */
927
928/**
929 * Translate an interface index to the corresponding link name
930 * @arg cache		link cache
931 * @arg ifindex		link interface index
932 * @arg dst		destination buffer
933 * @arg len		length of destination buffer
934 *
935 * Translates the specified interface index to the corresponding
936 * link name and stores the name in the destination buffer.
937 *
938 * @return link name or NULL if no match was found.
939 */
940char * rtnl_link_i2name(struct nl_cache *cache, int ifindex, char *dst,
941			size_t len)
942{
943	struct rtnl_link *link = rtnl_link_get(cache, ifindex);
944
945	if (link) {
946		strncpy(dst, link->l_name, len - 1);
947		rtnl_link_put(link);
948		return dst;
949	}
950
951	return NULL;
952}
953
954/**
955 * Translate a link name to the corresponding interface index
956 * @arg cache		link cache
957 * @arg name		link name
958 *
959 * @return interface index or RTNL_LINK_NOT_FOUND if no match was found.
960 */
961int rtnl_link_name2i(struct nl_cache *cache, const char *name)
962{
963	int ifindex = RTNL_LINK_NOT_FOUND;
964	struct rtnl_link *link;
965
966	link = rtnl_link_get_by_name(cache, name);
967	if (link) {
968		ifindex = link->l_index;
969		rtnl_link_put(link);
970	}
971
972	return ifindex;
973}
974
975/** @} */
976
977/**
978 * @name Link Flags Translations
979 * @{
980 */
981
982static struct trans_tbl link_flags[] = {
983	__ADD(IFF_LOOPBACK, loopback)
984	__ADD(IFF_BROADCAST, broadcast)
985	__ADD(IFF_POINTOPOINT, pointopoint)
986	__ADD(IFF_MULTICAST, multicast)
987	__ADD(IFF_NOARP, noarp)
988	__ADD(IFF_ALLMULTI, allmulti)
989	__ADD(IFF_PROMISC, promisc)
990	__ADD(IFF_MASTER, master)
991	__ADD(IFF_SLAVE, slave)
992	__ADD(IFF_DEBUG, debug)
993	__ADD(IFF_DYNAMIC, dynamic)
994	__ADD(IFF_AUTOMEDIA, automedia)
995	__ADD(IFF_PORTSEL, portsel)
996	__ADD(IFF_NOTRAILERS, notrailers)
997	__ADD(IFF_UP, up)
998	__ADD(IFF_RUNNING, running)
999	__ADD(IFF_LOWER_UP, lowerup)
1000	__ADD(IFF_DORMANT, dormant)
1001};
1002
1003char * rtnl_link_flags2str(int flags, char *buf, size_t len)
1004{
1005	return __flags2str(flags, buf, len, link_flags,
1006			   ARRAY_SIZE(link_flags));
1007}
1008
1009int rtnl_link_str2flags(const char *name)
1010{
1011	return __str2flags(name, link_flags, ARRAY_SIZE(link_flags));
1012}
1013
1014/** @} */
1015
1016/**
1017 * @name Link Statistics Translations
1018 * @{
1019 */
1020
1021static struct trans_tbl link_stats[] = {
1022	__ADD(RTNL_LINK_RX_PACKETS, rx_packets)
1023	__ADD(RTNL_LINK_TX_PACKETS, tx_packets)
1024	__ADD(RTNL_LINK_RX_BYTES, rx_bytes)
1025	__ADD(RTNL_LINK_TX_BYTES, tx_bytes)
1026	__ADD(RTNL_LINK_RX_ERRORS, rx_errors)
1027	__ADD(RTNL_LINK_TX_ERRORS, tx_errors)
1028	__ADD(RTNL_LINK_RX_DROPPED, rx_dropped)
1029	__ADD(RTNL_LINK_TX_DROPPED, tx_dropped)
1030	__ADD(RTNL_LINK_RX_COMPRESSED, rx_compressed)
1031	__ADD(RTNL_LINK_TX_COMPRESSED, tx_compressed)
1032	__ADD(RTNL_LINK_RX_FIFO_ERR, rx_fifo_err)
1033	__ADD(RTNL_LINK_TX_FIFO_ERR, tx_fifo_err)
1034	__ADD(RTNL_LINK_RX_LEN_ERR, rx_len_err)
1035	__ADD(RTNL_LINK_RX_OVER_ERR, rx_over_err)
1036	__ADD(RTNL_LINK_RX_CRC_ERR, rx_crc_err)
1037	__ADD(RTNL_LINK_RX_FRAME_ERR, rx_frame_err)
1038	__ADD(RTNL_LINK_RX_MISSED_ERR, rx_missed_err)
1039	__ADD(RTNL_LINK_TX_ABORT_ERR, tx_abort_err)
1040	__ADD(RTNL_LINK_TX_CARRIER_ERR, tx_carrier_err)
1041	__ADD(RTNL_LINK_TX_HBEAT_ERR, tx_hbeat_err)
1042	__ADD(RTNL_LINK_TX_WIN_ERR, tx_win_err)
1043	__ADD(RTNL_LINK_TX_COLLISIONS, tx_collision)
1044	__ADD(RTNL_LINK_MULTICAST, multicast)
1045};
1046
1047char *rtnl_link_stat2str(int st, char *buf, size_t len)
1048{
1049	return __type2str(st, buf, len, link_stats, ARRAY_SIZE(link_stats));
1050}
1051
1052int rtnl_link_str2stat(const char *name)
1053{
1054	return __str2type(name, link_stats, ARRAY_SIZE(link_stats));
1055}
1056
1057/** @} */
1058
1059/**
1060 * @name Attributes
1061 * @{
1062 */
1063
1064void rtnl_link_set_qdisc(struct rtnl_link *link, const char *qdisc)
1065{
1066	strncpy(link->l_qdisc, qdisc, sizeof(link->l_qdisc) - 1);
1067	link->ce_mask |= LINK_ATTR_QDISC;
1068}
1069
1070char *rtnl_link_get_qdisc(struct rtnl_link *link)
1071{
1072	if (link->ce_mask & LINK_ATTR_QDISC)
1073		return link->l_qdisc;
1074	else
1075		return NULL;
1076}
1077
1078void rtnl_link_set_name(struct rtnl_link *link, const char *name)
1079{
1080	strncpy(link->l_name, name, sizeof(link->l_name) - 1);
1081	link->ce_mask |= LINK_ATTR_IFNAME;
1082}
1083
1084char *rtnl_link_get_name(struct rtnl_link *link)
1085{
1086	if (link->ce_mask & LINK_ATTR_IFNAME)
1087		return link->l_name;
1088	else
1089		return NULL;
1090}
1091
1092static inline void __assign_addr(struct rtnl_link *link, struct nl_addr **pos,
1093				 struct nl_addr *new, int flag)
1094{
1095	if (*pos)
1096		nl_addr_put(*pos);
1097
1098	nl_addr_get(new);
1099	*pos = new;
1100
1101	link->ce_mask |= flag;
1102}
1103
1104void rtnl_link_set_addr(struct rtnl_link *link, struct nl_addr *addr)
1105{
1106	__assign_addr(link, &link->l_addr, addr, LINK_ATTR_ADDR);
1107}
1108
1109struct nl_addr *rtnl_link_get_addr(struct rtnl_link *link)
1110{
1111	if (link->ce_mask & LINK_ATTR_ADDR)
1112		return link->l_addr;
1113	else
1114		return NULL;
1115}
1116
1117void rtnl_link_set_broadcast(struct rtnl_link *link, struct nl_addr *brd)
1118{
1119	__assign_addr(link, &link->l_bcast, brd, LINK_ATTR_BRD);
1120}
1121
1122struct nl_addr *rtnl_link_get_broadcast(struct rtnl_link *link)
1123{
1124	if (link->ce_mask & LINK_ATTR_BRD)
1125		return link->l_bcast;
1126	else
1127		return NULL;
1128}
1129
1130void rtnl_link_set_flags(struct rtnl_link *link, unsigned int flags)
1131{
1132	link->l_flag_mask |= flags;
1133	link->l_flags |= flags;
1134	link->ce_mask |= LINK_ATTR_FLAGS;
1135}
1136
1137void rtnl_link_unset_flags(struct rtnl_link *link, unsigned int flags)
1138{
1139	link->l_flag_mask |= flags;
1140	link->l_flags &= ~flags;
1141	link->ce_mask |= LINK_ATTR_FLAGS;
1142}
1143
1144unsigned int rtnl_link_get_flags(struct rtnl_link *link)
1145{
1146	return link->l_flags;
1147}
1148
1149void rtnl_link_set_family(struct rtnl_link *link, int family)
1150{
1151	link->l_family = family;
1152	link->ce_mask |= LINK_ATTR_FAMILY;
1153}
1154
1155int rtnl_link_get_family(struct rtnl_link *link)
1156{
1157	if (link->l_family & LINK_ATTR_FAMILY)
1158		return link->l_family;
1159	else
1160		return AF_UNSPEC;
1161}
1162
1163void rtnl_link_set_arptype(struct rtnl_link *link, unsigned int arptype)
1164{
1165	link->l_arptype = arptype;
1166}
1167
1168unsigned int rtnl_link_get_arptype(struct rtnl_link *link)
1169{
1170	return link->l_arptype;
1171}
1172
1173void rtnl_link_set_ifindex(struct rtnl_link *link, int ifindex)
1174{
1175	link->l_index = ifindex;
1176	link->ce_mask |= LINK_ATTR_IFINDEX;
1177}
1178
1179int rtnl_link_get_ifindex(struct rtnl_link *link)
1180{
1181	if (link->ce_mask & LINK_ATTR_IFINDEX)
1182		return link->l_index;
1183	else
1184		return RTNL_LINK_NOT_FOUND;
1185}
1186
1187void rtnl_link_set_mtu(struct rtnl_link *link, unsigned int mtu)
1188{
1189	link->l_mtu = mtu;
1190	link->ce_mask |= LINK_ATTR_MTU;
1191}
1192
1193unsigned int rtnl_link_get_mtu(struct rtnl_link *link)
1194{
1195	if (link->ce_mask & LINK_ATTR_MTU)
1196		return link->l_mtu;
1197	else
1198		return 0;
1199}
1200
1201void rtnl_link_set_txqlen(struct rtnl_link *link, unsigned int txqlen)
1202{
1203	link->l_txqlen = txqlen;
1204	link->ce_mask |= LINK_ATTR_TXQLEN;
1205}
1206
1207unsigned int rtnl_link_get_txqlen(struct rtnl_link *link)
1208{
1209	if (link->ce_mask & LINK_ATTR_TXQLEN)
1210		return link->l_txqlen;
1211	else
1212		return UINT_MAX;
1213}
1214
1215void rtnl_link_set_weight(struct rtnl_link *link, unsigned int weight)
1216{
1217	link->l_weight = weight;
1218	link->ce_mask |= LINK_ATTR_WEIGHT;
1219}
1220
1221unsigned int rtnl_link_get_weight(struct rtnl_link *link)
1222{
1223	if (link->ce_mask & LINK_ATTR_WEIGHT)
1224		return link->l_weight;
1225	else
1226		return UINT_MAX;
1227}
1228
1229void rtnl_link_set_link(struct rtnl_link *link, int ifindex)
1230{
1231	link->l_link = ifindex;
1232	link->ce_mask |= LINK_ATTR_LINK;
1233}
1234
1235int rtnl_link_get_link(struct rtnl_link *link)
1236{
1237	if (link->ce_mask & LINK_ATTR_LINK)
1238		return link->l_link;
1239	else
1240		return RTNL_LINK_NOT_FOUND;
1241}
1242
1243void rtnl_link_set_master(struct rtnl_link *link, int ifindex)
1244{
1245	link->l_master = ifindex;
1246	link->ce_mask |= LINK_ATTR_MASTER;
1247}
1248
1249int rtnl_link_get_master(struct rtnl_link *link)
1250{
1251	if (link->ce_mask & LINK_ATTR_MASTER)
1252		return link->l_master;
1253	else
1254		return RTNL_LINK_NOT_FOUND;
1255}
1256
1257uint64_t rtnl_link_get_stat(struct rtnl_link *link, int id)
1258{
1259	if (id < 0 || id > RTNL_LINK_STATS_MAX)
1260		return 0;
1261
1262	return link->l_stats[id];
1263}
1264
1265/** @} */
1266
1267static struct nl_object_ops link_obj_ops = {
1268	.oo_name		= "route/link",
1269	.oo_size		= sizeof(struct rtnl_link),
1270	.oo_free_data		= link_free_data,
1271	.oo_clone		= link_clone,
1272	.oo_dump[NL_DUMP_BRIEF]	= link_dump_brief,
1273	.oo_dump[NL_DUMP_FULL]	= link_dump_full,
1274	.oo_dump[NL_DUMP_STATS]	= link_dump_stats,
1275	.oo_dump[NL_DUMP_XML]	= link_dump_xml,
1276	.oo_dump[NL_DUMP_ENV]	= link_dump_env,
1277	.oo_compare		= link_compare,
1278	.oo_attrs2str		= link_attrs2str,
1279	.oo_id_attrs		= LINK_ATTR_IFINDEX,
1280};
1281
1282static struct nl_af_group link_groups[] = {
1283	{ AF_UNSPEC,	RTNLGRP_LINK },
1284	{ END_OF_GROUP_LIST },
1285};
1286
1287static struct nl_cache_ops rtnl_link_ops = {
1288	.co_name		= "route/link",
1289	.co_hdrsize		= sizeof(struct ifinfomsg),
1290	.co_msgtypes		= {
1291					{ RTM_NEWLINK, NL_ACT_NEW, "new" },
1292					{ RTM_DELLINK, NL_ACT_DEL, "del" },
1293					{ RTM_GETLINK, NL_ACT_GET, "get" },
1294					END_OF_MSGTYPES_LIST,
1295				  },
1296	.co_protocol		= NETLINK_ROUTE,
1297	.co_groups		= link_groups,
1298	.co_request_update	= link_request_update,
1299	.co_msg_parser		= link_msg_parser,
1300	.co_obj_ops		= &link_obj_ops,
1301};
1302
1303static void __init link_init(void)
1304{
1305	nl_cache_mngt_register(&rtnl_link_ops);
1306}
1307
1308static void __exit link_exit(void)
1309{
1310	nl_cache_mngt_unregister(&rtnl_link_ops);
1311}
1312
1313/** @} */
1314