implement a dump status call for bridge devices
[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 }