]>
Commit | Line | Data |
---|---|---|
19d9a5ad YW |
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | ||
3 | #include "networkd-address.h" | |
354bc760 | 4 | #include "networkd-address-label.h" |
e5b35bf6 | 5 | #include "networkd-bridge-fdb.h" |
9a038aac | 6 | #include "networkd-bridge-mdb.h" |
1d28a3cf | 7 | #include "networkd-dhcp-server.h" |
ccffa166 YW |
8 | #include "networkd-dhcp4.h" |
9 | #include "networkd-dhcp6.h" | |
fdeba3f5 | 10 | #include "networkd-ipv6-proxy-ndp.h" |
19d9a5ad | 11 | #include "networkd-manager.h" |
ba4c7184 | 12 | #include "networkd-ndisc.h" |
19d9a5ad YW |
13 | #include "networkd-neighbor.h" |
14 | #include "networkd-nexthop.h" | |
15 | #include "networkd-route.h" | |
16 | #include "networkd-routing-policy-rule.h" | |
17 | #include "networkd-queue.h" | |
9b682672 | 18 | #include "networkd-setlink.h" |
1dec9d81 | 19 | #include "tc.h" |
19d9a5ad YW |
20 | |
21 | static void request_free_object(RequestType type, void *object) { | |
89346ac6 | 22 | switch (type) { |
112a0972 YW |
23 | case REQUEST_TYPE_ACTIVATE_LINK: |
24 | break; | |
76c5a0f2 YW |
25 | case REQUEST_TYPE_ADDRESS: |
26 | address_free(object); | |
27 | break; | |
354bc760 YW |
28 | case REQUEST_TYPE_ADDRESS_LABEL: |
29 | address_label_free(object); | |
30 | break; | |
e5b35bf6 YW |
31 | case REQUEST_TYPE_BRIDGE_FDB: |
32 | bridge_fdb_free(object); | |
33 | break; | |
9a038aac YW |
34 | case REQUEST_TYPE_BRIDGE_MDB: |
35 | bridge_mdb_free(object); | |
36 | break; | |
1d28a3cf | 37 | case REQUEST_TYPE_DHCP_SERVER: |
ccffa166 YW |
38 | case REQUEST_TYPE_DHCP4_CLIENT: |
39 | case REQUEST_TYPE_DHCP6_CLIENT: | |
1d28a3cf | 40 | break; |
fdeba3f5 YW |
41 | case REQUEST_TYPE_IPV6_PROXY_NDP: |
42 | free(object); | |
43 | break; | |
ba4c7184 YW |
44 | case REQUEST_TYPE_NDISC: |
45 | break; | |
40ca350e YW |
46 | case REQUEST_TYPE_NEIGHBOR: |
47 | neighbor_free(object); | |
48 | break; | |
709055da | 49 | case REQUEST_TYPE_NETDEV_STACKED: |
efa7b8ad | 50 | netdev_unref(object); |
709055da | 51 | break; |
76c5a0f2 YW |
52 | case REQUEST_TYPE_NEXTHOP: |
53 | nexthop_free(object); | |
54 | break; | |
a254fab2 YW |
55 | case REQUEST_TYPE_RADV: |
56 | break; | |
76c5a0f2 YW |
57 | case REQUEST_TYPE_ROUTE: |
58 | route_free(object); | |
59 | break; | |
0e5ef6be YW |
60 | case REQUEST_TYPE_ROUTING_POLICY_RULE: |
61 | routing_policy_rule_free(object); | |
62 | break; | |
0fa8ee6c | 63 | case REQUEST_TYPE_SET_LINK: |
1dec9d81 YW |
64 | break; |
65 | case REQUEST_TYPE_TRAFFIC_CONTROL: | |
66 | traffic_control_free(object); | |
67 | break; | |
68f52063 | 68 | case REQUEST_TYPE_UP_DOWN: |
0fa8ee6c | 69 | break; |
19d9a5ad | 70 | default: |
04499a70 | 71 | assert_not_reached(); |
19d9a5ad YW |
72 | } |
73 | } | |
74 | ||
40b12fa2 | 75 | static Request *request_free(Request *req) { |
19d9a5ad YW |
76 | if (!req) |
77 | return NULL; | |
78 | ||
40b12fa2 YW |
79 | if (req->link && req->link->manager) |
80 | /* To prevent from triggering assertions in hash functions, remove this request before | |
81 | * freeing object below. */ | |
82 | ordered_set_remove(req->link->manager->request_queue, req); | |
19d9a5ad YW |
83 | if (req->consume_object) |
84 | request_free_object(req->type, req->object); | |
19d9a5ad YW |
85 | link_unref(req->link); |
86 | ||
87 | return mfree(req); | |
88 | } | |
89 | ||
90 | DEFINE_TRIVIAL_CLEANUP_FUNC(Request*, request_free); | |
91 | ||
92 | void request_drop(Request *req) { | |
475ec334 YW |
93 | if (!req) |
94 | return; | |
95 | ||
19d9a5ad YW |
96 | if (req->message_counter) |
97 | (*req->message_counter)--; | |
98 | ||
99 | request_free(req); | |
100 | } | |
101 | ||
40b12fa2 YW |
102 | static void request_hash_func(const Request *req, struct siphash *state) { |
103 | assert(req); | |
104 | assert(req->link); | |
105 | assert(state); | |
106 | ||
107 | siphash24_compress(&req->link->ifindex, sizeof(req->link->ifindex), state); | |
108 | siphash24_compress(&req->type, sizeof(req->type), state); | |
109 | ||
89346ac6 | 110 | switch (req->type) { |
112a0972 YW |
111 | case REQUEST_TYPE_ACTIVATE_LINK: |
112 | break; | |
40b12fa2 YW |
113 | case REQUEST_TYPE_ADDRESS: |
114 | address_hash_func(req->address, state); | |
115 | break; | |
116 | case REQUEST_TYPE_ADDRESS_LABEL: | |
117 | case REQUEST_TYPE_BRIDGE_FDB: | |
118 | case REQUEST_TYPE_BRIDGE_MDB: | |
709055da | 119 | case REQUEST_TYPE_NETDEV_STACKED: |
40b12fa2 YW |
120 | /* TODO: Currently, these types do not have any specific hash and compare functions. |
121 | * Fortunately, all these objects are 'static', thus we can use the trivial functions. */ | |
122 | trivial_hash_func(req->object, state); | |
123 | break; | |
124 | case REQUEST_TYPE_DHCP_SERVER: | |
ccffa166 YW |
125 | case REQUEST_TYPE_DHCP4_CLIENT: |
126 | case REQUEST_TYPE_DHCP6_CLIENT: | |
127 | /* These types do not have an object. */ | |
40b12fa2 YW |
128 | break; |
129 | case REQUEST_TYPE_IPV6_PROXY_NDP: | |
130 | in6_addr_hash_func(req->ipv6_proxy_ndp, state); | |
131 | break; | |
ba4c7184 YW |
132 | case REQUEST_TYPE_NDISC: |
133 | /* This type does not have an object. */ | |
134 | break; | |
40b12fa2 YW |
135 | case REQUEST_TYPE_NEIGHBOR: |
136 | neighbor_hash_func(req->neighbor, state); | |
137 | break; | |
138 | case REQUEST_TYPE_NEXTHOP: | |
139 | nexthop_hash_func(req->nexthop, state); | |
140 | break; | |
a254fab2 YW |
141 | case REQUEST_TYPE_RADV: |
142 | /* This type does not have an object. */ | |
143 | break; | |
40b12fa2 YW |
144 | case REQUEST_TYPE_ROUTE: |
145 | route_hash_func(req->route, state); | |
146 | break; | |
147 | case REQUEST_TYPE_ROUTING_POLICY_RULE: | |
148 | routing_policy_rule_hash_func(req->rule, state); | |
149 | break; | |
89346ac6 | 150 | case REQUEST_TYPE_SET_LINK: |
9b682672 | 151 | trivial_hash_func(req->set_link_operation_ptr, state); |
0fa8ee6c | 152 | break; |
1dec9d81 YW |
153 | case REQUEST_TYPE_TRAFFIC_CONTROL: |
154 | traffic_control_hash_func(req->traffic_control, state); | |
155 | break; | |
68f52063 YW |
156 | case REQUEST_TYPE_UP_DOWN: |
157 | break; | |
40b12fa2 | 158 | default: |
04499a70 | 159 | assert_not_reached(); |
40b12fa2 YW |
160 | } |
161 | } | |
162 | ||
163 | static int request_compare_func(const struct Request *a, const struct Request *b) { | |
164 | int r; | |
165 | ||
166 | assert(a); | |
167 | assert(b); | |
168 | assert(a->link); | |
169 | assert(b->link); | |
170 | ||
171 | r = CMP(a->link->ifindex, b->link->ifindex); | |
172 | if (r != 0) | |
173 | return r; | |
174 | ||
175 | r = CMP(a->type, b->type); | |
176 | if (r != 0) | |
177 | return r; | |
178 | ||
179 | switch (a->type) { | |
112a0972 YW |
180 | case REQUEST_TYPE_ACTIVATE_LINK: |
181 | return 0; | |
40b12fa2 YW |
182 | case REQUEST_TYPE_ADDRESS: |
183 | return address_compare_func(a->address, b->address); | |
184 | case REQUEST_TYPE_ADDRESS_LABEL: | |
185 | case REQUEST_TYPE_BRIDGE_FDB: | |
186 | case REQUEST_TYPE_BRIDGE_MDB: | |
709055da | 187 | case REQUEST_TYPE_NETDEV_STACKED: |
40b12fa2 YW |
188 | return trivial_compare_func(a->object, b->object); |
189 | case REQUEST_TYPE_DHCP_SERVER: | |
ccffa166 YW |
190 | case REQUEST_TYPE_DHCP4_CLIENT: |
191 | case REQUEST_TYPE_DHCP6_CLIENT: | |
40b12fa2 YW |
192 | return 0; |
193 | case REQUEST_TYPE_IPV6_PROXY_NDP: | |
194 | return in6_addr_compare_func(a->ipv6_proxy_ndp, b->ipv6_proxy_ndp); | |
ba4c7184 YW |
195 | case REQUEST_TYPE_NDISC: |
196 | return 0; | |
40b12fa2 YW |
197 | case REQUEST_TYPE_NEIGHBOR: |
198 | return neighbor_compare_func(a->neighbor, b->neighbor); | |
199 | case REQUEST_TYPE_NEXTHOP: | |
200 | return nexthop_compare_func(a->nexthop, b->nexthop); | |
201 | case REQUEST_TYPE_ROUTE: | |
202 | return route_compare_func(a->route, b->route); | |
a254fab2 YW |
203 | case REQUEST_TYPE_RADV: |
204 | return 0; | |
40b12fa2 YW |
205 | case REQUEST_TYPE_ROUTING_POLICY_RULE: |
206 | return routing_policy_rule_compare_func(a->rule, b->rule); | |
0fa8ee6c | 207 | case REQUEST_TYPE_SET_LINK: |
9b682672 | 208 | return trivial_compare_func(a->set_link_operation_ptr, b->set_link_operation_ptr); |
1dec9d81 YW |
209 | case REQUEST_TYPE_TRAFFIC_CONTROL: |
210 | return traffic_control_compare_func(a->traffic_control, b->traffic_control); | |
68f52063 YW |
211 | case REQUEST_TYPE_UP_DOWN: |
212 | return 0; | |
40b12fa2 | 213 | default: |
04499a70 | 214 | assert_not_reached(); |
40b12fa2 YW |
215 | } |
216 | } | |
217 | ||
218 | DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( | |
219 | request_hash_ops, | |
220 | Request, | |
221 | request_hash_func, | |
222 | request_compare_func, | |
223 | request_free); | |
224 | ||
19d9a5ad YW |
225 | int link_queue_request( |
226 | Link *link, | |
227 | RequestType type, | |
228 | void *object, | |
229 | bool consume_object, | |
230 | unsigned *message_counter, | |
231 | link_netlink_message_handler_t netlink_handler, | |
232 | Request **ret) { | |
233 | ||
234 | _cleanup_(request_freep) Request *req = NULL; | |
0fa8ee6c | 235 | Request *existing; |
19d9a5ad YW |
236 | int r; |
237 | ||
238 | assert(link); | |
239 | assert(link->manager); | |
240 | assert(type >= 0 && type < _REQUEST_TYPE_MAX); | |
112a0972 YW |
241 | assert(IN_SET(type, |
242 | REQUEST_TYPE_ACTIVATE_LINK, | |
243 | REQUEST_TYPE_DHCP_SERVER, | |
ccffa166 YW |
244 | REQUEST_TYPE_DHCP4_CLIENT, |
245 | REQUEST_TYPE_DHCP6_CLIENT, | |
ba4c7184 | 246 | REQUEST_TYPE_NDISC, |
a254fab2 | 247 | REQUEST_TYPE_RADV, |
68f52063 YW |
248 | REQUEST_TYPE_SET_LINK, |
249 | REQUEST_TYPE_UP_DOWN) || | |
112a0972 | 250 | object); |
a254fab2 YW |
251 | assert(IN_SET(type, |
252 | REQUEST_TYPE_DHCP_SERVER, | |
ccffa166 YW |
253 | REQUEST_TYPE_DHCP4_CLIENT, |
254 | REQUEST_TYPE_DHCP6_CLIENT, | |
ba4c7184 | 255 | REQUEST_TYPE_NDISC, |
1dec9d81 YW |
256 | REQUEST_TYPE_RADV, |
257 | REQUEST_TYPE_TRAFFIC_CONTROL) || | |
a254fab2 | 258 | netlink_handler); |
19d9a5ad YW |
259 | |
260 | req = new(Request, 1); | |
261 | if (!req) { | |
262 | if (consume_object) | |
263 | request_free_object(type, object); | |
264 | return -ENOMEM; | |
265 | } | |
266 | ||
267 | *req = (Request) { | |
40b12fa2 | 268 | .link = link_ref(link), |
19d9a5ad YW |
269 | .type = type, |
270 | .object = object, | |
271 | .consume_object = consume_object, | |
272 | .message_counter = message_counter, | |
273 | .netlink_handler = netlink_handler, | |
274 | }; | |
275 | ||
0fa8ee6c YW |
276 | existing = ordered_set_get(link->manager->request_queue, req); |
277 | if (existing) { | |
278 | /* To prevent from removing the existing request. */ | |
40b12fa2 | 279 | req->link = link_unref(req->link); |
19d9a5ad | 280 | |
0fa8ee6c YW |
281 | if (ret) |
282 | *ret = existing; | |
283 | return 0; | |
40b12fa2 | 284 | } |
19d9a5ad | 285 | |
0fa8ee6c YW |
286 | r = ordered_set_ensure_put(&link->manager->request_queue, &request_hash_ops, req); |
287 | if (r < 0) | |
288 | return r; | |
289 | ||
19d9a5ad YW |
290 | if (req->message_counter) |
291 | (*req->message_counter)++; | |
292 | ||
293 | if (ret) | |
294 | *ret = req; | |
295 | ||
296 | TAKE_PTR(req); | |
40b12fa2 | 297 | return 1; |
19d9a5ad YW |
298 | } |
299 | ||
300 | int manager_process_requests(sd_event_source *s, void *userdata) { | |
301 | Manager *manager = userdata; | |
302 | int r; | |
303 | ||
304 | assert(manager); | |
305 | ||
306 | for (;;) { | |
307 | bool processed = false; | |
308 | Request *req; | |
309 | ||
310 | ORDERED_SET_FOREACH(req, manager->request_queue) { | |
89346ac6 | 311 | switch (req->type) { |
112a0972 YW |
312 | case REQUEST_TYPE_ACTIVATE_LINK: |
313 | r = request_process_activation(req); | |
314 | break; | |
76c5a0f2 YW |
315 | case REQUEST_TYPE_ADDRESS: |
316 | r = request_process_address(req); | |
317 | break; | |
354bc760 YW |
318 | case REQUEST_TYPE_ADDRESS_LABEL: |
319 | r = request_process_address_label(req); | |
320 | break; | |
e5b35bf6 YW |
321 | case REQUEST_TYPE_BRIDGE_FDB: |
322 | r = request_process_bridge_fdb(req); | |
323 | break; | |
9a038aac YW |
324 | case REQUEST_TYPE_BRIDGE_MDB: |
325 | r = request_process_bridge_mdb(req); | |
326 | break; | |
1d28a3cf YW |
327 | case REQUEST_TYPE_DHCP_SERVER: |
328 | r = request_process_dhcp_server(req); | |
329 | break; | |
ccffa166 YW |
330 | case REQUEST_TYPE_DHCP4_CLIENT: |
331 | r = request_process_dhcp4_client(req); | |
332 | break; | |
333 | case REQUEST_TYPE_DHCP6_CLIENT: | |
334 | r = request_process_dhcp6_client(req); | |
335 | break; | |
fdeba3f5 YW |
336 | case REQUEST_TYPE_IPV6_PROXY_NDP: |
337 | r = request_process_ipv6_proxy_ndp_address(req); | |
338 | break; | |
ba4c7184 YW |
339 | case REQUEST_TYPE_NDISC: |
340 | r = request_process_ndisc(req); | |
341 | break; | |
40ca350e YW |
342 | case REQUEST_TYPE_NEIGHBOR: |
343 | r = request_process_neighbor(req); | |
344 | break; | |
709055da YW |
345 | case REQUEST_TYPE_NETDEV_STACKED: |
346 | r = request_process_stacked_netdev(req); | |
347 | break; | |
76c5a0f2 YW |
348 | case REQUEST_TYPE_NEXTHOP: |
349 | r = request_process_nexthop(req); | |
350 | break; | |
a254fab2 YW |
351 | case REQUEST_TYPE_RADV: |
352 | r = request_process_radv(req); | |
353 | break; | |
76c5a0f2 YW |
354 | case REQUEST_TYPE_ROUTE: |
355 | r = request_process_route(req); | |
356 | break; | |
0e5ef6be YW |
357 | case REQUEST_TYPE_ROUTING_POLICY_RULE: |
358 | r = request_process_routing_policy_rule(req); | |
359 | break; | |
0fa8ee6c YW |
360 | case REQUEST_TYPE_SET_LINK: |
361 | r = request_process_set_link(req); | |
362 | break; | |
1dec9d81 YW |
363 | case REQUEST_TYPE_TRAFFIC_CONTROL: |
364 | r = request_process_traffic_control(req); | |
365 | break; | |
68f52063 YW |
366 | case REQUEST_TYPE_UP_DOWN: |
367 | r = request_process_link_up_or_down(req); | |
368 | break; | |
19d9a5ad YW |
369 | default: |
370 | return -EINVAL; | |
371 | } | |
372 | if (r < 0) | |
373 | link_enter_failed(req->link); | |
374 | if (r > 0) { | |
375 | ordered_set_remove(manager->request_queue, req); | |
376 | request_free(req); | |
377 | processed = true; | |
378 | } | |
379 | } | |
380 | ||
381 | if (!processed) | |
382 | break; | |
383 | } | |
384 | ||
385 | return 0; | |
386 | } |