• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * lib/route/link/bridge.c	AF_BRIDGE link support
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) 2010-2013 Thomas Graf <tgraf@suug.ch>
10  */
11 
12 /**
13  * @ingroup link
14  * @defgroup bridge Bridging
15  *
16  * @details
17  * @{
18  */
19 
20 #include <netlink-private/netlink.h>
21 #include <netlink/netlink.h>
22 #include <netlink/attr.h>
23 #include <netlink/route/rtnl.h>
24 #include <netlink/route/link/bridge.h>
25 #include <netlink-private/route/link/api.h>
26 #include <linux/if_bridge.h>
27 
28 /** @cond SKIP */
29 #define BRIDGE_ATTR_PORT_STATE		(1 << 0)
30 #define BRIDGE_ATTR_PRIORITY		(1 << 1)
31 #define BRIDGE_ATTR_COST		(1 << 2)
32 #define BRIDGE_ATTR_FLAGS		(1 << 3)
33 
34 #define PRIV_FLAG_NEW_ATTRS		(1 << 0)
35 
36 struct bridge_data
37 {
38 	uint8_t			b_port_state;
39 	uint8_t			b_priv_flags; /* internal flags */
40 	uint16_t		b_priority;
41 	uint32_t		b_cost;
42 	uint32_t		b_flags;
43 	uint32_t		b_flags_mask;
44 	uint32_t                ce_mask; /* HACK to support attr macros */
45 };
46 
47 static struct rtnl_link_af_ops bridge_ops;
48 
49 #define IS_BRIDGE_LINK_ASSERT(link) \
50 	if (!rtnl_link_is_bridge(link)) { \
51 		APPBUG("A function was expecting a link object of type bridge."); \
52 		return -NLE_OPNOTSUPP; \
53 	}
54 
bridge_data(struct rtnl_link * link)55 static inline struct bridge_data *bridge_data(struct rtnl_link *link)
56 {
57 	return rtnl_link_af_data(link, &bridge_ops);
58 }
59 
bridge_alloc(struct rtnl_link * link)60 static void *bridge_alloc(struct rtnl_link *link)
61 {
62 	return calloc(1, sizeof(struct bridge_data));
63 }
64 
bridge_clone(struct rtnl_link * link,void * data)65 static void *bridge_clone(struct rtnl_link *link, void *data)
66 {
67 	struct bridge_data *bd;
68 
69 	if ((bd = bridge_alloc(link)))
70 		memcpy(bd, data, sizeof(*bd));
71 
72 	return bd;
73 }
74 
bridge_free(struct rtnl_link * link,void * data)75 static void bridge_free(struct rtnl_link *link, void *data)
76 {
77 	free(data);
78 }
79 
80 static struct nla_policy br_attrs_policy[IFLA_BRPORT_MAX+1] = {
81 	[IFLA_BRPORT_STATE]		= { .type = NLA_U8 },
82 	[IFLA_BRPORT_PRIORITY]		= { .type = NLA_U16 },
83 	[IFLA_BRPORT_COST]		= { .type = NLA_U32 },
84 	[IFLA_BRPORT_MODE]		= { .type = NLA_U8 },
85 	[IFLA_BRPORT_GUARD]		= { .type = NLA_U8 },
86 	[IFLA_BRPORT_PROTECT]		= { .type = NLA_U8 },
87 	[IFLA_BRPORT_FAST_LEAVE]	= { .type = NLA_U8 },
88 };
89 
check_flag(struct rtnl_link * link,struct nlattr * attrs[],int type,int flag)90 static void check_flag(struct rtnl_link *link, struct nlattr *attrs[],
91 		       int type, int flag)
92 {
93 	if (attrs[type] && nla_get_u8(attrs[type]))
94 		rtnl_link_bridge_set_flags(link, flag);
95 }
96 
bridge_parse_protinfo(struct rtnl_link * link,struct nlattr * attr,void * data)97 static int bridge_parse_protinfo(struct rtnl_link *link, struct nlattr *attr,
98 				 void *data)
99 {
100 	struct bridge_data *bd = data;
101 	struct nlattr *br_attrs[IFLA_BRPORT_MAX+1];
102 	int err;
103 
104 	/* Backwards compatibility */
105 	if (!nla_is_nested(attr)) {
106 		if (nla_len(attr) < 1)
107 			return -NLE_RANGE;
108 
109 		bd->b_port_state = nla_get_u8(attr);
110 		bd->ce_mask |= BRIDGE_ATTR_PORT_STATE;
111 
112 		return 0;
113 	}
114 
115 	if ((err = nla_parse_nested(br_attrs, IFLA_BRPORT_MAX, attr,
116 	     br_attrs_policy)) < 0)
117 		return err;
118 
119 	bd->b_priv_flags |= PRIV_FLAG_NEW_ATTRS;
120 
121 	if (br_attrs[IFLA_BRPORT_STATE]) {
122 		bd->b_port_state = nla_get_u8(br_attrs[IFLA_BRPORT_STATE]);
123 		bd->ce_mask |= BRIDGE_ATTR_PORT_STATE;
124 	}
125 
126 	if (br_attrs[IFLA_BRPORT_PRIORITY]) {
127 		bd->b_priority = nla_get_u16(br_attrs[IFLA_BRPORT_PRIORITY]);
128 		bd->ce_mask |= BRIDGE_ATTR_PRIORITY;
129 	}
130 
131 	if (br_attrs[IFLA_BRPORT_COST]) {
132 		bd->b_cost = nla_get_u32(br_attrs[IFLA_BRPORT_COST]);
133 		bd->ce_mask |= BRIDGE_ATTR_COST;
134 	}
135 
136 	check_flag(link, br_attrs, IFLA_BRPORT_MODE, RTNL_BRIDGE_HAIRPIN_MODE);
137 	check_flag(link, br_attrs, IFLA_BRPORT_GUARD, RTNL_BRIDGE_BPDU_GUARD);
138 	check_flag(link, br_attrs, IFLA_BRPORT_PROTECT, RTNL_BRIDGE_ROOT_BLOCK);
139 	check_flag(link, br_attrs, IFLA_BRPORT_FAST_LEAVE, RTNL_BRIDGE_FAST_LEAVE);
140 
141 	return 0;
142 }
143 
bridge_dump_details(struct rtnl_link * link,struct nl_dump_params * p,void * data)144 static void bridge_dump_details(struct rtnl_link *link,
145 				struct nl_dump_params *p, void *data)
146 {
147 	struct bridge_data *bd = data;
148 
149 	nl_dump_line(p, "    bridge: ");
150 
151 	if (bd->ce_mask & BRIDGE_ATTR_PORT_STATE)
152 		nl_dump(p, "port-state %u ", bd->b_port_state);
153 
154 	if (bd->ce_mask & BRIDGE_ATTR_PRIORITY)
155 		nl_dump(p, "prio %u ", bd->b_priority);
156 
157 	if (bd->ce_mask & BRIDGE_ATTR_COST)
158 		nl_dump(p, "cost %u ", bd->b_cost);
159 
160 	nl_dump(p, "\n");
161 }
162 
bridge_compare(struct rtnl_link * _a,struct rtnl_link * _b,int family,uint32_t attrs,int flags)163 static int bridge_compare(struct rtnl_link *_a, struct rtnl_link *_b,
164 			  int family, uint32_t attrs, int flags)
165 {
166 	struct bridge_data *a = bridge_data(_a);
167 	struct bridge_data *b = bridge_data(_b);
168 	int diff = 0;
169 
170 #define BRIDGE_DIFF(ATTR, EXPR) ATTR_DIFF(attrs, BRIDGE_ATTR_##ATTR, a, b, EXPR)
171 	diff |= BRIDGE_DIFF(PORT_STATE,	a->b_port_state != b->b_port_state);
172 	diff |= BRIDGE_DIFF(PRIORITY, a->b_priority != b->b_priority);
173 	diff |= BRIDGE_DIFF(COST, a->b_cost != b->b_cost);
174 
175 	if (flags & LOOSE_COMPARISON)
176 		diff |= BRIDGE_DIFF(FLAGS,
177 				  (a->b_flags ^ b->b_flags) & b->b_flags_mask);
178 	else
179 		diff |= BRIDGE_DIFF(FLAGS, a->b_flags != b->b_flags);
180 #undef BRIDGE_DIFF
181 
182 	return diff;
183 }
184 /** @endcond */
185 
186 /**
187  * Allocate link object of type bridge
188  *
189  * @return Allocated link object or NULL.
190  */
rtnl_link_bridge_alloc(void)191 struct rtnl_link *rtnl_link_bridge_alloc(void)
192 {
193 	struct rtnl_link *link;
194 	int err;
195 
196 	if (!(link = rtnl_link_alloc()))
197 		return NULL;
198 
199 	if ((err = rtnl_link_set_type(link, "bridge")) < 0) {
200 		rtnl_link_put(link);
201 		return NULL;
202 	}
203 
204 	return link;
205 }
206 
207 /**
208  * Create a new kernel bridge device
209  * @arg sk              netlink socket
210  * @arg name            name of the bridge device or NULL
211  *
212  * Creates a new bridge device in the kernel. If no name is
213  * provided, the kernel will automatically pick a name of the
214  * form "type%d" (e.g. bridge0, vlan1, etc.)
215  *
216  * @return 0 on success or a negative error code
217 */
rtnl_link_bridge_add(struct nl_sock * sk,const char * name)218 int rtnl_link_bridge_add(struct nl_sock *sk, const char *name)
219 {
220 	int err;
221 	struct rtnl_link *link;
222 
223 	if (!(link = rtnl_link_bridge_alloc()))
224 		return -NLE_NOMEM;
225 
226 	if(name)
227 		rtnl_link_set_name(link, name);
228 
229 	err = rtnl_link_add(sk, link, NLM_F_CREATE);
230 	rtnl_link_put(link);
231 
232 	return err;
233 }
234 
235 /**
236  * Check if a link is a bridge
237  * @arg link		Link object
238  *
239  * @return 1 if the link is a bridge, 0 otherwise.
240  */
rtnl_link_is_bridge(struct rtnl_link * link)241 int rtnl_link_is_bridge(struct rtnl_link *link)
242 {
243 	return link->l_family == AF_BRIDGE &&
244 	       link->l_af_ops == &bridge_ops;
245 }
246 
247 /**
248  * Check if bridge has extended information
249  * @arg link		Link object of type bridge
250  *
251  * Checks if the bridge object has been constructed based on
252  * information that is only available in newer kernels. This
253  * affectes the following functions:
254  *  - rtnl_link_bridge_get_cost()
255  *  - rtnl_link_bridge_get_priority()
256  *  - rtnl_link_bridge_get_flags()
257  *
258  * @return 1 if extended information is available, otherwise 0 is returned.
259  */
rtnl_link_bridge_has_ext_info(struct rtnl_link * link)260 int rtnl_link_bridge_has_ext_info(struct rtnl_link *link)
261 {
262 	struct bridge_data *bd;
263 
264 	if (!rtnl_link_is_bridge(link))
265 		return 0;
266 
267 	bd = bridge_data(link);
268 	return !!(bd->b_priv_flags & PRIV_FLAG_NEW_ATTRS);
269 }
270 
271 /**
272  * Set Spanning Tree Protocol (STP) port state
273  * @arg link		Link object of type bridge
274  * @arg state		New STP port state
275  *
276  * The value of state must be one of the following:
277  *   - BR_STATE_DISABLED
278  *   - BR_STATE_LISTENING
279  *   - BR_STATE_LEARNING
280  *   - BR_STATE_FORWARDING
281  *   - BR_STATE_BLOCKING
282  *
283  * @see rtnl_link_bridge_get_port_state()
284  *
285  * @return 0 on success or a negative error code.
286  * @retval -NLE_OPNOTSUPP Link is not a bridge
287  * @retval -NLE_INVAL Invalid state value (0..BR_STATE_BLOCKING)
288  */
rtnl_link_bridge_set_port_state(struct rtnl_link * link,uint8_t state)289 int rtnl_link_bridge_set_port_state(struct rtnl_link *link, uint8_t state)
290 {
291 	struct bridge_data *bd = bridge_data(link);
292 
293 	IS_BRIDGE_LINK_ASSERT(link);
294 
295 	if (state > BR_STATE_BLOCKING)
296 		return -NLE_INVAL;
297 
298 	bd->b_port_state = state;
299 	bd->ce_mask |= BRIDGE_ATTR_PORT_STATE;
300 
301 	return 0;
302 }
303 
304 /**
305  * Get Spanning Tree Protocol (STP) port state
306  * @arg link		Link object of type bridge
307  *
308  * @see rtnl_link_bridge_set_port_state()
309  *
310  * @return The STP port state or a negative error code.
311  * @retval -NLE_OPNOTSUPP Link is not a bridge
312  */
rtnl_link_bridge_get_port_state(struct rtnl_link * link)313 int rtnl_link_bridge_get_port_state(struct rtnl_link *link)
314 {
315 	struct bridge_data *bd = bridge_data(link);
316 
317 	IS_BRIDGE_LINK_ASSERT(link);
318 
319 	return bd->b_port_state;
320 }
321 
322 /**
323  * Set priority
324  * @arg link		Link object of type bridge
325  * @arg prio		Bridge priority
326  *
327  * @see rtnl_link_bridge_get_priority()
328  *
329  * @return 0 on success or a negative error code.
330  * @retval -NLE_OPNOTSUPP Link is not a bridge
331  */
rtnl_link_bridge_set_priority(struct rtnl_link * link,uint16_t prio)332 int rtnl_link_bridge_set_priority(struct rtnl_link *link, uint16_t prio)
333 {
334 	struct bridge_data *bd = bridge_data(link);
335 
336 	IS_BRIDGE_LINK_ASSERT(link);
337 
338 	bd->b_priority = prio;
339 	bd->ce_mask |= BRIDGE_ATTR_PRIORITY;
340 
341 	return 0;
342 }
343 
344 /**
345  * Get priority
346  * @arg link		Link object of type bridge
347  *
348  * @see rtnl_link_bridge_set_priority()
349  *
350  * @return 0 on success or a negative error code.
351  * @retval -NLE_OPNOTSUPP Link is not a bridge
352  */
rtnl_link_bridge_get_priority(struct rtnl_link * link)353 int rtnl_link_bridge_get_priority(struct rtnl_link *link)
354 {
355 	struct bridge_data *bd = bridge_data(link);
356 
357 	IS_BRIDGE_LINK_ASSERT(link);
358 
359 	return bd->b_priority;
360 }
361 
362 /**
363  * Set Spanning Tree Protocol (STP) path cost
364  * @arg link		Link object of type bridge
365  * @arg cost		New STP path cost value
366  *
367  * @see rtnl_link_bridge_get_cost()
368  *
369  * @return The bridge priority or a negative error code.
370  * @retval -NLE_OPNOTSUPP Link is not a bridge
371  */
rtnl_link_bridge_set_cost(struct rtnl_link * link,uint32_t cost)372 int rtnl_link_bridge_set_cost(struct rtnl_link *link, uint32_t cost)
373 {
374 	struct bridge_data *bd = bridge_data(link);
375 
376 	IS_BRIDGE_LINK_ASSERT(link);
377 
378 	bd->b_cost = cost;
379 	bd->ce_mask |= BRIDGE_ATTR_COST;
380 
381 	return 0;
382 }
383 
384 /**
385  * Get Spanning Tree Protocol (STP) path cost
386  * @arg link		Link object of type bridge
387  * @arg cost		Pointer to store STP cost value
388  *
389  * @see rtnl_link_bridge_set_cost()
390  *
391  * @return 0 on success or a negative error code.
392  * @retval -NLE_OPNOTSUPP Link is not a bridge
393  * @retval -NLE_INVAL `cost` is not a valid pointer
394  */
rtnl_link_bridge_get_cost(struct rtnl_link * link,uint32_t * cost)395 int rtnl_link_bridge_get_cost(struct rtnl_link *link, uint32_t *cost)
396 {
397 	struct bridge_data *bd = bridge_data(link);
398 
399 	IS_BRIDGE_LINK_ASSERT(link);
400 
401 	if (!cost)
402 		return -NLE_INVAL;
403 
404 	*cost = bd->b_cost;
405 
406 	return 0;
407 }
408 
409 /**
410  * Unset flags
411  * @arg link		Link object of type bridge
412  * @arg flags		Bridging flags to unset
413  *
414  * @see rtnl_link_bridge_set_flags()
415  * @see rtnl_link_bridge_get_flags()
416  *
417  * @return 0 on success or a negative error code.
418  * @retval -NLE_OPNOTSUPP Link is not a bridge
419  */
rtnl_link_bridge_unset_flags(struct rtnl_link * link,unsigned int flags)420 int rtnl_link_bridge_unset_flags(struct rtnl_link *link, unsigned int flags)
421 {
422 	struct bridge_data *bd = bridge_data(link);
423 
424 	IS_BRIDGE_LINK_ASSERT(link);
425 
426 	bd->b_flags_mask |= flags;
427 	bd->b_flags &= ~flags;
428 	bd->ce_mask |= BRIDGE_ATTR_FLAGS;
429 
430 	return 0;
431 }
432 
433 /**
434  * Set flags
435  * @arg link		Link object of type bridge
436  * @arg flags		Bridging flags to set
437  *
438  * Valid flags are:
439  *   - RTNL_BRIDGE_HAIRPIN_MODE
440  *   - RTNL_BRIDGE_BPDU_GUARD
441  *   - RTNL_BRIDGE_ROOT_BLOCK
442  *   - RTNL_BRIDGE_FAST_LEAVE
443  *
444  * @see rtnl_link_bridge_unset_flags()
445  * @see rtnl_link_bridge_get_flags()
446  *
447  * @return 0 on success or a negative error code.
448  * @retval -NLE_OPNOTSUPP Link is not a bridge
449  */
rtnl_link_bridge_set_flags(struct rtnl_link * link,unsigned int flags)450 int rtnl_link_bridge_set_flags(struct rtnl_link *link, unsigned int flags)
451 {
452 	struct bridge_data *bd = bridge_data(link);
453 
454 	IS_BRIDGE_LINK_ASSERT(link);
455 
456 	bd->b_flags_mask |= flags;
457 	bd->b_flags |= flags;
458 	bd->ce_mask |= BRIDGE_ATTR_FLAGS;
459 
460 	return 0;
461 }
462 
463 /**
464  * Get flags
465  * @arg link		Link object of type bridge
466  *
467  * @see rtnl_link_bridge_set_flags()
468  * @see rtnl_link_bridge_unset_flags()
469  *
470  * @return Flags or a negative error code.
471  * @retval -NLE_OPNOTSUPP Link is not a bridge
472  */
rtnl_link_bridge_get_flags(struct rtnl_link * link)473 int rtnl_link_bridge_get_flags(struct rtnl_link *link)
474 {
475 	struct bridge_data *bd = bridge_data(link);
476 
477 	IS_BRIDGE_LINK_ASSERT(link);
478 
479 	return bd->b_flags;
480 }
481 
482 static const struct trans_tbl bridge_flags[] = {
483 	__ADD(RTNL_BRIDGE_HAIRPIN_MODE, hairpin_mode)
484 	__ADD(RTNL_BRIDGE_BPDU_GUARD, 	bpdu_guard)
485 	__ADD(RTNL_BRIDGE_ROOT_BLOCK,	root_block)
486 	__ADD(RTNL_BRIDGE_FAST_LEAVE,	fast_leave)
487 };
488 
489 /**
490  * @name Flag Translation
491  * @{
492  */
493 
rtnl_link_bridge_flags2str(int flags,char * buf,size_t len)494 char *rtnl_link_bridge_flags2str(int flags, char *buf, size_t len)
495 {
496 	return __flags2str(flags, buf, len, bridge_flags, ARRAY_SIZE(bridge_flags));
497 }
498 
rtnl_link_bridge_str2flags(const char * name)499 int rtnl_link_bridge_str2flags(const char *name)
500 {
501 	return __str2flags(name, bridge_flags, ARRAY_SIZE(bridge_flags));
502 }
503 
504 /** @} */
505 
506 static struct rtnl_link_af_ops bridge_ops = {
507 	.ao_family			= AF_BRIDGE,
508 	.ao_alloc			= &bridge_alloc,
509 	.ao_clone			= &bridge_clone,
510 	.ao_free			= &bridge_free,
511 	.ao_parse_protinfo		= &bridge_parse_protinfo,
512 	.ao_dump[NL_DUMP_DETAILS]	= &bridge_dump_details,
513 	.ao_compare			= &bridge_compare,
514 };
515 
bridge_init(void)516 static void __init bridge_init(void)
517 {
518 	rtnl_link_af_register(&bridge_ops);
519 }
520 
bridge_exit(void)521 static void __exit bridge_exit(void)
522 {
523 	rtnl_link_af_unregister(&bridge_ops);
524 }
525 
526 /** @} */
527