1 // SPDX-License-Identifier: GPL-2.0 2 #define _GNU_SOURCE 3 #include <sched.h> 4 #include <stdio.h> 5 #include <string.h> 6 #include <stdlib.h> 7 #include <arpa/inet.h> 8 #include <linux/pkt_sched.h> 9 #include <linux/tc_act/tc_vlan.h> 10 #include <linux/tc_act/tc_gact.h> 11 #include <linux/if_ether.h> 12 #include <net/if.h> 13 14 #include <ynl.h> 15 16 #include <kselftest_harness.h> 17 18 #include "tc-user.h" 19 20 #define TC_HANDLE (0xFFFF << 16) 21 22 static bool tc_qdisc_print(struct __test_metadata *_metadata, 23 struct tc_getqdisc_rsp *q) 24 { 25 bool was_fq_codel = false; 26 char ifname[IF_NAMESIZE]; 27 const char *name; 28 29 name = if_indextoname(q->_hdr.tcm_ifindex, ifname); 30 EXPECT_TRUE((bool)name); 31 ksft_print_msg("%16s: ", name ?: "no-name"); 32 33 if (q->_len.kind) { 34 printf("%s ", q->kind); 35 36 if (q->options._present.fq_codel) { 37 struct tc_fq_codel_attrs *fq_codel; 38 struct tc_fq_codel_xstats *stats; 39 40 fq_codel = &q->options.fq_codel; 41 stats = q->stats2.app.fq_codel; 42 43 EXPECT_EQ(true, 44 fq_codel->_present.limit && 45 fq_codel->_present.target && 46 q->stats2.app._len.fq_codel); 47 48 if (fq_codel->_present.limit) 49 printf("limit: %dp ", fq_codel->limit); 50 if (fq_codel->_present.target) 51 printf("target: %dms ", 52 (fq_codel->target + 500) / 1000); 53 if (q->stats2.app._len.fq_codel) 54 printf("new_flow_cnt: %d ", 55 stats->qdisc_stats.new_flow_count); 56 was_fq_codel = true; 57 } 58 } 59 printf("\n"); 60 61 return was_fq_codel; 62 } 63 64 static const char *vlan_act_name(struct tc_vlan *p) 65 { 66 switch (p->v_action) { 67 case TCA_VLAN_ACT_POP: 68 return "pop"; 69 case TCA_VLAN_ACT_PUSH: 70 return "push"; 71 case TCA_VLAN_ACT_MODIFY: 72 return "modify"; 73 default: 74 break; 75 } 76 77 return "not supported"; 78 } 79 80 static const char *gact_act_name(struct tc_gact *p) 81 { 82 switch (p->action) { 83 case TC_ACT_SHOT: 84 return "drop"; 85 case TC_ACT_OK: 86 return "ok"; 87 case TC_ACT_PIPE: 88 return "pipe"; 89 default: 90 break; 91 } 92 93 return "not supported"; 94 } 95 96 static void print_vlan(struct tc_act_vlan_attrs *vlan) 97 { 98 printf("%s ", vlan_act_name(vlan->parms)); 99 if (vlan->_present.push_vlan_id) 100 printf("id %u ", vlan->push_vlan_id); 101 if (vlan->_present.push_vlan_protocol) 102 printf("protocol %#x ", ntohs(vlan->push_vlan_protocol)); 103 if (vlan->_present.push_vlan_priority) 104 printf("priority %u ", vlan->push_vlan_priority); 105 } 106 107 static void print_gact(struct tc_act_gact_attrs *gact) 108 { 109 struct tc_gact *p = gact->parms; 110 111 printf("%s ", gact_act_name(p)); 112 } 113 114 static void flower_print(struct tc_flower_attrs *flower, const char *kind) 115 { 116 struct tc_act_attrs *a; 117 unsigned int i; 118 119 ksft_print_msg("%s:\n", kind); 120 121 if (flower->_present.key_vlan_id) 122 ksft_print_msg(" vlan_id: %u\n", flower->key_vlan_id); 123 if (flower->_present.key_vlan_prio) 124 ksft_print_msg(" vlan_prio: %u\n", flower->key_vlan_prio); 125 if (flower->_present.key_num_of_vlans) 126 ksft_print_msg(" num_of_vlans: %u\n", 127 flower->key_num_of_vlans); 128 129 for (i = 0; i < flower->_count.act; i++) { 130 a = &flower->act[i]; 131 ksft_print_msg("action order: %i %s ", i + 1, a->kind); 132 if (a->options._present.vlan) 133 print_vlan(&a->options.vlan); 134 else if (a->options._present.gact) 135 print_gact(&a->options.gact); 136 printf("\n"); 137 } 138 } 139 140 static void tc_filter_print(struct __test_metadata *_metadata, 141 struct tc_gettfilter_rsp *f) 142 { 143 struct tc_options_msg *opt = &f->options; 144 145 if (opt->_present.flower) { 146 EXPECT_TRUE((bool)f->_len.kind); 147 flower_print(&opt->flower, f->kind); 148 } else if (f->_len.kind) { 149 ksft_print_msg("%s pref %u proto: %#x\n", f->kind, 150 (f->_hdr.tcm_info >> 16), 151 ntohs(TC_H_MIN(f->_hdr.tcm_info))); 152 } 153 } 154 155 static int tc_clsact_add(struct ynl_sock *ys, int ifi) 156 { 157 struct tc_newqdisc_req *req; 158 int ret; 159 160 req = tc_newqdisc_req_alloc(); 161 if (!req) 162 return -1; 163 memset(req, 0, sizeof(*req)); 164 165 req->_hdr.tcm_ifindex = ifi; 166 req->_hdr.tcm_parent = TC_H_CLSACT; 167 req->_hdr.tcm_handle = TC_HANDLE; 168 tc_newqdisc_req_set_nlflags(req, 169 NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE); 170 tc_newqdisc_req_set_kind(req, "clsact"); 171 172 ret = tc_newqdisc(ys, req); 173 tc_newqdisc_req_free(req); 174 175 return ret; 176 } 177 178 static int tc_clsact_del(struct ynl_sock *ys, int ifi) 179 { 180 struct tc_delqdisc_req *req; 181 int ret; 182 183 req = tc_delqdisc_req_alloc(); 184 if (!req) 185 return -1; 186 memset(req, 0, sizeof(*req)); 187 188 req->_hdr.tcm_ifindex = ifi; 189 req->_hdr.tcm_parent = TC_H_CLSACT; 190 req->_hdr.tcm_handle = TC_HANDLE; 191 tc_delqdisc_req_set_nlflags(req, NLM_F_REQUEST); 192 193 ret = tc_delqdisc(ys, req); 194 tc_delqdisc_req_free(req); 195 196 return ret; 197 } 198 199 static int tc_filter_add(struct ynl_sock *ys, int ifi) 200 { 201 struct tc_newtfilter_req *req; 202 struct tc_act_attrs *acts; 203 struct tc_vlan p = { 204 .action = TC_ACT_PIPE, 205 .v_action = TCA_VLAN_ACT_PUSH 206 }; 207 int ret; 208 209 req = tc_newtfilter_req_alloc(); 210 if (!req) 211 return -1; 212 memset(req, 0, sizeof(*req)); 213 214 acts = tc_act_attrs_alloc(3); 215 if (!acts) { 216 tc_newtfilter_req_free(req); 217 return -1; 218 } 219 memset(acts, 0, sizeof(*acts) * 3); 220 221 req->_hdr.tcm_ifindex = ifi; 222 req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); 223 req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q)); 224 req->chain = 0; 225 226 tc_newtfilter_req_set_nlflags(req, NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE); 227 tc_newtfilter_req_set_kind(req, "flower"); 228 tc_newtfilter_req_set_options_flower_key_vlan_id(req, 100); 229 tc_newtfilter_req_set_options_flower_key_vlan_prio(req, 5); 230 tc_newtfilter_req_set_options_flower_key_num_of_vlans(req, 3); 231 232 __tc_newtfilter_req_set_options_flower_act(req, acts, 3); 233 234 /* Skip action at index 0 because in TC, the action array 235 * index starts at 1, with each index defining the action's 236 * order. In contrast, in YNL indexed arrays start at index 0. 237 */ 238 tc_act_attrs_set_kind(&acts[1], "vlan"); 239 tc_act_attrs_set_options_vlan_parms(&acts[1], &p, sizeof(p)); 240 tc_act_attrs_set_options_vlan_push_vlan_id(&acts[1], 200); 241 tc_act_attrs_set_kind(&acts[2], "vlan"); 242 tc_act_attrs_set_options_vlan_parms(&acts[2], &p, sizeof(p)); 243 tc_act_attrs_set_options_vlan_push_vlan_id(&acts[2], 300); 244 245 tc_newtfilter_req_set_options_flower_flags(req, 0); 246 tc_newtfilter_req_set_options_flower_key_eth_type(req, htons(0x8100)); 247 248 ret = tc_newtfilter(ys, req); 249 tc_newtfilter_req_free(req); 250 251 return ret; 252 } 253 254 static int tc_filter_del(struct ynl_sock *ys, int ifi) 255 { 256 struct tc_deltfilter_req *req; 257 int ret; 258 259 req = tc_deltfilter_req_alloc(); 260 if (!req) 261 return -1; 262 memset(req, 0, sizeof(*req)); 263 264 req->_hdr.tcm_ifindex = ifi; 265 req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); 266 req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q)); 267 tc_deltfilter_req_set_nlflags(req, NLM_F_REQUEST); 268 269 ret = tc_deltfilter(ys, req); 270 tc_deltfilter_req_free(req); 271 272 return ret; 273 } 274 275 FIXTURE(tc) 276 { 277 struct ynl_sock *ys; 278 int ifindex; 279 }; 280 281 FIXTURE_SETUP(tc) 282 { 283 struct ynl_error yerr; 284 int ret; 285 286 ret = unshare(CLONE_NEWNET); 287 ASSERT_EQ(0, ret); 288 289 self->ifindex = 1; /* loopback */ 290 291 self->ys = ynl_sock_create(&ynl_tc_family, &yerr); 292 ASSERT_NE(NULL, self->ys) { 293 TH_LOG("failed to create tc socket: %s", yerr.msg); 294 } 295 } 296 297 FIXTURE_TEARDOWN(tc) 298 { 299 ynl_sock_destroy(self->ys); 300 } 301 302 TEST_F(tc, qdisc) 303 { 304 struct tc_getqdisc_req_dump *dreq; 305 struct tc_newqdisc_req *add_req; 306 struct tc_delqdisc_req *del_req; 307 struct tc_getqdisc_list *rsp; 308 bool found = false; 309 int ret; 310 311 add_req = tc_newqdisc_req_alloc(); 312 ASSERT_NE(NULL, add_req); 313 memset(add_req, 0, sizeof(*add_req)); 314 315 add_req->_hdr.tcm_ifindex = self->ifindex; 316 add_req->_hdr.tcm_parent = TC_H_ROOT; 317 tc_newqdisc_req_set_nlflags(add_req, 318 NLM_F_REQUEST | NLM_F_CREATE); 319 tc_newqdisc_req_set_kind(add_req, "fq_codel"); 320 321 ret = tc_newqdisc(self->ys, add_req); 322 tc_newqdisc_req_free(add_req); 323 ASSERT_EQ(0, ret) { 324 TH_LOG("qdisc add failed: %s", self->ys->err.msg); 325 } 326 327 dreq = tc_getqdisc_req_dump_alloc(); 328 ASSERT_NE(NULL, dreq); 329 rsp = tc_getqdisc_dump(self->ys, dreq); 330 tc_getqdisc_req_dump_free(dreq); 331 ASSERT_NE(NULL, rsp) { 332 TH_LOG("dump failed: %s", self->ys->err.msg); 333 } 334 ASSERT_FALSE(ynl_dump_empty(rsp)); 335 336 ynl_dump_foreach(rsp, qdisc) { 337 found |= tc_qdisc_print(_metadata, qdisc); 338 } 339 tc_getqdisc_list_free(rsp); 340 EXPECT_TRUE(found); 341 342 del_req = tc_delqdisc_req_alloc(); 343 ASSERT_NE(NULL, del_req); 344 memset(del_req, 0, sizeof(*del_req)); 345 346 del_req->_hdr.tcm_ifindex = self->ifindex; 347 del_req->_hdr.tcm_parent = TC_H_ROOT; 348 tc_delqdisc_req_set_nlflags(del_req, NLM_F_REQUEST); 349 350 ret = tc_delqdisc(self->ys, del_req); 351 tc_delqdisc_req_free(del_req); 352 EXPECT_EQ(0, ret) { 353 TH_LOG("qdisc del failed: %s", self->ys->err.msg); 354 } 355 } 356 357 TEST_F(tc, flower) 358 { 359 struct tc_gettfilter_req_dump *dreq; 360 struct tc_gettfilter_list *rsp; 361 bool found = false; 362 int ret; 363 364 ret = tc_clsact_add(self->ys, self->ifindex); 365 if (ret) 366 SKIP(return, "clsact not supported: %s", self->ys->err.msg); 367 368 ret = tc_filter_add(self->ys, self->ifindex); 369 ASSERT_EQ(0, ret) { 370 TH_LOG("filter add failed: %s", self->ys->err.msg); 371 } 372 373 dreq = tc_gettfilter_req_dump_alloc(); 374 ASSERT_NE(NULL, dreq); 375 memset(dreq, 0, sizeof(*dreq)); 376 dreq->_hdr.tcm_ifindex = self->ifindex; 377 dreq->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); 378 dreq->_present.chain = 1; 379 dreq->chain = 0; 380 381 rsp = tc_gettfilter_dump(self->ys, dreq); 382 tc_gettfilter_req_dump_free(dreq); 383 ASSERT_NE(NULL, rsp) { 384 TH_LOG("filter dump failed: %s", self->ys->err.msg); 385 } 386 387 ynl_dump_foreach(rsp, flt) { 388 tc_filter_print(_metadata, flt); 389 if (flt->options._present.flower) { 390 EXPECT_EQ(100, flt->options.flower.key_vlan_id); 391 EXPECT_EQ(5, flt->options.flower.key_vlan_prio); 392 found = true; 393 } 394 } 395 tc_gettfilter_list_free(rsp); 396 EXPECT_TRUE(found); 397 398 ret = tc_filter_del(self->ys, self->ifindex); 399 EXPECT_EQ(0, ret) { 400 TH_LOG("filter del failed: %s", self->ys->err.msg); 401 } 402 403 ret = tc_clsact_del(self->ys, self->ifindex); 404 EXPECT_EQ(0, ret) { 405 TH_LOG("clsact del failed: %s", self->ys->err.msg); 406 } 407 } 408 409 TEST_HARNESS_MAIN 410