// SPDX-License-Identifier: GPL-2.0+ /* Microchip VCAP TC * * Copyright (c) 2023 Microchip Technology Inc. and its subsidiaries. */ #include #include #include #include "vcap_api_client.h" #include "vcap_tc.h" enum vcap_is2_arp_opcode { VCAP_IS2_ARP_REQUEST, VCAP_IS2_ARP_REPLY, VCAP_IS2_RARP_REQUEST, VCAP_IS2_RARP_REPLY, }; enum vcap_arp_opcode { VCAP_ARP_OP_RESERVED, VCAP_ARP_OP_REQUEST, VCAP_ARP_OP_REPLY, }; int vcap_tc_flower_handler_ethaddr_usage(struct vcap_tc_flower_parse_usage *st) { enum vcap_key_field smac_key = VCAP_KF_L2_SMAC; enum vcap_key_field dmac_key = VCAP_KF_L2_DMAC; struct flow_match_eth_addrs match; struct vcap_u48_key smac, dmac; int err = 0; flow_rule_match_eth_addrs(st->frule, &match); if (!is_zero_ether_addr(match.mask->src)) { vcap_netbytes_copy(smac.value, match.key->src, ETH_ALEN); vcap_netbytes_copy(smac.mask, match.mask->src, ETH_ALEN); err = vcap_rule_add_key_u48(st->vrule, smac_key, &smac); if (err) goto out; } if (!is_zero_ether_addr(match.mask->dst)) { vcap_netbytes_copy(dmac.value, match.key->dst, ETH_ALEN); vcap_netbytes_copy(dmac.mask, match.mask->dst, ETH_ALEN); err = vcap_rule_add_key_u48(st->vrule, dmac_key, &dmac); if (err) goto out; } st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_ETH_ADDRS); return err; out: NL_SET_ERR_MSG_MOD(st->fco->common.extack, "eth_addr parse error"); return err; } EXPORT_SYMBOL_GPL(vcap_tc_flower_handler_ethaddr_usage); int vcap_tc_flower_handler_ipv4_usage(struct vcap_tc_flower_parse_usage *st) { int err = 0; if (st->l3_proto == ETH_P_IP) { struct flow_match_ipv4_addrs mt; flow_rule_match_ipv4_addrs(st->frule, &mt); if (mt.mask->src) { err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_SIP, be32_to_cpu(mt.key->src), be32_to_cpu(mt.mask->src)); if (err) goto out; } if (mt.mask->dst) { err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_DIP, be32_to_cpu(mt.key->dst), be32_to_cpu(mt.mask->dst)); if (err) goto out; } } st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_IPV4_ADDRS); return err; out: NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv4_addr parse error"); return err; } EXPORT_SYMBOL_GPL(vcap_tc_flower_handler_ipv4_usage); int vcap_tc_flower_handler_ipv6_usage(struct vcap_tc_flower_parse_usage *st) { int err = 0; if (st->l3_proto == ETH_P_IPV6) { struct flow_match_ipv6_addrs mt; struct vcap_u128_key sip; struct vcap_u128_key dip; flow_rule_match_ipv6_addrs(st->frule, &mt); /* Check if address masks are non-zero */ if (!ipv6_addr_any(&mt.mask->src)) { vcap_netbytes_copy(sip.value, mt.key->src.s6_addr, 16); vcap_netbytes_copy(sip.mask, mt.mask->src.s6_addr, 16); err = vcap_rule_add_key_u128(st->vrule, VCAP_KF_L3_IP6_SIP, &sip); if (err) goto out; } if (!ipv6_addr_any(&mt.mask->dst)) { vcap_netbytes_copy(dip.value, mt.key->dst.s6_addr, 16); vcap_netbytes_copy(dip.mask, mt.mask->dst.s6_addr, 16); err = vcap_rule_add_key_u128(st->vrule, VCAP_KF_L3_IP6_DIP, &dip); if (err) goto out; } } st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_IPV6_ADDRS); return err; out: NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ipv6_addr parse error"); return err; } EXPORT_SYMBOL_GPL(vcap_tc_flower_handler_ipv6_usage); int vcap_tc_flower_handler_portnum_usage(struct vcap_tc_flower_parse_usage *st) { struct flow_match_ports mt; u16 value, mask; int err = 0; flow_rule_match_ports(st->frule, &mt); if (mt.mask->src) { value = be16_to_cpu(mt.key->src); mask = be16_to_cpu(mt.mask->src); err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_SPORT, value, mask); if (err) goto out; } if (mt.mask->dst) { value = be16_to_cpu(mt.key->dst); mask = be16_to_cpu(mt.mask->dst); err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L4_DPORT, value, mask); if (err) goto out; } st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_PORTS); return err; out: NL_SET_ERR_MSG_MOD(st->fco->common.extack, "port parse error"); return err; } EXPORT_SYMBOL_GPL(vcap_tc_flower_handler_portnum_usage); int vcap_tc_flower_handler_cvlan_usage(struct vcap_tc_flower_parse_usage *st) { enum vcap_key_field vid_key = VCAP_KF_8021Q_VID0; enum vcap_key_field pcp_key = VCAP_KF_8021Q_PCP0; struct flow_match_vlan mt; u16 tpid; int err; flow_rule_match_cvlan(st->frule, &mt); tpid = be16_to_cpu(mt.key->vlan_tpid); if (tpid == ETH_P_8021Q) { vid_key = VCAP_KF_8021Q_VID1; pcp_key = VCAP_KF_8021Q_PCP1; } if (mt.mask->vlan_id) { err = vcap_rule_add_key_u32(st->vrule, vid_key, mt.key->vlan_id, mt.mask->vlan_id); if (err) goto out; } if (mt.mask->vlan_priority) { err = vcap_rule_add_key_u32(st->vrule, pcp_key, mt.key->vlan_priority, mt.mask->vlan_priority); if (err) goto out; } st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_CVLAN); return 0; out: NL_SET_ERR_MSG_MOD(st->fco->common.extack, "cvlan parse error"); return err; } EXPORT_SYMBOL_GPL(vcap_tc_flower_handler_cvlan_usage); int vcap_tc_flower_handler_vlan_usage(struct vcap_tc_flower_parse_usage *st, enum vcap_key_field vid_key, enum vcap_key_field pcp_key) { struct flow_match_vlan mt; int err; flow_rule_match_vlan(st->frule, &mt); if (mt.mask->vlan_id) { err = vcap_rule_add_key_u32(st->vrule, vid_key, mt.key->vlan_id, mt.mask->vlan_id); if (err) goto out; } if (mt.mask->vlan_priority) { err = vcap_rule_add_key_u32(st->vrule, pcp_key, mt.key->vlan_priority, mt.mask->vlan_priority); if (err) goto out; } if (mt.mask->vlan_tpid) st->tpid = be16_to_cpu(mt.key->vlan_tpid); st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_VLAN); return 0; out: NL_SET_ERR_MSG_MOD(st->fco->common.extack, "vlan parse error"); return err; } EXPORT_SYMBOL_GPL(vcap_tc_flower_handler_vlan_usage); int vcap_tc_flower_handler_tcp_usage(struct vcap_tc_flower_parse_usage *st) { struct flow_match_tcp mt; u16 tcp_flags_mask; u16 tcp_flags_key; enum vcap_bit val; int err = 0; flow_rule_match_tcp(st->frule, &mt); tcp_flags_key = be16_to_cpu(mt.key->flags); tcp_flags_mask = be16_to_cpu(mt.mask->flags); if (tcp_flags_mask & TCPHDR_FIN) { val = VCAP_BIT_0; if (tcp_flags_key & TCPHDR_FIN) val = VCAP_BIT_1; err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_FIN, val); if (err) goto out; } if (tcp_flags_mask & TCPHDR_SYN) { val = VCAP_BIT_0; if (tcp_flags_key & TCPHDR_SYN) val = VCAP_BIT_1; err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_SYN, val); if (err) goto out; } if (tcp_flags_mask & TCPHDR_RST) { val = VCAP_BIT_0; if (tcp_flags_key & TCPHDR_RST) val = VCAP_BIT_1; err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_RST, val); if (err) goto out; } if (tcp_flags_mask & TCPHDR_PSH) { val = VCAP_BIT_0; if (tcp_flags_key & TCPHDR_PSH) val = VCAP_BIT_1; err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_PSH, val); if (err) goto out; } if (tcp_flags_mask & TCPHDR_ACK) { val = VCAP_BIT_0; if (tcp_flags_key & TCPHDR_ACK) val = VCAP_BIT_1; err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_ACK, val); if (err) goto out; } if (tcp_flags_mask & TCPHDR_URG) { val = VCAP_BIT_0; if (tcp_flags_key & TCPHDR_URG) val = VCAP_BIT_1; err = vcap_rule_add_key_bit(st->vrule, VCAP_KF_L4_URG, val); if (err) goto out; } st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_TCP); return err; out: NL_SET_ERR_MSG_MOD(st->fco->common.extack, "tcp_flags parse error"); return err; } EXPORT_SYMBOL_GPL(vcap_tc_flower_handler_tcp_usage); int vcap_tc_flower_handler_arp_usage(struct vcap_tc_flower_parse_usage *st) { struct flow_match_arp mt; u16 value, mask; u32 ipval, ipmsk; int err; flow_rule_match_arp(st->frule, &mt); if (mt.mask->op) { mask = 0x3; if (st->l3_proto == ETH_P_ARP) { value = mt.key->op == VCAP_ARP_OP_REQUEST ? VCAP_IS2_ARP_REQUEST : VCAP_IS2_ARP_REPLY; } else { /* RARP */ value = mt.key->op == VCAP_ARP_OP_REQUEST ? VCAP_IS2_RARP_REQUEST : VCAP_IS2_RARP_REPLY; } err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_ARP_OPCODE, value, mask); if (err) goto out; } /* The IS2 ARP keyset does not support ARP hardware addresses */ if (!is_zero_ether_addr(mt.mask->sha) || !is_zero_ether_addr(mt.mask->tha)) { err = -EINVAL; goto out; } if (mt.mask->sip) { ipval = be32_to_cpu((__force __be32)mt.key->sip); ipmsk = be32_to_cpu((__force __be32)mt.mask->sip); err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_SIP, ipval, ipmsk); if (err) goto out; } if (mt.mask->tip) { ipval = be32_to_cpu((__force __be32)mt.key->tip); ipmsk = be32_to_cpu((__force __be32)mt.mask->tip); err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_IP4_DIP, ipval, ipmsk); if (err) goto out; } st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_ARP); return 0; out: NL_SET_ERR_MSG_MOD(st->fco->common.extack, "arp parse error"); return err; } EXPORT_SYMBOL_GPL(vcap_tc_flower_handler_arp_usage); int vcap_tc_flower_handler_ip_usage(struct vcap_tc_flower_parse_usage *st) { struct flow_match_ip mt; int err = 0; flow_rule_match_ip(st->frule, &mt); if (mt.mask->tos) { err = vcap_rule_add_key_u32(st->vrule, VCAP_KF_L3_TOS, mt.key->tos, mt.mask->tos); if (err) goto out; } st->used_keys |= BIT_ULL(FLOW_DISSECTOR_KEY_IP); return err; out: NL_SET_ERR_MSG_MOD(st->fco->common.extack, "ip_tos parse error"); return err; } EXPORT_SYMBOL_GPL(vcap_tc_flower_handler_ip_usage);