]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-bridge.c
rtnl: replace message_append by typesafe versions
[thirdparty/systemd.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
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 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 }