|
29 | 29 | #include <linux/net.h>
|
30 | 30 | #include <net/devlink.h>
|
31 | 31 | #include <net/xdp_sock.h>
|
| 32 | +#include <net/flow_offload.h> |
32 | 33 |
|
33 | 34 | /*
|
34 | 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 | 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 | + match->dissector.used_keys |= BIT(FLOW_DISSECTOR_KEY_BASIC); |
| 2991 | + match->dissector.offset[FLOW_DISSECTOR_KEY_BASIC] = |
| 2992 | + offsetof(struct ethtool_rx_flow_key, basic); |
| 2993 | + |
| 2994 | + if (fs->flow_type & FLOW_EXT) { |
| 2995 | + const struct ethtool_flow_ext *ext_h_spec = &fs->h_ext; |
| 2996 | + const struct ethtool_flow_ext *ext_m_spec = &fs->m_ext; |
| 2997 | + |
| 2998 | + if (ext_m_spec->vlan_etype && |
| 2999 | + ext_m_spec->vlan_tci) { |
| 3000 | + match->key.vlan.vlan_tpid = ext_h_spec->vlan_etype; |
| 3001 | + match->mask.vlan.vlan_tpid = ext_m_spec->vlan_etype; |
| 3002 | + |
| 3003 | + match->key.vlan.vlan_id = |
| 3004 | + ntohs(ext_h_spec->vlan_tci) & 0x0fff; |
| 3005 | + match->mask.vlan.vlan_id = |
| 3006 | + ntohs(ext_m_spec->vlan_tci) & 0x0fff; |
| 3007 | + |
| 3008 | + match->key.vlan.vlan_priority = |
| 3009 | + (ntohs(ext_h_spec->vlan_tci) & 0xe000) >> 13; |
| 3010 | + match->mask.vlan.vlan_priority = |
| 3011 | + (ntohs(ext_m_spec->vlan_tci) & 0xe000) >> 13; |
| 3012 | + |
| 3013 | + match->dissector.used_keys |= |
| 3014 | + BIT(FLOW_DISSECTOR_KEY_VLAN); |
| 3015 | + match->dissector.offset[FLOW_DISSECTOR_KEY_VLAN] = |
| 3016 | + offsetof(struct ethtool_rx_flow_key, vlan); |
| 3017 | + } |
| 3018 | + } |
| 3019 | + if (fs->flow_type & FLOW_MAC_EXT) { |
| 3020 | + const struct ethtool_flow_ext *ext_h_spec = &fs->h_ext; |
| 3021 | + const struct ethtool_flow_ext *ext_m_spec = &fs->m_ext; |
| 3022 | + |
| 3023 | + if (ext_m_spec->h_dest) { |
| 3024 | + memcpy(match->key.eth_addrs.dst, ext_h_spec->h_dest, |
| 3025 | + ETH_ALEN); |
| 3026 | + memcpy(match->mask.eth_addrs.dst, ext_m_spec->h_dest, |
| 3027 | + ETH_ALEN); |
| 3028 | + |
| 3029 | + match->dissector.used_keys |= |
| 3030 | + BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS); |
| 3031 | + match->dissector.offset[FLOW_DISSECTOR_KEY_ETH_ADDRS] = |
| 3032 | + offsetof(struct ethtool_rx_flow_key, eth_addrs); |
| 3033 | + } |
| 3034 | + } |
| 3035 | + |
| 3036 | + act = &flow->rule->action.entries[0]; |
| 3037 | + switch (fs->ring_cookie) { |
| 3038 | + case RX_CLS_FLOW_DISC: |
| 3039 | + act->id = FLOW_ACTION_DROP; |
| 3040 | + break; |
| 3041 | + case RX_CLS_FLOW_WAKE: |
| 3042 | + act->id = FLOW_ACTION_WAKE; |
| 3043 | + break; |
| 3044 | + default: |
| 3045 | + act->id = FLOW_ACTION_QUEUE; |
| 3046 | + if (fs->flow_type & FLOW_RSS) |
| 3047 | + act->queue.ctx = input->rss_ctx; |
| 3048 | + |
| 3049 | + act->queue.vf = ethtool_get_flow_spec_ring_vf(fs->ring_cookie); |
| 3050 | + act->queue.index = ethtool_get_flow_spec_ring(fs->ring_cookie); |
| 3051 | + break; |
| 3052 | + } |
| 3053 | + |
| 3054 | + return flow; |
| 3055 | +} |
| 3056 | +EXPORT_SYMBOL(ethtool_rx_flow_rule_create); |
| 3057 | + |
| 3058 | +void ethtool_rx_flow_rule_destroy(struct ethtool_rx_flow_rule *flow) |
| 3059 | +{ |
| 3060 | + kfree(flow->rule); |
| 3061 | + kfree(flow); |
| 3062 | +} |
| 3063 | +EXPORT_SYMBOL(ethtool_rx_flow_rule_destroy); |
0 commit comments