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