]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-ipv4ll.c
core/namespace: rework the return semantics of clone_device_node yet again
[thirdparty/systemd.git] / src / network / networkd-ipv4ll.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3 This file is part of systemd.
4
5 Copyright 2013-2014 Tom Gundersen <teg@jklm.no>
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19 ***/
20
21 #include <netinet/ether.h>
22 #include <linux/if.h>
23
24 #include "network-internal.h"
25 #include "networkd-address.h"
26 #include "networkd-manager.h"
27 #include "networkd-link.h"
28
29 static int ipv4ll_address_lost(Link *link) {
30 _cleanup_address_free_ Address *address = NULL;
31 _cleanup_route_free_ Route *route = NULL;
32 struct in_addr addr;
33 int r;
34
35 assert(link);
36
37 link->ipv4ll_route = false;
38 link->ipv4ll_address = false;
39
40 r = sd_ipv4ll_get_address(link->ipv4ll, &addr);
41 if (r < 0)
42 return 0;
43
44 log_link_debug(link, "IPv4 link-local release %u.%u.%u.%u", ADDRESS_FMT_VAL(addr));
45
46 r = address_new(&address);
47 if (r < 0) {
48 log_link_error_errno(link, r, "Could not allocate address: %m");
49 return r;
50 }
51
52 address->family = AF_INET;
53 address->in_addr.in = addr;
54 address->prefixlen = 16;
55 address->scope = RT_SCOPE_LINK;
56
57 address_remove(address, link, link_address_remove_handler);
58
59 r = route_new(&route);
60 if (r < 0) {
61 log_link_error_errno(link, r, "Could not allocate route: %m");
62 return r;
63 }
64
65 route->family = AF_INET;
66 route->scope = RT_SCOPE_LINK;
67 route->priority = IPV4LL_ROUTE_METRIC;
68
69 route_remove(route, link, link_route_remove_handler);
70
71 link_check_ready(link);
72
73 return 0;
74 }
75
76 static int ipv4ll_route_handler(sd_netlink *rtnl, sd_netlink_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_netlink_message_get_errno(m);
84 if (r < 0 && r != -EEXIST) {
85 log_link_error_errno(link, r, "could not set ipv4ll route: %m");
86 link_enter_failed(link);
87 }
88
89 link->ipv4ll_route = true;
90
91 if (link->ipv4ll_address == true)
92 link_check_ready(link);
93
94 return 1;
95 }
96
97 static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_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_netlink_message_get_errno(m);
105 if (r < 0 && r != -EEXIST) {
106 log_link_error_errno(link, r, "could not set ipv4ll address: %m");
107 link_enter_failed(link);
108 } else if (r >= 0)
109 manager_rtnl_process_address(rtnl, m, link->manager);
110
111 link->ipv4ll_address = true;
112
113 if (link->ipv4ll_route)
114 link_check_ready(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(&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 | htobe32(0xfffffffflu >> ll_addr->prefixlen);
145 ll_addr->scope = RT_SCOPE_LINK;
146
147 r = address_configure(ll_addr, link, ipv4ll_address_handler, false);
148 if (r < 0)
149 return r;
150
151 link->ipv4ll_address = false;
152
153 r = route_new(&route);
154 if (r < 0)
155 return r;
156
157 route->family = AF_INET;
158 route->scope = RT_SCOPE_LINK;
159 route->protocol = RTPROT_STATIC;
160 route->priority = IPV4LL_ROUTE_METRIC;
161
162 r = route_configure(route, link, ipv4ll_route_handler);
163 if (r < 0)
164 return r;
165
166 link->ipv4ll_route = false;
167
168 return 0;
169 }
170
171 static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) {
172 Link *link = userdata;
173 int r;
174
175 assert(link);
176 assert(link->network);
177
178 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
179 return;
180
181 switch(event) {
182 case SD_IPV4LL_EVENT_STOP:
183 r = ipv4ll_address_lost(link);
184 if (r < 0) {
185 link_enter_failed(link);
186 return;
187 }
188 break;
189 case SD_IPV4LL_EVENT_CONFLICT:
190 r = ipv4ll_address_lost(link);
191 if (r < 0) {
192 link_enter_failed(link);
193 return;
194 }
195
196 r = sd_ipv4ll_restart(ll);
197 if (r < 0)
198 log_link_warning(link, "Could not acquire IPv4 link-local address");
199 break;
200 case SD_IPV4LL_EVENT_BIND:
201 r = ipv4ll_address_claimed(ll, link);
202 if (r < 0) {
203 link_enter_failed(link);
204 return;
205 }
206 break;
207 default:
208 log_link_warning(link, "IPv4 link-local unknown event: %d", event);
209 break;
210 }
211 }
212
213 int ipv4ll_configure(Link *link) {
214 uint64_t seed;
215 int r;
216
217 assert(link);
218 assert(link->network);
219 assert(link->network->link_local & ADDRESS_FAMILY_IPV4);
220
221 if (!link->ipv4ll) {
222 r = sd_ipv4ll_new(&link->ipv4ll);
223 if (r < 0)
224 return r;
225 }
226
227 if (link->udev_device) {
228 r = net_get_unique_predictable_data(link->udev_device, &seed);
229 if (r >= 0) {
230 r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
231 if (r < 0)
232 return r;
233 }
234 }
235
236 r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0);
237 if (r < 0)
238 return r;
239
240 r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac);
241 if (r < 0)
242 return r;
243
244 r = sd_ipv4ll_set_ifindex(link->ipv4ll, link->ifindex);
245 if (r < 0)
246 return r;
247
248 r = sd_ipv4ll_set_callback(link->ipv4ll, ipv4ll_handler, link);
249 if (r < 0)
250 return r;
251
252 return 0;
253 }