chiark / gitweb /
networkd: don't automatically reload config at runtime
[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         bridge = hashmap_get(manager->bridges, name);
59         if (!bridge) {
60                 *ret = NULL;
61                 return -ENOENT;
62         }
63
64         *ret = bridge;
65
66         return 0;
67 }
68
69 static int bridge_enter_failed(Bridge *bridge) {
70         bridge->state = BRIDGE_STATE_FAILED;
71
72         return 0;
73 }
74
75 static int bridge_join_ready(Bridge *bridge, Link* link, sd_rtnl_message_handler_t callback) {
76         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
77         int r;
78
79         assert(bridge);
80         assert(bridge->state == BRIDGE_STATE_READY);
81         assert(link);
82         assert(callback);
83
84         r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
85         if (r < 0) {
86                 log_error_bridge(bridge,
87                                  "Could not allocate RTM_SETLINK message: %s",
88                                  strerror(-r));
89                 return r;
90         }
91
92         r = sd_rtnl_message_append_u32(req, IFLA_MASTER, bridge->link->ifindex);
93         if (r < 0) {
94                 log_error_bridge(bridge,
95                                  "Could not append IFLA_MASTER attribute: %s",
96                                  strerror(-r));
97                 return r;
98         }
99
100         r = sd_rtnl_call_async(bridge->manager->rtnl, req, callback, link, 0, NULL);
101         if (r < 0) {
102                 log_error_bridge(bridge,
103                                  "Could not send rtnetlink message: %s",
104                                  strerror(-r));
105                 return r;
106         }
107
108         log_debug_bridge(bridge, "joining link %s to bridge", link->ifname);
109
110         return 0;
111 }
112
113 static int bridge_enter_ready(Bridge *bridge) {
114         bridge_join_callback *callback;
115
116         bridge->state = BRIDGE_STATE_READY;
117
118         log_info_bridge(bridge, "bridge ready");
119
120         LIST_FOREACH(callbacks, callback, bridge->callbacks) {
121                 /* join the links that were attempted to be joined befor the
122                  * link was ready */
123                 bridge_join_ready(bridge, callback->link, callback->callback);
124         }
125
126         return 0;
127 }
128
129 static int bridge_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
130         Bridge *bridge = userdata;
131         int r;
132
133         assert(bridge->state == BRIDGE_STATE_CREATING);
134
135         r = sd_rtnl_message_get_errno(m);
136         if (r < 0) {
137                 log_warning_bridge(bridge, "bridge failed: %s", strerror(-r));
138                 bridge_enter_failed(bridge);
139
140                 return 1;
141         }
142
143         if (bridge->link)
144                 bridge_enter_ready(bridge);
145         else
146                 bridge->state = BRIDGE_STATE_CREATED;
147
148         return 1;
149 }
150
151 static int bridge_create(Bridge *bridge) {
152         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
153         int r;
154
155         assert(bridge);
156         assert(bridge->state == _BRIDGE_STATE_INVALID);
157         assert(bridge->name);
158         assert(bridge->manager);
159         assert(bridge->manager->rtnl);
160
161         r = sd_rtnl_message_link_new(RTM_NEWLINK, 0, &req);
162         if (r < 0) {
163                 log_error_bridge(bridge,
164                                  "Could not allocate RTM_NEWLINK message: %s",
165                                  strerror(-r));
166                 return r;
167         }
168
169         r = sd_rtnl_message_append_string(req, IFLA_IFNAME, bridge->name);
170         if (r < 0) {
171                 log_error_bridge(bridge,
172                                  "Could not append IFLA_IFNAME attribute: %s",
173                                  strerror(-r));
174                 return r;
175         }
176
177         r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
178         if (r < 0) {
179                 log_error_bridge(bridge,
180                                  "Could not open IFLA_LINKINFO container: %s",
181                                  strerror(-r));
182                 return r;
183         }
184
185         r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, "bridge");
186         if (r < 0) {
187                 log_error_bridge(bridge,
188                                  "Could not append IFLA_INFO_KIND attribute: %s",
189                                  strerror(-r));
190                 return r;
191         }
192
193         r = sd_rtnl_message_close_container(req);
194         if (r < 0) {
195                 log_error_bridge(bridge,
196                                  "Could not close IFLA_LINKINFO container %s",
197                                  strerror(-r));
198                 return r;
199         }
200
201         r = sd_rtnl_call_async(bridge->manager->rtnl, req, &bridge_create_handler, bridge, 0, NULL);
202         if (r < 0) {
203                 log_error_bridge(bridge,
204                                  "Could not send rtnetlink message: %s", strerror(-r));
205                 return r;
206         }
207
208         log_debug_bridge(bridge, "creating bridge");
209
210         bridge->state = BRIDGE_STATE_CREATING;
211
212         return 0;
213 }
214
215 int bridge_join(Bridge *bridge, Link *link, sd_rtnl_message_handler_t callback) {
216         if (bridge->state == BRIDGE_STATE_READY) {
217                 bridge_join_ready(bridge, link, callback);
218         } else {
219                 /* the bridge is not yet read, save this request for when it is*/
220                 bridge_join_callback *cb;
221
222                 cb = new0(bridge_join_callback, 1);
223                 if (!cb)
224                         return log_oom();
225
226                 cb->callback = callback;
227                 cb->link = link;
228
229                 LIST_PREPEND(callbacks, bridge->callbacks, cb);
230         }
231
232         return 0;
233 }
234
235 int bridge_set_link(Manager *m, Link *link) {
236         Bridge *bridge;
237
238         bridge = hashmap_get(m->bridges, link->ifname);
239         if (!bridge)
240                 return -ENOENT;
241
242         if (bridge->link && bridge->link != link)
243                 return -EEXIST;
244
245         bridge->link = link;
246
247         if (bridge->state == BRIDGE_STATE_CREATED)
248                 bridge_enter_ready(bridge);
249
250         return 0;
251 }
252
253 static int bridge_load_one(Manager *manager, const char *filename) {
254         _cleanup_bridge_free_ Bridge *bridge = NULL;
255         _cleanup_fclose_ FILE *file = NULL;
256         int r;
257
258         assert(manager);
259         assert(filename);
260
261         file = fopen(filename, "re");
262         if (!file) {
263                 if (errno == ENOENT)
264                         return 0;
265                 else
266                         return errno;
267         }
268
269         bridge = new0(Bridge, 1);
270         if (!bridge)
271                 return log_oom();
272
273         bridge->manager = manager;
274         bridge->state = _BRIDGE_STATE_INVALID;
275
276         r = config_parse(NULL, filename, file, "Bridge\0", config_item_perf_lookup,
277                         (void*) network_gperf_lookup, false, false, bridge);
278         if (r < 0) {
279                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
280                 return r;
281         }
282
283         if (!bridge->name) {
284                 log_warning("Bridge without Name configured in %s. Ignoring", filename);
285                 return 0;
286         }
287
288         bridge->filename = strdup(filename);
289         if (!bridge->filename)
290                 return log_oom();
291
292         r = hashmap_put(bridge->manager->bridges, bridge->name, bridge);
293         if (r < 0)
294                 return r;
295
296         LIST_HEAD_INIT(bridge->callbacks);
297
298         r = bridge_create(bridge);
299         if (r < 0)
300                 return r;
301
302         bridge = NULL;
303
304         return 0;
305 }
306
307 int bridge_load(Manager *manager) {
308         Bridge *bridge;
309         char **files, **f;
310         int r;
311
312         assert(manager);
313
314         while ((bridge = hashmap_first(manager->bridges)))
315                 bridge_free(bridge);
316
317         r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
318         if (r < 0) {
319                 log_error("Failed to enumerate netdev files: %s", strerror(-r));
320                 return r;
321         }
322
323         STRV_FOREACH_BACKWARDS(f, files) {
324                 r = bridge_load_one(manager, *f);
325                 if (r < 0)
326                         return r;
327         }
328
329         strv_free(files);
330
331         return 0;
332 }