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>
diff --git a/Makefile.am b/Makefile.am
index 4fa2147..79f0d2e 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -130,6 +130,7 @@
include/netlink/route/cls/basic.h \
include/netlink/route/cls/cgroup.h \
include/netlink/route/cls/ematch.h \
+ include/netlink/route/cls/flower.h \
include/netlink/route/cls/fw.h \
include/netlink/route/cls/matchall.h \
include/netlink/route/cls/police.h \
@@ -392,6 +393,7 @@
lib/route/cls/ematch/meta.c \
lib/route/cls/ematch/nbyte.c \
lib/route/cls/ematch/text.c \
+ lib/route/cls/flower.c \
lib/route/cls/fw.c \
lib/route/cls/mall.c \
lib/route/cls/police.c \
diff --git a/include/netlink-private/types.h b/include/netlink-private/types.h
index 8e13222..be10d7a 100644
--- a/include/netlink-private/types.h
+++ b/include/netlink-private/types.h
@@ -27,6 +27,7 @@
#include <linux/tc_act/tc_vlan.h>
#include <linux/sock_diag.h>
#include <linux/fib_rules.h>
+#include <linux/if_ether.h>
#define NL_SOCK_PASSCRED (1<<1)
#define NL_OWN_PORT (1<<2)
@@ -610,6 +611,23 @@
int m_mask;
};
+struct rtnl_flower
+{
+ uint16_t cf_proto;
+ uint16_t cf_vlan_id;
+ uint8_t cf_vlan_prio;
+ uint16_t cf_vlan_ethtype;
+ uint8_t cf_src_mac[ETH_ALEN];
+ uint8_t cf_src_mac_mask[ETH_ALEN];
+ uint8_t cf_dst_mac[ETH_ALEN];
+ uint8_t cf_dst_mac_mask[ETH_ALEN];
+ uint8_t cf_ip_dscp;
+ uint8_t cf_ip_dscp_mask;
+ uint32_t cf_flags;
+ struct rtnl_act * cf_act;
+ int cf_mask;
+};
+
struct rtnl_cgroup
{
struct rtnl_ematch_tree *cg_ematch;
diff --git a/include/netlink/route/cls/flower.h b/include/netlink/route/cls/flower.h
new file mode 100644
index 0000000..f39ce1c
--- /dev/null
+++ b/include/netlink/route/cls/flower.h
@@ -0,0 +1,58 @@
+/*
+ * netlink/route/cls/flower.h flower classifier
+ *
+ * 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@westermo.se>
+ */
+
+#ifndef NETLINK_FLOWER_H_
+#define NETLINK_FLOWER_H_
+
+#include <netlink/netlink.h>
+#include <netlink/cache.h>
+#include <netlink/route/classifier.h>
+#include <netlink/route/action.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern int rtnl_flower_set_proto(struct rtnl_cls *cls, uint16_t);
+extern int rtnl_flower_get_proto(struct rtnl_cls *cls, uint16_t *);
+
+extern int rtnl_flower_set_vlan_id(struct rtnl_cls *, uint16_t);
+extern int rtnl_flower_get_vlan_id(struct rtnl_cls *, uint16_t *);
+
+extern int rtnl_flower_set_vlan_prio(struct rtnl_cls *, uint8_t);
+extern int rtnl_flower_get_vlan_prio(struct rtnl_cls *, uint8_t *);
+
+extern int rtnl_flower_set_vlan_ethtype(struct rtnl_cls *, uint16_t);
+
+extern int rtnl_flower_set_dst_mac(struct rtnl_cls *, unsigned char *,
+ unsigned char *);
+extern int rtnl_flower_get_dst_mac(struct rtnl_cls *, unsigned char *,
+ unsigned char *);
+
+extern int rtnl_flower_set_src_mac(struct rtnl_cls *, unsigned char *,
+ unsigned char *);
+extern int rtnl_flower_get_src_mac(struct rtnl_cls *, unsigned char *,
+ unsigned char *);
+
+extern int rtnl_flower_set_ip_dscp(struct rtnl_cls *, uint8_t, uint8_t);
+extern int rtnl_flower_get_ip_dscp(struct rtnl_cls *, uint8_t *, uint8_t *);
+
+extern int rtnl_flower_set_flags(struct rtnl_cls *, int);
+
+extern int rtnl_flower_append_action(struct rtnl_cls *, struct rtnl_act *);
+extern int rtnl_flower_del_action(struct rtnl_cls *, struct rtnl_act *);
+extern struct rtnl_act* rtnl_flower_get_action(struct rtnl_cls *);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
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);
+}
diff --git a/libnl-route-3.sym b/libnl-route-3.sym
index 6fdd6a9..8561705 100644
--- a/libnl-route-3.sym
+++ b/libnl-route-3.sym
@@ -1155,6 +1155,23 @@
global:
rtnl_cls_find_by_handle;
rtnl_cls_find_by_prio;
+ rtnl_flower_append_action;
+ rtnl_flower_del_action;
+ rtnl_flower_get_action;
+ rtnl_flower_get_dst_mac;
+ rtnl_flower_get_ip_dscp;
+ rtnl_flower_get_proto;
+ rtnl_flower_get_src_mac;
+ rtnl_flower_get_vlan_id;
+ rtnl_flower_get_vlan_prio;
+ rtnl_flower_set_dst_mac;
+ rtnl_flower_set_flags;
+ rtnl_flower_set_ip_dscp;
+ rtnl_flower_set_proto;
+ rtnl_flower_set_src_mac;
+ rtnl_flower_set_vlan_ethtype;
+ rtnl_flower_set_vlan_id;
+ rtnl_flower_set_vlan_prio;
rtnl_link_can_get_data_bittiming;
rtnl_link_can_get_data_bittiming_const;
rtnl_link_can_set_data_bittiming;