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