1 // SPDX-License-Identifier: GPL-2.0-or-later
3 * Zebra Traffic Control (TC) main handling.
5 * Copyright (C) 2022 Shichu Yang
15 #include "zebra/zebra_router.h"
16 #include "zebra/zebra_dplane.h"
17 #include "zebra/zebra_tc.h"
18 #include "zebra/debug.h"
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");
24 const struct message tc_qdisc_kinds
[] = {
25 {TC_QDISC_HTB
, "htb"},
26 {TC_QDISC_NOQUEUE
, "noqueue"},
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"},
38 const struct message
*tc_class_kinds
= tc_qdisc_kinds
;
40 static uint32_t lookup_key(const struct message
*mz
, const char *msg
,
43 static struct message nt
= {0};
44 uint32_t rz
= nf
? nf
: UINT32_MAX
;
45 const struct message
*pnt
;
47 for (pnt
= mz
; memcmp(pnt
, &nt
, sizeof(struct message
)); pnt
++)
48 if (strcmp(pnt
->str
, msg
) == 0) {
55 const char *tc_qdisc_kind2str(uint32_t type
)
57 return lookup_msg(tc_qdisc_kinds
, type
, "Unrecognized QDISC Type");
60 enum tc_qdisc_kind
tc_qdisc_str2kind(const char *type
)
62 return lookup_key(tc_qdisc_kinds
, type
, TC_QDISC_UNSPEC
);
65 uint32_t zebra_tc_qdisc_hash_key(const void *arg
)
67 const struct zebra_tc_qdisc
*qdisc
;
72 key
= jhash_1word(qdisc
->qdisc
.ifindex
, 0);
77 bool zebra_tc_qdisc_hash_equal(const void *arg1
, const void *arg2
)
79 const struct zebra_tc_qdisc
*q1
, *q2
;
81 q1
= (const struct zebra_tc_qdisc
*)arg1
;
82 q2
= (const struct zebra_tc_qdisc
*)arg2
;
84 if (q1
->qdisc
.ifindex
!= q2
->qdisc
.ifindex
)
90 struct tc_qdisc_ifindex_lookup
{
91 struct zebra_tc_qdisc
*qdisc
;
96 static int tc_qdisc_lookup_ifindex_walker(struct hash_bucket
*b
, void *data
)
98 struct tc_qdisc_ifindex_lookup
*lookup
= data
;
99 struct zebra_tc_qdisc
*qdisc
= b
->data
;
101 if (lookup
->ifindex
== qdisc
->qdisc
.ifindex
) {
102 lookup
->qdisc
= qdisc
;
103 return HASHWALK_ABORT
;
106 return HASHWALK_CONTINUE
;
109 static struct zebra_tc_qdisc
*
110 tc_qdisc_lookup_ifindex(struct zebra_tc_qdisc
*qdisc
)
112 struct tc_qdisc_ifindex_lookup lookup
;
114 lookup
.ifindex
= qdisc
->qdisc
.ifindex
;
116 hash_walk(zrouter
.rules_hash
, &tc_qdisc_lookup_ifindex_walker
, &lookup
);
121 static void *tc_qdisc_alloc_intern(void *arg
)
123 struct zebra_tc_qdisc
*ztq
;
124 struct zebra_tc_qdisc
*new;
126 ztq
= (struct zebra_tc_qdisc
*)arg
;
128 new = XCALLOC(MTYPE_TC_QDISC
, sizeof(*new));
130 memcpy(new, ztq
, sizeof(*ztq
));
135 static struct zebra_tc_qdisc
*tc_qdisc_free(struct zebra_tc_qdisc
*hash_data
,
138 hash_release(zrouter
.qdisc_hash
, hash_data
);
141 XFREE(MTYPE_TC_QDISC
, hash_data
);
148 static struct zebra_tc_qdisc
*tc_qdisc_release(struct zebra_tc_qdisc
*qdisc
,
151 struct zebra_tc_qdisc
*lookup
;
153 lookup
= hash_lookup(zrouter
.qdisc_hash
, qdisc
);
158 return tc_qdisc_free(lookup
, free_data
);
161 void zebra_tc_qdisc_install(struct zebra_tc_qdisc
*qdisc
)
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
));
168 struct zebra_tc_qdisc
*found
;
169 struct zebra_tc_qdisc
*old
;
170 struct zebra_tc_qdisc
*new;
172 found
= tc_qdisc_lookup_ifindex(qdisc
);
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
);
184 new = hash_get(zrouter
.qdisc_hash
, qdisc
,
185 tc_qdisc_alloc_intern
);
186 (void)dplane_tc_qdisc_install(new);
190 void zebra_tc_qdisc_uninstall(struct zebra_tc_qdisc
*qdisc
)
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
));
197 (void)dplane_tc_qdisc_uninstall(qdisc
);
199 if (tc_qdisc_release(qdisc
, true))
200 zlog_debug("%s: tc qdisc being deleted we know nothing about",
204 uint32_t zebra_tc_class_hash_key(const void *arg
)
206 const struct zebra_tc_class
*class;
211 key
= jhash_2words(class->class.ifindex
, class->class.handle
, 0);
216 bool zebra_tc_class_hash_equal(const void *arg1
, const void *arg2
)
218 const struct zebra_tc_class
*c1
, *c2
;
220 c1
= (const struct zebra_tc_class
*)arg1
;
221 c2
= (const struct zebra_tc_class
*)arg2
;
223 if (c1
->class.ifindex
!= c2
->class.ifindex
)
226 if (c1
->class.handle
!= c2
->class.handle
)
232 static void *tc_class_alloc_intern(void *arg
)
234 struct zebra_tc_class
*class;
235 struct zebra_tc_class
*new;
237 class = (struct zebra_tc_class
*)arg
;
239 new = XCALLOC(MTYPE_TC_CLASS
, sizeof(*new));
241 memcpy(new, class, sizeof(*class));
246 static struct zebra_tc_class
*tc_class_free(struct zebra_tc_class
*hash_data
,
249 hash_release(zrouter
.class_hash
, hash_data
);
252 XFREE(MTYPE_TC_CLASS
, hash_data
);
259 static struct zebra_tc_class
*tc_class_release(struct zebra_tc_class
*class,
262 struct zebra_tc_class
*lookup
;
264 lookup
= hash_lookup(zrouter
.class_hash
, class);
269 return tc_class_free(lookup
, free_data
);
272 void zebra_tc_class_add(struct zebra_tc_class
*class)
274 if (IS_ZEBRA_DEBUG_TC
)
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
));
282 struct zebra_tc_class
*found
;
283 struct zebra_tc_class
*new;
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.
289 * Currently deep equality is not checked since it will be okay to
290 * update the totally same class again.
292 found
= hash_lookup(zrouter
.class_hash
, class);
293 new = hash_get(zrouter
.class_hash
, class, tc_class_alloc_intern
);
296 (void)dplane_tc_class_update(new);
298 (void)dplane_tc_class_add(new);
301 void zebra_tc_class_delete(struct zebra_tc_class
*class)
303 if (IS_ZEBRA_DEBUG_TC
)
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
));
311 (void)dplane_tc_class_delete(class);
313 if (tc_class_release(class, true))
314 zlog_debug("%s: tc class being deleted we know nothing about",
318 const char *tc_filter_kind2str(uint32_t type
)
320 return lookup_msg(tc_filter_kinds
, type
, "Unrecognized TFILTER Type");
323 enum tc_qdisc_kind
tc_filter_str2kind(const char *type
)
325 return lookup_key(tc_filter_kinds
, type
, TC_FILTER_UNSPEC
);
328 uint32_t zebra_tc_filter_hash_key(const void *arg
)
330 const struct zebra_tc_filter
*filter
;
335 key
= jhash_2words(filter
->filter
.ifindex
, filter
->filter
.handle
, 0);
340 bool zebra_tc_filter_hash_equal(const void *arg1
, const void *arg2
)
342 const struct zebra_tc_filter
*f1
, *f2
;
344 f1
= (const struct zebra_tc_filter
*)arg1
;
345 f2
= (const struct zebra_tc_filter
*)arg2
;
347 if (f1
->filter
.ifindex
!= f2
->filter
.ifindex
)
350 if (f1
->filter
.handle
!= f2
->filter
.handle
)
356 static struct zebra_tc_filter
*tc_filter_free(struct zebra_tc_filter
*hash_data
,
359 hash_release(zrouter
.filter_hash
, hash_data
);
362 XFREE(MTYPE_TC_FILTER
, hash_data
);
369 static struct zebra_tc_filter
*tc_filter_release(struct zebra_tc_filter
*filter
,
372 struct zebra_tc_filter
*lookup
;
374 lookup
= hash_lookup(zrouter
.filter_hash
, filter
);
379 return tc_filter_free(lookup
, free_data
);
382 static void *tc_filter_alloc_intern(void *arg
)
384 struct zebra_tc_filter
*ztf
;
385 struct zebra_tc_filter
*new;
387 ztf
= (struct zebra_tc_filter
*)arg
;
389 new = XCALLOC(MTYPE_TC_FILTER
, sizeof(*new));
391 memcpy(new, ztf
, sizeof(*ztf
));
396 void zebra_tc_filter_add(struct zebra_tc_filter
*filter
)
398 if (IS_ZEBRA_DEBUG_TC
)
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
));
405 struct zebra_tc_filter
*found
;
406 struct zebra_tc_filter
*new;
408 found
= hash_lookup(zrouter
.filter_hash
, filter
);
409 new = hash_get(zrouter
.filter_hash
, filter
, tc_filter_alloc_intern
);
412 (void)dplane_tc_filter_update(new);
414 (void)dplane_tc_filter_add(new);
417 void zebra_tc_filter_delete(struct zebra_tc_filter
*filter
)
419 if (IS_ZEBRA_DEBUG_PBR
)
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
));
426 (void)dplane_tc_filter_delete(filter
);
428 if (tc_filter_release(filter
, true))
429 zlog_debug("%s: tc filter being deleted we know nothing about",