1 // SPDX-License-Identifier: GPL-2.0 2 #include <stdio.h> 3 #include <string.h> 4 #include <stdlib.h> 5 #include <arpa/inet.h> 6 #include <linux/pkt_sched.h> 7 #include <linux/tc_act/tc_vlan.h> 8 #include <linux/tc_act/tc_gact.h> 9 #include <linux/if_ether.h> 10 #include <net/if.h> 11 12 #include <ynl.h> 13 14 #include "tc-user.h" 15 16 #define TC_HANDLE (0xFFFF << 16) 17 18 const char *vlan_act_name(struct tc_vlan *p) 19 { 20 switch (p->v_action) { 21 case TCA_VLAN_ACT_POP: 22 return "pop"; 23 case TCA_VLAN_ACT_PUSH: 24 return "push"; 25 case TCA_VLAN_ACT_MODIFY: 26 return "modify"; 27 default: 28 break; 29 } 30 31 return "not supported"; 32 } 33 34 const char *gact_act_name(struct tc_gact *p) 35 { 36 switch (p->action) { 37 case TC_ACT_SHOT: 38 return "drop"; 39 case TC_ACT_OK: 40 return "ok"; 41 case TC_ACT_PIPE: 42 return "pipe"; 43 default: 44 break; 45 } 46 47 return "not supported"; 48 } 49 50 static void print_vlan(struct tc_act_vlan_attrs *vlan) 51 { 52 printf("%s ", vlan_act_name(vlan->parms)); 53 if (vlan->_present.push_vlan_id) 54 printf("id %u ", vlan->push_vlan_id); 55 if (vlan->_present.push_vlan_protocol) 56 printf("protocol %#x ", ntohs(vlan->push_vlan_protocol)); 57 if (vlan->_present.push_vlan_priority) 58 printf("priority %u ", vlan->push_vlan_priority); 59 } 60 61 static void print_gact(struct tc_act_gact_attrs *gact) 62 { 63 struct tc_gact *p = gact->parms; 64 65 printf("%s ", gact_act_name(p)); 66 } 67 68 static void flower_print(struct tc_flower_attrs *flower, const char *kind) 69 { 70 struct tc_act_attrs *a; 71 unsigned int i; 72 73 printf("%s:\n", kind); 74 75 if (flower->_present.key_vlan_id) 76 printf(" vlan_id: %u\n", flower->key_vlan_id); 77 if (flower->_present.key_vlan_prio) 78 printf(" vlan_prio: %u\n", flower->key_vlan_prio); 79 if (flower->_present.key_num_of_vlans) 80 printf(" num_of_vlans: %u\n", flower->key_num_of_vlans); 81 82 for (i = 0; i < flower->_count.act; i++) { 83 a = &flower->act[i]; 84 printf("action order: %i %s ", i + 1, a->kind); 85 if (a->options._present.vlan) 86 print_vlan(&a->options.vlan); 87 else if (a->options._present.gact) 88 print_gact(&a->options.gact); 89 printf("\n"); 90 } 91 printf("\n"); 92 } 93 94 static void tc_filter_print(struct tc_gettfilter_rsp *f) 95 { 96 struct tc_options_msg *opt = &f->options; 97 98 if (opt->_present.flower) 99 flower_print(&opt->flower, f->kind); 100 else if (f->_len.kind) 101 printf("%s pref %u proto: %#x\n", f->kind, 102 (f->_hdr.tcm_info >> 16), 103 ntohs(TC_H_MIN(f->_hdr.tcm_info))); 104 } 105 106 static int tc_filter_add(struct ynl_sock *ys, int ifi) 107 { 108 struct tc_newtfilter_req *req; 109 struct tc_act_attrs *acts; 110 struct tc_vlan p = { 111 .action = TC_ACT_PIPE, 112 .v_action = TCA_VLAN_ACT_PUSH 113 }; 114 __u16 flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE; 115 int ret; 116 117 req = tc_newtfilter_req_alloc(); 118 if (!req) { 119 fprintf(stderr, "tc_newtfilter_req_alloc failed\n"); 120 return -1; 121 } 122 memset(req, 0, sizeof(*req)); 123 124 acts = tc_act_attrs_alloc(3); 125 if (!acts) { 126 fprintf(stderr, "tc_act_attrs_alloc\n"); 127 tc_newtfilter_req_free(req); 128 return -1; 129 } 130 memset(acts, 0, sizeof(*acts) * 3); 131 132 req->_hdr.tcm_ifindex = ifi; 133 req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); 134 req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q)); 135 req->chain = 0; 136 137 tc_newtfilter_req_set_nlflags(req, flags); 138 tc_newtfilter_req_set_kind(req, "flower"); 139 tc_newtfilter_req_set_options_flower_key_vlan_id(req, 100); 140 tc_newtfilter_req_set_options_flower_key_vlan_prio(req, 5); 141 tc_newtfilter_req_set_options_flower_key_num_of_vlans(req, 3); 142 143 __tc_newtfilter_req_set_options_flower_act(req, acts, 3); 144 145 /* Skip action at index 0 because in TC, the action array 146 * index starts at 1, with each index defining the action's 147 * order. In contrast, in YNL indexed arrays start at index 0. 148 */ 149 tc_act_attrs_set_kind(&acts[1], "vlan"); 150 tc_act_attrs_set_options_vlan_parms(&acts[1], &p, sizeof(p)); 151 tc_act_attrs_set_options_vlan_push_vlan_id(&acts[1], 200); 152 tc_act_attrs_set_kind(&acts[2], "vlan"); 153 tc_act_attrs_set_options_vlan_parms(&acts[2], &p, sizeof(p)); 154 tc_act_attrs_set_options_vlan_push_vlan_id(&acts[2], 300); 155 156 tc_newtfilter_req_set_options_flower_flags(req, 0); 157 tc_newtfilter_req_set_options_flower_key_eth_type(req, htons(0x8100)); 158 159 ret = tc_newtfilter(ys, req); 160 if (ret) 161 fprintf(stderr, "tc_newtfilter: %s\n", ys->err.msg); 162 163 tc_newtfilter_req_free(req); 164 165 return ret; 166 } 167 168 static int tc_filter_show(struct ynl_sock *ys, int ifi) 169 { 170 struct tc_gettfilter_req_dump *req; 171 struct tc_gettfilter_list *rsp; 172 173 req = tc_gettfilter_req_dump_alloc(); 174 if (!req) { 175 fprintf(stderr, "tc_gettfilter_req_dump_alloc failed\n"); 176 return -1; 177 } 178 memset(req, 0, sizeof(*req)); 179 180 req->_hdr.tcm_ifindex = ifi; 181 req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); 182 req->_present.chain = 1; 183 req->chain = 0; 184 185 rsp = tc_gettfilter_dump(ys, req); 186 tc_gettfilter_req_dump_free(req); 187 if (!rsp) { 188 fprintf(stderr, "YNL: %s\n", ys->err.msg); 189 return -1; 190 } 191 192 if (ynl_dump_empty(rsp)) 193 fprintf(stderr, "Error: no filters reported\n"); 194 else 195 ynl_dump_foreach(rsp, flt) tc_filter_print(flt); 196 197 tc_gettfilter_list_free(rsp); 198 199 return 0; 200 } 201 202 static int tc_filter_del(struct ynl_sock *ys, int ifi) 203 { 204 struct tc_deltfilter_req *req; 205 __u16 flags = NLM_F_REQUEST; 206 int ret; 207 208 req = tc_deltfilter_req_alloc(); 209 if (!req) { 210 fprintf(stderr, "tc_deltfilter_req_alloc failed\n"); 211 return -1; 212 } 213 memset(req, 0, sizeof(*req)); 214 215 req->_hdr.tcm_ifindex = ifi; 216 req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); 217 req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q)); 218 tc_deltfilter_req_set_nlflags(req, flags); 219 220 ret = tc_deltfilter(ys, req); 221 if (ret) 222 fprintf(stderr, "tc_deltfilter failed: %s\n", ys->err.msg); 223 224 tc_deltfilter_req_free(req); 225 226 return ret; 227 } 228 229 static int tc_clsact_add(struct ynl_sock *ys, int ifi) 230 { 231 struct tc_newqdisc_req *req; 232 __u16 flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE; 233 int ret; 234 235 req = tc_newqdisc_req_alloc(); 236 if (!req) { 237 fprintf(stderr, "tc_newqdisc_req_alloc failed\n"); 238 return -1; 239 } 240 memset(req, 0, sizeof(*req)); 241 242 req->_hdr.tcm_ifindex = ifi; 243 req->_hdr.tcm_parent = TC_H_CLSACT; 244 req->_hdr.tcm_handle = TC_HANDLE; 245 tc_newqdisc_req_set_nlflags(req, flags); 246 tc_newqdisc_req_set_kind(req, "clsact"); 247 248 ret = tc_newqdisc(ys, req); 249 if (ret) 250 fprintf(stderr, "tc_newqdisc failed: %s\n", ys->err.msg); 251 252 tc_newqdisc_req_free(req); 253 254 return ret; 255 } 256 257 static int tc_clsact_del(struct ynl_sock *ys, int ifi) 258 { 259 struct tc_delqdisc_req *req; 260 __u16 flags = NLM_F_REQUEST; 261 int ret; 262 263 req = tc_delqdisc_req_alloc(); 264 if (!req) { 265 fprintf(stderr, "tc_delqdisc_req_alloc failed\n"); 266 return -1; 267 } 268 memset(req, 0, sizeof(*req)); 269 270 req->_hdr.tcm_ifindex = ifi; 271 req->_hdr.tcm_parent = TC_H_CLSACT; 272 req->_hdr.tcm_handle = TC_HANDLE; 273 tc_delqdisc_req_set_nlflags(req, flags); 274 275 ret = tc_delqdisc(ys, req); 276 if (ret) 277 fprintf(stderr, "tc_delqdisc failed: %s\n", ys->err.msg); 278 279 tc_delqdisc_req_free(req); 280 281 return ret; 282 } 283 284 static int tc_filter_config(struct ynl_sock *ys, int ifi) 285 { 286 int ret = 0; 287 288 if (tc_filter_add(ys, ifi)) 289 return -1; 290 291 ret = tc_filter_show(ys, ifi); 292 293 if (tc_filter_del(ys, ifi)) 294 return -1; 295 296 return ret; 297 } 298 299 int main(int argc, char **argv) 300 { 301 struct ynl_error yerr; 302 struct ynl_sock *ys; 303 int ifi, ret = 0; 304 305 if (argc < 2) { 306 fprintf(stderr, "Usage: %s <interface_name>\n", argv[0]); 307 return 1; 308 } 309 ifi = if_nametoindex(argv[1]); 310 if (!ifi) { 311 perror("if_nametoindex"); 312 return 1; 313 } 314 315 ys = ynl_sock_create(&ynl_tc_family, &yerr); 316 if (!ys) { 317 fprintf(stderr, "YNL: %s\n", yerr.msg); 318 return 1; 319 } 320 321 if (tc_clsact_add(ys, ifi)) { 322 ret = 2; 323 goto err_destroy; 324 } 325 326 if (tc_filter_config(ys, ifi)) 327 ret = 3; 328 329 if (tc_clsact_del(ys, ifi)) 330 ret = 4; 331 332 err_destroy: 333 ynl_sock_destroy(ys); 334 return ret; 335 } 336