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