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
mlx5e_tc_tun_can_offload_vxlan(struct mlx5e_priv * priv)9 static bool mlx5e_tc_tun_can_offload_vxlan(struct mlx5e_priv *priv)
10 {
11 return !!MLX5_CAP_ESW(priv->mdev, vxlan_encap_decap);
12 }
13
mlx5e_tc_tun_calc_hlen_vxlan(struct mlx5e_encap_entry * e)14 static int mlx5e_tc_tun_calc_hlen_vxlan(struct mlx5e_encap_entry *e)
15 {
16 return VXLAN_HLEN;
17 }
18
mlx5e_tc_tun_check_udp_dport_vxlan(struct mlx5e_priv * priv,struct flow_cls_offload * f)19 static 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(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, FLOW_DISSECTOR_KEY_ENC_PORTS))
27 return -EOPNOTSUPP;
28
29 flow_rule_match_enc_ports(rule, &enc_ports);
30
31 /* check the UDP destination port validity */
32
33 if (!mlx5_vxlan_lookup_port(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(priv->netdev,
38 "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
mlx5e_tc_tun_parse_udp_ports_vxlan(struct mlx5e_priv * priv,struct mlx5_flow_spec * spec,struct flow_cls_offload * f,void * headers_c,void * headers_v)46 static 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
mlx5e_tc_tun_init_encap_attr_vxlan(struct net_device * tunnel_dev,struct mlx5e_priv * priv,struct mlx5e_encap_entry * e,struct netlink_ext_ack * extack)61 static 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(priv->mdev->vxlan, dst_port)) {
71 NL_SET_ERR_MSG_MOD(extack,
72 "vxlan udp dport was not registered with the HW");
73 netdev_warn(priv->netdev,
74 "%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
mlx5e_gen_ip_tunnel_header_vxlan(char buf[],__u8 * ip_proto,struct mlx5e_encap_entry * e)83 static 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_key->tun_id);
89 struct udphdr *udp = (struct udphdr *)(buf);
90 const struct vxlan_metadata *md;
91 struct vxlanhdr *vxh;
92
93 if (test_bit(IP_TUNNEL_VXLAN_OPT_BIT, tun_key->tun_flags) &&
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(tun_id);
102 if (test_bit(IP_TUNNEL_VXLAN_OPT_BIT, tun_key->tun_flags)) {
103 md = ip_tunnel_info_opts(e->tun_info);
104 vxlan_build_gbp_hdr(vxh, md);
105 }
106
107 return 0;
108 }
109
mlx5e_tc_tun_parse_vxlan_gbp_option(struct mlx5e_priv * priv,struct mlx5_flow_spec * spec,struct flow_cls_offload * f)110 static 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(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, &enc_opts);
121
122 if (memchr_inv(&enc_opts.mask->data, 0, 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 != IP_TUNNEL_VXLAN_OPT_BIT) {
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
mlx5e_tc_tun_parse_vxlan(struct mlx5e_priv * priv,struct mlx5_flow_spec * spec,struct flow_cls_offload * f,void * headers_c,void * headers_v)157 static 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(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, FLOW_DISSECTOR_KEY_ENC_KEYID))
172 return 0;
173
174 flow_rule_match_enc_keyid(rule, &enc_keyid);
175
176 if (!enc_keyid.mask->keyid)
177 return 0;
178
179 if (flow_rule_match_key(rule, 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(priv->netdev,
194 "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
mlx5e_tc_tun_encap_info_equal_vxlan(struct mlx5e_encap_key * a,struct mlx5e_encap_key * b)208 static 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,
212 IP_TUNNEL_VXLAN_OPT_BIT);
213 }
214
mlx5e_tc_tun_get_remote_ifindex(struct net_device * mirred_dev)215 static int mlx5e_tc_tun_get_remote_ifindex(struct net_device *mirred_dev)
216 {
217 const struct vxlan_dev *vxlan = netdev_priv(mirred_dev);
218 const struct vxlan_rdst *dst = &vxlan->default_dst;
219
220 return dst->remote_ifindex;
221 }
222
223 struct mlx5e_tc_tunnel vxlan_tunnel = {
224 .tunnel_type = MLX5E_TC_TUNNEL_TYPE_VXLAN,
225 .match_level = MLX5_MATCH_L4,
226 .can_offload = mlx5e_tc_tun_can_offload_vxlan,
227 .calc_hlen = mlx5e_tc_tun_calc_hlen_vxlan,
228 .init_encap_attr = mlx5e_tc_tun_init_encap_attr_vxlan,
229 .generate_ip_tun_hdr = mlx5e_gen_ip_tunnel_header_vxlan,
230 .parse_udp_ports = mlx5e_tc_tun_parse_udp_ports_vxlan,
231 .parse_tunnel = mlx5e_tc_tun_parse_vxlan,
232 .encap_info_equal = mlx5e_tc_tun_encap_info_equal_vxlan,
233 .get_remote_ifindex = mlx5e_tc_tun_get_remote_ifindex,
234 };
235