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