Coverage Report

Created: 2026-01-25 06:17

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