Line data Source code
1 : /*
2 : * Zebra Traffic Control (TC) main handling.
3 : *
4 : * Copyright (C) 2022 Shichu Yang
5 : *
6 : * This program is free software; you can redistribute it and/or modify it
7 : * under the terms of the GNU General Public License as published by the Free
8 : * Software Foundation; either version 2 of the License, or (at your option)
9 : * any later version.
10 : *
11 : * This program is distributed in the hope that it will be useful, but WITHOUT
12 : * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 : * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
14 : * more details.
15 : *
16 : * You should have received a copy of the GNU General Public License along
17 : * with this program; see the file COPYING; if not, write to the Free Software
18 : * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
19 : */
20 :
21 : #include <zebra.h>
22 :
23 : #include <jhash.h>
24 : #include <hash.h>
25 : #include <memory.h>
26 : #include <hook.h>
27 :
28 : #include "zebra/zebra_router.h"
29 : #include "zebra/zebra_dplane.h"
30 : #include "zebra/zebra_tc.h"
31 : #include "zebra/debug.h"
32 :
33 12 : DEFINE_MTYPE_STATIC(ZEBRA, TC_QDISC, "TC queue discipline");
34 12 : DEFINE_MTYPE_STATIC(ZEBRA, TC_CLASS, "TC class");
35 12 : DEFINE_MTYPE_STATIC(ZEBRA, TC_FILTER, "TC filter");
36 :
37 : const struct message tc_qdisc_kinds[] = {
38 : {TC_QDISC_HTB, "htb"},
39 : {TC_QDISC_NOQUEUE, "noqueue"},
40 : {0},
41 : };
42 :
43 : const struct message tc_filter_kinds[] = {
44 : {TC_FILTER_BPF, "bpf"},
45 : {TC_FILTER_FLOW, "flow"},
46 : {TC_FILTER_FLOWER, "flower"},
47 : {TC_FILTER_U32, "u32"},
48 : {0},
49 : };
50 :
51 : const struct message *tc_class_kinds = tc_qdisc_kinds;
52 :
53 10 : static uint32_t lookup_key(const struct message *mz, const char *msg,
54 : uint32_t nf)
55 : {
56 10 : static struct message nt = {0};
57 10 : uint32_t rz = nf ? nf : UINT32_MAX;
58 10 : const struct message *pnt;
59 :
60 20 : for (pnt = mz; memcmp(pnt, &nt, sizeof(struct message)); pnt++)
61 20 : if (strcmp(pnt->str, msg) == 0) {
62 10 : rz = pnt->key;
63 10 : break;
64 : }
65 10 : return rz;
66 : }
67 :
68 0 : const char *tc_qdisc_kind2str(uint32_t type)
69 : {
70 0 : return lookup_msg(tc_qdisc_kinds, type, "Unrecognized QDISC Type");
71 : }
72 :
73 10 : enum tc_qdisc_kind tc_qdisc_str2kind(const char *type)
74 : {
75 10 : return lookup_key(tc_qdisc_kinds, type, TC_QDISC_UNSPEC);
76 : }
77 :
78 0 : uint32_t zebra_tc_qdisc_hash_key(const void *arg)
79 : {
80 0 : const struct zebra_tc_qdisc *qdisc;
81 0 : uint32_t key;
82 :
83 0 : qdisc = arg;
84 :
85 0 : key = jhash_1word(qdisc->qdisc.ifindex, 0);
86 :
87 0 : return key;
88 : }
89 :
90 0 : bool zebra_tc_qdisc_hash_equal(const void *arg1, const void *arg2)
91 : {
92 0 : const struct zebra_tc_qdisc *q1, *q2;
93 :
94 0 : q1 = (const struct zebra_tc_qdisc *)arg1;
95 0 : q2 = (const struct zebra_tc_qdisc *)arg2;
96 :
97 0 : if (q1->qdisc.ifindex != q2->qdisc.ifindex)
98 0 : return false;
99 :
100 : return true;
101 : }
102 :
103 : struct tc_qdisc_ifindex_lookup {
104 : struct zebra_tc_qdisc *qdisc;
105 : ifindex_t ifindex;
106 : };
107 :
108 :
109 0 : static int tc_qdisc_lookup_ifindex_walker(struct hash_bucket *b, void *data)
110 : {
111 0 : struct tc_qdisc_ifindex_lookup *lookup = data;
112 0 : struct zebra_tc_qdisc *qdisc = b->data;
113 :
114 0 : if (lookup->ifindex == qdisc->qdisc.ifindex) {
115 0 : lookup->qdisc = qdisc;
116 0 : return HASHWALK_ABORT;
117 : }
118 :
119 : return HASHWALK_CONTINUE;
120 : }
121 :
122 : static struct zebra_tc_qdisc *
123 0 : tc_qdisc_lookup_ifindex(struct zebra_tc_qdisc *qdisc)
124 : {
125 0 : struct tc_qdisc_ifindex_lookup lookup;
126 :
127 0 : lookup.ifindex = qdisc->qdisc.ifindex;
128 0 : lookup.qdisc = NULL;
129 0 : hash_walk(zrouter.rules_hash, &tc_qdisc_lookup_ifindex_walker, &lookup);
130 :
131 0 : return lookup.qdisc;
132 : }
133 :
134 0 : static void *tc_qdisc_alloc_intern(void *arg)
135 : {
136 0 : struct zebra_tc_qdisc *ztq;
137 0 : struct zebra_tc_qdisc *new;
138 :
139 0 : ztq = (struct zebra_tc_qdisc *)arg;
140 :
141 0 : new = XCALLOC(MTYPE_TC_QDISC, sizeof(*new));
142 :
143 0 : memcpy(new, ztq, sizeof(*ztq));
144 :
145 0 : return new;
146 : }
147 :
148 0 : static struct zebra_tc_qdisc *tc_qdisc_free(struct zebra_tc_qdisc *hash_data,
149 : bool free_data)
150 : {
151 0 : hash_release(zrouter.qdisc_hash, hash_data);
152 :
153 0 : if (free_data) {
154 0 : XFREE(MTYPE_TC_QDISC, hash_data);
155 0 : return NULL;
156 : }
157 :
158 : return hash_data;
159 : }
160 :
161 0 : static struct zebra_tc_qdisc *tc_qdisc_release(struct zebra_tc_qdisc *qdisc,
162 : bool free_data)
163 : {
164 0 : struct zebra_tc_qdisc *lookup;
165 :
166 0 : lookup = hash_lookup(zrouter.qdisc_hash, qdisc);
167 :
168 0 : if (!lookup)
169 : return NULL;
170 :
171 0 : return tc_qdisc_free(lookup, free_data);
172 : }
173 :
174 0 : void zebra_tc_qdisc_install(struct zebra_tc_qdisc *qdisc)
175 : {
176 0 : if (IS_ZEBRA_DEBUG_TC)
177 0 : zlog_debug("%s: install tc qdisc ifindex %d kind %s", __func__,
178 : qdisc->qdisc.ifindex,
179 : tc_qdisc_kind2str(qdisc->qdisc.kind));
180 :
181 0 : struct zebra_tc_qdisc *found;
182 0 : struct zebra_tc_qdisc *old;
183 0 : struct zebra_tc_qdisc *new;
184 :
185 0 : found = tc_qdisc_lookup_ifindex(qdisc);
186 :
187 0 : if (found) {
188 0 : if (!zebra_tc_qdisc_hash_equal(qdisc, found)) {
189 0 : old = tc_qdisc_release(found, false);
190 0 : (void)dplane_tc_qdisc_uninstall(old);
191 0 : new = hash_get(zrouter.qdisc_hash, qdisc,
192 : tc_qdisc_alloc_intern);
193 0 : (void)dplane_tc_qdisc_install(new);
194 0 : XFREE(MTYPE_TC_QDISC, old);
195 : }
196 : } else {
197 0 : new = hash_get(zrouter.qdisc_hash, qdisc,
198 : tc_qdisc_alloc_intern);
199 0 : (void)dplane_tc_qdisc_install(new);
200 : }
201 0 : }
202 :
203 0 : void zebra_tc_qdisc_uninstall(struct zebra_tc_qdisc *qdisc)
204 : {
205 0 : if (IS_ZEBRA_DEBUG_TC)
206 0 : zlog_debug("%s: uninstall tc qdisc ifindex %d kind %s",
207 : __func__, qdisc->qdisc.ifindex,
208 : tc_qdisc_kind2str(qdisc->qdisc.kind));
209 :
210 0 : (void)dplane_tc_qdisc_uninstall(qdisc);
211 :
212 0 : if (tc_qdisc_release(qdisc, true))
213 0 : zlog_debug("%s: tc qdisc being deleted we know nothing about",
214 : __func__);
215 0 : }
216 :
217 0 : uint32_t zebra_tc_class_hash_key(const void *arg)
218 : {
219 0 : const struct zebra_tc_class *class;
220 0 : uint32_t key;
221 :
222 0 : class = arg;
223 :
224 0 : key = jhash_2words(class->class.ifindex, class->class.handle, 0);
225 :
226 0 : return key;
227 : }
228 :
229 0 : bool zebra_tc_class_hash_equal(const void *arg1, const void *arg2)
230 : {
231 0 : const struct zebra_tc_class *c1, *c2;
232 :
233 0 : c1 = (const struct zebra_tc_class *)arg1;
234 0 : c2 = (const struct zebra_tc_class *)arg2;
235 :
236 0 : if (c1->class.ifindex != c2->class.ifindex)
237 : return false;
238 :
239 0 : if (c1->class.handle != c2->class.handle)
240 0 : return false;
241 :
242 : return true;
243 : }
244 :
245 0 : static void *tc_class_alloc_intern(void *arg)
246 : {
247 0 : struct zebra_tc_class *class;
248 0 : struct zebra_tc_class *new;
249 :
250 0 : class = (struct zebra_tc_class *)arg;
251 :
252 0 : new = XCALLOC(MTYPE_TC_CLASS, sizeof(*new));
253 :
254 0 : memcpy(new, class, sizeof(*class));
255 :
256 0 : return new;
257 : }
258 :
259 0 : static struct zebra_tc_class *tc_class_free(struct zebra_tc_class *hash_data,
260 : bool free_data)
261 : {
262 0 : hash_release(zrouter.class_hash, hash_data);
263 :
264 0 : if (free_data) {
265 0 : XFREE(MTYPE_TC_CLASS, hash_data);
266 0 : return NULL;
267 : }
268 :
269 : return hash_data;
270 : }
271 :
272 0 : static struct zebra_tc_class *tc_class_release(struct zebra_tc_class *class,
273 : bool free_data)
274 : {
275 0 : struct zebra_tc_class *lookup;
276 :
277 0 : lookup = hash_lookup(zrouter.class_hash, class);
278 :
279 0 : if (!lookup)
280 : return NULL;
281 :
282 0 : return tc_class_free(lookup, free_data);
283 : }
284 :
285 0 : void zebra_tc_class_add(struct zebra_tc_class *class)
286 : {
287 0 : if (IS_ZEBRA_DEBUG_TC)
288 0 : zlog_debug(
289 : "%s: add tc class ifindex %d handle %04x:%04x kind %s",
290 : __func__, class->class.ifindex,
291 : (class->class.handle & 0xffff0000u) >> 16,
292 : class->class.handle & 0x0000ffffu,
293 : tc_qdisc_kind2str(class->class.kind));
294 :
295 0 : struct zebra_tc_class *found;
296 0 : struct zebra_tc_class *new;
297 :
298 : /*
299 : * We find the class in the hash by (ifindex, handle) directly, and by
300 : * testing their deep equality to seek out whether it's an update.
301 : *
302 : * Currently deep equality is not checked since it will be okay to
303 : * update the totally same class again.
304 : */
305 0 : found = hash_lookup(zrouter.class_hash, class);
306 0 : new = hash_get(zrouter.class_hash, class, tc_class_alloc_intern);
307 :
308 0 : if (found)
309 0 : (void)dplane_tc_class_update(new);
310 : else
311 0 : (void)dplane_tc_class_add(new);
312 0 : }
313 :
314 0 : void zebra_tc_class_delete(struct zebra_tc_class *class)
315 : {
316 0 : if (IS_ZEBRA_DEBUG_TC)
317 0 : zlog_debug(
318 : "%s: delete tc class ifindex %d handle %04x:%04x kind %s",
319 : __func__, class->class.ifindex,
320 : (class->class.handle & 0xffff0000u) >> 16,
321 : class->class.handle & 0x0000ffffu,
322 : tc_qdisc_kind2str(class->class.kind));
323 :
324 0 : (void)dplane_tc_class_delete(class);
325 :
326 0 : if (tc_class_release(class, true))
327 0 : zlog_debug("%s: tc class being deleted we know nothing about",
328 : __func__);
329 0 : }
330 :
331 0 : const char *tc_filter_kind2str(uint32_t type)
332 : {
333 0 : return lookup_msg(tc_filter_kinds, type, "Unrecognized TFILTER Type");
334 : }
335 :
336 0 : enum tc_qdisc_kind tc_filter_str2kind(const char *type)
337 : {
338 0 : return lookup_key(tc_filter_kinds, type, TC_FILTER_UNSPEC);
339 : }
340 :
341 0 : uint32_t zebra_tc_filter_hash_key(const void *arg)
342 : {
343 0 : const struct zebra_tc_filter *filter;
344 0 : uint32_t key;
345 :
346 0 : filter = arg;
347 :
348 0 : key = jhash_2words(filter->filter.ifindex, filter->filter.handle, 0);
349 :
350 0 : return key;
351 : }
352 :
353 0 : bool zebra_tc_filter_hash_equal(const void *arg1, const void *arg2)
354 : {
355 0 : const struct zebra_tc_filter *f1, *f2;
356 :
357 0 : f1 = (const struct zebra_tc_filter *)arg1;
358 0 : f2 = (const struct zebra_tc_filter *)arg2;
359 :
360 0 : if (f1->filter.ifindex != f2->filter.ifindex)
361 : return false;
362 :
363 0 : if (f1->filter.handle != f2->filter.handle)
364 0 : return false;
365 :
366 : return true;
367 : }
368 :
369 0 : static struct zebra_tc_filter *tc_filter_free(struct zebra_tc_filter *hash_data,
370 : bool free_data)
371 : {
372 0 : hash_release(zrouter.filter_hash, hash_data);
373 :
374 0 : if (free_data) {
375 0 : XFREE(MTYPE_TC_FILTER, hash_data);
376 0 : return NULL;
377 : }
378 :
379 : return hash_data;
380 : }
381 :
382 0 : static struct zebra_tc_filter *tc_filter_release(struct zebra_tc_filter *filter,
383 : bool free_data)
384 : {
385 0 : struct zebra_tc_filter *lookup;
386 :
387 0 : lookup = hash_lookup(zrouter.filter_hash, filter);
388 :
389 0 : if (!lookup)
390 : return NULL;
391 :
392 0 : return tc_filter_free(lookup, free_data);
393 : }
394 :
395 0 : static void *tc_filter_alloc_intern(void *arg)
396 : {
397 0 : struct zebra_tc_filter *ztf;
398 0 : struct zebra_tc_filter *new;
399 :
400 0 : ztf = (struct zebra_tc_filter *)arg;
401 :
402 0 : new = XCALLOC(MTYPE_TC_FILTER, sizeof(*new));
403 :
404 0 : memcpy(new, ztf, sizeof(*ztf));
405 :
406 0 : return new;
407 : }
408 :
409 0 : void zebra_tc_filter_add(struct zebra_tc_filter *filter)
410 : {
411 0 : if (IS_ZEBRA_DEBUG_TC)
412 0 : zlog_debug(
413 : "%s: add tc filter ifindex %d priority %u handle %08x kind %s",
414 : __func__, filter->filter.ifindex,
415 : filter->filter.priority, filter->filter.handle,
416 : tc_filter_kind2str(filter->filter.kind));
417 :
418 0 : struct zebra_tc_filter *found;
419 0 : struct zebra_tc_filter *new;
420 :
421 0 : found = hash_lookup(zrouter.filter_hash, filter);
422 0 : new = hash_get(zrouter.filter_hash, filter, tc_filter_alloc_intern);
423 :
424 0 : if (found)
425 0 : (void)dplane_tc_filter_update(new);
426 : else
427 0 : (void)dplane_tc_filter_add(new);
428 0 : }
429 :
430 0 : void zebra_tc_filter_delete(struct zebra_tc_filter *filter)
431 : {
432 0 : if (IS_ZEBRA_DEBUG_PBR)
433 0 : zlog_debug(
434 : "%s: delete tc filter ifindex %d priority %u handle %08x kind %s",
435 : __func__, filter->filter.ifindex,
436 : filter->filter.priority, filter->filter.handle,
437 : tc_filter_kind2str(filter->filter.kind));
438 :
439 0 : (void)dplane_tc_filter_delete(filter);
440 :
441 0 : if (tc_filter_release(filter, true))
442 0 : zlog_debug("%s: tc filter being deleted we know nothing about",
443 : __func__);
444 0 : }
|