summaryrefslogtreecommitdiffstats
path: root/lib/route
diff options
context:
space:
mode:
authorVolodymyr Bendiuga <volodymyr.bendiuga@westermo.se>2018-05-03 09:20:53 (GMT)
committerThomas Haller <thaller@redhat.com>2022-03-15 09:33:42 (GMT)
commitef46de143206d118ff777e29f4b2d5fe78c62ada (patch)
treef5f2e6626180277d97360c02aade9a1daedeaf0a /lib/route
parentf0aad20815bf1a671071e17a227ec1a532376f11 (diff)
downloadlibnl-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.c731
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);
+}