1 | // SPDX-License-Identifier: GPL-2.0 OR Linux-OpenIB |
2 | /* Copyright (c) 2018 Mellanox Technologies. */ |
3 | |
4 | #include <net/geneve.h> |
5 | #include "lib/geneve.h" |
6 | #include "en/tc_tun.h" |
7 | |
8 | #define MLX5E_GENEVE_VER 0 |
9 | |
10 | static bool mlx5e_tc_tun_can_offload_geneve(struct mlx5e_priv *priv) |
11 | { |
12 | return !!(MLX5_CAP_GEN(priv->mdev, flex_parser_protocols) & MLX5_FLEX_PROTO_GENEVE); |
13 | } |
14 | |
15 | static int mlx5e_tc_tun_calc_hlen_geneve(struct mlx5e_encap_entry *e) |
16 | { |
17 | return sizeof(struct udphdr) + |
18 | sizeof(struct genevehdr) + |
19 | e->tun_info->options_len; |
20 | } |
21 | |
22 | static int mlx5e_tc_tun_check_udp_dport_geneve(struct mlx5e_priv *priv, |
23 | struct flow_cls_offload *f) |
24 | { |
25 | struct flow_rule *rule = flow_cls_offload_flow_rule(flow_cmd: f); |
26 | struct netlink_ext_ack *extack = f->common.extack; |
27 | struct flow_match_ports enc_ports; |
28 | |
29 | if (!flow_rule_match_key(rule, key: FLOW_DISSECTOR_KEY_ENC_PORTS)) |
30 | return -EOPNOTSUPP; |
31 | |
32 | flow_rule_match_enc_ports(rule, out: &enc_ports); |
33 | |
34 | /* Currently we support only default GENEVE |
35 | * port, so udp dst port must match. |
36 | */ |
37 | if (be16_to_cpu(enc_ports.key->dst) != GENEVE_UDP_PORT) { |
38 | NL_SET_ERR_MSG_MOD(extack, |
39 | "Matched UDP dst port is not registered as a GENEVE port" ); |
40 | netdev_warn(dev: priv->netdev, |
41 | format: "UDP port %d is not registered as a GENEVE port\n" , |
42 | be16_to_cpu(enc_ports.key->dst)); |
43 | return -EOPNOTSUPP; |
44 | } |
45 | |
46 | return 0; |
47 | } |
48 | |
49 | static int mlx5e_tc_tun_parse_udp_ports_geneve(struct mlx5e_priv *priv, |
50 | struct mlx5_flow_spec *spec, |
51 | struct flow_cls_offload *f, |
52 | void *, |
53 | void *) |
54 | { |
55 | int err; |
56 | |
57 | err = mlx5e_tc_tun_parse_udp_ports(priv, spec, f, headers_c, headers_v); |
58 | if (err) |
59 | return err; |
60 | |
61 | return mlx5e_tc_tun_check_udp_dport_geneve(priv, f); |
62 | } |
63 | |
64 | static int mlx5e_tc_tun_init_encap_attr_geneve(struct net_device *tunnel_dev, |
65 | struct mlx5e_priv *priv, |
66 | struct mlx5e_encap_entry *e, |
67 | struct netlink_ext_ack *extack) |
68 | { |
69 | e->tunnel = &geneve_tunnel; |
70 | |
71 | /* Reformat type for GENEVE encap is similar to VXLAN: |
72 | * in both cases the HW adds in the same place a |
73 | * defined encapsulation header that the SW provides. |
74 | */ |
75 | e->reformat_type = MLX5_REFORMAT_TYPE_L2_TO_VXLAN; |
76 | return 0; |
77 | } |
78 | |
79 | static void mlx5e_tunnel_id_to_vni(__be64 tun_id, __u8 *vni) |
80 | { |
81 | #ifdef __BIG_ENDIAN |
82 | vni[0] = (__force __u8)(tun_id >> 16); |
83 | vni[1] = (__force __u8)(tun_id >> 8); |
84 | vni[2] = (__force __u8)tun_id; |
85 | #else |
86 | vni[0] = (__force __u8)((__force u64)tun_id >> 40); |
87 | vni[1] = (__force __u8)((__force u64)tun_id >> 48); |
88 | vni[2] = (__force __u8)((__force u64)tun_id >> 56); |
89 | #endif |
90 | } |
91 | |
92 | static int (char buf[], |
93 | __u8 *ip_proto, |
94 | struct mlx5e_encap_entry *e) |
95 | { |
96 | const struct ip_tunnel_info *tun_info = e->tun_info; |
97 | struct udphdr *udp = (struct udphdr *)(buf); |
98 | struct genevehdr *geneveh; |
99 | |
100 | geneveh = (struct genevehdr *)((char *)udp + sizeof(struct udphdr)); |
101 | |
102 | *ip_proto = IPPROTO_UDP; |
103 | |
104 | udp->dest = tun_info->key.tp_dst; |
105 | |
106 | memset(geneveh, 0, sizeof(*geneveh)); |
107 | geneveh->ver = MLX5E_GENEVE_VER; |
108 | geneveh->opt_len = tun_info->options_len / 4; |
109 | geneveh->oam = !!(tun_info->key.tun_flags & TUNNEL_OAM); |
110 | geneveh->critical = !!(tun_info->key.tun_flags & TUNNEL_CRIT_OPT); |
111 | mlx5e_tunnel_id_to_vni(tun_id: tun_info->key.tun_id, vni: geneveh->vni); |
112 | geneveh->proto_type = htons(ETH_P_TEB); |
113 | |
114 | if (tun_info->key.tun_flags & TUNNEL_GENEVE_OPT) { |
115 | if (!geneveh->opt_len) |
116 | return -EOPNOTSUPP; |
117 | ip_tunnel_info_opts_get(to: geneveh->options, info: tun_info); |
118 | } |
119 | |
120 | return 0; |
121 | } |
122 | |
123 | static int mlx5e_tc_tun_parse_geneve_vni(struct mlx5e_priv *priv, |
124 | struct mlx5_flow_spec *spec, |
125 | struct flow_cls_offload *f) |
126 | { |
127 | struct flow_rule *rule = flow_cls_offload_flow_rule(flow_cmd: f); |
128 | struct netlink_ext_ack *extack = f->common.extack; |
129 | struct flow_match_enc_keyid enc_keyid; |
130 | void *misc_c, *misc_v; |
131 | |
132 | misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters); |
133 | misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters); |
134 | |
135 | if (!flow_rule_match_key(rule, key: FLOW_DISSECTOR_KEY_ENC_KEYID)) |
136 | return 0; |
137 | |
138 | flow_rule_match_enc_keyid(rule, out: &enc_keyid); |
139 | |
140 | if (!enc_keyid.mask->keyid) |
141 | return 0; |
142 | |
143 | if (!MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev, ft_field_support.outer_geneve_vni)) { |
144 | NL_SET_ERR_MSG_MOD(extack, "Matching on GENEVE VNI is not supported" ); |
145 | netdev_warn(dev: priv->netdev, format: "Matching on GENEVE VNI is not supported\n" ); |
146 | return -EOPNOTSUPP; |
147 | } |
148 | |
149 | MLX5_SET(fte_match_set_misc, misc_c, geneve_vni, be32_to_cpu(enc_keyid.mask->keyid)); |
150 | MLX5_SET(fte_match_set_misc, misc_v, geneve_vni, be32_to_cpu(enc_keyid.key->keyid)); |
151 | |
152 | return 0; |
153 | } |
154 | |
155 | static int mlx5e_tc_tun_parse_geneve_options(struct mlx5e_priv *priv, |
156 | struct mlx5_flow_spec *spec, |
157 | struct flow_cls_offload *f) |
158 | { |
159 | u8 max_tlv_option_data_len = MLX5_CAP_GEN(priv->mdev, max_geneve_tlv_option_data_len); |
160 | u8 max_tlv_options = MLX5_CAP_GEN(priv->mdev, max_geneve_tlv_options); |
161 | struct flow_rule *rule = flow_cls_offload_flow_rule(flow_cmd: f); |
162 | struct netlink_ext_ack *extack = f->common.extack; |
163 | void *misc_c, *misc_v, *misc_3_c, *misc_3_v; |
164 | struct geneve_opt *option_key, *option_mask; |
165 | __be32 opt_data_key = 0, opt_data_mask = 0; |
166 | struct flow_match_enc_opts enc_opts; |
167 | int res = 0; |
168 | |
169 | misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters); |
170 | misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters); |
171 | misc_3_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters_3); |
172 | misc_3_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters_3); |
173 | |
174 | if (!flow_rule_match_key(rule, key: FLOW_DISSECTOR_KEY_ENC_OPTS)) |
175 | return 0; |
176 | |
177 | flow_rule_match_enc_opts(rule, out: &enc_opts); |
178 | |
179 | if (memchr_inv(p: &enc_opts.mask->data, c: 0, size: sizeof(enc_opts.mask->data)) && |
180 | !MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev, |
181 | ft_field_support.geneve_tlv_option_0_data)) { |
182 | NL_SET_ERR_MSG_MOD(extack, |
183 | "Matching on GENEVE options is not supported" ); |
184 | netdev_warn(dev: priv->netdev, |
185 | format: "Matching on GENEVE options is not supported\n" ); |
186 | return -EOPNOTSUPP; |
187 | } |
188 | |
189 | /* make sure that we're talking about GENEVE options */ |
190 | |
191 | if (enc_opts.key->dst_opt_type != TUNNEL_GENEVE_OPT) { |
192 | NL_SET_ERR_MSG_MOD(extack, |
193 | "Matching on GENEVE options: option type is not GENEVE" ); |
194 | netdev_warn(dev: priv->netdev, |
195 | format: "Matching on GENEVE options: option type is not GENEVE\n" ); |
196 | return -EOPNOTSUPP; |
197 | } |
198 | |
199 | if (enc_opts.mask->len && |
200 | !MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev, |
201 | ft_field_support.outer_geneve_opt_len)) { |
202 | NL_SET_ERR_MSG_MOD(extack, "Matching on GENEVE options len is not supported" ); |
203 | netdev_warn(dev: priv->netdev, |
204 | format: "Matching on GENEVE options len is not supported\n" ); |
205 | return -EOPNOTSUPP; |
206 | } |
207 | |
208 | /* max_geneve_tlv_option_data_len comes in multiples of 4 bytes, and it |
209 | * doesn't include the TLV option header. 'geneve_opt_len' is a total |
210 | * len of all the options, including the headers, also multiples of 4 |
211 | * bytes. Len that comes from the dissector is in bytes. |
212 | */ |
213 | |
214 | if ((enc_opts.key->len / 4) > ((max_tlv_option_data_len + 1) * max_tlv_options)) { |
215 | NL_SET_ERR_MSG_MOD(extack, |
216 | "Matching on GENEVE options: unsupported options len" ); |
217 | netdev_warn(dev: priv->netdev, |
218 | format: "Matching on GENEVE options: unsupported options len (len=%d)\n" , |
219 | enc_opts.key->len); |
220 | return -EOPNOTSUPP; |
221 | } |
222 | |
223 | MLX5_SET(fte_match_set_misc, misc_c, geneve_opt_len, enc_opts.mask->len / 4); |
224 | MLX5_SET(fte_match_set_misc, misc_v, geneve_opt_len, enc_opts.key->len / 4); |
225 | |
226 | /* we support matching on one option only, so just get it */ |
227 | option_key = (struct geneve_opt *)&enc_opts.key->data[0]; |
228 | option_mask = (struct geneve_opt *)&enc_opts.mask->data[0]; |
229 | |
230 | if (option_mask->opt_class == 0 && option_mask->type == 0 && |
231 | !memchr_inv(p: option_mask->opt_data, c: 0, size: option_mask->length * 4)) |
232 | return 0; |
233 | |
234 | if (option_key->length > max_tlv_option_data_len) { |
235 | NL_SET_ERR_MSG_MOD(extack, |
236 | "Matching on GENEVE options: unsupported option len" ); |
237 | netdev_warn(dev: priv->netdev, |
238 | format: "Matching on GENEVE options: unsupported option len (key=%d, mask=%d)\n" , |
239 | option_key->length, option_mask->length); |
240 | return -EOPNOTSUPP; |
241 | } |
242 | |
243 | /* data can't be all 0 - fail to offload such rule */ |
244 | if (!memchr_inv(p: option_key->opt_data, c: 0, size: option_key->length * 4)) { |
245 | NL_SET_ERR_MSG_MOD(extack, |
246 | "Matching on GENEVE options: can't match on 0 data field" ); |
247 | netdev_warn(dev: priv->netdev, |
248 | format: "Matching on GENEVE options: can't match on 0 data field\n" ); |
249 | return -EOPNOTSUPP; |
250 | } |
251 | |
252 | /* add new GENEVE TLV options object */ |
253 | res = mlx5_geneve_tlv_option_add(geneve: priv->mdev->geneve, opt: option_key); |
254 | if (res) { |
255 | NL_SET_ERR_MSG_MOD(extack, |
256 | "Matching on GENEVE options: failed creating TLV opt object" ); |
257 | netdev_warn(dev: priv->netdev, |
258 | format: "Matching on GENEVE options: failed creating TLV opt object (class:type:len = 0x%x:0x%x:%d)\n" , |
259 | be16_to_cpu(option_key->opt_class), |
260 | option_key->type, option_key->length); |
261 | return res; |
262 | } |
263 | |
264 | /* In general, after creating the object, need to query it |
265 | * in order to check which option data to set in misc3. |
266 | * But we support only geneve_tlv_option_0_data, so no |
267 | * point querying at this stage. |
268 | */ |
269 | |
270 | memcpy(&opt_data_key, option_key->opt_data, option_key->length * 4); |
271 | memcpy(&opt_data_mask, option_mask->opt_data, option_mask->length * 4); |
272 | MLX5_SET(fte_match_set_misc3, misc_3_v, |
273 | geneve_tlv_option_0_data, be32_to_cpu(opt_data_key)); |
274 | MLX5_SET(fte_match_set_misc3, misc_3_c, |
275 | geneve_tlv_option_0_data, be32_to_cpu(opt_data_mask)); |
276 | if (MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev, |
277 | ft_field_support.geneve_tlv_option_0_exist)) { |
278 | MLX5_SET_TO_ONES(fte_match_set_misc, misc_c, geneve_tlv_option_0_exist); |
279 | MLX5_SET_TO_ONES(fte_match_set_misc, misc_v, geneve_tlv_option_0_exist); |
280 | } |
281 | |
282 | spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS_3; |
283 | |
284 | return 0; |
285 | } |
286 | |
287 | static int mlx5e_tc_tun_parse_geneve_params(struct mlx5e_priv *priv, |
288 | struct mlx5_flow_spec *spec, |
289 | struct flow_cls_offload *f) |
290 | { |
291 | void *misc_c = MLX5_ADDR_OF(fte_match_param, spec->match_criteria, misc_parameters); |
292 | void *misc_v = MLX5_ADDR_OF(fte_match_param, spec->match_value, misc_parameters); |
293 | struct netlink_ext_ack *extack = f->common.extack; |
294 | |
295 | /* match on OAM - packets with OAM bit on should NOT be offloaded */ |
296 | |
297 | if (!MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev, ft_field_support.outer_geneve_oam)) { |
298 | NL_SET_ERR_MSG_MOD(extack, "Matching on GENEVE OAM is not supported" ); |
299 | netdev_warn(dev: priv->netdev, format: "Matching on GENEVE OAM is not supported\n" ); |
300 | return -EOPNOTSUPP; |
301 | } |
302 | MLX5_SET_TO_ONES(fte_match_set_misc, misc_c, geneve_oam); |
303 | MLX5_SET(fte_match_set_misc, misc_v, geneve_oam, 0); |
304 | |
305 | /* Match on GENEVE protocol. We support only Transparent Eth Bridge. */ |
306 | |
307 | if (MLX5_CAP_ESW_FLOWTABLE_FDB(priv->mdev, |
308 | ft_field_support.outer_geneve_protocol_type)) { |
309 | MLX5_SET_TO_ONES(fte_match_set_misc, misc_c, geneve_protocol_type); |
310 | MLX5_SET(fte_match_set_misc, misc_v, geneve_protocol_type, ETH_P_TEB); |
311 | } |
312 | |
313 | spec->match_criteria_enable |= MLX5_MATCH_MISC_PARAMETERS; |
314 | |
315 | return 0; |
316 | } |
317 | |
318 | static int mlx5e_tc_tun_parse_geneve(struct mlx5e_priv *priv, |
319 | struct mlx5_flow_spec *spec, |
320 | struct flow_cls_offload *f, |
321 | void *, |
322 | void *) |
323 | { |
324 | int err; |
325 | |
326 | err = mlx5e_tc_tun_parse_geneve_params(priv, spec, f); |
327 | if (err) |
328 | return err; |
329 | |
330 | err = mlx5e_tc_tun_parse_geneve_vni(priv, spec, f); |
331 | if (err) |
332 | return err; |
333 | |
334 | return mlx5e_tc_tun_parse_geneve_options(priv, spec, f); |
335 | } |
336 | |
337 | static bool mlx5e_tc_tun_encap_info_equal_geneve(struct mlx5e_encap_key *a, |
338 | struct mlx5e_encap_key *b) |
339 | { |
340 | return mlx5e_tc_tun_encap_info_equal_options(a, b, TUNNEL_GENEVE_OPT); |
341 | } |
342 | |
343 | struct mlx5e_tc_tunnel geneve_tunnel = { |
344 | .tunnel_type = MLX5E_TC_TUNNEL_TYPE_GENEVE, |
345 | .match_level = MLX5_MATCH_L4, |
346 | .can_offload = mlx5e_tc_tun_can_offload_geneve, |
347 | .calc_hlen = mlx5e_tc_tun_calc_hlen_geneve, |
348 | .init_encap_attr = mlx5e_tc_tun_init_encap_attr_geneve, |
349 | .generate_ip_tun_hdr = mlx5e_gen_ip_tunnel_header_geneve, |
350 | .parse_udp_ports = mlx5e_tc_tun_parse_udp_ports_geneve, |
351 | .parse_tunnel = mlx5e_tc_tun_parse_geneve, |
352 | .encap_info_equal = mlx5e_tc_tun_encap_info_equal_geneve, |
353 | }; |
354 | |