]>
Commit | Line | Data |
---|---|---|
7aa0f5aa JP |
1 | /* |
2 | * drivers/net/ethernet/mellanox/mlxsw/spectrum_flower.c | |
3 | * Copyright (c) 2017 Mellanox Technologies. All rights reserved. | |
4 | * Copyright (c) 2017 Jiri Pirko <jiri@mellanox.com> | |
5 | * | |
6 | * Redistribution and use in source and binary forms, with or without | |
7 | * modification, are permitted provided that the following conditions are met: | |
8 | * | |
9 | * 1. Redistributions of source code must retain the above copyright | |
10 | * notice, this list of conditions and the following disclaimer. | |
11 | * 2. Redistributions in binary form must reproduce the above copyright | |
12 | * notice, this list of conditions and the following disclaimer in the | |
13 | * documentation and/or other materials provided with the distribution. | |
14 | * 3. Neither the names of the copyright holders nor the names of its | |
15 | * contributors may be used to endorse or promote products derived from | |
16 | * this software without specific prior written permission. | |
17 | * | |
18 | * Alternatively, this software may be distributed under the terms of the | |
19 | * GNU General Public License ("GPL") version 2 as published by the Free | |
20 | * Software Foundation. | |
21 | * | |
22 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
23 | * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
26 | * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
27 | * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
28 | * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
29 | * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
30 | * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
31 | * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
32 | * POSSIBILITY OF SUCH DAMAGE. | |
33 | */ | |
34 | ||
35 | #include <linux/kernel.h> | |
36 | #include <linux/errno.h> | |
37 | #include <linux/netdevice.h> | |
38 | #include <net/flow_dissector.h> | |
39 | #include <net/pkt_cls.h> | |
40 | #include <net/tc_act/tc_gact.h> | |
41 | #include <net/tc_act/tc_mirred.h> | |
42 | ||
43 | #include "spectrum.h" | |
44 | #include "core_acl_flex_keys.h" | |
45 | ||
46 | static int mlxsw_sp_flower_parse_actions(struct mlxsw_sp *mlxsw_sp, | |
47 | struct net_device *dev, | |
48 | struct mlxsw_sp_acl_rule_info *rulei, | |
49 | struct tcf_exts *exts) | |
50 | { | |
51 | const struct tc_action *a; | |
52 | LIST_HEAD(actions); | |
53 | int err; | |
54 | ||
55 | if (tc_no_actions(exts)) | |
56 | return 0; | |
57 | ||
58 | tcf_exts_to_list(exts, &actions); | |
59 | list_for_each_entry(a, &actions, list) { | |
60 | if (is_tcf_gact_shot(a)) { | |
61 | err = mlxsw_sp_acl_rulei_act_drop(rulei); | |
62 | if (err) | |
63 | return err; | |
64 | } else if (is_tcf_mirred_egress_redirect(a)) { | |
65 | int ifindex = tcf_mirred_ifindex(a); | |
66 | struct net_device *out_dev; | |
67 | ||
68 | out_dev = __dev_get_by_index(dev_net(dev), ifindex); | |
69 | if (out_dev == dev) | |
70 | out_dev = NULL; | |
71 | ||
72 | err = mlxsw_sp_acl_rulei_act_fwd(mlxsw_sp, rulei, | |
73 | out_dev); | |
74 | if (err) | |
75 | return err; | |
76 | } else { | |
77 | dev_err(mlxsw_sp->bus_info->dev, "Unsupported action\n"); | |
78 | return -EOPNOTSUPP; | |
79 | } | |
80 | } | |
81 | return 0; | |
82 | } | |
83 | ||
84 | static void mlxsw_sp_flower_parse_ipv4(struct mlxsw_sp_acl_rule_info *rulei, | |
85 | struct tc_cls_flower_offload *f) | |
86 | { | |
87 | struct flow_dissector_key_ipv4_addrs *key = | |
88 | skb_flow_dissector_target(f->dissector, | |
89 | FLOW_DISSECTOR_KEY_IPV4_ADDRS, | |
90 | f->key); | |
91 | struct flow_dissector_key_ipv4_addrs *mask = | |
92 | skb_flow_dissector_target(f->dissector, | |
93 | FLOW_DISSECTOR_KEY_IPV4_ADDRS, | |
94 | f->mask); | |
95 | ||
96 | mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_SRC_IP4, | |
97 | ntohl(key->src), ntohl(mask->src)); | |
98 | mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_DST_IP4, | |
99 | ntohl(key->dst), ntohl(mask->dst)); | |
100 | } | |
101 | ||
102 | static void mlxsw_sp_flower_parse_ipv6(struct mlxsw_sp_acl_rule_info *rulei, | |
103 | struct tc_cls_flower_offload *f) | |
104 | { | |
105 | struct flow_dissector_key_ipv6_addrs *key = | |
106 | skb_flow_dissector_target(f->dissector, | |
107 | FLOW_DISSECTOR_KEY_IPV6_ADDRS, | |
108 | f->key); | |
109 | struct flow_dissector_key_ipv6_addrs *mask = | |
110 | skb_flow_dissector_target(f->dissector, | |
111 | FLOW_DISSECTOR_KEY_IPV6_ADDRS, | |
112 | f->mask); | |
113 | size_t addr_half_size = sizeof(key->src) / 2; | |
114 | ||
115 | mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP6_HI, | |
116 | &key->src.s6_addr[0], | |
117 | &mask->src.s6_addr[0], | |
118 | addr_half_size); | |
119 | mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_SRC_IP6_LO, | |
120 | &key->src.s6_addr[addr_half_size], | |
121 | &mask->src.s6_addr[addr_half_size], | |
122 | addr_half_size); | |
123 | mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP6_HI, | |
124 | &key->dst.s6_addr[0], | |
125 | &mask->dst.s6_addr[0], | |
126 | addr_half_size); | |
127 | mlxsw_sp_acl_rulei_keymask_buf(rulei, MLXSW_AFK_ELEMENT_DST_IP6_LO, | |
128 | &key->dst.s6_addr[addr_half_size], | |
129 | &mask->dst.s6_addr[addr_half_size], | |
130 | addr_half_size); | |
131 | } | |
132 | ||
133 | static int mlxsw_sp_flower_parse_ports(struct mlxsw_sp *mlxsw_sp, | |
134 | struct mlxsw_sp_acl_rule_info *rulei, | |
135 | struct tc_cls_flower_offload *f, | |
136 | u8 ip_proto) | |
137 | { | |
138 | struct flow_dissector_key_ports *key, *mask; | |
139 | ||
140 | if (!dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_PORTS)) | |
141 | return 0; | |
142 | ||
143 | if (ip_proto != IPPROTO_TCP && ip_proto != IPPROTO_UDP) { | |
144 | dev_err(mlxsw_sp->bus_info->dev, "Only UDP and TCP keys are supported\n"); | |
145 | return -EINVAL; | |
146 | } | |
147 | ||
148 | key = skb_flow_dissector_target(f->dissector, | |
149 | FLOW_DISSECTOR_KEY_PORTS, | |
150 | f->key); | |
151 | mask = skb_flow_dissector_target(f->dissector, | |
152 | FLOW_DISSECTOR_KEY_PORTS, | |
153 | f->mask); | |
154 | mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_DST_L4_PORT, | |
155 | ntohs(key->dst), ntohs(mask->dst)); | |
156 | mlxsw_sp_acl_rulei_keymask_u32(rulei, MLXSW_AFK_ELEMENT_SRC_L4_PORT, | |
157 | ntohs(key->src), ntohs(mask->src)); | |
158 | return 0; | |
159 | } | |
160 | ||
161 | static int mlxsw_sp_flower_parse(struct mlxsw_sp *mlxsw_sp, | |
162 | struct net_device *dev, | |
163 | struct mlxsw_sp_acl_rule_info *rulei, | |
164 | struct tc_cls_flower_offload *f) | |
165 | { | |
166 | u16 addr_type = 0; | |
167 | u8 ip_proto = 0; | |
168 | int err; | |
169 | ||
170 | if (f->dissector->used_keys & | |
171 | ~(BIT(FLOW_DISSECTOR_KEY_CONTROL) | | |
172 | BIT(FLOW_DISSECTOR_KEY_BASIC) | | |
173 | BIT(FLOW_DISSECTOR_KEY_ETH_ADDRS) | | |
174 | BIT(FLOW_DISSECTOR_KEY_IPV4_ADDRS) | | |
175 | BIT(FLOW_DISSECTOR_KEY_IPV6_ADDRS) | | |
176 | BIT(FLOW_DISSECTOR_KEY_PORTS))) { | |
177 | dev_err(mlxsw_sp->bus_info->dev, "Unsupported key\n"); | |
178 | return -EOPNOTSUPP; | |
179 | } | |
180 | ||
181 | mlxsw_sp_acl_rulei_priority(rulei, f->prio); | |
182 | ||
183 | if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_CONTROL)) { | |
184 | struct flow_dissector_key_control *key = | |
185 | skb_flow_dissector_target(f->dissector, | |
186 | FLOW_DISSECTOR_KEY_CONTROL, | |
187 | f->key); | |
188 | addr_type = key->addr_type; | |
189 | } | |
190 | ||
191 | if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_BASIC)) { | |
192 | struct flow_dissector_key_basic *key = | |
193 | skb_flow_dissector_target(f->dissector, | |
194 | FLOW_DISSECTOR_KEY_BASIC, | |
195 | f->key); | |
196 | struct flow_dissector_key_basic *mask = | |
197 | skb_flow_dissector_target(f->dissector, | |
198 | FLOW_DISSECTOR_KEY_BASIC, | |
199 | f->mask); | |
dc371700 JP |
200 | u16 n_proto_key = ntohs(key->n_proto); |
201 | u16 n_proto_mask = ntohs(mask->n_proto); | |
202 | ||
203 | if (n_proto_key == ETH_P_ALL) { | |
204 | n_proto_key = 0; | |
205 | n_proto_mask = 0; | |
206 | } | |
7aa0f5aa JP |
207 | mlxsw_sp_acl_rulei_keymask_u32(rulei, |
208 | MLXSW_AFK_ELEMENT_ETHERTYPE, | |
dc371700 JP |
209 | n_proto_key, n_proto_mask); |
210 | ||
211 | ip_proto = key->ip_proto; | |
7aa0f5aa JP |
212 | mlxsw_sp_acl_rulei_keymask_u32(rulei, |
213 | MLXSW_AFK_ELEMENT_IP_PROTO, | |
214 | key->ip_proto, mask->ip_proto); | |
215 | } | |
216 | ||
217 | if (dissector_uses_key(f->dissector, FLOW_DISSECTOR_KEY_ETH_ADDRS)) { | |
218 | struct flow_dissector_key_eth_addrs *key = | |
219 | skb_flow_dissector_target(f->dissector, | |
220 | FLOW_DISSECTOR_KEY_ETH_ADDRS, | |
221 | f->key); | |
222 | struct flow_dissector_key_eth_addrs *mask = | |
223 | skb_flow_dissector_target(f->dissector, | |
224 | FLOW_DISSECTOR_KEY_ETH_ADDRS, | |
225 | f->mask); | |
226 | ||
227 | mlxsw_sp_acl_rulei_keymask_buf(rulei, | |
228 | MLXSW_AFK_ELEMENT_DMAC, | |
229 | key->dst, mask->dst, | |
230 | sizeof(key->dst)); | |
231 | mlxsw_sp_acl_rulei_keymask_buf(rulei, | |
232 | MLXSW_AFK_ELEMENT_SMAC, | |
233 | key->src, mask->src, | |
234 | sizeof(key->src)); | |
235 | } | |
236 | ||
237 | if (addr_type == FLOW_DISSECTOR_KEY_IPV4_ADDRS) | |
238 | mlxsw_sp_flower_parse_ipv4(rulei, f); | |
239 | ||
240 | if (addr_type == FLOW_DISSECTOR_KEY_IPV6_ADDRS) | |
241 | mlxsw_sp_flower_parse_ipv6(rulei, f); | |
242 | ||
243 | err = mlxsw_sp_flower_parse_ports(mlxsw_sp, rulei, f, ip_proto); | |
244 | if (err) | |
245 | return err; | |
246 | ||
247 | return mlxsw_sp_flower_parse_actions(mlxsw_sp, dev, rulei, f->exts); | |
248 | } | |
249 | ||
250 | int mlxsw_sp_flower_replace(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, | |
251 | __be16 protocol, struct tc_cls_flower_offload *f) | |
252 | { | |
253 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
254 | struct net_device *dev = mlxsw_sp_port->dev; | |
255 | struct mlxsw_sp_acl_rule_info *rulei; | |
256 | struct mlxsw_sp_acl_ruleset *ruleset; | |
257 | struct mlxsw_sp_acl_rule *rule; | |
258 | int err; | |
259 | ||
260 | ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, dev, ingress, | |
261 | MLXSW_SP_ACL_PROFILE_FLOWER); | |
262 | if (IS_ERR(ruleset)) | |
263 | return PTR_ERR(ruleset); | |
264 | ||
265 | rule = mlxsw_sp_acl_rule_create(mlxsw_sp, ruleset, f->cookie); | |
266 | if (IS_ERR(rule)) { | |
267 | err = PTR_ERR(rule); | |
268 | goto err_rule_create; | |
269 | } | |
270 | ||
271 | rulei = mlxsw_sp_acl_rule_rulei(rule); | |
272 | err = mlxsw_sp_flower_parse(mlxsw_sp, dev, rulei, f); | |
273 | if (err) | |
274 | goto err_flower_parse; | |
275 | ||
276 | err = mlxsw_sp_acl_rulei_commit(rulei); | |
277 | if (err) | |
278 | goto err_rulei_commit; | |
279 | ||
280 | err = mlxsw_sp_acl_rule_add(mlxsw_sp, rule); | |
281 | if (err) | |
282 | goto err_rule_add; | |
283 | ||
284 | mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset); | |
285 | return 0; | |
286 | ||
287 | err_rule_add: | |
288 | err_rulei_commit: | |
289 | err_flower_parse: | |
290 | mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule); | |
291 | err_rule_create: | |
292 | mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset); | |
293 | return err; | |
294 | } | |
295 | ||
296 | void mlxsw_sp_flower_destroy(struct mlxsw_sp_port *mlxsw_sp_port, bool ingress, | |
297 | struct tc_cls_flower_offload *f) | |
298 | { | |
299 | struct mlxsw_sp *mlxsw_sp = mlxsw_sp_port->mlxsw_sp; | |
300 | struct mlxsw_sp_acl_ruleset *ruleset; | |
301 | struct mlxsw_sp_acl_rule *rule; | |
302 | ||
303 | ruleset = mlxsw_sp_acl_ruleset_get(mlxsw_sp, mlxsw_sp_port->dev, | |
304 | ingress, | |
305 | MLXSW_SP_ACL_PROFILE_FLOWER); | |
713c43b3 | 306 | if (IS_ERR(ruleset)) |
7aa0f5aa JP |
307 | return; |
308 | ||
309 | rule = mlxsw_sp_acl_rule_lookup(mlxsw_sp, ruleset, f->cookie); | |
713c43b3 | 310 | if (rule) { |
7aa0f5aa JP |
311 | mlxsw_sp_acl_rule_del(mlxsw_sp, rule); |
312 | mlxsw_sp_acl_rule_destroy(mlxsw_sp, rule); | |
313 | } | |
314 | ||
315 | mlxsw_sp_acl_ruleset_put(mlxsw_sp, ruleset); | |
316 | } |