chiark / gitweb /
networkd: add bridge support
[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, 0, 0, &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(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                 return bridge_enter_failed(bridge);
136         }
137
138         if (bridge->link)
139                 return bridge_enter_ready(bridge);
140
141         bridge->state = BRIDGE_STATE_CREATED;
142
143         return 0;
144 }
145
146 static int bridge_create(Bridge *bridge) {
147         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
148         int r;
149
150         assert(bridge);
151         assert(bridge->state == _BRIDGE_STATE_INVALID);
152         assert(bridge->name);
153         assert(bridge->manager);
154         assert(bridge->manager->rtnl);
155
156         r = sd_rtnl_message_link_new(RTM_NEWLINK, 0, 0, 0, &req);
157         if (r < 0) {
158                 log_error("Could not allocate RTM_NEWLINK message: %s",
159                           strerror(-r));
160                 return r;
161         }
162
163         r = sd_rtnl_message_append(req, IFLA_IFNAME, bridge->name);
164         if (r < 0) {
165                 log_error("Could not append IFLA_IFNAME attribute: %s",
166                           strerror(-r));
167                 return r;
168         }
169
170         r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
171         if (r < 0) {
172                 log_error("Colud not open IFLA_LINKINFO container: %s",
173                           strerror(-r));
174                 return r;
175         }
176
177         r = sd_rtnl_message_append(req, IFLA_INFO_KIND, "bridge");
178         if (r < 0) {
179                 log_error("Could not append IFLA_INFO_KIND attribute: %s",
180                           strerror(-r));
181                 return r;
182         }
183
184         r = sd_rtnl_message_close_container(req);
185         if (r < 0) {
186                 log_error("Could not close IFLA_LINKINFO container %s",
187                           strerror(-r));
188                 return r;
189         }
190
191         r = sd_rtnl_call_async(bridge->manager->rtnl, req, &bridge_create_handler, bridge, 0, NULL);
192         if (r < 0) {
193                 log_error("Could not send rtnetlink message: %s", strerror(-r));
194                 return r;
195         }
196
197         log_info("Creating bridge '%s'", bridge->name);
198
199         bridge->state = BRIDGE_STATE_CREATING;
200
201         return 0;
202 }
203
204 int bridge_join(Bridge *bridge, Link *link, sd_rtnl_message_handler_t callback) {
205         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
206
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         file = fopen(filename, "re");
250         if (!file) {
251                 if (errno == ENOENT)
252                         return 0;
253                 else
254                         return errno;
255         }
256
257         bridge = new0(Bridge, 1);
258         if (!bridge)
259                 return log_oom();
260
261         bridge->manager = manager;
262         bridge->state = _BRIDGE_STATE_INVALID;
263
264         r = config_parse(NULL, filename, file, "Bridge\0", config_item_perf_lookup,
265                         (void*) network_gperf_lookup, false, false, bridge);
266         if (r < 0) {
267                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
268                 return r;
269         } else
270                 log_debug("Parsed configuration file %s", filename);
271
272         if (!bridge->name) {
273                 log_warning("Bridge without Name configured in %s. Ignoring", filename);
274                 return 0;
275         }
276
277         bridge->filename = strdup(filename);
278         if (!bridge->filename)
279                 return log_oom();
280
281         r = hashmap_put(bridge->manager->bridges, bridge->name, bridge);
282         if (r < 0)
283                 return r;
284
285         LIST_HEAD_INIT(bridge->callbacks);
286
287         r = bridge_create(bridge);
288         if (r < 0)
289                 return r;
290
291         bridge = NULL;
292
293         return 0;
294 }
295
296 int bridge_load(Manager *manager) {
297         Bridge *bridge;
298         char **files, **f;
299         int r;
300
301         assert(manager);
302
303         while ((bridge = hashmap_first(manager->bridges)))
304                 bridge_free(bridge);
305
306         r = conf_files_list_strv(&files, ".netdev", NULL, (const char **)manager->network_dirs);
307         if (r < 0) {
308                 log_error("Failed to enumerate netdev files: %s", strerror(-r));
309                 return r;
310         }
311
312         STRV_FOREACH_BACKWARDS(f, files) {
313                 r = bridge_load_one(manager, *f);
314                 if (r < 0)
315                         return r;
316         }
317
318         strv_free(files);
319
320         return 0;
321 }