1// SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB
2/* Copyright (c) 2018 Mellanox Technologies. */
3
4#include <net/ip_tunnels.h>
5#include <net/vxlan.h>
6#include "lib/vxlan.h"
7#include "en/tc_tun.h"
8
9static bool mlx5e_tc_tun_can_offload_vxlan(struct mlx5e_priv *priv)
10{
11 return !!MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap);
12}
13
14static int mlx5e_tc_tun_calc_hlen_vxlan(struct mlx5e_encap_entry *e)
15{
16 return VXLAN_HLEN;
17}
18
19static int mlx5e_tc_tun_check_udp_dport_vxlan(struct mlx5e_priv *priv,
20 struct flow_cls_offload *f)
21{
22 struct flow_rule *rule = flow_cls_offload_flow_rule(flow_cmd: f);
23 struct netlink_ext_ack *extack = f->common.extack;
24 struct flow_match_ports enc_ports;
25
26 if (!flow_rule_match_key(rule, key: FLOW_DISSECTOR_KEY_ENC_PORTS))
27 return -EOPNOTSUPP;
28
29 flow_rule_match_enc_ports(rule, out: &enc_ports);
30
31 /* check the UDP destination port validity */
32
33 if (!mlx5_vxlan_lookup_port(vxlan: priv->mdev->vxlan,
34 be16_to_cpu(enc_ports.key->dst))) {
35 NL_SET_ERR_MSG_MOD(extack,
36 "Matched UDP dst port is not registered as a VXLAN port");
37 netdev_warn(dev: priv->netdev,
38 format: "UDP port %d is not registered as a VXLAN port\n",
39 be16_to_cpu(enc_ports.key->dst));
40 return -EOPNOTSUPP;
41 }
42
43 return 0;
44}
45
46static int mlx5e_tc_tun_parse_udp_ports_vxlan(struct mlx5e_priv *priv,
47 struct mlx5_flow_spec *spec,
48 struct flow_cls_offload *f,
49 void *headers_c,
50 void *headers_v)
51{
52 int err = 0;
53
54 err = mlx5e_tc_tun_parse_udp_ports(priv, spec, f, headers_c, headers_v);
55 if (err)
56 return err;
57
58 return mlx5e_tc_tun_check_udp_dport_vxlan(priv, f);
59}
60
61static int mlx5e_tc_tun_init_encap_attr_vxlan(struct net_device *tunnel_dev,
62 struct mlx5e_priv *priv,
63 struct mlx5e_encap_entry *e,
64 struct netlink_ext_ack *extack)
65{
66 int dst_port = be16_to_cpu(e->tun_info->key.tp_dst);
67
68 e->tunnel = &vxlan_tunnel;
69
70 if (!mlx5_vxlan_lookup_port(vxlan: priv->mdev->vxlan, port: dst_port)) {
71 NL_SET_ERR_MSG_MOD(extack,
72 "vxlan udp dport was not registered with the HW");
73 netdev_warn(dev: priv->netdev,
74 format: "%d isn't an offloaded vxlan udp dport\n",
75 dst_port);
76 return -EOPNOTSUPP;
77 }
78
79 e->reformat_type = MLX5_REFORMAT_TYPE_L2_TO_VXLAN;
80 return 0;
81}
82
83static int mlx5e_gen_ip_tunnel_header_vxlan(char buf[],
84 __u8 *ip_proto,
85 struct mlx5e_encap_entry *e)
86{
87 const struct ip_tunnel_key *tun_key = &e->tun_info->key;
88 __be32 tun_id = tunnel_id_to_key32(tun_id: tun_key->tun_id);
89 struct udphdr *udp = (struct udphdr *)(buf);
90 const struct vxlan_metadata *md;
91 struct vxlanhdr *vxh;
92
93 if ((tun_key->tun_flags & TUNNEL_VXLAN_OPT) &&
94 e->tun_info->options_len != sizeof(*md))
95 return -EOPNOTSUPP;
96 vxh = (struct vxlanhdr *)((char *)udp + sizeof(struct udphdr));
97 *ip_proto = IPPROTO_UDP;
98
99 udp->dest = tun_key->tp_dst;
100 vxh->vx_flags = VXLAN_HF_VNI;
101 vxh->vx_vni = vxlan_vni_field(vni: tun_id);
102 if (tun_key->tun_flags & TUNNEL_VXLAN_OPT) {
103 md = ip_tunnel_info_opts(e->tun_info);
104 vxlan_build_gbp_hdr(vxh, md);
105 }
106
107 return 0;
108}
109
110static int mlx5e_tc_tun_parse_vxlan_gbp_option(struct mlx5e_priv *priv,
111 struct mlx5_flow_spec *spec,
112 struct flow_cls_offload *f)
113{
114 struct flow_rule *rule = flow_cls_offload_flow_rule(flow_cmd: f);
115 struct netlink_ext_ack *extack = f->common.extack;
116 struct flow_match_enc_opts enc_opts;
117 void *misc5_c, *misc5_v;
118 u32 *gbp, *gbp_mask;
119
120 flow_rule_match_enc_opts(rule, out: &enc_opts);
121
122 if (memchr_inv(p: &enc_opts.mask->data, c: 0, size: sizeof(enc_opts.mask->data)) &&
123 !MLX5_CAP_ESW_FT_FIELD_SUPPORT_2(priv->mdev, tunnel_header_0_1)) {
124 NL_SET_ERR_MSG_MOD(extack, "Matching on VxLAN GBP is not supported");
125 return -EOPNOTSUPP;
126 }
127
128 if (enc_opts.key->dst_opt_type != TUNNEL_VXLAN_OPT) {
129 NL_SET_ERR_MSG_MOD(extack, "Wrong VxLAN option type: not GBP");
130 return -EOPNOTSUPP;
131 }
132
133 if (enc_opts.key->len != sizeof(*gbp) ||
134 enc_opts.mask->len != sizeof(*gbp_mask)) {
135 NL_SET_ERR_MSG_MOD(extack, "VxLAN GBP option/mask len is not 32 bits");
136 return -EINVAL;
137 }
138
139 gbp = (u32 *)&enc_opts.key->data[0];
140 gbp_mask = (u32 *)&enc_opts.mask->data[0];
141
142 if (*gbp_mask & ~VXLAN_GBP_MASK) {
143 NL_SET_ERR_MSG_FMT_MOD(extack, "Wrong VxLAN GBP mask(0x%08X)\n", *gbp_mask);
144 return -EINVAL;
145 }
146
147 misc5_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters_5);
148 misc5_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters_5);
149 MLX5_SET(fte_match_set_misc5, misc5_c, tunnel_header_0, *gbp_mask);
150 MLX5_SET(fte_match_set_misc5, misc5_v, tunnel_header_0, *gbp);
151
152 spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_5;
153
154 return 0;
155}
156
157static int mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv *priv,
158 struct mlx5_flow_spec *spec,
159 struct flow_cls_offload *f,
160 void *headers_c,
161 void *headers_v)
162{
163 struct flow_rule *rule = flow_cls_offload_flow_rule(flow_cmd: f);
164 struct netlink_ext_ack *extack = f->common.extack;
165 struct flow_match_enc_keyid enc_keyid;
166 void *misc_c, *misc_v;
167
168 misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters);
169 misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters);
170
171 if (!flow_rule_match_key(rule, key: FLOW_DISSECTOR_KEY_ENC_KEYID))
172 return 0;
173
174 flow_rule_match_enc_keyid(rule, out: &enc_keyid);
175
176 if (!enc_keyid.mask->keyid)
177 return 0;
178
179 if (flow_rule_match_key(rule, key: FLOW_DISSECTOR_KEY_ENC_OPTS)) {
180 int err;
181
182 err = mlx5e_tc_tun_parse_vxlan_gbp_option(priv, spec, f);
183 if (err)
184 return err;
185 }
186
187 /* match on VNI is required */
188
189 if (!MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev,
190 ft_field_support.outer_vxlan_vni)) {
191 NL_SET_ERR_MSG_MOD(extack,
192 "Matching on VXLAN VNI is not supported");
193 netdev_warn(dev: priv->netdev,
194 format: "Matching on VXLAN VNI is not supported\n");
195 return -EOPNOTSUPP;
196 }
197
198 MLX5_SET(fte_match_set_misc, misc_c, vxlan_vni,
199 be32_to_cpu(enc_keyid.mask->keyid));
200 MLX5_SET(fte_match_set_misc, misc_v, vxlan_vni,
201 be32_to_cpu(enc_keyid.key->keyid));
202
203 spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS;
204
205 return 0;
206}
207
208static bool mlx5e_tc_tun_encap_info_equal_vxlan(struct mlx5e_encap_key *a,
209 struct mlx5e_encap_key *b)
210{
211 return mlx5e_tc_tun_encap_info_equal_options(a, b, TUNNEL_VXLAN_OPT);
212}
213
214static int mlx5e_tc_tun_get_remote_ifindex(struct net_device *mirred_dev)
215{
216 const struct vxlan_dev *vxlan = netdev_priv(dev: mirred_dev);
217 const struct vxlan_rdst *dst = &vxlan->default_dst;
218
219 return dst->remote_ifindex;
220}
221
222struct mlx5e_tc_tunnel vxlan_tunnel = {
223 .tunnel_type = MLX5E_TC_TUNNEL_TYPE_VXLAN,
224 .match_level = MLX5_MATCH_L4,
225 .can_offload = mlx5e_tc_tun_can_offload_vxlan,
226 .calc_hlen = mlx5e_tc_tun_calc_hlen_vxlan,
227 .init_encap_attr = mlx5e_tc_tun_init_encap_attr_vxlan,
228 .generate_ip_tun_hdr = mlx5e_gen_ip_tunnel_header_vxlan,
229 .parse_udp_ports = mlx5e_tc_tun_parse_udp_ports_vxlan,
230 .parse_tunnel = mlx5e_tc_tun_parse_vxlan,
231 .encap_info_equal = mlx5e_tc_tun_encap_info_equal_vxlan,
232 .get_remote_ifindex = mlx5e_tc_tun_get_remote_ifindex,
233};
234

source code of linux/drivers/net/ethernet/mellanox/mlx5/core/en/tc_tun_vxlan.c