]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later |
0f5bd7fe SS |
2 | * Copyright © 2019 VMware, Inc. */ |
3 | ||
4 | #include <linux/pkt_sched.h> | |
5 | ||
6 | #include "alloc-util.h" | |
7 | #include "conf-parser.h" | |
8 | #include "in-addr-util.h" | |
9 | #include "netlink-util.h" | |
3a67b8bb | 10 | #include "networkd-link.h" |
0f5bd7fe | 11 | #include "networkd-manager.h" |
3a67b8bb | 12 | #include "networkd-network.h" |
1dec9d81 | 13 | #include "networkd-queue.h" |
0f5bd7fe SS |
14 | #include "parse-util.h" |
15 | #include "qdisc.h" | |
16 | #include "set.h" | |
17 | #include "string-util.h" | |
2a096331 YW |
18 | #include "strv.h" |
19 | #include "tc-util.h" | |
0f5bd7fe | 20 | |
e8c17dc0 | 21 | const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX] = { |
c853f594 | 22 | [QDISC_KIND_BFIFO] = &bfifo_vtable, |
ad8352f4 | 23 | [QDISC_KIND_CAKE] = &cake_vtable, |
a9a5d632 | 24 | [QDISC_KIND_CODEL] = &codel_vtable, |
f5fc0441 | 25 | [QDISC_KIND_DRR] = &drr_vtable, |
d474aa51 | 26 | [QDISC_KIND_ETS] = &ets_vtable, |
7234b915 | 27 | [QDISC_KIND_FQ] = &fq_vtable, |
e8c17dc0 | 28 | [QDISC_KIND_FQ_CODEL] = &fq_codel_vtable, |
8f6b6d70 | 29 | [QDISC_KIND_FQ_PIE] = &fq_pie_vtable, |
609e8340 | 30 | [QDISC_KIND_GRED] = &gred_vtable, |
7f224020 | 31 | [QDISC_KIND_HHF] = &hhf_vtable, |
b934ac3d | 32 | [QDISC_KIND_HTB] = &htb_vtable, |
e8c17dc0 | 33 | [QDISC_KIND_NETEM] = &netem_vtable, |
bde4ae88 | 34 | [QDISC_KIND_PIE] = &pie_vtable, |
b12aaee5 | 35 | [QDISC_KIND_QFQ] = &qfq_vtable, |
a7476065 | 36 | [QDISC_KIND_PFIFO] = &pfifo_vtable, |
1a95964b | 37 | [QDISC_KIND_PFIFO_FAST] = &pfifo_fast_vtable, |
053a2ddb | 38 | [QDISC_KIND_PFIFO_HEAD_DROP] = &pfifo_head_drop_vtable, |
982998b0 | 39 | [QDISC_KIND_SFB] = &sfb_vtable, |
e8c17dc0 YW |
40 | [QDISC_KIND_SFQ] = &sfq_vtable, |
41 | [QDISC_KIND_TBF] = &tbf_vtable, | |
9b749c11 | 42 | [QDISC_KIND_TEQL] = &teql_vtable, |
e8c17dc0 YW |
43 | }; |
44 | ||
45 | static int qdisc_new(QDiscKind kind, QDisc **ret) { | |
58e6c621 | 46 | _cleanup_(qdisc_freep) QDisc *qdisc = NULL; |
ac810b75 | 47 | int r; |
0f5bd7fe | 48 | |
e8c17dc0 YW |
49 | if (kind == _QDISC_KIND_INVALID) { |
50 | qdisc = new(QDisc, 1); | |
51 | if (!qdisc) | |
52 | return -ENOMEM; | |
53 | ||
54 | *qdisc = (QDisc) { | |
e8c17dc0 YW |
55 | .parent = TC_H_ROOT, |
56 | .kind = kind, | |
57 | }; | |
58 | } else { | |
828a81a9 | 59 | assert(kind >= 0 && kind < _QDISC_KIND_MAX); |
e8c17dc0 YW |
60 | qdisc = malloc0(qdisc_vtable[kind]->object_size); |
61 | if (!qdisc) | |
62 | return -ENOMEM; | |
63 | ||
e8c17dc0 YW |
64 | qdisc->parent = TC_H_ROOT; |
65 | qdisc->kind = kind; | |
ac810b75 YW |
66 | |
67 | if (QDISC_VTABLE(qdisc)->init) { | |
68 | r = QDISC_VTABLE(qdisc)->init(qdisc); | |
69 | if (r < 0) | |
70 | return r; | |
71 | } | |
e8c17dc0 | 72 | } |
0f5bd7fe SS |
73 | |
74 | *ret = TAKE_PTR(qdisc); | |
75 | ||
76 | return 0; | |
77 | } | |
78 | ||
e8c17dc0 | 79 | int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, unsigned section_line, QDisc **ret) { |
307fe3cd | 80 | _cleanup_(config_section_freep) ConfigSection *n = NULL; |
edc54f2f | 81 | _cleanup_(qdisc_freep) QDisc *qdisc = NULL; |
3a67b8bb | 82 | QDisc *existing; |
0f5bd7fe SS |
83 | int r; |
84 | ||
85 | assert(network); | |
86 | assert(ret); | |
e8c17dc0 YW |
87 | assert(filename); |
88 | assert(section_line > 0); | |
0f5bd7fe | 89 | |
307fe3cd | 90 | r = config_section_new(filename, section_line, &n); |
e8c17dc0 YW |
91 | if (r < 0) |
92 | return r; | |
0f5bd7fe | 93 | |
3a67b8bb | 94 | existing = hashmap_get(network->qdiscs_by_section, n); |
e8c17dc0 | 95 | if (existing) { |
3a67b8bb | 96 | if (existing->kind != _QDISC_KIND_INVALID && |
e8c17dc0 | 97 | kind != _QDISC_KIND_INVALID && |
3a67b8bb | 98 | existing->kind != kind) |
e8c17dc0 | 99 | return -EINVAL; |
0f5bd7fe | 100 | |
3a67b8bb YW |
101 | if (existing->kind == kind || kind == _QDISC_KIND_INVALID) { |
102 | *ret = existing; | |
0f5bd7fe SS |
103 | return 0; |
104 | } | |
105 | } | |
106 | ||
e8c17dc0 | 107 | r = qdisc_new(kind, &qdisc); |
0f5bd7fe SS |
108 | if (r < 0) |
109 | return r; | |
110 | ||
3a67b8bb YW |
111 | if (existing) { |
112 | qdisc->handle = existing->handle; | |
113 | qdisc->parent = existing->parent; | |
114 | qdisc->tca_kind = TAKE_PTR(existing->tca_kind); | |
0f5bd7fe | 115 | |
3a67b8bb | 116 | qdisc_free(existing); |
e8c17dc0 | 117 | } |
0f5bd7fe | 118 | |
e8c17dc0 YW |
119 | qdisc->network = network; |
120 | qdisc->section = TAKE_PTR(n); | |
1dec9d81 | 121 | qdisc->source = NETWORK_CONFIG_SOURCE_STATIC; |
0f5bd7fe | 122 | |
3a67b8bb | 123 | r = hashmap_ensure_put(&network->qdiscs_by_section, &config_section_hash_ops, qdisc->section, qdisc); |
e8c17dc0 YW |
124 | if (r < 0) |
125 | return r; | |
0f5bd7fe | 126 | |
e8c17dc0 | 127 | *ret = TAKE_PTR(qdisc); |
0f5bd7fe SS |
128 | return 0; |
129 | } | |
130 | ||
75db809a | 131 | QDisc* qdisc_free(QDisc *qdisc) { |
0f5bd7fe | 132 | if (!qdisc) |
75db809a | 133 | return NULL; |
0f5bd7fe SS |
134 | |
135 | if (qdisc->network && qdisc->section) | |
3a67b8bb | 136 | hashmap_remove(qdisc->network->qdiscs_by_section, qdisc->section); |
0f5bd7fe | 137 | |
307fe3cd | 138 | config_section_free(qdisc->section); |
0f5bd7fe | 139 | |
828a81a9 | 140 | if (qdisc->link) |
3a67b8bb | 141 | set_remove(qdisc->link->qdiscs, qdisc); |
828a81a9 | 142 | |
d8081020 | 143 | free(qdisc->tca_kind); |
75db809a | 144 | return mfree(qdisc); |
0f5bd7fe SS |
145 | } |
146 | ||
828a81a9 YW |
147 | static const char *qdisc_get_tca_kind(const QDisc *qdisc) { |
148 | assert(qdisc); | |
149 | ||
150 | return (QDISC_VTABLE(qdisc) && QDISC_VTABLE(qdisc)->tca_kind) ? | |
151 | QDISC_VTABLE(qdisc)->tca_kind : qdisc->tca_kind; | |
152 | } | |
153 | ||
09d09207 | 154 | static void qdisc_hash_func(const QDisc *qdisc, struct siphash *state) { |
828a81a9 YW |
155 | assert(qdisc); |
156 | assert(state); | |
157 | ||
158 | siphash24_compress(&qdisc->handle, sizeof(qdisc->handle), state); | |
159 | siphash24_compress(&qdisc->parent, sizeof(qdisc->parent), state); | |
160 | siphash24_compress_string(qdisc_get_tca_kind(qdisc), state); | |
161 | } | |
162 | ||
09d09207 | 163 | static int qdisc_compare_func(const QDisc *a, const QDisc *b) { |
828a81a9 YW |
164 | int r; |
165 | ||
166 | assert(a); | |
167 | assert(b); | |
168 | ||
169 | r = CMP(a->handle, b->handle); | |
170 | if (r != 0) | |
171 | return r; | |
172 | ||
173 | r = CMP(a->parent, b->parent); | |
174 | if (r != 0) | |
175 | return r; | |
176 | ||
177 | return strcmp_ptr(qdisc_get_tca_kind(a), qdisc_get_tca_kind(b)); | |
178 | } | |
179 | ||
3a67b8bb YW |
180 | DEFINE_PRIVATE_HASH_OPS_WITH_KEY_DESTRUCTOR( |
181 | qdisc_hash_ops, | |
182 | QDisc, | |
183 | qdisc_hash_func, | |
184 | qdisc_compare_func, | |
185 | qdisc_free); | |
186 | ||
828a81a9 | 187 | static int qdisc_get(Link *link, const QDisc *in, QDisc **ret) { |
3a67b8bb | 188 | QDisc *existing; |
828a81a9 YW |
189 | |
190 | assert(link); | |
191 | assert(in); | |
192 | ||
3a67b8bb YW |
193 | existing = set_get(link->qdiscs, in); |
194 | if (!existing) | |
195 | return -ENOENT; | |
828a81a9 YW |
196 | |
197 | if (ret) | |
3a67b8bb | 198 | *ret = existing; |
828a81a9 YW |
199 | return 0; |
200 | } | |
201 | ||
202 | static int qdisc_add(Link *link, QDisc *qdisc) { | |
203 | int r; | |
204 | ||
205 | assert(link); | |
206 | assert(qdisc); | |
207 | ||
3a67b8bb | 208 | r = set_ensure_put(&link->qdiscs, &qdisc_hash_ops, qdisc); |
828a81a9 YW |
209 | if (r < 0) |
210 | return r; | |
3a67b8bb YW |
211 | if (r == 0) |
212 | return -EEXIST; | |
828a81a9 YW |
213 | |
214 | qdisc->link = link; | |
215 | return 0; | |
216 | } | |
217 | ||
1dec9d81 YW |
218 | static int qdisc_dup(const QDisc *src, QDisc **ret) { |
219 | _cleanup_(qdisc_freep) QDisc *dst = NULL; | |
220 | ||
221 | assert(src); | |
222 | assert(ret); | |
223 | ||
224 | if (QDISC_VTABLE(src)) | |
225 | dst = memdup(src, QDISC_VTABLE(src)->object_size); | |
226 | else | |
227 | dst = newdup(QDisc, src, 1); | |
228 | if (!dst) | |
229 | return -ENOMEM; | |
230 | ||
231 | /* clear all pointers */ | |
232 | dst->network = NULL; | |
233 | dst->section = NULL; | |
234 | dst->link = NULL; | |
235 | dst->tca_kind = NULL; | |
236 | ||
237 | if (src->tca_kind) { | |
238 | dst->tca_kind = strdup(src->tca_kind); | |
239 | if (!dst->tca_kind) | |
240 | return -ENOMEM; | |
241 | } | |
242 | ||
243 | *ret = TAKE_PTR(dst); | |
244 | return 0; | |
245 | } | |
246 | ||
828a81a9 YW |
247 | static void log_qdisc_debug(QDisc *qdisc, Link *link, const char *str) { |
248 | _cleanup_free_ char *state = NULL; | |
249 | ||
250 | assert(qdisc); | |
251 | assert(str); | |
252 | ||
253 | if (!DEBUG_LOGGING) | |
254 | return; | |
255 | ||
256 | (void) network_config_state_to_string_alloc(qdisc->state, &state); | |
257 | ||
258 | log_link_debug(link, "%s %s QDisc (%s): handle=%"PRIx32":%"PRIx32", parent=%"PRIx32":%"PRIx32", kind=%s", | |
259 | str, strna(network_config_source_to_string(qdisc->source)), strna(state), | |
260 | TC_H_MAJ(qdisc->handle) >> 16, TC_H_MIN(qdisc->handle), | |
261 | TC_H_MAJ(qdisc->parent) >> 16, TC_H_MIN(qdisc->parent), | |
262 | strna(qdisc_get_tca_kind(qdisc))); | |
263 | } | |
264 | ||
7ec18462 | 265 | int link_find_qdisc(Link *link, uint32_t handle, uint32_t parent, const char *kind, QDisc **ret) { |
3a67b8bb | 266 | QDisc *qdisc; |
7ec18462 YW |
267 | |
268 | assert(link); | |
269 | ||
270 | handle = TC_H_MAJ(handle); | |
271 | ||
3a67b8bb | 272 | SET_FOREACH(qdisc, link->qdiscs) { |
7ec18462 YW |
273 | if (qdisc->handle != handle) |
274 | continue; | |
275 | ||
276 | if (qdisc->parent != parent) | |
277 | continue; | |
278 | ||
279 | if (qdisc->source == NETWORK_CONFIG_SOURCE_FOREIGN) | |
280 | continue; | |
281 | ||
282 | if (!qdisc_exists(qdisc)) | |
283 | continue; | |
284 | ||
285 | if (kind && !streq_ptr(kind, qdisc_get_tca_kind(qdisc))) | |
286 | continue; | |
287 | ||
288 | if (ret) | |
289 | *ret = qdisc; | |
290 | return 0; | |
291 | } | |
292 | ||
293 | return -ENOENT; | |
294 | } | |
295 | ||
80d62d4f | 296 | static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Request *req, Link *link, QDisc *qdisc) { |
0f5bd7fe SS |
297 | int r; |
298 | ||
80d62d4f | 299 | assert(m); |
0f5bd7fe | 300 | assert(link); |
0f5bd7fe SS |
301 | |
302 | r = sd_netlink_message_get_errno(m); | |
303 | if (r < 0 && r != -EEXIST) { | |
4c272401 | 304 | log_link_message_error_errno(link, m, r, "Could not set QDisc"); |
4ecdcb07 | 305 | link_enter_failed(link); |
0f5bd7fe SS |
306 | return 1; |
307 | } | |
308 | ||
34658df2 YW |
309 | if (link->tc_messages == 0) { |
310 | log_link_debug(link, "Traffic control configured"); | |
311 | link->tc_configured = true; | |
4ecdcb07 YW |
312 | link_check_ready(link); |
313 | } | |
314 | ||
0f5bd7fe SS |
315 | return 1; |
316 | } | |
317 | ||
f1eef367 | 318 | static int qdisc_configure(QDisc *qdisc, Link *link, Request *req) { |
8c14846e | 319 | _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *m = NULL; |
0f5bd7fe SS |
320 | int r; |
321 | ||
3a67b8bb | 322 | assert(qdisc); |
0f5bd7fe SS |
323 | assert(link); |
324 | assert(link->manager); | |
325 | assert(link->manager->rtnl); | |
326 | assert(link->ifindex > 0); | |
f1eef367 | 327 | assert(req); |
0f5bd7fe | 328 | |
1dec9d81 YW |
329 | log_qdisc_debug(qdisc, link, "Configuring"); |
330 | ||
8c14846e | 331 | r = sd_rtnl_message_new_traffic_control(link->manager->rtnl, &m, RTM_NEWQDISC, |
f50b93fe | 332 | link->ifindex, qdisc->handle, qdisc->parent); |
0f5bd7fe | 333 | if (r < 0) |
8c14846e | 334 | return r; |
0f5bd7fe | 335 | |
8c14846e | 336 | r = sd_netlink_message_append_string(m, TCA_KIND, qdisc_get_tca_kind(qdisc)); |
b3208e0f YW |
337 | if (r < 0) |
338 | return r; | |
4e5ef149 | 339 | |
b3208e0f | 340 | if (QDISC_VTABLE(qdisc) && QDISC_VTABLE(qdisc)->fill_message) { |
8c14846e | 341 | r = QDISC_VTABLE(qdisc)->fill_message(link, qdisc, m); |
6edfb1f5 | 342 | if (r < 0) |
907e277f | 343 | return r; |
6edfb1f5 SS |
344 | } |
345 | ||
80d62d4f | 346 | return request_call_netlink_async(link->manager->rtnl, m, req); |
0f5bd7fe SS |
347 | } |
348 | ||
3a67b8bb | 349 | static bool qdisc_is_ready_to_configure(QDisc *qdisc, Link *link) { |
1dec9d81 | 350 | assert(qdisc); |
3a67b8bb YW |
351 | assert(link); |
352 | ||
353 | if (!IN_SET(link->state, LINK_STATE_CONFIGURING, LINK_STATE_CONFIGURED)) | |
354 | return false; | |
1dec9d81 | 355 | |
21261ea2 YW |
356 | /* TC_H_CLSACT == TC_H_INGRESS */ |
357 | if (!IN_SET(qdisc->parent, TC_H_ROOT, TC_H_CLSACT) && | |
358 | link_find_tclass(link, qdisc->parent, NULL) < 0) | |
359 | return false; | |
360 | ||
361 | if (QDISC_VTABLE(qdisc) && | |
362 | QDISC_VTABLE(qdisc)->is_ready && | |
363 | QDISC_VTABLE(qdisc)->is_ready(qdisc, link) <= 0) | |
364 | return false; | |
7ec18462 | 365 | |
21261ea2 | 366 | return true; |
1dec9d81 YW |
367 | } |
368 | ||
09d09207 | 369 | static int qdisc_process_request(Request *req, Link *link, QDisc *qdisc) { |
3a67b8bb YW |
370 | int r; |
371 | ||
372 | assert(req); | |
ff51134c YW |
373 | assert(link); |
374 | assert(qdisc); | |
3a67b8bb YW |
375 | |
376 | if (!qdisc_is_ready_to_configure(qdisc, link)) | |
377 | return 0; | |
378 | ||
f1eef367 | 379 | r = qdisc_configure(qdisc, link, req); |
3a67b8bb YW |
380 | if (r < 0) |
381 | return log_link_warning_errno(link, r, "Failed to configure QDisc: %m"); | |
382 | ||
383 | qdisc_enter_configuring(qdisc); | |
384 | return 1; | |
385 | } | |
386 | ||
1dec9d81 YW |
387 | int link_request_qdisc(Link *link, QDisc *qdisc) { |
388 | QDisc *existing; | |
389 | int r; | |
390 | ||
391 | assert(link); | |
392 | assert(qdisc); | |
393 | ||
394 | if (qdisc_get(link, qdisc, &existing) < 0) { | |
395 | _cleanup_(qdisc_freep) QDisc *tmp = NULL; | |
396 | ||
397 | r = qdisc_dup(qdisc, &tmp); | |
398 | if (r < 0) | |
399 | return log_oom(); | |
400 | ||
401 | r = qdisc_add(link, tmp); | |
402 | if (r < 0) | |
403 | return log_link_warning_errno(link, r, "Failed to store QDisc: %m"); | |
404 | ||
405 | existing = TAKE_PTR(tmp); | |
406 | } else | |
407 | existing->source = qdisc->source; | |
408 | ||
409 | log_qdisc_debug(existing, link, "Requesting"); | |
09d09207 YW |
410 | r = link_queue_request_safe(link, REQUEST_TYPE_TC_QDISC, |
411 | existing, NULL, | |
412 | qdisc_hash_func, | |
413 | qdisc_compare_func, | |
414 | qdisc_process_request, | |
415 | &link->tc_messages, | |
416 | qdisc_handler, | |
417 | NULL); | |
1dec9d81 YW |
418 | if (r < 0) |
419 | return log_link_warning_errno(link, r, "Failed to request QDisc: %m"); | |
420 | if (r == 0) | |
421 | return 0; | |
422 | ||
423 | qdisc_enter_requesting(existing); | |
424 | return 1; | |
425 | } | |
426 | ||
828a81a9 YW |
427 | int manager_rtnl_process_qdisc(sd_netlink *rtnl, sd_netlink_message *message, Manager *m) { |
428 | _cleanup_(qdisc_freep) QDisc *tmp = NULL; | |
429 | QDisc *qdisc = NULL; | |
430 | Link *link; | |
431 | uint16_t type; | |
432 | int ifindex, r; | |
433 | ||
434 | assert(rtnl); | |
435 | assert(message); | |
436 | assert(m); | |
437 | ||
438 | if (sd_netlink_message_is_error(message)) { | |
439 | r = sd_netlink_message_get_errno(message); | |
440 | if (r < 0) | |
441 | log_message_warning_errno(message, r, "rtnl: failed to receive QDisc message, ignoring"); | |
442 | ||
443 | return 0; | |
444 | } | |
445 | ||
446 | r = sd_netlink_message_get_type(message, &type); | |
447 | if (r < 0) { | |
448 | log_warning_errno(r, "rtnl: could not get message type, ignoring: %m"); | |
449 | return 0; | |
450 | } else if (!IN_SET(type, RTM_NEWQDISC, RTM_DELQDISC)) { | |
451 | log_warning("rtnl: received unexpected message type %u when processing QDisc, ignoring.", type); | |
452 | return 0; | |
453 | } | |
454 | ||
455 | r = sd_rtnl_message_traffic_control_get_ifindex(message, &ifindex); | |
456 | if (r < 0) { | |
457 | log_warning_errno(r, "rtnl: could not get ifindex from message, ignoring: %m"); | |
458 | return 0; | |
459 | } else if (ifindex <= 0) { | |
460 | log_warning("rtnl: received QDisc message with invalid ifindex %d, ignoring.", ifindex); | |
461 | return 0; | |
462 | } | |
463 | ||
464 | if (link_get_by_index(m, ifindex, &link) < 0) { | |
465 | if (!m->enumerating) | |
466 | log_warning("rtnl: received QDisc for link '%d' we don't know about, ignoring.", ifindex); | |
467 | return 0; | |
468 | } | |
469 | ||
470 | r = qdisc_new(_QDISC_KIND_INVALID, &tmp); | |
471 | if (r < 0) | |
472 | return log_oom(); | |
473 | ||
474 | r = sd_rtnl_message_traffic_control_get_handle(message, &tmp->handle); | |
475 | if (r < 0) { | |
476 | log_link_warning_errno(link, r, "rtnl: received QDisc message without handle, ignoring: %m"); | |
477 | return 0; | |
478 | } | |
479 | ||
480 | r = sd_rtnl_message_traffic_control_get_parent(message, &tmp->parent); | |
481 | if (r < 0) { | |
482 | log_link_warning_errno(link, r, "rtnl: received QDisc message without parent, ignoring: %m"); | |
483 | return 0; | |
484 | } | |
485 | ||
486 | r = sd_netlink_message_read_string_strdup(message, TCA_KIND, &tmp->tca_kind); | |
487 | if (r < 0) { | |
488 | log_link_warning_errno(link, r, "rtnl: received QDisc message without kind, ignoring: %m"); | |
489 | return 0; | |
490 | } | |
491 | ||
492 | (void) qdisc_get(link, tmp, &qdisc); | |
493 | ||
494 | switch (type) { | |
495 | case RTM_NEWQDISC: | |
496 | if (qdisc) { | |
497 | qdisc_enter_configured(qdisc); | |
498 | log_qdisc_debug(qdisc, link, "Received remembered"); | |
499 | } else { | |
500 | qdisc_enter_configured(tmp); | |
501 | log_qdisc_debug(tmp, link, "Received new"); | |
502 | ||
503 | r = qdisc_add(link, tmp); | |
504 | if (r < 0) { | |
505 | log_link_warning_errno(link, r, "Failed to remember QDisc, ignoring: %m"); | |
506 | return 0; | |
507 | } | |
508 | ||
509 | qdisc = TAKE_PTR(tmp); | |
510 | } | |
511 | ||
512 | break; | |
513 | ||
514 | case RTM_DELQDISC: | |
515 | if (qdisc) { | |
516 | qdisc_enter_removed(qdisc); | |
517 | if (qdisc->state == 0) { | |
518 | log_qdisc_debug(qdisc, link, "Forgetting"); | |
519 | qdisc_free(qdisc); | |
520 | } else | |
521 | log_qdisc_debug(qdisc, link, "Removed"); | |
522 | } else | |
523 | log_qdisc_debug(tmp, link, "Kernel removed unknown"); | |
524 | ||
525 | break; | |
526 | ||
527 | default: | |
528 | assert_not_reached(); | |
529 | } | |
530 | ||
531 | return 1; | |
532 | } | |
533 | ||
3a67b8bb | 534 | static int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact) { |
dcfc23ae | 535 | int r; |
b2340fbb | 536 | |
8efb93f0 YW |
537 | assert(qdisc); |
538 | assert(has_root); | |
539 | assert(has_clsact); | |
540 | ||
541 | if (section_is_invalid(qdisc->section)) | |
542 | return -EINVAL; | |
543 | ||
e8c17dc0 YW |
544 | if (QDISC_VTABLE(qdisc) && QDISC_VTABLE(qdisc)->verify) { |
545 | r = QDISC_VTABLE(qdisc)->verify(qdisc); | |
dcfc23ae YW |
546 | if (r < 0) |
547 | return r; | |
548 | } | |
549 | ||
8efb93f0 YW |
550 | if (qdisc->parent == TC_H_ROOT) { |
551 | if (*has_root) | |
552 | return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), | |
59bae425 YW |
553 | "%s: More than one root qdisc section is defined. " |
554 | "Ignoring the qdisc section from line %u.", | |
8efb93f0 YW |
555 | qdisc->section->filename, qdisc->section->line); |
556 | *has_root = true; | |
d8081020 | 557 | } else if (qdisc->parent == TC_H_CLSACT) { /* TC_H_CLSACT == TC_H_INGRESS */ |
8efb93f0 YW |
558 | if (*has_clsact) |
559 | return log_warning_errno(SYNTHETIC_ERRNO(EINVAL), | |
59bae425 YW |
560 | "%s: More than one clsact or ingress qdisc section is defined. " |
561 | "Ignoring the qdisc section from line %u.", | |
8efb93f0 YW |
562 | qdisc->section->filename, qdisc->section->line); |
563 | *has_clsact = true; | |
564 | } | |
565 | ||
566 | return 0; | |
567 | } | |
568 | ||
3a67b8bb YW |
569 | void network_drop_invalid_qdisc(Network *network) { |
570 | bool has_root = false, has_clsact = false; | |
571 | QDisc *qdisc; | |
572 | ||
573 | assert(network); | |
574 | ||
575 | HASHMAP_FOREACH(qdisc, network->qdiscs_by_section) | |
576 | if (qdisc_section_verify(qdisc, &has_root, &has_clsact) < 0) | |
577 | qdisc_free(qdisc); | |
578 | } | |
579 | ||
18de0969 | 580 | int config_parse_qdisc_parent( |
0f5bd7fe SS |
581 | const char *unit, |
582 | const char *filename, | |
583 | unsigned line, | |
584 | const char *section, | |
585 | unsigned section_line, | |
586 | const char *lvalue, | |
587 | int ltype, | |
588 | const char *rvalue, | |
589 | void *data, | |
590 | void *userdata) { | |
591 | ||
edc54f2f | 592 | _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; |
99534007 | 593 | Network *network = ASSERT_PTR(data); |
0f5bd7fe SS |
594 | int r; |
595 | ||
596 | assert(filename); | |
597 | assert(lvalue); | |
598 | assert(rvalue); | |
0f5bd7fe | 599 | |
18de0969 | 600 | r = qdisc_new_static(ltype, network, filename, section_line, &qdisc); |
d96edb2c YW |
601 | if (r == -ENOMEM) |
602 | return log_oom(); | |
603 | if (r < 0) { | |
604 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
605 | "More than one kind of queueing discipline, ignoring assignment: %m"); | |
606 | return 0; | |
607 | } | |
0f5bd7fe | 608 | |
55c6f705 | 609 | if (streq(rvalue, "root")) |
0f5bd7fe | 610 | qdisc->parent = TC_H_ROOT; |
55c6f705 | 611 | else if (streq(rvalue, "clsact")) { |
0f5bd7fe | 612 | qdisc->parent = TC_H_CLSACT; |
d8081020 SS |
613 | qdisc->handle = TC_H_MAKE(TC_H_CLSACT, 0); |
614 | } else if (streq(rvalue, "ingress")) { | |
615 | qdisc->parent = TC_H_INGRESS; | |
616 | qdisc->handle = TC_H_MAKE(TC_H_INGRESS, 0); | |
617 | } else { | |
2a096331 YW |
618 | r = parse_handle(rvalue, &qdisc->parent); |
619 | if (r < 0) { | |
d96edb2c | 620 | log_syntax(unit, LOG_WARNING, filename, line, r, |
2a096331 YW |
621 | "Failed to parse 'Parent=', ignoring assignment: %s", |
622 | rvalue); | |
623 | return 0; | |
624 | } | |
0f5bd7fe SS |
625 | } |
626 | ||
2a096331 | 627 | if (STR_IN_SET(rvalue, "clsact", "ingress")) { |
d8081020 SS |
628 | r = free_and_strdup(&qdisc->tca_kind, rvalue); |
629 | if (r < 0) | |
630 | return log_oom(); | |
2a096331 YW |
631 | } else |
632 | qdisc->tca_kind = mfree(qdisc->tca_kind); | |
d8081020 | 633 | |
0132453c | 634 | TAKE_PTR(qdisc); |
0f5bd7fe SS |
635 | |
636 | return 0; | |
637 | } | |
d8b2396d SS |
638 | |
639 | int config_parse_qdisc_handle( | |
640 | const char *unit, | |
641 | const char *filename, | |
642 | unsigned line, | |
643 | const char *section, | |
644 | unsigned section_line, | |
645 | const char *lvalue, | |
646 | int ltype, | |
647 | const char *rvalue, | |
648 | void *data, | |
649 | void *userdata) { | |
650 | ||
651 | _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL; | |
99534007 | 652 | Network *network = ASSERT_PTR(data); |
d8b2396d SS |
653 | uint16_t n; |
654 | int r; | |
655 | ||
656 | assert(filename); | |
657 | assert(lvalue); | |
658 | assert(rvalue); | |
d8b2396d SS |
659 | |
660 | r = qdisc_new_static(ltype, network, filename, section_line, &qdisc); | |
d96edb2c YW |
661 | if (r == -ENOMEM) |
662 | return log_oom(); | |
663 | if (r < 0) { | |
664 | log_syntax(unit, LOG_WARNING, filename, line, r, | |
665 | "More than one kind of queueing discipline, ignoring assignment: %m"); | |
666 | return 0; | |
667 | } | |
d8b2396d SS |
668 | |
669 | if (isempty(rvalue)) { | |
670 | qdisc->handle = TC_H_UNSPEC; | |
0132453c | 671 | TAKE_PTR(qdisc); |
d8b2396d SS |
672 | return 0; |
673 | } | |
674 | ||
675 | r = safe_atou16_full(rvalue, 16, &n); | |
676 | if (r < 0) { | |
d96edb2c | 677 | log_syntax(unit, LOG_WARNING, filename, line, r, |
d8b2396d SS |
678 | "Failed to parse 'Handle=', ignoring assignment: %s", |
679 | rvalue); | |
680 | return 0; | |
681 | } | |
682 | ||
683 | qdisc->handle = (uint32_t) n << 16; | |
0132453c | 684 | TAKE_PTR(qdisc); |
d8b2396d SS |
685 | |
686 | return 0; | |
687 | } |