From 1cdacdff1755a880c4b8e4d525190b34340a8126 Mon Sep 17 00:00:00 2001 From: "Kiedrzyn, PatrykX" Date: Mon, 23 Jul 2018 11:50:12 +0200 Subject: [07/17] vlan: add vlan action support --- Makefile.am | 2 + include/linux-private/linux/tc_act/tc_vlan.h | 38 +++ include/netlink-private/types.h | 10 + include/netlink/route/act/vlan.h | 37 +++ lib/route/act/vlan.c | 309 +++++++++++++++++++ libnl-route-3.sym | 8 + tests/test-flower-filter-with-actions.c | 154 ++++++++- 7 files changed, 547 insertions(+), 11 deletions(-) create mode 100644 include/linux-private/linux/tc_act/tc_vlan.h create mode 100644 include/netlink/route/act/vlan.h create mode 100644 lib/route/act/vlan.c diff --git a/Makefile.am b/Makefile.am index d161419..efebf31 100644 --- a/Makefile.am +++ b/Makefile.am @@ -112,6 +112,7 @@ libnlinclude_netlink_route_HEADERS = \ libnlinclude_netlink_route_actdir = $(libnlincludedir)/netlink/route/act libnlinclude_netlink_route_act_HEADERS = \ include/netlink/route/act/gact.h \ + include/netlink/route/act/vlan.h \ include/netlink/route/act/mirred.h \ include/netlink/route/act/skbedit.h libnlinclude_netlink_route_clsdir = $(libnlincludedir)/netlink/route/cls @@ -350,6 +351,7 @@ lib_libnl_route_3_la_SOURCES = \ lib/fib_lookup/request.c \ lib/route/act.c \ lib/route/act/gact.c \ + lib/route/act/vlan.c \ lib/route/act/mirred.c \ lib/route/act/skbedit.c \ lib/route/addr.c \ diff --git a/include/linux-private/linux/tc_act/tc_vlan.h b/include/linux-private/linux/tc_act/tc_vlan.h new file mode 100644 index 0000000..bddb272 --- /dev/null +++ b/include/linux-private/linux/tc_act/tc_vlan.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2014 Jiri Pirko + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#ifndef __LINUX_TC_VLAN_H +#define __LINUX_TC_VLAN_H + +#include + +#define TCA_ACT_VLAN 12 + +#define TCA_VLAN_ACT_POP 1 +#define TCA_VLAN_ACT_PUSH 2 +#define TCA_VLAN_ACT_MODIFY 3 + +struct tc_vlan { + tc_gen; + int v_action; +}; + +enum { + TCA_VLAN_UNSPEC, + TCA_VLAN_TM, + TCA_VLAN_PARMS, + TCA_VLAN_PUSH_VLAN_ID, + TCA_VLAN_PUSH_VLAN_PROTOCOL, + TCA_VLAN_PAD, + TCA_VLAN_PUSH_VLAN_PRIORITY, + __TCA_VLAN_MAX, +}; +#define TCA_VLAN_MAX (__TCA_VLAN_MAX - 1) + +#endif diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h index 9e4a20a..1672e3e 100644 --- a/include/netlink-private/types.h +++ b/include/netlink-private/types.h @@ -30,6 +30,7 @@ #include #include #include +#include #include #define NL_SOCK_PASSCRED (1<<1) @@ -609,6 +610,15 @@ struct rtnl_flower int fl_mask; }; +struct rtnl_vlan +{ + struct tc_vlan v_parm; + uint16_t v_vlan_id; + uint8_t v_vlan_prio; + uint16_t v_vlan_proto; + int v_mask; +}; + struct rtnl_cgroup { struct rtnl_ematch_tree *cg_ematch; diff --git a/include/netlink/route/act/vlan.h b/include/netlink/route/act/vlan.h new file mode 100644 index 0000000..ab76e1c --- /dev/null +++ b/include/netlink/route/act/vlan.h @@ -0,0 +1,37 @@ +/* + * lib/route/act/vlan.c vlan action + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2018 Intel Corporation + */ + +#ifndef NETLINK_VLAN_H_ +#define NETLINK_VLAN_H_ + +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +extern int rtnl_vlan_set_action(struct rtnl_act *, int); +extern int rtnl_vlan_get_action(struct rtnl_act *); +extern int rtnl_vlan_set_id(struct rtnl_act *, uint32_t); +extern int rtnl_vlan_get_id(struct rtnl_act *, uint32_t *); +extern int rtnl_vlan_set_prio(struct rtnl_act *, uint32_t); +extern int rtnl_vlan_get_prio(struct rtnl_act *, uint32_t *); +extern int rtnl_vlan_set_proto(struct rtnl_act *, uint32_t); +extern int rtnl_vlan_get_proto(struct rtnl_act *, uint32_t *); + +#ifdef __cplusplus +} +#endif + +#endif /* #ifndef NETLINK_VLAN_H_ */ diff --git a/lib/route/act/vlan.c b/lib/route/act/vlan.c new file mode 100644 index 0000000..e044059 --- /dev/null +++ b/lib/route/act/vlan.c @@ -0,0 +1,309 @@ +/* + * lib/route/act/vlan.c vlan action + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation version 2.1 + * of the License. + * + * Copyright (c) 2018 Intel Corporation + */ + +/** + * @ingroup act + * @defgroup act_vlan VLAN Editing + * + * @{ + */ + +#include +#include +#include +#include +#include +#include +#include + +/** @cond SKIP */ +#define VLAN_ATTR_VLAN_ID 0x001 +#define VLAN_ATTR_VLAN_PRIO 0x002 +#define VLAN_ATTR_VLAN_PROTO 0x004 +/** @endcond */ + +static struct nla_policy vlan_policy[TCA_VLAN_MAX + 1] = { + [TCA_VLAN_PARMS] = {.minlen = sizeof(struct tc_vlan)}, + [TCA_VLAN_PUSH_VLAN_ID] = {.type = NLA_U16}, + [TCA_VLAN_PUSH_VLAN_PRIORITY] = {.type = NLA_U8}, + [TCA_VLAN_PUSH_VLAN_PROTOCOL] = {.type = NLA_U16} +}; + +static int vlan_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_vlan *v = data; + struct nlattr *tb[TCA_VLAN_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_VLAN_MAX, tc, vlan_policy); + if (err < 0) + return err; + + if (tb[TCA_VLAN_PARMS]) + nla_memcpy(&v->v_parm, tb[TCA_VLAN_PARMS], sizeof(v->v_parm)); + + if (tb[TCA_VLAN_PUSH_VLAN_ID]) { + v->v_vlan_id = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_ID]); + v->v_mask |= VLAN_ATTR_VLAN_ID; + } + + if (tb[TCA_VLAN_PUSH_VLAN_PRIORITY]) { + v->v_vlan_prio = nla_get_u8(tb[TCA_VLAN_PUSH_VLAN_PRIORITY]); + v->v_mask |= VLAN_ATTR_VLAN_PRIO; + } + + if (tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]) { + v->v_vlan_proto = nla_get_u16(tb[TCA_VLAN_PUSH_VLAN_PROTOCOL]); + v->v_mask |= VLAN_ATTR_VLAN_PROTO; + } + + return 0; +} + +static int vlan_clone(void *_dst, void *_src) +{ + struct rtnl_vlan *dst = _dst, *src = _src; + + memcpy(&dst->v_parm, &src->v_parm, sizeof(src->v_parm)); + return 0; +} + +static void vlan_dump_line(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_vlan *v = data; + + if (!v) + return; + + nl_dump(p, " action type %u, vlan_id %u, vlan_prio %u, vlan_proto %u", + v->v_parm.v_action, v->v_vlan_id, v->v_vlan_prio, + v->v_vlan_proto); +} + +static void vlan_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_vlan *v = data; + + if (!v) + return; + + switch (v->v_parm.v_action) { + case TCA_VLAN_ACT_POP: + nl_dump(p, " vlan action pop"); + break; + case TCA_VLAN_ACT_PUSH: + nl_dump(p, " vlan action push"); + break; + case TCA_VLAN_ACT_MODIFY: + nl_dump(p, " vlan action modify"); + break; + } + + if (v->v_mask & VLAN_ATTR_VLAN_ID) + nl_dump(p, " vlan_id %u", v->v_vlan_id); + + if (v->v_mask & VLAN_ATTR_VLAN_PRIO) + nl_dump(p, " vlan_prio %u", v->v_vlan_prio); + + if (v->v_mask & VLAN_ATTR_VLAN_PROTO) + nl_dump(p, " vlan_proto %u", v->v_vlan_proto); + + nl_dump(p, "\n"); +} + +static int vlan_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_vlan *v = data; + + if (!v) + return 0; + + NLA_PUT(msg, TCA_VLAN_PARMS, sizeof(v->v_parm), &v->v_parm); + + if (v->v_mask & VLAN_ATTR_VLAN_ID) + NLA_PUT_U16(msg, TCA_VLAN_PUSH_VLAN_ID, v->v_vlan_id); + + if (v->v_mask & VLAN_ATTR_VLAN_PRIO) + NLA_PUT_U8(msg, TCA_VLAN_PUSH_VLAN_PRIORITY, v->v_vlan_prio); + + if (v->v_mask & VLAN_ATTR_VLAN_PROTO) + NLA_PUT_U16(msg, TCA_VLAN_PUSH_VLAN_PROTOCOL, v->v_vlan_proto); + + return 0; + +nla_put_failure: + return -NLE_NOMEM; +} + +/** + * @name Attribute Modifications + * @{ + */ + +int rtnl_vlan_set_action(struct rtnl_act *act, int action) +{ + struct rtnl_vlan *v; + + v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act)); + if (!v) + return -NLE_NOMEM; + + switch (action) { + case TCA_VLAN_ACT_POP: + case TCA_VLAN_ACT_PUSH: + case TCA_VLAN_ACT_MODIFY: + v->v_parm.v_action = action; + v->v_parm.action = TC_ACT_PIPE; + break; + default: + return NLE_OPNOTSUPP; + } + return 0; +} + +int rtnl_vlan_get_action(struct rtnl_act *act) +{ + struct rtnl_vlan *v; + + v = (struct rtnl_vlan *) rtnl_tc_data(TC_CAST(act)); + if (!v) + return -NLE_NOMEM; + + return v->v_parm.v_action; +} + +int rtnl_vlan_set_id(struct rtnl_act *act, uint32_t vlan_id) +{ + struct rtnl_vlan *v; + + v = rtnl_tc_data(TC_CAST(act)); + if (!v) + return -NLE_NOMEM; + + v->v_vlan_id = vlan_id; + v->v_mask |= VLAN_ATTR_VLAN_ID; + + return 0; +} + +int rtnl_vlan_get_id(struct rtnl_act *act, uint32_t *vlan_id) +{ + struct rtnl_vlan *v; + + v = rtnl_tc_data(TC_CAST(act)); + if (!v) + return -NLE_NOMEM; + + if (!(v->v_mask & VLAN_ATTR_VLAN_ID)) + return -NLE_INVAL; + + if (!vlan_id) + return -NLE_INVAL; + + *vlan_id = v->v_vlan_id; + + return 0; +} + +int rtnl_vlan_set_prio(struct rtnl_act *act, uint32_t vlan_prio) +{ + struct rtnl_vlan *v; + + v = rtnl_tc_data(TC_CAST(act)); + if (!v) + return -NLE_NOMEM; + + v->v_vlan_prio = vlan_prio; + v->v_mask |= VLAN_ATTR_VLAN_PRIO; + + return 0; +} + +int rtnl_vlan_get_prio(struct rtnl_act *act, uint32_t *vlan_prio) +{ + struct rtnl_vlan *v; + + v = rtnl_tc_data(TC_CAST(act)); + if (!v) + return -NLE_NOMEM; + + if (!(v->v_mask & VLAN_ATTR_VLAN_PRIO)) + return -NLE_INVAL; + + if (!vlan_prio) + return -NLE_INVAL; + + *vlan_prio = v->v_vlan_prio; + + return 0; +} + +int rtnl_vlan_set_proto(struct rtnl_act *act, uint32_t vlan_proto) +{ + struct rtnl_vlan *v; + + v = rtnl_tc_data(TC_CAST(act)); + if (!v) + return -NLE_NOMEM; + + v->v_vlan_proto = htons(vlan_proto); + v->v_mask |= VLAN_ATTR_VLAN_PROTO; + + return 0; +} + +int rtnl_vlan_get_proto(struct rtnl_act *act, uint32_t *vlan_proto) +{ + struct rtnl_vlan *v; + + v = rtnl_tc_data(TC_CAST(act)); + if (!v) + return -NLE_NOMEM; + + if (!(v->v_mask & VLAN_ATTR_VLAN_PROTO)) + return -NLE_INVAL; + + if (!vlan_proto) + return -NLE_INVAL; + + *vlan_proto = v->v_vlan_proto; + + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops vlan_ops = { + .to_kind = "vlan", + .to_type = RTNL_TC_TYPE_ACT, + .to_size = sizeof(struct rtnl_vlan), + .to_msg_parser = vlan_msg_parser, + .to_clone = vlan_clone, + .to_msg_fill = vlan_msg_fill, + .to_dump = { + [NL_DUMP_LINE] = vlan_dump_line, + [NL_DUMP_DETAILS] = vlan_dump_details, + }, +}; + +static void __init vlan_init(void) +{ + rtnl_tc_register(&vlan_ops); +} + +static void __exit vlan_exit(void) +{ + rtnl_tc_unregister(&vlan_ops); +} + +/** @} */ diff --git a/libnl-route-3.sym b/libnl-route-3.sym index 361a03c..c0f33b7 100644 --- a/libnl-route-3.sym +++ b/libnl-route-3.sym @@ -864,6 +864,14 @@ global: rtnl_flower_add_action; rtnl_flower_del_action; rtnl_flower_get_action; + rtnl_vlan_set_action; + rtnl_vlan_get_action; + rtnl_vlan_set_id; + rtnl_vlan_get_id; + rtnl_vlan_set_prio; + rtnl_vlan_get_prio; + rtnl_vlan_set_proto; + rtnl_vlan_get_proto; # The following symbols were added during the development of 3.2.26. # Keep them in libnl_3 to avoid breaking users. diff --git a/tests/test-flower-filter-with-actions.c b/tests/test-flower-filter-with-actions.c index 5658cce..11b2995 100644 --- a/tests/test-flower-filter-with-actions.c +++ b/tests/test-flower-filter-with-actions.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -106,7 +107,7 @@ char *run_command(const char *cmd) return output; } -static int run_test(void) +static int check_if_flower_is_created_correctly(void) { char *output = NULL; int ret = 0; @@ -141,16 +142,13 @@ static int run_test(void) err: if (ret) printf(output); - else - printf("ok\n"); free(output); return ret; } - -static int setup_flower_classifier(struct nl_sock *sock, - struct nl_cache *link_cache) +static int test_flower_classifier_is_created(struct nl_sock *sock, + struct nl_cache *link_cache) { struct rtnl_cls *cls; struct rtnl_act *act; @@ -227,16 +225,135 @@ static int setup_flower_classifier(struct nl_sock *sock, goto err_cls_add; } - err = run_test(); + err = check_if_flower_is_created_correctly(); + if (err) + printf("Flower classifier was not created correctly\n"); + + rtnl_cls_delete(sock, cls, 0); +err_cls_add: +err_action_add: + rtnl_act_put(act); +err_action: +err_vlan_prio: +err_vlan_id: +err_indev: +err_link_name: +err_cache: + rtnl_cls_put(cls); +err_classifier: + return err; +} + +static int check_if_flower_with_vlan_is_created_correctly(void) +{ + char *output = NULL; + int ret = 0; + + output = run_command("tc -s -d filter show dev d0 ingress"); + + if (!strstr(output, "vlan push id 777 protocol 802.1Q priority 5")) { + printf("unexpected vlan action options\n"); + ret = -1; + goto err; + } + +err: + if (ret) + printf(output); + + free(output); + return ret; +} + +static int test_flower_classifier_with_action_vlan_is_created(struct nl_sock + *sock, + struct nl_cache + *link_cache) +{ + struct rtnl_cls *cls; + struct rtnl_act *act; + int err = 0; + int ifindex; + + cls = rtnl_cls_alloc(); + if (!cls) { + printf("Unable to allocate classifier\n"); + err = -1; + goto err_classifier; + } + + err = rtnl_tc_set_kind(TC_CAST(cls), "flower"); + if (err) { + printf("Unable to set kind to flower classifier\n"); + goto err_classifier; + } + + rtnl_cls_set_prio(cls, 1); + rtnl_cls_set_protocol(cls, ETH_P_8021Q); + + err = nl_cache_refill(sock, link_cache); + if (err) { + printf("Unable to refill cache\n"); + goto err_cache; + } + + ifindex = rtnl_link_name2i(link_cache, "d0"); + if (!ifindex) { + printf("Unable to get link name\n"); + err = -1; + goto err_link_name; + } + + rtnl_tc_set_ifindex(TC_CAST(cls), ifindex); + rtnl_tc_set_handle(TC_CAST(cls), 1); + rtnl_tc_set_parent(TC_CAST(cls), TC_HANDLE(0xffff, 0xfff1)); + err = rtnl_flower_set_indev(cls, "d0"); + if (err) { + printf("Unable to set indev\n"); + goto err_indev; + } + err = rtnl_flower_set_vlan_id(cls, 7); + if (err) { + printf("Unable to set vlan_id: %d\n", err); + goto err_vlan_id; + } + err = rtnl_flower_set_vlan_prio(cls, 1); + if (err) { + printf("Unable to set vlan_prio: %d\n", err); + goto err_vlan_prio; + } + + act = rtnl_act_alloc(); + if (!act) { + printf("Unable to allocate action\n"); + err = -1; + goto err_action; + } + + rtnl_tc_set_kind(TC_CAST(act), "vlan"); + rtnl_vlan_set_action(act, TCA_VLAN_ACT_PUSH); + rtnl_vlan_set_id(act, 777); + rtnl_vlan_set_prio(act, 5); + rtnl_vlan_set_proto(act, ETH_P_8021Q); + + err = rtnl_flower_add_action(cls, act); if (err) { - printf("test case failed\n"); + printf("Unable to add action"); + goto err_action_add; } - err = rtnl_cls_delete(sock, cls, 0); + err = rtnl_cls_add(sock, cls, NLM_F_CREATE); if (err) { - printf("Unable to delete classifier: %d\n", err); + printf("Unable to create classifier: %d\n", err); + goto err_cls_add; } + err = check_if_flower_with_vlan_is_created_correctly(); + if (err) + printf + ("Flower classifier with vlan was not created correctly\n"); + + rtnl_cls_delete(sock, cls, 0); err_cls_add: err_action_add: rtnl_act_put(act); @@ -310,8 +427,23 @@ int main(void) } qdisc_add_ingress(sock, created); - err = setup_flower_classifier(sock, link_cache); + err = test_flower_classifier_is_created(sock, link_cache); + + if (err) + goto err_test_failed; + + printf("test_flower_classifier_is_created passed\n"); + + err = + test_flower_classifier_with_action_vlan_is_created(sock, + link_cache); + + if (err) + goto err_test_failed; + + printf("test_flower_classifier_with_action_vlan_is_created passed\n"); +err_test_failed: rtnl_link_put(created); err_get_link: err_cache: -- 2.17.1