/* SPDX-License-Identifier: LGPL-2.1-only */ /* * Copyright (c) 2018 Volodymyr Bendiuga */ #include #include #include #include #include #include #include #include #include /** @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) #define FLOWER_ATTR_IPV4_SRC (1 << 12) #define FLOWER_ATTR_IPV4_SRC_MASK (1 << 13) #define FLOWER_ATTR_IPV4_DST (1 << 14) #define FLOWER_ATTR_IPV4_DST_MASK (1 << 15) /** @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 }, [TCA_FLOWER_KEY_VLAN_ETH_TYPE] = { .type = NLA_U16 }, [TCA_FLOWER_KEY_IPV4_SRC] = { .type = NLA_U32 }, [TCA_FLOWER_KEY_IPV4_SRC_MASK] = { .type = NLA_U32 }, [TCA_FLOWER_KEY_IPV4_DST] = { .type = NLA_U32 }, [TCA_FLOWER_KEY_IPV4_DST_MASK] = { .type = NLA_U32 }, }; 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; } if (tb[TCA_FLOWER_KEY_IPV4_SRC]) { f->cf_ipv4_src = nla_get_u32(tb[TCA_FLOWER_KEY_IPV4_SRC]); f->cf_mask |= FLOWER_ATTR_IPV4_SRC; } if (tb[TCA_FLOWER_KEY_IPV4_SRC_MASK]) { f->cf_ipv4_src_mask = nla_get_u32(tb[TCA_FLOWER_KEY_IPV4_SRC_MASK]); f->cf_mask |= FLOWER_ATTR_IPV4_SRC_MASK; } if (tb[TCA_FLOWER_KEY_IPV4_DST]) { f->cf_ipv4_dst = nla_get_u32(tb[TCA_FLOWER_KEY_IPV4_DST]); f->cf_mask |= FLOWER_ATTR_IPV4_DST; } if (tb[TCA_FLOWER_KEY_IPV4_DST_MASK]) { f->cf_ipv4_dst_mask = nla_get_u32(tb[TCA_FLOWER_KEY_IPV4_DST_MASK]); f->cf_mask |= FLOWER_ATTR_IPV4_DST_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_flags); 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); if (f->cf_mask & FLOWER_ATTR_IPV4_SRC) NLA_PUT_U32(msg, TCA_FLOWER_KEY_IPV4_SRC, f->cf_ipv4_src); if (f->cf_mask & FLOWER_ATTR_IPV4_SRC_MASK) NLA_PUT_U32(msg, TCA_FLOWER_KEY_IPV4_SRC_MASK, f->cf_ipv4_src_mask); if (f->cf_mask & FLOWER_ATTR_IPV4_DST) NLA_PUT_U32(msg, TCA_FLOWER_KEY_IPV4_DST, f->cf_ipv4_dst); if (f->cf_mask & FLOWER_ATTR_IPV4_DST_MASK) NLA_PUT_U32(msg, TCA_FLOWER_KEY_IPV4_DST_MASK, f->cf_ipv4_dst_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_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; char addr_str[INET_ADDRSTRLEN]; char mask_str[INET_ADDRSTRLEN]; 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); if (f->cf_mask & FLOWER_ATTR_IPV4_SRC) { inet_ntop(AF_INET, &f->cf_ipv4_src, addr_str, sizeof(addr_str)); inet_ntop(AF_INET, &f->cf_ipv4_src_mask, mask_str, sizeof(mask_str)); nl_dump(p, "IPv4 src %s mask %s\n", addr_str, mask_str); } if (f->cf_mask & FLOWER_ATTR_IPV4_DST) { inet_ntop(AF_INET, &f->cf_ipv4_dst, addr_str, sizeof(addr_str)); inet_ntop(AF_INET, &f->cf_ipv4_dst_mask, mask_str, sizeof(mask_str)); nl_dump(p, "IPv4 dst %s mask %s\n", addr_str, mask_str); } } /** * @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_INVAL; 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_INVAL; 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_INVAL; 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_INVAL; if (!(f->cf_mask & FLOWER_ATTR_DST_MAC)) return -NLE_MISSING_ATTR; if (mac) memcpy(mac, f->cf_dst_mac, ETH_ALEN); if (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_INVAL; if (!(f->cf_mask & FLOWER_ATTR_SRC_MAC)) return -NLE_MISSING_ATTR; if (mac) memcpy(mac, f->cf_src_mac, ETH_ALEN); if (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_INVAL; 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; } /** * Set IPv4 source address for flower classifier * @arg cls Flower classifier. * @arg addr IPv4 source address * @arg mask mask for IPv4 source address * @return 0 on success or a negative error code. */ int rtnl_flower_set_ipv4_src(struct rtnl_cls *cls, in_addr_t addr, in_addr_t mask) { struct rtnl_flower *f; if (!(f = rtnl_tc_data(TC_CAST(cls)))) return -NLE_NOMEM; if (addr) { f->cf_ipv4_src = addr; f->cf_mask |= FLOWER_ATTR_IPV4_SRC; if (mask) { f->cf_ipv4_src_mask = mask; f->cf_mask |= FLOWER_ATTR_IPV4_SRC_MASK; } return 0; } return -NLE_FAILURE; } /** * Get IPv4 source address for flower classifier * @arg cls Flower classifier. * @arg addr IPv4 source address * @arg mask mask for IPv4 source address * @return 0 on success or a negative error code. */ int rtnl_flower_get_ipv4_src(struct rtnl_cls *cls, in_addr_t *out_addr, in_addr_t *out_mask) { struct rtnl_flower *f; if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) return -NLE_INVAL; if (!(f->cf_mask & FLOWER_ATTR_IPV4_SRC)) return -NLE_MISSING_ATTR; if (out_addr) *out_addr = f->cf_ipv4_src; if (out_mask) { if (f->cf_mask & FLOWER_ATTR_IPV4_SRC_MASK) *out_mask = f->cf_ipv4_src_mask; else *out_mask = 0xffffffff; } return 0; } /** * Set IPv4 destination address for flower classifier * @arg cls Flower classifier. * @arg addr IPv4 destination address * @arg mask mask for IPv4 destination address * @return 0 on success or a negative error code. */ int rtnl_flower_set_ipv4_dst(struct rtnl_cls *cls, in_addr_t addr, in_addr_t mask) { struct rtnl_flower *f; if (!(f = rtnl_tc_data(TC_CAST(cls)))) return -NLE_NOMEM; if (addr) { f->cf_ipv4_dst = addr; f->cf_mask |= FLOWER_ATTR_IPV4_DST; if (mask) { f->cf_ipv4_dst_mask = mask; f->cf_mask |= FLOWER_ATTR_IPV4_DST_MASK; } return 0; } return -NLE_FAILURE; } /** * Get IPv4 destination address for flower classifier * @arg cls Flower classifier. * @arg addr IPv4 destination address * @arg mask mask for IPv4 destination address * @return 0 on success or a negative error code. */ int rtnl_flower_get_ipv4_dst(struct rtnl_cls *cls, in_addr_t *out_addr, in_addr_t *out_mask) { struct rtnl_flower *f; if (!(f = rtnl_tc_data_peek(TC_CAST(cls)))) return -NLE_INVAL; if (!(f->cf_mask & FLOWER_ATTR_IPV4_DST)) return -NLE_MISSING_ATTR; if (out_addr) *out_addr = f->cf_ipv4_dst; if (out_mask) { if (f->cf_mask & FLOWER_ATTR_IPV4_DST_MASK) *out_mask = f->cf_ipv4_dst_mask; else *out_mask = 0xffffffff; } 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); }