chiark / gitweb /
d644bd5f55746e89048980c15f83a20b6039997d
[elogind.git] / src / network / networkd-bridge.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Tom Gundersen <teg@jklm.no>
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include "networkd.h"
23 #include "net-util.h"
24 #include "path-util.h"
25 #include "conf-files.h"
26 #include "conf-parser.h"
27 #include "list.h"
28
29 void bridge_free(Bridge *bridge) {
30         bridge_join_callback *callback;
31
32         if (!bridge)
33                 return;
34
35         while ((callback = bridge->callbacks)) {
36                 LIST_REMOVE(callbacks, bridge->callbacks, callback);
37                 free(callback);
38         }
39
40         if (bridge->name)
41                 hashmap_remove(bridge->manager->bridges, bridge->name);
42
43         free(bridge->filename);
44
45         free(bridge->description);
46         free(bridge->name);
47
48         free(bridge);
49 }
50
51 int bridge_get(Manager *manager, const char *name, Bridge **ret) {
52         Bridge *bridge;
53
54         assert(manager);
55         assert(name);
56         assert(ret);
57
58         if (manager_should_reload(manager))
59                 manager_load_config(manager);
60
61         bridge = hashmap_get(manager->bridges, name);
62         if (!bridge) {
63                 *ret = NULL;
64                 return -ENOENT;
65         }
66
67         *ret = bridge;
68
69         return 0;
70 }
71
72 static int bridge_enter_failed(Bridge *bridge) {
73         bridge->state = BRIDGE_STATE_FAILED;
74
75         return 0;
76 }
77
78 static int bridge_join_ready(Bridge *bridge, Link* link, sd_rtnl_message_handler_t callback) {
79         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
80         int r;
81
82         assert(bridge);
83         assert(bridge->state == BRIDGE_STATE_READY);
84         assert(link);
85         assert(callback);
86
87         r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
88         if (r < 0) {
89                 log_error_bridge(bridge,
90                                  "Could not allocate RTM_SETLINK message: %s",
91                                  strerror(-r));
92                 return r;
93         }
94
95         r = sd_rtnl_message_append_u32(req, IFLA_MASTER, bridge->link->ifindex);
96         if (r < 0) {
97                 log_error_bridge(bridge,
98                                  "Could not append IFLA_MASTER attribute: %s",
99                                  strerror(-r));
100                 return r;
101         }
102
103         r = sd_rtnl_call_async(bridge->manager->rtnl, req, callback, link, 0, NULL);
104         if (r < 0) {
105                 log_error_bridge(bridge,
106                                  "Could not send rtnetlink message: %s",
107                                  strerror(-r));
108                 return r;
109         }
110
111         return 0;
112 }
113
114 static int bridge_enter_ready(Bridge *bridge) {
115         bridge_join_callback *callback;
116
117         bridge->state = BRIDGE_STATE_READY;
118
119         log_bridge_info(bridge, "bridge ready");
120
121         LIST_FOREACH(callbacks, callback, bridge->callbacks) {
122                 /* join the links that were attempted to be joined befor the
123                  * link was ready */
124                 bridge_join_ready(bridge, callback->link, callback->callback);
125         }
126
127         return 0;
128 }
129
130 static int bridge_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
131         Bridge *bridge = userdata;
132         int r;
133
134         assert(bridge->state == BRIDGE_STATE_CREATING);
135
136         r = sd_rtnl_message_get_errno(m);
137         if (r < 0) {
138                 log_warning_bridge(bridge, "bridge failed: %s", strerror(-r));
139                 bridge_enter_failed(bridge);
140
141                 return 1;
142         }
143
144         if (bridge->link)
145                 bridge_enter_ready(bridge);
146         else
147                 bridge->state = BRIDGE_STATE_CREATED;
148
149         return 1;
150 }
151
152 static int bridge_create(Bridge *bridge) {
153         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
154         int r;
155
156         assert(bridge);
157         assert(bridge->state == _BRIDGE_STATE_INVALID);
158         assert(bridge->name);
159         assert(bridge->manager);
160         assert(bridge->manager->rtnl);
161
162         r = sd_rtnl_message_link_new(RTM_NEWLINK, 0, &req);
163         if (r < 0) {
164                 log_error_bridge(bridge,
165                                  "Could not allocate RTM_NEWLINK message: %s",
166                                  strerror(-r));
167                 return r;
168         }
169
170         r = sd_rtnl_message_append_string(req, IFLA_IFNAME, bridge->name);
171         if (r < 0) {
172                 log_error_bridge(bridge,
173                                  "Could not append IFLA_IFNAME attribute: %s",
174                                  strerror(-r));
175                 return r;
176         }
177
178         r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
179         if (r < 0) {
180                 log_error_bridge(bridge,
181                                  "Could not open IFLA_LINKINFO container: %s",
182                                  strerror(-r));
183                 return r;
184         }
185
186         r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, "bridge");
187         if (r < 0) {
188                 log_error_bridge(bridge,
189                                  "Could not append IFLA_INFO_KIND attribute: %s",
190                                  strerror(-r));
191                 return r;
192         }
193
194         r = sd_rtnl_message_close_container(req);
195         if (r < 0) {
196                 log_error_bridge(bridge,
197                                  "Could not close IFLA_LINKINFO container %s",
198                                  strerror(-r));
199                 return r;
200         }
201
202         r = sd_rtnl_call_async(bridge->manager->rtnl, req, &bridge_create_handler, bridge, 0, NULL);
203         if (r < 0) {
204                 log_error_bridge(bridge,
205                                  "Could not send rtnetlink message: %s", strerror(-r));
206                 return r;
207         }
208
209         log_bridge_debug(bridge, "creating bridge");
210
211         bridge->state = BRIDGE_STATE_CREATING;
212
213         return 0;
214 }
215
216 int bridge_join(Bridge *bridge, Link *link, sd_rtnl_message_handler_t callback) {
217         if (bridge->state == BRIDGE_STATE_READY) {
218                 bridge_join_ready(bridge, link, callback);
219         } else {
220                 /* the bridge is not yet read, save this request for when it is*/
221                 bridge_join_callback *cb;
222
223                 cb = new0(bridge_join_callback, 1);
224                 if (!cb)
225                         return log_oom();
226
227                 cb->callback = callback;
228                 cb->link = link;
229
230                 LIST_PREPEND(callbacks, bridge->callbacks, cb);
231         }
232
233         return 0;
234 }
235
236 int bridge_set_link(Manager *m, Link *link) {
237         Bridge *bridge;
238
239         bridge = hashmap_get(m->bridges, link->ifname);
240         if (!bridge)
241                 return -ENOENT;
242
243         if (bridge->link && bridge->link != link)
244                 return -EEXIST;
245
246         bridge->link = link;
247
248         if (bridge->state == BRIDGE_STATE_CREATED)
249                 bridge_enter_ready(bridge);
250
251         return 0;
252 }
253
254 static int bridge_load_one(Manager *manager, const char *filename) {
255         _cleanup_bridge_free_ Bridge *bridge = NULL;
256         _cleanup_fclose_ FILE *file = NULL;
257         int r;
258
259         assert(manager);
260         assert(filename);
261
262         file = fopen(filename, "re");
263         if (!file) {
264                 if (errno == ENOENT)
265                         return 0;
266                 else
267                         return errno;
268         }
269
270         bridge = new0(Bridge, 1);
271         if (!bridge)
272                 return log_oom();
273
274         bridge->manager = manager;
275         bridge->state = _BRIDGE_STATE_INVALID;
276
277         r = config_parse(NULL, filename, file, "Bridge\0", config_item_perf_lookup,
278                         (void*) network_gperf_lookup, false, false, bridge);
279         if (r < 0) {
280                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
281                 return r;
282         }
283
284         if (!bridge->name) {
285                 log_warning("Bridge without Name configured in %s. Ignoring", filename);
286                 return 0;
287         }
288
289         bridge->filename = strdup(filename);
290         if (!bridge->filename)
291                 return log_oom();
292
293         r = hashmap_put(bridge->manager->bridges, bridge->name, bridge);
294         if (r < 0)
295                 return r;
296
297         LIST_HEAD_INIT(bridge->callbacks);
298
299         r = bridge_create(bridge);
300         if (r < 0)
301                 return r;
302
303         bridge = NULL;
304
305         return 0;
306 }
307
308 int bridge_load(Manager *manager) {
309         Bridge *bridge;
310         char **files, **f;
311         int r;
312
313         assert(manager);
314
315         while ((bridge = hashmap_first(manager->bridges)))
316                 bridge_free(bridge);
317
318         r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
319         if (r < 0) {
320                 log_error("Failed to enumerate netdev files: %s", strerror(-r));
321                 return r;
322         }
323
324         STRV_FOREACH_BACKWARDS(f, files) {
325                 r = bridge_load_one(manager, *f);
326                 if (r < 0)
327                         return r;
328         }
329
330         strv_free(files);
331
332         return 0;
333 }