]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
b22d8a00 | 2 | |
9aa5d8ba | 3 | #include <netinet/in.h> |
b22d8a00 TG |
4 | #include <linux/if.h> |
5 | ||
b5cc5591 | 6 | #include "netif-util.h" |
23f53b99 | 7 | #include "networkd-address.h" |
ca5ad760 | 8 | #include "networkd-ipv4ll.h" |
23f53b99 | 9 | #include "networkd-link.h" |
ca5ad760 | 10 | #include "networkd-manager.h" |
76c5a0f2 | 11 | #include "networkd-queue.h" |
ca5ad760 | 12 | #include "parse-util.h" |
b22d8a00 | 13 | |
76c5a0f2 | 14 | static int address_new_from_ipv4ll(Link *link, Address **ret) { |
8e766630 | 15 | _cleanup_(address_freep) Address *address = NULL; |
b22d8a00 TG |
16 | struct in_addr addr; |
17 | int r; | |
18 | ||
19 | assert(link); | |
76c5a0f2 YW |
20 | assert(link->ipv4ll); |
21 | assert(ret); | |
b22d8a00 TG |
22 | |
23 | r = sd_ipv4ll_get_address(link->ipv4ll, &addr); | |
24 | if (r < 0) | |
76c5a0f2 | 25 | return r; |
b22d8a00 | 26 | |
f0213e37 | 27 | r = address_new(&address); |
fc95c359 | 28 | if (r < 0) |
76c5a0f2 | 29 | return -ENOMEM; |
b22d8a00 | 30 | |
3b6a3bde | 31 | address->source = NETWORK_CONFIG_SOURCE_IPV4LL; |
b22d8a00 TG |
32 | address->family = AF_INET; |
33 | address->in_addr.in = addr; | |
34 | address->prefixlen = 16; | |
35 | address->scope = RT_SCOPE_LINK; | |
76c5a0f2 | 36 | address->route_metric = IPV4LL_ROUTE_METRIC; |
473680be | 37 | address_set_broadcast(address); |
76c5a0f2 YW |
38 | |
39 | *ret = TAKE_PTR(address); | |
40 | return 0; | |
41 | } | |
42 | ||
43 | static int ipv4ll_address_lost(Link *link) { | |
44 | _cleanup_(address_freep) Address *address = NULL; | |
3b6a3bde | 45 | Address *existing; |
76c5a0f2 YW |
46 | int r; |
47 | ||
48 | assert(link); | |
b22d8a00 | 49 | |
76c5a0f2 YW |
50 | link->ipv4ll_address_configured = false; |
51 | ||
52 | r = address_new_from_ipv4ll(link, &address); | |
53 | if (r == -ENOENT) | |
54 | return 0; | |
807341ec YW |
55 | if (r < 0) |
56 | return r; | |
b22d8a00 | 57 | |
3b6a3bde YW |
58 | if (address_get(link, address, &existing) < 0) |
59 | return 0; | |
60 | ||
61 | if (existing->source != NETWORK_CONFIG_SOURCE_IPV4LL) | |
62 | return 0; | |
63 | ||
64 | if (!address_exists(existing)) | |
65 | return 0; | |
66 | ||
76c5a0f2 YW |
67 | log_link_debug(link, "IPv4 link-local release "IPV4_ADDRESS_FMT_STR, |
68 | IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); | |
b22d8a00 | 69 | |
3b6a3bde | 70 | return address_remove(existing); |
b22d8a00 TG |
71 | } |
72 | ||
302a796f | 73 | static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) { |
b22d8a00 TG |
74 | int r; |
75 | ||
76 | assert(link); | |
1c09d84e | 77 | assert(!link->ipv4ll_address_configured); |
b22d8a00 | 78 | |
5a07fa9d YW |
79 | r = address_configure_handler_internal(rtnl, m, link, "Could not set ipv4ll address"); |
80 | if (r <= 0) | |
81 | return r; | |
b22d8a00 | 82 | |
1c09d84e | 83 | link->ipv4ll_address_configured = true; |
2aa7d367 | 84 | link_check_ready(link); |
b22d8a00 TG |
85 | |
86 | return 1; | |
87 | } | |
88 | ||
89 | static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) { | |
76c5a0f2 | 90 | _cleanup_(address_freep) Address *address = NULL; |
b22d8a00 TG |
91 | int r; |
92 | ||
93 | assert(ll); | |
94 | assert(link); | |
95 | ||
1c09d84e | 96 | link->ipv4ll_address_configured = false; |
79316750 | 97 | |
76c5a0f2 | 98 | r = address_new_from_ipv4ll(link, &address); |
b22d8a00 TG |
99 | if (r == -ENOENT) |
100 | return 0; | |
b22d8a00 TG |
101 | if (r < 0) |
102 | return r; | |
103 | ||
76c5a0f2 YW |
104 | log_link_debug(link, "IPv4 link-local claim "IPV4_ADDRESS_FMT_STR, |
105 | IPV4_ADDRESS_FMT_VAL(address->in_addr.in)); | |
b22d8a00 | 106 | |
76c5a0f2 | 107 | return link_request_address(link, TAKE_PTR(address), true, NULL, ipv4ll_address_handler, NULL); |
b22d8a00 TG |
108 | } |
109 | ||
9ed794a3 | 110 | static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) { |
b22d8a00 TG |
111 | Link *link = userdata; |
112 | int r; | |
113 | ||
114 | assert(link); | |
115 | assert(link->network); | |
b22d8a00 TG |
116 | |
117 | if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER)) | |
118 | return; | |
119 | ||
89346ac6 | 120 | switch (event) { |
be19c5b5 | 121 | case SD_IPV4LL_EVENT_STOP: |
0698ff41 JR |
122 | r = ipv4ll_address_lost(link); |
123 | if (r < 0) { | |
124 | link_enter_failed(link); | |
125 | return; | |
126 | } | |
127 | break; | |
be19c5b5 | 128 | case SD_IPV4LL_EVENT_CONFLICT: |
b22d8a00 TG |
129 | r = ipv4ll_address_lost(link); |
130 | if (r < 0) { | |
131 | link_enter_failed(link); | |
132 | return; | |
133 | } | |
0698ff41 JR |
134 | |
135 | r = sd_ipv4ll_restart(ll); | |
89d8ed99 | 136 | if (r < 0) { |
aa5f4c77 | 137 | log_link_warning_errno(link, r, "Could not acquire IPv4 link-local address: %m"); |
89d8ed99 YW |
138 | link_enter_failed(link); |
139 | } | |
b22d8a00 | 140 | break; |
be19c5b5 | 141 | case SD_IPV4LL_EVENT_BIND: |
b22d8a00 TG |
142 | r = ipv4ll_address_claimed(ll, link); |
143 | if (r < 0) { | |
79316750 | 144 | log_link_error(link, "Failed to configure ipv4ll address: %m"); |
b22d8a00 TG |
145 | link_enter_failed(link); |
146 | return; | |
147 | } | |
148 | break; | |
149 | default: | |
b45e4eb6 | 150 | log_link_warning(link, "IPv4 link-local unknown event: %d", event); |
b22d8a00 TG |
151 | break; |
152 | } | |
153 | } | |
154 | ||
d7ab6ef0 YW |
155 | static int ipv4ll_check_mac(sd_ipv4ll *ll, const struct ether_addr *mac, void *userdata) { |
156 | Manager *m = userdata; | |
157 | struct hw_addr_data hw_addr; | |
158 | ||
159 | assert(m); | |
160 | assert(mac); | |
161 | ||
162 | hw_addr = (struct hw_addr_data) { | |
163 | .length = ETH_ALEN, | |
164 | .ether = *mac, | |
165 | }; | |
166 | ||
167 | return link_get_by_hw_addr(m, &hw_addr, NULL) >= 0; | |
168 | } | |
169 | ||
b22d8a00 | 170 | int ipv4ll_configure(Link *link) { |
dbe81cbd | 171 | uint64_t seed; |
b22d8a00 TG |
172 | int r; |
173 | ||
174 | assert(link); | |
2ffd6d73 | 175 | |
3ca1fab7 | 176 | if (!link_ipv4ll_enabled(link)) |
2ffd6d73 | 177 | return 0; |
b22d8a00 | 178 | |
bc9e40c9 YW |
179 | if (link->ipv4ll) |
180 | return -EBUSY; | |
ca97e7cd | 181 | |
bc9e40c9 YW |
182 | r = sd_ipv4ll_new(&link->ipv4ll); |
183 | if (r < 0) | |
184 | return r; | |
185 | ||
186 | r = sd_ipv4ll_attach_event(link->ipv4ll, link->manager->event, 0); | |
187 | if (r < 0) | |
188 | return r; | |
b22d8a00 | 189 | |
51517f9e | 190 | if (link->sd_device && |
96848152 | 191 | net_get_unique_predictable_data(link->sd_device, true, &seed) >= 0) { |
51517f9e YW |
192 | r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed); |
193 | if (r < 0) | |
194 | return r; | |
b22d8a00 TG |
195 | } |
196 | ||
ca2b7cd8 | 197 | r = sd_ipv4ll_set_mac(link->ipv4ll, &link->hw_addr.ether); |
b22d8a00 TG |
198 | if (r < 0) |
199 | return r; | |
200 | ||
2f8e7633 | 201 | r = sd_ipv4ll_set_ifindex(link->ipv4ll, link->ifindex); |
b22d8a00 TG |
202 | if (r < 0) |
203 | return r; | |
204 | ||
205 | r = sd_ipv4ll_set_callback(link->ipv4ll, ipv4ll_handler, link); | |
206 | if (r < 0) | |
207 | return r; | |
208 | ||
d7ab6ef0 | 209 | return sd_ipv4ll_set_check_mac_callback(link->ipv4ll, ipv4ll_check_mac, link->manager); |
b22d8a00 | 210 | } |
ca5ad760 | 211 | |
a3adb4a6 | 212 | int ipv4ll_update_mac(Link *link) { |
a3adb4a6 YW |
213 | assert(link); |
214 | ||
f8d6397a YW |
215 | if (link->hw_addr.length != ETH_ALEN) |
216 | return 0; | |
217 | if (ether_addr_is_null(&link->hw_addr.ether)) | |
218 | return 0; | |
a3adb4a6 YW |
219 | if (!link->ipv4ll) |
220 | return 0; | |
221 | ||
f8d6397a | 222 | return sd_ipv4ll_set_mac(link->ipv4ll, &link->hw_addr.ether); |
a3adb4a6 YW |
223 | } |
224 | ||
ca5ad760 YW |
225 | int config_parse_ipv4ll( |
226 | const char* unit, | |
227 | const char *filename, | |
228 | unsigned line, | |
229 | const char *section, | |
230 | unsigned section_line, | |
231 | const char *lvalue, | |
232 | int ltype, | |
233 | const char *rvalue, | |
234 | void *data, | |
235 | void *userdata) { | |
236 | ||
2d792895 | 237 | AddressFamily *link_local = data; |
ca5ad760 YW |
238 | int r; |
239 | ||
240 | assert(filename); | |
241 | assert(lvalue); | |
242 | assert(rvalue); | |
243 | assert(data); | |
244 | ||
245 | /* Note that this is mostly like | |
2d792895 | 246 | * config_parse_address_family(), except that it |
ca5ad760 YW |
247 | * applies only to IPv4 */ |
248 | ||
249 | r = parse_boolean(rvalue); | |
250 | if (r < 0) { | |
d96edb2c | 251 | log_syntax(unit, LOG_WARNING, filename, line, r, |
ca5ad760 YW |
252 | "Failed to parse %s=%s, ignoring assignment. " |
253 | "Note that the setting %s= is deprecated, please use LinkLocalAddressing= instead.", | |
254 | lvalue, rvalue, lvalue); | |
255 | return 0; | |
256 | } | |
257 | ||
258 | SET_FLAG(*link_local, ADDRESS_FAMILY_IPV4, r); | |
259 | ||
260 | log_syntax(unit, LOG_WARNING, filename, line, 0, | |
261 | "%s=%s is deprecated, please use LinkLocalAddressing=%s instead.", | |
2d792895 | 262 | lvalue, rvalue, address_family_to_string(*link_local)); |
ca5ad760 YW |
263 | |
264 | return 0; | |
265 | } |