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