]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/network/tc/tclass.c
Merge pull request #15891 from bluca/host_os_release
[thirdparty/systemd.git] / src / network / tc / tclass.c
CommitLineData
4666f63b
YW
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 "set.h"
13#include "string-util.h"
14#include "strv.h"
15#include "tc-util.h"
16#include "tclass.h"
17
18const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX] = {
ad365c5d 19 [TCLASS_KIND_DRR] = &drr_tclass_vtable,
19f86a63 20 [TCLASS_KIND_HTB] = &htb_tclass_vtable,
4d7ddaf9 21 [TCLASS_KIND_QFQ] = &qfq_tclass_vtable,
4666f63b
YW
22};
23
24static int tclass_new(TClassKind kind, TClass **ret) {
58e6c621 25 _cleanup_(tclass_freep) TClass *tclass = NULL;
4666f63b
YW
26 int r;
27
28 tclass = malloc0(tclass_vtable[kind]->object_size);
29 if (!tclass)
30 return -ENOMEM;
31
32 tclass->meta.kind = TC_KIND_TCLASS,
33 tclass->parent = TC_H_ROOT;
34 tclass->kind = kind;
35
36 if (TCLASS_VTABLE(tclass)->init) {
37 r = TCLASS_VTABLE(tclass)->init(tclass);
38 if (r < 0)
39 return r;
40 }
41
42 *ret = TAKE_PTR(tclass);
43
44 return 0;
45}
46
47int tclass_new_static(TClassKind kind, Network *network, const char *filename, unsigned section_line, TClass **ret) {
48 _cleanup_(network_config_section_freep) NetworkConfigSection *n = NULL;
49 _cleanup_(tclass_freep) TClass *tclass = NULL;
50 TrafficControl *existing;
51 int r;
52
53 assert(network);
54 assert(ret);
55 assert(filename);
56 assert(section_line > 0);
57
58 r = network_config_section_new(filename, section_line, &n);
59 if (r < 0)
60 return r;
61
62 existing = ordered_hashmap_get(network->tc_by_section, n);
63 if (existing) {
64 TClass *t;
65
66 if (existing->kind != TC_KIND_TCLASS)
67 return -EINVAL;
68
69 t = TC_TO_TCLASS(existing);
70
71 if (t->kind != kind)
72 return -EINVAL;
73
74 *ret = t;
75 return 0;
76 }
77
78 r = tclass_new(kind, &tclass);
79 if (r < 0)
80 return r;
81
82 tclass->network = network;
83 tclass->section = TAKE_PTR(n);
84
85 r = ordered_hashmap_ensure_allocated(&network->tc_by_section, &network_config_hash_ops);
86 if (r < 0)
87 return r;
88
89 r = ordered_hashmap_put(network->tc_by_section, tclass->section, tclass);
90 if (r < 0)
91 return r;
92
93 *ret = TAKE_PTR(tclass);
94 return 0;
95}
96
97void tclass_free(TClass *tclass) {
98 if (!tclass)
99 return;
100
101 if (tclass->network && tclass->section)
102 ordered_hashmap_remove(tclass->network->tc_by_section, tclass->section);
103
104 network_config_section_free(tclass->section);
105
106 free(tclass);
107}
108
109static int tclass_handler(sd_netlink *rtnl, sd_netlink_message *m, Link *link) {
110 int r;
111
112 assert(link);
113 assert(link->tc_messages > 0);
114 link->tc_messages--;
115
116 if (IN_SET(link->state, LINK_STATE_FAILED, LINK_STATE_LINGER))
117 return 1;
118
119 r = sd_netlink_message_get_errno(m);
120 if (r < 0 && r != -EEXIST) {
121 log_link_message_error_errno(link, m, r, "Could not set TClass");
122 link_enter_failed(link);
123 return 1;
124 }
125
126 if (link->tc_messages == 0) {
127 log_link_debug(link, "Traffic control configured");
128 link->tc_configured = true;
129 link_check_ready(link);
130 }
131
132 return 1;
133}
134
135int tclass_configure(Link *link, TClass *tclass) {
136 _cleanup_(sd_netlink_message_unrefp) sd_netlink_message *req = NULL;
137 int r;
138
139 assert(link);
140 assert(link->manager);
141 assert(link->manager->rtnl);
142 assert(link->ifindex > 0);
143
144 r = sd_rtnl_message_new_tclass(link->manager->rtnl, &req, RTM_NEWTCLASS, AF_UNSPEC, link->ifindex);
145 if (r < 0)
146 return log_link_error_errno(link, r, "Could not create RTM_NEWTCLASS message: %m");
147
148 r = sd_rtnl_message_set_tclass_parent(req, tclass->parent);
149 if (r < 0)
150 return log_link_error_errno(link, r, "Could not create tcm_parent message: %m");
151
152 if (tclass->classid != TC_H_UNSPEC) {
153 r = sd_rtnl_message_set_tclass_handle(req, tclass->classid);
154 if (r < 0)
155 return log_link_error_errno(link, r, "Could not set tcm_handle message: %m");
156 }
157
158 r = sd_netlink_message_append_string(req, TCA_KIND, TCLASS_VTABLE(tclass)->tca_kind);
159 if (r < 0)
160 return log_link_error_errno(link, r, "Could not append TCA_KIND attribute: %m");
161
162 if (TCLASS_VTABLE(tclass)->fill_message) {
163 r = TCLASS_VTABLE(tclass)->fill_message(link, tclass, req);
164 if (r < 0)
165 return r;
166 }
167
168 r = netlink_call_async(link->manager->rtnl, NULL, req, tclass_handler, link_netlink_destroy_callback, link);
169 if (r < 0)
170 return log_link_error_errno(link, r, "Could not send rtnetlink message: %m");
171
172 link_ref(link);
173 link->tc_messages++;
174
175 return 0;
176}
177
178int tclass_section_verify(TClass *tclass) {
179 int r;
180
181 assert(tclass);
182
183 if (section_is_invalid(tclass->section))
184 return -EINVAL;
185
186 if (TCLASS_VTABLE(tclass)->verify) {
187 r = TCLASS_VTABLE(tclass)->verify(tclass);
188 if (r < 0)
189 return r;
190 }
191
192 return 0;
193}
194
195int config_parse_tclass_parent(
196 const char *unit,
197 const char *filename,
198 unsigned line,
199 const char *section,
200 unsigned section_line,
201 const char *lvalue,
202 int ltype,
203 const char *rvalue,
204 void *data,
205 void *userdata) {
206
207 _cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
208 Network *network = data;
209 int r;
210
211 assert(filename);
212 assert(lvalue);
213 assert(rvalue);
214 assert(data);
215
216 r = tclass_new_static(ltype, network, filename, section_line, &tclass);
217 if (r < 0)
218 return r;
219
220 if (streq(rvalue, "root"))
221 tclass->parent = TC_H_ROOT;
222 else {
223 r = parse_handle(rvalue, &tclass->parent);
224 if (r < 0) {
225 log_syntax(unit, LOG_ERR, filename, line, r,
226 "Failed to parse 'Parent=', ignoring assignment: %s",
227 rvalue);
228 return 0;
229 }
230 }
231
232 tclass = NULL;
233
234 return 0;
235}
236
237int config_parse_tclass_classid(
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
249 _cleanup_(tclass_free_or_set_invalidp) TClass *tclass = NULL;
250 Network *network = data;
251 int r;
252
253 assert(filename);
254 assert(lvalue);
255 assert(rvalue);
256 assert(data);
257
258 r = tclass_new_static(ltype, network, filename, section_line, &tclass);
259 if (r < 0)
260 return r;
261
262 if (isempty(rvalue)) {
263 tclass->classid = TC_H_UNSPEC;
264 tclass = NULL;
265 return 0;
266 }
267
268 r = parse_handle(rvalue, &tclass->classid);
269 if (r < 0) {
270 log_syntax(unit, LOG_ERR, filename, line, r,
271 "Failed to parse 'ClassId=', ignoring assignment: %s",
272 rvalue);
273 return 0;
274 }
275
276 tclass = NULL;
277
278 return 0;
279}