aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPablo Neira Ayuso <pablo@netfilter.org>2019-02-02 06:50:51 -0500
committerDavid S. Miller <davem@davemloft.net>2019-02-06 13:38:26 -0500
commiteca4205f9ec3bea2d5aad0493c19f5d2675a20fc (patch)
tree3068cfcdfbd7cf6f2e95d0e69d8fba8c10ee15e3
parent8bec2833fb28d7c8fcc25127190abbe3eb0ca260 (diff)
ethtool: add ethtool_rx_flow_spec to flow_rule structure translator
This patch adds a function to translate the ethtool_rx_flow_spec structure to the flow_rule representation. This allows us to reuse code from the driver side given that both flower and ethtool_rx_flow interfaces use the same representation. This patch also includes support for the flow type flags FLOW_EXT, FLOW_MAC_EXT and FLOW_RSS. The ethtool_rx_flow_spec_input wrapper structure is used to convey the rss_context field, that is away from the ethtool_rx_flow_spec structure, and the ethtool_rx_flow_spec structure. Signed-off-by: Pablo Neira Ayuso <pablo@netfilter.org> Acked-by: Jiri Pirko <jiri@mellanox.com> Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r--include/linux/ethtool.h15
-rw-r--r--net/core/ethtool.c241
2 files changed, 256 insertions, 0 deletions
diff --git a/include/linux/ethtool.h b/include/linux/ethtool.h
index afd9596ce636..19a8de5326fb 100644
--- a/include/linux/ethtool.h
+++ b/include/linux/ethtool.h
@@ -400,4 +400,19 @@ struct ethtool_ops {
400 void (*get_ethtool_phy_stats)(struct net_device *, 400 void (*get_ethtool_phy_stats)(struct net_device *,
401 struct ethtool_stats *, u64 *); 401 struct ethtool_stats *, u64 *);
402}; 402};
403
404struct ethtool_rx_flow_rule {
405 struct flow_rule *rule;
406 unsigned long priv[0];
407};
408
409struct ethtool_rx_flow_spec_input {
410 const struct ethtool_rx_flow_spec *fs;
411 u32 rss_ctx;
412};
413
414struct ethtool_rx_flow_rule *
415ethtool_rx_flow_rule_create(const struct ethtool_rx_flow_spec_input *input);
416void ethtool_rx_flow_rule_destroy(struct ethtool_rx_flow_rule *rule);
417
403#endif /* _LINUX_ETHTOOL_H */ 418#endif /* _LINUX_ETHTOOL_H */
diff --git a/net/core/ethtool.c b/net/core/ethtool.c
index 45c0a6e3d6ad..0fbf39239b29 100644
--- a/net/core/ethtool.c
+++ b/net/core/ethtool.c
@@ -29,6 +29,7 @@
29#include <linux/net.h> 29#include <linux/net.h>
30#include <net/devlink.h> 30#include <net/devlink.h>
31#include <net/xdp_sock.h> 31#include <net/xdp_sock.h>
32#include <net/flow_offload.h>
32 33
33/* 34/*
34 * Some useful ethtool_ops methods that're device independent. 35 * Some useful ethtool_ops methods that're device independent.
@@ -2820,3 +2821,243 @@ int dev_ethtool(struct net *net, struct ifreq *ifr)
2820 2821
2821 return rc; 2822 return rc;
2822} 2823}
2824
2825struct ethtool_rx_flow_key {
2826 struct flow_dissector_key_basic basic;
2827 union {
2828 struct flow_dissector_key_ipv4_addrs ipv4;
2829 struct flow_dissector_key_ipv6_addrs ipv6;
2830 };
2831 struct flow_dissector_key_ports tp;
2832 struct flow_dissector_key_ip ip;
2833 struct flow_dissector_key_vlan vlan;
2834 struct flow_dissector_key_eth_addrs eth_addrs;
2835} __aligned(BITS_PER_LONG / 8); /* Ensure that we can do comparisons as longs. */
2836
2837struct ethtool_rx_flow_match {
2838 struct flow_dissector dissector;
2839 struct ethtool_rx_flow_key key;
2840 struct ethtool_rx_flow_key mask;
2841};
2842
2843struct ethtool_rx_flow_rule *
2844ethtool_rx_flow_rule_create(const struct ethtool_rx_flow_spec_input *input)
2845{
2846 const struct ethtool_rx_flow_spec *fs = input->fs;
2847 static struct in6_addr zero_addr = {};
2848 struct ethtool_rx_flow_match *match;
2849 struct ethtool_rx_flow_rule *flow;
2850 struct flow_action_entry *act;
2851
2852 flow = kzalloc(sizeof(struct ethtool_rx_flow_rule) +
2853 sizeof(struct ethtool_rx_flow_match), GFP_KERNEL);
2854 if (!flow)
2855 return ERR_PTR(-ENOMEM);
2856
2857 /* ethtool_rx supports only one single action per rule. */
2858 flow->rule = flow_rule_alloc(1);
2859 if (!flow->rule) {
2860 kfree(flow);
2861 return ERR_PTR(-ENOMEM);
2862 }
2863
2864 match = (struct ethtool_rx_flow_match *)flow->priv;
2865 flow->rule->match.dissector = &match->dissector;
2866 flow->rule->match.mask = &match->mask;
2867 flow->rule->match.key = &match->key;
2868
2869 match->mask.basic.n_proto = htons(0xffff);
2870
2871 switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS)) {
2872 case TCP_V4_FLOW:
2873 case UDP_V4_FLOW: {
2874 const struct ethtool_tcpip4_spec *v4_spec, *v4_m_spec;
2875
2876 match->key.basic.n_proto = htons(ETH_P_IP);
2877
2878 v4_spec = &fs->h_u.tcp_ip4_spec;
2879 v4_m_spec = &fs->m_u.tcp_ip4_spec;
2880
2881 if (v4_m_spec->ip4src) {
2882 match->key.ipv4.src = v4_spec->ip4src;
2883 match->mask.ipv4.src = v4_m_spec->ip4src;
2884 }
2885 if (v4_m_spec->ip4dst) {
2886 match->key.ipv4.dst = v4_spec->ip4dst;
2887 match->mask.ipv4.dst = v4_m_spec->ip4dst;
2888 }
2889 if (v4_m_spec->ip4src ||
2890 v4_m_spec->ip4dst) {
2891 match->dissector.used_keys |=
2892 BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS);
2893 match->dissector.offset[FLOW_DISSECTOR_KEY_IPV4_ADDRS] =
2894 offsetof(struct ethtool_rx_flow_key, ipv4);
2895 }
2896 if (v4_m_spec->psrc) {
2897 match->key.tp.src = v4_spec->psrc;
2898 match->mask.tp.src = v4_m_spec->psrc;
2899 }
2900 if (v4_m_spec->pdst) {
2901 match->key.tp.dst = v4_spec->pdst;
2902 match->mask.tp.dst = v4_m_spec->pdst;
2903 }
2904 if (v4_m_spec->psrc ||
2905 v4_m_spec->pdst) {
2906 match->dissector.used_keys |=
2907 BIT(FLOW_DISSECTOR_KEY_PORTS);
2908 match->dissector.offset[FLOW_DISSECTOR_KEY_PORTS] =
2909 offsetof(struct ethtool_rx_flow_key, tp);
2910 }
2911 if (v4_m_spec->tos) {
2912 match->key.ip.tos = v4_spec->tos;
2913 match->mask.ip.tos = v4_m_spec->tos;
2914 match->dissector.used_keys |=
2915 BIT(FLOW_DISSECTOR_KEY_IP);
2916 match->dissector.offset[FLOW_DISSECTOR_KEY_IP] =
2917 offsetof(struct ethtool_rx_flow_key, ip);
2918 }
2919 }
2920 break;
2921 case TCP_V6_FLOW:
2922 case UDP_V6_FLOW: {
2923 const struct ethtool_tcpip6_spec *v6_spec, *v6_m_spec;
2924
2925 match->key.basic.n_proto = htons(ETH_P_IPV6);
2926
2927 v6_spec = &fs->h_u.tcp_ip6_spec;
2928 v6_m_spec = &fs->m_u.tcp_ip6_spec;
2929 if (memcmp(v6_m_spec->ip6src, &zero_addr, sizeof(zero_addr))) {
2930 memcpy(&match->key.ipv6.src, v6_spec->ip6src,
2931 sizeof(match->key.ipv6.src));
2932 memcpy(&match->mask.ipv6.src, v6_m_spec->ip6src,
2933 sizeof(match->mask.ipv6.src));
2934 }
2935 if (memcmp(v6_m_spec->ip6dst, &zero_addr, sizeof(zero_addr))) {
2936 memcpy(&match->key.ipv6.dst, v6_spec->ip6dst,
2937 sizeof(match->key.ipv6.dst));
2938 memcpy(&match->mask.ipv6.dst, v6_m_spec->ip6dst,
2939 sizeof(match->mask.ipv6.dst));
2940 }
2941 if (memcmp(v6_m_spec->ip6src, &zero_addr, sizeof(zero_addr)) ||
2942 memcmp(v6_m_spec->ip6src, &zero_addr, sizeof(zero_addr))) {
2943 match->dissector.used_keys |=
2944 BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS);
2945 match->dissector.offset[FLOW_DISSECTOR_KEY_IPV6_ADDRS] =
2946 offsetof(struct ethtool_rx_flow_key, ipv6);
2947 }
2948 if (v6_m_spec->psrc) {
2949 match->key.tp.src = v6_spec->psrc;
2950 match->mask.tp.src = v6_m_spec->psrc;
2951 }
2952 if (v6_m_spec->pdst) {
2953 match->key.tp.dst = v6_spec->pdst;
2954 match->mask.tp.dst = v6_m_spec->pdst;
2955 }
2956 if (v6_m_spec->psrc ||
2957 v6_m_spec->pdst) {
2958 match->dissector.used_keys |=
2959 BIT(FLOW_DISSECTOR_KEY_PORTS);
2960 match->dissector.offset[FLOW_DISSECTOR_KEY_PORTS] =
2961 offsetof(struct ethtool_rx_flow_key, tp);
2962 }
2963 if (v6_m_spec->tclass) {
2964 match->key.ip.tos = v6_spec->tclass;
2965 match->mask.ip.tos = v6_m_spec->tclass;
2966 match->dissector.used_keys |=
2967 BIT(FLOW_DISSECTOR_KEY_IP);
2968 match->dissector.offset[FLOW_DISSECTOR_KEY_IP] =
2969 offsetof(struct ethtool_rx_flow_key, ip);
2970 }
2971 }
2972 break;
2973 default:
2974 ethtool_rx_flow_rule_destroy(flow);
2975 return ERR_PTR(-EINVAL);
2976 }
2977
2978 switch (fs->flow_type & ~(FLOW_EXT | FLOW_MAC_EXT | FLOW_RSS)) {
2979 case TCP_V4_FLOW:
2980 case TCP_V6_FLOW:
2981 match->key.basic.ip_proto = IPPROTO_TCP;
2982 break;
2983 case UDP_V4_FLOW:
2984 case UDP_V6_FLOW:
2985 match->key.basic.ip_proto = IPPROTO_UDP;
2986 break;
2987 }
2988 match->mask.basic.ip_proto = 0xff;
2989
2990