]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/networkd-link.c
networkd: avoid segfault
[thirdparty/systemd.git] / src / network / networkd-link.c
CommitLineData
f579559b
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 <netinet/ether.h>
23#include <linux/if.h>
24
25#include "networkd.h"
26#include "libudev-private.h"
27#include "util.h"
28
29int link_new(Manager *manager, struct udev_device *device, Link **ret) {
30 _cleanup_link_free_ Link *link = NULL;
8cd11a0f 31 const char *mac;
602cc437 32 struct ether_addr *mac_addr;
f579559b
TG
33 int r;
34
35 assert(device);
36 assert(ret);
37
38 link = new0(Link, 1);
39 if (!link)
40 return -ENOMEM;
41
0617ffab
TG
42 link->ifindex = udev_device_get_ifindex(device);
43 if (link->ifindex <= 0)
f579559b
TG
44 return -EINVAL;
45
8cd11a0f
TG
46 mac = udev_device_get_sysattr_value(device, "address");
47 if (!mac)
48 return -EINVAL;
49
602cc437
TG
50 mac_addr = ether_aton(mac);
51 if (!mac_addr)
52 return -EINVAL;
53
54 memcpy(&link->mac, mac_addr, sizeof(struct ether_addr));
55
f579559b 56 link->manager = manager;
f882c247 57 link->state = _LINK_STATE_INVALID;
f579559b 58
0617ffab 59 r = hashmap_put(manager->links, &link->ifindex, link);
f579559b
TG
60 if (r < 0)
61 return r;
62
63 *ret = link;
64 link = NULL;
65
66 return 0;
67}
68
69void link_free(Link *link) {
70 if (!link)
71 return;
72
0617ffab 73 assert(link->manager);
f579559b 74
0617ffab 75 hashmap_remove(link->manager->links, &link->ifindex);
f579559b
TG
76
77 free(link);
78}
79
80int link_add(Manager *m, struct udev_device *device) {
81 Link *link;
82 Network *network;
83 int r;
84 uint64_t ifindex;
85
86 assert(m);
87 assert(device);
88
89 ifindex = udev_device_get_ifindex(device);
90 link = hashmap_get(m->links, &ifindex);
91 if (link)
92 return 0;
93
94 r = link_new(m, device, &link);
95 if (r < 0) {
96 log_error("could not create link: %s", strerror(-r));
97 return r;
98 }
99
100 r = network_get(m, device, &network);
101 if (r < 0)
102 return r == -ENOENT ? 0 : r;
103
104 r = network_apply(m, network, link);
105 if (r < 0)
106 return r;
107
108 return 0;
109}
110
f882c247
TG
111static int link_enter_configured(Link *link) {
112 log_info("Link configured successfully.");
113
114 link->state = LINK_STATE_CONFIGURED;
115
116 return 0;
117}
118
119static int link_enter_failed(Link *link) {
120 log_warning("Could not configure link.");
121
122 link->state = LINK_STATE_FAILED;
123
124 return 0;
125}
126
127static bool link_is_up(Link *link) {
128 return link->flags & IFF_UP;
129}
130
131static int link_enter_routes_set(Link *link) {
0617ffab 132 log_info("Routes set for link %u", (unsigned)link->ifindex);
f882c247
TG
133
134 if (link_is_up(link))
135 return link_enter_configured(link);
136
137 link->state = LINK_STATE_ROUTES_SET;
138
139 return 0;
140}
141
142static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
143 Link *link = userdata;
144 int r;
145
146 assert(link->rtnl_messages > 0);
147 assert(link->state == LINK_STATE_SET_ROUTES || link->state == LINK_STATE_FAILED);
148
149 link->rtnl_messages --;
150
151 if (link->state == LINK_STATE_FAILED)
152 return 1;
153
154 r = sd_rtnl_message_get_errno(m);
155 if (r < 0 && r != -EEXIST) {
0617ffab
TG
156 log_warning("Could not set route on interface %u: %s",
157 (unsigned)link->ifindex, strerror(-r));
f882c247
TG
158 return link_enter_failed(link);
159 }
160
161 if (link->rtnl_messages == 0)
162 return link_enter_routes_set(link);
163
164 return 1;
165}
166
167static int link_enter_set_routes(Link *link) {
168 Route *route;
169 int r;
170
171 assert(link);
172 assert(link->network);
173 assert(link->rtnl_messages == 0);
174 assert(link->state == LINK_STATE_ADDRESSES_SET);
175
176 link->state = LINK_STATE_SET_ROUTES;
177
178 if (!link->network->routes)
179 return link_enter_routes_set(link);
180
181 LIST_FOREACH(routes, route, link->network->routes) {
182 r = route_configure(route, link, &route_handler);
183 if (r < 0)
184 link_enter_failed(link);
185 }
186
187 return 0;
188}
189
190static int link_enter_addresses_set(Link *link) {
0617ffab 191 log_info("Addresses set for link %u", (unsigned)link->ifindex);
f882c247
TG
192
193 link->state = LINK_STATE_ADDRESSES_SET;
194
195 return link_enter_set_routes(link);
196}
197
198static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
199 Link *link = userdata;
200 int r;
201
202 assert(link->rtnl_messages > 0);
203 assert(link->state == LINK_STATE_SET_ADDRESSES || link->state == LINK_STATE_FAILED);
204
205 link->rtnl_messages --;
206
207 if (link->state == LINK_STATE_FAILED)
208 return 1;
209
210 r = sd_rtnl_message_get_errno(m);
211 if (r < 0 && r != -EEXIST) {
0617ffab
TG
212 log_warning("Could not set address on interface %u: %s",
213 (unsigned)link->ifindex, strerror(-r));
f882c247
TG
214 link_enter_failed(link);
215 }
216
217 if (link->rtnl_messages == 0)
218 link_enter_addresses_set(link);
219
220 return 1;
221}
222
223static int link_enter_set_addresses(Link *link) {
224 Address *address;
225 int r;
226
227 assert(link);
228 assert(link->network);
229 assert(link->rtnl_messages == 0);
230
231 if (!link->network->addresses)
232 return link_enter_addresses_set(link);
233
234 link->state = LINK_STATE_SET_ADDRESSES;
235
236 LIST_FOREACH(addresses, address, link->network->addresses) {
237 r = address_configure(address, link, &address_handler);
238 if (r < 0)
239 link_enter_failed(link);
240 }
241
242 return 0;
243}
244
245static int link_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
246 Link *link = userdata;
247 int r;
248
249 r = sd_rtnl_message_get_errno(m);
250 if (r < 0) {
0617ffab
TG
251 log_warning("Could not bring up interface %u: %s",
252 (unsigned)link->ifindex, strerror(-r));
f882c247
TG
253 return link_enter_failed(link);
254 }
255
256 link->flags |= IFF_UP;
257
258 log_info("Link is UP.");
259
260 if (link->state == LINK_STATE_ROUTES_SET)
261 return link_enter_configured(link);
262
263 return 1;
264}
265
266static int link_up(Link *link) {
f579559b
TG
267 _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
268 int r;
269
f882c247
TG
270 assert(link);
271 assert(link->manager);
272 assert(link->manager->rtnl);
273
f579559b
TG
274 r = sd_rtnl_message_link_new(RTM_NEWLINK, link->ifindex, 0, IFF_UP, &req);
275 if (r < 0) {
276 log_error("Could not allocate RTM_NEWLINK message");
277 return r;
278 }
279
f882c247 280 r = sd_rtnl_call_async(link->manager->rtnl, req, link_handler, link, 0, NULL);
f579559b 281 if (r < 0) {
f882c247 282 log_error("Could not send rtnetlink message: %s", strerror(-r));
f579559b
TG
283 return r;
284 }
285
f882c247
TG
286 return 0;
287}
288
289int link_configure(Link *link) {
290 int r;
291
292 r = link_up(link);
293 if (r < 0)
294 return link_enter_failed(link);
295
296 r = link_enter_set_addresses(link);
297 if (r < 0)
298 return link_enter_failed(link);
f579559b
TG
299
300 return 0;
301}