Imported Upstream version 3.2.22
[platform/upstream/libnl3.git] / lib / route / link / 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  */
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  * 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  */
213 int rtnl_link_is_bridge(struct rtnl_link *link)
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  */
232 int rtnl_link_bridge_has_ext_info(struct rtnl_link *link)
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  */
285 int rtnl_link_bridge_get_port_state(struct rtnl_link *link)
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  */
325 int rtnl_link_bridge_get_priority(struct rtnl_link *link)
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  */
445 int rtnl_link_bridge_get_flags(struct rtnl_link *link)
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 /** @} */