diff options
author | Volodymyr Bendiuga <volodymyr.bendiuga@westermo.se> | 2018-05-03 09:20:53 (GMT) |
---|---|---|
committer | Thomas Haller <thaller@redhat.com> | 2022-03-15 09:33:42 (GMT) |
commit | ef46de143206d118ff777e29f4b2d5fe78c62ada (patch) | |
tree | f5f2e6626180277d97360c02aade9a1daedeaf0a /lib/route | |
parent | f0aad20815bf1a671071e17a227ec1a532376f11 (diff) | |
download | libnl-ef46de143206d118ff777e29f4b2d5fe78c62ada.zip libnl-ef46de143206d118ff777e29f4b2d5fe78c62ada.tar.gz libnl-ef46de143206d118ff777e29f4b2d5fe78c62ada.tar.bz2 |
route/cls: add flower classifier
This patch adds a subset of functions.
Implemented api:
rtnl_flower_set_proto;
rtnl_flower_get_proto;
rtnl_flower_set_vlan_id;
rtnl_flower_get_vlan_id;
rtnl_flower_set_vlan_prio;
rtnl_flower_get_vlan_prio;
rtnl_flower_set_vlan_ethtype;
rtnl_flower_set_dst_mac;
rtnl_flower_get_dst_mac;
rtnl_flower_set_src_mac;
rtnl_flower_get_src_mac;
rtnl_flower_set_ip_dscp;
rtnl_flower_get_ip_dscp;
rtnl_flower_set_flags;
rtnl_flower_append_action;
rtnl_flower_del_action;
rtnl_flower_get_action;
[thaller@redhat.com: squashed commit "route:cls:flower: use parentheses in
macro definitions"]
[thaller@redhat.com: squashed commit "cls:flower: add TCA_FLOWER_FLAGS
to flower_policy"]
[thaller@redhat.com: squashed commit "cls:flower: vlan priority is
uint8_t, not uint16_t"]
[thaller@redhat.com: squashed commit "route:cls:flower: substitute nl_data*
with uint8_t mac[ETH_ALEN]"]
[thaller@redhat.com: drop non-existing TCA_FLOWER_POLICE. That was
never merged to upstream kernel. While at it, use decimal numbers
for the bitshift.]
[thaller@redhat.com: fix build by including <linux/if_ether.h> in
"types.h".]
Signed-off-by: Volodymyr Bendiuga <volodymyr.bendiuga@westermo.se>
Diffstat (limited to 'lib/route')
-rw-r--r-- | lib/route/cls/flower.c | 731 |
1 files changed, 731 insertions, 0 deletions
diff --git a/lib/route/cls/flower.c b/lib/route/cls/flower.c new file mode 100644 index 0000000..7951376 --- /dev/null +++ b/lib/route/cls/flower.c @@ -0,0 +1,731 @@ +/* + * lib/route/cls/flower.c Flow based traffic control filter + * + * 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 Volodymyr Bendiuga <volodymyr.bendiuga@gmail.com> + */ + +#include <netlink-private/netlink.h> +#include <netlink-private/tc.h> +#include <netlink/netlink.h> +#include <netlink/attr.h> +#include <netlink/utils.h> +#include <netlink-private/route/tc-api.h> +#include <netlink/route/classifier.h> +#include <netlink/route/action.h> +#include <netlink/route/cls/flower.h> + + +/** @cond SKIP */ +#define FLOWER_ATTR_FLAGS (1 << 0) +#define FLOWER_ATTR_ACTION (1 << 1) +#define FLOWER_ATTR_VLAN_ID (1 << 2) +#define FLOWER_ATTR_VLAN_PRIO (1 << 3) +#define FLOWER_ATTR_VLAN_ETH_TYPE (1 << 4) +#define FLOWER_ATTR_DST_MAC (1 << 5) +#define FLOWER_ATTR_DST_MAC_MASK (1 << 6) +#define FLOWER_ATTR_SRC_MAC (1 << 7) +#define FLOWER_ATTR_SRC_MAC_MASK (1 << 8) +#define FLOWER_ATTR_IP_DSCP (1 << 9) +#define FLOWER_ATTR_IP_DSCP_MASK (1 << 10) +#define FLOWER_ATTR_PROTO (1 << 11) +/** @endcond */ + +#define FLOWER_DSCP_MAX 0xe0 +#define FLOWER_DSCP_MASK_MAX 0xe0 +#define FLOWER_VID_MAX 4095 +#define FLOWER_VLAN_PRIO_MAX 7 + +static struct nla_policy flower_policy[TCA_FLOWER_MAX + 1] = { + [TCA_FLOWER_FLAGS] = { .type = NLA_U32 }, + [TCA_FLOWER_KEY_ETH_TYPE] = { .type = NLA_U16 }, + [TCA_FLOWER_KEY_ETH_DST] = { .maxlen = ETH_ALEN }, + [TCA_FLOWER_KEY_ETH_DST_MASK] = { .maxlen = ETH_ALEN }, + [TCA_FLOWER_KEY_ETH_SRC] = { .maxlen = ETH_ALEN }, + [TCA_FLOWER_KEY_ETH_SRC_MASK] = { .maxlen = ETH_ALEN }, + [TCA_FLOWER_KEY_VLAN_ID] = { .type = NLA_U16 }, + [TCA_FLOWER_KEY_VLAN_PRIO] = { .type = NLA_U8 }, + [TCA_FLOWER_KEY_IP_TOS] = { .type = NLA_U8 }, + [TCA_FLOWER_KEY_IP_TOS_MASK] = { .type = NLA_U8 }, +}; + +static int flower_msg_parser(struct rtnl_tc *tc, void *data) +{ + struct rtnl_flower *f = data; + struct nlattr *tb[TCA_FLOWER_MAX + 1]; + int err; + + err = tca_parse(tb, TCA_FLOWER_MAX, tc, flower_policy); + if (err < 0) + return err; + + if (tb[TCA_FLOWER_FLAGS]) { + f->cf_flags = nla_get_u32(tb[TCA_FLOWER_FLAGS]); + f->cf_mask |= FLOWER_ATTR_FLAGS; + } + + if (tb[TCA_FLOWER_ACT]) { + err = rtnl_act_parse(&f->cf_act, tb[TCA_FLOWER_ACT]); + if (err) + return err; + + f->cf_mask |= FLOWER_ATTR_ACTION; + } + + if (tb[TCA_FLOWER_KEY_ETH_TYPE]) { + f->cf_proto = nla_get_u16(tb[TCA_FLOWER_KEY_ETH_TYPE]); + f->cf_mask |= FLOWER_ATTR_PROTO; + } + + if (tb[TCA_FLOWER_KEY_VLAN_ID]) { + f->cf_vlan_id = nla_get_u16(tb[TCA_FLOWER_KEY_VLAN_ID]); + f->cf_mask |= FLOWER_ATTR_VLAN_ID; + } + + if (tb[TCA_FLOWER_KEY_VLAN_PRIO]) { + f->cf_vlan_prio = nla_get_u8(tb[TCA_FLOWER_KEY_VLAN_PRIO]); + f->cf_mask |= FLOWER_ATTR_VLAN_PRIO; + } + + if (tb[TCA_FLOWER_KEY_VLAN_ETH_TYPE]) { + f->cf_vlan_ethtype = nla_get_u16(tb[TCA_FLOWER_KEY_VLAN_ETH_TYPE]); + f->cf_mask |= FLOWER_ATTR_VLAN_ETH_TYPE; + } + + if (tb[TCA_FLOWER_KEY_ETH_DST]) { + nla_memcpy(f->cf_dst_mac, tb[TCA_FLOWER_KEY_ETH_DST], ETH_ALEN); + f->cf_mask |= FLOWER_ATTR_DST_MAC; + } + + if (tb[TCA_FLOWER_KEY_ETH_DST_MASK]) { + nla_memcpy(f->cf_dst_mac_mask, tb[TCA_FLOWER_KEY_ETH_DST_MASK], ETH_ALEN); + f->cf_mask |= FLOWER_ATTR_DST_MAC_MASK; + } + + if (tb[TCA_FLOWER_KEY_ETH_SRC]) { + nla_memcpy(f->cf_src_mac, tb[TCA_FLOWER_KEY_ETH_SRC], ETH_ALEN); + f->cf_mask |= FLOWER_ATTR_SRC_MAC; + } + + if (tb[TCA_FLOWER_KEY_ETH_SRC_MASK]) { + nla_memcpy(f->cf_src_mac_mask, tb[TCA_FLOWER_KEY_ETH_SRC_MASK], ETH_ALEN); + f->cf_mask |= FLOWER_ATTR_SRC_MAC_MASK; + } + + if (tb[TCA_FLOWER_KEY_IP_TOS]) { + f->cf_ip_dscp = nla_get_u8(tb[TCA_FLOWER_KEY_IP_TOS]); + f->cf_mask |= FLOWER_ATTR_IP_DSCP; + } + + if (tb[TCA_FLOWER_KEY_IP_TOS_MASK]) { + f->cf_ip_dscp_mask = nla_get_u8(tb[TCA_FLOWER_KEY_IP_TOS_MASK]); + f->cf_mask |= FLOWER_ATTR_IP_DSCP_MASK; + } + + return 0; +} + +static int flower_msg_fill(struct rtnl_tc *tc, void *data, struct nl_msg *msg) +{ + struct rtnl_flower *f = data; + int err; + + if (!f) + return 0; + + if (f->cf_mask & FLOWER_ATTR_FLAGS) + NLA_PUT_U32(msg, TCA_FLOWER_FLAGS, f->cf_mask); + + if (f->cf_mask & FLOWER_ATTR_ACTION) { + err = rtnl_act_fill(msg, TCA_FLOWER_ACT, f->cf_act); + if (err) + return err; + } + + if (f->cf_mask & FLOWER_ATTR_PROTO) + NLA_PUT_U16(msg, TCA_FLOWER_KEY_ETH_TYPE, f->cf_proto); + + if (f->cf_mask & FLOWER_ATTR_VLAN_ID) + NLA_PUT_U16(msg, TCA_FLOWER_KEY_VLAN_ID, f->cf_vlan_id); + + if (f->cf_mask & FLOWER_ATTR_VLAN_PRIO) + NLA_PUT_U8(msg, TCA_FLOWER_KEY_VLAN_PRIO, f->cf_vlan_prio); + + if (f->cf_mask & FLOWER_ATTR_VLAN_ETH_TYPE) + NLA_PUT_U16(msg, TCA_FLOWER_KEY_VLAN_ETH_TYPE, f->cf_vlan_ethtype); + + if (f->cf_mask & FLOWER_ATTR_DST_MAC) + NLA_PUT(msg, TCA_FLOWER_KEY_ETH_DST, ETH_ALEN, f->cf_dst_mac); + + if (f->cf_mask & FLOWER_ATTR_DST_MAC_MASK) + NLA_PUT(msg, TCA_FLOWER_KEY_ETH_DST_MASK, ETH_ALEN, f->cf_dst_mac_mask); + + if (f->cf_mask & FLOWER_ATTR_SRC_MAC) + NLA_PUT(msg, TCA_FLOWER_KEY_ETH_SRC, ETH_ALEN, f->cf_src_mac); + + if (f->cf_mask & FLOWER_ATTR_SRC_MAC_MASK) + NLA_PUT(msg, TCA_FLOWER_KEY_ETH_SRC_MASK, ETH_ALEN, f->cf_src_mac_mask); + + if (f->cf_mask & FLOWER_ATTR_IP_DSCP) + NLA_PUT_U8(msg, TCA_FLOWER_KEY_IP_TOS, f->cf_ip_dscp); + + if (f->cf_mask & FLOWER_ATTR_IP_DSCP_MASK) + NLA_PUT_U8(msg, TCA_FLOWER_KEY_IP_TOS_MASK, f->cf_ip_dscp_mask); + + return 0; + + nla_put_failure: + return -NLE_NOMEM; +} + +static void flower_free_data(struct rtnl_tc *tc, void *data) +{ + struct rtnl_flower *f = data; + + if (f->cf_act) + rtnl_act_put_all(&f->cf_act); +} + +static int flower_clone(void *_dst, void *_src) +{ + struct rtnl_flower *dst = _dst, *src = _src; + + if (src->cf_mask & FLOWER_ATTR_DST_MAC) + memcpy(dst->cf_dst_mac, src->cf_dst_mac, ETH_ALEN); + + if (src->cf_mask & FLOWER_ATTR_DST_MAC_MASK) + memcpy(dst->cf_dst_mac_mask, src->cf_dst_mac_mask, ETH_ALEN); + + if (src->cf_mask & FLOWER_ATTR_SRC_MAC) + memcpy(dst->cf_src_mac, src->cf_src_mac, ETH_ALEN); + + if (src->cf_mask & FLOWER_ATTR_SRC_MAC_MASK) + memcpy(dst->cf_src_mac_mask, src->cf_src_mac_mask, ETH_ALEN); + + if (src->cf_act) { + if (!(dst->cf_act = rtnl_act_alloc())) + return -NLE_NOMEM; + + memcpy(dst->cf_act, src->cf_act, sizeof(struct rtnl_act)); + + /* action nl list next and prev pointers must be updated */ + nl_init_list_head(&dst->cf_act->ce_list); + + if (src->cf_act->c_opts && + !(dst->cf_act->c_opts = nl_data_clone(src->cf_act->c_opts))) + return -NLE_NOMEM; + + if (src->cf_act->c_xstats && + !(dst->cf_act->c_xstats = nl_data_clone(src->cf_act->c_xstats))) + return -NLE_NOMEM; + + if (src->cf_act->c_subdata && + !(dst->cf_act->c_subdata = nl_data_clone(src->cf_act->c_subdata))) + return -NLE_NOMEM; + + if (dst->cf_act->c_link) { + nl_object_get(OBJ_CAST(dst->cf_act->c_link)); + } + + dst->cf_act->a_next = NULL; /* Only clone first in chain */ + } + + return 0; +} + +static void flower_dump_details(struct rtnl_tc *tc, void *data, + struct nl_dump_params *p) +{ + struct rtnl_flower *f = data; + + if (!f) + return; + + if (f->cf_mask & FLOWER_ATTR_FLAGS) + nl_dump(p, " flags %u", f->cf_flags); + + if (f->cf_mask & FLOWER_ATTR_PROTO) + nl_dump(p, " protocol %u", f->cf_proto); + + if (f->cf_mask & FLOWER_ATTR_VLAN_ID) + nl_dump(p, " vlan_id %u", f->cf_vlan_id); + + if (f->cf_mask & FLOWER_ATTR_VLAN_PRIO) + nl_dump(p, " vlan_prio %u", f->cf_vlan_prio); + + if (f->cf_mask & FLOWER_ATTR_VLAN_ETH_TYPE) + nl_dump(p, " vlan_ethtype %u", f->cf_vlan_ethtype); + + if (f->cf_mask & FLOWER_ATTR_DST_MAC) + nl_dump(p, " dst_mac %02x:%02x:%02x:%02x:%02x:%02x", + f->cf_dst_mac[0], f->cf_dst_mac[1], + f->cf_dst_mac[2], f->cf_dst_mac[3], + f->cf_dst_mac[4], f->cf_dst_mac[5]); + + if (f->cf_mask & FLOWER_ATTR_DST_MAC_MASK) + nl_dump(p, " dst_mac_mask %02x:%02x:%02x:%02x:%02x:%02x", + f->cf_dst_mac_mask[0], f->cf_dst_mac_mask[1], + f->cf_dst_mac_mask[2], f->cf_dst_mac_mask[3], + f->cf_dst_mac_mask[4], f->cf_dst_mac_mask[5]); + + if (f->cf_mask & FLOWER_ATTR_SRC_MAC) + nl_dump(p, " src_mac %02x:%02x:%02x:%02x:%02x:%02x", + f->cf_src_mac[0], f->cf_src_mac[1], + f->cf_src_mac[2], f->cf_src_mac[3], + f->cf_src_mac[4], f->cf_src_mac[5]); + + if (f->cf_mask & FLOWER_ATTR_SRC_MAC_MASK) + nl_dump(p, " src_mac_mask %02x:%02x:%02x:%02x:%02x:%02x", + f->cf_src_mac_mask[0], f->cf_src_mac_mask[1], + f->cf_src_mac_mask[2], f->cf_src_mac_mask[3], + f->cf_src_mac_mask[4], f->cf_src_mac_mask[5]); + + if (f->cf_mask & FLOWER_ATTR_IP_DSCP) + nl_dump(p, " dscp %u", f->cf_ip_dscp); + + if (f->cf_mask & FLOWER_ATTR_IP_DSCP_MASK) + nl_dump(p, " dscp_mask %u", f->cf_ip_dscp_mask); +} + +/** + * @name Attribute Modification + * @{ + */ + +/** + * Set protocol for flower classifier + * @arg cls Flower classifier. + * @arg proto protocol (ETH_P_*) + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_proto(struct rtnl_cls *cls, uint16_t proto) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + f->cf_proto = htons(proto); + f->cf_mask |= FLOWER_ATTR_PROTO; + + return 0; +} + +/** + * Get protocol for flower classifier + * @arg cls Flower classifier. + * @arg proto protocol + * @return 0 on success or a negative error code. +*/ +int rtnl_flower_get_proto(struct rtnl_cls *cls, uint16_t *proto) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(f->cf_mask & FLOWER_ATTR_PROTO)) + return -NLE_MISSING_ATTR; + + *proto = ntohs(f->cf_proto); + + return 0; +} + +/** + * Set vlan id for flower classifier + * @arg cls Flower classifier. + * @arg vid vlan id + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_vlan_id(struct rtnl_cls *cls, uint16_t vid) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (vid > FLOWER_VID_MAX) + return -NLE_RANGE; + + f->cf_vlan_id = vid; + f->cf_mask |= FLOWER_ATTR_VLAN_ID; + + return 0; +} + +/** + * Get vlan id for flower classifier + * @arg cls Flower classifier. + * @arg vid vlan id + * @return 0 on success or a negative error code. +*/ +int rtnl_flower_get_vlan_id(struct rtnl_cls *cls, uint16_t *vid) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(f->cf_mask & FLOWER_ATTR_VLAN_ID)) + return -NLE_MISSING_ATTR; + + *vid = f->cf_vlan_id; + + return 0; +} + +/** + * Set vlan priority for flower classifier + * @arg cls Flower classifier. + * @arg prio vlan priority + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_vlan_prio(struct rtnl_cls *cls, uint8_t prio) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (prio > FLOWER_VLAN_PRIO_MAX) + return -NLE_RANGE; + + f->cf_vlan_prio = prio; + f->cf_mask |= FLOWER_ATTR_VLAN_PRIO; + + return 0; +} + +/** + * Get vlan prio for flower classifier + * @arg cls Flower classifier. + * @arg prio vlan priority + * @return 0 on success or a negative error code. +*/ +int rtnl_flower_get_vlan_prio(struct rtnl_cls *cls, uint8_t *prio) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(f->cf_mask & FLOWER_ATTR_VLAN_PRIO)) + return -NLE_MISSING_ATTR; + + *prio = f->cf_vlan_prio; + + return 0; +} + +/** + * Set vlan ethertype for flower classifier + * @arg cls Flower classifier. + * @arg ethtype vlan ethertype + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_vlan_ethtype(struct rtnl_cls *cls, uint16_t ethtype) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(f->cf_mask & FLOWER_ATTR_PROTO)) + return -NLE_MISSING_ATTR; + + if (f->cf_proto != htons(ETH_P_8021Q)) + return -NLE_INVAL; + + f->cf_vlan_ethtype = htons(ethtype); + f->cf_mask |= FLOWER_ATTR_VLAN_ETH_TYPE; + + return 0; +} + +/** + * Set destination mac address for flower classifier + * @arg cls Flower classifier. + * @arg mac destination mac address + * @arg mask mask for mac address + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_dst_mac(struct rtnl_cls *cls, unsigned char *mac, + unsigned char *mask) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (mac) { + memcpy(f->cf_dst_mac, mac, ETH_ALEN); + f->cf_mask |= FLOWER_ATTR_DST_MAC; + + if (mask) { + memcpy(f->cf_dst_mac_mask, mask, ETH_ALEN); + f->cf_mask |= FLOWER_ATTR_DST_MAC_MASK; + } + + return 0; + } + + return -NLE_FAILURE; +} + +/** + * Get destination mac address for flower classifier + * @arg cls Flower classifier. + * @arg mac destination mac address + * @arg mask mask for mac address + * @return 0 on success or a negative error code. +*/ +int rtnl_flower_get_dst_mac(struct rtnl_cls *cls, unsigned char *mac, + unsigned char *mask) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(f->cf_mask & FLOWER_ATTR_DST_MAC)) + return -NLE_MISSING_ATTR; + + memcpy(mac, f->cf_dst_mac, ETH_ALEN); + + if (f->cf_mask & FLOWER_ATTR_DST_MAC_MASK) + memcpy(mask, f->cf_dst_mac_mask, ETH_ALEN); + + return 0; +} + +/** + * Set source mac address for flower classifier + * @arg cls Flower classifier. + * @arg mac source mac address + * @arg mask mask for mac address + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_src_mac(struct rtnl_cls *cls, unsigned char *mac, + unsigned char *mask) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (mac) { + memcpy(f->cf_src_mac, mac, ETH_ALEN); + f->cf_mask |= FLOWER_ATTR_SRC_MAC; + + if (mask) { + memcpy(f->cf_src_mac_mask, mask, ETH_ALEN); + f->cf_mask |= FLOWER_ATTR_SRC_MAC_MASK; + } + + return 0; + } + + return -NLE_FAILURE; +} + +/** + * Get source mac address for flower classifier + * @arg cls Flower classifier. + * @arg mac source mac address + * @arg mask mask for mac address + * @return 0 on success or a negative error code. +*/ +int rtnl_flower_get_src_mac(struct rtnl_cls *cls, unsigned char *mac, + unsigned char *mask) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(f->cf_mask & FLOWER_ATTR_SRC_MAC)) + return -NLE_MISSING_ATTR; + + memcpy(mac, f->cf_src_mac, ETH_ALEN); + + if (f->cf_mask & FLOWER_ATTR_SRC_MAC_MASK) + memcpy(mask, f->cf_src_mac_mask, ETH_ALEN); + + return 0; +} + +/** + * Set dscp value for flower classifier + * @arg cls Flower classifier. + * @arg dscp dscp value + * @arg mask mask for dscp value + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_ip_dscp(struct rtnl_cls *cls, uint8_t dscp, uint8_t mask) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (dscp > FLOWER_DSCP_MAX) + return -NLE_RANGE; + + if (mask > FLOWER_DSCP_MASK_MAX) + return -NLE_RANGE; + + f->cf_ip_dscp = dscp; + f->cf_mask |= FLOWER_ATTR_IP_DSCP; + + if (mask) { + f->cf_ip_dscp_mask = mask; + f->cf_mask |= FLOWER_ATTR_IP_DSCP_MASK; + } + + return 0; +} + +/** + * Get dscp value for flower classifier + * @arg cls Flower classifier. + * @arg dscp dscp value + * @arg mask mask for dscp value + * @return 0 on success or a negative error code. +*/ +int rtnl_flower_get_ip_dscp(struct rtnl_cls *cls, uint8_t *dscp, uint8_t *mask) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(f->cf_mask & FLOWER_ATTR_IP_DSCP)) + return -NLE_MISSING_ATTR; + + *dscp = f->cf_ip_dscp; + *mask = f->cf_ip_dscp_mask; + + return 0; +} + +/** + * Append action for flower classifier + * @arg cls Flower classifier. + * @arg act action to append + * @return 0 on success or a negative error code. + */ +int rtnl_flower_append_action(struct rtnl_cls *cls, struct rtnl_act *act) +{ + struct rtnl_flower *f; + + if (!act) + return 0; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + f->cf_mask |= FLOWER_ATTR_ACTION; + + rtnl_act_get(act); + return rtnl_act_append(&f->cf_act, act); +} + +/** + * Delete action from flower classifier + * @arg cls Flower classifier. + * @arg act action to delete + * @return 0 on success or a negative error code. + */ +int rtnl_flower_del_action(struct rtnl_cls *cls, struct rtnl_act *act) +{ + struct rtnl_flower *f; + int ret; + + if (!act) + return 0; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + if (!(f->cf_mask & FLOWER_ATTR_ACTION)) + return -NLE_INVAL; + + ret = rtnl_act_remove(&f->cf_act, act); + if (ret) + return ret; + + if (!f->cf_act) + f->cf_mask &= ~FLOWER_ATTR_ACTION; + rtnl_act_put(act); + + return 0; +} + +/** + * Get action from flower classifier + * @arg cls Flower classifier. + * @return action on success or NULL on error. + */ +struct rtnl_act* rtnl_flower_get_action(struct rtnl_cls *cls) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) + return NULL; + + if (!(f->cf_mask & FLOWER_ATTR_ACTION)) + return NULL; + + rtnl_act_get(f->cf_act); + + return f->cf_act; +} + +/** + * Set flags for flower classifier + * @arg cls Flower classifier. + * @arg flags (TCA_CLS_FLAGS_SKIP_HW | TCA_CLS_FLAGS_SKIP_SW) + * @return 0 on success or a negative error code. + */ +int rtnl_flower_set_flags(struct rtnl_cls *cls, int flags) +{ + struct rtnl_flower *f; + + if (!(f = rtnl_tc_data(TC_CAST(cls)))) + return -NLE_NOMEM; + + f->cf_flags = flags; + f->cf_mask |= FLOWER_ATTR_FLAGS; + + return 0; +} + +/** @} */ + +static struct rtnl_tc_ops flower_ops = { + .to_kind = "flower", + .to_type = RTNL_TC_TYPE_CLS, + .to_size = sizeof(struct rtnl_flower), + .to_msg_parser = flower_msg_parser, + .to_free_data = flower_free_data, + .to_clone = flower_clone, + .to_msg_fill = flower_msg_fill, + .to_dump = { + [NL_DUMP_DETAILS] = flower_dump_details, + }, +}; + +static void __init flower_init(void) +{ + rtnl_tc_register(&flower_ops); +} + +static void __exit flower_exit(void) +{ + rtnl_tc_unregister(&flower_ops); +} |