]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
b22d8a00 | 2 | |
9aa5d8ba | 3 | #include <netinet/in.h> |
b22d8a00 TG |
4 | #include <linux/if.h> |
5 | ||
b22d8a00 | 6 | #include "network-internal.h" |
23f53b99 | 7 | #include "networkd-address.h" |
ca5ad760 | 8 | #include "networkd-ipv4ll.h" |
23f53b99 | 9 | #include "networkd-link.h" |
ca5ad760 YW |
10 | #include "networkd-manager.h" |
11 | #include "parse-util.h" | |
b22d8a00 TG |
12 | |
13 | static int ipv4ll_address_lost(Link *link) { | |
8e766630 LP |
14 | _cleanup_(address_freep) Address *address = NULL; |
15 | _cleanup_(route_freep) Route *route = NULL; | |
b22d8a00 TG |
16 | struct in_addr addr; |
17 | int r; | |
18 | ||
19 | assert(link); | |
20 | ||
21 | link->ipv4ll_route = false; | |
920b52e4 | 22 | link->ipv4ll_address = false; |
b22d8a00 TG |
23 | |
24 | r = sd_ipv4ll_get_address(link->ipv4ll, &addr); | |
25 | if (r < 0) | |
26 | return 0; | |
27 | ||
79008bdd | 28 | log_link_debug(link, "IPv4 link-local release %u.%u.%u.%u", ADDRESS_FMT_VAL(addr)); |
b22d8a00 | 29 | |
f0213e37 | 30 | r = address_new(&address); |
fc95c359 YW |
31 | if (r < 0) |
32 | return log_link_error_errno(link, r, "Could not allocate address: %m"); | |
b22d8a00 TG |
33 | |
34 | address->family = AF_INET; | |
35 | address->in_addr.in = addr; | |
36 | address->prefixlen = 16; | |
37 | address->scope = RT_SCOPE_LINK; | |
38 | ||
807341ec YW |
39 | r = address_remove(address, link, NULL); |
40 | if (r < 0) | |
41 | return r; | |
b22d8a00 | 42 | |
ed9e361a | 43 | r = route_new(&route); |
fc95c359 YW |
44 | if (r < 0) |
45 | return log_link_error_errno(link, r, "Could not allocate route: %m"); | |
b22d8a00 TG |
46 | |
47 | route->family = AF_INET; | |
48 | route->scope = RT_SCOPE_LINK; | |
86655331 | 49 | route->priority = IPV4LL_ROUTE_METRIC; |
b22d8a00 | 50 | |
807341ec YW |
51 | r = route_remove(route, link, NULL); |
52 | if (r < 0) | |
53 | return r; | |
b22d8a00 | 54 | |
8012cd39 | 55 | link_check_ready(link); |
b22d8a00 TG |
56 | |
57 | return 0; | |
58 | } | |
59 | ||
302a796f | 60 | static int ipv4ll_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { |
b22d8a00 TG |
61 | int r; |
62 | ||
63 | assert(link); | |
64 | assert(!link->ipv4ll_route); | |
65 | ||
1c4baffc | 66 | r = sd_netlink_message_get_errno(m); |
b22d8a00 | 67 | if (r < 0 && r != -EEXIST) { |
e53fc357 | 68 | log_link_error_errno(link, r, "could not set ipv4ll route: %m"); |
b22d8a00 | 69 | link_enter_failed(link); |
4ff296b0 | 70 | return 1; |
b22d8a00 TG |
71 | } |
72 | ||
73 | link->ipv4ll_route = true; | |
74 | ||
79316750 | 75 | link_check_ready(link); |
b22d8a00 TG |
76 | |
77 | return 1; | |
78 | } | |
79 | ||
79316750 YW |
80 | static int ipv4ll_route_configure(Link *link) { |
81 | _cleanup_(route_freep) Route *route = NULL; | |
82 | int r; | |
83 | ||
84 | r = route_new(&route); | |
85 | if (r < 0) | |
86 | return r; | |
87 | ||
88 | route->family = AF_INET; | |
89 | route->scope = RT_SCOPE_LINK; | |
90 | route->protocol = RTPROT_STATIC; | |
91 | route->priority = IPV4LL_ROUTE_METRIC; | |
92 | route->table = link_get_vrf_table(link); | |
93 | ||
94 | return route_configure(route, link, ipv4ll_route_handler); | |
95 | } | |
96 | ||
302a796f | 97 | static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { |
b22d8a00 TG |
98 | int r; |
99 | ||
100 | assert(link); | |
101 | assert(!link->ipv4ll_address); | |
102 | ||
1c4baffc | 103 | r = sd_netlink_message_get_errno(m); |
b22d8a00 | 104 | if (r < 0 && r != -EEXIST) { |
e53fc357 | 105 | log_link_error_errno(link, r, "could not set ipv4ll address: %m"); |
b22d8a00 | 106 | link_enter_failed(link); |
4ff296b0 | 107 | return 1; |
45af44d4 | 108 | } else if (r >= 0) |
4ff296b0 | 109 | (void) manager_rtnl_process_address(rtnl, m, link->manager); |
b22d8a00 TG |
110 | |
111 | link->ipv4ll_address = true; | |
112 | ||
79316750 YW |
113 | r = ipv4ll_route_configure(link); |
114 | if (r < 0) { | |
115 | log_link_error_errno(link, r, "Failed to configure ipv4ll route: %m"); | |
116 | link_enter_failed(link); | |
117 | } | |
b22d8a00 TG |
118 | |
119 | return 1; | |
120 | } | |
121 | ||
122 | static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { | |
8e766630 | 123 | _cleanup_(address_freep) Address *ll_addr = NULL; |
b22d8a00 TG |
124 | struct in_addr address; |
125 | int r; | |
126 | ||
127 | assert(ll); | |
128 | assert(link); | |
129 | ||
79316750 YW |
130 | link->ipv4ll_address = false; |
131 | link->ipv4ll_route = false; | |
132 | ||
b22d8a00 TG |
133 | r = sd_ipv4ll_get_address(ll, &address); |
134 | if (r == -ENOENT) | |
135 | return 0; | |
136 | else if (r < 0) | |
137 | return r; | |
138 | ||
79008bdd | 139 | log_link_debug(link, "IPv4 link-local claim %u.%u.%u.%u", |
b22d8a00 TG |
140 | ADDRESS_FMT_VAL(address)); |
141 | ||
f0213e37 | 142 | r = address_new(&ll_addr); |
b22d8a00 TG |
143 | if (r < 0) |
144 | return r; | |
145 | ||
146 | ll_addr->family = AF_INET; | |
147 | ll_addr->in_addr.in = address; | |
148 | ll_addr->prefixlen = 16; | |
8e38570e | 149 | ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htobe32(0xfffffffflu >> ll_addr->prefixlen); |
b22d8a00 TG |
150 | ll_addr->scope = RT_SCOPE_LINK; |
151 | ||
66669078 | 152 | r = address_configure(ll_addr, link, ipv4ll_address_handler, false); |
b22d8a00 TG |
153 | if (r < 0) |
154 | return r; | |
155 | ||
b22d8a00 TG |
156 | return 0; |
157 | } | |
158 | ||
9ed794a3 | 159 | static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) { |
b22d8a00 TG |
160 | Link *link = userdata; |
161 | int r; | |
162 | ||
163 | assert(link); | |
164 | assert(link->network); | |
b22d8a00 TG |
165 | |
166 | if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) | |
167 | return; | |
168 | ||
169 | switch(event) { | |
be19c5b5 | 170 | case SD_IPV4LL_EVENT_STOP: |
0698ff41 JR |
171 | r = ipv4ll_address_lost(link); |
172 | if (r < 0) { | |
173 | link_enter_failed(link); | |
174 | return; | |
175 | } | |
176 | break; | |
be19c5b5 | 177 | case SD_IPV4LL_EVENT_CONFLICT: |
b22d8a00 TG |
178 | r = ipv4ll_address_lost(link); |
179 | if (r < 0) { | |
180 | link_enter_failed(link); | |
181 | return; | |
182 | } | |
0698ff41 JR |
183 | |
184 | r = sd_ipv4ll_restart(ll); | |
185 | if (r < 0) | |
aa5f4c77 | 186 | log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m"); |
b22d8a00 | 187 | break; |
be19c5b5 | 188 | case SD_IPV4LL_EVENT_BIND: |
b22d8a00 TG |
189 | r = ipv4ll_address_claimed(ll, link); |
190 | if (r < 0) { | |
79316750 | 191 | log_link_error(link, "Failed to configure ipv4ll address: %m"); |
b22d8a00 TG |
192 | link_enter_failed(link); |
193 | return; | |
194 | } | |
195 | break; | |
196 | default: | |
b45e4eb6 | 197 | log_link_warning(link, "IPv4 link-local unknown event: %d", event); |
b22d8a00 TG |
198 | break; |
199 | } | |
200 | } | |
201 | ||
202 | int ipv4ll_configure(Link *link) { | |
dbe81cbd | 203 | uint64_t seed; |
b22d8a00 TG |
204 | int r; |
205 | ||
206 | assert(link); | |
207 | assert(link->network); | |
8bc17bb3 | 208 | assert(link->network->link_local & (ADDRESS_FAMILY_IPV4 | ADDRESS_FAMILY_FALLBACK_IPV4)); |
b22d8a00 | 209 | |
0bc70f1d TG |
210 | if (!link->ipv4ll) { |
211 | r = sd_ipv4ll_new(&link->ipv4ll); | |
212 | if (r < 0) | |
213 | return r; | |
214 | } | |
b22d8a00 | 215 | |
51517f9e | 216 | if (link->sd_device && |
96848152 | 217 | net_get_unique_predictable_data(link->sd_device, true, &seed) >= 0) { |
51517f9e YW |
218 | r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed); |
219 | if (r < 0) | |
220 | return r; | |
b22d8a00 TG |
221 | } |
222 | ||
223 | r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0); | |
224 | if (r < 0) | |
225 | return r; | |
226 | ||
227 | r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac); | |
228 | if (r < 0) | |
229 | return r; | |
230 | ||
2f8e7633 | 231 | r = sd_ipv4ll_set_ifindex(link->ipv4ll, link->ifindex); |
b22d8a00 TG |
232 | if (r < 0) |
233 | return r; | |
234 | ||
235 | r = sd_ipv4ll_set_callback(link->ipv4ll, ipv4ll_handler, link); | |
236 | if (r < 0) | |
237 | return r; | |
238 | ||
239 | return 0; | |
240 | } | |
ca5ad760 YW |
241 | |
242 | int config_parse_ipv4ll( | |
243 | const char* unit, | |
244 | const char *filename, | |
245 | unsigned line, | |
246 | const char *section, | |
247 | unsigned section_line, | |
248 | const char *lvalue, | |
249 | int ltype, | |
250 | const char *rvalue, | |
251 | void *data, | |
252 | void *userdata) { | |
253 | ||
254 | AddressFamilyBoolean *link_local = data; | |
255 | int r; | |
256 | ||
257 | assert(filename); | |
258 | assert(lvalue); | |
259 | assert(rvalue); | |
260 | assert(data); | |
261 | ||
262 | /* Note that this is mostly like | |
263 | * config_parse_address_family_boolean(), except that it | |
264 | * applies only to IPv4 */ | |
265 | ||
266 | r = parse_boolean(rvalue); | |
267 | if (r < 0) { | |
268 | log_syntax(unit, LOG_ERR, filename, line, r, | |
269 | "Failed to parse %s=%s, ignoring assignment. " | |
270 | "Note that the setting %s= is deprecated, please use LinkLocalAddressing= instead.", | |
271 | lvalue, rvalue, lvalue); | |
272 | return 0; | |
273 | } | |
274 | ||
275 | SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, r); | |
276 | ||
277 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
278 | "%s=%s is deprecated, please use LinkLocalAddressing=%s instead.", | |
279 | lvalue, rvalue, address_family_boolean_to_string(*link_local)); | |
280 | ||
281 | return 0; | |
282 | } |