diff options
| author | Pablo Neira Ayuso <pablo@netfilter.org> | 2019-02-02 06:50:51 -0500 |
|---|---|---|
| committer | David S. Miller <davem@davemloft.net> | 2019-02-06 13:38:26 -0500 |
| commit | eca4205f9ec3bea2d5aad0493c19f5d2675a20fc (patch) | |
| tree | 3068cfcdfbd7cf6f2e95d0e69d8fba8c10ee15e3 | |
| parent | 8bec2833fb28d7c8fcc25127190abbe3eb0ca260 (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.h | 15 | ||||
| -rw-r--r-- | net/core/ethtool.c | 241 |
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 | |||
| 404 | struct ethtool_rx_flow_rule { | ||
| 405 | struct flow_rule *rule; | ||
| 406 | unsigned long priv[0]; | ||
| 407 | }; | ||
| 408 | |||
| 409 | struct ethtool_rx_flow_spec_input { | ||
| 410 | const struct ethtool_rx_flow_spec *fs; | ||
| 411 | u32 rss_ctx; | ||
| 412 | }; | ||
| 413 | |||
| 414 | struct ethtool_rx_flow_rule * | ||
| 415 | ethtool_rx_flow_rule_create(const struct ethtool_rx_flow_spec_input *input); | ||
| 416 | void 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 | |||
| 2825 | struct 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 | |||
| 2837 | struct 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 | |||
| 2843 | struct ethtool_rx_flow_rule * | ||
| 2844 | ethtool_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 | |||
