chiark / gitweb /
network-address,test-network: avoid undefined behaviour
[elogind.git] / src / network / networkd-ipv4ll.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-2014 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-link.h"
26 #include "network-internal.h"
27
28 static int ipv4ll_address_lost(Link *link) {
29         _cleanup_address_free_ Address *address = NULL;
30         _cleanup_route_free_ Route *route = NULL;
31         struct in_addr addr;
32         int r;
33
34         assert(link);
35
36         link->ipv4ll_route = false;
37         link->ipv4ll_address =  false;
38
39         r = sd_ipv4ll_get_address(link->ipv4ll, &addr);
40         if (r < 0)
41                 return 0;
42
43         log_link_debug(link, "IPv4 link-local release %u.%u.%u.%u", ADDRESS_FMT_VAL(addr));
44
45         r = address_new_dynamic(&address);
46         if (r < 0) {
47                 log_link_error(link, "Could not allocate address: %s", strerror(-r));
48                 return r;
49         }
50
51         address->family = AF_INET;
52         address->in_addr.in = addr;
53         address->prefixlen = 16;
54         address->scope = RT_SCOPE_LINK;
55
56         address_drop(address, link, &link_address_drop_handler);
57
58         r = route_new_dynamic(&route, RTPROT_UNSPEC);
59         if (r < 0) {
60                 log_link_error(link, "Could not allocate route: %s",
61                                strerror(-r));
62                 return r;
63         }
64
65         route->family = AF_INET;
66         route->scope = RT_SCOPE_LINK;
67         route->metrics = IPV4LL_ROUTE_METRIC;
68
69         route_drop(route, link, &link_route_drop_handler);
70
71         link_client_handler(link);
72
73         return 0;
74 }
75
76 static int ipv4ll_route_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
77         _cleanup_link_unref_ Link *link = userdata;
78         int r;
79
80         assert(link);
81         assert(!link->ipv4ll_route);
82
83         r = sd_rtnl_message_get_errno(m);
84         if (r < 0 && r != -EEXIST) {
85                 log_link_error(link, "could not set ipv4ll route: %s", strerror(-r));
86                 link_enter_failed(link);
87         }
88
89         link->ipv4ll_route = true;
90
91         if (link->ipv4ll_address == true)
92                 link_client_handler(link);
93
94         return 1;
95 }
96
97 static int ipv4ll_address_handler(sd_rtnl *rtnl, sd_rtnl_message *m, void *userdata) {
98         _cleanup_link_unref_ Link *link = userdata;
99         int r;
100
101         assert(link);
102         assert(!link->ipv4ll_address);
103
104         r = sd_rtnl_message_get_errno(m);
105         if (r < 0 && r != -EEXIST) {
106                 log_link_error(link, "could not set ipv4ll address: %s", strerror(-r));
107                 link_enter_failed(link);
108         } else if (r >= 0)
109                 link_rtnl_process_address(rtnl, m, link->manager);
110
111         link->ipv4ll_address = true;
112
113         if (link->ipv4ll_route == true)
114                 link_client_handler(link);
115
116         return 1;
117 }
118
119 static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
120         _cleanup_address_free_ Address *ll_addr = NULL;
121         _cleanup_route_free_ Route *route = NULL;
122         struct in_addr address;
123         int r;
124
125         assert(ll);
126         assert(link);
127
128         r = sd_ipv4ll_get_address(ll, &address);
129         if (r == -ENOENT)
130                 return 0;
131         else if (r < 0)
132                 return r;
133
134         log_link_debug(link, "IPv4 link-local claim %u.%u.%u.%u",
135                        ADDRESS_FMT_VAL(address));
136
137         r = address_new_dynamic(&ll_addr);
138         if (r < 0)
139                 return r;
140
141         ll_addr->family = AF_INET;
142         ll_addr->in_addr.in = address;
143         ll_addr->prefixlen = 16;
144         ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htonl(0xfffffffflu >> ll_addr->prefixlen);
145         ll_addr->scope = RT_SCOPE_LINK;
146
147         r = address_configure(ll_addr, link, ipv4ll_address_handler);
148         if (r < 0)
149                 return r;
150
151         link->ipv4ll_address = false;
152
153         r = route_new_dynamic(&route, RTPROT_STATIC);
154         if (r < 0)
155                 return r;
156
157         route->family = AF_INET;
158         route->scope = RT_SCOPE_LINK;
159         route->metrics = IPV4LL_ROUTE_METRIC;
160
161         r = route_configure(route, link, ipv4ll_route_handler);
162         if (r < 0)
163                 return r;
164
165         link->ipv4ll_route = false;
166
167         return 0;
168 }
169
170 static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata){
171         Link *link = userdata;
172         int r;
173
174         assert(link);
175         assert(link->network);
176         assert(link->manager);
177
178         if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
179                 return;
180
181         switch(event) {
182                 case IPV4LL_EVENT_STOP:
183                 case IPV4LL_EVENT_CONFLICT:
184                         r = ipv4ll_address_lost(link);
185                         if (r < 0) {
186                                 link_enter_failed(link);
187                                 return;
188                         }
189                         break;
190                 case IPV4LL_EVENT_BIND:
191                         r = ipv4ll_address_claimed(ll, link);
192                         if (r < 0) {
193                                 link_enter_failed(link);
194                                 return;
195                         }
196                         break;
197                 default:
198                         if (event < 0)
199                                 log_link_warning(link, "IPv4 link-local error: %s", strerror(-event));
200                         else
201                                 log_link_warning(link, "IPv4 link-local unknown event: %d", event);
202                         break;
203         }
204 }
205
206 int ipv4ll_configure(Link *link) {
207         uint8_t seed[8];
208         int r;
209
210         assert(link);
211         assert(link->network);
212         assert(link->network->ipv4ll);
213
214         r = sd_ipv4ll_new(&link->ipv4ll);
215         if (r < 0)
216                 return r;
217
218         if (link->udev_device) {
219                 r = net_get_unique_predictable_data(link->udev_device, seed);
220                 if (r >= 0) {
221                         r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
222                         if (r < 0)
223                                 return r;
224                 }
225         }
226
227         r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0);
228         if (r < 0)
229                 return r;
230
231         r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac);
232         if (r < 0)
233                 return r;
234
235         r = sd_ipv4ll_set_index(link->ipv4ll, link->ifindex);
236         if (r < 0)
237                 return r;
238
239         r = sd_ipv4ll_set_callback(link->ipv4ll, ipv4ll_handler, link);
240         if (r < 0)
241                 return r;
242
243         return 0;
244 }