fix handling duplicate IFS_DOWN events
[project/netifd.git] / bridge.c
1 #include <string.h>
2 #include <stdlib.h>
3 #include <stdio.h>
4 #include <assert.h>
5 #include <errno.h>
6
7 #include "netifd.h"
8 #include "system.h"
9
10 struct bridge_state {
11         struct device dev;
12         device_state_cb set_state;
13
14         bool active;
15
16         struct list_head members;
17         int n_present;
18 };
19
20 struct bridge_member {
21         struct list_head list;
22         struct bridge_state *bst;
23         struct device_user dev;
24         bool present;
25 };
26
27 static int
28 bridge_disable_member(struct bridge_member *bm)
29 {
30         struct bridge_state *bst = bm->bst;
31
32         if (!bm->present)
33                 return 0;
34
35         system_bridge_delif(&bst->dev, bm->dev.dev);
36         release_device(bm->dev.dev);
37
38         return 0;
39 }
40
41 static int
42 bridge_enable_member(struct bridge_member *bm)
43 {
44         struct bridge_state *bst = bm->bst;
45         int ret;
46
47         if (!bm->present)
48                 return 0;
49
50         ret = claim_device(bm->dev.dev);
51         if (ret < 0)
52                 goto error;
53
54         ret = system_bridge_addif(&bst->dev, bm->dev.dev);
55         if (ret < 0)
56                 goto error;
57
58         return 0;
59
60 error:
61         bm->present = false;
62         bst->n_present--;
63         return ret;
64 }
65
66 static void
67 bridge_member_cb(struct device_user *dev, enum device_event ev)
68 {
69         struct bridge_member *bm = container_of(dev, struct bridge_member, dev);
70         struct bridge_state *bst = bm->bst;
71
72         switch (ev) {
73         case DEV_EVENT_ADD:
74                 assert(!bm->present);
75
76                 bm->present = true;
77                 bst->n_present++;
78
79                 if (bst->dev.active)
80                         bridge_enable_member(bm);
81                 else if (bst->n_present == 1)
82                         set_device_present(&bst->dev, true);
83
84                 break;
85         case DEV_EVENT_REMOVE:
86                 if (!bm->present)
87                         return;
88
89                 if (bst->dev.active)
90                         bridge_disable_member(bm);
91
92                 bm->present = false;
93                 bm->bst->n_present--;
94                 if (bst->n_present == 0)
95                         set_device_present(&bst->dev, false);
96
97                 break;
98         default:
99                 return;
100         }
101 }
102
103 static int
104 bridge_set_down(struct bridge_state *bst)
105 {
106         struct bridge_member *bm;
107
108         bst->set_state(&bst->dev, false);
109
110         list_for_each_entry(bm, &bst->members, list)
111                 bridge_disable_member(bm);
112
113         system_bridge_delbr(&bst->dev);
114
115         return 0;
116 }
117
118 static int
119 bridge_set_up(struct bridge_state *bst)
120 {
121         struct bridge_member *bm;
122         int ret;
123
124         if (!bst->n_present)
125                 return -ENOENT;
126
127         ret = system_bridge_addbr(&bst->dev);
128         if (ret < 0)
129                 goto out;
130
131         list_for_each_entry(bm, &bst->members, list)
132                 bridge_enable_member(bm);
133
134         if (!bst->n_present) {
135                 /* initialization of all member interfaces failed */
136                 system_bridge_delbr(&bst->dev);
137                 set_device_present(&bst->dev, false);
138                 return -ENOENT;
139         }
140
141         ret = bst->set_state(&bst->dev, true);
142         if (ret < 0)
143                 bridge_set_down(bst);
144
145 out:
146         return ret;
147 }
148
149 static int
150 bridge_set_state(struct device *dev, bool up)
151 {
152         struct bridge_state *bst;
153
154         bst = container_of(dev, struct bridge_state, dev);
155
156         if (up)
157                 return bridge_set_up(bst);
158         else
159                 return bridge_set_down(bst);
160 }
161
162 static struct bridge_member *
163 bridge_create_member(struct bridge_state *bst, struct device *dev)
164 {
165         struct bridge_member *bm;
166
167         bm = calloc(1, sizeof(*bm));
168         bm->bst = bst;
169         bm->dev.cb = bridge_member_cb;
170         add_device_user(&bm->dev, dev);
171
172         list_add(&bm->list, &bst->members);
173
174         if (bst->dev.active)
175                 bridge_enable_member(bm);
176
177         return bm;
178 }
179
180 static void
181 bridge_free_member(struct bridge_member *bm)
182 {
183         if (bm->present) {
184                 bridge_member_cb(&bm->dev, DEV_EVENT_REMOVE);
185                 bm->bst->n_present--;
186                 if (bm->bst->dev.active)
187                         bridge_disable_member(bm);
188         }
189
190         list_del(&bm->list);
191         remove_device_user(&bm->dev);
192         free(bm);
193 }
194
195 static void
196 bridge_add_member(struct bridge_state *bst, const char *name)
197 {
198         struct device *dev;
199
200         dev = get_device(name, true);
201         if (!dev)
202                 return;
203
204         bridge_create_member(bst, dev);
205 }
206
207 static int
208 bridge_hotplug_add(struct device *dev, struct device *member)
209 {
210         struct bridge_state *bst = container_of(dev, struct bridge_state, dev);
211
212         bridge_create_member(bst, member);
213
214         return 0;
215 }
216
217 static int
218 bridge_hotplug_del(struct device *dev, struct device *member)
219 {
220         struct bridge_state *bst = container_of(dev, struct bridge_state, dev);
221         struct bridge_member *bm;
222
223         list_for_each_entry(bm, &bst->members, list) {
224                 if (bm->dev.dev != member)
225                         continue;
226
227                 bridge_free_member(bm);
228                 return 0;
229         }
230
231         return -ENOENT;
232 }
233
234 static const struct device_hotplug_ops bridge_ops = {
235         .add = bridge_hotplug_add,
236         .del = bridge_hotplug_del
237 };
238
239 static void
240 bridge_parse_config(struct bridge_state *bst, struct uci_section *s)
241 {
242         struct uci_element *e;
243         struct uci_option *o;
244         char buf[IFNAMSIZ + 1];
245         char *p, *end;
246         int len;
247
248         o = uci_lookup_option(uci_ctx, s, "ifname");
249         if (!o)
250                 return;
251
252         if (o->type == UCI_TYPE_LIST) {
253                 uci_foreach_element(&o->v.list, e)
254                         bridge_add_member(bst, e->name);
255         } else {
256                 p = o->v.string;
257                 do {
258                         if (!*p)
259                                 break;
260
261                         if (*p == ' ')
262                                 continue;
263
264                         end = strchr(p, ' ');
265                         if (!end) {
266                                 bridge_add_member(bst, p);
267                                 break;
268                         }
269
270                         len = end - p;
271                         if (len <= IFNAMSIZ) {
272                                 memcpy(buf, p, len);
273                                 buf[len] = 0;
274                                 bridge_add_member(bst, buf);
275                         }
276                         p = end;
277                 } while (p++);
278         }
279 }
280
281 static void
282 bridge_free(struct device *dev)
283 {
284         struct bridge_state *bst;
285         struct bridge_member *bm;
286
287         bst = container_of(dev, struct bridge_state, dev);
288         while (!list_empty(&bst->members)) {
289                 bm = list_first_entry(&bst->members, struct bridge_member, list);
290                 bridge_free_member(bm);
291         }
292         free(bst);
293 }
294
295 static void
296 bridge_dump_status(struct device *dev, struct blob_buf *b)
297 {
298         struct bridge_state *bst;
299         struct bridge_member *bm;
300         void *list;
301
302         bst = container_of(dev, struct bridge_state, dev);
303
304         list = blobmsg_open_array(b, "bridge-members");
305         list_for_each_entry(bm, &bst->members, list) {
306                 blobmsg_add_string(b, NULL, bm->dev.dev->ifname);
307         }
308         blobmsg_close_array(b, list);
309 }
310
311 struct device *
312 bridge_create(const char *name, struct uci_section *s)
313 {
314         static const struct device_type bridge_type = {
315                 .name = "Bridge",
316                 .free = bridge_free,
317                 .dump_status = bridge_dump_status,
318         };
319         struct bridge_state *bst;
320         struct device *dev;
321
322         dev = get_device(name, false);
323         if (dev)
324                 return NULL;
325
326         bst = calloc(1, sizeof(*bst));
327         if (!bst)
328                 return NULL;
329
330         init_device(&bst->dev, &bridge_type, name);
331
332         bst->set_state = bst->dev.set_state;
333         bst->dev.set_state = bridge_set_state;
334
335         bst->dev.hotplug_ops = &bridge_ops;
336
337         INIT_LIST_HEAD(&bst->members);
338
339         if (s)
340                 bridge_parse_config(bst, s);
341
342         return &bst->dev;
343 }
344
345 int
346 interface_attach_bridge(struct interface *iface, struct uci_section *s)
347 {
348         struct device *dev;
349         char brname[IFNAMSIZ];
350
351         snprintf(brname, IFNAMSIZ - 1, "br-%s", iface->name);
352         brname[IFNAMSIZ - 1] = 0;
353
354         dev = bridge_create(brname, s);
355         if (!dev)
356                 return -1;
357
358         add_device_user(&iface->main_dev, dev);
359         return 0;
360 }