]>
Commit | Line | Data |
---|---|---|
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 | ||
18 | const TClassVTable * const tclass_vtable[_TCLASS_KIND_MAX] = { | |
19f86a63 | 19 | [TCLASS_KIND_HTB] = &htb_tclass_vtable, |
4666f63b YW |
20 | }; |
21 | ||
22 | static 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 | ||
45 | int 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 | ||
95 | void 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 | ||
107 | static 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 | ||
133 | int 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 | ||
176 | int 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 | ||
193 | int 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 | ||
235 | int 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 | } |