bool active;
bool force_active;
+ struct uloop_timeout retry;
struct bridge_member *primary_port;
struct vlist_tree members;
int n_present;
+ int n_failed;
};
struct bridge_member {
return 0;
}
+static int
+bridge_enable_interface(struct bridge_state *bst)
+{
+ int ret;
+
+ if (bst->active)
+ return 0;
+
+ ret = system_bridge_addbr(&bst->dev, &bst->config);
+ if (ret < 0)
+ return ret;
+
+ bst->active = true;
+ return 0;
+}
+
+static void
+bridge_disable_interface(struct bridge_state *bst)
+{
+ if (!bst->active)
+ return;
+
+ system_bridge_delbr(&bst->dev);
+ bst->active = false;
+}
+
static int
bridge_enable_member(struct bridge_member *bm)
{
if (!bm->present)
return 0;
+ ret = bridge_enable_interface(bst);
+ if (ret)
+ goto error;
+
+ /* Disable IPv6 for bridge members */
+ if (!(bm->dev.dev->settings.flags & DEV_OPT_IPV6)) {
+ bm->dev.dev->settings.ipv6 = 0;
+ bm->dev.dev->settings.flags |= DEV_OPT_IPV6;
+ }
+
ret = device_claim(&bm->dev);
if (ret < 0)
goto error;
goto error;
}
+ device_set_present(&bst->dev, true);
device_broadcast_event(&bst->dev, DEV_EVENT_TOPO_CHANGE);
return 0;
error:
+ bst->n_failed++;
bm->present = false;
bst->n_present--;
+ device_release(&bm->dev);
+
return ret;
}
free(bm);
}
+static void
+bridge_check_retry(struct bridge_state *bst)
+{
+ if (!bst->n_failed)
+ return;
+
+ uloop_timeout_set(&bst->retry, 100);
+}
+
static void
bridge_member_cb(struct device_user *dev, enum device_event ev)
{
vlist_for_each_element(&bst->members, bm, node)
bridge_disable_member(bm);
- system_bridge_delbr(&bst->dev);
+ bridge_disable_interface(bst);
return 0;
}
struct bridge_member *bm;
int ret;
- if (!bst->force_active && !bst->n_present)
- return -ENOENT;
+ if (!bst->n_present) {
+ if (!bst->force_active)
+ return -ENOENT;
- ret = system_bridge_addbr(&bst->dev, &bst->config);
- if (ret < 0)
- goto out;
+ ret = bridge_enable_interface(bst);
+ if (ret)
+ return ret;
+ }
+ bst->n_failed = 0;
vlist_for_each_element(&bst->members, bm, node)
bridge_enable_member(bm);
+ bridge_check_retry(bst);
if (!bst->force_active && !bst->n_present) {
/* initialization of all member interfaces failed */
- system_bridge_delbr(&bst->dev);
+ bridge_disable_interface(bst);
device_set_present(&bst->dev, false);
return -ENOENT;
}
if (ret < 0)
bridge_set_down(bst);
-out:
return ret;
}
strcpy(bm->name, dev->ifname);
bm->dev.dev = dev;
vlist_add(&bst->members, &bm->node, bm->name);
- if (hotplug)
+ // Need to look up the bridge member again as the above
+ // created pointer will be freed in case the bridge member
+ // already existed
+ bm = vlist_find(&bst->members, dev->ifname, bm, node);
+ if (hotplug && bm)
bm->node.version = -1;
return bm;
device_set_present(&bst->dev, true);
}
+ bst->n_failed = 0;
vlist_update(&bst->members);
if (bst->ifnames) {
blobmsg_for_each_attr(cur, bst->ifnames, rem) {
}
}
vlist_flush(&bst->members);
+ bridge_check_retry(bst);
}
static void
/* defaults */
cfg->stp = false;
cfg->forward_delay = 2;
- cfg->igmp_snoop = false;
+ cfg->igmp_snoop = true;
cfg->bridge_empty = false;
cfg->priority = 0x7FFF;
return ret;
}
+static void
+bridge_retry_members(struct uloop_timeout *timeout)
+{
+ struct bridge_state *bst = container_of(timeout, struct bridge_state, retry);
+ struct bridge_member *bm;
+
+ bst->n_failed = 0;
+ vlist_for_each_element(&bst->members, bm, node) {
+ if (bm->present)
+ continue;
+
+ if (!bm->dev.dev->present)
+ continue;
+
+ bm->present = true;
+ bst->n_present++;
+ bridge_enable_member(bm);
+ }
+}
+
static struct device *
bridge_create(const char *name, struct blob_attr *attr)
{
dev = &bst->dev;
device_init(dev, &bridge_device_type, name);
dev->config_pending = true;
+ bst->retry.cb = bridge_retry_members;
bst->set_state = dev->set_state;
dev->set_state = bridge_set_state;