]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-bridge.c
rtnl: add link_get_flags
[thirdparty/systemd.git] / src / network / networkd-bridge.c
CommitLineData
02b59d57
TG
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
29void 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
51int 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
72static int bridge_enter_failed(Bridge *bridge) {
73 bridge->state = BRIDGE_STATE_FAILED;
74
75 return 0;
76}
77
78static 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
110static 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
126static 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
146static 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
204int bridge_join(Bridge *bridge, Link *link, sd_rtnl_message_handler_t callback) {
02b59d57
TG
205 if (bridge->state == BRIDGE_STATE_READY) {
206 bridge_join_ready(bridge, link, callback);
207 } else {
208 /* the bridge is not yet read, save this request for when it is*/
209 bridge_join_callback *cb;
210
211 cb = new0(bridge_join_callback, 1);
212 if (!cb)
213 return log_oom();
214
215 cb->callback = callback;
216 cb->link = link;
217
218 LIST_PREPEND(callbacks, bridge->callbacks, cb);
219 }
220
221 return 0;
222}
223
224int bridge_set_link(Manager *m, Link *link) {
225 Bridge *bridge;
226
227 bridge = hashmap_get(m->bridges, link->ifname);
228 if (!bridge)
229 return -ENOENT;
230
231 if (bridge->link && bridge->link != link)
232 return -EEXIST;
233
234 bridge->link = link;
235
236 if (bridge->state == BRIDGE_STATE_CREATED)
237 bridge_enter_ready(bridge);
238
239 return 0;
240}
241
242static int bridge_load_one(Manager *manager, const char *filename) {
243 _cleanup_bridge_free_ Bridge *bridge = NULL;
244 _cleanup_fclose_ FILE *file = NULL;
245 int r;
246
247 file = fopen(filename, "re");
248 if (!file) {
249 if (errno == ENOENT)
250 return 0;
251 else
252 return errno;
253 }
254
255 bridge = new0(Bridge, 1);
256 if (!bridge)
257 return log_oom();
258
259 bridge->manager = manager;
260 bridge->state = _BRIDGE_STATE_INVALID;
261
262 r = config_parse(NULL, filename, file, "Bridge\0", config_item_perf_lookup,
263 (void*) network_gperf_lookup, false, false, bridge);
264 if (r < 0) {
265 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
266 return r;
267 } else
268 log_debug("Parsed configuration file %s", filename);
269
270 if (!bridge->name) {
271 log_warning("Bridge without Name configured in %s. Ignoring", filename);
272 return 0;
273 }
274
275 bridge->filename = strdup(filename);
276 if (!bridge->filename)
277 return log_oom();
278
279 r = hashmap_put(bridge->manager->bridges, bridge->name, bridge);
280 if (r < 0)
281 return r;
282
283 LIST_HEAD_INIT(bridge->callbacks);
284
285 r = bridge_create(bridge);
286 if (r < 0)
287 return r;
288
289 bridge = NULL;
290
291 return 0;
292}
293
294int bridge_load(Manager *manager) {
295 Bridge *bridge;
296 char **files, **f;
297 int r;
298
299 assert(manager);
300
301 while ((bridge = hashmap_first(manager->bridges)))
302 bridge_free(bridge);
303
304 r = conf_files_list_strv(&files, ".netdev", NULL, (const char **)manager->network_dirs);
305 if (r < 0) {
306 log_error("Failed to enumerate netdev files: %s", strerror(-r));
307 return r;
308 }
309
310 STRV_FOREACH_BACKWARDS(f, files) {
311 r = bridge_load_one(manager, *f);
312 if (r < 0)
313 return r;
314 }
315
316 strv_free(files);
317
318 return 0;
319}