]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/network/networkd-ipv4ll.c
3562e90535ddce1f5ea1736060a109d8581e06e3
[thirdparty/systemd.git] / src / network / networkd-ipv4ll.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <netinet/ether.h>
4 #include <linux/if.h>
5
6 #include "network-internal.h"
7 #include "networkd-address.h"
8 #include "networkd-manager.h"
9 #include "networkd-link.h"
10
11 static int ipv4ll_address_lost(Link *link) {
12 _cleanup_(address_freep) Address *address = NULL;
13 _cleanup_(route_freep) Route *route = NULL;
14 struct in_addr addr;
15 int r;
16
17 assert(link);
18
19 link->ipv4ll_route = false;
20 link->ipv4ll_address = false;
21
22 r = sd_ipv4ll_get_address(link->ipv4ll, &addr);
23 if (r < 0)
24 return 0;
25
26 log_link_debug(link, "IPv4 link-local release %u.%u.%u.%u", ADDRESS_FMT_VAL(addr));
27
28 r = address_new(&address);
29 if (r < 0)
30 return log_link_error_errno(link, r, "Could not allocate address: %m");
31
32 address->family = AF_INET;
33 address->in_addr.in = addr;
34 address->prefixlen = 16;
35 address->scope = RT_SCOPE_LINK;
36
37 address_remove(address, link, NULL);
38
39 r = route_new(&route);
40 if (r < 0)
41 return log_link_error_errno(link, r, "Could not allocate route: %m");
42
43 route->family = AF_INET;
44 route->scope = RT_SCOPE_LINK;
45 route->priority = IPV4LL_ROUTE_METRIC;
46
47 route_remove(route, link, NULL);
48
49 link_check_ready(link);
50
51 return 0;
52 }
53
54 static int ipv4ll_route_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
55 int r;
56
57 assert(link);
58 assert(!link->ipv4ll_route);
59
60 r = sd_netlink_message_get_errno(m);
61 if (r < 0 && r != -EEXIST) {
62 log_link_error_errno(link, r, "could not set ipv4ll route: %m");
63 link_enter_failed(link);
64 }
65
66 link->ipv4ll_route = true;
67
68 if (link->ipv4ll_address == true)
69 link_check_ready(link);
70
71 return 1;
72 }
73
74 static int ipv4ll_address_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
75 int r;
76
77 assert(link);
78 assert(!link->ipv4ll_address);
79
80 r = sd_netlink_message_get_errno(m);
81 if (r < 0 && r != -EEXIST) {
82 log_link_error_errno(link, r, "could not set ipv4ll address: %m");
83 link_enter_failed(link);
84 } else if (r >= 0)
85 manager_rtnl_process_address(rtnl, m, link->manager);
86
87 link->ipv4ll_address = true;
88
89 if (link->ipv4ll_route)
90 link_check_ready(link);
91
92 return 1;
93 }
94
95 static int ipv4ll_address_claimed(sd_ipv4ll *ll, Link *link) {
96 _cleanup_(address_freep) Address *ll_addr = NULL;
97 _cleanup_(route_freep) Route *route = NULL;
98 struct in_addr address;
99 int r;
100
101 assert(ll);
102 assert(link);
103
104 r = sd_ipv4ll_get_address(ll, &address);
105 if (r == -ENOENT)
106 return 0;
107 else if (r < 0)
108 return r;
109
110 log_link_debug(link, "IPv4 link-local claim %u.%u.%u.%u",
111 ADDRESS_FMT_VAL(address));
112
113 r = address_new(&ll_addr);
114 if (r < 0)
115 return r;
116
117 ll_addr->family = AF_INET;
118 ll_addr->in_addr.in = address;
119 ll_addr->prefixlen = 16;
120 ll_addr->broadcast.s_addr = ll_addr->in_addr.in.s_addr | htobe32(0xfffffffflu >> ll_addr->prefixlen);
121 ll_addr->scope = RT_SCOPE_LINK;
122
123 r = address_configure(ll_addr, link, ipv4ll_address_handler, false);
124 if (r < 0)
125 return r;
126
127 link->ipv4ll_address = false;
128
129 r = route_new(&route);
130 if (r < 0)
131 return r;
132
133 route->family = AF_INET;
134 route->scope = RT_SCOPE_LINK;
135 route->protocol = RTPROT_STATIC;
136 route->priority = IPV4LL_ROUTE_METRIC;
137
138 r = route_configure(route, link, ipv4ll_route_handler);
139 if (r < 0)
140 return r;
141
142 link->ipv4ll_route = false;
143
144 return 0;
145 }
146
147 static void ipv4ll_handler(sd_ipv4ll *ll, int event, void *userdata) {
148 Link *link = userdata;
149 int r;
150
151 assert(link);
152 assert(link->network);
153
154 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
155 return;
156
157 switch(event) {
158 case SD_IPV4LL_EVENT_STOP:
159 r = ipv4ll_address_lost(link);
160 if (r < 0) {
161 link_enter_failed(link);
162 return;
163 }
164 break;
165 case SD_IPV4LL_EVENT_CONFLICT:
166 r = ipv4ll_address_lost(link);
167 if (r < 0) {
168 link_enter_failed(link);
169 return;
170 }
171
172 r = sd_ipv4ll_restart(ll);
173 if (r < 0)
174 log_link_warning(link, "Could not acquire IPv4 link-local address");
175 break;
176 case SD_IPV4LL_EVENT_BIND:
177 r = ipv4ll_address_claimed(ll, link);
178 if (r < 0) {
179 link_enter_failed(link);
180 return;
181 }
182 break;
183 default:
184 log_link_warning(link, "IPv4 link-local unknown event: %d", event);
185 break;
186 }
187 }
188
189 int ipv4ll_configure(Link *link) {
190 uint64_t seed;
191 int r;
192
193 assert(link);
194 assert(link->network);
195 assert(link->network->link_local & ADDRESS_FAMILY_IPV4);
196
197 if (!link->ipv4ll) {
198 r = sd_ipv4ll_new(&link->ipv4ll);
199 if (r < 0)
200 return r;
201 }
202
203 if (link->sd_device &&
204 net_get_unique_predictable_data(link->sd_device, &seed) >= 0) {
205 r = sd_ipv4ll_set_address_seed(link->ipv4ll, seed);
206 if (r < 0)
207 return r;
208 }
209
210 r = sd_ipv4ll_attach_event(link->ipv4ll, NULL, 0);
211 if (r < 0)
212 return r;
213
214 r = sd_ipv4ll_set_mac(link->ipv4ll, &link->mac);
215 if (r < 0)
216 return r;
217
218 r = sd_ipv4ll_set_ifindex(link->ipv4ll, link->ifindex);
219 if (r < 0)
220 return r;
221
222 r = sd_ipv4ll_set_callback(link->ipv4ll, ipv4ll_handler, link);
223 if (r < 0)
224 return r;
225
226 return 0;
227 }