e4ec59744869b027288975ad2661d8bf3a048428
[project/netifd.git] / bridge.c
1 /*
2 * netifd - network interface daemon
3 * Copyright (C) 2012 Felix Fietkau <nbd@openwrt.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2
7 * as published by the Free Software Foundation
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14 #include <string.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17 #include <assert.h>
18 #include <errno.h>
19
20 #include "netifd.h"
21 #include "device.h"
22 #include "interface.h"
23 #include "system.h"
24
25 enum {
26 BRIDGE_ATTR_IFNAME,
27 BRIDGE_ATTR_STP,
28 BRIDGE_ATTR_FORWARD_DELAY,
29 BRIDGE_ATTR_PRIORITY,
30 BRIDGE_ATTR_IGMP_SNOOP,
31 BRIDGE_ATTR_AGEING_TIME,
32 BRIDGE_ATTR_HELLO_TIME,
33 BRIDGE_ATTR_MAX_AGE,
34 BRIDGE_ATTR_BRIDGE_EMPTY,
35 BRIDGE_ATTR_MULTICAST_QUERIER,
36 BRIDGE_ATTR_HASH_MAX,
37 BRIDGE_ATTR_ROBUSTNESS,
38 BRIDGE_ATTR_QUERY_INTERVAL,
39 BRIDGE_ATTR_QUERY_RESPONSE_INTERVAL,
40 BRIDGE_ATTR_LAST_MEMBER_INTERVAL,
41 BRIDGE_ATTR_VLAN_FILTERING,
42 __BRIDGE_ATTR_MAX
43 };
44
45 static const struct blobmsg_policy bridge_attrs[__BRIDGE_ATTR_MAX] = {
46 [BRIDGE_ATTR_IFNAME] = { "ifname", BLOBMSG_TYPE_ARRAY },
47 [BRIDGE_ATTR_STP] = { "stp", BLOBMSG_TYPE_BOOL },
48 [BRIDGE_ATTR_FORWARD_DELAY] = { "forward_delay", BLOBMSG_TYPE_INT32 },
49 [BRIDGE_ATTR_PRIORITY] = { "priority", BLOBMSG_TYPE_INT32 },
50 [BRIDGE_ATTR_AGEING_TIME] = { "ageing_time", BLOBMSG_TYPE_INT32 },
51 [BRIDGE_ATTR_HELLO_TIME] = { "hello_time", BLOBMSG_TYPE_INT32 },
52 [BRIDGE_ATTR_MAX_AGE] = { "max_age", BLOBMSG_TYPE_INT32 },
53 [BRIDGE_ATTR_IGMP_SNOOP] = { "igmp_snooping", BLOBMSG_TYPE_BOOL },
54 [BRIDGE_ATTR_BRIDGE_EMPTY] = { "bridge_empty", BLOBMSG_TYPE_BOOL },
55 [BRIDGE_ATTR_MULTICAST_QUERIER] = { "multicast_querier", BLOBMSG_TYPE_BOOL },
56 [BRIDGE_ATTR_HASH_MAX] = { "hash_max", BLOBMSG_TYPE_INT32 },
57 [BRIDGE_ATTR_ROBUSTNESS] = { "robustness", BLOBMSG_TYPE_INT32 },
58 [BRIDGE_ATTR_QUERY_INTERVAL] = { "query_interval", BLOBMSG_TYPE_INT32 },
59 [BRIDGE_ATTR_QUERY_RESPONSE_INTERVAL] = { "query_response_interval", BLOBMSG_TYPE_INT32 },
60 [BRIDGE_ATTR_LAST_MEMBER_INTERVAL] = { "last_member_interval", BLOBMSG_TYPE_INT32 },
61 [BRIDGE_ATTR_VLAN_FILTERING] = { "vlan_filtering", BLOBMSG_TYPE_BOOL },
62 };
63
64 static const struct uci_blob_param_info bridge_attr_info[__BRIDGE_ATTR_MAX] = {
65 [BRIDGE_ATTR_IFNAME] = { .type = BLOBMSG_TYPE_STRING },
66 };
67
68 static const struct uci_blob_param_list bridge_attr_list = {
69 .n_params = __BRIDGE_ATTR_MAX,
70 .params = bridge_attrs,
71 .info = bridge_attr_info,
72
73 .n_next = 1,
74 .next = { &device_attr_list },
75 };
76
77 static struct device *bridge_create(const char *name, struct device_type *devtype,
78 struct blob_attr *attr);
79 static void bridge_config_init(struct device *dev);
80 static void bridge_free(struct device *dev);
81 static void bridge_dump_info(struct device *dev, struct blob_buf *b);
82 static enum dev_change_type
83 bridge_reload(struct device *dev, struct blob_attr *attr);
84
85 static struct device_type bridge_device_type = {
86 .name = "bridge",
87 .config_params = &bridge_attr_list,
88
89 .bridge_capability = true,
90 .name_prefix = "br",
91
92 .create = bridge_create,
93 .config_init = bridge_config_init,
94 .reload = bridge_reload,
95 .free = bridge_free,
96 .dump_info = bridge_dump_info,
97 };
98
99 struct bridge_state {
100 struct device dev;
101 device_state_cb set_state;
102
103 struct blob_attr *config_data;
104 struct bridge_config config;
105 struct blob_attr *ifnames;
106 bool active;
107 bool force_active;
108
109 struct uloop_timeout retry;
110 struct bridge_member *primary_port;
111 struct vlist_tree members;
112 int n_present;
113 int n_failed;
114 };
115
116 struct bridge_member {
117 struct vlist_node node;
118 struct bridge_state *bst;
119 struct device_user dev;
120 bool present;
121 char name[];
122 };
123
124 static void
125 bridge_reset_primary(struct bridge_state *bst)
126 {
127 struct bridge_member *bm;
128
129 if (!bst->primary_port &&
130 (bst->dev.settings.flags & DEV_OPT_MACADDR))
131 return;
132
133 bst->primary_port = NULL;
134 bst->dev.settings.flags &= ~DEV_OPT_MACADDR;
135 vlist_for_each_element(&bst->members, bm, node) {
136 uint8_t *macaddr;
137
138 if (!bm->present)
139 continue;
140
141 bst->primary_port = bm;
142 if (bm->dev.dev->settings.flags & DEV_OPT_MACADDR)
143 macaddr = bm->dev.dev->settings.macaddr;
144 else
145 macaddr = bm->dev.dev->orig_settings.macaddr;
146 memcpy(bst->dev.settings.macaddr, macaddr, 6);
147 bst->dev.settings.flags |= DEV_OPT_MACADDR;
148 return;
149 }
150 }
151
152 static int
153 bridge_disable_member(struct bridge_member *bm)
154 {
155 struct bridge_state *bst = bm->bst;
156
157 if (!bm->present)
158 return 0;
159
160 system_bridge_delif(&bst->dev, bm->dev.dev);
161 device_release(&bm->dev);
162
163 device_broadcast_event(&bst->dev, DEV_EVENT_TOPO_CHANGE);
164
165 return 0;
166 }
167
168 static int
169 bridge_enable_interface(struct bridge_state *bst)
170 {
171 int ret;
172
173 if (bst->active)
174 return 0;
175
176 ret = system_bridge_addbr(&bst->dev, &bst->config);
177 if (ret < 0)
178 return ret;
179
180 bst->active = true;
181 return 0;
182 }
183
184 static void
185 bridge_disable_interface(struct bridge_state *bst)
186 {
187 if (!bst->active)
188 return;
189
190 system_bridge_delbr(&bst->dev);
191 bst->active = false;
192 }
193
194 static int
195 bridge_enable_member(struct bridge_member *bm)
196 {
197 struct bridge_state *bst = bm->bst;
198 int ret;
199
200 if (!bm->present)
201 return 0;
202
203 ret = bridge_enable_interface(bst);
204 if (ret)
205 goto error;
206
207 /* Disable IPv6 for bridge members */
208 if (!(bm->dev.dev->settings.flags & DEV_OPT_IPV6)) {
209 bm->dev.dev->settings.ipv6 = 0;
210 bm->dev.dev->settings.flags |= DEV_OPT_IPV6;
211 }
212
213 ret = device_claim(&bm->dev);
214 if (ret < 0)
215 goto error;
216
217 ret = system_bridge_addif(&bst->dev, bm->dev.dev);
218 if (ret < 0) {
219 D(DEVICE, "Bridge device %s could not be added\n", bm->dev.dev->ifname);
220 goto error;
221 }
222
223 device_set_present(&bst->dev, true);
224 device_broadcast_event(&bst->dev, DEV_EVENT_TOPO_CHANGE);
225
226 return 0;
227
228 error:
229 bst->n_failed++;
230 bm->present = false;
231 bst->n_present--;
232 device_release(&bm->dev);
233
234 return ret;
235 }
236
237 static void
238 bridge_remove_member(struct bridge_member *bm)
239 {
240 struct bridge_state *bst = bm->bst;
241
242 if (!bm->present)
243 return;
244
245 if (bst->dev.active)
246 bridge_disable_member(bm);
247
248 bm->present = false;
249 bm->bst->n_present--;
250
251 if (bm == bst->primary_port)
252 bridge_reset_primary(bst);
253
254 if (bst->config.bridge_empty)
255 return;
256
257 bst->force_active = false;
258 if (bst->n_present == 0)
259 device_set_present(&bst->dev, false);
260 }
261
262 static void
263 bridge_free_member(struct bridge_member *bm)
264 {
265 struct device *dev = bm->dev.dev;
266
267 bridge_remove_member(bm);
268 device_remove_user(&bm->dev);
269
270 /*
271 * When reloading the config and moving a device from one bridge to
272 * another, the other bridge may have tried to claim this device
273 * before it was removed here.
274 * Ensure that claiming the device is retried by toggling its present
275 * state
276 */
277 if (dev->present) {
278 device_set_present(dev, false);
279 device_set_present(dev, true);
280 }
281
282 free(bm);
283 }
284
285 static void
286 bridge_check_retry(struct bridge_state *bst)
287 {
288 if (!bst->n_failed)
289 return;
290
291 uloop_timeout_set(&bst->retry, 100);
292 }
293
294 static void
295 bridge_member_cb(struct device_user *dev, enum device_event ev)
296 {
297 struct bridge_member *bm = container_of(dev, struct bridge_member, dev);
298 struct bridge_state *bst = bm->bst;
299
300 switch (ev) {
301 case DEV_EVENT_ADD:
302 assert(!bm->present);
303
304 bm->present = true;
305 bst->n_present++;
306
307 if (bst->n_present == 1)
308 device_set_present(&bst->dev, true);
309 if (bst->dev.active && !bridge_enable_member(bm)) {
310 /*
311 * Adding a bridge member can overwrite the bridge mtu
312 * in the kernel, apply the bridge settings in case the
313 * bridge mtu is set
314 */
315 system_if_apply_settings(&bst->dev, &bst->dev.settings,
316 DEV_OPT_MTU | DEV_OPT_MTU6);
317 }
318
319 break;
320 case DEV_EVENT_REMOVE:
321 if (dev->hotplug) {
322 vlist_delete(&bst->members, &bm->node);
323 return;
324 }
325
326 if (bm->present)
327 bridge_remove_member(bm);
328
329 break;
330 default:
331 return;
332 }
333 }
334
335 static int
336 bridge_set_down(struct bridge_state *bst)
337 {
338 struct bridge_member *bm;
339
340 bst->set_state(&bst->dev, false);
341
342 vlist_for_each_element(&bst->members, bm, node)
343 bridge_disable_member(bm);
344
345 bridge_disable_interface(bst);
346
347 return 0;
348 }
349
350 static int
351 bridge_set_up(struct bridge_state *bst)
352 {
353 struct bridge_member *bm;
354 int ret;
355
356 if (!bst->n_present) {
357 if (!bst->force_active)
358 return -ENOENT;
359
360 ret = bridge_enable_interface(bst);
361 if (ret)
362 return ret;
363 }
364
365 bst->n_failed = 0;
366 vlist_for_each_element(&bst->members, bm, node)
367 bridge_enable_member(bm);
368 bridge_check_retry(bst);
369
370 if (!bst->force_active && !bst->n_present) {
371 /* initialization of all member interfaces failed */
372 bridge_disable_interface(bst);
373 device_set_present(&bst->dev, false);
374 return -ENOENT;
375 }
376
377 bridge_reset_primary(bst);
378 ret = bst->set_state(&bst->dev, true);
379 if (ret < 0)
380 bridge_set_down(bst);
381
382 return ret;
383 }
384
385 static int
386 bridge_set_state(struct device *dev, bool up)
387 {
388 struct bridge_state *bst;
389
390 bst = container_of(dev, struct bridge_state, dev);
391
392 if (up)
393 return bridge_set_up(bst);
394 else
395 return bridge_set_down(bst);
396 }
397
398 static struct bridge_member *
399 bridge_create_member(struct bridge_state *bst, const char *name,
400 struct device *dev, bool hotplug)
401 {
402 struct bridge_member *bm;
403
404 bm = calloc(1, sizeof(*bm) + strlen(name) + 1);
405 if (!bm)
406 return NULL;
407
408 bm->bst = bst;
409 bm->dev.cb = bridge_member_cb;
410 bm->dev.hotplug = hotplug;
411 strcpy(bm->name, name);
412 bm->dev.dev = dev;
413 vlist_add(&bst->members, &bm->node, bm->name);
414 /*
415 * Need to look up the bridge member again as the above
416 * created pointer will be freed in case the bridge member
417 * already existed
418 */
419 bm = vlist_find(&bst->members, name, bm, node);
420 if (hotplug && bm)
421 bm->node.version = -1;
422
423 return bm;
424 }
425
426 static void
427 bridge_member_update(struct vlist_tree *tree, struct vlist_node *node_new,
428 struct vlist_node *node_old)
429 {
430 struct bridge_member *bm;
431 struct device *dev;
432
433 if (node_new) {
434 bm = container_of(node_new, struct bridge_member, node);
435
436 if (node_old) {
437 free(bm);
438 return;
439 }
440
441 dev = bm->dev.dev;
442 bm->dev.dev = NULL;
443 device_add_user(&bm->dev, dev);
444 }
445
446
447 if (node_old) {
448 bm = container_of(node_old, struct bridge_member, node);
449 bridge_free_member(bm);
450 }
451 }
452
453
454 static void
455 bridge_add_member(struct bridge_state *bst, const char *name)
456 {
457 struct device *dev;
458
459 dev = device_get(name, true);
460 if (!dev)
461 return;
462
463 bridge_create_member(bst, name, dev, false);
464 }
465
466 static int
467 bridge_hotplug_add(struct device *dev, struct device *member)
468 {
469 struct bridge_state *bst = container_of(dev, struct bridge_state, dev);
470
471 bridge_create_member(bst, member->ifname, member, true);
472
473 return 0;
474 }
475
476 static int
477 bridge_hotplug_del(struct device *dev, struct device *member)
478 {
479 struct bridge_state *bst = container_of(dev, struct bridge_state, dev);
480 struct bridge_member *bm;
481
482 bm = vlist_find(&bst->members, member->ifname, bm, node);
483 if (!bm)
484 return UBUS_STATUS_NOT_FOUND;
485
486 vlist_delete(&bst->members, &bm->node);
487 return 0;
488 }
489
490 static int
491 bridge_hotplug_prepare(struct device *dev)
492 {
493 struct bridge_state *bst;
494
495 bst = container_of(dev, struct bridge_state, dev);
496 bst->force_active = true;
497 device_set_present(&bst->dev, true);
498
499 return 0;
500 }
501
502 static const struct device_hotplug_ops bridge_ops = {
503 .prepare = bridge_hotplug_prepare,
504 .add = bridge_hotplug_add,
505 .del = bridge_hotplug_del
506 };
507
508 static void
509 bridge_free(struct device *dev)
510 {
511 struct bridge_state *bst;
512
513 bst = container_of(dev, struct bridge_state, dev);
514 vlist_flush_all(&bst->members);
515 free(bst->config_data);
516 free(bst);
517 }
518
519 static void
520 bridge_dump_info(struct device *dev, struct blob_buf *b)
521 {
522 struct bridge_state *bst;
523 struct bridge_member *bm;
524 void *list;
525
526 bst = container_of(dev, struct bridge_state, dev);
527
528 system_if_dump_info(dev, b);
529 list = blobmsg_open_array(b, "bridge-members");
530
531 vlist_for_each_element(&bst->members, bm, node) {
532 if (bm->dev.dev->hidden)
533 continue;
534
535 blobmsg_add_string(b, NULL, bm->dev.dev->ifname);
536 }
537
538 blobmsg_close_array(b, list);
539 }
540
541 static void
542 bridge_config_init(struct device *dev)
543 {
544 struct bridge_state *bst;
545 struct blob_attr *cur;
546 int rem;
547
548 bst = container_of(dev, struct bridge_state, dev);
549
550 if (bst->config.bridge_empty) {
551 bst->force_active = true;
552 device_set_present(&bst->dev, true);
553 }
554
555 bst->n_failed = 0;
556 vlist_update(&bst->members);
557 if (bst->ifnames) {
558 blobmsg_for_each_attr(cur, bst->ifnames, rem) {
559 bridge_add_member(bst, blobmsg_data(cur));
560 }
561 }
562 vlist_flush(&bst->members);
563 bridge_check_retry(bst);
564 }
565
566 static void
567 bridge_apply_settings(struct bridge_state *bst, struct blob_attr **tb)
568 {
569 struct bridge_config *cfg = &bst->config;
570 struct blob_attr *cur;
571
572 /* defaults */
573 cfg->stp = false;
574 cfg->forward_delay = 2;
575 cfg->robustness = 2;
576 cfg->query_interval = 12500;
577 cfg->query_response_interval = 1000;
578 cfg->last_member_interval = 100;
579 cfg->hash_max = 512;
580 cfg->bridge_empty = false;
581 cfg->priority = 0x7FFF;
582 cfg->vlan_filtering = false;
583
584 if ((cur = tb[BRIDGE_ATTR_STP]))
585 cfg->stp = blobmsg_get_bool(cur);
586
587 if ((cur = tb[BRIDGE_ATTR_FORWARD_DELAY]))
588 cfg->forward_delay = blobmsg_get_u32(cur);
589
590 if ((cur = tb[BRIDGE_ATTR_PRIORITY]))
591 cfg->priority = blobmsg_get_u32(cur);
592
593 if ((cur = tb[BRIDGE_ATTR_IGMP_SNOOP]))
594 cfg->multicast_querier = cfg->igmp_snoop = blobmsg_get_bool(cur);
595
596 if ((cur = tb[BRIDGE_ATTR_MULTICAST_QUERIER]))
597 cfg->multicast_querier = blobmsg_get_bool(cur);
598
599 if ((cur = tb[BRIDGE_ATTR_HASH_MAX]))
600 cfg->hash_max = blobmsg_get_u32(cur);
601
602 if ((cur = tb[BRIDGE_ATTR_ROBUSTNESS])) {
603 cfg->robustness = blobmsg_get_u32(cur);
604 cfg->flags |= BRIDGE_OPT_ROBUSTNESS;
605 }
606
607 if ((cur = tb[BRIDGE_ATTR_QUERY_INTERVAL])) {
608 cfg->query_interval = blobmsg_get_u32(cur);
609 cfg->flags |= BRIDGE_OPT_QUERY_INTERVAL;
610 }
611
612 if ((cur = tb[BRIDGE_ATTR_QUERY_RESPONSE_INTERVAL])) {
613 cfg->query_response_interval = blobmsg_get_u32(cur);
614 cfg->flags |= BRIDGE_OPT_QUERY_RESPONSE_INTERVAL;
615 }
616
617 if ((cur = tb[BRIDGE_ATTR_LAST_MEMBER_INTERVAL])) {
618 cfg->last_member_interval = blobmsg_get_u32(cur);
619 cfg->flags |= BRIDGE_OPT_LAST_MEMBER_INTERVAL;
620 }
621
622 if ((cur = tb[BRIDGE_ATTR_AGEING_TIME])) {
623 cfg->ageing_time = blobmsg_get_u32(cur);
624 cfg->flags |= BRIDGE_OPT_AGEING_TIME;
625 }
626
627 if ((cur = tb[BRIDGE_ATTR_HELLO_TIME])) {
628 cfg->hello_time = blobmsg_get_u32(cur);
629 cfg->flags |= BRIDGE_OPT_HELLO_TIME;
630 }
631
632 if ((cur = tb[BRIDGE_ATTR_MAX_AGE])) {
633 cfg->max_age = blobmsg_get_u32(cur);
634 cfg->flags |= BRIDGE_OPT_MAX_AGE;
635 }
636
637 if ((cur = tb[BRIDGE_ATTR_BRIDGE_EMPTY]))
638 cfg->bridge_empty = blobmsg_get_bool(cur);
639
640 if ((cur = tb[BRIDGE_ATTR_VLAN_FILTERING]))
641 cfg->vlan_filtering = blobmsg_get_bool(cur);
642 }
643
644 static enum dev_change_type
645 bridge_reload(struct device *dev, struct blob_attr *attr)
646 {
647 struct blob_attr *tb_dev[__DEV_ATTR_MAX];
648 struct blob_attr *tb_br[__BRIDGE_ATTR_MAX];
649 enum dev_change_type ret = DEV_CONFIG_APPLIED;
650 unsigned long diff;
651 struct bridge_state *bst;
652
653 BUILD_BUG_ON(sizeof(diff) < __BRIDGE_ATTR_MAX / 8);
654 BUILD_BUG_ON(sizeof(diff) < __DEV_ATTR_MAX / 8);
655
656 bst = container_of(dev, struct bridge_state, dev);
657 attr = blob_memdup(attr);
658
659 blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, tb_dev,
660 blob_data(attr), blob_len(attr));
661 blobmsg_parse(bridge_attrs, __BRIDGE_ATTR_MAX, tb_br,
662 blob_data(attr), blob_len(attr));
663
664 if (tb_dev[DEV_ATTR_MACADDR])
665 bst->primary_port = NULL;
666
667 bst->ifnames = tb_br[BRIDGE_ATTR_IFNAME];
668 device_init_settings(dev, tb_dev);
669 bridge_apply_settings(bst, tb_br);
670
671 if (bst->config_data) {
672 struct blob_attr *otb_dev[__DEV_ATTR_MAX];
673 struct blob_attr *otb_br[__BRIDGE_ATTR_MAX];
674
675 blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, otb_dev,
676 blob_data(bst->config_data), blob_len(bst->config_data));
677
678 diff = 0;
679 uci_blob_diff(tb_dev, otb_dev, &device_attr_list, &diff);
680 if (diff)
681 ret = DEV_CONFIG_RESTART;
682
683 blobmsg_parse(bridge_attrs, __BRIDGE_ATTR_MAX, otb_br,
684 blob_data(bst->config_data), blob_len(bst->config_data));
685
686 diff = 0;
687 uci_blob_diff(tb_br, otb_br, &bridge_attr_list, &diff);
688 if (diff & ~(1 << BRIDGE_ATTR_IFNAME))
689 ret = DEV_CONFIG_RESTART;
690
691 bridge_config_init(dev);
692 }
693
694 free(bst->config_data);
695 bst->config_data = attr;
696 return ret;
697 }
698
699 static void
700 bridge_retry_members(struct uloop_timeout *timeout)
701 {
702 struct bridge_state *bst = container_of(timeout, struct bridge_state, retry);
703 struct bridge_member *bm;
704
705 bst->n_failed = 0;
706 vlist_for_each_element(&bst->members, bm, node) {
707 if (bm->present)
708 continue;
709
710 if (!bm->dev.dev->present)
711 continue;
712
713 bm->present = true;
714 bst->n_present++;
715 bridge_enable_member(bm);
716 }
717 }
718
719 static struct device *
720 bridge_create(const char *name, struct device_type *devtype,
721 struct blob_attr *attr)
722 {
723 struct bridge_state *bst;
724 struct device *dev = NULL;
725
726 bst = calloc(1, sizeof(*bst));
727 if (!bst)
728 return NULL;
729
730 dev = &bst->dev;
731
732 if (device_init(dev, devtype, name) < 0) {
733 device_cleanup(dev);
734 free(bst);
735 return NULL;
736 }
737
738 dev->config_pending = true;
739 bst->retry.cb = bridge_retry_members;
740
741 bst->set_state = dev->set_state;
742 dev->set_state = bridge_set_state;
743
744 dev->hotplug_ops = &bridge_ops;
745
746 vlist_init(&bst->members, avl_strcmp, bridge_member_update);
747 bst->members.keep_old = true;
748 bridge_reload(dev, attr);
749
750 return dev;
751 }
752
753 static void __init bridge_device_type_init(void)
754 {
755 device_type_add(&bridge_device_type);
756 }