| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298 |
- // SPDX-License-Identifier: (GPL-2.0 OR MIT)
- /* Google virtual Ethernet (gve) driver
- *
- * Copyright (C) 2015-2024 Google LLC
- */
- #include "gve.h"
- #include "gve_adminq.h"
- static
- int gve_fill_ethtool_flow_spec(struct ethtool_rx_flow_spec *fsp,
- struct gve_adminq_queried_flow_rule *rule)
- {
- struct gve_adminq_flow_rule *flow_rule = &rule->flow_rule;
- static const u16 flow_type_lut[] = {
- [GVE_FLOW_TYPE_TCPV4] = TCP_V4_FLOW,
- [GVE_FLOW_TYPE_UDPV4] = UDP_V4_FLOW,
- [GVE_FLOW_TYPE_SCTPV4] = SCTP_V4_FLOW,
- [GVE_FLOW_TYPE_AHV4] = AH_V4_FLOW,
- [GVE_FLOW_TYPE_ESPV4] = ESP_V4_FLOW,
- [GVE_FLOW_TYPE_TCPV6] = TCP_V6_FLOW,
- [GVE_FLOW_TYPE_UDPV6] = UDP_V6_FLOW,
- [GVE_FLOW_TYPE_SCTPV6] = SCTP_V6_FLOW,
- [GVE_FLOW_TYPE_AHV6] = AH_V6_FLOW,
- [GVE_FLOW_TYPE_ESPV6] = ESP_V6_FLOW,
- };
- if (be16_to_cpu(flow_rule->flow_type) >= ARRAY_SIZE(flow_type_lut))
- return -EINVAL;
- fsp->flow_type = flow_type_lut[be16_to_cpu(flow_rule->flow_type)];
- memset(&fsp->h_u, 0, sizeof(fsp->h_u));
- memset(&fsp->h_ext, 0, sizeof(fsp->h_ext));
- memset(&fsp->m_u, 0, sizeof(fsp->m_u));
- memset(&fsp->m_ext, 0, sizeof(fsp->m_ext));
- switch (fsp->flow_type) {
- case TCP_V4_FLOW:
- case UDP_V4_FLOW:
- case SCTP_V4_FLOW:
- fsp->h_u.tcp_ip4_spec.ip4src = flow_rule->key.src_ip[0];
- fsp->h_u.tcp_ip4_spec.ip4dst = flow_rule->key.dst_ip[0];
- fsp->h_u.tcp_ip4_spec.psrc = flow_rule->key.src_port;
- fsp->h_u.tcp_ip4_spec.pdst = flow_rule->key.dst_port;
- fsp->h_u.tcp_ip4_spec.tos = flow_rule->key.tos;
- fsp->m_u.tcp_ip4_spec.ip4src = flow_rule->mask.src_ip[0];
- fsp->m_u.tcp_ip4_spec.ip4dst = flow_rule->mask.dst_ip[0];
- fsp->m_u.tcp_ip4_spec.psrc = flow_rule->mask.src_port;
- fsp->m_u.tcp_ip4_spec.pdst = flow_rule->mask.dst_port;
- fsp->m_u.tcp_ip4_spec.tos = flow_rule->mask.tos;
- break;
- case AH_V4_FLOW:
- case ESP_V4_FLOW:
- fsp->h_u.ah_ip4_spec.ip4src = flow_rule->key.src_ip[0];
- fsp->h_u.ah_ip4_spec.ip4dst = flow_rule->key.dst_ip[0];
- fsp->h_u.ah_ip4_spec.spi = flow_rule->key.spi;
- fsp->h_u.ah_ip4_spec.tos = flow_rule->key.tos;
- fsp->m_u.ah_ip4_spec.ip4src = flow_rule->mask.src_ip[0];
- fsp->m_u.ah_ip4_spec.ip4dst = flow_rule->mask.dst_ip[0];
- fsp->m_u.ah_ip4_spec.spi = flow_rule->mask.spi;
- fsp->m_u.ah_ip4_spec.tos = flow_rule->mask.tos;
- break;
- case TCP_V6_FLOW:
- case UDP_V6_FLOW:
- case SCTP_V6_FLOW:
- memcpy(fsp->h_u.tcp_ip6_spec.ip6src, &flow_rule->key.src_ip,
- sizeof(struct in6_addr));
- memcpy(fsp->h_u.tcp_ip6_spec.ip6dst, &flow_rule->key.dst_ip,
- sizeof(struct in6_addr));
- fsp->h_u.tcp_ip6_spec.psrc = flow_rule->key.src_port;
- fsp->h_u.tcp_ip6_spec.pdst = flow_rule->key.dst_port;
- fsp->h_u.tcp_ip6_spec.tclass = flow_rule->key.tclass;
- memcpy(fsp->m_u.tcp_ip6_spec.ip6src, &flow_rule->mask.src_ip,
- sizeof(struct in6_addr));
- memcpy(fsp->m_u.tcp_ip6_spec.ip6dst, &flow_rule->mask.dst_ip,
- sizeof(struct in6_addr));
- fsp->m_u.tcp_ip6_spec.psrc = flow_rule->mask.src_port;
- fsp->m_u.tcp_ip6_spec.pdst = flow_rule->mask.dst_port;
- fsp->m_u.tcp_ip6_spec.tclass = flow_rule->mask.tclass;
- break;
- case AH_V6_FLOW:
- case ESP_V6_FLOW:
- memcpy(fsp->h_u.ah_ip6_spec.ip6src, &flow_rule->key.src_ip,
- sizeof(struct in6_addr));
- memcpy(fsp->h_u.ah_ip6_spec.ip6dst, &flow_rule->key.dst_ip,
- sizeof(struct in6_addr));
- fsp->h_u.ah_ip6_spec.spi = flow_rule->key.spi;
- fsp->h_u.ah_ip6_spec.tclass = flow_rule->key.tclass;
- memcpy(fsp->m_u.ah_ip6_spec.ip6src, &flow_rule->mask.src_ip,
- sizeof(struct in6_addr));
- memcpy(fsp->m_u.ah_ip6_spec.ip6dst, &flow_rule->mask.dst_ip,
- sizeof(struct in6_addr));
- fsp->m_u.ah_ip6_spec.spi = flow_rule->mask.spi;
- fsp->m_u.ah_ip6_spec.tclass = flow_rule->mask.tclass;
- break;
- default:
- return -EINVAL;
- }
- fsp->ring_cookie = be16_to_cpu(flow_rule->action);
- return 0;
- }
- static int gve_generate_flow_rule(struct gve_priv *priv, struct ethtool_rx_flow_spec *fsp,
- struct gve_adminq_flow_rule *rule)
- {
- static const u16 flow_type_lut[] = {
- [TCP_V4_FLOW] = GVE_FLOW_TYPE_TCPV4,
- [UDP_V4_FLOW] = GVE_FLOW_TYPE_UDPV4,
- [SCTP_V4_FLOW] = GVE_FLOW_TYPE_SCTPV4,
- [AH_V4_FLOW] = GVE_FLOW_TYPE_AHV4,
- [ESP_V4_FLOW] = GVE_FLOW_TYPE_ESPV4,
- [TCP_V6_FLOW] = GVE_FLOW_TYPE_TCPV6,
- [UDP_V6_FLOW] = GVE_FLOW_TYPE_UDPV6,
- [SCTP_V6_FLOW] = GVE_FLOW_TYPE_SCTPV6,
- [AH_V6_FLOW] = GVE_FLOW_TYPE_AHV6,
- [ESP_V6_FLOW] = GVE_FLOW_TYPE_ESPV6,
- };
- u32 flow_type;
- if (fsp->ring_cookie == RX_CLS_FLOW_DISC)
- return -EOPNOTSUPP;
- if (fsp->ring_cookie >= priv->rx_cfg.num_queues)
- return -EINVAL;
- rule->action = cpu_to_be16(fsp->ring_cookie);
- flow_type = fsp->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS);
- if (!flow_type || flow_type >= ARRAY_SIZE(flow_type_lut))
- return -EINVAL;
- rule->flow_type = cpu_to_be16(flow_type_lut[flow_type]);
- switch (flow_type) {
- case TCP_V4_FLOW:
- case UDP_V4_FLOW:
- case SCTP_V4_FLOW:
- rule->key.src_ip[0] = fsp->h_u.tcp_ip4_spec.ip4src;
- rule->key.dst_ip[0] = fsp->h_u.tcp_ip4_spec.ip4dst;
- rule->key.src_port = fsp->h_u.tcp_ip4_spec.psrc;
- rule->key.dst_port = fsp->h_u.tcp_ip4_spec.pdst;
- rule->mask.src_ip[0] = fsp->m_u.tcp_ip4_spec.ip4src;
- rule->mask.dst_ip[0] = fsp->m_u.tcp_ip4_spec.ip4dst;
- rule->mask.src_port = fsp->m_u.tcp_ip4_spec.psrc;
- rule->mask.dst_port = fsp->m_u.tcp_ip4_spec.pdst;
- break;
- case AH_V4_FLOW:
- case ESP_V4_FLOW:
- rule->key.src_ip[0] = fsp->h_u.tcp_ip4_spec.ip4src;
- rule->key.dst_ip[0] = fsp->h_u.tcp_ip4_spec.ip4dst;
- rule->key.spi = fsp->h_u.ah_ip4_spec.spi;
- rule->mask.src_ip[0] = fsp->m_u.tcp_ip4_spec.ip4src;
- rule->mask.dst_ip[0] = fsp->m_u.tcp_ip4_spec.ip4dst;
- rule->mask.spi = fsp->m_u.ah_ip4_spec.spi;
- break;
- case TCP_V6_FLOW:
- case UDP_V6_FLOW:
- case SCTP_V6_FLOW:
- memcpy(&rule->key.src_ip, fsp->h_u.tcp_ip6_spec.ip6src,
- sizeof(struct in6_addr));
- memcpy(&rule->key.dst_ip, fsp->h_u.tcp_ip6_spec.ip6dst,
- sizeof(struct in6_addr));
- rule->key.src_port = fsp->h_u.tcp_ip6_spec.psrc;
- rule->key.dst_port = fsp->h_u.tcp_ip6_spec.pdst;
- memcpy(&rule->mask.src_ip, fsp->m_u.tcp_ip6_spec.ip6src,
- sizeof(struct in6_addr));
- memcpy(&rule->mask.dst_ip, fsp->m_u.tcp_ip6_spec.ip6dst,
- sizeof(struct in6_addr));
- rule->mask.src_port = fsp->m_u.tcp_ip6_spec.psrc;
- rule->mask.dst_port = fsp->m_u.tcp_ip6_spec.pdst;
- break;
- case AH_V6_FLOW:
- case ESP_V6_FLOW:
- memcpy(&rule->key.src_ip, fsp->h_u.usr_ip6_spec.ip6src,
- sizeof(struct in6_addr));
- memcpy(&rule->key.dst_ip, fsp->h_u.usr_ip6_spec.ip6dst,
- sizeof(struct in6_addr));
- rule->key.spi = fsp->h_u.ah_ip6_spec.spi;
- memcpy(&rule->mask.src_ip, fsp->m_u.usr_ip6_spec.ip6src,
- sizeof(struct in6_addr));
- memcpy(&rule->mask.dst_ip, fsp->m_u.usr_ip6_spec.ip6dst,
- sizeof(struct in6_addr));
- rule->key.spi = fsp->h_u.ah_ip6_spec.spi;
- break;
- default:
- /* not doing un-parsed flow types */
- return -EINVAL;
- }
- return 0;
- }
- int gve_get_flow_rule_entry(struct gve_priv *priv, struct ethtool_rxnfc *cmd)
- {
- struct gve_adminq_queried_flow_rule *rules_cache = priv->flow_rules_cache.rules_cache;
- struct ethtool_rx_flow_spec *fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
- u32 *cache_num = &priv->flow_rules_cache.rules_cache_num;
- struct gve_adminq_queried_flow_rule *rule = NULL;
- int err = 0;
- u32 i;
- if (!priv->max_flow_rules)
- return -EOPNOTSUPP;
- if (!priv->flow_rules_cache.rules_cache_synced ||
- fsp->location < be32_to_cpu(rules_cache[0].location) ||
- fsp->location > be32_to_cpu(rules_cache[*cache_num - 1].location)) {
- err = gve_adminq_query_flow_rules(priv, GVE_FLOW_RULE_QUERY_RULES, fsp->location);
- if (err)
- return err;
- priv->flow_rules_cache.rules_cache_synced = true;
- }
- for (i = 0; i < *cache_num; i++) {
- if (fsp->location == be32_to_cpu(rules_cache[i].location)) {
- rule = &rules_cache[i];
- break;
- }
- }
- if (!rule)
- return -EINVAL;
- err = gve_fill_ethtool_flow_spec(fsp, rule);
- return err;
- }
- int gve_get_flow_rule_ids(struct gve_priv *priv, struct ethtool_rxnfc *cmd, u32 *rule_locs)
- {
- __be32 *rule_ids_cache = priv->flow_rules_cache.rule_ids_cache;
- u32 *cache_num = &priv->flow_rules_cache.rule_ids_cache_num;
- u32 starting_rule_id = 0;
- u32 i = 0, j = 0;
- int err = 0;
- if (!priv->max_flow_rules)
- return -EOPNOTSUPP;
- do {
- err = gve_adminq_query_flow_rules(priv, GVE_FLOW_RULE_QUERY_IDS,
- starting_rule_id);
- if (err)
- return err;
- for (i = 0; i < *cache_num; i++) {
- if (j >= cmd->rule_cnt)
- return -EMSGSIZE;
- rule_locs[j++] = be32_to_cpu(rule_ids_cache[i]);
- starting_rule_id = be32_to_cpu(rule_ids_cache[i]) + 1;
- }
- } while (*cache_num != 0);
- cmd->data = priv->max_flow_rules;
- return err;
- }
- int gve_add_flow_rule(struct gve_priv *priv, struct ethtool_rxnfc *cmd)
- {
- struct ethtool_rx_flow_spec *fsp = &cmd->fs;
- struct gve_adminq_flow_rule *rule = NULL;
- int err;
- if (!priv->max_flow_rules)
- return -EOPNOTSUPP;
- rule = kvzalloc(sizeof(*rule), GFP_KERNEL);
- if (!rule)
- return -ENOMEM;
- err = gve_generate_flow_rule(priv, fsp, rule);
- if (err)
- goto out;
- err = gve_adminq_add_flow_rule(priv, rule, fsp->location);
- out:
- kvfree(rule);
- if (err)
- dev_err(&priv->pdev->dev, "Failed to add the flow rule: %u", fsp->location);
- return err;
- }
- int gve_del_flow_rule(struct gve_priv *priv, struct ethtool_rxnfc *cmd)
- {
- struct ethtool_rx_flow_spec *fsp = (struct ethtool_rx_flow_spec *)&cmd->fs;
- if (!priv->max_flow_rules)
- return -EOPNOTSUPP;
- return gve_adminq_del_flow_rule(priv, fsp->location);
- }
|