chiark / gitweb /
networkd: netdev - rename Netdev to NetDev
[elogind.git] / src / network / networkd-netdev.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 static const char* const netdev_kind_table[] = {
30         [NETDEV_KIND_BRIDGE] = "bridge",
31         [NETDEV_KIND_BOND] = "bond",
32         [NETDEV_KIND_VLAN] = "vlan",
33 };
34
35 DEFINE_STRING_TABLE_LOOKUP(netdev_kind, NetDevKind);
36 DEFINE_CONFIG_PARSE_ENUM(config_parse_netdev_kind, netdev_kind, NetDevKind, "Failed to parse netdev kind");
37
38 void netdev_free(NetDev *netdev) {
39         netdev_enslave_callback *callback;
40
41         if (!netdev)
42                 return;
43
44         while ((callback = netdev->callbacks)) {
45                 LIST_REMOVE(callbacks, netdev->callbacks, callback);
46                 free(callback);
47         }
48
49         if (netdev->name)
50                 hashmap_remove(netdev->manager->netdevs, netdev->name);
51
52         free(netdev->filename);
53
54         free(netdev->description);
55         free(netdev->name);
56
57         free(netdev);
58 }
59
60 int netdev_get(Manager *manager, const char *name, NetDev **ret) {
61         NetDev *netdev;
62
63         assert(manager);
64         assert(name);
65         assert(ret);
66
67         netdev = hashmap_get(manager->netdevs, name);
68         if (!netdev) {
69                 *ret = NULL;
70                 return -ENOENT;
71         }
72
73         *ret = netdev;
74
75         return 0;
76 }
77
78 static int netdev_enter_failed(NetDev *netdev) {
79         netdev->state = NETDEV_STATE_FAILED;
80
81         return 0;
82 }
83
84 static int netdev_enslave_ready(NetDev *netdev, Link* link, sd_rtnl_message_handler_t callback) {
85         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
86         int r;
87
88         assert(netdev);
89         assert(netdev->state == NETDEV_STATE_READY);
90         assert(link);
91         assert(callback);
92
93         r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
94         if (r < 0) {
95                 log_error_netdev(netdev,
96                                  "Could not allocate RTM_SETLINK message: %s",
97                                  strerror(-r));
98                 return r;
99         }
100
101         r = sd_rtnl_message_append_u32(req, IFLA_MASTER, netdev->ifindex);
102         if (r < 0) {
103                 log_error_netdev(netdev,
104                                  "Could not append IFLA_MASTER attribute: %s",
105                                  strerror(-r));
106                 return r;
107         }
108
109         r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
110         if (r < 0) {
111                 log_error_netdev(netdev,
112                                  "Could not send rtnetlink message: %s",
113                                  strerror(-r));
114                 return r;
115         }
116
117         log_debug_netdev(netdev, "enslaving link '%s'", link->ifname);
118
119         return 0;
120 }
121
122 static int netdev_enter_ready(NetDev *netdev) {
123         netdev_enslave_callback *callback;
124
125         assert(netdev);
126         assert(netdev->name);
127
128         netdev->state = NETDEV_STATE_READY;
129
130         log_info_netdev(netdev, "netdev ready");
131
132         LIST_FOREACH(callbacks, callback, netdev->callbacks) {
133                 /* enslave the links that were attempted to be enslaved befor the
134                  * link was ready */
135                 netdev_enslave_ready(netdev, callback->link, callback->callback);
136         }
137
138         return 0;
139 }
140
141 static int netdev_create_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
142         NetDev *netdev = userdata;
143         int r;
144
145         assert(netdev->state != _NETDEV_STATE_INVALID);
146
147         r = sd_rtnl_message_get_errno(m);
148         if (r < 0) {
149                 log_warning_netdev(netdev, "netdev failed: %s", strerror(-r));
150                 netdev_enter_failed(netdev);
151
152                 return 1;
153         }
154
155         return 1;
156 }
157
158 static int netdev_create(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
159         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
160         const char *kind;
161         int r;
162
163         assert(netdev);
164         assert(!(netdev->kind == NETDEV_KIND_VLAN) || (link && callback && netdev->vlanid >= 0));
165         assert(netdev->name);
166         assert(netdev->manager);
167         assert(netdev->manager->rtnl);
168
169         r = sd_rtnl_message_link_new(RTM_NEWLINK, 0, &req);
170         if (r < 0) {
171                 log_error_netdev(netdev,
172                                  "Could not allocate RTM_NEWLINK message: %s",
173                                  strerror(-r));
174                 return r;
175         }
176
177         if (link) {
178                 r = sd_rtnl_message_append_u32(req, IFLA_LINK, link->ifindex);
179                 if (r < 0) {
180                         log_error_netdev(netdev,
181                                          "Could not append IFLA_LINK attribute: %s",
182                                          strerror(-r));
183                         return r;
184                 }
185         }
186
187         r = sd_rtnl_message_append_string(req, IFLA_IFNAME, netdev->name);
188         if (r < 0) {
189                 log_error_netdev(netdev,
190                                  "Could not append IFLA_IFNAME attribute: %s",
191                                  strerror(-r));
192                 return r;
193         }
194
195         r = sd_rtnl_message_open_container(req, IFLA_LINKINFO);
196         if (r < 0) {
197                 log_error_netdev(netdev,
198                                  "Could not open IFLA_LINKINFO container: %s",
199                                  strerror(-r));
200                 return r;
201         }
202
203         kind = netdev_kind_to_string(netdev->kind);
204         if (!kind) {
205                 log_error_netdev(netdev, "Invalid kind");
206                 return -EINVAL;
207         }
208
209         r = sd_rtnl_message_append_string(req, IFLA_INFO_KIND, kind);
210         if (r < 0) {
211                 log_error_netdev(netdev,
212                                  "Could not append IFLA_INFO_KIND attribute: %s",
213                                  strerror(-r));
214                 return r;
215         }
216
217         if (netdev->vlanid >= 0) {
218                 r = sd_rtnl_message_open_container(req, IFLA_INFO_DATA);
219                 if (r < 0) {
220                         log_error_netdev(netdev,
221                                          "Could not open IFLA_INFO_DATA container: %s",
222                                          strerror(-r));
223                         return r;
224                 }
225
226                 r = sd_rtnl_message_append_u16(req, IFLA_VLAN_ID, netdev->vlanid);
227                 if (r < 0) {
228                         log_error_netdev(netdev,
229                                          "Could not append IFLA_VLAN_ID attribute: %s",
230                                          strerror(-r));
231                         return r;
232                 }
233
234                 r = sd_rtnl_message_close_container(req);
235                 if (r < 0) {
236                         log_error_netdev(netdev,
237                                          "Could not close IFLA_INFO_DATA container %s",
238                                          strerror(-r));
239                         return r;
240                 }
241         }
242
243         r = sd_rtnl_message_close_container(req);
244         if (r < 0) {
245                 log_error_netdev(netdev,
246                                  "Could not close IFLA_LINKINFO container %s",
247                                  strerror(-r));
248                 return r;
249         }
250
251         if (link)
252                 r = sd_rtnl_call_async(netdev->manager->rtnl, req, callback, link, 0, NULL);
253         else
254                 r = sd_rtnl_call_async(netdev->manager->rtnl, req, &netdev_create_handler, netdev, 0, NULL);
255         if (r < 0) {
256                 log_error_netdev(netdev,
257                                  "Could not send rtnetlink message: %s", strerror(-r));
258                 return r;
259         }
260
261         log_debug_netdev(netdev, "creating netdev");
262
263         netdev->state = NETDEV_STATE_CREATING;
264
265         return 0;
266 }
267
268 int netdev_enslave(NetDev *netdev, Link *link, sd_rtnl_message_handler_t callback) {
269         if (netdev->kind == NETDEV_KIND_VLAN)
270                 return netdev_create(netdev, link, callback);
271
272         if (netdev->state == NETDEV_STATE_READY) {
273                 netdev_enslave_ready(netdev, link, callback);
274         } else {
275                 /* the netdev is not yet read, save this request for when it is*/
276                 netdev_enslave_callback *cb;
277
278                 cb = new0(netdev_enslave_callback, 1);
279                 if (!cb)
280                         return log_oom();
281
282                 cb->callback = callback;
283                 cb->link = link;
284
285                 LIST_PREPEND(callbacks, netdev->callbacks, cb);
286         }
287
288         return 0;
289 }
290
291 int netdev_set_ifindex(NetDev *netdev, int ifindex) {
292         assert(netdev);
293         assert(ifindex > 0);
294
295         if (netdev->ifindex > 0) {
296                 if (netdev->ifindex == ifindex)
297                         return 0;
298                 else
299                         return -EEXIST;
300         }
301
302         netdev->ifindex = ifindex;
303
304         netdev_enter_ready(netdev);
305
306         return 0;
307 }
308
309 static int netdev_load_one(Manager *manager, const char *filename) {
310         _cleanup_netdev_free_ NetDev *netdev = NULL;
311         _cleanup_fclose_ FILE *file = NULL;
312         int r;
313
314         assert(manager);
315         assert(filename);
316
317         file = fopen(filename, "re");
318         if (!file) {
319                 if (errno == ENOENT)
320                         return 0;
321                 else
322                         return errno;
323         }
324
325         netdev = new0(NetDev, 1);
326         if (!netdev)
327                 return log_oom();
328
329         netdev->manager = manager;
330         netdev->state = _NETDEV_STATE_INVALID;
331         netdev->kind = _NETDEV_KIND_INVALID;
332         netdev->vlanid = -1;
333
334         r = config_parse(NULL, filename, file, "NetDev\0VLAN\0", config_item_perf_lookup,
335                         (void*) network_gperf_lookup, false, false, netdev);
336         if (r < 0) {
337                 log_warning("Could not parse config file %s: %s", filename, strerror(-r));
338                 return r;
339         }
340
341         if (netdev->kind == _NETDEV_KIND_INVALID) {
342                 log_warning("NetDev without Kind configured in %s. Ignoring", filename);
343                 return 0;
344         }
345
346         if (!netdev->name) {
347                 log_warning("NetDev without Name configured in %s. Ignoring", filename);
348                 return 0;
349         }
350
351         if (netdev->kind == NETDEV_KIND_VLAN && netdev->vlanid < 0) {
352                 log_warning("VLAN without Id configured in %s. Ignoring", filename);
353                 return 0;
354         }
355
356         netdev->filename = strdup(filename);
357         if (!netdev->filename)
358                 return log_oom();
359
360         r = hashmap_put(netdev->manager->netdevs, netdev->name, netdev);
361         if (r < 0)
362                 return r;
363
364         LIST_HEAD_INIT(netdev->callbacks);
365
366         if (netdev->kind != NETDEV_KIND_VLAN) {
367                 r = netdev_create(netdev, NULL, NULL);
368                 if (r < 0)
369                         return r;
370         }
371
372         netdev = NULL;
373
374         return 0;
375 }
376
377 int netdev_load(Manager *manager) {
378         NetDev *netdev;
379         char **files, **f;
380         int r;
381
382         assert(manager);
383
384         while ((netdev = hashmap_first(manager->netdevs)))
385                 netdev_free(netdev);
386
387         r = conf_files_list_strv(&files, ".netdev", NULL, network_dirs);
388         if (r < 0) {
389                 log_error("Failed to enumerate netdev files: %s", strerror(-r));
390                 return r;
391         }
392
393         STRV_FOREACH_BACKWARDS(f, files) {
394                 r = netdev_load_one(manager, *f);
395                 if (r < 0)
396                         return r;
397         }
398
399         strv_free(files);
400
401         return 0;
402 }