add config state tracking
[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