Home | History | Annotate | Download | only in link
      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 (at) 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  * 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 */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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  */
    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 
    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 
    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 
    516 static void __init bridge_init(void)
    517 {
    518 	rtnl_link_af_register(&bridge_ops);
    519 }
    520 
    521 static void __exit bridge_exit(void)
    522 {
    523 	rtnl_link_af_unregister(&bridge_ops);
    524 }
    525 
    526 /** @} */
    527