| /* SPDX-License-Identifier: LGPL-2.1-only */ |
| /* |
| * Copyright (c) 2009-2013 Thomas Graf <[email protected]> |
| */ |
| |
| /** |
| * @ingroup cls |
| * @defgroup cls_cgroup Control Groups Classifier |
| * |
| * @{ |
| */ |
| |
| #include "nl-default.h" |
| |
| #include <netlink/netlink.h> |
| #include <netlink/attr.h> |
| #include <netlink/utils.h> |
| #include <netlink/route/classifier.h> |
| #include <netlink/route/cls/cgroup.h> |
| #include <netlink/route/cls/ematch.h> |
| |
| #include "tc-api.h" |
| |
| /** @cond SKIP */ |
| struct rtnl_cgroup { |
| struct rtnl_ematch_tree *cg_ematch; |
| int cg_mask; |
| }; |
| |
| #define CGROUP_ATTR_EMATCH 0x001 |
| /** @endcond */ |
| |
| static struct nla_policy cgroup_policy[TCA_CGROUP_MAX+1] = { |
| [TCA_CGROUP_EMATCHES] = { .type = NLA_NESTED }, |
| }; |
| |
| static int cgroup_clone(void *_dst, void *_src) |
| { |
| struct rtnl_cgroup *dst = _dst, *src = _src; |
| |
| dst->cg_ematch = NULL; |
| |
| if (src->cg_ematch) { |
| dst->cg_ematch = rtnl_ematch_tree_clone(src->cg_ematch); |
| if (!dst->cg_ematch) |
| return -NLE_NOMEM; |
| } |
| |
| return 0; |
| } |
| |
| static void cgroup_free_data(struct rtnl_tc *tc, void *data) |
| { |
| struct rtnl_cgroup *c = data; |
| |
| if (!c) |
| return; |
| |
| rtnl_ematch_tree_free(c->cg_ematch); |
| } |
| |
| static int cgroup_msg_parser(struct rtnl_tc *tc, void *data) |
| { |
| struct nlattr *tb[TCA_CGROUP_MAX + 1]; |
| struct rtnl_cgroup *c = data; |
| int err; |
| |
| err = tca_parse(tb, TCA_CGROUP_MAX, tc, cgroup_policy); |
| if (err < 0) |
| return err; |
| |
| if (tb[TCA_CGROUP_EMATCHES]) { |
| if ((err = rtnl_ematch_parse_attr(tb[TCA_CGROUP_EMATCHES], |
| &c->cg_ematch)) < 0) |
| return err; |
| c->cg_mask |= CGROUP_ATTR_EMATCH; |
| } |
| |
| #if 0 |
| TODO: |
| TCA_CGROUP_ACT, |
| TCA_CGROUP_POLICE, |
| #endif |
| |
| return 0; |
| } |
| |
| static void cgroup_dump_line(struct rtnl_tc *tc, void *data, |
| struct nl_dump_params *p) |
| { |
| struct rtnl_cgroup *c = data; |
| |
| if (!c) |
| return; |
| |
| if (c->cg_mask & CGROUP_ATTR_EMATCH) |
| nl_dump(p, " ematch"); |
| else |
| nl_dump(p, " match-all"); |
| } |
| |
| static void cgroup_dump_details(struct rtnl_tc *tc, void *data, |
| struct nl_dump_params *p) |
| { |
| struct rtnl_cgroup *c = data; |
| |
| if (!c) |
| return; |
| |
| if (c->cg_mask & CGROUP_ATTR_EMATCH) { |
| nl_dump_line(p, " ematch "); |
| |
| if (c->cg_ematch) |
| rtnl_ematch_tree_dump(c->cg_ematch, p); |
| else |
| nl_dump(p, "<no tree>"); |
| } else |
| nl_dump(p, "no options"); |
| } |
| |
| static int cgroup_fill_msg(struct rtnl_tc *tc, void *data, |
| struct nl_msg *msg) |
| { |
| struct rtnl_cgroup *c = data; |
| |
| if (!c) |
| BUG(); |
| |
| if (!(tc->ce_mask & TCA_ATTR_HANDLE)) |
| return -NLE_MISSING_ATTR; |
| |
| if (c->cg_mask & CGROUP_ATTR_EMATCH) |
| return rtnl_ematch_fill_attr(msg, TCA_CGROUP_EMATCHES, |
| c->cg_ematch); |
| |
| return 0; |
| } |
| |
| |
| /** |
| * @name Attribute Modifications |
| * @{ |
| */ |
| |
| void rtnl_cgroup_set_ematch(struct rtnl_cls *cls, struct rtnl_ematch_tree *tree) |
| { |
| struct rtnl_cgroup *c; |
| |
| if (!(c = rtnl_tc_data(TC_CAST(cls)))) |
| BUG(); |
| |
| if (c->cg_ematch) { |
| rtnl_ematch_tree_free(c->cg_ematch); |
| c->cg_mask &= ~CGROUP_ATTR_EMATCH; |
| } |
| |
| c->cg_ematch = tree; |
| |
| if (tree) |
| c->cg_mask |= CGROUP_ATTR_EMATCH; |
| } |
| |
| struct rtnl_ematch_tree *rtnl_cgroup_get_ematch(struct rtnl_cls *cls) |
| { |
| struct rtnl_cgroup *c; |
| |
| if (!(c = rtnl_tc_data(TC_CAST(cls)))) |
| BUG(); |
| |
| return c->cg_ematch; |
| } |
| |
| /** @} */ |
| |
| static struct rtnl_tc_ops cgroup_ops = { |
| .to_kind = "cgroup", |
| .to_type = RTNL_TC_TYPE_CLS, |
| .to_size = sizeof(struct rtnl_cgroup), |
| .to_clone = cgroup_clone, |
| .to_msg_parser = cgroup_msg_parser, |
| .to_free_data = cgroup_free_data, |
| .to_msg_fill = cgroup_fill_msg, |
| .to_dump = { |
| [NL_DUMP_LINE] = cgroup_dump_line, |
| [NL_DUMP_DETAILS] = cgroup_dump_details, |
| }, |
| }; |
| |
| static void _nl_init cgroup_init(void) |
| { |
| rtnl_tc_register(&cgroup_ops); |
| } |
| |
| static void _nl_exit cgroup_exit(void) |
| { |
| rtnl_tc_unregister(&cgroup_ops); |
| } |
| |
| /** @} */ |