]> git.proxmox.com Git - ovs.git/blame - lib/netdev-offload-tc.c
Merge tag 'v2.15.0' into master-dfsg
[ovs.git] / lib / netdev-offload-tc.c
CommitLineData
18ebd48c
PB
1/*
2 * Copyright (c) 2016 Mellanox Technologies, Ltd.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#include <config.h>
ef3767f5 18
18ebd48c
PB
19#include <errno.h>
20#include <linux/if_ether.h>
ef3767f5
JS
21
22#include "dpif.h"
23#include "hash.h"
18ebd48c
PB
24#include "openvswitch/hmap.h"
25#include "openvswitch/match.h"
26#include "openvswitch/ofpbuf.h"
27#include "openvswitch/thread.h"
28#include "openvswitch/types.h"
1ae83bb2 29#include "openvswitch/util.h"
18ebd48c 30#include "openvswitch/vlog.h"
ef3767f5 31#include "netdev-linux.h"
5fc5c50f
IM
32#include "netdev-offload-provider.h"
33#include "netdev-provider.h"
18ebd48c
PB
34#include "netlink.h"
35#include "netlink-socket.h"
36#include "odp-netlink.h"
1ae83bb2 37#include "odp-util.h"
ef3767f5 38#include "tc.h"
18ebd48c
PB
39#include "unaligned.h"
40#include "util.h"
b2ae4069 41#include "dpif-provider.h"
18ebd48c 42
4f746d52 43VLOG_DEFINE_THIS_MODULE(netdev_offload_tc);
18ebd48c 44
8140a5ff
PB
45static struct vlog_rate_limit error_rl = VLOG_RATE_LIMIT_INIT(60, 5);
46
900b5bdc
PB
47static struct hmap ufid_to_tc = HMAP_INITIALIZER(&ufid_to_tc);
48static struct hmap tc_to_ufid = HMAP_INITIALIZER(&tc_to_ufid);
d00eeded 49static bool multi_mask_per_prio = false;
093c9458 50static bool block_support = false;
1ae83bb2
PB
51
52struct netlink_field {
53 int offset;
54 int flower_offset;
55 int size;
56};
57
4aa2dc04
JH
58static bool
59is_internal_port(const char *type)
60{
61 return !strcmp(type, "internal");
62}
63
608ff46a
JH
64static enum tc_qdisc_hook
65get_tc_qdisc_hook(struct netdev *netdev)
66{
67 return is_internal_port(netdev_get_type(netdev)) ? TC_EGRESS : TC_INGRESS;
68}
69
95431229 70static struct netlink_field set_flower_map[][4] = {
1ae83bb2
PB
71 [OVS_KEY_ATTR_IPV4] = {
72 { offsetof(struct ovs_key_ipv4, ipv4_src),
73 offsetof(struct tc_flower_key, ipv4.ipv4_src),
74 MEMBER_SIZEOF(struct tc_flower_key, ipv4.ipv4_src)
75 },
76 { offsetof(struct ovs_key_ipv4, ipv4_dst),
77 offsetof(struct tc_flower_key, ipv4.ipv4_dst),
78 MEMBER_SIZEOF(struct tc_flower_key, ipv4.ipv4_dst)
79 },
80 { offsetof(struct ovs_key_ipv4, ipv4_ttl),
81 offsetof(struct tc_flower_key, ipv4.rewrite_ttl),
82 MEMBER_SIZEOF(struct tc_flower_key, ipv4.rewrite_ttl)
83 },
95431229
PJV
84 { offsetof(struct ovs_key_ipv4, ipv4_tos),
85 offsetof(struct tc_flower_key, ipv4.rewrite_tos),
86 MEMBER_SIZEOF(struct tc_flower_key, ipv4.rewrite_tos)
87 },
1ae83bb2
PB
88 },
89 [OVS_KEY_ATTR_IPV6] = {
90 { offsetof(struct ovs_key_ipv6, ipv6_src),
91 offsetof(struct tc_flower_key, ipv6.ipv6_src),
92 MEMBER_SIZEOF(struct tc_flower_key, ipv6.ipv6_src)
93 },
94 { offsetof(struct ovs_key_ipv6, ipv6_dst),
95 offsetof(struct tc_flower_key, ipv6.ipv6_dst),
96 MEMBER_SIZEOF(struct tc_flower_key, ipv6.ipv6_dst)
97 },
46df7fac
EB
98 { offsetof(struct ovs_key_ipv6, ipv6_hlimit),
99 offsetof(struct tc_flower_key, ipv6.rewrite_hlimit),
100 MEMBER_SIZEOF(struct tc_flower_key, ipv6.rewrite_hlimit)
101 },
dbcb014d
PJV
102 { offsetof(struct ovs_key_ipv6, ipv6_tclass),
103 offsetof(struct tc_flower_key, ipv6.rewrite_tclass),
104 MEMBER_SIZEOF(struct tc_flower_key, ipv6.rewrite_tclass)
105 },
1ae83bb2
PB
106 },
107 [OVS_KEY_ATTR_ETHERNET] = {
108 { offsetof(struct ovs_key_ethernet, eth_src),
109 offsetof(struct tc_flower_key, src_mac),
110 MEMBER_SIZEOF(struct tc_flower_key, src_mac)
111 },
112 { offsetof(struct ovs_key_ethernet, eth_dst),
113 offsetof(struct tc_flower_key, dst_mac),
114 MEMBER_SIZEOF(struct tc_flower_key, dst_mac)
115 },
116 },
117 [OVS_KEY_ATTR_ETHERTYPE] = {
118 { 0,
119 offsetof(struct tc_flower_key, eth_type),
120 MEMBER_SIZEOF(struct tc_flower_key, eth_type)
121 },
122 },
123 [OVS_KEY_ATTR_TCP] = {
124 { offsetof(struct ovs_key_tcp, tcp_src),
125 offsetof(struct tc_flower_key, tcp_src),
126 MEMBER_SIZEOF(struct tc_flower_key, tcp_src)
127 },
128 { offsetof(struct ovs_key_tcp, tcp_dst),
129 offsetof(struct tc_flower_key, tcp_dst),
130 MEMBER_SIZEOF(struct tc_flower_key, tcp_dst)
131 },
132 },
133 [OVS_KEY_ATTR_UDP] = {
134 { offsetof(struct ovs_key_udp, udp_src),
135 offsetof(struct tc_flower_key, udp_src),
136 MEMBER_SIZEOF(struct tc_flower_key, udp_src)
137 },
138 { offsetof(struct ovs_key_udp, udp_dst),
139 offsetof(struct tc_flower_key, udp_dst),
140 MEMBER_SIZEOF(struct tc_flower_key, udp_dst)
141 },
142 },
143};
144
9116730d
PB
145static struct ovs_mutex ufid_lock = OVS_MUTEX_INITIALIZER;
146
147/**
900b5bdc
PB
148 * struct ufid_tc_data - data entry for ufid-tc hashmaps.
149 * @ufid_to_tc_node: Element in @ufid_to_tc hash table by ufid key.
150 * @tc_to_ufid_node: Element in @tc_to_ufid hash table by tcf_id key.
9116730d 151 * @ufid: ufid assigned to the flow
acdd544c 152 * @id: tc filter id (tcf_id)
9116730d
PB
153 * @netdev: netdev associated with the tc rule
154 */
155struct ufid_tc_data {
900b5bdc
PB
156 struct hmap_node ufid_to_tc_node;
157 struct hmap_node tc_to_ufid_node;
9116730d 158 ovs_u128 ufid;
acdd544c 159 struct tcf_id id;
9116730d
PB
160 struct netdev *netdev;
161};
162
9116730d 163static void
900b5bdc 164del_ufid_tc_mapping_unlocked(const ovs_u128 *ufid)
9116730d
PB
165{
166 size_t ufid_hash = hash_bytes(ufid, sizeof *ufid, 0);
167 struct ufid_tc_data *data;
168
900b5bdc 169 HMAP_FOR_EACH_WITH_HASH (data, ufid_to_tc_node, ufid_hash, &ufid_to_tc) {
9116730d
PB
170 if (ovs_u128_equals(*ufid, data->ufid)) {
171 break;
172 }
173 }
174
175 if (!data) {
9116730d
PB
176 return;
177 }
178
900b5bdc
PB
179 hmap_remove(&ufid_to_tc, &data->ufid_to_tc_node);
180 hmap_remove(&tc_to_ufid, &data->tc_to_ufid_node);
9116730d
PB
181 netdev_close(data->netdev);
182 free(data);
900b5bdc
PB
183}
184
185/* Remove matching ufid entry from ufid-tc hashmaps. */
186static void
187del_ufid_tc_mapping(const ovs_u128 *ufid)
188{
189 ovs_mutex_lock(&ufid_lock);
190 del_ufid_tc_mapping_unlocked(ufid);
9116730d
PB
191 ovs_mutex_unlock(&ufid_lock);
192}
193
262a0795
CM
194/* Wrapper function to delete filter and ufid tc mapping */
195static int
acdd544c 196del_filter_and_ufid_mapping(struct tcf_id *id, const ovs_u128 *ufid)
262a0795
CM
197{
198 int err;
199
acdd544c 200 err = tc_del_filter(id);
dd8ca104
JL
201 if (!err) {
202 del_ufid_tc_mapping(ufid);
203 }
262a0795
CM
204 return err;
205}
206
900b5bdc 207/* Add ufid entry to ufid_to_tc hashmap. */
8f283af8 208static void
acdd544c
PB
209add_ufid_tc_mapping(struct netdev *netdev, const ovs_u128 *ufid,
210 struct tcf_id *id)
9116730d 211{
9116730d 212 struct ufid_tc_data *new_data = xzalloc(sizeof *new_data);
b2ae4069
PB
213 size_t ufid_hash = hash_bytes(ufid, sizeof *ufid, 0);
214 size_t tc_hash;
215
216 tc_hash = hash_int(hash_int(id->prio, id->handle), id->ifindex);
217 tc_hash = hash_int(id->chain, tc_hash);
9116730d 218
9116730d 219 new_data->ufid = *ufid;
acdd544c 220 new_data->id = *id;
9116730d 221 new_data->netdev = netdev_ref(netdev);
9116730d
PB
222
223 ovs_mutex_lock(&ufid_lock);
900b5bdc
PB
224 hmap_insert(&ufid_to_tc, &new_data->ufid_to_tc_node, ufid_hash);
225 hmap_insert(&tc_to_ufid, &new_data->tc_to_ufid_node, tc_hash);
9116730d
PB
226 ovs_mutex_unlock(&ufid_lock);
227}
228
900b5bdc 229/* Get tc id from ufid_to_tc hashmap.
9116730d 230 *
acdd544c
PB
231 * Returns 0 if successful and fills id.
232 * Otherwise returns the error.
9116730d 233 */
8f283af8 234static int
acdd544c 235get_ufid_tc_mapping(const ovs_u128 *ufid, struct tcf_id *id)
9116730d
PB
236{
237 size_t ufid_hash = hash_bytes(ufid, sizeof *ufid, 0);
238 struct ufid_tc_data *data;
9116730d
PB
239
240 ovs_mutex_lock(&ufid_lock);
900b5bdc 241 HMAP_FOR_EACH_WITH_HASH (data, ufid_to_tc_node, ufid_hash, &ufid_to_tc) {
9116730d 242 if (ovs_u128_equals(*ufid, data->ufid)) {
acdd544c
PB
243 *id = data->id;
244 ovs_mutex_unlock(&ufid_lock);
245 return 0;
9116730d
PB
246 }
247 }
248 ovs_mutex_unlock(&ufid_lock);
249
acdd544c 250 return ENOENT;
9116730d
PB
251}
252
900b5bdc 253/* Find ufid entry in ufid_to_tc hashmap using tcf_id id.
9116730d
PB
254 * The result is saved in ufid.
255 *
256 * Returns true on success.
257 */
8f7620e6 258static bool
acdd544c 259find_ufid(struct netdev *netdev, struct tcf_id *id, ovs_u128 *ufid)
9116730d 260{
9116730d 261 struct ufid_tc_data *data;
b2ae4069
PB
262 size_t tc_hash;
263
264 tc_hash = hash_int(hash_int(id->prio, id->handle), id->ifindex);
265 tc_hash = hash_int(id->chain, tc_hash);
9116730d
PB
266
267 ovs_mutex_lock(&ufid_lock);
900b5bdc 268 HMAP_FOR_EACH_WITH_HASH (data, tc_to_ufid_node, tc_hash, &tc_to_ufid) {
acdd544c 269 if (netdev == data->netdev && is_tcf_id_eq(&data->id, id)) {
9116730d
PB
270 *ufid = data->ufid;
271 break;
272 }
273 }
274 ovs_mutex_unlock(&ufid_lock);
275
276 return (data != NULL);
277}
278
d86eea7c
PB
279struct prio_map_data {
280 struct hmap_node node;
281 struct tc_flower_key mask;
282 ovs_be16 protocol;
283 uint16_t prio;
284};
285
286/* Get free prio for tc flower
287 * If prio is already allocated for mask/eth_type combination then return it.
288 * If not assign new prio.
289 *
290 * Return prio on success or 0 if we are out of prios.
291 */
8f283af8 292static uint16_t
d86eea7c
PB
293get_prio_for_tc_flower(struct tc_flower *flower)
294{
295 static struct hmap prios = HMAP_INITIALIZER(&prios);
296 static struct ovs_mutex prios_lock = OVS_MUTEX_INITIALIZER;
e7f6ba22 297 static uint16_t last_prio = TC_RESERVED_PRIORITY_MAX;
d86eea7c 298 size_t key_len = sizeof(struct tc_flower_key);
d00eeded 299 size_t hash = hash_int((OVS_FORCE uint32_t) flower->key.eth_type, 0);
d86eea7c
PB
300 struct prio_map_data *data;
301 struct prio_map_data *new_data;
302
d00eeded
PB
303 if (!multi_mask_per_prio) {
304 hash = hash_bytes(&flower->mask, key_len, hash);
305 }
306
d86eea7c
PB
307 /* We can use the same prio for same mask/eth combination but must have
308 * different prio if not. Flower classifier will reject same prio for
d00eeded 309 * different mask combination unless multi mask per prio is supported. */
d86eea7c 310 ovs_mutex_lock(&prios_lock);
900b5bdc 311 HMAP_FOR_EACH_WITH_HASH (data, node, hash, &prios) {
d00eeded
PB
312 if ((multi_mask_per_prio
313 || !memcmp(&flower->mask, &data->mask, key_len))
d86eea7c
PB
314 && data->protocol == flower->key.eth_type) {
315 ovs_mutex_unlock(&prios_lock);
316 return data->prio;
317 }
318 }
319
320 if (last_prio == UINT16_MAX) {
321 /* last_prio can overflow if there will be many different kinds of
322 * flows which shouldn't happen organically. */
323 ovs_mutex_unlock(&prios_lock);
324 return 0;
325 }
326
327 new_data = xzalloc(sizeof *new_data);
328 memcpy(&new_data->mask, &flower->mask, key_len);
329 new_data->prio = ++last_prio;
330 new_data->protocol = flower->key.eth_type;
331 hmap_insert(&prios, &new_data->node, hash);
332 ovs_mutex_unlock(&prios_lock);
333
334 return new_data->prio;
335}
336
88dcf2aa
JH
337static uint32_t
338get_block_id_from_netdev(struct netdev *netdev)
339{
340 if (block_support) {
341 return netdev_get_block_id(netdev);
342 }
343
344 return 0;
345}
346
5fc5c50f 347static int
8140a5ff 348netdev_tc_flow_flush(struct netdev *netdev)
18ebd48c 349{
900b5bdc
PB
350 struct ufid_tc_data *data, *next;
351 int err;
8140a5ff 352
900b5bdc
PB
353 ovs_mutex_lock(&ufid_lock);
354 HMAP_FOR_EACH_SAFE (data, next, tc_to_ufid_node, &tc_to_ufid) {
355 if (data->netdev != netdev) {
356 continue;
357 }
358
359 err = tc_del_filter(&data->id);
360 if (!err) {
361 del_ufid_tc_mapping_unlocked(&data->ufid);
362 }
8140a5ff 363 }
900b5bdc 364 ovs_mutex_unlock(&ufid_lock);
8140a5ff 365
900b5bdc 366 return 0;
18ebd48c
PB
367}
368
5fc5c50f 369static int
18ebd48c 370netdev_tc_flow_dump_create(struct netdev *netdev,
19153657
VB
371 struct netdev_flow_dump **dump_out,
372 bool terse)
18ebd48c 373{
608ff46a 374 enum tc_qdisc_hook hook = get_tc_qdisc_hook(netdev);
8f7620e6 375 struct netdev_flow_dump *dump;
093c9458 376 uint32_t block_id = 0;
acdd544c
PB
377 struct tcf_id id;
378 int prio = 0;
8f7620e6
PB
379 int ifindex;
380
381 ifindex = netdev_get_ifindex(netdev);
382 if (ifindex < 0) {
0164f10c 383 VLOG_ERR_RL(&error_rl, "dump_create: failed to get ifindex for %s: %s",
8f7620e6
PB
384 netdev_get_name(netdev), ovs_strerror(-ifindex));
385 return -ifindex;
386 }
18ebd48c 387
88dcf2aa 388 block_id = get_block_id_from_netdev(netdev);
8f7620e6
PB
389 dump = xzalloc(sizeof *dump);
390 dump->nl_dump = xzalloc(sizeof *dump->nl_dump);
18ebd48c 391 dump->netdev = netdev_ref(netdev);
19153657 392 dump->terse = terse;
acdd544c
PB
393
394 id = tc_make_tcf_id(ifindex, block_id, prio, hook);
5db012c4 395 tc_dump_flower_start(&id, dump->nl_dump, terse);
18ebd48c
PB
396
397 *dump_out = dump;
398
399 return 0;
400}
401
5fc5c50f 402static int
18ebd48c
PB
403netdev_tc_flow_dump_destroy(struct netdev_flow_dump *dump)
404{
8f7620e6 405 nl_dump_done(dump->nl_dump);
18ebd48c 406 netdev_close(dump->netdev);
8f7620e6 407 free(dump->nl_dump);
18ebd48c 408 free(dump);
8f7620e6
PB
409 return 0;
410}
411
1ae83bb2
PB
412static void
413parse_flower_rewrite_to_netlink_action(struct ofpbuf *buf,
414 struct tc_flower *flower)
415{
416 char *mask = (char *) &flower->rewrite.mask;
417 char *data = (char *) &flower->rewrite.key;
418
419 for (int type = 0; type < ARRAY_SIZE(set_flower_map); type++) {
420 char *put = NULL;
421 size_t nested = 0;
422 int len = ovs_flow_key_attr_lens[type].len;
423
424 if (len <= 0) {
425 continue;
426 }
427
428 for (int j = 0; j < ARRAY_SIZE(set_flower_map[type]); j++) {
429 struct netlink_field *f = &set_flower_map[type][j];
430
431 if (!f->size) {
432 break;
433 }
434
435 if (!is_all_zeros(mask + f->flower_offset, f->size)) {
436 if (!put) {
437 nested = nl_msg_start_nested(buf,
438 OVS_ACTION_ATTR_SET_MASKED);
439 put = nl_msg_put_unspec_zero(buf, type, len * 2);
440 }
441
442 memcpy(put + f->offset, data + f->flower_offset, f->size);
443 memcpy(put + len + f->offset,
444 mask + f->flower_offset, f->size);
445 }
446 }
447
448 if (put) {
449 nl_msg_end_nested(buf, nested);
450 }
451 }
452}
453
202469aa
PJV
454static void parse_tc_flower_geneve_opts(struct tc_action *action,
455 struct ofpbuf *buf)
456{
457 int tun_opt_len = action->encap.data.present.len;
458 size_t geneve_off;
459 int idx = 0;
460
461 if (!tun_opt_len) {
462 return;
463 }
464
465 geneve_off = nl_msg_start_nested(buf, OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS);
466 while (tun_opt_len) {
467 struct geneve_opt *opt;
468
469 opt = &action->encap.data.opts.gnv[idx];
470 nl_msg_put(buf, opt, sizeof(struct geneve_opt) + opt->length * 4);
471 idx += sizeof(struct geneve_opt) / 4 + opt->length;
472 tun_opt_len -= sizeof(struct geneve_opt) + opt->length * 4;
473 }
474 nl_msg_end_nested(buf, geneve_off);
475}
476
a468645c
PJV
477static void
478flower_tun_opt_to_match(struct match *match, struct tc_flower *flower)
479{
480 struct geneve_opt *opt, *opt_mask;
481 int len, cnt = 0;
482
483 memcpy(match->flow.tunnel.metadata.opts.gnv,
484 flower->key.tunnel.metadata.opts.gnv,
485 flower->key.tunnel.metadata.present.len);
486 match->flow.tunnel.metadata.present.len =
487 flower->key.tunnel.metadata.present.len;
488 match->flow.tunnel.flags |= FLOW_TNL_F_UDPIF;
489 memcpy(match->wc.masks.tunnel.metadata.opts.gnv,
490 flower->mask.tunnel.metadata.opts.gnv,
491 flower->mask.tunnel.metadata.present.len);
492
493 len = flower->key.tunnel.metadata.present.len;
494 while (len) {
495 opt = &match->flow.tunnel.metadata.opts.gnv[cnt];
496 opt_mask = &match->wc.masks.tunnel.metadata.opts.gnv[cnt];
497
498 opt_mask->length = 0x1f;
499
500 cnt += sizeof(struct geneve_opt) / 4 + opt->length;
501 len -= sizeof(struct geneve_opt) + opt->length * 4;
502 }
503
504 match->wc.masks.tunnel.metadata.present.len =
505 flower->mask.tunnel.metadata.present.len;
506 match->wc.masks.tunnel.flags |= FLOW_TNL_F_UDPIF;
507}
508
19153657
VB
509static void
510parse_tc_flower_to_stats(struct tc_flower *flower,
511 struct dpif_flow_stats *stats)
512{
513 if (!stats) {
514 return;
515 }
516
517 memset(stats, 0, sizeof *stats);
518 stats->n_packets = get_32aligned_u64(&flower->stats.n_packets);
519 stats->n_bytes = get_32aligned_u64(&flower->stats.n_bytes);
520 stats->used = flower->lastused;
521}
522
523static void
524parse_tc_flower_to_attrs(struct tc_flower *flower,
525 struct dpif_flow_attrs *attrs)
526{
527 attrs->offloaded = (flower->offloaded_state == TC_OFFLOADED_STATE_IN_HW ||
528 flower->offloaded_state ==
529 TC_OFFLOADED_STATE_UNDEFINED);
530 attrs->dp_layer = "tc";
531 attrs->dp_extra_info = NULL;
532}
533
534static int
535parse_tc_flower_terse_to_match(struct tc_flower *flower,
536 struct match *match,
537 struct dpif_flow_stats *stats,
538 struct dpif_flow_attrs *attrs)
539{
540 match_init_catchall(match);
541
542 parse_tc_flower_to_stats(flower, stats);
543 parse_tc_flower_to_attrs(flower, attrs);
544
545 return 0;
546}
547
8f7620e6
PB
548static int
549parse_tc_flower_to_match(struct tc_flower *flower,
550 struct match *match,
551 struct nlattr **actions,
552 struct dpif_flow_stats *stats,
d63ca532 553 struct dpif_flow_attrs *attrs,
19153657
VB
554 struct ofpbuf *buf,
555 bool terse)
0c70132c 556{
8f7620e6
PB
557 size_t act_off;
558 struct tc_flower_key *key = &flower->key;
559 struct tc_flower_key *mask = &flower->mask;
560 odp_port_t outport = 0;
0c70132c
CM
561 struct tc_action *action;
562 int i;
8f7620e6 563
19153657
VB
564 if (terse) {
565 return parse_tc_flower_terse_to_match(flower, match, stats, attrs);
566 }
567
8f7620e6
PB
568 ofpbuf_clear(buf);
569
570 match_init_catchall(match);
571 match_set_dl_src_masked(match, key->src_mac, mask->src_mac);
572 match_set_dl_dst_masked(match, key->dst_mac, mask->dst_mac);
573
b5ad40a9 574 if (eth_type_vlan(key->eth_type)) {
f9885dc5
JL
575 match->flow.vlans[0].tpid = key->eth_type;
576 match->wc.masks.vlans[0].tpid = OVS_BE16_MAX;
577 match_set_dl_vlan(match, htons(key->vlan_id[0]), 0);
578 match_set_dl_vlan_pcp(match, key->vlan_prio[0], 0);
579
580 if (eth_type_vlan(key->encap_eth_type[0])) {
581 match_set_dl_vlan(match, htons(key->vlan_id[1]), 1);
582 match_set_dl_vlan_pcp(match, key->vlan_prio[1], 1);
583 match_set_dl_type(match, key->encap_eth_type[1]);
584 match->flow.vlans[1].tpid = key->encap_eth_type[0];
585 match->wc.masks.vlans[1].tpid = OVS_BE16_MAX;
586 } else {
587 match_set_dl_type(match, key->encap_eth_type[0]);
588 }
8f7620e6 589 flow_fix_vlan_tpid(&match->flow);
34b16955
PJV
590 } else if (eth_type_mpls(key->eth_type)) {
591 match->flow.mpls_lse[0] = key->mpls_lse & mask->mpls_lse;
592 match->wc.masks.mpls_lse[0] = mask->mpls_lse;
593 match_set_dl_type(match, key->encap_eth_type[0]);
a3db6e47
TZ
594 } else if (key->eth_type == htons(ETH_TYPE_ARP)) {
595 match_set_arp_sha_masked(match, key->arp.sha, mask->arp.sha);
596 match_set_arp_tha_masked(match, key->arp.tha, mask->arp.tha);
597 match_set_arp_spa_masked(match, key->arp.spa, mask->arp.spa);
598 match_set_arp_tpa_masked(match, key->arp.tpa, mask->arp.tpa);
599 match_set_arp_opcode_masked(match, key->arp.opcode,
600 mask->arp.opcode);
601 match_set_dl_type(match, key->eth_type);
8f7620e6
PB
602 } else {
603 match_set_dl_type(match, key->eth_type);
604 }
605
83a87fae
PB
606 if (is_ip_any(&match->flow)) {
607 if (key->ip_proto) {
608 match_set_nw_proto(match, key->ip_proto);
609 }
8f7620e6 610
dfa2ccdb 611 match_set_nw_tos_masked(match, key->ip_tos, mask->ip_tos);
ab7ecf26
PB
612 match_set_nw_ttl_masked(match, key->ip_ttl, mask->ip_ttl);
613
83e86606
RD
614 if (mask->flags) {
615 uint8_t flags = 0;
616 uint8_t flags_mask = 0;
617
618 if (mask->flags & TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT) {
619 if (key->flags & TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT) {
620 flags |= FLOW_NW_FRAG_ANY;
621 }
622 flags_mask |= FLOW_NW_FRAG_ANY;
623 }
624
625 if (mask->flags & TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST) {
626 if (!(key->flags & TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST)) {
627 flags |= FLOW_NW_FRAG_LATER;
628 }
629 flags_mask |= FLOW_NW_FRAG_LATER;
630 }
631
632 match_set_nw_frag_masked(match, flags, flags_mask);
633 }
634
83a87fae
PB
635 match_set_nw_src_masked(match, key->ipv4.ipv4_src, mask->ipv4.ipv4_src);
636 match_set_nw_dst_masked(match, key->ipv4.ipv4_dst, mask->ipv4.ipv4_dst);
8f7620e6 637
83a87fae
PB
638 match_set_ipv6_src_masked(match,
639 &key->ipv6.ipv6_src, &mask->ipv6.ipv6_src);
640 match_set_ipv6_dst_masked(match,
641 &key->ipv6.ipv6_dst, &mask->ipv6.ipv6_dst);
8f7620e6 642
2b1d9fa9
PB
643 if (key->ip_proto == IPPROTO_TCP) {
644 match_set_tp_dst_masked(match, key->tcp_dst, mask->tcp_dst);
645 match_set_tp_src_masked(match, key->tcp_src, mask->tcp_src);
f2698407 646 match_set_tcp_flags_masked(match, key->tcp_flags, mask->tcp_flags);
2b1d9fa9
PB
647 } else if (key->ip_proto == IPPROTO_UDP) {
648 match_set_tp_dst_masked(match, key->udp_dst, mask->udp_dst);
649 match_set_tp_src_masked(match, key->udp_src, mask->udp_src);
a238dbb5
RD
650 } else if (key->ip_proto == IPPROTO_SCTP) {
651 match_set_tp_dst_masked(match, key->sctp_dst, mask->sctp_dst);
652 match_set_tp_src_masked(match, key->sctp_src, mask->sctp_src);
2b1d9fa9 653 }
576126a9
PB
654
655 if (mask->ct_state) {
656 uint8_t ct_statev = 0, ct_statem = 0;
657
658 if (mask->ct_state & TCA_FLOWER_KEY_CT_FLAGS_NEW) {
659 if (key->ct_state & TCA_FLOWER_KEY_CT_FLAGS_NEW) {
660 ct_statev |= OVS_CS_F_NEW;
661 }
662 ct_statem |= OVS_CS_F_NEW;
663 }
664
665 if (mask->ct_state & TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED) {
666 if (key->ct_state & TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED) {
667 ct_statev |= OVS_CS_F_ESTABLISHED;
668 }
669 ct_statem |= OVS_CS_F_ESTABLISHED;
670 }
671
672 if (mask->ct_state & TCA_FLOWER_KEY_CT_FLAGS_TRACKED) {
673 if (key->ct_state & TCA_FLOWER_KEY_CT_FLAGS_TRACKED) {
674 ct_statev |= OVS_CS_F_TRACKED;
675 }
676 ct_statem |= OVS_CS_F_TRACKED;
677 }
678
679 match_set_ct_state_masked(match, ct_statev, ct_statem);
680 }
681
682 match_set_ct_zone_masked(match, key->ct_zone, mask->ct_zone);
9221c721
PB
683 match_set_ct_mark_masked(match, key->ct_mark, mask->ct_mark);
684 match_set_ct_label_masked(match, key->ct_label, mask->ct_label);
2b1d9fa9 685 }
8f7620e6 686
105e8179 687 if (flower->tunnel) {
0227bf09
AN
688 if (flower->mask.tunnel.id) {
689 match_set_tun_id(match, flower->key.tunnel.id);
36e50679 690 match->flow.tunnel.flags |= FLOW_TNL_F_KEY;
0227bf09 691 }
3f82ac1f
TZ
692 if (flower->mask.tunnel.ipv4.ipv4_dst ||
693 flower->mask.tunnel.ipv4.ipv4_src) {
5f568d04
TZ
694 match_set_tun_dst_masked(match,
695 flower->key.tunnel.ipv4.ipv4_dst,
696 flower->mask.tunnel.ipv4.ipv4_dst);
697 match_set_tun_src_masked(match,
698 flower->key.tunnel.ipv4.ipv4_src,
699 flower->mask.tunnel.ipv4.ipv4_src);
3f82ac1f
TZ
700 } else if (ipv6_addr_is_set(&flower->mask.tunnel.ipv6.ipv6_dst) ||
701 ipv6_addr_is_set(&flower->mask.tunnel.ipv6.ipv6_src)) {
5f568d04
TZ
702 match_set_tun_ipv6_dst_masked(match,
703 &flower->key.tunnel.ipv6.ipv6_dst,
704 &flower->mask.tunnel.ipv6.ipv6_dst);
705 match_set_tun_ipv6_src_masked(match,
706 &flower->key.tunnel.ipv6.ipv6_src,
707 &flower->mask.tunnel.ipv6.ipv6_src);
8f7620e6 708 }
105e8179 709 if (flower->key.tunnel.tos) {
49a7961f
OG
710 match_set_tun_tos_masked(match, flower->key.tunnel.tos,
711 flower->mask.tunnel.tos);
dd83253e 712 }
105e8179 713 if (flower->key.tunnel.ttl) {
49a7961f
OG
714 match_set_tun_ttl_masked(match, flower->key.tunnel.ttl,
715 flower->mask.tunnel.ttl);
dd83253e 716 }
1fe42975
RD
717 if (flower->key.tunnel.tp_dst) {
718 match_set_tun_tp_dst(match, flower->key.tunnel.tp_dst);
8f7620e6 719 }
a468645c
PJV
720 if (flower->key.tunnel.metadata.present.len) {
721 flower_tun_opt_to_match(match, flower);
722 }
8f7620e6
PB
723 }
724
725 act_off = nl_msg_start_nested(buf, OVS_FLOW_ATTR_ACTIONS);
726 {
0c70132c
CM
727 action = flower->actions;
728 for (i = 0; i < flower->action_count; i++, action++) {
729 switch (action->type) {
730 case TC_ACT_VLAN_POP: {
731 nl_msg_put_flag(buf, OVS_ACTION_ATTR_POP_VLAN);
8f7620e6 732 }
0c70132c
CM
733 break;
734 case TC_ACT_VLAN_PUSH: {
735 struct ovs_action_push_vlan *push;
736
737 push = nl_msg_put_unspec_zero(buf, OVS_ACTION_ATTR_PUSH_VLAN,
738 sizeof *push);
b5ad40a9 739 push->vlan_tpid = action->vlan.vlan_push_tpid;
0c70132c
CM
740 push->vlan_tci = htons(action->vlan.vlan_push_id
741 | (action->vlan.vlan_push_prio << 13)
742 | VLAN_CFI);
8f7620e6 743 }
0c70132c 744 break;
55412eac
JH
745 case TC_ACT_MPLS_POP: {
746 nl_msg_put_be16(buf, OVS_ACTION_ATTR_POP_MPLS,
747 action->mpls.proto);
748 }
749 break;
283dcf85
JH
750 case TC_ACT_MPLS_PUSH: {
751 struct ovs_action_push_mpls *push;
752 ovs_be32 mpls_lse = 0;
753
754 flow_set_mpls_lse_label(&mpls_lse, action->mpls.label);
755 flow_set_mpls_lse_tc(&mpls_lse, action->mpls.tc);
756 flow_set_mpls_lse_ttl(&mpls_lse, action->mpls.ttl);
757 flow_set_mpls_lse_bos(&mpls_lse, action->mpls.bos);
758
759 push = nl_msg_put_unspec_zero(buf, OVS_ACTION_ATTR_PUSH_MPLS,
760 sizeof *push);
761 push->mpls_ethertype = action->mpls.proto;
762 push->mpls_lse = mpls_lse;
763 }
764 break;
a8f005cf
JH
765 case TC_ACT_MPLS_SET: {
766 size_t set_offset = nl_msg_start_nested(buf,
767 OVS_ACTION_ATTR_SET);
768 struct ovs_key_mpls *set_mpls;
769 ovs_be32 mpls_lse = 0;
770
771 flow_set_mpls_lse_label(&mpls_lse, action->mpls.label);
772 flow_set_mpls_lse_tc(&mpls_lse, action->mpls.tc);
773 flow_set_mpls_lse_ttl(&mpls_lse, action->mpls.ttl);
774 flow_set_mpls_lse_bos(&mpls_lse, action->mpls.bos);
775
776 set_mpls = nl_msg_put_unspec_zero(buf, OVS_KEY_ATTR_MPLS,
777 sizeof *set_mpls);
778 set_mpls->mpls_lse = mpls_lse;
779 nl_msg_end_nested(buf, set_offset);
780 }
781 break;
0c70132c
CM
782 case TC_ACT_PEDIT: {
783 parse_flower_rewrite_to_netlink_action(buf, flower);
8f7620e6 784 }
0c70132c
CM
785 break;
786 case TC_ACT_ENCAP: {
787 size_t set_offset = nl_msg_start_nested(buf, OVS_ACTION_ATTR_SET);
788 size_t tunnel_offset =
789 nl_msg_start_nested(buf, OVS_KEY_ATTR_TUNNEL);
790
0227bf09
AN
791 if (action->encap.id_present) {
792 nl_msg_put_be64(buf, OVS_TUNNEL_KEY_ATTR_ID, action->encap.id);
793 }
0c70132c
CM
794 if (action->encap.ipv4.ipv4_src) {
795 nl_msg_put_be32(buf, OVS_TUNNEL_KEY_ATTR_IPV4_SRC,
796 action->encap.ipv4.ipv4_src);
797 }
798 if (action->encap.ipv4.ipv4_dst) {
799 nl_msg_put_be32(buf, OVS_TUNNEL_KEY_ATTR_IPV4_DST,
800 action->encap.ipv4.ipv4_dst);
801 }
4f4be08e 802 if (ipv6_addr_is_set(&action->encap.ipv6.ipv6_src)) {
0c70132c
CM
803 nl_msg_put_in6_addr(buf, OVS_TUNNEL_KEY_ATTR_IPV6_SRC,
804 &action->encap.ipv6.ipv6_src);
805 }
4f4be08e 806 if (ipv6_addr_is_set(&action->encap.ipv6.ipv6_dst)) {
0c70132c
CM
807 nl_msg_put_in6_addr(buf, OVS_TUNNEL_KEY_ATTR_IPV6_DST,
808 &action->encap.ipv6.ipv6_dst);
809 }
4b12e454
OG
810 if (action->encap.tos) {
811 nl_msg_put_u8(buf, OVS_TUNNEL_KEY_ATTR_TOS,
812 action->encap.tos);
813 }
814 if (action->encap.ttl) {
815 nl_msg_put_u8(buf, OVS_TUNNEL_KEY_ATTR_TTL,
816 action->encap.ttl);
817 }
e48f49e0
EB
818 if (action->encap.tp_dst) {
819 nl_msg_put_be16(buf, OVS_TUNNEL_KEY_ATTR_TP_DST,
820 action->encap.tp_dst);
821 }
d9677a1f
EB
822 if (!action->encap.no_csum) {
823 nl_msg_put_u8(buf, OVS_TUNNEL_KEY_ATTR_CSUM,
824 !action->encap.no_csum);
825 }
8f7620e6 826
202469aa 827 parse_tc_flower_geneve_opts(action, buf);
0c70132c
CM
828 nl_msg_end_nested(buf, tunnel_offset);
829 nl_msg_end_nested(buf, set_offset);
830 }
831 break;
832 case TC_ACT_OUTPUT: {
4aa2dc04
JH
833 if (action->out.ifindex_out) {
834 outport =
835 netdev_ifindex_to_odp_port(action->out.ifindex_out);
0c70132c
CM
836 if (!outport) {
837 return ENOENT;
838 }
839 }
840 nl_msg_put_u32(buf, OVS_ACTION_ATTR_OUTPUT, odp_to_u32(outport));
841 }
842 break;
576126a9
PB
843 case TC_ACT_CT: {
844 size_t ct_offset;
845
846 if (action->ct.clear) {
847 nl_msg_put_flag(buf, OVS_ACTION_ATTR_CT_CLEAR);
848 break;
849 }
850
851 ct_offset = nl_msg_start_nested(buf, OVS_ACTION_ATTR_CT);
852
853 if (action->ct.commit) {
854 nl_msg_put_flag(buf, OVS_CT_ATTR_COMMIT);
855 }
856
857 if (action->ct.zone) {
858 nl_msg_put_u16(buf, OVS_CT_ATTR_ZONE, action->ct.zone);
859 }
860
9221c721
PB
861 if (action->ct.mark_mask) {
862 uint32_t mark_and_mask[2] = { action->ct.mark,
863 action->ct.mark_mask };
864 nl_msg_put_unspec(buf, OVS_CT_ATTR_MARK, &mark_and_mask,
865 sizeof mark_and_mask);
866 }
867
868 if (!ovs_u128_is_zero(action->ct.label_mask)) {
869 struct {
870 ovs_u128 key;
871 ovs_u128 mask;
872 } *ct_label;
873
874 ct_label = nl_msg_put_unspec_uninit(buf,
875 OVS_CT_ATTR_LABELS,
876 sizeof *ct_label);
877 ct_label->key = action->ct.label;
878 ct_label->mask = action->ct.label_mask;
879 }
880
2bf6ffb7
PB
881 if (action->ct.nat_type) {
882 size_t nat_offset = nl_msg_start_nested(buf,
883 OVS_CT_ATTR_NAT);
884
885 if (action->ct.nat_type == TC_NAT_SRC) {
886 nl_msg_put_flag(buf, OVS_NAT_ATTR_SRC);
887 } else if (action->ct.nat_type == TC_NAT_DST) {
888 nl_msg_put_flag(buf, OVS_NAT_ATTR_DST);
889 }
890
891 if (action->ct.range.ip_family == AF_INET) {
892 nl_msg_put_be32(buf, OVS_NAT_ATTR_IP_MIN,
893 action->ct.range.ipv4.min);
894 nl_msg_put_be32(buf, OVS_NAT_ATTR_IP_MAX,
895 action->ct.range.ipv4.max);
896 } else if (action->ct.range.ip_family == AF_INET6) {
897 nl_msg_put_in6_addr(buf, OVS_NAT_ATTR_IP_MIN,
898 &action->ct.range.ipv6.min);
899 nl_msg_put_in6_addr(buf, OVS_NAT_ATTR_IP_MAX,
900 &action->ct.range.ipv6.max);
901 }
902
903 if (action->ct.range.port.min) {
904 nl_msg_put_u16(buf, OVS_NAT_ATTR_PROTO_MIN,
905 ntohs(action->ct.range.port.min));
906 if (action->ct.range.port.max) {
907 nl_msg_put_u16(buf, OVS_NAT_ATTR_PROTO_MAX,
908 ntohs(action->ct.range.port.max));
909 }
910 }
911
912 nl_msg_end_nested(buf, nat_offset);
913 }
914
576126a9
PB
915 nl_msg_end_nested(buf, ct_offset);
916 }
917 break;
b2ae4069
PB
918 case TC_ACT_GOTO: {
919 nl_msg_put_u32(buf, OVS_ACTION_ATTR_RECIRC, action->chain);
920 }
921 break;
0c70132c 922 }
8f7620e6 923 }
8f7620e6
PB
924 }
925 nl_msg_end_nested(buf, act_off);
926
927 *actions = ofpbuf_at_assert(buf, act_off, sizeof(struct nlattr));
928
19153657
VB
929 parse_tc_flower_to_stats(flower, stats);
930 parse_tc_flower_to_attrs(flower, attrs);
d63ca532 931
18ebd48c
PB
932 return 0;
933}
934
5fc5c50f 935static bool
8f7620e6
PB
936netdev_tc_flow_dump_next(struct netdev_flow_dump *dump,
937 struct match *match,
938 struct nlattr **actions,
939 struct dpif_flow_stats *stats,
d63ca532 940 struct dpif_flow_attrs *attrs,
8f7620e6
PB
941 ovs_u128 *ufid,
942 struct ofpbuf *rbuffer,
943 struct ofpbuf *wbuffer)
18ebd48c 944{
acdd544c 945 struct netdev *netdev = dump->netdev;
8f7620e6 946 struct ofpbuf nl_flow;
acdd544c
PB
947 struct tcf_id id;
948
949 id = tc_make_tcf_id(netdev_get_ifindex(netdev),
950 get_block_id_from_netdev(netdev),
951 0, /* prio */
952 get_tc_qdisc_hook(netdev));
8f7620e6
PB
953
954 while (nl_dump_next(dump->nl_dump, &nl_flow, rbuffer)) {
955 struct tc_flower flower;
8f7620e6 956
5db012c4 957 if (parse_netlink_to_tc_flower(&nl_flow, &id, &flower, dump->terse)) {
8f7620e6
PB
958 continue;
959 }
960
d63ca532 961 if (parse_tc_flower_to_match(&flower, match, actions, stats, attrs,
19153657 962 wbuffer, dump->terse)) {
8f7620e6
PB
963 continue;
964 }
965
966 if (flower.act_cookie.len) {
967 *ufid = *((ovs_u128 *) flower.act_cookie.data);
acdd544c 968 } else if (!find_ufid(netdev, &id, ufid)) {
8f7620e6
PB
969 continue;
970 }
971
972 match->wc.masks.in_port.odp_port = u32_to_odp(UINT32_MAX);
973 match->flow.in_port.odp_port = dump->port;
b2ae4069 974 match_set_recirc_id(match, id.chain);
8f7620e6
PB
975
976 return true;
977 }
978
18ebd48c
PB
979 return false;
980}
981
a8f005cf
JH
982static int
983parse_mpls_set_action(struct tc_flower *flower, struct tc_action *action,
984 const struct nlattr *set)
985{
986 const struct ovs_key_mpls *mpls_set = nl_attr_get(set);
987
988 action->mpls.label = mpls_lse_to_label(mpls_set->mpls_lse);
989 action->mpls.tc = mpls_lse_to_tc(mpls_set->mpls_lse);
990 action->mpls.ttl = mpls_lse_to_ttl(mpls_set->mpls_lse);
991 action->mpls.bos = mpls_lse_to_bos(mpls_set->mpls_lse);
992 action->type = TC_ACT_MPLS_SET;
993 flower->action_count++;
994
995 return 0;
996}
997
2bf6ffb7
PB
998static int
999parse_put_flow_nat_action(struct tc_action *action,
1000 const struct nlattr *nat,
1001 size_t nat_len)
1002{
1003 const struct nlattr *nat_attr;
1004 size_t nat_left;
1005
1006 action->ct.nat_type = TC_NAT_RESTORE;
1007 NL_ATTR_FOR_EACH_UNSAFE (nat_attr, nat_left, nat, nat_len) {
1008 switch (nl_attr_type(nat_attr)) {
1009 case OVS_NAT_ATTR_SRC: {
1010 action->ct.nat_type = TC_NAT_SRC;
1011 };
1012 break;
1013 case OVS_NAT_ATTR_DST: {
1014 action->ct.nat_type = TC_NAT_DST;
1015 };
1016 break;
1017 case OVS_NAT_ATTR_IP_MIN: {
1018 if (nl_attr_get_size(nat_attr) == sizeof(ovs_be32)) {
1019 ovs_be32 addr = nl_attr_get_be32(nat_attr);
1020
1021 action->ct.range.ipv4.min = addr;
1022 action->ct.range.ip_family = AF_INET;
1023 } else {
1024 struct in6_addr addr = nl_attr_get_in6_addr(nat_attr);
1025
1026 action->ct.range.ipv6.min = addr;
1027 action->ct.range.ip_family = AF_INET6;
1028 }
1029 };
1030 break;
1031 case OVS_NAT_ATTR_IP_MAX: {
1032 if (nl_attr_get_size(nat_attr) == sizeof(ovs_be32)) {
1033 ovs_be32 addr = nl_attr_get_be32(nat_attr);
1034
1035 action->ct.range.ipv4.max = addr;
1036 action->ct.range.ip_family = AF_INET;
1037 } else {
1038 struct in6_addr addr = nl_attr_get_in6_addr(nat_attr);
1039
1040 action->ct.range.ipv6.max = addr;
1041 action->ct.range.ip_family = AF_INET6;
1042 }
1043 };
1044 break;
1045 case OVS_NAT_ATTR_PROTO_MIN: {
1046 action->ct.range.port.min = htons(nl_attr_get_u16(nat_attr));
1047 };
1048 break;
1049 case OVS_NAT_ATTR_PROTO_MAX: {
1050 action->ct.range.port.max = htons(nl_attr_get_u16(nat_attr));
1051 };
1052 break;
1053 }
1054 }
1055 return 0;
1056}
1057
576126a9
PB
1058static int
1059parse_put_flow_ct_action(struct tc_flower *flower,
1060 struct tc_action *action,
1061 const struct nlattr *ct,
1062 size_t ct_len)
1063{
1064 const struct nlattr *ct_attr;
1065 size_t ct_left;
2bf6ffb7 1066 int err;
576126a9
PB
1067
1068 NL_ATTR_FOR_EACH_UNSAFE (ct_attr, ct_left, ct, ct_len) {
1069 switch (nl_attr_type(ct_attr)) {
1070 case OVS_CT_ATTR_COMMIT: {
1071 action->ct.commit = true;
1072 }
1073 break;
1074 case OVS_CT_ATTR_ZONE: {
1075 action->ct.zone = nl_attr_get_u16(ct_attr);
1076 }
1077 break;
2bf6ffb7
PB
1078 case OVS_CT_ATTR_NAT: {
1079 const struct nlattr *nat = nl_attr_get(ct_attr);
1080 const size_t nat_len = nl_attr_get_size(ct_attr);
1081
1082 err = parse_put_flow_nat_action(action, nat, nat_len);
1083 if (err) {
1084 return err;
1085 }
1086 }
1087 break;
9221c721
PB
1088 case OVS_CT_ATTR_MARK: {
1089 const struct {
1090 uint32_t key;
1091 uint32_t mask;
1092 } *ct_mark;
1093
1094 ct_mark = nl_attr_get_unspec(ct_attr, sizeof *ct_mark);
1095 action->ct.mark = ct_mark->key;
1096 action->ct.mark_mask = ct_mark->mask;
1097 }
1098 break;
1099 case OVS_CT_ATTR_LABELS: {
1100 const struct {
1101 ovs_u128 key;
1102 ovs_u128 mask;
1103 } *ct_label;
1104
1105 ct_label = nl_attr_get_unspec(ct_attr, sizeof *ct_label);
1106 action->ct.label = ct_label->key;
1107 action->ct.label_mask = ct_label->mask;
1108 }
1109 break;
576126a9
PB
1110 }
1111 }
1112
1113 action->type = TC_ACT_CT;
1114 flower->action_count++;
1115 return 0;
1116}
1117
1ae83bb2
PB
1118static int
1119parse_put_flow_set_masked_action(struct tc_flower *flower,
0c70132c 1120 struct tc_action *action,
1ae83bb2
PB
1121 const struct nlattr *set,
1122 size_t set_len,
1123 bool hasmask)
1124{
1125 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
40f58368
BP
1126 uint64_t set_stub[1024 / 8];
1127 struct ofpbuf set_buf = OFPBUF_STUB_INITIALIZER(set_stub);
1128 char *set_data, *set_mask;
1ae83bb2
PB
1129 char *key = (char *) &flower->rewrite.key;
1130 char *mask = (char *) &flower->rewrite.mask;
1131 const struct nlattr *attr;
1132 int i, j, type;
1133 size_t size;
1134
1135 /* copy so we can set attr mask to 0 for used ovs key struct members */
40f58368 1136 attr = ofpbuf_put(&set_buf, set, set_len);
1ae83bb2
PB
1137
1138 type = nl_attr_type(attr);
1139 size = nl_attr_get_size(attr) / 2;
1140 set_data = CONST_CAST(char *, nl_attr_get(attr));
1141 set_mask = set_data + size;
1142
1143 if (type >= ARRAY_SIZE(set_flower_map)
1144 || !set_flower_map[type][0].size) {
1145 VLOG_DBG_RL(&rl, "unsupported set action type: %d", type);
40f58368 1146 ofpbuf_uninit(&set_buf);
1ae83bb2
PB
1147 return EOPNOTSUPP;
1148 }
1149
1150 for (i = 0; i < ARRAY_SIZE(set_flower_map[type]); i++) {
1151 struct netlink_field *f = &set_flower_map[type][i];
1152
1153 if (!f->size) {
1154 break;
1155 }
1156
1157 /* copy masked value */
1158 for (j = 0; j < f->size; j++) {
1159 char maskval = hasmask ? set_mask[f->offset + j] : 0xFF;
1160
1161 key[f->flower_offset + j] = maskval & set_data[f->offset + j];
1162 mask[f->flower_offset + j] = maskval;
1163
1164 }
1165
1166 /* set its mask to 0 to show it's been used. */
1167 if (hasmask) {
1168 memset(set_mask + f->offset, 0, f->size);
1169 }
1170 }
1171
1172 if (!is_all_zeros(&flower->rewrite, sizeof flower->rewrite)) {
0c70132c
CM
1173 if (flower->rewrite.rewrite == false) {
1174 flower->rewrite.rewrite = true;
1175 action->type = TC_ACT_PEDIT;
1176 flower->action_count++;
1177 }
1ae83bb2
PB
1178 }
1179
1180 if (hasmask && !is_all_zeros(set_mask, size)) {
1181 VLOG_DBG_RL(&rl, "unsupported sub attribute of set action type %d",
1182 type);
40f58368 1183 ofpbuf_uninit(&set_buf);
1ae83bb2
PB
1184 return EOPNOTSUPP;
1185 }
1186
40f58368 1187 ofpbuf_uninit(&set_buf);
1ae83bb2
PB
1188 return 0;
1189}
1190
8f283af8 1191static int
0c70132c
CM
1192parse_put_flow_set_action(struct tc_flower *flower, struct tc_action *action,
1193 const struct nlattr *set, size_t set_len)
8f283af8 1194{
518bbe99
PB
1195 const struct nlattr *tunnel;
1196 const struct nlattr *tun_attr;
1197 size_t tun_left, tunnel_len;
1198
a8f005cf
JH
1199 if (nl_attr_type(set) == OVS_KEY_ATTR_MPLS) {
1200 return parse_mpls_set_action(flower, action, set);
1201 }
1202
518bbe99 1203 if (nl_attr_type(set) != OVS_KEY_ATTR_TUNNEL) {
0c70132c
CM
1204 return parse_put_flow_set_masked_action(flower, action, set,
1205 set_len, false);
518bbe99
PB
1206 }
1207
1208 tunnel = nl_attr_get(set);
1209 tunnel_len = nl_attr_get_size(set);
1210
0c70132c 1211 action->type = TC_ACT_ENCAP;
0227bf09 1212 action->encap.id_present = false;
0c70132c 1213 flower->action_count++;
518bbe99
PB
1214 NL_ATTR_FOR_EACH_UNSAFE(tun_attr, tun_left, tunnel, tunnel_len) {
1215 switch (nl_attr_type(tun_attr)) {
1216 case OVS_TUNNEL_KEY_ATTR_ID: {
0c70132c 1217 action->encap.id = nl_attr_get_be64(tun_attr);
0227bf09 1218 action->encap.id_present = true;
518bbe99
PB
1219 }
1220 break;
1221 case OVS_TUNNEL_KEY_ATTR_IPV4_SRC: {
0c70132c 1222 action->encap.ipv4.ipv4_src = nl_attr_get_be32(tun_attr);
518bbe99
PB
1223 }
1224 break;
1225 case OVS_TUNNEL_KEY_ATTR_IPV4_DST: {
0c70132c 1226 action->encap.ipv4.ipv4_dst = nl_attr_get_be32(tun_attr);
518bbe99
PB
1227 }
1228 break;
4b12e454
OG
1229 case OVS_TUNNEL_KEY_ATTR_TOS: {
1230 action->encap.tos = nl_attr_get_u8(tun_attr);
1231 }
1232 break;
1233 case OVS_TUNNEL_KEY_ATTR_TTL: {
1234 action->encap.ttl = nl_attr_get_u8(tun_attr);
1235 }
1236 break;
518bbe99 1237 case OVS_TUNNEL_KEY_ATTR_IPV6_SRC: {
0c70132c 1238 action->encap.ipv6.ipv6_src =
518bbe99
PB
1239 nl_attr_get_in6_addr(tun_attr);
1240 }
1241 break;
1242 case OVS_TUNNEL_KEY_ATTR_IPV6_DST: {
0c70132c 1243 action->encap.ipv6.ipv6_dst =
518bbe99
PB
1244 nl_attr_get_in6_addr(tun_attr);
1245 }
1246 break;
1247 case OVS_TUNNEL_KEY_ATTR_TP_SRC: {
0c70132c 1248 action->encap.tp_src = nl_attr_get_be16(tun_attr);
518bbe99
PB
1249 }
1250 break;
1251 case OVS_TUNNEL_KEY_ATTR_TP_DST: {
0c70132c 1252 action->encap.tp_dst = nl_attr_get_be16(tun_attr);
518bbe99
PB
1253 }
1254 break;
202469aa
PJV
1255 case OVS_TUNNEL_KEY_ATTR_GENEVE_OPTS: {
1256 memcpy(action->encap.data.opts.gnv, nl_attr_get(tun_attr),
1257 nl_attr_get_size(tun_attr));
1258 action->encap.data.present.len = nl_attr_get_size(tun_attr);
1259 }
1260 break;
8f283af8
PB
1261 }
1262 }
518bbe99 1263
8f283af8
PB
1264 return 0;
1265}
1266
1267static int
1268test_key_and_mask(struct match *match)
1269{
1270 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
1271 const struct flow *key = &match->flow;
1272 struct flow *mask = &match->wc.masks;
1273
1274 if (mask->pkt_mark) {
1275 VLOG_DBG_RL(&rl, "offloading attribute pkt_mark isn't supported");
1276 return EOPNOTSUPP;
1277 }
1278
8f283af8
PB
1279 if (mask->dp_hash) {
1280 VLOG_DBG_RL(&rl, "offloading attribute dp_hash isn't supported");
1281 return EOPNOTSUPP;
1282 }
1283
1284 if (mask->conj_id) {
1285 VLOG_DBG_RL(&rl, "offloading attribute conj_id isn't supported");
1286 return EOPNOTSUPP;
1287 }
1288
1289 if (mask->skb_priority) {
1290 VLOG_DBG_RL(&rl, "offloading attribute skb_priority isn't supported");
1291 return EOPNOTSUPP;
1292 }
1293
1294 if (mask->actset_output) {
1295 VLOG_DBG_RL(&rl,
1296 "offloading attribute actset_output isn't supported");
1297 return EOPNOTSUPP;
1298 }
1299
8f283af8
PB
1300 if (mask->packet_type && key->packet_type) {
1301 VLOG_DBG_RL(&rl, "offloading attribute packet_type isn't supported");
1302 return EOPNOTSUPP;
1303 }
1304 mask->packet_type = 0;
1305
8f283af8
PB
1306 for (int i = 0; i < FLOW_N_REGS; i++) {
1307 if (mask->regs[i]) {
1308 VLOG_DBG_RL(&rl,
1309 "offloading attribute regs[%d] isn't supported", i);
1310 return EOPNOTSUPP;
1311 }
1312 }
1313
1314 if (mask->metadata) {
1315 VLOG_DBG_RL(&rl, "offloading attribute metadata isn't supported");
1316 return EOPNOTSUPP;
1317 }
1318
1319 if (mask->nw_tos) {
1320 VLOG_DBG_RL(&rl, "offloading attribute nw_tos isn't supported");
1321 return EOPNOTSUPP;
1322 }
1323
34b16955 1324 for (int i = 1; i < FLOW_MAX_MPLS_LABELS; i++) {
8f283af8 1325 if (mask->mpls_lse[i]) {
34b16955 1326 VLOG_DBG_RL(&rl, "offloading multiple mpls_lses isn't supported");
8f283af8
PB
1327 return EOPNOTSUPP;
1328 }
1329 }
1330
1331 if (key->dl_type == htons(ETH_TYPE_IP) &&
1332 key->nw_proto == IPPROTO_ICMP) {
1333 if (mask->tp_src) {
1334 VLOG_DBG_RL(&rl,
1335 "offloading attribute icmp_type isn't supported");
1336 return EOPNOTSUPP;
1337 }
1338 if (mask->tp_dst) {
1339 VLOG_DBG_RL(&rl,
1340 "offloading attribute icmp_code isn't supported");
1341 return EOPNOTSUPP;
1342 }
1343 } else if (key->dl_type == htons(ETH_TYPE_IP) &&
1344 key->nw_proto == IPPROTO_IGMP) {
1345 if (mask->tp_src) {
1346 VLOG_DBG_RL(&rl,
1347 "offloading attribute igmp_type isn't supported");
1348 return EOPNOTSUPP;
1349 }
1350 if (mask->tp_dst) {
1351 VLOG_DBG_RL(&rl,
1352 "offloading attribute igmp_code isn't supported");
1353 return EOPNOTSUPP;
1354 }
1355 } else if (key->dl_type == htons(ETH_TYPE_IPV6) &&
1356 key->nw_proto == IPPROTO_ICMPV6) {
1357 if (mask->tp_src) {
1358 VLOG_DBG_RL(&rl,
b1307a45 1359 "offloading attribute icmpv6_type isn't supported");
8f283af8
PB
1360 return EOPNOTSUPP;
1361 }
1362 if (mask->tp_dst) {
1363 VLOG_DBG_RL(&rl,
b1307a45 1364 "offloading attribute icmpv6_code isn't supported");
8f283af8
PB
1365 return EOPNOTSUPP;
1366 }
662a6fd4
LP
1367 } else if (key->dl_type == htons(OFP_DL_TYPE_NOT_ETH_TYPE)) {
1368 VLOG_DBG_RL(&rl,
1369 "offloading of non-ethernet packets isn't supported");
1370 return EOPNOTSUPP;
8f283af8 1371 }
8f283af8
PB
1372
1373 if (!is_all_zeros(mask, sizeof *mask)) {
1374 VLOG_DBG_RL(&rl, "offloading isn't supported, unknown attribute");
1375 return EOPNOTSUPP;
1376 }
1377
1378 return 0;
1379}
1380
a468645c
PJV
1381static void
1382flower_match_to_tun_opt(struct tc_flower *flower, const struct flow_tnl *tnl,
1383 const struct flow_tnl *tnl_mask)
1384{
1385 struct geneve_opt *opt, *opt_mask;
1386 int len, cnt = 0;
1387
1388 memcpy(flower->key.tunnel.metadata.opts.gnv, tnl->metadata.opts.gnv,
1389 tnl->metadata.present.len);
1390 flower->key.tunnel.metadata.present.len = tnl->metadata.present.len;
1391
1392 memcpy(flower->mask.tunnel.metadata.opts.gnv, tnl_mask->metadata.opts.gnv,
1393 tnl->metadata.present.len);
1394
1395 len = flower->key.tunnel.metadata.present.len;
1396 while (len) {
1397 opt = &flower->key.tunnel.metadata.opts.gnv[cnt];
1398 opt_mask = &flower->mask.tunnel.metadata.opts.gnv[cnt];
1399
1400 opt_mask->length = opt->length;
1401
1402 cnt += sizeof(struct geneve_opt) / 4 + opt->length;
1403 len -= sizeof(struct geneve_opt) + opt->length * 4;
1404 }
1405
1406 flower->mask.tunnel.metadata.present.len = tnl->metadata.present.len;
1407}
1408
5fc5c50f 1409static int
8f283af8
PB
1410netdev_tc_flow_put(struct netdev *netdev, struct match *match,
1411 struct nlattr *actions, size_t actions_len,
1412 const ovs_u128 *ufid, struct offload_info *info,
75ad1cd6 1413 struct dpif_flow_stats *stats)
18ebd48c 1414{
8f283af8 1415 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
608ff46a 1416 enum tc_qdisc_hook hook = get_tc_qdisc_hook(netdev);
8f283af8
PB
1417 struct tc_flower flower;
1418 const struct flow *key = &match->flow;
1419 struct flow *mask = &match->wc.masks;
1420 const struct flow_tnl *tnl = &match->flow.tunnel;
49a7961f 1421 const struct flow_tnl *tnl_mask = &mask->tunnel;
0c70132c 1422 struct tc_action *action;
aeee3344 1423 bool recirc_act = false;
093c9458 1424 uint32_t block_id = 0;
8f283af8 1425 struct nlattr *nla;
acdd544c 1426 struct tcf_id id;
b2ae4069 1427 uint32_t chain;
8f283af8
PB
1428 size_t left;
1429 int prio = 0;
8f283af8
PB
1430 int ifindex;
1431 int err;
1432
1433 ifindex = netdev_get_ifindex(netdev);
1434 if (ifindex < 0) {
0164f10c 1435 VLOG_ERR_RL(&error_rl, "flow_put: failed to get ifindex for %s: %s",
8f283af8
PB
1436 netdev_get_name(netdev), ovs_strerror(-ifindex));
1437 return -ifindex;
1438 }
1439
1440 memset(&flower, 0, sizeof flower);
1441
b2ae4069
PB
1442 chain = key->recirc_id;
1443 mask->recirc_id = 0;
1444
3f82ac1f
TZ
1445 if (flow_tnl_dst_is_set(&key->tunnel) ||
1446 flow_tnl_src_is_set(&key->tunnel)) {
8f283af8
PB
1447 VLOG_DBG_RL(&rl,
1448 "tunnel: id %#" PRIx64 " src " IP_FMT
1449 " dst " IP_FMT " tp_src %d tp_dst %d",
1450 ntohll(tnl->tun_id),
1451 IP_ARGS(tnl->ip_src), IP_ARGS(tnl->ip_dst),
1452 ntohs(tnl->tp_src), ntohs(tnl->tp_dst));
105e8179
OG
1453 flower.key.tunnel.id = tnl->tun_id;
1454 flower.key.tunnel.ipv4.ipv4_src = tnl->ip_src;
1455 flower.key.tunnel.ipv4.ipv4_dst = tnl->ip_dst;
1456 flower.key.tunnel.ipv6.ipv6_src = tnl->ipv6_src;
1457 flower.key.tunnel.ipv6.ipv6_dst = tnl->ipv6_dst;
1458 flower.key.tunnel.tos = tnl->ip_tos;
1459 flower.key.tunnel.ttl = tnl->ip_ttl;
1460 flower.key.tunnel.tp_src = tnl->tp_src;
1461 flower.key.tunnel.tp_dst = tnl->tp_dst;
5f568d04
TZ
1462 flower.mask.tunnel.ipv4.ipv4_src = tnl_mask->ip_src;
1463 flower.mask.tunnel.ipv4.ipv4_dst = tnl_mask->ip_dst;
1464 flower.mask.tunnel.ipv6.ipv6_src = tnl_mask->ipv6_src;
1465 flower.mask.tunnel.ipv6.ipv6_dst = tnl_mask->ipv6_dst;
49a7961f
OG
1466 flower.mask.tunnel.tos = tnl_mask->ip_tos;
1467 flower.mask.tunnel.ttl = tnl_mask->ip_ttl;
0227bf09 1468 flower.mask.tunnel.id = (tnl->flags & FLOW_TNL_F_KEY) ? tnl_mask->tun_id : 0;
a468645c 1469 flower_match_to_tun_opt(&flower, tnl, tnl_mask);
105e8179 1470 flower.tunnel = true;
8f283af8 1471 }
a90120fc 1472 memset(&mask->tunnel, 0, sizeof mask->tunnel);
8f283af8
PB
1473
1474 flower.key.eth_type = key->dl_type;
1475 flower.mask.eth_type = mask->dl_type;
34b16955
PJV
1476 if (mask->mpls_lse[0]) {
1477 flower.key.mpls_lse = key->mpls_lse[0];
1478 flower.mask.mpls_lse = mask->mpls_lse[0];
1479 flower.key.encap_eth_type[0] = flower.key.eth_type;
1480 }
1481 mask->mpls_lse[0] = 0;
8f283af8 1482
5efc6552 1483 if (mask->vlans[0].tpid && eth_type_vlan(key->vlans[0].tpid)) {
0b0a8478 1484 flower.key.encap_eth_type[0] = flower.key.eth_type;
5efc6552 1485 flower.mask.encap_eth_type[0] = flower.mask.eth_type;
0b0a8478 1486 flower.key.eth_type = key->vlans[0].tpid;
5efc6552 1487 flower.mask.eth_type = mask->vlans[0].tpid;
0b0a8478 1488 }
8f283af8
PB
1489 if (mask->vlans[0].tci) {
1490 ovs_be16 vid_mask = mask->vlans[0].tci & htons(VLAN_VID_MASK);
1491 ovs_be16 pcp_mask = mask->vlans[0].tci & htons(VLAN_PCP_MASK);
1492 ovs_be16 cfi = mask->vlans[0].tci & htons(VLAN_CFI);
1493
1494 if (cfi && key->vlans[0].tci & htons(VLAN_CFI)
1495 && (!vid_mask || vid_mask == htons(VLAN_VID_MASK))
1496 && (!pcp_mask || pcp_mask == htons(VLAN_PCP_MASK))
1497 && (vid_mask || pcp_mask)) {
1498 if (vid_mask) {
f9885dc5 1499 flower.key.vlan_id[0] = vlan_tci_to_vid(key->vlans[0].tci);
7f02f26c 1500 flower.mask.vlan_id[0] = vlan_tci_to_vid(mask->vlans[0].tci);
f9885dc5 1501 VLOG_DBG_RL(&rl, "vlan_id[0]: %d\n", flower.key.vlan_id[0]);
8f283af8
PB
1502 }
1503 if (pcp_mask) {
f9885dc5 1504 flower.key.vlan_prio[0] = vlan_tci_to_pcp(key->vlans[0].tci);
7f02f26c 1505 flower.mask.vlan_prio[0] = vlan_tci_to_pcp(mask->vlans[0].tci);
f9885dc5
JL
1506 VLOG_DBG_RL(&rl, "vlan_prio[0]: %d\n",
1507 flower.key.vlan_prio[0]);
8f283af8 1508 }
8f283af8
PB
1509 } else if (mask->vlans[0].tci == htons(0xffff) &&
1510 ntohs(key->vlans[0].tci) == 0) {
1511 /* exact && no vlan */
1512 } else {
1513 /* partial mask */
1514 return EOPNOTSUPP;
1515 }
f9885dc5
JL
1516 }
1517
5efc6552 1518 if (mask->vlans[1].tpid && eth_type_vlan(key->vlans[1].tpid)) {
0b0a8478 1519 flower.key.encap_eth_type[1] = flower.key.encap_eth_type[0];
5efc6552 1520 flower.mask.encap_eth_type[1] = flower.mask.encap_eth_type[0];
0b0a8478 1521 flower.key.encap_eth_type[0] = key->vlans[1].tpid;
5efc6552 1522 flower.mask.encap_eth_type[0] = mask->vlans[1].tpid;
0b0a8478 1523 }
f9885dc5
JL
1524 if (mask->vlans[1].tci) {
1525 ovs_be16 vid_mask = mask->vlans[1].tci & htons(VLAN_VID_MASK);
1526 ovs_be16 pcp_mask = mask->vlans[1].tci & htons(VLAN_PCP_MASK);
1527 ovs_be16 cfi = mask->vlans[1].tci & htons(VLAN_CFI);
1528
1529 if (cfi && key->vlans[1].tci & htons(VLAN_CFI)
1530 && (!vid_mask || vid_mask == htons(VLAN_VID_MASK))
1531 && (!pcp_mask || pcp_mask == htons(VLAN_PCP_MASK))
1532 && (vid_mask || pcp_mask)) {
1533 if (vid_mask) {
1534 flower.key.vlan_id[1] = vlan_tci_to_vid(key->vlans[1].tci);
7f02f26c 1535 flower.mask.vlan_id[1] = vlan_tci_to_vid(mask->vlans[1].tci);
f9885dc5
JL
1536 VLOG_DBG_RL(&rl, "vlan_id[1]: %d", flower.key.vlan_id[1]);
1537 }
1538 if (pcp_mask) {
1539 flower.key.vlan_prio[1] = vlan_tci_to_pcp(key->vlans[1].tci);
7f02f26c 1540 flower.mask.vlan_prio[1] = vlan_tci_to_pcp(mask->vlans[1].tci);
f9885dc5
JL
1541 VLOG_DBG_RL(&rl, "vlan_prio[1]: %d", flower.key.vlan_prio[1]);
1542 }
f9885dc5
JL
1543 } else if (mask->vlans[1].tci == htons(0xffff) &&
1544 ntohs(key->vlans[1].tci) == 0) {
1545 /* exact && no vlan */
1546 } else {
1547 /* partial mask */
1548 return EOPNOTSUPP;
1549 }
8f283af8
PB
1550 }
1551 memset(mask->vlans, 0, sizeof mask->vlans);
1552
1553 flower.key.dst_mac = key->dl_dst;
1554 flower.mask.dst_mac = mask->dl_dst;
1555 flower.key.src_mac = key->dl_src;
1556 flower.mask.src_mac = mask->dl_src;
1557 memset(&mask->dl_dst, 0, sizeof mask->dl_dst);
1558 memset(&mask->dl_src, 0, sizeof mask->dl_src);
1559 mask->dl_type = 0;
1560 mask->in_port.odp_port = 0;
1561
a3db6e47
TZ
1562 if (key->dl_type == htons(ETH_P_ARP)) {
1563 flower.key.arp.spa = key->nw_src;
1564 flower.key.arp.tpa = key->nw_dst;
1565 flower.key.arp.sha = key->arp_sha;
1566 flower.key.arp.tha = key->arp_tha;
1567 flower.key.arp.opcode = key->nw_proto;
1568 flower.mask.arp.spa = mask->nw_src;
1569 flower.mask.arp.tpa = mask->nw_dst;
1570 flower.mask.arp.sha = mask->arp_sha;
1571 flower.mask.arp.tha = mask->arp_tha;
1572 flower.mask.arp.opcode = mask->nw_proto;
1573
1574 mask->nw_src = 0;
1575 mask->nw_dst = 0;
1576 mask->nw_proto = 0;
1577 memset(&mask->arp_sha, 0, sizeof mask->arp_sha);
1578 memset(&mask->arp_tha, 0, sizeof mask->arp_tha);
1579 }
1580
8f283af8
PB
1581 if (is_ip_any(key)) {
1582 flower.key.ip_proto = key->nw_proto;
1583 flower.mask.ip_proto = mask->nw_proto;
b4496fc9 1584 mask->nw_proto = 0;
dfa2ccdb
OG
1585 flower.key.ip_tos = key->nw_tos;
1586 flower.mask.ip_tos = mask->nw_tos;
1587 mask->nw_tos = 0;
ab7ecf26
PB
1588 flower.key.ip_ttl = key->nw_ttl;
1589 flower.mask.ip_ttl = mask->nw_ttl;
b4496fc9 1590 mask->nw_ttl = 0;
8f283af8 1591
45a60c21
RD
1592 if (mask->nw_frag & FLOW_NW_FRAG_ANY) {
1593 flower.mask.flags |= TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT;
1594
1595 if (key->nw_frag & FLOW_NW_FRAG_ANY) {
83e86606 1596 flower.key.flags |= TCA_FLOWER_KEY_FLAGS_IS_FRAGMENT;
83e86606 1597
45a60c21
RD
1598 if (mask->nw_frag & FLOW_NW_FRAG_LATER) {
1599 flower.mask.flags |= TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST;
1600
1601 if (!(key->nw_frag & FLOW_NW_FRAG_LATER)) {
1602 flower.key.flags |= TCA_FLOWER_KEY_FLAGS_FRAG_IS_FIRST;
1603 }
1604 }
1605 }
1606
83e86606
RD
1607 mask->nw_frag = 0;
1608 }
1609
2b1d9fa9
PB
1610 if (key->nw_proto == IPPROTO_TCP) {
1611 flower.key.tcp_dst = key->tp_dst;
1612 flower.mask.tcp_dst = mask->tp_dst;
1613 flower.key.tcp_src = key->tp_src;
1614 flower.mask.tcp_src = mask->tp_src;
f2698407
PB
1615 flower.key.tcp_flags = key->tcp_flags;
1616 flower.mask.tcp_flags = mask->tcp_flags;
2b1d9fa9
PB
1617 mask->tp_src = 0;
1618 mask->tp_dst = 0;
f2698407 1619 mask->tcp_flags = 0;
2b1d9fa9
PB
1620 } else if (key->nw_proto == IPPROTO_UDP) {
1621 flower.key.udp_dst = key->tp_dst;
1622 flower.mask.udp_dst = mask->tp_dst;
1623 flower.key.udp_src = key->tp_src;
1624 flower.mask.udp_src = mask->tp_src;
1625 mask->tp_src = 0;
1626 mask->tp_dst = 0;
1627 } else if (key->nw_proto == IPPROTO_SCTP) {
1628 flower.key.sctp_dst = key->tp_dst;
1629 flower.mask.sctp_dst = mask->tp_dst;
1630 flower.key.sctp_src = key->tp_src;
1631 flower.mask.sctp_src = mask->tp_src;
8f283af8
PB
1632 mask->tp_src = 0;
1633 mask->tp_dst = 0;
1634 }
1635
8f283af8
PB
1636 if (key->dl_type == htons(ETH_P_IP)) {
1637 flower.key.ipv4.ipv4_src = key->nw_src;
1638 flower.mask.ipv4.ipv4_src = mask->nw_src;
1639 flower.key.ipv4.ipv4_dst = key->nw_dst;
1640 flower.mask.ipv4.ipv4_dst = mask->nw_dst;
1641 mask->nw_src = 0;
1642 mask->nw_dst = 0;
1643 } else if (key->dl_type == htons(ETH_P_IPV6)) {
1644 flower.key.ipv6.ipv6_src = key->ipv6_src;
1645 flower.mask.ipv6.ipv6_src = mask->ipv6_src;
1646 flower.key.ipv6.ipv6_dst = key->ipv6_dst;
1647 flower.mask.ipv6.ipv6_dst = mask->ipv6_dst;
1648 memset(&mask->ipv6_src, 0, sizeof mask->ipv6_src);
1649 memset(&mask->ipv6_dst, 0, sizeof mask->ipv6_dst);
1650 }
1651 }
1652
576126a9
PB
1653 if (mask->ct_state) {
1654 if (mask->ct_state & OVS_CS_F_NEW) {
1655 if (key->ct_state & OVS_CS_F_NEW) {
1656 flower.key.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_NEW;
1657 }
1658 flower.mask.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_NEW;
242b7b93 1659 mask->ct_state &= ~OVS_CS_F_NEW;
576126a9
PB
1660 }
1661
1662 if (mask->ct_state & OVS_CS_F_ESTABLISHED) {
1663 if (key->ct_state & OVS_CS_F_ESTABLISHED) {
1664 flower.key.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED;
1665 }
1666 flower.mask.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED;
242b7b93 1667 mask->ct_state &= ~OVS_CS_F_ESTABLISHED;
576126a9
PB
1668 }
1669
1670 if (mask->ct_state & OVS_CS_F_TRACKED) {
1671 if (key->ct_state & OVS_CS_F_TRACKED) {
1672 flower.key.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_TRACKED;
1673 }
1674 flower.mask.ct_state |= TCA_FLOWER_KEY_CT_FLAGS_TRACKED;
242b7b93 1675 mask->ct_state &= ~OVS_CS_F_TRACKED;
576126a9
PB
1676 }
1677
1678 if (flower.key.ct_state & TCA_FLOWER_KEY_CT_FLAGS_ESTABLISHED) {
1679 flower.key.ct_state &= ~(TCA_FLOWER_KEY_CT_FLAGS_NEW);
1680 flower.mask.ct_state &= ~(TCA_FLOWER_KEY_CT_FLAGS_NEW);
1681 }
576126a9
PB
1682 }
1683
1684 if (mask->ct_zone) {
1685 flower.key.ct_zone = key->ct_zone;
1686 flower.mask.ct_zone = mask->ct_zone;
1687 mask->ct_zone = 0;
1688 }
1689
9221c721
PB
1690 if (mask->ct_mark) {
1691 flower.key.ct_mark = key->ct_mark;
1692 flower.mask.ct_mark = mask->ct_mark;
1693 mask->ct_mark = 0;
1694 }
1695
1696 if (!ovs_u128_is_zero(mask->ct_label)) {
1697 flower.key.ct_label = key->ct_label;
1698 flower.mask.ct_label = mask->ct_label;
1699 mask->ct_label = OVS_U128_ZERO;
1700 }
1701
56c8027b
JH
1702 /* ignore exact match on skb_mark of 0. */
1703 if (mask->pkt_mark == UINT32_MAX && !key->pkt_mark) {
1704 mask->pkt_mark = 0;
1705 }
1706
8f283af8
PB
1707 err = test_key_and_mask(match);
1708 if (err) {
1709 return err;
1710 }
1711
1712 NL_ATTR_FOR_EACH(nla, left, actions, actions_len) {
d0fbb09f
CM
1713 if (flower.action_count >= TCA_ACT_MAX_NUM) {
1714 VLOG_DBG_RL(&rl, "Can only support %d actions", TCA_ACT_MAX_NUM);
0c70132c
CM
1715 return EOPNOTSUPP;
1716 }
1717 action = &flower.actions[flower.action_count];
8f283af8
PB
1718 if (nl_attr_type(nla) == OVS_ACTION_ATTR_OUTPUT) {
1719 odp_port_t port = nl_attr_get_odp_port(nla);
8842fdf1
IM
1720 struct netdev *outdev = netdev_ports_get(
1721 port, netdev_get_dpif_type(netdev));
8f283af8 1722
695e3509
IM
1723 if (!outdev) {
1724 VLOG_DBG_RL(&rl, "Can't find netdev for output port %d", port);
1725 return ENODEV;
1726 }
4aa2dc04
JH
1727 action->out.ifindex_out = netdev_get_ifindex(outdev);
1728 action->out.ingress = is_internal_port(netdev_get_type(outdev));
0c70132c
CM
1729 action->type = TC_ACT_OUTPUT;
1730 flower.action_count++;
8f283af8
PB
1731 netdev_close(outdev);
1732 } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_PUSH_VLAN) {
1733 const struct ovs_action_push_vlan *vlan_push = nl_attr_get(nla);
1734
61e8655c 1735 action->vlan.vlan_push_tpid = vlan_push->vlan_tpid;
0c70132c
CM
1736 action->vlan.vlan_push_id = vlan_tci_to_vid(vlan_push->vlan_tci);
1737 action->vlan.vlan_push_prio = vlan_tci_to_pcp(vlan_push->vlan_tci);
1738 action->type = TC_ACT_VLAN_PUSH;
1739 flower.action_count++;
8f283af8 1740 } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_POP_VLAN) {
0c70132c
CM
1741 action->type = TC_ACT_VLAN_POP;
1742 flower.action_count++;
283dcf85
JH
1743 } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_PUSH_MPLS) {
1744 const struct ovs_action_push_mpls *mpls_push = nl_attr_get(nla);
1745
1746 action->mpls.proto = mpls_push->mpls_ethertype;
1747 action->mpls.label = mpls_lse_to_label(mpls_push->mpls_lse);
1748 action->mpls.tc = mpls_lse_to_tc(mpls_push->mpls_lse);
1749 action->mpls.ttl = mpls_lse_to_ttl(mpls_push->mpls_lse);
1750 action->mpls.bos = mpls_lse_to_bos(mpls_push->mpls_lse);
1751 action->type = TC_ACT_MPLS_PUSH;
1752 flower.action_count++;
55412eac
JH
1753 } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_POP_MPLS) {
1754 action->mpls.proto = nl_attr_get_be16(nla);
1755 action->type = TC_ACT_MPLS_POP;
1756 flower.action_count++;
8f283af8
PB
1757 } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_SET) {
1758 const struct nlattr *set = nl_attr_get(nla);
1759 const size_t set_len = nl_attr_get_size(nla);
1760
0c70132c 1761 err = parse_put_flow_set_action(&flower, action, set, set_len);
8f283af8
PB
1762 if (err) {
1763 return err;
1764 }
0c70132c
CM
1765 if (action->type == TC_ACT_ENCAP) {
1766 action->encap.tp_dst = info->tp_dst_port;
d9677a1f 1767 action->encap.no_csum = !info->tunnel_csum_on;
0c70132c 1768 }
1ae83bb2
PB
1769 } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_SET_MASKED) {
1770 const struct nlattr *set = nl_attr_get(nla);
1771 const size_t set_len = nl_attr_get_size(nla);
1772
0c70132c
CM
1773 err = parse_put_flow_set_masked_action(&flower, action, set,
1774 set_len, true);
1ae83bb2
PB
1775 if (err) {
1776 return err;
1777 }
576126a9
PB
1778 } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_CT) {
1779 const struct nlattr *ct = nl_attr_get(nla);
1780 const size_t ct_len = nl_attr_get_size(nla);
1781
1782 err = parse_put_flow_ct_action(&flower, action, ct, ct_len);
1783 if (err) {
1784 return err;
1785 }
1786 } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_CT_CLEAR) {
1787 action->type = TC_ACT_CT;
1788 action->ct.clear = true;
1789 flower.action_count++;
b2ae4069
PB
1790 } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_RECIRC) {
1791 action->type = TC_ACT_GOTO;
1792 action->chain = nl_attr_get_u32(nla);
1793 flower.action_count++;
1794 recirc_act = true;
00268272
WT
1795 } else if (nl_attr_type(nla) == OVS_ACTION_ATTR_DROP) {
1796 action->type = TC_ACT_GOTO;
1797 action->chain = 0; /* 0 is reserved and not used by recirc. */
1798 flower.action_count++;
8f283af8
PB
1799 } else {
1800 VLOG_DBG_RL(&rl, "unsupported put action type: %d",
1801 nl_attr_type(nla));
1802 return EOPNOTSUPP;
1803 }
1804 }
1805
b2ae4069
PB
1806 if ((chain || recirc_act) && !info->recirc_id_shared_with_tc) {
1807 VLOG_ERR_RL(&error_rl, "flow_put: recirc_id sharing not supported");
1808 return EOPNOTSUPP;
1809 }
1810
acdd544c
PB
1811 if (get_ufid_tc_mapping(ufid, &id) == 0) {
1812 VLOG_DBG_RL(&rl, "updating old handle: %d prio: %d",
1813 id.handle, id.prio);
65b84d4a 1814 info->tc_modify_flow_deleted = !del_filter_and_ufid_mapping(&id, ufid);
acdd544c
PB
1815 }
1816
1817 prio = get_prio_for_tc_flower(&flower);
1818 if (prio == 0) {
1819 VLOG_ERR_RL(&rl, "couldn't get tc prio: %s", ovs_strerror(ENOSPC));
1820 return ENOSPC;
8f283af8
PB
1821 }
1822
1823 flower.act_cookie.data = ufid;
1824 flower.act_cookie.len = sizeof *ufid;
1825
acdd544c 1826 block_id = get_block_id_from_netdev(netdev);
b2ae4069 1827 id = tc_make_tcf_id_chain(ifindex, block_id, chain, prio, hook);
acdd544c 1828 err = tc_replace_flower(&id, &flower);
8f283af8 1829 if (!err) {
75ad1cd6
BP
1830 if (stats) {
1831 memset(stats, 0, sizeof *stats);
1832 }
acdd544c 1833 add_ufid_tc_mapping(netdev, ufid, &id);
8f283af8
PB
1834 }
1835
1836 return err;
18ebd48c
PB
1837}
1838
5fc5c50f 1839static int
acdd544c 1840netdev_tc_flow_get(struct netdev *netdev,
7ecdef27
PB
1841 struct match *match,
1842 struct nlattr **actions,
1843 const ovs_u128 *ufid,
1844 struct dpif_flow_stats *stats,
d63ca532 1845 struct dpif_flow_attrs *attrs,
7ecdef27 1846 struct ofpbuf *buf)
18ebd48c 1847{
7ecdef27 1848 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
7ecdef27
PB
1849 struct tc_flower flower;
1850 odp_port_t in_port;
acdd544c 1851 struct tcf_id id;
7ecdef27
PB
1852 int err;
1853
acdd544c
PB
1854 err = get_ufid_tc_mapping(ufid, &id);
1855 if (err) {
1856 return err;
7ecdef27
PB
1857 }
1858
41c2e25c 1859 VLOG_DBG_RL(&rl, "flow get (dev %s prio %d handle %d block_id %d)",
acdd544c
PB
1860 netdev_get_name(netdev), id.prio, id.handle, id.block_id);
1861
1862 err = tc_get_flower(&id, &flower);
7ecdef27
PB
1863 if (err) {
1864 VLOG_ERR_RL(&error_rl, "flow get failed (dev %s prio %d handle %d): %s",
acdd544c
PB
1865 netdev_get_name(netdev), id.prio, id.handle,
1866 ovs_strerror(err));
7ecdef27
PB
1867 return err;
1868 }
1869
acdd544c 1870 in_port = netdev_ifindex_to_odp_port(id.ifindex);
19153657 1871 parse_tc_flower_to_match(&flower, match, actions, stats, attrs, buf, false);
7ecdef27
PB
1872
1873 match->wc.masks.in_port.odp_port = u32_to_odp(UINT32_MAX);
1874 match->flow.in_port.odp_port = in_port;
b2ae4069 1875 match_set_recirc_id(match, id.chain);
7ecdef27
PB
1876
1877 return 0;
18ebd48c
PB
1878}
1879
5fc5c50f 1880static int
18ebd48c 1881netdev_tc_flow_del(struct netdev *netdev OVS_UNUSED,
30b6b047
PB
1882 const ovs_u128 *ufid,
1883 struct dpif_flow_stats *stats)
18ebd48c 1884{
52f793b8 1885 struct tc_flower flower;
acdd544c 1886 struct tcf_id id;
30b6b047
PB
1887 int error;
1888
acdd544c
PB
1889 error = get_ufid_tc_mapping(ufid, &id);
1890 if (error) {
1891 return error;
30b6b047
PB
1892 }
1893
52f793b8
PA
1894 if (stats) {
1895 memset(stats, 0, sizeof *stats);
acdd544c 1896 if (!tc_get_flower(&id, &flower)) {
52f793b8
PA
1897 stats->n_packets = get_32aligned_u64(&flower.stats.n_packets);
1898 stats->n_bytes = get_32aligned_u64(&flower.stats.n_bytes);
1899 stats->used = flower.lastused;
1900 }
1901 }
1902
acdd544c 1903 error = del_filter_and_ufid_mapping(&id, ufid);
30b6b047 1904
30b6b047 1905 return error;
18ebd48c
PB
1906}
1907
c5b4b0ce
JL
1908static int
1909netdev_tc_get_n_flows(struct netdev *netdev, uint64_t *n_flows)
1910{
1911 struct ufid_tc_data *data;
1912 uint64_t total = 0;
1913
1914 ovs_mutex_lock(&ufid_lock);
1915 HMAP_FOR_EACH (data, tc_to_ufid_node, &tc_to_ufid) {
1916 if (data->netdev == netdev) {
1917 total++;
1918 }
1919 }
1920 ovs_mutex_unlock(&ufid_lock);
1921
1922 *n_flows = total;
1923 return 0;
1924}
1925
d00eeded 1926static void
79d0dfa4 1927probe_multi_mask_per_prio(int ifindex)
d00eeded
PB
1928{
1929 struct tc_flower flower;
acdd544c 1930 struct tcf_id id1, id2;
79d0dfa4 1931 int block_id = 0;
acdd544c 1932 int prio = 1;
d00eeded
PB
1933 int error;
1934
95255018 1935 error = tc_add_del_qdisc(ifindex, true, block_id, TC_INGRESS);
79d0dfa4
RD
1936 if (error) {
1937 return;
1938 }
1939
d00eeded
PB
1940 memset(&flower, 0, sizeof flower);
1941
d5659751 1942 flower.tc_policy = TC_POLICY_SKIP_HW;
d00eeded 1943 flower.key.eth_type = htons(ETH_P_IP);
c22ab9fb 1944 flower.mask.eth_type = OVS_BE16_MAX;
d00eeded
PB
1945 memset(&flower.key.dst_mac, 0x11, sizeof flower.key.dst_mac);
1946 memset(&flower.mask.dst_mac, 0xff, sizeof flower.mask.dst_mac);
1947
acdd544c
PB
1948 id1 = tc_make_tcf_id(ifindex, block_id, prio, TC_INGRESS);
1949 error = tc_replace_flower(&id1, &flower);
d00eeded 1950 if (error) {
79d0dfa4 1951 goto out;
d00eeded
PB
1952 }
1953
1954 memset(&flower.key.src_mac, 0x11, sizeof flower.key.src_mac);
1955 memset(&flower.mask.src_mac, 0xff, sizeof flower.mask.src_mac);
1956
acdd544c
PB
1957 id2 = tc_make_tcf_id(ifindex, block_id, prio, TC_INGRESS);
1958 error = tc_replace_flower(&id2, &flower);
1959 tc_del_filter(&id1);
d00eeded
PB
1960
1961 if (error) {
79d0dfa4 1962 goto out;
d00eeded
PB
1963 }
1964
acdd544c 1965 tc_del_filter(&id2);
d00eeded
PB
1966
1967 multi_mask_per_prio = true;
1968 VLOG_INFO("probe tc: multiple masks on single tc prio is supported.");
79d0dfa4
RD
1969
1970out:
95255018 1971 tc_add_del_qdisc(ifindex, false, block_id, TC_INGRESS);
d00eeded
PB
1972}
1973
093c9458
JH
1974static void
1975probe_tc_block_support(int ifindex)
1976{
2a2e6370 1977 struct tc_flower flower;
093c9458 1978 uint32_t block_id = 1;
acdd544c
PB
1979 struct tcf_id id;
1980 int prio = 0;
093c9458
JH
1981 int error;
1982
95255018 1983 error = tc_add_del_qdisc(ifindex, true, block_id, TC_INGRESS);
093c9458
JH
1984 if (error) {
1985 return;
1986 }
1987
2a2e6370
RS
1988 memset(&flower, 0, sizeof flower);
1989
d5659751 1990 flower.tc_policy = TC_POLICY_SKIP_HW;
2a2e6370
RS
1991 flower.key.eth_type = htons(ETH_P_IP);
1992 flower.mask.eth_type = OVS_BE16_MAX;
1993 memset(&flower.key.dst_mac, 0x11, sizeof flower.key.dst_mac);
1994 memset(&flower.mask.dst_mac, 0xff, sizeof flower.mask.dst_mac);
1995
acdd544c
PB
1996 id = tc_make_tcf_id(ifindex, block_id, prio, TC_INGRESS);
1997 error = tc_replace_flower(&id, &flower);
2a2e6370 1998
95255018 1999 tc_add_del_qdisc(ifindex, false, block_id, TC_INGRESS);
093c9458 2000
2a2e6370
RS
2001 if (!error) {
2002 block_support = true;
2003 VLOG_INFO("probe tc: block offload is supported.");
2004 }
093c9458
JH
2005}
2006
5fc5c50f 2007static int
adbbe97f 2008netdev_tc_init_flow_api(struct netdev *netdev)
18ebd48c 2009{
ecadc3a3 2010 static struct ovsthread_once once = OVSTHREAD_ONCE_INITIALIZER;
608ff46a 2011 enum tc_qdisc_hook hook = get_tc_qdisc_hook(netdev);
093c9458 2012 uint32_t block_id = 0;
edc2055a 2013 struct tcf_id id;
adbbe97f
PB
2014 int ifindex;
2015 int error;
2016
2017 ifindex = netdev_get_ifindex(netdev);
2018 if (ifindex < 0) {
5fc5c50f
IM
2019 VLOG_INFO("init: failed to get ifindex for %s: %s",
2020 netdev_get_name(netdev), ovs_strerror(-ifindex));
adbbe97f
PB
2021 return -ifindex;
2022 }
2023
edc2055a
DL
2024 block_id = get_block_id_from_netdev(netdev);
2025
2026 /* Flush rules explicitly needed when we work with ingress_block,
2027 * so we will not fail with reattaching block to bond iface, for ex.
2028 */
2029 id = tc_make_tcf_id(ifindex, block_id, 0, hook);
2030 tc_del_filter(&id);
2031
9e74acbe
RS
2032 /* make sure there is no ingress/egress qdisc */
2033 tc_add_del_qdisc(ifindex, false, 0, hook);
bb5538d2 2034
ecadc3a3 2035 if (ovsthread_once_start(&once)) {
093c9458 2036 probe_tc_block_support(ifindex);
8508a572
AC
2037 /* Need to re-fetch block id as it depends on feature availability. */
2038 block_id = get_block_id_from_netdev(netdev);
093c9458 2039
79d0dfa4 2040 probe_multi_mask_per_prio(ifindex);
ecadc3a3 2041 ovsthread_once_done(&once);
79d0dfa4
RD
2042 }
2043
608ff46a 2044 error = tc_add_del_qdisc(ifindex, true, block_id, hook);
adbbe97f
PB
2045
2046 if (error && error != EEXIST) {
5fc5c50f
IM
2047 VLOG_INFO("failed adding ingress qdisc required for offloading: %s",
2048 ovs_strerror(error));
adbbe97f
PB
2049 return error;
2050 }
2051
2052 VLOG_INFO("added ingress qdisc to %s", netdev_get_name(netdev));
2053
18ebd48c
PB
2054 return 0;
2055}
5fc5c50f 2056
4f746d52 2057const struct netdev_flow_api netdev_offload_tc = {
5fc5c50f
IM
2058 .type = "linux_tc",
2059 .flow_flush = netdev_tc_flow_flush,
2060 .flow_dump_create = netdev_tc_flow_dump_create,
2061 .flow_dump_destroy = netdev_tc_flow_dump_destroy,
2062 .flow_dump_next = netdev_tc_flow_dump_next,
2063 .flow_put = netdev_tc_flow_put,
2064 .flow_get = netdev_tc_flow_get,
2065 .flow_del = netdev_tc_flow_del,
c5b4b0ce 2066 .flow_get_n_flows = netdev_tc_get_n_flows,
5fc5c50f
IM
2067 .init_flow_api = netdev_tc_init_flow_api,
2068};