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