]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/tc/qdisc.c
network: update log message
[thirdparty/systemd.git] / src / network / tc / qdisc.c
CommitLineData
0f5bd7fe
SS
1/* SPDX-License-Identifier: LGPL-2.1+
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"
10#include "networkd-manager.h"
11#include "parse-util.h"
12#include "qdisc.h"
13#include "set.h"
14#include "string-util.h"
0f5bd7fe 15
e8c17dc0 16const QDiscVTable * const qdisc_vtable[_QDISC_KIND_MAX] = {
a9a5d632 17 [QDISC_KIND_CODEL] = &codel_vtable,
7234b915 18 [QDISC_KIND_FQ] = &fq_vtable,
e8c17dc0
YW
19 [QDISC_KIND_FQ_CODEL] = &fq_codel_vtable,
20 [QDISC_KIND_NETEM] = &netem_vtable,
21 [QDISC_KIND_SFQ] = &sfq_vtable,
22 [QDISC_KIND_TBF] = &tbf_vtable,
23};
24
25static int qdisc_new(QDiscKind kind, QDisc **ret) {
edc54f2f 26 QDisc *qdisc;
ac810b75 27 int r;
0f5bd7fe 28
e8c17dc0
YW
29 if (kind == _QDISC_KIND_INVALID) {
30 qdisc = new(QDisc, 1);
31 if (!qdisc)
32 return -ENOMEM;
33
34 *qdisc = (QDisc) {
35 .family = AF_UNSPEC,
36 .parent = TC_H_ROOT,
37 .kind = kind,
38 };
39 } else {
40 qdisc = malloc0(qdisc_vtable[kind]->object_size);
41 if (!qdisc)
42 return -ENOMEM;
43
44 qdisc->family = AF_UNSPEC;
45 qdisc->parent = TC_H_ROOT;
46 qdisc->kind = kind;
ac810b75
YW
47
48 if (QDISC_VTABLE(qdisc)->init) {
49 r = QDISC_VTABLE(qdisc)->init(qdisc);
50 if (r < 0)
51 return r;
52 }
e8c17dc0 53 }
0f5bd7fe
SS
54
55 *ret = TAKE_PTR(qdisc);
56
57 return 0;
58}
59
e8c17dc0 60int qdisc_new_static(QDiscKind kind, Network *network, const char *filename, unsigned section_line, QDisc **ret) {
0f5bd7fe 61 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
edc54f2f 62 _cleanup_(qdisc_freep) QDisc *qdisc = NULL;
e8c17dc0 63 QDisc *existing;
0f5bd7fe
SS
64 int r;
65
66 assert(network);
67 assert(ret);
e8c17dc0
YW
68 assert(filename);
69 assert(section_line > 0);
0f5bd7fe 70
e8c17dc0
YW
71 r = network_config_section_new(filename, section_line, &n);
72 if (r < 0)
73 return r;
0f5bd7fe 74
e8c17dc0
YW
75 existing = ordered_hashmap_get(network->qdiscs_by_section, n);
76 if (existing) {
77 if (existing->kind != _QDISC_KIND_INVALID &&
78 kind != _QDISC_KIND_INVALID &&
79 existing->kind != kind)
80 return -EINVAL;
0f5bd7fe 81
e8c17dc0
YW
82 if (existing->kind == kind || kind == _QDISC_KIND_INVALID) {
83 *ret = existing;
0f5bd7fe
SS
84 return 0;
85 }
86 }
87
e8c17dc0 88 r = qdisc_new(kind, &qdisc);
0f5bd7fe
SS
89 if (r < 0)
90 return r;
91
e8c17dc0
YW
92 if (existing) {
93 qdisc->family = existing->family;
94 qdisc->handle = existing->handle;
95 qdisc->parent = existing->parent;
96 qdisc->tca_kind = TAKE_PTR(existing->tca_kind);
0f5bd7fe 97
e8c17dc0
YW
98 qdisc_free(ordered_hashmap_remove(network->qdiscs_by_section, n));
99 }
0f5bd7fe 100
e8c17dc0
YW
101 qdisc->network = network;
102 qdisc->section = TAKE_PTR(n);
0f5bd7fe 103
e8c17dc0
YW
104 r = ordered_hashmap_ensure_allocated(&network->qdiscs_by_section, &network_config_hash_ops);
105 if (r < 0)
106 return r;
0f5bd7fe 107
e8c17dc0
YW
108 r = ordered_hashmap_put(network->qdiscs_by_section, qdisc->section, qdisc);
109 if (r < 0)
110 return r;
0f5bd7fe 111
e8c17dc0 112 *ret = TAKE_PTR(qdisc);
0f5bd7fe
SS
113 return 0;
114}
115
edc54f2f 116void qdisc_free(QDisc *qdisc) {
0f5bd7fe
SS
117 if (!qdisc)
118 return;
119
120 if (qdisc->network && qdisc->section)
121 ordered_hashmap_remove(qdisc->network->qdiscs_by_section, qdisc->section);
122
123 network_config_section_free(qdisc->section);
124
d8081020 125 free(qdisc->tca_kind);
0f5bd7fe
SS
126 free(qdisc);
127}
128
129static int qdisc_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
130 int r;
131
132 assert(link);
133 assert(link->qdisc_messages > 0);
134 link->qdisc_messages--;
135
136 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
137 return 1;
138
139 r = sd_netlink_message_get_errno(m);
140 if (r < 0 && r != -EEXIST) {
4c272401 141 log_link_message_error_errno(link, m, r, "Could not set QDisc");
4ecdcb07 142 link_enter_failed(link);
0f5bd7fe
SS
143 return 1;
144 }
145
6d62ec61 146 if (link->qdisc_messages == 0) {
edc54f2f 147 log_link_debug(link, "QDisc configured");
4ecdcb07
YW
148 link->qdiscs_configured = true;
149 link_check_ready(link);
150 }
151
0f5bd7fe
SS
152 return 1;
153}
154
edc54f2f 155int qdisc_configure(Link *link, QDisc *qdisc) {
0f5bd7fe
SS
156 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
157 int r;
158
159 assert(link);
160 assert(link->manager);
161 assert(link->manager->rtnl);
162 assert(link->ifindex > 0);
163
164 r = sd_rtnl_message_new_qdisc(link->manager->rtnl, &req, RTM_NEWQDISC, qdisc->family, link->ifindex);
165 if (r < 0)
166 return log_link_error_errno(link, r, "Could not create RTM_NEWQDISC message: %m");
167
168 r = sd_rtnl_message_set_qdisc_parent(req, qdisc->parent);
169 if (r < 0)
170 return log_link_error_errno(link, r, "Could not create tcm_parent message: %m");
171
d8081020
SS
172 if (qdisc->handle != TC_H_UNSPEC) {
173 r = sd_rtnl_message_set_qdisc_handle(req, qdisc->handle);
0f5bd7fe
SS
174 if (r < 0)
175 return log_link_error_errno(link, r, "Could not set tcm_handle message: %m");
0f5bd7fe
SS
176 }
177
e8c17dc0
YW
178 if (QDISC_VTABLE(qdisc)) {
179 r = sd_netlink_message_append_string(req, TCA_KIND, QDISC_VTABLE(qdisc)->tca_kind);
0f5bd7fe 180 if (r < 0)
e8c17dc0 181 return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
4e5ef149 182
e8c17dc0 183 r = QDISC_VTABLE(qdisc)->fill_message(link, qdisc, req);
4e5ef149
SS
184 if (r < 0)
185 return r;
e8c17dc0
YW
186 } else {
187 r = sd_netlink_message_append_string(req, TCA_KIND, qdisc->tca_kind);
6edfb1f5
SS
188 if (r < 0)
189 return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
190 }
191
0f5bd7fe
SS
192 r = netlink_call_async(link->manager->rtnl, NULL, req, qdisc_handler, link_netlink_destroy_callback, link);
193 if (r < 0)
194 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
195
196 link_ref(link);
197 link->qdisc_messages++;
198
199 return 0;
200}
201
edc54f2f 202int qdisc_section_verify(QDisc *qdisc, bool *has_root, bool *has_clsact) {
dcfc23ae 203 int r;
b2340fbb 204
8efb93f0
YW
205 assert(qdisc);
206 assert(has_root);
207 assert(has_clsact);
208
209 if (section_is_invalid(qdisc->section))
210 return -EINVAL;
211
e8c17dc0
YW
212 if (QDISC_VTABLE(qdisc) && QDISC_VTABLE(qdisc)->verify) {
213 r = QDISC_VTABLE(qdisc)->verify(qdisc);
dcfc23ae
YW
214 if (r < 0)
215 return r;
216 }
217
8efb93f0
YW
218 if (qdisc->parent == TC_H_ROOT) {
219 if (*has_root)
220 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
59bae425
YW
221 "%s: More than one root qdisc section is defined. "
222 "Ignoring the qdisc section from line %u.",
8efb93f0
YW
223 qdisc->section->filename, qdisc->section->line);
224 *has_root = true;
d8081020 225 } else if (qdisc->parent == TC_H_CLSACT) { /* TC_H_CLSACT == TC_H_INGRESS */
8efb93f0
YW
226 if (*has_clsact)
227 return log_warning_errno(SYNTHETIC_ERRNO(EINVAL),
59bae425
YW
228 "%s: More than one clsact or ingress qdisc section is defined. "
229 "Ignoring the qdisc section from line %u.",
8efb93f0
YW
230 qdisc->section->filename, qdisc->section->line);
231 *has_clsact = true;
232 }
233
234 return 0;
235}
236
18de0969 237int config_parse_qdisc_parent(
0f5bd7fe
SS
238 const char *unit,
239 const char *filename,
240 unsigned line,
241 const char *section,
242 unsigned section_line,
243 const char *lvalue,
244 int ltype,
245 const char *rvalue,
246 void *data,
247 void *userdata) {
248
edc54f2f 249 _cleanup_(qdisc_free_or_set_invalidp) QDisc *qdisc = NULL;
0f5bd7fe
SS
250 Network *network = data;
251 int r;
252
253 assert(filename);
254 assert(lvalue);
255 assert(rvalue);
256 assert(data);
257
18de0969 258 r = qdisc_new_static(ltype, network, filename, section_line, &qdisc);
0f5bd7fe
SS
259 if (r < 0)
260 return r;
261
d8081020 262 if (streq(rvalue, "root")) {
0f5bd7fe 263 qdisc->parent = TC_H_ROOT;
d8081020
SS
264 qdisc->handle = TC_H_UNSPEC;
265 } else if (streq(rvalue, "clsact")) {
0f5bd7fe 266 qdisc->parent = TC_H_CLSACT;
d8081020
SS
267 qdisc->handle = TC_H_MAKE(TC_H_CLSACT, 0);
268 } else if (streq(rvalue, "ingress")) {
269 qdisc->parent = TC_H_INGRESS;
270 qdisc->handle = TC_H_MAKE(TC_H_INGRESS, 0);
271 } else {
0f5bd7fe 272 log_syntax(unit, LOG_ERR, filename, line, r,
d8081020 273 "Failed to parse 'Parent=', ignoring assignment: %s",
0f5bd7fe
SS
274 rvalue);
275 return 0;
276 }
277
d8081020
SS
278 if (streq(rvalue, "root"))
279 qdisc->tca_kind = mfree(qdisc->tca_kind);
280 else {
281 r = free_and_strdup(&qdisc->tca_kind, rvalue);
282 if (r < 0)
283 return log_oom();
284 }
285
0f5bd7fe
SS
286 qdisc = NULL;
287
288 return 0;
289}