// SPDX-License-Identifier: GPL-2.0 #include #include #include #include #include #include #include #include #include #include #include "tc-user.h" #define TC_HANDLE (0xFFFF << 16) const char *vlan_act_name(struct tc_vlan *p) { switch (p->v_action) { case TCA_VLAN_ACT_POP: return "pop"; case TCA_VLAN_ACT_PUSH: return "push"; case TCA_VLAN_ACT_MODIFY: return "modify"; default: break; } return "not supported"; } const char *gact_act_name(struct tc_gact *p) { switch (p->action) { case TC_ACT_SHOT: return "drop"; case TC_ACT_OK: return "ok"; case TC_ACT_PIPE: return "pipe"; default: break; } return "not supported"; } static void print_vlan(struct tc_act_vlan_attrs *vlan) { printf("%s ", vlan_act_name(vlan->parms)); if (vlan->_present.push_vlan_id) printf("id %u ", vlan->push_vlan_id); if (vlan->_present.push_vlan_protocol) printf("protocol %#x ", ntohs(vlan->push_vlan_protocol)); if (vlan->_present.push_vlan_priority) printf("priority %u ", vlan->push_vlan_priority); } static void print_gact(struct tc_act_gact_attrs *gact) { struct tc_gact *p = gact->parms; printf("%s ", gact_act_name(p)); } static void flower_print(struct tc_flower_attrs *flower, const char *kind) { struct tc_act_attrs *a; unsigned int i; printf("%s:\n", kind); if (flower->_present.key_vlan_id) printf(" vlan_id: %u\n", flower->key_vlan_id); if (flower->_present.key_vlan_prio) printf(" vlan_prio: %u\n", flower->key_vlan_prio); if (flower->_present.key_num_of_vlans) printf(" num_of_vlans: %u\n", flower->key_num_of_vlans); for (i = 0; i < flower->_count.act; i++) { a = &flower->act[i]; printf("action order: %i %s ", i + 1, a->kind); if (a->options._present.vlan) print_vlan(&a->options.vlan); else if (a->options._present.gact) print_gact(&a->options.gact); printf("\n"); } printf("\n"); } static void tc_filter_print(struct tc_gettfilter_rsp *f) { struct tc_options_msg *opt = &f->options; if (opt->_present.flower) flower_print(&opt->flower, f->kind); else if (f->_len.kind) printf("%s pref %u proto: %#x\n", f->kind, (f->_hdr.tcm_info >> 16), ntohs(TC_H_MIN(f->_hdr.tcm_info))); } static int tc_filter_add(struct ynl_sock *ys, int ifi) { struct tc_newtfilter_req *req; struct tc_act_attrs *acts; struct tc_vlan p = { .action = TC_ACT_PIPE, .v_action = TCA_VLAN_ACT_PUSH }; __u16 flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE; int ret; req = tc_newtfilter_req_alloc(); if (!req) { fprintf(stderr, "tc_newtfilter_req_alloc failed\n"); return -1; } memset(req, 0, sizeof(*req)); acts = tc_act_attrs_alloc(3); if (!acts) { fprintf(stderr, "tc_act_attrs_alloc\n"); tc_newtfilter_req_free(req); return -1; } memset(acts, 0, sizeof(*acts) * 3); req->_hdr.tcm_ifindex = ifi; req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q)); req->chain = 0; tc_newtfilter_req_set_nlflags(req, flags); tc_newtfilter_req_set_kind(req, "flower"); tc_newtfilter_req_set_options_flower_key_vlan_id(req, 100); tc_newtfilter_req_set_options_flower_key_vlan_prio(req, 5); tc_newtfilter_req_set_options_flower_key_num_of_vlans(req, 3); __tc_newtfilter_req_set_options_flower_act(req, acts, 3); /* Skip action at index 0 because in TC, the action array * index starts at 1, with each index defining the action's * order. In contrast, in YNL indexed arrays start at index 0. */ tc_act_attrs_set_kind(&acts[1], "vlan"); tc_act_attrs_set_options_vlan_parms(&acts[1], &p, sizeof(p)); tc_act_attrs_set_options_vlan_push_vlan_id(&acts[1], 200); tc_act_attrs_set_kind(&acts[2], "vlan"); tc_act_attrs_set_options_vlan_parms(&acts[2], &p, sizeof(p)); tc_act_attrs_set_options_vlan_push_vlan_id(&acts[2], 300); tc_newtfilter_req_set_options_flower_flags(req, 0); tc_newtfilter_req_set_options_flower_key_eth_type(req, htons(0x8100)); ret = tc_newtfilter(ys, req); if (ret) fprintf(stderr, "tc_newtfilter: %s\n", ys->err.msg); tc_newtfilter_req_free(req); return ret; } static int tc_filter_show(struct ynl_sock *ys, int ifi) { struct tc_gettfilter_req_dump *req; struct tc_gettfilter_list *rsp; req = tc_gettfilter_req_dump_alloc(); if (!req) { fprintf(stderr, "tc_gettfilter_req_dump_alloc failed\n"); return -1; } memset(req, 0, sizeof(*req)); req->_hdr.tcm_ifindex = ifi; req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); req->_present.chain = 1; req->chain = 0; rsp = tc_gettfilter_dump(ys, req); tc_gettfilter_req_dump_free(req); if (!rsp) { fprintf(stderr, "YNL: %s\n", ys->err.msg); return -1; } if (ynl_dump_empty(rsp)) fprintf(stderr, "Error: no filters reported\n"); else ynl_dump_foreach(rsp, flt) tc_filter_print(flt); tc_gettfilter_list_free(rsp); return 0; } static int tc_filter_del(struct ynl_sock *ys, int ifi) { struct tc_deltfilter_req *req; __u16 flags = NLM_F_REQUEST; int ret; req = tc_deltfilter_req_alloc(); if (!req) { fprintf(stderr, "tc_deltfilter_req_alloc failed\n"); return -1; } memset(req, 0, sizeof(*req)); req->_hdr.tcm_ifindex = ifi; req->_hdr.tcm_parent = TC_H_MAKE(TC_H_CLSACT, TC_H_MIN_INGRESS); req->_hdr.tcm_info = TC_H_MAKE(1 << 16, htons(ETH_P_8021Q)); tc_deltfilter_req_set_nlflags(req, flags); ret = tc_deltfilter(ys, req); if (ret) fprintf(stderr, "tc_deltfilter failed: %s\n", ys->err.msg); tc_deltfilter_req_free(req); return ret; } static int tc_clsact_add(struct ynl_sock *ys, int ifi) { struct tc_newqdisc_req *req; __u16 flags = NLM_F_REQUEST | NLM_F_EXCL | NLM_F_CREATE; int ret; req = tc_newqdisc_req_alloc(); if (!req) { fprintf(stderr, "tc_newqdisc_req_alloc failed\n"); return -1; } memset(req, 0, sizeof(*req)); req->_hdr.tcm_ifindex = ifi; req->_hdr.tcm_parent = TC_H_CLSACT; req->_hdr.tcm_handle = TC_HANDLE; tc_newqdisc_req_set_nlflags(req, flags); tc_newqdisc_req_set_kind(req, "clsact"); ret = tc_newqdisc(ys, req); if (ret) fprintf(stderr, "tc_newqdisc failed: %s\n", ys->err.msg); tc_newqdisc_req_free(req); return ret; } static int tc_clsact_del(struct ynl_sock *ys, int ifi) { struct tc_delqdisc_req *req; __u16 flags = NLM_F_REQUEST; int ret; req = tc_delqdisc_req_alloc(); if (!req) { fprintf(stderr, "tc_delqdisc_req_alloc failed\n"); return -1; } memset(req, 0, sizeof(*req)); req->_hdr.tcm_ifindex = ifi; req->_hdr.tcm_parent = TC_H_CLSACT; req->_hdr.tcm_handle = TC_HANDLE; tc_delqdisc_req_set_nlflags(req, flags); ret = tc_delqdisc(ys, req); if (ret) fprintf(stderr, "tc_delqdisc failed: %s\n", ys->err.msg); tc_delqdisc_req_free(req); return ret; } static int tc_filter_config(struct ynl_sock *ys, int ifi) { int ret = 0; if (tc_filter_add(ys, ifi)) return -1; ret = tc_filter_show(ys, ifi); if (tc_filter_del(ys, ifi)) return -1; return ret; } int main(int argc, char **argv) { struct ynl_error yerr; struct ynl_sock *ys; int ifi, ret = 0; if (argc < 2) { fprintf(stderr, "Usage: %s \n", argv[0]); return 1; } ifi = if_nametoindex(argv[1]); if (!ifi) { perror("if_nametoindex"); return 1; } ys = ynl_sock_create(&ynl_tc_family, &yerr); if (!ys) { fprintf(stderr, "YNL: %s\n", yerr.msg); return 1; } if (tc_clsact_add(ys, ifi)) { ret = 2; goto err_destroy; } if (tc_filter_config(ys, ifi)) ret = 3; if (tc_clsact_del(ys, ifi)) ret = 4; err_destroy: ynl_sock_destroy(ys); return ret; }