]>
Commit | Line | Data |
---|---|---|
f67539c2 TL |
1 | /* SPDX-License-Identifier: BSD-3-Clause |
2 | * Copyright(c) 2010-2020 Intel Corporation | |
3 | */ | |
4 | ||
5 | #include "rte_malloc.h" | |
6 | #include "igc_logs.h" | |
7 | #include "igc_txrx.h" | |
8 | #include "igc_filter.h" | |
9 | #include "igc_flow.h" | |
10 | ||
11 | /******************************************************************************* | |
12 | * All Supported Rule Type | |
13 | * | |
14 | * Notes: | |
15 | * `para` or `(para)`, the para must been set | |
16 | * `[para]`, the para is optional | |
17 | * `([para1][para2]...)`, all paras is optional, but must one of them been set | |
18 | * `para1 | para2 | ...`, only one of the paras can be set | |
19 | * | |
20 | * ether-type filter | |
21 | * pattern: ETH(type)/END | |
22 | * action: QUEUE/END | |
23 | * attribute: | |
24 | * | |
25 | * n-tuple filter | |
26 | * pattern: [ETH/]([IPv4(protocol)|IPv6(protocol)/][UDP(dst_port)| | |
27 | * TCP([dst_port],[flags])|SCTP(dst_port)/])END | |
28 | * action: QUEUE/END | |
29 | * attribute: [priority(0-7)] | |
30 | * | |
31 | * SYN filter | |
32 | * pattern: [ETH/][IPv4|IPv6/]TCP(flags=SYN)/END | |
33 | * action: QUEUE/END | |
34 | * attribute: [priority(0,1)] | |
35 | * | |
36 | * RSS filter | |
37 | * pattern: | |
38 | * action: RSS/END | |
39 | * attribute: | |
40 | ******************************************************************************/ | |
41 | ||
42 | /* Structure to store all filters */ | |
43 | struct igc_all_filter { | |
44 | struct igc_ethertype_filter ethertype; | |
45 | struct igc_ntuple_filter ntuple; | |
46 | struct igc_syn_filter syn; | |
47 | struct igc_rss_filter rss; | |
48 | uint32_t mask; /* see IGC_FILTER_MASK_* definition */ | |
49 | }; | |
50 | ||
51 | #define IGC_FILTER_MASK_ETHER (1u << IGC_FILTER_TYPE_ETHERTYPE) | |
52 | #define IGC_FILTER_MASK_NTUPLE (1u << IGC_FILTER_TYPE_NTUPLE) | |
53 | #define IGC_FILTER_MASK_TCP_SYN (1u << IGC_FILTER_TYPE_SYN) | |
54 | #define IGC_FILTER_MASK_RSS (1u << IGC_FILTER_TYPE_HASH) | |
55 | #define IGC_FILTER_MASK_ALL (IGC_FILTER_MASK_ETHER | \ | |
56 | IGC_FILTER_MASK_NTUPLE | \ | |
57 | IGC_FILTER_MASK_TCP_SYN | \ | |
58 | IGC_FILTER_MASK_RSS) | |
59 | ||
60 | #define IGC_SET_FILTER_MASK(_filter, _mask_bits) \ | |
61 | ((_filter)->mask &= (_mask_bits)) | |
62 | ||
63 | #define IGC_IS_ALL_BITS_SET(_val) ((_val) == (typeof(_val))~0) | |
64 | #define IGC_NOT_ALL_BITS_SET(_val) ((_val) != (typeof(_val))~0) | |
65 | ||
66 | /* Parse rule attribute */ | |
67 | static int | |
68 | igc_parse_attribute(const struct rte_flow_attr *attr, | |
69 | struct igc_all_filter *filter, struct rte_flow_error *error) | |
70 | { | |
71 | if (!attr) | |
72 | return 0; | |
73 | ||
74 | if (attr->group) | |
75 | return rte_flow_error_set(error, EINVAL, | |
76 | RTE_FLOW_ERROR_TYPE_ATTR_GROUP, attr, | |
77 | "Not support"); | |
78 | ||
79 | if (attr->egress) | |
80 | return rte_flow_error_set(error, EINVAL, | |
81 | RTE_FLOW_ERROR_TYPE_ATTR_EGRESS, attr, | |
82 | "Not support"); | |
83 | ||
84 | if (attr->transfer) | |
85 | return rte_flow_error_set(error, EINVAL, | |
86 | RTE_FLOW_ERROR_TYPE_ATTR_TRANSFER, attr, | |
87 | "Not support"); | |
88 | ||
89 | if (!attr->ingress) | |
90 | return rte_flow_error_set(error, EINVAL, | |
91 | RTE_FLOW_ERROR_TYPE_ATTR_INGRESS, attr, | |
92 | "A rule must apply to ingress traffic"); | |
93 | ||
94 | if (attr->priority == 0) | |
95 | return 0; | |
96 | ||
97 | /* only n-tuple and SYN filter have priority level */ | |
98 | IGC_SET_FILTER_MASK(filter, | |
99 | IGC_FILTER_MASK_NTUPLE | IGC_FILTER_MASK_TCP_SYN); | |
100 | ||
101 | if (IGC_IS_ALL_BITS_SET(attr->priority)) { | |
102 | /* only SYN filter match this value */ | |
103 | IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_TCP_SYN); | |
104 | filter->syn.hig_pri = 1; | |
105 | return 0; | |
106 | } | |
107 | ||
108 | if (attr->priority > IGC_NTUPLE_MAX_PRI) | |
109 | return rte_flow_error_set(error, EINVAL, | |
110 | RTE_FLOW_ERROR_TYPE_ATTR_PRIORITY, attr, | |
111 | "Priority value is invalid."); | |
112 | ||
113 | if (attr->priority > 1) { | |
114 | /* only n-tuple filter match this value */ | |
115 | IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE); | |
116 | ||
117 | /* get priority */ | |
118 | filter->ntuple.tuple_info.priority = (uint8_t)attr->priority; | |
119 | return 0; | |
120 | } | |
121 | ||
122 | /* get priority */ | |
123 | filter->ntuple.tuple_info.priority = (uint8_t)attr->priority; | |
124 | filter->syn.hig_pri = (uint8_t)attr->priority; | |
125 | ||
126 | return 0; | |
127 | } | |
128 | ||
129 | /* function type of parse pattern */ | |
130 | typedef int (*igc_pattern_parse)(const struct rte_flow_item *, | |
131 | struct igc_all_filter *, struct rte_flow_error *); | |
132 | ||
133 | static int igc_parse_pattern_void(__rte_unused const struct rte_flow_item *item, | |
134 | __rte_unused struct igc_all_filter *filter, | |
135 | __rte_unused struct rte_flow_error *error); | |
136 | static int igc_parse_pattern_ether(const struct rte_flow_item *item, | |
137 | struct igc_all_filter *filter, struct rte_flow_error *error); | |
138 | static int igc_parse_pattern_ip(const struct rte_flow_item *item, | |
139 | struct igc_all_filter *filter, struct rte_flow_error *error); | |
140 | static int igc_parse_pattern_ipv6(const struct rte_flow_item *item, | |
141 | struct igc_all_filter *filter, struct rte_flow_error *error); | |
142 | static int igc_parse_pattern_udp(const struct rte_flow_item *item, | |
143 | struct igc_all_filter *filter, struct rte_flow_error *error); | |
144 | static int igc_parse_pattern_tcp(const struct rte_flow_item *item, | |
145 | struct igc_all_filter *filter, struct rte_flow_error *error); | |
146 | ||
147 | static igc_pattern_parse pattern_parse_list[] = { | |
148 | [RTE_FLOW_ITEM_TYPE_VOID] = igc_parse_pattern_void, | |
149 | [RTE_FLOW_ITEM_TYPE_ETH] = igc_parse_pattern_ether, | |
150 | [RTE_FLOW_ITEM_TYPE_IPV4] = igc_parse_pattern_ip, | |
151 | [RTE_FLOW_ITEM_TYPE_IPV6] = igc_parse_pattern_ipv6, | |
152 | [RTE_FLOW_ITEM_TYPE_UDP] = igc_parse_pattern_udp, | |
153 | [RTE_FLOW_ITEM_TYPE_TCP] = igc_parse_pattern_tcp, | |
154 | }; | |
155 | ||
156 | /* Parse rule patterns */ | |
157 | static int | |
158 | igc_parse_patterns(const struct rte_flow_item patterns[], | |
159 | struct igc_all_filter *filter, struct rte_flow_error *error) | |
160 | { | |
161 | const struct rte_flow_item *item = patterns; | |
162 | ||
163 | if (item == NULL) { | |
164 | /* only RSS filter match this pattern */ | |
165 | IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_RSS); | |
166 | return 0; | |
167 | } | |
168 | ||
169 | for (; item->type != RTE_FLOW_ITEM_TYPE_END; item++) { | |
170 | int ret; | |
171 | ||
172 | if (item->type >= RTE_DIM(pattern_parse_list)) | |
173 | return rte_flow_error_set(error, EINVAL, | |
174 | RTE_FLOW_ERROR_TYPE_ITEM, item, | |
175 | "Not been supported"); | |
176 | ||
177 | if (item->last) | |
178 | return rte_flow_error_set(error, EINVAL, | |
179 | RTE_FLOW_ERROR_TYPE_ITEM_LAST, item, | |
180 | "Range not been supported"); | |
181 | ||
182 | /* check pattern format is valid */ | |
183 | if (!!item->spec ^ !!item->mask) | |
184 | return rte_flow_error_set(error, EINVAL, | |
185 | RTE_FLOW_ERROR_TYPE_ITEM, item, | |
186 | "Format error"); | |
187 | ||
188 | /* get the pattern type callback */ | |
189 | igc_pattern_parse parse_func = | |
190 | pattern_parse_list[item->type]; | |
191 | if (!parse_func) | |
192 | return rte_flow_error_set(error, EINVAL, | |
193 | RTE_FLOW_ERROR_TYPE_ITEM, item, | |
194 | "Not been supported"); | |
195 | ||
196 | /* call the pattern type function */ | |
197 | ret = parse_func(item, filter, error); | |
198 | if (ret) | |
199 | return ret; | |
200 | ||
201 | /* if no filter match the pattern */ | |
202 | if (filter->mask == 0) | |
203 | return rte_flow_error_set(error, EINVAL, | |
204 | RTE_FLOW_ERROR_TYPE_ITEM, item, | |
205 | "Not been supported"); | |
206 | } | |
207 | ||
208 | return 0; | |
209 | } | |
210 | ||
211 | static int igc_parse_action_queue(struct rte_eth_dev *dev, | |
212 | const struct rte_flow_action *act, | |
213 | struct igc_all_filter *filter, struct rte_flow_error *error); | |
214 | static int igc_parse_action_rss(struct rte_eth_dev *dev, | |
215 | const struct rte_flow_action *act, | |
216 | struct igc_all_filter *filter, struct rte_flow_error *error); | |
217 | ||
218 | /* Parse flow actions */ | |
219 | static int | |
220 | igc_parse_actions(struct rte_eth_dev *dev, | |
221 | const struct rte_flow_action actions[], | |
222 | struct igc_all_filter *filter, | |
223 | struct rte_flow_error *error) | |
224 | { | |
225 | const struct rte_flow_action *act = actions; | |
226 | int ret; | |
227 | ||
228 | if (act == NULL) | |
229 | return rte_flow_error_set(error, EINVAL, | |
230 | RTE_FLOW_ERROR_TYPE_ACTION_NUM, act, | |
231 | "Action is needed"); | |
232 | ||
233 | for (; act->type != RTE_FLOW_ACTION_TYPE_END; act++) { | |
234 | switch (act->type) { | |
235 | case RTE_FLOW_ACTION_TYPE_QUEUE: | |
236 | ret = igc_parse_action_queue(dev, act, filter, error); | |
237 | if (ret) | |
238 | return ret; | |
239 | break; | |
240 | case RTE_FLOW_ACTION_TYPE_RSS: | |
241 | ret = igc_parse_action_rss(dev, act, filter, error); | |
242 | if (ret) | |
243 | return ret; | |
244 | break; | |
245 | case RTE_FLOW_ACTION_TYPE_VOID: | |
246 | break; | |
247 | default: | |
248 | return rte_flow_error_set(error, EINVAL, | |
249 | RTE_FLOW_ERROR_TYPE_ACTION, act, | |
250 | "Not been supported"); | |
251 | } | |
252 | ||
253 | /* if no filter match the action */ | |
254 | if (filter->mask == 0) | |
255 | return rte_flow_error_set(error, EINVAL, | |
256 | RTE_FLOW_ERROR_TYPE_ACTION, act, | |
257 | "Not been supported"); | |
258 | } | |
259 | ||
260 | return 0; | |
261 | } | |
262 | ||
263 | /* Parse a flow rule */ | |
264 | static int | |
265 | igc_parse_flow(struct rte_eth_dev *dev, | |
266 | const struct rte_flow_attr *attr, | |
267 | const struct rte_flow_item patterns[], | |
268 | const struct rte_flow_action actions[], | |
269 | struct rte_flow_error *error, | |
270 | struct igc_all_filter *filter) | |
271 | { | |
272 | int ret; | |
273 | ||
274 | /* clear all filters */ | |
275 | memset(filter, 0, sizeof(*filter)); | |
276 | ||
277 | /* set default filter mask */ | |
278 | filter->mask = IGC_FILTER_MASK_ALL; | |
279 | ||
280 | ret = igc_parse_attribute(attr, filter, error); | |
281 | if (ret) | |
282 | return ret; | |
283 | ||
284 | ret = igc_parse_patterns(patterns, filter, error); | |
285 | if (ret) | |
286 | return ret; | |
287 | ||
288 | ret = igc_parse_actions(dev, actions, filter, error); | |
289 | if (ret) | |
290 | return ret; | |
291 | ||
292 | /* if no or more than one filter matched this flow */ | |
293 | if (filter->mask == 0 || (filter->mask & (filter->mask - 1))) | |
294 | return rte_flow_error_set(error, EINVAL, | |
295 | RTE_FLOW_ERROR_TYPE_ITEM, NULL, | |
296 | "Flow can't be recognized"); | |
297 | return 0; | |
298 | } | |
299 | ||
300 | /* Parse pattern type of void */ | |
301 | static int | |
302 | igc_parse_pattern_void(__rte_unused const struct rte_flow_item *item, | |
303 | __rte_unused struct igc_all_filter *filter, | |
304 | __rte_unused struct rte_flow_error *error) | |
305 | { | |
306 | return 0; | |
307 | } | |
308 | ||
309 | /* Parse pattern type of ethernet header */ | |
310 | static int | |
311 | igc_parse_pattern_ether(const struct rte_flow_item *item, | |
312 | struct igc_all_filter *filter, | |
313 | struct rte_flow_error *error) | |
314 | { | |
315 | const struct rte_flow_item_eth *spec = item->spec; | |
316 | const struct rte_flow_item_eth *mask = item->mask; | |
317 | struct igc_ethertype_filter *ether; | |
318 | ||
319 | if (mask == NULL) { | |
320 | /* only n-tuple and SYN filter match the pattern */ | |
321 | IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE | | |
322 | IGC_FILTER_MASK_TCP_SYN); | |
323 | return 0; | |
324 | } | |
325 | ||
326 | /* only ether-type filter match the pattern*/ | |
327 | IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_ETHER); | |
328 | ||
329 | /* destination and source MAC address are not supported */ | |
330 | if (!rte_is_zero_ether_addr(&mask->src) || | |
331 | !rte_is_zero_ether_addr(&mask->dst)) | |
332 | return rte_flow_error_set(error, EINVAL, | |
333 | RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, | |
334 | "Only support ether-type"); | |
335 | ||
336 | /* ether-type mask bits must be all 1 */ | |
337 | if (IGC_NOT_ALL_BITS_SET(mask->type)) | |
338 | return rte_flow_error_set(error, EINVAL, | |
339 | RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, | |
340 | "Ethernet type mask bits must be all 1"); | |
341 | ||
342 | ether = &filter->ethertype; | |
343 | ||
344 | /* get ether-type */ | |
345 | ether->ether_type = rte_be_to_cpu_16(spec->type); | |
346 | ||
347 | /* ether-type should not be IPv4 and IPv6 */ | |
348 | if (ether->ether_type == RTE_ETHER_TYPE_IPV4 || | |
349 | ether->ether_type == RTE_ETHER_TYPE_IPV6 || | |
350 | ether->ether_type == 0) | |
351 | return rte_flow_error_set(error, EINVAL, | |
352 | RTE_FLOW_ERROR_TYPE_ITEM, NULL, | |
353 | "IPv4/IPv6/0 not supported by ethertype filter"); | |
354 | return 0; | |
355 | } | |
356 | ||
357 | /* Parse pattern type of IP */ | |
358 | static int | |
359 | igc_parse_pattern_ip(const struct rte_flow_item *item, | |
360 | struct igc_all_filter *filter, | |
361 | struct rte_flow_error *error) | |
362 | { | |
363 | const struct rte_flow_item_ipv4 *spec = item->spec; | |
364 | const struct rte_flow_item_ipv4 *mask = item->mask; | |
365 | ||
366 | if (mask == NULL) { | |
367 | /* only n-tuple and SYN filter match this pattern */ | |
368 | IGC_SET_FILTER_MASK(filter, | |
369 | IGC_FILTER_MASK_NTUPLE | IGC_FILTER_MASK_TCP_SYN); | |
370 | return 0; | |
371 | } | |
372 | ||
373 | /* only n-tuple filter match this pattern */ | |
374 | IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE); | |
375 | ||
376 | /* only protocol is used */ | |
377 | if (mask->hdr.version_ihl || | |
378 | mask->hdr.type_of_service || | |
379 | mask->hdr.total_length || | |
380 | mask->hdr.packet_id || | |
381 | mask->hdr.fragment_offset || | |
382 | mask->hdr.time_to_live || | |
383 | mask->hdr.hdr_checksum || | |
384 | mask->hdr.dst_addr || | |
385 | mask->hdr.src_addr) | |
386 | return rte_flow_error_set(error, | |
387 | EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, | |
388 | "IPv4 only support protocol"); | |
389 | ||
390 | if (mask->hdr.next_proto_id == 0) | |
391 | return 0; | |
392 | ||
393 | if (IGC_NOT_ALL_BITS_SET(mask->hdr.next_proto_id)) | |
394 | return rte_flow_error_set(error, | |
395 | EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, | |
396 | "IPv4 protocol mask bits must be all 0 or 1"); | |
397 | ||
398 | /* get protocol type */ | |
399 | filter->ntuple.tuple_info.proto_mask = 1; | |
400 | filter->ntuple.tuple_info.proto = spec->hdr.next_proto_id; | |
401 | return 0; | |
402 | } | |
403 | ||
404 | /* | |
405 | * Check ipv6 address is 0 | |
406 | * Return 1 if true, 0 for false. | |
407 | */ | |
408 | static inline bool | |
409 | igc_is_zero_ipv6_addr(const void *ipv6_addr) | |
410 | { | |
411 | const uint64_t *ddw = ipv6_addr; | |
412 | return ddw[0] == 0 && ddw[1] == 0; | |
413 | } | |
414 | ||
415 | /* Parse pattern type of IPv6 */ | |
416 | static int | |
417 | igc_parse_pattern_ipv6(const struct rte_flow_item *item, | |
418 | struct igc_all_filter *filter, | |
419 | struct rte_flow_error *error) | |
420 | { | |
421 | const struct rte_flow_item_ipv6 *spec = item->spec; | |
422 | const struct rte_flow_item_ipv6 *mask = item->mask; | |
423 | ||
424 | if (mask == NULL) { | |
425 | /* only n-tuple and syn filter match this pattern */ | |
426 | IGC_SET_FILTER_MASK(filter, | |
427 | IGC_FILTER_MASK_NTUPLE | IGC_FILTER_MASK_TCP_SYN); | |
428 | return 0; | |
429 | } | |
430 | ||
431 | /* only n-tuple filter match this pattern */ | |
432 | IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE); | |
433 | ||
434 | /* only protocol is used */ | |
435 | if (mask->hdr.vtc_flow || | |
436 | mask->hdr.payload_len || | |
437 | mask->hdr.hop_limits || | |
438 | !igc_is_zero_ipv6_addr(mask->hdr.src_addr) || | |
439 | !igc_is_zero_ipv6_addr(mask->hdr.dst_addr)) | |
440 | return rte_flow_error_set(error, EINVAL, | |
441 | RTE_FLOW_ERROR_TYPE_ITEM, item, | |
442 | "IPv6 only support protocol"); | |
443 | ||
444 | if (mask->hdr.proto == 0) | |
445 | return 0; | |
446 | ||
447 | if (IGC_NOT_ALL_BITS_SET(mask->hdr.proto)) | |
448 | return rte_flow_error_set(error, | |
449 | EINVAL, RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, | |
450 | "IPv6 protocol mask bits must be all 0 or 1"); | |
451 | ||
452 | /* get protocol type */ | |
453 | filter->ntuple.tuple_info.proto_mask = 1; | |
454 | filter->ntuple.tuple_info.proto = spec->hdr.proto; | |
455 | ||
456 | return 0; | |
457 | } | |
458 | ||
459 | /* Parse pattern type of UDP */ | |
460 | static int | |
461 | igc_parse_pattern_udp(const struct rte_flow_item *item, | |
462 | struct igc_all_filter *filter, | |
463 | struct rte_flow_error *error) | |
464 | { | |
465 | const struct rte_flow_item_udp *spec = item->spec; | |
466 | const struct rte_flow_item_udp *mask = item->mask; | |
467 | ||
468 | /* only n-tuple filter match this pattern */ | |
469 | IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE); | |
470 | ||
471 | if (mask == NULL) | |
472 | return 0; | |
473 | ||
474 | /* only destination port is used */ | |
475 | if (mask->hdr.dgram_len || mask->hdr.dgram_cksum || mask->hdr.src_port) | |
476 | return rte_flow_error_set(error, EINVAL, | |
477 | RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, | |
478 | "UDP only support destination port"); | |
479 | ||
480 | if (mask->hdr.dst_port == 0) | |
481 | return 0; | |
482 | ||
483 | if (IGC_NOT_ALL_BITS_SET(mask->hdr.dst_port)) | |
484 | return rte_flow_error_set(error, EINVAL, | |
485 | RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, | |
486 | "UDP port mask bits must be all 0 or 1"); | |
487 | ||
488 | /* get destination port info. */ | |
489 | filter->ntuple.tuple_info.dst_port_mask = 1; | |
490 | filter->ntuple.tuple_info.dst_port = spec->hdr.dst_port; | |
491 | ||
492 | return 0; | |
493 | } | |
494 | ||
495 | /* Parse pattern type of TCP */ | |
496 | static int | |
497 | igc_parse_pattern_tcp(const struct rte_flow_item *item, | |
498 | struct igc_all_filter *filter, | |
499 | struct rte_flow_error *error) | |
500 | { | |
501 | const struct rte_flow_item_tcp *spec = item->spec; | |
502 | const struct rte_flow_item_tcp *mask = item->mask; | |
503 | struct igc_ntuple_info *tuple_info = &filter->ntuple.tuple_info; | |
504 | ||
505 | if (mask == NULL) { | |
506 | /* only n-tuple filter match this pattern */ | |
507 | IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE); | |
508 | return 0; | |
509 | } | |
510 | ||
511 | /* only n-tuple and SYN filter match this pattern */ | |
512 | IGC_SET_FILTER_MASK(filter, | |
513 | IGC_FILTER_MASK_NTUPLE | IGC_FILTER_MASK_TCP_SYN); | |
514 | ||
515 | /* only destination port and TCP flags are used */ | |
516 | if (mask->hdr.sent_seq || | |
517 | mask->hdr.recv_ack || | |
518 | mask->hdr.data_off || | |
519 | mask->hdr.rx_win || | |
520 | mask->hdr.cksum || | |
521 | mask->hdr.tcp_urp || | |
522 | mask->hdr.src_port) | |
523 | return rte_flow_error_set(error, EINVAL, | |
524 | RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, | |
525 | "TCP only support destination port and flags"); | |
526 | ||
527 | /* if destination port is used */ | |
528 | if (mask->hdr.dst_port) { | |
529 | /* only n-tuple match this pattern */ | |
530 | IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE); | |
531 | ||
532 | if (IGC_NOT_ALL_BITS_SET(mask->hdr.dst_port)) | |
533 | return rte_flow_error_set(error, EINVAL, | |
534 | RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, | |
535 | "TCP port mask bits must be all 1"); | |
536 | ||
537 | /* get destination port info. */ | |
538 | tuple_info->dst_port = spec->hdr.dst_port; | |
539 | tuple_info->dst_port_mask = 1; | |
540 | } | |
541 | ||
542 | /* if TCP flags are used */ | |
543 | if (mask->hdr.tcp_flags) { | |
544 | if (IGC_IS_ALL_BITS_SET(mask->hdr.tcp_flags)) { | |
545 | /* only n-tuple match this pattern */ | |
546 | IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE); | |
547 | ||
548 | /* get TCP flags */ | |
549 | tuple_info->tcp_flags = spec->hdr.tcp_flags; | |
550 | } else if (mask->hdr.tcp_flags == RTE_TCP_SYN_FLAG) { | |
551 | /* only TCP SYN filter match this pattern */ | |
552 | IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_TCP_SYN); | |
553 | } else { | |
554 | /* no filter match this pattern */ | |
555 | return rte_flow_error_set(error, EINVAL, | |
556 | RTE_FLOW_ERROR_TYPE_ITEM_MASK, item, | |
557 | "TCP flags can't match"); | |
558 | } | |
559 | } else { | |
560 | /* only n-tuple match this pattern */ | |
561 | IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_NTUPLE); | |
562 | } | |
563 | ||
564 | return 0; | |
565 | } | |
566 | ||
567 | static int | |
568 | igc_parse_action_queue(struct rte_eth_dev *dev, | |
569 | const struct rte_flow_action *act, | |
570 | struct igc_all_filter *filter, | |
571 | struct rte_flow_error *error) | |
572 | { | |
573 | uint16_t queue_idx; | |
574 | ||
575 | if (act->conf == NULL) | |
576 | return rte_flow_error_set(error, EINVAL, | |
577 | RTE_FLOW_ERROR_TYPE_ACTION_CONF, act, | |
578 | "NULL pointer"); | |
579 | ||
580 | /* only ether-type, n-tuple, SYN filter match the action */ | |
581 | IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_ETHER | | |
582 | IGC_FILTER_MASK_NTUPLE | IGC_FILTER_MASK_TCP_SYN); | |
583 | ||
584 | /* get queue index */ | |
585 | queue_idx = ((const struct rte_flow_action_queue *)act->conf)->index; | |
586 | ||
587 | /* check the queue index is valid */ | |
588 | if (queue_idx >= dev->data->nb_rx_queues) | |
589 | return rte_flow_error_set(error, EINVAL, | |
590 | RTE_FLOW_ERROR_TYPE_ACTION_CONF, act, | |
591 | "Queue id is invalid"); | |
592 | ||
593 | /* get queue info. */ | |
594 | filter->ethertype.queue = queue_idx; | |
595 | filter->ntuple.queue = queue_idx; | |
596 | filter->syn.queue = queue_idx; | |
597 | return 0; | |
598 | } | |
599 | ||
600 | /* Parse action of RSS */ | |
601 | static int | |
602 | igc_parse_action_rss(struct rte_eth_dev *dev, | |
603 | const struct rte_flow_action *act, | |
604 | struct igc_all_filter *filter, | |
605 | struct rte_flow_error *error) | |
606 | { | |
607 | const struct rte_flow_action_rss *rss = act->conf; | |
608 | uint32_t i; | |
609 | ||
610 | if (act->conf == NULL) | |
611 | return rte_flow_error_set(error, EINVAL, | |
612 | RTE_FLOW_ERROR_TYPE_ACTION_CONF, act, | |
613 | "NULL pointer"); | |
614 | ||
615 | /* only RSS match the action */ | |
616 | IGC_SET_FILTER_MASK(filter, IGC_FILTER_MASK_RSS); | |
617 | ||
618 | /* RSS redirect table can't be zero and can't exceed 128 */ | |
619 | if (!rss || !rss->queue_num || rss->queue_num > IGC_RSS_RDT_SIZD) | |
620 | return rte_flow_error_set(error, EINVAL, | |
621 | RTE_FLOW_ERROR_TYPE_ACTION_CONF, act, | |
622 | "No valid queues"); | |
623 | ||
624 | /* queue index can't exceed max queue index */ | |
625 | for (i = 0; i < rss->queue_num; i++) { | |
626 | if (rss->queue[i] >= dev->data->nb_rx_queues) | |
627 | return rte_flow_error_set(error, EINVAL, | |
628 | RTE_FLOW_ERROR_TYPE_ACTION_CONF, act, | |
629 | "Queue id is invalid"); | |
630 | } | |
631 | ||
632 | /* only default RSS hash function is supported */ | |
633 | if (rss->func != RTE_ETH_HASH_FUNCTION_DEFAULT) | |
634 | return rte_flow_error_set(error, ENOTSUP, | |
635 | RTE_FLOW_ERROR_TYPE_ACTION_CONF, act, | |
636 | "Only default RSS hash functions is supported"); | |
637 | ||
638 | if (rss->level) | |
639 | return rte_flow_error_set(error, ENOTSUP, | |
640 | RTE_FLOW_ERROR_TYPE_ACTION_CONF, act, | |
641 | "Only 0 RSS encapsulation level is supported"); | |
642 | ||
643 | /* check key length is valid */ | |
644 | if (rss->key_len && rss->key_len != sizeof(filter->rss.key)) | |
645 | return rte_flow_error_set(error, ENOTSUP, | |
646 | RTE_FLOW_ERROR_TYPE_ACTION_CONF, act, | |
647 | "RSS hash key must be exactly 40 bytes"); | |
648 | ||
649 | /* get RSS info. */ | |
650 | igc_rss_conf_set(&filter->rss, rss); | |
651 | return 0; | |
652 | } | |
653 | ||
654 | /** | |
655 | * Allocate a rte_flow from the heap | |
656 | * Return the pointer of the flow, or NULL for failed | |
657 | **/ | |
658 | static inline struct rte_flow * | |
659 | igc_alloc_flow(const void *filter, enum igc_filter_type type, uint inbytes) | |
660 | { | |
661 | /* allocate memory, 8 bytes boundary aligned */ | |
662 | struct rte_flow *flow = rte_malloc("igc flow filter", | |
663 | sizeof(struct rte_flow) + inbytes, 8); | |
664 | if (flow == NULL) { | |
665 | PMD_DRV_LOG(ERR, "failed to allocate memory"); | |
666 | return NULL; | |
667 | } | |
668 | ||
669 | flow->filter_type = type; | |
670 | ||
671 | /* copy filter data */ | |
672 | memcpy(flow->filter, filter, inbytes); | |
673 | return flow; | |
674 | } | |
675 | ||
676 | /* Append a rte_flow to the list */ | |
677 | static inline void | |
678 | igc_append_flow(struct igc_flow_list *list, struct rte_flow *flow) | |
679 | { | |
680 | TAILQ_INSERT_TAIL(list, flow, node); | |
681 | } | |
682 | ||
683 | /** | |
684 | * Remove the flow and free the flow buffer | |
685 | * The caller should make sure the flow is really exist in the list | |
686 | **/ | |
687 | static inline void | |
688 | igc_remove_flow(struct igc_flow_list *list, struct rte_flow *flow) | |
689 | { | |
690 | TAILQ_REMOVE(list, flow, node); | |
691 | rte_free(flow); | |
692 | } | |
693 | ||
694 | /* Check whether the flow is really in the list or not */ | |
695 | static inline bool | |
696 | igc_is_flow_in_list(struct igc_flow_list *list, struct rte_flow *flow) | |
697 | { | |
698 | struct rte_flow *it; | |
699 | ||
700 | TAILQ_FOREACH(it, list, node) { | |
701 | if (it == flow) | |
702 | return true; | |
703 | } | |
704 | ||
705 | return false; | |
706 | } | |
707 | ||
708 | /** | |
709 | * Create a flow rule. | |
710 | * Theoretically one rule can match more than one filters. | |
711 | * We will let it use the filter which it hit first. | |
712 | * So, the sequence matters. | |
713 | **/ | |
714 | static struct rte_flow * | |
715 | igc_flow_create(struct rte_eth_dev *dev, | |
716 | const struct rte_flow_attr *attr, | |
717 | const struct rte_flow_item patterns[], | |
718 | const struct rte_flow_action actions[], | |
719 | struct rte_flow_error *error) | |
720 | { | |
721 | struct rte_flow *flow = NULL; | |
722 | struct igc_all_filter filter; | |
723 | int ret; | |
724 | ||
725 | ret = igc_parse_flow(dev, attr, patterns, actions, error, &filter); | |
726 | if (ret) | |
727 | return NULL; | |
728 | ret = -ENOMEM; | |
729 | ||
730 | switch (filter.mask) { | |
731 | case IGC_FILTER_MASK_ETHER: | |
732 | flow = igc_alloc_flow(&filter.ethertype, | |
733 | IGC_FILTER_TYPE_ETHERTYPE, | |
734 | sizeof(filter.ethertype)); | |
735 | if (flow) | |
736 | ret = igc_add_ethertype_filter(dev, &filter.ethertype); | |
737 | break; | |
738 | case IGC_FILTER_MASK_NTUPLE: | |
739 | /* Check n-tuple filter is valid */ | |
740 | if (filter.ntuple.tuple_info.dst_port_mask == 0 && | |
741 | filter.ntuple.tuple_info.proto_mask == 0) { | |
742 | rte_flow_error_set(error, EINVAL, | |
743 | RTE_FLOW_ERROR_TYPE_NONE, NULL, | |
744 | "Flow can't be recognized"); | |
745 | return NULL; | |
746 | } | |
747 | ||
748 | flow = igc_alloc_flow(&filter.ntuple, IGC_FILTER_TYPE_NTUPLE, | |
749 | sizeof(filter.ntuple)); | |
750 | if (flow) | |
751 | ret = igc_add_ntuple_filter(dev, &filter.ntuple); | |
752 | break; | |
753 | case IGC_FILTER_MASK_TCP_SYN: | |
754 | flow = igc_alloc_flow(&filter.syn, IGC_FILTER_TYPE_SYN, | |
755 | sizeof(filter.syn)); | |
756 | if (flow) | |
757 | ret = igc_set_syn_filter(dev, &filter.syn); | |
758 | break; | |
759 | case IGC_FILTER_MASK_RSS: | |
760 | flow = igc_alloc_flow(&filter.rss, IGC_FILTER_TYPE_HASH, | |
761 | sizeof(filter.rss)); | |
762 | if (flow) { | |
763 | struct igc_rss_filter *rss = | |
764 | (struct igc_rss_filter *)flow->filter; | |
765 | rss->conf.key = rss->key; | |
766 | rss->conf.queue = rss->queue; | |
767 | ret = igc_add_rss_filter(dev, &filter.rss); | |
768 | } | |
769 | break; | |
770 | default: | |
771 | rte_flow_error_set(error, EINVAL, | |
772 | RTE_FLOW_ERROR_TYPE_NONE, NULL, | |
773 | "Flow can't be recognized"); | |
774 | return NULL; | |
775 | } | |
776 | ||
777 | if (ret) { | |
778 | /* check and free the memory */ | |
779 | if (flow) | |
780 | rte_free(flow); | |
781 | ||
782 | rte_flow_error_set(error, -ret, | |
783 | RTE_FLOW_ERROR_TYPE_HANDLE, NULL, | |
784 | "Failed to create flow."); | |
785 | return NULL; | |
786 | } | |
787 | ||
788 | /* append the flow to the tail of the list */ | |
789 | igc_append_flow(IGC_DEV_PRIVATE_FLOW_LIST(dev), flow); | |
790 | return flow; | |
791 | } | |
792 | ||
793 | /** | |
794 | * Check if the flow rule is supported by the device. | |
795 | * It only checks the format. Don't guarantee the rule can be programmed into | |
796 | * the HW. Because there can be no enough room for the rule. | |
797 | **/ | |
798 | static int | |
799 | igc_flow_validate(struct rte_eth_dev *dev, | |
800 | const struct rte_flow_attr *attr, | |
801 | const struct rte_flow_item patterns[], | |
802 | const struct rte_flow_action actions[], | |
803 | struct rte_flow_error *error) | |
804 | { | |
805 | struct igc_all_filter filter; | |
806 | int ret; | |
807 | ||
808 | ret = igc_parse_flow(dev, attr, patterns, actions, error, &filter); | |
809 | if (ret) | |
810 | return ret; | |
811 | ||
812 | switch (filter.mask) { | |
813 | case IGC_FILTER_MASK_NTUPLE: | |
814 | /* Check n-tuple filter is valid */ | |
815 | if (filter.ntuple.tuple_info.dst_port_mask == 0 && | |
816 | filter.ntuple.tuple_info.proto_mask == 0) | |
817 | return rte_flow_error_set(error, EINVAL, | |
818 | RTE_FLOW_ERROR_TYPE_NONE, NULL, | |
819 | "Flow can't be recognized"); | |
820 | break; | |
821 | } | |
822 | ||
823 | return 0; | |
824 | } | |
825 | ||
826 | /** | |
827 | * Disable a valid flow, the flow must be not NULL and | |
828 | * chained in the device flow list. | |
829 | **/ | |
830 | static int | |
831 | igc_disable_flow(struct rte_eth_dev *dev, struct rte_flow *flow) | |
832 | { | |
833 | int ret = 0; | |
834 | ||
835 | switch (flow->filter_type) { | |
836 | case IGC_FILTER_TYPE_ETHERTYPE: | |
837 | ret = igc_del_ethertype_filter(dev, | |
838 | (struct igc_ethertype_filter *)&flow->filter); | |
839 | break; | |
840 | case IGC_FILTER_TYPE_NTUPLE: | |
841 | ret = igc_del_ntuple_filter(dev, | |
842 | (struct igc_ntuple_filter *)&flow->filter); | |
843 | break; | |
844 | case IGC_FILTER_TYPE_SYN: | |
845 | igc_clear_syn_filter(dev); | |
846 | break; | |
847 | case IGC_FILTER_TYPE_HASH: | |
848 | ret = igc_del_rss_filter(dev); | |
849 | break; | |
850 | default: | |
851 | PMD_DRV_LOG(ERR, "Filter type (%d) not supported", | |
852 | flow->filter_type); | |
853 | ret = -EINVAL; | |
854 | } | |
855 | ||
856 | return ret; | |
857 | } | |
858 | ||
859 | /* Destroy a flow rule */ | |
860 | static int | |
861 | igc_flow_destroy(struct rte_eth_dev *dev, | |
862 | struct rte_flow *flow, | |
863 | struct rte_flow_error *error) | |
864 | { | |
865 | struct igc_flow_list *list = IGC_DEV_PRIVATE_FLOW_LIST(dev); | |
866 | int ret; | |
867 | ||
868 | if (!flow) { | |
869 | PMD_DRV_LOG(ERR, "NULL flow!"); | |
870 | return -EINVAL; | |
871 | } | |
872 | ||
873 | /* check the flow is create by IGC PMD */ | |
874 | if (!igc_is_flow_in_list(list, flow)) { | |
875 | PMD_DRV_LOG(ERR, "Flow(%p) not been found!", flow); | |
876 | return -ENOENT; | |
877 | } | |
878 | ||
879 | ret = igc_disable_flow(dev, flow); | |
880 | if (ret) | |
881 | rte_flow_error_set(error, -ret, | |
882 | RTE_FLOW_ERROR_TYPE_HANDLE, | |
883 | NULL, "Failed to destroy flow"); | |
884 | ||
885 | igc_remove_flow(list, flow); | |
886 | return ret; | |
887 | } | |
888 | ||
889 | /* Initiate device flow list header */ | |
890 | void | |
891 | igc_flow_init(struct rte_eth_dev *dev) | |
892 | { | |
893 | TAILQ_INIT(IGC_DEV_PRIVATE_FLOW_LIST(dev)); | |
894 | } | |
895 | ||
896 | /* Destroy all flow in the list and free memory */ | |
897 | int | |
898 | igc_flow_flush(struct rte_eth_dev *dev, | |
899 | __rte_unused struct rte_flow_error *error) | |
900 | { | |
901 | struct igc_flow_list *list = IGC_DEV_PRIVATE_FLOW_LIST(dev); | |
902 | struct rte_flow *flow; | |
903 | ||
904 | while ((flow = TAILQ_FIRST(list)) != NULL) { | |
905 | igc_disable_flow(dev, flow); | |
906 | igc_remove_flow(list, flow); | |
907 | } | |
908 | ||
909 | return 0; | |
910 | } | |
911 | ||
912 | const struct rte_flow_ops igc_flow_ops = { | |
913 | .validate = igc_flow_validate, | |
914 | .create = igc_flow_create, | |
915 | .destroy = igc_flow_destroy, | |
916 | .flush = igc_flow_flush, | |
917 | }; |