blob: 7951376d4e17d8748c2804c233f355e69dccfc65 [file] [log] [blame]
/*
* 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);
}