chiark / gitweb /
networkd: fix NULL pointer deref
[elogind.git] / src / network / networkd-link.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 <netinet/ether.h>
23 #include <linux/if.h>
24
25 #include "networkd.h"
26 #include "libudev-private.h"
27 #include "util.h"
28
29 int link_new(Manager *manager, struct udev_device *device, Link **ret) {
30         _cleanup_link_free_ Link *link = NULL;
31         const char *mac;
32         struct ether_addr *mac_addr;
33         const char *ifname;
34         int r;
35
36         assert(device);
37         assert(ret);
38
39         link = new0(Link, 1);
40         if (!link)
41                 return -ENOMEM;
42
43         link->manager = manager;
44         link->state = _LINK_STATE_INVALID;
45
46         link->ifindex = udev_device_get_ifindex(device);
47         if (link->ifindex <= 0)
48                 return -EINVAL;
49
50         mac = udev_device_get_sysattr_value(device, "address");
51         if (mac) {
52                 mac_addr = ether_aton(mac);
53                 if (mac_addr)
54                         memcpy(&link->mac, mac_addr, sizeof(struct ether_addr));
55         }
56
57         ifname = udev_device_get_sysname(device);
58         link->ifname = strdup(ifname);
59
60         r = hashmap_put(manager->links, &link->ifindex, link);
61         if (r < 0)
62                 return r;
63
64         *ret = link;
65         link = NULL;
66
67         return 0;
68 }
69
70 void link_free(Link *link) {
71         if (!link)
72                 return;
73
74         assert(link->manager);
75
76         if (link->dhcp)
77                 sd_dhcp_client_free(link->dhcp);
78
79         route_free(link->dhcp_route);
80         link->dhcp_route = NULL;
81
82         address_free(link->dhcp_address);
83         link->dhcp_address = NULL;
84
85         hashmap_remove(link->manager->links, &link->ifindex);
86
87         free(link->ifname);
88
89         free(link);
90 }
91
92 int link_add(Manager *m, struct udev_device *device) {
93         Link *link;
94         Network *network;
95         int r;
96         uint64_t ifindex;
97         const char *devtype;
98
99         assert(m);
100         assert(device);
101
102         ifindex = udev_device_get_ifindex(device);
103         link = hashmap_get(m->links, &ifindex);
104         if (link)
105                 return 0;
106
107         r = link_new(m, device, &link);
108         if (r < 0) {
109                 log_error("Could not create link: %s", strerror(-r));
110                 return r;
111         }
112
113         devtype = udev_device_get_devtype(device);
114         if (streq_ptr(devtype, "bridge")) {
115                 r = bridge_set_link(m, link);
116                 if (r < 0)
117                         return r == -ENOENT ? 0 : r;
118         }
119
120         r = network_get(m, device, &network);
121         if (r < 0)
122                 return r == -ENOENT ? 0 : r;
123
124         r = network_apply(m, network, link);
125         if (r < 0)
126                 return r;
127
128         return 0;
129 }
130
131 static int link_enter_configured(Link *link) {
132         assert(link);
133         assert(link->state == LINK_STATE_SETTING_ROUTES);
134
135         log_info("Link '%s' configured", link->ifname);
136
137         link->state = LINK_STATE_CONFIGURED;
138
139         return 0;
140 }
141
142 static void link_enter_failed(Link *link) {
143         assert(link);
144
145         link->state = LINK_STATE_FAILED;
146 }
147
148 static int route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
149         Link *link = userdata;
150         int r;
151
152         assert(link->route_messages > 0);
153         assert(link->state == LINK_STATE_SETTING_ADDRESSES ||
154                link->state == LINK_STATE_SETTING_ROUTES ||
155                link->state == LINK_STATE_FAILED);
156
157         link->route_messages --;
158
159         if (link->state == LINK_STATE_FAILED)
160                 return 1;
161
162         r = sd_rtnl_message_get_errno(m);
163         if (r < 0 && r != -EEXIST)
164                 log_warning("Could not set route on interface '%s': %s",
165                             link->ifname, strerror(-r));
166
167         /* we might have received an old reply after moving back to SETTING_ADDRESSES,
168          * ignore it */
169         if (link->route_messages == 0 && link->state == LINK_STATE_SETTING_ROUTES) {
170                 log_info("Routes set for link '%s'", link->ifname);
171                 link_enter_configured(link);
172         }
173
174         return 1;
175 }
176
177 static int link_enter_set_routes(Link *link) {
178         Route *route;
179         int r;
180
181         assert(link);
182         assert(link->network);
183         assert(link->state == LINK_STATE_SETTING_ADDRESSES);
184
185         link->state = LINK_STATE_SETTING_ROUTES;
186
187         if (!link->network->static_routes && !link->dhcp_route)
188                 return link_enter_configured(link);
189
190         LIST_FOREACH(static_routes, route, link->network->static_routes) {
191                 r = route_configure(route, link, &route_handler);
192                 if (r < 0) {
193                         log_warning("Could not set routes for link '%s'", link->ifname);
194                         link_enter_failed(link);
195                         return r;
196                 }
197
198                 link->route_messages ++;
199         }
200
201         if (link->dhcp_route) {
202                 r = route_configure(link->dhcp_route, link, &route_handler);
203                 if (r < 0) {
204                         log_warning("Could not set routes for link '%s'", link->ifname);
205                         link_enter_failed(link);
206                         return r;
207                 }
208
209                 link->route_messages ++;
210         }
211
212         return 0;
213 }
214
215 static int address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
216         Link *link = userdata;
217         int r;
218
219         assert(m);
220         assert(link);
221         assert(link->ifname);
222         assert(link->addr_messages > 0);
223         assert(link->state == LINK_STATE_SETTING_ADDRESSES || link->state == LINK_STATE_FAILED);
224
225         link->addr_messages --;
226
227         if (link->state == LINK_STATE_FAILED)
228                 return 1;
229
230         r = sd_rtnl_message_get_errno(m);
231         if (r < 0 && r != -EEXIST)
232                 log_warning("Could not set address on interface '%s': %s",
233                             link->ifname, strerror(-r));
234
235         if (link->addr_messages == 0) {
236                 log_info("Addresses set for link '%s'", link->ifname);
237                 link_enter_set_routes(link);
238         }
239
240         return 1;
241 }
242
243 static int link_enter_set_addresses(Link *link) {
244         Address *address;
245         int r;
246
247         assert(link);
248         assert(link->network);
249         assert(link->state != _LINK_STATE_INVALID);
250
251         link->state = LINK_STATE_SETTING_ADDRESSES;
252
253         if (!link->network->static_addresses && !link->dhcp_address)
254                 return link_enter_set_routes(link);
255
256         LIST_FOREACH(static_addresses, address, link->network->static_addresses) {
257                 r = address_configure(address, link, &address_handler);
258                 if (r < 0) {
259                         log_warning("Could not set addresses for link '%s'", link->ifname);
260                         link_enter_failed(link);
261                         return r;
262                 }
263
264                 link->addr_messages ++;
265         }
266
267         if (link->dhcp_address) {
268                 r = address_configure(link->dhcp_address, link, &address_handler);
269                 if (r < 0) {
270                         log_warning("Could not set addresses for link '%s'", link->ifname);
271                         link_enter_failed(link);
272                         return r;
273                 }
274
275                 link->addr_messages ++;
276         }
277
278         return 0;
279 }
280
281 static int link_up_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
282         Link *link = userdata;
283         int r;
284
285         r = sd_rtnl_message_get_errno(m);
286         if (r < 0) {
287                 log_warning("Could not bring up interface '%s': %s",
288                             link->ifname, strerror(-r));
289                 link_enter_failed(link);
290         }
291
292         return 1;
293 }
294
295 static int link_up(Link *link) {
296         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
297         int r;
298
299         assert(link);
300         assert(link->manager);
301         assert(link->manager->rtnl);
302
303         r = sd_rtnl_message_link_new(RTM_SETLINK, link->ifindex, &req);
304         if (r < 0) {
305                 log_error("Could not allocate RTM_SETLINK message");
306                 return r;
307         }
308
309         r = sd_rtnl_message_link_set_flags(req, IFF_UP);
310         if (r < 0) {
311                 log_error("Could not set link flags");
312                 return r;
313         }
314
315         r = sd_rtnl_call_async(link->manager->rtnl, req, link_up_handler, link, 0, NULL);
316         if (r < 0) {
317                 log_error("Could not send rtnetlink message: %s", strerror(-r));
318                 return r;
319         }
320
321         return 0;
322 }
323
324 static int link_bridge_joined(Link *link) {
325         int r;
326
327         assert(link);
328         assert(link->state == LINK_STATE_JOINING_BRIDGE);
329         assert(link->network);
330
331         r = link_up(link);
332         if (r < 0) {
333                 link_enter_failed(link);
334                 return r;
335         }
336
337         if (!link->network->dhcp) {
338                 r = link_enter_set_addresses(link);
339                 if (r < 0)
340                         link_enter_failed(link);
341                         return r;
342         }
343
344         return 0;
345 }
346
347 static int bridge_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
348         Link *link = userdata;
349         int r;
350
351         assert(link->state == LINK_STATE_JOINING_BRIDGE || link->state == LINK_STATE_FAILED);
352         assert(link->network);
353
354         if (link->state == LINK_STATE_FAILED)
355                 return 1;
356
357         r = sd_rtnl_message_get_errno(m);
358         if (r < 0) {
359                 log_warning("Could not join interface '%s' to bridge '%s': %s",
360                             link->ifname, link->network->bridge->name, strerror(-r));
361                 link_enter_failed(link);
362                 return 1;
363         } else
364                 log_info("Join interface '%s' to bridge: %s",
365                             link->ifname, link->network->bridge->name);
366
367         link_bridge_joined(link);
368
369         return 1;
370 }
371
372 static int link_enter_join_bridge(Link *link) {
373         int r;
374
375         assert(link);
376         assert(link->network);
377         assert(link->state == _LINK_STATE_INVALID);
378
379         link->state = LINK_STATE_JOINING_BRIDGE;
380
381         if (!link->network->bridge)
382                 return link_bridge_joined(link);
383
384         r = bridge_join(link->network->bridge, link, &bridge_handler);
385         if (r < 0) {
386                 log_warning("Could not join link '%s' to bridge '%s'", link->ifname,
387                             link->network->bridge->name);
388                 link_enter_failed(link);
389                 return r;
390         }
391
392         return 0;
393 }
394
395 static int link_get_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
396         Link *link = userdata;
397         int r;
398
399         r = sd_rtnl_message_get_errno(m);
400         if (r < 0) {
401                 log_warning("Could not get state of interface '%s': %s",
402                             link->ifname, strerror(-r));
403                 link_enter_failed(link);
404         }
405
406         link_update(link, m);
407
408         return 1;
409 }
410
411 static int link_get(Link *link) {
412         _cleanup_sd_rtnl_message_unref_ sd_rtnl_message *req = NULL;
413         int r;
414
415         assert(link);
416         assert(link->manager);
417         assert(link->manager->rtnl);
418
419         r = sd_rtnl_message_link_new(RTM_GETLINK, link->ifindex, &req);
420         if (r < 0) {
421                 log_error("Could not allocate RTM_GETLINK message");
422                 return r;
423         }
424
425         r = sd_rtnl_call_async(link->manager->rtnl, req, link_get_handler, link, 0, NULL);
426         if (r < 0) {
427                 log_error("Could not send rtnetlink message: %s", strerror(-r));
428                 return r;
429         }
430
431         return 0;
432 }
433
434 int link_configure(Link *link) {
435         int r;
436
437         assert(link);
438         assert(link->network);
439         assert(link->state == _LINK_STATE_INVALID);
440
441         r = link_get(link);
442         if (r < 0) {
443                 link_enter_failed(link);
444                 return r;
445         }
446
447         r = link_enter_join_bridge(link);
448         if (r < 0)
449                 return r;
450
451         return 0;
452 }
453
454 static int address_drop_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
455         Link *link = userdata;
456         int r;
457
458         assert(m);
459         assert(link);
460         assert(link->ifname);
461
462         if (link->state == LINK_STATE_FAILED)
463                 return 1;
464
465         r = sd_rtnl_message_get_errno(m);
466         if (r < 0 && r != -EEXIST)
467                 log_warning("Could not drop address from interface '%s': %s",
468                             link->ifname, strerror(-r));
469
470         return 1;
471 }
472
473 static void dhcp_handler(sd_dhcp_client *client, int event, void *userdata) {
474         Link *link = userdata;
475         struct in_addr address;
476         struct in_addr netmask;
477         struct in_addr gateway;
478         int prefixlen;
479         int r;
480
481         if (link->state == LINK_STATE_FAILED)
482                 return;
483
484         if (event < 0) {
485                 log_warning("DHCP error: %s", strerror(-event));
486                 link_enter_failed(link);
487                 return;
488         }
489
490         if (event == DHCP_EVENT_NO_LEASE)
491                 log_info("IP address in use.");
492
493         if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_EXPIRED ||
494             event == DHCP_EVENT_STOP) {
495                 address_drop(link->dhcp_address, link, address_drop_handler);
496
497                 address_free(link->dhcp_address);
498                 link->dhcp_address = NULL;
499
500                 route_free(link->dhcp_route);
501                 link->dhcp_route = NULL;
502         }
503
504         r = sd_dhcp_client_get_address(client, &address);
505         if (r < 0) {
506                 log_warning("DHCP error: no address");
507                 link_enter_failed(link);
508                 return;
509         }
510
511         r = sd_dhcp_client_get_netmask(client, &netmask);
512         if (r < 0) {
513                 log_warning("DHCP error: no netmask");
514                 link_enter_failed(link);
515                 return;
516         }
517
518         prefixlen = sd_dhcp_client_prefixlen(&netmask);
519         if (prefixlen < 0) {
520                 log_warning("DHCP error: no prefixlen");
521                 link_enter_failed(link);
522                 return;
523         }
524
525         r = sd_dhcp_client_get_router(client, &gateway);
526         if (r < 0) {
527                 log_warning("DHCP error: no router");
528                 link_enter_failed(link);
529                 return;
530         }
531
532         if (event == DHCP_EVENT_IP_CHANGE || event == DHCP_EVENT_IP_ACQUIRE) {
533                 _cleanup_address_free_ Address *addr = NULL;
534                 _cleanup_route_free_ Route *rt = NULL;
535
536                 log_info("Received config over DHCPv4");
537
538                 r = address_new_dynamic(&addr);
539                 if (r < 0) {
540                         log_error("Could not allocate address");
541                         link_enter_failed(link);
542                         return;
543                 }
544
545                 addr->family = AF_INET;
546                 addr->in_addr.in = address;
547                 addr->prefixlen = prefixlen;
548                 addr->netmask = netmask;
549
550                 r = route_new_dynamic(&rt);
551                 if (r < 0) {
552                         log_error("Could not allocate route");
553                         link_enter_failed(link);
554                         return;
555                 }
556
557                 rt->family = AF_INET;
558                 rt->in_addr.in = gateway;
559
560                 link->dhcp_address = addr;
561                 link->dhcp_route = rt;
562                 addr = NULL;
563                 rt = NULL;
564
565                 link_enter_set_addresses(link);
566         }
567
568         return;
569 }
570
571 static int link_acquire_conf(Link *link) {
572         int r;
573
574         assert(link);
575         assert(link->network);
576         assert(link->network->dhcp);
577         assert(link->manager);
578         assert(link->manager->event);
579
580         if (!link->dhcp) {
581                 link->dhcp = sd_dhcp_client_new(link->manager->event);
582                 if (!link->dhcp)
583                         return -ENOMEM;
584
585                 r = sd_dhcp_client_set_index(link->dhcp, link->ifindex);
586                 if (r < 0)
587                         return r;
588
589                 r = sd_dhcp_client_set_mac(link->dhcp, &link->mac);
590                 if (r < 0)
591                         return r;
592
593                 r = sd_dhcp_client_set_callback(link->dhcp, dhcp_handler, link);
594                 if (r < 0)
595                         return r;
596         }
597
598         r = sd_dhcp_client_start(link->dhcp);
599         if (r < 0)
600                 return r;
601
602         return 0;
603 }
604
605 int link_update(Link *link, sd_rtnl_message *m) {
606         unsigned flags;
607         int r;
608
609         assert(link);
610         assert(m);
611
612         r = sd_rtnl_message_link_get_flags(m, &flags);
613         if (r < 0) {
614                 log_warning("Could not get link flags of '%s'", link->ifname);
615                 return r;
616         }
617
618         if (link->flags & IFF_UP && !(flags & IFF_UP))
619                 log_info("Interface '%s' is down", link->ifname);
620         else if (!(link->flags & IFF_UP) && flags & IFF_UP)
621                 log_info("Interface '%s' is up", link->ifname);
622
623         if (link->flags & IFF_LOWER_UP && !(flags & IFF_LOWER_UP)) {
624                 log_info("Interface '%s' is disconnected", link->ifname);
625
626                 if (link->network->dhcp) {
627                         r = sd_dhcp_client_stop(link->dhcp);
628                         if (r < 0) {
629                                 link_enter_failed(link);
630                                 return r;
631                         }
632                 }
633         } else if (!(link->flags & IFF_LOWER_UP) && flags & IFF_LOWER_UP) {
634                 log_info("Interface '%s' is connected", link->ifname);
635
636                 if (link->network && link->network->dhcp) {
637                         r = link_acquire_conf(link);
638                         if (r < 0) {
639                                 link_enter_failed(link);
640                                 return r;
641                         }
642                 }
643         }
644
645         link->flags = flags;
646
647         return 0;
648 }