]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-ipv4ll.c
Merge pull request #5589 from jasonreeder/claimed_address_fix
[thirdparty/systemd.git] / src / network / networkd-ipv4ll.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2013-2014 Tom Gundersen <teg@jklm.no>
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <netinet/ether.h>
21 #include <linux/if.h>
22
23 #include "network-internal.h"
24 #include "networkd-address.h"
25 #include "networkd-manager.h"
26 #include "networkd-link.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(&address);
46 if (r < 0) {
47 log_link_error_errno(link, r, "Could not allocate address: %m");
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_remove(address, link, link_address_remove_handler);
57
58 r = route_new(&route);
59 if (r < 0) {
60 log_link_error_errno(link, r, "Could not allocate route: %m");
61 return r;
62 }
63
64 route->family = AF_INET;
65 route->scope = RT_SCOPE_LINK;
66 route->priority = IPV4LL_ROUTE_METRIC;
67
68 route_remove(route, link, link_route_remove_handler);
69
70 link_check_ready(link);
71
72 return 0;
73 }
74
75 static int ipv4ll_route_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
76 _cleanup_link_unref_ Link *link = userdata;
77 int r;
78
79 assert(link);
80 assert(!link->ipv4ll_route);
81
82 r = sd_netlink_message_get_errno(m);
83 if (r < 0 && r != -EEXIST) {
84 log_link_error_errno(link, r, "could not set ipv4ll route: %m");
85 link_enter_failed(link);
86 }
87
88 link->ipv4ll_route = true;
89
90 if (link->ipv4ll_address == true)
91 link_check_ready(link);
92
93 return 1;
94 }
95
96 static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, void *userdata) {
97 _cleanup_link_unref_ Link *link = userdata;
98 int r;
99
100 assert(link);
101 assert(!link->ipv4ll_address);
102
103 r = sd_netlink_message_get_errno(m);
104 if (r < 0 && r != -EEXIST) {
105 log_link_error_errno(link, r, "could not set ipv4ll address: %m");
106 link_enter_failed(link);
107 } else if (r >= 0)
108 manager_rtnl_process_address(rtnl, m, link->manager);
109
110 link->ipv4ll_address = true;
111
112 if (link->ipv4ll_route == true)
113 link_check_ready(link);
114
115 return 1;
116 }
117
118 static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
119 _cleanup_address_free_ Address *ll_addr = NULL;
120 _cleanup_route_free_ Route *route = NULL;
121 struct in_addr address;
122 int r;
123
124 assert(ll);
125 assert(link);
126
127 r = sd_ipv4ll_get_address(ll, &address);
128 if (r == -ENOENT)
129 return 0;
130 else if (r < 0)
131 return r;
132
133 log_link_debug(link, "IPv4 link-local claim %u.%u.%u.%u",
134 ADDRESS_FMT_VAL(address));
135
136 r = address_new(&ll_addr);
137 if (r < 0)
138 return r;
139
140 ll_addr->family = AF_INET;
141 ll_addr->in_addr.in = address;
142 ll_addr->prefixlen = 16;
143 ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htobe32(0xfffffffflu >> ll_addr->prefixlen);
144 ll_addr->scope = RT_SCOPE_LINK;
145
146 r = address_configure(ll_addr, link, ipv4ll_address_handler, false);
147 if (r < 0)
148 return r;
149
150 link->ipv4ll_address = false;
151
152 r = route_new(&route);
153 if (r < 0)
154 return r;
155
156 route->family = AF_INET;
157 route->scope = RT_SCOPE_LINK;
158 route->protocol = RTPROT_STATIC;
159 route->priority = 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
177 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
178 return;
179
180 switch(event) {
181 case SD_IPV4LL_EVENT_STOP:
182 r = ipv4ll_address_lost(link);
183 if (r < 0) {
184 link_enter_failed(link);
185 return;
186 }
187 break;
188 case SD_IPV4LL_EVENT_CONFLICT:
189 r = ipv4ll_address_lost(link);
190 if (r < 0) {
191 link_enter_failed(link);
192 return;
193 }
194
195 r = sd_ipv4ll_restart(ll);
196 if (r < 0)
197 log_link_warning(link, "Could not acquire IPv4 link-local address");
198 break;
199 case SD_IPV4LL_EVENT_BIND:
200 r = ipv4ll_address_claimed(ll, link);
201 if (r < 0) {
202 link_enter_failed(link);
203 return;
204 }
205 break;
206 default:
207 log_link_warning(link, "IPv4 link-local unknown event: %d", event);
208 break;
209 }
210 }
211
212 int ipv4ll_configure(Link *link) {
213 uint64_t seed;
214 int r;
215
216 assert(link);
217 assert(link->network);
218 assert(link->network->link_local & ADDRESS_FAMILY_IPV4);
219
220 if (!link->ipv4ll) {
221 r = sd_ipv4ll_new(&link->ipv4ll);
222 if (r < 0)
223 return r;
224 }
225
226 if (link->udev_device) {
227 r = net_get_unique_predictable_data(link->udev_device, &seed);
228 if (r >= 0) {
229 r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
230 if (r < 0)
231 return r;
232 }
233 }
234
235 r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0);
236 if (r < 0)
237 return r;
238
239 r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac);
240 if (r < 0)
241 return r;
242
243 r = sd_ipv4ll_set_ifindex(link->ipv4ll, link->ifindex);
244 if (r < 0)
245 return r;
246
247 r = sd_ipv4ll_set_callback(link->ipv4ll, ipv4ll_handler, link);
248 if (r < 0)
249 return r;
250
251 return 0;
252 }