libnl  3.2.21
bridge.c
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 
55 static inline struct bridge_data *bridge_data(struct rtnl_link *link)
56 {
57  return rtnl_link_af_data(link, &bridge_ops);
58 }
59 
60 static void *bridge_alloc(struct rtnl_link *link)
61 {
62  return calloc(1, sizeof(struct bridge_data));
63 }
64 
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 
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 
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 
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 
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 
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  */
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  * Check if a link is a bridge
209  * @arg link Link object
210  *
211  * @return 1 if the link is a bridge, 0 otherwise.
212  */
214 {
215  return link->l_family == AF_BRIDGE &&
216  link->l_af_ops == &bridge_ops;
217 }
218 
219 /**
220  * Check if bridge has extended information
221  * @arg link Link object of type bridge
222  *
223  * Checks if the bridge object has been constructed based on
224  * information that is only available in newer kernels. This
225  * affectes the following functions:
226  * - rtnl_link_bridge_get_cost()
227  * - rtnl_link_bridge_get_priority()
228  * - rtnl_link_bridge_get_flags()
229  *
230  * @return 1 if extended information is available, otherwise 0 is returned.
231  */
233 {
234  struct bridge_data *bd;
235 
236  if (!rtnl_link_is_bridge(link))
237  return 0;
238 
239  bd = bridge_data(link);
240  return !!(bd->b_priv_flags & PRIV_FLAG_NEW_ATTRS);
241 }
242 
243 /**
244  * Set Spanning Tree Protocol (STP) port state
245  * @arg link Link object of type bridge
246  * @arg state New STP port state
247  *
248  * The value of state must be one of the following:
249  * - BR_STATE_DISABLED
250  * - BR_STATE_LISTENING
251  * - BR_STATE_LEARNING
252  * - BR_STATE_FORWARDING
253  * - BR_STATE_BLOCKING
254  *
255  * @see rtnl_link_bridge_get_port_state()
256  *
257  * @return 0 on success or a negative error code.
258  * @retval -NLE_OPNOTSUPP Link is not a bridge
259  * @retval -NLE_INVAL Invalid state value (0..BR_STATE_BLOCKING)
260  */
261 int rtnl_link_bridge_set_port_state(struct rtnl_link *link, uint8_t state)
262 {
263  struct bridge_data *bd = bridge_data(link);
264 
265  IS_BRIDGE_LINK_ASSERT(link);
266 
267  if (state > BR_STATE_BLOCKING)
268  return -NLE_INVAL;
269 
270  bd->b_port_state = state;
271  bd->ce_mask |= BRIDGE_ATTR_PORT_STATE;
272 
273  return 0;
274 }
275 
276 /**
277  * Get Spanning Tree Protocol (STP) port state
278  * @arg link Link object of type bridge
279  *
280  * @see rtnl_link_bridge_set_port_state()
281  *
282  * @return The STP port state or a negative error code.
283  * @retval -NLE_OPNOTSUPP Link is not a bridge
284  */
286 {
287  struct bridge_data *bd = bridge_data(link);
288 
289  IS_BRIDGE_LINK_ASSERT(link);
290 
291  return bd->b_port_state;
292 }
293 
294 /**
295  * Set priority
296  * @arg link Link object of type bridge
297  * @arg prio Bridge priority
298  *
299  * @see rtnl_link_bridge_get_priority()
300  *
301  * @return 0 on success or a negative error code.
302  * @retval -NLE_OPNOTSUPP Link is not a bridge
303  */
304 int rtnl_link_bridge_set_priority(struct rtnl_link *link, uint16_t prio)
305 {
306  struct bridge_data *bd = bridge_data(link);
307 
308  IS_BRIDGE_LINK_ASSERT(link);
309 
310  bd->b_priority = prio;
311  bd->ce_mask |= BRIDGE_ATTR_PRIORITY;
312 
313  return 0;
314 }
315 
316 /**
317  * Get priority
318  * @arg link Link object of type bridge
319  *
320  * @see rtnl_link_bridge_set_priority()
321  *
322  * @return 0 on success or a negative error code.
323  * @retval -NLE_OPNOTSUPP Link is not a bridge
324  */
326 {
327  struct bridge_data *bd = bridge_data(link);
328 
329  IS_BRIDGE_LINK_ASSERT(link);
330 
331  return bd->b_priority;
332 }
333 
334 /**
335  * Set Spanning Tree Protocol (STP) path cost
336  * @arg link Link object of type bridge
337  * @arg cost New STP path cost value
338  *
339  * @see rtnl_link_bridge_get_cost()
340  *
341  * @return The bridge priority or a negative error code.
342  * @retval -NLE_OPNOTSUPP Link is not a bridge
343  */
344 int rtnl_link_bridge_set_cost(struct rtnl_link *link, uint32_t cost)
345 {
346  struct bridge_data *bd = bridge_data(link);
347 
348  IS_BRIDGE_LINK_ASSERT(link);
349 
350  bd->b_cost = cost;
351  bd->ce_mask |= BRIDGE_ATTR_COST;
352 
353  return 0;
354 }
355 
356 /**
357  * Get Spanning Tree Protocol (STP) path cost
358  * @arg link Link object of type bridge
359  * @arg cost Pointer to store STP cost value
360  *
361  * @see rtnl_link_bridge_set_cost()
362  *
363  * @return 0 on success or a negative error code.
364  * @retval -NLE_OPNOTSUPP Link is not a bridge
365  * @retval -NLE_INVAL `cost` is not a valid pointer
366  */
367 int rtnl_link_bridge_get_cost(struct rtnl_link *link, uint32_t *cost)
368 {
369  struct bridge_data *bd = bridge_data(link);
370 
371  IS_BRIDGE_LINK_ASSERT(link);
372 
373  if (!cost)
374  return -NLE_INVAL;
375 
376  *cost = bd->b_cost;
377 
378  return 0;
379 }
380 
381 /**
382  * Unset flags
383  * @arg link Link object of type bridge
384  * @arg flags Bridging flags to unset
385  *
386  * @see rtnl_link_bridge_set_flags()
387  * @see rtnl_link_bridge_get_flags()
388  *
389  * @return 0 on success or a negative error code.
390  * @retval -NLE_OPNOTSUPP Link is not a bridge
391  */
392 int rtnl_link_bridge_unset_flags(struct rtnl_link *link, unsigned int flags)
393 {
394  struct bridge_data *bd = bridge_data(link);
395 
396  IS_BRIDGE_LINK_ASSERT(link);
397 
398  bd->b_flags_mask |= flags;
399  bd->b_flags &= ~flags;
400  bd->ce_mask |= BRIDGE_ATTR_FLAGS;
401 
402  return 0;
403 }
404 
405 /**
406  * Set flags
407  * @arg link Link object of type bridge
408  * @arg flags Bridging flags to set
409  *
410  * Valid flags are:
411  * - RTNL_BRIDGE_HAIRPIN_MODE
412  * - RTNL_BRIDGE_BPDU_GUARD
413  * - RTNL_BRIDGE_ROOT_BLOCK
414  * - RTNL_BRIDGE_FAST_LEAVE
415  *
416  * @see rtnl_link_bridge_unset_flags()
417  * @see rtnl_link_bridge_get_flags()
418  *
419  * @return 0 on success or a negative error code.
420  * @retval -NLE_OPNOTSUPP Link is not a bridge
421  */
422 int rtnl_link_bridge_set_flags(struct rtnl_link *link, unsigned int flags)
423 {
424  struct bridge_data *bd = bridge_data(link);
425 
426  IS_BRIDGE_LINK_ASSERT(link);
427 
428  bd->b_flags_mask |= flags;
429  bd->b_flags |= flags;
430  bd->ce_mask |= BRIDGE_ATTR_FLAGS;
431 
432  return 0;
433 }
434 
435 /**
436  * Get flags
437  * @arg link Link object of type bridge
438  *
439  * @see rtnl_link_bridge_set_flags()
440  * @see rtnl_link_bridge_unset_flags()
441  *
442  * @return Flags or a negative error code.
443  * @retval -NLE_OPNOTSUPP Link is not a bridge
444  */
446 {
447  struct bridge_data *bd = bridge_data(link);
448 
449  IS_BRIDGE_LINK_ASSERT(link);
450 
451  return bd->b_flags;
452 }
453 
454 static struct rtnl_link_af_ops bridge_ops = {
455  .ao_family = AF_BRIDGE,
456  .ao_alloc = &bridge_alloc,
457  .ao_clone = &bridge_clone,
458  .ao_free = &bridge_free,
459  .ao_parse_protinfo = &bridge_parse_protinfo,
460  .ao_dump[NL_DUMP_DETAILS] = &bridge_dump_details,
461  .ao_compare = &bridge_compare,
462 };
463 
464 static void __init bridge_init(void)
465 {
466  rtnl_link_af_register(&bridge_ops);
467 }
468 
469 static void __exit bridge_exit(void)
470 {
471  rtnl_link_af_unregister(&bridge_ops);
472 }
473 
474 /** @} */