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